renderer_vulkan: implement layer stack composition
This commit is contained in:
		| @@ -174,6 +174,9 @@ add_library(video_core STATIC | ||||
|     renderer_vulkan/present/fsr.h | ||||
|     renderer_vulkan/present/fxaa.cpp | ||||
|     renderer_vulkan/present/fxaa.h | ||||
|     renderer_vulkan/present/layer.cpp | ||||
|     renderer_vulkan/present/layer.h | ||||
|     renderer_vulkan/present/present_push_constants.h | ||||
|     renderer_vulkan/present/smaa.cpp | ||||
|     renderer_vulkan/present/smaa.h | ||||
|     renderer_vulkan/present/util.cpp | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/scratch_buffer.h" | ||||
| #include "video_core/engines/engine_interface.h" | ||||
|   | ||||
| @@ -26,21 +26,11 @@ | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #ifdef VULKAN | ||||
|  | ||||
| #define BINDING_COLOR_TEXTURE 1 | ||||
|  | ||||
| #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv | ||||
|  | ||||
| #define BINDING_COLOR_TEXTURE 0 | ||||
|  | ||||
| #endif | ||||
|  | ||||
| layout (location = 0) in vec2 tex_coord; | ||||
|  | ||||
| layout (location = 0) out vec4 frag_color; | ||||
|  | ||||
| layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; | ||||
| layout (binding = 0) uniform sampler2D input_texture; | ||||
|  | ||||
| const bool ignore_alpha = true; | ||||
|  | ||||
|   | ||||
| @@ -3,22 +3,12 @@ | ||||
|  | ||||
| #version 460 core | ||||
|  | ||||
| #ifdef VULKAN | ||||
|  | ||||
| #define BINDING_COLOR_TEXTURE 1 | ||||
|  | ||||
| #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv | ||||
|  | ||||
| #define BINDING_COLOR_TEXTURE 0 | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| layout (location = 0) in vec2 frag_tex_coord; | ||||
|  | ||||
| layout (location = 0) out vec4 color; | ||||
|  | ||||
| layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; | ||||
| layout (binding = 0) uniform sampler2D color_texture; | ||||
|  | ||||
| vec4 cubic(float v) { | ||||
|     vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; | ||||
|   | ||||
| @@ -7,21 +7,11 @@ | ||||
|  | ||||
| #version 460 core | ||||
|  | ||||
| #ifdef VULKAN | ||||
|  | ||||
| #define BINDING_COLOR_TEXTURE 1 | ||||
|  | ||||
| #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv | ||||
|  | ||||
| #define BINDING_COLOR_TEXTURE 0 | ||||
|  | ||||
| #endif | ||||
|  | ||||
| layout(location = 0) in vec2 frag_tex_coord; | ||||
|  | ||||
| layout(location = 0) out vec4 color; | ||||
|  | ||||
| layout(binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; | ||||
| layout(binding = 0) uniform sampler2D color_texture; | ||||
|  | ||||
| const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308); | ||||
| const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703); | ||||
|   | ||||
| @@ -7,7 +7,7 @@ layout (location = 0) in vec2 frag_tex_coord; | ||||
|  | ||||
| layout (location = 0) out vec4 color; | ||||
|  | ||||
| layout (binding = 1) uniform sampler2D color_texture; | ||||
| layout (binding = 0) uniform sampler2D color_texture; | ||||
|  | ||||
| void main() { | ||||
|     color = texture(color_texture, frag_tex_coord); | ||||
|   | ||||
| @@ -3,16 +3,37 @@ | ||||
|  | ||||
| #version 460 core | ||||
|  | ||||
| layout (location = 0) in vec2 vert_position; | ||||
| layout (location = 1) in vec2 vert_tex_coord; | ||||
|  | ||||
| layout (location = 0) out vec2 frag_tex_coord; | ||||
|  | ||||
| layout (set = 0, binding = 0) uniform MatrixBlock { | ||||
|     mat4 modelview_matrix; | ||||
| struct ScreenRectVertex { | ||||
|     vec2 position; | ||||
|     vec2 tex_coord; | ||||
| }; | ||||
|  | ||||
| void main() { | ||||
|     gl_Position = modelview_matrix * vec4(vert_position, 0.0, 1.0); | ||||
|     frag_tex_coord = vert_tex_coord; | ||||
| layout (push_constant) uniform PushConstants { | ||||
|     mat4 modelview_matrix; | ||||
|     ScreenRectVertex vertices[4]; | ||||
| }; | ||||
|  | ||||
| // Vulkan spec 15.8.1: | ||||
| //   Any member of a push constant block that is declared as an | ||||
| //   array must only be accessed with dynamically uniform indices. | ||||
| ScreenRectVertex GetVertex(int index) { | ||||
|     switch (index) { | ||||
|     case 0: | ||||
|     default: | ||||
|         return vertices[0]; | ||||
|     case 1: | ||||
|         return vertices[1]; | ||||
|     case 2: | ||||
|         return vertices[2]; | ||||
|     case 3: | ||||
|         return vertices[3]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void main() { | ||||
|     ScreenRectVertex vertex = GetVertex(gl_VertexIndex); | ||||
|     gl_Position = modelview_matrix * vec4(vertex.position, 0.0, 1.0); | ||||
|     frag_tex_coord = vertex.tex_coord; | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
|  | ||||
| #extension GL_GOOGLE_include_directive : enable | ||||
|  | ||||
| #define VERSION 1 | ||||
| #define YUZU_USE_FP16 | ||||
|  | ||||
| #include "opengl_present_scaleforce.frag" | ||||
|   | ||||
| @@ -5,4 +5,6 @@ | ||||
|  | ||||
| #extension GL_GOOGLE_include_directive : enable | ||||
|  | ||||
| #define VERSION 1 | ||||
|  | ||||
| #include "opengl_present_scaleforce.frag" | ||||
|   | ||||
| @@ -27,43 +27,29 @@ vk::ShaderModule SelectScaleForceShader(const Device& device) { | ||||
|  | ||||
| } // Anonymous namespace | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, | ||||
|                                                      const MemoryAllocator& memory_allocator, | ||||
|                                                      size_t image_count, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, | ||||
| std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, frame_format, | ||||
|                                              CreateNearestNeighborSampler(device), | ||||
|                                              BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, | ||||
|                                               const MemoryAllocator& memory_allocator, | ||||
|                                               size_t image_count, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, | ||||
|                                              CreateBilinearSampler(device), | ||||
| std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||||
|                                              BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, | ||||
|                                              const MemoryAllocator& memory_allocator, | ||||
|                                              size_t image_count, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, | ||||
|                                              CreateBilinearSampler(device), | ||||
| std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||||
|                                              BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, | ||||
|                                               const MemoryAllocator& memory_allocator, | ||||
|                                               size_t image_count, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, | ||||
|                                              CreateBilinearSampler(device), | ||||
| std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||||
|                                              BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); | ||||
| } | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, | ||||
|                                                 const MemoryAllocator& memory_allocator, | ||||
|                                                 size_t image_count, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, | ||||
|                                              CreateBilinearSampler(device), | ||||
| std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format) { | ||||
|     return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||||
|                                              SelectScaleForceShader(device)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -7,24 +7,12 @@ | ||||
|  | ||||
| namespace Vulkan { | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, | ||||
|                                                      const MemoryAllocator& memory_allocator, | ||||
|                                                      size_t image_count, VkFormat frame_format); | ||||
| class MemoryAllocator; | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, | ||||
|                                               const MemoryAllocator& memory_allocator, | ||||
|                                               size_t image_count, VkFormat frame_format); | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, | ||||
|                                              const MemoryAllocator& memory_allocator, | ||||
|                                              size_t image_count, VkFormat frame_format); | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, | ||||
|                                               const MemoryAllocator& memory_allocator, | ||||
|                                               size_t image_count, VkFormat frame_format); | ||||
|  | ||||
| std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, | ||||
|                                                 const MemoryAllocator& memory_allocator, | ||||
|                                                 size_t image_count, VkFormat frame_format); | ||||
| std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format); | ||||
| std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format); | ||||
| std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format); | ||||
| std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format); | ||||
| std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format); | ||||
|  | ||||
| } // namespace Vulkan | ||||
|   | ||||
							
								
								
									
										336
									
								
								src/video_core/renderer_vulkan/present/layer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								src/video_core/renderer_vulkan/present/layer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,336 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "video_core/renderer_vulkan/vk_rasterizer.h" | ||||
