All files / engine/Source/Renderer SharedContext.js

5.55% Statements 3/54
0% Branches 0/27
0% Functions 0/9
5.55% Lines 3/54

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187                                                                                            1x                                                                                                                                                                                                                                               1x                             1x          
import clone from "../Core/clone.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.js";
import Context from "./Context.js";
 
/**
 * Enables a single WebGL context to be used by any number of {@link Scene}s.
 * You can pass a SharedContext in place of a {@link ContextOptions} to the constructors of {@link Scene}, {@link CesiumWidget}, and {@link Viewer}.
 * {@link Primitive}s associated with the shared WebGL context can be displayed in any Scene that uses the same context.
 * The context renders each Scene to an off-screen canvas, then blits the result to that Scene's on-screen canvas.
 *
 * @private
 * @alias SharedContext
 * @constructor
 *
 * @param {object} [options] Object with the following properties:
 * @param {ContextOptions} [options.contextOptions] Context and WebGL creation properties.
 * @param {boolean} [options.autoDestroy=true] Destroys this context and all of its WebGL resources after all Scenes using the context are destroyed.
 
 * @see {@link http://www.khronos.org/registry/webgl/specs/latest/#5.2|WebGLContextAttributes}
 *
 * @example
 * // Create two Scenes sharing a single WebGL context
 * const context = new Cesium.SharedContext();
 * const scene1 = new Cesium.Scene({
 *   canvas: canvas1,
 *   contextOptions: context,
 * });
 * const scene2 = new Cesium.Scene({
 *   canvas: canvas2,
 *   contextOptions: context,
 * });
 */
function SharedContext(options) {
  this._autoDestroy = options?.autoDestroy ?? true;
  this._canvas = document.createElement("canvas");
  this._context = new Context(this._canvas, clone(options?.contextOptions));
  this._canvases = [];
}
 
/**
 * Creates an instance of {@link Context} that manages the shared WebGL context for a specific canvas.
 * @param {HTMLCanvasElement} canvas The canvas element to which the context will be associated
 * @returns {Context} The created context instance
 * @private
 */
SharedContext.prototype.createSceneContext = function (canvas) {
  const context2d = canvas.getContext("2d", { alpha: true });
 
  //>>includeStart('debug', pragmas.debug);
  if (!context2d) {
    throw new DeveloperError(
      "canvas used with SharedContext must provide a 2d context",
    );
  }
 
  if (this._canvases.includes(canvas)) {
    throw new DeveloperError("canvas is already associated with a scene");
  }
  //>>includeEnd('debug');
 
  const sharedContext = this;
  sharedContext._canvases.push(canvas);
 
  let isDestroyed = false;
  const destroy = function () {
    isDestroyed = true;
    const index = sharedContext._canvases.indexOf(canvas);
    if (-1 !== index) {
      sharedContext._canvases.splice(index, 1);
      if (sharedContext._autoDestroy && sharedContext._canvases.length === 0) {
        sharedContext.destroy();
      }
    }
  };
 
  const beginFrame = function () {
    // Ensure the off-screen canvas is at least as large as the on-screen canvas.
    const sharedCanvas = sharedContext._context.canvas;
 
    const width = this.drawingBufferWidth;
    if (sharedCanvas.width < width) {
      sharedCanvas.width = width;
    }
 
    const height = this.drawingBufferHeight;
    if (sharedCanvas.height < height) {
      sharedCanvas.height = height;
    }
  };
 
  const endFrame = function () {
    // Blit the image from the off-screen canvas to the on-screen canvas.
    const w = this.drawingBufferWidth;
    const h = this.drawingBufferHeight;
    const yOffset = sharedContext._context.canvas.height - h; // drawImage has top as Y=0, GL has bottom as Y=0
    context2d.drawImage(
      sharedContext._context.canvas,
      0,
      yOffset,
      w,
      h,
      0,
      0,
      w,
      h,
    );
 
    // Do normal post-frame cleanup.
    sharedContext._context.endFrame();
  };
 
  const proxy = new Proxy(this._context, {
    get(target, prop, receiver) {
      if (prop === "isDestroyed") {
        return function () {
          return isDestroyed;
        };
      } else if (isDestroyed) {
        //>>includeStart('debug', pragmas.debug);
        throw new DeveloperError(
          "This object was destroyed, i.e., destroy() was called.",
        );
        //>>includeEnd('debug');
      }
 
      switch (prop) {
        case "_canvas":
          return canvas;
        case "destroy":
          return destroy;
        case "drawingBufferWidth":
          return canvas.width;
        case "drawingBufferHeight":
          return canvas.height;
        case "beginFrame":
          return beginFrame;
        case "endFrame":
          return endFrame;
        default:
          return Reflect.get(target, prop, receiver);
      }
    },
  });
 
  return proxy;
};
 
/**
 * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
 * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
 * <br /><br />
 * Once an object is destroyed, it should not be used; calling any function other than
 * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
 * assign the return value (<code>undefined</code>) to the object as done in the example.
 * <br /><br />
 * By default, a SharedContext is destroyed automatically once the last Scene using it is destroyed, in which case it
 * is not necessary to call this method directly.
 *
 * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
 *
 * @example
 * context = context && context.destroy();
 *
 * @see SharedContext#isDestroyed
 */
SharedContext.prototype.destroy = function () {
  this._context.destroy();
  destroyObject(this);
};
 
/**
 * Returns true if this object was destroyed; otherwise, false.
 * <br /><br />
 * If this object was destroyed, it should not be used; calling any function other than
 * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
 *
 * @returns {boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
 *
 * @see SharedContext#destroy
 */
SharedContext.prototype.isDestroyed = function () {
  return false;
};
 
export default SharedContext;