0
0
Fork 0
mirror of https://github.com/ryujinx-mirror/ryujinx.git synced 2024-12-22 23:05:46 +00:00

Restride vertex buffer when stride causes attributes to misalign in Vulkan. (#3679)

* Vertex Buffer Alignment part 1

* Update CacheByRange

* Add Stride Change compute shader, fix storage buffers in helpers

* An AMD exclusive

* Reword

* Change rules - stride conversion when attrs misalign

* Fix stupid mistake

* Fix background pipeline compile

* Improve a few things.

* Fix some feedback

* Address Feedback

(the shader binary didn't change when i changed the source to use the subgroup size)

* Fix bug where rewritten buffer would be disposed instantly.
This commit is contained in:
riperiperi 2022-09-09 00:30:19 +01:00 committed by GitHub
parent ee1825219b
commit c6d82209ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1069 additions and 120 deletions

View file

@ -151,6 +151,190 @@ namespace Ryujinx.Graphics.GAL
public static class FormatExtensions
{
/// <summary>
/// The largest scalar size for a buffer format.
/// </summary>
public const int MaxBufferFormatScalarSize = 4;
/// <summary>
/// Gets the byte size for a single component of this format, or its packed size.
/// </summary>
/// <param name="format">Texture format</param>
/// <returns>Byte size for a single component, or packed size</returns>
public static int GetScalarSize(this Format format)
{
switch (format)
{
case Format.R8Unorm:
case Format.R8Snorm:
case Format.R8Uint:
case Format.R8Sint:
case Format.R8G8Unorm:
case Format.R8G8Snorm:
case Format.R8G8Uint:
case Format.R8G8Sint:
case Format.R8G8B8Unorm:
case Format.R8G8B8Snorm:
case Format.R8G8B8Uint:
case Format.R8G8B8Sint:
case Format.R8G8B8A8Unorm:
case Format.R8G8B8A8Snorm:
case Format.R8G8B8A8Uint:
case Format.R8G8B8A8Sint:
case Format.R8G8B8A8Srgb:
case Format.R4G4Unorm:
case Format.R8Uscaled:
case Format.R8Sscaled:
case Format.R8G8Uscaled:
case Format.R8G8Sscaled:
case Format.R8G8B8Uscaled:
case Format.R8G8B8Sscaled:
case Format.R8G8B8A8Uscaled:
case Format.R8G8B8A8Sscaled:
case Format.B8G8R8A8Unorm:
case Format.B8G8R8A8Srgb:
return 1;
case Format.R16Float:
case Format.R16Unorm:
case Format.R16Snorm:
case Format.R16Uint:
case Format.R16Sint:
case Format.R16G16Float:
case Format.R16G16Unorm:
case Format.R16G16Snorm:
case Format.R16G16Uint:
case Format.R16G16Sint:
case Format.R16G16B16Float:
case Format.R16G16B16Unorm:
case Format.R16G16B16Snorm:
case Format.R16G16B16Uint:
case Format.R16G16B16Sint:
case Format.R16G16B16A16Float:
case Format.R16G16B16A16Unorm:
case Format.R16G16B16A16Snorm:
case Format.R16G16B16A16Uint:
case Format.R16G16B16A16Sint:
case Format.R4G4B4A4Unorm:
case Format.R5G5B5X1Unorm:
case Format.R5G5B5A1Unorm:
case Format.R5G6B5Unorm:
case Format.R16Uscaled:
case Format.R16Sscaled:
case Format.R16G16Uscaled:
case Format.R16G16Sscaled:
case Format.R16G16B16Uscaled:
case Format.R16G16B16Sscaled:
case Format.R16G16B16A16Uscaled:
case Format.R16G16B16A16Sscaled:
case Format.B5G6R5Unorm:
case Format.B5G5R5A1Unorm:
case Format.A1B5G5R5Unorm:
return 2;
case Format.R32Float:
case Format.R32Uint:
case Format.R32Sint:
case Format.R32G32Float:
case Format.R32G32Uint:
case Format.R32G32Sint:
case Format.R32G32B32Float:
case Format.R32G32B32Uint:
case Format.R32G32B32Sint:
case Format.R32G32B32A32Float:
case Format.R32G32B32A32Uint:
case Format.R32G32B32A32Sint:
case Format.R10G10B10A2Unorm:
case Format.R10G10B10A2Uint:
case Format.R11G11B10Float:
case Format.R9G9B9E5Float:
case Format.R32Uscaled:
case Format.R32Sscaled:
case Format.R32G32Uscaled:
case Format.R32G32Sscaled:
case Format.R32G32B32Uscaled:
case Format.R32G32B32Sscaled:
case Format.R32G32B32A32Uscaled:
case Format.R32G32B32A32Sscaled:
case Format.R10G10B10A2Snorm:
case Format.R10G10B10A2Sint:
case Format.R10G10B10A2Uscaled:
case Format.R10G10B10A2Sscaled:
return 4;
case Format.S8Uint:
return 1;
case Format.D16Unorm:
return 2;
case Format.S8UintD24Unorm:
case Format.D32Float:
case Format.D24UnormS8Uint:
return 4;
case Format.D32FloatS8Uint:
return 8;
case Format.Bc1RgbaUnorm:
case Format.Bc1RgbaSrgb:
return 8;
case Format.Bc2Unorm:
case Format.Bc3Unorm:
case Format.Bc2Srgb:
case Format.Bc3Srgb:
case Format.Bc4Unorm:
case Format.Bc4Snorm:
case Format.Bc5Unorm:
case Format.Bc5Snorm:
case Format.Bc7Unorm:
case Format.Bc7Srgb:
case Format.Bc6HSfloat:
case Format.Bc6HUfloat:
return 16;
case Format.Etc2RgbUnorm:
case Format.Etc2RgbPtaUnorm:
case Format.Etc2RgbSrgb:
case Format.Etc2RgbPtaSrgb:
return 8;
case Format.Etc2RgbaUnorm:
case Format.Etc2RgbaSrgb:
return 16;
case Format.Astc4x4Unorm:
case Format.Astc5x4Unorm:
case Format.Astc5x5Unorm:
case Format.Astc6x5Unorm:
case Format.Astc6x6Unorm:
case Format.Astc8x5Unorm:
case Format.Astc8x6Unorm:
case Format.Astc8x8Unorm:
case Format.Astc10x5Unorm:
case Format.Astc10x6Unorm:
case Format.Astc10x8Unorm:
case Format.Astc10x10Unorm:
case Format.Astc12x10Unorm:
case Format.Astc12x12Unorm:
case Format.Astc4x4Srgb:
case Format.Astc5x4Srgb:
case Format.Astc5x5Srgb:
case Format.Astc6x5Srgb:
case Format.Astc6x6Srgb:
case Format.Astc8x5Srgb:
case Format.Astc8x6Srgb:
case Format.Astc8x8Srgb:
case Format.Astc10x5Srgb:
case Format.Astc10x6Srgb:
case Format.Astc10x8Srgb:
case Format.Astc10x10Srgb:
case Format.Astc12x10Srgb:
case Format.Astc12x12Srgb:
return 16;
}
return 1;
}
/// <summary>
/// Checks if the texture format is valid to use as image format.
/// </summary>

View file

@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Auto<MemoryAllocation> _allocationAuto;
private readonly ulong _bufferHandle;
private CacheByRange<BufferHolder> _cachedConvertedIndexBuffers;
private CacheByRange<BufferHolder> _cachedConvertedBuffers;
public int Size { get; }
@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Vulkan
{
if (isWrite)
{
_cachedConvertedIndexBuffers.Clear();
_cachedConvertedBuffers.Clear();
}
return _buffer;
@ -364,13 +364,35 @@ namespace Ryujinx.Graphics.Vulkan
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
{
if (!_cachedConvertedIndexBuffers.TryGetValue(offset, size, out var holder))
var key = new I8ToI16CacheKey();
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
{
holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3);
_gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size);
_cachedConvertedIndexBuffers.Add(offset, size, holder);
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
}
public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, int offset, int size, int stride, int alignment)
{
var key = new AlignedVertexBufferCacheKey(_gd, stride, alignment);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
{
int alignedStride = (stride + (alignment - 1)) & -alignment;
holder = _gd.BufferManager.Create(_gd, (size / stride) * alignedStride);
_gd.HelperShader.ChangeStride(_gd, cbs, this, holder, offset, size, stride, alignedStride);
key.SetBuffer(holder.GetBuffer());
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
return holder.GetBuffer();
@ -382,7 +404,7 @@ namespace Ryujinx.Graphics.Vulkan
_buffer.Dispose();
_allocationAuto.Dispose();
_cachedConvertedIndexBuffers.Dispose();
_cachedConvertedBuffers.Dispose();
}
}
}

View file

@ -130,6 +130,16 @@ namespace Ryujinx.Graphics.Vulkan
return null;
}
public Auto<DisposableBuffer> GetAlignedVertexBuffer(CommandBufferScoped cbs, BufferHandle handle, int offset, int size, int stride, int alignment)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetAlignedVertexBuffer(cbs, offset, size, stride, alignment);
}
return null;
}
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size)
{
if (TryGetBuffer(handle, out var holder))

View file

@ -7,28 +7,28 @@ namespace Ryujinx.Graphics.Vulkan
{
public static BufferState Null => new BufferState(null, 0, 0);
private readonly Auto<DisposableBuffer> _buffer;
private readonly int _offset;
private readonly int _size;
private readonly ulong _stride;
private readonly IndexType _type;
private readonly Auto<DisposableBuffer> _buffer;
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, IndexType type)
{
_buffer = buffer;
_offset = offset;
_size = size;
_stride = 0;
_type = type;
buffer?.IncrementReferenceCount();
}
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, ulong stride = 0UL)
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size)
{
_buffer = buffer;
_offset = offset;
_size = size;
_stride = stride;
_type = IndexType.Uint16;
buffer?.IncrementReferenceCount();
}
@ -51,30 +51,6 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding)
{
if (_buffer != null)
{
var buffer = _buffer.Get(cbs, _offset, _size).Value;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
(ulong)_offset,
(ulong)_size,
_stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset);
}
}
}
public void Dispose()
{
_buffer?.DecrementReferenceCount();

View file

@ -3,29 +3,110 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics.Vulkan
{
struct CacheByRange<T> where T : IDisposable
interface ICacheKey : IDisposable
{
private Dictionary<ulong, T> _ranges;
bool KeyEqual(ICacheKey other);
}
public void Add(int offset, int size, T value)
struct I8ToI16CacheKey : ICacheKey
{
public I8ToI16CacheKey() { }
public bool KeyEqual(ICacheKey other)
{
EnsureInitialized();
_ranges.Add(PackRange(offset, size), value);
return other is I8ToI16CacheKey;
}
public bool TryGetValue(int offset, int size, out T value)
public void Dispose() { }
}
struct AlignedVertexBufferCacheKey : ICacheKey
{
private readonly int _stride;
private readonly int _alignment;
// Used to notify the pipeline that bindings have invalidated on dispose.
private readonly VulkanRenderer _gd;
private Auto<DisposableBuffer> _buffer;
public AlignedVertexBufferCacheKey(VulkanRenderer gd, int stride, int alignment)
{
EnsureInitialized();
return _ranges.TryGetValue(PackRange(offset, size), out value);
_gd = gd;
_stride = stride;
_alignment = alignment;
_buffer = null;
}
public bool KeyEqual(ICacheKey other)
{
return other is AlignedVertexBufferCacheKey entry &&
entry._stride == _stride &&
entry._alignment == _alignment;
}
public void SetBuffer(Auto<DisposableBuffer> buffer)
{
_buffer = buffer;
}
public void Dispose()
{
_gd.PipelineInternal.DirtyVertexBuffer(_buffer);
}
}
struct CacheByRange<T> where T : IDisposable
{
private struct Entry
{
public ICacheKey Key;
public T Value;
public Entry(ICacheKey key, T value)
{
Key = key;
Value = value;
}
}
private Dictionary<ulong, List<Entry>> _ranges;
public void Add(int offset, int size, ICacheKey key, T value)
{
List<Entry> entries = GetEntries(offset, size);
entries.Add(new Entry(key, value));
}
public bool TryGetValue(int offset, int size, ICacheKey key, out T value)
{
List<Entry> entries = GetEntries(offset, size);
foreach (Entry entry in entries)
{
if (entry.Key.KeyEqual(key))
{
value = entry.Value;
return true;
}
}
value = default;
return false;
}
public void Clear()
{
if (_ranges != null)
{
foreach (T value in _ranges.Values)
foreach (List<Entry> entries in _ranges.Values)
{
value.Dispose();
foreach (Entry entry in entries)
{
entry.Key.Dispose();
entry.Value.Dispose();
}
}
_ranges.Clear();
@ -33,12 +114,23 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private void EnsureInitialized()
private List<Entry> GetEntries(int offset, int size)
{
if (_ranges == null)
{
_ranges = new Dictionary<ulong, T>();
_ranges = new Dictionary<ulong, List<Entry>>();
}
ulong key = PackRange(offset, size);
List<Entry> value;
if (!_ranges.TryGetValue(key, out value))
{
value = new List<Entry>();
_ranges.Add(key, value);
}
return value;
}
private static ulong PackRange(int offset, int size)

View file

@ -185,6 +185,34 @@ namespace Ryujinx.Graphics.Vulkan
SignalDirty(DirtyFlags.Storage);
}
public void SetStorageBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
{
for (int i = 0; i < buffers.Length; i++)
{
var vkBuffer = buffers[i];
int index = first + i;
ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
DescriptorBufferInfo info = new DescriptorBufferInfo()
{
Offset = 0,
Range = Vk.WholeSize
};
ref DescriptorBufferInfo currentInfo = ref _storageBuffers[index];
if (vkBuffer != currentVkBuffer || currentInfo.Offset != info.Offset || currentInfo.Range != info.Range)
{
_storageSet[index] = false;
currentInfo = info;
currentVkBuffer = vkBuffer;
}
}
SignalDirty(DirtyFlags.Storage);
}
public void SetTextureAndSampler(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture texture, ISampler sampler)
{
if (texture == null)
@ -388,7 +416,14 @@ namespace Ryujinx.Graphics.Vulkan
}
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
if (program.HasMinimalLayout)
{
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
}
else
{
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
}
}
else if (setIndex == PipelineBase.TextureSetIndex)
{

View file

@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsGeometryShaderPassthrough;
public readonly bool SupportsSubgroupSizeControl;
public readonly bool SupportsShaderInt8;
public readonly bool SupportsConditionalRendering;
public readonly bool SupportsExtendedDynamicState;
public readonly bool SupportsMultiView;
@ -29,6 +30,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsFragmentShaderInterlock,
bool supportsGeometryShaderPassthrough,
bool supportsSubgroupSizeControl,
bool supportsShaderInt8,
bool supportsConditionalRendering,
bool supportsExtendedDynamicState,
bool supportsMultiView,
@ -47,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
SupportsSubgroupSizeControl = supportsSubgroupSizeControl;
SupportsShaderInt8 = supportsShaderInt8;
SupportsConditionalRendering = supportsConditionalRendering;
SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsMultiView = supportsMultiView;

View file

@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly IProgram _programColorBlit;
private readonly IProgram _programColorBlitClearAlpha;
private readonly IProgram _programColorClear;
private readonly IProgram _programStrideChange;
public HelperShader(VulkanRenderer gd, Device device)
{
@ -39,14 +40,14 @@ namespace Ryujinx.Graphics.Vulkan
_programColorBlit = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programColorBlitClearAlpha = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
var fragmentBindings2 = new ShaderBindings(
@ -57,8 +58,19 @@ namespace Ryujinx.Graphics.Vulkan
_programColorClear = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Glsl),
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Spirv),
});
var strideChangeBindings = new ShaderBindings(
new[] { 0 },
new[] { 1, 2 },
Array.Empty<int>(),
Array.Empty<int>());
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ChangeBufferStrideShaderSource, strideChangeBindings, ShaderStage.Compute, TargetLanguage.Spirv),
});
}
@ -163,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0);
_pipeline.Finish();
_pipeline.Finish(gd, cbs);
gd.BufferManager.Delete(bufferHandle);
}
@ -291,45 +303,100 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void ConvertI8ToI16(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size)
{
// TODO: Do this with a compute shader?
var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, size).Value;
var dstBuffer = dst.GetBuffer().Get(cbs, 0, size * 2).Value;
ChangeStride(gd, cbs, src, dst, srcOffset, size, 1, 2);
}
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
public unsafe void ChangeStride(VulkanRenderer gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size, int stride, int newStride)
{
bool supportsUint8 = gd.Capabilities.SupportsShaderInt8;
var bufferCopy = new BufferCopy[size];
int elems = size / stride;
int newSize = elems * newStride;
for (ulong i = 0; i < (ulong)size; i++)
{
bufferCopy[i] = new BufferCopy((ulong)srcOffset + i, i * 2, 1);
}
var srcBufferAuto = src.GetBuffer();
var dstBufferAuto = dst.GetBuffer();
var srcBuffer = srcBufferAuto.Get(cbs, srcOffset, size).Value;
var dstBuffer = dstBufferAuto.Get(cbs, 0, newSize).Value;
var access = supportsUint8 ? AccessFlags.AccessShaderWriteBit : AccessFlags.AccessTransferWriteBit;
var stage = supportsUint8 ? PipelineStageFlags.PipelineStageComputeShaderBit : PipelineStageFlags.PipelineStageTransferBit;
BufferHolder.InsertBufferBarrier(
gd,
cbs.CommandBuffer,
dstBuffer,
BufferHolder.DefaultAccessFlags,
AccessFlags.AccessTransferWriteBit,
access,
PipelineStageFlags.PipelineStageAllCommandsBit,
PipelineStageFlags.PipelineStageTransferBit,
stage,
0,
size * 2);
newSize);
fixed (BufferCopy* pBufferCopy = bufferCopy)
if (supportsUint8)
{
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)size, pBufferCopy);
const int ParamsBufferSize = 16;
Span<int> shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
shaderParams[0] = stride;
shaderParams[1] = newStride;
shaderParams[2] = size;
shaderParams[3] = srcOffset;
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false);
gd.BufferManager.SetData<int>(bufferHandle, 0, shaderParams);
_pipeline.SetCommandBuffer(cbs);
Span<BufferRange> cbRanges = stackalloc BufferRange[1];
cbRanges[0] = new BufferRange(bufferHandle, 0, ParamsBufferSize);
_pipeline.SetUniformBuffers(0, cbRanges);
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
sbRanges[0] = srcBufferAuto;
sbRanges[1] = dstBufferAuto;
_pipeline.SetStorageBuffers(1, sbRanges);
_pipeline.SetProgram(_programStrideChange);
_pipeline.DispatchCompute(1, 1, 1);
gd.BufferManager.Delete(bufferHandle);
_pipeline.Finish(gd, cbs);
}
else
{
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
var bufferCopy = new BufferCopy[elems];
for (ulong i = 0; i < (ulong)elems; i++)
{
bufferCopy[i] = new BufferCopy((ulong)srcOffset + i * (ulong)stride, i * (ulong)newStride, (ulong)stride);
}
fixed (BufferCopy* pBufferCopy = bufferCopy)
{
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)elems, pBufferCopy);
}
}
BufferHolder.InsertBufferBarrier(
gd,
cbs.CommandBuffer,
dstBuffer,
AccessFlags.AccessTransferWriteBit,
access,
BufferHolder.DefaultAccessFlags,
PipelineStageFlags.PipelineStageTransferBit,
stage,
PipelineStageFlags.PipelineStageAllCommandsBit,
0,
size * 2);
newSize);
}
protected virtual void Dispose(bool disposing)

