static public FormatElement[] ParseFormatString(string formatString, bool tightPacking, out string errors) { var elems = new List<FormatElement>(); var formatReader = new StringReader(formatString); // regex doesn't account for trailing or preceeding whitespace, or comments var regExpr = @"^(row_major\s+)?" + // row_major matrix @"(" + @"uintten|unormten" + @"|unormh|unormb" + @"|snormh|snormb" + @"|bool" + // bool is stored as 4-byte int in hlsl @"|byte|short|int" + // signed ints @"|ubyte|ushort|uint" + // unsigned ints @"|xbyte|xshort|xint" + // hex ints @"|half|float|double" + // float types @")" + @"([1-9])?" + // might be a vector @"(x[1-9])?" + // or a matrix @"(\s+[A-Za-z_][A-Za-z0-9_]*)?" + // get identifier name @"(\[[0-9]+\])?" + // optional array dimension @"(\s*:\s*[A-Za-z_][A-Za-z0-9_]*)?" + // optional semantic @"$"; Regex regParser = new Regex(regExpr, RegexOptions.Compiled); bool success = true; errors = ""; var text = formatReader.ReadToEnd(); text = text.Replace("{", "").Replace("}", ""); Regex c_comments = new Regex(@"/\*[^*]*\*+(?:[^*/][^*]*\*+)*/", RegexOptions.Compiled); text = c_comments.Replace(text, ""); Regex cpp_comments = new Regex(@"//.*", RegexOptions.Compiled); text = cpp_comments.Replace(text, ""); uint offset = 0; // get each line and parse it to determine the format the user wanted foreach (var l in text.Split(';')) { var line = l; line = line.Trim(); if (line == "") continue; var match = regParser.Match(line); if (!match.Success) { errors = "Couldn't parse line:\n" + line; success = false; break; } var basetype = match.Groups[2].Value; bool row_major = match.Groups[1].Success; var vectorDim = match.Groups[3].Success ? match.Groups[3].Value : "1"; var matrixDim = match.Groups[4].Success ? match.Groups[4].Value.Substring(1) : "1"; var name = match.Groups[5].Success ? match.Groups[5].Value.Trim() : "data"; var arrayDim = match.Groups[6].Success ? match.Groups[6].Value.Trim() : "[1]"; arrayDim = arrayDim.Substring(1, arrayDim.Length - 2); if (match.Groups[4].Success) { var a = vectorDim; vectorDim = matrixDim; matrixDim = a; } ResourceFormat fmt = new ResourceFormat(FormatComponentType.None, 0, 0); bool hex = false; FormatComponentType type = FormatComponentType.Float; uint count = 0; uint arrayCount = 1; uint matrixCount = 0; uint width = 0; // calculate format { if (!uint.TryParse(vectorDim, out count)) { errors = "Invalid vector dimension on line:\n" + line; success = false; break; } if (!uint.TryParse(arrayDim, out arrayCount)) { arrayCount = 1; } arrayCount = Math.Max(0, arrayCount); if (!uint.TryParse(matrixDim, out matrixCount)) { errors = "Invalid matrix second dimension on line:\n" + line; success = false; break; } if (basetype == "bool") { type = FormatComponentType.UInt; width = 4; } else if (basetype == "byte") { type = FormatComponentType.SInt; width = 1; } else if (basetype == "ubyte" || basetype == "xbyte") { type = FormatComponentType.UInt; width = 1; } else if (basetype == "short") { type = FormatComponentType.SInt; width = 2; } else if (basetype == "ushort" || basetype == "xshort") { type = FormatComponentType.UInt; width = 2; } else if (basetype == "int") { type = FormatComponentType.SInt; width = 4; } else if (basetype == "uint" || basetype == "xint") { type = FormatComponentType.UInt; width = 4; } else if (basetype == "half") { type = FormatComponentType.Float; width = 2; } else if (basetype == "float") { type = FormatComponentType.Float; width = 4; } else if (basetype == "double") { type = FormatComponentType.Float; width = 8; } else if (basetype == "unormh") { type = FormatComponentType.UNorm; width = 2; } else if (basetype == "unormb") { type = FormatComponentType.UNorm; width = 1; } else if (basetype == "snormh") { type = FormatComponentType.SNorm; width = 2; } else if (basetype == "snormb") { type = FormatComponentType.SNorm; width = 1; } else if (basetype == "uintten") { fmt = new ResourceFormat(FormatComponentType.UInt, 4 * count, 1); fmt.special = true; fmt.specialFormat = SpecialFormat.R10G10B10A2; } else if (basetype == "unormten") { fmt = new ResourceFormat(FormatComponentType.UNorm, 4 * count, 1); fmt.special = true; fmt.specialFormat = SpecialFormat.R10G10B10A2; } else { errors = "Unrecognised basic type on line:\n" + line; success = false; break; } } if (basetype == "xint" || basetype == "xshort" || basetype == "xbyte") hex = true; if (fmt.compType == FormatComponentType.None) fmt = new ResourceFormat(type, count, width); if (arrayCount == 1) { FormatElement elem = new FormatElement(name, 0, offset, false, row_major, matrixCount, fmt, hex); uint advance = elem.ByteSize; if (!tightPacking) { // cbuffer packing always works in floats advance = (advance + 3U) & (~3U); // cbuffer packing doesn't allow elements to cross float4 boundaries, nudge up if this was the case if (offset / 16 != (offset + elem.ByteSize - 1) / 16) { elem.offset = offset = (offset + 0xFU) & (~0xFU); } } elems.Add(elem); offset += advance; } else { // when cbuffer packing, arrays are always aligned at float4 boundary if (!tightPacking) { if (offset % 16 != 0) { offset = (offset + 0xFU) & (~0xFU); } } for (uint a = 0; a < arrayCount; a++) { FormatElement elem = new FormatElement(String.Format("{0}[{1}]", name, a), 0, offset, false, row_major, matrixCount, fmt, hex); elems.Add(elem); uint advance = elem.ByteSize; // cbuffer packing each array element is always float4 aligned if (!tightPacking) { advance = (advance + 0xFU) & (~0xFU); } offset += advance; } } } if (!success || elems.Count == 0) { elems.Clear(); var fmt = new ResourceFormat(FormatComponentType.UInt, 4, 4); elems.Add(new FormatElement("data", 0, 0, false, false, 1, fmt, true)); } return elems.ToArray(); }
static public FormatElement[] ParseFormatString(string formatString, UInt64 maxLen, bool tightPacking, out string errors) { var elems = new List <FormatElement>(); var formatReader = new StringReader(formatString); // regex doesn't account for trailing or preceeding whitespace, or comments var regExpr = @"^(row_major\s+)?" + // row_major matrix @"(" + @"uintten|unormten" + @"|unormh|unormb" + @"|snormh|snormb" + @"|bool" + // bool is stored as 4-byte int @"|byte|short|int" + // signed ints @"|ubyte|ushort|uint" + // unsigned ints @"|xbyte|xshort|xint" + // hex ints @"|half|float|double" + // float types @"|vec|uvec|ivec" + // OpenGL vector types @"|mat|umat|imat" + // OpenGL matrix types @")" + @"([1-9])?" + // might be a vector @"(x[1-9])?" + // or a matrix @"(\s+[A-Za-z_][A-Za-z0-9_]*)?" + // get identifier name @"(\[[0-9]+\])?" + // optional array dimension @"(\s*:\s*[A-Za-z_][A-Za-z0-9_]*)?" + // optional semantic @"$"; Regex regParser = new Regex(regExpr, RegexOptions.Compiled); bool success = true; errors = ""; var text = formatReader.ReadToEnd(); text = text.Replace("{", "").Replace("}", ""); Regex c_comments = new Regex(@"/\*[^*]*\*+(?:[^*/][^*]*\*+)*/", RegexOptions.Compiled); text = c_comments.Replace(text, ""); Regex cpp_comments = new Regex(@"//.*", RegexOptions.Compiled); text = cpp_comments.Replace(text, ""); uint offset = 0; // get each line and parse it to determine the format the user wanted foreach (var l in text.Split(';')) { var line = l; line = line.Trim(); if (line.Length == 0) { continue; } var match = regParser.Match(line); if (!match.Success) { errors = "Couldn't parse line:\n" + line; success = false; break; } var basetype = match.Groups[2].Value; bool row_major = match.Groups[1].Success; var vectorDim = match.Groups[3].Success ? match.Groups[3].Value : "1"; var matrixDim = match.Groups[4].Success ? match.Groups[4].Value.Substring(1) : "1"; var name = match.Groups[5].Success ? match.Groups[5].Value.Trim() : "data"; var arrayDim = match.Groups[6].Success ? match.Groups[6].Value.Trim() : "[1]"; arrayDim = arrayDim.Substring(1, arrayDim.Length - 2); if (match.Groups[4].Success) { var a = vectorDim; vectorDim = matrixDim; matrixDim = a; } ResourceFormat fmt = new ResourceFormat(FormatComponentType.None, 0, 0); bool hex = false; FormatComponentType type = FormatComponentType.Float; uint count = 0; uint arrayCount = 1; uint matrixCount = 0; uint width = 0; // check for square matrix declarations like 'mat4' and 'mat3' if (basetype == "mat" && !match.Groups[4].Success) { matrixDim = vectorDim; } // calculate format { if (!uint.TryParse(vectorDim, out count)) { errors = "Invalid vector dimension on line:\n" + line; success = false; break; } if (!uint.TryParse(arrayDim, out arrayCount)) { arrayCount = 1; } arrayCount = Math.Max(0, arrayCount); if (!uint.TryParse(matrixDim, out matrixCount)) { errors = "Invalid matrix second dimension on line:\n" + line; success = false; break; } if (basetype == "bool") { type = FormatComponentType.UInt; width = 4; } else if (basetype == "byte") { type = FormatComponentType.SInt; width = 1; } else if (basetype == "ubyte" || basetype == "xbyte") { type = FormatComponentType.UInt; width = 1; } else if (basetype == "short") { type = FormatComponentType.SInt; width = 2; } else if (basetype == "ushort" || basetype == "xshort") { type = FormatComponentType.UInt; width = 2; } else if (basetype == "int" || basetype == "ivec" || basetype == "imat") { type = FormatComponentType.SInt; width = 4; } else if (basetype == "uint" || basetype == "xint" || basetype == "uvec" || basetype == "umat") { type = FormatComponentType.UInt; width = 4; } else if (basetype == "half") { type = FormatComponentType.Float; width = 2; } else if (basetype == "float" || basetype == "vec" || basetype == "mat") { type = FormatComponentType.Float; width = 4; } else if (basetype == "double") { type = FormatComponentType.Double; width = 8; } else if (basetype == "unormh") { type = FormatComponentType.UNorm; width = 2; } else if (basetype == "unormb") { type = FormatComponentType.UNorm; width = 1; } else if (basetype == "snormh") { type = FormatComponentType.SNorm; width = 2; } else if (basetype == "snormb") { type = FormatComponentType.SNorm; width = 1; } else if (basetype == "uintten") { fmt = new ResourceFormat(FormatComponentType.UInt, 4 * count, 1); fmt.special = true; fmt.specialFormat = SpecialFormat.R10G10B10A2; } else if (basetype == "unormten") { fmt = new ResourceFormat(FormatComponentType.UNorm, 4 * count, 1); fmt.special = true; fmt.specialFormat = SpecialFormat.R10G10B10A2; } else { errors = "Unrecognised basic type on line:\n" + line; success = false; break; } } if (basetype == "xint" || basetype == "xshort" || basetype == "xbyte") { hex = true; } if (fmt.compType == FormatComponentType.None) { fmt = new ResourceFormat(type, count, width); } if (arrayCount == 1) { FormatElement elem = new FormatElement(name, 0, offset, false, 1, row_major, matrixCount, fmt, hex); uint advance = elem.ByteSize; if (!tightPacking) { // cbuffer packing always works in floats advance = (advance + 3U) & (~3U); // cbuffer packing doesn't allow elements to cross float4 boundaries, nudge up if this was the case if (offset / 16 != (offset + elem.ByteSize - 1) / 16) { elem.offset = offset = (offset + 0xFU) & (~0xFU); } } elems.Add(elem); offset += advance; } else { // when cbuffer packing, arrays are always aligned at float4 boundary if (!tightPacking) { if (offset % 16 != 0) { offset = (offset + 0xFU) & (~0xFU); } } for (uint a = 0; a < arrayCount; a++) { FormatElement elem = new FormatElement(String.Format("{0}[{1}]", name, a), 0, offset, false, 1, row_major, matrixCount, fmt, hex); elems.Add(elem); uint advance = elem.ByteSize; // cbuffer packing each array element is always float4 aligned if (!tightPacking) { advance = (advance + 0xFU) & (~0xFU); } offset += advance; } } } if (!success || elems.Count == 0) { elems.Clear(); var fmt = new ResourceFormat(FormatComponentType.UInt, 4, 4); if (maxLen > 0 && maxLen < 16) { fmt.compCount = 1; } if (maxLen > 0 && maxLen < 4) { fmt.compByteWidth = 1; } elems.Add(new FormatElement("data", 0, 0, false, 1, false, 1, fmt, true)); } return(elems.ToArray()); }