Vom Anderen PC aus hoch gespielt

This commit is contained in:
ChK
2026-02-01 13:40:05 +01:00
commit 60b1b7591c
1088 changed files with 452896 additions and 0 deletions

View File

@@ -0,0 +1,274 @@
import { Float16BufferAttribute } from 'three';
import { GPUInputStepMode } from './WebGPUConstants.js';
const typedArraysToVertexFormatPrefix = new Map( [
[ Int8Array, [ 'sint8', 'snorm8' ]],
[ Uint8Array, [ 'uint8', 'unorm8' ]],
[ Int16Array, [ 'sint16', 'snorm16' ]],
[ Uint16Array, [ 'uint16', 'unorm16' ]],
[ Int32Array, [ 'sint32', 'snorm32' ]],
[ Uint32Array, [ 'uint32', 'unorm32' ]],
[ Float32Array, [ 'float32', ]],
] );
const typedAttributeToVertexFormatPrefix = new Map( [
[ Float16BufferAttribute, [ 'float16', ]],
] );
const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [
[ Int32Array, 'sint32' ],
[ Uint32Array, 'uint32' ],
[ Float32Array, 'float32' ]
] );
class WebGPUAttributeUtils {
constructor( backend ) {
this.backend = backend;
}
createAttribute( attribute, usage ) {
const bufferAttribute = this._getBufferAttribute( attribute );
const backend = this.backend;
const bufferData = backend.get( bufferAttribute );
let buffer = bufferData.buffer;
if ( buffer === undefined ) {
const device = backend.device;
const array = bufferAttribute.array;
const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
buffer = device.createBuffer( {
label: bufferAttribute.name,
size: size,
usage: usage,
mappedAtCreation: true
} );
new array.constructor( buffer.getMappedRange() ).set( array );
buffer.unmap();
bufferData.buffer = buffer;
}
}
updateAttribute( attribute ) {
const bufferAttribute = this._getBufferAttribute( attribute );
const backend = this.backend;
const device = backend.device;
const buffer = backend.get( bufferAttribute ).buffer;
const array = bufferAttribute.array;
const updateRange = bufferAttribute.updateRange;
if ( updateRange.count === - 1 ) {
// Not using update ranges
device.queue.writeBuffer(
buffer,
0,
array,
0
);
} else {
device.queue.writeBuffer(
buffer,
0,
array,
updateRange.offset * array.BYTES_PER_ELEMENT,
updateRange.count * array.BYTES_PER_ELEMENT
);
updateRange.count = - 1; // reset range
}
}
createShaderVertexBuffers( renderObject ) {
const attributes = renderObject.getAttributes();
const vertexBuffers = new Map();
for ( let slot = 0; slot < attributes.length; slot ++ ) {
const geometryAttribute = attributes[ slot ];
const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;
const bufferAttribute = this._getBufferAttribute( geometryAttribute );
let vertexBufferLayout = vertexBuffers.get( bufferAttribute );
if ( vertexBufferLayout === undefined ) {
let arrayStride, stepMode;
if ( geometryAttribute.isInterleavedBufferAttribute === true ) {
arrayStride = geometryAttribute.data.stride * bytesPerElement;
stepMode = geometryAttribute.data.isInstancedInterleavedBuffer ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
} else {
arrayStride = geometryAttribute.itemSize * bytesPerElement;
stepMode = geometryAttribute.isInstancedBufferAttribute ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
}
vertexBufferLayout = {
arrayStride,
attributes: [],
stepMode
};
vertexBuffers.set( bufferAttribute, vertexBufferLayout );
}
const format = this._getVertexFormat( geometryAttribute );
const offset = ( geometryAttribute.isInterleavedBufferAttribute === true ) ? geometryAttribute.offset * bytesPerElement : 0;
vertexBufferLayout.attributes.push( {
shaderLocation: slot,
offset,
format
} );
}
return Array.from( vertexBuffers.values() );
}
destroyAttribute( attribute ) {
const backend = this.backend;
const data = backend.get( this._getBufferAttribute( attribute ) );
data.buffer.destroy();
backend.delete( attribute );
}
async getArrayBufferAsync( attribute ) {
const backend = this.backend;
const device = backend.device;
const data = backend.get( this._getBufferAttribute( attribute ) );
const bufferGPU = data.buffer;
const size = bufferGPU.size;
let readBufferGPU = data.readBuffer;
let needsUnmap = true;
if ( readBufferGPU === undefined ) {
readBufferGPU = device.createBuffer( {
label: attribute.name,
size,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
} );
needsUnmap = false;
data.readBuffer = readBufferGPU;
}
const cmdEncoder = device.createCommandEncoder( {} );
cmdEncoder.copyBufferToBuffer(
bufferGPU,
0,
readBufferGPU,
0,
size
);
if ( needsUnmap ) readBufferGPU.unmap();
const gpuCommands = cmdEncoder.finish();
device.queue.submit( [ gpuCommands ] );
await readBufferGPU.mapAsync( GPUMapMode.READ );
const arrayBuffer = readBufferGPU.getMappedRange();
return arrayBuffer;
}
_getVertexFormat( geometryAttribute ) {
const { itemSize, normalized } = geometryAttribute;
const ArrayType = geometryAttribute.array.constructor;
const AttributeType = geometryAttribute.constructor;
let format;
if ( itemSize == 1 ) {
format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType );
} else {
const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType );
const prefix = prefixOptions[ normalized ? 1 : 0 ];
if ( prefix ) {
const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize;
const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4;
const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT;
if ( paddedItemSize % 1 ) {
throw new Error( 'THREE.WebGPUAttributeUtils: Bad vertex format item size.' );
}
format = `${prefix}x${paddedItemSize}`;
}
}
if ( ! format ) {
console.error( 'THREE.WebGPUAttributeUtils: Vertex format not supported yet.' );
}
return format;
}
_getBufferAttribute( attribute ) {
if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
return attribute;
}
}
export default WebGPUAttributeUtils;

View File

@@ -0,0 +1,236 @@
import {
GPUTextureAspect, GPUTextureViewDimension, GPUBufferBindingType, GPUTextureSampleType
} from './WebGPUConstants.js';
import { FloatType } from 'three';
class WebGPUBindingUtils {
constructor( backend ) {
this.backend = backend;
}
createBindingsLayout( bindings ) {
const backend = this.backend;
const device = backend.device;
const entries = [];
let index = 0;
for ( const binding of bindings ) {
const bindingGPU = {
binding: index ++,
visibility: binding.visibility
};
if ( binding.isUniformBuffer || binding.isStorageBuffer ) {
const buffer = {}; // GPUBufferBindingLayout
if ( binding.isStorageBuffer ) {
buffer.type = GPUBufferBindingType.Storage;
}
bindingGPU.buffer = buffer;
} else if ( binding.isSampler ) {
const sampler = {}; // GPUSamplerBindingLayout
if ( binding.texture.isDepthTexture ) {
if ( binding.texture.compareFunction !== null ) {
sampler.type = 'comparison';
}
}
bindingGPU.sampler = sampler;
} else if ( binding.isSampledTexture && binding.texture.isVideoTexture ) {
bindingGPU.externalTexture = {}; // GPUExternalTextureBindingLayout
} else if ( binding.isSampledTexture && binding.store ) {
const format = this.backend.get( binding.texture ).texture.format;
bindingGPU.storageTexture = { format }; // GPUStorageTextureBindingLayout
} else if ( binding.isSampledTexture ) {
const texture = {}; // GPUTextureBindingLayout
if ( binding.texture.isDepthTexture ) {
texture.sampleType = GPUTextureSampleType.Depth;
} else if ( binding.texture.isDataTexture && binding.texture.type === FloatType ) {
// @TODO: Add support for this soon: backend.hasFeature( 'float32-filterable' )
texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
}
if ( binding.isSampledCubeTexture ) {
texture.viewDimension = GPUTextureViewDimension.Cube;
}
bindingGPU.texture = texture;
} else {
console.error( 'WebGPUBindingUtils: Unsupported binding "${ binding }".' );
}
entries.push( bindingGPU );
}
return device.createBindGroupLayout( { entries } );
}
createBindings( bindings ) {
const backend = this.backend;
const bindingsData = backend.get( bindings );
// setup (static) binding layout and (dynamic) binding group
const bindLayoutGPU = this.createBindingsLayout( bindings );
const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU );
bindingsData.layout = bindLayoutGPU;
bindingsData.group = bindGroupGPU;
bindingsData.bindings = bindings;
}
updateBinding( binding ) {
const backend = this.backend;
const device = backend.device;
const buffer = binding.buffer;
const bufferGPU = backend.get( binding ).buffer;
device.queue.writeBuffer( bufferGPU, 0, buffer, 0 );
}
createBindGroup( bindings, layoutGPU ) {
const backend = this.backend;
const device = backend.device;
let bindingPoint = 0;
const entriesGPU = [];
for ( const binding of bindings ) {
if ( binding.isUniformBuffer ) {
const bindingData = backend.get( binding );
if ( bindingData.buffer === undefined ) {
const byteLength = binding.byteLength;
const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
const bufferGPU = device.createBuffer( {
label: 'bindingBuffer',
size: byteLength,
usage: usage
} );
bindingData.buffer = bufferGPU;
}
entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } );
} else if ( binding.isStorageBuffer ) {
const bindingData = backend.get( binding );
if ( bindingData.buffer === undefined ) {
const attribute = binding.attribute;
//const usage = GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | /*GPUBufferUsage.COPY_SRC |*/ GPUBufferUsage.COPY_DST;
//backend.attributeUtils.createAttribute( attribute, usage ); // @TODO: Move it to universal renderer
bindingData.buffer = backend.get( attribute ).buffer;
}
entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } );
} else if ( binding.isSampler ) {
const textureGPU = backend.get( binding.texture );
entriesGPU.push( { binding: bindingPoint, resource: textureGPU.sampler } );
} else if ( binding.isSampledTexture ) {
const textureData = backend.get( binding.texture );
let dimensionViewGPU;
if ( binding.isSampledCubeTexture ) {
dimensionViewGPU = GPUTextureViewDimension.Cube;
} else {
dimensionViewGPU = GPUTextureViewDimension.TwoD;
}
let resourceGPU;
if ( textureData.externalTexture !== undefined ) {
resourceGPU = device.importExternalTexture( { source: textureData.externalTexture } );
} else {
const aspectGPU = GPUTextureAspect.All;
resourceGPU = textureData.texture.createView( { aspect: aspectGPU, dimension: dimensionViewGPU } );
}
entriesGPU.push( { binding: bindingPoint, resource: resourceGPU } );
}
bindingPoint ++;
}
return device.createBindGroup( {
layout: layoutGPU,
entries: entriesGPU
} );
}
}
export default WebGPUBindingUtils;

