| // 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(); |
| } |