mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Reland: Remove Entity capture/AiksInspector. (flutter/engine#52932)
Resolves https://github.com/flutter/flutter/issues/134748.
This was a really fun experiment. I learned a lot from it, and it
genuinely helped me solve some coverage-related problems, but the
reality is it was too little too late -- by the time we had this
capture system, we had already solved most of the problems that would
have benefitted from this.
It's been a few months since I've used or extended the capabilities of
this capture system for something, and I don't have the spare
time/energy to give it the love it needs to realize the vision I had
for it. I still almost exclusively use a combination of native frame
captures and print debugging to solve problems.
RIP in peace.
This reverts commit 104eb98e62. (https://github.com/flutter/engine/pull/52680)
This commit is contained in:
parent
a554688620
commit
2ffd763cfb
@ -42255,8 +42255,6 @@ ORIGIN: ../../../flutter/impeller/aiks/aiks_context.cc + ../../../flutter/LICENS
|
||||
ORIGIN: ../../../flutter/impeller/aiks/aiks_context.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/aiks/aiks_playground.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/aiks/aiks_playground.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/aiks/aiks_playground_inspector.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/aiks/aiks_playground_inspector.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/aiks/canvas.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/aiks/canvas.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/aiks/canvas_benchmarks.cc + ../../../flutter/LICENSE
|
||||
@ -42354,8 +42352,6 @@ ORIGIN: ../../../flutter/impeller/core/allocator.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/core/allocator.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/core/buffer_view.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/core/buffer_view.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/core/capture.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/core/capture.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/core/device_buffer.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/core/device_buffer.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/core/device_buffer_descriptor.cc + ../../../flutter/LICENSE
|
||||
@ -45126,8 +45122,6 @@ FILE: ../../../flutter/impeller/aiks/aiks_context.cc
|
||||
FILE: ../../../flutter/impeller/aiks/aiks_context.h
|
||||
FILE: ../../../flutter/impeller/aiks/aiks_playground.cc
|
||||
FILE: ../../../flutter/impeller/aiks/aiks_playground.h
|
||||
FILE: ../../../flutter/impeller/aiks/aiks_playground_inspector.cc
|
||||
FILE: ../../../flutter/impeller/aiks/aiks_playground_inspector.h
|
||||
FILE: ../../../flutter/impeller/aiks/canvas.cc
|
||||
FILE: ../../../flutter/impeller/aiks/canvas.h
|
||||
FILE: ../../../flutter/impeller/aiks/canvas_benchmarks.cc
|
||||
@ -45225,8 +45219,6 @@ FILE: ../../../flutter/impeller/core/allocator.cc
|
||||
FILE: ../../../flutter/impeller/core/allocator.h
|
||||
FILE: ../../../flutter/impeller/core/buffer_view.cc
|
||||
FILE: ../../../flutter/impeller/core/buffer_view.h
|
||||
FILE: ../../../flutter/impeller/core/capture.cc
|
||||
FILE: ../../../flutter/impeller/core/capture.h
|
||||
FILE: ../../../flutter/impeller/core/device_buffer.cc
|
||||
FILE: ../../../flutter/impeller/core/device_buffer.h
|
||||
FILE: ../../../flutter/impeller/core/device_buffer_descriptor.cc
|
||||
|
||||
@ -14,10 +14,6 @@ config("impeller_public_config") {
|
||||
defines += [ "IMPELLER_DEBUG=1" ]
|
||||
}
|
||||
|
||||
if (impeller_capture) {
|
||||
defines += [ "IMPELLER_ENABLE_CAPTURE=1" ]
|
||||
}
|
||||
|
||||
if (impeller_supports_rendering) {
|
||||
defines += [ "IMPELLER_SUPPORTS_RENDERING=1" ]
|
||||
}
|
||||
|
||||
@ -45,8 +45,6 @@ impeller_component("aiks_playground") {
|
||||
sources = [
|
||||
"aiks_playground.cc",
|
||||
"aiks_playground.h",
|
||||
"aiks_playground_inspector.cc",
|
||||
"aiks_playground_inspector.h",
|
||||
]
|
||||
deps = [
|
||||
":aiks",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "impeller/aiks/aiks_playground.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "impeller/aiks/aiks_context.h"
|
||||
#include "impeller/display_list/dl_dispatcher.h"
|
||||
@ -24,14 +25,24 @@ void AiksPlayground::SetTypographerContext(
|
||||
}
|
||||
|
||||
void AiksPlayground::TearDown() {
|
||||
inspector_.HackResetDueToTextureLeaks();
|
||||
PlaygroundTest::TearDown();
|
||||
}
|
||||
|
||||
bool AiksPlayground::OpenPlaygroundHere(Picture picture) {
|
||||
return OpenPlaygroundHere([&picture](AiksContext& renderer) -> Picture {
|
||||
return std::move(picture);
|
||||
});
|
||||
if (!switches_.enable_playground) {
|
||||
return true;
|
||||
}
|
||||
|
||||
AiksContext renderer(GetContext(), typographer_context_);
|
||||
|
||||
if (!renderer.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Playground::OpenPlaygroundHere(
|
||||
[&renderer, &picture](RenderTarget& render_target) -> bool {
|
||||
return renderer.Render(picture, render_target, true);
|
||||
});
|
||||
}
|
||||
|
||||
bool AiksPlayground::OpenPlaygroundHere(AiksPlaygroundCallback callback) {
|
||||
@ -46,10 +57,8 @@ bool AiksPlayground::OpenPlaygroundHere(AiksPlaygroundCallback callback) {
|
||||
}
|
||||
|
||||
return Playground::OpenPlaygroundHere(
|
||||
[this, &renderer, &callback](RenderTarget& render_target) -> bool {
|
||||
const std::optional<Picture>& picture = inspector_.RenderInspector(
|
||||
renderer, [&]() { return callback(renderer); });
|
||||
|
||||
[&renderer, &callback](RenderTarget& render_target) -> bool {
|
||||
std::optional<Picture> picture = callback(renderer);
|
||||
if (!picture.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
|
||||
#include "flutter/display_list/display_list.h"
|
||||
#include "impeller/aiks/aiks_context.h"
|
||||
#include "impeller/aiks/aiks_playground_inspector.h"
|
||||
#include "impeller/aiks/picture.h"
|
||||
#include "impeller/playground/playground_test.h"
|
||||
#include "impeller/typographer/typographer_context.h"
|
||||
@ -41,7 +40,6 @@ class AiksPlayground : public PlaygroundTest {
|
||||
|
||||
private:
|
||||
std::shared_ptr<TypographerContext> typographer_context_;
|
||||
AiksInspector inspector_;
|
||||
|
||||
AiksPlayground(const AiksPlayground&) = delete;
|
||||
|
||||
|
||||
@ -1,277 +0,0 @@
|
||||
// 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 "impeller/aiks/aiks_playground_inspector.h"
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
#include "impeller/core/capture.h"
|
||||
#include "impeller/entity/entity_pass.h"
|
||||
#include "impeller/renderer/context.h"
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "third_party/imgui/imgui_internal.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
static const char* kElementsWindowName = "Elements";
|
||||
static const char* kPropertiesWindowName = "Properties";
|
||||
|
||||
static const std::initializer_list<std::string> kSupportedDocuments = {
|
||||
EntityPass::kCaptureDocumentName};
|
||||
|
||||
AiksInspector::AiksInspector() = default;
|
||||
|
||||
const std::optional<Picture>& AiksInspector::RenderInspector(
|
||||
AiksContext& aiks_context,
|
||||
const std::function<std::optional<Picture>()>& picture_callback) {
|
||||
//----------------------------------------------------------------------------
|
||||
/// Configure the next frame.
|
||||
///
|
||||
|
||||
RenderCapture(aiks_context.GetContext()->capture);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Configure the next frame.
|
||||
///
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
|
||||
wireframe_ = !wireframe_;
|
||||
aiks_context.GetContentContext().SetWireframe(wireframe_);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_C)) {
|
||||
capturing_ = !capturing_;
|
||||
if (capturing_) {
|
||||
aiks_context.GetContext()->capture =
|
||||
CaptureContext::MakeAllowlist({kSupportedDocuments});
|
||||
}
|
||||
}
|
||||
if (!capturing_) {
|
||||
hovered_element_ = nullptr;
|
||||
selected_element_ = nullptr;
|
||||
aiks_context.GetContext()->capture = CaptureContext::MakeInactive();
|
||||
std::optional<Picture> new_picture = picture_callback();
|
||||
|
||||
// If the new picture doesn't have a pass, that means it was already moved
|
||||
// into the inspector. Simply re-emit the last received valid picture.
|
||||
if (!new_picture.has_value() || new_picture->pass) {
|
||||
last_picture_ = std::move(new_picture);
|
||||
}
|
||||
}
|
||||
|
||||
return last_picture_;
|
||||
}
|
||||
|
||||
void AiksInspector::HackResetDueToTextureLeaks() {
|
||||
last_picture_.reset();
|
||||
}
|
||||
|
||||
static const auto kPropertiesProcTable = CaptureProcTable{
|
||||
.boolean =
|
||||
[](CaptureBooleanProperty& p) {
|
||||
ImGui::Checkbox(p.label.c_str(), &p.value);
|
||||
},
|
||||
.integer =
|
||||
[](CaptureIntegerProperty& p) {
|
||||
if (p.options.range.has_value()) {
|
||||
ImGui::SliderInt(p.label.c_str(), &p.value,
|
||||
static_cast<int>(p.options.range->min),
|
||||
static_cast<int>(p.options.range->max));
|
||||
return;
|
||||
}
|
||||
ImGui::InputInt(p.label.c_str(), &p.value);
|
||||
},
|
||||
.scalar =
|
||||
[](CaptureScalarProperty& p) {
|
||||
if (p.options.range.has_value()) {
|
||||
ImGui::SliderFloat(p.label.c_str(), &p.value, p.options.range->min,
|
||||
p.options.range->max);
|
||||
return;
|
||||
}
|
||||
ImGui::DragFloat(p.label.c_str(), &p.value, 0.01);
|
||||
},
|
||||
.point =
|
||||
[](CapturePointProperty& p) {
|
||||
if (p.options.range.has_value()) {
|
||||
ImGui::SliderFloat2(p.label.c_str(),
|
||||
reinterpret_cast<float*>(&p.value),
|
||||
p.options.range->min, p.options.range->max);
|
||||
return;
|
||||
}
|
||||
ImGui::DragFloat2(p.label.c_str(), reinterpret_cast<float*>(&p.value),
|
||||
0.01);
|
||||
},
|
||||
.vector3 =
|
||||
[](CaptureVector3Property& p) {
|
||||
if (p.options.range.has_value()) {
|
||||
ImGui::SliderFloat3(p.label.c_str(),
|
||||
reinterpret_cast<float*>(&p.value),
|
||||
p.options.range->min, p.options.range->max);
|
||||
return;
|
||||
}
|
||||
ImGui::DragFloat3(p.label.c_str(), reinterpret_cast<float*>(&p.value),
|
||||
0.01);
|
||||
},
|
||||
.rect =
|
||||
[](CaptureRectProperty& p) {
|
||||
ImGui::DragFloat4(p.label.c_str(), reinterpret_cast<float*>(&p.value),
|
||||
0.01);
|
||||
},
|
||||
.color =
|
||||
[](CaptureColorProperty& p) {
|
||||
ImGui::ColorEdit4(p.label.c_str(),
|
||||
reinterpret_cast<float*>(&p.value));
|
||||
},
|
||||
.matrix =
|
||||
[](CaptureMatrixProperty& p) {
|
||||
float* pointer = reinterpret_cast<float*>(&p.value);
|
||||
ImGui::DragFloat4((p.label + " X basis").c_str(), pointer, 0.001);
|
||||
ImGui::DragFloat4((p.label + " Y basis").c_str(), pointer + 4, 0.001);
|
||||
ImGui::DragFloat4((p.label + " Z basis").c_str(), pointer + 8, 0.001);
|
||||
ImGui::DragFloat4((p.label + " Translation").c_str(), pointer + 12,
|
||||
0.001);
|
||||
},
|
||||
.string =
|
||||
[](CaptureStringProperty& p) {
|
||||
ImGui::InputTextEx(p.label.c_str(), "",
|
||||
// Fine as long as it's read-only.
|
||||
const_cast<char*>(p.value.c_str()), p.value.size(),
|
||||
ImVec2(0, 0), ImGuiInputTextFlags_ReadOnly);
|
||||
},
|
||||
};
|
||||
|
||||
void AiksInspector::RenderCapture(CaptureContext& capture_context) {
|
||||
if (!capturing_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto document = capture_context.GetDocument(EntityPass::kCaptureDocumentName);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Setup a shared dockspace to collect the capture windows.
|
||||
///
|
||||
|
||||
ImGui::SetNextWindowBgAlpha(0.5);
|
||||
ImGui::Begin("Capture");
|
||||
auto dockspace_id = ImGui::GetID("CaptureDockspace");
|
||||
if (!ImGui::DockBuilderGetNode(dockspace_id)) {
|
||||
ImGui::SetWindowSize(ImVec2(370, 680));
|
||||
ImGui::SetWindowPos(ImVec2(640, 55));
|
||||
|
||||
ImGui::DockBuilderRemoveNode(dockspace_id);
|
||||
ImGui::DockBuilderAddNode(dockspace_id);
|
||||
|
||||
ImGuiID opposite_id;
|
||||
ImGuiID up_id = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Up, 0.6,
|
||||
nullptr, &opposite_id);
|
||||
ImGuiID down_id = ImGui::DockBuilderSplitNode(opposite_id, ImGuiDir_Down,
|
||||
0.0, nullptr, nullptr);
|
||||
ImGui::DockBuilderDockWindow(kElementsWindowName, up_id);
|
||||
ImGui::DockBuilderDockWindow(kPropertiesWindowName, down_id);
|
||||
|
||||
ImGui::DockBuilderFinish(dockspace_id);
|
||||
}
|
||||
ImGui::DockSpace(dockspace_id);
|
||||
ImGui::End(); // Capture window.
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Element hierarchy window.
|
||||
///
|
||||
|
||||
ImGui::Begin(kElementsWindowName);
|
||||
auto root_element = document.GetElement();
|
||||
hovered_element_ = nullptr;
|
||||
if (root_element) {
|
||||
RenderCaptureElement(*root_element);
|
||||
}
|
||||
ImGui::End(); // Hierarchy window.
|
||||
|
||||
if (selected_element_) {
|
||||
//----------------------------------------------------------------------------
|
||||
/// Properties window.
|
||||
///
|
||||
|
||||
ImGui::Begin(kPropertiesWindowName);
|
||||
{
|
||||
selected_element_->properties.Iterate([&](CaptureProperty& property) {
|
||||
property.Invoke(kPropertiesProcTable);
|
||||
});
|
||||
}
|
||||
ImGui::End(); // Inspector window.
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Selected coverage highlighting.
|
||||
///
|
||||
|
||||
auto coverage_property =
|
||||
selected_element_->properties.FindFirstByLabel("Coverage");
|
||||
if (coverage_property) {
|
||||
auto coverage = coverage_property->AsRect();
|
||||
if (coverage.has_value()) {
|
||||
Scalar scale = ImGui::GetWindowDpiScale();
|
||||
ImGui::GetBackgroundDrawList()->AddRect(
|
||||
ImVec2(coverage->GetLeft() / scale,
|
||||
coverage->GetTop() / scale), // p_min
|
||||
ImVec2(coverage->GetRight() / scale,
|
||||
coverage->GetBottom() / scale), // p_max
|
||||
0x992222FF, // col
|
||||
0.0, // rounding
|
||||
ImDrawFlags_None, // flags
|
||||
8.0); // thickness
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Hover coverage highlight.
|
||||
///
|
||||
|
||||
if (hovered_element_) {
|
||||
auto coverage_property =
|
||||
hovered_element_->properties.FindFirstByLabel("Coverage");
|
||||
if (coverage_property) {
|
||||
auto coverage = coverage_property->AsRect();
|
||||
if (coverage.has_value()) {
|
||||
Scalar scale = ImGui::GetWindowDpiScale();
|
||||
ImGui::GetBackgroundDrawList()->AddRect(
|
||||
ImVec2(coverage->GetLeft() / scale,
|
||||
coverage->GetTop() / scale), // p_min
|
||||
ImVec2(coverage->GetRight() / scale,
|
||||
coverage->GetBottom() / scale), // p_max
|
||||
0x66FF2222, // col
|
||||
0.0, // rounding
|
||||
ImDrawFlags_None, // flags
|
||||
8.0); // thickness
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AiksInspector::RenderCaptureElement(CaptureElement& element) {
|
||||
ImGui::PushID(&element);
|
||||
|
||||
bool is_selected = selected_element_ == &element;
|
||||
bool has_children = element.children.Count() > 0;
|
||||
|
||||
bool opened = ImGui::TreeNodeEx(
|
||||
element.label.c_str(), (is_selected ? ImGuiTreeNodeFlags_Selected : 0) |
|
||||
(has_children ? 0 : ImGuiTreeNodeFlags_Leaf) |
|
||||
ImGuiTreeNodeFlags_SpanFullWidth |
|
||||
ImGuiTreeNodeFlags_OpenOnArrow |
|
||||
ImGuiTreeNodeFlags_DefaultOpen);
|
||||
if (ImGui::IsItemClicked()) {
|
||||
selected_element_ = &element;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
hovered_element_ = &element;
|
||||
}
|
||||
if (opened) {
|
||||
element.children.Iterate(
|
||||
[&](CaptureElement& child) { RenderCaptureElement(child); });
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
@ -1,55 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_IMPELLER_AIKS_AIKS_PLAYGROUND_INSPECTOR_H_
|
||||
#define FLUTTER_IMPELLER_AIKS_AIKS_PLAYGROUND_INSPECTOR_H_
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "impeller/aiks/aiks_context.h"
|
||||
#include "impeller/aiks/picture.h"
|
||||
#include "impeller/core/capture.h"
|
||||
#include "impeller/renderer/context.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class AiksInspector {
|
||||
public:
|
||||
AiksInspector();
|
||||
|
||||
const std::optional<Picture>& RenderInspector(
|
||||
AiksContext& aiks_context,
|
||||
const std::function<std::optional<Picture>()>& picture_callback);
|
||||
|
||||
// Resets (releases) the underlying |Picture| object.
|
||||
//
|
||||
// Underlying issue: <https://github.com/flutter/flutter/issues/134678>.
|
||||
//
|
||||
// The tear-down code is not running in the right order; we still have a
|
||||
// reference to the |Picture| object when the |Context| is being destroyed,
|
||||
// which causes the |Texture| objects to leak.
|
||||
//
|
||||
// TODO(matanlurey): https://github.com/flutter/flutter/issues/134748.
|
||||
void HackResetDueToTextureLeaks();
|
||||
|
||||
private:
|
||||
void RenderCapture(CaptureContext& capture_context);
|
||||
void RenderCaptureElement(CaptureElement& element);
|
||||
|
||||
bool capturing_ = false;
|
||||
bool wireframe_ = false;
|
||||
CaptureElement* hovered_element_ = nullptr;
|
||||
CaptureElement* selected_element_ = nullptr;
|
||||
std::optional<Picture> last_picture_;
|
||||
|
||||
AiksInspector(const AiksInspector&) = delete;
|
||||
|
||||
AiksInspector& operator=(const AiksInspector&) = delete;
|
||||
};
|
||||
|
||||
}; // namespace impeller
|
||||
|
||||
#endif // FLUTTER_IMPELLER_AIKS_AIKS_PLAYGROUND_INSPECTOR_H_
|
||||
@ -20,7 +20,6 @@
|
||||
#include "impeller/aiks/image_filter.h"
|
||||
#include "impeller/aiks/paint_pass_delegate.h"
|
||||
#include "impeller/aiks/testing/context_spy.h"
|
||||
#include "impeller/core/capture.h"
|
||||
#include "impeller/core/device_buffer.h"
|
||||
#include "impeller/entity/contents/solid_color_contents.h"
|
||||
#include "impeller/geometry/color.h"
|
||||
@ -2596,38 +2595,6 @@ TEST_P(AiksTest, PipelineBlendSingleParameter) {
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CaptureContext) {
|
||||
auto capture_context = CaptureContext::MakeAllowlist({"TestDocument"});
|
||||
|
||||
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
|
||||
Canvas canvas;
|
||||
|
||||
capture_context.Rewind();
|
||||
auto document = capture_context.GetDocument("TestDocument");
|
||||
|
||||
auto color = document.AddColor("Background color", Color::CornflowerBlue());
|
||||
canvas.DrawPaint({.color = color});
|
||||
|
||||
if (AiksTest::ImGuiBegin("TestDocument", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
document.GetElement()->properties.Iterate([](CaptureProperty& property) {
|
||||
property.Invoke({.color = [](CaptureColorProperty& p) {
|
||||
ImGui::ColorEdit4(p.label.c_str(),
|
||||
reinterpret_cast<float*>(&p.value));
|
||||
}});
|
||||
});
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
return canvas.EndRecordingAsPicture();
|
||||
};
|
||||
OpenPlaygroundHere(callback);
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CaptureInactivatedByDefault) {
|
||||
ASSERT_FALSE(GetContext()->capture.IsActive());
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/134678.
|
||||
TEST_P(AiksTest, ReleasesTextureOnTeardown) {
|
||||
auto context = MakeContext();
|
||||
|
||||
@ -10,8 +10,6 @@ impeller_component("core") {
|
||||
"allocator.h",
|
||||
"buffer_view.cc",
|
||||
"buffer_view.h",
|
||||
"capture.cc",
|
||||
"capture.h",
|
||||
"device_buffer.cc",
|
||||
"device_buffer.h",
|
||||
"device_buffer_descriptor.cc",
|
||||
|
||||
@ -1,220 +0,0 @@
|
||||
// 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 "impeller/core/capture.h"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
|
||||
namespace impeller {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// CaptureProperty
|
||||
///
|
||||
|
||||
CaptureProperty::CaptureProperty(const std::string& label, Options options)
|
||||
: CaptureCursorListElement(label), options(options) {}
|
||||
|
||||
CaptureProperty::~CaptureProperty() = default;
|
||||
|
||||
bool CaptureProperty::MatchesCloselyEnough(const CaptureProperty& other) const {
|
||||
if (label != other.label) {
|
||||
return false;
|
||||
}
|
||||
if (GetType() != other.GetType()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define _CAPTURE_PROPERTY_CAST_DEFINITION(type_name, pascal_name, lower_name) \
|
||||
std::optional<type_name> CaptureProperty::As##pascal_name() const { \
|
||||
if (GetType() != Type::k##pascal_name) { \
|
||||
return std::nullopt; \
|
||||
} \
|
||||
return reinterpret_cast<const Capture##pascal_name##Property*>(this) \
|
||||
->value; \
|
||||
}
|
||||
|
||||
_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_CAST_DEFINITION);
|
||||
|
||||
#define _CAPTURE_PROPERTY_DEFINITION(type_name, pascal_name, lower_name) \
|
||||
Capture##pascal_name##Property::Capture##pascal_name##Property( \
|
||||
const std::string& label, type_name value, Options options) \
|
||||
: CaptureProperty(label, options), value(std::move(value)) {} \
|
||||
\
|
||||
std::shared_ptr<Capture##pascal_name##Property> \
|
||||
Capture##pascal_name##Property::Make(const std::string& label, \
|
||||
type_name value, Options options) { \
|
||||
auto result = std::shared_ptr<Capture##pascal_name##Property>( \
|
||||
new Capture##pascal_name##Property(label, std::move(value), options)); \
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
CaptureProperty::Type Capture##pascal_name##Property::GetType() const { \
|
||||
return Type::k##pascal_name; \
|
||||
} \
|
||||
\
|
||||
void Capture##pascal_name##Property::Invoke( \
|
||||
const CaptureProcTable& proc_table) { \
|
||||
proc_table.lower_name(*this); \
|
||||
}
|
||||
|
||||
_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_DEFINITION);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// CaptureElement
|
||||
///
|
||||
|
||||
CaptureElement::CaptureElement(const std::string& label)
|
||||
: CaptureCursorListElement(label) {}
|
||||
|
||||
std::shared_ptr<CaptureElement> CaptureElement::Make(const std::string& label) {
|
||||
return std::shared_ptr<CaptureElement>(new CaptureElement(label));
|
||||
}
|
||||
|
||||
void CaptureElement::Rewind() {
|
||||
properties.Rewind();
|
||||
children.Rewind();
|
||||
}
|
||||
|
||||
bool CaptureElement::MatchesCloselyEnough(const CaptureElement& other) const {
|
||||
return label == other.label;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Capture
|
||||
///
|
||||
|
||||
Capture::Capture() = default;
|
||||
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
Capture::Capture(const std::string& label)
|
||||
: element_(CaptureElement::Make(label)), active_(true) {
|
||||
element_->label = label;
|
||||
}
|
||||
#else
|
||||
Capture::Capture(const std::string& label) {}
|
||||
#endif
|
||||
|
||||
Capture Capture::MakeInactive() {
|
||||
return Capture();
|
||||
}
|
||||
|
||||
std::shared_ptr<CaptureElement> Capture::GetElement() const {
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
return element_;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Capture::Rewind() {
|
||||
return GetElement()->Rewind();
|
||||
}
|
||||
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
#define _CAPTURE_PROPERTY_RECORDER_DEFINITION(type_name, pascal_name, \
|
||||
lower_name) \
|
||||
type_name Capture::Add##pascal_name(std::string_view label, type_name value, \
|
||||
CaptureProperty::Options options) { \
|
||||
if (!active_) { \
|
||||
return value; \
|
||||
} \
|
||||
FML_DCHECK(element_ != nullptr); \
|
||||
\
|
||||
std::string label_clone = std::string(label); \
|
||||
auto new_value = Capture##pascal_name##Property::Make( \
|
||||
label_clone, std::move(value), options); \
|
||||
\
|
||||
auto next = std::reinterpret_pointer_cast<Capture##pascal_name##Property>( \
|
||||
element_->properties.GetNext(std::move(new_value), options.readonly)); \
|
||||
\
|
||||
return next->value; \
|
||||
}
|
||||
|
||||
_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_RECORDER_DEFINITION);
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// CaptureContext
|
||||
///
|
||||
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
CaptureContext::CaptureContext() : active_(true) {}
|
||||
CaptureContext::CaptureContext(std::initializer_list<std::string> allowlist)
|
||||
: active_(true), allowlist_(allowlist) {}
|
||||
#else
|
||||
CaptureContext::CaptureContext() {}
|
||||
CaptureContext::CaptureContext(std::initializer_list<std::string> allowlist) {}
|
||||
#endif
|
||||
|
||||
CaptureContext::CaptureContext(CaptureContext::InactiveFlag) {}
|
||||
|
||||
CaptureContext CaptureContext::MakeInactive() {
|
||||
return CaptureContext(InactiveFlag{});
|
||||
}
|
||||
|
||||
CaptureContext CaptureContext::MakeAllowlist(
|
||||
std::initializer_list<std::string> allowlist) {
|
||||
return CaptureContext(allowlist);
|
||||
}
|
||||
|
||||
bool CaptureContext::IsActive() const {
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
return active_;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CaptureContext::Rewind() {
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
for (auto& [name, capture] : documents_) {
|
||||
capture.GetElement()->Rewind();
|
||||
}
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
Capture CaptureContext::GetDocument(const std::string& label) {
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
if (!active_) {
|
||||
return Capture::MakeInactive();
|
||||
}
|
||||
|
||||
if (allowlist_.has_value()) {
|
||||
if (allowlist_->find(label) == allowlist_->end()) {
|
||||
return Capture::MakeInactive();
|
||||
}
|
||||
}
|
||||
|
||||
auto found = documents_.find(label);
|
||||
if (found != documents_.end()) {
|
||||
// Always rewind when fetching an existing document.
|
||||
found->second.Rewind();
|
||||
return found->second;
|
||||
}
|
||||
|
||||
auto new_document = Capture(label);
|
||||
documents_.emplace(label, new_document);
|
||||
return new_document;
|
||||
#else
|
||||
return Capture::MakeInactive();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CaptureContext::DoesDocumentExist(const std::string& label) const {
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
if (!active_) {
|
||||
return false;
|
||||
}
|
||||
return documents_.find(label) != documents_.end();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
@ -1,300 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_IMPELLER_CORE_CAPTURE_H_
|
||||
#define FLUTTER_IMPELLER_CORE_CAPTURE_H_
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "impeller/geometry/color.h"
|
||||
#include "impeller/geometry/matrix.h"
|
||||
#include "impeller/geometry/point.h"
|
||||
#include "impeller/geometry/rect.h"
|
||||
#include "impeller/geometry/scalar.h"
|
||||
#include "impeller/geometry/vector.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
struct CaptureProcTable;
|
||||
|
||||
#define _FOR_EACH_CAPTURE_PROPERTY(PROPERTY_V) \
|
||||
PROPERTY_V(bool, Boolean, boolean) \
|
||||
PROPERTY_V(int, Integer, integer) \
|
||||
PROPERTY_V(Scalar, Scalar, scalar) \
|
||||
PROPERTY_V(Point, Point, point) \
|
||||
PROPERTY_V(Vector3, Vector3, vector3) \
|
||||
PROPERTY_V(Rect, Rect, rect) \
|
||||
PROPERTY_V(Color, Color, color) \
|
||||
PROPERTY_V(Matrix, Matrix, matrix) \
|
||||
PROPERTY_V(std::string, String, string)
|
||||
|
||||
template <typename Type>
|
||||
struct CaptureCursorListElement {
|
||||
std::string label;
|
||||
|
||||
explicit CaptureCursorListElement(const std::string& label) : label(label){};
|
||||
|
||||
virtual ~CaptureCursorListElement() = default;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Determines if previously captured data matches closely enough with
|
||||
/// newly recorded data to safely emitted in its place. If this
|
||||
/// returns `false`, then the remaining elements in the capture list
|
||||
/// are discarded and re-recorded.
|
||||
///
|
||||
/// This mechanism ensures that the UI of an interactive inspector can
|
||||
/// never deviate from reality, even if the schema of the captured
|
||||
/// data were to significantly deviate.
|
||||
///
|
||||
virtual bool MatchesCloselyEnough(const Type& other) const = 0;
|
||||
};
|
||||
|
||||
#define _CAPTURE_TYPE(type_name, pascal_name, lower_name) k##pascal_name,
|
||||
|
||||
#define _CAPTURE_PROPERTY_CAST_DECLARATION(type_name, pascal_name, lower_name) \
|
||||
std::optional<type_name> As##pascal_name() const;
|
||||
|
||||
/// A capturable property type
|
||||
struct CaptureProperty : public CaptureCursorListElement<CaptureProperty> {
|
||||
enum class Type { _FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_TYPE) };
|
||||
|
||||
struct Options {
|
||||
struct Range {
|
||||
Scalar min;
|
||||
Scalar max;
|
||||
};
|
||||
|
||||
/// Readonly properties are always re-recorded during capture. Any edits
|
||||
/// made to readonly values in-between captures are overwritten during the
|
||||
/// next capture.
|
||||
bool readonly = false;
|
||||
|
||||
/// An inspector hint that can be used for displaying sliders. Only used for
|
||||
/// numeric types. Rounded down for integer types.
|
||||
std::optional<Range> range;
|
||||
};
|
||||
|
||||
Options options;
|
||||
|
||||
CaptureProperty(const std::string& label, Options options);
|
||||
|
||||
virtual ~CaptureProperty();
|
||||
|
||||
virtual Type GetType() const = 0;
|
||||
|
||||
virtual void Invoke(const CaptureProcTable& proc_table) = 0;
|
||||
|
||||
bool MatchesCloselyEnough(const CaptureProperty& other) const override;
|
||||
|
||||
_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_CAST_DECLARATION)
|
||||
};
|
||||
|
||||
#define _CAPTURE_PROPERTY_DECLARATION(type_name, pascal_name, lower_name) \
|
||||
struct Capture##pascal_name##Property final : public CaptureProperty { \
|
||||
type_name value; \
|
||||
\
|
||||
static std::shared_ptr<Capture##pascal_name##Property> \
|
||||
Make(const std::string& label, type_name value, Options options); \
|
||||
\
|
||||
/* |CaptureProperty| */ \
|
||||
Type GetType() const override; \
|
||||
\
|
||||
/* |CaptureProperty| */ \
|
||||
void Invoke(const CaptureProcTable& proc_table) override; \
|
||||
\
|
||||
private: \
|
||||
Capture##pascal_name##Property(const std::string& label, \
|
||||
type_name value, \
|
||||
Options options); \
|
||||
\
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(Capture##pascal_name##Property); \
|
||||
};
|
||||
|
||||
_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_DECLARATION);
|
||||
|
||||
#define _CAPTURE_PROC(type_name, pascal_name, lower_name) \
|
||||
std::function<void(Capture##pascal_name##Property&)> lower_name = \
|
||||
[](Capture##pascal_name##Property& value) {};
|
||||
|
||||
struct CaptureProcTable {
|
||||
_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROC)
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class CapturePlaybackList {
|
||||
public:
|
||||
CapturePlaybackList() = default;
|
||||
|
||||
~CapturePlaybackList() {
|
||||
// Force the list element type to inherit the CRTP type. We can't enforce
|
||||
// this as a template requirement directly because `CaptureElement` has a
|
||||
// recursive `CaptureCursorList<CaptureElement>` property, and so the
|
||||
// compiler fails the check due to the type being incomplete.
|
||||
static_assert(std::is_base_of_v<CaptureCursorListElement<Type>, Type>);
|
||||
}
|
||||
|
||||
void Rewind() { cursor_ = 0; }
|
||||
|
||||
size_t Count() { return values_.size(); }
|
||||
|
||||
std::shared_ptr<Type> GetNext(std::shared_ptr<Type> captured,
|
||||
bool force_overwrite) {
|
||||
if (cursor_ < values_.size()) {
|
||||
std::shared_ptr<Type>& result = values_[cursor_];
|
||||
|
||||
if (result->MatchesCloselyEnough(*captured)) {
|
||||
if (force_overwrite) {
|
||||
values_[cursor_] = captured;
|
||||
}
|
||||
// Safe playback is possible.
|
||||
++cursor_;
|
||||
return result;
|
||||
}
|
||||
// The data has changed too much from the last capture to safely continue
|
||||
// playback. Discard this and all subsequent elements to re-record.
|
||||
values_.resize(cursor_);
|
||||
}
|
||||
|
||||
++cursor_;
|
||||
values_.push_back(captured);
|
||||
return captured;
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> FindFirstByLabel(const std::string& label) {
|
||||
for (std::shared_ptr<Type>& value : values_) {
|
||||
if (value->label == label) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Iterate(std::function<void(Type&)> iterator) const {
|
||||
for (auto& value : values_) {
|
||||
iterator(*value);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t cursor_ = 0;
|
||||
std::vector<std::shared_ptr<Type>> values_;
|
||||
|
||||
CapturePlaybackList(const CapturePlaybackList&) = delete;
|
||||
|
||||
CapturePlaybackList& operator=(const CapturePlaybackList&) = delete;
|
||||
};
|
||||
|
||||
/// A document of capture data, containing a list of properties and a list
|
||||
/// of subdocuments.
|
||||
struct CaptureElement final : public CaptureCursorListElement<CaptureElement> {
|
||||
CapturePlaybackList<CaptureProperty> properties;
|
||||
CapturePlaybackList<CaptureElement> children;
|
||||
|
||||
static std::shared_ptr<CaptureElement> Make(const std::string& label);
|
||||
|
||||
void Rewind();
|
||||
|
||||
bool MatchesCloselyEnough(const CaptureElement& other) const override;
|
||||
|
||||
private:
|
||||
explicit CaptureElement(const std::string& label);
|
||||
|
||||
CaptureElement(const CaptureElement&) = delete;
|
||||
|
||||
CaptureElement& operator=(const CaptureElement&) = delete;
|
||||
};
|
||||
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
#define _CAPTURE_PROPERTY_RECORDER_DECLARATION(type_name, pascal_name, \
|
||||
lower_name) \
|
||||
type_name Add##pascal_name(std::string_view label, type_name value, \
|
||||
CaptureProperty::Options options = {});
|
||||
#else
|
||||
#define _CAPTURE_PROPERTY_RECORDER_DECLARATION(type_name, pascal_name, \
|
||||
lower_name) \
|
||||
inline type_name Add##pascal_name(std::string_view label, type_name value, \
|
||||
CaptureProperty::Options options = {}) { \
|
||||
return value; \
|
||||
}
|
||||
#endif
|
||||
|
||||
class Capture {
|
||||
public:
|
||||
explicit Capture(const std::string& label);
|
||||
|
||||
Capture();
|
||||
|
||||
static Capture MakeInactive();
|
||||
|
||||
inline Capture CreateChild(std::string_view label) {
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
if (!active_) {
|
||||
return Capture();
|
||||
}
|
||||
|
||||
std::string label_copy = std::string(label);
|
||||
auto new_capture = Capture(label_copy);
|
||||
new_capture.element_ =
|
||||
element_->children.GetNext(new_capture.element_, false);
|
||||
new_capture.element_->Rewind();
|
||||
return new_capture;
|
||||
#else
|
||||
return Capture();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::shared_ptr<CaptureElement> GetElement() const;
|
||||
|
||||
void Rewind();
|
||||
|
||||
_FOR_EACH_CAPTURE_PROPERTY(_CAPTURE_PROPERTY_RECORDER_DECLARATION)
|
||||
|
||||
private:
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
std::shared_ptr<CaptureElement> element_;
|
||||
bool active_ = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
class CaptureContext {
|
||||
public:
|
||||
CaptureContext();
|
||||
|
||||
static CaptureContext MakeInactive();
|
||||
|
||||
static CaptureContext MakeAllowlist(
|
||||
std::initializer_list<std::string> allowlist);
|
||||
|
||||
bool IsActive() const;
|
||||
|
||||
void Rewind();
|
||||
|
||||
Capture GetDocument(const std::string& label);
|
||||
|
||||
bool DoesDocumentExist(const std::string& label) const;
|
||||
|
||||
private:
|
||||
struct InactiveFlag {};
|
||||
explicit CaptureContext(InactiveFlag);
|
||||
CaptureContext(std::initializer_list<std::string> allowlist);
|
||||
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
bool active_ = false;
|
||||
std::optional<std::unordered_set<std::string>> allowlist_;
|
||||
std::unordered_map<std::string, Capture> documents_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
#endif // FLUTTER_IMPELLER_CORE_CAPTURE_H_
|
||||
@ -10,18 +10,17 @@
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "flutter/fml/build_config.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/fml/status_or.h"
|
||||
#include "impeller/base/validation.h"
|
||||
#include "impeller/core/formats.h"
|
||||
#include "impeller/core/host_buffer.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
#include "impeller/renderer/capabilities.h"
|
||||
#include "impeller/renderer/command_buffer.h"
|
||||
#include "impeller/renderer/pipeline.h"
|
||||
#include "impeller/renderer/pipeline_descriptor.h"
|
||||
#include "impeller/renderer/render_target.h"
|
||||
#include "impeller/typographer/lazy_glyph_atlas.h"
|
||||
#include "impeller/typographer/typographer_context.h"
|
||||
|
||||
#include "impeller/entity/border_mask_blur.frag.h"
|
||||
@ -54,8 +53,6 @@
|
||||
#include "impeller/entity/tiled_texture_fill.frag.h"
|
||||
#include "impeller/entity/yuv_to_rgb_filter.frag.h"
|
||||
|
||||
#include "impeller/typographer/glyph_atlas.h"
|
||||
|
||||
#include "impeller/entity/conical_gradient_ssbo_fill.frag.h"
|
||||
#include "impeller/entity/linear_gradient_ssbo_fill.frag.h"
|
||||
#include "impeller/entity/radial_gradient_ssbo_fill.frag.h"
|
||||
|
||||
@ -6,12 +6,11 @@
|
||||
#include <optional>
|
||||
|
||||
#include "fml/logging.h"
|
||||
#include "impeller/base/strings.h"
|
||||
#include "impeller/base/validation.h"
|
||||
#include "impeller/core/formats.h"
|
||||
#include "impeller/entity/contents/anonymous_contents.h"
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/texture_contents.h"
|
||||
#include "impeller/entity/entity.h"
|
||||
#include "impeller/renderer/command_buffer.h"
|
||||
#include "impeller/renderer/render_pass.h"
|
||||
|
||||
|
||||
@ -48,11 +48,10 @@ std::optional<Rect> SolidColorContents::GetCoverage(
|
||||
bool SolidColorContents::Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) const {
|
||||
auto capture = entity.GetCapture().CreateChild("SolidColorContents");
|
||||
using VS = SolidFillPipeline::VertexShader;
|
||||
|
||||
VS::FrameInfo frame_info;
|
||||
frame_info.color = capture.AddColor("Color", GetColor()).Premultiply();
|
||||
frame_info.color = GetColor().Premultiply();
|
||||
|
||||
PipelineBuilderCallback pipeline_callback =
|
||||
[&renderer](ContentContextOptions options) {
|
||||
|
||||
@ -109,8 +109,6 @@ std::optional<Snapshot> TextureContents::RenderToSnapshot(
|
||||
bool TextureContents::Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) const {
|
||||
auto capture = entity.GetCapture().CreateChild("TextureContents");
|
||||
|
||||
using VS = TextureFillVertexShader;
|
||||
using FS = TextureFillFragmentShader;
|
||||
using FSStrict = TextureFillStrictSrcFragmentShader;
|
||||
@ -124,18 +122,15 @@ bool TextureContents::Render(const ContentContext& renderer,
|
||||
texture_->GetTextureDescriptor().type == TextureType::kTextureExternalOES;
|
||||
FML_DCHECK(!is_external_texture);
|
||||
|
||||
auto source_rect = capture.AddRect("Source rect", source_rect_);
|
||||
auto texture_coords =
|
||||
Rect::MakeSize(texture_->GetSize()).Project(source_rect);
|
||||
Rect::MakeSize(texture_->GetSize()).Project(source_rect_);
|
||||
|
||||
VertexBufferBuilder<VS::PerVertexData> vertex_builder;
|
||||
auto destination_rect =
|
||||
capture.AddRect("Destination rect", destination_rect_);
|
||||
vertex_builder.AddVertices({
|
||||
{destination_rect.GetLeftTop(), texture_coords.GetLeftTop()},
|
||||
{destination_rect.GetRightTop(), texture_coords.GetRightTop()},
|
||||
{destination_rect.GetLeftBottom(), texture_coords.GetLeftBottom()},
|
||||
{destination_rect.GetRightBottom(), texture_coords.GetRightBottom()},
|
||||
{destination_rect_.GetLeftTop(), texture_coords.GetLeftTop()},
|
||||
{destination_rect_.GetRightTop(), texture_coords.GetRightTop()},
|
||||
{destination_rect_.GetLeftBottom(), texture_coords.GetLeftBottom()},
|
||||
{destination_rect_.GetRightBottom(), texture_coords.GetRightBottom()},
|
||||
});
|
||||
|
||||
auto& host_buffer = renderer.GetTransientsBuffer();
|
||||
@ -170,11 +165,11 @@ bool TextureContents::Render(const ContentContext& renderer,
|
||||
// texel to ensure that linear filtering does not sample anything outside
|
||||
// the source rect bounds.
|
||||
auto strict_texture_coords =
|
||||
Rect::MakeSize(texture_->GetSize()).Project(source_rect.Expand(-0.5));
|
||||
Rect::MakeSize(texture_->GetSize()).Project(source_rect_.Expand(-0.5));
|
||||
|
||||
FSStrict::FragInfo frag_info;
|
||||
frag_info.source_rect = Vector4(strict_texture_coords.GetLTRB());
|
||||
frag_info.alpha = capture.AddScalar("Alpha", GetOpacity());
|
||||
frag_info.alpha = GetOpacity();
|
||||
FSStrict::BindFragInfo(pass, host_buffer.EmplaceUniform((frag_info)));
|
||||
FSStrict::BindTextureSampler(
|
||||
pass, texture_,
|
||||
@ -182,7 +177,7 @@ bool TextureContents::Render(const ContentContext& renderer,
|
||||
sampler_descriptor_));
|
||||
} else {
|
||||
FS::FragInfo frag_info;
|
||||
frag_info.alpha = capture.AddScalar("Alpha", GetOpacity());
|
||||
frag_info.alpha = GetOpacity();
|
||||
FS::BindFragInfo(pass, host_buffer.EmplaceUniform((frag_info)));
|
||||
FS::BindTextureSampler(
|
||||
pass, texture_,
|
||||
|
||||
@ -188,16 +188,8 @@ Scalar Entity::DeriveTextScale() const {
|
||||
return GetTransform().GetMaxBasisLengthXY();
|
||||
}
|
||||
|
||||
Capture& Entity::GetCapture() const {
|
||||
return capture_;
|
||||
}
|
||||
|
||||
Entity Entity::Clone() const {
|
||||
return Entity(*this);
|
||||
}
|
||||
|
||||
void Entity::SetCapture(Capture capture) const {
|
||||
capture_ = std::move(capture);
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "impeller/core/capture.h"
|
||||
#include "impeller/entity/contents/contents.h"
|
||||
#include "impeller/geometry/color.h"
|
||||
#include "impeller/geometry/matrix.h"
|
||||
@ -123,10 +122,6 @@ class Entity {
|
||||
|
||||
Scalar DeriveTextScale() const;
|
||||
|
||||
Capture& GetCapture() const;
|
||||
|
||||
void SetCapture(Capture capture) const;
|
||||
|
||||
Entity Clone() const;
|
||||
|
||||
private:
|
||||
@ -136,7 +131,6 @@ class Entity {
|
||||
std::shared_ptr<Contents> contents_;
|
||||
BlendMode blend_mode_ = BlendMode::kSourceOver;
|
||||
uint32_t clip_depth_ = 1u;
|
||||
mutable Capture capture_;
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -44,8 +44,6 @@ std::tuple<std::optional<Color>, BlendMode> ElementAsBackgroundColor(
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const std::string EntityPass::kCaptureDocumentName = "EntityPass";
|
||||
|
||||
EntityPass::EntityPass() = default;
|
||||
|
||||
EntityPass::~EntityPass() = default;
|
||||
@ -354,9 +352,6 @@ bool EntityPass::DoesBackdropGetRead(ContentContext& renderer) const {
|
||||
|
||||
bool EntityPass::Render(ContentContext& renderer,
|
||||
const RenderTarget& render_target) const {
|
||||
auto capture =
|
||||
renderer.GetContext()->capture.GetDocument(kCaptureDocumentName);
|
||||
|
||||
renderer.GetRenderTargetCache()->Start();
|
||||
fml::ScopedCleanupClosure reset_state([&renderer]() {
|
||||
renderer.GetLazyGlyphAtlas()->ResetTextFrames();
|
||||
@ -377,10 +372,6 @@ bool EntityPass::Render(ContentContext& renderer,
|
||||
return false;
|
||||
}
|
||||
|
||||
capture.AddRect("Coverage",
|
||||
Rect::MakeSize(root_render_target.GetRenderTargetSize()),
|
||||
{.readonly = true});
|
||||
|
||||
const auto& lazy_glyph_atlas = renderer.GetLazyGlyphAtlas();
|
||||
IterateAllEntities([&lazy_glyph_atlas](const Entity& entity) {
|
||||
if (const auto& contents = entity.GetContents()) {
|
||||
@ -402,7 +393,6 @@ bool EntityPass::Render(ContentContext& renderer,
|
||||
GetClearColorOrDefault(render_target.GetRenderTargetSize()));
|
||||
|
||||
if (!OnRender(renderer, // renderer
|
||||
capture, // capture
|
||||
offscreen_target.GetRenderTarget()
|
||||
.GetRenderTargetSize(), // root_pass_size
|
||||
offscreen_target, // pass_target
|
||||
@ -509,7 +499,6 @@ bool EntityPass::Render(ContentContext& renderer,
|
||||
|
||||
return OnRender( //
|
||||
renderer, // renderer
|
||||
capture, // capture
|
||||
root_render_target.GetRenderTargetSize(), // root_pass_size
|
||||
pass_target, // pass_target
|
||||
Point(), // global_pass_position
|
||||
@ -521,7 +510,6 @@ bool EntityPass::Render(ContentContext& renderer,
|
||||
EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
const EntityPass::Element& element,
|
||||
ContentContext& renderer,
|
||||
Capture& capture,
|
||||
InlinePassContext& pass_context,
|
||||
ISize root_pass_size,
|
||||
Point global_pass_position,
|
||||
@ -533,7 +521,6 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
///
|
||||
if (const auto& entity = std::get_if<Entity>(&element)) {
|
||||
Entity element_entity = entity->Clone();
|
||||
element_entity.SetCapture(capture.CreateChild("Entity"));
|
||||
|
||||
if (!global_pass_position.IsZero()) {
|
||||
// If the pass image is going to be rendered with a non-zero position,
|
||||
@ -558,11 +545,9 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
|
||||
if (!subpass->backdrop_filter_proc_ &&
|
||||
subpass->delegate_->CanCollapseIntoParentPass(subpass)) {
|
||||
auto subpass_capture = capture.CreateChild("EntityPass (Collapsed)");
|
||||
// Directly render into the parent target and move on.
|
||||
if (!subpass->OnRender(
|
||||
renderer, // renderer
|
||||
subpass_capture, // capture
|
||||
root_pass_size, // root_pass_size
|
||||
pass_context.GetPassTarget(), // pass_target
|
||||
global_pass_position, // global_pass_position
|
||||
@ -606,12 +591,10 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
if (!clip_coverage_stack.HasCoverage()) {
|
||||
// The current clip is empty. This means the pass texture won't be
|
||||
// visible, so skip it.
|
||||
capture.CreateChild("Subpass Entity (Skipped: Empty clip A)");
|
||||
return EntityPass::EntityResult::Skip();
|
||||
}
|
||||
auto clip_coverage_back = clip_coverage_stack.CurrentClipCoverage();
|
||||
if (!clip_coverage_back.has_value()) {
|
||||
capture.CreateChild("Subpass Entity (Skipped: Empty clip B)");
|
||||
return EntityPass::EntityResult::Skip();
|
||||
}
|
||||
|
||||
@ -623,14 +606,12 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
.GetRenderTargetSize()))
|
||||
.Intersection(clip_coverage_back.value());
|
||||
if (!coverage_limit.has_value()) {
|
||||
capture.CreateChild("Subpass Entity (Skipped: Empty coverage limit A)");
|
||||
return EntityPass::EntityResult::Skip();
|
||||
}
|
||||
|
||||
coverage_limit =
|
||||
coverage_limit->Intersection(Rect::MakeSize(root_pass_size));
|
||||
if (!coverage_limit.has_value()) {
|
||||
capture.CreateChild("Subpass Entity (Skipped: Empty coverage limit B)");
|
||||
return EntityPass::EntityResult::Skip();
|
||||
}
|
||||
|
||||
@ -639,13 +620,11 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
? coverage_limit
|
||||
: GetSubpassCoverage(*subpass, coverage_limit);
|
||||
if (!subpass_coverage.has_value()) {
|
||||
capture.CreateChild("Subpass Entity (Skipped: Empty subpass coverage A)");
|
||||
return EntityPass::EntityResult::Skip();
|
||||
}
|
||||
|
||||
auto subpass_size = ISize(subpass_coverage->GetSize());
|
||||
if (subpass_size.IsEmpty()) {
|
||||
capture.CreateChild("Subpass Entity (Skipped: Empty subpass coverage B)");
|
||||
return EntityPass::EntityResult::Skip();
|
||||
}
|
||||
|
||||
@ -660,9 +639,6 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
return EntityPass::EntityResult::Failure();
|
||||
}
|
||||
|
||||
auto subpass_capture = capture.CreateChild("EntityPass");
|
||||
subpass_capture.AddRect("Coverage", *subpass_coverage, {.readonly = true});
|
||||
|
||||
// Start non-collapsed subpasses with a fresh clip coverage stack limited by
|
||||
// the subpass coverage. This is important because image filters applied to
|
||||
// save layers may transform the subpass texture after it's rendered,
|
||||
@ -674,7 +650,6 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
// time they are transient).
|
||||
if (!subpass->OnRender(
|
||||
renderer, // renderer
|
||||
subpass_capture, // capture
|
||||
root_pass_size, // root_pass_size
|
||||
subpass_target, // pass_target
|
||||
subpass_coverage->GetOrigin(), // global_pass_position
|
||||
@ -714,16 +689,11 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
|
||||
return EntityPass::EntityResult::Failure();
|
||||
}
|
||||
Entity element_entity;
|
||||
Capture subpass_texture_capture =
|
||||
capture.CreateChild("Entity (Subpass texture)");
|
||||
element_entity.SetClipDepth(subpass->clip_depth_);
|
||||
element_entity.SetCapture(subpass_texture_capture);
|
||||
element_entity.SetContents(std::move(offscreen_texture_contents));
|
||||
element_entity.SetBlendMode(subpass->blend_mode_);
|
||||
element_entity.SetTransform(subpass_texture_capture.AddMatrix(
|
||||
"Transform",
|
||||
Matrix::MakeTranslation(
|
||||
Vector3(subpass_coverage->GetOrigin() - global_pass_position))));
|
||||
element_entity.SetTransform(Matrix::MakeTranslation(
|
||||
Vector3(subpass_coverage->GetOrigin() - global_pass_position)));
|
||||
|
||||
return EntityPass::EntityResult::Success(std::move(element_entity));
|
||||
}
|
||||
@ -846,7 +816,6 @@ bool EntityPass::RenderElement(Entity& element_entity,
|
||||
|
||||
bool EntityPass::OnRender(
|
||||
ContentContext& renderer,
|
||||
Capture& capture,
|
||||
ISize root_pass_size,
|
||||
EntityPassTarget& pass_target,
|
||||
Point global_pass_position,
|
||||
@ -919,7 +888,6 @@ bool EntityPass::OnRender(
|
||||
EntityResult result =
|
||||
GetEntityForElement(element, // element
|
||||
renderer, // renderer
|
||||
capture, // capture
|
||||
pass_context, // pass_context
|
||||
root_pass_size, // root_pass_size
|
||||
global_pass_position, // global_pass_position
|
||||
|
||||
@ -53,8 +53,6 @@ class EntityPass {
|
||||
/// `GetEntityForElement()`.
|
||||
using Element = std::variant<Entity, std::unique_ptr<EntityPass>>;
|
||||
|
||||
static const std::string kCaptureDocumentName;
|
||||
|
||||
using BackdropFilterProc = std::function<std::shared_ptr<FilterContents>(
|
||||
FilterInput::Ref,
|
||||
const Matrix& effect_transform,
|
||||
@ -238,7 +236,6 @@ class EntityPass {
|
||||
|
||||
EntityResult GetEntityForElement(const EntityPass::Element& element,
|
||||
ContentContext& renderer,
|
||||
Capture& capture,
|
||||
InlinePassContext& pass_context,
|
||||
ISize root_pass_size,
|
||||
Point global_pass_position,
|
||||
@ -304,7 +301,6 @@ class EntityPass {
|
||||
/// parent pass.
|
||||
///
|
||||
bool OnRender(ContentContext& renderer,
|
||||
Capture& capture,
|
||||
ISize root_pass_size,
|
||||
EntityPassTarget& pass_target,
|
||||
Point global_pass_position,
|
||||
|
||||
@ -124,18 +124,6 @@ EntityPassClipStack::ClipStateResult EntityPassClipStack::ApplyClipState(
|
||||
} break;
|
||||
}
|
||||
|
||||
#ifdef IMPELLER_ENABLE_CAPTURE
|
||||
{
|
||||
auto element_entity_coverage = entity.GetCoverage();
|
||||
if (element_entity_coverage.has_value()) {
|
||||
element_entity_coverage =
|
||||
element_entity_coverage->Shift(global_pass_position);
|
||||
entity.GetCapture().AddRect("Coverage", *element_entity_coverage,
|
||||
{.readonly = true});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RecordEntity(entity, global_clip_coverage.type,
|
||||
subpass_state.clip_coverage.back().coverage);
|
||||
|
||||
|
||||
@ -4,13 +4,11 @@
|
||||
|
||||
#include "impeller/renderer/context.h"
|
||||
|
||||
#include "impeller/core/capture.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
Context::~Context() = default;
|
||||
|
||||
Context::Context() : capture(CaptureContext::MakeInactive()) {}
|
||||
Context::Context() = default;
|
||||
|
||||
bool Context::UpdateOffscreenLayerPixelFormat(PixelFormat format) {
|
||||
return false;
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
#include <string>
|
||||
|
||||
#include "impeller/core/allocator.h"
|
||||
#include "impeller/core/capture.h"
|
||||
#include "impeller/core/formats.h"
|
||||
#include "impeller/renderer/capabilities.h"
|
||||
#include "impeller/renderer/command_queue.h"
|
||||
@ -171,8 +170,6 @@ class Context {
|
||||
///
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
CaptureContext capture;
|
||||
|
||||
/// Stores a task on the `ContextMTL` that is awaiting access for the GPU.
|
||||
///
|
||||
/// The task will be executed in the event that the GPU access has changed to
|
||||
|
||||
@ -11,9 +11,6 @@ declare_args() {
|
||||
impeller_debug =
|
||||
flutter_runtime_mode == "debug" || flutter_runtime_mode == "profile"
|
||||
|
||||
# Whether the runtime capture/playback system is enabled.
|
||||
impeller_capture = flutter_runtime_mode == "debug"
|
||||
|
||||
# Whether the Metal backend is enabled.
|
||||
impeller_enable_metal = (is_mac || is_ios) && target_os != "fuchsia"
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "flutter/impeller/aiks/aiks_context.h"
|
||||
#include "flutter/impeller/renderer/context.h"
|
||||
#include "flutter/shell/gpu/gpu_surface_gl_delegate.h"
|
||||
#include "impeller/renderer/renderer.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "flutter/impeller/aiks/aiks_context.h"
|
||||
#include "flutter/impeller/renderer/context.h"
|
||||
#include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h"
|
||||
#include "impeller/renderer/renderer.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
|
||||
@ -478,9 +478,6 @@ impeller_Play_AiksTest_CanRenderWithContiguousClipRestores_Vulkan.png
|
||||
impeller_Play_AiksTest_CanSaveLayerStandalone_Metal.png
|
||||
impeller_Play_AiksTest_CanSaveLayerStandalone_OpenGLES.png
|
||||
impeller_Play_AiksTest_CanSaveLayerStandalone_Vulkan.png
|
||||
impeller_Play_AiksTest_CaptureContext_Metal.png
|
||||
impeller_Play_AiksTest_CaptureContext_OpenGLES.png
|
||||
impeller_Play_AiksTest_CaptureContext_Vulkan.png
|
||||
impeller_Play_AiksTest_ClearBlendWithBlur_Metal.png
|
||||
impeller_Play_AiksTest_ClearBlendWithBlur_OpenGLES.png
|
||||
impeller_Play_AiksTest_ClearBlendWithBlur_Vulkan.png
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user