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/fsr.h | ||||||
|     renderer_vulkan/present/fxaa.cpp |     renderer_vulkan/present/fxaa.cpp | ||||||
|     renderer_vulkan/present/fxaa.h |     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.cpp | ||||||
|     renderer_vulkan/present/smaa.h |     renderer_vulkan/present/smaa.h | ||||||
|     renderer_vulkan/present/util.cpp |     renderer_vulkan/present/util.cpp | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
|  | #include "common/common_funcs.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/scratch_buffer.h" | #include "common/scratch_buffer.h" | ||||||
| #include "video_core/engines/engine_interface.h" | #include "video_core/engines/engine_interface.h" | ||||||
|   | |||||||
| @@ -26,21 +26,11 @@ | |||||||
|  |  | ||||||
| #endif | #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) in vec2 tex_coord; | ||||||
|  |  | ||||||
| layout (location = 0) out vec4 frag_color; | 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; | const bool ignore_alpha = true; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,22 +3,12 @@ | |||||||
|  |  | ||||||
| #version 460 core | #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) in vec2 frag_tex_coord; | ||||||
|  |  | ||||||
| layout (location = 0) out vec4 color; | 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 cubic(float v) { | ||||||
|     vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; |     vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; | ||||||
|   | |||||||
| @@ -7,21 +7,11 @@ | |||||||
|  |  | ||||||
| #version 460 core | #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) in vec2 frag_tex_coord; | ||||||
|  |  | ||||||
| layout(location = 0) out vec4 color; | 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 offset[3] = float[](0.0, 1.3846153846, 3.2307692308); | ||||||
| const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703); | 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 (location = 0) out vec4 color; | ||||||
|  |  | ||||||
| layout (binding = 1) uniform sampler2D color_texture; | layout (binding = 0) uniform sampler2D color_texture; | ||||||
|  |  | ||||||
| void main() { | void main() { | ||||||
|     color = texture(color_texture, frag_tex_coord); |     color = texture(color_texture, frag_tex_coord); | ||||||
|   | |||||||
| @@ -3,16 +3,37 @@ | |||||||
|  |  | ||||||
| #version 460 core | #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 (location = 0) out vec2 frag_tex_coord; | ||||||
|  |  | ||||||
| layout (set = 0, binding = 0) uniform MatrixBlock { | struct ScreenRectVertex { | ||||||
|     mat4 modelview_matrix; |     vec2 position; | ||||||
|  |     vec2 tex_coord; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void main() { | layout (push_constant) uniform PushConstants { | ||||||
|     gl_Position = modelview_matrix * vec4(vert_position, 0.0, 1.0); |     mat4 modelview_matrix; | ||||||
|     frag_tex_coord = vert_tex_coord; |     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 | #extension GL_GOOGLE_include_directive : enable | ||||||
|  |  | ||||||
|  | #define VERSION 1 | ||||||
| #define YUZU_USE_FP16 | #define YUZU_USE_FP16 | ||||||
|  |  | ||||||
| #include "opengl_present_scaleforce.frag" | #include "opengl_present_scaleforce.frag" | ||||||
|   | |||||||
| @@ -5,4 +5,6 @@ | |||||||
|  |  | ||||||
| #extension GL_GOOGLE_include_directive : enable | #extension GL_GOOGLE_include_directive : enable | ||||||
|  |  | ||||||
|  | #define VERSION 1 | ||||||
|  |  | ||||||
| #include "opengl_present_scaleforce.frag" | #include "opengl_present_scaleforce.frag" | ||||||
|   | |||||||
| @@ -27,43 +27,29 @@ vk::ShaderModule SelectScaleForceShader(const Device& device) { | |||||||
|  |  | ||||||
| } // Anonymous namespace | } // Anonymous namespace | ||||||
|  |  | ||||||
| std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, | std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format) { | ||||||
|                                                      const MemoryAllocator& memory_allocator, |     return std::make_unique<WindowAdaptPass>(device, frame_format, | ||||||
|                                                      size_t image_count, VkFormat frame_format) { |  | ||||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, |  | ||||||
|                                              CreateNearestNeighborSampler(device), |                                              CreateNearestNeighborSampler(device), | ||||||
|                                              BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); |                                              BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, | std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format) { | ||||||
|                                               const MemoryAllocator& memory_allocator, |     return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||||||
|                                               size_t image_count, VkFormat frame_format) { |  | ||||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, |  | ||||||
|                                              CreateBilinearSampler(device), |  | ||||||
|                                              BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); |                                              BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, | std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format) { | ||||||
|                                              const MemoryAllocator& memory_allocator, |     return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||||||
|                                              size_t image_count, VkFormat frame_format) { |  | ||||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, |  | ||||||
|                                              CreateBilinearSampler(device), |  | ||||||
|                                              BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); |                                              BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, | std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format) { | ||||||
|                                               const MemoryAllocator& memory_allocator, |     return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||||||
|                                               size_t image_count, VkFormat frame_format) { |  | ||||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, |  | ||||||
|                                              CreateBilinearSampler(device), |  | ||||||
|                                              BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); |                                              BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, | std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format) { | ||||||
|                                                 const MemoryAllocator& memory_allocator, |     return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||||||
|                                                 size_t image_count, VkFormat frame_format) { |  | ||||||
|     return std::make_unique<WindowAdaptPass>(device, memory_allocator, image_count, frame_format, |  | ||||||
|                                              CreateBilinearSampler(device), |  | ||||||
|                                              SelectScaleForceShader(device)); |                                              SelectScaleForceShader(device)); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,24 +7,12 @@ | |||||||
|  |  | ||||||
| namespace Vulkan { | namespace Vulkan { | ||||||
|  |  | ||||||
| std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, | class MemoryAllocator; | ||||||
|                                                      const MemoryAllocator& memory_allocator, |  | ||||||
|                                                      size_t image_count, VkFormat frame_format); |  | ||||||
|  |  | ||||||
| std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, | std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format); | ||||||
|                                               const MemoryAllocator& memory_allocator, | std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format); | ||||||
|                                               size_t image_count, 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> MakeBicubic(const Device& device, | std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format); | ||||||
|                                              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); |  | ||||||
|  |  | ||||||
| } // namespace Vulkan | } // 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{ |     const VkAttachmentDescription attachment{ | ||||||
|         .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, |         .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, | ||||||
|         .format = format, |         .format = format, | ||||||
|         .samples = VK_SAMPLE_COUNT_1_BIT, |         .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, |         .storeOp = VK_ATTACHMENT_STORE_OP_STORE, | ||||||
|         .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, |         .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, | ||||||
|         .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, |         .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, | ||||||
|         .initialLayout = VK_IMAGE_LAYOUT_GENERAL, |         .initialLayout = initial_layout, | ||||||
|         .finalLayout = VK_IMAGE_LAYOUT_GENERAL, |         .finalLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| @@ -244,8 +246,7 @@ vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout( | |||||||
|             .binding = static_cast<u32>(i), |             .binding = static_cast<u32>(i), | ||||||
|             .descriptorType = std::data(types)[i], |             .descriptorType = std::data(types)[i], | ||||||
|             .descriptorCount = 1, |             .descriptorCount = 1, | ||||||
|             .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | |             .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, | ||||||
|                           VK_SHADER_STAGE_COMPUTE_BIT, |  | ||||||
|             .pImmutableSamplers = nullptr, |             .pImmutableSamplers = nullptr, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| @@ -285,7 +286,8 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, | |||||||
|  |  | ||||||
| vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | ||||||
|                                    vk::PipelineLayout& layout, |                                    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{{ |     const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ | ||||||
|         { |         { | ||||||
|             .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |             .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, |         .alphaToOneEnable = VK_FALSE, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     constexpr VkPipelineColorBlendAttachmentState color_blend_attachment{ |     constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ | ||||||
|         .blendEnable = VK_FALSE, |         .blendEnable = VK_FALSE, | ||||||
|         .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, |         .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||||||
|         .dstColorBlendFactor = 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, |                           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{ |     const VkPipelineColorBlendStateCreateInfo color_blend_ci{ | ||||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, |         .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | ||||||
|         .pNext = nullptr, |         .pNext = nullptr, | ||||||
| @@ -382,7 +396,8 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | |||||||
|         .logicOpEnable = VK_FALSE, |         .logicOpEnable = VK_FALSE, | ||||||
|         .logicOp = VK_LOGIC_OP_COPY, |         .logicOp = VK_LOGIC_OP_COPY, | ||||||
|         .attachmentCount = 1, |         .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}, |         .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); | void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); | ||||||
|  |  | ||||||
| vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); | 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::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass, | ||||||
|                                          vk::ImageView& dest_image, VkExtent2D extent); |                                          vk::ImageView& dest_image, VkExtent2D extent); | ||||||
| vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter = VK_FILTER_LINEAR); | 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::DescriptorSetLayout& layout); | ||||||
| vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | ||||||
|                                    vk::PipelineLayout& layout, |                                    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, | VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, | ||||||
|                                               VkSampler sampler, VkImageView view, |                                               VkSampler sampler, VkImageView view, | ||||||
|                                               VkDescriptorSet set, u32 binding); |                                               VkDescriptorSet set, u32 binding); | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||||||
| // SPDX-License-Identifier: GPL-2.0-or-later | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
| #include <cstring> |  | ||||||
|  |  | ||||||
| #include "core/frontend/framebuffer_layout.h" | #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/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/util.h" | ||||||
| #include "video_core/renderer_vulkan/present/window_adapt_pass.h" | #include "video_core/renderer_vulkan/present/window_adapt_pass.h" | ||||||
| #include "video_core/renderer_vulkan/vk_present_manager.h" | #include "video_core/renderer_vulkan/vk_present_manager.h" | ||||||
| @@ -14,501 +15,123 @@ | |||||||
|  |  | ||||||
| namespace Vulkan { | namespace Vulkan { | ||||||
|  |  | ||||||
| namespace { | WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format, | ||||||
|  |                                  vk::Sampler&& sampler_, vk::ShaderModule&& fragment_shader_) | ||||||
| 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_) |  | ||||||
|     : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) { |     : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) { | ||||||
|     CreateDescriptorPool(num_images); |  | ||||||
|     CreateDescriptorSetLayout(); |     CreateDescriptorSetLayout(); | ||||||
|     CreateDescriptorSets(num_images); |  | ||||||
|     CreatePipelineLayout(); |     CreatePipelineLayout(); | ||||||
|     CreateVertexShader(); |     CreateVertexShader(); | ||||||
|     CreateRenderPass(frame_format); |     CreateRenderPass(frame_format); | ||||||
|     CreatePipeline(); |     CreatePipeline(); | ||||||
|     CreateBuffer(memory_allocator); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| WindowAdaptPass::~WindowAdaptPass() = default; | WindowAdaptPass::~WindowAdaptPass() = default; | ||||||
|  |  | ||||||
| void WindowAdaptPass::Draw(Scheduler& scheduler, size_t image_index, VkImageView src_image_view, | void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, | ||||||
|                            VkExtent2D src_image_extent, const Common::Rectangle<f32>& crop_rect, |                            std::list<Layer>& layers, | ||||||
|  |                            std::span<const Tegra::FramebufferConfig> configs, | ||||||
|                            const Layout::FramebufferLayout& layout, Frame* dst) { |                            const Layout::FramebufferLayout& layout, Frame* dst) { | ||||||
|     ConfigureLayout(image_index, src_image_view, layout, crop_rect); |  | ||||||
|  |  | ||||||
|     const VkFramebuffer host_framebuffer{*dst->framebuffer}; |     const VkFramebuffer host_framebuffer{*dst->framebuffer}; | ||||||
|     const VkRenderPass renderpass{*render_pass}; |     const VkRenderPass renderpass{*render_pass}; | ||||||
|     const VkPipeline graphics_pipeline{*pipeline}; |     const VkPipeline graphics_pipeline{*pipeline}; | ||||||
|     const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; |     const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; | ||||||
|     const VkDescriptorSet descriptor_set{descriptor_sets[image_index]}; |  | ||||||
|     const VkBuffer vertex_buffer{*buffer}; |  | ||||||
|     const VkExtent2D render_area{ |     const VkExtent2D render_area{ | ||||||
|         .width = dst->width, |         .width = dst->width, | ||||||
|         .height = dst->height, |         .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) { |     scheduler.Record([=](vk::CommandBuffer cmdbuf) { | ||||||
|         const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; |         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_green = Settings::values.bg_green.GetValue() / 255.0f; | ||||||
|         const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; |         const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; | ||||||
|         const VkClearValue clear_color{ |         const VkClearAttachment clear_attachment{ | ||||||
|  |             .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||||
|  |             .colorAttachment = 0, | ||||||
|  |             .clearValue = | ||||||
|  |                 { | ||||||
|                     .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, |                     .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, | ||||||
|  |                 }, | ||||||
|         }; |         }; | ||||||
|         const VkRenderPassBeginInfo renderpass_bi{ |         const VkClearRect clear_rect{ | ||||||
|             .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, |             .rect = | ||||||
|             .pNext = nullptr, |  | ||||||
|             .renderPass = renderpass, |  | ||||||
|             .framebuffer = host_framebuffer, |  | ||||||
|             .renderArea = |  | ||||||
|                 { |                 { | ||||||
|                     .offset = {0, 0}, |                     .offset = {0, 0}, | ||||||
|                     .extent = render_area, |                     .extent = render_area, | ||||||
|                 }, |                 }, | ||||||
|             .clearValueCount = 1, |             .baseArrayLayer = 0, | ||||||
|             .pClearValues = &clear_color, |             .layerCount = 1, | ||||||
|         }; |         }; | ||||||
|         const VkViewport viewport{ |  | ||||||
|             .x = 0.0f, |         BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); | ||||||
|             .y = 0.0f, |         cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); | ||||||
|             .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); |  | ||||||
|         cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); |         cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); | ||||||
|         cmdbuf.SetViewport(0, viewport); |         for (size_t i = 0; i < layer_count; i++) { | ||||||
|         cmdbuf.SetScissor(0, scissor); |             cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, | ||||||
|         cmdbuf.BindVertexBuffer(0, vertex_buffer, offsetof(BufferData, vertices)); |                                  push_constants[i]); | ||||||
|             cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, |             cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, | ||||||
|                                   descriptor_set, {}); |                                       descriptor_sets[i], {}); | ||||||
|             cmdbuf.Draw(4, 1, 0, 0); |             cmdbuf.Draw(4, 1, 0, 0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         cmdbuf.EndRenderPass(); |         cmdbuf.EndRenderPass(); | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() { | ||||||
|  |     return *descriptor_set_layout; | ||||||
|  | } | ||||||
|  |  | ||||||
| VkRenderPass WindowAdaptPass::GetRenderPass() { | VkRenderPass WindowAdaptPass::GetRenderPass() { | ||||||
|     return *render_pass; |     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() { | void WindowAdaptPass::CreateDescriptorSetLayout() { | ||||||
|     const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{ |     descriptor_set_layout = | ||||||
|         { |         CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); | ||||||
|             .binding = 0, | } | ||||||
|             .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |  | ||||||
|             .descriptorCount = 1, | void WindowAdaptPass::CreatePipelineLayout() { | ||||||
|  |     const VkPushConstantRange range{ | ||||||
|         .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, |         .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, | ||||||
|             .pImmutableSamplers = nullptr, |         .offset = 0, | ||||||
|         }, |         .size = sizeof(PresentPushConstants), | ||||||
|         { |     }; | ||||||
|             .binding = 1, |  | ||||||
|             .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, |  | ||||||
|             .descriptorCount = 1, |  | ||||||
|             .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, |  | ||||||
|             .pImmutableSamplers = nullptr, |  | ||||||
|         }, |  | ||||||
|     }}; |  | ||||||
|  |  | ||||||
|     const VkDescriptorSetLayoutCreateInfo ci{ |     pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{ | ||||||
|         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, |         .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||||||
|         .pNext = nullptr, |         .pNext = nullptr, | ||||||
|         .flags = 0, |         .flags = 0, | ||||||
|         .bindingCount = static_cast<u32>(layout_bindings.size()), |         .setLayoutCount = 1, | ||||||
|         .pBindings = layout_bindings.data(), |         .pSetLayouts = descriptor_set_layout.address(), | ||||||
|     }; |         .pushConstantRangeCount = 1, | ||||||
|  |         .pPushConstantRanges = &range, | ||||||
|     descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci); |     }); | ||||||
| } |  | ||||||
|  |  | ||||||
| void WindowAdaptPass::CreateDescriptorSets(size_t num_images) { |  | ||||||
|     const std::vector layouts(num_images, *descriptor_set_layout); |  | ||||||
|     descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void WindowAdaptPass::CreateBuffer(const MemoryAllocator& memory_allocator) { |  | ||||||
|     const VkBufferCreateInfo ci{ |  | ||||||
|         .sType = VK_STRUCTURE_TYPE_BUFFER_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); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void WindowAdaptPass::CreateVertexShader() { | void WindowAdaptPass::CreateVertexShader() { | ||||||
|     vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); |     vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); | ||||||
| } | } | ||||||
|  |  | ||||||
| void WindowAdaptPass::CreatePipelineLayout() { | void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { | ||||||
|     const VkPipelineLayoutCreateInfo ci{ |     render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); | ||||||
|         .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::CreatePipeline() { | void WindowAdaptPass::CreatePipeline() { | ||||||
|     const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ |     pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, | ||||||
|         { |                                      std::tie(vertex_shader, fragment_shader), false); | ||||||
|             .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); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace Vulkan | } // namespace Vulkan | ||||||
|   | |||||||
| @@ -3,6 +3,8 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <list> | ||||||
|  |  | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| #include "video_core/vulkan_common/vulkan_wrapper.h" | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||||||
|  |  | ||||||
| @@ -18,54 +20,39 @@ namespace Vulkan { | |||||||
|  |  | ||||||
| class Device; | class Device; | ||||||
| struct Frame; | struct Frame; | ||||||
| class MemoryAllocator; | class Layer; | ||||||
| class Scheduler; | class Scheduler; | ||||||
|  | class RasterizerVulkan; | ||||||
|  |  | ||||||
| class WindowAdaptPass final { | class WindowAdaptPass final { | ||||||
| public: | public: | ||||||
|     explicit WindowAdaptPass(const Device& device, const MemoryAllocator& memory_allocator, |     explicit WindowAdaptPass(const Device& device, VkFormat frame_format, vk::Sampler&& sampler, | ||||||
|                              size_t num_images, VkFormat frame_format, vk::Sampler&& sampler, |  | ||||||
|                              vk::ShaderModule&& fragment_shader); |                              vk::ShaderModule&& fragment_shader); | ||||||
|     ~WindowAdaptPass(); |     ~WindowAdaptPass(); | ||||||
|  |  | ||||||
|     void Draw(Scheduler& scheduler, size_t image_index, VkImageView src_image_view, |     void Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, | ||||||
|               VkExtent2D src_image_extent, const Common::Rectangle<f32>& crop_rect, |               std::list<Layer>& layers, std::span<const Tegra::FramebufferConfig> configs, | ||||||
|               const Layout::FramebufferLayout& layout, Frame* dst); |               const Layout::FramebufferLayout& layout, Frame* dst); | ||||||
|  |  | ||||||
|  |     VkDescriptorSetLayout GetDescriptorSetLayout(); | ||||||
|     VkRenderPass GetRenderPass(); |     VkRenderPass GetRenderPass(); | ||||||
|  |  | ||||||
| private: | 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 CreateDescriptorSetLayout(); | ||||||
|     void CreateDescriptorSets(size_t num_images); |  | ||||||
|     void CreatePipelineLayout(); |     void CreatePipelineLayout(); | ||||||
|     void CreateVertexShader(); |     void CreateVertexShader(); | ||||||
|     void CreateRenderPass(VkFormat frame_format); |     void CreateRenderPass(VkFormat frame_format); | ||||||
|     void CreatePipeline(); |     void CreatePipeline(); | ||||||
|     void CreateBuffer(const MemoryAllocator& memory_allocator); |  | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     const Device& device; |     const Device& device; | ||||||
|     vk::DescriptorPool descriptor_pool; |  | ||||||
|     vk::DescriptorSetLayout descriptor_set_layout; |     vk::DescriptorSetLayout descriptor_set_layout; | ||||||
|     vk::DescriptorSets descriptor_sets; |  | ||||||
|     vk::PipelineLayout pipeline_layout; |     vk::PipelineLayout pipeline_layout; | ||||||
|     vk::Sampler sampler; |     vk::Sampler sampler; | ||||||
|     vk::ShaderModule vertex_shader; |     vk::ShaderModule vertex_shader; | ||||||
|     vk::ShaderModule fragment_shader; |     vk::ShaderModule fragment_shader; | ||||||
|     vk::RenderPass render_pass; |     vk::RenderPass render_pass; | ||||||
|     vk::Pipeline pipeline; |     vk::Pipeline pipeline; | ||||||
|     vk::Buffer buffer; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| } // namespace Vulkan | } // namespace Vulkan | ||||||
|   | |||||||
| @@ -125,9 +125,9 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     RenderScreenshot(*framebuffer); |     RenderScreenshot(framebuffer); | ||||||
|     Frame* frame = present_manager.GetRenderFrame(); |     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(), |                                render_window.GetFramebufferLayout(), swapchain.GetImageCount(), | ||||||
|                                swapchain.GetImageViewFormat()); |                                swapchain.GetImageViewFormat()); | ||||||
|     scheduler.Flush(*frame->render_ready); |     scheduler.Flush(*frame->render_ready); | ||||||
| @@ -163,7 +163,7 @@ void RendererVulkan::Report() const { | |||||||
|     telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); |     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) { |     if (!renderer_settings.screenshot_requested) { | ||||||
|         return; |         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); |                                 VK_FORMAT_B8G8R8A8_UNORM); | ||||||
|  |  | ||||||
|     const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); |     const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ public: | |||||||
| private: | private: | ||||||
|     void Report() const; |     void Report() const; | ||||||
|  |  | ||||||
|     void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer); |     void RenderScreenshot(const Tegra::FramebufferConfig* framebuffer); | ||||||
|  |  | ||||||
|     Core::TelemetrySession& telemetry_session; |     Core::TelemetrySession& telemetry_session; | ||||||
|     Tegra::MaxwellDeviceMemoryManager& device_memory; |     Tegra::MaxwellDeviceMemoryManager& device_memory; | ||||||
|   | |||||||
| @@ -1,65 +1,15 @@ | |||||||
| // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||||||
| // SPDX-License-Identifier: GPL-2.0-or-later | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
| #include <algorithm> | #include "video_core/framebuffer_config.h" | ||||||
| #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/renderer_vulkan/present/filters.h" | #include "video_core/renderer_vulkan/present/filters.h" | ||||||
| #include "video_core/renderer_vulkan/present/fsr.h" | #include "video_core/renderer_vulkan/present/layer.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/vk_blit_screen.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_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 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_, | BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, | ||||||
|                        MemoryAllocator& memory_allocator_, PresentManager& present_manager_, |                        MemoryAllocator& memory_allocator_, PresentManager& present_manager_, | ||||||
|                        Scheduler& scheduler_) |                        Scheduler& scheduler_) | ||||||
| @@ -75,194 +25,35 @@ void BlitScreen::WaitIdle() { | |||||||
|     device.GetLogical().WaitIdle(); |     device.GetLogical().WaitIdle(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void BlitScreen::SetWindowAdaptPass(const Layout::FramebufferLayout& layout) { | void BlitScreen::SetWindowAdaptPass() { | ||||||
|  |     layers.clear(); | ||||||
|     scaling_filter = Settings::values.scaling_filter.GetValue(); |     scaling_filter = Settings::values.scaling_filter.GetValue(); | ||||||
|  |  | ||||||
|     const VkExtent2D adapt_size{ |  | ||||||
|         .width = layout.screen.GetWidth(), |  | ||||||
|         .height = layout.screen.GetHeight(), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     fsr.reset(); |  | ||||||
|  |  | ||||||
|     switch (scaling_filter) { |     switch (scaling_filter) { | ||||||
|     case Settings::ScalingFilter::NearestNeighbor: |     case Settings::ScalingFilter::NearestNeighbor: | ||||||
|         window_adapt = |         window_adapt = MakeNearestNeighbor(device, swapchain_view_format); | ||||||
|             MakeNearestNeighbor(device, memory_allocator, image_count, swapchain_view_format); |  | ||||||
|         break; |         break; | ||||||
|     case Settings::ScalingFilter::Bicubic: |     case Settings::ScalingFilter::Bicubic: | ||||||
|         window_adapt = MakeBicubic(device, memory_allocator, image_count, swapchain_view_format); |         window_adapt = MakeBicubic(device, swapchain_view_format); | ||||||
|         break; |         break; | ||||||
|     case Settings::ScalingFilter::Gaussian: |     case Settings::ScalingFilter::Gaussian: | ||||||
|         window_adapt = MakeGaussian(device, memory_allocator, image_count, swapchain_view_format); |         window_adapt = MakeGaussian(device, swapchain_view_format); | ||||||
|         break; |         break; | ||||||
|     case Settings::ScalingFilter::ScaleForce: |     case Settings::ScalingFilter::ScaleForce: | ||||||
|         window_adapt = MakeScaleForce(device, memory_allocator, image_count, swapchain_view_format); |         window_adapt = MakeScaleForce(device, swapchain_view_format); | ||||||
|         break; |         break; | ||||||
|     case Settings::ScalingFilter::Fsr: |     case Settings::ScalingFilter::Fsr: | ||||||
|         fsr = std::make_unique<FSR>(device, memory_allocator, image_count, adapt_size); |  | ||||||
|         [[fallthrough]]; |  | ||||||
|     case Settings::ScalingFilter::Bilinear: |     case Settings::ScalingFilter::Bilinear: | ||||||
|     default: |     default: | ||||||
|         window_adapt = MakeBilinear(device, memory_allocator, image_count, swapchain_view_format); |         window_adapt = MakeBilinear(device, swapchain_view_format); | ||||||
|         break; |         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, | void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | ||||||
|                              const Tegra::FramebufferConfig& framebuffer, |                              std::span<const Tegra::FramebufferConfig> framebuffers, | ||||||
|                              const Layout::FramebufferLayout& layout, size_t swapchain_images, |                              const Layout::FramebufferLayout& layout, | ||||||
|  |                              size_t current_swapchain_image_count, | ||||||
|                              VkFormat current_swapchain_view_format) { |                              VkFormat current_swapchain_view_format) { | ||||||
|     bool resource_update_required = false; |     bool resource_update_required = false; | ||||||
|     bool presentation_recreate_required = false; |     bool presentation_recreate_required = false; | ||||||
| @@ -272,11 +63,10 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | |||||||
|         resource_update_required = true; |         resource_update_required = true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Recreate dynamic resources if the the image count or input format changed |     // Recreate dynamic resources if the image count changed | ||||||
|     const VkFormat old_framebuffer_format = |     const size_t old_swapchain_image_count = | ||||||
|         std::exchange(framebuffer_view_format, GetFormat(framebuffer)); |         std::exchange(image_count, current_swapchain_image_count); | ||||||
|     if (swapchain_images != image_count || old_framebuffer_format != framebuffer_view_format) { |     if (old_swapchain_image_count != current_swapchain_image_count) { | ||||||
|         image_count = swapchain_images; |  | ||||||
|         resource_update_required = true; |         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 |         // Wait for idle to ensure no resources are in use | ||||||
|         WaitIdle(); |         WaitIdle(); | ||||||
|  |  | ||||||
|         // Set new number of resource ticks |  | ||||||
|         resource_ticks.resize(swapchain_images); |  | ||||||
|  |  | ||||||
|         // Update window adapt pass |         // Update window adapt pass | ||||||
|         SetWindowAdaptPass(layout); |         SetWindowAdaptPass(); | ||||||
|  |  | ||||||
|         // Update frame format if needed |         // Update frame format if needed | ||||||
|         if (presentation_recreate_required) { |         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) { |     if (++image_index >= image_count) { | ||||||
|         image_index = 0; |         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() || |     if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() || | ||||||
|         format_updated) { |         format_updated) { | ||||||
|         WaitIdle(); |         WaitIdle(); | ||||||
|         SetWindowAdaptPass(layout); |         SetWindowAdaptPass(); | ||||||
|     } |     } | ||||||
|     const VkExtent2D extent{ |     const VkExtent2D extent{ | ||||||
|         .width = layout.width, |         .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 | } // namespace Vulkan | ||||||
|   | |||||||
| @@ -3,10 +3,12 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <list> | ||||||
| #include <memory> | #include <memory> | ||||||
|  |  | ||||||
| #include "core/frontend/framebuffer_layout.h" | #include "core/frontend/framebuffer_layout.h" | ||||||
| #include "video_core/host1x/gpu_device_memory_manager.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_memory_allocator.h" | ||||||
| #include "video_core/vulkan_common/vulkan_wrapper.h" | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||||||
|  |  | ||||||
| @@ -14,32 +16,17 @@ namespace Core { | |||||||
| class System; | class System; | ||||||
| } | } | ||||||
|  |  | ||||||
| namespace Core::Frontend { |  | ||||||
| class EmuWindow; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| namespace Tegra { | namespace Tegra { | ||||||
| struct FramebufferConfig; | struct FramebufferConfig; | ||||||
| } | } | ||||||
|  |  | ||||||
| namespace VideoCore { |  | ||||||
| class RasterizerInterface; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| namespace Service::android { |  | ||||||
| enum class PixelFormat : u32; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| namespace Settings { | namespace Settings { | ||||||
| enum class AntiAliasing : u32; |  | ||||||
| enum class ScalingFilter : u32; | enum class ScalingFilter : u32; | ||||||
| } // namespace Settings | } // namespace Settings | ||||||
|  |  | ||||||
| namespace Vulkan { | namespace Vulkan { | ||||||
|  |  | ||||||
| class AntiAliasPass; |  | ||||||
| class Device; | class Device; | ||||||
| class FSR; |  | ||||||
| class RasterizerVulkan; | class RasterizerVulkan; | ||||||
| class Scheduler; | class Scheduler; | ||||||
| class PresentManager; | class PresentManager; | ||||||
| @@ -64,8 +51,8 @@ public: | |||||||
|     ~BlitScreen(); |     ~BlitScreen(); | ||||||
|  |  | ||||||
|     void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, |     void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, | ||||||
|                      const Tegra::FramebufferConfig& framebuffer, |                      std::span<const Tegra::FramebufferConfig> framebuffers, | ||||||
|                      const Layout::FramebufferLayout& layout, size_t swapchain_images, |                      const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count, | ||||||
|                      VkFormat current_swapchain_view_format); |                      VkFormat current_swapchain_view_format); | ||||||
|  |  | ||||||
|     [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, |     [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, | ||||||
| @@ -74,50 +61,22 @@ public: | |||||||
|  |  | ||||||
| private: | private: | ||||||
|     void WaitIdle(); |     void WaitIdle(); | ||||||
|     void SetWindowAdaptPass(const Layout::FramebufferLayout& layout); |     void SetWindowAdaptPass(); | ||||||
|     void SetAntiAliasPass(); |  | ||||||
|  |  | ||||||
|     void Draw(RasterizerVulkan& rasterizer, const Tegra::FramebufferConfig& framebuffer, |  | ||||||
|               const Layout::FramebufferLayout& layout, Frame* dst); |  | ||||||
|  |  | ||||||
|     vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, |     vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, | ||||||
|                                       VkRenderPass render_pass); |                                       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; |     Tegra::MaxwellDeviceMemoryManager& device_memory; | ||||||
|     const Device& device; |     const Device& device; | ||||||
|     MemoryAllocator& memory_allocator; |     MemoryAllocator& memory_allocator; | ||||||
|     PresentManager& present_manager; |     PresentManager& present_manager; | ||||||
|     Scheduler& scheduler; |     Scheduler& scheduler; | ||||||
|     std::size_t image_count; |     std::size_t image_count{}; | ||||||
|     std::size_t image_index{}; |     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{}; |     Settings::ScalingFilter scaling_filter{}; | ||||||
|  |     std::unique_ptr<WindowAdaptPass> window_adapt{}; | ||||||
|     std::unique_ptr<FSR> fsr; |     std::list<Layer> layers{}; | ||||||
|     std::unique_ptr<AntiAliasPass> anti_alias; |  | ||||||
|     std::unique_ptr<WindowAdaptPass> window_adapt; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| } // namespace Vulkan | } // namespace Vulkan | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user