Global memory emulation using NV_shader_buffer_store and VK_EXT_buffer_device_address

This commit is contained in:
gdk 2022-10-22 21:28:28 -03:00
parent dde9bb5c69
commit 01ca055d38
53 changed files with 1075 additions and 75 deletions

View file

@ -107,6 +107,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);
}
}

View file

@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.GAL
void DeleteBuffer(BufferHandle buffer);
ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size);
ulong GetBufferGpuAddress(BufferHandle buffer);
Capabilities GetCapabilities();
ulong GetCurrentSync();

View file

@ -77,6 +77,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
BufferDisposeCommand.Run(ref GetCommand<BufferDisposeCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.BufferGetData] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
BufferGetDataCommand.Run(ref GetCommand<BufferGetDataCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.BufferGetGpuAddress] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
BufferGetGpuAddressCommand.Run(ref GetCommand<BufferGetGpuAddressCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.BufferSetData] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
BufferSetDataCommand.Run(ref GetCommand<BufferSetDataCommand>(memory), threaded, renderer);
@ -229,6 +231,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
TryHostConditionalRenderingCommand.Run(ref GetCommand<TryHostConditionalRenderingCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.TryHostConditionalRenderingFlush] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
TryHostConditionalRenderingFlushCommand.Run(ref GetCommand<TryHostConditionalRenderingFlushCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.UpdatePageTableGpuAddress] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
UpdatePageTableGpuAddressCommand.Run(ref GetCommand<UpdatePageTableGpuAddressCommand>(memory), threaded, renderer);
_lookup[(int)CommandType.UpdateRenderScale] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
UpdateRenderScaleCommand.Run(ref GetCommand<UpdateRenderScaleCommand>(memory), threaded, renderer);
}

View file

@ -17,6 +17,7 @@
BufferDispose,
BufferGetData,
BufferGetGpuAddress,
BufferSetData,
CounterEventDispose,
@ -96,6 +97,7 @@
TextureBarrierTiled,
TryHostConditionalRendering,
TryHostConditionalRenderingFlush,
UpdatePageTableGpuAddress,
UpdateRenderScale
}
}

View file

@ -0,0 +1,24 @@
using Ryujinx.Graphics.GAL.Multithreading.Model;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer
{
struct BufferGetGpuAddressCommand : IGALCommand
{
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;
}
}
}

View file

@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{
struct UpdatePageTableGpuAddressCommand : IGALCommand
{
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);
}
}
}

View file

@ -365,6 +365,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);

View file

@ -329,6 +329,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>();

View file

@ -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)

View file

@ -203,14 +203,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();

View file

@ -36,6 +36,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;
@ -309,6 +311,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
UpdateStorageBuffers();
bool usesGlobalMemory = _globalMemoryUseMask != 0;
if (usesGlobalMemory)
{
_channel.BufferManager.SynchronizeGraphicsStorageBuffers(_globalMemoryUseMask, _globalMemoryWriteMask);
}
if (!_channel.TextureManager.CommitGraphicsBindings(_shaderSpecState) || (buffers.HasUnalignedStorageBuffers != hasUnaligned))
{
_currentSpecState.SetHasUnalignedStorageBuffer(buffers.HasUnalignedStorageBuffers);
@ -317,6 +326,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
_channel.BufferManager.CommitGraphicsBindings();
if (usesGlobalMemory)
{
_channel.BufferManager.UpdatePageTable();
}
}
/// <summary>
@ -1295,9 +1309,27 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
UpdateShaderBindings(gs.Bindings);
_globalMemoryUseMask = 0;
_globalMemoryWriteMask = 0;
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
{
_currentProgramInfo[stageIndex] = gs.Shaders[stageIndex + 1]?.Info;
ShaderProgramInfo info = gs.Shaders[stageIndex + 1]?.Info;
_currentProgramInfo[stageIndex] = info;
if (info != null)
{
if (info.UsesGlobalMemory)
{
_globalMemoryUseMask |= 1u << stageIndex;
}
if (info.UsesGlobalMemoryWrite)
{
_globalMemoryWriteMask |= 1u << stageIndex;
}
}
}
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);