View File

@@ -0,0 +1,324 @@
export const GPUPrimitiveTopology = {
PointList: 'point-list',
LineList: 'line-list',
LineStrip: 'line-strip',
TriangleList: 'triangle-list',
TriangleStrip: 'triangle-strip',
};
export const GPUCompareFunction = {
Never: 'never',
Less: 'less',
Equal: 'equal',
LessEqual: 'less-equal',
Greater: 'greater',
NotEqual: 'not-equal',
GreaterEqual: 'greater-equal',
Always: 'always'
};
export const GPUStoreOp = {
Store: 'store',
Discard: 'discard'
};
export const GPULoadOp = {
Load: 'load',
Clear: 'clear'
};
export const GPUFrontFace = {
CCW: 'ccw',
CW: 'cw'
};
export const GPUCullMode = {
None: 'none',
Front: 'front',
Back: 'back'
};
export const GPUIndexFormat = {
Uint16: 'uint16',
Uint32: 'uint32'
};
export const GPUVertexFormat = {
Uint8x2: 'uint8x2',
Uint8x4: 'uint8x4',
Sint8x2: 'sint8x2',
Sint8x4: 'sint8x4',
Unorm8x2: 'unorm8x2',
Unorm8x4: 'unorm8x4',
Snorm8x2: 'snorm8x2',
Snorm8x4: 'snorm8x4',
Uint16x2: 'uint16x2',
Uint16x4: 'uint16x4',
Sint16x2: 'sint16x2',
Sint16x4: 'sint16x4',
Unorm16x2: 'unorm16x2',
Unorm16x4: 'unorm16x4',
Snorm16x2: 'snorm16x2',
Snorm16x4: 'snorm16x4',
Float16x2: 'float16x2',
Float16x4: 'float16x4',
Float32: 'float32',
Float32x2: 'float32x2',
Float32x3: 'float32x3',
Float32x4: 'float32x4',
Uint32: 'uint32',
Uint32x2: 'uint32x2',
Uint32x3: 'uint32x3',
Uint32x4: 'uint32x4',
Sint32: 'sint32',
Sint32x2: 'sint32x2',
Sint32x3: 'sint32x3',
Sint32x4: 'sint32x4'
};
export const GPUTextureFormat = {
// 8-bit formats
R8Unorm: 'r8unorm',
R8Snorm: 'r8snorm',
R8Uint: 'r8uint',
R8Sint: 'r8sint',
// 16-bit formats
R16Uint: 'r16uint',
R16Sint: 'r16sint',
R16Float: 'r16float',
RG8Unorm: 'rg8unorm',
RG8Snorm: 'rg8snorm',
RG8Uint: 'rg8uint',
RG8Sint: 'rg8sint',
// 32-bit formats
R32Uint: 'r32uint',
R32Sint: 'r32sint',
R32Float: 'r32float',
RG16Uint: 'rg16uint',
RG16Sint: 'rg16sint',
RG16Float: 'rg16float',
RGBA8Unorm: 'rgba8unorm',
RGBA8UnormSRGB: 'rgba8unorm-srgb',
RGBA8Snorm: 'rgba8snorm',
RGBA8Uint: 'rgba8uint',
RGBA8Sint: 'rgba8sint',
BGRA8Unorm: 'bgra8unorm',
BGRA8UnormSRGB: 'bgra8unorm-srgb',
// Packed 32-bit formats
RGB9E5UFloat: 'rgb9e5ufloat',
RGB10A2Unorm: 'rgb10a2unorm',
RG11B10uFloat: 'rgb10a2unorm',
// 64-bit formats
RG32Uint: 'rg32uint',
RG32Sint: 'rg32sint',
RG32Float: 'rg32float',
RGBA16Uint: 'rgba16uint',
RGBA16Sint: 'rgba16sint',
RGBA16Float: 'rgba16float',
// 128-bit formats
RGBA32Uint: 'rgba32uint',
RGBA32Sint: 'rgba32sint',
RGBA32Float: 'rgba32float',
// Depth and stencil formats
Stencil8: 'stencil8',
Depth16Unorm: 'depth16unorm',
Depth24Plus: 'depth24plus',
Depth24PlusStencil8: 'depth24plus-stencil8',
Depth32Float: 'depth32float',
// 'depth32float-stencil8' extension
Depth32FloatStencil8: 'depth32float-stencil8',
// BC compressed formats usable if 'texture-compression-bc' is both
// supported by the device/user agent and enabled in requestDevice.
BC1RGBAUnorm: 'bc1-rgba-unorm',
BC1RGBAUnormSRGB: 'bc1-rgba-unorm-srgb',
BC2RGBAUnorm: 'bc2-rgba-unorm',
BC2RGBAUnormSRGB: 'bc2-rgba-unorm-srgb',
BC3RGBAUnorm: 'bc3-rgba-unorm',
BC3RGBAUnormSRGB: 'bc3-rgba-unorm-srgb',
BC4RUnorm: 'bc4-r-unorm',
BC4RSnorm: 'bc4-r-snorm',
BC5RGUnorm: 'bc5-rg-unorm',
BC5RGSnorm: 'bc5-rg-snorm',
BC6HRGBUFloat: 'bc6h-rgb-ufloat',
BC6HRGBFloat: 'bc6h-rgb-float',
BC7RGBAUnorm: 'bc7-rgba-unorm',
BC7RGBAUnormSRGB: 'bc7-rgba-srgb',
// ETC2 compressed formats usable if 'texture-compression-etc2' is both
// supported by the device/user agent and enabled in requestDevice.
ETC2RGB8Unorm: 'etc2-rgb8unorm',
ETC2RGB8UnormSRGB: 'etc2-rgb8unorm-srgb',
ETC2RGB8A1Unorm: 'etc2-rgb8a1unorm',
ETC2RGB8A1UnormSRGB: 'etc2-rgb8a1unorm-srgb',
ETC2RGBA8Unorm: 'etc2-rgba8unorm',
ETC2RGBA8UnormSRGB: 'etc2-rgba8unorm-srgb',
EACR11Unorm: 'eac-r11unorm',
EACR11Snorm: 'eac-r11snorm',
EACRG11Unorm: 'eac-rg11unorm',
EACRG11Snorm: 'eac-rg11snorm',
// ASTC compressed formats usable if 'texture-compression-astc' is both
// supported by the device/user agent and enabled in requestDevice.
ASTC4x4Unorm: 'astc-4x4-unorm',
ASTC4x4UnormSRGB: 'astc-4x4-unorm-srgb',
ASTC5x4Unorm: 'astc-5x4-unorm',
ASTC5x4UnormSRGB: 'astc-5x4-unorm-srgb',
ASTC5x5Unorm: 'astc-5x5-unorm',
ASTC5x5UnormSRGB: 'astc-5x5-unorm-srgb',
ASTC6x5Unorm: 'astc-6x5-unorm',
ASTC6x5UnormSRGB: 'astc-6x5-unorm-srgb',
ASTC6x6Unorm: 'astc-6x6-unorm',
ASTC6x6UnormSRGB: 'astc-6x6-unorm-srgb',
ASTC8x5Unorm: 'astc-8x5-unorm',
ASTC8x5UnormSRGB: 'astc-8x5-unorm-srgb',
ASTC8x6Unorm: 'astc-8x6-unorm',
ASTC8x6UnormSRGB: 'astc-8x6-unorm-srgb',
ASTC8x8Unorm: 'astc-8x8-unorm',
ASTC8x8UnormSRGB: 'astc-8x8-unorm-srgb',
ASTC10x5Unorm: 'astc-10x5-unorm',
ASTC10x5UnormSRGB: 'astc-10x5-unorm-srgb',
ASTC10x6Unorm: 'astc-10x6-unorm',
ASTC10x6UnormSRGB: 'astc-10x6-unorm-srgb',
ASTC10x8Unorm: 'astc-10x8-unorm',
ASTC10x8UnormSRGB: 'astc-10x8-unorm-srgb',
ASTC10x10Unorm: 'astc-10x10-unorm',
ASTC10x10UnormSRGB: 'astc-10x10-unorm-srgb',
ASTC12x10Unorm: 'astc-12x10-unorm',
ASTC12x10UnormSRGB: 'astc-12x10-unorm-srgb',
ASTC12x12Unorm: 'astc-12x12-unorm',
ASTC12x12UnormSRGB: 'astc-12x12-unorm-srgb',
};
export const GPUAddressMode = {
ClampToEdge: 'clamp-to-edge',
Repeat: 'repeat',
MirrorRepeat: 'mirror-repeat'
};
export const GPUFilterMode = {
Linear: 'linear',
Nearest: 'nearest'
};
export const GPUBlendFactor = {
Zero: 'zero',
One: 'one',
Src: 'src',
OneMinusSrc: 'one-minus-src',
SrcAlpha: 'src-alpha',
OneMinusSrcAlpha: 'one-minus-src-alpha',
Dst: 'dst',
OneMinusDstColor: 'one-minus-dst',
DstAlpha: 'dst-alpha',
OneMinusDstAlpha: 'one-minus-dst-alpha',
SrcAlphaSaturated: 'src-alpha-saturated',
Constant: 'constant',
OneMinusConstant: 'one-minus-constant'
};
export const GPUBlendOperation = {
Add: 'add',
Subtract: 'subtract',
ReverseSubtract: 'reverse-subtract',
Min: 'min',
Max: 'max'
};
export const GPUColorWriteFlags = {
None: 0,
Red: 0x1,
Green: 0x2,
Blue: 0x4,
Alpha: 0x8,
All: 0xF
};
export const GPUStencilOperation = {
Keep: 'keep',
Zero: 'zero',
Replace: 'replace',
Invert: 'invert',
IncrementClamp: 'increment-clamp',
DecrementClamp: 'decrement-clamp',
IncrementWrap: 'increment-wrap',
DecrementWrap: 'decrement-wrap'
};
export const GPUBufferBindingType = {
Uniform: 'uniform',
Storage: 'storage',
ReadOnlyStorage: 'read-only-storage'
};
export const GPUSamplerBindingType = {
Filtering: 'filtering',
NonFiltering: 'non-filtering',
Comparison: 'comparison'
};
export const GPUTextureSampleType = {
Float: 'float',
UnfilterableFloat: 'unfilterable-float',
Depth: 'depth',
SInt: 'sint',
UInt: 'uint'
};
export const GPUTextureDimension = {
OneD: '1d',
TwoD: '2d',
ThreeD: '3d'
};
export const GPUTextureViewDimension = {
OneD: '1d',
TwoD: '2d',
TwoDArray: '2d-array',
Cube: 'cube',
CubeArray: 'cube-array',
ThreeD: '3d'
};
export const GPUTextureAspect = {
All: 'all',
StencilOnly: 'stencil-only',
DepthOnly: 'depth-only'
};
export const GPUInputStepMode = {
Vertex: 'vertex',
Instance: 'instance'
};
export const GPUFeatureName = {
DepthClipControl: 'depth-clip-control',
Depth32FloatStencil8: 'depth32float-stencil8',
TextureCompressionBC: 'texture-compression-bc',
TextureCompressionETC2: 'texture-compression-etc2',
TextureCompressionASTC: 'texture-compression-astc',
TimestampQuery: 'timestamp-query',
IndirectFirstInstance: 'indirect-first-instance',
ShaderF16: 'shader-f16',
RG11B10UFloat: 'rg11b10ufloat-renderable',
BGRA8UNormStorage: 'bgra8unorm-storage',
Float32Filterable: 'float32-filterable'
};