View file

@ -2,6 +2,7 @@
using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
using System.Numerics;
namespace Ryujinx.Graphics.Vulkan
{
@ -50,14 +51,14 @@ namespace Ryujinx.Graphics.Vulkan
private BufferState _indexBuffer;
private readonly BufferState[] _transformFeedbackBuffers;
private readonly BufferState[] _vertexBuffers;
private readonly VertexBufferState[] _vertexBuffers;
private ulong _vertexBuffersDirty;
protected Rectangle<int> ClearScissor;
public SupportBufferUpdater SupportBufferUpdater;
private bool _needsIndexBufferRebind;
private bool _needsTransformFeedbackBuffersRebind;
private bool _needsVertexBuffersRebind;
private bool _tfEnabled;
private bool _tfActive;
@ -79,14 +80,14 @@ namespace Ryujinx.Graphics.Vulkan
_descriptorSetUpdater = new DescriptorSetUpdater(gd, this);
_transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
_vertexBuffers = new BufferState[Constants.MaxVertexBuffers + 1];
_vertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers + 1];
const int EmptyVbSize = 16;
using var emptyVb = gd.BufferManager.Create(gd, EmptyVbSize);
emptyVb.SetData(0, new byte[EmptyVbSize]);
_vertexBuffers[0] = new BufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0UL);
_needsVertexBuffersRebind = true;
_vertexBuffers[0] = new VertexBufferState(emptyVb.GetBuffer(), 0, EmptyVbSize, 0);
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
ClearScissor = new Rectangle<int>(0, 0, 0xffff, 0xffff);
@ -229,6 +230,17 @@ namespace Ryujinx.Graphics.Vulkan
BufferHolder.Copy(Gd, Cbs, src, dst, srcOffset, dstOffset, size);
}
public void DirtyVertexBuffer(Auto<DisposableBuffer> buffer)
{
for (int i = 0; i < _vertexBuffers.Length; i++)
{
if (_vertexBuffers[i].BoundEquals(buffer))
{
_vertexBuffersDirty |= 1UL << i;
}
}
}
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
{
if (!_program.IsLinked)
@ -345,6 +357,11 @@ namespace Ryujinx.Graphics.Vulkan
_tfEnabled = false;
}
public bool IsCommandBufferActive(CommandBuffer cb)
{
return CommandBuffer.Handle == cb.Handle;
}
public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
if (!Gd.Capabilities.SupportsIndirectParameters)
@ -689,6 +706,11 @@ namespace Ryujinx.Graphics.Vulkan
_descriptorSetUpdater.SetStorageBuffers(CommandBuffer, first, buffers);
}
public void SetStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
{
_descriptorSetUpdater.SetStorageBuffers(CommandBuffer, first, buffers);
}
public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
{
_descriptorSetUpdater.SetTextureAndSampler(Cbs, stage, binding, texture, sampler);
@ -732,12 +754,22 @@ namespace Ryujinx.Graphics.Vulkan
{
var formatCapabilities = Gd.FormatCapabilities;
Span<int> newVbScalarSizes = stackalloc int[Constants.MaxVertexBuffers];
int count = Math.Min(Constants.MaxVertexAttributes, vertexAttribs.Length);
uint dirtyVbSizes = 0;
for (int i = 0; i < count; i++)
{
var attribute = vertexAttribs[i];
var bufferIndex = attribute.IsZero ? 0 : attribute.BufferIndex + 1;
var rawIndex = attribute.BufferIndex;
var bufferIndex = attribute.IsZero ? 0 : rawIndex + 1;
if (!attribute.IsZero)
{
newVbScalarSizes[rawIndex] = Math.Max(newVbScalarSizes[rawIndex], attribute.Format.GetScalarSize());
dirtyVbSizes |= 1u << rawIndex;
}
_newState.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription(
(uint)i,
@ -746,6 +778,21 @@ namespace Ryujinx.Graphics.Vulkan
(uint)attribute.Offset);
}
while (dirtyVbSizes != 0)
{
int dirtyBit = BitOperations.TrailingZeroCount(dirtyVbSizes);
ref var buffer = ref _vertexBuffers[dirtyBit + 1];
if (buffer.AttributeScalarAlignment != newVbScalarSizes[dirtyBit])
{
_vertexBuffersDirty |= 1UL << (dirtyBit + 1);
buffer.AttributeScalarAlignment = newVbScalarSizes[dirtyBit];
}
dirtyVbSizes &= ~(1u << dirtyBit);
}
_newState.VertexAttributeDescriptionsCount = (uint)count;
SignalStateChange();
}
@ -792,14 +839,37 @@ namespace Ryujinx.Graphics.Vulkan
}
}
_vertexBuffers[binding].Dispose();
_vertexBuffers[binding] = new BufferState(
vb,
vertexBuffer.Buffer.Offset,
vbSize,
(ulong)vertexBuffer.Stride);
ref var buffer = ref _vertexBuffers[binding];
int oldScalarAlign = buffer.AttributeScalarAlignment;
_vertexBuffers[binding].BindVertexBuffer(Gd, Cbs, (uint)binding);
buffer.Dispose();
if ((vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
{
buffer = new VertexBufferState(
vb,
descriptorIndex,
vertexBuffer.Buffer.Offset,
vbSize,
vertexBuffer.Stride);
buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState);
}
else
{
// May need to be rewritten. Bind this buffer before draw.
buffer = new VertexBufferState(
vertexBuffer.Buffer.Handle,
descriptorIndex,
vertexBuffer.Buffer.Offset,
vbSize,
vertexBuffer.Stride);
_vertexBuffersDirty |= 1UL << binding;
}
buffer.AttributeScalarAlignment = oldScalarAlign;
}
}
}
@ -907,7 +977,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_needsIndexBufferRebind = true;
_needsTransformFeedbackBuffersRebind = true;
_needsVertexBuffersRebind = true;
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
_descriptorSetUpdater.SignalCommandBufferChange();
_dynamicState.ForceAllDirty();
@ -1053,13 +1123,6 @@ namespace Ryujinx.Graphics.Vulkan
// Commit changes to the support buffer before drawing.
SupportBufferUpdater.Commit();
if (_stateDirty || Pbp != pbp)
{
CreatePipeline(pbp);
_stateDirty = false;
Pbp = pbp;
}
if (_needsIndexBufferRebind)
{
_indexBuffer.BindIndexBuffer(Gd.Api, Cbs);
@ -1078,14 +1141,23 @@ namespace Ryujinx.Graphics.Vulkan
_needsTransformFeedbackBuffersRebind = false;
}
if (_needsVertexBuffersRebind)
if (_vertexBuffersDirty != 0)
{
for (int i = 0; i < Constants.MaxVertexBuffers + 1; i++)
while (_vertexBuffersDirty != 0)
{
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i);
}
int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty);
_needsVertexBuffersRebind = false;
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState);
_vertexBuffersDirty &= ~(1u << i);
}
}
if (_stateDirty || Pbp != pbp)
{
CreatePipeline(pbp);
_stateDirty = false;
Pbp = pbp;
}
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, pbp);

