((device_local_memory - mem_threshold) / 2);
-
- lowmemorydevice = false;
} else {
expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
minimum_memory = 0;
-
- lowmemorydevice = true;
}
const bool gpu_unswizzle_enabled = Settings::values.gpu_unswizzle_enabled.GetValue();
@@ -122,102 +118,48 @@ void TextureCache::RunGarbageCollector() {
bool aggressive_mode = false;
u64 ticks_to_destroy = 0;
size_t num_iterations = 0;
-
const auto Configure = [&](bool allow_aggressive) {
high_priority_mode = total_used_memory >= expected_memory;
aggressive_mode = allow_aggressive && total_used_memory >= critical_memory;
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
};
-
- const auto Cleanup = [this, &num_iterations, &high_priority_mode,
- &aggressive_mode](ImageId image_id) {
+ const auto Cleanup = [this, &num_iterations, &high_priority_mode, &aggressive_mode](ImageId image_id) {
if (num_iterations == 0) {
return true;
}
- --num_iterations;
auto& image = slot_images[image_id];
-
- // Never delete recently allocated sparse textures (within 3 frames)
- const bool is_recently_allocated = image.allocation_tick >= frame_tick - 3;
- if (is_recently_allocated && image.info.is_sparse) {
- return false;
- }
-
if (True(image.flags & ImageFlagBits::IsDecoding)) {
- // This image is still being decoded, deleting it will invalidate the slot
- // used by the async decoder thread.
return false;
}
-
- // Prioritize large sparse textures for cleanup
- const bool is_large_sparse = lowmemorydevice &&
- image.info.is_sparse &&
- image.guest_size_bytes >= 256_MiB;
-
- if (!aggressive_mode && !is_large_sparse &&
- True(image.flags & ImageFlagBits::CostlyLoad)) {
+ const bool must_download = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
+ if (!aggressive_mode && !high_priority_mode && (True(image.flags & ImageFlagBits::CostlyLoad) || must_download)) {
return false;
}
-
- const bool must_download =
- image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
- if (!high_priority_mode && !is_large_sparse && must_download) {
- return false;
- }
-
- if (must_download && !is_large_sparse) {
+ --num_iterations;
+ if (must_download) {
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
image.DownloadMemory(map, copies);
runtime.Finish();
- SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
- swizzle_data_buffer);
+ SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, swizzle_data_buffer);
}
-
if (True(image.flags & ImageFlagBits::Tracked)) {
UntrackImage(image, image_id);
}
UnregisterImage(image_id);
DeleteImage(image_id, image.scale_tick > frame_tick + 5);
-
- if (total_used_memory < critical_memory) {
- if (aggressive_mode) {
- // Sink the aggresiveness.
- num_iterations >>= 2;
- aggressive_mode = false;
- return false;
- }
- if (high_priority_mode && total_used_memory < expected_memory) {
- num_iterations >>= 1;
- high_priority_mode = false;
- }
+ if (aggressive_mode && total_used_memory < critical_memory) {
+ num_iterations >>= 2;
+ aggressive_mode = false;
+ } else if (high_priority_mode && total_used_memory < expected_memory) {
+ num_iterations >>= 1;
+ high_priority_mode = false;
}
return false;
};
-
- // Aggressively clear massive sparse textures
- if (total_used_memory >= expected_memory) {
- lru_cache.ForEachItemBelow(frame_tick, [&](ImageId image_id) {
- auto& image = slot_images[image_id];
- // Only target sparse textures that are old enough
- if (lowmemorydevice &&
- image.info.is_sparse &&
- image.guest_size_bytes >= 256_MiB &&
- image.allocation_tick < frame_tick - 3) {
- LOG_DEBUG(HW_GPU, "GC targeting old sparse texture at 0x{:X} ({} MiB, age: {} frames)",
- image.gpu_addr, image.guest_size_bytes / (1024 * 1024),
- frame_tick - image.allocation_tick);
- return Cleanup(image_id);
- }
- return false;
- });
- }
-
Configure(false);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
-
- // If pressure is still too high, prune aggressively.
if (total_used_memory >= critical_memory) {
Configure(true);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
@@ -1196,9 +1138,6 @@ void TextureCache
::RefreshContents(Image& image, ImageId image_id) {
}
image.flags &= ~ImageFlagBits::CpuModified;
- if( lowmemorydevice && image.info.format == PixelFormat::BC1_RGBA_UNORM && MapSizeBytes(image) >= 256_MiB ) {
- return;
- }
TrackImage(image, image_id);
@@ -1619,39 +1558,6 @@ ImageId TextureCache
::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
}
}
ASSERT_MSG(cpu_addr, "Tried to insert an image to an invalid gpu_addr=0x{:x}", gpu_addr);
-
- // For large sparse textures, aggressively clean up old allocations at same address
- if (lowmemorydevice && info.is_sparse && CalculateGuestSizeInBytes(info) >= 256_MiB) {
- const auto alloc_it = image_allocs_table.find(gpu_addr);
- if (alloc_it != image_allocs_table.end()) {
- const ImageAllocId alloc_id = alloc_it->second;
- auto& alloc_images = slot_image_allocs[alloc_id].images;
-
- // Collect old images at this address that were created more than 2 frames ago
- boost::container::small_vector to_delete;
- for (ImageId old_image_id : alloc_images) {
- Image& old_image = slot_images[old_image_id];
- if (old_image.info.is_sparse &&
- old_image.gpu_addr == gpu_addr &&
- old_image.allocation_tick < frame_tick - 2) { // Try not to delete fresh textures
- to_delete.push_back(old_image_id);
- }
- }
-
- // Delete old images immediately
- for (ImageId old_id : to_delete) {
- Image& old_image = slot_images[old_id];
- LOG_DEBUG(HW_GPU, "Immediately deleting old sparse texture at 0x{:X} ({} MiB)",
- gpu_addr, old_image.guest_size_bytes / (1024 * 1024));
- if (True(old_image.flags & ImageFlagBits::Tracked)) {
- UntrackImage(old_image, old_id);
- }
- UnregisterImage(old_id);
- DeleteImage(old_id, true);
- }
- }
- }
-
const ImageId image_id = JoinImages(info, gpu_addr, *cpu_addr);
const Image& image = slot_images[image_id];
// Using "image.gpu_addr" instead of "gpu_addr" is important because it might be different
@@ -1667,27 +1573,6 @@ template
ImageId TextureCache::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DAddr cpu_addr) {
ImageInfo new_info = info;
const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
-
- // Proactive cleanup for large sparse texture allocations
- if (lowmemorydevice && new_info.is_sparse && size_bytes >= 256_MiB) {
- const u64 estimated_alloc_size = size_bytes;
-
- if (total_used_memory + estimated_alloc_size >= critical_memory) {
- LOG_DEBUG(HW_GPU, "Large sparse texture allocation ({} MiB) - running aggressive GC. "
- "Current memory: {} MiB, Critical: {} MiB",
- size_bytes / (1024 * 1024),
- total_used_memory / (1024 * 1024),
- critical_memory / (1024 * 1024));
- RunGarbageCollector();
-
- // If still over threshold after GC, try one more aggressive pass
- if (total_used_memory + estimated_alloc_size >= critical_memory) {
- LOG_DEBUG(HW_GPU, "Still critically low on memory, running second GC pass");
- RunGarbageCollector();
- }
- }
- }
-
const bool broken_views = runtime.HasBrokenTextureViewFormats();
const bool native_bgr = runtime.HasNativeBgr();
join_overlap_ids.clear();
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 4b4061f21d..47f52c5c99 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -478,7 +478,6 @@ private:
u64 minimum_memory;
u64 expected_memory;
u64 critical_memory;
- bool lowmemorydevice = false;
size_t gpu_unswizzle_maxsize = 0;
size_t swizzle_chunk_size = 0;
u32 swizzle_slices_per_batch = 0;
diff --git a/src/video_core/vulkan_common/vulkan.h b/src/video_core/vulkan_common/vulkan.h
index 2cc0f0d7f0..2609e8dc0f 100644
--- a/src/video_core/vulkan_common/vulkan.h
+++ b/src/video_core/vulkan_common/vulkan.h
@@ -22,16 +22,13 @@
#include
-// Define maintenance 7-9 extension names (not yet in official Vulkan headers)
+// Define maintenance 7-8 extension names (not yet in official Vulkan headers)
#ifndef VK_KHR_MAINTENANCE_7_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_7_EXTENSION_NAME "VK_KHR_maintenance7"
#endif
#ifndef VK_KHR_MAINTENANCE_8_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_8_EXTENSION_NAME "VK_KHR_maintenance8"
#endif
-#ifndef VK_KHR_MAINTENANCE_9_EXTENSION_NAME
-#define VK_KHR_MAINTENANCE_9_EXTENSION_NAME "VK_KHR_maintenance9"
-#endif
// Sanitize macros
#undef CreateEvent
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index b51c57d380..5075a79bcd 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -465,18 +465,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
first_next = &diagnostics_nv;
}
- VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptor_indexing{
- .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT,
- .pNext = use_diagnostics_nv ? static_cast(&diagnostics_nv) : static_cast(&features2),
- .shaderSampledImageArrayNonUniformIndexing = VK_TRUE,
- .descriptorBindingPartiallyBound = VK_TRUE,
- .descriptorBindingVariableDescriptorCount = VK_TRUE,
- };
-
- if (extensions.descriptor_indexing && Settings::values.descriptor_indexing.GetValue()) {
- first_next = &descriptor_indexing;
- }
-
is_blit_depth24_stencil8_supported = TestDepthStencilBlits(VK_FORMAT_D24_UNORM_S8_UINT);
is_blit_depth32_stencil8_supported = TestDepthStencilBlits(VK_FORMAT_D32_SFLOAT_S8_UINT);
is_optimal_astc_supported = ComputeIsOptimalAstcSupported();
@@ -1078,11 +1066,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
SetNext(next, properties.maintenance5);
}
- if (extensions.multi_draw) {
- properties.multi_draw.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT;
- SetNext(next, properties.multi_draw);
- }
// Perform the property fetch.
physical.GetProperties2(properties2);
@@ -1196,7 +1179,7 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control,
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
- /* */ // VK_EXT_extended_dynamic_state
+ // VK_EXT_extended_dynamic_state
extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,
features.extended_dynamic_state,
@@ -1268,7 +1251,6 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
// VK_EXT_robustness2
- // Enable if at least one robustness2 feature is available
extensions.robustness_2 = features.robustness2.robustBufferAccess2 ||
features.robustness2.robustImageAccess2 ||
features.robustness2.nullDescriptor;
@@ -1277,25 +1259,10 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
// VK_EXT_image_robustness
- // Enable if robustImageAccess is available
extensions.image_robustness = features.image_robustness.robustImageAccess;
RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness,
VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME);
- // VK_EXT_provoking_vertex
- if (Settings::values.provoking_vertex.GetValue()) {
- extensions.provoking_vertex = features.provoking_vertex.provokingVertexLast
- && features.provoking_vertex
- .transformFeedbackPreservesProvokingVertex;
- RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex,
- features.provoking_vertex,
- VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
- } else {
- RemoveExtensionFeature(extensions.provoking_vertex,
- features.provoking_vertex,
- VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
- }
-
// VK_KHR_shader_atomic_int64
extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
features.shader_atomic_int64.shaderSharedInt64Atomics;
@@ -1319,21 +1286,12 @@ void Device::RemoveUnsuitableExtensions() {
VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
// VK_EXT_transform_feedback
- // We only require the basic transformFeedback feature and at least
- // one transform feedback buffer. We keep transformFeedbackQueries as it's used by
- // the streaming byte count implementation. GeometryStreams and multiple streams
- // are not strictly required since we currently support only stream 0.
extensions.transform_feedback =
features.transform_feedback.transformFeedback &&
properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
properties.transform_feedback.transformFeedbackQueries;
RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
- if (extensions.transform_feedback) {
- LOG_INFO(Render_Vulkan, "VK_EXT_transform_feedback enabled (buffers={}, queries={})",
- properties.transform_feedback.maxTransformFeedbackBuffers,
- properties.transform_feedback.transformFeedbackQueries);
- }
// VK_EXT_vertex_input_dynamic_state
extensions.vertex_input_dynamic_state =
@@ -1342,17 +1300,6 @@ void Device::RemoveUnsuitableExtensions() {
features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
- // VK_EXT_multi_draw
- extensions.multi_draw = features.multi_draw.multiDraw;
-
- if (extensions.multi_draw) {
- LOG_INFO(Render_Vulkan, "VK_EXT_multi_draw: maxMultiDrawCount={}",
- properties.multi_draw.maxMultiDrawCount);
- }
-
- RemoveExtensionFeatureIfUnsuitable(extensions.multi_draw, features.multi_draw,
- VK_EXT_MULTI_DRAW_EXTENSION_NAME);
-
// VK_KHR_pipeline_executable_properties
if (Settings::values.renderer_shader_feedback.GetValue()) {
extensions.pipeline_executable_properties =
@@ -1377,35 +1324,15 @@ void Device::RemoveUnsuitableExtensions() {
features.workgroup_memory_explicit_layout,
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
- // VK_EXT_swapchain_maintenance1 (extension only, has features)
- // Requires VK_EXT_surface_maintenance1 instance extension
- extensions.swapchain_maintenance1 = features.swapchain_maintenance1.swapchainMaintenance1;
- if (extensions.swapchain_maintenance1) {
- // Check if VK_EXT_surface_maintenance1 instance extension is available
- const auto instance_extensions = vk::EnumerateInstanceExtensionProperties(dld);
- const bool has_surface_maintenance1 = instance_extensions && std::ranges::any_of(*instance_extensions,
- [](const VkExtensionProperties& prop) {
- return std::strcmp(prop.extensionName, VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME) == 0;
- });
- if (!has_surface_maintenance1) {
- LOG_WARNING(Render_Vulkan,
- "VK_EXT_swapchain_maintenance1 requires VK_EXT_surface_maintenance1, disabling");
- extensions.swapchain_maintenance1 = false;
- features.swapchain_maintenance1.swapchainMaintenance1 = false;
- }
- }
- RemoveExtensionFeatureIfUnsuitable(extensions.swapchain_maintenance1, features.swapchain_maintenance1,
- VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME);
-
- // VK_KHR_maintenance1 (core in Vulkan 1.1, no features)
+ // VK_KHR_maintenance1
extensions.maintenance1 = loaded_extensions.contains(VK_KHR_MAINTENANCE_1_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance1, VK_KHR_MAINTENANCE_1_EXTENSION_NAME);
- // VK_KHR_maintenance2 (core in Vulkan 1.1, no features)
+ // VK_KHR_maintenance2
extensions.maintenance2 = loaded_extensions.contains(VK_KHR_MAINTENANCE_2_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance2, VK_KHR_MAINTENANCE_2_EXTENSION_NAME);
- // VK_KHR_maintenance3 (core in Vulkan 1.1, no features)
+ // VK_KHR_maintenance3
extensions.maintenance3 = loaded_extensions.contains(VK_KHR_MAINTENANCE_3_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance3, VK_KHR_MAINTENANCE_3_EXTENSION_NAME);
@@ -1416,17 +1343,6 @@ void Device::RemoveUnsuitableExtensions() {
// VK_KHR_maintenance5
extensions.maintenance5 = features.maintenance5.maintenance5;
-
- if (extensions.maintenance5) {
- LOG_INFO(Render_Vulkan, "VK_KHR_maintenance5 properties: polygonModePointSize={} "
- "depthStencilSwizzleOne={} earlyFragmentTests={} nonStrictWideLines={}",
- properties.maintenance5.polygonModePointSize,
- properties.maintenance5.depthStencilSwizzleOneSupport,
- properties.maintenance5.earlyFragmentMultisampleCoverageAfterSampleCounting &&
- properties.maintenance5.earlyFragmentSampleMaskTestBeforeSampleCounting,
- properties.maintenance5.nonStrictWideLinesUseParallelogram);
- }
-
RemoveExtensionFeatureIfUnsuitable(extensions.maintenance5, features.maintenance5,
VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
@@ -1435,17 +1351,13 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionFeatureIfUnsuitable(extensions.maintenance6, features.maintenance6,
VK_KHR_MAINTENANCE_6_EXTENSION_NAME);
- // VK_KHR_maintenance7 (proposed for Vulkan 1.4, no features)
+ // VK_KHR_maintenance7
extensions.maintenance7 = loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance7, VK_KHR_MAINTENANCE_7_EXTENSION_NAME);
- // VK_KHR_maintenance8 (proposed for Vulkan 1.4, no features)
+ // VK_KHR_maintenance8
extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
-
- // VK_KHR_maintenance9 (proposed for Vulkan 1.4, no features)
- extensions.maintenance9 = loaded_extensions.contains(VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
- RemoveExtensionIfUnsuitable(extensions.maintenance9, VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
}
void Device::SetupFamilies(VkSurfaceKHR surface) {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index caf91104df..ad9d53ce16 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -57,14 +57,12 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(EXT, 4444Formats, 4444_FORMATS, format_a4b4g4r4) \
FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \
FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \
- FEATURE(EXT, MultiDraw, MULTI_DRAW, multi_draw) \
FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \
primitive_topology_list_restart) \
FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \
FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
- FEATURE(EXT, SwapchainMaintenance1, SWAPCHAIN_MAINTENANCE_1, swapchain_maintenance1) \
FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \
FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
@@ -100,12 +98,10 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(KHR, MAINTENANCE_3, maintenance3) \
EXTENSION(KHR, MAINTENANCE_7, maintenance7) \
EXTENSION(KHR, MAINTENANCE_8, maintenance8) \
- EXTENSION(KHR, MAINTENANCE_9, maintenance9) \
EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \
EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \
EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \
EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \
- EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \
EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \
EXTENSION(IMG, FILTER_CUBIC, filter_cubic_img) \
EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights)
@@ -443,11 +439,6 @@ public:
return extensions.viewport_array2;
}
- /// Returns true if the device supporst VK_EXT_DESCRIPTOR_INDEXING
- bool isExtDescriptorIndexingSupported() const {
- return extensions.descriptor_indexing;
- }
-
/// Returns true if the device supports VK_NV_geometry_shader_passthrough.
bool IsNvGeometryShaderPassthroughSupported() const {
return extensions.geometry_shader_passthrough;
@@ -473,11 +464,6 @@ public:
return extensions.swapchain_mutable_format;
}
- /// Returns true if VK_EXT_swapchain_maintenance1 is enabled.
- bool IsExtSwapchainMaintenance1Enabled() const {
- return extensions.swapchain_maintenance1;
- }
-
/// Returns true if VK_KHR_shader_float_controls is enabled.
bool IsKhrShaderFloatControlsSupported() const {
return extensions.shader_float_controls;
@@ -719,6 +705,22 @@ public:
return extensions.provoking_vertex;
}
+ /// Returns true if first vertex provoking mode can be used.
+ bool SupportsProvokingVertexFirstMode() const {
+ return extensions.provoking_vertex;
+ }
+
+ /// Returns true if last vertex provoking mode can be used.
+ bool SupportsProvokingVertexLastMode() const {
+ return extensions.provoking_vertex && features.provoking_vertex.provokingVertexLast;
+ }
+
+ /// Returns true if transform feedback preserves provoking vertex mode semantics.
+ bool SupportsTransformFeedbackProvokingVertexPreservation() const {
+ return extensions.provoking_vertex &&
+ features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
+ }
+
/// Returns true if the device supports VK_KHR_shader_atomic_int64.
bool IsExtShaderAtomicInt64Supported() const {
return extensions.shader_atomic_int64;
@@ -878,11 +880,6 @@ public:
return extensions.maintenance6;
}
- /// Returns true if the device supports VK_EXT_multi_draw.
- bool IsExtMultiDrawSupported() const {
- return extensions.multi_draw;
- }
-
/// Returns true if the device supports VK_KHR_maintenance7.
bool IsKhrMaintenance7Supported() const {
return extensions.maintenance7;
@@ -893,11 +890,6 @@ public:
return extensions.maintenance8;
}
- /// Returns true if the device supports VK_KHR_maintenance9.
- bool IsKhrMaintenance9Supported() const {
- return extensions.maintenance9;
- }
-
/// Returns true if the device supports UINT8 index buffer conversion via compute shader.
bool SupportsUint8Indices() const {
return features.bit8_storage.storageBuffer8BitAccess &&
@@ -1025,7 +1017,6 @@ private:
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
- VkPhysicalDeviceMultiDrawPropertiesEXT multi_draw{};
VkPhysicalDeviceProperties properties{};
};
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index b1a53ab4f3..398342296e 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -81,14 +81,6 @@ namespace {
#endif
if (enable_validation && AreExtensionsSupported(dld, *properties, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME}))
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
- // VK_EXT_surface_maintenance1 is required for VK_EXT_swapchain_maintenance1
- if (window_type != Core::Frontend::WindowSystemType::Headless && AreExtensionsSupported(dld, *properties, std::array{VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME})) {
- extensions.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
- // Some(which?) drivers dont like being told to load this extension(why?)
- // NVIDIA on FreeBSD is totally fine with this through
- if (AreExtensionsSupported(dld, *properties, std::array{VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME}))
- extensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME);
- }
}
return extensions;
}
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index a2fa2b50c6..f59ac7d6bc 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -116,8 +116,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdDrawIndirectCount);
X(vkCmdDrawIndexedIndirectCount);
X(vkCmdDrawIndirectByteCountEXT);
- X(vkCmdDrawMultiEXT);
- X(vkCmdDrawMultiIndexedEXT);
X(vkCmdEndConditionalRenderingEXT);
X(vkCmdEndQuery);
X(vkCmdEndRenderPass);
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 872fbd858e..aaff66359e 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -218,8 +218,6 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount{};
PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount{};
PFN_vkCmdDrawIndirectByteCountEXT vkCmdDrawIndirectByteCountEXT{};
- PFN_vkCmdDrawMultiEXT vkCmdDrawMultiEXT{};
- PFN_vkCmdDrawMultiIndexedEXT vkCmdDrawMultiIndexedEXT{};
PFN_vkCmdEndConditionalRenderingEXT vkCmdEndConditionalRenderingEXT{};
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
PFN_vkCmdEndQuery vkCmdEndQuery{};
@@ -1241,19 +1239,6 @@ public:
counter_buffer_offset, counter_offset, stride);
}
- void DrawMultiEXT(u32 draw_count, const VkMultiDrawInfoEXT* vertex_info,
- u32 instance_count, u32 first_instance, u32 stride) const noexcept {
- dld->vkCmdDrawMultiEXT(handle, draw_count, vertex_info, instance_count, first_instance,
- stride);
- }
-
- void DrawMultiIndexedEXT(u32 draw_count, const VkMultiDrawIndexedInfoEXT* index_info,
- u32 instance_count, u32 first_instance, u32 stride,
- const int32_t* vertex_offset) const noexcept {
- dld->vkCmdDrawMultiIndexedEXT(handle, draw_count, index_info, instance_count,
- first_instance, stride, vertex_offset);
- }
-
void ClearAttachments(Span attachments,
Span rects) const noexcept {
dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),
diff --git a/src/yuzu/game/game_card.cpp b/src/yuzu/game/game_card.cpp
index ba60b54fcc..05167fe455 100644
--- a/src/yuzu/game/game_card.cpp
+++ b/src/yuzu/game/game_card.cpp
@@ -18,8 +18,25 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
- // padding
- QRect cardRect = option.rect.adjusted(4 + m_padding / 2, 4, -4 - m_padding / 2, -4);
+ // Padding, dimensions, alignment...
+ const int column = index.row() % m_columns;
+ const int cell_width = option.rect.width();
+ const int fixed_card_width = cell_width - m_padding;
+ const int margins = 8;
+
+ // The gist of it is that this anchors the left and right sides to the edges,
+ // while maintaining an even gap between each card.
+ // I just smashed random keys into my keyboard until something worked.
+ // Don't even bother trying to figure out what the hell this is doing.
+ const auto total_row_width = m_columns * cell_width;
+ const auto total_gap_space = total_row_width - (margins * 2) - (m_columns * fixed_card_width);
+ const auto gap = (m_columns > 1) ? (total_gap_space / (m_columns - 1)) : 0;
+
+ const auto relative_x = margins + (column * (fixed_card_width + gap));
+ const auto x_pos = option.rect.left() - (column * cell_width) + static_cast(relative_x);
+
+ // also, add some additional padding here to prevent card overlap
+ QRect cardRect(x_pos + 4, option.rect.top() + 4, fixed_card_width - 8, option.rect.height() - margins);
// colors
QPalette palette = option.palette;
@@ -41,8 +58,6 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->setPen(QPen(borderColor, 1));
painter->drawRoundedRect(cardRect, 10, 10);
- static constexpr const int padding = 8;
-
// icon
int _iconsize = UISettings::values.game_icon_size.GetValue();
QSize iconSize(_iconsize, _iconsize);
@@ -54,7 +69,7 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
scaledSize.scale(iconSize, Qt::KeepAspectRatio);
int x = cardRect.left() + (cardRect.width() - scaledSize.width()) / 2;
- int y = cardRect.top() + padding;
+ int y = cardRect.top() + margins;
iconRect = QRect(x, y, scaledSize.width(), scaledSize.height());
@@ -73,17 +88,14 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->restore();
} else {
// if there is no icon just draw a blank rect
- iconRect = QRect(cardRect.left() + padding, cardRect.top() + padding, _iconsize, _iconsize);
+ iconRect = QRect(cardRect.left() + margins, cardRect.top() + margins, _iconsize, _iconsize);
}
if (UISettings::values.show_game_name.GetValue()) {
- // if "none" is selected, pretend there's a
- _iconsize = _iconsize ? _iconsize : 96;
-
// padding + text
QRect textRect = cardRect;
- textRect.setTop(iconRect.bottom() + 8);
- textRect.adjust(padding, 0, -padding, -padding);
+ textRect.setTop(iconRect.bottom() + margins);
+ textRect.adjust(margins, 0, -margins, -margins);
// We are already crammed on space, ignore the row 2
QString title = index.data(Qt::DisplayRole).toString();
@@ -95,8 +107,7 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
font.setBold(true);
// TODO(crueter): fix this abysmal scaling
- // If "none" is selected, then default to 8.5 point font.
- font.setPointSize(1 + std::max(7.0, _iconsize ? std::sqrt(_iconsize * 0.6) : 7.5));
+ font.setPixelSize(1.5 + std::max(10.0, std::sqrt(_iconsize)));
// TODO(crueter): elide mode
painter->setFont(font);
@@ -111,7 +122,8 @@ QSize GameCard::sizeHint(const QStyleOptionViewItem& option, const QModelIndex&
return m_size;
}
-void GameCard::setSize(const QSize& newSize, const int padding) {
+void GameCard::setSize(const QSize& newSize, const int padding, const int columns) {
m_size = newSize;
m_padding = padding;
+ m_columns = columns;
}
diff --git a/src/yuzu/game/game_card.h b/src/yuzu/game/game_card.h
index 86387452b6..f2a3edfddf 100644
--- a/src/yuzu/game/game_card.h
+++ b/src/yuzu/game/game_card.h
@@ -18,9 +18,10 @@ public:
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
- void setSize(const QSize& newSize, const int padding);
+ void setSize(const QSize& newSize, const int padding, const int columns);
private:
QSize m_size;
int m_padding;
+ int m_columns;
};
diff --git a/src/yuzu/game/game_list.cpp b/src/yuzu/game/game_list.cpp
index 0e2b6cf7e6..87b5f87474 100644
--- a/src/yuzu/game/game_list.cpp
+++ b/src/yuzu/game/game_list.cpp
@@ -411,7 +411,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
list_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
list_view->setContextMenuPolicy(Qt::CustomContextMenu);
list_view->setGridSize(QSize(140, 160));
- m_gameCard->setSize(list_view->gridSize(), 0);
+ m_gameCard->setSize(list_view->gridSize(), 0, 4);
list_view->setSpacing(10);
list_view->setWordWrap(true);
@@ -1051,8 +1051,8 @@ void GameList::UpdateIconSize() {
// And now stretch it a bit to fill out remaining space.
// Not perfect but works well enough for now
- int columns = std::max(1, view_width / min_item_width);
- int stretched_width = (view_width - (spacing * (columns - 1))) / columns;
+ int columns = std::max(1, (view_width - 16) / min_item_width);
+ int stretched_width = ((view_width) - (spacing * (columns - 1))) / columns;
// only updates things if grid size is changed
QSize grid_size(stretched_width, icon_size + heightMargin);
@@ -1060,7 +1060,7 @@ void GameList::UpdateIconSize() {
list_view->setUpdatesEnabled(false);
list_view->setGridSize(grid_size);
- m_gameCard->setSize(grid_size, stretched_width - min_item_width);
+ m_gameCard->setSize(grid_size, stretched_width - min_item_width, columns);
list_view->setUpdatesEnabled(true);
}