View File

@@ -0,0 +1,558 @@
import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../common/Constants.js';
import {
GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat, GPUStencilOperation
} from './WebGPUConstants.js';
import {
FrontSide, BackSide, DoubleSide,
NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth,
NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending,
ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstColorFactor,
OneMinusDstColorFactor, DstAlphaFactor, OneMinusDstAlphaFactor, SrcAlphaSaturateFactor,
AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation,
KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp,
NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc
} from 'three';
class WebGPUPipelineUtils {
constructor( backend ) {
this.backend = backend;
}
createRenderPipeline( renderObject ) {
const { object, material, geometry, pipeline } = renderObject;
const { vertexProgram, fragmentProgram } = pipeline;
const backend = this.backend;
const device = backend.device;
const utils = backend.utils;
const pipelineData = backend.get( pipeline );
const bindingsData = backend.get( renderObject.getBindings() );
// vertex buffers
const vertexBuffers = backend.attributeUtils.createShaderVertexBuffers( renderObject );
// blending
let blending;
if ( material.transparent === true && material.blending !== NoBlending ) {
blending = this._getBlending( material );
}
// stencil
let stencilFront = {};
if ( material.stencilWrite === true ) {
stencilFront = {
compare: this._getStencilCompare( material ),
failOp: this._getStencilOperation( material.stencilFail ),
depthFailOp: this._getStencilOperation( material.stencilZFail ),
passOp: this._getStencilOperation( material.stencilZPass )
};
}
const colorWriteMask = this._getColorWriteMask( material );
const targets = [];
if ( renderObject.context.textures !== null ) {
const textures = renderObject.context.textures;
for ( let i = 0; i < textures.length; i ++ ) {
const colorFormat = utils.getTextureFormatGPU( textures[ i ] );
targets.push( {
format: colorFormat,
blend: blending,
writeMask: colorWriteMask
} );
}
} else {
const colorFormat = utils.getCurrentColorFormat( renderObject.context );
targets.push( {
format: colorFormat,
blend: blending,
writeMask: colorWriteMask
} );
}
const vertexModule = backend.get( vertexProgram ).module;
const fragmentModule = backend.get( fragmentProgram ).module;
const primitiveState = this._getPrimitiveState( object, geometry, material );
const depthCompare = this._getDepthCompare( material );
const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
const sampleCount = utils.getSampleCount( renderObject.context );
pipelineData.pipeline = device.createRenderPipeline( {
vertex: Object.assign( {}, vertexModule, { buffers: vertexBuffers } ),
fragment: Object.assign( {}, fragmentModule, { targets } ),
primitive: primitiveState,
depthStencil: {
format: depthStencilFormat,
depthWriteEnabled: material.depthWrite,
depthCompare: depthCompare,
stencilFront: stencilFront,
stencilBack: {}, // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used)
stencilReadMask: material.stencilFuncMask,
stencilWriteMask: material.stencilWriteMask
},
multisample: {
count: sampleCount,
alphaToCoverageEnabled: material.alphaToCoverage
},
layout: device.createPipelineLayout( {
bindGroupLayouts: [ bindingsData.layout ]
} )
} );
}
createComputePipeline( pipeline, bindings ) {
const backend = this.backend;
const device = backend.device;
const computeProgram = backend.get( pipeline.computeProgram ).module;
const pipelineGPU = backend.get( pipeline );
const bindingsData = backend.get( bindings );
pipelineGPU.pipeline = device.createComputePipeline( {
compute: computeProgram,
layout: device.createPipelineLayout( {
bindGroupLayouts: [ bindingsData.layout ]
} )
} );
}
_getBlending( material ) {
let color, alpha;
const blending = material.blending;
if ( blending === CustomBlending ) {
const blendSrcAlpha = material.blendSrcAlpha !== null ? material.blendSrcAlpha : GPUBlendFactor.One;
const blendDstAlpha = material.blendDstAlpha !== null ? material.blendDstAlpha : GPUBlendFactor.Zero;
const blendEquationAlpha = material.blendEquationAlpha !== null ? material.blendEquationAlpha : GPUBlendFactor.Add;
color = {
srcFactor: this._getBlendFactor( material.blendSrc ),
dstFactor: this._getBlendFactor( material.blendDst ),
operation: this._getBlendOperation( material.blendEquation )
};
alpha = {
srcFactor: this._getBlendFactor( blendSrcAlpha ),
dstFactor: this._getBlendFactor( blendDstAlpha ),
operation: this._getBlendOperation( blendEquationAlpha )
};
} else {
const premultipliedAlpha = material.premultipliedAlpha;
const setBlend = ( srcRGB, dstRGB, srcAlpha, dstAlpha ) => {
color = {
srcFactor: srcRGB,
dstFactor: dstRGB,
operation: GPUBlendOperation.Add
};
alpha = {
srcFactor: srcAlpha,
dstFactor: dstAlpha,
operation: GPUBlendOperation.Add
};
};
if ( premultipliedAlpha ) {
switch ( blending ) {
case NormalBlending:
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.One, GPUBlendFactor.OneMinusSrcAlpha );
break;
case AdditiveBlending:
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.One, GPUBlendFactor.One, GPUBlendFactor.One );
break;
case SubtractiveBlending:
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One );
break;
case MultiplyBlending:
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.Src, GPUBlendFactor.Zero, GPUBlendFactor.SrcAlpha );
break;
}
} else {
switch ( blending ) {
case NormalBlending:
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.One, GPUBlendFactor.OneMinusSrcAlpha );
break;
case AdditiveBlending:
setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.One, GPUBlendFactor.SrcAlpha, GPUBlendFactor.One );
break;
case SubtractiveBlending:
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One );
break;
case MultiplyBlending:
setBlend( GPUBlendFactor.Zero, GPUBlendFactor.Src, GPUBlendFactor.Zero, GPUBlendFactor.Src );
break;
}
}
}
if ( color !== undefined && alpha !== undefined ) {
return { color, alpha };
} else {
console.error( 'THREE.WebGPURenderer: Invalid blending: ', blending );
}
}
_getBlendFactor( blend ) {
let blendFactor;
switch ( blend ) {
case ZeroFactor:
blendFactor = GPUBlendFactor.Zero;
break;
case OneFactor:
blendFactor = GPUBlendFactor.One;
break;
case SrcColorFactor:
blendFactor = GPUBlendFactor.Src;
break;
case OneMinusSrcColorFactor:
blendFactor = GPUBlendFactor.OneMinusSrc;
break;
case SrcAlphaFactor:
blendFactor = GPUBlendFactor.SrcAlpha;
break;
case OneMinusSrcAlphaFactor:
blendFactor = GPUBlendFactor.OneMinusSrcAlpha;
break;
case DstColorFactor:
blendFactor = GPUBlendFactor.Dst;
break;
case OneMinusDstColorFactor:
blendFactor = GPUBlendFactor.OneMinusDstColor;
break;
case DstAlphaFactor:
blendFactor = GPUBlendFactor.DstAlpha;
break;
case OneMinusDstAlphaFactor:
blendFactor = GPUBlendFactor.OneMinusDstAlpha;
break;
case SrcAlphaSaturateFactor:
blendFactor = GPUBlendFactor.SrcAlphaSaturated;
break;
case BlendColorFactor:
blendFactor = GPUBlendFactor.Constant;
break;
case OneMinusBlendColorFactor:
blendFactor = GPUBlendFactor.OneMinusConstant;
break;
default:
console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend );
}
return blendFactor;
}
_getStencilCompare( material ) {
let stencilCompare;
const stencilFunc = material.stencilFunc;
switch ( stencilFunc ) {
case NeverStencilFunc:
stencilCompare = GPUCompareFunction.Never;
break;
case AlwaysStencilFunc:
stencilCompare = GPUCompareFunction.Always;
break;
case LessStencilFunc:
stencilCompare = GPUCompareFunction.Less;
break;
case LessEqualStencilFunc:
stencilCompare = GPUCompareFunction.LessEqual;
break;
case EqualStencilFunc:
stencilCompare = GPUCompareFunction.Equal;
break;
case GreaterEqualStencilFunc:
stencilCompare = GPUCompareFunction.GreaterEqual;
break;
case GreaterStencilFunc:
stencilCompare = GPUCompareFunction.Greater;
break;
case NotEqualStencilFunc:
stencilCompare = GPUCompareFunction.NotEqual;
break;
default:
console.error( 'THREE.WebGPURenderer: Invalid stencil function.', stencilFunc );
}
return stencilCompare;
}
_getStencilOperation( op ) {
let stencilOperation;
switch ( op ) {
case KeepStencilOp:
stencilOperation = GPUStencilOperation.Keep;
break;
case ZeroStencilOp:
stencilOperation = GPUStencilOperation.Zero;
break;
case ReplaceStencilOp:
stencilOperation = GPUStencilOperation.Replace;
break;
case InvertStencilOp:
stencilOperation = GPUStencilOperation.Invert;
break;
case IncrementStencilOp:
stencilOperation = GPUStencilOperation.IncrementClamp;
break;
case DecrementStencilOp:
stencilOperation = GPUStencilOperation.DecrementClamp;
break;
case IncrementWrapStencilOp:
stencilOperation = GPUStencilOperation.IncrementWrap;
break;
case DecrementWrapStencilOp:
stencilOperation = GPUStencilOperation.DecrementWrap;
break;
default:
console.error( 'THREE.WebGPURenderer: Invalid stencil operation.', stencilOperation );
}
return stencilOperation;
}
_getBlendOperation( blendEquation ) {
let blendOperation;
switch ( blendEquation ) {
case AddEquation:
blendOperation = GPUBlendOperation.Add;
break;
case SubtractEquation:
blendOperation = GPUBlendOperation.Subtract;
break;
case ReverseSubtractEquation:
blendOperation = GPUBlendOperation.ReverseSubtract;
break;
case MinEquation:
blendOperation = GPUBlendOperation.Min;
break;
case MaxEquation:
blendOperation = GPUBlendOperation.Max;
break;
default:
console.error( 'THREE.WebGPUPipelineUtils: Blend equation not supported.', blendEquation );
}
return blendOperation;
}
_getPrimitiveState( object, geometry, material ) {
const descriptor = {};
const utils = this.backend.utils;
descriptor.topology = utils.getPrimitiveTopology( object, material );
if ( object.isLine === true && object.isLineSegments !== true ) {
const count = ( geometry.index ) ? geometry.index.count : geometry.attributes.position.count;
descriptor.stripIndexFormat = ( count > 65535 ) ? GPUIndexFormat.Uint32 : GPUIndexFormat.Uint16; // define data type for primitive restart value
}
switch ( material.side ) {
case FrontSide:
descriptor.frontFace = GPUFrontFace.CCW;
descriptor.cullMode = GPUCullMode.Back;
break;
case BackSide:
descriptor.frontFace = GPUFrontFace.CCW;
descriptor.cullMode = GPUCullMode.Front;
break;
case DoubleSide:
descriptor.frontFace = GPUFrontFace.CCW;
descriptor.cullMode = GPUCullMode.None;
break;
default:
console.error( 'THREE.WebGPUPipelineUtils: Unknown material.side value.', material.side );
break;
}
return descriptor;
}
_getColorWriteMask( material ) {
return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None;
}
_getDepthCompare( material ) {
let depthCompare;
if ( material.depthTest === false ) {
depthCompare = GPUCompareFunction.Always;
} else {
const depthFunc = material.depthFunc;
switch ( depthFunc ) {
case NeverDepth:
depthCompare = GPUCompareFunction.Never;
break;
case AlwaysDepth:
depthCompare = GPUCompareFunction.Always;
break;
case LessDepth:
depthCompare = GPUCompareFunction.Less;
break;
case LessEqualDepth:
depthCompare = GPUCompareFunction.LessEqual;
break;
case EqualDepth:
depthCompare = GPUCompareFunction.Equal;
break;
case GreaterEqualDepth:
depthCompare = GPUCompareFunction.GreaterEqual;
break;
case GreaterDepth:
depthCompare = GPUCompareFunction.Greater;
break;
case NotEqualDepth:
depthCompare = GPUCompareFunction.NotEqual;
break;
default:
console.error( 'THREE.WebGPUPipelineUtils: Invalid depth function.', depthFunc );
}
}
return depthCompare;
}
}
export default WebGPUPipelineUtils;

