// 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);
CreatePipeline();
}
WindowAdaptPass::~WindowAdaptPass() = default;
void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index,
std::list<Layer>& layers,
std::span<const Tegra::FramebufferConfig> configs,
const Layout::FramebufferLayout& layout, Frame* dst) {
const VkFramebuffer host_framebuffer{*dst->framebuffer};
const VkRenderPass renderpass{*render_pass};
const VkPipeline graphics_pipeline{*pipeline};
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<PresentPushConstants> push_constants(layer_count);
std::vector<VkDescriptorSet> descriptor_sets(layer_count);
auto layer_it = layers.begin();
for (size_t i = 0; i < layer_count; i++) {
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});
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
for (size_t i = 0; i < layer_count; 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::CreatePipeline() {
pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout,
std::tie(vertex_shader, fragment_shader), false);
}
} // namespace Vulkan