mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-05-31 23:27:43 +00:00
* fixed bugs in packed mode and enable pack mode tests in ci * removed unnecessary space * pr comments * pr comments * disable an average pool test * try disabling another avg pool * disable more avg pool tests * disable maxpool tests
289 lines
8.6 KiB
TypeScript
289 lines
8.6 KiB
TypeScript
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
import {BinaryOp} from '../../../ops/binary-op';
|
|
import {Tensor} from '../../../tensor';
|
|
import {BroadcastUtil, ShapeUtil} from '../../../util';
|
|
import {FunctionType, GlslValueFunction} from '../glsl-definitions';
|
|
import {getGlsl} from '../glsl-source';
|
|
import {WebGLInferenceHandler} from '../inference-handler';
|
|
import {ProgramInfo, RunData, WebGLOperator} from '../types';
|
|
|
|
export class WebGLBinaryOp extends BinaryOp implements WebGLOperator {
|
|
private usePackedTexture?: boolean;
|
|
|
|
constructor(
|
|
typeConstraint: readonly Tensor.DataType[], protected glslFunc: GlslValueFunction, opType?: string,
|
|
resultType?: Tensor.DataType) {
|
|
super(typeConstraint, opType, resultType);
|
|
}
|
|
run(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): Tensor[] {
|
|
return inferenceHandler.run(this, inputs);
|
|
}
|
|
createProgramInfo(handler: WebGLInferenceHandler, inputs: Tensor[]): ProgramInfo {
|
|
const isBroadcast = !ShapeUtil.areEqual(inputs[0].dims, inputs[1].dims);
|
|
|
|
// TODO fix bcast in packed mode.
|
|
if (this.usePackedTexture === undefined) {
|
|
this.usePackedTexture = !isBroadcast && handler.session.pack;
|
|
}
|
|
|
|
const inputLayouts = this.usePackedTexture ?
|
|
inputs.map(t => handler.getOrCreateTextureLayout(t, 4, true, t.dims, true)) :
|
|
inputs.map(t => handler.getOrCreateTextureLayout(t));
|
|
const ouputLayout = this.usePackedTexture ?
|
|
handler.createTextureLayoutFromShape(inputs[0].dims, 4, inputs[0].dims, {isPacked: true, reverseWH: true}) :
|
|
handler.createTextureLayoutFromShape(inputs[0].dims);
|
|
|
|
if (isBroadcast) {
|
|
const outputShape = BroadcastUtil.calcShape(inputs[0].dims, inputs[1].dims, false);
|
|
if (!outputShape) {
|
|
throw new Error('Can\'t perform binary op on the given tensors');
|
|
}
|
|
const outputRank = outputShape.length;
|
|
const aRank = inputs[0].dims.length !== 0 ? inputs[0].dims.length : 1;
|
|
const bRank = inputs[1].dims.length !== 0 ? inputs[1].dims.length : 1;
|
|
const aBcast = inputs[0].dims.length !== 0 ? 'bcastIndices_A(indices, aindices);' : 'aindices[0] = 0;';
|
|
const bBcast = inputs[1].dims.length !== 0 ? 'bcastIndices_B(indices, bindices);' : 'bindices[0] = 0;';
|
|
|
|
// TODO: for packed tensors, we need to implement logic to caculate textCoords for broadcast tensor
|
|
const shaderSource = `
|
|
${this.glslFunc.body}
|
|
float process(int indices[${outputRank}]) {
|
|
int aindices[${aRank}];
|
|
int bindices[${bRank}];
|
|
${aBcast}
|
|
${bBcast}
|
|
return ${this.glslFunc.name}(_A(aindices), _B(bindices));
|
|
}`;
|
|
const outputLayout = this.usePackedTexture ?
|
|
handler.createTextureLayoutFromShape(outputShape, 4, outputShape, {isPacked: true, reverseWH: true}) :
|
|
handler.createTextureLayoutFromShape(outputShape);
|
|
|
|
return {
|
|
inputLayouts,
|
|
outputLayout,
|
|
samplers: ['A', 'B'],
|
|
shaderSource,
|
|
expectPackedInputs: this.usePackedTexture,
|
|
expectPackedOutputs: this.usePackedTexture
|
|
};
|
|
}
|
|
const glsl = getGlsl(handler.session.backend.glContext.version);
|
|
const shaderSource = `
|
|
${this.glslFunc.body}
|
|
void main() {
|
|
vec4 v1 = ${glsl.texture2D}(A, TexCoords);
|
|
vec4 v2 = ${glsl.texture2D}(B, TexCoords);
|
|
vec4 result = ${this.glslFunc.name}(v1, v2);
|
|
${glsl.output} = result;
|
|
}
|
|
`;
|
|
|
|
if (this.usePackedTexture) {
|
|
return {
|
|
hasMain: true,
|
|
inputLayouts,
|
|
outputLayout: ouputLayout,
|
|
samplers: ['A', 'B'],
|
|
shaderSource,
|
|
expectPackedInputs: true,
|
|
expectPackedOutputs: true
|
|
};
|
|
} else {
|
|
return {
|
|
hasMain: true,
|
|
inputLayouts,
|
|
outputLayout: handler.createTextureLayoutFromShape(inputs[0].dims),
|
|
samplers: ['A', 'B'],
|
|
shaderSource,
|
|
};
|
|
}
|
|
}
|
|
createRunData(handler: WebGLInferenceHandler, programInfo: ProgramInfo, inputs: Tensor[]): RunData {
|
|
const inputTDs = this.usePackedTexture ?
|
|
inputs.map((t) => handler.getOrCreateTextureData(t, handler.getOrCreateTextureLayout(t, 1, false, [], true))) :
|
|
inputs.map((t, i) => handler.getOrCreateTextureData(t, programInfo.inputLayouts[i]));
|
|
return {
|
|
inputTextureDatas: inputTDs,
|
|
outputTextureData: handler.createTextureDataFromLayout(
|
|
programInfo.outputLayout, this.resultType ? this.resultType : inputs[0].type),
|
|
uniformData: {}
|
|
};
|
|
}
|
|
}
|
|
|
|
export function glslAdd(): GlslValueFunction {
|
|
const name = 'add_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return a + b;
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
return v1 + v2;
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslDiv(): GlslValueFunction {
|
|
const name = 'div_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return a / b;
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
return v1 / v2;
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslMul(): GlslValueFunction {
|
|
const name = 'mul_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return a * b;
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
return v1 * v2;
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslSub(): GlslValueFunction {
|
|
const name = 'sub_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return a - b;
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
return v1 - v2;
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslEqual(): GlslValueFunction {
|
|
const name = 'equal_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return float(a == b);
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
return vec4(equal(v1, v2));
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslGreater(): GlslValueFunction {
|
|
const name = 'greater_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return float(a > b);
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
return vec4( v1.r > v2.r ,
|
|
v1.g > v2.g,
|
|
v1.b > v2.b,
|
|
v1.a > v2.a );
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslLess(): GlslValueFunction {
|
|
const name = 'less_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return float(a < b);
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
return vec4( v1.r < v2.r ,
|
|
v1.g < v2.g,
|
|
v1.b < v2.b,
|
|
v1.a < v2.a );
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslAnd(): GlslValueFunction {
|
|
const name = 'and_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return float( bool(a) && bool(b) );
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
bvec4 b1 = bvec4(v1);
|
|
bvec4 b2 = bvec4(v2);
|
|
return vec4( b1.r && b2.r ,
|
|
b1.g && b2.g,
|
|
b1.b && b2.b,
|
|
b1.a && b2.a );
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslOr(): GlslValueFunction {
|
|
const name = 'or_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return float( bool(a) || bool(b) );
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
bvec4 b1 = bvec4(v1);
|
|
bvec4 b2 = bvec4(v2);
|
|
return vec4( b1.r || b2.r ,
|
|
b1.g || b2.g,
|
|
b1.b || b2.b,
|
|
b1.a || b2.a );
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslXor(): GlslValueFunction {
|
|
const name = 'xor_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return float( bool(a) ^^ bool(b) );
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
bvec4 b1 = bvec4(v1);
|
|
bvec4 b2 = bvec4(v2);
|
|
return vec4( b1.r ^^ b2.r ,
|
|
b1.g ^^ b2.g,
|
|
b1.b ^^ b2.b,
|
|
b1.a ^^ b2.a );
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
export function glslPow(): GlslValueFunction {
|
|
return glslBuiltinBinary('pow');
|
|
}
|
|
export function glslPRelu(): GlslValueFunction {
|
|
const name = 'prelu_';
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return a < 0.0 ? a * b: a;
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
return vec4(
|
|
v1.r < 0.0 ? v1.r * v2.r: v1.r,
|
|
v1.g < 0.0 ? v1.g * v2.g: v1.g,
|
|
v1.b < 0.0 ? v1.b * v2.b: v1.b,
|
|
v1.a < 0.0 ? v1.a * v2.a: v1.a
|
|
);
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|
|
|
|
function glslBuiltinBinary(fname: string): GlslValueFunction {
|
|
const name = `${fname}_`;
|
|
const body = `
|
|
float ${name}(float a, float b) {
|
|
return ${fname}(a, b);
|
|
}
|
|
vec4 ${name}(vec4 v1, vec4 v2) {
|
|
return ${fname}(v1, v2);
|
|
}
|
|
`;
|
|
return {body, name, type: FunctionType.ValueBased};
|
|
}
|