color, shaders: Use color mgmt LUT path in shader compute path
This commit is contained in:
parent
c6e232ea8c
commit
0c980fbb0e
11 changed files with 410 additions and 203 deletions
19
src/drm.cpp
19
src/drm.cpp
|
|
@ -1866,11 +1866,11 @@ struct LiftoffStateCacheEntryKasher
|
|||
|
||||
std::unordered_set<LiftoffStateCacheEntry, LiftoffStateCacheEntryKasher> g_LiftoffStateCache;
|
||||
|
||||
static inline drm_valve1_transfer_function colorspace_to_degamma_tf(GamescopeAppTextureColorspace colorspace)
|
||||
static inline drm_valve1_transfer_function colorspace_to_plane_degamma_tf(GamescopeAppTextureColorspace colorspace)
|
||||
{
|
||||
switch ( colorspace )
|
||||
{
|
||||
default:
|
||||
default: // Linear in this sense is SRGB. Linear = sRGB image view doing automatic sRGB -> Linear which doesn't happen on DRM side.
|
||||
case GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB:
|
||||
return DRM_VALVE1_TRANSFER_FUNCTION_SRGB;
|
||||
case GAMESCOPE_APP_TEXTURE_COLORSPACE_SCRGB:
|
||||
|
|
@ -1884,7 +1884,7 @@ static inline drm_valve1_transfer_function colorspace_to_degamma_tf(GamescopeApp
|
|||
}
|
||||
}
|
||||
|
||||
static inline drm_valve1_transfer_function colorspace_to_regamma_tf(GamescopeAppTextureColorspace colorspace)
|
||||
static inline drm_valve1_transfer_function colorspace_to_plane_shaper_tf(GamescopeAppTextureColorspace colorspace)
|
||||
{
|
||||
switch ( colorspace )
|
||||
{
|
||||
|
|
@ -1908,10 +1908,8 @@ static inline uint32_t ColorSpaceToEOTFIndex( GamescopeAppTextureColorspace colo
|
|||
return EOTF_Gamma22;
|
||||
case GAMESCOPE_APP_TEXTURE_COLORSPACE_SCRGB:
|
||||
// Okay, so this is WEIRD right? OKAY Let me explain it to you.
|
||||
// The plan for scRGB content is to go from scRGB -> PQ in a DEGAMMA_LUT
|
||||
// I know I know this sounds crazy, but this then goes into the same shaper + 3D LUT
|
||||
// path as everything else does.
|
||||
// TODO(Josh): Hook up DEGAMMA_LUT + DEGAMMA_TF to actually do re-gamma.
|
||||
// The plan for scRGB content is to go from scRGB -> PQ in a SHAPER_TF
|
||||
// before indexing into the shaper. (input from colorspace_to_plane_regamma_tf!)
|
||||
return EOTF_PQ;
|
||||
case GAMESCOPE_APP_TEXTURE_COLORSPACE_HDR10_PQ:
|
||||
return EOTF_PQ;
|
||||
|
|
@ -2048,12 +2046,13 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo
|
|||
liftoff_layer_unset_property( drm->lo_layers[ i ], "COLOR_RANGE" );
|
||||
if ( drm_supports_color_mgmt( drm ) )
|
||||
{
|
||||
drm_valve1_transfer_function degamma_tf = colorspace_to_degamma_tf( entry.layerState[i].colorspace );
|
||||
drm_valve1_transfer_function regamma_tf = colorspace_to_regamma_tf( entry.layerState[i].colorspace );
|
||||
drm_valve1_transfer_function degamma_tf = colorspace_to_plane_degamma_tf( entry.layerState[i].colorspace );
|
||||
drm_valve1_transfer_function shaper_tf = colorspace_to_plane_shaper_tf( entry.layerState[i].colorspace );
|
||||
liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_DEGAMMA_TF", degamma_tf );
|
||||
liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_SHAPER_LUT", drm->pending.shaperlut_id[ ColorSpaceToEOTFIndex( entry.layerState[i].colorspace ) ] );
|
||||
liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_SHAPER_TF", regamma_tf );
|
||||
liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_SHAPER_TF", shaper_tf );
|
||||
liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_LUT3D", drm->pending.lut3d_id[ ColorSpaceToEOTFIndex( entry.layerState[i].colorspace ) ] );
|
||||
// Josh: See shaders/colorimetry.h colorspace_blend_tf if you have questions as to why we start doing sRGB for BLEND_TF despite potentially working in Gamma 2.2 space prior.
|
||||
liftoff_layer_set_property( drm->lo_layers[ i ], "VALVE1_PLANE_BLEND_TF", drm->pending.output_tf );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,8 +175,7 @@ struct PipelineInfo_t
|
|||
uint32_t compositeDebug;
|
||||
|
||||
uint32_t colorspaceMask;
|
||||
bool st2084Output;
|
||||
bool forceWideGammut;
|
||||
uint32_t outputEOTF;
|
||||
bool itmEnable;
|
||||
|
||||
bool operator==(const PipelineInfo_t& o) const {
|
||||
|
|
@ -187,8 +186,7 @@ struct PipelineInfo_t
|
|||
blurLayerCount == o.blurLayerCount &&
|
||||
compositeDebug == o.compositeDebug &&
|
||||
colorspaceMask == o.colorspaceMask &&
|
||||
st2084Output == o.st2084Output &&
|
||||
forceWideGammut == o.forceWideGammut &&
|
||||
outputEOTF == o.outputEOTF &&
|
||||
itmEnable == o.itmEnable;
|
||||
}
|
||||
};
|
||||
|
|
@ -211,8 +209,7 @@ namespace std
|
|||
hash = hash_combine(hash, k.blurLayerCount);
|
||||
hash = hash_combine(hash, k.compositeDebug);
|
||||
hash = hash_combine(hash, k.colorspaceMask);
|
||||
hash = hash_combine(hash, k.st2084Output);
|
||||
hash = hash_combine(hash, k.forceWideGammut);
|
||||
hash = hash_combine(hash, k.outputEOTF);
|
||||
hash = hash_combine(hash, k.itmEnable);
|
||||
return hash;
|
||||
}
|
||||
|
|
@ -382,6 +379,7 @@ public:
|
|||
void begin();
|
||||
void end();
|
||||
void bindTexture(uint32_t slot, std::shared_ptr<CVulkanTexture> texture);
|
||||
void bindColorMgmtLuts(uint32_t slot, const std::shared_ptr<CVulkanTexture>& lut1d, const std::shared_ptr<CVulkanTexture>& lut3d);
|
||||
void setTextureStorage(bool storage);
|
||||
void setTextureSrgb(uint32_t slot, bool srgb);
|
||||
void setSamplerNearest(uint32_t slot, bool nearest);
|
||||
|
|
@ -414,6 +412,9 @@ private:
|
|||
std::bitset<VKR_SAMPLER_SLOTS> m_useSrgb;
|
||||
std::array<SamplerState, VKR_SAMPLER_SLOTS> m_samplerState;
|
||||
CVulkanTexture *m_target;
|
||||
|
||||
std::array<CVulkanTexture *, VKR_LUT3D_COUNT> m_shaperLut;
|
||||
std::array<CVulkanTexture *, VKR_LUT3D_COUNT> m_lut3D;
|
||||
};
|
||||
|
||||
#define VULKAN_INSTANCE_FUNCTIONS \
|
||||
|
|
@ -495,7 +496,7 @@ public:
|
|||
bool BInit();
|
||||
|
||||
VkSampler sampler(SamplerState key);
|
||||
VkPipeline pipeline(ShaderType type, uint32_t layerCount = 1, uint32_t ycbcrMask = 0, uint32_t blur_layers = 0, uint32_t colorspace_mask = 0, bool st2084_output = false, bool force_wide_gammut = false, bool itm_enable = false);
|
||||
VkPipeline pipeline(ShaderType type, uint32_t layerCount = 1, uint32_t ycbcrMask = 0, uint32_t blur_layers = 0, uint32_t colorspace_mask = 0, uint32_t output_eotf = EOTF_Gamma22, bool itm_enable = false);
|
||||
int32_t findMemoryType( VkMemoryPropertyFlags properties, uint32_t requiredTypeBits );
|
||||
std::unique_ptr<CVulkanCmdBuffer> commandBuffer();
|
||||
uint64_t submit( std::unique_ptr<CVulkanCmdBuffer> cmdBuf);
|
||||
|
|
@ -541,7 +542,7 @@ private:
|
|||
bool createPools();
|
||||
bool createShaders();
|
||||
bool createScratchResources();
|
||||
VkPipeline compilePipeline(uint32_t layerCount, uint32_t ycbcrMask, ShaderType type, uint32_t blur_layer_count, uint32_t composite_debug, uint32_t colorspace_mask, bool st2084_output, bool force_wide_gammut, bool itm_enable);
|
||||
VkPipeline compilePipeline(uint32_t layerCount, uint32_t ycbcrMask, ShaderType type, uint32_t blur_layer_count, uint32_t composite_debug, uint32_t colorspace_mask, uint32_t output_eotf, bool itm_enable);
|
||||
void compileAllPipelines();
|
||||
void resetCmdBuffers(uint64_t sequence);
|
||||
|
||||
|
|
@ -1003,7 +1004,7 @@ bool CVulkanDevice::createLayouts()
|
|||
for (auto& sampler : ycbcrSamplers)
|
||||
sampler = m_ycbcrSampler;
|
||||
|
||||
std::array<VkDescriptorSetLayoutBinding, 4> layoutBindings = {
|
||||
std::array<VkDescriptorSetLayoutBinding, 6 > layoutBindings = {
|
||||
VkDescriptorSetLayoutBinding {
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
|
|
@ -1029,6 +1030,18 @@ bool CVulkanDevice::createLayouts()
|
|||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = ycbcrSamplers.data(),
|
||||
},
|
||||
VkDescriptorSetLayoutBinding {
|
||||
.binding = 4,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = VKR_LUT3D_COUNT,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
},
|
||||
VkDescriptorSetLayoutBinding {
|
||||
.binding = 5,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = VKR_LUT3D_COUNT,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
},
|
||||
};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo =
|
||||
|
|
@ -1091,7 +1104,7 @@ bool CVulkanDevice::createPools()
|
|||
},
|
||||
{
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
uint32_t(m_descriptorSets.size()) * 2 * VKR_SAMPLER_SLOTS,
|
||||
uint32_t(m_descriptorSets.size()) * ((2 * VKR_SAMPLER_SLOTS) + (2 * VKR_LUT3D_COUNT)),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -1264,9 +1277,9 @@ VkSampler CVulkanDevice::sampler( SamplerState key )
|
|||
return ret;
|
||||
}
|
||||
|
||||
VkPipeline CVulkanDevice::compilePipeline(uint32_t layerCount, uint32_t ycbcrMask, ShaderType type, uint32_t blur_layer_count, uint32_t composite_debug, uint32_t colorspace_mask, bool st2084_output, bool force_wide_gammut, bool itm_enable)
|
||||
VkPipeline CVulkanDevice::compilePipeline(uint32_t layerCount, uint32_t ycbcrMask, ShaderType type, uint32_t blur_layer_count, uint32_t composite_debug, uint32_t colorspace_mask, uint32_t output_eotf, bool itm_enable)
|
||||
{
|
||||
const std::array<VkSpecializationMapEntry, 8> specializationEntries = {{
|
||||
const std::array<VkSpecializationMapEntry, 7> specializationEntries = {{
|
||||
{
|
||||
.constantID = 0,
|
||||
.offset = sizeof(uint32_t) * 0,
|
||||
|
|
@ -1304,12 +1317,6 @@ VkPipeline CVulkanDevice::compilePipeline(uint32_t layerCount, uint32_t ycbcrMas
|
|||
.offset = sizeof(uint32_t) * 6,
|
||||
.size = sizeof(uint32_t)
|
||||
},
|
||||
|
||||
{
|
||||
.constantID = 7,
|
||||
.offset = sizeof(uint32_t) * 7,
|
||||
.size = sizeof(uint32_t)
|
||||
},
|
||||
}};
|
||||
|
||||
struct {
|
||||
|
|
@ -1318,8 +1325,7 @@ VkPipeline CVulkanDevice::compilePipeline(uint32_t layerCount, uint32_t ycbcrMas
|
|||
uint32_t debug;
|
||||
uint32_t blur_layer_count;
|
||||
uint32_t colorspace_mask;
|
||||
uint32_t st2084_output;
|
||||
uint32_t force_wide_gammut;
|
||||
uint32_t output_eotf;
|
||||
uint32_t itm_enable;
|
||||
} specializationData = {
|
||||
.layerCount = layerCount,
|
||||
|
|
@ -1327,8 +1333,7 @@ VkPipeline CVulkanDevice::compilePipeline(uint32_t layerCount, uint32_t ycbcrMas
|
|||
.debug = composite_debug,
|
||||
.blur_layer_count = blur_layer_count,
|
||||
.colorspace_mask = colorspace_mask,
|
||||
.st2084_output = st2084_output,
|
||||
.force_wide_gammut = force_wide_gammut,
|
||||
.output_eotf = output_eotf,
|
||||
.itm_enable = itm_enable,
|
||||
};
|
||||
|
||||
|
|
@ -1367,7 +1372,7 @@ void CVulkanDevice::compileAllPipelines()
|
|||
pthread_setname_np( pthread_self(), "gamescope-shdr" );
|
||||
|
||||
std::array<PipelineInfo_t, SHADER_TYPE_COUNT> pipelineInfos;
|
||||
#define SHADER(type, layer_count, max_ycbcr, blur_layers) pipelineInfos[SHADER_TYPE_##type] = {SHADER_TYPE_##type, layer_count, max_ycbcr, blur_layers, false}
|
||||
#define SHADER(type, layer_count, max_ycbcr, blur_layers) pipelineInfos[SHADER_TYPE_##type] = {SHADER_TYPE_##type, layer_count, max_ycbcr, blur_layers}
|
||||
SHADER(BLIT, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, 1);
|
||||
SHADER(BLUR, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, k_nMaxBlurLayers);
|
||||
SHADER(BLUR_COND, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, k_nMaxBlurLayers);
|
||||
|
|
@ -1387,7 +1392,7 @@ void CVulkanDevice::compileAllPipelines()
|
|||
if (blur_layers > layerCount)
|
||||
continue;
|
||||
|
||||
VkPipeline newPipeline = compilePipeline(layerCount, ycbcrMask, info.shaderType, blur_layers, info.compositeDebug, info.colorspaceMask, info.st2084Output, info.forceWideGammut, info.itmEnable);
|
||||
VkPipeline newPipeline = compilePipeline(layerCount, ycbcrMask, info.shaderType, blur_layers, info.compositeDebug, info.colorspaceMask, info.outputEOTF, info.itmEnable);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_pipelineMutex);
|
||||
PipelineInfo_t key = {info.shaderType, layerCount, ycbcrMask, blur_layers, info.compositeDebug};
|
||||
|
|
@ -1401,14 +1406,14 @@ void CVulkanDevice::compileAllPipelines()
|
|||
}
|
||||
}
|
||||
|
||||
VkPipeline CVulkanDevice::pipeline(ShaderType type, uint32_t layerCount, uint32_t ycbcrMask, uint32_t blur_layers, uint32_t colorspace_mask, bool st2084_output, bool force_wide_gammut, bool itm_enable)
|
||||
VkPipeline CVulkanDevice::pipeline(ShaderType type, uint32_t layerCount, uint32_t ycbcrMask, uint32_t blur_layers, uint32_t colorspace_mask, uint32_t output_eotf, bool itm_enable)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_pipelineMutex);
|
||||
PipelineInfo_t key = {type, layerCount, ycbcrMask, blur_layers, g_uCompositeDebug, colorspace_mask, st2084_output, force_wide_gammut, itm_enable};
|
||||
PipelineInfo_t key = {type, layerCount, ycbcrMask, blur_layers, g_uCompositeDebug, colorspace_mask, output_eotf, itm_enable};
|
||||
auto search = m_pipelineMap.find(key);
|
||||
if (search == m_pipelineMap.end())
|
||||
{
|
||||
VkPipeline result = compilePipeline(layerCount, ycbcrMask, type, blur_layers, g_uCompositeDebug, colorspace_mask, st2084_output, force_wide_gammut, itm_enable);
|
||||
VkPipeline result = compilePipeline(layerCount, ycbcrMask, type, blur_layers, g_uCompositeDebug, colorspace_mask, output_eotf, itm_enable);
|
||||
m_pipelineMap[key] = result;
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1600,6 +1605,17 @@ void CVulkanCmdBuffer::bindTexture(uint32_t slot, std::shared_ptr<CVulkanTexture
|
|||
m_textureRefs.emplace(texture.get(), texture);
|
||||
}
|
||||
|
||||
void CVulkanCmdBuffer::bindColorMgmtLuts(uint32_t slot, const std::shared_ptr<CVulkanTexture>& lut1d, const std::shared_ptr<CVulkanTexture>& lut3d)
|
||||
{
|
||||
m_shaperLut[slot] = lut1d.get();
|
||||
m_lut3D[slot] = lut3d.get();
|
||||
|
||||
if (lut1d != nullptr)
|
||||
m_textureRefs.emplace(lut1d.get(), lut1d);
|
||||
if (lut3d != nullptr)
|
||||
m_textureRefs.emplace(lut3d.get(), lut3d);
|
||||
}
|
||||
|
||||
void CVulkanCmdBuffer::setTextureSrgb(uint32_t slot, bool srgb)
|
||||
{
|
||||
m_useSrgb[slot] = srgb;
|
||||
|
|
@ -1660,10 +1676,12 @@ void CVulkanCmdBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z)
|
|||
|
||||
VkDescriptorSet descriptorSet = m_device->descriptorSet();
|
||||
|
||||
std::array<VkWriteDescriptorSet, 4> writeDescriptorSets;
|
||||
std::array<VkWriteDescriptorSet, 6> writeDescriptorSets;
|
||||
std::array<VkDescriptorImageInfo, VKR_SAMPLER_SLOTS> imageDescriptors = {};
|
||||
std::array<VkDescriptorImageInfo, VKR_SAMPLER_SLOTS> ycbcrImageDescriptors = {};
|
||||
std::array<VkDescriptorImageInfo, VKR_TARGET_SLOTS> targetDescriptors = {};
|
||||
std::array<VkDescriptorImageInfo, VKR_LUT3D_COUNT> shaperLutDescriptor = {};
|
||||
std::array<VkDescriptorImageInfo, VKR_LUT3D_COUNT> lut3DDescriptor = {};
|
||||
|
||||
writeDescriptorSets[0] = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
|
|
@ -1705,6 +1723,26 @@ void CVulkanCmdBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z)
|
|||
.pImageInfo = ycbcrImageDescriptors.data(),
|
||||
};
|
||||
|
||||
writeDescriptorSets[4] = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = descriptorSet,
|
||||
.dstBinding = 4,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = shaperLutDescriptor.size(),
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = shaperLutDescriptor.data(),
|
||||
};
|
||||
|
||||
writeDescriptorSets[5] = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = descriptorSet,
|
||||
.dstBinding = 5,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = lut3DDescriptor.size(),
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = lut3DDescriptor.data(),
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < VKR_SAMPLER_SLOTS; i++)
|
||||
{
|
||||
imageDescriptors[i].sampler = m_device->sampler(m_samplerState[i]);
|
||||
|
|
@ -1721,6 +1759,26 @@ void CVulkanCmdBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z)
|
|||
imageDescriptors[i].imageView = view;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < VKR_LUT3D_COUNT; i++)
|
||||
{
|
||||
SamplerState linearState;
|
||||
linearState.bNearest = false;
|
||||
linearState.bUnnormalized = false;
|
||||
SamplerState nearestState; // TODO(Josh): Probably want to do this when I bring in tetrahedral interpolation.
|
||||
nearestState.bNearest = true;
|
||||
nearestState.bUnnormalized = false;
|
||||
|
||||
shaperLutDescriptor[i].sampler = m_device->sampler(linearState);
|
||||
shaperLutDescriptor[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
// TODO(Josh): I hate the fact that srgbView = view *as* raw srgb and treat as linear.
|
||||
// I need to change this, it's so utterly stupid and confusing.
|
||||
shaperLutDescriptor[i].imageView = m_shaperLut[i] ? m_shaperLut[i]->srgbView() : nullptr;
|
||||
|
||||
lut3DDescriptor[i].sampler = m_device->sampler(linearState);
|
||||
lut3DDescriptor[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
lut3DDescriptor[i].imageView = m_lut3D[i] ? m_lut3D[i]->srgbView() : nullptr;
|
||||
}
|
||||
|
||||
if (!m_target->isYcbcr())
|
||||
{
|
||||
targetDescriptors[0].imageView = m_target->srgbView();
|
||||
|
|
@ -1790,7 +1848,7 @@ void CVulkanCmdBuffer::copyBufferToImage(VkBuffer buffer, VkDeviceSize offset, u
|
|||
.imageExtent = {
|
||||
.width = dst->width(),
|
||||
.height = dst->height(),
|
||||
.depth = 1,
|
||||
.depth = dst->depth(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -1954,7 +2012,18 @@ static VkResult getModifierProps( const VkImageCreateInfo *imageInfo, uint64_t m
|
|||
return g_device.vk.GetPhysicalDeviceImageFormatProperties2(g_device.physDev(), &imageFormatInfo, &imageProps);
|
||||
}
|
||||
|
||||
bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t drmFormat, createFlags flags, wlr_dmabuf_attributes *pDMA /* = nullptr */, uint32_t contentWidth /* = 0 */, uint32_t contentHeight /* = 0 */)
|
||||
static VkImageViewType VulkanImageTypeToViewType(VkImageType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VK_IMAGE_TYPE_1D: return VK_IMAGE_VIEW_TYPE_1D;
|
||||
case VK_IMAGE_TYPE_2D: return VK_IMAGE_VIEW_TYPE_2D;
|
||||
case VK_IMAGE_TYPE_3D: return VK_IMAGE_VIEW_TYPE_3D;
|
||||
default: abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool CVulkanTexture::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 */)
|
||||
{
|
||||
VkResult res = VK_ERROR_INITIALIZATION_FAILED;
|
||||
|
||||
|
|
@ -2012,12 +2081,12 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t drmFormat,
|
|||
|
||||
VkImageCreateInfo imageInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.imageType = flags.imageType,
|
||||
.format = DRMFormatToVulkan(drmFormat, false),
|
||||
.extent = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.depth = 1,
|
||||
.depth = depth,
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
|
|
@ -2160,6 +2229,7 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t drmFormat,
|
|||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_depth = depth;
|
||||
|
||||
if (contentWidth && contentHeight)
|
||||
{
|
||||
|
|
@ -2407,7 +2477,7 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t drmFormat,
|
|||
VkImageViewCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = m_vkImage,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.viewType = VulkanImageTypeToViewType(flags.imageType),
|
||||
.format = DRMFormatToVulkan(drmFormat, false),
|
||||
.components = {
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
|
|
@ -2492,6 +2562,7 @@ bool CVulkanTexture::BInitFromSwapchain( VkImage image, uint32_t width, uint32_t
|
|||
m_vkImageMemory = VK_NULL_HANDLE;
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_depth = 1;
|
||||
m_format = format;
|
||||
m_contentWidth = width;
|
||||
m_contentHeight = height;
|
||||
|
|
@ -2750,6 +2821,52 @@ void vulkan_present_to_window( void )
|
|||
vulkan_remake_swapchain();
|
||||
}
|
||||
|
||||
std::shared_ptr<CVulkanTexture> vulkan_create_1d_lut(uint32_t size)
|
||||
{
|
||||
CVulkanTexture::createFlags flags;
|
||||
flags.bSampled = true;
|
||||
flags.bTransferDst = true;
|
||||
flags.imageType = VK_IMAGE_TYPE_1D;
|
||||
|
||||
auto texture = std::make_shared<CVulkanTexture>();
|
||||
auto drmFormat = VulkanFormatToDRM( VK_FORMAT_R16G16B16A16_UNORM );
|
||||
assert( texture->BInit( size, 1u, 1u, drmFormat, flags ) );
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
std::shared_ptr<CVulkanTexture> vulkan_create_3d_lut(uint32_t width, uint32_t height, uint32_t depth)
|
||||
{
|
||||
CVulkanTexture::createFlags flags;
|
||||
flags.bSampled = true;
|
||||
flags.bTransferDst = true;
|
||||
flags.imageType = VK_IMAGE_TYPE_3D;
|
||||
|
||||
auto texture = std::make_shared<CVulkanTexture>();
|
||||
auto drmFormat = VulkanFormatToDRM( VK_FORMAT_R16G16B16A16_UNORM );
|
||||
assert( texture->BInit( width, height, depth, drmFormat, flags ) );
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
void vulkan_update_luts(const std::shared_ptr<CVulkanTexture>& lut1d, const std::shared_ptr<CVulkanTexture>& lut3d, void* lut1d_data, void* lut3d_data)
|
||||
{
|
||||
void* base_dst = g_device.uploadBufferData();
|
||||
|
||||
size_t lut1d_size = lut1d->width() * sizeof(uint16_t) * 4;
|
||||
size_t lut3d_size = lut3d->width() * lut3d->height() * lut3d->depth() * sizeof(uint16_t) * 4;
|
||||
|
||||
void* lut1d_dst = base_dst;
|
||||
void *lut3d_dst = ((uint8_t*)base_dst) + lut1d_size;
|
||||
memcpy(lut1d_dst, lut1d_data, lut1d_size);
|
||||
memcpy(lut3d_dst, lut3d_data, lut3d_size);
|
||||
|
||||
auto cmdBuffer = g_device.commandBuffer();
|
||||
cmdBuffer->copyBufferToImage(g_device.uploadBuffer(), 0, 0, lut1d);
|
||||
cmdBuffer->copyBufferToImage(g_device.uploadBuffer(), lut1d_size, 0, lut3d);
|
||||
g_device.submit(std::move(cmdBuffer));
|
||||
}
|
||||
|
||||
#if HAVE_OPENVR
|
||||
std::shared_ptr<CVulkanTexture> vulkan_create_debug_white_texture()
|
||||
{
|
||||
|
|
@ -2760,7 +2877,7 @@ std::shared_ptr<CVulkanTexture> vulkan_create_debug_white_texture()
|
|||
flags.bLinear = true;
|
||||
|
||||
auto texture = std::make_shared<CVulkanTexture>();
|
||||
assert( texture->BInit( g_nOutputWidth, g_nOutputHeight, VulkanFormatToDRM( VK_FORMAT_B8G8R8A8_UNORM ), flags ) );
|
||||
assert( texture->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM( VK_FORMAT_B8G8R8A8_UNORM ), flags ) );
|
||||
|
||||
memset( texture->mappedData(), 0xFF, texture->width() * texture->height() * 4 );
|
||||
|
||||
|
|
@ -2901,7 +3018,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput )
|
|||
VkFormat format = g_bOutputHDREnabled ? pOutput->outputFormatHDR : pOutput->outputFormat;
|
||||
|
||||
pOutput->outputImages[0] = std::make_shared<CVulkanTexture>();
|
||||
bool bSuccess = pOutput->outputImages[0]->BInit( g_nOutputWidth, g_nOutputHeight, VulkanFormatToDRM(format), outputImageflags );
|
||||
bool bSuccess = pOutput->outputImages[0]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(format), outputImageflags );
|
||||
if ( bSuccess != true )
|
||||
{
|
||||
vk_log.errorf( "failed to allocate buffer for KMS" );
|
||||
|
|
@ -2909,7 +3026,7 @@ static bool vulkan_make_output_images( VulkanOutput_t *pOutput )
|
|||
}
|
||||
|
||||
pOutput->outputImages[1] = std::make_shared<CVulkanTexture>();
|
||||
bSuccess = pOutput->outputImages[1]->BInit( g_nOutputWidth, g_nOutputHeight, VulkanFormatToDRM(format), outputImageflags );
|
||||
bSuccess = pOutput->outputImages[1]->BInit( g_nOutputWidth, g_nOutputHeight, 1u, VulkanFormatToDRM(format), outputImageflags );
|
||||
if ( bSuccess != true )
|
||||
{
|
||||
vk_log.errorf( "failed to allocate buffer for KMS" );
|
||||
|
|
@ -3044,7 +3161,7 @@ static void update_tmp_images( uint32_t width, uint32_t height )
|
|||
createFlags.bStorage = true;
|
||||
|
||||
g_output.tmpOutput = std::make_shared<CVulkanTexture>();
|
||||
bool bSuccess = g_output.tmpOutput->BInit( width, height, DRM_FORMAT_ARGB8888, createFlags, nullptr );
|
||||
bool bSuccess = g_output.tmpOutput->BInit( width, height, 1u, DRM_FORMAT_ARGB8888, createFlags, nullptr );
|
||||
|
||||
if ( !bSuccess )
|
||||
{
|
||||
|
|
@ -3097,7 +3214,7 @@ std::shared_ptr<CVulkanTexture> vulkan_create_texture_from_dmabuf( struct wlr_dm
|
|||
//fprintf(stderr, "pDMA->width: %d pDMA->height: %d pDMA->format: 0x%x pDMA->modifier: 0x%lx pDMA->n_planes: %d\n",
|
||||
// pDMA->width, pDMA->height, pDMA->format, pDMA->modifier, pDMA->n_planes);
|
||||
|
||||
if ( pTex->BInit( pDMA->width, pDMA->height, pDMA->format, texCreateFlags, pDMA ) == false )
|
||||
if ( pTex->BInit( pDMA->width, pDMA->height, 1u, pDMA->format, texCreateFlags, pDMA ) == false )
|
||||
return nullptr;
|
||||
|
||||
return pTex;
|
||||
|
|
@ -3110,7 +3227,7 @@ std::shared_ptr<CVulkanTexture> vulkan_create_texture_from_bits( uint32_t width,
|
|||
texCreateFlags.bSampled = true;
|
||||
texCreateFlags.bTransferDst = true;
|
||||
|
||||
if ( pTex->BInit( width, height, drmFormat, texCreateFlags, nullptr, contentWidth, contentHeight) == false )
|
||||
if ( pTex->BInit( width, height, 1u, drmFormat, texCreateFlags, nullptr, contentWidth, contentHeight) == false )
|
||||
return nullptr;
|
||||
|
||||
memcpy( g_device.uploadBufferData(), bits, width * height * DRMFormatGetBPP(drmFormat) );
|
||||
|
|
@ -3153,7 +3270,7 @@ std::shared_ptr<CVulkanTexture> vulkan_acquire_screenshot_texture(uint32_t width
|
|||
screenshotImageFlags.bStorage = true;
|
||||
}
|
||||
|
||||
bool bSuccess = pScreenshotImage->BInit( width, height, drmFormat, screenshotImageFlags );
|
||||
bool bSuccess = pScreenshotImage->BInit( width, height, 1u, drmFormat, screenshotImageFlags );
|
||||
pScreenshotImage->setStreamColorspace(colorspace);
|
||||
|
||||
assert( bSuccess );
|
||||
|
|
@ -3354,6 +3471,9 @@ bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr<CVul
|
|||
|
||||
auto cmdBuffer = g_device.commandBuffer();
|
||||
|
||||
for (uint32_t i = 0; i < EOTF_Count; i++)
|
||||
cmdBuffer->bindColorMgmtLuts(i, frameInfo->shaperLut[i], frameInfo->lut3D[i]);
|
||||
|
||||
if ( frameInfo->useFSRLayer0 )
|
||||
{
|
||||
uint32_t inputX = frameInfo->layers[0].tex->width();
|
||||
|
|
@ -3443,7 +3563,7 @@ bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr<CVul
|
|||
if (frameInfo->layerCount >= 2 && frameInfo->layers[1].zpos == g_zposOverride)
|
||||
blur_layer_count++;
|
||||
|
||||
cmdBuffer->bindPipeline(g_device.pipeline(type, blur_layer_count, frameInfo->ycbcrMask() & 0x3u, 0, frameInfo->colorspaceMask(), g_bOutputHDREnabled, false, false));
|
||||
cmdBuffer->bindPipeline(g_device.pipeline(type, blur_layer_count, frameInfo->ycbcrMask() & 0x3u, 0, frameInfo->colorspaceMask(), g_ColorMgmt.current.outputEncodingEOTF ));
|
||||
cmdBuffer->bindTarget(g_output.tmpOutput);
|
||||
for (uint32_t i = 0; i < blur_layer_count; i++)
|
||||
{
|
||||
|
|
@ -3459,7 +3579,7 @@ bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr<CVul
|
|||
cmdBuffer->dispatch(div_roundup(currentOutputWidth, pixelsPerGroup), div_roundup(currentOutputHeight, pixelsPerGroup));
|
||||
|
||||
type = frameInfo->blurLayer0 == BLUR_MODE_COND ? SHADER_TYPE_BLUR_COND : SHADER_TYPE_BLUR;
|
||||
cmdBuffer->bindPipeline(g_device.pipeline(type, frameInfo->layerCount, frameInfo->ycbcrMask(), blur_layer_count, frameInfo->colorspaceMask(), g_bOutputHDREnabled, false, false));
|
||||
cmdBuffer->bindPipeline(g_device.pipeline(type, frameInfo->layerCount, frameInfo->ycbcrMask(), blur_layer_count, frameInfo->colorspaceMask(), g_ColorMgmt.current.outputEncodingEOTF ));
|
||||
bind_all_layers(cmdBuffer.get(), frameInfo);
|
||||
cmdBuffer->bindTarget(compositeImage);
|
||||
cmdBuffer->bindTexture(VKR_BLUR_EXTRA_SLOT, g_output.tmpOutput);
|
||||
|
|
@ -3471,7 +3591,7 @@ bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr<CVul
|
|||
}
|
||||
else
|
||||
{
|
||||
cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), g_bOutputHDREnabled, false, g_bOutputHDREnabled ? g_bHDRItmEnable : false));
|
||||
cmdBuffer->bindPipeline( g_device.pipeline(SHADER_TYPE_BLIT, frameInfo->layerCount, frameInfo->ycbcrMask(), 0u, frameInfo->colorspaceMask(), g_ColorMgmt.current.outputEncodingEOTF ));
|
||||
bind_all_layers(cmdBuffer.get(), frameInfo);
|
||||
cmdBuffer->bindTarget(compositeImage);
|
||||
cmdBuffer->pushConstants<BlitPushData_t>(frameInfo);
|
||||
|
|
@ -3729,7 +3849,7 @@ std::shared_ptr<CVulkanTexture> vulkan_create_texture_from_wlr_buffer( struct wl
|
|||
CVulkanTexture::createFlags texCreateFlags;
|
||||
texCreateFlags.bSampled = true;
|
||||
texCreateFlags.bTransferDst = true;
|
||||
if ( pTex->BInit( width, height, drmFormat, texCreateFlags ) == false )
|
||||
if ( pTex->BInit( width, height, 1u, drmFormat, texCreateFlags ) == false )
|
||||
return nullptr;
|
||||
|
||||
auto cmdBuffer = g_device.commandBuffer();
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ public:
|
|||
bLinear = false;
|
||||
bExportable = false;
|
||||
bSwapchain = false;
|
||||
imageType = VK_IMAGE_TYPE_2D;
|
||||
}
|
||||
|
||||
bool bFlippable : 1;
|
||||
|
|
@ -140,9 +141,10 @@ public:
|
|||
bool bLinear : 1;
|
||||
bool bExportable : 1;
|
||||
bool bSwapchain : 1;
|
||||
VkImageType imageType;
|
||||
};
|
||||
|
||||
bool BInit( uint32_t width, uint32_t height, uint32_t drmFormat, createFlags flags, wlr_dmabuf_attributes *pDMA = nullptr, uint32_t contentWidth = 0, uint32_t contentHeight = 0 );
|
||||
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; }
|
||||
|
|
@ -152,6 +154,7 @@ public:
|
|||
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; }
|
||||
|
|
@ -198,6 +201,7 @@ private:
|
|||
|
||||
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;
|
||||
|
|
@ -233,6 +237,8 @@ struct FrameInfo_t
|
|||
BlurMode blurLayer0;
|
||||
int blurRadius;
|
||||
|
||||
std::shared_ptr<CVulkanTexture> shaperLut[EOTF_Count];
|
||||
std::shared_ptr<CVulkanTexture> lut3D[EOTF_Count];
|
||||
|
||||
int layerCount;
|
||||
struct Layer_t
|
||||
|
|
@ -333,6 +339,10 @@ 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>;
|
||||
|
|
@ -365,6 +375,9 @@ struct gamescope_color_mgmt_luts
|
|||
std::vector<uint16_t> lut3d;
|
||||
std::vector<uint16_t> lut1d;
|
||||
|
||||
std::shared_ptr<CVulkanTexture> vk_lut3d;
|
||||
std::shared_ptr<CVulkanTexture> vk_lut1d;
|
||||
|
||||
void reset()
|
||||
{
|
||||
lut3d.clear();
|
||||
|
|
|
|||
|
|
@ -493,24 +493,22 @@ vec4 gaussian_blur(sampler2D layerSampler, uint layerIdx, vec2 pos, uint radius,
|
|||
vec4 tmp0 = textureCond(layerSampler, layerIdx, pos - posOffset, unnormalized) * weights[i];
|
||||
if (vertical)
|
||||
{
|
||||
if (c_st2084Output)
|
||||
tmp0.rgb = pqToNits(tmp0.rgb);
|
||||
tmp0.rgb = colorspace_blend_tf(tmp0.rgb, c_output_eotf);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp0 = convert_to_dst_colorspace(tmp0, get_layer_colorspace(layerIdx));
|
||||
tmp0.rgb = colorspace_output_tf(tmp0.rgb, get_layer_colorspace(layerIdx));
|
||||
}
|
||||
color += tmp0;
|
||||
|
||||
vec4 tmp1 = textureCond(layerSampler, layerIdx, pos + posOffset, unnormalized) * weights[i];
|
||||
if (vertical)
|
||||
{
|
||||
if (c_st2084Output)
|
||||
tmp1.rgb = pqToNits(tmp1.rgb);
|
||||
tmp1.rgb = colorspace_blend_tf(tmp1.rgb, c_output_eotf);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp1 = convert_to_dst_colorspace(tmp1, get_layer_colorspace(layerIdx));
|
||||
tmp1.rgb = colorspace_output_tf(tmp1.rgb, get_layer_colorspace(layerIdx));
|
||||
}
|
||||
color += tmp1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,18 @@ vec3 pqToNits(vec3 pq) {
|
|||
return 10000.0 * pow(num / den, vec3(oo_m1));
|
||||
}
|
||||
|
||||
// does NOT change primaries, just
|
||||
// the pq value in nits / 80.0f!
|
||||
vec3 pqToScRGBEncoding(vec3 pq)
|
||||
{
|
||||
return pqToNits(pq) / 80.0f;
|
||||
}
|
||||
|
||||
vec3 scRGBEncodingToPQ(vec3 scRGBEncodedValue)
|
||||
{
|
||||
return pqToNits(scRGBEncodedValue * 80.0f);
|
||||
}
|
||||
|
||||
// This is apparently defined at 80 nits...
|
||||
// May want to take liberties with this when displaying
|
||||
// on SDR though... 100 may be a better fit for most content
|
||||
|
|
@ -134,39 +146,6 @@ vec3 convert_primaries(vec3 color, mat3 src_to_xyz, mat3 xyz_to_dst) {
|
|||
return color * mat3(src_to_xyz * xyz_to_dst);
|
||||
}
|
||||
|
||||
const float A = 0.15f;
|
||||
const float B = 0.50f;
|
||||
const float C = 0.10f;
|
||||
const float D = 0.20f;
|
||||
const float E = 0.02f;
|
||||
const float F = 0.30f;
|
||||
const float W = 11.2f;
|
||||
|
||||
vec3 Uncharted2Tonemap(vec3 x) {
|
||||
return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
|
||||
}
|
||||
|
||||
float Uncharted2Tonemap(float x) {
|
||||
return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
|
||||
}
|
||||
|
||||
vec3 tonemap_filmic(vec3 color) {
|
||||
vec3 curr = Uncharted2Tonemap(color);
|
||||
float white_scale = 1.0 / Uncharted2Tonemap(W);
|
||||
return curr * white_scale;
|
||||
}
|
||||
|
||||
vec3 tonemap_reinhard(vec3 color) {
|
||||
color = color / (1.0 + color);
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 tonemap(vec3 color) {
|
||||
if (checkDebugFlag(compositedebug_Tonemap_Reinhard))
|
||||
return tonemap_reinhard(color);
|
||||
return tonemap_filmic(color);
|
||||
}
|
||||
|
||||
// Rep. ITU-R BT.2446-1 Table 2-4 (inversed)
|
||||
// BT.2446 Method A inverse tone mapping (itm)
|
||||
vec3 bt2446a_inverse_tonemapping(
|
||||
|
|
@ -327,70 +306,117 @@ vec3 bt2446a_inverse_tonemapping(
|
|||
|
||||
// Generic helper
|
||||
|
||||
vec4 convert_to_dst_colorspace(vec4 color, uint colorspace)
|
||||
{
|
||||
if (colorspace == colorspace_pq) {
|
||||
color.rgb = pqToNits(color.rgb);
|
||||
vec3 colorspace_plane_degamma_tf(vec3 color, uint colorspace) {
|
||||
// matches with colorspace_to_plane_degamma_tf in drm.cpp
|
||||
|
||||
if (checkDebugFlag(compositedebug_Heatmap)) {
|
||||
color.rgb = hdr_heatmap(color.rgb, true, true, c_st2084Output);
|
||||
} else {
|
||||
if (!c_st2084Output) {
|
||||
// HDR10 ST2084 is rec2020.
|
||||
color.rgb = convert_primaries(color.rgb, rec2020_to_xyz, xyz_to_rec709);
|
||||
color.rgb = nitsToLinear(color.rgb);
|
||||
color.rgb = tonemap(color.rgb);
|
||||
}
|
||||
}
|
||||
} else if (colorspace == colorspace_scRGB) {
|
||||
color.rgb = scrgbToNits(color.rgb);
|
||||
switch (colorspace) {
|
||||
default: return vec3(1, 1, 0); // should never happen
|
||||
|
||||
if (checkDebugFlag(compositedebug_Heatmap)) {
|
||||
color.rgb = hdr_heatmap(color.rgb, false, true, c_st2084Output);
|
||||
} else {
|
||||
if (!c_st2084Output) {
|
||||
// scRGB is rec709.
|
||||
color.rgb = nitsToLinear(color.rgb);
|
||||
color.rgb = tonemap(color.rgb);
|
||||
} else {
|
||||
// scRGB is rec709.
|
||||
// ST2084 output needs rec2020.
|
||||
color.rgb = convert_primaries(color.rgb, rec709_to_xyz, xyz_to_rec2020);
|
||||
}
|
||||
}
|
||||
} else if (colorspace == colorspace_sRGB) {
|
||||
color.rgb = srgbToLinear(color.rgb);
|
||||
|
||||
if(c_itmEnable) {
|
||||
if (!c_forceWideGammut)
|
||||
color.rgb = convert_primaries(color.rgb, rec709_to_xyz, xyz_to_rec2020);
|
||||
color.rgb = bt2446a_inverse_tonemapping(color.rgb, u_itmSdrNits, u_itmTargetNits);
|
||||
}
|
||||
if (checkDebugFlag(compositedebug_Heatmap)) {
|
||||
color.rgb = hdr_heatmap(color.rgb, c_itmEnable, c_itmEnable, c_st2084Output);
|
||||
} else {
|
||||
if (!c_itmEnable && c_st2084Output) {
|
||||
color.rgb = linearToNits(color.rgb);
|
||||
if (!c_forceWideGammut)
|
||||
color.rgb = convert_primaries(color.rgb, rec709_to_xyz, xyz_to_rec2020);
|
||||
}
|
||||
}
|
||||
} else if (colorspace == colorspace_linear) {
|
||||
if(c_itmEnable) {
|
||||
if (!c_forceWideGammut)
|
||||
color.rgb = convert_primaries(color.rgb, rec709_to_xyz, xyz_to_rec2020);
|
||||
color.rgb = bt2446a_inverse_tonemapping(color.rgb, u_itmSdrNits, u_itmTargetNits);
|
||||
}
|
||||
if (checkDebugFlag(compositedebug_Heatmap)) {
|
||||
color.rgb = hdr_heatmap(color.rgb, c_itmEnable, c_itmEnable, c_st2084Output);
|
||||
} else {
|
||||
if (!c_itmEnable && c_st2084Output) {
|
||||
color.rgb = linearToNits(color.rgb);
|
||||
if (!c_forceWideGammut)
|
||||
color.rgb = convert_primaries(color.rgb, rec709_to_xyz, xyz_to_rec2020);
|
||||
}
|
||||
}
|
||||
case colorspace_linear: // Using sRGB image view. Unlike DRM which doesn't get that liberty for scanout.
|
||||
case colorspace_scRGB:
|
||||
return color;
|
||||
case colorspace_sRGB:
|
||||
return srgbToLinear(color);
|
||||
case colorspace_pq:
|
||||
return pqToScRGBEncoding(color);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 colorspace_plane_shaper_tf(vec3 color, uint colorspace) {
|
||||
// matches with colorspace_to_plane_regamma_tf in drm.cpp
|
||||
|
||||
switch (colorspace) {
|
||||
default: return vec3(0, 1, 1); // should never happen
|
||||
|
||||
case colorspace_linear:
|
||||
case colorspace_sRGB:
|
||||
return linearToSrgb(color);
|
||||
case colorspace_scRGB:
|
||||
case colorspace_pq:
|
||||
return scRGBEncodingToPQ(color);
|
||||
}
|
||||
}
|
||||
|
||||
// pre-blend doing display EOTF -> display linearized
|
||||
vec3 colorspace_blend_tf(vec3 color, uint eotf) {
|
||||
switch (eotf) {
|
||||
default: return vec3(1, 0, 0); // should never happen
|
||||
|
||||
// Note from Josh:
|
||||
//
|
||||
// We are kinda halfway between output space and not at this point
|
||||
// the color primaries, gamut remapping has already been performed
|
||||
// in display output 2.2 space, but that doesn't change the fact
|
||||
// that we haven't displayed it yet!
|
||||
//
|
||||
// Perform the alpha blending with sRGB linearization (like the CONTENT specifies) here
|
||||
// the primaries and gamut remapping transformations we performed in output 2.2 space do NOT matter.
|
||||
// This is more correct than using gamma 2.2 for that here.
|
||||
case EOTF_Gamma22:
|
||||
return srgbToLinear(color);
|
||||
case EOTF_PQ:
|
||||
return pqToScRGBEncoding(color);
|
||||
}
|
||||
}
|
||||
|
||||
// post blend doing display linearized -> display EOTF
|
||||
vec3 colorspace_output_tf(vec3 color, uint eotf) {
|
||||
switch (eotf) {
|
||||
default: return vec3(0, 1, 0); // should never happen
|
||||
|
||||
// see comment in colorspace_blend_tf
|
||||
case EOTF_Gamma22:
|
||||
return linearToSrgb(color);
|
||||
case EOTF_PQ:
|
||||
return scRGBEncodingToPQ(color);
|
||||
}
|
||||
}
|
||||
|
||||
// matches how we treat content here :)
|
||||
uint colorspace_to_eotf(uint colorspace)
|
||||
{
|
||||
// matches with ColorSpaceToEOTFIndex in drm.cpp
|
||||
switch ( colorspace )
|
||||
{
|
||||
default:
|
||||
case colorspace_linear: // Not actually linear, just Linear vs sRGB image views in Vulkan. Still viewed as sRGB on the DRM side.
|
||||
case colorspace_sRGB:
|
||||
// SDR sRGB content treated as native Gamma 22 curve. No need to do sRGB -> 2.2 or whatever.
|
||||
return EOTF_Gamma22;
|
||||
case colorspace_scRGB:
|
||||
// Okay, so this is WEIRD right? OKAY Let me explain it to you.
|
||||
// The plan for scRGB content is to go from scRGB -> PQ in a SHAPER_TF
|
||||
// before indexing into the shaper.
|
||||
return EOTF_PQ;
|
||||
case colorspace_pq:
|
||||
return EOTF_PQ;
|
||||
}
|
||||
}
|
||||
|
||||
float half_texel_scale(float x, float half_texel)
|
||||
{
|
||||
return mix(0.0f + half_texel, 1.0f - half_texel, x);
|
||||
}
|
||||
|
||||
vec3 half_texel_scale(vec3 x, vec3 half_texel)
|
||||
{
|
||||
return mix(vec3(0.0f) + half_texel, vec3(1.0f) - half_texel, x);
|
||||
}
|
||||
|
||||
vec3 perform_1dlut(vec3 color, sampler1D shaperLUT) {
|
||||
int size = textureSize(shaperLUT, 0);
|
||||
float offset = 0.5f / float(size);
|
||||
|
||||
return vec3(
|
||||
textureLod(shaperLUT, half_texel_scale(color.r, offset), 0.0f).r,
|
||||
textureLod(shaperLUT, half_texel_scale(color.g, offset), 0.0f).g,
|
||||
textureLod(shaperLUT, half_texel_scale(color.b, offset), 0.0f).b);
|
||||
}
|
||||
|
||||
vec3 perform_3dlut(vec3 color, sampler3D lut3D) {
|
||||
ivec3 size = textureSize(lut3D, 0);
|
||||
vec3 offset = 0.5f / vec3(float(size.x), float(size.y), float(size.z));
|
||||
|
||||
return textureLod(lut3D, half_texel_scale(color.rgb, offset), 0.0f).rgb;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,45 @@ void compositing_debug(uvec2 coord) {
|
|||
}
|
||||
}
|
||||
|
||||
vec3 layerColorFromColorspaceToScRGB(vec3 color, uint layerIdx) {
|
||||
uint colorspace = get_layer_colorspace(layerIdx);
|
||||
|
||||
color = colorspace_plane_degamma_tf(color, colorspace);
|
||||
|
||||
if (c_itm_enable)
|
||||
{
|
||||
color = bt2446a_inverse_tonemapping(color, u_itmSdrNits, u_itmTargetNits);
|
||||
colorspace = colorspace_pq;
|
||||
}
|
||||
|
||||
if (checkDebugFlag(compositedebug_Heatmap))
|
||||
{
|
||||
// Debug HDR heatmap.
|
||||
color = hdr_heatmap(color, colorspace);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shaper + 3D LUT path to match DRM.
|
||||
|
||||
uint plane_eotf = colorspace_to_eotf(colorspace);
|
||||
|
||||
color = colorspace_plane_shaper_tf(color, colorspace);
|
||||
// The shaper TF is basically just a regamma to get into something the shaper LUT can handle.
|
||||
//
|
||||
// Despite naming, degamma + shaper TF are NOT necessarily the inverse of each other. ^^^
|
||||
// This gets the color ready to go into the shaper LUT.
|
||||
// ie. scRGB -> PQ
|
||||
//
|
||||
// We also need to do degamma here for non-linear views to blend in linear space.
|
||||
// ie. PQ -> PQ would need us to manually do bilinear here.
|
||||
color = perform_1dlut(color, s_shaperLut[plane_eotf]);
|
||||
color = perform_3dlut(color, s_lut3D[plane_eotf]);
|
||||
color = colorspace_blend_tf(color, c_output_eotf);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 sampleLayer(sampler2D layerSampler, uint layerIdx, vec2 uv, bool unnormalized) {
|
||||
vec2 coord = ((uv + u_offset[layerIdx]) * u_scale[layerIdx]);
|
||||
vec2 texSize = textureSize(layerSampler, 0);
|
||||
|
|
@ -43,15 +82,14 @@ vec4 sampleLayer(sampler2D layerSampler, uint layerIdx, vec2 uv, bool unnormaliz
|
|||
|
||||
vec4 color = textureLod(layerSampler, coord, 0.0f);
|
||||
|
||||
return convert_to_dst_colorspace(color, get_layer_colorspace(layerIdx));
|
||||
// TODO(Josh): If colorspace != linear, emulate bilinear ourselves to blend
|
||||
// in linear space!
|
||||
// Split this into two parts!
|
||||
color.rgb = layerColorFromColorspaceToScRGB(color.rgb, layerIdx);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 encodeOutputColor(vec3 linearOrNits) {
|
||||
if (!c_st2084Output) {
|
||||
// linearOrNits -> linear
|
||||
return linearToSrgb(linearOrNits.rgb);
|
||||
} else {
|
||||
// linearOrNits -> nits
|
||||
return nitsToPq(linearOrNits.rgb);
|
||||
}
|
||||
vec3 encodeOutputColor(vec3 value) {
|
||||
return colorspace_output_tf(value, c_output_eotf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,18 +59,13 @@ void rcasComposite(uvec2 pos)
|
|||
if (all(lessThan(rcasPos, layer0Extent))) {
|
||||
FsrRcasF(outputValue.r, outputValue.g, outputValue.b, rcasPos, u_c1.xxxx);
|
||||
|
||||
if (c_layerCount == 1) {
|
||||
// Technically the wrong color space if you just have one layer.
|
||||
// but doing srgb -> linear -> srgb just for scaling is silly. This is good enough.
|
||||
outputValue *= u_opacity[0];
|
||||
}
|
||||
outputValue.rgb = layerColorFromColorspaceToScRGB(outputValue.rgb, 0);
|
||||
outputValue *= u_opacity[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (c_layerCount > 1) {
|
||||
outputValue = srgbToLinear(outputValue);
|
||||
outputValue *= u_opacity[0];
|
||||
vec2 uv = vec2(pos);
|
||||
|
||||
for (int i = 1; i < c_layerCount; i++) {
|
||||
|
|
@ -79,10 +74,9 @@ void rcasComposite(uvec2 pos)
|
|||
float layerAlpha = opacity * layerColor.a;
|
||||
outputValue = layerColor.rgb * opacity + outputValue * (1.0f - layerAlpha);
|
||||
}
|
||||
|
||||
outputValue = linearToSrgb(outputValue);
|
||||
}
|
||||
|
||||
outputValue = encodeOutputColor(outputValue);
|
||||
imageStore(dst, ivec2(pos), vec4(outputValue, 0));
|
||||
|
||||
if (checkDebugFlag(compositedebug_Markers))
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ layout(constant_id = 2) const uint c_compositing_debug = 0;
|
|||
layout(constant_id = 3) const int c_blur_layer_count = 0;
|
||||
|
||||
layout(constant_id = 4) const uint c_colorspaceMask = 0;
|
||||
layout(constant_id = 5) const bool c_st2084Output = false;
|
||||
layout(constant_id = 6) const bool c_forceWideGammut = false;
|
||||
layout(constant_id = 7) const bool c_itmEnable = false;
|
||||
layout(constant_id = 5) const uint c_output_eotf = 0;
|
||||
layout(constant_id = 7) const bool c_itm_enable = false;
|
||||
|
||||
const int colorspace_linear = 0;
|
||||
const int colorspace_sRGB = 1;
|
||||
|
|
@ -17,6 +16,10 @@ const int colorspace_pq = 3;
|
|||
const int colorspace_reserved = 3;
|
||||
const int colorspace_max_bits = 2;
|
||||
|
||||
const int EOTF_Gamma22 = 0;
|
||||
const int EOTF_PQ = 1;
|
||||
const int EOTF_Count = 2;
|
||||
|
||||
const uint compositedebug_Markers = 1u << 0;
|
||||
const uint compositedebug_PlaneBorders = 1u << 1;
|
||||
const uint compositedebug_Heatmap = 1u << 2;
|
||||
|
|
@ -38,3 +41,6 @@ layout(binding = 1, rgba8) writeonly uniform image2D dst_chroma;
|
|||
|
||||
layout(binding = 2) uniform sampler2D s_samplers[VKR_SAMPLER_SLOTS];
|
||||
layout(binding = 3) uniform sampler2D s_ycbcr_samplers[VKR_SAMPLER_SLOTS];
|
||||
|
||||
layout(binding = 4) uniform sampler1D s_shaperLut[VKR_LUT3D_COUNT];
|
||||
layout(binding = 5) uniform sampler3D s_lut3D[VKR_LUT3D_COUNT];
|
||||
|
|
|
|||
|
|
@ -9,4 +9,6 @@
|
|||
#define VKR_NIS_COEF_SCALER_SLOT (VKR_BLUR_EXTRA_SLOT + 1u)
|
||||
#define VKR_NIS_COEF_USM_SLOT (VKR_NIS_COEF_SCALER_SLOT + 1u)
|
||||
|
||||
#define VKR_LUT3D_COUNT 2 // Must match EOTF_Count
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -159,15 +159,19 @@ vec3 hdr_heatmap_lilium_impl(float Y) {
|
|||
return outColor;
|
||||
}
|
||||
|
||||
vec3 hdr_heatmap(vec3 inputColor, bool in_2020, bool in_nits, bool out_nits) {
|
||||
bool colorspace_is_2020(uint colorspace)
|
||||
{
|
||||
return colorspace == colorspace_pq;
|
||||
}
|
||||
|
||||
vec3 hdr_heatmap(vec3 inputColor, uint colorspace)
|
||||
{
|
||||
vec3 xyz;
|
||||
|
||||
// If the input is SDR, then just multiply by 100 (typical sRGB mastering)
|
||||
// don't multiply by our typical SDR scale.
|
||||
if (!in_nits)
|
||||
inputColor *= 100.0f;
|
||||
// scRGB encoding (linear / 80.0f) -> nits;
|
||||
inputColor *= 80.0f;
|
||||
|
||||
if (in_2020)
|
||||
if (colorspace_is_2020(colorspace))
|
||||
xyz = inputColor * rec2020_to_xyz;
|
||||
else
|
||||
xyz = inputColor * rec709_to_xyz;
|
||||
|
|
@ -178,11 +182,10 @@ vec3 hdr_heatmap(vec3 inputColor, bool in_2020, bool in_nits, bool out_nits) {
|
|||
else
|
||||
outputColor = hdr_heatmap_lilium_impl(xyz.y); // Lilium Heatmap
|
||||
|
||||
if (c_st2084Output)
|
||||
outputColor = convert_primaries(outputColor, rec709_to_xyz, xyz_to_rec2020);
|
||||
|
||||
if (out_nits)
|
||||
outputColor *= u_linearToNits;
|
||||
// Brighten the heatmap up to something reasonable.
|
||||
// If we care enough we could use some setting here someday.
|
||||
if (c_output_eotf == EOTF_PQ)
|
||||
outputColor *= 400.0f / 80.0f; // 400 nit as scRGB TF for blend space.
|
||||
|
||||
return outputColor;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,16 +142,22 @@ update_color_mgmt()
|
|||
const displaycolorimetry_t& displayColorimetry = g_ColorMgmt.pending.displayColorimetry;
|
||||
const displaycolorimetry_t& outputEncodingColorimetry = g_ColorMgmt.pending.outputEncodingColorimetry;
|
||||
|
||||
std::vector<uint16_t> lut3d;
|
||||
uint32_t nLutEdgeSize3d = 17;
|
||||
lut3d.resize( nLutEdgeSize3d*nLutEdgeSize3d*nLutEdgeSize3d*4 );
|
||||
|
||||
std::vector<uint16_t> lut1d;
|
||||
uint32_t nLutSize1d = 4096;
|
||||
lut1d.resize( nLutSize1d*4 );
|
||||
|
||||
for ( uint32_t nInputEOTF = 0; nInputEOTF < EOTF_Count; nInputEOTF++ )
|
||||
{
|
||||
std::vector<uint16_t> lut3d;
|
||||
uint32_t nLutEdgeSize3d = 17;
|
||||
lut3d.resize( nLutEdgeSize3d*nLutEdgeSize3d*nLutEdgeSize3d*4 );
|
||||
|
||||
std::vector<uint16_t> lut1d;
|
||||
uint32_t nLutSize1d = 4096;
|
||||
lut1d.resize( nLutSize1d*4 );
|
||||
|
||||
if (!g_ColorMgmtLuts[nInputEOTF].vk_lut1d)
|
||||
g_ColorMgmtLuts[nInputEOTF].vk_lut1d = vulkan_create_1d_lut(4096);
|
||||
|
||||
if (!g_ColorMgmtLuts[nInputEOTF].vk_lut3d)
|
||||
g_ColorMgmtLuts[nInputEOTF].vk_lut3d = vulkan_create_3d_lut(17, 17, 17);
|
||||
|
||||
displaycolorimetry_t inputColorimetry{};
|
||||
colormapping_t colorMapping{};
|
||||
|
||||
|
|
@ -217,12 +223,16 @@ update_color_mgmt()
|
|||
}
|
||||
else if ( !lut3d.empty() && !lut1d.empty() )
|
||||
{
|
||||
g_ColorMgmtLuts[nInputEOTF] = gamescope_color_mgmt_luts{ lut3d, lut1d };
|
||||
g_ColorMgmtLuts[nInputEOTF].lut3d = std::move(lut3d);
|
||||
g_ColorMgmtLuts[nInputEOTF].lut1d = std::move(lut1d);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ColorMgmtLuts[nInputEOTF].reset();
|
||||
}
|
||||
|
||||
if (!g_ColorMgmtLuts[nInputEOTF].lut3d.empty() && !g_ColorMgmtLuts[nInputEOTF].lut1d.empty())
|
||||
vulkan_update_luts(g_ColorMgmtLuts[nInputEOTF].vk_lut1d, g_ColorMgmtLuts[nInputEOTF].vk_lut3d, g_ColorMgmtLuts[nInputEOTF].lut1d.data(), g_ColorMgmtLuts[nInputEOTF].lut3d.data());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -2225,12 +2235,10 @@ paint_all(bool async)
|
|||
bNeedsComposite |= bNeedsNearest;
|
||||
bNeedsComposite |= bDrewCursor;
|
||||
|
||||
// Disable FSR if outputting HDR or not SDR colorspace game.
|
||||
// We can fix the former at some point, but the latter is much harder.
|
||||
if ( g_bOutputHDREnabled || !( frameInfo.layers[0].colorspace == GAMESCOPE_APP_TEXTURE_COLORSPACE_SRGB || frameInfo.layers[0].colorspace == GAMESCOPE_APP_TEXTURE_COLORSPACE_LINEAR ) )
|
||||
for (uint32_t i = 0; i < EOTF_Count; i++)
|
||||
{
|
||||
frameInfo.useFSRLayer0 = false;
|
||||
frameInfo.useNISLayer0 = false;
|
||||
frameInfo.shaperLut[i] = g_ColorMgmtLuts[i].vk_lut1d;
|
||||
frameInfo.lut3D[i] = g_ColorMgmtLuts[i].vk_lut3d;
|
||||
}
|
||||
|
||||
if ( !BIsNested() && g_bOutputHDREnabled )
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue