diff --git a/src/video_core/renderer_metal/mtl_command_recorder.h b/src/video_core/renderer_metal/mtl_command_recorder.h index c0337c776e..2cb503355e 100644 --- a/src/video_core/renderer_metal/mtl_command_recorder.h +++ b/src/video_core/renderer_metal/mtl_command_recorder.h @@ -40,8 +40,22 @@ public: return command_buffer; } - MTL::CommandEncoder* GetCommandEncoder() { - return encoder; + MTL::RenderCommandEncoder* GetRenderCommandEncoder() { + CheckIfRenderPassIsActive(); + + return static_cast(encoder); + } + + MTL::ComputeCommandEncoder* GetComputeCommandEncoder() { + RequireComputeEncoder(); + + return static_cast(encoder); + } + + MTL::BlitCommandEncoder* GetBlitCommandEncoder() { + RequireBlitEncoder(); + + return static_cast(encoder); } private: diff --git a/src/video_core/renderer_metal/mtl_graphics_pipeline.cpp b/src/video_core/renderer_metal/mtl_graphics_pipeline.cpp index 735d455bff..d0e9bebfce 100644 --- a/src/video_core/renderer_metal/mtl_graphics_pipeline.cpp +++ b/src/video_core/renderer_metal/mtl_graphics_pipeline.cpp @@ -89,6 +89,13 @@ void GraphicsPipeline::MakePipeline(MTL::RenderPassDescriptor* render_pass) { color_attachment->setPixelFormat(render_pass_attachment->texture()->pixelFormat()); // TODO: provide blend information } + + NS::Error* error = nullptr; + pipeline_state = device.GetDevice()->newRenderPipelineState(pipeline_descriptor, &error); + if (error) { + LOG_ERROR(Render_Metal, "failed to create pipeline state: {}", + error->description()->cString(NS::ASCIIStringEncoding)); + } } void GraphicsPipeline::Validate() { diff --git a/src/video_core/renderer_metal/mtl_rasterizer.h b/src/video_core/renderer_metal/mtl_rasterizer.h index f5d5f04dc0..29aabe3f56 100644 --- a/src/video_core/renderer_metal/mtl_rasterizer.h +++ b/src/video_core/renderer_metal/mtl_rasterizer.h @@ -106,6 +106,9 @@ private: BufferCacheRuntime buffer_cache_runtime; BufferCache buffer_cache; TextureCacheRuntime texture_cache_runtime; + + // HACK: make the texture cache public so that renderer can access it +public: TextureCache texture_cache; }; diff --git a/src/video_core/renderer_metal/renderer_metal.cpp b/src/video_core/renderer_metal/renderer_metal.cpp index 014bf4611e..3770119c23 100644 --- a/src/video_core/renderer_metal/renderer_metal.cpp +++ b/src/video_core/renderer_metal/renderer_metal.cpp @@ -14,13 +14,18 @@ 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{}, command_recorder(device), + : RendererBase(emu_window, std::move(context_)), device_memory{device_memory_}, gpu{gpu_}, + device{}, command_recorder(device), swap_chain(device, command_recorder, static_cast(render_window.GetWindowInfo().render_surface)), - rasterizer(gpu_, device_memory, device, command_recorder, swap_chain) {} + rasterizer(gpu_, device_memory, device, command_recorder, swap_chain) { + CreateBlitPipelineState(); +} -RendererMetal::~RendererMetal() = default; +RendererMetal::~RendererMetal() { + blit_pipeline_state->release(); + blit_sampler_state->release(); +} void RendererMetal::Composite(std::span framebuffers) { if (framebuffers.empty()) { @@ -32,15 +37,28 @@ void RendererMetal::Composite(std::span framebuf // 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)->setClearColor( - MTL::ClearColor::Make(1.0, 0.5, 0.0, 1.0)); - render_pass_descriptor->colorAttachments()->object(0)->setLoadAction(MTL::LoadActionClear); + 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.BeginRenderPass(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.GetRenderCommandEncoder()->setRenderPipelineState(blit_pipeline_state); + command_recorder.GetRenderCommandEncoder()->setFragmentTexture(src_texture, 0); + command_recorder.GetRenderCommandEncoder()->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(); @@ -54,4 +72,76 @@ std::vector RendererMetal::GetAppletCaptureBuffer() { return std::vector(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 + using namespace metal; + + constant float2 texCoords[] = { + float2(0.0, -1.0), + float2(0.0, 1.0), + float2(2.0, 1.0), + }; + + struct VertexOut { + float4 position [[position]]; + float2 texCoord; + }; + + vertex VertexOut vertexMain(uint vid [[vertex_id]]) { + VertexOut out; + out.position = float4(texCoords[vid] * 2.0 - 1.0, 0.0, 1.0); + out.texCoord = texCoords[vid]; + + return out; + } + + fragment float4 fragmentMain(VertexOut in [[stage_in]], + texture2d 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::PixelFormatRGBA8Unorm); + + 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 diff --git a/src/video_core/renderer_metal/renderer_metal.h b/src/video_core/renderer_metal/renderer_metal.h index fd811c66db..23013cad28 100644 --- a/src/video_core/renderer_metal/renderer_metal.h +++ b/src/video_core/renderer_metal/renderer_metal.h @@ -47,6 +47,8 @@ public: } private: + void CreateBlitPipelineState(); + Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::GPU& gpu; @@ -54,6 +56,9 @@ private: CommandRecorder command_recorder; SwapChain swap_chain; + MTL::RenderPipelineState* blit_pipeline_state{nullptr}; + MTL::SamplerState* blit_sampler_state{nullptr}; + RasterizerMetal rasterizer; };