Files
SpartanPort/tests/test_slot_vector.cpp
Mahdi Akbari Zarkesh c22329f13b Fix(GPU): ported texture, GPU and shader cache fixes (#1124)
EN-US

- Fixed texture cache image references moving when the cache grows, out-of-range slot IDs, per-dimension extent checks, view clamping, padding-only overlaps and in-flight sampler destruction
- Fixed depth and colour buffer copies to copy every shared mip and layer
- Fixed scheduler null chunks at 0x20000, exit 23 from oversized recording data and stale idle signals releasing new submissions
- Fixed IRQ heap corruption at startup and saved packet sizes before running packets
- Fixed shader cache loss: queued writes drained at exit, partial loads rejected, damaged records skipped
- Fixed draws whose pipelines the driver refuses, speculative specialization exceptions and null user-data tables at area loads
- Updated texture pressure to start at 60% of the budget on cards below 8 GB, unblocked evictions behind retained images and kept GPU-only images below the critical mark
- Added tests for the slot vector, subresource extents, IRQ controller, record data, cache storage, cache flush, serdes and shader user data

---

PT-BR

- Corrigidos no cache de texturas: referências de imagem que mudavam de lugar quando o cache crescia, IDs de slot fora do intervalo, checagem de tamanho por dimensão, recorte de views, sobreposição só de padding e destruição de samplers em uso
- Corrigidas as cópias de buffers de depth e color para copiar todos os mips e layers compartilhados
- Corrigidos no scheduler: chunks nulos em 0x20000, exit 23 com dados de gravação grandes demais e sinais de ocioso antigos liberando submissões novas
- Corrigida a corrupção do heap de IRQ na inicialização, e o tamanho dos pacotes passou a ser salvo antes de executá-los
- Corrigida a perda do cache de shaders: gravações na fila esvaziadas ao sair, cargas parciais rejeitadas e registros danificados pulados
- Corrigidos os draws com pipeline recusado pelo driver, as exceções de especialização especulativa e as tabelas de user data nulas na troca de área
- Atualizada a pressão de texturas para começar em 60% do orçamento em placas com menos de 8 GB, desbloqueadas as remoções atrás de imagens retidas e mantidas as imagens só da GPU abaixo da marca crítica
- Adicionados testes de slot vector, subresource extents, controlador de IRQ, record data, cache storage, flush do cache, serdes e shader user data
2026-11-01 21:31:31 -03:00

115 lines
4.1 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <cassert>
#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <type_traits>
#include <vector>
#include "common/slot_vector.h"
// This standalone test uses the production header and page operations, with a fatal
// assertion sink instead of the GPU logger. It needs no renderer or game resources.
void assert_fail_impl() {
std::abort();
}
namespace Common::Log {
bool ShouldLog(Level) {
return true;
}
void Write(std::string_view, Level, const char*, int, const char*, std::string&& message) {
std::fprintf(stderr, "%s\n", message.c_str());
}
} // namespace Common::Log
struct alignas(64) Item {
explicit Item(unsigned value_) : value(value_) {
++constructed;
++live;
payload.fill(value);
}
Item(const Item&) = delete;
Item(Item&&) = delete;
~Item() {
--live;
++destroyed;
}
unsigned value;
std::array<unsigned, 38> payload; // 192-byte entries straddle 2 MiB chunk boundaries.
static inline unsigned live = 0, constructed = 0, destroyed = 0;
};
int main() {
constexpr unsigned count = 50000;
static_assert(sizeof(Item) == 192);
static_assert(!std::is_copy_constructible_v<Common::SlotVector<Item>>);
static_assert(!std::is_move_constructible_v<Common::SlotVector<Item>>);
std::vector<Common::SlotId> ids;
std::vector<Item*> addresses;
ids.reserve(count);
addresses.reserve(count);
{
Common::SlotVector<Item> pool;
assert(pool.size() == 0);
assert(pool.begin() == pool.end());
// The first 2 MiB chunk rounds its allocation bitset up to a full word.
constexpr u32 initial_capacity = (2u << 20) / sizeof(Item);
constexpr u32 bitset_end = ((initial_capacity + 63) / 64) * 64;
assert(!pool.is_allocated(Common::SlotId{0}));
assert(!pool.is_allocated(Common::SlotId{63}));
assert(!pool.is_allocated(Common::SlotId{64}));
assert(!pool.is_allocated(Common::SlotId{bitset_end - 1}));
assert(!pool.is_allocated(Common::SlotId{bitset_end}));
assert(!pool.is_allocated(Common::SlotId{}));
assert(!pool.is_allocated(Common::SlotId{Common::SlotId::INVALID_INDEX - 1}));
for (unsigned i = 0; i < count; ++i) {
ids.push_back(pool.insert(i));
assert(pool.is_allocated(ids.back()));
addresses.push_back(&pool[ids.back()]);
assert(reinterpret_cast<std::uintptr_t>(addresses.back()) % alignof(Item) == 0);
assert(&pool[ids.front()] == addresses.front());
}
assert(pool.size() == count);
assert(Item::live == count);
assert(!pool.is_allocated(Common::SlotId{}));
assert(!pool.is_allocated(Common::SlotId{Common::SlotId::INVALID_INDEX - 1}));
unsigned visited = 0;
for (auto& item : pool) {
assert(item.value == visited);
assert(&item == addresses[visited]);
for (auto value : item.payload) {
assert(value == visited);
}
++visited;
}
assert(visited == count);
for (unsigned i = 0; i < count; i += 2) {
pool.erase(ids[i]);
assert(!pool.is_allocated(ids[i]));
}
assert(pool.size() == count / 2);
visited = 0;
for (auto& item : pool) {
assert(item.value == visited * 2 + 1);
++visited;
}
assert(visited == count / 2);
for (unsigned i = 0; i < count / 2; ++i) {
const auto id = pool.insert(count + i);
assert(pool.is_allocated(id));
assert(id.index % 2 == 0);
assert(&pool[id] == addresses[id.index]);
}
for (unsigned i = 1; i < count; i += 2) {
assert(&pool[ids[i]] == addresses[i]);
assert(pool[ids[i]].value == i);
}
assert(pool.size() == count);
}
assert(Item::live == 0);
assert(Item::constructed == count + count / 2);
assert(Item::destroyed == Item::constructed);
std::puts("SlotVector: 50000 stable entries, alignment, iteration, reuse and destruction passed");
}