mirror of
https://git.suyu.dev/suyu/suyu
synced 2024-06-02 19:28:27 -04:00
149 lines
5.9 KiB
C++
149 lines
5.9 KiB
C++
// SPDX-FileCopyrightText: Copyright 2024 suyu Emulator Project
|
|
// 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/mtl_device.h"
|
|
#include "video_core/renderer_metal/renderer_metal.h"
|
|
|
|
// TODO: Make it the way that we can make a swiftUI for the macOS app instead of qt
|
|
|
|
namespace Metal {
|
|
|
|
RendererMetal::RendererMetal(Core::Frontend::EmuWindow& emu_window,
|
|
Tegra::MaxwellDeviceMemoryManager& device_memory_, Tegra::GPU& gpu_,
|
|
std::unique_ptr<Core::Frontend::GraphicsContext> context_)
|
|
: RendererBase(emu_window, std::move(context_)), device_memory{device_memory_}, gpu{gpu_},
|
|
device{}, command_recorder(device),
|
|
swap_chain(device, command_recorder,
|
|
static_cast<CA::MetalLayer*>(render_window.GetWindowInfo().render_surface)),
|
|
rasterizer(gpu_, device_memory, device, command_recorder, swap_chain) {
|
|
CreateBlitPipelineState();
|
|
}
|
|
|
|
RendererMetal::~RendererMetal() {
|
|
blit_pipeline_state->release();
|
|
blit_sampler_state->release();
|
|
}
|
|
|
|
void RendererMetal::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) {
|
|
if (framebuffers.empty()) {
|
|
return;
|
|
}
|
|
|
|
// 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
|
|
MTL::RenderPassDescriptor* render_pass_descriptor = MTL::RenderPassDescriptor::alloc()->init();
|
|
render_pass_descriptor->colorAttachments()->object(0)->setLoadAction(MTL::LoadActionDontCare);
|
|
render_pass_descriptor->colorAttachments()->object(0)->setStoreAction(MTL::StoreActionStore);
|
|
render_pass_descriptor->colorAttachments()->object(0)->setTexture(
|
|
swap_chain.GetDrawableTexture());
|
|
|
|
command_recorder.BeginOrContinueRenderPass(render_pass_descriptor);
|
|
|
|
// Blit the framebuffer to the drawable texture
|
|
// TODO: acquire the texture from @ref framebuffers
|
|
const Framebuffer* const framebuffer = rasterizer.texture_cache.GetFramebuffer();
|
|
if (!framebuffer) {
|
|
return;
|
|
}
|
|
MTL::Texture* src_texture = framebuffer->GetHandle()->colorAttachments()->object(0)->texture();
|
|
command_recorder.SetRenderPipelineState(blit_pipeline_state);
|
|
command_recorder.SetFragmentTexture(src_texture, 0);
|
|
command_recorder.SetFragmentSamplerState(blit_sampler_state, 0);
|
|
|
|
// Draw a full screen triangle which will get clipped to a rectangle
|
|
command_recorder.GetRenderCommandEncoder()->drawPrimitives(MTL::PrimitiveTypeTriangle,
|
|
NS::UInteger(0), NS::UInteger(3));
|
|
|
|
swap_chain.Present();
|
|
command_recorder.Submit();
|
|
|
|
gpu.RendererFrameEndNotify();
|
|
rasterizer.TickFrame();
|
|
|
|
render_window.OnFrameDisplayed();
|
|
}
|
|
|
|
std::vector<u8> RendererMetal::GetAppletCaptureBuffer() {
|
|
return std::vector<u8>(VideoCore::Capture::TiledSize);
|
|
}
|
|
|
|
void RendererMetal::CreateBlitPipelineState() {
|
|
MTL::CompileOptions* compile_options = MTL::CompileOptions::alloc()->init();
|
|
NS::Error* error = nullptr;
|
|
MTL::Library* library = device.GetDevice()->newLibrary(NS::String::string(
|
|
R"(
|
|
#include <metal_stdlib>
|
|
using namespace metal;
|
|
|
|
constant float2 positions[] = {
|
|
float2(-1.0, -3.0),
|
|
float2(-1.0, 1.0),
|
|
float2( 3.0, 1.0),
|
|
};
|
|
|
|
struct VertexOut {
|
|
float4 position [[position]];
|
|
float2 texCoord;
|
|
};
|
|
|
|
vertex VertexOut vertexMain(uint vid [[vertex_id]]) {
|
|
VertexOut out;
|
|
out.position = float4(positions[vid], 0.0, 1.0);
|
|
out.texCoord = positions[vid] * 0.5 + 0.5;
|
|
out.texCoord.y = 1.0 - out.texCoord.y;
|
|
|
|
return out;
|
|
}
|
|
|
|
fragment float4 fragmentMain(VertexOut in [[stage_in]],
|
|
texture2d<float> texture [[texture(0)]],
|
|
sampler sampler [[sampler(0)]]) {
|
|
return texture.sample(sampler, in.texCoord);
|
|
}
|
|
)",
|
|
NS::ASCIIStringEncoding),
|
|
compile_options, &error);
|
|
if (error) {
|
|
LOG_ERROR(Render_Metal, "failed to create blit library: {}",
|
|
error->description()->cString(NS::ASCIIStringEncoding));
|
|
}
|
|
|
|
MTL::Function* vertex_function =
|
|
library->newFunction(NS::String::string("vertexMain", NS::ASCIIStringEncoding));
|
|
MTL::Function* fragment_function =
|
|
library->newFunction(NS::String::string("fragmentMain", NS::ASCIIStringEncoding));
|
|
|
|
MTL::RenderPipelineDescriptor* pipeline_descriptor =
|
|
MTL::RenderPipelineDescriptor::alloc()->init();
|
|
pipeline_descriptor->setVertexFunction(vertex_function);
|
|
pipeline_descriptor->setFragmentFunction(fragment_function);
|
|
// TODO: get the pixel format from metal layer
|
|
pipeline_descriptor->colorAttachments()->object(0)->setPixelFormat(MTL::PixelFormatBGRA8Unorm);
|
|
|
|
error = nullptr;
|
|
blit_pipeline_state = device.GetDevice()->newRenderPipelineState(pipeline_descriptor, &error);
|
|
if (error) {
|
|
LOG_ERROR(Render_Metal, "failed to create blit pipeline state: {}",
|
|
error->description()->cString(NS::ASCIIStringEncoding));
|
|
}
|
|
|
|
// Create sampler state
|
|
MTL::SamplerDescriptor* sampler_descriptor = MTL::SamplerDescriptor::alloc()->init();
|
|
sampler_descriptor->setMinFilter(MTL::SamplerMinMagFilterLinear);
|
|
sampler_descriptor->setMagFilter(MTL::SamplerMinMagFilterLinear);
|
|
|
|
blit_sampler_state = device.GetDevice()->newSamplerState(sampler_descriptor);
|
|
|
|
// Deallocate unnecessary objects
|
|
fragment_function->release();
|
|
vertex_function->release();
|
|
library->release();
|
|
}
|
|
|
|
} // namespace Metal
|