[android, tu] Adjusted PoD of Vertex/ Buffers for older turnip drivers (#3621)

This PR aims to return an older way to bind and host vertex/ buffers (via toggle), which had a bunch of indirection and added unnecessary overhead during the drawing phase; current new approach adds just PoD for this operations, which seems to not be acceptable for older turnip drivers.

Meanwhile the performance improvements are gonna be enabled only if the toggle is turned on, it will be required to use newer turnip drivers to make it work (26.0+), default behavior will allow older drivers work as intended.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3621
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Co-committed-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
This commit is contained in:
PavelBARABANOV 2026-02-24 18:49:44 +01:00 committed by crueter
parent b45c78a051
commit d2bef2731c
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
6 changed files with 105 additions and 38 deletions

View file

@ -25,6 +25,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"), RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"), ENABLE_BUFFER_HISTORY("enable_buffer_history"),
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
SYNC_MEMORY_OPERATIONS("sync_memory_operations"), SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
BUFFER_REORDER_DISABLE("disable_buffer_reorder"), BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
RENDERER_DEBUG("debug"), RENDERER_DEBUG("debug"),

View file

@ -795,6 +795,13 @@ abstract class SettingsItem(
descriptionId = R.string.enable_buffer_history_description descriptionId = R.string.enable_buffer_history_description
) )
) )
put(
SwitchSetting(
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
titleId = R.string.use_optimized_vertex_buffers,
descriptionId = R.string.use_optimized_vertex_buffers_description
)
)
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.SYNC_MEMORY_OPERATIONS, BooleanSetting.SYNC_MEMORY_OPERATIONS,

View file

@ -278,6 +278,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key) add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key) add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
add(HeaderSetting(R.string.hacks)) add(HeaderSetting(R.string.hacks))

View file

@ -500,7 +500,8 @@
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
<string name="enable_buffer_history">Enable buffer history</string> <string name="enable_buffer_history">Enable buffer history</string>
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string> <string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers. Will crash on older drivers.</string>
<string name="hacks">Hacks</string> <string name="hacks">Hacks</string>

View file

@ -519,6 +519,16 @@ struct Values {
true, true,
true}; true};
#ifdef ANDROID
SwitchableSetting<bool> use_optimized_vertex_buffers{linkage,
false,
"use_optimized_vertex_buffers",
Category::RendererAdvanced,
Specialization::Default,
true,
true};
#endif
// Renderer Hacks // // Renderer Hacks //
SwitchableSetting<GpuOverclock> fast_gpu_time{linkage, SwitchableSetting<GpuOverclock> fast_gpu_time{linkage,
GpuOverclock::Medium, GpuOverclock::Medium,

View file

@ -805,47 +805,94 @@ void BufferCache<P>::UpdateVertexBufferSlot(u32 index, const Binding& binding) {
template <class P> template <class P>
void BufferCache<P>::BindHostVertexBuffers() { void BufferCache<P>::BindHostVertexBuffers() {
auto& flags = maxwell3d->dirty.flags;
u32 enabled_mask = enabled_vertex_buffers_mask; #ifdef ANDROID
HostBindings<Buffer> bindings{}; const bool use_optimized_vertex_buffers = Settings::values.use_optimized_vertex_buffers.GetValue();
u32 last_index = std::numeric_limits<u32>::max(); #else
const auto flush_bindings = [&]() { constexpr bool use_optimized_vertex_buffers = true;
if (bindings.buffers.empty()) { #endif
return;
if (use_optimized_vertex_buffers) {
auto& flags = maxwell3d->dirty.flags;
u32 enabled_mask = enabled_vertex_buffers_mask;
HostBindings<Buffer> bindings{};
u32 last_index = std::numeric_limits<u32>::max();
const auto flush_bindings = [&]() {
if (bindings.buffers.empty()) {
return;
}
bindings.max_index = bindings.min_index + static_cast<u32>(bindings.buffers.size());
runtime.BindVertexBuffers(bindings);
bindings = HostBindings<Buffer>{};
last_index = std::numeric_limits<u32>::max();
};
while (enabled_mask != 0) {
const u32 index = std::countr_zero(enabled_mask);
enabled_mask &= (enabled_mask - 1);
const Binding& binding = VertexBufferSlot(index);
Buffer& buffer = slot_buffers[binding.buffer_id];
TouchBuffer(buffer, binding.buffer_id);
SynchronizeBuffer(buffer, binding.device_addr, binding.size);
if (!flags[Dirty::VertexBuffer0 + index]) {
flush_bindings();
continue;
}
flags[Dirty::VertexBuffer0 + index] = false;
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, binding.size);
if (!bindings.buffers.empty() && index != last_index + 1) {
flush_bindings();
}
if (bindings.buffers.empty()) {
bindings.min_index = index;
}
bindings.buffers.push_back(&buffer);
bindings.offsets.push_back(offset);
bindings.sizes.push_back(binding.size);
bindings.strides.push_back(stride);
last_index = index;
} }
bindings.max_index = bindings.min_index + static_cast<u32>(bindings.buffers.size()); flush_bindings();
runtime.BindVertexBuffers(bindings); } else {
bindings = HostBindings<Buffer>{}; HostBindings<typename P::Buffer> host_bindings;
last_index = std::numeric_limits<u32>::max(); bool any_valid{false};
}; auto& flags = maxwell3d->dirty.flags;
while (enabled_mask != 0) { for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
const u32 index = std::countr_zero(enabled_mask); const Binding& binding = channel_state->vertex_buffers[index];
enabled_mask &= (enabled_mask - 1); Buffer& buffer = slot_buffers[binding.buffer_id];
const Binding& binding = VertexBufferSlot(index); TouchBuffer(buffer, binding.buffer_id);
Buffer& buffer = slot_buffers[binding.buffer_id]; SynchronizeBuffer(buffer, binding.device_addr, binding.size);
TouchBuffer(buffer, binding.buffer_id); if (!flags[Dirty::VertexBuffer0 + index]) {
SynchronizeBuffer(buffer, binding.device_addr, binding.size); continue;
if (!flags[Dirty::VertexBuffer0 + index]) { }
flush_bindings(); flags[Dirty::VertexBuffer0 + index] = false;
continue;
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;
} }
flags[Dirty::VertexBuffer0 + index] = false;
const u32 stride = maxwell3d->regs.vertex_streams[index].stride; if (any_valid) {
const u32 offset = buffer.Offset(binding.device_addr); host_bindings.max_index++;
buffer.MarkUsage(offset, binding.size); for (u32 index = host_bindings.min_index; index < host_bindings.max_index; index++) {
if (!bindings.buffers.empty() && index != last_index + 1) { flags[Dirty::VertexBuffer0 + index] = false;
flush_bindings();
const Binding& binding = channel_state->vertex_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, binding.size);
host_bindings.buffers.push_back(&buffer);
host_bindings.offsets.push_back(offset);
host_bindings.sizes.push_back(binding.size);
host_bindings.strides.push_back(stride);
}
runtime.BindVertexBuffers(host_bindings);
} }
if (bindings.buffers.empty()) {
bindings.min_index = index;
}
bindings.buffers.push_back(&buffer);
bindings.offsets.push_back(offset);
bindings.sizes.push_back(binding.size);
bindings.strides.push_back(stride);
last_index = index;
} }
flush_bindings();
} }
template <class P> template <class P>