mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-31 23:19:44 +08:00
fix(gpu): support Gen5 flat memory and 3D images (#587)
Vector-mesh UI text samples type-10 volume LUTs; treat MIMG DIM=2 as Dim3D and transport depth through AGC and Vulkan so Z slices no longer collapse into a single 2D plane.
This commit is contained in:
@@ -8328,16 +8328,33 @@ public static partial class AgcExports
|
||||
return tailLinear;
|
||||
}
|
||||
|
||||
var volumeDepth = checked((int)GetTextureVolumeDepth(
|
||||
descriptor.Type,
|
||||
descriptor.Depth));
|
||||
if (logicalByteCount % volumeDepth != 0 ||
|
||||
source.Length % volumeDepth != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var logicalSliceByteCount = logicalByteCount / volumeDepth;
|
||||
var physicalSliceByteCount = source.Length / volumeDepth;
|
||||
var linear = new byte[logicalByteCount];
|
||||
return GnmTiling.TryDetile(
|
||||
source,
|
||||
linear,
|
||||
descriptor.TileMode,
|
||||
elementsWide,
|
||||
elementsHigh,
|
||||
bytesPerElement)
|
||||
? linear
|
||||
: null;
|
||||
for (var slice = 0; slice < volumeDepth; slice++)
|
||||
{
|
||||
if (!GnmTiling.TryDetile(
|
||||
source.AsSpan(slice * physicalSliceByteCount, physicalSliceByteCount),
|
||||
linear.AsSpan(slice * logicalSliceByteCount, logicalSliceByteCount),
|
||||
descriptor.TileMode,
|
||||
elementsWide,
|
||||
elementsHigh,
|
||||
bytesPerElement))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return linear;
|
||||
}
|
||||
|
||||
private static void TraceTextureFallback(TextureDescriptor descriptor, string reason)
|
||||
@@ -8369,6 +8386,9 @@ public static partial class AgcExports
|
||||
out GuestDrawTexture texture)
|
||||
{
|
||||
texture = default!;
|
||||
var textureDepth = GetTextureVolumeDepth(
|
||||
descriptor.Type,
|
||||
descriptor.Depth);
|
||||
if ((descriptor.Type != Gen5TextureType1D &&
|
||||
descriptor.Type != Gen5TextureType2D &&
|
||||
descriptor.Type != Gen5TextureType3D &&
|
||||
@@ -8381,7 +8401,13 @@ public static partial class AgcExports
|
||||
descriptor.Height > 8192)
|
||||
{
|
||||
TraceTextureFallback(descriptor, "invalid-descriptor");
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType, isArrayed);
|
||||
texture = CreateFallbackGuestDrawTexture(
|
||||
isStorage,
|
||||
descriptor.Format,
|
||||
descriptor.NumberType,
|
||||
isArrayed,
|
||||
descriptor.Type,
|
||||
textureDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8391,10 +8417,15 @@ public static partial class AgcExports
|
||||
descriptor.Height,
|
||||
descriptor.Format)
|
||||
: descriptor.Width;
|
||||
var sourceByteCount = GetTextureByteCount(
|
||||
var sourceSliceByteCount = GetTextureByteCount(
|
||||
descriptor.Format,
|
||||
sourceWidth,
|
||||
descriptor.Height);
|
||||
var sourceByteCount = GetTextureByteCount(
|
||||
descriptor.Format,
|
||||
sourceWidth,
|
||||
descriptor.Height,
|
||||
textureDepth);
|
||||
if (sourceByteCount == 0 ||
|
||||
sourceByteCount > MaxPresentedTextureBytes ||
|
||||
sourceByteCount > int.MaxValue)
|
||||
@@ -8402,11 +8433,17 @@ public static partial class AgcExports
|
||||
TraceTextureFallback(
|
||||
descriptor,
|
||||
$"invalid-byte-count:{sourceByteCount}");
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType, isArrayed);
|
||||
texture = CreateFallbackGuestDrawTexture(
|
||||
isStorage,
|
||||
descriptor.Format,
|
||||
descriptor.NumberType,
|
||||
isArrayed,
|
||||
descriptor.Type,
|
||||
textureDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
var physicalSourceByteCount = sourceByteCount;
|
||||
var physicalSourceByteCount = sourceSliceByteCount;
|
||||
var elementsWide = 0;
|
||||
var elementsHigh = 0;
|
||||
var bytesPerElement = 0;
|
||||
@@ -8428,13 +8465,6 @@ public static partial class AgcExports
|
||||
physicalSourceByteCount = tiledByteCount;
|
||||
}
|
||||
|
||||
if (physicalSourceByteCount > MaxPresentedTextureBytes ||
|
||||
physicalSourceByteCount > int.MaxValue)
|
||||
{
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType, isArrayed);
|
||||
return true;
|
||||
}
|
||||
|
||||
var resourceMipLevels = descriptor.HasExtendedDescriptor
|
||||
? descriptor.ResourceMipLevels
|
||||
: 1u;
|
||||
@@ -8459,6 +8489,20 @@ public static partial class AgcExports
|
||||
chainSliceBytes = placedChainSliceBytes;
|
||||
}
|
||||
|
||||
physicalSourceByteCount = checked(physicalSourceByteCount * textureDepth);
|
||||
if (physicalSourceByteCount > MaxPresentedTextureBytes ||
|
||||
physicalSourceByteCount > int.MaxValue)
|
||||
{
|
||||
texture = CreateFallbackGuestDrawTexture(
|
||||
isStorage,
|
||||
descriptor.Format,
|
||||
descriptor.NumberType,
|
||||
isArrayed,
|
||||
descriptor.Type,
|
||||
textureDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
var wantsArrayUpload = isArrayed &&
|
||||
!isStorage &&
|
||||
descriptor.Address != 0 &&
|
||||
@@ -8497,7 +8541,9 @@ public static partial class AgcExports
|
||||
TileMode: descriptor.TileMode,
|
||||
DstSelect: descriptor.DstSelect,
|
||||
Sampler: ToGuestSampler(samplerDescriptor),
|
||||
ArrayedView: isArrayed);
|
||||
ArrayedView: isArrayed,
|
||||
Type: descriptor.Type,
|
||||
Depth: textureDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8573,7 +8619,9 @@ public static partial class AgcExports
|
||||
Pitch: sourceWidth,
|
||||
TileMode: descriptor.TileMode,
|
||||
DstSelect: descriptor.DstSelect,
|
||||
Sampler: ToGuestSampler(samplerDescriptor));
|
||||
Sampler: ToGuestSampler(samplerDescriptor),
|
||||
Type: descriptor.Type,
|
||||
Depth: textureDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8614,7 +8662,9 @@ public static partial class AgcExports
|
||||
sourceWidth,
|
||||
sampler,
|
||||
isArrayed,
|
||||
arrayUploadLayers)))
|
||||
arrayUploadLayers,
|
||||
descriptor.Type,
|
||||
textureDepth)))
|
||||
{
|
||||
texture = new GuestDrawTexture(
|
||||
descriptor.Address,
|
||||
@@ -8634,14 +8684,16 @@ public static partial class AgcExports
|
||||
DstSelect: descriptor.DstSelect,
|
||||
Sampler: sampler,
|
||||
ArrayedView: isArrayed,
|
||||
ArrayLayers: arrayUploadLayers);
|
||||
ArrayLayers: arrayUploadLayers,
|
||||
Type: descriptor.Type,
|
||||
Depth: textureDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wantsArrayUpload)
|
||||
{
|
||||
var arrayLayers = arrayUploadLayers;
|
||||
var layerBytes = checked((int)sourceByteCount);
|
||||
var layerBytes = checked((int)sourceSliceByteCount);
|
||||
var totalBytes = (long)layerBytes * arrayLayers;
|
||||
if (totalBytes <= int.MaxValue)
|
||||
{
|
||||
@@ -8649,7 +8701,7 @@ public static partial class AgcExports
|
||||
var uploadedLayers = 0u;
|
||||
for (var layer = 0u; layer < arrayLayers; layer++)
|
||||
{
|
||||
var sliceSource = new byte[(int)physicalSourceByteCount];
|
||||
var sliceSource = new byte[(int)chainSliceBytes];
|
||||
if (!ctx.Memory.TryRead(
|
||||
descriptor.Address + layer * chainSliceBytes + baseMipByteOffset,
|
||||
sliceSource))
|
||||
@@ -8690,7 +8742,9 @@ public static partial class AgcExports
|
||||
DstSelect: descriptor.DstSelect,
|
||||
Sampler: sampler,
|
||||
ArrayedView: true,
|
||||
ArrayLayers: arrayLayers);
|
||||
ArrayLayers: arrayLayers,
|
||||
Type: descriptor.Type,
|
||||
Depth: textureDepth);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -8704,7 +8758,13 @@ public static partial class AgcExports
|
||||
TraceTextureFallback(
|
||||
descriptor,
|
||||
$"guest-read-failed:{sourceByteCount}");
|
||||
texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType, isArrayed);
|
||||
texture = CreateFallbackGuestDrawTexture(
|
||||
isStorage,
|
||||
descriptor.Format,
|
||||
descriptor.NumberType,
|
||||
isArrayed,
|
||||
descriptor.Type,
|
||||
textureDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8759,7 +8819,9 @@ public static partial class AgcExports
|
||||
DstSelect: descriptor.DstSelect,
|
||||
Sampler: ToGuestSampler(samplerDescriptor),
|
||||
WriteGeneration: hasWriteGeneration ? writeGeneration : -1,
|
||||
ArrayedView: isArrayed);
|
||||
ArrayedView: isArrayed,
|
||||
Type: descriptor.Type,
|
||||
Depth: textureDepth);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9039,7 +9101,9 @@ public static partial class AgcExports
|
||||
bool isStorage,
|
||||
uint format,
|
||||
uint numberType,
|
||||
bool isArrayed = false)
|
||||
bool isArrayed = false,
|
||||
uint type = Gen5TextureType2D,
|
||||
uint depth = 1)
|
||||
{
|
||||
var fallbackFormat = format == 0 ? 10u : format;
|
||||
var fallbackNumberType = numberType;
|
||||
@@ -9054,7 +9118,9 @@ public static partial class AgcExports
|
||||
IsStorage: isStorage,
|
||||
MipLevels: 1,
|
||||
MipLevel: 0,
|
||||
ArrayedView: isArrayed);
|
||||
ArrayedView: isArrayed,
|
||||
Type: type,
|
||||
Depth: GetTextureVolumeDepth(type, depth));
|
||||
}
|
||||
|
||||
private static GuestSampler ToGuestSampler(IReadOnlyList<uint> descriptor) =>
|
||||
@@ -10240,7 +10306,8 @@ public static partial class AgcExports
|
||||
var totalBytes = GetTextureByteCount(
|
||||
texture.Format,
|
||||
texture.Width,
|
||||
texture.Height);
|
||||
texture.Height,
|
||||
GetTextureVolumeDepth(texture.Type, texture.Depth));
|
||||
if (totalBytes == 0)
|
||||
{
|
||||
return "probe=unsupported";
|
||||
@@ -10323,20 +10390,37 @@ public static partial class AgcExports
|
||||
_ => 0UL,
|
||||
};
|
||||
|
||||
private static ulong GetTextureByteCount(uint format, uint width, uint height)
|
||||
internal static ulong GetTextureByteCount(
|
||||
uint format,
|
||||
uint width,
|
||||
uint height,
|
||||
uint depth = 1)
|
||||
{
|
||||
var bytesPerTexel = GetTextureBytesPerTexel(format);
|
||||
if (bytesPerTexel != 0)
|
||||
{
|
||||
return checked((ulong)width * height * bytesPerTexel);
|
||||
return checked(
|
||||
(ulong)width *
|
||||
height *
|
||||
Math.Max(depth, 1u) *
|
||||
bytesPerTexel);
|
||||
}
|
||||
|
||||
var blockBytes = (ulong)GetBlockCompressedBlockBytes(format);
|
||||
return blockBytes == 0
|
||||
? 0
|
||||
: checked(((ulong)width + 3) / 4 * (((ulong)height + 3) / 4) * blockBytes);
|
||||
: checked(
|
||||
((ulong)width + 3) / 4 *
|
||||
(((ulong)height + 3) / 4) *
|
||||
Math.Max(depth, 1u) *
|
||||
blockBytes);
|
||||
}
|
||||
|
||||
internal static uint GetTextureVolumeDepth(uint type, uint depth) =>
|
||||
type == Gen5TextureType3D
|
||||
? Math.Max(depth, 1u)
|
||||
: 1u;
|
||||
|
||||
private static uint GetLinearTexturePitch(uint pitch, uint height, uint format)
|
||||
{
|
||||
var bytesPerTexel = GetTextureBytesPerTexel(format);
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace SharpEmu.Libs.Gpu;
|
||||
// translation for its API.
|
||||
|
||||
/// <summary>A guest texture referenced by a draw or dispatch. Format/NumberType/
|
||||
/// TileMode/DstSelect are raw guest descriptor codes.</summary>
|
||||
/// TileMode/DstSelect/Type are raw guest descriptor codes. Depth is the
|
||||
/// normalized volume depth (one for non-3D resources).</summary>
|
||||
internal sealed record GuestDrawTexture(
|
||||
ulong Address,
|
||||
uint Width,
|
||||
@@ -32,7 +33,9 @@ internal sealed record GuestDrawTexture(
|
||||
// from; -1 when the range is untracked or the pixels were not read here.
|
||||
long WriteGeneration = -1,
|
||||
bool ArrayedView = false,
|
||||
uint ArrayLayers = 1);
|
||||
uint ArrayLayers = 1,
|
||||
uint Type = 9,
|
||||
uint Depth = 1);
|
||||
|
||||
/// <summary>Raw guest sampler descriptor dwords, copied verbatim from guest memory.</summary>
|
||||
internal readonly record struct GuestSampler(
|
||||
@@ -55,7 +58,9 @@ internal readonly record struct TextureContentIdentity(
|
||||
uint Pitch,
|
||||
GuestSampler Sampler,
|
||||
bool Arrayed = false,
|
||||
uint ArrayLayers = 1);
|
||||
uint ArrayLayers = 1,
|
||||
uint Type = 9,
|
||||
uint Depth = 1);
|
||||
|
||||
internal sealed record GuestMemoryBuffer(
|
||||
ulong BaseAddress,
|
||||
|
||||
@@ -113,6 +113,26 @@ internal static unsafe class VulkanVideoPresenter
|
||||
// always takes its dimensions from the native child control instead.
|
||||
private const uint DefaultWindowWidth = 1920;
|
||||
private const uint DefaultWindowHeight = 1080;
|
||||
internal const uint Gen5TextureType2D = 9;
|
||||
internal const uint Gen5TextureType3D = 10;
|
||||
|
||||
internal static bool IsGuestTexture3D(uint type) =>
|
||||
type == Gen5TextureType3D;
|
||||
|
||||
internal static uint GetGuestTextureDepth(uint type, uint depth) =>
|
||||
IsGuestTexture3D(type) ? Math.Max(depth, 1u) : 1u;
|
||||
|
||||
internal static ImageType GetGuestTextureImageType(uint type) =>
|
||||
IsGuestTexture3D(type) ? ImageType.Type3D : ImageType.Type2D;
|
||||
|
||||
internal static ImageViewType GetGuestTextureViewType(
|
||||
uint type,
|
||||
bool arrayedView = false) =>
|
||||
IsGuestTexture3D(type)
|
||||
? ImageViewType.Type3D
|
||||
: arrayedView
|
||||
? ImageViewType.Type2DArray
|
||||
: ImageViewType.Type2D;
|
||||
|
||||
internal enum StorageImageComponentKind
|
||||
{
|
||||
@@ -1759,6 +1779,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
return checked((ulong)width * height * bytesPerPixel);
|
||||
}
|
||||
|
||||
internal static ulong GetGuestImageByteCount(
|
||||
uint format,
|
||||
uint width,
|
||||
uint height,
|
||||
uint depth) =>
|
||||
checked(GetGuestImageByteCount(format, width, height) * Math.Max(depth, 1u));
|
||||
|
||||
// Maps a UNORM swapchain format to the sRGB view of the same bit layout,
|
||||
// or Undefined when no counterpart exists. Used to encode linear-float
|
||||
// guest flips on their way into a UNORM swapchain.
|
||||
@@ -2957,6 +2984,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
ulong Address,
|
||||
uint Width,
|
||||
uint Height,
|
||||
uint Depth,
|
||||
uint Type,
|
||||
uint MipLevels,
|
||||
uint GuestFormat,
|
||||
Format Format);
|
||||
@@ -3110,9 +3139,12 @@ internal static unsafe class VulkanVideoPresenter
|
||||
public ImageView View;
|
||||
public uint Width;
|
||||
public uint Height;
|
||||
public uint Depth = 1;
|
||||
public uint Type = Gen5TextureType2D;
|
||||
public uint RowLength;
|
||||
public uint DstSelect;
|
||||
public uint Layers = 1;
|
||||
public uint MipLevel;
|
||||
public bool NeedsUpload;
|
||||
public bool OwnsStorage;
|
||||
public bool IsStorage;
|
||||
@@ -3214,9 +3246,12 @@ internal static unsafe class VulkanVideoPresenter
|
||||
public long FlipVersion;
|
||||
public uint Width;
|
||||
public uint Height;
|
||||
public uint Depth = 1;
|
||||
public uint Type = Gen5TextureType2D;
|
||||
// Unscaled guest-requested size; Width/Height are the physical (scaled) backing size.
|
||||
public uint LogicalWidth;
|
||||
public uint LogicalHeight;
|
||||
public uint LogicalDepth = 1;
|
||||
public uint MipLevels;
|
||||
public uint GuestFormat;
|
||||
public Format Format;
|
||||
@@ -5599,7 +5634,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
var rowLength = texture.TileMode == 0
|
||||
? Math.Max(texture.Pitch, width)
|
||||
: width;
|
||||
var byteCount = GetTextureByteCount(texture.Format, rowLength, height);
|
||||
var depth = GetGuestTextureDepth(texture.Type, texture.Depth);
|
||||
var byteCount = GetTextureByteCount(texture.Format, rowLength, height, depth);
|
||||
if (byteCount == 0 || byteCount > int.MaxValue)
|
||||
{
|
||||
return null;
|
||||
@@ -7461,6 +7497,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
View = view,
|
||||
Width = guestImage.Width,
|
||||
Height = guestImage.Height,
|
||||
Depth = guestImage.Depth,
|
||||
Type = guestImage.Type,
|
||||
RowLength = guestImage.Width,
|
||||
DstSelect = texture.DstSelect,
|
||||
SamplerState = texture.Sampler,
|
||||
@@ -7500,6 +7538,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
if (!guestImage.IsCpuBacked ||
|
||||
guestImage.Width != texture.Width ||
|
||||
guestImage.Height != texture.Height ||
|
||||
guestImage.Depth != GetGuestTextureDepth(texture.Type, texture.Depth) ||
|
||||
IsGuestTexture3D(guestImage.Type) != IsGuestTexture3D(texture.Type) ||
|
||||
guestImage.MipLevels != 1 ||
|
||||
texture.RgbaPixels.Length == 0)
|
||||
{
|
||||
@@ -7509,7 +7549,12 @@ internal static unsafe class VulkanVideoPresenter
|
||||
var rowLength = texture.TileMode == 0
|
||||
? Math.Max(texture.Pitch, texture.Width)
|
||||
: texture.Width;
|
||||
var expectedSize = GetTextureByteCount(texture.Format, rowLength, texture.Height);
|
||||
var depth = GetGuestTextureDepth(texture.Type, texture.Depth);
|
||||
var expectedSize = GetTextureByteCount(
|
||||
texture.Format,
|
||||
rowLength,
|
||||
texture.Height,
|
||||
depth);
|
||||
if (expectedSize == 0 || expectedSize > int.MaxValue)
|
||||
{
|
||||
return false;
|
||||
@@ -7556,6 +7601,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
View = view,
|
||||
Width = guestImage.Width,
|
||||
Height = guestImage.Height,
|
||||
Depth = guestImage.Depth,
|
||||
Type = guestImage.Type,
|
||||
RowLength = rowLength,
|
||||
DstSelect = texture.DstSelect,
|
||||
NeedsUpload = true,
|
||||
@@ -7665,6 +7712,12 @@ internal static unsafe class VulkanVideoPresenter
|
||||
GuestDrawTexture texture,
|
||||
out TextureResource resource)
|
||||
{
|
||||
if (IsGuestTexture3D(texture.Type))
|
||||
{
|
||||
resource = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var depth in _guestDepthImages.Values)
|
||||
{
|
||||
if (texture.Address != depth.Address &&
|
||||
@@ -7761,7 +7814,9 @@ internal static unsafe class VulkanVideoPresenter
|
||||
texture.Pitch,
|
||||
texture.Sampler,
|
||||
texture.ArrayedView,
|
||||
Math.Max(texture.ArrayLayers, 1));
|
||||
Math.Max(texture.ArrayLayers, 1),
|
||||
Type: texture.Type,
|
||||
Depth: GetGuestTextureDepth(texture.Type, texture.Depth));
|
||||
if (_textureCache.TryGetValue(key, out var cached))
|
||||
{
|
||||
return cached;
|
||||
@@ -7886,6 +7941,20 @@ internal static unsafe class VulkanVideoPresenter
|
||||
GuestDrawTexture texture,
|
||||
GuestImageResource guestImage)
|
||||
{
|
||||
var textureIs3D = IsGuestTexture3D(texture.Type);
|
||||
if (textureIs3D != IsGuestTexture3D(guestImage.Type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (textureIs3D)
|
||||
{
|
||||
return texture.Width == guestImage.LogicalWidth &&
|
||||
texture.Height == guestImage.LogicalHeight &&
|
||||
GetGuestTextureDepth(texture.Type, texture.Depth) ==
|
||||
guestImage.LogicalDepth;
|
||||
}
|
||||
|
||||
if (guestImage.LogicalWidth == texture.Width &&
|
||||
guestImage.LogicalHeight == texture.Height)
|
||||
{
|
||||
@@ -7921,6 +7990,9 @@ internal static unsafe class VulkanVideoPresenter
|
||||
$"format={texture.Format}/num={texture.NumberType}.");
|
||||
}
|
||||
var selectedMipLevel = GetStorageMipLevel(texture);
|
||||
var mipWidth = GetMipDimension(guestImage.Width, selectedMipLevel);
|
||||
var mipHeight = GetMipDimension(guestImage.Height, selectedMipLevel);
|
||||
var mipDepth = GetMipDimension(guestImage.Depth, selectedMipLevel);
|
||||
var view = GetOrCreateGuestImageIdentityView(
|
||||
guestImage,
|
||||
vkFormat,
|
||||
@@ -7931,10 +8003,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
Address = texture.Address,
|
||||
Image = guestImage.Image,
|
||||
View = view,
|
||||
Width = guestImage.Width,
|
||||
Height = guestImage.Height,
|
||||
RowLength = guestImage.Width,
|
||||
Width = mipWidth,
|
||||
Height = mipHeight,
|
||||
Depth = mipDepth,
|
||||
Type = guestImage.Type,
|
||||
RowLength = mipWidth,
|
||||
DstSelect = texture.DstSelect,
|
||||
MipLevel = selectedMipLevel,
|
||||
IsStorage = true,
|
||||
SamplerState = texture.Sampler,
|
||||
GuestImage = guestImage,
|
||||
@@ -7947,7 +8022,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
var expectedSize = GetTextureByteCount(
|
||||
texture.Format,
|
||||
texture.Width,
|
||||
texture.Height);
|
||||
texture.Height,
|
||||
GetGuestTextureDepth(texture.Type, texture.Depth));
|
||||
if ((ulong)texture.RgbaPixels.Length == expectedSize &&
|
||||
texture.RgbaPixels.AsSpan().IndexOfAnyExcept((byte)0) >= 0)
|
||||
{
|
||||
@@ -7998,6 +8074,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
var width = Math.Max(texture.Width, 1);
|
||||
var height = Math.Max(texture.Height, 1);
|
||||
var depth = GetGuestTextureDepth(texture.Type, texture.Depth);
|
||||
var vkFormat = GetStorageImageFormat(
|
||||
GetTextureFormat(texture.Format, texture.NumberType));
|
||||
if (!SupportsStorageImage(vkFormat))
|
||||
@@ -8009,9 +8086,9 @@ internal static unsafe class VulkanVideoPresenter
|
||||
var imageInfo = new ImageCreateInfo
|
||||
{
|
||||
SType = StructureType.ImageCreateInfo,
|
||||
ImageType = ImageType.Type2D,
|
||||
ImageType = GetGuestTextureImageType(texture.Type),
|
||||
Format = vkFormat,
|
||||
Extent = new Extent3D(width, height, 1),
|
||||
Extent = new Extent3D(width, height, depth),
|
||||
MipLevels = 1,
|
||||
ArrayLayers = 1,
|
||||
Samples = SampleCountFlags.Count1Bit,
|
||||
@@ -8047,7 +8124,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
SType = StructureType.ImageViewCreateInfo,
|
||||
Image = image,
|
||||
ViewType = ImageViewType.Type2D,
|
||||
ViewType = GetGuestTextureViewType(texture.Type),
|
||||
Format = vkFormat,
|
||||
Components = new ComponentMapping(
|
||||
ComponentSwizzle.Identity,
|
||||
@@ -8067,8 +8144,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
Address = 0,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Depth = depth,
|
||||
Type = texture.Type,
|
||||
LogicalWidth = width,
|
||||
LogicalHeight = height,
|
||||
LogicalDepth = depth,
|
||||
MipLevels = 1,
|
||||
GuestFormat = GetGuestTextureFormat(texture.Format, texture.NumberType),
|
||||
Format = vkFormat,
|
||||
@@ -8086,6 +8166,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
View = view,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Depth = depth,
|
||||
Type = texture.Type,
|
||||
RowLength = width,
|
||||
DstSelect = texture.DstSelect,
|
||||
OwnsStorage = true,
|
||||
@@ -8113,7 +8195,9 @@ internal static unsafe class VulkanVideoPresenter
|
||||
texture.NumberType,
|
||||
texture.ResourceMipLevels),
|
||||
format,
|
||||
requiresStorage: true);
|
||||
requiresStorage: true,
|
||||
texture.Type,
|
||||
GetGuestTextureDepth(texture.Type, texture.Depth));
|
||||
var selectedMipLevel = GetStorageMipLevel(texture);
|
||||
if (selectedMipLevel >= guestImage.MipLevels)
|
||||
{
|
||||
@@ -8146,21 +8230,29 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
var width = Math.Max(texture.Width, 1);
|
||||
var height = Math.Max(texture.Height, 1);
|
||||
var depth = GetGuestTextureDepth(texture.Type, texture.Depth);
|
||||
var rowLength = texture.TileMode == 0
|
||||
? Math.Max(texture.Pitch, width)
|
||||
: width;
|
||||
var vkFormat = GetTextureFormat(texture.Format, texture.NumberType);
|
||||
|
||||
var layers = Math.Max(texture.ArrayLayers, 1);
|
||||
var expectedSize = GetTextureByteCount(texture.Format, rowLength, height);
|
||||
var layers = IsGuestTexture3D(texture.Type)
|
||||
? 1u
|
||||
: Math.Max(texture.ArrayLayers, 1);
|
||||
var expectedSize = GetTextureByteCount(
|
||||
texture.Format,
|
||||
rowLength,
|
||||
height,
|
||||
depth);
|
||||
if (ShouldTraceVulkanResources() &&
|
||||
_tracedTextureUploads.Add((texture.Address, width, height, vkFormat)))
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][TRACE] vk.texture addr=0x{texture.Address:X16} " +
|
||||
$"fmt={texture.Format} num={texture.NumberType} vk={vkFormat} " +
|
||||
$"size={width}x{height} row={rowLength} tile={texture.TileMode} " +
|
||||
$"layers={layers} dst=0x{texture.DstSelect:X3} " +
|
||||
$"size={width}x{height}x{depth} type={texture.Type} " +
|
||||
$"row={rowLength} tile={texture.TileMode} layers={layers} " +
|
||||
$"dst=0x{texture.DstSelect:X3} " +
|
||||
$"bytes={texture.RgbaPixels.Length} expected={expectedSize}");
|
||||
}
|
||||
var pixels = texture.RgbaPixels.Length == (int)(expectedSize * layers)
|
||||
@@ -8189,26 +8281,32 @@ internal static unsafe class VulkanVideoPresenter
|
||||
uploadPixels,
|
||||
$"{TextureDebugName(texture, vkFormat)} staging");
|
||||
|
||||
var supportsAttachmentUsage = !IsBlockCompressedFormat(vkFormat);
|
||||
var supportsStorageUsage = supportsAttachmentUsage &&
|
||||
var supportsMutableUsage = !IsBlockCompressedFormat(vkFormat);
|
||||
var supportsAttachmentUsage =
|
||||
supportsMutableUsage &&
|
||||
!IsGuestTexture3D(texture.Type);
|
||||
var supportsStorageUsage =
|
||||
supportsMutableUsage &&
|
||||
SupportsStorageImage(vkFormat);
|
||||
var imageInfo = new ImageCreateInfo
|
||||
{
|
||||
SType = StructureType.ImageCreateInfo,
|
||||
Flags = supportsAttachmentUsage
|
||||
Flags = supportsMutableUsage
|
||||
? ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit
|
||||
: 0,
|
||||
ImageType = ImageType.Type2D,
|
||||
ImageType = GetGuestTextureImageType(texture.Type),
|
||||
Format = vkFormat,
|
||||
Extent = new Extent3D(width, height, 1),
|
||||
Extent = new Extent3D(width, height, depth),
|
||||
MipLevels = 1,
|
||||
ArrayLayers = layers,
|
||||
Samples = SampleCountFlags.Count1Bit,
|
||||
Tiling = ImageTiling.Optimal,
|
||||
Usage = supportsAttachmentUsage
|
||||
Usage = supportsMutableUsage
|
||||
? ImageUsageFlags.TransferDstBit |
|
||||
ImageUsageFlags.SampledBit |
|
||||
ImageUsageFlags.ColorAttachmentBit |
|
||||
(supportsAttachmentUsage
|
||||
? ImageUsageFlags.ColorAttachmentBit
|
||||
: (ImageUsageFlags)0) |
|
||||
(supportsStorageUsage ? ImageUsageFlags.StorageBit : (ImageUsageFlags)0) |
|
||||
ImageUsageFlags.TransferSrcBit
|
||||
: ImageUsageFlags.TransferDstBit | ImageUsageFlags.SampledBit,
|
||||
@@ -8232,9 +8330,9 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
SType = StructureType.ImageViewCreateInfo,
|
||||
Image = image,
|
||||
ViewType = texture.ArrayedView
|
||||
? ImageViewType.Type2DArray
|
||||
: ImageViewType.Type2D,
|
||||
ViewType = GetGuestTextureViewType(
|
||||
texture.Type,
|
||||
texture.ArrayedView),
|
||||
Format = vkFormat,
|
||||
Components = ToVkComponentMapping(texture.DstSelect),
|
||||
SubresourceRange = ColorSubresourceRange(layerCount: layers),
|
||||
@@ -8253,6 +8351,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
View = view,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Depth = depth,
|
||||
Type = texture.Type,
|
||||
RowLength = rowLength,
|
||||
DstSelect = texture.DstSelect,
|
||||
Layers = layers,
|
||||
@@ -8275,8 +8375,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
Address = texture.Address,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Depth = depth,
|
||||
Type = texture.Type,
|
||||
LogicalWidth = width,
|
||||
LogicalHeight = height,
|
||||
LogicalDepth = depth,
|
||||
MipLevels = 1,
|
||||
GuestFormat = guestFormat,
|
||||
Format = vkFormat,
|
||||
@@ -9731,7 +9834,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
if (format is 9 or 10)
|
||||
{
|
||||
return CreateBlackFrame(width, height);
|
||||
var pixels = new byte[checked((int)expectedSize)];
|
||||
for (var offset = 3; offset < pixels.Length; offset += 4)
|
||||
{
|
||||
pixels[offset] = 0xFF;
|
||||
}
|
||||
|
||||
return pixels;
|
||||
}
|
||||
|
||||
return new byte[checked((int)expectedSize)];
|
||||
@@ -9762,6 +9871,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
private static ulong GetTextureByteCount(uint format, uint width, uint height)
|
||||
=> GetGuestImageByteCount(format, width, height);
|
||||
|
||||
private static ulong GetTextureByteCount(
|
||||
uint format,
|
||||
uint width,
|
||||
uint height,
|
||||
uint depth) =>
|
||||
GetGuestImageByteCount(format, width, height, depth);
|
||||
|
||||
private static ulong GetVulkanImageByteCount(Format format, uint width, uint height)
|
||||
{
|
||||
var blockBytes = format switch
|
||||
@@ -9808,6 +9924,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
: checked((ulong)width * height * bitsPerTexel / 8);
|
||||
}
|
||||
|
||||
private static ulong GetVulkanImageByteCount(
|
||||
Format format,
|
||||
uint width,
|
||||
uint height,
|
||||
uint depth) =>
|
||||
checked(GetVulkanImageByteCount(format, width, height) * Math.Max(depth, 1u));
|
||||
|
||||
private bool SupportsColorAttachment(Format format)
|
||||
{
|
||||
_vk.GetPhysicalDeviceFormatProperties(_physicalDevice, format, out var properties);
|
||||
@@ -11786,7 +11909,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
return 0;
|
||||
}
|
||||
|
||||
var texelCount = (ulong)target.Width * target.Height;
|
||||
var rowsPerVolume = checked((ulong)target.Height * target.Depth);
|
||||
var texelCount = checked((ulong)target.Width * rowsPerVolume);
|
||||
if (texelCount == 0 || expectedByteCount % texelCount != 0)
|
||||
{
|
||||
// Block-compressed or otherwise non-linear: no per-texel pitch.
|
||||
@@ -11794,12 +11918,12 @@ internal static unsafe class VulkanVideoPresenter
|
||||
}
|
||||
|
||||
var bytesPerTexel = expectedByteCount / texelCount;
|
||||
if (bytesPerTexel == 0 || uploadByteCount % target.Height != 0)
|
||||
if (bytesPerTexel == 0 || uploadByteCount % rowsPerVolume != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var rowBytes = uploadByteCount / target.Height;
|
||||
var rowBytes = uploadByteCount / rowsPerVolume;
|
||||
if (rowBytes % bytesPerTexel != 0)
|
||||
{
|
||||
return 0;
|
||||
@@ -11836,7 +11960,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
var expectedByteCount = GetVulkanImageByteCount(
|
||||
target.Format,
|
||||
target.Width,
|
||||
target.Height);
|
||||
target.Height,
|
||||
target.Depth);
|
||||
|
||||
// The guest can hand us linear pixel data whose rows are padded out
|
||||
// to a hardware pitch wider than the image, so the byte count runs
|
||||
@@ -11931,7 +12056,10 @@ internal static unsafe class VulkanVideoPresenter
|
||||
0,
|
||||
1),
|
||||
ImageOffset = default,
|
||||
ImageExtent = new Extent3D(target.Width, target.Height, 1),
|
||||
ImageExtent = new Extent3D(
|
||||
target.Width,
|
||||
target.Height,
|
||||
target.Depth),
|
||||
};
|
||||
_vk.CmdCopyBufferToImage(
|
||||
commandBuffer,
|
||||
@@ -11993,8 +12121,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
private GuestImageResource GetOrCreateGuestImage(
|
||||
GuestRenderTarget target,
|
||||
Format format,
|
||||
bool requiresStorage = false)
|
||||
bool requiresStorage = false,
|
||||
uint type = Gen5TextureType2D,
|
||||
uint depth = 1)
|
||||
{
|
||||
depth = GetGuestTextureDepth(type, depth);
|
||||
var supportsStorageUsage = SupportsStorageImage(format);
|
||||
if (requiresStorage && !supportsStorageUsage)
|
||||
{
|
||||
@@ -12010,12 +12141,19 @@ internal static unsafe class VulkanVideoPresenter
|
||||
var physicalHeight = requiresStorage
|
||||
? target.Height
|
||||
: ScaleGuestDimension(target.Height);
|
||||
var mipLevels = ClampMipLevels(physicalWidth, physicalHeight, target.MipLevels);
|
||||
var physicalDepth = depth;
|
||||
var mipLevels = ClampMipLevels(
|
||||
physicalWidth,
|
||||
physicalHeight,
|
||||
physicalDepth,
|
||||
target.MipLevels);
|
||||
var guestFormat = GetGuestTextureFormat(target.Format, target.NumberType);
|
||||
var requestedKey = new GuestImageVariantKey(
|
||||
target.Address,
|
||||
target.Width,
|
||||
target.Height,
|
||||
depth,
|
||||
type,
|
||||
mipLevels,
|
||||
guestFormat,
|
||||
format);
|
||||
@@ -12023,6 +12161,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
if (existing.LogicalWidth == target.Width &&
|
||||
existing.LogicalHeight == target.Height &&
|
||||
existing.LogicalDepth == depth &&
|
||||
existing.Type == type &&
|
||||
existing.MipLevels == mipLevels &&
|
||||
existing.GuestFormat == guestFormat &&
|
||||
existing.Format == format)
|
||||
@@ -12035,7 +12175,9 @@ internal static unsafe class VulkanVideoPresenter
|
||||
|
||||
existing.IsCpuBacked = false;
|
||||
existing.CpuContentFingerprint = 0;
|
||||
if (existing.RenderPass.Handle == 0 && !requiresStorage)
|
||||
if (existing.RenderPass.Handle == 0 &&
|
||||
!requiresStorage &&
|
||||
!IsGuestTexture3D(type))
|
||||
{
|
||||
var attachmentView = existing.MipViews.Length > 0
|
||||
? existing.MipViews[0]
|
||||
@@ -12074,9 +12216,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
existing.Address,
|
||||
existing.LogicalWidth,
|
||||
existing.LogicalHeight,
|
||||
existing.LogicalDepth,
|
||||
existing.Type,
|
||||
existing.MipLevels,
|
||||
existing.GuestFormat,
|
||||
existing.Format),
|
||||
existing.GuestFormat,
|
||||
existing.Format),
|
||||
existing);
|
||||
_guestImages.Remove(target.Address);
|
||||
lock (_gate)
|
||||
@@ -12111,14 +12255,19 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_guestImageExtents[target.Address] = (
|
||||
target.Width,
|
||||
target.Height,
|
||||
GetTextureByteCount(target.Format, target.Width, target.Height));
|
||||
GetTextureByteCount(
|
||||
target.Format,
|
||||
target.Width,
|
||||
target.Height,
|
||||
depth));
|
||||
}
|
||||
|
||||
if (target.Width <= 1920 && target.Height <= 1080)
|
||||
{
|
||||
SharpEmu.HLE.GuestImageWriteTracker.Track(
|
||||
target.Address,
|
||||
(ulong)target.Width * target.Height * GetTextureBytesPerPixel(target.Format),
|
||||
(ulong)target.Width * target.Height * depth *
|
||||
GetTextureBytesPerPixel(target.Format),
|
||||
CurrentGuestWorkSequenceForDiagnostics,
|
||||
"vulkan.render-target");
|
||||
}
|
||||
@@ -12140,15 +12289,17 @@ internal static unsafe class VulkanVideoPresenter
|
||||
Flags =
|
||||
ImageCreateFlags.CreateMutableFormatBit |
|
||||
ImageCreateFlags.CreateExtendedUsageBit,
|
||||
ImageType = ImageType.Type2D,
|
||||
ImageType = GetGuestTextureImageType(type),
|
||||
Format = format,
|
||||
Extent = new Extent3D(physicalWidth, physicalHeight, 1),
|
||||
Extent = new Extent3D(physicalWidth, physicalHeight, physicalDepth),
|
||||
MipLevels = mipLevels,
|
||||
ArrayLayers = 1,
|
||||
Samples = SampleCountFlags.Count1Bit,
|
||||
Tiling = ImageTiling.Optimal,
|
||||
Usage =
|
||||
ImageUsageFlags.ColorAttachmentBit |
|
||||
(IsGuestTexture3D(type)
|
||||
? (ImageUsageFlags)0
|
||||
: ImageUsageFlags.ColorAttachmentBit) |
|
||||
ImageUsageFlags.SampledBit |
|
||||
(supportsStorageUsage ? ImageUsageFlags.StorageBit : (ImageUsageFlags)0) |
|
||||
ImageUsageFlags.TransferSrcBit |
|
||||
@@ -12178,7 +12329,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
SType = StructureType.ImageViewCreateInfo,
|
||||
Image = image,
|
||||
ViewType = ImageViewType.Type2D,
|
||||
ViewType = GetGuestTextureViewType(type),
|
||||
Format = format,
|
||||
Components = new ComponentMapping(
|
||||
ComponentSwizzle.Identity,
|
||||
@@ -12209,7 +12360,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
RenderPass renderPass = default;
|
||||
RenderPass initialRenderPass = default;
|
||||
Framebuffer framebuffer = default;
|
||||
if (!requiresStorage)
|
||||
if (!requiresStorage && !IsGuestTexture3D(type))
|
||||
{
|
||||
(renderPass, initialRenderPass, framebuffer) =
|
||||
CreateRenderPassAndFramebuffer(
|
||||
@@ -12224,8 +12375,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
Address = target.Address,
|
||||
Width = physicalWidth,
|
||||
Height = physicalHeight,
|
||||
Depth = physicalDepth,
|
||||
Type = type,
|
||||
LogicalWidth = target.Width,
|
||||
LogicalHeight = target.Height,
|
||||
LogicalDepth = depth,
|
||||
MipLevels = mipLevels,
|
||||
GuestFormat = guestFormat,
|
||||
Format = format,
|
||||
@@ -12260,14 +12414,19 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_guestImageExtents[target.Address] = (
|
||||
target.Width,
|
||||
target.Height,
|
||||
GetTextureByteCount(target.Format, target.Width, target.Height));
|
||||
GetTextureByteCount(
|
||||
target.Format,
|
||||
target.Width,
|
||||
target.Height,
|
||||
depth));
|
||||
}
|
||||
|
||||
if (target.Width <= 1920 && target.Height <= 1080)
|
||||
{
|
||||
SharpEmu.HLE.GuestImageWriteTracker.Track(
|
||||
target.Address,
|
||||
(ulong)target.Width * target.Height * GetTextureBytesPerPixel(target.Format),
|
||||
(ulong)target.Width * target.Height * depth *
|
||||
GetTextureBytesPerPixel(target.Format),
|
||||
CurrentGuestWorkSequenceForDiagnostics,
|
||||
"vulkan.render-target");
|
||||
}
|
||||
@@ -12740,9 +12899,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
return renderPass;
|
||||
}
|
||||
|
||||
private static uint ClampMipLevels(uint width, uint height, uint requestedMipLevels)
|
||||
private static uint ClampMipLevels(
|
||||
uint width,
|
||||
uint height,
|
||||
uint depth,
|
||||
uint requestedMipLevels)
|
||||
{
|
||||
var largestDimension = Math.Max(width, height);
|
||||
var largestDimension = Math.Max(Math.Max(width, height), depth);
|
||||
uint maximumMipLevels = 1;
|
||||
while (largestDimension > 1)
|
||||
{
|
||||
@@ -12753,6 +12916,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
return Math.Min(Math.Max(requestedMipLevels, 1u), maximumMipLevels);
|
||||
}
|
||||
|
||||
private static uint GetMipDimension(uint dimension, uint mipLevel) =>
|
||||
mipLevel >= 32
|
||||
? 1
|
||||
: Math.Max(dimension >> (int)mipLevel, 1u);
|
||||
|
||||
private void DestroyGuestImage(GuestImageResource resource)
|
||||
{
|
||||
foreach (var depthFramebuffer in resource.DepthFramebuffers.Values)
|
||||
@@ -12929,7 +13097,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
SType = StructureType.ImageViewCreateInfo,
|
||||
Image = resource.Image,
|
||||
ViewType = arrayedView ? ImageViewType.Type2DArray : ImageViewType.Type2D,
|
||||
ViewType = GetGuestTextureViewType(resource.Type, arrayedView),
|
||||
Format = format,
|
||||
Components = ToVkComponentMapping(dstSelect),
|
||||
SubresourceRange = ColorSubresourceRange(mipLevel, levelCount),
|
||||
@@ -14060,7 +14228,10 @@ internal static unsafe class VulkanVideoPresenter
|
||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
Image = texture.Image,
|
||||
SubresourceRange = ColorSubresourceRange(layerCount: texture.Layers),
|
||||
SubresourceRange = ColorSubresourceRange(
|
||||
texture.MipLevel,
|
||||
1,
|
||||
texture.Layers),
|
||||
};
|
||||
_vk.CmdPipelineBarrier(
|
||||
_commandBuffer,
|
||||
@@ -14084,9 +14255,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
ImageSubresource = new ImageSubresourceLayers
|
||||
{
|
||||
AspectMask = ImageAspectFlags.ColorBit,
|
||||
MipLevel = texture.MipLevel,
|
||||
LayerCount = texture.Layers,
|
||||
},
|
||||
ImageExtent = new Extent3D(texture.Width, texture.Height, 1),
|
||||
ImageExtent = new Extent3D(
|
||||
texture.Width,
|
||||
texture.Height,
|
||||
texture.Depth),
|
||||
};
|
||||
_vk.CmdCopyBufferToImage(
|
||||
_commandBuffer,
|
||||
@@ -14106,7 +14281,10 @@ internal static unsafe class VulkanVideoPresenter
|
||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
Image = texture.Image,
|
||||
SubresourceRange = ColorSubresourceRange(layerCount: texture.Layers),
|
||||
SubresourceRange = ColorSubresourceRange(
|
||||
texture.MipLevel,
|
||||
1,
|
||||
texture.Layers),
|
||||
};
|
||||
_vk.CmdPipelineBarrier(
|
||||
_commandBuffer,
|
||||
|
||||
@@ -314,7 +314,8 @@ public static partial class Gen5SpirvTranslator
|
||||
uint VectorType,
|
||||
ImageComponentKind ComponentKind,
|
||||
bool IsStorage,
|
||||
bool Arrayed);
|
||||
bool Arrayed,
|
||||
SpirvImageDim Dimension);
|
||||
|
||||
private readonly record struct SpirvVertexInput(
|
||||
uint Variable,
|
||||
@@ -1001,11 +1002,15 @@ public static partial class Gen5SpirvTranslator
|
||||
SpirvCapability.StorageImageExtendedFormats);
|
||||
}
|
||||
|
||||
var isArrayed = !isStorage &&
|
||||
var dimension = binding.Control.Dimension == 2
|
||||
? SpirvImageDim.Dim3D
|
||||
: SpirvImageDim.Dim2D;
|
||||
var isArrayed = dimension != SpirvImageDim.Dim3D &&
|
||||
!isStorage &&
|
||||
Gen5ShaderTranslator.IsArrayedImageBinding(binding);
|
||||
var imageType = _module.TypeImage(
|
||||
componentType,
|
||||
SpirvImageDim.Dim2D,
|
||||
dimension,
|
||||
depth: false,
|
||||
arrayed: isArrayed,
|
||||
multisampled: false,
|
||||
@@ -1035,7 +1040,8 @@ public static partial class Gen5SpirvTranslator
|
||||
_module.TypeVector(componentType, 4),
|
||||
componentKind,
|
||||
isStorage,
|
||||
isArrayed));
|
||||
isArrayed,
|
||||
dimension));
|
||||
_interfaces.Add(variable);
|
||||
}
|
||||
}
|
||||
@@ -2259,20 +2265,39 @@ public static partial class Gen5SpirvTranslator
|
||||
return false;
|
||||
}
|
||||
|
||||
var memoryOpcode = control.UsesFlatAddress
|
||||
? "Global" + instruction.Opcode["Flat".Length..]
|
||||
: instruction.Opcode;
|
||||
var vectorByteAddress = LoadV(control.VectorAddress);
|
||||
if (control.UsesFlatAddress)
|
||||
{
|
||||
// FLAT instructions carry the complete 64-bit guest address
|
||||
// in a VGPR pair. The scalar evaluator captures the buffer
|
||||
// rooted at the inferred SGPR pair, so convert the low address
|
||||
// dword back to a byte offset inside that binding. Subtraction
|
||||
// modulo 2^32 also handles an address addition that carried
|
||||
// into the high dword, while the bounded binding prevents an
|
||||
// unrelated pointer from escaping into host storage.
|
||||
vectorByteAddress = _module.AddInstruction(
|
||||
SpirvOp.ISub,
|
||||
_uintType,
|
||||
vectorByteAddress,
|
||||
LoadS(control.ScalarAddress));
|
||||
}
|
||||
var byteAddress = IAdd(
|
||||
LoadV(control.VectorAddress),
|
||||
vectorByteAddress,
|
||||
UInt(unchecked((uint)control.OffsetBytes)));
|
||||
byteAddress = ApplyGuestBufferByteBias(bindingIndex, byteAddress);
|
||||
var dwordAddress = ShiftRightLogical(byteAddress, UInt(2));
|
||||
|
||||
if (instruction.Opcode is "GlobalAtomicAdd" or "GlobalAtomicUMax")
|
||||
if (memoryOpcode is "GlobalAtomicAdd" or "GlobalAtomicUMax")
|
||||
{
|
||||
EmitExecConditional(() =>
|
||||
{
|
||||
EmitConditional(IsBufferWordInRange(bindingIndex, dwordAddress), () =>
|
||||
{
|
||||
var original = _module.AddInstruction(
|
||||
instruction.Opcode == "GlobalAtomicAdd"
|
||||
memoryOpcode == "GlobalAtomicAdd"
|
||||
? SpirvOp.AtomicIAdd
|
||||
: SpirvOp.AtomicUMax,
|
||||
_uintType,
|
||||
@@ -2289,12 +2314,12 @@ public static partial class Gen5SpirvTranslator
|
||||
return true;
|
||||
}
|
||||
|
||||
if (instruction.Opcode.StartsWith("GlobalStore", StringComparison.Ordinal))
|
||||
if (memoryOpcode.StartsWith("GlobalStore", StringComparison.Ordinal))
|
||||
{
|
||||
EmitExecConditional(() =>
|
||||
{
|
||||
if (TryGetSubdwordStoreInfo(
|
||||
instruction.Opcode,
|
||||
memoryOpcode,
|
||||
out var byteCount,
|
||||
out var sourceShift))
|
||||
{
|
||||
@@ -2324,7 +2349,7 @@ public static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
if (TryGetSubdwordLoadInfo(
|
||||
instruction.Opcode,
|
||||
memoryOpcode,
|
||||
out var loadByteCount,
|
||||
out var signExtend,
|
||||
out var d16,
|
||||
@@ -3268,6 +3293,7 @@ public static partial class Gen5SpirvTranslator
|
||||
var imageObject = Load(resource.ObjectType, resource.Variable);
|
||||
if (instruction.Opcode == "ImageGetResinfo")
|
||||
{
|
||||
var sizeComponentCount = ImageCoordinateComponentCount(resource);
|
||||
var queryImage = resource.IsStorage
|
||||
? imageObject
|
||||
: _module.AddInstruction(
|
||||
@@ -3278,7 +3304,7 @@ public static partial class Gen5SpirvTranslator
|
||||
resource.IsStorage
|
||||
? SpirvOp.ImageQuerySize
|
||||
: SpirvOp.ImageQuerySizeLod,
|
||||
_module.TypeVector(_intType, 2),
|
||||
_module.TypeVector(_intType, sizeComponentCount),
|
||||
resource.IsStorage
|
||||
? [queryImage]
|
||||
: [queryImage, UInt(0)]);
|
||||
@@ -3291,7 +3317,7 @@ public static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
uint value;
|
||||
if (component < 2)
|
||||
if (component < sizeComponentCount)
|
||||
{
|
||||
var signedValue = _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
@@ -3319,7 +3345,12 @@ public static partial class Gen5SpirvTranslator
|
||||
return false;
|
||||
}
|
||||
|
||||
var coordinates = BuildIntegerCoordinates(image, 0);
|
||||
var coordinateComponentCount =
|
||||
ImageCoordinateComponentCount(resource);
|
||||
var coordinates = BuildIntegerCoordinates(
|
||||
image,
|
||||
0,
|
||||
coordinateComponentCount);
|
||||
var components = new uint[4];
|
||||
uint sourceIndex = 0;
|
||||
for (var component = 0; component < components.Length; component++)
|
||||
@@ -3355,13 +3386,14 @@ public static partial class Gen5SpirvTranslator
|
||||
components);
|
||||
var imageSize = _module.AddInstruction(
|
||||
SpirvOp.ImageQuerySize,
|
||||
_module.TypeVector(_intType, 2),
|
||||
_module.TypeVector(_intType, coordinateComponentCount),
|
||||
imageObject);
|
||||
EmitBoundsCheckedImageWrite(
|
||||
coordinates,
|
||||
imageSize,
|
||||
imageObject,
|
||||
texel);
|
||||
texel,
|
||||
coordinateComponentCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -3382,14 +3414,17 @@ public static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
var signed = resource.ComponentKind == ImageComponentKind.Sint;
|
||||
var coordinateComponentCount =
|
||||
ImageCoordinateComponentCount(resource);
|
||||
var atomicImageSize = _module.AddInstruction(
|
||||
SpirvOp.ImageQuerySize,
|
||||
_module.TypeVector(_intType, 2),
|
||||
_module.TypeVector(_intType, coordinateComponentCount),
|
||||
imageObject);
|
||||
var coordinates = BuildClampedIntegerCoordinates(
|
||||
image,
|
||||
0,
|
||||
atomicImageSize);
|
||||
atomicImageSize,
|
||||
coordinateComponentCount);
|
||||
EmitExecConditional(() =>
|
||||
{
|
||||
var pointer = _module.AddInstruction(
|
||||
@@ -3433,14 +3468,17 @@ public static partial class Gen5SpirvTranslator
|
||||
{
|
||||
if (resource.IsStorage)
|
||||
{
|
||||
var coordinateComponentCount =
|
||||
ImageCoordinateComponentCount(resource);
|
||||
var imageSize = _module.AddInstruction(
|
||||
SpirvOp.ImageQuerySize,
|
||||
_module.TypeVector(_intType, 2),
|
||||
_module.TypeVector(_intType, coordinateComponentCount),
|
||||
imageObject);
|
||||
var coordinates = BuildClampedIntegerCoordinates(
|
||||
image,
|
||||
0,
|
||||
imageSize);
|
||||
imageSize,
|
||||
coordinateComponentCount);
|
||||
sampled = _module.AddInstruction(
|
||||
SpirvOp.ImageRead,
|
||||
resource.VectorType,
|
||||
@@ -3454,15 +3492,18 @@ public static partial class Gen5SpirvTranslator
|
||||
SpirvOp.Image,
|
||||
resource.ImageType,
|
||||
imageObject);
|
||||
var coordinateComponentCount =
|
||||
ImageCoordinateComponentCount(resource);
|
||||
var imageSize = _module.AddInstruction(
|
||||
SpirvOp.ImageQuerySizeLod,
|
||||
_module.TypeVector(_intType, 2),
|
||||
_module.TypeVector(_intType, coordinateComponentCount),
|
||||
fetchedImage,
|
||||
UInt(mipLevel));
|
||||
var coordinates = BuildClampedIntegerCoordinates(
|
||||
image,
|
||||
0,
|
||||
imageSize);
|
||||
imageSize,
|
||||
coordinateComponentCount);
|
||||
sampled = _module.AddInstruction(
|
||||
SpirvOp.ImageFetch,
|
||||
resource.VectorType,
|
||||
@@ -3494,12 +3535,19 @@ public static partial class Gen5SpirvTranslator
|
||||
// lowering treated SAMPLE_D as body-first and consequently
|
||||
// sampled gradients as coordinates in every captured
|
||||
// derivative operation.
|
||||
var spatialComponentCount =
|
||||
ImageSpatialComponentCount(resource);
|
||||
var coordinateComponentCount =
|
||||
ImageCoordinateComponentCount(resource);
|
||||
var addressCursor = 0;
|
||||
var offset = 0u;
|
||||
if (hasOffset)
|
||||
{
|
||||
addressCursor = AlignFullImageAddress(image, addressCursor);
|
||||
offset = BuildImageOffset(image, addressCursor);
|
||||
offset = BuildImageOffset(
|
||||
image,
|
||||
addressCursor,
|
||||
spatialComponentCount);
|
||||
addressCursor += ImageFullAddressSlots(image);
|
||||
}
|
||||
|
||||
@@ -3523,26 +3571,33 @@ public static partial class Gen5SpirvTranslator
|
||||
}
|
||||
|
||||
var gradientX = hasGradients
|
||||
? BuildFloatCoordinates(image, addressCursor)
|
||||
? BuildFloatCoordinates(
|
||||
image,
|
||||
addressCursor,
|
||||
spatialComponentCount)
|
||||
: 0u;
|
||||
var gradientY = hasGradients
|
||||
? BuildFloatCoordinates(image, addressCursor + 2)
|
||||
? BuildFloatCoordinates(
|
||||
image,
|
||||
addressCursor + (int)spatialComponentCount,
|
||||
spatialComponentCount)
|
||||
: 0u;
|
||||
if (hasGradients)
|
||||
{
|
||||
addressCursor += 4;
|
||||
addressCursor += checked((int)(spatialComponentCount * 2));
|
||||
}
|
||||
|
||||
var coordinates = resource.Arrayed
|
||||
? BuildFloatArrayCoordinates(image, addressCursor)
|
||||
: BuildFloatCoordinates(image, addressCursor);
|
||||
var coordinates = BuildFloatCoordinates(
|
||||
image,
|
||||
addressCursor,
|
||||
coordinateComponentCount);
|
||||
var explicitLod = hasGradients || hasZeroLod || hasLod;
|
||||
var lod = hasZeroLod
|
||||
? Float(0)
|
||||
: hasLod
|
||||
? LoadImageFloatAddress(
|
||||
image,
|
||||
addressCursor + (resource.Arrayed ? 3 : 2))
|
||||
addressCursor + (int)coordinateComponentCount)
|
||||
: lodOrBias;
|
||||
if (hasOffset)
|
||||
{
|
||||
@@ -3608,11 +3663,18 @@ public static partial class Gen5SpirvTranslator
|
||||
instruction.Opcode.EndsWith("O", StringComparison.Ordinal);
|
||||
var hasCompare =
|
||||
instruction.Opcode.Contains("Gather4C", StringComparison.Ordinal);
|
||||
var spatialComponentCount =
|
||||
ImageSpatialComponentCount(resource);
|
||||
var coordinateComponentCount =
|
||||
ImageCoordinateComponentCount(resource);
|
||||
var addressCursor = 0;
|
||||
var offset = 0u;
|
||||
if (hasOffset)
|
||||
{
|
||||
offset = BuildImageOffset(image, addressCursor);
|
||||
offset = BuildImageOffset(
|
||||
image,
|
||||
addressCursor,
|
||||
spatialComponentCount);
|
||||
addressCursor += ImageFullAddressSlots(image);
|
||||
}
|
||||
|
||||
@@ -3627,9 +3689,10 @@ public static partial class Gen5SpirvTranslator
|
||||
addressCursor += ImageFullAddressSlots(image);
|
||||
}
|
||||
|
||||
var coordinates = resource.Arrayed
|
||||
? BuildFloatArrayCoordinates(image, addressCursor)
|
||||
: BuildFloatCoordinates(image, addressCursor);
|
||||
var coordinates = BuildFloatCoordinates(
|
||||
image,
|
||||
addressCursor,
|
||||
coordinateComponentCount);
|
||||
var operands = new List<uint>
|
||||
{
|
||||
imageObject,
|
||||
@@ -3822,28 +3885,31 @@ public static partial class Gen5SpirvTranslator
|
||||
});
|
||||
}
|
||||
|
||||
private uint BuildFloatCoordinates(Gen5ImageControl image, int start)
|
||||
{
|
||||
var x = LoadImageFloatAddress(image, start);
|
||||
var y = LoadImageFloatAddress(image, start + 1);
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.CompositeConstruct,
|
||||
_vec2Type,
|
||||
x,
|
||||
y);
|
||||
}
|
||||
private static uint ImageSpatialComponentCount(
|
||||
SpirvImageResource resource) =>
|
||||
resource.Dimension == SpirvImageDim.Dim3D ? 3u : 2u;
|
||||
|
||||
private uint BuildFloatArrayCoordinates(Gen5ImageControl image, int start)
|
||||
private static uint ImageCoordinateComponentCount(
|
||||
SpirvImageResource resource) =>
|
||||
resource.Arrayed ? 3u : ImageSpatialComponentCount(resource);
|
||||
|
||||
private uint BuildFloatCoordinates(
|
||||
Gen5ImageControl image,
|
||||
int start,
|
||||
uint componentCount)
|
||||
{
|
||||
var x = LoadImageFloatAddress(image, start);
|
||||
var y = LoadImageFloatAddress(image, start + 1);
|
||||
var slice = LoadImageFloatAddress(image, start + 2);
|
||||
var components = new uint[checked((int)componentCount)];
|
||||
for (var component = 0; component < components.Length; component++)
|
||||
{
|
||||
components[component] = LoadImageFloatAddress(
|
||||
image,
|
||||
start + component);
|
||||
}
|
||||
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.CompositeConstruct,
|
||||
_vec3Type,
|
||||
x,
|
||||
y,
|
||||
slice);
|
||||
_module.TypeVector(_floatType, componentCount),
|
||||
components);
|
||||
}
|
||||
|
||||
private static int ImageAddressRegister(
|
||||
@@ -3948,47 +4014,49 @@ public static partial class Gen5SpirvTranslator
|
||||
ShiftLeftLogical(BitwiseAnd(high, UInt(0xFFFF)), UInt(16)));
|
||||
}
|
||||
|
||||
private uint BuildIntegerCoordinates(Gen5ImageControl image, int start)
|
||||
private uint BuildIntegerCoordinates(
|
||||
Gen5ImageControl image,
|
||||
int start,
|
||||
uint componentCount)
|
||||
{
|
||||
var ivec2 = _module.TypeVector(_intType, 2);
|
||||
var x = Bitcast(_intType, LoadImageIntegerAddress(image, start));
|
||||
var y = Bitcast(_intType, LoadImageIntegerAddress(image, start + 1));
|
||||
var components = new uint[checked((int)componentCount)];
|
||||
for (var component = 0; component < components.Length; component++)
|
||||
{
|
||||
components[component] = Bitcast(
|
||||
_intType,
|
||||
LoadImageIntegerAddress(image, start + component));
|
||||
}
|
||||
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.CompositeConstruct,
|
||||
ivec2,
|
||||
x,
|
||||
y);
|
||||
_module.TypeVector(_intType, componentCount),
|
||||
components);
|
||||
}
|
||||
|
||||
private uint BuildClampedIntegerCoordinates(
|
||||
Gen5ImageControl image,
|
||||
int start,
|
||||
uint imageSize)
|
||||
uint imageSize,
|
||||
uint componentCount)
|
||||
{
|
||||
var ivec2 = _module.TypeVector(_intType, 2);
|
||||
var x = ClampSignedCoordinate(
|
||||
Bitcast(
|
||||
_intType,
|
||||
LoadImageIntegerAddress(image, start)),
|
||||
_module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_intType,
|
||||
imageSize,
|
||||
0));
|
||||
var y = ClampSignedCoordinate(
|
||||
Bitcast(
|
||||
_intType,
|
||||
LoadImageIntegerAddress(image, start + 1)),
|
||||
_module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_intType,
|
||||
imageSize,
|
||||
1));
|
||||
var components = new uint[checked((int)componentCount)];
|
||||
for (var component = 0; component < components.Length; component++)
|
||||
{
|
||||
components[component] = ClampSignedCoordinate(
|
||||
Bitcast(
|
||||
_intType,
|
||||
LoadImageIntegerAddress(image, start + component)),
|
||||
_module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_intType,
|
||||
imageSize,
|
||||
(uint)component));
|
||||
}
|
||||
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.CompositeConstruct,
|
||||
ivec2,
|
||||
x,
|
||||
y);
|
||||
_module.TypeVector(_intType, componentCount),
|
||||
components);
|
||||
}
|
||||
|
||||
private uint ClampSignedCoordinate(uint value, uint extent)
|
||||
@@ -4027,69 +4095,47 @@ public static partial class Gen5SpirvTranslator
|
||||
uint coordinates,
|
||||
uint imageSize,
|
||||
uint imageObject,
|
||||
uint texel)
|
||||
uint texel,
|
||||
uint coordinateComponentCount)
|
||||
{
|
||||
var x = _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_intType,
|
||||
coordinates,
|
||||
0);
|
||||
var y = _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_intType,
|
||||
coordinates,
|
||||
1);
|
||||
var width = _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_intType,
|
||||
imageSize,
|
||||
0);
|
||||
var height = _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_intType,
|
||||
imageSize,
|
||||
1);
|
||||
var zero = _module.Constant(_intType, 0);
|
||||
var xNonNegative = _module.AddInstruction(
|
||||
SpirvOp.SGreaterThanEqual,
|
||||
_boolType,
|
||||
x,
|
||||
zero);
|
||||
var yNonNegative = _module.AddInstruction(
|
||||
SpirvOp.SGreaterThanEqual,
|
||||
_boolType,
|
||||
y,
|
||||
zero);
|
||||
var xInRange = _module.AddInstruction(
|
||||
SpirvOp.SLessThan,
|
||||
_boolType,
|
||||
x,
|
||||
width);
|
||||
var yInRange = _module.AddInstruction(
|
||||
SpirvOp.SLessThan,
|
||||
_boolType,
|
||||
y,
|
||||
height);
|
||||
var lowerInRange = _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
xNonNegative,
|
||||
yNonNegative);
|
||||
var upperInRange = _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
xInRange,
|
||||
yInRange);
|
||||
var inRange = _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
lowerInRange,
|
||||
upperInRange);
|
||||
inRange = _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
Load(_boolType, _exec),
|
||||
inRange);
|
||||
var inRange = Load(_boolType, _exec);
|
||||
for (uint component = 0;
|
||||
component < coordinateComponentCount;
|
||||
component++)
|
||||
{
|
||||
var coordinate = _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_intType,
|
||||
coordinates,
|
||||
component);
|
||||
var extent = _module.AddInstruction(
|
||||
SpirvOp.CompositeExtract,
|
||||
_intType,
|
||||
imageSize,
|
||||
component);
|
||||
var nonNegative = _module.AddInstruction(
|
||||
SpirvOp.SGreaterThanEqual,
|
||||
_boolType,
|
||||
coordinate,
|
||||
zero);
|
||||
var belowExtent = _module.AddInstruction(
|
||||
SpirvOp.SLessThan,
|
||||
_boolType,
|
||||
coordinate,
|
||||
extent);
|
||||
var componentInRange = _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
nonNegative,
|
||||
belowExtent);
|
||||
inRange = _module.AddInstruction(
|
||||
SpirvOp.LogicalAnd,
|
||||
_boolType,
|
||||
inRange,
|
||||
componentInRange);
|
||||
}
|
||||
|
||||
var writeLabel = _module.AllocateId();
|
||||
var mergeLabel = _module.AllocateId();
|
||||
_module.AddStatement(SpirvOp.SelectionMerge, mergeLabel, 0);
|
||||
@@ -4108,30 +4154,30 @@ public static partial class Gen5SpirvTranslator
|
||||
_module.AddLabel(mergeLabel);
|
||||
}
|
||||
|
||||
private uint BuildImageOffset(Gen5ImageControl image, int component)
|
||||
private uint BuildImageOffset(
|
||||
Gen5ImageControl image,
|
||||
int component,
|
||||
uint componentCount)
|
||||
{
|
||||
var ivec2 = _module.TypeVector(_intType, 2);
|
||||
var packed = Bitcast(
|
||||
_intType,
|
||||
LoadV(image.GetAddressRegister(
|
||||
ImageAddressRegister(image, component))));
|
||||
var x = _module.AddInstruction(
|
||||
SpirvOp.BitFieldSExtract,
|
||||
_intType,
|
||||
packed,
|
||||
UInt(0),
|
||||
UInt(6));
|
||||
var y = _module.AddInstruction(
|
||||
SpirvOp.BitFieldSExtract,
|
||||
_intType,
|
||||
packed,
|
||||
UInt(8),
|
||||
UInt(6));
|
||||
var components = new uint[checked((int)componentCount)];
|
||||
for (var index = 0; index < components.Length; index++)
|
||||
{
|
||||
components[index] = _module.AddInstruction(
|
||||
SpirvOp.BitFieldSExtract,
|
||||
_intType,
|
||||
packed,
|
||||
UInt((uint)(index * 8)),
|
||||
UInt(6));
|
||||
}
|
||||
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.CompositeConstruct,
|
||||
ivec2,
|
||||
x,
|
||||
y);
|
||||
_module.TypeVector(_intType, componentCount),
|
||||
components);
|
||||
}
|
||||
|
||||
private uint ApplyDynamicSampleOffset(
|
||||
@@ -4141,7 +4187,16 @@ public static partial class Gen5SpirvTranslator
|
||||
uint texelOffset,
|
||||
uint lod)
|
||||
{
|
||||
var ivec2 = _module.TypeVector(_intType, 2);
|
||||
var spatialComponentCount = ImageSpatialComponentCount(resource);
|
||||
var coordinateComponentCount =
|
||||
ImageCoordinateComponentCount(resource);
|
||||
var spatialIntegerType =
|
||||
_module.TypeVector(_intType, spatialComponentCount);
|
||||
var spatialFloatType =
|
||||
_module.TypeVector(_floatType, spatialComponentCount);
|
||||
var queryComponentCount = resource.Arrayed
|
||||
? coordinateComponentCount
|
||||
: spatialComponentCount;
|
||||
var image = _module.AddInstruction(
|
||||
SpirvOp.Image,
|
||||
resource.ImageType,
|
||||
@@ -4163,14 +4218,14 @@ public static partial class Gen5SpirvTranslator
|
||||
signedLod);
|
||||
var size = _module.AddInstruction(
|
||||
SpirvOp.ImageQuerySizeLod,
|
||||
resource.Arrayed ? _module.TypeVector(_intType, 3) : ivec2,
|
||||
_module.TypeVector(_intType, queryComponentCount),
|
||||
image,
|
||||
clampedLod);
|
||||
if (resource.Arrayed)
|
||||
{
|
||||
size = _module.AddInstruction(
|
||||
SpirvOp.VectorShuffle,
|
||||
ivec2,
|
||||
spatialIntegerType,
|
||||
size,
|
||||
size,
|
||||
0u,
|
||||
@@ -4179,27 +4234,27 @@ public static partial class Gen5SpirvTranslator
|
||||
|
||||
var sizeFloat = _module.AddInstruction(
|
||||
SpirvOp.ConvertSToF,
|
||||
_vec2Type,
|
||||
spatialFloatType,
|
||||
size);
|
||||
var offsetFloat = _module.AddInstruction(
|
||||
SpirvOp.ConvertSToF,
|
||||
_vec2Type,
|
||||
spatialFloatType,
|
||||
texelOffset);
|
||||
var normalizedOffset = _module.AddInstruction(
|
||||
SpirvOp.FDiv,
|
||||
_vec2Type,
|
||||
spatialFloatType,
|
||||
offsetFloat,
|
||||
sizeFloat);
|
||||
if (!resource.Arrayed)
|
||||
{
|
||||
return _module.AddInstruction(
|
||||
SpirvOp.FAdd,
|
||||
_vec2Type,
|
||||
spatialFloatType,
|
||||
coordinates,
|
||||
normalizedOffset);
|
||||
}
|
||||
|
||||
var offsetVec3 = _module.AddInstruction(
|
||||
var arrayOffset = _module.AddInstruction(
|
||||
SpirvOp.CompositeConstruct,
|
||||
_vec3Type,
|
||||
_module.AddInstruction(
|
||||
@@ -4217,7 +4272,7 @@ public static partial class Gen5SpirvTranslator
|
||||
SpirvOp.FAdd,
|
||||
_vec3Type,
|
||||
coordinates,
|
||||
offsetVec3);
|
||||
arrayOffset);
|
||||
}
|
||||
|
||||
private bool TryEmitExport(
|
||||
|
||||
@@ -192,7 +192,8 @@ public sealed record Gen5GlobalMemoryControl(
|
||||
uint ScalarAddress,
|
||||
int OffsetBytes,
|
||||
bool Glc,
|
||||
bool Slc) : Gen5InstructionControl;
|
||||
bool Slc,
|
||||
bool UsesFlatAddress = false) : Gen5InstructionControl;
|
||||
|
||||
public sealed record Gen5BufferMemoryControl(
|
||||
uint DwordCount,
|
||||
|
||||
@@ -358,7 +358,10 @@ public static class Gen5ShaderScalarEvaluator
|
||||
if (globalMemory.ScalarAddress >= ScalarRegisterCount - 1)
|
||||
{
|
||||
error =
|
||||
$"global-address-register-range pc=0x{instruction.Pc:X} " +
|
||||
$"{(globalMemory.UsesFlatAddress
|
||||
? "flat-address-base-unresolved"
|
||||
: "global-address-register-range")} " +
|
||||
$"pc=0x{instruction.Pc:X} " +
|
||||
$"s{globalMemory.ScalarAddress}";
|
||||
return false;
|
||||
}
|
||||
@@ -373,11 +376,18 @@ public static class Gen5ShaderScalarEvaluator
|
||||
}
|
||||
|
||||
var key = (globalMemory.ScalarAddress, baseAddress);
|
||||
var writable = instruction.Opcode.StartsWith(
|
||||
var writable =
|
||||
instruction.Opcode.StartsWith(
|
||||
"GlobalStore",
|
||||
StringComparison.Ordinal) ||
|
||||
instruction.Opcode.StartsWith(
|
||||
"GlobalAtomic",
|
||||
StringComparison.Ordinal) ||
|
||||
instruction.Opcode.StartsWith(
|
||||
"FlatStore",
|
||||
StringComparison.Ordinal) ||
|
||||
instruction.Opcode.StartsWith(
|
||||
"FlatAtomic",
|
||||
StringComparison.Ordinal);
|
||||
if (globalMemoryByAddress.TryGetValue(key, out var existingBinding))
|
||||
{
|
||||
|
||||
@@ -471,7 +471,15 @@ public static class Gen5ShaderTranslator
|
||||
}
|
||||
}
|
||||
|
||||
instructions.Add(CreateInstruction(pc, encoding, name, words));
|
||||
var instruction = CreateInstruction(pc, encoding, name, words);
|
||||
if (instruction.Control is Gen5GlobalMemoryControl
|
||||
{
|
||||
UsesFlatAddress: true,
|
||||
})
|
||||
{
|
||||
instruction = ResolveFlatAddressBase(instructions, instruction);
|
||||
}
|
||||
instructions.Add(instruction);
|
||||
instructionCount++;
|
||||
|
||||
pc += sizeDwords * sizeof(uint);
|
||||
@@ -1395,35 +1403,42 @@ public static class Gen5ShaderTranslator
|
||||
var opcode = (word >> 18) & 0x7F;
|
||||
sizeDwords = 2;
|
||||
error = string.Empty;
|
||||
name = segment == 0x2
|
||||
? opcode switch
|
||||
{
|
||||
0x08 => "GlobalLoadUbyte",
|
||||
0x09 => "GlobalLoadSbyte",
|
||||
0x0A => "GlobalLoadUshort",
|
||||
0x0B => "GlobalLoadSshort",
|
||||
0x0C => "GlobalLoadDword",
|
||||
0x0D => "GlobalLoadDwordx2",
|
||||
0x0E => "GlobalLoadDwordx4",
|
||||
0x0F => "GlobalLoadDwordx3",
|
||||
0x18 => "GlobalStoreByte",
|
||||
0x19 => "GlobalStoreByteD16Hi",
|
||||
0x1A => "GlobalStoreShort",
|
||||
0x1B => "GlobalStoreShortD16Hi",
|
||||
0x1C => "GlobalStoreDword",
|
||||
0x1D => "GlobalStoreDwordx2",
|
||||
0x1E => "GlobalStoreDwordx4",
|
||||
0x1F => "GlobalStoreDwordx3",
|
||||
0x20 => "GlobalLoadUbyteD16",
|
||||
0x21 => "GlobalLoadUbyteD16Hi",
|
||||
0x22 => "GlobalLoadSbyteD16",
|
||||
0x23 => "GlobalLoadSbyteD16Hi",
|
||||
0x24 => "GlobalLoadShortD16",
|
||||
0x25 => "GlobalLoadShortD16Hi",
|
||||
0x32 => "GlobalAtomicAdd",
|
||||
0x38 => "GlobalAtomicUMax",
|
||||
_ => string.Empty,
|
||||
}
|
||||
var prefix = segment switch
|
||||
{
|
||||
0x0 => "Flat",
|
||||
0x2 => "Global",
|
||||
_ => string.Empty,
|
||||
};
|
||||
var suffix = opcode switch
|
||||
{
|
||||
0x08 => "LoadUbyte",
|
||||
0x09 => "LoadSbyte",
|
||||
0x0A => "LoadUshort",
|
||||
0x0B => "LoadSshort",
|
||||
0x0C => "LoadDword",
|
||||
0x0D => "LoadDwordx2",
|
||||
0x0E => "LoadDwordx4",
|
||||
0x0F => "LoadDwordx3",
|
||||
0x18 => "StoreByte",
|
||||
0x19 => "StoreByteD16Hi",
|
||||
0x1A => "StoreShort",
|
||||
0x1B => "StoreShortD16Hi",
|
||||
0x1C => "StoreDword",
|
||||
0x1D => "StoreDwordx2",
|
||||
0x1E => "StoreDwordx4",
|
||||
0x1F => "StoreDwordx3",
|
||||
0x20 => "LoadUbyteD16",
|
||||
0x21 => "LoadUbyteD16Hi",
|
||||
0x22 => "LoadSbyteD16",
|
||||
0x23 => "LoadSbyteD16Hi",
|
||||
0x24 => "LoadShortD16",
|
||||
0x25 => "LoadShortD16Hi",
|
||||
0x32 => "AtomicAdd",
|
||||
0x38 => "AtomicUMax",
|
||||
_ => string.Empty,
|
||||
};
|
||||
name = prefix.Length != 0 && suffix.Length != 0
|
||||
? prefix + suffix
|
||||
: string.Empty;
|
||||
|
||||
return FinishDecode(
|
||||
@@ -1646,6 +1661,82 @@ public static class Gen5ShaderTranslator
|
||||
_ => false,
|
||||
};
|
||||
|
||||
private static Gen5ShaderInstruction ResolveFlatAddressBase(
|
||||
IReadOnlyList<Gen5ShaderInstruction> precedingInstructions,
|
||||
Gen5ShaderInstruction instruction)
|
||||
{
|
||||
if (instruction.Control is not Gen5GlobalMemoryControl
|
||||
{
|
||||
UsesFlatAddress: true,
|
||||
} control ||
|
||||
!TryFindVectorDefinition(
|
||||
precedingInstructions,
|
||||
control.VectorAddress,
|
||||
out var lowDefinition) ||
|
||||
!TryFindVectorDefinition(
|
||||
precedingInstructions,
|
||||
control.VectorAddress + 1,
|
||||
out var highDefinition))
|
||||
{
|
||||
return instruction;
|
||||
}
|
||||
|
||||
foreach (var lowSource in lowDefinition.Sources)
|
||||
{
|
||||
if (lowSource.Kind != Gen5OperandKind.ScalarRegister)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var highSource in highDefinition.Sources)
|
||||
{
|
||||
if (highSource.Kind != Gen5OperandKind.ScalarRegister ||
|
||||
highSource.Value != lowSource.Value + 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return instruction with
|
||||
{
|
||||
Sources =
|
||||
[
|
||||
.. instruction.Sources,
|
||||
Gen5Operand.Scalar(lowSource.Value),
|
||||
],
|
||||
Control = control with
|
||||
{
|
||||
ScalarAddress = lowSource.Value,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return instruction;
|
||||
}
|
||||
|
||||
private static bool TryFindVectorDefinition(
|
||||
IReadOnlyList<Gen5ShaderInstruction> instructions,
|
||||
uint register,
|
||||
out Gen5ShaderInstruction definition)
|
||||
{
|
||||
for (var index = instructions.Count - 1; index >= 0; index--)
|
||||
{
|
||||
var candidate = instructions[index];
|
||||
foreach (var destination in candidate.Destinations)
|
||||
{
|
||||
if (destination.Kind == Gen5OperandKind.VectorRegister &&
|
||||
destination.Value == register)
|
||||
{
|
||||
definition = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
definition = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Gen5ShaderInstruction CreateInstruction(
|
||||
uint pc,
|
||||
Gen5ShaderEncoding encoding,
|
||||
@@ -2057,7 +2148,13 @@ public static class Gen5ShaderTranslator
|
||||
var vectorAddress = extra & 0xFF;
|
||||
var vectorData = (extra >> 8) & 0xFF;
|
||||
var scalarAddress = (extra >> 16) & 0x7F;
|
||||
var dwordCount = opcode switch
|
||||
var usesFlatAddress = opcode.StartsWith(
|
||||
"Flat",
|
||||
StringComparison.Ordinal);
|
||||
var memoryOpcode = usesFlatAddress
|
||||
? "Global" + opcode["Flat".Length..]
|
||||
: opcode;
|
||||
var dwordCount = memoryOpcode switch
|
||||
{
|
||||
"GlobalLoadUbyte" or
|
||||
"GlobalLoadSbyte" or
|
||||
@@ -2085,12 +2182,20 @@ public static class Gen5ShaderTranslator
|
||||
"GlobalStoreDwordx4" => 4u,
|
||||
_ => 0u,
|
||||
};
|
||||
sources =
|
||||
[
|
||||
Gen5Operand.Vector(vectorAddress),
|
||||
Gen5Operand.Scalar(scalarAddress),
|
||||
];
|
||||
destinations = opcode.StartsWith("GlobalLoad", StringComparison.Ordinal)
|
||||
sources = usesFlatAddress
|
||||
?
|
||||
[
|
||||
Gen5Operand.Vector(vectorAddress),
|
||||
Gen5Operand.Vector(vectorAddress + 1),
|
||||
]
|
||||
:
|
||||
[
|
||||
Gen5Operand.Vector(vectorAddress),
|
||||
Gen5Operand.Scalar(scalarAddress),
|
||||
];
|
||||
destinations = memoryOpcode.StartsWith(
|
||||
"GlobalLoad",
|
||||
StringComparison.Ordinal)
|
||||
? Enumerable
|
||||
.Range((int)vectorData, checked((int)dwordCount))
|
||||
.Select(index => Gen5Operand.Vector((uint)index))
|
||||
@@ -2100,10 +2205,11 @@ public static class Gen5ShaderTranslator
|
||||
dwordCount,
|
||||
vectorAddress,
|
||||
vectorData,
|
||||
scalarAddress,
|
||||
usesFlatAddress ? uint.MaxValue : scalarAddress,
|
||||
SignExtend(word & 0x1FFF, 13),
|
||||
((word >> 16) & 1) != 0,
|
||||
((word >> 17) & 1) != 0);
|
||||
((word >> 17) & 1) != 0,
|
||||
usesFlatAddress);
|
||||
break;
|
||||
}
|
||||
case Gen5ShaderEncoding.Mubuf:
|
||||
|
||||
Reference in New Issue
Block a user