View file

@ -202,6 +202,9 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.Topology = state.Topology.Convert();
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
Span<int> vbScalarSizes = stackalloc int[vbCount];
for (int i = 0; i < vaCount; i++)
{
@ -213,13 +216,16 @@ namespace Ryujinx.Graphics.Vulkan
(uint)bufferIndex,
gd.FormatCapabilities.ConvertToVertexVkFormat(attribute.Format),
(uint)attribute.Offset);
if (!attribute.IsZero && bufferIndex < vbCount)
{
vbScalarSizes[bufferIndex - 1] = Math.Max(attribute.Format.GetScalarSize(), vbScalarSizes[bufferIndex - 1]);
}
}
int descriptorIndex = 1;
pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
for (int i = 0; i < vbCount; i++)
{
var vertexBuffer = state.VertexBuffers[i];
@ -228,10 +234,17 @@ namespace Ryujinx.Graphics.Vulkan
{
var inputRate = vertexBuffer.Divisor != 0 ? VertexInputRate.Instance : VertexInputRate.Vertex;
int alignedStride = vertexBuffer.Stride;
if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment))
{
alignedStride = (vertexBuffer.Stride + (alignment - 1)) & -alignment;
}
// TODO: Support divisor > 1
pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription(
(uint)i + 1,
(uint)vertexBuffer.Stride,
(uint)alignedStride,
inputRate);
}
}

