From 98c4ff461fb8888dd6bea2deabaec79992e1f856 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Wed, 10 Apr 2024 17:20:24 +0200 Subject: [PATCH] metal: don't bind resources that are already bound --- .../renderer_metal/mtl_command_recorder.cpp | 8 +- .../renderer_metal/mtl_command_recorder.h | 96 ++++++++++++++++++- .../renderer_metal/mtl_graphics_pipeline.cpp | 6 +- .../renderer_metal/renderer_metal.cpp | 6 +- 4 files changed, 105 insertions(+), 11 deletions(-) diff --git a/src/video_core/renderer_metal/mtl_command_recorder.cpp b/src/video_core/renderer_metal/mtl_command_recorder.cpp index 0b0751891f..81cca08361 100644 --- a/src/video_core/renderer_metal/mtl_command_recorder.cpp +++ b/src/video_core/renderer_metal/mtl_command_recorder.cpp @@ -11,12 +11,12 @@ CommandRecorder::CommandRecorder(const Device& device_) : device(device_) {} CommandRecorder::~CommandRecorder() = default; void CommandRecorder::BeginOrContinueRenderPass(MTL::RenderPassDescriptor* render_pass) { - if (render_pass != bound_render_pass) { + if (render_pass != render_state.render_pass) { RequireCommandBuffer(); EndEncoding(); encoder = command_buffer->renderCommandEncoder(render_pass); encoder_type = EncoderType::Render; - bound_render_pass = render_pass; + render_state.render_pass = render_pass; } } @@ -43,7 +43,9 @@ void CommandRecorder::EndEncoding() { encoder->endEncoding(); //[encoder release]; encoder = nullptr; - bound_render_pass = nullptr; + if (encoder_type == EncoderType::Render) { + render_state = {}; + } } } diff --git a/src/video_core/renderer_metal/mtl_command_recorder.h b/src/video_core/renderer_metal/mtl_command_recorder.h index fc04965d86..feb81d10aa 100644 --- a/src/video_core/renderer_metal/mtl_command_recorder.h +++ b/src/video_core/renderer_metal/mtl_command_recorder.h @@ -12,6 +12,27 @@ class Device; enum class EncoderType { Render, Compute, Blit }; +constexpr size_t MAX_BUFFERS = 31; +constexpr size_t MAX_TEXTURES = 31; +constexpr size_t MAX_SAMPLERS = 31; + +struct RenderState { + MTL::RenderPassDescriptor* render_pass{nullptr}; + MTL::RenderPipelineState* pipeline_state{nullptr}; + + MTL::Buffer* vertex_buffers[MAX_BUFFERS] = {nullptr}; + MTL::Buffer* fragment_buffers[MAX_BUFFERS] = {nullptr}; + MTL::Buffer* compute_buffers[MAX_BUFFERS] = {nullptr}; + + MTL::Texture* vertex_textures[MAX_TEXTURES] = {nullptr}; + MTL::Texture* fragment_textures[MAX_TEXTURES] = {nullptr}; + MTL::Texture* compute_textures[MAX_TEXTURES] = {nullptr}; + + MTL::SamplerState* vertex_sampler_states[MAX_SAMPLERS] = {nullptr}; + MTL::SamplerState* fragment_sampler_states[MAX_SAMPLERS] = {nullptr}; + MTL::SamplerState* compute_sampler_states[MAX_SAMPLERS] = {nullptr}; +}; + // TODO: whenever a render pass gets interrupted by either a compute or blit command and application // then tries to perform a render command, begin the same render pass, but with all load actions set // to "load" @@ -61,6 +82,77 @@ public: return static_cast(encoder); } + // Render commands + inline void SetRenderPipelineState(MTL::RenderPipelineState* pipeline_state) { + if (pipeline_state != render_state.pipeline_state) { + GetRenderCommandEncoder()->setRenderPipelineState(pipeline_state); + render_state.pipeline_state = pipeline_state; + } + } + + inline void SetVertexBuffer(MTL::Buffer* buffer, size_t index) { + if (buffer != render_state.vertex_buffers[index]) { + GetRenderCommandEncoder()->setVertexBuffer(buffer, index, 0); + render_state.vertex_buffers[index] = buffer; + } + } + + inline void SetFragmentBuffer(MTL::Buffer* buffer, size_t index) { + if (buffer != render_state.fragment_buffers[index]) { + GetRenderCommandEncoder()->setFragmentBuffer(buffer, index, 0); + render_state.fragment_buffers[index] = buffer; + } + } + + inline void SetComputeBuffer(MTL::Buffer* buffer, size_t index) { + if (buffer != render_state.compute_buffers[index]) { + GetComputeCommandEncoder()->setBuffer(buffer, index, 0); + render_state.compute_buffers[index] = buffer; + } + } + + inline void SetVertexTexture(MTL::Texture* texture, size_t index) { + if (texture != render_state.vertex_textures[index]) { + GetRenderCommandEncoder()->setVertexTexture(texture, index); + render_state.vertex_textures[index] = texture; + } + } + + inline void SetFragmentTexture(MTL::Texture* texture, size_t index) { + if (texture != render_state.fragment_textures[index]) { + GetRenderCommandEncoder()->setFragmentTexture(texture, index); + render_state.fragment_textures[index] = texture; + } + } + + inline void SetComputeTexture(MTL::Texture* texture, size_t index) { + if (texture != render_state.compute_textures[index]) { + GetComputeCommandEncoder()->setTexture(texture, index); + render_state.compute_textures[index] = texture; + } + } + + inline void SetVertexSamplerState(MTL::SamplerState* sampler_state, size_t index) { + if (sampler_state != render_state.vertex_sampler_states[index]) { + GetRenderCommandEncoder()->setVertexSamplerState(sampler_state, index); + render_state.vertex_sampler_states[index] = sampler_state; + } + } + + inline void SetFragmentSamplerState(MTL::SamplerState* sampler_state, size_t index) { + if (sampler_state != render_state.fragment_sampler_states[index]) { + GetRenderCommandEncoder()->setFragmentSamplerState(sampler_state, index); + render_state.fragment_sampler_states[index] = sampler_state; + } + } + + inline void SetComputeSamplerState(MTL::SamplerState* sampler_state, size_t index) { + if (sampler_state != render_state.compute_sampler_states[index]) { + GetComputeCommandEncoder()->setSamplerState(sampler_state, index); + render_state.compute_sampler_states[index] = sampler_state; + } + } + private: const Device& device; @@ -71,8 +163,8 @@ private: EncoderType encoder_type; - // Keep track of last bound render pass - MTL::RenderPassDescriptor* bound_render_pass{nullptr}; + // Keep track of bound resources + RenderState render_state{}; void RequireCommandBuffer(); }; diff --git a/src/video_core/renderer_metal/mtl_graphics_pipeline.cpp b/src/video_core/renderer_metal/mtl_graphics_pipeline.cpp index e37eda2747..fb3d38a294 100644 --- a/src/video_core/renderer_metal/mtl_graphics_pipeline.cpp +++ b/src/video_core/renderer_metal/mtl_graphics_pipeline.cpp @@ -120,7 +120,7 @@ void GraphicsPipeline::Configure(bool is_indexed) { } command_recorder.BeginOrContinueRenderPass(framebuffer->GetHandle()); - command_recorder.GetRenderCommandEncoder()->setRenderPipelineState(pipeline_state); + command_recorder.SetRenderPipelineState(pipeline_state); // Bind resources @@ -131,8 +131,8 @@ void GraphicsPipeline::Configure(bool is_indexed) { ImageView& image_view{texture_cache.GetImageView(views_it->id)}; Sampler& sampler{texture_cache.GetSampler(*samplers_it)}; - command_recorder.GetRenderCommandEncoder()->setFragmentTexture(image_view.GetHandle(), 0); - command_recorder.GetRenderCommandEncoder()->setFragmentSamplerState(sampler.GetHandle(), 0); + command_recorder.SetFragmentTexture(image_view.GetHandle(), 0); + command_recorder.SetFragmentSamplerState(sampler.GetHandle(), 0); } void GraphicsPipeline::MakePipeline(MTL::RenderPassDescriptor* render_pass) { diff --git a/src/video_core/renderer_metal/renderer_metal.cpp b/src/video_core/renderer_metal/renderer_metal.cpp index 98adf12d68..e0a0031d08 100644 --- a/src/video_core/renderer_metal/renderer_metal.cpp +++ b/src/video_core/renderer_metal/renderer_metal.cpp @@ -51,9 +51,9 @@ void RendererMetal::Composite(std::span framebuf 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); + 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,