blob: 18156370de5f47be8a551c4be7fb2f2f30d40f31 [file] [log] [blame]
/**
* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
**/ import { assert, unreachable } from '../../../common/framework/util/util.js';
import {
assertInIntegerRange,
float32ToFloatBits,
floatAsNormalizedInteger,
gammaCompress,
gammaDecompress,
normalizedIntegerAsFloat,
packRGB9E5UFloat,
} from '../conversion.js';
export let TexelComponent;
(function (TexelComponent) {
TexelComponent['R'] = 'R';
TexelComponent['G'] = 'G';
TexelComponent['B'] = 'B';
TexelComponent['A'] = 'A';
TexelComponent['Depth'] = 'Depth';
TexelComponent['Stencil'] = 'Stencil';
})(TexelComponent || (TexelComponent = {}));
/**
* Create a PerTexelComponent object filled with the same value for all components.
* @param {TexelComponent[]} components - The component names.
* @param {T} value - The value to assign to each component.
* @returns {PerTexelComponent<T>}
*/
function makePerTexelComponent(components, value) {
const values = {};
for (const c of components) {
values[c] = value;
}
return values;
}
/**
* Create a function which applies clones a `PerTexelComponent<number>` and then applies the
* function `fn` to each component of `components`.
* @param {(value: number) => number} fn - The mapping function to apply to component values.
* @param {TexelComponent[]} components - The component names.
* @returns {ComponentMapFn} The map function which clones the input component values, and applies
* `fn` to each of component of `components`.
*/
function applyEach(fn, components) {
return values => {
values = Object.assign({}, values);
for (const c of components) {
assert(values[c] !== undefined);
values[c] = fn(values[c]);
}
return values;
};
}
/**
* A `ComponentMapFn` for encoding sRGB.
* @param {PerTexelComponent<number>} components - The input component values.
* @returns {TexelComponent<number>} Gamma-compressed copy of `components`.
*/
const encodeSRGB = components => {
assert(
components.R !== undefined && components.G !== undefined && components.B !== undefined,
'sRGB requires all of R, G, and B components'
);
return applyEach(gammaCompress, kRGB)(components);
};
/**
* A `ComponentMapFn` for decoding sRGB.
* @param {PerTexelComponent<number>} components - The input component values.
* @returns {TexelComponent<number>} Gamma-decompressed copy of `components`.
*/
const decodeSRGB = components => {
components = Object.assign({}, components);
assert(
components.R !== undefined && components.G !== undefined && components.B !== undefined,
'sRGB requires all of R, G, and B components'
);
return applyEach(gammaDecompress, kRGB)(components);
};
/**
* Helper function to pack components as an ArrayBuffer.
* @param {TexelComponent[]} componentOrder - The order of the component data.
* @param {PerTexelComponent<number>} components - The input component values.
* @param {number | PerTexelComponent<number>} bitLengths - The length in bits of each component.
* If a single number, all components are the same length, otherwise this is a dictionary of
* per-component bit lengths.
* @param {ComponentDataType | PerTexelComponent<ComponentDataType>} componentDataTypes -
* The type of the data in `components`. If a single value, all components have the same value.
* Otherwise, this is a dictionary of per-component data types.
* @returns {ArrayBuffer} The packed component data.
*/
function packComponents(componentOrder, components, bitLengths, componentDataTypes) {
const bitLengthMap =
typeof bitLengths === 'number' ? makePerTexelComponent(componentOrder, bitLengths) : bitLengths;
const componentDataTypeMap =
typeof componentDataTypes === 'string' || componentDataTypes === null
? makePerTexelComponent(componentOrder, componentDataTypes)
: componentDataTypes;
const totalBitLength = Object.entries(bitLengthMap).reduce((acc, [, value]) => {
assert(value !== undefined);
return acc + value;
}, 0);
assert(totalBitLength % 8 === 0);
const data = new ArrayBuffer(totalBitLength / 8);
let bitOffset = 0;
for (const c of componentOrder) {
const value = components[c];
const type = componentDataTypeMap[c];
const bitLength = bitLengthMap[c];
assert(value !== undefined);
assert(type !== undefined);
assert(bitLength !== undefined);
const byteOffset = Math.floor(bitOffset / 8);
const byteLength = Math.ceil(bitLength / 8);
switch (type) {
case 'uint':
case 'unorm':
if (byteOffset === bitOffset / 8 && byteLength === bitLength / 8) {
switch (byteLength) {
case 1:
new DataView(data, byteOffset, byteLength).setUint8(0, value);
break;
case 2:
new DataView(data, byteOffset, byteLength).setUint16(0, value, true);
break;
case 4:
new DataView(data, byteOffset, byteLength).setUint32(0, value, true);
break;
default:
unreachable();
}
} else {
// Packed representations are all 32-bit and use Uint as the data type.
// ex.) rg10b11float, rgb10a2unorm
const view = new DataView(data);
switch (view.byteLength) {
case 4: {
const currentValue = view.getUint32(0, true);
let mask = 0xffffffff;
const bitsToClearRight = bitOffset;
const bitsToClearLeft = 32 - (bitLength + bitOffset);
mask = (mask >>> bitsToClearRight) << bitsToClearRight;
mask = (mask << bitsToClearLeft) >>> bitsToClearLeft;
const newValue = (currentValue & ~mask) | (value << bitOffset);
view.setUint32(0, newValue, true);
break;
}
default:
unreachable();
}
}
break;
case 'sint':
case 'snorm':
assert(byteOffset === bitOffset / 8 && byteLength === bitLength / 8);
switch (byteLength) {
case 1:
new DataView(data, byteOffset, byteLength).setInt8(0, value);
break;
case 2:
new DataView(data, byteOffset, byteLength).setInt16(0, value, true);
break;
case 4:
new DataView(data, byteOffset, byteLength).setInt32(0, value, true);
break;
default:
unreachable();
}
break;
case 'float':
assert(byteOffset === bitOffset / 8 && byteLength === bitLength / 8);
switch (byteLength) {
case 4:
new DataView(data, byteOffset, byteLength).setFloat32(0, value, true);
break;
default:
unreachable();
}
break;
case 'ufloat':
case null:
unreachable();
}
bitOffset += bitLength;
}
return data;
}
/**
* Create an entry in `kTexelRepresentationInfo` for normalized integer texel data with constant
* bitlength.
* @param {TexelComponent[]} componentOrder - The order of the component data.
* @param {number} bitLength - The number of bits in each component.
* @param {{signed: boolean; sRGB: boolean}} opt - Boolean flags for `signed` and `sRGB`.
*/
function makeNormalizedInfo(componentOrder, bitLength, opt) {
const encodeNonSRGB = applyEach(
n => floatAsNormalizedInteger(n, bitLength, opt.signed),
componentOrder
);
const decodeNonSRGB = applyEach(
n => normalizedIntegerAsFloat(n, bitLength, opt.signed),
componentOrder
);
let encode;
let decode;
if (opt.sRGB) {
encode = components => encodeNonSRGB(encodeSRGB(components));
decode = components => decodeSRGB(decodeNonSRGB(components));
} else {
encode = encodeNonSRGB;
decode = decodeNonSRGB;
}
const dataType = opt.signed ? 'snorm' : 'unorm';
return {
componentOrder,
componentInfo: makePerTexelComponent(componentOrder, {
dataType,
bitLength,
}),
encode,
decode,
pack: components => packComponents(componentOrder, components, bitLength, dataType),
};
}
/**
* Create an entry in `kTexelRepresentationInfo` for integer texel data with constant bitlength.
* @param {TexelComponent[]} componentOrder - The order of the component data.
* @param {number} bitLength - The number of bits in each component.
* @param {{signed: boolean}} opt - Boolean flag for `signed`.
*/
function makeIntegerInfo(componentOrder, bitLength, opt) {
const encode = applyEach(
n => (assertInIntegerRange(n, bitLength, opt.signed), n),
componentOrder
);
const decode = applyEach(
n => (assertInIntegerRange(n, bitLength, opt.signed), n),
componentOrder
);
const dataType = opt.signed ? 'sint' : 'uint';
return {
componentOrder,
componentInfo: makePerTexelComponent(componentOrder, {
dataType,
bitLength,
}),
encode,
decode,
pack: components => packComponents(componentOrder, components, bitLength, dataType),
};
}
/**
* Create an entry in `kTexelRepresentationInfo` for floating point texel data with constant
* bitlength.
* @param {TexelComponent[]} componentOrder - The order of the component data.
* @param {number} bitLength - The number of bits in each component.
*/
function makeFloatInfo(componentOrder, bitLength) {
// TODO: Use |bitLength| to round float values based on precision.
const encode = applyEach(identity, componentOrder);
const decode = applyEach(identity, componentOrder);
return {
componentOrder,
componentInfo: makePerTexelComponent(componentOrder, {
dataType: 'float',
bitLength,
}),
encode,
decode,
pack: components => {
switch (bitLength) {
case 16:
components = applyEach(
n => float32ToFloatBits(n, 1, 5, 10, 15),
componentOrder
)(components);
return packComponents(componentOrder, components, 16, 'uint');
case 32:
return packComponents(componentOrder, components, bitLength, 'float');
default:
unreachable();
}
},
};
}
const kR = [TexelComponent.R];
const kRG = [TexelComponent.R, TexelComponent.G];
const kRGB = [TexelComponent.R, TexelComponent.G, TexelComponent.B];
const kRGBA = [TexelComponent.R, TexelComponent.G, TexelComponent.B, TexelComponent.A];
const kBGRA = [TexelComponent.B, TexelComponent.G, TexelComponent.R, TexelComponent.A];
const identity = n => n;
export const kTexelRepresentationInfo = {
...{
r8unorm: makeNormalizedInfo(kR, 8, { signed: false, sRGB: false }),
r8snorm: makeNormalizedInfo(kR, 8, { signed: true, sRGB: false }),
r8uint: makeIntegerInfo(kR, 8, { signed: false }),
r8sint: makeIntegerInfo(kR, 8, { signed: true }),
r16uint: makeIntegerInfo(kR, 16, { signed: false }),
r16sint: makeIntegerInfo(kR, 16, { signed: true }),
r16float: makeFloatInfo(kR, 16),
rg8unorm: makeNormalizedInfo(kRG, 8, { signed: false, sRGB: false }),
rg8snorm: makeNormalizedInfo(kRG, 8, { signed: true, sRGB: false }),
rg8uint: makeIntegerInfo(kRG, 8, { signed: false }),
rg8sint: makeIntegerInfo(kRG, 8, { signed: true }),
r32uint: makeIntegerInfo(kR, 32, { signed: false }),
r32sint: makeIntegerInfo(kR, 32, { signed: true }),
r32float: makeFloatInfo(kR, 32),
rg16uint: makeIntegerInfo(kRG, 16, { signed: false }),
rg16sint: makeIntegerInfo(kRG, 16, { signed: true }),
rg16float: makeFloatInfo(kRG, 16),
rgba8unorm: makeNormalizedInfo(kRGBA, 8, { signed: false, sRGB: false }),
'rgba8unorm-srgb': makeNormalizedInfo(kRGBA, 8, { signed: false, sRGB: true }),
rgba8snorm: makeNormalizedInfo(kRGBA, 8, { signed: true, sRGB: false }),
rgba8uint: makeIntegerInfo(kRGBA, 8, { signed: false }),
rgba8sint: makeIntegerInfo(kRGBA, 8, { signed: true }),
bgra8unorm: makeNormalizedInfo(kBGRA, 8, { signed: false, sRGB: false }),
'bgra8unorm-srgb': makeNormalizedInfo(kBGRA, 8, { signed: false, sRGB: true }),
rg32uint: makeIntegerInfo(kRG, 32, { signed: false }),
rg32sint: makeIntegerInfo(kRG, 32, { signed: true }),
rg32float: makeFloatInfo(kRG, 32),
rgba16uint: makeIntegerInfo(kRGBA, 16, { signed: false }),
rgba16sint: makeIntegerInfo(kRGBA, 16, { signed: true }),
rgba16float: makeFloatInfo(kRGBA, 16),
rgba32uint: makeIntegerInfo(kRGBA, 32, { signed: false }),
rgba32sint: makeIntegerInfo(kRGBA, 32, { signed: true }),
rgba32float: makeFloatInfo(kRGBA, 32),
},
...{
rgb10a2unorm: {
componentOrder: kRGBA,
componentInfo: {
R: { dataType: 'unorm', bitLength: 10 },
G: { dataType: 'unorm', bitLength: 10 },
B: { dataType: 'unorm', bitLength: 10 },
A: { dataType: 'unorm', bitLength: 2 },
},
encode: components => {
var _components$R, _components$G, _components$B, _components$A;
return {
R: floatAsNormalizedInteger(
(_components$R = components.R) !== null && _components$R !== void 0
? _components$R
: unreachable(),
10,
false
),
G: floatAsNormalizedInteger(
(_components$G = components.G) !== null && _components$G !== void 0
? _components$G
: unreachable(),
10,
false
),
B: floatAsNormalizedInteger(
(_components$B = components.B) !== null && _components$B !== void 0
? _components$B
: unreachable(),
10,
false
),
A: floatAsNormalizedInteger(
(_components$A = components.A) !== null && _components$A !== void 0
? _components$A
: unreachable(),
2,
false
),
};
},
decode: components => {
var _components$R2, _components$G2, _components$B2, _components$A2;
return {
R: normalizedIntegerAsFloat(
(_components$R2 = components.R) !== null && _components$R2 !== void 0
? _components$R2
: unreachable(),
10,
false
),
G: normalizedIntegerAsFloat(
(_components$G2 = components.G) !== null && _components$G2 !== void 0
? _components$G2
: unreachable(),
10,
false
),
B: normalizedIntegerAsFloat(
(_components$B2 = components.B) !== null && _components$B2 !== void 0
? _components$B2
: unreachable(),
10,
false
),
A: normalizedIntegerAsFloat(
(_components$A2 = components.A) !== null && _components$A2 !== void 0
? _components$A2
: unreachable(),
2,
false
),
};
},
pack: components =>
packComponents(
kRGBA,
components,
{
R: 10,
G: 10,
B: 10,
A: 2,
},
'uint'
),
},
rg11b10ufloat: {
componentOrder: kRGB,
encode: applyEach(identity, kRGB),
decode: applyEach(identity, kRGB),
componentInfo: {
R: { dataType: 'ufloat', bitLength: 11 },
G: { dataType: 'ufloat', bitLength: 11 },
B: { dataType: 'ufloat', bitLength: 10 },
},
pack: components => {
var _components$R3, _components$G3, _components$B3;
components = {
R: float32ToFloatBits(
(_components$R3 = components.R) !== null && _components$R3 !== void 0
? _components$R3
: unreachable(),
0,
5,
6,
15
),
G: float32ToFloatBits(
(_components$G3 = components.G) !== null && _components$G3 !== void 0
? _components$G3
: unreachable(),
0,
5,
6,
15
),
B: float32ToFloatBits(
(_components$B3 = components.B) !== null && _components$B3 !== void 0
? _components$B3
: unreachable(),
0,
5,
5,
15
),
};
return packComponents(
kRGB,
components,
{
R: 11,
G: 11,
B: 10,
},
'uint'
);
},
},
rgb9e5ufloat: {
componentOrder: kRGB,
componentInfo: makePerTexelComponent(kRGB, {
dataType: 'ufloat',
bitLength: -1, // Components don't really have a bitLength since the format is packed.
}),
encode: applyEach(identity, kRGB),
decode: applyEach(identity, kRGB),
pack: components => {
var _components$R4, _components$G4, _components$B4;
return new Uint32Array([
packRGB9E5UFloat(
(_components$R4 = components.R) !== null && _components$R4 !== void 0
? _components$R4
: unreachable(),
(_components$G4 = components.G) !== null && _components$G4 !== void 0
? _components$G4
: unreachable(),
(_components$B4 = components.B) !== null && _components$B4 !== void 0
? _components$B4
: unreachable()
),
]).buffer;
},
},
depth32float: {
componentOrder: [TexelComponent.Depth],
encode: applyEach(n => (assert(n >= 0 && n <= 1.0), n), [TexelComponent.Depth]),
decode: applyEach(n => (assert(n >= 0 && n <= 1.0), n), [TexelComponent.Depth]),
componentInfo: { Depth: { dataType: 'float', bitLength: 32 } },
pack: components => packComponents([TexelComponent.Depth], components, 32, 'float'),
},
depth24plus: {
componentOrder: [TexelComponent.Depth],
componentInfo: { Depth: { dataType: null, bitLength: 24 } },
encode: applyEach(() => unreachable('depth24plus cannot be encoded'), [TexelComponent.Depth]),
decode: applyEach(() => unreachable('depth24plus cannot be decoded'), [TexelComponent.Depth]),
pack: () => unreachable('depth24plus data cannot be packed'),
},
'depth24plus-stencil8': {
componentOrder: [TexelComponent.Depth, TexelComponent.Stencil],
componentInfo: {
Depth: {
dataType: null,
bitLength: 24,
},
Stencil: {
dataType: 'uint',
bitLength: 8,
},
},
encode: components => {
assert(components.Depth === undefined, 'depth24plus cannot be encoded');
assert(components.Stencil !== undefined);
assertInIntegerRange(components.Stencil, 8, false);
return components;
},
decode: components => {
assert(components.Depth === undefined, 'depth24plus cannot be decoded');
assert(components.Stencil !== undefined);
assertInIntegerRange(components.Stencil, 8, false);
return components;
},
pack: () => unreachable('depth24plus-stencil8 data cannot be packed'),
},
},
};
/**
* Get the `ComponentDataType` for a format. All components must have the same type.
* @param {UncompressedTextureFormat} format - The input format.
* @returns {ComponentDataType} The data of the components.
*/
export function getSingleDataType(format) {
const infos = Object.values(kTexelRepresentationInfo[format].componentInfo);
assert(infos.length > 0);
return infos.reduce((acc, cur) => {
assert(cur !== undefined);
assert(acc === undefined || acc === cur.dataType);
return cur.dataType;
}, infos[0].dataType);
}
/**
* Get traits for generating code to readback data from a component.
* @param {ComponentDataType} dataType - The input component data type.
* @returns A dictionary containing the respective `ReadbackTypedArray` and `shaderType`.
*/
export function getComponentReadbackTraits(dataType) {
switch (dataType) {
case 'ufloat':
case 'float':
case 'unorm':
case 'snorm':
return {
ReadbackTypedArray: Float32Array,
shaderType: 'f32',
};
case 'uint':
return {
ReadbackTypedArray: Uint32Array,
shaderType: 'u32',
};
case 'sint':
return {
ReadbackTypedArray: Int32Array,
shaderType: 'i32',
};
default:
unreachable();
}
}