Report effective visibility and stable GPU instance identity
Native and manual checks / native (ubuntu-24.04) (push) Failing after 36s
Native and manual checks / manual (push) Successful in 28s
Windows editor and software Vulkan / windows-graphics (push) Canceled after 0s
Native and manual checks / native (windows-2025) (push) Canceled after 0s
Native and manual checks / native (ubuntu-24.04) (push) Failing after 36s
Native and manual checks / manual (push) Successful in 28s
Windows editor and software Vulkan / windows-graphics (push) Canceled after 0s
Native and manual checks / native (windows-2025) (push) Canceled after 0s
This commit is contained in:
@@ -47,6 +47,9 @@ with tempfile.TemporaryDirectory(prefix="faset-player-diagnostics-") as temporar
|
||||
assert selected.returncode == 0, (mode, selected.stdout, selected.stderr)
|
||||
mode_report = json.loads(mode_profile.read_text(encoding="utf-8"))
|
||||
assert mode_report["visibility_mode"] == mode, mode_report
|
||||
assert mode_report["effective_visibility_mode"] == mode, mode_report
|
||||
assert all(sample["effective_visibility_mode"] == mode
|
||||
for sample in mode_report["samples"]), mode_report["samples"]
|
||||
assert all(sample["gpu_visibility_active"] is active
|
||||
for sample in mode_report["samples"]), mode_report["samples"]
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <faset/render/renderer.hpp>
|
||||
#include <faset/render/visibility.hpp>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -15,6 +18,49 @@ void require(bool condition, const std::string& message) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
void stable_gpu_identity_metadata() {
|
||||
auto mesh = cube_mesh();
|
||||
auto replacement = std::make_shared<Mesh>(*mesh);
|
||||
const Bounds bounds{{-1, -1, -1}, {1, 1, 1}};
|
||||
InstanceTracker tracker;
|
||||
const auto first = tracker.update("first", mesh, identity, bounds, "game");
|
||||
const auto second = tracker.update("second", mesh, identity, bounds, "game");
|
||||
const auto first_metadata = gpu_instance_metadata(first, true);
|
||||
const auto second_metadata = gpu_instance_metadata(second, true);
|
||||
require(first_metadata[0] == 0 && first_metadata[1] == first.slot &&
|
||||
first_metadata[2] == 1 && first_metadata[3] == 0 &&
|
||||
second_metadata[1] == second.slot && first_metadata[1] != second_metadata[1],
|
||||
"New tracked instances need distinct stable GPU identities without history");
|
||||
tracker.finish_frame();
|
||||
|
||||
const auto reordered_second = tracker.update("second", mesh, identity, bounds, "game");
|
||||
const auto reordered_first = tracker.update("first", mesh, identity, bounds, "game");
|
||||
const auto reordered_metadata = gpu_instance_metadata(reordered_first, true);
|
||||
require(reordered_metadata[0] == 1 && reordered_metadata[1] == first_metadata[1] &&
|
||||
reordered_metadata[2] == first_metadata[2] &&
|
||||
reordered_metadata[3] == first_metadata[3] &&
|
||||
gpu_instance_metadata(reordered_second, true)[1] == second_metadata[1],
|
||||
"Reordering must preserve stable GPU identity and enable valid history");
|
||||
require(gpu_instance_metadata(reordered_first, false)[0] == 0,
|
||||
"Incompatible history must clear only the history-valid lane");
|
||||
|
||||
const auto changed = tracker.update("first", replacement, identity, bounds, "game");
|
||||
const auto changed_metadata = gpu_instance_metadata(changed, true);
|
||||
require(changed_metadata[1] == first_metadata[1] &&
|
||||
changed_metadata[2] != first_metadata[2] && changed_metadata[0] == 0,
|
||||
"Replacing a mesh must advance the stable GPU identity generation");
|
||||
|
||||
InstanceUpdate wide_generation{};
|
||||
wide_generation.slot = 17;
|
||||
wide_generation.generation = 0x12345678abcdef01ULL;
|
||||
const auto wide_metadata = gpu_instance_metadata(wide_generation, true);
|
||||
require(wide_metadata[1] == 17 && wide_metadata[2] == 0xabcdef01U &&
|
||||
wide_metadata[3] == 0x12345678U,
|
||||
"GPU metadata must preserve all 64 generation bits");
|
||||
require(gpu_instance_metadata({}, true) == std::array<std::uint32_t, 4>{0, 0, 0, 0},
|
||||
"Anonymous draws have generation zero and are untracked");
|
||||
}
|
||||
|
||||
RendererConfig config(VisibilityMode mode) {
|
||||
RendererConfig result;
|
||||
result.width = 320;
|
||||
@@ -60,12 +106,15 @@ std::size_t different_pixels(const std::vector<std::uint8_t>& a,
|
||||
|
||||
int main() {
|
||||
try {
|
||||
stable_gpu_identity_metadata();
|
||||
Renderer direct(config(VisibilityMode::Direct));
|
||||
Renderer gpu(config(VisibilityMode::GpuFrustum));
|
||||
auto frame = scene();
|
||||
direct.render(frame);
|
||||
gpu.render(frame);
|
||||
require(gpu.stats().gpu_visibility_active, "GPU visibility path did not run");
|
||||
require(gpu.stats().effective_visibility_mode == VisibilityMode::GpuFrustum,
|
||||
"GPU frustum request did not use the frustum path");
|
||||
require(gpu.stats().gpu_bins == 1 && gpu.stats().gpu_visible_instances == 1 &&
|
||||
gpu.stats().gpu_frustum_rejected == 1,
|
||||
"GPU frustum/indirect counts are wrong");
|
||||
@@ -94,6 +143,8 @@ int main() {
|
||||
"Visibility counters can be enabled for diagnostics");
|
||||
Renderer occlusion(config(VisibilityMode::GpuOcclusion));
|
||||
occlusion.render(scene());
|
||||
require(occlusion.stats().effective_visibility_mode == VisibilityMode::GpuOcclusion,
|
||||
"GPU occlusion request silently selected another path");
|
||||
auto hzb = occlusion.hzb_debug_image(0);
|
||||
if (occlusion.stats().gpu_ms > 0)
|
||||
require(occlusion.stats().gpu_hzb_ms > 0 &&
|
||||
|
||||
@@ -119,6 +119,23 @@ void lod_thresholds_have_hysteresis_and_fallback() {
|
||||
rejects([&] { select_lod(std::numeric_limits<float>::quiet_NaN(), 0, thresholds, .1f); },
|
||||
"Nonfinite projected size cannot silently select a level");
|
||||
}
|
||||
void effective_mode_exposes_device_fallback() {
|
||||
require(select_effective_visibility_mode(VisibilityMode::Direct, true, true) ==
|
||||
VisibilityMode::Direct,
|
||||
"Direct request stays Direct even on a fully capable device");
|
||||
require(select_effective_visibility_mode(VisibilityMode::GpuFrustum, false, true) ==
|
||||
VisibilityMode::Direct,
|
||||
"Missing GPU culling profile falls back to Direct");
|
||||
require(select_effective_visibility_mode(VisibilityMode::GpuFrustum, true, false) ==
|
||||
VisibilityMode::GpuFrustum,
|
||||
"GPU frustum does not depend on HZB support");
|
||||
require(select_effective_visibility_mode(VisibilityMode::GpuOcclusion, true, false) ==
|
||||
VisibilityMode::GpuFrustum,
|
||||
"Missing HZB exposes a frustum-only effective mode");
|
||||
require(select_effective_visibility_mode(VisibilityMode::GpuOcclusion, true, true) ==
|
||||
VisibilityMode::GpuOcclusion,
|
||||
"Supported occlusion keeps the requested effective mode");
|
||||
}
|
||||
} // namespace
|
||||
int main() {
|
||||
try {
|
||||
@@ -126,6 +143,7 @@ int main() {
|
||||
ids_track_rendered_frames_and_generation();
|
||||
owned_mesh_identity_outlives_reimport_gap();
|
||||
lod_thresholds_have_hysteresis_and_fallback();
|
||||
effective_mode_exposes_device_fallback();
|
||||
std::cout << "Visibility bounds, instance identity and LOD policy passed\n";
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
|
||||
Reference in New Issue
Block a user