[Impeller] Change Renderer utility to pass RenderTarget to callback; render non-pipeline blend modes (flutter/engine#32982)

This commit is contained in:
Brandon DeRosier 2022-05-09 23:49:06 -07:00 committed by GitHub
parent 98a3d5b7a8
commit 08295d23f7
23 changed files with 415 additions and 138 deletions

View File

@ -28,13 +28,13 @@ bool AiksContext::IsValid() const {
return is_valid_;
}
bool AiksContext::Render(const Picture& picture, RenderPass& parent_pass) {
bool AiksContext::Render(const Picture& picture, RenderTarget& render_target) {
if (!IsValid()) {
return false;
}
if (picture.pass) {
return picture.pass->Render(*content_context_, parent_pass);
return picture.pass->Render(*content_context_, render_target);
}
return true;

View File

@ -9,6 +9,7 @@
#include "flutter/fml/macros.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/render_target.h"
namespace impeller {
@ -23,7 +24,7 @@ class AiksContext {
bool IsValid() const;
bool Render(const Picture& picture, RenderPass& parent_pass);
bool Render(const Picture& picture, RenderTarget& render_target);
private:
std::shared_ptr<Context> context_;

View File

@ -14,8 +14,8 @@ AiksPlayground::~AiksPlayground() = default;
bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) {
return OpenPlaygroundHere(
[&picture](AiksContext& renderer, RenderPass& pass) -> bool {
return renderer.Render(picture, pass);
[&picture](AiksContext& renderer, RenderTarget& render_target) -> bool {
return renderer.Render(picture, render_target);
});
}
@ -31,8 +31,8 @@ bool AiksPlayground::OpenPlaygroundHere(AiksPlaygroundCallback callback) {
}
return Playground::OpenPlaygroundHere(
[&renderer, &callback](RenderPass& pass) -> bool {
return callback(renderer, pass);
[&renderer, &callback](RenderTarget& render_target) -> bool {
return callback(renderer, render_target);
});
}

View File

@ -14,7 +14,7 @@ namespace impeller {
class AiksPlayground : public Playground {
public:
using AiksPlaygroundCallback =
std::function<bool(AiksContext& renderer, RenderPass& pass)>;
std::function<bool(AiksContext& renderer, RenderTarget& render_target)>;
AiksPlayground();

View File

@ -3,6 +3,8 @@
// found in the LICENSE file.
#include <array>
#include <cmath>
#include <tuple>
#include "flutter/testing/testing.h"
#include "impeller/aiks/aiks_playground.h"
@ -467,6 +469,121 @@ TEST_P(AiksTest, PaintBlendModeIsRespected) {
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, CanDrawWithAdvancedBlend) {
// Compare with https://fiddle.skia.org/c/@BlendModes
std::vector<const char*> blend_mode_names;
std::vector<Entity::BlendMode> blend_mode_values;
{
const std::vector<std::tuple<const char*, Entity::BlendMode>> blends = {
// Pipeline blends (Porter-Duff/alpha blends)
{"Clear", Entity::BlendMode::kClear},
{"Source", Entity::BlendMode::kSource},
{"Destination", Entity::BlendMode::kDestination},
{"SourceOver", Entity::BlendMode::kSourceOver},
{"DestinationOver", Entity::BlendMode::kDestinationOver},
{"SourceIn", Entity::BlendMode::kSourceIn},
{"DestinationIn", Entity::BlendMode::kDestinationIn},
{"SourceOut", Entity::BlendMode::kSourceOut},
{"DestinationOut", Entity::BlendMode::kDestinationOut},
{"SourceATop", Entity::BlendMode::kSourceATop},
{"DestinationATop", Entity::BlendMode::kDestinationATop},
{"Xor", Entity::BlendMode::kXor},
{"Plus", Entity::BlendMode::kPlus},
{"Modulate", Entity::BlendMode::kModulate},
// Advanced blends (non Porter-Duff/color blends)
{"Screen", Entity::BlendMode::kScreen},
};
assert(blends.size() ==
static_cast<size_t>(Entity::BlendMode::kLastAdvancedBlendMode) + 1);
for (const auto& [name, mode] : blends) {
blend_mode_names.push_back(name);
blend_mode_values.push_back(mode);
}
}
auto draw_color_wheel = [](Canvas& canvas) {
/// color_wheel_sampler: r=0 -> fuchsia, r=2pi/3 -> yellow, r=4pi/3 ->
/// cyan domain: r >= 0 (because modulo used is non euclidean)
auto color_wheel_sampler = [](Radians r) {
Scalar x = r.radians / k2Pi + 1;
// https://www.desmos.com/calculator/6nhjelyoaj
auto color_cycle = [](Scalar x) {
Scalar cycle = std::fmod(x, 6.0f);
return std::max(0.0f, std::min(1.0f, 2 - std::abs(2 - cycle)));
};
return Color(color_cycle(6 * x + 1), //
color_cycle(6 * x - 1), //
color_cycle(6 * x - 3), //
1);
};
Paint paint;
// Draw a fancy color wheel for the backdrop.
// https://www.desmos.com/calculator/xw7kafthwd
const int max_dist = 900;
for (int i = 0; i <= 900; i++) {
Radians r(kPhi / k2Pi * i);
Scalar distance = r.radians / std::powf(4.12, 0.0026 * r.radians);
Scalar normalized_distance = static_cast<Scalar>(i) / max_dist;
paint.color =
color_wheel_sampler(r).WithAlpha(1.0f - normalized_distance);
Point position(distance * std::sin(r.radians),
-distance * std::cos(r.radians));
canvas.DrawCircle(position, 9 + normalized_distance * 3, paint);
}
};
bool first_frame = true;
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
if (first_frame) {
first_frame = false;
ImGui::SetNextWindowSize({350, 200});
ImGui::SetNextWindowPos({325, 550});
}
ImGui::Begin("Controls");
static int current_blend_index = 3;
ImGui::ListBox("Blending mode", &current_blend_index,
blend_mode_names.data(), blend_mode_names.size());
ImGui::End();
Canvas canvas;
Paint paint;
// Default blend is kSourceOver.
paint.color = Color::White();
canvas.DrawPaint(paint);
canvas.Translate(Vector2(500, 400));
canvas.Scale(Vector2(3, 3));
draw_color_wheel(canvas);
// Draw 3 circles to a subpass and blend it in.
canvas.SaveLayer({.blend_mode = blend_mode_values[current_blend_index]});
{
paint.blend_mode = Entity::BlendMode::kPlus;
const Scalar x = std::sin(k2Pi / 3);
const Scalar y = -std::cos(k2Pi / 3);
paint.color = Color::Red();
canvas.DrawCircle(Point(-x, y) * 45, 65, paint);
paint.color = Color::Green();
canvas.DrawCircle(Point(0, -1) * 45, 65, paint);
paint.color = Color::Blue();
canvas.DrawCircle(Point(x, y) * 45, 65, paint);
}
canvas.Restore();
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
TEST_P(AiksTest, TransformMultipliesCorrectly) {
Canvas canvas;
ASSERT_MATRIX_NEAR(canvas.GetCurrentTransformation(), Matrix());
@ -509,7 +626,7 @@ TEST_P(AiksTest, TransformMultipliesCorrectly) {
TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
// Compare with https://fiddle.skia.org/c/027392122bec8ac2b5d5de00a4b9bbe2
bool first_frame = true;
auto callback = [&](AiksContext& renderer, RenderPass& pass) {
auto callback = [&](AiksContext& renderer, RenderTarget& render_target) {
if (first_frame) {
first_frame = false;
ImGui::SetNextWindowSize({480, 100});
@ -573,14 +690,14 @@ TEST_P(AiksTest, SolidStrokesRenderCorrectly) {
canvas.Translate({-240, 60});
}
return renderer.Render(canvas.EndRecordingAsPicture(), pass);
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}
TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
auto callback = [](AiksContext& renderer, RenderPass& pass) {
auto callback = [](AiksContext& renderer, RenderTarget& render_target) {
Canvas canvas;
Paint alpha;
alpha.color = Color::Red().WithAlpha(0.5);
@ -605,7 +722,7 @@ TEST_P(AiksTest, CoverageOriginShouldBeAccountedForInSubpasses) {
canvas.Restore();
return renderer.Render(canvas.EndRecordingAsPicture(), pass);
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
};
ASSERT_TRUE(OpenPlaygroundHere(callback));

View File

@ -36,14 +36,14 @@ bool DisplayListPlayground::OpenPlaygroundHere(
return false;
}
return Playground::OpenPlaygroundHere(
[&context, &callback](RenderPass& pass) -> bool {
[&context, &callback](RenderTarget& render_target) -> bool {
auto list = callback();
DisplayListDispatcher dispatcher;
list->Dispatch(dispatcher);
auto picture = dispatcher.EndRecordingAsPicture();
return context.Render(picture, pass);
return context.Render(picture, render_target);
});
}

View File

@ -7,6 +7,7 @@
#include <sstream>
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/formats.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/render_target.h"

View File

@ -27,9 +27,4 @@ std::optional<Rect> TextureFilterInput::GetCoverage(
.TransformBounds(GetTransform(entity));
}
Matrix TextureFilterInput::GetLocalTransform(const Entity& entity) const {
// Compute the local transform such that the texture is centered.
return Matrix::MakeTranslation(-Point(texture_->GetSize()) / 2);
}
} // namespace impeller

View File

@ -22,9 +22,6 @@ class TextureFilterInput final : public FilterInput {
// |FilterInput|
std::optional<Rect> GetCoverage(const Entity& entity) const override;
// |FilterInput|
Matrix GetLocalTransform(const Entity& entity) const override;
private:
TextureFilterInput(std::shared_ptr<Texture> texture);

View File

@ -8,6 +8,7 @@
#include "impeller/base/validation.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/renderer/render_pass.h"
namespace impeller {
@ -61,10 +62,6 @@ void Entity::IncrementStencilDepth(uint32_t increment) {
}
void Entity::SetBlendMode(BlendMode blend_mode) {
if (blend_mode_ > BlendMode::kLastPipelineBlendMode) {
VALIDATION_LOG << "Non-pipeline blend modes are not supported by the "
"entity blend mode setting.";
}
blend_mode_ = blend_mode;
}

View File

@ -3,13 +3,18 @@
// found in the LICENSE file.
#include "impeller/entity/entity_pass.h"
#include <variant>
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/validation.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/contents/filters/inputs/filter_input.h"
#include "impeller/entity/contents/texture_contents.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/command_buffer.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/texture.h"
@ -28,6 +33,10 @@ void EntityPass::SetDelegate(std::unique_ptr<EntityPassDelegate> delegate) {
}
void EntityPass::AddEntity(Entity entity) {
if (entity.GetBlendMode() > Entity::BlendMode::kLastPipelineBlendMode) {
contains_advanced_blends_ = true;
}
elements_.emplace_back(std::move(entity));
}
@ -111,39 +120,97 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
FML_DCHECK(pass->superpass_ == nullptr);
pass->superpass_ = this;
auto subpass_pointer = pass.get();
if (pass->blend_mode_ > Entity::BlendMode::kLastPipelineBlendMode) {
contains_advanced_blends_ = true;
}
elements_.emplace_back(std::move(pass));
return subpass_pointer;
}
bool EntityPass::Render(ContentContext& renderer,
RenderPass& parent_pass,
Point position) const {
RenderTarget render_target) const {
if (contains_advanced_blends_) {
auto offscreen_target = RenderTarget::CreateOffscreen(
*renderer.GetContext(), render_target.GetRenderTargetSize());
if (!RenderInternal(renderer, offscreen_target, Point(), 0)) {
return false;
}
auto command_buffer = renderer.GetContext()->CreateRenderCommandBuffer();
auto render_pass = command_buffer->CreateRenderPass(offscreen_target);
{
auto contents = std::make_shared<TextureContents>();
contents->SetTexture(offscreen_target.GetRenderTargetTexture());
Entity entity;
entity.SetContents(contents);
entity.SetBlendMode(Entity::BlendMode::kSource);
entity.Render(renderer, *render_pass);
}
if (!render_pass->EncodeCommands(
renderer.GetContext()->GetTransientsAllocator())) {
return false;
}
if (!command_buffer->SubmitCommands()) {
return false;
}
return true;
}
return RenderInternal(renderer, render_target, Point(), 0);
}
bool EntityPass::RenderInternal(ContentContext& renderer,
RenderTarget render_target,
Point position,
uint32_t depth) const {
TRACE_EVENT0("impeller", "EntityPass::Render");
auto context = renderer.GetContext();
std::shared_ptr<CommandBuffer> command_buffer;
std::shared_ptr<RenderPass> pass;
uint32_t pass_count = 0;
auto end_pass = [&command_buffer, &pass, &context]() {
if (!pass->EncodeCommands(context->GetTransientsAllocator())) {
return false;
}
if (!command_buffer->SubmitCommands()) {
return false;
}
return true;
};
for (const auto& element : elements_) {
Entity element_entity;
// =========================================================================
// Entity rendering ========================================================
// Setup entity element for rendering ======================================
// =========================================================================
if (const auto& entity = std::get_if<Entity>(&element)) {
Entity e = *entity;
element_entity = *entity;
if (!position.IsZero()) {
// If the pass image is going to be rendered with a non-zero position,
// apply the negative translation to entity copies before rendering them
// so that they'll end up rendering to the correct on-screen position.
e.SetTransformation(Matrix::MakeTranslation(Vector3(-position)) *
e.GetTransformation());
element_entity.SetTransformation(
Matrix::MakeTranslation(Vector3(-position)) *
element_entity.GetTransformation());
}
if (!e.Render(renderer, parent_pass)) {
return false;
}
continue;
}
// =========================================================================
// Subpass rendering =======================================================
// Setup subpass element for rendering =====================================
// =========================================================================
if (const auto& subpass_ptr =
std::get_if<std::unique_ptr<EntityPass>>(&element)) {
else if (const auto& subpass_ptr =
std::get_if<std::unique_ptr<EntityPass>>(&element)) {
auto subpass = subpass_ptr->get();
if (subpass->delegate_->CanElide()) {
@ -151,8 +218,9 @@ bool EntityPass::Render(ContentContext& renderer,
}
if (subpass->delegate_->CanCollapseIntoParentPass()) {
// Directly render into the parent pass and move on.
if (!subpass->Render(renderer, parent_pass, position)) {
// Directly render into the parent target and move on.
if (!subpass->RenderInternal(renderer, render_target, position,
depth)) {
return false;
}
continue;
@ -169,8 +237,6 @@ bool EntityPass::Render(ContentContext& renderer,
continue;
}
auto context = renderer.GetContext();
auto subpass_target = RenderTarget::CreateOffscreen(
*context, ISize::Ceil(subpass_coverage->size));
@ -195,56 +261,105 @@ bool EntityPass::Render(ContentContext& renderer,
return false;
}
auto sub_command_buffer = context->CreateRenderCommandBuffer();
if (!sub_command_buffer) {
if (!subpass->RenderInternal(renderer, subpass_target,
subpass_coverage->origin, ++depth)) {
return false;
}
sub_command_buffer->SetLabel("Offscreen Command Buffer");
auto sub_renderpass =
sub_command_buffer->CreateRenderPass(subpass_target);
if (!sub_renderpass) {
return false;
}
sub_renderpass->SetLabel("OffscreenPass");
if (!subpass->Render(renderer, *sub_renderpass,
subpass_coverage->origin)) {
return false;
}
if (!sub_renderpass->EncodeCommands(context->GetTransientsAllocator())) {
return false;
}
if (!sub_command_buffer->SubmitCommands()) {
return false;
}
Entity entity;
entity.SetContents(std::move(offscreen_texture_contents));
entity.SetStencilDepth(stencil_depth_);
entity.SetBlendMode(subpass->blend_mode_);
element_entity.SetContents(std::move(offscreen_texture_contents));
element_entity.SetStencilDepth(stencil_depth_);
element_entity.SetBlendMode(subpass->blend_mode_);
// Once we have filters being applied for SaveLayer, some special sauce
// may be needed here (or in PaintPassDelegate) to ensure the filter
// parameters are transformed by the `xformation_` matrix, while
// continuing to apply only the subpass offset to the offscreen texture.
entity.SetTransformation(Matrix::MakeTranslation(
element_entity.SetTransformation(Matrix::MakeTranslation(
Vector3(subpass_coverage->origin - position)));
if (!entity.Render(renderer, parent_pass)) {
} else {
FML_UNREACHABLE();
}
// =========================================================================
// Render the element ======================================================
// =========================================================================
if (pass && element_entity.GetBlendMode() >
Entity::BlendMode::kLastPipelineBlendMode) {
// End the active pass and flush the buffer before rendering "advanced"
// blends. Advanced blends work by binding the current render target
// texture as an input ("destination"), blending with a second texture
// input ("source"), writing the result to an intermediate texture, and
// finally copying the data from the intermediate texture back to the
// render target texture. And so all of the commands that have written to
// the render target texture so far need to execute before it's bound for
// blending (otherwise the blend pass will end up executing before all the
// previous commands in the active pass).
if (!end_pass()) {
return false;
}
// Resetting these handles triggers a new pass to get created below
pass = nullptr;
command_buffer = nullptr;
// Amend an advanced blend to the contents.
if (render_target.GetColorAttachments().empty()) {
return false;
}
auto color0 = render_target.GetColorAttachments().find(0)->second;
auto input = FilterInput::Make({
element_entity.GetContents(),
color0.resolve_texture ? color0.resolve_texture : color0.texture,
});
element_entity.SetContents(FilterContents::MakeBlend(blend_mode_, input));
element_entity.SetBlendMode(Entity::BlendMode::kSource);
}
// Create a new render pass to render the element if one isn't active.
if (!pass) {
command_buffer = context->CreateRenderCommandBuffer();
if (!command_buffer) {
return false;
}
continue;
command_buffer->SetLabel(
"EntityPass Command Buffer: Depth=" + std::to_string(depth) +
" Count=" + std::to_string(pass_count));
// Never clear the texture for subsequent passes.
if (pass_count > 0) {
if (!render_target.GetColorAttachments().empty()) {
auto color0 = render_target.GetColorAttachments().find(0)->second;
color0.load_action = LoadAction::kLoad;
render_target.SetColorAttachment(color0, 0);
}
if (auto stencil = render_target.GetStencilAttachment();
stencil.has_value()) {
stencil->load_action = LoadAction::kLoad;
render_target.SetStencilAttachment(stencil.value());
}
}
pass = command_buffer->CreateRenderPass(render_target);
if (!pass) {
return false;
}
pass->SetLabel("EntityPass Render Pass: Depth=" + std::to_string(depth) +
" Count=" + std::to_string(pass_count));
++pass_count;
}
FML_UNREACHABLE();
if (!element_entity.Render(renderer, *pass)) {
return false;
}
}
if (pass) {
return end_pass();
}
return true;
}

View File

@ -43,9 +43,7 @@ class EntityPass {
EntityPass* GetSuperpass() const;
bool Render(ContentContext& renderer,
RenderPass& parent_pass,
Point position = Point()) const;
bool Render(ContentContext& renderer, RenderTarget render_target) const;
void IterateAllEntities(std::function<bool(Entity&)> iterator);
@ -60,12 +58,18 @@ class EntityPass {
std::optional<Rect> GetElementsCoverage() const;
private:
bool RenderInternal(ContentContext& renderer,
RenderTarget render_target,
Point position,
uint32_t depth) const;
std::vector<Element> elements_;
EntityPass* superpass_ = nullptr;
Matrix xformation_;
size_t stencil_depth_ = 0u;
Entity::BlendMode blend_mode_ = Entity::BlendMode::kSourceOver;
bool contains_advanced_blends_ = false;
std::unique_ptr<EntityPassDelegate> delegate_ =
EntityPassDelegate::MakeDefault();
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_ =

View File

@ -21,7 +21,7 @@ bool EntityPlayground::OpenPlaygroundHere(Entity entity) {
if (!content_context.IsValid()) {
return false;
}
Renderer::RenderCallback callback = [&](RenderPass& pass) -> bool {
SinglePassCallback callback = [&](RenderPass& pass) -> bool {
return entity.Render(content_context, pass);
};
return Playground::OpenPlaygroundHere(callback);
@ -36,10 +36,10 @@ bool EntityPlayground::OpenPlaygroundHere(EntityPlaygroundCallback callback) {
if (!content_context.IsValid()) {
return false;
}
Renderer::RenderCallback render_callback = [&](RenderPass& pass) -> bool {
SinglePassCallback pass_callback = [&](RenderPass& pass) -> bool {
return callback(content_context, pass);
};
return Playground::OpenPlaygroundHere(render_callback);
return Playground::OpenPlaygroundHere(pass_callback);
}
} // namespace impeller

View File

@ -817,14 +817,16 @@ TEST_P(EntityTest, GaussianBlurFilter) {
blur_styles[selected_blur_style]);
auto mask_blur = FilterContents::MakeBorderMaskBlur(
FilterInput::Make(boston), FilterContents::Sigma{blur_amount[0]},
FilterInput::Make(bridge), FilterContents::Sigma{blur_amount[0]},
FilterContents::Sigma{blur_amount[1]},
blur_styles[selected_blur_style]);
auto input_size = bridge->GetSize();
auto ctm = Matrix::MakeTranslation(Vector3(offset[0], offset[1])) *
Matrix::MakeRotationZ(Radians(rotation)) *
Matrix::MakeScale(Vector2(scale[0], scale[1])) *
Matrix::MakeSkew(skew[0], skew[1]);
Matrix::MakeSkew(skew[0], skew[1]) *
Matrix::MakeTranslation(-Point(input_size) / 2);
auto target_contents = selected_blur_type == 0 ? blur : mask_blur;
@ -838,10 +840,7 @@ TEST_P(EntityTest, GaussianBlurFilter) {
// unfiltered input.
Entity cover_entity;
cover_entity.SetContents(SolidColorContents::Make(
PathBuilder{}
.AddRect(
Rect(-Point(bridge->GetSize()) / 2, Size(bridge->GetSize())))
.TakePath(),
PathBuilder{}.AddRect(Rect::MakeSize(Size(input_size))).TakePath(),
cover_color));
cover_entity.SetTransformation(ctm);

View File

@ -18,8 +18,17 @@ vec4 SampleWithBorder(sampler2D tex, vec2 uv) {
return vec4(0);
}
void main() {
vec4 dst = SampleWithBorder(texture_sampler_dst, v_dst_texture_coords);
vec4 src = SampleWithBorder(texture_sampler_src, v_src_texture_coords);
frag_color = src + dst - src * dst;
vec4 Unpremultiply(vec4 color) {
if (color.a == 0) {
return vec4(0);
}
return vec4(color.rgb / color.a, color.a);
}
void main() {
vec4 dst = texture(texture_sampler_dst, v_dst_texture_coords);
vec4 d = Unpremultiply(dst);
vec4 src = SampleWithBorder(texture_sampler_src, v_src_texture_coords);
vec4 s = Unpremultiply(src);
frag_color = 1 - ((1 - s) * (1 - d));
}

View File

@ -46,6 +46,9 @@ constexpr float kSqrt2 = 1.41421356237309504880;
// 1/sqrt(2)
constexpr float k1OverSqrt2 = 0.70710678118654752440;
// phi
constexpr float kPhi = 1.61803398874989484820;
// 0.001
constexpr float kEhCloseEnough = 1e-3;

View File

@ -2,8 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <sstream>
#include "impeller/renderer/command_buffer.h"
#define GLFW_INCLUDE_NONE
#import "third_party/glfw/include/GLFW/glfw3.h"
@ -211,13 +214,41 @@ bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) {
ImGui_ImplGlfw_NewFrame();
Renderer::RenderCallback wrapped_callback = [render_callback](auto& pass) {
pass.SetLabel("Playground Main Render Pass");
Renderer::RenderCallback wrapped_callback =
[render_callback,
&renderer = renderer_](RenderTarget& render_target) -> bool {
ImGui::NewFrame();
bool result = render_callback(pass);
bool result = render_callback(render_target);
ImGui::Render();
ImGui_ImplImpeller_RenderDrawData(ImGui::GetDrawData(), pass);
// Render ImGui overlay.
{
auto buffer = renderer->GetContext()->CreateRenderCommandBuffer();
if (!buffer) {
return false;
}
buffer->SetLabel("ImGui Command Buffer");
if (render_target.GetColorAttachments().empty()) {
return false;
}
auto color0 = render_target.GetColorAttachments().find(0)->second;
color0.load_action = LoadAction::kLoad;
render_target.SetColorAttachment(color0, 0);
auto pass = buffer->CreateRenderPass(render_target);
if (!pass) {
return false;
}
pass->SetLabel("ImGui Render Pass");
ImGui_ImplImpeller_RenderDrawData(ImGui::GetDrawData(), *pass);
pass->EncodeCommands(renderer->GetContext()->GetTransientsAllocator());
if (!buffer->SubmitCommands()) {
return false;
}
}
return result;
};
@ -233,6 +264,33 @@ bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) {
return true;
}
bool Playground::OpenPlaygroundHere(SinglePassCallback pass_callback) {
return OpenPlaygroundHere(
[context = GetContext(), &pass_callback](RenderTarget& render_target) {
auto buffer = context->CreateRenderCommandBuffer();
if (!buffer) {
return false;
}
buffer->SetLabel("Playground Command Buffer");
auto pass = buffer->CreateRenderPass(render_target);
if (!pass) {
return false;
}
pass->SetLabel("Playground Render Pass");
if (!pass_callback(*pass)) {
return false;
}
pass->EncodeCommands(context->GetTransientsAllocator());
if (!buffer->SubmitCommands()) {
return false;
}
return true;
});
}
std::shared_ptr<Texture> Playground::CreateTextureForFixture(
const char* fixture_name) const {
if (!renderer_) {

View File

@ -4,6 +4,8 @@
#pragma once
#include <memory>
#include "flutter/fml/closure.h"
#include "flutter/fml/macros.h"
#include "gtest/gtest.h"
@ -24,6 +26,8 @@ std::string PlaygroundBackendToString(PlaygroundBackend backend);
class Playground : public ::testing::TestWithParam<PlaygroundBackend> {
public:
using SinglePassCallback = std::function<bool(RenderPass& pass)>;
Playground();
~Playground();
@ -44,6 +48,8 @@ class Playground : public ::testing::TestWithParam<PlaygroundBackend> {
bool OpenPlaygroundHere(Renderer::RenderCallback render_callback);
bool OpenPlaygroundHere(SinglePassCallback pass_callback);
std::shared_ptr<Texture> CreateTextureForFixture(
const char* fixture_name) const;

View File

@ -43,27 +43,9 @@ bool Renderer::Render(std::unique_ptr<Surface> surface,
return false;
}
auto command_buffer = context_->CreateRenderCommandBuffer();
RenderTarget render_target = surface->GetTargetRenderPassDescriptor();
if (!command_buffer) {
return false;
}
command_buffer->SetLabel("Onscreen Command Buffer");
auto render_pass = command_buffer->CreateRenderPass(
surface->GetTargetRenderPassDescriptor());
if (!render_pass) {
return false;
}
render_pass->SetLabel("Onscreen Render Pass");
if (render_callback && !render_callback(*render_pass)) {
return false;
}
if (!render_pass->EncodeCommands(GetContext()->GetTransientsAllocator())) {
if (render_callback && !render_callback(render_target)) {
return false;
}
@ -71,16 +53,7 @@ bool Renderer::Render(std::unique_ptr<Surface> surface,
return false;
}
if (!command_buffer->SubmitCommands(
[sema = frames_in_flight_sema_](CommandBuffer::Status result) {
sema->Signal();
if (result != CommandBuffer::Status::kCompleted) {
VALIDATION_LOG << "Could not commit command buffer.";
}
})) {
return false;
}
frames_in_flight_sema_->Signal();
return surface->Present();
}

View File

@ -11,6 +11,7 @@
#include "flutter/fml/synchronization/semaphore.h"
#include "impeller/geometry/size.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/render_target.h"
namespace impeller {
@ -21,7 +22,7 @@ class Renderer {
public:
static constexpr size_t kDefaultMaxFramesInFlight = 3u;
using RenderCallback = std::function<bool(RenderPass& pass)>;
using RenderCallback = std::function<bool(RenderTarget& render_target)>;
Renderer(std::shared_ptr<Context> context,
size_t max_frames_in_flight = kDefaultMaxFramesInFlight);

View File

@ -65,7 +65,7 @@ TEST_P(RendererTest, CanCreateBoxPrimitive) {
ASSERT_TRUE(bridge && boston);
auto sampler = context->GetSamplerLibrary()->GetSampler({});
ASSERT_TRUE(sampler);
Renderer::RenderCallback callback = [&](RenderPass& pass) {
SinglePassCallback callback = [&](RenderPass& pass) {
Command cmd;
cmd.label = "Box";
cmd.pipeline = box_pipeline;
@ -131,7 +131,7 @@ TEST_P(RendererTest, CanRenderMultiplePrimitives) {
auto sampler = context->GetSamplerLibrary()->GetSampler({});
ASSERT_TRUE(sampler);
Renderer::RenderCallback callback = [&](RenderPass& pass) {
SinglePassCallback callback = [&](RenderPass& pass) {
Command cmd;
cmd.label = "Box";
cmd.pipeline = box_pipeline;

View File

@ -40,7 +40,7 @@ TEST_P(TypographerTest, CanCreateGlyphAtlas) {
ASSERT_TRUE(blob);
auto atlas = context->CreateGlyphAtlas(TextFrameFromTextBlob(blob));
ASSERT_NE(atlas, nullptr);
OpenPlaygroundHere([](auto&) { return true; });
OpenPlaygroundHere([](RenderTarget&) { return true; });
}
} // namespace testing

View File

@ -77,11 +77,12 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetalImpeller::AcquireFrame(const SkISiz
display_list->Dispatch(impeller_dispatcher);
auto picture = impeller_dispatcher.EndRecordingAsPicture();
return renderer->Render(std::move(surface),
fml::MakeCopyable([aiks_context, picture = std::move(picture)](
impeller::RenderPass& pass) -> bool {
return aiks_context->Render(picture, pass);
}));
return renderer->Render(
std::move(surface),
fml::MakeCopyable([aiks_context, picture = std::move(picture)](
impeller::RenderTarget& render_target) -> bool {
return aiks_context->Render(picture, render_target);
}));
});
return std::make_unique<SurfaceFrame>(nullptr, // surface