浏览代码

Merge branch 'skia' into siyaoH/skia

/siyaoH-1.17-PlatformMessage
siyao 4 年前
当前提交
8827b37e
共有 49 个文件被更改,包括 2635 次插入378 次删除
  1. 47
      com.unity.uiwidgets/Runtime/async2/future.cs
  2. 82
      com.unity.uiwidgets/Runtime/editor/editor_window.cs
  3. 172
      com.unity.uiwidgets/Runtime/engine2/UIWidgetsPanel.cs
  4. 88
      com.unity.uiwidgets/Runtime/gestures/converter.cs
  5. 8
      com.unity.uiwidgets/Runtime/services/asset_bundle.cs
  6. 72
      com.unity.uiwidgets/Runtime/ui/pointer.cs
  7. 60
      com.unity.uiwidgets/Runtime/ui2/compositing.cs
  8. 120
      com.unity.uiwidgets/Runtime/ui2/hooks.cs
  9. 50
      com.unity.uiwidgets/Runtime/ui2/isolate.cs
  10. 51
      com.unity.uiwidgets/Runtime/ui2/native_bindings.cs
  11. 313
      com.unity.uiwidgets/Runtime/ui2/painting.cs
  12. 17
      com.unity.uiwidgets/Runtime/ui2/window.cs
  13. 14
      engine/Build.bee.cs
  14. 14
      engine/src/flow/skia_gpu_object.cc
  15. 2
      engine/src/flow/skia_gpu_object.h
  16. 18
      engine/src/lib/ui/compositing/scene_builder.cc
  17. 6
      engine/src/lib/ui/painting/engine_layer.cc
  18. 15
      engine/src/lib/ui/painting/image.cc
  19. 2
      engine/src/lib/ui/painting/image.h
  20. 377
      engine/src/lib/ui/painting/image_decoder.cc
  21. 55
      engine/src/lib/ui/painting/image_decoder.h
  22. 42
      engine/src/lib/ui/painting/image_encoding.cc
  23. 14
      engine/src/lib/ui/painting/image_encoding.h
  24. 3
      engine/src/lib/ui/painting/picture.cc
  25. 14
      engine/src/lib/ui/window/platform_message_response_mono.cc
  26. 66
      engine/src/lib/ui/window/pointer_data.h
  27. 17
      engine/src/lib/ui/window/window.cc
  28. 41
      engine/src/render_api_d3d11.cc
  29. 60
      engine/src/runtime/mono_api.cc
  30. 1
      engine/src/runtime/mono_api.h
  31. 2
      engine/src/runtime/mono_isolate.cc
  32. 36
      engine/src/shell/common/shell.cc
  33. 16
      engine/src/shell/platform/embedder/embedder_task_runner.cc
  34. 3
      engine/src/shell/platform/embedder/embedder_task_runner.h
  35. 185
      engine/src/shell/platform/unity/uiwidgets_panel.cc
  36. 45
      engine/src/shell/platform/unity/uiwidgets_panel.h
  37. 7
      engine/src/shell/platform/unity/uiwidgets_system.h
  38. 6
      engine/src/shell/platform/unity/unity_surface_manager.h
  39. 3
      engine/third_party/Unity/IUnityUIWidgets.h
  40. 198
      engine/src/lib/ui/painting/codec.cc
  41. 46
      engine/src/lib/ui/painting/codec.h
  42. 22
      engine/src/lib/ui/painting/frame_info.cc
  43. 25
      engine/src/lib/ui/painting/frame_info.h
  44. 194
      engine/src/lib/ui/painting/multi_frame_codec.cc
  45. 69
      engine/src/lib/ui/painting/multi_frame_codec.h
  46. 110
      engine/src/lib/ui/painting/single_frame_codec.cc
  47. 40
      engine/src/lib/ui/painting/single_frame_codec.h
  48. 119
      engine/src/shell/platform/unity/unity_external_texture_gl.cc
  49. 46
      engine/src/shell/platform/unity/unity_external_texture_gl.h

47
com.unity.uiwidgets/Runtime/async2/future.cs


}
public static readonly FutureOr nil = value(null);
public static implicit operator FutureOr(string v) => value(v);
public static implicit operator FutureOr(byte[] v) => value(v);

public abstract Future then(Func<object, FutureOr> onValue, Func<Exception, FutureOr> onError = null);
public Future then(Action<object> onValue, Func<Exception, FutureOr> onError = null) {
return then(v => {
onValue(v);
return FutureOr.nil;
}, onError);
}
public Future catchError(Action<Exception> onError, Func<Exception, bool> test = null) {
return catchError(e => {
onError(e);
return FutureOr.nil;
}, test);
}
public Future whenComplete(Action action) {
return whenComplete(() => {
action();
return FutureOr.nil;
});
}
// public abstract Stream asStream();
public abstract Future timeout(TimeSpan timeLimit, Func<FutureOr> onTimeout = null);

return _future.then(onValue, onError);
}
public Future<R> then<R>(Func<T, FutureOr> onValue, Func<Exception, FutureOr> onError = null) {
public Future then_(Func<T, FutureOr> onValue, Func<Exception, FutureOr> onError = null) {
return _future.then(obj => onValue((T) obj), onError);
}
public Future<R> then_<R>(Func<T, FutureOr> onValue, Func<Exception, FutureOr> onError = null) {
}
public Future then_(Action<T> onValue, Func<Exception, FutureOr> onError = null) {
return _future.then(obj => onValue((T) obj), onError);
}
public override Future catchError(Func<Exception, FutureOr> onError, Func<Exception, bool> test = null) {

public static Completer sync() => new _SyncCompleter();
public abstract Future future { get; }
public abstract void complete(FutureOr value = default);
public abstract void completeError(Exception error);

82
com.unity.uiwidgets/Runtime/editor/editor_window.cs


}
if (onPointerEvent != null) {
PointerData pointerData = null;
PointerData pointerData = new PointerData();
if (evt.type == EventType.MouseDown) {
pointerData = new PointerData(

InputUtils.getScrollButtonKey()
);
}
else if (evt.type == EventType.DragUpdated) {
pointerData = new PointerData(
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.dragFromEditorMove,
kind: PointerDeviceKind.mouse,
device: InputUtils.getMouseButtonKey(evt.button),
physicalX: evt.mousePosition.x * _devicePixelRatio,
physicalY: evt.mousePosition.y * _devicePixelRatio
);
}
else if (evt.type == EventType.DragPerform) {
pointerData = new PointerData(
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.dragFromEditorRelease,
kind: PointerDeviceKind.mouse,
device: InputUtils.getMouseButtonKey(evt.button),
physicalX: evt.mousePosition.x * _devicePixelRatio,
physicalY: evt.mousePosition.y * _devicePixelRatio
);
}
if (pointerData != null) {
onPointerEvent(new PointerDataPacket(new List<PointerData> {
pointerData
}));
}
// else if (evt.type == EventType.DragUpdated) {
// pointerData = new PointerData(
// timeStamp: Timer.timespanSinceStartup,
// change: PointerChange.dragFromEditorMove,
// kind: PointerDeviceKind.mouse,
// device: InputUtils.getMouseButtonKey(evt.button),
// physicalX: evt.mousePosition.x * _devicePixelRatio,
// physicalY: evt.mousePosition.y * _devicePixelRatio
// );
// }
// else if (evt.type == EventType.DragPerform) {
// pointerData = new PointerData(
// timeStamp: Timer.timespanSinceStartup,
// change: PointerChange.dragFromEditorRelease,
// kind: PointerDeviceKind.mouse,
// device: InputUtils.getMouseButtonKey(evt.button),
// physicalX: evt.mousePosition.x * _devicePixelRatio,
// physicalY: evt.mousePosition.y * _devicePixelRatio
// );
// }
//
// if (pointerData != null) {
// onPointerEvent(new PointerDataPacket(new List<PointerData> {
// pointerData
// }));
// }
}
RawKeyboard.instance._handleKeyEvent(Event.current);

return;
}
PointerData pointerData = new ScrollData(
timeStamp: Timer.timespanSinceStartup,
change: PointerChange.scroll,
kind: PointerDeviceKind.mouse,
device: _scrollInput.getDeviceId(),
physicalX: _scrollInput.getPointerPosX(),
physicalY: _scrollInput.getPointerPosY(),
scrollX: deltaScroll.x,
scrollY: deltaScroll.y
);
onPointerEvent(new PointerDataPacket(new List<PointerData> {
pointerData
}));
// PointerData pointerData = new ScrollData(
// timeStamp: Timer.timespanSinceStartup,
// change: PointerChange.scroll,
// kind: PointerDeviceKind.mouse,
// device: _scrollInput.getDeviceId(),
// physicalX: _scrollInput.getPointerPosX(),
// physicalY: _scrollInput.getPointerPosY(),
// scrollX: deltaScroll.x,
// scrollY: deltaScroll.y
// );
//
// onPointerEvent(new PointerDataPacket(new List<PointerData> {
// pointerData
// }));
}
public void Update() {

172
com.unity.uiwidgets/Runtime/engine2/UIWidgetsPanel.cs


using System;
using System.Collections.Generic;
using UnityEngine.EventSystems;
public class UIWidgetsPanel : RawImage {
public partial class UIWidgetsPanel : RawImage {
public static UIWidgetsPanel current {
get { return Window.instance._panel; }
}

_ptr = UIWidgetsPanel_constructor((IntPtr) _handle, UIWidgetsPanel_entrypoint);
UIWidgetsPanel_onEnable(_ptr, _renderTexture.GetNativeTexturePtr(),
_width, _height, _devicePixelRatio, Application.streamingAssetsPath);
Input_OnEnable();
public void entryPoint() {
void _entryPoint() {
try {
isolate = Isolate.current;
Window.instance._panel = this;

}
protected override void OnDisable() {
Input_OnDisable();
UIWidgetsPanel_onDisable(_ptr);
UIWidgetsPanel_dispose(_ptr);
_ptr = IntPtr.Zero;

if (isolate != null)
Isolate.remove(isolate);
D.assert(!isolate.isValid);
base.OnDisable();
}

_renderTexture = null;
}
protected virtual void Update() {
Input_Update();
}
protected virtual void OnGUI() {
Input_OnGUI();
}
public int registerTexture(Texture texture) {
return UIWidgetsPanel_registerTexture(_ptr, texture.GetNativeTexturePtr());
}
public void unregisterTexture(int textureId) {
UIWidgetsPanel_unregisterTexture(_ptr, textureId);
}
public void markNewFrameAvailable(int textureId) {
UIWidgetsPanel_markNewFrameAvailable(_ptr, textureId);
}
delegate void UIWidgetsPanel_EntrypointCallback(IntPtr handle);
[MonoPInvokeCallback(typeof(UIWidgetsPanel_EntrypointCallback))]

panel.entryPoint();
panel._entryPoint();
}
[DllImport(NativeBindings.dllName)]

[DllImport(NativeBindings.dllName)]
static extern void UIWidgetsPanel_onRenderTexture(
IntPtr ptr, IntPtr nativeTexturePtr, int width, int height, float dpi);
[DllImport(NativeBindings.dllName)]
static extern int UIWidgetsPanel_registerTexture(IntPtr ptr, IntPtr nativeTexturePtr);
[DllImport(NativeBindings.dllName)]
static extern void UIWidgetsPanel_unregisterTexture(IntPtr ptr, int textureId);
[DllImport(NativeBindings.dllName)]
static extern void UIWidgetsPanel_markNewFrameAvailable(IntPtr ptr, int textureId);
}
public partial class UIWidgetsPanel : IPointerDownHandler, IPointerUpHandler,
IPointerEnterHandler, IPointerExitHandler, IDragHandler {
bool _isEntered;
Vector2 _lastMousePosition;
void Input_OnEnable() {
}
void Input_OnDisable() {
}
void Input_Update() {
if (Input.touchCount == 0 && Input.mousePresent) {
if (_isEntered) {
if (!Input.GetMouseButton(0) && !Input.GetMouseButton(1) && !Input.GetMouseButton(2)) {
if (_lastMousePosition.x != Input.mousePosition.x ||
_lastMousePosition.y != Input.mousePosition.y) {
_lastMousePosition = Input.mousePosition;
_onMouseMove();
}
}
else {
_lastMousePosition = Input.mousePosition;
}
}
}
}
void Input_OnGUI() {
Event e = Event.current;
if (e.isKey) {
UIWidgetsPanel_onKey(_ptr, e.keyCode, e.type == EventType.KeyDown);
if (e.character != 0) {
UIWidgetsPanel_onChar(_ptr, e.character);
}
}
}
Vector2? _getPointerPosition(Vector2 position) {
Camera worldCamera = canvas.worldCamera;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
rectTransform, position, worldCamera, out var localPoint)) {
var scaleFactor = canvas.scaleFactor;
localPoint.x = (localPoint.x - rectTransform.rect.min.x) * scaleFactor;
localPoint.y = (rectTransform.rect.max.y - localPoint.y) * scaleFactor;
return localPoint;
}
return null;
}
void _onMouseMove() {
var pos = _getPointerPosition(Input.mousePosition);
if (pos == null) {
return;
}
UIWidgetsPanel_onMouseMove(_ptr, pos.Value.x, pos.Value.y);
}
public void OnPointerDown(PointerEventData eventData) {
var pos = _getPointerPosition(Input.mousePosition);
if (pos == null) {
return;
}
// mouse event
if (eventData.pointerId < 0) {
UIWidgetsPanel_onMouseDown(_ptr, pos.Value.x, pos.Value.y, eventData.pointerId);
}
}
public void OnPointerUp(PointerEventData eventData) {
var pos = _getPointerPosition(Input.mousePosition);
if (pos == null) {
return;
}
// mouse event
if (eventData.pointerId < 0) {
UIWidgetsPanel_onMouseUp(_ptr, pos.Value.x, pos.Value.y, eventData.pointerId);
}
}
public void OnPointerEnter(PointerEventData eventData) {
D.assert(eventData.pointerId < 0);
_isEntered = true;
_lastMousePosition = Input.mousePosition;
}
public void OnPointerExit(PointerEventData eventData) {
D.assert(eventData.pointerId < 0);
_isEntered = false;
UIWidgetsPanel_onMouseLeave(_ptr);
}
public void OnDrag(PointerEventData eventData) {
var pos = _getPointerPosition(Input.mousePosition);
if (pos == null) {
return;
}
// mouse event
if (eventData.pointerId < 0) {
UIWidgetsPanel_onMouseMove(_ptr, pos.Value.x, pos.Value.y);
}
}
[DllImport(NativeBindings.dllName)]
static extern void UIWidgetsPanel_onChar(IntPtr ptr, char c);
[DllImport(NativeBindings.dllName)]
static extern void UIWidgetsPanel_onKey(IntPtr ptr, KeyCode keyCode, bool isKeyDown);
[DllImport(NativeBindings.dllName)]
static extern void UIWidgetsPanel_onMouseDown(IntPtr ptr, float x, float y, int button);
[DllImport(NativeBindings.dllName)]
static extern void UIWidgetsPanel_onMouseUp(IntPtr ptr, float x, float y, int button);
[DllImport(NativeBindings.dllName)]
static extern void UIWidgetsPanel_onMouseMove(IntPtr ptr, float x, float y);
[DllImport(NativeBindings.dllName)]
static extern void UIWidgetsPanel_onMouseLeave(IntPtr ptr);
}
}

88
com.unity.uiwidgets/Runtime/gestures/converter.cs