View file

@ -66,6 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
private bool _syncActionRegistered;
private int _referenceCount = 1;
private ulong _hostGpuAddress;
/// <summary>
/// Creates a new instance of the buffer.
@ -167,6 +168,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>

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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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.");
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
@ -379,6 +383,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
lock (_pageTable)
{
if (kind == PteKind.Pitch)
{
AddMapping(va, size);
}
MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size));
for (ulong offset = 0; offset < size; offset += PageSize)
@ -397,6 +406,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
lock (_pageTable)
{
RemoveMapping(va, size);
// Event handlers are not expected to be thread safe.
MemoryUnmapped?.Invoke(this, new UnmapEventArgs(va, size));
@ -677,5 +688,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();
}
}
}
}

View file

@ -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>
@ -775,6 +785,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
textures,
images,
dataInfo.Stage,
dataInfo.UsesGlobalMemory,
dataInfo.UsesGlobalMemoryWrite,
dataInfo.UsesInstanceId,
dataInfo.UsesDrawParameters,
dataInfo.UsesRtLayer,
@ -801,6 +813,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;

View file

@ -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.
}
}
}

View file

@ -73,6 +73,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());

View file

@ -99,6 +99,11 @@ namespace Ryujinx.Graphics.OpenGL
return Buffer.GetData(this, buffer, offset, size);
}
public ulong GetBufferGpuAddress(BufferHandle buffer)
{
return Buffer.GetGpuAddress(buffer);
}
public Capabilities GetCapabilities()
{
return new Capabilities(

View file

@ -1537,6 +1537,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;

View file

@ -59,6 +59,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");
}
}
context.AppendLine("#pragma optionNV(fastmath off)");
context.AppendLine();
@ -241,8 +257,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
bool isFragment = context.Config.Stage == ShaderStage.Fragment;
if (isFragment || context.Config.Stage == ShaderStage.Compute || context.Config.Stage == ShaderStage.Vertex)
{
if (isFragment && context.Config.GpuAccessor.QueryEarlyZForce())
{
context.AppendLine("layout(early_fragment_tests) in;");
@ -260,7 +274,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
scaleElements++; // Also includes render target scale, for gl_FragCoord.
}
DeclareSupportUniformBlock(context, context.Config.Stage, scaleElements);
DeclareSupportUniformBlock(context, info, context.Config.Stage, scaleElements);
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0)
{
@ -268,10 +282,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine();
}
}
else if (isFragment || context.Config.Stage == ShaderStage.Vertex)
else
{
DeclareSupportUniformBlock(context, context.Config.Stage, 0);
}
DeclareSupportUniformBlock(context, info, context.Config.Stage, 0);
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0)
@ -284,6 +297,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");
@ -672,9 +692,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine($"layout (location = {location}) patch out vec4 {name};");
}
private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)
private static void DeclareSupportUniformBlock(CodeGenContext context, StructuredProgramInfo info, ShaderStage stage, int scaleElements)
{
bool needsSupportBlock = stage == ShaderStage.Fragment ||
(info.HelperFunctionsMask & HelperFunctionsMask.GlobalMemory) != 0 ||
(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable());
if (!needsSupportBlock && scaleElements == 0)
@ -700,6 +721,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
}
context.AppendLine($"float {DefaultNames.SupportBlockRenderScaleName}[{SupportBuffer.RenderScaleMaxCount}];");
context.AppendLine($"uvec4 {DefaultNames.SupportBlockPageTableBasePointerName};");
context.LeaveScope(";");
context.AppendLine();

View file

@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public const string SupportBlockViewportInverse = "s_viewport_inverse";
public const string SupportBlockFragmentScaleCount = "s_frag_scale_count";
public const string SupportBlockRenderScaleName = "s_render_scale";
public const string SupportBlockPageTableBasePointerName = "s_page_table";
public const string BlockSuffix = "block";

View file

