mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-21 16:43:35 +00:00
WIP Vulkan implementation
This commit is contained in:
parent
68f9091870
commit
4513e8b226
165 changed files with 23123 additions and 399 deletions
BIN
Ryujinx.Ava/entryStorage
Normal file
BIN
Ryujinx.Ava/entryStorage
Normal file
Binary file not shown.
BIN
Ryujinx.Ava/nodeStorage
Normal file
BIN
Ryujinx.Ava/nodeStorage
Normal file
Binary file not shown.
8
Ryujinx.Common/Configuration/GraphicsBackend.cs
Normal file
8
Ryujinx.Common/Configuration/GraphicsBackend.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
public enum GraphicsBackend
|
||||
{
|
||||
Vulkan,
|
||||
OpenGl
|
||||
}
|
||||
}
|
|
@ -165,6 +165,120 @@ namespace Ryujinx.Graphics.GAL
|
|||
|
||||
public static class FormatExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the texture format is valid to use as image format.
|
||||
/// </summary>
|
||||
/// <param name="format">Texture format</param>
|
||||
/// <returns>True if the texture can be used as image, false otherwise</returns>
|
||||
public static bool IsImageCompatible(this Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case Format.R8Unorm:
|
||||
case Format.R8Snorm:
|
||||
case Format.R8Uint:
|
||||
case Format.R8Sint:
|
||||
case Format.R16Float:
|
||||
case Format.R16Unorm:
|
||||
case Format.R16Snorm:
|
||||
case Format.R16Uint:
|
||||
case Format.R16Sint:
|
||||
case Format.R32Float:
|
||||
case Format.R32Uint:
|
||||
case Format.R32Sint:
|
||||
case Format.R8G8Unorm:
|
||||
case Format.R8G8Snorm:
|
||||
case Format.R8G8Uint:
|
||||
case Format.R8G8Sint:
|
||||
case Format.R16G16Float:
|
||||
case Format.R16G16Unorm:
|
||||
case Format.R16G16Snorm:
|
||||
case Format.R16G16Uint:
|
||||
case Format.R16G16Sint:
|
||||
case Format.R32G32Float:
|
||||
case Format.R32G32Uint:
|
||||
case Format.R32G32Sint:
|
||||
case Format.R8G8B8A8Unorm:
|
||||
case Format.R8G8B8A8Snorm:
|
||||
case Format.R8G8B8A8Uint:
|
||||
case Format.R8G8B8A8Sint:
|
||||
case Format.R16G16B16A16Float:
|
||||
case Format.R16G16B16A16Unorm:
|
||||
case Format.R16G16B16A16Snorm:
|
||||
case Format.R16G16B16A16Uint:
|
||||
case Format.R16G16B16A16Sint:
|
||||
case Format.R32G32B32A32Float:
|
||||
case Format.R32G32B32A32Uint:
|
||||
case Format.R32G32B32A32Sint:
|
||||
case Format.R10G10B10A2Unorm:
|
||||
case Format.R10G10B10A2Uint:
|
||||
case Format.R11G11B10Float:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the texture format is valid to use as render target color format.
|
||||
/// </summary>
|
||||
/// <param name="format">Texture format</param>
|
||||
/// <returns>True if the texture can be used as render target, false otherwise</returns>
|
||||
public static bool IsRtColorCompatible(this Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case Format.R32G32B32A32Float:
|
||||
case Format.R32G32B32A32Sint:
|
||||
case Format.R32G32B32A32Uint:
|
||||
case Format.R16G16B16A16Unorm:
|
||||
case Format.R16G16B16A16Snorm:
|
||||
case Format.R16G16B16A16Sint:
|
||||
case Format.R16G16B16A16Uint:
|
||||
case Format.R16G16B16A16Float:
|
||||
case Format.R32G32Float:
|
||||
case Format.R32G32Sint:
|
||||
case Format.R32G32Uint:
|
||||
case Format.B8G8R8A8Unorm:
|
||||
case Format.B8G8R8A8Srgb:
|
||||
case Format.R10G10B10A2Unorm:
|
||||
case Format.R10G10B10A2Uint:
|
||||
case Format.R8G8B8A8Unorm:
|
||||
case Format.R8G8B8A8Srgb:
|
||||
case Format.R8G8B8A8Snorm:
|
||||
case Format.R8G8B8A8Sint:
|
||||
case Format.R8G8B8A8Uint:
|
||||
case Format.R16G16Unorm:
|
||||
case Format.R16G16Snorm:
|
||||
case Format.R16G16Sint:
|
||||
case Format.R16G16Uint:
|
||||
case Format.R16G16Float:
|
||||
case Format.R11G11B10Float:
|
||||
case Format.R32Sint:
|
||||
case Format.R32Uint:
|
||||
case Format.R32Float:
|
||||
case Format.B5G6R5Unorm:
|
||||
case Format.B5G5R5A1Unorm:
|
||||
case Format.R8G8Unorm:
|
||||
case Format.R8G8Snorm:
|
||||
case Format.R8G8Sint:
|
||||
case Format.R8G8Uint:
|
||||
case Format.R16Unorm:
|
||||
case Format.R16Snorm:
|
||||
case Format.R16Sint:
|
||||
case Format.R16Uint:
|
||||
case Format.R16Float:
|
||||
case Format.R8Unorm:
|
||||
case Format.R8Snorm:
|
||||
case Format.R8Sint:
|
||||
case Format.R8Uint:
|
||||
case Format.B5G5R5X1Unorm:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the texture format is an ASTC format.
|
||||
/// </summary>
|
||||
|
|
18
Ryujinx.Graphics.GAL/HardwareInfo.cs
Normal file
18
Ryujinx.Graphics.GAL/HardwareInfo.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public struct HardwareInfo
|
||||
{
|
||||
public string GpuVendor { get; }
|
||||
public string GpuModel { get; }
|
||||
|
||||
public HardwareInfo(string gpuVendor, string gpuModel)
|
||||
{
|
||||
GpuVendor = gpuVendor;
|
||||
GpuModel = gpuModel;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -77,15 +77,13 @@ namespace Ryujinx.Graphics.GAL
|
|||
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask);
|
||||
void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
|
||||
|
||||
void SetSampler(int binding, ISampler sampler);
|
||||
|
||||
void SetScissor(int index, bool enable, int x, int y, int width, int height);
|
||||
void SetScissors(ReadOnlySpan<Rectangle<int>> regions);
|
||||
|
||||
void SetStencilTest(StencilTestDescriptor stencilTest);
|
||||
|
||||
void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers);
|
||||
|
||||
void SetTexture(int binding, ITexture texture);
|
||||
void SetTextureAndSampler(int binding, ITexture texture, ISampler sampler);
|
||||
|
||||
void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
|
||||
void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers);
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace Ryujinx.Graphics.GAL
|
|||
ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size);
|
||||
|
||||
Capabilities GetCapabilities();
|
||||
HardwareInfo GetHardwareInfo();
|
||||
|
||||
IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info);
|
||||
|
||||
|
|
|
@ -199,14 +199,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||
SetRenderTargetScaleCommand.Run(ref GetCommand<SetRenderTargetScaleCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetRenderTargets] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetRenderTargetsCommand.Run(ref GetCommand<SetRenderTargetsCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetSampler] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetSamplerCommand.Run(ref GetCommand<SetSamplerCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetScissor] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetScissorCommand.Run(ref GetCommand<SetScissorCommand>(memory), threaded, renderer);
|
||||
SetScissorsCommand.Run(ref GetCommand<SetScissorsCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetStencilTest] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetStencilTestCommand.Run(ref GetCommand<SetStencilTestCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetTexture] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetTextureCommand.Run(ref GetCommand<SetTextureCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetTextureAndSampler] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetTextureAndSamplerCommand.Run(ref GetCommand<SetTextureAndSamplerCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetUserClipDistance] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetUserClipDistanceCommand.Run(ref GetCommand<SetUserClipDistanceCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetVertexAttribs] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
|
|
|
@ -81,10 +81,9 @@
|
|||
SetRenderTargetColorMasks,
|
||||
SetRenderTargetScale,
|
||||
SetRenderTargets,
|
||||
SetSampler,
|
||||
SetScissor,
|
||||
SetStencilTest,
|
||||
SetTexture,
|
||||
SetTextureAndSampler,
|
||||
SetUserClipDistance,
|
||||
SetVertexAttribs,
|
||||
SetVertexBuffers,
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct SetSamplerCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.SetSampler;
|
||||
private int _index;
|
||||
private TableRef<ISampler> _sampler;
|
||||
|
||||
public void Set(int index, TableRef<ISampler> sampler)
|
||||
{
|
||||
_index = index;
|
||||
_sampler = sampler;
|
||||
}
|
||||
|
||||
public static void Run(ref SetSamplerCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetSampler(command._index, command._sampler.GetAs<ThreadedSampler>(threaded)?.Base);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct SetScissorCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.SetScissor;
|
||||
private int _index;
|
||||
private bool _enable;
|
||||
private int _x;
|
||||
private int _y;
|
||||
private int _width;
|
||||
private int _height;
|
||||
|
||||
public void Set(int index, bool enable, int x, int y, int width, int height)
|
||||
{
|
||||
_index = index;
|
||||
_enable = enable;
|
||||
_x = x;
|
||||
_y = y;
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
public static void Run(ref SetScissorCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetScissor(command._index, command._enable, command._x, command._y, command._width, command._height);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct SetScissorsCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.SetScissor;
|
||||
private SpanRef<Rectangle<int>> _scissors;
|
||||
|
||||
public void Set(SpanRef<Rectangle<int>> scissors)
|
||||
{
|
||||
_scissors = scissors;
|
||||
}
|
||||
|
||||
public static void Run(ref SetScissorsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetScissors(command._scissors.Get(threaded));
|
||||
|
||||
command._scissors.Dispose(threaded);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct SetTextureAndSamplerCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.SetTextureAndSampler;
|
||||
private int _binding;
|
||||
private TableRef<ITexture> _texture;
|
||||
private TableRef<ISampler> _sampler;
|
||||
|
||||
public void Set(int binding, TableRef<ITexture> texture, TableRef<ISampler> sampler)
|
||||
{
|
||||
_binding = binding;
|
||||
_texture = texture;
|
||||
_sampler = sampler;
|
||||
}
|
||||
|
||||
public static void Run(ref SetTextureAndSamplerCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetTextureAndSampler(command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._sampler.GetAs<ThreadedSampler>(threaded)?.Base);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct SetTextureCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.SetTexture;
|
||||
private int _binding;
|
||||
private TableRef<ITexture> _texture;
|
||||
|
||||
public void Set(int binding, TableRef<ITexture> texture)
|
||||
{
|
||||
_binding = binding;
|
||||
_texture = texture;
|
||||
}
|
||||
|
||||
public static void Run(ref SetTextureCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetTexture(command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -244,15 +244,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetSampler(int binding, ISampler sampler)
|
||||
public void SetScissors(ReadOnlySpan<Rectangle<int>> scissors)
|
||||
{
|
||||
_renderer.New<SetSamplerCommand>().Set(binding, Ref(sampler));
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetScissor(int index, bool enable, int x, int y, int width, int height)
|
||||
{
|
||||
_renderer.New<SetScissorCommand>().Set(index, enable, x, y, width, height);
|
||||
_renderer.New<SetScissorsCommand>().Set(_renderer.CopySpan(scissors));
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
|
@ -268,9 +262,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetTexture(int binding, ITexture texture)
|
||||
public void SetTextureAndSampler(int binding, ITexture texture, ISampler sampler)
|
||||
{
|
||||
_renderer.New<SetTextureCommand>().Set(binding, Ref(texture));
|
||||
_renderer.New<SetTextureAndSamplerCommand>().Set(binding, Ref(texture), Ref(sampler));
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
|
|
|
@ -337,6 +337,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||
return box.Result;
|
||||
}
|
||||
|
||||
public HardwareInfo GetHardwareInfo()
|
||||
{
|
||||
return _baseRenderer.GetHardwareInfo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the base renderer. Must be called on the render thread.
|
||||
/// </summary>
|
||||
|
|
18
Ryujinx.Graphics.GAL/Rectangle.cs
Normal file
18
Ryujinx.Graphics.GAL/Rectangle.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public struct Rectangle<T> where T : unmanaged
|
||||
{
|
||||
public T X { get; }
|
||||
public T Y { get; }
|
||||
public T Width { get; }
|
||||
public T Height { get; }
|
||||
|
||||
public Rectangle(T x, T y, T width, T height)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
}
|
||||
}
|
30
Ryujinx.Graphics.GAL/ShaderBindings.cs
Normal file
30
Ryujinx.Graphics.GAL/ShaderBindings.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public struct ShaderBindings
|
||||
{
|
||||
public IReadOnlyCollection<int> UniformBufferBindings { get; }
|
||||
public IReadOnlyCollection<int> StorageBufferBindings { get; }
|
||||
public IReadOnlyCollection<int> TextureBindings { get; }
|
||||
public IReadOnlyCollection<int> ImageBindings { get; }
|
||||
public IReadOnlyCollection<int> BufferTextureBindings { get; }
|
||||
public IReadOnlyCollection<int> BufferImageBindings { get; }
|
||||
|
||||
public ShaderBindings(
|
||||
IReadOnlyCollection<int> uniformBufferBindings,
|
||||
IReadOnlyCollection<int> storageBufferBindings,
|
||||
IReadOnlyCollection<int> textureBindings,
|
||||
IReadOnlyCollection<int> imageBindings,
|
||||
IReadOnlyCollection<int> bufferTextureBindings,
|
||||
IReadOnlyCollection<int> bufferImageBindings)
|
||||
{
|
||||
UniformBufferBindings = uniformBufferBindings;
|
||||
StorageBufferBindings = storageBufferBindings;
|
||||
TextureBindings = textureBindings;
|
||||
ImageBindings = imageBindings;
|
||||
BufferTextureBindings = bufferTextureBindings;
|
||||
BufferImageBindings = bufferImageBindings;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,22 +7,24 @@ namespace Ryujinx.Graphics.GAL
|
|||
{
|
||||
public string Code { get; }
|
||||
public byte[] BinaryCode { get; }
|
||||
public ShaderBindings Bindings { get; }
|
||||
public ShaderStage Stage { get; }
|
||||
public TargetLanguage Language { get; }
|
||||
|
||||
public ShaderSource(string code, byte[] binaryCode, ShaderStage stage, TargetLanguage language)
|
||||
public ShaderSource(string code, byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language)
|
||||
{
|
||||
Code = code;
|
||||
BinaryCode = binaryCode;
|
||||
Bindings = bindings;
|
||||
Stage = stage;
|
||||
Language = language;
|
||||
}
|
||||
|
||||
public ShaderSource(string code, ShaderStage stage, TargetLanguage language) : this(code, null, stage, language)
|
||||
public ShaderSource(string code, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(code, null, bindings, stage, language)
|
||||
{
|
||||
}
|
||||
|
||||
public ShaderSource(byte[] binaryCode, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, stage, language)
|
||||
public ShaderSource(byte[] binaryCode, ShaderBindings bindings, ShaderStage stage, TargetLanguage language) : this(null, binaryCode, bindings, stage, language)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.GAL
|
|||
{
|
||||
public struct Viewport
|
||||
{
|
||||
public RectangleF Region { get; }
|
||||
public Rectangle<float> Region { get; }
|
||||
|
||||
public ViewportSwizzle SwizzleX { get; }
|
||||
public ViewportSwizzle SwizzleY { get; }
|
||||
|
@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.GAL
|
|||
public float DepthFar { get; }
|
||||
|
||||
public Viewport(
|
||||
RectangleF region,
|
||||
Rectangle<float> region,
|
||||
ViewportSwizzle swizzleX,
|
||||
ViewportSwizzle swizzleY,
|
||||
ViewportSwizzle swizzleZ,
|
||||
|
|
|
@ -40,6 +40,22 @@ namespace Ryujinx.Graphics.Gpu
|
|||
/// </summary>
|
||||
public const int TotalTransformFeedbackBuffers = 4;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of textures on a single shader stage.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The maximum number of textures is API limited, the hardware supports a unlimited amount.
|
||||
/// </remarks>
|
||||
public const int TotalTextures = 32;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of images on a single shader stage.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The maximum number of images is API limited, the hardware supports a unlimited amount.
|
||||
/// </remarks>
|
||||
public const int TotalImages = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of render target color buffers.
|
||||
/// </summary>
|
||||
|
|
|
@ -559,7 +559,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
scissorH = (int)MathF.Ceiling(scissorH * scale);
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.SetScissor(0, true, scissorX, scissorY, scissorW, scissorH);
|
||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
||||
scissors[0] = new Rectangle<int>(scissorX, scissorY, scissorW, scissorH);
|
||||
|
||||
_context.Renderer.Pipeline.SetScissors(scissors);
|
||||
}
|
||||
|
||||
if (clipMismatch)
|
||||
|
|
|
@ -493,11 +493,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
/// </summary>
|
||||
public void UpdateScissorState()
|
||||
{
|
||||
const int MinX = 0;
|
||||
const int MinY = 0;
|
||||
const int MaxW = 0xffff;
|
||||
const int MaxH = 0xffff;
|
||||
|
||||
Span<Rectangle<int>> regions = stackalloc Rectangle<int>[Constants.TotalViewports];
|
||||
|
||||
for (int index = 0; index < Constants.TotalViewports; index++)
|
||||
{
|
||||
ScissorState scissor = _state.State.ScissorState[index];
|
||||
|
||||
bool enable = scissor.Enable && (scissor.X1 != 0 || scissor.Y1 != 0 || scissor.X2 != 0xffff || scissor.Y2 != 0xffff);
|
||||
bool enable = scissor.Enable && (scissor.X1 != MinX ||
|
||||
scissor.Y1 != MinY ||
|
||||
scissor.X2 != MaxW ||
|
||||
scissor.Y2 != MaxH);
|
||||
|
||||
if (enable)
|
||||
{
|
||||
|
@ -527,13 +537,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
height = (int)MathF.Ceiling(height * scale);
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.SetScissor(index, true, x, y, width, height);
|
||||
regions[index] = new Rectangle<int>(x, y, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.SetScissor(index, false, 0, 0, 0, 0);
|
||||
regions[index] = new Rectangle<int>(MinX, MinY, MaxW, MaxH);
|
||||
}
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.SetScissors(regions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -592,7 +604,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
ref var scissor = ref _state.State.ScreenScissorState;
|
||||
|
||||
float rScale = _channel.TextureManager.RenderTargetScale;
|
||||
var scissorRect = new RectangleF(0, 0, (scissor.X + scissor.Width) * rScale, (scissor.Y + scissor.Height) * rScale);
|
||||
var scissorRect = new Rectangle<float>(0, 0, (scissor.X + scissor.Width) * rScale, (scissor.Y + scissor.Height) * rScale);
|
||||
|
||||
viewports[index] = new Viewport(scissorRect, ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, ViewportSwizzle.PositiveZ, ViewportSwizzle.PositiveW, 0, 1);
|
||||
continue;
|
||||
|
@ -629,7 +641,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
height *= scale;
|
||||
}
|
||||
|
||||
RectangleF region = new RectangleF(x, y, width, height);
|
||||
Rectangle<float> region = new Rectangle<float>(x, y, width, height);
|
||||
|
||||
ViewportSwizzle swizzleX = transform.UnpackSwizzleX();
|
||||
ViewportSwizzle swizzleY = transform.UnpackSwizzleY();
|
||||
|
@ -649,6 +661,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar);
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.SetDepthMode(GetDepthMode());
|
||||
_context.Renderer.Pipeline.SetViewports(0, viewports, disableTransform);
|
||||
}
|
||||
|
||||
|
@ -1231,7 +1244,29 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
_state.State.EarlyZForce,
|
||||
_drawState.Topology,
|
||||
_state.State.TessMode,
|
||||
_state.State.ViewportTransformEnable == 0);
|
||||
_state.State.ViewportTransformEnable == 0,
|
||||
GetDepthMode() == DepthMode.MinusOneToOne,
|
||||
_state.State.VertexProgramPointSize,
|
||||
_state.State.PointSize);
|
||||
}
|
||||
|
||||
private DepthMode GetDepthMode()
|
||||
{
|
||||
ref var transform = ref _state.State.ViewportTransform[0];
|
||||
ref var extents = ref _state.State.ViewportExtents[0];
|
||||
|
||||
// Try to guess the depth mode being used on the high level API
|
||||
// based on current transform.
|
||||
// It is setup like so by said APIs:
|
||||
// If depth mode is ZeroToOne:
|
||||
// TranslateZ = Near
|
||||
// ScaleZ = Far - Near
|
||||
// If depth mode is MinusOneToOne:
|
||||
// TranslateZ = (Near + Far) / 2
|
||||
// ScaleZ = (Far - Near) / 2
|
||||
// DepthNear/Far are sorted such as that Near is always less than Far.
|
||||
return extents.DepthNear != transform.TranslateZ &&
|
||||
extents.DepthFar != transform.TranslateZ ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -311,6 +311,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
{
|
||||
return Attribute & 0x3fe00000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the Maxwell attribute component type.
|
||||
/// </summary>
|
||||
/// <returns>Attribute component type</returns>
|
||||
public uint UnpackType()
|
||||
{
|
||||
return (Attribute >> 27) & 7;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -56,5 +56,10 @@ namespace Ryujinx.Graphics.Gpu
|
|||
/// Enables or disables the shader cache.
|
||||
/// </summary>
|
||||
public static bool EnableShaderCache;
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables shader SPIR-V compilation.
|
||||
/// </summary>
|
||||
public static bool EnableSpirvCompilation;
|
||||
}
|
||||
}
|
|
@ -435,6 +435,25 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counts the total number of texture bindings used by all shader stages.
|
||||
/// </summary>
|
||||
/// <returns>The total amount of textures used</returns>
|
||||
private int GetTextureBindingsCount()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < _textureBindings.Length; i++)
|
||||
{
|
||||
if (_textureBindings[i] != null)
|
||||
{
|
||||
count += _textureBindings[i].Length;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the texture bindings are visible to the host GPU.
|
||||
/// Note: this actually performs the binding using the host graphics API.
|
||||
|
@ -511,7 +530,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
state.ScaleIndex = index;
|
||||
state.UsageFlags = usageFlags;
|
||||
|
||||
_context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTextureRebind);
|
||||
_context.Renderer.Pipeline.SetTextureAndSampler(bindingInfo.Binding, hostTextureRebind, state.Sampler);
|
||||
}
|
||||
|
||||
continue;
|
||||
|
@ -524,7 +543,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
specStateMatches &= specState.MatchesTexture(stage, index, descriptor);
|
||||
|
||||
Sampler sampler = _samplerPool?.Get(samplerId);
|
||||
|
||||
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
||||
|
||||
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
||||
{
|
||||
|
@ -535,7 +557,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
else
|
||||
{
|
||||
if (state.Texture != hostTexture)
|
||||
if (state.Texture != hostTexture || state.Sampler != hostSampler)
|
||||
{
|
||||
if (UpdateScale(texture, usageFlags, index, stage))
|
||||
{
|
||||
|
@ -546,22 +568,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
state.ScaleIndex = index;
|
||||
state.UsageFlags = usageFlags;
|
||||
|
||||
_context.Renderer.Pipeline.SetTexture(bindingInfo.Binding, hostTexture);
|
||||
}
|
||||
|
||||
Sampler sampler = samplerPool?.Get(samplerId);
|
||||
state.CachedSampler = sampler;
|
||||
|
||||
ISampler hostSampler = sampler?.GetHostSampler(texture);
|
||||
|
||||
if (state.Sampler != hostSampler)
|
||||
{
|
||||
state.Sampler = hostSampler;
|
||||
|
||||
_context.Renderer.Pipeline.SetSampler(bindingInfo.Binding, hostSampler);
|
||||
_context.Renderer.Pipeline.SetTextureAndSampler(bindingInfo.Binding, hostTexture, hostSampler);
|
||||
}
|
||||
|
||||
state.CachedTexture = texture;
|
||||
state.CachedSampler = sampler;
|
||||
state.InvalidatedSequence = texture?.InvalidatedSequence ?? 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -435,7 +435,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.SetTexture(binding.BindingInfo.Binding, binding.Texture);
|
||||
_context.Renderer.Pipeline.SetTextureAndSampler(binding.BindingInfo.Binding, binding.Texture, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -252,6 +252,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
Write(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
|
||||
}
|
||||
|
||||
public void WriteUntracked<T>(ulong va, T value) where T : unmanaged
|
||||
{
|
||||
WriteUntracked(va, MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to GPU mapped memory.
|
||||
/// </summary>
|
||||
|
|
|
@ -167,7 +167,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
|
|||
accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce),
|
||||
topology,
|
||||
tessMode,
|
||||
false);
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
1f);
|
||||
|
||||
TransformFeedbackDescriptor[] tfdNew = null;
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
ShaderSpecializationState oldSpecState,
|
||||
ShaderSpecializationState newSpecState,
|
||||
ResourceCounts counts,
|
||||
int stageIndex) : base(context)
|
||||
int stageIndex) : base(context, counts, stageIndex)
|
||||
{
|
||||
_data = data;
|
||||
_cb1Data = cb1Data;
|
||||
|
@ -67,30 +67,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
return MemoryMarshal.Cast<byte, ulong>(_data.Span.Slice((int)address));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingConstantBuffer(int index)
|
||||
{
|
||||
return _resourceCounts.UniformBuffersCount++;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingStorageBuffer(int index)
|
||||
{
|
||||
return _resourceCounts.StorageBuffersCount++;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingTexture(int index)
|
||||
{
|
||||
return _resourceCounts.TexturesCount++;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingImage(int index)
|
||||
{
|
||||
return _resourceCounts.ImagesCount++;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryComputeLocalSizeX() => _oldSpecState.ComputeState.LocalSizeX;
|
||||
|
||||
|
|
|
@ -567,6 +567,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1];
|
||||
TranslatorContext nextStage = null;
|
||||
|
||||
TargetApi api = _context.Capabilities.Api;
|
||||
|
||||
for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
|
||||
{
|
||||
CachedShaderStage shader = shaders[stageIndex + 1];
|
||||
|
@ -577,7 +579,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
byte[] cb1Data = shader.Cb1Data;
|
||||
|
||||
DiskCacheGpuAccessor gpuAccessor = new DiskCacheGpuAccessor(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex);
|
||||
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, DefaultFlags, 0);
|
||||
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0);
|
||||
|
||||
if (nextStage != null)
|
||||
{
|
||||
|
@ -590,7 +592,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
byte[] cb1DataA = shaders[0].Cb1Data;
|
||||
|
||||
DiskCacheGpuAccessor gpuAccessorA = new DiskCacheGpuAccessor(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0);
|
||||
translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, DefaultFlags | TranslationFlags.VertexA, 0);
|
||||
translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0);
|
||||
}
|
||||
|
||||
translatorContexts[stageIndex + 1] = currentStage;
|
||||
|
@ -651,7 +653,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
ShaderSpecializationState newSpecState = new ShaderSpecializationState(specState.ComputeState);
|
||||
DiskCacheGpuAccessor gpuAccessor = new DiskCacheGpuAccessor(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0);
|
||||
|
||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, 0);
|
||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0);
|
||||
|
||||
ShaderProgram program = translatorContext.Translate();
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
{
|
||||
private readonly GpuChannel _channel;
|
||||
private readonly GpuAccessorState _state;
|
||||
private readonly AttributeType[] _attributeTypes;
|
||||
private readonly int _stageIndex;
|
||||
private readonly bool _compute;
|
||||
|
||||
|
@ -22,11 +23,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="context">GPU context</param>
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param>
|
||||
/// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param>
|
||||
public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state, int stageIndex) : base(context)
|
||||
public GpuAccessor(
|
||||
GpuContext context,
|
||||
GpuChannel channel,
|
||||
GpuAccessorState state,
|
||||
AttributeType[] attributeTypes,
|
||||
int stageIndex) : base(context, state.ResourceCounts, stageIndex)
|
||||
{
|
||||
_channel = channel;
|
||||
_state = state;
|
||||
_attributeTypes = attributeTypes;
|
||||
_stageIndex = stageIndex;
|
||||
}
|
||||
|
||||
|
@ -36,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="context">GPU context</param>
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state) : base(context)
|
||||
public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state) : base(context, state.ResourceCounts, 0)
|
||||
{
|
||||
_channel = channel;
|
||||
_state = state;
|
||||
|
@ -67,27 +75,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingConstantBuffer(int index)
|
||||
public AttributeType QueryAttributeType(int location)
|
||||
{
|
||||
return _state.ResourceCounts.UniformBuffersCount++;
|
||||
if (_attributeTypes != null)
|
||||
{
|
||||
return _attributeTypes[location];
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingStorageBuffer(int index)
|
||||
{
|
||||
return _state.ResourceCounts.StorageBuffersCount++;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingTexture(int index)
|
||||
{
|
||||
return _state.ResourceCounts.TexturesCount++;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingImage(int index)
|
||||
{
|
||||
return _state.ResourceCounts.ImagesCount++;
|
||||
return AttributeType.Float;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -123,6 +118,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
return ConvertToInputTopology(_state.GraphicsState.Topology, _state.GraphicsState.TessellationMode);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool QueryProgramPointSize()
|
||||
{
|
||||
return _state.GraphicsState.ProgramPointSizeEnable;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public float QueryPointSize()
|
||||
{
|
||||
return _state.GraphicsState.PointSize;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool QueryTessCw()
|
||||
{
|
||||
|
@ -192,6 +199,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool QueryTransformDepthMinusOneToOne()
|
||||
{
|
||||
return _state.GraphicsState.DepthMode;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool QueryTransformFeedbackEnabled()
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
|
|||
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
|
@ -11,14 +12,86 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
class GpuAccessorBase
|
||||
{
|
||||
private readonly GpuContext _context;
|
||||
private readonly ResourceCounts _resourceCounts;
|
||||
private readonly int _stageIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU accessor.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
public GpuAccessorBase(GpuContext context)
|
||||
public GpuAccessorBase(GpuContext context, ResourceCounts resourceCounts, int stageIndex)
|
||||
{
|
||||
_context = context;
|
||||
_resourceCounts = resourceCounts;
|
||||
_stageIndex = stageIndex;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingConstantBuffer(int index)
|
||||
{
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
return 1 + GetStageIndex() * 18 + index;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _resourceCounts.UniformBuffersCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingStorageBuffer(int index)
|
||||
{
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
return GetStageIndex() * 16 + index;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _resourceCounts.StorageBuffersCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingTexture(int index)
|
||||
{
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
return GetStageIndex() * 32 + index;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _resourceCounts.TexturesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int QueryBindingImage(int index)
|
||||
{
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
return GetStageIndex() * 8 + index;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _resourceCounts.ImagesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
private int GetStageIndex()
|
||||
{
|
||||
// This is just a simple remapping to ensure that most frequently used shader stages
|
||||
// have the lowest binding numbers.
|
||||
// This is useful because if we need to run on a system with a low limit on the bindings,
|
||||
// then we can still get most games working as the most common shaders will have low binding numbers.
|
||||
return _stageIndex switch
|
||||
{
|
||||
4 => 1, // Fragment
|
||||
3 => 2, // Geometry
|
||||
1 => 3, // Tessellation control
|
||||
2 => 4, // Tessellation evaluation
|
||||
_ => 0 // Vertex/Compute
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -30,6 +30,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// </summary>
|
||||
public readonly bool ViewportTransformDisable;
|
||||
|
||||
/// <summary>
|
||||
/// Depth mode zero to one or minus one to one.
|
||||
/// </summary>
|
||||
public readonly bool DepthMode;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the point size is set on the shader or is fixed.
|
||||
/// </summary>
|
||||
public readonly bool ProgramPointSizeEnable;
|
||||
|
||||
/// <summary>
|
||||
/// Point size if not set from shader.
|
||||
/// </summary>
|
||||
public readonly float PointSize;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU graphics state.
|
||||
/// </summary>
|
||||
|
@ -37,12 +52,25 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="tessellationMode">Tessellation mode</param>
|
||||
/// <param name="viewportTransformDisable">Indicates whenever the viewport transform is disabled</param>
|
||||
public GpuChannelGraphicsState(bool earlyZForce, PrimitiveTopology topology, TessMode tessellationMode, bool viewportTransformDisable)
|
||||
/// <param name="depthMode">Depth mode zero to one or minus one to one</param>
|
||||
/// <param name="programPointSizeEnable">Indicates if the point size is set on the shader or is fixed</param>
|
||||
/// <param name="pointSize">Point size if not set from shader</param>
|
||||
public GpuChannelGraphicsState(
|
||||
bool earlyZForce,
|
||||
PrimitiveTopology topology,
|
||||
TessMode tessellationMode,
|
||||
bool viewportTransformDisable,
|
||||
bool depthMode,
|
||||
bool programPointSizeEnable,
|
||||
float pointSize)
|
||||
{
|
||||
EarlyZForce = earlyZForce;
|
||||
Topology = topology;
|
||||
TessellationMode = tessellationMode;
|
||||
ViewportTransformDisable = viewportTransformDisable;
|
||||
DepthMode = depthMode;
|
||||
ProgramPointSizeEnable = programPointSizeEnable;
|
||||
PointSize = pointSize;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using Ryujinx.Graphics.Shader;
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
@ -214,7 +215,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
GpuAccessorState gpuAccessorState = new GpuAccessorState(poolState, computeState, default, specState);
|
||||
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState);
|
||||
|
||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, gpuVa);
|
||||
TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, gpuVa);
|
||||
|
||||
TranslatedShader translatedShader = TranslateShader(_dumper, channel, translatorContext, cachedGuestCode);
|
||||
|
||||
|
@ -260,6 +261,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
return gpShaders;
|
||||
}
|
||||
|
||||
AttributeType[] attributeTypes = new AttributeType[Constants.TotalVertexAttribs];
|
||||
|
||||
for (int location = 0; location < attributeTypes.Length; location++)
|
||||
{
|
||||
attributeTypes[location] = state.VertexAttribState[location].UnpackType() switch
|
||||
{
|
||||
3 => AttributeType.Sint,
|
||||
4 => AttributeType.Uint,
|
||||
_ => AttributeType.Float
|
||||
};
|
||||
}
|
||||
|
||||
TransformFeedbackDescriptor[] transformFeedbackDescriptors = GetTransformFeedbackDescriptors(ref state);
|
||||
|
||||
ShaderSpecializationState specState = new ShaderSpecializationState(graphicsState, transformFeedbackDescriptors);
|
||||
|
@ -270,14 +283,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1];
|
||||
TranslatorContext nextStage = null;
|
||||
|
||||
TargetApi api = _context.Capabilities.Api;
|
||||
|
||||
for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--)
|
||||
{
|
||||
ulong gpuVa = addressesSpan[stageIndex + 1];
|
||||
|
||||
if (gpuVa != 0)
|
||||
{
|
||||
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState, stageIndex);
|
||||
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, DefaultFlags, gpuVa);
|
||||
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gpuAccessorState, attributeTypes, stageIndex);
|
||||
TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, gpuVa);
|
||||
|
||||
if (nextStage != null)
|
||||
{
|
||||
|
@ -286,7 +301,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
if (stageIndex == 0 && addresses.VertexA != 0)
|
||||
{
|
||||
translatorContexts[0] = DecodeGraphicsShader(gpuAccessor, DefaultFlags | TranslationFlags.VertexA, addresses.VertexA);
|
||||
translatorContexts[0] = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags | TranslationFlags.VertexA, addresses.VertexA);
|
||||
}
|
||||
|
||||
translatorContexts[stageIndex + 1] = currentStage;
|
||||
|
@ -355,7 +370,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <returns>Shader source</returns>
|
||||
public static ShaderSource CreateShaderSource(ShaderProgram program)
|
||||
{
|
||||
return new ShaderSource(program.Code, program.BinaryCode, program.Info.Stage, program.Language);
|
||||
return new ShaderSource(program.Code, program.BinaryCode, GetBindings(program.Info), program.Info.Stage, program.Language);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -480,11 +495,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// Decode the binary Maxwell shader code to a translator context.
|
||||
/// </summary>
|
||||
/// <param name="gpuAccessor">GPU state accessor</param>
|
||||
/// <param name="api">Graphics API that will be used with the shader</param>
|
||||
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
|
||||
/// <returns>The generated translator context</returns>
|
||||
public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, ulong gpuVa)
|
||||
public static TranslatorContext DecodeComputeShader(IGpuAccessor gpuAccessor, TargetApi api, ulong gpuVa)
|
||||
{
|
||||
var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, DefaultFlags | TranslationFlags.Compute);
|
||||
var options = CreateTranslationOptions(api, DefaultFlags | TranslationFlags.Compute);
|
||||
return Translator.CreateContext(gpuVa, gpuAccessor, options);
|
||||
}
|
||||
|
||||
|
@ -495,12 +511,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
|
||||
/// </remarks>
|
||||
/// <param name="gpuAccessor">GPU state accessor</param>
|
||||
/// <param name="api">Graphics API that will be used with the shader</param>
|
||||
/// <param name="flags">Flags that controls shader translation</param>
|
||||
/// <param name="gpuVa">GPU virtual address of the shader code</param>
|
||||
/// <returns>The generated translator context</returns>
|
||||
public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TranslationFlags flags, ulong gpuVa)
|
||||
public static TranslatorContext DecodeGraphicsShader(IGpuAccessor gpuAccessor, TargetApi api, TranslationFlags flags, ulong gpuVa)
|
||||
{
|
||||
var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags);
|
||||
var options = CreateTranslationOptions(api, flags);
|
||||
return Translator.CreateContext(gpuVa, gpuAccessor, options);
|
||||
}
|
||||
|
||||
|
@ -595,6 +612,41 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
};
|
||||
}
|
||||
|
||||
private static ShaderBindings GetBindings(ShaderProgramInfo info)
|
||||
{
|
||||
static bool IsBuffer(TextureDescriptor descriptor)
|
||||
{
|
||||
return (descriptor.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
|
||||
}
|
||||
|
||||
static bool IsNotBuffer(TextureDescriptor descriptor)
|
||||
{
|
||||
return !IsBuffer(descriptor);
|
||||
}
|
||||
|
||||
var uniformBufferBindings = info.CBuffers.Select(x => x.Binding).ToArray();
|
||||
var storageBufferBindings = info.SBuffers.Select(x => x.Binding).ToArray();
|
||||
var textureBindings = info.Textures.Where(IsNotBuffer).Select(x => x.Binding).ToArray();
|
||||
var imageBindings = info.Images.Where(IsNotBuffer).Select(x => x.Binding).ToArray();
|
||||
var bufferTextureBindings = info.Textures.Where(IsBuffer).Select(x => x.Binding).ToArray();
|
||||
var bufferImageBindings = info.Images.Where(IsBuffer).Select(x => x.Binding).ToArray();
|
||||
|
||||
return new ShaderBindings(
|
||||
uniformBufferBindings,
|
||||
storageBufferBindings,
|
||||
textureBindings,
|
||||
imageBindings,
|
||||
bufferTextureBindings,
|
||||
bufferImageBindings);
|
||||
}
|
||||
|
||||
private static TranslationOptions CreateTranslationOptions(TargetApi api, TranslationFlags flags)
|
||||
{
|
||||
TargetLanguage lang = GraphicsConfig.EnableSpirvCompilation ? TargetLanguage.Spirv : TargetLanguage.Glsl;
|
||||
|
||||
return new TranslationOptions(lang, api, flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the shader cache, deleting all the cached shaders.
|
||||
/// It's an error to use the shader cache after disposal.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
|
|
@ -88,16 +88,14 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
Add(Format.Bc3Srgb, new FormatInfo(4, false, false, All.CompressedSrgbAlphaS3tcDxt5Ext));
|
||||
Add(Format.Bc4Unorm, new FormatInfo(1, true, false, All.CompressedRedRgtc1));
|
||||
Add(Format.Bc4Snorm, new FormatInfo(1, true, false, All.CompressedSignedRedRgtc1));
|
||||
Add(Format.Bc5Unorm, new FormatInfo(2, true, false, All.CompressedRgRgtc2));
|
||||
Add(Format.Bc5Snorm, new FormatInfo(2, true, false, All.CompressedSignedRgRgtc2));
|
||||
Add(Format.Bc7Unorm, new FormatInfo(4, true, false, All.CompressedRgbaBptcUnorm));
|
||||
Add(Format.Bc7Srgb, new FormatInfo(4, false, false, All.CompressedSrgbAlphaBptcUnorm));
|
||||
Add(Format.Bc6HSfloat, new FormatInfo(4, false, false, All.CompressedRgbBptcSignedFloat));
|
||||
Add(Format.Bc6HUfloat, new FormatInfo(4, false, false, All.CompressedRgbBptcUnsignedFloat));
|
||||
Add(Format.Etc2RgbUnorm, new FormatInfo(3, false, false, All.CompressedRgb8Etc2));
|
||||
Add(Format.Etc2RgbaUnorm, new FormatInfo(4, false, false, All.CompressedRgba8Etc2Eac));
|
||||
Add(Format.Etc2RgbSrgb, new FormatInfo(3, false, false, All.CompressedSrgb8Etc2));
|
||||
Add(Format.Etc2RgbaSrgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Etc2Eac));
|
||||
Add(Format.Bc5Unorm, new FormatInfo(1, true, false, All.CompressedRgRgtc2));
|
||||
Add(Format.Bc5Snorm, new FormatInfo(1, true, false, All.CompressedSignedRgRgtc2));
|
||||
Add(Format.Bc7Unorm, new FormatInfo(1, true, false, All.CompressedRgbaBptcUnorm));
|
||||
Add(Format.Bc7Srgb, new FormatInfo(1, false, false, All.CompressedSrgbAlphaBptcUnorm));
|
||||
Add(Format.Bc6HSfloat, new FormatInfo(1, false, false, All.CompressedRgbBptcSignedFloat));
|
||||
Add(Format.Bc6HUfloat, new FormatInfo(1, false, false, All.CompressedRgbBptcUnsignedFloat));
|
||||
Add(Format.Etc2RgbaUnorm, new FormatInfo(1, false, false, All.CompressedRgba8Etc2Eac));
|
||||
Add(Format.Etc2RgbaSrgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Etc2Eac));
|
||||
Add(Format.R8Uscaled, new FormatInfo(1, false, true, All.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte));
|
||||
Add(Format.R8Sscaled, new FormatInfo(1, false, true, All.R8i, PixelFormat.RedInteger, PixelType.Byte));
|
||||
Add(Format.R16Uscaled, new FormatInfo(1, false, true, All.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort));
|
||||
|
@ -138,34 +136,34 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
Add(Format.R32G32B32X32Float, new FormatInfo(4, false, false, All.Rgb32f, PixelFormat.Rgba, PixelType.Float));
|
||||
Add(Format.R32G32B32X32Uint, new FormatInfo(4, false, false, All.Rgb32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt));
|
||||
Add(Format.R32G32B32X32Sint, new FormatInfo(4, false, false, All.Rgb32i, PixelFormat.RgbaInteger, PixelType.Int));
|
||||
Add(Format.Astc4x4Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc4X4Khr));
|
||||
Add(Format.Astc5x4Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc5X4Khr));
|
||||
Add(Format.Astc5x5Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc5X5Khr));
|
||||
Add(Format.Astc6x5Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc6X5Khr));
|
||||
Add(Format.Astc6x6Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc6X6Khr));
|
||||
Add(Format.Astc8x5Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc8X5Khr));
|
||||
Add(Format.Astc8x6Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc8X6Khr));
|
||||
Add(Format.Astc8x8Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc8X8Khr));
|
||||
Add(Format.Astc10x5Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc10X5Khr));
|
||||
Add(Format.Astc10x6Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc10X6Khr));
|
||||
Add(Format.Astc10x8Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc10X8Khr));
|
||||
Add(Format.Astc10x10Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc10X10Khr));
|
||||
Add(Format.Astc12x10Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc12X10Khr));
|
||||
Add(Format.Astc12x12Unorm, new FormatInfo(4, true, false, All.CompressedRgbaAstc12X12Khr));
|
||||
Add(Format.Astc4x4Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc4X4Khr));
|
||||
Add(Format.Astc5x4Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc5X4Khr));
|
||||
Add(Format.Astc5x5Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc5X5Khr));
|
||||
Add(Format.Astc6x5Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc6X5Khr));
|
||||
Add(Format.Astc6x6Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc6X6Khr));
|
||||
Add(Format.Astc8x5Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc8X5Khr));
|
||||
Add(Format.Astc8x6Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc8X6Khr));
|
||||
Add(Format.Astc8x8Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc8X8Khr));
|
||||
Add(Format.Astc10x5Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc10X5Khr));
|
||||
Add(Format.Astc10x6Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc10X6Khr));
|
||||
Add(Format.Astc10x8Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc10X8Khr));
|
||||
Add(Format.Astc10x10Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc10X10Khr));
|
||||
Add(Format.Astc12x10Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc12X10Khr));
|
||||
Add(Format.Astc12x12Srgb, new FormatInfo(4, false, false, All.CompressedSrgb8Alpha8Astc12X12Khr));
|
||||
Add(Format.Astc4x4Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc4X4Khr));
|
||||
Add(Format.Astc5x4Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc5X4Khr));
|
||||
Add(Format.Astc5x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc5X5Khr));
|
||||
Add(Format.Astc6x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc6X5Khr));
|
||||
Add(Format.Astc6x6Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc6X6Khr));
|
||||
Add(Format.Astc8x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc8X5Khr));
|
||||
Add(Format.Astc8x6Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc8X6Khr));
|
||||
Add(Format.Astc8x8Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc8X8Khr));
|
||||
Add(Format.Astc10x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X5Khr));
|
||||
Add(Format.Astc10x6Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X6Khr));
|
||||
Add(Format.Astc10x8Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X8Khr));
|
||||
Add(Format.Astc10x10Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X10Khr));
|
||||
Add(Format.Astc12x10Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc12X10Khr));
|
||||
Add(Format.Astc12x12Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc12X12Khr));
|
||||
Add(Format.Astc4x4Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc4X4Khr));
|
||||
Add(Format.Astc5x4Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc5X4Khr));
|
||||
Add(Format.Astc5x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc5X5Khr));
|
||||
Add(Format.Astc6x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc6X5Khr));
|
||||
Add(Format.Astc6x6Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc6X6Khr));
|
||||
Add(Format.Astc8x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc8X5Khr));
|
||||
Add(Format.Astc8x6Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc8X6Khr));
|
||||
Add(Format.Astc8x8Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc8X8Khr));
|
||||
Add(Format.Astc10x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X5Khr));
|
||||
Add(Format.Astc10x6Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X6Khr));
|
||||
Add(Format.Astc10x8Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X8Khr));
|
||||
Add(Format.Astc10x10Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X10Khr));
|
||||
Add(Format.Astc12x10Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc12X10Khr));
|
||||
Add(Format.Astc12x12Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc12X12Khr));
|
||||
Add(Format.B5G6R5Unorm, new FormatInfo(3, true, false, All.Rgb565, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed));
|
||||
Add(Format.B5G5R5X1Unorm, new FormatInfo(4, true, false, All.Rgb5, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed));
|
||||
Add(Format.B5G5R5A1Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed));
|
||||
|
|
|
@ -1106,45 +1106,25 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
_framebuffer.SetDrawBuffers(colors.Length);
|
||||
}
|
||||
|
||||
public void SetSampler(int binding, ISampler sampler)
|
||||
public unsafe void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
|
||||
{
|
||||
if (sampler == null)
|
||||
int count = Math.Min(regions.Length, Constants.MaxViewports);
|
||||
|
||||
int* v = stackalloc int[count * 4];
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int vIndex = index * 4;
|
||||
|
||||
Sampler samp = (Sampler)sampler;
|
||||
v[vIndex] = regions[index].X;
|
||||
v[vIndex + 1] = regions[index].Y;
|
||||
v[vIndex + 2] = regions[index].Width;
|
||||
v[vIndex + 3] = regions[index].Height;
|
||||
|
||||
if (binding == 0)
|
||||
{
|
||||
_unit0Sampler = samp;
|
||||
}
|
||||
|
||||
samp.Bind(binding);
|
||||
}
|
||||
|
||||
public void SetScissor(int index, bool enable, int x, int y, int width, int height)
|
||||
{
|
||||
uint mask = 1u << index;
|
||||
|
||||
if (!enable)
|
||||
{
|
||||
if ((_scissorEnables & mask) != 0)
|
||||
{
|
||||
_scissorEnables &= ~mask;
|
||||
GL.Disable(IndexedEnableCap.ScissorTest, index);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_scissorEnables & mask) == 0)
|
||||
{
|
||||
_scissorEnables |= mask;
|
||||
GL.Enable(IndexedEnableCap.ScissorTest, index);
|
||||
}
|
||||
|
||||
GL.ScissorIndexed(index, x, y, width, height);
|
||||
GL.ScissorArray(0, count, v);
|
||||
}
|
||||
|
||||
public void SetStencilTest(StencilTestDescriptor stencilTest)
|
||||
|
@ -1195,22 +1175,24 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
SetBuffers(first, buffers, isStorage: true);
|
||||
}
|
||||
|
||||
public void SetTexture(int binding, ITexture texture)
|
||||
public void SetTextureAndSampler(int binding, ITexture texture, ISampler sampler)
|
||||
{
|
||||
if (texture == null)
|
||||
if (texture != null && sampler != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (binding == 0)
|
||||
{
|
||||
_unit0Texture = (TextureBase)texture;
|
||||
_unit0Sampler = (Sampler)sampler;
|
||||
}
|
||||
else
|
||||
{
|
||||
((TextureBase)texture).Bind(binding);
|
||||
}
|
||||
|
||||
((Sampler)sampler).Bind(binding);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
|
|
@ -87,6 +87,11 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
Buffer.Delete(buffer);
|
||||
}
|
||||
|
||||
public HardwareInfo GetHardwareInfo()
|
||||
{
|
||||
return new HardwareInfo(GpuVendor, GpuRenderer);
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
||||
{
|
||||
return Buffer.GetData(this, buffer, offset, size);
|
||||
|
|
|
@ -106,8 +106,6 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
GL.Disable(EnableCap.RasterizerDiscard);
|
||||
GL.Disable(IndexedEnableCap.ScissorTest, 0);
|
||||
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
|
||||
int srcX0, srcX1, srcY0, srcY1;
|
||||
float scale = view.ScaleFactor;
|
||||
|
||||
|
|
36
Ryujinx.Graphics.Shader/AttributeType.cs
Normal file
36
Ryujinx.Graphics.Shader/AttributeType.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
public enum AttributeType
|
||||
{
|
||||
Float,
|
||||
Sint,
|
||||
Uint
|
||||
}
|
||||
|
||||
static class AttributeTypeExtensions
|
||||
{
|
||||
public static string GetScalarType(this AttributeType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
AttributeType.Float => "float",
|
||||
AttributeType.Sint => "int",
|
||||
AttributeType.Uint => "uint",
|
||||
_ => throw new ArgumentException($"Invalid attribute type \"{type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetVec4Type(this AttributeType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
AttributeType.Float => "vec4",
|
||||
AttributeType.Sint => "ivec4",
|
||||
AttributeType.Uint => "uvec4",
|
||||
_ => throw new ArgumentException($"Invalid attribute type \"{type}\".")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
public static void Declare(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
context.AppendLine("#version 450 core");
|
||||
context.AppendLine(context.Config.Options.TargetApi == TargetApi.Vulkan ? "#version 460 core" : "#version 450 core");
|
||||
context.AppendLine("#extension GL_ARB_gpu_shader_int64 : enable");
|
||||
|
||||
if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot())
|
||||
|
@ -530,16 +530,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
if (context.Config.TransformFeedbackEnabled && context.Config.Stage != ShaderStage.Vertex)
|
||||
{
|
||||
string type;
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
type = context.Config.GpuAccessor.QueryAttributeType(attr).GetScalarType();
|
||||
}
|
||||
else
|
||||
{
|
||||
type = AttributeType.Float.GetScalarType();
|
||||
}
|
||||
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
char swzMask = "xyzw"[c];
|
||||
|
||||
context.AppendLine($"layout ({pass}location = {attr}, component = {c}) {iq}in float {name}_{swzMask}{suffix};");
|
||||
context.AppendLine($"layout ({pass}location = {attr}, component = {c}) {iq}in {type} {name}_{swzMask}{suffix};");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.AppendLine($"layout ({pass}location = {attr}) {iq}in vec4 {name}{suffix};");
|
||||
string type;
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
type = context.Config.GpuAccessor.QueryAttributeType(attr).GetVec4Type();
|
||||
}
|
||||
else
|
||||
{
|
||||
type = AttributeType.Float.GetVec4Type();
|
||||
}
|
||||
|
||||
context.AppendLine($"layout ({pass}location = {attr}) {iq}in {type} {name}{suffix};");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
else if (node is AstAssignment assignment)
|
||||
{
|
||||
VariableType srcType = OperandManager.GetNodeDestType(context, assignment.Source);
|
||||
VariableType dstType = OperandManager.GetNodeDestType(context, assignment.Destination);
|
||||
VariableType dstType = OperandManager.GetNodeDestType(context, assignment.Destination, isAsgDest: true);
|
||||
|
||||
string dest;
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
{
|
||||
static class InstGenHelper
|
||||
{
|
||||
private static InstInfo[] _infoTbl;
|
||||
private static readonly InstInfo[] InfoTable;
|
||||
|
||||
static InstGenHelper()
|
||||
{
|
||||
_infoTbl = new InstInfo[(int)Instruction.Count];
|
||||
InfoTable = new InstInfo[(int)Instruction.Count];
|
||||
|
||||
Add(Instruction.AtomicAdd, InstType.AtomicBinary, "atomicAdd");
|
||||
Add(Instruction.AtomicAnd, InstType.AtomicBinary, "atomicAnd");
|
||||
|
@ -139,12 +139,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
|
||||
private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0)
|
||||
{
|
||||
_infoTbl[(int)inst] = new InstInfo(flags, opName, precedence);
|
||||
InfoTable[(int)inst] = new InstInfo(flags, opName, precedence);
|
||||
}
|
||||
|
||||
public static InstInfo GetInstructionInfo(Instruction inst)
|
||||
{
|
||||
return _infoTbl[(int)(inst & Instruction.Mask)];
|
||||
return InfoTable[(int)(inst & Instruction.Mask)];
|
||||
}
|
||||
|
||||
public static string GetSoureExpr(CodeGenContext context, IAstNode node, VariableType dstType)
|
||||
|
@ -191,7 +191,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
return false;
|
||||
}
|
||||
|
||||
InstInfo info = _infoTbl[(int)(operation.Inst & Instruction.Mask)];
|
||||
InstInfo info = InfoTable[(int)(operation.Inst & Instruction.Mask)];
|
||||
|
||||
if ((info.Type & (InstType.Call | InstType.Special)) != 0)
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
class OperandManager
|
||||
{
|
||||
private static string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
||||
private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
||||
|
||||
private struct BuiltInAttribute
|
||||
{
|
||||
|
@ -26,8 +26,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
}
|
||||
}
|
||||
|
||||
private static Dictionary<int, BuiltInAttribute> _builtInAttributes =
|
||||
new Dictionary<int, BuiltInAttribute>()
|
||||
private static Dictionary<int, BuiltInAttribute> _builtInAttributes = new Dictionary<int, BuiltInAttribute>()
|
||||
{
|
||||
{ AttributeConsts.TessLevelOuter0, new BuiltInAttribute("gl_TessLevelOuter[0]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelOuter1, new BuiltInAttribute("gl_TessLevelOuter[1]", VariableType.F32) },
|
||||
|
@ -391,12 +390,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
int index = (int)stage;
|
||||
|
||||
if ((uint)index >= _stagePrefixes.Length)
|
||||
if ((uint)index >= StagePrefixes.Length)
|
||||
{
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
return _stagePrefixes[index];
|
||||
return StagePrefixes[index];
|
||||
}
|
||||
|
||||
private static char GetSwizzleMask(int value)
|
||||
|
@ -409,7 +408,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
return $"{DefaultNames.ArgumentNamePrefix}{argIndex}";
|
||||
}
|
||||
|
||||
public static VariableType GetNodeDestType(CodeGenContext context, IAstNode node)
|
||||
public static VariableType GetNodeDestType(CodeGenContext context, IAstNode node, bool isAsgDest = false)
|
||||
{
|
||||
if (node is AstOperation operation)
|
||||
{
|
||||
|
@ -455,7 +454,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
return context.CurrentFunction.GetArgumentType(argIndex);
|
||||
}
|
||||
|
||||
return GetOperandVarType(operand);
|
||||
return GetOperandVarType(context, operand, isAsgDest);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -463,7 +462,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
}
|
||||
}
|
||||
|
||||
private static VariableType GetOperandVarType(AstOperand operand)
|
||||
private static VariableType GetOperandVarType(CodeGenContext context, AstOperand operand, bool isAsgDest = false)
|
||||
{
|
||||
if (operand.Type == OperandType.Attribute)
|
||||
{
|
||||
|
@ -471,6 +470,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
return builtInAttr.Type;
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.Vertex && !isAsgDest &&
|
||||
operand.Value >= AttributeConsts.UserAttributeBase &&
|
||||
operand.Value < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
int location = (operand.Value - AttributeConsts.UserAttributeBase) / 16;
|
||||
|
||||
AttributeType type = context.Config.GpuAccessor.QueryAttributeType(location);
|
||||
|
||||
return type switch
|
||||
{
|
||||
AttributeType.Sint => VariableType.S32,
|
||||
AttributeType.Uint => VariableType.U32,
|
||||
_ => VariableType.F32
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return OperandInfo.GetVarType(operand);
|
||||
|
|
351
Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
Normal file
351
Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
Normal file
|
@ -0,0 +1,351 @@
|
|||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using Spv.Generator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
using IrConsts = IntermediateRepresentation.IrConsts;
|
||||
using IrOperandType = IntermediateRepresentation.OperandType;
|
||||
|
||||
partial class CodeGenContext : Module
|
||||
{
|
||||
public ShaderConfig Config { get; }
|
||||
|
||||
public Instruction ExtSet { get; }
|
||||
|
||||
public Dictionary<int, Instruction> UniformBuffers { get; } = new Dictionary<int, Instruction>();
|
||||
public Instruction StorageBuffersArray { get; set; }
|
||||
public Instruction LocalMemory { get; set; }
|
||||
public Instruction SharedMemory { get; set; }
|
||||
public Dictionary<TextureMeta, (Instruction, Instruction, Instruction)> Samplers { get; } = new Dictionary<TextureMeta, (Instruction, Instruction, Instruction)>();
|
||||
public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>();
|
||||
public Dictionary<int, Instruction> Inputs { get; } = new Dictionary<int, Instruction>();
|
||||
public Dictionary<int, Instruction> Outputs { get; } = new Dictionary<int, Instruction>();
|
||||
|
||||
private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
|
||||
private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
|
||||
private readonly Dictionary<int, Instruction> _funcArgs = new Dictionary<int, Instruction>();
|
||||
private readonly Dictionary<int, (StructuredFunction, Instruction)> _functions = new Dictionary<int, (StructuredFunction, Instruction)>();
|
||||
|
||||
private class BlockState
|
||||
{
|
||||
private int _entryCount;
|
||||
private readonly List<Instruction> _labels = new List<Instruction>();
|
||||
|
||||
public Instruction GetNextLabel(CodeGenContext context)
|
||||
{
|
||||
return GetLabel(context, _entryCount);
|
||||
}
|
||||
|
||||
public Instruction GetNextLabelAutoIncrement(CodeGenContext context)
|
||||
{
|
||||
return GetLabel(context, _entryCount++);
|
||||
}
|
||||
|
||||
public Instruction GetLabel(CodeGenContext context, int index)
|
||||
{
|
||||
while (index >= _labels.Count)
|
||||
{
|
||||
_labels.Add(context.Label());
|
||||
}
|
||||
|
||||
return _labels[index];
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<AstBlock, BlockState> _labels = new Dictionary<AstBlock, BlockState>();
|
||||
|
||||
public AstBlock CurrentBlock { get; private set; }
|
||||
|
||||
public CodeGenContext(ShaderConfig config) : base(0x00010300)
|
||||
{
|
||||
Config = config;
|
||||
|
||||
AddCapability(Capability.Shader);
|
||||
AddCapability(Capability.Float64);
|
||||
|
||||
ExtSet = AddExtInstImport("GLSL.std.450");
|
||||
|
||||
SetMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450);
|
||||
}
|
||||
|
||||
public void StartFunction()
|
||||
{
|
||||
_locals.Clear();
|
||||
_localForArgs.Clear();
|
||||
_funcArgs.Clear();
|
||||
}
|
||||
|
||||
public void EnterBlock(AstBlock block)
|
||||
{
|
||||
CurrentBlock = block;
|
||||
AddLabel(GetBlockStateLazy(block).GetNextLabelAutoIncrement(this));
|
||||
}
|
||||
|
||||
public Instruction GetFirstLabel(AstBlock block)
|
||||
{
|
||||
return GetBlockStateLazy(block).GetLabel(this, 0);
|
||||
}
|
||||
|
||||
public Instruction GetNextLabel(AstBlock block)
|
||||
{
|
||||
return GetBlockStateLazy(block).GetNextLabel(this);
|
||||
}
|
||||
|
||||
private BlockState GetBlockStateLazy(AstBlock block)
|
||||
{
|
||||
if (!_labels.TryGetValue(block, out var blockState))
|
||||
{
|
||||
blockState = new BlockState();
|
||||
|
||||
_labels.Add(block, blockState);
|
||||
}
|
||||
|
||||
return blockState;
|
||||
}
|
||||
|
||||
public Instruction NewBlock()
|
||||
{
|
||||
var label = Label();
|
||||
Branch(label);
|
||||
AddLabel(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
public Instruction[] GetMainInterface()
|
||||
{
|
||||
return Inputs.Values.Concat(Outputs.Values).ToArray();
|
||||
}
|
||||
|
||||
public void DeclareLocal(AstOperand local, Instruction spvLocal)
|
||||
{
|
||||
_locals.Add(local, spvLocal);
|
||||
}
|
||||
|
||||
public void DeclareLocalForArgs(int funcIndex, Instruction[] spvLocals)
|
||||
{
|
||||
_localForArgs.Add(funcIndex, spvLocals);
|
||||
}
|
||||
|
||||
public void DeclareArgument(int argIndex, Instruction spvLocal)
|
||||
{
|
||||
_funcArgs.Add(argIndex, spvLocal);
|
||||
}
|
||||
|
||||
public void DeclareFunction(int funcIndex, StructuredFunction function, Instruction spvFunc)
|
||||
{
|
||||
_functions.Add(funcIndex, (function, spvFunc));
|
||||
}
|
||||
|
||||
public Instruction GetFP32(IAstNode node)
|
||||
{
|
||||
return Get(AggregateType.FP32, node);
|
||||
}
|
||||
|
||||
public Instruction GetFP64(IAstNode node)
|
||||
{
|
||||
return Get(AggregateType.FP64, node);
|
||||
}
|
||||
|
||||
public Instruction GetS32(IAstNode node)
|
||||
{
|
||||
return Get(AggregateType.S32, node);
|
||||
}
|
||||
|
||||
public Instruction GetU32(IAstNode node)
|
||||
{
|
||||
return Get(AggregateType.U32, node);
|
||||
}
|
||||
|
||||
public Instruction Get(AggregateType type, IAstNode node)
|
||||
{
|
||||
if (node is AstOperation operation)
|
||||
{
|
||||
var opResult = Instructions.Generate(this, operation);
|
||||
return BitcastIfNeeded(type, opResult.Type, opResult.Value);
|
||||
}
|
||||
else if (node is AstOperand operand)
|
||||
{
|
||||
return operand.Type switch
|
||||
{
|
||||
IrOperandType.Argument => GetArgument(type, operand),
|
||||
IrOperandType.Attribute => GetAttribute(type, operand, false),
|
||||
IrOperandType.Constant => GetConstant(type, operand),
|
||||
IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
|
||||
IrOperandType.LocalVariable => GetLocal(type, operand),
|
||||
IrOperandType.Undefined => Undef(GetType(type)),
|
||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
throw new NotImplementedException(node.GetType().Name);
|
||||
}
|
||||
|
||||
public Instruction GetAttributeVectorPointer(AstOperand operand, bool isOutAttr)
|
||||
{
|
||||
var attrInfo = AttributeInfo.From(Config, operand.Value);
|
||||
|
||||
return isOutAttr ? Outputs[attrInfo.BaseValue] : Inputs[attrInfo.BaseValue];
|
||||
}
|
||||
|
||||
public Instruction GetAttributeElemPointer(AstOperand operand, bool isOutAttr, out AggregateType elemType)
|
||||
{
|
||||
var attrInfo = AttributeInfo.From(Config, operand.Value);
|
||||
if (attrInfo.BaseValue == AttributeConsts.PositionX && Config.Stage != ShaderStage.Fragment)
|
||||
{
|
||||
isOutAttr = true;
|
||||
}
|
||||
|
||||
elemType = attrInfo.Type & AggregateType.ElementTypeMask;
|
||||
|
||||
var ioVariable = isOutAttr ? Outputs[attrInfo.BaseValue] : Inputs[attrInfo.BaseValue];
|
||||
|
||||
if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0)
|
||||
{
|
||||
return ioVariable;
|
||||
}
|
||||
|
||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
||||
|
||||
var elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
|
||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex);
|
||||
}
|
||||
|
||||
public Instruction GetAttribute(AggregateType type, AstOperand operand, bool isOutAttr)
|
||||
{
|
||||
var elemPointer = GetAttributeElemPointer(operand, isOutAttr, out var elemType);
|
||||
return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer));
|
||||
}
|
||||
|
||||
public Instruction GetConstant(AggregateType type, AstOperand operand)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
AggregateType.Bool => operand.Value != 0 ? ConstantTrue(TypeBool()) : ConstantFalse(TypeBool()),
|
||||
AggregateType.FP32 => Constant(TypeFP32(), BitConverter.Int32BitsToSingle(operand.Value)),
|
||||
AggregateType.FP64 => Constant(TypeFP64(), (double)BitConverter.Int32BitsToSingle(operand.Value)),
|
||||
AggregateType.S32 => Constant(TypeS32(), operand.Value),
|
||||
AggregateType.U32 => Constant(TypeU32(), (uint)operand.Value),
|
||||
_ => throw new ArgumentException($"Invalid type \"{type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
public Instruction GetConstantBuffer(AggregateType type, AstOperand operand)
|
||||
{
|
||||
var ubVariable = UniformBuffers[operand.CbufSlot];
|
||||
var i0 = Constant(TypeS32(), 0);
|
||||
var i1 = Constant(TypeS32(), operand.CbufOffset >> 2);
|
||||
var i2 = Constant(TypeU32(), operand.CbufOffset & 3);
|
||||
|
||||
var elemPointer = AccessChain(TypePointer(StorageClass.Uniform, TypeFP32()), ubVariable, i0, i1, i2);
|
||||
return BitcastIfNeeded(type, AggregateType.FP32, Load(TypeFP32(), elemPointer));
|
||||
}
|
||||
|
||||
public Instruction GetLocalPointer(AstOperand local)
|
||||
{
|
||||
return _locals[local];
|
||||
}
|
||||
|
||||
public Instruction[] GetLocalForArgsPointers(int funcIndex)
|
||||
{
|
||||
return _localForArgs[funcIndex];
|
||||
}
|
||||
|
||||
public Instruction GetArgumentPointer(AstOperand funcArg)
|
||||
{
|
||||
return _funcArgs[funcArg.Value];
|
||||
}
|
||||
|
||||
public Instruction GetLocal(AggregateType dstType, AstOperand local)
|
||||
{
|
||||
var srcType = local.VarType.Convert();
|
||||
return BitcastIfNeeded(dstType, srcType, Load(GetType(srcType), GetLocalPointer(local)));
|
||||
}
|
||||
|
||||
public Instruction GetArgument(AggregateType dstType, AstOperand funcArg)
|
||||
{
|
||||
var srcType = funcArg.VarType.Convert();
|
||||
return BitcastIfNeeded(dstType, srcType, Load(GetType(srcType), GetArgumentPointer(funcArg)));
|
||||
}
|
||||
|
||||
public (StructuredFunction, Instruction) GetFunction(int funcIndex)
|
||||
{
|
||||
return _functions[funcIndex];
|
||||
}
|
||||
|
||||
protected override void Construct()
|
||||
{
|
||||
}
|
||||
|
||||
public Instruction GetType(AggregateType type, int length = 1)
|
||||
{
|
||||
if (type.HasFlag(AggregateType.Array))
|
||||
{
|
||||
return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length));
|
||||
}
|
||||
else if (type.HasFlag(AggregateType.Vector))
|
||||
{
|
||||
return TypeVector(GetType(type & ~AggregateType.Vector), length);
|
||||
}
|
||||
|
||||
return type switch
|
||||
{
|
||||
AggregateType.Void => TypeVoid(),
|
||||
AggregateType.Bool => TypeBool(),
|
||||
AggregateType.FP32 => TypeFP32(),
|
||||
AggregateType.FP64 => TypeFP64(),
|
||||
AggregateType.S32 => TypeS32(),
|
||||
AggregateType.U32 => TypeU32(),
|
||||
_ => throw new ArgumentException($"Invalid attribute type \"{type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
public Instruction BitcastIfNeeded(AggregateType dstType, AggregateType srcType, Instruction value)
|
||||
{
|
||||
if (dstType == srcType)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
if (dstType == AggregateType.Bool)
|
||||
{
|
||||
return INotEqual(TypeBool(), BitcastIfNeeded(AggregateType.S32, srcType, value), Constant(TypeS32(), 0));
|
||||
}
|
||||
else if (srcType == AggregateType.Bool)
|
||||
{
|
||||
var intTrue = Constant(TypeS32(), IrConsts.True);
|
||||
var intFalse = Constant(TypeS32(), IrConsts.False);
|
||||
|
||||
return BitcastIfNeeded(dstType, AggregateType.S32, Select(TypeS32(), value, intTrue, intFalse));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Bitcast(GetType(dstType, 1), value);
|
||||
}
|
||||
}
|
||||
|
||||
public Instruction TypeS32()
|
||||
{
|
||||
return TypeInt(32, true);
|
||||
}
|
||||
|
||||
public Instruction TypeU32()
|
||||
{
|
||||
return TypeInt(32, false);
|
||||
}
|
||||
|
||||
public Instruction TypeFP32()
|
||||
{
|
||||
return TypeFloat(32);
|
||||
}
|
||||
|
||||
public Instruction TypeFP64()
|
||||
{
|
||||
return TypeFloat(64);
|
||||
}
|
||||
}
|
||||
}
|
474
Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
Normal file
474
Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
Normal file
|
@ -0,0 +1,474 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using Spv.Generator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
using SpvInstruction = Spv.Generator.Instruction;
|
||||
|
||||
static class Declarations
|
||||
{
|
||||
// At least 16 attributes are guaranteed by the spec.
|
||||
public const int MaxAttributes = 16;
|
||||
|
||||
private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
|
||||
|
||||
public static void DeclareParameters(CodeGenContext context, StructuredFunction function)
|
||||
{
|
||||
DeclareParameters(context, function.InArguments, 0);
|
||||
DeclareParameters(context, function.OutArguments, function.InArguments.Length);
|
||||
}
|
||||
|
||||
private static void DeclareParameters(CodeGenContext context, IEnumerable<VariableType> argTypes, int argIndex)
|
||||
{
|
||||
foreach (var argType in argTypes)
|
||||
{
|
||||
var argPointerType = context.TypePointer(StorageClass.Function, context.GetType(argType.Convert()));
|
||||
var spvArg = context.FunctionParameter(argPointerType);
|
||||
|
||||
context.DeclareArgument(argIndex++, spvArg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeclareLocals(CodeGenContext context, StructuredFunction function)
|
||||
{
|
||||
foreach (AstOperand local in function.Locals)
|
||||
{
|
||||
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(local.VarType.Convert()));
|
||||
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
|
||||
|
||||
context.AddLocalVariable(spvLocal);
|
||||
context.DeclareLocal(local, spvLocal);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions)
|
||||
{
|
||||
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
||||
{
|
||||
StructuredFunction function = functions[funcIndex];
|
||||
SpvInstruction[] locals = new SpvInstruction[function.InArguments.Length];
|
||||
|
||||
for (int i = 0; i < function.InArguments.Length; i++)
|
||||
{
|
||||
var type = function.GetArgumentType(i).Convert();
|
||||
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
|
||||
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
|
||||
|
||||
context.AddLocalVariable(spvLocal);
|
||||
|
||||
locals[i] = spvLocal;
|
||||
}
|
||||
|
||||
context.DeclareLocalForArgs(funcIndex, locals);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
if (context.Config.Stage == ShaderStage.Compute)
|
||||
{
|
||||
int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4);
|
||||
|
||||
if (localMemorySize != 0)
|
||||
{
|
||||
DeclareLocalMemory(context, localMemorySize);
|
||||
}
|
||||
|
||||
int sharedMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeSharedMemorySize(), 4);
|
||||
|
||||
if (sharedMemorySize != 0)
|
||||
{
|
||||
DeclareSharedMemory(context, sharedMemorySize);
|
||||
}
|
||||
}
|
||||
else if (context.Config.LocalMemorySize != 0)
|
||||
{
|
||||
int localMemorySize = BitUtils.DivRoundUp(context.Config.LocalMemorySize, 4);
|
||||
DeclareLocalMemory(context, localMemorySize);
|
||||
}
|
||||
|
||||
DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
|
||||
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
||||
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
||||
DeclareImages(context, context.Config.GetImageDescriptors());
|
||||
DeclareInputAttributes(context, info);
|
||||
DeclareOutputAttributes(context, info);
|
||||
}
|
||||
|
||||
private static void DeclareLocalMemory(CodeGenContext context, int size)
|
||||
{
|
||||
context.LocalMemory = DeclareMemory(context, StorageClass.Private, size);
|
||||
}
|
||||
|
||||
private static void DeclareSharedMemory(CodeGenContext context, int size)
|
||||
{
|
||||
context.SharedMemory = DeclareMemory(context, StorageClass.Workgroup, size);
|
||||
}
|
||||
|
||||
private static SpvInstruction DeclareMemory(CodeGenContext context, StorageClass storage, int size)
|
||||
{
|
||||
var arrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), size));
|
||||
var pointerType = context.TypePointer(storage, arrayType);
|
||||
var variable = context.Variable(pointerType, storage);
|
||||
|
||||
context.AddGlobalVariable(variable);
|
||||
|
||||
return variable;
|
||||
}
|
||||
|
||||
private static void DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
|
||||
{
|
||||
uint ubSize = Constants.ConstantBufferSize / 16;
|
||||
|
||||
var ubArrayType = context.TypeArray(context.TypeVector(context.TypeFP32(), 4), context.Constant(context.TypeU32(), ubSize), true);
|
||||
context.Decorate(ubArrayType, Decoration.ArrayStride, (LiteralInteger)16);
|
||||
var ubStructType = context.TypeStruct(true, ubArrayType);
|
||||
context.Decorate(ubStructType, Decoration.Block);
|
||||
context.MemberDecorate(ubStructType, 0, Decoration.Offset, (LiteralInteger)0);
|
||||
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
||||
|
||||
context.Name(ubVariable, $"{GetStagePrefix(context.Config.Stage)}_c{descriptor.Slot}");
|
||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)0);
|
||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
||||
context.AddGlobalVariable(ubVariable);
|
||||
context.UniformBuffers.Add(descriptor.Slot, ubVariable);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
|
||||
{
|
||||
if (descriptors.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 1 : 0;
|
||||
int count = descriptors.Max(x => x.Binding) + 1;
|
||||
|
||||
var sbArrayType = context.TypeRuntimeArray(context.TypeU32());
|
||||
context.Decorate(sbArrayType, Decoration.ArrayStride, (LiteralInteger)4);
|
||||
var sbStructType = context.TypeStruct(true, sbArrayType);
|
||||
context.Decorate(sbStructType, Decoration.BufferBlock);
|
||||
context.MemberDecorate(sbStructType, 0, Decoration.Offset, (LiteralInteger)0);
|
||||
var sbStructArrayType = context.TypeArray(sbStructType, context.Constant(context.TypeU32(), count));
|
||||
var sbPointerType = context.TypePointer(StorageClass.Uniform, sbStructArrayType);
|
||||
var sbVariable = context.Variable(sbPointerType, StorageClass.Uniform);
|
||||
|
||||
context.Name(sbVariable, $"{GetStagePrefix(context.Config.Stage)}_s");
|
||||
context.Decorate(sbVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
||||
context.Decorate(sbVariable, Decoration.Binding, (LiteralInteger)descriptors[0].Binding);
|
||||
context.AddGlobalVariable(sbVariable);
|
||||
|
||||
context.StorageBuffersArray = sbVariable;
|
||||
}
|
||||
|
||||
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||
{
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format, descriptor.Type);
|
||||
|
||||
if (context.Samplers.ContainsKey(meta))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isBuffer = (descriptor.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
|
||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? (isBuffer ? 4 : 2) : 0;
|
||||
|
||||
var dim = (meta.Type & SamplerType.Mask) switch
|
||||
{
|
||||
SamplerType.Texture1D => Dim.Dim1D,
|
||||
SamplerType.Texture2D => Dim.Dim2D,
|
||||
SamplerType.Texture3D => Dim.Dim3D,
|
||||
SamplerType.TextureCube => Dim.Cube,
|
||||
SamplerType.TextureBuffer => Dim.Buffer,
|
||||
_ => throw new InvalidOperationException($"Invalid sampler type \"{meta.Type & SamplerType.Mask}\".")
|
||||
};
|
||||
|
||||
var imageType = context.TypeImage(
|
||||
context.TypeFP32(),
|
||||
dim,
|
||||
meta.Type.HasFlag(SamplerType.Shadow),
|
||||
meta.Type.HasFlag(SamplerType.Array),
|
||||
meta.Type.HasFlag(SamplerType.Multisample),
|
||||
1,
|
||||
ImageFormat.Unknown);
|
||||
|
||||
var nameSuffix = meta.CbufSlot < 0 ? $"_tcb_{meta.Handle:X}" : $"_cb{meta.CbufSlot}_{meta.Handle:X}";
|
||||
|
||||
var sampledImageType = context.TypeSampledImage(imageType);
|
||||
var sampledImagePointerType = context.TypePointer(StorageClass.UniformConstant, sampledImageType);
|
||||
var sampledImageVariable = context.Variable(sampledImagePointerType, StorageClass.UniformConstant);
|
||||
|
||||
context.Samplers.Add(meta, (imageType, sampledImageType, sampledImageVariable));
|
||||
|
||||
context.Name(sampledImageVariable, $"{GetStagePrefix(context.Config.Stage)}_tex{nameSuffix}");
|
||||
context.Decorate(sampledImageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
||||
context.Decorate(sampledImageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
||||
context.AddGlobalVariable(sampledImageVariable);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareImages(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||
{
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
var meta = new TextureMeta(descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Format, descriptor.Type);
|
||||
|
||||
if (context.Images.ContainsKey(meta))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isBuffer = (descriptor.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
|
||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? (isBuffer ? 5 : 3) : 0;
|
||||
|
||||
var dim = GetDim(meta.Type);
|
||||
|
||||
var imageType = context.TypeImage(
|
||||
context.GetType(meta.Format.GetComponentType().Convert()),
|
||||
dim,
|
||||
meta.Type.HasFlag(SamplerType.Shadow),
|
||||
meta.Type.HasFlag(SamplerType.Array),
|
||||
meta.Type.HasFlag(SamplerType.Multisample),
|
||||
2,
|
||||
GetImageFormat(meta.Format));
|
||||
|
||||
var nameSuffix = meta.CbufSlot < 0 ?
|
||||
$"_tcb_{meta.Handle:X}_{meta.Format.ToGlslFormat()}" :
|
||||
$"_cb{meta.CbufSlot}_{meta.Handle:X}_{meta.Format.ToGlslFormat()}";
|
||||
|
||||
var imagePointerType = context.TypePointer(StorageClass.UniformConstant, imageType);
|
||||
var imageVariable = context.Variable(imagePointerType, StorageClass.UniformConstant);
|
||||
|
||||
context.Images.Add(meta, (imageType, imageVariable));
|
||||
|
||||
context.Name(imageVariable, $"{GetStagePrefix(context.Config.Stage)}_img{nameSuffix}");
|
||||
context.Decorate(imageVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
||||
context.Decorate(imageVariable, Decoration.Binding, (LiteralInteger)descriptor.Binding);
|
||||
context.AddGlobalVariable(imageVariable);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dim GetDim(SamplerType type)
|
||||
{
|
||||
return (type & SamplerType.Mask) switch
|
||||
{
|
||||
SamplerType.Texture1D => Dim.Dim1D,
|
||||
SamplerType.Texture2D => Dim.Dim2D,
|
||||
SamplerType.Texture3D => Dim.Dim3D,
|
||||
SamplerType.TextureCube => Dim.Cube,
|
||||
SamplerType.TextureBuffer => Dim.Buffer,
|
||||
_ => throw new ArgumentException($"Invalid sampler type \"{type & SamplerType.Mask}\".")
|
||||
};
|
||||
}
|
||||
|
||||
private static ImageFormat GetImageFormat(TextureFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
TextureFormat.Unknown => ImageFormat.Unknown,
|
||||
TextureFormat.R8Unorm => ImageFormat.R8,
|
||||
TextureFormat.R8Snorm => ImageFormat.R8Snorm,
|
||||
TextureFormat.R8Uint => ImageFormat.R8ui,
|
||||
TextureFormat.R8Sint => ImageFormat.R8i,
|
||||
TextureFormat.R16Float => ImageFormat.R16f,
|
||||
TextureFormat.R16Unorm => ImageFormat.R16,
|
||||
TextureFormat.R16Snorm => ImageFormat.R16Snorm,
|
||||
TextureFormat.R16Uint => ImageFormat.R16ui,
|
||||
TextureFormat.R16Sint => ImageFormat.R16i,
|
||||
TextureFormat.R32Float => ImageFormat.R32f,
|
||||
TextureFormat.R32Uint => ImageFormat.R32ui,
|
||||
TextureFormat.R32Sint => ImageFormat.R32i,
|
||||
TextureFormat.R8G8Unorm => ImageFormat.Rg8,
|
||||
TextureFormat.R8G8Snorm => ImageFormat.Rg8Snorm,
|
||||
TextureFormat.R8G8Uint => ImageFormat.Rg8ui,
|
||||
TextureFormat.R8G8Sint => ImageFormat.Rg8i,
|
||||
TextureFormat.R16G16Float => ImageFormat.Rg16f,
|
||||
TextureFormat.R16G16Unorm => ImageFormat.Rg16,
|
||||
TextureFormat.R16G16Snorm => ImageFormat.Rg16Snorm,
|
||||
TextureFormat.R16G16Uint => ImageFormat.Rg16ui,
|
||||
TextureFormat.R16G16Sint => ImageFormat.Rg16i,
|
||||
TextureFormat.R32G32Float => ImageFormat.Rg32f,
|
||||
TextureFormat.R32G32Uint => ImageFormat.Rg32ui,
|
||||
TextureFormat.R32G32Sint => ImageFormat.Rg32i,
|
||||
TextureFormat.R8G8B8A8Unorm => ImageFormat.Rgba8,
|
||||
TextureFormat.R8G8B8A8Snorm => ImageFormat.Rgba8Snorm,
|
||||
TextureFormat.R8G8B8A8Uint => ImageFormat.Rgba8ui,
|
||||
TextureFormat.R8G8B8A8Sint => ImageFormat.Rgba8i,
|
||||
TextureFormat.R16G16B16A16Float => ImageFormat.Rgba16f,
|
||||
TextureFormat.R16G16B16A16Unorm => ImageFormat.Rgba16,
|
||||
TextureFormat.R16G16B16A16Snorm => ImageFormat.Rgba16Snorm,
|
||||
TextureFormat.R16G16B16A16Uint => ImageFormat.Rgba16ui,
|
||||
TextureFormat.R16G16B16A16Sint => ImageFormat.Rgba16i,
|
||||
TextureFormat.R32G32B32A32Float => ImageFormat.Rgba32f,
|
||||
TextureFormat.R32G32B32A32Uint => ImageFormat.Rgba32ui,
|
||||
TextureFormat.R32G32B32A32Sint => ImageFormat.Rgba32i,
|
||||
TextureFormat.R10G10B10A2Unorm => ImageFormat.Rgb10A2,
|
||||
TextureFormat.R10G10B10A2Uint => ImageFormat.Rgb10a2ui,
|
||||
TextureFormat.R11G11B10Float => ImageFormat.R11fG11fB10f,
|
||||
_ => throw new ArgumentException($"Invalid texture format \"{format}\".")
|
||||
};
|
||||
}
|
||||
|
||||
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
foreach (int attr in info.Inputs)
|
||||
{
|
||||
PixelImap iq = PixelImap.Unused;
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Fragment &&
|
||||
attr >= AttributeConsts.UserAttributeBase &&
|
||||
attr < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
|
||||
}
|
||||
|
||||
if (context.Config.TransformFeedbackEnabled)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
DeclareInputOrOutput(context, attr, false, iq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
foreach (int attr in info.Outputs)
|
||||
{
|
||||
DeclareOutputAttribute(context, attr);
|
||||
}
|
||||
|
||||
if (context.Config.Stage != ShaderStage.Compute &&
|
||||
context.Config.Stage != ShaderStage.Fragment &&
|
||||
!context.Config.GpPassthrough)
|
||||
{
|
||||
for (int attr = 0; attr < MaxAttributes; attr++)
|
||||
{
|
||||
DeclareOutputAttribute(context, AttributeConsts.UserAttributeBase + attr * 16);
|
||||
}
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
DeclareOutputAttribute(context, AttributeConsts.PositionX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareOutputAttribute(CodeGenContext context, int attr)
|
||||
{
|
||||
if (context.Config.TransformFeedbackEnabled)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
DeclareInputOrOutput(context, attr, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeclareInvocationId(CodeGenContext context)
|
||||
{
|
||||
DeclareInputOrOutput(context, AttributeConsts.LaneId, false);
|
||||
}
|
||||
|
||||
private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
|
||||
{
|
||||
var dict = isOutAttr ? context.Outputs : context.Inputs;
|
||||
var attrInfo = AttributeInfo.From(context.Config, attr);
|
||||
|
||||
if (attrInfo.BaseValue == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment)
|
||||
{
|
||||
isOutAttr = true;
|
||||
dict = context.Outputs;
|
||||
}
|
||||
|
||||
if (dict.ContainsKey(attrInfo.BaseValue))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
||||
var spvType = context.TypePointer(storageClass, context.GetType(attrInfo.Type, attrInfo.Length));
|
||||
var spvVar = context.Variable(spvType, storageClass);
|
||||
|
||||
if (attrInfo.IsBuiltin)
|
||||
{
|
||||
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
|
||||
}
|
||||
else if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
int location = (attr - AttributeConsts.UserAttributeBase) / 16;
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
|
||||
if (!isOutAttr)
|
||||
{
|
||||
switch (iq)
|
||||
{
|
||||
case PixelImap.Constant:
|
||||
context.Decorate(spvVar, Decoration.Flat);
|
||||
break;
|
||||
case PixelImap.ScreenLinear:
|
||||
context.Decorate(spvVar, Decoration.NoPerspective);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
|
||||
{
|
||||
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
|
||||
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
|
||||
}
|
||||
|
||||
context.AddGlobalVariable(spvVar);
|
||||
dict.Add(attrInfo.BaseValue, spvVar);
|
||||
}
|
||||
|
||||
private static BuiltIn GetBuiltIn(CodeGenContext context, int attr)
|
||||
{
|
||||
return attr switch
|
||||
{
|
||||
AttributeConsts.Layer => BuiltIn.Layer,
|
||||
AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex,
|
||||
AttributeConsts.PointSize => BuiltIn.PointSize,
|
||||
AttributeConsts.PositionX => context.Config.Stage == ShaderStage.Fragment ? BuiltIn.FragCoord : BuiltIn.Position,
|
||||
AttributeConsts.ClipDistance0 => BuiltIn.ClipDistance,
|
||||
AttributeConsts.PointCoordX => BuiltIn.PointCoord,
|
||||
AttributeConsts.TessCoordX => BuiltIn.TessCoord,
|
||||
AttributeConsts.InstanceId => BuiltIn.InstanceId,
|
||||
AttributeConsts.VertexId => BuiltIn.VertexId,
|
||||
AttributeConsts.FrontFacing => BuiltIn.FrontFacing,
|
||||
AttributeConsts.FragmentOutputDepth => BuiltIn.FragDepth,
|
||||
AttributeConsts.ThreadKill => BuiltIn.HelperInvocation,
|
||||
AttributeConsts.ThreadIdX => BuiltIn.LocalInvocationId,
|
||||
AttributeConsts.CtaIdX => BuiltIn.WorkgroupId,
|
||||
AttributeConsts.LaneId => BuiltIn.SubgroupLocalInvocationId,
|
||||
AttributeConsts.EqMask => BuiltIn.SubgroupEqMask,
|
||||
AttributeConsts.GeMask => BuiltIn.SubgroupGeMask,
|
||||
AttributeConsts.GtMask => BuiltIn.SubgroupGtMask,
|
||||
AttributeConsts.LeMask => BuiltIn.SubgroupLeMask,
|
||||
AttributeConsts.LtMask => BuiltIn.SubgroupLtMask,
|
||||
_ => throw new ArgumentException($"Invalid attribute number 0x{attr:X}.")
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetStagePrefix(ShaderStage stage)
|
||||
{
|
||||
return StagePrefixes[(int)stage];
|
||||
}
|
||||
}
|
||||
}
|
38
Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs
Normal file
38
Ryujinx.Graphics.Shader/CodeGen/Spirv/EnumConversion.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
static class EnumConversion
|
||||
{
|
||||
public static AggregateType Convert(this VariableType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
VariableType.None => AggregateType.Void,
|
||||
VariableType.Bool => AggregateType.Bool,
|
||||
VariableType.F32 => AggregateType.FP32,
|
||||
VariableType.F64 => AggregateType.FP64,
|
||||
VariableType.S32 => AggregateType.S32,
|
||||
VariableType.U32 => AggregateType.U32,
|
||||
_ => throw new ArgumentException($"Invalid variable type \"{type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
public static ExecutionModel Convert(this ShaderStage stage)
|
||||
{
|
||||
return stage switch
|
||||
{
|
||||
ShaderStage.Compute => ExecutionModel.GLCompute,
|
||||
ShaderStage.Vertex => ExecutionModel.Vertex,
|
||||
ShaderStage.TessellationControl => ExecutionModel.TessellationControl,
|
||||
ShaderStage.TessellationEvaluation => ExecutionModel.TessellationEvaluation,
|
||||
ShaderStage.Geometry => ExecutionModel.Geometry,
|
||||
ShaderStage.Fragment => ExecutionModel.Fragment,
|
||||
_ => throw new ArgumentException($"Invalid shader stage \"{stage}\".")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
1867
Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
Normal file
1867
Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs
Normal file
File diff suppressed because it is too large
Load diff
19
Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs
Normal file
19
Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using Spv.Generator;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
struct OperationResult
|
||||
{
|
||||
public static OperationResult Invalid => new OperationResult(AggregateType.Invalid, null);
|
||||
|
||||
public AggregateType Type { get; }
|
||||
public Instruction Value { get; }
|
||||
|
||||
public OperationResult(AggregateType type, Instruction value)
|
||||
{
|
||||
Type = type;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
275
Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
Normal file
275
Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
Normal file
|
@ -0,0 +1,275 @@
|
|||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
using SpvInstruction = Spv.Generator.Instruction;
|
||||
using SpvLiteralInteger = Spv.Generator.LiteralInteger;
|
||||
|
||||
static class SpirvGenerator
|
||||
{
|
||||
private const HelperFunctionsMask NeedsInvocationIdMask =
|
||||
HelperFunctionsMask.Shuffle |
|
||||
HelperFunctionsMask.ShuffleDown |
|
||||
HelperFunctionsMask.ShuffleUp |
|
||||
HelperFunctionsMask.ShuffleXor |
|
||||
HelperFunctionsMask.SwizzleAdd;
|
||||
|
||||
public static byte[] Generate(StructuredProgramInfo info, ShaderConfig config)
|
||||
{
|
||||
CodeGenContext context = new CodeGenContext(config);
|
||||
|
||||
context.AddCapability(Capability.GroupNonUniformBallot);
|
||||
context.AddCapability(Capability.ImageBuffer);
|
||||
context.AddCapability(Capability.SampledBuffer);
|
||||
context.AddCapability(Capability.SubgroupBallotKHR);
|
||||
context.AddCapability(Capability.SubgroupVoteKHR);
|
||||
context.AddExtension("SPV_KHR_shader_ballot");
|
||||
context.AddExtension("SPV_KHR_subgroup_vote");
|
||||
|
||||
Declarations.DeclareAll(context, info);
|
||||
|
||||
if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0)
|
||||
{
|
||||
Declarations.DeclareInvocationId(context);
|
||||
}
|
||||
|
||||
for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++)
|
||||
{
|
||||
var function = info.Functions[funcIndex];
|
||||
var retType = context.GetType(function.ReturnType.Convert());
|
||||
|
||||
var funcArgs = new SpvInstruction[function.InArguments.Length + function.OutArguments.Length];
|
||||
|
||||
for (int argIndex = 0; argIndex < funcArgs.Length; argIndex++)
|
||||
{
|
||||
var argType = context.GetType(function.GetArgumentType(argIndex).Convert());
|
||||
var argPointerType = context.TypePointer(StorageClass.Function, argType);
|
||||
funcArgs[argIndex] = argPointerType;
|
||||
}
|
||||
|
||||
var funcType = context.TypeFunction(retType, false, funcArgs);
|
||||
var spvFunc = context.Function(retType, FunctionControlMask.MaskNone, funcType);
|
||||
|
||||
context.DeclareFunction(funcIndex, function, spvFunc);
|
||||
}
|
||||
|
||||
for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++)
|
||||
{
|
||||
Generate(context, info, funcIndex);
|
||||
}
|
||||
|
||||
return context.Generate();
|
||||
}
|
||||
|
||||
private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex)
|
||||
{
|
||||
var function = info.Functions[funcIndex];
|
||||
|
||||
(_, var spvFunc) = context.GetFunction(funcIndex);
|
||||
|
||||
context.AddFunction(spvFunc);
|
||||
context.StartFunction();
|
||||
|
||||
Declarations.DeclareParameters(context, function);
|
||||
|
||||
context.EnterBlock(function.MainBlock);
|
||||
|
||||
Declarations.DeclareLocals(context, function);
|
||||
Declarations.DeclareLocalForArgs(context, info.Functions);
|
||||
|
||||
if (funcIndex == 0)
|
||||
{
|
||||
var v4Type = context.TypeVector(context.TypeFP32(), 4);
|
||||
var zero = context.Constant(context.TypeFP32(), 0f);
|
||||
var one = context.Constant(context.TypeFP32(), 1f);
|
||||
|
||||
// Some games will leave some elements of gl_Position uninitialized,
|
||||
// in those cases, the elements will contain undefined values according
|
||||
// to the spec, but on NVIDIA they seems to be always initialized to (0, 0, 0, 1),
|
||||
// so we do explicit initialization to avoid UB on non-NVIDIA gpus.
|
||||
if (context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
var elemPointer = context.GetAttributeVectorPointer(new AstOperand(OperandType.Attribute, AttributeConsts.PositionX), true);
|
||||
context.Store(elemPointer, context.CompositeConstruct(v4Type, zero, zero, zero, one));
|
||||
}
|
||||
|
||||
// Ensure that unused attributes are set, otherwise the downstream
|
||||
// compiler may eliminate them.
|
||||
// (Not needed for fragment shader as it is the last stage).
|
||||
if (context.Config.Stage != ShaderStage.Compute &&
|
||||
context.Config.Stage != ShaderStage.Fragment &&
|
||||
!context.Config.GpPassthrough)
|
||||
{
|
||||
for (int attr = 0; attr < Declarations.MaxAttributes; attr++)
|
||||
{
|
||||
if (context.Config.TransformFeedbackEnabled)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
int currAttr = AttributeConsts.UserAttributeBase + attr * 16;
|
||||
var elemPointer = context.GetAttributeVectorPointer(new AstOperand(OperandType.Attribute, currAttr), true);
|
||||
context.Store(elemPointer, context.CompositeConstruct(v4Type, zero, zero, zero, one));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generate(context, function.MainBlock);
|
||||
|
||||
context.FunctionEnd();
|
||||
|
||||
if (funcIndex == 0)
|
||||
{
|
||||
context.AddEntryPoint(context.Config.Stage.Convert(), spvFunc, "main", context.GetMainInterface());
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Fragment)
|
||||
{
|
||||
context.AddExecutionMode(spvFunc, context.Config.Options.TargetApi == TargetApi.Vulkan
|
||||
? ExecutionMode.OriginUpperLeft
|
||||
: ExecutionMode.OriginLowerLeft);
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.Compute)
|
||||
{
|
||||
var localSizeX = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeX();
|
||||
var localSizeY = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeY();
|
||||
var localSizeZ = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeZ();
|
||||
|
||||
context.AddExecutionMode(
|
||||
spvFunc,
|
||||
ExecutionMode.LocalSize,
|
||||
localSizeX,
|
||||
localSizeY,
|
||||
localSizeZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Generate(CodeGenContext context, AstBlock block)
|
||||
{
|
||||
AstBlockVisitor visitor = new AstBlockVisitor(block);
|
||||
|
||||
var loopTargets = new Dictionary<AstBlock, (SpvInstruction, SpvInstruction)>();
|
||||
|
||||
visitor.BlockEntered += (sender, e) =>
|
||||
{
|
||||
AstBlock mergeBlock = e.Block.Parent;
|
||||
|
||||
if (e.Block.Type == AstBlockType.If)
|
||||
{
|
||||
AstBlock ifTrueBlock = e.Block;
|
||||
AstBlock ifFalseBlock;
|
||||
|
||||
if (AstHelper.Next(e.Block) is AstBlock nextBlock && nextBlock.Type == AstBlockType.Else)
|
||||
{
|
||||
ifFalseBlock = nextBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
ifFalseBlock = mergeBlock;
|
||||
}
|
||||
|
||||
var condition = context.Get(AggregateType.Bool, e.Block.Condition);
|
||||
|
||||
context.SelectionMerge(context.GetNextLabel(mergeBlock), SelectionControlMask.MaskNone);
|
||||
context.BranchConditional(condition, context.GetNextLabel(ifTrueBlock), context.GetNextLabel(ifFalseBlock));
|
||||
}
|
||||
else if (e.Block.Type == AstBlockType.DoWhile)
|
||||
{
|
||||
var continueTarget = context.Label();
|
||||
|
||||
loopTargets.Add(e.Block, (context.NewBlock(), continueTarget));
|
||||
|
||||
context.LoopMerge(context.GetNextLabel(mergeBlock), continueTarget, LoopControlMask.MaskNone);
|
||||
context.Branch(context.GetFirstLabel(e.Block));
|
||||
}
|
||||
|
||||
context.EnterBlock(e.Block);
|
||||
};
|
||||
|
||||
visitor.BlockLeft += (sender, e) =>
|
||||
{
|
||||
if (e.Block.Parent != null)
|
||||
{
|
||||
if (e.Block.Type == AstBlockType.DoWhile)
|
||||
{
|
||||
// This is a loop, we need to jump back to the loop header
|
||||
// if the condition is true.
|
||||
AstBlock mergeBlock = e.Block.Parent;
|
||||
|
||||
(var loopTarget, var continueTarget) = loopTargets[e.Block];
|
||||
|
||||
context.Branch(continueTarget);
|
||||
context.AddLabel(continueTarget);
|
||||
|
||||
var condition = context.Get(AggregateType.Bool, e.Block.Condition);
|
||||
|
||||
context.BranchConditional(condition, loopTarget, context.GetNextLabel(mergeBlock));
|
||||
}
|
||||
else
|
||||
{
|
||||
// We only need a branch if the last instruction didn't
|
||||
// already cause the program to exit or jump elsewhere.
|
||||
bool lastIsCf = e.Block.Last is AstOperation lastOp &&
|
||||
(lastOp.Inst == Instruction.Discard ||
|
||||
lastOp.Inst == Instruction.LoopBreak ||
|
||||
lastOp.Inst == Instruction.Return);
|
||||
|
||||
if (!lastIsCf)
|
||||
{
|
||||
context.Branch(context.GetNextLabel(e.Block.Parent));
|
||||
}
|
||||
}
|
||||
|
||||
bool hasElse = AstHelper.Next(e.Block) is AstBlock nextBlock &&
|
||||
(nextBlock.Type == AstBlockType.Else ||
|
||||
nextBlock.Type == AstBlockType.ElseIf);
|
||||
|
||||
// Re-enter the parent block.
|
||||
if (e.Block.Parent != null && !hasElse)
|
||||
{
|
||||
context.EnterBlock(e.Block.Parent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
foreach (IAstNode node in visitor.Visit())
|
||||
{
|
||||
if (node is AstAssignment assignment)
|
||||
{
|
||||
var dest = (AstOperand)assignment.Destination;
|
||||
|
||||
if (dest.Type == OperandType.LocalVariable)
|
||||
{
|
||||
var source = context.Get(dest.VarType.Convert(), assignment.Source);
|
||||
context.Store(context.GetLocalPointer(dest), source);
|
||||
}
|
||||
else if (dest.Type == OperandType.Attribute)
|
||||
{
|
||||
var elemPointer = context.GetAttributeElemPointer(dest, true, out var elemType);
|
||||
context.Store(elemPointer, context.Get(elemType, assignment.Source));
|
||||
}
|
||||
else if (dest.Type == OperandType.Argument)
|
||||
{
|
||||
var source = context.Get(dest.VarType.Convert(), assignment.Source);
|
||||
context.Store(context.GetArgumentPointer(dest), source);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException(dest.Type.ToString());
|
||||
}
|
||||
}
|
||||
else if (node is AstOperation operation)
|
||||
{
|
||||
Instructions.Generate(context, operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs
Normal file
35
Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
struct TextureMeta : IEquatable<TextureMeta>
|
||||
{
|
||||
public int CbufSlot { get; }
|
||||
public int Handle { get; }
|
||||
public TextureFormat Format { get; }
|
||||
public SamplerType Type { get; }
|
||||
|
||||
public TextureMeta(int cbufSlot, int handle, TextureFormat format, SamplerType type)
|
||||
{
|
||||
CbufSlot = cbufSlot;
|
||||
Handle = handle;
|
||||
Format = format;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is TextureMeta other && Equals(other);
|
||||
}
|
||||
|
||||
public bool Equals(TextureMeta other)
|
||||
{
|
||||
return Handle == other.Handle && Type == other.Type;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Handle, Type);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,6 +34,11 @@ namespace Ryujinx.Graphics.Shader
|
|||
/// <returns>Span of the memory location</returns>
|
||||
ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize);
|
||||
|
||||
AttributeType QueryAttributeType(int location)
|
||||
{
|
||||
return AttributeType.Float;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the binding number of a constant buffer.
|
||||
/// </summary>
|
||||
|
@ -218,6 +223,16 @@ namespace Ryujinx.Graphics.Shader
|
|||
return true;
|
||||
}
|
||||
|
||||
float QueryPointSize()
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
|
||||
bool QueryProgramPointSize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries sampler type information.
|
||||
/// </summary>
|
||||
|
@ -291,6 +306,11 @@ namespace Ryujinx.Graphics.Shader
|
|||
return TextureFormat.R8G8B8A8Unorm;
|
||||
}
|
||||
|
||||
bool QueryTransformDepthMinusOneToOne()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries transform feedback enable state.
|
||||
/// </summary>
|
||||
|
|
|
@ -58,5 +58,34 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
{
|
||||
return Operations.Last?.Value;
|
||||
}
|
||||
|
||||
public void Append(INode node)
|
||||
{
|
||||
INode lastOp = GetLastOp();
|
||||
|
||||
if (lastOp is Operation operation && IsControlFlowInst(operation.Inst))
|
||||
{
|
||||
Operations.AddBefore(Operations.Last, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operations.AddLast(node);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsControlFlowInst(Instruction inst)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.Branch:
|
||||
case Instruction.BranchIfFalse:
|
||||
case Instruction.BranchIfTrue:
|
||||
case Instruction.Discard:
|
||||
case Instruction.Return:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\SpvGen\Spv.Generator.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -25,32 +25,28 @@ namespace Ryujinx.Graphics.Shader
|
|||
{
|
||||
public static int GetDimensions(this SamplerType type)
|
||||
{
|
||||
switch (type & SamplerType.Mask)
|
||||
return (type & SamplerType.Mask) switch
|
||||
{
|
||||
case SamplerType.Texture1D: return 1;
|
||||
case SamplerType.TextureBuffer: return 1;
|
||||
case SamplerType.Texture2D: return 2;
|
||||
case SamplerType.Texture3D: return 3;
|
||||
case SamplerType.TextureCube: return 3;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid sampler type \"{type}\".");
|
||||
SamplerType.Texture1D => 1,
|
||||
SamplerType.TextureBuffer => 1,
|
||||
SamplerType.Texture2D => 2,
|
||||
SamplerType.Texture3D => 3,
|
||||
SamplerType.TextureCube => 3,
|
||||
_ => throw new ArgumentException($"Invalid sampler type \"{type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
public static string ToGlslSamplerType(this SamplerType type)
|
||||
{
|
||||
string typeName;
|
||||
|
||||
switch (type & SamplerType.Mask)
|
||||
string typeName = (type & SamplerType.Mask) switch
|
||||
{
|
||||
case SamplerType.Texture1D: typeName = "sampler1D"; break;
|
||||
case SamplerType.TextureBuffer: typeName = "samplerBuffer"; break;
|
||||
case SamplerType.Texture2D: typeName = "sampler2D"; break;
|
||||
case SamplerType.Texture3D: typeName = "sampler3D"; break;
|
||||
case SamplerType.TextureCube: typeName = "samplerCube"; break;
|
||||
|
||||
default: throw new ArgumentException($"Invalid sampler type \"{type}\".");
|
||||
}
|
||||
SamplerType.Texture1D => "sampler1D",
|
||||
SamplerType.TextureBuffer => "samplerBuffer",
|
||||
SamplerType.Texture2D => "sampler2D",
|
||||
SamplerType.Texture3D => "sampler3D",
|
||||
SamplerType.TextureCube => "samplerCube",
|
||||
_ => throw new ArgumentException($"Invalid sampler type \"{type}\".")
|
||||
};
|
||||
|
||||
if ((type & SamplerType.Multisample) != 0)
|
||||
{
|
||||
|
@ -72,18 +68,15 @@ namespace Ryujinx.Graphics.Shader
|
|||
|
||||
public static string ToGlslImageType(this SamplerType type, VariableType componentType)
|
||||
{
|
||||
string typeName;
|
||||
|
||||
switch (type & SamplerType.Mask)
|
||||
string typeName = (type & SamplerType.Mask) switch
|
||||
{
|
||||
case SamplerType.Texture1D: typeName = "image1D"; break;
|
||||
case SamplerType.TextureBuffer: typeName = "imageBuffer"; break;
|
||||
case SamplerType.Texture2D: typeName = "image2D"; break;
|
||||
case SamplerType.Texture3D: typeName = "image3D"; break;
|
||||
case SamplerType.TextureCube: typeName = "imageCube"; break;
|
||||
|
||||
default: throw new ArgumentException($"Invalid sampler type \"{type}\".");
|
||||
}
|
||||
SamplerType.Texture1D => "image1D",
|
||||
SamplerType.TextureBuffer => "imageBuffer",
|
||||
SamplerType.Texture2D => "image2D",
|
||||
SamplerType.Texture3D => "image3D",
|
||||
SamplerType.TextureCube => "imageCube",
|
||||
_ => throw new ArgumentException($"Invalid sampler type \"{type}\".")
|
||||
};
|
||||
|
||||
if ((type & SamplerType.Multisample) != 0)
|
||||
{
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
|
||||
public void Prepend(string line)
|
||||
{
|
||||
System.Console.WriteLine("prepend " + line);
|
||||
Code = line + Environment.NewLine + Code;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
private LinkedList<IAstNode> _nodes;
|
||||
|
||||
public IAstNode First => _nodes.First?.Value;
|
||||
public IAstNode Last => _nodes.Last?.Value;
|
||||
|
||||
public int Count => _nodes.Count;
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
// When debug mode is enabled, we disable expression propagation
|
||||
// (this makes comparison with the disassembly easier).
|
||||
if ((context.Config.Options.Flags & TranslationFlags.DebugMode) == 0)
|
||||
if (!context.Config.Options.Flags.HasFlag(TranslationFlags.DebugMode))
|
||||
{
|
||||
AstBlockVisitor visitor = new AstBlockVisitor(mainBlock);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
{
|
||||
LinkedListNode<INode> nextNode = node.Next;
|
||||
|
||||
if (!(node.Value is PhiNode phi))
|
||||
if (node.Value is not PhiNode phi)
|
||||
{
|
||||
node = nextNode;
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
Operation copyOp = new Operation(Instruction.Copy, phi.Dest, src);
|
||||
|
||||
AddBeforeBranch(srcBlock, copyOp);
|
||||
srcBlock.Append(copyOp);
|
||||
}
|
||||
|
||||
block.Operations.Remove(node);
|
||||
|
@ -41,34 +41,5 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddBeforeBranch(BasicBlock block, INode node)
|
||||
{
|
||||
INode lastOp = block.GetLastOp();
|
||||
|
||||
if (lastOp is Operation operation && IsControlFlowInst(operation.Inst))
|
||||
{
|
||||
block.Operations.AddBefore(block.Operations.Last, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
block.Operations.AddLast(node);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsControlFlowInst(Instruction inst)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.Branch:
|
||||
case Instruction.BranchIfFalse:
|
||||
case Instruction.BranchIfTrue:
|
||||
case Instruction.Discard:
|
||||
case Instruction.Return:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -277,6 +277,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
public AstOperand GetOperandDef(Operand operand)
|
||||
{
|
||||
if (operand.Type == OperandType.Attribute)
|
||||
{
|
||||
Info.Outputs.Add(operand.Value);
|
||||
}
|
||||
|
||||
return GetOperand(operand);
|
||||
}
|
||||
|
||||
|
@ -288,6 +293,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
return GetOperandDef(operand);
|
||||
}
|
||||
|
||||
if (operand.Type == OperandType.Attribute)
|
||||
{
|
||||
Info.Inputs.Add(operand.Value);
|
||||
}
|
||||
|
||||
return GetOperand(operand);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
{
|
||||
public List<StructuredFunction> Functions { get; }
|
||||
|
||||
public HashSet<int> Inputs { get; }
|
||||
public HashSet<int> Outputs { get; }
|
||||
|
||||
public HelperFunctionsMask HelperFunctionsMask { get; set; }
|
||||
|
||||
public TransformFeedbackOutput[] TransformFeedbackOutputs { get; }
|
||||
|
@ -30,6 +33,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
{
|
||||
Functions = new List<StructuredFunction>();
|
||||
|
||||
Inputs = new HashSet<int>();
|
||||
Outputs = new HashSet<int>();
|
||||
|
||||
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
|
||||
}
|
||||
}
|
||||
|
|
18
Ryujinx.Graphics.Shader/Translation/AggregateType.cs
Normal file
18
Ryujinx.Graphics.Shader/Translation/AggregateType.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
enum AggregateType
|
||||
{
|
||||
Invalid,
|
||||
Void,
|
||||
Bool,
|
||||
FP32,
|
||||
FP64,
|
||||
S32,
|
||||
U32,
|
||||
|
||||
ElementTypeMask = 0xff,
|
||||
|
||||
Vector = 1 << 8,
|
||||
Array = 1 << 9
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
public const int TessLevelInner0 = 0x010;
|
||||
public const int TessLevelInner1 = 0x014;
|
||||
public const int Layer = 0x064;
|
||||
public const int ViewportIndex = 0x068;
|
||||
public const int PointSize = 0x06c;
|
||||
public const int PositionX = 0x070;
|
||||
public const int PositionY = 0x074;
|
||||
|
|
99
Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
Normal file
99
Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using Ryujinx.Common;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
struct AttributeInfo
|
||||
{
|
||||
private static readonly Dictionary<int, AttributeInfo> BuiltInAttributes = new Dictionary<int, AttributeInfo>()
|
||||
{
|
||||
{ AttributeConsts.Layer, new AttributeInfo(AttributeConsts.Layer, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.PointSize, new AttributeInfo(AttributeConsts.PointSize, 1, AggregateType.FP32) },
|
||||
{ AttributeConsts.PositionX, new AttributeInfo(AttributeConsts.PositionX, 4, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.PositionY, new AttributeInfo(AttributeConsts.PositionY, 4, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.PositionZ, new AttributeInfo(AttributeConsts.PositionZ, 4, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.PositionW, new AttributeInfo(AttributeConsts.PositionW, 4, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance1, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance2, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance3, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance4, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance5, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance6, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance7, 8, AggregateType.Array | AggregateType.FP32) },
|
||||
{ AttributeConsts.PointCoordX, new AttributeInfo(AttributeConsts.PointCoordX, 2, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.PointCoordY, new AttributeInfo(AttributeConsts.PointCoordY, 2, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessCoordX, new AttributeInfo(AttributeConsts.TessCoordX, 2, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.TessCoordY, new AttributeInfo(AttributeConsts.TessCoordY, 2, AggregateType.Vector | AggregateType.FP32) },
|
||||
{ AttributeConsts.InstanceId, new AttributeInfo(AttributeConsts.InstanceId, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.VertexId, new AttributeInfo(AttributeConsts.VertexId, 1, AggregateType.S32) },
|
||||
{ AttributeConsts.FrontFacing, new AttributeInfo(AttributeConsts.FrontFacing, 1, AggregateType.Bool) },
|
||||
|
||||
// Special.
|
||||
{ AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 1, AggregateType.FP32) },
|
||||
{ AttributeConsts.ThreadKill, new AttributeInfo(AttributeConsts.ThreadKill, 1, AggregateType.Bool) },
|
||||
{ AttributeConsts.ThreadIdX, new AttributeInfo(AttributeConsts.ThreadIdX, 3, AggregateType.Vector | AggregateType.U32) },
|
||||
{ AttributeConsts.ThreadIdY, new AttributeInfo(AttributeConsts.ThreadIdY, 3, AggregateType.Vector | AggregateType.U32) },
|
||||
{ AttributeConsts.ThreadIdZ, new AttributeInfo(AttributeConsts.ThreadIdZ, 3, AggregateType.Vector | AggregateType.U32) },
|
||||
{ AttributeConsts.CtaIdX, new AttributeInfo(AttributeConsts.CtaIdX, 3, AggregateType.Vector | AggregateType.U32) },
|
||||
{ AttributeConsts.CtaIdY, new AttributeInfo(AttributeConsts.CtaIdY, 3, AggregateType.Vector | AggregateType.U32) },
|
||||
{ AttributeConsts.CtaIdZ, new AttributeInfo(AttributeConsts.CtaIdZ, 3, AggregateType.Vector | AggregateType.U32) },
|
||||
{ AttributeConsts.LaneId, new AttributeInfo(AttributeConsts.LaneId, 1, AggregateType.U32) },
|
||||
{ AttributeConsts.EqMask, new AttributeInfo(AttributeConsts.EqMask, 1, AggregateType.U32) },
|
||||
{ AttributeConsts.GeMask, new AttributeInfo(AttributeConsts.GeMask, 1, AggregateType.U32) },
|
||||
{ AttributeConsts.GtMask, new AttributeInfo(AttributeConsts.GtMask, 1, AggregateType.U32) },
|
||||
{ AttributeConsts.LeMask, new AttributeInfo(AttributeConsts.LeMask, 1, AggregateType.U32) },
|
||||
{ AttributeConsts.LtMask, new AttributeInfo(AttributeConsts.LtMask, 1, AggregateType.U32) },
|
||||
};
|
||||
|
||||
public int BaseValue { get; }
|
||||
public int Value { get; }
|
||||
public int Length { get; }
|
||||
public AggregateType Type { get; }
|
||||
public bool IsBuiltin { get; }
|
||||
public bool IsValid => Type != AggregateType.Invalid;
|
||||
|
||||
public AttributeInfo(int value, int length, AggregateType type, bool isBuiltin = true)
|
||||
{
|
||||
BaseValue = value & ~(BitUtils.Pow2RoundUp(length) * 4 - 1);
|
||||
Value = value;
|
||||
Length = length;
|
||||
Type = type;
|
||||
IsBuiltin = isBuiltin;
|
||||
}
|
||||
|
||||
public int GetInnermostIndex()
|
||||
{
|
||||
return (Value - BaseValue) / 4;
|
||||
}
|
||||
|
||||
public static AttributeInfo From(ShaderConfig config, int value)
|
||||
{
|
||||
value &= ~3;
|
||||
|
||||
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
||||
{
|
||||
int location = (value - AttributeConsts.UserAttributeBase) / 16;
|
||||
var elemType = config.GpuAccessor.QueryAttributeType(location) switch
|
||||
{
|
||||
AttributeType.Sint => AggregateType.S32,
|
||||
AttributeType.Uint => AggregateType.U32,
|
||||
_ => AggregateType.FP32
|
||||
};
|
||||
|
||||
return new AttributeInfo(value, 4, AggregateType.Vector | elemType, false);
|
||||
}
|
||||
else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
|
||||
{
|
||||
return new AttributeInfo(value, 4, AggregateType.Vector | AggregateType.FP32, false);
|
||||
}
|
||||
else if (BuiltInAttributes.TryGetValue(value, out AttributeInfo info))
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
return new AttributeInfo(value, 0, AggregateType.Invalid);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,6 +30,19 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
IsNonMain = isNonMain;
|
||||
_operations = new List<Operation>();
|
||||
_labels = new Dictionary<ulong, Operand>();
|
||||
|
||||
EmitStart();
|
||||
}
|
||||
|
||||
private void EmitStart()
|
||||
{
|
||||
if (Config.Stage == ShaderStage.Vertex &&
|
||||
Config.Options.TargetApi == TargetApi.Vulkan &&
|
||||
(Config.Options.Flags & TranslationFlags.VertexA) == 0)
|
||||
{
|
||||
// Vulkan requires the point size to be always written on the shader if the primitive topology is points.
|
||||
this.Copy(Attribute(AttributeConsts.PointSize), ConstF(Config.GpuAccessor.QueryPointSize()));
|
||||
}
|
||||
}
|
||||
|
||||
public T GetOp<T>() where T : unmanaged
|
||||
|
@ -43,7 +56,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
Operation operation = new Operation(inst, dest, sources);
|
||||
|
||||
Add(operation);
|
||||
_operations.Add(operation);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
@ -167,6 +180,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
this.Copy(Attribute(AttributeConsts.PositionX), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
|
||||
this.Copy(Attribute(AttributeConsts.PositionY), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
|
||||
}
|
||||
|
||||
if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
|
||||
{
|
||||
Operand z = Attribute(AttributeConsts.PositionZ);
|
||||
Operand w = Attribute(AttributeConsts.PositionW);
|
||||
Operand halfW = this.FPMultiply(w, ConstF(0.5f));
|
||||
|
||||
this.Copy(Attribute(AttributeConsts.PositionZ), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
|
||||
}
|
||||
}
|
||||
|
||||
public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal)
|
||||
|
@ -184,8 +206,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
oldYLocal = null;
|
||||
}
|
||||
|
||||
// Will be used by Vulkan backend for depth mode emulation.
|
||||
if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
|
||||
{
|
||||
oldZLocal = Local();
|
||||
this.Copy(oldYLocal, Attribute(AttributeConsts.PositionZ | AttributeConsts.LoadOutputMask));
|
||||
}
|
||||
else
|
||||
{
|
||||
oldZLocal = null;
|
||||
}
|
||||
|
||||
PrepareForVertexReturn();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Ryujinx.Graphics.Shader.CodeGen.Glsl;
|
||||
using Ryujinx.Graphics.Shader.CodeGen.Spirv;
|
||||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
|
@ -72,16 +73,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
Ssa.Rename(cfg.Blocks);
|
||||
|
||||
Optimizer.RunPass(cfg.Blocks, config);
|
||||
|
||||
Rewriter.RunPass(cfg.Blocks, config);
|
||||
}
|
||||
|
||||
funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount);
|
||||
}
|
||||
|
||||
StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(funcs, config);
|
||||
var sInfo = StructuredProgram.MakeStructuredProgram(funcs, config);
|
||||
|
||||
ShaderProgramInfo info = new ShaderProgramInfo(
|
||||
var info = new ShaderProgramInfo(
|
||||
config.GetConstantBufferDescriptors(),
|
||||
config.GetStorageBufferDescriptors(),
|
||||
config.GetTextureDescriptors(),
|
||||
|
@ -95,6 +95,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return config.Options.TargetLanguage switch
|
||||
{
|
||||
TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)),
|
||||
TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, config)),
|
||||
_ => throw new NotImplementedException(config.Options.TargetLanguage.ToString())
|
||||
};
|
||||
}
|
||||
|
@ -105,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
DecodedProgram program;
|
||||
ulong maxEndAddress = 0;
|
||||
|
||||
if ((options.Flags & TranslationFlags.Compute) != 0)
|
||||
if (options.Flags.HasFlag(TranslationFlags.Compute))
|
||||
{
|
||||
config = new ShaderConfig(gpuAccessor, options);
|
||||
|
||||
|
|
141
Ryujinx.Graphics.Vulkan/Auto.cs
Normal file
141
Ryujinx.Graphics.Vulkan/Auto.cs
Normal file
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
interface IAuto
|
||||
{
|
||||
void IncrementReferenceCount();
|
||||
void DecrementReferenceCount(int cbIndex);
|
||||
void DecrementReferenceCount();
|
||||
}
|
||||
|
||||
interface IAutoPrivate : IAuto
|
||||
{
|
||||
void AddCommandBufferDependencies(CommandBufferScoped cbs);
|
||||
}
|
||||
|
||||
class Auto<T> : IAutoPrivate, IDisposable where T : IDisposable
|
||||
{
|
||||
private int _referenceCount;
|
||||
private T _value;
|
||||
|
||||
private readonly BitMap _cbOwnership;
|
||||
private readonly MultiFenceHolder _waitable;
|
||||
private readonly IAutoPrivate[] _referencedObjs;
|
||||
|
||||
private bool _disposed;
|
||||
private bool _destroyed;
|
||||
|
||||
public Auto(T value)
|
||||
{
|
||||
_referenceCount = 1;
|
||||
_value = value;
|
||||
_cbOwnership = new BitMap(CommandBufferPool.MaxCommandBuffers);
|
||||
}
|
||||
|
||||
public Auto(T value, MultiFenceHolder waitable, params IAutoPrivate[] referencedObjs) : this(value)
|
||||
{
|
||||
_waitable = waitable;
|
||||
_referencedObjs = referencedObjs;
|
||||
|
||||
for (int i = 0; i < referencedObjs.Length; i++)
|
||||
{
|
||||
referencedObjs[i].IncrementReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
public T Get(CommandBufferScoped cbs, int offset, int size)
|
||||
{
|
||||
_waitable?.AddBufferUse(cbs.CommandBufferIndex, offset, size);
|
||||
return Get(cbs);
|
||||
}
|
||||
|
||||
public T GetUnsafe()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
public T Get(CommandBufferScoped cbs)
|
||||
{
|
||||
if (!_destroyed)
|
||||
{
|
||||
AddCommandBufferDependencies(cbs);
|
||||
}
|
||||
|
||||
return _value;
|
||||
}
|
||||
|
||||
public void AddCommandBufferDependencies(CommandBufferScoped cbs)
|
||||
{
|
||||
// We don't want to add a reference to this object to the command buffer
|
||||
// more than once, so if we detect that the command buffer already has ownership
|
||||
// of this object, then we can just return without doing anything else.
|
||||
if (_cbOwnership.Set(cbs.CommandBufferIndex))
|
||||
{
|
||||
if (_waitable != null)
|
||||
{
|
||||
cbs.AddWaitable(_waitable);
|
||||
}
|
||||
|
||||
cbs.AddDependant(this);
|
||||
|
||||
// We need to add a dependency on the command buffer to all objects this object
|
||||
// references aswell.
|
||||
if (_referencedObjs != null)
|
||||
{
|
||||
for (int i = 0; i < _referencedObjs.Length; i++)
|
||||
{
|
||||
_referencedObjs[i].AddCommandBufferDependencies(cbs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void IncrementReferenceCount()
|
||||
{
|
||||
if (_referenceCount == 0)
|
||||
{
|
||||
throw new Exception("Attempted to inc ref of dead object.");
|
||||
}
|
||||
_referenceCount++;
|
||||
}
|
||||
|
||||
public void DecrementReferenceCount(int cbIndex)
|
||||
{
|
||||
_cbOwnership.Clear(cbIndex);
|
||||
DecrementReferenceCount();
|
||||
}
|
||||
|
||||
public void DecrementReferenceCount()
|
||||
{
|
||||
if (--_referenceCount == 0)
|
||||
{
|
||||
_value.Dispose();
|
||||
_value = default;
|
||||
_destroyed = true;
|
||||
|
||||
// Value is no longer in use by the GPU, dispose all other
|
||||
// resources that it references.
|
||||
if (_referencedObjs != null)
|
||||
{
|
||||
for (int i = 0; i < _referencedObjs.Length; i++)
|
||||
{
|
||||
_referencedObjs[i].DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(_referenceCount >= 0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
DecrementReferenceCount();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
42
Ryujinx.Graphics.Vulkan/BitMap.cs
Normal file
42
Ryujinx.Graphics.Vulkan/BitMap.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct BitMap
|
||||
{
|
||||
private const int IntSize = 64;
|
||||
private const int IntMask = IntSize - 1;
|
||||
|
||||
private readonly long[] _masks;
|
||||
|
||||
public BitMap(int count)
|
||||
{
|
||||
_masks = new long[(count + IntMask) / IntSize];
|
||||
}
|
||||
|
||||
public bool Set(int bit)
|
||||
{
|
||||
int wordIndex = bit / IntSize;
|
||||
int wordBit = bit & IntMask;
|
||||
|
||||
long wordMask = 1L << wordBit;
|
||||
|
||||
if ((_masks[wordIndex] & wordMask) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_masks[wordIndex] |= wordMask;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Clear(int bit)
|
||||
{
|
||||
int wordIndex = bit / IntSize;
|
||||
int wordBit = bit & IntMask;
|
||||
|
||||
long wordMask = 1L << wordBit;
|
||||
|
||||
_masks[wordIndex] &= ~wordMask;
|
||||
}
|
||||
}
|
||||
}
|
365
Ryujinx.Graphics.Vulkan/BufferHolder.cs
Normal file
365
Ryujinx.Graphics.Vulkan/BufferHolder.cs
Normal file
|
@ -0,0 +1,365 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class BufferHolder : IDisposable
|
||||
{
|
||||
private const int MaxUpdateBufferSize = 0x10000;
|
||||
|
||||
public const AccessFlags DefaultAccessFlags =
|
||||
AccessFlags.AccessShaderReadBit |
|
||||
AccessFlags.AccessShaderWriteBit |
|
||||
AccessFlags.AccessTransferReadBit |
|
||||
AccessFlags.AccessTransferWriteBit |
|
||||
AccessFlags.AccessUniformReadBit |
|
||||
AccessFlags.AccessShaderReadBit |
|
||||
AccessFlags.AccessShaderWriteBit;
|
||||
|
||||
private readonly VulkanGraphicsDevice _gd;
|
||||
private readonly Device _device;
|
||||
private readonly MemoryAllocation _allocation;
|
||||
private readonly Auto<DisposableBuffer> _buffer;
|
||||
private readonly Auto<MemoryAllocation> _allocationAuto;
|
||||
private readonly ulong _bufferHandle;
|
||||
|
||||
private CacheByRange<BufferHolder> _cachedConvertedIndexBuffers;
|
||||
|
||||
public int Size { get; }
|
||||
|
||||
private IntPtr _map;
|
||||
|
||||
private readonly MultiFenceHolder _waitable;
|
||||
|
||||
private bool _lastAccessIsWrite;
|
||||
|
||||
public BufferHolder(VulkanGraphicsDevice gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
_allocation = allocation;
|
||||
_allocationAuto = new Auto<MemoryAllocation>(allocation);
|
||||
_waitable = new MultiFenceHolder();
|
||||
_buffer = new Auto<DisposableBuffer>(new DisposableBuffer(gd.Api, device, buffer), _waitable, _allocationAuto);
|
||||
_bufferHandle = buffer.Handle;
|
||||
Size = size;
|
||||
_map = allocation.HostPointer;
|
||||
}
|
||||
|
||||
public unsafe Auto<DisposableBufferView> CreateView(VkFormat format, int offset, int size)
|
||||
{
|
||||
var bufferViewCreateInfo = new BufferViewCreateInfo()
|
||||
{
|
||||
SType = StructureType.BufferViewCreateInfo,
|
||||
Buffer = new VkBuffer(_bufferHandle),
|
||||
Format = format,
|
||||
Offset = (uint)offset,
|
||||
Range = (uint)size
|
||||
};
|
||||
|
||||
_gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
|
||||
|
||||
return new Auto<DisposableBufferView>(new DisposableBufferView(_gd.Api, _device, bufferView), _waitable, _buffer);
|
||||
}
|
||||
|
||||
public unsafe void InsertBarrier(CommandBuffer commandBuffer, bool isWrite)
|
||||
{
|
||||
// If the last access is write, we always need a barrier to be sure we will read or modify
|
||||
// the correct data.
|
||||
// If the last access is read, and current one is a write, we need to wait until the
|
||||
// read finishes to avoid overwriting data still in use.
|
||||
// Otherwise, if the last access is a read and the current one too, we don't need barriers.
|
||||
bool needsBarrier = isWrite || _lastAccessIsWrite;
|
||||
|
||||
_lastAccessIsWrite = isWrite;
|
||||
|
||||
if (needsBarrier)
|
||||
{
|
||||
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
||||
{
|
||||
SType = StructureType.MemoryBarrier,
|
||||
SrcAccessMask = DefaultAccessFlags,
|
||||
DstAccessMask = DefaultAccessFlags
|
||||
};
|
||||
|
||||
_gd.Api.CmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
DependencyFlags.DependencyDeviceGroupBit,
|
||||
1,
|
||||
memoryBarrier,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
public Auto<DisposableBuffer> GetBuffer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, bool isWrite = false)
|
||||
{
|
||||
if (isWrite)
|
||||
{
|
||||
_cachedConvertedIndexBuffers.Clear();
|
||||
}
|
||||
|
||||
// InsertBarrier(commandBuffer, isWrite);
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public BufferHandle GetHandle()
|
||||
{
|
||||
var handle = _bufferHandle;
|
||||
return Unsafe.As<ulong, BufferHandle>(ref handle);
|
||||
}
|
||||
|
||||
public unsafe IntPtr Map(int offset, int mappingSize)
|
||||
{
|
||||
return _map;
|
||||
}
|
||||
|
||||
public unsafe ReadOnlySpan<byte> GetData(int offset, int size)
|
||||
{
|
||||
return GetDataStorage(offset, size);
|
||||
}
|
||||
|
||||
public unsafe Span<byte> GetDataStorage(int offset, int size)
|
||||
{
|
||||
int mappingSize = Math.Min(size, Size - offset);
|
||||
|
||||
if (_map != IntPtr.Zero)
|
||||
{
|
||||
return new Span<byte>((void*)(_map + offset), mappingSize);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The buffer is not host mapped.");
|
||||
}
|
||||
|
||||
public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs = null, Action endRenderPass = null)
|
||||
{
|
||||
int dataSize = Math.Min(data.Length, Size - offset);
|
||||
if (dataSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool needsFlush = _gd.CommandBufferPool.HasWaitableOnRentedCommandBuffer(_waitable, offset, dataSize);
|
||||
bool needsWait = needsFlush || MayWait(offset, dataSize);
|
||||
|
||||
if (cbs == null ||
|
||||
!needsWait ||
|
||||
!VulkanConfiguration.UseFastBufferUpdates ||
|
||||
!TryPushData(cbs.Value, endRenderPass, offset, data))
|
||||
{
|
||||
// Some pending command might access the buffer,
|
||||
// so we need to ensure they are all submited to the GPU before waiting.
|
||||
// The flush below forces the submission of all pending commands.
|
||||
if (needsFlush)
|
||||
{
|
||||
_gd.FlushAllCommands();
|
||||
}
|
||||
|
||||
WaitForFences(offset, dataSize);
|
||||
|
||||
if (_map != IntPtr.Zero)
|
||||
{
|
||||
data.Slice(0, dataSize).CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void SetDataUnchecked(int offset, ReadOnlySpan<byte> data)
|
||||
{
|
||||
int dataSize = Math.Min(data.Length, Size - offset);
|
||||
if (dataSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_map != IntPtr.Zero)
|
||||
{
|
||||
data.Slice(0, dataSize).CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDataInline(CommandBufferScoped cbs, Action endRenderPass, int dstOffset, ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (!TryPushData(cbs, endRenderPass, dstOffset, data))
|
||||
{
|
||||
throw new ArgumentException($"Invalid offset 0x{dstOffset:X} or data size 0x{data.Length:X}.");
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe bool TryPushData(CommandBufferScoped cbs, Action endRenderPass, int dstOffset, ReadOnlySpan<byte> data)
|
||||
{
|
||||
if ((dstOffset & 3) != 0 || (data.Length & 3) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
endRenderPass();
|
||||
|
||||
var dstBuffer = GetBuffer(cbs.CommandBuffer, true).Get(cbs, dstOffset, data.Length).Value;
|
||||
|
||||
InsertBufferBarrier(
|
||||
_gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
AccessFlags.AccessTransferWriteBit,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
PipelineStageFlags.PipelineStageTransferBit,
|
||||
dstOffset,
|
||||
data.Length);
|
||||
|
||||
fixed (byte* pData = data)
|
||||
{
|
||||
for (ulong offset = 0; offset < (ulong)data.Length;)
|
||||
{
|
||||
ulong size = Math.Min(MaxUpdateBufferSize, (ulong)data.Length - offset);
|
||||
_gd.Api.CmdUpdateBuffer(cbs.CommandBuffer, dstBuffer, (ulong)dstOffset + offset, size, pData + offset);
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
InsertBufferBarrier(
|
||||
_gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
AccessFlags.AccessTransferWriteBit,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
PipelineStageFlags.PipelineStageTransferBit,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
dstOffset,
|
||||
data.Length);
|
||||
|
||||
// Not flushing commands here causes glitches on Intel (driver bug?)
|
||||
if (_gd.Vendor == Vendor.Intel)
|
||||
{
|
||||
_gd.FlushAllCommands();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe void Copy(
|
||||
VulkanGraphicsDevice gd,
|
||||
CommandBufferScoped cbs,
|
||||
Auto<DisposableBuffer> src,
|
||||
Auto<DisposableBuffer> dst,
|
||||
int srcOffset,
|
||||
int dstOffset,
|
||||
int size)
|
||||
{
|
||||
var srcBuffer = src.Get(cbs, srcOffset, size).Value;
|
||||
var dstBuffer = dst.Get(cbs, dstOffset, size).Value;
|
||||
|
||||
InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
AccessFlags.AccessTransferWriteBit,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
PipelineStageFlags.PipelineStageTransferBit,
|
||||
dstOffset,
|
||||
size);
|
||||
|
||||
var region = new BufferCopy((ulong)srcOffset, (ulong)dstOffset, (ulong)size);
|
||||
|
||||
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, 1, ®ion);
|
||||
|
||||
InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
AccessFlags.AccessTransferWriteBit,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
PipelineStageFlags.PipelineStageTransferBit,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
dstOffset,
|
||||
size);
|
||||
}
|
||||
|
||||
public static unsafe void InsertBufferBarrier(
|
||||
VulkanGraphicsDevice gd,
|
||||
CommandBuffer commandBuffer,
|
||||
VkBuffer buffer,
|
||||
AccessFlags srcAccessMask,
|
||||
AccessFlags dstAccessMask,
|
||||
PipelineStageFlags srcStageMask,
|
||||
PipelineStageFlags dstStageMask,
|
||||
int offset,
|
||||
int size)
|
||||
{
|
||||
BufferMemoryBarrier memoryBarrier = new BufferMemoryBarrier()
|
||||
{
|
||||
SType = StructureType.BufferMemoryBarrier,
|
||||
SrcAccessMask = srcAccessMask,
|
||||
DstAccessMask = dstAccessMask,
|
||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
Buffer = buffer,
|
||||
Offset = (ulong)offset,
|
||||
Size = (ulong)size
|
||||
};
|
||||
|
||||
gd.Api.CmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
srcStageMask,
|
||||
dstStageMask,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
memoryBarrier,
|
||||
0,
|
||||
null);
|
||||
}
|
||||
|
||||
public void WaitForFences()
|
||||
{
|
||||
_waitable.WaitForFences(_gd.Api, _device);
|
||||
}
|
||||
|
||||
public void WaitForFences(int offset, int size)
|
||||
{
|
||||
_waitable.WaitForFences(_gd.Api, _device, offset, size);
|
||||
}
|
||||
|
||||
public bool MayWait(int offset, int size)
|
||||
{
|
||||
return _waitable.MayWait(_gd.Api, _device, offset, size);
|
||||
}
|
||||
|
||||
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
|
||||
{
|
||||
if (!_cachedConvertedIndexBuffers.TryGetValue(offset, size, out var holder))
|
||||
{
|
||||
holder = _gd.BufferManager.Create(_gd, (size * 2 + 3) & ~3);
|
||||
|
||||
_gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size);
|
||||
|
||||
_cachedConvertedIndexBuffers.Add(offset, size, holder);
|
||||
}
|
||||
|
||||
return holder.GetBuffer();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_buffer.Dispose();
|
||||
_allocationAuto.Dispose();
|
||||
_cachedConvertedIndexBuffers.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
202
Ryujinx.Graphics.Vulkan/BufferManager.cs
Normal file
202
Ryujinx.Graphics.Vulkan/BufferManager.cs
Normal file
|
@ -0,0 +1,202 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class BufferManager : IDisposable
|
||||
{
|
||||
private const MemoryPropertyFlags DefaultBufferMemoryFlags =
|
||||
MemoryPropertyFlags.MemoryPropertyHostVisibleBit |
|
||||
MemoryPropertyFlags.MemoryPropertyHostCoherentBit |
|
||||
MemoryPropertyFlags.MemoryPropertyHostCachedBit;
|
||||
|
||||
private const BufferUsageFlags DefaultBufferUsageFlags =
|
||||
BufferUsageFlags.BufferUsageTransferSrcBit |
|
||||
BufferUsageFlags.BufferUsageTransferDstBit |
|
||||
BufferUsageFlags.BufferUsageUniformTexelBufferBit |
|
||||
BufferUsageFlags.BufferUsageStorageTexelBufferBit |
|
||||
BufferUsageFlags.BufferUsageUniformBufferBit |
|
||||
BufferUsageFlags.BufferUsageStorageBufferBit |
|
||||
BufferUsageFlags.BufferUsageIndexBufferBit |
|
||||
BufferUsageFlags.BufferUsageVertexBufferBit |
|
||||
BufferUsageFlags.BufferUsageTransformFeedbackBufferBitExt;
|
||||
|
||||
private readonly PhysicalDevice _physicalDevice;
|
||||
private readonly Device _device;
|
||||
|
||||
private readonly List<BufferHolder> _buffers;
|
||||
|
||||
public StagingBuffer StagingBuffer { get; }
|
||||
|
||||
public BufferManager(VulkanGraphicsDevice gd, PhysicalDevice physicalDevice, Device device)
|
||||
{
|
||||
_physicalDevice = physicalDevice;
|
||||
_device = device;
|
||||
_buffers = new List<BufferHolder>();
|
||||
StagingBuffer = new StagingBuffer(gd, this);
|
||||
}
|
||||
|
||||
public BufferHandle CreateWithHandle(VulkanGraphicsDevice gd, int size)
|
||||
{
|
||||
var holder = Create(gd, size);
|
||||
if (holder == null)
|
||||
{
|
||||
return BufferHandle.Null;
|
||||
}
|
||||
|
||||
ulong handle64 = (ulong)_buffers.Count + 1;
|
||||
|
||||
var handle = Unsafe.As<ulong, BufferHandle>(ref handle64);
|
||||
|
||||
_buffers.Add(holder);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
public unsafe BufferHolder Create(VulkanGraphicsDevice gd, int size, bool forConditionalRendering = false)
|
||||
{
|
||||
var usage = DefaultBufferUsageFlags;
|
||||
|
||||
if (forConditionalRendering && gd.Capabilities.SupportsConditionalRendering)
|
||||
{
|
||||
usage |= BufferUsageFlags.BufferUsageConditionalRenderingBitExt;
|
||||
}
|
||||
else if (gd.SupportsIndirectParameters)
|
||||
{
|
||||
usage |= BufferUsageFlags.BufferUsageIndirectBufferBit;
|
||||
}
|
||||
|
||||
var bufferCreateInfo = new BufferCreateInfo()
|
||||
{
|
||||
SType = StructureType.BufferCreateInfo,
|
||||
Size = (ulong)size,
|
||||
Usage = usage,
|
||||
SharingMode = SharingMode.Exclusive
|
||||
};
|
||||
|
||||
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
|
||||
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
|
||||
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, DefaultBufferMemoryFlags);
|
||||
|
||||
if (allocation.Memory.Handle == 0UL)
|
||||
{
|
||||
gd.Api.DestroyBuffer(_device, buffer, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
gd.Api.BindBufferMemory(_device, buffer, allocation.Memory, allocation.Offset);
|
||||
|
||||
return new BufferHolder(gd, _device, buffer, allocation, size);
|
||||
}
|
||||
|
||||
public Auto<DisposableBufferView> CreateView(BufferHandle handle, VkFormat format, int offset, int size)
|
||||
{
|
||||
if (TryGetBuffer(handle, out var holder))
|
||||
{
|
||||
return holder.CreateView(format, offset, size);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite)
|
||||
{
|
||||
if (TryGetBuffer(handle, out var holder))
|
||||
{
|
||||
return holder.GetBuffer(commandBuffer, isWrite);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size)
|
||||
{
|
||||
if (TryGetBuffer(handle, out var holder))
|
||||
{
|
||||
return holder.GetBufferI8ToI16(cbs, offset, size);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, bool isWrite, out int size)
|
||||
{
|
||||
if (TryGetBuffer(handle, out var holder))
|
||||
{
|
||||
size = holder.Size;
|
||||
return holder.GetBuffer(commandBuffer, isWrite);
|
||||
}
|
||||
|
||||
size = 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> GetData(BufferHandle handle, int offset, int size)
|
||||
{
|
||||
if (TryGetBuffer(handle, out var holder))
|
||||
{
|
||||
return holder.GetData(offset, size);
|
||||
}
|
||||
|
||||
return ReadOnlySpan<byte>.Empty;
|
||||
}
|
||||
|
||||
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
|
||||
{
|
||||
SetData(handle, offset, MemoryMarshal.Cast<T, byte>(data), null, null);
|
||||
}
|
||||
|
||||
public void SetData(BufferHandle handle, int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs, Action endRenderPass)
|
||||
{
|
||||
if (TryGetBuffer(handle, out var holder))
|
||||
{
|
||||
holder.SetData(offset, data, cbs, endRenderPass);
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(BufferHandle handle)
|
||||
{
|
||||
if (TryGetBuffer(handle, out var holder))
|
||||
{
|
||||
holder.Dispose();
|
||||
// _buffers.Remove(handle);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetBuffer(BufferHandle handle, out BufferHolder holder)
|
||||
{
|
||||
int index = (int)Unsafe.As<BufferHandle, ulong>(ref handle) - 1;
|
||||
if ((uint)index < _buffers.Count)
|
||||
{
|
||||
holder = _buffers[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
holder = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
for (int i = 0; i < _buffers.Count; i++)
|
||||
{
|
||||
_buffers[i].Dispose();
|
||||
}
|
||||
|
||||
StagingBuffer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
147
Ryujinx.Graphics.Vulkan/BufferRangeList.cs
Normal file
147
Ryujinx.Graphics.Vulkan/BufferRangeList.cs
Normal file
|
@ -0,0 +1,147 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct BufferRangeList
|
||||
{
|
||||
private struct Range
|
||||
{
|
||||
public int Offset { get; }
|
||||
public int Size { get; }
|
||||
|
||||
public Range(int offset, int size)
|
||||
{
|
||||
Offset = offset;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public bool OverlapsWith(int offset, int size)
|
||||
{
|
||||
return Offset < offset + size && offset < Offset + Size;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Range>[] _ranges;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_ranges = new List<Range>[CommandBufferPool.MaxCommandBuffers];
|
||||
}
|
||||
|
||||
public void Add(int cbIndex, int offset, int size)
|
||||
{
|
||||
var list = _ranges[cbIndex];
|
||||
if (list != null)
|
||||
{
|
||||
int overlapIndex = BinarySearch(list, offset, size);
|
||||
if (overlapIndex >= 0)
|
||||
{
|
||||
while (overlapIndex > 0 && list[overlapIndex - 1].OverlapsWith(offset, size))
|
||||
{
|
||||
overlapIndex--;
|
||||
}
|
||||
|
||||
int endOffset = offset + size;
|
||||
int startIndex = overlapIndex;
|
||||
|
||||
while (overlapIndex < list.Count && list[overlapIndex].OverlapsWith(offset, size))
|
||||
{
|
||||
var currentOverlap = list[overlapIndex];
|
||||
var currentOverlapEndOffset = currentOverlap.Offset + currentOverlap.Size;
|
||||
|
||||
if (offset > currentOverlap.Offset)
|
||||
{
|
||||
offset = currentOverlap.Offset;
|
||||
}
|
||||
|
||||
if (endOffset < currentOverlapEndOffset)
|
||||
{
|
||||
endOffset = currentOverlapEndOffset;
|
||||
}
|
||||
|
||||
overlapIndex++;
|
||||
}
|
||||
|
||||
int count = overlapIndex - startIndex;
|
||||
|
||||
list.RemoveRange(startIndex, count);
|
||||
|
||||
size = endOffset - offset;
|
||||
overlapIndex = startIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
overlapIndex = ~overlapIndex;
|
||||
}
|
||||
|
||||
list.Insert(overlapIndex, new Range(offset, size));
|
||||
|
||||
int last = 0;
|
||||
foreach (var rg in list)
|
||||
{
|
||||
if (rg.Offset < last)
|
||||
{
|
||||
throw new System.Exception("list not properly sorted");
|
||||
}
|
||||
last = rg.Offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
list = new List<Range>
|
||||
{
|
||||
new Range(offset, size)
|
||||
};
|
||||
|
||||
_ranges[cbIndex] = list;
|
||||
}
|
||||
}
|
||||
|
||||
public bool OverlapsWith(int cbIndex, int offset, int size)
|
||||
{
|
||||
var list = _ranges[cbIndex];
|
||||
if (list == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return BinarySearch(list, offset, size) >= 0;
|
||||
}
|
||||
|
||||
private static int BinarySearch(List<Range> list, int offset, int size)
|
||||
{
|
||||
int left = 0;
|
||||
int right = list.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int range = right - left;
|
||||
|
||||
int middle = left + (range >> 1);
|
||||
|
||||
var item = list[middle];
|
||||
|
||||
if (item.OverlapsWith(offset, size))
|
||||
{
|
||||
return middle;
|
||||
}
|
||||
|
||||
if (offset < item.Offset)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ~left;
|
||||
}
|
||||
|
||||
public void Clear(int cbIndex)
|
||||
{
|
||||
_ranges[cbIndex] = null;
|
||||
}
|
||||
}
|
||||
}
|
69
Ryujinx.Graphics.Vulkan/BufferState.cs
Normal file
69
Ryujinx.Graphics.Vulkan/BufferState.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct BufferState : IDisposable
|
||||
{
|
||||
public static BufferState Null => new BufferState(null, 0, 0);
|
||||
|
||||
private readonly Auto<DisposableBuffer> _buffer;
|
||||
private readonly int _offset;
|
||||
private readonly int _size;
|
||||
private readonly ulong _stride;
|
||||
private readonly IndexType _type;
|
||||
|
||||
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, IndexType type)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_offset = offset;
|
||||
_size = size;
|
||||
_stride = 0;
|
||||
_type = type;
|
||||
buffer?.IncrementReferenceCount();
|
||||
}
|
||||
|
||||
public BufferState(Auto<DisposableBuffer> buffer, int offset, int size, ulong stride = 0UL)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_offset = offset;
|
||||
_size = size;
|
||||
_stride = stride;
|
||||
_type = IndexType.Uint16;
|
||||
buffer?.IncrementReferenceCount();
|
||||
}
|
||||
|
||||
public void BindIndexBuffer(Vk api, CommandBufferScoped cbs)
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
api.CmdBindIndexBuffer(cbs.CommandBuffer, _buffer.Get(cbs, _offset, _size).Value, (ulong)_offset, _type);
|
||||
}
|
||||
}
|
||||
|
||||
public void BindTransformFeedbackBuffer(VulkanGraphicsDevice gd, CommandBufferScoped cbs, uint binding)
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
var buffer = _buffer.Get(cbs, _offset, _size).Value;
|
||||
|
||||
gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset, (ulong)_size);
|
||||
}
|
||||
}
|
||||
|
||||
public void BindVertexBuffer(VulkanGraphicsDevice gd, CommandBufferScoped cbs, uint binding)
|
||||
{
|
||||
if (_buffer != null)
|
||||
{
|
||||
var buffer = _buffer.Get(cbs, _offset, _size).Value;
|
||||
|
||||
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_buffer?.DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
}
|
54
Ryujinx.Graphics.Vulkan/CacheByRange.cs
Normal file
54
Ryujinx.Graphics.Vulkan/CacheByRange.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct CacheByRange<T> where T : IDisposable
|
||||
{
|
||||
private Dictionary<ulong, T> _ranges;
|
||||
|
||||
public void Add(int offset, int size, T value)
|
||||
{
|
||||
EnsureInitialized();
|
||||
_ranges.Add(PackRange(offset, size), value);
|
||||
}
|
||||
|
||||
public bool TryGetValue(int offset, int size, out T value)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _ranges.TryGetValue(PackRange(offset, size), out value);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (_ranges != null)
|
||||
{
|
||||
foreach (T value in _ranges.Values)
|
||||
{
|
||||
value.Dispose();
|
||||
}
|
||||
|
||||
_ranges.Clear();
|
||||
_ranges = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
if (_ranges == null)
|
||||
{
|
||||
_ranges = new Dictionary<ulong, T>();
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong PackRange(int offset, int size)
|
||||
{
|
||||
return (uint)offset | ((ulong)size << 32);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
}
|
||||
}
|
291
Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
Normal file
291
Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
Normal file
|
@ -0,0 +1,291 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Thread = System.Threading.Thread;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class CommandBufferPool : IDisposable
|
||||
{
|
||||
public const int MaxCommandBuffers = 8;
|
||||
|
||||
private int _totalCommandBuffers;
|
||||
private int _totalCommandBuffersMask;
|
||||
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
private readonly Queue _queue;
|
||||
private readonly CommandPool _pool;
|
||||
private readonly Thread _owner;
|
||||
|
||||
public bool OwnedByCurrentThread => _owner == Thread.CurrentThread;
|
||||
|
||||
private struct ReservedCommandBuffer
|
||||
{
|
||||
public bool InUse;
|
||||
public bool InConsumption;
|
||||
public CommandBuffer CommandBuffer;
|
||||
public FenceHolder Fence;
|
||||
public SemaphoreHolder Semaphore;
|
||||
|
||||
public List<IAuto> Dependants;
|
||||
public HashSet<MultiFenceHolder> Waitables;
|
||||
public HashSet<SemaphoreHolder> Dependencies;
|
||||
|
||||
public void Initialize(Vk api, Device device, CommandPool pool)
|
||||
{
|
||||
var allocateInfo = new CommandBufferAllocateInfo()
|
||||
{
|
||||
SType = StructureType.CommandBufferAllocateInfo,
|
||||
CommandBufferCount = 1,
|
||||
CommandPool = pool,
|
||||
Level = CommandBufferLevel.Primary
|
||||
};
|
||||
|
||||
api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer);
|
||||
|
||||
Dependants = new List<IAuto>();
|
||||
Waitables = new HashSet<MultiFenceHolder>();
|
||||
Dependencies = new HashSet<SemaphoreHolder>();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ReservedCommandBuffer[] _commandBuffers;
|
||||
|
||||
private int _cursor;
|
||||
|
||||
public unsafe CommandBufferPool(Vk api, Device device, Queue queue, uint queueFamilyIndex, bool isLight = false)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
_queue = queue;
|
||||
_owner = Thread.CurrentThread;
|
||||
|
||||
var commandPoolCreateInfo = new CommandPoolCreateInfo()
|
||||
{
|
||||
SType = StructureType.CommandPoolCreateInfo,
|
||||
QueueFamilyIndex = queueFamilyIndex,
|
||||
Flags = CommandPoolCreateFlags.CommandPoolCreateTransientBit |
|
||||
CommandPoolCreateFlags.CommandPoolCreateResetCommandBufferBit
|
||||
};
|
||||
|
||||
api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError();
|
||||
|
||||
_totalCommandBuffers = isLight ? 1 : MaxCommandBuffers;
|
||||
_totalCommandBuffersMask = _totalCommandBuffers - 1;
|
||||
|
||||
_commandBuffers = new ReservedCommandBuffer[_totalCommandBuffers];
|
||||
|
||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
||||
{
|
||||
_commandBuffers[i].Initialize(api, device, _pool);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddDependant(int cbIndex, IAuto dependant)
|
||||
{
|
||||
dependant.IncrementReferenceCount();
|
||||
_commandBuffers[cbIndex].Dependants.Add(dependant);
|
||||
}
|
||||
|
||||
public void AddWaitable(MultiFenceHolder waitable)
|
||||
{
|
||||
lock (_commandBuffers)
|
||||
{
|
||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
||||
{
|
||||
ref var entry = ref _commandBuffers[i];
|
||||
|
||||
if (entry.InConsumption)
|
||||
{
|
||||
AddWaitable(i, waitable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddDependency(int cbIndex, CommandBufferScoped dependencyCbs)
|
||||
{
|
||||
Debug.Assert(_commandBuffers[cbIndex].InUse);
|
||||
var semaphoreHolder = _commandBuffers[dependencyCbs.CommandBufferIndex].Semaphore;
|
||||
semaphoreHolder.Get();
|
||||
_commandBuffers[cbIndex].Dependencies.Add(semaphoreHolder);
|
||||
}
|
||||
|
||||
public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
|
||||
{
|
||||
ref var entry = ref _commandBuffers[cbIndex];
|
||||
waitable.AddFence(cbIndex, entry.Fence);
|
||||
entry.Waitables.Add(waitable);
|
||||
}
|
||||
|
||||
public bool HasWaitableOnRentedCommandBuffer(MultiFenceHolder waitable, int offset, int size)
|
||||
{
|
||||
lock (_commandBuffers)
|
||||
{
|
||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
||||
{
|
||||
ref var entry = ref _commandBuffers[i];
|
||||
|
||||
if (entry.InUse &&
|
||||
entry.Waitables.Contains(waitable) &&
|
||||
waitable.IsBufferRangeInUse(i, offset, size))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public FenceHolder GetFence(int cbIndex)
|
||||
{
|
||||
return _commandBuffers[cbIndex].Fence;
|
||||
}
|
||||
|
||||
public CommandBufferScoped ReturnAndRent(CommandBufferScoped cbs)
|
||||
{
|
||||
Return(cbs);
|
||||
return Rent();
|
||||
}
|
||||
|
||||
public CommandBufferScoped Rent()
|
||||
{
|
||||
lock (_commandBuffers)
|
||||
{
|
||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
||||
{
|
||||
int currentIndex = _cursor;
|
||||
|
||||
ref var entry = ref _commandBuffers[currentIndex];
|
||||
|
||||
if (!entry.InUse)
|
||||
{
|
||||
WaitAndDecrementRef(currentIndex);
|
||||
entry.InUse = true;
|
||||
|
||||
var commandBufferBeginInfo = new CommandBufferBeginInfo()
|
||||
{
|
||||
SType = StructureType.CommandBufferBeginInfo
|
||||
};
|
||||
|
||||
_api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo);
|
||||
|
||||
return new CommandBufferScoped(this, entry.CommandBuffer, currentIndex);
|
||||
}
|
||||
|
||||
_cursor = (currentIndex + 1) & _totalCommandBuffersMask;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public void Return(CommandBufferScoped cbs)
|
||||
{
|
||||
Return(cbs, null, null, null);
|
||||
}
|
||||
|
||||
public unsafe void Return(
|
||||
CommandBufferScoped cbs,
|
||||
Semaphore[] waitSemaphores,
|
||||
PipelineStageFlags[] waitDstStageMask,
|
||||
Semaphore[] signalSemaphores)
|
||||
{
|
||||
lock (_commandBuffers)
|
||||
{
|
||||
int cbIndex = cbs.CommandBufferIndex;
|
||||
|
||||
ref var entry = ref _commandBuffers[cbIndex];
|
||||
|
||||
if (_cursor == cbIndex)
|
||||
{
|
||||
_cursor = (cbIndex + 1) & _totalCommandBuffersMask;
|
||||
}
|
||||
|
||||
Debug.Assert(entry.InUse);
|
||||
Debug.Assert(entry.CommandBuffer.Handle == cbs.CommandBuffer.Handle);
|
||||
entry.InUse = false;
|
||||
entry.InConsumption = true;
|
||||
|
||||
var commandBuffer = entry.CommandBuffer;
|
||||
|
||||
_api.EndCommandBuffer(commandBuffer);
|
||||
|
||||
fixed (Semaphore* pWaitSemaphores = waitSemaphores, pSignalSemaphores = signalSemaphores)
|
||||
{
|
||||
fixed (PipelineStageFlags* pWaitDstStageMask = waitDstStageMask)
|
||||
{
|
||||
SubmitInfo sInfo = new SubmitInfo()
|
||||
{
|
||||
SType = StructureType.SubmitInfo,
|
||||
WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0,
|
||||
PWaitSemaphores = pWaitSemaphores,
|
||||
PWaitDstStageMask = pWaitDstStageMask,
|
||||
CommandBufferCount = 1,
|
||||
PCommandBuffers = &commandBuffer,
|
||||
SignalSemaphoreCount = signalSemaphores != null ? (uint)signalSemaphores.Length : 0,
|
||||
PSignalSemaphores = pSignalSemaphores
|
||||
};
|
||||
|
||||
_api.QueueSubmit(_queue, 1, sInfo, entry.Fence.GetUnsafe());
|
||||
}
|
||||
}
|
||||
// _api.QueueWaitIdle(_queue);
|
||||
}
|
||||
}
|
||||
|
||||
private void WaitAndDecrementRef(int cbIndex, bool refreshFence = true)
|
||||
{
|
||||
ref var entry = ref _commandBuffers[cbIndex];
|
||||
|
||||
if (entry.InConsumption)
|
||||
{
|
||||
entry.Fence.Wait();
|
||||
entry.InConsumption = false;
|
||||
}
|
||||
|
||||
foreach (var dependant in entry.Dependants)
|
||||
{
|
||||
dependant.DecrementReferenceCount(cbIndex);
|
||||
}
|
||||
|
||||
foreach (var waitable in entry.Waitables)
|
||||
{
|
||||
waitable.RemoveFence(cbIndex, entry.Fence);
|
||||
waitable.RemoveBufferUses(cbIndex);
|
||||
}
|
||||
|
||||
foreach (var dependency in entry.Dependencies)
|
||||
{
|
||||
dependency.Put();
|
||||
}
|
||||
|
||||
entry.Dependants.Clear();
|
||||
entry.Waitables.Clear();
|
||||
entry.Dependencies.Clear();
|
||||
entry.Fence?.Dispose();
|
||||
|
||||
if (refreshFence)
|
||||
{
|
||||
entry.Fence = new FenceHolder(_api, _device);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.Fence = null;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
||||
{
|
||||
WaitAndDecrementRef(i, refreshFence: false);
|
||||
}
|
||||
|
||||
_api.DestroyCommandPool(_device, _pool, null);
|
||||
}
|
||||
}
|
||||
}
|
44
Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs
Normal file
44
Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct CommandBufferScoped : IDisposable
|
||||
{
|
||||
private readonly CommandBufferPool _pool;
|
||||
public CommandBuffer CommandBuffer { get; }
|
||||
public int CommandBufferIndex { get; }
|
||||
|
||||
public CommandBufferScoped(CommandBufferPool pool, CommandBuffer commandBuffer, int commandBufferIndex)
|
||||
{
|
||||
_pool = pool;
|
||||
CommandBuffer = commandBuffer;
|
||||
CommandBufferIndex = commandBufferIndex;
|
||||
}
|
||||
|
||||
public void AddDependant(IAuto dependant)
|
||||
{
|
||||
_pool.AddDependant(CommandBufferIndex, dependant);
|
||||
}
|
||||
|
||||
public void AddWaitable(MultiFenceHolder waitable)
|
||||
{
|
||||
_pool.AddWaitable(CommandBufferIndex, waitable);
|
||||
}
|
||||
|
||||
public void AddDependency(CommandBufferScoped dependencyCbs)
|
||||
{
|
||||
_pool.AddDependency(CommandBufferIndex, dependencyCbs);
|
||||
}
|
||||
|
||||
public FenceHolder GetFence()
|
||||
{
|
||||
return _pool.GetFence(CommandBufferIndex);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_pool?.Return(this);
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Graphics.Vulkan/Constants.cs
Normal file
20
Ryujinx.Graphics.Vulkan/Constants.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
static class Constants
|
||||
{
|
||||
public const int MaxVertexAttributes = 32;
|
||||
public const int MaxVertexBuffers = 32;
|
||||
public const int MaxTransformFeedbackBuffers = 4;
|
||||
public const int MaxRenderTargets = 8;
|
||||
public const int MaxViewports = 16;
|
||||
public const int MaxShaderStages = 5;
|
||||
public const int MaxUniformBuffersPerStage = 18;
|
||||
public const int MaxStorageBuffersPerStage = 16;
|
||||
public const int MaxTexturesPerStage = 32;
|
||||
public const int MaxImagesPerStage = 8;
|
||||
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
|
||||
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
|
||||
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
|
||||
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
|
||||
}
|
||||
}
|
247
Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs
Normal file
247
Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs
Normal file
|
@ -0,0 +1,247 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DescriptorSetCollection : IDisposable
|
||||
{
|
||||
private DescriptorSetManager.DescriptorPoolHolder _holder;
|
||||
private readonly DescriptorSet[] _descriptorSets;
|
||||
public int SetsCount => _descriptorSets.Length;
|
||||
|
||||
public DescriptorSetCollection(DescriptorSetManager.DescriptorPoolHolder holder, DescriptorSet[] descriptorSets)
|
||||
{
|
||||
_holder = holder;
|
||||
_descriptorSets = descriptorSets;
|
||||
}
|
||||
|
||||
public void InitializeBuffers(int setIndex, int baseBinding, int countPerUnit, DescriptorType type)
|
||||
{
|
||||
Span<DescriptorBufferInfo> infos = stackalloc DescriptorBufferInfo[countPerUnit];
|
||||
|
||||
for (int j = 0; j < countPerUnit; j++)
|
||||
{
|
||||
infos[j] = new DescriptorBufferInfo()
|
||||
{
|
||||
Range = Vk.WholeSize
|
||||
};
|
||||
}
|
||||
|
||||
UpdateBuffers(setIndex, baseBinding, infos, type);
|
||||
}
|
||||
|
||||
public unsafe void UpdateBuffer(int setIndex, int bindingIndex, DescriptorBufferInfo bufferInfo, DescriptorType type)
|
||||
{
|
||||
if (bufferInfo.Buffer.Handle != 0UL)
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)bindingIndex,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = 1,
|
||||
PBufferInfo = &bufferInfo
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateBuffers(int setIndex, int baseBinding, ReadOnlySpan<DescriptorBufferInfo> bufferInfo, DescriptorType type)
|
||||
{
|
||||
if (bufferInfo.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo)
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)baseBinding,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = (uint)bufferInfo.Length,
|
||||
PBufferInfo = pBufferInfo
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateStorageBuffers(int setIndex, int baseBinding, ReadOnlySpan<DescriptorBufferInfo> bufferInfo)
|
||||
{
|
||||
if (bufferInfo.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo)
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)(baseBinding & ~(Constants.MaxStorageBuffersPerStage - 1)),
|
||||
DstArrayElement = (uint)(baseBinding & (Constants.MaxStorageBuffersPerStage - 1)),
|
||||
DescriptorType = DescriptorType.StorageBuffer,
|
||||
DescriptorCount = (uint)bufferInfo.Length,
|
||||
PBufferInfo = pBufferInfo
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateImage(int setIndex, int bindingIndex, DescriptorImageInfo imageInfo, DescriptorType type)
|
||||
{
|
||||
if (imageInfo.ImageView.Handle != 0UL)
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)bindingIndex,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = 1,
|
||||
PImageInfo = &imageInfo
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateImages(int setIndex, int baseBinding, ReadOnlySpan<DescriptorImageInfo> imageInfo, DescriptorType type)
|
||||
{
|
||||
if (imageInfo.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (DescriptorImageInfo* pImageInfo = imageInfo)
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)baseBinding,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = (uint)imageInfo.Length,
|
||||
PImageInfo = pImageInfo
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateImagesCombined(int setIndex, int baseBinding, ReadOnlySpan<DescriptorImageInfo> imageInfo, DescriptorType type)
|
||||
{
|
||||
if (imageInfo.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (DescriptorImageInfo* pImageInfo = imageInfo)
|
||||
{
|
||||
for (int i = 0; i < imageInfo.Length; i++)
|
||||
{
|
||||
bool nonNull = imageInfo[i].ImageView.Handle != 0 && imageInfo[i].Sampler.Handle != 0;
|
||||
if (nonNull)
|
||||
{
|
||||
int count = 1;
|
||||
|
||||
while (i + count < imageInfo.Length &&
|
||||
imageInfo[i + count].ImageView.Handle != 0 &&
|
||||
imageInfo[i + count].Sampler.Handle != 0)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)(baseBinding + i),
|
||||
DescriptorType = DescriptorType.CombinedImageSampler,
|
||||
DescriptorCount = (uint)count,
|
||||
PImageInfo = pImageInfo
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
|
||||
i += count - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateBufferImage(int setIndex, int bindingIndex, BufferView texelBufferView, DescriptorType type)
|
||||
{
|
||||
if (texelBufferView.Handle != 0UL)
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)bindingIndex,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = 1,
|
||||
PTexelBufferView = &texelBufferView
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateBufferImages(int setIndex, int baseBinding, ReadOnlySpan<BufferView> texelBufferView, DescriptorType type)
|
||||
{
|
||||
if (texelBufferView.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (BufferView* pTexelBufferView = texelBufferView)
|
||||
{
|
||||
for (uint i = 0; i < texelBufferView.Length;)
|
||||
{
|
||||
uint count = 1;
|
||||
|
||||
if (texelBufferView[(int)i].Handle != 0UL)
|
||||
{
|
||||
while (i + count < texelBufferView.Length && texelBufferView[(int)(i + count)].Handle != 0UL)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)baseBinding + i,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = count,
|
||||
PTexelBufferView = pTexelBufferView + i
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
}
|
||||
|
||||
i += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DescriptorSet[] GetSets()
|
||||
{
|
||||
return _descriptorSets;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_holder?.FreeDescriptorSets(this);
|
||||
_holder = null;
|
||||
}
|
||||
}
|
||||
}
|
201
Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
Normal file
201
Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
Normal file
|
@ -0,0 +1,201 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class DescriptorSetManager : IDisposable
|
||||
{
|
||||
private const uint DescriptorPoolMultiplier = 16;
|
||||
|
||||
public class DescriptorPoolHolder : IDisposable
|
||||
{
|
||||
public Vk Api { get; }
|
||||
public Device Device { get; }
|
||||
|
||||
private readonly DescriptorPool _pool;
|
||||
private readonly uint _capacity;
|
||||
private int _totalSets;
|
||||
private int _setsInUse;
|
||||
private bool _done;
|
||||
|
||||
public unsafe DescriptorPoolHolder(Vk api, Device device)
|
||||
{
|
||||
Api = api;
|
||||
Device = device;
|
||||
|
||||
var poolSizes = new DescriptorPoolSize[]
|
||||
{
|
||||
new DescriptorPoolSize(DescriptorType.UniformBuffer, (1 + Constants.MaxUniformBufferBindings) * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.StorageBuffer, Constants.MaxStorageBufferBindings * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.CombinedImageSampler, Constants.MaxTextureBindings * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.StorageImage, Constants.MaxImageBindings * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.UniformTexelBuffer, Constants.MaxTextureBindings * DescriptorPoolMultiplier),
|
||||
new DescriptorPoolSize(DescriptorType.StorageTexelBuffer, Constants.MaxImageBindings * DescriptorPoolMultiplier)
|
||||
};
|
||||
|
||||
uint maxSets = (uint)poolSizes.Length * DescriptorPoolMultiplier;
|
||||
|
||||
_capacity = maxSets;
|
||||
|
||||
fixed (DescriptorPoolSize* pPoolsSize = poolSizes)
|
||||
{
|
||||
var descriptorPoolCreateInfo = new DescriptorPoolCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorPoolCreateInfo,
|
||||
MaxSets = maxSets,
|
||||
PoolSizeCount = (uint)poolSizes.Length,
|
||||
PPoolSizes = pPoolsSize
|
||||
};
|
||||
|
||||
Api.CreateDescriptorPool(device, descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts)
|
||||
{
|
||||
TryAllocateDescriptorSets(layouts, isTry: false, out var dsc);
|
||||
return dsc;
|
||||
}
|
||||
|
||||
public bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, out DescriptorSetCollection dsc)
|
||||
{
|
||||
return TryAllocateDescriptorSets(layouts, isTry: true, out dsc);
|
||||
}
|
||||
|
||||
private unsafe bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, bool isTry, out DescriptorSetCollection dsc)
|
||||
{
|
||||
Debug.Assert(!_done);
|
||||
|
||||
DescriptorSet[] descriptorSets = new DescriptorSet[layouts.Length];
|
||||
|
||||
fixed (DescriptorSet* pDescriptorSets = descriptorSets)
|
||||
{
|
||||
fixed (DescriptorSetLayout* pLayouts = layouts)
|
||||
{
|
||||
var descriptorSetAllocateInfo = new DescriptorSetAllocateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorSetAllocateInfo,
|
||||
DescriptorPool = _pool,
|
||||
DescriptorSetCount = (uint)layouts.Length,
|
||||
PSetLayouts = pLayouts
|
||||
};
|
||||
|
||||
var result = Api.AllocateDescriptorSets(Device, &descriptorSetAllocateInfo, pDescriptorSets);
|
||||
if (isTry && result == Result.ErrorOutOfPoolMemory)
|
||||
{
|
||||
_totalSets = (int)_capacity;
|
||||
_done = true;
|
||||
DestroyIfDone();
|
||||
dsc = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
result.ThrowOnError();
|
||||
}
|
||||
}
|
||||
|
||||
_totalSets += layouts.Length;
|
||||
_setsInUse += layouts.Length;
|
||||
|
||||
dsc = new DescriptorSetCollection(this, descriptorSets);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void FreeDescriptorSets(DescriptorSetCollection dsc)
|
||||
{
|
||||
_setsInUse -= dsc.SetsCount;
|
||||
Debug.Assert(_setsInUse >= 0);
|
||||
DestroyIfDone();
|
||||
}
|
||||
|
||||
public bool CanFit(int count)
|
||||
{
|
||||
if (_totalSets + count <= _capacity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
_done = true;
|
||||
DestroyIfDone();
|
||||
return false;
|
||||
}
|
||||
|
||||
private unsafe void DestroyIfDone()
|
||||
{
|
||||
if (_done && _setsInUse == 0)
|
||||
{
|
||||
Api.DestroyDescriptorPool(Device, _pool, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
Api.DestroyDescriptorPool(Device, _pool, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Device _device;
|
||||
private DescriptorPoolHolder _currentPool;
|
||||
|
||||
public DescriptorSetManager(Device device)
|
||||
{
|
||||
_device = device;
|
||||
}
|
||||
|
||||
public Auto<DescriptorSetCollection> AllocateDescriptorSet(Vk api, DescriptorSetLayout layout)
|
||||
{
|
||||
Span<DescriptorSetLayout> layouts = stackalloc DescriptorSetLayout[1];
|
||||
layouts[0] = layout;
|
||||
return AllocateDescriptorSets(api, layouts);
|
||||
}
|
||||
|
||||
public Auto<DescriptorSetCollection> AllocateDescriptorSets(Vk api, ReadOnlySpan<DescriptorSetLayout> layouts)
|
||||
{
|
||||
// If we fail the first time, just create a new pool and try again.
|
||||
if (!GetPool(api, layouts.Length).TryAllocateDescriptorSets(layouts, out var dsc))
|
||||
{
|
||||
dsc = GetPool(api, layouts.Length).AllocateDescriptorSets(layouts);
|
||||
}
|
||||
|
||||
return new Auto<DescriptorSetCollection>(dsc);
|
||||
}
|
||||
|
||||
private DescriptorPoolHolder GetPool(Vk api, int requiredCount)
|
||||
{
|
||||
if (_currentPool == null || !_currentPool.CanFit(requiredCount))
|
||||
{
|
||||
_currentPool = new DescriptorPoolHolder(api, _device);
|
||||
}
|
||||
|
||||
return _currentPool;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
_currentPool?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
489
Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
Normal file
489
Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
Normal file
|
@ -0,0 +1,489 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class DescriptorSetUpdater
|
||||
{
|
||||
private readonly VulkanGraphicsDevice _gd;
|
||||
private readonly PipelineBase _pipeline;
|
||||
|
||||
private ShaderCollection _program;
|
||||
|
||||
private Auto<DisposableBuffer>[] _uniformBufferRefs;
|
||||
private Auto<DisposableBuffer>[] _storageBufferRefs;
|
||||
private Auto<DisposableImageView>[] _textureRefs;
|
||||
private Auto<DisposableSampler>[] _samplerRefs;
|
||||
private Auto<DisposableImageView>[] _imageRefs;
|
||||
private TextureBuffer[] _bufferTextureRefs;
|
||||
private TextureBuffer[] _bufferImageRefs;
|
||||
|
||||
private DescriptorBufferInfo[] _uniformBuffers;
|
||||
private DescriptorBufferInfo[] _storageBuffers;
|
||||
private DescriptorImageInfo[] _textures;
|
||||
private DescriptorImageInfo[] _images;
|
||||
private BufferView[] _bufferTextures;
|
||||
private BufferView[] _bufferImages;
|
||||
|
||||
[Flags]
|
||||
private enum DirtyFlags
|
||||
{
|
||||
None = 0,
|
||||
Uniform = 1 << 0,
|
||||
Storage = 1 << 1,
|
||||
Texture = 1 << 2,
|
||||
Image = 1 << 3,
|
||||
BufferTexture = 1 << 4,
|
||||
BufferImage = 1 << 5,
|
||||
All = Uniform | Storage | Texture | Image | BufferTexture | BufferImage
|
||||
}
|
||||
|
||||
private DirtyFlags _dirty;
|
||||
|
||||
private readonly TextureView _dummyTexture;
|
||||
private readonly SamplerHolder _dummySampler;
|
||||
|
||||
public DescriptorSetUpdater(VulkanGraphicsDevice gd, PipelineBase pipeline)
|
||||
{
|
||||
_gd = gd;
|
||||
_pipeline = pipeline;
|
||||
|
||||
_uniformBuffers = Array.Empty<DescriptorBufferInfo>();
|
||||
_storageBuffers = Array.Empty<DescriptorBufferInfo>();
|
||||
_textures = new DescriptorImageInfo[32 * 5];
|
||||
_textureRefs = new Auto<DisposableImageView>[32 * 5];
|
||||
_samplerRefs = new Auto<DisposableSampler>[32 * 5];
|
||||
_images = Array.Empty<DescriptorImageInfo>();
|
||||
_bufferTextures = Array.Empty<BufferView>();
|
||||
_bufferImages = Array.Empty<BufferView>();
|
||||
|
||||
_dummyTexture = (TextureView)gd.CreateTexture(new GAL.TextureCreateInfo(
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
4,
|
||||
GAL.Format.R8G8B8A8Unorm,
|
||||
DepthStencilMode.Depth,
|
||||
Target.Texture2D,
|
||||
SwizzleComponent.Red,
|
||||
SwizzleComponent.Green,
|
||||
SwizzleComponent.Blue,
|
||||
SwizzleComponent.Alpha), 1f);
|
||||
|
||||
_dummySampler = (SamplerHolder)gd.CreateSampler(new GAL.SamplerCreateInfo(
|
||||
MinFilter.Nearest,
|
||||
MagFilter.Nearest,
|
||||
false,
|
||||
AddressMode.Repeat,
|
||||
AddressMode.Repeat,
|
||||
AddressMode.Repeat,
|
||||
CompareMode.None,
|
||||
GAL.CompareOp.Always,
|
||||
new ColorF(0, 0, 0, 0),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1f));
|
||||
}
|
||||
|
||||
public void SetProgram(ShaderCollection program)
|
||||
{
|
||||
_program = program;
|
||||
_dirty = DirtyFlags.All;
|
||||
}
|
||||
|
||||
public void SetImage(int binding, ITexture image, GAL.Format imageFormat)
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (image is TextureBuffer imageBuffer)
|
||||
{
|
||||
if (_bufferImages.Length <= binding)
|
||||
{
|
||||
Array.Resize(ref _bufferImages, binding + 1);
|
||||
Array.Resize(ref _bufferImageRefs, binding + 1);
|
||||
}
|
||||
|
||||
_bufferImageRefs[binding] = imageBuffer;
|
||||
|
||||
SignalDirty(DirtyFlags.BufferImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_images.Length <= binding)
|
||||
{
|
||||
Array.Resize(ref _images, binding + 1);
|
||||
Array.Resize(ref _imageRefs, binding + 1);
|
||||
}
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
_imageRefs[binding] = ((TextureView)image).GetIdentityImageView();
|
||||
_images[binding] = new DescriptorImageInfo()
|
||||
{
|
||||
ImageLayout = ImageLayout.General
|
||||
};
|
||||
}
|
||||
|
||||
SignalDirty(DirtyFlags.Image);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStorageBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan<BufferRange> buffers)
|
||||
{
|
||||
Array.Resize(ref _storageBuffers, first + buffers.Length);
|
||||
Array.Resize(ref _storageBufferRefs, first + buffers.Length);
|
||||
|
||||
for (int i = 0; i < buffers.Length; i++)
|
||||
{
|
||||
var buffer = buffers[i];
|
||||
|
||||
_storageBufferRefs[first + i] = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
|
||||
|
||||
_storageBuffers[first + i] = new DescriptorBufferInfo()
|
||||
{
|
||||
Offset = (ulong)buffer.Offset,
|
||||
Range = (ulong)buffer.Size
|
||||
};
|
||||
}
|
||||
|
||||
SignalDirty(DirtyFlags.Storage);
|
||||
}
|
||||
|
||||
public void SetTextureAndSampler(int binding, ITexture texture, ISampler sampler)
|
||||
{
|
||||
if (texture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture is TextureBuffer textureBuffer)
|
||||
{
|
||||
if (_bufferTextures.Length <= binding)
|
||||
{
|
||||
Array.Resize(ref _bufferTextures, binding + 1);
|
||||
Array.Resize(ref _bufferTextureRefs, binding + 1);
|
||||
}
|
||||
|
||||
_bufferTextureRefs[binding] = textureBuffer;
|
||||
|
||||
SignalDirty(DirtyFlags.BufferTexture);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sampler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_textures.Length <= binding)
|
||||
{
|
||||
Array.Resize(ref _textures, binding + 1);
|
||||
Array.Resize(ref _textureRefs, binding + 1);
|
||||
Array.Resize(ref _samplerRefs, binding + 1);
|
||||
}
|
||||
|
||||
_textureRefs[binding] = ((TextureView)texture).GetImageView();
|
||||
_samplerRefs[binding] = ((SamplerHolder)sampler).GetSampler();
|
||||
|
||||
_textures[binding] = new DescriptorImageInfo()
|
||||
{
|
||||
ImageLayout = ImageLayout.General
|
||||
};
|
||||
|
||||
SignalDirty(DirtyFlags.Texture);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniformBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan<BufferRange> buffers)
|
||||
{
|
||||
Array.Resize(ref _uniformBuffers, first + buffers.Length);
|
||||
Array.Resize(ref _uniformBufferRefs, first + buffers.Length);
|
||||
|
||||
for (int i = 0; i < buffers.Length; i++)
|
||||
{
|
||||
var buffer = buffers[i];
|
||||
|
||||
_uniformBufferRefs[first + i] = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
|
||||
|
||||
_uniformBuffers[first + i] = new DescriptorBufferInfo()
|
||||
{
|
||||
Offset = (ulong)buffer.Offset,
|
||||
Range = (ulong)buffer.Size
|
||||
};
|
||||
}
|
||||
|
||||
SignalDirty(DirtyFlags.Uniform);
|
||||
}
|
||||
|
||||
private void SignalDirty(DirtyFlags flag)
|
||||
{
|
||||
_dirty |= flag;
|
||||
}
|
||||
|
||||
public void UpdateAndBindDescriptorSets(CommandBufferScoped cbs, PipelineBindPoint pbp)
|
||||
{
|
||||
if ((_dirty & DirtyFlags.All) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// System.Console.WriteLine("modified " + _dirty + " " + _modified + " on program " + _program.GetHashCode().ToString("X"));
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.Uniform))
|
||||
{
|
||||
UpdateAndBind(cbs, PipelineBase.UniformSetIndex, DirtyFlags.Uniform, pbp);
|
||||
}
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.Storage))
|
||||
{
|
||||
UpdateAndBind(cbs, PipelineBase.StorageSetIndex, DirtyFlags.Storage, pbp);
|
||||
}
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.Texture))
|
||||
{
|
||||
UpdateAndBind(cbs, PipelineBase.TextureSetIndex, DirtyFlags.Texture, pbp);
|
||||
}
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.Image))
|
||||
{
|
||||
UpdateAndBind(cbs, PipelineBase.ImageSetIndex, DirtyFlags.Image, pbp);
|
||||
}
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.BufferTexture))
|
||||
{
|
||||
UpdateAndBind(cbs, PipelineBase.BufferTextureSetIndex, DirtyFlags.BufferTexture, pbp);
|
||||
}
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.BufferImage))
|
||||
{
|
||||
UpdateAndBind(cbs, PipelineBase.BufferImageSetIndex, DirtyFlags.BufferImage, pbp);
|
||||
}
|
||||
|
||||
_dirty = DirtyFlags.None;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void UpdateBuffer(CommandBufferScoped cbs, ref DescriptorBufferInfo info, Auto<DisposableBuffer> buffer)
|
||||
{
|
||||
info.Buffer = buffer?.Get(cbs, (int)info.Offset, (int)info.Range).Value ?? default;
|
||||
|
||||
// The spec requires that buffers with null handle have offset as 0 and range as VK_WHOLE_SIZE.
|
||||
if (info.Buffer.Handle == 0)
|
||||
{
|
||||
info.Offset = 0;
|
||||
info.Range = Vk.WholeSize;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, DirtyFlags flag, PipelineBindPoint pbp)
|
||||
{
|
||||
int stagesCount = _program.Bindings[setIndex].Length;
|
||||
if (stagesCount == 0 && setIndex != PipelineBase.UniformSetIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dsc = _program.GetNewDescriptorSetCollection(_gd, cbs.CommandBufferIndex, setIndex, out var isNew).Get(cbs);
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
Initialize(cbs, setIndex, dsc);
|
||||
}
|
||||
|
||||
if (setIndex == PipelineBase.UniformSetIndex)
|
||||
{
|
||||
Span<DescriptorBufferInfo> uniformBuffer = stackalloc DescriptorBufferInfo[1];
|
||||
|
||||
uniformBuffer[0] = new DescriptorBufferInfo()
|
||||
{
|
||||
Offset = 0,
|
||||
Range = (ulong)SupportBuffer.RequiredSize,
|
||||
Buffer = _pipeline.RenderScaleBuffer.GetBuffer().Get(cbs, 0, SupportBuffer.RequiredSize).Value
|
||||
};
|
||||
|
||||
dsc.UpdateBuffers(0, 0, uniformBuffer, DescriptorType.UniformBuffer);
|
||||
}
|
||||
|
||||
for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++)
|
||||
{
|
||||
var stageBindings = _program.Bindings[setIndex][stageIndex];
|
||||
int bindingsCount = stageBindings.Length;
|
||||
int count;
|
||||
|
||||
for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count)
|
||||
{
|
||||
int binding = stageBindings[bindingIndex];
|
||||
count = 1;
|
||||
|
||||
while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
if (setIndex == PipelineBase.UniformSetIndex)
|
||||
{
|
||||
count = Math.Min(count, _uniformBuffers.Length - binding);
|
||||
|
||||
if (count <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
UpdateBuffer(cbs, ref _uniformBuffers[binding + i], _uniformBufferRefs[binding + i]);
|
||||
}
|
||||
|
||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||
dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
||||
}
|
||||
else if (setIndex == PipelineBase.StorageSetIndex)
|
||||
{
|
||||
count = Math.Min(count, _storageBuffers.Length - binding);
|
||||
|
||||
if (count <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
UpdateBuffer(cbs, ref _storageBuffers[binding + i], _storageBufferRefs[binding + i]);
|
||||
}
|
||||
|
||||
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
||||
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
|
||||
}
|
||||
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref var texture = ref _textures[binding + i];
|
||||
|
||||
texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||
texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||
texture.ImageLayout = ImageLayout.General;
|
||||
|
||||
if (texture.ImageView.Handle == 0 || texture.Sampler.Handle == 0)
|
||||
{
|
||||
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
|
||||
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
|
||||
}
|
||||
}
|
||||
|
||||
ReadOnlySpan<DescriptorImageInfo> textures = _textures;
|
||||
dsc.UpdateImages(0, binding, textures.Slice(binding, count), DescriptorType.CombinedImageSampler);
|
||||
}
|
||||
else if (setIndex == PipelineBase.ImageSetIndex)
|
||||
{
|
||||
count = Math.Min(count, _images.Length - binding);
|
||||
|
||||
if (count <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_images[binding + i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||
}
|
||||
|
||||
ReadOnlySpan<DescriptorImageInfo> images = _images;
|
||||
dsc.UpdateImages(0, binding, images.Slice(binding, count), DescriptorType.StorageImage);
|
||||
}
|
||||
else if (setIndex == PipelineBase.BufferTextureSetIndex)
|
||||
{
|
||||
count = Math.Min(count, _bufferTextures.Length - binding);
|
||||
|
||||
if (count <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_bufferTextures[binding + i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs) ?? default;
|
||||
}
|
||||
|
||||
ReadOnlySpan<BufferView> bufferTextures = _bufferTextures;
|
||||
dsc.UpdateBufferImages(0, binding, bufferTextures.Slice(binding, count), DescriptorType.UniformTexelBuffer);
|
||||
}
|
||||
else if (setIndex == PipelineBase.BufferImageSetIndex)
|
||||
{
|
||||
count = Math.Min(count, _bufferImages.Length - binding);
|
||||
|
||||
if (count <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_bufferImages[binding + i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs) ?? default;
|
||||
}
|
||||
|
||||
ReadOnlySpan<BufferView> bufferImages = _bufferImages;
|
||||
dsc.UpdateBufferImages(0, binding, bufferImages.Slice(binding, count), DescriptorType.StorageTexelBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sets = dsc.GetSets();
|
||||
|
||||
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc)
|
||||
{
|
||||
uint stages = _program.Stages;
|
||||
|
||||
while (stages != 0)
|
||||
{
|
||||
int stage = BitOperations.TrailingZeroCount(stages);
|
||||
stages &= ~(1u << stage);
|
||||
|
||||
if (setIndex == PipelineBase.UniformSetIndex)
|
||||
{
|
||||
dsc.InitializeBuffers(0, 1 + stage * Constants.MaxUniformBuffersPerStage, Constants.MaxUniformBuffersPerStage, DescriptorType.UniformBuffer);
|
||||
}
|
||||
else if (setIndex == PipelineBase.StorageSetIndex)
|
||||
{
|
||||
dsc.InitializeBuffers(0, stage * Constants.MaxStorageBuffersPerStage, Constants.MaxStorageBuffersPerStage, DescriptorType.StorageBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SignalCommandBufferChange()
|
||||
{
|
||||
_dirty = DirtyFlags.All;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_dummyTexture.Dispose();
|
||||
_dummySampler.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
24
Ryujinx.Graphics.Vulkan/DisposableBuffer.cs
Normal file
24
Ryujinx.Graphics.Vulkan/DisposableBuffer.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using Silk.NET.Vulkan;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DisposableBuffer : System.IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
|
||||
public Buffer Value { get; }
|
||||
|
||||
public DisposableBuffer(Vk api, Device device, Buffer buffer)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
Value = buffer;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_api.DestroyBuffer(_device, Value, null);
|
||||
}
|
||||
}
|
||||
}
|
24
Ryujinx.Graphics.Vulkan/DisposableBufferView.cs
Normal file
24
Ryujinx.Graphics.Vulkan/DisposableBufferView.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using Silk.NET.Vulkan;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DisposableBufferView : System.IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
|
||||
public BufferView Value { get; }
|
||||
|
||||
public DisposableBufferView(Vk api, Device device, BufferView bufferView)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
Value = bufferView;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_api.DestroyBufferView(_device, Value, null);
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs
Normal file
25
Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DisposableFramebuffer : IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
|
||||
public Framebuffer Value { get; }
|
||||
|
||||
public DisposableFramebuffer(Vk api, Device device, Framebuffer framebuffer)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
Value = framebuffer;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_api.DestroyFramebuffer(_device, Value, null);
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.Vulkan/DisposableImage.cs
Normal file
25
Ryujinx.Graphics.Vulkan/DisposableImage.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DisposableImage : IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
|
||||
public Image Value { get; }
|
||||
|
||||
public DisposableImage(Vk api, Device device, Image image)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
Value = image;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_api.DestroyImage(_device, Value, null);
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.Vulkan/DisposableImageView.cs
Normal file
25
Ryujinx.Graphics.Vulkan/DisposableImageView.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DisposableImageView : IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
|
||||
public ImageView Value { get; }
|
||||
|
||||
public DisposableImageView(Vk api, Device device, ImageView imageView)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
Value = imageView;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_api.DestroyImageView(_device, Value, null);
|
||||
}
|
||||
}
|
||||
}
|
24
Ryujinx.Graphics.Vulkan/DisposableMemory.cs
Normal file
24
Ryujinx.Graphics.Vulkan/DisposableMemory.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DisposableMemory : IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
private readonly DeviceMemory _memory;
|
||||
|
||||
public DisposableMemory(Vk api, Device device, DeviceMemory memory)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
_memory = memory;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_api.FreeMemory(_device, _memory, null);
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.Vulkan/DisposablePipeline.cs
Normal file
25
Ryujinx.Graphics.Vulkan/DisposablePipeline.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DisposablePipeline : IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
|
||||
public Pipeline Value { get; }
|
||||
|
||||
public DisposablePipeline(Vk api, Device device, Pipeline pipeline)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
Value = pipeline;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_api.DestroyPipeline(_device, Value, null);
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs
Normal file
25
Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DisposableRenderPass : IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
|
||||
public RenderPass Value { get; }
|
||||
|
||||
public DisposableRenderPass(Vk api, Device device, RenderPass renderPass)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
Value = renderPass;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_api.DestroyRenderPass(_device, Value, null);
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.Vulkan/DisposableSampler.cs
Normal file
25
Ryujinx.Graphics.Vulkan/DisposableSampler.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct DisposableSampler : IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
|
||||
public Sampler Value { get; }
|
||||
|
||||
public DisposableSampler(Vk api, Device device, Sampler sampler)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
Value = sampler;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_api.DestroySampler(_device, Value, null);
|
||||
}
|
||||
}
|
||||
}
|
489
Ryujinx.Graphics.Vulkan/EnumConversion.cs
Normal file
489
Ryujinx.Graphics.Vulkan/EnumConversion.cs
Normal file
|
@ -0,0 +1,489 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Silk.NET.Vulkan;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
static class EnumConversion
|
||||
{
|
||||
public static ShaderStageFlags Convert(this ShaderStage stage)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage.Vertex:
|
||||
return ShaderStageFlags.ShaderStageVertexBit;
|
||||
case ShaderStage.Geometry:
|
||||
return ShaderStageFlags.ShaderStageGeometryBit;
|
||||
case ShaderStage.TessellationControl:
|
||||
return ShaderStageFlags.ShaderStageTessellationControlBit;
|
||||
case ShaderStage.TessellationEvaluation:
|
||||
return ShaderStageFlags.ShaderStageTessellationEvaluationBit;
|
||||
case ShaderStage.Fragment:
|
||||
return ShaderStageFlags.ShaderStageFragmentBit;
|
||||
case ShaderStage.Compute:
|
||||
return ShaderStageFlags.ShaderStageComputeBit;
|
||||
};
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(ShaderStage)} enum value: {stage}.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static SamplerAddressMode Convert(this AddressMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AddressMode.Clamp:
|
||||
return SamplerAddressMode.ClampToBorder; // TODO: Should be clamp
|
||||
case AddressMode.Repeat:
|
||||
return SamplerAddressMode.Repeat;
|
||||
case AddressMode.MirrorClamp:
|
||||
return SamplerAddressMode.ClampToBorder; // TODO: Should be mirror clamp
|
||||
case AddressMode.MirrorClampToEdge:
|
||||
return SamplerAddressMode.MirrorClampToEdgeKhr;
|
||||
case AddressMode.MirrorClampToBorder:
|
||||
return SamplerAddressMode.ClampToBorder; // TODO: Should be mirror clamp to border
|
||||
case AddressMode.ClampToBorder:
|
||||
return SamplerAddressMode.ClampToBorder;
|
||||
case AddressMode.MirroredRepeat:
|
||||
return SamplerAddressMode.MirroredRepeat;
|
||||
case AddressMode.ClampToEdge:
|
||||
return SamplerAddressMode.ClampToEdge;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(AddressMode)} enum value: {mode}.");
|
||||
|
||||
return SamplerAddressMode.ClampToBorder; // TODO: Should be clamp
|
||||
}
|
||||
|
||||
public static Silk.NET.Vulkan.BlendFactor Convert(this GAL.BlendFactor factor)
|
||||
{
|
||||
switch (factor)
|
||||
{
|
||||
case GAL.BlendFactor.Zero:
|
||||
case GAL.BlendFactor.ZeroGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.Zero;
|
||||
case GAL.BlendFactor.One:
|
||||
case GAL.BlendFactor.OneGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.One;
|
||||
case GAL.BlendFactor.SrcColor:
|
||||
case GAL.BlendFactor.SrcColorGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.SrcColor;
|
||||
case GAL.BlendFactor.OneMinusSrcColor:
|
||||
case GAL.BlendFactor.OneMinusSrcColorGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.OneMinusSrcColor;
|
||||
case GAL.BlendFactor.SrcAlpha:
|
||||
case GAL.BlendFactor.SrcAlphaGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.SrcAlpha;
|
||||
case GAL.BlendFactor.OneMinusSrcAlpha:
|
||||
case GAL.BlendFactor.OneMinusSrcAlphaGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.OneMinusSrcAlpha;
|
||||
case GAL.BlendFactor.DstAlpha:
|
||||
case GAL.BlendFactor.DstAlphaGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.DstAlpha;
|
||||
case GAL.BlendFactor.OneMinusDstAlpha:
|
||||
case GAL.BlendFactor.OneMinusDstAlphaGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.OneMinusDstAlpha;
|
||||
case GAL.BlendFactor.DstColor:
|
||||
case GAL.BlendFactor.DstColorGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.DstColor;
|
||||
case GAL.BlendFactor.OneMinusDstColor:
|
||||
case GAL.BlendFactor.OneMinusDstColorGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.OneMinusDstColor;
|
||||
case GAL.BlendFactor.SrcAlphaSaturate:
|
||||
case GAL.BlendFactor.SrcAlphaSaturateGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.SrcAlphaSaturate;
|
||||
case GAL.BlendFactor.Src1Color:
|
||||
case GAL.BlendFactor.Src1ColorGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.Src1Color;
|
||||
case GAL.BlendFactor.OneMinusSrc1Color:
|
||||
case GAL.BlendFactor.OneMinusSrc1ColorGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.OneMinusSrc1Color;
|
||||
case GAL.BlendFactor.Src1Alpha:
|
||||
case GAL.BlendFactor.Src1AlphaGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.Src1Alpha;
|
||||
case GAL.BlendFactor.OneMinusSrc1Alpha:
|
||||
case GAL.BlendFactor.OneMinusSrc1AlphaGl:
|
||||
return Silk.NET.Vulkan.BlendFactor.OneMinusSrc1Alpha;
|
||||
case GAL.BlendFactor.ConstantColor:
|
||||
return Silk.NET.Vulkan.BlendFactor.ConstantColor;
|
||||
case GAL.BlendFactor.OneMinusConstantColor:
|
||||
return Silk.NET.Vulkan.BlendFactor.OneMinusConstantColor;
|
||||
case GAL.BlendFactor.ConstantAlpha:
|
||||
return Silk.NET.Vulkan.BlendFactor.ConstantAlpha;
|
||||
case GAL.BlendFactor.OneMinusConstantAlpha:
|
||||
return Silk.NET.Vulkan.BlendFactor.OneMinusConstantAlpha;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(GAL.BlendFactor)} enum value: {factor}.");
|
||||
|
||||
return Silk.NET.Vulkan.BlendFactor.Zero;
|
||||
}
|
||||
|
||||
public static Silk.NET.Vulkan.BlendOp Convert(this GAL.BlendOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case GAL.BlendOp.Add:
|
||||
case GAL.BlendOp.AddGl:
|
||||
return Silk.NET.Vulkan.BlendOp.Add;
|
||||
case GAL.BlendOp.Subtract:
|
||||
case GAL.BlendOp.SubtractGl:
|
||||
return Silk.NET.Vulkan.BlendOp.Subtract;
|
||||
case GAL.BlendOp.ReverseSubtract:
|
||||
case GAL.BlendOp.ReverseSubtractGl:
|
||||
return Silk.NET.Vulkan.BlendOp.ReverseSubtract;
|
||||
case GAL.BlendOp.Minimum:
|
||||
case GAL.BlendOp.MinimumGl:
|
||||
return Silk.NET.Vulkan.BlendOp.Min;
|
||||
case GAL.BlendOp.Maximum:
|
||||
case GAL.BlendOp.MaximumGl:
|
||||
return Silk.NET.Vulkan.BlendOp.Max;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(GAL.BlendOp)} enum value: {op}.");
|
||||
|
||||
return Silk.NET.Vulkan.BlendOp.Add;
|
||||
}
|
||||
|
||||
public static Silk.NET.Vulkan.CompareOp Convert(this GAL.CompareOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case GAL.CompareOp.Never:
|
||||
case GAL.CompareOp.NeverGl:
|
||||
return Silk.NET.Vulkan.CompareOp.Never;
|
||||
case GAL.CompareOp.Less:
|
||||
case GAL.CompareOp.LessGl:
|
||||
return Silk.NET.Vulkan.CompareOp.Less;
|
||||
case GAL.CompareOp.Equal:
|
||||
case GAL.CompareOp.EqualGl:
|
||||
return Silk.NET.Vulkan.CompareOp.Equal;
|
||||
case GAL.CompareOp.LessOrEqual:
|
||||
case GAL.CompareOp.LessOrEqualGl:
|
||||
return Silk.NET.Vulkan.CompareOp.LessOrEqual;
|
||||
case GAL.CompareOp.Greater:
|
||||
case GAL.CompareOp.GreaterGl:
|
||||
return Silk.NET.Vulkan.CompareOp.Greater;
|
||||
case GAL.CompareOp.NotEqual:
|
||||
case GAL.CompareOp.NotEqualGl:
|
||||
return Silk.NET.Vulkan.CompareOp.NotEqual;
|
||||
case GAL.CompareOp.GreaterOrEqual:
|
||||
case GAL.CompareOp.GreaterOrEqualGl:
|
||||
return Silk.NET.Vulkan.CompareOp.GreaterOrEqual;
|
||||
case GAL.CompareOp.Always:
|
||||
case GAL.CompareOp.AlwaysGl:
|
||||
return Silk.NET.Vulkan.CompareOp.Always;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(GAL.CompareOp)} enum value: {op}.");
|
||||
|
||||
return Silk.NET.Vulkan.CompareOp.Never;
|
||||
}
|
||||
|
||||
public static CullModeFlags Convert(this Face face)
|
||||
{
|
||||
switch (face)
|
||||
{
|
||||
case Face.Back:
|
||||
return CullModeFlags.CullModeBackBit;
|
||||
case Face.Front:
|
||||
return CullModeFlags.CullModeFrontBit;
|
||||
case Face.FrontAndBack:
|
||||
return CullModeFlags.CullModeFrontAndBack;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(Face)} enum value: {face}.");
|
||||
|
||||
return CullModeFlags.CullModeBackBit;
|
||||
}
|
||||
|
||||
public static Silk.NET.Vulkan.FrontFace Convert(this GAL.FrontFace frontFace)
|
||||
{
|
||||
// Flipped to account for origin differences.
|
||||
switch (frontFace)
|
||||
{
|
||||
case GAL.FrontFace.Clockwise:
|
||||
return Silk.NET.Vulkan.FrontFace.CounterClockwise;
|
||||
case GAL.FrontFace.CounterClockwise:
|
||||
return Silk.NET.Vulkan.FrontFace.Clockwise;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(GAL.FrontFace)} enum value: {frontFace}.");
|
||||
|
||||
return Silk.NET.Vulkan.FrontFace.Clockwise;
|
||||
}
|
||||
|
||||
public static Silk.NET.Vulkan.IndexType Convert(this GAL.IndexType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GAL.IndexType.UByte:
|
||||
return Silk.NET.Vulkan.IndexType.Uint8Ext;
|
||||
case GAL.IndexType.UShort:
|
||||
return Silk.NET.Vulkan.IndexType.Uint16;
|
||||
case GAL.IndexType.UInt:
|
||||
return Silk.NET.Vulkan.IndexType.Uint32;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(GAL.IndexType)} enum value: {type}.");
|
||||
|
||||
return Silk.NET.Vulkan.IndexType.Uint16;
|
||||
}
|
||||
|
||||
public static Filter Convert(this MagFilter filter)
|
||||
{
|
||||
switch (filter)
|
||||
{
|
||||
case MagFilter.Nearest:
|
||||
return Filter.Nearest;
|
||||
case MagFilter.Linear:
|
||||
return Filter.Linear;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(MagFilter)} enum value: {filter}.");
|
||||
|
||||
return Filter.Nearest;
|
||||
}
|
||||
|
||||
public static (Filter, SamplerMipmapMode) Convert(this MinFilter filter)
|
||||
{
|
||||
switch (filter)
|
||||
{
|
||||
case MinFilter.Nearest:
|
||||
return (Filter.Nearest, SamplerMipmapMode.Nearest);
|
||||
case MinFilter.Linear:
|
||||
return (Filter.Linear, SamplerMipmapMode.Nearest);
|
||||
case MinFilter.NearestMipmapNearest:
|
||||
return (Filter.Nearest, SamplerMipmapMode.Nearest);
|
||||
case MinFilter.LinearMipmapNearest:
|
||||
return (Filter.Linear, SamplerMipmapMode.Nearest);
|
||||
case MinFilter.NearestMipmapLinear:
|
||||
return (Filter.Nearest, SamplerMipmapMode.Linear);
|
||||
case MinFilter.LinearMipmapLinear:
|
||||
return (Filter.Linear, SamplerMipmapMode.Linear);
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(MinFilter)} enum value: {filter}.");
|
||||
|
||||
return (Filter.Nearest, SamplerMipmapMode.Nearest);
|
||||
}
|
||||
|
||||
public static Silk.NET.Vulkan.PrimitiveTopology Convert(this GAL.PrimitiveTopology topology)
|
||||
{
|
||||
switch (topology)
|
||||
{
|
||||
case GAL.PrimitiveTopology.Points:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.PointList;
|
||||
case GAL.PrimitiveTopology.Lines:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.LineList;
|
||||
case GAL.PrimitiveTopology.LineStrip:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.LineStrip;
|
||||
case GAL.PrimitiveTopology.Triangles:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.TriangleList;
|
||||
case GAL.PrimitiveTopology.TriangleStrip:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.TriangleStrip;
|
||||
case GAL.PrimitiveTopology.TriangleFan:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.TriangleFan;
|
||||
case GAL.PrimitiveTopology.LinesAdjacency:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.LineListWithAdjacency;
|
||||
case GAL.PrimitiveTopology.LineStripAdjacency:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.LineStripWithAdjacency;
|
||||
case GAL.PrimitiveTopology.TrianglesAdjacency:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency;
|
||||
case GAL.PrimitiveTopology.TriangleStripAdjacency:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency;
|
||||
case GAL.PrimitiveTopology.Patches:
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.PatchList;
|
||||
case GAL.PrimitiveTopology.Quads: // Emulated with triangle fans.
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.TriangleFan;
|
||||
case GAL.PrimitiveTopology.QuadStrip: // Emulated with triangle strips.
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.TriangleStrip;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(GAL.PrimitiveTopology)} enum value: {topology}.");
|
||||
|
||||
return Silk.NET.Vulkan.PrimitiveTopology.TriangleList;
|
||||
}
|
||||
|
||||
public static Silk.NET.Vulkan.StencilOp Convert(this GAL.StencilOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case GAL.StencilOp.Keep:
|
||||
return Silk.NET.Vulkan.StencilOp.Keep;
|
||||
case GAL.StencilOp.Zero:
|
||||
return Silk.NET.Vulkan.StencilOp.Zero;
|
||||
case GAL.StencilOp.Replace:
|
||||
return Silk.NET.Vulkan.StencilOp.Replace;
|
||||
case GAL.StencilOp.IncrementAndClamp:
|
||||
return Silk.NET.Vulkan.StencilOp.IncrementAndClamp;
|
||||
case GAL.StencilOp.DecrementAndClamp:
|
||||
return Silk.NET.Vulkan.StencilOp.DecrementAndClamp;
|
||||
case GAL.StencilOp.Invert:
|
||||
return Silk.NET.Vulkan.StencilOp.Invert;
|
||||
case GAL.StencilOp.IncrementAndWrap:
|
||||
return Silk.NET.Vulkan.StencilOp.IncrementAndWrap;
|
||||
case GAL.StencilOp.DecrementAndWrap:
|
||||
return Silk.NET.Vulkan.StencilOp.DecrementAndWrap;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(GAL.StencilOp)} enum value: {op}.");
|
||||
|
||||
return Silk.NET.Vulkan.StencilOp.Keep;
|
||||
}
|
||||
|
||||
public static ComponentSwizzle Convert(this SwizzleComponent swizzleComponent)
|
||||
{
|
||||
switch (swizzleComponent)
|
||||
{
|
||||
case SwizzleComponent.Zero:
|
||||
return ComponentSwizzle.Zero;
|
||||
case SwizzleComponent.One:
|
||||
return ComponentSwizzle.One;
|
||||
case SwizzleComponent.Red:
|
||||
return ComponentSwizzle.R;
|
||||
case SwizzleComponent.Green:
|
||||
return ComponentSwizzle.G;
|
||||
case SwizzleComponent.Blue:
|
||||
return ComponentSwizzle.B;
|
||||
case SwizzleComponent.Alpha:
|
||||
return ComponentSwizzle.A;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(SwizzleComponent)} enum value: {swizzleComponent}.");
|
||||
|
||||
return ComponentSwizzle.Zero;
|
||||
}
|
||||
|
||||
public static ImageType Convert(this Target target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case Target.Texture1D:
|
||||
case Target.Texture1DArray:
|
||||
case Target.TextureBuffer:
|
||||
return ImageType.ImageType1D;
|
||||
case Target.Texture2D:
|
||||
case Target.Texture2DArray:
|
||||
case Target.Texture2DMultisample:
|
||||
case Target.Cubemap:
|
||||
case Target.CubemapArray:
|
||||
return ImageType.ImageType2D;
|
||||
case Target.Texture3D:
|
||||
return ImageType.ImageType3D;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(Target)} enum value: {target}.");
|
||||
|
||||
return ImageType.ImageType2D;
|
||||
}
|
||||
|
||||
public static ImageViewType ConvertView(this Target target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case Target.Texture1D:
|
||||
return ImageViewType.ImageViewType1D;
|
||||
case Target.Texture2D:
|
||||
case Target.Texture2DMultisample:
|
||||
return ImageViewType.ImageViewType2D;
|
||||
case Target.Texture3D:
|
||||
return ImageViewType.ImageViewType3D;
|
||||
case Target.Texture1DArray:
|
||||
return ImageViewType.ImageViewType1DArray;
|
||||
case Target.Texture2DArray:
|
||||
return ImageViewType.ImageViewType2DArray;
|
||||
case Target.Cubemap:
|
||||
return ImageViewType.Cube;
|
||||
case Target.CubemapArray:
|
||||
return ImageViewType.CubeArray;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(Target)} enum value: {target}.");
|
||||
|
||||
return ImageViewType.ImageViewType2D;
|
||||
}
|
||||
|
||||
public static ImageAspectFlags ConvertAspectFlags(this GAL.Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case GAL.Format.D16Unorm:
|
||||
case GAL.Format.D32Float:
|
||||
return ImageAspectFlags.ImageAspectDepthBit;
|
||||
case GAL.Format.S8Uint:
|
||||
|
||||
return ImageAspectFlags.ImageAspectStencilBit;
|
||||
case GAL.Format.D24UnormS8Uint:
|
||||
case GAL.Format.D32FloatS8Uint:
|
||||
case GAL.Format.S8UintD24Unorm:
|
||||
return ImageAspectFlags.ImageAspectDepthBit | ImageAspectFlags.ImageAspectStencilBit;
|
||||
default:
|
||||
return ImageAspectFlags.ImageAspectColorBit;
|
||||
}
|
||||
}
|
||||
|
||||
public static ImageAspectFlags ConvertAspectFlags(this GAL.Format format, DepthStencilMode depthStencilMode)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case GAL.Format.D16Unorm:
|
||||
case GAL.Format.D32Float:
|
||||
return ImageAspectFlags.ImageAspectDepthBit;
|
||||
case GAL.Format.S8Uint:
|
||||
return ImageAspectFlags.ImageAspectStencilBit;
|
||||
case GAL.Format.D24UnormS8Uint:
|
||||
case GAL.Format.D32FloatS8Uint:
|
||||
case GAL.Format.S8UintD24Unorm:
|
||||
return depthStencilMode == DepthStencilMode.Stencil ? ImageAspectFlags.ImageAspectStencilBit : ImageAspectFlags.ImageAspectDepthBit;
|
||||
default:
|
||||
return ImageAspectFlags.ImageAspectColorBit;
|
||||
}
|
||||
}
|
||||
|
||||
public static LogicOp Convert(this LogicalOp op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case LogicalOp.Clear:
|
||||
return LogicOp.Clear;
|
||||
case LogicalOp.And:
|
||||
return LogicOp.And;
|
||||
case LogicalOp.AndReverse:
|
||||
return LogicOp.AndReverse;
|
||||
case LogicalOp.Copy:
|
||||
return LogicOp.Copy;
|
||||
case LogicalOp.AndInverted:
|
||||
return LogicOp.AndInverted;
|
||||
case LogicalOp.Noop:
|
||||
return LogicOp.NoOp;
|
||||
case LogicalOp.Xor:
|
||||
return LogicOp.Xor;
|
||||
case LogicalOp.Or:
|
||||
return LogicOp.Or;
|
||||
case LogicalOp.Nor:
|
||||
return LogicOp.Nor;
|
||||
case LogicalOp.Equiv:
|
||||
return LogicOp.Equivalent;
|
||||
case LogicalOp.Invert:
|
||||
return LogicOp.Invert;
|
||||
case LogicalOp.OrReverse:
|
||||
return LogicOp.OrReverse;
|
||||
case LogicalOp.CopyInverted:
|
||||
return LogicOp.CopyInverted;
|
||||
case LogicalOp.OrInverted:
|
||||
return LogicOp.OrInverted;
|
||||
case LogicalOp.Nand:
|
||||
return LogicOp.Nand;
|
||||
case LogicalOp.Set:
|
||||
return LogicOp.Set;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(LogicalOp)} enum value: {op}.");
|
||||
|
||||
return LogicOp.Copy;
|
||||
}
|
||||
}
|
||||
}
|
30
Ryujinx.Graphics.Vulkan/FenceHelper.cs
Normal file
30
Ryujinx.Graphics.Vulkan/FenceHelper.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
static class FenceHelper
|
||||
{
|
||||
private const ulong DefaultTimeout = 100000000; // 100ms
|
||||
|
||||
public static bool AnySignaled(Vk api, Device device, ReadOnlySpan<Fence> fences, ulong timeout = 0)
|
||||
{
|
||||
return api.WaitForFences(device, (uint)fences.Length, fences, false, timeout) == Result.Success;
|
||||
}
|
||||
|
||||
public static bool AllSignaled(Vk api, Device device, ReadOnlySpan<Fence> fences, ulong timeout = 0)
|
||||
{
|
||||
return api.WaitForFences(device, (uint)fences.Length, fences, true, timeout) == Result.Success;
|
||||
}
|
||||
|
||||
public static void WaitAllIndefinitely(Vk api, Device device, ReadOnlySpan<Fence> fences)
|
||||
{
|
||||
Result result;
|
||||
while ((result = api.WaitForFences(device, (uint)fences.Length, fences, true, DefaultTimeout)) == Result.Timeout)
|
||||
{
|
||||
// Keep waiting while the fence is not signaled.
|
||||
}
|
||||
result.ThrowOnError();
|
||||
}
|
||||
}
|
||||
}
|
73
Ryujinx.Graphics.Vulkan/FenceHolder.cs
Normal file
73
Ryujinx.Graphics.Vulkan/FenceHolder.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class FenceHolder : IDisposable
|
||||
{
|
||||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
private Fence _fence;
|
||||
private int _referenceCount;
|
||||
public bool _disposed;
|
||||
|
||||
public unsafe FenceHolder(Vk api, Device device)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
|
||||
var fenceCreateInfo = new FenceCreateInfo()
|
||||
{
|
||||
SType = StructureType.FenceCreateInfo
|
||||
};
|
||||
|
||||
api.CreateFence(device, in fenceCreateInfo, null, out _fence).ThrowOnError();
|
||||
|
||||
_referenceCount = 1;
|
||||
}
|
||||
|
||||
public Fence GetUnsafe()
|
||||
{
|
||||
return _fence;
|
||||
}
|
||||
|
||||
public Fence Get()
|
||||
{
|
||||
Interlocked.Increment(ref _referenceCount);
|
||||
return _fence;
|
||||
}
|
||||
|
||||
public unsafe void Put()
|
||||
{
|
||||
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
||||
{
|
||||
_api.DestroyFence(_device, _fence, null);
|
||||
_fence = default;
|
||||
}
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
Span<Fence> fences = stackalloc Fence[1];
|
||||
fences[0] = _fence;
|
||||
FenceHelper.WaitAllIndefinitely(_api, _device, fences);
|
||||
}
|
||||
|
||||
public bool IsSignaled()
|
||||
{
|
||||
Span<Fence> fences = stackalloc Fence[1];
|
||||
fences[0] = _fence;
|
||||
return FenceHelper.AllSignaled(_api, _device, fences);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
Put();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
75
Ryujinx.Graphics.Vulkan/FormatCapabilities.cs
Normal file
75
Ryujinx.Graphics.Vulkan/FormatCapabilities.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class FormatCapabilities
|
||||
{
|
||||
private readonly FormatFeatureFlags[] _table;
|
||||
|
||||
private readonly Vk _api;
|
||||
private readonly PhysicalDevice _physicalDevice;
|
||||
|
||||
public FormatCapabilities(Vk api, PhysicalDevice physicalDevice)
|
||||
{
|
||||
_api = api;
|
||||
_physicalDevice = physicalDevice;
|
||||
_table = new FormatFeatureFlags[Enum.GetNames(typeof(GAL.Format)).Length];
|
||||
}
|
||||
|
||||
public bool FormatSupports(GAL.Format format, FormatFeatureFlags flags)
|
||||
{
|
||||
var formatFeatureFlags = _table[(int)format];
|
||||
|
||||
if (formatFeatureFlags == 0)
|
||||
{
|
||||
_api.GetPhysicalDeviceFormatProperties(_physicalDevice, FormatTable.GetFormat(format), out var fp);
|
||||
formatFeatureFlags = fp.OptimalTilingFeatures;
|
||||
_table[(int)format] = formatFeatureFlags;
|
||||
}
|
||||
|
||||
return (formatFeatureFlags & flags) == flags;
|
||||
}
|
||||
|
||||
public VkFormat ConvertToVkFormat(GAL.Format srcFormat)
|
||||
{
|
||||
var format = FormatTable.GetFormat(srcFormat);
|
||||
|
||||
var requiredFeatures = FormatFeatureFlags.FormatFeatureSampledImageBit |
|
||||
FormatFeatureFlags.FormatFeatureTransferSrcBit |
|
||||
FormatFeatureFlags.FormatFeatureTransferDstBit;
|
||||
|
||||
if (srcFormat.IsDepthOrStencil())
|
||||
{
|
||||
requiredFeatures |= FormatFeatureFlags.FormatFeatureDepthStencilAttachmentBit;
|
||||
}
|
||||
else if (srcFormat.IsRtColorCompatible())
|
||||
{
|
||||
requiredFeatures |= FormatFeatureFlags.FormatFeatureColorAttachmentBit;
|
||||
}
|
||||
|
||||
if (srcFormat.IsImageCompatible())
|
||||
{
|
||||
requiredFeatures |= FormatFeatureFlags.FormatFeatureStorageImageBit;
|
||||
}
|
||||
|
||||
if (!FormatSupports(srcFormat, requiredFeatures))
|
||||
{
|
||||
// The format is not supported. Can we convert it to a higher precision format?
|
||||
if (srcFormat == GAL.Format.D24UnormS8Uint)
|
||||
{
|
||||
format = VkFormat.D32SfloatS8Uint;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Gpu, $"Format {srcFormat} is not supported by the host.");
|
||||
}
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
}
|
||||
}
|
49
Ryujinx.Graphics.Vulkan/FormatConverter.cs
Normal file
49
Ryujinx.Graphics.Vulkan/FormatConverter.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class FormatConverter
|
||||
{
|
||||
public unsafe static void ConvertD24S8ToD32FS8(Span<byte> output, ReadOnlySpan<byte> input)
|
||||
{
|
||||
const float UnormToFloat = 1f / 0xffffff;
|
||||
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
|
||||
ReadOnlySpan<uint> inputUint = MemoryMarshal.Cast<byte, uint>(input);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < inputUint.Length; i++)
|
||||
{
|
||||
uint depthStencil = inputUint[i];
|
||||
uint depth = depthStencil >> 8;
|
||||
uint stencil = depthStencil & 0xff;
|
||||
|
||||
int j = i * 2;
|
||||
|
||||
outputUint[j] = (uint)BitConverter.SingleToInt32Bits(depth * UnormToFloat);
|
||||
outputUint[j + 1] = stencil;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe static void ConvertD32FS8ToD24S8(Span<byte> output, ReadOnlySpan<byte> input)
|
||||
{
|
||||
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
|
||||
ReadOnlySpan<uint> inputUint = MemoryMarshal.Cast<byte, uint>(input);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < inputUint.Length; i += 2)
|
||||
{
|
||||
float depth = BitConverter.Int32BitsToSingle((int)inputUint[i]);
|
||||
uint stencil = inputUint[i + 1];
|
||||
uint depthStencil = (Math.Clamp((uint)(depth * 0xffffff), 0, 0xffffff) << 8) | (stencil & 0xff);
|
||||
|
||||
int j = i >> 1;
|
||||
|
||||
outputUint[j] = depthStencil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
181
Ryujinx.Graphics.Vulkan/FormatTable.cs
Normal file
181
Ryujinx.Graphics.Vulkan/FormatTable.cs
Normal file
|
@ -0,0 +1,181 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
static class FormatTable
|
||||
{
|
||||
private static readonly VkFormat[] Table;
|
||||
|
||||
static FormatTable()
|
||||
{
|
||||
Table = new VkFormat[Enum.GetNames(typeof(Format)).Length];
|
||||
|
||||
Add(Format.R8Unorm, VkFormat.R8Unorm);
|
||||
Add(Format.R8Snorm, VkFormat.R8SNorm);
|
||||
Add(Format.R8Uint, VkFormat.R8Uint);
|
||||
Add(Format.R8Sint, VkFormat.R8Sint);
|
||||
Add(Format.R16Float, VkFormat.R16Sfloat);
|
||||
Add(Format.R16Unorm, VkFormat.R16Unorm);
|
||||
Add(Format.R16Snorm, VkFormat.R16SNorm);
|
||||
Add(Format.R16Uint, VkFormat.R16Uint);
|
||||
Add(Format.R16Sint, VkFormat.R16Sint);
|
||||
Add(Format.R32Float, VkFormat.R32Sfloat);
|
||||
Add(Format.R32Uint, VkFormat.R32Uint);
|
||||
Add(Format.R32Sint, VkFormat.R32Sint);
|
||||
Add(Format.R8G8Unorm, VkFormat.R8G8Unorm);
|
||||
Add(Format.R8G8Snorm, VkFormat.R8G8SNorm);
|
||||
Add(Format.R8G8Uint, VkFormat.R8G8Uint);
|
||||
Add(Format.R8G8Sint, VkFormat.R8G8Sint);
|
||||
Add(Format.R16G16Float, VkFormat.R16G16Sfloat);
|
||||
Add(Format.R16G16Unorm, VkFormat.R16G16Unorm);
|
||||
Add(Format.R16G16Snorm, VkFormat.R16G16SNorm);
|
||||
Add(Format.R16G16Uint, VkFormat.R16G16Uint);
|
||||
Add(Format.R16G16Sint, VkFormat.R16G16Sint);
|
||||
Add(Format.R32G32Float, VkFormat.R32G32Sfloat);
|
||||
Add(Format.R32G32Uint, VkFormat.R32G32Uint);
|
||||
Add(Format.R32G32Sint, VkFormat.R32G32Sint);
|
||||
Add(Format.R8G8B8Unorm, VkFormat.R8G8B8Unorm);
|
||||
Add(Format.R8G8B8Snorm, VkFormat.R8G8B8SNorm);
|
||||
Add(Format.R8G8B8Uint, VkFormat.R8G8B8Uint);
|
||||
Add(Format.R8G8B8Sint, VkFormat.R8G8B8Sint);
|
||||
Add(Format.R16G16B16Float, VkFormat.R16G16B16Sfloat);
|
||||
Add(Format.R16G16B16Unorm, VkFormat.R16G16B16Unorm);
|
||||
Add(Format.R16G16B16Snorm, VkFormat.R16G16B16SNorm);
|
||||
Add(Format.R16G16B16Uint, VkFormat.R16G16B16Uint);
|
||||
Add(Format.R16G16B16Sint, VkFormat.R16G16B16Sint);
|
||||
Add(Format.R32G32B32Float, VkFormat.R32G32B32Sfloat);
|
||||
Add(Format.R32G32B32Uint, VkFormat.R32G32B32Uint);
|
||||
Add(Format.R32G32B32Sint, VkFormat.R32G32B32Sint);
|
||||
Add(Format.R8G8B8A8Unorm, VkFormat.R8G8B8A8Unorm);
|
||||
Add(Format.R8G8B8A8Snorm, VkFormat.R8G8B8A8SNorm);
|
||||
Add(Format.R8G8B8A8Uint, VkFormat.R8G8B8A8Uint);
|
||||
Add(Format.R8G8B8A8Sint, VkFormat.R8G8B8A8Sint);
|
||||
Add(Format.R16G16B16A16Float, VkFormat.R16G16B16A16Sfloat);
|
||||
Add(Format.R16G16B16A16Unorm, VkFormat.R16G16B16A16Unorm);
|
||||
Add(Format.R16G16B16A16Snorm, VkFormat.R16G16B16A16SNorm);
|
||||
Add(Format.R16G16B16A16Uint, VkFormat.R16G16B16A16Uint);
|
||||
Add(Format.R16G16B16A16Sint, VkFormat.R16G16B16A16Sint);
|
||||
Add(Format.R32G32B32A32Float, VkFormat.R32G32B32A32Sfloat);
|
||||
Add(Format.R32G32B32A32Uint, VkFormat.R32G32B32A32Uint);
|
||||
Add(Format.R32G32B32A32Sint, VkFormat.R32G32B32A32Sint);
|
||||
Add(Format.S8Uint, VkFormat.S8Uint);
|
||||
Add(Format.D16Unorm, VkFormat.D16Unorm);
|
||||
Add(Format.S8UintD24Unorm, VkFormat.D24UnormS8Uint);
|
||||
Add(Format.D32Float, VkFormat.D32Sfloat);
|
||||
Add(Format.D24UnormS8Uint, VkFormat.D24UnormS8Uint);
|
||||
Add(Format.D32FloatS8Uint, VkFormat.D32SfloatS8Uint);
|
||||
Add(Format.R8G8B8X8Srgb, VkFormat.R8G8B8Srgb);
|
||||
Add(Format.R8G8B8A8Srgb, VkFormat.R8G8B8A8Srgb);
|
||||
Add(Format.R4G4B4A4Unorm, VkFormat.R4G4B4A4UnormPack16);
|
||||
Add(Format.R5G5B5X1Unorm, VkFormat.A1R5G5B5UnormPack16);
|
||||
Add(Format.R5G5B5A1Unorm, VkFormat.A1R5G5B5UnormPack16);
|
||||
Add(Format.R5G6B5Unorm, VkFormat.R5G6B5UnormPack16);
|
||||
Add(Format.R10G10B10A2Unorm, VkFormat.A2B10G10R10UnormPack32);
|
||||
Add(Format.R10G10B10A2Uint, VkFormat.A2B10G10R10UintPack32);
|
||||
Add(Format.R11G11B10Float, VkFormat.B10G11R11UfloatPack32);
|
||||
Add(Format.R9G9B9E5Float, VkFormat.E5B9G9R9UfloatPack32);
|
||||
Add(Format.Bc1RgbaUnorm, VkFormat.BC1RgbaUnormBlock);
|
||||
Add(Format.Bc2Unorm, VkFormat.BC2UnormBlock);
|
||||
Add(Format.Bc3Unorm, VkFormat.BC3UnormBlock);
|
||||
Add(Format.Bc1RgbaSrgb, VkFormat.BC1RgbaSrgbBlock);
|
||||
Add(Format.Bc2Srgb, VkFormat.BC2SrgbBlock);
|
||||
Add(Format.Bc3Srgb, VkFormat.BC3SrgbBlock);
|
||||
Add(Format.Bc4Unorm, VkFormat.BC4UnormBlock);
|
||||
Add(Format.Bc4Snorm, VkFormat.BC4SNormBlock);
|
||||
Add(Format.Bc5Unorm, VkFormat.BC5UnormBlock);
|
||||
Add(Format.Bc5Snorm, VkFormat.BC5SNormBlock);
|
||||
Add(Format.Bc7Unorm, VkFormat.BC7UnormBlock);
|
||||
Add(Format.Bc7Srgb, VkFormat.BC7SrgbBlock);
|
||||
Add(Format.Bc6HSfloat, VkFormat.BC6HSfloatBlock);
|
||||
Add(Format.Bc6HUfloat, VkFormat.BC6HUfloatBlock);
|
||||
Add(Format.R8Uscaled, VkFormat.R8Uscaled);
|
||||
Add(Format.R8Sscaled, VkFormat.R8Sscaled);
|
||||
Add(Format.R16Uscaled, VkFormat.R16Uscaled);
|
||||
Add(Format.R16Sscaled, VkFormat.R16Sscaled);
|
||||
// Add(Format.R32Uscaled, VkFormat.R32Uscaled);
|
||||
// Add(Format.R32Sscaled, VkFormat.R32Sscaled);
|
||||
Add(Format.R8G8Uscaled, VkFormat.R8G8Uscaled);
|
||||
Add(Format.R8G8Sscaled, VkFormat.R8G8Sscaled);
|
||||
Add(Format.R16G16Uscaled, VkFormat.R16G16Uscaled);
|
||||
Add(Format.R16G16Sscaled, VkFormat.R16G16Sscaled);
|
||||
// Add(Format.R32G32Uscaled, VkFormat.R32G32Uscaled);
|
||||
// Add(Format.R32G32Sscaled, VkFormat.R32G32Sscaled);
|
||||
Add(Format.R8G8B8Uscaled, VkFormat.R8G8B8Uscaled);
|
||||
Add(Format.R8G8B8Sscaled, VkFormat.R8G8B8Sscaled);
|
||||
Add(Format.R16G16B16Uscaled, VkFormat.R16G16B16Uscaled);
|
||||
Add(Format.R16G16B16Sscaled, VkFormat.R16G16B16Sscaled);
|
||||
// Add(Format.R32G32B32Uscaled, VkFormat.R32G32B32Uscaled);
|
||||
// Add(Format.R32G32B32Sscaled, VkFormat.R32G32B32Sscaled);
|
||||
Add(Format.R8G8B8A8Uscaled, VkFormat.R8G8B8A8Uscaled);
|
||||
Add(Format.R8G8B8A8Sscaled, VkFormat.R8G8B8A8Sscaled);
|
||||
Add(Format.R16G16B16A16Uscaled, VkFormat.R16G16B16A16Uscaled);
|
||||
Add(Format.R16G16B16A16Sscaled, VkFormat.R16G16B16A16Sscaled);
|
||||
// Add(Format.R32G32B32A32Uscaled, VkFormat.R32G32B32A32Uscaled);
|
||||
// Add(Format.R32G32B32A32Sscaled, VkFormat.R32G32B32A32Sscaled);
|
||||
Add(Format.R10G10B10A2Snorm, VkFormat.A2B10G10R10SNormPack32);
|
||||
Add(Format.R10G10B10A2Sint, VkFormat.A2B10G10R10SintPack32);
|
||||
Add(Format.R10G10B10A2Uscaled, VkFormat.A2B10G10R10UscaledPack32);
|
||||
Add(Format.R10G10B10A2Sscaled, VkFormat.A2B10G10R10SscaledPack32);
|
||||
Add(Format.R8G8B8X8Unorm, VkFormat.R8G8B8Unorm);
|
||||
Add(Format.R8G8B8X8Snorm, VkFormat.R8G8B8SNorm);
|
||||
Add(Format.R8G8B8X8Uint, VkFormat.R8G8B8Uint);
|
||||
Add(Format.R8G8B8X8Sint, VkFormat.R8G8B8Sint);
|
||||
Add(Format.R16G16B16X16Float, VkFormat.R16G16B16Sfloat);
|
||||
Add(Format.R16G16B16X16Unorm, VkFormat.R16G16B16Unorm);
|
||||
Add(Format.R16G16B16X16Snorm, VkFormat.R16G16B16SNorm);
|
||||
Add(Format.R16G16B16X16Uint, VkFormat.R16G16B16Uint);
|
||||
Add(Format.R16G16B16X16Sint, VkFormat.R16G16B16Sint);
|
||||
Add(Format.R32G32B32X32Float, VkFormat.R32G32B32Sfloat);
|
||||
Add(Format.R32G32B32X32Uint, VkFormat.R32G32B32Uint);
|
||||
Add(Format.R32G32B32X32Sint, VkFormat.R32G32B32Sint);
|
||||
Add(Format.Astc4x4Unorm, VkFormat.Astc4x4UnormBlock);
|
||||
Add(Format.Astc5x4Unorm, VkFormat.Astc5x4UnormBlock);
|
||||
Add(Format.Astc5x5Unorm, VkFormat.Astc5x5UnormBlock);
|
||||
Add(Format.Astc6x5Unorm, VkFormat.Astc6x5UnormBlock);
|
||||
Add(Format.Astc6x6Unorm, VkFormat.Astc6x6UnormBlock);
|
||||
Add(Format.Astc8x5Unorm, VkFormat.Astc8x5UnormBlock);
|
||||
Add(Format.Astc8x6Unorm, VkFormat.Astc8x6UnormBlock);
|
||||
Add(Format.Astc8x8Unorm, VkFormat.Astc8x8UnormBlock);
|
||||
Add(Format.Astc10x5Unorm, VkFormat.Astc10x5UnormBlock);
|
||||
Add(Format.Astc10x6Unorm, VkFormat.Astc10x6UnormBlock);
|
||||
Add(Format.Astc10x8Unorm, VkFormat.Astc10x8UnormBlock);
|
||||
Add(Format.Astc10x10Unorm, VkFormat.Astc10x10UnormBlock);
|
||||
Add(Format.Astc12x10Unorm, VkFormat.Astc12x10UnormBlock);
|
||||
Add(Format.Astc12x12Unorm, VkFormat.Astc12x12UnormBlock);
|
||||
Add(Format.Astc4x4Srgb, VkFormat.Astc4x4SrgbBlock);
|
||||
Add(Format.Astc5x4Srgb, VkFormat.Astc5x4SrgbBlock);
|
||||
Add(Format.Astc5x5Srgb, VkFormat.Astc5x5SrgbBlock);
|
||||
Add(Format.Astc6x5Srgb, VkFormat.Astc6x5SrgbBlock);
|
||||
Add(Format.Astc6x6Srgb, VkFormat.Astc6x6SrgbBlock);
|
||||
Add(Format.Astc8x5Srgb, VkFormat.Astc8x5SrgbBlock);
|
||||
Add(Format.Astc8x6Srgb, VkFormat.Astc8x6SrgbBlock);
|
||||
Add(Format.Astc8x8Srgb, VkFormat.Astc8x8SrgbBlock);
|
||||
Add(Format.Astc10x5Srgb, VkFormat.Astc10x5SrgbBlock);
|
||||
Add(Format.Astc10x6Srgb, VkFormat.Astc10x6SrgbBlock);
|
||||
Add(Format.Astc10x8Srgb, VkFormat.Astc10x8SrgbBlock);
|
||||
Add(Format.Astc10x10Srgb, VkFormat.Astc10x10SrgbBlock);
|
||||
Add(Format.Astc12x10Srgb, VkFormat.Astc12x10SrgbBlock);
|
||||
Add(Format.Astc12x12Srgb, VkFormat.Astc12x12SrgbBlock);
|
||||
Add(Format.B5G6R5Unorm, VkFormat.R5G6B5UnormPack16);
|
||||
Add(Format.B5G5R5X1Unorm, VkFormat.A1R5G5B5UnormPack16);
|
||||
Add(Format.B5G5R5A1Unorm, VkFormat.A1R5G5B5UnormPack16);
|
||||
Add(Format.A1B5G5R5Unorm, VkFormat.R5G5B5A1UnormPack16);
|
||||
Add(Format.B8G8R8X8Unorm, VkFormat.B8G8R8Unorm);
|
||||
Add(Format.B8G8R8A8Unorm, VkFormat.B8G8R8A8Unorm);
|
||||
Add(Format.B8G8R8X8Srgb, VkFormat.B8G8R8Srgb);
|
||||
Add(Format.B8G8R8A8Srgb, VkFormat.B8G8R8A8Srgb);
|
||||
}
|
||||
|
||||
private static void Add(Format format, VkFormat vkFormat)
|
||||
{
|
||||
Table[(int)format] = vkFormat;
|
||||
}
|
||||
|
||||
public static VkFormat GetFormat(Format format)
|
||||
{
|
||||
return Table[(int)format];
|
||||
}
|
||||
}
|
||||
}
|
153
Ryujinx.Graphics.Vulkan/FramebufferParams.cs
Normal file
153
Ryujinx.Graphics.Vulkan/FramebufferParams.cs
Normal file
|
@ -0,0 +1,153 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class FramebufferParams
|
||||
{
|
||||
private readonly Device _device;
|
||||
private readonly Auto<DisposableImageView>[] _attachments;
|
||||
|
||||
public uint Width { get; }
|
||||
public uint Height { get; }
|
||||
public uint Layers { get; }
|
||||
|
||||
public VkFormat[] AttachmentFormats { get; }
|
||||
public int[] AttachmentIndices { get; }
|
||||
|
||||
public int AttachmentsCount { get; }
|
||||
public int MaxColorAttachmentIndex { get; }
|
||||
public bool HasDepthStencil { get; }
|
||||
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
|
||||
|
||||
public FramebufferParams(
|
||||
Device device,
|
||||
Auto<DisposableImageView> view,
|
||||
uint width,
|
||||
uint height,
|
||||
bool isDepthStencil,
|
||||
VkFormat format)
|
||||
{
|
||||
_device = device;
|
||||
_attachments = new[] { view };
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
Layers = 1;
|
||||
|
||||
AttachmentFormats = new[] { format };
|
||||
AttachmentIndices = new[] { 0 };
|
||||
|
||||
AttachmentsCount = 1;
|
||||
|
||||
HasDepthStencil = isDepthStencil;
|
||||
}
|
||||
|
||||
public FramebufferParams(Device device, ITexture[] colors, ITexture depthStencil)
|
||||
{
|
||||
_device = device;
|
||||
|
||||
int colorsCount = colors.Count(IsValidTextureView);
|
||||
|
||||
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
|
||||
|
||||
_attachments = new Auto<DisposableImageView>[count];
|
||||
|
||||
AttachmentFormats = new VkFormat[count];
|
||||
AttachmentIndices = new int[count];
|
||||
MaxColorAttachmentIndex = colors.Length - 1;
|
||||
|
||||
uint width = uint.MaxValue;
|
||||
uint height = uint.MaxValue;
|
||||
uint layers = uint.MaxValue;
|
||||
|
||||
int index = 0;
|
||||
int bindIndex = 0;
|
||||
|
||||
foreach (ITexture color in colors)
|
||||
{
|
||||
if (IsValidTextureView(color))
|
||||
{
|
||||
var texture = (TextureView)color;
|
||||
|
||||
_attachments[index] = texture.GetImageViewForAttachment();
|
||||
|
||||
AttachmentFormats[index] = texture.VkFormat;
|
||||
AttachmentIndices[index] = bindIndex;
|
||||
|
||||
width = Math.Min(width, (uint)texture.Width);
|
||||
height = Math.Min(height, (uint)texture.Height);
|
||||
layers = Math.Min(layers, (uint)texture.Layers);
|
||||
|
||||
if (++index >= colorsCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bindIndex++;
|
||||
}
|
||||
|
||||
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
|
||||
{
|
||||
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||
|
||||
AttachmentFormats[count - 1] = dsTexture.VkFormat;
|
||||
|
||||
width = Math.Min(width, (uint)dsTexture.Width);
|
||||
height = Math.Min(height, (uint)dsTexture.Height);
|
||||
layers = Math.Min(layers, (uint)dsTexture.Layers);
|
||||
|
||||
HasDepthStencil = true;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
width = height = layers = 1;
|
||||
}
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
Layers = layers;
|
||||
|
||||
AttachmentsCount = count;
|
||||
}
|
||||
|
||||
private static bool IsValidTextureView(ITexture texture)
|
||||
{
|
||||
return texture is TextureView view && view.Valid;
|
||||
}
|
||||
|
||||
public ClearRect GetClearRect()
|
||||
{
|
||||
return new ClearRect(new Rect2D(null, new Extent2D(Width, Height)), 0, Layers);
|
||||
}
|
||||
|
||||
public unsafe Auto<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)
|
||||
{
|
||||
ImageView* attachments = stackalloc ImageView[_attachments.Length];
|
||||
|
||||
for (int i = 0; i < _attachments.Length; i++)
|
||||
{
|
||||
attachments[i] = _attachments[i].Get(cbs).Value;
|
||||
}
|
||||
|
||||
var framebufferCreateInfo = new FramebufferCreateInfo()
|
||||
{
|
||||
SType = StructureType.FramebufferCreateInfo,
|
||||
RenderPass = renderPass.Get(cbs).Value,
|
||||
AttachmentCount = (uint)_attachments.Length,
|
||||
PAttachments = attachments,
|
||||
Width = Width,
|
||||
Height = Height,
|
||||
Layers = Layers
|
||||
};
|
||||
|
||||
api.CreateFramebuffer(_device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
||||
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
Normal file
16
Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
struct HardwareCapabilities
|
||||
{
|
||||
public bool SupportsConditionalRendering { get; }
|
||||
public bool SupportsExtendedDynamicState { get; }
|
||||
|
||||
public HardwareCapabilities(
|
||||
bool supportsConditionalRendering,
|
||||
bool supportsExtendedDynamicState)
|
||||
{
|
||||
SupportsConditionalRendering = supportsConditionalRendering;
|
||||
SupportsExtendedDynamicState = supportsExtendedDynamicState;
|
||||
}
|
||||
}
|
||||
}
|
117
Ryujinx.Graphics.Vulkan/HashTableSlim.cs
Normal file
117
Ryujinx.Graphics.Vulkan/HashTableSlim.cs
Normal file
|
@ -0,0 +1,117 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
interface IRefEquatable<T>
|
||||
{
|
||||
bool Equals(ref T other);
|
||||
}
|
||||
|
||||
class HashTableSlim<K, V> where K : IRefEquatable<K>
|
||||
{
|
||||
private const int TotalBuckets = 16; // Must be power of 2
|
||||
private const int TotalBucketsMask = TotalBuckets - 1;
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
public K Key;
|
||||
public V Value;
|
||||
}
|
||||
|
||||
private readonly Entry[][] _hashTable = new Entry[TotalBuckets][];
|
||||
|
||||
public IEnumerable<K> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (Entry[] bucket in _hashTable)
|
||||
{
|
||||
foreach (Entry entry in bucket)
|
||||
{
|
||||
yield return entry.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<V> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (Entry[] bucket in _hashTable)
|
||||
{
|
||||
if (bucket != null)
|
||||
{
|
||||
foreach (Entry entry in bucket)
|
||||
{
|
||||
yield return entry.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(ref K key, V value)
|
||||
{
|
||||
var entry = new Entry()
|
||||
{
|
||||
Key = key,
|
||||
Value = value
|
||||
};
|
||||
|
||||
int hashCode = key.GetHashCode();
|
||||
int bucketIndex = hashCode & TotalBucketsMask;
|
||||
|
||||
var bucket = _hashTable[bucketIndex];
|
||||
if (bucket != null)
|
||||
{
|
||||
int index = bucket.Length;
|
||||
|
||||
Array.Resize(ref _hashTable[bucketIndex], index + 1);
|
||||
|
||||
_hashTable[bucketIndex][index] = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
_hashTable[bucketIndex] = new Entry[]
|
||||
{
|
||||
entry
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(ref K key, out V value)
|
||||
{
|
||||
int hashCode = key.GetHashCode();
|
||||
|
||||
/* for (int i = 0; i < _hashTable.Length; i++)
|
||||
{
|
||||
var b = _hashTable[i];
|
||||
if (b != null)
|
||||
{
|
||||
System.Console.WriteLine(typeof(K).Name + " " + i + " " + b.Length);
|
||||
}
|
||||
} */
|
||||
|
||||
var bucket = _hashTable[hashCode & TotalBucketsMask];
|
||||
if (bucket != null)
|
||||
{
|
||||
|
||||
for (int i = 0; i < bucket.Length; i++)
|
||||
{
|
||||
ref var entry = ref bucket[i];
|
||||
|
||||
if (entry.Key.Equals(ref key))
|
||||
{
|
||||
value = entry.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
261
Ryujinx.Graphics.Vulkan/HelperShader.cs
Normal file
261
Ryujinx.Graphics.Vulkan/HelperShader.cs
Normal file
|
@ -0,0 +1,261 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class HelperShader : IDisposable
|
||||
{
|
||||
private const string VertexShaderSource = @"#version 450 core
|
||||
|
||||
layout (std140, binding = 1) uniform tex_coord_in
|
||||
{
|
||||
vec4 tex_coord_in_data;
|
||||
};
|
||||
|
||||
layout (location = 0) out vec2 tex_coord;
|
||||
|
||||
void main()
|
||||
{
|
||||
int low = gl_VertexIndex & 1;
|
||||
int high = gl_VertexIndex >> 1;
|
||||
tex_coord.x = tex_coord_in_data[low];
|
||||
tex_coord.y = tex_coord_in_data[2 + high];
|
||||
gl_Position.x = (float(low) - 0.5f) * 2.0f;
|
||||
gl_Position.y = (float(high) - 0.5f) * 2.0f;
|
||||
gl_Position.z = 0.0f;
|
||||
gl_Position.w = 1.0f;
|
||||
}";
|
||||
|
||||
private const string ColorBlitFragmentShaderSource = @"#version 450 core
|
||||
|
||||
layout (binding = 32, set = 2) uniform sampler2D tex;
|
||||
|
||||
layout (location = 0) in vec2 tex_coord;
|
||||
layout (location = 0) out vec4 colour;
|
||||
|
||||
void main()
|
||||
{
|
||||
colour = texture(tex, tex_coord);
|
||||
}";
|
||||
|
||||
private const string ClearAlphaFragmentShaderSource = @"#version 450 core
|
||||
|
||||
layout (binding = 32, set = 2) uniform sampler2D tex;
|
||||
|
||||
layout (location = 0) in vec2 tex_coord;
|
||||
layout (location = 0) out vec4 colour;
|
||||
|
||||
void main()
|
||||
{
|
||||
colour = vec4(texture(tex, tex_coord).rgb, 1.0f);
|
||||
}";
|
||||
|
||||
private readonly PipelineBlit _pipeline;
|
||||
private readonly ISampler _samplerLinear;
|
||||
private readonly ISampler _samplerNearest;
|
||||
private readonly IProgram _programColorBlit;
|
||||
private readonly IProgram _programClearAlpha;
|
||||
|
||||
public HelperShader(VulkanGraphicsDevice gd, Device device)
|
||||
{
|
||||
_pipeline = new PipelineBlit(gd, device);
|
||||
|
||||
static GAL.SamplerCreateInfo GetSamplerCreateInfo(MinFilter minFilter, MagFilter magFilter)
|
||||
{
|
||||
return new GAL.SamplerCreateInfo(
|
||||
minFilter,
|
||||
magFilter,
|
||||
false,
|
||||
AddressMode.ClampToEdge,
|
||||
AddressMode.ClampToEdge,
|
||||
AddressMode.ClampToEdge,
|
||||
CompareMode.None,
|
||||
GAL.CompareOp.Always,
|
||||
new ColorF(0f, 0f, 0f, 0f),
|
||||
0f,
|
||||
0f,
|
||||
0f,
|
||||
1f);
|
||||
}
|
||||
|
||||
_samplerLinear = gd.CreateSampler(GetSamplerCreateInfo(MinFilter.Linear, MagFilter.Linear));
|
||||
_samplerNearest = gd.CreateSampler(GetSamplerCreateInfo(MinFilter.Nearest, MagFilter.Nearest));
|
||||
|
||||
var vertexBindings = new ShaderBindings(
|
||||
new[] { 1 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
var fragmentBindings = new ShaderBindings(
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
new[] { 32 },
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>(),
|
||||
Array.Empty<int>());
|
||||
|
||||
var vertexShader = gd.CompileShader(ShaderStage.Vertex, vertexBindings, VertexShaderSource);
|
||||
var fragmentShaderColorBlit = gd.CompileShader(ShaderStage.Fragment, fragmentBindings, ColorBlitFragmentShaderSource);
|
||||
var fragmentShaderClearAlpha = gd.CompileShader(ShaderStage.Fragment, fragmentBindings, ClearAlphaFragmentShaderSource);
|
||||
|
||||
_programColorBlit = gd.CreateProgram(new[] { vertexShader, fragmentShaderColorBlit }, new ShaderInfo(-1));
|
||||
_programClearAlpha = gd.CreateProgram(new[] { vertexShader, fragmentShaderClearAlpha }, new ShaderInfo(-1));
|
||||
}
|
||||
|
||||
public void Blit(
|
||||
VulkanGraphicsDevice gd,
|
||||
TextureView src,
|
||||
Auto<DisposableImageView> dst,
|
||||
int dstWidth,
|
||||
int dstHeight,
|
||||
VkFormat dstFormat,
|
||||
Extents2D srcRegion,
|
||||
Extents2D dstRegion,
|
||||
bool linearFilter,
|
||||
bool clearAlpha = false)
|
||||
{
|
||||
gd.FlushAllCommands();
|
||||
|
||||
using var cbs = gd.CommandBufferPool.Rent();
|
||||
|
||||
Blit(gd, cbs, src, dst, dstWidth, dstHeight, dstFormat, srcRegion, dstRegion, linearFilter, clearAlpha);
|
||||
}
|
||||
|
||||
public void Blit(
|
||||
VulkanGraphicsDevice gd,
|
||||
CommandBufferScoped cbs,
|
||||
TextureView src,
|
||||
Auto<DisposableImageView> dst,
|
||||
int dstWidth,
|
||||
int dstHeight,
|
||||
VkFormat dstFormat,
|
||||
Extents2D srcRegion,
|
||||
Extents2D dstRegion,
|
||||
bool linearFilter,
|
||||
bool clearAlpha = false)
|
||||
{
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
|
||||
const int RegionBufferSize = 16;
|
||||
|
||||
var sampler = linearFilter ? _samplerLinear : _samplerNearest;
|
||||
|
||||
_pipeline.SetTextureAndSampler(32, src, sampler);
|
||||
|
||||
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
|
||||
|
||||
region[0] = (float)srcRegion.X1 / src.Width;
|
||||
region[1] = (float)srcRegion.X2 / src.Width;
|
||||
region[2] = (float)srcRegion.Y1 / src.Height;
|
||||
region[3] = (float)srcRegion.Y2 / src.Height;
|
||||
|
||||
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, RegionBufferSize);
|
||||
|
||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||
|
||||
Span<BufferRange> bufferRanges = stackalloc BufferRange[1];
|
||||
|
||||
bufferRanges[0] = new BufferRange(bufferHandle, 0, RegionBufferSize);
|
||||
|
||||
_pipeline.SetUniformBuffers(1, bufferRanges);
|
||||
|
||||
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||
|
||||
viewports[0] = new GAL.Viewport(
|
||||
new Rectangle<float>(dstRegion.X1, dstRegion.Y1, dstRegion.X2 - dstRegion.X1, dstRegion.Y2 - dstRegion.Y1),
|
||||
ViewportSwizzle.PositiveX,
|
||||
ViewportSwizzle.PositiveY,
|
||||
ViewportSwizzle.PositiveZ,
|
||||
ViewportSwizzle.PositiveW,
|
||||
0f,
|
||||
1f);
|
||||
|
||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
||||
|
||||
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
||||
|
||||
_pipeline.SetProgram(clearAlpha ? _programClearAlpha : _programColorBlit);
|
||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
|
||||
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
|
||||
|
||||
if (clearAlpha)
|
||||
{
|
||||
_pipeline.ClearRenderTargetColor(0, 0xf, new ColorF(0f, 0f, 0f, 1f));
|
||||
}
|
||||
|
||||
_pipeline.SetViewports(0, viewports);
|
||||
_pipeline.SetScissors(scissors);
|
||||
_pipeline.SetPrimitiveTopology(GAL.PrimitiveTopology.TriangleStrip);
|
||||
_pipeline.Draw(4, 1, 0, 0);
|
||||
_pipeline.Finish();
|
||||
|
||||
gd.BufferManager.Delete(bufferHandle);
|
||||
}
|
||||
|
||||
public unsafe void ConvertI8ToI16(VulkanGraphicsDevice gd, CommandBufferScoped cbs, BufferHolder src, BufferHolder dst, int srcOffset, int size)
|
||||
{
|
||||
// TODO: Do this with a compute shader?
|
||||
var srcBuffer = src.GetBuffer().Get(cbs, srcOffset, size).Value;
|
||||
var dstBuffer = dst.GetBuffer().Get(cbs, 0, size * 2).Value;
|
||||
|
||||
gd.Api.CmdFillBuffer(cbs.CommandBuffer, dstBuffer, 0, Vk.WholeSize, 0);
|
||||
|
||||
var bufferCopy = new BufferCopy[size];
|
||||
|
||||
for (ulong i = 0; i < (ulong)size; i++)
|
||||
{
|
||||
bufferCopy[i] = new BufferCopy((ulong)srcOffset + i, i * 2, 1);
|
||||
}
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
AccessFlags.AccessTransferWriteBit,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
PipelineStageFlags.PipelineStageTransferBit,
|
||||
0,
|
||||
size * 2);
|
||||
|
||||
fixed (BufferCopy* pBufferCopy = bufferCopy)
|
||||
{
|
||||
gd.Api.CmdCopyBuffer(cbs.CommandBuffer, srcBuffer, dstBuffer, (uint)size, pBufferCopy);
|
||||
}
|
||||
|
||||
BufferHolder.InsertBufferBarrier(
|
||||
gd,
|
||||
cbs.CommandBuffer,
|
||||
dstBuffer,
|
||||
AccessFlags.AccessTransferWriteBit,
|
||||
BufferHolder.DefaultAccessFlags,
|
||||
PipelineStageFlags.PipelineStageTransferBit,
|
||||
PipelineStageFlags.PipelineStageAllCommandsBit,
|
||||
0,
|
||||
size * 2);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_programClearAlpha.Dispose();
|
||||
_programColorBlit.Dispose();
|
||||
_samplerNearest.Dispose();
|
||||
_samplerLinear.Dispose();
|
||||
_pipeline.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue