Files
renderdoc/renderdocui/Code/FormatElement.cs
T
2016-02-07 18:49:30 +01:00

612 lines
23 KiB
C#

/******************************************************************************
* 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<object>();
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<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;
// 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;
}
}