prepare.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /*!
  2. * @pixi/prepare - v6.5.10
  3. * Compiled Thu, 06 Jul 2023 15:25:11 UTC
  4. *
  5. * @pixi/prepare is licensed under the MIT License.
  6. * http://www.opensource.org/licenses/mit-license
  7. */
  8. 'use strict';
  9. Object.defineProperty(exports, '__esModule', { value: true });
  10. var settings = require('@pixi/settings');
  11. var core = require('@pixi/core');
  12. var graphics = require('@pixi/graphics');
  13. var ticker = require('@pixi/ticker');
  14. var display = require('@pixi/display');
  15. var text = require('@pixi/text');
  16. var utils = require('@pixi/utils');
  17. /**
  18. * Default number of uploads per frame using prepare plugin.
  19. * @static
  20. * @memberof PIXI.settings
  21. * @name UPLOADS_PER_FRAME
  22. * @type {number}
  23. * @default 4
  24. */
  25. settings.settings.UPLOADS_PER_FRAME = 4;
  26. /*! *****************************************************************************
  27. Copyright (c) Microsoft Corporation.
  28. Permission to use, copy, modify, and/or distribute this software for any
  29. purpose with or without fee is hereby granted.
  30. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  31. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  32. AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  33. INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  34. LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  35. OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  36. PERFORMANCE OF THIS SOFTWARE.
  37. ***************************************************************************** */
  38. /* global Reflect, Promise */
  39. var extendStatics = function(d, b) {
  40. extendStatics = Object.setPrototypeOf ||
  41. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  42. function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
  43. return extendStatics(d, b);
  44. };
  45. function __extends(d, b) {
  46. extendStatics(d, b);
  47. function __() { this.constructor = d; }
  48. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  49. }
  50. /**
  51. * CountLimiter limits the number of items handled by a {@link PIXI.BasePrepare} to a specified
  52. * number of items per frame.
  53. * @memberof PIXI
  54. */
  55. var CountLimiter = /** @class */ (function () {
  56. /**
  57. * @param maxItemsPerFrame - The maximum number of items that can be prepared each frame.
  58. */
  59. function CountLimiter(maxItemsPerFrame) {
  60. this.maxItemsPerFrame = maxItemsPerFrame;
  61. this.itemsLeft = 0;
  62. }
  63. /** Resets any counting properties to start fresh on a new frame. */
  64. CountLimiter.prototype.beginFrame = function () {
  65. this.itemsLeft = this.maxItemsPerFrame;
  66. };
  67. /**
  68. * Checks to see if another item can be uploaded. This should only be called once per item.
  69. * @returns If the item is allowed to be uploaded.
  70. */
  71. CountLimiter.prototype.allowedToUpload = function () {
  72. return this.itemsLeft-- > 0;
  73. };
  74. return CountLimiter;
  75. }());
  76. /**
  77. * Built-in hook to find multiple textures from objects like AnimatedSprites.
  78. * @private
  79. * @param item - Display object to check
  80. * @param queue - Collection of items to upload
  81. * @returns If a PIXI.Texture object was found.
  82. */
  83. function findMultipleBaseTextures(item, queue) {
  84. var result = false;
  85. // Objects with multiple textures
  86. if (item && item._textures && item._textures.length) {
  87. for (var i = 0; i < item._textures.length; i++) {
  88. if (item._textures[i] instanceof core.Texture) {
  89. var baseTexture = item._textures[i].baseTexture;
  90. if (queue.indexOf(baseTexture) === -1) {
  91. queue.push(baseTexture);
  92. result = true;
  93. }
  94. }
  95. }
  96. }
  97. return result;
  98. }
  99. /**
  100. * Built-in hook to find BaseTextures from Texture.
  101. * @private
  102. * @param item - Display object to check
  103. * @param queue - Collection of items to upload
  104. * @returns If a PIXI.Texture object was found.
  105. */
  106. function findBaseTexture(item, queue) {
  107. if (item.baseTexture instanceof core.BaseTexture) {
  108. var texture = item.baseTexture;
  109. if (queue.indexOf(texture) === -1) {
  110. queue.push(texture);
  111. }
  112. return true;
  113. }
  114. return false;
  115. }
  116. /**
  117. * Built-in hook to find textures from objects.
  118. * @private
  119. * @param item - Display object to check
  120. * @param queue - Collection of items to upload
  121. * @returns If a PIXI.Texture object was found.
  122. */
  123. function findTexture(item, queue) {
  124. if (item._texture && item._texture instanceof core.Texture) {
  125. var texture = item._texture.baseTexture;
  126. if (queue.indexOf(texture) === -1) {
  127. queue.push(texture);
  128. }
  129. return true;
  130. }
  131. return false;
  132. }
  133. /**
  134. * Built-in hook to draw PIXI.Text to its texture.
  135. * @private
  136. * @param _helper - Not used by this upload handler
  137. * @param item - Item to check
  138. * @returns If item was uploaded.
  139. */
  140. function drawText(_helper, item) {
  141. if (item instanceof text.Text) {
  142. // updating text will return early if it is not dirty
  143. item.updateText(true);
  144. return true;
  145. }
  146. return false;
  147. }
  148. /**
  149. * Built-in hook to calculate a text style for a PIXI.Text object.
  150. * @private
  151. * @param _helper - Not used by this upload handler
  152. * @param item - Item to check
  153. * @returns If item was uploaded.
  154. */
  155. function calculateTextStyle(_helper, item) {
  156. if (item instanceof text.TextStyle) {
  157. var font = item.toFontString();
  158. text.TextMetrics.measureFont(font);
  159. return true;
  160. }
  161. return false;
  162. }
  163. /**
  164. * Built-in hook to find Text objects.
  165. * @private
  166. * @param item - Display object to check
  167. * @param queue - Collection of items to upload
  168. * @returns if a PIXI.Text object was found.
  169. */
  170. function findText(item, queue) {
  171. if (item instanceof text.Text) {
  172. // push the text style to prepare it - this can be really expensive
  173. if (queue.indexOf(item.style) === -1) {
  174. queue.push(item.style);
  175. }
  176. // also push the text object so that we can render it (to canvas/texture) if needed
  177. if (queue.indexOf(item) === -1) {
  178. queue.push(item);
  179. }
  180. // also push the Text's texture for upload to GPU
  181. var texture = item._texture.baseTexture;
  182. if (queue.indexOf(texture) === -1) {
  183. queue.push(texture);
  184. }
  185. return true;
  186. }
  187. return false;
  188. }
  189. /**
  190. * Built-in hook to find TextStyle objects.
  191. * @private
  192. * @param item - Display object to check
  193. * @param queue - Collection of items to upload
  194. * @returns If a PIXI.TextStyle object was found.
  195. */
  196. function findTextStyle(item, queue) {
  197. if (item instanceof text.TextStyle) {
  198. if (queue.indexOf(item) === -1) {
  199. queue.push(item);
  200. }
  201. return true;
  202. }
  203. return false;
  204. }
  205. /**
  206. * The prepare manager provides functionality to upload content to the GPU.
  207. *
  208. * BasePrepare handles basic queuing functionality and is extended by
  209. * {@link PIXI.Prepare} and {@link PIXI.CanvasPrepare}
  210. * to provide preparation capabilities specific to their respective renderers.
  211. * @example
  212. * // Create a sprite
  213. * const sprite = PIXI.Sprite.from('something.png');
  214. *
  215. * // Load object into GPU
  216. * app.renderer.plugins.prepare.upload(sprite, () => {
  217. *
  218. * //Texture(s) has been uploaded to GPU
  219. * app.stage.addChild(sprite);
  220. *
  221. * })
  222. * @abstract
  223. * @memberof PIXI
  224. */
  225. var BasePrepare = /** @class */ (function () {
  226. /**
  227. * @param {PIXI.AbstractRenderer} renderer - A reference to the current renderer
  228. */
  229. function BasePrepare(renderer) {
  230. var _this = this;
  231. this.limiter = new CountLimiter(settings.settings.UPLOADS_PER_FRAME);
  232. this.renderer = renderer;
  233. this.uploadHookHelper = null;
  234. this.queue = [];
  235. this.addHooks = [];
  236. this.uploadHooks = [];
  237. this.completes = [];
  238. this.ticking = false;
  239. this.delayedTick = function () {
  240. // unlikely, but in case we were destroyed between tick() and delayedTick()
  241. if (!_this.queue) {
  242. return;
  243. }
  244. _this.prepareItems();
  245. };
  246. // hooks to find the correct texture
  247. this.registerFindHook(findText);
  248. this.registerFindHook(findTextStyle);
  249. this.registerFindHook(findMultipleBaseTextures);
  250. this.registerFindHook(findBaseTexture);
  251. this.registerFindHook(findTexture);
  252. // upload hooks
  253. this.registerUploadHook(drawText);
  254. this.registerUploadHook(calculateTextStyle);
  255. }
  256. /** @ignore */
  257. BasePrepare.prototype.upload = function (item, done) {
  258. var _this = this;
  259. if (typeof item === 'function') {
  260. done = item;
  261. item = null;
  262. }
  263. if (done) {
  264. utils.deprecation('6.5.0', 'BasePrepare.upload callback is deprecated, use the return Promise instead.');
  265. }
  266. return new Promise(function (resolve) {
  267. // If a display object, search for items
  268. // that we could upload
  269. if (item) {
  270. _this.add(item);
  271. }
  272. // TODO: remove done callback and just use resolve
  273. var complete = function () {
  274. done === null || done === void 0 ? void 0 : done();
  275. resolve();
  276. };
  277. // Get the items for upload from the display
  278. if (_this.queue.length) {
  279. _this.completes.push(complete);
  280. if (!_this.ticking) {
  281. _this.ticking = true;
  282. ticker.Ticker.system.addOnce(_this.tick, _this, ticker.UPDATE_PRIORITY.UTILITY);
  283. }
  284. }
  285. else {
  286. complete();
  287. }
  288. });
  289. };
  290. /**
  291. * Handle tick update
  292. * @private
  293. */
  294. BasePrepare.prototype.tick = function () {
  295. setTimeout(this.delayedTick, 0);
  296. };
  297. /**
  298. * Actually prepare items. This is handled outside of the tick because it will take a while
  299. * and we do NOT want to block the current animation frame from rendering.
  300. * @private
  301. */
  302. BasePrepare.prototype.prepareItems = function () {
  303. this.limiter.beginFrame();
  304. // Upload the graphics
  305. while (this.queue.length && this.limiter.allowedToUpload()) {
  306. var item = this.queue[0];
  307. var uploaded = false;
  308. if (item && !item._destroyed) {
  309. for (var i = 0, len = this.uploadHooks.length; i < len; i++) {
  310. if (this.uploadHooks[i](this.uploadHookHelper, item)) {
  311. this.queue.shift();
  312. uploaded = true;
  313. break;
  314. }
  315. }
  316. }
  317. if (!uploaded) {
  318. this.queue.shift();
  319. }
  320. }
  321. // We're finished
  322. if (!this.queue.length) {
  323. this.ticking = false;
  324. var completes = this.completes.slice(0);
  325. this.completes.length = 0;
  326. for (var i = 0, len = completes.length; i < len; i++) {
  327. completes[i]();
  328. }
  329. }
  330. else {
  331. // if we are not finished, on the next rAF do this again
  332. ticker.Ticker.system.addOnce(this.tick, this, ticker.UPDATE_PRIORITY.UTILITY);
  333. }
  334. };
  335. /**
  336. * Adds hooks for finding items.
  337. * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
  338. * function must return `true` if it was able to add item to the queue.
  339. * @returns Instance of plugin for chaining.
  340. */
  341. BasePrepare.prototype.registerFindHook = function (addHook) {
  342. if (addHook) {
  343. this.addHooks.push(addHook);
  344. }
  345. return this;
  346. };
  347. /**
  348. * Adds hooks for uploading items.
  349. * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
  350. * function must return `true` if it was able to handle upload of item.
  351. * @returns Instance of plugin for chaining.
  352. */
  353. BasePrepare.prototype.registerUploadHook = function (uploadHook) {
  354. if (uploadHook) {
  355. this.uploadHooks.push(uploadHook);
  356. }
  357. return this;
  358. };
  359. /**
  360. * Manually add an item to the uploading queue.
  361. * @param {PIXI.DisplayObject|PIXI.Container|PIXI.BaseTexture|PIXI.Texture|PIXI.Graphics|PIXI.Text|*} item - Object to
  362. * add to the queue
  363. * @returns Instance of plugin for chaining.
  364. */
  365. BasePrepare.prototype.add = function (item) {
  366. // Add additional hooks for finding elements on special
  367. // types of objects that
  368. for (var i = 0, len = this.addHooks.length; i < len; i++) {
  369. if (this.addHooks[i](item, this.queue)) {
  370. break;
  371. }
  372. }
  373. // Get children recursively
  374. if (item instanceof display.Container) {
  375. for (var i = item.children.length - 1; i >= 0; i--) {
  376. this.add(item.children[i]);
  377. }
  378. }
  379. return this;
  380. };
  381. /** Destroys the plugin, don't use after this. */
  382. BasePrepare.prototype.destroy = function () {
  383. if (this.ticking) {
  384. ticker.Ticker.system.remove(this.tick, this);
  385. }
  386. this.ticking = false;
  387. this.addHooks = null;
  388. this.uploadHooks = null;
  389. this.renderer = null;
  390. this.completes = null;
  391. this.queue = null;
  392. this.limiter = null;
  393. this.uploadHookHelper = null;
  394. };
  395. return BasePrepare;
  396. }());
  397. /**
  398. * Built-in hook to upload PIXI.Texture objects to the GPU.
  399. * @private
  400. * @param renderer - instance of the webgl renderer
  401. * @param item - Item to check
  402. * @returns If item was uploaded.
  403. */
  404. function uploadBaseTextures(renderer, item) {
  405. if (item instanceof core.BaseTexture) {
  406. // if the texture already has a GL texture, then the texture has been prepared or rendered
  407. // before now. If the texture changed, then the changer should be calling texture.update() which
  408. // reuploads the texture without need for preparing it again
  409. if (!item._glTextures[renderer.CONTEXT_UID]) {
  410. renderer.texture.bind(item);
  411. }
  412. return true;
  413. }
  414. return false;
  415. }
  416. /**
  417. * Built-in hook to upload PIXI.Graphics to the GPU.
  418. * @private
  419. * @param renderer - instance of the webgl renderer
  420. * @param item - Item to check
  421. * @returns If item was uploaded.
  422. */
  423. function uploadGraphics(renderer, item) {
  424. if (!(item instanceof graphics.Graphics)) {
  425. return false;
  426. }
  427. var geometry = item.geometry;
  428. // update dirty graphics to get batches
  429. item.finishPoly();
  430. geometry.updateBatches();
  431. var batches = geometry.batches;
  432. // upload all textures found in styles
  433. for (var i = 0; i < batches.length; i++) {
  434. var texture = batches[i].style.texture;
  435. if (texture) {
  436. uploadBaseTextures(renderer, texture.baseTexture);
  437. }
  438. }
  439. // if its not batchable - update vao for particular shader
  440. if (!geometry.batchable) {
  441. renderer.geometry.bind(geometry, item._resolveDirectShader(renderer));
  442. }
  443. return true;
  444. }
  445. /**
  446. * Built-in hook to find graphics.
  447. * @private
  448. * @param item - Display object to check
  449. * @param queue - Collection of items to upload
  450. * @returns if a PIXI.Graphics object was found.
  451. */
  452. function findGraphics(item, queue) {
  453. if (item instanceof graphics.Graphics) {
  454. queue.push(item);
  455. return true;
  456. }
  457. return false;
  458. }
  459. /**
  460. * The prepare plugin provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
  461. * asynchronously preparing and uploading to the GPU assets, textures, graphics waiting to be displayed.
  462. *
  463. * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
  464. * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.Renderer#plugins}.
  465. * @example
  466. * // Create a new application
  467. * const app = new PIXI.Application();
  468. * document.body.appendChild(app.view);
  469. *
  470. * // Don't start rendering right away
  471. * app.stop();
  472. *
  473. * // create a display object
  474. * const rect = new PIXI.Graphics()
  475. * .beginFill(0x00ff00)
  476. * .drawRect(40, 40, 200, 200);
  477. *
  478. * // Add to the stage
  479. * app.stage.addChild(rect);
  480. *
  481. * // Don't start rendering until the graphic is uploaded to the GPU
  482. * app.renderer.plugins.prepare.upload(app.stage, () => {
  483. * app.start();
  484. * });
  485. * @memberof PIXI
  486. */
  487. var Prepare = /** @class */ (function (_super) {
  488. __extends(Prepare, _super);
  489. /**
  490. * @param {PIXI.Renderer} renderer - A reference to the current renderer
  491. */
  492. function Prepare(renderer) {
  493. var _this = _super.call(this, renderer) || this;
  494. _this.uploadHookHelper = _this.renderer;
  495. // Add textures and graphics to upload
  496. _this.registerFindHook(findGraphics);
  497. _this.registerUploadHook(uploadBaseTextures);
  498. _this.registerUploadHook(uploadGraphics);
  499. return _this;
  500. }
  501. /** @ignore */
  502. Prepare.extension = {
  503. name: 'prepare',
  504. type: core.ExtensionType.RendererPlugin,
  505. };
  506. return Prepare;
  507. }(BasePrepare));
  508. /**
  509. * TimeLimiter limits the number of items handled by a {@link PIXI.BasePrepare} to a specified
  510. * number of milliseconds per frame.
  511. * @memberof PIXI
  512. */
  513. var TimeLimiter = /** @class */ (function () {
  514. /** @param maxMilliseconds - The maximum milliseconds that can be spent preparing items each frame. */
  515. function TimeLimiter(maxMilliseconds) {
  516. this.maxMilliseconds = maxMilliseconds;
  517. this.frameStart = 0;
  518. }
  519. /** Resets any counting properties to start fresh on a new frame. */
  520. TimeLimiter.prototype.beginFrame = function () {
  521. this.frameStart = Date.now();
  522. };
  523. /**
  524. * Checks to see if another item can be uploaded. This should only be called once per item.
  525. * @returns - If the item is allowed to be uploaded.
  526. */
  527. TimeLimiter.prototype.allowedToUpload = function () {
  528. return Date.now() - this.frameStart < this.maxMilliseconds;
  529. };
  530. return TimeLimiter;
  531. }());
  532. exports.BasePrepare = BasePrepare;
  533. exports.CountLimiter = CountLimiter;
  534. exports.Prepare = Prepare;
  535. exports.TimeLimiter = TimeLimiter;
  536. //# sourceMappingURL=prepare.js.map