@ -0,0 +1,36 @@
uvec2 Helper_TranslateAddress(uvec2 address)
{
uvec4* buffer_regions = (uvec4*)packPtr(s_page_table.xy);
uint64_t address64 = packUint2x32(address);
uint count = buffer_regions[0].x;
uint left = 0;
uint right = count;
while (left != right)
{
uint middle = left + ((right - left) >> 1);
uint offset = middle * 2;
uvec4 guest_info = buffer_regions[1 + offset];
uvec4 host_info = buffer_regions[2 + offset];
uint64_t start_address = packUint2x32(guest_info.xy);
uint64_t end_address = packUint2x32(guest_info.zw);
if (address64 >= start_address && address64 < end_address)
{
uint64_t host_address = packUint2x32(host_info.xy);
return unpackUint2x32((address64 - start_address) + host_address);
}
if (address64 < start_address)
{
right = middle;
}
else
{
left = middle + 1;
}
}
return uvec2(0, 0);
}

View file

@ -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;
}

View file

@ -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";
}
}

View file

@ -87,6 +87,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
switch (memRegion)
{
case Instruction.MrGlobal: args += LoadGlobal(context, operation); break;
case Instruction.MrShared: args += LoadShared(context, operation); break;
case Instruction.MrStorage: args += LoadStorage(context, operation); break;
@ -170,6 +171,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.LoadConstant:
return LoadConstant(context, operation);
case Instruction.LoadGlobal:
return LoadGlobal(context, operation);
case Instruction.LoadLocal:
return LoadLocal(context, operation);
@ -194,6 +198,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.StoreAttribute:
return StoreAttribute(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);

View file

@ -83,6 +83,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.IsNan, InstType.CallUnary, "isnan");
Add(Instruction.LoadAttribute, InstType.Special);
Add(Instruction.LoadConstant, InstType.Special);
Add(Instruction.LoadGlobal, InstType.Special);
Add(Instruction.LoadLocal, InstType.Special);
Add(Instruction.LoadShared, InstType.Special);
Add(Instruction.LoadStorage, InstType.Special);
@ -118,6 +119,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.Sine, InstType.CallUnary, "sin");
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
Add(Instruction.StoreAttribute, InstType.Special);
Add(Instruction.StoreGlobal, InstType.Special);
Add(Instruction.StoreLocal, InstType.Special);
Add(Instruction.StoreShared, InstType.Special);
Add(Instruction.StoreShared16, InstType.Special);

View file

@ -1,5 +1,6 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation;
using System;
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
@ -238,6 +239,24 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
}
}
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);
@ -345,6 +364,44 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return $"{attrName} = {value}";
}
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));
VariableType srcType = OperandManager.GetNodeDestType(context, src3);
string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.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);

View file

@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public int InputVertices { get; }
public Dictionary<int, Instruction> UniformBuffers { get; } = new Dictionary<int, Instruction>();
public Instruction PageTablePointerType { get; set; }
public Instruction SupportBuffer { get; set; }
public Instruction UniformBuffersArray { get; set; }
public Instruction StorageBuffersArray { get; set; }

View file

