summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/video_core/renderer_base.cpp2
-rw-r--r--src/video_core/renderer_base.h4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp2
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp152
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp143
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h13
-rw-r--r--src/yuzu/bootmanager.cpp4
8 files changed, 247 insertions, 75 deletions
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 3ea72fda9..a99c33c37 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -27,7 +27,7 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
27 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height); 27 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
28} 28}
29 29
30void RendererBase::RequestScreenshot(void* data, std::function<void()> callback, 30void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
31 const Layout::FramebufferLayout& layout) { 31 const Layout::FramebufferLayout& layout) {
32 if (renderer_settings.screenshot_requested) { 32 if (renderer_settings.screenshot_requested) {
33 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); 33 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 22b80c328..bb204454e 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -24,7 +24,7 @@ struct RendererSettings {
24 // Screenshot 24 // Screenshot
25 std::atomic<bool> screenshot_requested{false}; 25 std::atomic<bool> screenshot_requested{false};
26 void* screenshot_bits{}; 26 void* screenshot_bits{};
27 std::function<void()> screenshot_complete_callback; 27 std::function<void(bool)> screenshot_complete_callback;
28 Layout::FramebufferLayout screenshot_framebuffer_layout; 28 Layout::FramebufferLayout screenshot_framebuffer_layout;
29}; 29};
30 30
@@ -80,7 +80,7 @@ public:
80 void RefreshBaseSettings(); 80 void RefreshBaseSettings();
81 81
82 /// Request a screenshot of the next frame 82 /// Request a screenshot of the next frame
83 void RequestScreenshot(void* data, std::function<void()> callback, 83 void RequestScreenshot(void* data, std::function<void(bool)> callback,
84 const Layout::FramebufferLayout& layout); 84 const Layout::FramebufferLayout& layout);
85 85
86protected: 86protected:
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f1b00c24c..7d7cba69c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -486,7 +486,7 @@ void RendererOpenGL::RenderScreenshot() {
486 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); 486 glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
487 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); 487 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
488 488
489 renderer_settings.screenshot_complete_callback(); 489 renderer_settings.screenshot_complete_callback(true);
490 renderer_settings.screenshot_requested = false; 490 renderer_settings.screenshot_requested = false;
491} 491}
492 492
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index a8d04dc61..7c9b0d6db 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -138,6 +138,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
138 const bool use_accelerated = 138 const bool use_accelerated =
139 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 139 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
140 const bool is_srgb = use_accelerated && screen_info.is_srgb; 140 const bool is_srgb = use_accelerated && screen_info.is_srgb;
141 RenderScreenshot(*framebuffer, use_accelerated);
141 142
142 bool has_been_recreated = false; 143 bool has_been_recreated = false;
143 const auto recreate_swapchain = [&] { 144 const auto recreate_swapchain = [&] {
@@ -162,7 +163,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
162 if (has_been_recreated) { 163 if (has_been_recreated) {
163 blit_screen.Recreate(); 164 blit_screen.Recreate();
164 } 165 }
165 const VkSemaphore render_semaphore = blit_screen.Draw(*framebuffer, use_accelerated); 166 const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated);
166 scheduler.Flush(render_semaphore); 167 scheduler.Flush(render_semaphore);
167 scheduler.WaitWorker(); 168 scheduler.WaitWorker();
168 swapchain.Present(render_semaphore); 169 swapchain.Present(render_semaphore);
@@ -193,4 +194,153 @@ void RendererVulkan::Report() const {
193 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); 194 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
194} 195}
195 196
197void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer,
198 bool use_accelerated) {
199 if (!renderer_settings.screenshot_requested) {
200 return;
201 }
202 const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
203 vk::Image staging_image = device.GetLogical().CreateImage(VkImageCreateInfo{
204 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
205 .pNext = nullptr,
206 .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
207 .imageType = VK_IMAGE_TYPE_2D,
208 .format = VK_FORMAT_B8G8R8A8_UNORM,
209 .extent =
210 {
211 .width = layout.width,
212 .height = layout.height,
213 .depth = 1,
214 },
215 .mipLevels = 1,
216 .arrayLayers = 1,
217 .samples = VK_SAMPLE_COUNT_1_BIT,
218 .tiling = VK_IMAGE_TILING_OPTIMAL,
219 .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
220 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
221 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
222 .queueFamilyIndexCount = 0,
223 .pQueueFamilyIndices = nullptr,
224 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
225 });
226 const auto image_commit = memory_allocator.Commit(staging_image, MemoryUsage::DeviceLocal);
227
228 const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
229 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
230 .pNext = nullptr,
231 .flags = 0,
232 .image = *staging_image,
233 .viewType = VK_IMAGE_VIEW_TYPE_2D,
234 .format = screen_info.is_srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM,
235 .components{
236 .r = VK_COMPONENT_SWIZZLE_IDENTITY,
237 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
238 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
239 .a = VK_COMPONENT_SWIZZLE_IDENTITY,
240 },
241 .subresourceRange{
242 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
243 .baseMipLevel = 0,
244 .levelCount = 1,
245 .baseArrayLayer = 0,
246 .layerCount = VK_REMAINING_ARRAY_LAYERS,
247 },
248 });
249 const VkExtent2D render_area{.width = layout.width, .height = layout.height};
250 const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area);
251 // Since we're not rendering to the screen, ignore the render semaphore.
252 void(blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated));
253
254 const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4);
255 const VkBufferCreateInfo dst_buffer_info{
256 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
257 .pNext = nullptr,
258 .flags = 0,
259 .size = buffer_size,
260 .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
261 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
262 .queueFamilyIndexCount = 0,
263 .pQueueFamilyIndices = nullptr,
264 };
265 const vk::Buffer dst_buffer = device.GetLogical().CreateBuffer(dst_buffer_info);
266 MemoryCommit dst_buffer_memory = memory_allocator.Commit(dst_buffer, MemoryUsage::Download);
267
268 scheduler.RequestOutsideRenderPassOperationContext();
269 scheduler.Record([&](vk::CommandBuffer cmdbuf) {
270 const VkImageMemoryBarrier read_barrier{
271 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
272 .pNext = nullptr,
273 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
274 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
275 .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
276 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
277 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
278 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
279 .image = *staging_image,
280 .subresourceRange{
281 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
282 .baseMipLevel = 0,
283 .levelCount = VK_REMAINING_MIP_LEVELS,
284 .baseArrayLayer = 0,
285 .layerCount = VK_REMAINING_ARRAY_LAYERS,
286 },
287 };
288 const VkImageMemoryBarrier image_write_barrier{
289 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
290 .pNext = nullptr,
291 .srcAccessMask = 0,
292 .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
293 .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
294 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
295 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
296 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
297 .image = *staging_image,
298 .subresourceRange{
299 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
300 .baseMipLevel = 0,
301 .levelCount = VK_REMAINING_MIP_LEVELS,
302 .baseArrayLayer = 0,
303 .layerCount = VK_REMAINING_ARRAY_LAYERS,
304 },
305 };
306 static constexpr VkMemoryBarrier memory_write_barrier{
307 .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
308 .pNext = nullptr,
309 .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
310 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
311 };
312 const VkBufferImageCopy copy{
313 .bufferOffset = 0,
314 .bufferRowLength = 0,
315 .bufferImageHeight = 0,
316 .imageSubresource{
317 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
318 .mipLevel = 0,
319 .baseArrayLayer = 0,
320 .layerCount = 1,
321 },
322 .imageOffset{.x = 0, .y = 0, .z = 0},
323 .imageExtent{
324 .width = layout.width,
325 .height = layout.height,
326 .depth = 1,
327 },
328 };
329 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
330 0, read_barrier);
331 cmdbuf.CopyImageToBuffer(*staging_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer,
332 copy);
333 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
334 0, memory_write_barrier, nullptr, image_write_barrier);
335 });
336 // Ensure the copy is fully completed before saving the screenshot
337 scheduler.Finish();
338
339 // Copy backing image data to the QImage screenshot buffer
340 const auto dst_memory_map = dst_buffer_memory.Map();
341 std::memcpy(renderer_settings.screenshot_bits, dst_memory_map.data(), dst_memory_map.size());
342 renderer_settings.screenshot_complete_callback(false);
343 renderer_settings.screenshot_requested = false;
344}
345
196} // namespace Vulkan 346} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index d7d17e110..6dc985109 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -54,6 +54,8 @@ public:
54private: 54private:
55 void Report() const; 55 void Report() const;
56 56
57 void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated);
58
57 Core::TelemetrySession& telemetry_session; 59 Core::TelemetrySession& telemetry_session;
58 Core::Memory::Memory& cpu_memory; 60 Core::Memory::Memory& cpu_memory;
59 Tegra::GPU& gpu; 61 Tegra::GPU& gpu;
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 516f428e7..5c43b8acf 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -130,7 +130,10 @@ void VKBlitScreen::Recreate() {
130 CreateDynamicResources(); 130 CreateDynamicResources();
131} 131}
132 132
133VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated) { 133VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
134 const VkFramebuffer& host_framebuffer,
135 const Layout::FramebufferLayout layout, VkExtent2D render_area,
136 bool use_accelerated) {
134 RefreshResources(framebuffer); 137 RefreshResources(framebuffer);
135 138
136 // Finish any pending renderpass 139 // Finish any pending renderpass
@@ -145,8 +148,8 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
145 use_accelerated ? screen_info.image_view : *raw_image_views[image_index]); 148 use_accelerated ? screen_info.image_view : *raw_image_views[image_index]);
146 149
147 BufferData data; 150 BufferData data;
148 SetUniformData(data, framebuffer); 151 SetUniformData(data, layout);
149 SetVertexData(data, framebuffer); 152 SetVertexData(data, framebuffer, layout);
150 153
151 const std::span<u8> mapped_span = buffer_commit.Map(); 154 const std::span<u8> mapped_span = buffer_commit.Map();
152 std::memcpy(mapped_span.data(), &data, sizeof(data)); 155 std::memcpy(mapped_span.data(), &data, sizeof(data));
@@ -220,52 +223,75 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool
220 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier); 223 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier);
221 }); 224 });
222 } 225 }
223 scheduler.Record([this, image_index, size = swapchain.GetSize()](vk::CommandBuffer cmdbuf) { 226 scheduler.Record(
224 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; 227 [this, host_framebuffer, image_index, size = render_area](vk::CommandBuffer cmdbuf) {
225 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; 228 const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f;
226 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; 229 const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f;
227 const VkClearValue clear_color{ 230 const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f;
228 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, 231 const VkClearValue clear_color{
229 }; 232 .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}},
230 const VkRenderPassBeginInfo renderpass_bi{ 233 };
231 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 234 const VkRenderPassBeginInfo renderpass_bi{
232 .pNext = nullptr, 235 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
233 .renderPass = *renderpass, 236 .pNext = nullptr,
234 .framebuffer = *framebuffers[image_index], 237 .renderPass = *renderpass,
235 .renderArea = 238 .framebuffer = host_framebuffer,
236 { 239 .renderArea =
237 .offset = {0, 0}, 240 {
238 .extent = size, 241 .offset = {0, 0},
239 }, 242 .extent = size,
240 .clearValueCount = 1, 243 },
241 .pClearValues = &clear_color, 244 .clearValueCount = 1,
242 }; 245 .pClearValues = &clear_color,
243 const VkViewport viewport{ 246 };
244 .x = 0.0f, 247 const VkViewport viewport{
245 .y = 0.0f, 248 .x = 0.0f,
246 .width = static_cast<float>(size.width), 249 .y = 0.0f,
247 .height = static_cast<float>(size.height), 250 .width = static_cast<float>(size.width),
248 .minDepth = 0.0f, 251 .height = static_cast<float>(size.height),
249 .maxDepth = 1.0f, 252 .minDepth = 0.0f,
250 }; 253 .maxDepth = 1.0f,
251 const VkRect2D scissor{ 254 };
252 .offset = {0, 0}, 255 const VkRect2D scissor{
253 .extent = size, 256 .offset = {0, 0},
254 }; 257 .extent = size,
255 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); 258 };
256 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 259 cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
257 cmdbuf.SetViewport(0, viewport); 260 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
258 cmdbuf.SetScissor(0, scissor); 261 cmdbuf.SetViewport(0, viewport);
259 262 cmdbuf.SetScissor(0, scissor);
260 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); 263
261 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, 264 cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices));
262 descriptor_sets[image_index], {}); 265 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0,
263 cmdbuf.Draw(4, 1, 0, 0); 266 descriptor_sets[image_index], {});
264 cmdbuf.EndRenderPass(); 267 cmdbuf.Draw(4, 1, 0, 0);
265 }); 268 cmdbuf.EndRenderPass();
269 });
266 return *semaphores[image_index]; 270 return *semaphores[image_index];
267} 271}
268 272
273VkSemaphore VKBlitScreen::DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer,
274 bool use_accelerated) {
275 const std::size_t image_index = swapchain.GetImageIndex();
276 const VkExtent2D render_area = swapchain.GetSize();
277 const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout();
278 return Draw(framebuffer, *framebuffers[image_index], layout, render_area, use_accelerated);
279}
280
281vk::Framebuffer VKBlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) {
282 return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
283 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
284 .pNext = nullptr,
285 .flags = 0,
286 .renderPass = *renderpass,
287 .attachmentCount = 1,
288 .pAttachments = &image_view,
289 .width = extent.width,
290 .height = extent.height,
291 .layers = 1,
292 });
293}
294
269void VKBlitScreen::CreateStaticResources() { 295void VKBlitScreen::CreateStaticResources() {
270 CreateShaders(); 296 CreateShaders();
271 CreateSemaphores(); 297 CreateSemaphores();
@@ -609,22 +635,9 @@ void VKBlitScreen::CreateFramebuffers() {
609 const VkExtent2D size{swapchain.GetSize()}; 635 const VkExtent2D size{swapchain.GetSize()};
610 framebuffers.resize(image_count); 636 framebuffers.resize(image_count);
611 637
612 VkFramebufferCreateInfo ci{
613 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
614 .pNext = nullptr,
615 .flags = 0,
616 .renderPass = *renderpass,
617 .attachmentCount = 1,
618 .pAttachments = nullptr,
619 .width = size.width,
620 .height = size.height,
621 .layers = 1,
622 };
623
624 for (std::size_t i = 0; i < image_count; ++i) { 638 for (std::size_t i = 0; i < image_count; ++i) {
625 const VkImageView image_view{swapchain.GetImageViewIndex(i)}; 639 const VkImageView image_view{swapchain.GetImageViewIndex(i)};
626 ci.pAttachments = &image_view; 640 framebuffers[i] = CreateFramebuffer(image_view, size);
627 framebuffers[i] = device.GetLogical().CreateFramebuffer(ci);
628 } 641 }
629} 642}
630 643
@@ -752,15 +765,13 @@ void VKBlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView imag
752 device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {}); 765 device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {});
753} 766}
754 767
755void VKBlitScreen::SetUniformData(BufferData& data, 768void VKBlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const {
756 const Tegra::FramebufferConfig& framebuffer) const {
757 const auto& layout = render_window.GetFramebufferLayout();
758 data.uniform.modelview_matrix = 769 data.uniform.modelview_matrix =
759 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height)); 770 MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height));
760} 771}
761 772
762void VKBlitScreen::SetVertexData(BufferData& data, 773void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
763 const Tegra::FramebufferConfig& framebuffer) const { 774 const Layout::FramebufferLayout layout) const {
764 const auto& framebuffer_transform_flags = framebuffer.transform_flags; 775 const auto& framebuffer_transform_flags = framebuffer.transform_flags;
765 const auto& framebuffer_crop_rect = framebuffer.crop_rect; 776 const auto& framebuffer_crop_rect = framebuffer.crop_rect;
766 777
@@ -798,7 +809,7 @@ void VKBlitScreen::SetVertexData(BufferData& data,
798 static_cast<f32>(screen_info.height); 809 static_cast<f32>(screen_info.height);
799 } 810 }
800 811
801 const auto& screen = render_window.GetFramebufferLayout().screen; 812 const auto& screen = layout.screen;
802 const auto x = static_cast<f32>(screen.left); 813 const auto x = static_cast<f32>(screen.left);
803 const auto y = static_cast<f32>(screen.top); 814 const auto y = static_cast<f32>(screen.top);
804 const auto w = static_cast<f32>(screen.GetWidth()); 815 const auto w = static_cast<f32>(screen.GetWidth());
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 5e3177685..430bcfbca 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -56,8 +56,16 @@ public:
56 void Recreate(); 56 void Recreate();
57 57
58 [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer, 58 [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer,
59 const VkFramebuffer& host_framebuffer,
60 const Layout::FramebufferLayout layout, VkExtent2D render_area,
59 bool use_accelerated); 61 bool use_accelerated);
60 62
63 [[nodiscard]] VkSemaphore DrawToSwapchain(const Tegra::FramebufferConfig& framebuffer,
64 bool use_accelerated);
65
66 [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view,
67 VkExtent2D extent);
68
61private: 69private:
62 struct BufferData; 70 struct BufferData;
63 71
@@ -81,8 +89,9 @@ private:
81 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); 89 void CreateRawImages(const Tegra::FramebufferConfig& framebuffer);
82 90
83 void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const; 91 void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const;
84 void SetUniformData(BufferData& data, const Tegra::FramebufferConfig& framebuffer) const; 92 void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const;
85 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer) const; 93 void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer,
94 const Layout::FramebufferLayout layout) const;
86 95
87 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; 96 u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const;
88 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, 97 u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer,
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 25b658b2a..2e0ade815 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -632,9 +632,9 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p
632 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); 632 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
633 renderer.RequestScreenshot( 633 renderer.RequestScreenshot(
634 screenshot_image.bits(), 634 screenshot_image.bits(),
635 [=, this] { 635 [=, this](bool invert_y) {
636 const std::string std_screenshot_path = screenshot_path.toStdString(); 636 const std::string std_screenshot_path = screenshot_path.toStdString();
637 if (screenshot_image.mirrored(false, true).save(screenshot_path)) { 637 if (screenshot_image.mirrored(false, invert_y).save(screenshot_path)) {
638 LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path); 638 LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
639 } else { 639 } else {
640 LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path); 640 LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);