blob: 89ace8c5d12839198da265bfa3e95a6c8b0376cf [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "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