| // Copyright 2013 The Flutter Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "flutter/lib/gpu/render_pass.h" | 
 |  | 
 | #include "flutter/lib/gpu/formats.h" | 
 | #include "flutter/lib/gpu/render_pipeline.h" | 
 | #include "flutter/lib/gpu/shader.h" | 
 | #include "fml/memory/ref_ptr.h" | 
 | #include "impeller/core/buffer_view.h" | 
 | #include "impeller/core/formats.h" | 
 | #include "impeller/core/sampler_descriptor.h" | 
 | #include "impeller/core/shader_types.h" | 
 | #include "impeller/core/vertex_buffer.h" | 
 | #include "impeller/geometry/color.h" | 
 | #include "impeller/renderer/pipeline_library.h" | 
 | #include "tonic/converter/dart_converter.h" | 
 |  | 
 | namespace flutter { | 
 | namespace gpu { | 
 |  | 
 | IMPLEMENT_WRAPPERTYPEINFO(flutter_gpu, RenderPass); | 
 |  | 
 | RenderPass::RenderPass() | 
 |     : vertex_buffer_( | 
 |           impeller::VertexBuffer{.index_type = impeller::IndexType::kNone}){}; | 
 |  | 
 | RenderPass::~RenderPass() = default; | 
 |  | 
 | const std::shared_ptr<const impeller::Context>& RenderPass::GetContext() const { | 
 |   return render_pass_->GetContext(); | 
 | } | 
 |  | 
 | impeller::Command& RenderPass::GetCommand() { | 
 |   return command_; | 
 | } | 
 |  | 
 | const impeller::Command& RenderPass::GetCommand() const { | 
 |   return command_; | 
 | } | 
 |  | 
 | impeller::RenderTarget& RenderPass::GetRenderTarget() { | 
 |   return render_target_; | 
 | } | 
 |  | 
 | const impeller::RenderTarget& RenderPass::GetRenderTarget() const { | 
 |   return render_target_; | 
 | } | 
 |  | 
 | impeller::ColorAttachmentDescriptor& RenderPass::GetColorAttachmentDescriptor( | 
 |     size_t color_attachment_index) { | 
 |   auto color = color_descriptors_.find(color_attachment_index); | 
 |   if (color == color_descriptors_.end()) { | 
 |     return color_descriptors_[color_attachment_index] = {}; | 
 |   } | 
 |   return color->second; | 
 | } | 
 |  | 
 | impeller::DepthAttachmentDescriptor& | 
 | RenderPass::GetDepthAttachmentDescriptor() { | 
 |   return depth_desc_; | 
 | } | 
 |  | 
 | impeller::VertexBuffer& RenderPass::GetVertexBuffer() { | 
 |   return vertex_buffer_; | 
 | } | 
 |  | 
 | bool RenderPass::Begin(flutter::gpu::CommandBuffer& command_buffer) { | 
 |   render_pass_ = | 
 |       command_buffer.GetCommandBuffer()->CreateRenderPass(render_target_); | 
 |   if (!render_pass_) { | 
 |     return false; | 
 |   } | 
 |   command_buffer.AddRenderPass(render_pass_); | 
 |   return true; | 
 | } | 
 |  | 
 | void RenderPass::SetPipeline(fml::RefPtr<RenderPipeline> pipeline) { | 
 |   render_pipeline_ = std::move(pipeline); | 
 | } | 
 |  | 
 | std::shared_ptr<impeller::Pipeline<impeller::PipelineDescriptor>> | 
 | RenderPass::GetOrCreatePipeline() { | 
 |   // Infer the pipeline layout based on the shape of the RenderTarget. | 
 |   auto pipeline_desc = pipeline_descriptor_; | 
 |   for (const auto& it : render_target_.GetColorAttachments()) { | 
 |     auto& color = GetColorAttachmentDescriptor(it.first); | 
 |     color.format = render_target_.GetRenderTargetPixelFormat(); | 
 |   } | 
 |   pipeline_desc.SetColorAttachmentDescriptors(color_descriptors_); | 
 |  | 
 |   { | 
 |     auto stencil = render_target_.GetStencilAttachment(); | 
 |     if (stencil && impeller::IsStencilWritable( | 
 |                        stencil->texture->GetTextureDescriptor().format)) { | 
 |       pipeline_desc.SetStencilPixelFormat( | 
 |           stencil->texture->GetTextureDescriptor().format); | 
 |       pipeline_desc.SetStencilAttachmentDescriptors(stencil_front_desc_, | 
 |                                                     stencil_back_desc_); | 
 |     } else { | 
 |       pipeline_desc.ClearStencilAttachments(); | 
 |     } | 
 |   } | 
 |  | 
 |   { | 
 |     auto depth = render_target_.GetDepthAttachment(); | 
 |     if (depth && impeller::IsDepthWritable( | 
 |                      depth->texture->GetTextureDescriptor().format)) { | 
 |       pipeline_desc.SetDepthPixelFormat( | 
 |           depth->texture->GetTextureDescriptor().format); | 
 |       pipeline_desc.SetDepthStencilAttachmentDescriptor(depth_desc_); | 
 |     } else { | 
 |       pipeline_desc.ClearDepthAttachment(); | 
 |     } | 
 |   } | 
 |  | 
 |   auto& context = *GetContext(); | 
 |  | 
 |   render_pipeline_->BindToPipelineDescriptor(*context.GetShaderLibrary(), | 
 |                                              pipeline_desc); | 
 |  | 
 |   auto pipeline = | 
 |       context.GetPipelineLibrary()->GetPipeline(pipeline_desc).Get(); | 
 |   FML_DCHECK(pipeline) << "Couldn't resolve render pipeline"; | 
 |   return pipeline; | 
 | } | 
 |  | 
 | impeller::Command RenderPass::ProvisionRasterCommand() { | 
 |   impeller::Command result = command_; | 
 |  | 
 |   result.pipeline = GetOrCreatePipeline(); | 
 |   result.BindVertices(vertex_buffer_); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | bool RenderPass::Draw() { | 
 |   impeller::Command result = ProvisionRasterCommand(); | 
 | #ifdef IMPELLER_DEBUG | 
 |   render_pass_->SetCommandLabel(result.label); | 
 | #endif  // IMPELLER_DEBUG | 
 |   render_pass_->SetPipeline(result.pipeline); | 
 |   render_pass_->SetStencilReference(result.stencil_reference); | 
 |   render_pass_->SetBaseVertex(result.base_vertex); | 
 |   if (result.viewport.has_value()) { | 
 |     render_pass_->SetViewport(result.viewport.value()); | 
 |   } | 
 |   if (result.scissor.has_value()) { | 
 |     render_pass_->SetScissor(result.scissor.value()); | 
 |   } | 
 |   render_pass_->SetVertexBuffer(result.vertex_buffer); | 
 |   for (const auto& buffer : result.vertex_bindings.buffers) { | 
 |     render_pass_->BindResource(impeller::ShaderStage::kVertex, | 
 |                                impeller::DescriptorType::kUniformBuffer, | 
 |                                buffer.slot, *buffer.view.GetMetadata(), | 
 |                                buffer.view.resource); | 
 |   } | 
 |   for (const auto& texture : result.vertex_bindings.sampled_images) { | 
 |     render_pass_->BindResource(impeller::ShaderStage::kVertex, | 
 |                                impeller::DescriptorType::kSampledImage, | 
 |                                texture.slot, *texture.texture.GetMetadata(), | 
 |                                texture.texture.resource, texture.sampler); | 
 |   } | 
 |   for (const auto& buffer : result.fragment_bindings.buffers) { | 
 |     render_pass_->BindResource(impeller::ShaderStage::kFragment, | 
 |                                impeller::DescriptorType::kUniformBuffer, | 
 |                                buffer.slot, *buffer.view.GetMetadata(), | 
 |                                buffer.view.resource); | 
 |   } | 
 |   for (const auto& texture : result.fragment_bindings.sampled_images) { | 
 |     render_pass_->BindResource(impeller::ShaderStage::kFragment, | 
 |                                impeller::DescriptorType::kSampledImage, | 
 |                                texture.slot, *texture.texture.GetMetadata(), | 
 |                                texture.texture.resource, texture.sampler); | 
 |   } | 
 |   return render_pass_->Draw().ok(); | 
 | } | 
 |  | 
 | }  // namespace gpu | 
 | }  // namespace flutter | 
 |  | 
 | static impeller::Color ToImpellerColor(uint32_t argb) { | 
 |   return impeller::Color::MakeRGBA8((argb >> 16) & 0xFF,  // R | 
 |                                     (argb >> 8) & 0xFF,   // G | 
 |                                     argb & 0xFF,          // B | 
 |                                     argb >> 24);          // A | 
 | } | 
 |  | 
 | //---------------------------------------------------------------------------- | 
 | /// Exports | 
 | /// | 
 |  | 
 | void InternalFlutterGpu_RenderPass_Initialize(Dart_Handle wrapper) { | 
 |   auto res = fml::MakeRefCounted<flutter::gpu::RenderPass>(); | 
 |   res->AssociateWithDartWrapper(wrapper); | 
 | } | 
 |  | 
 | Dart_Handle InternalFlutterGpu_RenderPass_SetColorAttachment( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     int color_attachment_index, | 
 |     int load_action, | 
 |     int store_action, | 
 |     int clear_color, | 
 |     flutter::gpu::Texture* texture, | 
 |     Dart_Handle resolve_texture_wrapper) { | 
 |   impeller::ColorAttachment desc; | 
 |   desc.load_action = flutter::gpu::ToImpellerLoadAction(load_action); | 
 |   desc.store_action = flutter::gpu::ToImpellerStoreAction(store_action); | 
 |   desc.clear_color = ToImpellerColor(static_cast<uint32_t>(clear_color)); | 
 |   desc.texture = texture->GetTexture(); | 
 |   if (!Dart_IsNull(resolve_texture_wrapper)) { | 
 |     flutter::gpu::Texture* resolve_texture = | 
 |         tonic::DartConverter<flutter::gpu::Texture*>::FromDart( | 
 |             resolve_texture_wrapper); | 
 |     desc.resolve_texture = resolve_texture->GetTexture(); | 
 |   } | 
 |   wrapper->GetRenderTarget().SetColorAttachment(desc, color_attachment_index); | 
 |   return Dart_Null(); | 
 | } | 
 |  | 
 | Dart_Handle InternalFlutterGpu_RenderPass_SetDepthStencilAttachment( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     int depth_load_action, | 
 |     int depth_store_action, | 
 |     float depth_clear_value, | 
 |     int stencil_load_action, | 
 |     int stencil_store_action, | 
 |     int stencil_clear_value, | 
 |     flutter::gpu::Texture* texture) { | 
 |   { | 
 |     impeller::DepthAttachment desc; | 
 |     desc.load_action = flutter::gpu::ToImpellerLoadAction(depth_load_action); | 
 |     desc.store_action = flutter::gpu::ToImpellerStoreAction(depth_store_action); | 
 |     desc.clear_depth = depth_clear_value; | 
 |     desc.texture = texture->GetTexture(); | 
 |     wrapper->GetRenderTarget().SetDepthAttachment(desc); | 
 |   } | 
 |   { | 
 |     impeller::StencilAttachment desc; | 
 |     desc.load_action = flutter::gpu::ToImpellerLoadAction(stencil_load_action); | 
 |     desc.store_action = | 
 |         flutter::gpu::ToImpellerStoreAction(stencil_store_action); | 
 |     desc.clear_stencil = stencil_clear_value; | 
 |     desc.texture = texture->GetTexture(); | 
 |     wrapper->GetRenderTarget().SetStencilAttachment(desc); | 
 |   } | 
 |  | 
 |   return Dart_Null(); | 
 | } | 
 |  | 
 | Dart_Handle InternalFlutterGpu_RenderPass_Begin( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     flutter::gpu::CommandBuffer* command_buffer) { | 
 |   if (!wrapper->Begin(*command_buffer)) { | 
 |     return tonic::ToDart("Failed to begin RenderPass"); | 
 |   } | 
 |   return Dart_Null(); | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_BindPipeline( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     flutter::gpu::RenderPipeline* pipeline) { | 
 |   auto ref = fml::RefPtr<flutter::gpu::RenderPipeline>(pipeline); | 
 |   wrapper->SetPipeline(std::move(ref)); | 
 | } | 
 |  | 
 | template <typename TBuffer> | 
 | static void BindVertexBuffer(flutter::gpu::RenderPass* wrapper, | 
 |                              TBuffer buffer, | 
 |                              int offset_in_bytes, | 
 |                              int length_in_bytes, | 
 |                              int vertex_count) { | 
 |   auto& vertex_buffer = wrapper->GetVertexBuffer(); | 
 |   vertex_buffer.vertex_buffer = impeller::BufferView{ | 
 |       .buffer = buffer, | 
 |       .range = impeller::Range(offset_in_bytes, length_in_bytes), | 
 |   }; | 
 |   // If the index type is set, then the `vertex_count` becomes the index | 
 |   // count... So don't overwrite the count if it's already been set when binding | 
 |   // the index buffer. | 
 |   // TODO(bdero): Consider just doing a more traditional API with | 
 |   //              draw(vertexCount) and drawIndexed(indexCount). This is fine, | 
 |   //              but overall it would be a bit more explicit and we wouldn't | 
 |   //              have to document this behavior where the presence of the index | 
 |   //              buffer always takes precedent. | 
 |   if (vertex_buffer.index_type == impeller::IndexType::kNone) { | 
 |     vertex_buffer.vertex_count = vertex_count; | 
 |   } | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_BindVertexBufferDevice( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     flutter::gpu::DeviceBuffer* device_buffer, | 
 |     int offset_in_bytes, | 
 |     int length_in_bytes, | 
 |     int vertex_count) { | 
 |   BindVertexBuffer(wrapper, device_buffer->GetBuffer(), offset_in_bytes, | 
 |                    length_in_bytes, vertex_count); | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_BindVertexBufferHost( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     flutter::gpu::HostBuffer* host_buffer, | 
 |     int offset_in_bytes, | 
 |     int length_in_bytes, | 
 |     int vertex_count) { | 
 |   std::optional<impeller::BufferView> view = | 
 |       host_buffer->GetBufferViewForOffset(offset_in_bytes); | 
 |   if (!view.has_value()) { | 
 |     FML_LOG(ERROR) | 
 |         << "Failed to bind vertex buffer due to invalid HostBuffer offset: " | 
 |         << offset_in_bytes; | 
 |     return; | 
 |   } | 
 |   BindVertexBuffer(wrapper, view->buffer, view->range.offset, | 
 |                    view->range.length, vertex_count); | 
 | } | 
 |  | 
 | template <typename TBuffer> | 
 | static void BindIndexBuffer(flutter::gpu::RenderPass* wrapper, | 
 |                             TBuffer buffer, | 
 |                             int offset_in_bytes, | 
 |                             int length_in_bytes, | 
 |                             int index_type, | 
 |                             int index_count) { | 
 |   auto& vertex_buffer = wrapper->GetVertexBuffer(); | 
 |   vertex_buffer.index_buffer = impeller::BufferView{ | 
 |       .buffer = buffer, | 
 |       .range = impeller::Range(offset_in_bytes, length_in_bytes), | 
 |   }; | 
 |   vertex_buffer.index_type = flutter::gpu::ToImpellerIndexType(index_type); | 
 |   vertex_buffer.vertex_count = index_count; | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_BindIndexBufferDevice( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     flutter::gpu::DeviceBuffer* device_buffer, | 
 |     int offset_in_bytes, | 
 |     int length_in_bytes, | 
 |     int index_type, | 
 |     int index_count) { | 
 |   BindIndexBuffer(wrapper, device_buffer->GetBuffer(), offset_in_bytes, | 
 |                   length_in_bytes, index_type, index_count); | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_BindIndexBufferHost( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     flutter::gpu::HostBuffer* host_buffer, | 
 |     int offset_in_bytes, | 
 |     int length_in_bytes, | 
 |     int index_type, | 
 |     int index_count) { | 
 |   auto view = host_buffer->GetBufferViewForOffset(offset_in_bytes); | 
 |   if (!view.has_value()) { | 
 |     FML_LOG(ERROR) | 
 |         << "Failed to bind index buffer due to invalid HostBuffer offset: " | 
 |         << offset_in_bytes; | 
 |     return; | 
 |   } | 
 |   BindIndexBuffer(wrapper, view->buffer, view->range.offset, view->range.length, | 
 |                   index_type, index_count); | 
 | } | 
 |  | 
 | template <typename TBuffer> | 
 | static bool BindUniform(flutter::gpu::RenderPass* wrapper, | 
 |                         flutter::gpu::Shader* shader, | 
 |                         Dart_Handle uniform_name_handle, | 
 |                         TBuffer buffer, | 
 |                         int offset_in_bytes, | 
 |                         int length_in_bytes) { | 
 |   auto& command = wrapper->GetCommand(); | 
 |  | 
 |   auto uniform_name = tonic::StdStringFromDart(uniform_name_handle); | 
 |   const flutter::gpu::Shader::UniformBinding* uniform_struct = | 
 |       shader->GetUniformStruct(uniform_name); | 
 |   // TODO(bdero): Return an error string stating that no uniform struct with | 
 |   //              this name exists and throw an exception. | 
 |   if (!uniform_struct) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return command.BindResource( | 
 |       shader->GetShaderStage(), impeller::DescriptorType::kUniformBuffer, | 
 |       uniform_struct->slot, uniform_struct->metadata, | 
 |       impeller::BufferView{ | 
 |           .buffer = buffer, | 
 |           .range = impeller::Range(offset_in_bytes, length_in_bytes), | 
 |       }); | 
 | } | 
 |  | 
 | bool InternalFlutterGpu_RenderPass_BindUniformDevice( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     flutter::gpu::Shader* shader, | 
 |     Dart_Handle uniform_name_handle, | 
 |     flutter::gpu::DeviceBuffer* device_buffer, | 
 |     int offset_in_bytes, | 
 |     int length_in_bytes) { | 
 |   return BindUniform(wrapper, shader, uniform_name_handle, | 
 |                      device_buffer->GetBuffer(), offset_in_bytes, | 
 |                      length_in_bytes); | 
 | } | 
 |  | 
 | bool InternalFlutterGpu_RenderPass_BindUniformHost( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     flutter::gpu::Shader* shader, | 
 |     Dart_Handle uniform_name_handle, | 
 |     flutter::gpu::HostBuffer* host_buffer, | 
 |     int offset_in_bytes, | 
 |     int length_in_bytes) { | 
 |   auto view = host_buffer->GetBufferViewForOffset(offset_in_bytes); | 
 |   if (!view.has_value()) { | 
 |     FML_LOG(ERROR) | 
 |         << "Failed to bind index buffer due to invalid HostBuffer offset: " | 
 |         << offset_in_bytes; | 
 |     return false; | 
 |   } | 
 |   return BindUniform(wrapper, shader, uniform_name_handle, view->buffer, | 
 |                      view->range.offset, view->range.length); | 
 | } | 
 |  | 
 | bool InternalFlutterGpu_RenderPass_BindTexture( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     flutter::gpu::Shader* shader, | 
 |     Dart_Handle uniform_name_handle, | 
 |     flutter::gpu::Texture* texture, | 
 |     int min_filter, | 
 |     int mag_filter, | 
 |     int mip_filter, | 
 |     int width_address_mode, | 
 |     int height_address_mode) { | 
 |   auto& command = wrapper->GetCommand(); | 
 |  | 
 |   auto uniform_name = tonic::StdStringFromDart(uniform_name_handle); | 
 |   const impeller::SampledImageSlot* image_slot = | 
 |       shader->GetUniformTexture(uniform_name); | 
 |   // TODO(bdero): Return an error string stating that no uniform texture with | 
 |   //              this name exists and throw an exception. | 
 |   if (!image_slot) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   impeller::SamplerDescriptor sampler_desc; | 
 |   sampler_desc.min_filter = flutter::gpu::ToImpellerMinMagFilter(min_filter); | 
 |   sampler_desc.mag_filter = flutter::gpu::ToImpellerMinMagFilter(mag_filter); | 
 |   sampler_desc.mip_filter = flutter::gpu::ToImpellerMipFilter(mip_filter); | 
 |   sampler_desc.width_address_mode = | 
 |       flutter::gpu::ToImpellerSamplerAddressMode(width_address_mode); | 
 |   sampler_desc.height_address_mode = | 
 |       flutter::gpu::ToImpellerSamplerAddressMode(height_address_mode); | 
 |   const std::unique_ptr<const impeller::Sampler>& sampler = | 
 |       wrapper->GetContext()->GetSamplerLibrary()->GetSampler(sampler_desc); | 
 |  | 
 |   return command.BindResource( | 
 |       shader->GetShaderStage(), impeller::DescriptorType::kSampledImage, | 
 |       *image_slot, impeller::ShaderMetadata{}, texture->GetTexture(), sampler); | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_ClearBindings( | 
 |     flutter::gpu::RenderPass* wrapper) { | 
 |   auto& command = wrapper->GetCommand(); | 
 |   command.vertex_buffer = {}; | 
 |   command.vertex_bindings = {}; | 
 |   command.fragment_bindings = {}; | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_SetColorBlendEnable( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     int color_attachment_index, | 
 |     bool enable) { | 
 |   auto& color = wrapper->GetColorAttachmentDescriptor(color_attachment_index); | 
 |   color.blending_enabled = enable; | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_SetColorBlendEquation( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     int color_attachment_index, | 
 |     int color_blend_operation, | 
 |     int source_color_blend_factor, | 
 |     int destination_color_blend_factor, | 
 |     int alpha_blend_operation, | 
 |     int source_alpha_blend_factor, | 
 |     int destination_alpha_blend_factor) { | 
 |   auto& color = wrapper->GetColorAttachmentDescriptor(color_attachment_index); | 
 |   color.color_blend_op = | 
 |       flutter::gpu::ToImpellerBlendOperation(color_blend_operation); | 
 |   color.src_color_blend_factor = | 
 |       flutter::gpu::ToImpellerBlendFactor(source_color_blend_factor); | 
 |   color.dst_color_blend_factor = | 
 |       flutter::gpu::ToImpellerBlendFactor(destination_color_blend_factor); | 
 |   color.alpha_blend_op = | 
 |       flutter::gpu::ToImpellerBlendOperation(alpha_blend_operation); | 
 |   color.src_alpha_blend_factor = | 
 |       flutter::gpu::ToImpellerBlendFactor(source_alpha_blend_factor); | 
 |   color.dst_alpha_blend_factor = | 
 |       flutter::gpu::ToImpellerBlendFactor(destination_alpha_blend_factor); | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_SetDepthWriteEnable( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     bool enable) { | 
 |   auto& depth = wrapper->GetDepthAttachmentDescriptor(); | 
 |   depth.depth_write_enabled = true; | 
 | } | 
 |  | 
 | void InternalFlutterGpu_RenderPass_SetDepthCompareOperation( | 
 |     flutter::gpu::RenderPass* wrapper, | 
 |     int compare_operation) { | 
 |   auto& depth = wrapper->GetDepthAttachmentDescriptor(); | 
 |   depth.depth_compare = | 
 |       flutter::gpu::ToImpellerCompareFunction(compare_operation); | 
 | } | 
 |  | 
 | bool InternalFlutterGpu_RenderPass_Draw(flutter::gpu::RenderPass* wrapper) { | 
 |   return wrapper->Draw(); | 
 | } |