1 module skia.SKObject; 2 3 import skia.Definitions; 4 import skia.Exceptions; 5 import skia.IDisposable; 6 import skia.SkiaApi; 7 import std.experimental.logger; 8 9 import core.atomic; 10 import core.sync.condition; 11 import core.sync.mutex; 12 import core.sync.rwmutex; 13 14 15 /** 16 * 17 */ 18 class SKObject : SKNativeObject, ISKReferenceCounted 19 { 20 // private const object locker = new object (); 21 22 // FIXME: Needing refactor or cleanup -@putao at 2020-12-27T16:44:22+08:00 23 // 24 // private ConcurrentDictionary<void*, SKObject> ownedObjects; 25 // private ConcurrentDictionary<void*, SKObject> keepAliveObjects; 26 private SKObject[void*] ownedObjects; 27 private SKObject[void*] keepAliveObjects; 28 29 private Mutex _ownedObjectsMutex; 30 private Mutex _keepAliveObjectsMutex; 31 32 // shared static this() 33 // { 34 // // SkiaSharpVersion.CheckNativeLibraryCompatible (true); 35 36 // // SKColorSpace.EnsureStaticInstanceAreInitialized (); 37 // // SKData.EnsureStaticInstanceAreInitialized (); 38 // // SKFontManager.EnsureStaticInstanceAreInitialized (); 39 // // SKTypeface.EnsureStaticInstanceAreInitialized (); 40 // } 41 42 this (void* handle, bool owns) 43 { 44 _ownedObjectsMutex = new Mutex(); 45 _keepAliveObjectsMutex = new Mutex(); 46 super (handle, owns); 47 } 48 49 // protected override void Dispose (bool disposing) { 50 // return super.Dispose (disposing); 51 // } 52 53 // override void Dispose () 54 // { 55 // if (IgnorePublicDispose) 56 // return; 57 58 // DisposeInternal (); 59 // } 60 61 override void* Handle() { 62 return super.Handle(); 63 } 64 65 override void Handle(void* value) { 66 if (value is null) { 67 DeregisterHandle (Handle, this); 68 super.Handle = value; 69 } else { 70 super.Handle = value; 71 RegisterHandle (Handle, this); 72 } 73 } 74 75 76 // SKObject[void*] OwnedObjects() { 77 // return ownedObjects; 78 // } 79 80 // SKObject[void*] KeepAliveObjects() { 81 // return keepAliveObjects; 82 // } 83 84 void registerOwnedObject(void* handle, SKObject obj) { 85 _ownedObjectsMutex.lock(); 86 scope(exit) { 87 _ownedObjectsMutex.unlock(); 88 } 89 90 ownedObjects[handle] = obj; 91 } 92 93 void registerKeepAliveObject(void* handle, SKObject obj) { 94 _ownedObjectsMutex.lock(); 95 scope(exit) { 96 _ownedObjectsMutex.unlock(); 97 } 98 99 keepAliveObjects[handle] = obj; 100 } 101 102 protected override void DisposeUnownedManaged () 103 { 104 if (ownedObjects !is null) { 105 foreach (SKObject c; ownedObjects.byValue()) { 106 if(c is null) continue; 107 108 version(SKIA_DEBUG) { 109 tracef("SKObject: %s, OwnsHandle: %s", typeid(c), c.OwnsHandle); 110 } 111 112 if (!c.OwnsHandle) 113 c.DisposeInternal (); 114 } 115 } 116 } 117 118 protected override void DisposeManaged () 119 { 120 if (ownedObjects !is null) { 121 foreach (SKObject c; ownedObjects.byValue()) { 122 if (c !is null && c.OwnsHandle) 123 c.DisposeInternal (); 124 } 125 ownedObjects.clear (); 126 } 127 keepAliveObjects.clear (); 128 } 129 130 protected override void DisposeNative () 131 { 132 ISKReferenceCounted refcnt = cast(ISKReferenceCounted)this; 133 if (refcnt !is null) 134 refcnt.SafeUnRef (); 135 } 136 137 static TSkiaObject GetOrAddObject(TSkiaObject) (void* handle, Func!(void*, bool, TSkiaObject) objectFactory) 138 if(is(TSkiaObject : SKObject)) 139 { 140 if (handle is null) 141 return null; 142 143 return HandleDictionary.GetOrAddObject!(TSkiaObject) (handle, true, true, objectFactory); 144 } 145 146 static TSkiaObject GetOrAddObject(TSkiaObject) (void* handle, bool owns, Func!(void*, bool, TSkiaObject) objectFactory) 147 if(is(TSkiaObject : SKObject)) 148 { 149 trace("xxxx"); 150 151 if (handle is null) 152 return null; 153 154 return HandleDictionary.GetOrAddObject!(TSkiaObject) (handle, owns, true, objectFactory); 155 } 156 157 static TSkiaObject GetOrAddObject(TSkiaObject) (void* handle, bool owns, bool unrefExisting, Func!(void*, bool, TSkiaObject) objectFactory) 158 if(is(TSkiaObject : SKObject)) 159 { 160 if (handle is null) 161 return null; 162 163 return HandleDictionary.GetOrAddObject!(TSkiaObject) (handle, owns, unrefExisting, objectFactory); 164 } 165 166 static void RegisterHandle (void* handle, SKObject instance) 167 { 168 if (handle is null || instance is null) 169 return; 170 HandleDictionary.RegisterHandle (handle, instance); 171 } 172 173 static void DeregisterHandle (void* handle, SKObject instance) 174 { 175 if (handle is null) 176 return; 177 178 HandleDictionary.DeregisterHandle (handle, instance); 179 } 180 181 static bool GetInstance(TSkiaObject) (void* handle, ref TSkiaObject instance) 182 if(is(TSkiaObject : SKObject)) 183 { 184 if (handle is null) { 185 instance = null; 186 return false; 187 } 188 189 return HandleDictionary.GetInstance!(TSkiaObject) (handle, instance); 190 } 191 192 // indicate that the user cannot dispose the object 193 void PreventPublicDisposal () 194 { 195 IgnorePublicDispose = true; 196 } 197 198 // indicate that the ownership of this object is now in the hands of 199 // the native object 200 void RevokeOwnership (SKObject newOwner) 201 { 202 OwnsHandle = false; 203 IgnorePublicDispose = true; 204 205 if (newOwner is null) 206 DisposeInternal (); 207 else { 208 newOwner.registerOwnedObject(Handle, this); 209 } 210 } 211 212 // indicate that the child is controlled by the native code and 213 // the managed wrapper should be disposed when the owner is 214 static T OwnedBy(T) (T child, SKObject owner) 215 if(is(T : SKObject)) 216 { 217 if (child !is null) { 218 owner.registerOwnedObject(child.Handle, child); 219 // owner.OwnedObjects[child.Handle] = child; 220 } 221 222 return child; 223 } 224 225 // indicate that the child was created by the managed code and 226 // should be disposed when the owner is 227 static T Owned(T) (T owner, SKObject child) 228 if(is(T : SKObject)) 229 { 230 if (child !is null) { 231 if (owner !is null){ 232 owner.registerOwnedObject(child.Handle, child); 233 // owner.OwnedObjects[child.Handle] = child; 234 } 235 else 236 child.Dispose (); 237 } 238 239 return owner; 240 } 241 242 // indicate that the chile should not be garbage collected while 243 // the owner still lives 244 static T Referenced(T) (T owner, SKObject child) 245 if(is(T : SKObject)) 246 { 247 if (child !is null && owner !is null) { 248 // owner.KeepAliveObjects[child.Handle] = child; 249 owner.registerKeepAliveObject(child.Handle, child); 250 251 } 252 253 return owner; 254 } 255 256 static T[] PtrToStructureArray(T) (void* intPtr, int count) 257 { 258 warning("TODO"); 259 auto items = new T[count]; 260 // var size = Marshal.SizeOf!(T) (); 261 // for (var i = 0; i < count; i++) { 262 // var newPtr = new void* (intPtr.ToInt64 () + (i * size)); 263 // items[i] = Marshal.PtrToStructure<T> (newPtr); 264 // } 265 return items; 266 } 267 268 // static T PtrToStructure<T> (void* intPtr, int index) 269 // { 270 // var size = Marshal.SizeOf<T> (); 271 // var newPtr = new void* (intPtr.ToInt64 () + (index * size)); 272 // return Marshal.PtrToStructure<T> (newPtr); 273 // } 274 } 275 276 277 /** 278 * 279 */ 280 class SKNativeObject : IDisposable 281 { 282 bool fromFinalizer = false; 283 284 private shared int isDisposed = 0; 285 286 this (void* handle) 287 { 288 this (handle, true); 289 } 290 291 this (void* handle, bool ownsHandle) 292 { 293 version(SKIA_DEBUG) { 294 tracef("SkObject: %s, ownsHandle: %s", typeid(this), ownsHandle); 295 } 296 Handle = handle; 297 OwnsHandle = ownsHandle; 298 } 299 300 ~this () 301 { 302 fromFinalizer = true; 303 Dispose (false); 304 } 305 306 private void* _handle = null; 307 void* Handle() { 308 return _handle; 309 } 310 311 protected void Handle(void* value) { 312 _handle = value; 313 } 314 315 private bool _ownsHandle = false; 316 protected bool OwnsHandle() { 317 return _ownsHandle; 318 } 319 320 protected void OwnsHandle(bool value) { 321 _ownsHandle = value; 322 } 323 324 private bool _ignorePublicDispose = false; 325 protected bool IgnorePublicDispose() { 326 return _ignorePublicDispose; 327 } 328 329 protected void IgnorePublicDispose(bool value) { 330 _ignorePublicDispose = value; 331 } 332 333 protected bool IsDisposed() { 334 return isDisposed == 1; 335 } 336 337 protected void DisposeUnownedManaged () 338 { 339 // dispose of any managed resources that are not actually owned 340 } 341 342 protected void DisposeManaged () 343 { 344 // dispose of any managed resources 345 } 346 347 protected void DisposeNative () 348 { 349 // dispose of any unmanaged resources 350 } 351 352 protected void Dispose (bool disposing) 353 { 354 // if (Interlocked.CompareExchange (ref isDisposed, 1, 0) != 0) 355 // return; 356 int v = cas(&isDisposed, 1, 0); 357 if(v != 0) { 358 return; 359 } 360 361 // dispose any objects that are owned/created by native code 362 if (disposing) 363 DisposeUnownedManaged (); 364 365 // destroy the native object 366 if (Handle !is null && OwnsHandle) 367 DisposeNative (); 368 369 // dispose any remaining managed-created objects 370 if (disposing) 371 DisposeManaged (); 372 373 Handle = null; 374 } 375 376 void Dispose () 377 { 378 if (IgnorePublicDispose) 379 return; 380 381 DisposeInternal (); 382 } 383 384 protected void DisposeInternal () 385 { 386 Dispose (true); 387 // FIXME: Needing refactor or cleanup -@putao at 2020-12-27T16:08:53+08:00 388 // 389 // GC.SuppressFinalize (this); 390 } 391 } 392 393 394 /** 395 * SKObjectExtensions 396 */ 397 bool IsUnique (void* handle, bool isVirtual) 398 { 399 if (isVirtual) 400 return SkiaApi.sk_refcnt_unique (cast(sk_refcnt_t*)handle); 401 else 402 return SkiaApi.sk_nvrefcnt_unique (cast(sk_nvrefcnt_t*)handle); 403 } 404 405 int GetReferenceCount (void* handle, bool isVirtual) 406 { 407 if (isVirtual) 408 return SkiaApi.sk_refcnt_get_ref_count (cast(sk_refcnt_t*)handle); 409 else 410 return SkiaApi.sk_nvrefcnt_get_ref_count (cast(sk_nvrefcnt_t* )handle); 411 } 412 413 void SafeRef (ISKReferenceCounted obj) 414 { 415 ISKNonVirtualReferenceCounted nvrefcnt = cast(ISKNonVirtualReferenceCounted)obj; 416 if (nvrefcnt !is nvrefcnt) 417 nvrefcnt.ReferenceNative (); 418 else 419 SkiaApi.sk_refcnt_safe_unref (cast(sk_refcnt_t*)obj.Handle); 420 } 421 422 void SafeUnRef (ISKReferenceCounted obj) 423 { 424 ISKNonVirtualReferenceCounted nvrefcnt = cast(ISKNonVirtualReferenceCounted)obj; 425 if (nvrefcnt !is nvrefcnt) 426 nvrefcnt.UnreferenceNative (); 427 else 428 SkiaApi.sk_refcnt_safe_unref (cast(sk_refcnt_t*)obj.Handle); 429 } 430 431 int GetReferenceCount (ISKReferenceCounted obj) 432 { 433 ISKNonVirtualReferenceCounted skObj = cast(ISKNonVirtualReferenceCounted)obj; 434 if (skObj !is null) 435 return SkiaApi.sk_nvrefcnt_get_ref_count (cast(sk_nvrefcnt_t*)obj.Handle); 436 else 437 return SkiaApi.sk_refcnt_get_ref_count (cast(sk_refcnt_t*)obj.Handle); 438 } 439 440 441 /** 442 * 443 */ 444 interface ISKReferenceCounted 445 { 446 void* Handle(); 447 } 448 449 450 /** 451 * 452 */ 453 interface ISKNonVirtualReferenceCounted : ISKReferenceCounted 454 { 455 void ReferenceNative (); 456 457 void UnreferenceNative (); 458 } 459 460 461 /** 462 * 463 */ 464 interface ISKSkipObjectRegistration 465 { 466 } 467 468 469 alias Func(T, R) = R delegate(T); 470 alias Func(T1, T2, R) = R delegate(T1, T2); 471 alias Func(T1, T2, T3, R) = R delegate(T1, T2, T3); 472 alias Func(T1, T2, T3, T4, R) = R delegate(T1, T2, T3, T4); 473 alias Func(T1, T2, T3, T4, T5, R) = R delegate(T1, T2, T3, T4, T5); 474 475 package struct HandleDictionary { 476 private __gshared Object[void*] instances; 477 private __gshared Mutex instancesLock; 478 479 shared static this() { 480 instancesLock = new Mutex(); 481 } 482 483 /// <summary> 484 /// Retrieve the living instance if there is one, or null if not. 485 /// </summary> 486 /// <returns>The instance if it is alive, or null if there is none.</returns> 487 static bool GetInstance(TSkiaObject) (void* handle, ref TSkiaObject instance) 488 if(is(TSkiaObject : SKObject)) 489 { 490 if (handle is null) { 491 instance = null; 492 return false; 493 } 494 495 // FIXME: Needing refactor or cleanup -@putao at 2021-01-01T12:20:15+08:00 496 // 497 // if (SkipObjectRegistrationType.IsAssignableFrom (typeof (TSkiaObject))) { 498 // instance = null; 499 // return false; 500 // } 501 502 instancesLock.lock(); 503 try { 504 return GetInstanceNoLocks (handle, instance); 505 } finally { 506 instancesLock.unlock(); 507 } 508 } 509 510 /// <summary> 511 /// Retrieve or create an instance for the native handle. 512 /// </summary> 513 /// <returns>The instance, or null if the handle was null.</returns> 514 static TSkiaObject GetOrAddObject(TSkiaObject) (void* handle, bool owns, bool unrefExisting, Func!(void*, bool, TSkiaObject) objectFactory) 515 if(is(TSkiaObject : SKObject)) 516 { 517 if (handle is null) 518 return null; 519 520 // if (SkipObjectRegistrationType.IsAssignableFrom (typeof (TSkiaObject))) { 521 // #if THROW_OBJECT_EXCEPTIONS 522 // throw new InvalidOperationException ( 523 // "For some reason, the object was constructed using a factory function instead of the constructor. " + 524 // "H: {handle.ToString ("x")} Type: {typeof (TSkiaObject)}"); 525 // #else 526 // return objectFactory.Invoke (handle, owns); 527 // #endif 528 // } 529 530 instancesLock.lock(); 531 532 try { 533 TSkiaObject instance; 534 if (GetInstanceNoLocks!(TSkiaObject)(handle, instance)) { 535 // some object get automatically referenced on the native side, 536 // but managed code just has the same reference 537 ISKReferenceCounted refcnt = cast(ISKReferenceCounted)instance; 538 if (unrefExisting && refcnt !is null) { 539 if (refcnt.GetReferenceCount () == 1) { 540 throw new InvalidOperationException ( "About to unreference an object that has no references. "); 541 // throw new InvalidOperationException ( 542 // "About to unreference an object that has no references. " ~ 543 // format("H: {handle.ToString (\"%02X\")} Type: {instance.GetType ()}")); 544 } 545 refcnt.SafeUnRef (); 546 } 547 548 return instance; 549 } 550 551 TSkiaObject obj = objectFactory(handle, owns); 552 553 return obj; 554 } catch(Exception ex) { 555 warning(ex); 556 return TSkiaObject.init; 557 } finally { 558 instancesLock.unlock(); 559 } 560 } 561 562 /// <summary> 563 /// Retrieve the living instance if there is one, or null if not. This does not use locks. 564 /// </summary> 565 /// <returns>The instance if it is alive, or null if there is none.</returns> 566 private static bool GetInstanceNoLocks(TSkiaObject) (void* handle, TSkiaObject instance) 567 if(is(TSkiaObject : SKObject)) 568 { 569 auto itemPtr = handle in instances; 570 if(itemPtr !is null) { 571 TSkiaObject match = cast(TSkiaObject)*itemPtr; 572 if(match is null) { 573 warning("A managed object exists for the handle, but is not the expected type."); 574 } else { 575 instance = match; 576 return true; 577 } 578 } 579 580 instance = null; 581 return false; 582 } 583 584 /// <summary> 585 /// Registers the specified instance with the dictionary. 586 /// </summary> 587 static void RegisterHandle (void* handle, SKObject instance) 588 { 589 if (handle is null || instance is null) 590 return; 591 592 ISKSkipObjectRegistration objectRegistration = cast(ISKSkipObjectRegistration)instance; 593 if (objectRegistration !is null) 594 return; 595 596 SKObject objectToDispose = null; 597 instancesLock.lock(); 598 599 try { 600 Object oldValue; 601 auto itemPtr = handle in instances; 602 603 if(itemPtr !is null) { 604 oldValue = *itemPtr; 605 SKObject obj = cast(SKObject)oldValue; 606 if(obj is null) { 607 warning("No a SKObject"); 608 } else { 609 if (obj.OwnsHandle) { 610 // a mostly recoverable error 611 // if there is a managed object, then maybe something happened and the native object is dead 612 // throw new InvalidOperationException ( 613 // "A managed object already exists for the specified native object. " + 614 // "H: {handle.ToString ("x")} Type: ({obj.GetType ()}, {instance.GetType ()})"); 615 throw new Exception("A managed object already exists for the specified native object. "); 616 } 617 618 objectToDispose = obj; 619 } 620 } 621 622 instances[handle] = instance; 623 } finally { 624 instancesLock.unlock(); 625 // instancesLock.ExitWriteLock (); 626 } 627 628 // dispose the object we just replaced 629 if(objectToDispose !is null) { 630 objectToDispose.DisposeInternal (); 631 } 632 } 633 634 /// <summary> 635 /// Removes the registered instance from the dictionary. 636 /// </summary> 637 static void DeregisterHandle (void* handle, SKObject instance) 638 { 639 if (handle is null) 640 return; 641 642 ISKSkipObjectRegistration objectRegistration = cast(ISKSkipObjectRegistration)instance; 643 if (objectRegistration !is null) 644 return; 645 646 instancesLock.lock(); 647 648 try { 649 650 void* weak; 651 auto itemPtr = handle in instances; 652 if(itemPtr !is null) { 653 SKObject obj = cast(SKObject)*itemPtr; 654 if(*itemPtr is null || obj is instance) { 655 instances.remove(handle); 656 } else { 657 warning("A managed object did not exist for the specified native object. " ); 658 } 659 } else { 660 warning("A managed object did not exist for the specified native object. " ); 661 } 662 663 } finally { 664 instancesLock.unlock(); 665 } 666 } 667 }