forked from eden-emu/eden
Compare commits
6 commits
8cb82b8919
...
d3558b794d
Author | SHA1 | Date | |
---|---|---|---|
d3558b794d | |||
a5d883c910 | |||
86cfd7ed87 | |||
383fb23348 | |||
89d40c6302 | |||
234e41193e |
3 changed files with 149 additions and 76 deletions
|
@ -14,23 +14,55 @@
|
|||
namespace Vulkan {
|
||||
namespace {
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
using VideoCore::Surface::SurfaceType;
|
||||
|
||||
VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat format,
|
||||
VkSampleCountFlagBits samples) {
|
||||
using MaxwellToVK::SurfaceFormat;
|
||||
return {
|
||||
.flags = {},
|
||||
.format = SurfaceFormat(device, FormatType::Optimal, true, format).format,
|
||||
.samples = samples,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
};
|
||||
}
|
||||
} // Anonymous namespace
|
||||
constexpr SurfaceType GetSurfaceType(PixelFormat format) {
|
||||
switch (format) {
|
||||
// Depth formats
|
||||
case PixelFormat::D16_UNORM:
|
||||
case PixelFormat::D32_FLOAT:
|
||||
case PixelFormat::X8_D24_UNORM:
|
||||
return SurfaceType::Depth;
|
||||
|
||||
// Stencil formats
|
||||
case PixelFormat::S8_UINT:
|
||||
return SurfaceType::Stencil;
|
||||
|
||||
// Depth+Stencil formats
|
||||
case PixelFormat::D24_UNORM_S8_UINT:
|
||||
case PixelFormat::S8_UINT_D24_UNORM:
|
||||
case PixelFormat::D32_FLOAT_S8_UINT:
|
||||
return SurfaceType::DepthStencil;
|
||||
|
||||
// Everything else is a color texture
|
||||
default:
|
||||
return SurfaceType::ColorTexture;
|
||||
}
|
||||
}
|
||||
|
||||
VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat format,
|
||||
VkSampleCountFlagBits samples) {
|
||||
using MaxwellToVK::SurfaceFormat;
|
||||
|
||||
const SurfaceType surface_type = GetSurfaceType(format);
|
||||
const bool has_stencil = surface_type == SurfaceType::DepthStencil ||
|
||||
surface_type == SurfaceType::Stencil;
|
||||
|
||||
return {
|
||||
.flags = {},
|
||||
.format = SurfaceFormat(device, FormatType::Optimal, true, format).format,
|
||||
.samples = samples,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = has_stencil ? VK_ATTACHMENT_LOAD_OP_LOAD
|
||||
: VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = has_stencil ? VK_ATTACHMENT_STORE_OP_STORE
|
||||
: VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
};
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
|
||||
|
||||
|
@ -78,6 +110,18 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
|||
.preserveAttachmentCount = 0,
|
||||
.pPreserveAttachments = nullptr,
|
||||
};
|
||||
const VkSubpassDependency dependency{
|
||||
.srcSubpass = 0, // Current subpass
|
||||
.dstSubpass = 0, // Same subpass (self-dependency)
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
|
||||
};
|
||||
pair->second = device->GetLogical().CreateRenderPass({
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
|
@ -86,8 +130,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
|||
.pAttachments = descriptions.empty() ? nullptr : descriptions.data(),
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass,
|
||||
.dependencyCount = 0,
|
||||
.pDependencies = nullptr,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &dependency,
|
||||
});
|
||||
return *pair->second;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
|
@ -1538,7 +1539,7 @@ Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBas
|
|||
Image::~Image() = default;
|
||||
|
||||
void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
||||
std::span<const VideoCommon::BufferImageCopy> copies) {
|
||||
std::span<const VideoCommon::BufferImageCopy> copies) {
|
||||
// TODO: Move this to another API
|
||||
const bool is_rescaled = True(flags & ImageFlagBits::Rescaled);
|
||||
if (is_rescaled) {
|
||||
|
@ -1546,70 +1547,89 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
|||
}
|
||||
|
||||
// Handle MSAA upload if necessary
|
||||
/* WARNING, TODO: This code uses some hacks, besides being fundamentally ugly
|
||||
since tropic didn't want to touch it for a long time, so it needs a rewrite from someone better than me at vulkan.*/
|
||||
if (info.num_samples > 1 && runtime->CanUploadMSAA()) {
|
||||
// Only use MSAA copy pass for color formats
|
||||
// TODO: Depth/stencil formats need special handling
|
||||
if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) {
|
||||
// Create a temporary non-MSAA image to upload the data first
|
||||
ImageInfo temp_info = info;
|
||||
temp_info.num_samples = 1;
|
||||
/* WARNING, TODO: This code uses some hacks, besides being fundamentally ugly
|
||||
since tropic didn't want to touch it for a long time, so it needs a rewrite from someone
|
||||
better than me at vulkan. */
|
||||
// CHANGE: Gate the MSAA path more strictly and only use it for color, when the pass and device
|
||||
// support are available. Avoid running the MSAA path when prerequisites aren't met, preventing
|
||||
// validation and runtime issues.
|
||||
const bool wants_msaa_upload = info.num_samples > 1 &&
|
||||
aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT &&
|
||||
runtime->CanUploadMSAA() && runtime->msaa_copy_pass != nullptr &&
|
||||
runtime->device.IsStorageImageMultisampleSupported();
|
||||
|
||||
// Create image with same usage flags as the target image to avoid validation errors
|
||||
VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info);
|
||||
image_ci.usage = original_image.UsageFlags();
|
||||
vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci);
|
||||
if (wants_msaa_upload) {
|
||||
// Create a temporary non-MSAA image to upload the data first
|
||||
ImageInfo temp_info = info;
|
||||
temp_info.num_samples = 1;
|
||||
|
||||
// Upload to the temporary non-MSAA image
|
||||
scheduler->RequestOutsideRenderPassOperationContext();
|
||||
auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
|
||||
const VkBuffer src_buffer = buffer;
|
||||
const VkImage temp_vk_image = *temp_image;
|
||||
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
|
||||
scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) {
|
||||
CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, vk_copies);
|
||||
});
|
||||
// CHANGE: Build a fresh VkImageCreateInfo with robust usage flags for the temp image.
|
||||
// Using the target image's usage as-is could miss STORAGE/TRANSFER bits and trigger validation errors.
|
||||
VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info);
|
||||
image_ci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
|
||||
// Use MSAACopyPass to convert from non-MSAA to MSAA
|
||||
std::vector<VideoCommon::ImageCopy> image_copies;
|
||||
for (const auto& copy : copies) {
|
||||
VideoCommon::ImageCopy image_copy;
|
||||
image_copy.src_offset = {0, 0, 0}; // Use zero offset for source
|
||||
image_copy.dst_offset = copy.image_offset;
|
||||
image_copy.src_subresource = copy.image_subresource;
|
||||
image_copy.dst_subresource = copy.image_subresource;
|
||||
image_copy.extent = copy.image_extent;
|
||||
image_copies.push_back(image_copy);
|
||||
}
|
||||
// CHANGE: The previous stack-allocated wrapper was destroyed at function exit,
|
||||
// which could destroy VkImage before the GPU used it.
|
||||
auto temp_wrapper = std::make_shared<Image>(*runtime, temp_info, 0, 0);
|
||||
temp_wrapper->original_image = runtime->memory_allocator.CreateImage(image_ci);
|
||||
temp_wrapper->current_image = &Image::original_image;
|
||||
temp_wrapper->aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
temp_wrapper->initialized = true;
|
||||
|
||||
// wrapper image for the temporary image
|
||||
Image temp_wrapper(*runtime, temp_info, 0, 0);
|
||||
temp_wrapper.original_image = std::move(temp_image);
|
||||
temp_wrapper.current_image = &Image::original_image;
|
||||
temp_wrapper.aspect_mask = aspect_mask;
|
||||
temp_wrapper.initialized = true;
|
||||
|
||||
// Use MSAACopyPass to convert from non-MSAA to MSAA
|
||||
runtime->msaa_copy_pass->CopyImage(*this, temp_wrapper, image_copies, false);
|
||||
std::exchange(initialized, true);
|
||||
return;
|
||||
}
|
||||
// For depth/stencil formats, fall back to regular upload
|
||||
} else {
|
||||
// Regular non-MSAA upload
|
||||
// Upload to the temporary non-MSAA image
|
||||
scheduler->RequestOutsideRenderPassOperationContext();
|
||||
auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
|
||||
auto vk_copies = TransformBufferImageCopies(copies, offset, temp_wrapper->aspect_mask);
|
||||
const VkBuffer src_buffer = buffer;
|
||||
const VkImage vk_image = *original_image;
|
||||
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
|
||||
const bool is_initialized = std::exchange(initialized, true);
|
||||
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, is_initialized,
|
||||
vk_copies](vk::CommandBuffer cmdbuf) {
|
||||
CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, is_initialized, vk_copies);
|
||||
const VkImage temp_vk_image = *temp_wrapper->original_image;
|
||||
const VkImageAspectFlags vk_aspect_mask = temp_wrapper->aspect_mask;
|
||||
scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies,
|
||||
// CHANGE: capture shared_ptr to pin lifetime through submission
|
||||
keep = temp_wrapper](vk::CommandBuffer cmdbuf) {
|
||||
CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, vk_copies);
|
||||
});
|
||||
|
||||
// Use MSAACopyPass to convert from non-MSAA to MSAA
|
||||
// CHANGE: only lifetime and usage were fixed.
|
||||
std::vector<VideoCommon::ImageCopy> image_copies;
|
||||
image_copies.reserve(copies.size());
|
||||
for (const auto& copy : copies) {
|
||||
VideoCommon::ImageCopy image_copy;
|
||||
image_copy.src_offset = {0, 0, 0}; // Use zero offset for source
|
||||
image_copy.dst_offset = copy.image_offset;
|
||||
image_copy.src_subresource = copy.image_subresource;
|
||||
image_copy.dst_subresource = copy.image_subresource;
|
||||
image_copy.extent = copy.image_extent;
|
||||
image_copies.push_back(image_copy);
|
||||
}
|
||||
|
||||
runtime->msaa_copy_pass->CopyImage(*this, *temp_wrapper, image_copies,
|
||||
/*msaa_to_non_msaa=*/false);
|
||||
std::exchange(initialized, true);
|
||||
|
||||
// CHANGE: Add a no-op recording that captures temp_wrapper to ensure it stays alive
|
||||
// at least until commands are submitted/recorded.
|
||||
scheduler->Record([keep = std::move(temp_wrapper)](vk::CommandBuffer) {});
|
||||
|
||||
// CHANGE: Restore scaling before returning from the MSAA path.
|
||||
if (is_rescaled) {
|
||||
ScaleUp();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Regular non-MSAA upload (original behavior preserved)
|
||||
scheduler->RequestOutsideRenderPassOperationContext();
|
||||
auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask);
|
||||
const VkBuffer src_buffer = buffer;
|
||||
const VkImage vk_image = *original_image;
|
||||
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
|
||||
const bool is_initialized = std::exchange(initialized, true);
|
||||
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, is_initialized,
|
||||
vk_copies](vk::CommandBuffer cmdbuf) {
|
||||
CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, is_initialized, vk_copies);
|
||||
});
|
||||
|
||||
if (is_rescaled) {
|
||||
ScaleUp();
|
||||
}
|
||||
|
@ -1994,10 +2014,15 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
|||
}
|
||||
}
|
||||
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
|
||||
if (ImageUsageFlags(format_info, format) != image.UsageFlags()) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Image view format {} has different usage flags than image format {}", format,
|
||||
image.info.format);
|
||||
}
|
||||
const VkImageViewUsageCreateInfo image_view_usage{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.usage = image.UsageFlags(),
|
||||
.usage = ImageUsageFlags(format_info, format),
|
||||
};
|
||||
const VkImageViewCreateInfo create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
|
|
|
@ -444,7 +444,6 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
|
|||
ui->setupUi(this);
|
||||
statusBar()->hide();
|
||||
|
||||
// Check dark mode before a theme is loaded
|
||||
startup_icon_theme = QIcon::themeName();
|
||||
// fallback can only be set once, colorful theme icons are okay on both light/dark
|
||||
QIcon::setFallbackThemeName(QStringLiteral("colorful"));
|
||||
|
@ -5475,6 +5474,10 @@ void GMainWindow::UpdateUITheme() {
|
|||
current_theme = default_theme;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
QIcon::setThemeName(current_theme);
|
||||
AdjustLinkColor();
|
||||
#else
|
||||
if (current_theme == QStringLiteral("default") || current_theme == QStringLiteral("colorful")) {
|
||||
QIcon::setThemeName(current_theme == QStringLiteral("colorful") ? current_theme
|
||||
: startup_icon_theme);
|
||||
|
@ -5487,6 +5490,7 @@ void GMainWindow::UpdateUITheme() {
|
|||
QIcon::setThemeSearchPaths(QStringList(QStringLiteral(":/icons")));
|
||||
AdjustLinkColor();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (current_theme != default_theme) {
|
||||
QString theme_uri{QStringLiteral(":%1/style.qss").arg(current_theme)};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue