未验证 提交 ff1bcdc0 编写于 作者: D David Worsham 提交者: GitHub

Add Views V2 support for Fuchsia (#8115)

上级 0c393d64
......@@ -117,6 +117,8 @@ FILE: ../../../flutter/flow/skia_gpu_object.cc
FILE: ../../../flutter/flow/skia_gpu_object.h
FILE: ../../../flutter/flow/texture.cc
FILE: ../../../flutter/flow/texture.h
FILE: ../../../flutter/flow/view_holder.cc
FILE: ../../../flutter/flow/view_holder.h
FILE: ../../../flutter/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart
FILE: ../../../flutter/fml/arraysize.h
FILE: ../../../flutter/fml/base32.cc
......
......@@ -77,15 +77,17 @@ source_set("flow") {
"layers/child_scene_layer.h",
"scene_update_context.cc",
"scene_update_context.h",
"view_holder.cc",
"view_holder.h",
]
public_deps += [
"//garnet/public/lib/ui/scenic/cpp",
"//sdk/fidl/fuchsia.ui.scenic",
"//sdk/fidl/fuchsia.ui.views",
"//sdk/lib/ui/scenic/cpp",
"//topaz/public/dart-pkg/zircon",
"//zircon/public/lib/zx",
]
public_deps += [ "//zircon/public/lib/zx" ]
}
}
......
......@@ -4,81 +4,82 @@
#include "flutter/flow/export_node.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/thread_local.h"
namespace {
using ExportNodeBindings =
std::unordered_map<zx_koid_t, std::unique_ptr<flow::ExportNode>>;
FML_THREAD_LOCAL fml::ThreadLocal tls_export_node_bindings([](intptr_t value) {
delete reinterpret_cast<ExportNodeBindings*>(value);
});
} // namespace
namespace flow {
ExportNodeHolder::ExportNodeHolder(
fml::RefPtr<fml::TaskRunner> gpu_task_runner,
fml::RefPtr<zircon::dart::Handle> export_token_handle)
: gpu_task_runner_(std::move(gpu_task_runner)),
export_node_(std::make_unique<ExportNode>(export_token_handle)) {
FML_DCHECK(gpu_task_runner_);
ExportNode::ExportNode(zx::eventpair export_token)
: pending_export_token_(std::move(export_token)) {
FML_DCHECK(pending_export_token_);
}
void ExportNodeHolder::Bind(SceneUpdateContext& context,
scenic::ContainerNode& container,
const SkPoint& offset,
bool hit_testable) {
export_node_->Bind(context, container, offset, hit_testable);
}
void ExportNode::Create(zx_koid_t id, zx::eventpair export_token) {
// This GPU thread contains at least 1 ViewHolder. Initialize the per-thread
// bindings.
if (tls_export_node_bindings.Get() == 0) {
tls_export_node_bindings.Set(
reinterpret_cast<intptr_t>(new ExportNodeBindings()));
}
ExportNodeHolder::~ExportNodeHolder() {
gpu_task_runner_->PostTask(
fml::MakeCopyable([export_node = std::move(export_node_)]() {
export_node->Dispose(true);
}));
auto* bindings =
reinterpret_cast<ExportNodeBindings*>(tls_export_node_bindings.Get());
FML_DCHECK(bindings);
FML_DCHECK(bindings->find(id) == bindings->end());
auto export_node =
std::unique_ptr<ExportNode>(new ExportNode(std::move(export_token)));
bindings->emplace(id, std::move(export_node));
}
ExportNode::ExportNode(fml::RefPtr<zircon::dart::Handle> export_token_handle)
: export_token_(export_token_handle->ReleaseHandle()) {}
void ExportNode::Destroy(zx_koid_t id) {
auto* bindings =
reinterpret_cast<ExportNodeBindings*>(tls_export_node_bindings.Get());
FML_DCHECK(bindings);
ExportNode::~ExportNode() {
// Ensure that we properly released the node.
FML_DCHECK(!node_);
FML_DCHECK(scene_update_context_ == nullptr);
bindings->erase(id);
}
void ExportNode::Bind(SceneUpdateContext& context,
scenic::ContainerNode& container,
const SkPoint& offset,
bool hit_testable) {
if (export_token_) {
// Happens first time we bind.
node_.reset(new scenic::EntityNode(container.session()));
node_->Export(std::move(export_token_));
// Add ourselves to the context so it can call Dispose() on us if the Scenic
// session is closed.
context.AddExportNode(this);
scene_update_context_ = &context;
ExportNode* ExportNode::FromId(zx_koid_t id) {
auto* bindings =
reinterpret_cast<ExportNodeBindings*>(tls_export_node_bindings.Get());
if (!bindings) {
return nullptr;
}
if (node_) {
container.AddChild(*node_);
node_->SetTranslation(offset.x(), offset.y(), 0.f);
node_->SetHitTestBehavior(
hit_testable ? fuchsia::ui::gfx::HitTestBehavior::kDefault
: fuchsia::ui::gfx::HitTestBehavior::kSuppress);
auto binding = bindings->find(id);
if (binding == bindings->end()) {
return nullptr;
}
return binding->second.get();
}
void ExportNode::Dispose(bool remove_from_scene_update_context) {
// If scene_update_context_ is set, then we should still have a node left to
// dereference.
// If scene_update_context_ is null, then either:
// 1. A node was never created, or
// 2. A node was created but was already dereferenced (i.e. Dispose has
// already been called).
FML_DCHECK(scene_update_context_ || !node_);
if (remove_from_scene_update_context && scene_update_context_) {
scene_update_context_->RemoveExportNode(this);
void ExportNode::UpdateScene(SceneUpdateContext& context,
const SkPoint& offset,
const SkSize& size,
bool hit_testable) {
if (pending_export_token_) {
export_node_ = std::make_unique<scenic::EntityNode>(context.session());
export_node_->Export(std::move(pending_export_token_));
}
FML_DCHECK(export_node_);
scene_update_context_ = nullptr;
export_token_.reset();
node_ = nullptr;
context.top_entity()->entity_node().AddChild(*export_node_);
export_node_->SetTranslation(offset.x(), offset.y(), -0.1f);
export_node_->SetHitTestBehavior(
hit_testable ? fuchsia::ui::gfx::HitTestBehavior::kDefault
: fuchsia::ui::gfx::HitTestBehavior::kSuppress);
}
} // namespace flow
......@@ -5,75 +5,42 @@
#ifndef FLUTTER_FLOW_EXPORT_NODE_H_
#define FLUTTER_FLOW_EXPORT_NODE_H_
#include <memory>
#include <lib/ui/scenic/cpp/resources.h>
#include <lib/zx/eventpair.h>
#include <third_party/skia/include/core/SkMatrix.h>
#include <third_party/skia/include/core/SkPoint.h>
#include <third_party/skia/include/core/SkSize.h>
#include <zircon/types.h>
#include <memory>
#include "dart-pkg/zircon/sdk_ext/handle.h"
#include "flutter/flow/scene_update_context.h"
#include "flutter/fml/build_config.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/memory/ref_counted.h"
#include "lib/ui/scenic/cpp/resources.h"
#include "third_party/flutter/fml/task_runner.h"
#include "third_party/skia/include/core/SkPoint.h"
namespace flow {
// Wrapper class for ExportNode to use on UI Thread. When ExportNodeHolder is
// destroyed, a task is posted on the Rasterizer thread to dispose the resources
// held by the ExportNode.
class ExportNodeHolder : public fml::RefCountedThreadSafe<ExportNodeHolder> {
public:
ExportNodeHolder(fml::RefPtr<fml::TaskRunner> gpu_task_runner,
fml::RefPtr<zircon::dart::Handle> export_token_handle);
~ExportNodeHolder();
// Calls Bind() on the wrapped ExportNode.
void Bind(SceneUpdateContext& context,
scenic::ContainerNode& container,
const SkPoint& offset,
bool hit_testable);
ExportNode* export_node() { return export_node_.get(); }
private:
fml::RefPtr<fml::TaskRunner> gpu_task_runner_;
std::unique_ptr<ExportNode> export_node_;
FML_FRIEND_MAKE_REF_COUNTED(ExportNodeHolder);
FML_FRIEND_REF_COUNTED_THREAD_SAFE(ExportNodeHolder);
FML_DISALLOW_COPY_AND_ASSIGN(ExportNodeHolder);
};
// Represents a node which is being exported from the session.
// This object is created on the UI thread but the entity node it contains
// must be created and destroyed by the rasterizer thread.
// Represents a Scenic |ExportNode| resource that exports an |EntityNode| to
// another session.
//
// This object is created and destroyed on the |Rasterizer|'s' thread.
class ExportNode {
public:
ExportNode(fml::RefPtr<zircon::dart::Handle> export_token_handle);
~ExportNode();
static void Create(zx_koid_t id, zx::eventpair export_token);
static void Destroy(zx_koid_t id);
static ExportNode* FromId(zx_koid_t id);
// Binds the export token to the entity node and adds it as a child of
// the specified container. Must be called on the Rasterizer thread.
void Bind(SceneUpdateContext& context,
scenic::ContainerNode& container,
const SkPoint& offset,
bool hit_testable);
// Creates or updates the contained EntityNode resource using the specified
// |SceneUpdateContext|.
void UpdateScene(SceneUpdateContext& context,
const SkPoint& offset,
const SkSize& size,
bool hit_testable);
private:
friend class SceneUpdateContext;
friend class ExportNodeHolder;
// Cleans up resources held and removes this ExportNode from
// SceneUpdateContext. Must be called on the Rasterizer thread.
void Dispose(bool remove_from_scene_update_context);
ExportNode(zx::eventpair export_token);
// Member variables can only be read or modified on Rasterizer thread.
SceneUpdateContext* scene_update_context_ = nullptr;
zx::eventpair export_token_;
std::unique_ptr<scenic::EntityNode> node_;
zx::eventpair pending_export_token_;
std::unique_ptr<scenic::EntityNode> export_node_;
FML_DISALLOW_COPY_AND_ASSIGN(ExportNode);
};
......
......@@ -4,31 +4,43 @@
#include "flutter/flow/layers/child_scene_layer.h"
namespace flow {
#include "flutter/flow/export_node.h"
#include "flutter/flow/view_holder.h"
ChildSceneLayer::ChildSceneLayer() = default;
namespace flow {
ChildSceneLayer::~ChildSceneLayer() = default;
ChildSceneLayer::ChildSceneLayer(zx_koid_t layer_id,
bool use_view_holder,
const SkPoint& offset,
const SkSize& size,
bool hit_testable)
: layer_id_(layer_id),
offset_(offset),
size_(size),
hit_testable_(hit_testable),
use_view_holder_(use_view_holder) {}
void ChildSceneLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
set_needs_system_composite(true);
}
void ChildSceneLayer::Paint(PaintContext& context) const {
FXL_NOTREACHED() << "This layer never needs painting.";
FML_NOTREACHED() << "This layer never needs painting.";
}
void ChildSceneLayer::UpdateScene(SceneUpdateContext& context) {
FML_DCHECK(needs_system_composite());
// TODO(MZ-191): Set clip.
// It's worth asking whether all children should be clipped implicitly
// or whether we should leave this up to the Flutter application to decide.
// In some situations, it might be useful to allow children to draw
// outside of their layout bounds.
if (export_node_holder_) {
context.AddChildScene(export_node_holder_->export_node(), offset_,
hit_testable_);
if (use_view_holder_) {
auto* view_holder = ViewHolder::FromId(layer_id_);
FML_DCHECK(view_holder);
view_holder->UpdateScene(context, offset_, size_, hit_testable_);
} else {
auto* export_node = ExportNode::FromId(layer_id_);
FML_DCHECK(export_node);
export_node->UpdateScene(context, offset_, size_, hit_testable_);
}
}
......
......@@ -5,27 +5,24 @@
#ifndef FLUTTER_FLOW_LAYERS_CHILD_SCENE_LAYER_H_
#define FLUTTER_FLOW_LAYERS_CHILD_SCENE_LAYER_H_
#include "flutter/flow/export_node.h"
#include <third_party/skia/include/core/SkMatrix.h>
#include <third_party/skia/include/core/SkPoint.h>
#include <third_party/skia/include/core/SkSize.h>
#include "flutter/flow/layers/layer.h"
#include "flutter/flow/scene_update_context.h"
namespace flow {
// Layer that represents an embedded child.
class ChildSceneLayer : public Layer {
public:
ChildSceneLayer();
~ChildSceneLayer() override;
void set_offset(const SkPoint& offset) { offset_ = offset; }
void set_size(const SkSize& size) { size_ = size; }
void set_export_node_holder(
fml::RefPtr<ExportNodeHolder> export_node_holder) {
export_node_holder_ = std::move(export_node_holder);
}
void set_hit_testable(bool hit_testable) { hit_testable_ = hit_testable; }
ChildSceneLayer(zx_koid_t layer_id,
bool use_view_holder,
const SkPoint& offset,
const SkSize& size,
bool hit_testable);
~ChildSceneLayer() override = default;
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
......@@ -34,10 +31,11 @@ class ChildSceneLayer : public Layer {
void UpdateScene(SceneUpdateContext& context) override;
private:
zx_koid_t layer_id_ = ZX_KOID_INVALID;
SkPoint offset_;
SkSize size_;
fml::RefPtr<ExportNodeHolder> export_node_holder_;
bool hit_testable_ = true;
bool use_view_holder_ = true;
FML_DISALLOW_COPY_AND_ASSIGN(ChildSceneLayer);
};
......
......@@ -4,42 +4,12 @@
#include "flutter/flow/scene_update_context.h"
#include "flutter/flow/export_node.h"
#include "flutter/flow/layers/layer.h"
#include "flutter/flow/matrix_decomposition.h"
#include "flutter/fml/trace_event.h"
namespace flow {
SceneUpdateContext::SceneUpdateContext(scenic::Session* session,
SurfaceProducer* surface_producer)
: session_(session), surface_producer_(surface_producer) {
FML_DCHECK(surface_producer_ != nullptr);
}
SceneUpdateContext::~SceneUpdateContext() {
// Release Mozart session resources for all ExportNodes.
for (auto export_node : export_nodes_) {
export_node->Dispose(false);
}
};
void SceneUpdateContext::AddChildScene(ExportNode* export_node,
SkPoint offset,
bool hit_testable) {
FML_DCHECK(top_entity_);
export_node->Bind(*this, top_entity_->entity_node(), offset, hit_testable);
}
void SceneUpdateContext::AddExportNode(ExportNode* export_node) {
export_nodes_.insert(export_node); // Might already have been added.
}
void SceneUpdateContext::RemoveExportNode(ExportNode* export_node) {
export_nodes_.erase(export_node);
}
// Helper function to generate clip planes for a scenic::EntityNode.
static void SetEntityNodeClipPlanes(scenic::EntityNode* entity_node,
const SkRect& bounds) {
......@@ -79,6 +49,12 @@ static void SetEntityNodeClipPlanes(scenic::EntityNode* entity_node,
entity_node->SetClipPlanes(std::move(clip_planes));
}
SceneUpdateContext::SceneUpdateContext(scenic::Session* session,
SurfaceProducer* surface_producer)
: session_(session), surface_producer_(surface_producer) {
FML_DCHECK(surface_producer_ != nullptr);
}
void SceneUpdateContext::CreateFrame(
std::unique_ptr<scenic::EntityNode> entity_node,
std::unique_ptr<scenic::ShapeNode> shape_node,
......@@ -256,21 +232,6 @@ SceneUpdateContext::Entity::~Entity() {
context_.top_entity_ = previous_entity_;
}
SceneUpdateContext::Clip::Clip(SceneUpdateContext& context,
scenic::Shape& shape,
const SkRect& shape_bounds)
: Entity(context) {
shape_node().SetShape(shape);
shape_node().SetTranslationRH(
shape_bounds.width() * 0.5f + shape_bounds.left(),
shape_bounds.height() * 0.5f + shape_bounds.top(), 0.f);
entity_node().SetClip(0u, true /* clip to self */);
SetEntityNodeClipPlanes(&entity_node(), shape_bounds);
}
SceneUpdateContext::Clip::~Clip() = default;
SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
const SkMatrix& transform)
: Entity(context),
......@@ -283,7 +244,7 @@ SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
if (decomposition.IsValid()) {
entity_node().SetTranslation(decomposition.translation().x(), //
decomposition.translation().y(), //
decomposition.translation().z() //
-decomposition.translation().z() //
);
entity_node().SetScale(decomposition.scale().x(), //
......@@ -347,4 +308,17 @@ void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) {
paint_bounds_.join(layer->paint_bounds());
}
SceneUpdateContext::Clip::Clip(SceneUpdateContext& context,
scenic::Shape& shape,
const SkRect& shape_bounds)
: Entity(context) {
shape_node().SetShape(shape);
shape_node().SetTranslationRH(
shape_bounds.width() * 0.5f + shape_bounds.left(),
shape_bounds.height() * 0.5f + shape_bounds.top(), 0.f);
entity_node().SetClip(0u, true /* clip to self */);
SetEntityNodeClipPlanes(&entity_node(), shape_bounds);
}
} // namespace flow
......@@ -11,7 +11,6 @@
#include "flutter/flow/compositor_context.h"
#include "flutter/flow/raster_cache_key.h"
#include "flutter/fml/build_config.h"
#include "flutter/fml/compiler_specific.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/macros.h"
......@@ -22,8 +21,6 @@
namespace flow {
class Layer;
class ExportNodeHolder;
class ExportNode;
class SceneUpdateContext {
public:
......@@ -93,14 +90,6 @@ class SceneUpdateContext {
std::unique_ptr<scenic::ShapeNode> shape_node_ptr_;
};
class Clip : public Entity {
public:
Clip(SceneUpdateContext& context,
scenic::Shape& shape,
const SkRect& shape_bounds);
~Clip();
};
class Transform : public Entity {
public:
Transform(SceneUpdateContext& context, const SkMatrix& transform);
......@@ -139,10 +128,17 @@ class SceneUpdateContext {
Layer* layer_;
};
class Clip : public Entity {
public:
Clip(SceneUpdateContext& context,
scenic::Shape& shape,
const SkRect& shape_bounds);
~Clip() = default;
};
SceneUpdateContext(scenic::Session* session,
SurfaceProducer* surface_producer);
~SceneUpdateContext();
~SceneUpdateContext() = default;
scenic::Session* session() { return session_; }
......@@ -154,18 +150,6 @@ class SceneUpdateContext {
}
const fuchsia::ui::gfx::MetricsPtr& metrics() const { return metrics_; }
void AddChildScene(ExportNode* export_node,
SkPoint offset,
bool hit_testable);
// Adds reference to |export_node| so we can call export_node->Dispose() in
// our destructor. Caller is responsible for calling RemoveExportNode() before
// |export_node| is destroyed.
void AddExportNode(ExportNode* export_node);
// Removes reference to |export_node|.
void RemoveExportNode(ExportNode* export_node);
// TODO(chinmaygarde): This method must submit the surfaces as soon as paint
// tasks are done. However, given that there is no support currently for
// Vulkan semaphores, we need to submit all the surfaces after an explicit
......@@ -243,9 +227,6 @@ class SceneUpdateContext {
std::vector<PaintTask> paint_tasks_;
// Save ExportNodes so we can dispose them in our destructor.
std::set<ExportNode*> export_nodes_;
FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext);
};
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/view_holder.h"
#include "flutter/fml/thread_local.h"
namespace {
using ViewHolderBindings =
std::unordered_map<zx_koid_t, std::unique_ptr<flow::ViewHolder>>;
FML_THREAD_LOCAL fml::ThreadLocal tls_view_holder_bindings([](intptr_t value) {
delete reinterpret_cast<ViewHolderBindings*>(value);
});
fuchsia::ui::gfx::ViewProperties ToViewProperties(float width,
float height,
float insetTop,
float insetRight,
float insetBottom,
float insetLeft,
bool focusable) {
return fuchsia::ui::gfx::ViewProperties({
.bounding_box = fuchsia::ui::gfx::BoundingBox({
.min = fuchsia::ui::gfx::vec3({
.x = 0.f,
.y = 0.f,
.z = -1000.f,
}),
.max = fuchsia::ui::gfx::vec3({.x = width, .y = height, .z = 0.f}),
}),
.inset_from_min = fuchsia::ui::gfx::vec3({
.x = insetLeft,
.y = insetTop,
.z = 0.f,
}),
.inset_from_max = fuchsia::ui::gfx::vec3({
.x = insetRight,
.y = insetBottom,
.z = 0.f,
}),
.focus_change = focusable,
});
}
} // namespace
namespace flow {
ViewHolder::ViewHolder(fml::RefPtr<fml::TaskRunner> ui_task_runner,
fuchsia::ui::views::ViewHolderToken view_holder_token,
BindCallback on_bind_callback)
: pending_view_holder_token_(std::move(view_holder_token)),
ui_task_runner_(std::move(ui_task_runner)),
pending_bind_callback_(std::move(on_bind_callback)) {
FML_DCHECK(pending_view_holder_token_.value);
FML_DCHECK(ui_task_runner_);
}
void ViewHolder::Create(zx_koid_t id,
fml::RefPtr<fml::TaskRunner> ui_task_runner,
fuchsia::ui::views::ViewHolderToken view_holder_token,
BindCallback on_bind_callback) {
// This GPU thread contains at least 1 ViewHolder. Initialize the per-thread
// bindings.
if (tls_view_holder_bindings.Get() == 0) {
tls_view_holder_bindings.Set(
reinterpret_cast<intptr_t>(new ViewHolderBindings()));
}
auto* bindings =
reinterpret_cast<ViewHolderBindings*>(tls_view_holder_bindings.Get());
FML_DCHECK(bindings);
FML_DCHECK(bindings->find(id) == bindings->end());
auto view_holder = std::unique_ptr<ViewHolder>(
new ViewHolder(std::move(ui_task_runner), std::move(view_holder_token),
std::move(on_bind_callback)));
bindings->emplace(id, std::move(view_holder));
}
void ViewHolder::Destroy(zx_koid_t id) {
auto* bindings =
reinterpret_cast<ViewHolderBindings*>(tls_view_holder_bindings.Get());
FML_DCHECK(bindings);
bindings->erase(id);
}
ViewHolder* ViewHolder::FromId(zx_koid_t id) {
auto* bindings =
reinterpret_cast<ViewHolderBindings*>(tls_view_holder_bindings.Get());
if (!bindings) {
return nullptr;
}
auto binding = bindings->find(id);
if (binding == bindings->end()) {
return nullptr;
}
return binding->second.get();
}
void ViewHolder::UpdateScene(SceneUpdateContext& context,
const SkPoint& offset,
const SkSize& size,
bool hit_testable) {
if (pending_view_holder_token_.value) {
entity_node_ = std::make_unique<scenic::EntityNode>(context.session());
view_holder_ = std::make_unique<scenic::ViewHolder>(
context.session(), std::move(pending_view_holder_token_),
"Flutter SceneHost");
entity_node_->Attach(*view_holder_);
ui_task_runner_->PostTask(
[bind_callback = std::move(pending_bind_callback_),
view_holder_id = view_holder_->id()]() {
bind_callback(view_holder_id);
});
}
FML_DCHECK(entity_node_);
FML_DCHECK(view_holder_);
context.top_entity()->entity_node().AddChild(*entity_node_);
entity_node_->SetTranslation(offset.x(), offset.y(), -0.1f);
entity_node_->SetHitTestBehavior(
hit_testable ? fuchsia::ui::gfx::HitTestBehavior::kDefault
: fuchsia::ui::gfx::HitTestBehavior::kSuppress);
if (has_pending_properties_) {
view_holder_->SetViewProperties(std::move(pending_properties_));
has_pending_properties_ = false;
}
}
void ViewHolder::SetProperties(double width,
double height,
double insetTop,
double insetRight,
double insetBottom,
double insetLeft,
bool focusable) {
pending_properties_ = ToViewProperties(width, height, insetTop, insetRight,
insetBottom, insetLeft, focusable);
has_pending_properties_ = true;
}
} // namespace flow
// 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_FLOW_VIEW_HOLDER_H_
#define FLUTTER_FLOW_VIEW_HOLDER_H_
#include <fuchsia/ui/gfx/cpp/fidl.h>
#include <fuchsia/ui/views/cpp/fidl.h>
#include <lib/ui/scenic/cpp/id.h>
#include <lib/ui/scenic/cpp/resources.h>
#include <third_party/skia/include/core/SkMatrix.h>
#include <third_party/skia/include/core/SkPoint.h>
#include <third_party/skia/include/core/SkSize.h>
#include <zircon/types.h>
#include <memory>
#include "flutter/flow/scene_update_context.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/memory/ref_counted.h"
#include "flutter/fml/task_runner.h"
namespace flow {
// Represents a Scenic |ViewHolder| resource that imports a |View| from another
// session.
//
// This object is created and destroyed on the |Rasterizer|'s thread.
class ViewHolder {
public:
using BindCallback = std::function<void(scenic::ResourceId)>;
static void Create(zx_koid_t id,
fml::RefPtr<fml::TaskRunner> ui_task_runner,
fuchsia::ui::views::ViewHolderToken view_holder_token,
BindCallback on_bind_callback);
static void Destroy(zx_koid_t id);
static ViewHolder* FromId(zx_koid_t id);
// Sets the properties of the child view by issuing a Scenic command.
void SetProperties(double width,
double height,
double insetTop,
double insetRight,
double insetBottom,
double insetLeft,
bool focusable);
// Creates or updates the contained ViewHolder resource using the specified
// |SceneUpdateContext|.
void UpdateScene(SceneUpdateContext& context,
const SkPoint& offset,
const SkSize& size,
bool hit_testable);
private:
ViewHolder(fml::RefPtr<fml::TaskRunner> ui_task_runner,
fuchsia::ui::views::ViewHolderToken view_holder_token,
BindCallback on_bind_callback);
fuchsia::ui::gfx::ViewProperties pending_properties_;
fuchsia::ui::views::ViewHolderToken pending_view_holder_token_;
fml::RefPtr<fml::TaskRunner> ui_task_runner_;
std::unique_ptr<scenic::EntityNode> entity_node_;
std::unique_ptr<scenic::ViewHolder> view_holder_;
BindCallback pending_bind_callback_;
bool has_pending_properties_ = false;
FML_DISALLOW_COPY_AND_ASSIGN(ViewHolder);
};
} // namespace flow
#endif // FLUTTER_FLOW_VIEW_HOLDER_H_
......@@ -33,8 +33,8 @@
// mostly explains a.), c. it makes parsing "subcommands", like "my_program
// --flag_for_my_program subcommand --flag_for_subcommand" saner.
#ifndef LIB_FXL_COMMAND_LINE_H_
#define LIB_FXL_COMMAND_LINE_H_
#ifndef LIB_FML_COMMAND_LINE_H_
#define LIB_FML_COMMAND_LINE_H_
#include <stddef.h>
......@@ -239,4 +239,4 @@ std::vector<std::string> CommandLineToArgv(const CommandLine& command_line);
} // namespace fml
#endif // LIB_FXL_COMMAND_LINE_H_
#endif // LIB_FML_COMMAND_LINE_H_
......@@ -318,6 +318,11 @@ class SceneHost {
///
/// The scene host takes ownership of the provided export token handle.
SceneHost(dynamic exportTokenHandle);
SceneHost.fromViewHolderToken(
dynamic viewHolderTokenHandle,
void Function() viewConnectedCallback,
void Function() viewDisconnectedCallback,
void Function(bool) viewStateChangedCallback);
/// Releases the resources associated with the child scene host.
///
......@@ -325,4 +330,17 @@ class SceneHost {
void dispose() {
throw UnimplementedError();
}
/// Set properties on the linked scene. These properties include its bounds,
/// as well as whether it can be the target of focus events or not.
void setProperties(
double width,
double height,
double insetTop,
double insetRight,
double insetBottom,
double insetLeft,
bool focusable) {
throw UnimplementedError();
}
}
......@@ -8,8 +8,6 @@ source_set("ui") {
"compositing/scene.h",
"compositing/scene_builder.cc",
"compositing/scene_builder.h",
"compositing/scene_host.cc",
"compositing/scene_host.h",
"dart_runtime_hooks.cc",
"dart_runtime_hooks.h",
"dart_ui.cc",
......@@ -115,11 +113,17 @@ source_set("ui") {
"//third_party/skia",
"//third_party/tonic",
]
if (is_fuchsia) {
deps += [ "//topaz/public/dart-pkg/zircon" ]
}
public_deps = [
"$flutter_root/third_party/txt",
]
if (is_fuchsia) {
sources += [
"compositing/scene_host.cc",
"compositing/scene_host.h",
]
deps += [ "//topaz/public/dart-pkg/zircon" ]
}
}
......@@ -376,10 +376,37 @@ class SceneHost extends NativeFieldWrapperClass2 {
SceneHost(dynamic exportTokenHandle) {
_constructor(exportTokenHandle);
}
SceneHost.fromViewHolderToken(
dynamic viewHolderTokenHandle,
void Function() viewConnectedCallback,
void Function() viewDisconnectedCallback,
void Function(bool) viewStateChangedCallback) {
_constructorViewHolderToken(viewHolderTokenHandle, viewConnectedCallback,
viewDisconnectedCallback, viewStateChangedCallback);
}
void _constructor(dynamic exportTokenHandle) native 'SceneHost_constructor';
void
_constructorViewHolderToken(
dynamic viewHolderTokenHandle,
void Function() viewConnectedCallback,
void Function() viewDisconnectedCallback,
void Function(bool) viewStateChangedCallback)
native 'SceneHost_constructorViewHolderToken';
/// Releases the resources associated with the child scene host.
///
/// After calling this function, the child scene host cannot be used further.
void dispose() native 'SceneHost_dispose';
/// Set properties on the linked scene. These properties include its bounds,
/// as well as whether it can be the target of focus events or not.
void setProperties(
double width,
double height,
double insetTop,
double insetRight,
double insetBottom,
double insetLeft,
bool focusable) native 'SceneHost_setProperties';
}
......@@ -5,9 +5,6 @@
#include "flutter/lib/ui/compositing/scene_builder.h"
#include "flutter/flow/layers/backdrop_filter_layer.h"
#if defined(OS_FUCHSIA)
#include "flutter/flow/layers/child_scene_layer.h"
#endif
#include "flutter/flow/layers/clip_path_layer.h"
#include "flutter/flow/layers/clip_rect_layer.h"
#include "flutter/flow/layers/clip_rrect_layer.h"
......@@ -34,6 +31,10 @@
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"
#if defined(OS_FUCHSIA)
#include "flutter/flow/layers/child_scene_layer.h"
#endif
namespace blink {
static void SceneBuilder_constructor(Dart_NativeArguments args) {
......@@ -58,7 +59,6 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, SceneBuilder);
V(SceneBuilder, addRetained) \
V(SceneBuilder, addPicture) \
V(SceneBuilder, addTexture) \
V(SceneBuilder, addChildScene) \
V(SceneBuilder, addPerformanceOverlay) \
V(SceneBuilder, setRasterizerTracingThreshold) \
V(SceneBuilder, setCheckerboardOffscreenLayers) \
......@@ -66,11 +66,18 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, SceneBuilder);
V(SceneBuilder, build)
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
#if defined(OS_FUCHSIA)
DART_NATIVE_CALLBACK(SceneBuilder, addChildScene)
#endif
void SceneBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register(
{{"SceneBuilder_constructor", SceneBuilder_constructor, 1, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
natives->Register({
{"SceneBuilder_constructor", SceneBuilder_constructor, 1, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)
#if defined(OS_FUCHSIA)
DART_REGISTER_NATIVE(SceneBuilder, addChildScene)
#endif
});
}
SceneBuilder::SceneBuilder() = default;
......@@ -251,24 +258,22 @@ void SceneBuilder::addPlatformView(double dx,
current_layer_->Add(std::move(layer));
}
#if defined(OS_FUCHSIA)
void SceneBuilder::addChildScene(double dx,
double dy,
double width,
double height,
SceneHost* sceneHost,
bool hitTestable) {
#if defined(OS_FUCHSIA)
if (!current_layer_) {
return;
}
auto layer = std::make_unique<flow::ChildSceneLayer>();
layer->set_offset(SkPoint::Make(dx, dy));
layer->set_size(SkSize::Make(width, height));
layer->set_export_node_holder(sceneHost->export_node_holder());
layer->set_hit_testable(hitTestable);
auto layer = std::make_unique<flow::ChildSceneLayer>(
sceneHost->id(), sceneHost->use_view_holder(), SkPoint::Make(dx, dy),
SkSize::Make(width, height), hitTestable);
current_layer_->Add(std::move(layer));
#endif // defined(OS_FUCHSIA)
}
#endif // defined(OS_FUCHSIA)
void SceneBuilder::addPerformanceOverlay(uint64_t enabledOptions,
double left,
......
......@@ -6,11 +6,11 @@
#define FLUTTER_LIB_UI_COMPOSITING_SCENE_BUILDER_H_
#include <stdint.h>
#include <memory>
#include <stack>
#include "flutter/lib/ui/compositing/scene.h"
#include "flutter/lib/ui/compositing/scene_host.h"
#include "flutter/lib/ui/dart_wrapper.h"
#include "flutter/lib/ui/painting/engine_layer.h"
#include "flutter/lib/ui/painting/image_filter.h"
......@@ -20,6 +20,10 @@
#include "flutter/lib/ui/painting/shader.h"
#include "third_party/tonic/typed_data/float64_list.h"
#if defined(OS_FUCHSIA)
#include "flutter/lib/ui/compositing/scene_host.h"
#endif
namespace blink {
class SceneBuilder : public RefCountedDartWrappable<SceneBuilder> {
......@@ -83,12 +87,14 @@ class SceneBuilder : public RefCountedDartWrappable<SceneBuilder> {
double height,
int64_t viewId);
#if defined(OS_FUCHSIA)
void addChildScene(double dx,
double dy,
double width,
double height,
SceneHost* sceneHost,
bool hitTestable);
#endif
void setRasterizerTracingThreshold(uint32_t frameInterval);
......
......@@ -4,52 +4,226 @@
#include "flutter/lib/ui/compositing/scene_host.h"
#include <lib/fsl/handles/object_info.h>
#include <lib/ui/scenic/cpp/view_token_pair.h>
#include <lib/zx/eventpair.h>
#include <third_party/tonic/dart_args.h>
#include <third_party/tonic/dart_binding_macros.h>
#include <third_party/tonic/logging/dart_invoke.h>
#include "flutter/flow/export_node.h"
#include "flutter/flow/view_holder.h"
#include "flutter/fml/thread_local.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "third_party/tonic/dart_args.h"
#include "third_party/tonic/dart_binding_macros.h"
#include "third_party/tonic/dart_library_natives.h"
#ifdef OS_FUCHSIA
#include "dart-pkg/zircon/sdk_ext/handle.h"
#endif
namespace {
namespace blink {
using SceneHostBindings = std::unordered_map<zx_koid_t, blink::SceneHost*>;
FML_THREAD_LOCAL fml::ThreadLocal tls_scene_host_bindings([](intptr_t value) {
delete reinterpret_cast<SceneHostBindings*>(value);
});
void SceneHost_constructor(Dart_NativeArguments args) {
tonic::DartCallConstructor(&blink::SceneHost::Create, args);
}
void SceneHost_constructorViewHolderToken(Dart_NativeArguments args) {
// This UI thread / Isolate contains at least 1 SceneHost. Initialize the
// per-Isolate bindings.
if (tls_scene_host_bindings.Get() == 0) {
tls_scene_host_bindings.Set(
reinterpret_cast<intptr_t>(new SceneHostBindings()));
}
tonic::DartCallConstructor(&blink::SceneHost::CreateViewHolder, args);
}
blink::SceneHost* GetSceneHost(scenic::ResourceId id) {
auto* bindings =
reinterpret_cast<SceneHostBindings*>(tls_scene_host_bindings.Get());
FML_DCHECK(bindings);
auto binding = bindings->find(id);
if (binding != bindings->end()) {
return binding->second;
}
static void SceneHost_constructor(Dart_NativeArguments args) {
DartCallConstructor(&SceneHost::create, args);
return nullptr;
}
void InvokeDartClosure(tonic::DartPersistentValue* closure) {
if (closure) {
std::shared_ptr<tonic::DartState> dart_state = closure->dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
tonic::DartInvoke(closure->value(), {});
}
}
template <typename T>
void InvokeDartFunction(tonic::DartPersistentValue* function, T& arg) {
if (function) {
std::shared_ptr<tonic::DartState> dart_state =
function->dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
tonic::DartInvoke(function->value(), {tonic::ToDart(arg)});
}
}
} // namespace
namespace blink {
IMPLEMENT_WRAPPERTYPEINFO(ui, SceneHost);
#define FOR_EACH_BINDING(V) V(SceneHost, dispose)
#define FOR_EACH_BINDING(V) \
V(SceneHost, dispose) \
V(SceneHost, setProperties)
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
void SceneHost::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({{"SceneHost_constructor", SceneHost_constructor, 2, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
natives->Register({{"SceneHost_constructorViewHolderToken",
SceneHost_constructorViewHolderToken, 5, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
}
#if defined(OS_FUCHSIA)
fml::RefPtr<SceneHost> SceneHost::create(
fml::RefPtr<zircon::dart::Handle> export_token_handle) {
return fml::MakeRefCounted<SceneHost>(export_token_handle);
fml::RefPtr<SceneHost> SceneHost::Create(
fml::RefPtr<zircon::dart::Handle> exportTokenHandle) {
return fml::MakeRefCounted<SceneHost>(exportTokenHandle);
}
SceneHost::SceneHost(fml::RefPtr<zircon::dart::Handle> export_token_handle) {
export_node_holder_ = fml::MakeRefCounted<flow::ExportNodeHolder>(
blink::UIDartState::Current()->GetTaskRunners().GetGPUTaskRunner(),
export_token_handle);
fml::RefPtr<SceneHost> SceneHost::CreateViewHolder(
fml::RefPtr<zircon::dart::Handle> viewHolderTokenHandle,
Dart_Handle viewConnectedCallback,
Dart_Handle viewDisconnectedCallback,
Dart_Handle viewStateChangedCallback) {
return fml::MakeRefCounted<SceneHost>(
viewHolderTokenHandle, viewConnectedCallback, viewDisconnectedCallback,
viewStateChangedCallback);
}
#else
fml::RefPtr<SceneHost> SceneHost::create(Dart_Handle export_token_handle) {
return fml::MakeRefCounted<SceneHost>(export_token_handle);
SceneHost::SceneHost(fml::RefPtr<zircon::dart::Handle> exportTokenHandle)
: gpu_task_runner_(
UIDartState::Current()->GetTaskRunners().GetGPUTaskRunner()),
id_(fsl::GetKoid(exportTokenHandle->handle())),
use_view_holder_(false) {
gpu_task_runner_->PostTask(
[id = id_, handle = std::move(exportTokenHandle)]() {
auto export_token = zx::eventpair(handle->ReleaseHandle());
flow::ExportNode::Create(id, std::move(export_token));
});
}
SceneHost::SceneHost(fml::RefPtr<zircon::dart::Handle> viewHolderTokenHandle,
Dart_Handle viewConnectedCallback,
Dart_Handle viewDisconnectedCallback,
Dart_Handle viewStateChangedCallback)
: gpu_task_runner_(
UIDartState::Current()->GetTaskRunners().GetGPUTaskRunner()),
id_(fsl::GetKoid(viewHolderTokenHandle->handle())),
use_view_holder_(true) {
if (Dart_IsClosure(viewConnectedCallback)) {
view_connected_callback_ = std::make_unique<tonic::DartPersistentValue>(
UIDartState::Current(), viewConnectedCallback);
}
if (Dart_IsClosure(viewDisconnectedCallback)) {
view_disconnected_callback_ = std::make_unique<tonic::DartPersistentValue>(
UIDartState::Current(), viewDisconnectedCallback);
}
if (Dart_IsClosure(viewConnectedCallback)) {
view_state_changed_callback_ = std::make_unique<tonic::DartPersistentValue>(
UIDartState::Current(), viewStateChangedCallback);
}
auto bind_callback = [scene_host = this](scenic::ResourceId id) {
auto* bindings =
reinterpret_cast<SceneHostBindings*>(tls_scene_host_bindings.Get());
FML_DCHECK(bindings);
FML_DCHECK(bindings->find(id) == bindings->end());
bindings->emplace(std::make_pair(id, scene_host));
};
auto ui_task_runner =
UIDartState::Current()->GetTaskRunners().GetUITaskRunner();
gpu_task_runner_->PostTask([id = id_,
ui_task_runner = std::move(ui_task_runner),
handle = std::move(viewHolderTokenHandle),
bind_callback = std::move(bind_callback)]() {
auto view_holder_token =
scenic::ToViewHolderToken(zx::eventpair(handle->ReleaseHandle()));
flow::ViewHolder::Create(id, std::move(ui_task_runner),
std::move(view_holder_token),
std::move(bind_callback));
});
}
SceneHost::~SceneHost() {
if (use_view_holder_) {
auto* bindings =
reinterpret_cast<SceneHostBindings*>(tls_scene_host_bindings.Get());
FML_DCHECK(bindings);
bindings->erase(id_);
gpu_task_runner_->PostTask([id = id_]() { flow::ViewHolder::Destroy(id); });
} else {
gpu_task_runner_->PostTask([id = id_]() { flow::ExportNode::Destroy(id); });
}
}
void SceneHost::OnViewConnected(scenic::ResourceId id) {
auto* scene_host = GetSceneHost(id);
if (scene_host) {
InvokeDartClosure(scene_host->view_connected_callback_.get());
}
}
void SceneHost::OnViewDisconnected(scenic::ResourceId id) {
auto* scene_host = GetSceneHost(id);
if (scene_host) {
InvokeDartClosure(scene_host->view_disconnected_callback_.get());
}
}
SceneHost::SceneHost(Dart_Handle export_token_handle) {}
#endif
void SceneHost::OnViewStateChanged(scenic::ResourceId id, bool state) {
auto* scene_host = GetSceneHost(id);
SceneHost::~SceneHost() {}
if (scene_host) {
InvokeDartFunction(scene_host->view_state_changed_callback_.get(), state);
}
}
void SceneHost::setProperties(double width,
double height,
double insetTop,
double insetRight,
double insetBottom,
double insetLeft,
bool focusable) {
FML_DCHECK(use_view_holder_);
gpu_task_runner_->PostTask([id = id_, width, height, insetTop, insetRight,
insetBottom, insetLeft, focusable]() {
auto* view_holder = flow::ViewHolder::FromId(id);
FML_DCHECK(view_holder);
view_holder->SetProperties(width, height, insetTop, insetRight, insetBottom,
insetLeft, focusable);
});
}
void SceneHost::dispose() {
ClearDartWrapper();
......
......@@ -5,19 +5,17 @@
#ifndef FLUTTER_LIB_UI_COMPOSITING_SCENE_HOST_H_
#define FLUTTER_LIB_UI_COMPOSITING_SCENE_HOST_H_
#include <dart-pkg/zircon/sdk_ext/handle.h>
#include <lib/ui/scenic/cpp/id.h>
#include <stdint.h>
#include <third_party/tonic/dart_library_natives.h>
#include <third_party/tonic/dart_persistent_value.h>
#include <zircon/types.h>
#include "flutter/fml/build_config.h"
#include "flutter/fml/memory/ref_counted.h"
#include "flutter/fml/task_runner.h"
#include "flutter/lib/ui/dart_wrapper.h"
#if defined(OS_FUCHSIA)
#include "flutter/flow/export_node.h"
#endif
namespace tonic {
class DartLibraryNatives;
} // namespace tonic
namespace blink {
class SceneHost : public RefCountedDartWrappable<SceneHost> {
......@@ -25,35 +23,45 @@ class SceneHost : public RefCountedDartWrappable<SceneHost> {
FML_FRIEND_MAKE_REF_COUNTED(SceneHost);
public:
#if defined(OS_FUCHSIA)
static fml::RefPtr<SceneHost> create(
fml::RefPtr<zircon::dart::Handle> export_token_handle);
#else
static fml::RefPtr<SceneHost> create(Dart_Handle export_token_handle);
#endif
~SceneHost() override;
#if defined(OS_FUCHSIA)
const fml::RefPtr<flow::ExportNodeHolder>& export_node_holder() const {
return export_node_holder_;
}
#endif
void dispose();
static void RegisterNatives(tonic::DartLibraryNatives* natives);
static fml::RefPtr<SceneHost> Create(
fml::RefPtr<zircon::dart::Handle> exportTokenHandle);
static fml::RefPtr<SceneHost> CreateViewHolder(
fml::RefPtr<zircon::dart::Handle> viewHolderTokenHandle,
Dart_Handle viewConnectedCallback,
Dart_Handle viewDisconnectedCallback,
Dart_Handle viewStateChangedCallback);
static void OnViewConnected(scenic::ResourceId id);
static void OnViewDisconnected(scenic::ResourceId id);
static void OnViewStateChanged(scenic::ResourceId id, bool state);
zx_koid_t id() const { return id_; }
bool use_view_holder() const { return use_view_holder_; }
void setProperties(double width,
double height,
double insetTop,
double insetRight,
double insetBottom,
double insetLeft,
bool focusable);
void dispose();
private:
#if defined(OS_FUCHSIA)
fml::RefPtr<flow::ExportNodeHolder> export_node_holder_;
#endif
#if defined(OS_FUCHSIA)
explicit SceneHost(fml::RefPtr<zircon::dart::Handle> export_token_handle);
#else
explicit SceneHost(Dart_Handle export_token_handle);
#endif
explicit SceneHost(fml::RefPtr<zircon::dart::Handle> exportTokenHandle);
SceneHost(fml::RefPtr<zircon::dart::Handle> viewHolderTokenHandle,
Dart_Handle viewConnectedCallback,
Dart_Handle viewDisconnectedCallback,
Dart_Handle viewStateChangedCallback);
fml::RefPtr<fml::TaskRunner> gpu_task_runner_;
std::unique_ptr<tonic::DartPersistentValue> view_connected_callback_;
std::unique_ptr<tonic::DartPersistentValue> view_disconnected_callback_;
std::unique_ptr<tonic::DartPersistentValue> view_state_changed_callback_;
zx_koid_t id_ = ZX_KOID_INVALID;
bool use_view_holder_ = false;
};
} // namespace blink
......
......@@ -32,6 +32,10 @@
#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/logging/dart_error.h"
#if defined(OS_FUCHSIA)
#include "flutter/lib/ui/compositing/scene_host.h"
#endif
using tonic::ToDart;
namespace blink {
......@@ -85,12 +89,14 @@ void DartUI::InitForGlobal() {
PictureRecorder::RegisterNatives(g_natives);
Scene::RegisterNatives(g_natives);
SceneBuilder::RegisterNatives(g_natives);
SceneHost::RegisterNatives(g_natives);
SemanticsUpdate::RegisterNatives(g_natives);
SemanticsUpdateBuilder::RegisterNatives(g_natives);
Versions::RegisterNatives(g_natives);
Vertices::RegisterNatives(g_natives);
Window::RegisterNatives(g_natives);
#if defined(OS_FUCHSIA)
SceneHost::RegisterNatives(g_natives);
#endif
// Secondary isolates do not provide UI-related APIs.
g_natives_secondary = new tonic::DartLibraryNatives();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册