Resolve field volume texture resources
This commit is contained in:
parent
6cdfc9f3a6
commit
024b439006
10 changed files with 307 additions and 18 deletions
|
|
@ -388,6 +388,32 @@ public readonly record struct AquariumFieldResourceDeclaration(
|
|||
NativeHandleKind: string.IsNullOrWhiteSpace(sourceUri) ? "fensalir-surface-page" : "local-asset",
|
||||
SourceUri: sourceUri);
|
||||
|
||||
public static AquariumFieldResourceDeclaration VolumeTexture(
|
||||
string resourceKey,
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
string format = "R16Float",
|
||||
ulong version = 0,
|
||||
long validFromNs = 0,
|
||||
long validUntilNs = 0) =>
|
||||
new(
|
||||
ResourceKey: resourceKey,
|
||||
Kind: AquariumFieldResourceKind.VolumeTexture,
|
||||
Residency: AquariumFieldResourceResidency.GpuResident,
|
||||
Access: AquariumFieldShaderAccess.ShaderResource,
|
||||
Format: format,
|
||||
Width: Math.Max(1, width),
|
||||
Height: Math.Max(1, height),
|
||||
DepthOrCount: Math.Max(1, depth),
|
||||
StrideBytes: FormatStrideBytes(format),
|
||||
ValidFromNs: validFromNs,
|
||||
ValidUntilNs: validUntilNs,
|
||||
Version: version,
|
||||
NativeHandle: IntPtr.Zero,
|
||||
NativeHandleKind: "fensalir-volume-texture",
|
||||
SourceUri: "");
|
||||
|
||||
private static int FormatStrideBytes(string format) =>
|
||||
format switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ public static class AquariumFieldScriptCompiler
|
|||
case "surfacepage":
|
||||
BindSurfacePage(args, resourceAliases, resources, lineIndex);
|
||||
break;
|
||||
case "volumetexture":
|
||||
BindVolumeTexture(args, resourceAliases, resources, lineIndex);
|
||||
break;
|
||||
case "domain":
|
||||
var domain = ParseFieldDomain(args, lineIndex);
|
||||
if (domainKeys.Add(domain.DomainKey))
|
||||
|
|
@ -128,6 +131,30 @@ public static class AquariumFieldScriptCompiler
|
|||
}
|
||||
}
|
||||
|
||||
private static void BindVolumeTexture(
|
||||
IReadOnlyDictionary<string, string> args,
|
||||
IDictionary<string, AquariumFieldResourceDeclaration> resourceAliases,
|
||||
ICollection<AquariumFieldResourceDeclaration> resources,
|
||||
int lineIndex)
|
||||
{
|
||||
var id = Required(args, "id", lineIndex);
|
||||
var key = StringValue(args, "key", $"aquarium:resource:volume-texture:{id}");
|
||||
var resource = AquariumFieldResourceDeclaration.VolumeTexture(
|
||||
key,
|
||||
Int(args, "width", 1, lineIndex),
|
||||
Int(args, "height", 1, lineIndex),
|
||||
Int(args, "depth", 1, lineIndex),
|
||||
StringValue(args, "format", "R16Float"),
|
||||
UInt64(args, "version", 0, lineIndex));
|
||||
|
||||
resourceAliases[id] = resource;
|
||||
resourceAliases[key] = resource;
|
||||
if (resources.All(existing => !string.Equals(existing.ResourceKey, resource.ResourceKey, StringComparison.Ordinal)))
|
||||
{
|
||||
resources.Add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
public static AquariumBufferFieldFrame Compile(
|
||||
string source,
|
||||
IReadOnlyDictionary<string, AquariumTextureFieldBinding> textureBindings,
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ internal readonly record struct D3D12FieldResourceStats(
|
|||
int StructuredBuffers,
|
||||
int Texture2D,
|
||||
int SurfacePages,
|
||||
int VolumeTextures,
|
||||
int Unsupported,
|
||||
int StaleRemoved)
|
||||
{
|
||||
public static D3D12FieldResourceStats Empty { get; } = new(0, 0, 0, 0, 0, 0, 0);
|
||||
public static D3D12FieldResourceStats Empty { get; } = new(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
internal sealed class D3D12FieldResourceRegistry : IDisposable
|
||||
|
|
@ -21,6 +22,7 @@ internal sealed class D3D12FieldResourceRegistry : IDisposable
|
|||
|
||||
private readonly Dictionary<string, StructuredBufferSlot> structuredBuffers = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, Texture2DSlot> textures = new(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, VolumeTextureSlot> volumeTextures = new(StringComparer.Ordinal);
|
||||
private readonly HashSet<string> liveKeys = new(StringComparer.Ordinal);
|
||||
|
||||
public D3D12FieldResourceStats Resolve(
|
||||
|
|
@ -38,6 +40,7 @@ internal sealed class D3D12FieldResourceRegistry : IDisposable
|
|||
var structuredBufferCount = 0;
|
||||
var textureCount = 0;
|
||||
var surfacePageCount = 0;
|
||||
var volumeTextureCount = 0;
|
||||
var unsupported = 0;
|
||||
foreach (var declaration in declarations)
|
||||
{
|
||||
|
|
@ -93,17 +96,34 @@ internal sealed class D3D12FieldResourceRegistry : IDisposable
|
|||
continue;
|
||||
}
|
||||
|
||||
if (declaration.Kind == AquariumFieldResourceKind.VolumeTexture)
|
||||
{
|
||||
volumeTextureCount++;
|
||||
if (ResolveVolumeTexture(device, declaration))
|
||||
{
|
||||
resolved++;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsupported++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
unsupported++;
|
||||
}
|
||||
|
||||
var staleRemoved = RemoveStaleStructuredBuffers(resourceRegistry, liveKeys);
|
||||
staleRemoved += RemoveStaleTextures(liveKeys);
|
||||
staleRemoved += RemoveStaleVolumeTextures(liveKeys);
|
||||
return new D3D12FieldResourceStats(
|
||||
Declared: declarations.Count,
|
||||
Resolved: resolved,
|
||||
StructuredBuffers: structuredBufferCount,
|
||||
Texture2D: textureCount,
|
||||
SurfacePages: surfacePageCount,
|
||||
VolumeTextures: volumeTextureCount,
|
||||
Unsupported: unsupported,
|
||||
StaleRemoved: staleRemoved);
|
||||
}
|
||||
|
|
@ -120,8 +140,14 @@ internal sealed class D3D12FieldResourceRegistry : IDisposable
|
|||
slot.Texture.Dispose();
|
||||
}
|
||||
|
||||
foreach (var slot in volumeTextures.Values)
|
||||
{
|
||||
slot.Texture.Dispose();
|
||||
}
|
||||
|
||||
structuredBuffers.Clear();
|
||||
textures.Clear();
|
||||
volumeTextures.Clear();
|
||||
liveKeys.Clear();
|
||||
}
|
||||
|
||||
|
|
@ -162,6 +188,18 @@ internal sealed class D3D12FieldResourceRegistry : IDisposable
|
|||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetVolumeTexture(string resourceKey, out D3D12FieldTexture3D volumeTexture)
|
||||
{
|
||||
if (volumeTextures.TryGetValue(resourceKey, out var slot))
|
||||
{
|
||||
volumeTexture = slot.Texture;
|
||||
return true;
|
||||
}
|
||||
|
||||
volumeTexture = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryResolveTexture2D(
|
||||
ID3D12Device device,
|
||||
ID3D12GraphicsCommandList commandList,
|
||||
|
|
@ -386,6 +424,45 @@ internal sealed class D3D12FieldResourceRegistry : IDisposable
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool ResolveVolumeTexture(
|
||||
ID3D12Device device,
|
||||
AquariumFieldResourceDeclaration declaration)
|
||||
{
|
||||
if (declaration.Residency != AquariumFieldResourceResidency.GpuResident ||
|
||||
declaration.Access != AquariumFieldShaderAccess.ShaderResource)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (volumeTextures.TryGetValue(declaration.ResourceKey, out var existing) &&
|
||||
existing.Version == declaration.Version &&
|
||||
existing.Width == declaration.Width &&
|
||||
existing.Height == declaration.Height &&
|
||||
existing.Depth == declaration.DepthOrCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (existing is not null)
|
||||
{
|
||||
existing.Texture.Dispose();
|
||||
volumeTextures.Remove(declaration.ResourceKey);
|
||||
}
|
||||
|
||||
if (!D3D12FieldTexture3D.TryCreateEmpty(device, declaration, out var volumeTexture))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
volumeTextures[declaration.ResourceKey] = new VolumeTextureSlot(
|
||||
volumeTexture,
|
||||
declaration.Width,
|
||||
declaration.Height,
|
||||
declaration.DepthOrCount,
|
||||
declaration.Version);
|
||||
return true;
|
||||
}
|
||||
|
||||
private int RemoveStaleStructuredBuffers(D3D12ResourceRegistry resourceRegistry, HashSet<string> currentKeys)
|
||||
{
|
||||
var removed = 0;
|
||||
|
|
@ -423,6 +500,24 @@ internal sealed class D3D12FieldResourceRegistry : IDisposable
|
|||
return removed;
|
||||
}
|
||||
|
||||
private int RemoveStaleVolumeTextures(HashSet<string> currentKeys)
|
||||
{
|
||||
var removed = 0;
|
||||
foreach (var key in volumeTextures.Keys.ToArray())
|
||||
{
|
||||
if (currentKeys.Contains(key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
volumeTextures[key].Texture.Dispose();
|
||||
volumeTextures.Remove(key);
|
||||
removed++;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
private static string RegistryKey(string resourceKey) => RegistryPrefix + resourceKey;
|
||||
|
||||
private static D3D12StructuredBuffer? OpenSharedStructuredBuffer(
|
||||
|
|
@ -468,4 +563,11 @@ internal sealed class D3D12FieldResourceRegistry : IDisposable
|
|||
AquariumFieldResourceKind Kind,
|
||||
string SourceUri,
|
||||
ulong Version);
|
||||
|
||||
private sealed record VolumeTextureSlot(
|
||||
D3D12FieldTexture3D Texture,
|
||||
int Width,
|
||||
int Height,
|
||||
int Depth,
|
||||
ulong Version);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ internal sealed unsafe class D3D12FieldTexture2D : IDisposable
|
|||
out D3D12FieldTexture2D texture)
|
||||
{
|
||||
texture = null!;
|
||||
if (!TryFormat(declaration.Format, out var format))
|
||||
if (!D3D12FieldTextureFormat.TryFormat(declaration.Format, out var format))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -258,17 +258,4 @@ internal sealed unsafe class D3D12FieldTexture2D : IDisposable
|
|||
return (value + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
private static bool TryFormat(string formatName, out Format format)
|
||||
{
|
||||
format = formatName switch
|
||||
{
|
||||
"R8Unorm" or "R8_UNorm" or "R8_UNORM" => Format.R8_UNorm,
|
||||
"R16Float" or "R16_Float" or "R16_FLOAT" => Format.R16_Float,
|
||||
"R32Float" or "R32_Float" or "R32_FLOAT" or "Float32" => Format.R32_Float,
|
||||
"Rgba8Unorm" or "R8G8B8A8_UNorm" or "R8G8B8A8_UNORM" => Format.R8G8B8A8_UNorm,
|
||||
"Rgba16Float" or "R16G16B16A16_Float" or "R16G16B16A16_FLOAT" => Format.R16G16B16A16_Float,
|
||||
_ => Format.Unknown,
|
||||
};
|
||||
return format != Format.Unknown;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
101
src/Aquarium.Engine/Render/D3D12FieldTexture3D.cs
Normal file
101
src/Aquarium.Engine/Render/D3D12FieldTexture3D.cs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
using Vortice.Direct3D12;
|
||||
using Vortice.DXGI;
|
||||
|
||||
namespace Aquarium.Engine.Render;
|
||||
|
||||
internal sealed class D3D12FieldTexture3D : IDisposable
|
||||
{
|
||||
private D3D12FieldTexture3D(
|
||||
ID3D12Resource resource,
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
Format format,
|
||||
string name)
|
||||
{
|
||||
Resource = resource;
|
||||
Width = width;
|
||||
Height = height;
|
||||
Depth = depth;
|
||||
Format = format;
|
||||
Resource.Name = name;
|
||||
}
|
||||
|
||||
public ID3D12Resource Resource { get; }
|
||||
|
||||
public int Width { get; }
|
||||
|
||||
public int Height { get; }
|
||||
|
||||
public int Depth { get; }
|
||||
|
||||
public Format Format { get; }
|
||||
|
||||
public ResourceStates State { get; private set; } = ResourceStates.PixelShaderResource;
|
||||
|
||||
public static bool TryCreateEmpty(
|
||||
ID3D12Device device,
|
||||
AquariumFieldResourceDeclaration declaration,
|
||||
out D3D12FieldTexture3D texture)
|
||||
{
|
||||
texture = null!;
|
||||
if (!D3D12FieldTextureFormat.TryFormat(declaration.Format, out var format))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var width = Math.Max(1, declaration.Width);
|
||||
var height = Math.Max(1, declaration.Height);
|
||||
var depth = Math.Max(1, declaration.DepthOrCount);
|
||||
var resource = device.CreateCommittedResource(
|
||||
HeapType.Default,
|
||||
ResourceDescription.Texture3D(
|
||||
format,
|
||||
(uint)width,
|
||||
(uint)height,
|
||||
(ushort)Math.Min(depth, ushort.MaxValue),
|
||||
1,
|
||||
ResourceFlags.None),
|
||||
ResourceStates.PixelShaderResource,
|
||||
null);
|
||||
|
||||
texture = new D3D12FieldTexture3D(
|
||||
resource,
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
format,
|
||||
$"Aquarium D3D12 Field VolumeTexture {declaration.ResourceKey}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CreateShaderResourceView(ID3D12Device device, D3D12DescriptorSlot descriptor)
|
||||
{
|
||||
device.CreateShaderResourceView(
|
||||
Resource,
|
||||
new ShaderResourceViewDescription
|
||||
{
|
||||
Format = Format,
|
||||
ViewDimension = ShaderResourceViewDimension.Texture3D,
|
||||
Shader4ComponentMapping = ShaderComponentMapping.Default,
|
||||
Texture3D = new Texture3DShaderResourceView { MipLevels = 1 },
|
||||
},
|
||||
descriptor.Cpu);
|
||||
}
|
||||
|
||||
public void Transition(ID3D12GraphicsCommandList commandList, ResourceStates nextState)
|
||||
{
|
||||
if (State == nextState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
commandList.ResourceBarrier(ResourceBarrier.BarrierTransition(Resource, State, nextState));
|
||||
State = nextState;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Resource.Dispose();
|
||||
}
|
||||
}
|
||||
20
src/Aquarium.Engine/Render/D3D12FieldTextureFormat.cs
Normal file
20
src/Aquarium.Engine/Render/D3D12FieldTextureFormat.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using Vortice.DXGI;
|
||||
|
||||
namespace Aquarium.Engine.Render;
|
||||
|
||||
internal static class D3D12FieldTextureFormat
|
||||
{
|
||||
public static bool TryFormat(string formatName, out Format format)
|
||||
{
|
||||
format = formatName switch
|
||||
{
|
||||
"R8Unorm" or "R8_UNorm" or "R8_UNORM" => Format.R8_UNorm,
|
||||
"R16Float" or "R16_Float" or "R16_FLOAT" => Format.R16_Float,
|
||||
"R32Float" or "R32_Float" or "R32_FLOAT" or "Float32" => Format.R32_Float,
|
||||
"Rgba8Unorm" or "R8G8B8A8_UNorm" or "R8G8B8A8_UNORM" => Format.R8G8B8A8_UNorm,
|
||||
"Rgba16Float" or "R16G16B16A16_Float" or "R16G16B16A16_FLOAT" => Format.R16G16B16A16_Float,
|
||||
_ => Format.Unknown,
|
||||
};
|
||||
return format != Format.Unknown;
|
||||
}
|
||||
}
|
||||
|
|
@ -3031,6 +3031,7 @@ public sealed class D3D12Renderer : IAquariumRenderer
|
|||
$"structured buffers {activeFieldResourceStats.StructuredBuffers:N0}; " +
|
||||
$"texture2d {activeFieldResourceStats.Texture2D:N0}; " +
|
||||
$"surface pages {activeFieldResourceStats.SurfacePages:N0}; " +
|
||||
$"volume textures {activeFieldResourceStats.VolumeTextures:N0}; " +
|
||||
$"unsupported resources {activeFieldResourceStats.Unsupported:N0}; " +
|
||||
$"claims {activeFieldEvidenceFrame.Claims.Count:N0}; " +
|
||||
$"candidates {activeFieldEvidenceFrame.Candidates.Count:N0}; " +
|
||||
|
|
|
|||
|
|
@ -240,3 +240,4 @@
|
|||
{"lesson":"Texture2D local assets are now first-class field resources. AquariumFieldResourceDeclaration carries SourceUri and has a LocalTexture2D factory; the field DSL accepts `texture2d id=... key=... path=...` and TubeField `ramp=...` resolves aliases into RampResourceKey. D3D12FieldResourceRegistry lazily uploads local Rgba8 assets into GPU-resident textures, TubeField rendering binds the ramp SRV plus linear clamp sampler, and unresolved ramps use a Fensalir-owned 0-1 fallback texture. This replaces raw ramp paths as live authority while keeping them as compatibility metadata. Fensalir build/tests/headless reload and Mimir build/field smokes passed.","timestamp":"2026-05-26T20:40:00+01:00","files":["src/Aquarium.Engine.Contracts/Render/AquariumFieldEvidence.cs","src/Aquarium.Engine.Contracts/Render/AquariumFieldScriptCompiler.cs","src/Aquarium.Engine/Render/D3D12FieldTexture2D.cs","src/Aquarium.Engine/Render/D3D12FieldResourceRegistry.cs","src/Aquarium.Engine/Render/D3D12Renderer.cs","src/Aquarium.Engine/Render/Shaders/D3D12TubeField.hlsl","tests/Aquarium.Engine.Tests/FieldScriptCompilerTests.cs","state/map.yaml"],"source":"user requested Texture2D local asset resource declaration and automatic DSL binding","topic":"field-texture2d-resource-binding"}
|
||||
{"lesson":"TubeField draw dimensions are now emitted as GPU-resident indirect draw packets. The expansion compute shader writes one D3D12 DrawIndexedArguments packet per resolved TubeField batch into a UAV argument buffer; render binds the matching source/ramp material state and calls ExecuteIndirect instead of CPU-manufacturing DrawIndexedInstanced counts. CPU still schedules one indirect call per material/resource batch because each batch has distinct root bindings, but draw count/start index authority now lives in the GPU expansion output. Fensalir build/tests/headless reload and Mimir build/field smokes passed.","timestamp":"2026-05-26T20:59:00+01:00","files":["src/Aquarium.Engine/Render/D3D12Renderer.cs","src/Aquarium.Engine/Render/Shaders/D3D12TubeField.hlsl","state/map.yaml"],"source":"continued migration plan after Texture2D resolver cut","topic":"d3d12-tubefield-indirect-draw-packets"}
|
||||
{"lesson":"SurfacePage is now a resolvable field resource kind rather than just a planner enum. AquariumFieldResourceDeclaration has a SurfacePage factory, the field evidence DSL accepts `surfacepage id=... key=... width=... height=... format=...`, and D3D12FieldResourceRegistry allocates empty GPU-resident shader-readable page textures or lazily loads local source-backed pages through the same texture upload path. This does not finish terrain/heightfield lowering; it creates the resource authority that future Height/Sdf2D/Material packets can bind. Fensalir build/tests/headless reload and Mimir build/field smokes passed.","timestamp":"2026-05-26T21:05:00+01:00","files":["src/Aquarium.Engine.Contracts/Render/AquariumFieldEvidence.cs","src/Aquarium.Engine.Contracts/Render/AquariumFieldScriptCompiler.cs","src/Aquarium.Engine/Render/D3D12FieldTexture2D.cs","src/Aquarium.Engine/Render/D3D12FieldResourceRegistry.cs","src/Aquarium.Engine/Render/D3D12Renderer.cs","tests/Aquarium.Engine.Tests/FieldScriptCompilerTests.cs","state/map.yaml"],"source":"continued field resource resolver migration","topic":"field-surface-page-resource-resolver"}
|
||||
{"lesson":"VolumeTexture is now a resolvable field resource kind. AquariumFieldResourceDeclaration has a VolumeTexture factory, the field evidence DSL accepts `volumetexture id=... key=... width=... height=... depth=... format=...`, and D3D12FieldResourceRegistry allocates empty GPU-resident shader-readable Texture3D resources with format/dimension/version tracking. This creates resource authority for future density/extinction/SDF3D lowerings without pretending those render paths exist yet. Fensalir build/tests/headless reload and Mimir build/field smokes passed.","timestamp":"2026-05-26T21:11:00+01:00","files":["src/Aquarium.Engine.Contracts/Render/AquariumFieldEvidence.cs","src/Aquarium.Engine.Contracts/Render/AquariumFieldScriptCompiler.cs","src/Aquarium.Engine/Render/D3D12FieldTexture3D.cs","src/Aquarium.Engine/Render/D3D12FieldTextureFormat.cs","src/Aquarium.Engine/Render/D3D12FieldResourceRegistry.cs","src/Aquarium.Engine/Render/D3D12Renderer.cs","tests/Aquarium.Engine.Tests/FieldScriptCompilerTests.cs","state/map.yaml"],"source":"continued field resource resolver migration","topic":"field-volume-texture-resource-resolver"}
|
||||
|
|
|
|||
|
|
@ -100,9 +100,12 @@ next_actions:
|
|||
reporting. SurfacePage resources now have a first resolver: contracts and
|
||||
DSL can declare GPU-resident 2D pages with width, height, format, version,
|
||||
and optional local source, and the D3D12 registry allocates/imports them as
|
||||
shader-readable page textures. Remaining cuts are mesh and volume resolvers,
|
||||
plus render lowerings that consume surface pages as height/SDF/material
|
||||
domains.
|
||||
shader-readable page textures. VolumeTexture resources now have the same
|
||||
first resolver shape: contracts and DSL can declare GPU-resident 3D
|
||||
textures with dimensions, depth, format, and version, and the D3D12 registry
|
||||
allocates them as shader-readable volume textures. Remaining resolver cut
|
||||
is Mesh, plus render lowerings that consume surface pages and volumes as
|
||||
height/SDF/material/density domains.
|
||||
- Move any remaining client-only Epiphany state found here back to `E:\Projects\Aquarium-Engine`.
|
||||
- Keep Mimir app/adapters out of this repo; Fensalir may expose generic sensor-fusion contracts only.
|
||||
- Package contracts/host deliberately before replacing sibling source references in client repos.
|
||||
|
|
|
|||
|
|
@ -147,4 +147,25 @@ public sealed class FieldScriptCompilerTests
|
|||
Assert.Equal("R16Float", resource.Format);
|
||||
Assert.Equal("fensalir-surface-page", resource.NativeHandleKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompileEvidenceDeclaresVolumeTexturesAsGpuResources()
|
||||
{
|
||||
var frame = AquariumFieldScriptCompiler.CompileEvidence(
|
||||
"""
|
||||
volumetexture id=density key=aquarium:resource:volume:density width=32 height=32 depth=16 format=R16Float version=5
|
||||
""",
|
||||
new Dictionary<string, AquariumFieldResourceDeclaration>(StringComparer.Ordinal));
|
||||
|
||||
var resource = Assert.Single(frame.Resources);
|
||||
Assert.Equal("aquarium:resource:volume:density", resource.ResourceKey);
|
||||
Assert.Equal(AquariumFieldResourceKind.VolumeTexture, resource.Kind);
|
||||
Assert.Equal(AquariumFieldResourceResidency.GpuResident, resource.Residency);
|
||||
Assert.Equal(AquariumFieldShaderAccess.ShaderResource, resource.Access);
|
||||
Assert.Equal(32, resource.Width);
|
||||
Assert.Equal(32, resource.Height);
|
||||
Assert.Equal(16, resource.DepthOrCount);
|
||||
Assert.Equal("R16Float", resource.Format);
|
||||
Assert.Equal("fensalir-volume-texture", resource.NativeHandleKind);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue