// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/frontend/framebuffer_layout.h" #include "video_core/framebuffer_config.h" #include "video_core/host_shaders/vulkan_present_vert_spv.h" #include "video_core/renderer_vulkan/present/layer.h" #include "video_core/renderer_vulkan/present/present_push_constants.h" #include "video_core/renderer_vulkan/present/util.h" #include "video_core/renderer_vulkan/present/window_adapt_pass.h" #include "video_core/renderer_vulkan/vk_present_manager.h" #include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" namespace Vulkan { WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format, vk::Sampler&& sampler_, vk::ShaderModule&& fragment_shader_) : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) { CreateDescriptorSetLayout(); CreatePipelineLayout(); CreateVertexShader(); CreateRenderPass(frame_format); CreatePipelines(); } WindowAdaptPass::~WindowAdaptPass() = default; void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, std::list& layers, std::span configs, const Layout::FramebufferLayout& layout, Frame* dst) { const VkFramebuffer host_framebuffer{*dst->framebuffer}; const VkRenderPass renderpass{*render_pass}; const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; const VkExtent2D render_area{ .width = dst->width, .height = dst->height, }; const size_t layer_count = configs.size(); std::vector push_constants(layer_count); std::vector descriptor_sets(layer_count); std::vector graphics_pipelines(layer_count); auto layer_it = layers.begin(); for (size_t i = 0; i < layer_count; i++) { switch (configs[i].blending) { case Tegra::BlendMode::Opaque: default: graphics_pipelines[i] = *opaque_pipeline; break; case Tegra::BlendMode::Premultiplied: graphics_pipelines[i] = *premultiplied_pipeline; break; case Tegra::BlendMode::Coverage: graphics_pipelines[i] = *coverage_pipeline; break; } layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler, image_index, configs[i], layout); layer_it++; } scheduler.Record([=](vk::CommandBuffer cmdbuf) { const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; const VkClearAttachment clear_attachment{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .colorAttachment = 0, .clearValue = { .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, }, }; const VkClearRect clear_rect{ .rect = { .offset = {0, 0}, .extent = render_area, }, .baseArrayLayer = 0, .layerCount = 1, }; BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); for (size_t i = 0; i < layer_count; i++) { cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipelines[i]); cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants[i]); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, descriptor_sets[i], {}); cmdbuf.Draw(4, 1, 0, 0); } cmdbuf.EndRenderPass(); }); } VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() { return *descriptor_set_layout; } VkRenderPass WindowAdaptPass::GetRenderPass() { return *render_pass; } void WindowAdaptPass::CreateDescriptorSetLayout() { descriptor_set_layout = CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); } void WindowAdaptPass::CreatePipelineLayout() { const VkPushConstantRange range{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .offset = 0, .size = sizeof(PresentPushConstants), }; pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, .setLayoutCount = 1, .pSetLayouts = descriptor_set_layout.address(), .pushConstantRangeCount = 1, .pPushConstantRanges = &range, }); } void WindowAdaptPass::CreateVertexShader() { vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); } void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); } void WindowAdaptPass::CreatePipelines() { opaque_pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); premultiplied_pipeline = CreateWrappedPremultipliedBlendingPipeline( device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); coverage_pipeline = CreateWrappedCoverageBlendingPipeline( device, render_pass, pipeline_layout, std::tie(vertex_shader, fragment_shader)); } } // namespace Vulkan