Skip to content

Commit

Permalink
only use mipmaps for power-of-two textures (#6444)
Browse files Browse the repository at this point in the history
also:
move texSubImage updating and mipmap generation into Texture

This lets Texture (instead of RasterTileSource) handle the actual gl
calls and dealing with cases where mipmaps can't be used.
  • Loading branch information
ansis authored Apr 19, 2018
1 parent a80519a commit 0a53a14
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 22 deletions.
4 changes: 2 additions & 2 deletions src/render/draw_hillshade.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,10 @@ function prepareHillshade(painter, tile, sourceMaxZoom) {
tile.demTexture = tile.demTexture || painter.getTileTexture(tile.tileSize);
if (tile.demTexture) {
const demTexture = tile.demTexture;
demTexture.update(pixelData, false);
demTexture.update(pixelData, { premultiply: false });
demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
} else {
tile.demTexture = new Texture(context, pixelData, gl.RGBA, false);
tile.demTexture = new Texture(context, pixelData, gl.RGBA, { premultiply: false });
tile.demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
}

Expand Down
54 changes: 38 additions & 16 deletions src/render/texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,49 @@ class Texture {
format: TextureFormat;
filter: ?TextureFilter;
wrap: ?TextureWrap;
useMipmap: boolean;

constructor(context: Context, image: TextureImage, format: TextureFormat, premultiply: ?boolean) {
constructor(context: Context, image: TextureImage, format: TextureFormat, options: ?{ premultiply?: boolean, useMipmap?: boolean }) {
this.context = context;

const {width, height} = image;
this.size = [width, height];
this.format = format;

this.texture = context.gl.createTexture();
this.update(image, premultiply);
this.update(image, options);
}

update(image: TextureImage, premultiply: ?boolean) {
update(image: TextureImage, options: ?{premultiply?: boolean, useMipmap?: boolean}) {
const {width, height} = image;
this.size = [width, height];

const resize = !this.size || this.size[0] !== width || this.size[1] !== height;
const {context} = this;
const {gl} = context;

this.useMipmap = Boolean(options && options.useMipmap);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
context.pixelStoreUnpack.set(1);

if (this.format === gl.RGBA && premultiply !== false) {
context.pixelStoreUnpackPremultiplyAlpha.set(true);
}
if (resize) {
this.size = [width, height];

context.pixelStoreUnpack.set(1);

if (this.format === gl.RGBA && (!options || options.premultiply !== false)) {
context.pixelStoreUnpackPremultiplyAlpha.set(true);
}

if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof HTMLVideoElement || image instanceof ImageData) {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.format, gl.UNSIGNED_BYTE, image);
} else {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, width, height, 0, this.format, gl.UNSIGNED_BYTE, image.data);
}

if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof HTMLVideoElement || image instanceof ImageData) {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.format, gl.UNSIGNED_BYTE, image);
} else {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, width, height, 0, this.format, gl.UNSIGNED_BYTE, image.data);
if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof HTMLVideoElement || image instanceof ImageData) {
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
} else {
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, image.data);
}
}

if (this.useMipmap && this.isSizePowerOfTwo()) {
gl.generateMipmap(gl.TEXTURE_2D);
}
}

Expand All @@ -77,6 +91,10 @@ class Texture {
const {gl} = context;
gl.bindTexture(gl.TEXTURE_2D, this.texture);

if (minFilter === gl.LINEAR_MIPMAP_NEAREST && !this.isSizePowerOfTwo()) {
minFilter = gl.LINEAR;
}

if (filter !== this.filter) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter || filter);
Expand All @@ -90,6 +108,10 @@ class Texture {
}
}

isSizePowerOfTwo() {
return this.size[0] === this.size[1] && (Math.log(this.size[0]) / Math.LN2) % 1 === 0;
}

destroy() {
const {gl} = this.context;
gl.deleteTexture(this.texture);
Expand Down
6 changes: 2 additions & 4 deletions src/source/raster_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,15 @@ class RasterTileSource extends Evented implements Source {
const gl = context.gl;
tile.texture = this.map.painter.getTileTexture(img.width);
if (tile.texture) {
tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, img);
tile.texture.update(img, { useMipmap: true });
} else {
tile.texture = new Texture(context, img, gl.RGBA);
tile.texture = new Texture(context, img, gl.RGBA, { useMipmap: true });
tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);

if (context.extTextureFilterAnisotropic) {
gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax);
}
}
gl.generateMipmap(gl.TEXTURE_2D);

tile.state = 'loaded';

Expand Down

0 comments on commit 0a53a14

Please sign in to comment.