Skip to content

Commit

Permalink
calcGPUSize
Browse files Browse the repository at this point in the history
  • Loading branch information
heretique committed Jun 14, 2024
1 parent 685aee8 commit 247bf51
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 74 deletions.
5 changes: 3 additions & 2 deletions examples/src/examples/graphics/texture-array.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,11 @@ assetListLoader.load(() => {

const textureArrayOptions = {
name: 'textureArrayImages',
dimension: pc.TEXTUREDIMENSION_2D_ARRAY,
format: pc.PIXELFORMAT_R8_G8_B8_A8,
width: 1024,
height: 1024,
arrayLength: 4, // array texture with 4 textures
slices: 4, // array texture with 4 textures
magFilter: pc.FILTER_NEAREST,
minFilter: pc.FILTER_NEAREST_MIPMAP_NEAREST,
mipmaps: true,
Expand All @@ -157,7 +158,7 @@ assetListLoader.load(() => {
const mipmaps = generateMipmaps(textureArrayOptions.width, textureArrayOptions.height);
const levels = mipmaps.map((data) => {
const textures = [];
for (let i = 0; i < textureArrayOptions.arrayLength; i++) {
for (let i = 0; i < textureArrayOptions.slices; i++) {
textures.push(data);
}
return textures;
Expand Down
8 changes: 4 additions & 4 deletions src/framework/handlers/texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ const _completePartialMipmapChain = function (texture) {

if (!(texture._format === PIXELFORMAT_RGBA8 ||
texture._format === PIXELFORMAT_RGBA32F) ||
texture._volume ||
texture.volume ||
texture._compressed ||
texture._levels.length === 1 ||
texture._levels.length === requiredMipLevels ||
isHtmlElement(texture._cubemap ? texture._levels[0][0] : texture._levels[0])) {
isHtmlElement(texture.cubemap ? texture._levels[0][0] : texture._levels[0])) {
return;
}

Expand Down Expand Up @@ -98,7 +98,7 @@ const _completePartialMipmapChain = function (texture) {
for (let level = texture._levels.length; level < requiredMipLevels; ++level) {
const width = Math.max(1, texture._width >> (level - 1));
const height = Math.max(1, texture._height >> (level - 1));
if (texture._cubemap) {
if (texture.cubemap) {
const mips = [];
for (let face = 0; face < 6; ++face) {
mips.push(downsample(width, height, texture._levels[level - 1][face]));
Expand All @@ -109,7 +109,7 @@ const _completePartialMipmapChain = function (texture) {
}
}

texture._levelsUpdated = texture._cubemap ? [[true, true, true, true, true, true]] : [true];
texture._levelsUpdated = texture.cubemap ? [[true, true, true, true, true, true]] : [true];
};

/**
Expand Down
3 changes: 2 additions & 1 deletion src/framework/xr/xr-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ class XrView extends EventHandler {

this._textureDepth = new Texture(device, {
format: this._manager.views.depthPixelFormat,
arrayLength: (viewsCount === 1) ? 0 : viewsCount,
array: viewsCount > 1,
slices: viewsCount,
mipmaps: false,
addressU: ADDRESS_CLAMP_TO_EDGE,
addressV: ADDRESS_CLAMP_TO_EDGE,
Expand Down
16 changes: 9 additions & 7 deletions src/platform/graphics/texture-utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Debug } from '../../core/debug.js';
import {
pixelFormatInfo,
PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1
PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1,
TEXTUREDIMENSION_3D
} from './constants.js';

/**
Expand All @@ -26,7 +27,7 @@ class TextureUtils {
*
* @param {number} width - Texture's width.
* @param {number} height - Texture's height.
* @param {number} [depth] - Texture's depth. Defaults to 1.
* @param {number} [depth] - Texture's depth slices. Defaults to 1.
* @returns {number} The number of mip levels required for the texture.
*/
static calcMipLevelsCount(width, height, depth = 1) {
Expand All @@ -38,7 +39,7 @@ class TextureUtils {
*
* @param {number} width - Texture's width.
* @param {number} height - Texture's height.
* @param {number} depth - Texture's depth.
* @param {number} depth - Texture's depth slices.
* @param {number} format - Texture's pixel format PIXELFORMAT_***.
* @returns {number} The number of bytes of GPU memory required for the texture.
* @ignore
Expand Down Expand Up @@ -69,17 +70,18 @@ class TextureUtils {
/**
* Calculate the GPU memory required for a texture.
*
* @param {string} dimension - Texture's dimension
* @param {number} width - Texture's width.
* @param {number} height - Texture's height.
* @param {number} depth - Texture's depth.
* @param {number} slices - Texture's slices.
* @param {number} format - Texture's pixel format PIXELFORMAT_***.
* @param {boolean} mipmaps - True if the texture includes mipmaps, false otherwise.
* @param {boolean} cubemap - True is the texture is a cubemap, false otherwise.
* @returns {number} The number of bytes of GPU memory required for the texture.
* @ignore
*/
static calcGpuSize(width, height, depth, format, mipmaps, cubemap) {
static calcGpuSize(width, height, slices, format, isVolume, mipmaps) {
let result = 0;
let depth = isVolume ? slices : 1;

while (1) {
result += TextureUtils.calcLevelGpuSize(width, height, depth, format);
Expand All @@ -93,7 +95,7 @@ class TextureUtils {
depth = Math.max(depth >> 1, 1);
}

return result * (cubemap ? 6 : 1);
return result * (isVolume ? 1 : slices);
}
}

Expand Down
87 changes: 51 additions & 36 deletions src/platform/graphics/texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import {
TEXTURELOCK_WRITE,
TEXTUREPROJECTION_NONE, TEXTUREPROJECTION_CUBE,
TEXTURETYPE_DEFAULT, TEXTURETYPE_RGBM, TEXTURETYPE_RGBE, TEXTURETYPE_RGBP,
isIntegerPixelFormat, FILTER_NEAREST, TEXTURELOCK_NONE, TEXTURELOCK_READ
isIntegerPixelFormat, FILTER_NEAREST, TEXTURELOCK_NONE, TEXTURELOCK_READ,
TEXTUREDIMENSION_2D,
TEXTUREDIMENSION_3D,
TEXTUREDIMENSION_2D_ARRAY,
TEXTUREDIMENSION_CUBE
} from './constants.js';

let id = 0;
Expand Down Expand Up @@ -101,7 +105,15 @@ class Texture {
* @param {string} [options.name] - The name of the texture. Defaults to null.
* @param {number} [options.width] - The width of the texture in pixels. Defaults to 4.
* @param {number} [options.height] - The height of the texture in pixels. Defaults to 4.
* @param {number} [options.depth] - The number of depth slices in a 3D texture.
* @param {number} [options.slices] - The number of depth slices in a 3D texture, the number of textures
* in a texture array or the number of faces for a cubemap.
* @param {string} [options.dimension] - The texture dimension type. Can be:
* - {@link TEXTUREDIMENSION_2D}
* - {@link TEXTUREDIMENSION_2D_ARRAY}
* - {@link TEXTUREDIMENSION_3D}
* - {@link TEXTUREDIMENSION_CUBE}
* Defaults to {@link TEXTUREDIMENSION_2D}. Alternatively, you can specify the dimension using
* the options.cubemap, options.volume or options.array properties.
* @param {number} [options.format] - The pixel format of the texture. Can be:
*
* - {@link PIXELFORMAT_R8}
Expand Down Expand Up @@ -155,9 +167,8 @@ class Texture {
* texture. Default is true.
* @param {boolean} [options.cubemap] - Specifies whether the texture is to be a cubemap.
* Defaults to false.
* @param {number} [options.arrayLength] - Specifies whether the texture is to be a 2D texture array.
* When passed in as undefined or < 1, this is not an array texture. If >= 1, this is an array texture.
* Defaults to undefined.
* @param {boolean} [options.array] - Specifies whether the texture is to be a 2D texture array.
* Defaults to false.
* @param {boolean} [options.volume] - Specifies whether the texture is to be a 3D volume.
* Defaults to false.
* @param {string} [options.type] - Specifies the texture type. Can be:
Expand Down Expand Up @@ -192,7 +203,7 @@ class Texture {
* Defaults to {@link FUNC_LESS}.
* @param {Uint8Array[]|HTMLCanvasElement[]|HTMLImageElement[]|HTMLVideoElement[]|Uint8Array[][]} [options.levels]
* - Array of Uint8Array or other supported browser interface; or a two-dimensional array
* of Uint8Array if options.arrayLength is defined and greater than zero.
* of Uint8Array if options.dimension is {@link TEXTUREDIMENSION_2D_ARRAY}.
* @param {boolean} [options.storage] - Defines if texture can be used as a storage texture by
* a compute shader. Defaults to false.
* @param {boolean} [options.immediate] - If set and true, the texture will be uploaded to the GPU immediately.
Expand Down Expand Up @@ -221,13 +232,22 @@ class Texture {
Debug.assert(this.device, "Texture constructor requires a graphicsDevice to be valid");
Debug.assert(!options.width || Number.isInteger(options.width), "Texture width must be an integer number, got", options);
Debug.assert(!options.height || Number.isInteger(options.height), "Texture height must be an integer number, got", options);
Debug.assert(!options.depth || Number.isInteger(options.depth), "Texture depth must be an integer number, got", options);
Debug.assert(!options.slices || Number.isInteger(options.slices), "Texture slices must be an integer number, got", options);

this.name = options.name ?? '';

this._dimension = options.dimension ?? TEXTUREDIMENSION_2D;
this._dimension = options.array ? TEXTUREDIMENSION_2D_ARRAY : this._dimension;
this._dimension = options.cubemap ? TEXTUREDIMENSION_CUBE : this._dimension;
this._dimension = options.volume ? TEXTUREDIMENSION_3D : this._dimension;

this._width = Math.floor(options.width ?? 4);
this._height = Math.floor(options.height ?? 4);

this._slices = Math.floor(options.slices ?? (this._dimension === TEXTUREDIMENSION_CUBE ? 6 : 1));

Debug.assert((this._dimension === TEXTUREDIMENSION_CUBE ? this._slices === 6 : true), "Texture cube map must have 6 slices");

this._format = options.format ?? PIXELFORMAT_RGBA8;
this._compressed = isCompressedPixelFormat(this._format);
this._integerFormat = isIntegerPixelFormat(this._format);
Expand All @@ -237,12 +257,7 @@ class Texture {
options.magFilter = FILTER_NEAREST;
}

this._volume = options.volume ?? false;
this._depth = Math.floor(options.depth ?? 1);
this._arrayLength = Math.floor(options.arrayLength ?? 0);

this._storage = options.storage ?? false;
this._cubemap = options.cubemap ?? false;
this._flipY = options.flipY ?? false;
this._premultiplyAlpha = options.premultiplyAlpha ?? false;

Expand All @@ -262,7 +277,7 @@ class Texture {
Debug.assert(!options.hasOwnProperty('swizzleGGGR'), 'Use options.type.');

this.projection = TEXTUREPROJECTION_NONE;
if (this._cubemap) {
if (this.cubemap) {
this.projection = TEXTUREPROJECTION_CUBE;
} else if (options.projection && options.projection !== TEXTUREPROJECTION_CUBE) {
this.projection = options.projection;
Expand All @@ -280,7 +295,7 @@ class Texture {
if (this._levels) {
this.upload(options.immediate ?? false);
} else {
this._levels = this._cubemap ? [[null, null, null, null, null, null]] : [null];
this._levels = this.cubemap ? [[null, null, null, null, null, null]] : [null];
}

// track the texture
Expand Down Expand Up @@ -328,10 +343,10 @@ class Texture {
*
* @param {number} width - The new width of the texture.
* @param {number} height - The new height of the texture.
* @param {number} [depth] - The new depth of the texture. Defaults to 1.
* @param {number} [slices] - The new number of slices for the texture. Defaults to 1.
* @ignore
*/
resize(width, height, depth = 1) {
resize(width, height, slices = 1) {

// destroy texture impl
const device = this.device;
Expand All @@ -340,7 +355,7 @@ class Texture {

this._width = Math.floor(width);
this._height = Math.floor(height);
this._depth = Math.floor(depth);
this._slices = Math.floor(slices);

// re-create the implementation
this.impl = device.createTextureImpl(this);
Expand Down Expand Up @@ -528,7 +543,7 @@ class Texture {
* @type {number}
*/
set addressW(addressW) {
if (!this._volume) {
if (!this.volume) {
Debug.warn("pc.Texture#addressW: Can't set W addressing mode for a non-3D texture.");
return;
}
Expand Down Expand Up @@ -682,7 +697,16 @@ class Texture {
* @type {number}
*/
get depth() {
return this._depth;
return this._dimension === TEXTUREDIMENSION_3D ? this._slices : 1;
}

/**
* The number of textures in a texture array or the number of faces for a cubemap.
*
* @type {number}
*/
get slices() {
return this._slices;
}

/**
Expand Down Expand Up @@ -724,12 +748,12 @@ class Texture {
* @type {boolean}
*/
get cubemap() {
return this._cubemap;
return this._dimension === TEXTUREDIMENSION_CUBE;
}

get gpuSize() {
const mips = this.pot && this._mipmaps && !(this._compressed && this._levels.length === 1);
return TextureUtils.calcGpuSize(this._width, this._height, this._depth, this._format, mips, this._cubemap);
return TextureUtils.calcGpuSize(this._width, this._height, this._slices, this._format, this.volume, mips);
}

/**
Expand All @@ -738,16 +762,7 @@ class Texture {
* @type {boolean}
*/
get array() {
return this._arrayLength > 0;
}

/**
* Returns the number of textures inside this texture if this is a 2D array texture or 0 otherwise.
*
* @type {number}
*/
get arrayLength() {
return this._arrayLength;
return this._dimension === TEXTUREDIMENSION_2D_ARRAY;
}

/**
Expand All @@ -756,7 +771,7 @@ class Texture {
* @type {boolean}
*/
get volume() {
return this._volume;
return this._dimension === TEXTUREDIMENSION_3D;
}

/**
Expand Down Expand Up @@ -822,7 +837,7 @@ class Texture {

// Force a full resubmission of the texture to the GPU (used on a context restore event)
dirtyAll() {
this._levelsUpdated = this._cubemap ? [[true, true, true, true, true, true]] : [true];
this._levelsUpdated = this.cubemap ? [[true, true, true, true, true, true]] : [true];

this._needsUpload = true;
this._needsMipmapsUpload = this._mipmaps;
Expand Down Expand Up @@ -872,7 +887,7 @@ class Texture {
// allocate storage for this mip level
const width = Math.max(1, this._width >> options.level);
const height = Math.max(1, this._height >> options.level);
const depth = Math.max(1, this._depth >> options.level);
const depth = Math.max(1, (this._dimension === TEXTUREDIMENSION_3D ? this._slices : 1) >> options.level);
const data = new ArrayBuffer(TextureUtils.calcLevelGpuSize(width, height, depth, this._format));
levels[options.level] = new (getPixelFormatArrayType(this._format))(data);
}
Expand All @@ -895,7 +910,7 @@ class Texture {
let invalid = false;
let width, height;

if (this._cubemap) {
if (this.cubemap) {
if (source[0]) {
// rely on first face sizes
width = source[0].width || 0;
Expand Down Expand Up @@ -947,7 +962,7 @@ class Texture {
this._height = 4;

// remove levels
if (this._cubemap) {
if (this.cubemap) {
for (let i = 0; i < 6; i++) {
this._levels[mipLevel][i] = null;
this._levelsUpdated[mipLevel][i] = true;
Expand Down
8 changes: 4 additions & 4 deletions src/platform/graphics/webgl/webgl-render-target.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class WebglRenderTarget {
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
attachmentBaseConstant + i,
colorBuffer._cubemap ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + target._face : gl.TEXTURE_2D,
colorBuffer.cubemap ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + target._face : gl.TEXTURE_2D,
colorBuffer.impl._glTexture,
0
);
Expand All @@ -162,11 +162,11 @@ class WebglRenderTarget {
// Attach
if (target._stencil) {
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT,
depthBuffer._cubemap ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + target._face : gl.TEXTURE_2D,
depthBuffer.cubemap ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + target._face : gl.TEXTURE_2D,
target._depthBuffer.impl._glTexture, 0);
} else {
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
depthBuffer._cubemap ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + target._face : gl.TEXTURE_2D,
depthBuffer.cubemap ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + target._face : gl.TEXTURE_2D,
target._depthBuffer.impl._glTexture, 0);
}
} else if (target._depth) {
Expand Down Expand Up @@ -291,7 +291,7 @@ class WebglRenderTarget {
const dstFramebuffer = gl.createFramebuffer();
device.setFramebuffer(dstFramebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
colorBuffer._cubemap ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + target._face : gl.TEXTURE_2D,
colorBuffer.cubemap ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + target._face : gl.TEXTURE_2D,
colorBuffer.impl._glTexture,
0
);
Expand Down
Loading

0 comments on commit 247bf51

Please sign in to comment.