This commit is contained in:
Kevin Gu 2020-09-21 16:20:45 +08:00
Родитель d2992d5f9d
Коммит f5232e4046
31 изменённых файлов: 1065 добавлений и 216 удалений

Просмотреть файл

@ -17,19 +17,19 @@ namespace Unity.UIWidgets.async2 {
}
public static readonly FutureOr nil = value(null);
public static implicit operator FutureOr(Future f) => future(f);
public static implicit operator FutureOr(bool v) => value(v);
public static implicit operator FutureOr(int v) => value(v);
public static implicit operator FutureOr(long v) => value(v);
public static implicit operator FutureOr(float v) => value(v);
public static implicit operator FutureOr(double v) => value(v);
public static implicit operator FutureOr(string v) => value(v);
public static implicit operator FutureOr(byte[] v) => value(v);
@ -309,10 +309,31 @@ namespace Unity.UIWidgets.async2 {
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 abstract Future catchError(Func<Exception, FutureOr> onError, Func<Exception, bool> test = null);
public Future catchError(Action<Exception> onError, Func<Exception, bool> test = null) {
return catchError(e => {
onError(e);
return FutureOr.nil;
}, test);
}
public abstract Future whenComplete(Func<FutureOr> action);
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);
@ -337,10 +358,18 @@ namespace Unity.UIWidgets.async2 {
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) {
return _future.then(obj => onValue((T) obj), onError).to<R>();
}
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) {
return _future.catchError(onError, test);
}
@ -375,7 +404,7 @@ namespace Unity.UIWidgets.async2 {
public static Completer sync() => new _SyncCompleter();
public abstract Future future { get; }
public abstract void complete(FutureOr value = default);
public abstract void completeError(Exception error);

Просмотреть файл

@ -87,8 +87,7 @@ namespace Unity.UIWidgets.engine2 {
_handle.Free();
_handle = default;
if (isolate != null)
Isolate.remove(isolate);
D.assert(!isolate.isValid);
base.OnDisable();
}

Просмотреть файл

@ -13,7 +13,7 @@ namespace Unity.UIWidgets.services {
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}");
@ -82,7 +82,7 @@ namespace Unity.UIWidgets.services {
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})";
@ -107,7 +107,7 @@ namespace Unity.UIWidgets.services {
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) {
@ -143,7 +143,7 @@ namespace Unity.UIWidgets.services {
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;

Просмотреть файл

@ -2,7 +2,7 @@
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;
@ -16,39 +16,39 @@ namespace Unity.UIWidgets.ui2 {
Scene_dispose(ptr);
}
public Promise<Image> toImage(int width, int height) {
public Future<Image> toImage(int width, int height) {
if (width <= 0 || height <= 0) {
throw new Exception("Invalid image dimensions.");
}
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;
});
}
[MonoPInvokeCallback(typeof(Scene_toImageCallback))]
static void _toImageCallback(IntPtr callbackHandle, IntPtr result) {
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;
}
try {
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);

Просмотреть файл

@ -15,7 +15,8 @@ namespace Unity.UIWidgets.ui2 {
#endif
static unsafe void hook() {
Mono_hook(
Mono_throwException);
Mono_throwException,
Mono_shutdown);
Window_hook(
Window_constructor,
@ -33,8 +34,20 @@ namespace Unity.UIWidgets.ui2 {
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);
}
}
[DllImport(NativeBindings.dllName)]
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);

Просмотреть файл

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.UIWidgets.foundation;
using UnityEngine;
namespace Unity.UIWidgets.ui2 {
public class Isolate {
@ -13,6 +14,28 @@ namespace Unity.UIWidgets.ui2 {
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 {
@ -22,7 +45,7 @@ namespace Unity.UIWidgets.ui2 {
D.assert(value.isValid);
return value;
}
var isolate = new Isolate(ptr);
_isolates.Add(ptr, isolate);
return isolate;
@ -40,9 +63,28 @@ namespace Unity.UIWidgets.ui2 {
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;
}

Просмотреть файл

@ -8,57 +8,60 @@ namespace Unity.UIWidgets.ui2 {
#else
internal const string dllName = "libUIWidgets_d";
#endif
}
public abstract class NativeWrapper {
protected internal IntPtr _ptr { get; protected set; }
protected internal IntPtr _ptr { get; private set; }
protected NativeWrapper() {
}
Isolate _isolate;
protected NativeWrapper(IntPtr ptr) {
_setPtr(ptr);
}
protected void _setPtr(IntPtr ptr) {
D.assert(ptr != IntPtr.Zero);
_ptr = ptr;
_isolate = Isolate.current;
_isolate.addNativeWrapper(this);
}
internal void _dispose(bool finalizer = false) {
if (_ptr != IntPtr.Zero) {
D.assert(_isolate.isValid);
_isolate.removeNativeWrapper(_ptr);
DisposePtr(_ptr);
_ptr = IntPtr.Zero;
if (!finalizer) {
GC.SuppressFinalize(this);
}
}
}
~NativeWrapper() {
if (_ptr != IntPtr.Zero) {
DisposePtr(_ptr);
_ptr = IntPtr.Zero;
}
_dispose(true);
}
protected abstract void DisposePtr(IntPtr ptr);
}
public abstract class NativeWrapperDisposable : IDisposable {
protected internal IntPtr _ptr { get; protected set; }
public abstract class NativeWrapperDisposable : NativeWrapper, IDisposable {
protected NativeWrapperDisposable() {
}
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);
public void Dispose() {
if (_ptr != IntPtr.Zero) {
DisposePtr(_ptr);
_ptr = IntPtr.Zero;
}
GC.SuppressFinalize(this);
_dispose();
}
}
}

Просмотреть файл

@ -4,7 +4,6 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using AOT;
using RSG;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using UnityEngine;
@ -392,6 +391,10 @@ namespace Unity.UIWidgets.ui2 {
antiAliasWithSaveLayer,
}
public static partial class ui_ {
const int _kDoNotResizeDimension = -1;
}
public class Paint {
internal readonly byte[] _data = new byte[_kDataByteCount];
@ -730,57 +733,74 @@ namespace Unity.UIWidgets.ui2 {
png,
}
enum PixelFormat {
public enum PixelFormat {
rgba8888,
bgra8888,
}
/**
* 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;
}
public class Image : NativeWrapperDisposable {
internal Image(IntPtr ptr) : base(ptr) {
}
protected override void DisposePtr(IntPtr ptr) {
Image_dispose(_ptr);
Image_dispose(ptr);
}
public int width => Image_width(_ptr);
public int height => Image_height(_ptr);
public Promise<byte[]> toByteData(
public Future<byte[]> toByteData(
ImageByteFormat format = ImageByteFormat.rawRgba
) {
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;
});
}
[MonoPInvokeCallback(typeof(Image_toByteDataCallback))]
static void _toByteDataCallback(IntPtr callbackHandle, IntPtr data, int length) {
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;
}
try {
if (data == IntPtr.Zero || length == 0) {
completer.Resolve(new byte[0]);
callback(new byte[0]);
}
else {
var bytes = new byte[length];
Marshal.Copy(data, bytes, 0, length);
completer.Resolve(bytes);
callback(bytes);
}
}
catch (Exception ex) {
@ -809,6 +829,171 @@ namespace Unity.UIWidgets.ui2 {
/*
* 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,
@ -827,11 +1012,11 @@ namespace Unity.UIWidgets.ui2 {
}
protected override void DisposePtr(IntPtr ptr) {
Layer_dispose(ptr);
EngineLayer_dispose(ptr);
}
[DllImport(NativeBindings.dllName)]
static extern void Layer_dispose(IntPtr ptr);
static extern void EngineLayer_dispose(IntPtr ptr);
}
public class Path : NativeWrapper {
@ -1902,9 +2087,9 @@ namespace Unity.UIWidgets.ui2 {
}
cullRect = cullRect ?? Rect.largest;
_ptr = Canvas_constructor(recorder._ptr, cullRect.left, cullRect.top,
_setPtr(Canvas_constructor(recorder._ptr, cullRect.left, cullRect.top,
cullRect.right,
cullRect.bottom);
cullRect.bottom));
}
protected override void DisposePtr(IntPtr ptr) {
@ -2272,137 +2457,137 @@ namespace Unity.UIWidgets.ui2 {
}
[DllImport(NativeBindings.dllName)]
public static extern IntPtr Canvas_constructor(IntPtr recorder,
static extern IntPtr Canvas_constructor(IntPtr recorder,
float left,
float top,
float right,
float bottom);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_dispose(IntPtr ptr);
static extern void Canvas_dispose(IntPtr ptr);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_save(IntPtr ptr);
static extern void Canvas_save(IntPtr ptr);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void
static extern unsafe void
Canvas_saveLayerWithoutBounds(IntPtr ptr, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_saveLayer(IntPtr ptr,
static extern unsafe void Canvas_saveLayer(IntPtr ptr,
float left, float top, float right, float bottom, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_restore(IntPtr ptr);
static extern void Canvas_restore(IntPtr ptr);
[DllImport(NativeBindings.dllName)]
public static extern int Canvas_getSaveCount(IntPtr ptr);
static extern int Canvas_getSaveCount(IntPtr ptr);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_translate(IntPtr ptr,
static extern void Canvas_translate(IntPtr ptr,
float x, float y);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_scale(IntPtr ptr,
static extern void Canvas_scale(IntPtr ptr,
float sx, float sy);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_rotate(IntPtr ptr,
static extern void Canvas_rotate(IntPtr ptr,
float radians);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_skew(IntPtr ptr,
static extern void Canvas_skew(IntPtr ptr,
float sx, float sy);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_transform(IntPtr ptr,
static extern unsafe void Canvas_transform(IntPtr ptr,
float* matrix4);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_clipRect(IntPtr ptr,
static extern void Canvas_clipRect(IntPtr ptr,
float left, float top, float right, float bottom, int clipOp, bool doAntiAlias);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_clipRRect(IntPtr ptr,
static extern unsafe void Canvas_clipRRect(IntPtr ptr,
float* rrect, bool doAntiAlias);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_clipPath(IntPtr ptr,
static extern void Canvas_clipPath(IntPtr ptr,
IntPtr path, bool doAntiAlias);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_drawColor(IntPtr ptr,
static extern void Canvas_drawColor(IntPtr ptr,
uint color, int blendMode);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawLine(IntPtr ptr,
static extern unsafe void Canvas_drawLine(IntPtr ptr,
float x1, float y1, float x2, float y2, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawPaint(IntPtr ptr,
static extern unsafe void Canvas_drawPaint(IntPtr ptr,
IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawRect(IntPtr ptr,
static extern unsafe void Canvas_drawRect(IntPtr ptr,
float left, float top, float right, float bottom, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawRRect(IntPtr ptr,
static extern unsafe void Canvas_drawRRect(IntPtr ptr,
float* rrect, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawDRRect(IntPtr ptr,
static extern unsafe void Canvas_drawDRRect(IntPtr ptr,
float* outer, float* inner, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawOval(IntPtr ptr,
static extern unsafe void Canvas_drawOval(IntPtr ptr,
float left, float top, float right, float bottom, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawCircle(IntPtr ptr,
static extern unsafe void Canvas_drawCircle(IntPtr ptr,
float x, float y, float radius, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawArc(IntPtr ptr,
static extern unsafe void Canvas_drawArc(IntPtr ptr,
float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawPath(IntPtr ptr,
static extern unsafe void Canvas_drawPath(IntPtr ptr,
IntPtr path, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawImage(IntPtr ptr,
static extern unsafe void Canvas_drawImage(IntPtr ptr,
IntPtr image, float x, float y, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawImageRect(IntPtr ptr,
static extern unsafe void Canvas_drawImageRect(IntPtr ptr,
IntPtr image, float srcLeft, float srcTop, float srcRight, float srcBottom,
float dstLeft, float dstTop, float dstRight, float dstBottom, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawImageNine(IntPtr ptr,
static extern unsafe void Canvas_drawImageNine(IntPtr ptr,
IntPtr image, float centerLeft, float centerTop, float centerRight, float centerBottom,
float dstLeft, float dstTop, float dstRight, float dstBottom, IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
public static extern void Canvas_drawPicture(IntPtr ptr, IntPtr picture);
static extern void Canvas_drawPicture(IntPtr ptr, IntPtr picture);
[DllImport(NativeBindings.dllName)]
public static extern unsafe void Canvas_drawPoints(IntPtr ptr,
static extern unsafe void Canvas_drawPoints(IntPtr ptr,
IntPtr* paintObject, byte* paintData, int pointMode, float* points, int pointsLength);
[DllImport(NativeBindings.dllName)]
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,
IntPtr* paintObject, byte* paintData);
[DllImport(NativeBindings.dllName)]
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,
IntPtr atlas, float* rstTransforms, int rstTransformsLength,
float* rects, int rectsLength, uint* colors, int colorsLength, int blendMode, float* cullRect);
[DllImport(NativeBindings.dllName)]
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);
}
@ -2414,7 +2599,7 @@ namespace Unity.UIWidgets.ui2 {
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.");
}
@ -2441,6 +2626,10 @@ namespace Unity.UIWidgets.ui2 {
var callback = (_Callback<Image>) handle.Target;
handle.Free();
if (!Isolate.checkExists()) {
return;
}
try {
callback(result == IntPtr.Zero ? null : new Image(result));
}
@ -2500,7 +2689,7 @@ namespace Unity.UIWidgets.ui2 {
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) {
@ -2512,7 +2701,7 @@ namespace Unity.UIWidgets.ui2 {
});
if (error != null)
throw new Exception(error);
return completer.future;
return completer.future.to<T>();
}
}
}

Просмотреть файл

@ -235,6 +235,10 @@ namespace Unity.UIWidgets.ui2 {
var callback = (PlatformMessageResponseCallback) handle.Target;
handle.Free();
if (!Isolate.checkExists()) {
return;
}
byte[] bytes = null;
if (data != null && dataLength != 0) {
bytes = new byte[dataLength];

Просмотреть файл

@ -122,10 +122,14 @@ class Build
"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/color_filter.cc",
"src/lib/ui/painting/color_filter.h",
"src/lib/ui/painting/engine_layer.cc",
"src/lib/ui/painting/engine_layer.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",
@ -140,6 +144,8 @@ class Build
"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",
@ -152,6 +158,8 @@ class Build
"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",

Просмотреть файл

@ -13,11 +13,7 @@ SkiaUnrefQueue::SkiaUnrefQueue(fml::RefPtr<fml::TaskRunner> task_runner,
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_);
@ -29,7 +25,7 @@ void SkiaUnrefQueue::Unref(SkRefCnt* object) {
}
}
void SkiaUnrefQueue::Drain(bool performDeferredCleanup) {
void SkiaUnrefQueue::Drain() {
TRACE_EVENT0("uiwidgets", "SkiaUnrefQueue::Drain");
std::deque<SkRefCnt*> skia_objects;
{
@ -42,10 +38,8 @@ void SkiaUnrefQueue::Drain(bool performDeferredCleanup) {
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));
}
}

Просмотреть файл

@ -22,7 +22,7 @@ class SkiaUnrefQueue : public fml::RefCountedThreadSafe<SkiaUnrefQueue> {
// 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_;

Просмотреть файл

@ -207,14 +207,18 @@ UIWIDGETS_API(SceneBuilder*) SceneBuilder_constructor() {
UIWIDGETS_API(void) SceneBuilder_dispose(SceneBuilder* ptr) { ptr->Release(); }
UIWIDGETS_API(void)
UIWIDGETS_API(EngineLayer*)
SceneBuilder_pushTransform(SceneBuilder* ptr, const float* matrix4) {
ptr->pushTransform(matrix4);
const auto layer = ptr->pushTransform(matrix4);
layer->AddRef();
return layer.get();
}
UIWIDGETS_API(void)
UIWIDGETS_API(EngineLayer*)
SceneBuilder_pushOffset(SceneBuilder* ptr, float dx, float dy) {
ptr->pushOffset(dx, dy);
const auto layer = ptr->pushOffset(dx, dy);
layer->AddRef();
return layer.get();
}
UIWIDGETS_API(void)

Просмотреть файл

@ -1,5 +1,7 @@
#include "engine_layer.h"
#include "runtime/mono_api.h"
namespace uiwidgets {
EngineLayer::EngineLayer(std::shared_ptr<ContainerLayer> layer)
@ -7,6 +9,8 @@ EngineLayer::EngineLayer(std::shared_ptr<ContainerLayer> layer)
EngineLayer::~EngineLayer() = default;
size_t EngineLayer::GetAllocationSize() { return 3000; };
size_t EngineLayer::GetAllocationSize() { return 3000; }
UIWIDGETS_API(void) EngineLayer_dispose(EngineLayer* ptr) { ptr->Release(); }
} // namespace uiwidgets

Просмотреть файл

@ -10,7 +10,7 @@ CanvasImage::CanvasImage() = default;
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);
}
@ -28,4 +28,17 @@ size_t CanvasImage::GetAllocationSize() {
}
}
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

Просмотреть файл

@ -19,7 +19,7 @@ class CanvasImage final : public fml::RefCountedThreadSafe<CanvasImage> {
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();

Просмотреть файл

@ -7,12 +7,385 @@
#include "src/codec/SkCodecImageGenerator.h"
namespace uiwidgets {
namespace {
constexpr double kAspectRatioChangedThreshold = 0.01;
} // namespace
ImageDecoder::ImageDecoder(
TaskRunners runners,
std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
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));
}));
}));
}
fml::WeakPtr<ImageDecoder> ImageDecoder::GetWeakPtr() const {
return fml::WeakPtr<ImageDecoder>();
return weak_factory_.GetWeakPtr();
}
} // namespace uiwidgets

Просмотреть файл

@ -1,13 +1,28 @@
#pragma once
#include <flutter/fml/concurrent_message_loop.h>
#include <flutter/fml/memory/weak_ptr.h>
#include <memory>
#include <optional>
#include "common/task_runners.h"
#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"
#include "lib/ui/io_manager.h"
namespace uiwidgets {
// 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(
@ -15,7 +30,43 @@ class ImageDecoder {
std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
fml::WeakPtr<IOManager> io_manager);
~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);
fml::WeakPtr<ImageDecoder> GetWeakPtr() const;
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

Просмотреть файл

@ -16,6 +16,22 @@
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,
@ -161,7 +177,7 @@ sk_sp<SkData> EncodeImage(sk_sp<SkImage> raster_image, ImageByteFormat format) {
if (png_image == nullptr) {
FML_LOG(ERROR) << "Could not convert raster image to PNG.";
return nullptr;
};
}
return png_image;
} break;
case kRawRGBA: {
@ -177,19 +193,18 @@ sk_sp<SkData> EncodeImage(sk_sp<SkImage> raster_image, ImageByteFormat format) {
}
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,
fml::RefPtr<fml::TaskRunner> raster_task_runner,
fml::RefPtr<fml::TaskRunner> io_task_runner, GrContext* resource_context,
fml::WeakPtr<SnapshotDelegate> snapshot_delegate) {
auto callback_task = fml::MakeCopyable(
[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));
});
auto encode_task = [callback_task = std::move(callback_task), format,
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),
@ -203,26 +218,29 @@ void EncodeImageAndInvokeDataCallback(
} // namespace
const char* EncodeImage(CanvasImage* canvas_image, int format,
EncodeImageCallback callback,
RawEncodeImageCallback raw_callback,
Mono_Handle callback_handle) {
if (!canvas_image) return "encode called with non-genuine Image.";
if (!callback || !callback_handle) return "Callback must be a function.";
if (!raw_callback || !callback_handle) return "Callback must be a function.";
ImageByteFormat image_format = static_cast<ImageByteFormat>(format);
auto callback = std::make_unique<EncodeImageCallback>(EncodeImageCallback{
MonoState::Current()->GetWeakPtr(), raw_callback, callback_handle});
const auto& task_runners = UIMonoState::Current()->GetTaskRunners();
task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable(
[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(),
snapshot_delegate =
UIMonoState::Current()->GetSnapshotDelegate()]() mutable {
EncodeImageAndInvokeDataCallback(
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));

Просмотреть файл

@ -1,16 +1,22 @@
#pragma once
#include "runtime/mono_api.h"
#include "runtime/mono_state.h"
namespace uiwidgets {
class CanvasImage;
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;
};
const char* EncodeImage(CanvasImage* canvas_image, int format,
EncodeImageCallback callback,
Mono_Handle callback_handle);
RawEncodeImageCallback callback, Mono_Handle callback_handle);
} // namespace uiwidgets

Просмотреть файл

@ -71,8 +71,10 @@ const char* Picture::RasterizeToImage(sk_sp<SkPicture> picture, uint32_t width,
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);
return;
}
MonoState::Scope scope(mono_state);
if (!raster_image) {
@ -82,6 +84,7 @@ const char* Picture::RasterizeToImage(sk_sp<SkPicture> picture, uint32_t width,
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!

Просмотреть файл

@ -26,9 +26,12 @@ void PlatformMessageResponseMono::Complete(std::unique_ptr<fml::Mapping> data) {
[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;
}
MonoState::Scope scope(mono_state);
callback(handle, data->GetMapping(), static_cast<int>(data->GetSize()));
}));
}
@ -40,9 +43,12 @@ void PlatformMessageResponseMono::CompleteEmpty() {
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;
}
MonoState::Scope scope(mono_state);
callback(handle, nullptr, 0);
});
}

Просмотреть файл

@ -2,6 +2,7 @@
#include "platform_base.h"
#include "render_api.h"
#include "shell/platform/unity/uiwidgets_system.h"
// Direct3D 11 implementation of RenderAPI.
@ -17,13 +18,13 @@
#include <vector>
#include "TestLoadICU.h"
#include "Unity/IUnityGraphicsD3D11.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/third_party/txt/src/txt/font_collection.h"
#include "flutter/third_party/txt/src/txt/paragraph.h"
#include "flutter/third_party/txt/src/txt/paragraph_builder.h"
#include "flutter/third_party/txt/src/txt/font_collection.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkSurface.h"
#include "include/core/SkTextBlob.h"
@ -35,14 +36,9 @@
#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"
static std::shared_ptr<txt::FontCollection> _fontCollection;
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:
@ -416,26 +412,26 @@ void RenderAPI_D3D11::draw(SkCanvas* canvas) {
txt::ParagraphStyle style;
txt::FontCollection tf;
txt::TextStyle ts;
//style.
// style.
style.font_family = "Arial";
ts.font_size = 28;
ts.height = 4.0;
std::unique_ptr<txt::ParagraphBuilder> pb = txt::ParagraphBuilder::CreateTxtBuilder(style, _fontCollection);
std::unique_ptr<txt::ParagraphBuilder> pb =
txt::ParagraphBuilder::CreateTxtBuilder(style, _fontCollection);
ts.font_families.clear();
ts.font_families.push_back("Arial");
ts.color = SK_ColorBLACK;
pb->PushStyle(ts);
std::u16string s16 = u"Hello, some text.你好!";
pb->AddText(s16);
testParagraph = pb->Build();
testParagraph->Layout(500);
}
// canvas->drawColor(SK_ColorWHITE);
testParagraph->Paint(canvas, 10, 200);
// canvas->drawColor(SK_ColorWHITE);
testParagraph->Paint(canvas, 10, 200);
}
void draw1(SkCanvas* canvas) {
@ -448,18 +444,12 @@ void draw1(SkCanvas* canvas) {
double t = 0;
void RenderAPI_D3D11::Draw() {
// mutex_skia->AcquireSync(0, 5000);
SkCanvas* canvas = m_SkSurface->getCanvas();
draw(canvas);
/* canvas->clear(SK_ColorWHITE);
t += 1.0 / 60;
@ -470,8 +460,8 @@ void RenderAPI_D3D11::Draw() {
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();
@ -491,10 +481,10 @@ void RenderAPI_D3D11::PostDraw() {
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

Просмотреть файл

@ -5,6 +5,7 @@
#include "mono_isolate.h"
#include "mono_state.h"
#include "shell/platform/unity/uiwidgets_system.h"
namespace uiwidgets {
@ -50,6 +51,7 @@ void Mono_ShutdownIsolate() {
const Mono_Isolate current = *ptr;
FML_DCHECK(current != nullptr);
Mono_Shutdown(current);
*ptr = nullptr;
delete current;
@ -68,10 +70,16 @@ typedef void (*Mono_ThrowExceptionCallback)(const char* exception);
static Mono_ThrowExceptionCallback Mono_ThrowExceptionCallback_;
typedef void (*Mono_ShutdownCallback)(Mono_Isolate isolate);
static Mono_ShutdownCallback Mono_ShutdownCallback_;
void Mono_ThrowException(const char* exception) {
Mono_ThrowExceptionCallback_(exception);
}
void Mono_Shutdown(Mono_Isolate isolate) { Mono_ShutdownCallback_(isolate); }
int64_t Mono_TimelineGetMicros() {
return fml::TimePoint::Now().ToEpochDelta().ToMicroseconds();
}
@ -79,8 +87,10 @@ int64_t Mono_TimelineGetMicros() {
void Mono_NotifyIdle(int64_t deadline) {}
UIWIDGETS_API(void)
Mono_hook(Mono_ThrowExceptionCallback throwException) {
Mono_hook(Mono_ThrowExceptionCallback throwException,
Mono_ShutdownCallback shutdown) {
Mono_ThrowExceptionCallback_ = throwException;
Mono_ShutdownCallback_ = shutdown;
}
UIWIDGETS_API(Mono_Isolate)
@ -97,3 +107,51 @@ Isolate_exit() { Mono_ExitIsolate(); }
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);
}
}

Просмотреть файл

@ -36,6 +36,7 @@ void* Mono_IsolateData(Mono_Isolate isolate);
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);

Просмотреть файл

@ -87,7 +87,7 @@ bool MonoIsolate::Shutdown() {
FML_DCHECK(Mono_CurrentIsolate() == nullptr);
}
return true;
return true;
}
MonoIsolate::AutoFireClosure::AutoFireClosure(const fml::closure& closure)

Просмотреть файл

@ -26,6 +26,7 @@
//#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 {
@ -303,21 +304,30 @@ Shell::~Shell() {
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();
}));
ui_latch.Wait();
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetRasterTaskRunner(),
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(
@ -599,6 +609,7 @@ void Shell::OnPlatformViewDestroyed() {
if (rasterizer) {
rasterizer->Teardown();
}
// Step 2: Next, tell the IO thread to complete its remaining work.
fml::TaskRunner::RunNowOrPostTask(io_task_runner, io_task);
};
@ -617,11 +628,13 @@ void Shell::OnPlatformViewDestroyed() {
task_runners_.GetPlatformTaskRunner();
auto ui_task = [engine = engine_->GetWeakPtr(),
ui_task_runner = task_runners_.GetUITaskRunner(),
raster_task_runner = task_runners_.GetRasterTaskRunner(),
raster_task, should_post_raster_task, &latch]() {
if (engine) {
engine->OnOutputSurfaceDestroyed();
}
// Step 1: Next, tell the raster thread that its rasterizer should suspend
// access to the underlying surface.
if (should_post_raster_task) {
@ -637,6 +650,7 @@ void Shell::OnPlatformViewDestroyed() {
// 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

Просмотреть файл

@ -37,6 +37,10 @@ void EmbedderTaskRunner::PostTaskForTime(const fml::closure& task,
{
// 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;
}
@ -74,6 +78,18 @@ bool EmbedderTaskRunner::PostTask(uint64_t baton) {
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|
fml::TaskQueueId EmbedderTaskRunner::GetTaskQueueId() {
return placeholder_id_;

Просмотреть файл

@ -26,6 +26,8 @@ class EmbedderTaskRunner final : public fml::TaskRunner {
bool PostTask(uint64_t baton);
void Terminate();
private:
const size_t embedder_identifier_;
DispatchTable dispatch_table_;
@ -33,6 +35,7 @@ class EmbedderTaskRunner final : public fml::TaskRunner {
uint64_t last_baton_;
std::unordered_map<uint64_t, fml::closure> pending_tasks_;
fml::TaskQueueId placeholder_id_;
bool terminated_ = false;
void PostTask(const fml::closure& task) override;

Просмотреть файл

@ -3,6 +3,7 @@
#include <flutter/fml/closure.h>
#include <chrono>
#include <cstdarg>
#include <set>
#include <unordered_map>
@ -26,6 +27,12 @@ class UIWidgetsSystem {
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();

3
engine/third_party/Unity/IUnityUIWidgets.h поставляемый
Просмотреть файл

@ -1,7 +1,7 @@
#pragma once
#include "IUnityInterface.h"
#include "IUnityGraphics.h"
#include "IUnityInterface.h"
namespace UnityUIWidgets {
typedef void (*VoidCallback)();
@ -16,6 +16,7 @@ UNITY_DECLARE_INTERFACE(IUnityUIWidgets) {
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