rendervulkan: Add NV12 capture blit infra
Performs blit + rgb -> nv12 in one.
This commit is contained in:
parent
dba8480d18
commit
30519fd9ec
8 changed files with 233 additions and 54 deletions
|
@ -97,7 +97,8 @@ shader_src = [
|
|||
'src/shaders/cs_easu_fp16.comp',
|
||||
'src/shaders/cs_gaussian_blur_horizontal.comp',
|
||||
'src/shaders/cs_nis.comp',
|
||||
'src/shaders/cs_nis_fp16.comp'
|
||||
'src/shaders/cs_nis_fp16.comp',
|
||||
'src/shaders/cs_rgb_to_nv12.comp',
|
||||
]
|
||||
|
||||
spirv_shaders = glsl_generator.process(shader_src)
|
||||
|
|
|
@ -355,7 +355,7 @@ static void stream_handle_add_buffer(void *user_data, struct pw_buffer *pw_buffe
|
|||
bool is_dmabuf = (spa_data->type & (1 << SPA_DATA_DmaBuf)) != 0;
|
||||
bool is_memfd = (spa_data->type & (1 << SPA_DATA_MemFd)) != 0;
|
||||
|
||||
buffer->texture = vulkan_acquire_screenshot_texture(s_nCaptureWidth, s_nCaptureHeight, is_dmabuf);
|
||||
buffer->texture = vulkan_acquire_screenshot_texture(s_nCaptureWidth, s_nCaptureHeight, is_dmabuf, false);
|
||||
assert(buffer->texture != nullptr);
|
||||
|
||||
if (is_dmabuf) {
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include "cs_gaussian_blur_horizontal.h"
|
||||
#include "cs_nis.h"
|
||||
#include "cs_nis_fp16.h"
|
||||
|
||||
#include "cs_rgb_to_nv12.h"
|
||||
|
||||
#define A_CPU
|
||||
#include "shaders/ffx_a.h"
|
||||
|
@ -88,6 +88,7 @@ enum ShaderType {
|
|||
SHADER_TYPE_EASU,
|
||||
SHADER_TYPE_RCAS,
|
||||
SHADER_TYPE_NIS,
|
||||
SHADER_TYPE_RGB_TO_NV12,
|
||||
|
||||
SHADER_TYPE_COUNT
|
||||
};
|
||||
|
@ -335,6 +336,7 @@ public:
|
|||
void begin();
|
||||
void end();
|
||||
void bindTexture(uint32_t slot, std::shared_ptr<CVulkanTexture> texture);
|
||||
void setTextureStorage(bool storage);
|
||||
void setTextureSrgb(uint32_t slot, bool srgb);
|
||||
void setSamplerNearest(uint32_t slot, bool nearest);
|
||||
void setSamplerUnnormalized(uint32_t slot, bool unnormalized);
|
||||
|
@ -942,7 +944,7 @@ bool CVulkanDevice::createLayouts()
|
|||
for (auto& sampler : ycbcrSamplers)
|
||||
sampler = m_ycbcrSampler;
|
||||
|
||||
std::array<VkDescriptorSetLayoutBinding, 3> layoutBindings = {
|
||||
std::array<VkDescriptorSetLayoutBinding, 4> layoutBindings = {
|
||||
VkDescriptorSetLayoutBinding {
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
|
@ -951,12 +953,18 @@ bool CVulkanDevice::createLayouts()
|
|||
},
|
||||
VkDescriptorSetLayoutBinding {
|
||||
.binding = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
},
|
||||
VkDescriptorSetLayoutBinding {
|
||||
.binding = 2,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = VKR_SAMPLER_SLOTS,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
},
|
||||
VkDescriptorSetLayoutBinding {
|
||||
.binding = 2,
|
||||
.binding = 3,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = VKR_SAMPLER_SLOTS,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
|
@ -1020,7 +1028,7 @@ bool CVulkanDevice::createPools()
|
|||
VkDescriptorPoolSize poolSizes[2] {
|
||||
{
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
uint32_t(m_descriptorSets.size()) * 1,
|
||||
uint32_t(m_descriptorSets.size()) * 2,
|
||||
},
|
||||
{
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
|
@ -1070,6 +1078,7 @@ bool CVulkanDevice::createShaders()
|
|||
SHADER(EASU, cs_easu);
|
||||
SHADER(NIS, cs_nis);
|
||||
}
|
||||
SHADER(RGB_TO_NV12, cs_rgb_to_nv12);
|
||||
#undef SHADER
|
||||
|
||||
for (uint32_t i = 0; i < shaderInfos.size(); i++)
|
||||
|
@ -1276,6 +1285,7 @@ void CVulkanDevice::compileAllPipelines()
|
|||
SHADER(RCAS, k_nMaxLayers, k_nMaxYcbcrMask_ToPreCompile, 1);
|
||||
SHADER(EASU, 1, 1, 1);
|
||||
SHADER(NIS, 1, 1, 1);
|
||||
SHADER(RGB_TO_NV12, 1, 1, 1);
|
||||
#undef SHADER
|
||||
|
||||
for (auto& info : pipelineInfos) {
|
||||
|
@ -1560,13 +1570,10 @@ void CVulkanCmdBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z)
|
|||
|
||||
VkDescriptorSet descriptorSet = m_device->descriptorSet();
|
||||
|
||||
std::array<VkWriteDescriptorSet, 3> writeDescriptorSets;
|
||||
std::array<VkWriteDescriptorSet, 4> writeDescriptorSets;
|
||||
std::array<VkDescriptorImageInfo, VKR_SAMPLER_SLOTS> imageDescriptors = {};
|
||||
std::array<VkDescriptorImageInfo, VKR_SAMPLER_SLOTS> ycbcrImageDescriptors = {};
|
||||
VkDescriptorImageInfo targetDescriptor = {
|
||||
.imageView = m_target->srgbView(),
|
||||
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
};
|
||||
std::array<VkDescriptorImageInfo, VKR_TARGET_SLOTS> targetDescriptors = {};
|
||||
|
||||
writeDescriptorSets[0] = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
|
@ -1575,7 +1582,7 @@ void CVulkanCmdBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z)
|
|||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.pImageInfo = &targetDescriptor,
|
||||
.pImageInfo = &targetDescriptors[0],
|
||||
};
|
||||
|
||||
writeDescriptorSets[1] = {
|
||||
|
@ -1583,9 +1590,9 @@ void CVulkanCmdBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z)
|
|||
.dstSet = descriptorSet,
|
||||
.dstBinding = 1,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = imageDescriptors.size(),
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = imageDescriptors.data(),
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.pImageInfo = &targetDescriptors[1],
|
||||
};
|
||||
|
||||
writeDescriptorSets[2] = {
|
||||
|
@ -1593,6 +1600,16 @@ void CVulkanCmdBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z)
|
|||
.dstSet = descriptorSet,
|
||||
.dstBinding = 2,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = imageDescriptors.size(),
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = imageDescriptors.data(),
|
||||
};
|
||||
|
||||
writeDescriptorSets[3] = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = descriptorSet,
|
||||
.dstBinding = 3,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = ycbcrImageDescriptors.size(),
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = ycbcrImageDescriptors.data(),
|
||||
|
@ -1614,6 +1631,20 @@ void CVulkanCmdBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z)
|
|||
imageDescriptors[i].imageView = view;
|
||||
}
|
||||
|
||||
if (!m_target->isYcbcr())
|
||||
{
|
||||
targetDescriptors[0].imageView = m_target->srgbView();
|
||||
targetDescriptors[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetDescriptors[0].imageView = m_target->lumaView();
|
||||
targetDescriptors[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
|
||||
targetDescriptors[1].imageView = m_target->chromaView();
|
||||
targetDescriptors[1].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
|
||||
m_device->vk.UpdateDescriptorSets(m_device->device(), writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr);
|
||||
|
||||
m_device->vk.CmdBindDescriptorSets(m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_device->pipelineLayout(), 0, 1, &descriptorSet, 0, nullptr);
|
||||
|
@ -2067,6 +2098,8 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t drmFormat,
|
|||
.memoryTypeIndex = uint32_t(g_device.findMemoryType(properties, memRequirements.memoryTypeBits)),
|
||||
};
|
||||
|
||||
m_size = allocInfo.allocationSize;
|
||||
|
||||
if ( flags.bExportable == true || pDMA != nullptr )
|
||||
{
|
||||
memory_dedicated_info = {
|
||||
|
@ -2291,6 +2324,31 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, uint32_t drmFormat,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( isYcbcr() )
|
||||
{
|
||||
createInfo.pNext = NULL;
|
||||
createInfo.format = VK_FORMAT_R8_UNORM;
|
||||
|
||||
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT;
|
||||
res = vkCreateImageView(g_device.device(), &createInfo, nullptr, &m_lumaView);
|
||||
if ( res != VK_SUCCESS ) {
|
||||
vk_errorf( res, "vkCreateImageView failed" );
|
||||
return false;
|
||||
}
|
||||
|
||||
createInfo.pNext = NULL;
|
||||
createInfo.format = VK_FORMAT_R8G8_UNORM;
|
||||
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT;
|
||||
res = vkCreateImageView(g_device.device(), &createInfo, nullptr, &m_chromaView);
|
||||
if ( res != VK_SUCCESS ) {
|
||||
vk_errorf( res, "vkCreateImageView failed" );
|
||||
return false;
|
||||
}
|
||||
|
||||
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
if ( flags.bMappable )
|
||||
|
@ -2904,7 +2962,7 @@ void vulkan_garbage_collect( void )
|
|||
g_device.garbageCollect();
|
||||
}
|
||||
|
||||
std::shared_ptr<CVulkanTexture> vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable)
|
||||
std::shared_ptr<CVulkanTexture> vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable, bool nv12)
|
||||
{
|
||||
for (auto& pScreenshotImage : g_output.pScreenshotImages)
|
||||
{
|
||||
|
@ -2915,13 +2973,13 @@ std::shared_ptr<CVulkanTexture> vulkan_acquire_screenshot_texture(uint32_t width
|
|||
CVulkanTexture::createFlags screenshotImageFlags;
|
||||
screenshotImageFlags.bMappable = true;
|
||||
screenshotImageFlags.bTransferDst = true;
|
||||
if (exportable) {
|
||||
if (exportable || nv12) {
|
||||
screenshotImageFlags.bExportable = true;
|
||||
screenshotImageFlags.bLinear = true; // TODO: support multi-planar DMA-BUF export via PipeWire
|
||||
screenshotImageFlags.bStorage = true;
|
||||
}
|
||||
|
||||
bool bSuccess = pScreenshotImage->BInit( width, height, VulkanFormatToDRM(g_output.outputFormat), screenshotImageFlags );
|
||||
bool bSuccess = pScreenshotImage->BInit( width, height, nv12 ? DRM_FORMAT_NV12 : VulkanFormatToDRM(g_output.outputFormat), screenshotImageFlags );
|
||||
|
||||
assert( bSuccess );
|
||||
}
|
||||
|
@ -3196,14 +3254,17 @@ bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr<CVul
|
|||
|
||||
if ( pScreenshotTexture != nullptr )
|
||||
{
|
||||
if (compositeImage->width() == pScreenshotTexture->width() &&
|
||||
if (compositeImage->format() == pScreenshotTexture->format() &&
|
||||
compositeImage->width() == pScreenshotTexture->width() &&
|
||||
compositeImage->height() == pScreenshotTexture->height()) {
|
||||
cmdBuffer->copyImage(compositeImage, pScreenshotTexture);
|
||||
} else {
|
||||
const bool ycbcr = pScreenshotTexture->isYcbcr();
|
||||
|
||||
float scale = (float)compositeImage->width() / pScreenshotTexture->width();
|
||||
BlitPushData_t constants( scale );
|
||||
|
||||
cmdBuffer->bindPipeline(g_device.pipeline(SHADER_TYPE_BLIT));
|
||||
cmdBuffer->bindPipeline(g_device.pipeline( ycbcr ? SHADER_TYPE_RGB_TO_NV12 : SHADER_TYPE_BLIT ));
|
||||
cmdBuffer->bindTexture(0, compositeImage);
|
||||
cmdBuffer->setTextureSrgb(0, false);
|
||||
cmdBuffer->setSamplerNearest(0, false);
|
||||
|
@ -3217,7 +3278,10 @@ bool vulkan_composite( const struct FrameInfo_t *frameInfo, std::shared_ptr<CVul
|
|||
|
||||
const int pixelsPerGroup = 8;
|
||||
|
||||
cmdBuffer->dispatch(div_roundup(pScreenshotTexture->width(), pixelsPerGroup), div_roundup(pScreenshotTexture->height(), pixelsPerGroup));
|
||||
// For ycbcr, we operate on 2 pixels at a time, so use the half-extent.
|
||||
const int dispatchSize = ycbcr ? pixelsPerGroup * 2 : pixelsPerGroup;
|
||||
|
||||
cmdBuffer->dispatch(div_roundup(pScreenshotTexture->width(), dispatchSize), div_roundup(pScreenshotTexture->height(), 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,8 @@ public:
|
|||
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 contentWidth() {return m_contentWidth; }
|
||||
|
@ -114,11 +116,17 @@ public:
|
|||
inline uint32_t rowPitch() { return m_unRowPitch; }
|
||||
inline uint32_t fbid() { return m_FBID; }
|
||||
inline void *mappedData() { return m_pMappedData; }
|
||||
inline VkFormat format() { return m_format; }
|
||||
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_vkImageMemory == VK_NULL_HANDLE; }
|
||||
inline bool externalImage() { return m_bExternal; }
|
||||
inline VkDeviceSize totalSize() const { return m_size; }
|
||||
|
||||
inline bool isYcbcr() const
|
||||
{
|
||||
return format() == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
|
||||
}
|
||||
|
||||
int memoryFence();
|
||||
|
||||
|
@ -135,6 +143,9 @@ private:
|
|||
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;
|
||||
|
||||
|
@ -142,6 +153,7 @@ private:
|
|||
uint32_t m_contentHeight = 0;
|
||||
|
||||
uint32_t m_unRowPitch = 0;
|
||||
VkDeviceSize m_size = 0;
|
||||
|
||||
uint32_t m_FBID = 0;
|
||||
|
||||
|
@ -185,7 +197,7 @@ struct FrameInfo_t
|
|||
if ( !tex )
|
||||
return false;
|
||||
|
||||
return tex->format() == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
|
||||
return tex->isYcbcr();
|
||||
}
|
||||
|
||||
uint32_t integerWidth() const { return tex->width() / scale.x; }
|
||||
|
@ -230,7 +242,7 @@ std::shared_ptr<CVulkanTexture> vulkan_create_texture_from_wlr_buffer( struct wl
|
|||
|
||||
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);
|
||||
std::shared_ptr<CVulkanTexture> vulkan_acquire_screenshot_texture(uint32_t width, uint32_t height, bool exportable, bool nv12);
|
||||
|
||||
void vulkan_present_to_window( void );
|
||||
|
||||
|
|
63
src/shaders/cs_rgb_to_nv12.comp
Normal file
63
src/shaders/cs_rgb_to_nv12.comp
Normal file
|
@ -0,0 +1,63 @@
|
|||
#version 450
|
||||
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
|
||||
#include "descriptor_set.h"
|
||||
|
||||
layout(
|
||||
local_size_x = 8,
|
||||
local_size_y = 8,
|
||||
local_size_z = 1) in;
|
||||
|
||||
// NV12 Format is:
|
||||
// YYYYYYYYYYYYYYY...
|
||||
// YYYYYYYYYYYYYYY...
|
||||
// UVUVUVUVUVUVUVU...
|
||||
|
||||
layout(push_constant)
|
||||
uniform layers_t {
|
||||
vec2 u_scale[VKR_MAX_LAYERS];
|
||||
vec2 u_offset[VKR_MAX_LAYERS];
|
||||
float u_opacity[VKR_MAX_LAYERS];
|
||||
uint u_borderMask;
|
||||
uint u_frameId;
|
||||
};
|
||||
|
||||
#include "composite.h"
|
||||
|
||||
vec4 sampleLayer(uint layerIdx, vec2 uv) {
|
||||
return sampleLayer(s_samplers[layerIdx], layerIdx, uv, true);
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec3 thread_id = ivec3(gl_GlobalInvocationID);
|
||||
|
||||
// todo: fix
|
||||
if (all(lessThan(thread_id.xy, ivec2(640, 400)))) {
|
||||
ivec2 offset_table[4] = {
|
||||
ivec2(0, 0), ivec2(1, 0), ivec2(0, 1), ivec2(1, 1),
|
||||
};
|
||||
|
||||
ivec2 chroma_uv = thread_id.xy;
|
||||
ivec2 luma_uv = thread_id.xy * 2;
|
||||
|
||||
vec3 color[4] = {
|
||||
sampleLayer(0, vec2(luma_uv.x + offset_table[0].x, luma_uv.y + offset_table[0].y)).rgb,
|
||||
sampleLayer(0, vec2(luma_uv.x + offset_table[1].x, luma_uv.y + offset_table[1].y)).rgb,
|
||||
sampleLayer(0, vec2(luma_uv.x + offset_table[2].x, luma_uv.y + offset_table[2].y)).rgb,
|
||||
sampleLayer(0, vec2(luma_uv.x + offset_table[3].x, luma_uv.y + offset_table[3].y)).rgb,
|
||||
};
|
||||
|
||||
vec3 avg_color = (color[0] + color[1] + color[2] + color[3]) / 4.0f;
|
||||
vec2 uv = vec2(
|
||||
-0.148f * avg_color.r - 0.291f * avg_color.g + 0.439f * avg_color.b + (128.0f / 255.0f),
|
||||
0.439f * avg_color.r - 0.368f * avg_color.g - 0.071f * avg_color.b + (128.0f / 255.0f)
|
||||
);
|
||||
imageStore(dst_chroma, chroma_uv, vec4(uv, 0.0f, 1.0f));
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float y = 0.257f * color[i].r + 0.504f * color[i].g + 0.098f * color[i].b + (16.0f / 255.0f);
|
||||
imageStore(dst_luma, luma_uv + offset_table[i], vec4(y, 0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,5 +6,9 @@ layout(constant_id = 2) const bool c_compositing_debug = false;
|
|||
layout(constant_id = 3) const int c_blur_layer_count = 0;
|
||||
|
||||
layout(binding = 0, rgba8) writeonly uniform image2D dst;
|
||||
layout(binding = 1) uniform sampler2D s_samplers[VKR_SAMPLER_SLOTS];
|
||||
layout(binding = 2) uniform sampler2D s_ycbcr_samplers[VKR_SAMPLER_SLOTS];
|
||||
// alias
|
||||
layout(binding = 0, rgba8) writeonly uniform image2D dst_luma;
|
||||
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];
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef DESCRIPTOR_SET_CONSTANTS_H_
|
||||
#define DESCRIPTOR_SET_CONSTANTS_H_
|
||||
|
||||
#define VKR_TARGET_SLOTS 2u
|
||||
#define VKR_SAMPLER_SLOTS 16u
|
||||
#define VKR_MAX_LAYERS 6u
|
||||
|
||||
|
|
|
@ -1917,9 +1917,12 @@ paint_all(bool async)
|
|||
pCaptureTexture = pw_buffer->texture;
|
||||
}
|
||||
#endif
|
||||
// hack: forced to nv12 rn
|
||||
bool bHackForceNV12DumpScreenshot = false;
|
||||
|
||||
if ( bCapture && pCaptureTexture == nullptr )
|
||||
{
|
||||
pCaptureTexture = vulkan_acquire_screenshot_texture(g_nOutputWidth, g_nOutputHeight, false);
|
||||
pCaptureTexture = vulkan_acquire_screenshot_texture(g_nOutputWidth, g_nOutputHeight, false, bHackForceNV12DumpScreenshot);
|
||||
}
|
||||
|
||||
bool bResult = vulkan_composite( &frameInfo, pCaptureTexture );
|
||||
|
@ -1999,45 +2002,76 @@ paint_all(bool async)
|
|||
if ( takeScreenshot )
|
||||
{
|
||||
assert( pCaptureTexture != nullptr );
|
||||
assert( pCaptureTexture->format() == VK_FORMAT_B8G8R8A8_UNORM );
|
||||
|
||||
std::thread screenshotThread = std::thread([=] {
|
||||
pthread_setname_np( pthread_self(), "gamescope-scrsh" );
|
||||
|
||||
const uint8_t *mappedData = reinterpret_cast<const uint8_t *>(pCaptureTexture->mappedData());
|
||||
|
||||
// Make our own copy of the image to remove the alpha channel.
|
||||
auto imageData = std::vector<uint8_t>(currentOutputWidth * currentOutputHeight * 4);
|
||||
const uint32_t comp = 4;
|
||||
const uint32_t pitch = currentOutputWidth * comp;
|
||||
for (uint32_t y = 0; y < currentOutputHeight; y++)
|
||||
if (pCaptureTexture->format() == VK_FORMAT_B8G8R8A8_UNORM)
|
||||
{
|
||||
for (uint32_t x = 0; x < currentOutputWidth; x++)
|
||||
// Make our own copy of the image to remove the alpha channel.
|
||||
auto imageData = std::vector<uint8_t>(currentOutputWidth * currentOutputHeight * 4);
|
||||
const uint32_t comp = 4;
|
||||
const uint32_t pitch = currentOutputWidth * comp;
|
||||
for (uint32_t y = 0; y < currentOutputHeight; y++)
|
||||
{
|
||||
// BGR...
|
||||
imageData[y * pitch + x * comp + 0] = mappedData[y * pCaptureTexture->rowPitch() + x * comp + 2];
|
||||
imageData[y * pitch + x * comp + 1] = mappedData[y * pCaptureTexture->rowPitch() + x * comp + 1];
|
||||
imageData[y * pitch + x * comp + 2] = mappedData[y * pCaptureTexture->rowPitch() + x * comp + 0];
|
||||
imageData[y * pitch + x * comp + 3] = 255;
|
||||
for (uint32_t x = 0; x < currentOutputWidth; x++)
|
||||
{
|
||||
// BGR...
|
||||
imageData[y * pitch + x * comp + 0] = mappedData[y * pCaptureTexture->rowPitch() + x * comp + 2];
|
||||
imageData[y * pitch + x * comp + 1] = mappedData[y * pCaptureTexture->rowPitch() + x * comp + 1];
|
||||
imageData[y * pitch + x * comp + 2] = mappedData[y * pCaptureTexture->rowPitch() + x * comp + 0];
|
||||
imageData[y * pitch + x * comp + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
char pTimeBuffer[1024] = "/tmp/gamescope.png";
|
||||
|
||||
if ( !propertyRequestedScreenshot )
|
||||
{
|
||||
time_t currentTime = time(0);
|
||||
struct tm *localTime = localtime( ¤tTime );
|
||||
strftime( pTimeBuffer, sizeof( pTimeBuffer ), "/tmp/gamescope_%Y-%m-%d_%H-%M-%S.png", localTime );
|
||||
}
|
||||
|
||||
if ( stbi_write_png(pTimeBuffer, currentOutputWidth, currentOutputHeight, 4, imageData.data(), pitch) )
|
||||
{
|
||||
xwm_log.infof("Screenshot saved to %s", pTimeBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
xwm_log.errorf( "Failed to save screenshot to %s", pTimeBuffer );
|
||||
}
|
||||
}
|
||||
|
||||
char pTimeBuffer[1024] = "/tmp/gamescope.png";
|
||||
|
||||
if ( !propertyRequestedScreenshot )
|
||||
else if (pCaptureTexture->format() == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM)
|
||||
{
|
||||
time_t currentTime = time(0);
|
||||
struct tm *localTime = localtime( ¤tTime );
|
||||
strftime( pTimeBuffer, sizeof( pTimeBuffer ), "/tmp/gamescope_%Y-%m-%d_%H-%M-%S.png", localTime );
|
||||
}
|
||||
char pTimeBuffer[1024] = "/tmp/gamescope.raw";
|
||||
|
||||
if ( stbi_write_png(pTimeBuffer, currentOutputWidth, currentOutputHeight, 4, imageData.data(), pitch) )
|
||||
{
|
||||
xwm_log.infof("Screenshot saved to %s", pTimeBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
xwm_log.errorf( "Failed to save screenshot to %s", pTimeBuffer );
|
||||
if ( !propertyRequestedScreenshot )
|
||||
{
|
||||
time_t currentTime = time(0);
|
||||
struct tm *localTime = localtime( ¤tTime );
|
||||
strftime( pTimeBuffer, sizeof( pTimeBuffer ), "/tmp/gamescope_%Y-%m-%d_%H-%M-%S.raw", localTime );
|
||||
}
|
||||
|
||||
FILE *file = fopen(pTimeBuffer, "wb");
|
||||
if (file)
|
||||
{
|
||||
fwrite(mappedData, 1, pCaptureTexture->totalSize(), file );
|
||||
fclose(file);
|
||||
|
||||
char cmd[4096];
|
||||
sprintf(cmd, "ffmpeg -f rawvideo -pixel_format nv12 -video_size %dx%d -i %s %s_encoded.png", pCaptureTexture->width(), pCaptureTexture->height() pTimeBuffer, pTimeBuffer);
|
||||
|
||||
system(cmd);
|
||||
|
||||
xwm_log.infof("Screenshot saved to %s", pTimeBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
xwm_log.errorf( "Failed to save screenshot to %s", pTimeBuffer );
|
||||
}
|
||||
}
|
||||
|
||||
XDeleteProperty( root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeScreenShotAtom );
|
||||
|
|
Loading…
Reference in a new issue