diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 4b1a9e7503..38e2e68ef8 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -376,6 +376,7 @@ if (APPLE)
     list(APPEND sources
         renderer_metal/mtl_device.mm
         renderer_metal/mtl_rasterizer.mm
+        renderer_metal/mtl_swap_chain.mm
         renderer_metal/renderer_metal.mm
     )
 endif()
diff --git a/src/video_core/renderer_metal/mtl_rasterizer.h b/src/video_core/renderer_metal/mtl_rasterizer.h
index 5637e40344..a9479db9c5 100644
--- a/src/video_core/renderer_metal/mtl_rasterizer.h
+++ b/src/video_core/renderer_metal/mtl_rasterizer.h
@@ -3,6 +3,7 @@
 #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"
@@ -15,6 +16,7 @@ class System;
 namespace Metal {
 
 class Device;
+class SwapChain;
 
 class RasterizerMetal;
 
@@ -36,7 +38,7 @@ public:
 class RasterizerMetal final : public VideoCore::RasterizerInterface,
                               protected VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
 public:
-    explicit RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, const CAMetalLayer* layer_);
+    explicit RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, const SwapChain& swap_chain_);
     ~RasterizerMetal() override;
 
     void Draw(bool is_indexed, u32 instance_count) override;
@@ -90,7 +92,9 @@ private:
     AccelerateDMA accelerate_dma;
 
     const Device& device;
-    const CAMetalLayer* layer;
+    const SwapChain& swap_chain;
+
+    MTLCommandBuffer_t command_buffer;
 };
 
 } // namespace Metal
diff --git a/src/video_core/renderer_metal/mtl_rasterizer.mm b/src/video_core/renderer_metal/mtl_rasterizer.mm
index aa44783bb1..65e4eae2a5 100644
--- a/src/video_core/renderer_metal/mtl_rasterizer.mm
+++ b/src/video_core/renderer_metal/mtl_rasterizer.mm
@@ -4,6 +4,10 @@
 #include "video_core/control/channel_state.h"
 #include "video_core/host1x/host1x.h"
 #include "video_core/memory_manager.h"
+#include "video_core/buffer_cache/buffer_cache.h"
+#include "video_core/engines/draw_manager.h"
+#include "video_core/engines/kepler_compute.h"
+#include "video_core/engines/maxwell_3d.h"
 #include "video_core/renderer_metal/mtl_rasterizer.h"
 #include "video_core/renderer_metal/mtl_device.h"
 
@@ -20,11 +24,31 @@ bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) {
     return true;
 }
 
-RasterizerMetal::RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, const CAMetalLayer* layer_)
-    : gpu{gpu_}, device{device_}, layer{layer_} {}
+RasterizerMetal::RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, const SwapChain& swap_chain_)
+    : gpu{gpu_}, device{device_}, swap_chain{swap_chain_} {}
 RasterizerMetal::~RasterizerMetal() = default;
 
-void RasterizerMetal::Draw(bool is_indexed, u32 instance_count) {}
+void RasterizerMetal::Draw(bool is_indexed, u32 instance_count) {
+    //const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
+    if (is_indexed) {
+        std::cout << "DrawIndexed" << std::endl;
+        /*[command_buffer drawIndexedPrimitives:MTLPrimitiveTypeTriangle
+                                   indexCount:draw_params.num_indices
+                                    indexType:MTLIndexTypeUInt32
+                                  indexBuffer:draw_state.index_buffer
+                            indexBufferOffset:draw_params.first_index * sizeof(u32)
+                                instanceCount:draw_params.num_instances
+                                   baseVertex:draw_params.base_vertex
+                                 baseInstance:draw_params.base_instance];*/
+        //cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,
+        //                    draw_params.first_index, draw_params.base_vertex,
+        //                    draw_params.base_instance);
+    } else {
+        std::cout << "Draw" << std::endl;
+        //cmdbuf.Draw(draw_params.num_vertices, draw_params.num_instances,
+        //            draw_params.base_vertex, draw_params.base_instance);
+    }
+}
 void RasterizerMetal::DrawTexture() {}
 void RasterizerMetal::Clear(u32 layer_count) {}
 void RasterizerMetal::DispatchCompute() {}
