Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/scene/gsplat-unified/gsplat-renderer.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SEMANTIC_POSITION, SEMANTIC_ATTR13, CULLFACE_NONE } from '../../platform/graphics/constants.js';
import { SEMANTIC_POSITION, SEMANTIC_ATTR13, CULLFACE_NONE, PIXELFORMAT_RGBA16U } from '../../platform/graphics/constants.js';
import { BLEND_NONE, BLEND_PREMULTIPLIED, BLEND_ADDITIVE } from '../constants.js';
import { ShaderMaterial } from '../materials/shader-material.js';
import { GSplatResourceBase } from '../gsplat/gsplat-resource-base.js';
Expand Down Expand Up @@ -83,6 +83,10 @@ class GSplatRenderer {
this._material.setDefine('GSPLAT_WORKBUFFER_DATA', true);
this._material.setDefine('STORAGE_ORDER', device.isWebGPU);

// Check if using RGBA16U format (fallback for when RGBA16F not supported)
const isColorUint = workBuffer.colorTextureFormat === PIXELFORMAT_RGBA16U;
this._material.setDefine('GSPLAT_COLOR_UINT', isColorUint);

// input textures (work buffer textures)
this._material.setParameter('splatColor', workBuffer.colorTexture);
this._material.setParameter('splatTexture0', workBuffer.splatTexture0);
Expand Down
11 changes: 10 additions & 1 deletion src/scene/gsplat-unified/gsplat-work-buffer-render-pass.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CULLFACE_NONE } from '../../platform/graphics/constants.js';
* @import { GSplatInfo } from './gsplat-info.js'
* @import { GraphNode } from '../graph-node.js'
* @import { RenderTarget } from '../../platform/graphics/render-target.js'
* @import { GSplatWorkBuffer } from './gsplat-work-buffer.js'
*/

