diff --git a/src/alternativa/engine3d/materials/A3DUtils.as b/src/alternativa/engine3d/materials/A3DUtils.as index f66bb9a..37f4355 100644 --- a/src/alternativa/engine3d/materials/A3DUtils.as +++ b/src/alternativa/engine3d/materials/A3DUtils.as @@ -189,7 +189,6 @@ package alternativa.engine3d.materials { private static var samplerFilter:Vector. = Vector.(["nearest", "linear"]); private static var swizzleType:Vector. = Vector.(["x", "y", "z", "w"]); private static var twoOperandsCommands:Dictionary; - private static const O_CODE:uint = "o".charCodeAt(0); // TODO: option to turn off auto-prefixes public static function disassemble(byteCode:ByteArray):String { @@ -207,6 +206,7 @@ package alternativa.engine3d.materials { twoOperandsCommands[0x17] = true; twoOperandsCommands[0x18] = true; twoOperandsCommands[0x19] = true; + twoOperandsCommands[0x26] = true; twoOperandsCommands[0x28] = true; twoOperandsCommands[0x29] = true; twoOperandsCommands[0x2a] = true; @@ -237,7 +237,6 @@ package alternativa.engine3d.materials { } private static function getCommand(byteCode:ByteArray, programType:String):String { - var cmd:uint = byteCode.readUnsignedInt(); var command:String = CommandType.COMMAND_NAMES[cmd]; var result:String; @@ -264,16 +263,11 @@ package alternativa.engine3d.materials { } var destType:String = VariableType.TYPE_NAMES[byteCode.readUnsignedByte()].charAt(0); - if (destType.charCodeAt(0) == O_CODE) { - result = command + " " + attachProgramPrefix(destType, programType) + s + ", "; - } else { - result = command + " " + attachProgramPrefix(destType, programType) + destNumber.toString() + s + ", "; - } - + result = command + " " + attachProgramPrefix(destType, programType) + destNumber.toString() + s + ", "; result += attachProgramPrefix(getSourceVariable(byteCode, sourceSwizzleLimit), programType); if (twoOperandsCommands[cmd]) { - if (cmd == CommandType.TEX) { + if (cmd == CommandType.TEX || cmd == CommandType.TED) { result += ", " + attachProgramPrefix(getSamplerVariable(byteCode), programType); } else { result += ", " + attachProgramPrefix(getSourceVariable(byteCode, sourceSwizzleLimit), programType); @@ -281,15 +275,22 @@ package alternativa.engine3d.materials { } else { byteCode.readDouble(); } + + if (cmd == CommandType.ELS || cmd == CommandType.EIF) { + result = " " + command; + } return result; } private static function attachProgramPrefix(variable:String, programType:String):String { - var char:uint = variable.charCodeAt(0); - if (char == "o".charCodeAt(0)) + var char : uint = variable.charCodeAt(0); + if (char == "o".charCodeAt(0)) { return variable + (programType == "f" ? "c" : "p"); - else if (char != "v".charCodeAt(0)) + } else if (char == "d".charCodeAt(0)) { + return "o"+variable; + } else if (char != "v".charCodeAt(0)) { return programType + variable; + } return variable; } diff --git a/src/alternativa/engine3d/materials/compiler/CommandType.as b/src/alternativa/engine3d/materials/compiler/CommandType.as index b82571c..cd74482 100644 --- a/src/alternativa/engine3d/materials/compiler/CommandType.as +++ b/src/alternativa/engine3d/materials/compiler/CommandType.as @@ -5,113 +5,144 @@ * * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ * */ - package alternativa.engine3d.materials.compiler { -/** + import flash.utils.Dictionary; + + /** * @private */ public class CommandType { - - public static const MOV:uint = 0x00; - public static const ADD:uint = 0x01; - public static const SUB:uint = 0x02; - public static const MUL:uint = 0x03; - public static const DIV:uint = 0x04; - public static const RCP:uint = 0x05; - public static const MIN:uint = 0x06; - public static const MAX:uint = 0x07; - public static const FRC:uint = 0x08; - public static const SQT:uint = 0x09; - public static const RSQ:uint = 0x0a; - public static const POW:uint = 0x0b; - public static const LOG:uint = 0x0c; - public static const EXP:uint = 0x0d; - public static const NRM:uint = 0x0e; - public static const SIN:uint = 0x0f; - public static const COS:uint = 0x10; - public static const CRS:uint = 0x11; - public static const DP3:uint = 0x12; - public static const DP4:uint = 0x13; - public static const ABS:uint = 0x14; - public static const NEG:uint = 0x15; - public static const SAT:uint = 0x16; - public static const M33:uint = 0x17; - public static const M44:uint = 0x18; - public static const M34:uint = 0x19; - public static const KIL:uint = 0x27; - public static const TEX:uint = 0x28; - public static const SGE:uint = 0x29; - public static const SLT:uint = 0x2a; - public static const SEQ:uint = 0x2c; - public static const SNE:uint = 0x2d; - public static const DEF:uint = 0x80; - public static const CAL:uint = 0x81; - public static const COMMAND_NAMES:Vector. = Vector.( - ["mov", "add", "sub", "mul", "div", "rcp", "min", "max", "frc", "sqt", "rsq", "pow", "log", "exp", - "nrm", "sin", "cos", "crs", "dp3", "dp4", "abs", "neg", "sat", "m33", "m44", "m34","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26", "kil", "tex", "sge", "slt", "2b", "seq", "sne"] - ); + public static const MOV : uint = 0x00; - public static const commands:Object = formCommandsList(); + public static const ADD : uint = 0x01; - private static function formCommandsList():Object { - // dp3( ,) +- */ - var result:Object = {}; - result[","] = new CommandType(0, 1); - result[")"] = new CommandType(0, 1); - result["+"] = new CommandType(2, 2); - result["-"] = new CommandType(2, 2); - result["*"] = new CommandType(2, 3); - result["/"] = new CommandType(2, 3); - result["mov"] = new CommandType(1); - result["add"] = new CommandType(); - result["sub"] = new CommandType(); - result["mul"] = new CommandType(); - result["div"] = new CommandType(); - result["rcp"] = new CommandType(1); - result["min"] = new CommandType(); - result["max"] = new CommandType(); - result["frc"] = new CommandType(1); - result["sqt"] = new CommandType(1); - result["rsq"] = new CommandType(1); - result["pow"] = new CommandType(); - result["log"] = new CommandType(1); - result["exp"] = new CommandType(1); - result["nrm"] = new CommandType(1); - result["sin"] = new CommandType(1); - result["cos"] = new CommandType(1); - result["crs"] = new CommandType(); - result["dp3"] = new CommandType(); - result["dp4"] = new CommandType(); - result["abs"] = new CommandType(1); - result["neg"] = new CommandType(1); - result["sat"] = new CommandType(1); - result["m33"] = new CommandType(); - result["m44"] = new CommandType(); - result["m34"] = new CommandType(); -// result["kil"] = new CommandType(); -// result["tex"] = new CommandType(); - result["sge"] = new CommandType(); - result["slt"] = new CommandType(); - result["seq"] = new CommandType(); - result["sne"] = new CommandType(); - // setting ids - for (var s:String in result) { - result[s].id = s; - } - result["+"].id = "add"; - result["-"].id = "sub"; - result["*"].id = "mul"; - result["/"].id = "div"; - return result; - } + public static const SUB : uint = 0x02; - public var id:String; - public var priority:int; - public var numInputs:int; + public static const MUL : uint = 0x03; - public function CommandType(numInputs:int = 2, priority:int = 0) { - this.numInputs = numInputs; - this.priority = priority; - } + public static const DIV : uint = 0x04; + + public static const RCP : uint = 0x05; + + public static const MIN : uint = 0x06; + + public static const MAX : uint = 0x07; + + public static const FRC : uint = 0x08; + + public static const SQT : uint = 0x09; + + public static const RSQ : uint = 0x0a; + + public static const POW : uint = 0x0b; + + public static const LOG : uint = 0x0c; + + public static const EXP : uint = 0x0d; + + public static const NRM : uint = 0x0e; + + public static const SIN : uint = 0x0f; + + public static const COS : uint = 0x10; + + public static const CRS : uint = 0x11; + + public static const DP3 : uint = 0x12; + + public static const DP4 : uint = 0x13; + + public static const ABS : uint = 0x14; + + public static const NEG : uint = 0x15; + + public static const SAT : uint = 0x16; + + public static const M33 : uint = 0x17; + + public static const M44 : uint = 0x18; + + public static const M34 : uint = 0x19; + + public static const DDX : uint = 0x1a; + + public static const DDY : uint = 0x1b; + + public static const IFE : uint = 0x1c; + + public static const INE : uint = 0x1d; + + public static const IFG : uint = 0x1e; + + public static const IFL : uint = 0x1f; + + public static const ELS : uint = 0x20; + + public static const EIF : uint = 0x21; + + public static const TED : uint = 0x26; + + public static const KIL : uint = 0x27; + + public static const TEX : uint = 0x28; + + // set if greater equal + public static const SGE : uint = 0x29; + + // set if less than + public static const SLT : uint = 0x2a; + + // set if greater than + public static const SGN : uint = 0x2b; + + // set if equal + public static const SEQ : uint = 0x2c; + + // set if not equal + public static const SNE : uint = 0x2d; + + public static const COMMAND_NAMES : Dictionary = new Dictionary(); + COMMAND_NAMES[MOV] = "mov"; + COMMAND_NAMES[ADD] = "add"; + COMMAND_NAMES[SUB] = "sub"; + COMMAND_NAMES[MUL] = "mul"; + COMMAND_NAMES[DIV] = "div"; + COMMAND_NAMES[RCP] = "rcp"; + COMMAND_NAMES[MIN] = "min"; + COMMAND_NAMES[MAX] = "max"; + COMMAND_NAMES[FRC] = "frc"; + COMMAND_NAMES[SQT] = "sqt"; + COMMAND_NAMES[RSQ] = "rsq"; + COMMAND_NAMES[POW] = "pow"; + COMMAND_NAMES[LOG] = "log"; + COMMAND_NAMES[EXP] = "exp"; + COMMAND_NAMES[NRM] = "nrm"; + COMMAND_NAMES[SIN] = "sin"; + COMMAND_NAMES[COS] = "cos"; + COMMAND_NAMES[CRS] = "crs"; + COMMAND_NAMES[DP3] = "dp3"; + COMMAND_NAMES[DP4] = "dp4"; + COMMAND_NAMES[ABS] = "abs"; + COMMAND_NAMES[NEG] = "neg"; + COMMAND_NAMES[SAT] = "sat"; + COMMAND_NAMES[M33] = "m33"; + COMMAND_NAMES[M44] = "m44"; + COMMAND_NAMES[M34] = "m34"; + COMMAND_NAMES[DDX] = "ddx"; + COMMAND_NAMES[DDY] = "ddy"; + COMMAND_NAMES[IFE] = "ife"; + COMMAND_NAMES[INE] = "ine"; + COMMAND_NAMES[IFG] = "ifg"; + COMMAND_NAMES[IFL] = "ifl"; + COMMAND_NAMES[ELS] = "els"; + COMMAND_NAMES[EIF] = "eif"; + COMMAND_NAMES[TED] = "ted"; + COMMAND_NAMES[KIL] = "kil"; + COMMAND_NAMES[TEX] = "tex"; + COMMAND_NAMES[SGE] = "sge"; + COMMAND_NAMES[SLT] = "slt"; + COMMAND_NAMES[SGN] = "sgn"; + COMMAND_NAMES[SEQ] = "seq"; + COMMAND_NAMES[SNE] = "sne"; } } diff --git a/src/alternativa/engine3d/materials/compiler/DestinationVariable.as b/src/alternativa/engine3d/materials/compiler/DestinationVariable.as index f0fcfbd..c2bd655 100644 --- a/src/alternativa/engine3d/materials/compiler/DestinationVariable.as +++ b/src/alternativa/engine3d/materials/compiler/DestinationVariable.as @@ -16,7 +16,7 @@ package alternativa.engine3d.materials.compiler { public class DestinationVariable extends Variable { public function DestinationVariable(source:String) { - var strType:String = source.match(/[tovi]/)[0]; + var strType : String = source.match(/[tovid]/)[0]; index = parseInt(source.match(/\d+/)[0], 10); var swizzle:Array = source.match(/\.[xyzw]{1,4}/); var regmask:uint; @@ -32,7 +32,8 @@ package alternativa.engine3d.materials.compiler { regmask |= 1 << cv; } } else { - regmask = 0xf; // id swizzle or mask + regmask = 0xf; + // id swizzle or mask } lowerCode = (regmask << 16) | index; @@ -49,10 +50,14 @@ package alternativa.engine3d.materials.compiler { lowerCode |= 0x4000000; type = 4; break; - case "i": + case "d": lowerCode |= 0x6000000; type = 6; break; + case "i": + lowerCode |= 0x7000000; + type = 7; + break; default : throw new ArgumentError("Wrong destination register type, must be \"t\" or \"o\" or \"v\", var = " + source); break; diff --git a/src/alternativa/engine3d/materials/compiler/Linker.as b/src/alternativa/engine3d/materials/compiler/Linker.as index ece0216..bd16d72 100644 --- a/src/alternativa/engine3d/materials/compiler/Linker.as +++ b/src/alternativa/engine3d/materials/compiler/Linker.as @@ -51,7 +51,7 @@ package alternativa.engine3d.materials.compiler { alternativa3d var _linkedVariables:Object; // Dictionary of temporary variables at this linker. Key is a name of variable, value is a variable. - private var _localVariables:Object = {}; + private var _localVariables:Object = new Object(); // Key - procedure, value - array of strings. private var _inputParams:Dictionary = new Dictionary(); @@ -59,11 +59,11 @@ package alternativa.engine3d.materials.compiler { private var _outputParams:Dictionary = new Dictionary(); // Counters of variables by types - private var _locals:Vector. = new Vector.(6, true); + private var _locals:Vector. = new Vector.(7, true); - private var samplers:Object = {}; + private var samplers:Object = new Object(); - private var _varyings:Object = {}; + private var _varyings:Object = new Object(); /** * Creates a new Linker instance. @@ -79,10 +79,10 @@ package alternativa.engine3d.materials.compiler { */ public function clear():void { data = null; - _locals[0] = _locals[1] = _locals[2] = _locals[3] = _locals[4] = _locals[5] = 0; + _locals[0] = _locals[1] = _locals[2] = _locals[3] = _locals[4] = _locals[5] = _locals[6] = 0; procedures.length = 0; - _varyings = {}; - samplers = {}; + _varyings = new Object(); + samplers = new Object(); commandsCount = 0; slotsCount = 0; @@ -214,11 +214,11 @@ package alternativa.engine3d.materials.compiler { /** * Linking of procedures to one shader. */ - public function link():void { + public function link(version:uint = 1):void { if (data != null) return; var v:Variable; - var variables:Object = _linkedVariables = {}; + var variables:Object = _linkedVariables = new Object(); var p:Procedure; var i:int, j:int; var nv:Variable; @@ -232,8 +232,11 @@ package alternativa.engine3d.materials.compiler { data = new ByteArray(); data.endian = Endian.LITTLE_ENDIAN; data.writeByte(0xa0); - data.writeUnsignedInt(0x1); // AGAL version, big endian, bit pattern will be 0x01000000 - data.writeByte(0xa1); // tag program id + // tag version + data.writeUnsignedInt(version); + // AGAL version, big endian, bit pattern will be 0x01000000 + data.writeByte(0xa1); + // tag program id data.writeByte((type == Context3DProgramType.FRAGMENT) ? 1 : 0); // vertex or fragment commandsCount = 0; @@ -245,17 +248,18 @@ package alternativa.engine3d.materials.compiler { _locals[3] = 0; _locals[4] = 0; _locals[5] = 0; + _locals[6] = 0; // First iteration - collecting of variables. for each (p in procedures) { - var iLength:int = p.variablesUsages.length; _locals[1] += p.reservedConstants; + var iLength:int = p.variablesUsages.length; for (i = 0; i < iLength; i++) { var vector:Vector. = p.variablesUsages[i]; var jLength:int = vector.length; for (j = 0; j < jLength; j++) { v = vector[j]; if (v == null || v.name == null) continue; - if (v.name == null && i != 2 && i != 6 && i != 3) { + if (v.name == null && i != 2 && i != 6 && i != 3 && i != 7) { throw new Error("Linkage error: Noname variable. Procedure = " + p.name + ", type = " + i.toString() + ", index = " + j.toString()); } nv = variables[v.name] = new Variable(); @@ -284,8 +288,8 @@ package alternativa.engine3d.materials.compiler { if (v == null) { throw new Error("Input parameter not set. paramName = " + param); } - if (p.variablesUsages[6].length > j) { - var inParam:Variable = p.variablesUsages[6][j]; + if (p.variablesUsages[7].length > j) { + var inParam:Variable = p.variablesUsages[7][j]; if (inParam == null) { throw new Error("Input parameter set, but not exist in code. paramName = " + param + ", register = i" + j.toString()); } @@ -350,6 +354,7 @@ package alternativa.engine3d.materials.compiler { slotsCount += p.slotsCount; } } + private function resolveVariablesUsages(code:ByteArray, variables:Object, variableUsages:Vector., type:uint, offset:int):void { for (var j:int = 0; j < variableUsages.length; j++) { var vUsage:Variable = variableUsages[j]; diff --git a/src/alternativa/engine3d/materials/compiler/Procedure.as b/src/alternativa/engine3d/materials/compiler/Procedure.as index aa64880..acb3a6b 100644 --- a/src/alternativa/engine3d/materials/compiler/Procedure.as +++ b/src/alternativa/engine3d/materials/compiler/Procedure.as @@ -5,33 +5,30 @@ * * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ * */ - package alternativa.engine3d.materials.compiler { + import alternativa.engine3d.alternativa3d; -import alternativa.engine3d.alternativa3d; - -import flash.display3D.Context3DProgramType; -import flash.utils.ByteArray; -import flash.utils.Endian; - -use namespace alternativa3d; + import flash.display3D.Context3DProgramType; + import flash.utils.ByteArray; + import flash.utils.Endian; + use namespace alternativa3d; /** * @private * Shader procedure */ public class Procedure { - // Name of procedure - public var name:String; - /** - * @private - */ - alternativa3d static const crc32Table:Vector. = createCRC32Table(); + public var name : String; - private static function createCRC32Table():Vector. { - var crc_table:Vector. = new Vector.(256); - var crc:uint, i:int, j:int; + /** + * @private + */ + alternativa3d static const crc32Table : Vector. = createCRC32Table(); + + private static function createCRC32Table() : Vector. { + var crc_table : Vector. = new Vector.(256); + var crc : uint, i : int, j : int; for (i = 0; i < 256; i++) { crc = i; for (j = 0; j < 8; j++) @@ -41,37 +38,40 @@ use namespace alternativa3d; } return crc_table; } - /** - * @private - */ - alternativa3d var crc32:uint = 0; + + /** + * @private + */ + alternativa3d var crc32 : uint = 0; /** * Code of procedure. */ - public var byteCode:ByteArray = new ByteArray(); - public var variablesUsages:Vector.> = new Vector.>(); + public var byteCode : ByteArray = new ByteArray(); + + public var variablesUsages : Vector.> = new Vector.>(); /** * Number of instruction slots in a procedure. */ - public var slotsCount:int = 0; + public var slotsCount : int = 0; /** * Number of strings in a procedure. */ - public var commandsCount:int = 0; - /** - * @private - */ - alternativa3d var reservedConstants:uint = 0; + public var commandsCount : int = 0; + + /** + * @private + */ + alternativa3d var reservedConstants : uint = 0; /** * Creates a new Procedure instance. * * @param array Array of AGAL strings */ - public function Procedure(array:Array = null, name:String = null) { + public function Procedure(array : Array = null, name : String = null) { byteCode.endian = Endian.LITTLE_ENDIAN; this.name = name; if (array != null) { @@ -79,20 +79,23 @@ use namespace alternativa3d; } } - public function getByteCode(type:String):ByteArray { - var result:ByteArray = new ByteArray(); + public function getByteCode(type : String, version:uint = 1) : ByteArray { + var result : ByteArray = new ByteArray(); result.endian = Endian.LITTLE_ENDIAN; result.writeByte(0xa0); - result.writeUnsignedInt(0x1); // AGAL version, big endian, bit pattern will be 0x01000000 - result.writeByte(0xa1); // tag program id - result.writeByte((type == Context3DProgramType.FRAGMENT) ? 1 : 0); // vertex or fragment + result.writeUnsignedInt(version); + // AGAL version, big endian, bit pattern will be 0x01000000 + result.writeByte(0xa1); + // tag program id + result.writeByte((type == Context3DProgramType.FRAGMENT) ? 1 : 0); + // vertex or fragment result.writeBytes(byteCode); return result; } - private function addVariableUsage(v:Variable):void { - var vars:Vector. = variablesUsages[v.type]; - var index:int = v.index; + private function addVariableUsage(v : Variable) : void { + var vars : Vector. = variablesUsages[v.type]; + var index : int = v.index; if (index >= vars.length) { vars.length = index + 1; } else { @@ -111,8 +114,8 @@ use namespace alternativa3d; * * @see VariableType */ - public function assignVariableName(type:uint, index:uint, name:String, size:uint = 1):void { - var v:Variable = variablesUsages[type][index]; + public function assignVariableName(type : uint, index : uint, name : String, size : uint = 1) : void { + var v : Variable = variablesUsages[type][index]; while (v != null) { v.size = size; v.name = name; @@ -123,45 +126,39 @@ use namespace alternativa3d; /** * Compiles shader from the string. */ - public function compileFromString(source:String):void { - var commands:Array = source.split("\n"); + public function compileFromString(source : String) : void { + var commands : Array = source.split("\n"); compileFromArray(commands); } /** * Compiles shader from the array of strings. */ - public function compileFromArray(source:Array):void { - for (var i:int = 0; i < 7; i++) { + public function compileFromArray(source : Array) : void { + for (var i : int = 0; i < 8; i++) { variablesUsages[i] = new Vector.(); } byteCode.length = 0; commandsCount = 0; slotsCount = 0; - const decPattern:RegExp = /# *[acvs]\d{1,3} *= *[a-zA-Z0-9_]*/i; - const rpnPattern:RegExp = /[tivo]\d+(\.[xyzw]{1,4})? *=/; - - var declarationStrings:Vector. = new Vector.(); - var count:int = source.length; + const decPattern : RegExp = /# *[acvs]\d{1,3} *= *[a-zA-Z0-9_]*/i; + var declarationStrings : Vector. = new Vector.(); + var count : int = source.length; for (i = 0; i < count; i++) { - var cmd:String = source[i]; - var declaration:Array = cmd.match(decPattern); + var cmd : String = source[i]; + var declaration : Array = cmd.match(decPattern); if (declaration != null && declaration.length > 0) { declarationStrings.push(declaration[0]); } else { - if (rpnPattern.test(cmd)) { - writeRPNExpression(cmd); - } else { - writeAGALExpression(cmd); - } + writeAGALExpression(cmd); } } for (i = 0,count = declarationStrings.length; i < count; i++) { - var decArray:Array = declarationStrings[i].split("="); - var regType:String = decArray[0].match(/[acvs]/i); - var varIndex:int = int(decArray[0].match(/\d{1,3}/i)); - var varName:String = decArray[1].match(/[a-zA-Z0-9]*/i); + var decArray : Array = declarationStrings[i].split("="); + var regType : String = decArray[0].match(/[acvs]/i); + var varIndex : int = int(decArray[0].match(/\d{1,3}/i)); + var varName : String = decArray[1].match(/[a-zA-Z0-9]*/i); switch (regType.toLowerCase()) { case "a": assignVariableName(VariableType.ATTRIBUTE, varIndex, varName); @@ -180,256 +177,70 @@ use namespace alternativa3d; crc32 = createCRC32(byteCode); } - public function assignConstantsArray(registersCount:uint = 1):void { + public function assignConstantsArray(registersCount : uint = 1) : void { reservedConstants = registersCount; } - private function writeRPNExpression(source:String):void { - // 1) Output in the same variable - // 2) Check for errors and complex expressions - // 3) Compile through AGAL - // 4) Only +-/* operators and one assignment = - // 5) output mask supported for : (.x, .y, .z, .w, .xy, .xyz, .xyzw) - // 6) swizzle supported - // 7) swizzle handled like in AGAL compiler + private const agalParser : RegExp = /[A-Za-z]+(((\[.+\])|(\d+))(\.[xyzw]{1,4})?(\ *\<.*>)?)?/g; - // TODO: handle swizzle smartly (.zw -> .zwzw) - // TODO: implement operators inputs size check - // TODO: implement operators auto output size - // TODO: implement operators output swizzle - // TODO: minimize output temporaries count (sort operators by priority) - // TODO: write to ByteArray directly - // TODO: implement negate unary operator (-x) - // TODO: implement tex (tex2D, texCube) in any form - // TODO: implement groups and complex expressions - // TODO: support additional output masks - // TODO: optimize variables components usage (sort by swizzles length) - // TODO: optimize - // TODO: implement alternate assignments - - var commentIndex:int = source.indexOf("//"); - if (commentIndex >= 0) { - source = source.substr(0, commentIndex); - } - var operands:Array = source.match(/[a-z]+(((\[.+\])|(\d+))(\.[xyzw]{1,4})?)?|[+\-*\/=(),]/g); - var numOperands:int = operands.length; - if (numOperands < 3) return; - if (operands[1] != "=") { - throw new Error("Syntax error"); - } - var i:int; - var output:String = operands[0]; - var maskIndex:int = output.lastIndexOf("."); - var outputMaskLen:int = (maskIndex >= 0) ? output.length - maskIndex - 1 : 4; - if (outputMaskLen != 1 && maskIndex >= 0) { - // check mask - const X_CHAR_CODE:int = "x".charCodeAt(0); - for (i = 0; i < outputMaskLen; i++) { - var code:int = (i == 3) ? X_CHAR_CODE -1 : X_CHAR_CODE + i; // .w - if (output.charCodeAt(maskIndex + i + 1) != code) { - throw new Error("Output mask with such type not supported " + output + "."); - } - } - } - var outputVar:String = (maskIndex >= 0) ? output.substr(0, maskIndex) : output; - if (outputMaskLen == 4) output = outputVar; - - var operators:Vector. = new Vector.(); - var variables:Vector. = new Vector.(); - function getPriority(command:CommandType):int { - return command.priority; - } - function getSwizzleLen(value:String):uint { - var i:int = value.lastIndexOf("."); - return (i < 0 ? 4 : value.length - i - 1); - } - function writeCommand(command:CommandType, numInputs:int, operandIndex:int, isLastOperator:Boolean):void { - if (numInputs != command.numInputs) { - throw new Error("Syntax error. Operator " + command.id + " inputs count wrong. Expected " + command.numInputs + "."); - } - var b:String = (numInputs > 1) ? variables.pop() : null; - var a:String = variables.pop(); - if (a == null || (numInputs > 1 && b == null)) throw new Error("Syntax error. Variable expected after " + command + "."); - // Check can we use output for writing - var i:int; - for (i = 0; i < variables.length; i++) { - if (variables[i].indexOf(output) >= 0) { - // output already used - throw new Error("Expression is too complex. Groups unsupported."); - } - } - for (i = operandIndex + 1; i < numOperands; i++) { - if (operands[i].indexOf(output) >= 0) { - // output is used as source - throw new Error("Expression is too complex. Output used as source."); - } - } - var maxSwizzle:uint; - if (numInputs <= 1) { - maxSwizzle = getSwizzleLen(a); - } else { - var aSwizzleLen:uint = getSwizzleLen(a); - var bSwizzleLen:uint = getSwizzleLen(b); - if (aSwizzleLen != bSwizzleLen && aSwizzleLen != 1 && bSwizzleLen != 1) { - throw new Error("Variables size mistmatch " + a + " and " + b + "."); - } - maxSwizzle = (aSwizzleLen > bSwizzleLen) ? aSwizzleLen : bSwizzleLen; - } - if (maxSwizzle > outputMaskLen || (isLastOperator && maxSwizzle != outputMaskLen && maxSwizzle != 1)) { - throw new Error("Expression differs in size with output " + output + "."); - } - var out:String = output; - if (!isLastOperator && maxSwizzle != outputMaskLen) { - // TODO: use same components like in variables (.zw + .zw -> .zw) - if (maxSwizzle == 1) { - out = outputVar + ".x"; - } else if (maxSwizzle == 2) { - out = outputVar + ".xy"; - } else if (maxSwizzle == 3) { - out = outputVar + ".xyz"; - } - } - if (numInputs > 1) { - writeAGALExpression(command.id + " " + out + " " + a + " " + b); - } else { - writeAGALExpression(command.id + " " + out + " " + a); - } - variables.push(out); - } - var operand:String; - if (numOperands == 3) { - operand = operands[2]; - if (getSwizzleLen(operand) != outputMaskLen && getSwizzleLen(operand) != 1) { - throw new Error("Expression differs in size with output " + output + "."); - } - writeAGALExpression("mov " + output + " " + operand); - } - var command:CommandType; - var wasVariable:Boolean = false; - for (i = 2; i < numOperands; i++) { - operand = operands[i]; - switch (operand) { - case "+": - case "-": - case "*": - case "/": - if (!wasVariable) throw new Error("Syntax error. Variable expected before " + operand + "."); - command = CommandType.commands[operand]; - // process operators from stack while their priority is higher or equal - while (operators.length > 0 && getPriority(operators[operators.length - 1]) >= getPriority(command)) { - writeCommand(operators.pop(), 2, i, false); - } - operators.push(command); - wasVariable = false; - break; - case ")": - case ",": - if (!wasVariable) throw new Error("Syntax error. Variable expected before " + operand + "."); - command = CommandType.commands[operand]; - // process all commands before until comma or left bracket - while (operators.length > 0 && getPriority(operators[operators.length - 1]) > getPriority(command)) { - writeCommand(operators.pop(), 2, i, false); - } - if (operand == ",") { - operators.push(command); - wasVariable = false; - } else { - // count all commas until function - var numParams:int = 1; - while ((command = operators.pop()) != null && command.priority != 0) { - numParams++; - } - writeCommand(command, numParams, i, i == numOperands - 1); - wasVariable = true; - } - break; - default: - if (wasVariable) throw new Error("Syntax error. Operator expected before " + operand + "."); - - command = CommandType.commands[operand]; - if (command != null) { - // is command - // test bracket - if (i + 1 >= numOperands || operands[i + 1] != "(") { - throw new Error("Syntax error. Expected bracket after " + operand + "."); - } - operators.push(command); - i++; // skip bracket -// wasVariable = false; - } else { - // is variable - variables.push(operand); - wasVariable = true; - } - break; - } - } - // process remained operators - while ((command = operators.pop()) != null) { - writeCommand(command, 2, numOperands, operators.length == 0); - } - if (variables.length > 1) throw new Error("Syntax error. Unknown novel error."); - } - - private const agalParser:RegExp = /[A-Za-z]+(((\[.+\])|(\d+))(\.[xyzw]{1,4})?(\ *\<.*>)?)?/g; - private function writeAGALExpression(source:String):void { - var commentIndex:int = source.indexOf("//"); + private function writeAGALExpression(source : String) : void { + var commentIndex : int = source.indexOf("//"); if (commentIndex >= 0) { source = source.substr(0, commentIndex); } // Errors: - //1) Merged commands - //2) Syntax errors - //-- incorrect number of operands - //-- unknown commands - //-- unknown registers - //-- unknown constructions - //3) Using of unwritable registers - //-- in vertex shader (va0, c0, s0); - //-- in fragment shader (v0, va0, c0, s0); - //4) Using of unreadable registers - //-- in vertex shader (v0, s0); - //-- in fragment shader (va0); - //5) Deny write into the input registers - //6) Mismatch the size of types of registers - //7) Relative addressing in the fragment shader is not possible - //-- You can not use it for recording - //-- Offset is out of range [0..255] - //8) Flow errors - //-- unused variable - //-- using of uninitialized variable - //-- using of partially uninitialized variable - //-- function is not return value - //9) Restrictions - //-- too many commands - //-- too many constants - //-- too many textures - //-- too many temporary variables - //-- too many interpolated values + // 1) Merged commands + // 2) Syntax errors + // -- incorrect number of operands + // -- unknown commands + // -- unknown registers + // -- unknown constructions + // 3) Using of unwritable registers + // -- in vertex shader (va0, c0, s0); + // -- in fragment shader (v0, va0, c0, s0); + // 4) Using of unreadable registers + // -- in vertex shader (v0, s0); + // -- in fragment shader (va0); + // 5) Deny write into the input registers + // 6) Mismatch the size of types of registers + // 7) Relative addressing in the fragment shader is not possible + // -- You can not use it for recording + // -- Offset is out of range [0..255] + // 8) Flow errors + // -- unused variable + // -- using of uninitialized variable + // -- using of partially uninitialized variable + // -- function is not return value + // 9) Restrictions + // -- too many commands + // -- too many constants + // -- too many textures + // -- too many temporary variables + // -- too many interpolated values // You can not use kil in fragment shader - var operands:Array = source.match(agalParser); + var operands : Array = source.match(agalParser); // It is possible not use the input parameter. It is optimization of the linker // Determine the size of constant - if (operands.length < 2) { - return; - } - var opCode:String = operands[0]; - var destination:Variable; - var source1:SourceVariable; - var source2:Variable; - if (opCode == "kil") { + var opCode : String = operands[0]; + var destination : DestinationVariable; + var source1 : SourceVariable; + var source2 : Variable; + if (opCode == "kil" || opCode == "ife" || opCode == "ine" || opCode == "ifg" || opCode == "ifl") {//no dist source1 = new SourceVariable(operands[1]); + addVariableUsage(source1); + } else if (opCode == "els" || opCode == "eif") { // no dist no sources + source1 = null; + source2 = null; } else { destination = new DestinationVariable(operands[1]); - source1 = new SourceVariable(operands[2]); addVariableUsage(destination); + source1 = new SourceVariable(operands[2]); + addVariableUsage(source1); } - addVariableUsage(source1); - - var type:uint; + var type : uint; switch (opCode) { case "mov": type = CommandType.MOV; @@ -561,6 +372,52 @@ use namespace alternativa3d; addVariableUsage(source2); slotsCount += 3; break; + case "ddx": + type = CommandType.DDX; + slotsCount += 2; + break; + case "ddy": + type = CommandType.DDY; + slotsCount += 2; + break; + case "ife": + type = CommandType.IFE; + source2 = new SourceVariable(operands[2]); + addVariableUsage(source2); + slotsCount++; + break; + case "ine": + type = CommandType.INE; + source2 = new SourceVariable(operands[2]); + addVariableUsage(source2); + slotsCount++; + break; + case "ifg": + type = CommandType.IFG; + source2 = new SourceVariable(operands[2]); + addVariableUsage(source2); + slotsCount++; + break; + case "ifl": + type = CommandType.IFL; + source2 = new SourceVariable(operands[2]); + addVariableUsage(source2); + slotsCount++; + break; + case "els": + type = CommandType.ELS; + slotsCount++; + break; + case "eif": + type = CommandType.EIF; + slotsCount++; + break; + case "ted": + type = CommandType.TED; + source2 = new SamplerVariable(operands[3]); + addVariableUsage(source2); + slotsCount++; + break; case "kil": type = CommandType.KIL; slotsCount++; @@ -583,6 +440,10 @@ use namespace alternativa3d; addVariableUsage(source2); slotsCount++; break; + case "sgn": + type = CommandType.SGN; + slotsCount++; + break; case "seq": type = CommandType.SEQ; source2 = new SourceVariable(operands[3]); @@ -599,6 +460,8 @@ use namespace alternativa3d; // TODO: throw error - unknown command break; } + + // Fill of byteCode of command byteCode.writeUnsignedInt(type); if (destination != null) { @@ -607,16 +470,20 @@ use namespace alternativa3d; } else { byteCode.writeUnsignedInt(0); } - source1.position = byteCode.position; - if (source1.relative != null) { - addVariableUsage(source1.relative); - source1.relative.position = byteCode.position; + if (source1 != null) { + source1.position = byteCode.position; + if (source1.relative != null) { + addVariableUsage(source1.relative); + source1.relative.position = byteCode.position; + } + byteCode.writeUnsignedInt(source1.lowerCode); + byteCode.writeUnsignedInt(source1.upperCode); + } else { + byteCode.position = (byteCode.length += 8); } - byteCode.writeUnsignedInt(source1.lowerCode); - byteCode.writeUnsignedInt(source1.upperCode); if (source2 != null) { source2.position = byteCode.position; - var s2v:SourceVariable = source2 as SourceVariable; + var s2v : SourceVariable = source2 as SourceVariable; if (s2v != null && s2v.relative != null) { addVariableUsage(s2v.relative); s2v.relative.position = s2v.position; @@ -632,16 +499,16 @@ use namespace alternativa3d; /** * Creates and returns an instance of procedure from array of strings. */ - public static function compileFromArray(source:Array, name:String = null):Procedure { - var proc:Procedure = new Procedure(source, name); + public static function compileFromArray(source : Array, name : String = null) : Procedure { + var proc : Procedure = new Procedure(source, name); return proc; } /** * Creates and returns an instance of procedure from string. */ - public static function compileFromString(source:String, name:String = null):Procedure { - var proc:Procedure = new Procedure(null, name); + public static function compileFromString(source : String, name : String = null) : Procedure { + var proc : Procedure = new Procedure(null, name); proc.compileFromString(source); return proc; } @@ -649,8 +516,8 @@ use namespace alternativa3d; /** * Create an instance of procedure. */ - public function newInstance():Procedure { - var res:Procedure = new Procedure(); + public function newInstance() : Procedure { + var res : Procedure = new Procedure(); res.byteCode = this.byteCode; res.variablesUsages = this.variablesUsages; res.slotsCount = this.slotsCount; @@ -659,19 +526,19 @@ use namespace alternativa3d; res.name = name; return res; } - /** - * @private - */ - alternativa3d static function createCRC32(byteCode:ByteArray):uint { + + /** + * @private + */ + alternativa3d static function createCRC32(byteCode : ByteArray) : uint { byteCode.position = 0; - var len:uint = byteCode.length; - var crc:uint = 0xFFFFFFFF; + var len : uint = byteCode.length; + var crc : uint = 0xFFFFFFFF; while (len--) { - var byte:int = byteCode.readByte(); + var byte : int = byteCode.readByte(); crc = crc32Table[(crc ^ byte) & 0xFF] ^ (crc >> 8); } return crc ^ 0xFFFFFFFF; } } - } diff --git a/src/alternativa/engine3d/materials/compiler/SourceVariable.as b/src/alternativa/engine3d/materials/compiler/SourceVariable.as index 2b710f6..5b9c829 100644 --- a/src/alternativa/engine3d/materials/compiler/SourceVariable.as +++ b/src/alternativa/engine3d/materials/compiler/SourceVariable.as @@ -5,58 +5,57 @@ * * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ * */ - package alternativa.engine3d.materials.compiler { - import flash.utils.ByteArray; /** * @private */ public class SourceVariable extends Variable { + public var relative : RelativeVariable; - public var relative:RelativeVariable; - - override public function get size():uint { - if(relative){ + override public function get size() : uint { + if (relative) { return 0; } return super.size; } - public function SourceVariable(source:String) { - var strType:String = String(source.match(/[catsoiv]/g)[0]); - - var regmask:uint; - - var relreg:Array = source.match( /\[.*\]/g ); - var isRel:Boolean = relreg.length > 0; - if(isRel){ + public function SourceVariable(source : String) { + var strType : String = String(source.match(/[catsoivd]/g)[0]); + + var regmask : uint; + + var relreg : Array = source.match(/\[.*\]/g); + var isRel : Boolean = relreg.length > 0; + if (isRel) { source = source.replace(relreg[0], "0"); } else { index = parseInt(source.match(/\d+/g)[0], 10); } - var swizzle:Array = source.match(/\.[xyzw]{1,4}/); + var swizzle : Array = source.match(/\.[xyzw]{1,4}/); - var maskmatch:String = swizzle ? swizzle[0] : null; + var maskmatch : String = swizzle ? swizzle[0] : null; if (maskmatch) { regmask = 0; - var cv:int; - var maskLength:uint = maskmatch.length; - for (var i:int = 1; i < maskLength; i++) { + var cv : int; + var maskLength : uint = maskmatch.length; + for (var i : int = 1; i < maskLength; i++) { cv = maskmatch.charCodeAt(i) - X_CHAR_CODE; if (cv == -1) cv = 3; regmask |= cv << ( ( i - 1 ) << 1 ); } for ( ; i <= 4; i++ ) - regmask |= cv << ( ( i - 1 ) << 1 ); // repeat last + regmask |= cv << ( ( i - 1 ) << 1 ); + // repeat last } else { - regmask = 0xe4; // id swizzle or mask + regmask = 0xe4; + // id swizzle or mask } lowerCode = (regmask << 24) | index; - switch(strType){ + switch(strType) { case "a": type = VariableType.ATTRIBUTE; break; @@ -75,8 +74,11 @@ package alternativa.engine3d.materials.compiler { case "i": type = VariableType.INPUT; break; + case "d": + type = VariableType.DEPTH; + break; default : - throw new ArgumentError('Wrong source register type, must be "a" or "c" or "t" or "o" or "v" or "i", var = ' + source); + throw new ArgumentError('Wrong source register type, must be "a" or "c" or "t" or "o" or "v" or "i" or "d", var = ' + source); break; } upperCode = type; @@ -87,8 +89,8 @@ package alternativa.engine3d.materials.compiler { isRelative = true; } } - - override public function writeToByteArray(byteCode:ByteArray, newIndex:int, newType:int, offset:int = 0):void { + + override public function writeToByteArray(byteCode : ByteArray, newIndex : int, newType : int, offset : int = 0) : void { if (relative == null) { super.writeToByteArray(byteCode, newIndex, newType, offset); } else { @@ -97,6 +99,5 @@ package alternativa.engine3d.materials.compiler { byteCode.position = position + offset + 4; byteCode.writeByte(newType); } - } } diff --git a/src/alternativa/engine3d/materials/compiler/VariableType.as b/src/alternativa/engine3d/materials/compiler/VariableType.as index d69d819..98b7ac2 100644 --- a/src/alternativa/engine3d/materials/compiler/VariableType.as +++ b/src/alternativa/engine3d/materials/compiler/VariableType.as @@ -38,13 +38,17 @@ package alternativa.engine3d.materials.compiler { * Texture. */ public static const SAMPLER:uint = 5; + /** + * Depth variable. + */ + public static const DEPTH:uint = 6; /** * Input variable. */ - public static const INPUT:uint = 6; + public static const INPUT:uint = 7; public static const TYPE_NAMES:Vector. = Vector.( - ["attribute", "constant", "temporary", "output", "varying", "sampler", "input"] + ["attribute", "constant", "temporary", "output", "varying", "sampler", "depth", "input"] ); public function VariableType() { }