mapbox-gl中每页地图是否有限制? [英] Is there any maps per page limitation in mapbox-gl?

查看:80
本文介绍了mapbox-gl中每页地图是否有限制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用mapbox-gl面向并在同一页面上制作17张小地图:

I'm trying to have 17 small maps on the same page using mapbox-gl and facing:

警告:活动WebGL上下文过多.最早的上下文将丢失.

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)

当我试图在同一页面上放置许多Google Streetview画廊时,我遇到了同样的问题,但是由于我不能同时看到我的街景,所以我动态地使用了相同的街景更改地址.

I had the same issue when i tried to have many google streetview galleries on the same page, but as my streetview shouldn't be visible at the same moment i ended using the same streetview changing address dynamically.

但是对于地图列表,要求是向用户显示许多地图.无法一一展示.不知道如何解决这个问题.

But for maps list requirement is to show that many maps to user. Can't show them one by one. Not sure how i could work out that issue.

我正在使用mapbox-gl@0.45.0,并在Mac OS Sierra 10.12.6(16G1036)的Chrome版本66.0.3359.181(正式版本)(64位)中对其进行了测试

i'm using mapbox-gl@0.45.0, and testing it in chrome Version 66.0.3359.181 (Official Build) (64-bit) on Mac OS Sierra 10.12.6 (16G1036)

推荐答案

我猜你很不走运.浏览器限制了WebGL实例的数量.有解决方法,但要使用它们可能需要更改mapbox-gl的实现方式.我建议您问他们,如果他们考虑采用以下解决方法之一,他们还没有.

I'm going to guess you are out of luck. Browsers limit the number of WebGL instances. There are workarounds but to use them would probably require changes to the way mapbox-gl is implemented. I suggest you ask them if they'd consider implementing one of the workarounds assuming they haven't already.

我想到了另一种可能性,那就是用JavaScript对WebGL进行自己的虚拟化.但这可能不是一个好的解决方案,因为它不会在地图之间共享资源,而且可能太重了.

There is one other possibility that comes to mind and that would be to do your own virtualization of WebGL in JavaScript. That's probably not a good solution though because it wouldn't share resources across maps and it might be too heavy.

在我的头顶上,您必须创建一个屏幕外画布并覆盖 HTMLCanvasElement.prototype.getContext ,以便当有人创建 webgl 上下文时,您返回虚拟上下文.您将包装每个函数,如果该虚拟上下文与上次使用的虚拟上下文不匹配,则将保存所有webgl状态并为新上下文恢复状态.您还必须保留帧缓冲区以匹配每个画布的绘图缓冲区,在当前帧缓冲区绑定为 null 时绑定它们,如果画布大小发生变化,则调整它们的大小,然后渲染到屏幕外画布,然后每当当前事件退出时,分别将 canvas2d.drawImage 移至其各自的画布.这是最后一个最重的部分.

Off the top of my head you'd have to create an offscreen canvas and override HTMLCanvasElement.prototype.getContext so that when someone makes a webgl context you return a virtual context. You'd wrap every function and if that virtual context doesn't match the last used virtual context you'd save all the webgl state and restore the state for the new context. You'd also have to keep framebuffers to match the drawingbuffer for each canvas, bind them when the current framebuffer binding is null and resize them if the canvas sized changed, and then render to the offscreen canvas and then canvas2d.drawImage to their respective canvases anytime the current event exits. It's that last part that would be heaviest.

使用半伪代码

// 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);
  }
}

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

you'd also need to trap events that cause rendering which would be unique to each app. If the app uses requetsAnimationFrame to render then maybe something like

window.requestAnimationFrame = (function(origFn) {

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

}(window.requestAnimationFrame));

如果应用在其他事件上呈现,例如说mousemove,那么也许像这样的东西

If the app renders on other events, like say mousemove then maybe something like this

let someContextNeedsRendering;

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;

    if (!someContextsNeedRendering) {
      someContextsNeedRendering = true;
      setTimeout(dealWithDirtyContexts, 0);
    }

    return newFn.call(this, ...args);
  };
}

function dealWithDirtyContexts() {
  someContextsNeedRendering = false;
  renderAllDirtyVirtualCanvas();
});

让我想知道其他人是否已经做过.

这篇关于mapbox-gl中每页地图是否有限制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