diff --git a/src/SharpEmu.Libs/Agc/AgcExports.cs b/src/SharpEmu.Libs/Agc/AgcExports.cs index 6c852693..8dc7f67e 100644 --- a/src/SharpEmu.Libs/Agc/AgcExports.cs +++ b/src/SharpEmu.Libs/Agc/AgcExports.cs @@ -147,6 +147,10 @@ public static partial class AgcExports private const uint Gen5TextureFormatR16G16B16A16Float = 12; private const uint Gen5TextureType1D = 8; private const uint Gen5TextureType2D = 9; + private const uint Gen5TextureType3D = 10; + private const uint Gen5TextureTypeCube = 11; + private const uint Gen5TextureType1DArray = 12; + private const uint Gen5TextureType2DArray = 13; private const ulong MaxPresentedTextureBytes = 128UL * 1024UL * 1024UL; private const ulong VideoOutPixelFormatA8R8G8B8Srgb = 0x80000000; private const ulong VideoOutPixelFormatA8B8G8R8Srgb = 0x80002200; @@ -438,7 +442,8 @@ public static partial class AgcExports TextureDescriptor Descriptor, bool IsStorage, uint MipLevel, - IReadOnlyList SamplerDescriptor); + IReadOnlyList SamplerDescriptor, + bool IsArrayed = false); private readonly record struct RenderTargetWriter( ulong Sequence, @@ -5959,7 +5964,8 @@ public static partial class AgcExports binding, exportEvaluation.ImageBindings), binding.MipLevel ?? 0, - binding.SamplerDescriptor)); + binding.SamplerDescriptor, + Gen5ShaderTranslator.IsArrayedImageBinding(binding))); } IReadOnlyList vertexInputs = @@ -6416,7 +6422,8 @@ public static partial class AgcExports texture, isStorage, binding.MipLevel ?? 0, - binding.SamplerDescriptor)); + binding.SamplerDescriptor, + Gen5ShaderTranslator.IsArrayedImageBinding(binding))); } error = string.Empty; @@ -7387,6 +7394,7 @@ public static partial class AgcExports binding.IsStorage, binding.MipLevel, binding.SamplerDescriptor, + binding.IsArrayed, out var texture)) { textures.Add(texture); @@ -7877,18 +7885,23 @@ public static partial class AgcExports bool isStorage, uint mipLevel, IReadOnlyList samplerDescriptor, + bool isArrayed, out GuestDrawTexture texture) { texture = default!; if ((descriptor.Type != Gen5TextureType1D && - descriptor.Type != Gen5TextureType2D) || + descriptor.Type != Gen5TextureType2D && + descriptor.Type != Gen5TextureType3D && + descriptor.Type != Gen5TextureTypeCube && + descriptor.Type != Gen5TextureType1DArray && + descriptor.Type != Gen5TextureType2DArray) || descriptor.Width == 0 || descriptor.Height == 0 || descriptor.Width > 8192 || descriptor.Height > 8192) { TraceTextureFallback(descriptor, "invalid-descriptor"); - texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType); + texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType, isArrayed); return true; } @@ -7909,7 +7922,7 @@ public static partial class AgcExports TraceTextureFallback( descriptor, $"invalid-byte-count:{sourceByteCount}"); - texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType); + texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType, isArrayed); return true; } @@ -7938,7 +7951,7 @@ public static partial class AgcExports if (physicalSourceByteCount > MaxPresentedTextureBytes || physicalSourceByteCount > int.MaxValue) { - texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType); + texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType, isArrayed); return true; } @@ -7949,8 +7962,8 @@ public static partial class AgcExports var baseMipInTail = false; var mipTailElementX = 0; var mipTailElementY = 0; - if (hasElementLayout && resourceMipLevels > 1) - { + var chainSliceBytes = physicalSourceByteCount; + if (hasElementLayout && resourceMipLevels > 1 && GnmTiling.TryGetBaseMipPlacement( descriptor.TileMode, elementsWide, @@ -7960,14 +7973,26 @@ public static partial class AgcExports out baseMipByteOffset, out baseMipInTail, out mipTailElementX, - out mipTailElementY); + out mipTailElementY, + out var placedChainSliceBytes)) + { + chainSliceBytes = placedChainSliceBytes; } + var wantsArrayUpload = isArrayed && + !isStorage && + descriptor.Address != 0 && + (descriptor.Type == Gen5TextureType2DArray || + descriptor.Type == Gen5TextureType1DArray) && + descriptor.Depth > 1; + var arrayUploadLayers = wantsArrayUpload ? descriptor.Depth : 1u; + // Upload-known (not plain availability): the presenter's answer goes // generation-stale when the guest CPU rewrites a CPU-backed image // (video planes, streamed font atlases), which routes this draw back // through the texel copy below so the refresh path re-uploads. if (!isStorage && + !wantsArrayUpload && descriptor.Address != 0 && GuestGpu.Current.IsGuestImageUploadKnown( descriptor.Address, @@ -7990,7 +8015,8 @@ public static partial class AgcExports Pitch: sourceWidth, TileMode: descriptor.TileMode, DstSelect: descriptor.DstSelect, - Sampler: ToGuestSampler(samplerDescriptor)); + Sampler: ToGuestSampler(samplerDescriptor), + ArrayedView: isArrayed); return true; } @@ -8105,7 +8131,9 @@ public static partial class AgcExports descriptor.DstSelect, descriptor.TileMode, sourceWidth, - sampler))) + sampler, + isArrayed, + arrayUploadLayers))) { texture = new GuestDrawTexture( descriptor.Address, @@ -8123,17 +8151,77 @@ public static partial class AgcExports Pitch: sourceWidth, TileMode: descriptor.TileMode, DstSelect: descriptor.DstSelect, - Sampler: sampler); + Sampler: sampler, + ArrayedView: isArrayed, + ArrayLayers: arrayUploadLayers); return true; } + if (wantsArrayUpload) + { + var arrayLayers = arrayUploadLayers; + var layerBytes = checked((int)sourceByteCount); + var totalBytes = (long)layerBytes * arrayLayers; + if (totalBytes <= int.MaxValue) + { + var layered = new byte[totalBytes]; + var uploadedLayers = 0u; + for (var layer = 0u; layer < arrayLayers; layer++) + { + var sliceSource = new byte[(int)physicalSourceByteCount]; + if (!ctx.Memory.TryRead( + descriptor.Address + layer * chainSliceBytes + baseMipByteOffset, + sliceSource)) + { + break; + } + + var sliceLinear = TryDetileTextureSource( + descriptor, + sourceWidth, + layerBytes, + sliceSource, + baseMipInTail, + mipTailElementX, + mipTailElementY) ?? sliceSource.AsSpan(0, layerBytes).ToArray(); + sliceLinear.AsSpan(0, layerBytes) + .CopyTo(layered.AsSpan(checked((int)(layer * layerBytes)))); + uploadedLayers++; + } + + if (uploadedLayers == arrayLayers) + { + texture = new GuestDrawTexture( + descriptor.Address, + descriptor.Width, + descriptor.Height, + descriptor.Format, + descriptor.NumberType, + layered, + IsFallback: false, + IsStorage: false, + MipLevels: descriptor.MipLevels, + MipLevel: mipLevel, + BaseMipLevel: descriptor.ViewBaseLevel, + ResourceMipLevels: descriptor.ResourceMipLevels, + Pitch: sourceWidth, + TileMode: descriptor.TileMode, + DstSelect: descriptor.DstSelect, + Sampler: sampler, + ArrayedView: true, + ArrayLayers: arrayLayers); + return true; + } + } + } + var source = new byte[(int)physicalSourceByteCount]; if (!ctx.Memory.TryRead(descriptor.Address + baseMipByteOffset, source)) { TraceTextureFallback( descriptor, $"guest-read-failed:{sourceByteCount}"); - texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType); + texture = CreateFallbackGuestDrawTexture(isStorage, descriptor.Format, descriptor.NumberType, isArrayed); return true; } @@ -8187,7 +8275,8 @@ public static partial class AgcExports TileMode: descriptor.TileMode, DstSelect: descriptor.DstSelect, Sampler: ToGuestSampler(samplerDescriptor), - WriteGeneration: hasWriteGeneration ? writeGeneration : -1); + WriteGeneration: hasWriteGeneration ? writeGeneration : -1, + ArrayedView: isArrayed); return true; } @@ -8466,7 +8555,8 @@ public static partial class AgcExports private static GuestDrawTexture CreateFallbackGuestDrawTexture( bool isStorage, uint format, - uint numberType) + uint numberType, + bool isArrayed = false) { var fallbackFormat = format == 0 ? 10u : format; var fallbackNumberType = numberType; @@ -8480,7 +8570,8 @@ public static partial class AgcExports IsFallback: true, IsStorage: isStorage, MipLevels: 1, - MipLevel: 0); + MipLevel: 0, + ArrayedView: isArrayed); } private static GuestSampler ToGuestSampler(IReadOnlyList descriptor) => @@ -8856,7 +8947,8 @@ public static partial class AgcExports texture, isStorage, binding.MipLevel ?? 0, - binding.SamplerDescriptor)); + binding.SamplerDescriptor, + Gen5ShaderTranslator.IsArrayedImageBinding(binding))); hasStorageBinding |= isStorage; var descriptorState = descriptorValid ? string.Empty : "/invalid-desc"; diff --git a/src/SharpEmu.Libs/Agc/GnmTiling.cs b/src/SharpEmu.Libs/Agc/GnmTiling.cs index c3683733..565f6139 100644 --- a/src/SharpEmu.Libs/Agc/GnmTiling.cs +++ b/src/SharpEmu.Libs/Agc/GnmTiling.cs @@ -231,12 +231,14 @@ internal static class GnmTiling out ulong byteOffset, out bool inMipTail, out int tailElementX, - out int tailElementY) + out int tailElementY, + out ulong chainSliceBytes) { byteOffset = 0; inMipTail = false; tailElementX = 0; tailElementY = 0; + chainSliceBytes = 0; if (resourceMipLevels <= 1 || !ShouldDetile(swizzleMode) || elementsWide <= 0 || @@ -331,15 +333,22 @@ internal static class GnmTiling } inMipTail = true; + chainSliceBytes = (ulong)blockBytes; return true; } byteOffset = firstMipInTail < mipLevels ? (ulong)blockBytes : 0; + chainSliceBytes = byteOffset; for (var i = firstMipInTail - 1; i >= 1; i--) { byteOffset += mipSizes[i]; } + for (var i = 0; i < firstMipInTail; i++) + { + chainSliceBytes += mipSizes[i]; + } + return true; } diff --git a/src/SharpEmu.Libs/Gpu/GuestGpuTypes.cs b/src/SharpEmu.Libs/Gpu/GuestGpuTypes.cs index 759bf8ef..91a2ea3f 100644 --- a/src/SharpEmu.Libs/Gpu/GuestGpuTypes.cs +++ b/src/SharpEmu.Libs/Gpu/GuestGpuTypes.cs @@ -30,7 +30,9 @@ internal sealed record GuestDrawTexture( GuestSampler Sampler = default, // Guest CPU write-tracker generation of the memory RgbaPixels was read // from; -1 when the range is untracked or the pixels were not read here. - long WriteGeneration = -1); + long WriteGeneration = -1, + bool ArrayedView = false, + uint ArrayLayers = 1); /// Raw guest sampler descriptor dwords, copied verbatim from guest memory. internal readonly record struct GuestSampler( @@ -51,7 +53,9 @@ internal readonly record struct TextureContentIdentity( uint DstSelect, uint TileMode, uint Pitch, - GuestSampler Sampler); + GuestSampler Sampler, + bool Arrayed = false, + uint ArrayLayers = 1); internal sealed record GuestMemoryBuffer( ulong BaseAddress, diff --git a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs index a7252d8c..82b32289 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs @@ -3022,6 +3022,7 @@ internal static unsafe class VulkanVideoPresenter public uint Height; public uint RowLength; public uint DstSelect; + public uint Layers = 1; public bool NeedsUpload; public bool OwnsStorage; public bool IsStorage; @@ -6939,6 +6940,7 @@ internal static unsafe class VulkanVideoPresenter var vkFormat = GetTextureFormat(texture.Format, texture.NumberType); if (texture.Address != 0 && + !(texture.ArrayedView && texture.ArrayLayers > 1) && TryResolveGuestImageAlias(texture, vkFormat, out var guestImage) && TryGetOrCreateGuestImageView( guestImage, @@ -6946,7 +6948,8 @@ internal static unsafe class VulkanVideoPresenter mipLevel: texture.BaseMipLevel, levelCount: texture.MipLevels, dstSelect: texture.DstSelect, - out var view)) + out var view, + arrayedView: texture.ArrayedView)) { if (ShouldTraceVulkanResources() && _tracedTextureCacheHits.Add( @@ -7293,7 +7296,9 @@ internal static unsafe class VulkanVideoPresenter texture.DstSelect, texture.TileMode, texture.Pitch, - texture.Sampler); + texture.Sampler, + texture.ArrayedView, + Math.Max(texture.ArrayLayers, 1)); if (_textureCache.TryGetValue(key, out var cached)) { return cached; @@ -7681,6 +7686,7 @@ internal static unsafe class VulkanVideoPresenter : width; var vkFormat = GetTextureFormat(texture.Format, texture.NumberType); + var layers = Math.Max(texture.ArrayLayers, 1); var expectedSize = GetTextureByteCount(texture.Format, rowLength, height); if (ShouldTraceVulkanResources() && _tracedTextureUploads.Add((texture.Address, width, height, vkFormat))) @@ -7689,12 +7695,16 @@ internal static unsafe class VulkanVideoPresenter $"[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} " + - $"dst=0x{texture.DstSelect:X3} " + + $"layers={layers} dst=0x{texture.DstSelect:X3} " + $"bytes={texture.RgbaPixels.Length} expected={expectedSize}"); } - var pixels = texture.RgbaPixels.Length == (int)expectedSize + var pixels = texture.RgbaPixels.Length == (int)(expectedSize * layers) ? texture.RgbaPixels : CreateFallbackTexturePixels(texture.Format, rowLength, height, expectedSize); + if (!ReferenceEquals(pixels, texture.RgbaPixels)) + { + layers = 1; + } if (AddressListContains("SHARPEMU_FORCE_WHITE_TEXTURE_TARGETS", texture.Address)) { pixels = pixels.ToArray(); @@ -7727,7 +7737,7 @@ internal static unsafe class VulkanVideoPresenter Format = vkFormat, Extent = new Extent3D(width, height, 1), MipLevels = 1, - ArrayLayers = 1, + ArrayLayers = layers, Samples = SampleCountFlags.Count1Bit, Tiling = ImageTiling.Optimal, Usage = supportsAttachmentUsage @@ -7757,10 +7767,12 @@ internal static unsafe class VulkanVideoPresenter { SType = StructureType.ImageViewCreateInfo, Image = image, - ViewType = ImageViewType.Type2D, + ViewType = texture.ArrayedView + ? ImageViewType.Type2DArray + : ImageViewType.Type2D, Format = vkFormat, Components = ToVkComponentMapping(texture.DstSelect), - SubresourceRange = ColorSubresourceRange(), + SubresourceRange = ColorSubresourceRange(layerCount: layers), }; Check(_vk.CreateImageView(_device, &viewInfo, null, out var view), "vkCreateImageView(texture)"); var debugName = TextureDebugName(texture, vkFormat); @@ -7778,6 +7790,7 @@ internal static unsafe class VulkanVideoPresenter Height = height, RowLength = rowLength, DstSelect = texture.DstSelect, + Layers = layers, NeedsUpload = true, OwnsStorage = true, SamplerState = texture.Sampler, @@ -7787,6 +7800,8 @@ internal static unsafe class VulkanVideoPresenter }; if (texture.Address != 0 && + !texture.ArrayedView && + layers == 1 && !_guestImages.ContainsKey(texture.Address)) { var guestFormat = GetGuestTextureFormat(texture.Format, texture.NumberType); @@ -12105,11 +12120,12 @@ internal static unsafe class VulkanVideoPresenter uint mipLevel, uint levelCount, uint dstSelect, - out ImageView view) + out ImageView view, + bool arrayedView = false) { try { - view = GetOrCreateGuestImageView(resource, format, mipLevel, levelCount, dstSelect); + view = GetOrCreateGuestImageView(resource, format, mipLevel, levelCount, dstSelect, arrayedView); return true; } catch (Exception exception) @@ -12127,7 +12143,8 @@ internal static unsafe class VulkanVideoPresenter Format format, uint mipLevel, uint levelCount, - uint dstSelect = 0xFAC) + uint dstSelect = 0xFAC, + bool arrayedView = false) { if (mipLevel >= resource.MipLevels) { @@ -12137,7 +12154,7 @@ internal static unsafe class VulkanVideoPresenter levelCount = Math.Max(levelCount, 1); levelCount = Math.Min(levelCount, resource.MipLevels - mipLevel); - if (format == resource.Format && dstSelect == 0xFAC) + if (format == resource.Format && dstSelect == 0xFAC && !arrayedView) { if (mipLevel == 0 && levelCount == resource.MipLevels) { @@ -12156,7 +12173,7 @@ internal static unsafe class VulkanVideoPresenter $"Incompatible image view format {format} for image {resource.Format}."); } - var key = (format, mipLevel, levelCount, dstSelect); + var key = (format, mipLevel + (arrayedView ? 0x100u : 0), levelCount, dstSelect); if (resource.FormatViews.TryGetValue(key, out var existing)) { return existing; @@ -12166,7 +12183,7 @@ internal static unsafe class VulkanVideoPresenter { SType = StructureType.ImageViewCreateInfo, Image = resource.Image, - ViewType = ImageViewType.Type2D, + ViewType = arrayedView ? ImageViewType.Type2DArray : ImageViewType.Type2D, Format = format, Components = ToVkComponentMapping(dstSelect), SubresourceRange = ColorSubresourceRange(mipLevel, levelCount), @@ -13272,7 +13289,7 @@ internal static unsafe class VulkanVideoPresenter SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, DstQueueFamilyIndex = Vk.QueueFamilyIgnored, Image = texture.Image, - SubresourceRange = ColorSubresourceRange(), + SubresourceRange = ColorSubresourceRange(layerCount: texture.Layers), }; _vk.CmdPipelineBarrier( _commandBuffer, @@ -13294,7 +13311,7 @@ internal static unsafe class VulkanVideoPresenter ImageSubresource = new ImageSubresourceLayers { AspectMask = ImageAspectFlags.ColorBit, - LayerCount = 1, + LayerCount = texture.Layers, }, ImageExtent = new Extent3D(texture.Width, texture.Height, 1), }; @@ -13316,7 +13333,7 @@ internal static unsafe class VulkanVideoPresenter SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, DstQueueFamilyIndex = Vk.QueueFamilyIgnored, Image = texture.Image, - SubresourceRange = ColorSubresourceRange(), + SubresourceRange = ColorSubresourceRange(layerCount: texture.Layers), }; _vk.CmdPipelineBarrier( _commandBuffer, @@ -15287,13 +15304,14 @@ internal static unsafe class VulkanVideoPresenter private static ImageSubresourceRange ColorSubresourceRange( uint baseMipLevel = 0, - uint levelCount = 1) => + uint levelCount = 1, + uint layerCount = 1) => new() { AspectMask = ImageAspectFlags.ColorBit, BaseMipLevel = baseMipLevel, LevelCount = levelCount, - LayerCount = 1, + LayerCount = layerCount, }; private static byte[] ScaleBgra(byte[] source, uint sourceWidth, uint sourceHeight, uint width, uint height) diff --git a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs index 2769e0dc..3d5d216a 100644 --- a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs +++ b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs @@ -313,7 +313,8 @@ public static partial class Gen5SpirvTranslator uint ComponentType, uint VectorType, ImageComponentKind ComponentKind, - bool IsStorage); + bool IsStorage, + bool Arrayed); private readonly record struct SpirvVertexInput( uint Variable, @@ -1000,11 +1001,13 @@ public static partial class Gen5SpirvTranslator SpirvCapability.StorageImageExtendedFormats); } + var isArrayed = !isStorage && + Gen5ShaderTranslator.IsArrayedImageBinding(binding); var imageType = _module.TypeImage( componentType, SpirvImageDim.Dim2D, depth: false, - arrayed: false, + arrayed: isArrayed, multisampled: false, sampled: isStorage ? 2u : 1u, isStorage ? format : SpirvImageFormat.Unknown); @@ -1031,7 +1034,8 @@ public static partial class Gen5SpirvTranslator componentType, _module.TypeVector(componentType, 4), componentKind, - isStorage)); + isStorage, + isArrayed)); _interfaces.Add(variable); } } @@ -3529,12 +3533,16 @@ public static partial class Gen5SpirvTranslator addressCursor += 4; } - var coordinates = BuildFloatCoordinates(image, addressCursor); + var coordinates = resource.Arrayed + ? BuildFloatArrayCoordinates(image, addressCursor) + : BuildFloatCoordinates(image, addressCursor); var explicitLod = hasGradients || hasZeroLod || hasLod; var lod = hasZeroLod ? Float(0) : hasLod - ? LoadImageFloatAddress(image, addressCursor + 2) + ? LoadImageFloatAddress( + image, + addressCursor + (resource.Arrayed ? 3 : 2)) : lodOrBias; if (hasOffset) { @@ -3619,7 +3627,9 @@ public static partial class Gen5SpirvTranslator addressCursor += ImageFullAddressSlots(image); } - var coordinates = BuildFloatCoordinates(image, addressCursor); + var coordinates = resource.Arrayed + ? BuildFloatArrayCoordinates(image, addressCursor) + : BuildFloatCoordinates(image, addressCursor); var operands = new List { imageObject, @@ -3823,6 +3833,19 @@ public static partial class Gen5SpirvTranslator y); } + private uint BuildFloatArrayCoordinates(Gen5ImageControl image, int start) + { + var x = LoadImageFloatAddress(image, start); + var y = LoadImageFloatAddress(image, start + 1); + var slice = LoadImageFloatAddress(image, start + 2); + return _module.AddInstruction( + SpirvOp.CompositeConstruct, + _vec3Type, + x, + y, + slice); + } + private static int ImageAddressRegister( Gen5ImageControl image, int component) => image.A16 ? component / 2 : component; @@ -4140,9 +4163,20 @@ public static partial class Gen5SpirvTranslator signedLod); var size = _module.AddInstruction( SpirvOp.ImageQuerySizeLod, - ivec2, + resource.Arrayed ? _module.TypeVector(_intType, 3) : ivec2, image, clampedLod); + if (resource.Arrayed) + { + size = _module.AddInstruction( + SpirvOp.VectorShuffle, + ivec2, + size, + size, + 0u, + 1u); + } + var sizeFloat = _module.AddInstruction( SpirvOp.ConvertSToF, _vec2Type, @@ -4156,11 +4190,34 @@ public static partial class Gen5SpirvTranslator _vec2Type, offsetFloat, sizeFloat); + if (!resource.Arrayed) + { + return _module.AddInstruction( + SpirvOp.FAdd, + _vec2Type, + coordinates, + normalizedOffset); + } + + var offsetVec3 = _module.AddInstruction( + SpirvOp.CompositeConstruct, + _vec3Type, + _module.AddInstruction( + SpirvOp.CompositeExtract, + _floatType, + normalizedOffset, + 0u), + _module.AddInstruction( + SpirvOp.CompositeExtract, + _floatType, + normalizedOffset, + 1u), + Float(0)); return _module.AddInstruction( SpirvOp.FAdd, - _vec2Type, + _vec3Type, coordinates, - normalizedOffset); + offsetVec3); } private bool TryEmitExport( diff --git a/src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs b/src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs index ec14e8ff..7c33629a 100644 --- a/src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs +++ b/src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs @@ -1612,6 +1612,11 @@ public static class Gen5ShaderTranslator binding.ResourceDescriptor.SequenceEqual(candidate.ResourceDescriptor)); } + public static bool IsArrayedImageBinding(Gen5ImageBinding binding) => + binding.Control.IsArray && + (binding.Opcode.StartsWith("ImageSample", StringComparison.Ordinal) || + binding.Opcode.StartsWith("ImageGather4", StringComparison.Ordinal)); + public static bool IsDataShareAtomic(string name) => name switch { "DsAddU32" or "DsSubU32" or "DsIncU32" or "DsDecU32" or