Global memory emulation using NV_shader_buffer_store and VK_EXT_buffer_device_address

* Make global memory emulation work on OpenGL again

* Shader cache version bump
This commit is contained in:
gdkchan
2026-09-07 04:10:47 -05:00
committed by KeatonTheBot
parent 81c9052847
commit dbf78ba55e
52 changed files with 1013 additions and 53 deletions
+1
View File
@@ -108,6 +108,7 @@ namespace Ryujinx.Graphics.GAL
bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual);
void EndHostConditionalRendering();
void UpdatePageTableGpuAddress(ulong address);
void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount);
}
}
+1
View File
@@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.GAL
void DeleteBuffer(BufferHandle buffer);
PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size);
ulong GetBufferGpuAddress(BufferHandle buffer);
Capabilities GetCapabilities();
ulong GetCurrentSync();
@@ -57,6 +57,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<BufferDisposeCommand>(CommandType.BufferDispose);
Register<BufferGetDataCommand>(CommandType.BufferGetData);
Register<BufferGetGpuAddressCommand>(CommandType.BufferGetGpuAddress);
Register<BufferSetDataCommand>(CommandType.BufferSetData);
Register<CounterEventDisposeCommand>(CommandType.CounterEventDispose);
@@ -138,6 +139,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register<TextureBarrierTiledCommand>(CommandType.TextureBarrierTiled);
Register<TryHostConditionalRenderingCommand>(CommandType.TryHostConditionalRendering);
Register<TryHostConditionalRenderingFlushCommand>(CommandType.TryHostConditionalRenderingFlush);
Register<UpdatePageTableGpuAddressCommand>(CommandType.UpdatePageTableGpuAddress);
Register<UpdateRenderScaleCommand>(CommandType.UpdateRenderScale);
return maxCommandSize;
@@ -19,6 +19,7 @@
BufferDispose,
BufferGetData,
BufferGetGpuAddress,
BufferSetData,
CounterEventDispose,
@@ -100,6 +101,7 @@
TextureBarrierTiled,
TryHostConditionalRendering,
TryHostConditionalRenderingFlush,
UpdatePageTableGpuAddress,
UpdateRenderScale
}
}
@@ -0,0 +1,24 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer
{
struct BufferGetGpuAddressCommand : IGALCommand, IGALCommand<BufferGetGpuAddressCommand>
{
public CommandType CommandType => CommandType.BufferGetGpuAddress;
private BufferHandle _buffer;
private TableRef<ResultBox<ulong>> _result;
public void Set(BufferHandle buffer, TableRef<ResultBox<ulong>> result)
{
_buffer = buffer;
_result = result;
}
public static void Run(ref BufferGetGpuAddressCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
ulong result = renderer.GetBufferGpuAddress(threaded.Buffers.MapBuffer(command._buffer));
command._result.Get(threaded).Result = result;
}
}
}
@@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct UpdatePageTableGpuAddressCommand : IGALCommand, IGALCommand<UpdatePageTableGpuAddressCommand>
{
public CommandType CommandType => CommandType.UpdatePageTableGpuAddress;
private ulong _address;
public void Set(ulong address)
{
_address = address;
}
public static void Run(ref UpdatePageTableGpuAddressCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.UpdatePageTableGpuAddress(command._address);
}
}
}
@@ -371,6 +371,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
return false;
}
public void UpdatePageTableGpuAddress(ulong address)
{
_renderer.New<UpdatePageTableGpuAddressCommand>().Set(address);
_renderer.QueueCommand();
}
public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount)
{
_renderer.New<UpdateRenderScaleCommand>().Set(_renderer.CopySpan(scales.Slice(0, totalCount)), totalCount, fragmentCount);
@@ -361,6 +361,22 @@ namespace Ryujinx.Graphics.GAL.Multithreading
}
}
public ulong GetBufferGpuAddress(BufferHandle buffer)
{
if (IsGpuThread())
{
ResultBox<ulong> box = new ResultBox<ulong>();
New<BufferGetGpuAddressCommand>().Set(buffer, Ref(box));
InvokeCommand();
return box.Result;
}
else
{
return _baseRenderer.GetBufferGpuAddress(Buffers.MapBufferBlocking(buffer));
}
}
public Capabilities GetCapabilities()
{
ResultBox<Capabilities> box = new ResultBox<Capabilities>();
@@ -44,6 +44,20 @@ namespace Ryujinx.Graphics.GAL
}
}
public void UpdatePageTableBasePointer(ulong address)
{
uint addrLow = (uint)address;
uint addrHigh = (uint)(address >> 32);
if (Data.PageTableBasePointer.X != addrLow || Data.PageTableBasePointer.Y != addrHigh)
{
Data.PageTableBasePointer.X = addrLow;
Data.PageTableBasePointer.Y = addrHigh;
MarkDirty(SupportBuffer.PageTableBasePointerOffset, sizeof(ulong));
}
}
public void UpdateFragmentRenderScaleCount(int count)
{
if (Data.FragmentRenderScaleCount.X != count)
@@ -212,14 +212,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
}
_channel.BufferManager.SetComputeBufferBindings(cs.Bindings);
_channel.TextureManager.SetComputeBindings(cs.Bindings);
if (info.UsesGlobalMemory)
{
_channel.BufferManager.SynchronizeComputeStorageBuffers(info.UsesGlobalMemoryWrite);
}
// Should never return false for mismatching spec state, since the shader was fetched above.
_channel.TextureManager.CommitComputeBindings(cs.SpecializationState);
_channel.BufferManager.CommitComputeBindings();
if (info.UsesGlobalMemory)
{
_channel.BufferManager.UpdatePageTable();
}
_context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth);
_3dEngine.ForceShaderUpdate();
@@ -37,6 +37,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private ProgramPipelineState _pipeline;
private uint _globalMemoryUseMask;
private uint _globalMemoryWriteMask;
private bool _vsUsesDrawParameters;
private bool _vtgWritesRtLayer;
private byte _vsClipDistancesWritten;
@@ -320,9 +322,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
private void CommitBindings()
{
UpdateStorageBuffers();
bool unalignedChanged = _currentSpecState.SetHasUnalignedStorageBuffer(_channel.BufferManager.HasUnalignedStorageBuffers);
bool usesGlobalMemory = _globalMemoryUseMask != 0;
UpdateStorageBuffers();
if (usesGlobalMemory)
{
_channel.BufferManager.SynchronizeGraphicsStorageBuffers(_globalMemoryUseMask, _globalMemoryWriteMask);
}
if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState) || unalignedChanged)
{
@@ -331,6 +339,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
_channel.BufferManager.CommitGraphicsBindings();
if (usesGlobalMemory)
{
_channel.BufferManager.UpdatePageTable();
}
}
/// <summary>
@@ -1374,16 +1387,32 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
UpdateShaderBindings(gs.Bindings);
_globalMemoryUseMask = 0;
_globalMemoryWriteMask = 0;
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
{
ShaderProgramInfo info = gs.Shaders[stageIndex + 1]?.Info;
if (info?.UsesRtLayer == true)
{
_vtgWritesRtLayer = true;
}
_currentProgramInfo[stageIndex] = info;
if (info != null)
{
if (info.UsesGlobalMemory)
{
_globalMemoryUseMask |= 1u << stageIndex;
}
if (info.UsesGlobalMemoryWrite)
{
_globalMemoryWriteMask |= 1u << stageIndex;
}
if (info?.UsesRtLayer == true)
{
_vtgWritesRtLayer = true;
}
}
}
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);
+11
View File
@@ -66,6 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
private bool _syncActionRegistered;
private int _referenceCount = 1;
private ulong _hostGpuAddress;
private ulong _dirtyStart = ulong.MaxValue;
private ulong _dirtyEnd = ulong.MaxValue;
@@ -170,6 +171,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
return new BufferRange(Handle, offset, (int)size);
}
public ulong GetHostGpuAddress(ulong address)
{
if (_hostGpuAddress == 0)
{
_hostGpuAddress = _context.Renderer.GetBufferGpuAddress(Handle);
}
return _hostGpuAddress + (address - Address);
}
/// <summary>
/// Checks if a given range overlaps with the buffer.
/// </summary>
+12 -2
View File
@@ -27,7 +27,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Must lock for any access from other threads.
/// </remarks>
private readonly RangeList<Buffer> _buffers;
private Buffer[] _bufferOverlaps;
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
@@ -395,6 +394,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
return GetBuffer(address, size, write).GetRange(address, size);
}
public ulong GetBufferHostGpuAddress(ulong address, ulong size, bool write = false)
{
return GetBuffer(address, size, write).GetHostGpuAddress(address);
}
/// <summary>
/// Gets a buffer for a given memory range.
/// A buffer overlapping with the specified range is assumed to already exist on the cache.
@@ -431,13 +435,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
/// <param name="address">Start address of the memory range</param>
/// <param name="size">Size in bytes of the memory range</param>
public void SynchronizeBufferRange(ulong address, ulong size)
/// <param name="write">Whether the buffer will be written to by this use</param>
public void SynchronizeBufferRange(ulong address, ulong size, bool write = false)
{
if (size != 0)
{
Buffer buffer = _buffers.FindFirstOverlap(address, size);
buffer.SynchronizeMemory(address, size);
if (write)
{
buffer.SignalModified(address, size);
}
}
}
@@ -1,5 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Shader;
@@ -107,6 +108,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
private bool _transformFeedbackBuffersDirty;
private bool _rebind;
private bool _rebindPageTable;
private BufferPageTable _bufferPageTable;
/// <summary>
/// Creates a new instance of the buffer manager.
@@ -137,6 +141,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_bufferTextures = new List<BufferTextureBinding>();
_ranges = new BufferAssignment[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
_bufferPageTable = new BufferPageTable(context);
}
@@ -438,7 +444,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
CommitBufferTextureBindings();
// Force rebind after doing compute work.
Rebind();
Rebind(rebindPageTable: false);
}
/// <summary>
@@ -747,8 +753,90 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
/// </summary>
public void Rebind()
{
Rebind(rebindPageTable: true);
}
/// <summary>
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
/// </summary>
/// <param name="rebindPageTable">Indicates that the page table needs to also be rebound</param>
public void Rebind(bool rebindPageTable)
{
_rebind = true;
if (rebindPageTable)
{
_rebindPageTable = true;
}
}
public void SynchronizeComputeStorageBuffers(bool write)
{
MemoryManager memoryManager = _channel.MemoryManager;
var bufferCache = memoryManager.Physical.BufferCache;
for (int index = 0; index < 16; index++)
{
ulong sbDescAddress = GetComputeUniformBufferAddress(0);
int sbDescOffset = 0x310 + index * 0x10;
sbDescAddress += (ulong)sbDescOffset;
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
ulong address = bufferCache.TranslateAndCreateBuffer(memoryManager, sbDescriptor.PackAddress(), (ulong)sbDescriptor.Size);
if (address != 0)
{
bufferCache.SynchronizeBufferRange(address, (ulong)sbDescriptor.Size, write);
}
}
}
public void SynchronizeGraphicsStorageBuffers(uint globalMemoryUseMask, uint globalMemoryWriteMask)
{
MemoryManager memoryManager = _channel.MemoryManager;
var bufferCache = memoryManager.Physical.BufferCache;
for (int stage = 0; stage < Constants.ShaderStages; stage++)
{
if ((globalMemoryUseMask & (1u << stage)) == 0)
{
continue;
}
bool write = (globalMemoryWriteMask & (1u << stage)) != 0;
for (int index = 0; index < 16; index++)
{
ulong sbDescAddress = GetGraphicsUniformBufferAddress(stage, 0);
int sbDescOffset = 0x110 + stage * 0x100 + index * 0x10;
sbDescAddress += (ulong)sbDescOffset;
SbDescriptor sbDescriptor = memoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
ulong address = bufferCache.TranslateAndCreateBuffer(memoryManager, sbDescriptor.PackAddress(), (ulong)sbDescriptor.Size);
if (address != 0)
{
bufferCache.SynchronizeBufferRange(address, (ulong)sbDescriptor.Size, write);
}
}
}
}
public void UpdatePageTable()
{
MemoryManager memoryManager = _channel.MemoryManager;
_bufferPageTable.Update(memoryManager, _rebindPageTable);
_rebindPageTable = false;
}
}
}
@@ -0,0 +1,201 @@
using Ryujinx.Graphics.GAL;
using System;
using System.Numerics;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Memory
{
class BufferPageTable
{
private const int PageBits = MemoryManager.PtPageBits;
private const ulong PageSize = MemoryManager.PageSize;
private const ulong PageMask = MemoryManager.PageMask;
private const int AsBits = 40;
private const ulong AsSize = 1UL << AsBits;
private const int AsPtBits = AsBits - PageBits;
private const int AsPtLevels = 2;
private const int AsPtLevelBits = AsPtBits / AsPtLevels;
private const int PtLevel0Shift = PageBits;
private const int PtLevel1Shift = PtLevel0Shift + AsPtLevelBits;
private const ulong PtLevelMask = (1UL << AsPtLevelBits) - 1;
private readonly GpuContext _context;
private struct BufferMapping
{
public readonly ulong CpuAddress;
public readonly ulong GpuAddress;
public readonly ulong Size;
public BufferMapping(ulong cpuAddress, ulong gpuAddress, ulong size)
{
CpuAddress = cpuAddress;
GpuAddress = gpuAddress;
Size = size;
}
}
private BufferMapping[] _mappings;
private BufferHandle _bufferMap;
private ulong _bufferMapHostGpuAddress;
private int _bufferMapSize;
private readonly Dictionary<int, int> _blockIdMap;
private readonly ulong[] _blockBitmap;
private readonly int[] _idMap;
private bool _idMapDataDirty;
public BufferPageTable(GpuContext context)
{
_context = context;
_blockIdMap = new Dictionary<int, int>();
_blockBitmap = new ulong[((1 << AsPtLevelBits) + 63) / 64];
_idMap = new int[1 << AsPtLevelBits];
}
public void Update(MemoryManager memoryManager, bool forceUpdate)
{
BufferCache bufferCache = memoryManager.Physical.BufferCache;
if (memoryManager.MappingsModified || forceUpdate)
{
Mapping[] mappings = memoryManager.GetMappings();
BufferMapping[] bufferMappings = new BufferMapping[mappings.Length];
for (int i = 0; i < mappings.Length; i++)
{
Mapping mapping = mappings[i];
ulong cpuAddress = bufferCache.TranslateAndCreateBuffer(memoryManager, mapping.Address, mapping.Size);
bufferMappings[i] = new BufferMapping(cpuAddress, mapping.Address, mapping.Size);
}
_mappings = bufferMappings;
for (int i = 0; i < bufferMappings.Length; i++)
{
BufferMapping mapping = bufferMappings[i];
ulong hostAddress = 0;
if (mapping.CpuAddress != 0)
{
hostAddress = bufferCache.GetBufferHostGpuAddress(mapping.CpuAddress, mapping.Size);
}
Map(hostAddress, mapping.GpuAddress, mapping.Size);
}
if (_idMapDataDirty)
{
BufferHandle bufferMap = EnsureBufferMap(_idMap.Length * sizeof(int));
_context.Renderer.SetBufferData(bufferMap, 0, MemoryMarshal.Cast<int, byte>(_idMap));
_idMapDataDirty = false;
}
_context.Renderer.Pipeline.UpdatePageTableGpuAddress(_bufferMapHostGpuAddress);
}
}
private void Map(ulong hostAddress, ulong guestAddress, ulong size)
{
ulong endGuestAddress = guestAddress + size;
ulong blockSize = PageSize << AsPtLevelBits;
while (guestAddress < endGuestAddress)
{
ulong nextGuestAddress = (guestAddress + blockSize) & ~(blockSize - 1);
ulong chunckSize = Math.Min(nextGuestAddress - guestAddress, endGuestAddress - guestAddress);
int pages = (int)(chunckSize / PageSize);
int blockRegionOffset = sizeof(uint) << AsPtLevelBits;
int blockOffset = GetBlockId(guestAddress) * (sizeof(ulong) << AsPtLevelBits);
int blockInnerOffset = (int)((guestAddress >> PtLevel0Shift) & PtLevelMask) * sizeof(ulong);
int baseOffset = blockRegionOffset + blockOffset + blockInnerOffset;
ulong[] data = new ulong[pages];
for (int page = 0; page < pages; page++)
{
data[page] = hostAddress;
if (hostAddress != 0)
{
hostAddress += PageSize;
}
}
BufferHandle bufferMap = EnsureBufferMap(blockRegionOffset + blockOffset + (sizeof(ulong) << AsPtLevelBits));
_context.Renderer.SetBufferData(bufferMap, baseOffset, MemoryMarshal.Cast<ulong, byte>(data));
guestAddress += chunckSize;
}
}
private BufferHandle EnsureBufferMap(int requiredSize)
{
if (requiredSize > _bufferMapSize)
{
BufferHandle newBuffer = _context.Renderer.CreateBuffer(requiredSize);
if (_bufferMap != BufferHandle.Null)
{
_context.Renderer.Pipeline.CopyBuffer(_bufferMap, newBuffer, 0, 0, _bufferMapSize);
_context.Renderer.DeleteBuffer(_bufferMap);
}
_bufferMap = newBuffer;
_bufferMapHostGpuAddress = _context.Renderer.GetBufferGpuAddress(_bufferMap);
_bufferMapSize = requiredSize;
}
return _bufferMap;
}
private int GetBlockId(ulong address)
{
int blockIndex = (int)((address >> PtLevel1Shift) & PtLevelMask);
if (!_blockIdMap.TryGetValue(blockIndex, out int mappedIndex))
{
mappedIndex = AllocateNewBlock(_blockBitmap);
_idMap[blockIndex] = mappedIndex << AsPtLevelBits;
_idMapDataDirty = true;
_blockIdMap.Add(blockIndex, mappedIndex);
}
return mappedIndex;
}
private static int AllocateNewBlock(ulong[] bitmap)
{
for (int index = 0; index < bitmap.Length; index++)
{
ref ulong v = ref bitmap[index];
if (v == ulong.MaxValue)
{
continue;
}
int firstFreeBit = BitOperations.TrailingZeroCount(~v);
v |= 1UL << firstFreeBit;
return index * 64 + firstFreeBit;
}
throw new InvalidOperationException("No free space left on the texture or sampler table.");
}
}
}
@@ -0,0 +1,22 @@
using Ryujinx.Memory.Range;
namespace Ryujinx.Graphics.Gpu.Memory
{
struct Mapping : IRange
{
public ulong Address { get; }
public ulong Size { get; }
public ulong EndAddress => Address + Size;
public bool OverlapsWith(ulong address, ulong size)
{
return Address < address + size && address < EndAddress;
}
public Mapping(ulong address, ulong size)
{
Address = address;
Size = size;
}
}
}
@@ -32,6 +32,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
private readonly ulong[][] _pageTable;
private readonly RangeList<Mapping> _mappings;
internal bool MappingsModified { get; private set; }
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
/// <summary>
@@ -53,6 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
Physical = physicalMemory;
CounterCache = new CounterCache();
_pageTable = new ulong[PtLvl0Size][];
_mappings = new RangeList<Mapping>();
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
@@ -395,6 +399,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
lock (_pageTable)
{
if (kind == PteKind.Pitch)
{
AddMapping(va, size);
}
UnmapEventArgs e = new(va, size);
MemoryUnmapped?.Invoke(this, e);
@@ -416,6 +425,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
lock (_pageTable)
{
RemoveMapping(va, size);
// Event handlers are not expected to be thread safe.
UnmapEventArgs e = new(va, size);
MemoryUnmapped?.Invoke(this, e);
@@ -758,5 +769,79 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
return pte & 0xffffffffffffffUL;
}
private void AddMapping(ulong va, ulong size)
{
lock (_mappings)
{
ulong startAddress = va;
ulong endAddress = va + size;
Mapping[] overlaps = Array.Empty<Mapping>();
int overlapsCount = _mappings.FindOverlapsNonOverlapping(va, size, ref overlaps);
for (int i = 0; i < overlapsCount; i++)
{
Mapping overlap = overlaps[i];
if (overlap.Address < startAddress)
{
startAddress = overlap.Address;
}
if (overlap.EndAddress > endAddress)
{
endAddress = overlap.EndAddress;
}
_mappings.Remove(overlap);
}
_mappings.Add(new Mapping(startAddress, endAddress - startAddress));
MappingsModified = true;
}
}
private void RemoveMapping(ulong va, ulong size)
{
lock (_mappings)
{
ulong endAddress = va + size;
Mapping[] overlaps = Array.Empty<Mapping>();
int overlapsCount = _mappings.FindOverlapsNonOverlapping(va, size, ref overlaps);
for (int i = 0; i < overlapsCount; i++)
{
Mapping overlap = overlaps[i];
_mappings.Remove(overlap);
if (overlap.Address < va)
{
_mappings.Add(new Mapping(overlap.Address, va - overlap.Address));
}
if (overlap.EndAddress > endAddress)
{
_mappings.Add(new Mapping(endAddress, overlap.EndAddress - endAddress));
}
}
if (overlapsCount != 0)
{
MappingsModified = true;
}
}
}
internal Mapping[] GetMappings()
{
lock (_mappings)
{
MappingsModified = false;
return _mappings.ToArray();
}
}
}
}
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 5027;
private const uint CodeGenVersion = 4735;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
@@ -140,6 +140,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// </summary>
public ShaderStage Stage;
/// <summary>
/// Indicates if the shader uses instructions that access global memory, such as LDG, STG and ATOM.
/// </summary>
public bool UsesGlobalMemory;
/// <summary>
/// Indicates if the shader uses instructions that modify global memory, such as STG and ATOM.
/// </summary>
public bool UsesGlobalMemoryWrite;
/// <summary>
/// Indicates if the shader accesses the Instance ID built-in variable.
/// </summary>
@@ -774,6 +784,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
ShaderIdentification.None,
0,
dataInfo.Stage,
dataInfo.UsesGlobalMemory,
dataInfo.UsesGlobalMemoryWrite,
dataInfo.UsesInstanceId,
dataInfo.UsesDrawParameters,
dataInfo.UsesRtLayer,
@@ -800,6 +812,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
dataInfo.TexturesCount = (ushort)info.Textures.Count;
dataInfo.ImagesCount = (ushort)info.Images.Count;
dataInfo.Stage = info.Stage;
dataInfo.UsesGlobalMemory = info.UsesGlobalMemory;
dataInfo.UsesGlobalMemoryWrite = info.UsesGlobalMemoryWrite;
dataInfo.UsesInstanceId = info.UsesInstanceId;
dataInfo.UsesDrawParameters = info.UsesDrawParameters;
dataInfo.UsesRtLayer = info.UsesRtLayer;
@@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
public ResourceCounts()
{
UniformBuffersCount = 1; // The first binding is reserved for the support buffer.
StorageBuffersCount = 1; // The first binding is reserved for the buffer mappings table for GPU address translation.
}
}
}
+8
View File
@@ -94,6 +94,14 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public static ulong GetGpuAddress(BufferHandle handle)
{
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32());
GL.NV.MakeBufferResident((NvShaderBufferLoad)BufferTarget.CopyWriteBuffer, (NvShaderBufferLoad)All.ReadWrite);
GL.NV.GetBufferParameter(BufferTargetArb.CopyWriteBuffer, NvShaderBufferLoad.BufferGpuAddressNv, out ulong gpuAddress);
return gpuAddress;
}
public static void Resize(BufferHandle handle, int size)
{
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32());
@@ -124,6 +124,11 @@ namespace Ryujinx.Graphics.OpenGL
return Buffer.GetData(this, buffer, offset, size);
}
public ulong GetBufferGpuAddress(BufferHandle buffer)
{
return Buffer.GetGpuAddress(buffer);
}
public Capabilities GetCapabilities()
{
bool intelWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows;
+5
View File
@@ -1548,6 +1548,11 @@ namespace Ryujinx.Graphics.OpenGL
return (_boundDrawFramebuffer, _boundReadFramebuffer);
}
public void UpdatePageTableGpuAddress(ulong address)
{
_supportBuffer.UpdatePageTableBasePointer(address);
}
public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount)
{
bool changed = false;
@@ -60,6 +60,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine("#extension GL_NV_geometry_shader_passthrough : enable");
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.GlobalMemory) != 0)
{
context.AppendLine("#extension GL_EXT_shader_16bit_storage : enable");
context.AppendLine("#extension GL_EXT_shader_8bit_storage : enable");
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
context.AppendLine("#extension GL_EXT_buffer_reference : enable");
context.AppendLine("#extension GL_EXT_buffer_reference_uvec2 : enable");
}
else
{
context.AppendLine("#extension GL_NV_shader_buffer_load : enable");
}
}
if (context.Config.GpuAccessor.QueryHostSupportsViewportMask())
{
context.AppendLine("#extension GL_NV_viewport_array2 : enable");
@@ -255,6 +271,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl");
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.GlobalMemory) != 0)
{
AppendHelperFunction(context, context.Config.Options.TargetApi == TargetApi.Vulkan
? "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/GlobalMemoryVk.glsl"
: "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/GlobalMemory.glsl");
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)
{
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl");
@@ -0,0 +1,19 @@
struct PTType
{
uint blockIndices[1 << 14];
uvec2 pointers[1 << 28];
};
uvec2 Helper_TranslateAddress(uvec2 address)
{
PTType* br = (PTType*)packPtr(s_page_table.xy);
uint l0 = (address.x >> 12) & 0x3fff;
uint l1 = ((address.x >> 26) & 0x3f) | ((address.y << 6) & 0x3fc0);
uvec2 hostAddress = br->pointers[br->blockIndices[l1] + l0];
hostAddress.x += (address.x & 0xfff);
return hostAddress;
}
@@ -0,0 +1,34 @@
layout (buffer_reference, std430, buffer_reference_align = 8) buffer buffer_regions_block
{
uint blockIndices[1 << 14];
uvec2 pointers[];
};
layout (buffer_reference, std430, buffer_reference_align = 1) buffer uint8_t_ptr
{
uint8_t value;
};
layout (buffer_reference, std430, buffer_reference_align = 2) buffer uint16_t_ptr
{
uint16_t value;
};
layout (buffer_reference, std430, buffer_reference_align = 4) buffer uint_ptr
{
uint value;
};
uvec2 Helper_TranslateAddress(uvec2 address)
{
buffer_regions_block br = buffer_regions_block(s_page_table.xy);
uint l0 = (address.x >> 12) & 0x3fff;
uint l1 = ((address.x >> 26) & 0x3f) | ((address.y << 6) & 0x3fc0);
uvec2 hostAddress = br.pointers[br.blockIndices[l1] + l0];
hostAddress.x += (address.x & 0xfff);
return hostAddress;
}
@@ -18,5 +18,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public static string StoreShared8 = "Helper_StoreShared8";
public static string StoreStorage16 = "Helper_StoreStorage16";
public static string StoreStorage8 = "Helper_StoreStorage8";
public static string TranslateAddress = "Helper_TranslateAddress";
}
}
@@ -87,6 +87,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
{
switch (operation.StorageKind)
{
case StorageKind.GlobalMemory: args += LoadGlobal(context, operation); break;
case StorageKind.SharedMemory: args += LoadShared(context, operation); break;
case StorageKind.StorageBuffer: args += LoadStorage(context, operation); break;
@@ -167,6 +168,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.Load:
return Load(context, operation);
case Instruction.LoadGlobal:
return LoadGlobal(context, operation);
case Instruction.LoadLocal:
return LoadLocal(context, operation);
@@ -191,6 +195,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.Store:
return Store(context, operation);
case Instruction.StoreGlobal:
return StoreGlobal(context, operation);
case Instruction.StoreGlobal16:
return StoreGlobal16(context, operation);
case Instruction.StoreGlobal8:
return StoreGlobal8(context, operation);
case Instruction.StoreLocal:
return StoreLocal(context, operation);
@@ -83,6 +83,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.ImageAtomic, InstType.Special);
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
Add(Instruction.Load, InstType.Special);
Add(Instruction.LoadGlobal, InstType.Special);
Add(Instruction.LoadLocal, InstType.Special);
Add(Instruction.LoadShared, InstType.Special);
Add(Instruction.LoadStorage, InstType.Special);
@@ -119,6 +120,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.Sine, InstType.CallUnary, "sin");
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
Add(Instruction.Store, InstType.Special);
Add(Instruction.StoreGlobal, InstType.Special);
Add(Instruction.StoreLocal, InstType.Special);
Add(Instruction.StoreShared, InstType.Special);
Add(Instruction.StoreShared16, InstType.Special);
@@ -191,6 +191,24 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return GenerateLoadOrStore(context, operation, isStore: false);
}
public static string LoadGlobal(CodeGenContext context, AstOperation operation)
{
IAstNode src1 = operation.GetSource(0);
IAstNode src2 = operation.GetSource(1);
string addressLowExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string addressHighExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
return $"uint_ptr({HelperFunctionNames.TranslateAddress}(uvec2({addressLowExpr}, {addressHighExpr}))).value";
}
else
{
return $"*(uint*)packPtr({HelperFunctionNames.TranslateAddress}(uvec2({addressLowExpr}, {addressHighExpr})))";
}
}
public static string LoadLocal(CodeGenContext context, AstOperation operation)
{
return LoadLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
@@ -274,6 +292,44 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return GenerateLoadOrStore(context, operation, isStore: true);
}
public static string StoreGlobal(CodeGenContext context, AstOperation operation)
{
return StoreGlobal(context, operation, "uint");
}
public static string StoreGlobal16(CodeGenContext context, AstOperation operation)
{
return StoreGlobal(context, operation, "uint16_t");
}
public static string StoreGlobal8(CodeGenContext context, AstOperation operation)
{
return StoreGlobal(context, operation, "uint8_t");
}
private static string StoreGlobal(CodeGenContext context, AstOperation operation, string type)
{
IAstNode src1 = operation.GetSource(0);
IAstNode src2 = operation.GetSource(1);
IAstNode src3 = operation.GetSource(2);
string addressLowExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
string addressHighExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
if (context.Config.Options.TargetApi == TargetApi.Vulkan)
{
return $"{type}_ptr({HelperFunctionNames.TranslateAddress}(uvec2({addressLowExpr}, {addressHighExpr}))).value = {src}";
}
else
{
return $"*({type}*)packPtr({HelperFunctionNames.TranslateAddress}(uvec2({addressLowExpr}, {addressHighExpr}))) = {src}";
}
}
public static string StoreLocal(CodeGenContext context, AstOperation operation)
{
return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
@@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public int InputVertices { get; }
public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
public Instruction PageTablePointerType { get; set; }
public Instruction StorageBuffersArray { get; set; }
public Instruction LocalMemory { get; set; }
public Instruction SharedMemory { get; set; }
@@ -184,6 +184,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddGlobalVariable(ubVariable);
context.ConstantBuffers.Add(buffer.Binding, ubVariable);
}
var blockArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), 1 << 14));
var pointerArrayType = context.TypeRuntimeArray(context.TypeVector(context.TypeU32(), 2));
context.Decorate(blockArrayType, Decoration.ArrayStride, (LiteralInteger)4);
context.Decorate(pointerArrayType, Decoration.ArrayStride, (LiteralInteger)8);
var ptStructType = context.TypeStruct(false, blockArrayType, pointerArrayType);
context.MemberDecorate(ptStructType, 0, Decoration.Offset, (LiteralInteger)0);
context.MemberDecorate(ptStructType, 1, Decoration.Offset, (LiteralInteger)((1 << 14) * sizeof(uint)));
context.Decorate(ptStructType, Decoration.Block);
context.PageTablePointerType = context.TypePointer(StorageClass.PhysicalStorageBuffer, ptStructType);
}
private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
@@ -97,6 +97,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.ImageStore, GenerateImageStore);
Add(Instruction.IsNan, GenerateIsNan);
Add(Instruction.Load, GenerateLoad);
Add(Instruction.LoadGlobal, GenerateLoadGlobal);
Add(Instruction.LoadLocal, GenerateLoadLocal);
Add(Instruction.LoadShared, GenerateLoadShared);
Add(Instruction.LoadStorage, GenerateLoadStorage);
@@ -133,6 +134,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.Sine, GenerateSine);
Add(Instruction.SquareRoot, GenerateSquareRoot);
Add(Instruction.Store, GenerateStore);
Add(Instruction.StoreGlobal, GenerateStoreGlobal);
// Add(Instruction.StoreGlobal16, GenerateStoreGlobal16);
// Add(Instruction.StoreGlobal8, GenerateStoreGlobal8);
Add(Instruction.StoreLocal, GenerateStoreLocal);
Add(Instruction.StoreShared, GenerateStoreShared);
Add(Instruction.StoreShared16, GenerateStoreShared16);
@@ -865,6 +869,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return GenerateLoadOrStore(context, operation, isStore: false);
}
private static OperationResult GenerateLoadGlobal(CodeGenContext context, AstOperation operation)
{
var elemPointer = GetGlobalElemPointer(context, operation, context.TypeU32());
var value = context.Load(context.TypeU32(), elemPointer, MemoryAccessMask.Aligned, 4);
return new OperationResult(AggregateType.U32, value);
}
private static OperationResult GenerateLoadLocal(CodeGenContext context, AstOperation operation)
{
return GenerateLoadLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory);
@@ -1268,6 +1280,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return GenerateLoadOrStore(context, operation, isStore: true);
}
private static OperationResult GenerateStoreGlobal(CodeGenContext context, AstOperation operation)
{
var elemPointer = GetGlobalElemPointer(context, operation, context.TypeU32());
context.Store(elemPointer, context.Get(AggregateType.U32, operation.GetSource(2)), MemoryAccessMask.Aligned, 4);
return OperationResult.Invalid;
}
private static OperationResult GenerateStoreGlobal16(CodeGenContext context, AstOperation operation)
{
var elemPointer = GetGlobalElemPointer(context, operation, context.TypeInt(16, 0));
context.Store(elemPointer, context.Get(AggregateType.U32, operation.GetSource(2)), MemoryAccessMask.Aligned, 2);
return OperationResult.Invalid;
}
private static OperationResult GenerateStoreGlobal8(CodeGenContext context, AstOperation operation)
{
var elemPointer = GetGlobalElemPointer(context, operation, context.TypeInt(8, 0));
context.Store(elemPointer, context.Get(AggregateType.U32, operation.GetSource(2)), MemoryAccessMask.Aligned, 1);
return OperationResult.Invalid;
}
private static OperationResult GenerateStoreLocal(CodeGenContext context, AstOperation operation)
{
return GenerateStoreLocalOrShared(context, operation, StorageClass.Private, context.LocalMemory);
@@ -1853,7 +1889,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction elemPointer;
if (operation.StorageKind == StorageKind.StorageBuffer)
if (operation.StorageKind == StorageKind.GlobalMemory)
{
elemPointer = GetGlobalElemPointer(context, operation, context.TypeU32());
}
else if (operation.StorageKind == StorageKind.StorageBuffer)
{
elemPointer = GetStorageElemPointer(context, operation);
}
@@ -1880,7 +1920,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction elemPointer;
if (operation.StorageKind == StorageKind.StorageBuffer)
if (operation.StorageKind == StorageKind.GlobalMemory)
{
elemPointer = GetGlobalElemPointer(context, operation, context.TypeU32());
}
else if (operation.StorageKind == StorageKind.StorageBuffer)
{
elemPointer = GetStorageElemPointer(context, operation);
}
@@ -2115,6 +2159,60 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddLabel(loopEnd);
}
private static SpvInstruction GetGlobalElemPointer(CodeGenContext context, AstOperation operation, SpvInstruction elemType)
{
var vec4UintType = context.TypeVector(context.TypeU32(), 4);
var ptBasePointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, vec4UintType), context.ConstantBuffers[0], context.Constant(context.TypeU32(), 5));
ptBasePointer = context.Load(vec4UintType, ptBasePointer);
ptBasePointer = context.VectorShuffle(context.TypeVector(context.TypeU32(), 2), ptBasePointer, ptBasePointer, 0, 1);
ptBasePointer = context.Bitcast(context.PageTablePointerType, ptBasePointer);
var addrLow = context.Get(AggregateType.U32, operation.GetSource(0));
var addrHigh = context.Get(AggregateType.U32, operation.GetSource(1));
// uint l0 = (addrLow >> 12) & 0x3fff;
// uint l1 = ((addrLow >> 26) & 0x3f) | ((addrHigh << 6) & 0x3fc0);
var l0 = ShiftRightAndMask(context, addrLow, 12, 0x3fff);
var l1 = context.BitwiseOr(context.TypeU32(),
ShiftRightAndMask(context, addrLow, 26, 0x3f),
ShiftLeftAndMask(context, addrHigh, 6, 0x3fc0));
var blockIndexPointerType = context.TypePointer(StorageClass.PhysicalStorageBuffer, context.TypeU32());
var blockIndex = context.AccessChain(blockIndexPointerType, ptBasePointer, context.Constant(context.TypeS32(), 0), l1);
blockIndex = context.Load(context.TypeU32(), blockIndex, MemoryAccessMask.Aligned, 4);
var offset = context.IAdd(context.TypeU32(), blockIndex, l0);
var vec2UintType = context.TypeVector(context.TypeU32(), 2);
var vec2UintPointerType = context.TypePointer(StorageClass.PhysicalStorageBuffer, vec2UintType);
var hostPointer = context.AccessChain(vec2UintPointerType, ptBasePointer, context.Constant(context.TypeS32(), 1), offset);
hostPointer = context.Load(vec2UintType, hostPointer, MemoryAccessMask.Aligned, 8);
var pageOffset = context.BitwiseAnd(context.TypeU32(), addrLow, context.Constant(context.TypeU32(), 0xfff));
var hostPointerLow = context.IAdd(context.TypeU32(), context.CompositeExtract(context.TypeU32(), hostPointer, 0), pageOffset);
var hostPointerHigh = context.CompositeExtract(context.TypeU32(), hostPointer, 1);
hostPointer = context.CompositeConstruct(vec2UintType, hostPointerLow, hostPointerHigh);
var elemStructType = context.TypeStruct(false, elemType);
var elemStructPointerType = context.TypePointer(StorageClass.PhysicalStorageBuffer, elemStructType);
var elemPointerType = context.TypePointer(StorageClass.PhysicalStorageBuffer, elemType);
return context.AccessChain(elemPointerType, context.Bitcast(elemStructPointerType, hostPointer), context.Constant(context.TypeS32(), 0));
}
private static SpvInstruction ShiftLeftAndMask(CodeGenContext context, SpvInstruction value, int shift, int mask)
{
value = context.ShiftLeftLogical(context.TypeU32(), value, context.Constant(context.TypeS32(), shift));
return context.BitwiseAnd(context.TypeU32(), value, context.Constant(context.TypeU32(), mask));
}
private static SpvInstruction ShiftRightAndMask(CodeGenContext context, SpvInstruction value, int shift, int mask)
{
value = context.ShiftRightLogical(context.TypeU32(), value, context.Constant(context.TypeS32(), shift));
return context.BitwiseAnd(context.TypeU32(), value, context.Constant(context.TypeU32(), mask));
}
private static OperationResult GetZeroOperationResult(
CodeGenContext context,
AstTextureOperation texOp,
@@ -93,6 +93,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddCapability(Capability.DrawParameters);
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.GlobalMemory) != 0)
{
context.AddCapability(Capability.PhysicalStorageBufferAddresses);
context.AddExtension("SPV_KHR_physical_storage_buffer");
}
if (context.Info.IoDefinitions.Contains(new IoDefinition(StorageKind.Output, IoVariable.ViewportMask)))
{
context.AddExtension("SPV_NV_viewport_array2");
@@ -234,11 +234,6 @@ namespace Ryujinx.Graphics.Shader.Decoders
op = InstTable.GetOp(address, opCode);
if (op.Props.HasFlag(InstProps.TexB))
{
config.SetUsedFeature(FeatureFlags.Bindless);
}
if (op.Name == InstName.Ald || op.Name == InstName.Ast || op.Name == InstName.Ipa)
{
SetUserAttributeUses(config, op.Name, opCode);
@@ -12,6 +12,8 @@
<ItemGroup>
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Shared.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Storage.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\GlobalMemory.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\GlobalMemoryVk.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
@@ -13,6 +13,8 @@ namespace Ryujinx.Graphics.Shader
public ShaderIdentification Identification { get; }
public int GpLayerInputAttribute { get; }
public ShaderStage Stage { get; }
public bool UsesGlobalMemory { get; }
public bool UsesGlobalMemoryWrite { get; }
public bool UsesInstanceId { get; }
public bool UsesDrawParameters { get; }
public bool UsesRtLayer { get; }
@@ -27,6 +29,8 @@ namespace Ryujinx.Graphics.Shader
ShaderIdentification identification,
int gpLayerInputAttribute,
ShaderStage stage,
bool usesGlobalMemory,
bool usesGlobalMemoryWrite,
bool usesInstanceId,
bool usesDrawParameters,
bool usesRtLayer,
@@ -41,6 +45,8 @@ namespace Ryujinx.Graphics.Shader
Identification = identification;
GpLayerInputAttribute = gpLayerInputAttribute;
Stage = stage;
UsesGlobalMemory = usesGlobalMemory;
UsesGlobalMemoryWrite = usesGlobalMemoryWrite;
UsesInstanceId = usesInstanceId;
UsesDrawParameters = usesDrawParameters;
UsesRtLayer = usesRtLayer;
@@ -7,15 +7,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
AtomicMinMaxS32Shared = 1 << 0,
AtomicMinMaxS32Storage = 1 << 1,
MultiplyHighS32 = 1 << 2,
MultiplyHighU32 = 1 << 3,
Shuffle = 1 << 4,
ShuffleDown = 1 << 5,
ShuffleUp = 1 << 6,
ShuffleXor = 1 << 7,
StoreSharedSmallInt = 1 << 8,
StoreStorageSmallInt = 1 << 9,
SwizzleAdd = 1 << 10,
FSI = 1 << 11
GlobalMemory = 1 << 2,
MultiplyHighS32 = 1 << 3,
MultiplyHighU32 = 1 << 4,
Shuffle = 1 << 5,
ShuffleDown = 1 << 6,
ShuffleUp = 1 << 7,
ShuffleXor = 1 << 8,
StoreSharedSmallInt = 1 << 9,
StoreStorageSmallInt = 1 << 10,
SwizzleAdd = 1 << 11,
FSI = 1 << 12
}
}
@@ -269,12 +269,16 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
operation.SourcesCount));
}
// Those instructions needs to be emulated by using helper functions,
// because they are NVIDIA specific. Those flags helps the backend to
// Those instructions need to be emulated by using helper functions,
// because they are NVIDIA specific. Those flags help the backend to
// decide which helper functions are needed on the final generated code.
switch (operation.Inst)
{
case Instruction.AtomicAdd:
case Instruction.AtomicAnd :
case Instruction.AtomicCompareAndSwap:
case Instruction.AtomicMaxS32:
case Instruction.AtomicMaxU32:
case Instruction.AtomicMinS32:
if (operation.StorageKind == StorageKind.SharedMemory)
{
@@ -285,6 +289,29 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
}
break;
case Instruction.AtomicMinU32:
case Instruction.AtomicOr:
case Instruction.AtomicSwap:
case Instruction.AtomicXor:
context.Config.SetUsedFeature(FeatureFlags.GlobalMemory);
context.Config.SetUsedFeature(FeatureFlags.GlobalMemoryWrite);
context.Info.HelperFunctionsMask |= HelperFunctionsMask.GlobalMemory;
break;
case Instruction.LoadGlobal:
case Instruction.StoreGlobal:
case Instruction.StoreGlobal16:
case Instruction.StoreGlobal8:
context.Config.SetUsedFeature(FeatureFlags.GlobalMemory);
if (operation.Inst == Instruction.StoreGlobal ||
operation.Inst == Instruction.StoreGlobal16 ||
operation.Inst == Instruction.StoreGlobal8)
{
context.Config.SetUsedFeature(FeatureFlags.GlobalMemoryWrite);
}
context.Info.HelperFunctionsMask |= HelperFunctionsMask.GlobalMemory;
break;
case Instruction.MultiplyHighS32:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
break;
+7 -2
View File
@@ -20,7 +20,8 @@ namespace Ryujinx.Graphics.Shader
FragmentIsBgra,
ViewportInverse,
FragmentRenderScaleCount,
RenderScale
RenderScale,
PageTableBasePointer
}
public struct SupportBuffer
@@ -36,6 +37,7 @@ namespace Ryujinx.Graphics.Shader
public static int FragmentRenderScaleCountOffset;
public static int GraphicsRenderScaleOffset;
public static int ComputeRenderScaleOffset;
public static int PageTableBasePointerOffset;
public const int FragmentIsBgraCount = 8;
// One for the render target, 64 for the textures, and 8 for the images.
@@ -59,6 +61,7 @@ namespace Ryujinx.Graphics.Shader
FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount);
GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale);
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
PageTableBasePointerOffset = OffsetOf(ref instance, ref instance.PageTableBasePointer);
}
internal static StructureType GetStructureType()
@@ -69,7 +72,8 @@ namespace Ryujinx.Graphics.Shader
new StructureField(AggregateType.Array | AggregateType.U32, "s_is_bgra", FragmentIsBgraCount),
new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_inverse"),
new StructureField(AggregateType.S32, "s_frag_scale_count"),
new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount)
new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount),
new StructureField(AggregateType.Vector4 | AggregateType.U32, "s_page_table")
});
}
@@ -80,5 +84,6 @@ namespace Ryujinx.Graphics.Shader
// Render scale max count: 1 + 64 + 8. First scale is fragment output scale, others are textures/image inputs.
public Array73<Vector4<float>> RenderScale;
public Vector4<uint> PageTableBasePointer;
}
}
@@ -18,9 +18,11 @@ namespace Ryujinx.Graphics.Shader.Translation
Bindless = 1 << 2,
InstanceId = 1 << 3,
DrawParameters = 1 << 4,
RtLayer = 1 << 5,
IaIndexing = 1 << 7,
OaIndexing = 1 << 8,
FixedFuncAttr = 1 << 9
GlobalMemory = 1 << 5,
GlobalMemoryWrite = 1 << 6,
RtLayer = 1 << 7,
IaIndexing = 1 << 9,
OaIndexing = 1 << 10,
FixedFuncAttr = 1 << 11
}
}
@@ -77,10 +77,10 @@ namespace Ryujinx.Graphics.Shader.Translation
nextNode = node.Next;
}
else if (UsesGlobalMemory(operation.Inst, operation.StorageKind))
/* else if (UsesGlobalMemory(operation.Inst, operation.StorageKind))
{
nextNode = RewriteGlobalAccess(node, config)?.Next ?? nextNode;
}
} */
node = nextNode;
}
@@ -509,7 +509,15 @@ namespace Ryujinx.Graphics.Shader.Translation
NextInputAttributesComponents = config.ThisInputAttributesComponents;
NextUsedInputAttributesPerPatch = config.UsedInputAttributesPerPatch;
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
MergeOutputUserAttributes(config.UsedInputAttributes | config.PassthroughAttributes, config.UsedInputAttributesPerPatch);
int passthroughAttributes = config.PassthroughAttributes;
while (passthroughAttributes != 0)
{
int bit = BitOperations.TrailingZeroCount(passthroughAttributes);
NextInputAttributesComponents |= new UInt128(0, 0xf) << (bit * 4);
passthroughAttributes &= ~(1 << bit);
}
if (UsedOutputAttributesPerPatch.Count != 0)
{
@@ -966,6 +974,8 @@ namespace Ryujinx.Graphics.Shader.Translation
identification,
GpLayerInputAttribute,
Stage,
UsedFeatures.HasFlag(FeatureFlags.GlobalMemory),
UsedFeatures.HasFlag(FeatureFlags.GlobalMemoryWrite),
UsedFeatures.HasFlag(FeatureFlags.InstanceId),
UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
UsedFeatures.HasFlag(FeatureFlags.RtLayer),
+11 -6
View File
@@ -360,12 +360,6 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public BufferHandle GetHandle()
{
var handle = _bufferHandle;
return Unsafe.As<ulong, BufferHandle>(ref handle);
}
public unsafe IntPtr Map(int offset, int mappingSize)
{
return _map;
@@ -481,6 +475,17 @@ namespace Ryujinx.Graphics.Vulkan
throw new InvalidOperationException("The buffer is not host mapped.");
}
public ulong GetGpuAddress()
{
BufferDeviceAddressInfo info = new BufferDeviceAddressInfo()
{
SType = StructureType.BufferDeviceAddressInfo,
Buffer = GetBuffer().GetUnsafe().Value
};
return _gd.Api.GetBufferDeviceAddress(_device, info);
}
public unsafe void SetData(int offset, ReadOnlySpan<byte> data, CommandBufferScoped? cbs = null, Action endRenderPass = null)
{
int dataSize = Math.Min(data.Length, Size - offset);
+20 -2
View File
@@ -166,9 +166,17 @@ namespace Ryujinx.Graphics.Vulkan
{
usage |= BufferUsageFlags.ConditionalRenderingBitExt;
}
else if (gd.Capabilities.SupportsIndirectParameters)
else
{
usage |= BufferUsageFlags.IndirectBufferBit;
if (gd.Capabilities.SupportsIndirectParameters)
{
usage |= BufferUsageFlags.IndirectBufferBit;
}
if (gd.Capabilities.SupportsBufferDeviceAddress)
{
usage |= BufferUsageFlags.ShaderDeviceAddressBitExt;
}
}
var bufferCreateInfo = new BufferCreateInfo()
@@ -484,6 +492,16 @@ namespace Ryujinx.Graphics.Vulkan
return new PinnedSpan<byte>();
}
public ulong GetBufferGpuAddress(BufferHandle handle)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetGpuAddress();
}
return 0;
}
public void SetData<T>(BufferHandle handle, int offset, ReadOnlySpan<T> data) where T : unmanaged
{
SetData(handle, offset, MemoryMarshal.Cast<T, byte>(data), null, null);
+1 -1
View File
@@ -12,7 +12,7 @@
public const int MaxStorageBuffersPerStage = 16;
public const int MaxTexturesPerStage = 64;
public const int MaxImagesPerStage = 16;
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages;
public const int MaxUniformBufferBindings = MaxUniformBuffersPerStage * MaxShaderStages + 1;
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
@@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Vulkan
var poolSizes = new DescriptorPoolSize[]
{
new DescriptorPoolSize(DescriptorType.UniformBuffer, (1 + Constants.MaxUniformBufferBindings) * DescriptorPoolMultiplier),
new DescriptorPoolSize(DescriptorType.UniformBuffer, Constants.MaxUniformBufferBindings * DescriptorPoolMultiplier),
new DescriptorPoolSize(DescriptorType.StorageBuffer, Constants.MaxStorageBufferBindings * DescriptorPoolMultiplier),
new DescriptorPoolSize(DescriptorType.CombinedImageSampler, Constants.MaxTextureBindings * DescriptorPoolMultiplier),
new DescriptorPoolSize(DescriptorType.StorageImage, Constants.MaxImageBindings * DescriptorPoolMultiplier),
@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsGeometryShaderPassthrough;
public readonly bool SupportsSubgroupSizeControl;
public readonly bool SupportsShaderInt8;
public readonly bool SupportsBufferDeviceAddress;
public readonly bool SupportsShaderStencilExport;
public readonly bool SupportsShaderStorageImageMultisample;
public readonly bool SupportsConditionalRendering;
@@ -64,6 +65,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsGeometryShaderPassthrough,
bool supportsSubgroupSizeControl,
bool supportsShaderInt8,
bool supportsBufferDeviceAddress,
bool supportsShaderStencilExport,
bool supportsShaderStorageImageMultisample,
bool supportsConditionalRendering,
@@ -100,6 +102,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
SupportsSubgroupSizeControl = supportsSubgroupSizeControl;
SupportsShaderInt8 = supportsShaderInt8;
SupportsBufferDeviceAddress = supportsBufferDeviceAddress;
SupportsShaderStencilExport = supportsShaderStencilExport;
SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample;
SupportsConditionalRendering = supportsConditionalRendering;
@@ -1386,6 +1386,11 @@ namespace Ryujinx.Graphics.Vulkan
TextureBarrier();
}
public void UpdatePageTableGpuAddress(ulong address)
{
SupportBufferUpdater.UpdatePageTableBasePointer(address);
}
public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount)
{
bool changed = false;
@@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Vulkan
private static readonly string[] _desirableExtensions = new string[]
{
ExtBufferDeviceAddress.ExtensionName,
ExtConditionalRendering.ExtensionName,
ExtExtendedDynamicState.ExtensionName,
ExtTransformFeedback.ExtensionName,
@@ -502,6 +503,20 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresSubgroupSizeControl;
}
PhysicalDeviceBufferDeviceAddressFeaturesEXT featuresBufferDeviceAddress;
if (physicalDevice.IsDeviceExtensionPresent(ExtBufferDeviceAddress.ExtensionName))
{
featuresBufferDeviceAddress = new PhysicalDeviceBufferDeviceAddressFeaturesEXT()
{
SType = StructureType.PhysicalDeviceBufferAddressFeaturesExt,
PNext = pExtendedFeatures,
BufferDeviceAddress = true
};
pExtendedFeatures = &featuresBufferDeviceAddress;
}
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
@@ -307,6 +307,7 @@ namespace Ryujinx.Graphics.Vulkan
_physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"),
supportsSubgroupSizeControl,
featuresShaderInt8.ShaderInt8,
_physicalDevice.IsDeviceExtensionPresent(ExtBufferDeviceAddress.ExtensionName),
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
features2.Features.ShaderStorageImageMultisample,
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
@@ -472,6 +473,11 @@ namespace Ryujinx.Graphics.Vulkan
return BufferManager.GetData(buffer, offset, size);
}
public ulong GetBufferGpuAddress(BufferHandle buffer)
{
return BufferManager.GetBufferGpuAddress(buffer);
}
public unsafe Capabilities GetCapabilities()
{
FormatFeatureFlags compressedFormatFeatureFlags =
+12
View File
@@ -464,6 +464,18 @@ namespace Ryujinx.Memory.Range
return ~left;
}
public T[] ToArray()
{
T[] output = new T[Count];
for (int i = 0; i < output.Length; i++)
{
output[i] = _items[i].Value;
}
return output;
}
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < Count; i++)
+10 -2
View File
@@ -710,7 +710,7 @@ namespace Spv.Generator
return result;
}
public Instruction Load(Instruction resultType, Instruction pointer, MemoryAccessMask memoryAccess = (MemoryAccessMask)int.MaxValue)
public Instruction Load(Instruction resultType, Instruction pointer, MemoryAccessMask memoryAccess = (MemoryAccessMask)int.MaxValue, LiteralInteger operand2 = null)
{
Instruction result = NewInstruction(Op.OpLoad, GetNewId(), resultType);
@@ -718,13 +718,17 @@ namespace Spv.Generator
if (memoryAccess != (MemoryAccessMask)int.MaxValue)
{
result.AddOperand(memoryAccess);
if (operand2 != null)
{
result.AddOperand(operand2);
}
}
AddToFunctionDefinitions(result);
return result;
}
public Instruction Store(Instruction pointer, Instruction obj, MemoryAccessMask memoryAccess = (MemoryAccessMask)int.MaxValue)
public Instruction Store(Instruction pointer, Instruction obj, MemoryAccessMask memoryAccess = (MemoryAccessMask)int.MaxValue, LiteralInteger operand2 = null)
{
Instruction result = NewInstruction(Op.OpStore);
@@ -733,6 +737,10 @@ namespace Spv.Generator
if (memoryAccess != (MemoryAccessMask)int.MaxValue)
{
result.AddOperand(memoryAccess);
if (operand2 != null)
{
result.AddOperand(operand2);
}
}
AddToFunctionDefinitions(result);