Files
EVEngine/test/gpu_driven.cpp
T
西风逍遥游 674643c2da feat(graphics): GPU-driven phase 0 - bindless set, GPU tables, per-frame arena
- GpuDrivenCaps: probe + enable Vulkan 1.2 core features (drawIndirectCount,
  descriptor indexing, non-uniform indexing, update-after-bind) at device
  creation; the toolchain's Vulkan headers omit the core drawIndirect bit so
  it is assumed supported and left untouched.
- Bindless Set1: sampler2D array (4096) + samplerCube array (64) + SSBO table
  array (64) with PARTIALLY_BOUND | UPDATE_AFTER_BIND; textures register on
  creation, placeholder-filled with the 1x1 white texture/cubemap.
- GPU tables: GpuMeshRecord (bounds + index/vertex ranges) filled at mesh
  upload and GpuMaterialRecord built from Material params, uploaded to
  host-visible SSBOs with fixed capacity.
- FrameArena: per-frame-slot host-visible ring allocator, reset each
  begin3DFrame; unused until phase 1 (indirect draw).
- Tests: GpuDriven.capsAvailable / bindlessTextureRegistration /
  meshTableRegistration (GPU runtime verification pending driver recovery).

Rendering is unchanged: nothing consumes the new infrastructure yet.
2026-08-20 21:17:17 +08:00

91 lines
2.9 KiB
C++

#include "zeroerr/assert.h"
#include "zeroerr/unittest.h"
#include <SDL2/SDL.h>
#include "graphics/Graphics.h"
#include "graphics/Mesh.h"
#include "graphics/Texture.h"
#include "graphics/vulkan/Graphics.h"
#include "graphics/vulkan/GpuDriven.h"
#include "window/Window.h"
using namespace eve::graphics;
namespace {
void openGfxWindow(eve::window::Window *&win, Graphics *&gfx, int w = 320, int h = 240) {
win = eve::window::Window::create();
gfx = Graphics::create();
REQUIRE(win != nullptr);
REQUIRE(gfx != nullptr);
win->setGraphics(gfx);
eve::window::WindowSettings s;
s.width = w;
s.height = h;
s.centered = true;
REQUIRE(win->setWindowSettings(s));
}
} // namespace
TEST_CASE("GpuDriven.capsAvailable") {
eve::window::Window *win = nullptr;
Graphics *gfx = nullptr;
openGfxWindow(win, gfx);
auto *vg = dynamic_cast<eve::graphics::vulkan::Graphics *>(gfx);
REQUIRE(vg != nullptr);
CHECK(vg->gpuDrivenCaps().gpuDrivenAvailable());
win->close();
}
TEST_CASE("GpuDriven.bindlessTextureRegistration") {
eve::window::Window *win = nullptr;
Graphics *gfx = nullptr;
openGfxWindow(win, gfx);
auto *vg = dynamic_cast<eve::graphics::vulkan::Graphics *>(gfx);
REQUIRE(vg != nullptr);
if (!vg->gpuDrivenCaps().gpuDrivenAvailable()) {
win->close();
return; // environment without 1.2 features: nothing to validate
}
const uint8_t red[4] = {255, 0, 0, 255};
const uint8_t green[4] = {0, 255, 0, 255};
Texture *a = gfx->newTexture(1, 1, red);
Texture *b = gfx->newTexture(1, 1, green);
const uint32_t sa = vg->debugBindlessIndex(a);
const uint32_t sb = vg->debugBindlessIndex(b);
CHECK(sa != eve::graphics::vulkan::kInvalidBindlessSlot);
CHECK(sb != eve::graphics::vulkan::kInvalidBindlessSlot);
CHECK(sa != sb); // distinct slots
const std::vector<uint8_t> cubePx(6 * 4, 128);
Texture *cube = gfx->newCubemap(1, cubePx.data());
auto *gpuCube = static_cast<eve::graphics::vulkan::GpuTexture *>(cube->gpuHandle);
CHECK(gpuCube->bindlessIndexCube != eve::graphics::vulkan::kInvalidBindlessSlot);
win->close();
}
TEST_CASE("GpuDriven.meshTableRegistration") {
eve::window::Window *win = nullptr;
Graphics *gfx = nullptr;
openGfxWindow(win, gfx);
auto *vg = dynamic_cast<eve::graphics::vulkan::Graphics *>(gfx);
REQUIRE(vg != nullptr);
if (!vg->gpuDrivenCaps().gpuDrivenAvailable()) {
win->close();
return;
}
Mesh *m = gfx->newMeshSphere(8, 4);
REQUIRE(m != nullptr);
const uint32_t idx = vg->debugMeshRecordIndex(m);
CHECK(idx != eve::graphics::vulkan::kInvalidBindlessSlot);
auto *gpu = static_cast<eve::graphics::vulkan::GpuMesh *>(m->gpuHandle);
CHECK(gpu->record.vertexCount > 0);
CHECK(gpu->record.indexCount > 0);
CHECK(gpu->record.boundsCenterRadius.w > 0.f); // bounds computed at upload
win->close();
}