Ryujinx/src/Ryujinx.Graphics.Metal/Pipeline.cs

701 lines
24 KiB
C#
Raw Normal View History

2023-07-28 20:39:14 +00:00
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using SharpMetal.Foundation;
using SharpMetal.Metal;
2023-08-02 23:56:59 +00:00
using SharpMetal.QuartzCore;
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Metal
{
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
}
[SupportedOSPlatform("macos")]
class Pipeline : IPipeline, IEncoderFactory, IDisposable
{
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
2023-07-28 02:54:24 +00:00
private readonly MTLDevice _device;
private readonly MetalRenderer _renderer;
private EncoderStateManager _encoderStateManager;
private ulong _byteWeight;
public MTLCommandBuffer CommandBuffer;
public IndexBufferPattern QuadsToTrisPattern;
public IndexBufferPattern TriFanToTrisPattern;
internal CommandBufferScoped? PreloadCbs { get; private set; }
internal CommandBufferScoped Cbs { get; private set; }
internal CommandBufferEncoder Encoders => Cbs.Encoders;
internal EncoderType CurrentEncoderType => Encoders.CurrentEncoderType;
public Pipeline(MTLDevice device, MetalRenderer renderer)
{
2023-07-28 02:54:24 +00:00
_device = device;
_renderer = renderer;
renderer.CommandBufferPool.Initialize(this);
CommandBuffer = (Cbs = _renderer.CommandBufferPool.Rent()).CommandBuffer;
}
internal void InitEncoderStateManager(BufferManager bufferManager)
{
_encoderStateManager = new EncoderStateManager(_device, bufferManager, this);
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
}
public EncoderState SwapState(EncoderState state, DirtyFlags flags = DirtyFlags.All, bool endRenderPass = true)
{
if (endRenderPass && CurrentEncoderType == EncoderType.Render)
{
EndCurrentPass();
}
return _encoderStateManager.SwapState(state, flags);
}
public PredrawState SavePredrawState()
{
return _encoderStateManager.SavePredrawState();
}
public void RestorePredrawState(PredrawState state)
{
_encoderStateManager.RestorePredrawState(state);
}
public void SetClearLoadAction(bool clear)
{
_encoderStateManager.SetClearLoadAction(clear);
}
public MTLRenderCommandEncoder GetOrCreateRenderEncoder(bool forDraw = false)
{
MTLRenderCommandEncoder renderCommandEncoder = Cbs.Encoders.EnsureRenderEncoder();
if (forDraw)
{
_encoderStateManager.RebindRenderState(renderCommandEncoder);
}
return renderCommandEncoder;
}
public MTLBlitCommandEncoder GetOrCreateBlitEncoder()
{
return Cbs.Encoders.EnsureBlitEncoder();
}
public MTLComputeCommandEncoder GetOrCreateComputeEncoder(bool forDispatch = false)
{
MTLComputeCommandEncoder computeCommandEncoder = Cbs.Encoders.EnsureComputeEncoder();
if (forDispatch)
{
_encoderStateManager.RebindComputeState(computeCommandEncoder);
}
return computeCommandEncoder;
}
2023-07-28 20:23:13 +00:00
public void EndCurrentPass()
2023-07-28 02:54:24 +00:00
{
Cbs.Encoders.EndCurrentPass();
2023-07-28 02:54:24 +00:00
}
public MTLRenderCommandEncoder CreateRenderCommandEncoder()
2023-07-28 02:54:24 +00:00
{
return _encoderStateManager.CreateRenderCommandEncoder();
2023-07-28 20:23:13 +00:00
}
public MTLComputeCommandEncoder CreateComputeCommandEncoder()
2023-07-29 05:18:51 +00:00
{
return _encoderStateManager.CreateComputeCommandEncoder();
2023-07-29 05:18:51 +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);
var dst = new Texture(_device, _renderer, this, textureInfo, drawable.Texture, 0, 0);
2024-05-19 00:27:27 +00:00
_renderer.HelperShader.BlitColor(Cbs, src, dst, srcRegion, dstRegion, isLinear, true);
2023-08-02 23:56:59 +00:00
EndCurrentPass();
Cbs.CommandBuffer.PresentDrawable(drawable);
FlushCommandsImpl();
2023-08-02 23:56:59 +00:00
// TODO: Auto flush counting
_renderer.SyncManager.GetAndResetWaitTicks();
2024-05-25 12:38:39 +00:00
// Cleanup
dst.Dispose();
}
public CommandBufferScoped GetPreloadCommandBuffer()
{
PreloadCbs ??= _renderer.CommandBufferPool.Rent();
return PreloadCbs.Value;
}
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();
}
}
}
public void FlushCommandsImpl()
{
EndCurrentPass();
_byteWeight = 0;
if (PreloadCbs != null)
{
PreloadCbs.Value.Dispose();
PreloadCbs = null;
}
CommandBuffer = (Cbs = _renderer.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
_renderer.RegisterFlush();
}
public void Blit(
Texture src,
Texture dst,
2024-05-27 14:38:00 +00:00
Extents2D srcRegion,
Extents2D dstRegion,
2024-07-24 22:27:59 +00:00
bool isDepthOrStencil,
2024-05-27 14:38:00 +00:00
bool linearFilter)
{
2024-07-24 22:27:59 +00:00
if (isDepthOrStencil)
{
_renderer.HelperShader.BlitDepthStencil(Cbs, src, dst, srcRegion, dstRegion);
}
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
{
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;
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;
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
}
public void ClearBuffer(BufferHandle destination, int offset, int size, uint value)
{
var blitCommandEncoder = GetOrCreateBlitEncoder();
2023-07-28 20:51:07 +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);
}
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;
}
_renderer.HelperShader.ClearColor(index, colors, componentMask, dst.Width, dst.Height);
}
2024-05-18 22:54:55 +00:00
public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
{
2024-05-30 12:20:16 +00:00
var depthStencil = _encoderStateManager.DepthStencil;
2024-05-30 12:24:05 +00:00
if (depthStencil == null)
{
return;
}
_renderer.HelperShader.ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask, depthStencil.Width, depthStencil.Height);
}
public void CommandBufferBarrier()
{
2024-06-27 21:51:12 +00:00
Barrier();
}
public void CopyBuffer(BufferHandle src, BufferHandle dst, int srcOffset, int dstOffset, int size)
{
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
BufferHolder.Copy(Cbs, srcBuffer, dstBuffer, srcOffset, dstOffset, size);
}
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
{
var computeCommandEncoder = GetOrCreateComputeEncoder(true);
ComputeSize localSize = _encoderStateManager.ComputeLocalSize;
computeCommandEncoder.DispatchThreadgroups(
2024-05-29 15:24:49 +00:00
new MTLSize { width = (ulong)groupsX, height = (ulong)groupsY, depth = (ulong)groupsZ },
new MTLSize { width = (ulong)localSize.X, height = (ulong)localSize.Y, depth = (ulong)localSize.Z });
}
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
{
if (vertexCount == 0)
{
return;
}
var renderCommandEncoder = GetOrCreateRenderEncoder(true);
2023-07-28 20:23:13 +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,
};
}
private bool TopologyUnsupported(PrimitiveTopology topology)
{
return topology switch
{
PrimitiveTopology.Quads or PrimitiveTopology.TriangleFan or PrimitiveTopology.Polygon => true,
_ => false,
};
}
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
{
if (indexCount == 0)
{
return;
}
// 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();
2024-07-08 11:02:42 +00:00
(MTLBuffer mtlBuffer, int offset, MTLIndexType type) = _encoderStateManager.IndexBuffer.GetIndexBuffer(_renderer, Cbs);
if (mtlBuffer.NativePtr != IntPtr.Zero)
{
var renderCommandEncoder = GetOrCreateRenderEncoder(true);
renderCommandEncoder.DrawIndexedPrimitives(
primitiveType,
(ulong)indexCount,
type,
mtlBuffer,
(ulong)offset,
(ulong)instanceCount,
firstVertex,
(ulong)firstInstance);
}
}
public void DrawIndexedIndirect(BufferRange indirectBuffer)
{
// 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!");
}
public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
// 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!");
}
public void DrawIndirect(BufferRange indirectBuffer)
{
// 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!");
}
public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{
// 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!");
}
public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
{
_renderer.HelperShader.DrawTexture(texture, sampler, srcRegion, dstRegion);
}
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.
}
public void SetBlendState(AdvancedBlendDescriptor blend)
{
// Metal does not support advanced blend.
}
public void SetBlendState(int index, BlendDescriptor blend)
{
2024-05-19 01:20:15 +00:00
_encoderStateManager.UpdateBlendDescriptors(index, blend);
}
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
{
if (enables == 0)
{
_encoderStateManager.UpdateDepthBias(0, 0, 0);
}
else
{
_encoderStateManager.UpdateDepthBias(units, factor, clamp);
}
}
public void SetDepthClamp(bool clamp)
{
2024-05-19 01:29:46 +00:00
_encoderStateManager.UpdateDepthClamp(clamp);
}
public void SetDepthMode(DepthMode mode)
{
2024-05-19 01:02:49 +00:00
// Metal does not support depth clip control.
}
public void SetDepthTest(DepthTestDescriptor depthTest)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdateDepthState(depthTest);
}
public void SetFaceCulling(bool enable, Face face)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdateCullMode(enable, face);
}
public void SetFrontFace(FrontFace frontFace)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdateFrontFace(frontFace);
}
public void SetIndexBuffer(BufferRange buffer, IndexType type)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdateIndexBuffer(buffer, type);
}
2024-03-18 18:32:59 +00:00
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
{
if (texture is TextureBase tex)
{
var index = (ulong)binding;
_encoderStateManager.UpdateImage(stage, index, tex);
}
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!");
}
public void SetLineParameters(float width, bool smooth)
{
// Metal does not support wide-lines.
}
public void SetLogicOpState(bool enable, LogicalOp op)
{
// Metal does not support logic operations.
}
public void SetMultisampleState(MultisampleDescriptor multisample)
{
2023-08-02 02:36:07 +00:00
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
}
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!");
}
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!");
}
public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode)
{
// Metal does not support polygon mode.
}
public void SetPrimitiveRestart(bool enable, int index)
{
// Always active 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
// Emulating disabling this is very difficult. It's unlikely for an index buffer to use the largest possible index,
// so it's fine nearly all of the time.
}
public void SetPrimitiveTopology(PrimitiveTopology topology)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdatePrimitiveTopology(topology);
}
public void SetProgram(IProgram program)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdateProgram(program);
}
public void SetRasterizerDiscard(bool discard)
{
2023-08-02 02:36:07 +00:00
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
}
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
{
2024-05-28 01:35:32 +00:00
_encoderStateManager.UpdateRenderTargetColorMasks(componentMask);
}
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdateRenderTargets(colors, depthStencil);
}
2024-05-18 22:54:55 +00:00
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
{
2024-05-24 13:52:17 +00:00
_encoderStateManager.UpdateScissors(regions);
}
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);
}
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdateStorageBuffers(buffers);
}
internal void SetStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
{
_encoderStateManager.UpdateStorageBuffers(first, buffers);
}
public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
{
if (texture is TextureBase tex)
2024-03-19 19:07:35 +00:00
{
if (sampler == null || sampler is Sampler)
2024-03-19 19:07:35 +00:00
{
var index = (ulong)binding;
_encoderStateManager.UpdateTextureAndSampler(stage, index, tex, (Sampler)sampler);
2024-03-19 19:07:35 +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!");
}
public void SetUserClipDistance(int index, bool enableClip)
{
2023-08-02 02:36:07 +00:00
Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
}
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdateVertexAttribs(vertexAttribs);
}
public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
{
2024-05-18 22:54:55 +00:00
_encoderStateManager.UpdateVertexBuffers(vertexBuffers);
}
2024-05-18 22:54:55 +00:00
public void SetViewports(ReadOnlySpan<Viewport> viewports)
{
2024-05-24 13:52:17 +00:00
_encoderStateManager.UpdateViewports(viewports);
}
public void TextureBarrier()
{
2024-06-27 21:51:12 +00:00
if (CurrentEncoderType == EncoderType.Render)
{
Encoders.RenderEncoder.MemoryBarrier(MTLBarrierScope.Textures, MTLRenderStages.RenderStageFragment, MTLRenderStages.RenderStageFragment);
2024-06-27 21:51:12 +00:00
}
}
public void TextureBarrierTiled()
{
2024-05-23 16:23:01 +00:00
TextureBarrier();
}
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
{
// TODO: Implementable via indirect draw commands
return false;
}
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual)
{
// TODO: Implementable via indirect draw commands
return false;
}
public void EndHostConditionalRendering()
{
// TODO: Implementable via indirect draw commands
}
public void BeginTransformFeedback(PrimitiveTopology topology)
{
// Metal does not support transform feedback.
}
public void EndTransformFeedback()
{
// Metal does not support transform feedback.
}
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
{
// Metal does not support transform feedback.
}
public void Dispose()
{
2024-01-27 21:09:24 +00:00
EndCurrentPass();
2024-05-25 06:27:28 +00:00
_encoderStateManager.Dispose();
}
}
}