summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h22
-rw-r--r--src/video_core/dma_pusher.cpp14
-rw-r--r--src/video_core/dma_pusher.h1
-rw-r--r--src/video_core/engines/draw_manager.cpp21
-rw-r--r--src/video_core/engines/draw_manager.h20
-rw-r--r--src/video_core/engines/engine_interface.h2
-rw-r--r--src/video_core/macro/macro_hle.cpp53
-rw-r--r--src/video_core/rasterizer_interface.h3
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp57
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h15
13 files changed, 169 insertions, 47 deletions
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index f1c60d1f3..99abe0edf 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -170,6 +170,9 @@ public:
170 void BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size, PixelFormat format, 170 void BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size, PixelFormat format,
171 bool is_written, bool is_image); 171 bool is_written, bool is_image);
172 172
173 [[nodiscard]] std::pair<Buffer*, u32> ObtainBuffer(GPUVAddr gpu_addr, u32 size,
174 bool synchronize, bool mark_as_written);
175
173 void FlushCachedWrites(); 176 void FlushCachedWrites();
174 177
175 /// Return true when there are uncommitted buffers to be downloaded 178 /// Return true when there are uncommitted buffers to be downloaded
@@ -791,6 +794,25 @@ void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_add
791} 794}
792 795
793template <class P> 796template <class P>
797std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_addr, u32 size,
798 bool synchronize,
799 bool mark_as_written) {
800 const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
801 if (!cpu_addr) {
802 return {&slot_buffers[NULL_BUFFER_ID], 0};
803 }
804 const BufferId buffer_id = FindBuffer(*cpu_addr, size);
805 Buffer& buffer = slot_buffers[buffer_id];
806 if (synchronize) {
807 SynchronizeBuffer(buffer, *cpu_addr, size);
808 }
809 if (mark_as_written) {
810 MarkWrittenBuffer(buffer_id, *cpu_addr, size);
811 }
812 return {&buffer, buffer.Offset(*cpu_addr)};
813}
814
815template <class P>
794void BufferCache<P>::FlushCachedWrites() { 816void BufferCache<P>::FlushCachedWrites() {
795 for (const BufferId buffer_id : cached_write_buffer_ids) { 817 for (const BufferId buffer_id : cached_write_buffer_ids) {
796 slot_buffers[buffer_id].FlushCachedWrites(); 818 slot_buffers[buffer_id].FlushCachedWrites();
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 322de2606..eb1371612 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -61,7 +61,7 @@ bool DmaPusher::Step() {
61 } else { 61 } else {
62 const CommandListHeader command_list_header{ 62 const CommandListHeader command_list_header{
63 command_list.command_lists[dma_pushbuffer_subindex++]}; 63 command_list.command_lists[dma_pushbuffer_subindex++]};
64 const GPUVAddr dma_get = command_list_header.addr; 64 dma_state.dma_get = command_list_header.addr;
65 65
66 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) { 66 if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
67 // We've gone through the current list, remove it from the queue 67 // We've gone through the current list, remove it from the queue
@@ -75,11 +75,11 @@ bool DmaPusher::Step() {
75 75
76 // Push buffer non-empty, read a word 76 // Push buffer non-empty, read a word
77 command_headers.resize_destructive(command_list_header.size); 77 command_headers.resize_destructive(command_list_header.size);
78 if (Settings::IsGPULevelHigh()) { 78 if (Settings::IsGPULevelExtreme()) {
79 memory_manager.ReadBlock(dma_get, command_headers.data(), 79 memory_manager.ReadBlock(dma_state.dma_get, command_headers.data(),
80 command_list_header.size * sizeof(u32)); 80 command_list_header.size * sizeof(u32));
81 } else { 81 } else {
82 memory_manager.ReadBlockUnsafe(dma_get, command_headers.data(), 82 memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(),
83 command_list_header.size * sizeof(u32)); 83 command_list_header.size * sizeof(u32));
84 } 84 }
85 ProcessCommands(command_headers); 85 ProcessCommands(command_headers);
@@ -174,8 +174,10 @@ void DmaPusher::CallMultiMethod(const u32* base_start, u32 num_methods) const {
174 puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods, 174 puller.CallMultiMethod(dma_state.method, dma_state.subchannel, base_start, num_methods,
175 dma_state.method_count); 175 dma_state.method_count);
176 } else { 176 } else {
177 subchannels[dma_state.subchannel]->CallMultiMethod(dma_state.method, base_start, 177 auto subchannel = subchannels[dma_state.subchannel];
178 num_methods, dma_state.method_count); 178 subchannel->current_dma_segment = dma_state.dma_get;
179 subchannel->CallMultiMethod(dma_state.method, base_start, num_methods,
180 dma_state.method_count);
179 } 181 }
180} 182}
181 183
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 6f00de937..ca0899ba7 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -156,6 +156,7 @@ private:
156 u32 subchannel; ///< Current subchannel 156 u32 subchannel; ///< Current subchannel
157 u32 method_count; ///< Current method count 157 u32 method_count; ///< Current method count
158 u32 length_pending; ///< Large NI command length pending 158 u32 length_pending; ///< Large NI command length pending
159 GPUVAddr dma_get; ///< Currently read segment
159 bool non_incrementing; ///< Current command's NI flag 160 bool non_incrementing; ///< Current command's NI flag
160 bool is_last_call; 161 bool is_last_call;
161 }; 162 };
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index 3a78421f6..4fa77b684 100644
--- a/src/video_core/engines/draw_manager.cpp
+++ b/src/video_core/engines/draw_manager.cpp
@@ -91,6 +91,16 @@ void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 ind
91 ProcessDraw(true, num_instances); 91 ProcessDraw(true, num_instances);
92} 92}
93 93
94void DrawManager::DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first, u32 index_count) {
95 const auto& regs{maxwell3d->regs};
96 draw_state.topology = topology;
97 draw_state.index_buffer = regs.index_buffer;
98 draw_state.index_buffer.first = index_first;
99 draw_state.index_buffer.count = index_count;
100
101 ProcessDrawIndirect(true);
102}
103
94void DrawManager::SetInlineIndexBuffer(u32 index) { 104void DrawManager::SetInlineIndexBuffer(u32 index) {
95 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff)); 105 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>(index & 0x000000ff));
96 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8)); 106 draw_state.inline_index_draw_indexes.push_back(static_cast<u8>((index & 0x0000ff00) >> 8));
@@ -198,4 +208,15 @@ void DrawManager::ProcessDraw(bool draw_indexed, u32 instance_count) {
198 maxwell3d->rasterizer->Draw(draw_indexed, instance_count); 208 maxwell3d->rasterizer->Draw(draw_indexed, instance_count);
199 } 209 }
200} 210}
211
212void DrawManager::ProcessDrawIndirect(bool draw_indexed) {
213 LOG_TRACE(HW_GPU, "called, topology={}, count={}", draw_state.topology,
214 draw_indexed ? draw_state.index_buffer.count : draw_state.vertex_buffer.count);
215
216 UpdateTopology();
217
218 if (maxwell3d->ShouldExecute()) {
219 maxwell3d->rasterizer->DrawIndirect(draw_indexed);
220 }
221}
201} // namespace Tegra::Engines 222} // namespace Tegra::Engines
diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h
index 0e6930a9c..0cdb37f83 100644
--- a/src/video_core/engines/draw_manager.h
+++ b/src/video_core/engines/draw_manager.h
@@ -32,6 +32,13 @@ public:
32 std::vector<u8> inline_index_draw_indexes; 32 std::vector<u8> inline_index_draw_indexes;
33 }; 33 };
34 34
35 struct IndirectParams {
36 GPUVAddr start_address;
37 size_t buffer_size;
38 size_t max_draw_counts;
39 size_t stride;
40 };
41
35 explicit DrawManager(Maxwell3D* maxwell_3d); 42 explicit DrawManager(Maxwell3D* maxwell_3d);
36 43
37 void ProcessMethodCall(u32 method, u32 argument); 44 void ProcessMethodCall(u32 method, u32 argument);
@@ -46,10 +53,20 @@ public:
46 void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, 53 void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
47 u32 base_instance, u32 num_instances); 54 u32 base_instance, u32 num_instances);
48 55
56 void DrawIndexedIndirect(PrimitiveTopology topology, u32 index_first, u32 index_count);
57
49 const State& GetDrawState() const { 58 const State& GetDrawState() const {
50 return draw_state; 59 return draw_state;
51 } 60 }
52 61
62 IndirectParams& GetIndirectParams() {
63 return indirect_state;
64 }
65
66 const IndirectParams& GetIndirectParams() const {
67 return indirect_state;
68 }
69
53private: 70private:
54 void SetInlineIndexBuffer(u32 index); 71 void SetInlineIndexBuffer(u32 index);
55 72
@@ -63,7 +80,10 @@ private:
63 80
64 void ProcessDraw(bool draw_indexed, u32 instance_count); 81 void ProcessDraw(bool draw_indexed, u32 instance_count);
65 82
83 void ProcessDrawIndirect(bool draw_indexed);
84
66 Maxwell3D* maxwell3d{}; 85 Maxwell3D* maxwell3d{};
67 State draw_state{}; 86 State draw_state{};
87 IndirectParams indirect_state{};
68}; 88};
69} // namespace Tegra::Engines 89} // namespace Tegra::Engines
diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h
index 26cde8584..76630272d 100644
--- a/src/video_core/engines/engine_interface.h
+++ b/src/video_core/engines/engine_interface.h
@@ -17,6 +17,8 @@ public:
17 /// Write multiple values to the register identified by method. 17 /// Write multiple values to the register identified by method.
18 virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount, 18 virtual void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
19 u32 methods_pending) = 0; 19 u32 methods_pending) = 0;
20
21 GPUVAddr current_dma_segment;
20}; 22};
21 23
22} // namespace Tegra::Engines 24} // namespace Tegra::Engines
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 8549db2e4..1cc202cc7 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -53,42 +53,43 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
53 53
54// Multidraw Indirect 54// Multidraw Indirect
55void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { 55void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
56 SCOPE_EXIT({
57 // Clean everything.
58 maxwell3d.regs.vertex_id_base = 0x0;
59 maxwell3d.CallMethod(0x8e3, 0x640, true);
60 maxwell3d.CallMethod(0x8e4, 0x0, true);
61 maxwell3d.CallMethod(0x8e5, 0x0, true);
62 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
63 });
64 const u32 start_indirect = parameters[0]; 56 const u32 start_indirect = parameters[0];
65 const u32 end_indirect = parameters[1]; 57 const u32 end_indirect = parameters[1];
66 if (start_indirect >= end_indirect) { 58 if (start_indirect >= end_indirect) {
67 // Nothing to do. 59 // Nothing to do.
68 return; 60 return;
69 } 61 }
70 const u32 padding = parameters[3]; 62 const auto topology =
71 const std::size_t max_draws = parameters[4]; 63 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
64 const u32 padding = parameters[3]; // padding is in words
72 65
66 // size of each indirect segment
73 const u32 indirect_words = 5 + padding; 67 const u32 indirect_words = 5 + padding;
74 const std::size_t first_draw = start_indirect; 68 const u32 stride = indirect_words * sizeof(u32);
75 const std::size_t effective_draws = end_indirect - start_indirect; 69 const GPUVAddr start_address = maxwell3d.current_dma_segment + 4 * sizeof(u32);
76 const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws); 70 const std::size_t draw_count = end_indirect - start_indirect;
77 71 u32 lowest_first = std::numeric_limits<u32>::max();
78 for (std::size_t index = first_draw; index < last_draw; index++) { 72 u32 highest_limit = std::numeric_limits<u32>::min();
73 for (std::size_t index = 0; index < draw_count; index++) {
79 const std::size_t base = index * indirect_words + 5; 74 const std::size_t base = index * indirect_words + 5;
80 const u32 base_vertex = parameters[base + 3]; 75 const u32 count = parameters[base];
81 const u32 base_instance = parameters[base + 4]; 76 const u32 first_index = parameters[base + 2];
82 maxwell3d.regs.vertex_id_base = base_vertex; 77 lowest_first = std::min(lowest_first, first_index);
83 maxwell3d.CallMethod(0x8e3, 0x640, true); 78 highest_limit = std::max(highest_limit, first_index + count);
84 maxwell3d.CallMethod(0x8e4, base_vertex, true);
85 maxwell3d.CallMethod(0x8e5, base_instance, true);
86 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
87 maxwell3d.draw_manager->DrawIndex(
88 static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]),
89 parameters[base + 2], parameters[base], base_vertex, base_instance,
90 parameters[base + 1]);
91 } 79 }
80
81 const u32 base_vertex = parameters[8];
82 const u32 base_instance = parameters[9];
83 maxwell3d.CallMethod(0x8e3, 0x640, true);
84 maxwell3d.CallMethod(0x8e4, base_vertex, true);
85 maxwell3d.CallMethod(0x8e5, base_instance, true);
86 auto& params = maxwell3d.draw_manager->GetIndirectParams();
87 params.start_address = start_address;
88 params.buffer_size = sizeof(u32) + stride * draw_count;
89 params.max_draw_counts = draw_count;
90 params.stride = stride;
91 maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
92 maxwell3d.draw_manager->DrawIndexedIndirect(topology, 0, highest_limit);
92} 93}
93 94
94// Multi-layer Clear 95// Multi-layer Clear
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index b6907463c..a2a651f34 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -42,6 +42,9 @@ public:
42 /// Dispatches a draw invocation 42 /// Dispatches a draw invocation
43 virtual void Draw(bool is_indexed, u32 instance_count) = 0; 43 virtual void Draw(bool is_indexed, u32 instance_count) = 0;
44 44
45 /// Dispatches an indirect draw invocation
46 virtual void DrawIndirect(bool is_indexed) {}
47
45 /// Clear the current framebuffer 48 /// Clear the current framebuffer
46 virtual void Clear(u32 layer_count) = 0; 49 virtual void Clear(u32 layer_count) = 0;
47 50
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index ac1eb9895..9b75f33dd 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -180,7 +180,8 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
180 180
181RasterizerVulkan::~RasterizerVulkan() = default; 181RasterizerVulkan::~RasterizerVulkan() = default;
182 182
183void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { 183template <typename Func>
184void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
184 MICROPROFILE_SCOPE(Vulkan_Drawing); 185 MICROPROFILE_SCOPE(Vulkan_Drawing);
185 186
186 SCOPE_EXIT({ gpu.TickWork(); }); 187 SCOPE_EXIT({ gpu.TickWork(); });
@@ -201,22 +202,50 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
201 202
202 UpdateDynamicStates(); 203 UpdateDynamicStates();
203 204
204 const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); 205 draw_func();
205 const u32 num_instances{instance_count}; 206
206 const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
207 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
208 if (draw_params.is_indexed) {
209 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
210 draw_params.first_index, draw_params.base_vertex,
211 draw_params.base_instance);
212 } else {
213 cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
214 draw_params.base_vertex, draw_params.base_instance);
215 }
216 });
217 EndTransformFeedback(); 207 EndTransformFeedback();
218} 208}
219 209
210void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
211 PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
212 const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
213 const u32 num_instances{instance_count};
214 const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
215 scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
216 if (draw_params.is_indexed) {
217 cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
218 draw_params.first_index, draw_params.base_vertex,
219 draw_params.base_instance);
220 } else {
221 cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
222 draw_params.base_vertex, draw_params.base_instance);
223 }
224 });
225 });
226}
227
228void RasterizerVulkan::DrawIndirect(bool is_indexed) {
229 PrepareDraw(is_indexed, [this, is_indexed] {
230 const auto params = maxwell3d->draw_manager->GetIndirectParams();
231 const auto [buffer, offset] = buffer_cache.ObtainBuffer(
232 params.start_address, static_cast<u32>(params.buffer_size), true, false);
233 scheduler.Record([buffer_obj = buffer->Handle(), offset,
234 max_draw_counts = params.max_draw_counts, stride = params.stride,
235 is_indexed](vk::CommandBuffer cmdbuf) {
236 if (is_indexed) {
237 cmdbuf.DrawIndexedIndirectCount(buffer_obj, offset + 4ULL, buffer_obj, offset,
238 static_cast<u32>(max_draw_counts),
239 static_cast<u32>(stride));
240 } else {
241 cmdbuf.DrawIndirectCount(buffer_obj, offset + 4ULL, buffer_obj, offset,
242 static_cast<u32>(max_draw_counts),
243 static_cast<u32>(stride));
244 }
245 });
246 });
247}
248
220void RasterizerVulkan::Clear(u32 layer_count) { 249void RasterizerVulkan::Clear(u32 layer_count) {
221 MICROPROFILE_SCOPE(Vulkan_Clearing); 250 MICROPROFILE_SCOPE(Vulkan_Clearing);
222 251
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index ee483cfd9..bc43a8a1f 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -65,6 +65,7 @@ public:
65 ~RasterizerVulkan() override; 65 ~RasterizerVulkan() override;
66 66
67 void Draw(bool is_indexed, u32 instance_count) override; 67 void Draw(bool is_indexed, u32 instance_count) override;
68 void DrawIndirect(bool is_indexed) override;
68 void Clear(u32 layer_count) override; 69 void Clear(u32 layer_count) override;
69 void DispatchCompute() override; 70 void DispatchCompute() override;
70 void ResetCounter(VideoCore::QueryType type) override; 71 void ResetCounter(VideoCore::QueryType type) override;
@@ -114,6 +115,9 @@ private:
114 115
115 static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float); 116 static constexpr VkDeviceSize DEFAULT_BUFFER_SIZE = 4 * sizeof(float);
116 117
118 template <typename Func>
119 void PrepareDraw(bool is_indexed, Func&&);
120
117 void FlushWork(); 121 void FlushWork();
118 122
119 void UpdateDynamicStates(); 123 void UpdateDynamicStates();
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index c4d31681a..477fc428b 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -350,7 +350,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
350 .sampleRateShading = true, 350 .sampleRateShading = true,
351 .dualSrcBlend = true, 351 .dualSrcBlend = true,
352 .logicOp = true, 352 .logicOp = true,
353 .multiDrawIndirect = false, 353 .multiDrawIndirect = true,
354 .drawIndirectFirstInstance = false, 354 .drawIndirectFirstInstance = false,
355 .depthClamp = true, 355 .depthClamp = true,
356 .depthBiasClamp = true, 356 .depthBiasClamp = true,
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 7dca7341c..c58c4c1c4 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -94,6 +94,8 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
94 X(vkCmdDispatch); 94 X(vkCmdDispatch);
95 X(vkCmdDraw); 95 X(vkCmdDraw);
96 X(vkCmdDrawIndexed); 96 X(vkCmdDrawIndexed);
97 X(vkCmdDrawIndirectCount);
98 X(vkCmdDrawIndexedIndirectCount);
97 X(vkCmdEndQuery); 99 X(vkCmdEndQuery);
98 X(vkCmdEndRenderPass); 100 X(vkCmdEndRenderPass);
99 X(vkCmdEndTransformFeedbackEXT); 101 X(vkCmdEndTransformFeedbackEXT);
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index 8bd4fd4d9..9bd158dce 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -213,6 +213,8 @@ struct DeviceDispatch : InstanceDispatch {
213 PFN_vkCmdDispatch vkCmdDispatch{}; 213 PFN_vkCmdDispatch vkCmdDispatch{};
214 PFN_vkCmdDraw vkCmdDraw{}; 214 PFN_vkCmdDraw vkCmdDraw{};
215 PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; 215 PFN_vkCmdDrawIndexed vkCmdDrawIndexed{};
216 PFN_vkCmdDrawIndirectCount vkCmdDrawIndirectCount{};
217 PFN_vkCmdDrawIndexedIndirectCount vkCmdDrawIndexedIndirectCount{};
216 PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{}; 218 PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
217 PFN_vkCmdEndQuery vkCmdEndQuery{}; 219 PFN_vkCmdEndQuery vkCmdEndQuery{};
218 PFN_vkCmdEndRenderPass vkCmdEndRenderPass{}; 220 PFN_vkCmdEndRenderPass vkCmdEndRenderPass{};
@@ -1019,6 +1021,19 @@ public:
1019 first_instance); 1021 first_instance);
1020 } 1022 }
1021 1023
1024 void DrawIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset, VkBuffer count_buffer,
1025 VkDeviceSize count_offset, u32 draw_count, u32 stride) const noexcept {
1026 dld->vkCmdDrawIndirectCount(handle, src_buffer, src_offset, count_buffer, count_offset,
1027 draw_count, stride);
1028 }
1029
1030 void DrawIndexedIndirectCount(VkBuffer src_buffer, VkDeviceSize src_offset,
1031 VkBuffer count_buffer, VkDeviceSize count_offset, u32 draw_count,
1032 u32 stride) const noexcept {
1033 dld->vkCmdDrawIndexedIndirectCount(handle, src_buffer, src_offset, count_buffer,
1034 count_offset, draw_count, stride);
1035 }
1036
1022 void ClearAttachments(Span<VkClearAttachment> attachments, 1037 void ClearAttachments(Span<VkClearAttachment> attachments,
1023 Span<VkClearRect> rects) const noexcept { 1038 Span<VkClearRect> rects) const noexcept {
1024 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), 1039 dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),