diff options
| -rw-r--r-- | src/video_core/renderer_vulkan/renderer_vulkan.cpp | 150 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/renderer_vulkan.h | 2 |
2 files changed, 152 insertions, 0 deletions
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 7ced5006b..c92718a1f 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 = [&] { |
| @@ -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 | ||
| 197 | void 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(); | ||
| 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: | |||
| 54 | private: | 54 | private: |
| 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; |