@ -101,7 +101,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
DeclareLocalMemory(context, localMemorySize);
}
DeclareSupportBuffer(context);
DeclareSupportBuffer(context, info);
DeclareUniformBuffers(context, context.Config.GetConstantBufferDescriptors());
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
DeclareSamplers(context, context.Config.GetTextureDescriptors());
@ -133,9 +133,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return variable;
}
private static void DeclareSupportBuffer(CodeGenContext context)
private static void DeclareSupportBuffer(CodeGenContext context, StructuredProgramInfo info)
{
if (!context.Config.Stage.SupportsRenderScale() && !(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
if (!context.Config.Stage.SupportsRenderScale() &&
(info.HelperFunctionsMask & HelperFunctionsMask.GlobalMemory) == 0 &&
!(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable()))
{
return;
}
@ -143,17 +145,26 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var isBgraArrayType = context.TypeArray(context.TypeU32(), context.Constant(context.TypeU32(), SupportBuffer.FragmentIsBgraCount));
var viewportInverseVectorType = context.TypeVector(context.TypeFP32(), 4);
var renderScaleArrayType = context.TypeArray(context.TypeFP32(), context.Constant(context.TypeU32(), SupportBuffer.RenderScaleMaxCount));
var pageTablePointerVectorType = context.TypeVector(context.TypeU32(), 4);
context.Decorate(isBgraArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
context.Decorate(renderScaleArrayType, Decoration.ArrayStride, (LiteralInteger)SupportBuffer.FieldSize);
var supportBufferStructType = context.TypeStruct(false, context.TypeU32(), isBgraArrayType, viewportInverseVectorType, context.TypeS32(), renderScaleArrayType);
var supportBufferStructType = context.TypeStruct(
false,
context.TypeU32(),
isBgraArrayType,
viewportInverseVectorType,
context.TypeS32(),
renderScaleArrayType,
pageTablePointerVectorType);
context.MemberDecorate(supportBufferStructType, 0, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentAlphaTestOffset);
context.MemberDecorate(supportBufferStructType, 1, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentIsBgraOffset);
context.MemberDecorate(supportBufferStructType, 2, Decoration.Offset, (LiteralInteger)SupportBuffer.ViewportInverseOffset);
context.MemberDecorate(supportBufferStructType, 3, Decoration.Offset, (LiteralInteger)SupportBuffer.FragmentRenderScaleCountOffset);
context.MemberDecorate(supportBufferStructType, 4, Decoration.Offset, (LiteralInteger)SupportBuffer.GraphicsRenderScaleOffset);
context.MemberDecorate(supportBufferStructType, 5, Decoration.Offset, (LiteralInteger)SupportBuffer.PageTableBasePointerOffset);
context.Decorate(supportBufferStructType, Decoration.Block);
var supportBufferPointerType = context.TypePointer(StorageClass.Uniform, supportBufferStructType);
@ -165,6 +176,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddGlobalVariable(supportBufferVariable);
context.SupportBuffer = supportBufferVariable;
if ((info.HelperFunctionsMask & HelperFunctionsMask.GlobalMemory) != 0)
{
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 DeclareUniformBuffers(CodeGenContext context, BufferDescriptor[] descriptors)

View file

@ -97,6 +97,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.IsNan, GenerateIsNan);
Add(Instruction.LoadAttribute, GenerateLoadAttribute);
Add(Instruction.LoadConstant, GenerateLoadConstant);
Add(Instruction.LoadGlobal, GenerateLoadGlobal);
Add(Instruction.LoadLocal, GenerateLoadLocal);
Add(Instruction.LoadShared, GenerateLoadShared);
Add(Instruction.LoadStorage, GenerateLoadStorage);
@ -132,6 +133,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
Add(Instruction.Sine, GenerateSine);
Add(Instruction.SquareRoot, GenerateSquareRoot);
Add(Instruction.StoreAttribute, GenerateStoreAttribute);
Add(Instruction.StoreGlobal, GenerateStoreGlobal);
// Add(Instruction.StoreGlobal16, GenerateStoreGlobal16);
// Add(Instruction.StoreGlobal8, GenerateStoreGlobal8);
Add(Instruction.StoreLocal, GenerateStoreLocal);
Add(Instruction.StoreShared, GenerateStoreShared);
Add(Instruction.StoreShared16, GenerateStoreShared16);
@ -954,6 +958,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return new OperationResult(AggregateType.FP32, value);
}
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);
@ -1370,6 +1382,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return OperationResult.Invalid;
}
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);
@ -1930,7 +1966,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction elemPointer;
Instruction mr = operation.Inst & Instruction.MrMask;
if (mr == Instruction.MrStorage)
if (mr == Instruction.MrGlobal)
{
elemPointer = GetGlobalElemPointer(context, operation, context.TypeU32());
}
else if (mr == Instruction.MrStorage)
{
elemPointer = GetStorageElemPointer(context, operation);
}
@ -1958,7 +1998,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction elemPointer;
Instruction mr = operation.Inst & Instruction.MrMask;
if (mr == Instruction.MrStorage)
if (mr == Instruction.MrGlobal)
{
elemPointer = GetGlobalElemPointer(context, operation, context.TypeU32());
}
else if (mr == Instruction.MrStorage)
{
elemPointer = GetStorageElemPointer(context, operation);
}
@ -2041,6 +2085,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.SupportBuffer, 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 SpvInstruction GetStorageElemPointer(CodeGenContext context, AstOperation operation)
{
var sbVariable = context.StorageBuffersArray;

View file

@ -94,6 +94,16 @@ 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");
}
context.AddExtension("SPV_KHR_shader_ballot");
context.AddExtension("SPV_KHR_subgroup_vote");
Declarations.DeclareAll(context, info);
if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0)