break;
}
case PointerChange.scroll: {
var _scrollData = (ScrollData) datum;
_PointerState state = _ensureStateForPointer(datum, position);
state.initScrollPointer();
if (state.lastPosition != position) {
state.lastPosition = position;
}
Offset scrollDelta = new Offset(_scrollData.scrollX, _scrollData.scrollY) / devicePixelRatio;
yield return new PointerScrollEvent(
timeStamp: timeStamp,
pointer: state.pointer,
kind: kind,
device: _scrollData.device,
position: position,
scrollDelta: scrollDelta
);
break;
}
// case PointerChange.scroll: {
// var _scrollData = (ScrollData) datum;
// _PointerState state = _ensureStateForPointer(datum, position);
// state.initScrollPointer();
//
// if (state.lastPosition != position) {
// state.lastPosition = position;
// }
//
// Offset scrollDelta = new Offset(_scrollData.scrollX, _scrollData.scrollY) / devicePixelRatio;
// yield return new PointerScrollEvent(
// timeStamp: timeStamp,
// pointer: state.pointer,
// kind: kind,
// device: _scrollData.device,
// position: position,
// scrollDelta: scrollDelta
// );
// break;
// }
case PointerChange.up:
case PointerChange.cancel: {

}
break;
#if UNITY_EDITOR
case PointerChange.dragFromEditorMove: {
_PointerState state = _ensureStateForPointer(datum, position);
state.startNewPointer();
yield return new PointerDragFromEditorHoverEvent(
timeStamp: timeStamp,
pointer: state.pointer,
kind: kind,
device: datum.device,
position: position
);
}
break;
case PointerChange.dragFromEditorRelease: {
_PointerState state = _ensureStateForPointer(datum, position);
state.startNewPointer();
yield return new PointerDragFromEditorReleaseEvent(
timeStamp: timeStamp,
pointer: state.pointer,
kind: kind,
device: datum.device,
position: position
);
}
break;
// case PointerChange.dragFromEditorMove: {
// _PointerState state = _ensureStateForPointer(datum, position);
// state.startNewPointer();
// yield return new PointerDragFromEditorHoverEvent(
// timeStamp: timeStamp,
// pointer: state.pointer,
// kind: kind,
// device: datum.device,
// position: position
// );
// }
// break;
// case PointerChange.dragFromEditorRelease: {
// _PointerState state = _ensureStateForPointer(datum, position);
// state.startNewPointer();
// yield return new PointerDragFromEditorReleaseEvent(
// timeStamp: timeStamp,
// pointer: state.pointer,
// kind: kind,
// device: datum.device,
// position: position
// );
// }
// break;
#endif
}
}

8
com.unity.uiwidgets/Runtime/services/asset_bundle.cs


