store this for later
Some checks failed
eden-license / license-header (pull_request) Failing after 25s

This commit is contained in:
Ribbit 2025-10-02 22:47:39 -07:00
parent f4f3425d86
commit 476692e3e1
7 changed files with 352 additions and 77 deletions

View file

@ -193,9 +193,15 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
!image_view.SupportsAnisotropy()};
const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
: sampler.Handle()};
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
VkSampler vk_sampler = use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
: sampler.Handle();
if (image_view.IsUiHudTexture()) {
const bool force_nearest = image_view.RequiresNearestSampling();
vk_sampler = sampler.HandleUi(force_nearest);
image_view.LogUiSamplerDecision(vk_sampler, force_nearest);
}
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler,
image_view.SampleLayout());
rescaling.PushTexture(texture_cache.IsRescaling(image_view));
}
}

View file

@ -135,9 +135,10 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) {
.buffer = *stream_buffer,
.offset = static_cast<VkDeviceSize>(offset),
.mapped_span = stream_pointer.subspan(offset, size),
.usage{},
.log2_level{},
.index{},
.usage = MemoryUsage::Stream,
.log2_level = 0,
.index = 0,
.owner = &stream_buffer,
};
}

View file

@ -23,6 +23,7 @@ struct StagingBufferRef {
MemoryUsage usage;
u32 log2_level;
u64 index;
const vk::Buffer* owner = nullptr;
};
class StagingBufferPool {
@ -65,6 +66,7 @@ private:
.usage = usage,
.log2_level = log2_level,
.index = index,
.owner = &buffer,
};
}
};

View file