diff --git a/src/video_core/renderer_metal/mtl_swap_chain.h b/src/video_core/renderer_metal/mtl_swap_chain.h
new file mode 100644
index 0000000000..d923896676
--- /dev/null
+++ b/src/video_core/renderer_metal/mtl_swap_chain.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "video_core/renderer_metal/objc_bridge.h"
+
+namespace Metal {
+
+class Device;
+
+class SwapChain {
+public:
+    SwapChain(const Device& device_, const CAMetalLayer* layer_);
+    ~SwapChain();
+
+    void AcquireNextDrawable();
+
+    void Present(MTLCommandBuffer_t command_buffer);
+
+    MTLTexture_t GetDrawableTexture();
+
+private:
+    const Device& device;
+    const CAMetalLayer* layer;
+
+    CAMetalDrawable_t drawable;
+};
+
+} // namespace Metal
diff --git a/src/video_core/renderer_metal/mtl_swap_chain.mm b/src/video_core/renderer_metal/mtl_swap_chain.mm
new file mode 100644
index 0000000000..4158ddcc7c
--- /dev/null
+++ b/src/video_core/renderer_metal/mtl_swap_chain.mm
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#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]) {
+    // Give the layer our device
+    layer.device = device.GetDevice();
+}
+
+SwapChain::~SwapChain() {
+    [layer release];
+}
+
+void SwapChain::AcquireNextDrawable() {
+    // Get the next drawable
+    drawable = [layer nextDrawable];
+}
+
+void SwapChain::Present(MTLCommandBuffer_t command_buffer) {
+    [command_buffer presentDrawable:drawable];
+}
+
+MTLTexture_t SwapChain::GetDrawableTexture() {
+    return drawable.texture;
+}
+
+} // namespace Metal
diff --git a/src/video_core/renderer_metal/objc_bridge.h b/src/video_core/renderer_metal/objc_bridge.h
index 5fd55169c3..0e4493ffa0 100644
--- a/src/video_core/renderer_metal/objc_bridge.h
+++ b/src/video_core/renderer_metal/objc_bridge.h
@@ -7,10 +7,14 @@
 #import <QuartzCore/QuartzCore.h>
 typedef id<MTLDevice> MTLDevice_t;
 typedef id<MTLCommandQueue> MTLCommandQueue_t;
+typedef id<MTLCommandBuffer> MTLCommandBuffer_t;
 typedef id<MTLTexture> MTLTexture_t;
+typedef id<CAMetalDrawable> CAMetalDrawable_t;
 #else
 typedef void* MTLDevice_t;
 typedef void* MTLCommandQueue_t;
+typedef void* MTLCommandBuffer_t;
 typedef void* MTLTexture_t;
 typedef void CAMetalLayer;
+typedef void* CAMetalDrawable_t;
 #endif
diff --git a/src/video_core/renderer_metal/renderer_metal.h b/src/video_core/renderer_metal/renderer_metal.h
index 852964d5bf..888faa8149 100644
--- a/src/video_core/renderer_metal/renderer_metal.h
+++ b/src/video_core/renderer_metal/renderer_metal.h
@@ -10,6 +10,7 @@
 #include "video_core/renderer_base.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"
 
 namespace Core {
 class TelemetrySession;
@@ -49,8 +50,7 @@ private:
     Tegra::GPU& gpu;
 
     Device device;
-    // TODO: use the layer to get the drawable when drawing directly to the screen
-    const CAMetalLayer* layer;
+    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 6009eed606..82f3dbefb7 100644
--- a/src/video_core/renderer_metal/renderer_metal.mm
+++ b/src/video_core/renderer_metal/renderer_metal.mm
@@ -12,17 +12,11 @@ RendererMetal::RendererMetal(Core::Frontend::EmuWindow& emu_window,
                              Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
                              std::unique_ptr<Core::Frontend::GraphicsContext> context_)
     : RendererBase(emu_window, std::move(context_)), device_memory{device_memory_},
-                   gpu{gpu_}, device{},
-                   layer([static_cast<const CAMetalLayer*>(render_window.GetWindowInfo().render_surface)
-                          retain]),
-                   rasterizer(gpu_, device, layer) {
-    // Give the layer our device
-    layer.device = device.GetDevice();
-}
+      gpu{gpu_}, device{},
+      swap_chain(device, static_cast<const CAMetalLayer*>(render_window.GetWindowInfo().render_surface)),
+      rasterizer(gpu_, device, swap_chain) {}
 
-RendererMetal::~RendererMetal() {
-    [layer release];
-}
+RendererMetal::~RendererMetal() = default;
 
 void RendererMetal::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
     if (framebuffers.empty()) {
@@ -31,20 +25,20 @@ void RendererMetal::Composite(std::span<const Tegra::FramebufferConfig> framebuf
 
     // HACK
     @autoreleasepool {
-        id<CAMetalDrawable> drawable = [layer nextDrawable];
+        swap_chain.AcquireNextDrawable();
 
-        MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
-        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0, 0.5, 0.0, 1.0);
-        renderPassDescriptor.colorAttachments[0].loadAction  = MTLLoadActionClear;
-        renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
-        renderPassDescriptor.colorAttachments[0].texture = drawable.texture;
+        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<MTLCommandBuffer> commandBuffer = [device.GetCommandQueue() commandBuffer];
-        id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer
-                                    renderCommandEncoderWithDescriptor:renderPassDescriptor];
-        [renderEncoder endEncoding];
-        [commandBuffer presentDrawable:drawable];
-        [commandBuffer commit];
+        id<MTLCommandBuffer> command_buffer = [device.GetCommandQueue() commandBuffer];
+        id<MTLRenderCommandEncoder> render_encoder = [command_buffer
+                                    renderCommandEncoderWithDescriptor:render_pass_descriptor];
+        [render_encoder endEncoding];
+        swap_chain.Present(command_buffer);
+        [command_buffer commit];
     }
 
     gpu.RendererFrameEndNotify();