From 7ea460a800b87ff5b5301076a9d202c715e26811 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 5 Apr 2024 06:35:06 +0200 Subject: [PATCH 1/6] metal: initial support --- CMakeLists.txt | 2 +- src/common/settings_enums.h | 2 +- src/suyu/bootmanager.cpp | 19 ++- src/suyu/bootmanager.h | 1 + src/suyu/configuration/configure_graphics.cpp | 3 + src/suyu/configuration/shared_translation.cpp | 3 + src/suyu/configuration/shared_translation.h | 3 + src/suyu/main.cpp | 2 + src/suyu_cmd/CMakeLists.txt | 2 + .../emu_window/emu_window_sdl2_mtl.cpp | 66 +++++++++++ src/suyu_cmd/emu_window/emu_window_sdl2_mtl.h | 25 ++++ src/suyu_cmd/suyu.cpp | 4 + src/video_core/CMakeLists.txt | 6 + src/video_core/renderer_metal/mtl_device.h | 27 +++++ src/video_core/renderer_metal/mtl_device.mm | 23 ++++ .../renderer_metal/mtl_rasterizer.h | 96 +++++++++++++++ .../renderer_metal/mtl_rasterizer.mm | 110 ++++++++++++++++++ src/video_core/renderer_metal/objc_bridge.h | 16 +++ .../renderer_metal/renderer_metal.h | 61 ++++++++++ .../renderer_metal/renderer_metal.mm | 60 ++++++++++ src/video_core/video_core.cpp | 7 +- 21 files changed, 533 insertions(+), 5 deletions(-) create mode 100644 src/suyu_cmd/emu_window/emu_window_sdl2_mtl.cpp create mode 100644 src/suyu_cmd/emu_window/emu_window_sdl2_mtl.h create mode 100644 src/video_core/renderer_metal/mtl_device.h create mode 100644 src/video_core/renderer_metal/mtl_device.mm create mode 100644 src/video_core/renderer_metal/mtl_rasterizer.h create mode 100644 src/video_core/renderer_metal/mtl_rasterizer.mm create mode 100644 src/video_core/renderer_metal/objc_bridge.h create mode 100644 src/video_core/renderer_metal/renderer_metal.h create mode 100644 src/video_core/renderer_metal/renderer_metal.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index 42e8615e57..36e1ba30bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ cmake_minimum_required(VERSION 3.22) set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT OFF) set(CMAKE_XCODE_EMIT_RELATIVE_PATH YES) -project(suyu) +project(suyu LANGUAGES C CXX OBJC OBJCXX) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index fabae6d687..09006f6223 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -124,7 +124,7 @@ ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed); ENUM(VramUsageMode, Conservative, Aggressive); -ENUM(RendererBackend, OpenGL, Vulkan, Null); +ENUM(RendererBackend, OpenGL, Vulkan, Metal, Null); ENUM(ShaderBackend, Glsl, Glasm, SpirV); diff --git a/src/suyu/bootmanager.cpp b/src/suyu/bootmanager.cpp index 606325bf5e..a84dcdb3a3 100644 --- a/src/suyu/bootmanager.cpp +++ b/src/suyu/bootmanager.cpp @@ -284,8 +284,8 @@ struct NullRenderWidget : public RenderWidget { GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, std::shared_ptr input_subsystem_, Core::System& system_) - : QWidget(parent), - emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)}, system{system_} { + : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)}, + system{system_} { setWindowTitle(QStringLiteral("suyu %1 | %2-%3") .arg(QString::fromUtf8(Common::g_build_name), QString::fromUtf8(Common::g_scm_branch), @@ -933,6 +933,13 @@ bool GRenderWindow::InitRenderTarget() { return false; } break; +#ifdef __APPLE__ + case Settings::RendererBackend::Metal: + if (!InitializeMetal()) { + return false; + } + break; +#endif case Settings::RendererBackend::Null: InitializeNull(); break; @@ -1048,6 +1055,14 @@ bool GRenderWindow::InitializeVulkan() { return true; } +bool GRenderWindow::InitializeMetal() { + // TODO: initialize Metal + child_widget = new NullRenderWidget(this); + main_context = std::make_unique(); + + return true; +} + void GRenderWindow::InitializeNull() { child_widget = new NullRenderWidget(this); main_context = std::make_unique(); diff --git a/src/suyu/bootmanager.h b/src/suyu/bootmanager.h index 36eb956ec7..f301b812c7 100644 --- a/src/suyu/bootmanager.h +++ b/src/suyu/bootmanager.h @@ -239,6 +239,7 @@ private: bool InitializeOpenGL(); bool InitializeVulkan(); + bool InitializeMetal(); void InitializeNull(); bool LoadOpenGL(); QStringList GetUnsupportedGLExtensions() const; diff --git a/src/suyu/configuration/configure_graphics.cpp b/src/suyu/configuration/configure_graphics.cpp index d11110a74a..0810009125 100644 --- a/src/suyu/configuration/configure_graphics.cpp +++ b/src/suyu/configuration/configure_graphics.cpp @@ -466,6 +466,9 @@ void ConfigureGraphics::ApplyConfiguration() { Settings::values.vulkan_device.SetGlobal(Settings::IsConfiguringGlobal()); Settings::values.vulkan_device.SetValue(vulkan_device_combobox->currentIndex()); break; + case Settings::RendererBackend::Metal: + // TODO + break; case Settings::RendererBackend::Null: break; } diff --git a/src/suyu/configuration/shared_translation.cpp b/src/suyu/configuration/shared_translation.cpp index 0e4d13bc64..90548d4fe5 100644 --- a/src/suyu/configuration/shared_translation.cpp +++ b/src/suyu/configuration/shared_translation.cpp @@ -331,6 +331,9 @@ std::unique_ptr ComboboxEnumeration(QWidget* parent) { PAIR(RendererBackend, OpenGL, tr("OpenGL")), #endif PAIR(RendererBackend, Vulkan, tr("Vulkan")), +#ifdef __APPLE__ + PAIR(RendererBackend, Metal, tr("Metal")), +#endif PAIR(RendererBackend, Null, tr("Null")), }}); translations->insert( diff --git a/src/suyu/configuration/shared_translation.h b/src/suyu/configuration/shared_translation.h index d5fc3b8def..9cc8ba6a31 100644 --- a/src/suyu/configuration/shared_translation.h +++ b/src/suyu/configuration/shared_translation.h @@ -56,6 +56,9 @@ static const std::map gpu_accuracy_texts_map = { static const std::map renderer_backend_texts_map = { {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))}, {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))}, +#ifdef __APPLE__ + {Settings::RendererBackend::Metal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Metal"))}, +#endif {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, }; diff --git a/src/suyu/main.cpp b/src/suyu/main.cpp index 9a3ee7f662..a23ba9e17d 100644 --- a/src/suyu/main.cpp +++ b/src/suyu/main.cpp @@ -3800,6 +3800,8 @@ void GMainWindow::OnToggleGraphicsAPI() { } else { #ifdef HAS_OPENGL api = Settings::RendererBackend::OpenGL; +#elif __APPLE__ + api = Settings::RendererBackend::Metal; #else api = Settings::RendererBackend::Null; #endif diff --git a/src/suyu_cmd/CMakeLists.txt b/src/suyu_cmd/CMakeLists.txt index 45cc281218..3aba7800ae 100644 --- a/src/suyu_cmd/CMakeLists.txt +++ b/src/suyu_cmd/CMakeLists.txt @@ -21,6 +21,8 @@ add_executable(suyu-cmd emu_window/emu_window_sdl2_null.h emu_window/emu_window_sdl2_vk.cpp emu_window/emu_window_sdl2_vk.h + emu_window/emu_window_sdl2_mtl.cpp + emu_window/emu_window_sdl2_mtl.h precompiled_headers.h sdl_config.cpp sdl_config.h diff --git a/src/suyu_cmd/emu_window/emu_window_sdl2_mtl.cpp b/src/suyu_cmd/emu_window/emu_window_sdl2_mtl.cpp new file mode 100644 index 0000000000..d84b939586 --- /dev/null +++ b/src/suyu_cmd/emu_window/emu_window_sdl2_mtl.cpp @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include + +#include "common/logging/log.h" +#include "common/scm_rev.h" +#include "suyu_cmd/emu_window/emu_window_sdl2_mtl.h" +#include "video_core/renderer_metal/renderer_metal.h" + +#include +#include + +EmuWindow_SDL2_MTL::EmuWindow_SDL2_MTL(InputCommon::InputSubsystem* input_subsystem_, + Core::System& system_, bool fullscreen) + : EmuWindow_SDL2{input_subsystem_, system_} { + const std::string window_title = fmt::format("suyu {} | {}-{} (Vulkan)", Common::g_build_name, + Common::g_scm_branch, Common::g_scm_desc); + render_window = + SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + + SDL_SysWMinfo wm; + SDL_VERSION(&wm.version); + if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { + LOG_CRITICAL(Frontend, "Failed to get information from the window manager: {}", + SDL_GetError()); + std::exit(EXIT_FAILURE); + } + + SetWindowIcon(); + + if (fullscreen) { + Fullscreen(); + ShowCursor(false); + } + + switch (wm.subsystem) { +#ifdef SDL_VIDEO_DRIVER_COCOA + case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA: + window_info.type = Core::Frontend::WindowSystemType::Cocoa; + window_info.render_surface = SDL_Metal_CreateView(render_window); + break; +#endif + default: + LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem); + std::exit(EXIT_FAILURE); + break; + } + + OnResize(); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); + SDL_PumpEvents(); + LOG_INFO(Frontend, "suyu Version: {} | {}-{} (Vulkan)", Common::g_build_name, + Common::g_scm_branch, Common::g_scm_desc); +} + +EmuWindow_SDL2_MTL::~EmuWindow_SDL2_MTL() = default; + +std::unique_ptr EmuWindow_SDL2_MTL::CreateSharedContext() const { + return std::make_unique(); +} diff --git a/src/suyu_cmd/emu_window/emu_window_sdl2_mtl.h b/src/suyu_cmd/emu_window/emu_window_sdl2_mtl.h new file mode 100644 index 0000000000..c4fcbe5d68 --- /dev/null +++ b/src/suyu_cmd/emu_window/emu_window_sdl2_mtl.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/frontend/emu_window.h" +#include "suyu_cmd/emu_window/emu_window_sdl2.h" + +namespace Core { +class System; +} + +namespace InputCommon { +class InputSubsystem; +} + +class EmuWindow_SDL2_MTL final : public EmuWindow_SDL2 { +public: + explicit EmuWindow_SDL2_MTL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system, + bool fullscreen); + ~EmuWindow_SDL2_MTL() override; + + std::unique_ptr CreateSharedContext() const override; +}; diff --git a/src/suyu_cmd/suyu.cpp b/src/suyu_cmd/suyu.cpp index c9c35464e6..12114b565d 100644 --- a/src/suyu_cmd/suyu.cpp +++ b/src/suyu_cmd/suyu.cpp @@ -38,6 +38,7 @@ #include "sdl_config.h" #include "suyu_cmd/emu_window/emu_window_sdl2.h" #include "suyu_cmd/emu_window/emu_window_sdl2_gl.h" +#include "suyu_cmd/emu_window/emu_window_sdl2_mtl.h" #include "suyu_cmd/emu_window/emu_window_sdl2_null.h" #include "suyu_cmd/emu_window/emu_window_sdl2_vk.h" #include "video_core/renderer_base.h" @@ -385,6 +386,9 @@ int main(int argc, char** argv) { case Settings::RendererBackend::Vulkan: emu_window = std::make_unique(&input_subsystem, system, fullscreen); break; + case Settings::RendererBackend::Metal: + emu_window = std::make_unique(&input_subsystem, system, fullscreen); + break; case Settings::RendererBackend::Null: emu_window = std::make_unique(&input_subsystem, system, fullscreen); break; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e5cd0278fa..4b1a9e7503 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -372,6 +372,12 @@ if (APPLE) renderer_opengl/util_shaders.cpp renderer_opengl/util_shaders.h ) + + list(APPEND sources + renderer_metal/mtl_device.mm + renderer_metal/mtl_rasterizer.mm + renderer_metal/renderer_metal.mm + ) endif() add_library(video_core STATIC ${sources}) diff --git a/src/video_core/renderer_metal/mtl_device.h b/src/video_core/renderer_metal/mtl_device.h new file mode 100644 index 0000000000..d6414cd222 --- /dev/null +++ b/src/video_core/renderer_metal/mtl_device.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_metal/objc_bridge.h" + +namespace Metal { + +class Device { +public: + explicit Device(); + ~Device(); + + MTLDevice_t GetDevice() const { + return device; + } + + MTLCommandQueue_t GetCommandQueue() const { + return command_queue; + } + +private: + MTLDevice_t device; + MTLCommandQueue_t command_queue; +}; + +} // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_device.mm b/src/video_core/renderer_metal/mtl_device.mm new file mode 100644 index 0000000000..2c79381faa --- /dev/null +++ b/src/video_core/renderer_metal/mtl_device.mm @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/renderer_metal/mtl_device.h" + +namespace Metal { + +Device::Device() { + device = MTLCreateSystemDefaultDevice(); + if (!device) { + throw std::runtime_error("Failed to create Metal device"); + } + command_queue = [device newCommandQueue]; + if (!command_queue) { + throw std::runtime_error("Failed to create Metal command queue"); + } +} + +Device::~Device() { + [command_queue release]; + [device release]; +} + +} // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_rasterizer.h b/src/video_core/renderer_metal/mtl_rasterizer.h new file mode 100644 index 0000000000..5637e40344 --- /dev/null +++ b/src/video_core/renderer_metal/mtl_rasterizer.h @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "video_core/control/channel_state_cache.h" +#include "video_core/engines/maxwell_dma.h" +#include "video_core/rasterizer_interface.h" +#include "video_core/renderer_metal/objc_bridge.h" + +namespace Core { +class System; +} + +namespace Metal { + +class Device; + +class RasterizerMetal; + +class AccelerateDMA : public Tegra::Engines::AccelerateDMAInterface { +public: + explicit AccelerateDMA(); + bool BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) override; + bool BufferClear(GPUVAddr src_address, u64 amount, u32 value) override; + bool ImageToBuffer(const Tegra::DMA::ImageCopy& copy_info, const Tegra::DMA::ImageOperand& src, + const Tegra::DMA::BufferOperand& dst) override { + return false; + } + bool BufferToImage(const Tegra::DMA::ImageCopy& copy_info, const Tegra::DMA::BufferOperand& src, + const Tegra::DMA::ImageOperand& dst) override { + return false; + } +}; + +class RasterizerMetal final : public VideoCore::RasterizerInterface, + protected VideoCommon::ChannelSetupCaches { +public: + explicit RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, const CAMetalLayer* layer_); + ~RasterizerMetal() override; + + void Draw(bool is_indexed, u32 instance_count) override; + void DrawTexture() override; + void Clear(u32 layer_count) override; + void DispatchCompute() override; + void ResetCounter(VideoCommon::QueryType type) override; + void Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, + VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) override; + void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size) override; + void DisableGraphicsUniformBuffer(size_t stage, u32 index) override; + void FlushAll() override; + void FlushRegion(DAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + bool MustFlushRegion(DAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + void InvalidateRegion(DAddr addr, u64 size, + VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + void OnCacheInvalidation(DAddr addr, u64 size) override; + bool OnCPUWrite(DAddr addr, u64 size) override; + VideoCore::RasterizerDownloadArea GetFlushArea(DAddr addr, u64 size) override; + void InvalidateGPUCache() override; + void UnmapMemory(DAddr addr, u64 size) override; + void ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) override; + void SignalFence(std::function&& func) override; + void SyncOperation(std::function&& func) override; + void SignalSyncPoint(u32 value) override; + void SignalReference() override; + void ReleaseFences(bool force) override; + void FlushAndInvalidateRegion( + DAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + void WaitForIdle() override; + void FragmentBarrier() override; + void TiledCacheBarrier() override; + void FlushCommands() override; + void TickFrame() override; + bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, + const Tegra::Engines::Fermi2D::Surface& dst, + const Tegra::Engines::Fermi2D::Config& copy_config) override; + Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; + void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span memory) override; + void LoadDiskResources(u64 title_id, std::stop_token stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) override; + void InitializeChannel(Tegra::Control::ChannelState& channel) override; + void BindChannel(Tegra::Control::ChannelState& channel) override; + void ReleaseChannel(s32 channel_id) override; + +private: + Tegra::GPU& gpu; + AccelerateDMA accelerate_dma; + + const Device& device; + const CAMetalLayer* layer; +}; + +} // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_rasterizer.mm b/src/video_core/renderer_metal/mtl_rasterizer.mm new file mode 100644 index 0000000000..aa44783bb1 --- /dev/null +++ b/src/video_core/renderer_metal/mtl_rasterizer.mm @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/alignment.h" +#include "video_core/control/channel_state.h" +#include "video_core/host1x/host1x.h" +#include "video_core/memory_manager.h" +#include "video_core/renderer_metal/mtl_rasterizer.h" +#include "video_core/renderer_metal/mtl_device.h" + +#include + +namespace Metal { + +AccelerateDMA::AccelerateDMA() = default; + +bool AccelerateDMA::BufferCopy(GPUVAddr start_address, GPUVAddr end_address, u64 amount) { + return true; +} +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() = default; + +void RasterizerMetal::Draw(bool is_indexed, u32 instance_count) {} +void RasterizerMetal::DrawTexture() {} +void RasterizerMetal::Clear(u32 layer_count) {} +void RasterizerMetal::DispatchCompute() {} +void RasterizerMetal::ResetCounter(VideoCommon::QueryType type) {} +void RasterizerMetal::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, + VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) { + if (!gpu_memory) { + return; + } + if (True(flags & VideoCommon::QueryPropertiesFlags::HasTimeout)) { + u64 ticks = gpu.GetTicks(); + gpu_memory->Write(gpu_addr + 8, ticks); + gpu_memory->Write(gpu_addr, static_cast(payload)); + } else { + gpu_memory->Write(gpu_addr, payload); + } +} +void RasterizerMetal::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, + u32 size) {} +void RasterizerMetal::DisableGraphicsUniformBuffer(size_t stage, u32 index) {} +void RasterizerMetal::FlushAll() {} +void RasterizerMetal::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType) {} +bool RasterizerMetal::MustFlushRegion(DAddr addr, u64 size, VideoCommon::CacheType) { + return false; +} +void RasterizerMetal::InvalidateRegion(DAddr addr, u64 size, VideoCommon::CacheType) {} +bool RasterizerMetal::OnCPUWrite(PAddr addr, u64 size) { + return false; +} +void RasterizerMetal::OnCacheInvalidation(PAddr addr, u64 size) {} +VideoCore::RasterizerDownloadArea RasterizerMetal::GetFlushArea(PAddr addr, u64 size) { + VideoCore::RasterizerDownloadArea new_area{ + .start_address = Common::AlignDown(addr, Core::DEVICE_PAGESIZE), + .end_address = Common::AlignUp(addr + size, Core::DEVICE_PAGESIZE), + .preemtive = true, + }; + return new_area; +} +void RasterizerMetal::InvalidateGPUCache() {} +void RasterizerMetal::UnmapMemory(DAddr addr, u64 size) {} +void RasterizerMetal::ModifyGPUMemory(size_t as_id, GPUVAddr addr, u64 size) {} +void RasterizerMetal::SignalFence(std::function&& func) { + func(); +} +void RasterizerMetal::SyncOperation(std::function&& func) { + func(); +} +void RasterizerMetal::SignalSyncPoint(u32 value) { + auto& syncpoint_manager = gpu.Host1x().GetSyncpointManager(); + syncpoint_manager.IncrementGuest(value); + syncpoint_manager.IncrementHost(value); +} +void RasterizerMetal::SignalReference() {} +void RasterizerMetal::ReleaseFences(bool) {} +void RasterizerMetal::FlushAndInvalidateRegion(DAddr addr, u64 size, VideoCommon::CacheType) {} +void RasterizerMetal::WaitForIdle() {} +void RasterizerMetal::FragmentBarrier() {} +void RasterizerMetal::TiledCacheBarrier() {} +void RasterizerMetal::FlushCommands() {} +void RasterizerMetal::TickFrame() {} +Tegra::Engines::AccelerateDMAInterface& RasterizerMetal::AccessAccelerateDMA() { + return accelerate_dma; +} +bool RasterizerMetal::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, + const Tegra::Engines::Fermi2D::Surface& dst, + const Tegra::Engines::Fermi2D::Config& copy_config) { + return true; +} +void RasterizerMetal::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span memory) {} +void RasterizerMetal::LoadDiskResources(u64 title_id, std::stop_token stop_loading, + const VideoCore::DiskResourceLoadCallback& callback) {} +void RasterizerMetal::InitializeChannel(Tegra::Control::ChannelState& channel) { + CreateChannel(channel); +} +void RasterizerMetal::BindChannel(Tegra::Control::ChannelState& channel) { + BindToChannel(channel.bind_id); +} +void RasterizerMetal::ReleaseChannel(s32 channel_id) { + EraseChannel(channel_id); +} + +} // namespace Metal diff --git a/src/video_core/renderer_metal/objc_bridge.h b/src/video_core/renderer_metal/objc_bridge.h new file mode 100644 index 0000000000..5fd55169c3 --- /dev/null +++ b/src/video_core/renderer_metal/objc_bridge.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef __OBJC__ +#import +#import +typedef id MTLDevice_t; +typedef id MTLCommandQueue_t; +typedef id MTLTexture_t; +#else +typedef void* MTLDevice_t; +typedef void* MTLCommandQueue_t; +typedef void* MTLTexture_t; +typedef void CAMetalLayer; +#endif diff --git a/src/video_core/renderer_metal/renderer_metal.h b/src/video_core/renderer_metal/renderer_metal.h new file mode 100644 index 0000000000..12c3396583 --- /dev/null +++ b/src/video_core/renderer_metal/renderer_metal.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#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_device.h" +#include "video_core/renderer_metal/mtl_rasterizer.h" + +namespace Core { +class TelemetrySession; +} + +namespace Core::Memory { +class Memory; +} + +namespace Tegra { +class GPU; +} + +namespace Metal { + +class RendererMetal final : public VideoCore::RendererBase { +public: + explicit RendererMetal(Core::Frontend::EmuWindow& emu_window, + Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_, + std::unique_ptr context); + ~RendererMetal() override; + + void Composite(std::span framebuffer) override; + + std::vector GetAppletCaptureBuffer() override; + + VideoCore::RasterizerInterface* ReadRasterizer() override { + return &rasterizer; + } + + [[nodiscard]] std::string GetDeviceVendor() const override { + return "Apple"; + } + +private: + Tegra::MaxwellDeviceMemoryManager& device_memory; + Tegra::GPU& gpu; + + Device device; + // TODO: use the layer to get the drawable when drawing directly to the screen + const CAMetalLayer* layer; + + RasterizerMetal rasterizer; + + // HACK + MTLTexture_t renderTexture; +}; + +} // namespace Metal diff --git a/src/video_core/renderer_metal/renderer_metal.mm b/src/video_core/renderer_metal/renderer_metal.mm new file mode 100644 index 0000000000..a3416805d3 --- /dev/null +++ b/src/video_core/renderer_metal/renderer_metal.mm @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/frontend/emu_window.h" +#include "core/frontend/graphics_context.h" +#include "video_core/capture.h" +#include "video_core/renderer_metal/renderer_metal.h" +#include "video_core/renderer_metal/mtl_device.h" + +namespace Metal { + +RendererMetal::RendererMetal(Core::Frontend::EmuWindow& emu_window, + Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_, + std::unique_ptr context_) + : RendererBase(emu_window, std::move(context_)), device_memory{device_memory_}, + gpu{gpu_}, device{}, + layer(static_cast(render_window.GetWindowInfo().render_surface)), + rasterizer(gpu_, device, layer) { + // HACK + MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm_sRGB + width:1280 + height:720 + mipmapped:NO]; + renderTexture = [device.GetDevice() newTextureWithDescriptor:textureDescriptor]; +} + +RendererMetal::~RendererMetal() = default; + +void RendererMetal::Composite(std::span framebuffers) { + if (framebuffers.empty()) { + return; + } + + // HACK + @autoreleasepool { + //id drawable = [layer nextDrawable]; + + 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 = renderTexture;//drawable.texture; + + id commandBuffer = [device.GetCommandQueue() commandBuffer]; + id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + [renderEncoder endEncoding]; + //[commandBuffer presentDrawable:drawable]; + [commandBuffer commit]; + } + + gpu.RendererFrameEndNotify(); + rasterizer.TickFrame(); + + render_window.OnFrameDisplayed(); +} + +std::vector RendererMetal::GetAppletCaptureBuffer() { + return std::vector(VideoCore::Capture::TiledSize); +} + +} // namespace Metal diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 509ba8a5b4..efcf315e02 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -11,6 +11,9 @@ #include "video_core/renderer_null/renderer_null.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" +#ifdef __APPLE__ +#include "video_core/renderer_metal/renderer_metal.h" +#endif #include "video_core/video_core.h" namespace { @@ -22,7 +25,9 @@ std::unique_ptr CreateRenderer( switch (Settings::values.renderer_backend.GetValue()) { #ifdef __APPLE__ - // do nothing for now, include metal in here at later date. + case Settings::RendererBackend::Metal: + return std::make_unique(emu_window, device_memory, gpu, + std::move(context)); #else // openGL, not supported on Apple so not bothering to include if macos case Settings::RendererBackend::OpenGL: From 380af618d3bc7e4514fe6ac906b3c79aa8b472a4 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 5 Apr 2024 14:19:07 +0200 Subject: [PATCH 2/6] metal: draw to on-screen qwidget --- src/suyu/bootmanager.cpp | 13 ++++++++-- .../renderer_metal/renderer_metal.h | 3 --- .../renderer_metal/renderer_metal.mm | 24 +++++++++---------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/suyu/bootmanager.cpp b/src/suyu/bootmanager.cpp index a84dcdb3a3..023ece4868 100644 --- a/src/suyu/bootmanager.cpp +++ b/src/suyu/bootmanager.cpp @@ -277,6 +277,14 @@ struct VulkanRenderWidget : public RenderWidget { } }; +struct MetalRenderWidget : public RenderWidget { + explicit MetalRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { + // HACK: manually resize the renderable area + resize(600, 400); + windowHandle()->setSurfaceType(QWindow::MetalSurface); + } +}; + struct NullRenderWidget : public RenderWidget { explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {} }; @@ -1056,8 +1064,9 @@ bool GRenderWindow::InitializeVulkan() { } bool GRenderWindow::InitializeMetal() { - // TODO: initialize Metal - child_widget = new NullRenderWidget(this); + auto child = new MetalRenderWidget(this); + child_widget = child; + child_widget->windowHandle()->create(); main_context = std::make_unique(); return true; diff --git a/src/video_core/renderer_metal/renderer_metal.h b/src/video_core/renderer_metal/renderer_metal.h index 12c3396583..852964d5bf 100644 --- a/src/video_core/renderer_metal/renderer_metal.h +++ b/src/video_core/renderer_metal/renderer_metal.h @@ -53,9 +53,6 @@ private: const CAMetalLayer* layer; RasterizerMetal rasterizer; - - // HACK - MTLTexture_t renderTexture; }; } // namespace Metal diff --git a/src/video_core/renderer_metal/renderer_metal.mm b/src/video_core/renderer_metal/renderer_metal.mm index a3416805d3..6009eed606 100644 --- a/src/video_core/renderer_metal/renderer_metal.mm +++ b/src/video_core/renderer_metal/renderer_metal.mm @@ -13,17 +13,16 @@ RendererMetal::RendererMetal(Core::Frontend::EmuWindow& emu_window, std::unique_ptr context_) : RendererBase(emu_window, std::move(context_)), device_memory{device_memory_}, gpu{gpu_}, device{}, - layer(static_cast(render_window.GetWindowInfo().render_surface)), + layer([static_cast(render_window.GetWindowInfo().render_surface) + retain]), rasterizer(gpu_, device, layer) { - // HACK - MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm_sRGB - width:1280 - height:720 - mipmapped:NO]; - renderTexture = [device.GetDevice() newTextureWithDescriptor:textureDescriptor]; + // Give the layer our device + layer.device = device.GetDevice(); } -RendererMetal::~RendererMetal() = default; +RendererMetal::~RendererMetal() { + [layer release]; +} void RendererMetal::Composite(std::span framebuffers) { if (framebuffers.empty()) { @@ -32,18 +31,19 @@ void RendererMetal::Composite(std::span framebuf // HACK @autoreleasepool { - //id drawable = [layer nextDrawable]; + id drawable = [layer nextDrawable]; 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 = renderTexture;//drawable.texture; + renderPassDescriptor.colorAttachments[0].texture = drawable.texture; id commandBuffer = [device.GetCommandQueue() commandBuffer]; - id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + id renderEncoder = [commandBuffer + renderCommandEncoderWithDescriptor:renderPassDescriptor]; [renderEncoder endEncoding]; - //[commandBuffer presentDrawable:drawable]; + [commandBuffer presentDrawable:drawable]; [commandBuffer commit]; } From 79ff60356dc754950916d092e9433feacf098b5a Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 5 Apr 2024 16:21:39 +0200 Subject: [PATCH 3/6] metal: create swap chain to manage metal layer --- src/video_core/CMakeLists.txt | 1 + .../renderer_metal/mtl_rasterizer.h | 8 +++- .../renderer_metal/mtl_rasterizer.mm | 30 +++++++++++++-- .../renderer_metal/mtl_swap_chain.h | 29 ++++++++++++++ .../renderer_metal/mtl_swap_chain.mm | 30 +++++++++++++++ src/video_core/renderer_metal/objc_bridge.h | 4 ++ .../renderer_metal/renderer_metal.h | 4 +- .../renderer_metal/renderer_metal.mm | 38 ++++++++----------- 8 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 src/video_core/renderer_metal/mtl_swap_chain.h create mode 100644 src/video_core/renderer_metal/mtl_swap_chain.mm 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 { 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 typedef id MTLDevice_t; typedef id MTLCommandQueue_t; +typedef id MTLCommandBuffer_t; typedef id MTLTexture_t; +typedef id 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 context_) : RendererBase(emu_window, std::move(context_)), device_memory{device_memory_}, - gpu{gpu_}, device{}, - layer([static_cast(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(render_window.GetWindowInfo().render_surface)), + rasterizer(gpu_, device, swap_chain) {} -RendererMetal::~RendererMetal() { - [layer release]; -} +RendererMetal::~RendererMetal() = default; void RendererMetal::Composite(std::span framebuffers) { if (framebuffers.empty()) { @@ -31,20 +25,20 @@ void RendererMetal::Composite(std::span framebuf // HACK @autoreleasepool { - id 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 commandBuffer = [device.GetCommandQueue() commandBuffer]; - id renderEncoder = [commandBuffer - renderCommandEncoderWithDescriptor:renderPassDescriptor]; - [renderEncoder endEncoding]; - [commandBuffer presentDrawable:drawable]; - [commandBuffer commit]; + 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]; } gpu.RendererFrameEndNotify(); From 35b751de1b99f336420fa4bad1c5079656fde5f1 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Fri, 5 Apr 2024 17:10:21 +0200 Subject: [PATCH 4/6] 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(); From ea5dc91b9d81210fabc7f8a22588dd8beb54a630 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 6 Apr 2024 08:55:11 +0200 Subject: [PATCH 5/6] metal: add basic staging buffer pool and texture cache --- src/video_core/CMakeLists.txt | 3 + .../renderer_metal/mtl_command_recorder.h | 7 + .../renderer_metal/mtl_command_recorder.mm | 10 +- .../renderer_metal/mtl_rasterizer.h | 7 +- .../renderer_metal/mtl_rasterizer.mm | 10 +- .../renderer_metal/mtl_staging_buffer_pool.h | 101 ++++++++ .../renderer_metal/mtl_staging_buffer_pool.mm | 116 ++++++++++ .../renderer_metal/mtl_texture_cache.h | 219 ++++++++++++++++++ .../renderer_metal/mtl_texture_cache.mm | 123 ++++++++++ .../renderer_metal/mtl_texture_cache_base.cpp | 8 + src/video_core/renderer_metal/objc_bridge.h | 4 + .../renderer_metal/renderer_metal.mm | 6 +- 12 files changed, 602 insertions(+), 12 deletions(-) create mode 100644 src/video_core/renderer_metal/mtl_staging_buffer_pool.h create mode 100644 src/video_core/renderer_metal/mtl_staging_buffer_pool.mm create mode 100644 src/video_core/renderer_metal/mtl_texture_cache.h create mode 100644 src/video_core/renderer_metal/mtl_texture_cache.mm create mode 100644 src/video_core/renderer_metal/mtl_texture_cache_base.cpp diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index cb60d096a8..ac35538662 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -377,7 +377,10 @@ if (APPLE) renderer_metal/mtl_command_recorder.mm renderer_metal/mtl_device.mm renderer_metal/mtl_rasterizer.mm + renderer_metal/mtl_staging_buffer_pool.mm renderer_metal/mtl_swap_chain.mm + renderer_metal/mtl_texture_cache.mm + renderer_metal/mtl_texture_cache_base.cpp renderer_metal/renderer_metal.mm ) endif() diff --git a/src/video_core/renderer_metal/mtl_command_recorder.h b/src/video_core/renderer_metal/mtl_command_recorder.h index 12a78b374c..81895572f9 100644 --- a/src/video_core/renderer_metal/mtl_command_recorder.h +++ b/src/video_core/renderer_metal/mtl_command_recorder.h @@ -18,6 +18,13 @@ public: void BeginRenderPass(MTLRenderPassDescriptor* render_pass_descriptor); + void CheckIfRenderPassIsActive() { + if (!encoder || encoder_type != EncoderType::Render) { + throw std::runtime_error( + "Trying to perform render command, but render pass is not active"); + } + } + void RequireComputeEncoder(); void RequireBlitEncoder(); diff --git a/src/video_core/renderer_metal/mtl_command_recorder.mm b/src/video_core/renderer_metal/mtl_command_recorder.mm index f44e9811b2..b005d55b66 100644 --- a/src/video_core/renderer_metal/mtl_command_recorder.mm +++ b/src/video_core/renderer_metal/mtl_command_recorder.mm @@ -49,10 +49,12 @@ void CommandRecorder::Present(CAMetalDrawable_t drawable) { } void CommandRecorder::Submit() { - EndEncoding(); - [command_buffer commit]; - [command_buffer release]; - command_buffer = nil; + if (command_buffer) { + EndEncoding(); + [command_buffer commit]; + [command_buffer release]; + command_buffer = nil; + } } void CommandRecorder::RequireCommandBuffer() { diff --git a/src/video_core/renderer_metal/mtl_rasterizer.h b/src/video_core/renderer_metal/mtl_rasterizer.h index 87bbfea585..95263566af 100644 --- a/src/video_core/renderer_metal/mtl_rasterizer.h +++ b/src/video_core/renderer_metal/mtl_rasterizer.h @@ -15,6 +15,7 @@ class System; namespace Metal { class Device; +class CommandRecorder; class SwapChain; class RasterizerMetal; @@ -37,7 +38,8 @@ public: class RasterizerMetal final : public VideoCore::RasterizerInterface, protected VideoCommon::ChannelSetupCaches { public: - explicit RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, const SwapChain& swap_chain_); + explicit RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, + CommandRecorder& command_recorder_, const SwapChain& swap_chain_); ~RasterizerMetal() override; void Draw(bool is_indexed, u32 instance_count) override; @@ -91,9 +93,8 @@ private: AccelerateDMA accelerate_dma; const Device& device; + CommandRecorder& command_recorder; 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 65e4eae2a5..3554670cbd 100644 --- a/src/video_core/renderer_metal/mtl_rasterizer.mm +++ b/src/video_core/renderer_metal/mtl_rasterizer.mm @@ -8,8 +8,9 @@ #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_command_recorder.h" #include "video_core/renderer_metal/mtl_device.h" +#include "video_core/renderer_metal/mtl_rasterizer.h" #include @@ -24,11 +25,14 @@ bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) { return true; } -RasterizerMetal::RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, const SwapChain& swap_chain_) - : gpu{gpu_}, device{device_}, swap_chain{swap_chain_} {} +RasterizerMetal::RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, + CommandRecorder& command_recorder_, const SwapChain& swap_chain_) + : gpu{gpu_}, device{device_}, command_recorder{command_recorder_}, swap_chain{swap_chain_} {} RasterizerMetal::~RasterizerMetal() = default; void RasterizerMetal::Draw(bool is_indexed, u32 instance_count) { + // TODO: uncomment + //command_recorder.CheckIfRenderPassIsActive(); //const auto& draw_state = maxwell3d->draw_manager->GetDrawState(); if (is_indexed) { std::cout << "DrawIndexed" << std::endl; diff --git a/src/video_core/renderer_metal/mtl_staging_buffer_pool.h b/src/video_core/renderer_metal/mtl_staging_buffer_pool.h new file mode 100644 index 0000000000..24584c9ac9 --- /dev/null +++ b/src/video_core/renderer_metal/mtl_staging_buffer_pool.h @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_types.h" + +#include "objc_bridge.h" +#include "video_core/renderer_metal/objc_bridge.h" + +namespace Metal { + +class Device; +class CommandRecorder; + +enum class MemoryUsage { + DeviceLocal, + Upload, + Download, +}; + +struct StagingBufferRef { + StagingBufferRef(MTLBuffer_t buffer_, size_t offset_, std::span mapped_span_); + ~StagingBufferRef(); + + MTLBuffer_t buffer; + size_t offset; + std::span mapped_span; +}; + +struct StagingBuffer { + StagingBuffer(MTLBuffer_t buffer_, std::span mapped_span_); + ~StagingBuffer(); + + MTLBuffer_t buffer; + std::span mapped_span; + + StagingBufferRef Ref() const noexcept; +}; + +class StagingBufferPool { +public: + static constexpr size_t NUM_SYNCS = 16; + + explicit StagingBufferPool(const Device& device, CommandRecorder& command_recorder_); + ~StagingBufferPool(); + + StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); + void FreeDeferred(StagingBufferRef& ref); + + [[nodiscard]] MTLBuffer_t GetSTreamBufferHandle() const noexcept { + return stream_buffer; + } + + void TickFrame(); + +private: + struct StagingBuffers { + std::vector entries; + size_t delete_index = 0; + size_t iterate_index = 0; + }; + + static constexpr size_t NUM_LEVELS = sizeof(size_t) * CHAR_BIT; + using StagingBuffersCache = std::array; + + StagingBufferRef GetStreamBuffer(size_t size); + + StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false); + + StagingBufferRef CreateStagingBuffer(size_t size, MemoryUsage usage, bool deferred); + + StagingBuffersCache& GetCache(MemoryUsage usage); + + void ReleaseCache(MemoryUsage usage); + + void ReleaseLevel(StagingBuffersCache& cache, size_t log2); + + const Device& device; + CommandRecorder& command_recorder; + + MTLBuffer_t stream_buffer{}; + + size_t iterator = 0; + size_t used_iterator = 0; + size_t free_iterator = 0; + std::array sync_ticks{}; + + StagingBuffersCache device_local_cache; + StagingBuffersCache upload_cache; + StagingBuffersCache download_cache; + + size_t current_delete_level = 0; + u64 buffer_index = 0; + u64 unique_ids{}; +}; + +} // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_staging_buffer_pool.mm b/src/video_core/renderer_metal/mtl_staging_buffer_pool.mm new file mode 100644 index 0000000000..b69720a44e --- /dev/null +++ b/src/video_core/renderer_metal/mtl_staging_buffer_pool.mm @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include + +#include + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/bit_util.h" +#include "common/common_types.h" +#include "common/literals.h" +#include "video_core/renderer_metal/mtl_command_recorder.h" +#include "video_core/renderer_metal/mtl_device.h" +#include "video_core/renderer_metal/mtl_staging_buffer_pool.h" + +namespace Metal { + +StagingBufferRef::StagingBufferRef(MTLBuffer_t buffer_, size_t offset_, std::span mapped_span_) + : buffer{[buffer_ retain]}, offset{offset_}, mapped_span{mapped_span_} {} + +StagingBufferRef::~StagingBufferRef() { + [buffer release]; +} + +StagingBuffer::StagingBuffer(MTLBuffer_t buffer_, std::span mapped_span_) + : buffer{[buffer_ retain]}, mapped_span{mapped_span_} {} + +StagingBuffer::~StagingBuffer() { + [buffer release]; +} + +StagingBufferRef StagingBuffer::Ref() const noexcept { + return StagingBufferRef(buffer, 0, mapped_span); +} + +// TODO: use the _MiB suffix +constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;//128_MiB; +constexpr size_t REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS; + +StagingBufferPool::StagingBufferPool(const Device& device_, CommandRecorder& command_recorder_) + : device{device_}, command_recorder{command_recorder_} { + stream_buffer = [device.GetDevice() newBufferWithLength:STREAM_BUFFER_SIZE + options:MTLResourceStorageModePrivate]; +} + +StagingBufferPool::~StagingBufferPool() = default; + +StagingBufferRef StagingBufferPool::Request(size_t size, MemoryUsage usage, bool deferred) { + if (!deferred && usage == MemoryUsage::Upload && size <= REGION_SIZE) { + return GetStreamBuffer(size); + } + + return GetStagingBuffer(size, usage, deferred); +} + +void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) { + // TODO: implement this +} + +void StagingBufferPool::TickFrame() { + current_delete_level = (current_delete_level + 1) % NUM_LEVELS; + + ReleaseCache(MemoryUsage::DeviceLocal); + ReleaseCache(MemoryUsage::Upload); + ReleaseCache(MemoryUsage::Download); +} + +StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { + // TODO: implement this + + // HACK + return GetStagingBuffer(size, MemoryUsage::Upload); +} + +StagingBufferRef StagingBufferPool::GetStagingBuffer(size_t size, MemoryUsage usage, + bool deferred) { + return CreateStagingBuffer(size, usage, deferred); +} + +StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage usage, + bool deferred) { + const u32 log2 = Common::Log2Ceil64(size); + MTLBuffer_t buffer = [device.GetDevice() newBufferWithLength:size + options:MTLResourceStorageModePrivate]; + // TODO: check if the mapped span is correct + std::span mapped_span(static_cast([buffer contents]), size); + auto& entry = GetCache(usage)[log2].entries.emplace_back(buffer, mapped_span); + + return entry.Ref(); +} + +StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage usage) { + switch (usage) { + case MemoryUsage::DeviceLocal: + return device_local_cache; + case MemoryUsage::Upload: + return upload_cache; + case MemoryUsage::Download: + return download_cache; + default: + ASSERT_MSG(false, "Invalid memory usage={}", usage); + return upload_cache; + } +} + +void StagingBufferPool::ReleaseCache(MemoryUsage usage) { + ReleaseLevel(GetCache(usage), current_delete_level); +} + +void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) { + // TODO: implement this +} + +} // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_texture_cache.h b/src/video_core/renderer_metal/mtl_texture_cache.h new file mode 100644 index 0000000000..a866bf731e --- /dev/null +++ b/src/video_core/renderer_metal/mtl_texture_cache.h @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "video_core/texture_cache/texture_cache_base.h" + +#include "shader_recompiler/shader_info.h" +#include "video_core/renderer_metal/mtl_staging_buffer_pool.h" +#include "video_core/renderer_metal/objc_bridge.h" +#include "video_core/texture_cache/image_view_base.h" + +namespace Settings { +struct ResolutionScalingInfo; +} + +namespace Metal { + +using Common::SlotVector; +using VideoCommon::ImageId; +using VideoCommon::NUM_RT; +using VideoCommon::Region2D; +using VideoCommon::RenderTargets; +using VideoCore::Surface::PixelFormat; + +class Device; +class Image; +class ImageView; +class Framebuffer; + +class TextureCacheRuntime { +public: + explicit TextureCacheRuntime(const Device& device_); + + void Finish(); + + StagingBufferRef UploadStagingBuffer(size_t size); + + StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); + + void FreeDeferredStagingBuffer(StagingBufferRef& ref); + + bool CanUploadMSAA() const noexcept { + return true; + } + + void TickFrame(); + + u64 GetDeviceLocalMemory() const; + + u64 GetDeviceMemoryUsage() const; + + bool CanReportMemoryUsage() const; + + void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, + const Region2D& dst_region, const Region2D& src_region, + Tegra::Engines::Fermi2D::Filter filter, + Tegra::Engines::Fermi2D::Operation operation); + + void CopyImage(Image& dst, Image& src, std::span copies); + + void CopyImageMSAA(Image& dst, Image& src, std::span copies); + + bool ShouldReinterpret(Image& dst, Image& src); + + void ReinterpretImage(Image& dst, Image& src, std::span copies); + + void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view); + + void InsertUploadMemoryBarrier(); + + void TransitionImageLayout(Image& image) {} + + void AccelerateImageUpload(Image&, const StagingBufferRef&, + std::span); + + bool HasNativeBgr() const noexcept { + return true; + } + + bool HasBrokenTextureViewFormats() const noexcept { + return false; + } + + void BarrierFeedbackLoop(); + + const Device& device; + const Settings::ResolutionScalingInfo& resolution; +}; + +class Image : public VideoCommon::ImageBase { +public: + explicit Image(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info, + GPUVAddr gpu_addr, VAddr cpu_addr); + explicit Image(const VideoCommon::NullImageParams&); + + ~Image(); + + Image(const Image&) = delete; + Image& operator=(const Image&) = delete; + + Image(Image&&) = default; + Image& operator=(Image&&) = default; + + void UploadMemory(MTLBuffer_t buffer, size_t offset, + std::span copies); + + void UploadMemory(const StagingBufferRef& map, + std::span copies); + + void DownloadMemory(MTLBuffer_t buffer, size_t offset, + std::span copies); + + void DownloadMemory(std::span buffers, std::span offsets, + std::span copies); + + void DownloadMemory(const StagingBufferRef& map, + std::span copies); + + bool IsRescaled() const; + + bool ScaleUp(bool ignore = false); + + bool ScaleDown(bool ignore = false); + + MTLTexture_t GetHandle() const noexcept { + return texture; + } + +private: + MTLTexture_t texture; + bool initialized = false; +}; + +class ImageView : public VideoCommon::ImageViewBase { +public: + explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&); + explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&, + const SlotVector&); + explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&, + const VideoCommon::ImageViewInfo&, GPUVAddr); + explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams&); + + ~ImageView(); + + ImageView(const ImageView&) = delete; + ImageView& operator=(const ImageView&) = delete; + + ImageView(ImageView&&) = default; + ImageView& operator=(ImageView&&) = default; + + MTLTexture_t GetHandle() const noexcept { + return texture; + } + +private: + MTLTexture_t texture; +}; + +class ImageAlloc : public VideoCommon::ImageAllocBase {}; + +class Sampler { +public: + explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&); + + MTLSamplerState_t GetHandle() const noexcept { + return sampler_state; + } + +private: + MTLSamplerState_t sampler_state; +}; + +class Framebuffer { +public: + explicit Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers, + ImageView* depth_buffer, const VideoCommon::RenderTargets& key); + ~Framebuffer(); + + Framebuffer(const Framebuffer&) = delete; + Framebuffer& operator=(const Framebuffer&) = delete; + + Framebuffer(Framebuffer&&) = default; + Framebuffer& operator=(Framebuffer&&) = default; + + void CreateRenderPassDescriptor(TextureCacheRuntime& runtime, + std::span color_buffers, + ImageView* depth_buffer, bool is_rescaled, size_t width, + size_t height); + + MTLRenderPassDescriptor* GetHandle() const noexcept { + return render_pass; + } + +private: + MTLRenderPassDescriptor* render_pass{}; +}; + +struct TextureCacheParams { + static constexpr bool ENABLE_VALIDATION = true; + static constexpr bool FRAMEBUFFER_BLITS = false; + static constexpr bool HAS_EMULATED_COPIES = false; + static constexpr bool HAS_DEVICE_MEMORY_INFO = true; + static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true; + + using Runtime = Metal::TextureCacheRuntime; + using Image = Metal::Image; + using ImageAlloc = Metal::ImageAlloc; + using ImageView = Metal::ImageView; + using Sampler = Metal::Sampler; + using Framebuffer = Metal::Framebuffer; + using AsyncBuffer = Metal::StagingBufferRef; + using BufferType = MTLBuffer_t; +}; + +using TextureCache = VideoCommon::TextureCache; + +} // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_texture_cache.mm b/src/video_core/renderer_metal/mtl_texture_cache.mm new file mode 100644 index 0000000000..ac9de037d1 --- /dev/null +++ b/src/video_core/renderer_metal/mtl_texture_cache.mm @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include +#include + +#include "common/bit_cast.h" +#include "common/bit_util.h" +#include "common/settings.h" + +#include "video_core/renderer_metal/mtl_device.h" +#include "video_core/renderer_metal/mtl_texture_cache.h" + +#include "video_core/engines/fermi_2d.h" +#include "video_core/texture_cache/formatter.h" +#include "video_core/texture_cache/samples_helper.h" +#include "video_core/texture_cache/util.h" + +namespace Metal { + +using Tegra::Engines::Fermi2D; +using Tegra::Texture::SwizzleSource; +using Tegra::Texture::TextureMipmapFilter; +using VideoCommon::BufferImageCopy; +using VideoCommon::ImageFlagBits; +using VideoCommon::ImageInfo; +using VideoCommon::ImageType; +using VideoCommon::SubresourceRange; +using VideoCore::Surface::BytesPerBlock; +using VideoCore::Surface::IsPixelFormatASTC; +using VideoCore::Surface::IsPixelFormatInteger; +using VideoCore::Surface::SurfaceType; + +TextureCacheRuntime::TextureCacheRuntime(const Device &device_) + : device{device_}, resolution{Settings::values.resolution_info} {} + +void TextureCacheRuntime::TickFrame() {} + +Image::Image(TextureCacheRuntime &runtime, const ImageInfo &info, + GPUVAddr gpu_addr_, VAddr cpu_addr_) + : VideoCommon::ImageBase(info, gpu_addr_, cpu_addr_) { + MTLTextureDescriptor *texture_descriptor = + [[MTLTextureDescriptor alloc] init]; + // TODO: don't hardcode the format + texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; + texture_descriptor.width = info.size.width; + texture_descriptor.height = info.size.height; + + texture = + [runtime.device.GetDevice() newTextureWithDescriptor:texture_descriptor]; +} + +Image::~Image() { [texture release]; } + +ImageView::ImageView(TextureCacheRuntime &runtime, + const VideoCommon::ImageViewInfo &info, ImageId image_id_, + Image &image) + : VideoCommon::ImageViewBase{info, image.info, image_id_, image.gpu_addr} { + using Shader::TextureType; + + texture = [image.GetHandle() retain]; + + // TODO: create texture view +} + +ImageView::ImageView(TextureCacheRuntime &runtime, + const VideoCommon::ImageViewInfo &info, ImageId image_id_, + Image &image, const SlotVector& slot_imgs) + : ImageView(runtime, info, image_id_, image) { + // TODO: save slot images +} + +ImageView::~ImageView() { [texture release]; } + +Sampler::Sampler(TextureCacheRuntime &runtime, + const Tegra::Texture::TSCEntry &tsc) { + MTLSamplerDescriptor *sampler_descriptor = + [[MTLSamplerDescriptor alloc] init]; + + // TODO: configure the descriptor + + sampler_state = [runtime.device.GetDevice() + newSamplerStateWithDescriptor:sampler_descriptor]; +} + +Framebuffer::Framebuffer(TextureCacheRuntime &runtime, + std::span color_buffers, + ImageView *depth_buffer, + const VideoCommon::RenderTargets &key) { + CreateRenderPassDescriptor(runtime, color_buffers, depth_buffer, + key.is_rescaled, key.size.width, key.size.height); +} + +Framebuffer::~Framebuffer() = default; + +void Framebuffer::CreateRenderPassDescriptor( + TextureCacheRuntime &runtime, std::span color_buffers, + ImageView *depth_buffer, bool is_rescaled, size_t width, size_t height) { + render_pass = [MTLRenderPassDescriptor renderPassDescriptor]; + + for (size_t index = 0; index < NUM_RT; ++index) { + const ImageView *const color_buffer = color_buffers[index]; + if (!color_buffer) { + continue; + } + // TODO: don't use index as attachment index + render_pass.colorAttachments[index].clearColor = + MTLClearColorMake(0.5, 1.0, 0.0, 1.0); + render_pass.colorAttachments[index].loadAction = MTLLoadActionClear; + render_pass.colorAttachments[index].storeAction = MTLStoreActionStore; + render_pass.colorAttachments[index].texture = color_buffer->GetHandle(); + } + if (depth_buffer) { + render_pass.depthAttachment.clearDepth = 1.0; + render_pass.depthAttachment.loadAction = MTLLoadActionClear; + render_pass.depthAttachment.storeAction = MTLStoreActionStore; + render_pass.depthAttachment.texture = depth_buffer->GetHandle(); + } +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_metal/mtl_texture_cache_base.cpp b/src/video_core/renderer_metal/mtl_texture_cache_base.cpp new file mode 100644 index 0000000000..714484b747 --- /dev/null +++ b/src/video_core/renderer_metal/mtl_texture_cache_base.cpp @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/renderer_metal/mtl_texture_cache.h" +#include "video_core/texture_cache/texture_cache.h" + +namespace VideoCommon { +template class VideoCommon::TextureCache; +} diff --git a/src/video_core/renderer_metal/objc_bridge.h b/src/video_core/renderer_metal/objc_bridge.h index 21bc3e74c7..37c120626d 100644 --- a/src/video_core/renderer_metal/objc_bridge.h +++ b/src/video_core/renderer_metal/objc_bridge.h @@ -9,14 +9,18 @@ typedef id MTLDevice_t; typedef id MTLCommandQueue_t; typedef id MTLCommandBuffer_t; typedef id MTLCommandEncoder_t; +typedef id MTLBuffer_t; typedef id MTLTexture_t; +typedef id MTLSamplerState_t; typedef id CAMetalDrawable_t; #else typedef void* MTLDevice_t; typedef void* MTLCommandQueue_t; typedef void* MTLCommandBuffer_t; typedef void* MTLCommandEncoder_t; +typedef void* MTLBuffer_t; typedef void* MTLTexture_t; +typedef void* MTLSamplerState_t; typedef void MTLRenderPassDescriptor; typedef void CAMetalLayer; typedef void* CAMetalDrawable_t; diff --git a/src/video_core/renderer_metal/renderer_metal.mm b/src/video_core/renderer_metal/renderer_metal.mm index 1c556d5433..add0f11eaf 100644 --- a/src/video_core/renderer_metal/renderer_metal.mm +++ b/src/video_core/renderer_metal/renderer_metal.mm @@ -16,7 +16,7 @@ RendererMetal::RendererMetal(Core::Frontend::EmuWindow& emu_window, command_recorder(device), swap_chain(device, command_recorder, static_cast(render_window.GetWindowInfo().render_surface)), - rasterizer(gpu_, device, swap_chain) {} + rasterizer(gpu_, device, command_recorder, swap_chain) {} RendererMetal::~RendererMetal() = default; @@ -25,9 +25,10 @@ void RendererMetal::Composite(std::span framebuf return; } - // HACK + // Ask the swap chain to get next drawable swap_chain.AcquireNextDrawable(); + // TODO: copy the framebuffer to the drawable texture instead of this dummy render pass 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; @@ -35,6 +36,7 @@ void RendererMetal::Composite(std::span framebuf render_pass_descriptor.colorAttachments[0].texture = swap_chain.GetDrawableTexture(); command_recorder.BeginRenderPass(render_pass_descriptor); + swap_chain.Present(); command_recorder.Submit(); From 82b3fcca18ab20764036de8b8d2eaf84416ddb55 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sat, 6 Apr 2024 09:55:26 +0200 Subject: [PATCH 6/6] metal: create texture cache in rasterizer --- .../renderer_metal/mtl_rasterizer.h | 14 +- .../renderer_metal/mtl_rasterizer.mm | 13 +- .../renderer_metal/mtl_staging_buffer_pool.mm | 4 +- .../renderer_metal/mtl_texture_cache.h | 79 +++++--- .../renderer_metal/mtl_texture_cache.mm | 171 ++++++++++++------ .../renderer_metal/renderer_metal.mm | 2 +- 6 files changed, 198 insertions(+), 85 deletions(-) diff --git a/src/video_core/renderer_metal/mtl_rasterizer.h b/src/video_core/renderer_metal/mtl_rasterizer.h index 95263566af..9ea17af354 100644 --- a/src/video_core/renderer_metal/mtl_rasterizer.h +++ b/src/video_core/renderer_metal/mtl_rasterizer.h @@ -3,10 +3,11 @@ #pragma once #include "common/common_types.h" +#include "mtl_texture_cache.h" #include "video_core/control/channel_state_cache.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_interface.h" -#include "video_core/renderer_metal/objc_bridge.h" +#include "video_core/renderer_metal/mtl_texture_cache.h" namespace Core { class System; @@ -17,6 +18,7 @@ namespace Metal { class Device; class CommandRecorder; class SwapChain; +class TextureCacheRuntime; class RasterizerMetal; @@ -38,8 +40,9 @@ public: class RasterizerMetal final : public VideoCore::RasterizerInterface, protected VideoCommon::ChannelSetupCaches { public: - explicit RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, - CommandRecorder& command_recorder_, const SwapChain& swap_chain_); + explicit RasterizerMetal(Tegra::GPU& gpu_, Tegra::MaxwellDeviceMemoryManager& device_memory_, + const Device& device_, CommandRecorder& command_recorder_, + const SwapChain& swap_chain_); ~RasterizerMetal() override; void Draw(bool is_indexed, u32 instance_count) override; @@ -91,10 +94,15 @@ public: private: Tegra::GPU& gpu; AccelerateDMA accelerate_dma; + Tegra::MaxwellDeviceMemoryManager& device_memory; const Device& device; CommandRecorder& command_recorder; const SwapChain& swap_chain; + + StagingBufferPool staging_buffer_pool; + TextureCacheRuntime texture_cache_runtime; + TextureCache texture_cache; }; } // namespace Metal diff --git a/src/video_core/renderer_metal/mtl_rasterizer.mm b/src/video_core/renderer_metal/mtl_rasterizer.mm index 3554670cbd..ce93692547 100644 --- a/src/video_core/renderer_metal/mtl_rasterizer.mm +++ b/src/video_core/renderer_metal/mtl_rasterizer.mm @@ -11,6 +11,7 @@ #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/texture_cache/texture_cache_base.h" #include @@ -25,9 +26,15 @@ bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) { return true; } -RasterizerMetal::RasterizerMetal(Tegra::GPU& gpu_, const Device& device_, - CommandRecorder& command_recorder_, const SwapChain& swap_chain_) - : gpu{gpu_}, device{device_}, command_recorder{command_recorder_}, swap_chain{swap_chain_} {} +RasterizerMetal::RasterizerMetal(Tegra::GPU& gpu_, + Tegra::MaxwellDeviceMemoryManager& device_memory_, + const Device& device_, CommandRecorder& command_recorder_, + const SwapChain& swap_chain_) + : gpu{gpu_}, device_memory{device_memory_}, device{device_}, + command_recorder{command_recorder_}, swap_chain{swap_chain_}, + staging_buffer_pool(device, command_recorder), + texture_cache_runtime(device, command_recorder, staging_buffer_pool), + texture_cache(texture_cache_runtime, device_memory) {} RasterizerMetal::~RasterizerMetal() = default; void RasterizerMetal::Draw(bool is_indexed, u32 instance_count) { diff --git a/src/video_core/renderer_metal/mtl_staging_buffer_pool.mm b/src/video_core/renderer_metal/mtl_staging_buffer_pool.mm index b69720a44e..7f420c64a5 100644 --- a/src/video_core/renderer_metal/mtl_staging_buffer_pool.mm +++ b/src/video_core/renderer_metal/mtl_staging_buffer_pool.mm @@ -42,7 +42,7 @@ constexpr size_t REGION_SIZE = STREAM_BUFFER_SIZE / StagingBufferPool::NUM_SYNCS StagingBufferPool::StagingBufferPool(const Device& device_, CommandRecorder& command_recorder_) : device{device_}, command_recorder{command_recorder_} { stream_buffer = [device.GetDevice() newBufferWithLength:STREAM_BUFFER_SIZE - options:MTLResourceStorageModePrivate]; + options:MTLResourceStorageModeShared]; } StagingBufferPool::~StagingBufferPool() = default; @@ -83,7 +83,7 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage bool deferred) { const u32 log2 = Common::Log2Ceil64(size); MTLBuffer_t buffer = [device.GetDevice() newBufferWithLength:size - options:MTLResourceStorageModePrivate]; + options:MTLResourceStorageModeShared]; // TODO: check if the mapped span is correct std::span mapped_span(static_cast([buffer contents]), size); auto& entry = GetCache(usage)[log2].entries.emplace_back(buffer, mapped_span); diff --git a/src/video_core/renderer_metal/mtl_texture_cache.h b/src/video_core/renderer_metal/mtl_texture_cache.h index a866bf731e..328a074263 100644 --- a/src/video_core/renderer_metal/mtl_texture_cache.h +++ b/src/video_core/renderer_metal/mtl_texture_cache.h @@ -4,6 +4,7 @@ #include +#include "mtl_staging_buffer_pool.h" #include "video_core/texture_cache/texture_cache_base.h" #include "shader_recompiler/shader_info.h" @@ -24,6 +25,7 @@ using VideoCommon::Region2D; using VideoCommon::RenderTargets; using VideoCore::Surface::PixelFormat; +class CommandRecorder; class Device; class Image; class ImageView; @@ -31,9 +33,13 @@ class Framebuffer; class TextureCacheRuntime { public: - explicit TextureCacheRuntime(const Device& device_); + explicit TextureCacheRuntime(const Device& device_, CommandRecorder& command_recorder_, + StagingBufferPool& staging_buffer_pool_); - void Finish(); + // TODO: implement + void Finish() {} + + void TickFrame(); StagingBufferRef UploadStagingBuffer(size_t size); @@ -45,35 +51,49 @@ public: return true; } - void TickFrame(); + u64 GetDeviceLocalMemory() const { + return 0; + } - u64 GetDeviceLocalMemory() const; + u64 GetDeviceMemoryUsage() const { + return 0; + } - u64 GetDeviceMemoryUsage() const; - - bool CanReportMemoryUsage() const; + bool CanReportMemoryUsage() const { + return false; + } + // TODO: implement void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src, const Region2D& dst_region, const Region2D& src_region, Tegra::Engines::Fermi2D::Filter filter, - Tegra::Engines::Fermi2D::Operation operation); + Tegra::Engines::Fermi2D::Operation operation) {} - void CopyImage(Image& dst, Image& src, std::span copies); + // TODO: implement + void CopyImage(Image& dst, Image& src, std::span copies) {} - void CopyImageMSAA(Image& dst, Image& src, std::span copies); + // TODO: implement + void CopyImageMSAA(Image& dst, Image& src, std::span copies) {} - bool ShouldReinterpret(Image& dst, Image& src); + bool ShouldReinterpret(Image& dst, Image& src) { + // HACK + return false; + } - void ReinterpretImage(Image& dst, Image& src, std::span copies); + // TODO: implement + void ReinterpretImage(Image& dst, Image& src, std::span copies) {} - void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view); + // TODO: implement + void ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {} - void InsertUploadMemoryBarrier(); + // TODO: implement + void InsertUploadMemoryBarrier() {} void TransitionImageLayout(Image& image) {} + // TODO: implement void AccelerateImageUpload(Image&, const StagingBufferRef&, - std::span); + std::span) {} bool HasNativeBgr() const noexcept { return true; @@ -83,9 +103,12 @@ public: return false; } - void BarrierFeedbackLoop(); + // TODO: implement + void BarrierFeedbackLoop() {} const Device& device; + CommandRecorder& command_recorder; + StagingBufferPool& staging_buffer_pool; const Settings::ResolutionScalingInfo& resolution; }; @@ -112,25 +135,39 @@ public: void DownloadMemory(MTLBuffer_t buffer, size_t offset, std::span copies); + // For some reason, this function cannot be defined in the .mm file since it would report + // undefined symbols void DownloadMemory(std::span buffers, std::span offsets, - std::span copies); + std::span copies) { + // TODO: implement + } void DownloadMemory(const StagingBufferRef& map, std::span copies); - bool IsRescaled() const; + bool IsRescaled() const { + return rescaled; + } - bool ScaleUp(bool ignore = false); + bool ScaleUp(bool ignore = false) { + // HACK + return true; + } - bool ScaleDown(bool ignore = false); + bool ScaleDown(bool ignore = false) { + // HACK + return true; + } MTLTexture_t GetHandle() const noexcept { return texture; } private: - MTLTexture_t texture; + MTLTexture_t texture = nil; bool initialized = false; + + bool rescaled = false; }; class ImageView : public VideoCommon::ImageViewBase { diff --git a/src/video_core/renderer_metal/mtl_texture_cache.mm b/src/video_core/renderer_metal/mtl_texture_cache.mm index ac9de037d1..6bc65929f3 100644 --- a/src/video_core/renderer_metal/mtl_texture_cache.mm +++ b/src/video_core/renderer_metal/mtl_texture_cache.mm @@ -33,91 +33,152 @@ using VideoCore::Surface::IsPixelFormatASTC; using VideoCore::Surface::IsPixelFormatInteger; using VideoCore::Surface::SurfaceType; -TextureCacheRuntime::TextureCacheRuntime(const Device &device_) - : device{device_}, resolution{Settings::values.resolution_info} {} +TextureCacheRuntime::TextureCacheRuntime(const Device& device_, CommandRecorder& command_recorder_, + StagingBufferPool& staging_buffer_pool_) + : device{device_}, command_recorder{command_recorder_}, + staging_buffer_pool{staging_buffer_pool_}, + resolution{Settings::values.resolution_info} {} void TextureCacheRuntime::TickFrame() {} -Image::Image(TextureCacheRuntime &runtime, const ImageInfo &info, +StagingBufferRef TextureCacheRuntime::UploadStagingBuffer(size_t size) { + return staging_buffer_pool.Request(size, MemoryUsage::Upload); +} + +StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size, bool deferred) { + return staging_buffer_pool.Request(size, MemoryUsage::Download, deferred); +} + +void TextureCacheRuntime::FreeDeferredStagingBuffer(StagingBufferRef& ref) { + staging_buffer_pool.FreeDeferred(ref); +} + +Image::Image(TextureCacheRuntime& runtime, const ImageInfo& info, GPUVAddr gpu_addr_, VAddr cpu_addr_) : VideoCommon::ImageBase(info, gpu_addr_, cpu_addr_) { - MTLTextureDescriptor *texture_descriptor = - [[MTLTextureDescriptor alloc] init]; - // TODO: don't hardcode the format - texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; - texture_descriptor.width = info.size.width; - texture_descriptor.height = info.size.height; + MTLTextureDescriptor *texture_descriptor = + [[MTLTextureDescriptor alloc] init]; + // TODO: don't hardcode the format + texture_descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; + texture_descriptor.width = info.size.width; + texture_descriptor.height = info.size.height; - texture = - [runtime.device.GetDevice() newTextureWithDescriptor:texture_descriptor]; + texture = + [runtime.device.GetDevice() newTextureWithDescriptor:texture_descriptor]; } -Image::~Image() { [texture release]; } +Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {} -ImageView::ImageView(TextureCacheRuntime &runtime, - const VideoCommon::ImageViewInfo &info, ImageId image_id_, - Image &image) +Image::~Image() { + if (texture) { + [texture release]; + } +} + +// TODO: implement these +void Image::UploadMemory(MTLBuffer_t buffer, size_t offset, + std::span copies) { + ; +} + +void Image::UploadMemory(const StagingBufferRef& map, + std::span copies) { + ; +} + +void Image::DownloadMemory(MTLBuffer_t buffer, size_t offset, + std::span copies) { + ; +} + +// TODO: uncomment +/* +void Image::DownloadMemory(std::span buffers, std::span offsets, + std::span copies) { + ; +} +*/ + +void Image::DownloadMemory(const StagingBufferRef& map, + std::span copies) { + ; +} + +ImageView::ImageView(TextureCacheRuntime& runtime, + const VideoCommon::ImageViewInfo& info, ImageId image_id_, + Image& image) : VideoCommon::ImageViewBase{info, image.info, image_id_, image.gpu_addr} { - using Shader::TextureType; + using Shader::TextureType; - texture = [image.GetHandle() retain]; + texture = [image.GetHandle() retain]; - // TODO: create texture view + // TODO: create texture view } -ImageView::ImageView(TextureCacheRuntime &runtime, - const VideoCommon::ImageViewInfo &info, ImageId image_id_, - Image &image, const SlotVector& slot_imgs) +ImageView::ImageView(TextureCacheRuntime& runtime, + const VideoCommon::ImageViewInfo& info, ImageId image_id_, + Image& image, const SlotVector& slot_imgs) : ImageView(runtime, info, image_id_, image) { - // TODO: save slot images + // TODO: save slot images +} + +ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, + const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_) + : VideoCommon::ImageViewBase{info, view_info, gpu_addr_} { + // TODO: implement +} + +ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params) + : VideoCommon::ImageViewBase{params} { + // TODO: implement } ImageView::~ImageView() { [texture release]; } -Sampler::Sampler(TextureCacheRuntime &runtime, - const Tegra::Texture::TSCEntry &tsc) { - MTLSamplerDescriptor *sampler_descriptor = - [[MTLSamplerDescriptor alloc] init]; +Sampler::Sampler(TextureCacheRuntime& runtime, + const Tegra::Texture::TSCEntry& tsc) { + MTLSamplerDescriptor* sampler_descriptor = + [[MTLSamplerDescriptor alloc] init]; - // TODO: configure the descriptor + // TODO: configure the descriptor - sampler_state = [runtime.device.GetDevice() - newSamplerStateWithDescriptor:sampler_descriptor]; + sampler_state = [runtime.device.GetDevice() + newSamplerStateWithDescriptor:sampler_descriptor]; } -Framebuffer::Framebuffer(TextureCacheRuntime &runtime, - std::span color_buffers, - ImageView *depth_buffer, - const VideoCommon::RenderTargets &key) { - CreateRenderPassDescriptor(runtime, color_buffers, depth_buffer, - key.is_rescaled, key.size.width, key.size.height); +Framebuffer::Framebuffer(TextureCacheRuntime& runtime, + std::span color_buffers, + ImageView* depth_buffer, + const VideoCommon::RenderTargets& key) { + CreateRenderPassDescriptor(runtime, color_buffers, depth_buffer, + key.is_rescaled, key.size.width, key.size.height); } Framebuffer::~Framebuffer() = default; void Framebuffer::CreateRenderPassDescriptor( - TextureCacheRuntime &runtime, std::span color_buffers, - ImageView *depth_buffer, bool is_rescaled, size_t width, size_t height) { - render_pass = [MTLRenderPassDescriptor renderPassDescriptor]; + TextureCacheRuntime& runtime, std::span color_buffers, + ImageView* depth_buffer, bool is_rescaled, size_t width, size_t height) { + render_pass = [MTLRenderPassDescriptor renderPassDescriptor]; - for (size_t index = 0; index < NUM_RT; ++index) { - const ImageView *const color_buffer = color_buffers[index]; - if (!color_buffer) { - continue; + for (size_t index = 0; index < NUM_RT; ++index) { + const ImageView* const color_buffer = color_buffers[index]; + if (!color_buffer) { + continue; + } + // TODO: don't use index as attachment index + render_pass.colorAttachments[index].clearColor = + MTLClearColorMake(0.5, 1.0, 0.0, 1.0); + render_pass.colorAttachments[index].loadAction = MTLLoadActionClear; + render_pass.colorAttachments[index].storeAction = MTLStoreActionStore; + render_pass.colorAttachments[index].texture = color_buffer->GetHandle(); + } + if (depth_buffer) { + render_pass.depthAttachment.clearDepth = 1.0; + render_pass.depthAttachment.loadAction = MTLLoadActionClear; + render_pass.depthAttachment.storeAction = MTLStoreActionStore; + render_pass.depthAttachment.texture = depth_buffer->GetHandle(); } - // TODO: don't use index as attachment index - render_pass.colorAttachments[index].clearColor = - MTLClearColorMake(0.5, 1.0, 0.0, 1.0); - render_pass.colorAttachments[index].loadAction = MTLLoadActionClear; - render_pass.colorAttachments[index].storeAction = MTLStoreActionStore; - render_pass.colorAttachments[index].texture = color_buffer->GetHandle(); - } - if (depth_buffer) { - render_pass.depthAttachment.clearDepth = 1.0; - render_pass.depthAttachment.loadAction = MTLLoadActionClear; - render_pass.depthAttachment.storeAction = MTLStoreActionStore; - render_pass.depthAttachment.texture = depth_buffer->GetHandle(); - } } } // namespace Vulkan diff --git a/src/video_core/renderer_metal/renderer_metal.mm b/src/video_core/renderer_metal/renderer_metal.mm index add0f11eaf..8911f0e1b2 100644 --- a/src/video_core/renderer_metal/renderer_metal.mm +++ b/src/video_core/renderer_metal/renderer_metal.mm @@ -16,7 +16,7 @@ RendererMetal::RendererMetal(Core::Frontend::EmuWindow& emu_window, command_recorder(device), swap_chain(device, command_recorder, static_cast(render_window.GetWindowInfo().render_surface)), - rasterizer(gpu_, device, command_recorder, swap_chain) {} + rasterizer(gpu_, device_memory, device, command_recorder, swap_chain) {} RendererMetal::~RendererMetal() = default;