summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/shader_recompiler/CMakeLists.txt2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp9
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_vote.cpp58
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp16
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h5
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc6
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/vote.cpp52
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp6
-rw-r--r--src/shader_recompiler/profile.h2
-rw-r--r--src/shader_recompiler/shader_info.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp7
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h6
18 files changed, 182 insertions, 6 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 23cb523a8..086bdf8d0 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -15,6 +15,7 @@ add_library(shader_recompiler STATIC
15 backend/spirv/emit_spirv_memory.cpp 15 backend/spirv/emit_spirv_memory.cpp
16 backend/spirv/emit_spirv_select.cpp 16 backend/spirv/emit_spirv_select.cpp
17 backend/spirv/emit_spirv_undefined.cpp 17 backend/spirv/emit_spirv_undefined.cpp
18 backend/spirv/emit_spirv_vote.cpp
18 environment.h 19 environment.h
19 exception.h 20 exception.h
20 file_environment.cpp 21 file_environment.cpp
@@ -122,6 +123,7 @@ add_library(shader_recompiler STATIC
122 frontend/maxwell/translate/impl/select_source_with_predicate.cpp 123 frontend/maxwell/translate/impl/select_source_with_predicate.cpp
123 frontend/maxwell/translate/impl/texture_fetch.cpp 124 frontend/maxwell/translate/impl/texture_fetch.cpp
124 frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp 125 frontend/maxwell/translate/impl/texture_fetch_swizzled.cpp
126 frontend/maxwell/translate/impl/vote.cpp
125 frontend/maxwell/translate/translate.cpp 127 frontend/maxwell/translate/translate.cpp
126 frontend/maxwell/translate/translate.h 128 frontend/maxwell/translate/translate.h
127 ir_opt/collect_shader_info_pass.cpp 129 ir_opt/collect_shader_info_pass.cpp
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 4a4de3676..36f130781 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -259,6 +259,10 @@ void EmitContext::DefineInputs(const Info& info, Stage stage) {
259 if (info.uses_local_invocation_id) { 259 if (info.uses_local_invocation_id) {
260 local_invocation_id = DefineInput(*this, U32[3], spv::BuiltIn::LocalInvocationId); 260 local_invocation_id = DefineInput(*this, U32[3], spv::BuiltIn::LocalInvocationId);
261 } 261 }
262 if (profile.warp_size_potentially_larger_than_guest && info.uses_subgroup_vote) {
263 subgroup_local_invocation_id =
264 DefineInput(*this, U32[1], spv::BuiltIn::SubgroupLocalInvocationId);
265 }
262 if (info.loads_position) { 266 if (info.loads_position) {
263 const bool is_fragment{stage != Stage::Fragment}; 267 const bool is_fragment{stage != Stage::Fragment};
264 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord}; 268 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index 9b9e0d6b1..6e64360bf 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -82,6 +82,7 @@ public:
82 82
83 Id workgroup_id{}; 83 Id workgroup_id{};
84 Id local_invocation_id{}; 84 Id local_invocation_id{};
85 Id subgroup_local_invocation_id{};
85 Id instance_id{}; 86 Id instance_id{};
86 Id instance_index{}; 87 Id instance_index{};
87 Id base_instance{}; 88 Id base_instance{};
@@ -96,7 +97,7 @@ public:
96 std::array<Id, 32> output_generics{}; 97 std::array<Id, 32> output_generics{};
97 98
98 std::array<Id, 8> frag_color{}; 99 std::array<Id, 8> frag_color{};
99 Id frag_depth {}; 100 Id frag_depth{};
100 101
101 std::vector<Id> interfaces; 102 std::vector<Id> interfaces;
102 103
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 93e851133..107403912 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -224,6 +224,15 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
224 ctx.AddExtension("SPV_KHR_shader_draw_parameters"); 224 ctx.AddExtension("SPV_KHR_shader_draw_parameters");
225 ctx.AddCapability(spv::Capability::DrawParameters); 225 ctx.AddCapability(spv::Capability::DrawParameters);
226 } 226 }
227 if (info.uses_subgroup_vote && profile.support_vote) {
228 ctx.AddExtension("SPV_KHR_shader_ballot");
229 ctx.AddCapability(spv::Capability::SubgroupBallotKHR);
230 if (!profile.warp_size_potentially_larger_than_guest) {
231 // vote ops are only used when not taking the long path
232 ctx.AddExtension("SPV_KHR_subgroup_vote");
233 ctx.AddCapability(spv::Capability::SubgroupVoteKHR);
234 }
235 }
227 // TODO: Track this usage 236 // TODO: Track this usage
228 ctx.AddCapability(spv::Capability::ImageGatherExtended); 237 ctx.AddCapability(spv::Capability::ImageGatherExtended);
229} 238}
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 960d022ff..ce23200f2 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -346,5 +346,9 @@ Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
346 Id coords, Id dref, Id bias_lc, Id offset); 346 Id coords, Id dref, Id bias_lc, Id offset);
347Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, 347Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
348 Id coords, Id dref, Id lod_lc, Id offset); 348 Id coords, Id dref, Id lod_lc, Id offset);
349Id EmitVoteAll(EmitContext& ctx, Id pred);
350Id EmitVoteAny(EmitContext& ctx, Id pred);
351Id EmitVoteEqual(EmitContext& ctx, Id pred);
352Id EmitSubgroupBallot(EmitContext& ctx, Id pred);
349 353
350} // namespace Shader::Backend::SPIRV 354} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_vote.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_vote.cpp
new file mode 100644
index 000000000..a63677ef2
--- /dev/null
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_vote.cpp
@@ -0,0 +1,58 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/backend/spirv/emit_spirv.h"
6
7namespace Shader::Backend::SPIRV {
8namespace {
9Id LargeWarpBallot(EmitContext& ctx, Id ballot) {
10 const Id shift{ctx.Constant(ctx.U32[1], 5)};
11 const Id local_index{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
12 return ctx.OpVectorExtractDynamic(ctx.U32[1], ballot, local_index);
13}
14} // Anonymous namespace
15
16Id EmitVoteAll(EmitContext& ctx, Id pred) {
17 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
18 return ctx.OpSubgroupAllKHR(ctx.U1, pred);
19 }
20 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
21 const Id active_mask{LargeWarpBallot(ctx, mask_ballot)};
22 const Id ballot{LargeWarpBallot(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
23 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
24 return ctx.OpIEqual(ctx.U1, lhs, active_mask);
25}
26
27Id EmitVoteAny(EmitContext& ctx, Id pred) {
28 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
29 return ctx.OpSubgroupAnyKHR(ctx.U1, pred);
30 }
31 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
32 const Id active_mask{LargeWarpBallot(ctx, mask_ballot)};
33 const Id ballot{LargeWarpBallot(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
34 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
35 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value);
36}
37
38Id EmitVoteEqual(EmitContext& ctx, Id pred) {
39 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
40 return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred);
41 }
42 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)};
43 const Id active_mask{LargeWarpBallot(ctx, mask_ballot)};
44 const Id ballot{LargeWarpBallot(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))};
45 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)};
46 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value),
47 ctx.OpIEqual(ctx.U1, lhs, active_mask));
48}
49
50Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
51 const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)};
52 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
53 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
54 }
55 return LargeWarpBallot(ctx, ballot);
56}
57
58} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index 432dd29a5..ff2970125 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -1444,4 +1444,20 @@ F32 IREmitter::ImageSampleDrefExplicitLod(const Value& handle, const Value& coor
1444 return Inst<F32>(op, Flags{info}, handle, coords, dref, lod_lc, offset); 1444 return Inst<F32>(op, Flags{info}, handle, coords, dref, lod_lc, offset);
1445} 1445}
1446 1446
1447U1 IREmitter::VoteAll(const U1& value) {
1448 return Inst<U1>(Opcode::VoteAll, value);
1449}
1450
1451U1 IREmitter::VoteAny(const U1& value) {
1452 return Inst<U1>(Opcode::VoteAny, value);
1453}
1454
1455U1 IREmitter::VoteEqual(const U1& value) {
1456 return Inst<U1>(Opcode::VoteEqual, value);
1457}
1458
1459U32 IREmitter::SubgroupBallot(const U1& value) {
1460 return Inst<U32>(Opcode::SubgroupBallot, value);
1461}
1462
1447} // namespace Shader::IR 1463} // namespace Shader::IR
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 346cef3ab..1708be3ef 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -234,6 +234,11 @@ public:
234 const Value& offset, const F32& lod_clamp, 234 const Value& offset, const F32& lod_clamp,
235 TextureInstInfo info); 235 TextureInstInfo info);
236 236
237 [[nodiscard]] U1 VoteAll(const U1& value);
238 [[nodiscard]] U1 VoteAny(const U1& value);
239 [[nodiscard]] U1 VoteEqual(const U1& value);
240 [[nodiscard]] U32 SubgroupBallot(const U1& value);
241
237private: 242private:
238 IR::Block::iterator insertion_point; 243 IR::Block::iterator insertion_point;
239 244
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index bdc07b9a7..fe888b8b2 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -355,3 +355,9 @@ OPCODE(ImageSampleImplicitLod, F32x4, U32,
355OPCODE(ImageSampleExplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) 355OPCODE(ImageSampleExplicitLod, F32x4, U32, Opaque, Opaque, Opaque, )
356OPCODE(ImageSampleDrefImplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) 356OPCODE(ImageSampleDrefImplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, )
357OPCODE(ImageSampleDrefExplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) 357OPCODE(ImageSampleDrefExplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, )
358
359// Vote operations
360OPCODE(VoteAll, U1, U1, )
361OPCODE(VoteAny, U1, U1, )
362OPCODE(VoteEqual, U1, U1, )
363OPCODE(SubgroupBallot, U32, U1, )
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
index 27b12ff3c..c0e36a7e2 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
@@ -417,10 +417,6 @@ void TranslatorVisitor::VMNMX(u64) {
417 ThrowNotImplemented(Opcode::VMNMX); 417 ThrowNotImplemented(Opcode::VMNMX);
418} 418}
419 419
420void TranslatorVisitor::VOTE(u64) {
421 ThrowNotImplemented(Opcode::VOTE);
422}
423
424void TranslatorVisitor::VOTE_vtg(u64) { 420void TranslatorVisitor::VOTE_vtg(u64) {
425 ThrowNotImplemented(Opcode::VOTE_vtg); 421 ThrowNotImplemented(Opcode::VOTE_vtg);
426} 422}
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/vote.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/vote.cpp
new file mode 100644
index 000000000..a88894a7e
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/vote.cpp
@@ -0,0 +1,52 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <optional>
6
7#include "common/bit_field.h"
8#include "common/common_types.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10
11namespace Shader::Maxwell {
12namespace {
13enum class VoteOp : u64 {
14 ALL,
15 ANY,
16 EQ,
17};
18
19[[nodiscard]] IR::U1 VoteOperation(IR::IREmitter& ir, const IR::U1& pred, VoteOp vote_op) {
20 switch (vote_op) {
21 case VoteOp::ALL:
22 return ir.VoteAll(pred);
23 case VoteOp::ANY:
24 return ir.VoteAny(pred);
25 case VoteOp::EQ:
26 return ir.VoteEqual(pred);
27 default:
28 throw NotImplementedException("Invalid VOTE op {}", vote_op);
29 }
30}
31
32void Vote(TranslatorVisitor& v, u64 insn) {
33 union {
34 u64 insn;
35 BitField<0, 8, IR::Reg> dest_reg;
36 BitField<39, 3, IR::Pred> pred_a;
37 BitField<42, 1, u64> neg_pred_a;
38 BitField<45, 3, IR::Pred> pred_b;
39 BitField<48, 2, VoteOp> vote_op;
40 } const vote{insn};
41
42 const IR::U1 vote_pred{v.ir.GetPred(vote.pred_a, vote.neg_pred_a != 0)};
43 v.ir.SetPred(vote.pred_b, VoteOperation(v.ir, vote_pred, vote.vote_op));
44 v.X(vote.dest_reg, v.ir.SubgroupBallot(vote_pred));
45}
46} // Anonymous namespace
47
48void TranslatorVisitor::VOTE(u64 insn) {
49 Vote(*this, insn);
50}
51
52} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index f44eac5d8..db5138e4d 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -359,6 +359,12 @@ void VisitUsages(Info& info, IR::Inst& inst) {
359 inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr; 359 inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
360 break; 360 break;
361 } 361 }
362 case IR::Opcode::VoteAll:
363 case IR::Opcode::VoteAny:
364 case IR::Opcode::VoteEqual:
365 case IR::Opcode::SubgroupBallot:
366 info.uses_subgroup_vote = true;
367 break;
362 default: 368 default:
363 break; 369 break;
364 } 370 }
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 3181c79fb..b57cbc310 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -19,6 +19,8 @@ struct Profile {
19 bool support_fp16_signed_zero_nan_preserve{}; 19 bool support_fp16_signed_zero_nan_preserve{};
20 bool support_fp32_signed_zero_nan_preserve{}; 20 bool support_fp32_signed_zero_nan_preserve{};
21 bool support_fp64_signed_zero_nan_preserve{}; 21 bool support_fp64_signed_zero_nan_preserve{};
22 bool support_vote{};
23 bool warp_size_potentially_larger_than_guest{};
22 24
23 // FClamp is broken and OpFMax + OpFMin should be used instead 25 // FClamp is broken and OpFMax + OpFMin should be used instead
24 bool has_broken_spirv_clamp{}; 26 bool has_broken_spirv_clamp{};
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index f97730b34..3d9f04d1a 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -80,6 +80,7 @@ struct Info {
80 bool uses_sampled_1d{}; 80 bool uses_sampled_1d{};
81 bool uses_sparse_residency{}; 81 bool uses_sparse_residency{};
82 bool uses_demote_to_helper_invocation{}; 82 bool uses_demote_to_helper_invocation{};
83 bool uses_subgroup_vote{};
83 84
84 IR::Type used_constant_buffer_types{}; 85 IR::Type used_constant_buffer_types{};
85 86
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 6684d37a6..8e544d745 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -36,13 +36,18 @@ ComputePipeline::ComputePipeline(const Device& device, VKDescriptorPool& descrip
36 descriptor_update_template = std::move(tuple.descriptor_update_template); 36 descriptor_update_template = std::move(tuple.descriptor_update_template);
37 descriptor_allocator = DescriptorAllocator(descriptor_pool, *descriptor_set_layout); 37 descriptor_allocator = DescriptorAllocator(descriptor_pool, *descriptor_set_layout);
38 38
39 const VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci{
40 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT,
41 .pNext = nullptr,
42 .requiredSubgroupSize = GuestWarpSize,
43 };
39 pipeline = device.GetLogical().CreateComputePipeline({ 44 pipeline = device.GetLogical().CreateComputePipeline({
40 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, 45 .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
41 .pNext = nullptr, 46 .pNext = nullptr,
42 .flags = 0, 47 .flags = 0,
43 .stage{ 48 .stage{
44 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 49 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
45 .pNext = nullptr, 50 .pNext = device.IsExtSubgroupSizeControlSupported() ? &subgroup_size_ci : nullptr,
46 .flags = 0, 51 .flags = 0,
47 .stage = VK_SHADER_STAGE_COMPUTE_BIT, 52 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
48 .module = *spv_module, 53 .module = *spv_module,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 41fc9588f..bdbc8dd1e 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -455,6 +455,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::GPU& gpu_,
455 float_control.shaderSignedZeroInfNanPreserveFloat32 != VK_FALSE, 455 float_control.shaderSignedZeroInfNanPreserveFloat32 != VK_FALSE,
456 .support_fp64_signed_zero_nan_preserve = 456 .support_fp64_signed_zero_nan_preserve =
457 float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE, 457 float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
458 .support_vote = true,
459 .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
458 .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR, 460 .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR,
459 }; 461 };
460} 462}
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index f0e5b098c..009b74f12 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -737,6 +737,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
737 subgroup_properties.maxSubgroupSize >= GuestWarpSize) { 737 subgroup_properties.maxSubgroupSize >= GuestWarpSize) {
738 extensions.push_back(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); 738 extensions.push_back(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
739 guest_warp_stages = subgroup_properties.requiredSubgroupSizeStages; 739 guest_warp_stages = subgroup_properties.requiredSubgroupSizeStages;
740 ext_subgroup_size_control = true;
740 } 741 }
741 } else { 742 } else {
742 is_warp_potentially_bigger = true; 743 is_warp_potentially_bigger = true;
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 82bccc8f0..c268a4f8d 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -193,6 +193,11 @@ public:
193 return ext_shader_viewport_index_layer; 193 return ext_shader_viewport_index_layer;
194 } 194 }
195 195
196 /// Returns true if the device supports VK_EXT_subgroup_size_control.
197 bool IsExtSubgroupSizeControlSupported() const {
198 return ext_subgroup_size_control;
199 }
200
196 /// Returns true if the device supports VK_EXT_transform_feedback. 201 /// Returns true if the device supports VK_EXT_transform_feedback.
197 bool IsExtTransformFeedbackSupported() const { 202 bool IsExtTransformFeedbackSupported() const {
198 return ext_transform_feedback; 203 return ext_transform_feedback;
@@ -297,6 +302,7 @@ private:
297 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. 302 bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.
298 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 303 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
299 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info. 304 bool ext_tooling_info{}; ///< Support for VK_EXT_tooling_info.
305 bool ext_subgroup_size_control{}; ///< Support for VK_EXT_subgroup_size_control.
300 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 306 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
301 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. 307 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
302 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state. 308 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.