const _viewMat = new Mat4();
Expand Down Expand Up @@ -39,6 +40,14 @@ class GSplatWorkBufferRenderPass extends RenderPass {
*/
cameraNode = /** @type {any} */ (null);

/** @type {GSplatWorkBuffer} */
workBuffer;

constructor(device, workBuffer) {
super(device);
this.workBuffer = workBuffer;
}

/**
* Initialize the render pass with the specified render target.
*
Expand Down Expand Up @@ -111,7 +120,7 @@ class GSplatWorkBufferRenderPass extends RenderPass {
const { intervals, activeSplats, lineStart, viewport, intervalTexture } = splatInfo;

// quad renderer and material are cached in the resource
const workBufferRenderInfo = resource.getWorkBufferRenderInfo(intervals.length > 0);
const workBufferRenderInfo = resource.getWorkBufferRenderInfo(intervals.length > 0, this.workBuffer.colorTextureFormat);

// Assign material properties to scope
workBufferRenderInfo.material.setParameters(device);
Expand Down
23 changes: 18 additions & 5 deletions src/scene/gsplat-unified/gsplat-work-buffer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Debug } from '../../core/debug.js';
import { ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_R32U, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32U, PIXELFORMAT_RG32U, BUFFERUSAGE_COPY_DST, SEMANTIC_POSITION } from '../../platform/graphics/constants.js';
import { ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_R32U, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA16U, PIXELFORMAT_RGBA32U, PIXELFORMAT_RG32U, BUFFERUSAGE_COPY_DST, SEMANTIC_POSITION } from '../../platform/graphics/constants.js';
import { RenderTarget } from '../../platform/graphics/render-target.js';
import { StorageBuffer } from '../../platform/graphics/storage-buffer.js';
import { Texture } from '../../platform/graphics/texture.js';
Expand Down Expand Up @@ -31,11 +31,18 @@ class WorkBufferRenderInfo {
/** @type {QuadRender} */
quadRender;

constructor(device, key, material) {
constructor(device, key, material, colorTextureFormat) {
this.device = device;
this.material = material;

const clonedDefines = new Map(material.defines);

// when using fallback RGBA16U format
const isColorUint = colorTextureFormat === PIXELFORMAT_RGBA16U;
if (isColorUint) {
clonedDefines.set('GSPLAT_COLOR_UINT', '');
}

const shader = ShaderUtils.createShader(this.device, {
uniqueName: `SplatCopyToWorkBuffer:${key}`,
attributes: { vertex_position: SEMANTIC_POSITION },
Expand All @@ -44,7 +51,7 @@ class WorkBufferRenderInfo {
vertexChunk: 'fullscreenQuadVS',
fragmentGLSL: glslGsplatCopyToWorkBufferPS,
fragmentWGSL: wgslGsplatCopyToWorkBufferPS,
fragmentOutputTypes: ['vec4', 'uvec4', 'uvec2']
fragmentOutputTypes: [isColorUint ? 'uvec4' : 'vec4', 'uvec4', 'uvec2']
});

this.quadRender = new QuadRender(shader);
Expand All @@ -66,6 +73,9 @@ class GSplatWorkBuffer {
/** @type {number} */
id = id++;

/** @type {number} */
colorTextureFormat;

/** @type {Texture} */
colorTexture;

Expand Down Expand Up @@ -96,7 +106,10 @@ class GSplatWorkBuffer {
constructor(device) {
this.device = device;

this.colorTexture = this.createTexture('splatColor', PIXELFORMAT_RGBA16F, 1, 1);
// Detect compatible HDR format for color texture, fallback to RGBA16U if RGBA16F not supported
this.colorTextureFormat = device.getRenderableHdrFormat([PIXELFORMAT_RGBA16F]) || PIXELFORMAT_RGBA16U;

this.colorTexture = this.createTexture('splatColor', this.colorTextureFormat, 1, 1);
this.splatTexture0 = this.createTexture('splatTexture0', PIXELFORMAT_RGBA32U, 1, 1);
this.splatTexture1 = this.createTexture('splatTexture1', PIXELFORMAT_RG32U, 1, 1);

Expand All @@ -118,7 +131,7 @@ class GSplatWorkBuffer {
}

// Create the optimized render pass for batched splat rendering
this.renderPass = new GSplatWorkBufferRenderPass(device);
this.renderPass = new GSplatWorkBufferRenderPass(device, this);
this.renderPass.init(this.renderTarget);
}

Expand Down
5 changes: 3 additions & 2 deletions src/scene/gsplat/gsplat-resource-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ class GSplatResourceBase {
* Get or create a QuadRender for rendering to work buffer.
*
* @param {boolean} useIntervals - Whether to use intervals.
* @param {number} colorTextureFormat - The format of the color texture (RGBA16F or RGBA16U).
* @returns {WorkBufferRenderInfo} The WorkBufferRenderInfo instance.
*/
getWorkBufferRenderInfo(useIntervals) {
getWorkBufferRenderInfo(useIntervals, colorTextureFormat) {

// configure defines to fetch cached data
this.configureMaterialDefines(tempMap);
Expand All @@ -99,7 +100,7 @@ class GSplatResourceBase {
tempMap.forEach((v, k) => material.setDefine(k, v));

// create new cache entry
info = new WorkBufferRenderInfo(this.device, key, material);
info = new WorkBufferRenderInfo(this.device, key, material, colorTextureFormat);
this.workBufferRenderInfos.set(key, info);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ void main(void) {
if (targetIndex >= uActiveSplats) {

// Out of bounds: write zeros
pcFragColor0 = vec4(0.0);
#ifdef GSPLAT_COLOR_UINT
pcFragColor0 = uvec4(0u);
#else
pcFragColor0 = vec4(0.0);
#endif
pcFragColor1 = uvec4(0u);
pcFragColor2 = uvec2(0u);

Expand Down Expand Up @@ -100,7 +104,19 @@ void main(void) {
color.xyz *= uColorMultiply;

// write out results
pcFragColor0 = color;
#ifdef GSPLAT_COLOR_UINT
// Pack RGBA as 4x half-float (16-bit) values for RGBA16U format
uint packed_rg = packHalf2x16(color.rg);
uint packed_ba = packHalf2x16(color.ba);
pcFragColor0 = uvec4(
packed_rg & 0xFFFFu, // R as half
packed_rg >> 16u, // G as half
packed_ba & 0xFFFFu, // B as half
packed_ba >> 16u // A as half
);
#else
pcFragColor0 = color;
#endif
pcFragColor1 = uvec4(floatBitsToUint(modelCenter.x), floatBitsToUint(modelCenter.y), floatBitsToUint(modelCenter.z), packHalf2x16(vec2(covA.z, covB.z)));
pcFragColor2 = uvec2(packHalf2x16(covA.xy), packHalf2x16(covB.xy));
}
Expand Down
17 changes: 15 additions & 2 deletions src/scene/shader-lib/glsl/chunks/gsplat/vert/gsplatWorkBuffer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
export default /* glsl */`
uniform highp usampler2D splatTexture0;
uniform highp usampler2D splatTexture1;
uniform mediump sampler2D splatColor;

#ifdef GSPLAT_COLOR_UINT
uniform highp usampler2D splatColor;
#else
uniform mediump sampler2D splatColor;
#endif

// cached texture fetches
uvec4 cachedSplatTexture0Data;
Expand All @@ -28,6 +33,14 @@ void readCovariance(in SplatSource source, out vec3 cov_A, out vec3 cov_B) {
}

vec4 readColor(in SplatSource source) {
return texelFetch(splatColor, source.uv, 0);
#ifdef GSPLAT_COLOR_UINT
// Unpack RGBA from 4x half-float (16-bit) values stored in RGBA16U format
uvec4 packed = texelFetch(splatColor, source.uv, 0);
uint packed_rg = packed.r | (packed.g << 16u);
uint packed_ba = packed.b | (packed.a << 16u);
return vec4(unpackHalf2x16(packed_rg), unpackHalf2x16(packed_ba));
#else
return texelFetch(splatColor, source.uv, 0);
#endif
}
`;