mapbox-gl中是否有每页限制的地图?

时间:2018-06-04 16:21:31

标签: javascript webgl mapbox-gl-js

我尝试使用mapbox-gl和面对同一页面上的17张小地图:

WARNING: Too many active WebGL contexts. Oldest context will be lost.

Uncaught TypeError: Failed to execute 'shaderSource' on 'WebGLRenderingContext': parameter 1 is not of type 'WebGLShader'.
    at new Program (mapbox-gl.js:182)
    at Painter._createProgramCached (mapbox-gl.js:178)
    at Painter.useProgram (mapbox-gl.js:178)
    at setFillProgram (mapbox-gl.js:154)
    at drawFillTile (mapbox-gl.js:154)
    at drawFillTiles (mapbox-gl.js:154)
    at Object.drawFill [as fill] (mapbox-gl.js:154)
    at Painter.renderLayer (mapbox-gl.js:178)
    at Painter.render (mapbox-gl.js:178)
    at e._render (mapbox-gl.js:497)

当我试图在同一页面上有许多谷歌街景画廊时,我遇到了同样的问题,但由于我的街景不应该在同一时刻可见,所以我最终动态地使用相同的街景视图更改地址。

但是对于地图列表要求是要向用户显示许多地图。无法逐一显示它们。不知道我怎么能解决这个问题。

我使用mapbox-gl@0.45.0,并在Mac OS Sierra 10.12.6(16G1036)上使用chrome版本66.0.3359.181(官方版本)(64位)进行测试

1 个答案:

答案 0 :(得分:1)

我猜猜你运气不好。浏览器限制了WebGL实例的数量。 There are workarounds但要使用它们可能需要更改mapbox-gl的实现方式。我建议you ask them如果他们考虑实施其中一个解决方案,假设他们还没有。

还有另外一种可能性,那就是在JavaScript中进行自己的WebGL虚拟化。这可能不是一个好的解决方案,因为它不会跨地图共享资源,而且可能太重了。

在我的脑海中,您必须创建一个屏幕外画布并覆盖HTMLCanvasElement.prototype.getContext,这样当有人创建webgl上下文时,您将返回虚拟上下文。您将包装每个函数,如果该虚拟上下文与上次使用的虚拟上下文不匹配,则您将保存所有webgl状态并恢复新上下文的状态。您还必须保持framebuffers与每个画布的drawingbuffer匹配,在当前帧缓冲区绑定为null时绑定它们,并在画布大小更改后调整它们大小,然后渲染到屏幕外画布然后{当前事件退出时,{1}}到各自的画布。这是最重要的部分。

在半伪代码中



canvas2d.drawImage




您还需要捕获导致呈现的事件,这些事件对于每个应用程序都是唯一的。如果应用程序使用requetsAnimationFrame进行渲染,则可能类似

// This is just off the top of my head and is just pseudo code
// but hopefully gives an idea of how to virtualize WebGL.

const canvasToVirtualContextMap = new Map();
let currentVirtualContext = null;
let sharedWebGLContext;
const baseState = makeDefaultState();

HTMLCanvasElement.prototype.getContext = (function(origFn) {

  return function(type, contextAttributes) {
    if (type === 'webgl') {
      return createOrGetVirtualWebGLContext(this, type, contextAttributes);
    }
    return origFn.call(this, contextAttributes);
  };

}(HTMLCanvasElement.prototype.getContext));

class VirutalWebGLContext {
  constructor(cavnas, contextAttributes) {
    this.canvas = canvas;
    // based on context attributes and canvas.width, canvas.height 
    // create a texture and framebuffer
    this._drawingbufferTexture = ...;
    this._drawingbufferFramebuffer = ...;
    
    // remember all WebGL state (default bindings, default texture units,
    // default attributes and/or vertex shade object, default program,
    // default blend, stencil, zbuffer, culling, viewport etc... state
    this._state = makeDefaultState();
  }
}

function makeDefaultState() {
  const state ={};
  state[WebGLRenderingContext.ARRAY_BUFFER] = null;
  ... tons more ...
}

// copy all WebGL constants and functions to the prototype of
// VirtualWebGLContext

for (let key in WebGLRenderingContext.protoype) {
  const value = WebGLRenderingContext.prototype[key];
  let newValue = value;
  switch (key) {
    case 'bindFramebuffer': 
      newValue = virutalBindFramebuffer;
      break;
    case 'clear':
    case 'drawArrays':
    case 'drawElements':
      newValue = createDrawWrapper(value);
      break;
    default:
      if (typeof value === 'function') {
        newValue = createWrapper(value); 
      }
      break;
   }
   VirtualWebGLContext.prototype[key] = newValue;
}

