/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2014 Baldur Karlsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Text.RegularExpressions; using renderdoc; namespace renderdocui.Code { public class FormatElement { public FormatElement() { name = ""; buffer = 0; offset = 0; perinstance = false; instancerate = 1; rowmajor = false; matrixdim = 0; format = new ResourceFormat(); hex = false; systemValue = SystemAttribute.None; } public FormatElement(string Name, int buf, uint offs, bool pi, int ir, bool rowMat, uint matDim, ResourceFormat f, bool h) { name = Name; buffer = buf; offset = offs; format = f; perinstance = pi; instancerate = ir; rowmajor = rowMat; matrixdim = matDim; hex = h; systemValue = SystemAttribute.None; } public override bool Equals(Object obj) { return obj is FormatElement && this == (FormatElement)obj; } public override int GetHashCode() { int hash = name.GetHashCode() * 17; hash = hash * 17 + buffer.GetHashCode(); hash = hash * 17 + offset.GetHashCode(); hash = hash * 17 + format.GetHashCode(); hash = hash * 17 + perinstance.GetHashCode(); hash = hash * 17 + rowmajor.GetHashCode(); hash = hash * 17 + matrixdim.GetHashCode(); hash = hash * 17 + hex.GetHashCode(); return hash; } public static bool operator ==(FormatElement x, FormatElement y) { if ((object)x == null) return (object)y == null; if ((object)y == null) return (object)x == null; return x.name == y.name && x.buffer == y.buffer && x.offset == y.offset && x.format == y.format && x.perinstance == y.perinstance && x.rowmajor == y.rowmajor && x.matrixdim == y.matrixdim && x.hex == y.hex; } public static bool operator !=(FormatElement x, FormatElement y) { return !(x == y); } public uint ByteSize { get { return format.compByteWidth * format.compCount * matrixdim; } } public object[] GetObjects(BinaryReader read) { var ret = new List(); if (format.special && format.specialFormat == SpecialFormat.R5G5B5A1) { ushort packed = read.ReadUInt16(); ret.Add((float)((packed >> 0) & 0x1f) / 31.0f); ret.Add((float)((packed >> 5) & 0x1f) / 31.0f); ret.Add((float)((packed >> 10) & 0x1f) / 31.0f); ret.Add(((packed & 0x8000) > 0) ? 1.0f : 0.0f); if (format.bgraOrder) { object tmp = ret[2]; ret[2] = ret[0]; ret[0] = tmp; } } else if (format.special && format.specialFormat == SpecialFormat.R5G6B5) { ushort packed = read.ReadUInt16(); ret.Add((float)((packed >> 0) & 0x1f) / 31.0f); ret.Add((float)((packed >> 5) & 0x3f) / 63.0f); ret.Add((float)((packed >> 11) & 0x1f) / 31.0f); if (format.bgraOrder) { object tmp = ret[2]; ret[2] = ret[0]; ret[0] = tmp; } } else if (format.special && format.specialFormat == SpecialFormat.R4G4B4A4) { ushort packed = read.ReadUInt16(); ret.Add((float)((packed >> 0) & 0xf) / 15.0f); ret.Add((float)((packed >> 4) & 0xf) / 15.0f); ret.Add((float)((packed >> 8) & 0xf) / 15.0f); ret.Add((float)((packed >> 12) & 0xf) / 15.0f); if (format.bgraOrder) { object tmp = ret[2]; ret[2] = ret[0]; ret[0] = tmp; } } else if (format.special && format.specialFormat == SpecialFormat.R10G10B10A2) { // allow for vectors of this format - for raw buffer viewer for (int i = 0; i < (format.compCount / 4); i++) { uint packed = read.ReadUInt32(); uint r = (packed >> 0) & 0x3ff; uint g = (packed >> 10) & 0x3ff; uint b = (packed >> 20) & 0x3ff; uint a = (packed >> 30) & 0x003; if (format.compType == FormatComponentType.UInt) { ret.Add(r); ret.Add(g); ret.Add(b); ret.Add(a); } else { ret.Add((float)r / 1023.0f); ret.Add((float)g / 1023.0f); ret.Add((float)b / 1023.0f); ret.Add((float)a / 3.0f); } } } else if (format.special && format.specialFormat == SpecialFormat.R11G11B10) { uint packed = read.ReadUInt32(); uint xMantissa = ((packed >> 0) & 0x3f); uint xExponent = ((packed >> 6) & 0x1f); uint yMantissa = ((packed >> 11) & 0x3f); uint yExponent = ((packed >> 17) & 0x1f); uint zMantissa = ((packed >> 22) & 0x1f); uint zExponent = ((packed >> 27) & 0x1f); ret.Add(((float)(xMantissa) / 64.0f) * Math.Pow(2.0f, (float)xExponent - 15.0f)); ret.Add(((float)(yMantissa) / 32.0f) * Math.Pow(2.0f, (float)yExponent - 15.0f)); ret.Add(((float)(zMantissa) / 32.0f) * Math.Pow(2.0f, (float)zExponent - 15.0f)); } else { int dim = (int)(Math.Max(matrixdim, 1) * format.compCount); for (int i = 0; i < dim; i++) { if (format.compType == FormatComponentType.Float) { if (format.compByteWidth == 8) ret.Add(read.ReadDouble()); else if (format.compByteWidth == 4) ret.Add(read.ReadSingle()); else if (format.compByteWidth == 2) ret.Add(format.ConvertFromHalf(read.ReadUInt16())); } else if (format.compType == FormatComponentType.SInt) { if (format.compByteWidth == 4) ret.Add((int)read.ReadInt32()); else if (format.compByteWidth == 2) ret.Add((int)read.ReadInt16()); else if (format.compByteWidth == 1) ret.Add((int)read.ReadSByte()); } else if (format.compType == FormatComponentType.UInt) { if (format.compByteWidth == 4) ret.Add((uint)read.ReadUInt32()); else if (format.compByteWidth == 2) ret.Add((uint)read.ReadUInt16()); else if (format.compByteWidth == 1) ret.Add((uint)read.ReadByte()); } else if (format.compType == FormatComponentType.Depth) { float f = (float)read.ReadUInt32(); if (format.compByteWidth == 4) ret.Add(f / (float)uint.MaxValue); else if (format.compByteWidth == 3) ret.Add(f / (float)0x00ffffff); else if (format.compByteWidth == 2) ret.Add(f / (float)0xffff); } else if (format.compType == FormatComponentType.Double) { ret.Add(read.ReadDouble()); } else { // unorm/snorm if (format.compByteWidth == 4) { renderdoc.StaticExports.LogText("Unexpected 4-byte unorm/snorm value"); ret.Add((float)read.ReadUInt32() / (float)uint.MaxValue); // should never hit this - no 32bit unorm/snorm type } else if (format.compByteWidth == 2) { ret.Add(format.Interpret(read.ReadUInt16())); } else if (format.compByteWidth == 1) { ret.Add(format.Interpret(read.ReadByte())); } } } if (format.bgraOrder) { object tmp = ret[2]; ret[2] = ret[0]; ret[0] = tmp; } } return ret.ToArray(); } public ShaderVariable GetShaderVar(BinaryReader read) { object[] objs = GetObjects(read); ShaderVariable ret = new ShaderVariable(); ret.name = name; ret.type = VarType.Float; if (format.compType == FormatComponentType.UInt) ret.type = VarType.UInt; if (format.compType == FormatComponentType.SInt) ret.type = VarType.Int; if (format.compType == FormatComponentType.Double) ret.type = VarType.Double; ret.columns = Math.Min(format.compCount, 4); ret.rows = Math.Min(matrixdim, 4); ret.members = new ShaderVariable[0] { }; ret.value.fv = new float[16]; ret.value.uv = new uint[16]; ret.value.iv = new int[16]; ret.value.dv = new double[16]; for (uint row = 0; row < ret.rows; row++) { for (uint col = 0; col < ret.columns; col++) { uint dst = row * ret.columns + col; uint src = row * format.compCount + col; object o = objs[src]; if (o is double) ret.value.dv[dst] = (double)o; else if (o is float) ret.value.fv[dst] = (float)o; else if (o is uint) ret.value.uv[dst] = (uint)o; else if (o is int) ret.value.iv[dst] = (int)o; } } return ret; } static public FormatElement[] ParseFormatString(string formatString, UInt64 maxLen, bool tightPacking, out string errors) { var elems = new List(); 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; // 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(); } public string name; public int buffer; public uint offset; public bool perinstance; public int instancerate; public bool rowmajor; public uint matrixdim; public ResourceFormat format; public bool hex; public SystemAttribute systemValue; } }