mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-26 04:50:05 +00:00
Remade Vertex Cache
This commit is contained in:
parent
372db835f4
commit
43a425038c
@ -44,6 +44,7 @@ set(HEADERS
|
|||||||
shader/shader_interpreter.h
|
shader/shader_interpreter.h
|
||||||
swrasterizer.h
|
swrasterizer.h
|
||||||
utils.h
|
utils.h
|
||||||
|
vertex_cache.h
|
||||||
vertex_loader.h
|
vertex_loader.h
|
||||||
video_core.h
|
video_core.h
|
||||||
)
|
)
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/shader/shader.h"
|
#include "video_core/shader/shader.h"
|
||||||
|
#include "video_core/vertex_cache.h"
|
||||||
#include "video_core/vertex_loader.h"
|
#include "video_core/vertex_loader.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
@ -235,16 +236,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
|
|
||||||
DebugUtils::MemoryAccessTracker memory_accesses;
|
DebugUtils::MemoryAccessTracker memory_accesses;
|
||||||
|
|
||||||
// Simple circular-replacement vertex cache
|
|
||||||
// The size has been tuned for optimal balance between hit-rate and the cost of lookup
|
|
||||||
const size_t VERTEX_CACHE_SIZE = 32;
|
|
||||||
std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids;
|
|
||||||
std::array<Shader::OutputVertex, VERTEX_CACHE_SIZE> vertex_cache;
|
|
||||||
Shader::OutputVertex output_vertex;
|
Shader::OutputVertex output_vertex;
|
||||||
|
|
||||||
unsigned int vertex_cache_pos = 0;
|
|
||||||
vertex_cache_ids.fill(-1);
|
|
||||||
|
|
||||||
auto* shader_engine = Shader::GetEngine();
|
auto* shader_engine = Shader::GetEngine();
|
||||||
Shader::UnitState shader_unit;
|
Shader::UnitState shader_unit;
|
||||||
|
|
||||||
@ -260,25 +253,15 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
// the PICA supports it, and it would mess up the caching, guard against it here.
|
// the PICA supports it, and it would mess up the caching, guard against it here.
|
||||||
ASSERT(vertex != -1);
|
ASSERT(vertex != -1);
|
||||||
|
|
||||||
bool vertex_cache_hit = false;
|
|
||||||
|
|
||||||
if (is_indexed) {
|
if (is_indexed) {
|
||||||
if (g_debug_context && Pica::g_debug_context->recorder) {
|
if (g_debug_context && Pica::g_debug_context->recorder) {
|
||||||
int size = index_u16 ? 2 : 1;
|
int size = index_u16 ? 2 : 1;
|
||||||
memory_accesses.AddAccess(base_address + index_info.offset + size * index,
|
memory_accesses.AddAccess(base_address + index_info.offset + size * index,
|
||||||
size);
|
size);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) {
|
|
||||||
if (vertex == vertex_cache_ids[i]) {
|
|
||||||
output_vertex = vertex_cache[i];
|
|
||||||
vertex_cache_hit = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vertex_cache_hit) {
|
if (!VertexCache::contains(vertex)) {
|
||||||
// Initialize data for the current vertex
|
// Initialize data for the current vertex
|
||||||
Shader::InputVertex input;
|
Shader::InputVertex input;
|
||||||
loader.LoadVertex(base_address, index, vertex, input, memory_accesses);
|
loader.LoadVertex(base_address, index, vertex, input, memory_accesses);
|
||||||
@ -294,12 +277,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
output_vertex = Shader::OutputVertex::FromRegisters(shader_unit.registers.output,
|
output_vertex = Shader::OutputVertex::FromRegisters(shader_unit.registers.output,
|
||||||
regs, regs.vs.output_mask);
|
regs, regs.vs.output_mask);
|
||||||
|
|
||||||
if (is_indexed) {
|
if (is_indexed)
|
||||||
vertex_cache[vertex_cache_pos] = output_vertex;
|
VertexCache::store(vertex, output_vertex);
|
||||||
vertex_cache_ids[vertex_cache_pos] = vertex;
|
} else
|
||||||
vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE;
|
output_vertex = VertexCache::obtain(vertex);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send to renderer
|
// Send to renderer
|
||||||
using Pica::Shader::OutputVertex;
|
using Pica::Shader::OutputVertex;
|
||||||
@ -316,6 +297,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
|
|||||||
range.second, range.first);
|
range.second, range.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_indexed)
|
||||||
|
VertexCache::clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
81
src/video_core/vertex_cache.h
Normal file
81
src/video_core/vertex_cache.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/shader/shader.h"
|
||||||
|
|
||||||
|
// This little module emulates PICA's vertex shader's post transform vertex cache.
|
||||||
|
namespace VertexCache {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr size_t cache_max_size = 256 * 256;
|
||||||
|
// testing has proven that this size produces few misses in most games.
|
||||||
|
// in total the cache size is 64Kb
|
||||||
|
constexpr size_t cache_size = 512;
|
||||||
|
|
||||||
|
// this is used to find if a vertex is in the cache
|
||||||
|
static std::array<bool, cache_max_size> index_table = {};
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
static std::array<bool, cache_max_size> seen_table = {};
|
||||||
|
u32 cache_misses = 0;
|
||||||
|
|
||||||
|
// The number of misses we can tolerate.
|
||||||
|
const u32 tolerated_misses = 16;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// le cache
|
||||||
|
static std::array<Pica::Shader::OutputVertex, cache_size> cache;
|
||||||
|
|
||||||
|
// stores positions that are cached;
|
||||||
|
static std::array<u16, cache_size> remapper = {};
|
||||||
|
|
||||||
|
// used for clearing the cache;
|
||||||
|
u32 min_index = cache_size;
|
||||||
|
u32 max_index = 0;
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
inline bool contains(u32 position) {
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (seen_table[position] != index_table[position])
|
||||||
|
cache_misses++;
|
||||||
|
#endif
|
||||||
|
return index_table[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Pica::Shader::OutputVertex& obtain(u32 position) {
|
||||||
|
return cache[position % cache_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void store(u32 position, Pica::Shader::OutputVertex& vertex) {
|
||||||
|
const u32 remap_index = position % cache_size;
|
||||||
|
index_table[remapper[remap_index]] = false;
|
||||||
|
remapper[remap_index] = position;
|
||||||
|
index_table[position] = true;
|
||||||
|
cache[remap_index] = vertex;
|
||||||
|
max_index = std::max(max_index, remap_index);
|
||||||
|
min_index = std::min(min_index, remap_index);
|
||||||
|
#ifdef _DEBUG
|
||||||
|
seen_table[position] = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
max_index++;
|
||||||
|
auto result = std::minmax_element(remapper.begin() + min_index, remapper.begin() + max_index);
|
||||||
|
const u32 min = *result.first;
|
||||||
|
const u32 max = *result.second;
|
||||||
|
std::fill(index_table.begin() + min, index_table.begin() + max + 1, 0);
|
||||||
|
max_index = 0;
|
||||||
|
min_index = cache_size;
|
||||||
|
#ifdef _DEBUG
|
||||||
|
seen_table.fill(0);
|
||||||
|
if (cache_misses > tolerated_misses)
|
||||||
|
LOG_TRACE(HW_GPU, "The vertex cache had %d misses", cache_misses);
|
||||||
|
cache_misses = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // VertexCache
|
Loading…
Reference in New Issue
Block a user