269 lines
9.8 KiB
JavaScript
269 lines
9.8 KiB
JavaScript
'use strict';
|
|
|
|
var adapter = require('../../../../environment/adapter.js');
|
|
var Extensions = require('../../../../extensions/Extensions.js');
|
|
var Texture = require('../../shared/texture/Texture.js');
|
|
var GlTexture = require('./GlTexture.js');
|
|
var glUploadBufferImageResource = require('./uploaders/glUploadBufferImageResource.js');
|
|
var glUploadCompressedTextureResource = require('./uploaders/glUploadCompressedTextureResource.js');
|
|
var glUploadImageResource = require('./uploaders/glUploadImageResource.js');
|
|
var glUploadVideoResource = require('./uploaders/glUploadVideoResource.js');
|
|
var applyStyleParams = require('./utils/applyStyleParams.js');
|
|
var mapFormatToGlFormat = require('./utils/mapFormatToGlFormat.js');
|
|
var mapFormatToGlInternalFormat = require('./utils/mapFormatToGlInternalFormat.js');
|
|
var mapFormatToGlType = require('./utils/mapFormatToGlType.js');
|
|
require('./utils/unpremultiplyAlpha.js');
|
|
|
|
"use strict";
|
|
const BYTES_PER_PIXEL = 4;
|
|
class GlTextureSystem {
|
|
constructor(renderer) {
|
|
this.managedTextures = [];
|
|
this._glTextures = /* @__PURE__ */ Object.create(null);
|
|
this._glSamplers = /* @__PURE__ */ Object.create(null);
|
|
this._boundTextures = [];
|
|
this._activeTextureLocation = -1;
|
|
this._boundSamplers = /* @__PURE__ */ Object.create(null);
|
|
this._uploads = {
|
|
image: glUploadImageResource.glUploadImageResource,
|
|
buffer: glUploadBufferImageResource.glUploadBufferImageResource,
|
|
video: glUploadVideoResource.glUploadVideoResource,
|
|
compressed: glUploadCompressedTextureResource.glUploadCompressedTextureResource
|
|
};
|
|
// TODO - separate samplers will be a cool thing to add, but not right now!
|
|
this._useSeparateSamplers = false;
|
|
this._renderer = renderer;
|
|
}
|
|
contextChange(gl) {
|
|
this._gl = gl;
|
|
if (!this._mapFormatToInternalFormat) {
|
|
this._mapFormatToInternalFormat = mapFormatToGlInternalFormat.mapFormatToGlInternalFormat(gl, this._renderer.context.extensions);
|
|
this._mapFormatToType = mapFormatToGlType.mapFormatToGlType(gl);
|
|
this._mapFormatToFormat = mapFormatToGlFormat.mapFormatToGlFormat(gl);
|
|
}
|
|
this._glTextures = /* @__PURE__ */ Object.create(null);
|
|
this._glSamplers = /* @__PURE__ */ Object.create(null);
|
|
this._boundSamplers = /* @__PURE__ */ Object.create(null);
|
|
for (let i = 0; i < 16; i++) {
|
|
this.bind(Texture.Texture.EMPTY, i);
|
|
}
|
|
}
|
|
initSource(source) {
|
|
this.bind(source);
|
|
}
|
|
bind(texture, location = 0) {
|
|
const source = texture.source;
|
|
if (texture) {
|
|
this.bindSource(source, location);
|
|
if (this._useSeparateSamplers) {
|
|
this._bindSampler(source.style, location);
|
|
}
|
|
} else {
|
|
this.bindSource(null, location);
|
|
if (this._useSeparateSamplers) {
|
|
this._bindSampler(null, location);
|
|
}
|
|
}
|
|
}
|
|
bindSource(source, location = 0) {
|
|
const gl = this._gl;
|
|
source._touched = this._renderer.textureGC.count;
|
|
if (this._boundTextures[location] !== source) {
|
|
this._boundTextures[location] = source;
|
|
this._activateLocation(location);
|
|
source = source || Texture.Texture.EMPTY.source;
|
|
const glTexture = this.getGlSource(source);
|
|
gl.bindTexture(glTexture.target, glTexture.texture);
|
|
}
|
|
}
|
|
_bindSampler(style, location = 0) {
|
|
const gl = this._gl;
|
|
if (!style) {
|
|
this._boundSamplers[location] = null;
|
|
gl.bindSampler(location, null);
|
|
return;
|
|
}
|
|
const sampler = this._getGlSampler(style);
|
|
if (this._boundSamplers[location] !== sampler) {
|
|
this._boundSamplers[location] = sampler;
|
|
gl.bindSampler(location, sampler);
|
|
}
|
|
}
|
|
unbind(texture) {
|
|
const source = texture.source;
|
|
const boundTextures = this._boundTextures;
|
|
const gl = this._gl;
|
|
for (let i = 0; i < boundTextures.length; i++) {
|
|
if (boundTextures[i] === source) {
|
|
this._activateLocation(i);
|
|
const glTexture = this.getGlSource(source);
|
|
gl.bindTexture(glTexture.target, null);
|
|
boundTextures[i] = null;
|
|
}
|
|
}
|
|
}
|
|
_activateLocation(location) {
|
|
if (this._activeTextureLocation !== location) {
|
|
this._activeTextureLocation = location;
|
|
this._gl.activeTexture(this._gl.TEXTURE0 + location);
|
|
}
|
|
}
|
|
_initSource(source) {
|
|
const gl = this._gl;
|
|
const glTexture = new GlTexture.GlTexture(gl.createTexture());
|
|
glTexture.type = this._mapFormatToType[source.format];
|
|
glTexture.internalFormat = this._mapFormatToInternalFormat[source.format];
|
|
glTexture.format = this._mapFormatToFormat[source.format];
|
|
if (source.autoGenerateMipmaps && (this._renderer.context.supports.nonPowOf2mipmaps || source.isPowerOfTwo)) {
|
|
const biggestDimension = Math.max(source.width, source.height);
|
|
source.mipLevelCount = Math.floor(Math.log2(biggestDimension)) + 1;
|
|
}
|
|
this._glTextures[source.uid] = glTexture;
|
|
if (!this.managedTextures.includes(source)) {
|
|
source.on("update", this.onSourceUpdate, this);
|
|
source.on("resize", this.onSourceUpdate, this);
|
|
source.on("styleChange", this.onStyleChange, this);
|
|
source.on("destroy", this.onSourceDestroy, this);
|
|
source.on("unload", this.onSourceUnload, this);
|
|
source.on("updateMipmaps", this.onUpdateMipmaps, this);
|
|
this.managedTextures.push(source);
|
|
}
|
|
this.onSourceUpdate(source);
|
|
this.updateStyle(source, false);
|
|
return glTexture;
|
|
}
|
|
onStyleChange(source) {
|
|
this.updateStyle(source, false);
|
|
}
|
|
updateStyle(source, firstCreation) {
|
|
const gl = this._gl;
|
|
const glTexture = this.getGlSource(source);
|
|
gl.bindTexture(gl.TEXTURE_2D, glTexture.texture);
|
|
this._boundTextures[this._activeTextureLocation] = source;
|
|
applyStyleParams.applyStyleParams(
|
|
source.style,
|
|
gl,
|
|
source.mipLevelCount > 1,
|
|
this._renderer.context.extensions.anisotropicFiltering,
|
|
"texParameteri",
|
|
gl.TEXTURE_2D,
|
|
// will force a clamp to edge if the texture is not a power of two
|
|
!this._renderer.context.supports.nonPowOf2wrapping && !source.isPowerOfTwo,
|
|
firstCreation
|
|
);
|
|
}
|
|
onSourceUnload(source) {
|
|
const glTexture = this._glTextures[source.uid];
|
|
if (!glTexture)
|
|
return;
|
|
this.unbind(source);
|
|
this._glTextures[source.uid] = null;
|
|
this._gl.deleteTexture(glTexture.texture);
|
|
}
|
|
onSourceUpdate(source) {
|
|
const gl = this._gl;
|
|
const glTexture = this.getGlSource(source);
|
|
gl.bindTexture(gl.TEXTURE_2D, glTexture.texture);
|
|
this._boundTextures[this._activeTextureLocation] = source;
|
|
if (this._uploads[source.uploadMethodId]) {
|
|
this._uploads[source.uploadMethodId].upload(source, glTexture, gl, this._renderer.context.webGLVersion);
|
|
} else {
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, source.pixelWidth, source.pixelHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
}
|
|
if (source.autoGenerateMipmaps && source.mipLevelCount > 1) {
|
|
this.onUpdateMipmaps(source, false);
|
|
}
|
|
}
|
|
onUpdateMipmaps(source, bind = true) {
|
|
if (bind)
|
|
this.bindSource(source, 0);
|
|
const glTexture = this.getGlSource(source);
|
|
this._gl.generateMipmap(glTexture.target);
|
|
}
|
|
onSourceDestroy(source) {
|
|
source.off("destroy", this.onSourceDestroy, this);
|
|
source.off("update", this.onSourceUpdate, this);
|
|
source.off("resize", this.onSourceUpdate, this);
|
|
source.off("unload", this.onSourceUnload, this);
|
|
source.off("styleChange", this.onStyleChange, this);
|
|
source.off("updateMipmaps", this.onUpdateMipmaps, this);
|
|
this.managedTextures.splice(this.managedTextures.indexOf(source), 1);
|
|
this.onSourceUnload(source);
|
|
}
|
|
_initSampler(style) {
|
|
const gl = this._gl;
|
|
const glSampler = this._gl.createSampler();
|
|
this._glSamplers[style._resourceId] = glSampler;
|
|
applyStyleParams.applyStyleParams(
|
|
style,
|
|
gl,
|
|
this._boundTextures[this._activeTextureLocation].mipLevelCount > 1,
|
|
this._renderer.context.extensions.anisotropicFiltering,
|
|
"samplerParameteri",
|
|
glSampler,
|
|
false,
|
|
true
|
|
);
|
|
return this._glSamplers[style._resourceId];
|
|
}
|
|
_getGlSampler(sampler) {
|
|
return this._glSamplers[sampler._resourceId] || this._initSampler(sampler);
|
|
}
|
|
getGlSource(source) {
|
|
return this._glTextures[source.uid] || this._initSource(source);
|
|
}
|
|
generateCanvas(texture) {
|
|
const { pixels, width, height } = this.getPixels(texture);
|
|
const canvas = adapter.DOMAdapter.get().createCanvas();
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
const ctx = canvas.getContext("2d");
|
|
if (ctx) {
|
|
const imageData = ctx.createImageData(width, height);
|
|
imageData.data.set(pixels);
|
|
ctx.putImageData(imageData, 0, 0);
|
|
}
|
|
return canvas;
|
|
}
|
|
getPixels(texture) {
|
|
const resolution = texture.source.resolution;
|
|
const frame = texture.frame;
|
|
const width = Math.max(Math.round(frame.width * resolution), 1);
|
|
const height = Math.max(Math.round(frame.height * resolution), 1);
|
|
const pixels = new Uint8Array(BYTES_PER_PIXEL * width * height);
|
|
const renderer = this._renderer;
|
|
const renderTarget = renderer.renderTarget.getRenderTarget(texture);
|
|
const glRenterTarget = renderer.renderTarget.getGpuRenderTarget(renderTarget);
|
|
const gl = renderer.gl;
|
|
gl.bindFramebuffer(gl.FRAMEBUFFER, glRenterTarget.resolveTargetFramebuffer);
|
|
gl.readPixels(
|
|
Math.round(frame.x * resolution),
|
|
Math.round(frame.y * resolution),
|
|
width,
|
|
height,
|
|
gl.RGBA,
|
|
gl.UNSIGNED_BYTE,
|
|
pixels
|
|
);
|
|
if (false) {
|
|
unpremultiplyAlpha(pixels);
|
|
}
|
|
return { pixels: new Uint8ClampedArray(pixels.buffer), width, height };
|
|
}
|
|
destroy() {
|
|
this.managedTextures.slice().forEach((source) => this.onSourceDestroy(source));
|
|
this.managedTextures = null;
|
|
this._renderer = null;
|
|
}
|
|
}
|
|
/** @ignore */
|
|
GlTextureSystem.extension = {
|
|
type: [
|
|
Extensions.ExtensionType.WebGLSystem
|
|
],
|
|
name: "texture"
|
|
};
|
|
|
|
exports.GlTextureSystem = GlTextureSystem;
|
|
//# sourceMappingURL=GlTextureSystem.js.map
|