diff --git a/Ryujinx.Ava/Assets/Locales/en_US.json b/Ryujinx.Ava/Assets/Locales/en_US.json
index 3a841157f6..3a72be34fd 100644
--- a/Ryujinx.Ava/Assets/Locales/en_US.json
+++ b/Ryujinx.Ava/Assets/Locales/en_US.json
@@ -587,10 +587,12 @@
"SettingsTabGraphicsPreferredGpu": "Preferred GPU",
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
- "SettingsGpuBackendRestartMessage": "Graphics Backend or Gpu settings have been modified. This will require a restart to be applied",
+ "SettingsGpuBackendRestartMessage": "Graphics Backend or GPU settings have been modified. This will require a restart to be applied",
"SettingsGpuBackendRestartSubMessage": "Do you want to restart now?",
"RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?",
"SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:",
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
- "VolumeShort": "Vol"
+ "VolumeShort": "Vol",
+ "SettingsEnableMacroHLE": "Enable Macro HLE",
+ "SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure."
}
diff --git a/Ryujinx.Ava/Assets/Locales/pt_BR.json b/Ryujinx.Ava/Assets/Locales/pt_BR.json
index c5dbe49a41..01f6290952 100644
--- a/Ryujinx.Ava/Assets/Locales/pt_BR.json
+++ b/Ryujinx.Ava/Assets/Locales/pt_BR.json
@@ -556,5 +556,7 @@
"SettingsSelectThemeFileDialogTitle" : "Selecionar arquivo do tema",
"SettingsXamlThemeFile" : "Arquivo de tema Xaml",
"SettingsTabHotkeysResScaleUpHotkey": "Aumentar a resolução:",
- "SettingsTabHotkeysResScaleDownHotkey": "Diminuir a resolução:"
+ "SettingsTabHotkeysResScaleDownHotkey": "Diminuir a resolução:",
+ "SettingsEnableMacroHLE": "Habilitar emulação de alto nível para Macros",
+ "SettingsEnableMacroHLETooltip": "Habilita emulação de alto nível de códigos Macro da GPU.\n\nMelhora a performance, mas pode causar problemas gráficos em alguns jogos.\n\nEm caso de dúvida, deixe ATIVADO."
}
diff --git a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
index 088bf3ac9e..584627417a 100644
--- a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
+++ b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
@@ -134,6 +134,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public bool ExpandDramSize { get; set; }
public bool EnableShaderCache { get; set; }
public bool EnableTextureRecompression { get; set; }
+ public bool EnableMacroHLE { get; set; }
public bool EnableFileLog { get; set; }
public bool EnableStub { get; set; }
public bool EnableInfo { get; set; }
@@ -335,6 +336,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
ExpandDramSize = config.System.ExpandRam;
EnableShaderCache = config.Graphics.EnableShaderCache;
EnableTextureRecompression = config.Graphics.EnableTextureRecompression;
+ EnableMacroHLE = config.Graphics.EnableMacroHLE;
EnableFileLog = config.Logger.EnableFileLog;
EnableStub = config.Logger.EnableStub;
EnableInfo = config.Logger.EnableInfo;
@@ -418,6 +420,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
config.Graphics.EnableVsync.Value = EnableVsync;
config.Graphics.EnableShaderCache.Value = EnableShaderCache;
config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression;
+ config.Graphics.EnableMacroHLE.Value = EnableMacroHLE;
config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex;
config.System.EnablePtc.Value = EnablePptc;
config.System.EnableInternetAccess.Value = EnableInternetAccess;
diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
index 774178d662..0e03803ba6 100644
--- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
+++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs
@@ -526,6 +526,7 @@ namespace Ryujinx.Ava.Ui.Windows
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression;
+ GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
}
public void LoadHotKeys()
diff --git a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml
index 55fe83656c..0946a2eb90 100644
--- a/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml
+++ b/Ryujinx.Ava/Ui/Windows/SettingsWindow.axaml
@@ -568,6 +568,10 @@
ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}">
+
+
+
(memory), threaded, renderer);
_lookup[(int)CommandType.DrawIndexed] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
DrawIndexedCommand.Run(ref GetCommand(memory), threaded, renderer);
+ _lookup[(int)CommandType.DrawIndexedIndirect] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
+ DrawIndexedIndirectCommand.Run(ref GetCommand(memory), threaded, renderer);
+ _lookup[(int)CommandType.DrawIndexedIndirectCount] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
+ DrawIndexedIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer);
+ _lookup[(int)CommandType.DrawIndirect] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
+ DrawIndirectCommand.Run(ref GetCommand(memory), threaded, renderer);
+ _lookup[(int)CommandType.DrawIndirectCount] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
+ DrawIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer);
_lookup[(int)CommandType.DrawTexture] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
DrawTextureCommand.Run(ref GetCommand(memory), threaded, renderer);
_lookup[(int)CommandType.EndHostConditionalRendering] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
EndHostConditionalRenderingCommand.Run(renderer);
_lookup[(int)CommandType.EndTransformFeedback] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
EndTransformFeedbackCommand.Run(ref GetCommand(memory), threaded, renderer);
- _lookup[(int)CommandType.MultiDrawIndirectCount] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
- MultiDrawIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer);
- _lookup[(int)CommandType.MultiDrawIndexedIndirectCount] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
- MultiDrawIndexedIndirectCountCommand.Run(ref GetCommand(memory), threaded, renderer);
_lookup[(int)CommandType.SetAlphaTest] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
SetAlphaTestCommand.Run(ref GetCommand(memory), threaded, renderer);
_lookup[(int)CommandType.SetBlendState] = (Span memory, ThreadedRenderer threaded, IRenderer renderer) =>
diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs
index c25f0834ec..c199ff34c2 100644
--- a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs
+++ b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs
@@ -52,11 +52,13 @@
DispatchCompute,
Draw,
DrawIndexed,
+ DrawIndexedIndirect,
+ DrawIndexedIndirectCount,
+ DrawIndirect,
+ DrawIndirectCount,
DrawTexture,
EndHostConditionalRendering,
EndTransformFeedback,
- MultiDrawIndirectCount,
- MultiDrawIndexedIndirectCount,
SetAlphaTest,
SetBlendState,
SetDepthBias,
diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs
new file mode 100644
index 0000000000..3a47e96216
--- /dev/null
+++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCommand.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.Graphics.GAL.Multithreading.Commands
+{
+ struct DrawIndexedIndirectCommand : IGALCommand
+ {
+ public CommandType CommandType => CommandType.DrawIndexedIndirect;
+ private BufferRange _indirectBuffer;
+
+ public void Set(BufferRange indirectBuffer)
+ {
+ _indirectBuffer = indirectBuffer;
+ }
+
+ public static void Run(ref DrawIndexedIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ {
+ renderer.Pipeline.DrawIndexedIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer));
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndexedIndirectCountCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs
similarity index 70%
rename from Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndexedIndirectCountCommand.cs
rename to Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs
index 6798f8cc5c..79d9792e94 100644
--- a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndexedIndirectCountCommand.cs
+++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndexedIndirectCountCommand.cs
@@ -1,8 +1,8 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
- struct MultiDrawIndexedIndirectCountCommand : IGALCommand
+ struct DrawIndexedIndirectCountCommand : IGALCommand
{
- public CommandType CommandType => CommandType.MultiDrawIndexedIndirectCount;
+ public CommandType CommandType => CommandType.DrawIndexedIndirectCount;
private BufferRange _indirectBuffer;
private BufferRange _parameterBuffer;
private int _maxDrawCount;
@@ -16,9 +16,9 @@
_stride = stride;
}
- public static void Run(ref MultiDrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ public static void Run(ref DrawIndexedIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
- renderer.Pipeline.MultiDrawIndexedIndirectCount(
+ renderer.Pipeline.DrawIndexedIndirectCount(
threaded.Buffers.MapBufferRange(command._indirectBuffer),
threaded.Buffers.MapBufferRange(command._parameterBuffer),
command._maxDrawCount,
diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs
new file mode 100644
index 0000000000..73414e4415
--- /dev/null
+++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCommand.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.Graphics.GAL.Multithreading.Commands
+{
+ struct DrawIndirectCommand : IGALCommand
+ {
+ public CommandType CommandType => CommandType.DrawIndirect;
+ private BufferRange _indirectBuffer;
+
+ public void Set(BufferRange indirectBuffer)
+ {
+ _indirectBuffer = indirectBuffer;
+ }
+
+ public static void Run(ref DrawIndirectCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ {
+ renderer.Pipeline.DrawIndirect(threaded.Buffers.MapBufferRange(command._indirectBuffer));
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndirectCountCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs
similarity index 71%
rename from Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndirectCountCommand.cs
rename to Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs
index 7a9d07f330..96f60f4aa1 100644
--- a/Ryujinx.Graphics.GAL/Multithreading/Commands/MultiDrawIndirectCountCommand.cs
+++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/DrawIndirectCountCommand.cs
@@ -1,8 +1,8 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
- struct MultiDrawIndirectCountCommand : IGALCommand
+ struct DrawIndirectCountCommand : IGALCommand
{
- public CommandType CommandType => CommandType.MultiDrawIndirectCount;
+ public CommandType CommandType => CommandType.DrawIndirectCount;
private BufferRange _indirectBuffer;
private BufferRange _parameterBuffer;
private int _maxDrawCount;
@@ -16,9 +16,9 @@
_stride = stride;
}
- public static void Run(ref MultiDrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ public static void Run(ref DrawIndirectCountCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
- renderer.Pipeline.MultiDrawIndirectCount(
+ renderer.Pipeline.DrawIndirectCount(
threaded.Buffers.MapBufferRange(command._indirectBuffer),
threaded.Buffers.MapBufferRange(command._parameterBuffer),
command._maxDrawCount,
diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs
index 723d29f1f2..52d699335e 100644
--- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs
+++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs
@@ -83,6 +83,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
+ public void DrawIndexedIndirect(BufferRange indirectBuffer)
+ {
+ _renderer.New().Set(indirectBuffer);
+ _renderer.QueueCommand();
+ }
+
+ public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
+ {
+ _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
+ _renderer.QueueCommand();
+ }
+
+ public void DrawIndirect(BufferRange indirectBuffer)
+ {
+ _renderer.New().Set(indirectBuffer);
+ _renderer.QueueCommand();
+ }
+
+ public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
+ {
+ _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
+ _renderer.QueueCommand();
+ }
+
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
{
_renderer.New().Set(Ref(texture), Ref(sampler), srcRegion, dstRegion);
@@ -101,18 +125,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
- public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
- {
- _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
- _renderer.QueueCommand();
- }
-
- public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
- {
- _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride);
- _renderer.QueueCommand();
- }
-
public void SetAlphaTest(bool enable, float reference, CompareOp op)
{
_renderer.New().Set(enable, reference, op);
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
index 1d054969a1..12a3ac028f 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
@@ -62,10 +62,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
}
- if (_hleFunction == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
+ // We don't consume the parameter buffer value, so we don't need to flush it.
+ // Doing so improves performance if the value was written by a GPU shader.
+ if (_hleFunction == MacroHLEFunctionName.DrawElementsIndirect)
+ {
+ context.GPFifo.SetFlushSkips(1);
+ }
+ else if (_hleFunction == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
{
- // We don't consume the parameter buffer value, so we don't need to flush it.
- // Doing so improves performance if the value was written by a GPU shader.
context.GPFifo.SetFlushSkips(2);
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs
index 5f238a7185..8630bbc426 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs
@@ -16,6 +16,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
private const int ColorStructSize = 0x40;
private const int ZetaLayerCountOffset = 0x1230;
+ private const int IndirectDataEntrySize = 0x10;
+ private const int IndirectIndexedDataEntrySize = 0x14;
+
private readonly GPFifoProcessor _processor;
private readonly MacroHLEFunctionName _functionName;
@@ -27,9 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
///
/// Creates a new instance of the HLE macro handler.
///
- /// GPU context the macro is being executed on
- /// GPU memory manager
- /// 3D engine where this macro is being called
+ /// GPU GP FIFO command processor
/// Name of the HLE macro function to be called
public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName)
{
@@ -55,12 +56,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
case MacroHLEFunctionName.ClearDepthStencil:
ClearDepthStencil(state, arg0);
break;
+ case MacroHLEFunctionName.DrawArraysInstanced:
+ DrawArraysInstanced(state, arg0);
+ break;
+ case MacroHLEFunctionName.DrawElementsInstanced:
+ DrawElementsInstanced(state, arg0);
+ break;
+ case MacroHLEFunctionName.DrawElementsIndirect:
+ DrawElementsIndirect(state, arg0);
+ break;
case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
MultiDrawElementsIndirectCount(state, arg0);
break;
default:
throw new NotImplementedException(_functionName.ToString());
}
+
+ // It should be empty at this point, but clear it just to be safe.
+ Fifo.Clear();
}
///
@@ -89,7 +102,118 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
///
- /// Performs a indirect multi-draw, with parameters from a GPU buffer.
+ /// Performs a draw.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void DrawArraysInstanced(IDeviceState state, int arg0)
+ {
+ var topology = (PrimitiveTopology)arg0;
+
+ var count = FetchParam();
+ var instanceCount = FetchParam();
+ var firstVertex = FetchParam();
+ var firstInstance = FetchParam();
+
+ if (ShouldSkipDraw(state, instanceCount.Word))
+ {
+ return;
+ }
+
+ _processor.ThreedClass.Draw(
+ topology,
+ count.Word,
+ instanceCount.Word,
+ 0,
+ firstVertex.Word,
+ firstInstance.Word,
+ indexed: false);
+ }
+
+ ///
+ /// Performs a indexed draw.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void DrawElementsInstanced(IDeviceState state, int arg0)
+ {
+ var topology = (PrimitiveTopology)arg0;
+
+ var count = FetchParam();
+ var instanceCount = FetchParam();
+ var firstIndex = FetchParam();
+ var firstVertex = FetchParam();
+ var firstInstance = FetchParam();
+
+ if (ShouldSkipDraw(state, instanceCount.Word))
+ {
+ return;
+ }
+
+ _processor.ThreedClass.Draw(
+ topology,
+ count.Word,
+ instanceCount.Word,
+ firstIndex.Word,
+ firstVertex.Word,
+ firstInstance.Word,
+ indexed: true);
+ }
+
+ ///
+ /// Performs a indirect indexed draw, with parameters from a GPU buffer.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void DrawElementsIndirect(IDeviceState state, int arg0)
+ {
+ var topology = (PrimitiveTopology)arg0;
+
+ var count = FetchParam();
+ var instanceCount = FetchParam();
+ var firstIndex = FetchParam();
+ var firstVertex = FetchParam();
+ var firstInstance = FetchParam();
+
+ ulong indirectBufferGpuVa = count.GpuVa;
+
+ var bufferCache = _processor.MemoryManager.Physical.BufferCache;
+
+ bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
+
+ if (useBuffer)
+ {
+ int indexCount = firstIndex.Word + count.Word;
+
+ _processor.ThreedClass.DrawIndirect(
+ topology,
+ indirectBufferAddress,
+ 0,
+ 1,
+ IndirectIndexedDataEntrySize,
+ indexCount,
+ Threed.IndirectDrawType.DrawIndexedIndirect);
+ }
+ else
+ {
+ if (ShouldSkipDraw(state, instanceCount.Word))
+ {
+ return;
+ }
+
+ _processor.ThreedClass.Draw(
+ topology,
+ count.Word,
+ instanceCount.Word,
+ firstIndex.Word,
+ firstVertex.Word,
+ firstInstance.Word,
+ indexed: true);
+ }
+ }
+
+ ///
+ /// Performs a indirect indexed multi-draw, with parameters from a GPU buffer.
///
/// GPU state at the time of the call
/// First argument of the call
@@ -132,8 +256,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
return;
}
- int indirectBufferSize = maxDrawCount * stride;
-
ulong indirectBufferGpuVa = 0;
int indexCount = 0;
@@ -142,8 +264,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
var count = FetchParam();
var instanceCount = FetchParam();
var firstIndex = FetchParam();
- var baseVertex = FetchParam();
- var baseInstance = FetchParam();
+ var firstVertex = FetchParam();
+ var firstInstance = FetchParam();
if (i == 0)
{
@@ -161,15 +283,32 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
}
- // It should be empty at this point, but clear it just to be safe.
- Fifo.Clear();
-
var bufferCache = _processor.MemoryManager.Physical.BufferCache;
- var parameterBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, parameterBufferGpuVa, 4);
- var indirectBuffer = bufferCache.GetGpuBufferRange(_processor.MemoryManager, indirectBufferGpuVa, (ulong)indirectBufferSize);
+ ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
- _processor.ThreedClass.MultiDrawIndirectCount(indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride);
+ ulong indirectBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize);
+ ulong parameterBufferAddress = bufferCache.TranslateAndCreateBuffer(_processor.MemoryManager, parameterBufferGpuVa, 4);
+
+ _processor.ThreedClass.DrawIndirect(
+ topology,
+ indirectBufferAddress,
+ parameterBufferAddress,
+ maxDrawCount,
+ stride,
+ indexCount,
+ Threed.IndirectDrawType.DrawIndexedIndirectCount);
+ }
+
+ ///
+ /// Checks if the draw should be skipped, because the masked instance count is zero.
+ ///
+ /// Current GPU state
+ /// Draw instance count
+ /// True if the draw should be skipped, false otherwise
+ private static bool ShouldSkipDraw(IDeviceState state, int instanceCount)
+ {
+ return (Read(state, 0xd1b) & instanceCount) == 0;
}
///
@@ -189,14 +328,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
///
- /// Performs a GPU method call.
+ /// Reads data from a GPU register.
///
/// Current GPU state
- /// Address, in words, of the method
- /// Call argument
- private static void Send(IDeviceState state, int methAddr, int value)
+ /// Register offset to read
+ /// GPU register value
+ private static int Read(IDeviceState state, int reg)
{
- state.Write(methAddr * 4, value);
+ return state.Read(reg * 4);
}
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs
index 4cce07fa0c..751867fc78 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs
@@ -8,6 +8,9 @@
None,
ClearColor,
ClearDepthStencil,
+ DrawArraysInstanced,
+ DrawElementsInstanced,
+ DrawElementsIndirect,
MultiDrawElementsIndirectCount
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
index c5d9884888..ab6f54efbe 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
@@ -44,17 +44,29 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
}
}
- private static readonly TableEntry[] Table = new TableEntry[]
+ private static readonly TableEntry[] _table = new TableEntry[]
{
new TableEntry(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28),
new TableEntry(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24),
+ new TableEntry(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48),
+ new TableEntry(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c),
+ new TableEntry(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c),
new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C)
};
+ ///
+ /// Checks if the host supports all features required by the HLE macro.
+ ///
+ /// Host capabilities
+ /// Name of the HLE macro to be checked
+ /// True if the host supports the HLE macro, false otherwise
private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name)
{
if (name == MacroHLEFunctionName.ClearColor ||
- name == MacroHLEFunctionName.ClearDepthStencil)
+ name == MacroHLEFunctionName.ClearDepthStencil ||
+ name == MacroHLEFunctionName.DrawArraysInstanced ||
+ name == MacroHLEFunctionName.DrawElementsInstanced ||
+ name == MacroHLEFunctionName.DrawElementsIndirect)
{
return true;
}
@@ -77,15 +89,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
{
var mc = MemoryMarshal.Cast(code);
- for (int i = 0; i < Table.Length; i++)
+ for (int i = 0; i < _table.Length; i++)
{
- ref var entry = ref Table[i];
+ ref var entry = ref _table[i];
var hash = XXHash128.ComputeHash(mc.Slice(0, entry.Length));
if (hash == entry.Hash)
{
- name = entry.Name;
- return IsMacroHLESupported(caps, name);
+ if (IsMacroHLESupported(caps, entry.Name))
+ {
+ name = entry.Name;
+ return true;
+ }
+
+ break;
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
index 85f669850f..a6b62a4a8e 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
@@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
else
{
evt.Flush();
- return (memoryManager.Read(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
+ return (memoryManager.Read(gpuVa, true) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
}
}
@@ -108,8 +108,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
evt?.Flush();
evt2?.Flush();
- ulong x = memoryManager.Read(gpuVa);
- ulong y = memoryManager.Read(gpuVa + 16);
+ ulong x = memoryManager.Read(gpuVa, true);
+ ulong y = memoryManager.Read(gpuVa + 16, true);
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
index e02c8cdc6b..a7acb4691c 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
@@ -1,5 +1,6 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types;
+using Ryujinx.Graphics.Gpu.Memory;
using System;
namespace Ryujinx.Graphics.Gpu.Engine.Threed
@@ -9,6 +10,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
class DrawManager
{
+ // Since we don't know the index buffer size for indirect draws,
+ // we must assume a minimum and maximum size and use that for buffer data update purposes.
+ private const int MinIndirectIndexCount = 0x10000;
+ private const int MaxIndirectIndexCount = 0x4000000;
+
private readonly GpuContext _context;
private readonly GpuChannel _channel;
private readonly DeviceStateWithShadow _state;
@@ -28,6 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private int _instanceIndex;
+ private const int VertexBufferFirstMethodOffset = 0x35d;
private const int IndexBufferCountMethodOffset = 0x5f8;
///
@@ -237,6 +244,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_instanceIndex = 0;
}
+ UpdateTopology(topology);
+ }
+
+ ///
+ /// Updates the current primitive topology if needed.
+ ///
+ /// New primitive topology
+ private void UpdateTopology(PrimitiveTopology topology)
+ {
if (_drawState.Topology != topology || !_topologySet)
{
_context.Renderer.Pipeline.SetPrimitiveTopology(topology);
@@ -383,28 +399,27 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
///
- /// Performs a indirect multi-draw, with parameters from a GPU buffer.
+ /// Performs a indexed or non-indexed draw.
///
/// 3D engine where this method is being called
/// Primitive topology
- /// GPU buffer with the draw parameters, such as count, first index, etc
- /// GPU buffer with the draw count
- /// Maximum number of draws that can be made
- /// Distance in bytes between each element on the array
- public void MultiDrawIndirectCount(
+ /// Index count for indexed draws, vertex count for non-indexed draws
+ /// Instance count
+ /// First index on the index buffer for indexed draws, ignored for non-indexed draws
+ /// First vertex on the vertex buffer
+ /// First instance
+ /// True if the draw is indexed, false otherwise
+ public void Draw(
ThreedClass engine,
- int indexCount,
PrimitiveTopology topology,
- BufferRange indirectBuffer,
- BufferRange parameterBuffer,
- int maxDrawCount,
- int stride)
+ int count,
+ int instanceCount,
+ int firstIndex,
+ int firstVertex,
+ int firstInstance,
+ bool indexed)
{
- engine.Write(IndexBufferCountMethodOffset * 4, indexCount);
-
- _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
- _drawState.Topology = topology;
- _topologySet = true;
+ UpdateTopology(topology);
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
_context,
@@ -418,21 +433,133 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
return;
}
- _drawState.FirstIndex = _state.State.IndexBufferState.First;
- _drawState.IndexCount = indexCount;
-
- engine.UpdateState();
-
- if (_drawState.DrawIndexed)
+ if (indexed)
{
- _context.Renderer.Pipeline.MultiDrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
+ _drawState.FirstIndex = firstIndex;
+ _drawState.IndexCount = count;
+ _state.State.FirstVertex = (uint)firstVertex;
+ engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
}
else
{
- _context.Renderer.Pipeline.MultiDrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
+ _state.State.VertexBufferDrawState.First = firstVertex;
+ _state.State.VertexBufferDrawState.Count = count;
+ engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
+ }
+
+ _state.State.FirstInstance = (uint)firstInstance;
+
+ _drawState.DrawIndexed = indexed;
+ _drawState.HasConstantBufferDrawParameters = true;
+
+ engine.UpdateState();
+
+ if (indexed)
+ {
+ _context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance);
+ _state.State.FirstVertex = 0;
+ }
+ else
+ {
+ _context.Renderer.Pipeline.Draw(count, instanceCount, firstVertex, firstInstance);
+ }
+
+ _state.State.FirstInstance = 0;
+
+ _drawState.DrawIndexed = false;
+ _drawState.HasConstantBufferDrawParameters = false;
+
+ if (renderEnable == ConditionalRenderEnabled.Host)
+ {
+ _context.Renderer.Pipeline.EndHostConditionalRendering();
+ }
+ }
+
+ ///
+ /// Performs a indirect draw, with parameters from a GPU buffer.
+ ///
+ /// 3D engine where this method is being called
+ /// Primitive topology
+ /// Address of the buffer with the draw parameters, such as count, first index, etc
+ /// Address of the buffer with the draw count
+ /// Maximum number of draws that can be made
+ /// Distance in bytes between each entry on the data pointed to by
+ /// Maximum number of indices that the draw can consume
+ /// Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count
+ public void DrawIndirect(
+ ThreedClass engine,
+ PrimitiveTopology topology,
+ ulong indirectBufferAddress,
+ ulong parameterBufferAddress,
+ int maxDrawCount,
+ int stride,
+ int indexCount,
+ IndirectDrawType drawType)
+ {
+ UpdateTopology(topology);
+
+ ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
+ _context,
+ _channel.MemoryManager,
+ _state.State.RenderEnableAddress,
+ _state.State.RenderEnableCondition);
+
+ if (renderEnable == ConditionalRenderEnabled.False)
+ {
+ _drawState.DrawIndexed = false;
+ return;
+ }
+
+ PhysicalMemory memory = _channel.MemoryManager.Physical;
+
+ bool hasCount = (drawType & IndirectDrawType.Count) != 0;
+ bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
+
+ if (indexed)
+ {
+ indexCount = Math.Clamp(indexCount, MinIndirectIndexCount, MaxIndirectIndexCount);
+ _drawState.FirstIndex = 0;
+ _drawState.IndexCount = indexCount;
+ engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
+ }
+
+ _drawState.DrawIndexed = indexed;
+ _drawState.DrawIndirect = true;
+ _drawState.HasConstantBufferDrawParameters = true;
+
+ engine.UpdateState();
+
+ if (hasCount)
+ {
+ var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)maxDrawCount * (ulong)stride);
+ var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferAddress, 4);
+
+ if (indexed)
+ {
+ _context.Renderer.Pipeline.DrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
+ }
+ else
+ {
+ _context.Renderer.Pipeline.DrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
+ }
+ }
+ else
+ {
+ var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferAddress, (ulong)stride);
+
+ if (indexed)
+ {
+ _context.Renderer.Pipeline.DrawIndexedIndirect(indirectBuffer);
+ }
+ else
+ {
+ _context.Renderer.Pipeline.DrawIndirect(indirectBuffer);
+ }
}
_drawState.DrawIndexed = false;
+ _drawState.DrawIndirect = false;
+ _drawState.HasConstantBufferDrawParameters = false;
if (renderEnable == ConditionalRenderEnabled.Host)
{
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
index ff186acc98..fd1cb0ea61 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
@@ -22,6 +22,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
public bool DrawIndexed;
+ ///
+ /// Indicates if the next draw will be a indirect draw.
+ ///
+ public bool DrawIndirect;
+
///
/// Indicates if any of the currently used vertex shaders reads the instance ID.
///
@@ -32,6 +37,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
public bool IsAnyVbInstanced;
+ ///
+ /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
+ ///
+ public bool HasConstantBufferDrawParameters;
+
///
/// Primitive topology for the next draw.
///
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs
new file mode 100644
index 0000000000..d78aa49828
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/IndirectDrawType.cs
@@ -0,0 +1,38 @@
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ ///
+ /// Indirect draw type, which can be indexed or non-indexed, with or without a draw count.
+ ///
+ enum IndirectDrawType
+ {
+ ///
+ /// Non-indexed draw without draw count.
+ ///
+ DrawIndirect = 0,
+
+ ///
+ /// Indexed draw without draw count.
+ ///
+ DrawIndexedIndirect = Indexed,
+
+ ///
+ /// Non-indexed draw with draw count.
+ ///
+ DrawIndirectCount = Count,
+
+ ///
+ /// Indexed draw with draw count.
+ ///
+ DrawIndexedIndirectCount = Indexed | Count,
+
+ ///
+ /// Indexed flag.
+ ///
+ Indexed = 1 << 0,
+
+ ///
+ /// Draw count flag.
+ ///
+ Count = 1 << 1
+ }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index fdf8f82224..3f71172c05 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -34,10 +34,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private ProgramPipelineState _pipeline;
+ private bool _vsUsesDrawParameters;
private bool _vtgWritesRtLayer;
private byte _vsClipDistancesWritten;
private bool _prevDrawIndexed;
+ private bool _prevDrawIndirect;
private IndexType _prevIndexType;
private uint _prevFirstVertex;
private bool _prevTfEnable;
@@ -210,7 +212,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// of the shader for the new state.
if (_shaderSpecState != null)
{
- if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), false))
+ if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState(), _vsUsesDrawParameters, false))
{
ForceShaderUpdate();
}
@@ -237,6 +239,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_prevDrawIndexed = _drawState.DrawIndexed;
}
+ // Some draw parameters are used to restrict the vertex buffer size,
+ // but they can't be used on indirect draws because their values are unknown in this case.
+ // When switching between indirect and non-indirect draw, we need to
+ // make sure the vertex buffer sizes are still correct.
+ if (_drawState.DrawIndirect != _prevDrawIndirect)
+ {
+ _updateTracker.ForceDirty(VertexBufferStateIndex);
+ }
+
// In some cases, the index type is also used to guess the
// vertex buffer size, so we must update it if the type changed too.
if (_drawState.DrawIndexed &&
@@ -938,6 +949,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.IsAnyVbInstanced = false;
+ bool drawIndexed = _drawState.DrawIndexed;
+ bool drawIndirect = _drawState.DrawIndirect;
+
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
{
var vertexBuffer = _state.State.VertexBufferState[index];
@@ -965,14 +979,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
ulong vbSize = endAddress.Pack() - address + 1;
ulong size;
- if (_drawState.IbStreamer.HasInlineIndexData || _drawState.DrawIndexed || stride == 0 || instanced)
+ if (_drawState.IbStreamer.HasInlineIndexData || drawIndexed || stride == 0 || instanced)
{
// This size may be (much) larger than the real vertex buffer size.
// Avoid calculating it this way, unless we don't have any other option.
size = vbSize;
- if (stride > 0 && indexTypeSmall && _drawState.DrawIndexed && !instanced)
+ if (stride > 0 && indexTypeSmall && drawIndexed && !drawIndirect && !instanced)
{
// If the index type is a small integer type, then we might be still able
// to reduce the vertex buffer size based on the maximum possible index value.
@@ -1207,6 +1221,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
_drawState.VsUsesInstanceId = gs.Shaders[1]?.Info.UsesInstanceId ?? false;
+ _vsUsesDrawParameters = gs.Shaders[1]?.Info.UsesDrawParameters ?? false;
_vsClipDistancesWritten = gs.Shaders[1]?.Info.ClipDistancesWritten ?? 0;
if (oldVsClipDistancesWritten != _vsClipDistancesWritten)
@@ -1222,6 +1237,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);
}
+ ///
+ /// Updates bindings consumed by the shader stage on the texture and buffer managers.
+ ///
+ /// Shader stage to have the bindings updated
+ /// Shader stage bindings info
private void UpdateStageBindings(int stage, ShaderProgramInfo info)
{
_currentProgramInfo[stage] = info;
@@ -1340,7 +1360,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state.State.AlphaTestEnable,
_state.State.AlphaTestFunc,
_state.State.AlphaTestRef,
- ref attributeTypes);
+ ref attributeTypes,
+ _drawState.HasConstantBufferDrawParameters);
}
///
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
index 8e222e719b..106a6f3f4c 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
@@ -497,6 +497,50 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
return 0;
}
+ ///
+ /// Performs a indexed or non-indexed draw.
+ ///
+ /// Primitive topology
+ /// Index count for indexed draws, vertex count for non-indexed draws
+ /// Instance count
+ /// First index on the index buffer for indexed draws, ignored for non-indexed draws
+ /// First vertex on the vertex buffer
+ /// First instance
+ /// True if the draw is indexed, false otherwise
+ public void Draw(
+ PrimitiveTopology topology,
+ int count,
+ int instanceCount,
+ int firstIndex,
+ int firstVertex,
+ int firstInstance,
+ bool indexed)
+ {
+ _drawManager.Draw(this, topology, count, instanceCount, firstIndex, firstVertex, firstInstance, indexed);
+ }
+
+ ///
+ /// Performs a indirect draw, with parameters from a GPU buffer.
+ ///
+ /// Primitive topology
+ /// Address of the buffer with the draw parameters, such as count, first index, etc
+ /// Address of the buffer with the draw count
+ /// Maximum number of draws that can be made
+ /// Distance in bytes between each entry on the data pointed to by
+ /// Maximum number of indices that the draw can consume
+ /// Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count
+ public void DrawIndirect(
+ PrimitiveTopology topology,
+ ulong indirectBufferAddress,
+ ulong parameterBufferAddress,
+ int maxDrawCount,
+ int stride,
+ int indexCount,
+ IndirectDrawType drawType)
+ {
+ _drawManager.DrawIndirect(this, topology, indirectBufferAddress, parameterBufferAddress, maxDrawCount, stride, indexCount, drawType);
+ }
+
///
/// Clears the current color and depth-stencil buffers.
/// Which buffers should be cleared can also specified with the arguments.
@@ -507,25 +551,5 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
_drawManager.Clear(this, argument, layerCount);
}
-
- ///
- /// Performs a indirect multi-draw, with parameters from a GPU buffer.
- ///
- /// Index Buffer Count
- /// Primitive topology
- /// GPU buffer with the draw parameters, such as count, first index, etc
- /// GPU buffer with the draw count
- /// Maximum number of draws that can be made
- /// Distance in bytes between each element on the array
- public void MultiDrawIndirectCount(
- int indexCount,
- PrimitiveTopology topology,
- BufferRange indirectBuffer,
- BufferRange parameterBuffer,
- int maxDrawCount,
- int stride)
- {
- _drawManager.MultiDrawIndirectCount(this, indexCount, topology, indirectBuffer, parameterBuffer, maxDrawCount, stride);
- }
}
}
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs
index 14b177aa4b..894d009c0e 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs
@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
private Buffer[] _bufferOverlaps;
private readonly Dictionary _dirtyCache;
+ private readonly Dictionary _modifiedCache;
public event Action NotifyBuffersModified;
@@ -45,6 +46,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
_bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
_dirtyCache = new Dictionary();
+
+ // There are a lot more entries on the modified cache, so it is separate from the one for ForceDirty.
+ _modifiedCache = new Dictionary();
}
///
@@ -145,6 +149,30 @@ namespace Ryujinx.Graphics.Gpu.Memory
result.Buffer.ForceDirty(result.Address, size);
}
+ ///
+ /// Checks if the given buffer range has been GPU modifed.
+ ///
+ /// GPU memory manager where the buffer is mapped
+ /// Start GPU virtual address of the buffer
+ /// Size in bytes of the buffer
+ /// True if modified, false otherwise
+ public bool CheckModified(MemoryManager memoryManager, ulong gpuVa, ulong size, out ulong outAddr)
+ {
+ if (!_modifiedCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
+ result.EndGpuAddress < gpuVa + size ||
+ result.UnmappedSequence != result.Buffer.UnmappedSequence)
+ {
+ ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
+ result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
+
+ _modifiedCache[gpuVa] = result;
+ }
+
+ outAddr = result.Address;
+
+ return result.Buffer.IsModified(result.Address, size);
+ }
+
///
/// Creates a new buffer for the specified range, if needed.
/// If a buffer where this range can be fully contained already exists,
@@ -326,18 +354,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
buffer.SignalModified(address, size);
}
- ///
- /// Gets a buffer sub-range for a given GPU memory range.
- ///
- /// GPU memory manager where the buffer is mapped
- /// Start GPU virtual address of the buffer
- /// Size in bytes of the buffer
- /// The buffer sub-range for the given range
- public BufferRange GetGpuBufferRange(MemoryManager memoryManager, ulong gpuVa, ulong size)
- {
- return GetBufferRange(TranslateAndCreateBuffer(memoryManager, gpuVa, size), size);
- }
-
///
/// Gets a buffer sub-range starting at a given memory address.
///
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
index 68ff4f2a77..98748bf62d 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGpuAccessor.cs
@@ -129,6 +129,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.ConstantBufferUse[_stageIndex];
}
+ ///
+ public bool QueryHasConstantBufferDrawParameters()
+ {
+ return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
+ }
+
///
public InputTopology QueryPrimitiveTopology()
{
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
index e728c48c40..0bdf49499b 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
- private const uint CodeGenVersion = 3831;
+ private const uint CodeGenVersion = 3747;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
@@ -159,6 +159,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// Bit mask of the render target components written by the fragment stage.
///
public int FragmentOutputMap;
+
+ ///
+ /// Indicates if the vertex shader accesses draw parameters.
+ ///
+ public bool UsesDrawParameters;
}
private readonly DiskCacheGuestStorage _guestStorage;
@@ -771,6 +776,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
images,
dataInfo.Stage,
dataInfo.UsesInstanceId,
+ dataInfo.UsesDrawParameters,
dataInfo.UsesRtLayer,
dataInfo.ClipDistancesWritten,
dataInfo.FragmentOutputMap);
@@ -796,6 +802,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
dataInfo.ImagesCount = (ushort)info.Images.Count;
dataInfo.Stage = info.Stage;
dataInfo.UsesInstanceId = info.UsesInstanceId;
+ dataInfo.UsesDrawParameters = info.UsesDrawParameters;
dataInfo.UsesRtLayer = info.UsesRtLayer;
dataInfo.ClipDistancesWritten = info.ClipDistancesWritten;
dataInfo.FragmentOutputMap = info.FragmentOutputMap;
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index 44c26efbf6..b8cb1107fa 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -139,6 +139,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return useMask;
}
+ ///
+ public bool QueryHasConstantBufferDrawParameters()
+ {
+ return _state.GraphicsState.HasConstantBufferDrawParameters;
+ }
+
///
public InputTopology QueryPrimitiveTopology()
{
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
index 82252cedab..b072767742 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelGraphicsState.cs
@@ -77,6 +77,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
///
public Array32 AttributeTypes;
+ ///
+ /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
+ ///
+ public readonly bool HasConstantBufferDrawParameters;
+
///
/// Creates a new GPU graphics state.
///
@@ -93,6 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// When alpha test is enabled, indicates the comparison that decides if the fragment should be discarded
/// When alpha test is enabled, indicates the value to compare with the fragment output alpha
/// Type of the vertex attributes consumed by the shader
+ /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0
public GpuChannelGraphicsState(
bool earlyZForce,
PrimitiveTopology topology,
@@ -106,7 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
bool alphaTestEnable,
CompareOp alphaTestCompare,
float alphaTestReference,
- ref Array32 attributeTypes)
+ ref Array32 attributeTypes,
+ bool hasConstantBufferDrawParameters)
{
EarlyZForce = earlyZForce;
Topology = topology;
@@ -121,6 +128,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
AlphaTestCompare = alphaTestCompare;
AlphaTestReference = alphaTestReference;
AttributeTypes = attributeTypes;
+ HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
}
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index c998fe0939..1803dae61d 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -520,7 +520,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
}
- return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true);
+ bool usesDrawParameters = gpShaders.Shaders[1]?.Info.UsesDrawParameters ?? false;
+
+ return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true);
}
///
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs
index 43ccd892c3..abc9d913b3 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationList.cs
@@ -35,7 +35,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
foreach (var entry in _entries)
{
- if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, true))
+ bool usesDrawParameters = entry.Shaders[1]?.Info.UsesDrawParameters ?? false;
+
+ if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState, usesDrawParameters, true))
{
program = entry;
return true;
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
index 28818304a9..0aecc5b7b2 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
@@ -481,9 +481,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// GPU channel
/// Texture pool state
/// Graphics state
+ /// Indicates whether the vertex shader accesses draw parameters
/// Indicates whether texture descriptors should be checked
/// True if the state matches, false otherwise
- public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState, bool checkTextures)
+ public bool MatchesGraphics(
+ GpuChannel channel,
+ GpuChannelPoolState poolState,
+ GpuChannelGraphicsState graphicsState,
+ bool usesDrawParameters,
+ bool checkTextures)
{
if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable)
{
@@ -520,6 +526,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false;
}
+ if (usesDrawParameters && graphicsState.HasConstantBufferDrawParameters != GraphicsState.HasConstantBufferDrawParameters)
+ {
+ return false;
+ }
+
return Matches(channel, poolState, checkTextures, isCompute: false);
}
diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs
index 5911758efd..3b234eb08f 100644
--- a/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -586,6 +586,95 @@ namespace Ryujinx.Graphics.OpenGL
}
}
+ public void DrawIndexedIndirect(BufferRange indirectBuffer)
+ {
+ if (!_program.IsLinked)
+ {
+ Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
+ return;
+ }
+
+ PreDrawVbUnbounded();
+
+ _vertexArray.SetRangeOfIndexBuffer();
+
+ GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
+
+ GL.DrawElementsIndirect(_primitiveType, _elementsType, (IntPtr)indirectBuffer.Offset);
+
+ _vertexArray.RestoreIndexBuffer();
+
+ PostDraw();
+ }
+
+ public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
+ {
+ if (!_program.IsLinked)
+ {
+ Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
+ return;
+ }
+
+ PreDrawVbUnbounded();
+
+ _vertexArray.SetRangeOfIndexBuffer();
+
+ GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
+ GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
+
+ GL.MultiDrawElementsIndirectCount(
+ _primitiveType,
+ (All)_elementsType,
+ (IntPtr)indirectBuffer.Offset,
+ (IntPtr)parameterBuffer.Offset,
+ maxDrawCount,
+ stride);
+
+ _vertexArray.RestoreIndexBuffer();
+
+ PostDraw();
+ }
+
+ public void DrawIndirect(BufferRange indirectBuffer)
+ {
+ if (!_program.IsLinked)
+ {
+ Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
+ return;
+ }
+
+ PreDrawVbUnbounded();
+
+ GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
+
+ GL.DrawArraysIndirect(_primitiveType, (IntPtr)indirectBuffer.Offset);
+
+ PostDraw();
+ }
+
+ public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
+ {
+ if (!_program.IsLinked)
+ {
+ Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
+ return;
+ }
+
+ PreDrawVbUnbounded();
+
+ GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
+ GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
+
+ GL.MultiDrawArraysIndirectCount(
+ _primitiveType,
+ (IntPtr)indirectBuffer.Offset,
+ (IntPtr)parameterBuffer.Offset,
+ maxDrawCount,
+ stride);
+
+ PostDraw();
+ }
+
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
{
if (texture is TextureView view && sampler is Sampler samp)
@@ -683,57 +772,6 @@ namespace Ryujinx.Graphics.OpenGL
_tfEnabled = false;
}
- public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
- {
- if (!_program.IsLinked)
- {
- Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
- return;
- }
-
- PreDrawVbUnbounded();
-
- GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
- GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
-
- GL.MultiDrawArraysIndirectCount(
- _primitiveType,
- (IntPtr)indirectBuffer.Offset,
- (IntPtr)parameterBuffer.Offset,
- maxDrawCount,
- stride);
-
- PostDraw();
- }
-
- public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
- {
- if (!_program.IsLinked)
- {
- Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
- return;
- }
-
- PreDrawVbUnbounded();
-
- _vertexArray.SetRangeOfIndexBuffer();
-
- GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
- GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
-
- GL.MultiDrawElementsIndirectCount(
- _primitiveType,
- (All)_elementsType,
- (IntPtr)indirectBuffer.Offset,
- (IntPtr)parameterBuffer.Offset,
- maxDrawCount,
- stride);
-
- _vertexArray.RestoreIndexBuffer();
-
- PostDraw();
- }
-
public void SetAlphaTest(bool enable, float reference, CompareOp op)
{
if (!enable)
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index 4f2751b129..b2eeb5f5fa 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -46,6 +46,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
else
{
+ if (context.Config.Stage == ShaderStage.Vertex)
+ {
+ context.AppendLine("#extension GL_ARB_shader_draw_parameters : enable");
+ }
+
context.AppendLine("#extension GL_ARB_shader_viewport_layer_array : enable");
}
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
index 031b1c44c1..b78914260e 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
@@ -48,10 +48,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) },
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) },
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) },
- { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstance", VariableType.S32) },
- { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertex", VariableType.S32) },
+ { AttributeConsts.BaseInstance, new BuiltInAttribute("gl_BaseInstanceARB", VariableType.S32) },
+ { AttributeConsts.BaseVertex, new BuiltInAttribute("gl_BaseVertexARB", VariableType.S32) },
{ AttributeConsts.InstanceIndex, new BuiltInAttribute("gl_InstanceIndex", VariableType.S32) },
{ AttributeConsts.VertexIndex, new BuiltInAttribute("gl_VertexIndex", VariableType.S32) },
+ { AttributeConsts.DrawIndex, new BuiltInAttribute("gl_DrawIDARB", VariableType.S32) },
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) },
// Special.
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index fafb917dbf..54b00708be 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -743,6 +743,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
AttributeConsts.BaseVertex => BuiltIn.BaseVertex,
AttributeConsts.InstanceIndex => BuiltIn.InstanceIndex,
AttributeConsts.VertexIndex => BuiltIn.VertexIndex,
+ AttributeConsts.DrawIndex => BuiltIn.DrawIndex,
AttributeConsts.FrontFacing => BuiltIn.FrontFacing,
AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth,
AttributeConsts.ThreadKill => BuiltIn.HelperInvocation,
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
index 69283b0a3f..95df077bfd 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
@@ -89,6 +89,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
context.AddCapability(Capability.Tessellation);
}
+ else if (config.Stage == ShaderStage.Vertex)
+ {
+ context.AddCapability(Capability.DrawParameters);
+ }
context.AddExtension("SPV_KHR_shader_ballot");
context.AddExtension("SPV_KHR_subgroup_vote");
diff --git a/Ryujinx.Graphics.Shader/Constants.cs b/Ryujinx.Graphics.Shader/Constants.cs
index 86af48cfa6..7f1445ed04 100644
--- a/Ryujinx.Graphics.Shader/Constants.cs
+++ b/Ryujinx.Graphics.Shader/Constants.cs
@@ -6,5 +6,9 @@ namespace Ryujinx.Graphics.Shader
public const int MaxAttributes = 16;
public const int AllAttributesMask = (int)(uint.MaxValue >> (32 - MaxAttributes));
+
+ public const int NvnBaseVertexByteOffset = 0x640;
+ public const int NvnBaseInstanceByteOffset = 0x644;
+ public const int NvnDrawIndexByteOffset = 0x648;
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index 2cdc81fbc8..4f800a1450 100644
--- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -168,6 +168,15 @@ namespace Ryujinx.Graphics.Shader
return 0;
}
+ ///
+ /// Queries whenever the current draw has written the base vertex and base instance into Constant Buffer 0.
+ ///
+ /// True if the shader translator can assume that the constant buffer contains the base IDs, false otherwise
+ bool QueryHasConstantBufferDrawParameters()
+ {
+ return false;
+ }
+
///
/// Queries host about the presence of the FrontFacing built-in variable bug.
///
diff --git a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs
index 659f6167e8..bb75b10ae8 100644
--- a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs
+++ b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs
@@ -12,6 +12,7 @@ namespace Ryujinx.Graphics.Shader
public ShaderStage Stage { get; }
public bool UsesInstanceId { get; }
+ public bool UsesDrawParameters { get; }
public bool UsesRtLayer { get; }
public byte ClipDistancesWritten { get; }
public int FragmentOutputMap { get; }
@@ -23,6 +24,7 @@ namespace Ryujinx.Graphics.Shader
TextureDescriptor[] images,
ShaderStage stage,
bool usesInstanceId,
+ bool usesDrawParameters,
bool usesRtLayer,
byte clipDistancesWritten,
int fragmentOutputMap)
@@ -34,6 +36,7 @@ namespace Ryujinx.Graphics.Shader
Stage = stage;
UsesInstanceId = usesInstanceId;
+ UsesDrawParameters = usesDrawParameters;
UsesRtLayer = usesRtLayer;
ClipDistancesWritten = clipDistancesWritten;
FragmentOutputMap = fragmentOutputMap;
diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
index 47367f89c7..863e19a0d2 100644
--- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
+++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
@@ -100,5 +100,6 @@ namespace Ryujinx.Graphics.Shader.Translation
public const int BaseVertex = 0x2000054;
public const int InstanceIndex = 0x2000058;
public const int VertexIndex = 0x200005c;
+ public const int DrawIndex = 0x2000060;
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
index 6e95722ffe..839edbe9e3 100644
--- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
+++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
@@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ AttributeConsts.BaseVertex, new AttributeInfo(AttributeConsts.BaseVertex, 0, 1, AggregateType.S32) },
{ AttributeConsts.InstanceIndex, new AttributeInfo(AttributeConsts.InstanceIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.VertexIndex, new AttributeInfo(AttributeConsts.VertexIndex, 0, 1, AggregateType.S32) },
+ { AttributeConsts.DrawIndex, new AttributeInfo(AttributeConsts.DrawIndex, 0, 1, AggregateType.S32) },
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 0, 1, AggregateType.Bool) },
// Special.
diff --git a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs
index a2363fcbb4..c035f212dc 100644
--- a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs
+++ b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs
@@ -17,10 +17,11 @@ namespace Ryujinx.Graphics.Shader.Translation
Bindless = 1 << 2,
InstanceId = 1 << 3,
- RtLayer = 1 << 4,
- CbIndexing = 1 << 5,
- IaIndexing = 1 << 6,
- OaIndexing = 1 << 7,
- FixedFuncAttr = 1 << 8
+ DrawParameters = 1 << 4,
+ RtLayer = 1 << 5,
+ CbIndexing = 1 << 6,
+ IaIndexing = 1 << 7,
+ OaIndexing = 1 << 8,
+ FixedFuncAttr = 1 << 9
}
}
diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
index 4d66597f79..640717f919 100644
--- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs
@@ -12,6 +12,9 @@ namespace Ryujinx.Graphics.Shader.Translation
{
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
{
+ bool isVertexShader = config.Stage == ShaderStage.Vertex;
+ bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
+
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
{
BasicBlock block = blocks[blkIndex];
@@ -23,6 +26,21 @@ namespace Ryujinx.Graphics.Shader.Translation
continue;
}
+ if (isVertexShader)
+ {
+ if (hasConstantBufferDrawParameters)
+ {
+ if (ReplaceConstantBufferWithDrawParameters(operation))
+ {
+ config.SetUsedFeature(FeatureFlags.DrawParameters);
+ }
+ }
+ else if (HasConstantBufferDrawParameters(operation))
+ {
+ config.SetUsedFeature(FeatureFlags.DrawParameters);
+ }
+ }
+
if (UsesGlobalMemory(operation.Inst))
{
node = RewriteGlobalAccess(node, config);
@@ -528,5 +546,57 @@ namespace Ryujinx.Graphics.Shader.Translation
return node;
}
+
+ private static bool ReplaceConstantBufferWithDrawParameters(Operation operation)
+ {
+ bool modified = false;
+
+ for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
+ {
+ Operand src = operation.GetSource(srcIndex);
+
+ if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0)
+ {
+ switch (src.GetCbufOffset())
+ {
+ case Constants.NvnBaseVertexByteOffset / 4:
+ operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseVertex));
+ modified = true;
+ break;
+ case Constants.NvnBaseInstanceByteOffset / 4:
+ operation.SetSource(srcIndex, Attribute(AttributeConsts.BaseInstance));
+ modified = true;
+ break;
+ case Constants.NvnDrawIndexByteOffset / 4:
+ operation.SetSource(srcIndex, Attribute(AttributeConsts.DrawIndex));
+ modified = true;
+ break;
+ }
+ }
+ }
+
+ return modified;
+ }
+
+ private static bool HasConstantBufferDrawParameters(Operation operation)
+ {
+ for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
+ {
+ Operand src = operation.GetSource(srcIndex);
+
+ if (src.Type == OperandType.ConstantBuffer && src.GetCbufSlot() == 0)
+ {
+ switch (src.GetCbufOffset())
+ {
+ case Constants.NvnBaseVertexByteOffset / 4:
+ case Constants.NvnBaseInstanceByteOffset / 4:
+ case Constants.NvnDrawIndexByteOffset / 4:
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs
index 8741f8487d..58a934c73b 100644
--- a/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -86,6 +86,7 @@ namespace Ryujinx.Graphics.Shader.Translation
config.GetImageDescriptors(),
config.Stage,
config.UsedFeatures.HasFlag(FeatureFlags.InstanceId),
+ config.UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
config.UsedFeatures.HasFlag(FeatureFlags.RtLayer),
config.ClipDistancesWritten,
config.OmapTargets);
diff --git a/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/Ryujinx.Graphics.Vulkan/BufferHolder.cs
index 4660765a46..24f789f623 100644
--- a/Ryujinx.Graphics.Vulkan/BufferHolder.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferHolder.cs
@@ -477,6 +477,26 @@ namespace Ryujinx.Graphics.Vulkan
return holder.GetBuffer();
}
+ public bool TryGetCachedConvertedBuffer(int offset, int size, ICacheKey key, out BufferHolder holder)
+ {
+ return _cachedConvertedBuffers.TryGetValue(offset, size, key, out holder);
+ }
+
+ public void AddCachedConvertedBuffer(int offset, int size, ICacheKey key, BufferHolder holder)
+ {
+ _cachedConvertedBuffers.Add(offset, size, key, holder);
+ }
+
+ public void AddCachedConvertedBufferDependency(int offset, int size, ICacheKey key, Dependency dependency)
+ {
+ _cachedConvertedBuffers.AddDependency(offset, size, key, dependency);
+ }
+
+ public void RemoveCachedConvertedBuffer(int offset, int size, ICacheKey key)
+ {
+ _cachedConvertedBuffers.Remove(offset, size, key);
+ }
+
public void Dispose()
{
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_buffer, (ulong)Size);
diff --git a/Ryujinx.Graphics.Vulkan/BufferManager.cs b/Ryujinx.Graphics.Vulkan/BufferManager.cs
index 57d6724237..432898a9cc 100644
--- a/Ryujinx.Graphics.Vulkan/BufferManager.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferManager.cs
@@ -52,7 +52,12 @@ namespace Ryujinx.Graphics.Vulkan
public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal)
{
- var holder = Create(gd, size, deviceLocal: deviceLocal);
+ return CreateWithHandle(gd, size, deviceLocal, out _);
+ }
+
+ public BufferHandle CreateWithHandle(VulkanRenderer gd, int size, bool deviceLocal, out BufferHolder holder)
+ {
+ holder = Create(gd, size, deviceLocal: deviceLocal);
if (holder == null)
{
return BufferHandle.Null;
@@ -164,6 +169,141 @@ namespace Ryujinx.Graphics.Vulkan
return null;
}
+ public (Auto, Auto) GetBufferTopologyConversionIndirect(
+ VulkanRenderer gd,
+ CommandBufferScoped cbs,
+ BufferRange indexBuffer,
+ BufferRange indirectBuffer,
+ BufferRange drawCountBuffer,
+ IndexBufferPattern pattern,
+ int indexSize,
+ bool hasDrawCount,
+ int maxDrawCount,
+ int indirectDataStride)
+ {
+ BufferHolder drawCountBufferHolder = null;
+
+ if (!TryGetBuffer(indexBuffer.Handle, out var indexBufferHolder) ||
+ !TryGetBuffer(indirectBuffer.Handle, out var indirectBufferHolder) ||
+ (hasDrawCount && !TryGetBuffer(drawCountBuffer.Handle, out drawCountBufferHolder)))
+ {
+ return (null, null);
+ }
+
+ var indexBufferKey = new TopologyConversionIndirectCacheKey(
+ gd,
+ pattern,
+ indexSize,
+ indirectBufferHolder,
+ indirectBuffer.Offset,
+ indirectBuffer.Size);
+
+ bool hasConvertedIndexBuffer = indexBufferHolder.TryGetCachedConvertedBuffer(
+ indexBuffer.Offset,
+ indexBuffer.Size,
+ indexBufferKey,
+ out var convertedIndexBuffer);
+
+ var indirectBufferKey = new IndirectDataCacheKey(pattern);
+ bool hasConvertedIndirectBuffer = indirectBufferHolder.TryGetCachedConvertedBuffer(
+ indirectBuffer.Offset,
+ indirectBuffer.Size,
+ indirectBufferKey,
+ out var convertedIndirectBuffer);
+
+ var drawCountBufferKey = new DrawCountCacheKey();
+ bool hasCachedDrawCount = true;
+
+ if (hasDrawCount)
+ {
+ hasCachedDrawCount = drawCountBufferHolder.TryGetCachedConvertedBuffer(
+ drawCountBuffer.Offset,
+ drawCountBuffer.Size,
+ drawCountBufferKey,
+ out _);
+ }
+
+ if (!hasConvertedIndexBuffer || !hasConvertedIndirectBuffer || !hasCachedDrawCount)
+ {
+ // The destination index size is always I32.
+
+ int indexCount = indexBuffer.Size / indexSize;
+
+ int convertedCount = pattern.GetConvertedCount(indexCount);
+
+ if (!hasConvertedIndexBuffer)
+ {
+ convertedIndexBuffer = Create(gd, convertedCount * 4);
+ indexBufferKey.SetBuffer(convertedIndexBuffer.GetBuffer());
+ indexBufferHolder.AddCachedConvertedBuffer(indexBuffer.Offset, indexBuffer.Size, indexBufferKey, convertedIndexBuffer);
+ }
+
+ if (!hasConvertedIndirectBuffer)
+ {
+ convertedIndirectBuffer = Create(gd, indirectBuffer.Size);
+ indirectBufferHolder.AddCachedConvertedBuffer(indirectBuffer.Offset, indirectBuffer.Size, indirectBufferKey, convertedIndirectBuffer);
+ }
+
+ gd.PipelineInternal.EndRenderPass();
+ gd.HelperShader.ConvertIndexBufferIndirect(
+ gd,
+ cbs,
+ indirectBufferHolder,
+ convertedIndirectBuffer,
+ drawCountBuffer,
+ indexBufferHolder,
+ convertedIndexBuffer,
+ pattern,
+ indexSize,
+ indexBuffer.Offset,
+ indexBuffer.Size,
+ indirectBuffer.Offset,
+ hasDrawCount,
+ maxDrawCount,
+ indirectDataStride);
+
+ // Any modification of the indirect buffer should invalidate the index buffers that are associated with it,
+ // since we used the indirect data to find the range of the index buffer that is used.
+
+ var indexBufferDependency = new Dependency(
+ indexBufferHolder,
+ indexBuffer.Offset,
+ indexBuffer.Size,
+ indexBufferKey);
+
+ indirectBufferHolder.AddCachedConvertedBufferDependency(
+ indirectBuffer.Offset,
+ indirectBuffer.Size,
+ indirectBufferKey,
+ indexBufferDependency);
+
+ if (hasDrawCount)
+ {
+ if (!hasCachedDrawCount)
+ {
+ drawCountBufferHolder.AddCachedConvertedBuffer(drawCountBuffer.Offset, drawCountBuffer.Size, drawCountBufferKey, null);
+ }
+
+ // If we have a draw count, any modification of the draw count should invalidate all indirect buffers
+ // where we used it to find the range of indirect data that is actually used.
+
+ var indirectBufferDependency = new Dependency(
+ indirectBufferHolder,
+ indirectBuffer.Offset,
+ indirectBuffer.Size,
+ indirectBufferKey);
+
+ drawCountBufferHolder.AddCachedConvertedBufferDependency(
+ drawCountBuffer.Offset,
+ drawCountBuffer.Size,
+ drawCountBufferKey,
+ indirectBufferDependency);
+ }
+ }
+
+ return (convertedIndexBuffer.GetBuffer(), convertedIndirectBuffer.GetBuffer());
+ }
+
public Auto GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size)
{
if (TryGetBuffer(handle, out var holder))
diff --git a/Ryujinx.Graphics.Vulkan/CacheByRange.cs b/Ryujinx.Graphics.Vulkan/CacheByRange.cs
index c77e66ae86..afd7140eb2 100644
--- a/Ryujinx.Graphics.Vulkan/CacheByRange.cs
+++ b/Ryujinx.Graphics.Vulkan/CacheByRange.cs
@@ -106,17 +106,125 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ struct TopologyConversionIndirectCacheKey : ICacheKey
+ {
+ private readonly TopologyConversionCacheKey _baseKey;
+ private readonly BufferHolder _indirectDataBuffer;
+ private readonly int _indirectDataOffset;
+ private readonly int _indirectDataSize;
+
+ public TopologyConversionIndirectCacheKey(
+ VulkanRenderer gd,
+ IndexBufferPattern pattern,
+ int indexSize,
+ BufferHolder indirectDataBuffer,
+ int indirectDataOffset,
+ int indirectDataSize)
+ {
+ _baseKey = new TopologyConversionCacheKey(gd, pattern, indexSize);
+ _indirectDataBuffer = indirectDataBuffer;
+ _indirectDataOffset = indirectDataOffset;
+ _indirectDataSize = indirectDataSize;
+ }
+
+ public bool KeyEqual(ICacheKey other)
+ {
+ return other is TopologyConversionIndirectCacheKey entry &&
+ entry._baseKey.KeyEqual(_baseKey) &&
+ entry._indirectDataBuffer == _indirectDataBuffer &&
+ entry._indirectDataOffset == _indirectDataOffset &&
+ entry._indirectDataSize == _indirectDataSize;
+ }
+
+ public void SetBuffer(Auto buffer)
+ {
+ _baseKey.SetBuffer(buffer);
+ }
+
+ public void Dispose()
+ {
+ _baseKey.Dispose();
+ }
+ }
+
+ struct IndirectDataCacheKey : ICacheKey
+ {
+ private IndexBufferPattern _pattern;
+
+ public IndirectDataCacheKey(IndexBufferPattern pattern)
+ {
+ _pattern = pattern;
+ }
+
+ public bool KeyEqual(ICacheKey other)
+ {
+ return other is IndirectDataCacheKey entry && entry._pattern == _pattern;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+
+ struct DrawCountCacheKey : ICacheKey
+ {
+ public bool KeyEqual(ICacheKey other)
+ {
+ return other is DrawCountCacheKey;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+
+ struct Dependency
+ {
+ private readonly BufferHolder _buffer;
+ private readonly int _offset;
+ private readonly int _size;
+ private readonly ICacheKey _key;
+
+ public Dependency(BufferHolder buffer, int offset, int size, ICacheKey key)
+ {
+ _buffer = buffer;
+ _offset = offset;
+ _size = size;
+ _key = key;
+ }
+
+ public void RemoveFromOwner()
+ {
+ _buffer.RemoveCachedConvertedBuffer(_offset, _size, _key);
+ }
+ }
+
struct CacheByRange where T : IDisposable
{
private struct Entry
{
public ICacheKey Key;
public T Value;
+ public List DependencyList;
public Entry(ICacheKey key, T value)
{
Key = key;
Value = value;
+ DependencyList = null;
+ }
+
+ public void InvalidateDependencies()
+ {
+ if (DependencyList != null)
+ {
+ foreach (Dependency dependency in DependencyList)
+ {
+ dependency.RemoveFromOwner();
+ }
+
+ DependencyList.Clear();
+ }
}
}
@@ -129,6 +237,51 @@ namespace Ryujinx.Graphics.Vulkan
entries.Add(new Entry(key, value));
}
+ public void AddDependency(int offset, int size, ICacheKey key, Dependency dependency)
+ {
+ List entries = GetEntries(offset, size);
+
+ for (int i = 0; i < entries.Count; i++)
+ {
+ Entry entry = entries[i];
+
+ if (entry.Key.KeyEqual(key))
+ {
+ if (entry.DependencyList == null)
+ {
+ entry.DependencyList = new List();
+ entries[i] = entry;
+ }
+
+ entry.DependencyList.Add(dependency);
+
+ break;
+ }
+ }
+ }
+
+ public void Remove(int offset, int size, ICacheKey key)
+ {
+ List entries = GetEntries(offset, size);
+
+ for (int i = 0; i < entries.Count; i++)
+ {
+ Entry entry = entries[i];
+
+ if (entry.Key.KeyEqual(key))
+ {
+ entries.RemoveAt(i--);
+
+ DestroyEntry(entry);
+ }
+ }
+
+ if (entries.Count == 0)
+ {
+ _ranges.Remove(PackRange(offset, size));
+ }
+ }
+
public bool TryGetValue(int offset, int size, ICacheKey key, out T value)
{
List entries = GetEntries(offset, size);
@@ -155,8 +308,7 @@ namespace Ryujinx.Graphics.Vulkan
{
foreach (Entry entry in entries)
{
- entry.Key.Dispose();
- entry.Value.Dispose();
+ DestroyEntry(entry);
}
}
@@ -185,8 +337,7 @@ namespace Ryujinx.Graphics.Vulkan
foreach (Entry entry in entries)
{
- entry.Key.Dispose();
- entry.Value.Dispose();
+ DestroyEntry(entry);
}
(toRemove ??= new List()).Add(range.Key);
@@ -222,6 +373,13 @@ namespace Ryujinx.Graphics.Vulkan
return value;
}
+ private static void DestroyEntry(Entry entry)
+ {
+ entry.Key.Dispose();
+ entry.Value?.Dispose();
+ entry.InvalidateDependencies();
+ }
+
private static ulong PackRange(int offset, int size)
{
return (uint)offset | ((ulong)size << 32);
diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs
index c842b92224..1ef22dc29d 100644
--- a/Ryujinx.Graphics.Vulkan/HelperShader.cs
+++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs
@@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Vulkan
{
class HelperShader : IDisposable
{
+ private const int UniformBufferAlignment = 256;
+
private readonly PipelineHelperShader _pipeline;
private readonly ISampler _samplerLinear;
private readonly ISampler _samplerNearest;
@@ -19,6 +21,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly IProgram _programColorClear;
private readonly IProgram _programStrideChange;
private readonly IProgram _programColorCopyBetweenMsNonMs;
+ private readonly IProgram _programConvertIndexBuffer;
+ private readonly IProgram _programConvertIndirectData;
public HelperShader(VulkanRenderer gd, Device device)
{
@@ -88,6 +92,28 @@ namespace Ryujinx.Graphics.Vulkan
{
new SpecDescription((0, SpecConstType.Int32))
});
+
+ var convertIndexBufferBindings = new ShaderBindings(
+ new[] { 0 },
+ new[] { 1, 2 },
+ Array.Empty(),
+ Array.Empty());
+
+ _programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
+ {
+ new ShaderSource(ShaderBinaries.ConvertIndexBufferShaderSource, convertIndexBufferBindings, ShaderStage.Compute, TargetLanguage.Spirv),
+ });
+
+ var convertIndirectDataBindings = new ShaderBindings(
+ new[] { 0 },
+ new[] { 1, 2, 3 },
+ Array.Empty(),
+ Array.Empty());
+
+ _programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]
+ {
+ new ShaderSource(ShaderBinaries.ConvertIndirectDataShaderSource, convertIndirectDataBindings, ShaderStage.Compute, TargetLanguage.Spirv),
+ });
}
public void Blit(
@@ -408,10 +434,12 @@ namespace Ryujinx.Graphics.Vulkan
int srcOffset,
int indexCount)
{
+ // TODO: Support conversion with primitive restart enabled.
+ // TODO: Convert with a compute shader?
+
int convertedCount = pattern.GetConvertedCount(indexCount);
int outputIndexSize = 4;
- // TODO: Do this with a compute shader?
var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, indexCount * indexSize).Value;
var dstBuffer = dst.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
@@ -671,6 +699,133 @@ namespace Ryujinx.Graphics.Vulkan
};
}
+ public void ConvertIndexBufferIndirect(
+ VulkanRenderer gd,
+ CommandBufferScoped cbs,
+ BufferHolder srcIndirectBuffer,
+ BufferHolder dstIndirectBuffer,
+ BufferRange drawCountBuffer,
+ BufferHolder srcIndexBuffer,
+ BufferHolder dstIndexBuffer,
+ IndexBufferPattern pattern,
+ int indexSize,
+ int srcIndexBufferOffset,
+ int srcIndexBufferSize,
+ int srcIndirectBufferOffset,
+ bool hasDrawCount,
+ int maxDrawCount,
+ int indirectDataStride)
+ {
+ // TODO: Support conversion with primitive restart enabled.
+
+ BufferRange drawCountBufferAligned = new BufferRange(
+ drawCountBuffer.Handle,
+ drawCountBuffer.Offset & ~(UniformBufferAlignment - 1),
+ UniformBufferAlignment);
+
+ int indirectDataSize = maxDrawCount * indirectDataStride;
+
+ int indexCount = srcIndexBufferSize / indexSize;
+ int primitivesCount = pattern.GetPrimitiveCount(indexCount);
+ int convertedCount = pattern.GetConvertedCount(indexCount);
+ int outputIndexSize = 4;
+
+ var srcBuffer = srcIndexBuffer.GetBuffer().Get(cbs, srcIndexBufferOffset, indexCount * indexSize).Value;
+ var dstBuffer = dstIndexBuffer.GetBuffer().Get(cbs, 0, convertedCount * outputIndexSize).Value;
+
+ const int ParamsBufferSize = 24 * sizeof(int);
+ const int ParamsIndirectDispatchOffset = 16 * sizeof(int);
+ const int ParamsIndirectDispatchSize = 3 * sizeof(int);
+
+ Span shaderParams = stackalloc int[ParamsBufferSize / sizeof(int)];
+
+ shaderParams[8] = pattern.PrimitiveVertices;
+ shaderParams[9] = pattern.PrimitiveVerticesOut;
+ shaderParams[10] = indexSize;
+ shaderParams[11] = outputIndexSize;
+ shaderParams[12] = pattern.BaseIndex;
+ shaderParams[13] = pattern.IndexStride;
+ shaderParams[14] = srcIndexBufferOffset;
+ shaderParams[15] = primitivesCount;
+ shaderParams[16] = 1;
+ shaderParams[17] = 1;
+ shaderParams[18] = 1;
+ shaderParams[19] = hasDrawCount ? 1 : 0;
+ shaderParams[20] = maxDrawCount;
+ shaderParams[21] = (drawCountBuffer.Offset & (UniformBufferAlignment - 1)) / 4;
+ shaderParams[22] = indirectDataStride / 4;
+ shaderParams[23] = srcIndirectBufferOffset / 4;
+
+ pattern.OffsetIndex.CopyTo(shaderParams.Slice(0, pattern.OffsetIndex.Length));
+
+ var patternBufferHandle = gd.BufferManager.CreateWithHandle(gd, ParamsBufferSize, false, out var patternBuffer);
+ var patternBufferAuto = patternBuffer.GetBuffer();
+
+ gd.BufferManager.SetData(patternBufferHandle, 0, shaderParams);
+
+ _pipeline.SetCommandBuffer(cbs);
+
+ BufferHolder.InsertBufferBarrier(
+ gd,
+ cbs.CommandBuffer,
+ srcIndirectBuffer.GetBuffer().Get(cbs, srcIndirectBufferOffset, indirectDataSize).Value,
+ BufferHolder.DefaultAccessFlags,
+ AccessFlags.AccessShaderReadBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ PipelineStageFlags.PipelineStageComputeShaderBit,
+ srcIndirectBufferOffset,
+ indirectDataSize);
+
+ _pipeline.SetUniformBuffers(0, stackalloc[] { drawCountBufferAligned });
+ _pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer(), patternBuffer.GetBuffer() });
+
+ _pipeline.SetProgram(_programConvertIndirectData);
+ _pipeline.DispatchCompute(1, 1, 1);
+
+ BufferHolder.InsertBufferBarrier(
+ gd,
+ cbs.CommandBuffer,
+ patternBufferAuto.Get(cbs, ParamsIndirectDispatchOffset, ParamsIndirectDispatchSize).Value,
+ AccessFlags.AccessShaderWriteBit,
+ AccessFlags.AccessIndirectCommandReadBit,
+ PipelineStageFlags.PipelineStageComputeShaderBit,
+ PipelineStageFlags.PipelineStageDrawIndirectBit,
+ ParamsIndirectDispatchOffset,
+ ParamsIndirectDispatchSize);
+
+ BufferHolder.InsertBufferBarrier(
+ gd,
+ cbs.CommandBuffer,
+ dstBuffer,
+ BufferHolder.DefaultAccessFlags,
+ AccessFlags.AccessTransferWriteBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ PipelineStageFlags.PipelineStageTransferBit,
+ 0,
+ convertedCount * outputIndexSize);
+
+ _pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(patternBufferHandle, 0, ParamsBufferSize) });
+ _pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() });
+
+ _pipeline.SetProgram(_programConvertIndexBuffer);
+ _pipeline.DispatchComputeIndirect(patternBufferAuto, ParamsIndirectDispatchOffset);
+
+ BufferHolder.InsertBufferBarrier(
+ gd,
+ cbs.CommandBuffer,
+ dstBuffer,
+ AccessFlags.AccessTransferWriteBit,
+ BufferHolder.DefaultAccessFlags,
+ PipelineStageFlags.PipelineStageTransferBit,
+ PipelineStageFlags.PipelineStageAllCommandsBit,
+ 0,
+ convertedCount * outputIndexSize);
+
+ gd.BufferManager.Delete(patternBufferHandle);
+
+ _pipeline.Finish(gd, cbs);
+ }
+
protected virtual void Dispose(bool disposing)
{
if (disposing)
@@ -680,6 +835,8 @@ namespace Ryujinx.Graphics.Vulkan
_programColorClear.Dispose();
_programStrideChange.Dispose();
_programColorCopyBetweenMsNonMs.Dispose();
+ _programConvertIndexBuffer.Dispose();
+ _programConvertIndirectData.Dispose();
_samplerNearest.Dispose();
_samplerLinear.Dispose();
_pipeline.Dispose();
diff --git a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
index 205eab2771..64b95f6003 100644
--- a/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
+++ b/Ryujinx.Graphics.Vulkan/IndexBufferState.cs
@@ -1,5 +1,4 @@
using Silk.NET.Vulkan;
-using System;
namespace Ryujinx.Graphics.Vulkan
{
@@ -68,17 +67,18 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- public void BindConvertedIndexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, int firstIndex, int indexCount, int convertedCount, IndexBufferPattern pattern)
+ public void BindConvertedIndexBuffer(
+ VulkanRenderer gd,
+ CommandBufferScoped cbs,
+ int firstIndex,
+ int indexCount,
+ int convertedCount,
+ IndexBufferPattern pattern)
{
Auto autoBuffer;
// Convert the index buffer using the given pattern.
- int indexSize = _type switch
- {
- IndexType.Uint32 => 4,
- IndexType.Uint16 => 2,
- _ => 1,
- };
+ int indexSize = GetIndexSize();
int firstIndexOffset = firstIndex * indexSize;
@@ -94,6 +94,54 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ public Auto BindConvertedIndexBufferIndirect(
+ VulkanRenderer gd,
+ CommandBufferScoped cbs,
+ GAL.BufferRange indirectBuffer,
+ GAL.BufferRange drawCountBuffer,
+ IndexBufferPattern pattern,
+ bool hasDrawCount,
+ int maxDrawCount,
+ int indirectDataStride)
+ {
+ // Convert the index buffer using the given pattern.
+ int indexSize = GetIndexSize();
+
+ (var indexBufferAuto, var indirectBufferAuto) = gd.BufferManager.GetBufferTopologyConversionIndirect(
+ gd,
+ cbs,
+ new GAL.BufferRange(_handle, _offset, _size),
+ indirectBuffer,
+ drawCountBuffer,
+ pattern,
+ indexSize,
+ hasDrawCount,
+ maxDrawCount,
+ indirectDataStride);
+
+ int convertedCount = pattern.GetConvertedCount(_size / indexSize);
+ int size = convertedCount * 4;
+
+ _buffer = indexBufferAuto;
+
+ if (indexBufferAuto != null)
+ {
+ gd.Api.CmdBindIndexBuffer(cbs.CommandBuffer, indexBufferAuto.Get(cbs, 0, size).Value, 0, IndexType.Uint32);
+ }
+
+ return indirectBufferAuto;
+ }
+
+ private int GetIndexSize()
+ {
+ return _type switch
+ {
+ IndexType.Uint32 => 4,
+ IndexType.Uint16 => 2,
+ _ => 1,
+ };
+ }
+
public bool BoundEquals(Auto buffer)
{
return _buffer == buffer;
diff --git a/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index f333c944bd..55c3fea228 100644
--- a/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -294,6 +294,19 @@ namespace Ryujinx.Graphics.Vulkan
Gd.Api.CmdDispatch(CommandBuffer, (uint)groupsX, (uint)groupsY, (uint)groupsZ);
}
+ public void DispatchComputeIndirect(Auto indirectBuffer, int indirectBufferOffset)
+ {
+ if (!_program.IsLinked)
+ {
+ return;
+ }
+
+ EndRenderPass();
+ RecreatePipelineIfNeeded(PipelineBindPoint.Compute);
+
+ Gd.Api.CmdDispatchIndirect(CommandBuffer, indirectBuffer.Get(Cbs, indirectBufferOffset, 12).Value, (ulong)indirectBufferOffset);
+ }
+
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
{
if (!_program.IsLinked)
@@ -395,6 +408,204 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ public void DrawIndexedIndirect(BufferRange indirectBuffer)
+ {
+ if (!_program.IsLinked)
+ {
+ return;
+ }
+
+ UpdateIndexBufferPattern();
+ RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
+ BeginRenderPass();
+ DrawCount++;
+
+ if (_indexBufferPattern != null)
+ {
+ // Convert the index buffer into a supported topology.
+ IndexBufferPattern pattern = _indexBufferPattern;
+
+ Auto indirectBufferAuto = _indexBuffer.BindConvertedIndexBufferIndirect(
+ Gd,
+ Cbs,
+ indirectBuffer,
+ BufferRange.Empty,
+ pattern,
+ false,
+ 1,
+ indirectBuffer.Size);
+
+ _needsIndexBufferRebind = false;
+
+ BeginRenderPass(); // May have been interrupted to set buffer data.
+ ResumeTransformFeedbackInternal();
+
+ Gd.Api.CmdDrawIndexedIndirect(CommandBuffer, indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value, 0, 1, (uint)indirectBuffer.Size);
+ }
+ else
+ {
+ var buffer = Gd.BufferManager
+ .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
+ .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
+
+ ResumeTransformFeedbackInternal();
+
+ Gd.Api.CmdDrawIndexedIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size);
+ }
+ }
+
+ public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
+ {
+ if (!_program.IsLinked)
+ {
+ return;
+ }
+
+ UpdateIndexBufferPattern();
+ RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
+ BeginRenderPass();
+ DrawCount++;
+
+ var countBuffer = Gd.BufferManager
+ .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false)
+ .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
+
+ if (_indexBufferPattern != null)
+ {
+ // Convert the index buffer into a supported topology.
+ IndexBufferPattern pattern = _indexBufferPattern;
+
+ Auto indirectBufferAuto = _indexBuffer.BindConvertedIndexBufferIndirect(
+ Gd,
+ Cbs,
+ indirectBuffer,
+ parameterBuffer,
+ pattern,
+ true,
+ maxDrawCount,
+ stride);
+
+ _needsIndexBufferRebind = false;
+
+ BeginRenderPass(); // May have been interrupted to set buffer data.
+ ResumeTransformFeedbackInternal();
+
+ if (Gd.Capabilities.SupportsIndirectParameters)
+ {
+ Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount(
+ CommandBuffer,
+ indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value,
+ 0,
+ countBuffer,
+ (ulong)parameterBuffer.Offset,
+ (uint)maxDrawCount,
+ (uint)stride);
+ }
+ else
+ {
+ // This is also fine because the indirect data conversion always zeros
+ // the entries that are past the current draw count.
+
+ Gd.Api.CmdDrawIndexedIndirect(
+ CommandBuffer,
+ indirectBufferAuto.Get(Cbs, 0, indirectBuffer.Size).Value,
+ 0,
+ (uint)maxDrawCount,
+ (uint)stride);
+ }
+
+ }
+ else
+ {
+ var buffer = Gd.BufferManager
+ .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
+ .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
+
+ ResumeTransformFeedbackInternal();
+
+ if (Gd.Capabilities.SupportsIndirectParameters)
+ {
+ Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount(
+ CommandBuffer,
+ buffer,
+ (ulong)indirectBuffer.Offset,
+ countBuffer,
+ (ulong)parameterBuffer.Offset,
+ (uint)maxDrawCount,
+ (uint)stride);
+ }
+ else
+ {
+ // Not fully correct, but we can't do much better if the host does not support indirect count.
+ Gd.Api.CmdDrawIndexedIndirect(
+ CommandBuffer,
+ buffer,
+ (ulong)indirectBuffer.Offset,
+ (uint)maxDrawCount,
+ (uint)stride);
+ }
+ }
+ }
+
+ public void DrawIndirect(BufferRange indirectBuffer)
+ {
+ if (!_program.IsLinked)
+ {
+ return;
+ }
+
+ // TODO: Support quads and other unsupported topologies.
+
+ RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
+ BeginRenderPass();
+ ResumeTransformFeedbackInternal();
+ DrawCount++;
+
+ var buffer = Gd.BufferManager
+ .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
+ .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
+
+ Gd.Api.CmdDrawIndirect(CommandBuffer, buffer, (ulong)indirectBuffer.Offset, 1, (uint)indirectBuffer.Size);
+ }
+
+ public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
+ {
+ if (!Gd.Capabilities.SupportsIndirectParameters)
+ {
+ // TODO: Fallback for when this is not supported.
+ throw new NotSupportedException();
+ }
+
+ if (!_program.IsLinked)
+ {
+ return;
+ }
+
+ // TODO: Support quads and other unsupported topologies.
+
+ RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
+ BeginRenderPass();
+ ResumeTransformFeedbackInternal();
+ DrawCount++;
+
+ var buffer = Gd.BufferManager
+ .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
+ .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
+
+ var countBuffer = Gd.BufferManager
+ .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false)
+ .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
+
+ Gd.DrawIndirectCountApi.CmdDrawIndirectCount(
+ CommandBuffer,
+ buffer,
+ (ulong)indirectBuffer.Offset,
+ countBuffer,
+ (ulong)parameterBuffer.Offset,
+ (uint)maxDrawCount,
+ (uint)stride);
+ }
+
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
{
if (texture is TextureView srcTexture)
@@ -449,76 +660,6 @@ namespace Ryujinx.Graphics.Vulkan
return CommandBuffer.Handle == cb.Handle;
}
- public void MultiDrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
- {
- if (!Gd.Capabilities.SupportsIndirectParameters)
- {
- throw new NotSupportedException();
- }
-
- if (_program.LinkStatus != ProgramLinkStatus.Success)
- {
- return;
- }
-
- RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
- BeginRenderPass();
- ResumeTransformFeedbackInternal();
- DrawCount++;
-
- var buffer = Gd.BufferManager
- .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, true)
- .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
-
- var countBuffer = Gd.BufferManager
- .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true)
- .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
-
- Gd.DrawIndirectCountApi.CmdDrawIndirectCount(
- CommandBuffer,
- buffer,
- (ulong)indirectBuffer.Offset,
- countBuffer,
- (ulong)parameterBuffer.Offset,
- (uint)maxDrawCount,
- (uint)stride);
- }
-
- public void MultiDrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
- {
- if (!Gd.Capabilities.SupportsIndirectParameters)
- {
- throw new NotSupportedException();
- }
-
- if (_program.LinkStatus != ProgramLinkStatus.Success)
- {
- return;
- }
-
- RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
- BeginRenderPass();
- ResumeTransformFeedbackInternal();
- DrawCount++;
-
- var buffer = Gd.BufferManager
- .GetBuffer(CommandBuffer, indirectBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true)
- .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
-
- var countBuffer = Gd.BufferManager
- .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true)
- .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
-
- Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount(
- CommandBuffer,
- buffer,
- (ulong)indirectBuffer.Offset,
- countBuffer,
- (ulong)parameterBuffer.Offset,
- (uint)maxDrawCount,
- (uint)stride);
- }
-
public void SetAlphaTest(bool enable, float reference, GAL.CompareOp op)
{
// This is currently handled using shader specialization, as Vulkan does not support alpha test.
@@ -706,7 +847,7 @@ namespace Ryujinx.Graphics.Vulkan
if (!dataSpan.SequenceEqual(_newState.SpecializationData.Span))
{
_newState.SpecializationData = new SpecData(dataSpan);
-
+
SignalStateChange();
}
}
diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp b/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp
new file mode 100644
index 0000000000..d56d6cfd1c
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndexBufferShaderSource.comp
@@ -0,0 +1,58 @@
+#version 450 core
+
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_EXT_shader_8bit_storage : require
+
+layout (local_size_x = 16, local_size_y = 1, local_size_z = 1) in;
+
+layout (std430, set = 0, binding = 0) uniform index_buffer_pattern
+{
+ int ibp_pattern[8];
+ int ibp_primitive_vertices;
+ int ibp_primitive_vertices_out;
+ int ibp_index_size;
+ int ibp_index_size_out;
+ int ibp_base_index;
+ int ibp_index_stride;
+ int src_offset;
+ int total_primitives;
+};
+
+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()
+{
+ int primitiveIndex = int(gl_GlobalInvocationID.x);
+ if (primitiveIndex >= total_primitives)
+ {
+ return;
+ }
+
+ int inOffset = primitiveIndex * ibp_index_stride;
+ int outOffset = primitiveIndex * ibp_primitive_vertices_out;
+
+ for (int i = 0; i < ibp_primitive_vertices_out; i++)
+ {
+ int j;
+ int io = max(0, inOffset + ibp_base_index + ibp_pattern[i]) * ibp_index_size;
+ int oo = (outOffset + i) * ibp_index_size_out;
+
+ for (j = 0; j < ibp_index_size; j++)
+ {
+ out_data[oo + j] = in_data[src_offset + io + j];
+ }
+
+ for (; j < ibp_index_size_out; j++)
+ {
+ out_data[oo + j] = uint8_t(0);
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp b/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp
new file mode 100644
index 0000000000..6ee96b7b3a
--- /dev/null
+++ b/Ryujinx.Graphics.Vulkan/Shaders/ConvertIndirectDataShaderSource.comp
@@ -0,0 +1,103 @@
+#version 450 core
+
+#extension GL_EXT_scalar_block_layout : require
+
+layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+layout (std430, set = 0, binding = 0) uniform draw_count_uniform
+{
+ uint[64] draw_count_buffer;
+};
+
+layout (std430, set = 1, binding = 1) buffer indirect_in
+{
+ int[] indirect_data_in;
+};
+
+layout (std430, set = 1, binding = 2) buffer indirect_out
+{
+ int[] indirect_data_out;
+};
+
+layout (std430, set = 1, binding = 3) buffer index_buffer_pattern
+{
+ int ibp_pattern[8];
+ int ibp_primitive_vertices;
+ int ibp_primitive_vertices_out;
+ int ibp_index_size;
+ int ibp_index_size_out;
+ int ibp_base_index;
+ int ibp_index_stride;
+ int src_offset;
+ int total_primitives;
+ int dispatch_x;
+ int dispatch_y;
+ int dispatch_z;
+ int has_draw_count;
+ uint max_draw_count;
+ int draw_count_offset;
+ int indirect_data_stride;
+ int indirect_data_offset;
+};
+
+int GetPrimitiveCount(int vertexCount)
+{
+ return max(0, (vertexCount - ibp_base_index) / ibp_index_stride);
+}
+
+int GetConvertedCount(int indexCount)
+{
+ int primitiveCount = GetPrimitiveCount(indexCount);
+ return primitiveCount * ibp_primitive_vertices_out;
+}
+
+void main()
+{
+ uint drawCount = has_draw_count != 0 ? min(draw_count_buffer[draw_count_offset], max_draw_count) : max_draw_count;
+ uint i = 0;
+
+ if (drawCount != 0)
+ {
+ int firstIndex = indirect_data_in[indirect_data_offset + 2];
+ int endIndex = firstIndex + indirect_data_in[indirect_data_offset];
+
+ for (i = 1; i < drawCount; i++)
+ {
+ int offset = int(i) * indirect_data_stride;
+ int inOffset = indirect_data_offset + offset;
+
+ int currentFirstIndex = indirect_data_in[inOffset + 2];
+ firstIndex = min(firstIndex, currentFirstIndex);
+ endIndex = max(endIndex, currentFirstIndex + indirect_data_in[inOffset]);
+ }
+
+ int indexCount = endIndex - firstIndex;
+
+ dispatch_x = (indexCount + 15) / 16;
+ src_offset += firstIndex * ibp_index_size;
+ total_primitives = GetPrimitiveCount(indexCount);
+
+ for (i = 0; i < drawCount; i++)
+ {
+ int offset = int(i) * indirect_data_stride;
+ int inOffset = indirect_data_offset + offset;
+
+ indirect_data_out[offset] = GetConvertedCount(indirect_data_in[inOffset]); // Index count
+ indirect_data_out[offset + 1] = indirect_data_in[inOffset + 1]; // Instance count
+ indirect_data_out[offset + 2] = GetConvertedCount(indirect_data_in[inOffset + 2] - firstIndex); // First index
+ indirect_data_out[offset + 3] = indirect_data_in[inOffset + 3]; // Vertex offset
+ indirect_data_out[offset + 4] = indirect_data_in[inOffset + 4]; // First instance
+ }
+ }
+
+ for (; i < max_draw_count; i++)
+ {
+ int offset = int(i) * indirect_data_stride;
+
+ indirect_data_out[offset] = 0;
+ indirect_data_out[offset + 1] = 0;
+ indirect_data_out[offset + 2] = 0;
+ indirect_data_out[offset + 3] = 0;
+ indirect_data_out[offset + 4] = 0;
+ }
+}
diff --git a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs
index 9cc7e034ea..f05f04a894 100644
--- a/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs
+++ b/Ryujinx.Graphics.Vulkan/Shaders/ShaderBinaries.cs
@@ -870,5 +870,543 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
0x1E, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x01, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00,
};
+
+ public static readonly byte[] ConvertIndexBufferShaderSource = new byte[]
+ {
+ 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x91, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
+ 0x61, 0x11, 0x00, 0x00, 0x0A, 0x00, 0x07, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F,
+ 0x38, 0x62, 0x69, 0x74, 0x5F, 0x73, 0x74, 0x6F, 0x72, 0x61, 0x67, 0x65, 0x00, 0x00, 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, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 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, 0x63, 0x61, 0x6C, 0x61,
+ 0x72, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x5F, 0x6C, 0x61, 0x79, 0x6F, 0x75, 0x74, 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,
+ 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x67, 0x6C, 0x5F, 0x47, 0x6C, 0x6F, 0x62, 0x61,
+ 0x6C, 0x49, 0x6E, 0x76, 0x6F, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x44, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x62, 0x75,
+ 0x66, 0x66, 0x65, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F,
+ 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x00, 0x06, 0x00, 0x09, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76,
+ 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x70, 0x72, 0x69, 0x6D,
+ 0x69, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x5F, 0x6F,
+ 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00,
+ 0x06, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F,
+ 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00,
+ 0x06, 0x00, 0x07, 0x00, 0x15, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F,
+ 0x62, 0x61, 0x73, 0x65, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65,
+ 0x78, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x73, 0x72, 0x63, 0x5F, 0x6F, 0x66, 0x66, 0x73,
+ 0x65, 0x74, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x74, 0x6F, 0x74, 0x61, 0x6C, 0x5F, 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x04, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F, 0x73, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x06, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0x75, 0x74, 0x5F,
+ 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x5D, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x5F, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x69, 0x6E, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x00, 0x05, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
+ 0x1C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x5B, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x61, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x62, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x87, 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, 0x15, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x0B, 0x00, 0x15, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
+ 0x59, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00,
+ 0x5A, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x5B, 0x00, 0x00, 0x00,
+ 0x5A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x5B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0x61, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00,
+ 0x1E, 0x00, 0x03, 0x00, 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0x63, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
+ 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0x6C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06, 0x00,
+ 0x0A, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00,
+ 0x86, 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,
+ 0xF7, 0x00, 0x03, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x03, 0x00,
+ 0x0D, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x89, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
+ 0x0D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x1B, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00,
+ 0x1D, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00,
+ 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00,
+ 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1E, 0x00, 0x00, 0x00,
+ 0xF9, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1F, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x2A, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
+ 0xF9, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00,
+ 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
+ 0x1F, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00,
+ 0x1C, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00,
+ 0xF6, 0x00, 0x04, 0x00, 0x31, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFA, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0xF8, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x3B, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x06, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x2E, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00,
+ 0x2E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
+ 0x8E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x4D, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x4E, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00,
+ 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
+ 0x51, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x4E, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x66, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
+ 0x8F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x59, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
+ 0x6C, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00,
+ 0x71, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
+ 0x52, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
+ 0x73, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x8F, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00,
+ 0xB1, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x4D, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x75, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00,
+ 0x75, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x71, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x06, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00,
+ 0x2E, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x7F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00,
+ 0x90, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00,
+ 0xF8, 0x00, 0x02, 0x00, 0x75, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00,
+ 0xF8, 0x00, 0x02, 0x00, 0x32, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x84, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
+ 0x2F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x31, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
+ 0x88, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x88, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00,
+ 0x38, 0x00, 0x01, 0x00,
+ };
+
+ public static readonly byte[] ConvertIndirectDataShaderSource = new byte[]
+ {
+ 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x3D, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 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, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x01, 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, 0x63, 0x61, 0x6C, 0x61, 0x72, 0x5F, 0x62, 0x6C, 0x6F, 0x63, 0x6B, 0x5F,
+ 0x6C, 0x61, 0x79, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5F, 0x70, 0x61, 0x74,
+ 0x74, 0x65, 0x72, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x00,
+ 0x06, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F,
+ 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x76, 0x65, 0x72, 0x74, 0x69, 0x63,
+ 0x65, 0x73, 0x00, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x69, 0x62, 0x70, 0x5F, 0x70, 0x72, 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5F, 0x76, 0x65,
+ 0x72, 0x74, 0x69, 0x63, 0x65, 0x73, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65,
+ 0x78, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x69,
+ 0x7A, 0x65, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x69, 0x62, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x5F, 0x69, 0x6E, 0x64,
+ 0x65, 0x78, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x69, 0x62, 0x70, 0x5F, 0x69, 0x6E, 0x64, 0x65, 0x78, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x73, 0x72, 0x63, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x74, 0x6F, 0x74, 0x61, 0x6C, 0x5F, 0x70, 0x72,
+ 0x69, 0x6D, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68,
+ 0x5F, 0x78, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
+ 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x79, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68,
+ 0x5F, 0x7A, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
+ 0x68, 0x61, 0x73, 0x5F, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00,
+ 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x78, 0x5F,
+ 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75,
+ 0x6E, 0x74, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
+ 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69,
+ 0x72, 0x65, 0x63, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x07, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75,
+ 0x6E, 0x74, 0x5F, 0x75, 0x6E, 0x69, 0x66, 0x6F, 0x72, 0x6D, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00,
+ 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x72, 0x61, 0x77, 0x5F, 0x63, 0x6F, 0x75,
+ 0x6E, 0x74, 0x5F, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
+ 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x53, 0x00, 0x00, 0x00,
+ 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5F, 0x69, 0x6E, 0x00, 0x06, 0x00, 0x08, 0x00,
+ 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
+ 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00,
+ 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0xB6, 0x00, 0x00, 0x00,
+ 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x08, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6E, 0x64, 0x69,
+ 0x72, 0x65, 0x63, 0x74, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x6F, 0x75, 0x74, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x03, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x0B, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
+ 0x23, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00,
+ 0x47, 0x00, 0x03, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x39, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x47, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x3C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x3C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x52, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x47, 0x00, 0x03, 0x00, 0x53, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x55, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x55, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0xB5, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
+ 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x47, 0x00, 0x03, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0xB8, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0xB8, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
+ 0x03, 0x01, 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,
+ 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x15, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x1C, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x1E, 0x00, 0x13, 0x00, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
+ 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00,
+ 0x33, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00,
+ 0x3B, 0x00, 0x04, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
+ 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1D, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00,
+ 0x53, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x55, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x56, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x6F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x8E, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x93, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x95, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x9D, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x03, 0x00, 0xB5, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0xB6, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x04, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00,
+ 0x3B, 0x00, 0x04, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x17, 0x00, 0x04, 0x00, 0x02, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x2C, 0x00, 0x06, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00,
+ 0x63, 0x00, 0x00, 0x00, 0x63, 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, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x36, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00,
+ 0x3E, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x40, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
+ 0x3C, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x26, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x47, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0xF9, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00,
+ 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x05, 0x00,
+ 0x33, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0xF7, 0x00, 0x03, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00,
+ 0x4E, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
+ 0x4F, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x59, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
+ 0x59, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00,
+ 0x5A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00,
+ 0x57, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00,
+ 0xF9, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0xF5, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00,
+ 0x4F, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00,
+ 0x86, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x37, 0x01, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00,
+ 0x37, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x6B, 0x00, 0x00, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x65, 0x00, 0x00, 0x00,
+ 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00,
+ 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x6F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
+ 0x6E, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x75, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x77, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x7C, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x7F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00,
+ 0x7C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00,
+ 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x2A, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
+ 0xF9, 0x00, 0x02, 0x00, 0x64, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x66, 0x00, 0x00, 0x00,
+ 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00,
+ 0x39, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x8D, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x91, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00,
+ 0x3E, 0x00, 0x03, 0x00, 0x92, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00,
+ 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00,
+ 0x97, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x9A, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x9B, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
+ 0x99, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x07, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00,
+ 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x1C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00,
+ 0x0A, 0x01, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00,
+ 0x09, 0x01, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x0D, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
+ 0x0C, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA1, 0x00, 0x00, 0x00,
+ 0x0D, 0x01, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xA2, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
+ 0xA2, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00,
+ 0x4C, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00,
+ 0xB0, 0x00, 0x05, 0x00, 0x33, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00,
+ 0x36, 0x01, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00,
+ 0xA4, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00,
+ 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00,
+ 0xAE, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00,
+ 0x57, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00,
+ 0xB2, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0xBC, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00,
+ 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x01, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00,
+ 0x1C, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00,
+ 0x0A, 0x01, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x1D, 0x01, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x21, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x16, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x17, 0x01, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
+ 0xAF, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00,
+ 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00,
+ 0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00,
+ 0xB4, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0xC4, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00,
+ 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xC6, 0x00, 0x00, 0x00,
+ 0xC5, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00,
+ 0xAF, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0xCA, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
+ 0xCA, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00,
+ 0xCB, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00,
+ 0xCC, 0x00, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x30, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x31, 0x01, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x87, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00,
+ 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x2A, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00,
+ 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD1, 0x00, 0x00, 0x00,
+ 0x2B, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00,
+ 0xAF, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0xD5, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
+ 0xD5, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00,
+ 0xD6, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00,
+ 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00,
+ 0xD8, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0xDB, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0xDF, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0xE0, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00,
+ 0x3E, 0x00, 0x03, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
+ 0xF9, 0x00, 0x02, 0x00, 0xA2, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA4, 0x00, 0x00, 0x00,
+ 0xF9, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0xF5, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00,
+ 0xE3, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0xE9, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x05, 0x00,
+ 0x33, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00,
+ 0xF6, 0x00, 0x04, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFA, 0x00, 0x04, 0x00, 0xEB, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00,
+ 0xF8, 0x00, 0x02, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0xEE, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0xEF, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF3, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00,
+ 0xF1, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0xF6, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00,
+ 0x3E, 0x00, 0x03, 0x00, 0xF6, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF9, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00,
+ 0xF1, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0xFC, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00,
+ 0x3E, 0x00, 0x03, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00,
+ 0x41, 0x00, 0x06, 0x00, 0x18, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xFF, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+ 0x3B, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xE3, 0x00, 0x00, 0x00,
+ 0xF8, 0x00, 0x02, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
+ };
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
index e54eef4f9e..48f80ad485 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
@@ -444,7 +444,8 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceVulkan12Features,
PNext = pExtendedFeatures,
DescriptorIndexing = supportedExtensions.Contains("VK_EXT_descriptor_indexing"),
- DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName)
+ DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
+ UniformBufferStandardLayout = supportedExtensions.Contains("VK_KHR_uniform_buffer_standard_layout")
};
pExtendedFeatures = &featuresVk12;
diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index ef1360e75b..4a905d33de 100644
--- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -403,7 +403,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsTextureShadowLod: false,
supportsViewportIndex: featuresVk12.ShaderOutputViewportIndex,
supportsViewportSwizzle: false,
- supportsIndirectParameters: Capabilities.SupportsIndirectParameters,
+ supportsIndirectParameters: true,
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
maximumTexturesPerStage: Constants.MaxTexturesPerStage,
diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs
index f271165741..acfcdcb468 100644
--- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration
///
/// The current version of the file format
///
- public const int CurrentVersion = 41;
+ public const int CurrentVersion = 42;
///
/// Version of the configuration file format
@@ -166,6 +166,11 @@ namespace Ryujinx.Ui.Common.Configuration
///
public bool EnableTextureRecompression { get; set; }
+ ///
+ /// Enables or disables Macro high-level emulation
+ ///
+ public bool EnableMacroHLE { get; set; }
+
///
/// Enables or disables profiled translation cache persistency
///
diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs
index 3dbbb3dd30..18cf7640ca 100644
--- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs
@@ -416,6 +416,11 @@ namespace Ryujinx.Ui.Common.Configuration
///
public ReactiveObject EnableTextureRecompression { get; private set; }
+ ///
+ /// Enables or disables Macro high-level emulation
+ ///
+ public ReactiveObject EnableMacroHLE { get; private set; }
+
///
/// Graphics backend
///
@@ -449,6 +454,8 @@ namespace Ryujinx.Ui.Common.Configuration
GraphicsBackend.Event += static (sender, e) => LogValueChange(sender, e, nameof(GraphicsBackend));
PreferredGpu = new ReactiveObject();
PreferredGpu.Event += static (sender, e) => LogValueChange(sender, e, nameof(PreferredGpu));
+ EnableMacroHLE = new ReactiveObject();
+ EnableMacroHLE.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableMacroHLE));
}
}
@@ -549,6 +556,7 @@ namespace Ryujinx.Ui.Common.Configuration
EnableVsync = Graphics.EnableVsync,
EnableShaderCache = Graphics.EnableShaderCache,
EnableTextureRecompression = Graphics.EnableTextureRecompression,
+ EnableMacroHLE = Graphics.EnableMacroHLE,
EnablePtc = System.EnablePtc,
EnableInternetAccess = System.EnableInternetAccess,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
@@ -634,6 +642,7 @@ namespace Ryujinx.Ui.Common.Configuration
Graphics.EnableVsync.Value = true;
Graphics.EnableShaderCache.Value = true;
Graphics.EnableTextureRecompression.Value = false;
+ Graphics.EnableMacroHLE.Value = true;
System.EnablePtc.Value = true;
System.EnableInternetAccess.Value = false;
System.EnableFsIntegrityChecks.Value = true;
@@ -1176,6 +1185,13 @@ namespace Ryujinx.Ui.Common.Configuration
};
}
+ if (configurationFileFormat.Version < 42)
+ {
+ Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 42.");
+
+ configurationFileFormat.EnableMacroHLE = true;
+ }
+
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
@@ -1207,6 +1223,7 @@ namespace Ryujinx.Ui.Common.Configuration
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression;
+ Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE;
System.EnablePtc.Value = configurationFileFormat.EnablePtc;
System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index c78b7a2f23..7b8b6a98db 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -1039,6 +1039,7 @@ namespace Ryujinx.Ui
Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
Graphics.Gpu.GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression;
+ Graphics.Gpu.GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
}
public void SaveConfig()
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs
index c07ad11557..901973188e 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.cs
+++ b/Ryujinx/Ui/Windows/SettingsWindow.cs
@@ -56,6 +56,7 @@ namespace Ryujinx.Ui.Windows
[GUI] CheckButton _vSyncToggle;
[GUI] CheckButton _shaderCacheToggle;
[GUI] CheckButton _textureRecompressionToggle;
+ [GUI] CheckButton _macroHLEToggle;
[GUI] CheckButton _ptcToggle;
[GUI] CheckButton _internetToggle;
[GUI] CheckButton _fsicToggle;
@@ -239,6 +240,11 @@ namespace Ryujinx.Ui.Windows
_textureRecompressionToggle.Click();
}
+ if (ConfigurationState.Instance.Graphics.EnableMacroHLE)
+ {
+ _macroHLEToggle.Click();
+ }
+
if (ConfigurationState.Instance.System.EnablePtc)
{
_ptcToggle.Click();
@@ -566,6 +572,7 @@ namespace Ryujinx.Ui.Windows
ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active;
ConfigurationState.Instance.Graphics.EnableTextureRecompression.Value = _textureRecompressionToggle.Active;
+ ConfigurationState.Instance.Graphics.EnableMacroHLE.Value = _macroHLEToggle.Active;
ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active;
ConfigurationState.Instance.System.EnableInternetAccess.Value = _internetToggle.Active;
ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade
index 24d36ebd4a..88e6dae59c 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.glade
+++ b/Ryujinx/Ui/Windows/SettingsWindow.glade
@@ -2070,6 +2070,24 @@
1
+
+
+
+ False
+ True
+ 2
+
+
@@ -2179,7 +2197,7 @@
False
True
5
- 3
+ 4
@@ -2228,7 +2246,7 @@
False
True
5
- 4
+ 5