siyao
4 年前
当前提交
6715fa3d
共有 139 个文件被更改,包括 7574 次插入 和 1243 次删除
-
7Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/gallery/app.cs
-
18com.unity.uiwidgets/Runtime/async/priority_queue.cs
-
2com.unity.uiwidgets/Runtime/debugger/inspector_service.cs
-
55com.unity.uiwidgets/Runtime/foundation/binding.cs
-
20com.unity.uiwidgets/Runtime/foundation/constants.cs
-
20com.unity.uiwidgets/Runtime/foundation/debug.cs
-
4com.unity.uiwidgets/Runtime/material/slider.cs
-
5com.unity.uiwidgets/Runtime/painting/image_stream.cs
-
12com.unity.uiwidgets/Runtime/scheduler/binding.cs
-
16com.unity.uiwidgets/Runtime/ui2/compositing.cs
-
21com.unity.uiwidgets/Runtime/ui2/hooks.cs
-
2com.unity.uiwidgets/Runtime/ui2/native_bindings.cs
-
43com.unity.uiwidgets/Runtime/ui2/painting.cs
-
1com.unity.uiwidgets/Runtime/ui2/window.cs
-
93engine/Build.bee.cs
-
7engine/src/common/settings.h
-
10engine/src/engine.cc
-
14engine/src/flow/skia_gpu_object.cc
-
2engine/src/flow/skia_gpu_object.h
-
2engine/src/lib/ui/compositing/scene.cc
-
35engine/src/lib/ui/compositing/scene_builder.cc
-
2engine/src/lib/ui/compositing/scene_builder.h
-
49engine/src/lib/ui/painting/gradient.cc
-
7engine/src/lib/ui/painting/image_decoder.cc
-
13engine/src/lib/ui/painting/image_decoder.h
-
18engine/src/lib/ui/ui_mono_state.cc
-
10engine/src/lib/ui/window/window.cc
-
2engine/src/lib/ui/window/window.h
-
65engine/src/runtime/mono_api.cc
-
4engine/src/runtime/mono_api.h
-
108engine/src/runtime/runtime_controller.cc
-
30engine/src/runtime/runtime_controller.h
-
2engine/src/runtime/runtime_delegate.h
-
8engine/src/shell/common/animator.cc
-
229engine/src/shell/common/engine.cc
-
15engine/src/shell/common/engine.h
-
622engine/src/shell/common/shell.cc
-
335engine/src/shell/common/shell.h
-
2engine/src/shell/common/vsync_waiter.h
-
3com.unity.uiwidgets/Runtime/async2.meta
-
3com.unity.uiwidgets/Runtime/developer.meta
-
3com.unity.uiwidgets/Runtime/engine2.meta
-
3com.unity.uiwidgets/Runtime/scheduler2.meta
-
7engine/src/runtime/start_up.cc
-
9engine/src/runtime/start_up.h
-
194engine/src/shell/common/canvas_spy.cc
-
165engine/src/shell/common/canvas_spy.h
-
64engine/src/shell/common/run_configuration.cc
-
42engine/src/shell/common/run_configuration.h
-
24engine/third_party/Unity/IUnityUIWidgets.h
-
360com.unity.uiwidgets/Runtime/async2/future.cs
-
3com.unity.uiwidgets/Runtime/async2/future.cs.meta
-
812com.unity.uiwidgets/Runtime/async2/future_impl.cs
-
3com.unity.uiwidgets/Runtime/async2/future_impl.cs.meta
-
129com.unity.uiwidgets/Runtime/async2/schedule_microtask.cs
-
3com.unity.uiwidgets/Runtime/async2/schedule_microtask.cs.meta
-
142com.unity.uiwidgets/Runtime/async2/timer.cs
-
3com.unity.uiwidgets/Runtime/async2/timer.cs.meta
-
1001com.unity.uiwidgets/Runtime/async2/zone.cs
-
3com.unity.uiwidgets/Runtime/async2/zone.cs.meta
-
8com.unity.uiwidgets/Runtime/developer/extension.cs
-
3com.unity.uiwidgets/Runtime/developer/extension.cs.meta
-
11com.unity.uiwidgets/Runtime/developer/timeline.cs
-
3com.unity.uiwidgets/Runtime/developer/timeline.cs.meta
-
3com.unity.uiwidgets/Runtime/engine2/UIWidgetsPanel.cs.meta
-
155com.unity.uiwidgets/Runtime/engine2/UIWidgetsPanel.cs
-
674com.unity.uiwidgets/Runtime/scheduler2/binding.cs
-
3com.unity.uiwidgets/Runtime/scheduler2/binding.cs.meta
-
7com.unity.uiwidgets/Runtime/scheduler2/debug.cs
-
3com.unity.uiwidgets/Runtime/scheduler2/debug.cs.meta
-
30com.unity.uiwidgets/Runtime/scheduler2/priority.cs
-
3com.unity.uiwidgets/Runtime/scheduler2/priority.cs.meta
-
307com.unity.uiwidgets/Runtime/scheduler2/ticker.cs
-
3com.unity.uiwidgets/Runtime/scheduler2/ticker.cs.meta
-
48engine/src/assets/asset_manager.cc
-
35engine/src/assets/asset_manager.h
-
26engine/src/assets/asset_resolver.h
-
41engine/src/assets/directory_asset_bundle.cc
-
30engine/src/assets/directory_asset_bundle.h
-
14engine/src/shell/gpu/gpu_surface_delegate.h
-
328engine/src/shell/gpu/gpu_surface_gl.cc
-
68engine/src/shell/gpu/gpu_surface_gl.h
-
100engine/src/shell/gpu/gpu_surface_gl_delegate.cc
-
65engine/src/shell/gpu/gpu_surface_gl_delegate.h
-
90engine/src/shell/gpu/gpu_surface_software.cc
-
44engine/src/shell/gpu/gpu_surface_software.h
-
11engine/src/shell/gpu/gpu_surface_software_delegate.cc
-
22engine/src/shell/gpu/gpu_surface_software_delegate.h
-
1001engine/src/shell/platform/embedder/embedder.cc
-
459engine/src/shell/platform/embedder/embedder.h
-
252engine/src/shell/platform/embedder/embedder_engine.cc
-
84engine/src/shell/platform/embedder/embedder_engine.h
|
|||
namespace Unity.UIWidgets.foundation { |
|||
using System; |
|||
using RSG; |
|||
using Unity.UIWidgets.async2; |
|||
using Unity.UIWidgets.ui2; |
|||
|
|||
namespace Unity.UIWidgets.foundation { |
|||
public abstract class BindingBase { |
|||
protected BindingBase() { |
|||
D.assert(!_debugInitialized); |
|||
initInstances(); |
|||
D.assert(_debugInitialized); |
|||
} |
|||
|
|||
static bool _debugInitialized = false; |
|||
|
|||
public Window window => Window.instance; |
|||
|
|||
protected virtual void initInstances() { |
|||
D.assert(!_debugInitialized); |
|||
D.assert(() => { |
|||
_debugInitialized = true; |
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
protected bool locked => _lockCount > 0; |
|||
int _lockCount = 0; |
|||
|
|||
protected Future lockEvents(Func<Future> callback) { |
|||
developer.Timeline.startSync("Lock events"); |
|||
|
|||
D.assert(callback != null); |
|||
_lockCount += 1; |
|||
Future future = callback(); |
|||
D.assert(future != null, |
|||
() => |
|||
"The lockEvents() callback returned null; " + |
|||
"it should return a Promise that completes when the lock is to expire."); |
|||
future.whenComplete(() => { |
|||
_lockCount -= 1; |
|||
if (!locked) { |
|||
developer.Timeline.finishSync(); |
|||
unlocked(); |
|||
} |
|||
|
|||
return null; |
|||
}); |
|||
return future; |
|||
} |
|||
|
|||
protected virtual void unlocked() { |
|||
D.assert(!locked); |
|||
} |
|||
} |
|||
} |
|
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.foundation { |
|||
public class FoundationConstants { |
|||
public static bool kReleaseMode = !Debug.isDebugBuild; |
|||
public static class foundation_ { |
|||
public static readonly bool kReleaseMode = !Debug.isDebugBuild; |
|||
|
|||
#if UIWidgets_PROFILE
|
|||
public const bool kProfileMode = true; |
|||
#else
|
|||
public const bool kProfileMode = false; |
|||
#endif
|
|||
|
|||
public static readonly bool kDebugMode = !kReleaseMode && !kProfileMode; |
|||
|
|||
public const float precisionErrorTolerance = 1e-10f; |
|||
|
|||
#if UNITY_WEBGL
|
|||
public const bool kIsWeb = true; |
|||
#else
|
|||
public const bool kIsWeb = false; |
|||
#endif
|
|||
} |
|||
} |
|
|||
#pragma once |
|||
|
|||
#include <flutter/fml/concurrent_message_loop.h> |
|||
#include <flutter/fml/memory/weak_ptr.h> |
|||
|
|||
#include "common/task_runners.h" |
|||
#include "lib/ui/io_manager.h" |
|||
|
|||
public: |
|||
ImageDecoder( |
|||
TaskRunners runners, |
|||
std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner, |
|||
fml::WeakPtr<IOManager> io_manager); |
|||
|
|||
fml::WeakPtr<ImageDecoder> GetWeakPtr() const; |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
fileFormatVersion: 2 |
|||
guid: 0dd4bb15542f4ad9a7bef43d7defab5f |
|||
timeCreated: 1599458064 |
|
|||
fileFormatVersion: 2 |
|||
guid: 32b343b53c4d484f8c149742f9d0a919 |
|||
timeCreated: 1599028965 |
|
|||
fileFormatVersion: 2 |
|||
guid: ffb851d47f1449fa9ea328aa58f27be7 |
|||
timeCreated: 1597215228 |
|
|||
fileFormatVersion: 2 |
|||
guid: 1fea1ea4786b4e6bb65b233b11e8b2d5 |
|||
timeCreated: 1599458322 |
|
|||
#include "start_up.h"
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
int64_t engine_main_enter_ts = 0; |
|||
|
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include <stdint.h> |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
extern int64_t engine_main_enter_ts; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#include "canvas_spy.h"
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
CanvasSpy::CanvasSpy(SkCanvas* target_canvas) { |
|||
SkISize canvas_size = target_canvas->getBaseLayerSize(); |
|||
n_way_canvas_ = |
|||
std::make_unique<SkNWayCanvas>(canvas_size.width(), canvas_size.height()); |
|||
did_draw_canvas_ = std::make_unique<DidDrawCanvas>(canvas_size.width(), |
|||
canvas_size.height()); |
|||
n_way_canvas_->addCanvas(target_canvas); |
|||
n_way_canvas_->addCanvas(did_draw_canvas_.get()); |
|||
} |
|||
|
|||
SkCanvas* CanvasSpy::GetSpyingCanvas() { return n_way_canvas_.get(); } |
|||
|
|||
DidDrawCanvas::DidDrawCanvas(int width, int height) |
|||
: SkCanvasVirtualEnforcer<SkNoDrawCanvas>(width, height) {} |
|||
|
|||
DidDrawCanvas::~DidDrawCanvas() {} |
|||
|
|||
void DidDrawCanvas::MarkDrawIfNonTransparentPaint(const SkPaint& paint) { |
|||
bool isTransparent = paint.getAlpha() == 0; |
|||
did_draw_ |= !isTransparent; |
|||
} |
|||
|
|||
bool CanvasSpy::DidDrawIntoCanvas() { |
|||
return did_draw_canvas_->DidDrawIntoCanvas(); |
|||
} |
|||
|
|||
bool DidDrawCanvas::DidDrawIntoCanvas() { return did_draw_; } |
|||
|
|||
void DidDrawCanvas::willSave() {} |
|||
|
|||
SkCanvas::SaveLayerStrategy DidDrawCanvas::getSaveLayerStrategy( |
|||
const SaveLayerRec& rec) { |
|||
return kNoLayer_SaveLayerStrategy; |
|||
} |
|||
|
|||
bool DidDrawCanvas::onDoSaveBehind(const SkRect* bounds) { return false; } |
|||
|
|||
void DidDrawCanvas::willRestore() {} |
|||
|
|||
void DidDrawCanvas::didConcat(const SkMatrix& matrix) {} |
|||
|
|||
void DidDrawCanvas::didConcat44(const SkM44&) {} |
|||
|
|||
void DidDrawCanvas::didScale(SkScalar, SkScalar) {} |
|||
|
|||
void DidDrawCanvas::didTranslate(SkScalar, SkScalar) {} |
|||
|
|||
void DidDrawCanvas::didSetMatrix(const SkMatrix& matrix) {} |
|||
|
|||
void DidDrawCanvas::onClipRect(const SkRect& rect, SkClipOp op, |
|||
ClipEdgeStyle edgeStyle) {} |
|||
|
|||
void DidDrawCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, |
|||
ClipEdgeStyle edgeStyle) {} |
|||
|
|||
void DidDrawCanvas::onClipPath(const SkPath& path, SkClipOp op, |
|||
ClipEdgeStyle edgeStyle) {} |
|||
|
|||
void DidDrawCanvas::onClipRegion(const SkRegion& deviceRgn, SkClipOp op) {} |
|||
|
|||
void DidDrawCanvas::onDrawPaint(const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawBehind(const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawPoints(PointMode mode, size_t count, |
|||
const SkPoint pts[], const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawOval(const SkRect& rect, const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawArc(const SkRect& rect, SkScalar startAngle, |
|||
SkScalar sweepAngle, bool useCenter, |
|||
const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, |
|||
const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawImage(const SkImage* image, SkScalar left, |
|||
SkScalar top, const SkPaint* paint) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, |
|||
const SkRect& dst, const SkPaint* paint, |
|||
SrcRectConstraint constraint) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, |
|||
const SkRect& dst, const SkPaint* paint) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawImageLattice(const SkImage* image, |
|||
const Lattice& lattice, |
|||
const SkRect& dst, |
|||
const SkPaint* paint) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, |
|||
SkScalar y, const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawPicture(const SkPicture* picture, |
|||
const SkMatrix* matrix, |
|||
const SkPaint* paint) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawDrawable(SkDrawable* drawable, |
|||
const SkMatrix* matrix) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawVerticesObject(const SkVertices* vertices, |
|||
SkBlendMode bmode, |
|||
const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawPatch(const SkPoint cubics[12], |
|||
const SkColor colors[4], |
|||
const SkPoint texCoords[4], SkBlendMode bmode, |
|||
const SkPaint& paint) { |
|||
MarkDrawIfNonTransparentPaint(paint); |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawAtlas(const SkImage* image, const SkRSXform xform[], |
|||
const SkRect tex[], const SkColor colors[], |
|||
int count, SkBlendMode bmode, |
|||
const SkRect* cull, const SkPaint* paint) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawShadowRec(const SkPath& path, |
|||
const SkDrawShadowRec& rec) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawAnnotation(const SkRect& rect, const char key[], |
|||
SkData* data) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], |
|||
SkCanvas::QuadAAFlags aa, |
|||
const SkColor4f& color, SkBlendMode mode) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onDrawEdgeAAImageSet(const ImageSetEntry set[], int count, |
|||
const SkPoint dstClips[], |
|||
const SkMatrix preViewMatrices[], |
|||
const SkPaint* paint, |
|||
SrcRectConstraint constraint) { |
|||
did_draw_ = true; |
|||
} |
|||
|
|||
void DidDrawCanvas::onFlush() {} |
|||
|
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include "flutter/fml/macros.h" |
|||
#include "include/core/SkCanvas.h" |
|||
#include "include/core/SkCanvasVirtualEnforcer.h" |
|||
#include "include/utils/SkNWayCanvas.h" |
|||
#include "include/utils/SkNoDrawCanvas.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class DidDrawCanvas; |
|||
|
|||
class CanvasSpy { |
|||
public: |
|||
CanvasSpy(SkCanvas* target_canvas); |
|||
|
|||
bool DidDrawIntoCanvas(); |
|||
|
|||
SkCanvas* GetSpyingCanvas(); |
|||
|
|||
private: |
|||
std::unique_ptr<SkNWayCanvas> n_way_canvas_; |
|||
std::unique_ptr<DidDrawCanvas> did_draw_canvas_; |
|||
|
|||
FML_DISALLOW_COPY_AND_ASSIGN(CanvasSpy); |
|||
}; |
|||
|
|||
class DidDrawCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> { |
|||
public: |
|||
DidDrawCanvas(int width, int height); |
|||
~DidDrawCanvas() override; |
|||
bool DidDrawIntoCanvas(); |
|||
|
|||
private: |
|||
bool did_draw_ = false; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void willSave() override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
bool onDoSaveBehind(const SkRect*) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void willRestore() override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void didConcat(const SkMatrix&) override; |
|||
void didConcat44(const SkM44&) override; |
|||
void didScale(SkScalar, SkScalar) override; |
|||
void didTranslate(SkScalar, SkScalar) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void didSetMatrix(const SkMatrix&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, |
|||
const SkPaint& paint) override; |
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], |
|||
const SkPoint texCoords[4], SkBlendMode, |
|||
const SkPaint& paint) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawPaint(const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawBehind(const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawPoints(PointMode, size_t count, const SkPoint pts[], |
|||
const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawRect(const SkRect&, const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawRegion(const SkRegion&, const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawOval(const SkRect&, const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, |
|||
const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawRRect(const SkRRect&, const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawPath(const SkPath&, const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawImage(const SkImage*, SkScalar left, SkScalar top, |
|||
const SkPaint*) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, |
|||
const SkPaint*, SrcRectConstraint) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, |
|||
const SkPaint*) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, |
|||
const SkPaint*) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawVerticesObject(const SkVertices*, SkBlendMode, |
|||
const SkPaint&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], |
|||
const SkColor[], int, SkBlendMode, const SkRect*, |
|||
const SkPaint*) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onClipRegion(const SkRegion&, SkClipOp) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawPicture(const SkPicture*, const SkMatrix*, |
|||
const SkPaint*) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawDrawable(SkDrawable*, const SkMatrix*) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawAnnotation(const SkRect&, const char[], SkData*) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawEdgeAAQuad(const SkRect&, const SkPoint[4], SkCanvas::QuadAAFlags, |
|||
const SkColor4f&, SkBlendMode) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onDrawEdgeAAImageSet(const ImageSetEntry[], int count, const SkPoint[], |
|||
const SkMatrix[], const SkPaint*, |
|||
SrcRectConstraint) override; |
|||
|
|||
// |SkCanvasVirtualEnforcer<SkNoDrawCanvas>| |
|||
void onFlush() override; |
|||
|
|||
void MarkDrawIfNonTransparentPaint(const SkPaint& paint); |
|||
|
|||
FML_DISALLOW_COPY_AND_ASSIGN(DidDrawCanvas); |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#include "run_configuration.h"
|
|||
|
|||
#include <sstream>
|
|||
#include <utility>
|
|||
|
|||
#include "assets/directory_asset_bundle.h"
|
|||
#include "flutter/fml/file.h"
|
|||
#include "flutter/fml/unique_fd.h"
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
RunConfiguration RunConfiguration::InferFromSettings(const Settings& settings) { |
|||
auto asset_manager = std::make_shared<AssetManager>(); |
|||
|
|||
if (fml::UniqueFD::traits_type::IsValid(settings.assets_dir)) { |
|||
asset_manager->PushBack(std::make_unique<DirectoryAssetBundle>( |
|||
fml::Duplicate(settings.assets_dir))); |
|||
} |
|||
|
|||
asset_manager->PushBack( |
|||
std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory( |
|||
settings.assets_path.c_str(), false, fml::FilePermission::kRead))); |
|||
|
|||
return {asset_manager, settings.mono_entrypoint_callback}; |
|||
} |
|||
|
|||
RunConfiguration::RunConfiguration() |
|||
: RunConfiguration(std::make_shared<AssetManager>(), fml::closure()) {} |
|||
|
|||
RunConfiguration::RunConfiguration(std::shared_ptr<AssetManager> asset_manager, |
|||
fml::closure mono_entrypoint_callback) |
|||
: asset_manager_(std::move(asset_manager)), |
|||
mono_entrypoint_callback_(std::move(mono_entrypoint_callback)) {} |
|||
|
|||
RunConfiguration::RunConfiguration(RunConfiguration&&) = default; |
|||
|
|||
RunConfiguration::~RunConfiguration() = default; |
|||
|
|||
bool RunConfiguration::IsValid() const { |
|||
return static_cast<bool>(asset_manager_); |
|||
} |
|||
|
|||
bool RunConfiguration::AddAssetResolver( |
|||
std::unique_ptr<AssetResolver> resolver) { |
|||
if (!resolver || !resolver->IsValid()) { |
|||
return false; |
|||
} |
|||
|
|||
asset_manager_->PushBack(std::move(resolver)); |
|||
return true; |
|||
} |
|||
|
|||
std::shared_ptr<AssetManager> RunConfiguration::GetAssetManager() const { |
|||
return asset_manager_; |
|||
} |
|||
|
|||
void RunConfiguration::SetMonoEntrypointCallback(fml::closure mono_entrypoint_callback) { |
|||
mono_entrypoint_callback_ = std::move(mono_entrypoint_callback); |
|||
} |
|||
|
|||
fml::closure RunConfiguration::GetMonoEntrypointCallback() const { |
|||
return mono_entrypoint_callback_; |
|||
} |
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <string> |
|||
|
|||
#include "assets/asset_manager.h" |
|||
#include "assets/asset_resolver.h" |
|||
#include "common/settings.h" |
|||
#include "flutter/fml/macros.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class RunConfiguration { |
|||
public: |
|||
static RunConfiguration InferFromSettings(const Settings& settings); |
|||
|
|||
RunConfiguration(); |
|||
|
|||
RunConfiguration(std::shared_ptr<AssetManager> asset_manager, fml::closure mono_entrypoint_callback); |
|||
|
|||
RunConfiguration(RunConfiguration&& config); |
|||
|
|||
~RunConfiguration(); |
|||
|
|||
bool IsValid() const; |
|||
|
|||
bool AddAssetResolver(std::unique_ptr<AssetResolver> resolver); |
|||
|
|||
std::shared_ptr<AssetManager> GetAssetManager() const; |
|||
|
|||
void SetMonoEntrypointCallback(fml::closure mono_entrypoint_callback); |
|||
|
|||
fml::closure GetMonoEntrypointCallback() const; |
|||
|
|||
private: |
|||
std::shared_ptr<AssetManager> asset_manager_; |
|||
fml::closure mono_entrypoint_callback_; |
|||
|
|||
FML_DISALLOW_COPY_AND_ASSIGN(RunConfiguration); |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#pragma once |
|||
|
|||
#include "IUnityInterface.h" |
|||
#include "IUnityGraphics.h" |
|||
|
|||
namespace UnityUIWidgets { |
|||
typedef void (*VoidCallback)(); |
|||
typedef void (*VoidCallbackLong)(long); |
|||
|
|||
UNITY_DECLARE_INTERFACE(IUnityUIWidgets) { |
|||
virtual ~IUnityUIWidgets() {} |
|||
|
|||
virtual void SetUpdateCallback(VoidCallback callback) = 0; |
|||
virtual void SetVSyncCallback(VoidCallback callback) = 0; |
|||
virtual void SetWaitCallback(VoidCallbackLong callback) = 0; |
|||
virtual void SetWakeUpCallback(VoidCallback callback) = 0; |
|||
virtual void IssuePluginEventAndData(UnityRenderingEventAndData callback, |
|||
int eventId, void* data) = 0; |
|||
}; |
|||
} // namespace UnityUIWidgets |
|||
|
|||
UNITY_REGISTER_INTERFACE_GUID_IN_NAMESPACE(0x4C8BE8056B3C41D7ULL, |
|||
0xBC8BF5F2F0AC3532ULL, |
|||
IUnityUIWidgets, UnityUIWidgets) |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Unity.UIWidgets.async2 { |
|||
public struct FutureOr { |
|||
public object value; |
|||
public Future future; |
|||
|
|||
public bool isFuture => future != null; |
|||
|
|||
public static FutureOr withValue(object value) { |
|||
return new FutureOr {value = value}; |
|||
} |
|||
|
|||
public static FutureOr withFuture(Future future) { |
|||
return new FutureOr {future = future}; |
|||
} |
|||
|
|||
public static readonly FutureOr nullValue = withValue(null); |
|||
|
|||
public static readonly FutureOr trueValue = withValue(true); |
|||
|
|||
public static readonly FutureOr falseValue = withValue(false); |
|||
} |
|||
|
|||
public abstract class Future { |
|||
static readonly _Future _nullFuture = _Future.zoneValue(null, Zone.root); |
|||
|
|||
static readonly _Future _falseFuture = _Future.zoneValue(false, Zone.root); |
|||
|
|||
public static Future create(Func<FutureOr> computation) { |
|||
_Future result = new _Future(); |
|||
Timer.run(() => { |
|||
try { |
|||
result._complete(computation()); |
|||
} |
|||
catch (Exception e) { |
|||
async_._completeWithErrorCallback(result, e); |
|||
} |
|||
|
|||
return null; |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
public static Future microtask(Func<FutureOr> computation) { |
|||
_Future result = new _Future(); |
|||
async_.scheduleMicrotask(() => { |
|||
try { |
|||
result._complete(computation()); |
|||
} |
|||
catch (Exception e) { |
|||
async_._completeWithErrorCallback(result, e); |
|||
} |
|||
|
|||
return null; |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
public static Future sync(Func<FutureOr> computation) { |
|||
try { |
|||
var result = computation(); |
|||
if (result.isFuture) { |
|||
return result.future; |
|||
} |
|||
else { |
|||
return _Future.value(result); |
|||
} |
|||
} |
|||
catch (Exception error) { |
|||
var future = new _Future(); |
|||
AsyncError replacement = Zone.current.errorCallback(error); |
|||
if (replacement != null) { |
|||
future._asyncCompleteError(async_._nonNullError(replacement.InnerException)); |
|||
} |
|||
else { |
|||
future._asyncCompleteError(error); |
|||
} |
|||
|
|||
return future; |
|||
} |
|||
} |
|||
|
|||
public static Future value(FutureOr value = default) { |
|||
return _Future.immediate(value); |
|||
} |
|||
|
|||
public static Future error(Exception error) { |
|||
if (error == null) |
|||
throw new ArgumentNullException(nameof(error)); |
|||
|
|||
if (!ReferenceEquals(Zone.current, async_._rootZone)) { |
|||
AsyncError replacement = Zone.current.errorCallback(error); |
|||
if (replacement != null) { |
|||
error = async_._nonNullError(replacement.InnerException); |
|||
} |
|||
} |
|||
|
|||
return _Future.immediateError(error); |
|||
} |
|||
|
|||
public static Future delayed(TimeSpan duration, Func<FutureOr> computation = null) { |
|||
_Future result = new _Future(); |
|||
Timer.create(duration, () => { |
|||
if (computation == null) { |
|||
result._complete(FutureOr.nullValue); |
|||
} |
|||
else { |
|||
try { |
|||
result._complete(computation()); |
|||
} |
|||
catch (Exception e) { |
|||
async_._completeWithErrorCallback(result, e); |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
public static Future wait<T>(IEnumerable<Future> futures, bool eagerError = false, Action<T> cleanUp = null) { |
|||
_Future result = new _Future(); |
|||
List<T> values = null; // Collects the values. Set to null on error.
|
|||
int remaining = 0; // How many futures are we waiting for.
|
|||
Exception error = null; // The first error from a future.
|
|||
|
|||
Func<Exception, FutureOr> handleError = (Exception theError) => { |
|||
remaining--; |
|||
if (values != null) { |
|||
if (cleanUp != null) { |
|||
foreach (var value in values) { |
|||
if (value != null) { |
|||
// Ensure errors from cleanUp are uncaught.
|
|||
Future.sync(() => { |
|||
cleanUp(value); |
|||
return FutureOr.nullValue; |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
values = null; |
|||
if (remaining == 0 || eagerError) { |
|||
result._completeError(theError); |
|||
} |
|||
else { |
|||
error = theError; |
|||
} |
|||
} |
|||
else if (remaining == 0 && !eagerError) { |
|||
result._completeError(error); |
|||
} |
|||
|
|||
return FutureOr.nullValue; |
|||
}; |
|||
|
|||
try { |
|||
// As each future completes, put its value into the corresponding
|
|||
// position in the list of values.
|
|||
foreach (var future in futures) { |
|||
int pos = remaining; |
|||
future.then((object value) => { |
|||
remaining--; |
|||
if (values != null) { |
|||
values[pos] = (T) value; |
|||
if (remaining == 0) { |
|||
result._completeWithValue(values); |
|||
} |
|||
} |
|||
else { |
|||
if (cleanUp != null && value != null) { |
|||
// Ensure errors from cleanUp are uncaught.
|
|||
Future.sync(() => { |
|||
cleanUp((T) value); |
|||
return FutureOr.nullValue; |
|||
}); |
|||
} |
|||
|
|||
if (remaining == 0 && !eagerError) { |
|||
result._completeError(error); |
|||
} |
|||
} |
|||
|
|||
return FutureOr.nullValue; |
|||
}, onError: handleError); |
|||
// Increment the 'remaining' after the call to 'then'.
|
|||
// If that call throws, we don't expect any future callback from
|
|||
// the future, and we also don't increment remaining.
|
|||
remaining++; |
|||
} |
|||
|
|||
if (remaining == 0) { |
|||
return Future.value(FutureOr.withValue(new List<T>())); |
|||
} |
|||
|
|||
values = new List<T>(remaining); |
|||
} |
|||
catch (Exception e) { |
|||
// The error must have been thrown while iterating over the futures
|
|||
// list, or while installing a callback handler on the future.
|
|||
if (remaining == 0 || eagerError) { |
|||
// Throw a new Future.error.
|
|||
// Don't just call `result._completeError` since that would propagate
|
|||
// the error too eagerly, not giving the callers time to install
|
|||
// error handlers.
|
|||
// Also, don't use `_asyncCompleteError` since that one doesn't give
|
|||
// zones the chance to intercept the error.
|
|||
return Future.error(e); |
|||
} |
|||
else { |
|||
// Don't allocate a list for values, thus indicating that there was an
|
|||
// error.
|
|||
// Set error to the caught exception.
|
|||
error = e; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static Future any(IEnumerable<Future> futures) { |
|||
var completer = Completer.sync(); |
|||
Func<object, FutureOr> onValue = (object value) => { |
|||
if (!completer.isCompleted) completer.complete(FutureOr.withValue(value)); |
|||
return FutureOr.nullValue; |
|||
}; |
|||
|
|||
Func<Exception, FutureOr> onError = (Exception error) => { |
|||
if (!completer.isCompleted) completer.completeError(error); |
|||
return FutureOr.nullValue; |
|||
}; |
|||
|
|||
foreach (var future in futures) { |
|||
future.then(onValue, onError: onError); |
|||
} |
|||
|
|||
return completer.future; |
|||
} |
|||
|
|||
public static Future forEach<T>(IEnumerable<T> elements, Func<T, FutureOr> action) { |
|||
var iterator = elements.GetEnumerator(); |
|||
return doWhile(() => { |
|||
if (!iterator.MoveNext()) return FutureOr.falseValue; |
|||
|
|||
var result = action(iterator.Current); |
|||
if (result.isFuture) return FutureOr.withFuture(result.future.then(_kTrue)); |
|||
return FutureOr.trueValue; |
|||
}); |
|||
} |
|||
|
|||
static readonly Func<object, FutureOr> _kTrue = (_) => FutureOr.trueValue; |
|||
|
|||
public static Future doWhile(Func<FutureOr> action) { |
|||
_Future doneSignal = new _Future(); |
|||
ZoneUnaryCallback nextIteration = null; |
|||
// Bind this callback explicitly so that each iteration isn't bound in the
|
|||
// context of all the previous iterations' callbacks.
|
|||
// This avoids, e.g., deeply nested stack traces from the stack trace
|
|||
// package.
|
|||
nextIteration = Zone.current.bindUnaryCallbackGuarded((object keepGoingObj) => { |
|||
bool keepGoing = (bool) keepGoingObj; |
|||
while (keepGoing) { |
|||
FutureOr result; |
|||
try { |
|||
result = action(); |
|||
} |
|||
catch (Exception error) { |
|||
// Cannot use _completeWithErrorCallback because it completes
|
|||
// the future synchronously.
|
|||
async_._asyncCompleteWithErrorCallback(doneSignal, error); |
|||
return null; |
|||
} |
|||
|
|||
if (result.isFuture) { |
|||
result.future.then((value) => { |
|||
nextIteration((bool) value); |
|||
return FutureOr.nullValue; |
|||
}, onError: error => { |
|||
doneSignal._completeError(error); |
|||
return FutureOr.nullValue; |
|||
}); |
|||
return null; |
|||
} |
|||
|
|||
keepGoing = (bool) result.value; |
|||
} |
|||
|
|||
doneSignal._complete(FutureOr.nullValue); |
|||
return null; |
|||
}); |
|||
|
|||
nextIteration(true); |
|||
return doneSignal; |
|||
} |
|||
|
|||
public abstract Future then(Func<object, FutureOr> onValue, Func<Exception, FutureOr> onError = null); |
|||
|
|||
public abstract Future catchError(Func<Exception, FutureOr> onError, Func<Exception, bool> test = null); |
|||
|
|||
public abstract Future whenComplete(Func<object> action); |
|||
|
|||
// public abstract Stream asStream();
|
|||
|
|||
public abstract Future timeout(TimeSpan timeLimit, Func<FutureOr> onTimeout = null); |
|||
} |
|||
|
|||
|
|||
public class TimeoutException : Exception { |
|||
public readonly TimeSpan? duration; |
|||
|
|||
public TimeoutException(string message, TimeSpan? duration = null) : base(message) { |
|||
this.duration = duration; |
|||
} |
|||
|
|||
public override string ToString() { |
|||
string result = "TimeoutException"; |
|||
if (duration != null) result = $"TimeoutException after {duration}"; |
|||
if (Message != null) result = $"result: {Message}"; |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public abstract class Completer { |
|||
public static Completer create() => new _AsyncCompleter(); |
|||
|
|||
public static Completer sync() => new _SyncCompleter(); |
|||
|
|||
public abstract Future future { get; } |
|||
|
|||
public abstract void complete(FutureOr value = default); |
|||
|
|||
public abstract void completeError(Exception error); |
|||
public abstract bool isCompleted { get; } |
|||
} |
|||
|
|||
public static partial class async_ { |
|||
internal static void _completeWithErrorCallback(_Future result, Exception error) { |
|||
AsyncError replacement = Zone.current.errorCallback(error); |
|||
if (replacement != null) { |
|||
error = _nonNullError(replacement.InnerException); |
|||
} |
|||
|
|||
result._completeError(error); |
|||
} |
|||
|
|||
internal static void _asyncCompleteWithErrorCallback(_Future result, Exception error) { |
|||
AsyncError replacement = Zone.current.errorCallback(error); |
|||
if (replacement != null) { |
|||
error = _nonNullError(replacement.InnerException); |
|||
} |
|||
|
|||
result._asyncCompleteError(error); |
|||
} |
|||
|
|||
internal static Exception _nonNullError(Exception error) => |
|||
error ?? new Exception("Throw of null."); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: bdf10194e91e4558bc97ebad8b81d5a0 |
|||
timeCreated: 1599458114 |
|
|||
using System; |
|||
using Unity.UIWidgets.foundation; |
|||
|
|||
namespace Unity.UIWidgets.async2 { |
|||
using _FutureOnValue = Func<object, FutureOr>; |
|||
using _FutureErrorTest = Func<Exception, bool>; |
|||
using _FutureAction = Func<object>; |
|||
|
|||
abstract class _Completer : Completer { |
|||
protected readonly _Future _future = new _Future(); |
|||
public override Future future => _future; |
|||
|
|||
public override void completeError(Exception error) { |
|||
if (error == null) |
|||
throw new ArgumentNullException(nameof(error)); |
|||
|
|||
if (!_future._mayComplete) throw new Exception("Future already completed"); |
|||
AsyncError replacement = Zone.current.errorCallback(error); |
|||
if (replacement != null) { |
|||
error = async_._nonNullError(replacement.InnerException); |
|||
} |
|||
|
|||
_completeError(error); |
|||
} |
|||
|
|||
protected abstract void _completeError(Exception error); |
|||
|
|||
public override bool isCompleted => !_future._mayComplete; |
|||
} |
|||
|
|||
class _AsyncCompleter : _Completer { |
|||
public override void complete(FutureOr value = default) { |
|||
if (!_future._mayComplete) throw new Exception("Future already completed"); |
|||
_future._asyncComplete(value); |
|||
} |
|||
|
|||
protected override void _completeError(Exception error) { |
|||
_future._asyncCompleteError(error); |
|||
} |
|||
} |
|||
|
|||
class _SyncCompleter : _Completer { |
|||
public override void complete(FutureOr value = default) { |
|||
if (!_future._mayComplete) throw new Exception("Future already completed"); |
|||
_future._complete(value); |
|||
} |
|||
|
|||
protected override void _completeError(Exception error) { |
|||
_future._completeError(error); |
|||
} |
|||
} |
|||
|
|||
|
|||
class _FutureListener { |
|||
public const int maskValue = 1; |
|||
public const int maskError = 2; |
|||
public const int maskTestError = 4; |
|||
public const int maskWhencomplete = 8; |
|||
|
|||
public const int stateChain = 0; |
|||
public const int stateThen = maskValue; |
|||
public const int stateThenOnerror = maskValue | maskError; |
|||
public const int stateCatcherror = maskError; |
|||
public const int stateCatcherrorTest = maskError | maskTestError; |
|||
public const int stateWhencomplete = maskWhencomplete; |
|||
public const int maskType = maskValue | maskError | maskTestError | maskWhencomplete; |
|||
public const int stateIsAwait = 16; |
|||
|
|||
internal _FutureListener _nextListener; |
|||
|
|||
public readonly _Future result; |
|||
public readonly int state; |
|||
public readonly Delegate callback; |
|||
public readonly Func<Exception, FutureOr> errorCallback; |
|||
|
|||
_FutureListener(_Future result, Delegate callback, Func<Exception, FutureOr> errorCallback, int state) { |
|||
this.result = result; |
|||
this.state = state; |
|||
this.callback = callback; |
|||
this.errorCallback = errorCallback; |
|||
} |
|||
|
|||
public static _FutureListener then( |
|||
_Future result, _FutureOnValue onValue, Func<Exception, FutureOr> errorCallback) { |
|||
return new _FutureListener( |
|||
result, onValue, errorCallback, |
|||
(errorCallback == null) ? stateThen : stateThenOnerror |
|||
); |
|||
} |
|||
|
|||
public static _FutureListener thenAwait( |
|||
_Future result, _FutureOnValue onValue, Func<Exception, FutureOr> errorCallback) { |
|||
return new _FutureListener( |
|||
result, onValue, errorCallback, |
|||
((errorCallback == null) ? stateThen : stateThenOnerror) | stateIsAwait |
|||
); |
|||
} |
|||
|
|||
public static _FutureListener catchError(_Future result, Func<Exception, FutureOr> errorCallback, |
|||
_FutureErrorTest callback) { |
|||
return new _FutureListener( |
|||
result, callback, errorCallback, |
|||
(callback == null) ? stateCatcherror : stateCatcherrorTest |
|||
); |
|||
} |
|||
|
|||
public static _FutureListener whenComplete(_Future result, _FutureAction callback) { |
|||
return new _FutureListener( |
|||
result, callback, null, |
|||
stateWhencomplete |
|||
); |
|||
} |
|||
|
|||
internal Zone _zone => result._zone; |
|||
|
|||
public bool handlesValue => (state & maskValue) != 0; |
|||
public bool handlesError => (state & maskError) != 0; |
|||
public bool hasErrorTest => (state & maskType) == stateCatcherrorTest; |
|||
public bool handlesComplete => (state & maskType) == stateWhencomplete; |
|||
|
|||
public bool isAwait => (state & stateIsAwait) != 0; |
|||
|
|||
internal _FutureOnValue _onValue { |
|||
get { |
|||
D.assert(handlesValue); |
|||
return (_FutureOnValue) callback; |
|||
} |
|||
} |
|||
|
|||
internal Func<Exception, FutureOr> _onError => errorCallback; |
|||
|
|||
internal _FutureErrorTest _errorTest { |
|||
get { |
|||
D.assert(hasErrorTest); |
|||
return (_FutureErrorTest) callback; |
|||
} |
|||
} |
|||
|
|||
internal _FutureAction _whenCompleteAction { |
|||
get { |
|||
D.assert(handlesComplete); |
|||
return (_FutureAction) callback; |
|||
} |
|||
} |
|||
|
|||
public bool hasErrorCallback { |
|||
get { |
|||
D.assert(handlesError); |
|||
return _onError != null; |
|||
} |
|||
} |
|||
|
|||
public FutureOr handleValue(object sourceResult) { |
|||
return (FutureOr) _zone.runUnary(arg => _onValue(arg), sourceResult); |
|||
} |
|||
|
|||
public bool matchesErrorTest(AsyncError asyncError) { |
|||
if (!hasErrorTest) return true; |
|||
return (bool) _zone.runUnary(arg => _errorTest((Exception) arg), asyncError.InnerException); |
|||
} |
|||
|
|||
public FutureOr handleError(AsyncError asyncError) { |
|||
D.assert(handlesError && hasErrorCallback); |
|||
|
|||
var errorCallback = this.errorCallback; |
|||
return (FutureOr) _zone.runUnary(arg => errorCallback((Exception) arg), asyncError.InnerException); |
|||
} |
|||
|
|||
public object handleWhenComplete() { |
|||
D.assert(!handlesError); |
|||
return _zone.run(() => _whenCompleteAction()); |
|||
} |
|||
} |
|||
|
|||
public class _Future : Future { |
|||
internal const int _stateIncomplete = 0; |
|||
internal const int _statePendingComplete = 1; |
|||
internal const int _stateChained = 2; |
|||
internal const int _stateValue = 4; |
|||
internal const int _stateError = 8; |
|||
|
|||
internal int _state = _stateIncomplete; |
|||
|
|||
internal readonly Zone _zone; |
|||
|
|||
internal object _resultOrListeners; |
|||
|
|||
internal _Future() { |
|||
_zone = Zone.current; |
|||
} |
|||
|
|||
internal _Future(Zone zone) { |
|||
_zone = zone; |
|||
} |
|||
|
|||
internal static _Future immediate(object result) { |
|||
var future = new _Future(Zone.current); |
|||
future._asyncComplete(result); |
|||
return future; |
|||
} |
|||
|
|||
internal static _Future zoneValue(object value, Zone zone) { |
|||
var future = new _Future(zone); |
|||
future._setValue(value); |
|||
return future; |
|||
} |
|||
|
|||
internal static _Future immediateError(Exception error) { |
|||
var future = new _Future(Zone.current); |
|||
future._asyncCompleteError(error); |
|||
return future; |
|||
} |
|||
|
|||
internal static _Future value(object value) { |
|||
return zoneValue(value, Zone.current); |
|||
} |
|||
|
|||
internal bool _mayComplete => _state == _stateIncomplete; |
|||
internal bool _isPendingComplete => _state == _statePendingComplete; |
|||
internal bool _mayAddListener => _state <= _statePendingComplete; |
|||
internal bool _isChained => _state == _stateChained; |
|||
internal bool _isComplete => _state >= _stateValue; |
|||
internal bool _hasError => _state == _stateError; |
|||
|
|||
internal void _setChained(_Future source) { |
|||
D.assert(_mayAddListener); |
|||
_state = _stateChained; |
|||
_resultOrListeners = source; |
|||
} |
|||
|
|||
public override Future then(Func<object, FutureOr> f, Func<Exception, FutureOr> onError = null) { |
|||
Zone currentZone = Zone.current; |
|||
if (!ReferenceEquals(currentZone, async_._rootZone)) { |
|||
f = async_._registerUnaryHandler(f, currentZone); |
|||
if (onError != null) { |
|||
onError = async_._registerErrorHandler(onError, currentZone); |
|||
} |
|||
} |
|||
|
|||
_Future result = new _Future(); |
|||
_addListener(_FutureListener.then(result, f, onError)); |
|||
return result; |
|||
} |
|||
|
|||
public override Future catchError(Func<Exception, FutureOr> onError, Func<Exception, bool> test = null) { |
|||
_Future result = new _Future(); |
|||
if (!ReferenceEquals(result._zone, async_._rootZone)) { |
|||
onError = async_._registerErrorHandler(onError, result._zone); |
|||
if (test != null) { |
|||
test = async_._registerUnaryHandler(test, result._zone); |
|||
} |
|||
} |
|||
|
|||
_addListener(_FutureListener.catchError(result, onError, test)); |
|||
return result; |
|||
} |
|||
|
|||
public override Future whenComplete(Func<object> action) { |
|||
_Future result = new _Future(); |
|||
if (!ReferenceEquals(result._zone, async_._rootZone)) { |
|||
action = async_._registerHandler(action, result._zone); |
|||
} |
|||
|
|||
_addListener(_FutureListener.whenComplete(result, action)); |
|||
return result; |
|||
} |
|||
|
|||
// Stream<T> asStream() => new Stream<T>.fromFuture(this);
|
|||
|
|||
internal void _setPendingComplete() { |
|||
D.assert(_mayComplete); |
|||
_state = _statePendingComplete; |
|||
} |
|||
|
|||
internal void _clearPendingComplete() { |
|||
D.assert(_isPendingComplete); |
|||
_state = _stateIncomplete; |
|||
} |
|||
|
|||
internal AsyncError _error { |
|||
get { |
|||
D.assert(_hasError); |
|||
return (AsyncError) _resultOrListeners; |
|||
} |
|||
} |
|||
|
|||
internal _Future _chainSource { |
|||
get { |
|||
D.assert(_isChained); |
|||
return (_Future) _resultOrListeners; |
|||
} |
|||
} |
|||
|
|||
internal void _setValue(object value) { |
|||
D.assert(!_isComplete); // But may have a completion pending.
|
|||
_state = _stateValue; |
|||
_resultOrListeners = value; |
|||
} |
|||
|
|||
internal void _setErrorObject(AsyncError error) { |
|||
D.assert(!_isComplete); // But may have a completion pending.
|
|||
_state = _stateError; |
|||
_resultOrListeners = error; |
|||
} |
|||
|
|||
internal void _setError(Exception error) { |
|||
_setErrorObject(new AsyncError(error)); |
|||
} |
|||
|
|||
internal void _cloneResult(_Future source) { |
|||
D.assert(!_isComplete); |
|||
D.assert(source._isComplete); |
|||
_state = source._state; |
|||
_resultOrListeners = source._resultOrListeners; |
|||
} |
|||
|
|||
internal void _addListener(_FutureListener listener) { |
|||
D.assert(listener._nextListener == null); |
|||
if (_mayAddListener) { |
|||
listener._nextListener = (_FutureListener) _resultOrListeners; |
|||
_resultOrListeners = listener; |
|||
} |
|||
else { |
|||
if (_isChained) { |
|||
// Delegate listeners to chained source future.
|
|||
// If the source is complete, instead copy its values and
|
|||
// drop the chaining.
|
|||
_Future source = _chainSource; |
|||
if (!source._isComplete) { |
|||
source._addListener(listener); |
|||
return; |
|||
} |
|||
|
|||
_cloneResult(source); |
|||
} |
|||
|
|||
D.assert(_isComplete); |
|||
// Handle late listeners asynchronously.
|
|||
_zone.scheduleMicrotask(() => { |
|||
_propagateToListeners(this, listener); |
|||
return null; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
void _prependListeners(_FutureListener listeners) { |
|||
if (listeners == null) return; |
|||
if (_mayAddListener) { |
|||
_FutureListener existingListeners = (_FutureListener) _resultOrListeners; |
|||
_resultOrListeners = listeners; |
|||
if (existingListeners != null) { |
|||
_FutureListener cursor = listeners; |
|||
while (cursor._nextListener != null) { |
|||
cursor = cursor._nextListener; |
|||
} |
|||
|
|||
cursor._nextListener = existingListeners; |
|||
} |
|||
} |
|||
else { |
|||
if (_isChained) { |
|||
// Delegate listeners to chained source future.
|
|||
// If the source is complete, instead copy its values and
|
|||
// drop the chaining.
|
|||
_Future source = _chainSource; |
|||
if (!source._isComplete) { |
|||
source._prependListeners(listeners); |
|||
return; |
|||
} |
|||
|
|||
_cloneResult(source); |
|||
} |
|||
|
|||
D.assert(_isComplete); |
|||
listeners = _reverseListeners(listeners); |
|||
_zone.scheduleMicrotask(() => { |
|||
_propagateToListeners(this, listeners); |
|||
return null; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
_FutureListener _removeListeners() { |
|||
// Reverse listeners before returning them, so the resulting list is in
|
|||
// subscription order.
|
|||
D.assert(!_isComplete); |
|||
_FutureListener current = (_FutureListener) _resultOrListeners; |
|||
_resultOrListeners = null; |
|||
return _reverseListeners(current); |
|||
} |
|||
|
|||
_FutureListener _reverseListeners(_FutureListener listeners) { |
|||
_FutureListener prev = null; |
|||
_FutureListener current = listeners; |
|||
while (current != null) { |
|||
_FutureListener next = current._nextListener; |
|||
current._nextListener = prev; |
|||
prev = current; |
|||
current = next; |
|||
} |
|||
|
|||
return prev; |
|||
} |
|||
|
|||
static void _chainForeignFuture(Future source, _Future target) { |
|||
D.assert(!target._isComplete); |
|||
D.assert(!(source is _Future)); |
|||
|
|||
// Mark the target as chained (and as such half-completed).
|
|||
target._setPendingComplete(); |
|||
try { |
|||
source.then((value) => { |
|||
D.assert(target._isPendingComplete); |
|||
// The "value" may be another future if the foreign future
|
|||
// implementation is mis-behaving,
|
|||
// so use _complete instead of _completeWithValue.
|
|||
target._clearPendingComplete(); // Clear this first, it's set again.
|
|||
target._complete(FutureOr.withValue(value)); |
|||
return new FutureOr(); |
|||
}, |
|||
onError: (Exception error) => { |
|||
D.assert(target._isPendingComplete); |
|||
target._completeError(error); |
|||
return new FutureOr(); |
|||
}); |
|||
} |
|||
catch (Exception e) { |
|||
// This only happens if the `then` call threw synchronously when given
|
|||
// valid arguments.
|
|||
// That requires a non-conforming implementation of the Future interface,
|
|||
// which should, hopefully, never happen.
|
|||
async_.scheduleMicrotask(() => { |
|||
target._completeError(e); |
|||
return null; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
static void _chainCoreFuture(_Future source, _Future target) { |
|||
D.assert(target._mayAddListener); // Not completed, not already chained.
|
|||
while (source._isChained) { |
|||
source = source._chainSource; |
|||
} |
|||
|
|||
if (source._isComplete) { |
|||
_FutureListener listeners = target._removeListeners(); |
|||
target._cloneResult(source); |
|||
_propagateToListeners(target, listeners); |
|||
} |
|||
else { |
|||
_FutureListener listeners = (_FutureListener) target._resultOrListeners; |
|||
target._setChained(source); |
|||
source._prependListeners(listeners); |
|||
} |
|||
} |
|||
|
|||
internal void _complete(FutureOr value) { |
|||
D.assert(!_isComplete); |
|||
if (value.isFuture) { |
|||
if (value.future is _Future coreFuture) { |
|||
_chainCoreFuture(coreFuture, this); |
|||
} |
|||
else { |
|||
_chainForeignFuture(value.future, this); |
|||
} |
|||
} |
|||
else { |
|||
_FutureListener listeners = _removeListeners(); |
|||
_setValue(value); |
|||
_propagateToListeners(this, listeners); |
|||
} |
|||
} |
|||
|
|||
internal void _completeWithValue(object value) { |
|||
D.assert(!_isComplete); |
|||
D.assert(!(value is Future)); |
|||
|
|||
_FutureListener listeners = _removeListeners(); |
|||
_setValue(value); |
|||
_propagateToListeners(this, listeners); |
|||
} |
|||
|
|||
internal void _completeError(Exception error) { |
|||
D.assert(!_isComplete); |
|||
|
|||
_FutureListener listeners = _removeListeners(); |
|||
_setError(error); |
|||
_propagateToListeners(this, listeners); |
|||
} |
|||
|
|||
internal void _asyncComplete(object value) { |
|||
D.assert(!_isComplete); |
|||
// Two corner cases if the value is a future:
|
|||
// 1. the future is already completed and an error.
|
|||
// 2. the future is not yet completed but might become an error.
|
|||
// The first case means that we must not immediately complete the Future,
|
|||
// as our code would immediately start propagating the error without
|
|||
// giving the time to install error-handlers.
|
|||
// However the second case requires us to deal with the value immediately.
|
|||
// Otherwise the value could complete with an error and report an
|
|||
// unhandled error, even though we know we are already going to listen to
|
|||
// it.
|
|||
|
|||
if (value is Future future) { |
|||
_chainFuture(future); |
|||
return; |
|||
} |
|||
|
|||
_setPendingComplete(); |
|||
_zone.scheduleMicrotask(() => { |
|||
_completeWithValue(value); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
internal void _chainFuture(Future value) { |
|||
if (value is _Future future) { |
|||
if (future._hasError) { |
|||
// Delay completion to allow the user to register callbacks.
|
|||
_setPendingComplete(); |
|||
_zone.scheduleMicrotask(() => { |
|||
_chainCoreFuture(future, this); |
|||
return null; |
|||
}); |
|||
} |
|||
else { |
|||
_chainCoreFuture(future, this); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
// Just listen on the foreign future. This guarantees an async delay.
|
|||
_chainForeignFuture(value, this); |
|||
} |
|||
|
|||
|
|||
internal void _asyncCompleteError(Exception error) { |
|||
D.assert(!_isComplete); |
|||
|
|||
_setPendingComplete(); |
|||
_zone.scheduleMicrotask(() => { |
|||
_completeError(error); |
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
|
|||
static void _propagateToListeners(_Future source, _FutureListener listeners) { |
|||
while (true) { |
|||
D.assert(source._isComplete); |
|||
bool hasError = source._hasError; |
|||
if (listeners == null) { |
|||
if (hasError) { |
|||
AsyncError asyncError = source._error; |
|||
source._zone.handleUncaughtError(asyncError); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
// Usually futures only have one listener. If they have several, we
|
|||
// call handle them separately in recursive calls, continuing
|
|||
// here only when there is only one listener left.
|
|||
while (listeners._nextListener != null) { |
|||
_FutureListener currentListener = listeners; |
|||
listeners = currentListener._nextListener; |
|||
currentListener._nextListener = null; |
|||
_propagateToListeners(source, currentListener); |
|||
} |
|||
|
|||
_FutureListener listener = listeners; |
|||
var sourceResult = source._resultOrListeners; |
|||
|
|||
// Do the actual propagation.
|
|||
// Set initial state of listenerHasError and listenerValueOrError. These
|
|||
// variables are updated with the outcome of potential callbacks.
|
|||
// Non-error results, including futures, are stored in
|
|||
// listenerValueOrError and listenerHasError is set to false. Errors
|
|||
// are stored in listenerValueOrError as an [AsyncError] and
|
|||
// listenerHasError is set to true.
|
|||
bool listenerHasError = hasError; |
|||
var listenerValueOrError = sourceResult; |
|||
|
|||
// Only if we either have an error or callbacks, go into this, somewhat
|
|||
// expensive, branch. Here we'll enter/leave the zone. Many futures
|
|||
// don't have callbacks, so this is a significant optimization.
|
|||
if (hasError || listener.handlesValue || listener.handlesComplete) { |
|||
Zone zone = listener._zone; |
|||
if (hasError && !source._zone.inSameErrorZone(zone)) { |
|||
// Don't cross zone boundaries with errors.
|
|||
AsyncError asyncError = source._error; |
|||
source._zone.handleUncaughtError(asyncError); |
|||
return; |
|||
} |
|||
|
|||
Zone oldZone = null; |
|||
if (!ReferenceEquals(Zone.current, zone)) { |
|||
// Change zone if it's not current.
|
|||
oldZone = Zone._enter(zone); |
|||
} |
|||
|
|||
// These callbacks are abstracted to isolate the try/catch blocks
|
|||
// from the rest of the code to work around a V8 glass jaw.
|
|||
Action handleWhenCompleteCallback = () => { |
|||
// The whenComplete-handler is not combined with normal value/error
|
|||
// handling. This means at most one handleX method is called per
|
|||
// listener.
|
|||
D.assert(!listener.handlesValue); |
|||
D.assert(!listener.handlesError); |
|||
object completeResult = null; |
|||
try { |
|||
completeResult = listener.handleWhenComplete(); |
|||
} |
|||
catch (Exception e) { |
|||
if (hasError && ReferenceEquals(source._error.InnerException, e)) { |
|||
listenerValueOrError = source._error; |
|||
} |
|||
else { |
|||
listenerValueOrError = new AsyncError(e); |
|||
} |
|||
|
|||
listenerHasError = true; |
|||
return; |
|||
} |
|||
|
|||
if (completeResult is Future completeResultFuture) { |
|||
if (completeResult is _Future completeResultCoreFuture && |
|||
completeResultCoreFuture._isComplete) { |
|||
if (completeResultCoreFuture._hasError) { |
|||
listenerValueOrError = completeResultCoreFuture._error; |
|||
listenerHasError = true; |
|||
} |
|||
|
|||
// Otherwise use the existing result of source.
|
|||
return; |
|||
} |
|||
|
|||
// We have to wait for the completeResult future to complete
|
|||
// before knowing if it's an error or we should use the result
|
|||
// of source.
|
|||
var originalSource = source; |
|||
listenerValueOrError = |
|||
completeResultFuture.then((_) => FutureOr.withFuture(originalSource)); |
|||
listenerHasError = false; |
|||
} |
|||
}; |
|||
|
|||
Action handleValueCallback = () => { |
|||
try { |
|||
listenerValueOrError = listener.handleValue(sourceResult); |
|||
} |
|||
catch (Exception e) { |
|||
listenerValueOrError = new AsyncError(e); |
|||
listenerHasError = true; |
|||
} |
|||
}; |
|||
|
|||
Action handleError = () => { |
|||
try { |
|||
AsyncError asyncError = source._error; |
|||
if (listener.matchesErrorTest(asyncError) && |
|||
listener.hasErrorCallback) { |
|||
listenerValueOrError = listener.handleError(asyncError); |
|||
listenerHasError = false; |
|||
} |
|||
} |
|||
catch (Exception e) { |
|||
if (ReferenceEquals(source._error.InnerException, e)) { |
|||
listenerValueOrError = source._error; |
|||
} |
|||
else { |
|||
listenerValueOrError = new AsyncError(e); |
|||
} |
|||
|
|||
listenerHasError = true; |
|||
} |
|||
}; |
|||
|
|||
if (listener.handlesComplete) { |
|||
handleWhenCompleteCallback(); |
|||
} |
|||
else if (!hasError) { |
|||
if (listener.handlesValue) { |
|||
handleValueCallback(); |
|||
} |
|||
} |
|||
else { |
|||
if (listener.handlesError) { |
|||
handleError(); |
|||
} |
|||
} |
|||
|
|||
// If we changed zone, oldZone will not be null.
|
|||
if (oldZone != null) Zone._leave(oldZone); |
|||
|
|||
// If the listener's value is a future we need to chain it. Note that
|
|||
// this can only happen if there is a callback.
|
|||
if (listenerValueOrError is Future chainSource) { |
|||
// Shortcut if the chain-source is already completed. Just continue
|
|||
// the loop.
|
|||
_Future listenerResult = listener.result; |
|||
if (chainSource is _Future chainSourceCore) { |
|||
if (chainSourceCore._isComplete) { |
|||
listeners = listenerResult._removeListeners(); |
|||
listenerResult._cloneResult(chainSourceCore); |
|||
source = chainSourceCore; |
|||
continue; |
|||
} |
|||
else { |
|||
_chainCoreFuture(chainSourceCore, listenerResult); |
|||
} |
|||
} |
|||
else { |
|||
_chainForeignFuture(chainSource, listenerResult); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
} |
|||
|
|||
_Future result = listener.result; |
|||
listeners = result._removeListeners(); |
|||
if (!listenerHasError) { |
|||
result._setValue(listenerValueOrError); |
|||
} |
|||
else { |
|||
AsyncError asyncError = (AsyncError) listenerValueOrError; |
|||
result._setErrorObject(asyncError); |
|||
} |
|||
|
|||
// Prepare for next round.
|
|||
source = result; |
|||
} |
|||
} |
|||
|
|||
|
|||
public override Future timeout(TimeSpan timeLimit, Func<FutureOr> onTimeout = null) { |
|||
if (_isComplete) return _Future.immediate(this); |
|||
|
|||
_Future result = new _Future(); |
|||
Timer timer; |
|||
if (onTimeout == null) { |
|||
timer = Timer.create(timeLimit, () => { |
|||
result._completeError( |
|||
new TimeoutException("Future not completed", timeLimit)); |
|||
return null; |
|||
}); |
|||
} |
|||
else { |
|||
Zone zone = Zone.current; |
|||
onTimeout = async_._registerHandler(onTimeout, zone); |
|||
|
|||
timer = Timer.create(timeLimit, () => { |
|||
try { |
|||
result._complete((FutureOr) zone.run(() => onTimeout())); |
|||
} |
|||
catch (Exception e) { |
|||
result._completeError(e); |
|||
} |
|||
|
|||
return null; |
|||
}); |
|||
} |
|||
|
|||
then(v => { |
|||
if (timer.isActive) { |
|||
timer.cancel(); |
|||
result._completeWithValue(v); |
|||
} |
|||
|
|||
return FutureOr.nullValue; |
|||
}, onError: e => { |
|||
if (timer.isActive) { |
|||
timer.cancel(); |
|||
result._completeError(e); |
|||
} |
|||
|
|||
return FutureOr.nullValue; |
|||
}); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
public static partial class async_ { |
|||
internal static Func<object> _registerHandler(Func<object> handler, Zone zone) { |
|||
var callback = zone.registerCallback(() => handler()); |
|||
return () => callback(); |
|||
} |
|||
|
|||
internal static Func<FutureOr> _registerHandler(Func<FutureOr> handler, Zone zone) { |
|||
var callback = zone.registerCallback(() => handler()); |
|||
return () => (FutureOr) callback(); |
|||
} |
|||
|
|||
internal static Func<object, FutureOr> _registerUnaryHandler(Func<object, FutureOr> handler, Zone zone) { |
|||
var callback = zone.registerUnaryCallback(arg => handler(arg)); |
|||
return arg => (FutureOr) callback(arg); |
|||
} |
|||
|
|||
internal static Func<Exception, bool> _registerUnaryHandler(Func<Exception, bool> handler, Zone zone) { |
|||
var callback = zone.registerUnaryCallback(arg => handler((Exception) arg)); |
|||
return arg => (bool) callback(arg); |
|||
} |
|||
|
|||
internal static Func<Exception, FutureOr> _registerErrorHandler(Func<Exception, FutureOr> errorHandler, |
|||
Zone zone) { |
|||
var callback = zone.registerUnaryCallback(arg => errorHandler((Exception) arg)); |
|||
return arg => (FutureOr) callback(arg); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 4072406cc46448f99159c137b458841e |
|||
timeCreated: 1599458114 |
|
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using AOT; |
|||
using Unity.UIWidgets.ui2; |
|||
|
|||
namespace Unity.UIWidgets.async2 { |
|||
class _AsyncCallbackEntry { |
|||
public readonly ZoneCallback callback; |
|||
public _AsyncCallbackEntry next; |
|||
|
|||
internal _AsyncCallbackEntry(ZoneCallback callback) { |
|||
this.callback = callback; |
|||
} |
|||
} |
|||
|
|||
public static partial class async_ { |
|||
static _AsyncCallbackEntry _nextCallback; |
|||
static _AsyncCallbackEntry _lastCallback; |
|||
static _AsyncCallbackEntry _lastPriorityCallback; |
|||
|
|||
static bool _isInCallbackLoop = false; |
|||
|
|||
static void _microtaskLoop() { |
|||
while (_nextCallback != null) { |
|||
_lastPriorityCallback = null; |
|||
_AsyncCallbackEntry entry = _nextCallback; |
|||
_nextCallback = entry.next; |
|||
if (_nextCallback == null) _lastCallback = null; |
|||
entry.callback(); |
|||
} |
|||
} |
|||
|
|||
static object _startMicrotaskLoop() { |
|||
_isInCallbackLoop = true; |
|||
try { |
|||
// Moved to separate function because try-finally prevents
|
|||
// good optimization.
|
|||
_microtaskLoop(); |
|||
} |
|||
finally { |
|||
_lastPriorityCallback = null; |
|||
_isInCallbackLoop = false; |
|||
if (_nextCallback != null) { |
|||
_AsyncRun._scheduleImmediate(_startMicrotaskLoop); |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
static void _scheduleAsyncCallback(ZoneCallback callback) { |
|||
_AsyncCallbackEntry newEntry = new _AsyncCallbackEntry(callback); |
|||
if (_nextCallback == null) { |
|||
_nextCallback = _lastCallback = newEntry; |
|||
if (!_isInCallbackLoop) { |
|||
_AsyncRun._scheduleImmediate(_startMicrotaskLoop); |
|||
} |
|||
} |
|||
else { |
|||
_lastCallback.next = newEntry; |
|||
_lastCallback = newEntry; |
|||
} |
|||
} |
|||
|
|||
static void _schedulePriorityAsyncCallback(ZoneCallback callback) { |
|||
if (_nextCallback == null) { |
|||
_scheduleAsyncCallback(callback); |
|||
_lastPriorityCallback = _lastCallback; |
|||
return; |
|||
} |
|||
|
|||
_AsyncCallbackEntry entry = new _AsyncCallbackEntry(callback); |
|||
if (_lastPriorityCallback == null) { |
|||
entry.next = _nextCallback; |
|||
_nextCallback = _lastPriorityCallback = entry; |
|||
} |
|||
else { |
|||
entry.next = _lastPriorityCallback.next; |
|||
_lastPriorityCallback.next = entry; |
|||
_lastPriorityCallback = entry; |
|||
if (entry.next == null) { |
|||
_lastCallback = entry; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void scheduleMicrotask(ZoneCallback callback) { |
|||
_Zone currentZone = (_Zone) Zone.current; |
|||
if (ReferenceEquals(_rootZone, currentZone)) { |
|||
// No need to bind the callback. We know that the root's scheduleMicrotask
|
|||
// will be invoked in the root zone.
|
|||
_rootScheduleMicrotask(null, null, _rootZone, callback); |
|||
return; |
|||
} |
|||
|
|||
_ZoneFunction<ScheduleMicrotaskHandler> implementation = currentZone._scheduleMicrotask; |
|||
if (ReferenceEquals(_rootZone, implementation.zone) && |
|||
_rootZone.inSameErrorZone(currentZone)) { |
|||
_rootScheduleMicrotask( |
|||
null, null, currentZone, currentZone.registerCallback(callback)); |
|||
return; |
|||
} |
|||
|
|||
Zone.current.scheduleMicrotask(Zone.current.bindCallbackGuarded(callback)); |
|||
} |
|||
} |
|||
|
|||
class _AsyncRun { |
|||
internal static void _scheduleImmediate(ZoneCallback callback) { |
|||
GCHandle callabackHandle = GCHandle.Alloc(callback); |
|||
UIMonoState_scheduleMicrotask(_scheduleMicrotask, (IntPtr) callabackHandle); |
|||
} |
|||
|
|||
[MonoPInvokeCallback(typeof(UIMonoState_scheduleMicrotaskCallback))] |
|||
static void _scheduleMicrotask(IntPtr callbackHandle) { |
|||
GCHandle handle = (GCHandle) callbackHandle; |
|||
var callback = (ZoneCallback) handle.Target; |
|||
handle.Free(); |
|||
|
|||
callback(); |
|||
} |
|||
|
|||
delegate void UIMonoState_scheduleMicrotaskCallback(IntPtr callbackHandle); |
|||
|
|||
[DllImport(NativeBindings.dllName)] |
|||
static extern void UIMonoState_scheduleMicrotask(UIMonoState_scheduleMicrotaskCallback callback, |
|||
IntPtr callbackHandle); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: e9d7bdadaa9c46e0b96ba19f4261185a |
|||
timeCreated: 1599458114 |
|
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using AOT; |
|||
using Unity.UIWidgets.ui2; |
|||
|
|||
namespace Unity.UIWidgets.async2 { |
|||
public abstract class Timer : IDisposable { |
|||
public static Timer create(TimeSpan duration, ZoneCallback callback) { |
|||
if (Zone.current == Zone.root) { |
|||
return Zone.current.createTimer(duration, callback); |
|||
} |
|||
|
|||
return Zone.current |
|||
.createTimer(duration, Zone.current.bindCallbackGuarded(callback)); |
|||
} |
|||
|
|||
public static Timer periodic(TimeSpan duration, ZoneUnaryCallback callback) { |
|||
if (Zone.current == Zone.root) { |
|||
return Zone.current.createPeriodicTimer(duration, callback); |
|||
} |
|||
|
|||
var boundCallback = Zone.current.bindUnaryCallbackGuarded(callback); |
|||
return Zone.current.createPeriodicTimer(duration, boundCallback); |
|||
} |
|||
|
|||
public static void run(ZoneCallback callback) { |
|||
Timer.create(TimeSpan.Zero, callback); |
|||
} |
|||
|
|||
public abstract void cancel(); |
|||
|
|||
public void Dispose() { |
|||
cancel(); |
|||
} |
|||
|
|||
public abstract int tick { get; } |
|||
|
|||
public abstract bool isActive { get; } |
|||
|
|||
internal static Timer _createTimer(TimeSpan duration, ZoneCallback callback) { |
|||
return _Timer._createTimer(_ => callback(), (int) duration.TotalMilliseconds, false); |
|||
} |
|||
|
|||
internal static Timer _createPeriodicTimer( |
|||
TimeSpan duration, ZoneUnaryCallback callback) { |
|||
return _Timer._createTimer(callback, (int) duration.TotalMilliseconds, true); |
|||
} |
|||
} |
|||
|
|||
class _Timer : Timer { |
|||
int _tick = 0; |
|||
|
|||
ZoneUnaryCallback _callback; |
|||
int _wakeupTime; |
|||
readonly int _milliSeconds; |
|||
readonly bool _repeating; |
|||
|
|||
_Timer(ZoneUnaryCallback callback, int wakeupTime, int milliSeconds, bool repeating) { |
|||
_callback = callback; |
|||
_wakeupTime = wakeupTime; |
|||
_milliSeconds = milliSeconds; |
|||
_repeating = repeating; |
|||
} |
|||
|
|||
internal static _Timer _createTimer(ZoneUnaryCallback callback, int milliSeconds, bool repeating) { |
|||
if (milliSeconds < 0) { |
|||
milliSeconds = 0; |
|||
} |
|||
|
|||
int now = UIMonoState_timerMillisecondClock(); |
|||
int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds); |
|||
|
|||
_Timer timer = new _Timer(callback, wakeupTime, milliSeconds, repeating); |
|||
timer._enqueue(); |
|||
|
|||
return timer; |
|||
} |
|||
|
|||
public override void cancel() { |
|||
_callback = null; |
|||
} |
|||
|
|||
public override bool isActive => _callback != null; |
|||
|
|||
public override int tick => _tick; |
|||
|
|||
void _advanceWakeupTime() { |
|||
if (_milliSeconds > 0) { |
|||
_wakeupTime += _milliSeconds; |
|||
} |
|||
else { |
|||
_wakeupTime = UIMonoState_timerMillisecondClock(); |
|||
} |
|||
} |
|||
|
|||
void _enqueue() { |
|||
GCHandle callabackHandle = GCHandle.Alloc(this); |
|||
UIMonoState_postTaskForTime(_postTaskForTime, (IntPtr) callabackHandle, _wakeupTime * 1000L); |
|||
} |
|||
|
|||
[MonoPInvokeCallback(typeof(UIMonoState_postTaskForTimeCallback))] |
|||
static void _postTaskForTime(IntPtr callbackHandle) { |
|||
GCHandle timerHandle = (GCHandle) callbackHandle; |
|||
var timer = (_Timer) timerHandle.Target; |
|||
timerHandle.Free(); |
|||
|
|||
if (timer._callback != null) { |
|||
var callback = timer._callback; |
|||
if (!timer._repeating) { |
|||
timer._callback = null; |
|||
} |
|||
else if (timer._milliSeconds > 0) { |
|||
var ms = timer._milliSeconds; |
|||
int overdue = UIMonoState_timerMillisecondClock() - timer._wakeupTime; |
|||
if (overdue > ms) { |
|||
int missedTicks = overdue / ms; |
|||
timer._wakeupTime += missedTicks * ms; |
|||
timer._tick += missedTicks; |
|||
} |
|||
} |
|||
|
|||
timer._tick += 1; |
|||
|
|||
callback(timer); |
|||
|
|||
if (timer._repeating && (timer._callback != null)) { |
|||
timer._advanceWakeupTime(); |
|||
timer._enqueue(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[DllImport(NativeBindings.dllName)] |
|||
static extern int UIMonoState_timerMillisecondClock(); |
|||
|
|||
delegate void UIMonoState_postTaskForTimeCallback(IntPtr callbackHandle); |
|||
|
|||
[DllImport(NativeBindings.dllName)] |
|||
static extern void UIMonoState_postTaskForTime(UIMonoState_postTaskForTimeCallback callback, |
|||
IntPtr callbackHandle, long targetTimeNanos); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: dc44ad1967714937b709d5f22c6b3bec |
|||
timeCreated: 1599458114 |
1001
com.unity.uiwidgets/Runtime/async2/zone.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: d8dbf866190b44e6a47e0ff7c0e2bdc4 |
|||
timeCreated: 1599458114 |
|
|||
using System.Collections; |
|||
|
|||
namespace developer { |
|||
public static partial class developer_ { |
|||
public static void postEvent(string eventKind, Hashtable eventData) { |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d2dcff701560471ab813d010a98a7d36 |
|||
timeCreated: 1599030815 |
|
|||
using System.Collections; |
|||
|
|||
namespace developer { |
|||
public class Timeline { |
|||
public static void startSync(string name, Hashtable arguments = null) { |
|||
} |
|||
|
|||
public static void finishSync() { |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 263dee475b944a33a99e0d8925f904c4 |
|||
timeCreated: 1599028971 |
|
|||
fileFormatVersion: 2 |
|||
guid: 305c123a2a944eba8f2b397a962323ff |
|||
timeCreated: 1597215247 |
|
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using AOT; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui2; |
|||
using UnityEngine; |
|||
using UnityEngine.UI; |
|||
using NativeBindings = Unity.UIWidgets.ui2.NativeBindings; |
|||
|
|||
namespace Unity.UIWidgets.engine2 { |
|||
public class UIWidgetsPanel : RawImage { |
|||
public IntPtr isolate { get; private set; } |
|||
|
|||
IntPtr _ptr; |
|||
GCHandle _handle; |
|||
|
|||
RenderTexture _renderTexture; |
|||
int _width; |
|||
int _height; |
|||
float _devicePixelRatio; |
|||
|
|||
int _currentWidth { |
|||
get { return Mathf.RoundToInt(rectTransform.rect.width * canvas.scaleFactor); } |
|||
} |
|||
|
|||
int _currentHeight { |
|||
get { return Mathf.RoundToInt(rectTransform.rect.height * canvas.scaleFactor); } |
|||
} |
|||
|
|||
float _currentDevicePixelRatio { |
|||
get { |
|||
float currentDpi = Screen.dpi; |
|||
if (currentDpi == 0) { |
|||
currentDpi = canvas.GetComponent<CanvasScaler>().fallbackScreenDPI; |
|||
} |
|||
|
|||
return currentDpi / 96; |
|||
} |
|||
} |
|||
|
|||
protected override void OnEnable() { |
|||
base.OnEnable(); |
|||
|
|||
_recreateRenderTexture(_currentWidth, _currentHeight, _currentDevicePixelRatio); |
|||
|
|||
_handle = GCHandle.Alloc(this); |
|||
|
|||
_ptr = UIWidgetsPanel_constructor((IntPtr) _handle, UIWidgetsPanel_entrypoint); |
|||
UIWidgetsPanel_onEnable(_ptr, _renderTexture.GetNativeTexturePtr(), |
|||
_width, _height, _devicePixelRatio); |
|||
} |
|||
|
|||
protected virtual void main() { |
|||
Debug.Log(Debug.isDebugBuild); |
|||
} |
|||
|
|||
public void entryPoint() { |
|||
try { |
|||
isolate = Isolate.current; |
|||
main(); |
|||
} |
|||
catch (Exception ex) { |
|||
Debug.LogException(new Exception("exception in main", ex)); |
|||
} |
|||
} |
|||
|
|||
protected override void OnRectTransformDimensionsChange() { |
|||
if (_ptr != IntPtr.Zero) { |
|||
if (_recreateRenderTexture(_currentWidth, _currentHeight, _currentDevicePixelRatio)) { |
|||
UIWidgetsPanel_onRenderTexture(_ptr, |
|||
_renderTexture.GetNativeTexturePtr(), |
|||
_width, _height, _devicePixelRatio); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override void OnDisable() { |
|||
UIWidgetsPanel_onDisable(_ptr); |
|||
UIWidgetsPanel_dispose(_ptr); |
|||
_ptr = IntPtr.Zero; |
|||
|
|||
_handle.Free(); |
|||
_handle = default; |
|||
|
|||
base.OnDisable(); |
|||
} |
|||
|
|||
bool _recreateRenderTexture(int width, int height, float devicePixelRatio) { |
|||
if (_renderTexture != null && _width == width && _height == height && |
|||
_devicePixelRatio == devicePixelRatio) { |
|||
return false; |
|||
} |
|||
|
|||
if (_renderTexture) { |
|||
_destroyRenderTexture(); |
|||
} |
|||
|
|||
_createRenderTexture(width, height, devicePixelRatio); |
|||
return true; |
|||
} |
|||
|
|||
void _createRenderTexture(int width, int height, float devicePixelRatio) { |
|||
D.assert(_renderTexture == null); |
|||
|
|||
var desc = new RenderTextureDescriptor( |
|||
width, height, RenderTextureFormat.ARGB32, 0) { |
|||
useMipMap = false, |
|||
autoGenerateMips = false, |
|||
}; |
|||
|
|||
_renderTexture = new RenderTexture(desc) {hideFlags = HideFlags.HideAndDontSave}; |
|||
_renderTexture.Create(); |
|||
|
|||
_width = width; |
|||
_height = height; |
|||
_devicePixelRatio = devicePixelRatio; |
|||
|
|||
texture = _renderTexture; |
|||
} |
|||
|
|||
void _destroyRenderTexture() { |
|||
D.assert(_renderTexture != null); |
|||
texture = null; |
|||
ObjectUtils.SafeDestroy(_renderTexture); |
|||
_renderTexture = null; |
|||
} |
|||
|
|||
delegate void UIWidgetsPanel_EntrypointCallback(IntPtr handle); |
|||
|
|||
[MonoPInvokeCallback(typeof(UIWidgetsPanel_EntrypointCallback))] |
|||
static void UIWidgetsPanel_entrypoint(IntPtr handle) { |
|||
GCHandle gcHandle = (GCHandle) handle; |
|||
UIWidgetsPanel panel = (UIWidgetsPanel) gcHandle.Target; |
|||
panel.entryPoint(); |
|||
} |
|||
|
|||
[DllImport(NativeBindings.dllName)] |
|||
static extern IntPtr UIWidgetsPanel_constructor(IntPtr handle, |
|||
UIWidgetsPanel_EntrypointCallback entrypointCallback); |
|||
|
|||
[DllImport(NativeBindings.dllName)] |
|||
static extern void UIWidgetsPanel_dispose(IntPtr ptr); |
|||
|
|||
[DllImport(NativeBindings.dllName)] |
|||
static extern void UIWidgetsPanel_onEnable(IntPtr ptr, |
|||
IntPtr nativeTexturePtr, int width, int height, float dpi); |
|||
|
|||
[DllImport(NativeBindings.dllName)] |
|||
static extern void UIWidgetsPanel_onDisable(IntPtr ptr); |
|||
|
|||
[DllImport(NativeBindings.dllName)] |
|||
static extern void UIWidgetsPanel_onRenderTexture( |
|||
IntPtr ptr, IntPtr nativeTexturePtr, int width, int height, float dpi); |
|||
} |
|||
} |
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using developer; |
|||
using Unity.UIWidgets.async; |
|||
using Unity.UIWidgets.async2; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui2; |
|||
using UnityEngine; |
|||
using FrameTiming = Unity.UIWidgets.ui2.FrameTiming; |
|||
using Timer = Unity.UIWidgets.async2.Timer; |
|||
|
|||
namespace Unity.UIWidgets.scheduler2 { |
|||
public static partial class scheduler_ { |
|||
public static float timeDilation { |
|||
get { return _timeDilation; } |
|||
set { |
|||
D.assert(value > 0.0f); |
|||
if (_timeDilation == value) |
|||
return; |
|||
|
|||
SchedulerBinding.instance?.resetEpoch(); |
|||
_timeDilation = value; |
|||
} |
|||
} |
|||
|
|||
static float _timeDilation = 1.0f; |
|||
} |
|||
|
|||
public delegate void FrameCallback(TimeSpan timeStamp); |
|||
|
|||
public delegate T TaskCallback<out T>(); |
|||
|
|||
public delegate bool SchedulingStrategy(int priority = 0, SchedulerBinding scheduler = null); |
|||
|
|||
interface _TaskEntry : IComparable<_TaskEntry> { |
|||
int priority { get; } |
|||
string debugStack { get; } |
|||
void run(); |
|||
} |
|||
|
|||
class _TaskEntry<T> : _TaskEntry { |
|||
internal _TaskEntry(TaskCallback<T> task, int priority) { |
|||
this.task = task; |
|||
this.priority = priority; |
|||
|
|||
D.assert(() => { |
|||
debugStack = StackTraceUtility.ExtractStackTrace(); |
|||
return true; |
|||
}); |
|||
completer = Completer.create(); |
|||
} |
|||
|
|||
public readonly TaskCallback<T> task; |
|||
public int priority { get; } |
|||
public string debugStack { get; private set; } |
|||
public Completer completer; |
|||
|
|||
public void run() { |
|||
if (!foundation_.kReleaseMode) { |
|||
completer.complete(FutureOr.withValue(task())); |
|||
} |
|||
else { |
|||
completer.complete(FutureOr.withValue(task())); |
|||
} |
|||
} |
|||
|
|||
public int CompareTo(_TaskEntry other) { |
|||
return -priority.CompareTo(other.priority); |
|||
} |
|||
} |
|||
|
|||
class _FrameCallbackEntry { |
|||
internal _FrameCallbackEntry(FrameCallback callback, bool rescheduling = false) { |
|||
this.callback = callback; |
|||
|
|||
D.assert(() => { |
|||
if (rescheduling) { |
|||
D.assert(() => { |
|||
if (debugCurrentCallbackStack == null) { |
|||
throw new UIWidgetsError( |
|||
"scheduleFrameCallback called with rescheduling true, but no callback is in scope.\n" + |
|||
"The \"rescheduling\" argument should only be set to true if the " + |
|||
"callback is being reregistered from within the callback itself, " + |
|||
"and only then if the callback itself is entirely synchronous. \n" + |
|||
"If this is the initial registration of the callback, or if the " + |
|||
"callback is asynchronous, then do not use the \"rescheduling\" " + |
|||
"argument."); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
debugStack = debugCurrentCallbackStack; |
|||
} |
|||
else { |
|||
debugStack = StackTraceUtility.ExtractStackTrace(); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
public readonly FrameCallback callback; |
|||
|
|||
public static string debugCurrentCallbackStack; |
|||
public string debugStack; |
|||
} |
|||
|
|||
public enum SchedulerPhase { |
|||
idle, |
|||
transientCallbacks, |
|||
midFrameMicrotasks, |
|||
persistentCallbacks, |
|||
postFrameCallbacks, |
|||
} |
|||
|
|||
public class SchedulerBinding : BindingBase { |
|||
protected override void initInstances() { |
|||
base.initInstances(); |
|||
instance = this; |
|||
|
|||
//SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
|
|||
readInitialLifecycleStateFromNativeWindow(); |
|||
|
|||
if (!foundation_.kReleaseMode) { |
|||
int frameNumber = 0; |
|||
addTimingsCallback((List<FrameTiming> timings) => { |
|||
foreach (FrameTiming frameTiming in timings) { |
|||
frameNumber += 1; |
|||
_profileFramePostEvent(frameNumber, frameTiming); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
readonly List<TimingsCallback> _timingsCallbacks = new List<TimingsCallback>(); |
|||
|
|||
public void addTimingsCallback(TimingsCallback callback) { |
|||
_timingsCallbacks.Add(callback); |
|||
if (_timingsCallbacks.Count == 1) { |
|||
D.assert(window.onReportTimings == null); |
|||
window.onReportTimings = _executeTimingsCallbacks; |
|||
} |
|||
|
|||
D.assert(window.onReportTimings == _executeTimingsCallbacks); |
|||
} |
|||
|
|||
public void removeTimingsCallback(TimingsCallback callback) { |
|||
D.assert(_timingsCallbacks.Contains(callback)); |
|||
_timingsCallbacks.Remove(callback); |
|||
if (_timingsCallbacks.isEmpty()) { |
|||
window.onReportTimings = null; |
|||
} |
|||
} |
|||
|
|||
void _executeTimingsCallbacks(List<FrameTiming> timings) { |
|||
List<TimingsCallback> clonedCallbacks = |
|||
new List<TimingsCallback>(_timingsCallbacks); |
|||
foreach (TimingsCallback callback in clonedCallbacks) { |
|||
try { |
|||
if (_timingsCallbacks.Contains(callback)) { |
|||
callback(timings); |
|||
} |
|||
} |
|||
catch (Exception ex) { |
|||
InformationCollector collector = null; |
|||
D.assert(() => { |
|||
collector = (StringBuilder sb) => { |
|||
sb.AppendLine("The TimingsCallback that gets executed was " + callback); |
|||
}; |
|||
return true; |
|||
}); |
|||
|
|||
UIWidgetsError.reportError(new UIWidgetsErrorDetails( |
|||
exception: ex, |
|||
context: "while executing callbacks for FrameTiming", |
|||
informationCollector: collector |
|||
)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static SchedulerBinding instance { |
|||
get { return (SchedulerBinding) Window.instance._binding; } |
|||
set { Window.instance._binding = value; } |
|||
} |
|||
|
|||
public AppLifecycleState? lifecycleState => _lifecycleState; |
|||
AppLifecycleState? _lifecycleState; |
|||
|
|||
protected void readInitialLifecycleStateFromNativeWindow() { |
|||
if (_lifecycleState == null) { |
|||
handleAppLifecycleStateChanged(_parseAppLifecycleMessage(window.initialLifecycleState)); |
|||
} |
|||
} |
|||
|
|||
protected virtual void handleAppLifecycleStateChanged(AppLifecycleState state) { |
|||
_lifecycleState = state; |
|||
switch (state) { |
|||
case AppLifecycleState.resumed: |
|||
case AppLifecycleState.inactive: |
|||
_setFramesEnabledState(true); |
|||
break; |
|||
case AppLifecycleState.paused: |
|||
case AppLifecycleState.detached: |
|||
_setFramesEnabledState(false); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
static AppLifecycleState _parseAppLifecycleMessage(string message) { |
|||
switch (message) { |
|||
case "AppLifecycleState.paused": |
|||
return AppLifecycleState.paused; |
|||
case "AppLifecycleState.resumed": |
|||
return AppLifecycleState.resumed; |
|||
case "AppLifecycleState.inactive": |
|||
return AppLifecycleState.inactive; |
|||
case "AppLifecycleState.detached": |
|||
return AppLifecycleState.detached; |
|||
} |
|||
|
|||
throw new Exception("unknown AppLifecycleState: " + message); |
|||
} |
|||
|
|||
|
|||
public SchedulingStrategy schedulingStrategy = scheduler_.defaultSchedulingStrategy; |
|||
|
|||
readonly PriorityQueue<_TaskEntry> _taskQueue = new PriorityQueue<_TaskEntry>(); |
|||
|
|||
public Future scheduleTask<T>( |
|||
TaskCallback<T> task, |
|||
Priority priority) { |
|||
bool isFirstTask = _taskQueue.isEmpty; |
|||
_TaskEntry<T> entry = new _TaskEntry<T>( |
|||
task, |
|||
priority.value |
|||
); |
|||
_taskQueue.enqueue(entry); |
|||
if (isFirstTask && !locked) |
|||
_ensureEventLoopCallback(); |
|||
return entry.completer.future; |
|||
} |
|||
|
|||
protected override void unlocked() { |
|||
base.unlocked(); |
|||
if (_taskQueue.isNotEmpty) |
|||
_ensureEventLoopCallback(); |
|||
} |
|||
|
|||
bool _hasRequestedAnEventLoopCallback = false; |
|||
|
|||
void _ensureEventLoopCallback() { |
|||
D.assert(!locked); |
|||
D.assert(_taskQueue.count != 0); |
|||
if (_hasRequestedAnEventLoopCallback) |
|||
return; |
|||
_hasRequestedAnEventLoopCallback = true; |
|||
Timer.run(_runTasks); |
|||
} |
|||
|
|||
object _runTasks() { |
|||
_hasRequestedAnEventLoopCallback = false; |
|||
if (handleEventLoopCallback()) |
|||
_ensureEventLoopCallback(); // runs next task when there's time
|
|||
return null; |
|||
} |
|||
|
|||
bool handleEventLoopCallback() { |
|||
if (_taskQueue.isEmpty || locked) |
|||
return false; |
|||
|
|||
_TaskEntry entry = _taskQueue.first; |
|||
if (schedulingStrategy(priority: entry.priority, scheduler: this)) { |
|||
try { |
|||
_taskQueue.removeFirst(); |
|||
entry.run(); |
|||
} |
|||
catch (Exception exception) { |
|||
string callbackStack = null; |
|||
D.assert(() => { |
|||
callbackStack = entry.debugStack; |
|||
return true; |
|||
}); |
|||
UIWidgetsError.reportError(new UIWidgetsErrorDetails( |
|||
exception: exception, |
|||
library: "scheduler library", |
|||
context: "during a task callback", |
|||
informationCollector: callbackStack == null |
|||
? (InformationCollector) null |
|||
: sb => { |
|||
sb.AppendLine("\nThis exception was thrown in the context of a scheduler callback. " + |
|||
"When the scheduler callback was _registered_ (as opposed to when the " + |
|||
"exception was thrown), this was the stack: " + callbackStack); |
|||
} |
|||
)); |
|||
} |
|||
|
|||
return _taskQueue.isNotEmpty; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
int _nextFrameCallbackId = 0; |
|||
Dictionary<int, _FrameCallbackEntry> _transientCallbacks = new Dictionary<int, _FrameCallbackEntry>(); |
|||
readonly HashSet<int> _removedIds = new HashSet<int>(); |
|||
|
|||
public int transientCallbackCount => _transientCallbacks.Count; |
|||
|
|||
public int scheduleFrameCallback(FrameCallback callback, bool rescheduling = false) { |
|||
scheduleFrame(); |
|||
_nextFrameCallbackId += 1; |
|||
_transientCallbacks[_nextFrameCallbackId] = |
|||
new _FrameCallbackEntry(callback, rescheduling: rescheduling); |
|||
return _nextFrameCallbackId; |
|||
} |
|||
|
|||
public void cancelFrameCallbackWithId(int id) { |
|||
D.assert(id > 0); |
|||
_transientCallbacks.Remove(id); |
|||
_removedIds.Add(id); |
|||
} |
|||
|
|||
readonly List<FrameCallback> _persistentCallbacks = new List<FrameCallback>(); |
|||
|
|||
public void addPersistentFrameCallback(FrameCallback callback) { |
|||
_persistentCallbacks.Add(callback); |
|||
} |
|||
|
|||
readonly List<FrameCallback> _postFrameCallbacks = new List<FrameCallback>(); |
|||
|
|||
public void addPostFrameCallback(FrameCallback callback) { |
|||
_postFrameCallbacks.Add(callback); |
|||
} |
|||
|
|||
Completer _nextFrameCompleter; |
|||
|
|||
Future endOfFrame { |
|||
get { |
|||
if (_nextFrameCompleter == null) { |
|||
if (schedulerPhase == SchedulerPhase.idle) |
|||
scheduleFrame(); |
|||
_nextFrameCompleter = Completer.create(); |
|||
addPostFrameCallback((TimeSpan timeStamp) => { |
|||
_nextFrameCompleter.complete(); |
|||
_nextFrameCompleter = null; |
|||
}); |
|||
} |
|||
|
|||
return _nextFrameCompleter.future; |
|||
} |
|||
} |
|||
|
|||
public bool hasScheduledFrame => _hasScheduledFrame; |
|||
bool _hasScheduledFrame = false; |
|||
|
|||
public SchedulerPhase schedulerPhase => _schedulerPhase; |
|||
SchedulerPhase _schedulerPhase = SchedulerPhase.idle; |
|||
|
|||
public bool framesEnabled => _framesEnabled; |
|||
bool _framesEnabled = true; |
|||
|
|||
void _setFramesEnabledState(bool enabled) { |
|||
if (_framesEnabled == enabled) |
|||
return; |
|||
_framesEnabled = enabled; |
|||
if (enabled) |
|||
scheduleFrame(); |
|||
} |
|||
|
|||
protected void ensureFrameCallbacksRegistered() { |
|||
window.onBeginFrame = window.onBeginFrame ?? _handleBeginFrame; |
|||
window.onDrawFrame = window.onDrawFrame ?? _handleDrawFrame; |
|||
} |
|||
|
|||
public void ensureVisualUpdate() { |
|||
switch (schedulerPhase) { |
|||
case SchedulerPhase.idle: |
|||
case SchedulerPhase.postFrameCallbacks: |
|||
scheduleFrame(); |
|||
return; |
|||
case SchedulerPhase.transientCallbacks: |
|||
case SchedulerPhase.midFrameMicrotasks: |
|||
case SchedulerPhase.persistentCallbacks: |
|||
return; |
|||
} |
|||
} |
|||
|
|||
public void scheduleFrame() { |
|||
if (_hasScheduledFrame || !_framesEnabled) |
|||
return; |
|||
|
|||
D.assert(() => { |
|||
if (scheduler_.debugPrintScheduleFrameStacks) { |
|||
Debug.LogFormat("scheduleFrame() called. Current phase is {0}.", schedulerPhase); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
|
|||
ensureFrameCallbacksRegistered(); |
|||
Window.instance.scheduleFrame(); |
|||
_hasScheduledFrame = true; |
|||
} |
|||
|
|||
public void scheduleForcedFrame() { |
|||
if (!framesEnabled) |
|||
return; |
|||
|
|||
if (_hasScheduledFrame) |
|||
return; |
|||
|
|||
D.assert(() => { |
|||
if (scheduler_.debugPrintScheduleFrameStacks) { |
|||
Debug.LogFormat("scheduleForcedFrame() called. Current phase is {0}.", schedulerPhase); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
|
|||
ensureFrameCallbacksRegistered(); |
|||
Window.instance.scheduleFrame(); |
|||
_hasScheduledFrame = true; |
|||
} |
|||
|
|||
bool _warmUpFrame = false; |
|||
|
|||
public void scheduleWarmUpFrame() { |
|||
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) |
|||
return; |
|||
|
|||
_warmUpFrame = true; |
|||
Timeline.startSync("Warm-up frame"); |
|||
|
|||
bool hadScheduledFrame = _hasScheduledFrame; |
|||
// We use timers here to ensure that microtasks flush in between.
|
|||
Timer.run(() => { |
|||
D.assert(_warmUpFrame); |
|||
handleBeginFrame(null); |
|||
return null; |
|||
}); |
|||
Timer.run(() => { |
|||
D.assert(_warmUpFrame); |
|||
handleDrawFrame(); |
|||
// We call resetEpoch after this frame so that, in the hot reload case,
|
|||
// the very next frame pretends to have occurred immediately after this
|
|||
// warm-up frame. The warm-up frame's timestamp will typically be far in
|
|||
// the past (the time of the last real frame), so if we didn't reset the
|
|||
// epoch we would see a sudden jump from the old time in the warm-up frame
|
|||
// to the new time in the "real" frame. The biggest problem with this is
|
|||
// that implicit animations end up being triggered at the old time and
|
|||
// then skipping every frame and finishing in the new time.
|
|||
resetEpoch(); |
|||
_warmUpFrame = false; |
|||
if (hadScheduledFrame) |
|||
scheduleFrame(); |
|||
return null; |
|||
}); |
|||
|
|||
// Lock events so touch events etc don't insert themselves until the
|
|||
// scheduled frame has finished.
|
|||
lockEvents(() => endOfFrame.then(v => { |
|||
Timeline.finishSync(); |
|||
return FutureOr.nullValue; |
|||
})); |
|||
} |
|||
|
|||
TimeSpan? _firstRawTimeStampInEpoch; |
|||
TimeSpan _epochStart = TimeSpan.Zero; |
|||
TimeSpan _lastRawTimeStamp = TimeSpan.Zero; |
|||
|
|||
public void resetEpoch() { |
|||
_epochStart = _adjustForEpoch(_lastRawTimeStamp); |
|||
_firstRawTimeStampInEpoch = null; |
|||
} |
|||
|
|||
TimeSpan _adjustForEpoch(TimeSpan rawTimeStamp) { |
|||
var rawDurationSinceEpoch = _firstRawTimeStampInEpoch == null |
|||
? TimeSpan.Zero |
|||
: rawTimeStamp - _firstRawTimeStampInEpoch.Value; |
|||
return new TimeSpan((long) (rawDurationSinceEpoch.Ticks / scheduler_.timeDilation) + |
|||
_epochStart.Ticks); |
|||
} |
|||
|
|||
public TimeSpan currentFrameTimeStamp { |
|||
get { |
|||
D.assert(_currentFrameTimeStamp != null); |
|||
return _currentFrameTimeStamp.Value; |
|||
} |
|||
} |
|||
|
|||
TimeSpan? _currentFrameTimeStamp; |
|||
|
|||
public TimeSpan currentSystemFrameTimeStamp => _lastRawTimeStamp; |
|||
|
|||
int _debugFrameNumber = 0; |
|||
string _debugBanner; |
|||
bool _ignoreNextEngineDrawFrame = false; |
|||
|
|||
void _handleBeginFrame(TimeSpan rawTimeStamp) { |
|||
if (_warmUpFrame) { |
|||
D.assert(!_ignoreNextEngineDrawFrame); |
|||
_ignoreNextEngineDrawFrame = true; |
|||
return; |
|||
} |
|||
|
|||
handleBeginFrame(rawTimeStamp); |
|||
} |
|||
|
|||
void _handleDrawFrame() { |
|||
if (_ignoreNextEngineDrawFrame) { |
|||
_ignoreNextEngineDrawFrame = false; |
|||
return; |
|||
} |
|||
|
|||
handleDrawFrame(); |
|||
} |
|||
|
|||
public void handleBeginFrame(TimeSpan? rawTimeStamp) { |
|||
Timeline.startSync("Frame"); |
|||
|
|||
_firstRawTimeStampInEpoch = _firstRawTimeStampInEpoch ?? rawTimeStamp; |
|||
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp); |
|||
|
|||
if (rawTimeStamp != null) |
|||
_lastRawTimeStamp = rawTimeStamp.Value; |
|||
|
|||
D.assert(() => { |
|||
_debugFrameNumber += 1; |
|||
|
|||
if (scheduler_.debugPrintBeginFrameBanner || scheduler_.debugPrintEndFrameBanner) { |
|||
StringBuilder frameTimeStampDescription = new StringBuilder(); |
|||
if (rawTimeStamp != null) { |
|||
_debugDescribeTimeStamp(_currentFrameTimeStamp.Value, frameTimeStampDescription); |
|||
} |
|||
else { |
|||
frameTimeStampDescription.Append("(warm-up frame)"); |
|||
} |
|||
|
|||
_debugBanner = |
|||
$"▄▄▄▄▄▄▄▄ Frame {_debugFrameNumber.ToString().PadRight(7)} ${frameTimeStampDescription.ToString().PadLeft(18)} ▄▄▄▄▄▄▄▄"; |
|||
if (scheduler_.debugPrintBeginFrameBanner) |
|||
Debug.Log(_debugBanner); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
|
|||
D.assert(_schedulerPhase == SchedulerPhase.idle); |
|||
_hasScheduledFrame = false; |
|||
|
|||
try { |
|||
Timeline.startSync("Animate"); |
|||
_schedulerPhase = SchedulerPhase.transientCallbacks; |
|||
var callbacks = _transientCallbacks; |
|||
_transientCallbacks = new Dictionary<int, _FrameCallbackEntry>(); |
|||
foreach (var entry in callbacks) { |
|||
if (!_removedIds.Contains(entry.Key)) { |
|||
_invokeFrameCallback( |
|||
entry.Value.callback, _currentFrameTimeStamp.Value, entry.Value.debugStack); |
|||
} |
|||
} |
|||
|
|||
_removedIds.Clear(); |
|||
} |
|||
finally { |
|||
_schedulerPhase = SchedulerPhase.midFrameMicrotasks; |
|||
} |
|||
} |
|||
|
|||
public void handleDrawFrame() { |
|||
D.assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks); |
|||
Timeline.finishSync(); |
|||
|
|||
try { |
|||
_schedulerPhase = SchedulerPhase.persistentCallbacks; |
|||
foreach (FrameCallback callback in _persistentCallbacks) |
|||
_invokeFrameCallback(callback, _currentFrameTimeStamp.Value); |
|||
|
|||
_schedulerPhase = SchedulerPhase.postFrameCallbacks; |
|||
var localPostFrameCallbacks = new List<FrameCallback>(_postFrameCallbacks); |
|||
_postFrameCallbacks.Clear(); |
|||
foreach (FrameCallback callback in localPostFrameCallbacks) |
|||
_invokeFrameCallback(callback, _currentFrameTimeStamp.Value); |
|||
} |
|||
finally { |
|||
_schedulerPhase = SchedulerPhase.idle; |
|||
D.assert(() => { |
|||
if (scheduler_.debugPrintEndFrameBanner) |
|||
Debug.Log(new string('▀', _debugBanner.Length)); |
|||
_debugBanner = null; |
|||
return true; |
|||
}); |
|||
_currentFrameTimeStamp = null; |
|||
} |
|||
} |
|||
|
|||
void _profileFramePostEvent(int frameNumber, FrameTiming frameTiming) { |
|||
developer_.postEvent("Flutter.Frame", new Hashtable { |
|||
{"number", frameNumber}, |
|||
{"startTime", frameTiming.timestampInMicroseconds(FramePhase.buildStart)}, |
|||
{"elapsed", (int) (frameTiming.totalSpan.TotalMilliseconds * 1000)}, |
|||
{"build", (int) (frameTiming.buildDuration.TotalMilliseconds * 1000)}, |
|||
{"raster", (int) (frameTiming.rasterDuration.TotalMilliseconds * 1000)}, |
|||
}); |
|||
} |
|||
|
|||
|
|||
static void _debugDescribeTimeStamp(TimeSpan timeStamp, StringBuilder buffer) { |
|||
if (timeStamp.TotalDays > 0) |
|||
buffer.AppendFormat("{0}d ", timeStamp.Days); |
|||
if (timeStamp.TotalHours > 0) |
|||
buffer.AppendFormat("{0}h ", timeStamp.Hours); |
|||
if (timeStamp.TotalMinutes > 0) |
|||
buffer.AppendFormat("{0}m ", timeStamp.Minutes); |
|||
if (timeStamp.TotalSeconds > 0) |
|||
buffer.AppendFormat("{0}s ", timeStamp.Seconds); |
|||
buffer.AppendFormat("{0}", timeStamp.Milliseconds); |
|||
|
|||
int microseconds = (int) (timeStamp.Ticks % 10000 / 10); |
|||
if (microseconds > 0) |
|||
buffer.AppendFormat(".{0}", microseconds.ToString().PadLeft(3, '0')); |
|||
buffer.Append("ms"); |
|||
} |
|||
|
|||
void _invokeFrameCallback(FrameCallback callback, TimeSpan timeStamp, string callbackStack = null) { |
|||
D.assert(callback != null); |
|||
D.assert(_FrameCallbackEntry.debugCurrentCallbackStack == null); |
|||
D.assert(() => { |
|||
_FrameCallbackEntry.debugCurrentCallbackStack = callbackStack; |
|||
return true; |
|||
}); |
|||
|
|||
try { |
|||
callback(timeStamp); |
|||
} |
|||
catch (Exception ex) { |
|||
UIWidgetsError.reportError(new UIWidgetsErrorDetails( |
|||
exception: ex, |
|||
library: "scheduler library", |
|||
context: "during a scheduler callback", |
|||
informationCollector: callbackStack == null |
|||
? (InformationCollector) null |
|||
: information => { |
|||
information.AppendLine( |
|||
"\nThis exception was thrown in the context of a scheduler callback. " + |
|||
"When the scheduler callback was _registered_ (as opposed to when the " + |
|||
"exception was thrown), this was the stack:" |
|||
); |
|||
foreach (var line in UIWidgetsError.defaultStackFilter( |
|||
callbackStack.TrimEnd().Split('\n'))) { |
|||
information.AppendLine(line); |
|||
} |
|||
} |
|||
)); |
|||
} |
|||
|
|||
D.assert(() => { |
|||
_FrameCallbackEntry.debugCurrentCallbackStack = null; |
|||
return true; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public static partial class scheduler_ { |
|||
public static bool defaultSchedulingStrategy(int priority, SchedulerBinding scheduler) { |
|||
if (scheduler.transientCallbackCount > 0) |
|||
return priority >= Priority.animation.value; |
|||
return true; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 0756240ed259436a82cdc6ee65831a4f |
|||
timeCreated: 1599458325 |
|
|||
namespace Unity.UIWidgets.scheduler2 { |
|||
public static partial class scheduler_ { |
|||
public static bool debugPrintBeginFrameBanner = false; |
|||
public static bool debugPrintEndFrameBanner = false; |
|||
public static bool debugPrintScheduleFrameStacks = false; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 486e9bc3d81445fba548c7cddf682b4d |
|||
timeCreated: 1599458325 |
|
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.scheduler2 { |
|||
public class Priority { |
|||
Priority(int value) { |
|||
_value = value; |
|||
} |
|||
|
|||
public int value => _value; |
|||
int _value; |
|||
|
|||
public static readonly Priority idle = new Priority(0); |
|||
|
|||
public static readonly Priority animation = new Priority(100000); |
|||
|
|||
public static readonly Priority touch = new Priority(200000); |
|||
|
|||
public static readonly int kMaxOffset = 10000; |
|||
|
|||
public static Priority operator +(Priority it, int offset) { |
|||
if (Mathf.Abs(offset) > kMaxOffset) { |
|||
offset = kMaxOffset * (int) Mathf.Sign(offset); |
|||
} |
|||
|
|||
return new Priority(it._value + offset); |
|||
} |
|||
|
|||
public static Priority operator -(Priority it, int offset) => it + (-offset); |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 814da0eaecc042059b36143e73f60f48 |
|||
timeCreated: 1599458325 |
|
|||
using System; |
|||
using System.Text; |
|||
using Unity.UIWidgets.async2; |
|||
using Unity.UIWidgets.foundation; |
|||
using Unity.UIWidgets.ui; |
|||
using UnityEngine; |
|||
|
|||
namespace Unity.UIWidgets.scheduler2 { |
|||
public delegate void TickerCallback(TimeSpan elapsed); |
|||
|
|||
public interface TickerProvider { |
|||
Ticker createTicker(TickerCallback onTick); |
|||
} |
|||
|
|||
public class Ticker : IDisposable { |
|||
public Ticker(TickerCallback onTick, string debugLabel = null) { |
|||
D.assert(() => { |
|||
_debugCreationStack = StackTraceUtility.ExtractStackTrace(); |
|||
return true; |
|||
}); |
|||
_onTick = onTick; |
|||
this.debugLabel = debugLabel; |
|||
} |
|||
|
|||
TickerFuture _future; |
|||
|
|||
public bool muted { |
|||
get => _muted; |
|||
set { |
|||
if (value == _muted) |
|||
return; |
|||
|
|||
_muted = value; |
|||
if (value) { |
|||
unscheduleTick(); |
|||
} |
|||
else if (shouldScheduleTick) { |
|||
scheduleTick(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
bool _muted = false; |
|||
|
|||
public bool isTicking { |
|||
get { |
|||
if (_future == null) |
|||
return false; |
|||
if (muted) |
|||
return false; |
|||
if (SchedulerBinding.instance.framesEnabled) |
|||
return true; |
|||
if (SchedulerBinding.instance.schedulerPhase != SchedulerPhase.idle) |
|||
return true; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public bool isActive { |
|||
get { return _future != null; } |
|||
} |
|||
|
|||
TimeSpan? _startTime; |
|||
|
|||
public TickerFuture start() { |
|||
D.assert(() => { |
|||
if (isActive) { |
|||
throw new UIWidgetsError( |
|||
"A ticker was started twice.\nA ticker that is already active cannot be started again without first stopping it.\n" + |
|||
"The affected ticker was: " + toString(debugIncludeStack: true)); |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
|
|||
D.assert(_startTime == null); |
|||
_future = new TickerFuture(); |
|||
if (shouldScheduleTick) { |
|||
scheduleTick(); |
|||
} |
|||
|
|||
if (SchedulerBinding.instance.schedulerPhase > SchedulerPhase.idle && |
|||
SchedulerBinding.instance.schedulerPhase < SchedulerPhase.postFrameCallbacks) { |
|||
_startTime = SchedulerBinding.instance.currentFrameTimeStamp; |
|||
} |
|||
|
|||
return _future; |
|||
} |
|||
|
|||
public void stop(bool canceled = false) { |
|||
if (!isActive) |
|||
return; |
|||
|
|||
var localFuture = _future; |
|||
_future = null; |
|||
_startTime = null; |
|||
D.assert(!isActive); |
|||
|
|||
unscheduleTick(); |
|||
if (canceled) { |
|||
localFuture._cancel(this); |
|||
} |
|||
else { |
|||
localFuture._complete(); |
|||
} |
|||
} |
|||
|
|||
readonly TickerCallback _onTick; |
|||
|
|||
int? _animationId; |
|||
|
|||
protected bool scheduled { |
|||
get => _animationId != null; |
|||
} |
|||
|
|||
protected bool shouldScheduleTick => !muted && isActive && !scheduled; |
|||
|
|||
void _tick(TimeSpan timeStamp) { |
|||
D.assert(isTicking); |
|||
D.assert(scheduled); |
|||
_animationId = null; |
|||
|
|||
_startTime = _startTime ?? timeStamp; |
|||
_onTick(timeStamp - _startTime.Value); |
|||
|
|||
if (shouldScheduleTick) |
|||
scheduleTick(rescheduling: true); |
|||
} |
|||
|
|||
protected void scheduleTick(bool rescheduling = false) { |
|||
D.assert(!scheduled); |
|||
D.assert(shouldScheduleTick); |
|||
_animationId = SchedulerBinding.instance.scheduleFrameCallback(_tick, rescheduling: rescheduling); |
|||
} |
|||
|
|||
protected void unscheduleTick() { |
|||
if (scheduled) { |
|||
SchedulerBinding.instance.cancelFrameCallbackWithId(_animationId.Value); |
|||
_animationId = null; |
|||
} |
|||
|
|||
D.assert(!shouldScheduleTick); |
|||
} |
|||
|
|||
public void absorbTicker(Ticker originalTicker) { |
|||
D.assert(!isActive); |
|||
D.assert(_future == null); |
|||
D.assert(_startTime == null); |
|||
D.assert(_animationId == null); |
|||
D.assert((originalTicker._future == null) == (originalTicker._startTime == null), |
|||
() => "Cannot absorb Ticker after it has been disposed."); |
|||
if (originalTicker._future != null) { |
|||
_future = originalTicker._future; |
|||
_startTime = originalTicker._startTime; |
|||
if (shouldScheduleTick) { |
|||
scheduleTick(); |
|||
} |
|||
|
|||
originalTicker._future = null; |
|||
originalTicker.unscheduleTick(); |
|||
} |
|||
|
|||
originalTicker.Dispose(); |
|||
} |
|||
|
|||
public virtual void Dispose() { |
|||
if (_future != null) { |
|||
var localFuture = _future; |
|||
_future = null; |
|||
D.assert(!isActive); |
|||
unscheduleTick(); |
|||
localFuture._cancel(this); |
|||
} |
|||
|
|||
D.assert(() => { |
|||
_startTime = TimeSpan.Zero; |
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
public readonly string debugLabel; |
|||
internal string _debugCreationStack; |
|||
|
|||
public override string ToString() { |
|||
return toString(debugIncludeStack: false); |
|||
} |
|||
|
|||
public string toString(bool debugIncludeStack = false) { |
|||
var buffer = new StringBuilder(); |
|||
buffer.Append(GetType() + "("); |
|||
D.assert(() => { |
|||
buffer.Append(debugLabel ?? ""); |
|||
return true; |
|||
}); |
|||
buffer.Append(')'); |
|||
D.assert(() => { |
|||
if (debugIncludeStack) { |
|||
buffer.AppendLine(); |
|||
buffer.AppendLine("The stack trace when the " + GetType() + " was actually created was: "); |
|||
foreach (var line in UIWidgetsError.defaultStackFilter( |
|||
_debugCreationStack.TrimEnd().Split('\n'))) { |
|||
buffer.AppendLine(line); |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
}); |
|||
return buffer.ToString(); |
|||
} |
|||
} |
|||
|
|||
public class TickerFuture : Future { |
|||
internal TickerFuture() { |
|||
} |
|||
|
|||
public static TickerFuture complete() { |
|||
var result = new TickerFuture(); |
|||
result._complete(); |
|||
return result; |
|||
} |
|||
|
|||
readonly Completer _primaryCompleter = Completer.create(); |
|||
Completer _secondaryCompleter; |
|||
bool? _completed; // null means unresolved, true means complete, false means canceled
|
|||
|
|||
internal void _complete() { |
|||
D.assert(_completed == null); |
|||
_completed = true; |
|||
_primaryCompleter.complete(); |
|||
_secondaryCompleter?.complete(); |
|||
} |
|||
|
|||
internal void _cancel(Ticker ticker) { |
|||
D.assert(_completed == null); |
|||
_completed = false; |
|||
_secondaryCompleter?.completeError(new TickerCanceled(ticker)); |
|||
} |
|||
|
|||
public void whenCompleteOrCancel(VoidCallback callback) { |
|||
orCancel.then((value) => { |
|||
callback(); |
|||
return FutureOr.nullValue; |
|||
}, ex => { |
|||
callback(); |
|||
return FutureOr.nullValue; |
|||
}); |
|||
} |
|||
|
|||
public Future orCancel { |
|||
get { |
|||
if (_secondaryCompleter == null) { |
|||
_secondaryCompleter = Completer.create(); |
|||
if (_completed != null) { |
|||
if (_completed.Value) { |
|||
_secondaryCompleter.complete(); |
|||
} |
|||
else { |
|||
_secondaryCompleter.completeError(new TickerCanceled()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return _secondaryCompleter.future; |
|||
} |
|||
} |
|||
|
|||
// public override Stream asStream() {
|
|||
// return _primaryCompleter.future.asStream();
|
|||
// }
|
|||
|
|||
public override Future catchError(Func<Exception, FutureOr> onError, Func<Exception, bool> test = null) { |
|||
return _primaryCompleter.future.catchError(onError, test: test); |
|||
} |
|||
|
|||
public override Future then(Func<object, FutureOr> onValue, Func<Exception, FutureOr> onError = null) { |
|||
return _primaryCompleter.future.then(onValue, onError: onError); |
|||
} |
|||
|
|||
public override Future timeout(TimeSpan timeLimit, Func<FutureOr> onTimeout = null) { |
|||
return _primaryCompleter.future.timeout(timeLimit, onTimeout: onTimeout); |
|||
} |
|||
|
|||
public override Future whenComplete(Func<object> action) { |
|||
return _primaryCompleter.future.whenComplete(action); |
|||
} |
|||
|
|||
public override string ToString() => |
|||
$"{Diagnostics.describeIdentity(this)}({(_completed == null ? "active" : (_completed.Value ? "complete" : "canceled"))})"; |
|||
} |
|||
|
|||
|
|||
public class TickerCanceled : Exception { |
|||
public TickerCanceled(Ticker ticker = null) { |
|||
this.ticker = ticker; |
|||
} |
|||
|
|||
public readonly Ticker ticker; |
|||
|
|||
public override string ToString() { |
|||
if (ticker != null) { |
|||
return "This ticker was canceled: " + ticker; |
|||
} |
|||
|
|||
return "The ticker was canceled before the \"orCancel\" property was first used."; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 9ee135f21ca64943a5f75a04a0e30cc3 |
|||
timeCreated: 1599458326 |
|
|||
#include "asset_manager.h"
|
|||
|
|||
#include "flutter/fml/trace_event.h"
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
AssetManager::AssetManager() = default; |
|||
|
|||
AssetManager::~AssetManager() = default; |
|||
|
|||
void AssetManager::PushFront(std::unique_ptr<AssetResolver> resolver) { |
|||
if (resolver == nullptr || !resolver->IsValid()) { |
|||
return; |
|||
} |
|||
|
|||
resolvers_.push_front(std::move(resolver)); |
|||
} |
|||
|
|||
void AssetManager::PushBack(std::unique_ptr<AssetResolver> resolver) { |
|||
if (resolver == nullptr || !resolver->IsValid()) { |
|||
return; |
|||
} |
|||
|
|||
resolvers_.push_back(std::move(resolver)); |
|||
} |
|||
|
|||
// |AssetResolver|
|
|||
std::unique_ptr<fml::Mapping> AssetManager::GetAsMapping( |
|||
const std::string& asset_name) const { |
|||
if (asset_name.empty()) { |
|||
return std::unique_ptr<fml::Mapping>(nullptr); |
|||
} |
|||
TRACE_EVENT1("uiwidgets", "AssetManager::GetAsMapping", "name", |
|||
asset_name.c_str()); |
|||
for (const auto& resolver : resolvers_) { |
|||
auto mapping = resolver->GetAsMapping(asset_name); |
|||
if (mapping != nullptr) { |
|||
return mapping; |
|||
} |
|||
} |
|||
FML_DLOG(WARNING) << "Could not find asset: " << asset_name; |
|||
return std::unique_ptr<fml::Mapping>(nullptr); |
|||
} |
|||
|
|||
// |AssetResolver|
|
|||
bool AssetManager::IsValid() const { return !resolvers_.empty(); } |
|||
|
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include <deque> |
|||
#include <memory> |
|||
#include <string> |
|||
|
|||
#include "asset_resolver.h" |
|||
#include "flutter/fml/macros.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class AssetManager final : public AssetResolver { |
|||
public: |
|||
AssetManager(); |
|||
|
|||
~AssetManager() override; |
|||
|
|||
void PushFront(std::unique_ptr<AssetResolver> resolver); |
|||
|
|||
void PushBack(std::unique_ptr<AssetResolver> resolver); |
|||
|
|||
// |AssetResolver| |
|||
bool IsValid() const override; |
|||
|
|||
// |AssetResolver| |
|||
std::unique_ptr<fml::Mapping> GetAsMapping( |
|||
const std::string& asset_name) const override; |
|||
|
|||
private: |
|||
std::deque<std::unique_ptr<AssetResolver>> resolvers_; |
|||
|
|||
FML_DISALLOW_COPY_AND_ASSIGN(AssetManager); |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#pragma once |
|||
|
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
#include "flutter/fml/macros.h" |
|||
#include "flutter/fml/mapping.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class AssetResolver { |
|||
public: |
|||
AssetResolver() = default; |
|||
|
|||
virtual ~AssetResolver() = default; |
|||
|
|||
virtual bool IsValid() const = 0; |
|||
|
|||
[[nodiscard]] virtual std::unique_ptr<fml::Mapping> GetAsMapping( |
|||
const std::string& asset_name) const = 0; |
|||
|
|||
private: |
|||
FML_DISALLOW_COPY_AND_ASSIGN(AssetResolver); |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#include "directory_asset_bundle.h"
|
|||
|
|||
#include <utility>
|
|||
|
|||
#include "flutter/fml/file.h"
|
|||
#include "flutter/fml/mapping.h"
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
DirectoryAssetBundle::DirectoryAssetBundle(fml::UniqueFD descriptor) |
|||
: descriptor_(std::move(descriptor)) { |
|||
if (!fml::IsDirectory(descriptor_)) { |
|||
return; |
|||
} |
|||
is_valid_ = true; |
|||
} |
|||
|
|||
DirectoryAssetBundle::~DirectoryAssetBundle() = default; |
|||
|
|||
// |AssetResolver|
|
|||
bool DirectoryAssetBundle::IsValid() const { return is_valid_; } |
|||
|
|||
// |AssetResolver|
|
|||
std::unique_ptr<fml::Mapping> DirectoryAssetBundle::GetAsMapping( |
|||
const std::string& asset_name) const { |
|||
if (!is_valid_) { |
|||
FML_DLOG(WARNING) << "Asset bundle was not valid."; |
|||
return std::unique_ptr<fml::Mapping>(nullptr); |
|||
} |
|||
|
|||
auto mapping = std::make_unique<fml::FileMapping>(fml::OpenFile( |
|||
descriptor_, asset_name.c_str(), false, fml::FilePermission::kRead)); |
|||
|
|||
if (!mapping->IsValid()) { |
|||
return std::unique_ptr<fml::Mapping>(nullptr); |
|||
} |
|||
|
|||
return mapping; |
|||
} |
|||
|
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include "asset_resolver.h" |
|||
#include "flutter/fml/macros.h" |
|||
#include "flutter/fml/memory/ref_counted.h" |
|||
#include "flutter/fml/unique_fd.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class DirectoryAssetBundle : public AssetResolver { |
|||
public: |
|||
explicit DirectoryAssetBundle(fml::UniqueFD descriptor); |
|||
|
|||
~DirectoryAssetBundle() override; |
|||
|
|||
private: |
|||
const fml::UniqueFD descriptor_; |
|||
bool is_valid_ = false; |
|||
|
|||
// |AssetResolver| |
|||
bool IsValid() const override; |
|||
|
|||
// |AssetResolver| |
|||
std::unique_ptr<fml::Mapping> GetAsMapping( |
|||
const std::string& asset_name) const override; |
|||
|
|||
FML_DISALLOW_COPY_AND_ASSIGN(DirectoryAssetBundle); |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#pragma once |
|||
|
|||
#include "flow/embedded_views.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class GPUSurfaceDelegate { |
|||
public: |
|||
virtual ~GPUSurfaceDelegate() {} |
|||
|
|||
virtual ExternalViewEmbedder* GetExternalViewEmbedder() = 0; |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#include "gpu_surface_gl.h"
|
|||
|
|||
#include "flutter/fml/base32.h"
|
|||
#include "flutter/fml/logging.h"
|
|||
#include "flutter/fml/size.h"
|
|||
#include "flutter/fml/trace_event.h"
|
|||
#include "include/core/SkColorFilter.h"
|
|||
#include "include/core/SkSurface.h"
|
|||
#include "include/gpu/GrBackendSurface.h"
|
|||
#include "include/gpu/GrContextOptions.h"
|
|||
#include "shell/common/persistent_cache.h"
|
|||
|
|||
// These are common defines present on all OpenGL headers. However, we don't
|
|||
// want to perform GL header reasolution on each platform we support. So just
|
|||
// define these upfront. It is unlikely we will need more. But, if we do, we can
|
|||
// add the same here.
|
|||
#define GPU_GL_RGBA8 0x8058
|
|||
#define GPU_GL_RGBA4 0x8056
|
|||
#define GPU_GL_RGB565 0x8D62
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
// Default maximum number of budgeted resources in the cache.
|
|||
static const int kGrCacheMaxCount = 8192; |
|||
|
|||
// Default maximum number of bytes of GPU memory of budgeted resources in the
|
|||
// cache.
|
|||
// The shell will dynamically increase or decrease this cache based on the
|
|||
// viewport size, unless a user has specifically requested a size on the Skia
|
|||
// system channel.
|
|||
static const size_t kGrCacheMaxByteSize = 24 * (1 << 20); |
|||
|
|||
GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, |
|||
bool render_to_surface) |
|||
: delegate_(delegate), |
|||
render_to_surface_(render_to_surface), |
|||
weak_factory_(this) { |
|||
if (!delegate_->GLContextMakeCurrent()) { |
|||
FML_LOG(ERROR) |
|||
<< "Could not make the context current to setup the gr context."; |
|||
return; |
|||
} |
|||
|
|||
GrContextOptions options; |
|||
|
|||
if (PersistentCache::cache_sksl()) { |
|||
FML_LOG(INFO) << "Cache SkSL"; |
|||
options.fShaderCacheStrategy = GrContextOptions::ShaderCacheStrategy::kSkSL; |
|||
} |
|||
PersistentCache::MarkStrategySet(); |
|||
options.fPersistentCache = PersistentCache::GetCacheForProcess(); |
|||
|
|||
options.fAvoidStencilBuffers = true; |
|||
|
|||
// To get video playback on the widest range of devices, we limit Skia to
|
|||
// ES2 shading language when the ES3 external image extension is missing.
|
|||
options.fPreferExternalImagesOverES3 = true; |
|||
|
|||
// TODO(goderbauer): remove option when skbug.com/7523 is fixed.
|
|||
// A similar work-around is also used in shell/common/io_manager.cc.
|
|||
options.fDisableGpuYUVConversion = true; |
|||
|
|||
auto context = GrContext::MakeGL(delegate_->GetGLInterface(), options); |
|||
|
|||
if (context == nullptr) { |
|||
FML_LOG(ERROR) << "Failed to setup Skia Gr context."; |
|||
return; |
|||
} |
|||
|
|||
context_ = std::move(context); |
|||
|
|||
context_->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize); |
|||
|
|||
context_owner_ = true; |
|||
|
|||
valid_ = true; |
|||
|
|||
std::vector<PersistentCache::SkSLCache> caches = |
|||
PersistentCache::GetCacheForProcess()->LoadSkSLs(); |
|||
int compiled_count = 0; |
|||
for (const auto& cache : caches) { |
|||
compiled_count += context_->precompileShader(*cache.first, *cache.second); |
|||
} |
|||
FML_LOG(INFO) << "Found " << caches.size() << " SkSL shaders; precompiled " |
|||
<< compiled_count; |
|||
|
|||
delegate_->GLContextClearCurrent(); |
|||
} |
|||
|
|||
GPUSurfaceGL::GPUSurfaceGL(sk_sp<GrContext> gr_context, |
|||
GPUSurfaceGLDelegate* delegate, |
|||
bool render_to_surface) |
|||
: delegate_(delegate), |
|||
context_(gr_context), |
|||
render_to_surface_(render_to_surface), |
|||
weak_factory_(this) { |
|||
if (!delegate_->GLContextMakeCurrent()) { |
|||
FML_LOG(ERROR) |
|||
<< "Could not make the context current to setup the gr context."; |
|||
return; |
|||
} |
|||
|
|||
delegate_->GLContextClearCurrent(); |
|||
|
|||
valid_ = true; |
|||
context_owner_ = false; |
|||
} |
|||
|
|||
GPUSurfaceGL::~GPUSurfaceGL() { |
|||
if (!valid_) { |
|||
return; |
|||
} |
|||
|
|||
if (!delegate_->GLContextMakeCurrent()) { |
|||
FML_LOG(ERROR) << "Could not make the context current to destroy the " |
|||
"GrContext resources."; |
|||
return; |
|||
} |
|||
|
|||
onscreen_surface_ = nullptr; |
|||
if (context_owner_) { |
|||
context_->releaseResourcesAndAbandonContext(); |
|||
} |
|||
context_ = nullptr; |
|||
|
|||
delegate_->GLContextClearCurrent(); |
|||
} |
|||
|
|||
// |Surface|
|
|||
bool GPUSurfaceGL::IsValid() { return valid_; } |
|||
|
|||
static SkColorType FirstSupportedColorType(GrContext* context, |
|||
GrGLenum* format) { |
|||
#define RETURN_IF_RENDERABLE(x, y) \
|
|||
if (context->colorTypeSupportedAsSurface((x))) { \ |
|||
*format = (y); \ |
|||
return (x); \ |
|||
} |
|||
RETURN_IF_RENDERABLE(kRGBA_8888_SkColorType, GPU_GL_RGBA8); |
|||
RETURN_IF_RENDERABLE(kARGB_4444_SkColorType, GPU_GL_RGBA4); |
|||
RETURN_IF_RENDERABLE(kRGB_565_SkColorType, GPU_GL_RGB565); |
|||
return kUnknown_SkColorType; |
|||
} |
|||
|
|||
static sk_sp<SkSurface> WrapOnscreenSurface(GrContext* context, |
|||
const SkISize& size, intptr_t fbo) { |
|||
GrGLenum format; |
|||
const SkColorType color_type = FirstSupportedColorType(context, &format); |
|||
|
|||
GrGLFramebufferInfo framebuffer_info = {}; |
|||
framebuffer_info.fFBOID = static_cast<GrGLuint>(fbo); |
|||
framebuffer_info.fFormat = format; |
|||
|
|||
GrBackendRenderTarget render_target(size.width(), // width
|
|||
size.height(), // height
|
|||
0, // sample count
|
|||
0, // stencil bits (TODO)
|
|||
framebuffer_info // framebuffer info
|
|||
); |
|||
|
|||
sk_sp<SkColorSpace> colorspace = SkColorSpace::MakeSRGB(); |
|||
|
|||
SkSurfaceProps surface_props( |
|||
SkSurfaceProps::InitType::kLegacyFontHost_InitType); |
|||
|
|||
return SkSurface::MakeFromBackendRenderTarget( |
|||
context, // gr context
|
|||
render_target, // render target
|
|||
GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin, // origin
|
|||
color_type, // color type
|
|||
colorspace, // colorspace
|
|||
&surface_props // surface properties
|
|||
); |
|||
} |
|||
|
|||
bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { |
|||
if (onscreen_surface_ != nullptr && |
|||
size == SkISize::Make(onscreen_surface_->width(), |
|||
onscreen_surface_->height())) { |
|||
// Surface size appears unchanged. So bail.
|
|||
return true; |
|||
} |
|||
|
|||
// We need to do some updates.
|
|||
TRACE_EVENT0("uiwidgets", "UpdateSurfacesSize"); |
|||
|
|||
// Either way, we need to get rid of previous surface.
|
|||
onscreen_surface_ = nullptr; |
|||
|
|||
if (size.isEmpty()) { |
|||
FML_LOG(ERROR) << "Cannot create surfaces of empty size."; |
|||
return false; |
|||
} |
|||
|
|||
sk_sp<SkSurface> onscreen_surface; |
|||
|
|||
onscreen_surface = |
|||
WrapOnscreenSurface(context_.get(), // GL context
|
|||
size, // root surface size
|
|||
delegate_->GLContextFBO() // window FBO ID
|
|||
); |
|||
|
|||
if (onscreen_surface == nullptr) { |
|||
// If the onscreen surface could not be wrapped. There is absolutely no
|
|||
// point in moving forward.
|
|||
FML_LOG(ERROR) << "Could not wrap onscreen surface."; |
|||
return false; |
|||
} |
|||
|
|||
onscreen_surface_ = std::move(onscreen_surface); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
// |Surface|
|
|||
SkMatrix GPUSurfaceGL::GetRootTransformation() const { |
|||
return delegate_->GLContextSurfaceTransformation(); |
|||
} |
|||
|
|||
// |Surface|
|
|||
std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) { |
|||
if (delegate_ == nullptr) { |
|||
return nullptr; |
|||
} |
|||
|
|||
if (!delegate_->GLContextMakeCurrent()) { |
|||
FML_LOG(ERROR) |
|||
<< "Could not make the context current to acquire the frame."; |
|||
return nullptr; |
|||
} |
|||
|
|||
// TODO(38466): Refactor GPU surface APIs take into account the fact that an
|
|||
// external view embedder may want to render to the root surface.
|
|||
if (!render_to_surface_) { |
|||
return std::make_unique<SurfaceFrame>( |
|||
nullptr, true, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) { |
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
const auto root_surface_transformation = GetRootTransformation(); |
|||
|
|||
sk_sp<SkSurface> surface = |
|||
AcquireRenderSurface(size, root_surface_transformation); |
|||
|
|||
if (surface == nullptr) { |
|||
return nullptr; |
|||
} |
|||
|
|||
surface->getCanvas()->setMatrix(root_surface_transformation); |
|||
|
|||
SurfaceFrame::SubmitCallback submit_callback = |
|||
[weak = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame, |
|||
SkCanvas* canvas) { |
|||
return weak ? weak->PresentSurface(canvas) : false; |
|||
}; |
|||
|
|||
return std::make_unique<SurfaceFrame>( |
|||
surface, delegate_->SurfaceSupportsReadback(), submit_callback); |
|||
} |
|||
|
|||
bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { |
|||
if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) { |
|||
return false; |
|||
} |
|||
|
|||
{ |
|||
TRACE_EVENT0("uiwidgets", "SkCanvas::Flush"); |
|||
onscreen_surface_->getCanvas()->flush(); |
|||
onscreen_surface_->getContext()->submit(true); |
|||
} |
|||
|
|||
if (!delegate_->GLContextPresent()) { |
|||
return false; |
|||
} |
|||
|
|||
if (delegate_->GLContextFBOResetAfterPresent()) { |
|||
auto current_size = |
|||
SkISize::Make(onscreen_surface_->width(), onscreen_surface_->height()); |
|||
|
|||
// The FBO has changed, ask the delegate for the new FBO and do a surface
|
|||
// re-wrap.
|
|||
auto new_onscreen_surface = |
|||
WrapOnscreenSurface(context_.get(), // GL context
|
|||
current_size, // root surface size
|
|||
delegate_->GLContextFBO() // window FBO ID
|
|||
); |
|||
|
|||
if (!new_onscreen_surface) { |
|||
return false; |
|||
} |
|||
|
|||
onscreen_surface_ = std::move(new_onscreen_surface); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
sk_sp<SkSurface> GPUSurfaceGL::AcquireRenderSurface( |
|||
const SkISize& untransformed_size, |
|||
const SkMatrix& root_surface_transformation) { |
|||
const auto transformed_rect = root_surface_transformation.mapRect( |
|||
SkRect::MakeWH(untransformed_size.width(), untransformed_size.height())); |
|||
|
|||
const auto transformed_size = |
|||
SkISize::Make(transformed_rect.width(), transformed_rect.height()); |
|||
|
|||
if (!CreateOrUpdateSurfaces(transformed_size)) { |
|||
return nullptr; |
|||
} |
|||
|
|||
return onscreen_surface_; |
|||
} |
|||
|
|||
// |Surface|
|
|||
GrContext* GPUSurfaceGL::GetContext() { return context_.get(); } |
|||
|
|||
// |Surface|
|
|||
ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { |
|||
return delegate_->GetExternalViewEmbedder(); |
|||
} |
|||
|
|||
// |Surface|
|
|||
bool GPUSurfaceGL::MakeRenderContextCurrent() { |
|||
return delegate_->GLContextMakeCurrent(); |
|||
} |
|||
|
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include <functional> |
|||
#include <memory> |
|||
|
|||
#include "flow/embedded_views.h" |
|||
#include "flutter/fml/macros.h" |
|||
#include "flutter/fml/memory/weak_ptr.h" |
|||
#include "include/gpu/GrContext.h" |
|||
#include "shell/common/surface.h" |
|||
#include "shell/gpu/gpu_surface_gl_delegate.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class GPUSurfaceGL : public Surface { |
|||
public: |
|||
GPUSurfaceGL(GPUSurfaceGLDelegate* delegate, bool render_to_surface); |
|||
|
|||
// Creates a new GL surface reusing an existing GrContext. |
|||
GPUSurfaceGL(sk_sp<GrContext> gr_context, GPUSurfaceGLDelegate* delegate, |
|||
bool render_to_surface); |
|||
|
|||
// |Surface| |
|||
~GPUSurfaceGL() override; |
|||
|
|||
// |Surface| |
|||
bool IsValid() override; |
|||
|
|||
// |Surface| |
|||
std::unique_ptr<SurfaceFrame> AcquireFrame(const SkISize& size) override; |
|||
|
|||
// |Surface| |
|||
SkMatrix GetRootTransformation() const override; |
|||
|
|||
// |Surface| |
|||
GrContext* GetContext() override; |
|||
|
|||
// |Surface| |
|||
ExternalViewEmbedder* GetExternalViewEmbedder() override; |
|||
|
|||
// |Surface| |
|||
bool MakeRenderContextCurrent() override; |
|||
|
|||
private: |
|||
GPUSurfaceGLDelegate* delegate_; |
|||
sk_sp<GrContext> context_; |
|||
sk_sp<SkSurface> onscreen_surface_; |
|||
bool context_owner_; |
|||
// TODO(38466): Refactor GPU surface APIs take into account the fact that an |
|||
// external view embedder may want to render to the root surface. This is a |
|||
// hack to make avoid allocating resources for the root surface when an |
|||
// external view embedder is present. |
|||
const bool render_to_surface_; |
|||
bool valid_ = false; |
|||
fml::WeakPtrFactory<GPUSurfaceGL> weak_factory_; |
|||
|
|||
bool CreateOrUpdateSurfaces(const SkISize& size); |
|||
|
|||
sk_sp<SkSurface> AcquireRenderSurface( |
|||
const SkISize& untransformed_size, |
|||
const SkMatrix& root_surface_transformation); |
|||
|
|||
bool PresentSurface(SkCanvas* canvas); |
|||
|
|||
FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceGL); |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#pragma once
|
|||
|
|||
#include "gpu_surface_gl_delegate.h"
|
|||
|
|||
#include "include/gpu/gl/GrGLAssembleInterface.h"
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
GPUSurfaceGLDelegate::~GPUSurfaceGLDelegate() = default; |
|||
|
|||
bool GPUSurfaceGLDelegate::GLContextFBOResetAfterPresent() const { |
|||
return false; |
|||
} |
|||
|
|||
bool GPUSurfaceGLDelegate::SurfaceSupportsReadback() const { return true; } |
|||
|
|||
SkMatrix GPUSurfaceGLDelegate::GLContextSurfaceTransformation() const { |
|||
SkMatrix matrix; |
|||
matrix.setIdentity(); |
|||
return matrix; |
|||
} |
|||
|
|||
GPUSurfaceGLDelegate::GLProcResolver GPUSurfaceGLDelegate::GetGLProcResolver() |
|||
const { |
|||
return nullptr; |
|||
} |
|||
|
|||
static bool IsProcResolverOpenGLES( |
|||
GPUSurfaceGLDelegate::GLProcResolver proc_resolver) { |
|||
// Version string prefix that identifies an OpenGL ES implementation.
|
|||
#define GPU_GL_VERSION 0x1F02
|
|||
constexpr char kGLESVersionPrefix[] = "OpenGL ES"; |
|||
|
|||
using GLGetStringProc = const char* (*)(uint32_t); |
|||
|
|||
GLGetStringProc gl_get_string = |
|||
reinterpret_cast<GLGetStringProc>(proc_resolver("glGetString")); |
|||
|
|||
FML_CHECK(gl_get_string) |
|||
<< "The GL proc resolver could not resolve glGetString"; |
|||
|
|||
const char* gl_version_string = gl_get_string(GPU_GL_VERSION); |
|||
|
|||
FML_CHECK(gl_version_string) |
|||
<< "The GL proc resolver's glGetString(GL_VERSION) failed"; |
|||
|
|||
return strncmp(gl_version_string, kGLESVersionPrefix, |
|||
strlen(kGLESVersionPrefix)) == 0; |
|||
} |
|||
|
|||
static sk_sp<const GrGLInterface> CreateGLInterface( |
|||
GPUSurfaceGLDelegate::GLProcResolver proc_resolver) { |
|||
if (proc_resolver == nullptr) { |
|||
// If there is no custom proc resolver, ask Skia to guess the native
|
|||
// interface. This often leads to interesting results on most platforms.
|
|||
return GrGLMakeNativeInterface(); |
|||
} |
|||
|
|||
struct ProcResolverContext { |
|||
GPUSurfaceGLDelegate::GLProcResolver resolver; |
|||
}; |
|||
|
|||
ProcResolverContext context = {proc_resolver}; |
|||
|
|||
GrGLGetProc gl_get_proc = [](void* context, |
|||
const char gl_proc_name[]) -> GrGLFuncPtr { |
|||
auto proc_resolver_context = |
|||
reinterpret_cast<ProcResolverContext*>(context); |
|||
return reinterpret_cast<GrGLFuncPtr>( |
|||
proc_resolver_context->resolver(gl_proc_name)); |
|||
}; |
|||
|
|||
// glGetString indicates an OpenGL ES interface.
|
|||
if (IsProcResolverOpenGLES(proc_resolver)) { |
|||
return GrGLMakeAssembledGLESInterface(&context, gl_get_proc); |
|||
} |
|||
|
|||
// Fallback to OpenGL.
|
|||
if (auto interface = GrGLMakeAssembledGLInterface(&context, gl_get_proc)) { |
|||
return interface; |
|||
} |
|||
|
|||
FML_LOG(ERROR) << "Could not create a valid GL interface."; |
|||
return nullptr; |
|||
} |
|||
|
|||
sk_sp<const GrGLInterface> GPUSurfaceGLDelegate::GetGLInterface() const { |
|||
return CreateGLInterface(GetGLProcResolver()); |
|||
} |
|||
|
|||
sk_sp<const GrGLInterface> |
|||
GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface() { |
|||
return CreateGLInterface(nullptr); |
|||
} |
|||
|
|||
ExternalViewEmbedder* GPUSurfaceGLDelegate::GetExternalViewEmbedder() { |
|||
return nullptr; |
|||
} |
|||
|
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include "flow/embedded_views.h" |
|||
#include "flutter/fml/macros.h" |
|||
#include "include/core/SkMatrix.h" |
|||
#include "include/gpu/gl/GrGLInterface.h" |
|||
#include "shell/gpu/gpu_surface_delegate.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { |
|||
public: |
|||
~GPUSurfaceGLDelegate() override; |
|||
|
|||
// |GPUSurfaceDelegate| |
|||
ExternalViewEmbedder* GetExternalViewEmbedder() override; |
|||
|
|||
// Called to make the main GL context current on the current thread. |
|||
virtual bool GLContextMakeCurrent() = 0; |
|||
|
|||
// Called to clear the current GL context on the thread. This may be called on |
|||
// either the GPU or IO threads. |
|||
virtual bool GLContextClearCurrent() = 0; |
|||
|
|||
// Called to present the main GL surface. This is only called for the main GL |
|||
// context and not any of the contexts dedicated for IO. |
|||
virtual bool GLContextPresent() = 0; |
|||
|
|||
// The ID of the main window bound framebuffer. Typically FBO0. |
|||
virtual intptr_t GLContextFBO() const = 0; |
|||
|
|||
// The rendering subsystem assumes that the ID of the main window bound |
|||
// framebuffer remains constant throughout. If this assumption in incorrect, |
|||
// embedders are required to return true from this method. In such cases, |
|||
// GLContextFBO() will be called again to acquire the new FBO ID for rendering |
|||
// subsequent frames. |
|||
virtual bool GLContextFBOResetAfterPresent() const; |
|||
|
|||
// Indicates whether or not the surface supports pixel readback as used in |
|||
// circumstances such as a BackdropFilter. |
|||
virtual bool SurfaceSupportsReadback() const; |
|||
|
|||
// A transformation applied to the onscreen surface before the canvas is |
|||
// flushed. |
|||
virtual SkMatrix GLContextSurfaceTransformation() const; |
|||
|
|||
sk_sp<const GrGLInterface> GetGLInterface() const; |
|||
|
|||
// TODO(chinmaygarde): The presence of this method is to work around the fact |
|||
// that not all platforms can accept a custom GL proc table. Migrate all |
|||
// platforms to move GL proc resolution to the embedder and remove this |
|||
// method. |
|||
static sk_sp<const GrGLInterface> GetDefaultPlatformGLInterface(); |
|||
|
|||
using GLProcResolver = |
|||
std::function<void* /* proc name */ (const char* /* proc address */)>; |
|||
// Provide a custom GL proc resolver. If no such resolver is present, Skia |
|||
// will attempt to do GL proc address resolution on its own. Embedders that |
|||
// have specific opinions on GL API selection or need to add their own |
|||
// instrumentation to specific GL calls can specify custom GL functions |
|||
// here. |
|||
virtual GLProcResolver GetGLProcResolver() const; |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#include "gpu_surface_software.h"
|
|||
|
|||
#include <memory>
|
|||
|
|||
#include "flutter/fml/logging.h"
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
GPUSurfaceSoftware::GPUSurfaceSoftware(GPUSurfaceSoftwareDelegate* delegate, |
|||
bool render_to_surface) |
|||
: delegate_(delegate), |
|||
render_to_surface_(render_to_surface), |
|||
weak_factory_(this) {} |
|||
|
|||
GPUSurfaceSoftware::~GPUSurfaceSoftware() = default; |
|||
|
|||
// |Surface|
|
|||
bool GPUSurfaceSoftware::IsValid() { return delegate_ != nullptr; } |
|||
|
|||
// |Surface|
|
|||
std::unique_ptr<SurfaceFrame> GPUSurfaceSoftware::AcquireFrame( |
|||
const SkISize& logical_size) { |
|||
// TODO(38466): Refactor GPU surface APIs take into account the fact that an
|
|||
// external view embedder may want to render to the root surface.
|
|||
if (!render_to_surface_) { |
|||
return std::make_unique<SurfaceFrame>( |
|||
nullptr, true, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) { |
|||
return true; |
|||
}); |
|||
} |
|||
|
|||
if (!IsValid()) { |
|||
return nullptr; |
|||
} |
|||
|
|||
const auto size = SkISize::Make(logical_size.width(), logical_size.height()); |
|||
|
|||
sk_sp<SkSurface> backing_store = delegate_->AcquireBackingStore(size); |
|||
|
|||
if (backing_store == nullptr) { |
|||
return nullptr; |
|||
} |
|||
|
|||
if (size != SkISize::Make(backing_store->width(), backing_store->height())) { |
|||
return nullptr; |
|||
} |
|||
|
|||
// If the surface has been scaled, we need to apply the inverse scaling to the
|
|||
// underlying canvas so that coordinates are mapped to the same spot
|
|||
// irrespective of surface scaling.
|
|||
SkCanvas* canvas = backing_store->getCanvas(); |
|||
canvas->resetMatrix(); |
|||
|
|||
SurfaceFrame::SubmitCallback on_submit = |
|||
[self = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame, |
|||
SkCanvas* canvas) -> bool { |
|||
// If the surface itself went away, there is nothing more to do.
|
|||
if (!self || !self->IsValid() || canvas == nullptr) { |
|||
return false; |
|||
} |
|||
|
|||
canvas->flush(); |
|||
|
|||
return self->delegate_->PresentBackingStore(surface_frame.SkiaSurface()); |
|||
}; |
|||
|
|||
return std::make_unique<SurfaceFrame>(backing_store, true, on_submit); |
|||
} |
|||
|
|||
// |Surface|
|
|||
SkMatrix GPUSurfaceSoftware::GetRootTransformation() const { |
|||
// This backend does not currently support root surface transformations. Just
|
|||
// return identity.
|
|||
SkMatrix matrix; |
|||
matrix.reset(); |
|||
return matrix; |
|||
} |
|||
|
|||
// |Surface|
|
|||
GrContext* GPUSurfaceSoftware::GetContext() { |
|||
// There is no GrContext associated with a software surface.
|
|||
return nullptr; |
|||
} |
|||
|
|||
// |Surface|
|
|||
ExternalViewEmbedder* GPUSurfaceSoftware::GetExternalViewEmbedder() { |
|||
return delegate_->GetExternalViewEmbedder(); |
|||
} |
|||
|
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include "flutter/fml/macros.h" |
|||
#include "flutter/fml/memory/weak_ptr.h" |
|||
#include "shell/common/surface.h" |
|||
#include "shell/gpu/gpu_surface_software_delegate.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class GPUSurfaceSoftware : public Surface { |
|||
public: |
|||
GPUSurfaceSoftware(GPUSurfaceSoftwareDelegate* delegate, |
|||
bool render_to_surface); |
|||
|
|||
~GPUSurfaceSoftware() override; |
|||
|
|||
// |Surface| |
|||
bool IsValid() override; |
|||
|
|||
// |Surface| |
|||
std::unique_ptr<SurfaceFrame> AcquireFrame(const SkISize& size) override; |
|||
|
|||
// |Surface| |
|||
SkMatrix GetRootTransformation() const override; |
|||
|
|||
// |Surface| |
|||
GrContext* GetContext() override; |
|||
|
|||
// |Surface| |
|||
ExternalViewEmbedder* GetExternalViewEmbedder() override; |
|||
|
|||
private: |
|||
GPUSurfaceSoftwareDelegate* delegate_; |
|||
// TODO(38466): Refactor GPU surface APIs take into account the fact that an |
|||
// external view embedder may want to render to the root surface. This is a |
|||
// hack to make avoid allocating resources for the root surface when an |
|||
// external view embedder is present. |
|||
const bool render_to_surface_; |
|||
fml::WeakPtrFactory<GPUSurfaceSoftware> weak_factory_; |
|||
|
|||
FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceSoftware); |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#include "gpu_surface_software_delegate.h"
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
GPUSurfaceSoftwareDelegate::~GPUSurfaceSoftwareDelegate() = default; |
|||
|
|||
ExternalViewEmbedder* GPUSurfaceSoftwareDelegate::GetExternalViewEmbedder() { |
|||
return nullptr; |
|||
} |
|||
|
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include "flow/embedded_views.h" |
|||
#include "flutter/fml/macros.h" |
|||
#include "include/core/SkSurface.h" |
|||
#include "shell/gpu/gpu_surface_delegate.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
class GPUSurfaceSoftwareDelegate : public GPUSurfaceDelegate { |
|||
public: |
|||
~GPUSurfaceSoftwareDelegate() override; |
|||
|
|||
// |GPUSurfaceDelegate| |
|||
ExternalViewEmbedder* GetExternalViewEmbedder() override; |
|||
|
|||
virtual sk_sp<SkSurface> AcquireBackingStore(const SkISize& size) = 0; |
|||
|
|||
virtual bool PresentBackingStore(sk_sp<SkSurface> backing_store) = 0; |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
1001
engine/src/shell/platform/embedder/embedder.cc
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
#pragma once |
|||
|
|||
#include <stdbool.h> |
|||
#include <stddef.h> |
|||
#include <stdint.h> |
|||
|
|||
#if defined(__cplusplus) |
|||
extern "C" { |
|||
#endif |
|||
|
|||
typedef enum { |
|||
kSuccess = 0, |
|||
kInvalidLibraryVersion, |
|||
kInvalidArguments, |
|||
kInternalInconsistency, |
|||
} UIWidgetsEngineResult; |
|||
|
|||
typedef enum { |
|||
kOpenGL, |
|||
kSoftware, |
|||
} UIWidgetsRendererType; |
|||
|
|||
typedef enum { |
|||
kUIWidgetsAccessibilityFeatureAccessibleNavigation = 1 << 0, |
|||
kUIWidgetsAccessibilityFeatureInvertColors = 1 << 1, |
|||
kUIWidgetsAccessibilityFeatureDisableAnimations = 1 << 2, |
|||
kUIWidgetsAccessibilityFeatureBoldText = 1 << 3, |
|||
kUIWidgetsAccessibilityFeatureReduceMotion = 1 << 4, |
|||
} UIWidgetsAccessibilityFeature; |
|||
|
|||
typedef enum { |
|||
kUIWidgetsTextDirectionUnknown = 0, |
|||
kUIWidgetsTextDirectionRTL = 1, |
|||
kUIWidgetsTextDirectionLTR = 2, |
|||
} UIWidgetsTextDirection; |
|||
|
|||
typedef struct _UIWidgetsEngine* UIWidgetsEngine; |
|||
|
|||
typedef struct { |
|||
float scaleX; |
|||
float skewX; |
|||
float transX; |
|||
float skewY; |
|||
float scaleY; |
|||
float transY; |
|||
float pers0; |
|||
float pers1; |
|||
float pers2; |
|||
} UIWidgetsTransformation; |
|||
|
|||
typedef void (*VoidCallback)(void* /* user data */); |
|||
|
|||
typedef enum { |
|||
kUIWidgetsOpenGLTargetTypeTexture, |
|||
kUIWidgetsOpenGLTargetTypeFramebuffer, |
|||
} UIWidgetsOpenGLTargetType; |
|||
|
|||
typedef struct { |
|||
uint32_t target; |
|||
uint32_t name; |
|||
uint32_t format; |
|||
void* user_data; |
|||
VoidCallback destruction_callback; |
|||
size_t width; |
|||
size_t height; |
|||
} UIWidgetsOpenGLTexture; |
|||
|
|||
typedef struct { |
|||
uint32_t target; |
|||
uint32_t name; |
|||
void* user_data; |
|||
VoidCallback destruction_callback; |
|||
} UIWidgetsOpenGLFramebuffer; |
|||
|
|||
typedef bool (*BoolCallback)(void* /* user data */); |
|||
typedef UIWidgetsTransformation (*TransformationCallback)( |
|||
void* /* user data */); |
|||
typedef uint32_t (*UIntCallback)(void* /* user data */); |
|||
typedef bool (*SoftwareSurfacePresentCallback)(void* /* user data */, |
|||
const void* /* allocation */, |
|||
size_t /* row bytes */, |
|||
size_t /* height */); |
|||
typedef void* (*ProcResolver)(void* /* user data */, const char* /* name */); |
|||
typedef bool (*TextureFrameCallback)(void* /* user data */, |
|||
int64_t /* texture identifier */, |
|||
size_t /* width */, size_t /* height */, |
|||
UIWidgetsOpenGLTexture* /* texture out */); |
|||
typedef void (*VsyncCallback)(void* /* user data */, intptr_t /* baton */); |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
BoolCallback make_current; |
|||
BoolCallback clear_current; |
|||
BoolCallback present; |
|||
UIntCallback fbo_callback; |
|||
BoolCallback make_resource_current; |
|||
bool fbo_reset_after_present; |
|||
TransformationCallback surface_transformation; |
|||
ProcResolver gl_proc_resolver; |
|||
TextureFrameCallback gl_external_texture_frame_callback; |
|||
} UIWidgetsOpenGLRendererConfig; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
SoftwareSurfacePresentCallback surface_present_callback; |
|||
} UIWidgetsSoftwareRendererConfig; |
|||
|
|||
typedef struct { |
|||
UIWidgetsRendererType type; |
|||
union { |
|||
UIWidgetsOpenGLRendererConfig open_gl; |
|||
UIWidgetsSoftwareRendererConfig software; |
|||
}; |
|||
} UIWidgetsRendererConfig; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
size_t width; |
|||
size_t height; |
|||
float pixel_ratio; |
|||
} UIWidgetsWindowMetricsEvent; |
|||
|
|||
typedef enum { |
|||
kCancel, |
|||
kUp, |
|||
kDown, |
|||
kMove, |
|||
kAdd, |
|||
kRemove, |
|||
kHover, |
|||
} UIWidgetsPointerPhase; |
|||
|
|||
typedef enum { |
|||
kUIWidgetsPointerDeviceKindMouse = 1, |
|||
kUIWidgetsPointerDeviceKindTouch, |
|||
} UIWidgetsPointerDeviceKind; |
|||
|
|||
typedef enum { |
|||
kUIWidgetsPointerButtonMousePrimary = 1 << 0, |
|||
kUIWidgetsPointerButtonMouseSecondary = 1 << 1, |
|||
kUIWidgetsPointerButtonMouseMiddle = 1 << 2, |
|||
kUIWidgetsPointerButtonMouseBack = 1 << 3, |
|||
kUIWidgetsPointerButtonMouseForward = 1 << 4, |
|||
} UIWidgetsPointerMouseButtons; |
|||
|
|||
typedef enum { |
|||
kUIWidgetsPointerSignalKindNone, |
|||
kUIWidgetsPointerSignalKindScroll, |
|||
} UIWidgetsPointerSignalKind; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
UIWidgetsPointerPhase phase; |
|||
size_t timestamp; |
|||
float x; |
|||
float y; |
|||
int32_t device; |
|||
UIWidgetsPointerSignalKind signal_kind; |
|||
float scroll_delta_x; |
|||
float scroll_delta_y; |
|||
UIWidgetsPointerDeviceKind device_kind; |
|||
int64_t buttons; |
|||
} UIWidgetsPointerEvent; |
|||
|
|||
struct _UIWidgetsPlatformMessageResponseHandle; |
|||
typedef struct _UIWidgetsPlatformMessageResponseHandle |
|||
UIWidgetsPlatformMessageResponseHandle; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
const char* channel; |
|||
const uint8_t* message; |
|||
size_t message_size; |
|||
const UIWidgetsPlatformMessageResponseHandle* response_handle; |
|||
} UIWidgetsPlatformMessage; |
|||
|
|||
typedef void (*UIWidgetsPlatformMessageCallback)( |
|||
const UIWidgetsPlatformMessage* /* message*/, void* /* user data */); |
|||
|
|||
typedef void (*UIWidgetsDataCallback)(const uint8_t* /* data */, |
|||
size_t /* size */, void* /* user data */); |
|||
|
|||
typedef struct { |
|||
float left; |
|||
float top; |
|||
float right; |
|||
float bottom; |
|||
} UIWidgetsRect; |
|||
|
|||
typedef struct { |
|||
float x; |
|||
float y; |
|||
} UIWidgetsPoint; |
|||
|
|||
typedef struct { |
|||
float width; |
|||
float height; |
|||
} UIWidgetsSize; |
|||
|
|||
typedef struct { |
|||
UIWidgetsRect rect; |
|||
UIWidgetsSize upper_left_corner_radius; |
|||
UIWidgetsSize upper_right_corner_radius; |
|||
UIWidgetsSize lower_right_corner_radius; |
|||
UIWidgetsSize lower_left_corner_radius; |
|||
} UIWidgetsRoundedRect; |
|||
|
|||
typedef int64_t UIWidgetsPlatformViewIdentifier; |
|||
|
|||
typedef struct _UIWidgetsTaskRunner* UIWidgetsTaskRunner; |
|||
|
|||
typedef struct { |
|||
UIWidgetsTaskRunner runner; |
|||
uint64_t task; |
|||
} UIWidgetsTask; |
|||
|
|||
typedef void (*UIWidgetsTaskRunnerPostTaskCallback)( |
|||
UIWidgetsTask /* task */, uint64_t /* target time nanos */, |
|||
void* /* user data */); |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
void* user_data; |
|||
BoolCallback runs_task_on_current_thread_callback; |
|||
UIWidgetsTaskRunnerPostTaskCallback post_task_callback; |
|||
size_t identifier; |
|||
} UIWidgetsTaskRunnerDescription; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
const UIWidgetsTaskRunnerDescription* platform_task_runner; |
|||
const UIWidgetsTaskRunnerDescription* ui_task_runner; |
|||
const UIWidgetsTaskRunnerDescription* render_task_runner; |
|||
} UIWidgetsCustomTaskRunners; |
|||
|
|||
typedef struct { |
|||
UIWidgetsOpenGLTargetType type; |
|||
union { |
|||
UIWidgetsOpenGLTexture texture; |
|||
UIWidgetsOpenGLFramebuffer framebuffer; |
|||
}; |
|||
} UIWidgetsOpenGLBackingStore; |
|||
|
|||
typedef struct { |
|||
const void* allocation; |
|||
size_t row_bytes; |
|||
size_t height; |
|||
void* user_data; |
|||
VoidCallback destruction_callback; |
|||
} UIWidgetsSoftwareBackingStore; |
|||
|
|||
typedef enum { |
|||
kUIWidgetsPlatformViewMutationTypeOpacity, |
|||
kUIWidgetsPlatformViewMutationTypeClipRect, |
|||
kUIWidgetsPlatformViewMutationTypeClipRoundedRect, |
|||
kUIWidgetsPlatformViewMutationTypeTransformation, |
|||
} UIWidgetsPlatformViewMutationType; |
|||
|
|||
typedef struct { |
|||
UIWidgetsPlatformViewMutationType type; |
|||
union { |
|||
double opacity; |
|||
UIWidgetsRect clip_rect; |
|||
UIWidgetsRoundedRect clip_rounded_rect; |
|||
UIWidgetsTransformation transformation; |
|||
}; |
|||
} UIWidgetsPlatformViewMutation; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
UIWidgetsPlatformViewIdentifier identifier; |
|||
size_t mutations_count; |
|||
const UIWidgetsPlatformViewMutation** mutations; |
|||
} UIWidgetsPlatformView; |
|||
|
|||
typedef enum { |
|||
kUIWidgetsBackingStoreTypeOpenGL, |
|||
kUIWidgetsBackingStoreTypeSoftware, |
|||
} UIWidgetsBackingStoreType; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
void* user_data; |
|||
UIWidgetsBackingStoreType type; |
|||
bool did_update; |
|||
union { |
|||
UIWidgetsOpenGLBackingStore open_gl; |
|||
UIWidgetsSoftwareBackingStore software; |
|||
}; |
|||
} UIWidgetsBackingStore; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
UIWidgetsSize size; |
|||
} UIWidgetsBackingStoreConfig; |
|||
|
|||
typedef enum { |
|||
kUIWidgetsLayerContentTypeBackingStore, |
|||
kUIWidgetsLayerContentTypePlatformView, |
|||
} UIWidgetsLayerContentType; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
UIWidgetsLayerContentType type; |
|||
union { |
|||
const UIWidgetsBackingStore* backing_store; |
|||
const UIWidgetsPlatformView* platform_view; |
|||
}; |
|||
UIWidgetsPoint offset; |
|||
UIWidgetsSize size; |
|||
} UIWidgetsLayer; |
|||
|
|||
typedef bool (*UIWidgetsBackingStoreCreateCallback)( |
|||
const UIWidgetsBackingStoreConfig* config, |
|||
UIWidgetsBackingStore* backing_store_out, void* user_data); |
|||
|
|||
typedef bool (*UIWidgetsBackingStoreCollectCallback)( |
|||
const UIWidgetsBackingStore* renderer, void* user_data); |
|||
|
|||
typedef bool (*UIWidgetsLayersPresentCallback)(const UIWidgetsLayer** layers, |
|||
size_t layers_count, |
|||
void* user_data); |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
void* user_data; |
|||
UIWidgetsBackingStoreCreateCallback create_backing_store_callback; |
|||
UIWidgetsBackingStoreCollectCallback collect_backing_store_callback; |
|||
UIWidgetsLayersPresentCallback present_layers_callback; |
|||
} UIWidgetsCompositor; |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
const char* language_code; |
|||
const char* country_code; |
|||
const char* script_code; |
|||
const char* variant_code; |
|||
} UIWidgetsLocale; |
|||
|
|||
typedef enum { |
|||
kUIWidgetsNativeThreadTypePlatform, |
|||
kUIWidgetsNativeThreadTypeRender, |
|||
kUIWidgetsNativeThreadTypeUI, |
|||
kUIWidgetsNativeThreadTypeWorker, |
|||
} UIWidgetsNativeThreadType; |
|||
|
|||
typedef void (*UIWidgetsNativeThreadCallback)(UIWidgetsNativeThreadType type, |
|||
void* user_data); |
|||
|
|||
typedef void (*UIWidgetsTaskObserverAdd)(intptr_t key, void* callback, |
|||
void* user_data); |
|||
typedef void (*UIWidgetsTaskObserverRemove)(intptr_t key, void* user_data); |
|||
|
|||
typedef void (*UIWidgetsMonoEntrypointCallback)(void* user_data); |
|||
|
|||
typedef struct { |
|||
size_t struct_size; |
|||
const char* assets_path; |
|||
const char* icu_data_path; |
|||
int command_line_argc; |
|||
const char* const* command_line_argv; |
|||
UIWidgetsPlatformMessageCallback platform_message_callback; |
|||
VoidCallback root_isolate_create_callback; |
|||
const char* persistent_cache_path; |
|||
bool is_persistent_cache_read_only; |
|||
VsyncCallback vsync_callback; |
|||
const UIWidgetsCustomTaskRunners* custom_task_runners; |
|||
const UIWidgetsCompositor* compositor; |
|||
UIWidgetsTaskObserverAdd task_observer_add; |
|||
UIWidgetsTaskObserverRemove task_observer_remove; |
|||
UIWidgetsMonoEntrypointCallback custom_mono_entrypoint; |
|||
UIWidgetsWindowMetricsEvent initial_window_metrics; |
|||
} UIWidgetsProjectArgs; |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineRun(const UIWidgetsRendererConfig* config, |
|||
const UIWidgetsProjectArgs* args, |
|||
void* user_data, |
|||
UIWidgetsEngine* engine_out); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineShutdown(UIWidgetsEngine engine); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineInitialize( |
|||
const UIWidgetsRendererConfig* config, const UIWidgetsProjectArgs* args, |
|||
void* user_data, UIWidgetsEngine* engine_out); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineDeinitialize(UIWidgetsEngine engine); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineRunInitialized(UIWidgetsEngine engine); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineSendWindowMetricsEvent( |
|||
UIWidgetsEngine engine, const UIWidgetsWindowMetricsEvent* event); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineSendPointerEvent( |
|||
UIWidgetsEngine engine, const UIWidgetsPointerEvent* events, |
|||
size_t events_count); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineSendPlatformMessage( |
|||
UIWidgetsEngine engine, const UIWidgetsPlatformMessage* message); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsPlatformMessageCreateResponseHandle( |
|||
UIWidgetsEngine engine, UIWidgetsDataCallback data_callback, |
|||
void* user_data, UIWidgetsPlatformMessageResponseHandle** response_out); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsPlatformMessageReleaseResponseHandle( |
|||
UIWidgetsEngine engine, UIWidgetsPlatformMessageResponseHandle* response); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineSendPlatformMessageResponse( |
|||
UIWidgetsEngine engine, |
|||
const UIWidgetsPlatformMessageResponseHandle* handle, const uint8_t* data, |
|||
size_t data_length); |
|||
|
|||
UIWidgetsEngineResult __UIWidgetsEngineFlushPendingTasksNow(); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineRegisterExternalTexture( |
|||
UIWidgetsEngine engine, int64_t texture_identifier); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineUnregisterExternalTexture( |
|||
UIWidgetsEngine engine, int64_t texture_identifier); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineMarkExternalTextureFrameAvailable( |
|||
UIWidgetsEngine engine, int64_t texture_identifier); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineUpdateAccessibilityFeatures( |
|||
UIWidgetsEngine engine, UIWidgetsAccessibilityFeature features); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineOnVsync(UIWidgetsEngine engine, |
|||
intptr_t baton, |
|||
uint64_t frame_start_time_nanos, |
|||
uint64_t frame_target_time_nanos); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineReloadSystemFonts(UIWidgetsEngine engine); |
|||
|
|||
void UIWidgetsEngineTraceEventDurationBegin(const char* name); |
|||
|
|||
void UIWidgetsEngineTraceEventDurationEnd(const char* name); |
|||
|
|||
void UIWidgetsEngineTraceEventInstant(const char* name); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEnginePostRenderThreadTask( |
|||
UIWidgetsEngine engine, VoidCallback callback, void* callback_data); |
|||
|
|||
uint64_t UIWidgetsEngineGetCurrentTime(); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineRunTask(UIWidgetsEngine engine, |
|||
const UIWidgetsTask* task); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEngineUpdateLocales( |
|||
UIWidgetsEngine engine, const UIWidgetsLocale** locales, |
|||
size_t locales_count); |
|||
UIWidgetsEngineResult UIWidgetsEngineNotifyLowMemoryWarning( |
|||
UIWidgetsEngine engine); |
|||
|
|||
UIWidgetsEngineResult UIWidgetsEnginePostCallbackOnAllNativeThreads( |
|||
UIWidgetsEngine engine, UIWidgetsNativeThreadCallback callback, |
|||
void* user_data); |
|||
|
|||
#if defined(__cplusplus) |
|||
} // extern "C" |
|||
#endif |
|
|||
#include "embedder_engine.h"
|
|||
|
|||
#include <utility>
|
|||
|
|||
#include "flutter/fml/make_copyable.h"
|
|||
#include "shell/platform/embedder/vsync_waiter_embedder.h"
|
|||
|
|||
namespace uiwidgets { |
|||
|
|||
struct ShellArgs { |
|||
WindowData window_data; |
|||
Settings settings; |
|||
Shell::CreateCallback<PlatformView> on_create_platform_view; |
|||
Shell::CreateCallback<Rasterizer> on_create_rasterizer; |
|||
ShellArgs(const WindowData& p_window_data, const Settings& p_settings, |
|||
Shell::CreateCallback<PlatformView> p_on_create_platform_view, |
|||
Shell::CreateCallback<Rasterizer> p_on_create_rasterizer) |
|||
: window_data(p_window_data), |
|||
settings(p_settings), |
|||
on_create_platform_view(std::move(p_on_create_platform_view)), |
|||
on_create_rasterizer(std::move(p_on_create_rasterizer)) {} |
|||
}; |
|||
|
|||
EmbedderEngine::EmbedderEngine( |
|||
std::unique_ptr<EmbedderThreadHost> thread_host, |
|||
const TaskRunners& task_runners, const WindowData& window_data, |
|||
const Settings& settings, RunConfiguration run_configuration, |
|||
Shell::CreateCallback<PlatformView> on_create_platform_view, |
|||
Shell::CreateCallback<Rasterizer> on_create_rasterizer, |
|||
EmbedderExternalTextureGL::ExternalTextureCallback |
|||
external_texture_callback) |
|||
: thread_host_(std::move(thread_host)), |
|||
task_runners_(task_runners), |
|||
run_configuration_(std::move(run_configuration)), |
|||
shell_args_(std::make_unique<ShellArgs>( |
|||
window_data, settings, std::move(on_create_platform_view), |
|||
std::move(on_create_rasterizer))), |
|||
external_texture_callback_(std::move(external_texture_callback)) {} |
|||
|
|||
EmbedderEngine::~EmbedderEngine() = default; |
|||
|
|||
bool EmbedderEngine::LaunchShell() { |
|||
if (!shell_args_) { |
|||
FML_DLOG(ERROR) << "Invalid shell arguments."; |
|||
return false; |
|||
} |
|||
|
|||
if (shell_) { |
|||
FML_DLOG(ERROR) << "Shell already initialized"; |
|||
} |
|||
|
|||
shell_ = Shell::Create( |
|||
task_runners_, shell_args_->window_data, shell_args_->settings, |
|||
shell_args_->on_create_platform_view, shell_args_->on_create_rasterizer); |
|||
|
|||
// Reset the args no matter what. They will never be used to initialize a
|
|||
// shell again.
|
|||
shell_args_.reset(); |
|||
|
|||
return IsValid(); |
|||
} |
|||
|
|||
bool EmbedderEngine::CollectShell() { |
|||
shell_.reset(); |
|||
return IsValid(); |
|||
} |
|||
|
|||
bool EmbedderEngine::RunRootIsolate() { |
|||
if (!IsValid() || !run_configuration_.IsValid()) { |
|||
return false; |
|||
} |
|||
shell_->RunEngine(std::move(run_configuration_)); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::IsValid() const { return static_cast<bool>(shell_); } |
|||
|
|||
const TaskRunners& EmbedderEngine::GetTaskRunners() const { |
|||
return task_runners_; |
|||
} |
|||
|
|||
bool EmbedderEngine::NotifyCreated() { |
|||
if (!IsValid()) { |
|||
return false; |
|||
} |
|||
|
|||
shell_->GetPlatformView()->NotifyCreated(); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::NotifyDestroyed() { |
|||
if (!IsValid()) { |
|||
return false; |
|||
} |
|||
|
|||
shell_->GetPlatformView()->NotifyDestroyed(); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::SetViewportMetrics(ViewportMetrics metrics) { |
|||
if (!IsValid()) { |
|||
return false; |
|||
} |
|||
|
|||
auto platform_view = shell_->GetPlatformView(); |
|||
if (!platform_view) { |
|||
return false; |
|||
} |
|||
platform_view->SetViewportMetrics(std::move(metrics)); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::DispatchPointerDataPacket( |
|||
std::unique_ptr<PointerDataPacket> packet) { |
|||
if (!IsValid() || !packet) { |
|||
return false; |
|||
} |
|||
|
|||
auto platform_view = shell_->GetPlatformView(); |
|||
if (!platform_view) { |
|||
return false; |
|||
} |
|||
|
|||
platform_view->DispatchPointerDataPacket(std::move(packet)); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::SendPlatformMessage(fml::RefPtr<PlatformMessage> message) { |
|||
if (!IsValid() || !message) { |
|||
return false; |
|||
} |
|||
|
|||
auto platform_view = shell_->GetPlatformView(); |
|||
if (!platform_view) { |
|||
return false; |
|||
} |
|||
|
|||
platform_view->DispatchPlatformMessage(message); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::RegisterTexture(int64_t texture) { |
|||
if (!IsValid() || !external_texture_callback_) { |
|||
return false; |
|||
} |
|||
shell_->GetPlatformView()->RegisterTexture( |
|||
std::make_unique<EmbedderExternalTextureGL>(texture, |
|||
external_texture_callback_)); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::UnregisterTexture(int64_t texture) { |
|||
if (!IsValid() || !external_texture_callback_) { |
|||
return false; |
|||
} |
|||
shell_->GetPlatformView()->UnregisterTexture(texture); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::MarkTextureFrameAvailable(int64_t texture) { |
|||
if (!IsValid() || !external_texture_callback_) { |
|||
return false; |
|||
} |
|||
shell_->GetPlatformView()->MarkTextureFrameAvailable(texture); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::SetAccessibilityFeatures(int32_t flags) { |
|||
if (!IsValid()) { |
|||
return false; |
|||
} |
|||
auto platform_view = shell_->GetPlatformView(); |
|||
if (!platform_view) { |
|||
return false; |
|||
} |
|||
platform_view->SetAccessibilityFeatures(flags); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::OnVsyncEvent(intptr_t baton, |
|||
fml::TimePoint frame_start_time, |
|||
fml::TimePoint frame_target_time) { |
|||
if (!IsValid()) { |
|||
return false; |
|||
} |
|||
|
|||
return VsyncWaiterEmbedder::OnEmbedderVsync(baton, frame_start_time, |
|||
frame_target_time); |
|||
} |
|||
|
|||
bool EmbedderEngine::ReloadSystemFonts() { |
|||
if (!IsValid()) { |
|||
return false; |
|||
} |
|||
|
|||
return shell_->ReloadSystemFonts(); |
|||
} |
|||
|
|||
bool EmbedderEngine::PostRenderThreadTask(const fml::closure& task) { |
|||
if (!IsValid()) { |
|||
return false; |
|||
} |
|||
|
|||
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(task); |
|||
return true; |
|||
} |
|||
|
|||
bool EmbedderEngine::RunTask(const UIWidgetsTask* task) { |
|||
// The shell doesn't need to be running or valid for access to the thread
|
|||
// host. This is why there is no `IsValid` check here. This allows embedders
|
|||
// to perform custom task runner interop before the shell is running.
|
|||
if (task == nullptr) { |
|||
return false; |
|||
} |
|||
return thread_host_->PostTask(reinterpret_cast<int64_t>(task->runner), |
|||
task->task); |
|||
} |
|||
|
|||
bool EmbedderEngine::PostTaskOnEngineManagedNativeThreads( |
|||
std::function<void(UIWidgetsNativeThreadType)> closure) const { |
|||
if (!IsValid() || closure == nullptr) { |
|||
return false; |
|||
} |
|||
|
|||
const auto trampoline = [closure](UIWidgetsNativeThreadType type, |
|||
fml::RefPtr<fml::TaskRunner> runner) { |
|||
runner->PostTask([closure, type] { closure(type); }); |
|||
}; |
|||
|
|||
// Post the task to all thread host threads.
|
|||
const auto& task_runners = shell_->GetTaskRunners(); |
|||
trampoline(kUIWidgetsNativeThreadTypeRender, |
|||
task_runners.GetRasterTaskRunner()); |
|||
trampoline(kUIWidgetsNativeThreadTypeWorker, task_runners.GetIOTaskRunner()); |
|||
trampoline(kUIWidgetsNativeThreadTypeUI, task_runners.GetUITaskRunner()); |
|||
trampoline(kUIWidgetsNativeThreadTypePlatform, |
|||
task_runners.GetPlatformTaskRunner()); |
|||
|
|||
// Post the task to all worker threads.
|
|||
auto engine = shell_->GetEngine(); |
|||
engine->GetConcurrentMessageLoop()->PostTaskToAllWorkers( |
|||
[closure]() { closure(kUIWidgetsNativeThreadTypeWorker); }); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
Shell& EmbedderEngine::GetShell() { |
|||
FML_DCHECK(shell_); |
|||
return *shell_.get(); |
|||
} |
|||
|
|||
} // namespace uiwidgets
|
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <unordered_map> |
|||
|
|||
#include "flutter/fml/macros.h" |
|||
#include "shell/common/shell.h" |
|||
#include "shell/common/thread_host.h" |
|||
#include "shell/platform/embedder/embedder.h" |
|||
#include "shell/platform/embedder/embedder_engine.h" |
|||
#include "shell/platform/embedder/embedder_external_texture_gl.h" |
|||
#include "shell/platform/embedder/embedder_thread_host.h" |
|||
|
|||
namespace uiwidgets { |
|||
|
|||
struct ShellArgs; |
|||
|
|||
class EmbedderEngine { |
|||
public: |
|||
EmbedderEngine(std::unique_ptr<EmbedderThreadHost> thread_host, |
|||
const TaskRunners& task_runners, const WindowData& window_data, const Settings& settings, |
|||
RunConfiguration run_configuration, |
|||
Shell::CreateCallback<PlatformView> on_create_platform_view, |
|||
Shell::CreateCallback<Rasterizer> on_create_rasterizer, |
|||
EmbedderExternalTextureGL::ExternalTextureCallback |
|||
external_texture_callback); |
|||
|
|||
~EmbedderEngine(); |
|||
|
|||
bool LaunchShell(); |
|||
|
|||
bool CollectShell(); |
|||
|
|||
const TaskRunners& GetTaskRunners() const; |
|||
|
|||
bool NotifyCreated(); |
|||
|
|||
bool NotifyDestroyed(); |
|||
|
|||
bool RunRootIsolate(); |
|||
|
|||
bool IsValid() const; |
|||
|
|||
bool SetViewportMetrics(ViewportMetrics metrics); |
|||
|
|||
bool DispatchPointerDataPacket(std::unique_ptr<PointerDataPacket> packet); |
|||
|
|||
bool SendPlatformMessage(fml::RefPtr<PlatformMessage> message); |
|||
|
|||
bool RegisterTexture(int64_t texture); |
|||
|
|||
bool UnregisterTexture(int64_t texture); |
|||
|
|||
bool MarkTextureFrameAvailable(int64_t texture); |
|||
|
|||
bool SetAccessibilityFeatures(int32_t flags); |
|||
|
|||
bool OnVsyncEvent(intptr_t baton, fml::TimePoint frame_start_time, |
|||
fml::TimePoint frame_target_time); |
|||
|
|||
bool ReloadSystemFonts(); |
|||
|
|||
bool PostRenderThreadTask(const fml::closure& task); |
|||
|
|||
bool RunTask(const UIWidgetsTask* task); |
|||
|
|||
bool PostTaskOnEngineManagedNativeThreads( |
|||
std::function<void(UIWidgetsNativeThreadType)> closure) const; |
|||
|
|||
Shell& GetShell(); |
|||
|
|||
private: |
|||
const std::unique_ptr<EmbedderThreadHost> thread_host_; |
|||
TaskRunners task_runners_; |
|||
RunConfiguration run_configuration_; |
|||
std::unique_ptr<ShellArgs> shell_args_; |
|||
std::unique_ptr<Shell> shell_; |
|||
const EmbedderExternalTextureGL::ExternalTextureCallback |
|||
external_texture_callback_; |
|||
|
|||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderEngine); |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
部分文件因为文件数量过多而无法显示
撰写
预览
正在加载...
取消
保存
Reference in new issue