Initial commit of existing code.

* All renderdoc code up to this point was written by me, history is available by request
This commit is contained in:
baldurk
2014-05-02 08:33:01 +01:00
parent 04b1549c0f
commit c38affcded
695 changed files with 230316 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* 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.Windows.Forms;
using renderdoc;
namespace renderdocui.Code
{
class AppMain
{
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// command line arguments that we can call when we temporarily elevate the process
if(args.Contains("--registerRDCext"))
{
Helpers.InstallRDCAssociation();
return;
}
if(args.Contains("--registerCAPext"))
{
Helpers.InstallCAPAssociation();
return;
}
Win32PInvoke.LoadLibrary("renderdoc.dll");
string filename = "";
bool temp = false;
// not real command line argument processing, but allow an argument to indicate we're being passed
// a temporary filename that we should take ownership of to delete when we're done (if the user doesn't
// save it)
foreach(var a in args)
{
if(a.ToLowerInvariant() == "--tempfile")
temp = true;
}
if (args.Length > 0 && File.Exists(args[args.Length - 1]))
{
filename = args[args.Length - 1];
}
var cfg = new PersistantConfig();
// load up the config from user folder, handling errors if it's malformed and falling back to defaults
if (File.Exists(Core.ConfigFilename))
{
try
{
cfg = PersistantConfig.Deserialize(Core.ConfigFilename);
}
catch (System.Xml.XmlException)
{
MessageBox.Show(String.Format("Error loading config file\n{0}\nA default config is loaded and will be saved out.", Core.ConfigFilename));
}
catch (System.InvalidOperationException)
{
MessageBox.Show(String.Format("Error loading config file\n{0}\nA default config is loaded and will be saved out.", Core.ConfigFilename));
}
}
// propogate float formatting settings to the Formatter class used globally to format float values
cfg.SetupFormatter();
var core = new Core(filename, temp, cfg);
try
{
Application.Run(core.AppWindow);
}
catch (Exception e)
{
HandleException(e);
}
cfg.Serialize(Core.ConfigFilename);
}
static void LogException(Exception ex)
{
StaticExports.LogText(ex.ToString());
if (ex.InnerException != null)
{
StaticExports.LogText("InnerException:");
LogException(ex.InnerException);
}
}
static void HandleException(Exception ex)
{
// we log out this string, which is matched against in renderdoccmd to pull out the callstack
// from the log even in the case where the user chooses not to submit the error log
StaticExports.LogText("--- Begin C# Exception Data ---");
if (ex != null)
{
LogException(ex);
StaticExports.TriggerExceptionHandler(System.Runtime.InteropServices.Marshal.GetExceptionPointers(), true);
}
else
{
StaticExports.LogText("Exception is NULL");
StaticExports.TriggerExceptionHandler(IntPtr.Zero, true);
}
System.Diagnostics.Process.GetCurrentProcess().Kill();
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception)
HandleException(e.ExceptionObject as Exception);
else
HandleException(null);
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
HandleException(e.Exception);
}
}
}
+303
View File
@@ -0,0 +1,303 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* 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.Windows.Forms;
using System.Drawing;
using renderdoc;
namespace renderdocui.Code
{
class TimedUpdate
{
public delegate void UpdateMethod();
public TimedUpdate(int msCount, UpdateMethod up)
{
m_Rate = msCount;
m_Update = up;
m_CameraTick = new System.Threading.Timer(TickCB, this as object, m_Rate, System.Threading.Timeout.Infinite);
}
private int m_Rate;
private UpdateMethod m_Update;
private System.Threading.Timer m_CameraTick = null;
private static void TickCB(object state)
{
var me = (TimedUpdate)state;
me.m_Update();
me.m_CameraTick.Change(me.m_Rate, System.Threading.Timeout.Infinite);
}
}
abstract class CameraControls
{
protected CameraControls(Camera c)
{
m_Camera = c;
}
abstract public void MouseWheel(object sender, MouseEventArgs e);
abstract public void Reset(Vec3f pos);
abstract public void Update();
abstract public void Apply();
abstract public bool Dirty { get; }
abstract public Vec3f Position { get; }
abstract public Vec3f Rotation { get; }
virtual public void MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_DragStartPos = e.Location;
}
}
virtual public void MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (m_DragStartPos.X < 0)
{
m_DragStartPos = e.Location;
}
m_DragStartPos = e.Location;
}
else
{
m_DragStartPos = new Point(-1, -1);
}
}
virtual public void KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.A || e.KeyCode == Keys.D)
m_CurrentMove[0] = 0;
if (e.KeyCode == Keys.Q || e.KeyCode == Keys.E)
m_CurrentMove[1] = 0;
if (e.KeyCode == Keys.W || e.KeyCode == Keys.S)
m_CurrentMove[2] = 0;
if (e.Shift)
m_CurrentSpeed = 3.0f;
else
m_CurrentSpeed = 1.0f;
}
virtual public void KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W)
m_CurrentMove[2] = 1;
if (e.KeyCode == Keys.S)
m_CurrentMove[2] = -1;
if (e.KeyCode == Keys.Q)
m_CurrentMove[1] = 1;
if (e.KeyCode == Keys.E)
m_CurrentMove[1] = -1;
if (e.KeyCode == Keys.D)
m_CurrentMove[0] = 1;
if (e.KeyCode == Keys.A)
m_CurrentMove[0] = -1;
if (e.Shift)
m_CurrentSpeed = 3.0f;
else
m_CurrentSpeed = 1.0f;
}
private float m_CurrentSpeed = 1.0f;
private int[] m_CurrentMove = new int[3] { 0, 0, 0 };
public float SpeedMultiplier = 0.05f;
protected int[] CurrentMove { get { return m_CurrentMove; } }
protected float CurrentSpeed { get { return m_CurrentSpeed * SpeedMultiplier; } }
private Point m_DragStartPos = new Point(-1, -1);
protected Point DragStartPos { get { return m_DragStartPos; } }
protected Camera m_Camera;
}
class ArcballCamera : CameraControls
{
public ArcballCamera(Camera c)
: base(c)
{
}
public override void Reset(Vec3f dist)
{
m_Distance = Math.Abs(dist.z);
m_Rotation = new Vec3f();
}
public override void Update()
{
}
public override void Apply()
{
m_Camera.Arcball(m_Distance, Rotation);
}
public override void MouseWheel(object sender, MouseEventArgs e)
{
float mod = (1.0f - (float)e.Delta / 2500.0f);
m_Distance = Math.Max(1.0f, m_Distance * mod);
((HandledMouseEventArgs)e).Handled = true;
m_Dirty = true;
}
public override void MouseMove(object sender, MouseEventArgs e)
{
if (DragStartPos.X > 0 && e.Button == MouseButtons.Left)
{
m_Rotation.y += (float)(e.X - DragStartPos.X) / 300.0f;
m_Rotation.x += (float)(e.Y - DragStartPos.Y) / 300.0f;
m_Dirty = true;
}
base.MouseMove(sender, e);
}
bool m_Dirty = false;
public override bool Dirty
{
get
{
bool ret = m_Dirty;
m_Dirty = false;
return ret;
}
}
private float m_Distance = 10.0f;
private Vec3f m_Rotation = new Vec3f();
public override Vec3f Position { get { return m_Camera.Position; } }
public override Vec3f Rotation { get { return m_Rotation; } }
}
class FlyCamera : CameraControls
{
public FlyCamera(Camera c)
: base(c)
{
}
public override void Reset(Vec3f pos)
{
m_Position = pos;
m_Rotation = new Vec3f();
}
public override void Update()
{
if (CurrentMove[0] != 0)
{
Vec3f dir = m_Camera.Right;
dir.Mul((float)CurrentMove[0]);
m_Position.x += dir.x * CurrentSpeed;
m_Position.y += dir.y * CurrentSpeed;
m_Position.z += dir.z * CurrentSpeed;
m_Dirty = true;
}
if (CurrentMove[1] != 0)
{
Vec3f dir = new Vec3f(0.0f, 1.0f, 0.0f);
//dir = m_Camera.GetUp();
dir.Mul((float)CurrentMove[1]);
m_Position.x += dir.x * CurrentSpeed;
m_Position.y += dir.y * CurrentSpeed;
m_Position.z += dir.z * CurrentSpeed;
m_Dirty = true;
}
if (CurrentMove[2] != 0)
{
Vec3f dir = m_Camera.Forward;
dir.Mul((float)CurrentMove[2]);
m_Position.x += dir.x * CurrentSpeed;
m_Position.y += dir.y * CurrentSpeed;
m_Position.z += dir.z * CurrentSpeed;
m_Dirty = true;
}
}
public override void Apply()
{
m_Camera.fpsLook(m_Position, m_Rotation);
}
public override void MouseWheel(object sender, MouseEventArgs e)
{
}
public override void MouseMove(object sender, MouseEventArgs e)
{
if (DragStartPos.X > 0 && e.Button == MouseButtons.Left)
{
m_Rotation.y -= (float)(e.X - DragStartPos.X) / 300.0f;
m_Rotation.x -= (float)(e.Y - DragStartPos.Y) / 300.0f;
m_Dirty = true;
}
base.MouseMove(sender, e);
}
bool m_Dirty = false;
public override bool Dirty
{
get
{
bool ret = m_Dirty;
m_Dirty = false;
return ret;
}
}
private Vec3f m_Position = new Vec3f(),
m_Rotation = new Vec3f();
public override Vec3f Position { get { return m_Position; } }
public override Vec3f Rotation { get { return m_Rotation; } }
}
}
+467
View File
@@ -0,0 +1,467 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* 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 renderdoc;
namespace renderdocui.Code
{
public class CommonPipelineState
{
private D3D11PipelineState m_D3D11 = null;
private GLPipelineState m_GL = null;
private APIProperties m_APIProps = null;
public CommonPipelineState()
{
}
public void SetStates(APIProperties props, D3D11PipelineState d3d11, GLPipelineState gl)
{
m_APIProps = props;
m_D3D11 = d3d11;
m_GL = gl;
}
private bool LogLoaded
{
get
{
return m_D3D11 != null || m_GL != null;
}
}
private bool IsLogD3D11
{
get
{
return LogLoaded && m_APIProps.pipelineType == APIPipelineStateType.D3D11 && m_D3D11 != null;
}
}
private bool IsLogGL
{
get
{
return LogLoaded && m_APIProps.pipelineType == APIPipelineStateType.OpenGL && m_GL != null;
}
}
// add a bunch of generic properties that people can check to save having to see which pipeline state
// is valid and look at the appropriate part of it
public bool IsTessellationEnabled
{
get
{
if (LogLoaded)
{
if (IsLogD3D11)
return m_D3D11 != null && m_D3D11.m_HS.Shader != ResourceId.Null;
if (IsLogGL)
return m_GL != null && m_GL.m_TES.Shader != ResourceId.Null;
}
return false;
}
}
public PrimitiveTopology DrawTopology
{
get
{
if (LogLoaded)
{
if (IsLogD3D11)
return m_D3D11.m_IA.Topology;
if (IsLogGL)
return m_GL.m_VtxIn.Topology;
}
return PrimitiveTopology.Unknown;
}
}
// there's a lot of redundancy in these functions
public ShaderReflection GetShaderReflection(ShaderStageType stage)
{
if (LogLoaded)
{
if (IsLogD3D11)
{
switch (stage)
{
case ShaderStageType.Vertex: return m_D3D11.m_VS.ShaderDetails;
case ShaderStageType.Domain: return m_D3D11.m_DS.ShaderDetails;
case ShaderStageType.Hull: return m_D3D11.m_HS.ShaderDetails;
case ShaderStageType.Geometry: return m_D3D11.m_GS.ShaderDetails;
case ShaderStageType.Pixel: return m_D3D11.m_PS.ShaderDetails;
case ShaderStageType.Compute: return m_D3D11.m_CS.ShaderDetails;
}
}
else if (IsLogGL)
{
switch (stage)
{
case ShaderStageType.Vertex: return m_GL.m_VS.ShaderDetails;
case ShaderStageType.Tess_Control: return m_GL.m_TCS.ShaderDetails;
case ShaderStageType.Tess_Eval: return m_GL.m_TES.ShaderDetails;
case ShaderStageType.Geometry: return m_GL.m_GS.ShaderDetails;
case ShaderStageType.Fragment: return m_GL.m_FS.ShaderDetails;
case ShaderStageType.Compute: return m_GL.m_CS.ShaderDetails;
}
}
}
return null;
}
public ResourceId GetShader(ShaderStageType stage)
{
if (LogLoaded)
{
if (IsLogD3D11)
{
switch (stage)
{
case ShaderStageType.Vertex: return m_D3D11.m_VS.Shader;
case ShaderStageType.Domain: return m_D3D11.m_DS.Shader;
case ShaderStageType.Hull: return m_D3D11.m_HS.Shader;
case ShaderStageType.Geometry: return m_D3D11.m_GS.Shader;
case ShaderStageType.Pixel: return m_D3D11.m_PS.Shader;
case ShaderStageType.Compute: return m_D3D11.m_CS.Shader;
}
}
else if (IsLogGL)
{
switch (stage)
{
case ShaderStageType.Vertex: return m_GL.m_VS.Shader;
case ShaderStageType.Tess_Control: return m_GL.m_TCS.Shader;
case ShaderStageType.Tess_Eval: return m_GL.m_TES.Shader;
case ShaderStageType.Geometry: return m_GL.m_GS.Shader;
case ShaderStageType.Fragment: return m_GL.m_FS.Shader;
case ShaderStageType.Compute: return m_GL.m_CS.Shader;
}
}
}
return ResourceId.Null;
}
public void GetIBuffer(out ResourceId buf, out uint ByteOffset, out ResourceFormat IndexFormat)
{
if (LogLoaded)
{
if (IsLogD3D11)
{
buf = m_D3D11.m_IA.ibuffer.Buffer;
ByteOffset = m_D3D11.m_IA.ibuffer.Offset;
IndexFormat = m_D3D11.m_IA.ibuffer.Format;
return;
}
else if (IsLogGL)
{
buf = m_GL.m_VtxIn.ibuffer.Buffer;
ByteOffset = m_GL.m_VtxIn.ibuffer.Offset;
IndexFormat = m_GL.m_VtxIn.ibuffer.Format;
return;
}
}
buf = ResourceId.Null;
ByteOffset = 0;
IndexFormat = new ResourceFormat(FormatComponentType.UInt, 1, 2);
}
public struct VBuffer
{
public ResourceId Buffer;
public uint ByteOffset;
public uint ByteStride;
};
public VBuffer[] GetVBuffers()
{
if (LogLoaded)
{
if (IsLogD3D11)
{
VBuffer[] ret = new VBuffer[m_D3D11.m_IA.vbuffers.Length];
for (int i = 0; i < m_D3D11.m_IA.vbuffers.Length; i++)
{
ret[i].Buffer = m_D3D11.m_IA.vbuffers[i].Buffer;
ret[i].ByteOffset = m_D3D11.m_IA.vbuffers[i].Offset;
ret[i].ByteStride = m_D3D11.m_IA.vbuffers[i].Stride;
}
return ret;
}
else if (IsLogGL)
{
VBuffer[] ret = new VBuffer[m_GL.m_VtxIn.vbuffers.Length];
for (int i = 0; i < m_GL.m_VtxIn.vbuffers.Length; i++)
{
ret[i].Buffer = m_GL.m_VtxIn.vbuffers[i].Buffer;
ret[i].ByteOffset = m_GL.m_VtxIn.vbuffers[i].Offset;
ret[i].ByteStride = m_GL.m_VtxIn.vbuffers[i].Stride;
}
return ret;
}
}
return null;
}
public struct VertexInputAttribute
{
public string Name;
public int VertexBuffer;
public uint RelativeByteOffset;
public bool PerInstance;
public int InstanceRate;
public ResourceFormat Format;
};
public VertexInputAttribute[] GetVertexInputs()
{
if (LogLoaded)
{
if (IsLogD3D11)
{
uint[] byteOffs = new uint[128];
for (int i = 0; i < 128; i++)
byteOffs[i] = 0;
var layouts = m_D3D11.m_IA.layouts;
VertexInputAttribute[] ret = new VertexInputAttribute[layouts.Length];
for (int i = 0; i < layouts.Length; i++)
{
bool needsSemanticIdx = false;
for (int j = 0; j < layouts.Length; j++)
{
if (i != j && layouts[i].SemanticName == layouts[j].SemanticName)
{
needsSemanticIdx = true;
break;
}
}
uint offs = layouts[i].ByteOffset;
if (offs == uint.MaxValue) // APPEND_ALIGNED
offs = byteOffs[layouts[i].InputSlot];
else
byteOffs[layouts[i].InputSlot] = offs = layouts[i].ByteOffset;
byteOffs[layouts[i].InputSlot] += layouts[i].Format.compByteWidth * layouts[i].Format.compCount;
ret[i].Name = layouts[i].SemanticName + (needsSemanticIdx ? layouts[i].SemanticIndex.ToString() : "");
ret[i].VertexBuffer = (int)layouts[i].InputSlot;
ret[i].RelativeByteOffset = offs;
ret[i].PerInstance = layouts[i].PerInstance;
ret[i].InstanceRate = (int)layouts[i].InstanceDataStepRate;
ret[i].Format = layouts[i].Format;
}
return ret;
}
else if (IsLogGL)
{
var attrs = m_GL.m_VtxIn.attributes;
int num = 0;
for (int i = 0; i < attrs.Length; i++)
{
if (attrs[i].Enabled)
num++;
}
int a = 0;
VertexInputAttribute[] ret = new VertexInputAttribute[num];
for (int i = 0; i < attrs.Length; i++)
{
if (!attrs[i].Enabled) continue;
ret[a].Name = String.Format("attr{0}", i);
ret[a].VertexBuffer = (int)attrs[i].BufferSlot;
ret[a].RelativeByteOffset = attrs[i].RelativeOffset;
ret[a].PerInstance = m_GL.m_VtxIn.vbuffers[attrs[i].BufferSlot].PerInstance;
ret[a].InstanceRate = (int)m_GL.m_VtxIn.vbuffers[attrs[i].BufferSlot].Divisor;
ret[a].Format = attrs[i].Format;
a++;
}
return ret;
}
}
return null;
}
public void GetConstantBuffer(ShaderStageType stage, uint BindPoint, out ResourceId buf, out uint ByteOffset)
{
if (LogLoaded)
{
if (IsLogD3D11)
{
D3D11PipelineState.ShaderStage s = null;
switch (stage)
{
case ShaderStageType.Vertex: s = m_D3D11.m_VS; break;
case ShaderStageType.Domain: s = m_D3D11.m_DS; break;
case ShaderStageType.Hull: s = m_D3D11.m_HS; break;
case ShaderStageType.Geometry: s = m_D3D11.m_GS; break;
case ShaderStageType.Pixel: s = m_D3D11.m_PS; break;
case ShaderStageType.Compute: s = m_D3D11.m_CS; break;
}
buf = s.ConstantBuffers[BindPoint].Buffer;
ByteOffset = s.ConstantBuffers[BindPoint].VecOffset * 4 * sizeof(float);
return;
}
else if (IsLogGL)
{
buf = ResourceId.Null;
ByteOffset = 0;
return;
}
}
buf = ResourceId.Null;
ByteOffset = 0;
}
public ResourceId[] GetResources(ShaderStageType stage)
{
if (LogLoaded)
{
if (IsLogD3D11)
{
D3D11PipelineState.ShaderStage s = null;
switch (stage)
{
case ShaderStageType.Vertex: s = m_D3D11.m_VS; break;
case ShaderStageType.Domain: s = m_D3D11.m_DS; break;
case ShaderStageType.Hull: s = m_D3D11.m_HS; break;
case ShaderStageType.Geometry: s = m_D3D11.m_GS; break;
case ShaderStageType.Pixel: s = m_D3D11.m_PS; break;
case ShaderStageType.Compute: s = m_D3D11.m_CS; break;
}
ResourceId[] ret = new ResourceId[s.SRVs.Length];
for (int i = 0; i < s.SRVs.Length; i++)
ret[i] = s.SRVs[i].Resource;
return ret;
}
else if (IsLogGL)
{
ResourceId[] ret = new ResourceId[m_GL.Textures.Length];
for (int i = 0; i < m_GL.Textures.Length; i++)
ret[i] = m_GL.Textures[i].Resource;
return ret;
}
}
return null;
}
public ResourceId[] GetOutputTargets()
{
if (LogLoaded)
{
if (IsLogD3D11)
{
ResourceId[] ret = new ResourceId[m_D3D11.m_OM.RenderTargets.Length];
for (int i = 0; i < m_D3D11.m_OM.RenderTargets.Length; i++)
{
ret[i] = m_D3D11.m_OM.RenderTargets[i].Resource;
if (ret[i] == ResourceId.Null && i > m_D3D11.m_OM.UAVStartSlot)
ret[i] = m_D3D11.m_OM.UAVs[i - m_D3D11.m_OM.UAVStartSlot].Resource;
}
return ret;
}
else if (IsLogGL)
{
return m_GL.m_FB.Color;
}
}
return null;
}
public ResourceId OutputDepth
{
get
{
if (LogLoaded)
{
if (IsLogD3D11)
return m_D3D11.m_OM.DepthTarget.Resource;
if (IsLogGL)
return m_GL.m_FB.Depth;
}
return ResourceId.Null;
}
}
public ResourceId OutputStencil
{
get
{
if (LogLoaded)
{
if (IsLogD3D11)
return m_D3D11.m_OM.DepthTarget.Resource;
if (IsLogGL)
return m_GL.m_FB.Stencil;
}
return ResourceId.Null;
}
}
// Still to add:
// [ShaderViewer] * {FetchTexture,FetchBuffer} GetFetchBufferOrFetchTexture(ShaderResource)
}
}
+806
View File
@@ -0,0 +1,806 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* 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.ComponentModel;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.Linq;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using renderdocui.Windows;
using renderdocui.Windows.Dialogs;
using renderdocui.Windows.PipelineState;
using renderdoc;
namespace renderdocui.Code
{
// Single core class. Between this and the RenderManager these classes govern the interaction
// between the UI and the actual implementation.
//
// This class primarily controls things that need to be propogated globally, it keeps a list of
// ILogViewerForms which are windows that would like to be notified of changes to the current event,
// when a log is opened or closed, etc. It also contains data that potentially every window will
// want access to - like a list of all buffers in the log and their properties, etc.
public class Core
{
#region Privates
private RenderManager m_Renderer = new RenderManager();
private PersistantConfig m_Config = null;
private bool m_LogLoaded = false;
private string m_LogFile = "";
private UInt32 m_FrameID = 0;
private UInt32 m_EventID = 0;
private UInt32 m_DeferredEvent = 0;
private APIProperties m_APIProperties = null;
private FetchFrameInfo[] m_FrameInfo = null;
private FetchDrawcall[][] m_DrawCalls = null;
private FetchBuffer[] m_Buffers = null;
private FetchTexture[] m_Textures = null;
private D3D11PipelineState m_D3D11PipelineState = null;
private GLPipelineState m_GLPipelineState = null;
private CommonPipelineState m_PipelineState = new CommonPipelineState();
private List<ILogViewerForm> m_LogViewers = new List<ILogViewerForm>();
private List<ILogLoadProgressListener> m_ProgressListeners = new List<ILogLoadProgressListener>();
private MainWindow m_MainWindow = null;
private EventBrowser m_EventBrowser = null;
private APIInspector m_APIInspector = null;
private DebugMessages m_DebugMessages = null;
private TimelineBar m_TimelineBar = null;
private TextureViewer m_TextureViewer = null;
private PipelineStateViewer m_PipelineStateViewer = null;
#endregion
#region Properties
public static string ConfigDirectory
{
get
{
string appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
return Path.Combine(appdata, "renderdoc");
}
}
public static string ConfigFilename
{
get
{
return Path.Combine(ConfigDirectory, "UI.config");
}
}
public PersistantConfig Config { get { return m_Config; } }
public bool LogLoaded { get { return m_LogLoaded; } }
public bool LogLoading { get { return m_LogLoadingInProgress; } }
public string LogFileName { get { return m_LogFile; } set { if (LogLoaded) m_LogFile = value; } }
public FetchFrameInfo[] FrameInfo { get { while (m_FrameInfo == null); return m_FrameInfo; } }
public APIProperties APIProps { get { return m_APIProperties; } }
// typically 0 right now as we haven't supported multiple frames in logs for a loooong time.
public UInt32 CurFrame { get { return m_FrameID; } }
public UInt32 CurEvent { get { return m_DeferredEvent > 0 ? m_DeferredEvent : m_EventID; } }
public FetchDrawcall[] CurDrawcalls { get { return GetDrawcalls(CurFrame); } }
public FetchDrawcall CurDrawcall { get { return GetDrawcall(CurFrame, CurEvent); } }
public FetchTexture[] CurTextures { get { return m_Textures; } }
public FetchBuffer[] CurBuffers { get { return m_Buffers; } }
// the RenderManager can be used when you want to perform an operation, it will let you Invoke or
// BeginInvoke onto the thread that's used to access the renderdoc project.
public RenderManager Renderer { get { return m_Renderer; } }
public Form AppWindow { get { return m_MainWindow; } }
#endregion
#region Pipeline State
// direct access (note that only one of these will be valid for a log, check APIProps.pipelineType)
public D3D11PipelineState CurD3D11PipelineState { get { return m_D3D11PipelineState; } }
public GLPipelineState CurGLPipelineState { get { return m_GLPipelineState; } }
public CommonPipelineState CurPipelineState { get { return m_PipelineState; } }
#endregion
#region Init and Shutdown
public Core(string paramFilename, bool temp, PersistantConfig config)
{
if (!Directory.Exists(ConfigDirectory))
Directory.CreateDirectory(ConfigDirectory);
m_Config = config;
m_MainWindow = new MainWindow(this, paramFilename, temp);
}
public void Shutdown()
{
if (m_Renderer != null)
m_Renderer.CloseThreadSync();
}
#endregion
#region Log Loading & Capture
private bool m_LogLoadingInProgress = false;
private bool LogLoadCallback()
{
return !m_LogLoadingInProgress;
}
// used to determine if two drawcalls can be considered in the same 'pass',
// ie. writing to similar targets, same type of call, etc.
//
// When a log has no markers, this is used to group up drawcalls into fake markers
private bool PassEquivalent(FetchDrawcall a, FetchDrawcall b)
{
// executing command lists can have children
if(a.children.Length > 0 || b.children.Length > 0)
return false;
// don't group draws and compute executes
if ((a.flags & DrawcallFlags.Dispatch) != (b.flags & DrawcallFlags.Dispatch))
return false;
// don't group things run on different multithreaded contexts
if(a.context != b.context)
return false;
// don't group things with different depth outputs
if (a.depthOut != b.depthOut)
return false;
int numAOuts = 0, numBOuts = 0;
for (int i = 0; i < 8; i++)
{
if (a.outputs[i] != ResourceId.Null) numAOuts++;
if (b.outputs[i] != ResourceId.Null) numBOuts++;
}
int numSame = 0;
if (a.depthOut != ResourceId.Null)
{
numAOuts++;
numBOuts++;
numSame++;
}
for (int i = 0; i < 8; i++)
{
if (a.outputs[i] != ResourceId.Null)
{
for (int j = 0; j < 8; j++)
{
if (a.outputs[i] == b.outputs[j])
{
numSame++;
break;
}
}
}
else if (b.outputs[i] != ResourceId.Null)
{
for (int j = 0; j < 8; j++)
{
if (a.outputs[j] == b.outputs[i])
{
numSame++;
break;
}
}
}
}
// use a kind of heuristic to group together passes where the outputs are similar enough.
// could be useful for example if you're rendering to a gbuffer and sometimes you render
// without one target, but the draws are still batched up.
if (numSame > Math.Max(numAOuts, numBOuts) / 2 && Math.Max(numAOuts, numBOuts) > 1)
return true;
if (numSame == Math.Max(numAOuts, numBOuts))
return true;
return false;
}
private bool ContainsMarker(FetchDrawcall[] draws)
{
bool ret = false;
foreach (var d in draws)
{
ret |= (d.flags & (DrawcallFlags.PushMarker | DrawcallFlags.SetMarker)) > 0 && (d.flags & DrawcallFlags.CmdList) == 0;
ret |= ContainsMarker(d.children);
}
return ret;
}
// if a log doesn't contain any markers specified at all by the user, then we can
// fake some up by determining batches of draws that are similar and giving them a
// pass number
private FetchDrawcall[] FakeProfileMarkers(int frameID, FetchDrawcall[] draws)
{
if (ContainsMarker(draws))
return draws;
var ret = new List<FetchDrawcall>();
int depthpassID = 1;
int computepassID = 1;
int passID = 1;
int start = 0;
int counter = 1;
for (int i = 1; i < draws.Length; i++)
{
if (PassEquivalent(draws[i], draws[start]) && i+1 < draws.Length)
continue;
int end = i - 1;
if (i == draws.Length - 1)
end = i;
if (end - start < 2 ||
draws[i].children.Length > 0 || draws[start].children.Length > 0 ||
draws[i].context != m_FrameInfo[frameID].immContextId ||
draws[start].context != m_FrameInfo[frameID].immContextId)
{
for (int j = start; j <= end; j++)
{
ret.Add(draws[j]);
counter++;
}
start = i;
continue;
}
int minOutCount = 100;
int maxOutCount = 0;
for (int j = start; j <= end; j++)
{
int outCount = 0;
foreach (var o in draws[j].outputs)
if (o != ResourceId.Null)
outCount++;
minOutCount = Math.Min(minOutCount, outCount);
maxOutCount = Math.Max(maxOutCount, outCount);
}
FetchDrawcall mark = new FetchDrawcall();
mark.eventID = draws[end].eventID;
mark.drawcallID = draws[end].drawcallID;
mark.context = draws[end].context;
mark.flags = DrawcallFlags.PushMarker;
mark.outputs = draws[end].outputs;
mark.depthOut = draws[end].depthOut;
mark.name = "Guessed Pass";
if((draws[end].flags & DrawcallFlags.Dispatch) != 0)
mark.name = String.Format("Compute Pass #{0}", computepassID++);
else if (maxOutCount == 0)
mark.name = String.Format("Depth-only Pass #{0}", depthpassID++);
else if(minOutCount == maxOutCount)
mark.name = String.Format("Colour Pass #{0} ({1} Targets{2})", passID++, minOutCount, draws[end].depthOut == ResourceId.Null ? "" : " + Depth");
else
mark.name = String.Format("Colour Pass #{0} ({1}-{2} Targets{3})", passID++, minOutCount, maxOutCount, draws[end].depthOut == ResourceId.Null ? "" : " + Depth");
mark.children = new FetchDrawcall[end - start + 1];
for (int j = start; j <= end; j++)
{
mark.children[j - start] = draws[j];
draws[j].parent = mark;
}
ret.Add(mark);
start = i;
counter++;
}
return ret.ToArray();
}
// loading a local log, no remote replay
public void LoadLogfile(string logFile, bool temporary)
{
LoadLogfile(-1, "", logFile, temporary);
}
// when loading a log while replaying remotely, provide the proxy renderer that will be used
// as well as the hostname to replay on.
public void LoadLogfile(int proxyRenderer, string replayHost, string logFile, bool temporary)
{
m_LogFile = logFile;
m_LogLoadingInProgress = true;
if(!temporary)
m_Config.AddRecentFile(m_Config.RecentLogFiles, logFile, 10);
if (File.Exists(Core.ConfigFilename))
m_Config.Serialize(Core.ConfigFilename);
float postloadProgress = 0.0f;
bool progressThread = true;
// start a modal dialog to prevent the user interacting with the form while the log is loading.
// We'll close it down when log loading finishes (whether it succeeds or fails)
ModalPopup modal = new ModalPopup(LogLoadCallback, true);
Thread modalThread = new Thread(new ThreadStart(() =>
{
modal.SetModalText(string.Format("Loading Log {0}.", m_LogFile));
AppWindow.BeginInvoke(new Action(() =>
{
modal.ShowDialog(AppWindow);
}));
}));
modalThread.Start();
// this thread continually ticks and notifies any threads of the progress, through a float
// that is updated by the main loading code
Thread thread = new Thread(new ThreadStart(() =>
{
modal.LogfileProgressBegin();
foreach (var p in m_ProgressListeners)
p.LogfileProgressBegin();
while (progressThread)
{
Thread.Sleep(2);
float progress = 0.5f * m_Renderer.LoadProgress + 0.49f * postloadProgress + 0.01f;
modal.LogfileProgress(progress);
foreach (var p in m_ProgressListeners)
p.LogfileProgress(progress);
}
}));
thread.Start();
// this function call will block until the log is either loaded, or there's some failure
m_Renderer.Init(proxyRenderer, replayHost, logFile);
// if the renderer isn't running, we hit a failure case so display an error message
if (!m_Renderer.Running)
{
string errmsg = "Unknown error message";
if (m_Renderer.InitException.Data.Contains("status"))
errmsg = ((ReplayCreateStatus)m_Renderer.InitException.Data["status"]).Str();
if(proxyRenderer >= 0)
MessageBox.Show(String.Format("{0}\nFailed to transfer and replay on remote host {1}: {2}.\n\n" +
"Check diagnostic log in Help menu for more details.", logFile, replayHost, errmsg),
"Error opening log", MessageBoxButtons.OK, MessageBoxIcon.Error);
else
MessageBox.Show(String.Format("{0}\nFailed to open logfile for replay: {1}.\n\n" +
"Check diagnostic log in Help menu for more details.", logFile, errmsg),
"Error opening log", MessageBoxButtons.OK, MessageBoxIcon.Error);
progressThread = false;
thread.Join();
m_LogLoadingInProgress = false;
modal.LogfileProgress(-1.0f);
foreach (var p in m_ProgressListeners)
p.LogfileProgress(-1.0f);
return;
}
m_FrameID = 0;
m_EventID = 0;
m_FrameInfo = null;
m_APIProperties = null;
// fetch initial data like drawcalls, textures and buffers
m_Renderer.Invoke((ReplayRenderer r) =>
{
m_FrameInfo = r.GetFrameInfo();
m_APIProperties = r.GetAPIProperties();
postloadProgress = 0.2f;
m_DrawCalls = new FetchDrawcall[m_FrameInfo.Length][];
postloadProgress = 0.4f;
for (int i = 0; i < m_FrameInfo.Length; i++)
m_DrawCalls[i] = FakeProfileMarkers(i, r.GetDrawcalls((UInt32)i, false));
m_TimedDrawcalls = false;
postloadProgress = 0.7f;
m_Buffers = r.GetBuffers();
postloadProgress = 0.8f;
var texs = new List<FetchTexture>(r.GetTextures());
m_Textures = texs.OrderBy(o => o.name).ToArray();
postloadProgress = 0.9f;
m_D3D11PipelineState = r.GetD3D11PipelineState();
m_GLPipelineState = r.GetGLPipelineState();
m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_GLPipelineState);
postloadProgress = 1.0f;
});
Thread.Sleep(20);
m_LogLoaded = true;
progressThread = false;
// notify all the registers log viewers that a log has been loaded
foreach (var logviewer in m_LogViewers)
{
Control c = (Control)logviewer;
if (c.InvokeRequired)
{
if (!c.IsDisposed)
{
c.Invoke(new Action(() => {
try
{
logviewer.OnLogfileLoaded();
}
catch (Exception ex)
{
throw new AccessViolationException("Rethrown from Invoke:\n" + ex.ToString());
}
}));
}
}
else if (!c.IsDisposed)
logviewer.OnLogfileLoaded();
}
m_LogLoadingInProgress = false;
modal.LogfileProgress(1.0f);
foreach (var p in m_ProgressListeners)
p.LogfileProgress(1.0f);
}
public void CloseLogfile()
{
if (!m_LogLoaded) return;
m_LogFile = "";
m_Renderer.CloseThreadSync();
m_Renderer = new RenderManager();
m_APIProperties = null;
m_FrameInfo = null;
m_DrawCalls = null;
m_Buffers = null;
m_Textures = null;
m_D3D11PipelineState = null;
m_GLPipelineState = null;
m_PipelineState.SetStates(null, null, null);
m_LogLoaded = false;
foreach (var logviewer in m_LogViewers)
{
Control c = (Control)logviewer;
if (c.InvokeRequired)
c.Invoke(new Action(() => logviewer.OnLogfileClosed()));
else
logviewer.OnLogfileClosed();
}
}
public String TempLogFilename(String appname)
{
string folder = Config.CaptureSavePath;
try
{
if (folder == "" || !Directory.Exists(folder))
folder = Path.GetTempPath();
}
catch (ArgumentException)
{
// invalid path or similar
folder = Path.GetTempPath();
}
return Path.Combine(folder, appname + "_" + DateTime.Now.ToString(@"yyyy.MM.dd_HH.mm.ss") + ".rdc");
}
#endregion
#region Log drawcalls
private bool m_TimedDrawcalls = false;
public void TimeDrawcalls(ReplayRenderer r)
{
if (m_TimedDrawcalls) return;
m_TimedDrawcalls = true;
for (int i = 0; i < m_FrameInfo.Length; i++)
m_DrawCalls[i] = FakeProfileMarkers(i, r.GetDrawcalls((UInt32)i, true));
}
public FetchDrawcall[] GetDrawcalls(UInt32 frameIdx)
{
if (m_DrawCalls == null) return null;
return m_DrawCalls[frameIdx];
}
private FetchDrawcall GetDrawcall(FetchDrawcall[] draws, UInt32 eventID)
{
foreach (var d in draws)
{
if (d.children != null && d.children.Length > 0)
{
var draw = GetDrawcall(d.children, eventID);
if (draw != null) return draw;
}
if (d.eventID == eventID)
return d;
}
return null;
}
public FetchDrawcall GetDrawcall(UInt32 frameID, UInt32 eventID)
{
if (frameID < 0 || m_DrawCalls == null || frameID >= m_DrawCalls.Length)
return null;
return GetDrawcall(m_DrawCalls[frameID], eventID);
}
#endregion
#region Viewers
// Some viewers we only allow one to exist at once, so we keep the instance here.
public EventBrowser GetEventBrowser()
{
if (m_EventBrowser == null || m_EventBrowser.IsDisposed)
{
m_EventBrowser = new EventBrowser(this);
AddLogViewer(m_EventBrowser);
}
return m_EventBrowser;
}
public TextureViewer GetTextureViewer()
{
if (m_TextureViewer == null || m_TextureViewer.IsDisposed)
{
m_TextureViewer = new TextureViewer(this);
AddLogViewer(m_TextureViewer);
}
return m_TextureViewer;
}
public PipelineStateViewer GetPipelineStateViewer()
{
if (m_PipelineStateViewer == null || m_PipelineStateViewer.IsDisposed)
{
m_PipelineStateViewer = new PipelineStateViewer(this);
AddLogViewer(m_PipelineStateViewer);
}
return m_PipelineStateViewer;
}
public APIInspector GetAPIInspector()
{
if (m_APIInspector == null || m_APIInspector.IsDisposed)
{
m_APIInspector = new APIInspector(this);
AddLogViewer(m_APIInspector);
}
return m_APIInspector;
}
public DebugMessages GetDebugMessages()
{
if (m_DebugMessages == null || m_DebugMessages.IsDisposed)
{
m_DebugMessages = new DebugMessages(this);
AddLogViewer(m_DebugMessages);
}
return m_DebugMessages;
}
public TimelineBar TimelineBar
{
get
{
if (m_TimelineBar == null || m_TimelineBar.IsDisposed)
return null;
return m_TimelineBar;
}
}
private CaptureDialog m_CaptureDialog = null;
public CaptureDialog CaptureDialog
{
get
{
return m_CaptureDialog == null || m_CaptureDialog.IsDisposed ? null : m_CaptureDialog;
}
set
{
if (m_CaptureDialog == null || m_CaptureDialog.IsDisposed)
m_CaptureDialog = value;
}
}
public TimelineBar GetTimelineBar()
{
if (m_TimelineBar == null || m_TimelineBar.IsDisposed)
{
m_TimelineBar = new TimelineBar(this);
AddLogViewer(m_TimelineBar);
}
return m_TimelineBar;
}
public void AddLogProgressListener(ILogLoadProgressListener p)
{
m_ProgressListeners.Add(p);
}
public void AddLogViewer(ILogViewerForm f)
{
m_LogViewers.Add(f);
if (LogLoaded)
{
f.OnLogfileLoaded();
f.OnEventSelected(CurFrame, CurEvent);
}
}
public void RemoveLogViewer(ILogViewerForm f)
{
m_LogViewers.Remove(f);
}
#endregion
#region Log Browsing
// setting a context filter allows replaying of deferred events. You can set the deferred
// events to replay in a context, after replaying up to a given event on the main thread
public void SetContextFilter(ILogViewerForm exclude, UInt32 frameID, UInt32 eventID,
ResourceId ctx, UInt32 firstDeferred, UInt32 lastDeferred)
{
m_FrameID = frameID;
m_EventID = eventID;
m_DeferredEvent = lastDeferred;
m_Renderer.Invoke((ReplayRenderer r) => { r.SetContextFilter(ctx, firstDeferred, lastDeferred); });
m_Renderer.Invoke((ReplayRenderer r) => {
r.SetFrameEvent(m_FrameID, m_EventID);
m_D3D11PipelineState = r.GetD3D11PipelineState();
m_GLPipelineState = r.GetGLPipelineState();
m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_GLPipelineState);
});
foreach (var logviewer in m_LogViewers)
{
if (logviewer == exclude)
continue;
Control c = (Control)logviewer;
if (c.InvokeRequired)
c.BeginInvoke(new Action(() => logviewer.OnEventSelected(frameID, eventID)));
else
logviewer.OnEventSelected(frameID, eventID);
}
}
public void SetEventID(ILogViewerForm exclude, UInt32 frameID, UInt32 eventID)
{
m_FrameID = frameID;
m_EventID = eventID;
m_DeferredEvent = 0;
m_Renderer.Invoke((ReplayRenderer r) => { r.SetContextFilter(ResourceId.Null, 0, 0); });
m_Renderer.Invoke((ReplayRenderer r) =>
{
r.SetFrameEvent(m_FrameID, m_EventID);
m_D3D11PipelineState = r.GetD3D11PipelineState();
m_GLPipelineState = r.GetGLPipelineState();
m_PipelineState.SetStates(m_APIProperties, m_D3D11PipelineState, m_GLPipelineState);
});
foreach (var logviewer in m_LogViewers)
{
if(logviewer == exclude)
continue;
Control c = (Control)logviewer;
if (c.InvokeRequired)
c.BeginInvoke(new Action(() => logviewer.OnEventSelected(frameID, eventID)));
else
logviewer.OnEventSelected(frameID, eventID);
}
}
#endregion
}
}
+182
View File
@@ -0,0 +1,182 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* 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.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
using Microsoft.Win32;
using System.Security.Principal;
using System.Diagnostics;
using System.Xml.Serialization;
namespace renderdocui.Code
{
static class Helpers
{
// simple helpers to wrap a given control in a DockContent, so it can be docked into a panel.
static public DockContent WrapDockContent(DockPanel panel, Control c)
{
return WrapDockContent(panel, c, c.Text);
}
static public DockContent WrapDockContent(DockPanel panel, Control c, string Title)
{
DockContent w = new DockContent();
c.Dock = DockStyle.Fill;
w.Controls.Add(c);
w.DockAreas &= ~DockAreas.Float;
w.Text = Title;
w.DockPanel = panel;
w.DockHandler.GetPersistStringCallback = new GetPersistStringCallback(() => { return c.Name; });
Control win = panel as Control;
while (win != null && !(win is Form))
win = win.Parent;
if (win != null && win is Form)
w.Icon = (win as Form).Icon;
return w;
}
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
{
if (val.CompareTo(min) < 0) return min;
else if (val.CompareTo(max) > 0) return max;
else return val;
}
public static bool IsElevated
{
get
{
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
}
}
public static void RefreshAssociations()
{
Win32PInvoke.SHChangeNotify(Win32PInvoke.HChangeNotifyEventID.SHCNE_ASSOCCHANGED,
Win32PInvoke.HChangeNotifyFlags.SHCNF_IDLIST |
Win32PInvoke.HChangeNotifyFlags.SHCNF_FLUSHNOWAIT |
Win32PInvoke.HChangeNotifyFlags.SHCNF_NOTIFYRECURSIVE,
IntPtr.Zero, IntPtr.Zero);
}
public static void InstallRDCAssociation()
{
if (!IsElevated)
{
var process = new Process();
process.StartInfo = new ProcessStartInfo(Application.ExecutablePath, "--registerRDCext");
process.StartInfo.Verb = "runas";
try
{
process.Start();
}
catch (Exception)
{
// fire and forget - most likely caused by user saying no to UAC prompt
}
return;
}
var path = Path.GetFullPath(Application.ExecutablePath);
RegistryKey key = Registry.ClassesRoot.CreateSubKey("RenderDoc.RDCCapture.1");
key.SetValue("", "RenderDoc Capture Log (.rdc)");
key.CreateSubKey("shell").CreateSubKey("open").CreateSubKey("command").SetValue("", "\"" + path + "\" \"%1\"");
key.CreateSubKey("DefaultIcon").SetValue("", path);
key.CreateSubKey("CLSID").SetValue("", "{5D6BF029-A6BA-417A-8523-120492B1DCE3}");
key.CreateSubKey("ShellEx").CreateSubKey("{e357fccd-a995-4576-b01f-234630154e96}").SetValue("", "{5D6BF029-A6BA-417A-8523-120492B1DCE3}");
key.Close();
key = Registry.ClassesRoot.CreateSubKey(".rdc");
key.SetValue("", "RenderDoc.RDCCapture.1");
key.Close();
var dllpath = Path.Combine(Path.GetDirectoryName(path), "renderdoc.dll");
key = Registry.ClassesRoot.OpenSubKey("CLSID", true).CreateSubKey("{5D6BF029-A6BA-417A-8523-120492B1DCE3}");
key.SetValue("", "RenderDoc Thumbnail Handler");
key.CreateSubKey("InprocServer32").SetValue("", dllpath);
key.Close();
RefreshAssociations();
}
public static void InstallCAPAssociation()
{
if (!IsElevated)
{
var process = new Process();
process.StartInfo = new ProcessStartInfo(Application.ExecutablePath, "--registerCAPext");
process.StartInfo.Verb = "runas";
try
{
process.Start();
}
catch (Exception)
{
// fire and forget - most likely caused by user saying no to UAC prompt
}
return;
}
var path = Path.GetFullPath(Application.ExecutablePath);
RegistryKey key = Registry.ClassesRoot.CreateSubKey("RenderDoc.RDCSettings.1");
key.SetValue("", "RenderDoc Capture Settings (.cap)");
key.CreateSubKey("DefaultIcon").SetValue("", path);
key.CreateSubKey("shell").CreateSubKey("open").CreateSubKey("command").SetValue("", "\"" + path + "\" \"%1\"");
key.Close();
key = Registry.ClassesRoot.CreateSubKey(".cap");
key.SetValue("", "RenderDoc.RDCSettings.1");
key.Close();
RefreshAssociations();
}
}
// KeyValuePair isn't serializable, so we make our own that is
[Serializable]
public struct SerializableKeyValuePair<K, V>
{
public SerializableKeyValuePair(K k, V v) : this() { Key = k; Value = v; }
public K Key
{ get; set; }
public V Value
{ get; set; }
}
}
+45
View File
@@ -0,0 +1,45 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* 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;
namespace renderdocui.Code
{
public interface ILogViewerForm
{
void OnLogfileLoaded();
void OnLogfileClosed();
void OnEventSelected(UInt32 frameID, UInt32 eventID);
}
public interface ILogLoadProgressListener
{
void LogfileProgressBegin();
void LogfileProgress(float progress);
}
}
+156
View File
@@ -0,0 +1,156 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* 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.Xml;
using System.Xml.Serialization;
using renderdoc;
namespace renderdocui.Code
{
[Serializable]
public class PersistantConfig
{
public string LastLogPath = "";
public List<string> RecentLogFiles = new List<string>();
public string LastCapturePath = "";
public List<string> RecentCaptureSettings = new List<string>();
public int CallstackLevelSkip = 0;
public string CaptureSavePath = "";
public bool TextureViewer_ResetRange = false;
public bool TextureViewer_DisableThumbnails = false;
public bool ShaderViewer_FriendlyNaming = true;
public List<string> RecentHosts = new List<string>();
public int LocalProxy = 0;
[XmlIgnore] // not directly serializable
public Dictionary<string, string> ReplayHosts = new Dictionary<string, string>();
public List<SerializableKeyValuePair<string, string>> ReplayHostKeyValues = new List<SerializableKeyValuePair<string, string>>();
public List<SerializableKeyValuePair<string, string>> PreviouslyUsedHosts = new List<SerializableKeyValuePair<string, string>>();
public enum TimeUnit
{
Seconds = 0,
Milliseconds,
Microseconds,
Nanoseconds,
};
public static String UnitPrefix(TimeUnit t)
{
if (t == TimeUnit.Seconds)
return "s";
else if (t == TimeUnit.Milliseconds)
return "ms";
else if (t == TimeUnit.Microseconds)
return "µs";
else if (t == TimeUnit.Nanoseconds)
return "ns";
return "s";
}
public TimeUnit EventBrowser_TimeUnit = TimeUnit.Microseconds;
public bool EventBrowser_HideEmpty = false;
public int Formatter_MinFigures = 2;
public int Formatter_MaxFigures = 5;
public int Formatter_NegExp = 5;
public int Formatter_PosExp = 7;
public bool CheckUpdate_AllowChecks = true;
public bool CheckUpdate_UpdateAvailable = false;
public DateTime CheckUpdate_LastUpdate = new DateTime(2012, 06, 27);
public void SetupFormatter()
{
Formatter.MinFigures = Formatter_MinFigures;
Formatter.MaxFigures = Formatter_MaxFigures;
Formatter.ExponentialNegCutoff = Formatter_NegExp;
Formatter.ExponentialPosCutoff = Formatter_PosExp;
}
public void AddRecentFile(List<string> recentList, string file, int maxItems)
{
if (!recentList.Contains(Path.GetFullPath(file)))
{
recentList.Add(Path.GetFullPath(file));
if (recentList.Count >= maxItems)
recentList.RemoveAt(0);
}
else
{
recentList.Remove(Path.GetFullPath(file));
recentList.Add(Path.GetFullPath(file));
}
}
public PersistantConfig()
{
CallstackLevelSkip = 0;
RecentLogFiles.Clear();
RecentCaptureSettings.Clear();
}
public void Serialize(string file)
{
ReplayHostKeyValues.Clear();
foreach(var kv in ReplayHosts)
ReplayHostKeyValues.Add(new SerializableKeyValuePair<string,string>(kv.Key, kv.Value));
XmlSerializer xs = new XmlSerializer(this.GetType());
StreamWriter writer = File.CreateText(file);
xs.Serialize(writer, this);
writer.Flush();
writer.Close();
}
public static PersistantConfig Deserialize(string file)
{
XmlSerializer xs = new XmlSerializer(typeof(PersistantConfig));
StreamReader reader = File.OpenText(file);
PersistantConfig c = (PersistantConfig)xs.Deserialize(reader);
reader.Close();
foreach (var kv in c.ReplayHostKeyValues)
{
if(kv.Key != null && kv.Key != "" &&
kv.Value != null)
c.ReplayHosts.Add(kv.Key, kv.Value);
}
return c;
}
}
}
+238
View File
@@ -0,0 +1,238 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* 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.Threading;
using System.Reflection;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using renderdoc;
namespace renderdocui.Code
{
public delegate void InvokeMethod(ReplayRenderer r);
// this class owns the thread that interacts with the main library, to ensure that we don't
// have to worry elsewhere about threading access. Elsewhere in the UI you can do Invoke or
// BeginInvoke and get a ReplayRenderer reference back to access through
public class RenderManager
{
private class InvokeHandle
{
public InvokeHandle(InvokeMethod m)
{
method = m;
processed = false;
}
public InvokeMethod method;
volatile public bool processed;
};
////////////////////////////////////////////
// variables
private AutoResetEvent m_WakeupEvent = new AutoResetEvent(false);
private Thread m_Thread;
private int m_ProxyRenderer = -1;
private string m_ReplayHost;
private string m_Logfile;
private bool m_Running;
private List<InvokeHandle> m_renderQueue;
////////////////////////////////////////////
// Interface
public RenderManager()
{
Running = false;
m_renderQueue = new List<InvokeHandle>();
}
public void Init(int proxyRenderer, string replayHost, string logfile)
{
if(Running)
return;
m_ProxyRenderer = proxyRenderer;
m_ReplayHost = replayHost;
m_Logfile = logfile;
LoadProgress = 0.0f;
InitException = null;
m_Thread = new Thread(new ThreadStart(this.RunThread));
m_Thread.Priority = ThreadPriority.Highest;
m_Thread.Start();
while (m_Thread.IsAlive && !Running) ;
}
public bool Running
{
get { return m_Running; }
set { m_Running = value; m_WakeupEvent.Set(); }
}
public ApplicationException InitException = null;
public void CloseThreadSync()
{
Running = false;
while (m_Thread != null && m_Thread.IsAlive) ;
}
public float LoadProgress;
public void BeginInvoke(InvokeMethod m)
{
InvokeHandle cmd = new InvokeHandle(m);
PushInvoke(cmd);
}
public void Invoke(InvokeMethod m)
{
InvokeHandle cmd = new InvokeHandle(m);
PushInvoke(cmd);
while (!cmd.processed) ;
}
private void PushInvoke(InvokeHandle cmd)
{
if (m_Thread == null || !Running)
{
cmd.processed = true;
return;
}
m_WakeupEvent.Set();
lock (m_renderQueue)
{
m_renderQueue.Add(cmd);
}
}
////////////////////////////////////////////
// Internals
private void CreateReplayRenderer(ref ReplayRenderer renderer, ref RemoteRenderer remote)
{
if (m_ProxyRenderer < 0)
{
renderer = StaticExports.CreateReplayRenderer(m_Logfile, ref LoadProgress);
return;
}
remote = StaticExports.CreateRemoteReplayConnection(m_ReplayHost);
if(remote == null)
{
var e = new System.ApplicationException("Failed to connect to remote replay host");
e.Data.Add("status", ReplayCreateStatus.UnknownError);
throw e;
}
renderer = remote.CreateProxyRenderer(m_ProxyRenderer, m_Logfile, ref LoadProgress);
if(renderer == null)
{
remote.Shutdown();
var e = new System.ApplicationException("Failed to connect to remote replay host");
e.Data.Add("status", ReplayCreateStatus.UnknownError);
throw e;
}
}
private void RunThread()
{
try
{
ReplayRenderer renderer = null;
RemoteRenderer remote = null;
CreateReplayRenderer(ref renderer, ref remote);
if(renderer != null)
{
System.Diagnostics.Debug.WriteLine("Renderer created");
DateTime prevTime = DateTime.Now;
Running = true;
while (Running)
{
DateTime curTime = DateTime.Now;
long msPassed = (curTime.Ticks - prevTime.Ticks) / TimeSpan.TicksPerMillisecond;
List<InvokeHandle> queue = new List<InvokeHandle>();
lock (m_renderQueue)
{
foreach (var cmd in m_renderQueue)
queue.Add(cmd);
m_renderQueue.Clear();
}
foreach (var cmd in queue)
{
if (cmd.method != null)
cmd.method(renderer);
cmd.processed = true;
}
m_WakeupEvent.WaitOne(10);
}
lock (m_renderQueue)
{
foreach (var cmd in m_renderQueue)
cmd.processed = true;
m_renderQueue.Clear();
}
renderer.Shutdown();
if (remote != null) remote.Shutdown();
}
}
catch (ApplicationException ex)
{
InitException = ex;
}
}
}
}
+95
View File
@@ -0,0 +1,95 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2014 Crytek
*
* 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.Runtime.InteropServices;
namespace renderdocui.Code
{
class Win32PInvoke
{
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr LoadLibrary(string lpFileName);
// for redirecting mousewheel
[DllImport("user32.dll")]
public static extern IntPtr WindowFromPoint(System.Drawing.Point pt);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr wnd, int msg, IntPtr wp, IntPtr lp);
// windows message from winuser.h
public enum Win32Message
{
WM_MOUSEWHEEL = 0x020A,
TCM_ADJUSTRECT = 0x1328,
};
[Flags]
public enum HChangeNotifyEventID
{
SHCNE_ALLEVENTS = 0x7FFFFFFF,
SHCNE_ASSOCCHANGED = 0x08000000,
SHCNE_ATTRIBUTES = 0x00000800,
SHCNE_CREATE = 0x00000002,
SHCNE_DELETE = 0x00000004,
SHCNE_DRIVEADD = 0x00000100,
SHCNE_DRIVEADDGUI = 0x00010000,
SHCNE_DRIVEREMOVED = 0x00000080,
SHCNE_EXTENDED_EVENT = 0x04000000,
SHCNE_FREESPACE = 0x00040000,
SHCNE_MEDIAINSERTED = 0x00000020,
SHCNE_MEDIAREMOVED = 0x00000040,
SHCNE_MKDIR = 0x00000008,
SHCNE_NETSHARE = 0x00000200,
SHCNE_NETUNSHARE = 0x00000400,
SHCNE_RENAMEFOLDER = 0x00020000,
SHCNE_RENAMEITEM = 0x00000001,
SHCNE_RMDIR = 0x00000010,
SHCNE_SERVERDISCONNECT = 0x00004000,
SHCNE_UPDATEDIR = 0x00001000,
SHCNE_UPDATEIMAGE = 0x00008000,
}
[Flags]
public enum HChangeNotifyFlags
{
SHCNF_DWORD = 0x0003,
SHCNF_IDLIST = 0x0000,
SHCNF_PATHA = 0x0001,
SHCNF_PATHW = 0x0005,
SHCNF_PRINTERA = 0x0002,
SHCNF_PRINTERW = 0x0006,
SHCNF_FLUSH = 0x1000,
SHCNF_FLUSHNOWAIT = 0x2000,
SHCNF_NOTIFYRECURSIVE = 0x10000,
}
[DllImport("shell32.dll")]
public static extern void SHChangeNotify(HChangeNotifyEventID wEventId, HChangeNotifyFlags uFlags, IntPtr dwItem1, IntPtr dwItem2);
}
}