View File

@@ -0,0 +1,285 @@
import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './WebGPUConstants.js';
class WebGPUTexturePassUtils {
constructor( device ) {
this.device = device;
const mipmapVertexSource = `
struct VarysStruct {
@builtin( position ) Position: vec4<f32>,
@location( 0 ) vTex : vec2<f32>
};
@vertex
fn main( @builtin( vertex_index ) vertexIndex : u32 ) -> VarysStruct {
var Varys : VarysStruct;
var pos = array< vec2<f32>, 4 >(
vec2<f32>( -1.0, 1.0 ),
vec2<f32>( 1.0, 1.0 ),
vec2<f32>( -1.0, -1.0 ),
vec2<f32>( 1.0, -1.0 )
);
var tex = array< vec2<f32>, 4 >(
vec2<f32>( 0.0, 0.0 ),
vec2<f32>( 1.0, 0.0 ),
vec2<f32>( 0.0, 1.0 ),
vec2<f32>( 1.0, 1.0 )
);
Varys.vTex = tex[ vertexIndex ];
Varys.Position = vec4<f32>( pos[ vertexIndex ], 0.0, 1.0 );
return Varys;
}
`;
const mipmapFragmentSource = `
@group( 0 ) @binding( 0 )
var imgSampler : sampler;
@group( 0 ) @binding( 1 )
var img : texture_2d<f32>;
@fragment
fn main( @location( 0 ) vTex : vec2<f32> ) -> @location( 0 ) vec4<f32> {
return textureSample( img, imgSampler, vTex );
}
`;
const flipYFragmentSource = `
@group( 0 ) @binding( 0 )
var imgSampler : sampler;
@group( 0 ) @binding( 1 )
var img : texture_2d<f32>;
@fragment
fn main( @location( 0 ) vTex : vec2<f32> ) -> @location( 0 ) vec4<f32> {
return textureSample( img, imgSampler, vec2( vTex.x, 1.0 - vTex.y ) );
}
`;
this.mipmapSampler = device.createSampler( { minFilter: GPUFilterMode.Linear } );
this.flipYSampler = device.createSampler( { minFilter: GPUFilterMode.Nearest } ); //@TODO?: Consider using textureLoad()
// We'll need a new pipeline for every texture format used.
this.transferPipelines = {};
this.flipYPipelines = {};
this.mipmapVertexShaderModule = device.createShaderModule( {
label: 'mipmapVertex',
code: mipmapVertexSource
} );
this.mipmapFragmentShaderModule = device.createShaderModule( {
label: 'mipmapFragment',
code: mipmapFragmentSource
} );
this.flipYFragmentShaderModule = device.createShaderModule( {
label: 'flipYFragment',
code: flipYFragmentSource
} );
}
getTransferPipeline( format ) {
let pipeline = this.transferPipelines[ format ];
if ( pipeline === undefined ) {
pipeline = this.device.createRenderPipeline( {
vertex: {
module: this.mipmapVertexShaderModule,
entryPoint: 'main'
},
fragment: {
module: this.mipmapFragmentShaderModule,
entryPoint: 'main',
targets: [ { format } ]
},
primitive: {
topology: GPUPrimitiveTopology.TriangleStrip,
stripIndexFormat: GPUIndexFormat.Uint32
},
layout: 'auto'
} );
this.transferPipelines[ format ] = pipeline;
}
return pipeline;
}
getFlipYPipeline( format ) {
let pipeline = this.flipYPipelines[ format ];
if ( pipeline === undefined ) {
pipeline = this.device.createRenderPipeline( {
vertex: {
module: this.mipmapVertexShaderModule,
entryPoint: 'main'
},
fragment: {
module: this.flipYFragmentShaderModule,
entryPoint: 'main',
targets: [ { format } ]
},
primitive: {
topology: GPUPrimitiveTopology.TriangleStrip,
stripIndexFormat: GPUIndexFormat.Uint32
},
layout: 'auto'
} );
this.flipYPipelines[ format ] = pipeline;
}
return pipeline;
}
flipY( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) {
const format = textureGPUDescriptor.format;
const { width, height } = textureGPUDescriptor.size;
const transferPipeline = this.getTransferPipeline( format );
const flipYPipeline = this.getFlipYPipeline( format );
const tempTexture = this.device.createTexture( {
size: { width, height, depthOrArrayLayers: 1 },
format,
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
} );
const srcView = textureGPU.createView( {
baseMipLevel: 0,
mipLevelCount: 1,
dimension: GPUTextureViewDimension.TwoD,
baseArrayLayer
} );
const dstView = tempTexture.createView( {
baseMipLevel: 0,
mipLevelCount: 1,
dimension: GPUTextureViewDimension.TwoD,
baseArrayLayer: 0
} );
const commandEncoder = this.device.createCommandEncoder( {} );
const pass = ( pipeline, sourceView, destinationView ) => {
const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static.
const bindGroup = this.device.createBindGroup( {
layout: bindGroupLayout,
entries: [ {
binding: 0,
resource: this.flipYSampler
}, {
binding: 1,
resource: sourceView
} ]
} );
const passEncoder = commandEncoder.beginRenderPass( {
colorAttachments: [ {
view: destinationView,
loadOp: GPULoadOp.Clear,
storeOp: GPUStoreOp.Store,
clearValue: [ 0, 0, 0, 0 ]
} ]
} );
passEncoder.setPipeline( pipeline );
passEncoder.setBindGroup( 0, bindGroup );
passEncoder.draw( 4, 1, 0, 0 );
passEncoder.end();
};
pass( transferPipeline, srcView, dstView );
pass( flipYPipeline, dstView, srcView );
this.device.queue.submit( [ commandEncoder.finish() ] );
tempTexture.destroy();
}
generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) {
const pipeline = this.getTransferPipeline( textureGPUDescriptor.format );
const commandEncoder = this.device.createCommandEncoder( {} );
const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static.
let srcView = textureGPU.createView( {
baseMipLevel: 0,
mipLevelCount: 1,
dimension: GPUTextureViewDimension.TwoD,
baseArrayLayer
} );
for ( let i = 1; i < textureGPUDescriptor.mipLevelCount; i ++ ) {
const bindGroup = this.device.createBindGroup( {
layout: bindGroupLayout,
entries: [ {
binding: 0,
resource: this.mipmapSampler
}, {
binding: 1,
resource: srcView
} ]
} );
const dstView = textureGPU.createView( {
baseMipLevel: i,
mipLevelCount: 1,
dimension: GPUTextureViewDimension.TwoD,
baseArrayLayer
} );
const passEncoder = commandEncoder.beginRenderPass( {
colorAttachments: [ {
view: dstView,
loadOp: GPULoadOp.Clear,
storeOp: GPUStoreOp.Store,
clearValue: [ 0, 0, 0, 0 ]
} ]
} );
passEncoder.setPipeline( pipeline );
passEncoder.setBindGroup( 0, bindGroup );
passEncoder.draw( 4, 1, 0, 0 );
passEncoder.end();
srcView = dstView;
}
this.device.queue.submit( [ commandEncoder.finish() ] );
}
}
export default WebGPUTexturePassUtils;

