mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-23 23:16:31 +00:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; } }
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user