From 7449fa23806762cc69d2d8d05ec9765572e77b7c Mon Sep 17 00:00:00 2001 From: yangmingming Date: Tue, 20 Aug 2019 15:23:31 +0800 Subject: [PATCH] paddle web (#1811) speed up WebGL implementation --- web/ci.yml | 19 - web/demo/index.es6 | 457 +++++++ web/demo/index.html | 39 + web/demo/videoDemo.es6 | 57 + web/demo/videoDemo.html | 36 + web/package.json | 13 +- web/src/executor/camera.es6 | 142 +++ web/src/executor/executor.es6 | 22 +- web/src/executor/loader.es6 | 91 +- web/src/executor/postProcess.es6 | 262 ++++ web/src/executor/runner.es6 | 153 +++ web/src/factory/fshader/factory.es6 | 15 +- web/src/factory/fshader/ops.es6 | 46 + web/src/feed/ImageFeed.es6 | 39 +- web/src/feed/dataFeed.es6 | 42 + web/src/feed/io.es6 | 1 + web/src/gpu/gpu.es6 | 362 ++++-- web/src/ops/dummy.js | 12 - web/src/ops/feed.js | 13 - web/src/ops/fetch.js | 12 - web/src/package.json | 11 - web/src/runtime/runtime.es6 | 65 +- web/src/shader/atom/common_params.es6 | 20 +- .../atom/getArrayIndexFromTensorPos.es6 | 5 - .../atom/getArrayIndexFromTexturePos.es6 | 4 - .../shader/atom/getPixelsFromTexturePos.es6 | 4 +- web/src/shader/atom/getValueFromTensorPos.es6 | 25 +- .../atom/getValueFromTensorPosPacked.es6 | 26 + .../shader/atom/getValueFromTexturePos.es6 | 2 +- web/src/shader/atom/prefix.es6 | 4 + web/src/shader/atom/prefix2.es6 | 22 + web/src/shader/atom/suffix.es6 | 25 + web/src/shader/batchnorm/conf.es6 | 1 + web/src/shader/batchnorm/main.es6 | 2 +- web/src/shader/conv2d/conf.es6 | 1 + web/src/shader/conv2d/main.es6 | 8 +- web/src/shader/conv2d_depthwise/conf.es6 | 67 ++ web/src/shader/conv2d_depthwise/main.es6 | 42 + web/src/shader/conv2d_depthwise/params.es6 | 43 + .../shader/conv2d_elementwise_add/conf.es6 | 77 ++ .../shader/conv2d_elementwise_add/main.es6 | 53 + .../shader/conv2d_elementwise_add/params.es6 | 54 + .../conv2d_elementwise_add_winograd/conf.es6 | 72 ++ .../conv2d_elementwise_add_winograd/main.es6 | 98 ++ .../params.es6 | 47 + web/src/shader/dynamic/conf.es6 | 1 + web/src/shader/dynamic/main.es6 | 2 +- web/src/shader/elementwise_add/conf.es6 | 1 + web/src/shader/elementwise_add/main.es6 | 4 +- web/src/shader/mul/conf.es6 | 3 +- web/src/shader/mul/main.es6 | 2 +- web/src/shader/pool2d/conf.es6 | 1 + web/src/shader/pool2d/main.es6 | 6 +- web/src/shader/pool2d_avg/conf.es6 | 47 + web/src/shader/pool2d_avg/main.es6 | 40 + web/src/shader/pool2d_avg/params.es6 | 30 + web/src/shader/pool2d_max/conf.es6 | 47 + web/src/shader/pool2d_max/main.es6 | 45 + web/src/shader/pool2d_max/params.es6 | 29 + web/src/shader/pool2d_winograd/conf.es6 | 50 + web/src/shader/pool2d_winograd/main.es6 | 63 + web/src/shader/pool2d_winograd/params.es6 | 33 + web/src/shader/softmax/conf.es6 | 3 +- web/src/shader/v_shader2.es6 | 15 + web/src/test/getMaxUniforms.es6 | 59 + web/src/utils/models.es6 | 46 + web/src/utils/opData.es6 | 123 +- web/src/utils/tensor.es6 | 52 +- web/src/utils/utils.es6 | 91 +- web/test/testUtils/diff.js | 1055 ----------------- web/tools/logger.es6 | 101 ++ web/tools/toBinaryFile.py | 18 +- web/webpack.config.js | 48 + 73 files changed, 3220 insertions(+), 1406 deletions(-) delete mode 100644 web/ci.yml create mode 100644 web/demo/index.es6 create mode 100644 web/demo/index.html create mode 100644 web/demo/videoDemo.es6 create mode 100644 web/demo/videoDemo.html create mode 100644 web/src/executor/camera.es6 create mode 100644 web/src/executor/postProcess.es6 create mode 100644 web/src/executor/runner.es6 create mode 100644 web/src/feed/dataFeed.es6 delete mode 100644 web/src/ops/dummy.js delete mode 100644 web/src/ops/feed.js delete mode 100644 web/src/ops/fetch.js delete mode 100644 web/src/package.json create mode 100644 web/src/shader/atom/getValueFromTensorPosPacked.es6 create mode 100644 web/src/shader/atom/prefix2.es6 create mode 100644 web/src/shader/conv2d_depthwise/conf.es6 create mode 100644 web/src/shader/conv2d_depthwise/main.es6 create mode 100644 web/src/shader/conv2d_depthwise/params.es6 create mode 100644 web/src/shader/conv2d_elementwise_add/conf.es6 create mode 100644 web/src/shader/conv2d_elementwise_add/main.es6 create mode 100644 web/src/shader/conv2d_elementwise_add/params.es6 create mode 100644 web/src/shader/conv2d_elementwise_add_winograd/conf.es6 create mode 100644 web/src/shader/conv2d_elementwise_add_winograd/main.es6 create mode 100644 web/src/shader/conv2d_elementwise_add_winograd/params.es6 create mode 100644 web/src/shader/pool2d_avg/conf.es6 create mode 100644 web/src/shader/pool2d_avg/main.es6 create mode 100644 web/src/shader/pool2d_avg/params.es6 create mode 100644 web/src/shader/pool2d_max/conf.es6 create mode 100644 web/src/shader/pool2d_max/main.es6 create mode 100644 web/src/shader/pool2d_max/params.es6 create mode 100644 web/src/shader/pool2d_winograd/conf.es6 create mode 100644 web/src/shader/pool2d_winograd/main.es6 create mode 100644 web/src/shader/pool2d_winograd/params.es6 create mode 100644 web/src/shader/v_shader2.es6 create mode 100644 web/src/test/getMaxUniforms.es6 create mode 100644 web/src/utils/models.es6 delete mode 100644 web/test/testUtils/diff.js create mode 100644 web/tools/logger.es6 create mode 100644 web/webpack.config.js diff --git a/web/ci.yml b/web/ci.yml deleted file mode 100644 index a78e80a1a5..0000000000 --- a/web/ci.yml +++ /dev/null @@ -1,19 +0,0 @@ -Global: - tool: build_submitter - -Default: - profile: [buildProduction] - -Profiles: - - - profile: - name: buildProduction - env: cmc_standard - command: export NODE_ENV=production && sh scripts/build.sh - release: true - - - profile: - name: buildDevelopment - env: cmc_standard - command: export NODE_ENV=development && sh scripts/build.sh - release: true diff --git a/web/demo/index.es6 b/web/demo/index.es6 new file mode 100644 index 0000000000..1095b4a8a6 --- /dev/null +++ b/web/demo/index.es6 @@ -0,0 +1,457 @@ +import 'babel-polyfill'; +import VConsole from 'vconsole'; +import Graph from '../src/executor/loader'; +import IO from '../src/feed/imageFeed'; +import Logger from '../tools/logger'; +window.log = new Logger(); +// var vConsole = new VConsole(); +// 统计参数 +window.badCases = []; +// import Utils from '../src/utils/utils'; +// 获取map表 +// import Map from '../test/data/map'; + +// import demoPic from './bbt1.jpg'; +// import demoPic2 from './bbt2.jpg'; +// import demoPic3 from './bbt3.jpg'; +// import demoPic4 from './bbt4.jpg'; +// import demoPic5 from './bbt5.jpg'; +// import testOutput from './data.json'; + +// 后处理测试用例 +// let tempPic = [demoPic, demoPic2, demoPic3, demoPic4, demoPic5]; +/** + * @file model demo 入口文件 + * @author wangqun@baidu.com + * + */ +// 'http://mms-xr.cdn.bcebos.com/paddle/mnist/model.json' +// 模型输出shape +const outputShapes = { + '608': { + from: [19, 19, 25, 1], + to: [19, 19, 5, 5] + }, + '320': { + from: [10, 10, 25, 1], + to: [10, 10, 5, 5] + }, + '320fused': { + from: [10, 10, 25, 1], + to: [10, 10, 5, 5] + }, + 'separate': { + from: [10, 10, 25, 1], + to: [10, 10, 5, 5] + } +}; +// 模型feed数据 +const feedShape = { + '608': { + fw: 608, + fh: 608 + }, + '320': { + fw: 320, + fh: 320 + }, + '320fused': { + fw: 320, + fh: 320 + }, + 'separate': { + fw: 320, + fh: 320 + } +}; +// 模型路径 +const modelPath = { + '608': 'faceModel', + '320': 'facemodel320', + '320fused': 'facemodelfused', + 'separate': 'separablemodel' +}; +const modelType = 'separate'; +const path = modelPath[modelType]; +// 统计参数 +let loaded = false; +let model = {}; +window.statistic = []; +const {fw, fh} = feedShape[modelType]; +// 第一遍执行比较慢 所以预热一下 +async function preheat() { + const io = new IO(); + let feed = io.process({ + input: video, + params: { + gapFillWith: '#000', // 缩放后用什么填充不足方形部分 + targetSize: { + height: fw, + width: fh + }, + targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名 + // shape: [3, 608, 608], // 预设tensor形状 + mean: [117.001, 114.697, 97.404], // 预设期望 + // std: [0.229, 0.224, 0.225] // 预设方差 + } + }); + const MODEL_URL = `/${path}/model.json`; + const MODEL_CONFIG = { + dir: `/${path}/`, // 存放模型的文件夹 + main: 'model.json', // 主文件 + }; + loaded = true; + const graphModel = new Graph(); + log.start('加载模型'); + model = await graphModel.loadGraphModel(MODEL_CONFIG, { + multipart: true, + dataType: 'binary', + binaryOption: { + fileCount: 1, // 切成了多少文件 + getFileName(i) { // 获取第i个文件的名称 + return 'chunk_0.dat'; + } + }, + feed + }); + log.end('加载模型'); + let inst = model.execute({ + input: feed + }); +} +async function run(input) { + // const input = document.getElementById('mobilenet'); + log.start('总耗时'); + const io = new IO(); + log.start('预处理'); + let feed = io.process({ + input: input, + params: { + gapFillWith: '#000', // 缩放后用什么填充不足方形部分 + targetSize: { + height: fw, + width: fh + }, + targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名 + // shape: [3, 608, 608], // 预设tensor形状 + mean: [117.001, 114.697, 97.404], // 预设期望 + // std: [0.229, 0.224, 0.225] // 预设方差 + } + }); + log.end('预处理'); + if (!loaded) { + const MODEL_URL = `/${path}/model.json`; + const MODEL_CONFIG = { + dir: `/${path}/`, // 存放模型的文件夹 + main: 'model.json', // 主文件 + }; + loaded = true; + const graphModel = new Graph(); + log.start('加载模型'); + model = await graphModel.loadGraphModel(MODEL_CONFIG, { + multipart: true, + dataType: 'binary', + binaryOption: { + fileCount: 1, // 切成了多少文件 + getFileName(i) { // 获取第i个文件的名称 + return 'chunk_0.dat'; + } + }, + feed + }); + log.end('加载模型'); + } + + log.start('运行耗时'); + let inst = model.execute({ + input: feed + }); + + // 其实这里应该有个fetch的执行调用或者fetch的输出 + let result = await inst.read(); + log.end('后处理-读取数据'); + // console.dir(['result', result]); + log.start('后处理-形状调整'); + const newData = []; + let newIndex = -1; + const [w, h, c, b] = outputShapes[modelType].from; + // c channel + for (let i = 0; i < c; i++) { + // height channel + for (let j = 0; j < h; j++) { + // width channel + for (let k = 0; k < w; k++) { + // position: (0, 0, 0, 0) + const index = j * (c * h) + k * c + i; + // const index = j * (i * k) + k * i + i; + newData[++newIndex] = result[index]; + } + } + } + log.end('后处理-形状调整'); + log.start('后处理-画框'); + testRun(newData, input); + log.end('后处理-画框'); + log.end('后处理'); + log.end('总耗时'); +} +var image = ''; + +function selectImage(file) { + if (!file.files || !file.files[0]) { + return; + } + let reader = new FileReader(); + reader.onload = function (evt) { + let img = document.getElementById('image'); + img.src = evt.target.result; + img.onload = function() { + log.during('每次执行的时间间隔'); + run(img); + }; + image = evt.target.result; + } + reader.readAsDataURL(file.files[0]); +} +// selectImage +document.getElementById("uploadImg").onchange = function () { + selectImage(this); +}; + +/* 后处理图片 by zhangmiao06 */ +let preTestRun = (index) => { + let img = document.getElementById('image'); + img.src = tempPic[index]; + img.onload = function() { + testRun(testOutput.data[index], img); + }; +}; +let testRun = (data, img) => { + // console.log('ori', data); + const {from, to} = outputShapes[modelType]; + // let shape = [1, 25, 19, 19]; + let shape = [].concat(from).reverse(); + // 1.从一维数组到1*25*19*19 + let formatData = reshapeMany({ + data: data, + reshapeShape: shape + }); + // console.log('一维到多维', formatData); + // 2.从1*25*19*19 到 19*19*25*1 + let formatData2 = transpose({ + data: formatData, + shape: shape, + transposeShape: [2, 3, 1, 0] + }); + // console.log('transpose', formatData2); + // 3.从19*19*25*1到19*19*5*5 + let formatData3 = reshape({ + data: formatData2, + shape: from, + reshapeShape: to + }); + // console.log('reshape', formatData3); + // 4.运算 + let finalData = handleFinal(formatData3, shape, img); + // console.log('final', finalData); + // 5.处理画布 + // handleCanvas(finalData, img); + handleDiv(finalData, img); +}; + +// sigmoid +let sigmoid = (x) => { + if (x < -100) { + return 0.0; + } + return 1 / (1 + Math.exp(-x)); +} + +// transpose +let transpose = (data) => { + let shape = data.shape; + let transposeShape = data.transposeShape; + let formatData = data.data; + let formatData2 = []; + for(let n = 0; n < shape[transposeShape[0]]; n++) { + let nData = []; + for(let c = 0; c < shape[transposeShape[1]]; c++) { + let cData = []; + for(let row = 0; row < shape[transposeShape[2]]; row++) { + let rowData = []; + for(let col = 0; col < shape[transposeShape[3]]; col++) { + let tempArr = [n, c, row, col]; + let newN = n; + let newC = c; + let newW = row; + let newH = col; + transposeShape.forEach((item, index)=> { + switch(item) { + case 0: + newN = tempArr[index]; + break; + case 1: + newC = tempArr[index]; + break; + case 2: + newW = tempArr[index]; + break; + case 3: + newH = tempArr[index]; + } + }); + rowData.push(formatData[newN][newC][newW][newH]); + } + cData.push(rowData); + } + nData.push(cData); + } + formatData2.push(nData); + } + return formatData2; +}; + +// reshape +let reshape = (data) =>{ + let formatData2 = data.data; + let shape = data.shape; + let reshapeShape = data.reshapeShape; + // 1.变成一维 + let tempData = reshapeOne({ + data: formatData2, + shape: shape + }); + // 2.变成多维 + let formatData3 = reshapeMany({ + data: tempData, + reshapeShape: reshapeShape + }); + return formatData3; +}; + +// 变成一维 +let reshapeOne = (data) => { + let formatData2 = data.data; + let shape = data.shape; + let tempData = []; + for(let n = 0; n < shape[0]; n++) { + for(let c = 0; c < shape[1]; c++) { + for(let row = 0; row < shape[2]; row++) { + for(let col = 0; col < shape[3]; col++) { + tempData.push(formatData2[n][c][row][col]); + } + } + } + } + return tempData; +}; + +// 变成多维 +let reshapeMany = (data) => { + let tempData = data.data; + let reshapeShape = data.reshapeShape; + let formatData3 = []; + for(let n = 0; n < reshapeShape[0]; n++) { + let nData = []; + for(let c = 0; c < reshapeShape[1]; c++) { + let cData = []; + for(let row = 0; row < reshapeShape[2]; row++) { + let rowData = []; + for(let col = 0; col < reshapeShape[3]; col++) { + let tempN = n * reshapeShape[1] * reshapeShape[2] * reshapeShape[3]; + let tempC = c * reshapeShape[2] * reshapeShape[3]; + let tempRow = row * reshapeShape[3]; + rowData.push(tempData[tempN + tempC + tempRow + col]); + } + cData.push(rowData); + } + nData.push(cData); + } + formatData3.push(nData); + } + return formatData3; +}; +let calSize = (img) => { + let w1 = img.width; + let h1 = img.height; + let wh1 = Math.max(w1, h1); + // let factor = 608.0 / wh1; + let factor = fw / wh1; + let width = Math.round(w1 * factor); + let height = Math.round(h1 * factor); + return [w1, h1, width, height]; +}; +// 处理运算 +let handleFinal = (formatData3, shape, img) => { + let finalData = []; + let c = shape[2]; + let [w1, h1, width, height] = calSize(img); + let factorX = Math.max(width, height) / width; + let factorY = Math.max(width, height) / height; + + let maxProb = 0.0; + let anchors = [[1.603231, 2.094468], [6.041143, 7.080126], [2.882459, 3.518061], [4.266906, 5.178857], [9.041765, 10.66308]]; + + for(let i = 0; i < shape[2]; i++) { + for(let j = 0; j < shape[3]; j++) { + for(let k = 0; k < anchors.length; k++) { + let [a1, a2, a3, a4, prob] = formatData3[i][j][k]; + prob = sigmoid(prob); + if (prob > maxProb && prob >= 0.5) { + let ctx = (j + sigmoid(a1)) / c * factorX; + let cty = (i + sigmoid(a2)) / c * factorY; + let col = Math.exp(a3) * anchors[k][0] / c * factorX; + let row = Math.exp(a4) * anchors[k][1] / c * factorY; + let x = (ctx - (col / 2)); + let y = (cty - (row / 2)); + finalData.push([x * w1, y * h1, col * w1, row * h1, prob]); + } + } + } + } + return finalData; +}; + +// 处理画布 +let handleCanvas = (finalData, img) => { + let myCanvas = document.getElementById('myCanvas'); + let [w1, h1, width, height] = calSize(img); + myCanvas.width = w1; + myCanvas.height = h1; + let ctx = myCanvas.getContext("2d"); + ctx.drawImage(img, 0, 0, w1, h1); + + finalData.forEach((demoArr,index) => { + let [demoLeft, demoTop, demoWidth, demoHeight, prob] = demoArr; + ctx.beginPath(); + ctx.strokeStyle="red"; + ctx.moveTo(demoLeft, demoTop); + ctx.lineTo(demoLeft + demoWidth, demoTop); + ctx.lineTo(demoLeft + demoWidth, demoTop + demoHeight); + ctx.lineTo(demoLeft, demoTop + demoHeight); + ctx.closePath(); + ctx.stroke(); + }); +}; +let handleDiv = (finalData, img) => { + if (finalData.length < 1) { + return false; + } + let myCanvas = document.getElementById('myDiv'); + let maxIndex = 0; + if (finalData.length > 1) { + for(let i = 1; i < finalData.length; i++) { + if (finalData[i].prob > finalData[maxIndex].prob) { + maxIndex = i; + } + } + } + let [demoLeft, demoTop, demoWidth, demoHeight, prob] = finalData[maxIndex]; + myCanvas.style.width = demoWidth; + myCanvas.style.height = demoHeight; + myCanvas.style.left = demoLeft; + myCanvas.style.top = demoTop; +}; +// preTestRun(0); + +// run(document.getElementById('pic')); diff --git a/web/demo/index.html b/web/demo/index.html new file mode 100644 index 0000000000..c418dffd78 --- /dev/null +++ b/web/demo/index.html @@ -0,0 +1,39 @@ + + + + + paddle web demo + + + + + +
+ + + + +

原图片

+
+ +
+
+

画布

+ +
+ +
+ + + + + diff --git a/web/demo/videoDemo.es6 b/web/demo/videoDemo.es6 new file mode 100644 index 0000000000..98e0a6e2b3 --- /dev/null +++ b/web/demo/videoDemo.es6 @@ -0,0 +1,57 @@ +import 'babel-polyfill'; +import Runner from '../src/executor/runner'; +import Camera from '../src/executor/camera'; +// 调试工具 +// import vConsole from 'vconsole'; +// const theConsole = new vConsole(); +let startBtn = document.getElementById('start'); +let stopBtn = document.getElementById('stop') + +const runner = new Runner({ + // 用哪个模型 + modelName: 'separate' // '608' | '320' | '320fused' | 'separate' +}); +startBtn.disabled = true; +runner.preheat() +.then(() =>{ + startBtn.disabled = false +}); + +const domElement = document.getElementById('video'); +const myCanvas = document.getElementById('myDiv'); +const videoSelect = document.getElementById('videoSelect'); +let camera = new Camera({ + // 用来显示摄像头图像的dom + videoDom: domElement +}); +camera.getDevices().then(devices => { + if (devices.length) { + camera.run(devices[0].deviceId); + devices.forEach((element, index) => { + let option = document.createElement('option'); + option.value = element.deviceId; + option.text = (index + 1); + videoSelect.appendChild(option); + }); + videoSelect.onchange = () => { + camera.run(videoSelect.value); + }; + } + else { + camera.run(); + } +}); +const handleDiv = function (data) { + myCanvas.style.width = (data ? data[0] : 0) + 'px'; + myCanvas.style.height = (data ? data[0] : 0) + 'px'; + myCanvas.style.left = (data ? data[2] : 0) + 'px'; + myCanvas.style.top = (data ? data[3] : 0) + 'px'; +} +startBtn.addEventListener('click', function () { + startBtn.disabled = true; + runner.startStream(() => camera.curVideo, handleDiv); +}); +stopBtn.addEventListener('click', function () { + startBtn.disabled = false; + runner.stopStream(); +}); \ No newline at end of file diff --git a/web/demo/videoDemo.html b/web/demo/videoDemo.html new file mode 100644 index 0000000000..a4009fbd29 --- /dev/null +++ b/web/demo/videoDemo.html @@ -0,0 +1,36 @@ + + + + + + + 识别摄像头里的脸 + + + + +

+ + +

+ +

tips

+
+ + + + diff --git a/web/package.json b/web/package.json index 1fdcb34cca..d2c45041a3 100644 --- a/web/package.json +++ b/web/package.json @@ -1,18 +1,16 @@ { - "name": "paddle-web", + "name": "paddle-web-demo", "version": "1.0.0", "description": "paddle", "main": "index.js", "scripts": { "server": "parcel ./src/index.html", "testDemo": "parcel ./demo/index.html", + "testSDemo": "parcel ./demo/index.html --port 8125 --https", + "testVideoDemo": "parcel ./demo/videoDemo.html --port 8123 --https", "unit": "parcel ./test/unitTest.html", "test": "echo \"Error: no test specified\" && exit 1" }, - "repository": { - "type": "git", - "url": "https://github.com/PaddlePaddle/paddle-mobile" - }, "devDependencies": { "babel-core": "^6.26.3", "babel-plugin-transform-class-properties": "^6.24.1", @@ -24,12 +22,13 @@ "babel-preset-stage-0": "^6.24.1", "babel-runtime": "^6.26.0", "parcel-bundler": "^1.10.3", - "axios": ">=0.18.1" + "axios": "^0.17.1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { - "js-file-download": "^0.4.5" + "js-file-download": "^0.4.5", + "vconsole": "^3.3.2" } } diff --git a/web/src/executor/camera.es6 b/web/src/executor/camera.es6 new file mode 100644 index 0000000000..1050c14bf3 --- /dev/null +++ b/web/src/executor/camera.es6 @@ -0,0 +1,142 @@ +/** + * @file 视频流类 + * @author zhangmiao06 + */ +import $ from 'webpack-zepto'; +export default class Camera { + constructor(option) { + this.option = option; + this.video = option.videoDom; + // 标志是否可以切换摄像头 + this.haveDevice = false; + // 设置视频流宽度 + if (option.width) { + this.video.width = option.width; + } + else if (option.height) { + this.video.height = option.height; + } + else { + this.video.width = window.innerWidth; + } + this.deviceInfos = []; + if(navigator.mediaDevices) { + this.haveDevice = true; + } + } + + // 访问用户媒体设备的兼容方法 + run(deviceId) { + if (window.stream) { + window.stream.getTracks().forEach(function (track) { + track.stop(); + }); + } + + let constraints = { + video: {} + }; + const success = this.success.bind(this); + const error = this.error.bind(this); + if (this.deviceInfos.length) { + constraints.video.deviceId= {exact: deviceId || this.deviceInfos[0]}; + } + + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + // 最新的标准API + navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error); + } + else if (navigator.webkitGetUserMedia) { + // webkit核心浏览器 + navigator.webkitGetUserMedia(constraints, success, error); + } + else if (navigator.mozGetUserMedia) { + // firfox浏览器 + navigator.mozGetUserMedia(constraints, success, error); + } + else if (navigator.getUserMedia) { + // 旧版API + navigator.getUserMedia(constraints, success, error); + } + else { + console.log('您的浏览器不支持获取视频流~'); + } + } + + success(stream) { + const domElement = this.video; + // make stream available to console + window.stream = stream; + // 旧的浏览器可能没有srcObject + const URL = window.URL || window.webkitURL || window.mozURL || window.msURL; + if ('srcObject' in domElement) { + try { + domElement.srcObject = stream; + } catch (error) { + domElement.src = URL.createObjectURL(stream) || stream; + } + } else { + // 防止再新的浏览器里使用它,应为它已经不再支持了 + domElement.src = URL.createObjectURL(stream) || stream; + } + domElement.addEventListener('loadeddata', () => { + // 设置视频流高度 + if (this.option.height) { + domElement.width = $(domElement).width(); + } + else { + domElement.height = $(domElement).height(); + } + domElement.play(); + }, false); + } + + error(error) { + alert(`访问用户媒体设备失败${error.name}, ${error.message}`); + } + // 处理摄像头列表 + gotDevices(deviceInfos) { + const ua = navigator.userAgent; + const isIos = /iphone|ipod|ipad/ig.test(ua); + + let delt = -1; + const range = deviceInfos.length; + let start = range - 1; + let end = - 1; + // ios机型camare顺序相反 + if (isIos) { + delt = 1; + start = 0; + end = range; + } + for (let i = start; i !== end; i += delt) { + const deviceInfo = deviceInfos[i]; + if (deviceInfo.kind === 'videoinput') { + this.deviceInfos.push(deviceInfos[i]); + } + } + } + + get curVideo() { + return this.video; + } + getDevices() { + return new Promise((resolve, reject)=> { + if (this.haveDevice) { + if (this.deviceInfos.length) { + resolve(this.deviceInfos); + } + else { + navigator.mediaDevices.enumerateDevices() + .then(this.gotDevices.bind(this)) + .then(()=> { + resolve(this.deviceInfos); + }); + } + } + else { + resolve([]); + } + }); + } +} diff --git a/web/src/executor/executor.es6 b/web/src/executor/executor.es6 index dc9b689f10..a00dc9b7fd 100644 --- a/web/src/executor/executor.es6 +++ b/web/src/executor/executor.es6 @@ -10,7 +10,7 @@ export default class GraphExecutor { constructor(model) { this.inputs = model.inputs; this.outputs = model.outputs; - this.attrs = model.attrs; + this.attrs = model.attrs || model['sub-attrs']; this.type = model.type; this.finish = false; this.next = null; @@ -53,7 +53,7 @@ export default class GraphExecutor { else if (this.type === 'fetch') { return this.inputs.X; } - return null; + return this.inputs.Input || this.inputs.X; } get outputsName() { @@ -68,29 +68,31 @@ export default class GraphExecutor { return this.outputs.Y; } else { - return this.outputs.Out; + return this.outputs.Out || this.outputs.Output; } } /** * 将输入数据和具体op进行关联,触发执行具体每一个op - * @param inputs * @param runtime + * @param isRendered */ - execute(runtime) { + execute(runtime, isRendered) { // console.log(inputs, outputs); if (this.type !== 'feed') { - let time = +Date.now(); - runtime.run(this.type, this.opData); + // let time = +Date.now(); + log.start(this.opData.iLayer + '-' + this.type); + runtime.run(this.type, this.opData, isRendered); + log.end(this.opData.iLayer + '-' + this.type); // if (runtime.gpu.frameBufferIsComplete().isComplete) { // var result = runtime.read(); // let res = Array.prototype.slice.call(result); // fileDownload(res, "result.csv"); // } - let length = statistic.length; - statistic[length - 1].type = this.type; - statistic[length - 1].runTime = +Date.now() - time; + // let length = statistic.length; + // statistic[length - 1].type = this.type; + // statistic[length - 1].runTime = +Date.now() - time; // if (this.type === 'scale') { // console.log('时间是:' + (+Date.now() - start)); // } diff --git a/web/src/executor/loader.es6 b/web/src/executor/loader.es6 index de7341aa80..8be4ab4db0 100644 --- a/web/src/executor/loader.es6 +++ b/web/src/executor/loader.es6 @@ -26,6 +26,8 @@ export default class GraphModel { this.feedOp = null; this.feedItem = null; this.isExecuted = false; + // 网络层数 + this.iLayer = 0; // fetch xhr jsonp this.params = {type: 'fetch'}; // 设置分片加载model @@ -36,22 +38,41 @@ export default class GraphModel { this.binaryOption = loadOptions.binaryOption; } } - // op runner - this.inst = Runtime.init({ - 'width_raw_canvas': 512, - 'height_raw_canvas': 512 - }); - if (this.loadOptions === null) { + + if (!this.loadOptions) { this.loadOptions = {}; + } else { + // op runner + this.inst = Runtime.init(); + factory.setWebglVersion(this.inst.getWebglVersion()); + // this.fetchJson(this.modelGonfig.dir + 'x.json').then(data => { + // const [b, c, h, w] = [1, 3, 320, 320]; + // const size = data.length; + // const total = 3 * 320 * 320; + // this.testData = new Float32Array(total); + // for (let i = 0; i < size; i++) { + // let j = i / (c * w) | 0; + // let k = i % (c * w); + // let b1 = j / h | 0; + // let h1 = j % h; + // let c1 = k % c; + // let w1 = k / c | 0; + // let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1; + // this.testData[i] = data[l]; + // } + // }); } } fetchOneChunk(path) { - console.time(path) - return fetch(path).then(request => { - console.timeEnd(path); + return this.fetch(path).then(request => { return request.arrayBuffer(); }) } + fetchJson(path) { + return this.fetch(path).then(request => { + return request.json(); + }) + } fetchAllData() { // todo 兼容一下json的模式 let counts = this.binaryOption.fileCount; @@ -61,8 +82,6 @@ export default class GraphModel { this.fetchOneChunk(this.modelGonfig.dir + this.binaryOption.getFileName(i)) ); } - // 1个文件 - // let chunkArray = [this.fetchOneChunk('/faceModel/mergedData.dat')]; console.time('加载时间'); return Promise.all(chunkArray).then(chunks => { console.timeEnd('加载时间'); @@ -109,13 +128,24 @@ export default class GraphModel { marker += len; }); } + + fetch(path, params) { + params = params || this.params; + let method = params.method || 'get'; + let mode = params.mode || 'cors'; + let myHeaders = new Headers(); + return fetch(path, { + method: method, + mode: mode, + credentials: 'include', + headers: myHeaders + }); + } + fetchModel(params) { params = params || this.params; const path = this.modelGonfig.dir + this.modelGonfig.main; - let URL_SCHEME_REGEX = /^https?:\/\//; let load = null; - let method = params.method || 'get'; - let mode = params.mode || 'cors'; // jsonp请求方式 if (params && params.type === 'jsonp') { let json; @@ -139,14 +169,8 @@ export default class GraphModel { } // 原生fetch else if (params.type === 'fetch') { - let myHeaders = new Headers(); load = new Promise((resolve, reject) => { - fetch(path, { - method: method, - mode: mode, - credentials: "include", - headers: myHeaders - }) + this.fetch(path, params) .then(response => response.json()) .then(responseData => resolve(responseData)) .then(err => reject(err)) @@ -161,23 +185,13 @@ export default class GraphModel { } async load() { let that = this; - console.time('生成op数据之前') - console.time('fetchModel'); const artifacts = this.handler = await this.fetchModel(); - console.timeEnd('fetchModel'); if (this.multipart === true) { - console.time('6个文件准备好op数据'); await this.fetchAllData() .then(() => this.traverse(artifacts.vars)); - console.timeEnd('6个文件准备好op数据'); } - console.time('createOpsMap'); const opsMap = this.createOpsMap(artifacts.ops, artifacts.vars); - console.timeEnd('createOpsMap'); - console.time('constructOpsMap'); this.weightMap = this.constructOpsMap(opsMap); - console.timeEnd('constructOpsMap'); - console.timeEnd('生成op数据之前') // 生成op数据 this.weightMap.forEach(op => { const type = op.type; @@ -192,23 +206,27 @@ export default class GraphModel { const opData = new OpData(op.type, tensor.inputs, tensor.outputs, tensor.attrs); const name = opData.name; const fsCode = factory.buildShader(name, opData.data); - opData.fshader = this.inst.createFragmentShader(fsCode); + opData.fsCode = fsCode; + opData.program = this.inst.createProgram(fsCode, opData.tensor['out']); opData.renderData = opConfs[name].map(elem => { let item = Object.assign({}, elem); const tensorData = opData.tensor[item.tensor]; if (item.type === 'texture') { item.data = tensorData.data; - if (this.feedOp.id === op.id) { + if (this.feedOp.id === op.id && item.tensor === 'origin') { item.shape = tensorData.shape; this.feedItem = item; } item['width_texture'] = tensorData['width_texture']; item['height_texture'] = tensorData['height_texture']; + item['channel'] = tensorData['channel']; } else if (item.type === 'uniform') { item.data = tensorData[item.variable]; } return item; }); + // console.timeEnd('opData.renderData'); + opData.iLayer = this.iLayer++; op.opData = opData; // delete op.inputs; // delete op.outputs; @@ -218,7 +236,7 @@ export default class GraphModel { if (executor.type === 'fetch') { return; } - executor.execute(this.inst); + executor.execute(this.inst, this.isExecuted); if (executor.next) { const id = executor.next; const next = this.getTensor(id); @@ -245,13 +263,12 @@ export default class GraphModel { } let start = +Date.now(); this.execute_(executor[0]); - console.log('总的执行时间是' + (+Date.now() - start)); this.isExecuted = true; return this.inst; } updateFeed() { this.feedItem.data = this.feed.input[0].data; - Utils.img2texture(this.feedItem); + // Utils.img2texture(this.feedItem); } /** * predict enter @@ -282,6 +299,7 @@ export default class GraphModel { input[key] = io.fromPixels(data, pixel); } else if ((key === 'Input') && (inputName === 'image' || inputName === 'x')) { + // that.feed.input[0].data = that.testData; input[key] = that.feed.input; that.feedOp = executor; } @@ -289,6 +307,7 @@ export default class GraphModel { input[key] = that.getTensorAttr(input[key][0]); } }); + // console.log(input); const tensor = { inputs: input, outputs: output, diff --git a/web/src/executor/postProcess.es6 b/web/src/executor/postProcess.es6 new file mode 100644 index 0000000000..5e4d1672e3 --- /dev/null +++ b/web/src/executor/postProcess.es6 @@ -0,0 +1,262 @@ +/* eslint-disable */ + +/* 后处理图片 by zhangmiao06 */ +// let preTestRun = index => { +// let img = document.getElementById('image'); +// img.src = tempPic[index]; +// img.onload = function () { +// testRun(testOutput.data[index], img); +// }; +// }; + +import models from '../utils/models'; + +const isSimilar = (r1, r2, threshold = 5) => { + return Math.max(Math.abs(r1[0] - r2[0]), Math.abs(r1[1] - r2[1])) < threshold; + // return Math.abs((r1[0] + r1[1] + r1[2] + r1[3]) - (r2[0] + r2[1] + r2[2] + r2[3])) < threshold; +} + +// sigmoid +let sigmoid = (x) => { + if (x < -100) { + return 0.0; + } + + return 1 / (1 + Math.exp(-x)); +}; + +// transpose +let transpose = (data) => { + let shape = data.shape; + let transposeShape = data.transposeShape; + let formatData = data.data; + let formatData2 = []; + for (let n = 0; n < shape[transposeShape[0]]; n++) { + let nData = []; + for (let c = 0; c < shape[transposeShape[1]]; c++) { + let cData = []; + for (let row = 0; row < shape[transposeShape[2]]; row++) { + let rowData = []; + for (let col = 0; col < shape[transposeShape[3]]; col++) { + let tempArr = [n, c, row, col]; + let newN = n; + let newC = c; + let newW = row; + let newH = col; + transposeShape.forEach((item, index) => { + switch (item) { + case 0: + newN = tempArr[index]; + break; + case 1: + newC = tempArr[index]; + break; + case 2: + newW = tempArr[index]; + break; + case 3: + newH = tempArr[index]; + } + }); + rowData.push(formatData[newN][newC][newW][newH]); + } + cData.push(rowData); + } + nData.push(cData); + } + formatData2.push(nData); + } + return formatData2; +}; + +// reshape +const reshape = (data) => { + let formatData2 = data.data; + let shape = data.shape; + let reshapeShape = data.reshapeShape; + // 1.变成一维 + let tempData = reshapeOne({ + data: formatData2, + shape: shape + }); + // 2.变成多维 + let formatData3 = reshapeMany({ + data: tempData, + reshapeShape: reshapeShape + }); + return formatData3; +}; + +// 变成一维 +const reshapeOne = (data) => { + let formatData2 = data.data; + let shape = data.shape; + let tempData = []; + for (let n = 0; n < shape[0]; n++) { + for (let c = 0; c < shape[1]; c++) { + for (let row = 0; row < shape[2]; row++) { + for (let col = 0; col < shape[3]; col++) { + tempData.push(formatData2[n][c][row][col]); + } + } + } + } + return tempData; +}; + +// 变成多维 +const reshapeMany = data => { + let tempData = data.data; + let reshapeShape = data.reshapeShape; + let formatData3 = []; + for (let n = 0; n < reshapeShape[0]; n++) { + let nData = []; + for (let c = 0; c < reshapeShape[1]; c++) { + let cData = []; + for (let row = 0; row < reshapeShape[2]; row++) { + let rowData = []; + for (let col = 0; col < reshapeShape[3]; col++) { + let tempN = n * reshapeShape[1] * reshapeShape[2] * reshapeShape[3]; + let tempC = c * reshapeShape[2] * reshapeShape[3]; + let tempRow = row * reshapeShape[3]; + rowData.push(tempData[tempN + tempC + tempRow + col]); + } + cData.push(rowData); + } + nData.push(cData); + } + formatData3.push(nData); + } + return formatData3; +}; + +export default class PostProcess { + constructor(options) { + this.modelConfig = models[options.modelName]; + this.count = 0; + this.lastRect = [0, 0, 0, 0] + } + + run(data, img, callback) { + let {from, to} = this.modelConfig.outputShapes; + let shape = [].concat(from).reverse(); + // 1.从一维数组到1*25*19*19 + let formatData = reshapeMany({ + data: data, + reshapeShape: shape + }); + // console.log('一维到多维', formatData); + // 2.从1*25*19*19 到 19*19*25*1 + let formatData2 = transpose({ + data: formatData, + shape: shape, + transposeShape: [2, 3, 1, 0] + }); + // console.log('transpose', formatData2); + // 3.从19*19*25*1到19*19*5*5 + let formatData3 = reshape({ + data: formatData2, + // shape: [19, 19, 25, 1], + // reshapeShape: [19, 19, 5, 5] + shape: from, + reshapeShape: to + }); + // console.log('reshape', formatData3); + // 4.运算 + let finalData = this.handleFinal(formatData3, shape, img); + // console.log('final', finalData); + // 5.处理画布 + // finalData.length && handleCanvas(finalData, img); + this.handleDiv(finalData, img, callback); + } + + calSize(img) { + let w1 = img.width; + let h1 = img.height; + let wh1 = Math.max(w1, h1); + let factor = this.modelConfig.feedShape.fw / wh1; + // let factor = 608.0 / wh1; + let width = Math.round(w1 * factor); + let height = Math.round(h1 * factor); + return [w1, h1, width, height]; + } + + // 处理运算 + handleFinal(formatData3, shape, img) { + let finalData = []; + let c = shape[2]; + let [w1, h1, width, height] = this.calSize(img); + let factorX = Math.max(width, height) / width; + let factorY = Math.max(width, height) / height; + + let maxProb = 0.0; + let anchors = [[1.603231, 2.094468], [6.041143, 7.080126], [2.882459, 3.518061], [4.266906, 5.178857], [9.041765, 10.66308]]; + + for (let i = 0; i < shape[2]; i++) { + for (let j = 0; j < shape[3]; j++) { + for (let k = 0; k < anchors.length; k++) { + let [a1, a2, a3, a4, prob] = formatData3[i][j][k]; + prob = sigmoid(prob); + if (prob > maxProb && prob >= 0.5) { + let ctx = (j + sigmoid(a1)) / c * factorX; + let cty = (i + sigmoid(a2)) / c * factorY; + let col = Math.exp(a3) * anchors[k][0] / c * factorX; + let row = Math.exp(a4) * anchors[k][1] / c * factorY; + let x = (ctx - (col / 2)); + let y = (cty - (row / 2)); + finalData.push([x * w1, y * h1, col * w1, row * h1, prob]); + } + + } + } + } + return finalData; + } + + handleDiv(finalData, img, callback) { + if (finalData.length < 1) { + callback(); + return false; + } + let maxIndex = 0; + if (finalData.length > 1) { + for (let i = 1; i < finalData.length; i++) { + if (finalData[i].prob > finalData[maxIndex].prob) { + maxIndex = i; + } + + } + } + + let [demoLeft, demoTop, demoWidth, demoHeight] = finalData[maxIndex]; + if (!isSimilar(this.lastRect, [demoLeft, demoTop, demoWidth, demoHeight])) { + callback([demoWidth, demoHeight,demoLeft, demoTop]); + }; + this.lastRect = [demoLeft, demoTop, demoWidth, demoHeight]; + } + + // 处理画布 + handleCanvas(finalData, img) { + let myCanvas = document.getElementById('myCanvas'); + let [w1, h1, width, height] = calSize(img); + myCanvas.width = w1; + myCanvas.height = h1; + let ctx = myCanvas.getContext('2d'); + // ctx.drawImage(img, 0, 0, w1, h1); + + // finalData.forEach((demoArr, index) => { + // let [demoLeft, demoTop, demoWidth, demoHeight, prob] = demoArr; + let [demoLeft, demoTop, demoWidth, demoHeight, prob] = finalData[0]; + ctx.beginPath(); + ctx.lineWidth = 4; + ctx.strokeStyle = 'red'; + ctx.moveTo(demoLeft, demoTop); + ctx.lineTo(demoLeft + demoWidth, demoTop); + ctx.lineTo(demoLeft + demoWidth, demoTop + demoHeight); + ctx.lineTo(demoLeft, demoTop + demoHeight); + ctx.closePath(); + ctx.stroke(); + // }); + } +} + diff --git a/web/src/executor/runner.es6 b/web/src/executor/runner.es6 new file mode 100644 index 0000000000..edb70b6f84 --- /dev/null +++ b/web/src/executor/runner.es6 @@ -0,0 +1,153 @@ +/** + * @file Runner 整个流程封装一下 + * @author hantian(hantianjiao@baidu.com) + * 使用方法: + * const runner = new Runner({ + * modelName: 'separate' // '608' | '320' | '320fused' | 'separate' + * }); + * runner.preheat().then(r => { + * r.run(document.getElementById('test')); + * }); + */ + +import IO from '../feed/ImageFeed'; +import DataFeed from '../feed/dataFeed'; +import Graph from './loader'; +import PostProcess from './postProcess'; +import models from '../utils/models'; +import Logger from '../../tools/logger'; +window.log = new Logger(); + +export default class Runner { + // 加载模型&预热 + constructor(options) { + this.modelConfig = models[options.modelName]; + this.flags = { + isRunning: false, + isPreheating: false, + runVideoPaused: false + }; + this.buffer = new Float32Array(); + this.io = new IO(); + this.postProcess = new PostProcess(options); + } + + // 预热 用用空数据跑一遍 + async preheat() { + this.flags.isPreheating = true; + let {fh, fw} = this.modelConfig.feedShape; + let path = this.modelConfig.modelPath; + let feed = [{ + data: new Float32Array(3 * fh * fw), + name: 'image', + shape: [1, 3, fh, fw] + }]; + const MODEL_URL = `/${path}/model.json`; + const MODEL_CONFIG = { + dir: `/${path}/`, // 存放模型的文件夹 + // dir: `https://graph.baidu.com/mms/graph/static/asset/dll/${path}/`, // rd测试地址 + // dir: `/src/view/common/lib/paddle/dist/${path}/`, // 本地测试地址 + main: 'model.json' // 主文件 + }; + const graphModel = new Graph(); + this.model = await graphModel.loadGraphModel(MODEL_CONFIG, { + multipart: true, + dataType: 'binary', + binaryOption: { + fileCount: 1, // 切成了多少文件 + getFileName(i) { // 获取第i个文件的名称 + return 'chunk_0.dat'; + } + }, + feed + }); + this.model.execute({ + input: feed + }); + this.flags.isPreheating = false; + return this; + } + + // 跑一遍 + async run(input, callback) { + this.flags.isRunning = true; + let {fh, fw} = this.modelConfig.feedShape; + let path = this.modelConfig.modelPath; + if (!this.model) { + console.warn('It\'s better to preheat the model before running.'); + await this.preheat(); + } + log.start('总耗时'); // eslint-disable-line + log.start('预处理'); // eslint-disable-line + let feed; + if (typeof input === 'string') { + const dfIO = new DataFeed(); + feed = await dfIO.process({ + input: `/${path}/${input}`, + shape: [1, 3, fh, fw] + }); + } + else { + feed = this.io.process({ + input: input, + params: { + gapFillWith: '#000', // 缩放后用什么填充不足方形部分 + targetSize: { + height: fw, + width: fh + }, + targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名 + // shape: [3, 608, 608], // 预设tensor形状 + mean: [117.001, 114.697, 97.404] // 预设期望 + // std: [0.229, 0.224, 0.225] // 预设方差 + } + }); + } + log.end('预处理'); // eslint-disable-line + log.start('运行耗时'); // eslint-disable-line + let inst = this.model.execute({ + input: feed + }); + let result = await inst.read(); + log.end('后处理-读取数据'); // eslint-disable-line + const newData = []; + let newIndex = -1; + const [w, h, c, b] = this.modelConfig.outputShapes.from; + // c channel + for (let i = 0; i < c; i++) { + // height channel + for (let j = 0; j < h; j++) { + // width channel + for (let k = 0; k < w; k++) { + // position: (0, 0, 0, 0) + const index = j * (c * h) + k * c + i; + // const index = j * (i * k) + k * i + i; + newData[++newIndex] = result[index]; + } + } + } + this.postProcess.run(newData, input, callback); + log.end('后处理'); // eslint-disable-line + this.flags.isRunning = false; + log.end('总耗时'); // eslint-disable-line + } + + // 传入获取图片的function + async runStream(getMedia, callback) { + await this.run(getMedia(), callback); + if (!this.flags.runVideoPaused) { + setTimeout(async () => { + await this.runStream(getMedia, callback); + }, 0); + } + } + + stopStream() { + this.flags.runVideoPaused = true; + } + + startStream(getMedia, callback) { + this.flags.runVideoPaused = false; + this.runStream(getMedia, callback); + } +} diff --git a/web/src/factory/fshader/factory.es6 b/web/src/factory/fshader/factory.es6 index 0b218fc458..4be6cfb0a0 100644 --- a/web/src/factory/fshader/factory.es6 +++ b/web/src/factory/fshader/factory.es6 @@ -6,6 +6,15 @@ import ops from './ops'; export default class Factory { constructor(opts) { this.defaultOpts = Object.assign({}, opts); + this.webglVersion = 2; + this.texture2d = 'texture'; + } + + setWebglVersion(vs = 0) { + this.webglVersion = vs; + if (vs === 1) { + this.texture2d = 'texture2D'; + } } buildShader(opName, data) { @@ -13,12 +22,16 @@ export default class Factory { result = this.buildPrefix(opName); result += this.buildCommon(opName); result += this.buildOp(opName); + data.texture2d = this.texture2d; result = this.populateData(result, data); return result; } buildPrefix(opName) { - return ops.common.prefix; + if (this.webglVersion === 1) { + return ops.common.prefix; + } + return ops.common.prefix2; } buildCommon(opName) { diff --git a/web/src/factory/fshader/ops.es6 b/web/src/factory/fshader/ops.es6 index dfdf6e6535..94c6fd220b 100644 --- a/web/src/factory/fshader/ops.es6 +++ b/web/src/factory/fshader/ops.es6 @@ -2,18 +2,28 @@ import common_params from '../../shader/atom/common_params'; import common_func from '../../shader/atom/common_func'; import prefix from '../../shader/atom/prefix'; +import prefix2 from '../../shader/atom/prefix2'; import suffix from '../../shader/atom/suffix'; import ivec56 from '../../shader/atom/type_ivec56'; import conv2d_params from '../../shader/conv2d/params'; import conv2d_func from '../../shader/conv2d/main'; import conv2d_conf from '../../shader/conv2d/conf'; +import conv2d_depthwise_params from '../../shader/conv2d_depthwise/params'; +import conv2d_depthwise_func from '../../shader/conv2d_depthwise/main'; +import conv2d_depthwise_conf from '../../shader/conv2d_depthwise/conf'; import dynamic_params from '../../shader/dynamic/params'; import dynamic_func from '../../shader/dynamic/main'; import dynamic_conf from '../../shader/dynamic/conf'; import pool2d_params from '../../shader/pool2d/params'; import pool2d_func from '../../shader/pool2d/main'; import pool2d_conf from '../../shader/pool2d/conf'; +import pool2d_max_params from '../../shader/pool2d_max/params'; +import pool2d_max_func from '../../shader/pool2d_max/main'; +import pool2d_max_conf from '../../shader/pool2d_max/conf'; +import pool2d_winograd_params from '../../shader/pool2d_winograd/params'; +import pool2d_winograd_func from '../../shader/pool2d_winograd/main'; +import pool2d_winograd_conf from '../../shader/pool2d_winograd/conf'; import elementwise_add_params from '../../shader/elementwise_add/params'; import elementwise_add_func from '../../shader/elementwise_add/main'; import elementwise_add_conf from '../../shader/elementwise_add/conf'; @@ -27,12 +37,21 @@ import batchnorm_params from '../../shader/batchnorm/params'; import batchnorm_func from '../../shader/batchnorm/main'; import batchnorm_conf from '../../shader/batchnorm/conf'; +import conv2d_elementwise_add_params from '../../shader/conv2d_elementwise_add/params'; +import conv2d_elementwise_add_func from '../../shader/conv2d_elementwise_add/main'; +import conv2d_elementwise_add_conf from '../../shader/conv2d_elementwise_add/conf'; + +import conv2d_elementwise_add_winograd_params from '../../shader/conv2d_elementwise_add_winograd/params'; +import conv2d_elementwise_add_winograd_func from '../../shader/conv2d_elementwise_add_winograd/main'; +import conv2d_elementwise_add_winograd_conf from '../../shader/conv2d_elementwise_add_winograd/conf'; + import getArrayIndexFromTensorPos from '../../shader/atom/getArrayIndexFromTensorPos'; import getArrayIndexFromTexturePos from '../../shader/atom/getArrayIndexFromTexturePos'; import getTensorPosFromArrayIndex from '../../shader/atom/getTensorPosFromArrayIndex'; import getTexturePosFromArrayIndex from '../../shader/atom/getTexturePosFromArrayIndex'; import getValueFromTexturePos from '../../shader/atom/getValueFromTexturePos'; import getValueFromTensorPos from '../../shader/atom/getValueFromTensorPos'; +import getValueFromTensorPosPacked from '../../shader/atom/getValueFromTensorPosPacked'; import moveTexture2PosToReal from '../../shader/atom/moveTexture2PosToReal'; import getPixelsFromTexturePos from '../../shader/atom/getPixelsFromTexturePos'; import getRangePowSumFromArrayIndex from '../../shader/atom/getRangePowSumFromArrayIndex'; @@ -51,6 +70,7 @@ export default { params: common_params, func: common_func, prefix, + prefix2, suffix, ivec56 }, @@ -60,6 +80,21 @@ export default { func: conv2d_func, confs: conv2d_conf }, + conv2d_depthwise: { + params: conv2d_depthwise_params, + func: conv2d_depthwise_func, + confs: conv2d_depthwise_conf + }, + conv2d_elementwise_add: { + params: conv2d_elementwise_add_params, + func: conv2d_elementwise_add_func, + confs: conv2d_elementwise_add_conf + }, + conv2d_elementwise_add_winograd: { + params: conv2d_elementwise_add_winograd_params, + func: conv2d_elementwise_add_winograd_func, + confs: conv2d_elementwise_add_winograd_conf + }, dynamic: { params: dynamic_params, func: dynamic_func, @@ -70,6 +105,16 @@ export default { func: pool2d_func, confs: pool2d_conf }, + pool2d_max: { + params: pool2d_max_params, + func: pool2d_max_func, + confs: pool2d_max_conf + }, + pool2d_winograd: { + params: pool2d_winograd_params, + func: pool2d_winograd_func, + confs: pool2d_winograd_conf + }, elementwise_add: { params: elementwise_add_params, func: elementwise_add_func, @@ -108,6 +153,7 @@ export default { getTexturePosFromArrayIndex, getValueFromTexturePos, getValueFromTensorPos, + getValueFromTensorPosPacked, moveTexture2PosToReal, getPixelsFromTexturePos, getRangeSumFromArrayIndex, diff --git a/web/src/feed/ImageFeed.es6 b/web/src/feed/ImageFeed.es6 index 59c2bd66ab..fcd1820f41 100644 --- a/web/src/feed/ImageFeed.es6 +++ b/web/src/feed/ImageFeed.es6 @@ -30,7 +30,10 @@ export default class imageFeed { ...inputs.params }; let output = []; - + if (!this.result) { + const [b, c, h, w] = params.targetShape; + this.result = new Float32Array(h * w * 3); + } output = this.fromPixels(input, params); return output; }; @@ -74,21 +77,27 @@ export default class imageFeed { */ allReshapeToRGB(imageData, opt, scaleSize) { const {sw, sh} = scaleSize; - const {width, height} = opt; + const [b, c, h, w] = opt.targetShape; let data = imageData.data; let mean = opt.mean; let dataLength = data.length; - let result = new Float32Array(dataLength * 3 / 4); - let offsetR = 0; - let offsetG = dataLength / 4; - let offsetB = dataLength / 2; - for (let i = 0; i < data.length; i += 4) { - result[offsetR++] = (data[i] - mean[0]) / 256; - result[offsetG++] = (data[i + 1] - mean[1]) / 256; - result[offsetB++] = (data[i + 2] - mean[2]) / 256; - // result.push((data[i] - mean[0]) / 256); // red - // result.push((data[i + 1] - mean[1]) / 256); // green - // result.push((data[i + 2] - mean[2]) / 256); // blue + // let result = new Float32Array(dataLength * 3); + let result = this.result; + // let offsetR = 0; + // let offsetG = dataLength / 4; + // let offsetB = dataLength / 2; + let offset = 0; + let size = h * w; + // h w c + for (let i = 0; i < h; ++i) { + let iw = i * w; + for (let j = 0; j < w; ++j) { + let iwj = iw + j; + for (let k = 0; k < c; ++k) { + let a = iwj * 4 + k; + result[offset++] = (data[a] - mean[k]) / 256; + } + } } return result; }; @@ -156,8 +165,10 @@ export default class imageFeed { else { this.fromPixels2DContext.drawImage( image, 0, 0, sw, sh); + // currentPic = this.fromPixels2DContext.canvas.toDataURL(); } - document.getElementById('p-c').appendChild(this.fromPixels2DContext.canvas);// test only, demele me + // window.currentPic = this.fromPixels2DContext.canvas;// test only, demele me + // document.getElementById('p-c').appendChild(this.fromPixels2DContext.canvas);// test only, demele me return {sw: targetWidth, sh: targetHeight}; } diff --git a/web/src/feed/dataFeed.es6 b/web/src/feed/dataFeed.es6 new file mode 100644 index 0000000000..c582d9c853 --- /dev/null +++ b/web/src/feed/dataFeed.es6 @@ -0,0 +1,42 @@ +/** + * @file 直接数据输入 + * @author hantianjiao@baidu.com + */ + +export default class dataFeed { + toFloat32Array(data) { + for (let i = 0; i < data.length; i++) { + this.f32Arr[i] = data[i]; + } + } + + getLengthFromShape(shape) { + return shape.reduce((a, b) => a * b); + } + + loadData() { + return fetch(this.dataPath).then(res => res.json()); + } + + getOutput() { + return this.loadData().then(data => { + this.toFloat32Array(data); + return [{ + data: this.f32Arr, + shape: this.shape, + name: 'x' + }]; + }); + } + + async process(input) { + this.len = this.getLengthFromShape(input.shape); + if (!this.f32Arr || this.len > this.f32Arr.length) { + this.f32Arr = new Float32Array(this.len); + } + this.shape = input.shape; + this.dataPath = input.input; + let output = await this.getOutput(); + return output; + } +} \ No newline at end of file diff --git a/web/src/feed/io.es6 b/web/src/feed/io.es6 index 032f192687..644f66dfb6 100644 --- a/web/src/feed/io.es6 +++ b/web/src/feed/io.es6 @@ -3,6 +3,7 @@ * @file io,loader相关输入输出 * @author wangqun@baidu.com */ + export default class io { constructor() { this.fromPixels2DContext = document.createElement('canvas').getContext('2d'); diff --git a/web/src/gpu/gpu.es6 b/web/src/gpu/gpu.es6 index f2c1c4c5ea..e59b7aa114 100644 --- a/web/src/gpu/gpu.es6 +++ b/web/src/gpu/gpu.es6 @@ -1,33 +1,83 @@ /* eslint-disable */ import VSHADER from '../shader/v_shader'; +import VSHADER2 from '../shader/v_shader2'; /** * @file gpu运算 * @author yangmingming */ +const CONF = { + alpha: false, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false, + depth: false, + stencil: false, + failIfMajorPerformanceCaveat: true +}; +const MAX_WAIT = 100; export default class gpu { constructor(opts = {}) { + // 版本, 默认webgl version 2.0 + this.version = 2; this.opts = opts; opts.width_raw_canvas = Number(opts.width_raw_canvas) || 512; opts.height_raw_canvas = Number(opts.height_raw_canvas) || 512; - let canvas = opts.el ? opts.el : document.createElement('canvas'); - canvas.width = opts.width_raw_canvas; - canvas.height = opts.height_raw_canvas; - this.gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); - this.gl.viewport(0, 0, canvas.width, canvas.height); - // Attempt to activate the extension, returns null if unavailable - this.textureFloat = this.gl.getExtension('OES_texture_float'); - // this.setOutProps(); + const canvas = opts.el ? opts.el : document.createElement('canvas'); + canvas.addEventListener('webglcontextlost', evt => { + evt.preventDefault(); + console.log('webgl context is lost~'); + }, false); + let gl = canvas.getContext('webgl2', CONF); + if (!!gl) { + // 开启float32 + this.version = 2; + this.textureFloat = gl.getExtension('EXT_color_buffer_float'); + this.internalFormat = gl.R32F; + this.textureFormat = gl.RED; + this.downloadInternalFormat = gl.RGBA32F; + } else { + gl = canvas.getContext('webgl', CONF) || canvas.getContext('experimental-webgl', CONF); + this.version = 1; + this.internalFormat = gl.RGBA; + this.textureFormat = gl.RGBA; + this.downloadInternalFormat = gl.RGBA; + if (!gl) { + this.version = 0; + alert('当前环境创建webgl context失败'); + } else { + // 开启扩展 + this.textureFloat = gl.getExtension('OES_texture_float'); + console.log('float extension is started or not? ' + !!this.textureFloat); + } + } + // 关闭相关功能 + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.STENCIL_TEST); + gl.disable(gl.BLEND); + gl.disable(gl.DITHER); + gl.disable(gl.POLYGON_OFFSET_FILL); + gl.disable(gl.SAMPLE_COVERAGE); + gl.enable(gl.SCISSOR_TEST); + gl.enable(gl.CULL_FACE); + gl.cullFace(gl.BACK); + this.gl = gl; this.initCache(); - console.log('float extension is started or not? ' + !!this.textureFloat); - console.log('WebGl版本是 ' + this.gl.getParameter(this.gl.SHADING_LANGUAGE_VERSION)); + // 同步查看次数 + this.waits = 0; + + console.log('WebGl版本是 ' + this.version); + console.log('MAX_TEXTURE_SIZE is ' + gl.getParameter(gl.MAX_TEXTURE_SIZE)); + console.log('MAX_TEXTURE_IMAGE_UNITS is ' + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)); + } + + getWebglVersion() { + return this.version; } initCache() { // 运行次数 this.times = 0; const gl = this.gl; - // 缓存每个op的texture - // this.textures = []; // 顶点数据 let vertices = new Float32Array([ -1.0, 1.0, 0.0, 1.0, @@ -40,7 +90,7 @@ export default class gpu { // shader this.vertexShader = null; // 生成vertextShader - this.initShader(VSHADER); + this.initShader(this.version === 2 ? VSHADER2 : VSHADER); this.fragmentShader = null; // 上一个texture this.prevTexture = null; @@ -49,27 +99,18 @@ export default class gpu { // 帧缓存 this.frameBuffer = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - // 计算texture cache, 最多3个 - this.cacheTextures = [gl.createTexture(), gl.createTexture(), gl.createTexture()]; + // 计算texture cache + this.cacheTextures = {}; + this.uniformLocations = {}; // texture buffer - this.textureBuffer = [gl.createTexture(), gl.createTexture()]; - // program - this.programs = [gl.createProgram(), gl.createProgram()]; - this.program = this.programs[0]; - this.textureBufferIndex = 0; - for (let i = 0; i < 2; i++) { - gl.bindTexture(gl.TEXTURE_2D, this.textureBuffer[i]); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.bindTexture(gl.TEXTURE_2D, null); - } + this.outTextures = []; + // pbo + this.pbo = gl.createBuffer(); } - runVertexShader() { + runVertexShader(program) { const gl = this.gl; - let aPosition = gl.getAttribLocation(this.program, 'position'); + let aPosition = gl.getAttribLocation(program, 'position'); // Turn on the position attribute gl.enableVertexAttribArray(aPosition); // Bind the position buffer. @@ -82,6 +123,7 @@ export default class gpu { this.height_shape_out = opts.height_shape || 1; this.width_texture_out = opts.width_texture || 1; this.height_texture_out = opts.height_texture || 1; + this.channel = opts.channel || 0; this.total_shape = opts.total_shape || 0; } @@ -89,27 +131,63 @@ export default class gpu { return (this.textureFloat !== null); } - attachShader(fshader) { + createProgram(fshader, out) { const gl = this.gl; - let index = this.textureBufferIndex % 2; - const program = this.programs[index]; + const program = gl.createProgram(); + gl.attachShader(program, this.vertexShader); + gl.attachShader(program, fshader); + gl.linkProgram(program); + // 生成output的texture缓存 + const texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + gl.texImage2D(gl.TEXTURE_2D, // Target, matches bind above. + 0, // Level of detail. + this.downloadInternalFormat, // Internal format. + out.width_texture, + out.height_texture, + 0, // Always 0 in OpenGL ES. + gl.RGBA, // Format for each pixel. + gl.FLOAT, // Data type for each chanel. + null); + gl.bindTexture(gl.TEXTURE_2D, null); + this.outTextures.push(texture); + return program; + } + + setProgram(program, isRendered) { + const gl = this.gl; + gl.useProgram(program); this.program = program; - if (this.times < 2) { - gl.attachShader(program, this.vertexShader); + if (!isRendered) { + this.runVertexShader(program); } + } + + attachShader(fshader) { + const gl = this.gl; + // let index = this.textureBufferIndex % 2; + // const program = this.programs[index]; + // this.program = program; + const program = this.program; + // if (this.times < 2) { + // gl.attachShader(program, this.vertexShader); + // } this.textureBufferIndex = (this.textureBufferIndex + 1) >= 2 ? 0 : 1; + if (!!this.fragmentShader) { + gl.detachShader(program, this.fragmentShader); + } this.gl.attachShader(program, fshader); + this.fragmentShader = fshader; gl.linkProgram(program); - gl.useProgram(program); - if (this.times++ < 2) { + if (this.times++ === 0) { + gl.useProgram(program); this.runVertexShader(); } - if (!!this.fragmentShader) { - const cache = this.programs[(index + 1) % 2]; - gl.detachShader(cache, this.fragmentShader); - // gl.deleteShader(this.fragmentShader); - } - this.fragmentShader = fshader; } create(vshaderCode, fshaderCode) { @@ -185,10 +263,11 @@ export default class gpu { * @param {WebGLTexture} texture 材质 * @returns {WebGLFramebuffer} The framebuffer */ - attachFrameBuffer(opts = {}) { + attachFrameBuffer(iLayer) { this.prevTexture = this.currentTexture; - this.currentTexture = this.textureBuffer[this.textureBufferIndex % 2]; + // this.currentTexture = this.textureBuffer[this.textureBufferIndex % 2]; // this.textureBufferIndex = (this.textureBufferIndex + 1) >= 2 ? 0 : 1; + this.currentTexture = this.outTextures[iLayer]; const gl = this.gl; gl.framebufferTexture2D(gl.FRAMEBUFFER, // The target is always a FRAMEBUFFER. gl.COLOR_ATTACHMENT0, // We are providing the color buffer. @@ -199,8 +278,14 @@ export default class gpu { gl.viewport( 0, 0, - opts.width_texture_out || this.width_texture_out, - opts.height_texture_out || this.height_texture_out + this.width_texture_out, + this.height_texture_out + ); + gl.scissor( + 0, + 0, + this.width_texture_out, + this.height_texture_out ); return this.frameBuffer; } @@ -243,66 +328,53 @@ export default class gpu { return {isComplete: value, message: message}; } - /** - * 更新材质 - * @param {WebGLTexture} texture 材质对象 - * @param {number} type 材质类型. FLOAT, UNSIGNED_BYTE, etc. - * @param {Float32Array[]} data 材质数据 - */ - refreshTexture(texture, type, data) { - const gl = this.gl; - // Bind the texture so the following methods effect it. - gl.bindTexture(gl.TEXTURE_2D, texture); - - // Replace the texture data - gl.texSubImage2D(gl.TEXTURE_2D, // Target, matches bind above. - 0, // Level of detail. - 0, // xOffset - 0, // yOffset - this.opts.width_raw_canvas, // Width - normalized to s. - this.opts.height_raw_canvas, // Height - normalized to t. - gl.RGBA, // Format for each pixel. - type, // Data type for each chanel. - data); // Image data in the described format. - - // Unbind the texture. - gl.bindTexture(gl.TEXTURE_2D, null); - - return texture; - } - /** * 初始化材质 * @param {int} index 材质索引 * @param {string} tSampler 材质名称 * @param {Object} bufferData 数据 + * @param {boolean} isRendered 是否已运行过 */ - initTexture(index, item) { + initTexture(index, item, iLayer, isRendered) { const gl = this.gl; let texture; if (!item.data) { texture = this.prevTexture; } else { // texture = gl.createTexture(); - texture = this.cacheTextures[index]; - // this.textures.push(texture); + if (isRendered && (iLayer > 0 || (iLayer === 0 && item.tensor !== 'origin'))) { + const tData = this.cacheTextures['' + iLayer]; + texture = tData[item.variable + '_' + item.tensor]; + } else { + texture = gl.createTexture(); + if (index === 0) { + this.cacheTextures['' + iLayer] = this.cacheTextures['' + iLayer] || {}; + } + this.cacheTextures['' + iLayer][item.variable + '_' + item.tensor] = texture; + } } gl.activeTexture(gl[`TEXTURE${index}`]); gl.bindTexture(gl.TEXTURE_2D, texture); - if (item.data) { + if (item.data && (!isRendered || (isRendered && iLayer === 0 && item.tensor === 'origin'))) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, item.width_texture || this.opts.width_raw_canvas, - item.height_texture || this.opts.height_raw_canvas, 0, - gl.RGBA, gl.FLOAT, item.data, 0); + gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, item.width_texture, + item.height_texture, 0, + this.textureFormat, gl.FLOAT, item.data, 0); } } - getUniformLoc(name) { + getUniformLoc(name, ilayer, isRendered) { + if (isRendered) { + return this.uniformLocations['' + ilayer][name]; + } let loc = this.gl.getUniformLocation(this.program, name); if (loc === null) throw `getUniformLoc ${name} err`; + // 缓存 + this.uniformLocations['' + ilayer] = this.uniformLocations['' + ilayer] || {}; + this.uniformLocations['' + ilayer][name] = loc; return loc; } @@ -330,16 +402,16 @@ export default class gpu { return texture; } - render(data = []) { + render(data = [], iLayer = 0, isRendered = false) { const gl = this.gl; let textureIndex = 0; - // 输入数据 data.forEach(item => { if (item.type === 'texture') { - this.initTexture(textureIndex, item); - gl.uniform1i(this.getUniformLoc(item.variable + '_' + item.tensor), textureIndex++); - } else if (item.type === 'uniform') { - gl[item.setter](this.getUniformLoc(item.variable + '_' + item.tensor), item.data); + this.initTexture(textureIndex, item, iLayer, isRendered); + gl.uniform1i(this.getUniformLoc(item.variable + '_' + item.tensor, iLayer, isRendered), textureIndex++); + } + else if (item.type === 'uniform') { + gl[item.setter](this.getUniformLoc(item.variable + '_' + item.tensor, iLayer, isRendered), item.data); } }); // gl.clearColor(.0, .0, .0, 1); @@ -347,25 +419,127 @@ export default class gpu { gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); } + createPBO() { + const gl2 = this.gl; + const buffer = this.pbo; + gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, buffer); + const sizeBytes = 4 * 4 * this.width_texture_out * this.height_texture_out; + gl2.bufferData(gl2.PIXEL_PACK_BUFFER, sizeBytes, gl2.STREAM_READ); + gl2.readPixels(0, 0, this.width_texture_out, this.height_texture_out, gl2.RGBA, gl2.FLOAT, 0); + gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, null); + return buffer; + } + + downloadFoat32TensorFromBuffer(buffer) { + const gl2 = this.gl; + const size = 4 * this.width_texture_out * this.height_texture_out; + const pixels = new Float32Array(size); + gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, buffer); + gl2.getBufferSubData(gl2.PIXEL_PACK_BUFFER, 0, pixels); + gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, null); + log.start('后处理-readloop'); + // let result = []; + // let offset = 0; + // for (let h = 0; h < this.height_texture_out; h++) { + // // 纪录第1和2行数据 + // let temp1 = []; + // let temp2 = []; + // for (let w = 0; w < this.width_texture_out; w++) { + // temp1.push(pixels[offset]); + // temp1.push(pixels[offset + 1]); + // temp2.push(pixels[offset + 2]); + // temp2.push(pixels[offset + 3]); + // offset += 4; + // } + // result = result.concat(temp1); + // result = result.concat(temp2); + // } + let result = []; + for (let i = 0; i < this.width_texture_out * this.height_texture_out; i++) { + result.push(pixels[4 * i]); + } + // const result = Array.prototype.slice.call(pixels); + // console.dir(['result', result]); + log.end('后处理-readloop'); + return result; + } + + getWebglError(status) { + const gl2 = this.gl; + switch (status) { + case gl2.NO_ERROR: + return 'NO_ERROR'; + case gl2.INVALID_ENUM: + return 'INVALID_ENUM'; + case gl2.INVALID_VALUE: + return 'INVALID_VALUE'; + case gl2.INVALID_OPERATION: + return 'INVALID_OPERATION'; + case gl2.INVALID_FRAMEBUFFER_OPERATION: + return 'INVALID_FRAMEBUFFER_OPERATION'; + case gl2.OUT_OF_MEMORY: + return 'OUT_OF_MEMORY'; + case gl2.CONTEXT_LOST_WEBGL: + return 'CONTEXT_LOST_WEBGL'; + default: + return `Unknown error code ${status}`; + } + } + + createAndWaitForFence() { + const gl2 = this.gl; + const isFenceEnabled = (gl2.fenceSync !== null); + let isFencePassed = () => true; + if (isFenceEnabled) { + const sync = gl2.fenceSync(gl2.SYNC_GPU_COMMANDS_COMPLETE, 0); + gl2.flush(); + isFencePassed = () => { + const status = gl2.clientWaitSync(sync, 0, 0); + return status === gl2.ALREADY_SIGNALED || + status === gl2.CONDITION_SATISFIED; + }; + } + return new Promise(resolve => { + this.pollItem(isFencePassed, resolve); + }); + } + + pollItem(isDone, resolveFn) { + const fn = () => { + if (isDone()) { + resolveFn(); + return; + } + setTimeout(fn, 1); + }; + fn(); + } + compute() { let gl = this.gl; + log.start('后处理-readinside'); + const tt = +Date.now(); let pixels = new Float32Array(this.width_texture_out * this.height_texture_out * 4); // gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); + const tt2 = +Date.now(); gl.readPixels(0, 0, this.width_texture_out, this.height_texture_out, gl.RGBA, gl.FLOAT, pixels, 0); - + // console.log('本次读取数据时间是' + (+Date.now() - tt2)+ ',' + (tt2 - tt)); + log.end('后处理-readinside'); + log.start('后处理-readloop'); let result = []; for (let i = 0; i < this.width_texture_out * this.height_texture_out; i++) { result.push(pixels[4 * i]); } + log.end('后处理-readloop'); return result; } dispose() { const gl = this.gl; - this.cacheTextures.forEach(texture => { - gl.deleteTexture(texture); - }); - this.cacheTextures = []; + // this.cacheTextures.forEach(texture => { + // gl.deleteTexture(texture); + // }); + this.cacheTextures = {}; this.programs.forEach(program => { gl.detachShader(program, this.vertexShader); gl.deleteShader(this.vertexShader); diff --git a/web/src/ops/dummy.js b/web/src/ops/dummy.js deleted file mode 100644 index 08686bd2ab..0000000000 --- a/web/src/ops/dummy.js +++ /dev/null @@ -1,12 +0,0 @@ - -module.exports.create = function (args, scope, gl) { - const output = scope[args.outputs.Out[0]] - return { - inferShape() { - output.dim = output.dim.forEach(d => d === -1 ? 1 : d) - }, - compute() { - console.log(output) - } - } -} diff --git a/web/src/ops/feed.js b/web/src/ops/feed.js deleted file mode 100644 index 7e132c5403..0000000000 --- a/web/src/ops/feed.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports.create = function (args, scope, gl) { - const output = scope[args.outputs.Out[0]] - const input = scope[args.inputs.X[0]] - return { - inferShape() { - output.dim = output.dim.map(d => d === -1 ? 1 : d) - }, - compute() { - console.log(input) - console.log(output) - } - } -} diff --git a/web/src/ops/fetch.js b/web/src/ops/fetch.js deleted file mode 100644 index ab7d4c3086..0000000000 --- a/web/src/ops/fetch.js +++ /dev/null @@ -1,12 +0,0 @@ - -module.exports.create = function (args, scope, gl) { - // const output = scope[args.outputs.Out[0]] - return { - inferShape() { - // output.dim = output.dim.forEach(d => d === -1 ? 1 : d) - }, - compute() { - console.log(`${args.type} is going to be implemented`) - } - } -} diff --git a/web/src/package.json b/web/src/package.json deleted file mode 100644 index 49f0c29c77..0000000000 --- a/web/src/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "paddel-web", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC" -} diff --git a/web/src/runtime/runtime.es6 b/web/src/runtime/runtime.es6 index f95a6a29fd..9969fd8f08 100644 --- a/web/src/runtime/runtime.es6 +++ b/web/src/runtime/runtime.es6 @@ -1,5 +1,6 @@ /* eslint-disable */ import Gpu from '../gpu/gpu'; +import getMaxUniforms from '../test/getMaxUniforms'; /** * @file gpu运行时 * @author yangmingming @@ -20,10 +21,15 @@ export default { } }, - run(opName, opData) { - let time = +Date.now(); - let start = time; - let timeObj = {}; + getWebglVersion() { + return this.gpu.getWebglVersion(); + }, + + run(opName, opData, isRendered) { + // console.dir(['fscode', opData.fsCode]); + // let time = +Date.now(); + // let start = time; + // let timeObj = {}; if (!opData.isPass) { console.log('跳过当前op:' + opName); return this; @@ -32,19 +38,20 @@ export default { const gpu = this.gpu; gpu.setOutProps(opData.tensor['out']); // 生成帧缓存材质 - gpu.makeTexure(WebGLRenderingContext.FLOAT, null); - let end = +Date.now(); + gpu.attachFrameBuffer(opData.iLayer); + // let end = +Date.now(); let bufferStatus = gpu.frameBufferIsComplete(); if (bufferStatus.isComplete) { - start = +Date.now(); - timeObj['buferstatus-time'] = start - end; - gpu.attachShader(opData.fshader); - end = +Date.now(); - timeObj['createshader-time'] = end - start; - timeObj['jsTime'] = end - time; - statistic.push(timeObj); + // start = +Date.now(); + // timeObj['buferstatus-time'] = start - end; + // gpu.attachShader(opData.fshader); + gpu.setProgram(opData.program, isRendered); + // end = +Date.now(); + // timeObj['createshader-time'] = end - start; + // timeObj['jsTime'] = end - time; + // statistic.push(timeObj); // 开始计算 - this.gpu.render(opData.renderData); + this.gpu.render(opData.renderData, opData.iLayer, isRendered); return this; } else { return bufferStatus.message; @@ -54,12 +61,34 @@ export default { /** * 读取op计算结果, 并返回数据 */ - read() { - return this.gpu.compute(); + read2() { + let bufferStatus = this.gpu.frameBufferIsComplete(); + if (bufferStatus.isComplete) { + + return this.gpu.compute(); + } + return null; + }, + + async read() { + const pbo = this.gpu.createPBO(); + await this.gpu.createAndWaitForFence(); + log.end('运行耗时'); + log.start('后处理'); + // 其实这里应该有个fetch的执行调用或者fetch的输出 + log.start('后处理-读取数据'); + // 开始读数据 + return this.gpu.downloadFoat32TensorFromBuffer(pbo); }, - createFragmentShader(fsCode) { - return this.gpu.initShader(fsCode, 'fragment'); + createProgram(fsCode, outTensor) { + const fshader = this.gpu.initShader(fsCode, 'fragment'); + const program = this.gpu.createProgram(fshader, outTensor); + // test uniforms的个数 + // const maxUniforms = getMaxUniforms(this.gpu.gl, program); + // alert(maxUniforms.maxFragmentShader); + // console.table(maxUniforms.uniforms); + return program; }, // 释放资源 diff --git a/web/src/shader/atom/common_params.es6 b/web/src/shader/atom/common_params.es6 index a99019f223..f4462ddced 100644 --- a/web/src/shader/atom/common_params.es6 +++ b/web/src/shader/atom/common_params.es6 @@ -4,25 +4,6 @@ * @author yangmingming */ export default ` - // varying变量 - // 顶点shader透传的材质坐标 - varying vec2 vCoord; - // 扩展shader的ivec类型 - // struct ivec5 { - // int x; - // int y; - // int z; - // int w; - // int u; - // }; - // struct ivec6 { - // int x; - // int y; - // int z; - // int w; - // int u; - // int v; - // }; // dynamic的input数据 const float multi_value = float(MULTI_VALUE); const float bias_value = float(BIAS_VALUE); @@ -33,4 +14,5 @@ export default ` const int width_texture_out = WIDTH_TEXTURE_OUT; const int height_texture_out = HEIGHT_TEXTURE_OUT; const int channel_out = CHANNEL_OUT; + const int offset_y_out = OFFSET_Y_OUT; `; diff --git a/web/src/shader/atom/getArrayIndexFromTensorPos.es6 b/web/src/shader/atom/getArrayIndexFromTensorPos.es6 index b0d17e4f3b..3b28fb839d 100644 --- a/web/src/shader/atom/getArrayIndexFromTensorPos.es6 +++ b/web/src/shader/atom/getArrayIndexFromTensorPos.es6 @@ -4,11 +4,6 @@ * @author yangmingming */ export default ` -// TENSOR_TYPE, tensor坐标的类型,ivec4 -// TENSOR_NAME, tensor name - -// 获取tensor坐标对应数组中的索引 -// uniform int numbers_shape_TENSOR_NAME[LENGTH_SHAPE_TENSOR_NAME]; int getArrayIndexFromTensorPos_TENSOR_NAME(TENSOR_TYPE tensorPos) { int index = 0; diff --git a/web/src/shader/atom/getArrayIndexFromTexturePos.es6 b/web/src/shader/atom/getArrayIndexFromTexturePos.es6 index 1cff8a703e..c48576d5e2 100644 --- a/web/src/shader/atom/getArrayIndexFromTexturePos.es6 +++ b/web/src/shader/atom/getArrayIndexFromTexturePos.es6 @@ -3,11 +3,7 @@ * @file 公共方法 * @author yangmingming */ -// TEXTURE_NAME, texture name -// WIDTH_TEXTURE_NAME_VALUE, texture的宽度 -// 获取材质元素在数组中的索引 -// const int width_TEXTURE_NAME = WIDTH_TEXTURE_NAME_VALUE; export default ` int getArrayIndexFromTexturePos_TEXTURE_NAME(vec3 pos) { int x = int(floor(pos.x)); diff --git a/web/src/shader/atom/getPixelsFromTexturePos.es6 b/web/src/shader/atom/getPixelsFromTexturePos.es6 index 052bc93759..683b85e9be 100644 --- a/web/src/shader/atom/getPixelsFromTexturePos.es6 +++ b/web/src/shader/atom/getPixelsFromTexturePos.es6 @@ -3,9 +3,7 @@ * @file 公共方法 * @author yangmingming */ -// TEXTURE_NAME, tensor name // 获取材质中的像素 -// uniform sampler2D TEXTURE_NAME; export default ` -#define getPixelsFromTexturePos_TEXTURE_NAME(pos) texture2D(TEXTURE_NAME, pos) +#define getPixelsFromTexturePos_TEXTURE_NAME(pos) TEXTURE2D(TEXTURE_NAME, pos) `; diff --git a/web/src/shader/atom/getValueFromTensorPos.es6 b/web/src/shader/atom/getValueFromTensorPos.es6 index 5a52397053..b07d07fa4d 100644 --- a/web/src/shader/atom/getValueFromTensorPos.es6 +++ b/web/src/shader/atom/getValueFromTensorPos.es6 @@ -5,15 +5,28 @@ */ export default ` float getValueFromTensorPos_TENSOR_NAME(int r, int g, int b, int a) { - vec4 pixels = texture2D(texture_TENSOR_NAME, vec2((float(a * channel_TENSOR_NAME + g) + 0.5) / float(width_texture_TENSOR_NAME), (float(r * height_shape_TENSOR_NAME + b) + 0.5) / float(height_texture_TENSOR_NAME))); + vec4 pixels = TEXTURE2D(texture_TENSOR_NAME, + vec2( + (float(a * channel_TENSOR_NAME + g) + 0.5) / float(width_texture_TENSOR_NAME), + (float(r * height_shape_TENSOR_NAME + b) + 0.5) / float(height_texture_TENSOR_NAME) + ) + ); return pixels.r; } -float getValueFromTensorPos_TENSOR_NAME(ivec4 pos) { - float offset = 0.5; - float width = float(pos.a * channel_TENSOR_NAME + pos.g) + offset; - float height = float(pos.r * height_shape_TENSOR_NAME + pos.b) + offset; - vec4 pixels = texture2D(texture_TENSOR_NAME, vec2(width / float(width_texture_TENSOR_NAME), height / float(height_texture_TENSOR_NAME))); +float getValueFromTensorPosLimit_TENSOR_NAME(int r, int g, int b, int a) { + float halfW = ceil(float(width_shape_TENSOR_NAME) / 2.0); + int x = int(mod(float(a), halfW)); + int offsetY = 0; + if (a > x) { + offsetY = height_shape_TENSOR_NAME; + } + vec4 pixels = TEXTURE2D(texture_TENSOR_NAME, + vec2( + (float(x * channel_TENSOR_NAME + g) + 0.5) / float(width_texture_TENSOR_NAME), + (float(r * 2 * height_shape_TENSOR_NAME + b + offsetY) + 0.5) / float(height_texture_TENSOR_NAME) + ) + ); return pixels.r; } `; diff --git a/web/src/shader/atom/getValueFromTensorPosPacked.es6 b/web/src/shader/atom/getValueFromTensorPosPacked.es6 new file mode 100644 index 0000000000..5b64a7aaed --- /dev/null +++ b/web/src/shader/atom/getValueFromTensorPosPacked.es6 @@ -0,0 +1,26 @@ +/* eslint-disable */ +/** + * @file 公共方法 + * @author yangmingming + */ +export default ` +float getValueFromTensorPosPacked_TENSOR_NAME(int r, int g, int b, int a) { + int y = b / 2; + int yOffset = int(mod(float(b), 2.0)); + int x = a / 2; + int xOffset = int(mod(float(a), 2.0)); + int height = height_shape_TENSOR_NAME + offset_y_TENSOR_NAME; + vec4 pixels = TEXTURE2D(texture_TENSOR_NAME, vec2((float(x) + 0.5) / float(width_texture_TENSOR_NAME), (float(g * height / 2 + y) + 0.5) / float(height_texture_TENSOR_NAME))); + int index = 0; + if (xOffset == 0 && yOffset == 0) { + return pixels[0]; + } + else if (xOffset == 1 && yOffset == 0) { + return pixels[1]; + } + else if (xOffset == 0 && yOffset == 1) { + return pixels[2]; + } + return pixels[3]; +} +`; diff --git a/web/src/shader/atom/getValueFromTexturePos.es6 b/web/src/shader/atom/getValueFromTexturePos.es6 index f68f3f4a38..f810a04bd8 100644 --- a/web/src/shader/atom/getValueFromTexturePos.es6 +++ b/web/src/shader/atom/getValueFromTexturePos.es6 @@ -8,7 +8,7 @@ // uniform sampler2D TEXTURE_NAME; export default ` float getValueFromTexturePos_TEXTURE_NAME(vec3 pos) { - vec4 pixels = texture2D(TEXTURE_NAME, pos.xy); + vec4 pixels = TEXTURE2D(TEXTURE_NAME, pos.xy); int d = int(pos.z); if (d == 0) { return pixels.r; diff --git a/web/src/shader/atom/prefix.es6 b/web/src/shader/atom/prefix.es6 index 54a1742088..0d028a6bba 100644 --- a/web/src/shader/atom/prefix.es6 +++ b/web/src/shader/atom/prefix.es6 @@ -11,4 +11,8 @@ export default ` precision mediump float; precision mediump int; #endif + + void setOutput(float result) { + gl_FragColor.r = result; + } `; diff --git a/web/src/shader/atom/prefix2.es6 b/web/src/shader/atom/prefix2.es6 new file mode 100644 index 0000000000..e17ad09fa5 --- /dev/null +++ b/web/src/shader/atom/prefix2.es6 @@ -0,0 +1,22 @@ +/* eslint-disable */ +/** + * @file 预设条件, webgl 2.0版本 + * @author yangmingming + */ +export default `#version 300 es + +#ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp float; + precision highp int; +#else + precision mediump float; + precision mediump int; +#endif + +// 顶点shader透传的材质坐标 + in vec2 vCoord; + out vec4 outColor; + void setOutput(float result) { + outColor.r = result; + } +`; diff --git a/web/src/shader/atom/suffix.es6 b/web/src/shader/atom/suffix.es6 index 97eb819ffd..840ef29de4 100644 --- a/web/src/shader/atom/suffix.es6 +++ b/web/src/shader/atom/suffix.es6 @@ -14,4 +14,29 @@ ivec4 getOutputTensorPos() { int b = int(outCoord.y / float(height_shape_out)); return ivec4(b, c, y, x); } + +ivec4 getOutputTensorPosLimit() { + // 获取原始长度 + vec2 outCoord = vCoord.xy * _2d_shape_texture_out; + float offsetY = floor(outCoord.y / float(height_shape_out)); + int x = int(outCoord.x / float(channel_out)); + if (mod(offsetY, 2.0) > 0.0) { + x += int(ceil(float(width_shape_out) / 2.0)); + } + int y = int(mod(outCoord.y, float(height_shape_out))); + int c = int(mod(outCoord.x, float(channel_out))); + int b = int(outCoord.y / float(2 * height_shape_out)); + return ivec4(b, c, y, x); +} + +ivec4 getOutputPackedTensorPos() { + // 获取原始长度 + vec2 outCoord = vCoord.xy * _2d_shape_texture_out; + int height = height_shape_out + offset_y_out; + int x = int(outCoord.x); + int c = int(outCoord.y / float(height / 2)); + int y = int(mod(outCoord.y, float(height / 2))); + int b = 0; + return ivec4(b, c, y, x); +} `; diff --git a/web/src/shader/batchnorm/conf.es6 b/web/src/shader/batchnorm/conf.es6 index 7cdfd95e63..b64fd4a80b 100644 --- a/web/src/shader/batchnorm/conf.es6 +++ b/web/src/shader/batchnorm/conf.es6 @@ -32,6 +32,7 @@ export default { 'WIDTH_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT', 'CHANNEL_OUT', + 'OFFSET_Y_OUT', 'EPSILON', 'WIDTH_TEXTURE_SCALE', diff --git a/web/src/shader/batchnorm/main.es6 b/web/src/shader/batchnorm/main.es6 index ae702250ff..e964a02841 100644 --- a/web/src/shader/batchnorm/main.es6 +++ b/web/src/shader/batchnorm/main.es6 @@ -13,6 +13,6 @@ void main(void) { vec4 scale = getPixelsFromTexturePos_texture_scale(vec2((float(int(oPos.g)) + 0.5) / float(width_texture_scale), 0.0)); float x = (o - scale[3]) / sqrt(scale[2] + epsilon); float res = scale[0] * x + scale[1]; - gl_FragColor.r = res; + setOutput(res); } `; diff --git a/web/src/shader/conv2d/conf.es6 b/web/src/shader/conv2d/conf.es6 index c66c40a9d3..bcd2e5313a 100644 --- a/web/src/shader/conv2d/conf.es6 +++ b/web/src/shader/conv2d/conf.es6 @@ -38,6 +38,7 @@ export default { 'WIDTH_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT', 'CHANNEL_OUT', + 'OFFSET_Y_OUT', 'STRIDE_HORIZONTAL', 'STRIDE_VERTICAL', diff --git a/web/src/shader/conv2d/main.es6 b/web/src/shader/conv2d/main.es6 index e6513899af..ffa8e5fedc 100644 --- a/web/src/shader/conv2d/main.es6 +++ b/web/src/shader/conv2d/main.es6 @@ -6,7 +6,7 @@ export default ` // start函数 void main(void) { - ivec4 oPos = getOutputTensorPos(); + ivec4 oPos = getOutputTensorPosLIMIT_OUT(); int x = oPos.a; int c = oPos.g; int y = oPos.b; @@ -35,14 +35,14 @@ export default ` } // channel计算 for (int j = 0; j < channel_filter; j++) { - float f = getValueFromTensorPos_filter(c, j, fy, fx); - float o = getValueFromTensorPos_origin(b, oTensorChannel + j, oy, ox); + float f = getValueFromTensorPosLIMIT_FILTER_filter(c, j, fy, fx); + float o = getValueFromTensorPosLIMIT_ORIGIN_origin(b, oTensorChannel + j, oy, ox); res += f * o; } ox += dilation_h; } oy += dilation_v; } - gl_FragColor.r = res; + setOutput(res); } `; diff --git a/web/src/shader/conv2d_depthwise/conf.es6 b/web/src/shader/conv2d_depthwise/conf.es6 new file mode 100644 index 0000000000..467320904b --- /dev/null +++ b/web/src/shader/conv2d_depthwise/conf.es6 @@ -0,0 +1,67 @@ +/* eslint-disable */ +/** + * @file conv2d的配置文件 + * @author yangmingming + */ +export default { + dep: [ + { + func: 'getValueFromTensorPos', + conf: { + TENSOR_NAME: 'origin' + } + }, + { + func: 'getValueFromTensorPos', + conf: { + TENSOR_NAME: 'filter' + } + } + ], + conf: [ + 'LENGTH_SHAPE_FILTER', + 'WIDTH_SHAPE_FILTER', + 'HEIGHT_SHAPE_FILTER', + 'WIDTH_TEXTURE_FILTER', + 'HEIGHT_TEXTURE_FILTER', + 'CHANNEL_FILTER', + + 'WIDTH_SHAPE_ORIGIN', + 'HEIGHT_SHAPE_ORIGIN', + 'LENGTH_SHAPE_ORIGIN', + 'WIDTH_TEXTURE_ORIGIN', + 'HEIGHT_TEXTURE_ORIGIN', + 'CHANNEL_ORIGIN', + + 'WIDTH_SHAPE_OUT', + 'HEIGHT_SHAPE_OUT', + 'WIDTH_TEXTURE_OUT', + 'HEIGHT_TEXTURE_OUT', + 'CHANNEL_OUT', + 'OFFSET_Y_OUT', + + 'STRIDE_HORIZONTAL', + 'STRIDE_VERTICAL', + 'PAD_LEFT', + 'PAD_TOP', + 'DILATION_HORIZONTAL', + 'DILATION_VERTICAL', + 'MULTI_VALUE', + 'BIAS_VALUE', + 'ACTIVE_FUNCTION' + ], + input: [ + { + tensor: 'filter', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + }, + { + tensor: 'origin', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + } + ] +}; diff --git a/web/src/shader/conv2d_depthwise/main.es6 b/web/src/shader/conv2d_depthwise/main.es6 new file mode 100644 index 0000000000..870607c648 --- /dev/null +++ b/web/src/shader/conv2d_depthwise/main.es6 @@ -0,0 +1,42 @@ +/* eslint-disable */ +/** + * @file 主函数 + * @author yangmingming + */ +export default ` + // start函数 + void main(void) { + ivec4 oPos = getOutputTensorPosLIMIT_OUT(); + int x = oPos.a; + int c = oPos.g; + int y = oPos.b; + int b = oPos.r; + float res = 0.0; + + int top = y * stride_v - padTop; + int left = x * stride_h - padLeft; + for (int fy = 0; fy < height_shape_filter; fy++) { + int oy = top + fy * dilation_v; + if (oy >= height_shape_origin) { + break; + } + if (oy < 0) { + continue; + } + for (int fx = 0; fx < width_shape_filter; fx++) { + int ox = left + fx * dilation_h; + if (ox >= width_shape_origin) { + break; + } + if (ox < 0) { + continue; + } + // b默认是0 + float f = getValueFromTensorPosLIMIT_FILTER_filter(c, 0, fy, fx); + float o = getValueFromTensorPosLIMIT_ORIGIN_origin(b, c, oy, ox); + res += f * o; + } + } + setOutput(res); + } +`; diff --git a/web/src/shader/conv2d_depthwise/params.es6 b/web/src/shader/conv2d_depthwise/params.es6 new file mode 100644 index 0000000000..ae408cceeb --- /dev/null +++ b/web/src/shader/conv2d_depthwise/params.es6 @@ -0,0 +1,43 @@ +/* eslint-disable */ +/** + * @file 参数文件 + * @author yangmingming + */ +export default ` + // conv2d的input数据 + + // 常量 + // 卷积核 + const int length_shape_filter = LENGTH_SHAPE_FILTER; + const int width_shape_filter = WIDTH_SHAPE_FILTER; + const int height_shape_filter = HEIGHT_SHAPE_FILTER; + const int width_texture_filter = WIDTH_TEXTURE_FILTER; + const int height_texture_filter = HEIGHT_TEXTURE_FILTER; + const int channel_filter = CHANNEL_FILTER; + + // 输入数据 + const int width_shape_origin = WIDTH_SHAPE_ORIGIN; + const int height_shape_origin = HEIGHT_SHAPE_ORIGIN; + const int length_shape_origin = LENGTH_SHAPE_ORIGIN; + const int width_texture_origin = WIDTH_TEXTURE_ORIGIN; + const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN; + const int channel_origin = CHANNEL_ORIGIN; + + // 计算相关 + // 拆分步长 + const int stride_h = STRIDES_X; + const int stride_v = STRIDES_Y; + // padding的数目 + const int padLeft = PADDINGS_X; + const int padTop = PADDINGS_Y; + // dilation膨胀系数 + const int dilation_h = DILATIONS_X; + const int dilation_v = DILATIONS_Y; + + // uniform变量 + // 卷积核 + uniform sampler2D texture_filter; + + // 输入数据 + uniform sampler2D texture_origin; +`; diff --git a/web/src/shader/conv2d_elementwise_add/conf.es6 b/web/src/shader/conv2d_elementwise_add/conf.es6 new file mode 100644 index 0000000000..7d3a58e294 --- /dev/null +++ b/web/src/shader/conv2d_elementwise_add/conf.es6 @@ -0,0 +1,77 @@ +/* eslint-disable */ +/** + * @file conv2d-elementwise_add的配置文件 + * @author yangmingming + */ +export default { + dep: [ + { + func: 'getValueFromTensorPos', + conf: { + TENSOR_NAME: 'origin' + } + }, + { + func: 'getValueFromTensorPos', + conf: { + TENSOR_NAME: 'filter' + } + } + ], + conf: [ + 'LENGTH_SHAPE_FILTER', + 'WIDTH_SHAPE_FILTER', + 'HEIGHT_SHAPE_FILTER', + 'WIDTH_TEXTURE_FILTER', + 'HEIGHT_TEXTURE_FILTER', + 'CHANNEL_FILTER', + + 'WIDTH_SHAPE_ORIGIN', + 'HEIGHT_SHAPE_ORIGIN', + 'LENGTH_SHAPE_ORIGIN', + 'WIDTH_TEXTURE_ORIGIN', + 'HEIGHT_TEXTURE_ORIGIN', + 'CHANNEL_ORIGIN', + + 'WIDTH_SHAPE_OUT', + 'HEIGHT_SHAPE_OUT', + 'WIDTH_TEXTURE_OUT', + 'HEIGHT_TEXTURE_OUT', + 'CHANNEL_OUT', + 'OFFSET_Y_OUT', + + 'WIDTH_SHAPE_COUNTER', + + 'STRIDE_HORIZONTAL', + 'STRIDE_VERTICAL', + 'PAD_LEFT', + 'PAD_TOP', + 'DILATION_HORIZONTAL', + 'DILATION_VERTICAL', + 'GROUPS', + 'AXIS', + 'MULTI_VALUE', + 'BIAS_VALUE', + 'ACTIVE_FUNCTION' + ], + input: [ + { + tensor: 'filter', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + }, + { + tensor: 'counter', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + }, + { + tensor: 'origin', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + } + ] +}; diff --git a/web/src/shader/conv2d_elementwise_add/main.es6 b/web/src/shader/conv2d_elementwise_add/main.es6 new file mode 100644 index 0000000000..90607a350f --- /dev/null +++ b/web/src/shader/conv2d_elementwise_add/main.es6 @@ -0,0 +1,53 @@ +/* eslint-disable */ +/** + * @file 主函数 + * @author yangmingming + */ +export default ` + // start函数 + void main(void) { + ivec4 oPos = getOutputTensorPosLIMIT_OUT(); + int x = oPos.a; + int c = oPos.g; + int y = oPos.b; + int b = oPos.r; + int addAxis = oPos[axis]; + float res = getValueFromCounter(addAxis); + + // 获取output的坐标 + int oTensorChannel = (c / (channel_out / groups)) * channel_filter; + int oy = y * stride_v - padTop; + for (int fy = 0; fy < height_shape_filter; fy++) { + if (oy >= height_shape_origin) { + break; + } + if (oy < 0) { + oy += dilation_v; + continue; + } + int ox = x * stride_h - padLeft; + for (int fx = 0; fx < width_shape_filter; fx++) { + if (ox >= width_shape_origin) { + break; + } + if (ox < 0) { + ox += dilation_h; + continue; + } + // channel计算 + for (int j = 0; j < channel_filter; j++) { + float f = getValueFromTensorPosLIMIT_FILTER_filter(c, j, fy, fx); + float o = getValueFromTensorPosLIMIT_ORIGIN_origin(b, oTensorChannel + j, oy, ox); + res += f * o; + } + ox += dilation_h; + } + oy += dilation_v; + } + setOutput(ACTIVE_FUNCTION(res, multi_value, bias_value)); + // outColor.r = float(b); + // outColor.g = float(c); + // outColor.b = float(y); + // outColor.a = float(x); + } +`; diff --git a/web/src/shader/conv2d_elementwise_add/params.es6 b/web/src/shader/conv2d_elementwise_add/params.es6 new file mode 100644 index 0000000000..e4a4f6aed6 --- /dev/null +++ b/web/src/shader/conv2d_elementwise_add/params.es6 @@ -0,0 +1,54 @@ +/* eslint-disable */ +/** + * @file 参数文件 + * @author yangmingming + */ +export default ` + // 卷积核 + const int length_shape_filter = LENGTH_SHAPE_FILTER; + const int width_shape_filter = WIDTH_SHAPE_FILTER; + const int height_shape_filter = HEIGHT_SHAPE_FILTER; + const int width_texture_filter = WIDTH_TEXTURE_FILTER; + const int height_texture_filter = HEIGHT_TEXTURE_FILTER; + const int channel_filter = CHANNEL_FILTER; + + // 输入数据 + const int width_shape_origin = WIDTH_SHAPE_ORIGIN; + const int height_shape_origin = HEIGHT_SHAPE_ORIGIN; + const int length_shape_origin = LENGTH_SHAPE_ORIGIN; + const int width_texture_origin = WIDTH_TEXTURE_ORIGIN; + const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN; + const int channel_origin = CHANNEL_ORIGIN; + + // 计算相关 + // 拆分步长 + const int stride_h = STRIDES_X; + const int stride_v = STRIDES_Y; + // padding的数目 + const int padLeft = PADDINGS_X; + const int padTop = PADDINGS_Y; + // dilation膨胀系数 + const int dilation_h = DILATIONS_X; + const int dilation_v = DILATIONS_Y; + // groups + const int groups = GROUPS; + + // 加法 + const int axis = AXIS; + + // uniform变量 + // 卷积核 + uniform sampler2D texture_filter; + + // 输入数据 + uniform sampler2D texture_origin; + + // 加法 + uniform sampler2D texture_counter; + // 加法用到的函数 + float getValueFromCounter(int index) { + float xPos = float(index) / float(WIDTH_SHAPE_COUNTER); + vec4 pixels = TEXTURE2D(texture_counter, vec2(xPos, 0.5)); + return pixels.r; + } +`; diff --git a/web/src/shader/conv2d_elementwise_add_winograd/conf.es6 b/web/src/shader/conv2d_elementwise_add_winograd/conf.es6 new file mode 100644 index 0000000000..61bd86fdee --- /dev/null +++ b/web/src/shader/conv2d_elementwise_add_winograd/conf.es6 @@ -0,0 +1,72 @@ +/* eslint-disable */ +/** + * @file conv2d-elementwise_add的配置文件 + * @author yangmingming + */ +export default { + dep: [ + { + func: 'getValueFromTensorPos', + conf: { + TENSOR_NAME: 'origin' + } + }, + { + func: 'getValueFromTensorPos', + conf: { + TENSOR_NAME: 'filter' + } + } + ], + conf: [ + 'LENGTH_SHAPE_FILTER', + 'WIDTH_SHAPE_FILTER', + 'HEIGHT_SHAPE_FILTER', + 'WIDTH_TEXTURE_FILTER', + 'HEIGHT_TEXTURE_FILTER', + 'CHANNEL_FILTER', + + 'WIDTH_SHAPE_ORIGIN', + 'HEIGHT_SHAPE_ORIGIN', + 'LENGTH_SHAPE_ORIGIN', + 'WIDTH_TEXTURE_ORIGIN', + 'HEIGHT_TEXTURE_ORIGIN', + 'CHANNEL_ORIGIN', + + 'WIDTH_SHAPE_OUT', + 'HEIGHT_SHAPE_OUT', + 'WIDTH_TEXTURE_OUT', + 'HEIGHT_TEXTURE_OUT', + 'CHANNEL_OUT', + 'OFFSET_Y_OUT', + + 'TOTAL_SHAPE_COUNTER', + + 'PAD_LEFT', + 'PAD_TOP', + 'AXIS', + 'MULTI_VALUE', + 'BIAS_VALUE', + 'ACTIVE_FUNCTION' + ], + input: [ + { + tensor: 'filter', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + }, + { + tensor: 'origin', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + }, + { + tensor: 'counter', + variable: 'data', + setter: 'uniform1fv', + type: 'uniform' + } + ] +}; diff --git a/web/src/shader/conv2d_elementwise_add_winograd/main.es6 b/web/src/shader/conv2d_elementwise_add_winograd/main.es6 new file mode 100644 index 0000000000..bf8da439aa --- /dev/null +++ b/web/src/shader/conv2d_elementwise_add_winograd/main.es6 @@ -0,0 +1,98 @@ +/* eslint-disable */ +/** + * @file 主函数 + * @author yangmingming + */ +export default ` + // start函数 + void main(void) { + ivec4 oPos = getOutputPackedTensorPos(); + int x = oPos.a; + int c = oPos.g; + int y = oPos.b; + int b = oPos.r; + // b = 0; + // c = 1; + // y = 0; + // x = 0; + int addAxis = oPos[axis]; + float res = getValueFromCounter(addAxis); + // 输出结果 + vec4 v4 = vec4(res); + + float I[16]; + float B[16]; + float T[16]; + float f[16]; + for (int cl = 0; cl < channel_filter; cl++) { + // 获取output的坐标 + int oy = 2*y - padTop; + // 计算输入 4 * 4矩阵 和filter + for (int fy = 0; fy < 4; fy++) { + int ox = 2*x - padLeft; + int index = fy * 4; + for (int fx = 0; fx < 4; fx++) { + if (oy < 0 || oy >= height_shape_origin || ox >= width_shape_origin || ox < 0) { + I[index + fx] = 0.0; + } else { + I[index + fx] = getValueFromTensorPos_origin(b, cl, oy, ox); + } + f[index + fx] = getValueFromTensorPos_filter(c, cl, fy, fx); + ox += 1; + } + oy += 1; + } + // input转化 + float tmp1 = I[2] - I[10]; + float tmp2 = I[9] - I[1]; + B[0] = I[0] - I[8] - tmp1; + B[1] = tmp1 - tmp2; + B[2] = tmp1 + tmp2; + B[3] = I[3] - I[11] + tmp2; + tmp1 = I[6] + I[10]; + tmp2 = I[5] + I[9]; + B[4] = I[4] + I[8] - tmp1; + B[5] = tmp1 + tmp2; + B[6] = tmp1 - tmp2; + B[7] = I[7] + I[11] - tmp2; + tmp1 = I[10] - I[6]; + tmp2 = I[5] - I[9]; + B[8] = I[8] - I[4] - tmp1; + B[9] = tmp1 - tmp2; + B[10] = tmp1 + tmp2; + B[11] = tmp2 - I[7] + I[11]; + tmp1 = I[14] - I[6]; + tmp2 = I[5] - I[13]; + B[12] = I[12] - I[4] - tmp1; + B[13] = tmp1 - tmp2; + B[14] = tmp1 + tmp2; + B[15] = tmp2 - I[7] + I[15]; + // 点乘 + for (int i = 0; i < 16; i++) { + T[i] = B[i] * f[i]; + } + // final output + tmp1 = T[1] + T[5] + T[9]; + tmp2 = T[2] + T[6] + T[10]; + v4[0] += T[0] + T[4] + T[8] + tmp1 + tmp2; + v4[1] += T[3] + T[7] + T[11] + tmp1 - tmp2; + tmp1 = T[5] - T[9] + T[13]; + tmp2 = T[6] - T[10] + T[14]; + v4[2] += T[4] - T[8] + T[12] + tmp1 + tmp2; + v4[3] += T[7] - T[11] + T[15] + tmp1 - tmp2; + } + outColor.r = ACTIVE_FUNCTION(v4[0], multi_value, bias_value); + outColor.g = ACTIVE_FUNCTION(v4[1], multi_value, bias_value); + outColor.b = ACTIVE_FUNCTION(v4[2], multi_value, bias_value); + outColor.a = ACTIVE_FUNCTION(v4[3], multi_value, bias_value); + // outColor = v4; + // outColor.r = I[0]; + // outColor.g = I[1]; + // outColor.b = I[2]; + // outColor.a = I[3]; + // outColor.r = float(b); + // outColor.g = float(c); + // outColor.b = float(y); + // outColor.a = float(x); + } +`; diff --git a/web/src/shader/conv2d_elementwise_add_winograd/params.es6 b/web/src/shader/conv2d_elementwise_add_winograd/params.es6 new file mode 100644 index 0000000000..288ad211ac --- /dev/null +++ b/web/src/shader/conv2d_elementwise_add_winograd/params.es6 @@ -0,0 +1,47 @@ +/* eslint-disable */ +/** + * @file 参数文件 + * @author yangmingming + */ +export default ` + // 卷积核 + const int length_shape_filter = LENGTH_SHAPE_FILTER; + const int width_shape_filter = WIDTH_SHAPE_FILTER; + const int height_shape_filter = HEIGHT_SHAPE_FILTER; + const int width_texture_filter = WIDTH_TEXTURE_FILTER; + const int height_texture_filter = HEIGHT_TEXTURE_FILTER; + const int channel_filter = CHANNEL_FILTER; + + // 输入数据 + const int width_shape_origin = WIDTH_SHAPE_ORIGIN; + const int height_shape_origin = HEIGHT_SHAPE_ORIGIN; + const int length_shape_origin = LENGTH_SHAPE_ORIGIN; + const int width_texture_origin = WIDTH_TEXTURE_ORIGIN; + const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN; + const int channel_origin = CHANNEL_ORIGIN; + + // 计算相关 + // padding的数目 + const int padLeft = PADDINGS_X; + const int padTop = PADDINGS_Y; + + // 加法 + const int axis = AXIS; + uniform float data_counter[TOTAL_SHAPE_COUNTER]; + + // uniform变量 + // 卷积核 + uniform sampler2D texture_filter; + + // 输入数据 + uniform sampler2D texture_origin; + // 加法用到的函数 + float getValueFromCounter(int index) { + for (int i = 0; i < TOTAL_SHAPE_COUNTER; i++) { + if (i == index) { + return data_counter[i]; + } + } + return 0.0; + } +`; diff --git a/web/src/shader/dynamic/conf.es6 b/web/src/shader/dynamic/conf.es6 index 2541ecf755..eddd1418ca 100644 --- a/web/src/shader/dynamic/conf.es6 +++ b/web/src/shader/dynamic/conf.es6 @@ -18,6 +18,7 @@ export default { 'WIDTH_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT', 'CHANNEL_OUT', + 'OFFSET_Y_OUT', 'MULTI_VALUE', 'BIAS_VALUE', diff --git a/web/src/shader/dynamic/main.es6 b/web/src/shader/dynamic/main.es6 index f747a477d7..54c068ff9b 100644 --- a/web/src/shader/dynamic/main.es6 +++ b/web/src/shader/dynamic/main.es6 @@ -9,6 +9,6 @@ void main(void) { // 输出数据 float o = getPixelsFromTexturePos_texture_origin(vCoord).r; float res = ACTIVE_FUNCTION(o, multi_value, bias_value); - gl_FragColor.r = res; + setOutput(res); } `; diff --git a/web/src/shader/elementwise_add/conf.es6 b/web/src/shader/elementwise_add/conf.es6 index a1cef63cad..bfd3fb24c2 100644 --- a/web/src/shader/elementwise_add/conf.es6 +++ b/web/src/shader/elementwise_add/conf.es6 @@ -33,6 +33,7 @@ export default { 'WIDTH_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT', 'CHANNEL_OUT', + 'OFFSET_Y_OUT', 'AXIS', 'MULTI_VALUE', diff --git a/web/src/shader/elementwise_add/main.es6 b/web/src/shader/elementwise_add/main.es6 index 5e9b1bd125..2ec3c47cb0 100644 --- a/web/src/shader/elementwise_add/main.es6 +++ b/web/src/shader/elementwise_add/main.es6 @@ -7,11 +7,11 @@ export default ` // start函数 void main(void) { // 输出数据 - ivec4 oPos = getOutputTensorPos(); + ivec4 oPos = getOutputTensorPosLIMIT_OUT(); int index = oPos[axis]; float o = getPixelsFromTexturePos_texture_origin(vCoord).r; float c = getValueFromCounter(index); float res = ACTIVE_FUNCTION(o + c, multi_value, bias_value); - gl_FragColor.r = res; + setOutput(res); } `; diff --git a/web/src/shader/mul/conf.es6 b/web/src/shader/mul/conf.es6 index 25b6575757..1d02279049 100644 --- a/web/src/shader/mul/conf.es6 +++ b/web/src/shader/mul/conf.es6 @@ -37,7 +37,8 @@ export default { 'HEIGHT_SHAPE_OUT', 'WIDTH_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT', - 'CHANNEL_OUT' + 'CHANNEL_OUT', + 'OFFSET_Y_OUT' ], input: [ { diff --git a/web/src/shader/mul/main.es6 b/web/src/shader/mul/main.es6 index 4628b96ec3..bdec1041a9 100644 --- a/web/src/shader/mul/main.es6 +++ b/web/src/shader/mul/main.es6 @@ -13,6 +13,6 @@ void main(void) { float o = getValueFromTensorPos_origin(out_pos[0], out_pos[1], out_pos[2], j); res += c * o; } - gl_FragColor.r = res; + setOutput(res); } `; diff --git a/web/src/shader/pool2d/conf.es6 b/web/src/shader/pool2d/conf.es6 index 9d2ca4d0db..ebb6070400 100644 --- a/web/src/shader/pool2d/conf.es6 +++ b/web/src/shader/pool2d/conf.es6 @@ -29,6 +29,7 @@ export default { 'WIDTH_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT', 'CHANNEL_OUT', + 'OFFSET_Y_OUT', 'STRIDES_X', 'STRIDES_Y', diff --git a/web/src/shader/pool2d/main.es6 b/web/src/shader/pool2d/main.es6 index df295618a4..113de0b586 100644 --- a/web/src/shader/pool2d/main.es6 +++ b/web/src/shader/pool2d/main.es6 @@ -7,7 +7,7 @@ export default ` void main(void) { float res = (-1.0 / exp(-20.0)); // 获取output的坐标 - ivec4 out_pos = getOutputTensorPos(); + ivec4 out_pos = getOutputTensorPosLIMIT_OUT(); // X、Y方向的移动步长 int count_pool = 0; int oy_base = out_pos[2] * stride_v - padTop; @@ -29,7 +29,7 @@ void main(void) { continue; } // origin数据 - float curr = getValueFromTensorPos_origin(out_pos[0], out_pos[1], oy, ox); + float curr = getValueFromTensorPosLIMIT_ORIGIN_origin(out_pos[0], out_pos[1], oy, ox); if (type_pool == 1) { if (curr > res) { res = curr; @@ -44,6 +44,6 @@ void main(void) { if (type_pool != 1) { res = res / float(count_pool); } - gl_FragColor.r = res; + setOutput(res); } `; diff --git a/web/src/shader/pool2d_avg/conf.es6 b/web/src/shader/pool2d_avg/conf.es6 new file mode 100644 index 0000000000..fe45ac6450 --- /dev/null +++ b/web/src/shader/pool2d_avg/conf.es6 @@ -0,0 +1,47 @@ +/* eslint-disable */ +/** + * @file pool2d_avg的配置文件 + * @author yangmingming zhangmiao06 + */ +export default { + dep: [ + { + func: 'getValueFromTensorPos', + conf: { + TENSOR_NAME: 'origin' + } + } + ], + conf: [ + 'KSIZE_X', + 'KSIZE_Y', + + 'WIDTH_SHAPE_ORIGIN', + 'HEIGHT_SHAPE_ORIGIN', + 'LENGTH_SHAPE_ORIGIN', + 'WIDTH_TEXTURE_ORIGIN', + 'HEIGHT_TEXTURE_ORIGIN', + 'CHANNEL_ORIGIN', + + 'WIDTH_SHAPE_OUT', + 'HEIGHT_SHAPE_OUT', + 'WIDTH_TEXTURE_OUT', + 'HEIGHT_TEXTURE_OUT', + 'CHANNEL_OUT', + 'OFFSET_Y_OUT', + + 'STRIDES_X', + 'STRIDES_Y', + 'PADDING_X', + 'PADDING_Y' + ], + input: [ + // texture类型,若添加from: 'prev', 表示读取上一个op的产出 + { + tensor: 'origin', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + } + ] +}; diff --git a/web/src/shader/pool2d_avg/main.es6 b/web/src/shader/pool2d_avg/main.es6 new file mode 100644 index 0000000000..b9f157e402 --- /dev/null +++ b/web/src/shader/pool2d_avg/main.es6 @@ -0,0 +1,40 @@ +/* eslint-disable */ +/** + * @file pool2d_avg主函数 + * @author yangmingming zhangmiao06 + */ +export default ` +// start函数 +void main(void) { + float res = 0.0; + // 获取output的坐标 + ivec4 out_pos = getOutputTensorPos(); + // X、Y方向的移动步长 + int oy_base = out_pos[2] * stride_v - padTop; + int ox_base = out_pos[3] * stride_h - padLeft; + for (int fy = 0; fy < height_shape_pool; fy++) { + int oy = oy_base + fy; + if (oy >= height_shape_origin) { + break; + } + if (oy < 0) { + continue; + } + for (int fx = 0; fx < width_shape_pool; fx++) { + int ox = ox_base + fx; + if (ox >= width_shape_origin) { + break; + } + if (ox < 0) { + continue; + } + // origin数据 + float curr = getValueFromTensorPos_origin(out_pos[0], out_pos[1], oy, ox); + res += curr; + // 在平均池化模式忽略填充值(exclusive默认为true) + } + } + res = res / float(height_shape_pool * width_shape_pool); + setOutput(res); +} +`; diff --git a/web/src/shader/pool2d_avg/params.es6 b/web/src/shader/pool2d_avg/params.es6 new file mode 100644 index 0000000000..17b55948e0 --- /dev/null +++ b/web/src/shader/pool2d_avg/params.es6 @@ -0,0 +1,30 @@ +/* eslint-disable */ +/** + * @file pool2d_avg参数文件 + * @author yangmingming zhangmiao06 + */ +export default ` +// 常量 +// 池化大小 +const int width_shape_pool = KSIZE_X; +const int height_shape_pool = KSIZE_Y; +// 输入数据 +const int width_shape_origin = WIDTH_SHAPE_ORIGIN; +const int height_shape_origin = HEIGHT_SHAPE_ORIGIN; +const int length_shape_origin = LENGTH_SHAPE_ORIGIN; +const int width_texture_origin = WIDTH_TEXTURE_ORIGIN; +const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN; +const int channel_origin = CHANNEL_ORIGIN; + +// 计算相关 +// 拆分步长 +const int stride_h = STRIDES_X; +const int stride_v = STRIDES_Y; +// padding的数目 +const int padLeft = PADDINGS_X; +const int padTop = PADDINGS_Y; + + +// uniform变量 +uniform sampler2D texture_origin; +`; diff --git a/web/src/shader/pool2d_max/conf.es6 b/web/src/shader/pool2d_max/conf.es6 new file mode 100644 index 0000000000..722e7df28a --- /dev/null +++ b/web/src/shader/pool2d_max/conf.es6 @@ -0,0 +1,47 @@ +/* eslint-disable */ +/** + * @file pool2d的配置文件 + * @author yangmingming zhangmiao06 + */ +export default { + dep: [ + { + func: 'getValueFromTensorPos', + conf: { + TENSOR_NAME: 'origin' + } + } + ], + conf: [ + 'KSIZE_X', + 'KSIZE_Y', + + 'WIDTH_SHAPE_ORIGIN', + 'HEIGHT_SHAPE_ORIGIN', + 'LENGTH_SHAPE_ORIGIN', + 'WIDTH_TEXTURE_ORIGIN', + 'HEIGHT_TEXTURE_ORIGIN', + 'CHANNEL_ORIGIN', + + 'WIDTH_SHAPE_OUT', + 'HEIGHT_SHAPE_OUT', + 'WIDTH_TEXTURE_OUT', + 'HEIGHT_TEXTURE_OUT', + 'CHANNEL_OUT', + 'OFFSET_Y_OUT', + + 'STRIDES_X', + 'STRIDES_Y', + 'PADDING_X', + 'PADDING_Y' + ], + input: [ + // texture类型,若添加from: 'prev', 表示读取上一个op的产出 + { + tensor: 'origin', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + } + ] +}; diff --git a/web/src/shader/pool2d_max/main.es6 b/web/src/shader/pool2d_max/main.es6 new file mode 100644 index 0000000000..2555efc95d --- /dev/null +++ b/web/src/shader/pool2d_max/main.es6 @@ -0,0 +1,45 @@ +/* eslint-disable */ +/** + * @file pool2d主函数 + */ +export default ` +// start函数 +void main(void) { + float res = (-1.0 / exp(-20.0)); + // 获取output的坐标 + ivec4 out_pos = getOutputTensorPosLIMIT_OUT(); + int b = out_pos[0]; + int c = out_pos[1]; + int y = out_pos[2]; + int x = out_pos[3]; + // X、Y方向的移动步长 + int oy_base = out_pos[2] * stride_v - padTop; + int ox_base = out_pos[3] * stride_h - padLeft; + for (int fy = 0; fy < height_shape_pool; fy++) { + int oy = oy_base + fy; + if (oy >= height_shape_origin) { + break; + } + if (oy < 0) { + continue; + } + for (int fx = 0; fx < width_shape_pool; fx++) { + int ox = ox_base + fx; + if (ox >= width_shape_origin) { + break; + } + if (ox < 0) { + continue; + } + // origin数据 + float curr = getValueFromTensorPosLIMIT_ORIGIN_origin(out_pos[0], out_pos[1], oy, ox); + res = max(res, curr); + } + } + setOutput(res); + // outColor.r = float(b); + // outColor.g = float(c); + // outColor.b = float(y); + // outColor.a = float(x); +} +`; diff --git a/web/src/shader/pool2d_max/params.es6 b/web/src/shader/pool2d_max/params.es6 new file mode 100644 index 0000000000..1ddd6befc8 --- /dev/null +++ b/web/src/shader/pool2d_max/params.es6 @@ -0,0 +1,29 @@ +/* eslint-disable */ +/** + * @file pool2d参数文件 + */ +export default ` +// 常量 +// 池化大小 +const int width_shape_pool = KSIZE_X; +const int height_shape_pool = KSIZE_Y; +// 输入数据 +const int width_shape_origin = WIDTH_SHAPE_ORIGIN; +const int height_shape_origin = HEIGHT_SHAPE_ORIGIN; +const int length_shape_origin = LENGTH_SHAPE_ORIGIN; +const int width_texture_origin = WIDTH_TEXTURE_ORIGIN; +const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN; +const int channel_origin = CHANNEL_ORIGIN; + +// 计算相关 +// 拆分步长 +const int stride_h = STRIDES_X; +const int stride_v = STRIDES_Y; +// padding的数目 +const int padLeft = PADDINGS_X; +const int padTop = PADDINGS_Y; + + +// uniform变量 +uniform sampler2D texture_origin; +`; diff --git a/web/src/shader/pool2d_winograd/conf.es6 b/web/src/shader/pool2d_winograd/conf.es6 new file mode 100644 index 0000000000..3c3925a3b4 --- /dev/null +++ b/web/src/shader/pool2d_winograd/conf.es6 @@ -0,0 +1,50 @@ +/* eslint-disable */ +/** + * @file pool2d的配置文件 + * @author yangmingming zhangmiao06 + */ +export default { + dep: [ + { + func: 'getValueFromTensorPosPacked', + conf: { + TENSOR_NAME: 'origin' + } + } + ], + conf: [ + 'KSIZE_X', + 'KSIZE_Y', + 'TYPE_POOL', + + 'WIDTH_SHAPE_ORIGIN', + 'HEIGHT_SHAPE_ORIGIN', + 'LENGTH_SHAPE_ORIGIN', + 'WIDTH_TEXTURE_ORIGIN', + 'HEIGHT_TEXTURE_ORIGIN', + 'CHANNEL_ORIGIN', + 'OFFSET_X_ORIGIN', + 'OFFSET_Y_ORIGIN', + + 'WIDTH_SHAPE_OUT', + 'HEIGHT_SHAPE_OUT', + 'WIDTH_TEXTURE_OUT', + 'HEIGHT_TEXTURE_OUT', + 'CHANNEL_OUT', + 'OFFSET_Y_OUT', + + 'STRIDES_X', + 'STRIDES_Y', + 'PADDING_X', + 'PADDING_Y' + ], + input: [ + // texture类型,若添加from: 'prev', 表示读取上一个op的产出 + { + tensor: 'origin', + variable: 'texture', + setter: 'initTexture', + type: 'texture' + } + ] +}; diff --git a/web/src/shader/pool2d_winograd/main.es6 b/web/src/shader/pool2d_winograd/main.es6 new file mode 100644 index 0000000000..0112654fdd --- /dev/null +++ b/web/src/shader/pool2d_winograd/main.es6 @@ -0,0 +1,63 @@ +/* eslint-disable */ +/** + * @file pool2d主函数 + */ +export default ` +// start函数 +void main(void) { + float res = (-1.0 / exp(-20.0)); + // 获取output的坐标 + ivec4 out_pos = getOutputTensorPos(); + // int b = out_pos[0]; + // int c = out_pos[1]; + // int y = out_pos[2]; + // int x = out_pos[3]; + // X、Y方向的移动步长 + int count_pool = 0; + int oy_base = out_pos[2] * stride_v - padTop; + int ox_base = out_pos[3] * stride_h - padLeft; + // int offset = 0; + // vec4 v4 = texture(texture_origin, vec2((float(0) + 0.5) / float(width_texture_origin), (float(1 * height_shape_origin / 2 + 0) + 0.5) / float(height_texture_origin))); + for (int fy = 0; fy < height_shape_pool; fy++) { + int oy = oy_base + fy; + if (oy >= height_shape_origin) { + break; + } + if (oy < 0) { + continue; + } + for (int fx = 0; fx < width_shape_pool; fx++) { + int ox = ox_base + fx; + if (ox >= width_shape_origin) { + break; + } + if (ox < 0) { + continue; + } + // origin数据 + float curr = getValueFromTensorPosPacked_origin(out_pos[0], out_pos[1], oy, ox); + // y = oy; + // x = ox; + // v4[offset++] = curr; + if (type_pool == 1) { + if (curr > res) { + res = curr; + } + } else { + res += curr; + // 在平均池化模式忽略填充值(exclusive默认为true) + count_pool++; + } + } + } + if (type_pool != 1) { + res = res / float(count_pool); + } + setOutput(res); + // outColor = v4; + // outColor.r = float(b); + // outColor.g = float(c); + // outColor.b = float(y); + // outColor.a = float(x); +} +`; diff --git a/web/src/shader/pool2d_winograd/params.es6 b/web/src/shader/pool2d_winograd/params.es6 new file mode 100644 index 0000000000..4310c0f7b2 --- /dev/null +++ b/web/src/shader/pool2d_winograd/params.es6 @@ -0,0 +1,33 @@ +/* eslint-disable */ +/** + * @file pool2d参数文件 + */ +export default ` +// 常量 +// 池化大小 +const int width_shape_pool = KSIZE_X; +const int height_shape_pool = KSIZE_Y; +const int type_pool = TYPE_POOL; +// 输入数据 +const int width_shape_origin = WIDTH_SHAPE_ORIGIN; +const int height_shape_origin = HEIGHT_SHAPE_ORIGIN; +const int length_shape_origin = LENGTH_SHAPE_ORIGIN; +const int width_texture_origin = WIDTH_TEXTURE_ORIGIN; +const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN; +const int channel_origin = CHANNEL_ORIGIN; +const int offset_x_origin = OFFSET_X_ORIGIN; +const int offset_y_origin = OFFSET_Y_ORIGIN; + + +// 计算相关 +// 拆分步长 +const int stride_h = STRIDES_X; +const int stride_v = STRIDES_Y; +// padding的数目 +const int padLeft = PADDINGS_X; +const int padTop = PADDINGS_Y; + + +// uniform变量 +uniform sampler2D texture_origin; +`; diff --git a/web/src/shader/softmax/conf.es6 b/web/src/shader/softmax/conf.es6 index ff04060e3b..07a7bbab41 100644 --- a/web/src/shader/softmax/conf.es6 +++ b/web/src/shader/softmax/conf.es6 @@ -15,7 +15,8 @@ export default { conf: [ 'WIDTH_TEXTURE_ORIGIN', 'HEIGHT_TEXTURE_ORIGIN', - 'TOTAL_SHAPE_ORIGIN' + 'TOTAL_SHAPE_ORIGIN', + 'OFFSET_Y_OUT' ], input: [ { diff --git a/web/src/shader/v_shader2.es6 b/web/src/shader/v_shader2.es6 new file mode 100644 index 0000000000..529dc61102 --- /dev/null +++ b/web/src/shader/v_shader2.es6 @@ -0,0 +1,15 @@ +/* eslint-disable */ +/** + * @file 顶点文件,webgl 2.0 + * @author yangmingming + */ +export default `#version 300 es +in vec4 position; +out vec2 vCoord; + +void main() { + vCoord.x = (position.x + 1.0) / 2.0; + vCoord.y = (position.y + 1.0) / 2.0; + gl_Position = position; +} +`; diff --git a/web/src/test/getMaxUniforms.es6 b/web/src/test/getMaxUniforms.es6 new file mode 100644 index 0000000000..9fd0f710a8 --- /dev/null +++ b/web/src/test/getMaxUniforms.es6 @@ -0,0 +1,59 @@ +/** + * @file 获取当前环境的max uniform变量 + * @author yangmingming + */ +// uniform变量类型 +const enums = { + 0x8B50: 'FLOAT_VEC2', + 0x8B51: 'FLOAT_VEC3', + 0x8B52: 'FLOAT_VEC4', + 0x8B53: 'INT_VEC2', + 0x8B54: 'INT_VEC3', + 0x8B55: 'INT_VEC4', + 0x8B56: 'BOOL', + 0x8B57: 'BOOL_VEC2', + 0x8B58: 'BOOL_VEC3', + 0x8B59: 'BOOL_VEC4', + 0x8B5A: 'FLOAT_MAT2', + 0x8B5B: 'FLOAT_MAT3', + 0x8B5C: 'FLOAT_MAT4', + 0x8B5E: 'SAMPLER_2D', + 0x8B60: 'SAMPLER_CUBE', + 0x1400: 'BYTE', + 0x1401: 'UNSIGNED_BYTE', + 0x1402: 'SHORT', + 0x1403: 'UNSIGNED_SHORT', + 0x1404: 'INT', + 0x1405: 'UNSIGNED_INT', + 0x1406: 'FLOAT' +}; +export default function(gl, program) { + // max fragment shader, 安卓是256, 桌面chrome浏览器是1024 + const result = { + attributes: [], + uniforms: [], + attributeCount: 0, + uniformCount: 0, + maxVertexShader: gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS), + maxFragmentShader: gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS) + }; + const activeUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + const activeAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); + // Loop through active uniforms + for (let i = 0; i < activeUniforms; i++) { + const uniform = gl.getActiveUniform(program, i); + uniform.typeName = enums[uniform.type]; + result.uniforms.push(uniform); + result.uniformCount += uniform.size; + } + + // Loop through active attributes + for (let i = 0; i < activeAttributes; i++) { + const attribute = gl.getActiveAttrib(program, i); + attribute.typeName = enums[attribute.type]; + result.attributes.push(attribute); + result.attributeCount += attribute.size; + } + + return result; +}; diff --git a/web/src/utils/models.es6 b/web/src/utils/models.es6 new file mode 100644 index 0000000000..80c1270441 --- /dev/null +++ b/web/src/utils/models.es6 @@ -0,0 +1,46 @@ +export default { + '608': { + modelPath: 'faceModel', + feedShape: { + fw: 608, + fh: 608 + }, + outputShapes: { + from: [19, 19, 25, 1], + to: [19, 19, 5, 5] + } + }, + '320': { + modelPath: 'facemodel320', + feedShape: { + fw: 320, + fh: 320 + }, + outputShapes: { + from: [10, 10, 25, 1], + to: [10, 10, 5, 5] + } + }, + '320fused': { + modelPath: 'facemodelfused', + feedShape: { + fw: 320, + fh: 320 + }, + outputShapes: { + from: [10, 10, 25, 1], + to: [10, 10, 5, 5] + } + }, + 'separate': { + modelPath: 'separablemodel', + feedShape: { + fw: 320, + fh: 320 + }, + outputShapes: { + from: [10, 10, 25, 1], + to: [10, 10, 5, 5] + } + } +}; diff --git a/web/src/utils/opData.es6 b/web/src/utils/opData.es6 index ae9d21f3b8..80526f4f8b 100644 --- a/web/src/utils/opData.es6 +++ b/web/src/utils/opData.es6 @@ -19,6 +19,9 @@ const tensorAttrs = [ 'height_shape', 'width_texture', 'height_texture', + 'offset_x', + 'offset_y', + 'limit', 'channel', 'total_shape' ]; @@ -30,6 +33,9 @@ const shaderAttrs = { }, pool2d: { 'pooling_type': 'type_pool' + }, + pool2d_winograd: { + 'pooling_type': 'type_pool' } }; // model的名字和paddle web的tensor名字mapping @@ -48,7 +54,8 @@ const tensorName = { // unique behavior const opBehavior = { conv2d: [ - 'needBatch' + 'needBatch', + 'isApplySeparableConv' ], batchnorm: [ 'needBatch', @@ -58,9 +65,15 @@ const opBehavior = { 'broadcast', 'needBatch' ], + conv2d_elementwise_add: [ + 'mergeAttrs', + 'setActiveFunc', + 'needBatch' + ], pool2d: [ 'isMax', 'needBatch', + 'setPacked', 'isGlobalPooling' ], relu: [ @@ -76,10 +89,14 @@ const opBehavior = { 'needBatch' ] }; +const mergeType = 'conv2d-elementwise_add'; export default class OpData { constructor(name, input = {}, output = {}, attrs = {}) { + this.realName = name; this.name = name; this.attrs = attrs; + // 检查是否是融合op + this.checkIsMerge(); // 是否忽略当前当前op, 使用dropout this.isPass = this.checkIsPass(); if (this.isPass) { @@ -133,6 +150,7 @@ export default class OpData { }); // 生成tensor对象 tensorData.forEach(data => { + // console.log(data); if (data) { if (data.notTensor) { this.tensor[data.tensorName] = { @@ -142,11 +160,13 @@ export default class OpData { }; } else { this.tensor[data.tensorName] = new Tensor({ + type: data.name, name: data.tensorName, shape: data.shape, data: data.data, needBatch: data.needBatch || false, - notCompressed: data.notCompressed || false + notCompressed: data.notCompressed || false, + isPacked: data.isPacked || false }); } } @@ -195,15 +215,74 @@ export default class OpData { } } - broadcast(tensorData = []) { - const x = tensorData[0]; - const y = tensorData[1]; - let small = y; - if (x.shape.length - y.shape.length < 0) { - small = x; + mergeAttrs() { + this.attrs = this.attrs.reduce((attrs, item) => { + return Object.assign(attrs, item); + }, {}); + } + + isApplyWinoGrad(tensorData = []) { + const filter = tensorData.filter(item => { + const [b, c, h, w] = item.shape; + return (h === 3) && (w === 3) && (item.tensorName === 'filter'); + }); + // 使用winograd算法 + if (filter && filter.length) { + this.setPacked(tensorData); + this.applyWinograd(tensorData); + this.setOutputPacked(tensorData); + this.name += '_winograd'; } - // face model - small.notTensor = true; + } + + isApplySeparableConv(tensorData = []) { + const groups = this.attrs.groups; + const filter = tensorData.filter(item => { + const [b, c, h, w] = item.shape; + return (b === groups) && (c === 1) && (item.tensorName === 'filter'); + }); + if (filter && filter.length) { + // 可以执行separable conv + this.name += '_depthwise'; + } + } + + setPacked(tensorData = []) { + const isPacked = this.attrs.ispacked; + tensorData.forEach(item => { + if (item.tensorName === 'origin' && isPacked) { + item.isPacked = true; + if (this.name.indexOf('pool') > -1) { + this.name += '_winograd'; + } + } + }); + } + + applyWinograd(tensorData = []) { + tensorData.forEach(item => { + if (item.tensorName === 'filter') { + const [b, c, h, w] = item.shape; + item.shape = [b, c, 4, 4]; + item.data = Utils.applyFilterWinograd(item.data, item.shape); + } + }); + } + + setOutputPacked(tensorData = []) { + tensorData.forEach(item => { + if (item.tensorName === 'out') { + item.isPacked = true; + } + }); + } + + broadcast(tensorData = []) { + tensorData.forEach(item => { + if (item.tensorName === 'counter') { + item.notTensor = true; + } + }); return; // mobilenet model @@ -227,6 +306,9 @@ export default class OpData { isMax(tensorData = []) { const type = this.attrs['pooling_type'] === 'max' ? 1 : 0; this.attrs['pooling_type'] = type; + if (type === 1) { + this.name += '_max'; + } } transToPrelu(tensorData = []) { @@ -240,10 +322,13 @@ export default class OpData { this.name = 'relu'; } - setActiveFunc(tensorData = []) { - this.data['multi_value'] = '0.0'; - this.data['active_function'] = 'softmax'; - + setActiveFunc() { + // 用于融合op + const suffix = this.realName.replace(mergeType + '-', ''); + if (suffix === 'leaky_relu') { + this.data['multi_value'] = this.attrs.alpha; + this.data['active_function'] = 'leakyRelu'; + } } reshape(tensorData = []) { @@ -284,6 +369,16 @@ export default class OpData { tensorData.splice(result[constants[3] + 'Index'], 1, 0); } + checkIsMerge() { + if (this.name.indexOf(mergeType) > -1 + && Object.prototype.toString.apply(this.attrs) === '[object Array]') { + // 第一个融合op + this.name = 'conv2d_elementwise_add'; + return true; + } + return false; + } + checkIsPass() { if (this.name === 'dropout') { if (this.attrs['dropout_implementation'] === 'downgrade_in_infer') { diff --git a/web/src/utils/tensor.es6 b/web/src/utils/tensor.es6 index 3afad8eb2e..492d02ba05 100644 --- a/web/src/utils/tensor.es6 +++ b/web/src/utils/tensor.es6 @@ -7,6 +7,8 @@ import Utils from './utils'; export default class Tensor { constructor(opts = {}) { this.opts = opts; + // 数据存储方式 + this.isPacked = this.isPacked || false; // 设置tensor名字 this.name = opts.name; // tensor的形状 @@ -23,36 +25,44 @@ export default class Tensor { this.shape = shape; } // 获取转换到texture后的信息 - let {zeroNumber, shape: shape_texture} = Utils.getTextureInfoFromTensorShape(shape); + let {offsetX, offsetY, exceedMax, zeroNumber, shape: shape_texture} = Utils.getTextureInfoFromTensorShape(shape, opts.isPacked); this.shape_texture = shape_texture; + this.exceedMax = exceedMax; + this.offsetX = offsetX; + this.offsetY = offsetY; // tensor数据 - let data = []; - if (opts.data && opts.data.length) { + let data; + if (opts.type === 'image' || opts.type === 'x') { + this.data = opts.data; + } + else if (opts.data && opts.data.length) { + data = new Float32Array(opts.data.length); if (!opts.notCompressed) { let b = shape[0]; let c = shape[1]; let h = shape[2]; let w = shape[3]; + for (let i = 0; i < opts.data.length; i++) { - let j = Math.floor(i / (c * w)); - let k = Math.floor(i % (c * w)); - let b1 = Math.floor(j / h); - let h1 = Math.floor(j % h); - let c1 = Math.floor(k % c); - let w1 = Math.floor(k / c); + let j = i / (c * w) | 0; + let k = i % (c * w); + let b1 = j / h | 0; + let h1 = j % h; + let c1 = k % c; + let w1 = k / c | 0; let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1; - data.push(opts.data[l]); - data.push(0); - data.push(0); - data.push(0); + data[i] = opts.data[l]; } + this.data = data; } else { // batchnorm的scale this.shape_texture = [4, 1, this.total / 4]; - data = [].concat(opts.data); + // data = [].concat(opts.data); + this.data = new Float32Array(opts.data); } - this.data = new Float32Array(data); + // this.data = new Float32Array(data); + // console.log('this.data.length', this.data.length); // 清理缓存 opts.data = null; } @@ -106,6 +116,18 @@ export default class Tensor { return 0; } + get offset_x() { + return this.offsetX; + } + + get offset_y() { + return this.offsetY; + } + + get limit() { + return this.exceedMax ? 'Limit' : ''; + } + get length_shape() { return this.shape.length || 0; } diff --git a/web/src/utils/utils.es6 b/web/src/utils/utils.es6 index 6c747c0978..f27318e9e5 100644 --- a/web/src/utils/utils.es6 +++ b/web/src/utils/utils.es6 @@ -64,18 +64,78 @@ export default { return result; }, + applyFilterWinograd(data, shape) { + const [b, c, h, w] = shape; + let offset = 0; + let index = 0; + const result = new Float32Array(b * c * 16); + // h和w是3、3 + const size2D = 9; + for (let i = 0; i < b; i++) { + // let index = i * c * size2D; + for (let j = 0; j < c; j++) { + // index += j * size2D; + const filter = data.subarray(index, index + size2D); + const [f11, f12, f13, f21, f22, f23, f31, f32, f33] = filter; + const square = [ + f11, + 0.5 * f11 + 0.5 * f12 + 0.5 * f13, + 0.5 * f11 - 0.5 * f12 + 0.5 * f13, + f13, + 0.5 * f11 + 0.5 * f21 + 0.5 * f31, + 0.25 * f11 + 0.25 * f12 + 0.25 * f13 + 0.25 * f21 + 0.25 * f22 + 0.25 * f23 + 0.25 * f31 + 0.25 * f32 + 0.25 * f33, + 0.25 * f11 - 0.25 * f12 + 0.25 * f13 + 0.25 * f21 - 0.25 * f22 + 0.25 * f23 + 0.25 * f31 - 0.25 * f32 + 0.25 * f33, + 0.5 * f13 + 0.5 * f23 + 0.5 * f33, + 0.5 * f11 - 0.5 * f21 + 0.5 * f31, + 0.25 * f11 + 0.25 * f12 + 0.25 * f13 - 0.25 * f21 - 0.25 * f22 - 0.25 * f23 + 0.25 * f31 + 0.25 * f32 + 0.25 * f33, + 0.25 * f11 - 0.25 * f12 + 0.25 * f13 - 0.25 * f21 + 0.25 * f22 - 0.25 * f23 + 0.25 * f31 - 0.25 * f32 + 0.25 * f33, + 0.5 * f13 - 0.5 * f23 + 0.5 * f33, + f31, + 0.5 * f31 + 0.5 * f32 + 0.5 * f33, + 0.5 * f31 - 0.5 * f32 + 0.5 * f33, + f33 + ]; + result.set(square, offset); + offset += 16; + index += size2D; + } + } + return result; + }, + /** * 获取texture形状和补0个数 * @param shape {Array} tensor的形状 * @return {{shape: *[], zeroNumber: number}} {Object} texture信息 */ - getTextureInfoFromTensorShape(shape = []) { + getTextureInfoFromTensorShape(shape = [], isPacked = false) { let b = shape[0]; let c = shape[1]; let h = shape[2]; let w = shape[3]; + let height = b * h; + let width = c * w; + let offsetX = 0; + let offsetY = 0; + // 安卓和ios的max texture size是4096, 改造存储空间(2bh, cw / 2) + let exceedMax = false; + if (height > 4096 || width > 4096) { + height *= 2; + width = c * (Math.ceil(w / 2)); + exceedMax = true; + } + if (isPacked) { + // 紧凑布局 + height = b * c * Math.ceil(h / 2); + width = Math.ceil(w / 2); + offsetX = w % 2; + offsetY = h % 2; + } return { - shape: [4, b * h, c * w], + offsetX, + offsetY, + exceedMax, + shape: [4, height, width], zeroNumber: 0 }; }, @@ -107,21 +167,24 @@ export default { const c = shape[1]; const h = shape[2]; const w = shape[3]; - const data = []; + let data = new Float32Array(b * c * h * w * 4); + let offset = 0; for (let i = 0; i < total; i++) { - let j = Math.floor(i / (c * w)); - let k = Math.floor(i % (c * w)); - let b1 = Math.floor(j / h); - let h1 = Math.floor(j % h); - let c1 = Math.floor(k % c); - let w1 = Math.floor(k / c); + let j = (i / (c * w)) | 0; + let k = i % (c * w); + let b1 = j / h | 0; + let h1 = j % h; + let c1 = k % c; + let w1 = k / c | 0; let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1; - data.push(renderData.data[l]); - data.push(0); - data.push(0); - data.push(0); + data[offset] = renderData.data[l]; + offset += 4; + // data.push(renderData.data[l]); + // data.push(0); + // data.push(0); + // data.push(0); } - renderData.data = new Float32Array(data); + renderData.data = data; } }; /* eslint-enable */ diff --git a/web/test/testUtils/diff.js b/web/test/testUtils/diff.js deleted file mode 100644 index beb9b6ce23..0000000000 --- a/web/test/testUtils/diff.js +++ /dev/null @@ -1,1055 +0,0 @@ -/*! - - diff v2.0.1 - -Software License Agreement (BSD License) - -Copyright (c) 2009-2015, Kevin Decker - -All rights reserved. - -Redistribution and use of this software in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of Kevin Decker nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -@license -*/ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(); - else if(typeof define === 'function' && define.amd) - define(factory); - else if(typeof exports === 'object') - exports["JsDiff"] = factory(); - else - root["JsDiff"] = factory(); -})(this, function() { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - /* See LICENSE file for terms of use */ - - /* - * Text diff implementation. - * - * This library supports the following APIS: - * JsDiff.diffChars: Character by character diff - * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace - * JsDiff.diffLines: Line based diff - * - * JsDiff.diffCss: Diff targeted at CSS content - * - * These methods are based on the implementation proposed in - * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986). - * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927 - */ - 'use strict'; - - exports.__esModule = true; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _diffBase = __webpack_require__(1); - - var _diffBase2 = _interopRequireDefault(_diffBase); - - var _diffCharacter = __webpack_require__(3); - - var _diffWord = __webpack_require__(4); - - var _diffLine = __webpack_require__(5); - - var _diffSentence = __webpack_require__(6); - - var _diffCss = __webpack_require__(7); - - var _diffJson = __webpack_require__(8); - - var _patchApply = __webpack_require__(9); - - var _patchCreate = __webpack_require__(10); - - var _convertDmp = __webpack_require__(12); - - var _convertXml = __webpack_require__(13); - - exports.Diff = _diffBase2['default']; - exports.diffChars = _diffCharacter.diffChars; - exports.diffWords = _diffWord.diffWords; - exports.diffWordsWithSpace = _diffWord.diffWordsWithSpace; - exports.diffLines = _diffLine.diffLines; - exports.diffTrimmedLines = _diffLine.diffTrimmedLines; - exports.diffSentences = _diffSentence.diffSentences; - exports.diffCss = _diffCss.diffCss; - exports.diffJson = _diffJson.diffJson; - exports.structuredPatch = _patchCreate.structuredPatch; - exports.createTwoFilesPatch = _patchCreate.createTwoFilesPatch; - exports.createPatch = _patchCreate.createPatch; - exports.applyPatch = _patchApply.applyPatch; - exports.convertChangesToDMP = _convertDmp.convertChangesToDMP; - exports.convertChangesToXML = _convertXml.convertChangesToXML; - exports.canonicalize = _diffJson.canonicalize; - - -/***/ }, -/* 1 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - exports['default'] = Diff; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _utilMap = __webpack_require__(2); - - var _utilMap2 = _interopRequireDefault(_utilMap); - - function Diff(ignoreWhitespace) { - this.ignoreWhitespace = ignoreWhitespace; - } - - Diff.prototype = { - diff: function diff(oldString, newString, callback) { - var self = this; - - function done(value) { - if (callback) { - setTimeout(function () { - callback(undefined, value); - }, 0); - return true; - } else { - return value; - } - } - - // Allow subclasses to massage the input prior to running - oldString = this.castInput(oldString); - newString = this.castInput(newString); - - // Handle the identity case (this is due to unrolling editLength == 0 - if (newString === oldString) { - return done([{ value: newString }]); - } - if (!newString) { - return done([{ value: oldString, removed: true }]); - } - if (!oldString) { - return done([{ value: newString, added: true }]); - } - - newString = this.removeEmpty(this.tokenize(newString)); - oldString = this.removeEmpty(this.tokenize(oldString)); - - var newLen = newString.length, - oldLen = oldString.length; - var editLength = 1; - var maxEditLength = newLen + oldLen; - var bestPath = [{ newPos: -1, components: [] }]; - - // Seed editLength = 0, i.e. the content starts with the same values - var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); - if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { - // Identity per the equality and tokenizer - return done([{ value: newString.join('') }]); - } - - // Main worker method. checks all permutations of a given edit length for acceptance. - function execEditLength() { - for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { - var basePath = undefined; - var addPath = bestPath[diagonalPath - 1], - removePath = bestPath[diagonalPath + 1], - _oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; - if (addPath) { - // No one else is going to attempt to use this value, clear it - bestPath[diagonalPath - 1] = undefined; - } - - var canAdd = addPath && addPath.newPos + 1 < newLen, - canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen; - if (!canAdd && !canRemove) { - // If this path is a terminal then prune - bestPath[diagonalPath] = undefined; - continue; - } - - // Select the diagonal that we want to branch from. We select the prior - // path whose position in the new string is the farthest from the origin - // and does not pass the bounds of the diff graph - if (!canAdd || canRemove && addPath.newPos < removePath.newPos) { - basePath = clonePath(removePath); - self.pushComponent(basePath.components, undefined, true); - } else { - basePath = addPath; // No need to clone, we've pulled it from the list - basePath.newPos++; - self.pushComponent(basePath.components, true, undefined); - } - - _oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); - - // If we have hit the end of both strings, then we are done - if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) { - return done(buildValues(basePath.components, newString, oldString, self.useLongestToken)); - } else { - // Otherwise track this path as a potential candidate and continue. - bestPath[diagonalPath] = basePath; - } - } - - editLength++; - } - - // Performs the length of edit iteration. Is a bit fugly as this has to support the - // sync and async mode which is never fun. Loops over execEditLength until a value - // is produced. - if (callback) { - (function exec() { - setTimeout(function () { - // This should not happen, but we want to be safe. - /* istanbul ignore next */ - if (editLength > maxEditLength) { - return callback(); - } - - if (!execEditLength()) { - exec(); - } - }, 0); - })(); - } else { - while (editLength <= maxEditLength) { - var ret = execEditLength(); - if (ret) { - return ret; - } - } - } - }, - - pushComponent: function pushComponent(components, added, removed) { - var last = components[components.length - 1]; - if (last && last.added === added && last.removed === removed) { - // We need to clone here as the component clone operation is just - // as shallow array clone - components[components.length - 1] = { count: last.count + 1, added: added, removed: removed }; - } else { - components.push({ count: 1, added: added, removed: removed }); - } - }, - extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { - var newLen = newString.length, - oldLen = oldString.length, - newPos = basePath.newPos, - oldPos = newPos - diagonalPath, - commonCount = 0; - while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { - newPos++; - oldPos++; - commonCount++; - } - - if (commonCount) { - basePath.components.push({ count: commonCount }); - } - - basePath.newPos = newPos; - return oldPos; - }, - - equals: function equals(left, right) { - var reWhitespace = /\S/; - return left === right || this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); - }, - removeEmpty: function removeEmpty(array) { - var ret = []; - for (var i = 0; i < array.length; i++) { - if (array[i]) { - ret.push(array[i]); - } - } - return ret; - }, - castInput: function castInput(value) { - return value; - }, - tokenize: function tokenize(value) { - return value.split(''); - } - }; - - function buildValues(components, newString, oldString, useLongestToken) { - var componentPos = 0, - componentLen = components.length, - newPos = 0, - oldPos = 0; - - for (; componentPos < componentLen; componentPos++) { - var component = components[componentPos]; - if (!component.removed) { - if (!component.added && useLongestToken) { - var value = newString.slice(newPos, newPos + component.count); - value = _utilMap2['default'](value, function (value, i) { - var oldValue = oldString[oldPos + i]; - return oldValue.length > value.length ? oldValue : value; - }); - - component.value = value.join(''); - } else { - component.value = newString.slice(newPos, newPos + component.count).join(''); - } - newPos += component.count; - - // Common case - if (!component.added) { - oldPos += component.count; - } - } else { - component.value = oldString.slice(oldPos, oldPos + component.count).join(''); - oldPos += component.count; - - // Reverse add and remove so removes are output first to match common convention - // The diffing algorithm is tied to add then remove output and this is the simplest - // route to get the desired output with minimal overhead. - if (componentPos && components[componentPos - 1].added) { - var tmp = components[componentPos - 1]; - components[componentPos - 1] = components[componentPos]; - components[componentPos] = tmp; - } - } - } - - return components; - } - - function clonePath(path) { - return { newPos: path.newPos, components: path.components.slice(0) }; - } - module.exports = exports['default']; - - -/***/ }, -/* 2 */ -/***/ function(module, exports) { - - // Following this pattern to make sure the ignore next is in the correct place after babel builds - "use strict"; - - exports.__esModule = true; - exports["default"] = map; - - /* istanbul ignore next */ - function map(arr, mapper, that) { - if (Array.prototype.map) { - return Array.prototype.map.call(arr, mapper, that); - } - - var other = new Array(arr.length); - - for (var i = 0, n = arr.length; i < n; i++) { - other[i] = mapper.call(that, arr[i], i, arr); - } - return other; - } - module.exports = exports["default"]; - - -/***/ }, -/* 3 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - exports.diffChars = diffChars; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _base = __webpack_require__(1); - - var _base2 = _interopRequireDefault(_base); - - var characterDiff = new _base2['default'](); - exports.characterDiff = characterDiff; - - function diffChars(oldStr, newStr, callback) { - return characterDiff.diff(oldStr, newStr, callback); - } - - -/***/ }, -/* 4 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - exports.diffWords = diffWords; - exports.diffWordsWithSpace = diffWordsWithSpace; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _base = __webpack_require__(1); - - // Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode - // - // Ranges and exceptions: - // Latin-1 Supplement, 0080–00FF - // - U+00D7 × Multiplication sign - // - U+00F7 ÷ Division sign - // Latin Extended-A, 0100–017F - // Latin Extended-B, 0180–024F - // IPA Extensions, 0250–02AF - // Spacing Modifier Letters, 02B0–02FF - // - U+02C7 ˇ ˇ Caron - // - U+02D8 ˘ ˘ Breve - // - U+02D9 ˙ ˙ Dot Above - // - U+02DA ˚ ˚ Ring Above - // - U+02DB ˛ ˛ Ogonek - // - U+02DC ˜ ˜ Small Tilde - // - U+02DD ˝ ˝ Double Acute Accent - // Latin Extended Additional, 1E00–1EFF - - var _base2 = _interopRequireDefault(_base); - - var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; - - var wordDiff = new _base2['default'](true); - exports.wordDiff = wordDiff; - var wordWithSpaceDiff = new _base2['default'](); - exports.wordWithSpaceDiff = wordWithSpaceDiff; - wordDiff.tokenize = wordWithSpaceDiff.tokenize = function (value) { - var tokens = value.split(/(\s+|\b)/); - - // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. - for (var i = 0; i < tokens.length - 1; i++) { - // If we have an empty string in the next field and we have only word chars before and after, merge - if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { - tokens[i] += tokens[i + 2]; - tokens.splice(i + 1, 2); - i--; - } - } - - return tokens; - }; - - function diffWords(oldStr, newStr, callback) { - return wordDiff.diff(oldStr, newStr, callback); - } - - function diffWordsWithSpace(oldStr, newStr, callback) { - return wordWithSpaceDiff.diff(oldStr, newStr, callback); - } - - -/***/ }, -/* 5 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - exports.diffLines = diffLines; - exports.diffTrimmedLines = diffTrimmedLines; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _base = __webpack_require__(1); - - var _base2 = _interopRequireDefault(_base); - - var lineDiff = new _base2['default'](); - exports.lineDiff = lineDiff; - var trimmedLineDiff = new _base2['default'](); - exports.trimmedLineDiff = trimmedLineDiff; - trimmedLineDiff.ignoreTrim = true; - - lineDiff.tokenize = trimmedLineDiff.tokenize = function (value) { - var retLines = [], - lines = value.split(/^/m); - for (var i = 0; i < lines.length; i++) { - var line = lines[i], - lastLine = lines[i - 1], - lastLineLastChar = lastLine && lastLine[lastLine.length - 1]; - - // Merge lines that may contain windows new lines - if (line === '\n' && lastLineLastChar === '\r') { - retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0, -1) + '\r\n'; - } else { - if (this.ignoreTrim) { - line = line.trim(); - // add a newline unless this is the last line. - if (i < lines.length - 1) { - line += '\n'; - } - } - retLines.push(line); - } - } - - return retLines; - }; - - function diffLines(oldStr, newStr, callback) { - return lineDiff.diff(oldStr, newStr, callback); - } - - function diffTrimmedLines(oldStr, newStr, callback) { - return trimmedLineDiff.diff(oldStr, newStr, callback); - } - - -/***/ }, -/* 6 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - exports.diffSentences = diffSentences; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _base = __webpack_require__(1); - - var _base2 = _interopRequireDefault(_base); - - var sentenceDiff = new _base2['default'](); - exports.sentenceDiff = sentenceDiff; - sentenceDiff.tokenize = function (value) { - return value.split(/(\S.+?[.!?])(?=\s+|$)/); - }; - - function diffSentences(oldStr, newStr, callback) { - return sentenceDiff.diff(oldStr, newStr, callback); - } - - -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - exports.diffCss = diffCss; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _base = __webpack_require__(1); - - var _base2 = _interopRequireDefault(_base); - - var cssDiff = new _base2['default'](); - exports.cssDiff = cssDiff; - cssDiff.tokenize = function (value) { - return value.split(/([{}:;,]|\s+)/); - }; - - function diffCss(oldStr, newStr, callback) { - return cssDiff.diff(oldStr, newStr, callback); - } - - -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - exports.diffJson = diffJson; - exports.canonicalize = canonicalize; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _base = __webpack_require__(1); - - var _base2 = _interopRequireDefault(_base); - - var _line = __webpack_require__(5); - - var objectPrototypeToString = Object.prototype.toString; - - var jsonDiff = new _base2['default'](); - // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a - // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: - exports.jsonDiff = jsonDiff; - jsonDiff.useLongestToken = true; - - jsonDiff.tokenize = _line.lineDiff.tokenize; - jsonDiff.castInput = function (value) { - return typeof value === 'string' ? value : JSON.stringify(canonicalize(value), undefined, ' '); - }; - jsonDiff.equals = function (left, right) { - return _base2['default'].prototype.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); - }; - - function diffJson(oldObj, newObj, callback) { - return jsonDiff.diff(oldObj, newObj, callback); - } - - // This function handles the presence of circular references by bailing out when encountering an - // object that is already on the "stack" of items being processed. - - function canonicalize(obj, stack, replacementStack) { - stack = stack || []; - replacementStack = replacementStack || []; - - var i = undefined; - - for (i = 0; i < stack.length; i += 1) { - if (stack[i] === obj) { - return replacementStack[i]; - } - } - - var canonicalizedObj = undefined; - - if ('[object Array]' === objectPrototypeToString.call(obj)) { - stack.push(obj); - canonicalizedObj = new Array(obj.length); - replacementStack.push(canonicalizedObj); - for (i = 0; i < obj.length; i += 1) { - canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack); - } - stack.pop(); - replacementStack.pop(); - } else if (typeof obj === 'object' && obj !== null) { - stack.push(obj); - canonicalizedObj = {}; - replacementStack.push(canonicalizedObj); - var sortedKeys = [], - key = undefined; - for (key in obj) { - /* istanbul ignore else */ - if (obj.hasOwnProperty(key)) { - sortedKeys.push(key); - } - } - sortedKeys.sort(); - for (i = 0; i < sortedKeys.length; i += 1) { - key = sortedKeys[i]; - canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack); - } - stack.pop(); - replacementStack.pop(); - } else { - canonicalizedObj = obj; - } - return canonicalizedObj; - } - - -/***/ }, -/* 9 */ -/***/ function(module, exports) { - - 'use strict'; - - exports.__esModule = true; - exports.applyPatch = applyPatch; - - function applyPatch(oldStr, uniDiff) { - var diffstr = uniDiff.split('\n'), - hunks = [], - i = 0, - remEOFNL = false, - addEOFNL = false; - - // Skip to the first change hunk - while (i < diffstr.length && !/^@@/.test(diffstr[i])) { - i++; - } - - // Parse the unified diff - for (; i < diffstr.length; i++) { - if (diffstr[i][0] === '@') { - var chnukHeader = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); - hunks.unshift({ - start: chnukHeader[3], - oldlength: +chnukHeader[2], - removed: [], - newlength: chnukHeader[4], - added: [] - }); - } else if (diffstr[i][0] === '+') { - hunks[0].added.push(diffstr[i].substr(1)); - } else if (diffstr[i][0] === '-') { - hunks[0].removed.push(diffstr[i].substr(1)); - } else if (diffstr[i][0] === ' ') { - hunks[0].added.push(diffstr[i].substr(1)); - hunks[0].removed.push(diffstr[i].substr(1)); - } else if (diffstr[i][0] === '\\') { - if (diffstr[i - 1][0] === '+') { - remEOFNL = true; - } else if (diffstr[i - 1][0] === '-') { - addEOFNL = true; - } - } - } - - // Apply the diff to the input - var lines = oldStr.split('\n'); - for (i = hunks.length - 1; i >= 0; i--) { - var hunk = hunks[i]; - // Sanity check the input string. Bail if we don't match. - for (var j = 0; j < hunk.oldlength; j++) { - if (lines[hunk.start - 1 + j] !== hunk.removed[j]) { - return false; - } - } - Array.prototype.splice.apply(lines, [hunk.start - 1, hunk.oldlength].concat(hunk.added)); - } - - // Handle EOFNL insertion/removal - if (remEOFNL) { - while (!lines[lines.length - 1]) { - lines.pop(); - } - } else if (addEOFNL) { - lines.push(''); - } - return lines.join('\n'); - } - - -/***/ }, -/* 10 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - exports.structuredPatch = structuredPatch; - exports.createTwoFilesPatch = createTwoFilesPatch; - exports.createPatch = createPatch; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _diffPatch = __webpack_require__(11); - - var _utilMap = __webpack_require__(2); - - var _utilMap2 = _interopRequireDefault(_utilMap); - - function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { - if (!options) { - options = { context: 4 }; - } - - var diff = _diffPatch.patchDiff.diff(oldStr, newStr); - diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier - - function contextLines(lines) { - return _utilMap2['default'](lines, function (entry) { - return ' ' + entry; - }); - } - - var hunks = []; - var oldRangeStart = 0, - newRangeStart = 0, - curRange = [], - oldLine = 1, - newLine = 1; - - var _loop = function (i) { - var current = diff[i], - lines = current.lines || current.value.replace(/\n$/, '').split('\n'); - current.lines = lines; - - if (current.added || current.removed) { - // If we have previous context, start with that - if (!oldRangeStart) { - var prev = diff[i - 1]; - oldRangeStart = oldLine; - newRangeStart = newLine; - - if (prev) { - curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; - oldRangeStart -= curRange.length; - newRangeStart -= curRange.length; - } - } - - // Output our changes - curRange.push.apply(curRange, _utilMap2['default'](lines, function (entry) { - return (current.added ? '+' : '-') + entry; - })); - - // Track the updated file position - if (current.added) { - newLine += lines.length; - } else { - oldLine += lines.length; - } - } else { - // Identical context lines. Track line changes - if (oldRangeStart) { - // Close out any changes that have been output (or join overlapping) - if (lines.length <= options.context * 2 && i < diff.length - 2) { - // Overlapping - curRange.push.apply(curRange, contextLines(lines)); - } else { - // end the range and output - var contextSize = Math.min(lines.length, options.context); - curRange.push.apply(curRange, contextLines(lines.slice(0, contextSize))); - - var hunk = { - oldStart: oldRangeStart, - oldLines: oldLine - oldRangeStart + contextSize, - newStart: newRangeStart, - newLines: newLine - newRangeStart + contextSize, - lines: curRange - }; - if (i >= diff.length - 2 && lines.length <= options.context) { - // EOF is inside this hunk - var oldEOFNewline = /\n$/.test(oldStr); - var newEOFNewline = /\n$/.test(newStr); - if (lines.length == 0 && !oldEOFNewline) { - // special case: old has no eol and no trailing context; no-nl can end up before adds - curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); - } else if (!oldEOFNewline || !newEOFNewline) { - curRange.push('\\ No newline at end of file'); - } - } - hunks.push(hunk); - - oldRangeStart = 0; - newRangeStart = 0; - curRange = []; - } - } - oldLine += lines.length; - newLine += lines.length; - } - }; - - for (var i = 0; i < diff.length; i++) { - _loop(i); - } - - return { - oldFileName: oldFileName, newFileName: newFileName, - oldHeader: oldHeader, newHeader: newHeader, - hunks: hunks - }; - } - - function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { - var diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options); - - var ret = []; - if (oldFileName == newFileName) { - ret.push('Index: ' + oldFileName); - } - ret.push('==================================================================='); - ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); - ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); - - for (var i = 0; i < diff.hunks.length; i++) { - var hunk = diff.hunks[i]; - ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); - ret.push.apply(ret, hunk.lines); - } - - return ret.join('\n') + '\n'; - } - - function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { - return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); - } - - -/***/ }, -/* 11 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - // istanbul ignore next - - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } - - var _base = __webpack_require__(1); - - var _base2 = _interopRequireDefault(_base); - - var patchDiff = new _base2['default'](); - exports.patchDiff = patchDiff; - patchDiff.tokenize = function (value) { - var ret = [], - linesAndNewlines = value.split(/(\n|\r\n)/); - - // Ignore the final empty token that occurs if the string ends with a new line - if (!linesAndNewlines[linesAndNewlines.length - 1]) { - linesAndNewlines.pop(); - } - - // Merge the content and line separators into single tokens - for (var i = 0; i < linesAndNewlines.length; i++) { - var line = linesAndNewlines[i]; - - if (i % 2) { - ret[ret.length - 1] += line; - } else { - ret.push(line); - } - } - return ret; - }; - - -/***/ }, -/* 12 */ -/***/ function(module, exports) { - - // See: http://code.google.com/p/google-diff-match-patch/wiki/API - "use strict"; - - exports.__esModule = true; - exports.convertChangesToDMP = convertChangesToDMP; - - function convertChangesToDMP(changes) { - var ret = [], - change = undefined, - operation = undefined; - for (var i = 0; i < changes.length; i++) { - change = changes[i]; - if (change.added) { - operation = 1; - } else if (change.removed) { - operation = -1; - } else { - operation = 0; - } - - ret.push([operation, change.value]); - } - return ret; - } - - -/***/ }, -/* 13 */ -/***/ function(module, exports) { - - 'use strict'; - - exports.__esModule = true; - exports.convertChangesToXML = convertChangesToXML; - - function convertChangesToXML(changes) { - var ret = []; - for (var i = 0; i < changes.length; i++) { - var change = changes[i]; - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } - - ret.push(escapeHTML(change.value)); - - if (change.added) { - ret.push(''); - } else if (change.removed) { - ret.push(''); - } - } - return ret.join(''); - } - - function escapeHTML(s) { - var n = s; - n = n.replace(/&/g, '&'); - n = n.replace(//g, '>'); - n = n.replace(/"/g, '"'); - - return n; - } - - -/***/ } -/******/ ]) -}); -; diff --git a/web/tools/logger.es6 b/web/tools/logger.es6 new file mode 100644 index 0000000000..d6f53cdf4a --- /dev/null +++ b/web/tools/logger.es6 @@ -0,0 +1,101 @@ +/** + * file tools/logger logger工具 + * author saniac(snailsword@gmail.com) + */ + +export default class Logger { + constructor() { + this.timeTable = {}; + this.countTable = {}; + this.duringTable = {}; + this.lastStopTable = {}; + } + + start(key) { + let arr = this.timeTable[key]; + if (!arr) { + arr = [{}]; + } + else { + if (!arr[arr.length - 1].endTime) { + console.error('[logger] key:' + key + ' duplicate start logger'); + return; + } + arr.push({}); + } + arr[arr.length - 1].startTime = this.time; + this.timeTable[key] = arr; + return this; + } + + end(key) { + // console.log(this.timeTable[key]); + if (!this.timeTable[key]) { + console.log(this.timeTable[key]); + console.error('[logger] key:' + key + ' no matching start logger'); + return; + } + let currentObj = this.timeTable[key][this.timeTable[key].length - 1]; + if (currentObj.endTime) { + console.error('[logger] key:' + key + ' duplicate end logger'); + return; + } + currentObj.endTime = this.time; + currentObj.during = currentObj.endTime - currentObj.startTime; + return this; + } + + // 数次数 + count(key) { + if (this.countTable[key]) { + this.countTable[key]++; + } + else { + this.countTable[key] = 1; + } + return this; + } + + // 看每次执行的时间间隔 + during(key) { + if (this.lastStopTable[key]) { + this.duringTable[key].push(this.time - this.lastStopTable[key]); + this.lastStopTable[key] = this.time; + } + else { + this.lastStopTable[key] = this.time; + this.duringTable[key] = []; + } + return this; + } + + get time() { + return +new Date().getTime(); + } + get statistics() { + // time + let timeResult = []; + let item; + for (let key in this.timeTable) { + item = this.timeTable[key]; + let len = item.length; + let max = 0; + let min = Number.MAX_VALUE; + let sum = 0; + for (let i = 0; i < len; i++) { + max = Math.max(max, item[i].during); + min = Math.min(min, item[i].during); + sum += item[i].during; + } + timeResult.push({ + name: key, + length: len, + avg: sum / len, + max, + min + }); + } + console.table(timeResult); + return {timeResult}; + } +} diff --git a/web/tools/toBinaryFile.py b/web/tools/toBinaryFile.py index bd505b44c2..e3b4ff91d4 100644 --- a/web/tools/toBinaryFile.py +++ b/web/tools/toBinaryFile.py @@ -99,13 +99,13 @@ class BinaryFileConverter: BinaryFileConverter( - delimiter = ',', - ignorChar = ['[', ']'], - ignorLine = ['[', ']'], - types = ['.txt', '.json'], - originDir = './mobileNet', - resultDir = './binf', - ext = '.dat', - formatter = 'f', - dotPrintRatio = 0, + delimiter = ',', + ignorChar = ['[', ']'], + ignorLine = ['[', ']'], + types = ['.txt', '.json'], + originDir = './mobileNet', + resultDir = './binf', + ext = '.dat', + formatter = 'f', + dotPrintRatio = 0, merge = 6).convert() diff --git a/web/webpack.config.js b/web/webpack.config.js new file mode 100644 index 0000000000..ebfdb84225 --- /dev/null +++ b/web/webpack.config.js @@ -0,0 +1,48 @@ + +/** + * @file 打包到rd机器的配置 + * @author yangmingming zhangmiao06 + */ + +const path = require('path'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); + +const extractLess = new ExtractTextPlugin({ + filename: '[name].css' +}); + +module.exports = { + mode: 'development', + devtool: 'none', + optimization: { + minimize: false + }, + entry: { + camera: './src/executor/camera', + index: './src/executor/runner' + }, + output: { + filename: '../graphfe/src/view/common/lib/paddle/[name].js', + path: path.resolve(__dirname, './'), + library: 'panorama', + libraryTarget: 'umd', + libraryExport: 'default' + }, + module: { + rules: [{ + test: /\.(eot|woff|woff2|ttf|svg|png|jpg)$/, + loader: 'url-loader?limit=30000&name=[name].[ext]' + }, { + test: /\.less$/, + exclude: /node_modules/, + loader: ExtractTextPlugin.extract([ + {loader: 'css-loader', options: {minimize: true}}, + {loader: 'less-loader'} + ]) + }] + }, + plugins: [extractLess], + resolve: { + extensions: ['.es6', '.js', '.json'] + } +}; -- GitLab