UNPKG

@awayjs/stage

Version:
308 lines (307 loc) 12.2 kB
import { OpcodeMap } from './OpcodeMap'; import { Part } from './Part'; import { RegMap } from './RegMap'; import { SamplerMap } from './SamplerMap'; var AGALMiniAssembler = /** @class */ (function () { function AGALMiniAssembler() { this.nativeInstructionLastRegister = 0xffff - 1; this.r = { vertex: null, fragment: null, }; this.cur = new Part(); } AGALMiniAssembler.prototype.assemble = function (source, ext_part, ext_version) { if (ext_part === void 0) { ext_part = null; } if (ext_version === void 0) { ext_version = null; } if (!ext_version) { ext_version = 1; } if (ext_part) { this.addHeader(ext_part, ext_version); } var lines = source.replace(/[\f\n\r\v]+/g, '\n').split('\n'); // handle breaks, then split into lines for (var i in lines) { this.processLine(lines[i], +i); } return this.r; }; AGALMiniAssembler.prototype.processLine = function (line, linenr) { var startcomment = line.search('//'); // remove comments if (startcomment != -1) { line = line.slice(0, startcomment); } line = line.replace(/^\s+|\s+$/g, ''); // remove outer space if (!(line.length > 0)) { return; } var optsi = line.search(/<.*>/g); // split of options part <*> if there var opts = null; if (optsi != -1) { opts = line.slice(optsi).match(/([\w\.\-\+]+)/gi); line = line.slice(0, optsi); } var natives = {}; var nativesLen = 0; var idx; while ((idx = line.indexOf('#native')) > 0) { var start = idx; idx = line.indexOf('native#', idx); var end = idx > -1 ? idx + 7 : line.length; natives['nat' + nativesLen] = line.substring(start + 7, end - 7); // replace native block on to native index in block line = line.substring(0, start) + 'nat' + (nativesLen) + line.substring(end); nativesLen++; } // get opcode/command var tokens = line.match(/([\w\.\+\[\]]+)/gi); // get tokens in line if (!tokens || tokens.length < 1) { if (line.length >= 3) { console.log('Warning: bad line ' + linenr + ': ' + line); } return; } //console.log ( linenr, line, cur, tokens ); switch (tokens[0]) { case 'part': this.addHeader(tokens[1], Number(tokens[2])); break; case 'endpart': if (!this.cur) { throw 'Unexpected endpart'; } this.cur.data.position = 0; this.cur = null; return; default: { if (!this.cur) { console.log('Warning: bad line ' + linenr + ': ' + line + ' (Outside of any part definition)'); return; } if (this.cur.name == 'comment') { return; } var op = OpcodeMap.map[tokens[0]]; if (!op) { throw 'Bad opcode ' + tokens[0] + ' ' + linenr + ': ' + line; } // console.log( 'AGALMiniAssembler' , 'op' , op ); this.emitOpcode(this.cur, op.opcode); var ti = 1; if (op.dest && op.dest != 'none') { if (!this.emitDest(this.cur, tokens[ti++], op.dest)) { throw 'Bad destination register ' + tokens[ti - 1] + ' ' + linenr + ': ' + line; } } else { this.emitZeroDword(this.cur); } if (op.a && op.a.format != 'none') { if (!this.emitSource(this.cur, tokens[ti++], op.a, natives)) throw 'Bad source register ' + tokens[ti - 1] + ' ' + linenr + ': ' + line; } else { this.emitZeroQword(this.cur); } if (op.b && op.b.format != 'none') { if (op.b.format == 'sampler') { if (!this.emitSampler(this.cur, tokens[ti++], op.b, opts)) { throw 'Bad sampler register ' + tokens[ti - 1] + ' ' + linenr + ': ' + line; } } else { if (!this.emitSource(this.cur, tokens[ti++], op.b, natives)) { throw 'Bad source register ' + tokens[ti - 1] + ' ' + linenr + ': ' + line; } } } else { this.emitZeroQword(this.cur); } break; } } }; AGALMiniAssembler.prototype.emitHeader = function (pr) { pr.data.writeUnsignedByte(0xa0); // tag version pr.data.writeUnsignedInt(pr.version); if (pr.version >= 0x10) { pr.data.writeUnsignedByte(0); // align, for higher versions } pr.data.writeUnsignedByte(0xa1); // tag program id switch (pr.name) { case 'fragment': pr.data.writeUnsignedByte(1); break; case 'vertex': pr.data.writeUnsignedByte(0); break; case 'cpu': pr.data.writeUnsignedByte(2); break; default: pr.data.writeUnsignedByte(0xff); break; // unknown/comment } }; AGALMiniAssembler.prototype.emitOpcode = function (pr, opcode) { pr.data.writeUnsignedInt(opcode); //console.log ( "Emit opcode: ", opcode ); }; AGALMiniAssembler.prototype.emitZeroDword = function (pr) { pr.data.writeUnsignedInt(0); }; AGALMiniAssembler.prototype.emitZeroQword = function (pr) { pr.data.writeUnsignedInt(0); pr.data.writeUnsignedInt(0); }; AGALMiniAssembler.prototype.emitDest = function (pr, token, opdest) { //console.log( 'AGALMiniAssembler' , 'emitDest' , 'RegMap.map' , RegMap.map); var reg = token.match(/([fov]?[tpocidavs])(\d*)(\.[xyzw]{1,4})?/i); // g1: regname, g2:regnum, g3:mask // console.log( 'AGALMiniAssembler' , 'emitDest' , 'reg' , reg , reg[1] , RegMap.map[reg[1]] ); // console.log( 'AGALMiniAssembler' , 'emitDest' , 'RegMap.map[reg[1]]' , RegMap.map[reg[1]] , 'bool' , !RegMap.map[reg[1]] ) ; if (!RegMap.map[reg[1]]) return false; var em = { num: reg[2] ? reg[2] : 0, code: RegMap.map[reg[1]].code, mask: this.stringToMask(reg[3]) }; pr.data.writeUnsignedShort(em.num); pr.data.writeUnsignedByte(em.mask); pr.data.writeUnsignedByte(em.code); //console.log ( " Emit dest: ", em ); return true; }; AGALMiniAssembler.prototype.stringToMask = function (s) { if (!s) return 0xf; var r = 0; if (s.indexOf('x') != -1) r |= 1; if (s.indexOf('y') != -1) r |= 2; if (s.indexOf('z') != -1) r |= 4; if (s.indexOf('w') != -1) r |= 8; return r; }; AGALMiniAssembler.prototype.stringToSwizzle = function (s) { if (!s) { return 0xe4; } var chartoindex = { x: 0, y: 1, z: 2, w: 3 }; var sw = 0; if (s.charAt(0) != '.') { throw 'Missing . for swizzle'; } if (s.length > 1) { sw |= chartoindex[s.charAt(1)]; } if (s.length > 2) { sw |= chartoindex[s.charAt(2)] << 2; } else { sw |= (sw & 3) << 2; } if (s.length > 3) { sw |= chartoindex[s.charAt(3)] << 4; } else { sw |= (sw & (3 << 2)) << 2; } if (s.length > 4) { sw |= chartoindex[s.charAt(4)] << 6; } else { sw |= (sw & (3 << 4)) << 2; } return sw; }; AGALMiniAssembler.prototype.emitSampler = function (pr, token, opsrc, opts) { var reg = token.match(/fs(\d*)/i); // g1:regnum if (!reg || !reg[1]) { return false; } pr.data.writeUnsignedShort(parseInt(reg[1])); pr.data.writeUnsignedByte(0); // bias pr.data.writeUnsignedByte(0); /* pr.data.writeUnsignedByte ( 0x5 ); pr.data.writeUnsignedByte ( 0 ); // readmode, dim pr.data.writeUnsignedByte ( 0 ); // special, wrap pr.data.writeUnsignedByte ( 0 ); // mip, filter */ var samplerbits = 0x5; var sampleroptset = 0; for (var i = 0; i < opts.length; i++) { var o = SamplerMap.map[opts[i].toLowerCase()]; //console.log( 'AGALMiniAssembler' , 'emitSampler' , 'SampleMap opt:' , o , '<-------- WATCH FOR THIS'); if (o) { if (((sampleroptset >> o.shift) & o.mask) != 0) { console.log('Warning, duplicate sampler option'); } sampleroptset |= o.mask << o.shift; samplerbits &= ~(o.mask << o.shift); samplerbits |= o.value << o.shift; } else { console.log('Warning, unknown sampler option: ', opts[i]); // todo bias } } pr.data.writeUnsignedInt(samplerbits); return true; }; AGALMiniAssembler.prototype.emitSource = function (pr, token, opp, natives) { // native instruction if (natives && (token in natives)) { // native call, will be replaced without modification this.cur.native[this.nativeInstructionLastRegister] = natives[token]; token = "".concat(this.cur.name === 'vertex' ? 'vn' : 'fn').concat(this.nativeInstructionLastRegister); this.nativeInstructionLastRegister--; } var reg; // g1: indexregname, g2:indexregnum, g3:select, [g4:offset], [g5:swizzle] var indexed = token.match(/vc\[(v[tcai])(\d+)\.([xyzw])([+-]\d+)?\](\.[xyzw]{1,4})?/i); if (indexed) { if (!RegMap.map[indexed[1]]) { return false; } var selindex = { x: 0, y: 1, z: 2, w: 3 }; pr.data.writeUnsignedShort(+indexed[2] | 0); pr.data.writeByte(+indexed[4] | 0); pr.data.writeUnsignedByte(this.stringToSwizzle(indexed[5])); pr.data.writeUnsignedByte(0x1); // constant reg pr.data.writeUnsignedByte(RegMap.map[indexed[1]].code); pr.data.writeUnsignedByte(selindex[indexed[3]]); pr.data.writeUnsignedByte(1 << 7); } else { // g1: regname, g2:regnum, g3:swizzle reg = token.match(/([fov]?[tpocidavsn])(\d*)(\.[xyzw]{1,4})?/i); if (!RegMap.map[reg[1]]) { return false; } pr.data.writeUnsignedShort(reg[2] | 0); pr.data.writeUnsignedByte(0); pr.data.writeUnsignedByte(this.stringToSwizzle(reg[3])); pr.data.writeUnsignedByte(RegMap.map[reg[1]].code); pr.data.writeUnsignedByte(0); pr.data.writeUnsignedByte(0); pr.data.writeUnsignedByte(0); //console.log ( " Emit source: ", em, pr.data.length ); } return true; }; AGALMiniAssembler.prototype.addHeader = function (partname, version) { if (version === void 0) { version = 1; } if (this.r[partname] == undefined) { this.r[partname] = new Part(partname, version); this.emitHeader(this.r[partname]); } else if (this.r[partname].version != version) { throw 'Multiple versions for part ' + partname; } this.cur = this.r[partname]; }; return AGALMiniAssembler; }()); export { AGALMiniAssembler };