View file

@ -199,6 +199,16 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void Restore()
{
if (Pipeline != null)
{
Gd.Api.CmdBindPipeline(CommandBuffer, Pbp, Pipeline.Get(Cbs).Value);
}
SignalCommandBufferChange();
}
public void FlushCommandsImpl()
{
EndRenderPass();
@ -220,18 +230,13 @@ namespace Ryujinx.Graphics.Vulkan
// Restore per-command buffer state.
if (Pipeline != null)
{
Gd.Api.CmdBindPipeline(CommandBuffer, Pbp, Pipeline.Get(Cbs).Value);
}
foreach (var queryPool in _activeQueries)
{
Gd.Api.CmdResetQueryPool(CommandBuffer, queryPool, 0, 1);
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, 0);
}
SignalCommandBufferChange();
Restore();
}
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset)

View file

@ -40,5 +40,15 @@ namespace Ryujinx.Graphics.Vulkan
{
EndRenderPass();
}
public void Finish(VulkanRenderer gd, CommandBufferScoped cbs)
{
Finish();
if (gd.PipelineInternal.IsCommandBufferActive(cbs.CommandBuffer))
{
gd.PipelineInternal.Restore();
}
}
}
}

View file

@ -142,18 +142,20 @@ namespace Ryujinx.Graphics.Vulkan
int stagesCount = shaders.Length;
int uCount = 0;
int sCount = 0;
int tCount = 0;
int iCount = 0;
foreach (var shader in shaders)
{
uCount += shader.Bindings.UniformBufferBindings.Count;
sCount += shader.Bindings.StorageBufferBindings.Count;
tCount += shader.Bindings.TextureBindings.Count;
iCount += shader.Bindings.ImageBindings.Count;
}
DescriptorSetLayoutBinding* uLayoutBindings = stackalloc DescriptorSetLayoutBinding[uCount];
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[stagesCount];
DescriptorSetLayoutBinding* sLayoutBindings = stackalloc DescriptorSetLayoutBinding[sCount];
DescriptorSetLayoutBinding* tLayoutBindings = stackalloc DescriptorSetLayoutBinding[tCount];
DescriptorSetLayoutBinding* iLayoutBindings = stackalloc DescriptorSetLayoutBinding[iCount];
@ -180,22 +182,11 @@ namespace Ryujinx.Graphics.Vulkan
}
}
void SetStorage(DescriptorSetLayoutBinding* bindings, ref int start, int count)
{
bindings[start++] = new DescriptorSetLayoutBinding
{
Binding = (uint)start,
DescriptorType = DescriptorType.StorageBuffer,
DescriptorCount = (uint)count,
StageFlags = stageFlags
};
}
// TODO: Support buffer textures and images here.
// This is only used for the helper shaders on the backend, and we don't use buffer textures on them
// so far, so it's not really necessary right now.
Set(uLayoutBindings, DescriptorType.UniformBuffer, ref uIndex, shader.Bindings.UniformBufferBindings);
SetStorage(sLayoutBindings, ref sIndex, shader.Bindings.StorageBufferBindings.Count);
Set(sLayoutBindings, DescriptorType.StorageBuffer, ref sIndex, shader.Bindings.StorageBufferBindings);
Set(tLayoutBindings, DescriptorType.CombinedImageSampler, ref tIndex, shader.Bindings.TextureBindings);
Set(iLayoutBindings, DescriptorType.StorageImage, ref iIndex, shader.Bindings.ImageBindings);
}
@ -213,7 +204,7 @@ namespace Ryujinx.Graphics.Vulkan
{
SType = StructureType.DescriptorSetLayoutCreateInfo,
PBindings = sLayoutBindings,
BindingCount = (uint)stagesCount
BindingCount = (uint)sCount
};
var tDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo()

View file

@ -0,0 +1,64 @@
#version 450 core
#extension GL_EXT_shader_8bit_storage : require
layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
layout (std140, set = 0, binding = 0) uniform stride_arguments
{
ivec4 stride_arguments_data;
};
layout (std430, set = 1, binding = 1) buffer in_s
{
uint8_t[] in_data;
};
layout (std430, set = 1, binding = 2) buffer out_s
{
uint8_t[] out_data;
};
void main()
{
// Determine what slice of the stride copies this invocation will perform.
int sourceStride = stride_arguments_data.x;
int targetStride = stride_arguments_data.y;
int bufferSize = stride_arguments_data.z;
int sourceOffset = stride_arguments_data.w;
int strideRemainder = targetStride - sourceStride;
int invocations = int(gl_WorkGroupSize.x);
int copiesRequired = bufferSize / sourceStride;
// Find the copies that this invocation should perform.
// - Copies that all invocations perform.
int allInvocationCopies = copiesRequired / invocations;
// - Extra remainder copy that this invocation performs.
int index = int(gl_LocalInvocationID.x);
int extra = (index < (copiesRequired % invocations)) ? 1 : 0;
int copyCount = allInvocationCopies + extra;
// Finally, get the starting offset. Make sure to count extra copies.
int startCopy = allInvocationCopies * index + min(copiesRequired % invocations, index);
int srcOffset = sourceOffset + startCopy * sourceStride;
int dstOffset = startCopy * targetStride;
// Perform the copies for this region
for (int i=0; i<copyCount; i++) {
for (int j=0; j<sourceStride; j++) {
out_data[dstOffset++] = in_data[srcOffset++];
}
for (int j=0; j<strideRemainder; j++) {
out_data[dstOffset++] = uint8_t(0);
}
}
}

View file

@ -2,6 +2,249 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
{
static class ShaderBinaries
{
public static readonly byte[] ChangeBufferStrideShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x05, 0x01, 0x00, 0x0A, 0x00, 0x0D, 0x00, 0x8E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x60, 0x11, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C,
0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00,
0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00,
0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x45, 0x58, 0x54, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x65,
0x72, 0x5F, 0x38, 0x62, 0x69, 0x74, 0x5F, 0x73, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x00, 0x00,
0x04, 0x00, 0x0A, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x63, 0x70,
0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, 0x5F, 0x64, 0x69, 0x72,
0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x47, 0x4C, 0x5F, 0x47,
0x4F, 0x4F, 0x47, 0x4C, 0x45, 0x5F, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x5F, 0x64, 0x69,
0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00,
0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x72, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x07, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x5F, 0x61,
0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x5F, 0x61,
0x72, 0x67, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x73, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00,
0x13, 0x00, 0x00, 0x00, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x74, 0x72, 0x69, 0x64, 0x65,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x62, 0x75, 0x66, 0x66,
0x65, 0x72, 0x53, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x52, 0x65,
0x6D, 0x61, 0x69, 0x6E, 0x64, 0x65, 0x72, 0x00, 0x05, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x00,
0x69, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x00, 0x05, 0x00, 0x06, 0x00,
0x25, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72,
0x65, 0x64, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x29, 0x00, 0x00, 0x00, 0x61, 0x6C, 0x6C, 0x49,
0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x43, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00,
0x05, 0x00, 0x04, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x00, 0x00,
0x05, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x4C, 0x6F, 0x63, 0x61, 0x6C,
0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, 0x65, 0x78, 0x74, 0x72, 0x61, 0x00, 0x00, 0x00,
0x05, 0x00, 0x05, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x70, 0x79, 0x43, 0x6F, 0x75, 0x6E,
0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x42, 0x00, 0x00, 0x00, 0x73, 0x74, 0x61, 0x72,
0x74, 0x43, 0x6F, 0x70, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x73, 0x72, 0x63, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x52, 0x00, 0x00, 0x00, 0x64, 0x73, 0x74, 0x4F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
0x5F, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x6A, 0x00, 0x00, 0x00,
0x6F, 0x75, 0x74, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x6A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00,
0x70, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00,
0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00,
0x05, 0x00, 0x03, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
0x7B, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x30, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x6A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x6A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6C, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x6F, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x70, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00,
0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x72, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x72, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8D, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00,
0x0A, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x2F, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x3A, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x15, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1D, 0x00, 0x03, 0x00, 0x69, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00,
0x6A, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x6B, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x6B, 0x00, 0x00, 0x00,
0x6C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0x6F, 0x00, 0x00, 0x00,
0x68, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x70, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x71, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x71, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x75, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x06, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x08, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x13, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x10, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x41, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x1E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
0x27, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
0x2B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x32, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
0x38, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00,
0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x35, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x8B, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
0x47, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00,
0x2D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00,
0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
0x4A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00,
0x4F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
0x4D, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x51, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00,
0x42, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
0x53, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00,
0x55, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x57, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x57, 0x00, 0x00, 0x00,
0xF6, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5B, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
0xB1, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00,
0x5D, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
0x59, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x58, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x5F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00,
0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x65, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x66, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x3A, 0x00, 0x00, 0x00,
0x67, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00,
0x67, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
0x61, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00,
0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00,
0x6D, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00,
0x6E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00,
0x4C, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00,
0x73, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00,
0x74, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x75, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00,
0x72, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x68, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
0x75, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x6D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x63, 0x00, 0x00, 0x00,
0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00,
0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00,
0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00,
0xF9, 0x00, 0x02, 0x00, 0x60, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x62, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x7C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00,
0x7E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x80, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00,
0x3A, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00,
0xFA, 0x00, 0x04, 0x00, 0x83, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x84, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
0x85, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
0x52, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x71, 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00,
0x86, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x75, 0x00, 0x00, 0x00,
0x87, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x87, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x7F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x7C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x5A, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
0x06, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
0x3E, 0x00, 0x03, 0x00, 0x56, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
0x57, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x59, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00
};
public static readonly byte[] ColorBlitClearAlphaFragmentShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x1B, 0x00, 0x00, 0x00,

View file

@ -0,0 +1,131 @@
using BufferHandle = Ryujinx.Graphics.GAL.BufferHandle;
namespace Ryujinx.Graphics.Vulkan
{
internal struct VertexBufferState
{
public static VertexBufferState Null => new VertexBufferState(null, 0, 0, 0);
private readonly int _offset;
private readonly int _size;
private readonly int _stride;
private readonly BufferHandle _handle;
private Auto<DisposableBuffer> _buffer;
internal readonly int DescriptorIndex;
internal int AttributeScalarAlignment;
public VertexBufferState(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0)
{
_buffer = buffer;
_handle = BufferHandle.Null;
_offset = offset;
_size = size;
_stride = stride;
DescriptorIndex = descriptorIndex;
AttributeScalarAlignment = 1;
buffer?.IncrementReferenceCount();
}
public VertexBufferState(BufferHandle handle, int descriptorIndex, int offset, int size, int stride = 0)
{
// This buffer state may be rewritten at bind time, so it must be retrieved on bind.
_buffer = null;
_handle = handle;
_offset = offset;
_size = size;
_stride = stride;
DescriptorIndex = descriptorIndex;
AttributeScalarAlignment = 1;
}
public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state)
{
var autoBuffer = _buffer;
if (_handle != BufferHandle.Null)
{
// May need to restride the vertex buffer.
if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
{
autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);
int stride = (_stride + (alignment - 1)) & -alignment;
var buffer = autoBuffer.Get(cbs, _offset, _size).Value;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
0,
(ulong)(_size / _stride) * (ulong)stride,
(ulong)stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
}
_buffer = autoBuffer;
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
return;
}
else
{
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int _);
// The original stride must be reapplied in case it was rewritten.
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
}
}
if (autoBuffer != null)
{
var buffer = autoBuffer.Get(cbs, _offset, _size).Value;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
(ulong)_offset,
(ulong)_size,
(ulong)_stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset);
}
}
}
public bool BoundEquals(Auto<DisposableBuffer> buffer)
{
return _buffer == buffer;
}
public void Dispose()
{
// Only dispose if this buffer is not refetched on each bind.
if (_handle == BufferHandle.Null)
{
_buffer?.DecrementReferenceCount();
}
}
}
}

