blob: 8dd9403d2935f45a7c649283a2d272c340672dfb [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 "flutter/lib/gpu/formats.h"
#include "flutter/lib/gpu/render_pipeline.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 "impeller/renderer/sampler_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::weak_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().lock();
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();
return render_pass_->AddCommand(std::move(result));
}
} // 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->GetBuffer(),
.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, 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) {
BindVertexBuffer(wrapper, host_buffer, offset_in_bytes, length_in_bytes,
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->GetBuffer(),
.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, 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) {
BindIndexBuffer(wrapper, host_buffer, offset_in_bytes, length_in_bytes,
index_type, index_count);
}
template <typename TBuffer>
static bool BindUniform(flutter::gpu::RenderPass* wrapper,
int stage,
int slot_id,
TBuffer* buffer,
int offset_in_bytes,
int length_in_bytes) {
// TODO(113715): Populate this metadata once GLES is able to handle
// non-struct uniform names.
std::shared_ptr<impeller::ShaderMetadata> metadata =
std::make_shared<impeller::ShaderMetadata>();
auto& command = wrapper->GetCommand();
impeller::ShaderUniformSlot slot;
// Don't populate the slot name... we don't have it here and Impeller doesn't
// even use it for anything.
slot.ext_res_0 = slot_id;
return command.BindResource(
flutter::gpu::ToImpellerShaderStage(stage), slot, metadata,
impeller::BufferView{
.buffer = buffer->GetBuffer(),
.range = impeller::Range(offset_in_bytes, length_in_bytes),
});
}
bool InternalFlutterGpu_RenderPass_BindUniformDevice(
flutter::gpu::RenderPass* wrapper,
int stage,
int slot_id,
flutter::gpu::DeviceBuffer* device_buffer,
int offset_in_bytes,
int length_in_bytes) {
return BindUniform(wrapper, stage, slot_id, device_buffer, offset_in_bytes,
length_in_bytes);
}
bool InternalFlutterGpu_RenderPass_BindUniformHost(
flutter::gpu::RenderPass* wrapper,
int stage,
int slot_id,
flutter::gpu::HostBuffer* host_buffer,
int offset_in_bytes,
int length_in_bytes) {
return BindUniform(wrapper, stage, slot_id, host_buffer, offset_in_bytes,
length_in_bytes);
}
bool InternalFlutterGpu_RenderPass_BindTexture(
flutter::gpu::RenderPass* wrapper,
int stage,
int slot_id,
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();
// TODO(113715): Populate this metadata once GLES is able to handle
// non-struct uniform names.
impeller::ShaderMetadata metadata;
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);
auto sampler = wrapper->GetContext().lock()->GetSamplerLibrary()->GetSampler(
sampler_desc);
impeller::SampledImageSlot image_slot;
image_slot.texture_index = slot_id;
image_slot.sampler_index = slot_id;
return command.BindResource(flutter::gpu::ToImpellerShaderStage(stage),
image_slot, metadata, texture->GetTexture(),
sampler);
}
void InternalFlutterGpu_RenderPass_ClearBindings(
flutter::gpu::RenderPass* wrapper) {
auto& command = wrapper->GetCommand();
command.vertex_count = 0;
command.vertex_bindings = {};
command.fragment_bindings = {};
command.index_buffer = {};
}
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();
}