diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 54a902f56f..9251690631 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -2,9 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include + +#include "common/alignment.h" #include "common/assert.h" +#include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" #include "video_core/dirty_flags.h" @@ -213,6 +217,9 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume regs.index_array.count = regs.small_index.count; regs.index_array.first = regs.small_index.first; dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; + if (!Settings::IsGPULevelExtreme()) { + RecalculateVertexArrayLimit(); + } return DrawArrays(); case MAXWELL3D_REG_INDEX(topology_override): use_topology_override = true; @@ -667,4 +674,71 @@ void Maxwell3D::ProcessClearBuffers() { rasterizer->Clear(); } +void Maxwell3D::RecalculateVertexArrayLimit() { + GPUVAddr start_address = regs.index_array.StartAddress(); + auto& vn_state = vertex_num_approx_state; + if (start_address != vn_state.last_index_array_start || + vn_state.current_min_index != regs.index_array.first) { + vn_state.last_index_array_start = start_address; + vn_state.current_max_index = regs.index_array.first; + vn_state.current_min_index = regs.index_array.first; + vn_state.current_num_vertices = 0; + } + const u32 index_count = regs.index_array.first + regs.index_array.count; + if (index_count <= vn_state.current_max_index) { + return; + } + const u32 max_base = std::max(regs.index_array.first, vn_state.current_max_index); + const u32 num_indices = index_count - max_base; + const size_t size_index = regs.index_array.FormatSizeInBytes(); + const size_t expected_size = num_indices * size_index; + const size_t offset = max_base * size_index; + + auto maybe_ptr = memory_manager.GpuToHostPointer(start_address + offset); + u8* ptr; + if (maybe_ptr) { + ptr = *maybe_ptr; + } else { + vn_state.index_buffer_cache.resize(Common::DivideUp(expected_size, sizeof(u32))); + ptr = reinterpret_cast(vn_state.index_buffer_cache.data()); + memory_manager.ReadBlockUnsafe(start_address + offset, ptr, expected_size); + } + vn_state.current_max_index = index_count; + + u32 new_num_vertices{}; + switch (regs.index_array.format) { + case Regs::IndexFormat::UnsignedByte: { + std::span span{ptr, num_indices}; + const auto max = std::max_element(span.begin(), span.end()); + new_num_vertices = *max + 1; + break; + } + case Regs::IndexFormat::UnsignedShort: { + std::span span{reinterpret_cast(ptr), num_indices}; + const auto max = std::max_element(span.begin(), span.end()); + new_num_vertices = *max + 1; + break; + } + case Regs::IndexFormat::UnsignedInt: { + std::span span{reinterpret_cast(ptr), num_indices}; + const auto max = std::max_element(span.begin(), span.end()); + new_num_vertices = *max + 1; + break; + } + } + if (new_num_vertices > vn_state.current_num_vertices) { + vn_state.current_num_vertices = new_num_vertices; + for (size_t i = 0; i < Regs::NumVertexArrays; i++) { + if (!regs.vertex_array[i].enable) { + continue; + } + const u32 stride = regs.vertex_array[i].stride; + const GPUVAddr gpu_addr_begin = regs.vertex_array[i].StartAddress(); + const GPUVAddr gpu_addr_end = gpu_addr_begin + new_num_vertices * stride - 1; + regs.vertex_array_limit[i].SetAddress(gpu_addr_end); + dirty.flags[VideoCommon::Dirty::VertexBuffer0 + i] = true; + } + } +} + } // namespace Tegra::Engines diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 357a74c705..3419dfcd45 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -1349,6 +1349,12 @@ public: return static_cast((static_cast(limit_high) << 32) | limit_low); } + + void SetAddress(GPUVAddr address) { + limit_low = static_cast(address); + limit_high = static_cast(address >> 32); + } + } vertex_array_limit[NumVertexArrays]; struct { @@ -1492,6 +1498,14 @@ public: Tables tables{}; } dirty; + struct VertexNumApproxState { + GPUVAddr last_index_array_start; + u32 current_max_index; + u32 current_min_index; + u32 current_num_vertices; + std::vector index_buffer_cache; + } vertex_num_approx_state; + private: void InitializeRegisterDefaults(); @@ -1560,6 +1574,8 @@ private: // Handles a instance drawcall from MME void StepInstance(MMEDrawMode expected_mode, u32 count); + void RecalculateVertexArrayLimit(); + /// Returns a query's value or an empty object if the value will be deferred through a cache. std::optional GetQueryResult(); diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 4ff3fa268a..4e0acfaba7 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -6,6 +6,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/host_memory.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/k_page_table.h" @@ -186,6 +187,19 @@ std::optional MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const { return page_entry.ToAddress() + (gpu_addr & page_mask); } +std::optional MemoryManager::GpuToHostPointer(GPUVAddr gpu_addr) const { + auto cpu_addr = GpuToCpuAddress(gpu_addr); + if (!cpu_addr) { + return std::nullopt; + } + auto& device_memory = system.DeviceMemory(); + auto base = device_memory.buffer.VirtualBasePointer(); + if (!base) { + return std::nullopt; + } + return base + *cpu_addr; +} + std::optional MemoryManager::GpuToCpuAddress(GPUVAddr addr, std::size_t size) const { size_t page_index{addr >> page_bits}; const size_t page_last{(addr + size + page_size - 1) >> page_bits}; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 61bfe47c7e..8774c405fc 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -76,6 +76,8 @@ public: [[nodiscard]] std::optional GpuToCpuAddress(GPUVAddr addr) const; + [[nodiscard]] std::optional GpuToHostPointer(GPUVAddr addr) const; + [[nodiscard]] std::optional GpuToCpuAddress(GPUVAddr addr, std::size_t size) const; template