phase(N.5) Task 3: TextureCache bindless GetOrUpload + parallel cache

Adds three Bindless variants (GetOrUploadBindless,
GetOrUploadWithOrigTextureOverrideBindless,
GetOrUploadWithPaletteOverrideBindless) that decode + upload via
UploadRgba8AsLayer1Array (Texture2DArray) and cache in three new
dictionaries that mirror the legacy three-cache structure. Each entry
stores both the GL texture name (for Dispose cleanup in Task 4) and
the resident bindless handle.

Constructor gains optional BindlessSupport param; null keeps backward
compat. EnsureBindlessAvailable throws InvalidOperationException if
Bindless* methods are called without BindlessSupport (fail-fast vs
silent zero handle that would produce GPU faults).

Dispose extended to make handles non-resident before deleting the
underlying Texture2DArray names (bindless handles must be made
non-resident before the texture is deleted; skipping this causes
GPU faults on driver cleanup).

Marker test in TextureCacheBindlessTests documents the throw contract
for future engineers; real bindless integration is verified at
Task 14's visual gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-08 19:53:10 +02:00
parent 4b9a9bb721
commit 0d96716825
2 changed files with 130 additions and 1 deletions

View file

@ -0,0 +1,32 @@
using AcDream.App.Rendering;
using AcDream.App.Rendering.Wb;
using DatReaderWriter;
using Xunit;
namespace AcDream.Core.Tests.Rendering;
/// <summary>
/// Lightweight unit tests for <see cref="TextureCache"/>'s bindless path.
/// We can't construct a real TextureCache in a headless test (it requires a
/// live GL context), so this file documents contracts that future engineers
/// should preserve. Real bindless integration is verified at Task 14's
/// visual gate.
/// </summary>
public sealed class TextureCacheBindlessTests
{
[Fact]
public void Contract_BindlessMethodsThrowWithoutBindlessSupport()
{
// The actual throw lives in TextureCache.EnsureBindlessAvailable
// and is reached only via GL-bound Bindless* method calls. The
// contract is: if the dispatcher (which requires bindless) ever
// gets a TextureCache constructed without BindlessSupport, it
// should fail-fast with InvalidOperationException — NOT silently
// route a draw to handle 0 (which would produce a non-resident
// GPU fault).
//
// This test is a marker. Future engineers: do not weaken
// EnsureBindlessAvailable to swallow the missing dependency.
Assert.True(true, "Contract documented in TextureCache.EnsureBindlessAvailable");
}
}