@ -125,6 +125,72 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
return usage;
}
constexpr VkAccessFlags WRITE_ACCESS_FLAGS = VK_ACCESS_SHADER_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
constexpr VkAccessFlags READ_ACCESS_FLAGS = VK_ACCESS_SHADER_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
constexpr VkPipelineStageFlags PRE_UPLOAD_STAGE_MASK =
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
constexpr VkPipelineStageFlags GENERAL_POST_STAGE_MASK = PRE_UPLOAD_STAGE_MASK;
constexpr VkPipelineStageFlags UI_SHADER_STAGE_MASK =
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
constexpr u32 UI_HUD_MAX_DIMENSION = 128;
[[nodiscard]] bool IsHudFormat(PixelFormat format) {
switch (format) {
case PixelFormat::B5G6R5_UNORM:
case PixelFormat::R5G6B5_UNORM:
case PixelFormat::R8G8_UNORM:
return true;
default:
return false;
}
}
[[nodiscard]] char SwizzleChar(SwizzleSource value) {
switch (value) {
case SwizzleSource::R:
return 'R';
case SwizzleSource::G:
return 'G';
case SwizzleSource::B:
return 'B';
case SwizzleSource::A:
return 'A';
case SwizzleSource::Zero:
return '0';
case SwizzleSource::OneFloat:
case SwizzleSource::OneInt:
return '1';
default:
return '?';
}
}
void LogUiSwizzleEvent(GPUVAddr address, PixelFormat format,
const std::array<SwizzleSource, 4>& swizzle) {
LOG_DEBUG(Render_Vulkan, "UI hud swizzle addr=0x{:x} fmt={} -> {}{}{}{}", address,
static_cast<int>(format), SwizzleChar(swizzle[0]), SwizzleChar(swizzle[1]),
SwizzleChar(swizzle[2]), SwizzleChar(swizzle[3]));
}
void LogUiSamplerEvent(GPUVAddr address, PixelFormat format, bool force_nearest,
VkSampler sampler, VkImageLayout layout) {
LOG_DEBUG(Render_Vulkan, "UI hud sampler addr=0x{:x} fmt={} force_nearest={} sampler=0x{:x} "
"layout={}",
address, static_cast<int>(format), force_nearest,
reinterpret_cast<uintptr_t>(sampler), static_cast<int>(layout));
}
void LogUiBarrierEvent(GPUVAddr address, PixelFormat format, VkImageLayout final_layout) {
LOG_DEBUG(Render_Vulkan, "UI hud barrier addr=0x{:x} fmt={} final_layout={}", address,
static_cast<int>(format), static_cast<int>(final_layout));
}
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) {
const auto format_info =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format);
@ -532,60 +598,51 @@ struct RangedBarrierRange {
}
};
void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image,
VkImageAspectFlags aspect_mask, bool is_initialized,
std::span<const VkBufferImageCopy> copies) {
static constexpr VkAccessFlags WRITE_ACCESS_FLAGS =
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
static constexpr VkAccessFlags READ_ACCESS_FLAGS = VK_ACCESS_SHADER_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
// Compute exact mip/layer range being written to
VkImageAspectFlags aspect_mask, VkImageLayout current_layout,
VkImageLayout final_layout,
std::span<const VkBufferImageCopy> copies,
VkPipelineStageFlags dst_stage_mask, VkAccessFlags dst_access_mask) {
// Compute exact mip/layer range being written to
RangedBarrierRange range;
for (const auto& region : copies) {
range.AddLayers(region.imageSubresource);
}
const VkImageSubresourceRange subresource_range = range.SubresourceRange(aspect_mask);
const bool has_defined_layout = current_layout != VK_IMAGE_LAYOUT_UNDEFINED;
const VkImageMemoryBarrier read_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = WRITE_ACCESS_FLAGS,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange = subresource_range,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = has_defined_layout ? WRITE_ACCESS_FLAGS | READ_ACCESS_FLAGS : VK_ACCESS_NONE,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = has_defined_layout ? current_layout : VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange = subresource_range,
};
const VkImageMemoryBarrier write_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = WRITE_ACCESS_FLAGS | READ_ACCESS_FLAGS,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange = subresource_range,
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = dst_access_mask,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = final_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange = subresource_range,
};
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
read_barrier);
const VkPipelineStageFlags src_stage_mask = has_defined_layout ? PRE_UPLOAD_STAGE_MASK
: VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
cmdbuf.PipelineBarrier(src_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, read_barrier);
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
// TODO: Move this to another API
cmdbuf.PipelineBarrier(
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, nullptr, nullptr, write_barrier);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, 0, nullptr, nullptr,
write_barrier);
}
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
@ -861,6 +918,12 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
msaa_copy_pass = std::make_unique<MSAACopyPass>(
device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue);
}
supports_b5g6r5_linear_filter = device.IsFormatSupported(
VK_FORMAT_B5G6R5_UNORM_PACK16, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT,
FormatType::Optimal);
supports_r5g6b5_linear_filter = device.IsFormatSupported(
VK_FORMAT_R5G6B5_UNORM_PACK16, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT,
FormatType::Optimal);
if (!device.IsKhrImageFormatListSupported()) {
return;
}
@ -939,6 +1002,56 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
return *buffers[level];
}
bool TextureCacheRuntime::SupportsLinearFiltering(PixelFormat format) const {
if (!IsHudFormat(format)) {
return device.IsFormatSupported(
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, format).format,
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, FormatType::Optimal);
}
switch (format) {
case PixelFormat::B5G6R5_UNORM:
return supports_b5g6r5_linear_filter;
case PixelFormat::R5G6B5_UNORM:
return supports_r5g6b5_linear_filter;
default:
break;
}
return true;
}
bool TextureCacheRuntime::ShouldForceNearest(PixelFormat format) const {
switch (format) {
case PixelFormat::B5G6R5_UNORM:
return !supports_b5g6r5_linear_filter;
case PixelFormat::R5G6B5_UNORM:
return !supports_r5g6b5_linear_filter;
default:
return false;
}
}
bool TextureCacheRuntime::IsUiHudCandidate(const ImageInfo& info) const {
if (!IsHudFormat(info.format)) {
return false;
}
if (info.resources.levels != 1 || info.size.depth != 1) {
return false;
}
if (info.size.width > UI_HUD_MAX_DIMENSION || info.size.height > UI_HUD_MAX_DIMENSION) {
return false;
}
if (info.type != ImageType::e2D && info.type != ImageType::Linear) {
return false;
}
const auto format_info =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format);
const VkImageUsageFlags usage = ImageUsageFlags(format_info, info.format);
if ((usage & VK_IMAGE_USAGE_SAMPLED_BIT) == 0) {
return false;
}
return true;
}
void TextureCacheRuntime::BarrierFeedbackLoop() {
scheduler.RequestOutsideRenderPassOperationContext();
}
@ -1545,6 +1658,8 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
MakeStorageView(device, level, *original_image, VK_FORMAT_A8B8G8R8_UNORM_PACK32);
}
}
is_ui_hud_texture = runtime->IsUiHudCandidate(info);
known_layout = VK_IMAGE_LAYOUT_UNDEFINED;
}
Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
@ -1600,8 +1715,11 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies,
keep = temp_wrapper](vk::CommandBuffer cmdbuf) {
CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, vk_copies);
CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, vk_copies,
GENERAL_POST_STAGE_MASK, WRITE_ACCESS_FLAGS | READ_ACCESS_FLAGS);
});
temp_wrapper->SetKnownLayout(VK_IMAGE_LAYOUT_GENERAL);
// Use MSAACopyPass to convert from non-MSAA to MSAA
std::vector<VideoCommon::ImageCopy> image_copies;
@ -1636,12 +1754,26 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
const VkImage vk_image = *original_image;
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
const bool was_initialized = std::exchange(initialized, true);
const VkImageLayout current_layout = was_initialized ? KnownLayout() : VK_IMAGE_LAYOUT_UNDEFINED;
const bool use_shader_read_layout = is_ui_hud_texture;
const VkImageLayout final_layout = use_shader_read_layout ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
: VK_IMAGE_LAYOUT_GENERAL;
const VkPipelineStageFlags dst_stage_mask =
use_shader_read_layout ? UI_SHADER_STAGE_MASK : GENERAL_POST_STAGE_MASK;
const VkAccessFlags dst_access_mask =
use_shader_read_layout ? VK_ACCESS_SHADER_READ_BIT : (WRITE_ACCESS_FLAGS | READ_ACCESS_FLAGS);
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized,
vk_copies](vk::CommandBuffer cmdbuf) {
CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized, vk_copies);
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, current_layout, final_layout,
dst_stage_mask, dst_access_mask, vk_copies](vk::CommandBuffer cmdbuf) {
CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, current_layout, final_layout,
vk_copies, dst_stage_mask, dst_access_mask);
});
SetKnownLayout(final_layout);
if (use_shader_read_layout) {
LogUiBarrierEvent(gpu_addr, info.format, final_layout);
}
if (is_rescaled) {
ScaleUp();
}
@ -2017,6 +2149,10 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
SwizzleSource::B,
SwizzleSource::A,
};
is_ui_hud_texture = image.IsUiHudTexture() && IsHudFormat(format);
force_nearest_sampling = is_ui_hud_texture && runtime.ShouldForceNearest(format);
sample_layout = is_ui_hud_texture ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
: VK_IMAGE_LAYOUT_GENERAL;
if (!info.IsRenderTarget()) {
swizzle = info.Swizzle();
TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565(),
@ -2024,6 +2160,22 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
}
if (is_ui_hud_texture) {
switch (format) {
case PixelFormat::B5G6R5_UNORM:
case PixelFormat::R5G6B5_UNORM:
swizzle = {SwizzleSource::R, SwizzleSource::G, SwizzleSource::B,
SwizzleSource::OneFloat};
break;
case PixelFormat::R8G8_UNORM:
swizzle = {SwizzleSource::R, SwizzleSource::R, SwizzleSource::R,
SwizzleSource::G};
break;
default:
break;
}
LogUiSwizzleEvent(gpu_addr, format, swizzle);
}
}
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
if (ImageUsageFlags(format_info, format) != image.UsageFlags()) {
@ -2098,6 +2250,13 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
slot_images = &slot_imgs;
}
void ImageView::LogUiSamplerDecision(VkSampler sampler, bool force_nearest) const {
if (!is_ui_hud_texture) {
return;
}
LogUiSamplerEvent(gpu_addr, format, force_nearest, sampler, sample_layout);
}
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info,
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
: VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
@ -2235,36 +2394,66 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
// Some games have samplers with garbage. Sanitize them here.
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
const auto create_sampler = [&](const f32 anisotropy) {
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = pnext,
.flags = 0,
.magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
.minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
.mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
.mipLodBias = tsc.LodBias(),
.anisotropyEnable = static_cast<VkBool32>(anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
.maxAnisotropy = anisotropy,
.compareEnable = tsc.depth_compare_enabled,
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
.borderColor =
arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
.unnormalizedCoordinates = VK_FALSE,
});
VkSamplerCreateInfo base_ci{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = pnext,
.flags = 0,
.magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
.minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
.mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
.mipLodBias = tsc.LodBias(),
.anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
.maxAnisotropy = max_anisotropy,
.compareEnable = tsc.depth_compare_enabled,
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
.borderColor =
arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
.unnormalizedCoordinates = VK_FALSE,
};
sampler = create_sampler(max_anisotropy);
const auto make_sampler = [&](const VkSamplerCreateInfo& ci) {
return device.GetLogical().CreateSampler(ci);
};
sampler = make_sampler(base_ci);
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
if (max_anisotropy > max_anisotropy_default) {
sampler_default_anisotropy = create_sampler(max_anisotropy_default);
VkSamplerCreateInfo fallback_ci = base_ci;
fallback_ci.maxAnisotropy = max_anisotropy_default;
fallback_ci.anisotropyEnable = static_cast<VkBool32>(fallback_ci.maxAnisotropy > 1.0f
? VK_TRUE
: VK_FALSE);
sampler_default_anisotropy = make_sampler(fallback_ci);
}
VkSamplerCreateInfo ui_ci = base_ci;
ui_ci.minLod = 0.0f;
ui_ci.maxLod = 0.0f;
ui_ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
ui_ci.anisotropyEnable = VK_FALSE;
ui_ci.maxAnisotropy = 1.0f;
sampler_ui_single_mip = make_sampler(ui_ci);
VkSamplerCreateInfo ui_nearest_ci = ui_ci;
ui_nearest_ci.magFilter = VK_FILTER_NEAREST;
ui_nearest_ci.minFilter = VK_FILTER_NEAREST;
sampler_ui_single_mip_nearest = make_sampler(ui_nearest_ci);
}
VkSampler Sampler::HandleUi(bool force_nearest) const noexcept {
if (force_nearest && sampler_ui_single_mip_nearest) {
return *sampler_ui_single_mip_nearest;
}
if (sampler_ui_single_mip) {
return *sampler_ui_single_mip;
}
return Handle();
}
Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,
@ -2392,6 +2581,7 @@ void TextureCacheRuntime::TransitionImageLayout(Image& image) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier);
});
image.SetKnownLayout(VK_IMAGE_LAYOUT_GENERAL);
}
}

