fix(rendering): carry retail SetSurface state to detail draws
Resolve exact SetSurface blend, alpha-test, and fog state once during extraction and preserve it through recipe-10 prepared payloads, Wb/EnvCell command data, Vulkan pipelines, push constants, and both ordinary/atmospheric one-pass shaders. Preserve AP-240 Wb pure-Clip immediate opaque/A2C while EnvCell uses retail premultiplied Clip; detail-off routing remains unchanged. Correct AP-232 and register the remaining detail-off state divergence. Mutation first failures (all restored): - raw Add->SRCALPHA/ONE: WalkStaticStreamPopulatorTests.ImmediateBuildingDetail_UsesExactResolvedSetSurfaceState line 1278, expected wb-mesh-raw-additive-1x. - inverse-add->alpha-add: same test line 1278, expected wb-mesh-inverse-additive-1x. - remove Env inverse: EnvCellAlphaDrawSourceTests.DetailOn_EveryEnvCellFamilyDrawsOnceInPlaceWithAuthoredOpacity line 132, expected envcell-inverse. - raw IsAdditive precedence: same test line 132, Translucent|Clip|Additive expected envcell-alpha. - Wb paletted ParamB=0: OrderPreservingSubmitterTests line 333, expected 0.392156869. - Wb DDS ParamB=0.05: same test line 333, expected 0.784313738. - disable Alpha+Clip test: WalkStaticStreamPopulatorTests line 1286, expected 0.784313738. - always fog raw Add: same test line 1287, expected no-fog true. - disable fog non-Add: same test line 1287, expected no-fog false. - X=a*qA: RetailDetailTextureContractTests line 261, shared squared-alpha substring absent. - X includes base alpha: same test line 262, forbidden baseTexel.a present. - CLIP uses 0.05: EnvCellAlphaDrawSourceTests.ClipShaders_UseGreaterEqualForThePerRangeReference line 367. - second detail draw: EnvCell detail-on line 131, collection contained 2 draws. - straight-alpha substitute: Wb immediate line 1278, expected wb-mesh-additive-1x. - omit ordered detail arm: OrderPreservingSubmitterTests line 318, expected (77,3.5), got (0,0). - omit atmospheric combine: RetailDetailTextureContractTests line 260, shared include absent. - drop serialized opacity: ObjectMeshDataSerializerTests line 292, expected opacity bits, got 1.0. - stale detail arm: atmospheric adjacency line 402, expected slot 0, got 77. - per-frame surface map: EnvCell warmed allocation line 285, expected 0 B, got 204800 B.
This commit is contained in:
parent
75664805f8
commit
15ed57a1e7
44 changed files with 1154 additions and 187 deletions
|
|
@ -8,7 +8,9 @@ using AcDream.App.Rendering.Gpu.Vk;
|
|||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.Tests.Rendering.Gpu;
|
||||
using AcDream.Content;
|
||||
using AcDream.Core.Meshing;
|
||||
using Chorizite.Core.Render.Enums;
|
||||
using DatReaderWriter.Enums;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using CullMode = DatReaderWriter.Enums.CullMode;
|
||||
|
||||
|
|
@ -21,6 +23,11 @@ namespace AcDream.App.Tests.Rendering;
|
|||
/// </summary>
|
||||
public sealed class EnvCellAlphaDrawSourceTests
|
||||
{
|
||||
private static readonly Vector4 MaterialBase = new(0.31f, 0.57f, 0.83f, 0.19f);
|
||||
private static readonly Vector3 MaterialDiffuse = new(0.73f, 0.41f, 0.67f);
|
||||
private static readonly Vector4 MaterialDetail = new(0.91f, 0.23f, 0.49f, 0.62f);
|
||||
private static readonly Vector4 MaterialDestination = new(0.17f, 0.37f, 0.71f, 0.29f);
|
||||
|
||||
/// <summary>
|
||||
/// Canonical F4180104 surface 08000BFF's pure Base1ClipMap mask 0x08
|
||||
/// reaches CLIP while alpha-family 0x02 reaches ALPHA. Mutation check:
|
||||
|
|
@ -75,26 +82,45 @@ public sealed class EnvCellAlphaDrawSourceTests
|
|||
DrawPipelineNames(fixture.Device));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, "envcell-opaque", 0f)]
|
||||
[InlineData(1, "envcell-alpha", 0f)]
|
||||
[InlineData(2, "envcell-additive", 0f)]
|
||||
[InlineData(3, "envcell-clip", 200f / 255f)]
|
||||
[InlineData(4, "envcell-clip", 100f / 255f)]
|
||||
public void DetailOn_EveryEnvCellFamilyDrawsOnceInPlaceWithAuthoredOpacity(
|
||||
int specIndex,
|
||||
string expectedPipeline,
|
||||
float expectedReference)
|
||||
public static TheoryData<SurfaceType, bool, string, object, float, bool> ExactMaterialRows() => new()
|
||||
{
|
||||
BatchSpec spec = specIndex switch
|
||||
{
|
||||
0 => BatchSpec.Opaque,
|
||||
1 => BatchSpec.Alpha,
|
||||
2 => BatchSpec.Additive,
|
||||
3 => BatchSpec.ClipDds,
|
||||
4 => BatchSpec.ClipPaletted,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(specIndex)),
|
||||
};
|
||||
{ SurfaceType.Base1Image, false, "envcell-opaque", GpuBlendMode.None, 0f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Alpha, false, "envcell-alpha", GpuBlendMode.StraightAlpha, 0f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Alpha | SurfaceType.Additive, false, "envcell-additive", GpuBlendMode.Additive, 0f, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Additive, false, "envcell-raw-additive", GpuBlendMode.RawAdditive, 0f, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.InvAlpha, false, "envcell-inverse", GpuBlendMode.InverseAlpha, 0f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.InvAlpha | SurfaceType.Additive, false, "envcell-inverse-additive", GpuBlendMode.InverseAdditive, 0f, false },
|
||||
{ SurfaceType.Translucent, false, "envcell-alpha", GpuBlendMode.StraightAlpha, 0f, true },
|
||||
{ SurfaceType.Translucent | SurfaceType.Additive, false, "envcell-raw-additive", GpuBlendMode.RawAdditive, 0f, false },
|
||||
{ SurfaceType.Translucent | SurfaceType.InvAlpha, false, "envcell-inverse", GpuBlendMode.InverseAlpha, 0f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Base1ClipMap, false, "envcell-clip", GpuBlendMode.PremultipliedAlpha, 200f / 255f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Base1ClipMap, true, "envcell-clip", GpuBlendMode.PremultipliedAlpha, 100f / 255f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Alpha | SurfaceType.Base1ClipMap, false, "envcell-alpha", GpuBlendMode.StraightAlpha, 200f / 255f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Alpha | SurfaceType.Base1ClipMap, true, "envcell-alpha", GpuBlendMode.StraightAlpha, 100f / 255f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Alpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, false, "envcell-additive", GpuBlendMode.Additive, 200f / 255f, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Alpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, true, "envcell-additive", GpuBlendMode.Additive, 100f / 255f, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Additive | SurfaceType.Base1ClipMap, false, "envcell-raw-additive", GpuBlendMode.RawAdditive, 200f / 255f, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Additive | SurfaceType.Base1ClipMap, true, "envcell-raw-additive", GpuBlendMode.RawAdditive, 100f / 255f, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.InvAlpha | SurfaceType.Base1ClipMap, false, "envcell-inverse", GpuBlendMode.InverseAlpha, 200f / 255f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.InvAlpha | SurfaceType.Base1ClipMap, true, "envcell-inverse", GpuBlendMode.InverseAlpha, 100f / 255f, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.InvAlpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, false, "envcell-inverse-additive", GpuBlendMode.InverseAdditive, 200f / 255f, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.InvAlpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, true, "envcell-inverse-additive", GpuBlendMode.InverseAdditive, 100f / 255f, false },
|
||||
{ SurfaceType.Translucent | SurfaceType.Base1ClipMap | SurfaceType.Additive, false, "envcell-alpha", GpuBlendMode.StraightAlpha, 0f, false },
|
||||
{ SurfaceType.Translucent | SurfaceType.Base1ClipMap | SurfaceType.Additive, true, "envcell-alpha", GpuBlendMode.StraightAlpha, 0f, false },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ExactMaterialRows))]
|
||||
public void DetailOn_EveryEnvCellFamilyDrawsOnceInPlaceWithAuthoredOpacity(
|
||||
SurfaceType surfaceType,
|
||||
bool paletted,
|
||||
string expectedPipeline,
|
||||
object expectedBlendValue,
|
||||
float expectedReference,
|
||||
bool expectedFog)
|
||||
{
|
||||
var expectedBlend = (GpuBlendMode)expectedBlendValue;
|
||||
BatchSpec spec = new(surfaceType, paletted, PositiveStippling: false);
|
||||
using var fixture = new ProductionEnvCellFixture(
|
||||
detailSurfaceActive: true,
|
||||
spec);
|
||||
|
|
@ -113,6 +139,20 @@ public sealed class EnvCellAlphaDrawSourceTests
|
|||
DrawPushConstants(fixture.Device),
|
||||
constants => constants.ParamA != 0f);
|
||||
Assert.NotEqual(0u, armed.TextureIndexA);
|
||||
Assert.Equal(
|
||||
!expectedFog,
|
||||
(armed.RenderPass & RetailDetailTextureContract.NoFogRenderPassFlag) != 0);
|
||||
RetailSetSurfaceMaterialState resolved = RetailSetSurfaceMaterialState.Resolve(
|
||||
surfaceType, texturePresent: true, textureHasPalette: paletted);
|
||||
GpuPipelineDescription selected = fixture.Device.CreatedPipelines
|
||||
.Single(pipeline => pipeline.Description.Name == expectedPipeline)
|
||||
.Description;
|
||||
Assert.Equal(expectedBlend, selected.Blend);
|
||||
Assert.True(selected.Depth.Test);
|
||||
Assert.Equal(
|
||||
resolved.Blend is RetailSetSurfaceBlend.Opaque or RetailSetSurfaceBlend.Clip,
|
||||
selected.Depth.Write);
|
||||
Assert.Equal(WorldDepthContract.WorldCompare, selected.Depth.Compare);
|
||||
Assert.Equal(0, fixture.Queue.PendingCount);
|
||||
GpuRecordedStorageBind batchBind = fixture.Device.Calls
|
||||
.OfType<GpuRecordedStorageBind>()
|
||||
|
|
@ -120,10 +160,77 @@ public sealed class EnvCellAlphaDrawSourceTests
|
|||
ReadOnlySpan<ModernBatchData> gpuBatches = MemoryMarshal.Cast<byte, ModernBatchData>(
|
||||
fixture.Device.RingBytes.Slice((int)batchBind.OffsetBytes, (int)batchBind.SizeBytes));
|
||||
Assert.Equal(0.25f, Assert.Single(gpuBatches.ToArray()).SurfaceOpacity);
|
||||
Vector4 source = RetailDetailTextureContract.Combine(
|
||||
MaterialBase, MaterialDiffuse, MaterialDetail, 0.75f, 0.4f);
|
||||
AssertVector(new Vector4(0.3534682f, 0.2330118f, 0.5438054f, 0.11532f), source);
|
||||
RetailDetailTextureContract.FramebufferFamily family = expectedBlend switch
|
||||
{
|
||||
GpuBlendMode.None => RetailDetailTextureContract.FramebufferFamily.Opaque,
|
||||
GpuBlendMode.StraightAlpha => RetailDetailTextureContract.FramebufferFamily.Alpha,
|
||||
GpuBlendMode.Additive => RetailDetailTextureContract.FramebufferFamily.AlphaAdditive,
|
||||
GpuBlendMode.RawAdditive => RetailDetailTextureContract.FramebufferFamily.Additive,
|
||||
GpuBlendMode.InverseAlpha => RetailDetailTextureContract.FramebufferFamily.InverseAlpha,
|
||||
GpuBlendMode.InverseAdditive => RetailDetailTextureContract.FramebufferFamily.InverseAlphaAdditive,
|
||||
GpuBlendMode.PremultipliedAlpha => RetailDetailTextureContract.FramebufferFamily.Clip,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(expectedBlend)),
|
||||
};
|
||||
AssertVector(
|
||||
IndependentComposite(source, MaterialDestination, family),
|
||||
RetailDetailTextureContract.Composite(source, MaterialDestination, family));
|
||||
if (resolved.AlphaTestEnabled)
|
||||
{
|
||||
Assert.False(RetailDetailTextureContract.SurvivesClip(
|
||||
MathF.BitDecrement(expectedReference), expectedReference));
|
||||
Assert.True(RetailDetailTextureContract.SurvivesClip(expectedReference, expectedReference));
|
||||
Assert.True(RetailDetailTextureContract.SurvivesClip(
|
||||
MathF.BitIncrement(expectedReference), expectedReference));
|
||||
}
|
||||
fixture.Queue.EndFrame();
|
||||
Assert.Single(fixture.Device.Calls.OfType<GpuRecordedMultiDrawIndirect>());
|
||||
}
|
||||
|
||||
public static TheoryData<SurfaceType, bool, string, float> DetailOffRows() => new()
|
||||
{
|
||||
{ SurfaceType.Base1Image, false, "envcell-opaque", 0f },
|
||||
{ SurfaceType.Alpha, false, "envcell-alpha", 0f },
|
||||
{ SurfaceType.Alpha | SurfaceType.Additive, false, "envcell-additive", 0f },
|
||||
{ SurfaceType.Additive, false, "envcell-additive", 0f },
|
||||
{ SurfaceType.InvAlpha, false, "envcell-alpha", 0f },
|
||||
{ SurfaceType.InvAlpha | SurfaceType.Additive, false, "envcell-additive", 0f },
|
||||
{ SurfaceType.Base1ClipMap, false, "envcell-clip", 200f / 255f },
|
||||
{ SurfaceType.Base1ClipMap, true, "envcell-clip", 100f / 255f },
|
||||
{ SurfaceType.Alpha | SurfaceType.Base1ClipMap, true, "envcell-alpha", 0f },
|
||||
{ SurfaceType.InvAlpha | SurfaceType.Base1ClipMap, false, "envcell-alpha", 0f },
|
||||
{ SurfaceType.Translucent, false, "envcell-alpha", 0f },
|
||||
{ SurfaceType.Translucent | SurfaceType.Additive, false, "envcell-additive", 0f },
|
||||
{ SurfaceType.Translucent | SurfaceType.InvAlpha, false, "envcell-alpha", 0f },
|
||||
{ SurfaceType.Translucent | SurfaceType.Base1ClipMap | SurfaceType.Additive, true, "envcell-additive", 0f },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(DetailOffRows))]
|
||||
public void DetailOff_EveryRawStateRetainsThePreFixLogicalPath(
|
||||
SurfaceType surfaceType,
|
||||
bool paletted,
|
||||
string expectedPipeline,
|
||||
float expectedReference)
|
||||
{
|
||||
using var fixture = new ProductionEnvCellFixture(
|
||||
detailSurfaceActive: false,
|
||||
new BatchSpec(surfaceType, paletted, PositiveStippling: false));
|
||||
fixture.Queue.BeginFrame();
|
||||
fixture.Leaf.DrawCellShell(ProductionEnvCellFixture.CellId);
|
||||
fixture.Queue.EndFrame();
|
||||
|
||||
Assert.Single(fixture.Device.Calls.OfType<GpuRecordedMultiDrawIndirect>());
|
||||
Assert.Equal([expectedPipeline], DrawPipelineNames(fixture.Device));
|
||||
GpuPushConstants constants = Assert.Single(DrawPushConstants(fixture.Device));
|
||||
Assert.Equal(0u, constants.TextureIndexA);
|
||||
Assert.Equal(0f, constants.ParamA);
|
||||
Assert.Equal(expectedReference, constants.ParamB);
|
||||
Assert.Equal(0, constants.RenderPass & RetailDetailTextureContract.NoFogRenderPassFlag);
|
||||
}
|
||||
|
||||
/// <summary>Two cell tokens from the SAME <see cref="RetailPViewPassExecutor.EnvCellAlphaDrawSource"/>,
|
||||
/// with an unrelated source's entry appended between them, must still
|
||||
/// produce TWO separate single-cell draw calls around the interposed
|
||||
|
|
@ -258,9 +365,10 @@ public sealed class EnvCellAlphaDrawSourceTests
|
|||
foreach (string shader in new[] { modern, atmospheric })
|
||||
{
|
||||
Assert.Contains(
|
||||
"if (detailActive ? alpha < alphaCutoff : color.a < alphaCutoff)",
|
||||
"? isRetailClipReference(uParamB) && alpha < uParamB",
|
||||
shader,
|
||||
StringComparison.Ordinal);
|
||||
Assert.Contains("(uRenderPass & 0x200) == 0", shader, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("alpha <= alphaCutoff", shader, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("color.a <= alphaCutoff", shader, StringComparison.Ordinal);
|
||||
Assert.Contains("isRetailClipReference(uParamB) ? uParamB : 0.05", shader, StringComparison.Ordinal);
|
||||
|
|
@ -409,6 +517,38 @@ public sealed class EnvCellAlphaDrawSourceTests
|
|||
return [.. constants];
|
||||
}
|
||||
|
||||
private static void AssertVector(Vector4 expected, Vector4 actual)
|
||||
{
|
||||
Assert.Equal(expected.X, actual.X, 6);
|
||||
Assert.Equal(expected.Y, actual.Y, 6);
|
||||
Assert.Equal(expected.Z, actual.Z, 6);
|
||||
Assert.Equal(expected.W, actual.W, 6);
|
||||
}
|
||||
|
||||
private static Vector4 IndependentComposite(
|
||||
Vector4 source,
|
||||
Vector4 destination,
|
||||
RetailDetailTextureContract.FramebufferFamily family)
|
||||
{
|
||||
float x = source.W;
|
||||
return family switch
|
||||
{
|
||||
RetailDetailTextureContract.FramebufferFamily.Opaque => source,
|
||||
RetailDetailTextureContract.FramebufferFamily.Alpha =>
|
||||
source * x + destination * (1f - x),
|
||||
RetailDetailTextureContract.FramebufferFamily.AlphaAdditive =>
|
||||
source * x + destination,
|
||||
RetailDetailTextureContract.FramebufferFamily.Additive => source + destination,
|
||||
RetailDetailTextureContract.FramebufferFamily.InverseAlpha =>
|
||||
source * (1f - x) + destination * x,
|
||||
RetailDetailTextureContract.FramebufferFamily.InverseAlphaAdditive =>
|
||||
source * (1f - x) + destination,
|
||||
RetailDetailTextureContract.FramebufferFamily.Clip =>
|
||||
source + destination * new Vector4(1f - x),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(family)),
|
||||
};
|
||||
}
|
||||
|
||||
private static string RepositoryRoot()
|
||||
{
|
||||
DirectoryInfo? cursor = new(AppContext.BaseDirectory);
|
||||
|
|
@ -419,46 +559,32 @@ public sealed class EnvCellAlphaDrawSourceTests
|
|||
}
|
||||
|
||||
private readonly record struct BatchSpec(
|
||||
byte Mask,
|
||||
bool IsTransparent,
|
||||
AcDream.Core.Meshing.TranslucencyKind Translucency,
|
||||
uint PaletteId)
|
||||
SurfaceType Type,
|
||||
bool Paletted,
|
||||
bool PositiveStippling)
|
||||
{
|
||||
internal static BatchSpec Opaque { get; } = new(
|
||||
0x00,
|
||||
IsTransparent: false,
|
||||
AcDream.Core.Meshing.TranslucencyKind.Opaque,
|
||||
PaletteId: 0);
|
||||
SurfaceType.Base1Image, Paletted: false, PositiveStippling: false);
|
||||
|
||||
internal static BatchSpec ClipDds { get; } = new(
|
||||
RetailAlphaMeshRouter.MaskClipMap,
|
||||
IsTransparent: true,
|
||||
AcDream.Core.Meshing.TranslucencyKind.ClipMap,
|
||||
PaletteId: 0);
|
||||
SurfaceType.Base1Image | SurfaceType.Base1ClipMap,
|
||||
Paletted: false, PositiveStippling: false);
|
||||
|
||||
internal static BatchSpec ClipPaletted { get; } = new(
|
||||
RetailAlphaMeshRouter.MaskClipMap,
|
||||
IsTransparent: true,
|
||||
AcDream.Core.Meshing.TranslucencyKind.ClipMap,
|
||||
PaletteId: 0x04000001u);
|
||||
SurfaceType.Base1Image | SurfaceType.Base1ClipMap,
|
||||
Paletted: true, PositiveStippling: false);
|
||||
|
||||
internal static BatchSpec ClipPositiveStippleDds { get; } = new(
|
||||
RetailAlphaMeshRouter.MaskClipMap | RetailAlphaMeshRouter.MaskPositiveStipple,
|
||||
IsTransparent: true,
|
||||
AcDream.Core.Meshing.TranslucencyKind.ClipMap,
|
||||
PaletteId: 0);
|
||||
SurfaceType.Base1Image | SurfaceType.Base1ClipMap,
|
||||
Paletted: false, PositiveStippling: true);
|
||||
|
||||
internal static BatchSpec Alpha { get; } = new(
|
||||
RetailAlphaMeshRouter.MaskAlphaFamily,
|
||||
IsTransparent: true,
|
||||
AcDream.Core.Meshing.TranslucencyKind.AlphaBlend,
|
||||
PaletteId: 0);
|
||||
SurfaceType.Base1Image | SurfaceType.Alpha,
|
||||
Paletted: false, PositiveStippling: false);
|
||||
|
||||
internal static BatchSpec Additive { get; } = new(
|
||||
RetailAlphaMeshRouter.MaskAlphaFamily,
|
||||
IsTransparent: true,
|
||||
AcDream.Core.Meshing.TranslucencyKind.Additive,
|
||||
PaletteId: 0);
|
||||
SurfaceType.Base1Image | SurfaceType.Additive,
|
||||
Paletted: false, PositiveStippling: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -512,27 +638,45 @@ public sealed class EnvCellAlphaDrawSourceTests
|
|||
for (int i = 0; i < specs.Length; i++)
|
||||
{
|
||||
BatchSpec spec = specs[i];
|
||||
TranslucencyKind translucency =
|
||||
TranslucencyKindExtensions.FromSurfaceType(spec.Type);
|
||||
bool alphaFamily = (spec.Type
|
||||
& (SurfaceType.Alpha | SurfaceType.InvAlpha | SurfaceType.Additive)) != 0;
|
||||
byte mask = RetailAlphaMeshRouter.ConstructSubsetMask(
|
||||
alphaFamily,
|
||||
(spec.Type & SurfaceType.Base1ClipMap) != 0,
|
||||
(spec.Type & SurfaceType.Translucent) != 0,
|
||||
spec.PositiveStippling);
|
||||
uint paletteId = spec.Paletted ? 0x04000001u : 0u;
|
||||
batches.Add(new TextureBatchData
|
||||
{
|
||||
Key = new TextureKey
|
||||
{
|
||||
SurfaceId = 0x08000BFFu + (uint)i,
|
||||
PaletteId = spec.PaletteId,
|
||||
PaletteId = paletteId,
|
||||
},
|
||||
TextureData = new byte[8 * 8 * 4],
|
||||
Indices = [0, 1, 2],
|
||||
IsTransparent = spec.IsTransparent,
|
||||
IsAdditive = spec.Translucency == AcDream.Core.Meshing.TranslucencyKind.Additive,
|
||||
Translucency = spec.Translucency,
|
||||
IsTransparent = translucency != TranslucencyKind.Opaque,
|
||||
IsAdditive = (spec.Type & SurfaceType.Additive) != 0,
|
||||
Translucency = translucency,
|
||||
MaterialState = RetailSetSurfaceMaterialState.Resolve(
|
||||
spec.Type,
|
||||
texturePresent: true,
|
||||
textureHasPalette: spec.Paletted),
|
||||
SurfaceOpacity = 0.25f,
|
||||
RetailSurfaceMask = spec.Mask,
|
||||
RetailSurfaceMask = mask,
|
||||
CullMode = CullMode.Clockwise,
|
||||
IsCellShell = true,
|
||||
SourceSurfaceIndex = i,
|
||||
});
|
||||
}
|
||||
mesh.TextureBatches[(8, 8, TextureFormat.RGBA8)] = batches;
|
||||
Assert.NotNull(_meshManager.UploadMeshData(mesh));
|
||||
ObjectRenderData uploaded = Assert.IsType<ObjectRenderData>(
|
||||
_meshManager.UploadMeshData(mesh));
|
||||
Assert.Equal(
|
||||
batches.Select(static batch => batch.MaterialState),
|
||||
uploaded.Batches.Select(static batch => batch.MaterialState));
|
||||
|
||||
Renderer = new EnvCellRenderer(
|
||||
Device,
|
||||
|
|
|
|||
|
|
@ -80,12 +80,14 @@ public sealed class GpuContractTests
|
|||
// pure Base1ClipMap with override=false (SetSurface @0x0059c72a), while
|
||||
// detail is now computed inside those same pipelines. Collapsing any of
|
||||
// these modes changes the corresponding subset's composition.
|
||||
Assert.Equal(5, Enum.GetValues<GpuBlendMode>().Length);
|
||||
Assert.Equal(7, Enum.GetValues<GpuBlendMode>().Length);
|
||||
Assert.Contains(GpuBlendMode.None, Enum.GetValues<GpuBlendMode>());
|
||||
Assert.Contains(GpuBlendMode.StraightAlpha, Enum.GetValues<GpuBlendMode>());
|
||||
Assert.Contains(GpuBlendMode.PremultipliedAlpha, Enum.GetValues<GpuBlendMode>());
|
||||
Assert.Contains(GpuBlendMode.Additive, Enum.GetValues<GpuBlendMode>());
|
||||
Assert.Contains(GpuBlendMode.RawAdditive, Enum.GetValues<GpuBlendMode>());
|
||||
Assert.Contains(GpuBlendMode.InverseAlpha, Enum.GetValues<GpuBlendMode>());
|
||||
Assert.Contains(GpuBlendMode.InverseAdditive, Enum.GetValues<GpuBlendMode>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ public sealed class VulkanShaderManifestTests
|
|||
// single-pass building detail material now runs inside the base
|
||||
// fragment. Final alpha is authoredOpacity*liveFade*detail.a^2;
|
||||
// CLIP tests that value while detail-off retains base-alpha logic.
|
||||
["mesh_modern.frag.spv"] = "576af229b96a2a5e9b3b1ff16ab686d96578c2d9d1762b0e225df31bfd7aa86f",
|
||||
// Fix round 1 additionally carries the exact SetSurface alpha-test
|
||||
// reference and raw-additive fog suppression into this same pass.
|
||||
["mesh_modern.frag.spv"] = "bd47fe8a33e0f1d025fe48fd95b034ba6fe591a6d20e88e636c86238d8773db1",
|
||||
// S5-c4 replaces the padding word at batch offset 4 with authored
|
||||
// surface opacity and forwards it plus exact detail category/flags
|
||||
// flat to the fragment shader. The 16-byte ABI is unchanged.
|
||||
|
|
|
|||
|
|
@ -143,6 +143,12 @@ public sealed class VulkanViewportMappingTests
|
|||
Assert.Equal(
|
||||
(BlendFactor.SrcAlpha, BlendFactor.One),
|
||||
VulkanViewportMapping.BlendFactorsOf(GpuBlendMode.Additive));
|
||||
Assert.Equal(
|
||||
(BlendFactor.One, BlendFactor.One),
|
||||
VulkanViewportMapping.BlendFactorsOf(GpuBlendMode.RawAdditive));
|
||||
Assert.Equal(
|
||||
(BlendFactor.OneMinusSrcAlpha, BlendFactor.One),
|
||||
VulkanViewportMapping.BlendFactorsOf(GpuBlendMode.InverseAdditive));
|
||||
// Retail's third mode, found at slice V4c. Mapping it onto straight
|
||||
// alpha would have silently changed how every inverse-alpha surface
|
||||
// composites.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,45 @@ public sealed class RetailDetailTextureContractTests
|
|||
private static readonly Vector4 Detail = new(0.91f, 0.23f, 0.49f, 0.62f);
|
||||
private static readonly Vector4 Destination = new(0.17f, 0.37f, 0.71f, 0.29f);
|
||||
|
||||
public static TheoryData<SurfaceType, bool, RetailSetSurfaceBlend,
|
||||
RetailSetSurfaceAlphaTest, bool> ResolvedRows() => new()
|
||||
{
|
||||
{ SurfaceType.Base1Image, false, RetailSetSurfaceBlend.Opaque, RetailSetSurfaceAlphaTest.Disabled, true },
|
||||
{ SurfaceType.Alpha, false, RetailSetSurfaceBlend.StraightAlpha, RetailSetSurfaceAlphaTest.Disabled, true },
|
||||
{ SurfaceType.Alpha | SurfaceType.Additive, false, RetailSetSurfaceBlend.AlphaAdditive, RetailSetSurfaceAlphaTest.Disabled, false },
|
||||
{ SurfaceType.Additive, false, RetailSetSurfaceBlend.Additive, RetailSetSurfaceAlphaTest.Disabled, false },
|
||||
{ SurfaceType.InvAlpha, false, RetailSetSurfaceBlend.InverseAlpha, RetailSetSurfaceAlphaTest.Disabled, true },
|
||||
{ SurfaceType.InvAlpha | SurfaceType.Additive, false, RetailSetSurfaceBlend.InverseAdditive, RetailSetSurfaceAlphaTest.Disabled, false },
|
||||
{ SurfaceType.Base1ClipMap, false, RetailSetSurfaceBlend.Clip, RetailSetSurfaceAlphaTest.Dds, true },
|
||||
{ SurfaceType.Base1ClipMap, true, RetailSetSurfaceBlend.Clip, RetailSetSurfaceAlphaTest.Paletted, true },
|
||||
{ SurfaceType.Alpha | SurfaceType.Base1ClipMap, true, RetailSetSurfaceBlend.StraightAlpha, RetailSetSurfaceAlphaTest.Paletted, true },
|
||||
{ SurfaceType.InvAlpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, false, RetailSetSurfaceBlend.InverseAdditive, RetailSetSurfaceAlphaTest.Dds, false },
|
||||
{ SurfaceType.Translucent, false, RetailSetSurfaceBlend.StraightAlpha, RetailSetSurfaceAlphaTest.Disabled, true },
|
||||
{ SurfaceType.Translucent | SurfaceType.Additive, false, RetailSetSurfaceBlend.Additive, RetailSetSurfaceAlphaTest.Disabled, false },
|
||||
{ SurfaceType.Translucent | SurfaceType.InvAlpha, false, RetailSetSurfaceBlend.InverseAlpha, RetailSetSurfaceAlphaTest.Disabled, true },
|
||||
{ SurfaceType.Translucent | SurfaceType.Base1ClipMap | SurfaceType.Additive, true, RetailSetSurfaceBlend.StraightAlpha, RetailSetSurfaceAlphaTest.Disabled, false },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ResolvedRows))]
|
||||
public void SetSurfaceResolver_PreservesEveryRawFamilyClipAndLateOverride(
|
||||
SurfaceType type,
|
||||
bool paletted,
|
||||
RetailSetSurfaceBlend expectedBlend,
|
||||
RetailSetSurfaceAlphaTest expectedAlphaTest,
|
||||
bool expectedFog)
|
||||
{
|
||||
RetailSetSurfaceMaterialState state = RetailSetSurfaceMaterialState.Resolve(
|
||||
type,
|
||||
texturePresent: true,
|
||||
textureHasPalette: paletted);
|
||||
|
||||
Assert.Equal(expectedBlend, state.Blend);
|
||||
Assert.Equal(expectedAlphaTest, state.AlphaTest);
|
||||
Assert.Equal(expectedFog, state.FogEnabled);
|
||||
Assert.Equal(state, RetailSetSurfaceMaterialState.FromPackedByte(state.ToPackedByte()));
|
||||
}
|
||||
|
||||
public static TheoryData<string, SurfaceType, bool, bool, TranslucencyKind, int, float> PolicyRows()
|
||||
{
|
||||
var rows = new TheoryData<string, SurfaceType, bool, bool, TranslucencyKind, int, float>();
|
||||
|
|
@ -65,27 +104,22 @@ public sealed class RetailDetailTextureContractTests
|
|||
Assert.Equal(["source-subset"], PhysicalDrawSequence(detailEnabled));
|
||||
|
||||
PipelineState state = ExpectedPipelineState(
|
||||
consumer, expectedKind, paletted, expectedReference);
|
||||
Assert.Equal(expectedReference, state.AlphaReference);
|
||||
Assert.Equal(
|
||||
expectedKind == TranslucencyKind.ClipMap
|
||||
? paletted ? 100f / 255f : 200f / 255f
|
||||
: 0.05f,
|
||||
state.AlphaReference);
|
||||
Assert.True(state.AlphaTest);
|
||||
Assert.Equal(expectedKind is TranslucencyKind.Opaque or TranslucencyKind.ClipMap,
|
||||
state.DepthWrite);
|
||||
Assert.Equal(expectedKind switch
|
||||
consumer, surfaceType, expectedKind, paletted, detailEnabled);
|
||||
RetailSetSurfaceMaterialState resolved = RetailSetSurfaceMaterialState.Resolve(
|
||||
surfaceType,
|
||||
texturePresent: true,
|
||||
textureHasPalette: paletted);
|
||||
if (detailEnabled)
|
||||
{
|
||||
TranslucencyKind.Opaque => GpuBlendMode.None,
|
||||
TranslucencyKind.AlphaBlend => GpuBlendMode.StraightAlpha,
|
||||
TranslucencyKind.Additive => GpuBlendMode.Additive,
|
||||
TranslucencyKind.InvAlpha => GpuBlendMode.InverseAlpha,
|
||||
TranslucencyKind.ClipMap when consumer == "EnvCell" =>
|
||||
GpuBlendMode.PremultipliedAlpha,
|
||||
TranslucencyKind.ClipMap => GpuBlendMode.None,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(expectedKind)),
|
||||
}, state.Blend);
|
||||
Assert.Equal(resolved.AlphaTestReference, state.AlphaReference);
|
||||
Assert.Equal(resolved.AlphaTestEnabled, state.AlphaTest);
|
||||
Assert.Equal(resolved.FogEnabled, state.FogEnabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(expectedReference, state.AlphaReference);
|
||||
Assert.True(state.AlphaTest);
|
||||
}
|
||||
Assert.Equal(detailEnabled, DetailIsArmed(detailEnabled));
|
||||
if (consumer == "Building" && expectedKind == TranslucencyKind.ClipMap)
|
||||
Assert.Equal("AP-240 immediate A2C", state.Placement);
|
||||
|
|
@ -263,11 +297,38 @@ public sealed class RetailDetailTextureContractTests
|
|||
|
||||
private static PipelineState ExpectedPipelineState(
|
||||
string consumer,
|
||||
SurfaceType surfaceType,
|
||||
TranslucencyKind kind,
|
||||
bool paletted,
|
||||
float reference)
|
||||
bool detailEnabled)
|
||||
{
|
||||
_ = paletted;
|
||||
RetailSetSurfaceMaterialState resolved = RetailSetSurfaceMaterialState.Resolve(
|
||||
surfaceType,
|
||||
texturePresent: true,
|
||||
textureHasPalette: paletted);
|
||||
if (detailEnabled)
|
||||
{
|
||||
GpuBlendMode exactBlend = resolved.Blend switch
|
||||
{
|
||||
RetailSetSurfaceBlend.Opaque => GpuBlendMode.None,
|
||||
RetailSetSurfaceBlend.StraightAlpha => GpuBlendMode.StraightAlpha,
|
||||
RetailSetSurfaceBlend.AlphaAdditive => GpuBlendMode.Additive,
|
||||
RetailSetSurfaceBlend.Additive => GpuBlendMode.RawAdditive,
|
||||
RetailSetSurfaceBlend.InverseAlpha => GpuBlendMode.InverseAlpha,
|
||||
RetailSetSurfaceBlend.InverseAdditive => GpuBlendMode.InverseAdditive,
|
||||
RetailSetSurfaceBlend.Clip => GpuBlendMode.PremultipliedAlpha,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
return new PipelineState(
|
||||
exactBlend,
|
||||
resolved.Blend is RetailSetSurfaceBlend.Opaque or RetailSetSurfaceBlend.Clip,
|
||||
resolved.AlphaTestEnabled,
|
||||
resolved.AlphaTestReference,
|
||||
resolved.FogEnabled,
|
||||
consumer == "Building" && kind == TranslucencyKind.ClipMap
|
||||
? "AP-240 immediate A2C"
|
||||
: "source FIFO position");
|
||||
}
|
||||
GpuBlendMode blend = kind switch
|
||||
{
|
||||
TranslucencyKind.Opaque => GpuBlendMode.None,
|
||||
|
|
@ -283,7 +344,10 @@ public sealed class RetailDetailTextureContractTests
|
|||
blend,
|
||||
kind is TranslucencyKind.Opaque or TranslucencyKind.ClipMap,
|
||||
AlphaTest: true,
|
||||
reference,
|
||||
kind == TranslucencyKind.ClipMap
|
||||
? paletted ? 100f / 255f : 200f / 255f
|
||||
: 0.05f,
|
||||
FogEnabled: true,
|
||||
consumer == "Building" && kind == TranslucencyKind.ClipMap
|
||||
? "AP-240 immediate A2C"
|
||||
: "source FIFO position");
|
||||
|
|
@ -310,6 +374,7 @@ public sealed class RetailDetailTextureContractTests
|
|||
bool DepthWrite,
|
||||
bool AlphaTest,
|
||||
float AlphaReference,
|
||||
bool FogEnabled,
|
||||
string Placement);
|
||||
|
||||
private static Vector4 IndependentComposite(
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ public sealed class OrderPreservingSubmitterTests
|
|||
WalkDrawStage stage = WalkDrawStage.Terrain,
|
||||
TranslucencyKind translucency = TranslucencyKind.Opaque,
|
||||
CullMode cullMode = CullMode.CounterClockwise,
|
||||
uint detailCategory = 0) =>
|
||||
uint detailCategory = 0,
|
||||
RetailSetSurfaceMaterialState? materialState = null) =>
|
||||
new(
|
||||
Key: new GroupKey(
|
||||
FirstIndex: (uint)index * 3,
|
||||
|
|
@ -41,6 +42,7 @@ public sealed class OrderPreservingSubmitterTests
|
|||
TextureSlot: new GpuTextureSlot((uint)index),
|
||||
TextureLayer: 0,
|
||||
Translucency: translucency,
|
||||
MaterialState: materialState ?? RetailSetSurfaceMaterialState.Opaque,
|
||||
FoliageFlags: 0,
|
||||
CullMode: cullMode),
|
||||
Transform: Matrix4x4.CreateTranslation(index, index * 2, index * 3),
|
||||
|
|
@ -284,8 +286,12 @@ public sealed class OrderPreservingSubmitterTests
|
|||
Assert.Equal([(0, 1), (1, 1)], DecodeDrawRanges(fx.Device));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrepareThenDraw_OrdinaryBuildingClipBuildingOrdinary_ArmsOnePassInPlace()
|
||||
[Theory]
|
||||
[InlineData(false, 200f / 255f)]
|
||||
[InlineData(true, 100f / 255f)]
|
||||
public void PrepareThenDraw_OrdinaryBuildingClipBuildingOrdinary_ArmsOnePassInPlace(
|
||||
bool paletted,
|
||||
float expectedReference)
|
||||
{
|
||||
using var fx = new DispatcherFixture(detailAvailable: true, detailEnabled: true);
|
||||
using DrawScope draw = fx.BeginDraw();
|
||||
|
|
@ -293,7 +299,14 @@ public sealed class OrderPreservingSubmitterTests
|
|||
OrderedDrawStream stream = StreamOf(
|
||||
MakeCommand(0),
|
||||
MakeCommand(1, detailCategory: 1),
|
||||
MakeCommand(2, translucency: TranslucencyKind.ClipMap, detailCategory: 1),
|
||||
MakeCommand(
|
||||
2,
|
||||
translucency: TranslucencyKind.ClipMap,
|
||||
detailCategory: 1,
|
||||
materialState: RetailSetSurfaceMaterialState.Resolve(
|
||||
SurfaceType.Base1ClipMap,
|
||||
texturePresent: true,
|
||||
textureHasPalette: paletted)),
|
||||
MakeCommand(3));
|
||||
|
||||
PrepareAndDrawWhole(fx.Dispatcher, draw, stream);
|
||||
|
|
@ -317,8 +330,90 @@ public sealed class OrderPreservingSubmitterTests
|
|||
.Select(call => call.PipelineName)
|
||||
.ToArray());
|
||||
Assert.Equal(4, fx.Device.Calls.OfType<GpuRecordedMultiDrawIndirect>().Count());
|
||||
Assert.Equal(expectedReference, runs[2].Constants.ParamB);
|
||||
GpuPipelineDescription a2c = fx.Device.CreatedPipelines
|
||||
.Single(pipeline => pipeline.Description.Name == "wb-mesh-opaque-a2c-1x")
|
||||
.Description;
|
||||
Assert.Equal(GpuBlendMode.None, a2c.Blend);
|
||||
Assert.True(a2c.AlphaToCoverage);
|
||||
Assert.True(a2c.Depth.Test);
|
||||
Assert.True(a2c.Depth.Write);
|
||||
Assert.Equal(WorldDepthContract.WorldCompare, a2c.Depth.Compare);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrepareThenDraw_PureClipBuildingDetailOffRetainsAp240A2cAndNeutralDetailState()
|
||||
{
|
||||
using var fx = new DispatcherFixture(detailAvailable: true, detailEnabled: false);
|
||||
using DrawScope draw = fx.BeginDraw();
|
||||
OrderedDrawStream stream = StreamOf(
|
||||
MakeCommand(0),
|
||||
MakeCommand(
|
||||
1,
|
||||
translucency: TranslucencyKind.ClipMap,
|
||||
detailCategory: 1,
|
||||
materialState: RetailSetSurfaceMaterialState.Resolve(
|
||||
SurfaceType.Base1ClipMap,
|
||||
texturePresent: true,
|
||||
textureHasPalette: true)),
|
||||
MakeCommand(2));
|
||||
|
||||
PrepareAndDrawWhole(fx.Dispatcher, draw, stream);
|
||||
|
||||
List<(GpuPushConstants Constants, int Start, int Count)> runs = DecodeRuns(fx.Device);
|
||||
Assert.Equal([(0, 1), (1, 1), (2, 1)],
|
||||
runs.Select(run => (run.Start, run.Count)).ToList());
|
||||
Assert.Equal(0u, runs[1].Constants.TextureIndexA);
|
||||
Assert.Equal(0f, runs[1].Constants.ParamA);
|
||||
Assert.Equal(0f, runs[1].Constants.ParamB);
|
||||
Assert.Equal(0, runs[1].Constants.RenderPass & RetailDetailTextureContract.NoFogRenderPassFlag);
|
||||
Assert.Contains(fx.Device.Calls.OfType<GpuRecordedPipelineBind>(),
|
||||
call => call.PipelineName == "wb-mesh-opaque-a2c-1x");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AtmosphericReceiver_AdjacentOrdinaryDetailOrdinaryCommandsDoNotLeakExactState()
|
||||
{
|
||||
using var fx = new DispatcherFixture(
|
||||
detailAvailable: true,
|
||||
detailEnabled: true,
|
||||
atmospheric: true);
|
||||
using DrawScope draw = fx.BeginDraw();
|
||||
OrderedDrawStream stream = StreamOf(
|
||||
MakeCommand(0),
|
||||
MakeCommand(
|
||||
1,
|
||||
translucency: TranslucencyKind.Additive,
|
||||
detailCategory: 1,
|
||||
materialState: RetailSetSurfaceMaterialState.Resolve(
|
||||
SurfaceType.Additive,
|
||||
texturePresent: true,
|
||||
textureHasPalette: false)),
|
||||
MakeCommand(2));
|
||||
|
||||
PrepareAndDrawWhole(fx.Dispatcher, draw, stream);
|
||||
|
||||
List<(GpuPushConstants Constants, int Start, int Count)> runs = DecodeRuns(fx.Device);
|
||||
Assert.Equal([(0, 1), (1, 1), (2, 1)],
|
||||
runs.Select(run => (run.Start, run.Count)).ToList());
|
||||
Assert.Equal((0u, 0f, 0f, 0), DetailState(runs[0].Constants));
|
||||
Assert.Equal((77u, 3.5f, 0f, RetailDetailTextureContract.NoFogRenderPassFlag),
|
||||
DetailState(runs[1].Constants));
|
||||
Assert.Equal((0u, 0f, 0f, 0), DetailState(runs[2].Constants));
|
||||
Assert.Contains(fx.Device.Calls.OfType<GpuRecordedPipelineBind>(),
|
||||
call => call.PipelineName == "wb-mesh-atmospheric-raw-additive-1x");
|
||||
GpuPipelineDescription selected = fx.Device.CreatedPipelines
|
||||
.Single(pipeline => pipeline.Description.Name == "wb-mesh-atmospheric-raw-additive-1x")
|
||||
.Description;
|
||||
Assert.Equal("mesh_atmospheric", selected.Shaders.Name);
|
||||
Assert.Equal(GpuBlendMode.RawAdditive, selected.Blend);
|
||||
}
|
||||
|
||||
private static (uint Slot, float Tiling, float Reference, int NoFog) DetailState(
|
||||
GpuPushConstants constants) =>
|
||||
(constants.TextureIndexA, constants.ParamA, constants.ParamB,
|
||||
constants.RenderPass & RetailDetailTextureContract.NoFogRenderPassFlag);
|
||||
|
||||
[Fact]
|
||||
public void PrepareThenDraw_OpaqueRunUsesRenderPassZeroAndAlphaBlendRunUsesRenderPassOne()
|
||||
{
|
||||
|
|
@ -609,7 +704,10 @@ public sealed class OrderPreservingSubmitterTests
|
|||
private readonly WbMeshAdapter _meshAdapter;
|
||||
private readonly TextureCache _textures;
|
||||
|
||||
public DispatcherFixture(bool detailAvailable = false, bool detailEnabled = false)
|
||||
public DispatcherFixture(
|
||||
bool detailAvailable = false,
|
||||
bool detailEnabled = false,
|
||||
bool atmospheric = false)
|
||||
{
|
||||
Device = new RecordingGpuDevice();
|
||||
FrameLifetime = new GpuDeviceFrameLifetime(Device);
|
||||
|
|
@ -640,6 +738,15 @@ public sealed class OrderPreservingSubmitterTests
|
|||
new GpuTextureSlot(77), 3.5f, 0x05000001, 0x08000001, 16, 16)
|
||||
: default,
|
||||
buildingDetailEnabled: () => detailEnabled);
|
||||
if (atmospheric)
|
||||
{
|
||||
var source = new BindableAtmosphericSource();
|
||||
WbDrawDispatcher.DirectionalShadowReceiverPipelineState candidate =
|
||||
Assert.IsType<WbDrawDispatcher.DirectionalShadowReceiverPipelineState>(
|
||||
Dispatcher.PrepareDirectionalShadowReceiver(source, sampleCount: 1));
|
||||
Assert.Null(Dispatcher.SwapDirectionalShadowReceiver(candidate));
|
||||
}
|
||||
Atmospheric = atmospheric;
|
||||
}
|
||||
|
||||
public RecordingGpuDevice Device { get; }
|
||||
|
|
@ -650,6 +757,8 @@ public sealed class OrderPreservingSubmitterTests
|
|||
|
||||
public WbDrawDispatcher Dispatcher { get; }
|
||||
|
||||
public bool Atmospheric { get; }
|
||||
|
||||
/// <summary>Opens a frame and a backbuffer pass, publishes it on
|
||||
/// <see cref="Scope"/>, then clears the recorded calls so a test only
|
||||
/// sees what its own <c>SubmitOrderedStream</c> call produced.</summary>
|
||||
|
|
@ -659,7 +768,11 @@ public sealed class OrderPreservingSubmitterTests
|
|||
IGpuFrame frame = FrameLifetime.CurrentFrame!;
|
||||
IGpuPassEncoder pass = frame.BeginPass(
|
||||
GpuPassDescription.BackbufferClear(
|
||||
"fw2-ordered-stream-test", Vector4.Zero, sampleCount: 1));
|
||||
Atmospheric
|
||||
? DirectionalShadowReceiverPolicy.AtmosphericWorldPassName
|
||||
: "fw2-ordered-stream-test",
|
||||
Vector4.Zero,
|
||||
sampleCount: 1));
|
||||
IDisposable publication = Scope.Publish(pass);
|
||||
Device.Clear();
|
||||
return new DrawScope(frame, pass, publication);
|
||||
|
|
@ -674,6 +787,30 @@ public sealed class OrderPreservingSubmitterTests
|
|||
}
|
||||
}
|
||||
|
||||
private sealed class BindableAtmosphericSource : IDirectionalShadowReceiverSource
|
||||
{
|
||||
public DirectionalShadowPipelineShaders PipelineShaders =>
|
||||
DirectionalShadowPipelineShaders.Local;
|
||||
|
||||
public bool TryGetCurrentFrameBinding(
|
||||
IGpuFrame frame,
|
||||
out DirectionalShadowFrameBinding binding)
|
||||
{
|
||||
GpuRingAllocation allocation = frame.AllocateRing(
|
||||
checked((int)DirectionalShadowUniforms.SizeInBytes),
|
||||
GpuRingUsage.Uniform);
|
||||
binding = new DirectionalShadowFrameBinding(
|
||||
frame.Serial,
|
||||
Enabled: false,
|
||||
allocation.Buffer,
|
||||
allocation.OffsetBytes,
|
||||
DirectionalShadowUniforms.SizeInBytes,
|
||||
GpuTextureSlot.Unassigned,
|
||||
CascadeCount: 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NullPreparedAssetSource : IPreparedAssetSource
|
||||
{
|
||||
public PreparedAssetSourceStats Stats => default;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public sealed class OrderedDrawStreamTests
|
|||
TextureSlot: new GpuTextureSlot((uint)index),
|
||||
TextureLayer: 0,
|
||||
Translucency: translucency,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
FoliageFlags: 0,
|
||||
CullMode: cullMode),
|
||||
Transform: Matrix4x4.CreateTranslation(index, index * 2, index * 3),
|
||||
|
|
|
|||
|
|
@ -105,11 +105,13 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
int indexCount,
|
||||
uint textureSlotIndex,
|
||||
uint textureLayer = 0,
|
||||
CullMode cullMode = CullMode.CounterClockwise) =>
|
||||
CullMode cullMode = CullMode.CounterClockwise,
|
||||
RetailSetSurfaceMaterialState? materialState = null) =>
|
||||
new()
|
||||
{
|
||||
Key = new TextureKey { SurfaceId = surfaceId, IsSolid = false },
|
||||
Translucency = translucency,
|
||||
MaterialState = materialState ?? RetailSetSurfaceMaterialState.Opaque,
|
||||
FirstIndex = firstIndex,
|
||||
BaseVertex = (uint)baseVertex,
|
||||
IndexCount = indexCount,
|
||||
|
|
@ -316,14 +318,14 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
Assert.DoesNotContain(
|
||||
"one stable far-to-near stream keyed", architecture, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
Assert.Contains("160 active rows", register, StringComparison.Ordinal);
|
||||
Assert.Contains("active total to **161**", register, StringComparison.Ordinal);
|
||||
int apSectionStart = register.IndexOf(
|
||||
"## 3. Documented approximation (AP)", StringComparison.Ordinal);
|
||||
int apSectionEnd = register.IndexOf(
|
||||
"## 4. Temporary stopgap (TS)", StringComparison.Ordinal);
|
||||
Assert.True(apSectionStart >= 0, "AP section heading must exist.");
|
||||
Assert.True(apSectionEnd > apSectionStart, "TS heading must follow the AP section.");
|
||||
foreach (string id in new[] { "AP-241", "AP-242", "AP-243" })
|
||||
foreach (string id in new[] { "AP-241", "AP-242", "AP-243", "AP-244" })
|
||||
{
|
||||
int rowIndex = register.IndexOf($"| {id} |", StringComparison.Ordinal);
|
||||
Assert.True(
|
||||
|
|
@ -337,8 +339,10 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
line.StartsWith("| AP-242 |", StringComparison.Ordinal));
|
||||
Assert.Single(File.ReadLines(registerPath), static line =>
|
||||
line.StartsWith("| AP-243 |", StringComparison.Ordinal));
|
||||
Assert.Single(File.ReadLines(registerPath), static line =>
|
||||
line.StartsWith("| AP-244 |", StringComparison.Ordinal));
|
||||
Assert.Equal(
|
||||
160,
|
||||
161,
|
||||
File.ReadLines(registerPath).Count(static line =>
|
||||
line.StartsWith("| AP-", StringComparison.Ordinal)));
|
||||
Assert.Contains("greater than 50 m", register, StringComparison.Ordinal);
|
||||
|
|
@ -417,7 +421,12 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
InjectRenderData(fx.Manager, opaqueGfxObj, MakeFlatMesh(
|
||||
MakeBatch(0x08000001u, TranslucencyKind.Opaque, firstIndex: 0, baseVertex: 0, indexCount: 3, textureSlotIndex: 1)));
|
||||
InjectRenderData(fx.Manager, alphaGfxObj, MakeFlatMesh(
|
||||
MakeBatch(0x08000002u, TranslucencyKind.AlphaBlend, firstIndex: 3, baseVertex: 4, indexCount: 6, textureSlotIndex: 2)));
|
||||
MakeBatch(0x08000002u, TranslucencyKind.AlphaBlend, firstIndex: 3, baseVertex: 4,
|
||||
indexCount: 6, textureSlotIndex: 2,
|
||||
materialState: RetailSetSurfaceMaterialState.Resolve(
|
||||
SurfaceType.Alpha | SurfaceType.Additive,
|
||||
texturePresent: true,
|
||||
textureHasPalette: false))));
|
||||
|
||||
var meshRefs = new[]
|
||||
{
|
||||
|
|
@ -448,6 +457,7 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
Assert.Equal(3u, translucent.Key.FirstIndex);
|
||||
Assert.Equal(6, translucent.Key.IndexCount);
|
||||
Assert.Equal(2u, translucent.Key.TextureSlot.Index);
|
||||
Assert.Equal(RetailSetSurfaceBlend.AlphaAdditive, translucent.Key.MaterialState.Blend);
|
||||
Assert.Equal(meshRefs[1].PartTransform * record.Transform.LocalToWorld, translucent.Transform);
|
||||
|
||||
Assert.Equal(2, selectionParts.Count);
|
||||
|
|
@ -1095,7 +1105,8 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
using var fx = new DispatcherFixture(withAlphaQueue: true);
|
||||
fx.AlphaQueue!.BeginFrame();
|
||||
|
||||
var key = new GroupKey(10, 2, 6, new GpuTextureSlot(3), 1, TranslucencyKind.AlphaBlend, FoliageFlags: 0);
|
||||
var key = new GroupKey(10, 2, 6, new GpuTextureSlot(3), 1, TranslucencyKind.AlphaBlend,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque, FoliageFlags: 0);
|
||||
Vector3 localSortCenter = new(1, 2, 3);
|
||||
Matrix4x4 model = Matrix4x4.CreateTranslation(4, 5, 6);
|
||||
var batch = new WbDrawDispatcher.WalkClassifiedBatch(
|
||||
|
|
@ -1221,13 +1232,35 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TranslucencyKind.AlphaBlend, "wb-mesh-alpha-1x")]
|
||||
[InlineData(TranslucencyKind.Additive, "wb-mesh-additive-1x")]
|
||||
[InlineData(TranslucencyKind.InvAlpha, "wb-mesh-inverse-1x")]
|
||||
public void ImmediateBuildingDetail_RetainsOriginalFramebufferFamily(
|
||||
TranslucencyKind kind,
|
||||
string expectedPipeline)
|
||||
[InlineData(SurfaceType.Alpha, false, "wb-mesh-alpha-1x", GpuBlendMode.StraightAlpha, 0f, true)]
|
||||
[InlineData(SurfaceType.Alpha | SurfaceType.Additive, false, "wb-mesh-additive-1x", GpuBlendMode.Additive, 0f, false)]
|
||||
[InlineData(SurfaceType.Additive, false, "wb-mesh-raw-additive-1x", GpuBlendMode.RawAdditive, 0f, false)]
|
||||
[InlineData(SurfaceType.InvAlpha, false, "wb-mesh-inverse-1x", GpuBlendMode.InverseAlpha, 0f, true)]
|
||||
[InlineData(SurfaceType.InvAlpha | SurfaceType.Additive, false, "wb-mesh-inverse-additive-1x", GpuBlendMode.InverseAdditive, 0f, false)]
|
||||
[InlineData(SurfaceType.Translucent, false, "wb-mesh-alpha-1x", GpuBlendMode.StraightAlpha, 0f, true)]
|
||||
[InlineData(SurfaceType.Translucent | SurfaceType.Additive, false, "wb-mesh-raw-additive-1x", GpuBlendMode.RawAdditive, 0f, false)]
|
||||
[InlineData(SurfaceType.Translucent | SurfaceType.InvAlpha, false, "wb-mesh-inverse-1x", GpuBlendMode.InverseAlpha, 0f, true)]
|
||||
[InlineData(SurfaceType.Alpha | SurfaceType.Base1ClipMap, false, "wb-mesh-alpha-1x", GpuBlendMode.StraightAlpha, 200f / 255f, true)]
|
||||
[InlineData(SurfaceType.Alpha | SurfaceType.Base1ClipMap, true, "wb-mesh-alpha-1x", GpuBlendMode.StraightAlpha, 100f / 255f, true)]
|
||||
[InlineData(SurfaceType.Alpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, false, "wb-mesh-additive-1x", GpuBlendMode.Additive, 200f / 255f, false)]
|
||||
[InlineData(SurfaceType.Alpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, true, "wb-mesh-additive-1x", GpuBlendMode.Additive, 100f / 255f, false)]
|
||||
[InlineData(SurfaceType.Additive | SurfaceType.Base1ClipMap, false, "wb-mesh-raw-additive-1x", GpuBlendMode.RawAdditive, 200f / 255f, false)]
|
||||
[InlineData(SurfaceType.Additive | SurfaceType.Base1ClipMap, true, "wb-mesh-raw-additive-1x", GpuBlendMode.RawAdditive, 100f / 255f, false)]
|
||||
[InlineData(SurfaceType.InvAlpha | SurfaceType.Base1ClipMap, false, "wb-mesh-inverse-1x", GpuBlendMode.InverseAlpha, 200f / 255f, true)]
|
||||
[InlineData(SurfaceType.InvAlpha | SurfaceType.Base1ClipMap, true, "wb-mesh-inverse-1x", GpuBlendMode.InverseAlpha, 100f / 255f, true)]
|
||||
[InlineData(SurfaceType.InvAlpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, false, "wb-mesh-inverse-additive-1x", GpuBlendMode.InverseAdditive, 200f / 255f, false)]
|
||||
[InlineData(SurfaceType.InvAlpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, true, "wb-mesh-inverse-additive-1x", GpuBlendMode.InverseAdditive, 100f / 255f, false)]
|
||||
[InlineData(SurfaceType.Translucent | SurfaceType.Base1ClipMap | SurfaceType.Additive,
|
||||
true, "wb-mesh-alpha-1x", GpuBlendMode.StraightAlpha, 0f, false)]
|
||||
public void ImmediateBuildingDetail_UsesExactResolvedSetSurfaceState(
|
||||
SurfaceType type,
|
||||
bool paletted,
|
||||
string expectedPipeline,
|
||||
object expectedBlendValue,
|
||||
float expectedReference,
|
||||
bool expectedFog)
|
||||
{
|
||||
var expectedBlend = (GpuBlendMode)expectedBlendValue;
|
||||
using var fx = new DispatcherFixture(
|
||||
withAlphaQueue: true,
|
||||
detailAvailable: true,
|
||||
|
|
@ -1235,7 +1268,8 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
using DrawScope draw = fx.BeginDraw(beginAlpha: true);
|
||||
WbDrawDispatcher.WalkClassifiedBatch building = AlphaWalkBatch(
|
||||
detailCategory: 1u,
|
||||
translucency: kind);
|
||||
surfaceType: type,
|
||||
paletted: paletted);
|
||||
|
||||
fx.Dispatcher.SubmitWalkAlphaInstance(in building, Matrix4x4.Identity);
|
||||
|
||||
|
|
@ -1243,11 +1277,121 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
Assert.Single(fx.Device.Calls.OfType<GpuRecordedMultiDrawIndirect>());
|
||||
Assert.Contains(fx.Device.Calls.OfType<GpuRecordedPipelineBind>(),
|
||||
call => call.PipelineName == expectedPipeline);
|
||||
GpuPushConstants armed = fx.Device.Calls
|
||||
.OfType<GpuRecordedPushConstants>()
|
||||
.Last(call => call.Constants.ParamA != 0f)
|
||||
.Constants;
|
||||
Assert.Equal(99u, armed.TextureIndexA);
|
||||
Assert.Equal(2f, armed.ParamA);
|
||||
Assert.Equal(expectedReference, armed.ParamB);
|
||||
Assert.Equal(!expectedFog,
|
||||
(armed.RenderPass & RetailDetailTextureContract.NoFogRenderPassFlag) != 0);
|
||||
RetailSetSurfaceMaterialState resolved = RetailSetSurfaceMaterialState.Resolve(
|
||||
type, texturePresent: true, textureHasPalette: paletted);
|
||||
GpuPipelineDescription selected = fx.Device.CreatedPipelines
|
||||
.Single(pipeline => pipeline.Description.Name == expectedPipeline)
|
||||
.Description;
|
||||
Assert.Equal(expectedBlend, selected.Blend);
|
||||
Assert.True(selected.Depth.Test);
|
||||
Assert.Equal(
|
||||
resolved.Blend is RetailSetSurfaceBlend.Opaque or RetailSetSurfaceBlend.Clip,
|
||||
selected.Depth.Write);
|
||||
Assert.Equal(WorldDepthContract.WorldCompare, selected.Depth.Compare);
|
||||
Vector4 source = RetailDetailTextureContract.Combine(
|
||||
new Vector4(0.31f, 0.57f, 0.83f, 0.19f),
|
||||
new Vector3(0.73f, 0.41f, 0.67f),
|
||||
new Vector4(0.91f, 0.23f, 0.49f, 0.62f),
|
||||
authoredOpacity: 0.75f,
|
||||
liveOpacity: 0.4f);
|
||||
Assert.Equal(0.3534682f, source.X, 6);
|
||||
Assert.Equal(0.2330118f, source.Y, 6);
|
||||
Assert.Equal(0.5438054f, source.Z, 6);
|
||||
Assert.Equal(0.11532f, source.W, 6);
|
||||
Vector4 destination = new(0.17f, 0.37f, 0.71f, 0.29f);
|
||||
float x = source.W;
|
||||
RetailDetailTextureContract.FramebufferFamily family = expectedBlend switch
|
||||
{
|
||||
GpuBlendMode.StraightAlpha => RetailDetailTextureContract.FramebufferFamily.Alpha,
|
||||
GpuBlendMode.Additive => RetailDetailTextureContract.FramebufferFamily.AlphaAdditive,
|
||||
GpuBlendMode.RawAdditive => RetailDetailTextureContract.FramebufferFamily.Additive,
|
||||
GpuBlendMode.InverseAlpha => RetailDetailTextureContract.FramebufferFamily.InverseAlpha,
|
||||
GpuBlendMode.InverseAdditive => RetailDetailTextureContract.FramebufferFamily.InverseAlphaAdditive,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(expectedBlend)),
|
||||
};
|
||||
Vector4 expectedFramebuffer = family switch
|
||||
{
|
||||
RetailDetailTextureContract.FramebufferFamily.Alpha => source * x + destination * (1f - x),
|
||||
RetailDetailTextureContract.FramebufferFamily.AlphaAdditive => source * x + destination,
|
||||
RetailDetailTextureContract.FramebufferFamily.Additive => source + destination,
|
||||
RetailDetailTextureContract.FramebufferFamily.InverseAlpha => source * (1f - x) + destination * x,
|
||||
RetailDetailTextureContract.FramebufferFamily.InverseAlphaAdditive => source * (1f - x) + destination,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(family)),
|
||||
};
|
||||
Vector4 framebuffer = RetailDetailTextureContract.Composite(source, destination, family);
|
||||
Assert.Equal(expectedFramebuffer.X, framebuffer.X, 6);
|
||||
Assert.Equal(expectedFramebuffer.Y, framebuffer.Y, 6);
|
||||
Assert.Equal(expectedFramebuffer.Z, framebuffer.Z, 6);
|
||||
Assert.Equal(expectedFramebuffer.W, framebuffer.W, 6);
|
||||
if (resolved.AlphaTestEnabled)
|
||||
{
|
||||
Assert.False(RetailDetailTextureContract.SurvivesClip(
|
||||
MathF.BitDecrement(expectedReference), expectedReference));
|
||||
Assert.True(RetailDetailTextureContract.SurvivesClip(expectedReference, expectedReference));
|
||||
Assert.True(RetailDetailTextureContract.SurvivesClip(
|
||||
MathF.BitIncrement(expectedReference), expectedReference));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SurfaceType.Alpha, "wb-mesh-alpha-1x")]
|
||||
[InlineData(SurfaceType.Alpha | SurfaceType.Additive, "wb-mesh-additive-1x")]
|
||||
[InlineData(SurfaceType.Additive, "wb-mesh-additive-1x")]
|
||||
[InlineData(SurfaceType.InvAlpha, "wb-mesh-inverse-1x")]
|
||||
[InlineData(SurfaceType.InvAlpha | SurfaceType.Additive, "wb-mesh-additive-1x")]
|
||||
[InlineData(SurfaceType.Alpha | SurfaceType.Base1ClipMap, "wb-mesh-alpha-1x")]
|
||||
[InlineData(SurfaceType.Alpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, "wb-mesh-additive-1x")]
|
||||
[InlineData(SurfaceType.Additive | SurfaceType.Base1ClipMap, "wb-mesh-additive-1x")]
|
||||
[InlineData(SurfaceType.InvAlpha | SurfaceType.Base1ClipMap, "wb-mesh-inverse-1x")]
|
||||
[InlineData(SurfaceType.InvAlpha | SurfaceType.Additive | SurfaceType.Base1ClipMap, "wb-mesh-additive-1x")]
|
||||
[InlineData(SurfaceType.Translucent, "wb-mesh-alpha-1x")]
|
||||
[InlineData(SurfaceType.Translucent | SurfaceType.Additive, "wb-mesh-additive-1x")]
|
||||
[InlineData(SurfaceType.Translucent | SurfaceType.InvAlpha, "wb-mesh-inverse-1x")]
|
||||
[InlineData(SurfaceType.Translucent | SurfaceType.Base1ClipMap | SurfaceType.Additive, "wb-mesh-alpha-1x")]
|
||||
public void BuildingDetailOff_EveryRawStateRetainsThePreFixLogicalPath(
|
||||
SurfaceType type,
|
||||
string expectedPipeline)
|
||||
{
|
||||
using var fx = new DispatcherFixture(
|
||||
withAlphaQueue: true,
|
||||
detailAvailable: true,
|
||||
detailEnabled: false);
|
||||
using DrawScope draw = fx.BeginDraw(beginAlpha: true);
|
||||
WbDrawDispatcher.WalkClassifiedBatch building = AlphaWalkBatch(
|
||||
detailCategory: 1u,
|
||||
surfaceType: type);
|
||||
|
||||
fx.Dispatcher.SubmitWalkAlphaInstance(in building, Matrix4x4.Identity);
|
||||
|
||||
Assert.Equal(1, fx.AlphaQueue!.PendingCount);
|
||||
Assert.Empty(fx.Device.Calls.OfType<GpuRecordedMultiDrawIndirect>());
|
||||
fx.AlphaQueue.EndFrame();
|
||||
Assert.Single(fx.Device.Calls.OfType<GpuRecordedMultiDrawIndirect>());
|
||||
Assert.Contains(fx.Device.Calls.OfType<GpuRecordedPipelineBind>(),
|
||||
call => call.PipelineName == expectedPipeline);
|
||||
GpuPushConstants constants = fx.Device.Calls
|
||||
.OfType<GpuRecordedPushConstants>()
|
||||
.Last()
|
||||
.Constants;
|
||||
Assert.Equal(0u, constants.TextureIndexA);
|
||||
Assert.Equal(0f, constants.ParamA);
|
||||
Assert.Equal(0f, constants.ParamB);
|
||||
Assert.Equal(0, constants.RenderPass & RetailDetailTextureContract.NoFogRenderPassFlag);
|
||||
}
|
||||
|
||||
private static WbDrawDispatcher.WalkClassifiedBatch AlphaWalkBatch(
|
||||
uint detailCategory,
|
||||
TranslucencyKind translucency = TranslucencyKind.AlphaBlend) =>
|
||||
SurfaceType surfaceType = SurfaceType.Alpha,
|
||||
bool paletted = false) =>
|
||||
new(
|
||||
new GroupKey(
|
||||
0,
|
||||
|
|
@ -1255,7 +1399,11 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
3,
|
||||
new GpuTextureSlot(1),
|
||||
0,
|
||||
translucency,
|
||||
TranslucencyKindExtensions.FromSurfaceType(surfaceType),
|
||||
RetailSetSurfaceMaterialState.Resolve(
|
||||
surfaceType,
|
||||
texturePresent: true,
|
||||
textureHasPalette: paletted),
|
||||
FoliageFlags: 0),
|
||||
Matrix4x4.Identity,
|
||||
ClipSlot: 0,
|
||||
|
|
@ -1277,6 +1425,10 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
TextureSlot = new GpuTextureSlot(1),
|
||||
TextureLayer = 0,
|
||||
Translucency = TranslucencyKind.AlphaBlend,
|
||||
MaterialState = RetailSetSurfaceMaterialState.Resolve(
|
||||
SurfaceType.Alpha,
|
||||
texturePresent: true,
|
||||
textureHasPalette: false),
|
||||
};
|
||||
group.Matrices.Add(Matrix4x4.Identity);
|
||||
group.SubmissionOrders.Add(0);
|
||||
|
|
@ -1320,7 +1472,8 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
using var fx = new DispatcherFixture(withAlphaQueue: true);
|
||||
fx.AlphaQueue!.BeginFrame();
|
||||
|
||||
var key = new GroupKey(0, 0, 3, new GpuTextureSlot(1), 0, TranslucencyKind.AlphaBlend, FoliageFlags: 0);
|
||||
var key = new GroupKey(0, 0, 3, new GpuTextureSlot(1), 0, TranslucencyKind.AlphaBlend,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque, FoliageFlags: 0);
|
||||
var batch = new WbDrawDispatcher.WalkClassifiedBatch(
|
||||
key, Matrix4x4.Identity, 0, WbDrawDispatcher.InstanceLightSet.Disabled, 0, 1f,
|
||||
Vector2.Zero, 0, IsOpaque: false, LocalSortCenter: new Vector3(0, 0, 10));
|
||||
|
|
@ -1354,7 +1507,8 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
|
||||
var stream = new OrderedDrawStream();
|
||||
stream.Append(new OrderedDrawCommand(
|
||||
new GroupKey(0, 0, 3, new GpuTextureSlot(1), 0, TranslucencyKind.Opaque, FoliageFlags: 0, CullMode: CullMode.Clockwise),
|
||||
new GroupKey(0, 0, 3, new GpuTextureSlot(1), 0, TranslucencyKind.Opaque,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque, FoliageFlags: 0, CullMode: CullMode.Clockwise),
|
||||
Matrix4x4.Identity, WalkDrawStage.Terrain, 0, 0,
|
||||
WbDrawDispatcher.InstanceLightSet.Disabled, 0, 1f, Vector2.Zero, 0));
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ using AcDream.App.Rendering.Gpu.Vk;
|
|||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.Tests.Rendering.Gpu;
|
||||
using AcDream.Content;
|
||||
using AcDream.Core.Meshing;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
using CullMode = DatReaderWriter.Enums.CullMode;
|
||||
|
|
@ -186,7 +187,8 @@ public class EnvCellRendererTests
|
|||
"_mdiDrawRanges", BindingFlags.NonPublic | BindingFlags.Instance)!;
|
||||
var ranges = (List<EnvCellRenderer.MdiDrawRange>)rangesField.GetValue(renderer)!;
|
||||
ranges.Clear();
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 0, firstCommand: 0, commandCount: 1);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 0, firstCommand: 0, commandCount: 1,
|
||||
RetailSetSurfaceMaterialState.Opaque);
|
||||
|
||||
var allInstances = new List<InstanceData>();
|
||||
for (int i = 0; i < instanceCount; i++)
|
||||
|
|
@ -239,7 +241,7 @@ public class EnvCellRendererTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void DetailUsesOnlyTheOriginalFourShellPipelines()
|
||||
public void DetailUsesOnlySetSurfaceShellPipelinesAndNoReplayPipeline()
|
||||
{
|
||||
using var device = new RecordingGpuDevice();
|
||||
using var meshManager = CreateMeshManager(device);
|
||||
|
|
@ -250,7 +252,7 @@ public class EnvCellRendererTests
|
|||
meshManager,
|
||||
new WbFrustum());
|
||||
|
||||
Assert.Equal(4, device.CreatedPipelines.Count);
|
||||
Assert.Equal(7, device.CreatedPipelines.Count);
|
||||
Assert.DoesNotContain(device.CreatedPipelines,
|
||||
pipeline => pipeline.Description.Name.Contains("detail", StringComparison.Ordinal));
|
||||
}
|
||||
|
|
@ -260,11 +262,14 @@ public class EnvCellRendererTests
|
|||
{
|
||||
var ranges = new List<EnvCellRenderer.MdiDrawRange>();
|
||||
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 0, commandCount: 3);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 3, commandCount: 4);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 0, commandCount: 3,
|
||||
RetailSetSurfaceMaterialState.Opaque);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 3, commandCount: 4,
|
||||
RetailSetSurfaceMaterialState.Opaque);
|
||||
|
||||
Assert.Equal(
|
||||
[new EnvCellRenderer.MdiDrawRange(GroupIndex: 2, FirstCommand: 0, CommandCount: 7)],
|
||||
[new EnvCellRenderer.MdiDrawRange(GroupIndex: 2, FirstCommand: 0, CommandCount: 7,
|
||||
RetailSetSurfaceMaterialState.Opaque)],
|
||||
ranges);
|
||||
}
|
||||
|
||||
|
|
@ -273,17 +278,21 @@ public class EnvCellRendererTests
|
|||
{
|
||||
var ranges = new List<EnvCellRenderer.MdiDrawRange>();
|
||||
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 0, commandCount: 3);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 6, firstCommand: 3, commandCount: 2);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 5, commandCount: 1);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 9, commandCount: 2);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 0, commandCount: 3,
|
||||
RetailSetSurfaceMaterialState.Opaque);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 6, firstCommand: 3, commandCount: 2,
|
||||
RetailSetSurfaceMaterialState.Opaque);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 5, commandCount: 1,
|
||||
RetailSetSurfaceMaterialState.Opaque);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 9, commandCount: 2,
|
||||
RetailSetSurfaceMaterialState.Opaque);
|
||||
|
||||
Assert.Equal(
|
||||
[
|
||||
new EnvCellRenderer.MdiDrawRange(2, 0, 3),
|
||||
new EnvCellRenderer.MdiDrawRange(6, 3, 2),
|
||||
new EnvCellRenderer.MdiDrawRange(2, 5, 1),
|
||||
new EnvCellRenderer.MdiDrawRange(2, 9, 2),
|
||||
new EnvCellRenderer.MdiDrawRange(2, 0, 3, RetailSetSurfaceMaterialState.Opaque),
|
||||
new EnvCellRenderer.MdiDrawRange(6, 3, 2, RetailSetSurfaceMaterialState.Opaque),
|
||||
new EnvCellRenderer.MdiDrawRange(2, 5, 1, RetailSetSurfaceMaterialState.Opaque),
|
||||
new EnvCellRenderer.MdiDrawRange(2, 9, 2, RetailSetSurfaceMaterialState.Opaque),
|
||||
],
|
||||
ranges);
|
||||
}
|
||||
|
|
@ -293,7 +302,8 @@ public class EnvCellRendererTests
|
|||
{
|
||||
var ranges = new List<EnvCellRenderer.MdiDrawRange>();
|
||||
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 0, commandCount: 0);
|
||||
EnvCellRenderer.AppendMdiDrawRange(ranges, groupIndex: 2, firstCommand: 0, commandCount: 0,
|
||||
RetailSetSurfaceMaterialState.Opaque);
|
||||
|
||||
Assert.Empty(ranges);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ public sealed class FoliageWindClassificationTests
|
|||
TextureSlot: new AcDream.App.Rendering.Gpu.GpuTextureSlot(42),
|
||||
TextureLayer: 0,
|
||||
Translucency: TranslucencyKind.ClipMap,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
CullMode: DatReaderWriter.Enums.CullMode.CounterClockwise,
|
||||
FoliageFlags: sceneryFlags);
|
||||
var landblockStaticKey = sceneryKey with { FoliageFlags = landblockStaticFlags };
|
||||
|
|
|
|||
|
|
@ -270,6 +270,7 @@ public class InstanceGroupClearTests
|
|||
TextureSlot: Slot(0xAA),
|
||||
TextureLayer: 0,
|
||||
Translucency: TranslucencyKind.Opaque,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
FoliageFlags: 0u);
|
||||
var cached = new CachedBatch(
|
||||
key,
|
||||
|
|
@ -312,6 +313,7 @@ public class InstanceGroupClearTests
|
|||
TextureSlot: Slot(0x77),
|
||||
TextureLayer: 0,
|
||||
Translucency: TranslucencyKind.ClipMap,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
FoliageFlags: 0x2u,
|
||||
CullMode: DatReaderWriter.Enums.CullMode.CounterClockwise);
|
||||
|
||||
|
|
@ -416,6 +418,7 @@ public class InstanceGroupClearTests
|
|||
TextureSlot: Slot(textureSlot),
|
||||
TextureLayer: 0,
|
||||
Translucency: TranslucencyKind.Opaque,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
FoliageFlags: 0u);
|
||||
|
||||
private static WbDrawDispatcher.InstanceGroup MakeGroup(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using AcDream.Content;
|
||||
using AcDream.Core.Meshing;
|
||||
using Chorizite.Core.Render.Enums;
|
||||
using DatReaderWriter;
|
||||
using DatReaderWriter.DBObjs;
|
||||
|
|
@ -255,6 +256,60 @@ public sealed class MeshExtractorSolidFaceExtractionTests
|
|||
BitConverter.SingleToInt32Bits(batch.SurfaceOpacity));
|
||||
}
|
||||
|
||||
public static TheoryData<SurfaceType, bool, RetailSetSurfaceBlend,
|
||||
RetailSetSurfaceAlphaTest, bool> SetSurfaceExtractionRows() => new()
|
||||
{
|
||||
{ SurfaceType.Base1Image, false, RetailSetSurfaceBlend.Opaque, RetailSetSurfaceAlphaTest.Disabled, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Alpha, false, RetailSetSurfaceBlend.StraightAlpha, RetailSetSurfaceAlphaTest.Disabled, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Alpha | SurfaceType.Additive, false, RetailSetSurfaceBlend.AlphaAdditive, RetailSetSurfaceAlphaTest.Disabled, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Additive, false, RetailSetSurfaceBlend.Additive, RetailSetSurfaceAlphaTest.Disabled, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.InvAlpha, false, RetailSetSurfaceBlend.InverseAlpha, RetailSetSurfaceAlphaTest.Disabled, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.InvAlpha | SurfaceType.Additive, false, RetailSetSurfaceBlend.InverseAdditive, RetailSetSurfaceAlphaTest.Disabled, false },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Base1ClipMap, false, RetailSetSurfaceBlend.Clip, RetailSetSurfaceAlphaTest.Dds, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Base1ClipMap, true, RetailSetSurfaceBlend.Clip, RetailSetSurfaceAlphaTest.Paletted, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Alpha | SurfaceType.Base1ClipMap, true, RetailSetSurfaceBlend.StraightAlpha, RetailSetSurfaceAlphaTest.Paletted, true },
|
||||
{ SurfaceType.Base1Image | SurfaceType.Translucent | SurfaceType.Base1ClipMap | SurfaceType.Additive, true, RetailSetSurfaceBlend.StraightAlpha, RetailSetSurfaceAlphaTest.Disabled, false },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(SetSurfaceExtractionRows))]
|
||||
public void OrdinaryAndCellExtraction_CarryIdenticalResolvedSetSurfaceState(
|
||||
SurfaceType type,
|
||||
bool paletted,
|
||||
RetailSetSurfaceBlend expectedBlend,
|
||||
RetailSetSurfaceAlphaTest expectedAlphaTest,
|
||||
bool expectedFog)
|
||||
{
|
||||
var dats = new FakeMeshExtractorDats();
|
||||
uint paletteId = paletted ? 0x04000001u : 0u;
|
||||
RegisterTexturedQuad(
|
||||
dats,
|
||||
type,
|
||||
PixelFormat.PFID_A8R8G8B8,
|
||||
new byte[4 * 4 * 4],
|
||||
paletteId: paletteId);
|
||||
var extractor = new MeshExtractor(dats, NullLogger.Instance, sideStagedSink: null);
|
||||
|
||||
ObjectMeshData ordinary = Assert.IsType<ObjectMeshData>(
|
||||
extractor.PrepareMeshData(GfxObjId, isSetup: false));
|
||||
ObjectMeshData cell = Assert.IsType<ObjectMeshData>(
|
||||
extractor.PrepareCellStructMeshData(
|
||||
id: 1,
|
||||
BuildQuadCellStruct(RetailCullMode.Landblock),
|
||||
surfaceOverrides: [2],
|
||||
Matrix4x4.Identity,
|
||||
CancellationToken.None));
|
||||
RetailSetSurfaceMaterialState ordinaryState =
|
||||
Assert.Single(Assert.Single(ordinary.TextureBatches).Value).MaterialState;
|
||||
RetailSetSurfaceMaterialState cellState =
|
||||
Assert.Single(Assert.Single(cell.TextureBatches).Value).MaterialState;
|
||||
|
||||
Assert.Equal(ordinaryState, cellState);
|
||||
Assert.Equal(expectedBlend, ordinaryState.Blend);
|
||||
Assert.Equal(expectedAlphaTest, ordinaryState.AlphaTest);
|
||||
Assert.Equal(expectedFog, ordinaryState.FogEnabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OH2/S1 chunk-2 (2026-09-02): these two winding tests used to register
|
||||
/// an UNTEXTURED (Base1Solid) surface. Under the exact contract, an
|
||||
|
|
@ -800,7 +855,8 @@ public sealed class MeshExtractorSolidFaceExtractionTests
|
|||
SurfaceType surfaceType,
|
||||
PixelFormat pixelFormat,
|
||||
byte[] sourceData,
|
||||
float translucency = 0.0f)
|
||||
float translucency = 0.0f,
|
||||
uint paletteId = 0)
|
||||
{
|
||||
dats.RegisterRootGfxObj(GfxObjId, BuildQuadGfxObj(TexturedSurfaceId, noPos: false));
|
||||
dats.Register(TexturedSurfaceId, new Surface
|
||||
|
|
@ -818,6 +874,7 @@ public sealed class MeshExtractorSolidFaceExtractionTests
|
|||
Width = 4,
|
||||
Height = 4,
|
||||
Format = pixelFormat,
|
||||
DefaultPaletteId = paletteId,
|
||||
SourceData = sourceData,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,8 @@ public static class ObjectMeshDataEquality {
|
|||
Assert.True(expected.CullMode == actual.CullMode, $"{path}.CullMode: expected {expected.CullMode}, got {actual.CullMode}");
|
||||
Assert.True(expected.Translucency == actual.Translucency,
|
||||
$"{path}.Translucency: expected {expected.Translucency}, got {actual.Translucency}");
|
||||
Assert.True(expected.MaterialState == actual.MaterialState,
|
||||
$"{path}.MaterialState: expected {expected.MaterialState}, got {actual.MaterialState}");
|
||||
Assert.True(expected.IsTransparent == actual.IsTransparent, $"{path}.IsTransparent: expected {expected.IsTransparent}, got {actual.IsTransparent}");
|
||||
Assert.True(expected.IsAdditive == actual.IsAdditive, $"{path}.IsAdditive: expected {expected.IsAdditive}, got {actual.IsAdditive}");
|
||||
Assert.True(expected.HasWrappingUVs == actual.HasWrappingUVs, $"{path}.HasWrappingUVs: expected {expected.HasWrappingUVs}, got {actual.HasWrappingUVs}");
|
||||
|
|
|
|||
|
|
@ -192,6 +192,10 @@ public class ObjectMeshDataSerializerTests {
|
|||
Indices = new List<ushort> { 0, 1, 2 },
|
||||
CullMode = CullMode.Clockwise,
|
||||
Translucency = TranslucencyKind.Opaque,
|
||||
MaterialState = new RetailSetSurfaceMaterialState(
|
||||
RetailSetSurfaceBlend.InverseAdditive,
|
||||
RetailSetSurfaceAlphaTest.Paletted,
|
||||
FogEnabled: false),
|
||||
IsTransparent = false,
|
||||
IsAdditive = false,
|
||||
HasWrappingUVs = false,
|
||||
|
|
@ -290,6 +294,37 @@ public class ObjectMeshDataSerializerTests {
|
|||
BitConverter.SingleToInt32Bits(read.SurfaceOpacity));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, true)]
|
||||
[InlineData(1, 0, true)]
|
||||
[InlineData(2, 1, true)]
|
||||
[InlineData(3, 2, false)]
|
||||
[InlineData(4, 0, true)]
|
||||
[InlineData(5, 1, false)]
|
||||
[InlineData(6, 2, true)]
|
||||
public void SetSurfaceState_RoundTripsEveryPackedFieldDeterministically(
|
||||
int blendValue,
|
||||
int alphaTestValue,
|
||||
bool fogEnabled) {
|
||||
ObjectMeshData data = WithCellShellSubset();
|
||||
TextureBatchData batch = Assert.Single(Assert.Single(data.TextureBatches).Value);
|
||||
batch.MaterialState = new RetailSetSurfaceMaterialState(
|
||||
(RetailSetSurfaceBlend)blendValue,
|
||||
(RetailSetSurfaceAlphaTest)alphaTestValue,
|
||||
fogEnabled);
|
||||
|
||||
using var first = new MemoryStream();
|
||||
ObjectMeshDataSerializer.Write(data, first);
|
||||
using var second = new MemoryStream();
|
||||
ObjectMeshDataSerializer.Write(data, second);
|
||||
|
||||
Assert.Equal(first.ToArray(), second.ToArray());
|
||||
TextureBatchData read = Assert.Single(Assert.Single(
|
||||
ObjectMeshDataSerializer.Read(first.ToArray()).TextureBatches).Value);
|
||||
Assert.Equal(batch.MaterialState, read.MaterialState);
|
||||
Assert.Equal(batch.MaterialState.ToPackedByte(), read.MaterialState.ToPackedByte());
|
||||
}
|
||||
|
||||
// ---- determinism -----------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -10,13 +10,14 @@ public class PakFormatTests {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// S5-c4: recipe 9 carries authored surface opacity in every prepared
|
||||
/// texture batch. It remains a payload recipe change, not pak framing.
|
||||
/// S5-c4 fix round 1: recipe 10 carries exact SetSurface state after
|
||||
/// recipe 10's resolved SetSurface state after recipe 9's authored opacity.
|
||||
/// It remains a payload recipe change.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FormatVersion_StaysAtTwo_AcrossTheS5C4RecipeBump() {
|
||||
Assert.Equal(2u, PakFormat.CurrentFormatVersion);
|
||||
Assert.Equal(9u, PakFormat.CurrentBakeToolVersion);
|
||||
Assert.Equal(10u, PakFormat.CurrentBakeToolVersion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ public sealed class PreparedAssetSourceTests : IDisposable
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// S5-c4: an older recipe-8 package must be rejected by a recipe-9
|
||||
/// S5-c4 fix round 1: an older recipe-9 package must be rejected by a recipe-10
|
||||
/// consumer through the catalog-identity check
|
||||
/// alone, BEFORE any TextureBatchData payload field is ever
|
||||
/// deserialized -- so an older pak can never be silently misread as
|
||||
|
|
@ -160,7 +160,7 @@ public sealed class PreparedAssetSourceTests : IDisposable
|
|||
fs.Position = 36;
|
||||
Span<byte> buf = stackalloc byte[4];
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(
|
||||
buf, 8u);
|
||||
buf, 9u);
|
||||
fs.Write(buf);
|
||||
}
|
||||
|
||||
|
|
@ -168,7 +168,7 @@ public sealed class PreparedAssetSourceTests : IDisposable
|
|||
() => new PakPreparedAssetSource(path, Identity));
|
||||
Assert.Contains("does not match the installed DAT set", error.Message);
|
||||
Assert.Contains("Re-bake", error.Message);
|
||||
Assert.Contains("bake tool 8 != 9", error.Message);
|
||||
Assert.Contains("bake tool 9 != 10", error.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -337,6 +337,7 @@ public class EntityClassificationCacheTests
|
|||
TextureSlot: new AcDream.App.Rendering.Gpu.GpuTextureSlot(texSlot),
|
||||
TextureLayer: 0,
|
||||
Translucency: TranslucencyKind.Opaque,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
FoliageFlags: 0u);
|
||||
return new CachedBatch(key, new AcDream.App.Rendering.Gpu.GpuTextureSlot(texSlot), Matrix4x4.Identity);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ public sealed class WbDrawDispatcherBucketingTests
|
|||
TextureSlot: new AcDream.App.Rendering.Gpu.GpuTextureSlot(texSlot),
|
||||
TextureLayer: 0,
|
||||
Translucency: TranslucencyKind.Opaque,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
FoliageFlags: 0u);
|
||||
return new CachedBatch(
|
||||
key,
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ public sealed class WbDrawDispatcherIndirectBuilderTests
|
|||
// Arrange — three groups: 2 opaque (12+1 instances) + 1 transparent (12 instances)
|
||||
var groups = new List<WbDrawDispatcher.IndirectGroupInput>
|
||||
{
|
||||
new(IndexCount: 100, FirstIndex: 0, BaseVertex: 0, InstanceCount: 12, FirstInstance: 0, TextureIndex: 0xAA, TextureLayer: 0, Translucency: TranslucencyKind.Opaque, SurfaceOpacity: 0.75f),
|
||||
new(IndexCount: 200, FirstIndex: 100, BaseVertex: 0, InstanceCount: 12, FirstInstance: 12, TextureIndex: 0xBB, TextureLayer: 0, Translucency: TranslucencyKind.AlphaBlend, SurfaceOpacity: 0.25f),
|
||||
new(IndexCount: 50, FirstIndex: 300, BaseVertex: 100, InstanceCount: 1, FirstInstance: 24, TextureIndex: 0xCC, TextureLayer: 0, Translucency: TranslucencyKind.Opaque, SurfaceOpacity: 0.5f),
|
||||
new(IndexCount: 100, FirstIndex: 0, BaseVertex: 0, InstanceCount: 12, FirstInstance: 0, TextureIndex: 0xAA, TextureLayer: 0, Translucency: TranslucencyKind.Opaque, MaterialState: RetailSetSurfaceMaterialState.Opaque, SurfaceOpacity: 0.75f),
|
||||
new(IndexCount: 200, FirstIndex: 100, BaseVertex: 0, InstanceCount: 12, FirstInstance: 12, TextureIndex: 0xBB, TextureLayer: 0, Translucency: TranslucencyKind.AlphaBlend, MaterialState: RetailSetSurfaceMaterialState.Opaque, SurfaceOpacity: 0.25f),
|
||||
new(IndexCount: 50, FirstIndex: 300, BaseVertex: 100, InstanceCount: 1, FirstInstance: 24, TextureIndex: 0xCC, TextureLayer: 0, Translucency: TranslucencyKind.Opaque, MaterialState: RetailSetSurfaceMaterialState.Opaque, SurfaceOpacity: 0.5f),
|
||||
};
|
||||
|
||||
var indirect = new DrawElementsIndirectCommand[16];
|
||||
|
|
@ -75,12 +75,15 @@ public sealed class WbDrawDispatcherIndirectBuilderTests
|
|||
{
|
||||
new(IndexCount: 10, FirstIndex: 0, BaseVertex: 0, InstanceCount: 1, FirstInstance: 0,
|
||||
TextureIndex: 0x1, TextureLayer: 0, Translucency: TranslucencyKind.Opaque,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
CullMode: CullMode.Clockwise),
|
||||
new(IndexCount: 20, FirstIndex: 10, BaseVertex: 0, InstanceCount: 1, FirstInstance: 1,
|
||||
TextureIndex: 0x2, TextureLayer: 0, Translucency: TranslucencyKind.AlphaBlend,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
CullMode: CullMode.None),
|
||||
new(IndexCount: 30, FirstIndex: 30, BaseVertex: 0, InstanceCount: 1, FirstInstance: 2,
|
||||
TextureIndex: 0x3, TextureLayer: 0, Translucency: TranslucencyKind.ClipMap,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque,
|
||||
CullMode: CullMode.Landblock),
|
||||
};
|
||||
var indirect = new DrawElementsIndirectCommand[4];
|
||||
|
|
@ -116,7 +119,7 @@ public sealed class WbDrawDispatcherIndirectBuilderTests
|
|||
// because the discard handles transparency, not blending.
|
||||
var groups = new List<WbDrawDispatcher.IndirectGroupInput>
|
||||
{
|
||||
new(IndexCount: 10, FirstIndex: 0, BaseVertex: 0, InstanceCount: 1, FirstInstance: 0, TextureIndex: 0x1, TextureLayer: 0, Translucency: TranslucencyKind.ClipMap),
|
||||
new(IndexCount: 10, FirstIndex: 0, BaseVertex: 0, InstanceCount: 1, FirstInstance: 0, TextureIndex: 0x1, TextureLayer: 0, Translucency: TranslucencyKind.ClipMap, MaterialState: RetailSetSurfaceMaterialState.Opaque),
|
||||
};
|
||||
var indirect = new DrawElementsIndirectCommand[4];
|
||||
var batch = new WbDrawDispatcher.BatchDataPublic[4];
|
||||
|
|
@ -146,7 +149,8 @@ public sealed class WbDrawDispatcherIndirectBuilderTests
|
|||
FirstInstance: index,
|
||||
TextureIndex: (uint)index,
|
||||
TextureLayer: 0,
|
||||
Translucency: kind)).ToList();
|
||||
Translucency: kind,
|
||||
MaterialState: RetailSetSurfaceMaterialState.Opaque)).ToList();
|
||||
var indirect = new DrawElementsIndirectCommand[kinds.Length];
|
||||
var batches = new WbDrawDispatcher.BatchDataPublic[kinds.Length];
|
||||
|
||||
|
|
|
|||
|
|
@ -77,4 +77,28 @@ public sealed class ContentMigrationCatalogTests
|
|||
Assert.Contains("CellStruct", plan.Reason, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("surface opacity", plan.Reason, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RecipeNineToTenRequiresOneExplicitFullRebuild()
|
||||
{
|
||||
ContentMigrationPlan plan = ContentMigrationCatalog.Resolve(9, 10);
|
||||
|
||||
Assert.Equal(ContentWorkKind.FullRebuild, plan.Kind);
|
||||
Assert.Equal(9u, plan.FromRecipeVersion);
|
||||
Assert.Equal(10u, plan.TargetRecipeVersion);
|
||||
Assert.Contains("SetSurface", plan.Reason, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Empty(plan.EffectiveDatIds);
|
||||
Assert.Empty(plan.EffectiveLandblocks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnyOlderRecipeToTenCollapsesToOneFullRebuild()
|
||||
{
|
||||
ContentMigrationPlan plan = ContentMigrationCatalog.Resolve(1, 10);
|
||||
|
||||
Assert.Equal(ContentWorkKind.FullRebuild, plan.Kind);
|
||||
Assert.Equal(10u, plan.TargetRecipeVersion);
|
||||
Assert.Contains("surface opacity", plan.Reason, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("SetSurface", plan.Reason, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue