From af8e9ad5b1a2099aaaf5349e7f33068f5a4bf553 Mon Sep 17 00:00:00 2001 From: Martin Valigursky <59932779+mvaligursky@users.noreply.github.com> Date: Fri, 17 Nov 2023 17:20:19 +0000 Subject: [PATCH] New GraphicsDevice.getRenderableHdrFormat function (#5830) * GraphicsDevice.getRenderableHdrFormat function * Update src/platform/graphics/graphics-device.js Co-authored-by: Will Eastcott --------- Co-authored-by: Martin Valigursky Co-authored-by: Will Eastcott --- .../components/camera/post-effect-queue.js | 4 +- src/platform/graphics/blend-state.js | 8 +++ src/platform/graphics/graphics-device.js | 53 ++++++++++++++++++- src/platform/graphics/texture-utils.js | 2 +- .../graphics/webgl/webgl-graphics-device.js | 43 +++------------ .../graphics/webgpu/webgpu-graphics-device.js | 1 + .../graphics/webgpu/webgpu-texture.js | 2 + src/scene/scene.js | 2 +- 8 files changed, 74 insertions(+), 41 deletions(-) diff --git a/src/framework/components/camera/post-effect-queue.js b/src/framework/components/camera/post-effect-queue.js index 1aea0b96d4a..ac9f73bcd3b 100644 --- a/src/framework/components/camera/post-effect-queue.js +++ b/src/framework/components/camera/post-effect-queue.js @@ -1,4 +1,4 @@ -import { ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_RGBA8 } from '../../../platform/graphics/constants.js'; +import { ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F, PIXELFORMAT_RGBA8 } from '../../../platform/graphics/constants.js'; import { DebugGraphics } from '../../../platform/graphics/debug-graphics.js'; import { RenderTarget } from '../../../platform/graphics/render-target.js'; import { Texture } from '../../../platform/graphics/texture.js'; @@ -104,7 +104,7 @@ class PostEffectQueue { _createOffscreenTarget(useDepth, hdr) { const device = this.app.graphicsDevice; - const format = hdr && device.getHdrFormat(false, true, false, false) || PIXELFORMAT_RGBA8; + const format = hdr && device.getRenderableHdrFormat([PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F], true) || PIXELFORMAT_RGBA8; const name = this.camera.entity.name + '-posteffect-' + this.effects.length; const colorBuffer = this._allocateColorBuffer(format, name); diff --git a/src/platform/graphics/blend-state.js b/src/platform/graphics/blend-state.js index 1bbe0bdc8d2..2f35263a376 100644 --- a/src/platform/graphics/blend-state.js +++ b/src/platform/graphics/blend-state.js @@ -251,6 +251,14 @@ class BlendState { * @readonly */ static ALPHABLEND = Object.freeze(new BlendState(true, BLENDEQUATION_ADD, BLENDMODE_SRC_ALPHA, BLENDMODE_ONE_MINUS_SRC_ALPHA)); + + /** + * A blend state that does simple additive blending. + * + * @type {BlendState} + * @readonly + */ + static ADDBLEND = Object.freeze(new BlendState(true, BLENDEQUATION_ADD, BLENDMODE_ONE, BLENDMODE_ONE)); } export { BlendState }; diff --git a/src/platform/graphics/graphics-device.js b/src/platform/graphics/graphics-device.js index bc41008014d..e1ced7f2212 100644 --- a/src/platform/graphics/graphics-device.js +++ b/src/platform/graphics/graphics-device.js @@ -10,7 +10,7 @@ import { BUFFER_STATIC, CULLFACE_BACK, CLEARFLAG_COLOR, CLEARFLAG_DEPTH, - PRIMITIVE_POINTS, PRIMITIVE_TRIFAN, SEMANTIC_POSITION, TYPE_FLOAT32 + PRIMITIVE_POINTS, PRIMITIVE_TRIFAN, SEMANTIC_POSITION, TYPE_FLOAT32, PIXELFORMAT_111110F, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F } from './constants.js'; import { BlendState } from './blend-state.js'; import { DepthState } from './depth-state.js'; @@ -285,7 +285,15 @@ class GraphicsDevice extends EventHandler { * @type {boolean} * @readonly */ - textureFloatFilterable = true; + textureFloatFilterable = false; + + /** + * True if filtering can be applied when sampling 16-bit float textures. + * + * @type {boolean} + * @readonly + */ + textureHalfFloatFilterable = false; /** * A vertex buffer representing a quad. @@ -809,6 +817,47 @@ class GraphicsDevice extends EventHandler { */ frameEnd() { } + + /** + * Get a renderable HDR pixel format supported by the graphics device. + * + * @param {number[]} [formats] - An array of pixel formats to check for support. Can contain: + * + * - {@link PIXELFORMAT_111110F} + * - {@link PIXELFORMAT_RGBA16F} + * - {@link PIXELFORMAT_RGBA32F} + * + * @param {boolean} [filterable] - If true, the format also needs to be filterable. Defaults to + * true. + * @returns {number|undefined} The first supported renderable HDR format or undefined if none is + * supported. + */ + getRenderableHdrFormat(formats = [PIXELFORMAT_111110F, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F], filterable = true) { + for (let i = 0; i < formats.length; i++) { + const format = formats[i]; + switch (format) { + + case PIXELFORMAT_111110F: { + if (this.textureRG11B10Renderable) + return format; + break; + } + + case PIXELFORMAT_RGBA16F: + if (this.textureHalfFloatRenderable && (!filterable || this.textureHalfFloatFilterable)) { + return format; + } + break; + + case PIXELFORMAT_RGBA32F: + if (this.textureFloatRenderable && (!filterable || this.textureFloatFilterable)) { + return format; + } + break; + } + } + return undefined; + } } export { GraphicsDevice }; diff --git a/src/platform/graphics/texture-utils.js b/src/platform/graphics/texture-utils.js index 64f363e9331..b1aed8592bd 100644 --- a/src/platform/graphics/texture-utils.js +++ b/src/platform/graphics/texture-utils.js @@ -30,7 +30,7 @@ class TextureUtils { * @returns {number} The number of mip levels required for the texture. */ static calcMipLevelsCount(width, height, depth = 1) { - return 1 + Math.log2(Math.max(width, height, depth)); + return 1 + Math.floor(Math.log2(Math.max(width, height, depth))); } /** diff --git a/src/platform/graphics/webgl/webgl-graphics-device.js b/src/platform/graphics/webgl/webgl-graphics-device.js index 4a0eed93431..a7938aba14e 100644 --- a/src/platform/graphics/webgl/webgl-graphics-device.js +++ b/src/platform/graphics/webgl/webgl-graphics-device.js @@ -932,6 +932,7 @@ class WebglGraphicsDevice extends GraphicsDevice { this.extStandardDerivatives = true; this.extTextureFloat = true; this.extTextureHalfFloat = true; + this.textureHalfFloatFilterable = true; this.extTextureLod = true; this.extUintElement = true; this.extVertexArrayObject = true; @@ -953,7 +954,6 @@ class WebglGraphicsDevice extends GraphicsDevice { this.extStandardDerivatives = this.getExtension("OES_standard_derivatives"); this.extTextureFloat = this.getExtension("OES_texture_float"); - this.extTextureHalfFloat = this.getExtension("OES_texture_half_float"); this.extTextureLod = this.getExtension('EXT_shader_texture_lod'); this.extUintElement = this.getExtension("OES_element_index_uint"); this.extVertexArrayObject = this.getExtension("OES_vertex_array_object"); @@ -967,11 +967,17 @@ class WebglGraphicsDevice extends GraphicsDevice { } this.extColorBufferFloat = null; this.extDepthTexture = gl.getExtension('WEBGL_depth_texture'); + + this.extTextureHalfFloat = this.getExtension("OES_texture_half_float"); + this.extTextureHalfFloatLinear = this.getExtension("OES_texture_half_float_linear"); + this.textureHalfFloatFilterable = !!this.extTextureHalfFloatLinear; } this.extDebugRendererInfo = this.getExtension('WEBGL_debug_renderer_info'); + this.extTextureFloatLinear = this.getExtension("OES_texture_float_linear"); - this.extTextureHalfFloatLinear = this.getExtension("OES_texture_half_float_linear"); + this.textureFloatFilterable = !!this.extTextureFloatLinear; + this.extFloatBlend = this.getExtension("EXT_float_blend"); this.extTextureFilterAnisotropic = this.getExtension('EXT_texture_filter_anisotropic', 'WEBKIT_EXT_texture_filter_anisotropic'); this.extCompressedTextureETC1 = this.getExtension('WEBGL_compressed_texture_etc1'); @@ -2650,39 +2656,6 @@ class WebglGraphicsDevice extends GraphicsDevice { return true; } - /** - * Get a supported HDR pixel format given a set of hardware support requirements. - * - * @param {boolean} preferLargest - If true, prefer the highest precision format. Otherwise prefer the lowest precision format. - * @param {boolean} renderable - If true, only include pixel formats that can be used as render targets. - * @param {boolean} updatable - If true, only include formats that can be updated by the CPU. - * @param {boolean} filterable - If true, only include formats that support texture filtering. - * - * @returns {number} The HDR pixel format or null if there are none. - * @ignore - */ - getHdrFormat(preferLargest, renderable, updatable, filterable) { - // Note that for WebGL2, PIXELFORMAT_RGB16F and PIXELFORMAT_RGB32F are not renderable according to this: - // https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float - // For WebGL1, only PIXELFORMAT_RGBA16F and PIXELFORMAT_RGBA32F are tested for being renderable. - const f16Valid = this.extTextureHalfFloat && - (!renderable || this.textureHalfFloatRenderable) && - (!updatable || this.textureHalfFloatUpdatable) && - (!filterable || this.extTextureHalfFloatLinear); - const f32Valid = this.extTextureFloat && - (!renderable || this.textureFloatRenderable) && - (!filterable || this.extTextureFloatLinear); - - if (f16Valid && f32Valid) { - return preferLargest ? PIXELFORMAT_RGBA32F : PIXELFORMAT_RGBA16F; - } else if (f16Valid) { - return PIXELFORMAT_RGBA16F; - } else if (f32Valid) { - return PIXELFORMAT_RGBA32F; - } /* else */ - return null; - } - /** * Frees memory from all vertex array objects ever allocated with this device. * diff --git a/src/platform/graphics/webgpu/webgpu-graphics-device.js b/src/platform/graphics/webgpu/webgpu-graphics-device.js index fb14fd7ff54..b548a9d7379 100644 --- a/src/platform/graphics/webgpu/webgpu-graphics-device.js +++ b/src/platform/graphics/webgpu/webgpu-graphics-device.js @@ -151,6 +151,7 @@ class WebgpuGraphicsDevice extends GraphicsDevice { this.extUintElement = true; this.extTextureFloat = true; this.textureFloatRenderable = true; + this.textureHalfFloatFilterable = true; this.extTextureHalfFloat = true; this.textureHalfFloatRenderable = true; this.textureHalfFloatUpdatable = true; diff --git a/src/platform/graphics/webgpu/webgpu-texture.js b/src/platform/graphics/webgpu/webgpu-texture.js index d913af6b34b..8694b368fe4 100644 --- a/src/platform/graphics/webgpu/webgpu-texture.js +++ b/src/platform/graphics/webgpu/webgpu-texture.js @@ -90,6 +90,8 @@ class WebgpuTexture { const wgpu = device.wgpu; const mipLevelCount = texture.requiredMipLevels; + Debug.assert(texture.width > 0 && texture.height > 0, `Invalid texture dimensions ${texture.width}x${texture.height} for texture ${texture.name}`, texture); + this.descr = { size: { width: texture.width, diff --git a/src/scene/scene.js b/src/scene/scene.js index 5d6ed0ae7c5..79faf565b62 100644 --- a/src/scene/scene.js +++ b/src/scene/scene.js @@ -791,7 +791,7 @@ class Scene extends EventHandler { * @type {number} */ get lightmapPixelFormat() { - return this.lightmapHDR && this.device.getHdrFormat(false, true, false, true) || PIXELFORMAT_RGBA8; + return this.lightmapHDR && this.device.getRenderableHdrFormat() || PIXELFORMAT_RGBA8; } }