mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Change Renderer utility to pass RenderTarget to callback; render non-pipeline blend modes (flutter/engine#32982)
This commit is contained in:
parent
98a3d5b7a8
commit
08295d23f7
@ -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;
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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", ¤t_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));
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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_ =
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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_) {
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user