[TEST] Vertex, Samplers limits adjustments based on hardware support + MSAA Sample Counts degradation

This commit is contained in:
CamilleLaVey
2026-08-17 20:00:06 -04:00
parent 4244a6418d
commit c7b24bad1a
9 changed files with 287 additions and 123 deletions
@@ -964,6 +964,9 @@ bool BlockLinearUnswizzle2DPass::IsSupported(const VideoCommon::ImageInfo& info)
VideoCore::Surface::IsPixelFormatBCn(info.format)) {
return false;
}
if (info.format >= VideoCore::Surface::PixelFormat::MaxColorFormat) {
return false;
}
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
if (bytes_per_block != 4 && bytes_per_block != 8 && bytes_per_block != 16) {
return false;
@@ -1207,6 +1210,9 @@ bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device,
if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
return false;
}
if (info.format >= VideoCore::Surface::PixelFormat::MaxColorFormat) {
return false;
}
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
}
@@ -695,23 +695,30 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
if (!key.state.dynamic_vertex_input) {
const size_t num_vertex_arrays = (std::min)(
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
const u32 max_stride = device.GetMaxVertexInputBindingStride();
const u32 max_divisor = device.GetMaxVertexAttribDivisor();
for (size_t index = 0; index < num_vertex_arrays; ++index) {
const bool instanced = key.state.binding_divisors[index] != 0;
const auto rate =
instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
vertex_bindings.push_back({
.binding = static_cast<u32>(index),
.stride = key.state.vertex_strides[index],
.stride = (std::min)(u32{key.state.vertex_strides[index]}, max_stride),
.inputRate = rate,
});
if (instanced) {
vertex_binding_divisors.push_back({
.binding = static_cast<u32>(index),
.divisor = key.state.binding_divisors[index],
.divisor = (std::min)(key.state.binding_divisors[index], max_divisor),
});
}
}
const size_t max_attributes = static_cast<size_t>(device.GetMaxVertexInputAttributes());
const u32 max_offset = device.GetMaxVertexInputAttributeOffset();
for (size_t index = 0; index < key.state.attributes.size(); ++index) {
if (vertex_attributes.size() >= max_attributes) {
break;
}
const auto& attribute = key.state.attributes[index];
if (!attribute.enabled || !stage_infos[0].loads.Generic(index)) {
continue;
@@ -720,11 +727,10 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.location = static_cast<u32>(index),
.binding = attribute.buffer,
.format = MaxwellToVK::VertexFormat(device, attribute.Type(), attribute.Size()),
.offset = attribute.offset,
.offset = (std::min)(attribute.offset.Value(), max_offset),
});
}
}
ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes());
VkPipelineVertexInputStateCreateInfo vertex_input_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
@@ -1907,7 +1907,9 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
const u32 max_bindings =
static_cast<u32>(std::min<size_t>(Maxwell::NumVertexArrays,
device.GetMaxVertexInputBindings()));
const u32 max_offset = device.GetMaxVertexInputAttributeOffset();
const u32 max_stride = device.GetMaxVertexInputBindingStride();
const u32 max_divisor = device.GetMaxVertexAttribDivisor();
for (u32 index = 0; index < max_attributes; ++index) {
const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
@@ -1921,20 +1923,24 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
.location = index,
.binding = binding,
.format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
.offset = attribute.offset,
.offset = (std::min)(attribute.offset.Value(), max_offset),
});
}
for (u32 binding = 0; binding < max_bindings; ++binding) {
const auto& input_binding{regs.vertex_streams[binding]};
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
u32 divisor = 1;
if (is_instanced) {
divisor = (std::min)(input_binding.frequency, max_divisor);
}
bindings.push_back({
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
.pNext = nullptr,
.binding = binding,
.stride = input_binding.stride,
.stride = (std::min)(input_binding.stride.Value(), max_stride),
.inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX,
.divisor = is_instanced ? input_binding.frequency : 1,
.divisor = divisor,
});
}
@@ -9,6 +9,7 @@
#include <optional>
#include <span>
#include <memory>
#include <utility>
#include <vector>
#include <boost/container/small_vector.hpp>
#include <bit>
@@ -119,7 +120,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
}
[[nodiscard]] VkImageUsageFlags ImageUsageFlags(const MaxwellToVK::FormatInfo& info,
PixelFormat format) {
PixelFormat format, bool allow_storage = true) {
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
if (info.attachable) {
@@ -137,7 +138,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
break;
}
}
if (info.storage) {
if (info.storage && allow_storage) {
usage |= VK_IMAGE_USAGE_STORAGE_BIT;
}
return usage;
@@ -173,6 +174,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
}
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(info.num_samples);
const bool allow_storage =
info.num_samples == 1 || device.IsStorageImageMultisampleSupported();
return VkImageCreateInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
@@ -188,7 +191,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
.arrayLayers = static_cast<u32>(info.resources.layers),
.samples = ConvertSampleCount(info.num_samples),
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = ImageUsageFlags(format_info, info.format),
.usage = ImageUsageFlags(format_info, info.format, allow_storage),
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 0,
.pQueueFamilyIndices = nullptr,
@@ -1766,8 +1769,19 @@ bool TextureCacheRuntime::CanReportMemoryUsage() const {
return device.CanReportMemoryUsage();
}
std::optional<size_t> TextureCacheRuntime::GetSamplerHeapBudget() const {
return device.GetSamplerHeapBudget();
bool TextureCacheRuntime::IsSampleCountSupported(const VideoCommon::ImageInfo& info) const {
if (info.num_samples <= 1) {
return true;
}
const auto format_info =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format);
const bool allow_storage = device.IsStorageImageMultisampleSupported();
const VkImageUsageFlags usage = ImageUsageFlags(format_info, info.format, allow_storage);
const VkImageAspectFlags aspect = ImageAspectMask(info.format);
const bool is_integer = VideoCore::Surface::IsPixelFormatInteger(info.format);
const VkSampleCountFlags supported =
device.GetSupportedSampleCounts(usage, aspect, is_integer);
return (supported & ConvertSampleCount(info.num_samples)) != 0;
}
void TextureCacheRuntime::TickFrame() {
@@ -2632,12 +2646,92 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_
});
}
CustomBorderColorBudget::~CustomBorderColorBudget() {
Release();
}
CustomBorderColorBudget::CustomBorderColorBudget(CustomBorderColorBudget&& rhs) noexcept
: device_ptr{std::exchange(rhs.device_ptr, nullptr)}, held{std::exchange(rhs.held, 0)} {}
CustomBorderColorBudget& CustomBorderColorBudget::operator=(
CustomBorderColorBudget&& rhs) noexcept {
if (this != &rhs) {
Release();
device_ptr = std::exchange(rhs.device_ptr, nullptr);
held = std::exchange(rhs.held, 0);
}
return *this;
}
bool CustomBorderColorBudget::TryAcquire(const Device& device, size_t count) {
if (!device.TryReserveCustomBorderColorSamplers(count)) {
return false;
}
device_ptr = &device;
held += count;
return true;
}
void CustomBorderColorBudget::Release() noexcept {
if (device_ptr != nullptr) {
device_ptr->ReleaseCustomBorderColorSamplers(held);
}
device_ptr = nullptr;
held = 0;
}
Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) {
const auto& device = runtime.device;
const bool has_custom_border_colors = runtime.device.IsCustomBorderColorUsable();
const auto color = tsc.BorderColor();
const auto srgb_color = tsc.SrgbBorderColor();
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
const VkFilter mag_filter{MaxwellToVK::Sampler::Filter(tsc.mag_filter)};
const VkFilter min_filter{MaxwellToVK::Sampler::Filter(tsc.min_filter)};
const VkSamplerMipmapMode mipmap_mode{MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter)};
const bool has_linear_filtering{mag_filter == VK_FILTER_LINEAR ||
min_filter == VK_FILTER_LINEAR ||
mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR};
const VkSamplerAddressMode wrap_u{
MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter)};
const VkSamplerAddressMode wrap_v{
MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter)};
const VkSamplerAddressMode wrap_p{
MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter)};
const bool samples_border = wrap_u == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
wrap_v == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
wrap_p == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
const VkSamplerReductionModeEXT reduction_mode =
MaxwellToVK::SamplerReduction(tsc.reduction_filter);
const bool has_minmax_reduction =
device.IsExtSamplerFilterMinmaxSupported() &&
reduction_mode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT;
size_t custom_border_color_count = 1;
if (max_anisotropy > max_anisotropy_default) {
++custom_border_color_count;
}
if (has_linear_filtering) {
++custom_border_color_count;
}
if (tsc.depth_compare_enabled) {
++custom_border_color_count;
}
if (has_minmax_reduction) {
++custom_border_color_count;
}
if (tsc.srgb_conversion && srgb_color != color) {
++custom_border_color_count;
}
const bool has_custom_border_colors =
samples_border && device.IsCustomBorderColorUsable() &&
custom_border_color_budget.TryAcquire(device, custom_border_color_count);
const VkSamplerCustomBorderColorCreateInfoEXT border_ci{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
.pNext = nullptr,
@@ -2667,38 +2761,26 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
const VkSamplerReductionModeCreateInfoEXT reduction_ci{
.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT,
.pNext = pnext,
.reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter),
.reductionMode = reduction_mode,
};
const VkSamplerReductionModeCreateInfoEXT reduction_ci_srgb{
.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT,
.pNext = srgb_pnext,
.reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter),
.reductionMode = reduction_mode,
};
const VkSamplerReductionModeCreateInfoEXT reduction_ci_without_border{
.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT,
.pNext = nullptr,
.reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter),
.reductionMode = reduction_mode,
};
const void* pnext_without_border = nullptr;
const bool has_minmax_reduction =
runtime.device.IsExtSamplerFilterMinmaxSupported() &&
reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT;
if (runtime.device.IsExtSamplerFilterMinmaxSupported()) {
if (device.IsExtSamplerFilterMinmaxSupported()) {
pnext = &reduction_ci;
srgb_pnext = &reduction_ci_srgb;
pnext_without_border = &reduction_ci_without_border;
} else if (reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) {
} else if (reduction_mode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) {
LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required");
}
// Some games have samplers with garbage. Sanitize them here.
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
const VkFilter mag_filter{MaxwellToVK::Sampler::Filter(tsc.mag_filter)};
const VkFilter min_filter{MaxwellToVK::Sampler::Filter(tsc.min_filter)};
const VkSamplerMipmapMode mipmap_mode{MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter)};
const bool has_linear_filtering{mag_filter == VK_FILTER_LINEAR ||
min_filter == VK_FILTER_LINEAR ||
mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR};
const auto make_create_info = [&](const f32 anisotropy, bool force_nearest,
bool disable_compare, const void* chain,
@@ -2710,9 +2792,9 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
.magFilter = force_nearest ? VK_FILTER_NEAREST : mag_filter,
.minFilter = force_nearest ? VK_FILTER_NEAREST : min_filter,
.mipmapMode = force_nearest ? VK_SAMPLER_MIPMAP_MODE_NEAREST : mipmap_mode,
.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),
.addressModeU = wrap_u,
.addressModeV = wrap_v,
.addressModeW = wrap_p,
.mipLodBias = tsc.LodBias(),
.anisotropyEnable =
static_cast<VkBool32>(!force_nearest && anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
@@ -2760,7 +2842,6 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
sampler = create_sampler(max_anisotropy, false);
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, false);
}
@@ -2785,7 +2866,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
device_ptr = &device;
border_color_value = color;
srgb_border_color_value = srgb_color;
swizzle_reduction_mode = reduction_ci.reductionMode;
swizzle_reduction_mode = reduction_mode;
swizzle_uses_reduction = has_minmax_reduction;
swizzle_base_ci = make_create_info(max_anisotropy, false, false, nullptr,
VK_BORDER_COLOR_FLOAT_CUSTOM_EXT);
@@ -2802,6 +2883,11 @@ VkSampler Sampler::HandleWithSwizzle(const VkComponentMapping& mapping, bool srg
if (it != swizzle_variants.end()) {
return *it->sampler;
}
constexpr size_t MAX_SWIZZLE_VARIANTS = 8;
if (swizzle_variants.size() >= MAX_SWIZZLE_VARIANTS ||
!custom_border_color_budget.TryAcquire(*device_ptr, 1)) {
return *sampler;
}
std::array<float, 4> value = border_color_value;
if (srgb) {
value = srgb_border_color_value;
@@ -66,7 +66,7 @@ public:
bool CanReportMemoryUsage() const;
std::optional<size_t> GetSamplerHeapBudget() const;
bool IsSampleCountSupported(const VideoCommon::ImageInfo& info) const;
void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
const Region2D& dst_region, const Region2D& src_region,
@@ -472,6 +472,26 @@ private:
class ImageAlloc : public VideoCommon::ImageAllocBase {};
class CustomBorderColorBudget {
public:
CustomBorderColorBudget() = default;
~CustomBorderColorBudget();
CustomBorderColorBudget(const CustomBorderColorBudget&) = delete;
CustomBorderColorBudget& operator=(const CustomBorderColorBudget&) = delete;
CustomBorderColorBudget(CustomBorderColorBudget&& rhs) noexcept;
CustomBorderColorBudget& operator=(CustomBorderColorBudget&& rhs) noexcept;
bool TryAcquire(const Device& device, size_t count);
private:
void Release() noexcept;
const Device* device_ptr = nullptr;
size_t held = 0;
};
class Sampler {
public:
explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
@@ -541,6 +561,8 @@ private:
vk::Sampler sampler;
};
CustomBorderColorBudget custom_border_color_budget;
vk::Sampler sampler;
vk::Sampler sampler_default_anisotropy;
vk::Sampler sampler_nearest;
+17 -60
View File
@@ -1161,9 +1161,21 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
}
}
template <class P>
ImageInfo TextureCache<P>::ClampedSampleCount(ImageInfo info) const {
if (info.num_samples > 1) {
if constexpr (requires { runtime.IsSampleCountSupported(info); }) {
if (!runtime.IsSampleCountSupported(info)) {
info.num_samples = 1;
}
}
}
return info;
}
template <class P>
ImageViewId TextureCache<P>::CreateImageView(const TICEntry& config) {
const ImageInfo info(config);
const ImageInfo info = ClampedSampleCount(ImageInfo(config));
if (info.type == ImageType::Buffer) {
const ImageViewInfo view_info(config, 0);
return slot_image_views.insert(runtime, info, view_info, config.Address());
@@ -1877,67 +1889,10 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config, bool compute) {
const auto [pair, is_new] = channel_state->samplers.try_emplace(config);
if (is_new) {
pair->second = slot_samplers.insert(runtime, config);
EnforceSamplerBudget();
}
return pair->second;
}
template <class P>
std::optional<size_t> TextureCache<P>::QuerySamplerBudget() const {
if constexpr (requires { runtime.GetSamplerHeapBudget(); }) {
return runtime.GetSamplerHeapBudget();
} else {
return std::nullopt;
}
}
template <class P>
void TextureCache<P>::EnforceSamplerBudget() {
if (auto const budget = QuerySamplerBudget(); budget) {
if (slot_samplers.size() < *budget) {
return;
}
if (!channel_state) {
return;
}
if (last_sampler_gc_frame == frame_tick) {
return;
}
last_sampler_gc_frame = frame_tick;
TrimInactiveSamplers(*budget);
}
}
template <class P>
void TextureCache<P>::TrimInactiveSamplers(size_t budget) {
if (channel_state->samplers.size() > 0) {
constexpr size_t SAMPLER_GC_SLACK = 1024;
ankerl::unordered_dense::set<SamplerId> active_sampler_ids;
for (auto const& e : channel_state->sampler_ids)
active_sampler_ids.insert(e.second);
// Elements in the map must be necesarily valid
size_t removed = 0;
for (auto it = channel_state->samplers.begin(); it != channel_state->samplers.end();) {
const SamplerId sampler_id = it->second;
if (!sampler_id || sampler_id == CORRUPT_ID) {
it = channel_state->samplers.erase(it);
} else if (std::ranges::find(active_sampler_ids, sampler_id) != active_sampler_ids.end()) {
++it;
} else {
slot_samplers.erase(sampler_id);
it = channel_state->samplers.erase(it);
++removed;
if (slot_samplers.size() + SAMPLER_GC_SLACK <= budget) {
break;
}
}
}
if (removed != 0) {
LOG_WARNING(HW_GPU, "Sampler cache exceeded {} entries on this driver; reclaimed {} inactive samplers", budget, removed);
}
}
}
template <class P>
ImageViewId TextureCache<P>::FindColorBuffer(size_t index) {
const auto& regs = maxwell3d->regs;
@@ -1952,7 +1907,8 @@ ImageViewId TextureCache<P>::FindColorBuffer(size_t index) {
if (rt.format == Tegra::RenderTargetFormat::NONE) {
return ImageViewId{};
}
const ImageInfo info(regs.rt[index], regs.anti_alias_samples_mode);
const ImageInfo info =
ClampedSampleCount(ImageInfo(regs.rt[index], regs.anti_alias_samples_mode));
return FindRenderTargetView(info, gpu_addr);
}
@@ -1966,7 +1922,8 @@ ImageViewId TextureCache<P>::FindDepthBuffer() {
if (gpu_addr == 0) {
return ImageViewId{};
}
const ImageInfo info(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode);
const ImageInfo info =
ClampedSampleCount(ImageInfo(regs.zeta, regs.zeta_size, regs.anti_alias_samples_mode));
return FindRenderTargetView(info, gpu_addr);
}
@@ -416,11 +416,10 @@ private:
bool ScaleDown(Image& image);
u64 GetScaledImageSizeBytes(const ImageBase& image);
[[nodiscard]] ImageInfo ClampedSampleCount(ImageInfo info) const;
void QueueAsyncDecode(Image& image, ImageId image_id);
void TickAsyncDecode();
void EnforceSamplerBudget();
void TrimInactiveSamplers(size_t budget);
std::optional<size_t> QuerySamplerBudget() const;
void QueueAsyncUnswizzle(Image& image, ImageId image_id);
void TickAsyncUnswizzle();
@@ -507,7 +506,6 @@ private:
u64 modification_tick = 0;
u64 frame_tick = 0;
u64 last_sampler_gc_frame = (std::numeric_limits<u64>::max)();
Common::ThreadWorker texture_decode_worker{1, "TextureDecoder", {},
Common::ThreadPlacement::Efficiency};
+70 -19
View File
@@ -628,21 +628,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
}
}
if (is_qualcomm) {
const size_t sampler_limit = properties.properties.limits.maxSamplerAllocationCount;
if (sampler_limit > 0) {
constexpr size_t MIN_SAMPLER_BUDGET = 1024U;
const size_t reserved = sampler_limit / 4U;
const size_t derived_budget =
(std::max)(MIN_SAMPLER_BUDGET, sampler_limit - reserved);
sampler_heap_budget = derived_budget;
LOG_WARNING(Render_Vulkan,
"Qualcomm driver reports max {} samplers; reserving {} (25%) and "
"allowing Eden to use {} (75%) to avoid heap exhaustion",
sampler_limit, reserved, sampler_heap_budget);
}
}
if (extensions.sampler_filter_minmax && is_amd) {
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
if (!features.shader_float16_int8.shaderFloat16) {
@@ -1240,6 +1225,16 @@ bool Device::GetSuitability(bool requires_swapchain) {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
SetNext(next, properties.maintenance5);
}
if (extensions.custom_border_color) {
properties.custom_border_color.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
SetNext(next, properties.custom_border_color);
}
if (extensions.vertex_attribute_divisor) {
properties.vertex_attribute_divisor.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
SetNext(next, properties.vertex_attribute_divisor);
}
// Perform the property fetch.
physical.GetProperties2(properties2);
@@ -1582,11 +1577,67 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
}
}
std::optional<size_t> Device::GetSamplerHeapBudget() const {
if (sampler_heap_budget == 0) {
return std::nullopt;
VkSampleCountFlags Device::GetSupportedSampleCounts(VkImageUsageFlags usage,
VkImageAspectFlags aspect,
bool is_integer) const {
const VkPhysicalDeviceLimits& limits = properties.properties.limits;
const bool has_color = (aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
const bool has_depth = (aspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
const bool has_stencil = (aspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
VkSampleCountFlags counts = ~VkSampleCountFlags{0};
if ((usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) {
if (has_color) {
if (is_integer) {
counts &= limits.sampledImageIntegerSampleCounts;
} else {
counts &= limits.sampledImageColorSampleCounts;
}
}
if (has_depth) {
counts &= limits.sampledImageDepthSampleCounts;
}
if (has_stencil) {
counts &= limits.sampledImageStencilSampleCounts;
}
}
return sampler_heap_budget;
if ((usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) {
counts &= limits.framebufferColorSampleCounts;
}
if ((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) {
if (has_depth) {
counts &= limits.framebufferDepthSampleCounts;
}
if (has_stencil) {
counts &= limits.framebufferStencilSampleCounts;
}
}
if ((usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0) {
counts &= limits.storageImageSampleCounts;
}
return counts;
}
bool Device::TryReserveCustomBorderColorSamplers(size_t count) const {
const size_t limit = properties.custom_border_color.maxCustomBorderColorSamplers;
if (limit == 0) {
return true;
}
size_t used = custom_border_color_samplers_used.load(std::memory_order_relaxed);
while (used + count <= limit) {
if (custom_border_color_samplers_used.compare_exchange_weak(
used, used + count, std::memory_order_relaxed, std::memory_order_relaxed)) {
return true;
}
}
return false;
}
void Device::ReleaseCustomBorderColorSamplers(size_t count) const {
if (count == 0) {
return;
}
custom_border_color_samplers_used.fetch_sub(count, std::memory_order_relaxed);
}
u64 Device::GetDeviceMemoryUsage() const {
+35 -3
View File
@@ -6,6 +6,7 @@
#pragma once
#include <atomic>
#include <optional>
#include <set>
#include <span>
@@ -458,6 +459,10 @@ FN_MAX_LIMIT_LIST
return features.features.shaderStorageImageMultisample;
}
/// Returns the sample counts an image with the given usage and aspect can be created with.
VkSampleCountFlags GetSupportedSampleCounts(VkImageUsageFlags usage, VkImageAspectFlags aspect,
bool is_integer) const;
/// Returns true if the device warp size can potentially be bigger than guest's warp size.
bool IsWarpSizePotentiallyBiggerThanGuest() const {
return is_warp_potentially_bigger;
@@ -714,6 +719,17 @@ FN_MAX_LIMIT_LIST
features.custom_border_color.customBorderColorWithoutFormat;
}
/// Returns how many live samplers may carry a custom border color, 0 when unknown.
u32 GetMaxCustomBorderColorSamplers() const {
return properties.custom_border_color.maxCustomBorderColorSamplers;
}
/// Takes budget for samplers carrying a custom border color, false when exhausted.
bool TryReserveCustomBorderColorSamplers(size_t count) const;
/// Gives back budget taken by TryReserveCustomBorderColorSamplers.
void ReleaseCustomBorderColorSamplers(size_t count) const;
/// Returns true if the device supports VK_EXT_color_write_enable.
bool IsExtColorWriteEnableSupported() const {
return extensions.color_write_enable;
@@ -925,8 +941,6 @@ FN_MAX_LIMIT_LIST
return has_broken_parallel_compiling;
}
std::optional<size_t> GetSamplerHeapBudget() const;
/// Returns the vendor name reported from Vulkan.
std::string_view GetVendorName() const {
return properties.driver.driverName;
@@ -977,6 +991,22 @@ FN_MAX_LIMIT_LIST
return properties.properties.limits.maxVertexInputBindings;
}
u32 GetMaxVertexInputAttributeOffset() const {
return properties.properties.limits.maxVertexInputAttributeOffset;
}
u32 GetMaxVertexInputBindingStride() const {
return properties.properties.limits.maxVertexInputBindingStride;
}
u32 GetMaxVertexAttribDivisor() const {
const u32 limit = properties.vertex_attribute_divisor.maxVertexAttribDivisor;
if (!extensions.vertex_attribute_divisor || limit == 0) {
return 1U;
}
return limit;
}
u32 GetMaxViewports() const {
return properties.properties.limits.maxViewports;
}
@@ -1199,6 +1229,8 @@ private:
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_attribute_divisor{};
VkPhysicalDeviceProperties properties{};
};
@@ -1237,7 +1269,7 @@ private:
bool dynamic_state3_alpha_to_coverage{};
bool dynamic_state3_alpha_to_one{};
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
size_t sampler_heap_budget{}; ///< Sampler budget for buggy drivers (0 = unlimited).
mutable std::atomic<size_t> custom_border_color_samplers_used{};
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
u32 sets_per_pool{}; ///< Sets per Description Pool
NvidiaArchitecture nvidia_arch{NvidiaArchitecture::Arch_AmpereOrNewer};