View File

@@ -0,0 +1,885 @@
import {
GPUTextureFormat, GPUAddressMode, GPUFilterMode, GPUTextureDimension, GPUFeatureName
} from './WebGPUConstants.js';
import {
CubeTexture, Texture,
NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter,
RepeatWrapping, MirroredRepeatWrapping,
RGB_ETC2_Format, RGBA_ETC2_EAC_Format,
RGBAFormat, RedFormat, RGFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType, SRGBColorSpace, DepthFormat, DepthStencilFormat,
RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format,
RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, UnsignedIntType, UnsignedShortType, UnsignedInt248Type,
NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare
} from 'three';
import { CubeReflectionMapping, CubeRefractionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three';
import WebGPUTexturePassUtils from './WebGPUTexturePassUtils.js';
const _compareToWebGPU = {
[ NeverCompare ]: 'never',
[ LessCompare ]: 'less',
[ EqualCompare ]: 'equal',
[ LessEqualCompare ]: 'less-equal',
[ GreaterCompare ]: 'greater',
[ GreaterEqualCompare ]: 'greater-equal',
[ AlwaysCompare ]: 'always',
[ NotEqualCompare ]: 'not-equal'
};
const _flipMap = [ 0, 1, 3, 2, 4, 5 ];
class WebGPUTextureUtils {
constructor( backend ) {
this.backend = backend;
this._passUtils = null;
this.defaultTexture = null;
this.defaultCubeTexture = null;
}
createSampler( texture ) {
const backend = this.backend;
const device = backend.device;
const textureGPU = backend.get( texture );
const samplerDescriptorGPU = {
addressModeU: this._convertAddressMode( texture.wrapS ),
addressModeV: this._convertAddressMode( texture.wrapT ),
addressModeW: this._convertAddressMode( texture.wrapR ),
magFilter: this._convertFilterMode( texture.magFilter ),
minFilter: this._convertFilterMode( texture.minFilter ),
mipmapFilter: this._convertFilterMode( texture.minFilter ),
maxAnisotropy: texture.anisotropy
};
if ( texture.isDepthTexture && texture.compareFunction !== null ) {
samplerDescriptorGPU.compare = _compareToWebGPU[ texture.compareFunction ];
}
textureGPU.sampler = device.createSampler( samplerDescriptorGPU );
}
createDefaultTexture( texture ) {
let textureGPU;
if ( texture.isCubeTexture ) {
textureGPU = this._getDefaultCubeTextureGPU();
} else {
textureGPU = this._getDefaultTextureGPU();
}
this.backend.get( texture ).texture = textureGPU;
}
createTexture( texture, options = {} ) {
const backend = this.backend;
const textureData = backend.get( texture );
if ( textureData.initialized ) {
throw new Error( 'WebGPUTextureUtils: Texture already initialized.' );
}
if ( options.needsMipmaps === undefined ) options.needsMipmaps = false;
if ( options.levels === undefined ) options.levels = 1;
if ( options.depth === undefined ) options.depth = 1;
const { width, height, depth, levels } = options;
const dimension = this._getDimension( texture );
const format = texture.internalFormat || getFormat( texture, this.device );
const sampleCount = options.sampleCount !== undefined ? options.sampleCount : 1;
const primarySampleCount = texture.isRenderTargetTexture ? 1 : sampleCount;
let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC;
if ( texture.isStorageTexture === true ) {
usage |= GPUTextureUsage.STORAGE_BINDING;
}
if ( texture.isCompressedTexture !== true ) {
usage |= GPUTextureUsage.RENDER_ATTACHMENT;
}
const textureDescriptorGPU = {
label: texture.name,
size: {
width: width,
height: height,
depthOrArrayLayers: depth,
},
mipLevelCount: levels,
sampleCount: primarySampleCount,
dimension: dimension,
format: format,
usage: usage
};
// texture creation
if ( texture.isVideoTexture ) {
const video = texture.source.data;
const videoFrame = new VideoFrame( video );
textureDescriptorGPU.size.width = videoFrame.displayWidth;
textureDescriptorGPU.size.height = videoFrame.displayHeight;
videoFrame.close();
textureData.externalTexture = video;
} else {
if ( format === undefined ) {
console.warn( 'WebGPURenderer: Texture format not supported.' );
return this.createDefaultTexture( texture );
}
textureData.texture = backend.device.createTexture( textureDescriptorGPU );
}
if ( texture.isRenderTargetTexture && sampleCount > 1 ) {
const msaaTextureDescriptorGPU = Object.assign( {}, textureDescriptorGPU );
msaaTextureDescriptorGPU.label = msaaTextureDescriptorGPU.label + '-msaa';
msaaTextureDescriptorGPU.sampleCount = sampleCount;
textureData.msaaTexture = backend.device.createTexture( msaaTextureDescriptorGPU );
}
textureData.initialized = true;
textureData.textureDescriptorGPU = textureDescriptorGPU;
}
destroyTexture( texture ) {
const backend = this.backend;
const textureData = backend.get( texture );
textureData.texture.destroy();
if ( textureData.msaaTexture !== undefined ) textureData.msaaTexture.destroy();
backend.delete( texture );
}
destroySampler( texture ) {
const backend = this.backend;
const textureData = backend.get( texture );
delete textureData.sampler;
}
generateMipmaps( texture ) {
const textureData = this.backend.get( texture );
if ( texture.isCubeTexture ) {
for ( let i = 0; i < 6; i ++ ) {
this._generateMipmaps( textureData.texture, textureData.textureDescriptorGPU, i );
}
} else {
this._generateMipmaps( textureData.texture, textureData.textureDescriptorGPU );
}
}
updateTexture( texture, options ) {
const textureData = this.backend.get( texture );
const { textureDescriptorGPU } = textureData;
if ( texture.isRenderTargetTexture || ( textureDescriptorGPU === undefined /* unsupported texture format */ ) )
return;
// transfer texture data
if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) {
this._copyBufferToTexture( options.image, textureData.texture, textureDescriptorGPU, 0, false );
} else if ( texture.isCompressedTexture ) {
this._copyCompressedBufferToTexture( texture.mipmaps, textureData.texture, textureDescriptorGPU );
} else if ( texture.isCubeTexture ) {
this._copyCubeMapToTexture( options.images, textureData.texture, textureDescriptorGPU, texture.flipY );
} else if ( texture.isVideoTexture ) {
const video = texture.source.data;
textureData.externalTexture = video;
} else {
this._copyImageToTexture( options.image, textureData.texture, textureDescriptorGPU, 0, texture.flipY );
}
//
textureData.version = texture.version;
if ( texture.onUpdate ) texture.onUpdate( texture );
}
async copyTextureToBuffer( texture, x, y, width, height ) {
const device = this.backend.device;
const textureData = this.backend.get( texture );
const textureGPU = textureData.texture;
const format = textureData.textureDescriptorGPU.format;
const bytesPerTexel = this._getBytesPerTexel( format );
const readBuffer = device.createBuffer(
{
size: width * height * bytesPerTexel,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
}
);
const encoder = device.createCommandEncoder();
encoder.copyTextureToBuffer(
{
texture: textureGPU,
origin: { x, y },
},
{
buffer: readBuffer,
bytesPerRow: width * bytesPerTexel
},
{
width: width,
height: height
}
);
const typedArrayType = this._getTypedArrayType( format );
device.queue.submit( [ encoder.finish() ] );
await readBuffer.mapAsync( GPUMapMode.READ );
const buffer = readBuffer.getMappedRange();
return new typedArrayType( buffer );
}
_isEnvironmentTexture( texture ) {
const mapping = texture.mapping;
return ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) || ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping );
}
_getDefaultTextureGPU() {
let defaultTexture = this.defaultTexture;
if ( defaultTexture === null ) {
const texture = new Texture();
texture.minFilter = NearestFilter;
texture.magFilter = NearestFilter;
this.createTexture( texture, { width: 1, height: 1 } );
this.defaultTexture = defaultTexture = texture;
}
return this.backend.get( defaultTexture ).texture;
}
_getDefaultCubeTextureGPU() {
let defaultCubeTexture = this.defaultTexture;
if ( defaultCubeTexture === null ) {
const texture = new CubeTexture();
texture.minFilter = NearestFilter;
texture.magFilter = NearestFilter;
this.createTexture( texture, { width: 1, height: 1, depth: 6 } );
this.defaultCubeTexture = defaultCubeTexture = texture;
}
return this.backend.get( defaultCubeTexture ).texture;
}
_copyCubeMapToTexture( images, textureGPU, textureDescriptorGPU, flipY ) {
for ( let i = 0; i < 6; i ++ ) {
const image = images[ i ];
const flipIndex = flipY === true ? _flipMap[ i ] : i;
if ( image.isDataTexture ) {
this._copyBufferToTexture( image.image, textureGPU, textureDescriptorGPU, flipIndex, flipY );
} else {
this._copyImageToTexture( image, textureGPU, textureDescriptorGPU, flipIndex, flipY );
}
}
}
_copyImageToTexture( image, textureGPU, textureDescriptorGPU, originDepth, flipY ) {
const device = this.backend.device;
device.queue.copyExternalImageToTexture(
{
source: image
}, {
texture: textureGPU,
mipLevel: 0,
origin: { x: 0, y: 0, z: originDepth }
}, {
width: image.width,
height: image.height,
depthOrArrayLayers: 1
}
);
if ( flipY === true ) {
this._flipY( textureGPU, textureDescriptorGPU, originDepth );
}
}
_getPassUtils() {
let passUtils = this._passUtils;
if ( passUtils === null ) {
this._passUtils = passUtils = new WebGPUTexturePassUtils( this.backend.device );
}
return passUtils;
}
_generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer = 0 ) {
this._getPassUtils().generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer );
}
_flipY( textureGPU, textureDescriptorGPU, originDepth = 0 ) {
this._getPassUtils().flipY( textureGPU, textureDescriptorGPU, originDepth );
}
_copyBufferToTexture( image, textureGPU, textureDescriptorGPU, originDepth, flipY ) {
// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
// @TODO: Consider to support valid buffer layouts with other formats like RGB
const device = this.backend.device;
const data = image.data;
const bytesPerTexel = this._getBytesPerTexel( textureDescriptorGPU.format );
const bytesPerRow = image.width * bytesPerTexel;
device.queue.writeTexture(
{
texture: textureGPU,
mipLevel: 0,
origin: { x: 0, y: 0, z: originDepth }
},
data,
{
offset: 0,
bytesPerRow
},
{
width: image.width,
height: image.height,
depthOrArrayLayers: ( image.depth !== undefined ) ? image.depth : 1
} );
if ( flipY === true ) {
this._flipY( textureGPU, textureDescriptorGPU, originDepth );
}
}
_copyCompressedBufferToTexture( mipmaps, textureGPU, textureDescriptorGPU ) {
// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
const device = this.backend.device;
const blockData = this._getBlockData( textureDescriptorGPU.format );
for ( let i = 0; i < mipmaps.length; i ++ ) {
const mipmap = mipmaps[ i ];
const width = mipmap.width;
const height = mipmap.height;
const bytesPerRow = Math.ceil( width / blockData.width ) * blockData.byteLength;
device.queue.writeTexture(
{
texture: textureGPU,
mipLevel: i
},
mipmap.data,
{
offset: 0,
bytesPerRow
},
{
width: Math.ceil( width / blockData.width ) * blockData.width,
height: Math.ceil( height / blockData.width ) * blockData.width,
depthOrArrayLayers: 1
}
);
}
}
_getBlockData( format ) {
// this method is only relevant for compressed texture formats
if ( format === GPUTextureFormat.BC1RGBAUnorm || format === GPUTextureFormat.BC1RGBAUnormSRGB ) return { byteLength: 8, width: 4, height: 4 }; // DXT1
if ( format === GPUTextureFormat.BC2RGBAUnorm || format === GPUTextureFormat.BC2RGBAUnormSRGB ) return { byteLength: 16, width: 4, height: 4 }; // DXT3
if ( format === GPUTextureFormat.BC3RGBAUnorm || format === GPUTextureFormat.BC3RGBAUnormSRGB ) return { byteLength: 16, width: 4, height: 4 }; // DXT5
if ( format === GPUTextureFormat.BC4RUnorm || format === GPUTextureFormat.BC4RSNorm ) return { byteLength: 8, width: 4, height: 4 }; // RGTC1
if ( format === GPUTextureFormat.BC5RGUnorm || format === GPUTextureFormat.BC5RGSnorm ) return { byteLength: 16, width: 4, height: 4 }; // RGTC2
if ( format === GPUTextureFormat.BC6HRGBUFloat || format === GPUTextureFormat.BC6HRGBFloat ) return { byteLength: 16, width: 4, height: 4 }; // BPTC (float)
if ( format === GPUTextureFormat.BC7RGBAUnorm || format === GPUTextureFormat.BC7RGBAUnormSRGB ) return { byteLength: 16, width: 4, height: 4 }; // BPTC (unorm)
if ( format === GPUTextureFormat.ETC2RGB8Unorm || format === GPUTextureFormat.ETC2RGB8UnormSRGB ) return { byteLength: 8, width: 4, height: 4 };
if ( format === GPUTextureFormat.ETC2RGB8A1Unorm || format === GPUTextureFormat.ETC2RGB8A1UnormSRGB ) return { byteLength: 8, width: 4, height: 4 };
if ( format === GPUTextureFormat.ETC2RGBA8Unorm || format === GPUTextureFormat.ETC2RGBA8UnormSRGB ) return { byteLength: 16, width: 4, height: 4 };
if ( format === GPUTextureFormat.EACR11Unorm ) return { byteLength: 8, width: 4, height: 4 };
if ( format === GPUTextureFormat.EACR11Snorm ) return { byteLength: 8, width: 4, height: 4 };
if ( format === GPUTextureFormat.EACRG11Unorm ) return { byteLength: 16, width: 4, height: 4 };
if ( format === GPUTextureFormat.EACRG11Snorm ) return { byteLength: 16, width: 4, height: 4 };
if ( format === GPUTextureFormat.ASTC4x4Unorm || format === GPUTextureFormat.ASTC4x4UnormSRGB ) return { byteLength: 16, width: 4, height: 4 };
if ( format === GPUTextureFormat.ASTC5x4Unorm || format === GPUTextureFormat.ASTC5x4UnormSRGB ) return { byteLength: 16, width: 5, height: 4 };
if ( format === GPUTextureFormat.ASTC5x5Unorm || format === GPUTextureFormat.ASTC5x5UnormSRGB ) return { byteLength: 16, width: 5, height: 5 };
if ( format === GPUTextureFormat.ASTC6x5Unorm || format === GPUTextureFormat.ASTC6x5UnormSRGB ) return { byteLength: 16, width: 6, height: 5 };
if ( format === GPUTextureFormat.ASTC6x6Unorm || format === GPUTextureFormat.ASTC6x6UnormSRGB ) return { byteLength: 16, width: 6, height: 6 };
if ( format === GPUTextureFormat.ASTC8x5Unorm || format === GPUTextureFormat.ASTC8x5UnormSRGB ) return { byteLength: 16, width: 8, height: 5 };
if ( format === GPUTextureFormat.ASTC8x6Unorm || format === GPUTextureFormat.ASTC8x6UnormSRGB ) return { byteLength: 16, width: 8, height: 6 };
if ( format === GPUTextureFormat.ASTC8x8Unorm || format === GPUTextureFormat.ASTC8x8UnormSRGB ) return { byteLength: 16, width: 8, height: 8 };
if ( format === GPUTextureFormat.ASTC10x5Unorm || format === GPUTextureFormat.ASTC10x5UnormSRGB ) return { byteLength: 16, width: 10, height: 5 };
if ( format === GPUTextureFormat.ASTC10x6Unorm || format === GPUTextureFormat.ASTC10x6UnormSRGB ) return { byteLength: 16, width: 10, height: 6 };
if ( format === GPUTextureFormat.ASTC10x8Unorm || format === GPUTextureFormat.ASTC10x8UnormSRGB ) return { byteLength: 16, width: 10, height: 8 };
if ( format === GPUTextureFormat.ASTC10x10Unorm || format === GPUTextureFormat.ASTC10x10UnormSRGB ) return { byteLength: 16, width: 10, height: 10 };
if ( format === GPUTextureFormat.ASTC12x10Unorm || format === GPUTextureFormat.ASTC12x10UnormSRGB ) return { byteLength: 16, width: 12, height: 10 };
if ( format === GPUTextureFormat.ASTC12x12Unorm || format === GPUTextureFormat.ASTC12x12UnormSRGB ) return { byteLength: 16, width: 12, height: 12 };
}
_convertAddressMode( value ) {
let addressMode = GPUAddressMode.ClampToEdge;
if ( value === RepeatWrapping ) {
addressMode = GPUAddressMode.Repeat;
} else if ( value === MirroredRepeatWrapping ) {
addressMode = GPUAddressMode.MirrorRepeat;
}
return addressMode;
}
_convertFilterMode( value ) {
let filterMode = GPUFilterMode.Linear;
if ( value === NearestFilter || value === NearestMipmapNearestFilter || value === NearestMipmapLinearFilter ) {
filterMode = GPUFilterMode.Nearest;
}
return filterMode;
}
_getBytesPerTexel( format ) {
if ( format === GPUTextureFormat.R8Unorm ) return 1;
if ( format === GPUTextureFormat.R16Float ) return 2;
if ( format === GPUTextureFormat.RG8Unorm ) return 2;
if ( format === GPUTextureFormat.RG16Float ) return 4;
if ( format === GPUTextureFormat.R32Float ) return 4;
if ( format === GPUTextureFormat.RGBA8Unorm || format === GPUTextureFormat.RGBA8UnormSRGB ) return 4;
if ( format === GPUTextureFormat.RG32Float ) return 8;
if ( format === GPUTextureFormat.RGBA16Float ) return 8;
if ( format === GPUTextureFormat.RGBA32Float ) return 16;
}
_getTypedArrayType( format ) {
if ( format === GPUTextureFormat.R8Uint ) return Uint8Array;
if ( format === GPUTextureFormat.R8Sint ) return Int8Array;
if ( format === GPUTextureFormat.R8Unorm ) return Uint8Array;
if ( format === GPUTextureFormat.R8Snorm ) return Int8Array;
if ( format === GPUTextureFormat.RG8Uint ) return Uint8Array;
if ( format === GPUTextureFormat.RG8Sint ) return Int8Array;
if ( format === GPUTextureFormat.RG8Unorm ) return Uint8Array;
if ( format === GPUTextureFormat.RG8Snorm ) return Int8Array;
if ( format === GPUTextureFormat.RGBA8Uint ) return Uint8Array;
if ( format === GPUTextureFormat.RGBA8Sint ) return Int8Array;
if ( format === GPUTextureFormat.RGBA8Unorm ) return Uint8Array;
if ( format === GPUTextureFormat.RGBA8Snorm ) return Int8Array;
if ( format === GPUTextureFormat.R16Uint ) return Uint16Array;
if ( format === GPUTextureFormat.R16Sint ) return Int16Array;
if ( format === GPUTextureFormat.RG16Uint ) return Uint16Array;
if ( format === GPUTextureFormat.RG16Sint ) return Int16Array;
if ( format === GPUTextureFormat.RGBA16Uint ) return Uint16Array;
if ( format === GPUTextureFormat.RGBA16Sint ) return Int16Array;
if ( format === GPUTextureFormat.R32Uint ) return Uint32Array;
if ( format === GPUTextureFormat.R32Sint ) return Int32Array;
if ( format === GPUTextureFormat.R32Float ) return Float32Array;
if ( format === GPUTextureFormat.RG32Uint ) return Uint32Array;
if ( format === GPUTextureFormat.RG32Sint ) return Int32Array;
if ( format === GPUTextureFormat.RG32Float ) return Float32Array;
if ( format === GPUTextureFormat.RGBA32Uint ) return Uint32Array;
if ( format === GPUTextureFormat.RGBA32Sint ) return Int32Array;
if ( format === GPUTextureFormat.RGBA32Float ) return Float32Array;
}
_getDimension( texture ) {
let dimension;
if ( texture.isData3DTexture ) {
dimension = GPUTextureDimension.ThreeD;
} else {
dimension = GPUTextureDimension.TwoD;
}
return dimension;
}
}
export function getFormat( texture, device = null ) {
const format = texture.format;
const type = texture.type;
const colorSpace = texture.colorSpace;
let formatGPU;
if ( /*texture.isRenderTargetTexture === true ||*/ texture.isFramebufferTexture === true ) {
formatGPU = GPUTextureFormat.BGRA8Unorm;
} else if ( texture.isCompressedTexture === true ) {
switch ( format ) {
case RGBA_S3TC_DXT1_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.BC1RGBAUnormSRGB : GPUTextureFormat.BC1RGBAUnorm;
break;
case RGBA_S3TC_DXT3_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.BC2RGBAUnormSRGB : GPUTextureFormat.BC2RGBAUnorm;
break;
case RGBA_S3TC_DXT5_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.BC3RGBAUnormSRGB : GPUTextureFormat.BC3RGBAUnorm;
break;
case RGB_ETC2_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ETC2RGB8UnormSRGB : GPUTextureFormat.ETC2RGB8Unorm;
break;
case RGBA_ETC2_EAC_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ETC2RGBA8UnormSRGB : GPUTextureFormat.ETC2RGBA8Unorm;
break;
case RGBA_ASTC_4x4_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC4x4UnormSRGB : GPUTextureFormat.ASTC4x4Unorm;
break;
case RGBA_ASTC_5x4_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC5x4UnormSRGB : GPUTextureFormat.ASTC5x4Unorm;
break;
case RGBA_ASTC_5x5_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC5x5UnormSRGB : GPUTextureFormat.ASTC5x5Unorm;
break;
case RGBA_ASTC_6x5_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC6x5UnormSRGB : GPUTextureFormat.ASTC6x5Unorm;
break;
case RGBA_ASTC_6x6_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC6x6UnormSRGB : GPUTextureFormat.ASTC6x6Unorm;
break;
case RGBA_ASTC_8x5_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC8x5UnormSRGB : GPUTextureFormat.ASTC8x5Unorm;
break;
case RGBA_ASTC_8x6_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC8x6UnormSRGB : GPUTextureFormat.ASTC8x6Unorm;
break;
case RGBA_ASTC_8x8_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC8x8UnormSRGB : GPUTextureFormat.ASTC8x8Unorm;
break;
case RGBA_ASTC_10x5_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC10x5UnormSRGB : GPUTextureFormat.ASTC10x5Unorm;
break;
case RGBA_ASTC_10x6_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC10x6UnormSRGB : GPUTextureFormat.ASTC10x6Unorm;
break;
case RGBA_ASTC_10x8_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC10x8UnormSRGB : GPUTextureFormat.ASTC10x8Unorm;
break;
case RGBA_ASTC_10x10_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC10x10UnormSRGB : GPUTextureFormat.ASTC10x10Unorm;
break;
case RGBA_ASTC_12x10_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC12x10UnormSRGB : GPUTextureFormat.ASTC12x10Unorm;
break;
case RGBA_ASTC_12x12_Format:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.ASTC12x12UnormSRGB : GPUTextureFormat.ASTC12x12Unorm;
break;
default:
console.error( 'WebGPURenderer: Unsupported texture format.', format );
}
} else {
switch ( format ) {
case RGBAFormat:
switch ( type ) {
case UnsignedByteType:
formatGPU = ( colorSpace === SRGBColorSpace ) ? GPUTextureFormat.RGBA8UnormSRGB : GPUTextureFormat.RGBA8Unorm;
break;
case HalfFloatType:
formatGPU = GPUTextureFormat.RGBA16Float;
break;
case FloatType:
formatGPU = GPUTextureFormat.RGBA32Float;
break;
default:
console.error( 'WebGPURenderer: Unsupported texture type with RGBAFormat.', type );
}
break;
case RedFormat:
switch ( type ) {
case UnsignedByteType:
formatGPU = GPUTextureFormat.R8Unorm;
break;
case HalfFloatType:
formatGPU = GPUTextureFormat.R16Float;
break;
case FloatType:
formatGPU = GPUTextureFormat.R32Float;
break;
default:
console.error( 'WebGPURenderer: Unsupported texture type with RedFormat.', type );
}
break;
case RGFormat:
switch ( type ) {
case UnsignedByteType:
formatGPU = GPUTextureFormat.RG8Unorm;
break;
case HalfFloatType:
formatGPU = GPUTextureFormat.RG16Float;
break;
case FloatType:
formatGPU = GPUTextureFormat.RG32Float;
break;
default:
console.error( 'WebGPURenderer: Unsupported texture type with RGFormat.', type );
}
break;
case DepthFormat:
switch ( type ) {
case UnsignedShortType:
formatGPU = GPUTextureFormat.Depth16Unorm;
break;
case UnsignedIntType:
formatGPU = GPUTextureFormat.Depth24Plus;
break;
case FloatType:
formatGPU = GPUTextureFormat.Depth32Float;
break;
default:
console.error( 'WebGPURenderer: Unsupported texture type with DepthFormat.', type );
}
break;
case DepthStencilFormat:
switch ( type ) {
case UnsignedInt248Type:
formatGPU = GPUTextureFormat.Depth24PlusStencil8;
break;
case FloatType:
if ( device && device.features.has( GPUFeatureName.Depth32FloatStencil8 ) === false ) {
console.error( 'WebGPURenderer: Depth textures with DepthStencilFormat + FloatType can only be used with the "depth32float-stencil8" GPU feature.' );
}
formatGPU = GPUTextureFormat.Depth32FloatStencil8;
break;
default:
console.error( 'WebGPURenderer: Unsupported texture type with DepthStencilFormat.', type );
}
break;
default:
console.error( 'WebGPURenderer: Unsupported texture format.', format );
}
}
return formatGPU;
}
export default WebGPUTextureUtils;

