diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 2418003904..7b98fe9b44 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -30,8 +30,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { BUFFER_REORDER_DISABLE("disable_buffer_reorder"), RENDERER_DEBUG("debug"), RENDERER_PATCH_OLD_QCOM_DRIVERS("patch_old_qcom_drivers"), - RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"), - RENDERER_PROVOKING_VERTEX("provoking_vertex"), RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"), RENDERER_SAMPLE_SHADING("sample_shading"), GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index a8bd44983b..cabea73353 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -141,20 +141,6 @@ abstract class SettingsItem( valuesId = R.array.dynaStateValues ) ) - put( - SwitchSetting( - BooleanSetting.RENDERER_PROVOKING_VERTEX, - titleId = R.string.provoking_vertex, - descriptionId = R.string.provoking_vertex_description - ) - ) - put( - SwitchSetting( - BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE, - titleId = R.string.vertex_input_dynamic_state, - descriptionId = R.string.vertex_input_dynamic_state_description - ) - ) put( SwitchSetting( BooleanSetting.RENDERER_DESCRIPTOR_INDEXING, @@ -349,15 +335,6 @@ abstract class SettingsItem( valuesId = R.array.astcDecodingMethodValues ) ) - put( - SingleChoiceSetting( - IntSetting.RENDERER_ASTC_RECOMPRESSION, - titleId = R.string.astc_recompression, - descriptionId = R.string.astc_recompression_description, - choicesId = R.array.astcRecompressionMethodNames, - valuesId = R.array.astcRecompressionMethodValues - ) - ) put( SingleChoiceSetting( IntSetting.RENDERER_VRAM_USAGE_MODE, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 77104e0614..c43de4d5c7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -271,7 +271,6 @@ class SettingsFragmentPresenter( add(IntSetting.MAX_ANISOTROPY.key) add(IntSetting.RENDERER_VRAM_USAGE_MODE.key) add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key) - add(IntSetting.RENDERER_ASTC_RECOMPRESSION.key) add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key) add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) @@ -291,8 +290,6 @@ class SettingsFragmentPresenter( add(HeaderSetting(R.string.extensions)) add(IntSetting.RENDERER_DYNA_STATE.key) - add(BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE.key) - add(BooleanSetting.RENDERER_PROVOKING_VERTEX.key) add(BooleanSetting.RENDERER_DESCRIPTOR_INDEXING.key) add(IntSetting.RENDERER_SAMPLE_SHADING.key) diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index 05f65ecf78..7f3982f49b 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -506,8 +506,6 @@ الحالة الديناميكية الموسعة يتحكم هذا الخيار في عدد الميزات التي يمكن استخدامها في حالة الديناميكية الموسعة. تسمح الأرقام الأعلى بمزيد من الميزات ويمكن أن تزيد من الأداء، ولكنها قد تسبب مشاكل مع بعض برامج التشغيل والأجهزة. معطل - حالة ديناميكية لإدخال الرأس - يتيح ميزة الحالة الديناميكية لإدخال الرأس لتحسين الجودة والأداء. الرأس المثير يحسن الإضاءة ومعالجة الرؤوس في بعض الألعاب. مدعوم فقط على وحدات معالجة الرسومات Vulkan 1.0+. فهرسة الوصف diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml index b56c21c9b0..c3ee5e6f53 100644 --- a/src/android/app/src/main/res/values-cs/strings.xml +++ b/src/android/app/src/main/res/values-cs/strings.xml @@ -488,8 +488,6 @@ Úroveň EDS Určuje počet funkcí využívaných v rámci rozšířeného dynamického stavu API Vulkan (Extended Dynamic State). Vyšší hodnoty umožňují využít více funkcí a mohou zvýšit výkon, ale u některých ovladačů a výrobců grafických karet mohou způsobovat problémy s kompatibilitou. Vypnuto - Dynamický stav vstupu vrcholů (Vertex Input) - Aktivuje funkci dynamického stavu vstupu vrcholů (Vertex Input Dynamic State) pro lepší kvalitu a výkon. Určující vrchol Zlepšuje osvětlení a zpracování vrcholů v některých hrách. Podporováno pouze na GPU s API Vulkan 1.0+. Indexování deskriptorů diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 7524402e6e..5c499a4080 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -486,8 +486,6 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die Erweiterter dynamischer Status Steuert die Anzahl der Funktionen, die im \"Vertex Input Dynamic State\" werden können. Höhere Werte ermöglichen mehr Funktionen und können die Leistung steigern, können aber bei einigen Treibern und Anbietern zu Problemen führen. Deaktiviert - Vertex Input Dynamic State - Aktiviert die Funktion \"Vertex Input Dynamic State\" für bessere Qualität und Leistung. Provokanter Vertex Verbessert die Beleuchtung und die Vertex-Verarbeitung in einigen Spielen. Wird nur von GPUs mit Vulkan 1.0+ unterstützt. Deskriptor-Indizierung diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index add275870d..4294e6d81e 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -436,8 +436,6 @@ Compile les shaders de manière asynchrone. Cela peut réduire les saccades mais peut aussi provoquer des problèmes graphiques. État dynamique étendu Désactivé - État dynamique d\'entrée de sommet - Active la fonctionnalité d\'état dynamique des entrées de sommets pour une meilleure qualité et de meilleures performances. Provoque des Vertex Améliore l`éclairage et la gestion des vertex dans certains jeux. Pris en charge uniquement par les GPU Vulkan 1.0+. Indexation des descripteurs diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index 3d69cce8f3..6954b65fe1 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -488,8 +488,6 @@ Rozszerzony stan dynamiczny Kontroluje liczbę funkcji, które mogą być używane w Extended Dynamic State. Wyższe wartości pozwalają na użycie większej liczby funkcji i mogą zwiększyć wydajność, ale mogą powodować problemy z niektórymi sterownikami i u niektórych producentów. Wyłączone - Dynamiczny stan wejścia wierzchołków - Włącza funkcję dynamicznego stanu wejścia wierzchołków, poprawiając jakość i wydajność. Wierzchołek prowokujący Poprawia oświetlenie i obsługę wierzchołków w niektórych grach. Obsługiwane tylko przez GPU Vulkan 1.0+. Indeksowanie deskryptorów diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index 08e2695d2e..5d1843fdae 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -471,8 +471,6 @@ Compila shaders de forma assíncrona. Isso pode reduzir engasgos, mas também pode introduzir falhas gráficas. Extended Dynamic State Desativado - Vertex Input Dynamic State - Ativa o recurso de vertex input dynamic state para melhor qualidade e desempenho. Provoking Vertex Vértice Provocante: Melhora a iluminação e o processamento de vértices em certos jogos. Suportado apenas em GPUs com Vulkan 1.0 ou superior. Descriptor Indexing diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index a9a3cceaae..de2bcea0f0 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -498,8 +498,6 @@ Расширенное динамическое состояние Управляет количеством функций, доступных в режиме «Расширенное динамическое состояние». Большее число позволяет задействовать больше функций и может повысить производительность, но способно вызывать проблемы с некоторыми драйверами и графикой. Отключено - Динамическое состояние ввода вершин - Включает функцию динамического состояния ввода вершин для повышения качества и производительности Определяющая вершина Улучшает освещение и обработку вершин в некоторых играх. Поддерживается только ГПУ с Vulkan 1.0+. Индексирование дескрипторов diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index ada2445d05..343d38103f 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -502,8 +502,6 @@ Розширений динамічний стан Керує кількістю функцій, які можна використовувати в «Розширеному динамічному стані». Вище число дозволяє більше функцій і може покращити продуктивність, але може спричинити проблеми з деякими драйверами й виробниками. Вимкнено - Динамічний стан введення вершин - Вмикає можливість динамічного стану введення вершин для кращих якості й продуктивності. Провокативна вершина Покращує освітлення та взаємодію з вершинами у деяких іграх. Лише для ГП з підтримкою Vulkan 1.0+. Індексація дескрипторів diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index 08b55297a7..b1da5135dc 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -496,8 +496,6 @@ 扩展动态状态 控制在扩展动态状态中可使用的函数数量。更高的数值允许启用更多功能,并可能提升性能,但同时也可能导致额外的图形问题。 已禁用 - 顶点输入动态状态 - 开启顶点输入动态状态功能来获得更好的质量和性能。 引发顶点 改善某些游戏中的光照和顶点处理。仅支持Vulkan 1.0+ GPU。 描述符索引 diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index c7061ebc03..b593f97575 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -467,8 +467,6 @@ 非同步編譯著色器。這可能會減少卡頓,但也可能導致圖形錯誤。 擴展動態狀態 已停用 - 頂點輸入動態狀態 - 啟用頂點輸入動態狀態以取得更佳的品質及性能 引發頂點 改善某些遊戲中的光照和頂點處理。僅支援Vulkan 1.0+ GPU。 描述符索引 diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 565decb390..31709eb89b 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -632,14 +632,12 @@ @string/disabled ExtendedDynamicState 1 ExtendedDynamicState 2 - ExtendedDynamicState 3 0 1 2 - 3 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 7d094effcb..de74b6c2eb 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -531,8 +531,6 @@ Extended Dynamic State Controls the number of features that can be used in Extended Dynamic State. Higher numbers allow for more features and can increase performance, but may cause issues with some drivers and vendors. Disabled - Vertex Input Dynamic State - Enables vertex input dynamic state feature for better quality and performance. Provoking Vertex Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs. Descriptor Indexing diff --git a/src/common/settings.h b/src/common/settings.h index 7c6c0d062f..ff32ee42c3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -453,7 +453,7 @@ struct Values { Category::RendererAdvanced}; SwitchableSetting accelerate_astc{linkage, #ifdef ANDROID - AstcDecodeMode::Cpu, + AstcDecodeMode::Gpu, #else AstcDecodeMode::Gpu, #endif @@ -586,7 +586,7 @@ struct Values { SwitchableSetting dyna_state{linkage, #if defined (ANDROID) || defined (__APPLE__) - ExtendedDynamicState::Disabled, + ExtendedDynamicState::EDS1, #else ExtendedDynamicState::EDS2, #endif @@ -601,14 +601,6 @@ struct Values { Category::RendererExtensions, Specialization::Scalar}; - SwitchableSetting vertex_input_dynamic_state{linkage, -#if defined (ANDROID) - false, -#else - true, -#endif - "vertex_input_dynamic_state", Category::RendererExtensions}; - SwitchableSetting provoking_vertex{linkage, false, "provoking_vertex", Category::RendererExtensions}; SwitchableSetting descriptor_indexing{linkage, false, "descriptor_indexing", Category::RendererExtensions}; Setting renderer_debug{linkage, false, "debug", Category::RendererDebug}; diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 638be4127f..5de0641b69 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -154,7 +154,7 @@ ENUM(GpuUnswizzleSize, VerySmall, Small, Normal, Large, VeryLarge) ENUM(GpuUnswizzle, VeryLow, Low, Normal, Medium, High) ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High) ENUM(TemperatureUnits, Celsius, Fahrenheit) -ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3); +ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2); ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All) ENUM(GameListMode, TreeView, GridView); ENUM(SpeedMode, Standard, Turbo, Slow); diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index f49c43ee2a..2de868839e 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -368,17 +368,6 @@ std::unique_ptr InitializeTranslations(QObject* parent) "Higher states allow for more features and can increase performance, but may cause " "additional graphical issues.")); - INSERT(Settings, - vertex_input_dynamic_state, - tr("Vertex Input Dynamic State"), - tr("Enables vertex input dynamic state feature for better quality and performance.")); - - INSERT(Settings, - provoking_vertex, - tr("Provoking Vertex"), - tr("Improves lighting and vertex handling in some games.\n" - "Only Vulkan 1.0+ devices support this extension.")); - INSERT(Settings, descriptor_indexing, tr("Descriptor Indexing"), @@ -796,7 +785,6 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ExtendedDynamicState, Disabled, tr("Disabled")), PAIR(ExtendedDynamicState, EDS1, tr("ExtendedDynamicState 1")), PAIR(ExtendedDynamicState, EDS2, tr("ExtendedDynamicState 2")), - PAIR(ExtendedDynamicState, EDS3, tr("ExtendedDynamicState 3")), }}); translations->insert({Settings::EnumMetadata::Index(), diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index db11def7b2..beab29ec8a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -491,6 +491,9 @@ void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) { } void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) { + if (!ctx.runtime_info.active_color_outputs[index]) { + return; + } const Id component_id{ctx.Const(component)}; const AttributeType type{ctx.runtime_info.color_output_types[index]}; if (type == AttributeType::Float) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 4bff810547..2fd0f3bd1a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -14,6 +14,25 @@ namespace Shader::Backend::SPIRV { namespace { +Id GetResultType(EmitContext& ctx, NumericType numeric_type) { + switch (numeric_type) { + case NumericType::Float: + return ctx.F32[4]; + case NumericType::SignedInt: + return ctx.S32[4]; + case NumericType::UnsignedInt: + return ctx.U32[4]; + } + throw LogicError("Invalid numeric type {}", static_cast(numeric_type)); +} + +NumericType GetTextureNumericType(EmitContext& ctx, const IR::TextureInstInfo& info) { + if (info.type == TextureType::Buffer) { + return ctx.texture_buffers.at(info.descriptor_index).numeric_type; + } + return ctx.textures.at(info.descriptor_index).numeric_type; +} + class ImageOperands { public: [[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false; @@ -201,10 +220,10 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind const TextureBufferDefinition& def{ctx.texture_buffers.at(info.descriptor_index)}; if (def.count > 1) { const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}; - const Id ptr{ctx.OpAccessChain(ctx.image_buffer_type, def.id, idx)}; - return ctx.OpLoad(ctx.image_buffer_type, ptr); + const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)}; + return ctx.OpLoad(def.image_type, ptr); } - return ctx.OpLoad(ctx.image_buffer_type, def.id); + return ctx.OpLoad(def.image_type, def.id); } else { const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; if (def.count > 1) { @@ -216,23 +235,24 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind } } -std::pair Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { +std::pair Image(EmitContext& ctx, const IR::Value& index, + IR::TextureInstInfo info) { if (info.type == TextureType::Buffer) { const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)}; if (def.count > 1) { const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}; const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)}; - return {ctx.OpLoad(def.image_type, ptr), def.is_integer}; + return {ctx.OpLoad(def.image_type, ptr), def.numeric_type}; } - return {ctx.OpLoad(def.image_type, def.id), def.is_integer}; + return {ctx.OpLoad(def.image_type, def.id), def.numeric_type}; } else { const ImageDefinition def{ctx.images.at(info.descriptor_index)}; if (def.count > 1) { const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}; const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)}; - return {ctx.OpLoad(def.image_type, ptr), def.is_integer}; + return {ctx.OpLoad(def.image_type, ptr), def.numeric_type}; } - return {ctx.OpLoad(def.image_type, def.id), def.is_integer}; + return {ctx.OpLoad(def.image_type, def.id), def.numeric_type}; } } @@ -461,8 +481,9 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& if (ctx.stage == Stage::Fragment) { const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc, offset); + const Id result_type{GetResultType(ctx, GetTextureNumericType(ctx, info))}; return Emit(&EmitContext::OpImageSparseSampleImplicitLod, - &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4], + &EmitContext::OpImageSampleImplicitLod, ctx, inst, result_type, Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); } else { // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as @@ -470,8 +491,9 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& // derivatives const Id lod{ctx.Const(0.0f)}; const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset); + const Id result_type{GetResultType(ctx, GetTextureNumericType(ctx, info))}; return Emit(&EmitContext::OpImageSparseSampleExplicitLod, - &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], + &EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type, Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); } } @@ -480,12 +502,14 @@ Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& Id lod, const IR::Value& offset) { const auto info{inst->Flags()}; const ImageOperands operands(ctx, false, true, false, lod, offset); + const NumericType numeric_type{GetTextureNumericType(ctx, info)}; + const Id result_type{GetResultType(ctx, numeric_type)}; Id result = Emit(&EmitContext::OpImageSparseSampleExplicitLod, - &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], + &EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type, Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); #ifdef ANDROID - if (Settings::values.fix_bloom_effects.GetValue()) { + if (numeric_type == NumericType::Float && Settings::values.fix_bloom_effects.GetValue()) { result = ctx.OpVectorTimesScalar(ctx.F32[4], result, ctx.Const(0.98f)); } #endif @@ -529,8 +553,9 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id if (ctx.profile.need_gather_subpixel_offset) { coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords); } + const Id result_type{GetResultType(ctx, GetTextureNumericType(ctx, info))}; return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, - ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component), + result_type, Texture(ctx, info, index), coords, ctx.Const(info.gather_component), operands.MaskOptional(), operands.Span()); } @@ -558,8 +583,10 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c lod = Id{}; } const ImageOperands operands(lod, ms); - return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], - TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); + const Id result_type{GetResultType(ctx, GetTextureNumericType(ctx, info))}; + return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, + result_type, TextureImage(ctx, info, index), coords, operands.MaskOptional(), + operands.Span()); } Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, @@ -609,8 +636,9 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I ctx.Def(offset), {}, lod_clamp) : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset, lod_clamp); + const Id result_type{GetResultType(ctx, GetTextureNumericType(ctx, info))}; return Emit(&EmitContext::OpImageSparseSampleExplicitLod, - &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], + &EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type, Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); } @@ -620,11 +648,11 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host"); return ctx.ConstantNull(ctx.U32[4]); } - const auto [image, is_integer] = Image(ctx, index, info); - const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]}; + const auto [image, numeric_type] = Image(ctx, index, info); + const Id result_type{GetResultType(ctx, numeric_type)}; Id color{Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, result_type, image, coords, std::nullopt, std::span{})}; - if (!is_integer) { + if (numeric_type == NumericType::Float) { color = ctx.OpBitcast(ctx.U32[4], color); } return color; @@ -632,8 +660,8 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) { const auto info{inst->Flags()}; - const auto [image, is_integer] = Image(ctx, index, info); - if (!is_integer) { + const auto [image, numeric_type] = Image(ctx, index, info); + if (numeric_type == NumericType::Float) { color = ctx.OpBitcast(ctx.F32[4], color); } ctx.OpImageWrite(image, coords, color); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index b9a24496c9..fb66a7962e 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -28,9 +28,21 @@ enum class Operation { FPMax, }; +Id GetNumericTypeId(EmitContext& ctx, NumericType numeric_type) { + switch (numeric_type) { + case NumericType::Float: + return ctx.F32[1]; + case NumericType::SignedInt: + return ctx.S32[1]; + case NumericType::UnsignedInt: + return ctx.U32[1]; + } + throw InvalidArgument("Invalid numeric type {}", static_cast(numeric_type)); +} + Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { const spv::ImageFormat format{spv::ImageFormat::Unknown}; - const Id type{ctx.F32[1]}; + const Id type{GetNumericTypeId(ctx, desc.numeric_type)}; const bool depth{desc.is_depth}; const bool ms{desc.is_multisample}; switch (desc.type) { @@ -1304,22 +1316,26 @@ void EmitContext::DefineTextureBuffers(const Info& info, u32& binding) { if (info.texture_buffer_descriptors.empty()) { return; } - const spv::ImageFormat format{spv::ImageFormat::Unknown}; - image_buffer_type = TypeImage(F32[1], spv::Dim::Buffer, 0U, false, false, 1, format); - - const Id type{TypePointer(spv::StorageClass::UniformConstant, image_buffer_type)}; texture_buffers.reserve(info.texture_buffer_descriptors.size()); for (const TextureBufferDescriptor& desc : info.texture_buffer_descriptors) { if (desc.count != 1) { throw NotImplementedException("Array of texture buffers"); } + const spv::ImageFormat format{spv::ImageFormat::Unknown}; + const Id image_type{ + TypeImage(GetNumericTypeId(*this, desc.numeric_type), spv::Dim::Buffer, 0U, false, + false, 1, format)}; + const Id type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; const Id id{AddGlobalVariable(type, spv::StorageClass::UniformConstant)}; Decorate(id, spv::Decoration::Binding, binding); Decorate(id, spv::Decoration::DescriptorSet, 0U); Name(id, NameOf(stage, desc, "texbuf")); texture_buffers.push_back({ .id = id, + .image_type = image_type, + .pointer_type = type, .count = desc.count, + .numeric_type = desc.numeric_type, }); if (profile.supported_spirv >= 0x00010400) { interfaces.push_back(id); @@ -1332,7 +1348,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) { image_buffers.reserve(info.image_buffer_descriptors.size()); for (const ImageBufferDescriptor& desc : info.image_buffer_descriptors) { const spv::ImageFormat format{GetImageFormat(desc.format)}; - const Id sampled_type{desc.is_integer ? U32[1] : F32[1]}; + const Id sampled_type{GetNumericTypeId(*this, desc.numeric_type)}; const Id image_type{ TypeImage(sampled_type, spv::Dim::Buffer, false, false, false, 2, format)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; @@ -1345,7 +1361,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) { .image_type = image_type, .pointer_type = pointer_type, .count = desc.count, - .is_integer = desc.is_integer, + .numeric_type = desc.numeric_type, }); if (profile.supported_spirv >= 0x00010400) { interfaces.push_back(id); @@ -1372,6 +1388,7 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in .image_type = image_type, .count = desc.count, .is_multisample = desc.is_multisample, + .numeric_type = desc.numeric_type, }); if (profile.supported_spirv >= 0x00010400) { interfaces.push_back(id); @@ -1387,7 +1404,7 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_index) { images.reserve(info.image_descriptors.size()); for (const ImageDescriptor& desc : info.image_descriptors) { - const Id sampled_type{desc.is_integer ? U32[1] : F32[1]}; + const Id sampled_type{GetNumericTypeId(*this, desc.numeric_type)}; const Id image_type{ImageType(*this, desc, sampled_type)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; @@ -1399,7 +1416,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde .image_type = image_type, .pointer_type = pointer_type, .count = desc.count, - .is_integer = desc.is_integer, + .numeric_type = desc.numeric_type, }); if (profile.supported_spirv >= 0x00010400) { interfaces.push_back(id); @@ -1671,8 +1688,10 @@ void EmitContext::DefineOutputs(const IR::Program& program) { case Stage::Fragment: for (u32 index = 0; index < 8; ++index) { const bool need_dual_source = runtime_info.dual_source_blend && index <= 1; - if (!need_dual_source && !info.stores_frag_color[index] && - !profile.need_declared_frag_colors) { + const bool should_declare = runtime_info.active_color_outputs[index] && + (info.stores_frag_color[index] || + profile.need_declared_frag_colors); + if (!need_dual_source && !should_declare) { continue; } const Id type{GetAttributeType(*this, runtime_info.color_output_types[index])}; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index de56809a98..396022eddf 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -41,11 +41,15 @@ struct TextureDefinition { Id image_type; u32 count; bool is_multisample; + NumericType numeric_type; }; struct TextureBufferDefinition { Id id; + Id image_type; + Id pointer_type; u32 count; + NumericType numeric_type; }; struct ImageBufferDefinition { @@ -53,7 +57,7 @@ struct ImageBufferDefinition { Id image_type; Id pointer_type; u32 count; - bool is_integer; + NumericType numeric_type; }; struct ImageDefinition { @@ -61,7 +65,7 @@ struct ImageDefinition { Id image_type; Id pointer_type; u32 count; - bool is_integer; + NumericType numeric_type; }; struct UniformDefinitions { diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index f156192c13..f52a3e72de 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -5,10 +5,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include +#include #include #include +#include "common/logging/log.h" #include "common/settings.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/frontend/ir/basic_block.h" @@ -22,6 +25,214 @@ namespace Shader::Maxwell { namespace { +struct FpControlHistogram { + std::array total{}; + std::array no_contraction{}; + std::array, 2> rounding{}; + std::array, 2> fmz{}; + std::array, 5>, 2> combos{}; +}; + +[[nodiscard]] constexpr std::string_view StageName(Stage stage) noexcept { + switch (stage) { + case Stage::VertexA: + return "VertexA"; + case Stage::VertexB: + return "VertexB"; + case Stage::TessellationControl: + return "TessellationControl"; + case Stage::TessellationEval: + return "TessellationEval"; + case Stage::Geometry: + return "Geometry"; + case Stage::Fragment: + return "Fragment"; + case Stage::Compute: + return "Compute"; + } + return "Unknown"; +} + +[[nodiscard]] constexpr std::string_view RoundingName(IR::FpRounding rounding) noexcept { + switch (rounding) { + case IR::FpRounding::DontCare: + return "DontCare"; + case IR::FpRounding::RN: + return "RN"; + case IR::FpRounding::RM: + return "RM"; + case IR::FpRounding::RP: + return "RP"; + case IR::FpRounding::RZ: + return "RZ"; + } + return "Unknown"; +} + +[[nodiscard]] constexpr std::string_view FmzName(IR::FmzMode fmz_mode) noexcept { + switch (fmz_mode) { + case IR::FmzMode::DontCare: + return "DontCare"; + case IR::FmzMode::FTZ: + return "FTZ"; + case IR::FmzMode::FMZ: + return "FMZ"; + case IR::FmzMode::None: + return "None"; + } + return "Unknown"; +} + +[[nodiscard]] constexpr std::optional FpControlBucket(const IR::Opcode opcode) noexcept { + switch (opcode) { + case IR::Opcode::FPAdd16: + case IR::Opcode::FPFma16: + case IR::Opcode::FPMul16: + case IR::Opcode::FPRoundEven16: + case IR::Opcode::FPFloor16: + case IR::Opcode::FPCeil16: + case IR::Opcode::FPTrunc16: + return 0; + case IR::Opcode::FPAdd32: + case IR::Opcode::FPFma32: + case IR::Opcode::FPMul32: + case IR::Opcode::FPRoundEven32: + case IR::Opcode::FPFloor32: + case IR::Opcode::FPCeil32: + case IR::Opcode::FPTrunc32: + case IR::Opcode::FPOrdEqual32: + case IR::Opcode::FPUnordEqual32: + case IR::Opcode::FPOrdNotEqual32: + case IR::Opcode::FPUnordNotEqual32: + case IR::Opcode::FPOrdLessThan32: + case IR::Opcode::FPUnordLessThan32: + case IR::Opcode::FPOrdGreaterThan32: + case IR::Opcode::FPUnordGreaterThan32: + case IR::Opcode::FPOrdLessThanEqual32: + case IR::Opcode::FPUnordLessThanEqual32: + case IR::Opcode::FPOrdGreaterThanEqual32: + case IR::Opcode::FPUnordGreaterThanEqual32: + case IR::Opcode::ConvertF16F32: + case IR::Opcode::ConvertF64F32: + return 1; + default: + return std::nullopt; + } +} + +FpControlHistogram CollectFpControlHistogram(const IR::Program& program) { + FpControlHistogram histogram{}; + for (const IR::Block* const block : program.post_order_blocks) { + for (const IR::Inst& inst : block->Instructions()) { + const std::optional bucket{FpControlBucket(inst.GetOpcode())}; + if (!bucket) { + continue; + } + const auto flags{inst.Flags()}; + ++histogram.total[*bucket]; + if (flags.no_contraction) { + ++histogram.no_contraction[*bucket]; + } + ++histogram.rounding[*bucket][static_cast(flags.rounding)]; + ++histogram.fmz[*bucket][static_cast(flags.fmz_mode)]; + ++histogram.combos[*bucket][static_cast(flags.rounding)] + [static_cast(flags.fmz_mode)]; + } + } + return histogram; +} + +void LogRzFpControlTrace(Environment& env, const IR::Program& program) { + std::array totals{}; + for (const IR::Block* const block : program.post_order_blocks) { + for (const IR::Inst& inst : block->Instructions()) { + const std::optional bucket{FpControlBucket(inst.GetOpcode())}; + if (!bucket) { + continue; + } + const auto flags{inst.Flags()}; + if (flags.rounding != IR::FpRounding::RZ) { + continue; + } + ++totals[*bucket]; + } + } + + if (totals[0] == 0 && totals[1] == 0) { + return; + } + + constexpr std::array precision_names{"fp16", "fp32"}; + LOG_INFO(Shader, + "FP_RZ {} shader start={:#010x} blocks={} post_order_blocks={} fp16={} fp32={}", + StageName(program.stage), env.StartAddress(), program.blocks.size(), + program.post_order_blocks.size(), totals[0], totals[1]); + + for (const IR::Block* const block : program.post_order_blocks) { + u32 inst_index{}; + for (const IR::Inst& inst : block->Instructions()) { + const std::optional bucket{FpControlBucket(inst.GetOpcode())}; + if (!bucket) { + ++inst_index; + continue; + } + const auto flags{inst.Flags()}; + if (flags.rounding != IR::FpRounding::RZ) { + ++inst_index; + continue; + } + LOG_INFO(Shader, + "FP_RZ {} start={:#010x} block_order={} inst_index={} precision={} opcode={} no_contraction={} fmz={}", + StageName(program.stage), env.StartAddress(), block->GetOrder(), inst_index, + precision_names[*bucket], inst.GetOpcode(), flags.no_contraction, + FmzName(flags.fmz_mode)); + ++inst_index; + } + } +} + +void LogFpControlHistogram(const IR::Program& program) { + const FpControlHistogram histogram{CollectFpControlHistogram(program)}; + if (histogram.total[0] == 0 && histogram.total[1] == 0) { + return; + } + + LOG_INFO(Shader, "FP_HIST {} shader blocks={} post_order_blocks={}", + StageName(program.stage), program.blocks.size(), program.post_order_blocks.size()); + + constexpr std::array precision_names{"fp16", "fp32"}; + for (size_t bucket = 0; bucket < precision_names.size(); ++bucket) { + if (histogram.total[bucket] == 0) { + continue; + } + + LOG_INFO(Shader, + "FP_HIST {} total={} no_contraction={} rounding[DontCare={}, RN={}, RM={}, RP={}, RZ={}] fmz[DontCare={}, FTZ={}, FMZ={}, None={}]", + precision_names[bucket], histogram.total[bucket], histogram.no_contraction[bucket], + histogram.rounding[bucket][static_cast(IR::FpRounding::DontCare)], + histogram.rounding[bucket][static_cast(IR::FpRounding::RN)], + histogram.rounding[bucket][static_cast(IR::FpRounding::RM)], + histogram.rounding[bucket][static_cast(IR::FpRounding::RP)], + histogram.rounding[bucket][static_cast(IR::FpRounding::RZ)], + histogram.fmz[bucket][static_cast(IR::FmzMode::DontCare)], + histogram.fmz[bucket][static_cast(IR::FmzMode::FTZ)], + histogram.fmz[bucket][static_cast(IR::FmzMode::FMZ)], + histogram.fmz[bucket][static_cast(IR::FmzMode::None)]); + + for (size_t rounding = 0; rounding < histogram.combos[bucket].size(); ++rounding) { + for (size_t fmz = 0; fmz < histogram.combos[bucket][rounding].size(); ++fmz) { + const u32 count{histogram.combos[bucket][rounding][fmz]}; + if (count == 0) { + continue; + } + LOG_INFO(Shader, "FP_HIST {} combo {} / {} = {}", precision_names[bucket], + RoundingName(static_cast(rounding)), + FmzName(static_cast(fmz)), count); + } + } + } +} + IR::BlockList GenerateBlocks(const IR::AbstractSyntaxList& syntax_list) { size_t num_syntax_blocks{}; for (const auto& node : syntax_list) { @@ -315,6 +526,11 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool; constexpr u32 DESCRIPTOR_SIZE = 8; constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast(std::countr_zero(DESCRIPTOR_SIZE)); +NumericType GetNumericType(TexturePixelFormat format) { + const auto pixel_format = static_cast(format); + if (!VideoCore::Surface::IsPixelFormatInteger(pixel_format)) { + return NumericType::Float; + } + return VideoCore::Surface::IsPixelFormatSignedInteger(pixel_format) + ? NumericType::SignedInt + : NumericType::UnsignedInt; +} + IR::Opcode IndexedInstruction(const IR::Inst& inst) { switch (inst.GetOpcode()) { case IR::Opcode::BindlessImageSampleImplicitLod: @@ -199,11 +210,6 @@ static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env, const ConstBufferAddr& cbuf) { return env.ReadTexturePixelFormat(GetTextureHandleCached(env, cbuf)); } -static inline bool IsTexturePixelFormatIntegerCached(Environment& env, - const ConstBufferAddr& cbuf) { - return env.IsTexturePixelFormatInteger(GetTextureHandleCached(env, cbuf)); -} - std::optional Track(const IR::Value& value, Environment& env); static inline std::optional TrackCached(const IR::Value& v, Environment& env) { @@ -430,7 +436,8 @@ public: u32 Add(const TextureBufferDescriptor& desc) { return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) { - return desc.cbuf_index == existing.cbuf_index && + return desc.numeric_type == existing.numeric_type && + desc.cbuf_index == existing.cbuf_index && desc.cbuf_offset == existing.cbuf_offset && desc.shift_left == existing.shift_left && desc.secondary_cbuf_index == existing.secondary_cbuf_index && @@ -449,13 +456,13 @@ public: })}; image_buffer_descriptors[index].is_written |= desc.is_written; image_buffer_descriptors[index].is_read |= desc.is_read; - image_buffer_descriptors[index].is_integer |= desc.is_integer; return index; } u32 Add(const TextureDescriptor& desc) { const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) { return desc.type == existing.type && desc.is_depth == existing.is_depth && + desc.numeric_type == existing.numeric_type && desc.has_secondary == existing.has_secondary && desc.cbuf_index == existing.cbuf_index && desc.cbuf_offset == existing.cbuf_offset && @@ -479,7 +486,6 @@ public: })}; image_descriptors[index].is_written |= desc.is_written; image_descriptors[index].is_read |= desc.is_read; - image_descriptors[index].is_integer |= desc.is_integer; return index; } @@ -651,13 +657,13 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo } const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead}; const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite}; - const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)}; + const NumericType numeric_type{GetNumericType(ReadTexturePixelFormatCached(env, cbuf))}; if (flags.type == TextureType::Buffer) { index = descriptors.Add(ImageBufferDescriptor{ .format = flags.image_format, .is_written = is_written, .is_read = is_read, - .is_integer = is_integer, + .numeric_type = numeric_type, .cbuf_index = cbuf.index, .cbuf_offset = cbuf.offset, .count = cbuf.count, @@ -669,7 +675,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo .format = flags.image_format, .is_written = is_written, .is_read = is_read, - .is_integer = is_integer, + .numeric_type = numeric_type, .cbuf_index = cbuf.index, .cbuf_offset = cbuf.offset, .count = cbuf.count, @@ -681,6 +687,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo default: if (flags.type == TextureType::Buffer) { index = descriptors.Add(TextureBufferDescriptor{ + .numeric_type = GetNumericType(ReadTexturePixelFormatCached(env, cbuf)), .has_secondary = cbuf.has_secondary, .cbuf_index = cbuf.index, .cbuf_offset = cbuf.offset, @@ -696,6 +703,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo .type = flags.type, .is_depth = flags.is_depth != 0, .is_multisample = is_multisample, + .numeric_type = GetNumericType(ReadTexturePixelFormatCached(env, cbuf)), .has_secondary = cbuf.has_secondary, .cbuf_index = cbuf.index, .cbuf_offset = cbuf.offset, diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index be10a9bb08..b8888504bb 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -111,6 +111,9 @@ struct RuntimeInfo { /// Output types for each color attachment std::array color_output_types{}; + /// Fragment color outputs that are active for the current pipeline. + std::array active_color_outputs{true, true, true, true, true, true, true, true}; + /// Dual source blending bool dual_source_blend{}; }; diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index dfacc06802..87dd14fa46 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -38,6 +38,12 @@ enum class TextureType : u32 { }; constexpr u32 NUM_TEXTURE_TYPES = 9; +enum class NumericType : u8 { + Float, + SignedInt, + UnsignedInt, +}; + enum class TexturePixelFormat { A8B8G8R8_UNORM, A8B8G8R8_SNORM, @@ -177,6 +183,7 @@ struct StorageBufferDescriptor { }; struct TextureBufferDescriptor { + NumericType numeric_type; bool has_secondary; u32 cbuf_index; u32 cbuf_offset; @@ -195,7 +202,7 @@ struct ImageBufferDescriptor { ImageFormat format; bool is_written; bool is_read; - bool is_integer; + NumericType numeric_type; u32 cbuf_index; u32 cbuf_offset; u32 count; @@ -209,6 +216,7 @@ struct TextureDescriptor { TextureType type; bool is_depth; bool is_multisample; + NumericType numeric_type; bool has_secondary; u32 cbuf_index; u32 cbuf_offset; @@ -228,7 +236,7 @@ struct ImageDescriptor { ImageFormat format; bool is_written; bool is_read; - bool is_integer; + NumericType numeric_type; u32 cbuf_index; u32 cbuf_offset; u32 count; diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 07611ef98c..be3de9bdf4 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -56,16 +56,23 @@ namespace { [[nodiscard]] VkImageSubresourceRange SubresourceRangeFromView(const ImageView& image_view) { auto range = image_view.range; + const bool is_3d_image = image_view.type == VideoCommon::ImageViewType::e3D || + (image_view.flags & VideoCommon::ImageViewFlagBits::Slice) != + VideoCommon::ImageViewFlagBits{}; if ((image_view.flags & VideoCommon::ImageViewFlagBits::Slice) != VideoCommon::ImageViewFlagBits{}) { range.base.layer = 0; range.extent.layers = 1; } + u32 layer_count = static_cast(range.extent.layers); + if (is_3d_image && layer_count == 1) { + layer_count = VK_REMAINING_ARRAY_LAYERS; + } return VkImageSubresourceRange{ .aspectMask = AspectMaskFromFormat(image_view.format), .baseMipLevel = static_cast(range.base.level), .levelCount = static_cast(range.extent.levels), .baseArrayLayer = static_cast(range.base.layer), - .layerCount = static_cast(range.extent.layers), + .layerCount = layer_count, }; } @@ -170,8 +177,14 @@ constexpr VkPipelineMultisampleStateCreateInfo PIPELINE_MULTISAMPLE_STATE_CREATE .alphaToCoverageEnable = VK_FALSE, .alphaToOneEnable = VK_FALSE, }; -constexpr std::array DYNAMIC_STATES{VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, - VK_DYNAMIC_STATE_BLEND_CONSTANTS}; +constexpr std::array DYNAMIC_STATES{ + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_BLEND_CONSTANTS, + VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, + VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, + VK_DYNAMIC_STATE_STENCIL_REFERENCE, +}; constexpr VkPipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 06cbd9e6da..fa25d99016 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -50,6 +50,38 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& }); state.varyings = regs.stream_out_layout; } + +Maxwell::PrimitiveTopology NormalizeDynamicTopologyClass(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Maxwell::PrimitiveTopology::Points; + + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineStrip: + return Maxwell::PrimitiveTopology::Lines; + + case Maxwell::PrimitiveTopology::Triangles: + case Maxwell::PrimitiveTopology::TriangleStrip: + case Maxwell::PrimitiveTopology::TriangleFan: + case Maxwell::PrimitiveTopology::Quads: + case Maxwell::PrimitiveTopology::QuadStrip: + case Maxwell::PrimitiveTopology::Polygon: + case Maxwell::PrimitiveTopology::LineLoop: + return Maxwell::PrimitiveTopology::Triangles; + + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return Maxwell::PrimitiveTopology::LinesAdjacency; + + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return Maxwell::PrimitiveTopology::TrianglesAdjacency; + + case Maxwell::PrimitiveTopology::Patches: + return Maxwell::PrimitiveTopology::Patches; + } + return topology; +} } // Anonymous namespace void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) { @@ -60,9 +92,9 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0); extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0); extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0); - extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0); - extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0); - dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0); + reserved_dynamic_state_3_blend.Assign(0); + reserved_dynamic_state_3_enables.Assign(0); + reserved_bit_5.Assign(0); xfb_enabled.Assign(regs.transform_feedback_enabled != 0); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front)); @@ -71,7 +103,9 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == Maxwell::Tessellation::OutputPrimitives::Triangles_CW); patch_control_points_minus_one.Assign(regs.patch_vertices - 1); - topology.Assign(topology_); + const bool can_normalize_topology = + features.has_extended_dynamic_state && features.has_extended_dynamic_state_2; + topology.Assign(can_normalize_topology ? NormalizeDynamicTopologyClass(topology_) : topology_); msaa_mode.Assign(regs.anti_alias_samples_mode); raw2 = 0; @@ -103,43 +137,22 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe point_size = std::bit_cast(regs.point_size); if (maxwell3d.dirty.flags[Dirty::VertexInput]) { - if (features.has_dynamic_vertex_input) { - // Dirty flag will be reset by the command buffer update - static constexpr std::array LUT{ - 0u, // Invalid - 1u, // SignedNorm - 1u, // UnsignedNorm - 2u, // SignedInt - 3u, // UnsignedInt - 1u, // UnsignedScaled - 1u, // SignedScaled - 1u, // Float - }; - const auto& attrs = regs.vertex_attrib_format; - attribute_types = 0; - for (size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { - const u32 mask = attrs[i].constant != 0 ? 0 : 3; - const u32 type = LUT[static_cast(attrs[i].type.Value())]; - attribute_types |= static_cast(type & mask) << (i * 2); - } - } else { - maxwell3d.dirty.flags[Dirty::VertexInput] = false; - enabled_divisors = 0; - for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { - const bool is_enabled = regs.vertex_stream_instances.IsInstancingEnabled(index); - binding_divisors[index] = is_enabled ? regs.vertex_streams[index].frequency : 0; - enabled_divisors |= (is_enabled ? u64{1} : 0) << index; - } - for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { - const auto& input = regs.vertex_attrib_format[index]; - auto& attribute = attributes[index]; - attribute.raw = 0; - attribute.enabled.Assign(input.constant ? 0 : 1); - attribute.buffer.Assign(input.buffer); - attribute.offset.Assign(input.offset); - attribute.type.Assign(static_cast(input.type.Value())); - attribute.size.Assign(static_cast(input.size.Value())); - } + maxwell3d.dirty.flags[Dirty::VertexInput] = false; + enabled_divisors = 0; + for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { + const bool is_enabled = regs.vertex_stream_instances.IsInstancingEnabled(index); + binding_divisors[index] = is_enabled ? regs.vertex_streams[index].frequency : 0; + enabled_divisors |= (is_enabled ? u64{1} : 0) << index; + } + for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { + const auto& input = regs.vertex_attrib_format[index]; + auto& attribute = attributes[index]; + attribute.raw = 0; + attribute.enabled.Assign(input.constant ? 0 : 1); + attribute.buffer.Assign(input.buffer); + attribute.offset.Assign(input.offset); + attribute.type.Assign(static_cast(input.type.Value())); + attribute.size.Assign(static_cast(input.size.Value())); } } if (maxwell3d.dirty.flags[Dirty::ViewportSwizzles]) { @@ -153,24 +166,20 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe dynamic_state.raw2 = 0; if (!extended_dynamic_state) { dynamic_state.Refresh(regs); - std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { - return static_cast(array.stride.Value()); - }); } - if (!extended_dynamic_state_2_logic_op) { + std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { + return static_cast(array.stride.Value()); + }); + if (!extended_dynamic_state_2) { dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2); } - if (!extended_dynamic_state_3_blend) { - if (maxwell3d.dirty.flags[Dirty::Blending]) { - maxwell3d.dirty.flags[Dirty::Blending] = false; - for (size_t index = 0; index < attachments.size(); ++index) { - attachments[index].Refresh(regs, index); - } + if (maxwell3d.dirty.flags[Dirty::Blending]) { + maxwell3d.dirty.flags[Dirty::Blending] = false; + for (size_t index = 0; index < attachments.size(); ++index) { + attachments[index].Refresh(regs, index); } } - if (!extended_dynamic_state_3_enables) { - dynamic_state.Refresh3(regs); - } + dynamic_state.Refresh3(regs); if (xfb_enabled) { RefreshXfbState(xfb_state, regs); } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index c5bc14f448..030c62a883 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -25,9 +25,6 @@ struct DynamicFeatures { bool has_extended_dynamic_state_2; bool has_extended_dynamic_state_2_logic_op; bool has_extended_dynamic_state_2_patch_control_points; - bool has_extended_dynamic_state_3_blend; - bool has_extended_dynamic_state_3_enables; - bool has_dynamic_vertex_input; }; struct FixedPipelineState { @@ -191,9 +188,9 @@ struct FixedPipelineState { BitField<0, 1, u32> extended_dynamic_state; BitField<1, 1, u32> extended_dynamic_state_2; BitField<2, 1, u32> extended_dynamic_state_2_logic_op; - BitField<3, 1, u32> extended_dynamic_state_3_blend; - BitField<4, 1, u32> extended_dynamic_state_3_enables; - BitField<5, 1, u32> dynamic_vertex_input; + BitField<3, 1, u32> reserved_dynamic_state_3_blend; + BitField<4, 1, u32> reserved_dynamic_state_3_enables; + BitField<5, 1, u32> reserved_bit_5; BitField<6, 1, u32> xfb_enabled; BitField<7, 1, u32> ndc_minus_one_to_one; BitField<8, 2, u32> polygon_mode; @@ -225,10 +222,7 @@ struct FixedPipelineState { u32 point_size; std::array viewport_swizzles; - union { - u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state - u64 enabled_divisors; - }; + u64 enabled_divisors; DynamicState dynamic_state; std::array attachments; @@ -260,14 +254,6 @@ struct FixedPipelineState { // When transform feedback is enabled, use the whole struct return sizeof(*this); } - if (dynamic_vertex_input && extended_dynamic_state_3_blend) { - // Exclude dynamic state and attributes - return offsetof(FixedPipelineState, dynamic_state); - } - if (dynamic_vertex_input) { - // Exclude dynamic state - return offsetof(FixedPipelineState, attributes); - } if (extended_dynamic_state) { // Exclude dynamic state return offsetof(FixedPipelineState, vertex_strides); @@ -275,10 +261,6 @@ struct FixedPipelineState { // Default return offsetof(FixedPipelineState, xfb_state); } - - u32 DynamicAttributeType(size_t index) const noexcept { - return (attribute_types >> (index * 2)) & 0b11; - } }; static_assert(std::has_unique_object_representations_v); static_assert(std::is_trivially_copyable_v); diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 024c72b38e..0538102e4a 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index e88b27b273..8d085f4541 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -30,23 +30,58 @@ public: return device->IsKhrPushDescriptorSupported() && num_descriptors <= device->MaxPushDescriptors(); } - - // TODO(crueter): utilize layout binding flags + vk::DescriptorSetLayout CreateDescriptorSetLayout(bool use_push_descriptor) const { if (bindings.empty()) { return nullptr; } + + variable_descriptor_count = 0; + binding_flags.clear(); + + VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags_ci{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .pNext = nullptr, + .bindingCount = 0, + .pBindingFlags = nullptr, + }; + + const bool use_descriptor_indexing = + !use_push_descriptor && device->isExtDescriptorIndexingSupported(); + const void* layout_next = nullptr; + if (use_descriptor_indexing) { + binding_flags.assign(bindings.size(), 0); + for (size_t i = 0; i < bindings.size(); ++i) { + if (bindings[i].descriptorCount > 1) { + binding_flags[i] |= VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + } + } + + if (bindings.back().descriptorCount > 1) { + binding_flags.back() |= VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT; + variable_descriptor_count = bindings.back().descriptorCount; + } + + binding_flags_ci.bindingCount = static_cast(binding_flags.size()); + binding_flags_ci.pBindingFlags = binding_flags.data(); + layout_next = &binding_flags_ci; + } + const VkDescriptorSetLayoutCreateFlags flags = use_push_descriptor ? VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR : 0; return device->GetLogical().CreateDescriptorSetLayout({ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = nullptr, + .pNext = layout_next, .flags = flags, .bindingCount = static_cast(bindings.size()), .pBindings = bindings.data(), }); } + u32 VariableDescriptorCount() const noexcept { + return variable_descriptor_count; + } + vk::DescriptorUpdateTemplate CreateTemplate(VkDescriptorSetLayout descriptor_set_layout, VkPipelineLayout pipeline_layout, bool use_push_descriptor) const { @@ -134,8 +169,10 @@ private: bool is_compute{}; boost::container::small_vector bindings; boost::container::small_vector entries; + mutable boost::container::small_vector binding_flags; u32 binding{}; u32 num_descriptors{}; + mutable u32 variable_descriptor_count{}; size_t offset{}; }; diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index a2c4727703..383e4205c1 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -461,6 +461,9 @@ static vk::Pipeline CreateWrappedPipelineImpl( constexpr std::array dynamic_states{ VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, + VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, + VK_DYNAMIC_STATE_STENCIL_REFERENCE, }; const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index f4345262fb..05fce08a42 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -346,7 +346,7 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; if (limit_dynamic_storage_buffers) { max_dynamic_storage_buffers = device.GetMaxDescriptorSetStorageBuffersDynamic(); - } + } if (device.SupportsUint8Indices()) { uint8_pass = std::make_unique(device, scheduler, descriptor_pool, staging_pool, compute_pass_descriptor_queue); @@ -449,8 +449,16 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, } cmdbuf.CopyBuffer(src_buffer, dst_buffer, VideoCommon::FixSmallVectorADL(vk_copies)); if (barrier) { + // Buffer reads can go to vertex input, shaders, or compute + const VkPipelineStageFlags dst_stages = + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER); + dst_stages, 0, WRITE_BARRIER); } }); } @@ -478,7 +486,14 @@ void BufferCacheRuntime::PostCopyBarrier() { }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([](vk::CommandBuffer cmdbuf) { - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + const VkPipelineStageFlags dst_stages = + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stages, 0, WRITE_BARRIER); }); } @@ -505,7 +520,15 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); cmdbuf.FillBuffer(dest_buffer, offset, size, value); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + // Buffer reads can go to vertex input, shaders, or compute + const VkPipelineStageFlags dst_stages_clear = + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stages_clear, 0, WRITE_BARRIER); }); } @@ -563,23 +586,14 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset if (index >= device.GetMaxVertexInputBindings()) { return; } - if (device.IsExtExtendedDynamicStateSupported()) { - scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { - const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; - const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE; - const VkDeviceSize vk_stride = stride; - cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); - }); - } else { - if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) { - ReserveNullBuffer(); - buffer = *null_buffer; - offset = 0; - } - scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) { - cmdbuf.BindVertexBuffer(index, buffer, offset); - }); + if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) { + ReserveNullBuffer(); + buffer = *null_buffer; + offset = 0; } + scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) { + cmdbuf.BindVertexBuffer(index, buffer, offset); + }); } void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { @@ -603,22 +617,12 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bi if (binding_count == 0) { return; } - if (device.IsExtExtendedDynamicStateSupported()) { - scheduler.Record([bindings_ = std::move(bindings), - buffer_handles_ = std::move(buffer_handles), - binding_count](vk::CommandBuffer cmdbuf) { - cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(), - bindings_.offsets.data(), bindings_.sizes.data(), - bindings_.strides.data()); - }); - } else { - scheduler.Record([bindings_ = std::move(bindings), - buffer_handles_ = std::move(buffer_handles), - binding_count](vk::CommandBuffer cmdbuf) { - cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(), - bindings_.offsets.data()); - }); - } + scheduler.Record([bindings_ = std::move(bindings), + buffer_handles_ = std::move(buffer_handles), + binding_count](vk::CommandBuffer cmdbuf) { + cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(), + bindings_.offsets.data()); + }); } void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index b73fcd162b..5b0b876364 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -127,6 +127,7 @@ public: void BindVertexBuffers(VideoCommon::HostBindings& bindings); + void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings); @@ -181,7 +182,7 @@ private: vk::Buffer null_buffer; std::unique_ptr uint8_pass; - QuadIndexedPass quad_index_pass; + QuadIndexedPass quad_index_pass; bool limit_dynamic_storage_buffers = false; u32 max_dynamic_storage_buffers = (std::numeric_limits::max)(); diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 22e646afe9..14bd64ff66 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "video_core/renderer_vulkan/vk_texture_cache.h" @@ -237,9 +238,37 @@ ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, vk::Span push_constants, std::span code, std::optional optional_subgroup_size) : device{device_} { + u32 variable_descriptor_count{}; + std::vector binding_flags; + VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags_ci{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .pNext = nullptr, + .bindingCount = 0, + .pBindingFlags = nullptr, + }; + const bool use_descriptor_indexing = device.isExtDescriptorIndexingSupported(); + const void* layout_next = nullptr; + if (use_descriptor_indexing && !bindings.empty()) { + binding_flags.assign(bindings.size(), 0); + for (size_t i = 0; i < bindings.size(); ++i) { + if (bindings[i].descriptorCount > 1) { + binding_flags[i] |= VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + } + } + const size_t last_binding = bindings.size() - 1; + if (bindings[last_binding].descriptorCount > 1) { + binding_flags[last_binding] |= VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT; + variable_descriptor_count = bindings[last_binding].descriptorCount; + } + + binding_flags_ci.bindingCount = static_cast(binding_flags.size()); + binding_flags_ci.pBindingFlags = binding_flags.data(); + layout_next = &binding_flags_ci; + } + descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout({ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = nullptr, + .pNext = layout_next, .flags = 0, .bindingCount = bindings.size(), .pBindings = bindings.data(), @@ -266,7 +295,8 @@ ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, .pipelineLayout = *layout, .set = 0, }); - descriptor_allocator = descriptor_pool.Allocator(*descriptor_set_layout, bank_info); + descriptor_allocator = + descriptor_pool.Allocator(*descriptor_set_layout, bank_info, variable_descriptor_count); } if (code.empty()) { return; diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index 3af9758a31..97f51988bc 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -88,9 +88,10 @@ static void AllocatePool(const Device& device, DescriptorBank& bank) { } DescriptorAllocator::DescriptorAllocator(const Device& device_, MasterSemaphore& master_semaphore_, - DescriptorBank& bank_, VkDescriptorSetLayout layout_) + DescriptorBank& bank_, VkDescriptorSetLayout layout_, + u32 variable_descriptor_count_) : ResourcePool(master_semaphore_, SETS_GROW_RATE), device{&device_}, bank{&bank_}, - layout{layout_} {} + layout{layout_}, variable_descriptor_count{variable_descriptor_count_} {} VkDescriptorSet DescriptorAllocator::Commit() { const size_t index = CommitResource(); @@ -103,9 +104,25 @@ void DescriptorAllocator::Allocate(size_t begin, size_t end) { vk::DescriptorSets DescriptorAllocator::AllocateDescriptors(size_t count) { const std::vector layouts(count, layout); + + std::vector variable_descriptor_counts; + VkDescriptorSetVariableDescriptorCountAllocateInfo variable_descriptor_count_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, + .pNext = nullptr, + .descriptorSetCount = 0, + .pDescriptorCounts = nullptr, + }; + const void* allocate_next = nullptr; + if (variable_descriptor_count != 0) { + variable_descriptor_counts.assign(count, variable_descriptor_count); + variable_descriptor_count_info.descriptorSetCount = static_cast(count); + variable_descriptor_count_info.pDescriptorCounts = variable_descriptor_counts.data(); + allocate_next = &variable_descriptor_count_info; + } + VkDescriptorSetAllocateInfo allocate_info{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .pNext = nullptr, + .pNext = allocate_next, .descriptorPool = *bank->pools.back(), .descriptorSetCount = static_cast(count), .pSetLayouts = layouts.data(), @@ -131,18 +148,22 @@ DescriptorPool::DescriptorPool(const Device& device_, Scheduler& scheduler) DescriptorPool::~DescriptorPool() = default; DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout, - std::span infos) { - return Allocator(layout, MakeBankInfo(infos)); + std::span infos, + u32 variable_descriptor_count) { + return Allocator(layout, MakeBankInfo(infos), variable_descriptor_count); } DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout, - const Shader::Info& info) { - return Allocator(layout, MakeBankInfo(std::array{info})); + const Shader::Info& info, + u32 variable_descriptor_count) { + return Allocator(layout, MakeBankInfo(std::array{info}), variable_descriptor_count); } DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout, - const DescriptorBankInfo& info) { - return DescriptorAllocator(device, master_semaphore, Bank(info), layout); + const DescriptorBankInfo& info, + u32 variable_descriptor_count) { + return DescriptorAllocator(device, master_semaphore, Bank(info), layout, + variable_descriptor_count); } DescriptorBank& DescriptorPool::Bank(const DescriptorBankInfo& reqs) { diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h index 4aada5a006..fed43a45f8 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -47,7 +50,8 @@ public: private: explicit DescriptorAllocator(const Device& device_, MasterSemaphore& master_semaphore_, - DescriptorBank& bank_, VkDescriptorSetLayout layout_); + DescriptorBank& bank_, VkDescriptorSetLayout layout_, + u32 variable_descriptor_count_); void Allocate(size_t begin, size_t end) override; @@ -56,6 +60,7 @@ private: const Device* device{}; DescriptorBank* bank{}; VkDescriptorSetLayout layout{}; + u32 variable_descriptor_count{}; std::vector sets; }; @@ -69,9 +74,12 @@ public: DescriptorPool(const DescriptorPool&) = delete; DescriptorAllocator Allocator(VkDescriptorSetLayout layout, - std::span infos); - DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const Shader::Info& info); - DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const DescriptorBankInfo& info); + std::span infos, + u32 variable_descriptor_count = 0); + DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const Shader::Info& info, + u32 variable_descriptor_count = 0); + DescriptorAllocator Allocator(VkDescriptorSetLayout layout, const DescriptorBankInfo& info, + u32 variable_descriptor_count = 0); private: DescriptorBank& Bank(const DescriptorBankInfo& reqs); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 5b11a34232..0ccad3f24d 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -23,6 +23,7 @@ #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" +#include "video_core/surface.h" #include "video_core/shader_notify.h" #include "video_core/texture_cache/texture_cache.h" #include "video_core/vulkan_common/vulkan_device.h" @@ -273,7 +274,8 @@ GraphicsPipeline::GraphicsPipeline( descriptor_set_layout = builder.CreateDescriptorSetLayout(uses_push_descriptor); if (!uses_push_descriptor) { - descriptor_allocator = descriptor_pool.Allocator(*descriptor_set_layout, stage_infos); + descriptor_allocator = descriptor_pool.Allocator( + *descriptor_set_layout, stage_infos, builder.VariableDescriptorCount()); } const VkDescriptorSetLayout set_layout{*descriptor_set_layout}; @@ -529,6 +531,12 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, } const void* const descriptor_data{guest_descriptor_queue.UpdateData()}; + FixedPipelineState::DynamicState dynamic_state{}; + if (!key.state.extended_dynamic_state) { + dynamic_state = key.state.dynamic_state; + } else { + dynamic_state.raw1 = key.state.dynamic_state.raw1; + } scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), is_rescaling, update_rescaling, uses_render_area = render_area.uses_render_area, @@ -577,38 +585,35 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { static_vector vertex_bindings; static_vector vertex_binding_divisors; static_vector vertex_attributes; - if (!key.state.dynamic_vertex_input) { - const size_t num_vertex_arrays = (std::min)( - Maxwell::NumVertexArrays, static_cast(device.GetMaxVertexInputBindings())); - 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({ + const size_t num_vertex_arrays = + (std::min)(Maxwell::NumVertexArrays, static_cast(device.GetMaxVertexInputBindings())); + 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(index), + .stride = key.state.vertex_strides[index], + .inputRate = rate, + }); + if (instanced) { + vertex_binding_divisors.push_back({ .binding = static_cast(index), - .stride = key.state.vertex_strides[index], - .inputRate = rate, - }); - if (instanced) { - vertex_binding_divisors.push_back({ - .binding = static_cast(index), - .divisor = key.state.binding_divisors[index], - }); - } - } - for (size_t index = 0; index < key.state.attributes.size(); ++index) { - const auto& attribute = key.state.attributes[index]; - if (!attribute.enabled || !stage_infos[0].loads.Generic(index)) { - continue; - } - vertex_attributes.push_back({ - .location = static_cast(index), - .binding = attribute.buffer, - .format = MaxwellToVK::VertexFormat(device, attribute.Type(), attribute.Size()), - .offset = attribute.offset, + .divisor = key.state.binding_divisors[index], }); } } + for (size_t index = 0; index < key.state.attributes.size(); ++index) { + const auto& attribute = key.state.attributes[index]; + if (!attribute.enabled || !stage_infos[0].loads.Generic(index)) { + continue; + } + vertex_attributes.push_back({ + .location = static_cast(index), + .binding = attribute.buffer, + .format = MaxwellToVK::VertexFormat(device, attribute.Type(), attribute.Size()), + .offset = attribute.offset, + }); + } ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes()); VkPipelineVertexInputStateCreateInfo vertex_input_ci{ @@ -687,9 +692,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, - .viewportCount = num_viewports, + .viewportCount = key.state.extended_dynamic_state ? 0u : num_viewports, .pViewports = nullptr, - .scissorCount = num_viewports, + .scissorCount = key.state.extended_dynamic_state ? 0u : num_viewports, .pScissors = nullptr, }; if (device.IsNvViewportSwizzleSupported()) { @@ -716,20 +721,62 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .depthBiasClamp = 0.0f, .depthBiasSlopeFactor = 0.0f, .lineWidth = 1.0f, - // TODO(alekpop): Transfer from regs }; - const bool smooth_lines_supported = - device.IsExtLineRasterizationSupported() && device.SupportsSmoothLines(); - const bool stippled_lines_supported = - device.IsExtLineRasterizationSupported() && device.SupportsStippledRectangularLines(); + const bool line_rasterization_supported = device.IsExtLineRasterizationSupported(); + const bool any_stippled_lines_supported = + line_rasterization_supported && + (device.SupportsStippledRectangularLines() || device.SupportsStippledBresenhamLines() || + device.SupportsStippledSmoothLines()); + const bool line_stipple_dynamic_state_supported = + IsLine(input_assembly_topology) && any_stippled_lines_supported; + const bool supports_rectangular_lines = + line_rasterization_supported && device.SupportsRectangularLines(); + const bool supports_bresenham_lines = + line_rasterization_supported && device.SupportsBresenhamLines(); + const bool supports_smooth_lines = line_rasterization_supported && device.SupportsSmoothLines(); + + VkLineRasterizationModeEXT line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT; + if (line_rasterization_supported) { + if (key.state.smooth_lines != 0) { + if (supports_smooth_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT; + } else if (supports_rectangular_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; + } else if (supports_bresenham_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT; + } + } else { + if (supports_rectangular_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; + } else if (supports_bresenham_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT; + } else if (supports_smooth_lines) { + line_rasterization_mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT; + } + } + } + + const bool stippled_lines_supported = [&]() { + if (!line_rasterization_supported || !dynamic.line_stipple_enable) { + return false; + } + switch (line_rasterization_mode) { + case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT: + return device.SupportsStippledRectangularLines(); + case VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT: + return device.SupportsStippledBresenhamLines(); + case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT: + return device.SupportsStippledSmoothLines(); + default: + return false; + } + }(); + VkPipelineRasterizationLineStateCreateInfoEXT line_state{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT, .pNext = nullptr, - .lineRasterizationMode = key.state.smooth_lines != 0 && smooth_lines_supported - ? VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT - : VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT, - .stippledLineEnable = - (dynamic.line_stipple_enable && stippled_lines_supported) ? VK_TRUE : VK_FALSE, + .lineRasterizationMode = line_rasterization_mode, + .stippledLineEnable = stippled_lines_supported ? VK_TRUE : VK_FALSE, .lineStippleFactor = key.state.line_stipple_factor, .lineStipplePattern = static_cast(key.state.line_stipple_pattern), }; @@ -737,15 +784,31 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT, .pNext = nullptr, .flags = 0, - .conservativeRasterizationMode = key.state.conservative_raster_enable != 0 - ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT - : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, + .conservativeRasterizationMode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, .extraPrimitiveOverestimationSize = 0.0f, }; + const bool conservative_requested = key.state.conservative_raster_enable != 0; + if (conservative_requested) { + const bool is_point_topology = input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + const bool is_line_topology = IsLine(input_assembly_topology); + const bool needs_point_or_line_support = is_point_topology || is_line_topology; + const bool supports_requested_topology = + !needs_point_or_line_support || device.SupportsConservativePointAndLineRasterization(); + + conservative_raster.conservativeRasterizationMode = + supports_requested_topology ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT + : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT; + } + const bool preserve_provoking_vertex_for_xfb = + !key.state.xfb_enabled || device.IsTransformFeedbackProvokingVertexPreserved(); + const bool use_last_provoking_vertex = + key.state.provoking_vertex_last != 0 && preserve_provoking_vertex_for_xfb && + device.IsProvokingVertexLastSupported(); + VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provoking_vertex{ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT, .pNext = nullptr, - .provokingVertexMode = key.state.provoking_vertex_last != 0 + .provokingVertexMode = use_last_provoking_vertex ? VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT : VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT, }; @@ -756,7 +819,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (device.IsExtConservativeRasterizationSupported()) { conservative_raster.pNext = std::exchange(rasterization_ci.pNext, &conservative_raster); } - if (device.IsExtProvokingVertexSupported() && Settings::values.provoking_vertex.GetValue()) { + if (device.IsExtProvokingVertexSupported()) { provoking_vertex.pNext = std::exchange(rasterization_ci.pNext, &provoking_vertex); } @@ -804,13 +867,15 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { VK_COLOR_COMPONENT_A_BIT, }; const auto& blend{key.state.attachments[index]}; + const PixelFormat color_format{DecodeFormat(key.state.color_formats[index])}; + const bool supports_blending = !VideoCore::Surface::IsPixelFormatInteger(color_format); const std::array mask{blend.Mask()}; VkColorComponentFlags write_mask{}; for (size_t i = 0; i < mask_table.size(); ++i) { write_mask |= mask[i] ? mask_table[i] : 0; } cb_attachments.push_back({ - .blendEnable = blend.enable != 0, + .blendEnable = supports_blending && blend.enable != 0, .srcColorBlendFactor = MaxwellToVK::BlendFactor(blend.SourceRGBFactor()), .dstColorBlendFactor = MaxwellToVK::BlendFactor(blend.DestRGBFactor()), .colorBlendOp = MaxwellToVK::BlendEquation(blend.EquationRGB()), @@ -830,17 +895,22 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pAttachments = cb_attachments.data(), .blendConstants = {} }; - static_vector dynamic_states{ - VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, + static_vector dynamic_states{ VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_LINE_WIDTH, }; + if (line_stipple_dynamic_state_supported) { + dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_STIPPLE_EXT); + } if (key.state.extended_dynamic_state) { static constexpr std::array extended{ + VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT, + VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT, VK_DYNAMIC_STATE_CULL_MODE_EXT, VK_DYNAMIC_STATE_FRONT_FACE_EXT, + VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT, VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, @@ -849,18 +919,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { VK_DYNAMIC_STATE_STENCIL_OP_EXT, }; dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); - - // VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT is part of EDS1 - // Only use it if VIDS is not active (VIDS replaces it with full vertex input control) - if (!key.state.dynamic_vertex_input) { - dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT); - } - } - - // VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) - Independent from EDS - // Provides full dynamic vertex input control, replaces VERTEX_INPUT_BINDING_STRIDE - if (key.state.dynamic_vertex_input) { - dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); + } else { + dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); + dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); } // EDS2 - Core (3 states) @@ -878,41 +939,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT); } - // EDS3 - Blending (composite: 3 states) - if (key.state.extended_dynamic_state_3_blend) { - static constexpr std::array extended3{ - VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, - VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, - VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, - }; - dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); - } - - // EDS3 - Enables (composite: per-feature) - if (key.state.extended_dynamic_state_3_enables) { - if (device.SupportsDynamicState3DepthClampEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT); - } - if (device.SupportsDynamicState3LogicOpEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT); - } - if (device.SupportsDynamicState3LineRasterizationMode()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT); - } - if (device.SupportsDynamicState3ConservativeRasterizationMode()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT); - } - if (device.SupportsDynamicState3LineStippleEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT); - } - if (device.SupportsDynamicState3AlphaToCoverageEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT); - } - if (device.SupportsDynamicState3AlphaToOneEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT); - } - } - const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 34941d6e8d..418a4cfd26 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -81,7 +81,6 @@ public: const GraphicsPipelineCacheKey& key, std::array stages, const std::array& infos); - bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; } bool SupportsAlphaToCoverage() const noexcept { return fragment_has_color0_output; } @@ -93,6 +92,12 @@ public: bool UsesExtendedDynamicState() const noexcept { return key.state.extended_dynamic_state != 0; } + bool UsesExtendedDynamicState2() const noexcept { + return key.state.extended_dynamic_state_2 != 0; + } + bool UsesExtendedDynamicState2LogicOp() const noexcept { + return key.state.extended_dynamic_state_2_logic_op != 0; + } GraphicsPipeline& operator=(GraphicsPipeline&&) noexcept = delete; GraphicsPipeline(GraphicsPipeline&&) noexcept = delete; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 77a4e8616a..e838dc0b43 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -58,7 +58,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 16; +constexpr u32 CACHE_VERSION = 17; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; template @@ -132,20 +132,6 @@ Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribut return Shader::AttributeType::Float; } -Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t index) { - switch (state.DynamicAttributeType(index)) { - case 0: - return Shader::AttributeType::Disabled; - case 1: - return Shader::AttributeType::Float; - case 2: - return Shader::AttributeType::SignedInt; - case 3: - return Shader::AttributeType::UnsignedInt; - } - return Shader::AttributeType::Disabled; -} - Shader::RuntimeInfo MakeRuntimeInfo(std::span programs, const GraphicsPipelineCacheKey& key, const Shader::IR::Program& program, @@ -183,14 +169,8 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program } info.convert_depth_mode = gl_ndc; } - if (key.state.dynamic_vertex_input) { - for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { - info.generic_input_types[index] = AttributeType(key.state, index); - } - } else { - std::ranges::transform(key.state.attributes, info.generic_input_types.begin(), - &CastAttributeType); - } + std::ranges::transform(key.state.attributes, info.generic_input_types.begin(), + &CastAttributeType); break; case Shader::Stage::TessellationEval: info.tess_clockwise = key.state.tessellation_clockwise != 0; @@ -269,6 +249,17 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program dst_a == F::Source1Alpha_GL || dst_a == F::OneMinusSource1Alpha_GL; } + for (size_t i = 0; i < info.active_color_outputs.size(); ++i) { + const auto format = static_cast(key.state.color_formats[i]); + info.active_color_outputs[i] = format != Tegra::RenderTargetFormat::NONE; + } + if (info.dual_source_blend && info.active_color_outputs[0]) { + info.active_color_outputs[1] = true; + } + if (info.alpha_test_func && *info.alpha_test_func != Shader::CompareFunction::Always) { + info.active_color_outputs[0] = true; + } + if (device.IsMoltenVK()) { for (size_t i = 0; i < 8; ++i) { const auto format = static_cast(key.state.color_formats[i]); @@ -469,7 +460,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, // Level 0: Core Dynamic States only // Level 1: Core + EDS1 // Level 2: Core + EDS1 + EDS2 (accumulative) - // Level 3: Core + EDS1 + EDS2 + EDS3 (accumulative) + // Level 2: Core + EDS1 + EDS2 (accumulative) // Here we only verify if extensions were successfully loaded by the device dynamic_features.has_extended_dynamic_state = @@ -480,16 +471,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, dynamic_features.has_extended_dynamic_state_2_logic_op = device.IsExtExtendedDynamicState2ExtrasSupported(); dynamic_features.has_extended_dynamic_state_2_patch_control_points = false; - - dynamic_features.has_extended_dynamic_state_3_blend = - device.IsExtExtendedDynamicState3BlendingSupported(); - dynamic_features.has_extended_dynamic_state_3_enables = - device.IsExtExtendedDynamicState3EnablesSupported(); - - // VIDS: Independent toggle (not affected by dyna_state levels) - dynamic_features.has_dynamic_vertex_input = - device.IsExtVertexInputDynamicStateSupported() && - Settings::values.vertex_input_dynamic_state.GetValue(); } PipelineCache::~PipelineCache() { @@ -595,12 +576,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading (key.state.extended_dynamic_state_2 != 0) != dynamic_features.has_extended_dynamic_state_2 || (key.state.extended_dynamic_state_2_logic_op != 0) != - dynamic_features.has_extended_dynamic_state_2_logic_op || - (key.state.extended_dynamic_state_3_blend != 0) != - dynamic_features.has_extended_dynamic_state_3_blend || - (key.state.extended_dynamic_state_3_enables != 0) != - dynamic_features.has_extended_dynamic_state_3_enables || - (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) { + dynamic_features.has_extended_dynamic_state_2_logic_op) { return; } workers.QueueWork([this, key, envs_ = std::move(envs), &state, &callback]() mutable { diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 415259c72c..8332b489ac 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -60,8 +60,6 @@ public: void Reset() override { ASSERT(references == 0); VideoCommon::BankBase::Reset(); - const auto& dev = device.GetLogical(); - dev.ResetQueryPool(*query_pool, 0, BANK_SIZE); host_results.fill(0ULL); next_bank = 0; } @@ -145,6 +143,7 @@ public: scheduler.Record([buffer = *accumulation_buffer](vk::CommandBuffer cmdbuf) { cmdbuf.FillBuffer(buffer, 0, 8, 0); }); + ReserveBank(); } ~SamplesStreamer() = default; @@ -441,6 +440,10 @@ private: } current_bank = &bank_pool.GetBank(current_bank_id); current_query_pool = current_bank->GetInnerPool(); + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([query_pool = current_query_pool](vk::CommandBuffer cmdbuf) { + cmdbuf.ResetQueryPool(query_pool, 0, SamplesQueryBank::BANK_SIZE); + }); } size_t ReserveBankSlot() { @@ -1205,13 +1208,18 @@ struct QueryCacheRuntimeImpl { conditional_resolve_pass = std::make_unique( device, scheduler, descriptor_pool, compute_pass_descriptor_queue); + VkBufferUsageFlags conditional_usage = + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + if (device.IsExtConditionalRendering()) { + conditional_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT; + } + const VkBufferCreateInfo buffer_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .size = sizeof(u32), - .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT, + .usage = conditional_usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 60b899a811..e01bf3a11a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -173,6 +173,55 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, } return params; } + +bool IsLineRasterizationTopology(const Device& device, Maxwell::PrimitiveTopology topology) { + const VkPrimitiveTopology vk_topology = MaxwellToVK::PrimitiveTopology(device, topology); + return vk_topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || + vk_topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; +} + +VkLineRasterizationModeEXT SelectLineRasterizationMode(const Device& device, bool smooth_lines) { + const bool supports_rectangular_lines = device.SupportsRectangularLines(); + const bool supports_bresenham_lines = device.SupportsBresenhamLines(); + const bool supports_smooth_lines = device.SupportsSmoothLines(); + + if (smooth_lines) { + if (supports_smooth_lines) { + return VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT; + } + if (supports_rectangular_lines) { + return VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; + } + if (supports_bresenham_lines) { + return VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT; + } + } else { + if (supports_rectangular_lines) { + return VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; + } + if (supports_bresenham_lines) { + return VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT; + } + if (supports_smooth_lines) { + return VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT; + } + } + + return VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT; +} + +bool SupportsStippleForMode(const Device& device, VkLineRasterizationModeEXT mode) { + switch (mode) { + case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT: + return device.SupportsStippledRectangularLines(); + case VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT: + return device.SupportsStippledBresenhamLines(); + case VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT: + return device.SupportsStippledSmoothLines(); + default: + return false; + } +} } // Anonymous namespace RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, @@ -228,12 +277,16 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { if (!pipeline->Configure(is_indexed)) return; - UpdateDynamicStates(); + if (pipeline->UsesExtendedDynamicState()) { + state_tracker.InvalidateStateEnableFlag(); + } HandleTransformFeedback(); query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); + UpdateDynamicStates(); + scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); draw_func(); } @@ -305,11 +358,18 @@ void RasterizerVulkan::DrawIndirect() { const auto& buffer = indirect_buffer.first; const auto& offset = indirect_buffer.second; if (params.is_byte_count) { - scheduler.Record([buffer_obj = buffer->Handle(), offset, - stride = params.stride](vk::CommandBuffer cmdbuf) { - cmdbuf.DrawIndirectByteCountEXT(1, 0, buffer_obj, offset, 0, - static_cast(stride)); - }); + if (!device.IsExtTransformFeedbackSupported()) { + scheduler.Record([buffer_obj = buffer->Handle(), offset, + stride = params.stride](vk::CommandBuffer cmdbuf) { + cmdbuf.DrawIndirect(buffer_obj, offset, 1, static_cast(stride)); + }); + } else { + scheduler.Record([buffer_obj = buffer->Handle(), offset, + stride = params.stride](vk::CommandBuffer cmdbuf) { + cmdbuf.DrawIndirectByteCountEXT(1, 0, buffer_obj, offset, 0, + static_cast(stride)); + }); + } return; } if (params.include_count) { @@ -422,7 +482,6 @@ void RasterizerVulkan::Clear(u32 layer_count) { scheduler.RequestRenderpass(framebuffer); query_cache.NotifySegment(true); - query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); u32 up_scale = 1; u32 down_shift = 0; if (texture_cache.IsRescaling()) { @@ -1008,6 +1067,7 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info, void RasterizerVulkan::UpdateDynamicStates() { auto& regs = maxwell3d->regs; + GraphicsPipeline* pipeline = pipeline_cache.CurrentGraphicsPipeline(); // Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting UpdateViewportsState(regs); @@ -1015,14 +1075,15 @@ void RasterizerVulkan::UpdateDynamicStates() { UpdateDepthBias(regs); UpdateBlendConstants(regs); UpdateDepthBounds(regs); - UpdateStencilFaces(regs); UpdateLineWidth(regs); + UpdateLineStipple(regs); // EDS1: CullMode, DepthCompare, FrontFace, StencilOp, DepthBoundsTest, DepthTest, DepthWrite, StencilTest - if (device.IsExtExtendedDynamicStateSupported()) { + if (device.IsExtExtendedDynamicStateSupported() && pipeline && pipeline->UsesExtendedDynamicState()) { UpdateCullMode(regs); UpdateDepthCompareOp(regs); UpdateFrontFace(regs); + UpdatePrimitiveTopology(regs); UpdateStencilOp(regs); if (state_tracker.TouchStateEnable()) { UpdateDepthBoundsTestEnable(regs); @@ -1032,54 +1093,20 @@ void RasterizerVulkan::UpdateDynamicStates() { } } + UpdateStencilFaces(regs); + // EDS2: PrimitiveRestart, RasterizerDiscard, DepthBias enable/disable - if (device.IsExtExtendedDynamicState2Supported()) { + if (device.IsExtExtendedDynamicState2Supported() && pipeline && pipeline->UsesExtendedDynamicState2()) { UpdatePrimitiveRestartEnable(regs); UpdateRasterizerDiscardEnable(regs); UpdateDepthBiasEnable(regs); } // EDS2 Extras: LogicOp operation selection - if (device.IsExtExtendedDynamicState2ExtrasSupported()) { + if (device.IsExtExtendedDynamicState2ExtrasSupported() && pipeline && pipeline->UsesExtendedDynamicState2LogicOp()) { UpdateLogicOp(regs); } - // EDS3 Enables: LogicOpEnable, DepthClamp, LineStipple, ConservativeRaster - if (device.IsExtExtendedDynamicState3EnablesSupported()) { - using namespace Tegra::Engines; - // AMD Workaround: LogicOp incompatible with float render targets - if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || - device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) { - const auto has_float = std::any_of( - regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), - [](const auto& attrib) { - return attrib.type == Maxwell3D::Regs::VertexAttribute::Type::Float; - } - ); - if (regs.logic_op.enable) { - regs.logic_op.enable = static_cast(!has_float); - } - } - UpdateLogicOpEnable(regs); - UpdateDepthClampEnable(regs); - UpdateLineRasterizationMode(regs); - UpdateLineStippleEnable(regs); - UpdateConservativeRasterizationMode(regs); - UpdateAlphaToCoverageEnable(regs); - UpdateAlphaToOneEnable(regs); - } - - // EDS3 Blending: ColorBlendEnable, ColorBlendEquation, ColorWriteMask - if (device.IsExtExtendedDynamicState3BlendingSupported()) { - UpdateBlending(regs); - } - - // Vertex Input Dynamic State: Independent from EDS levels - if (device.IsExtVertexInputDynamicStateSupported()) { - if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) { - UpdateVertexInput(regs); - } - } } void RasterizerVulkan::HandleTransformFeedback() { @@ -1133,8 +1160,16 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg .minDepth = 0.0f, .maxDepth = 1.0f, }; - scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { - cmdbuf.SetViewport(0, viewport); + GraphicsPipeline* pipeline = pipeline_cache.CurrentGraphicsPipeline(); + const bool use_viewport_with_count = device.IsExtExtendedDynamicStateSupported() && + (!pipeline || pipeline->UsesExtendedDynamicState()); + scheduler.Record([viewport, use_viewport_with_count](vk::CommandBuffer cmdbuf) { + if (use_viewport_with_count) { + std::array viewports{viewport}; + cmdbuf.SetViewportWithCountEXT(viewports); + } else { + cmdbuf.SetViewport(0, viewport); + } }); return; } @@ -1150,10 +1185,17 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale), GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale), }; - scheduler.Record([this, viewport_list](vk::CommandBuffer cmdbuf) { + GraphicsPipeline* pipeline = pipeline_cache.CurrentGraphicsPipeline(); + const bool use_viewport_with_count = device.IsExtExtendedDynamicStateSupported() && + (!pipeline || pipeline->UsesExtendedDynamicState()); + scheduler.Record([this, viewport_list, use_viewport_with_count](vk::CommandBuffer cmdbuf) { const u32 num_viewports = std::min(device.GetMaxViewports(), Maxwell::NumViewports); const vk::Span viewports(viewport_list.data(), num_viewports); - cmdbuf.SetViewport(0, viewports); + if (use_viewport_with_count) { + cmdbuf.SetViewportWithCountEXT(viewports); + } else { + cmdbuf.SetViewport(0, viewports); + } }); } @@ -1174,8 +1216,16 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs scissor.offset.y = static_cast(y); scissor.extent.width = width; scissor.extent.height = height; - scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { - cmdbuf.SetScissor(0, scissor); + GraphicsPipeline* pipeline = pipeline_cache.CurrentGraphicsPipeline(); + const bool use_scissor_with_count = device.IsExtExtendedDynamicStateSupported() && + (!pipeline || pipeline->UsesExtendedDynamicState()); + scheduler.Record([scissor, use_scissor_with_count](vk::CommandBuffer cmdbuf) { + if (use_scissor_with_count) { + std::array scissors{scissor}; + cmdbuf.SetScissorWithCountEXT(scissors); + } else { + cmdbuf.SetScissor(0, scissor); + } }); return; } @@ -1203,10 +1253,17 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs GetScissorState(regs, 14, up_scale, down_shift), GetScissorState(regs, 15, up_scale, down_shift), }; - scheduler.Record([this, scissor_list](vk::CommandBuffer cmdbuf) { + GraphicsPipeline* pipeline = pipeline_cache.CurrentGraphicsPipeline(); + const bool use_scissor_with_count = device.IsExtExtendedDynamicStateSupported() && + (!pipeline || pipeline->UsesExtendedDynamicState()); + scheduler.Record([this, scissor_list, use_scissor_with_count](vk::CommandBuffer cmdbuf) { const u32 num_scissors = std::min(device.GetMaxViewports(), Maxwell::NumViewports); const vk::Span scissors(scissor_list.data(), num_scissors); - cmdbuf.SetScissor(0, scissors); + if (use_scissor_with_count) { + cmdbuf.SetScissorWithCountEXT(scissors); + } else { + cmdbuf.SetScissor(0, scissors); + } }); } @@ -1374,6 +1431,33 @@ void RasterizerVulkan::UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs) { scheduler.Record([width](vk::CommandBuffer cmdbuf) { cmdbuf.SetLineWidth(width); }); } +void RasterizerVulkan::UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchLineStipple()) { + return; + } + if (!device.IsExtLineRasterizationSupported()) { + return; + } + + const auto topology = maxwell3d->draw_manager->GetDrawState().topology; + if (!IsLineRasterizationTopology(device, topology)) { + return; + } + + const VkLineRasterizationModeEXT mode = + SelectLineRasterizationMode(device, regs.line_anti_alias_enable != 0); + + if (regs.line_stipple_enable == 0 || !SupportsStippleForMode(device, mode)) { + return; + } + + scheduler.Record( + [factor = regs.line_stipple_params.factor, + pattern = static_cast(regs.line_stipple_params.pattern)](vk::CommandBuffer cmdbuf) { + cmdbuf.SetLineStippleEXT(factor, pattern); + }); +} + void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchCullMode()) { return; @@ -1434,73 +1518,6 @@ void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D:: }); } -void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!state_tracker.TouchConservativeRasterizationMode()) { - return; - } - - if (!device.SupportsDynamicState3ConservativeRasterizationMode()) { - return; - } - - scheduler.Record([enable = regs.conservative_raster_enable](vk::CommandBuffer cmdbuf) { - cmdbuf.SetConservativeRasterizationModeEXT( - enable ? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT - : VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT); - }); -} - -void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!state_tracker.TouchLineStippleEnable()) { - return; - } - - if (!device.SupportsDynamicState3LineStippleEnable()) { - return; - } - - scheduler.Record([enable = regs.line_stipple_enable](vk::CommandBuffer cmdbuf) { - cmdbuf.SetLineStippleEnableEXT(enable); - }); -} - -void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!device.IsExtLineRasterizationSupported()) { - return; - } - if (!state_tracker.TouchLineRasterizationMode()) { - return; - } - - if (!device.SupportsDynamicState3LineRasterizationMode()) { - static std::once_flag warn_missing_rect; - std::call_once(warn_missing_rect, [] { - LOG_WARNING(Render_Vulkan, - "Driver lacks rectangular line rasterization support; skipping dynamic " - "line state updates"); - }); - return; - } - - const bool wants_smooth = regs.line_anti_alias_enable != 0; - VkLineRasterizationModeEXT mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT; - if (wants_smooth) { - if (device.SupportsSmoothLines()) { - mode = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT; - } else { - static std::once_flag warn_missing_smooth; - std::call_once(warn_missing_smooth, [] { - LOG_WARNING(Render_Vulkan, - "Line anti-aliasing requested but smoothLines feature unavailable; " - "using rectangular rasterization"); - }); - } - } - scheduler.Record([mode](vk::CommandBuffer cmdbuf) { - cmdbuf.SetLineRasterizationModeEXT(mode); - }); -} - void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchDepthBiasEnable()) { return; @@ -1536,70 +1553,6 @@ void RasterizerVulkan::UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& re [enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthBiasEnableEXT(enable != 0); }); } -void RasterizerVulkan::UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!state_tracker.TouchLogicOpEnable()) { - return; - } - if (!device.SupportsDynamicState3LogicOpEnable()) { - return; - } - scheduler.Record([enable = regs.logic_op.enable](vk::CommandBuffer cmdbuf) { - cmdbuf.SetLogicOpEnableEXT(enable != 0); - }); -} - -void RasterizerVulkan::UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!state_tracker.TouchDepthClampEnable()) { - return; - } - if (!device.SupportsDynamicState3DepthClampEnable()) { - return; - } - bool is_enabled = !(regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::Passthrough || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumZ); - scheduler.Record( - [is_enabled](vk::CommandBuffer cmdbuf) { cmdbuf.SetDepthClampEnableEXT(is_enabled); }); -} - -void RasterizerVulkan::UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!state_tracker.TouchAlphaToCoverageEnable()) { - return; - } - if (!device.SupportsDynamicState3AlphaToCoverageEnable()) { - return; - } - GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline(); - const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToCoverage() && - regs.anti_alias_alpha_control.alpha_to_coverage != 0; - scheduler.Record([enable](vk::CommandBuffer cmdbuf) { - cmdbuf.SetAlphaToCoverageEnableEXT(enable ? VK_TRUE : VK_FALSE); - }); -} - -void RasterizerVulkan::UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs) { - if (!state_tracker.TouchAlphaToOneEnable()) { - return; - } - if (!device.SupportsDynamicState3AlphaToOneEnable()) { - static std::once_flag warn_alpha_to_one; - std::call_once(warn_alpha_to_one, [] { - LOG_WARNING(Render_Vulkan, - "Alpha-to-one is not supported on this device; forcing it disabled"); - }); - return; - } - GraphicsPipeline* const pipeline = pipeline_cache.CurrentGraphicsPipeline(); - const bool enable = pipeline != nullptr && pipeline->SupportsAlphaToOne() && - regs.anti_alias_alpha_control.alpha_to_one != 0; - scheduler.Record([enable](vk::CommandBuffer cmdbuf) { - cmdbuf.SetAlphaToOneEnableEXT(enable ? VK_TRUE : VK_FALSE); - }); -} - void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchDepthCompareOp()) { return; @@ -1609,6 +1562,17 @@ void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& reg }); } +void RasterizerVulkan::UpdatePrimitiveTopology([[maybe_unused]] Tegra::Engines::Maxwell3D::Regs& regs) { + const auto topology = maxwell3d->draw_manager->GetDrawState().topology; + if (!state_tracker.ChangePrimitiveTopology(topology)) { + return; + } + const auto vk_topology = MaxwellToVK::PrimitiveTopology(device, topology); + scheduler.Record([vk_topology](vk::CommandBuffer cmdbuf) { + cmdbuf.SetPrimitiveTopologyEXT(vk_topology); + }); +} + void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchFrontFace()) { return; @@ -1671,87 +1635,15 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchBlending()) { return; } - - if (state_tracker.TouchColorMask()) { - std::array setup_masks{}; - for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; - auto& current = setup_masks[index]; - if (mask.R) { - current |= VK_COLOR_COMPONENT_R_BIT; - } - if (mask.G) { - current |= VK_COLOR_COMPONENT_G_BIT; - } - if (mask.B) { - current |= VK_COLOR_COMPONENT_B_BIT; - } - if (mask.A) { - current |= VK_COLOR_COMPONENT_A_BIT; - } - } - scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) { - cmdbuf.SetColorWriteMaskEXT(0, setup_masks); - }); - } - - if (state_tracker.TouchBlendEnable()) { - std::array setup_enables{}; - std::ranges::transform( - regs.blend.enable, setup_enables.begin(), - [&](const auto& is_enabled) { return is_enabled != 0 ? VK_TRUE : VK_FALSE; }); - scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { - cmdbuf.SetColorBlendEnableEXT(0, setup_enables); - }); - } - - if (state_tracker.TouchBlendEquations()) { - std::array setup_blends{}; - - const auto blend_setup = [&](auto& host_blend, const auto& guest_blend) { - host_blend.srcColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_source); - host_blend.dstColorBlendFactor = MaxwellToVK::BlendFactor(guest_blend.color_dest); - host_blend.colorBlendOp = MaxwellToVK::BlendEquation(guest_blend.color_op); - host_blend.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_source); - host_blend.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(guest_blend.alpha_dest); - host_blend.alphaBlendOp = MaxwellToVK::BlendEquation(guest_blend.alpha_op); - }; - - // Single blend equation for all targets - if (!regs.blend_per_target_enabled) { - // Temporary workaround for games that use iterated blending - if (regs.iterated_blend.enable && Settings::values.use_squashed_iterated_blend) { - setup_blends[0].srcColorBlendFactor = VK_BLEND_FACTOR_ONE; - setup_blends[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE; - setup_blends[0].colorBlendOp = VK_BLEND_OP_ADD; - setup_blends[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; - setup_blends[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - setup_blends[0].alphaBlendOp = VK_BLEND_OP_ADD; - } else { - blend_setup(setup_blends[0], regs.blend); - } - - // Copy first blend state to all other targets - for (size_t index = 1; index < Maxwell::NumRenderTargets; index++) { - setup_blends[index] = setup_blends[0]; - } - } else { - // Per-target blending - for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - blend_setup(setup_blends[index], regs.blend_per_target[index]); - } - } - - scheduler.Record([setup_blends](vk::CommandBuffer cmdbuf) { - cmdbuf.SetColorBlendEquationEXT(0, setup_blends); - }); - } } void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchStencilTestEnable()) { return; } + if (regs.stencil_enable != 0) { + state_tracker.ResetStencilState(); + } scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetStencilTestEnableEXT(enable); }); @@ -1763,56 +1655,6 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) return; } dirty[Dirty::VertexInput] = false; - - boost::container::static_vector bindings; - boost::container::static_vector attributes; - - // There seems to be a bug on Nvidia's driver where updating only higher attributes ends up - // generating dirty state. Track the highest dirty attribute and update all attributes until - // that one. - size_t highest_dirty_attr{}; - for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { - if (dirty[Dirty::VertexAttribute0 + index]) { - highest_dirty_attr = index; - } - } - for (size_t index = 0; index < highest_dirty_attr; ++index) { - const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]}; - const u32 binding{attribute.buffer}; - dirty[Dirty::VertexAttribute0 + index] = false; - dirty[Dirty::VertexBinding0 + static_cast(binding)] = true; - if (!attribute.constant) { - attributes.push_back({ - .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, - .pNext = nullptr, - .location = static_cast(index), - .binding = binding, - .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), - .offset = attribute.offset, - }); - } - } - for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { - if (!dirty[Dirty::VertexBinding0 + index]) { - continue; - } - dirty[Dirty::VertexBinding0 + index] = false; - - const u32 binding{static_cast(index)}; - const auto& input_binding{regs.vertex_streams[binding]}; - const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; - bindings.push_back({ - .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT, - .pNext = nullptr, - .binding = binding, - .stride = input_binding.stride, - .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX, - .divisor = is_instanced ? input_binding.frequency : 1, - }); - } - scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { - cmdbuf.SetVertexInputEXT(bindings, attributes); - }); } void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b689c6b660..bdea6510c0 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -168,23 +168,17 @@ private: void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLineWidth(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs); void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateLogicOpEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateDepthClampEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateAlphaToCoverageEnable(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateAlphaToOneEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 0a032cdae0..57f3467d4a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -337,6 +337,13 @@ void Scheduler::EndRenderPass() images = renderpass_images, ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) { std::array barriers; + constexpr VkPipelineStageFlags src_stages = + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + constexpr VkPipelineStageFlags dst_stages = + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; for (size_t i = 0; i < num_images; ++i) { const VkImageSubresourceRange& range = ranges[i]; const bool is_color = (range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0; @@ -372,9 +379,8 @@ void Scheduler::EndRenderPass() }; } cmdbuf.EndRenderPass(); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); + cmdbuf.PipelineBarrier(src_stages, dst_stages, 0, nullptr, nullptr, + vk::Span(barriers.data(), num_images)); }); state.renderpass = nullptr; diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 79967d540a..19133d3255 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -40,6 +40,7 @@ Flags MakeInvalidationFlags() { StencilWriteMask, StencilCompare, LineWidth, + LineStipple, CullMode, DepthBoundsEnable, DepthTestEnable, @@ -54,19 +55,11 @@ Flags MakeInvalidationFlags() { StateEnable, PrimitiveRestartEnable, DepthBiasEnable, - LogicOpEnable, - DepthClampEnable, - AlphaToCoverageEnable, - AlphaToOneEnable, - LineRasterizationMode, LogicOp, Blending, ColorMask, BlendEquations, BlendEnable, - ConservativeRasterizationMode, - LineStippleEnable, - LineStippleParams, }; Flags flags{}; for (const int flag : INVALIDATION_FLAGS) { @@ -127,6 +120,13 @@ void SetupDirtyStencilProperties(Tables& tables) { void SetupDirtyLineWidth(Tables& tables) { tables[0][OFF(line_width_smooth)] = LineWidth; tables[0][OFF(line_width_aliased)] = LineWidth; + tables[0][OFF(line_anti_alias_enable)] = LineWidth; +} + +void SetupDirtyLineStipple(Tables& tables) { + tables[0][OFF(line_stipple_enable)] = LineStipple; + FillBlock(tables[0], OFF(line_stipple_params), NUM(line_stipple_params), LineStipple); + tables[1][OFF(line_anti_alias_enable)] = LineStipple; } void SetupDirtyCullMode(Tables& tables) { @@ -149,11 +149,6 @@ void SetupDirtyStateEnable(Tables& tables) { setup(OFF(polygon_offset_point_enable), DepthBiasEnable); setup(OFF(polygon_offset_line_enable), DepthBiasEnable); setup(OFF(polygon_offset_fill_enable), DepthBiasEnable); - setup(OFF(logic_op.enable), LogicOpEnable); - setup(OFF(viewport_clip_control.geometry_clip), DepthClampEnable); - setup(OFF(line_stipple_enable), LineStippleEnable); - setup(OFF(anti_alias_alpha_control.alpha_to_coverage), AlphaToCoverageEnable); - setup(OFF(anti_alias_alpha_control.alpha_to_one), AlphaToOneEnable); } void SetupDirtyDepthCompareOp(Tables& tables) { @@ -227,13 +222,6 @@ void SetupDirtyVertexBindings(Tables& tables) { } } -void SetupRasterModes(Tables &tables) { - auto& table = tables[0]; - - table[OFF(line_stipple_params)] = LineStippleParams; - table[OFF(conservative_raster_enable)] = ConservativeRasterizationMode; - table[OFF(line_anti_alias_enable)] = LineRasterizationMode; -} } // Anonymous namespace void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { @@ -246,6 +234,7 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { SetupDirtyDepthBounds(tables); SetupDirtyStencilProperties(tables); SetupDirtyLineWidth(tables); + SetupDirtyLineStipple(tables); SetupDirtyCullMode(tables); SetupDirtyStateEnable(tables); SetupDirtyDepthCompareOp(tables); @@ -256,7 +245,6 @@ void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) { SetupDirtyVertexAttributes(tables); SetupDirtyVertexBindings(tables); SetupDirtySpecialOps(tables); - SetupRasterModes(tables); } void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) { diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 74bae9e181..81cd4d9631 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -42,6 +42,7 @@ enum : u8 { StencilWriteMask, StencilCompare, LineWidth, + LineStipple, CullMode, DepthBoundsEnable, @@ -53,17 +54,9 @@ enum : u8 { StencilTestEnable, PrimitiveRestartEnable, RasterizerDiscardEnable, - ConservativeRasterizationMode, - LineRasterizationMode, - LineStippleEnable, - LineStippleParams, DepthBiasEnable, StateEnable, LogicOp, - LogicOpEnable, - DepthClampEnable, - AlphaToCoverageEnable, - AlphaToOneEnable, Blending, BlendEnable, @@ -177,6 +170,10 @@ public: return ExchangeCheck(back.compare_mask, new_value) || stencil_reset; } + void ResetStencilState() { + stencil_reset = true; + } + void ClearStencilReset() { stencil_reset = false; } @@ -185,6 +182,10 @@ public: return Exchange(Dirty::LineWidth, false); } + bool TouchLineStipple() const { + return Exchange(Dirty::LineStipple, false); + } + bool TouchCullMode() { return Exchange(Dirty::CullMode, false); } @@ -213,33 +214,8 @@ public: return Exchange(Dirty::RasterizerDiscardEnable, false); } - bool TouchConservativeRasterizationMode() - { - return Exchange(Dirty::ConservativeRasterizationMode, false); - } - - bool TouchLineStippleEnable() { return Exchange(Dirty::LineStippleEnable, false); } - - bool TouchLineStipple() { return Exchange(Dirty::LineStippleParams, false); } - bool TouchDepthBiasEnable() { return Exchange(Dirty::DepthBiasEnable, false); } - bool TouchLogicOpEnable() { - return Exchange(Dirty::LogicOpEnable, false); - } - - bool TouchDepthClampEnable() { - return Exchange(Dirty::DepthClampEnable, false); - } - - bool TouchAlphaToCoverageEnable() { - return Exchange(Dirty::AlphaToCoverageEnable, false); - } - - bool TouchAlphaToOneEnable() { - return Exchange(Dirty::AlphaToOneEnable, false); - } - bool TouchDepthCompareOp() { return Exchange(Dirty::DepthCompareOp, false); } @@ -276,10 +252,6 @@ public: return Exchange(Dirty::LogicOp, false); } - bool TouchLineRasterizationMode() { - return Exchange(Dirty::LineRasterizationMode, false); - } - bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) { const bool has_changed = current_topology != new_topology; current_topology = new_topology; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index d51564dcb3..5cd4273c2d 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -446,15 +446,24 @@ TransformBufferCopies(std::span copies, size_t bu }; } +[[nodiscard]] VkImageSubresourceRange MakeBarrierSubresourceRange( + VkImageAspectFlags aspect_mask, const SubresourceRange& range, bool is_3d_image) { + VkImageSubresourceRange subresource_range = MakeSubresourceRange(aspect_mask, range); + if (is_3d_image && subresource_range.layerCount == 1) { + subresource_range.layerCount = VK_REMAINING_ARRAY_LAYERS; + } + return subresource_range; +} + [[nodiscard]] VkImageSubresourceRange MakeSubresourceRange(const ImageView* image_view) { SubresourceRange range = image_view->range; + const bool is_3d_image = image_view->type == VideoCommon::ImageViewType::e3D || + True(image_view->flags & VideoCommon::ImageViewFlagBits::Slice); if (True(image_view->flags & VideoCommon::ImageViewFlagBits::Slice)) { - // Slice image views always affect a single layer, but their subresource range corresponds - // to the slice. Override the value to affect a single layer. range.base.layer = 0; range.extent.layers = 1; } - return MakeSubresourceRange(ImageAspectMask(image_view->format), range); + return MakeBarrierSubresourceRange(ImageAspectMask(image_view->format), range, is_3d_image); } [[nodiscard]] VkImageSubresourceLayers MakeSubresourceLayers(const ImageView* image_view) { @@ -524,18 +533,23 @@ struct RangedBarrierRange { max_layer = (std::max)(max_layer, layers.baseArrayLayer + layers.layerCount); } - VkImageSubresourceRange SubresourceRange(VkImageAspectFlags aspect_mask) const noexcept { - return VkImageSubresourceRange{ - .aspectMask = aspect_mask, - .baseMipLevel = min_mip, - .levelCount = max_mip - min_mip, - .baseArrayLayer = min_layer, - .layerCount = max_layer - min_layer, + VkImageSubresourceRange SubresourceRange(VkImageAspectFlags aspect_mask, + bool is_3d_image) const noexcept { + const VideoCommon::SubresourceRange range{ + .base = { + .level = static_cast(min_mip), + .layer = static_cast(min_layer), + }, + .extent = { + .levels = static_cast(max_mip - min_mip), + .layers = static_cast(max_layer - min_layer), + }, }; + return MakeBarrierSubresourceRange(aspect_mask, range, is_3d_image); } }; void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image, - VkImageAspectFlags aspect_mask, bool is_initialized, + VkImageAspectFlags aspect_mask, bool is_initialized, bool is_3d_image, std::span copies) { static constexpr VkAccessFlags WRITE_ACCESS_FLAGS = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | @@ -549,7 +563,8 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im for (const auto& region : copies) { range.AddLayers(region.imageSubresource); } - const VkImageSubresourceRange subresource_range = range.SubresourceRange(aspect_mask); + const VkImageSubresourceRange subresource_range = + range.SubresourceRange(aspect_mask, is_3d_image); const VkImageMemoryBarrier read_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -1006,9 +1021,12 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, const VkBuffer copy_buffer = GetTemporaryBuffer(total_size); const VkImage dst_image = dst.Handle(); const VkImage src_image = src.Handle(); + const bool dst_is_3d = dst.info.type == ImageType::e3D; + const bool src_is_3d = src.info.type == ImageType::e3D; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dst_image, src_image, copy_buffer, src_aspect_mask, dst_aspect_mask, - vk_in_copies, vk_out_copies](vk::CommandBuffer cmdbuf) { + dst_is_3d, src_is_3d, vk_in_copies, + vk_out_copies](vk::CommandBuffer cmdbuf) { RangedBarrierRange dst_range; RangedBarrierRange src_range; for (const VkBufferImageCopy& copy : vk_in_copies) { @@ -1042,7 +1060,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = src_image, - .subresourceRange = src_range.SubresourceRange(src_aspect_mask), + .subresourceRange = src_range.SubresourceRange(src_aspect_mask, src_is_3d), }, }; const std::array middle_in_barrier{ @@ -1056,7 +1074,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = src_image, - .subresourceRange = src_range.SubresourceRange(src_aspect_mask), + .subresourceRange = src_range.SubresourceRange(src_aspect_mask, src_is_3d), }, }; const std::array middle_out_barrier{ @@ -1072,7 +1090,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = dst_image, - .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask), + .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask, dst_is_3d), }, }; const std::array post_barriers{ @@ -1091,7 +1109,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = dst_image, - .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask), + .subresourceRange = dst_range.SubresourceRange(dst_aspect_mask, dst_is_3d), }, }; cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, @@ -1440,6 +1458,8 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, boost::container::small_vector vk_copies(copies.size()); const VkImageAspectFlags aspect_mask = dst.AspectMask(); ASSERT(aspect_mask == src.AspectMask()); + const bool dst_is_3d = dst.info.type == ImageType::e3D; + const bool src_is_3d = src.info.type == ImageType::e3D; std::ranges::transform(copies, vk_copies.begin(), [aspect_mask](const auto& copy) { return MakeImageCopy(copy, aspect_mask); @@ -1447,7 +1467,8 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, const VkImage dst_image = dst.Handle(); const VkImage src_image = src.Handle(); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([dst_image, src_image, aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { + scheduler.Record([dst_image, src_image, aspect_mask, dst_is_3d, src_is_3d, + vk_copies](vk::CommandBuffer cmdbuf) { RangedBarrierRange dst_range; RangedBarrierRange src_range; for (const VkImageCopy& copy : vk_copies) { @@ -1467,7 +1488,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = src_image, - .subresourceRange = src_range.SubresourceRange(aspect_mask), + .subresourceRange = src_range.SubresourceRange(aspect_mask, src_is_3d), }, VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -1481,7 +1502,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = dst_image, - .subresourceRange = dst_range.SubresourceRange(aspect_mask), + .subresourceRange = dst_range.SubresourceRange(aspect_mask, dst_is_3d), }, }; const std::array post_barriers{ @@ -1495,7 +1516,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = src_image, - .subresourceRange = src_range.SubresourceRange(aspect_mask), + .subresourceRange = src_range.SubresourceRange(aspect_mask, src_is_3d), }, VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -1512,7 +1533,7 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = dst_image, - .subresourceRange = dst_range.SubresourceRange(aspect_mask), + .subresourceRange = dst_range.SubresourceRange(aspect_mask, dst_is_3d), }, }; cmdbuf.PipelineBarrier( @@ -1691,10 +1712,12 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, const VkBuffer src_buffer = buffer; const VkImage temp_vk_image = *temp_wrapper->original_image; const VkImageAspectFlags vk_aspect_mask = temp_wrapper->aspect_mask; + const bool temp_is_3d = temp_info.type == ImageType::e3D; - scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies, + scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, temp_is_3d, vk_copies, keep = temp_wrapper](vk::CommandBuffer cmdbuf) { - CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, VideoCommon::FixSmallVectorADL(vk_copies)); + CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, + temp_is_3d, VideoCommon::FixSmallVectorADL(vk_copies)); }); // Use MSAACopyPass to convert from non-MSAA to MSAA @@ -1730,10 +1753,12 @@ 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 bool is_3d_image = info.type == ImageType::e3D; - scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized, + scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized, is_3d_image, vk_copies](vk::CommandBuffer cmdbuf) { - CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized, VideoCommon::FixSmallVectorADL(vk_copies)); + CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized, + is_3d_image, VideoCommon::FixSmallVectorADL(vk_copies)); }); if (is_rescaled) { @@ -2112,7 +2137,10 @@ 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()) { + const VkImageUsageFlags desired_view_usage = ImageUsageFlags(format_info, format); + const VkImageUsageFlags image_usage = image.UsageFlags(); + const VkImageUsageFlags view_usage = desired_view_usage & image_usage; + if (desired_view_usage != image_usage) { LOG_WARNING(Render_Vulkan, "Image view format {} has different usage flags than image format {}", format, image.info.format); @@ -2120,7 +2148,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI const VkImageViewUsageCreateInfo image_view_usage{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .pNext = nullptr, - .usage = ImageUsageFlags(format_info, format), + .usage = view_usage, }; const VkImageViewCreateInfo create_info{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 71210ffe6e..a92f44cc30 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -1191,7 +1191,7 @@ void TextureCache

::DownloadImageIntoBuffer(typename TextureCache

::Image* i template void TextureCache

::RefreshContents(Image& image, ImageId image_id) { if (False(image.flags & ImageFlagBits::CpuModified)) { - // Only upload modified images + runtime.TransitionImageLayout(image); return; } diff --git a/src/video_core/vulkan_common/vulkan.h b/src/video_core/vulkan_common/vulkan.h index 8d2e8e2a37..8d774a853e 100644 --- a/src/video_core/vulkan_common/vulkan.h +++ b/src/video_core/vulkan_common/vulkan.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -22,16 +22,13 @@ #include -// Define maintenance 7-9 extension names (not yet in official Vulkan headers) +// Define maintenance 7-8 extension names #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_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp index cd6653c86e..c3aba89179 100644 --- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp +++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp @@ -43,19 +43,9 @@ VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, case 0xdff2e5c1u: // VUID-vkCmdSetRasterizerDiscardEnable-None-04871 case 0x0cc85f41u: // VUID-vkCmdSetPrimitiveRestartEnable-None-04866 case 0x01257b492: // VUID-vkCmdSetLogicOpEXT-None-0486 - // The below are due to incorrect reporting of vertexInputDynamicState - case 0x398e0dabu: // VUID-vkCmdSetVertexInputEXT-None-04790 - // The below are due to incorrect reporting of extendedDynamicState3 - case 0x970c11a5u: // VUID-vkCmdSetColorWriteMaskEXT-extendedDynamicState3ColorWriteMask-07364 - case 0x6b453f78u: // VUID-vkCmdSetColorBlendEnableEXT-extendedDynamicState3ColorBlendEnable-07355 - case 0xf66469d0u: // VUID-vkCmdSetColorBlendEquationEXT-extendedDynamicState3ColorBlendEquation-07356 - case 0x1d43405eu: // VUID-vkCmdSetLogicOpEnableEXT-extendedDynamicState3LogicOpEnable-07365 - case 0x638462e8u: // VUID-vkCmdSetDepthClampEnableEXT-extendedDynamicState3DepthClampEnable-07448 // Misc case 0xe0a2da61u: // VUID-vkCmdDrawIndexed-format-07753 -#else - case 0x682a878au: // VUID-vkCmdBindVertexBuffers2EXT-pBuffers-parameter - case 0x99fb7dfdu: // UNASSIGNED-RequiredParameter (vkCmdBindVertexBuffers2EXT pBuffers[0]) +#else case 0xe8616bf2u: // Bound VkDescriptorSet 0x0[] was destroyed. Likely push_descriptor related case 0x1608dec0u: // Image layout in vkUpdateDescriptorSet doesn't match descriptor use case 0x55362756u: // Descriptor binding and framebuffer attachment overlap diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index a2ff3ee6ed..c84c7b6a5a 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -475,6 +475,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR if (extensions.descriptor_indexing && Settings::values.descriptor_indexing.GetValue()) { first_next = &descriptor_indexing; + } else { + RemoveExtension(extensions.descriptor_indexing, + VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); } is_blit_depth24_stencil8_supported = TestDepthStencilBlits(VK_FORMAT_D24_UNORM_S8_UINT); @@ -498,14 +501,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR CollectToolingInfo(); if (is_qualcomm) { - // Qualcomm Adreno GPUs doesn't handle scaled vertex attributes; keep emulation enabled must_emulate_scaled_formats = true; - LOG_WARNING(Render_Vulkan, - "Qualcomm drivers require scaled vertex format emulation; forcing fallback"); - - LOG_WARNING(Render_Vulkan, - "Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers"); - RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); features.shader_atomic_int64.shaderBufferInt64Atomics = false; @@ -523,9 +519,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR bool should_patch_bcn = api_level >= 28; const bool bcn_debug_override = Settings::values.patch_old_qcom_drivers.GetValue(); if (bcn_debug_override != should_patch_bcn) { - LOG_WARNING(Render_Vulkan, - "BCn patch debug override active: {} (auto-detected: {})", - bcn_debug_override, should_patch_bcn); should_patch_bcn = bcn_debug_override; } @@ -540,11 +533,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } else { LOG_ERROR(Render_Vulkan, "BCn patch failed! Driver code may now crash"); } - } else { - LOG_WARNING(Render_Vulkan, - "BCn texture patching skipped for stability (Android API {} < 28). " - "Driver version {}.{} would support patching, but may crash on older Android.", - api_level, major, minor); } } else if (patch_status == ADRENOTOOLS_BCN_BLOB) { LOG_INFO(Render_Vulkan, "Adreno driver supports BCn textures natively (no patch needed)"); @@ -563,7 +551,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR features.shader_float16_int8.shaderFloat16 = false; } - // Mali/ NVIDIA proprietary drivers: Shader stencil export not supported + // NVIDIA proprietary drivers: Shader stencil export not supported + if (properties.properties.driverVersion >= VK_MAKE_API_VERSION(510, 0, 0, 0)) { + LOG_WARNING(Render_Vulkan, + "NVIDIA Drivers >= 510 do not support MSAA->MSAA image blits. " + "MSAA scaling will use 3D helpers. MSAA resolves work normally."); + cant_blit_msaa = true; + } + // Use hardware depth/stencil blits instead when available if (!extensions.shader_stencil_export) { LOG_INFO(Render_Vulkan, @@ -610,10 +605,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR 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); } } @@ -660,53 +651,25 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const auto dyna_state = Settings::values.dyna_state.GetValue(); - // Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp - // This slider controls EXTENDED dynamic states with accumulative levels per Vulkan specs: - // Level 0 = Core Dynamic States only (Vulkan 1.0) - // Level 1 = Core + VK_EXT_extended_dynamic_state - // Level 2 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 - // Level 3 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + VK_EXT_extended_dynamic_state3 - switch (dyna_state) { case Settings::ExtendedDynamicState::Disabled: - // Level 0: Disable all extended dynamic state extensions + // Level 0: Disable all configured extended dynamic state extensions RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, - VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - dynamic_state3_blending = false; - dynamic_state3_enables = false; break; case Settings::ExtendedDynamicState::EDS1: - // Level 1: Enable EDS1, disable EDS2 and EDS3 + // Level 1: Enable EDS1, disable EDS2 RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, - VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - dynamic_state3_blending = false; - dynamic_state3_enables = false; break; case Settings::ExtendedDynamicState::EDS2: - // Level 2: Enable EDS1 + EDS2, disable EDS3 - RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, - VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - dynamic_state3_blending = false; - dynamic_state3_enables = false; - break; - case Settings::ExtendedDynamicState::EDS3: default: - // Level 3: Enable all (EDS1 + EDS2 + EDS3) + // Level 2: Enable EDS1 + EDS2 break; } - // VK_EXT_vertex_input_dynamic_state is independent from EDS - // It can be enabled even without extended_dynamic_state - if (!Settings::values.vertex_input_dynamic_state.GetValue()) { - RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); - } - logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld); graphics_queue = logical.GetQueue(graphics_family); @@ -1063,6 +1026,11 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; SetNext(next, properties.push_descriptor); } + if (extensions.conservative_rasterization) { + properties.conservative_rasterization.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT; + SetNext(next, properties.conservative_rasterization); + } if (extensions.subgroup_size_control || features.subgroup_size_control.subgroupSizeControl) { properties.subgroup_size_control.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES; @@ -1093,6 +1061,22 @@ bool Device::GetSuitability(bool requires_swapchain) { // Unload extensions if feature support is insufficient. RemoveUnsuitableExtensions(); + // Query VK_EXT_custom_border_color properties if the extension is enabled. + if (extensions.custom_border_color) { + auto proc = dld.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceCustomBorderColorPropertiesEXT"); + if (proc != nullptr) { + auto vkGetPhysicalDeviceCustomBorderColorPropertiesEXT = + reinterpret_cast( + proc); + custom_border_color_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT; + custom_border_color_properties.pNext = nullptr; + vkGetPhysicalDeviceCustomBorderColorPropertiesEXT(physical, &custom_border_color_properties); + has_custom_border_color_properties = true; + } else { + has_custom_border_color_properties = false; + } + } + // Check limits. struct Limit { u32 minimum; @@ -1117,46 +1101,14 @@ bool Device::GetSuitability(bool requires_swapchain) { // VK_DYNAMIC_STATE - // Driver detection variables for workarounds in GetSuitability - const VkDriverId driver_id = properties.driver.driverID; + // VK_EXT_extended_dynamic_state below this will appear drivers that need workarounds. // VK_EXT_extended_dynamic_state2 below this will appear drivers that need workarounds. - // VK_EXT_extended_dynamic_state3 below this will appear drivers that need workarounds. - - // Samsung: Broken extendedDynamicState3ColorBlendEquation - // Disable blend equation dynamic state, force static pipeline state - if (extensions.extended_dynamic_state3 && - (driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) { - LOG_WARNING(Render_Vulkan, - "Samsung: Disabling broken extendedDynamicState3ColorBlendEquation"); - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; - } - - // Intel Windows < 27.20.100.0: Broken VertexInputDynamicState - // Same for NVIDIA Proprietary < 580.119.02, unknown when VIDS was first NOT broken - // Disable VertexInputDynamicState on old Intel Windows drivers - if (extensions.vertex_input_dynamic_state) { - const u32 version = (properties.properties.driverVersion << 3) >> 3; - if ((driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS && version < VK_MAKE_API_VERSION(27, 20, 100, 0)) - || (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY && version < VK_MAKE_API_VERSION(580, 119, 02, 0))) { - LOG_WARNING(Render_Vulkan, "Disabling broken VK_EXT_vertex_input_dynamic_state"); - RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); - } - } - if (u32(Settings::values.dyna_state.GetValue()) == 0) { LOG_INFO(Render_Vulkan, "Extended Dynamic State disabled by user setting, clearing all EDS features"); - features.custom_border_color.customBorderColors = false; - features.custom_border_color.customBorderColorWithoutFormat = false; features.extended_dynamic_state.extendedDynamicState = false; features.extended_dynamic_state2.extendedDynamicState2 = false; - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; - features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask = false; - features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false; - features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = false; } // Return whether we were suitable. @@ -1165,15 +1117,9 @@ bool Device::GetSuitability(bool requires_swapchain) { void Device::RemoveUnsuitableExtensions() { // VK_EXT_custom_border_color - // Enable extension if driver supports it, then check individual features - // - customBorderColors: Required to use VK_BORDER_COLOR_FLOAT_CUSTOM_EXT - // - customBorderColorWithoutFormat: Optional, allows VK_FORMAT_UNDEFINED - // If only customBorderColors is available, we must provide a specific format if (extensions.custom_border_color) { // Verify that at least customBorderColors is available if (!features.custom_border_color.customBorderColors) { - LOG_WARNING(Render_Vulkan, - "VK_EXT_custom_border_color reported but customBorderColors feature not available, disabling"); extensions.custom_border_color = false; } } @@ -1196,7 +1142,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, @@ -1208,67 +1154,7 @@ void Device::RemoveUnsuitableExtensions() { features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - // VK_EXT_extended_dynamic_state3 - const bool supports_color_blend_enable = - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable; - const bool supports_color_blend_equation = - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation; - const bool supports_color_write_mask = - features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask; - dynamic_state3_blending = supports_color_blend_enable && supports_color_blend_equation && - supports_color_write_mask; - - const bool supports_depth_clamp_enable = - features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable; - const bool supports_logic_op_enable = - features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable; - const bool supports_line_raster_mode = - features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode && - extensions.line_rasterization && features.line_rasterization.rectangularLines; - const bool supports_conservative_raster_mode = - features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode && - extensions.conservative_rasterization; - const bool supports_line_stipple_enable = - features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable && - extensions.line_rasterization && features.line_rasterization.stippledRectangularLines; - const bool supports_alpha_to_coverage = - features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable; - const bool supports_alpha_to_one = - features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable && - features.features.alphaToOne; - - dynamic_state3_depth_clamp_enable = supports_depth_clamp_enable; - dynamic_state3_logic_op_enable = supports_logic_op_enable; - dynamic_state3_line_raster_mode = supports_line_raster_mode; - dynamic_state3_conservative_raster_mode = supports_conservative_raster_mode; - dynamic_state3_line_stipple_enable = supports_line_stipple_enable; - dynamic_state3_alpha_to_coverage = supports_alpha_to_coverage; - dynamic_state3_alpha_to_one = supports_alpha_to_one; - - dynamic_state3_enables = dynamic_state3_depth_clamp_enable || dynamic_state3_logic_op_enable || - dynamic_state3_line_raster_mode || - dynamic_state3_conservative_raster_mode || - dynamic_state3_line_stipple_enable || - dynamic_state3_alpha_to_coverage || dynamic_state3_alpha_to_one; - - extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables; - if (!extensions.extended_dynamic_state3) { - dynamic_state3_blending = false; - dynamic_state3_enables = false; - dynamic_state3_depth_clamp_enable = false; - dynamic_state3_logic_op_enable = false; - dynamic_state3_line_raster_mode = false; - dynamic_state3_conservative_raster_mode = false; - dynamic_state3_line_stipple_enable = false; - dynamic_state3_alpha_to_coverage = false; - dynamic_state3_alpha_to_one = false; - } - RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3, - features.extended_dynamic_state3, - 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 +1163,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,28 +1190,32 @@ 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 = - features.vertex_input_dynamic_state.vertexInputDynamicState; - RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state, - features.vertex_input_dynamic_state, - VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + // VK_EXT_provoking_vertex + extensions.provoking_vertex = features.provoking_vertex.provokingVertexLast; + RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, + features.provoking_vertex, + VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); + + // VK_EXT_line_rasterization + extensions.line_rasterization = features.line_rasterization.rectangularLines || + features.line_rasterization.bresenhamLines || + features.line_rasterization.smoothLines; + RemoveExtensionFeatureIfUnsuitable(extensions.line_rasterization, + features.line_rasterization, + VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME); + + // VK_EXT_conditional_rendering + extensions.conditional_rendering = features.conditional_rendering.conditionalRendering; + RemoveExtensionFeatureIfUnsuitable(extensions.conditional_rendering, + features.conditional_rendering, + VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME); // VK_EXT_multi_draw extensions.multi_draw = features.multi_draw.multiDraw; @@ -1377,35 +1252,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 +1271,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 +1279,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 d29a8cd3f3..2afb57def5 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -48,12 +48,12 @@ VK_DEFINE_HANDLE(VmaAllocator) // Define all features which may be used by the implementation and require an extension here. #define FOR_EACH_VK_FEATURE_EXT(FEATURE) \ + FEATURE(EXT, ConditionalRendering, CONDITIONAL_RENDERING, conditional_rendering) \ FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \ FEATURE(EXT, DepthBiasControl, DEPTH_BIAS_CONTROL, depth_bias_control) \ FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \ FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \ FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \ - FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \ FEATURE(EXT, 4444Formats, 4444_FORMATS, format_a4b4g4r4) \ FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \ FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \ @@ -63,8 +63,6 @@ VK_DEFINE_HANDLE(VmaAllocator) 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, \ @@ -76,7 +74,6 @@ VK_DEFINE_HANDLE(VmaAllocator) // Define miscellaneous extensions which may be used by the implementation here. #define FOR_EACH_VK_EXTENSION(EXTENSION) \ - EXTENSION(EXT, CONDITIONAL_RENDERING, conditional_rendering) \ EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \ EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \ EXTENSION(EXT, MEMORY_BUDGET, memory_budget) \ @@ -100,7 +97,6 @@ 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) \ @@ -125,13 +121,11 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION_NAME(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \ - EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ - EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \ EXTENSION_NAME(VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME) \ EXTENSION_NAME(VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME) \ EXTENSION_NAME(VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME) @@ -189,8 +183,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE_NAME(shader_float16_int8, shaderInt8) \ FEATURE_NAME(timeline_semaphore, timelineSemaphore) \ FEATURE_NAME(transform_feedback, transformFeedback) \ - FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) \ - FEATURE_NAME(vertex_input_dynamic_state, vertexInputDynamicState) + FEATURE_NAME(uniform_buffer_standard_layout, uniformBufferStandardLayout) // These features are not required but can be helpful for drivers that can use it. #define FOR_EACH_VK_OPTIONAL_FEATURE(FEATURE_NAME) \ @@ -448,7 +441,7 @@ public: return extensions.viewport_array2; } - /// Returns true if the device supporst VK_EXT_DESCRIPTOR_INDEXING + /// Returns true if the device supporst VK_EXT_descriptor_indexing. bool isExtDescriptorIndexingSupported() const { return extensions.descriptor_indexing; } @@ -478,11 +471,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; @@ -519,13 +507,11 @@ public: } /// Returns true if the device supports VK_EXT_shader_stencil_export. - /// Note: Most Mali/NVIDIA drivers don't support this. Use hardware blits as fallback. bool IsExtShaderStencilExportSupported() const { return extensions.shader_stencil_export; } - /// Returns true if depth/stencil operations can be performed efficiently. - /// Either through shader export or hardware blits. + /// Returns true if depth/stencil operations through shader export or hardware blits. bool CanPerformDepthStencilOperations() const { return extensions.shader_stencil_export || is_blit_depth24_stencil8_supported || is_blit_depth32_stencil8_supported; @@ -561,11 +547,14 @@ public: return extensions.transform_feedback; } - /// Returns true if the device supports VK_EXT_transform_feedback properly. bool AreTransformFeedbackGeometryStreamsSupported() const { return features.transform_feedback.geometryStreams; } + bool IsTransformFeedbackProvokingVertexPreserved() const { + return features.provoking_vertex.transformFeedbackPreservesProvokingVertex; + } + /// Returns true if the device supports VK_EXT_custom_border_color. bool IsExtCustomBorderColorSupported() const { return extensions.custom_border_color; @@ -611,6 +600,16 @@ public: return features.custom_border_color.customBorderColorWithoutFormat; } + /// Returns true if physical device custom border color properties were queried. + bool HasCustomBorderColorProperties() const { + return has_custom_border_color_properties; + } + + /// Returns the queried VkPhysicalDeviceCustomBorderColorPropertiesEXT. + const VkPhysicalDeviceCustomBorderColorPropertiesEXT& GetCustomBorderColorProperties() const { + return custom_border_color_properties; + } + /// Returns true if the device supports VK_EXT_extended_dynamic_state. bool IsExtExtendedDynamicStateSupported() const { return extensions.extended_dynamic_state; @@ -625,32 +624,21 @@ public: return features.extended_dynamic_state2.extendedDynamicState2LogicOp; } - /// Returns true if the device supports VK_EXT_extended_dynamic_state3. - bool IsExtExtendedDynamicState3Supported() const { - return extensions.extended_dynamic_state3; - } - /// Returns true if the device supports VK_EXT_4444_formats. bool IsExt4444FormatsSupported() const { return features.format_a4b4g4r4.formatA4B4G4R4; } - /// Returns true if the device supports VK_EXT_extended_dynamic_state3. - bool IsExtExtendedDynamicState3BlendingSupported() const { - return dynamic_state3_blending; - } - - /// Returns true if the device supports VK_EXT_extended_dynamic_state3. - bool IsExtExtendedDynamicState3EnablesSupported() const { - return dynamic_state3_enables; - } - - /// Returns true if the device supports VK_EXT_filter_cubic + /// Returns true if the device supports VK_EXT_filter_cubic. bool IsExtFilterCubicSupported() const { return extensions.filter_cubic; } - /// Returns true if the device supports VK_QCOM_filter_cubic_weights + /// Custom border color properties retrieved from the physical device (if available). + VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color_properties{}; + bool has_custom_border_color_properties = false; + + /// Returns true if the device supports VK_QCOM_filter_cubic_weights. bool IsQcomFilterCubicWeightsSupported() const { return extensions.filter_cubic_weights; } @@ -661,55 +649,36 @@ public: } bool SupportsRectangularLines() const { - return features.line_rasterization.rectangularLines != VK_FALSE; + return features.line_rasterization.rectangularLines; + } + + bool SupportsBresenhamLines() const { + return features.line_rasterization.bresenhamLines; } bool SupportsSmoothLines() const { - return features.line_rasterization.smoothLines != VK_FALSE; + return features.line_rasterization.smoothLines; } bool SupportsStippledRectangularLines() const { - return features.line_rasterization.stippledRectangularLines != VK_FALSE; + return features.line_rasterization.stippledRectangularLines; } + bool SupportsStippledBresenhamLines() const { + return features.line_rasterization.stippledBresenhamLines; + } + + bool SupportsStippledSmoothLines() const { + return features.line_rasterization.stippledSmoothLines; + } + + /// Returns true if the device supports AlphaToOne. bool SupportsAlphaToOne() const { - return features.features.alphaToOne != VK_FALSE; + return features.features.alphaToOne; } - bool SupportsDynamicState3DepthClampEnable() const { - return dynamic_state3_depth_clamp_enable; - } - bool SupportsDynamicState3LogicOpEnable() const { - return dynamic_state3_logic_op_enable; - } - - bool SupportsDynamicState3LineRasterizationMode() const { - return dynamic_state3_line_raster_mode; - } - - bool SupportsDynamicState3ConservativeRasterizationMode() const { - return dynamic_state3_conservative_raster_mode; - } - - bool SupportsDynamicState3LineStippleEnable() const { - return dynamic_state3_line_stipple_enable; - } - - bool SupportsDynamicState3AlphaToCoverageEnable() const { - return dynamic_state3_alpha_to_coverage; - } - - bool SupportsDynamicState3AlphaToOneEnable() const { - return dynamic_state3_alpha_to_one; - } - - /// Returns true if the device supports VK_EXT_vertex_input_dynamic_state. - bool IsExtVertexInputDynamicStateSupported() const { - return extensions.vertex_input_dynamic_state; - } - - /// Returns true if the device supports VK_EXT_shader_demote_to_helper_invocation + /// Returns true if the device supports VK_EXT_shader_demote_to_helper_invocation. bool IsExtShaderDemoteToHelperInvocationSupported() const { return extensions.shader_demote_to_helper_invocation; } @@ -718,21 +687,37 @@ public: bool IsExtConservativeRasterizationSupported() const { return extensions.conservative_rasterization; } + + /// Returns true if the device supports conservative rasterization for points and lines. + bool SupportsConservativePointAndLineRasterization() const { + return extensions.conservative_rasterization && + properties.conservative_rasterization.conservativePointAndLineRasterization; + } /// Returns true if the device supports VK_EXT_provoking_vertex. bool IsExtProvokingVertexSupported() const { return extensions.provoking_vertex; } + /// Returns true if the device supports provoking-vertex LAST mode. + bool IsProvokingVertexLastSupported() const { + return features.provoking_vertex.provokingVertexLast; + } + /// Returns true if the device supports VK_KHR_shader_atomic_int64. bool IsExtShaderAtomicInt64Supported() const { return extensions.shader_atomic_int64; } - bool IsExtConditionalRendering() const { + /// Returns true if the device supports VK_EXT_conditional_rendering. + bool IsExtConditionalRenderingSupported() const { return extensions.conditional_rendering; } + bool IsExtConditionalRendering() const { + return IsExtConditionalRenderingSupported(); + } + bool HasTimelineSemaphore() const; /// Returns the minimum supported version of SPIR-V. @@ -751,7 +736,7 @@ public: return has_renderdoc || has_nsight_graphics || has_radeon_gpu_profiler; } - /// @returns True if compute pipelines can cause crashing. + /// Returns true if compute pipelines can cause crashing. bool HasBrokenCompute() const { return has_broken_compute; } @@ -898,11 +883,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 && @@ -1027,6 +1007,7 @@ private: VkPhysicalDeviceSubgroupProperties subgroup_properties{}; VkPhysicalDeviceFloatControlsProperties float_controls{}; VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor{}; + VkPhysicalDeviceConservativeRasterizationPropertiesEXT conservative_rasterization{}; VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; @@ -1059,15 +1040,6 @@ private: bool supports_d24_depth{}; ///< Supports D24 depth buffers. bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation - bool dynamic_state3_blending{}; ///< Has blending features of dynamic_state3. - bool dynamic_state3_enables{}; ///< Has at least one enable feature of dynamic_state3. - bool dynamic_state3_depth_clamp_enable{}; - bool dynamic_state3_logic_op_enable{}; - bool dynamic_state3_line_raster_mode{}; - bool dynamic_state3_conservative_raster_mode{}; - bool dynamic_state3_line_stipple_enable{}; - 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). u64 device_access_memory{}; ///< Total size of device local memory in bytes. diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index 47e18dd6a5..9cf65ad252 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 926f48de89..f1854d4792 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -127,18 +127,20 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdPipelineBarrier); X(vkCmdPushConstants); X(vkCmdPushDescriptorSetWithTemplateKHR); + X(vkCmdResetQueryPool); X(vkCmdSetBlendConstants); X(vkCmdSetDepthBias); X(vkCmdSetDepthBias2EXT); X(vkCmdSetDepthBounds); X(vkCmdSetEvent); X(vkCmdSetScissor); + X(vkCmdSetScissorWithCountEXT); X(vkCmdSetStencilCompareMask); X(vkCmdSetStencilReference); X(vkCmdSetStencilWriteMask); X(vkCmdSetViewport); + X(vkCmdSetViewportWithCountEXT); X(vkCmdWaitEvents); - X(vkCmdBindVertexBuffers2EXT); X(vkCmdSetCullModeEXT); X(vkCmdSetDepthBoundsTestEnableEXT); X(vkCmdSetDepthCompareOpEXT); @@ -146,25 +148,15 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetDepthWriteEnableEXT); X(vkCmdSetPrimitiveRestartEnableEXT); X(vkCmdSetRasterizerDiscardEnableEXT); - X(vkCmdSetAlphaToCoverageEnableEXT); - X(vkCmdSetAlphaToOneEnableEXT); - X(vkCmdSetConservativeRasterizationModeEXT); - X(vkCmdSetLineRasterizationModeEXT); - X(vkCmdSetLineStippleEnableEXT); X(vkCmdSetDepthBiasEnableEXT); - X(vkCmdSetLogicOpEnableEXT); - X(vkCmdSetDepthClampEnableEXT); X(vkCmdSetFrontFaceEXT); X(vkCmdSetLogicOpEXT); X(vkCmdSetPatchControlPointsEXT); + X(vkCmdSetLineStippleEXT); X(vkCmdSetLineWidth); X(vkCmdSetPrimitiveTopologyEXT); X(vkCmdSetStencilOpEXT); X(vkCmdSetStencilTestEnableEXT); - X(vkCmdSetVertexInputEXT); - X(vkCmdSetColorWriteMaskEXT); - X(vkCmdSetColorBlendEnableEXT); - X(vkCmdSetColorBlendEquationEXT); X(vkCmdResolveImage); X(vkCreateBuffer); X(vkCreateBufferView); @@ -254,6 +246,15 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { Proc(dld.vkCmdDrawIndirectCount, dld, "vkCmdDrawIndirectCountKHR", device); Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device); } + if (!dld.vkCmdSetPrimitiveTopologyEXT) { + Proc(dld.vkCmdSetPrimitiveTopologyEXT, dld, "vkCmdSetPrimitiveTopology", device); + } + if (!dld.vkCmdSetViewportWithCountEXT) { + Proc(dld.vkCmdSetViewportWithCountEXT, dld, "vkCmdSetViewportWithCount", device); + } + if (!dld.vkCmdSetScissorWithCountEXT) { + Proc(dld.vkCmdSetScissorWithCountEXT, dld, "vkCmdSetScissorWithCount", device); + } #undef X } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 5c04132f7b..594194d788 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -200,7 +200,6 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdBindPipeline vkCmdBindPipeline{}; PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT{}; PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers{}; - PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT{}; PFN_vkCmdBlitImage vkCmdBlitImage{}; PFN_vkCmdClearAttachments vkCmdClearAttachments{}; PFN_vkCmdClearColorImage vkCmdClearColorImage{}; @@ -229,6 +228,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{}; PFN_vkCmdPushConstants vkCmdPushConstants{}; PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{}; + PFN_vkCmdResetQueryPool vkCmdResetQueryPool{}; PFN_vkCmdResolveImage vkCmdResolveImage{}; PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants{}; PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT{}; @@ -241,15 +241,8 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{}; PFN_vkCmdSetPrimitiveRestartEnableEXT vkCmdSetPrimitiveRestartEnableEXT{}; PFN_vkCmdSetRasterizerDiscardEnableEXT vkCmdSetRasterizerDiscardEnableEXT{}; - PFN_vkCmdSetAlphaToCoverageEnableEXT vkCmdSetAlphaToCoverageEnableEXT{}; - PFN_vkCmdSetAlphaToOneEnableEXT vkCmdSetAlphaToOneEnableEXT{}; - PFN_vkCmdSetConservativeRasterizationModeEXT vkCmdSetConservativeRasterizationModeEXT{}; - PFN_vkCmdSetLineRasterizationModeEXT vkCmdSetLineRasterizationModeEXT{}; - PFN_vkCmdSetLineStippleEnableEXT vkCmdSetLineStippleEnableEXT{}; PFN_vkCmdSetLineStippleEXT vkCmdSetLineStippleEXT{}; PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{}; - PFN_vkCmdSetLogicOpEnableEXT vkCmdSetLogicOpEnableEXT{}; - PFN_vkCmdSetDepthClampEnableEXT vkCmdSetDepthClampEnableEXT{}; PFN_vkCmdSetEvent vkCmdSetEvent{}; PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{}; PFN_vkCmdSetPatchControlPointsEXT vkCmdSetPatchControlPointsEXT{}; @@ -257,16 +250,14 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetLineWidth vkCmdSetLineWidth{}; PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{}; PFN_vkCmdSetScissor vkCmdSetScissor{}; + PFN_vkCmdSetScissorWithCountEXT vkCmdSetScissorWithCountEXT{}; PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask{}; PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT{}; PFN_vkCmdSetStencilReference vkCmdSetStencilReference{}; PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT{}; PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{}; - PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{}; PFN_vkCmdSetViewport vkCmdSetViewport{}; - PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{}; - PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{}; - PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{}; + PFN_vkCmdSetViewportWithCountEXT vkCmdSetViewportWithCountEXT{}; PFN_vkCmdWaitEvents vkCmdWaitEvents{}; PFN_vkCreateBuffer vkCreateBuffer{}; PFN_vkCreateBufferView vkCreateBufferView{}; @@ -1182,6 +1173,10 @@ public: dld->vkCmdPushDescriptorSetWithTemplateKHR(handle, update_template, layout, set, data); } + void ResetQueryPool(VkQueryPool query_pool, u32 first, u32 count) const noexcept { + dld->vkCmdResetQueryPool(handle, query_pool, first, count); + } + void BindPipeline(VkPipelineBindPoint bind_point, VkPipeline pipeline) const noexcept { dld->vkCmdBindPipeline(handle, bind_point, pipeline); } @@ -1374,6 +1369,18 @@ public: dld->vkCmdSetScissor(handle, first, scissors.size(), scissors.data()); } + void SetViewportWithCountEXT(Span viewports) const noexcept { + if (dld && dld->vkCmdSetViewportWithCountEXT) { + dld->vkCmdSetViewportWithCountEXT(handle, viewports.size(), viewports.data()); + } + } + + void SetScissorWithCountEXT(Span scissors) const noexcept { + if (dld && dld->vkCmdSetScissorWithCountEXT) { + dld->vkCmdSetScissorWithCountEXT(handle, scissors.size(), scissors.data()); + } + } + void SetBlendConstants(const float blend_constants[4]) const noexcept { dld->vkCmdSetBlendConstants(handle, blend_constants); } @@ -1403,7 +1410,9 @@ public: .depthBiasClamp = clamp, .depthBiasSlopeFactor = slope_factor, }; - dld->vkCmdSetDepthBias2EXT(handle, &info); + if (dld && dld->vkCmdSetDepthBias2EXT) { + dld->vkCmdSetDepthBias2EXT(handle, &info); + } } void SetDepthBounds(float min_depth_bounds, float max_depth_bounds) const noexcept { @@ -1423,104 +1432,76 @@ public: buffer_barriers.data(), image_barriers.size(), image_barriers.data()); } - void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers, - const VkDeviceSize* offsets, const VkDeviceSize* sizes, - const VkDeviceSize* strides) const noexcept { - dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets, - sizes, strides); - } - void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept { - dld->vkCmdSetCullModeEXT(handle, cull_mode); + if (dld && dld->vkCmdSetCullModeEXT) { + dld->vkCmdSetCullModeEXT(handle, cull_mode); + } } void SetDepthBoundsTestEnableEXT(bool enable) const noexcept { - dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + if (dld && dld->vkCmdSetDepthBoundsTestEnableEXT) { + dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } } void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept { - dld->vkCmdSetDepthCompareOpEXT(handle, compare_op); + if (dld && dld->vkCmdSetDepthCompareOpEXT) { + dld->vkCmdSetDepthCompareOpEXT(handle, compare_op); + } } void SetDepthTestEnableEXT(bool enable) const noexcept { - dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + if (dld && dld->vkCmdSetDepthTestEnableEXT) { + dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } } void SetDepthWriteEnableEXT(bool enable) const noexcept { - dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + if (dld && dld->vkCmdSetDepthWriteEnableEXT) { + dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } } void SetPrimitiveRestartEnableEXT(bool enable) const noexcept { - dld->vkCmdSetPrimitiveRestartEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + if (dld && dld->vkCmdSetPrimitiveRestartEnableEXT) { + dld->vkCmdSetPrimitiveRestartEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } } void SetRasterizerDiscardEnableEXT(bool enable) const noexcept { - dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + if (dld && dld->vkCmdSetRasterizerDiscardEnableEXT) { + dld->vkCmdSetRasterizerDiscardEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } } - void SetConservativeRasterizationModeEXT(VkConservativeRasterizationModeEXT mode) const noexcept - { - dld->vkCmdSetConservativeRasterizationModeEXT(handle, mode); - } - - void SetLineRasterizationModeEXT(VkLineRasterizationModeEXT mode) const noexcept - { - dld->vkCmdSetLineRasterizationModeEXT(handle, mode); - } - - void SetLineStippleEnableEXT(bool enable) const noexcept - { - dld->vkCmdSetLineStippleEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); - } - - void SetLineStippleEXT(u32 factor, u16 pattern) const noexcept - { - dld->vkCmdSetLineStippleEXT(handle, factor, pattern); + void SetLineStippleEXT(u32 factor, u16 pattern) const noexcept { + if (dld && dld->vkCmdSetLineStippleEXT) { + dld->vkCmdSetLineStippleEXT(handle, factor, pattern); + } } void SetDepthBiasEnableEXT(bool enable) const noexcept { - dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); - } - - void SetLogicOpEnableEXT(bool enable) const noexcept { - dld->vkCmdSetLogicOpEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); - } - - void SetAlphaToCoverageEnableEXT(bool enable) const noexcept { - dld->vkCmdSetAlphaToCoverageEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); - } - - void SetAlphaToOneEnableEXT(bool enable) const noexcept { - dld->vkCmdSetAlphaToOneEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); - } - - void SetDepthClampEnableEXT(bool enable) const noexcept { - dld->vkCmdSetDepthClampEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + if (dld && dld->vkCmdSetDepthBiasEnableEXT) { + dld->vkCmdSetDepthBiasEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } } void SetFrontFaceEXT(VkFrontFace front_face) const noexcept { - dld->vkCmdSetFrontFaceEXT(handle, front_face); + if (dld && dld->vkCmdSetFrontFaceEXT) { + dld->vkCmdSetFrontFaceEXT(handle, front_face); + } } void SetLogicOpEXT(VkLogicOp logic_op) const noexcept { - dld->vkCmdSetLogicOpEXT(handle, logic_op); + if (dld && dld->vkCmdSetLogicOpEXT) { + dld->vkCmdSetLogicOpEXT(handle, logic_op); + } } void SetPatchControlPointsEXT(uint32_t patch_control_points) const noexcept { - dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points); - } - - void SetColorWriteMaskEXT(u32 first, Span masks) const noexcept { - dld->vkCmdSetColorWriteMaskEXT(handle, first, masks.size(), masks.data()); - } - - void SetColorBlendEnableEXT(u32 first, Span enables) const noexcept { - dld->vkCmdSetColorBlendEnableEXT(handle, first, enables.size(), enables.data()); - } - - void SetColorBlendEquationEXT(u32 first, - Span equations) const noexcept { - dld->vkCmdSetColorBlendEquationEXT(handle, first, equations.size(), equations.data()); + if (dld && dld->vkCmdSetPatchControlPointsEXT) { + dld->vkCmdSetPatchControlPointsEXT(handle, patch_control_points); + } } void SetLineWidth(float line_width) const noexcept { @@ -1528,52 +1509,61 @@ public: } void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept { - dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology); + if (dld && dld->vkCmdSetPrimitiveTopologyEXT) { + dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology); + } } void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op, VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept { - dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op); + if (dld && dld->vkCmdSetStencilOpEXT) { + dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op); + } } void SetStencilTestEnableEXT(bool enable) const noexcept { - dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); - } - - void SetVertexInputEXT( - vk::Span bindings, - vk::Span attributes) const noexcept { - dld->vkCmdSetVertexInputEXT(handle, bindings.size(), bindings.data(), attributes.size(), - attributes.data()); + if (dld && dld->vkCmdSetStencilTestEnableEXT) { + dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE); + } } void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, const VkDeviceSize* offsets, const VkDeviceSize* sizes) const noexcept { - dld->vkCmdBindTransformFeedbackBuffersEXT(handle, first, count, buffers, offsets, sizes); + if (dld && dld->vkCmdBindTransformFeedbackBuffersEXT) { + dld->vkCmdBindTransformFeedbackBuffersEXT(handle, first, count, buffers, offsets, sizes); + } } void BeginTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count, const VkBuffer* counter_buffers, const VkDeviceSize* counter_buffer_offsets) const noexcept { - dld->vkCmdBeginTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count, - counter_buffers, counter_buffer_offsets); + if (dld && dld->vkCmdBeginTransformFeedbackEXT) { + dld->vkCmdBeginTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count, + counter_buffers, counter_buffer_offsets); + } } void EndTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count, const VkBuffer* counter_buffers, const VkDeviceSize* counter_buffer_offsets) const noexcept { - dld->vkCmdEndTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count, - counter_buffers, counter_buffer_offsets); + if (dld && dld->vkCmdEndTransformFeedbackEXT) { + dld->vkCmdEndTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count, + counter_buffers, counter_buffer_offsets); + } } void BeginConditionalRenderingEXT( const VkConditionalRenderingBeginInfoEXT& info) const noexcept { - dld->vkCmdBeginConditionalRenderingEXT(handle, &info); + if (dld && dld->vkCmdBeginConditionalRenderingEXT) { + dld->vkCmdBeginConditionalRenderingEXT(handle, &info); + } } void EndConditionalRenderingEXT() const noexcept { - dld->vkCmdEndConditionalRenderingEXT(handle); + if (dld && dld->vkCmdEndConditionalRenderingEXT) { + dld->vkCmdEndConditionalRenderingEXT(handle); + } } void BeginDebugUtilsLabelEXT(const char* label, std::span color) const noexcept { @@ -1583,11 +1573,15 @@ public: .pLabelName = label, .color{color[0], color[1], color[2], color[3]}, }; - dld->vkCmdBeginDebugUtilsLabelEXT(handle, &label_info); + if (dld && dld->vkCmdBeginDebugUtilsLabelEXT) { + dld->vkCmdBeginDebugUtilsLabelEXT(handle, &label_info); + } } void EndDebugUtilsLabelEXT() const noexcept { - dld->vkCmdEndDebugUtilsLabelEXT(handle); + if (dld && dld->vkCmdEndDebugUtilsLabelEXT) { + dld->vkCmdEndDebugUtilsLabelEXT(handle); + } } private: