|
1 |
| -/* [email protected].9, license MIT */ |
| 1 | +/* [email protected].10, license MIT */ |
2 | 2 | (function (factory) {
|
3 | 3 | typeof define === 'function' && define.amd ? define(factory) :
|
4 | 4 | factory();
|
|
411 | 411 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
412 | 412 | */
|
413 | 413 |
|
| 414 | + |
| 415 | + const augmentedSet = new Set(); |
| 416 | + |
414 | 417 | /**
|
415 | 418 | * Given a WebGL context replaces all the functions with wrapped functions
|
416 | 419 | * that call gl.getError after every command
|
|
419 | 422 | * @param {string} nameOfClass (eg, webgl, webgl2, OES_texture_float)
|
420 | 423 | */
|
421 | 424 | function augmentAPI(ctx, nameOfClass, options = {}) {
|
| 425 | + |
| 426 | + if (augmentedSet.has(ctx)) { |
| 427 | + return ctx; |
| 428 | + } |
| 429 | + augmentedSet.add(ctx); |
| 430 | + |
422 | 431 | const origGLErrorFn = options.origGLErrorFn || ctx.getError;
|
423 | 432 |
|
424 | 433 | function createSharedState(ctx) {
|
|
456 | 465 | defaultVertexArray: {},
|
457 | 466 | webglObjectToMemory: new Map(),
|
458 | 467 | };
|
459 |
| - sharedState.webglObjectToMemory.set(sharedState.defaultVertexArray, {}); |
460 |
| - sharedState.currentVertexArray = sharedState.defaultVertexArray; |
| 468 | + |
| 469 | + const unRestorableAPIs = new Set([ |
| 470 | + 'webgl', |
| 471 | + 'webgl2', |
| 472 | + 'webgl_lose_context', |
| 473 | + ]); |
| 474 | + |
| 475 | + function resetSharedState() { |
| 476 | + sharedState.bindings.clear(); |
| 477 | + sharedState.webglObjectToMemory.clear(); |
| 478 | + sharedState.webglObjectToMemory.set(sharedState.defaultVertexArray, {}); |
| 479 | + sharedState.currentVertexArray = sharedState.defaultVertexArray; |
| 480 | + [sharedState.resources, sharedState.memory].forEach(function(obj) { |
| 481 | + for (let prop in obj) { |
| 482 | + obj[prop] = 0; |
| 483 | + } |
| 484 | + }); |
| 485 | + } |
| 486 | + |
| 487 | + function handleContextLost() { |
| 488 | + // Issues: |
| 489 | + // * all resources are lost. |
| 490 | + // Solution: handled by resetSharedState |
| 491 | + // * all functions are no-op |
| 492 | + // Solutions: |
| 493 | + // * swap all functions for noop |
| 494 | + // (not so easy because some functions return values) |
| 495 | + // * wrap all functions is a isContextLost check forwarder |
| 496 | + // (slow? and same as above) |
| 497 | + // * have each function manually check for context lost |
| 498 | + // (simple but repetitive) |
| 499 | + // * all extensions are lost |
| 500 | + // Solution: For these we go through and restore all the functions |
| 501 | + // on each extension |
| 502 | + resetSharedState(); |
| 503 | + sharedState.isContextLost = true; |
| 504 | + |
| 505 | + // restore all original functions for extensions since |
| 506 | + // user will have to get new extensions. |
| 507 | + for (const [name, {ctx, origFuncs}] of [...Object.entries(sharedState.apis)]) { |
| 508 | + if (!unRestorableAPIs.has(name) && origFuncs) { |
| 509 | + augmentedSet.delete(ctx); |
| 510 | + for (const [funcName, origFn] of Object.entries(origFuncs)) { |
| 511 | + ctx[funcName] = origFn; |
| 512 | + } |
| 513 | + delete apis[name]; |
| 514 | + } |
| 515 | + } |
| 516 | + } |
| 517 | + |
| 518 | + function handleContextRestored() { |
| 519 | + sharedState.isContextLost = false; |
| 520 | + } |
| 521 | + |
| 522 | + if (ctx.canvas) { |
| 523 | + ctx.canvas.addEventListener('webglcontextlost', handleContextLost); |
| 524 | + ctx.canvas.addEventListener('webglcontextrestored', handleContextRestored); |
| 525 | + } |
| 526 | + |
| 527 | + resetSharedState(); |
461 | 528 | return sharedState;
|
462 | 529 | }
|
463 | 530 |
|
|
486 | 553 | }
|
487 | 554 | resources[typeName] = 0;
|
488 | 555 | return function(ctx, funcName, args, webglObj) {
|
| 556 | + if (sharedState.isContextLost) { |
| 557 | + return null; |
| 558 | + } |
489 | 559 | ++resources[typeName];
|
490 | 560 | webglObjectToMemory.set(webglObj, {
|
491 | 561 | size: 0,
|
|
499 | 569 | return;
|
500 | 570 | }
|
501 | 571 | return function(ctx, funcName, args) {
|
| 572 | + if (sharedState.isContextLost) { |
| 573 | + return; |
| 574 | + } |
502 | 575 | const [obj] = args;
|
503 | 576 | const info = webglObjectToMemory.get(obj);
|
504 | 577 | if (info) {
|
|
511 | 584 | }
|
512 | 585 |
|
513 | 586 | function updateRenderbuffer(target, samples, internalFormat, width, height) {
|
| 587 | + if (sharedState.isContextLost) { |
| 588 | + return; |
| 589 | + } |
514 | 590 | const obj = bindings.get(target);
|
515 | 591 | if (!obj) {
|
516 | 592 | throw new Error(`no renderbuffer bound to ${target}`);
|
|
581 | 657 | info.mips[level] = info.mips[level] || [];
|
582 | 658 | const mipFaceInfo = info.mips[level][faceNdx] || {};
|
583 | 659 | info.size -= mipFaceInfo.size || 0;
|
584 |
| - |
| 660 | + |
585 | 661 | mipFaceInfo.size = newMipSize;
|
586 | 662 | mipFaceInfo.internalFormat = internalFormat;
|
587 | 663 | mipFaceInfo.type = type;
|
588 | 664 | mipFaceInfo.width = width;
|
589 | 665 | mipFaceInfo.height = height;
|
590 | 666 | mipFaceInfo.depth = depth;
|
591 |
| - |
| 667 | + |
592 | 668 | info.mips[level][faceNdx] = mipFaceInfo;
|
593 | 669 | info.size += newMipSize;
|
594 | 670 |
|
|
610 | 686 | }
|
611 | 687 |
|
612 | 688 | function handleBindVertexArray(gl, funcName, args) {
|
| 689 | + if (sharedState.isContextLost) { |
| 690 | + return; |
| 691 | + } |
613 | 692 | const [va] = args;
|
614 | 693 | sharedState.currentVertexArray = va ? va : sharedState.defaultVertexArray;
|
615 | 694 | }
|
616 | 695 |
|
617 | 696 | function handleBufferBinding(target, obj) {
|
| 697 | + if (sharedState.isContextLost) { |
| 698 | + return; |
| 699 | + } |
618 | 700 | switch (target) {
|
619 | 701 | case ELEMENT_ARRAY_BUFFER:
|
620 | 702 | const info = webglObjectToMemory.get(sharedState.currentVertexArray);
|
|
635 | 717 | // void bufferData(GLenum target, [AllowShared] ArrayBufferView srcData, GLenum usage, GLuint srcOffset,
|
636 | 718 | // optional GLuint length = 0);
|
637 | 719 | bufferData(gl, funcName, args) {
|
| 720 | + if (sharedState.isContextLost) { |
| 721 | + return; |
| 722 | + } |
638 | 723 | const [target, src, /* usage */, srcOffset = 0, length = undefined] = args;
|
639 | 724 | let obj;
|
640 | 725 | switch (target) {
|
|
691 | 776 | },
|
692 | 777 |
|
693 | 778 | bindRenderbuffer(gl, funcName, args) {
|
| 779 | + if (sharedState.isContextLost) { |
| 780 | + return; |
| 781 | + } |
694 | 782 | const [target, obj] = args;
|
695 | 783 | bindings.set(target, obj);
|
696 | 784 | },
|
697 | 785 |
|
698 | 786 | bindTexture(gl, funcName, args) {
|
| 787 | + if (sharedState.isContextLost) { |
| 788 | + return; |
| 789 | + } |
699 | 790 | const [target, obj] = args;
|
700 | 791 | bindings.set(target, obj);
|
701 | 792 | },
|
702 | 793 |
|
703 | 794 | // void gl.copyTexImage2D(target, level, internalformat, x, y, width, height, border);
|
704 | 795 | copyTexImage2D(ctx, funcName, args) {
|
| 796 | + if (sharedState.isContextLost) { |
| 797 | + return; |
| 798 | + } |
705 | 799 | const [target, level, internalFormat, x, y, width, height, border] = args;
|
706 | 800 | const info = getTextureInfo(target);
|
707 | 801 | updateMipLevel(info, target, level, internalFormat, width, height, 1, UNSIGNED_BYTE);
|
|
728 | 822 | // void gl.compressedTexImage2D(target, level, internalformat, width, height, border,
|
729 | 823 | // ArrayBufferView srcData, optional srcOffset, optional srcLengthOverride);
|
730 | 824 | compressedTexImage2D(ctx, funcName, args) {
|
| 825 | + if (sharedState.isContextLost) { |
| 826 | + return; |
| 827 | + } |
731 | 828 | const [target, level, internalFormat, width, height] = args;
|
732 | 829 | const info = getTextureInfo(target);
|
733 | 830 | updateMipLevel(info, target, level, internalFormat, width, height, 1, UNSIGNED_BYTE);
|
|
738 | 835 | // void gl.compressedTexImage3D(target, level, internalformat, width, height, depth, border,
|
739 | 836 | // ArrayBufferView srcData, optional srcOffset, optional srcLengthOverride);
|
740 | 837 | compressedTexImage3D(ctx, funcName, args) {
|
| 838 | + if (sharedState.isContextLost) { |
| 839 | + return; |
| 840 | + } |
741 | 841 | const [target, level, internalFormat, width, height, depth] = args;
|
742 | 842 | const info = getTextureInfo(target);
|
743 | 843 | updateMipLevel(info, target, level, internalFormat, width, height, depth, UNSIGNED_BYTE);
|
|
763 | 863 | deleteVertexArrayOES: makeDeleteWrapper('vertexArray', noop, 'deleteVertexArrayOES'),
|
764 | 864 |
|
765 | 865 | fenceSync: function(ctx) {
|
| 866 | + if (sharedState.isContextLost) { |
| 867 | + return; |
| 868 | + } |
766 | 869 | if (!ctx.fenceSync) {
|
767 | 870 | return;
|
768 | 871 | }
|
|
777 | 880 | }(ctx),
|
778 | 881 |
|
779 | 882 | generateMipmap(ctx, funcName, args) {
|
| 883 | + if (sharedState.isContextLost) { |
| 884 | + return; |
| 885 | + } |
780 | 886 | const [target] = args;
|
781 | 887 | const info = getTextureInfo(target);
|
782 | 888 | const baseMipNdx = info.parameters ? info.parameters.get(TEXTURE_BASE_LEVEL) || 0 : 0;
|
|
797 | 903 | },
|
798 | 904 |
|
799 | 905 | getSupportedExtensions(ctx, funcName, args, result) {
|
| 906 | + if (sharedState.isContextLost) { |
| 907 | + return; |
| 908 | + } |
800 | 909 | result.push('GMAN_webgl_memory');
|
801 | 910 | },
|
802 | 911 |
|
|
819 | 928 | },
|
820 | 929 |
|
821 | 930 | texImage2D(ctx, funcName, args) {
|
| 931 | + if (sharedState.isContextLost) { |
| 932 | + return; |
| 933 | + } |
822 | 934 | // WebGL1:
|
823 | 935 | // void gl.texImage2D(target, level, internalformat, width, height, border, format, type, ArrayBufferView? pixels);
|
824 | 936 | // void gl.texImage2D(target, level, internalformat, format, type, ImageData? pixels);
|
|
865 | 977 | // void gl.texImage3D(target, level, internalformat, width, height, depth, border, format, type, ArrayBufferView srcData, srcOffset);
|
866 | 978 |
|
867 | 979 | texImage3D(ctx, funcName, args) {
|
| 980 | + if (sharedState.isContextLost) { |
| 981 | + return; |
| 982 | + } |
868 | 983 | let [target, level, internalFormat, width, height, depth, border, format, type] = args;
|
869 | 984 | const info = getTextureInfo(target);
|
870 | 985 | updateMipLevel(info, target, level, internalFormat, width, height, depth, type);
|
871 | 986 | },
|
872 | 987 |
|
873 | 988 | texParameteri(ctx, funcName, args) {
|
| 989 | + if (sharedState.isContextLost) { |
| 990 | + return; |
| 991 | + } |
874 | 992 | let [target, pname, value] = args;
|
875 | 993 | const info = getTextureInfo(target);
|
876 | 994 | info.parameters = info.parameters || new Map();
|
|
892 | 1010 |
|
893 | 1011 | const extraWrappers = {
|
894 | 1012 | getExtension(ctx, propertyName) {
|
| 1013 | + if (sharedState.isContextLost) { |
| 1014 | + return null; |
| 1015 | + } |
895 | 1016 | const origFn = ctx[propertyName];
|
896 | 1017 | ctx[propertyName] = function(...args) {
|
897 | 1018 | const extensionName = args[0].toLowerCase();
|
|
0 commit comments