View File

@@ -0,0 +1,93 @@
import { GPUPrimitiveTopology, GPUTextureFormat } from './WebGPUConstants.js';
class WebGPUUtils {
constructor( backend ) {
this.backend = backend;
}
getCurrentDepthStencilFormat( renderContext ) {
let format;
if ( renderContext.depthTexture !== null ) {
format = this.getTextureFormatGPU( renderContext.depthTexture );
} else if ( renderContext.depth && renderContext.stencil ) {
format = GPUTextureFormat.Depth24PlusStencil8;
} else if ( renderContext.depth ) {
format = GPUTextureFormat.Depth24Plus;
}
return format;
}
getTextureFormatGPU( texture ) {
return this.backend.get( texture ).texture.format;
}
getCurrentColorFormat( renderContext ) {
let format;
if ( renderContext.textures !== null ) {
format = this.getTextureFormatGPU( renderContext.textures[ 0 ] );
} else {
format = GPUTextureFormat.BGRA8Unorm; // default context format
}
return format;
}
getCurrentColorSpace( renderContext ) {
if ( renderContext.textures !== null ) {
return renderContext.textures[ 0 ].colorSpace;
}
return this.backend.renderer.outputColorSpace;
}
getPrimitiveTopology( object, material ) {
if ( object.isPoints ) return GPUPrimitiveTopology.PointList;
else if ( object.isLineSegments || ( object.isMesh && material.wireframe === true ) ) return GPUPrimitiveTopology.LineList;
else if ( object.isLine ) return GPUPrimitiveTopology.LineStrip;
else if ( object.isMesh ) return GPUPrimitiveTopology.TriangleList;
}
getSampleCount( renderContext ) {
if ( renderContext.textures !== null ) {
return renderContext.sampleCount;
}
return this.backend.parameters.sampleCount;
}
}
export default WebGPUUtils;