| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- using Ryujinx.Common.Configuration;
- using Ryujinx.Graphics.GAL;
- using Ryujinx.Graphics.Shader.Translation;
- using SharpMetal.Metal;
- using SharpMetal.QuartzCore;
- using System;
- using System.Collections.Generic;
- using System.Runtime.Versioning;
- namespace Ryujinx.Graphics.Metal
- {
- [SupportedOSPlatform("macos")]
- public sealed class MetalRenderer : IRenderer
- {
- public const int TotalSets = 4;
- private readonly MTLDevice _device;
- private readonly MTLCommandQueue _queue;
- private readonly Func<CAMetalLayer> _getMetalLayer;
- private Pipeline _pipeline;
- private Window _window;
-
- public uint ProgramCount { get; set; }
- #pragma warning disable CS0067 // The event is never used
- public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
- #pragma warning restore CS0067
-
- public bool PreferThreading => true;
- public IPipeline Pipeline => _pipeline;
- public IWindow Window => _window;
- internal MTLCommandQueue BackgroundQueue { get; private set; }
- internal HelperShader HelperShader { get; private set; }
- internal BufferManager BufferManager { get; private set; }
- internal CommandBufferPool CommandBufferPool { get; private set; }
- internal BackgroundResources BackgroundResources { get; private set; }
- internal Action<Action> InterruptAction { get; private set; }
- internal SyncManager SyncManager { get; private set; }
- internal HashSet<Program> Programs { get; }
- internal HashSet<SamplerHolder> Samplers { get; }
- public MetalRenderer(Func<CAMetalLayer> metalLayer)
- {
- _device = MTLDevice.CreateSystemDefaultDevice();
- Programs = [];
- Samplers = [];
- if (_device.ArgumentBuffersSupport != MTLArgumentBuffersTier.Tier2)
- {
- throw new NotSupportedException("Metal backend requires Tier 2 Argument Buffer support.");
- }
- _queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers + 1);
- BackgroundQueue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers);
- _getMetalLayer = metalLayer;
- }
- public void Initialize(GraphicsDebugLevel logLevel)
- {
- CAMetalLayer layer = _getMetalLayer();
- layer.Device = _device;
- layer.FramebufferOnly = false;
- CommandBufferPool = new CommandBufferPool(_queue);
- _window = new Window(this, layer);
- _pipeline = new Pipeline(_device, this);
- BufferManager = new BufferManager(_device, this, _pipeline);
- _pipeline.InitEncoderStateManager(BufferManager);
- BackgroundResources = new BackgroundResources(this);
- HelperShader = new HelperShader(_device, this, _pipeline);
- SyncManager = new SyncManager(this);
- }
- public void BackgroundContextAction(Action action, bool alwaysBackground = false)
- {
- // GetData methods should be thread safe, so we can call this directly.
- // Texture copy (scaled) may also happen in here, so that should also be thread safe.
- action();
- }
- public BufferHandle CreateBuffer(int size, BufferAccess access)
- {
- return BufferManager.CreateWithHandle(size);
- }
- public BufferHandle CreateBuffer(IntPtr pointer, int size)
- {
- return BufferManager.Create(pointer, size);
- }
- public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers)
- {
- throw new NotImplementedException();
- }
- public IImageArray CreateImageArray(int size, bool isBuffer)
- {
- return new ImageArray(size, isBuffer, _pipeline);
- }
- public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
- {
- ProgramCount++;
- return new Program(this, _device, shaders, info.ResourceLayout, info.ComputeLocalSize);
- }
- public ISampler CreateSampler(SamplerCreateInfo info)
- {
- return new SamplerHolder(this, _device, info);
- }
- public ITexture CreateTexture(TextureCreateInfo info)
- {
- if (info.Target == Target.TextureBuffer)
- {
- return new TextureBuffer(_device, this, _pipeline, info);
- }
- return new Texture(_device, this, _pipeline, info);
- }
- public ITextureArray CreateTextureArray(int size, bool isBuffer)
- {
- return new TextureArray(size, isBuffer, _pipeline);
- }
- public bool PrepareHostMapping(IntPtr address, ulong size)
- {
- // TODO: Metal Host Mapping
- return false;
- }
- public void CreateSync(ulong id, bool strict)
- {
- SyncManager.Create(id, strict);
- }
- public void DeleteBuffer(BufferHandle buffer)
- {
- BufferManager.Delete(buffer);
- }
- public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
- {
- return BufferManager.GetData(buffer, offset, size);
- }
- public Capabilities GetCapabilities()
- {
- // TODO: Finalize these values
- return new Capabilities(
- api: TargetApi.Metal,
- vendorName: HardwareInfoTools.GetVendor(),
- SystemMemoryType.UnifiedMemory,
- hasFrontFacingBug: false,
- hasVectorIndexingBug: false,
- needsFragmentOutputSpecialization: true,
- reduceShaderPrecision: true,
- supportsAstcCompression: true,
- supportsBc123Compression: true,
- supportsBc45Compression: true,
- supportsBc67Compression: true,
- supportsEtc2Compression: true,
- supports3DTextureCompression: true,
- supportsBgraFormat: true,
- supportsR4G4Format: false,
- supportsR4G4B4A4Format: true,
- supportsScaledVertexFormats: false,
- supportsSnormBufferTextureFormat: true,
- supportsSparseBuffer: false,
- supports5BitComponentFormat: true,
- supportsBlendEquationAdvanced: false,
- supportsFragmentShaderInterlock: true,
- supportsFragmentShaderOrderingIntel: false,
- supportsGeometryShader: false,
- supportsGeometryShaderPassthrough: false,
- supportsTransformFeedback: false,
- supportsImageLoadFormatted: false,
- supportsLayerVertexTessellation: false,
- supportsMismatchingViewFormat: true,
- supportsCubemapView: true,
- supportsNonConstantTextureOffset: false,
- supportsQuads: false,
- supportsSeparateSampler: true,
- supportsShaderBallot: false,
- supportsShaderBarrierDivergence: false,
- supportsShaderFloat64: false,
- supportsTextureGatherOffsets: false,
- supportsTextureShadowLod: false,
- supportsVertexStoreAndAtomics: false,
- supportsViewportIndexVertexTessellation: false,
- supportsViewportMask: false,
- supportsViewportSwizzle: false,
- supportsIndirectParameters: true,
- supportsDepthClipControl: false,
- uniformBufferSetIndex: (int)Constants.ConstantBuffersSetIndex,
- storageBufferSetIndex: (int)Constants.StorageBuffersSetIndex,
- textureSetIndex: (int)Constants.TexturesSetIndex,
- imageSetIndex: (int)Constants.ImagesSetIndex,
- extraSetBaseIndex: TotalSets,
- maximumExtraSets: (int)Constants.MaximumExtraSets,
- maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
- maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
- maximumTexturesPerStage: Constants.MaxTexturesPerStage,
- maximumImagesPerStage: Constants.MaxImagesPerStage,
- maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength,
- maximumSupportedAnisotropy: 16,
- shaderSubgroupSize: 256,
- storageBufferOffsetAlignment: 16,
- textureBufferOffsetAlignment: 16,
- gatherBiasPrecision: 0,
- maximumGpuMemory: 0
- );
- }
- public ulong GetCurrentSync()
- {
- return SyncManager.GetCurrent();
- }
- public HardwareInfo GetHardwareInfo()
- {
- return new HardwareInfo(HardwareInfoTools.GetVendor(), HardwareInfoTools.GetModel(), "Apple");
- }
- public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info)
- {
- throw new NotImplementedException();
- }
- public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
- {
- BufferManager.SetData(buffer, offset, data, _pipeline.Cbs);
- }
- public void UpdateCounters()
- {
- // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc
- }
- public void PreFrame()
- {
- SyncManager.Cleanup();
- }
- public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved)
- {
- // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc
- CounterEvent counterEvent = new();
- resultHandler?.Invoke(counterEvent, type == CounterType.SamplesPassed ? (ulong)1 : 0);
- return counterEvent;
- }
- public void ResetCounter(CounterType type)
- {
- // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc
- }
- public void WaitSync(ulong id)
- {
- SyncManager.Wait(id);
- }
- public void FlushAllCommands()
- {
- _pipeline.FlushCommandsImpl();
- }
- public void RegisterFlush()
- {
- SyncManager.RegisterFlush();
- // Periodically free unused regions of the staging buffer to avoid doing it all at once.
- BufferManager.StagingBuffer.FreeCompleted();
- }
- public void SetInterruptAction(Action<Action> interruptAction)
- {
- InterruptAction = interruptAction;
- }
- public void Screenshot()
- {
- // TODO: Screenshots
- }
- public void Dispose()
- {
- BackgroundResources.Dispose();
- foreach (Program program in Programs)
- {
- program.Dispose();
- }
- foreach (SamplerHolder sampler in Samplers)
- {
- sampler.Dispose();
- }
- _pipeline.Dispose();
- _window.Dispose();
- }
- }
- }
|