2023-07-28 20:39:14 +00:00
|
|
|
using Ryujinx.Common.Logging;
|
2024-08-31 20:42:56 +00:00
|
|
|
using Ryujinx.Graphics.GAL;
|
|
|
|
using Ryujinx.Graphics.Shader;
|
2023-07-28 01:51:20 +00:00
|
|
|
using SharpMetal.Foundation;
|
|
|
|
using SharpMetal.Metal;
|
2023-08-02 23:56:59 +00:00
|
|
|
using SharpMetal.QuartzCore;
|
2024-08-31 20:42:56 +00:00
|
|
|
using System;
|
|
|
|
using System.Runtime.Versioning;
|
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.Metal
|
|
|
|
{
|
2024-06-19 22:13:55 +00:00
|
|
|
public enum EncoderType
|
2024-03-18 18:48:54 +00:00
|
|
|
{
|
|
|
|
Blit,
|
|
|
|
Compute,
|
2024-03-20 02:58:42 +00:00
|
|
|
Render,
|
|
|
|
None
|
2024-03-18 18:48:54 +00:00
|
|
|
}
|
|
|
|
|
2024-08-31 20:42:56 +00:00
|
|
|
[SupportedOSPlatform("macos")]
|
2024-06-30 16:23:53 +00:00
|
|
|
class Pipeline : IPipeline, IEncoderFactory, IDisposable
|
2024-08-31 20:42:56 +00:00
|
|
|
{
|
2024-06-20 23:54:04 +00:00
|
|
|
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
|
|
|
|
|
2023-07-28 02:54:24 +00:00
|
|
|
private readonly MTLDevice _device;
|
2024-06-19 22:13:55 +00:00
|
|
|
private readonly MetalRenderer _renderer;
|
2024-06-20 23:21:06 +00:00
|
|
|
private EncoderStateManager _encoderStateManager;
|
2024-06-20 23:54:04 +00:00
|
|
|
private ulong _byteWeight;
|
2024-06-19 22:13:55 +00:00
|
|
|
|
2024-06-20 23:21:06 +00:00
|
|
|
public MTLCommandBuffer CommandBuffer;
|
2024-06-19 22:13:55 +00:00
|
|
|
|
2024-06-26 21:26:27 +00:00
|
|
|
public IndexBufferPattern QuadsToTrisPattern;
|
|
|
|
public IndexBufferPattern TriFanToTrisPattern;
|
|
|
|
|
2024-06-20 23:54:04 +00:00
|
|
|
internal CommandBufferScoped? PreloadCbs { get; private set; }
|
2024-06-20 23:21:06 +00:00
|
|
|
internal CommandBufferScoped Cbs { get; private set; }
|
2024-06-30 16:23:53 +00:00
|
|
|
internal CommandBufferEncoder Encoders => Cbs.Encoders;
|
|
|
|
internal EncoderType CurrentEncoderType => Encoders.CurrentEncoderType;
|
2024-06-20 23:54:04 +00:00
|
|
|
internal bool RenderPassActive { get; private set; }
|
2023-07-28 01:51:20 +00:00
|
|
|
|
2024-06-20 23:21:06 +00:00
|
|
|
public Pipeline(MTLDevice device, MetalRenderer renderer)
|
2024-08-31 20:42:56 +00:00
|
|
|
{
|
2023-07-28 02:54:24 +00:00
|
|
|
_device = device;
|
2024-06-19 22:13:55 +00:00
|
|
|
_renderer = renderer;
|
2024-08-31 20:42:56 +00:00
|
|
|
|
2024-06-30 16:23:53 +00:00
|
|
|
renderer.CommandBufferPool.Initialize(this);
|
2024-06-19 22:13:55 +00:00
|
|
|
|
|
|
|
CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer;
|
|
|
|
}
|
|
|
|
|
2024-06-20 23:21:06 +00:00
|
|
|
internal void InitEncoderStateManager(BufferManager bufferManager)
|
2024-06-19 22:13:55 +00:00
|
|
|
{
|
|
|
|
_encoderStateManager = new EncoderStateManager(_device, bufferManager, this);
|
2024-06-26 21:26:27 +00:00
|
|
|
|
|
|
|
QuadsToTrisPattern = new IndexBufferPattern(_renderer, 4, 6, 0, [0, 1, 2, 0, 2, 3], 4, false);
|
|
|
|
TriFanToTrisPattern = new IndexBufferPattern(_renderer, 3, 3, 2, [int.MinValue, -1, 0], 1, true);
|
2023-07-28 02:54:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-30 16:23:53 +00:00
|
|
|
public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All, bool endRenderPass = true)
|
2024-05-24 13:09:06 +00:00
|
|
|
{
|
2024-06-30 16:23:53 +00:00
|
|
|
if (endRenderPass && CurrentEncoderType == EncoderType.Render)
|
|
|
|
{
|
|
|
|
EndCurrentPass();
|
|
|
|
}
|
|
|
|
|
2024-06-28 20:14:53 +00:00
|
|
|
return _encoderStateManager.SwapState(state, flags);
|
2024-05-24 13:09:06 +00:00
|
|
|
}
|
|
|
|
|
2024-06-28 20:14:53 +00:00
|
|
|
public PredrawState SavePredrawState()
|
2024-05-27 11:58:03 +00:00
|
|
|
{
|
2024-06-28 20:14:53 +00:00
|
|
|
return _encoderStateManager.SavePredrawState();
|
2024-05-27 11:58:03 +00:00
|
|
|
}
|
|
|
|
|
2024-06-28 20:14:53 +00:00
|
|
|
public void RestorePredrawState(PredrawState state)
|
2024-05-24 13:09:06 +00:00
|
|
|
{
|
2024-06-28 20:14:53 +00:00
|
|
|
_encoderStateManager.RestorePredrawState(state);
|
2024-05-24 13:09:06 +00:00
|
|
|
}
|
|
|
|
|
2024-05-27 13:47:50 +00:00
|
|
|
public void SetClearLoadAction(bool clear)
|
|
|
|
{
|
|
|
|
_encoderStateManager.SetClearLoadAction(clear);
|
|
|
|
}
|
|
|
|
|
2024-06-19 22:14:23 +00:00
|
|
|
public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false)
|
2023-08-03 20:47:10 +00:00
|
|
|
{
|
2024-06-30 16:23:53 +00:00
|
|
|
MTLRenderCommandEncoder renderCommandEncoder = Cbs.Encoders.EnsureRenderEncoder();
|
2024-05-19 06:08:12 +00:00
|
|
|
|
2024-06-19 22:14:23 +00:00
|
|
|
if (forDraw)
|
|
|
|
{
|
|
|
|
_encoderStateManager.RebindRenderState(renderCommandEncoder);
|
|
|
|
}
|
2024-05-19 06:08:12 +00:00
|
|
|
|
|
|
|
return renderCommandEncoder;
|
2023-08-03 20:47:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public MTLBlitCommandEncoder GetOrCreateBlitEncoder()
|
|
|
|
{
|
2024-06-30 16:23:53 +00:00
|
|
|
return Cbs.Encoders.EnsureBlitEncoder();
|
2023-08-03 20:47:10 +00:00
|
|
|
}
|
|
|
|
|
2024-06-25 13:25:31 +00:00
|
|
|
public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false)
|
2023-08-03 20:47:10 +00:00
|
|
|
{
|
2024-06-30 16:23:53 +00:00
|
|
|
MTLComputeCommandEncoder computeCommandEncoder = Cbs.Encoders.EnsureComputeEncoder();
|
2023-08-03 20:47:10 +00:00
|
|
|
|
2024-06-25 13:25:31 +00:00
|
|
|
if (forDispatch)
|
|
|
|
{
|
|
|
|
_encoderStateManager.RebindComputeState(computeCommandEncoder);
|
|
|
|
}
|
2024-05-29 15:21:59 +00:00
|
|
|
|
|
|
|
return computeCommandEncoder;
|
2023-08-03 20:47:10 +00:00
|
|
|
}
|
|
|
|
|
2023-07-28 20:23:13 +00:00
|
|
|
public void EndCurrentPass()
|
2023-07-28 02:54:24 +00:00
|
|
|
{
|
2024-06-30 16:23:53 +00:00
|
|
|
Cbs.Encoders.EndCurrentPass();
|
2023-07-28 02:54:24 +00:00
|
|
|
}
|
|
|
|
|
2024-06-30 16:23:53 +00:00
|
|
|
public MTLRenderCommandEncoder CreateRenderCommandEncoder()
|
2023-07-28 02:54:24 +00:00
|
|
|
{
|
2024-06-30 16:23:53 +00:00
|
|
|
return _encoderStateManager.CreateRenderCommandEncoder();
|
2023-07-28 20:23:13 +00:00
|
|
|
}
|
|
|
|
|
2024-06-30 16:23:53 +00:00
|
|
|
public MTLComputeCommandEncoder CreateComputeCommandEncoder()
|
2023-07-29 05:18:51 +00:00
|
|
|
{
|
2024-06-30 16:23:53 +00:00
|
|
|
return _encoderStateManager.CreateComputeCommandEncoder();
|
2023-07-29 05:18:51 +00:00
|
|
|
}
|
|
|
|
|
2024-05-27 13:47:50 +00:00
|
|
|
public void Present(CAMetalDrawable drawable, Texture src, Extents2D srcRegion, Extents2D dstRegion, bool isLinear)
|
2023-07-28 20:23:13 +00:00
|
|
|
{
|
2024-05-19 00:27:27 +00:00
|
|
|
// TODO: Clean this up
|
|
|
|
var textureInfo = new TextureCreateInfo((int)drawable.Texture.Width, (int)drawable.Texture.Height, (int)drawable.Texture.Depth, (int)drawable.Texture.MipmapLevelCount, (int)drawable.Texture.SampleCount, 0, 0, 0, Format.B8G8R8A8Unorm, 0, Target.Texture2D, SwizzleComponent.Red, SwizzleComponent.Green, SwizzleComponent.Blue, SwizzleComponent.Alpha);
|
2024-06-19 22:13:55 +00:00
|
|
|
var dst = new Texture(_device, _renderer, this, textureInfo, drawable.Texture, 0, 0);
|
2024-05-19 00:27:27 +00:00
|
|
|
|
2024-06-25 13:25:31 +00:00
|
|
|
_renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear, true);
|
2023-08-02 23:56:59 +00:00
|
|
|
|
2024-05-24 13:09:06 +00:00
|
|
|
EndCurrentPass();
|
|
|
|
|
2024-06-19 22:13:55 +00:00
|
|
|
Cbs.CommandBuffer.PresentDrawable(drawable);
|
|
|
|
|
|
|
|
CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
2023-08-02 23:56:59 +00:00
|
|
|
|
2024-06-19 22:13:55 +00:00
|
|
|
// TODO: Auto flush counting
|
|
|
|
_renderer.SyncManager.GetAndResetWaitTicks();
|
2024-08-31 20:42:56 +00:00
|
|
|
|
2024-05-25 12:38:39 +00:00
|
|
|
// Cleanup
|
2024-05-27 13:47:50 +00:00
|
|
|
dst.Dispose();
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
2024-06-20 23:54:04 +00:00
|
|
|
public void FlushCommandsIfWeightExceeding(IAuto disposedResource, ulong byteWeight)
|
|
|
|
{
|
|
|
|
bool usedByCurrentCb = disposedResource.HasCommandBufferDependency(Cbs);
|
|
|
|
|
|
|
|
if (PreloadCbs != null && !usedByCurrentCb)
|
|
|
|
{
|
|
|
|
usedByCurrentCb = disposedResource.HasCommandBufferDependency(PreloadCbs.Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (usedByCurrentCb)
|
|
|
|
{
|
|
|
|
// Since we can only free memory after the command buffer that uses a given resource was executed,
|
|
|
|
// keeping the command buffer might cause a high amount of memory to be in use.
|
|
|
|
// To prevent that, we force submit command buffers if the memory usage by resources
|
|
|
|
// in use by the current command buffer is above a given limit, and those resources were disposed.
|
|
|
|
_byteWeight += byteWeight;
|
|
|
|
|
|
|
|
if (_byteWeight >= MinByteWeightForFlush)
|
|
|
|
{
|
|
|
|
FlushCommandsImpl();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-19 22:13:55 +00:00
|
|
|
public void FlushCommandsImpl()
|
|
|
|
{
|
|
|
|
EndCurrentPass();
|
|
|
|
|
2024-06-20 23:54:04 +00:00
|
|
|
_byteWeight = 0;
|
|
|
|
|
|
|
|
if (PreloadCbs != null)
|
|
|
|
{
|
|
|
|
PreloadCbs.Value.Dispose();
|
|
|
|
PreloadCbs = null;
|
|
|
|
}
|
|
|
|
|
2024-06-19 22:13:55 +00:00
|
|
|
CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
|
|
|
_renderer.RegisterFlush();
|
|
|
|
}
|
|
|
|
|
2024-06-29 21:54:28 +00:00
|
|
|
public void Blit(
|
2024-05-27 14:38:00 +00:00
|
|
|
ITexture src,
|
|
|
|
ITexture dst,
|
|
|
|
Extents2D srcRegion,
|
|
|
|
Extents2D dstRegion,
|
2024-06-29 21:54:28 +00:00
|
|
|
bool isDepthOrStencil,
|
2024-05-27 14:38:00 +00:00
|
|
|
bool linearFilter)
|
|
|
|
{
|
2024-06-29 21:54:28 +00:00
|
|
|
if (isDepthOrStencil)
|
|
|
|
{
|
|
|
|
// TODO: Depth & stencil blit!
|
|
|
|
Logger.Warning?.PrintMsg(LogClass.Gpu, "Requested a depth or stencil blit!");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, linearFilter);
|
|
|
|
}
|
2024-05-27 14:38:00 +00:00
|
|
|
}
|
|
|
|
|
2024-05-18 22:54:55 +00:00
|
|
|
public void Barrier()
|
2024-03-20 20:37:08 +00:00
|
|
|
{
|
2024-06-20 23:21:06 +00:00
|
|
|
switch (CurrentEncoderType)
|
2024-05-24 16:37:31 +00:00
|
|
|
{
|
2024-05-29 22:10:00 +00:00
|
|
|
case EncoderType.Render:
|
|
|
|
{
|
|
|
|
var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;
|
|
|
|
MTLRenderStages stages = MTLRenderStages.RenderStageVertex | MTLRenderStages.RenderStageFragment;
|
2024-06-30 16:23:53 +00:00
|
|
|
Encoders.RenderEncoder.MemoryBarrier(scope, stages, stages);
|
2024-05-29 22:10:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EncoderType.Compute:
|
|
|
|
{
|
2024-07-02 16:52:53 +00:00
|
|
|
var scope = MTLBarrierScope.Buffers | MTLBarrierScope.Textures | MTLBarrierScope.RenderTargets;
|
2024-06-30 16:23:53 +00:00
|
|
|
Encoders.ComputeEncoder.MemoryBarrier(scope);
|
2024-05-29 22:10:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2024-05-24 16:37:31 +00:00
|
|
|
}
|
2024-03-20 20:37:08 +00:00
|
|
|
}
|
|
|
|
|
2024-08-31 20:42:56 +00:00
|
|
|
public void ClearBuffer(BufferHandle destination, int offset, int size, uint value)
|
|
|
|
{
|
2023-08-03 20:47:10 +00:00
|
|
|
var blitCommandEncoder = GetOrCreateBlitEncoder();
|
2023-07-28 20:51:07 +00:00
|
|
|
|
2024-06-19 22:13:55 +00:00
|
|
|
var mtlBuffer = _renderer.BufferManager.GetBuffer(destination, offset, size, true).Get(Cbs, offset, size, true).Value;
|
|
|
|
|
2023-07-28 20:51:07 +00:00
|
|
|
// Might need a closer look, range's count, lower, and upper bound
|
|
|
|
// must be a multiple of 4
|
|
|
|
blitCommandEncoder.FillBuffer(mtlBuffer,
|
|
|
|
new NSRange
|
|
|
|
{
|
|
|
|
location = (ulong)offset,
|
|
|
|
length = (ulong)size
|
|
|
|
},
|
|
|
|
(byte)value);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color)
|
|
|
|
{
|
2024-05-24 12:41:51 +00:00
|
|
|
float[] colors = [color.Red, color.Green, color.Blue, color.Alpha];
|
2024-06-21 15:39:27 +00:00
|
|
|
var dst = _encoderStateManager.RenderTargets[index];
|
2024-05-24 12:41:51 +00:00
|
|
|
|
2024-05-30 12:24:05 +00:00
|
|
|
// TODO: Remove workaround for Wonder which has an invalid texture due to unsupported format
|
|
|
|
if (dst == null)
|
|
|
|
{
|
|
|
|
Logger.Warning?.PrintMsg(LogClass.Gpu, "Attempted to clear invalid render target!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-19 22:13:55 +00:00
|
|
|
_renderer.HelperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
2024-05-18 22:54:55 +00:00
|
|
|
public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
2024-08-31 20:42:56 +00:00
|
|
|
{
|
2024-05-30 12:20:16 +00:00
|
|
|
var depthStencil = _encoderStateManager.DepthStencil;
|
|
|
|
|
2024-05-30 12:24:05 +00:00
|
|
|
// TODO: Remove workaround for Wonder which has an invalid texture due to unsupported format
|
|
|
|
if (depthStencil == null)
|
|
|
|
{
|
|
|
|
Logger.Warning?.PrintMsg(LogClass.Gpu, "Attempted to clear invalid depth stencil!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-19 22:13:55 +00:00
|
|
|
_renderer.HelperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask, depthStencil.Width, depthStencil.Height);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void CommandBufferBarrier()
|
|
|
|
{
|
2024-06-27 21:51:12 +00:00
|
|
|
Barrier();
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
2024-06-19 22:13:55 +00:00
|
|
|
public void CopyBuffer(BufferHandle src, BufferHandle dst, int srcOffset, int dstOffset, int size)
|
2024-08-31 20:42:56 +00:00
|
|
|
{
|
2024-06-19 22:13:55 +00:00
|
|
|
var srcBuffer = _renderer.BufferManager.GetBuffer(src, srcOffset, size, false);
|
|
|
|
var dstBuffer = _renderer.BufferManager.GetBuffer(dst, dstOffset, size, true);
|
2023-07-29 03:56:33 +00:00
|
|
|
|
2024-06-30 16:23:53 +00:00
|
|
|
BufferHolder.Copy(Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
2024-06-29 18:07:07 +00:00
|
|
|
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
|
2024-08-31 20:42:56 +00:00
|
|
|
{
|
2024-06-25 13:25:31 +00:00
|
|
|
var computeCommandEncoder = GetOrCreateComputeEncoder(true);
|
2024-05-29 15:21:59 +00:00
|
|
|
|
2024-06-29 18:07:07 +00:00
|
|
|
ComputeSize localSize = _encoderStateManager.ComputeLocalSize;
|
|
|
|
|
2024-05-29 15:21:59 +00:00
|
|
|
computeCommandEncoder.DispatchThreadgroups(
|
2024-05-29 15:24:49 +00:00
|
|
|
new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ },
|
2024-06-29 18:07:07 +00:00
|
|
|
new MTLSize { width = (ulong)localSize.X, height = (ulong)localSize.Y, depth = (ulong)localSize.Z });
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
|
|
|
|
{
|
2024-06-26 21:26:27 +00:00
|
|
|
if (vertexCount == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-19 22:14:23 +00:00
|
|
|
var renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
2023-07-28 20:23:13 +00:00
|
|
|
|
2024-06-26 21:26:27 +00:00
|
|
|
if (TopologyUnsupported(_encoderStateManager.Topology))
|
|
|
|
{
|
|
|
|
var pattern = GetIndexBufferPattern();
|
|
|
|
|
|
|
|
BufferHandle handle = pattern.GetRepeatingBuffer(vertexCount, out int indexCount);
|
|
|
|
var buffer = _renderer.BufferManager.GetBuffer(handle, false);
|
|
|
|
var mtlBuffer = buffer.Get(Cbs, 0, indexCount * sizeof(int)).Value;
|
|
|
|
|
|
|
|
var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert();
|
|
|
|
|
|
|
|
renderCommandEncoder.DrawIndexedPrimitives(
|
|
|
|
primitiveType,
|
|
|
|
(ulong)indexCount,
|
|
|
|
MTLIndexType.UInt32,
|
|
|
|
mtlBuffer,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert();
|
|
|
|
|
|
|
|
renderCommandEncoder.DrawPrimitives(
|
|
|
|
primitiveType,
|
|
|
|
(ulong)firstVertex,
|
|
|
|
(ulong)vertexCount,
|
|
|
|
(ulong)instanceCount,
|
|
|
|
(ulong)firstInstance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private IndexBufferPattern GetIndexBufferPattern()
|
|
|
|
{
|
|
|
|
return _encoderStateManager.Topology switch
|
|
|
|
{
|
|
|
|
PrimitiveTopology.Quads => QuadsToTrisPattern,
|
|
|
|
PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => TriFanToTrisPattern,
|
|
|
|
_ => throw new NotSupportedException($"Unsupported topology: {_encoderStateManager.Topology}"),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private PrimitiveTopology TopologyRemap(PrimitiveTopology topology)
|
|
|
|
{
|
|
|
|
return topology switch
|
|
|
|
{
|
|
|
|
PrimitiveTopology.Quads => PrimitiveTopology.Triangles,
|
|
|
|
PrimitiveTopology.QuadStrip => PrimitiveTopology.TriangleStrip,
|
|
|
|
PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => PrimitiveTopology.Triangles,
|
|
|
|
_ => topology,
|
|
|
|
};
|
|
|
|
}
|
2023-07-28 01:51:20 +00:00
|
|
|
|
2024-06-26 21:26:27 +00:00
|
|
|
private bool TopologyUnsupported(PrimitiveTopology topology)
|
|
|
|
{
|
|
|
|
return topology switch
|
|
|
|
{
|
|
|
|
PrimitiveTopology.Quads or PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => true,
|
|
|
|
_ => false,
|
|
|
|
};
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
|
|
|
{
|
2024-06-26 21:26:27 +00:00
|
|
|
if (indexCount == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-19 22:14:23 +00:00
|
|
|
var renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
2023-08-03 12:58:14 +00:00
|
|
|
|
2024-06-26 21:26:27 +00:00
|
|
|
// TODO: Reindex unsupported topologies
|
|
|
|
if (TopologyUnsupported(_encoderStateManager.Topology))
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Gpu, $"Drawing indexed with unsupported topology: {_encoderStateManager.Topology}");
|
|
|
|
}
|
|
|
|
|
|
|
|
var primitiveType = TopologyRemap(_encoderStateManager.Topology).Convert();
|
2023-07-28 01:51:20 +00:00
|
|
|
|
2024-06-20 11:59:29 +00:00
|
|
|
var indexBuffer = _encoderStateManager.IndexBuffer;
|
2024-06-19 22:13:55 +00:00
|
|
|
|
2024-05-18 22:54:55 +00:00
|
|
|
renderCommandEncoder.DrawIndexedPrimitives(
|
|
|
|
primitiveType,
|
|
|
|
(ulong)indexCount,
|
|
|
|
_encoderStateManager.IndexType,
|
2024-06-19 22:13:55 +00:00
|
|
|
indexBuffer.Get(Cbs, 0, indexCount * sizeof(int)).Value,
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.IndexBufferOffset,
|
|
|
|
(ulong)instanceCount,
|
|
|
|
firstVertex,
|
|
|
|
(ulong)firstInstance);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void DrawIndexedIndirect(BufferRange indirectBuffer)
|
|
|
|
{
|
2024-06-19 22:14:23 +00:00
|
|
|
// var renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
2024-03-19 18:05:09 +00:00
|
|
|
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
|
|
|
{
|
2024-06-19 22:14:23 +00:00
|
|
|
// var renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
2024-03-19 18:05:09 +00:00
|
|
|
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void DrawIndirect(BufferRange indirectBuffer)
|
|
|
|
{
|
2024-06-19 22:14:23 +00:00
|
|
|
// var renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
2024-03-19 18:05:09 +00:00
|
|
|
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
|
|
|
|
{
|
2024-06-19 22:14:23 +00:00
|
|
|
// var renderCommandEncoder = GetOrCreateRenderEncoder(true);
|
2024-03-19 18:05:09 +00:00
|
|
|
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
|
|
|
|
{
|
2024-06-19 22:13:55 +00:00
|
|
|
_renderer.HelperShader.DrawTexture(texture, sampler, srcRegion, dstRegion);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetAlphaTest(bool enable, float reference, CompareOp op)
|
|
|
|
{
|
2024-06-26 14:00:22 +00:00
|
|
|
// This is currently handled using shader specialization, as Metal does not support alpha test.
|
|
|
|
// In the future, we may want to use this to write the reference value into the support buffer,
|
|
|
|
// to avoid creating one version of the shader per reference value used.
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetBlendState(AdvancedBlendDescriptor blend)
|
|
|
|
{
|
2024-05-19 01:07:05 +00:00
|
|
|
// Metal does not support advanced blend.
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetBlendState(int index, BlendDescriptor blend)
|
|
|
|
{
|
2024-05-19 01:20:15 +00:00
|
|
|
_encoderStateManager.UpdateBlendDescriptors(index, blend);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
|
|
|
|
{
|
2024-06-28 20:14:53 +00:00
|
|
|
if (enables == 0)
|
|
|
|
{
|
|
|
|
_encoderStateManager.UpdateDepthBias(0, 0, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_encoderStateManager.UpdateDepthBias(units, factor, clamp);
|
|
|
|
}
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetDepthClamp(bool clamp)
|
|
|
|
{
|
2024-05-19 01:29:46 +00:00
|
|
|
_encoderStateManager.UpdateDepthClamp(clamp);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetDepthMode(DepthMode mode)
|
|
|
|
{
|
2024-05-19 01:02:49 +00:00
|
|
|
// Metal does not support depth clip control.
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetDepthTest(DepthTestDescriptor depthTest)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateDepthState(depthTest);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetFaceCulling(bool enable, Face face)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateCullMode(enable, face);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetFrontFace(FrontFace frontFace)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateFrontFace(frontFace);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetIndexBuffer(BufferRange buffer, IndexType type)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateIndexBuffer(buffer, type);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
2024-03-18 18:32:59 +00:00
|
|
|
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
|
|
|
{
|
2024-06-30 11:01:24 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-03-18 18:32:59 +00:00
|
|
|
}
|
|
|
|
|
2024-04-22 21:44:55 +00:00
|
|
|
public void SetImageArray(ShaderStage stage, int binding, IImageArray array)
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
|
|
|
}
|
|
|
|
|
2024-05-29 15:38:11 +00:00
|
|
|
public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array)
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
|
|
|
}
|
|
|
|
|
2024-08-31 20:42:56 +00:00
|
|
|
public void SetLineParameters(float width, bool smooth)
|
|
|
|
{
|
2024-05-19 01:07:05 +00:00
|
|
|
// Metal does not support wide-lines.
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetLogicOpState(bool enable, LogicalOp op)
|
|
|
|
{
|
2024-05-19 01:07:05 +00:00
|
|
|
// Metal does not support logic operations.
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetMultisampleState(MultisampleDescriptor multisample)
|
|
|
|
{
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
|
|
|
|
{
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin)
|
|
|
|
{
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode)
|
|
|
|
{
|
2024-05-19 01:07:05 +00:00
|
|
|
// Metal does not support polygon mode.
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetPrimitiveRestart(bool enable, int index)
|
|
|
|
{
|
2023-07-28 01:51:20 +00:00
|
|
|
// TODO: Supported for LineStrip and TriangleStrip
|
|
|
|
// https://github.com/gpuweb/gpuweb/issues/1220#issuecomment-732483263
|
|
|
|
// https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515520-drawindexedprimitives
|
|
|
|
// https://stackoverflow.com/questions/70813665/how-to-render-multiple-trianglestrips-using-metal
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetPrimitiveTopology(PrimitiveTopology topology)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdatePrimitiveTopology(topology);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetProgram(IProgram program)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateProgram(program);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetRasterizerDiscard(bool discard)
|
|
|
|
{
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
|
|
|
|
{
|
2024-05-28 01:35:32 +00:00
|
|
|
_encoderStateManager.UpdateRenderTargetColorMasks(componentMask);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateRenderTargets(colors, depthStencil);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
2024-05-18 22:54:55 +00:00
|
|
|
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
|
2024-08-31 20:42:56 +00:00
|
|
|
{
|
2024-05-24 13:52:17 +00:00
|
|
|
_encoderStateManager.UpdateScissors(regions);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetStencilTest(StencilTestDescriptor stencilTest)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateStencilState(stencilTest);
|
|
|
|
}
|
2023-07-29 04:30:08 +00:00
|
|
|
|
2024-05-18 22:54:55 +00:00
|
|
|
public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
|
|
|
{
|
|
|
|
_encoderStateManager.UpdateUniformBuffers(buffers);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateStorageBuffers(buffers);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
2024-06-20 23:21:06 +00:00
|
|
|
internal void SetStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
|
2024-06-20 11:59:29 +00:00
|
|
|
{
|
|
|
|
_encoderStateManager.UpdateStorageBuffers(first, buffers);
|
|
|
|
}
|
|
|
|
|
2024-08-31 20:42:56 +00:00
|
|
|
public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
|
|
|
|
{
|
2024-05-29 15:21:59 +00:00
|
|
|
if (texture is TextureBase tex)
|
2024-03-19 19:07:35 +00:00
|
|
|
{
|
2024-06-29 18:07:07 +00:00
|
|
|
if (sampler == null || sampler is Sampler)
|
2024-03-19 19:07:35 +00:00
|
|
|
{
|
|
|
|
var index = (ulong)binding;
|
|
|
|
|
2024-06-29 18:07:07 +00:00
|
|
|
_encoderStateManager.UpdateTextureAndSampler(stage, index, tex, (Sampler)sampler);
|
2024-03-19 19:07:35 +00:00
|
|
|
}
|
|
|
|
}
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
2024-04-22 21:44:55 +00:00
|
|
|
public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array)
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
|
|
|
}
|
|
|
|
|
2024-05-29 15:38:11 +00:00
|
|
|
public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array)
|
|
|
|
{
|
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
|
|
|
}
|
|
|
|
|
2024-08-31 20:42:56 +00:00
|
|
|
public void SetUserClipDistance(int index, bool enableClip)
|
|
|
|
{
|
2023-08-02 02:36:07 +00:00
|
|
|
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateVertexAttribs(vertexAttribs);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
|
|
|
|
{
|
2024-05-18 22:54:55 +00:00
|
|
|
_encoderStateManager.UpdateVertexBuffers(vertexBuffers);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
2024-05-18 22:54:55 +00:00
|
|
|
public void SetViewports(ReadOnlySpan<Viewport> viewports)
|
2024-08-31 20:42:56 +00:00
|
|
|
{
|
2024-05-24 13:52:17 +00:00
|
|
|
_encoderStateManager.UpdateViewports(viewports);
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void TextureBarrier()
|
|
|
|
{
|
2024-06-27 21:51:12 +00:00
|
|
|
if (CurrentEncoderType == EncoderType.Render)
|
|
|
|
{
|
2024-06-30 16:23:53 +00:00
|
|
|
Encoders.RenderEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment);
|
2024-06-27 21:51:12 +00:00
|
|
|
}
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void TextureBarrierTiled()
|
|
|
|
{
|
2024-05-23 16:23:01 +00:00
|
|
|
TextureBarrier();
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
|
|
|
|
{
|
2023-07-28 01:51:20 +00:00
|
|
|
// TODO: Implementable via indirect draw commands
|
|
|
|
return false;
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual)
|
|
|
|
{
|
2023-07-28 01:51:20 +00:00
|
|
|
// TODO: Implementable via indirect draw commands
|
|
|
|
return false;
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void EndHostConditionalRendering()
|
|
|
|
{
|
2023-07-28 01:51:20 +00:00
|
|
|
// TODO: Implementable via indirect draw commands
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void BeginTransformFeedback(PrimitiveTopology topology)
|
|
|
|
{
|
2024-05-19 01:07:05 +00:00
|
|
|
// Metal does not support transform feedback.
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void EndTransformFeedback()
|
|
|
|
{
|
2024-05-19 01:07:05 +00:00
|
|
|
// Metal does not support transform feedback.
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
|
|
|
|
{
|
2024-05-19 01:07:05 +00:00
|
|
|
// Metal does not support transform feedback.
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
2024-01-27 21:09:24 +00:00
|
|
|
EndCurrentPass();
|
2024-05-25 06:27:28 +00:00
|
|
|
_encoderStateManager.Dispose();
|
2024-08-31 20:42:56 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-28 01:51:20 +00:00
|
|
|
}
|