View file

@ -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);

View file

@ -16,6 +16,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" />

View file

@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Shader
public ReadOnlyCollection<TextureDescriptor> Images { 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; }
@ -23,6 +25,8 @@ namespace Ryujinx.Graphics.Shader
TextureDescriptor[] textures,
TextureDescriptor[] images,
ShaderStage stage,
bool usesGlobalMemory,
bool usesGlobalMemoryWrite,
bool usesInstanceId,
bool usesDrawParameters,
bool usesRtLayer,
@ -35,6 +39,8 @@ namespace Ryujinx.Graphics.Shader
Images = Array.AsReadOnly(images);
Stage = stage;
UsesGlobalMemory = usesGlobalMemory;
UsesGlobalMemoryWrite = usesGlobalMemoryWrite;
UsesInstanceId = usesInstanceId;
UsesDrawParameters = usesDrawParameters;
UsesRtLayer = usesRtLayer;

View file

@ -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
}
}

View file

@ -223,6 +223,35 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
case Instruction.AtomicMinS32 | Instruction.MrStorage:
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
break;
case Instruction.AtomicAdd | Instruction.MrGlobal:
case Instruction.AtomicAnd | Instruction.MrGlobal:
case Instruction.AtomicCompareAndSwap | Instruction.MrGlobal:
case Instruction.AtomicMaxS32 | Instruction.MrGlobal:
case Instruction.AtomicMaxU32 | Instruction.MrGlobal:
case Instruction.AtomicMinS32 | Instruction.MrGlobal:
case Instruction.AtomicMinU32 | Instruction.MrGlobal:
case Instruction.AtomicOr | Instruction.MrGlobal:
case Instruction.AtomicSwap | Instruction.MrGlobal:
case Instruction.AtomicXor | Instruction.MrGlobal:
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;

View file

@ -22,6 +22,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.
@ -45,6 +46,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);
}
public Vector4<int> FragmentAlphaTest;
@ -54,5 +56,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;
}
}

View file

@ -18,10 +18,12 @@ namespace Ryujinx.Graphics.Shader.Translation
Bindless = 1 << 2,
InstanceId = 1 << 3,
DrawParameters = 1 << 4,
RtLayer = 1 << 5,
CbIndexing = 1 << 6,
IaIndexing = 1 << 7,
OaIndexing = 1 << 8,
FixedFuncAttr = 1 << 9
GlobalMemory = 1 << 5,
GlobalMemoryWrite = 1 << 6,
RtLayer = 1 << 7,
CbIndexing = 1 << 8,
IaIndexing = 1 << 9,
OaIndexing = 1 << 10,
FixedFuncAttr = 1 << 11
}
}

View file

@ -60,10 +60,10 @@ namespace Ryujinx.Graphics.Shader.Translation
nextNode = node.Next;
}
else if (UsesGlobalMemory(operation.Inst))
/* else if (UsesGlobalMemory(operation.Inst))
{
nextNode = RewriteGlobalAccess(node, config)?.Next ?? nextNode;
}
} */
node = nextNode;
}

View file