function virutalBindFramebuffer(bindpoint, framebuffer) {
  if (bindpoint === WebGLRenderingContext.FRAMEBUFFER) {
    if (target === null) {
      // bind our drawingBuffer
      sharedWebGLContext.bindFramebuffer(bindpoint, this._drawingbufferFramebuffer);
    }
  }

  sharedWebGLContext.bindFramebuffer(bindpoint, framebuffer);
}  

function createWrapper(origFn) {
  // lots of optimization could happen here depending on specific functions
  return function(...args) {
    makeCurrentContext(this);
    resizeCanvasIfChanged(this);
    return origFn.call(sharedWebGLContext, ...args);
  };
}

function createDrawWrapper(origFn) {
  const newFn = createWrapper(origFn);
  return function(...args) {
    // a rendering function was called so we need to copy are drawingBuffer
    // to the canvas for this context after the current event.
    this._needComposite = true;
    return newFn.call(this, ...args);
  };
}

function makeCurrentContext(vctx) {
  if (currentVirtualContext === vctx) {
    return;
  }
  
  // save all current WebGL state on the previous current virtual context
  saveAllState(currentVirutalContext._state);
  
  // restore all state for the 
  restoreAllState(vctx._state);
  
  // check if the current state is supposed to be rendering to the canvas.
  // if so bind vctx._drawingbuffer
  
  currentVirtualContext = vctx;
}

function resizeCanvasIfChanged(vctx) {
  if (canvas.width !== vtx._width || canvas.height !== vctx._height) {
    // resize this._drawingBuffer to match the new canvas size
  }  
}

function createOrGetVirtualWebGLContext(canvas, type, contextAttributes) {
  // check if this canvas already has a context
  const existingVirtualCtx = canvasToVirtualContextMap.get(canvas);
  if (existingVirtualCtx) {
    return existingVirtualCtx;
  }
  
  if (!sharedWebGLContext) {
    sharedWebGLContext = document.createElement("canvas").getContext("webgl");
  }
  
  const newVirtualCtx = new VirtualWebGLContext(canvas, contextAttributes);
  canvasToVirtualContextMap.set(canvas, newVirtualCtx);
  
  return newVirtualCtx;   
}

function saveAllState(state) {
  // save all WebGL state (current bindings, current texture units,
  // current attributes and/or vertex shade object, current program,
  // current blend, stencil, zbuffer, culling, viewport etc... state
  state[WebGLRenderingContext.ARRAY_BUFFER] = sharedGLState.getParameter(gl.ARRAY_BUFFER_BINDING);
  state[WebGLRenderingContext.TEXTURE_2D] = sharedGLState.getParameter(gl.TEXTURE_BINDING_2D);
  ... tons more ...
}

function restoreAllState(state) {
  // resture all WebGL state (current bindings, current texture units,
  // current attributes and/or vertex shade object, current program,
  // current blend, stencil, zbuffer, culling, viewport etc... state
  gl.bindArray(gl.ARRAY_BUFFER, state[WebGLRenderingContext.ARRAY_BUFFER]);
  gl.bindTexture(gl.TEXTURE_2D, state[WebGLRenderingContext.TEXTURE_2D]);
  ... tons more ...
}

function renderAllDirtyVirtualCanvas() {
  let setup = false;
  for (const vctx of canvasToVirtualContextMap.values()) {
    if (!vctx._needComposite) {
      continue;
    }
    
    vctx._needComposite = false;
     
    if (!setup) {
      setup = true;
      // save all current WebGL state on the previous current virtual context
      saveAllState(currentVirutalContext._state);
      currentVirutalContext = null;
      
      // set the state back to the default
      restoreAllState(sharedGlContext, baseState);
        
      // setup whatever state we need to render vctx._drawinbufferTexture
      // to the canvas.
      sharedWebGLContext.useProgram(programToRenderCanvas);
      ...
    }
    
    // draw the drawingbuffer's texture to the canvas
    sharedWebGLContext.bindTexture(gl.TEXTURE_2D, vctx._drawingbufferTexture);
    sharedWebGLContext.drawArrays(gl.TRIANGLES, 0, 6);
  }
}

如果应用程序渲染其他事件,比如说mousemove那么可能 像这样的东西

window.requestAnimationFrame = (function(origFn) {

  return function(callback) {
    return origFn.call(window, (time) {
      const result = callback(time);
      renderAllDirtyVirtualCanvases();
      return result;
    };
  };

}(window.requestAnimationFrame));

让我怀疑if someone else has already done this