Merge pull request #10668 from Kelebek1/reduce_vertex_bindings

Combine vertex/transform feedback buffer binding into a single call
This commit is contained in:
bunnei 2023-06-11 11:33:48 -07:00 committed by GitHub
commit 569f8d3b44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 148 additions and 24 deletions

View File

@ -715,20 +715,38 @@ void BufferCache<P>::BindHostIndexBuffer() {
template <class P> template <class P>
void BufferCache<P>::BindHostVertexBuffers() { void BufferCache<P>::BindHostVertexBuffers() {
HostBindings host_bindings;
bool any_valid{false};
auto& flags = maxwell3d->dirty.flags; auto& flags = maxwell3d->dirty.flags;
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
const Binding& binding = channel_state->vertex_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
if (!flags[Dirty::VertexBuffer0 + index]) { if (!flags[Dirty::VertexBuffer0 + index]) {
continue; continue;
} }
flags[Dirty::VertexBuffer0 + index] = false; host_bindings.min_index = std::min(host_bindings.min_index, index);
host_bindings.max_index = std::max(host_bindings.max_index, index);
any_valid = true;
}
const u32 stride = maxwell3d->regs.vertex_streams[index].stride; if (any_valid) {
const u32 offset = buffer.Offset(binding.cpu_addr); host_bindings.max_index++;
runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride); for (u32 index = host_bindings.min_index; index < host_bindings.max_index; index++) {
flags[Dirty::VertexBuffer0 + index] = false;
const Binding& binding = channel_state->vertex_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
const u32 offset = buffer.Offset(binding.cpu_addr);
host_bindings.buffers.push_back(reinterpret_cast<void*>(&buffer));
host_bindings.offsets.push_back(offset);
host_bindings.sizes.push_back(binding.size);
host_bindings.strides.push_back(stride);
}
runtime.BindVertexBuffers(host_bindings);
} }
} }
@ -882,15 +900,25 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
if (maxwell3d->regs.transform_feedback_enabled == 0) { if (maxwell3d->regs.transform_feedback_enabled == 0) {
return; return;
} }
HostBindings host_bindings;
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
const Binding& binding = channel_state->transform_feedback_buffers[index]; const Binding& binding = channel_state->transform_feedback_buffers[index];
if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 &&
maxwell3d->regs.transform_feedback.controls[index].stride == 0) {
break;
}
Buffer& buffer = slot_buffers[binding.buffer_id]; Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id); TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size; const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size); SynchronizeBuffer(buffer, binding.cpu_addr, size);
const u32 offset = buffer.Offset(binding.cpu_addr); const u32 offset = buffer.Offset(binding.cpu_addr);
runtime.BindTransformFeedbackBuffer(index, buffer, offset, size); host_bindings.buffers.push_back(reinterpret_cast<void*>(&buffer));
host_bindings.offsets.push_back(offset);
host_bindings.sizes.push_back(binding.size);
}
if (host_bindings.buffers.size() > 0) {
runtime.BindTransformFeedbackBuffers(host_bindings);
} }
} }
@ -1616,6 +1644,8 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si
template <class P> template <class P>
void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
bool dirty_index{false};
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> dirty_vertex_buffers;
const auto scalar_replace = [buffer_id](Binding& binding) { const auto scalar_replace = [buffer_id](Binding& binding) {
if (binding.buffer_id == buffer_id) { if (binding.buffer_id == buffer_id) {
binding.buffer_id = BufferId{}; binding.buffer_id = BufferId{};
@ -1624,8 +1654,19 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
const auto replace = [scalar_replace](std::span<Binding> bindings) { const auto replace = [scalar_replace](std::span<Binding> bindings) {
std::ranges::for_each(bindings, scalar_replace); std::ranges::for_each(bindings, scalar_replace);
}; };
scalar_replace(channel_state->index_buffer);
replace(channel_state->vertex_buffers); if (channel_state->index_buffer.buffer_id == buffer_id) {
channel_state->index_buffer.buffer_id = BufferId{};
dirty_index = true;
}
for (u32 index = 0; index < channel_state->vertex_buffers.size(); index++) {
auto& binding = channel_state->vertex_buffers[index];
if (binding.buffer_id == buffer_id) {
binding.buffer_id = BufferId{};
dirty_vertex_buffers.push_back(index);
}
}
std::ranges::for_each(channel_state->uniform_buffers, replace); std::ranges::for_each(channel_state->uniform_buffers, replace);
std::ranges::for_each(channel_state->storage_buffers, replace); std::ranges::for_each(channel_state->storage_buffers, replace);
replace(channel_state->transform_feedback_buffers); replace(channel_state->transform_feedback_buffers);
@ -1642,20 +1683,21 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id])); delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
slot_buffers.erase(buffer_id); slot_buffers.erase(buffer_id);
NotifyBufferDeletion();
}
template <class P>
void BufferCache<P>::NotifyBufferDeletion() {
if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) {
channel_state->dirty_uniform_buffers.fill(~u32{0}); channel_state->dirty_uniform_buffers.fill(~u32{0});
channel_state->uniform_buffer_binding_sizes.fill({}); channel_state->uniform_buffer_binding_sizes.fill({});
} }
auto& flags = maxwell3d->dirty.flags; auto& flags = maxwell3d->dirty.flags;
flags[Dirty::IndexBuffer] = true; if (dirty_index) {
flags[Dirty::VertexBuffers] = true; flags[Dirty::IndexBuffer] = true;
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { }
flags[Dirty::VertexBuffer0 + index] = true;
if (dirty_vertex_buffers.size() > 0) {
flags[Dirty::VertexBuffers] = true;
for (auto index : dirty_vertex_buffers) {
flags[Dirty::VertexBuffer0 + index] = true;
}
} }
channel_state->has_deleted_buffers = true; channel_state->has_deleted_buffers = true;
} }

