mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-15 07:10:05 +00:00
renderer_vulkan: Fix screenshots under NVIDIA vulkan (#7082)
This commit is contained in:
parent
597297ffb4
commit
36146459f8
@ -669,9 +669,9 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p
|
|||||||
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||||
system.Renderer().RequestScreenshot(
|
system.Renderer().RequestScreenshot(
|
||||||
screenshot_image.bits(),
|
screenshot_image.bits(),
|
||||||
[this, screenshot_path] {
|
[this, screenshot_path](bool invert_y) {
|
||||||
const std::string std_screenshot_path = screenshot_path.toStdString();
|
const std::string std_screenshot_path = screenshot_path.toStdString();
|
||||||
if (screenshot_image.mirrored(false, true).save(screenshot_path)) {
|
if (screenshot_image.mirrored(false, invert_y).save(screenshot_path)) {
|
||||||
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
|
LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);
|
LOG_ERROR(Frontend, "Failed to save screenshot to \"{}\"", std_screenshot_path);
|
||||||
|
@ -59,7 +59,7 @@ bool RendererBase::IsScreenshotPending() const {
|
|||||||
return settings.screenshot_requested;
|
return settings.screenshot_requested;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererBase::RequestScreenshot(void* data, std::function<void()> callback,
|
void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
|
||||||
const Layout::FramebufferLayout& layout) {
|
const Layout::FramebufferLayout& layout) {
|
||||||
if (settings.screenshot_requested) {
|
if (settings.screenshot_requested) {
|
||||||
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
|
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
|
||||||
|
@ -28,7 +28,7 @@ struct RendererSettings {
|
|||||||
// Screenshot
|
// Screenshot
|
||||||
std::atomic_bool screenshot_requested{false};
|
std::atomic_bool screenshot_requested{false};
|
||||||
void* screenshot_bits{};
|
void* screenshot_bits{};
|
||||||
std::function<void()> screenshot_complete_callback;
|
std::function<void(bool)> screenshot_complete_callback;
|
||||||
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
||||||
// Renderer
|
// Renderer
|
||||||
std::atomic_bool bg_color_update_requested{false};
|
std::atomic_bool bg_color_update_requested{false};
|
||||||
@ -103,7 +103,7 @@ public:
|
|||||||
[[nodiscard]] bool IsScreenshotPending() const;
|
[[nodiscard]] bool IsScreenshotPending() const;
|
||||||
|
|
||||||
/// Request a screenshot of the next frame
|
/// Request a screenshot of the next frame
|
||||||
void RequestScreenshot(void* data, std::function<void()> callback,
|
void RequestScreenshot(void* data, std::function<void(bool)> callback,
|
||||||
const Layout::FramebufferLayout& layout);
|
const Layout::FramebufferLayout& layout);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -151,7 +151,7 @@ void RendererOpenGL::RenderScreenshot() {
|
|||||||
state.Apply();
|
state.Apply();
|
||||||
glDeleteRenderbuffers(1, &renderbuffer);
|
glDeleteRenderbuffers(1, &renderbuffer);
|
||||||
|
|
||||||
settings.screenshot_complete_callback();
|
settings.screenshot_complete_callback(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/microprofile.h"
|
#include "common/microprofile.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "common/texture.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "core/hw/gpu.h"
|
#include "core/hw/gpu.h"
|
||||||
@ -18,6 +19,7 @@
|
|||||||
#include "video_core/host_shaders/vulkan_present_frag_spv.h"
|
#include "video_core/host_shaders/vulkan_present_frag_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_present_interlaced_frag_spv.h"
|
#include "video_core/host_shaders/vulkan_present_interlaced_frag_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_present_vert_spv.h"
|
#include "video_core/host_shaders/vulkan_present_vert_spv.h"
|
||||||
|
#include "vulkan/vulkan_format_traits.hpp"
|
||||||
|
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
|
|
||||||
@ -867,20 +869,9 @@ void RendererVulkan::RenderScreenshot() {
|
|||||||
const u32 width = layout.width;
|
const u32 width = layout.width;
|
||||||
const u32 height = layout.height;
|
const u32 height = layout.height;
|
||||||
|
|
||||||
const vk::ImageCreateInfo staging_image_info = {
|
const vk::BufferCreateInfo staging_buffer_info = {
|
||||||
.imageType = vk::ImageType::e2D,
|
.size = width * height * 4,
|
||||||
.format = vk::Format::eB8G8R8A8Unorm,
|
.usage = vk::BufferUsageFlagBits::eTransferDst,
|
||||||
.extent{
|
|
||||||
.width = width,
|
|
||||||
.height = height,
|
|
||||||
.depth = 1,
|
|
||||||
},
|
|
||||||
.mipLevels = 1,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.samples = vk::SampleCountFlagBits::e1,
|
|
||||||
.tiling = vk::ImageTiling::eLinear,
|
|
||||||
.usage = vk::ImageUsageFlagBits::eTransferDst,
|
|
||||||
.initialLayout = vk::ImageLayout::eUndefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const VmaAllocationCreateInfo alloc_create_info = {
|
const VmaAllocationCreateInfo alloc_create_info = {
|
||||||
@ -893,18 +884,18 @@ void RendererVulkan::RenderScreenshot() {
|
|||||||
.pUserData = nullptr,
|
.pUserData = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
VkImage unsafe_image{};
|
VkBuffer unsafe_buffer{};
|
||||||
VmaAllocation allocation{};
|
VmaAllocation allocation{};
|
||||||
VmaAllocationInfo alloc_info;
|
VmaAllocationInfo alloc_info;
|
||||||
VkImageCreateInfo unsafe_image_info = static_cast<VkImageCreateInfo>(staging_image_info);
|
VkBufferCreateInfo unsafe_buffer_info = static_cast<VkBufferCreateInfo>(staging_buffer_info);
|
||||||
|
|
||||||
VkResult result = vmaCreateImage(instance.GetAllocator(), &unsafe_image_info,
|
VkResult result = vmaCreateBuffer(instance.GetAllocator(), &unsafe_buffer_info,
|
||||||
&alloc_create_info, &unsafe_image, &allocation, &alloc_info);
|
&alloc_create_info, &unsafe_buffer, &allocation, &alloc_info);
|
||||||
if (result != VK_SUCCESS) [[unlikely]] {
|
if (result != VK_SUCCESS) [[unlikely]] {
|
||||||
LOG_CRITICAL(Render_Vulkan, "Failed allocating texture with error {}", result);
|
LOG_CRITICAL(Render_Vulkan, "Failed allocating texture with error {}", result);
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
vk::Image staging_image{unsafe_image};
|
vk::Buffer staging_buffer{unsafe_buffer};
|
||||||
|
|
||||||
Frame frame{};
|
Frame frame{};
|
||||||
main_window.RecreateFrame(&frame, width, height);
|
main_window.RecreateFrame(&frame, width, height);
|
||||||
@ -912,73 +903,37 @@ void RendererVulkan::RenderScreenshot() {
|
|||||||
DrawScreens(&frame, layout, false);
|
DrawScreens(&frame, layout, false);
|
||||||
|
|
||||||
scheduler.Record(
|
scheduler.Record(
|
||||||
[width, height, source_image = frame.image, staging_image](vk::CommandBuffer cmdbuf) {
|
[width, height, source_image = frame.image, staging_buffer](vk::CommandBuffer cmdbuf) {
|
||||||
const std::array read_barriers = {
|
const vk::ImageMemoryBarrier read_barrier = {
|
||||||
vk::ImageMemoryBarrier{
|
.srcAccessMask = vk::AccessFlagBits::eMemoryWrite,
|
||||||
.srcAccessMask = vk::AccessFlagBits::eMemoryWrite,
|
.dstAccessMask = vk::AccessFlagBits::eTransferRead,
|
||||||
.dstAccessMask = vk::AccessFlagBits::eTransferRead,
|
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||||
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
|
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||||
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.image = source_image,
|
||||||
.image = source_image,
|
.subresourceRange{
|
||||||
.subresourceRange{
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.baseMipLevel = 0,
|
||||||
.baseMipLevel = 0,
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
.baseArrayLayer = 0,
|
||||||
.baseArrayLayer = 0,
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
vk::ImageMemoryBarrier{
|
|
||||||
.srcAccessMask = vk::AccessFlagBits::eNone,
|
|
||||||
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
|
|
||||||
.oldLayout = vk::ImageLayout::eUndefined,
|
|
||||||
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = staging_image,
|
|
||||||
.subresourceRange{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const std::array write_barriers = {
|
const vk::ImageMemoryBarrier write_barrier = {
|
||||||
vk::ImageMemoryBarrier{
|
.srcAccessMask = vk::AccessFlagBits::eTransferRead,
|
||||||
.srcAccessMask = vk::AccessFlagBits::eTransferRead,
|
.dstAccessMask = vk::AccessFlagBits::eMemoryWrite,
|
||||||
.dstAccessMask = vk::AccessFlagBits::eMemoryWrite,
|
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||||
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
|
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||||
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.image = source_image,
|
||||||
.image = source_image,
|
.subresourceRange{
|
||||||
.subresourceRange{
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.baseMipLevel = 0,
|
||||||
.baseMipLevel = 0,
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
.baseArrayLayer = 0,
|
||||||
.baseArrayLayer = 0,
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
vk::ImageMemoryBarrier{
|
|
||||||
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
|
|
||||||
.dstAccessMask = vk::AccessFlagBits::eMemoryRead,
|
|
||||||
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
|
||||||
.newLayout = vk::ImageLayout::eGeneral,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = staging_image,
|
|
||||||
.subresourceRange{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
static constexpr vk::MemoryBarrier memory_write_barrier = {
|
static constexpr vk::MemoryBarrier memory_write_barrier = {
|
||||||
@ -986,41 +941,29 @@ void RendererVulkan::RenderScreenshot() {
|
|||||||
.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::array src_offsets = {
|
const vk::BufferImageCopy image_copy = {
|
||||||
vk::Offset3D{0, 0, 0},
|
.bufferOffset = 0,
|
||||||
vk::Offset3D{static_cast<s32>(width), static_cast<s32>(height), 1},
|
.bufferRowLength = 0,
|
||||||
};
|
.bufferImageHeight = 0,
|
||||||
|
.imageSubresource =
|
||||||
const std::array dst_offsets = {
|
{
|
||||||
vk::Offset3D{0, static_cast<s32>(height), 0},
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
vk::Offset3D{static_cast<s32>(width), 0, 1},
|
.mipLevel = 0,
|
||||||
};
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
const vk::ImageBlit blit_area = {
|
},
|
||||||
.srcSubresource{
|
.imageOffset = {0, 0, 0},
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.imageExtent = {width, height, 1},
|
||||||
.mipLevel = 0,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
.srcOffsets = src_offsets,
|
|
||||||
.dstSubresource{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.mipLevel = 0,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
.dstOffsets = dst_offsets,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
|
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
|
||||||
vk::PipelineStageFlagBits::eTransfer,
|
vk::PipelineStageFlagBits::eTransfer,
|
||||||
vk::DependencyFlagBits::eByRegion, {}, {}, read_barriers);
|
vk::DependencyFlagBits::eByRegion, {}, {}, read_barrier);
|
||||||
cmdbuf.blitImage(source_image, vk::ImageLayout::eTransferSrcOptimal, staging_image,
|
cmdbuf.copyImageToBuffer(source_image, vk::ImageLayout::eTransferSrcOptimal,
|
||||||
vk::ImageLayout::eTransferDstOptimal, blit_area, vk::Filter::eNearest);
|
staging_buffer, image_copy);
|
||||||
cmdbuf.pipelineBarrier(
|
cmdbuf.pipelineBarrier(
|
||||||
vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eAllCommands,
|
vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eAllCommands,
|
||||||
vk::DependencyFlagBits::eByRegion, memory_write_barrier, {}, write_barriers);
|
vk::DependencyFlagBits::eByRegion, memory_write_barrier, {}, write_barrier);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ensure the copy is fully completed before saving the screenshot
|
// Ensure the copy is fully completed before saving the screenshot
|
||||||
@ -1028,27 +971,16 @@ void RendererVulkan::RenderScreenshot() {
|
|||||||
|
|
||||||
const vk::Device device = instance.GetDevice();
|
const vk::Device device = instance.GetDevice();
|
||||||
|
|
||||||
// Get layout of the image (including row pitch)
|
|
||||||
const vk::ImageSubresource subresource = {
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.mipLevel = 0,
|
|
||||||
.arrayLayer = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::SubresourceLayout subresource_layout =
|
|
||||||
device.getImageSubresourceLayout(staging_image, subresource);
|
|
||||||
|
|
||||||
// Copy backing image data to the QImage screenshot buffer
|
// Copy backing image data to the QImage screenshot buffer
|
||||||
const u8* data = reinterpret_cast<const u8*>(alloc_info.pMappedData);
|
std::memcpy(settings.screenshot_bits, alloc_info.pMappedData, staging_buffer_info.size);
|
||||||
std::memcpy(settings.screenshot_bits, data + subresource_layout.offset,
|
|
||||||
subresource_layout.size);
|
|
||||||
|
|
||||||
// Destroy allocated resources
|
// Destroy allocated resources
|
||||||
|
vmaDestroyBuffer(instance.GetAllocator(), unsafe_buffer, allocation);
|
||||||
vmaDestroyImage(instance.GetAllocator(), frame.image, frame.allocation);
|
vmaDestroyImage(instance.GetAllocator(), frame.image, frame.allocation);
|
||||||
device.destroyFramebuffer(frame.framebuffer);
|
device.destroyFramebuffer(frame.framebuffer);
|
||||||
device.destroyImageView(frame.image_view);
|
device.destroyImageView(frame.image_view);
|
||||||
|
|
||||||
settings.screenshot_complete_callback();
|
settings.screenshot_complete_callback(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
@ -150,8 +150,7 @@ void Swapchain::FindPresentFormat() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unable to find required swapchain format!");
|
UNREACHABLE_MSG("Unable to find required swapchain format!");
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::SetPresentMode() {
|
void Swapchain::SetPresentMode() {
|
||||||
|
Loading…
Reference in New Issue
Block a user