|  | ||||
| #include "common/settings.h" | ||||
| #include "video_core/framebuffer_config.h" | ||||
| #include "video_core/renderer_vulkan/present/fsr.h" | ||||
| #include "video_core/renderer_vulkan/present/fxaa.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/smaa.h" | ||||
| #include "video_core/renderer_vulkan/present/util.h" | ||||
| #include "video_core/renderer_vulkan/vk_blit_screen.h" | ||||
| #include "video_core/textures/decoders.h" | ||||
|  | ||||
| namespace Vulkan { | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     using namespace VideoCore::Surface; | ||||
|     return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); | ||||
| } | ||||
|  | ||||
| std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     return static_cast<std::size_t>(framebuffer.stride) * | ||||
|            static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); | ||||
| } | ||||
|  | ||||
| VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     switch (framebuffer.pixel_format) { | ||||
|     case Service::android::PixelFormat::Rgba8888: | ||||
|     case Service::android::PixelFormat::Rgbx8888: | ||||
|         return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | ||||
|     case Service::android::PixelFormat::Rgb565: | ||||
|         return VK_FORMAT_R5G6B5_UNORM_PACK16; | ||||
|     case Service::android::PixelFormat::Bgra8888: | ||||
|         return VK_FORMAT_B8G8R8A8_UNORM; | ||||
|     default: | ||||
|         UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | ||||
|                           static_cast<u32>(framebuffer.pixel_format)); | ||||
|         return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // Anonymous namespace | ||||
|  | ||||
| Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, | ||||
|              Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, | ||||
|              VkExtent2D output_size, VkDescriptorSetLayout layout) | ||||
|     : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_), | ||||
|       device_memory(device_memory_), image_count(image_count_) { | ||||
|     CreateDescriptorPool(); | ||||
|     CreateDescriptorSets(layout); | ||||
|     if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||||
|         CreateFSR(output_size); | ||||
|     } | ||||
| } | ||||
|  | ||||
| Layer::~Layer() { | ||||
|     ReleaseRawImages(); | ||||
| } | ||||
|  | ||||
| void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, | ||||
|                           VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, | ||||
|                           VkSampler sampler, size_t image_index, | ||||
|                           const Tegra::FramebufferConfig& framebuffer, | ||||
|                           const Layout::FramebufferLayout& layout) { | ||||
|     const auto texture_info = rasterizer.AccelerateDisplay( | ||||
|         framebuffer, framebuffer.address + framebuffer.offset, framebuffer.stride); | ||||
|     const u32 texture_width = texture_info ? texture_info->width : framebuffer.width; | ||||
|     const u32 texture_height = texture_info ? texture_info->height : framebuffer.height; | ||||
|     const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width; | ||||
|     const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height; | ||||
|     const bool use_accelerated = texture_info.has_value(); | ||||
|  | ||||
|     RefreshResources(framebuffer); | ||||
|     SetAntiAliasPass(); | ||||
|  | ||||
|     // Finish any pending renderpass | ||||
|     scheduler.RequestOutsideRenderPassOperationContext(); | ||||
|     scheduler.Wait(resource_ticks[image_index]); | ||||
|     SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); }); | ||||
|  | ||||
|     if (!use_accelerated) { | ||||
|         UpdateRawImage(framebuffer, image_index); | ||||
|     } | ||||
|  | ||||
|     VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index]; | ||||
|     VkImageView source_image_view = | ||||
|         texture_info ? texture_info->image_view : *raw_image_views[image_index]; | ||||
|  | ||||
|     anti_alias->Draw(scheduler, image_index, &source_image, &source_image_view); | ||||
|  | ||||
|     auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height); | ||||
|     const VkExtent2D render_extent{ | ||||
|         .width = scaled_width, | ||||
|         .height = scaled_height, | ||||
|     }; | ||||
|  | ||||
|     if (fsr) { | ||||
|         source_image_view = fsr->Draw(scheduler, image_index, source_image, source_image_view, | ||||
|                                       render_extent, crop_rect); | ||||
|         crop_rect = {0, 0, 1, 1}; | ||||
|     } | ||||
|  | ||||
|     SetMatrixData(*out_push_constants, layout); | ||||
|     SetVertexData(*out_push_constants, layout, crop_rect); | ||||
|  | ||||
|     UpdateDescriptorSet(source_image_view, sampler, image_index); | ||||
|     *out_descriptor_set = descriptor_sets[image_index]; | ||||
| } | ||||
|  | ||||
| void Layer::CreateDescriptorPool() { | ||||
|     descriptor_pool = CreateWrappedDescriptorPool(device, image_count, image_count); | ||||
| } | ||||
|  | ||||
| void Layer::CreateDescriptorSets(VkDescriptorSetLayout layout) { | ||||
|     const std::vector layouts(image_count, layout); | ||||
|     descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); | ||||
| } | ||||
|  | ||||
| void Layer::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     const VkBufferCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .size = CalculateBufferSize(framebuffer), | ||||
|         .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | | ||||
|                  VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, | ||||
|         .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||||
|         .queueFamilyIndexCount = 0, | ||||
|         .pQueueFamilyIndices = nullptr, | ||||
|     }; | ||||
|  | ||||
|     buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload); | ||||
| } | ||||
|  | ||||
| void Layer::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     const auto format = GetFormat(framebuffer); | ||||
|     resource_ticks.resize(image_count); | ||||
|     raw_images.resize(image_count); | ||||
|     raw_image_views.resize(image_count); | ||||
|  | ||||
|     for (size_t i = 0; i < image_count; ++i) { | ||||
|         raw_images[i] = | ||||
|             CreateWrappedImage(memory_allocator, {framebuffer.width, framebuffer.height}, format); | ||||
|         raw_image_views[i] = CreateWrappedImageView(device, raw_images[i], format); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Layer::CreateFSR(VkExtent2D output_size) { | ||||
|     fsr = std::make_unique<FSR>(device, memory_allocator, image_count, output_size); | ||||
| } | ||||
|  | ||||
| void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     if (framebuffer.width == raw_width && framebuffer.height == raw_height && | ||||
|         framebuffer.pixel_format == pixel_format && !raw_images.empty()) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     raw_width = framebuffer.width; | ||||
|     raw_height = framebuffer.height; | ||||
|     pixel_format = framebuffer.pixel_format; | ||||
|     anti_alias.reset(); | ||||
|  | ||||
|     ReleaseRawImages(); | ||||
|     CreateStagingBuffer(framebuffer); | ||||
|     CreateRawImages(framebuffer); | ||||
| } | ||||
|  | ||||
| void Layer::SetAntiAliasPass() { | ||||
|     if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     anti_alias_setting = Settings::values.anti_aliasing.GetValue(); | ||||
|  | ||||
|     const VkExtent2D render_area{ | ||||
|         .width = Settings::values.resolution_info.ScaleUp(raw_width), | ||||
|         .height = Settings::values.resolution_info.ScaleUp(raw_height), | ||||
|     }; | ||||
|  | ||||
|     switch (anti_alias_setting) { | ||||
|     case Settings::AntiAliasing::Fxaa: | ||||
|         anti_alias = std::make_unique<FXAA>(device, memory_allocator, image_count, render_area); | ||||
|         break; | ||||
|     case Settings::AntiAliasing::Smaa: | ||||
|         anti_alias = std::make_unique<SMAA>(device, memory_allocator, image_count, render_area); | ||||
|         break; | ||||
|     default: | ||||
|         anti_alias = std::make_unique<NoAA>(); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Layer::ReleaseRawImages() { | ||||
|     for (const u64 tick : resource_ticks) { | ||||
|         scheduler.Wait(tick); | ||||
|     } | ||||
|     raw_images.clear(); | ||||
|     buffer.reset(); | ||||
| } | ||||
|  | ||||
| u64 Layer::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const { | ||||
|     return GetSizeInBytes(framebuffer) * image_count; | ||||
| } | ||||
|  | ||||
| u64 Layer::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, | ||||
|                              size_t image_index) const { | ||||
|     return GetSizeInBytes(framebuffer) * image_index; | ||||
| } | ||||
|  | ||||
| void Layer::SetMatrixData(PresentPushConstants& data, | ||||
|                           const Layout::FramebufferLayout& layout) const { | ||||
|     data.modelview_matrix = | ||||
|         MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height)); | ||||
| } | ||||
|  | ||||
| void Layer::SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, | ||||
|                           const Common::Rectangle<f32>& crop) const { | ||||
|     // Map the coordinates to the screen. | ||||
|     const auto& screen = layout.screen; | ||||
|     const auto x = static_cast<f32>(screen.left); | ||||
|     const auto y = static_cast<f32>(screen.top); | ||||
|     const auto w = static_cast<f32>(screen.GetWidth()); | ||||
|     const auto h = static_cast<f32>(screen.GetHeight()); | ||||
|  | ||||
|     data.vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); | ||||
|     data.vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); | ||||
|     data.vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); | ||||
|     data.vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); | ||||
| } | ||||
|  | ||||
| void Layer::UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index) { | ||||
|     const VkDescriptorImageInfo image_info{ | ||||
|         .sampler = sampler, | ||||
|         .imageView = image_view, | ||||
|         .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|     }; | ||||
|  | ||||
|     const VkWriteDescriptorSet sampler_write{ | ||||
|         .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||||
|         .pNext = nullptr, | ||||
|         .dstSet = descriptor_sets[image_index], | ||||
|         .dstBinding = 0, | ||||
|         .dstArrayElement = 0, | ||||
|         .descriptorCount = 1, | ||||
|         .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||||
|         .pImageInfo = &image_info, | ||||
|         .pBufferInfo = nullptr, | ||||
|         .pTexelBufferView = nullptr, | ||||
|     }; | ||||
|  | ||||
|     device.GetLogical().UpdateDescriptorSets(std::array{sampler_write}, {}); | ||||
| } | ||||
|  | ||||
| void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index) { | ||||
|     const std::span<u8> mapped_span = buffer.Mapped(); | ||||
|  | ||||
|     const u64 image_offset = GetRawImageOffset(framebuffer, image_index); | ||||
|  | ||||
|     const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset; | ||||
|     const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr); | ||||
|  | ||||
|     // TODO(Rodrigo): Read this from HLE | ||||
|     constexpr u32 block_height_log2 = 4; | ||||
|     const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); | ||||
|     const u64 linear_size{GetSizeInBytes(framebuffer)}; | ||||
|     const u64 tiled_size{Tegra::Texture::CalculateSize( | ||||
|         true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | ||||
|     Tegra::Texture::UnswizzleTexture( | ||||
|         mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), | ||||
|         bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); | ||||
|  | ||||
|     const VkBufferImageCopy copy{ | ||||
|         .bufferOffset = image_offset, | ||||
|         .bufferRowLength = 0, | ||||
|         .bufferImageHeight = 0, | ||||
|         .imageSubresource = | ||||
|             { | ||||
|                 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||
|                 .mipLevel = 0, | ||||
|                 .baseArrayLayer = 0, | ||||
|                 .layerCount = 1, | ||||
|             }, | ||||
|         .imageOffset = {.x = 0, .y = 0, .z = 0}, | ||||
|         .imageExtent = | ||||
|             { | ||||
|                 .width = framebuffer.width, | ||||
|                 .height = framebuffer.height, | ||||
|                 .depth = 1, | ||||
|             }, | ||||
|     }; | ||||
|     scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) { | ||||
|         const VkImage image = *raw_images[index]; | ||||
|         const VkImageMemoryBarrier base_barrier{ | ||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||||
|             .pNext = nullptr, | ||||
|             .srcAccessMask = 0, | ||||
|             .dstAccessMask = 0, | ||||
|             .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|             .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|             .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||
|             .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||
|             .image = image, | ||||
|             .subresourceRange{ | ||||
|                 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||
|                 .baseMipLevel = 0, | ||||
|                 .levelCount = 1, | ||||
|                 .baseArrayLayer = 0, | ||||
|                 .layerCount = 1, | ||||
|             }, | ||||
|         }; | ||||
|         VkImageMemoryBarrier read_barrier = base_barrier; | ||||
|         read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | ||||
|         read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | ||||
|         read_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; | ||||
|  | ||||
|         VkImageMemoryBarrier write_barrier = base_barrier; | ||||
|         write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | ||||
|         write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | ||||
|         write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; | ||||
|  | ||||
|         cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, | ||||
|                                read_barrier); | ||||
|         cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); | ||||
|         cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, | ||||
|                                VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | | ||||
|                                    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | ||||
|                                0, write_barrier); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| } // namespace Vulkan | ||||
							
								
								
									
										92
									
								
								src/video_core/renderer_vulkan/present/layer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/video_core/renderer_vulkan/present/layer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/math_util.h" | ||||
