| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- using Ryujinx.Common.Logging;
- using Ryujinx.Common.Memory;
- using Ryujinx.Graphics.Device;
- using Ryujinx.Graphics.GAL;
- using Ryujinx.Graphics.Gpu.Engine.GPFifo;
- using Ryujinx.Graphics.Gpu.Engine.Threed;
- using Ryujinx.Graphics.Gpu.Engine.Types;
- using Ryujinx.Graphics.Gpu.Memory;
- using Ryujinx.Memory.Range;
- using System;
- using System.Collections.Generic;
- namespace Ryujinx.Graphics.Gpu.Engine.MME
- {
- /// <summary>
- /// Macro High-level emulation.
- /// </summary>
- class MacroHLE : IMacroEE
- {
- private const int ColorLayerCountOffset = 0x818;
- private const int ColorStructSize = 0x40;
- private const int ZetaLayerCountOffset = 0x1230;
- private const int UniformBufferBindVertexOffset = 0x2410;
- private const int FirstVertexOffset = 0x1434;
- private const int IndirectIndexedDataEntrySize = 0x14;
- private const int LogicOpOffset = 0x19c4;
- private const int ShaderIdScratchOffset = 0x3470;
- private const int ShaderAddressScratchOffset = 0x3488;
- private const int UpdateConstantBufferAddressesBase = 0x34a8;
- private const int UpdateConstantBufferSizesBase = 0x34bc;
- private const int UpdateConstantBufferAddressCbu = 0x3460;
- private readonly GPFifoProcessor _processor;
- private readonly MacroHLEFunctionName _functionName;
- /// <summary>
- /// Arguments FIFO.
- /// </summary>
- public Queue<FifoWord> Fifo { get; }
- /// <summary>
- /// Creates a new instance of the HLE macro handler.
- /// </summary>
- /// <param name="processor">GPU GP FIFO command processor</param>
- /// <param name="functionName">Name of the HLE macro function to be called</param>
- public MacroHLE(GPFifoProcessor processor, MacroHLEFunctionName functionName)
- {
- _processor = processor;
- _functionName = functionName;
- Fifo = new Queue<FifoWord>();
- }
- /// <summary>
- /// Executes a macro program until it exits.
- /// </summary>
- /// <param name="code">Code of the program to execute</param>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">Optional argument passed to the program, 0 if not used</param>
- public void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0)
- {
- switch (_functionName)
- {
- case MacroHLEFunctionName.BindShaderProgram:
- BindShaderProgram(state, arg0);
- break;
- case MacroHLEFunctionName.ClearColor:
- ClearColor(state, arg0);
- break;
- case MacroHLEFunctionName.ClearDepthStencil:
- ClearDepthStencil(state, arg0);
- break;
- case MacroHLEFunctionName.DrawArraysInstanced:
- DrawArraysInstanced(state, arg0);
- break;
- case MacroHLEFunctionName.DrawElements:
- DrawElements(state, arg0);
- break;
- case MacroHLEFunctionName.DrawElementsInstanced:
- DrawElementsInstanced(state, arg0);
- break;
- case MacroHLEFunctionName.DrawElementsIndirect:
- DrawElementsIndirect(state, arg0);
- break;
- case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
- MultiDrawElementsIndirectCount(state, arg0);
- break;
- case MacroHLEFunctionName.UpdateBlendState:
- UpdateBlendState(state, arg0);
- break;
- case MacroHLEFunctionName.UpdateColorMasks:
- UpdateColorMasks(state, arg0);
- break;
- case MacroHLEFunctionName.UpdateUniformBufferState:
- UpdateUniformBufferState(state, arg0);
- break;
- case MacroHLEFunctionName.UpdateUniformBufferStateCbu:
- UpdateUniformBufferStateCbu(state, arg0);
- break;
- case MacroHLEFunctionName.UpdateUniformBufferStateCbuV2:
- UpdateUniformBufferStateCbuV2(state, arg0);
- break;
- default:
- throw new NotImplementedException(_functionName.ToString());
- }
- // It should be empty at this point, but clear it just to be safe.
- Fifo.Clear();
- }
- /// <summary>
- /// Binds a shader program with the index in arg0.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void BindShaderProgram(IDeviceState state, int arg0)
- {
- int scratchOffset = ShaderIdScratchOffset + arg0 * 4;
- int lastId = state.Read(scratchOffset);
- int id = FetchParam().Word;
- int offset = FetchParam().Word;
- if (lastId == id)
- {
- FetchParam();
- FetchParam();
- return;
- }
- _processor.ThreedClass.SetShaderOffset(arg0, (uint)offset);
- // Removes overflow on the method address into the increment portion.
- // Present in the original macro.
- int addrMask = unchecked((int)0xfffc0fff) << 2;
- state.Write(scratchOffset & addrMask, id);
- state.Write((ShaderAddressScratchOffset + arg0 * 4) & addrMask, offset);
- int stage = FetchParam().Word;
- uint cbAddress = (uint)FetchParam().Word;
- _processor.ThreedClass.UpdateUniformBufferState(65536, cbAddress >> 24, cbAddress << 8);
- int stageOffset = (stage & 0x7f) << 3;
- state.Write((UniformBufferBindVertexOffset + stageOffset * 4) & addrMask, 17);
- }
- /// <summary>
- /// Updates uniform buffer state for update or bind.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void UpdateUniformBufferState(IDeviceState state, int arg0)
- {
- uint address = (uint)state.Read(UpdateConstantBufferAddressesBase + arg0 * 4);
- int size = state.Read(UpdateConstantBufferSizesBase + arg0 * 4);
- _processor.ThreedClass.UpdateUniformBufferState(size, address >> 24, address << 8);
- }
- /// <summary>
- /// Updates uniform buffer state for update.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void UpdateUniformBufferStateCbu(IDeviceState state, int arg0)
- {
- uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
- UniformBufferState ubState = new()
- {
- Address = new()
- {
- High = address >> 24,
- Low = address << 8
- },
- Size = 24320,
- Offset = arg0 << 2
- };
- _processor.ThreedClass.UpdateUniformBufferState(ubState);
- }
- /// <summary>
- /// Updates uniform buffer state for update.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void UpdateUniformBufferStateCbuV2(IDeviceState state, int arg0)
- {
- uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
- UniformBufferState ubState = new()
- {
- Address = new()
- {
- High = address >> 24,
- Low = address << 8
- },
- Size = 28672,
- Offset = arg0 << 2
- };
- _processor.ThreedClass.UpdateUniformBufferState(ubState);
- }
- /// <summary>
- /// Updates blend enable using the given argument.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void UpdateBlendState(IDeviceState state, int arg0)
- {
- state.Write(LogicOpOffset, 0);
- Array8<Boolean32> enable = new();
- for (int i = 0; i < 8; i++)
- {
- enable[i] = new Boolean32((uint)(arg0 >> (i + 8)) & 1);
- }
- _processor.ThreedClass.UpdateBlendEnable(ref enable);
- }
- /// <summary>
- /// Updates color masks using the given argument and three pushed arguments.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void UpdateColorMasks(IDeviceState state, int arg0)
- {
- Array8<RtColorMask> masks = new();
- int index = 0;
- for (int i = 0; i < 4; i++)
- {
- masks[index++] = new RtColorMask((uint)arg0 & 0x1fff);
- masks[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff);
- if (i != 3)
- {
- arg0 = FetchParam().Word;
- }
- }
- _processor.ThreedClass.UpdateColorMasks(ref masks);
- }
- /// <summary>
- /// Clears one bound color target.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void ClearColor(IDeviceState state, int arg0)
- {
- int index = (arg0 >> 6) & 0xf;
- int layerCount = state.Read(ColorLayerCountOffset + index * ColorStructSize);
- _processor.ThreedClass.Clear(arg0, layerCount);
- }
- /// <summary>
- /// Clears the current depth-stencil target.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void ClearDepthStencil(IDeviceState state, int arg0)
- {
- int layerCount = state.Read(ZetaLayerCountOffset);
- _processor.ThreedClass.Clear(arg0, layerCount);
- }
- /// <summary>
- /// Performs a draw.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void DrawArraysInstanced(IDeviceState state, int arg0)
- {
- var topology = (PrimitiveTopology)arg0;
- var count = FetchParam();
- var instanceCount = FetchParam();
- var firstVertex = FetchParam();
- var firstInstance = FetchParam();
- if (ShouldSkipDraw(state, instanceCount.Word))
- {
- return;
- }
- _processor.ThreedClass.Draw(
- topology,
- count.Word,
- instanceCount.Word,
- 0,
- firstVertex.Word,
- firstInstance.Word,
- indexed: false);
- }
- /// <summary>
- /// Performs a indexed draw.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void DrawElements(IDeviceState state, int arg0)
- {
- var topology = (PrimitiveTopology)arg0;
- var indexAddressHigh = FetchParam();
- var indexAddressLow = FetchParam();
- var indexType = FetchParam();
- var firstIndex = 0;
- var indexCount = FetchParam();
- _processor.ThreedClass.UpdateIndexBuffer(
- (uint)indexAddressHigh.Word,
- (uint)indexAddressLow.Word,
- (IndexType)indexType.Word);
- _processor.ThreedClass.Draw(
- topology,
- indexCount.Word,
- 1,
- firstIndex,
- state.Read(FirstVertexOffset),
- 0,
- indexed: true);
- }
- /// <summary>
- /// Performs a indexed draw.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void DrawElementsInstanced(IDeviceState state, int arg0)
- {
- var topology = (PrimitiveTopology)arg0;
- var count = FetchParam();
- var instanceCount = FetchParam();
- var firstIndex = FetchParam();
- var firstVertex = FetchParam();
- var firstInstance = FetchParam();
- if (ShouldSkipDraw(state, instanceCount.Word))
- {
- return;
- }
- _processor.ThreedClass.Draw(
- topology,
- count.Word,
- instanceCount.Word,
- firstIndex.Word,
- firstVertex.Word,
- firstInstance.Word,
- indexed: true);
- }
- /// <summary>
- /// Performs a indirect indexed draw, with parameters from a GPU buffer.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void DrawElementsIndirect(IDeviceState state, int arg0)
- {
- var topology = (PrimitiveTopology)arg0;
- var count = FetchParam();
- var instanceCount = FetchParam();
- var firstIndex = FetchParam();
- var firstVertex = FetchParam();
- var firstInstance = FetchParam();
- ulong indirectBufferGpuVa = count.GpuVa;
- var bufferCache = _processor.MemoryManager.Physical.BufferCache;
- bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
- if (useBuffer)
- {
- int indexCount = firstIndex.Word + count.Word;
- _processor.ThreedClass.DrawIndirect(
- topology,
- new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
- default,
- 1,
- IndirectIndexedDataEntrySize,
- indexCount,
- IndirectDrawType.DrawIndexedIndirect);
- }
- else
- {
- if (ShouldSkipDraw(state, instanceCount.Word))
- {
- return;
- }
- _processor.ThreedClass.Draw(
- topology,
- count.Word,
- instanceCount.Word,
- firstIndex.Word,
- firstVertex.Word,
- firstInstance.Word,
- indexed: true);
- }
- }
- /// <summary>
- /// Performs a indirect indexed multi-draw, with parameters from a GPU buffer.
- /// </summary>
- /// <param name="state">GPU state at the time of the call</param>
- /// <param name="arg0">First argument of the call</param>
- private void MultiDrawElementsIndirectCount(IDeviceState state, int arg0)
- {
- int arg1 = FetchParam().Word;
- int arg2 = FetchParam().Word;
- int arg3 = FetchParam().Word;
- int startDraw = arg0;
- int endDraw = arg1;
- var topology = (PrimitiveTopology)arg2;
- int paddingWords = arg3;
- int stride = paddingWords * 4 + 0x14;
- ulong parameterBufferGpuVa = FetchParam().GpuVa;
- int maxDrawCount = endDraw - startDraw;
- if (startDraw != 0)
- {
- int drawCount = _processor.MemoryManager.Read<int>(parameterBufferGpuVa, tracked: true);
- // Calculate maximum draw count based on the previous draw count and current draw count.
- if ((uint)drawCount <= (uint)startDraw)
- {
- // The start draw is past our total draw count, so all draws were already performed.
- maxDrawCount = 0;
- }
- else
- {
- // Perform just the missing number of draws.
- maxDrawCount = (int)Math.Min((uint)maxDrawCount, (uint)(drawCount - startDraw));
- }
- }
- if (maxDrawCount == 0)
- {
- Fifo.Clear();
- return;
- }
- ulong indirectBufferGpuVa = 0;
- int indexCount = 0;
- for (int i = 0; i < maxDrawCount; i++)
- {
- var count = FetchParam();
- #pragma warning disable IDE0059 // Remove unnecessary value assignment
- var instanceCount = FetchParam();
- var firstIndex = FetchParam();
- var firstVertex = FetchParam();
- var firstInstance = FetchParam();
- #pragma warning restore IDE0059
- if (i == 0)
- {
- indirectBufferGpuVa = count.GpuVa;
- }
- indexCount = Math.Max(indexCount, count.Word + firstIndex.Word);
- if (i != maxDrawCount - 1)
- {
- for (int j = 0; j < paddingWords; j++)
- {
- FetchParam();
- }
- }
- }
- var bufferCache = _processor.MemoryManager.Physical.BufferCache;
- ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
- MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
- MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
- _processor.ThreedClass.DrawIndirect(
- topology,
- indirectBufferRange,
- parameterBufferRange,
- maxDrawCount,
- stride,
- indexCount,
- Threed.IndirectDrawType.DrawIndexedIndirectCount);
- }
- /// <summary>
- /// Checks if the draw should be skipped, because the masked instance count is zero.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="instanceCount">Draw instance count</param>
- /// <returns>True if the draw should be skipped, false otherwise</returns>
- private static bool ShouldSkipDraw(IDeviceState state, int instanceCount)
- {
- return (Read(state, 0xd1b) & instanceCount) == 0;
- }
- /// <summary>
- /// Fetches a arguments from the arguments FIFO.
- /// </summary>
- /// <returns>The call argument, or a 0 value with null address if the FIFO is empty</returns>
- private FifoWord FetchParam()
- {
- if (!Fifo.TryDequeue(out var value))
- {
- Logger.Warning?.Print(LogClass.Gpu, "Macro attempted to fetch an inexistent argument.");
- return new FifoWord(0UL, 0);
- }
- return value;
- }
- /// <summary>
- /// Reads data from a GPU register.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="reg">Register offset to read</param>
- /// <returns>GPU register value</returns>
- private static int Read(IDeviceState state, int reg)
- {
- return state.Read(reg * 4);
- }
- }
- }
|