blob: 637f7446e5266a36a7ec8017cc03c27dbbbdf4ec [file] [log] [blame]
// 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 <future>
#include <memory>
#include "flutter/lib/gpu/formats.h"
#include "flutter/lib/gpu/render_pipeline.h"
#include "flutter/lib/gpu/shader.h"
#include "fml/make_copyable.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/geometry/color.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/pipeline.h"
#include "impeller/renderer/pipeline_descriptor.h"
#include "impeller/renderer/pipeline_library.h"
#include "lib/gpu/context.h"
#include "lib/ui/ui_dart_state.h"
#include "tonic/converter/dart_converter.h"
namespace flutter {
namespace gpu {
IMPLEMENT_WRAPPERTYPEINFO(flutter_gpu, RenderPass);
RenderPass::RenderPass() = default;
RenderPass::~RenderPass() = default;
const std::shared_ptr<const impeller::Context>& RenderPass::GetContext() const {
return render_pass_->GetContext();
}
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::StencilAttachmentDescriptor&
RenderPass::GetStencilFrontAttachmentDescriptor() {
return stencil_front_desc_;
}
impeller::StencilAttachmentDescriptor&
RenderPass::GetStencilBackAttachmentDescriptor() {
return stencil_back_desc_;
}
impeller::PipelineDescriptor& RenderPass::GetPipelineDescriptor() {
return pipeline_descriptor_;
}
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);
}
void RenderPass::ClearBindings() {
vertex_uniform_bindings.clear();
vertex_texture_bindings.clear();
fragment_uniform_bindings.clear();
fragment_texture_bindings.clear();
vertex_buffer = {};
index_buffer = {};
index_buffer_type = impeller::IndexType::kNone;
element_count = 0;
}
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_;
pipeline_desc.SetSampleCount(render_target_.GetSampleCount());
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);
std::shared_ptr<impeller::Pipeline<impeller::PipelineDescriptor>> pipeline;
if (context.GetBackendType() == impeller::Context::BackendType::kOpenGLES &&
!context.GetPipelineLibrary()->HasPipeline(pipeline_desc)) {
// For GLES, new pipeline creation must be done on the reactor (raster)
// thread. We're about the draw, so we need to synchronize with a raster
// task in order to get the new pipeline. Depending on how busy the raster
// thread is, this could hang the UI thread long enough to miss a frame.
// Note that this branch is only called if a new pipeline actually needs to
// be built.
auto dart_state = flutter::UIDartState::Current();
std::promise<
std::shared_ptr<impeller::Pipeline<impeller::PipelineDescriptor>>>
pipeline_promise;
auto pipeline_future = pipeline_promise.get_future();
fml::TaskRunner::RunNowOrPostTask(
dart_state->GetTaskRunners().GetRasterTaskRunner(),
fml::MakeCopyable([promise = std::move(pipeline_promise),
context = GetContext(), pipeline_desc]() mutable {
promise.set_value(
context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get());
}));
pipeline = pipeline_future.get();
} else {
pipeline = context.GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
}
FML_DCHECK(pipeline) << "Couldn't resolve render pipeline";
return pipeline;
}
bool RenderPass::Draw() {
render_pass_->SetPipeline(GetOrCreatePipeline());
for (const auto& [_, buffer] : vertex_uniform_bindings) {
render_pass_->BindResource(impeller::ShaderStage::kVertex,
impeller::DescriptorType::kUniformBuffer,
buffer.slot, *buffer.view.GetMetadata(),
buffer.view.resource);
}
for (const auto& [_, texture] : vertex_texture_bindings) {
render_pass_->BindResource(impeller::ShaderStage::kVertex,
impeller::DescriptorType::kSampledImage,
texture.slot, *texture.texture.GetMetadata(),
texture.texture.resource, *texture.sampler);
}
for (const auto& [_, buffer] : fragment_uniform_bindings) {
render_pass_->BindResource(impeller::ShaderStage::kFragment,
impeller::DescriptorType::kUniformBuffer,
buffer.slot, *buffer.view.GetMetadata(),
buffer.view.resource);
}
for (const auto& [_, texture] : fragment_texture_bindings) {
render_pass_->BindResource(impeller::ShaderStage::kFragment,
impeller::DescriptorType::kSampledImage,
texture.slot, *texture.texture.GetMetadata(),
texture.texture.resource, *texture.sampler);
}
render_pass_->SetVertexBuffer(vertex_buffer);
render_pass_->SetIndexBuffer(index_buffer, index_buffer_type);
render_pass_->SetElementCount(element_count);
render_pass_->SetStencilReference(stencil_reference);
bool result = render_pass_->Draw().ok();
return result;
}
} // namespace gpu
} // namespace flutter
//----------------------------------------------------------------------------
/// 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,
flutter::gpu::Context* context,
int color_attachment_index,
int load_action,
int store_action,
float clear_color_r,
float clear_color_g,
float clear_color_b,
float clear_color_a,
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 = impeller::Color(clear_color_r, clear_color_g,
clear_color_b, clear_color_a);
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();
// If the backend doesn't support normal MSAA, gracefully fallback to
// rendering without MSAA.
if (!flutter::gpu::SupportsNormalOffscreenMSAA(*context->GetContext())) {
desc.texture = desc.resolve_texture;
desc.resolve_texture = nullptr;
desc.store_action = impeller::StoreAction::kStore;
}
}
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));
}
static void BindVertexBuffer(
flutter::gpu::RenderPass* wrapper,
const std::shared_ptr<const impeller::DeviceBuffer>& buffer,
int offset_in_bytes,
int length_in_bytes,
int vertex_count) {
wrapper->vertex_buffer = impeller::BufferView(
buffer, 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 (!wrapper->has_index_buffer) {
wrapper->element_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);
}
static void BindIndexBuffer(
flutter::gpu::RenderPass* wrapper,
const std::shared_ptr<const impeller::DeviceBuffer>& buffer,
int offset_in_bytes,
int length_in_bytes,
int index_type,
int index_count) {
impeller::IndexType type = flutter::gpu::ToImpellerIndexType(index_type);
wrapper->index_buffer = impeller::BufferView(
buffer, impeller::Range(offset_in_bytes, length_in_bytes));
wrapper->index_buffer_type = type;
bool setting_index_buffer = type != impeller::IndexType::kNone;
if (setting_index_buffer) {
wrapper->element_count = index_count;
}
wrapper->has_index_buffer = setting_index_buffer;
}
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);
}
static bool BindUniform(
flutter::gpu::RenderPass* wrapper,
flutter::gpu::Shader* shader,
Dart_Handle uniform_name_handle,
const std::shared_ptr<const impeller::DeviceBuffer>& buffer,
int offset_in_bytes,
int length_in_bytes) {
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;
}
flutter::gpu::RenderPass::BufferUniformMap* uniform_map = nullptr;
switch (shader->GetShaderStage()) {
case impeller::ShaderStage::kVertex:
uniform_map = &wrapper->vertex_uniform_bindings;
break;
case impeller::ShaderStage::kFragment:
uniform_map = &wrapper->fragment_uniform_bindings;
break;
case impeller::ShaderStage::kUnknown:
case impeller::ShaderStage::kCompute:
return false;
}
if (!buffer || static_cast<size_t>(offset_in_bytes + length_in_bytes) >
buffer->GetDeviceBufferDescriptor().size) {
return false;
}
uniform_map->insert_or_assign(
uniform_struct,
impeller::BufferAndUniformSlot{
.slot = uniform_struct->slot,
.view = impeller::BufferResource{
&uniform_struct->metadata,
impeller::BufferView(
buffer, impeller::Range(offset_in_bytes, length_in_bytes)),
}});
return true;
}
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_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 uniform_name = tonic::StdStringFromDart(uniform_name_handle);
const flutter::gpu::Shader::TextureBinding* texture_binding =
shader->GetUniformTexture(uniform_name);
// TODO(bdero): Return an error string stating that no uniform texture with
// this name exists and throw an exception.
if (!texture_binding) {
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);
flutter::gpu::RenderPass::TextureUniformMap* uniform_map = nullptr;
switch (shader->GetShaderStage()) {
case impeller::ShaderStage::kVertex:
uniform_map = &wrapper->vertex_texture_bindings;
break;
case impeller::ShaderStage::kFragment:
uniform_map = &wrapper->fragment_texture_bindings;
break;
case impeller::ShaderStage::kUnknown:
case impeller::ShaderStage::kCompute:
return false;
}
uniform_map->insert_or_assign(
texture_binding,
impeller::TextureAndSampler{
.slot = texture_binding->slot,
.texture = {&texture_binding->metadata, texture->GetTexture()},
.sampler = &sampler,
});
return true;
}
void InternalFlutterGpu_RenderPass_ClearBindings(
flutter::gpu::RenderPass* wrapper) {
wrapper->ClearBindings();
}
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);
}
void InternalFlutterGpu_RenderPass_SetStencilReference(
flutter::gpu::RenderPass* wrapper,
int stencil_reference) {
wrapper->stencil_reference = static_cast<uint32_t>(stencil_reference);
}
void InternalFlutterGpu_RenderPass_SetStencilConfig(
flutter::gpu::RenderPass* wrapper,
int stencil_compare_operation,
int stencil_fail_operation,
int depth_fail_operation,
int depth_stencil_pass_operation,
int read_mask,
int write_mask,
int target_face) {
impeller::StencilAttachmentDescriptor desc;
desc.stencil_compare =
flutter::gpu::ToImpellerCompareFunction(stencil_compare_operation);
desc.stencil_failure =
flutter::gpu::ToImpellerStencilOperation(stencil_fail_operation);
desc.depth_failure =
flutter::gpu::ToImpellerStencilOperation(depth_fail_operation);
desc.depth_stencil_pass =
flutter::gpu::ToImpellerStencilOperation(depth_stencil_pass_operation);
desc.read_mask = static_cast<uint32_t>(read_mask);
desc.write_mask = static_cast<uint32_t>(write_mask);
// Corresponds to the `StencilFace` enum in `gpu/lib/src/render_pass.dart`.
if (target_face != 2 /* both or front */) {
wrapper->GetStencilFrontAttachmentDescriptor() = desc;
}
if (target_face != 1 /* both or back */) {
wrapper->GetStencilBackAttachmentDescriptor() = desc;
}
}
void InternalFlutterGpu_RenderPass_SetCullMode(
flutter::gpu::RenderPass* wrapper,
int cull_mode) {
impeller::PipelineDescriptor& pipeline_descriptor =
wrapper->GetPipelineDescriptor();
pipeline_descriptor.SetCullMode(flutter::gpu::ToImpellerCullMode(cull_mode));
}
void InternalFlutterGpu_RenderPass_SetPrimitiveType(
flutter::gpu::RenderPass* wrapper,
int primitive_type) {
impeller::PipelineDescriptor& pipeline_descriptor =
wrapper->GetPipelineDescriptor();
pipeline_descriptor.SetPrimitiveType(
flutter::gpu::ToImpellerPrimitiveType(primitive_type));
}
void InternalFlutterGpu_RenderPass_SetWindingOrder(
flutter::gpu::RenderPass* wrapper,
int winding_order) {
impeller::PipelineDescriptor& pipeline_descriptor =
wrapper->GetPipelineDescriptor();
pipeline_descriptor.SetWindingOrder(
flutter::gpu::ToImpellerWindingOrder(winding_order));
}
void InternalFlutterGpu_RenderPass_SetPolygonMode(
flutter::gpu::RenderPass* wrapper,
int polygon_mode) {
impeller::PipelineDescriptor& pipeline_descriptor =
wrapper->GetPipelineDescriptor();
pipeline_descriptor.SetPolygonMode(
flutter::gpu::ToImpellerPolygonMode(polygon_mode));
}
bool InternalFlutterGpu_RenderPass_Draw(flutter::gpu::RenderPass* wrapper) {
return wrapper->Draw();
}