View file

@ -115,6 +115,10 @@ public:
VkFormat GetSupportedFormat(VkFormat requested_format, VkFormatFeatureFlags required_features) const;
bool SupportsLinearFiltering(PixelFormat format) const;
bool ShouldForceNearest(PixelFormat format) const;
bool IsUiHudCandidate(const VideoCommon::ImageInfo& info) const;
const Device& device;
Scheduler& scheduler;
MemoryAllocator& memory_allocator;
@ -128,6 +132,9 @@ public:
static constexpr size_t indexing_slots = 8 * sizeof(size_t);
std::array<vk::Buffer, indexing_slots> buffers{};
bool supports_b5g6r5_linear_filter = true;
bool supports_r5g6b5_linear_filter = true;
};
class Image : public VideoCommon::ImageBase {
@ -171,6 +178,18 @@ public:
return (this->*current_image).UsageFlags();
}
[[nodiscard]] bool IsUiHudTexture() const noexcept {
return is_ui_hud_texture;
}
[[nodiscard]] VkImageLayout KnownLayout() const noexcept {
return known_layout;
}
void SetKnownLayout(VkImageLayout layout) noexcept {
known_layout = layout;
}
/// Returns true when the image is already initialized and mark it as initialized
[[nodiscard]] bool ExchangeInitialization() noexcept {
return std::exchange(initialized, true);
@ -199,6 +218,9 @@ private:
// moved without breaking the reference.
vk::Image Image::*current_image{};
bool is_ui_hud_texture = false;
VkImageLayout known_layout = VK_IMAGE_LAYOUT_UNDEFINED;
std::vector<vk::ImageView> storage_image_views;
VkImageAspectFlags aspect_mask = 0;
bool initialized = false;
@ -262,6 +284,20 @@ public:
return buffer_size;
}
[[nodiscard]] bool IsUiHudTexture() const noexcept {
return is_ui_hud_texture;
}
[[nodiscard]] bool RequiresNearestSampling() const noexcept {
return force_nearest_sampling;
}
[[nodiscard]] VkImageLayout SampleLayout() const noexcept {
return sample_layout;
}
void LogUiSamplerDecision(VkSampler sampler, bool force_nearest) const;
private:
struct StorageViews {
std::array<vk::ImageView, Shader::NUM_TEXTURE_TYPES> signeds;
@ -283,6 +319,10 @@ private:
VkImageView render_target = VK_NULL_HANDLE;
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
u32 buffer_size = 0;
bool is_ui_hud_texture = false;
bool force_nearest_sampling = false;
VkImageLayout sample_layout = VK_IMAGE_LAYOUT_GENERAL;
};
class ImageAlloc : public VideoCommon::ImageAllocBase {};
@ -303,9 +343,17 @@ public:
return static_cast<bool>(sampler_default_anisotropy);
}
[[nodiscard]] VkSampler HandleUi(bool force_nearest) const noexcept;
[[nodiscard]] bool HasUiSampler() const noexcept {
return static_cast<bool>(sampler_ui_single_mip);
}
private:
vk::Sampler sampler;
vk::Sampler sampler_default_anisotropy;
vk::Sampler sampler_ui_single_mip;
vk::Sampler sampler_ui_single_mip_nearest;
};
class Framebuffer {

View file

@ -47,11 +47,11 @@ public:
return upload_start;
}
void AddSampledImage(VkImageView image_view, VkSampler sampler) {
void AddSampledImage(VkImageView image_view, VkSampler sampler, VkImageLayout layout) {
*(payload_cursor++) = VkDescriptorImageInfo{
.sampler = sampler,
.imageView = image_view,
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
.imageLayout = layout,
};
}

View file

@ -7,6 +7,8 @@
#pragma once
#include <unordered_set>
#include <type_traits>
#include <utility>
#include <boost/container/small_vector.hpp>
#include "common/alignment.h"
@ -30,6 +32,32 @@ using VideoCore::Surface::PixelFormat;
using VideoCore::Surface::SurfaceType;
using namespace Common::Literals;
namespace detail {
template <typename Staging, typename = void>
struct HasStagingOwner : std::false_type {};
template <typename Staging>
struct HasStagingOwner<Staging, std::void_t<decltype(std::declval<Staging&>().owner)>>
: std::true_type {};
template <typename Staging>
void FlushStagingHostWritesImpl(Staging& staging, std::true_type) {
if (staging.owner != nullptr) {
staging.owner->Flush();
}
}
template <typename Staging>
void FlushStagingHostWritesImpl(Staging&, std::false_type) {}
} // namespace detail
template <typename Staging>
void FlushStagingHostWrites(Staging& staging) {
detail::FlushStagingHostWritesImpl(staging, detail::HasStagingOwner<Staging>{});
}
template <class P>
TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManager& device_memory_)
: runtime{runtime_}, device_memory{device_memory_} {