Compare commits

..
Author SHA1 Message Date
FadilandXxOtakuXx 026189166f graphics: convert HDR10 video-out output to SDR (#2003)
graphics: convert PQ video-out frames for SDR swapchains

Co-authored-by: XxOtakuXx <fadilfawmi@gmail.com>
2026-10-31 04:26:58 +01:00
6 changed files with 239 additions and 28 deletions

No files matched your search

+3 -1
View File
@@ -316,7 +316,7 @@ function(add_gpu_blit_shader shader_name shader_file)
"-I${gpu_tiler_shader_dir}" -o "${shader_spv}" "${shader_source}" "-I${gpu_tiler_shader_dir}" -o "${shader_spv}" "${shader_source}"
COMMAND ${CMAKE_COMMAND} -DINPUT=${shader_spv} -DOUTPUT=${shader_header} COMMAND ${CMAKE_COMMAND} -DINPUT=${shader_spv} -DOUTPUT=${shader_header}
-DSYMBOL=${shader_symbol} -P "${KYTY_SOURCE_DIR}/embed_spirv.cmake" -DSYMBOL=${shader_symbol} -P "${KYTY_SOURCE_DIR}/embed_spirv.cmake"
DEPENDS "${shader_source}" DEPENDS "${shader_source}" "${gpu_tiler_shader_dir}/gpu_video_out_hdr10_to_sdr.inc"
VERBATIM VERBATIM
) )
set(gpu_blit_shader_headers ${gpu_blit_shader_headers} "${shader_header}" PARENT_SCOPE) set(gpu_blit_shader_headers ${gpu_blit_shader_headers} "${shader_header}" PARENT_SCOPE)
@@ -738,6 +738,8 @@ if(BUILD_TESTING)
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --gpu-command-lane-only) COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --gpu-command-lane-only)
add_test(NAME pm4_flip add_test(NAME pm4_flip
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --flip-only) COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --flip-only)
add_test(NAME video_out_hdr10_to_sdr
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --video-out-only)
add_test(NAME depth_bits add_test(NAME depth_bits
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --depth-bits-only) COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --depth-bits-only)
add_test(NAME msaa_storage add_test(NAME msaa_storage
@@ -0,0 +1,28 @@
// Converts SMPTE ST 2084 PQ (or linear nits) Rec.2020 to sRGB-encoded Rec.709. The 250-nit SDR
// white scales to 1.0, then an extended Reinhard curve rolls off luminance above 0.75 so that
// 10,000 nits reaches 1.0.
vec3 Hdr10ToSdr(vec3 rgb, bool source_linear) {
vec3 nits = clamp(rgb, 0.0, 10000.0);
if (!source_linear) {
vec3 powered = pow(rgb, vec3(32.0 / 2523.0));
nits = 10000.0 * pow(max(powered - 3424.0 / 4096.0, 0.0) /
(2413.0 / 128.0 - (2392.0 / 128.0) * powered),
vec3(16384.0 / 2610.0));
}
const mat3 to_rec709 = (1.0 / 250.0) *
mat3(1.660491, -0.124550, -0.018151,
-0.587641, 1.132900, -0.100579,
-0.072850, -0.008349, 1.118730);
vec3 linear = max(to_rec709 * nits, 0.0);
const float knee = 0.75;
const float span = 1.0 - knee;
const float white = (10000.0 / 250.0 - knee) / span;
float luma = dot(linear, vec3(0.2126, 0.7152, 0.0722));
if (luma > knee) {
float x = (luma - knee) / span;
linear *= (knee + span * x * (1.0 + x / (white * white)) / (1.0 + x)) / luma;
}
linear = clamp(linear, 0.0, 1.0);
return mix(linear * 12.92, 1.055 * pow(linear, vec3(1.0 / 2.4)) - 0.055,
greaterThan(linear, vec3(0.0031308)));
}
@@ -1,4 +1,5 @@
#version 450 #version 450
#extension GL_GOOGLE_include_directive : require
layout(location = 0) out vec4 color; layout(location = 0) out vec4 color;
@@ -27,25 +28,33 @@ void main() {
color = vec4(SdrToHdr10(sampled.rgb, false), sampled.a); color = vec4(SdrToHdr10(sampled.rgb, false), sampled.a);
} }
#else #else
#include "gpu_video_out_hdr10_to_sdr.inc"
layout(location = 0) in vec2 uv; layout(location = 0) in vec2 uv;
layout(binding = 0) uniform sampler2D source; layout(binding = 0) uniform sampler2D source;
layout(push_constant) uniform AlphaControl { layout(push_constant) uniform AlphaControl {
uint flags; // bit 0: premultiplied, bit 1: HDR target, bit 2: PQ source, bit 3: linear source uint flags; // bit 0: premultiplied, 1: HDR target, 2: PQ source, 3: linear source, 4: opaque
}; };
void main() { void main() {
const uint HDR_TARGET = 2u; const uint HDR_TARGET = 2u;
const uint PQ_SOURCE = 4u; const uint PQ_SOURCE = 4u;
vec4 sampled = texture(source, uv); vec4 sampled = texture(source, uv);
if ((flags & 16u) != 0u) {
sampled.a = 1.0;
}
bool premultiplied = (flags & 1u) != 0u; bool premultiplied = (flags & 1u) != 0u;
bool convert_to_hdr = (flags & (HDR_TARGET | PQ_SOURCE)) == HDR_TARGET; uint conversion = flags & (HDR_TARGET | PQ_SOURCE);
if (convert_to_hdr && premultiplied) { bool convert = conversion == HDR_TARGET || conversion == PQ_SOURCE;
if (convert && premultiplied) {
sampled.rgb = sampled.a > 0.0 ? sampled.rgb / sampled.a : vec3(0.0); sampled.rgb = sampled.a > 0.0 ? sampled.rgb / sampled.a : vec3(0.0);
} }
if (convert_to_hdr) { if (conversion == HDR_TARGET) {
sampled.rgb = SdrToHdr10(sampled.rgb, (flags & 8u) != 0u); sampled.rgb = SdrToHdr10(sampled.rgb, (flags & 8u) != 0u);
} else if (conversion == PQ_SOURCE) {
sampled.rgb = Hdr10ToSdr(sampled.rgb, (flags & 8u) != 0u);
} }
if (!premultiplied || convert_to_hdr) { if (!premultiplied || convert) {
sampled.rgb *= sampled.a; sampled.rgb *= sampled.a;
} }
color = sampled; color = sampled;
-6
View File
@@ -1125,12 +1125,6 @@ void FlipQueue::Prepare(uint64_t request_id, Graphics::CommandBuffer& buffer) {
} }
return; return;
} }
static std::atomic_flag warned = ATOMIC_FLAG_INIT;
if (hdr && !m_presenter.IsHdrSupported() && !warned.test_and_set(std::memory_order_relaxed)) {
Log::WriteToConsoleAndLog(
"Warning: HDR output is unavailable; HDR-to-SDR conversion is not implemented, "
"displaying PQ video-out pixels unchanged.\n");
}
Graphics::Presenter::Frame* frame = nullptr; Graphics::Presenter::Frame* frame = nullptr;
if (special) { if (special) {
frame = &m_presenter.PrepareBlankFrame(width, height, index == VIDEO_OUT_BUFFER_INDEX_BLACK, frame = &m_presenter.PrepareBlankFrame(width, height, index == VIDEO_OUT_BUFFER_INDEX_BLACK,
+32 -16
View File
@@ -290,7 +290,7 @@ public:
private: private:
vk::SurfaceFormatKHR SelectFormat(); vk::SurfaceFormatKHR SelectFormat();
void Destroy(); void Destroy();
void DrawOverlay(vk::CommandBuffer command, const Presenter::Layer& layer, void DrawLayer(vk::CommandBuffer command, const Presenter::Layer& layer,
const vk::Rect2D& rect); const vk::Rect2D& rect);
WindowContext& m_window; WindowContext& m_window;
@@ -653,7 +653,7 @@ static vk::Rect2D FitAspect(const vk::Extent3D& source, const vk::Extent2D& targ
{w, h}}; {w, h}};
} }
void Swapchain::DrawOverlay(vk::CommandBuffer command, const Presenter::Layer& layer, void Swapchain::DrawLayer(vk::CommandBuffer command, const Presenter::Layer& layer,
const vk::Rect2D& rect) { const vk::Rect2D& rect) {
auto device = m_window.graphic_ctx.device; auto device = m_window.graphic_ctx.device;
if (m_overlay_pipeline == nullptr) { if (m_overlay_pipeline == nullptr) {
@@ -756,9 +756,11 @@ void Swapchain::DrawOverlay(vk::CommandBuffer command, const Presenter::Layer& l
write.descriptorType = vk::DescriptorType::eCombinedImageSampler; write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
write.pImageInfo = &image; write.pImageInfo = &image;
command.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, m_overlay_layout, 0, 1, &write); command.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, m_overlay_layout, 0, 1, &write);
const uint32_t flags = (layer.premultiplied_alpha ? 1u : 0u) | // The main bus ignores alpha.
(m_hdr_active ? 2u : 0u) | (frame.hdr ? 4u : 0u) | const uint32_t flags = (layer.premultiplied_alpha ? 1u : 0u) | (m_hdr_active ? 2u : 0u) |
(frame.image.format == vk::Format::eR16G16B16A16Sfloat ? 8u : 0u); (frame.hdr ? 4u : 0u) |
(frame.image.format == vk::Format::eR16G16B16A16Sfloat ? 8u : 0u) |
(layer.bus == 0 ? 16u : 0u);
command.pushConstants(m_overlay_layout, vk::ShaderStageFlagBits::eFragment, 0, command.pushConstants(m_overlay_layout, vk::ShaderStageFlagBits::eFragment, 0,
sizeof(flags), &flags); sizeof(flags), &flags);
command.bindPipeline(vk::PipelineBindPoint::eGraphics, m_overlay_pipeline); command.bindPipeline(vk::PipelineBindPoint::eGraphics, m_overlay_pipeline);
@@ -789,12 +791,18 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, Presenter::Frame*
const Presenter::Layer& overlay, bool draw_system_overlay) { const Presenter::Layer& overlay, bool draw_system_overlay) {
EXIT_IF(m_image_index >= m_images.size()); EXIT_IF(m_image_index >= m_images.size());
auto vk_command = command.Handle(); auto vk_command = command.Handle();
const bool convert_source = source != nullptr && source->hdr && !m_hdr_active;
const bool blit_source = source != nullptr && !convert_source;
const bool draw_overlay = overlay.frame != nullptr; const bool draw_overlay = overlay.frame != nullptr;
const bool draw_attachment = draw_overlay || draw_system_overlay; const bool draw_attachment = convert_source || draw_overlay || draw_system_overlay;
if (source != nullptr) { if (blit_source) {
source->Transit(vk_command, vk::ImageLayout::eTransferSrcOptimal, source->Transit(vk_command, vk::ImageLayout::eTransferSrcOptimal,
vk::AccessFlagBits2::eTransferRead); vk::AccessFlagBits2::eTransferRead);
} }
if (convert_source) {
source->Transit(vk_command, vk::ImageLayout::eShaderReadOnlyOptimal,
vk::AccessFlagBits2::eShaderRead);
}
if (draw_overlay) { if (draw_overlay) {
overlay.frame->Transit(vk_command, vk::ImageLayout::eShaderReadOnlyOptimal, overlay.frame->Transit(vk_command, vk::ImageLayout::eShaderReadOnlyOptimal,
vk::AccessFlagBits2::eShaderRead); vk::AccessFlagBits2::eShaderRead);
@@ -826,7 +834,7 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, Presenter::Frame*
const vk::ClearColorValue black {}; const vk::ClearColorValue black {};
vk_command.clearColorImage(m_images[m_image_index], vk::ImageLayout::eTransferDstOptimal, vk_command.clearColorImage(m_images[m_image_index], vk::ImageLayout::eTransferDstOptimal,
&black, 1, &to_transfer.subresourceRange); &black, 1, &to_transfer.subresourceRange);
if (source != nullptr) { if (blit_source) {
vk::ImageMemoryBarrier after_clear = to_transfer; vk::ImageMemoryBarrier after_clear = to_transfer;
after_clear.srcAccessMask = vk::AccessFlagBits::eTransferWrite; after_clear.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
after_clear.oldLayout = vk::ImageLayout::eTransferDstOptimal; after_clear.oldLayout = vk::ImageLayout::eTransferDstOptimal;
@@ -836,7 +844,7 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, Presenter::Frame*
&after_clear); &after_clear);
} }
} }
if (source != nullptr) { if (blit_source) {
vk::ImageBlit region {}; vk::ImageBlit region {};
region.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor; region.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
region.srcSubresource.mipLevel = 0; region.srcSubresource.mipLevel = 0;
@@ -882,23 +890,31 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, Presenter::Frame*
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1, vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1,
&to_present); &to_present);
if (draw_attachment) { if (draw_attachment) {
if (draw_overlay) { // Each pass blends over the color writes of the previous one.
DrawOverlay(vk_command, overlay, game_rect);
if (draw_system_overlay) {
to_present.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; to_present.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
to_present.oldLayout = vk::ImageLayout::eColorAttachmentOptimal; to_present.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
const auto attachment_barrier = [&] {
vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1,
nullptr, 1, &to_present); &to_present);
};
if (convert_source) {
DrawLayer(vk_command, {source, 0, false}, game_rect);
if (draw_overlay || draw_system_overlay) {
attachment_barrier();
}
}
if (draw_overlay) {
DrawLayer(vk_command, overlay, game_rect);
if (draw_system_overlay) {
attachment_barrier();
} }
} }
if (draw_system_overlay) { if (draw_system_overlay) {
m_system_overlay->Record(vk_command, m_image_views[m_image_index]); m_system_overlay->Record(vk_command, m_image_views[m_image_index]);
} }
to_present.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
to_present.dstAccessMask = vk::AccessFlagBits::eMemoryRead; to_present.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
to_present.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
to_present.newLayout = vk::ImageLayout::ePresentSrcKHR; to_present.newLayout = vk::ImageLayout::ePresentSrcKHR;
vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands,
+162
View File
@@ -8,6 +8,8 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/subsystems.h" #include "common/subsystems.h"
#include "common/threads.h" #include "common/threads.h"
#include "gpu_blit_shaders/gpu_blit_fs_triangle_spv.h"
#include "gpu_blit_shaders/gpu_video_out_overlay_spv.h"
#include "gpu_test_shaders/gpu_test_ms_depth_spv.h" #include "gpu_test_shaders/gpu_test_ms_depth_spv.h"
#include "graphics/guest_gpu/command_processor/commandProcessor.h" #include "graphics/guest_gpu/command_processor/commandProcessor.h"
#include "graphics/guest_gpu/command_processor/pm4Dispatch.h" #include "graphics/guest_gpu/command_processor/pm4Dispatch.h"
@@ -2213,6 +2215,160 @@ public:
std::printf("[host] %-32s ok\n", name); std::printf("[host] %-32s ok\n", name);
} }
// The video-out layer shader converts PQ and linear-nit main-bus frames for an SDR swapchain.
void CheckVideoOutHdr10ToSdr() {
constexpr const char *name = "VideoOutHdr10ToSdr";
const auto vertex = CompileSPV(GPU_BLIT_FS_TRIANGLE_SPV, m_device);
const auto fragment = CompileSPV(GPU_VIDEO_OUT_OVERLAY_SPV, m_device);
const auto sampler = CreateSampler(name);
const vk::DescriptorSetLayoutBinding binding{0, vk::DescriptorType::eCombinedImageSampler,
1, vk::ShaderStageFlagBits::eFragment};
vk::DescriptorSetLayoutCreateInfo descriptors{};
descriptors.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR;
descriptors.bindingCount = 1;
descriptors.pBindings = &binding;
vk::DescriptorSetLayout descriptor_layout = nullptr;
RequireVk(name, "descriptors",
m_device.createDescriptorSetLayout(&descriptors, nullptr, &descriptor_layout),
"vkCreateDescriptorSetLayout");
const vk::PushConstantRange push{vk::ShaderStageFlagBits::eFragment, 0, sizeof(u32)};
vk::PipelineLayoutCreateInfo layout_info{};
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &descriptor_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = &push;
vk::PipelineLayout layout = nullptr;
RequireVk(name, "layout", m_device.createPipelineLayout(&layout_info, nullptr, &layout),
"vkCreatePipelineLayout");
std::array<vk::PipelineShaderStageCreateInfo, 2> stages{};
stages[0].stage = vk::ShaderStageFlagBits::eVertex;
stages[0].module = vertex;
stages[0].pName = "main";
stages[1].stage = vk::ShaderStageFlagBits::eFragment;
stages[1].module = fragment;
stages[1].pName = "main";
vk::PipelineVertexInputStateCreateInfo vertex_input{};
vk::PipelineInputAssemblyStateCreateInfo assembly{};
assembly.topology = vk::PrimitiveTopology::eTriangleList;
const vk::Viewport viewport{0.0f, 0.0f, 4.0f, 1.0f, 0.0f, 1.0f};
const vk::Rect2D scissor{{0, 0}, {4, 1}};
vk::PipelineViewportStateCreateInfo viewport_state{};
viewport_state.viewportCount = 1;
viewport_state.pViewports = &viewport;
viewport_state.scissorCount = 1;
viewport_state.pScissors = &scissor;
vk::PipelineRasterizationStateCreateInfo rasterization{};
rasterization.lineWidth = 1.0f;
vk::PipelineMultisampleStateCreateInfo multisample{};
vk::PipelineColorBlendAttachmentState attachment{};
attachment.colorWriteMask =
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
vk::PipelineColorBlendStateCreateInfo blend{};
blend.attachmentCount = 1;
blend.pAttachments = &attachment;
const auto target_format = vk::Format::eR8G8B8A8Unorm;
vk::PipelineRenderingCreateInfo rendering_format{};
rendering_format.colorAttachmentCount = 1;
rendering_format.pColorAttachmentFormats = &target_format;
vk::GraphicsPipelineCreateInfo create{};
create.pNext = &rendering_format;
create.stageCount = static_cast<u32>(stages.size());
create.pStages = stages.data();
create.pVertexInputState = &vertex_input;
create.pInputAssemblyState = &assembly;
create.pViewportState = &viewport_state;
create.pRasterizationState = &rasterization;
create.pMultisampleState = &multisample;
create.pColorBlendState = &blend;
create.layout = layout;
vk::Pipeline pipeline = nullptr;
RequireVk(name, "pipeline",
m_device.createGraphicsPipelines(nullptr, 1, &create, nullptr, &pipeline),
"vkCreateGraphicsPipelines");
auto target = CreateImage2D(name, 4, 1, target_format,
vk::ImageUsageFlagBits::eColorAttachment, {}, 1,
vk::ImageLayout::eTransferSrcOptimal);
const auto render = [&](Image &source, u32 flags) {
vk::CommandBuffer cmd = BeginCommands(name, "draw");
AddImageBarrier(cmd, target.image, target.layout,
vk::ImageLayout::eColorAttachmentOptimal,
vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::AccessFlagBits::eTransferRead,
vk::AccessFlagBits::eColorAttachmentWrite);
target.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::RenderingAttachmentInfo color{};
color.imageView = target.view;
color.imageLayout = vk::ImageLayout::eColorAttachmentOptimal;
color.loadOp = vk::AttachmentLoadOp::eClear;
color.storeOp = vk::AttachmentStoreOp::eStore;
vk::RenderingInfo rendering{};
rendering.renderArea.extent = scissor.extent;
rendering.layerCount = 1;
rendering.colorAttachmentCount = 1;
rendering.pColorAttachments = &color;
const vk::DescriptorImageInfo image{sampler, source.view,
vk::ImageLayout::eShaderReadOnlyOptimal};
vk::WriteDescriptorSet write{};
write.descriptorCount = 1;
write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
write.pImageInfo = &image;
cmd.beginRendering(rendering);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
cmd.pushDescriptorSetKHR(vk::PipelineBindPoint::eGraphics, layout, 0, 1, &write);
cmd.pushConstants(layout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(flags), &flags);
cmd.draw(3, 1, 0, 0);
cmd.endRendering();
EndSubmitAndFree(name, "draw", cmd);
return ReadImage(name, &target);
};
const auto near = [](const std::vector<u32> &actual, const std::vector<u32> &expected) {
for (size_t i = 0; i < expected.size(); i++) {
for (u32 shift = 0; shift < 32; shift += 8) {
if (std::abs(static_cast<int>((actual[i] >> shift) & 0xffu) -
static_cast<int>((expected[i] >> shift) & 0xffu)) > 1) {
return false;
}
}
}
return true;
};
// Black, 100-nit and 250-nit gray, then a color; every source alpha is zero. Flags: PQ
// source (4), linear source (8), opaque main bus (16).
const auto pq = [](u32 r, u32 g, u32 b) { return r | (g << 10u) | (b << 20u); };
auto pq_source = CreateImage2D(
name, 4, 1, vk::Format::eA2B10G10R10UnormPack32, vk::ImageUsageFlagBits::eSampled,
{pq(0, 0, 0), pq(520, 520, 520), pq(616, 616, 616), pq(560, 500, 440)}, 1,
vk::ImageLayout::eShaderReadOnlyOptimal);
Require(name, "PQ source",
near(render(pq_source, 4u | 16u),
{0xff000000u, 0xffaaaaaau, 0xfff0f0f0u, 0xff6e95e3u}),
"PQ pixels were not converted to opaque sRGB");
// Half-float nits: 0, 100, 250 and (150, 90, 40).
auto nit_source = CreateImage2D(
name, 4, 1, vk::Format::eR16G16B16A16Sfloat, vk::ImageUsageFlagBits::eSampled,
{0, 0, 0x56405640u, 0x5640u, 0x5bd05bd0u, 0x5bd0u, 0x55a058b0u, 0x5100u}, 2,
vk::ImageLayout::eShaderReadOnlyOptimal);
Require(name, "linear source",
near(render(nit_source, 4u | 8u | 16u),
{0xff000000u, 0xffaaaaaau, 0xfff0f0f0u, 0xff669ce4u}),
"linear-nit pixels were not converted to opaque sRGB");
DestroyImage(&nit_source);
DestroyImage(&pq_source);
DestroyImage(&target);
m_device.destroyPipeline(pipeline, nullptr);
m_device.destroyPipelineLayout(layout, nullptr);
m_device.destroyDescriptorSetLayout(descriptor_layout, nullptr);
m_device.destroySampler(sampler, nullptr);
m_device.destroyShaderModule(fragment, nullptr);
m_device.destroyShaderModule(vertex, nullptr);
std::printf("[host] %-32s ok\n", name);
}
void CheckDescriptorHeapLargeSet() { void CheckDescriptorHeapLargeSet() {
EnsureRuntimeContext(); EnsureRuntimeContext();
std::array<vk::DescriptorSetLayoutBinding, 3> bindings{}; std::array<vk::DescriptorSetLayoutBinding, 3> bindings{};
@@ -54104,6 +54260,11 @@ int main(int argc, char **argv) {
CheckPm4Flip(); CheckPm4Flip();
return 0; return 0;
} }
if (argc == 2 && std::strcmp(argv[1], "--video-out-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckVideoOutHdr10ToSdr();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--gpu-command-lane-only") == 0) { if (argc == 2 && std::strcmp(argv[1], "--gpu-command-lane-only") == 0) {
VulkanHarness vulkan; VulkanHarness vulkan;
vulkan.CheckGpuCommandLane(); vulkan.CheckGpuCommandLane();
@@ -54641,6 +54802,7 @@ int main(int argc, char **argv) {
CheckPs5GameExampleImageClearRuntimeShape(); CheckPs5GameExampleImageClearRuntimeShape();
vulkan.CheckSchedulerTimeline(); vulkan.CheckSchedulerTimeline();
vulkan.CheckHostImageAllocation(); vulkan.CheckHostImageAllocation();
vulkan.CheckVideoOutHdr10ToSdr();
vulkan.CheckDescriptorHeapLargeSet(); vulkan.CheckDescriptorHeapLargeSet();
vulkan.CheckGpuMappedRangeLifecycle(); vulkan.CheckGpuMappedRangeLifecycle();
vulkan.CheckStreamBufferRing(); vulkan.CheckStreamBufferRing();