[Impeller] Get rid of redundant viewport and scissor bindings. (#35330)

diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm
index 5f38a62..90f074e 100644
--- a/impeller/renderer/backend/metal/render_pass_mtl.mm
+++ b/impeller/renderer/backend/metal/render_pass_mtl.mm
@@ -299,6 +299,36 @@
     return false;
   }
 
+  void SetViewport(const Viewport& viewport) {
+    if (viewport_.has_value() && viewport_.value() == viewport) {
+      return;
+    }
+    [encoder_ setViewport:MTLViewport{
+                              .originX = viewport.rect.origin.x,
+                              .originY = viewport.rect.origin.y,
+                              .width = viewport.rect.size.width,
+                              .height = viewport.rect.size.height,
+                              .znear = viewport.depth_range.z_near,
+                              .zfar = viewport.depth_range.z_far,
+                          }];
+    viewport_ = viewport;
+  }
+
+  void SetScissor(const IRect& scissor) {
+    if (scissor_.has_value() && scissor_.value() == scissor) {
+      return;
+    }
+    [encoder_
+        setScissorRect:MTLScissorRect{
+                           .x = static_cast<NSUInteger>(scissor.origin.x),
+                           .y = static_cast<NSUInteger>(scissor.origin.y),
+                           .width = static_cast<NSUInteger>(scissor.size.width),
+                           .height =
+                               static_cast<NSUInteger>(scissor.size.height),
+                       }];
+    scissor_ = scissor;
+  }
+
  private:
   struct BufferOffsetPair {
     id<MTLBuffer> buffer = nullptr;
@@ -314,6 +344,8 @@
   std::map<ShaderStage, BufferMap> buffers_;
   std::map<ShaderStage, TextureMap> textures_;
   std::map<ShaderStage, SamplerMap> samplers_;
+  std::optional<Viewport> viewport_;
+  std::optional<IRect> scissor_;
 };
 
 static bool Bind(PassBindingsCache& pass,
@@ -423,6 +455,11 @@
         PipelineMTL::Cast(*command.pipeline).GetMTLRenderPipelineState());
     pass_bindings.SetDepthStencilState(
         PipelineMTL::Cast(*command.pipeline).GetMTLDepthStencilState());
+    pass_bindings.SetViewport(command.viewport.value_or<Viewport>(
+        {.rect = Rect::MakeSize(GetRenderTargetSize())}));
+    pass_bindings.SetScissor(
+        command.scissor.value_or(IRect::MakeSize(GetRenderTargetSize())));
+
     [encoder setFrontFacingWinding:pipeline_desc.GetWindingOrder() ==
                                            WindingOrder::kClockwise
                                        ? MTLWindingClockwise
@@ -430,27 +467,6 @@
     [encoder setCullMode:ToMTLCullMode(pipeline_desc.GetCullMode())];
     [encoder setStencilReferenceValue:command.stencil_reference];
 
-    auto v = command.viewport.value_or<Viewport>(
-        {.rect = Rect::MakeSize(GetRenderTargetSize())});
-    MTLViewport viewport = {
-        .originX = v.rect.origin.x,
-        .originY = v.rect.origin.y,
-        .width = v.rect.size.width,
-        .height = v.rect.size.height,
-        .znear = v.depth_range.z_near,
-        .zfar = v.depth_range.z_far,
-    };
-    [encoder setViewport:viewport];
-
-    auto s = command.scissor.value_or(IRect::MakeSize(GetRenderTargetSize()));
-    MTLScissorRect scissor = {
-        .x = static_cast<NSUInteger>(s.origin.x),
-        .y = static_cast<NSUInteger>(s.origin.y),
-        .width = static_cast<NSUInteger>(s.size.width),
-        .height = static_cast<NSUInteger>(s.size.height),
-    };
-    [encoder setScissorRect:scissor];
-
     if (!bind_stage_resources(command.vertex_bindings, ShaderStage::kVertex)) {
       return false;
     }
diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h
index ebf318f..65509c6 100644
--- a/impeller/renderer/formats.h
+++ b/impeller/renderer/formats.h
@@ -184,11 +184,19 @@
 struct DepthRange {
   Scalar z_near = 0.0;
   Scalar z_far = 1.0;
+
+  constexpr bool operator==(const DepthRange& other) const {
+    return z_near == other.z_near && z_far == other.z_far;
+  }
 };
 
 struct Viewport {
   Rect rect;
   DepthRange depth_range;
+
+  constexpr bool operator==(const Viewport& other) const {
+    return rect == other.rect && depth_range == other.depth_range;
+  }
 };
 
 enum class MinMagFilter {