public abstract Future<byte[]> load(string key);
public virtual Future<string> loadString(string key, bool cache = true) {
return load(key).then<string>(data => {
return load(key).then_<string>(data => {
if (data == null)
throw new UIWidgetsError($"Unable to load asset: {key}");

public override Future<T> loadStructuredData<T>(string key, Func<string, Future<T>> parser) {
D.assert(key != null);
D.assert(parser != null);
return loadString(key).then<T>(value => parser(value));
return loadString(key).then_<T>(value => parser(value));
}
public override string ToString() => $"{foundation_.describeIdentity(this)}({_baseUrl})";

Completer completer = null;
Future<T> result = null;
loadString(key, cache: false).then<T>(value => parser(value)).then<object>((T value) => {
loadString(key, cache: false).then_<T>(value => parser(value)).then_<object>((T value) => {
result = new SynchronousFuture<T>(value);
_structuredDataCache[key] = result;
if (completer != null) {

public override Future<byte[]> load(string key) {
byte[] encoded = Encoding.UTF8.GetBytes(key);
return ServicesBinding.instance.defaultBinaryMessenger.send(
"uiwidgets/assets", encoded).then<byte[]>(asset => {
"uiwidgets/assets", encoded).then_<byte[]>(asset => {
if (asset == null)
throw new UIWidgetsError($"Unable to load asset: {key}");
return asset;

72
com.unity.uiwidgets/Runtime/ui/pointer.cs


down,
move,
up,
scroll,
dragFromEditorMove,
dragFromEditorRelease
stylus,
invertedStylus,
unknown
}
public enum PointerSignalKind {

}
public class PointerData {
public readonly struct PointerData {
TimeSpan timeStamp,
PointerChange change,
PointerDeviceKind kind,
TimeSpan? timeStamp = null,
PointerChange change = PointerChange.cancel,
PointerDeviceKind kind = PointerDeviceKind.touch,
int pointerIdentifier = 0,
float physicalDeltaX = 0.0f,
float physicalDeltaY = 0.0f,
bool synthesized = false,
float pressure = 0.0f,
float pressureMin = 0.0f,
float pressureMax = 0.0f,

int platformData = 0,
float scrollDeltaX = 0.0f,
float scrollDeltaY = 0.0f) {
this.timeStamp = timeStamp;
this.timeStamp = timeStamp ?? TimeSpan.Zero;
this.pointerIdentifier = pointerIdentifier;
this.physicalDeltaX = physicalDeltaX;
this.physicalDeltaY = physicalDeltaY;
this.synthesized = synthesized;
this.pressure = pressure;
this.pressureMin = pressureMin;
this.pressureMax = pressureMax;

public readonly PointerDeviceKind kind;
public readonly PointerSignalKind signalKind;
public readonly int device;
public readonly int pointerIdentifier;
public readonly float physicalDeltaX;
public readonly float physicalDeltaY;
public readonly bool synthesized;
public readonly float pressure;
public readonly float pressureMin;
public readonly float pressureMax;

public readonly int platformData;
public readonly float scrollDeltaX;
public readonly float scrollDeltaY;
}
public class ScrollData : PointerData {
public ScrollData(
TimeSpan timeStamp,
PointerChange change,
PointerDeviceKind kind,
PointerSignalKind signalKind = PointerSignalKind.none,
int device = 0,
float physicalX = 0.0f,
float physicalY = 0.0f,
float scrollX = 0.0f,
float scrollY = 0.0f) : base(timeStamp, change, kind, signalKind, device, physicalX, physicalY) {
this.scrollX = scrollX;
this.scrollY = scrollY;
}
public float scrollX;
public float scrollY;
public override string ToString() => $"PointerData(x: {physicalX}, y: {physicalY})";
public class PointerDataPacket {
public PointerDataPacket(List<PointerData> data) {
this.data = data;
// public class ScrollData : PointerData {
// public ScrollData(
// TimeSpan timeStamp,
// PointerChange change,
// PointerDeviceKind kind,
// PointerSignalKind signalKind = PointerSignalKind.none,
// int device = 0,
// float physicalX = 0.0f,
// float physicalY = 0.0f,
// float scrollX = 0.0f,
// float scrollY = 0.0f) : base(timeStamp, change, kind, signalKind, device, physicalX, physicalY) {
// this.scrollX = scrollX;
// this.scrollY = scrollY;
// }
//
// public float scrollX;
// public float scrollY;
// }
public struct PointerDataPacket {
public PointerDataPacket(List<PointerData> data = null) {
this.data = data ?? new List<PointerData>();
}
public readonly List<PointerData> data;

60
com.unity.uiwidgets/Runtime/ui2/compositing.cs


using System.Collections.Generic;
using System.Runtime.InteropServices;
using AOT;
using RSG;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;

Scene_dispose(ptr);
}
public Promise<Image> toImage(int width, int height) {
public Future<Image> toImage(int width, int height) {
var completer = new Promise<Image>(true);
GCHandle completerHandle = GCHandle.Alloc(completer);
return ui_._futurize(
(_Callback<Image> callback) => {
GCHandle callbackHandle = GCHandle.Alloc(callback);
IntPtr error =
Scene_toImage(_ptr, width, height, _toImageCallback,
(IntPtr) callbackHandle);
IntPtr error =
Scene_toImage(_ptr, width, height, _toImageCallback,
(IntPtr) completerHandle);
if (error != null) {
completerHandle.Free();
throw new Exception(Marshal.PtrToStringAnsi(error));
}
if (error != IntPtr.Zero) {
callbackHandle.Free();
return Marshal.PtrToStringAnsi(error);
}
return completer;
return null;
});
GCHandle completerHandle = (GCHandle) callbackHandle;
var completer = (Promise<Image>) completerHandle.Target;
completerHandle.Free();
GCHandle handle = (GCHandle) callbackHandle;
var callback = (_Callback<Image>) handle.Target;
handle.Free();
if (!Isolate.checkExists()) {
return;
}
if (result == IntPtr.Zero) {
completer.Reject(new Exception("operation failed"));
}
else {
var image = new Image(result);
completer.Resolve(image);
}
callback(result == IntPtr.Zero ? null : new Image(result));
}
catch (Exception ex) {
Debug.LogException(ex);

SceneBuilder_addPicture(_ptr, offset.dx, offset.dy, picture._ptr, hints);
}
public void addTexture(
int textureId,
Offset offset = null,
float width = 0.0f,
float height = 0.0f,
bool freeze = false
) {
offset = offset ?? Offset.zero;
SceneBuilder_addTexture(_ptr, offset.dx, offset.dy, width, height, textureId, freeze);
}
[DllImport(NativeBindings.dllName)]
static extern IntPtr SceneBuilder_constructor();

[DllImport(NativeBindings.dllName)]
static extern IntPtr SceneBuilder_addPicture(IntPtr ptr, float dx, float dy, IntPtr picture, int hints);
[DllImport(NativeBindings.dllName)]
static extern void SceneBuilder_addTexture(IntPtr ptr, float dx, float dy, float width, float height,
int textureId, bool freeze);
[DllImport(NativeBindings.dllName)]
static extern void SceneBuilder_addRetained(IntPtr ptr, IntPtr retainedLayer);

120
com.unity.uiwidgets/Runtime/ui2/hooks.cs


using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using AOT;
using Unity.UIWidgets.async2;

#endif
static unsafe void hook() {
Mono_hook(
Mono_throwException);
Mono_throwException,
Mono_shutdown);
Window_hook(
Window_constructor,

Window_drawFrame,
ui_._dispatchPlatformMessage);
ui_._dispatchPlatformMessage,
ui_._dispatchPointerDataPacket);
}
delegate void Mono_ThrowExceptionCallback(IntPtr exception);

throw new Exception(Marshal.PtrToStringAnsi(exception));
}
delegate void Mono_ShutdownCallback(IntPtr isolate);
[MonoPInvokeCallback(typeof(Mono_ShutdownCallback))]
static void Mono_shutdown(IntPtr isolate) {
try {
Isolate.shutdown(isolate);
}
catch (Exception ex) {
Debug.LogException(ex);
}
}
static extern void Mono_hook(Mono_ThrowExceptionCallback throwException);
static extern void Mono_hook(Mono_ThrowExceptionCallback throwException, Mono_ShutdownCallback shutdown);
delegate IntPtr Window_constructorCallback(IntPtr ptr);

Window_updateWindowMetricsCallback Window_updateWindowMetrics,
Window_beginFrameCallback Window_beginFrame,
Window_drawFrameCallback Window_drawFrame,
ui_.Window_dispatchPlatformMessageCallback Window_dispatchPlatformMessage);
ui_.Window_dispatchPlatformMessageCallback Window_dispatchPlatformMessage,
ui_.Window_dispatchPointerDataPacketCallback Window_dispatchPointerDataPacket);
}
public static partial class ui_ {

internal static unsafe void _dispatchPlatformMessage(
string name, byte* dataRaw, int dataLength, int responseId) {
try {
var window = Window.instance;
var data = new byte[dataLength];
Marshal.Copy((IntPtr) dataRaw, data, 0, dataLength);

Debug.LogError($"Message to \"{name}\" caused exception {ex}");
}
finally {
Window.instance._respondToPlatformMessage(responseId, null);
window._respondToPlatformMessage(responseId, null);
else if (Window.instance.onPlatformMessage != null) {
else if (window.onPlatformMessage != null) {
(name1, data1, callback1) => Window.instance.onPlatformMessage(name1, data1, callback1),
Window.instance._onPlatformMessageZone,
name, data, responseData =>
Window.instance._respondToPlatformMessage(responseId, responseData)
(name1, data1, callback1) => window.onPlatformMessage(name1, data1, callback1),
window._onPlatformMessageZone,
name, data, responseData => window._respondToPlatformMessage(responseId, responseData)
=> Window.instance._respondToPlatformMessage(responseId, responseData));
=> window._respondToPlatformMessage(responseId, responseData));
}
}
catch (Exception ex) {

internal unsafe delegate void Window_dispatchPointerDataPacketCallback(byte* bytes, int length);
[MonoPInvokeCallback(typeof(Window_dispatchPointerDataPacketCallback))]
internal static unsafe void _dispatchPointerDataPacket(byte* bytes, int length) {
try {
var window = Window.instance;
if (window.onPointerDataPacket != null)
_invoke1<PointerDataPacket>(
p => window.onPointerDataPacket(p),
window._onPointerDataPacketZone,
_unpackPointerDataPacket(bytes, length));
}
catch (Exception ex) {
Debug.LogException(ex);
}
}
internal static void _invoke1<A>(Action<A> callback, Zone zone, A arg) {
if (callback == null)
return;
D.assert(zone != null);
if (ReferenceEquals(zone, Zone.current)) {
callback(arg);
}
else {
zone.runUnaryGuarded((a) => {
callback((A) a);
return null;
}, arg);
}
}
internal static void _invoke3<A1, A2, A3>(Action<A1, A2, A3> callback, Zone zone, A1 arg1, A2 arg2, A3 arg3) {
if (callback == null)
return;

return null;
});
}
}
const int _kPointerDataFieldCount = 28;
static unsafe PointerDataPacket _unpackPointerDataPacket(byte* packet, int packetLength) {
const int kStride = 8;
const int kBytesPerPointerData = _kPointerDataFieldCount * kStride;
int length = packetLength / kBytesPerPointerData;
D.assert(length * kBytesPerPointerData == packetLength);
List<PointerData> data = new List<PointerData>(length);
for (int i = 0; i < length; ++i) {
int offset = i * _kPointerDataFieldCount;
data.Add(new PointerData(
timeStamp: TimeSpan.FromMilliseconds(*(long*) (packet + kStride * offset++)),
change: (PointerChange) (*(long*) (packet + kStride * offset++)),
kind: (PointerDeviceKind) (*(long*) (packet + kStride * offset++)),
signalKind: (PointerSignalKind) (*(long*) (packet + kStride * offset++)),
device: (int) *(long*) (packet + kStride * offset++),
pointerIdentifier: (int) (*(long*) (packet + kStride * offset++)),
physicalX: (float) *(double*) (packet + kStride * offset++),
physicalY: (float) *(double*) (packet + kStride * offset++),
physicalDeltaX: (float) *(double*) (packet + kStride * offset++),
physicalDeltaY: (float) *(double*) (packet + kStride * offset++),
buttons: (int) *(long*) (packet + kStride * offset++),
obscured: *(long*) (packet + kStride * offset++) != 0,
synthesized: *(long*) (packet + kStride * offset++) != 0,
pressure: (float) *(double*) (packet + kStride * offset++),
pressureMin: (float) *(double*) (packet + kStride * offset++),
pressureMax: (float) *(double*) (packet + kStride * offset++),
distance: (float) *(double*) (packet + kStride * offset++),
distanceMax: (float) *(double*) (packet + kStride * offset++),
size: (float) *(double*) (packet + kStride * offset++),
radiusMajor: (float) *(double*) (packet + kStride * offset++),
radiusMinor: (float) *(double*) (packet + kStride * offset++),
radiusMin: (float) *(double*) (packet + kStride * offset++),
radiusMax: (float) *(double*) (packet + kStride * offset++),
orientation: (float) *(double*) (packet + kStride * offset++),
tilt: (float) *(double*) (packet + kStride * offset++),
platformData: (int) *(long*) (packet + kStride * offset++),
scrollDeltaX: (float) *(double*) (packet + kStride * offset++),
scrollDeltaY: (float) *(double*) (packet + kStride * offset++)
));
D.assert(offset == (i + 1) * _kPointerDataFieldCount);
}
return new PointerDataPacket(data: data);
}
}
}

50
com.unity.uiwidgets/Runtime/ui2/isolate.cs


using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.UIWidgets.foundation;
using UnityEngine;
namespace Unity.UIWidgets.ui2 {
public class Isolate {

IntPtr _ptr;
readonly Dictionary<IntPtr, WeakReference<NativeWrapper>> _nativeWrappers =
new Dictionary<IntPtr, WeakReference<NativeWrapper>>();
bool _inShutdown = false;
internal void addNativeWrapper(NativeWrapper wrapper) {
lock (_nativeWrappers) {
_nativeWrappers.Add(wrapper._ptr, new WeakReference<NativeWrapper>(wrapper));
}
}
internal void removeNativeWrapper(IntPtr ptr) {
lock (_nativeWrappers) {
if (_inShutdown) {
return;
}
_nativeWrappers.Remove(ptr);
}
}
public bool isValid => _ptr != IntPtr.Zero;
public static Isolate current {

D.assert(value.isValid);
return value;
}
var isolate = new Isolate(ptr);
_isolates.Add(ptr, isolate);
return isolate;

return ptr;
}
internal static void remove(Isolate isolate) {
D.assert(isolate != null && isolate.isValid);
_isolates.Remove(isolate._ptr);
public static bool checkExists() {
IntPtr ptr = Isolate_current();
return ptr != IntPtr.Zero;
}
internal static void shutdown(IntPtr ptr) {
var isolate = _isolates[ptr];
D.assert(isolate._ptr == ptr);
lock (isolate._nativeWrappers) {
isolate._inShutdown = true;
foreach (var entry in isolate._nativeWrappers) {
if (entry.Value.TryGetTarget(out NativeWrapper target)) {
target._dispose();
}
}
isolate._nativeWrappers.Clear();
isolate._inShutdown = false;
}
_isolates.Remove(ptr);
isolate._ptr = IntPtr.Zero;
}

51
com.unity.uiwidgets/Runtime/ui2/native_bindings.cs


#else
internal const string dllName = "libUIWidgets_d";
#endif
protected internal IntPtr _ptr { get; protected set; }
protected internal IntPtr _ptr { get; private set; }
Isolate _isolate;
_setPtr(ptr);
}
protected void _setPtr(IntPtr ptr) {
_isolate = Isolate.current;
_isolate.addNativeWrapper(this);
~NativeWrapper() {
internal void _dispose(bool finalizer = false) {
D.assert(_isolate.isValid);
_isolate.removeNativeWrapper(_ptr);
if (!finalizer) {
GC.SuppressFinalize(this);
}
~NativeWrapper() {
_dispose(true);
}
public abstract class NativeWrapperDisposable : IDisposable {
protected internal IntPtr _ptr { get; protected set; }
public abstract class NativeWrapperDisposable : NativeWrapper, IDisposable {
protected NativeWrapperDisposable(IntPtr ptr) {
D.assert(ptr != IntPtr.Zero);
_ptr = ptr;
protected NativeWrapperDisposable(IntPtr ptr) : base(ptr) {
~NativeWrapperDisposable() {
if (_ptr != IntPtr.Zero) {
DisposePtr(_ptr);
_ptr = IntPtr.Zero;
}
}
protected abstract void DisposePtr(IntPtr ptr);
if (_ptr != IntPtr.Zero) {
DisposePtr(_ptr);
_ptr = IntPtr.Zero;
}
GC.SuppressFinalize(this);
_dispose();
}
}
}

313
com.unity.uiwidgets/Runtime/ui2/painting.cs


using System.Runtime.InteropServices;
using System.Text;
using AOT;
using RSG;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using UnityEngine;

antiAliasWithSaveLayer,
}
public static partial class ui_ {
const int _kDoNotResizeDimension = -1;
}
public class Paint {
internal readonly byte[] _data = new byte[_kDataByteCount];

png,
}
enum PixelFormat {
public enum PixelFormat {
/**
* TODO: _ImageInfo
*/
[StructLayout(LayoutKind.Sequential)]
struct _ImageInfo {
internal _ImageInfo(int width, int height, int format, int? rowBytes = null) {
this.width = width;
this.height = height;
this.format = format;
this.rowBytes = rowBytes ?? width * 4;
}
public int width;
public int height;
public int format;
public int rowBytes;
}
Image_dispose(_ptr);
Image_dispose(ptr);
}
public int width => Image_width(_ptr);

public Promise<byte[]> toByteData(
public Future<byte[]> toByteData(
var completer = new Promise<byte[]>(true);
GCHandle completerHandle = GCHandle.Alloc(completer);
return ui_._futurize(
(_Callback<byte[]> callback) => {
GCHandle callbackHandle = GCHandle.Alloc(callback);
IntPtr error =
Image_toByteData(_ptr, (int) format, _toByteDataCallback,
(IntPtr) completerHandle);
if (error != null) {
completerHandle.Free();
throw new Exception(Marshal.PtrToStringAnsi(error));
}
IntPtr error = Image_toByteData(_ptr,
(int) format, _toByteDataCallback, (IntPtr) callbackHandle);
if (error != IntPtr.Zero) {
callbackHandle.Free();
return Marshal.PtrToStringAnsi(error);
}
return completer;
return null;
});
GCHandle completerHandle = (GCHandle) callbackHandle;
var completer = (Promise<byte[]>) completerHandle.Target;
completerHandle.Free();
GCHandle handle = (GCHandle) callbackHandle;
var callback = (_Callback<byte[]>) handle.Target;
handle.Free();
if (!Isolate.checkExists()) {
return;
}
completer.Resolve(new byte[0]);
callback(new byte[0]);
completer.Resolve(bytes);
callback(bytes);
}
}
catch (Exception ex) {

/*
* TODO: FrameInfo, Codec
*/
public delegate void ImageDecoderCallback(Image result);
public class FrameInfo : NativeWrapper {
internal FrameInfo(IntPtr ptr) : base(ptr) {
}
protected override void DisposePtr(IntPtr ptr) {
FrameInfo_dispose(ptr);
}
public TimeSpan duration => TimeSpan.FromMilliseconds(_durationMillis);
int _durationMillis => FrameInfo_durationMillis(_ptr);
public Image image => new Image(FrameInfo_image(_ptr));
[DllImport(NativeBindings.dllName)]
static extern void FrameInfo_dispose(IntPtr ptr);
[DllImport(NativeBindings.dllName)]
static extern int FrameInfo_durationMillis(IntPtr ptr);
[DllImport(NativeBindings.dllName)]
static extern IntPtr FrameInfo_image(IntPtr ptr);
}
public class Codec : NativeWrapperDisposable {
internal Codec(IntPtr ptr) : base(ptr) {
}
protected override void DisposePtr(IntPtr ptr) {
Codec_dispose(ptr);
}
public int frameCount => Codec_frameCount(_ptr);
public int repetitionCount => Codec_repetitionCount(_ptr);
public Future<FrameInfo> getNextFrame() {
return ui_._futurize<FrameInfo>(_getNextFrame);
}
string _getNextFrame(_Callback<FrameInfo> callback) {
GCHandle callbackHandle = GCHandle.Alloc(callback);
IntPtr error = Codec_getNextFrame(_ptr, _getNextFrameCallback, (IntPtr) callbackHandle);
if (error != IntPtr.Zero) {
callbackHandle.Free();
return Marshal.PtrToStringAnsi(error);
}
return null;
}
[MonoPInvokeCallback(typeof(Codec_getNextFrameCallback))]
static void _getNextFrameCallback(IntPtr callbackHandle, IntPtr ptr) {
GCHandle handle = (GCHandle) callbackHandle;
var callback = (_Callback<FrameInfo>) handle.Target;
handle.Free();
if (!Isolate.checkExists()) {
return;
}
try {
callback(ptr == IntPtr.Zero ? null : new FrameInfo(ptr));
}
catch (Exception ex) {
Debug.LogException(ex);
}
}
internal static unsafe string _instantiateImageCodec(byte[] list, _Callback<Codec> callback,
_ImageInfo? imageInfo, int targetWidth, int targetHeight) {
GCHandle callbackHandle = GCHandle.Alloc(callback);
fixed (byte* bytes = list) {
IntPtr error = Codec_instantiateImageCodec(bytes, list?.Length ?? 0,
_instantiateImageCodecCallback, (IntPtr) callbackHandle,
imageInfo ?? default, imageInfo.HasValue, targetWidth, targetHeight);
if (error != IntPtr.Zero) {
callbackHandle.Free();
return Marshal.PtrToStringAnsi(error);
}
}
return null;
}
[MonoPInvokeCallback(typeof(Codec_instantiateImageCodecCallback))]
static void _instantiateImageCodecCallback(IntPtr callbackHandle, IntPtr ptr) {
GCHandle handle = (GCHandle) callbackHandle;
var callback = (_Callback<Codec>) handle.Target;
handle.Free();
try {
D.assert(ptr != IntPtr.Zero);
callback(new Codec(ptr));
}
catch (Exception ex) {
Debug.LogException(ex);
}
}
[DllImport(NativeBindings.dllName)]
static extern void Codec_dispose(IntPtr ptr);
[DllImport(NativeBindings.dllName)]
static extern int Codec_frameCount(IntPtr ptr);
[DllImport(NativeBindings.dllName)]
static extern int Codec_repetitionCount(IntPtr ptr);
delegate void Codec_getNextFrameCallback(IntPtr callbackHandle, IntPtr ptr);
[DllImport(NativeBindings.dllName)]
static extern IntPtr Codec_getNextFrame(IntPtr ptr, Codec_getNextFrameCallback callback, IntPtr callbackHandle);
delegate void Codec_instantiateImageCodecCallback(IntPtr callbackHandle, IntPtr ptr);
[DllImport(NativeBindings.dllName)]
static extern unsafe IntPtr Codec_instantiateImageCodec(byte* list, int listLength,
Codec_instantiateImageCodecCallback callback,
IntPtr callbackHandle, _ImageInfo imageInfo, bool hasImageInfo, int targetWidth, int targetHeight);
}
public static partial class ui_ {
public static Future<Codec> instantiateImageCodec(byte[] list, int? targetWidth = null,
int? targetHeight = null) {
return _futurize(
(_Callback<Codec> callback) => Codec._instantiateImageCodec(list, callback, null,
targetWidth ?? _kDoNotResizeDimension, targetHeight ?? _kDoNotResizeDimension)
);
}
public static void decodeImageFromList(byte[] list, ImageDecoderCallback callback) {
_decodeImageFromListAsync(list, callback);
}
static Future _decodeImageFromListAsync(byte[] list, ImageDecoderCallback callback) {
return instantiateImageCodec(list).then_(codec => {
return codec.getNextFrame().then_(frameInfo => { callback(frameInfo.image); });
});
}
public static void decodeImageFromPixels(
byte[] pixels,
int width,
int height,
PixelFormat format,
ImageDecoderCallback callback,
int? rowBytes = null, int? targetWidth = null, int? targetHeight = null
) {
_ImageInfo imageInfo = new _ImageInfo(width, height, (int) format, rowBytes);
Future<Codec> codecFuture = _futurize(
(_Callback<Codec> callback1) => Codec._instantiateImageCodec(pixels, callback1, imageInfo,
targetWidth ?? _kDoNotResizeDimension, targetHeight ?? _kDoNotResizeDimension)
);
codecFuture.then_<FrameInfo>(codec => codec.getNextFrame())
.then_(frameInfo => callback(frameInfo.image));
}
}
public enum PathFillType {
nonZero,
evenOdd,

}
protected override void DisposePtr(IntPtr ptr) {
Layer_dispose(ptr);
EngineLayer_dispose(ptr);
static extern void Layer_dispose(IntPtr ptr);
static extern void EngineLayer_dispose(IntPtr ptr);
}
public class Path : NativeWrapper {

}
cullRect = cullRect ?? Rect.largest;
_ptr = Canvas_constructor(recorder._ptr, cullRect.left, cullRect.top,
_setPtr(Canvas_constructor(recorder._ptr, cullRect.left, cullRect.top,
cullRect.bottom);
cullRect.bottom));
}
protected override void DisposePtr(IntPtr ptr) {

}
[DllImport(NativeBindings.dllName)]
public static extern IntPtr Canvas_constructor(IntPtr recorder,
static extern IntPtr Canvas_constructor(IntPtr recorder,
float left,
float top,
float right,

public static extern void Canvas_dispose(IntPtr ptr);
static extern void Canvas_dispose(IntPtr ptr);
public static extern void Canvas_save(IntPtr ptr);
static extern void Canvas_save(IntPtr ptr);
public static extern unsafe void
static extern unsafe void
public static extern unsafe void Canvas_saveLayer(IntPtr ptr,
static extern unsafe void Canvas_saveLayer(IntPtr ptr,
public static extern void Canvas_restore(IntPtr ptr);
static extern void Canvas_restore(IntPtr ptr);
public static extern int Canvas_getSaveCount(IntPtr ptr);
static extern int Canvas_getSaveCount(IntPtr ptr);
public static extern void Canvas_translate(IntPtr ptr,
static extern void Canvas_translate(IntPtr ptr,
public static extern void Canvas_scale(IntPtr ptr,
static extern void Canvas_scale(IntPtr ptr,
public static extern void Canvas_rotate(IntPtr ptr,
static extern void Canvas_rotate(IntPtr ptr,
public static extern void Canvas_skew(IntPtr ptr,
static extern void Canvas_skew(IntPtr ptr,
public static extern unsafe void Canvas_transform(IntPtr ptr,
static extern unsafe void Canvas_transform(IntPtr ptr,
public static extern void Canvas_clipRect(IntPtr ptr,
static extern void Canvas_clipRect(IntPtr ptr,
public static extern unsafe void Canvas_clipRRect(IntPtr ptr,
static extern unsafe void Canvas_clipRRect(IntPtr ptr,
public static extern void Canvas_clipPath(IntPtr ptr,
static extern void Canvas_clipPath(IntPtr ptr,
public static extern void Canvas_drawColor(IntPtr ptr,
static extern void Canvas_drawColor(IntPtr ptr,
public static extern unsafe void Canvas_drawLine(IntPtr ptr,
static extern unsafe void Canvas_drawLine(IntPtr ptr,
public static extern unsafe void Canvas_drawPaint(IntPtr ptr,
static extern unsafe void Canvas_drawPaint(IntPtr ptr,
public static extern unsafe void Canvas_drawRect(IntPtr ptr,
static extern unsafe void Canvas_drawRect(IntPtr ptr,
public static extern unsafe void Canvas_drawRRect(IntPtr ptr,
static extern unsafe void Canvas_drawRRect(IntPtr ptr,
public static extern unsafe void Canvas_drawDRRect(IntPtr ptr,
static extern unsafe void Canvas_drawDRRect(IntPtr ptr,
public static extern unsafe void Canvas_drawOval(IntPtr ptr,
static extern unsafe void Canvas_drawOval(IntPtr ptr,
public static extern unsafe void Canvas_drawCircle(IntPtr ptr,
static extern unsafe void Canvas_drawCircle(IntPtr ptr,
public static extern unsafe void Canvas_drawArc(IntPtr ptr,
static extern unsafe void Canvas_drawArc(IntPtr ptr,
public static extern unsafe void Canvas_drawPath(IntPtr ptr,
static extern unsafe void Canvas_drawPath(IntPtr ptr,
public static extern unsafe void Canvas_drawImage(IntPtr ptr,
static extern unsafe void Canvas_drawImage(IntPtr ptr,
public static extern unsafe void Canvas_drawImageRect(IntPtr ptr,
static extern unsafe void Canvas_drawImageRect(IntPtr ptr,
public static extern unsafe void Canvas_drawImageNine(IntPtr ptr,
static extern unsafe void Canvas_drawImageNine(IntPtr ptr,
public static extern void Canvas_drawPicture(IntPtr ptr, IntPtr picture);
static extern void Canvas_drawPicture(IntPtr ptr, IntPtr picture);
public static extern unsafe void Canvas_drawPoints(IntPtr ptr,
static extern unsafe void Canvas_drawPoints(IntPtr ptr,
public static extern unsafe void Canvas_drawVertices(IntPtr ptr, IntPtr vertices, int blendMode,
static extern unsafe void Canvas_drawVertices(IntPtr ptr, IntPtr vertices, int blendMode,
public static extern unsafe void Canvas_drawAtlas(IntPtr ptr, IntPtr* paintObjects, byte* paintData,
static extern unsafe void Canvas_drawAtlas(IntPtr ptr, IntPtr* paintObjects, byte* paintData,
public static extern void Canvas_drawShadow(IntPtr ptr, IntPtr path, uint color, float elevation,
static extern void Canvas_drawShadow(IntPtr ptr, IntPtr path, uint color, float elevation,
bool transparentOccluder);
}

Picture_dispose(ptr);
}
public Future toImage(int width, int height) {
public Future<Image> toImage(int width, int height) {
if (width <= 0 || height <= 0) {
throw new ArgumentException("Invalid image dimensions.");
}

GCHandle handle = (GCHandle) callbackHandle;
var callback = (_Callback<Image>) handle.Target;
handle.Free();
if (!Isolate.checkExists()) {
return;
}
try {
callback(result == IntPtr.Zero ? null : new Image(result));

delegate string _Callbacker<T>(_Callback<T> callback);
public static partial class ui_ {
internal static Future _futurize<T>(_Callbacker<T> callbacker) {
internal static Future<T> _futurize<T>(_Callbacker<T> callbacker) {
Completer completer = Completer.sync();
string error = callbacker(t => {
if (t == null) {

});
if (error != null)
throw new Exception(error);
return completer.future;
return completer.future.to<T>();
}
}
}

17
com.unity.uiwidgets/Runtime/ui2/window.cs


}
}
public PointerDataPacketCallback onPointerDataPacket { get; set; }
public PointerDataPacketCallback onPointerDataPacket {
get { return _onPointerDataPacket; }
set {
_onPointerDataPacket = value;
_onPointerDataPacketZone = Zone.current;
}
}
PointerDataPacketCallback _onPointerDataPacket;
internal Zone _onPointerDataPacketZone;
public string defaultRouteName {
get {

[DllImport(NativeBindings.dllName)]
static extern void Window_freeDefaultRouteName(IntPtr routeNamePtr);
[DllImport(NativeBindings.dllName)]
static extern void Window_scheduleFrame(IntPtr ptr);

GCHandle handle = (GCHandle) callbackHandle;
var callback = (PlatformMessageResponseCallback) handle.Target;
handle.Free();
if (!Isolate.checkExists()) {
return;
}
byte[] bytes = null;
if (data != null && dataLength != 0) {

14
engine/Build.bee.cs


"src/lib/ui/painting/canvas.cc",
"src/lib/ui/painting/canvas.h",
"src/lib/ui/painting/codec.cc",
"src/lib/ui/painting/codec.h",
"src/lib/ui/painting/frame_info.cc",
"src/lib/ui/painting/frame_info.h",
"src/lib/ui/painting/gradient.cc",
"src/lib/ui/painting/gradient.h",
"src/lib/ui/painting/image.cc",

"src/lib/ui/painting/image_shader.h",
"src/lib/ui/painting/matrix.cc",
"src/lib/ui/painting/matrix.h",
"src/lib/ui/painting/multi_frame_codec.cc",
"src/lib/ui/painting/multi_frame_codec.h",
"src/lib/ui/painting/path.cc",
"src/lib/ui/painting/path.h",
"src/lib/ui/painting/paint.cc",

"src/lib/ui/painting/rrect.h",
"src/lib/ui/painting/shader.cc",
"src/lib/ui/painting/shader.h",
"src/lib/ui/painting/single_frame_codec.cc",
"src/lib/ui/painting/single_frame_codec.h",
"src/lib/ui/painting/vertices.cc",
"src/lib/ui/painting/vertices.h",

"src/shell/platform/unity/gfx_worker_task_runner.cc",
"src/shell/platform/unity/gfx_worker_task_runner.h",
"src/shell/platform/unity/unity_surface_manager.cc",
"src/shell/platform/unity/unity_surface_manager.h",
"src/shell/platform/unity/unity_external_texture_gl.cc",
"src/shell/platform/unity/unity_external_texture_gl.h",
"src/shell/platform/unity/unity_surface_manager.cc",
"src/shell/platform/unity/unity_surface_manager.h",
"src/shell/platform/unity/win32_task_runner.cc",
"src/shell/platform/unity/win32_task_runner.h",

14
engine/src/flow/skia_gpu_object.cc


drain_pending_(false),
context_(context) {}
SkiaUnrefQueue::~SkiaUnrefQueue() {
if (!objects_.empty()) {
Drain(false);
}
}
SkiaUnrefQueue::~SkiaUnrefQueue() { FML_DCHECK(objects_.empty()); }
void SkiaUnrefQueue::Unref(SkRefCnt* object) {
std::scoped_lock lock(mutex_);

}
}
void SkiaUnrefQueue::Drain(bool performDeferredCleanup) {
void SkiaUnrefQueue::Drain() {
TRACE_EVENT0("uiwidgets", "SkiaUnrefQueue::Drain");
std::deque<SkRefCnt*> skia_objects;
{

skia_object->unref();
}
if (performDeferredCleanup) {
if (context_ && skia_objects.size() > 0) {
context_->performDeferredCleanup(std::chrono::milliseconds(0));
}
if (context_ && skia_objects.size() > 0) {
context_->performDeferredCleanup(std::chrono::milliseconds(0));
}
}

2
engine/src/flow/skia_gpu_object.h


// to go away), we may need to pre-emptively drain the unref queue. It is the
// responsibility of the caller to ensure that no further unrefs are queued
// after this call.
void Drain(bool performDeferredCleanup = true);
void Drain();
private:
const fml::RefPtr<fml::TaskRunner> task_runner_;

18
engine/src/lib/ui/compositing/scene_builder.cc


UIWIDGETS_API(void) SceneBuilder_dispose(SceneBuilder* ptr) { ptr->Release(); }
UIWIDGETS_API(void)
UIWIDGETS_API(EngineLayer*)
ptr->pushTransform(matrix4);
const auto layer = ptr->pushTransform(matrix4);
layer->AddRef();
return layer.get();
UIWIDGETS_API(void)
UIWIDGETS_API(EngineLayer*)
ptr->pushOffset(dx, dy);
const auto layer = ptr->pushOffset(dx, dy);
layer->AddRef();
return layer.get();
}
UIWIDGETS_API(void)

SceneBuilder_addPicture(SceneBuilder* ptr, float dx, float dy, Picture* picture,
int hints) {
ptr->addPicture(dx, dy, picture, hints);
}
UIWIDGETS_API(void)
SceneBuilder_addTexture(SceneBuilder* ptr, float dx, float dy, float width,
float height, int texture_id, bool freeze) {
ptr->addTexture(dx, dy, width, height, texture_id, freeze);
}
} // namespace uiwidgets

6
engine/src/lib/ui/painting/engine_layer.cc


#include "engine_layer.h"
#include "runtime/mono_api.h"
namespace uiwidgets {
EngineLayer::EngineLayer(std::shared_ptr<ContainerLayer> layer)

size_t EngineLayer::GetAllocationSize() { return 3000; };
size_t EngineLayer::GetAllocationSize() { return 3000; }
UIWIDGETS_API(void) EngineLayer_dispose(EngineLayer* ptr) { ptr->Release(); }
} // namespace uiwidgets

15
engine/src/lib/ui/painting/image.cc


CanvasImage::~CanvasImage() = default;
const char* CanvasImage::toByteData(int format, EncodeImageCallback callback,
const char* CanvasImage::toByteData(int format, RawEncodeImageCallback callback,
Mono_Handle callback_handle) {
return EncodeImage(this, format, callback, callback_handle);
}

} else {
return sizeof(CanvasImage);
}
}
UIWIDGETS_API(void) Image_dispose(CanvasImage* ptr) { ptr->Release(); }
UIWIDGETS_API(int) Image_width(CanvasImage* ptr) { return ptr->width(); }
UIWIDGETS_API(int) Image_height(CanvasImage* ptr) { return ptr->height(); }
UIWIDGETS_API(const char*)
Image_toByteData(CanvasImage* ptr, int format,
RawEncodeImageCallback encode_image_callback,
Mono_Handle callback_handle) {
return ptr->toByteData(format, encode_image_callback, callback_handle);
}
} // namespace uiwidgets

2
engine/src/lib/ui/painting/image.h


int height() { return image_.get()->height(); }
const char* toByteData(int format, EncodeImageCallback callback,
const char* toByteData(int format, RawEncodeImageCallback callback,
Mono_Handle callback_handle);
void dispose();

377
engine/src/lib/ui/painting/image_decoder.cc


#include "src/codec/SkCodecImageGenerator.h"
namespace uiwidgets {
namespace {
constexpr double kAspectRatioChangedThreshold = 0.01;
} // namespace
fml::WeakPtr<IOManager> io_manager) {}
fml::WeakPtr<IOManager> io_manager)
: runners_(std::move(runners)),
concurrent_task_runner_(std::move(concurrent_task_runner)),
io_manager_(std::move(io_manager)),
weak_factory_(this) {
FML_DCHECK(runners_.IsValid());
FML_DCHECK(runners_.GetUITaskRunner()->RunsTasksOnCurrentThread())
<< "The image decoder must be created & collected on the UI thread.";
}
ImageDecoder::~ImageDecoder() = default;
static double AspectRatio(const SkISize& size) {
return static_cast<double>(size.width()) / size.height();
}
// Get the updated dimensions of the image. If both dimensions are specified,
// use them. If one of them is specified, respect the one that is and use the
// aspect ratio to calculate the other. If neither dimension is specified, use
// intrinsic dimensions of the image.
static SkISize GetResizedDimensions(SkISize current_size,
std::optional<uint32_t> target_width,
std::optional<uint32_t> target_height) {
if (current_size.isEmpty()) {
return SkISize::MakeEmpty();
}
if (target_width && target_height) {
return SkISize::Make(target_width.value(), target_height.value());
}
const auto aspect_ratio = AspectRatio(current_size);
if (target_width) {
return SkISize::Make(target_width.value(),
target_width.value() / aspect_ratio);
}
if (target_height) {
return SkISize::Make(target_height.value() * aspect_ratio,
target_height.value());
}
return current_size;
}
static sk_sp<SkImage> ResizeRasterImage(sk_sp<SkImage> image,
const SkISize& resized_dimensions,
const fml::tracing::TraceFlow& flow) {
FML_DCHECK(!image->isTextureBacked());
TRACE_EVENT0("uiwidgets", __FUNCTION__);
flow.Step(__FUNCTION__);
if (resized_dimensions.isEmpty()) {
FML_LOG(ERROR) << "Could not resize to empty dimensions.";
return nullptr;
}
if (image->dimensions() == resized_dimensions) {
return image->makeRasterImage();
}
if (resized_dimensions.width() > image->dimensions().width() ||
resized_dimensions.height() > image->dimensions().height()) {
FML_LOG(WARNING) << "Image is being upsized from "
<< image->dimensions().width() << "x"
<< image->dimensions().height() << " to "
<< resized_dimensions.width() << "x"
<< resized_dimensions.height()
<< ". Are cache(Height|Width) used correctly?";
// TOOD(48885): consider exiting here, there's no good reason to support
// upsampling in a "caching"-optimization context..
}
const bool aspect_ratio_changed =
std::abs(AspectRatio(resized_dimensions) -
AspectRatio(image->dimensions())) > kAspectRatioChangedThreshold;
if (aspect_ratio_changed) {
// This is probably a bug. If a user passes dimensions that change the
// aspect ratio in a "caching" context that's probably not working as
// intended and rather a signal that the API is hard to use.
FML_LOG(WARNING)
<< "Aspect ratio changes. Are cache(Height|Width) used correctly?";
}
const auto scaled_image_info =
image->imageInfo().makeDimensions(resized_dimensions);
SkBitmap scaled_bitmap;
if (!scaled_bitmap.tryAllocPixels(scaled_image_info)) {
FML_LOG(ERROR) << "Failed to allocate memory for bitmap of size "
<< scaled_image_info.computeMinByteSize() << "B";
return nullptr;
}
if (!image->scalePixels(scaled_bitmap.pixmap(), kLow_SkFilterQuality,
SkImage::kDisallow_CachingHint)) {
FML_LOG(ERROR) << "Could not scale pixels";
return nullptr;
}
// Marking this as immutable makes the MakeFromBitmap call share the pixels
// instead of copying.
scaled_bitmap.setImmutable();
auto scaled_image = SkImage::MakeFromBitmap(scaled_bitmap);
if (!scaled_image) {
FML_LOG(ERROR) << "Could not create a scaled image from a scaled bitmap.";
return nullptr;
}
return scaled_image;
}
static sk_sp<SkImage> ImageFromDecompressedData(
sk_sp<SkData> data, ImageDecoder::ImageInfo info,
std::optional<uint32_t> target_width, std::optional<uint32_t> target_height,
const fml::tracing::TraceFlow& flow) {
TRACE_EVENT0("uiwidgets", __FUNCTION__);
flow.Step(__FUNCTION__);
auto image = SkImage::MakeRasterData(info.sk_info, data, info.row_bytes);
if (!image) {
FML_LOG(ERROR) << "Could not create image from decompressed bytes.";
return nullptr;
}
if (!target_width && !target_height) {
// No resizing requested. Just rasterize the image.
return image->makeRasterImage();
}
auto resized_dimensions =
GetResizedDimensions(image->dimensions(), target_width, target_height);
return ResizeRasterImage(std::move(image), resized_dimensions, flow);
}
sk_sp<SkImage> ImageFromCompressedData(sk_sp<SkData> data,
std::optional<uint32_t> target_width,
std::optional<uint32_t> target_height,
const fml::tracing::TraceFlow& flow) {
TRACE_EVENT0("uiwidgets", __FUNCTION__);
flow.Step(__FUNCTION__);
if (!target_width && !target_height) {
// No resizing requested. Just decode & rasterize the image.
return SkImage::MakeFromEncoded(data)->makeRasterImage();
}
auto codec = SkCodec::MakeFromData(data);
if (codec == nullptr) {
return nullptr;
}
const auto* codec_ptr = codec.get();
// Note that we cannot read the dimensions from the codec since they don't
// respect image orientation provided e.g. in EXIF data.
auto image_generator = SkCodecImageGenerator::MakeFromCodec(std::move(codec));
const auto& source_dimensions = image_generator->getInfo().dimensions();
auto resized_dimensions =
GetResizedDimensions(source_dimensions, target_width, target_height);
// No resize needed.
if (resized_dimensions == source_dimensions) {
return SkImage::MakeFromEncoded(data)->makeRasterImage();
}
auto decode_dimensions = codec_ptr->getScaledDimensions(
std::max(static_cast<double>(resized_dimensions.width()) /
source_dimensions.width(),
static_cast<double>(resized_dimensions.height()) /
source_dimensions.height()));
// If the codec supports efficient sub-pixel decoding, decoded at a resolution
// close to the target resolution before resizing.
if (decode_dimensions != codec_ptr->dimensions()) {
if (source_dimensions != codec_ptr->dimensions()) {
decode_dimensions =
SkISize::Make(decode_dimensions.height(), decode_dimensions.width());
}
auto scaled_image_info =
image_generator->getInfo().makeDimensions(decode_dimensions);
SkBitmap scaled_bitmap;
if (!scaled_bitmap.tryAllocPixels(scaled_image_info)) {
FML_LOG(ERROR) << "Failed to allocate memory for bitmap of size "
<< scaled_image_info.computeMinByteSize() << "B";
return nullptr;
}
const auto& pixmap = scaled_bitmap.pixmap();
if (image_generator->getPixels(pixmap.info(), pixmap.writable_addr(),
pixmap.rowBytes())) {
// Marking this as immutable makes the MakeFromBitmap call share
// the pixels instead of copying.
scaled_bitmap.setImmutable();
auto decoded_image = SkImage::MakeFromBitmap(scaled_bitmap);
FML_DCHECK(decoded_image);
if (!decoded_image) {
FML_LOG(ERROR)
<< "Could not create a scaled image from a scaled bitmap.";
return nullptr;
}
return ResizeRasterImage(std::move(decoded_image), resized_dimensions,
flow);
}
}
auto image = SkImage::MakeFromEncoded(data);
if (!image) {
return nullptr;
}
return ResizeRasterImage(std::move(image), resized_dimensions, flow);
}
static SkiaGPUObject<SkImage> UploadRasterImage(
sk_sp<SkImage> image, fml::WeakPtr<IOManager> io_manager,
const fml::tracing::TraceFlow& flow) {
TRACE_EVENT0("uiwidgets", __FUNCTION__);
flow.Step(__FUNCTION__);
// Should not already be a texture image because that is the entire point of
// the this method.
FML_DCHECK(!image->isTextureBacked());
if (!io_manager->GetResourceContext() || !io_manager->GetSkiaUnrefQueue()) {
FML_LOG(ERROR)
<< "Could not acquire context of release queue for texture upload.";
return {};
}
SkPixmap pixmap;
if (!image->peekPixels(&pixmap)) {
FML_LOG(ERROR) << "Could not peek pixels of image for texture upload.";
return {};
}
SkiaGPUObject<SkImage> result;
io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
fml::SyncSwitch::Handlers()
.SetIfTrue([&result, &pixmap, &image] {
SkSafeRef(image.get());
sk_sp<SkImage> texture_image = SkImage::MakeFromRaster(
pixmap,
[](const void* pixels, SkImage::ReleaseContext context) {
SkSafeUnref(static_cast<SkImage*>(context));
},
image.get());
result = {texture_image, nullptr};
})
.SetIfFalse([&result, context = io_manager->GetResourceContext(),
&pixmap, queue = io_manager->GetSkiaUnrefQueue()] {
TRACE_EVENT0("uiwidgets", "MakeCrossContextImageFromPixmap");
sk_sp<SkImage> texture_image = SkImage::MakeCrossContextFromPixmap(
context.get(), // context
pixmap, // pixmap
true, // buildMips,
true // limitToMaxTextureSize
);
if (!texture_image) {
FML_LOG(ERROR) << "Could not make x-context image.";
result = {};
} else {
result = {texture_image, queue};
}
}));
return result;
}
void ImageDecoder::Decode(ImageDescriptor descriptor,
const ImageResult& callback) {
TRACE_EVENT0("uiwidgets", __FUNCTION__);
fml::tracing::TraceFlow flow(__FUNCTION__);
FML_DCHECK(callback);
FML_DCHECK(runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
// Always service the callback on the UI thread.
auto result = [callback, ui_runner = runners_.GetUITaskRunner()](
SkiaGPUObject<SkImage> image,
fml::tracing::TraceFlow flow) {
ui_runner->PostTask(fml::MakeCopyable(
[callback, image = std::move(image), flow = std::move(flow)]() mutable {
// We are going to terminate the trace flow here. Flows cannot
// terminate without a base trace. Add one explicitly.
TRACE_EVENT0("uiwidgets", "ImageDecodeCallback");
flow.End();
callback(std::move(image));
}));
};
if (!descriptor.data || descriptor.data->size() == 0) {
result({}, std::move(flow));
return;
}
concurrent_task_runner_->PostTask(
fml::MakeCopyable([descriptor, //
io_manager = io_manager_, //
io_runner = runners_.GetIOTaskRunner(), //
result, //
flow = std::move(flow) //
]() mutable {
// Step 1: Decompress the image.
// On Worker.
auto decompressed =
descriptor.decompressed_image_info
? ImageFromDecompressedData(
std::move(descriptor.data), //
descriptor.decompressed_image_info.value(), //
descriptor.target_width, //
descriptor.target_height, //
flow //
)
: ImageFromCompressedData(std::move(descriptor.data), //
descriptor.target_width, //
descriptor.target_height, //
flow);
if (!decompressed) {
FML_LOG(ERROR) << "Could not decompress image.";
result({}, std::move(flow));
return;
}
// Step 2: Update the image to the GPU.
// On IO Thread.
io_runner->PostTask(fml::MakeCopyable([io_manager, decompressed, result,
flow =
std::move(flow)]() mutable {
if (!io_manager) {
FML_LOG(ERROR) << "Could not acquire IO manager.";
return result({}, std::move(flow));
}
// If the IO manager does not have a resource context, the caller
// might not have set one or a software backend could be in use.
// Either way, just return the image as-is.
if (!io_manager->GetResourceContext()) {
result({std::move(decompressed), io_manager->GetSkiaUnrefQueue()},
std::move(flow));
return;
}
auto uploaded =
UploadRasterImage(std::move(decompressed), io_manager, flow);
if (!uploaded.get()) {
FML_LOG(ERROR) << "Could not upload image to the GPU.";
result({}, std::move(flow));
return;
}
// Finally, all done.
result(std::move(uploaded), std::move(flow));
}));
}));
}
return fml::WeakPtr<ImageDecoder>();
return weak_factory_.GetWeakPtr();
}
} // namespace uiwidgets

55
engine/src/lib/ui/painting/image_decoder.h


#pragma once
#include <flutter/fml/concurrent_message_loop.h>
#include <flutter/fml/memory/weak_ptr.h>
#include <memory>
#include <optional>
#include "flow/skia_gpu_object.h"
#include "flutter/fml/concurrent_message_loop.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/mapping.h"
#include "flutter/fml/trace_event.h"
#include "include/core/SkData.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSize.h"
// An object that coordinates image decompression and texture upload across
// multiple threads/components in the shell. This object must be created,
// accessed and collected on the UI thread (typically the engine or its runtime
// controller). None of the expensive operations performed by this component
// occur in a frame pipeline.
class ImageDecoder {
public:
ImageDecoder(

~ImageDecoder();
struct ImageInfo {
SkImageInfo sk_info = {};
size_t row_bytes = 0;
};
struct ImageDescriptor {
sk_sp<SkData> data;
std::optional<ImageInfo> decompressed_image_info;
std::optional<uint32_t> target_width;
std::optional<uint32_t> target_height;
};
using ImageResult = std::function<void(SkiaGPUObject<SkImage>)>;
// Takes an image descriptor and returns a handle to a texture resident on the
// GPU. All image decompression and resizes are done on a worker thread
// concurrently. Texture upload is done on the IO thread and the result
// returned back on the UI thread. On error, the texture is null but the
// callback is guaranteed to return on the UI thread.
void Decode(ImageDescriptor descriptor, const ImageResult& result);
private:
TaskRunners runners_;
std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner_;
fml::WeakPtr<IOManager> io_manager_;
fml::WeakPtrFactory<ImageDecoder> weak_factory_;
FML_DISALLOW_COPY_AND_ASSIGN(ImageDecoder);
sk_sp<SkImage> ImageFromCompressedData(sk_sp<SkData> data,
std::optional<uint32_t> target_width,
std::optional<uint32_t> target_height,
const fml::tracing::TraceFlow& flow);
} // namespace uiwidgets

42
engine/src/lib/ui/painting/image_encoding.cc


namespace uiwidgets {
namespace {
void InvokeDataCallback(std::unique_ptr<EncodeImageCallback> callback,
sk_sp<SkData> buffer) {
std::shared_ptr<MonoState> mono_state = callback->mono_state.lock();
if (!mono_state) {
callback->callback(callback->callback_handle, nullptr, 0);
return;
}
MonoState::Scope scope(mono_state);
if (!buffer) {
callback->callback(callback->callback_handle, nullptr, 0);
} else {
callback->callback(callback->callback_handle, buffer->bytes(),
buffer->size());
}
}
// This must be kept in sync with the enum in painting.dart
enum ImageByteFormat {
kRawRGBA,

if (png_image == nullptr) {
FML_LOG(ERROR) << "Could not convert raster image to PNG.";
return nullptr;
};
}
return png_image;
} break;
case kRawRGBA: {

}
void EncodeImageAndInvokeDataCallback(
sk_sp<SkImage> image, EncodeImageCallback callback,
Mono_Handle callback_handle, ImageByteFormat format,
fml::RefPtr<fml::TaskRunner> ui_task_runner,
sk_sp<SkImage> image, std::unique_ptr<EncodeImageCallback> callback,
ImageByteFormat format, fml::RefPtr<fml::TaskRunner> ui_task_runner,
[callback, callback_handle](sk_sp<SkData> encoded) mutable {
callback(callback_handle, encoded->bytes(), encoded->size());
[callback = std::move(callback)](sk_sp<SkData> encoded) mutable {
InvokeDataCallback(std::move(callback), std::move(encoded));
ui_task_runner](sk_sp<SkImage> raster_image) {
ui_task_runner](sk_sp<SkImage> raster_image) mutable {
sk_sp<SkData> encoded = EncodeImage(std::move(raster_image), format);
ui_task_runner->PostTask(
[callback_task = std::move(callback_task),

} // namespace
const char* EncodeImage(CanvasImage* canvas_image, int format,
EncodeImageCallback callback,
RawEncodeImageCallback raw_callback,
if (!callback || !callback_handle) return "Callback must be a function.";
if (!raw_callback || !callback_handle) return "Callback must be a function.";
auto callback = std::make_unique<EncodeImageCallback>(EncodeImageCallback{
MonoState::Current()->GetWeakPtr(), raw_callback, callback_handle});
[callback, callback_handle, image = canvas_image->image(), image_format,
ui_task_runner = task_runners.GetUITaskRunner(),
[callback = std::move(callback), image = canvas_image->image(),
image_format, ui_task_runner = task_runners.GetUITaskRunner(),
raster_task_runner = task_runners.GetRasterTaskRunner(),
io_task_runner = task_runners.GetIOTaskRunner(),
io_manager = UIMonoState::Current()->GetIOManager(),

std::move(image), callback, callback_handle, image_format,
std::move(image), std::move(callback), image_format,
std::move(ui_task_runner), std::move(raster_task_runner),
std::move(io_task_runner), io_manager->GetResourceContext().get(),
std::move(snapshot_delegate));

14
engine/src/lib/ui/painting/image_encoding.h


#pragma once
#include "runtime/mono_api.h"
#include "runtime/mono_state.h"
typedef void (*EncodeImageCallback)(Mono_Handle callback_handle,
const uint8_t* data, size_t length);
typedef void (*RawEncodeImageCallback)(Mono_Handle callback_handle,
const uint8_t* data, size_t length);
struct EncodeImageCallback {
std::weak_ptr<MonoState> mono_state;
RawEncodeImageCallback callback;
Mono_Handle callback_handle;
};
EncodeImageCallback callback,
Mono_Handle callback_handle);
RawEncodeImageCallback callback, Mono_Handle callback_handle);
} // namespace uiwidgets

3
engine/src/lib/ui/painting/picture.cc


auto mono_state = mono_state_weak.lock();
if (!mono_state) {
// The root isolate could have died in the meantime.
raw_image_callback(callback_handle, nullptr);
MonoState::Scope scope(mono_state);
if (!raster_image) {

auto mono_image = CanvasImage::Create();
mono_image->set_image({std::move(raster_image), std::move(unref_queue)});
mono_image->AddRef();
auto* raw_mono_image = mono_image.get();
// All done!

14
engine/src/lib/ui/window/platform_message_response_mono.cc


[mono_state_weak = mono_state_weak_, callback = callback_,
handle = handle_, data = std::move(data)]() {
const std::shared_ptr<MonoState> mono_state = mono_state_weak.lock();
if (!mono_state) return;
if (!mono_state) {
callback(handle, nullptr, 0);
return;
}
callback(handle, data->GetMapping(), static_cast<int>(data->GetSize()));
}));
}

ui_task_runner_->PostTask([mono_state_weak = mono_state_weak_,
callback = callback_, handle = handle_]() {
const std::shared_ptr<MonoState> mono_state = mono_state_weak.lock();
if (!mono_state) return;
if (!mono_state) {
callback(handle, nullptr, 0);
return;
}
callback(handle, nullptr, 0);
});
}

66
engine/src/lib/ui/window/pointer_data.h


namespace uiwidgets {
static constexpr int kPointerDataFieldCount = 28;
static constexpr int kBytesPerField = sizeof(int32_t);
static constexpr int kBytesPerField = sizeof(int64_t);
enum PointerButtonMouse : int32_t {
enum PointerButtonMouse : int64_t {
kPointerButtonMousePrimary = 1 << 0,
kPointerButtonMouseSecondary = 1 << 1,
kPointerButtonMouseMiddle = 1 << 2,

enum PointerButtonTouch : int32_t {
enum PointerButtonTouch : int64_t {
enum PointerButtonStylus : int32_t {
enum PointerButtonStylus : int64_t {
struct alignas(8) PointerData {
enum class Change : int32_t {
struct alignas(4) PointerData {
enum class Change : int64_t {
kCancel,
kAdd,
kRemove,

kUp,
};
enum class DeviceKind : int32_t {
enum class DeviceKind : int64_t {
kTouch,
kMouse,
kStylus,

enum class SignalKind : int32_t {
enum class SignalKind : int64_t {
int32_t time_stamp;
int64_t time_stamp;
int32_t device;
int32_t pointer_identifier;
float physical_x;
float physical_y;
float physical_delta_x;
float physical_delta_y;
int32_t buttons;
int32_t obscured;
int32_t synthesized;
float pressure;
float pressure_min;
float pressure_max;
float distance;
float distance_max;
float size;
float radius_major;
float radius_minor;
float radius_min;
float radius_max;
float orientation;
float tilt;
int32_t platformData;
float scroll_delta_x;
float scroll_delta_y;
int64_t device;
int64_t pointer_identifier;
double physical_x;
double physical_y;
double physical_delta_x;
double physical_delta_y;
int64_t buttons;
int64_t obscured;
int64_t synthesized;
double pressure;
double pressure_min;
double pressure_max;
double distance;
double distance_max;
double size;
double radius_major;
double radius_minor;
double radius_min;
double radius_max;
double orientation;
double tilt;
int64_t platformData;
double scroll_delta_x;
double scroll_delta_y;
void Clear();
};

17
engine/src/lib/ui/window/window.cc


int response_id);
Window_dispatchPlatformMessageCallback Window_dispatchPlatformMessage_;
typedef void (*Window_dispatchPointerDataPacketCallback)(const uint8_t* data,
int data_length);
Window_dispatchPointerDataPacketCallback Window_dispatchPointerDataPacket_;
UIWIDGETS_API(void)
Window_hook(
Window_constructorCallback Window_constructor,

Window_drawFrameCallback Window_drawFrame,
Window_dispatchPlatformMessageCallback Window_dispatchPlatformMessage) {
Window_dispatchPlatformMessageCallback Window_dispatchPlatformMessage,
Window_dispatchPointerDataPacketCallback Window_dispatchPointerDataPacket) {
Window_constructor_ = Window_constructor;
Window_dispose_ = Window_dispose;
Window_updateWindowMetrics_ = Window_updateWindowMetrics;

Window_dispatchPointerDataPacket_ = Window_dispatchPointerDataPacket;
}
UIWIDGETS_API(Mono_Handle) Window_instance() {

response_id);
}
void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) {}
void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) {
std::shared_ptr<MonoState> mono_state = mono_state_.lock();
if (!mono_state) return;
MonoState::Scope scope(mono_state);
const auto& buffer = packet.data();
Window_dispatchPointerDataPacket_(buffer.data(), buffer.size());
}
void Window::BeginFrame(fml::TimePoint frameTime) {
std::shared_ptr<MonoState> mono_state = mono_state_.lock();

41
engine/src/render_api_d3d11.cc


#include "platform_base.h"
#include "render_api.h"
#include "shell/platform/unity/uiwidgets_system.h"
// Direct3D 11 implementation of RenderAPI.

#include <vector>
#include "TestLoadICU.h"
#include "Unity/IUnityGraphicsD3D11.h"
#include "flutter/fml/message_loop.h"
#include "txt/paragraph.h"

#include "src/gpu/gl/GrGLDefines.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"
#include "third_party/icu/SkLoadICU.h"
#include "TestLoadICU.h"
void Dart_TimelineEvent(const char* label, int64_t timestamp0,
int64_t timestamp1_or_async_id,
Dart_Timeline_Event_Type type, intptr_t argument_count,
const char** argument_names,
const char** argument_values) {}
class RenderAPI_D3D11 : public RenderAPI {
public:

txt::ParagraphStyle style;
txt::FontCollection tf;
txt::TextStyle ts;
//style.
// style.
std::unique_ptr<txt::ParagraphBuilder> pb = txt::ParagraphBuilder::CreateTxtBuilder(style, _fontCollection);
std::unique_ptr<txt::ParagraphBuilder> pb =
txt::ParagraphBuilder::CreateTxtBuilder(style, _fontCollection);
pb->PushStyle(ts);
std::u16string s16 = u"Hello, some text.你好!";
pb->AddText(s16);

// canvas->drawColor(SK_ColorWHITE);
testParagraph->Paint(canvas, 10, 200);
// canvas->drawColor(SK_ColorWHITE);
testParagraph->Paint(canvas, 10, 200);
}
void draw1(SkCanvas* canvas) {

}
double t = 0;
/* canvas->clear(SK_ColorWHITE);

animation_->seekFrameTime(t);
animation_->render(canvas);*/
//SkRect rect = SkRect::MakeLTRB(100, 100, 200, 200);
//canvas->drawImageRect(image_, rect, nullptr);
// SkRect rect = SkRect::MakeLTRB(100, 100, 200, 200);
// canvas->drawImageRect(image_, rect, nullptr);
// mutex_skia->ReleaseSync(0);
canvas->flush();

using namespace txt;
std::shared_ptr<txt::FontCollection> GetTestFontCollection() {
std::shared_ptr<txt::FontCollection> collection =
std::make_shared<txt::FontCollection>();
collection->SetupDefaultFontManager();
return collection;
std::shared_ptr<txt::FontCollection> collection =
std::make_shared<txt::FontCollection>();
collection->SetupDefaultFontManager();
return collection;
}
#endif // #if SUPPORT_D3D11

60
engine/src/runtime/mono_api.cc


#include "mono_isolate.h"
#include "mono_state.h"
#include "shell/platform/unity/uiwidgets_system.h"
namespace uiwidgets {

const Mono_Isolate current = *ptr;
FML_DCHECK(current != nullptr);
Mono_Shutdown(current);
*ptr = nullptr;
delete current;

static Mono_ThrowExceptionCallback Mono_ThrowExceptionCallback_;
typedef void (*Mono_ShutdownCallback)(Mono_Isolate isolate);
static Mono_ShutdownCallback Mono_ShutdownCallback_;
void Mono_Shutdown(Mono_Isolate isolate) { Mono_ShutdownCallback_(isolate); }
int64_t Mono_TimelineGetMicros() {
return fml::TimePoint::Now().ToEpochDelta().ToMicroseconds();

UIWIDGETS_API(void)
Mono_hook(Mono_ThrowExceptionCallback throwException) {
Mono_hook(Mono_ThrowExceptionCallback throwException,
Mono_ShutdownCallback shutdown) {
Mono_ShutdownCallback_ = shutdown;
}
UIWIDGETS_API(Mono_Isolate)

extern "C" int64_t Dart_TimelineGetMicros() {
return uiwidgets::Mono_TimelineGetMicros();
}
inline const char* TimelineEventToString(Dart_Timeline_Event_Type type) {
switch (type) {
case Dart_Timeline_Event_Begin:
return "Begin";
case Dart_Timeline_Event_End:
return "End";
case Dart_Timeline_Event_Instant:
return "Instant";
case Dart_Timeline_Event_Async_Begin:
return "AsyncBegin";
case Dart_Timeline_Event_Async_End:
return "AsyncEnd";
case Dart_Timeline_Event_Async_Instant:
return "AsyncInstant";
case Dart_Timeline_Event_Counter:
return "Counter";
case Dart_Timeline_Event_Flow_Begin:
return "FlowBegin";
case Dart_Timeline_Event_Flow_Step:
return "FlowStep";
case Dart_Timeline_Event_Flow_End:
return "FlowEnd";
default:
return "";
}
}
extern "C" void Dart_TimelineEvent(const char* label, int64_t timestamp0,
int64_t timestamp1_or_async_id,
Dart_Timeline_Event_Type type,
intptr_t argument_count,
const char** argument_names,
const char** argument_values) {
static int64_t timestamp_begin = timestamp0;
if (timestamp1_or_async_id) {
uiwidgets::UIWidgetsSystem::GetInstancePtr()->printf_console(
"uiwidgets Timeline [Thread:%d] [%lld ms] [%lld] [%s]: %s\n",
GetCurrentThreadId(), (timestamp0 - timestamp_begin) / 1000,
timestamp1_or_async_id, TimelineEventToString(type), label);
} else {
uiwidgets::UIWidgetsSystem::GetInstancePtr()->printf_console(
"uiwidgets Timeline [Thread:%d] [%d ms] [%s]: %s\n",
GetCurrentThreadId(), (timestamp0 - timestamp_begin) / 1000,
TimelineEventToString(type), label);
}
}

1
engine/src/runtime/mono_api.h


void* Mono_CurrentIsolateData();
void Mono_ThrowException(const char* exception);
void Mono_Shutdown(Mono_Isolate isolate);
int64_t Mono_TimelineGetMicros();
void Mono_NotifyIdle(int64_t deadline);

2
engine/src/runtime/mono_isolate.cc


FML_DCHECK(Mono_CurrentIsolate() == nullptr);
}
return true;
return true;
}
MonoIsolate::AutoFireClosure::AutoFireClosure(const fml::closure& closure)

36
engine/src/shell/common/shell.cc


//#include "shell/common/skia_event_tracer_impl.h"
//#include "shell/common/switches.h"
#include "shell/common/vsync_waiter.h"
#include "shell/platform/embedder/embedder_task_runner.h"
namespace uiwidgets {

fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(),
fml::MakeCopyable([engine = std::move(engine_), &ui_latch]() mutable {
engine.reset();
ui_latch.Signal();
}));
fml::MakeCopyable(
[engine = std::move(engine_), &ui_latch,
task_runner = task_runners_.GetUITaskRunner()]() mutable {
engine.reset();
ui_latch.Signal();
// assume it's always EmbedderTaskRunner
static_cast<EmbedderTaskRunner*>(task_runner.get())->Terminate();
}));
fml::MakeCopyable([rasterizer = std::move(rasterizer_),
weak_factory_gpu = std::move(weak_factory_gpu_),
&gpu_latch]() mutable {
rasterizer.reset();
weak_factory_gpu.reset();
gpu_latch.Signal();
}));
fml::MakeCopyable(
[rasterizer = std::move(rasterizer_),
weak_factory_gpu = std::move(weak_factory_gpu_), &gpu_latch,
task_runner = task_runners_.GetRasterTaskRunner()]() mutable {
rasterizer.reset();
weak_factory_gpu.reset();
gpu_latch.Signal();
// assume it's always EmbedderTaskRunner
static_cast<EmbedderTaskRunner*>(task_runner.get())->Terminate();
}));
gpu_latch.Wait();
fml::TaskRunner::RunNowOrPostTask(

if (rasterizer) {
rasterizer->Teardown();
}
// Step 2: Next, tell the IO thread to complete its remaining work.
fml::TaskRunner::RunNowOrPostTask(io_task_runner, io_task);
};

task_runners_.GetPlatformTaskRunner();
auto ui_task = [engine = engine_->GetWeakPtr(),
ui_task_runner = task_runners_.GetUITaskRunner(),
// Step 1: Next, tell the raster thread that its rasterizer should suspend
// access to the underlying surface.
if (should_post_raster_task) {

// surface is about to go away.
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task);
latch.Wait();
if (!should_post_raster_task) {
// See comment on should_post_raster_task, in this case the raster_task
// wasn't executed, and we just run it here as the platform thread

16
engine/src/shell/platform/embedder/embedder_task_runner.cc


{
// Release the lock before the jump via the dispatch table.
std::scoped_lock lock(tasks_mutex_);
if (terminated_) {
return;
}
baton = ++last_baton_;
pending_tasks_[baton] = task;
}

FML_DCHECK(task);
task();
return true;
}
void EmbedderTaskRunner::Terminate() {
FML_DCHECK(RunsTasksOnCurrentThread());
std::unordered_map<uint64_t, fml::closure> local_tasks;
{
std::scoped_lock lock(tasks_mutex_);
terminated_ = true;
pending_tasks_.swap(local_tasks);
}
}
// |fml::TaskRunner|

3
engine/src/shell/platform/embedder/embedder_task_runner.h


bool PostTask(uint64_t baton);
void Terminate();
private:
const size_t embedder_identifier_;
DispatchTable dispatch_table_;

fml::TaskQueueId placeholder_id_;
bool terminated_ = false;
void PostTask(const fml::closure& task) override;

185
engine/src/shell/platform/unity/uiwidgets_panel.cc


#include "shell/platform/embedder/embedder_engine.h"
#include "shell/common/switches.h"
#include "uiwidgets_system.h"
#include "unity_external_texture_gl.h"
namespace uiwidgets {

UIWidgetsEngineRunInitialized(engine);
UIWidgetsSystem::GetInstancePtr()->RegisterPanel(this);
process_events_ = true;
}
void UIWidgetsPanel::MonoEntrypoint() { entrypoint_callback_(handle_); }

// drain pending vsync batons
ProcessVSync();
process_events_ = false;
UIWidgetsSystem::GetInstancePtr()->UnregisterPanel(this);

reinterpret_cast<EmbedderEngine*>(engine_)->SetViewportMetrics(metrics);
}
int UIWidgetsPanel::RegisterTexture(void* native_texture_ptr) {
int texture_identifier = 0;
texture_identifier++;
auto* engine = reinterpret_cast<EmbedderEngine*>(engine_);
engine->GetShell().GetPlatformView()->RegisterTexture(
std::make_unique<UnityExternalTextureGL>(
texture_identifier, native_texture_ptr, surface_manager_.get()));
return texture_identifier;
}
void UIWidgetsPanel::UnregisterTexture(int texture_id) {
auto* engine = reinterpret_cast<EmbedderEngine*>(engine_);
engine->GetShell().GetPlatformView()->UnregisterTexture(texture_id);
}
std::chrono::nanoseconds UIWidgetsPanel::ProcessMessages() {
return std::chrono::nanoseconds(task_runner_->ProcessTasks().count());
}

vsync_batons_.push_back(baton);
}
using TimePoint = std::chrono::steady_clock::time_point;
void UIWidgetsPanel::SetEventPhaseFromCursorButtonState(
UIWidgetsPointerEvent* event_data) {
MouseState state = GetMouseState();
event_data->phase = state.buttons == 0
? state.state_is_down ? UIWidgetsPointerPhase::kUp
: UIWidgetsPointerPhase::kHover
: state.state_is_down ? UIWidgetsPointerPhase::kMove
: UIWidgetsPointerPhase::kDown;
}
void UIWidgetsPanel::SendMouseMove(float x, float y) {
UIWidgetsPointerEvent event = {};
event.x = x;
event.y = y;
SetEventPhaseFromCursorButtonState(&event);
SendPointerEventWithData(event);
}
void UIWidgetsPanel::SendMouseDown(float x, float y) {
UIWidgetsPointerEvent event = {};
SetEventPhaseFromCursorButtonState(&event);
event.x = x;
event.y = y;
SendPointerEventWithData(event);
SetMouseStateDown(true);
}
void UIWidgetsPanel::SendMouseUp(float x, float y) {
UIWidgetsPointerEvent event = {};
SetEventPhaseFromCursorButtonState(&event);
event.x = x;
event.y = y;
SendPointerEventWithData(event);
if (event.phase == UIWidgetsPointerPhase::kUp) {
SetMouseStateDown(false);
}
}
void UIWidgetsPanel::SendMouseLeave() {
UIWidgetsPointerEvent event = {};
event.phase = UIWidgetsPointerPhase::kRemove;
SendPointerEventWithData(event);
}
void UIWidgetsPanel::SendPointerEventWithData(
const UIWidgetsPointerEvent& event_data) {
MouseState mouse_state = GetMouseState();
// If sending anything other than an add, and the pointer isn't already added,
// synthesize an add to satisfy Flutter's expectations about events.
if (!mouse_state.state_is_added &&
event_data.phase != UIWidgetsPointerPhase::kAdd) {
UIWidgetsPointerEvent event = {};
event.phase = UIWidgetsPointerPhase::kAdd;
event.x = event_data.x;
event.y = event_data.y;
event.buttons = 0;
SendPointerEventWithData(event);
}
// Don't double-add (e.g., if events are delivered out of order, so an add has
// already been synthesized).
if (mouse_state.state_is_added &&
event_data.phase == UIWidgetsPointerPhase::kAdd) {
return;
}
UIWidgetsPointerEvent event = event_data;
event.device_kind = kUIWidgetsPointerDeviceKindMouse;
event.buttons = mouse_state.buttons;
// Set metadata that's always the same regardless of the event.
event.struct_size = sizeof(event);
event.timestamp =
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
UIWidgetsEngineSendPointerEvent(engine_, &event, 1);
if (event_data.phase == UIWidgetsPointerPhase::kAdd) {
SetMouseStateAdded(true);
} else if (event_data.phase == UIWidgetsPointerPhase::kRemove) {
SetMouseStateAdded(false);
ResetMouseState();
}
}
TimePoint next_flutter_event_time = TimePoint::clock::now();
void UIWidgetsPanel::OnMouseMove(float x, float y) {
if (process_events_) {
SendMouseMove(x, y);
}
}
static uint64_t ConvertToUIWidgetsButton(int button) {
switch (button) {
case -1:
return kUIWidgetsPointerButtonMousePrimary;
case -2:
return kUIWidgetsPointerButtonMouseSecondary;
case -3:
return kUIWidgetsPointerButtonMouseMiddle;
}
std::cerr << "Mouse button not recognized: " << button << std::endl;
return 0;
}
void UIWidgetsPanel::OnMouseDown(float x, float y, int button) {
if (process_events_) {
uint64_t uiwidgets_button = ConvertToUIWidgetsButton(button);
if (uiwidgets_button != 0) {
uint64_t mouse_buttons = GetMouseState().buttons | uiwidgets_button;
SetMouseButtons(mouse_buttons);
SendMouseDown(x, y);
}
}
}
void UIWidgetsPanel::OnMouseUp(float x, float y, int button) {
if (process_events_) {
uint64_t uiwidgets_button = ConvertToUIWidgetsButton(button);
if (uiwidgets_button != 0) {
uint64_t mouse_buttons = GetMouseState().buttons & ~uiwidgets_button;
SetMouseButtons(mouse_buttons);
SendMouseUp(x, y);
}
}
}
void UIWidgetsPanel::OnMouseLeave() {
if (process_events_) {
SendMouseLeave();
}
}
UIWIDGETS_API(UIWidgetsPanel*)
UIWidgetsPanel_constructor(

int width, int height, float dpi) {
panel->OnRenderTexture(native_texture_ptr, width, height, dpi);
}
UIWIDGETS_API(int)
UIWidgetsPanel_registerTexture(UIWidgetsPanel* panel,
void* native_texture_ptr) {
return panel->RegisterTexture(native_texture_ptr);
}
UIWIDGETS_API(void)
UIWidgetsPanel_unregisterTexture(UIWidgetsPanel* panel, int texture_id) {
panel->UnregisterTexture(texture_id);
}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseDown(UIWidgetsPanel* panel, float x, float y,
int button) {
panel->OnMouseDown(x, y, button);
}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseUp(UIWidgetsPanel* panel, float x, float y, int button) {
panel->OnMouseUp(x, y, button);
}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseMove(UIWidgetsPanel* panel, float x, float y) {
panel->OnMouseMove(x, y);
}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseLeave(UIWidgetsPanel* panel) { panel->OnMouseLeave(); }
} // namespace uiwidgets

45
engine/src/shell/platform/unity/uiwidgets_panel.h


namespace uiwidgets {
struct MouseState {
bool state_is_down = false;
bool state_is_added = false;
uint64_t buttons = 0;
};
class UIWidgetsPanel : public fml::RefCountedThreadSafe<UIWidgetsPanel> {
FML_FRIEND_MAKE_REF_COUNTED(UIWidgetsPanel);

void OnRenderTexture(void* native_texture_ptr, size_t width, size_t height,
float dpi);
int RegisterTexture(void* native_texture_ptr);
void UnregisterTexture(int texture_id);
std::chrono::nanoseconds ProcessMessages();
void ProcessVSync();

void OnMouseMove(float x, float y);
void OnMouseDown(float x, float y, int button);
void OnMouseUp(float x, float y, int button);
void OnMouseLeave();
MouseState GetMouseState() { return mouse_state_; }
void ResetMouseState() { mouse_state_ = MouseState(); }
void SetMouseStateDown(bool is_down) { mouse_state_.state_is_down = is_down; }
void SetMouseStateAdded(bool is_added) {
mouse_state_.state_is_added = is_added;
}
void SetMouseButtons(uint64_t buttons) { mouse_state_.buttons = buttons; }
void SendMouseMove(float x, float y);
void SendMouseDown(float x, float y);
void SendMouseUp(float x, float y);
void SendMouseLeave();
void SetEventPhaseFromCursorButtonState(UIWidgetsPointerEvent* event_data);
void SendPointerEventWithData(const UIWidgetsPointerEvent& event_data);
Mono_Handle handle_;
EntrypointCallback entrypoint_callback_;

UIWidgetsEngine engine_ = nullptr;
std::vector<intptr_t> vsync_batons_;
MouseState mouse_state_;
bool process_events_ = false;
};
} // namespace uiwidgets

7
engine/src/shell/platform/unity/uiwidgets_system.h


#include <flutter/fml/closure.h>
#include <chrono>
#include <cstdarg>
#include <set>
#include <unordered_map>

void UnregisterPanel(UIWidgetsPanel* panel);
void PostTaskToGfxWorker(const fml::closure& task);
void printf_console(const char* log, ...) {
va_list vl;
va_start(vl, log);
unity_uiwidgets_->printf_consolev(log, vl);
va_end(vl);
}
void BindUnityInterfaces(IUnityInterfaces* unity_interfaces);
void UnBindUnityInterfaces();

6
engine/src/shell/platform/unity/unity_surface_manager.h


void DestroyRenderSurface();
bool ClearCurrent();
EGLDisplay GetEGLDisplay() const { return egl_display_; }
ID3D11Device* GetD3D11Device() const { return d3d11_device_; }
FML_DISALLOW_COPY_AND_ASSIGN(UnitySurfaceManager);

3
engine/third_party/Unity/IUnityUIWidgets.h


#pragma once
#include "IUnityInterface.h"
#include "IUnityInterface.h"
namespace UnityUIWidgets {
typedef void (*VoidCallback)();

virtual void SetWakeUpCallback(VoidCallback callback) = 0;
virtual void IssuePluginEventAndData(UnityRenderingEventAndData callback,
int eventId, void* data) = 0;
virtual void printf_consolev(const char* log, va_list alist) = 0;
};
} // namespace UnityUIWidgets

198
engine/src/lib/ui/painting/codec.cc


#include "codec.h"
#include <variant>
#include "common/task_runners.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/trace_event.h"
#include "frame_info.h"
#include "include/codec/SkCodec.h"
#include "include/core/SkPixelRef.h"
#include "multi_frame_codec.h"
#include "single_frame_codec.h"
#if OS_ANDROID
#include <sys/mman.h>
#endif
namespace uiwidgets {
namespace {
// This must be kept in sync with the enum in painting.dart
enum PixelFormat {
kRGBA8888,
kBGRA8888,
};
#if OS_ANDROID
// Compressed image buffers are allocated on the UI thread but are deleted on a
// decoder worker thread. Android's implementation of malloc appears to
// continue growing the native heap size when the allocating thread is
// different from the freeing thread. To work around this, create an SkData
// backed by an anonymous mapping.
sk_sp<SkData> MakeSkDataWithCopy(const void* data, size_t length) {
if (length == 0) {
return SkData::MakeEmpty();
}
size_t mapping_length = length + sizeof(size_t);
void* mapping = ::mmap(nullptr, mapping_length, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (mapping == MAP_FAILED) {
return SkData::MakeEmpty();
}
*reinterpret_cast<size_t*>(mapping) = mapping_length;
void* mapping_data = reinterpret_cast<char*>(mapping) + sizeof(size_t);
::memcpy(mapping_data, data, length);
SkData::ReleaseProc proc = [](const void* ptr, void* context) {
size_t* size_ptr = reinterpret_cast<size_t*>(context);
FML_DCHECK(ptr == size_ptr + 1);
if (::munmap(const_cast<void*>(context), *size_ptr) == -1) {
FML_LOG(ERROR) << "munmap of codec SkData failed";
}
};
return SkData::MakeWithProc(mapping_data, length, proc, mapping);
}
#else
sk_sp<SkData> MakeSkDataWithCopy(const void* data, size_t length) {
return SkData::MakeWithCopy(data, length);
}
#endif // OS_ANDROID
} // anonymous namespace
static std::variant<ImageDecoder::ImageInfo, std::string> ConvertImageInfo(
Codec::_ImageInfo _image_info) {
PixelFormat pixel_format = static_cast<PixelFormat>(_image_info.format);
SkColorType color_type = kUnknown_SkColorType;
switch (pixel_format) {
case kRGBA8888:
color_type = kRGBA_8888_SkColorType;
break;
case kBGRA8888:
color_type = kBGRA_8888_SkColorType;
break;
}
if (color_type == kUnknown_SkColorType) {
return "Invalid pixel format";
}
int width = _image_info.width;
if (width <= 0) {
return "width must be greater than zero";
}
int height = _image_info.height;
if (height <= 0) {
return "height must be greater than zero";
}
ImageDecoder::ImageInfo image_info;
image_info.sk_info =
SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType);
image_info.row_bytes = _image_info.rowBytes;
if (image_info.row_bytes < image_info.sk_info.minRowBytes()) {
return "rowBytes does not match the width of the image";
}
return image_info;
}
UIWIDGETS_API(const char*)
Codec_instantiateImageCodec(uint8_t* data, int data_length,
Codec::InstantiateImageCodecCallback callback,
Mono_Handle callback_handle,
Codec::_ImageInfo _image_info, bool has_image_info,
int target_width, int target_height) {
if (!callback || !callback_handle) {
return "Callback must be a function";
}
std::optional<ImageDecoder::ImageInfo> image_info;
if (has_image_info) {
auto image_info_results = ConvertImageInfo(_image_info);
if (auto value =
std::get_if<ImageDecoder::ImageInfo>(&image_info_results)) {
image_info = *value;
} else if (auto error = std::get_if<std::string>(&image_info_results)) {
return error->c_str();
}
}
sk_sp<SkData> buffer = MakeSkDataWithCopy(data, data_length);
if (image_info) {
const auto expected_size =
image_info->row_bytes * image_info->sk_info.height();
if (buffer->size() < expected_size) {
return "Pixel buffer size does not match image size";
}
}
const int targetWidth = target_width;
const int targetHeight = target_height;
std::unique_ptr<SkCodec> codec;
bool single_frame;
if (image_info) {
single_frame = true;
} else {
codec = SkCodec::MakeFromData(buffer);
if (!codec) {
return "Could not instantiate image codec.";
}
single_frame = codec->getFrameCount() == 1;
}
fml::RefPtr<Codec> ui_codec;
if (single_frame) {
ImageDecoder::ImageDescriptor descriptor;
descriptor.decompressed_image_info = image_info;
if (targetWidth > 0) {
descriptor.target_width = targetWidth;
}
if (targetHeight > 0) {
descriptor.target_height = targetHeight;
}
descriptor.data = std::move(buffer);
ui_codec = fml::MakeRefCounted<SingleFrameCodec>(std::move(descriptor));
} else {
ui_codec = fml::MakeRefCounted<MultiFrameCodec>(std::move(codec));
}
ui_codec->AddRef();
callback(callback_handle, ui_codec.get());
return nullptr;
}
void Codec::dispose() {}
UIWIDGETS_API(void) Codec_dispose(Codec* ptr) { ptr->Release(); }
UIWIDGETS_API(int) Codec_frameCount(Codec* ptr) { return ptr->frameCount(); }
UIWIDGETS_API(int) Codec_repetitionCount(Codec* ptr) {
return ptr->repetitionCount();
}
UIWIDGETS_API(const char*)
Codec_getNextFrame(Codec* ptr, Codec::GetNextFrameCallback callback,
Mono_Handle callback_handle) {
return ptr->getNextFrame(callback, callback_handle);
}
} // namespace uiwidgets

46
engine/src/lib/ui/painting/codec.h


#pragma once
#include "frame_info.h"
#include "include/codec/SkCodec.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkImage.h"
#include "runtime/mono_state.h"
namespace uiwidgets {
class Codec : public fml::RefCountedThreadSafe<Codec> {
public:
virtual ~Codec() {}
virtual int frameCount() const = 0;
virtual int repetitionCount() const = 0;
typedef void (*InstantiateImageCodecCallback)(Mono_Handle callback_handle,
Codec* codec);
typedef void (*GetNextFrameCallback)(Mono_Handle callback_handle,
FrameInfo* frame_info);
virtual const char* getNextFrame(GetNextFrameCallback callback,
Mono_Handle callback_handle) = 0;
virtual size_t GetAllocationSize() { return 0; }
void dispose();
struct _ImageInfo {
int width;
int height;
int format;
int rowBytes;
};
struct PendingCallback {
std::weak_ptr<MonoState> mono_state;
GetNextFrameCallback callback;
Mono_Handle callback_handle;
};
};
} // namespace uiwidgets

22
engine/src/lib/ui/painting/frame_info.cc


#include "frame_info.h"
namespace uiwidgets {
FrameInfo::FrameInfo(fml::RefPtr<CanvasImage> image, int durationMillis)
: image_(std::move(image)), durationMillis_(durationMillis) {}
FrameInfo::~FrameInfo() {}
UIWIDGETS_API(void) FrameInfo_dispose(FrameInfo* ptr) { ptr->Release(); }
UIWIDGETS_API(int) FrameInfo_durationMillis(FrameInfo* ptr) {
return ptr->durationMillis();
}
UIWIDGETS_API(CanvasImage*) FrameInfo_image(FrameInfo* ptr) {
auto image = ptr->image();
image->AddRef();
return image.get();
}
} // namespace uiwidgets

25
engine/src/lib/ui/painting/frame_info.h


#pragma once
#include "image.h"
namespace uiwidgets {
// A single animation frame.
class FrameInfo final : public fml::RefCountedThreadSafe<FrameInfo> {
public:
int durationMillis() { return durationMillis_; }
fml::RefPtr<CanvasImage> image() { return image_; }
private:
FrameInfo(fml::RefPtr<CanvasImage> image, int durationMillis);
~FrameInfo();
const fml::RefPtr<CanvasImage> image_;
const int durationMillis_;
FML_FRIEND_MAKE_REF_COUNTED(FrameInfo);
FML_FRIEND_REF_COUNTED_THREAD_SAFE(FrameInfo);
};
} // namespace uiwidgets

194
engine/src/lib/ui/painting/multi_frame_codec.cc


#include "multi_frame_codec.h"
#include "flutter/fml/make_copyable.h"
#include "include/core/SkPixelRef.h"
#include "lib/ui/ui_mono_state.h"
namespace uiwidgets {
MultiFrameCodec::MultiFrameCodec(std::unique_ptr<SkCodec> codec)
: state_(new State(std::move(codec))) {}
MultiFrameCodec::~MultiFrameCodec() = default;
MultiFrameCodec::State::State(std::unique_ptr<SkCodec> codec)
: codec_(std::move(codec)),
frameCount_(codec_->getFrameCount()),
repetitionCount_(codec_->getRepetitionCount()),
nextFrameIndex_(0) {}
static void InvokeNextFrameCallback(
fml::RefPtr<FrameInfo> frameInfo,
std::unique_ptr<Codec::PendingCallback> callback, size_t trace_id) {
std::shared_ptr<MonoState> mono_state = callback->mono_state.lock();
if (!mono_state) {
FML_DLOG(ERROR) << "Could not acquire Mono state while attempting to fire "
"next frame callback.";
callback->callback(callback->callback_handle, nullptr);
return;
}
MonoState::Scope scope(mono_state);
if (!frameInfo) {
callback->callback(callback->callback_handle, nullptr);
} else {
frameInfo->AddRef();
callback->callback(callback->callback_handle, frameInfo.get());
}
}
// Copied the source bitmap to the destination. If this cannot occur due to
// running out of memory or the image info not being compatible, returns false.
static bool CopyToBitmap(SkBitmap* dst, SkColorType dstColorType,
const SkBitmap& src) {
SkPixmap srcPM;
if (!src.peekPixels(&srcPM)) {
return false;
}
SkBitmap tmpDst;
SkImageInfo dstInfo = srcPM.info().makeColorType(dstColorType);
if (!tmpDst.setInfo(dstInfo)) {
return false;
}
if (!tmpDst.tryAllocPixels()) {
return false;
}
SkPixmap dstPM;
if (!tmpDst.peekPixels(&dstPM)) {
return false;
}
if (!srcPM.readPixels(dstPM)) {
return false;
}
dst->swap(tmpDst);
return true;
}
sk_sp<SkImage> MultiFrameCodec::State::GetNextFrameImage(
fml::WeakPtr<GrContext> resourceContext) {
SkBitmap bitmap = SkBitmap();
SkImageInfo info = codec_->getInfo().makeColorType(kN32_SkColorType);
if (info.alphaType() == kUnpremul_SkAlphaType) {
info = info.makeAlphaType(kPremul_SkAlphaType);
}
bitmap.allocPixels(info);
SkCodec::Options options;
options.fFrameIndex = nextFrameIndex_;
SkCodec::FrameInfo frameInfo;
codec_->getFrameInfo(nextFrameIndex_, &frameInfo);
const int requiredFrameIndex = frameInfo.fRequiredFrame;
if (requiredFrameIndex != SkCodec::kNoFrame) {
if (lastRequiredFrame_ == nullptr) {
FML_LOG(ERROR) << "Frame " << nextFrameIndex_ << " depends on frame "
<< requiredFrameIndex
<< " and no required frames are cached.";
return nullptr;
} else if (lastRequiredFrameIndex_ != requiredFrameIndex) {
FML_DLOG(INFO) << "Required frame " << requiredFrameIndex
<< " is not cached. Using " << lastRequiredFrameIndex_
<< " instead";
}
if (lastRequiredFrame_->getPixels() &&
CopyToBitmap(&bitmap, lastRequiredFrame_->colorType(),
*lastRequiredFrame_)) {
options.fPriorFrame = requiredFrameIndex;
}
}
if (SkCodec::kSuccess != codec_->getPixels(info, bitmap.getPixels(),
bitmap.rowBytes(), &options)) {
FML_LOG(ERROR) << "Could not getPixels for frame " << nextFrameIndex_;
return nullptr;
}
// Hold onto this if we need it to decode future frames.
if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kKeep) {
lastRequiredFrame_ = std::make_unique<SkBitmap>(bitmap);
lastRequiredFrameIndex_ = nextFrameIndex_;
}
if (resourceContext) {
SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(),
bitmap.pixelRef()->rowBytes());
return SkImage::MakeCrossContextFromPixmap(resourceContext.get(), pixmap,
true);
} else {
// Defer decoding until time of draw later on the raster thread. Can happen
// when GL operations are currently forbidden such as in the background
// on iOS.
return SkImage::MakeFromBitmap(bitmap);
}
}
void MultiFrameCodec::State::GetNextFrameAndInvokeCallback(
std::unique_ptr<PendingCallback> callback,
fml::RefPtr<fml::TaskRunner> ui_task_runner,
fml::WeakPtr<GrContext> resourceContext,
fml::RefPtr<SkiaUnrefQueue> unref_queue, size_t trace_id) {
fml::RefPtr<FrameInfo> frameInfo = NULL;
sk_sp<SkImage> skImage = GetNextFrameImage(resourceContext);
if (skImage) {
fml::RefPtr<CanvasImage> image = CanvasImage::Create();
image->set_image({skImage, std::move(unref_queue)});
SkCodec::FrameInfo skFrameInfo;
codec_->getFrameInfo(nextFrameIndex_, &skFrameInfo);
frameInfo =
fml::MakeRefCounted<FrameInfo>(std::move(image), skFrameInfo.fDuration);
}
nextFrameIndex_ = (nextFrameIndex_ + 1) % frameCount_;
ui_task_runner->PostTask(fml::MakeCopyable(
[callback = std::move(callback), frameInfo, trace_id]() mutable {
InvokeNextFrameCallback(frameInfo, std::move(callback), trace_id);
}));
}
const char* MultiFrameCodec::getNextFrame(GetNextFrameCallback callback,
Mono_Handle callback_handle) {
static size_t trace_counter = 1;
const size_t trace_id = trace_counter++;
if (!callback || !callback_handle) {
return "Callback must be a function";
}
auto* mono_state = UIMonoState::Current();
const auto& task_runners = mono_state->GetTaskRunners();
task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable(
[callback = std::make_unique<PendingCallback>(PendingCallback{
MonoState::Current()->GetWeakPtr(), callback, callback_handle}),
weak_state = std::weak_ptr<MultiFrameCodec::State>(state_), trace_id,
ui_task_runner = task_runners.GetUITaskRunner(),
io_manager = mono_state->GetIOManager()]() mutable {
auto state = weak_state.lock();
if (!state) {
ui_task_runner->PostTask(
fml::MakeCopyable([callback = std::move(callback)]() {
callback->callback(callback->callback_handle, nullptr);
}));
return;
}
state->GetNextFrameAndInvokeCallback(
std::move(callback), std::move(ui_task_runner),
io_manager->GetResourceContext(), io_manager->GetSkiaUnrefQueue(),
trace_id);
}));
return nullptr;
}
int MultiFrameCodec::frameCount() const { return state_->frameCount_; }
int MultiFrameCodec::repetitionCount() const {
return state_->repetitionCount_;
}
} // namespace uiwidgets

69
engine/src/lib/ui/painting/multi_frame_codec.h


#pragma once
#include "codec.h"
#include "flutter/fml/macros.h"
#include "runtime/mono_state.h"
namespace uiwidgets {
class MultiFrameCodec : public Codec {
public:
MultiFrameCodec(std::unique_ptr<SkCodec> codec);
~MultiFrameCodec() override;
// |Codec|
int frameCount() const override;
// |Codec|
int repetitionCount() const override;
// |Codec|
const char* getNextFrame(GetNextFrameCallback callback,
Mono_Handle callback_handle) override;
private:
// Captures the state shared between the IO and UI task runners.
//
// The state is initialized on the UI task runner when the Dart object is
// created. Decoding occurs on the IO task runner. Since it is possible for
// the UI object to be collected independently of the IO task runner work,
// it is not safe for this state to live directly on the MultiFrameCodec.
// Instead, the MultiFrameCodec creates this object when it is constructed,
// shares it with the IO task runner's decoding work, and sets the live_
// member to false when it is destructed.
struct State {
State(std::unique_ptr<SkCodec> codec);
const std::unique_ptr<SkCodec> codec_;
const int frameCount_;
const int repetitionCount_;
// The non-const members and functions below here are only read or written
// to on the IO thread. They are not safe to access or write on the UI
// thread.
int nextFrameIndex_;
// The last decoded frame that's required to decode any subsequent frames.
std::unique_ptr<SkBitmap> lastRequiredFrame_;
// The index of the last decoded required frame.
int lastRequiredFrameIndex_ = -1;
sk_sp<SkImage> GetNextFrameImage(fml::WeakPtr<GrContext> resourceContext);
void GetNextFrameAndInvokeCallback(
std::unique_ptr<PendingCallback> callback,
fml::RefPtr<fml::TaskRunner> ui_task_runner,
fml::WeakPtr<GrContext> resourceContext,
fml::RefPtr<SkiaUnrefQueue> unref_queue, size_t trace_id);
};
// Shared across the UI and IO task runners.
std::shared_ptr<State> state_;
FML_FRIEND_MAKE_REF_COUNTED(MultiFrameCodec);
FML_FRIEND_REF_COUNTED_THREAD_SAFE(MultiFrameCodec);
};
} // namespace uiwidgets

110
engine/src/lib/ui/painting/single_frame_codec.cc


#include "single_frame_codec.h"
#include "frame_info.h"
#include "lib/ui/ui_mono_state.h"
namespace uiwidgets {
SingleFrameCodec::SingleFrameCodec(ImageDecoder::ImageDescriptor descriptor)
: status_(Status::kNew), descriptor_(std::move(descriptor)) {}
SingleFrameCodec::~SingleFrameCodec() = default;
int SingleFrameCodec::frameCount() const { return 1; }
int SingleFrameCodec::repetitionCount() const { return 0; }
const char* SingleFrameCodec::getNextFrame(GetNextFrameCallback callback,
Mono_Handle callback_handle) {
if (!callback || !callback_handle) {
return "Callback must be a function";
}
if (status_ == Status::kComplete) {
cached_frame_->AddRef();
callback(callback_handle, cached_frame_.get());
return nullptr;
}
// This has to be valid because this method is called from Dart.
auto* mono_state = UIMonoState::Current();
pending_callbacks_.emplace_back(PendingCallback{mono_state->GetWeakPtr(), callback, callback_handle});
if (status_ == Status::kInProgress) {
// Another call to getNextFrame is in progress and will invoke the
// pending callbacks when decoding completes.
return nullptr;
}
auto decoder = mono_state->GetImageDecoder();
if (!decoder) {
return "Image decoder not available.";
}
// The SingleFrameCodec must be deleted on the UI thread. Allocate a RefPtr
// on the heap to ensure that the SingleFrameCodec remains alive until the
// decoder callback is invoked on the UI thread. The callback can then
// drop the reference.
fml::RefPtr<SingleFrameCodec>* raw_codec_ref =
new fml::RefPtr<SingleFrameCodec>(this);
decoder->Decode(descriptor_, [raw_codec_ref](auto image) {
std::unique_ptr<fml::RefPtr<SingleFrameCodec>> codec_ref(raw_codec_ref);
fml::RefPtr<SingleFrameCodec> codec(std::move(*codec_ref));
auto state = codec->pending_callbacks_.front().mono_state.lock();
if (!state) {
// This is probably because the isolate has been terminated before the
// image could be decoded.
for (const auto& entry : codec->pending_callbacks_) {
entry.callback(entry.callback_handle, nullptr);
}
codec->pending_callbacks_.clear();
return;
}
MonoState::Scope scope(state.get());
if (image.get()) {
auto canvas_image = fml::MakeRefCounted<CanvasImage>();
canvas_image->set_image(std::move(image));
codec->cached_frame_ = fml::MakeRefCounted<FrameInfo>(
std::move(canvas_image), 0 /* duration */);
}
// The cached frame is now available and should be returned to any future
// callers.
codec->status_ = Status::kComplete;
// Invoke any callbacks that were provided before the frame was decoded.
for (const auto& entry : codec->pending_callbacks_) {
codec->cached_frame_->AddRef();
entry.callback(entry.callback_handle, codec->cached_frame_.get());
}
codec->pending_callbacks_.clear();
});
// The encoded data is no longer needed now that it has been handed off
// to the decoder.
descriptor_.data.reset();
status_ = Status::kInProgress;
return nullptr;
}
size_t SingleFrameCodec::GetAllocationSize() {
const auto& data = descriptor_.data;
const auto data_byte_size = data ? data->size() : 0;
const auto frame_byte_size = (cached_frame_ && cached_frame_->image())
? cached_frame_->image()->GetAllocationSize()
: 0;
return data_byte_size + frame_byte_size + sizeof(this);
}
} // namespace uiwidgets

40
engine/src/lib/ui/painting/single_frame_codec.h


#pragma once
#include "codec.h"
#include "flutter/fml/macros.h"
#include "frame_info.h"
#include "image_decoder.h"
namespace uiwidgets {
class SingleFrameCodec : public Codec {
public:
SingleFrameCodec(ImageDecoder::ImageDescriptor descriptor);
~SingleFrameCodec() override;
// |Codec|
int frameCount() const override;
// |Codec|
int repetitionCount() const override;
// |Codec|
const char* getNextFrame(GetNextFrameCallback callback,
Mono_Handle callback_handle) override;
size_t GetAllocationSize() override;
private:
enum class Status { kNew, kInProgress, kComplete };
Status status_;
ImageDecoder::ImageDescriptor descriptor_;
fml::RefPtr<FrameInfo> cached_frame_;
std::vector<PendingCallback> pending_callbacks_;
FML_FRIEND_MAKE_REF_COUNTED(SingleFrameCodec);
FML_FRIEND_REF_COUNTED_THREAD_SAFE(SingleFrameCodec);
};
} // namespace uiwidgets

119
engine/src/shell/platform/unity/unity_external_texture_gl.cc


#include "unity_external_texture_gl.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <EGL/eglext_angle.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <d3d11.h>
#include "flutter/fml/logging.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrContext.h"
#include "include/gpu/gl/GrGLTypes.h"
#include "src/gpu/gl/GrGLDefines.h"
#include "uiwidgets_system.h"
namespace uiwidgets {
UnityExternalTextureGL::UnityExternalTextureGL(
int64_t texture_identifier, void* native_texture_ptr,
UnitySurfaceManager* unity_surface_manager)
: Texture(texture_identifier),
unity_surface_manager_(unity_surface_manager) {
auto* graphics = UIWidgetsSystem::GetInstancePtr()
->GetUnityInterfaces()
->Get<IUnityGraphics>();
FML_DCHECK(graphics->GetRenderer() == kUnityGfxRendererD3D11);
auto* src_d3d11_texture = static_cast<ID3D11Texture2D*>(native_texture_ptr);
IDXGIResource* src_d3d11_resource;
HRESULT hr = src_d3d11_texture->QueryInterface(
__uuidof(IDXGIResource), reinterpret_cast<void**>(&src_d3d11_resource));
FML_CHECK(SUCCEEDED(hr));
HANDLE shared_image_handle;
hr = src_d3d11_resource->GetSharedHandle(&shared_image_handle);
FML_CHECK(SUCCEEDED(hr));
src_d3d11_resource->Release();
IDXGIResource* d3d11_resource;
unity_surface_manager_->GetD3D11Device()->OpenSharedResource(
shared_image_handle, __uuidof(ID3D11Resource),
reinterpret_cast<void**>(&d3d11_resource));
d3d11_resource->QueryInterface(__uuidof(ID3D11Texture2D),
reinterpret_cast<void**>(&d3d11_texture_));
d3d11_resource->Release();
const EGLint attribs[] = {EGL_NONE};
egl_image_ =
eglCreateImageKHR(unity_surface_manager_->GetEGLDisplay(), EGL_NO_CONTEXT,
EGL_D3D11_TEXTURE_ANGLE,
static_cast<EGLClientBuffer>(d3d11_texture_), attribs);
gl_texture_ = 0;
}
UnityExternalTextureGL::~UnityExternalTextureGL() {
last_image_ = nullptr;
if (gl_texture_) {
glDeleteTextures(1, &gl_texture_);
gl_texture_ = 0;
}
eglDestroyImageKHR(unity_surface_manager_->GetEGLDisplay(), egl_image_);
d3d11_texture_->Release();
}
// |flutter::Texture|
void UnityExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds,
bool freeze, GrContext* context) {
if (!last_image_) {
if (!gl_texture_) {
glGenTextures(1, &gl_texture_);
glBindTexture(GL_TEXTURE_2D, gl_texture_);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
}
GrGLTextureInfo texture_info;
texture_info.fTarget = GR_GL_TEXTURE_2D;
texture_info.fID = gl_texture_;
texture_info.fFormat = GR_GL_RGBA8;
GrBackendTexture backend_tex = GrBackendTexture(
bounds.width(), bounds.height(), GrMipMapped::kNo, texture_info);
last_image_ = SkImage::MakeFromTexture(
context, backend_tex, kBottomLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kOpaque_SkAlphaType, nullptr);
}
if (last_image_) {
if (bounds != SkRect::Make(last_image_->bounds())) {
canvas.drawImageRect(last_image_, bounds, nullptr);
} else {
canvas.drawImage(last_image_, bounds.x(), bounds.y());
}
}
}
// |flutter::Texture|
void UnityExternalTextureGL::OnGrContextCreated() {}
// |flutter::Texture|
void UnityExternalTextureGL::OnGrContextDestroyed() {}
// |flutter::Texture|
void UnityExternalTextureGL::MarkNewFrameAvailable() {}
// |flutter::Texture|
void UnityExternalTextureGL::OnTextureUnregistered() {}
} // namespace uiwidgets

46
engine/src/shell/platform/unity/unity_external_texture_gl.h


#pragma once
#include "flow/texture.h"
#include "flutter/fml/macros.h"
#include "include/core/SkImage.h"
#include "include/core/SkSize.h"
#include "unity_surface_manager.h"
namespace uiwidgets {
class UnityExternalTextureGL : public Texture {
public:
UnityExternalTextureGL(int64_t texture_identifier, void* native_texture_ptr,
UnitySurfaceManager* unity_surface_manager);
~UnityExternalTextureGL() override;
private:
UnitySurfaceManager* unity_surface_manager_;
bool gr_context_created_ = false;
ID3D11Texture2D* d3d11_texture_;
EGLImage egl_image_;
GLuint gl_texture_;
sk_sp<SkImage> last_image_;
// |flutter::Texture|
void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze,
GrContext* context) override;
// |flutter::Texture|
void OnGrContextCreated() override;
// |flutter::Texture|
void OnGrContextDestroyed() override;
// |flutter::Texture|
void MarkNewFrameAvailable() override;
// |flutter::Texture|
void OnTextureUnregistered() override;
FML_DISALLOW_COPY_AND_ASSIGN(UnityExternalTextureGL);
};
} // namespace uiwidgets
正在加载...
取消
保存