View file

@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Vulkan
"VK_EXT_fragment_shader_interlock",
"VK_EXT_index_type_uint8",
"VK_EXT_robustness2",
"VK_KHR_shader_float16_int8",
"VK_EXT_shader_subgroup_ballot",
"VK_EXT_subgroup_size_control",
"VK_NV_geometry_shader_passthrough"

View file

@ -188,11 +188,22 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
};
PhysicalDeviceShaderFloat16Int8FeaturesKHR featuresShaderInt8 = new PhysicalDeviceShaderFloat16Int8FeaturesKHR()
{
SType = StructureType.PhysicalDeviceShaderFloat16Int8Features
};
if (supportedExtensions.Contains("VK_EXT_robustness2"))
{
features2.PNext = &featuresRobustness2;
}
if (supportedExtensions.Contains("VK_KHR_shader_float16_int8"))
{
featuresShaderInt8.PNext = features2.PNext;
features2.PNext = &featuresShaderInt8;
}
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
Capabilities = new HardwareCapabilities(
@ -202,6 +213,7 @@ namespace Ryujinx.Graphics.Vulkan
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"),
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
supportedExtensions.Contains("VK_EXT_subgroup_size_control"),
featuresShaderInt8.ShaderInt8,
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName),
supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport,
@ -506,6 +518,24 @@ namespace Ryujinx.Graphics.Vulkan
PrintGpuInformation();
}
public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
{
if (Vendor != Vendor.Nvidia)
{
// Vulkan requires that vertex attributes are globally aligned by their component size,
// so buffer strides that don't divide by the largest scalar element are invalid.
// Guest applications do this, NVIDIA GPUs are OK with it, others are not.
alignment = attrScalarAlignment;
return true;
}
alignment = 1;
return false;
}
public void PreFrame()
{
_syncManager.Cleanup();