@ -277,7 +277,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(0xf, 0) << (bit * 4);
passthroughAttributes &= ~(1 << bit);
}
if (UsedOutputAttributesPerPatch.Count != 0)
{
@ -706,6 +714,8 @@ namespace Ryujinx.Graphics.Shader.Translation
GetTextureDescriptors(),
GetImageDescriptors(),
Stage,
UsedFeatures.HasFlag(FeatureFlags.GlobalMemory),
UsedFeatures.HasFlag(FeatureFlags.GlobalMemoryWrite),
UsedFeatures.HasFlag(FeatureFlags.InstanceId),
UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
UsedFeatures.HasFlag(FeatureFlags.RtLayer),

View file

@ -136,12 +136,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;
@ -182,6 +176,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);

View file

@ -78,11 +78,19 @@ namespace Ryujinx.Graphics.Vulkan
{
usage |= BufferUsageFlags.ConditionalRenderingBitExt;
}
else if (gd.Capabilities.SupportsIndirectParameters)
else
{
if (gd.Capabilities.SupportsIndirectParameters)
{
usage |= BufferUsageFlags.IndirectBufferBit;
}
if (gd.Capabilities.SupportsBufferDeviceAddress)
{
usage |= BufferUsageFlags.ShaderDeviceAddressBitExt;
}
}
var bufferCreateInfo = new BufferCreateInfo()
{
SType = StructureType.BufferCreateInfo,
@ -326,6 +334,16 @@ namespace Ryujinx.Graphics.Vulkan
return ReadOnlySpan<byte>.Empty;
}
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);

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;

View file

@ -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),

View file

@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsGeometryShaderPassthrough;
public readonly bool SupportsSubgroupSizeControl;
public readonly bool SupportsShaderInt8;
public readonly bool SupportsBufferDeviceAddress;
public readonly bool SupportsConditionalRendering;
public readonly bool SupportsExtendedDynamicState;
public readonly bool SupportsMultiView;
@ -32,6 +33,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsGeometryShaderPassthrough,
bool supportsSubgroupSizeControl,
bool supportsShaderInt8,
bool supportsBufferDeviceAddress,
bool supportsConditionalRendering,
bool supportsExtendedDynamicState,
bool supportsMultiView,
@ -52,6 +54,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
SupportsSubgroupSizeControl = supportsSubgroupSizeControl;
SupportsShaderInt8 = supportsShaderInt8;
SupportsBufferDeviceAddress = supportsBufferDeviceAddress;
SupportsConditionalRendering = supportsConditionalRendering;
SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsMultiView = supportsMultiView;

View file

@ -1222,6 +1222,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;

View file

@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Vulkan
public static string[] DesirableExtensions { get; } = new string[]
{
ExtBufferDeviceAddress.ExtensionName,
ExtConditionalRendering.ExtensionName,
ExtExtendedDynamicState.ExtensionName,
KhrDrawIndirectCount.ExtensionName,
@ -491,6 +492,20 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresSubgroupSizeControl;
}
PhysicalDeviceBufferDeviceAddressFeaturesEXT featuresBufferDeviceAddress;
if (supportedExtensions.Contains(ExtBufferDeviceAddress.ExtensionName))
{
featuresBufferDeviceAddress = new PhysicalDeviceBufferDeviceAddressFeaturesEXT()
{
SType = StructureType.PhysicalDeviceBufferAddressFeaturesExt,
PNext = pExtendedFeatures,
BufferDeviceAddress = true
};
pExtendedFeatures = &featuresBufferDeviceAddress;
}
var enabledExtensions = RequiredExtensions.Union(DesirableExtensions.Intersect(supportedExtensions)).ToArray();
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];

View file

@ -205,6 +205,7 @@ namespace Ryujinx.Graphics.Vulkan
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
supportedExtensions.Contains("VK_EXT_subgroup_size_control"),
featuresShaderInt8.ShaderInt8,
supportedExtensions.Contains(ExtBufferDeviceAddress.ExtensionName),
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName),
supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport,
@ -344,6 +345,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 =

View file

@ -427,6 +427,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++)

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);