View File

@ -105,6 +105,15 @@ static constexpr Binding NULL_BINDING{
.buffer_id = NULL_BUFFER_ID, .buffer_id = NULL_BUFFER_ID,
}; };
struct HostBindings {
boost::container::small_vector<void*, NUM_VERTEX_BUFFERS> buffers;
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> offsets;
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> sizes;
boost::container::small_vector<u64, NUM_VERTEX_BUFFERS> strides;
u32 min_index{NUM_VERTEX_BUFFERS};
u32 max_index{0};
};
class BufferCacheChannelInfo : public ChannelInfo { class BufferCacheChannelInfo : public ChannelInfo {
public: public:
BufferCacheChannelInfo() = delete; BufferCacheChannelInfo() = delete;
@ -519,8 +528,6 @@ private:
void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false); void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false);
void NotifyBufferDeletion();
[[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index, [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
bool is_written) const; bool is_written) const;

View File

@ -232,6 +232,15 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset,
} }
} }
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) {
for (u32 index = 0; index < bindings.buffers.size(); index++) {
BindVertexBuffer(
bindings.min_index + index, *reinterpret_cast<Buffer*>(bindings.buffers[index]),
static_cast<u32>(bindings.offsets[index]), static_cast<u32>(bindings.sizes[index]),
static_cast<u32>(bindings.strides[index]));
}
}
void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer,
u32 offset, u32 size) { u32 offset, u32 size) {
if (use_assembly_shaders) { if (use_assembly_shaders) {
@ -320,6 +329,15 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, Buffer& buffer,
static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size)); static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size));
} }
void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) {
for (u32 index = 0; index < bindings.buffers.size(); index++) {
glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index,
reinterpret_cast<Buffer*>(bindings.buffers[index])->Handle(),
static_cast<GLintptr>(bindings.offsets[index]),
static_cast<GLsizeiptr>(bindings.sizes[index]));
}
}
void BufferCacheRuntime::BindTextureBuffer(Buffer& buffer, u32 offset, u32 size, void BufferCacheRuntime::BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
PixelFormat format) { PixelFormat format) {
*texture_handles++ = buffer.View(offset, size, format); *texture_handles++ = buffer.View(offset, size, format);

View File

@ -7,7 +7,7 @@
#include <span> #include <span>
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/buffer_cache/buffer_cache.h" #include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/buffer_cache/memory_tracker_base.h" #include "video_core/buffer_cache/memory_tracker_base.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_device.h"
@ -87,6 +87,7 @@ public:
void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
void BindVertexBuffers(VideoCommon::HostBindings& bindings);
void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size); void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
@ -99,6 +100,7 @@ public:
bool is_written); bool is_written);
void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size); void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size);
void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings);
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size, void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
VideoCore::Surface::PixelFormat format); VideoCore::Surface::PixelFormat format);

