import { assert , unreachable } from '../../common/util/util.js' ;
export const kDefaultVertexShaderCode = `
@vertex fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>(0 .0 , 0 .0 , 0 .0 , 1 .0 );
}
`;
export const kDefaultFragmentShaderCode = `
@fragment fn main() -> @location(0 ) vec4<f32> {
return vec4<f32>(1 .0 , 1 .0 , 1 .0 , 1 .0 );
}`;
// MAINTENANCE_TODO(#3344): deduplicate fullscreen quad shader code.
export const kFullscreenQuadVertexShaderCode = `
struct VertexOutput {
@builtin(position) Position : vec4<f32>
};
@vertex fn main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
var pos = array<vec2<f32>, 6 >(
vec2<f32>( 1 .0 , 1 .0 ),
vec2<f32>( 1 .0 , -1 .0 ),
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 output : VertexOutput;
output.Position = vec4<f32>(pos[VertexIndex], 0 .0 , 1 .0 );
return output;
}
`;
const kPlainTypeInfo = {
i32: {
suffix: '' ,
fractionDigits: 0 ,
},
u32: {
suffix: 'u' ,
fractionDigits: 0 ,
},
f32: {
suffix: '' ,
fractionDigits: 4 ,
},
};
/**
*
* @ param sampleType sampleType of texture format
* @ returns plain type compatible of the sampleType
*/
export function getPlainTypeInfo(sampleType: GPUTextureSampleType): keyof typeof kPlainTypeInfo {
switch (sampleType) {
case 'sint' :
return 'i32' ;
case 'uint' :
return 'u32' ;
case 'float' :
case 'unfilterable-float' :
case 'depth' :
return 'f32' ;
default :
unreachable();
}
}
/**
* Build a fragment shader based on output value and types
* e . g . write to color target 0 a ` vec4 < f32 > ( 1 . 0 , 0 . 0 , 1 . 0 , 1 . 0 ) ` and color target 2 a ` vec2 < u32 > ( 1 , 2 ) `
* ` ` `
* outputs : [
* {
* values : [ 1 , 0 , 1 , 1 ] , ,
* plainType : ' f32 ' ,
* componentCount : 4 ,
* } ,
* null ,
* {
* values : [ 1 , 2 ] ,
* plainType : ' u32 ' ,
* componentCount : 2 ,
* } ,
* ]
* ` ` `
*
* return :
* ` ` `
* struct Outputs {
* @ location ( 0 ) o1 : vec4 < f32 > ,
* @ location ( 2 ) o3 : vec2 < u32 > ,
* }
* @ fragment fn main ( ) - > Outputs {
* return Outputs ( vec4 < f32 > ( 1 . 0 , 0 . 0 , 1 . 0 , 1 . 0 ) , vec4 < u32 > ( 1 , 2 ) ) ;
* }
* ` ` `
*
* If fragDepth is given there will be an extra @ builtin ( frag_depth ) output with the specified value assigned .
*
* @ param outputs the shader outputs for each location attribute
* @ param fragDepth the shader outputs frag_depth value ( optional )
* @ returns the fragment shader string
*/
export function getFragmentShaderCodeWithOutput(
outputs: ({
values: readonly number[];
plainType: 'i32' | 'u32' | 'f32' ;
componentCount: number;
} | null )[],
fragDepth: { value: number } | null = null ,
dualSourceBlending: boolean = false
): string {
if (outputs.length === 0 ) {
if (fragDepth) {
return `
@fragment fn main() -> @builtin(frag_depth) f32 {
return ${fragDepth.value.toFixed(kPlainTypeInfo['f32' ].fractionDigits)};
}`;
}
return `
@fragment fn main() {
}`;
}
const resultStrings = [] as string[];
let outputStructString = '' ;
if (fragDepth) {
resultStrings.push(`${fragDepth.value.toFixed(kPlainTypeInfo['f32' ].fractionDigits)}`);
outputStructString += `@builtin(frag_depth) depth_out: f32,\n`;
}
for (let i = 0 ; i < outputs.length; i++) {
const o = outputs[i];
if (o === null ) {
continue ;
}
const plainType = o.plainType;
const { suffix, fractionDigits } = kPlainTypeInfo[plainType];
let outputType;
const v = o.values.map(n => n.toFixed(fractionDigits));
switch (o.componentCount) {
case 1 :
outputType = plainType;
resultStrings.push(`${v[0 ]}${suffix}`);
break ;
case 2 :
outputType = `vec2<${plainType}>`;
resultStrings.push(`${outputType}(${v[0 ]}${suffix}, ${v[1 ]}${suffix})`);
break ;
case 3 :
outputType = `vec3<${plainType}>`;
resultStrings.push(`${outputType}(${v[0 ]}${suffix}, ${v[1 ]}${suffix}, ${v[2 ]}${suffix})`);
break ;
case 4 :
outputType = `vec4<${plainType}>`;
resultStrings.push(
`${outputType}(${v[0 ]}${suffix}, ${v[1 ]}${suffix}, ${v[2 ]}${suffix}, ${v[3 ]}${suffix})`
);
break ;
default :
unreachable();
}
if (dualSourceBlending) {
assert (i === 0 && outputs.length === 1 );
outputStructString += `
@location(0 ) @blend_src(0 ) o0 : ${outputType},
@location(0 ) @blend_src(1 ) o0_blend : ${outputType},
`;
resultStrings.push(resultStrings[0 ]);
break ;
} else {
outputStructString += `@location(${i}) o${i} : ${outputType},\n`;
}
}
return `
${dualSourceBlending ? 'enable dual_source_blending;' : '' }
struct Outputs {
${outputStructString}
}
@fragment fn main() -> Outputs {
return Outputs(${resultStrings.join(',' )});
}`;
}
export const kValidShaderStages = ['compute' , 'vertex' , 'fragment' ] as const ;
export type TValidShaderStage = (typeof kValidShaderStages)[number];
export type TShaderStage = TValidShaderStage | 'empty' ;
/**
* Return a foo shader of the given stage with the given entry point
* @ param shaderStage
* @ param entryPoint
* @ returns the shader string
*/
export function getShaderWithEntryPoint(shaderStage: TShaderStage, entryPoint: string): string {
let code;
switch (shaderStage) {
case 'compute' : {
code = `@compute @workgroup_size(1 ) fn ${entryPoint}() {}`;
break ;
}
case 'vertex' : {
code = `
@vertex fn ${entryPoint}() -> @builtin(position) vec4<f32> {
return vec4<f32>(0 .0 , 0 .0 , 0 .0 , 1 .0 );
}`;
break ;
}
case 'fragment' : {
code = `
@fragment fn ${entryPoint}() -> @location(0 ) vec4<f32> {
return vec4<f32>(0 .0 , 1 .0 , 0 .0 , 1 .0 );
}`;
break ;
}
case 'empty' :
default : {
code = '' ;
break ;
}
}
return code;
}
Messung V0.5 in Prozent C=93 H=91 G=91
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland