Skip to content

Commit

Permalink
Use per-texture FBOs
Browse files Browse the repository at this point in the history
  • Loading branch information
Lauren Budorick committed Aug 25, 2017
1 parent aaee694 commit 0651dcf
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 48 deletions.
6 changes: 5 additions & 1 deletion src/render/draw_fill_extrusion.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function draw(painter: Painter, source: SourceCache, layer: FillExtrusionStyleLa
}

function drawExtrusionTexture(painter, layer) {
const renderedTexture = painter._prerenderedTextures[layer.id];
const renderedTexture = painter._prerenderedFrames[layer.id];
if (!renderedTexture) return;

const gl = painter.gl;
Expand All @@ -55,6 +55,10 @@ function drawExtrusionTexture(painter, layer) {

renderedTexture.vao.bind(gl, program, renderedTexture.buffer);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

// Since this texture has been rendered, make it available for reuse in the next frame.
painter.viewportFbos.push(renderedTexture);
delete painter._prerenderedFrames[layer.id];
}

function drawExtrusion(painter, source, layer, coord) {
Expand Down
77 changes: 34 additions & 43 deletions src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,8 @@ class Painter {
emptyProgramConfiguration: ProgramConfiguration;
width: number;
height: number;
viewportTextures: Array<RenderTexture>;
_prerenderedTextures: { [string]: ?RenderTexture };
viewportFbo: WebGLFramebuffer;
viewportFbos: Array<RenderTexture>;
_prerenderedFrames: { [string]: ?RenderTexture };
depthRbo: WebGLRenderbuffer;
_depthMask: boolean;
tileExtentBuffer: Buffer;
Expand Down Expand Up @@ -96,8 +95,8 @@ class Painter {
this.gl = gl;
this.transform = transform;
this._tileTextures = {};
this._prerenderedTextures = {};
this.viewportTextures = [];
this._prerenderedFrames = {};
this.viewportFbos = [];

this.frameHistory = new FrameHistory();

Expand Down Expand Up @@ -125,15 +124,13 @@ class Painter {
this.height = height * browser.devicePixelRatio;
gl.viewport(0, 0, this.width, this.height);

for (const texture of this.viewportTextures) {
this.gl.deleteTexture(texture.texture);
}
this.viewportTextures = [];

if (this.viewportFbo) {
this.gl.deleteFramebuffer(this.viewportFbo);
this.viewportFbo = null;
for (const frame of this.viewportFbos) {
this.gl.deleteTexture(frame.texture);
// TODO instead of deleting FBO on resize/destroying whole object,
// should we keep it around and only create a new texture next time?
this.gl.deleteFramebuffer(frame.fbo);
}
this.viewportFbos = [];

if (this.depthRbo) {
this.gl.deleteRenderbuffer(this.depthRbo);
Expand Down Expand Up @@ -277,9 +274,10 @@ class Painter {
const layerIds = this.style._order;

// 3D pass
// We first bind an offscreen framebuffer and render each 3D layer to
// its own texture, which we'll use later in the translucent pass to
// render to the main framebuffer. By doing this before we render to
// We first create a renderbuffer that we'll use to preserve depth
// results across 3D layers, then render each 3D layer to its own
// framebuffer/texture, which we'll use later in the translucent pass
// to render to the main framebuffer. By doing this before we render to
// the main framebuffer we won't have to do an expensive framebuffer
// restore mid-render pass.
// The most important distinction of the 3D pass is that we use the
Expand All @@ -288,9 +286,9 @@ class Painter {
this.renderPass = '3d';
{
const gl = this.gl;
// We'll wait and only attach the framebuffer if we think we're
// We'll wait and only attach the depth renderbuffer if we think we're
// rendering something.
let fboAttached = false;
let rboAttached = false;

let sourceCache;
let coords = [];
Expand All @@ -304,7 +302,7 @@ class Painter {
// Don't bother creating a texture if we're not going
// to render anything: emplace a null value as a marker
// for the regular translucent render pass.
this._prerenderedTextures[layer.id] = null;
this._prerenderedFrames[layer.id] = null;
} else {
if (layer.source !== (sourceCache && sourceCache.id)) {
sourceCache = this.style.sourceCaches[layer.source];
Expand All @@ -320,38 +318,38 @@ class Painter {
}

if (!coords.length) {
this._prerenderedTextures[layer.id] = null;
this._prerenderedFrames[layer.id] = null;
continue;
}

if (!fboAttached) {
// This is the first 3D layer we're rendering: attach
// the framebuffer now.
this._setup3DFramebuffer();
if (!rboAttached) {
// This is the first 3D layer we're rendering: setup the
// depth renderbuffer now.
this._setup3DRenderbuffer();
// Wait to flip the boolean until after we attach the first
// texture and clear the depth buffer a few lines down.
// frame and clear the depth buffer a few lines down.
}

let renderTarget = this.viewportTextures.pop();
let renderTarget = this.viewportFbos.pop();
if (!renderTarget) {
renderTarget = new RenderTexture(this);
}
renderTarget.attachToFramebuffer();
renderTarget.attachRenderbuffer(this.depthRbo);

if (!fboAttached) {
if (!rboAttached) {
this.clearDepth();
fboAttached = true;
rboAttached = true;
}

this.renderLayer(this, (sourceCache: any), layer, coords);

renderTarget.detachFromFramebuffer();
renderTarget.detachRenderbuffer();

this._prerenderedTextures[layer.id] = renderTarget;
this._prerenderedFrames[layer.id] = renderTarget;
}
}

if (fboAttached) gl.bindFramebuffer(gl.FRAMEBUFFER, null);
if (rboAttached) gl.bindRenderbuffer(gl.RENDERBUFFER, null);
}

// Clear buffers in preparation for drawing to the main framebuffer
Expand Down Expand Up @@ -439,21 +437,14 @@ class Painter {
}
}

_setup3DFramebuffer() {
const gl = this.gl;
// All of the 3D textures will use the same framebuffer and depth renderbuffer.
let fbo = this.viewportFbo;
if (!fbo) {
fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
_setup3DRenderbuffer() {
// All of the 3D textures will use the same depth renderbuffer.
if (!this.depthRbo) {
const gl = this.gl;
this.depthRbo = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthRbo);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthRbo);
this.viewportFbo = fbo;
} else {
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/render/render_texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type Painter from './painter';
class RenderTexture {
gl: WebGLRenderingContext;
texture: WebGLTexture;
fbo: WebGLFramebuffer;
buffer: Buffer;
vao: VertexArrayObject;

Expand All @@ -25,6 +26,10 @@ class RenderTexture {

gl.bindTexture(gl.TEXTURE_2D, null);

const fbo = this.fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

const array = new PosArray();
array.emplaceBack(0, 0);
array.emplaceBack(1, 0);
Expand All @@ -34,14 +39,16 @@ class RenderTexture {
this.vao = new VertexArrayObject();
}

attachToFramebuffer() {
attachRenderbuffer(depthRbo: WebGLRenderbuffer) {
const gl = this.gl;
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRbo);
}

detachFromFramebuffer() {
detachRenderbuffer() {
const gl = this.gl;
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
}

Expand Down

0 comments on commit 0651dcf

Please sign in to comment.