View File

@ -7,7 +7,6 @@
#include <span> #include <span>
#include <vector> #include <vector>
#include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/renderer_vulkan/maxwell_to_vk.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
@ -502,6 +501,40 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
} }
} }
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) {
boost::container::small_vector<VkBuffer, 32> buffer_handles;
for (u32 index = 0; index < bindings.buffers.size(); index++) {
auto& buffer = *reinterpret_cast<Buffer*>(bindings.buffers[index]);
auto handle = buffer.Handle();
if (handle == VK_NULL_HANDLE) {
bindings.offsets[index] = 0;
bindings.sizes[index] = VK_WHOLE_SIZE;
if (!device.HasNullDescriptor()) {
ReserveNullBuffer();
handle = *null_buffer;
}
}
buffer_handles.push_back(handle);
}
if (device.IsExtExtendedDynamicStateSupported()) {
scheduler.Record([bindings = bindings,
buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers2EXT(
bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(),
reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()),
reinterpret_cast<const VkDeviceSize*>(bindings.sizes.data()),
reinterpret_cast<const VkDeviceSize*>(bindings.strides.data()));
});
} else {
scheduler.Record([bindings = bindings,
buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers(
bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(),
reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()));
});
}
}
void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset,
u32 size) { u32 size) {
if (!device.IsExtTransformFeedbackSupported()) { if (!device.IsExtTransformFeedbackSupported()) {
@ -523,6 +556,25 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer,
}); });
} }
void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) {
if (!device.IsExtTransformFeedbackSupported()) {
// Already logged in the rasterizer
return;
}
boost::container::small_vector<VkBuffer, 4> buffer_handles;
for (u32 index = 0; index < bindings.buffers.size(); index++) {
auto& buffer = *reinterpret_cast<Buffer*>(bindings.buffers[index]);
buffer_handles.push_back(buffer.Handle());
}
scheduler.Record(
[bindings = bindings, buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) {
cmdbuf.BindTransformFeedbackBuffersEXT(
0, static_cast<u32>(buffer_handles.size()), buffer_handles.data(),
reinterpret_cast<const VkDeviceSize*>(bindings.offsets.data()),
reinterpret_cast<const VkDeviceSize*>(bindings.sizes.data()));
});
}
void BufferCacheRuntime::ReserveNullBuffer() { void BufferCacheRuntime::ReserveNullBuffer() {
if (null_buffer) { if (null_buffer) {
return; return;

View File

@ -18,6 +18,7 @@ namespace Vulkan {
class Device; class Device;
class DescriptorPool; class DescriptorPool;
class Scheduler; class Scheduler;
struct HostVertexBinding;
class BufferCacheRuntime; class BufferCacheRuntime;
@ -96,8 +97,10 @@ public:
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count); void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
void BindVertexBuffers(VideoCommon::HostBindings& bindings);
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings);
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage, std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
[[maybe_unused]] u32 binding_index, u32 size) { [[maybe_unused]] u32 binding_index, u32 size) {