| #include "video_core/host1x/gpu_device_memory_manager.h" | ||||
| #include "video_core/vulkan_common/vulkan_wrapper.h" | ||||
|  | ||||
| namespace Layout { | ||||
| struct FramebufferLayout; | ||||
| } | ||||
|  | ||||
| namespace Tegra { | ||||
| struct FramebufferConfig; | ||||
| } | ||||
|  | ||||
| namespace Service::android { | ||||
| enum class PixelFormat : u32; | ||||
| } | ||||
|  | ||||
| namespace Settings { | ||||
| enum class AntiAliasing : u32; | ||||
| } | ||||
|  | ||||
| namespace Vulkan { | ||||
|  | ||||
| class AntiAliasPass; | ||||
| class Device; | ||||
| class FSR; | ||||
| class MemoryAllocator; | ||||
| struct PresentPushConstants; | ||||
| class RasterizerVulkan; | ||||
| class Scheduler; | ||||
|  | ||||
| class Layer final { | ||||
| public: | ||||
|     explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler, | ||||
|                    Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count, | ||||
|                    VkExtent2D output_size, VkDescriptorSetLayout layout); | ||||
|     ~Layer(); | ||||
|  | ||||
|     void ConfigureDraw(PresentPushConstants* out_push_constants, | ||||
|                        VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, | ||||
|                        VkSampler sampler, size_t image_index, | ||||
|                        const Tegra::FramebufferConfig& framebuffer, | ||||
|                        const Layout::FramebufferLayout& layout); | ||||
|  | ||||
| private: | ||||
|     void CreateDescriptorPool(); | ||||
|     void CreateDescriptorSets(VkDescriptorSetLayout layout); | ||||
|     void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); | ||||
|     void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); | ||||
|     void CreateFSR(VkExtent2D output_size); | ||||
|  | ||||
|     void RefreshResources(const Tegra::FramebufferConfig& framebuffer); | ||||
|     void SetAntiAliasPass(); | ||||
|     void ReleaseRawImages(); | ||||
|  | ||||
|     u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; | ||||
|     u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, size_t image_index) const; | ||||
|  | ||||
|     void SetMatrixData(PresentPushConstants& data, const Layout::FramebufferLayout& layout) const; | ||||
|     void SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, | ||||
|                        const Common::Rectangle<f32>& crop) const; | ||||
|     void UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index); | ||||
|     void UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index); | ||||
|  | ||||
| private: | ||||
|     const Device& device; | ||||
|     MemoryAllocator& memory_allocator; | ||||
|     Scheduler& scheduler; | ||||
|     Tegra::MaxwellDeviceMemoryManager& device_memory; | ||||
|     const size_t image_count{}; | ||||
|     vk::DescriptorPool descriptor_pool{}; | ||||
|     vk::DescriptorSets descriptor_sets{}; | ||||
|  | ||||
|     vk::Buffer buffer{}; | ||||
|     std::vector<vk::Image> raw_images{}; | ||||
|     std::vector<vk::ImageView> raw_image_views{}; | ||||
|     u32 raw_width{}; | ||||
|     u32 raw_height{}; | ||||
|     Service::android::PixelFormat pixel_format{}; | ||||
|  | ||||
|     Settings::AntiAliasing anti_alias_setting{}; | ||||
|     std::unique_ptr<AntiAliasPass> anti_alias{}; | ||||
|  | ||||
|     std::unique_ptr<FSR> fsr{}; | ||||
|     std::vector<u64> resource_ticks{}; | ||||
| }; | ||||
|  | ||||
| } // namespace Vulkan | ||||
| @@ -0,0 +1,34 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace Vulkan { | ||||
|  | ||||
| struct ScreenRectVertex { | ||||
|     ScreenRectVertex() = default; | ||||
|     explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {} | ||||
|  | ||||
|     std::array<f32, 2> position; | ||||
|     std::array<f32, 2> tex_coord; | ||||
| }; | ||||
|  | ||||
| static inline std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { | ||||
|     // clang-format off | ||||
|     return { 2.f / width, 0.f,          0.f, 0.f, | ||||
|              0.f,         2.f / height, 0.f, 0.f, | ||||
|              0.f,         0.f,          1.f, 0.f, | ||||
|             -1.f,        -1.f,          0.f, 1.f}; | ||||
|     // clang-format on | ||||
| } | ||||
|  | ||||
| struct PresentPushConstants { | ||||
|     std::array<f32, 4 * 4> modelview_matrix; | ||||
|     std::array<ScreenRectVertex, 4> vertices; | ||||
| }; | ||||
|  | ||||
| static_assert(sizeof(PresentPushConstants) <= 128, "Push constants are too large"); | ||||
|  | ||||
| } // namespace Vulkan | ||||
| @@ -113,16 +113,18 @@ vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkF | ||||
|     }); | ||||
| } | ||||
|  | ||||
| vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format) { | ||||
| vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, | ||||
|                                        VkImageLayout initial_layout) { | ||||
|     const VkAttachmentDescription attachment{ | ||||
|         .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, | ||||
|         .format = format, | ||||
|         .samples = VK_SAMPLE_COUNT_1_BIT, | ||||
|         .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, | ||||
|         .loadOp = initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE | ||||
|                                                               : VK_ATTACHMENT_LOAD_OP_LOAD, | ||||
|         .storeOp = VK_ATTACHMENT_STORE_OP_STORE, | ||||
|         .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, | ||||
|         .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, | ||||
|         .initialLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|         .initialLayout = initial_layout, | ||||
|         .finalLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|     }; | ||||
|  | ||||
| @@ -244,8 +246,7 @@ vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout( | ||||
|             .binding = static_cast<u32>(i), | ||||
|             .descriptorType = std::data(types)[i], | ||||
|             .descriptorCount = 1, | ||||
|             .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | | ||||
|                           VK_SHADER_STAGE_COMPUTE_BIT, | ||||
|             .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, | ||||
|             .pImmutableSamplers = nullptr, | ||||
|         }; | ||||
|     } | ||||
| @@ -285,7 +286,8 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, | ||||
|  | ||||
| vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | ||||
|                                    vk::PipelineLayout& layout, | ||||
|                                    std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { | ||||
|                                    std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, | ||||
|                                    bool enable_blending) { | ||||
|     const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ | ||||
|         { | ||||
|             .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||||
| @@ -363,7 +365,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | ||||
|         .alphaToOneEnable = VK_FALSE, | ||||
|     }; | ||||
|  | ||||
|     constexpr VkPipelineColorBlendAttachmentState color_blend_attachment{ | ||||
|     constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ | ||||
|         .blendEnable = VK_FALSE, | ||||
|         .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||||
|         .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||||
| @@ -375,6 +377,18 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | ||||
|                           VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||||
|     }; | ||||
|  | ||||
|     constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{ | ||||
|         .blendEnable = VK_TRUE, | ||||
|         .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, | ||||
|         .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, | ||||
|         .colorBlendOp = VK_BLEND_OP_ADD, | ||||
|         .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, | ||||
|         .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||||
|         .alphaBlendOp = VK_BLEND_OP_ADD, | ||||
|         .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||||
|                           VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||||
|     }; | ||||
|  | ||||
|     const VkPipelineColorBlendStateCreateInfo color_blend_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
| @@ -382,7 +396,8 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | ||||
|         .logicOpEnable = VK_FALSE, | ||||
|         .logicOp = VK_LOGIC_OP_COPY, | ||||
|         .attachmentCount = 1, | ||||
|         .pAttachments = &color_blend_attachment, | ||||
|         .pAttachments = | ||||
|             enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled, | ||||
|         .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -20,7 +20,8 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc | ||||
| void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); | ||||
|  | ||||
| vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); | ||||
| vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format); | ||||
| vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, | ||||
|                                        VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL); | ||||
| vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass, | ||||
|                                          vk::ImageView& dest_image, VkExtent2D extent); | ||||
| vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter = VK_FILTER_LINEAR); | ||||
| @@ -37,7 +38,8 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, | ||||
|                                                vk::DescriptorSetLayout& layout); | ||||
| vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | ||||
|                                    vk::PipelineLayout& layout, | ||||
|                                    std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders); | ||||
|                                    std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, | ||||
|                                    bool enable_blending = false); | ||||
| VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, | ||||
|                                               VkSampler sampler, VkImageView view, | ||||
|                                               VkDescriptorSet set, u32 binding); | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include <cstring> | ||||
|  | ||||
| #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" | ||||
| @@ -14,501 +15,123 @@ | ||||
|  | ||||
| namespace Vulkan { | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| struct ScreenRectVertex { | ||||
|     ScreenRectVertex() = default; | ||||
|     explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {} | ||||
|  | ||||
|     std::array<f32, 2> position; | ||||
|     std::array<f32, 2> tex_coord; | ||||
|  | ||||
|     static VkVertexInputBindingDescription GetDescription() { | ||||
|         return { | ||||
|             .binding = 0, | ||||
|             .stride = sizeof(ScreenRectVertex), | ||||
|             .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     static std::array<VkVertexInputAttributeDescription, 2> GetAttributes() { | ||||
|         return {{ | ||||
|             { | ||||
|                 .location = 0, | ||||
|                 .binding = 0, | ||||
|                 .format = VK_FORMAT_R32G32_SFLOAT, | ||||
|                 .offset = offsetof(ScreenRectVertex, position), | ||||
|             }, | ||||
|             { | ||||
|                 .location = 1, | ||||
|                 .binding = 0, | ||||
|                 .format = VK_FORMAT_R32G32_SFLOAT, | ||||
|                 .offset = offsetof(ScreenRectVertex, tex_coord), | ||||
|             }, | ||||
|         }}; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { | ||||
|     // clang-format off | ||||
|     return { 2.f / width, 0.f,          0.f, 0.f, | ||||
|              0.f,         2.f / height, 0.f, 0.f, | ||||
|              0.f,         0.f,          1.f, 0.f, | ||||
|             -1.f,        -1.f,          0.f, 1.f}; | ||||
|     // clang-format on | ||||
| } | ||||
|  | ||||
| } // Anonymous namespace | ||||
|  | ||||
| struct WindowAdaptPass::BufferData { | ||||
|     struct { | ||||
|         std::array<f32, 4 * 4> modelview_matrix; | ||||
|     } uniform; | ||||
|  | ||||
|     std::array<ScreenRectVertex, 4> vertices; | ||||
| }; | ||||
|  | ||||
| WindowAdaptPass::WindowAdaptPass(const Device& device_, const MemoryAllocator& memory_allocator, | ||||
|                                  size_t num_images, VkFormat frame_format, vk::Sampler&& sampler_, | ||||
|                                  vk::ShaderModule&& fragment_shader_) | ||||
| 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_)) { | ||||
|     CreateDescriptorPool(num_images); | ||||
|     CreateDescriptorSetLayout(); | ||||
|     CreateDescriptorSets(num_images); | ||||
|     CreatePipelineLayout(); | ||||
|     CreateVertexShader(); | ||||
|     CreateRenderPass(frame_format); | ||||
|     CreatePipeline(); | ||||
|     CreateBuffer(memory_allocator); | ||||
| } | ||||
|  | ||||
| WindowAdaptPass::~WindowAdaptPass() = default; | ||||
|  | ||||
| void WindowAdaptPass::Draw(Scheduler& scheduler, size_t image_index, VkImageView src_image_view, | ||||
|                            VkExtent2D src_image_extent, const Common::Rectangle<f32>& crop_rect, | ||||
| 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) { | ||||
|     ConfigureLayout(image_index, src_image_view, layout, crop_rect); | ||||
|  | ||||
|     const VkFramebuffer host_framebuffer{*dst->framebuffer}; | ||||
|     const VkRenderPass renderpass{*render_pass}; | ||||
|     const VkPipeline graphics_pipeline{*pipeline}; | ||||
|     const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; | ||||
|     const VkDescriptorSet descriptor_set{descriptor_sets[image_index]}; | ||||
|     const VkBuffer vertex_buffer{*buffer}; | ||||
|     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 VkClearValue clear_color{ | ||||
|             .color = {.float32 = {bg_red, bg_green, bg_blue, 1.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 VkRenderPassBeginInfo renderpass_bi{ | ||||
|             .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | ||||
|             .pNext = nullptr, | ||||
|             .renderPass = renderpass, | ||||
|             .framebuffer = host_framebuffer, | ||||
|             .renderArea = | ||||
|         const VkClearRect clear_rect{ | ||||
|             .rect = | ||||
|                 { | ||||
|                     .offset = {0, 0}, | ||||
|                     .extent = render_area, | ||||
|                 }, | ||||
|             .clearValueCount = 1, | ||||
|             .pClearValues = &clear_color, | ||||
|             .baseArrayLayer = 0, | ||||
|             .layerCount = 1, | ||||
|         }; | ||||
|         const VkViewport viewport{ | ||||
|             .x = 0.0f, | ||||
|             .y = 0.0f, | ||||
|             .width = static_cast<float>(render_area.width), | ||||
|             .height = static_cast<float>(render_area.height), | ||||
|             .minDepth = 0.0f, | ||||
|             .maxDepth = 1.0f, | ||||
|         }; | ||||
|         const VkRect2D scissor{ | ||||
|             .offset = {0, 0}, | ||||
|             .extent = render_area, | ||||
|         }; | ||||
|         cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); | ||||
|  | ||||
|         BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); | ||||
|         cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); | ||||
|  | ||||
|         cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); | ||||
|         cmdbuf.SetViewport(0, viewport); | ||||
|         cmdbuf.SetScissor(0, scissor); | ||||
|         cmdbuf.BindVertexBuffer(0, vertex_buffer, offsetof(BufferData, vertices)); | ||||
|         cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, | ||||
|                                   descriptor_set, {}); | ||||
|         cmdbuf.Draw(4, 1, 0, 0); | ||||
|         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::CreateDescriptorPool(size_t num_images) { | ||||
|     const std::array<VkDescriptorPoolSize, 2> pool_sizes{{ | ||||
|         { | ||||
|             .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | ||||
|             .descriptorCount = static_cast<u32>(num_images), | ||||
|         }, | ||||
|         { | ||||
|             .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||||
|             .descriptorCount = static_cast<u32>(num_images), | ||||
|         }, | ||||
|     }}; | ||||
|  | ||||
|     const VkDescriptorPoolCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .maxSets = static_cast<u32>(num_images), | ||||
|         .poolSizeCount = static_cast<u32>(pool_sizes.size()), | ||||
|         .pPoolSizes = pool_sizes.data(), | ||||
|     }; | ||||
|     descriptor_pool = device.GetLogical().CreateDescriptorPool(ci); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::CreateDescriptorSetLayout() { | ||||
|     const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{ | ||||
|         { | ||||
|             .binding = 0, | ||||
|             .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | ||||
|             .descriptorCount = 1, | ||||
|             .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, | ||||
|             .pImmutableSamplers = nullptr, | ||||
|         }, | ||||
|         { | ||||
|             .binding = 1, | ||||
|             .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||||
|             .descriptorCount = 1, | ||||
|             .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, | ||||
|             .pImmutableSamplers = nullptr, | ||||
|         }, | ||||
|     }}; | ||||
|  | ||||
|     const VkDescriptorSetLayoutCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .bindingCount = static_cast<u32>(layout_bindings.size()), | ||||
|         .pBindings = layout_bindings.data(), | ||||
|     }; | ||||
|  | ||||
|     descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci); | ||||
|     descriptor_set_layout = | ||||
|         CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::CreateDescriptorSets(size_t num_images) { | ||||
|     const std::vector layouts(num_images, *descriptor_set_layout); | ||||
|     descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); | ||||
| } | ||||
| void WindowAdaptPass::CreatePipelineLayout() { | ||||
|     const VkPushConstantRange range{ | ||||
|         .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, | ||||
|         .offset = 0, | ||||
|         .size = sizeof(PresentPushConstants), | ||||
|     }; | ||||
|  | ||||
| void WindowAdaptPass::CreateBuffer(const MemoryAllocator& memory_allocator) { | ||||
|     const VkBufferCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||||
|     pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .size = sizeof(BufferData), | ||||
|         .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | | ||||
|                  VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, | ||||
|         .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||||
|         .queueFamilyIndexCount = 0, | ||||
|         .pQueueFamilyIndices = nullptr, | ||||
|     }; | ||||
|  | ||||
|     buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { | ||||
|     const VkAttachmentDescription color_attachment{ | ||||
|         .flags = 0, | ||||
|         .format = frame_format, | ||||
|         .samples = VK_SAMPLE_COUNT_1_BIT, | ||||
|         .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, | ||||
|         .storeOp = VK_ATTACHMENT_STORE_OP_STORE, | ||||
|         .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, | ||||
|         .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | ||||
|         .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||||
|         .finalLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|     }; | ||||
|  | ||||
|     const VkAttachmentReference color_attachment_ref{ | ||||
|         .attachment = 0, | ||||
|         .layout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|     }; | ||||
|  | ||||
|     const VkSubpassDescription subpass_description{ | ||||
|         .flags = 0, | ||||
|         .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, | ||||
|         .inputAttachmentCount = 0, | ||||
|         .pInputAttachments = nullptr, | ||||
|         .colorAttachmentCount = 1, | ||||
|         .pColorAttachments = &color_attachment_ref, | ||||
|         .pResolveAttachments = nullptr, | ||||
|         .pDepthStencilAttachment = nullptr, | ||||
|         .preserveAttachmentCount = 0, | ||||
|         .pPreserveAttachments = nullptr, | ||||
|     }; | ||||
|  | ||||
|     const VkSubpassDependency dependency{ | ||||
|         .srcSubpass = VK_SUBPASS_EXTERNAL, | ||||
|         .dstSubpass = 0, | ||||
|         .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||||
|         .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||||
|         .srcAccessMask = 0, | ||||
|         .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, | ||||
|         .dependencyFlags = 0, | ||||
|     }; | ||||
|  | ||||
|     const VkRenderPassCreateInfo renderpass_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .attachmentCount = 1, | ||||
|         .pAttachments = &color_attachment, | ||||
|         .subpassCount = 1, | ||||
|         .pSubpasses = &subpass_description, | ||||
|         .dependencyCount = 1, | ||||
|         .pDependencies = &dependency, | ||||
|     }; | ||||
|  | ||||
|     render_pass = device.GetLogical().CreateRenderPass(renderpass_ci); | ||||
|         .setLayoutCount = 1, | ||||
|         .pSetLayouts = descriptor_set_layout.address(), | ||||
|         .pushConstantRangeCount = 1, | ||||
|         .pPushConstantRanges = &range, | ||||
|     }); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::CreateVertexShader() { | ||||
|     vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::CreatePipelineLayout() { | ||||
|     const VkPipelineLayoutCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .setLayoutCount = 1, | ||||
|         .pSetLayouts = descriptor_set_layout.address(), | ||||
|         .pushConstantRangeCount = 0, | ||||
|         .pPushConstantRanges = nullptr, | ||||
|     }; | ||||
|     pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::SetUniformData(BufferData& data, | ||||
|                                      const Layout::FramebufferLayout& layout) const { | ||||
|     data.uniform.modelview_matrix = | ||||
|         MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height)); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::SetVertexData(BufferData& data, const Layout::FramebufferLayout& layout, | ||||
|                                     const Common::Rectangle<f32>& crop) const { | ||||
|     // Map the coordinates to the screen. | ||||
|     const auto& screen = layout.screen; | ||||
|     const auto x = static_cast<f32>(screen.left); | ||||
|     const auto y = static_cast<f32>(screen.top); | ||||
|     const auto w = static_cast<f32>(screen.GetWidth()); | ||||
|     const auto h = static_cast<f32>(screen.GetHeight()); | ||||
|  | ||||
|     data.vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); | ||||
|     data.vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); | ||||
|     data.vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); | ||||
|     data.vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::UpdateDescriptorSet(size_t image_index, VkImageView image_view) { | ||||
|     const VkDescriptorBufferInfo buffer_info{ | ||||
|         .buffer = *buffer, | ||||
|         .offset = offsetof(BufferData, uniform), | ||||
|         .range = sizeof(BufferData::uniform), | ||||
|     }; | ||||
|  | ||||
|     const VkWriteDescriptorSet ubo_write{ | ||||
|         .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||||
|         .pNext = nullptr, | ||||
|         .dstSet = descriptor_sets[image_index], | ||||
|         .dstBinding = 0, | ||||
|         .dstArrayElement = 0, | ||||
|         .descriptorCount = 1, | ||||
|         .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | ||||
|         .pImageInfo = nullptr, | ||||
|         .pBufferInfo = &buffer_info, | ||||
|         .pTexelBufferView = nullptr, | ||||
|     }; | ||||
|  | ||||
|     const VkDescriptorImageInfo image_info{ | ||||
|         .sampler = *sampler, | ||||
|         .imageView = image_view, | ||||
|         .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|     }; | ||||
|  | ||||
|     const VkWriteDescriptorSet sampler_write{ | ||||
|         .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||||
|         .pNext = nullptr, | ||||
|         .dstSet = descriptor_sets[image_index], | ||||
|         .dstBinding = 1, | ||||
|         .dstArrayElement = 0, | ||||
|         .descriptorCount = 1, | ||||
|         .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||||
|         .pImageInfo = &image_info, | ||||
|         .pBufferInfo = nullptr, | ||||
|         .pTexelBufferView = nullptr, | ||||
|     }; | ||||
|  | ||||
|     device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {}); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::ConfigureLayout(size_t image_index, VkImageView image_view, | ||||
|                                       const Layout::FramebufferLayout& layout, | ||||
|                                       const Common::Rectangle<f32>& crop_rect) { | ||||
|     BufferData data; | ||||
|     SetUniformData(data, layout); | ||||
|     SetVertexData(data, layout, crop_rect); | ||||
|  | ||||
|     const std::span<u8> mapped_span = buffer.Mapped(); | ||||
|     std::memcpy(mapped_span.data(), &data, sizeof(data)); | ||||
|  | ||||
|     UpdateDescriptorSet(image_index, image_view); | ||||
| void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { | ||||
|     render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); | ||||
| } | ||||
|  | ||||
| void WindowAdaptPass::CreatePipeline() { | ||||
|     const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ | ||||
|         { | ||||
|             .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||||
|             .pNext = nullptr, | ||||
|             .flags = 0, | ||||
|             .stage = VK_SHADER_STAGE_VERTEX_BIT, | ||||
|             .module = *vertex_shader, | ||||
|             .pName = "main", | ||||
|             .pSpecializationInfo = nullptr, | ||||
|         }, | ||||
|         { | ||||
|             .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||||
|             .pNext = nullptr, | ||||
|             .flags = 0, | ||||
|             .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | ||||
|             .module = *fragment_shader, | ||||
|             .pName = "main", | ||||
|             .pSpecializationInfo = nullptr, | ||||
|         }, | ||||
|     }}; | ||||
|  | ||||
|     const auto vertex_binding_description = ScreenRectVertex::GetDescription(); | ||||
|     const auto vertex_attrs_description = ScreenRectVertex::GetAttributes(); | ||||
|  | ||||
|     const VkPipelineVertexInputStateCreateInfo vertex_input_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .vertexBindingDescriptionCount = 1, | ||||
|         .pVertexBindingDescriptions = &vertex_binding_description, | ||||
|         .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()}, | ||||
|         .pVertexAttributeDescriptions = vertex_attrs_description.data(), | ||||
|     }; | ||||
|  | ||||
|     const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, | ||||
|         .primitiveRestartEnable = VK_FALSE, | ||||
|     }; | ||||
|  | ||||
|     const VkPipelineViewportStateCreateInfo viewport_state_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .viewportCount = 1, | ||||
|         .pViewports = nullptr, | ||||
|         .scissorCount = 1, | ||||
|         .pScissors = nullptr, | ||||
|     }; | ||||
|  | ||||
|     const VkPipelineRasterizationStateCreateInfo rasterization_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .depthClampEnable = VK_FALSE, | ||||
|         .rasterizerDiscardEnable = VK_FALSE, | ||||
|         .polygonMode = VK_POLYGON_MODE_FILL, | ||||
|         .cullMode = VK_CULL_MODE_NONE, | ||||
|         .frontFace = VK_FRONT_FACE_CLOCKWISE, | ||||
|         .depthBiasEnable = VK_FALSE, | ||||
|         .depthBiasConstantFactor = 0.0f, | ||||
|         .depthBiasClamp = 0.0f, | ||||
|         .depthBiasSlopeFactor = 0.0f, | ||||
|         .lineWidth = 1.0f, | ||||
|     }; | ||||
|  | ||||
|     const VkPipelineMultisampleStateCreateInfo multisampling_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, | ||||
|         .sampleShadingEnable = VK_FALSE, | ||||
|         .minSampleShading = 0.0f, | ||||
|         .pSampleMask = nullptr, | ||||
|         .alphaToCoverageEnable = VK_FALSE, | ||||
|         .alphaToOneEnable = VK_FALSE, | ||||
|     }; | ||||
|  | ||||
|     const VkPipelineColorBlendAttachmentState color_blend_attachment{ | ||||
|         .blendEnable = VK_FALSE, | ||||
|         .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||||
|         .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||||
|         .colorBlendOp = VK_BLEND_OP_ADD, | ||||
|         .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||||
|         .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||||
|         .alphaBlendOp = VK_BLEND_OP_ADD, | ||||
|         .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||||
|                           VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||||
|     }; | ||||
|  | ||||
|     const VkPipelineColorBlendStateCreateInfo color_blend_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .logicOpEnable = VK_FALSE, | ||||
|         .logicOp = VK_LOGIC_OP_COPY, | ||||
|         .attachmentCount = 1, | ||||
|         .pAttachments = &color_blend_attachment, | ||||
|         .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, | ||||
|     }; | ||||
|  | ||||
|     static constexpr std::array dynamic_states{ | ||||
|         VK_DYNAMIC_STATE_VIEWPORT, | ||||
|         VK_DYNAMIC_STATE_SCISSOR, | ||||
|     }; | ||||
|     const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .dynamicStateCount = static_cast<u32>(dynamic_states.size()), | ||||
|         .pDynamicStates = dynamic_states.data(), | ||||
|     }; | ||||
|  | ||||
|     const VkGraphicsPipelineCreateInfo pipeline_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .stageCount = static_cast<u32>(shader_stages.size()), | ||||
|         .pStages = shader_stages.data(), | ||||
|         .pVertexInputState = &vertex_input_ci, | ||||
|         .pInputAssemblyState = &input_assembly_ci, | ||||
|         .pTessellationState = nullptr, | ||||
|         .pViewportState = &viewport_state_ci, | ||||
|         .pRasterizationState = &rasterization_ci, | ||||
|         .pMultisampleState = &multisampling_ci, | ||||
|         .pDepthStencilState = nullptr, | ||||
|         .pColorBlendState = &color_blend_ci, | ||||
|         .pDynamicState = &dynamic_state_ci, | ||||
|         .layout = *pipeline_layout, | ||||
|         .renderPass = *render_pass, | ||||
|         .subpass = 0, | ||||
|         .basePipelineHandle = 0, | ||||
|         .basePipelineIndex = 0, | ||||
|     }; | ||||
|  | ||||
|     pipeline = device.GetLogical().CreateGraphicsPipeline(pipeline_ci); | ||||
|     pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, | ||||
|                                      std::tie(vertex_shader, fragment_shader), false); | ||||
| } | ||||
|  | ||||
| } // namespace Vulkan | ||||
|   | ||||
| @@ -3,6 +3,8 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <list> | ||||
|  | ||||
| #include "common/math_util.h" | ||||
| #include "video_core/vulkan_common/vulkan_wrapper.h" | ||||
|  | ||||
| @@ -18,54 +20,39 @@ namespace Vulkan { | ||||
|  | ||||
| class Device; | ||||
| struct Frame; | ||||
| class MemoryAllocator; | ||||
| class Layer; | ||||
| class Scheduler; | ||||
| class RasterizerVulkan; | ||||
|  | ||||
| class WindowAdaptPass final { | ||||
| public: | ||||
|     explicit WindowAdaptPass(const Device& device, const MemoryAllocator& memory_allocator, | ||||
|                              size_t num_images, VkFormat frame_format, vk::Sampler&& sampler, | ||||
|     explicit WindowAdaptPass(const Device& device, VkFormat frame_format, vk::Sampler&& sampler, | ||||
|                              vk::ShaderModule&& fragment_shader); | ||||
|     ~WindowAdaptPass(); | ||||
|  | ||||
|     void Draw(Scheduler& scheduler, size_t image_index, VkImageView src_image_view, | ||||
|               VkExtent2D src_image_extent, const Common::Rectangle<f32>& crop_rect, | ||||
|     void 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); | ||||
|  | ||||
|     VkDescriptorSetLayout GetDescriptorSetLayout(); | ||||
|     VkRenderPass GetRenderPass(); | ||||
|  | ||||
| private: | ||||
|     struct BufferData; | ||||
|  | ||||
|     void SetUniformData(BufferData& data, const Layout::FramebufferLayout& layout) const; | ||||
|     void SetVertexData(BufferData& data, const Layout::FramebufferLayout& layout, | ||||
|                        const Common::Rectangle<f32>& crop_rect) const; | ||||
|     void UpdateDescriptorSet(size_t image_index, VkImageView image_view); | ||||
|     void ConfigureLayout(size_t image_index, VkImageView image_view, | ||||
|                          const Layout::FramebufferLayout& layout, | ||||
|                          const Common::Rectangle<f32>& crop_rect); | ||||
|  | ||||
|     void CreateDescriptorPool(size_t num_images); | ||||
|     void CreateDescriptorSetLayout(); | ||||
|     void CreateDescriptorSets(size_t num_images); | ||||
|     void CreatePipelineLayout(); | ||||
|     void CreateVertexShader(); | ||||
|     void CreateRenderPass(VkFormat frame_format); | ||||
|     void CreatePipeline(); | ||||
|     void CreateBuffer(const MemoryAllocator& memory_allocator); | ||||
|  | ||||
| private: | ||||
|     const Device& device; | ||||
|     vk::DescriptorPool descriptor_pool; | ||||
|     vk::DescriptorSetLayout descriptor_set_layout; | ||||
|     vk::DescriptorSets descriptor_sets; | ||||
|     vk::PipelineLayout pipeline_layout; | ||||
|     vk::Sampler sampler; | ||||
|     vk::ShaderModule vertex_shader; | ||||
|     vk::ShaderModule fragment_shader; | ||||
|     vk::RenderPass render_pass; | ||||
|     vk::Pipeline pipeline; | ||||
|     vk::Buffer buffer; | ||||
| }; | ||||
|  | ||||
| } // namespace Vulkan | ||||
|   | ||||
| @@ -125,9 +125,9 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     RenderScreenshot(*framebuffer); | ||||
|     RenderScreenshot(framebuffer); | ||||
|     Frame* frame = present_manager.GetRenderFrame(); | ||||
|     blit_swapchain.DrawToFrame(rasterizer, frame, *framebuffer, | ||||
|     blit_swapchain.DrawToFrame(rasterizer, frame, std::span(framebuffer, 1), | ||||
|                                render_window.GetFramebufferLayout(), swapchain.GetImageCount(), | ||||
|                                swapchain.GetImageViewFormat()); | ||||
|     scheduler.Flush(*frame->render_ready); | ||||
| @@ -163,7 +163,7 @@ void RendererVulkan::Report() const { | ||||
|     telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | ||||
| } | ||||
|  | ||||
| void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer) { | ||||
| void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig* framebuffer) { | ||||
|     if (!renderer_settings.screenshot_requested) { | ||||
|         return; | ||||
|     } | ||||
| @@ -228,7 +228,7 @@ void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& fr | ||||
|         }; | ||||
|     }(); | ||||
|  | ||||
|     blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffer, layout, 1, | ||||
|     blit_screenshot.DrawToFrame(rasterizer, &frame, std::span(framebuffer, 1), layout, 1, | ||||
|                                 VK_FORMAT_B8G8R8A8_UNORM); | ||||
|  | ||||
|     const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); | ||||
|   | ||||
| @@ -59,7 +59,7 @@ public: | ||||
| private: | ||||
|     void Report() const; | ||||
|  | ||||
|     void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer); | ||||
|     void RenderScreenshot(const Tegra::FramebufferConfig* framebuffer); | ||||
|  | ||||
|     Core::TelemetrySession& telemetry_session; | ||||
|     Tegra::MaxwellDeviceMemoryManager& device_memory; | ||||
|   | ||||
| @@ -1,65 +1,15 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <array> | ||||
| #include <cstring> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/core.h" | ||||
| #include "core/frontend/emu_window.h" | ||||
| #include "video_core/gpu.h" | ||||
| #include "video_core/host1x/gpu_device_memory_manager.h" | ||||
| #include "video_core/framebuffer_config.h" | ||||
| #include "video_core/renderer_vulkan/present/filters.h" | ||||
| #include "video_core/renderer_vulkan/present/fsr.h" | ||||
| #include "video_core/renderer_vulkan/present/fxaa.h" | ||||
| #include "video_core/renderer_vulkan/present/smaa.h" | ||||
| #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||||
| #include "video_core/renderer_vulkan/present/layer.h" | ||||
| #include "video_core/renderer_vulkan/vk_blit_screen.h" | ||||
| #include "video_core/renderer_vulkan/vk_present_manager.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_shader_util.h" | ||||
| #include "video_core/surface.h" | ||||
| #include "video_core/textures/decoders.h" | ||||
| #include "video_core/vulkan_common/vulkan_device.h" | ||||
| #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||||
| #include "video_core/vulkan_common/vulkan_wrapper.h" | ||||
|  | ||||
| namespace Vulkan { | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     using namespace VideoCore::Surface; | ||||
|     return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); | ||||
| } | ||||
|  | ||||
| std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     return static_cast<std::size_t>(framebuffer.stride) * | ||||
|            static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); | ||||
| } | ||||
|  | ||||
| VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     switch (framebuffer.pixel_format) { | ||||
|     case Service::android::PixelFormat::Rgba8888: | ||||
|     case Service::android::PixelFormat::Rgbx8888: | ||||
|         return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | ||||
|     case Service::android::PixelFormat::Rgb565: | ||||
|         return VK_FORMAT_R5G6B5_UNORM_PACK16; | ||||
|     case Service::android::PixelFormat::Bgra8888: | ||||
|         return VK_FORMAT_B8G8R8A8_UNORM; | ||||
|     default: | ||||
|         UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | ||||
|                           static_cast<u32>(framebuffer.pixel_format)); | ||||
|         return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // Anonymous namespace | ||||
|  | ||||
| BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, | ||||
|                        MemoryAllocator& memory_allocator_, PresentManager& present_manager_, | ||||
|                        Scheduler& scheduler_) | ||||
| @@ -75,194 +25,35 @@ void BlitScreen::WaitIdle() { | ||||
|     device.GetLogical().WaitIdle(); | ||||
| } | ||||
|  | ||||
| void BlitScreen::SetWindowAdaptPass(const Layout::FramebufferLayout& layout) { | ||||
| void BlitScreen::SetWindowAdaptPass() { | ||||
|     layers.clear(); | ||||
|     scaling_filter = Settings::values.scaling_filter.GetValue(); | ||||
|  | ||||
|     const VkExtent2D adapt_size{ | ||||
|         .width = layout.screen.GetWidth(), | ||||
|         .height = layout.screen.GetHeight(), | ||||
|     }; | ||||
|  | ||||
|     fsr.reset(); | ||||
|  | ||||
|     switch (scaling_filter) { | ||||
|     case Settings::ScalingFilter::NearestNeighbor: | ||||
|         window_adapt = | ||||
|             MakeNearestNeighbor(device, memory_allocator, image_count, swapchain_view_format); | ||||
|         window_adapt = MakeNearestNeighbor(device, swapchain_view_format); | ||||
|         break; | ||||
|     case Settings::ScalingFilter::Bicubic: | ||||
|         window_adapt = MakeBicubic(device, memory_allocator, image_count, swapchain_view_format); | ||||
|         window_adapt = MakeBicubic(device, swapchain_view_format); | ||||
|         break; | ||||
|     case Settings::ScalingFilter::Gaussian: | ||||
|         window_adapt = MakeGaussian(device, memory_allocator, image_count, swapchain_view_format); | ||||
|         window_adapt = MakeGaussian(device, swapchain_view_format); | ||||
|         break; | ||||
|     case Settings::ScalingFilter::ScaleForce: | ||||
|         window_adapt = MakeScaleForce(device, memory_allocator, image_count, swapchain_view_format); | ||||
|         window_adapt = MakeScaleForce(device, swapchain_view_format); | ||||
|         break; | ||||
|     case Settings::ScalingFilter::Fsr: | ||||
|         fsr = std::make_unique<FSR>(device, memory_allocator, image_count, adapt_size); | ||||
|         [[fallthrough]]; | ||||
|     case Settings::ScalingFilter::Bilinear: | ||||
|     default: | ||||
|         window_adapt = MakeBilinear(device, memory_allocator, image_count, swapchain_view_format); | ||||
|         window_adapt = MakeBilinear(device, swapchain_view_format); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void BlitScreen::SetAntiAliasPass() { | ||||
|     if (anti_alias && anti_aliasing == Settings::values.anti_aliasing.GetValue()) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     anti_aliasing = Settings::values.anti_aliasing.GetValue(); | ||||
|  | ||||
|     const VkExtent2D render_area{ | ||||
|         .width = Settings::values.resolution_info.ScaleUp(raw_width), | ||||
|         .height = Settings::values.resolution_info.ScaleUp(raw_height), | ||||
|     }; | ||||
|  | ||||
|     switch (anti_aliasing) { | ||||
|     case Settings::AntiAliasing::Fxaa: | ||||
|         anti_alias = std::make_unique<FXAA>(device, memory_allocator, image_count, render_area); | ||||
|         break; | ||||
|     case Settings::AntiAliasing::Smaa: | ||||
|         anti_alias = std::make_unique<SMAA>(device, memory_allocator, image_count, render_area); | ||||
|         break; | ||||
|     default: | ||||
|         anti_alias = std::make_unique<NoAA>(); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void BlitScreen::Draw(RasterizerVulkan& rasterizer, const Tegra::FramebufferConfig& framebuffer, | ||||
|                       const Layout::FramebufferLayout& layout, Frame* dst) { | ||||
|     const auto texture_info = rasterizer.AccelerateDisplay( | ||||
|         framebuffer, framebuffer.address + framebuffer.offset, framebuffer.stride); | ||||
|     const u32 texture_width = texture_info ? texture_info->width : framebuffer.width; | ||||
|     const u32 texture_height = texture_info ? texture_info->height : framebuffer.height; | ||||
|     const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width; | ||||
|     const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height; | ||||
|     const bool use_accelerated = texture_info.has_value(); | ||||
|  | ||||
|     RefreshResources(framebuffer); | ||||
|     SetAntiAliasPass(); | ||||
|  | ||||
|     // Finish any pending renderpass | ||||
|     scheduler.RequestOutsideRenderPassOperationContext(); | ||||
|  | ||||
|     scheduler.Wait(resource_ticks[image_index]); | ||||
|     SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); }); | ||||
|  | ||||
|     VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index]; | ||||
|     VkImageView source_image_view = | ||||
|         texture_info ? texture_info->image_view : *raw_image_views[image_index]; | ||||
|  | ||||
|     const std::span<u8> mapped_span = buffer.Mapped(); | ||||
|  | ||||
|     if (!use_accelerated) { | ||||
|         const u64 image_offset = GetRawImageOffset(framebuffer); | ||||
|  | ||||
|         const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset; | ||||
|         const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr); | ||||
|  | ||||
|         // TODO(Rodrigo): Read this from HLE | ||||
|         constexpr u32 block_height_log2 = 4; | ||||
|         const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); | ||||
|         const u64 linear_size{GetSizeInBytes(framebuffer)}; | ||||
|         const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel, | ||||
|                                                            framebuffer.stride, framebuffer.height, | ||||
|                                                            1, block_height_log2, 0)}; | ||||
|         Tegra::Texture::UnswizzleTexture( | ||||
|             mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), | ||||
|             bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); | ||||
|  | ||||
|         const VkBufferImageCopy copy{ | ||||
|             .bufferOffset = image_offset, | ||||
|             .bufferRowLength = 0, | ||||
|             .bufferImageHeight = 0, | ||||
|             .imageSubresource = | ||||
|                 { | ||||
|                     .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||
|                     .mipLevel = 0, | ||||
|                     .baseArrayLayer = 0, | ||||
|                     .layerCount = 1, | ||||
|                 }, | ||||
|             .imageOffset = {.x = 0, .y = 0, .z = 0}, | ||||
|             .imageExtent = | ||||
|                 { | ||||
|                     .width = framebuffer.width, | ||||
|                     .height = framebuffer.height, | ||||
|                     .depth = 1, | ||||
|                 }, | ||||
|         }; | ||||
|         scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) { | ||||
|             const VkImage image = *raw_images[index]; | ||||
|             const VkImageMemoryBarrier base_barrier{ | ||||
|                 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||||
|                 .pNext = nullptr, | ||||
|                 .srcAccessMask = 0, | ||||
|                 .dstAccessMask = 0, | ||||
|                 .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|                 .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|                 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||
|                 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||
|                 .image = image, | ||||
|                 .subresourceRange{ | ||||
|                     .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||
|                     .baseMipLevel = 0, | ||||
|                     .levelCount = 1, | ||||
|                     .baseArrayLayer = 0, | ||||
|                     .layerCount = 1, | ||||
|                 }, | ||||
|             }; | ||||
|             VkImageMemoryBarrier read_barrier = base_barrier; | ||||
|             read_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; | ||||
|             read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | ||||
|             read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | ||||
|  | ||||
|             VkImageMemoryBarrier write_barrier = base_barrier; | ||||
|             write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | ||||
|             write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | ||||
|  | ||||
|             cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, | ||||
|                                    read_barrier); | ||||
|             cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_GENERAL, copy); | ||||
|             cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, | ||||
|                                    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | | ||||
|                                        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | ||||
|                                    0, write_barrier); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     anti_alias->Draw(scheduler, image_index, &source_image, &source_image_view); | ||||
|  | ||||
|     const auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height); | ||||
|     const VkExtent2D render_extent{ | ||||
|         .width = scaled_width, | ||||
|         .height = scaled_height, | ||||
|     }; | ||||
|  | ||||
|     if (fsr) { | ||||
|         const VkExtent2D adapt_size{ | ||||
|             .width = layout.screen.GetWidth(), | ||||
|             .height = layout.screen.GetHeight(), | ||||
|         }; | ||||
|  | ||||
|         source_image_view = fsr->Draw(scheduler, image_index, source_image, source_image_view, | ||||
|                                       render_extent, crop_rect); | ||||
|  | ||||
|         const Common::Rectangle<f32> output_crop{0, 0, 1, 1}; | ||||
|         window_adapt->Draw(scheduler, image_index, source_image_view, adapt_size, output_crop, | ||||
|                            layout, dst); | ||||
|     } else { | ||||
|         window_adapt->Draw(scheduler, image_index, source_image_view, render_extent, crop_rect, | ||||
|                            layout, dst); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | ||||
|                              const Tegra::FramebufferConfig& framebuffer, | ||||
|                              const Layout::FramebufferLayout& layout, size_t swapchain_images, | ||||
|                              std::span<const Tegra::FramebufferConfig> framebuffers, | ||||
|                              const Layout::FramebufferLayout& layout, | ||||
|                              size_t current_swapchain_image_count, | ||||
|                              VkFormat current_swapchain_view_format) { | ||||
|     bool resource_update_required = false; | ||||
|     bool presentation_recreate_required = false; | ||||
| @@ -272,11 +63,10 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | ||||
|         resource_update_required = true; | ||||
|     } | ||||
|  | ||||
|     // Recreate dynamic resources if the the image count or input format changed | ||||
|     const VkFormat old_framebuffer_format = | ||||
|         std::exchange(framebuffer_view_format, GetFormat(framebuffer)); | ||||
|     if (swapchain_images != image_count || old_framebuffer_format != framebuffer_view_format) { | ||||
|         image_count = swapchain_images; | ||||
|     // Recreate dynamic resources if the image count changed | ||||
|     const size_t old_swapchain_image_count = | ||||
|         std::exchange(image_count, current_swapchain_image_count); | ||||
|     if (old_swapchain_image_count != current_swapchain_image_count) { | ||||
|         resource_update_required = true; | ||||
|     } | ||||
|  | ||||
| @@ -294,11 +84,8 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | ||||
|         // Wait for idle to ensure no resources are in use | ||||
|         WaitIdle(); | ||||
|  | ||||
|         // Set new number of resource ticks | ||||
|         resource_ticks.resize(swapchain_images); | ||||
|  | ||||
|         // Update window adapt pass | ||||
|         SetWindowAdaptPass(layout); | ||||
|         SetWindowAdaptPass(); | ||||
|  | ||||
|         // Update frame format if needed | ||||
|         if (presentation_recreate_required) { | ||||
| @@ -307,7 +94,21 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Draw(rasterizer, framebuffer, layout, frame); | ||||
|     // Add additional layers if needed | ||||
|     const VkExtent2D window_size{ | ||||
|         .width = layout.screen.GetWidth(), | ||||
|         .height = layout.screen.GetHeight(), | ||||
|     }; | ||||
|  | ||||
|     while (layers.size() < framebuffers.size()) { | ||||
|         layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, | ||||
|                             window_size, window_adapt->GetDescriptorSetLayout()); | ||||
|     } | ||||
|  | ||||
|     // Perform the draw | ||||
|     window_adapt->Draw(rasterizer, scheduler, image_index, layers, framebuffers, layout, frame); | ||||
|  | ||||
|     // Advance to next image | ||||
|     if (++image_index >= image_count) { | ||||
|         image_index = 0; | ||||
|     } | ||||
| @@ -321,7 +122,7 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& l | ||||
|     if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() || | ||||
|         format_updated) { | ||||
|         WaitIdle(); | ||||
|         SetWindowAdaptPass(layout); | ||||
|         SetWindowAdaptPass(); | ||||
|     } | ||||
|     const VkExtent2D extent{ | ||||
|         .width = layout.width, | ||||
| @@ -345,115 +146,4 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkE | ||||
|     }); | ||||
| } | ||||
|  | ||||
| void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     if (framebuffer.width == raw_width && framebuffer.height == raw_height && | ||||
|         framebuffer.pixel_format == pixel_format && !raw_images.empty()) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     raw_width = framebuffer.width; | ||||
|     raw_height = framebuffer.height; | ||||
|     pixel_format = framebuffer.pixel_format; | ||||
|     anti_alias.reset(); | ||||
|  | ||||
|     ReleaseRawImages(); | ||||
|     CreateStagingBuffer(framebuffer); | ||||
|     CreateRawImages(framebuffer); | ||||
| } | ||||
|  | ||||
| void BlitScreen::ReleaseRawImages() { | ||||
|     for (const u64 tick : resource_ticks) { | ||||
|         scheduler.Wait(tick); | ||||
|     } | ||||
|     raw_images.clear(); | ||||
|     buffer.reset(); | ||||
| } | ||||
|  | ||||
| void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     const VkBufferCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .size = CalculateBufferSize(framebuffer), | ||||
|         .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | | ||||
|                  VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, | ||||
|         .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||||
|         .queueFamilyIndexCount = 0, | ||||
|         .pQueueFamilyIndices = nullptr, | ||||
|     }; | ||||
|  | ||||
|     buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload); | ||||
| } | ||||
|  | ||||
| void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { | ||||
|     raw_images.resize(image_count); | ||||
|     raw_image_views.resize(image_count); | ||||
|  | ||||
|     const auto create_image = [&](bool used_on_framebuffer = false, u32 up_scale = 1, | ||||
|                                   u32 down_shift = 0) { | ||||
|         u32 extra_usages = used_on_framebuffer ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | ||||
|                                                : VK_IMAGE_USAGE_TRANSFER_DST_BIT; | ||||
|         return memory_allocator.CreateImage(VkImageCreateInfo{ | ||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | ||||
|             .pNext = nullptr, | ||||
|             .flags = 0, | ||||
|             .imageType = VK_IMAGE_TYPE_2D, | ||||
|             .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format, | ||||
|             .extent = | ||||
|                 { | ||||
|                     .width = (up_scale * framebuffer.width) >> down_shift, | ||||
|                     .height = (up_scale * framebuffer.height) >> down_shift, | ||||
|                     .depth = 1, | ||||
|                 }, | ||||
|             .mipLevels = 1, | ||||
|             .arrayLayers = 1, | ||||
|             .samples = VK_SAMPLE_COUNT_1_BIT, | ||||
|             .tiling = used_on_framebuffer ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR, | ||||
|             .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | extra_usages, | ||||
|             .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||||
|             .queueFamilyIndexCount = 0, | ||||
|             .pQueueFamilyIndices = nullptr, | ||||
|             .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||||
|         }); | ||||
|     }; | ||||
|     const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) { | ||||
|         return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | ||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | ||||
|             .pNext = nullptr, | ||||
|             .flags = 0, | ||||
|             .image = *image, | ||||
|             .viewType = VK_IMAGE_VIEW_TYPE_2D, | ||||
|             .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format, | ||||
|             .components = | ||||
|                 { | ||||
|                     .r = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|                     .g = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|                     .b = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|                     .a = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|                 }, | ||||
|             .subresourceRange = | ||||
|                 { | ||||
|                     .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||
|                     .baseMipLevel = 0, | ||||
|                     .levelCount = 1, | ||||
|                     .baseArrayLayer = 0, | ||||
|                     .layerCount = 1, | ||||
|                 }, | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     for (size_t i = 0; i < image_count; ++i) { | ||||
|         raw_images[i] = create_image(); | ||||
|         raw_image_views[i] = create_image_view(raw_images[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const { | ||||
|     return GetSizeInBytes(framebuffer) * image_count; | ||||
| } | ||||
|  | ||||
| u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const { | ||||
|     return GetSizeInBytes(framebuffer) * image_index; | ||||
| } | ||||
|  | ||||
| } // namespace Vulkan | ||||
|   | ||||
| @@ -3,10 +3,12 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <list> | ||||
| #include <memory> | ||||
|  | ||||
| #include "core/frontend/framebuffer_layout.h" | ||||
| #include "video_core/host1x/gpu_device_memory_manager.h" | ||||
| #include "video_core/renderer_vulkan/present/layer.h" | ||||
| #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||||
| #include "video_core/vulkan_common/vulkan_wrapper.h" | ||||
|  | ||||
| @@ -14,32 +16,17 @@ namespace Core { | ||||
| class System; | ||||
| } | ||||
|  | ||||
| namespace Core::Frontend { | ||||
| class EmuWindow; | ||||
| } | ||||
|  | ||||
| namespace Tegra { | ||||
| struct FramebufferConfig; | ||||
| } | ||||
|  | ||||
| namespace VideoCore { | ||||
| class RasterizerInterface; | ||||
| } | ||||
|  | ||||
| namespace Service::android { | ||||
| enum class PixelFormat : u32; | ||||
| } | ||||
|  | ||||
| namespace Settings { | ||||
| enum class AntiAliasing : u32; | ||||
| enum class ScalingFilter : u32; | ||||
| } // namespace Settings | ||||
|  | ||||
| namespace Vulkan { | ||||
|  | ||||
| class AntiAliasPass; | ||||
| class Device; | ||||
| class FSR; | ||||
| class RasterizerVulkan; | ||||
| class Scheduler; | ||||
| class PresentManager; | ||||
| @@ -64,8 +51,8 @@ public: | ||||
|     ~BlitScreen(); | ||||
|  | ||||
|     void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | ||||
|                      const Tegra::FramebufferConfig& framebuffer, | ||||
|                      const Layout::FramebufferLayout& layout, size_t swapchain_images, | ||||
|                      std::span<const Tegra::FramebufferConfig> framebuffers, | ||||
|                      const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count, | ||||
|                      VkFormat current_swapchain_view_format); | ||||
|  | ||||
|     [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, | ||||
| @@ -74,50 +61,22 @@ public: | ||||
|  | ||||
| private: | ||||
|     void WaitIdle(); | ||||
|     void SetWindowAdaptPass(const Layout::FramebufferLayout& layout); | ||||
|     void SetAntiAliasPass(); | ||||
|  | ||||
|     void Draw(RasterizerVulkan& rasterizer, const Tegra::FramebufferConfig& framebuffer, | ||||
|               const Layout::FramebufferLayout& layout, Frame* dst); | ||||
|  | ||||
|     void SetWindowAdaptPass(); | ||||
|     vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, | ||||
|                                       VkRenderPass render_pass); | ||||
|  | ||||
|     void RefreshResources(const Tegra::FramebufferConfig& framebuffer); | ||||
|     void ReleaseRawImages(); | ||||
|     void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); | ||||
|     void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); | ||||
|  | ||||
|     u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; | ||||
|     u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const; | ||||
|  | ||||
|     Tegra::MaxwellDeviceMemoryManager& device_memory; | ||||
|     const Device& device; | ||||
|     MemoryAllocator& memory_allocator; | ||||
|     PresentManager& present_manager; | ||||
|     Scheduler& scheduler; | ||||
|     std::size_t image_count; | ||||
|     std::size_t image_count{}; | ||||
|     std::size_t image_index{}; | ||||
|     VkFormat swapchain_view_format{}; | ||||
|  | ||||
|     vk::Buffer buffer; | ||||
|  | ||||
|     std::vector<u64> resource_ticks; | ||||
|  | ||||
|     std::vector<vk::Image> raw_images; | ||||
|     std::vector<vk::ImageView> raw_image_views; | ||||
|     u32 raw_width = 0; | ||||
|     u32 raw_height = 0; | ||||
|  | ||||
|     Service::android::PixelFormat pixel_format{}; | ||||
|     VkFormat framebuffer_view_format; | ||||
|     VkFormat swapchain_view_format; | ||||
|  | ||||
|     Settings::AntiAliasing anti_aliasing{}; | ||||
|     Settings::ScalingFilter scaling_filter{}; | ||||
|  | ||||
|     std::unique_ptr<FSR> fsr; | ||||
|     std::unique_ptr<AntiAliasPass> anti_alias; | ||||
|     std::unique_ptr<WindowAdaptPass> window_adapt; | ||||
|     std::unique_ptr<WindowAdaptPass> window_adapt{}; | ||||
|     std::list<Layer> layers{}; | ||||
| }; | ||||
|  | ||||
| } // namespace Vulkan | ||||
|   | ||||
		Reference in New Issue
	
	Block a user