From 35b751de1b99f336420fa4bad1c5079656fde5f1 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 5 Apr 2024 17:10:21 +0200 Subject: [PATCH] metal: add command recorder for command management --- src/video_core/CMakeLists.txt | 1 + .../renderer_metal/mtl_command_recorder.h | 50 +++++++++++++++ .../renderer_metal/mtl_command_recorder.mm | 64 +++++++++++++++++++ .../renderer_metal/mtl_rasterizer.h | 1 - .../renderer_metal/mtl_swap_chain.h | 9 ++- .../renderer_metal/mtl_swap_chain.mm | 14 +++- src/video_core/renderer_metal/objc_bridge.h | 4 ++ .../renderer_metal/renderer_metal.h | 2 + .../renderer_metal/renderer_metal.mm | 27 ++++---- 9 files changed, 150 insertions(+), 22 deletions(-) create mode 100644 src/video_core/renderer_metal/mtl_command_recorder.h create mode 100644 src/video_core/renderer_metal/mtl_command_recorder.mm diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 38e2e68ef8..cb60d096a8 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -374,6 +374,7 @@ if (APPLE) ) list(APPEND sources + renderer_metal/mtl_command_recorder.mm renderer_metal/mtl_device.mm renderer_metal/mtl_rasterizer.mm renderer_metal/mtl_swap_chain.mm diff --git a/src/video_core/renderer_metal/mtl_command_recorder.h b/src/video_core/renderer_metal/mtl_command_recorder.h new file mode 100644 index 0000000000..12a78b374c --- /dev/null +++ b/src/video_core/renderer_metal/mtl_command_recorder.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "objc_bridge.h" +#include "video_core/renderer_metal/objc_bridge.h" + +namespace Metal { + +class Device; + +enum class EncoderType { Render, Compute, Blit }; + +class CommandRecorder { +public: + CommandRecorder(const Device& device_); + ~CommandRecorder(); + + void BeginRenderPass(MTLRenderPassDescriptor* render_pass_descriptor); + + void RequireComputeEncoder(); + + void RequireBlitEncoder(); + + void EndEncoding(); + + void Present(CAMetalDrawable_t drawable); + + void Submit(); + + MTLCommandBuffer_t GetCommandBuffer() { + return command_buffer; + } + + MTLCommandEncoder_t GetCommandEncoder() { + return encoder; + } + +private: + const Device& device; + + MTLCommandBuffer_t command_buffer = nil; + MTLCommandEncoder_t encoder = nil; + + EncoderType encoder_type; + + void RequireCommandBuffer(); +}; + +} // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_command_recorder.mm b/src/video_core/renderer_metal/mtl_command_recorder.mm new file mode 100644 index 0000000000..f44e9811b2 --- /dev/null +++ b/src/video_core/renderer_metal/mtl_command_recorder.mm @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/renderer_metal/mtl_command_recorder.h" +#include "video_core/renderer_metal/mtl_device.h" + +#include + +namespace Metal { + +CommandRecorder::CommandRecorder(const Device& device_) : device(device_) {} + +CommandRecorder::~CommandRecorder() = default; + +void CommandRecorder::BeginRenderPass(MTLRenderPassDescriptor* render_pass_descriptor) { + RequireCommandBuffer(); + EndEncoding(); + encoder = [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor]; + encoder_type = EncoderType::Render; +} + +void CommandRecorder::RequireComputeEncoder() { + RequireCommandBuffer(); + if (!encoder || encoder_type != EncoderType::Compute) { + EndEncoding(); + encoder = [command_buffer computeCommandEncoder]; + encoder_type = EncoderType::Compute; + } +} + +void CommandRecorder::RequireBlitEncoder() { + RequireCommandBuffer(); + if (!encoder || encoder_type != EncoderType::Blit) { + EndEncoding(); + encoder = [command_buffer blitCommandEncoder]; + encoder_type = EncoderType::Blit; + } +} + +void CommandRecorder::EndEncoding() { + if (encoder) { + [encoder endEncoding]; + [encoder release]; + encoder = nil; + } +} + +void CommandRecorder::Present(CAMetalDrawable_t drawable) { + [command_buffer presentDrawable:drawable]; +} + +void CommandRecorder::Submit() { + EndEncoding(); + [command_buffer commit]; + [command_buffer release]; + command_buffer = nil; +} + +void CommandRecorder::RequireCommandBuffer() { + if (!command_buffer) { + command_buffer = [device.GetCommandQueue() commandBuffer]; + } +} + +} // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_rasterizer.h b/src/video_core/renderer_metal/mtl_rasterizer.h index a9479db9c5..87bbfea585 100644 --- a/src/video_core/renderer_metal/mtl_rasterizer.h +++ b/src/video_core/renderer_metal/mtl_rasterizer.h @@ -3,7 +3,6 @@ #pragma once #include "common/common_types.h" -#include "objc_bridge.h" #include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_interface.h" diff --git a/src/video_core/renderer_metal/mtl_swap_chain.h b/src/video_core/renderer_metal/mtl_swap_chain.h index d923896676..7c492f5dc0 100644 --- a/src/video_core/renderer_metal/mtl_swap_chain.h +++ b/src/video_core/renderer_metal/mtl_swap_chain.h @@ -7,23 +7,26 @@ namespace Metal { class Device; +class CommandRecorder; class SwapChain { public: - SwapChain(const Device& device_, const CAMetalLayer* layer_); + SwapChain(const Device& device_, CommandRecorder& command_recorder_, + const CAMetalLayer* layer_); ~SwapChain(); void AcquireNextDrawable(); - void Present(MTLCommandBuffer_t command_buffer); + void Present(); MTLTexture_t GetDrawableTexture(); private: const Device& device; + CommandRecorder& command_recorder; const CAMetalLayer* layer; - CAMetalDrawable_t drawable; + CAMetalDrawable_t drawable = nil; }; } // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_swap_chain.mm b/src/video_core/renderer_metal/mtl_swap_chain.mm index 4158ddcc7c..22bbd43f09 100644 --- a/src/video_core/renderer_metal/mtl_swap_chain.mm +++ b/src/video_core/renderer_metal/mtl_swap_chain.mm @@ -1,16 +1,23 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include "video_core/renderer_metal/mtl_command_recorder.h" #include "video_core/renderer_metal/mtl_device.h" #include "video_core/renderer_metal/mtl_swap_chain.h" namespace Metal { -SwapChain::SwapChain(const Device& device_, const CAMetalLayer* layer_) : device(device_), layer([layer_ retain]) { +SwapChain::SwapChain(const Device& device_, CommandRecorder& command_recorder_, + const CAMetalLayer* layer_) + : device(device_), command_recorder(command_recorder_), layer([layer_ retain]) { // Give the layer our device layer.device = device.GetDevice(); } SwapChain::~SwapChain() { + if (drawable) { + // TODO: should drawable be released? + [drawable release]; + } [layer release]; } @@ -19,8 +26,9 @@ void SwapChain::AcquireNextDrawable() { drawable = [layer nextDrawable]; } -void SwapChain::Present(MTLCommandBuffer_t command_buffer) { - [command_buffer presentDrawable:drawable]; +void SwapChain::Present() { + command_recorder.EndEncoding(); + command_recorder.Present(drawable); } MTLTexture_t SwapChain::GetDrawableTexture() { diff --git a/src/video_core/renderer_metal/objc_bridge.h b/src/video_core/renderer_metal/objc_bridge.h index 0e4493ffa0..21bc3e74c7 100644 --- a/src/video_core/renderer_metal/objc_bridge.h +++ b/src/video_core/renderer_metal/objc_bridge.h @@ -8,13 +8,17 @@ typedef id MTLDevice_t; typedef id MTLCommandQueue_t; typedef id MTLCommandBuffer_t; +typedef id MTLCommandEncoder_t; typedef id MTLTexture_t; typedef id CAMetalDrawable_t; #else typedef void* MTLDevice_t; typedef void* MTLCommandQueue_t; typedef void* MTLCommandBuffer_t; +typedef void* MTLCommandEncoder_t; typedef void* MTLTexture_t; +typedef void MTLRenderPassDescriptor; typedef void CAMetalLayer; typedef void* CAMetalDrawable_t; +#define nil NULL #endif diff --git a/src/video_core/renderer_metal/renderer_metal.h b/src/video_core/renderer_metal/renderer_metal.h index 888faa8149..877c50fe77 100644 --- a/src/video_core/renderer_metal/renderer_metal.h +++ b/src/video_core/renderer_metal/renderer_metal.h @@ -8,6 +8,7 @@ #include "objc_bridge.h" #include "video_core/host1x/gpu_device_memory_manager.h" #include "video_core/renderer_base.h" +#include "video_core/renderer_metal/mtl_command_recorder.h" #include "video_core/renderer_metal/mtl_device.h" #include "video_core/renderer_metal/mtl_rasterizer.h" #include "video_core/renderer_metal/mtl_swap_chain.h" @@ -50,6 +51,7 @@ private: Tegra::GPU& gpu; Device device; + CommandRecorder command_recorder; SwapChain swap_chain; RasterizerMetal rasterizer; diff --git a/src/video_core/renderer_metal/renderer_metal.mm b/src/video_core/renderer_metal/renderer_metal.mm index 82f3dbefb7..1c556d5433 100644 --- a/src/video_core/renderer_metal/renderer_metal.mm +++ b/src/video_core/renderer_metal/renderer_metal.mm @@ -13,7 +13,9 @@ RendererMetal::RendererMetal(Core::Frontend::EmuWindow& emu_window, std::unique_ptr context_) : RendererBase(emu_window, std::move(context_)), device_memory{device_memory_}, gpu{gpu_}, device{}, - swap_chain(device, static_cast(render_window.GetWindowInfo().render_surface)), + command_recorder(device), + swap_chain(device, command_recorder, + static_cast(render_window.GetWindowInfo().render_surface)), rasterizer(gpu_, device, swap_chain) {} RendererMetal::~RendererMetal() = default; @@ -24,22 +26,17 @@ void RendererMetal::Composite(std::span framebuf } // HACK - @autoreleasepool { - swap_chain.AcquireNextDrawable(); + swap_chain.AcquireNextDrawable(); - MTLRenderPassDescriptor* render_pass_descriptor = [MTLRenderPassDescriptor renderPassDescriptor]; - render_pass_descriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0, 0.5, 0.0, 1.0); - render_pass_descriptor.colorAttachments[0].loadAction = MTLLoadActionClear; - render_pass_descriptor.colorAttachments[0].storeAction = MTLStoreActionStore; - render_pass_descriptor.colorAttachments[0].texture = swap_chain.GetDrawableTexture(); + MTLRenderPassDescriptor* render_pass_descriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + render_pass_descriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0, 0.5, 0.0, 1.0); + render_pass_descriptor.colorAttachments[0].loadAction = MTLLoadActionClear; + render_pass_descriptor.colorAttachments[0].storeAction = MTLStoreActionStore; + render_pass_descriptor.colorAttachments[0].texture = swap_chain.GetDrawableTexture(); - id command_buffer = [device.GetCommandQueue() commandBuffer]; - id render_encoder = [command_buffer - renderCommandEncoderWithDescriptor:render_pass_descriptor]; - [render_encoder endEncoding]; - swap_chain.Present(command_buffer); - [command_buffer commit]; - } + command_recorder.BeginRenderPass(render_pass_descriptor); + swap_chain.Present(); + command_recorder.Submit(); gpu.RendererFrameEndNotify(); rasterizer.TickFrame();