Skip to content

Commit ffed60e

Browse files
committed
Texture array updates WIP
1 parent 247bf51 commit ffed60e

File tree

5 files changed

+96
-79
lines changed

5 files changed

+96
-79
lines changed

src/framework/handlers/texture.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,18 @@ const _completePartialMipmapChain = function (texture) {
9898
for (let level = texture._levels.length; level < requiredMipLevels; ++level) {
9999
const width = Math.max(1, texture._width >> (level - 1));
100100
const height = Math.max(1, texture._height >> (level - 1));
101-
if (texture.cubemap) {
101+
if (texture.cubemap || texture.array) {
102102
const mips = [];
103-
for (let face = 0; face < 6; ++face) {
104-
mips.push(downsample(width, height, texture._levels[level - 1][face]));
103+
for (let slice = 0; slice < texture.slices; ++slice) {
104+
mips.push(downsample(width, height, texture._levels[level - 1][slice]));
105105
}
106106
texture._levels.push(mips);
107107
} else {
108108
texture._levels.push(downsample(width, height, texture._levels[level - 1]));
109109
}
110110
}
111111

112-
texture._levelsUpdated = texture.cubemap ? [[true, true, true, true, true, true]] : [true];
112+
texture._levelsUpdated = (texture.cubemap || texture.array) ? [Array(texture.slices).fill(true)] : [true];
113113
};
114114

115115
/**

src/platform/graphics/null/null-texture.js

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ class NullTexture {
1212

1313
loseContext() {
1414
}
15+
16+
uploadImmediate(device, texture, immediate) {
17+
}
1518
}
1619

1720
export { NullTexture };

src/platform/graphics/texture-utils.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Debug } from '../../core/debug.js';
22
import {
33
pixelFormatInfo,
4-
PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1,
5-
TEXTUREDIMENSION_3D
4+
PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1
65
} from './constants.js';
76

87
/**
@@ -70,11 +69,11 @@ class TextureUtils {
7069
/**
7170
* Calculate the GPU memory required for a texture.
7271
*
73-
* @param {string} dimension - Texture's dimension
7472
* @param {number} width - Texture's width.
7573
* @param {number} height - Texture's height.
7674
* @param {number} slices - Texture's slices.
7775
* @param {number} format - Texture's pixel format PIXELFORMAT_***.
76+
* @param {boolean} isVolume - True if the texture is a volume texture, false otherwise.
7877
* @param {boolean} mipmaps - True if the texture includes mipmaps, false otherwise.
7978
* @returns {number} The number of bytes of GPU memory required for the texture.
8079
* @ignore

src/platform/graphics/texture.js

+47-34
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,6 @@ class Texture {
7878
/** @protected */
7979
_invalid = false;
8080

81-
/** @protected */
82-
_lockedLevel = -1;
83-
8481
/** @protected */
8582
_lockedMode = TEXTURELOCK_NONE;
8683

@@ -295,7 +292,7 @@ class Texture {
295292
if (this._levels) {
296293
this.upload(options.immediate ?? false);
297294
} else {
298-
this._levels = this.cubemap ? [[null, null, null, null, null, null]] : [null];
295+
this._levels = (this.cubemap || this.array) ? [Array(this._slices).fill(null)] : [null];
299296
}
300297

301298
// track the texture
@@ -837,7 +834,7 @@ class Texture {
837834

838835
// Force a full resubmission of the texture to the GPU (used on a context restore event)
839836
dirtyAll() {
840-
this._levelsUpdated = this.cubemap ? [[true, true, true, true, true, true]] : [true];
837+
this._levelsUpdated = (this.cubemap || this.array) ? [Array(this._slices).fill(true)] : [true];
841838

842839
this._needsUpload = true;
843840
this._needsMipmapsUpload = this._mipmaps;
@@ -854,6 +851,8 @@ class Texture {
854851
* to 0.
855852
* @param {number} [options.face] - If the texture is a cubemap, this is the index of the face
856853
* to lock.
854+
* @param {number} [options.slice] - If the texture is a texture array, this is the index of the
855+
* slice to lock.
857856
* @param {number} [options.mode] - The lock mode. Can be:
858857
* - {@link TEXTURELOCK_READ}
859858
* - {@link TEXTURELOCK_WRITE}
@@ -865,6 +864,7 @@ class Texture {
865864
// Initialize options to some sensible defaults
866865
options.level ??= 0;
867866
options.face ??= 0;
867+
options.slice ??= 0;
868868
options.mode ??= TEXTURELOCK_WRITE;
869869

870870
Debug.assert(
@@ -879,19 +879,38 @@ class Texture {
879879
this
880880
);
881881

882+
Debug.assert(
883+
options.level >= 0 && options.level < this._levels.length,
884+
'Invalid mip level',
885+
this
886+
);
887+
888+
Debug.assert(
889+
((this.cubemap || this.array) && options.mode === TEXTURELOCK_WRITE) ? options.level === 0 : true,
890+
'Only mip level 0 can be locked for writing for cubemaps and texture arrays',
891+
this
892+
);
893+
882894
this._lockedMode = options.mode;
883-
this._lockedLevel = options.level;
884895

885-
const levels = this.cubemap ? this._levels[options.face] : this._levels;
896+
const levels = this.cubemap ? this._levels[options.face] : this.array ? this._levels[options.slice] : this._levels;
886897
if (levels[options.level] === null) {
887898
// allocate storage for this mip level
888899
const width = Math.max(1, this._width >> options.level);
889900
const height = Math.max(1, this._height >> options.level);
890-
const depth = Math.max(1, (this._dimension === TEXTUREDIMENSION_3D ? this._slices : 1) >> options.level);
901+
const depth = Math.max(1, this.depth >> options.level);
891902
const data = new ArrayBuffer(TextureUtils.calcLevelGpuSize(width, height, depth, this._format));
892903
levels[options.level] = new (getPixelFormatArrayType(this._format))(data);
893904
}
894905

906+
if (this._lockedMode === TEXTURELOCK_WRITE) {
907+
if (this.cubemap || this.array) {
908+
this._levelsUpdated[0][options.face ?? options.slice] = true;
909+
} else {
910+
this._levelsUpdated[0] = true;
911+
}
912+
}
913+
895914
return levels[options.level];
896915
}
897916

@@ -901,22 +920,21 @@ class Texture {
901920
*
902921
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement|HTMLCanvasElement[]|HTMLImageElement[]|HTMLVideoElement[]} source - A
903922
* canvas, image or video element, or an array of 6 canvas, image or video elements.
904-
* @param {number} [mipLevel] - A non-negative integer specifying the image level of detail.
905923
* Defaults to 0, which represents the base image source. A level value of N, that is greater
906924
* than 0, represents the image source for the Nth mipmap reduction level.
907925
* @param {boolean} [immediate] - When set to true it forces an immediate upload upon assignment. Defaults to false.
908926
*/
909-
setSource(source, mipLevel = 0, immediate = false) {
927+
setSource(source, immediate = false) {
910928
let invalid = false;
911929
let width, height;
912930

913-
if (this.cubemap) {
931+
if (this.cubemap || this.array) {
914932
if (source[0]) {
915933
// rely on first face sizes
916934
width = source[0].width || 0;
917935
height = source[0].height || 0;
918936

919-
for (let i = 0; i < 6; i++) {
937+
for (let i = 0; i < this._slices; i++) {
920938
const face = source[i];
921939
// cubemap becomes invalid if any condition is not satisfied
922940
if (!face || // face is missing
@@ -934,9 +952,9 @@ class Texture {
934952

935953
if (!invalid) {
936954
// mark levels as updated
937-
for (let i = 0; i < 6; i++) {
938-
if (this._levels[mipLevel][i] !== source[i])
939-
this._levelsUpdated[mipLevel][i] = true;
955+
for (let i = 0; i < this._slices; i++) {
956+
if (this._levels[0][i] !== source[i])
957+
this._levelsUpdated[0][i] = true;
940958
}
941959
}
942960
} else {
@@ -946,8 +964,8 @@ class Texture {
946964

947965
if (!invalid) {
948966
// mark level as updated
949-
if (source !== this._levels[mipLevel])
950-
this._levelsUpdated[mipLevel] = true;
967+
if (source !== this._levels[0])
968+
this._levelsUpdated[0] = true;
951969

952970
width = source.width;
953971
height = source.height;
@@ -962,23 +980,22 @@ class Texture {
962980
this._height = 4;
963981

964982
// remove levels
965-
if (this.cubemap) {
966-
for (let i = 0; i < 6; i++) {
967-
this._levels[mipLevel][i] = null;
968-
this._levelsUpdated[mipLevel][i] = true;
983+
if (this.cubemap || this.array) {
984+
for (let i = 0; i < this._slices; i++) {
985+
this._levels[0][i] = null;
986+
this._levelsUpdated[0][i] = true;
969987
}
970988
} else {
971-
this._levels[mipLevel] = null;
972-
this._levelsUpdated[mipLevel] = true;
989+
this._levels[0] = null;
990+
this._levelsUpdated[0] = true;
973991
}
974992
} else {
975993
// valid texture
976-
if (mipLevel === 0) {
977-
this._width = width;
978-
this._height = height;
979-
}
980994

981-
this._levels[mipLevel] = source;
995+
this._width = width;
996+
this._height = height;
997+
998+
this._levels[0] = source;
982999
}
9831000

9841001
// valid or changed state of validity
@@ -994,14 +1011,11 @@ class Texture {
9941011
* Get the pixel data of the texture. If this is a cubemap then an array of 6 images will be
9951012
* returned otherwise a single image.
9961013
*
997-
* @param {number} [mipLevel] - A non-negative integer specifying the image level of detail.
998-
* Defaults to 0, which represents the base image source. A level value of N, that is greater
999-
* than 0, represents the image source for the Nth mipmap reduction level.
10001014
* @returns {HTMLImageElement} The source image of this texture. Can be null if source not
10011015
* assigned for specific image level.
10021016
*/
1003-
getSource(mipLevel = 0) {
1004-
return this._levels[mipLevel];
1017+
getSource() {
1018+
return this._levels[0];
10051019
}
10061020

10071021
/**
@@ -1017,7 +1031,6 @@ class Texture {
10171031
if (this._lockedMode === TEXTURELOCK_WRITE) {
10181032
this.upload(immediate);
10191033
}
1020-
this._lockedLevel = -1;
10211034
this._lockedMode = TEXTURELOCK_NONE;
10221035
}
10231036

src/platform/graphics/webgl/webgl-texture.js

+40-38
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ class WebglTexture {
407407

408408
const requiredMipLevels = texture.requiredMipLevels;
409409

410-
if (texture.array) {
410+
if (texture.array && !this._glCreated) {
411411
// for texture arrays we reserve the space in advance
412412
gl.texStorage3D(gl.TEXTURE_2D_ARRAY,
413413
requiredMipLevels,
@@ -445,10 +445,10 @@ class WebglTexture {
445445
if (device._isBrowserInterface(mipObject[0])) {
446446
// Upload the image, canvas or video
447447
for (face = 0; face < texture.slices; face++) {
448-
if (!texture._levelsUpdated[0][face])
448+
let src = mipObject[face];
449+
if (!texture._levelsUpdated[0][face] || !src)
449450
continue;
450451

451-
let src = mipObject[face];
452452
// Downsize images that are too large to be used as cube maps
453453
if (device._isImageBrowserInterface(src)) {
454454
if (src.width > device.maxCubeMapSize || src.height > device.maxCubeMapSize) {
@@ -487,10 +487,10 @@ class WebglTexture {
487487
// Upload the byte array
488488
resMult = 1 / Math.pow(2, mipLevel);
489489
for (face = 0; face < texture.slices; face++) {
490-
if (!texture._levelsUpdated[0][face])
490+
const texData = mipObject[face];
491+
if (!texture._levelsUpdated[0][face] || !texData)
491492
continue;
492493

493-
const texData = mipObject[face];
494494
if (texture._compressed) {
495495
if (this._glCreated && texData) {
496496
gl.compressedTexSubImage2D(
@@ -570,38 +570,40 @@ class WebglTexture {
570570
mipObject);
571571
}
572572
} else if (texture.array && typeof mipObject === "object") {
573-
if (texture._slices === mipObject.length) {
574-
if (texture._compressed) {
575-
for (let index = 0; index < texture._slices; index++) {
576-
gl.compressedTexSubImage3D(
577-
gl.TEXTURE_2D_ARRAY,
578-
mipLevel,
579-
0,
580-
0,
581-
index,
582-
Math.max(Math.floor(texture._width * resMult), 1),
583-
Math.max(Math.floor(texture._height * resMult), 1),
584-
1,
585-
this._glFormat,
586-
mipObject[index]
587-
);
588-
}
589-
} else {
590-
for (let index = 0; index < texture.slices; index++) {
591-
gl.texSubImage3D(
592-
gl.TEXTURE_2D_ARRAY,
593-
mipLevel,
594-
0,
595-
0,
596-
index,
597-
Math.max(Math.floor(texture._width * resMult), 1),
598-
Math.max(Math.floor(texture._height * resMult), 1),
599-
1,
600-
this._glFormat,
601-
this._glPixelType,
602-
mipObject[index]
603-
);
604-
}
573+
if (texture._compressed) {
574+
for (let index = 0; index < texture._slices; index++) {
575+
if (!texture._levelsUpdated[0][index] || !mipObject[index])
576+
continue;
577+
gl.compressedTexSubImage3D(
578+
gl.TEXTURE_2D_ARRAY,
579+
mipLevel,
580+
0,
581+
0,
582+
index,
583+
Math.max(Math.floor(texture._width * resMult), 1),
584+
Math.max(Math.floor(texture._height * resMult), 1),
585+
1,
586+
this._glFormat,
587+
mipObject[index]
588+
);
589+
}
590+
} else {
591+
for (let index = 0; index < texture.slices; index++) {
592+
if (!texture._levelsUpdated[0][index] || !mipObject[index])
593+
continue;
594+
gl.texSubImage3D(
595+
gl.TEXTURE_2D_ARRAY,
596+
mipLevel,
597+
0,
598+
0,
599+
index,
600+
Math.max(Math.floor(texture._width * resMult), 1),
601+
Math.max(Math.floor(texture._height * resMult), 1),
602+
1,
603+
this._glFormat,
604+
this._glPixelType,
605+
mipObject[index]
606+
);
605607
}
606608
}
607609
} else {
@@ -716,7 +718,7 @@ class WebglTexture {
716718
}
717719

718720
if (texture._needsUpload) {
719-
if (texture.cubemap) {
721+
if (texture.cubemap || texture.array) {
720722
for (let i = 0; i < texture.slices; i++)
721723
texture._levelsUpdated[0][i] = false;
722724
} else {

0 commit comments

Comments
 (0)