sharpemu/tests/SharpEmu.Libs.Tests/Gpu/Images/ImageRequestBuildersTests.cs

496 lines
24 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.Libs.Gpu.Images;
using SharpEmu.ShaderCompiler;
using SharpEmu.ShaderCompiler.Resources;
using SharpEmu.Libs.Gpu.Scheduling;
using SharpEmu.Libs.Tests.Gpu.Scheduling;
using SharpEmu.Libs.Tests.Gpu.Vulkan;
using Silk.NET.Vulkan;
using Xunit;
namespace SharpEmu.Libs.Tests.Gpu.Images;
// The image requests the presenter builds from raw target registers, descriptor words and display attributes.
[Collection(SchedulingStateCollection.Name)]
public sealed class ImageRequestBuildersTests : IClassFixture<HeadlessVulkanFixture>
{
private const ulong Base = 0x1_0000_0000;
[Fact]
public void EightBitUnsignedScaledTextureUsesUnormBackingWithShaderConversion()
{
Assert.Equal(ImageNumericClass.Float, GuestImageFormat.SampledNumericClass(GuestImageFormat.Format8Uscaled));
Assert.Equal(1u, GuestImageFormat.Remap(GuestImageFormat.Format8Uscaled));
var request = ImageRequestBuilders.Texture(
RegisterWords.Texture(Base, GuestPixelFormat.Bits8UScaled, 32, 32),
new ShaderImageShape(false, false, false, false, TextureNumericClass.Float));
Assert.Equal(Format.R8Unorm, request.Request.Description.PixelFormat);
Assert.True(request.ExactFormat);
var twoChannel = ImageRequestBuilders.Texture(
RegisterWords.Texture(Base, GuestPixelFormat.Bits8_8UScaled, 32, 32),
new ShaderImageShape(false, false, false, false, TextureNumericClass.Float));
Assert.Equal(Format.R8G8Unorm, twoChannel.Request.Description.PixelFormat);
Assert.True(twoChannel.ExactFormat);
}
[Fact]
public void StorageWithoutMipOperandUsesTheAvailableMipRange()
{
var words = RegisterWords.Texture(0x22AC00000, GuestPixelFormat.Bits8_8_8_8UNorm,
512, 512, baseLevel: 0, lastLevel: 9, maxMip: 8);
var shape = new ShaderImageShape(false, false, true, false, TextureNumericClass.Float);
var request = ImageRequestBuilders.Texture(words, shape).Request;
Assert.Equal(9u, request.Description.Resources.Levels);
Assert.Equal(0u, request.View.BaseLevel);
using var fatal = new FatalScope();
Assert.Throws<SchedulerFatalException>(() =>
ImageRequestBuilders.Texture(words, shape with { DynamicMip = true }));
}
private static readonly ShaderImageShape Sampled2D = new(false, false, false, false, TextureNumericClass.Float);
[Theory]
[InlineData(GuestPixelFormat.Bits16UNorm, Format.D16Unorm, Format.R16Unorm)]
[InlineData(GuestPixelFormat.Bits32Float, Format.D32Sfloat, Format.R32Sfloat)]
public void ComparisonTexture_SelectsDepthWithoutChangingTheGuestLayout(
GuestPixelFormat guestFormat, Format depthFormat, Format colorFormat)
{
var words = RegisterWords.Texture(Base, guestFormat, 48, 24);
var ordinary = ImageRequestBuilders.Texture(words, Sampled2D);
var comparison = ImageRequestBuilders.Texture(words, Sampled2D with { DepthCompare = true });
Assert.Equal(colorFormat, ordinary.Request.Description.PixelFormat);
Assert.False(ordinary.Request.Description.IsDepth);
Assert.Equal(depthFormat, comparison.Request.Description.PixelFormat);
Assert.Equal(depthFormat, comparison.Request.View.Format);
Assert.True(comparison.Request.Description.IsDepth);
Assert.Equal(ordinary.Request.Description.Data, comparison.Request.Description.Data);
Assert.Equal(ordinary.Request.Description.Pitch, comparison.Request.Description.Pitch);
Assert.Equal(ordinary.Request.Description.MipLayout[0], comparison.Request.Description.MipLayout[0]);
Assert.Equal(ImageRole.Texture, comparison.Request.Role);
}
[Theory]
[InlineData(GuestImageType.Color2D, 1u, false)]
[InlineData(GuestImageType.Color2D, 1u, true)]
[InlineData(GuestImageType.Color3D, 32u, false)]
[InlineData(GuestImageType.Color3D, 32u, true)]
[InlineData(GuestImageType.Color2DArray, 4u, false)]
[InlineData(GuestImageType.Color2DArray, 4u, true)]
public void CompressedTexture_CarriesDccMetadataForEverySlice(GuestImageType type, uint layers, bool storage)
{
const ulong metadata = 0x1_2000_0000;
var words = RegisterWords.Texture(Base, GuestPixelFormat.Bits8_8_8_8UNorm, 32, 32, type, GuestTileMode.RenderTarget, layers: layers);
words[6] |= (1u << 21) | (uint)(((metadata >> 8) & 0xFF) << 24);
words[7] = (uint)(metadata >> 16);
var shape = Sampled2D with { Volume = type == GuestImageType.Color3D, Arrayed = type == GuestImageType.Color2DArray, Storage = storage };
var description = ImageRequestBuilders.Texture(words, shape).Request.Description;
Assert.Equal(MetadataKind.Dcc, description.Metadata.Kind);
Assert.Equal(metadata, description.Metadata.Range.Address);
Assert.Equal((ulong)layers * 0x1000, description.Metadata.Range.Size);
}
[Fact]
public void DemonSlayerColorLut_CarriesItsVolumeDccFootprintAndAlphaPosition()
{
uint[] words = [0x606D4E00, 0xC3200000, 0x0007C007, 0xA1B00FAC, 0x0000001F, 0x00700000, 0x006B0000, 0x00606D6E];
var description = ImageRequestBuilders.Texture(words, Sampled2D with { Volume = true }).Request.Description;
Assert.Equal(GuestImageType.Color3D, description.Type);
Assert.Equal(new Extent3D(32, 32, 32), description.Extent);
Assert.Equal(Format.A2B10G10R10UnormPack32, description.PixelFormat);
Assert.Equal(MetadataKind.Dcc, description.Metadata.Kind);
Assert.Equal(0x606D6E0000UL, description.Metadata.Range.Address);
Assert.Equal(0x20000UL, description.Metadata.Range.Size);
Assert.True(description.Metadata.DccAlphaMsb);
}
private readonly HeadlessVulkan? _vulkan;
public ImageRequestBuildersTests(HeadlessVulkanFixture fixture) => _vulkan = fixture.Vulkan;
[Fact]
public void ColorTarget_LinearTargetFromRegisters()
{
var resolution = ImageRequestBuilders.ColorTarget(RegisterWords.Color(Base, 64, 64), 0xF, 0, false);
Assert.NotNull(resolution);
var request = resolution.Value.Request;
Assert.Equal(ImageRole.ColorTarget, request.Role);
Assert.Equal(Base, request.Description.Data.Address);
Assert.Equal(16384UL, request.Description.Data.Size);
Assert.Equal(Format.R8G8B8A8Unorm, request.Description.PixelFormat);
Assert.Equal(GuestImageType.Color2D, request.Description.Type);
Assert.Equal(new Extent3D(64, 64, 1), request.Description.Extent);
Assert.Equal(new SubresourceCount(1, 1), request.Description.Resources);
Assert.Equal(64u, request.Description.Pitch);
Assert.Equal(4u, request.Description.BytesPerBlock);
Assert.Equal(1u, request.Description.Samples);
Assert.Equal(GuestTileMode.Linear, request.Description.TileMode);
Assert.Equal(16384UL, request.Description.MipLayout[0].Size);
Assert.Equal(ImageViewType.Type2D, request.View.Type);
Assert.Equal(ImageUsageFlags.ColorAttachmentBit, request.View.Usage);
Assert.Equal(new Extent2D(64, 64), resolution.Value.Extent);
Assert.Equal(16384UL, resolution.Value.BackingSize);
Assert.Equal(1u, resolution.Value.Samples);
Assert.Equal(0u, resolution.Value.BaseMipLevel);
Assert.False(resolution.Value.MetadataClearSupported);
}
[Fact]
public void ColorTarget_NoTargetWithoutAnAddressOrAWriteMask()
{
Assert.Null(ImageRequestBuilders.ColorTarget(RegisterWords.Color(0, 64, 64), 0xF, 0, false));
Assert.Null(ImageRequestBuilders.ColorTarget(RegisterWords.Color(Base, 64, 64), 0, 0, false));
Assert.NotNull(ImageRequestBuilders.ColorTarget(RegisterWords.Color(Base, 64, 64), 0, 0, ignoreTargetMask: true));
}
[Fact]
public void ColorTarget_MipViewSelectsTheLevelExtent()
{
var words = RegisterWords.Color(Base, 64, 64, GuestTileMode.RenderTarget, maxMip: 1, mipLevel: 1);
var resolution = ImageRequestBuilders.ColorTarget(words, 0xF, 0, false);
Assert.NotNull(resolution);
var request = resolution.Value.Request;
Assert.Equal(2u, request.Description.Resources.Levels);
Assert.Equal(GuestTileMode.RenderTarget, request.Description.TileMode);
Assert.True(request.Description.MipLayout[1].Size > 0);
Assert.Equal(1u, request.View.BaseLevel);
Assert.Equal(1u, request.View.LevelCount);
Assert.Equal(new Extent2D(32, 32), resolution.Value.Extent);
Assert.Equal(1u, resolution.Value.BaseMipLevel);
Assert.Equal(request.Description.Data.Size, resolution.Value.BackingSize);
}
[Fact]
public void ColorTarget_LayeredViewCoversEveryLayer()
{
var resolution = ImageRequestBuilders.ColorTarget(RegisterWords.Color(Base, 64, 64, sliceMax: 3), 0xF, 0, false);
Assert.NotNull(resolution);
var request = resolution.Value.Request;
Assert.Equal(new SubresourceCount(1, 4), request.Description.Resources);
Assert.Equal(4 * 16384UL, request.Description.Data.Size);
Assert.Equal(ImageViewType.Type2DArray, request.View.Type);
Assert.Equal(0u, request.View.BaseLayer);
Assert.Equal(4u, request.View.LayerCount);
Assert.Equal(4 * 16384UL, resolution.Value.BackingSize);
}
[Fact]
public void ColorTarget_VolumeAcceptsExclusiveSliceMax()
{
var words = RegisterWords.Color(
Base,
64,
64,
GuestTileMode.Standard4KB,
sliceMax: 64,
dimension: 2,
depth: 63);
var resolution = ImageRequestBuilders.ColorTarget(words, 0xF, 0, false);
Assert.NotNull(resolution);
Assert.Equal(64u, resolution.Value.Request.View.LayerCount);
Assert.Equal(64u, resolution.Value.Request.Description.Extent.Depth);
}
[Fact]
public void ColorTarget_VolumeViewEndingOnePastTheLastSliceIsClamped()
{
var resolution = ImageRequestBuilders.ColorTarget(RegisterWords.Color(Base, 32, 32, GuestTileMode.RenderTarget, sliceMax: 32, dimension: 2, depth: 31), 0xF, 0, false);
Assert.NotNull(resolution);
var request = resolution.Value.Request;
Assert.Equal(32u, request.Description.Extent.Depth);
Assert.Equal(0u, request.View.BaseLayer);
}
[Fact]
public void ColorTarget_VolumeViewStartingPastTheLastSliceIsFatal()
{
using var fatal = new FatalScope();
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.ColorTarget(
RegisterWords.Color(Base, 32, 32, GuestTileMode.RenderTarget, sliceStart: 32, sliceMax: 32, dimension: 2, depth: 31), 0xF, 0, false));
}
[Fact]
public void ColorTarget_RejectsUnsupportedRegisterStates()
{
using var fatal = new FatalScope();
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.ColorTarget(RegisterWords.Color(Base, 64, 64, samplesLog2: 1), 0xF, 0, false));
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.ColorTarget(RegisterWords.Color(Base, 64, 64, maxMip: 1), 0xF, 0, false));
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.ColorTarget(RegisterWords.Color(Base, 64, 64, dimension: 3), 0xF, 0, false));
Assert.Equal(3, fatal.Messages.Count);
}
[Fact]
public void SampleCount_DecodesTheLog2Encoding()
{
Assert.Equal(1u, ImageRequestBuilders.SampleCount(0));
Assert.Equal(2u, ImageRequestBuilders.SampleCount(1));
Assert.Equal(8u, ImageRequestBuilders.SampleCount(3));
Assert.Equal(0u, ImageRequestBuilders.SampleCount(4));
}
[Fact]
public void ColorTarget_LinearTargetPitchMatchesTheLinearTexturePitch()
{
var target = ImageRequestBuilders.ColorTarget(RegisterWords.Color(Base, 816, 604), 0xF, 0, false);
var texture = ImageRequestBuilders.Texture(RegisterWords.Texture(Base, GuestPixelFormat.Bits8_8_8_8UNorm, 816, 604), Sampled2D);
Assert.NotNull(target);
var description = target.Value.Request.Description;
Assert.Equal(new Extent3D(816, 604, 1), description.Extent);
Assert.Equal(832u, description.Pitch);
Assert.Equal(832UL * 604 * 4, description.Data.Size);
Assert.Equal(texture.Request.Description.Pitch, description.Pitch);
Assert.Equal(texture.Request.Description.Data.Size, description.Data.Size);
}
[Fact]
public void Texture_LinearTwoDimensionalFromDescriptor()
{
var words = RegisterWords.Texture(Base, GuestPixelFormat.Bits8_8_8_8UNorm, 64, 64);
var resolution = ImageRequestBuilders.Texture(words, Sampled2D);
var request = resolution.Request;
Assert.Equal(ImageRole.Texture, request.Role);
Assert.Equal(Base, request.Description.Data.Address);
Assert.True(request.Description.Data.Size >= 16384UL);
Assert.Equal(Format.R8G8B8A8Unorm, request.Description.PixelFormat);
Assert.Equal(GuestPixelFormat.Bits8_8_8_8UNorm, request.Description.GuestFormat);
Assert.Equal(GuestImageType.Color2D, request.Description.Type);
Assert.Equal(new Extent3D(64, 64, 1), request.Description.Extent);
Assert.Equal(new SubresourceCount(1, 1), request.Description.Resources);
Assert.Equal(4u, request.Description.BytesPerBlock);
Assert.Equal(1u, request.Description.Samples);
Assert.Equal(request.Description.Data.Size, request.Description.MipLayout[0].Size);
Assert.Equal(ImageViewType.Type2D, request.View.Type);
Assert.Equal(ImageUsageFlags.SampledBit, request.View.Usage);
Assert.Equal(Format.R8G8B8A8Unorm, request.View.Format);
Assert.False(resolution.ExactFormat);
Assert.Equal(Format.R8G8B8A8Unorm, resolution.ViewFormat);
Assert.Equal(ImageRequestBuilders.DestinationSwizzle(new TextureDescriptorWords(words)), resolution.Swizzle);
}
[Fact]
public void Texture_MipWindowFollowsTheDescriptor()
{
var words = RegisterWords.Texture(Base, GuestPixelFormat.Bits8_8_8_8UNorm, 64, 64, baseLevel: 1, lastLevel: 2, maxMip: 2);
var request = ImageRequestBuilders.Texture(words, Sampled2D).Request;
Assert.Equal(3u, request.Description.Resources.Levels);
Assert.True(request.Description.MipLayout[2].Size > 0);
Assert.NotEqual(request.Description.MipLayout[1].Offset, request.Description.MipLayout[2].Offset);
Assert.Equal(1u, request.View.BaseLevel);
Assert.Equal(2u, request.View.LevelCount);
}
[Fact]
public void Texture_CubeBecomesALayeredTwoDimensionalImage()
{
var words = RegisterWords.Texture(Base, GuestPixelFormat.Bits8_8_8_8UNorm, 32, 32, GuestImageType.Cube, layers: 6, baseArray: 2);
var arrayed = ImageRequestBuilders.Texture(words, Sampled2D with { Arrayed = true }).Request;
var single = ImageRequestBuilders.Texture(words, Sampled2D).Request;
Assert.Equal(GuestImageType.Color2D, arrayed.Description.Type);
Assert.Equal(new SubresourceCount(1, 6), arrayed.Description.Resources);
Assert.Equal(ImageViewType.Type2DArray, arrayed.View.Type);
Assert.Equal(2u, arrayed.View.BaseLayer);
Assert.Equal(4u, arrayed.View.LayerCount);
Assert.Equal(ImageViewType.Type2D, single.View.Type);
Assert.Equal(2u, single.View.BaseLayer);
Assert.Equal(1u, single.View.LayerCount);
}
[Fact]
public void Texture_NullDescriptorBindsAOneTexelImage()
{
var words = new uint[8];
var sampled = ImageRequestBuilders.Texture(words, Sampled2D);
var storage = ImageRequestBuilders.Texture(words, Sampled2D with { Storage = true, NumericClass = TextureNumericClass.Uint });
Assert.Equal(ImageRole.Texture, sampled.Request.Role);
Assert.Equal(new Extent3D(1, 1, 1), sampled.Request.Description.Extent);
Assert.Equal(Format.R32Sfloat, sampled.Request.Description.PixelFormat);
Assert.Equal(ImageUsageFlags.SampledBit, sampled.Request.View.Usage);
Assert.Equal(ImageRole.StorageImage, storage.Request.Role);
Assert.Equal(Format.R32Uint, storage.Request.Description.PixelFormat);
Assert.Equal(ImageUsageFlags.StorageBit, storage.Request.View.Usage);
}
[Fact]
public void Texture_StorageViewOfAnSrgbImageUsesTheLinearFormat()
{
var words = RegisterWords.Texture(Base, GuestPixelFormat.Bits8_8_8_8Srgb, 64, 64);
var resolution = ImageRequestBuilders.Texture(words, Sampled2D with { Storage = true });
Assert.Equal(ImageRole.StorageImage, resolution.Request.Role);
Assert.Equal(Format.R8G8B8A8Srgb, resolution.Request.Description.PixelFormat);
Assert.Equal(Format.R8G8B8A8Unorm, resolution.Request.View.Format);
Assert.Equal(ImageUsageFlags.StorageBit, resolution.Request.View.Usage);
Assert.Equal(Format.R8G8B8A8Srgb, resolution.ViewFormat);
}
[Fact]
public void Texture_RejectsAnInvertedMipWindow()
{
using var fatal = new FatalScope();
var words = RegisterWords.Texture(Base, GuestPixelFormat.Bits8_8_8_8UNorm, 64, 64, baseLevel: 2, lastLevel: 1, maxMip: 2);
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.Texture(words, Sampled2D));
Assert.Contains("baseLevel=2", Assert.Single(fatal.Messages));
}
[Fact]
public void DepthTarget_ResolvesTheDepthOnlyAttachment()
{
if (!GatePrerequisites.Ready(_vulkan)) return;
var resolution = ImageRequestBuilders.DepthTarget(RegisterWords.Depth(Base, 64, 64, depthClear: true), _vulkan.DeviceInfo);
Assert.NotNull(resolution);
var value = resolution.Value;
Assert.Equal(Format.D32Sfloat, value.Format);
Assert.Equal(64u, value.Width);
Assert.Equal(64u, value.Height);
Assert.Equal(1u, value.Samples);
Assert.False(value.HasStencil);
Assert.False(value.HasHtile);
Assert.Equal(Base, value.DepthAddress);
Assert.True(value.DepthSize > 0);
Assert.Equal(0UL, value.StencilSize);
Assert.True(value.DepthClearEnabled);
Assert.False(value.StencilClearEnabled);
var request = value.Request;
Assert.Equal(ImageRole.DepthTarget, request.Role);
Assert.Equal(new GuestSpanCheck(Base, value.DepthSize), new GuestSpanCheck(request.Description.Data.Address, request.Description.Data.Size));
Assert.Equal(GuestTileMode.Depth, request.Description.TileMode);
Assert.Equal(value.DepthSize, request.Description.MipLayout[0].Size);
Assert.Equal(ImageAspectFlags.DepthBit, request.View.Aspect);
Assert.Equal(ImageUsageFlags.DepthStencilAttachmentBit, request.View.Usage);
}
[Fact]
public void DepthTarget_WithStencilSelectsACombinedFormat()
{
if (!GatePrerequisites.Ready(_vulkan)) return;
var words = RegisterWords.Depth(Base, 64, 64, stencilBase: Base + 0x80000);
var resolution = ImageRequestBuilders.DepthTarget(words, _vulkan.DeviceInfo);
Assert.NotNull(resolution);
var value = resolution.Value;
Assert.Equal(Format.D32SfloatS8Uint, value.Format);
Assert.True(value.HasStencil);
Assert.Equal(Base + 0x80000, value.StencilAddress);
Assert.True(value.StencilSize > 0);
Assert.Equal(value.StencilSize, value.Request.Description.Stencil.Size);
Assert.Equal(ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit, value.Request.View.Aspect);
Assert.False(value.StencilClearEnabled);
}
[Fact]
public void DepthTarget_StencilIsCompressedOnlyWithHtileBacking()
{
if (!GatePrerequisites.Ready(_vulkan)) return;
const ulong stencilBase = Base + 0x80000;
const ulong htileBase = Base + 0x100000;
var words = RegisterWords.Depth(Base, 64, 64, stencilBase: stencilBase) with
{
ZInfo = (uint)GuestDepthFormat.Z32Float | (1u << 29),
StencilInfo = 0x00100981,
HtileBase = htileBase,
};
var resolution = ImageRequestBuilders.DepthTarget(words, _vulkan.DeviceInfo);
Assert.NotNull(resolution);
var value = resolution.Value;
Assert.True(value.HasStencil);
Assert.True(value.HasHtile);
Assert.Equal(MetadataKind.Htile, value.Request.Description.Metadata.Kind);
Assert.Equal(htileBase, value.Request.Description.Metadata.Range.Address);
Assert.True(value.Request.Description.Metadata.StencilCompressed);
var missingHtile = words with { ZInfo = (uint)GuestDepthFormat.Z32Float, HtileBase = 0 };
var uncompressed = ImageRequestBuilders.DepthTarget(missingHtile, _vulkan.DeviceInfo);
Assert.NotNull(uncompressed);
Assert.True(uncompressed.Value.HasStencil);
Assert.False(uncompressed.Value.HasHtile);
Assert.False(uncompressed.Value.Request.Description.Metadata.StencilCompressed);
}
[Fact]
public void DepthTarget_NoAttachmentWhenNothingIsActive()
{
if (!GatePrerequisites.Ready(_vulkan)) return;
Assert.Null(ImageRequestBuilders.DepthTarget(RegisterWords.Depth(Base, 64, 64, depthTest: false, depthWrite: false), _vulkan.DeviceInfo));
var unbound = new DepthTargetWords(0, 0, 0, 0, false, 0, 0, 2, 0, 0, 0, 0, 0);
Assert.Null(ImageRequestBuilders.DepthTarget(unbound, _vulkan.DeviceInfo));
}
[Fact]
public void DepthTarget_RejectsAMissingDepthBase()
{
if (!GatePrerequisites.Ready(_vulkan)) return;
using var fatal = new FatalScope();
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.DepthTarget(RegisterWords.Depth(0, 64, 64), _vulkan.DeviceInfo));
Assert.Contains("read=0x0000000000000000", Assert.Single(fatal.Messages));
}
[Fact]
public void DisplaySurface_FromRegisteredAttributes()
{
var surface = new DisplaySurfaceWords(Base, 0, 0x8000000022000000, 1920, 1080, 0, 0, 0, 0, false);
var request = ImageRequestBuilders.DisplaySurface(surface);
Assert.Equal(ImageRole.DisplaySurface, request.Role);
Assert.Equal(Base, request.Description.Data.Address);
Assert.Equal(request.Description.MipLayout[0].Size, request.Description.Data.Size);
Assert.Equal(Format.R8G8B8A8Srgb, request.Description.PixelFormat);
Assert.Equal(GuestPixelFormat.Bits8_8_8_8Srgb, request.Description.GuestFormat);
Assert.Equal(new Extent3D(1920, 1080, 1), request.Description.Extent);
Assert.Equal(GuestTileMode.RenderTarget, request.Description.TileMode);
Assert.Equal(4u, request.Description.BytesPerBlock);
Assert.Equal(MetadataKind.None, request.Description.Metadata.Kind);
Assert.Equal(ImageViewType.Type2D, request.View.Type);
Assert.Equal(ImageUsageFlags.TransferSrcBit, request.View.Usage);
}
[Theory]
[InlineData(0x8100070422000000UL, Format.A2B10G10R10UnormPack32, 8UL)]
[InlineData(0x8100070400000000UL, Format.A2R10G10B10UnormPack32, 40UL)]
[InlineData(0x8100000622000000UL, Format.A2B10G10R10UnormPack32, 32UL)]
[InlineData(0x8100000600000000UL, Format.A2R10G10B10UnormPack32, 0UL)]
public void DisplaySurface_Packed10BitFormatsPreserveEncodedValues(ulong pixelFormat, Format expectedFormat, ulong option)
{
var surface = new DisplaySurfaceWords(Base, 0, pixelFormat, 3840, 2160, 0, option, 0, 0, false);
var request = ImageRequestBuilders.DisplaySurface(surface);
Assert.Equal(expectedFormat, request.Description.PixelFormat);
Assert.Equal(GuestPixelFormat.Bits10_10_10_2UNorm, request.Description.GuestFormat);
Assert.Equal(4u, request.Description.BytesPerBlock);
Assert.Equal(expectedFormat, request.View.Format);
Assert.True(DisplayFormatRule.Supports(request.Description));
}
[Fact]
public void DisplaySurface_RejectsUnsupportedAttributes()
{
using var fatal = new FatalScope();
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.DisplaySurface(new DisplaySurfaceWords(Base, 0, 0x8000000022000000, 1920, 1080, 1, 0, 0, 0, false)));
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.DisplaySurface(new DisplaySurfaceWords(Base, 0, 0x1234, 1920, 1080, 0, 0, 0, 0, false)));
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.DisplaySurface(new DisplaySurfaceWords(Base, 0, 0x8000000022000000, 0, 1080, 0, 0, 0, 0, false)));
Assert.Throws<SchedulerFatalException>(() => ImageRequestBuilders.DisplaySurface(new DisplaySurfaceWords(Base, 0, 0x8100070422000000, 1920, 1080, 0, 1, 0, 0, false)));
Assert.Equal(4, fatal.Messages.Count);
}
private readonly record struct GuestSpanCheck(ulong Address, ulong Size);
}