mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 17:10:47 +00:00
0261095de6
* This means when multiple fragments are writing to a pixel you can choose precisely the one you want to debug, rather than the debugging always running the approximately last fragment to pass
2989 lines
103 KiB
C#
2989 lines
103 KiB
C#
/******************************************************************************
|
|
* 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.Drawing;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Windows.Forms;
|
|
using WeifenLuo.WinFormsUI.Docking;
|
|
using renderdocui.Code;
|
|
using renderdocui.Controls;
|
|
using renderdocui.Windows.Dialogs;
|
|
using renderdoc;
|
|
using System.Threading;
|
|
|
|
namespace renderdocui.Windows
|
|
{
|
|
public partial class TextureViewer : DockContent, ILogViewerForm
|
|
{
|
|
#region Privates
|
|
|
|
private Core m_Core;
|
|
|
|
private ReplayOutput m_Output = null;
|
|
|
|
private TextureDisplay m_TexDisplay = new TextureDisplay();
|
|
|
|
private ToolStripControlHost depthStencilToolstrip = null;
|
|
|
|
private DockContent m_PreviewPanel = null;
|
|
private DockContent m_TexlistDockPanel = null;
|
|
|
|
private FileSystemWatcher m_FSWatcher = null;
|
|
|
|
public enum FollowType { RT_UAV, Depth, PSResource }
|
|
struct Following
|
|
{
|
|
public FollowType Type;
|
|
public int index;
|
|
|
|
public Following(FollowType t, int i) { Type = t; index = i; }
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return Type.GetHashCode() + index.GetHashCode();
|
|
}
|
|
public override bool Equals(object obj)
|
|
{
|
|
return obj is Following && this == (Following)obj;
|
|
}
|
|
public static bool operator ==(Following s1, Following s2)
|
|
{
|
|
return s1.Type == s2.Type && s1.index == s2.index;
|
|
}
|
|
public static bool operator !=(Following s1, Following s2)
|
|
{
|
|
return !(s1 == s2);
|
|
}
|
|
|
|
public UInt32 GetFirstArraySlice(Core core)
|
|
{
|
|
// todo, implement this better for GL :(
|
|
|
|
if (core.APIProps.pipelineType == APIPipelineStateType.D3D11)
|
|
{
|
|
D3D11PipelineState.ShaderStage.ResourceView view = null;
|
|
|
|
if (Type == FollowType.RT_UAV)
|
|
{
|
|
view = core.CurD3D11PipelineState.m_OM.RenderTargets[index];
|
|
|
|
if (view.Resource == ResourceId.Null && index >= core.CurD3D11PipelineState.m_OM.UAVStartSlot)
|
|
view = core.CurD3D11PipelineState.m_OM.UAVs[index - core.CurD3D11PipelineState.m_OM.UAVStartSlot];
|
|
}
|
|
else if (Type == FollowType.Depth)
|
|
{
|
|
view = core.CurD3D11PipelineState.m_OM.DepthTarget;
|
|
}
|
|
else if (Type == FollowType.PSResource)
|
|
{
|
|
view = core.CurD3D11PipelineState.m_PS.SRVs[index];
|
|
}
|
|
|
|
return view != null ? view.FirstArraySlice : 0;
|
|
}
|
|
else
|
|
{
|
|
if (Type == FollowType.PSResource)
|
|
{
|
|
return core.CurGLPipelineState.Textures[index].FirstSlice;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public ResourceId GetResourceId(Core core)
|
|
{
|
|
ResourceId id = ResourceId.Null;
|
|
|
|
if (Type == FollowType.RT_UAV)
|
|
{
|
|
var outputs = core.CurPipelineState.GetOutputTargets();
|
|
if(outputs.Length > index)
|
|
id = outputs[index];
|
|
}
|
|
else if (Type == FollowType.Depth)
|
|
{
|
|
id = core.CurPipelineState.OutputDepth;
|
|
}
|
|
else if (Type == FollowType.PSResource)
|
|
{
|
|
var res = core.CurPipelineState.GetResources(ShaderStageType.Pixel);
|
|
if(res.Length > index)
|
|
id = res[index];
|
|
}
|
|
|
|
return id;
|
|
}
|
|
}
|
|
private Following m_Following = new Following(FollowType.RT_UAV, 0);
|
|
|
|
#endregion
|
|
|
|
public TextureViewer(Core core)
|
|
{
|
|
m_Core = core;
|
|
|
|
InitializeComponent();
|
|
|
|
Icon = global::renderdocui.Properties.Resources.icon;
|
|
|
|
textureList.m_Core = core;
|
|
textureList.GoIconClick += new EventHandler<GoIconClickEventArgs>(textureList_GoIconClick);
|
|
|
|
UI_SetupToolstrips();
|
|
UI_SetupDocks();
|
|
UI_UpdateTextureDetails();
|
|
statusLabel.Text = "";
|
|
zoomOption.SelectedText = "";
|
|
mipLevel.Enabled = false;
|
|
sliceFace.Enabled = false;
|
|
|
|
PixelPicked = false;
|
|
|
|
mainLayout.Dock = DockStyle.Fill;
|
|
|
|
render.Painting = true;
|
|
pixelContext.Painting = true;
|
|
|
|
saveTex.Enabled = false;
|
|
|
|
DockHandler.GetPersistStringCallback = PersistString;
|
|
|
|
renderContainer.MouseWheelHandler = render_MouseWheel;
|
|
render.MouseWheel += render_MouseWheel;
|
|
renderContainer.MouseDown += render_MouseClick;
|
|
renderContainer.MouseMove += render_MouseMove;
|
|
|
|
render.KeyHandler = render_KeyDown;
|
|
|
|
rangeHistogram.RangeUpdated += new EventHandler<RangeHistogramEventArgs>(rangeHistogram_RangeUpdated);
|
|
|
|
this.DoubleBuffered = true;
|
|
|
|
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
|
|
|
channels.SelectedIndex = 0;
|
|
|
|
FitToWindow = true;
|
|
overlay.SelectedIndex = 0;
|
|
m_Following = new Following(FollowType.RT_UAV, 0);
|
|
|
|
texturefilter.SelectedIndex = 0;
|
|
|
|
if (m_Core.LogLoaded)
|
|
OnLogfileLoaded();
|
|
}
|
|
|
|
private void UI_SetupDocks()
|
|
{
|
|
m_PreviewPanel = Helpers.WrapDockContent(dockPanel, renderToolstripContainer, "Current");
|
|
m_PreviewPanel.DockState = DockState.Document;
|
|
m_PreviewPanel.AllowEndUserDocking = false;
|
|
m_PreviewPanel.Show();
|
|
|
|
m_PreviewPanel.CloseButton = false;
|
|
m_PreviewPanel.CloseButtonVisible = false;
|
|
|
|
m_PreviewPanel.DockHandler.TabPageContextMenuStrip = tabContextMenu;
|
|
|
|
dockPanel.ActiveDocumentChanged += new EventHandler(dockPanel_ActiveDocumentChanged);
|
|
|
|
var w3 = Helpers.WrapDockContent(dockPanel, texPanel, "PS Resources");
|
|
w3.DockAreas &= ~DockAreas.Document;
|
|
w3.DockState = DockState.DockRight;
|
|
w3.Show();
|
|
|
|
w3.CloseButton = false;
|
|
w3.CloseButtonVisible = false;
|
|
|
|
var w5 = Helpers.WrapDockContent(dockPanel, rtPanel, "OM Targets");
|
|
w5.DockAreas &= ~DockAreas.Document;
|
|
w5.DockState = DockState.DockRight;
|
|
w5.Show(w3.Pane, w3);
|
|
|
|
w5.CloseButton = false;
|
|
w5.CloseButtonVisible = false;
|
|
|
|
m_TexlistDockPanel = Helpers.WrapDockContent(dockPanel, texlistContainer, "Texture List");
|
|
m_TexlistDockPanel.DockAreas &= ~DockAreas.Document;
|
|
m_TexlistDockPanel.DockState = DockState.DockLeft;
|
|
m_TexlistDockPanel.Hide();
|
|
|
|
m_TexlistDockPanel.HideOnClose = true;
|
|
|
|
var w4 = Helpers.WrapDockContent(dockPanel, pixelContextPanel, "Pixel Context");
|
|
w4.DockAreas &= ~DockAreas.Document;
|
|
w4.Show(w3.Pane, DockAlignment.Bottom, 0.3);
|
|
|
|
w4.CloseButton = false;
|
|
w4.CloseButtonVisible = false;
|
|
}
|
|
|
|
private void UI_SetupToolstrips()
|
|
{
|
|
int idx = rangeStrip.Items.IndexOf(rangeWhite);
|
|
rangeStrip.Items.Insert(idx, new ToolStripControlHost(rangeHistogram));
|
|
|
|
for (int i = 0; i < channelStrip.Items.Count; i++)
|
|
{
|
|
if (channelStrip.Items[i] == mulSep)
|
|
{
|
|
depthStencilToolstrip = new ToolStripControlHost(depthstencilPanel);
|
|
channelStrip.Items.Insert(i, depthStencilToolstrip);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class PersistData
|
|
{
|
|
public static int currentPersistVersion = 4;
|
|
public int persistVersion = currentPersistVersion;
|
|
|
|
public string panelLayout;
|
|
public FloatVector darkBack = new FloatVector(0, 0, 0, 0);
|
|
public FloatVector lightBack = new FloatVector(0, 0, 0, 0);
|
|
|
|
public static PersistData GetDefaults()
|
|
{
|
|
PersistData data = new PersistData();
|
|
|
|
data.panelLayout = "";
|
|
data.darkBack = new FloatVector(0, 0, 0, 0);
|
|
data.lightBack = new FloatVector(0, 0, 0, 0);
|
|
|
|
return data;
|
|
}
|
|
}
|
|
|
|
public void InitFromPersistString(string str)
|
|
{
|
|
PersistData data = null;
|
|
|
|
try
|
|
{
|
|
if (str.Length > GetType().ToString().Length)
|
|
{
|
|
var reader = new StringReader(str.Substring(GetType().ToString().Length));
|
|
|
|
System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(PersistData));
|
|
data = (PersistData)xs.Deserialize(reader);
|
|
|
|
reader.Close();
|
|
}
|
|
}
|
|
catch (System.Xml.XmlException)
|
|
{
|
|
}
|
|
catch(InvalidOperationException)
|
|
{
|
|
// don't need to handle it. Leave data null and pick up defaults below
|
|
}
|
|
|
|
if (data == null || data.persistVersion != PersistData.currentPersistVersion)
|
|
{
|
|
data = PersistData.GetDefaults();
|
|
}
|
|
|
|
ApplyPersistData(data);
|
|
}
|
|
|
|
private IDockContent GetContentFromPersistString(string persistString)
|
|
{
|
|
Control[] persistors = {
|
|
renderToolstripContainer,
|
|
texPanel,
|
|
rtPanel,
|
|
texlistContainer,
|
|
pixelContextPanel
|
|
};
|
|
|
|
foreach(var p in persistors)
|
|
if (persistString == p.Name && p.Parent is IDockContent && (p.Parent as DockContent).DockPanel == null)
|
|
return p.Parent as IDockContent;
|
|
|
|
return null;
|
|
}
|
|
|
|
private string onloadLayout = "";
|
|
private FloatVector darkBack = new FloatVector(0, 0, 0, 0);
|
|
private FloatVector lightBack = new FloatVector(0, 0, 0, 0);
|
|
|
|
private void ApplyPersistData(PersistData data)
|
|
{
|
|
onloadLayout = data.panelLayout;
|
|
darkBack = data.darkBack;
|
|
lightBack = data.lightBack;
|
|
}
|
|
|
|
private void TextureViewer_Load(object sender, EventArgs e)
|
|
{
|
|
if (onloadLayout != "")
|
|
{
|
|
Control[] persistors = {
|
|
renderToolstripContainer,
|
|
texPanel,
|
|
rtPanel,
|
|
texlistContainer,
|
|
pixelContextPanel
|
|
};
|
|
|
|
foreach (var p in persistors)
|
|
(p.Parent as DockContent).DockPanel = null;
|
|
|
|
var enc = new UnicodeEncoding();
|
|
using (var strm = new MemoryStream(enc.GetBytes(onloadLayout)))
|
|
{
|
|
strm.Flush();
|
|
strm.Position = 0;
|
|
|
|
dockPanel.LoadFromXml(strm, new DeserializeDockContent(GetContentFromPersistString));
|
|
}
|
|
|
|
onloadLayout = "";
|
|
}
|
|
|
|
if (darkBack.x != lightBack.x)
|
|
{
|
|
backcolorPick.Checked = false;
|
|
checkerBack.Checked = true;
|
|
}
|
|
else
|
|
{
|
|
backcolorPick.Checked = true;
|
|
checkerBack.Checked = false;
|
|
|
|
colorDialog.Color = Color.FromArgb((int)(255 * darkBack.x),
|
|
(int)(255 * darkBack.y),
|
|
(int)(255 * darkBack.z));
|
|
}
|
|
}
|
|
|
|
private string PersistString()
|
|
{
|
|
var writer = new StringWriter();
|
|
|
|
writer.Write(GetType().ToString());
|
|
|
|
PersistData data = new PersistData();
|
|
|
|
// passing in a MemoryStream gets disposed - can't see a way to retrieve this
|
|
// in-memory.
|
|
var enc = new UnicodeEncoding();
|
|
var path = Path.GetTempFileName();
|
|
dockPanel.SaveAsXml(path, "", enc);
|
|
data.panelLayout = File.ReadAllText(path, enc);
|
|
File.Delete(path);
|
|
|
|
data.darkBack = darkBack;
|
|
data.lightBack = lightBack;
|
|
|
|
System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(PersistData));
|
|
xs.Serialize(writer, data);
|
|
|
|
return writer.ToString();
|
|
}
|
|
|
|
private bool DisableThumbnails { get { return m_Core != null && m_Core.Config != null &&
|
|
m_Core.Config.TextureViewer_DisableThumbnails; } }
|
|
|
|
#region Public Functions
|
|
|
|
private Dictionary<ResourceId, DockContent> lockedTabs = new Dictionary<ResourceId, DockContent>();
|
|
|
|
public void ViewTexture(ResourceId ID, bool focus)
|
|
{
|
|
TextureViewer_Load(null, null);
|
|
|
|
if (lockedTabs.ContainsKey(ID))
|
|
{
|
|
if (!lockedTabs[ID].IsDisposed && !lockedTabs[ID].IsHidden)
|
|
{
|
|
if (focus)
|
|
Show();
|
|
|
|
lockedTabs[ID].Show();
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
return;
|
|
}
|
|
|
|
lockedTabs.Remove(ID);
|
|
}
|
|
|
|
for (int i = 0; i < m_Core.CurTextures.Length; i++)
|
|
{
|
|
if (m_Core.CurTextures[i].ID == ID)
|
|
{
|
|
FetchTexture current = m_Core.CurTextures[i];
|
|
|
|
var newPanel = Helpers.WrapDockContent(dockPanel, renderToolstripContainer, current.name);
|
|
|
|
newPanel.DockState = DockState.Document;
|
|
newPanel.AllowEndUserDocking = false;
|
|
|
|
newPanel.Icon = Icon.FromHandle(global::renderdocui.Properties.Resources.page_white_link.GetHicon());
|
|
|
|
newPanel.Tag = current;
|
|
|
|
newPanel.DockHandler.TabPageContextMenuStrip = tabContextMenu;
|
|
newPanel.FormClosing += new FormClosingEventHandler(PreviewPanel_FormClosing);
|
|
|
|
newPanel.Show(m_PreviewPanel.Pane, null);
|
|
|
|
newPanel.Show();
|
|
|
|
if (focus)
|
|
Show();
|
|
|
|
lockedTabs.Add(ID, newPanel);
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < m_Core.CurBuffers.Length; i++)
|
|
{
|
|
if (m_Core.CurBuffers[i].ID == ID)
|
|
{
|
|
var viewer = new BufferViewer(m_Core, false);
|
|
viewer.ViewRawBuffer(ID);
|
|
viewer.Show(DockPanel);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Custom Shader handling
|
|
|
|
private List<string> m_CustomShadersBusy = new List<string>();
|
|
private Dictionary<string, ResourceId> m_CustomShaders = new Dictionary<string, ResourceId>();
|
|
private Dictionary<string, ShaderViewer> m_CustomShaderEditor = new Dictionary<string, ShaderViewer>();
|
|
|
|
private void ReloadCustomShaders(string filter)
|
|
{
|
|
if (!m_Core.LogLoaded) return;
|
|
|
|
if (filter == "")
|
|
{
|
|
var shaders = m_CustomShaders.Values.ToArray();
|
|
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
foreach (var s in shaders)
|
|
r.FreeCustomShader(s);
|
|
});
|
|
|
|
customShader.Items.Clear();
|
|
m_CustomShaders.Clear();
|
|
}
|
|
else
|
|
{
|
|
var fn = Path.GetFileNameWithoutExtension(filter);
|
|
var key = fn.ToLowerInvariant();
|
|
|
|
if (m_CustomShaders.ContainsKey(key))
|
|
{
|
|
if (m_CustomShadersBusy.Contains(key))
|
|
return;
|
|
|
|
ResourceId freed = m_CustomShaders[key];
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
r.FreeCustomShader(freed);
|
|
});
|
|
|
|
m_CustomShaders.Remove(key);
|
|
|
|
var text = customShader.Text;
|
|
|
|
for (int i = 0; i < customShader.Items.Count; i++)
|
|
{
|
|
if (customShader.Items[i].ToString() == fn)
|
|
{
|
|
customShader.Items.RemoveAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
customShader.Text = text;
|
|
}
|
|
}
|
|
|
|
foreach (var f in Directory.EnumerateFiles(Core.ConfigDirectory, "*.hlsl"))
|
|
{
|
|
var fn = Path.GetFileNameWithoutExtension(f);
|
|
var key = fn.ToLowerInvariant();
|
|
|
|
if (!m_CustomShaders.ContainsKey(key))
|
|
{
|
|
string source = File.ReadAllText(f);
|
|
|
|
m_CustomShaders.Add(key, ResourceId.Null);
|
|
m_CustomShadersBusy.Add(key);
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
string errors = "";
|
|
|
|
ResourceId id = r.BuildCustomShader("main", source, 0, ShaderStageType.Pixel, out errors);
|
|
|
|
if (m_CustomShaderEditor.ContainsKey(key))
|
|
{
|
|
BeginInvoke((MethodInvoker)delegate
|
|
{
|
|
m_CustomShaderEditor[key].ShowErrors(errors);
|
|
});
|
|
}
|
|
|
|
BeginInvoke((MethodInvoker)delegate
|
|
{
|
|
customShader.Items.Add(fn);
|
|
m_CustomShaders[key] = id;
|
|
m_CustomShadersBusy.Remove(key);
|
|
|
|
customShader.AutoCompleteSource = AutoCompleteSource.None;
|
|
customShader.AutoCompleteSource = AutoCompleteSource.ListItems;
|
|
|
|
UI_UpdateChannels();
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private void customCreate_Click(object sender, EventArgs e)
|
|
{
|
|
if (customShader.Text == null || customShader.Text == "")
|
|
{
|
|
MessageBox.Show("No name entered.\nEnter a name in the textbox.", "Error Creating Shader", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
if (m_CustomShaders.ContainsKey(customShader.Text.ToLowerInvariant()))
|
|
{
|
|
MessageBox.Show("Selected shader already exists.\nEnter a new name in the textbox.", "Error Creating Shader", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
var path = Path.Combine(Core.ConfigDirectory, customShader.Text + ".hlsl");
|
|
File.WriteAllText(path, "float4 main(float4 pos : SV_Position, float4 uv : TEXCOORD0) : SV_Target0\n{\n\treturn float4(0,0,0,1);\n}\n");
|
|
|
|
// auto-open edit window
|
|
customEdit_Click(sender, e);
|
|
}
|
|
|
|
private void customEdit_Click(object sender, EventArgs e)
|
|
{
|
|
var filename = customShader.Text;
|
|
|
|
var files = new Dictionary<string, string>();
|
|
files.Add(filename, File.ReadAllText(Path.Combine(Core.ConfigDirectory, filename + ".hlsl")));
|
|
ShaderViewer s = new ShaderViewer(m_Core, true, "Custom Shader", files,
|
|
|
|
// Save Callback
|
|
(ShaderViewer viewer, Dictionary<string, string> updatedfiles) =>
|
|
{
|
|
foreach (var f in updatedfiles)
|
|
{
|
|
var path = Path.Combine(Core.ConfigDirectory, f.Key + ".hlsl");
|
|
File.WriteAllText(path, f.Value);
|
|
}
|
|
},
|
|
|
|
// Close Callback
|
|
() =>
|
|
{
|
|
m_CustomShaderEditor.Remove(filename);
|
|
});
|
|
|
|
m_CustomShaderEditor[customShader.Text] = s;
|
|
|
|
s.Show(this.DockPanel);
|
|
}
|
|
|
|
private void customDelete_Click(object sender, EventArgs e)
|
|
{
|
|
if (customShader.Text == null || customShader.Text == "")
|
|
{
|
|
MessageBox.Show("No shader selected.\nSelect a custom shader from the drop-down", "Error Deleting Shader", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
if (!m_CustomShaders.ContainsKey(customShader.Text.ToLowerInvariant()))
|
|
{
|
|
MessageBox.Show("Selected shader doesn't exist.\nSelect a custom shader from the drop-down", "Error Deleting Shader", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
DialogResult res = MessageBox.Show(String.Format("Really delete {0}?", customShader.Text), "Deleting Custom Shader", MessageBoxButtons.YesNoCancel);
|
|
|
|
if (res == DialogResult.Yes)
|
|
{
|
|
var path = Path.Combine(Core.ConfigDirectory, customShader.Text + ".hlsl");
|
|
if(!File.Exists(path))
|
|
{
|
|
MessageBox.Show(String.Format("Shader file {0} can't be found.\nSelect a custom shader from the drop-down", customShader.Text),
|
|
"Error Deleting Shader", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
File.Delete(path);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
MessageBox.Show(String.Format("Error deleting shader {0}.\nSelect a custom shader from the drop-down", customShader.Text),
|
|
"Error Deleting Shader", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
customShader.Text = "";
|
|
UI_UpdateChannels();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ILogViewerForm
|
|
|
|
public void OnLogfileLoaded()
|
|
{
|
|
var outConfig = new OutputConfig();
|
|
outConfig.m_Type = OutputType.TexDisplay;
|
|
|
|
saveTex.Enabled = true;
|
|
|
|
m_Following = new Following(FollowType.RT_UAV, 0);
|
|
|
|
IntPtr contextHandle = pixelContext.Handle;
|
|
IntPtr renderHandle = render.Handle;
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
m_Output = r.CreateOutput(renderHandle);
|
|
m_Output.SetPixelContext(contextHandle);
|
|
m_Output.SetOutputConfig(outConfig);
|
|
|
|
this.BeginInvoke(new Action(UI_CreateThumbnails));
|
|
});
|
|
|
|
m_FSWatcher = new FileSystemWatcher(Core.ConfigDirectory, "*.hlsl");
|
|
m_FSWatcher.EnableRaisingEvents = true;
|
|
m_FSWatcher.Changed += new FileSystemEventHandler(CustomShaderModified);
|
|
m_FSWatcher.Renamed += new RenamedEventHandler(CustomShaderModified);
|
|
m_FSWatcher.Created += new FileSystemEventHandler(CustomShaderModified);
|
|
m_FSWatcher.Deleted += new FileSystemEventHandler(CustomShaderModified);
|
|
ReloadCustomShaders("");
|
|
|
|
texturefilter.SelectedIndex = 0;
|
|
texturefilter.Text = "";
|
|
textureList.FillTextureList("", true, true);
|
|
|
|
m_TexDisplay.darkBackgroundColour = darkBack;
|
|
m_TexDisplay.lightBackgroundColour = lightBack;
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
}
|
|
|
|
void CustomShaderModified(object sender, FileSystemEventArgs e)
|
|
{
|
|
Thread.Sleep(5);
|
|
BeginInvoke((MethodInvoker)delegate
|
|
{
|
|
ReloadCustomShaders(e.Name);
|
|
});
|
|
}
|
|
|
|
public void OnLogfileClosed()
|
|
{
|
|
if (IsDisposed) return;
|
|
|
|
if(m_FSWatcher != null)
|
|
m_FSWatcher.EnableRaisingEvents = false;
|
|
m_FSWatcher = null;
|
|
|
|
m_Output = null;
|
|
|
|
saveTex.Enabled = false;
|
|
|
|
rtPanel.ClearThumbnails();
|
|
texPanel.ClearThumbnails();
|
|
|
|
texturefilter.SelectedIndex = 0;
|
|
|
|
m_TexDisplay = new TextureDisplay();
|
|
m_TexDisplay.darkBackgroundColour = darkBack;
|
|
m_TexDisplay.lightBackgroundColour = lightBack;
|
|
|
|
PixelPicked = false;
|
|
|
|
statusLabel.Text = "";
|
|
m_PreviewPanel.Text = "Current";
|
|
zoomOption.Text = "";
|
|
mipLevel.Items.Clear();
|
|
sliceFace.Items.Clear();
|
|
rangeHistogram.SetRange(0.0f, 1.0f);
|
|
|
|
channels.SelectedIndex = 0;
|
|
overlay.SelectedIndex = 0;
|
|
|
|
customShader.Items.Clear();
|
|
m_CustomShaders.Clear();
|
|
|
|
textureList.Items.Clear();
|
|
|
|
render.Invalidate();
|
|
|
|
renderHScroll.Enabled = false;
|
|
renderVScroll.Enabled = false;
|
|
|
|
hoverSwatch.BackColor = Color.Black;
|
|
|
|
var tabs = m_PreviewPanel.Pane.TabStripControl.Tabs;
|
|
|
|
for (int i = 0; i < tabs.Count; i++)
|
|
{
|
|
if (tabs[i].Content != m_PreviewPanel)
|
|
{
|
|
(tabs[i].Content as DockContent).Close();
|
|
i--;
|
|
}
|
|
}
|
|
|
|
(m_PreviewPanel as DockContent).Show();
|
|
|
|
UI_UpdateTextureDetails();
|
|
UI_UpdateChannels();
|
|
}
|
|
|
|
public void OnEventSelected(UInt32 frameID, UInt32 eventID)
|
|
{
|
|
if (IsDisposed) return;
|
|
|
|
UI_OnTextureSelectionChanged();
|
|
|
|
ResourceId[] RTs = m_Core.CurPipelineState.GetOutputTargets();
|
|
ResourceId Depth = m_Core.CurPipelineState.OutputDepth;
|
|
ResourceId[] Texs = m_Core.CurPipelineState.GetResources(ShaderStageType.Pixel);
|
|
|
|
ShaderReflection details = m_Core.CurPipelineState.GetShaderReflection(ShaderStageType.Pixel);
|
|
ShaderBindpointMapping mapping = m_Core.CurPipelineState.GetBindpointMapping(ShaderStageType.Pixel);
|
|
|
|
uint firstuav = uint.MaxValue;
|
|
|
|
if (m_Core.APIProps.pipelineType == APIPipelineStateType.D3D11 &&
|
|
m_Core.CurD3D11PipelineState != null &&
|
|
m_Core.CurD3D11PipelineState.m_OM.UAVs[0].Resource != ResourceId.Null)
|
|
firstuav = m_Core.CurD3D11PipelineState.m_OM.UAVStartSlot;
|
|
|
|
if (m_Output == null) return;
|
|
|
|
int i = 0;
|
|
foreach (var prev in rtPanel.Thumbnails)
|
|
{
|
|
if (prev.SlotName == "D" && Depth != ResourceId.Null)
|
|
{
|
|
FetchTexture tex = null;
|
|
foreach (var t in m_Core.CurTextures)
|
|
if (t.ID == Depth)
|
|
tex = t;
|
|
|
|
FetchBuffer buf = null;
|
|
foreach (var b in m_Core.CurBuffers)
|
|
if (b.ID == Depth)
|
|
buf = b;
|
|
|
|
if (tex != null)
|
|
{
|
|
prev.Init(tex.name, tex.width, tex.height, tex.depth, tex.mips);
|
|
IntPtr handle = prev.ThumbnailHandle;
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer rep) =>
|
|
{
|
|
m_Output.AddThumbnail(handle, DisableThumbnails ? ResourceId.Null : Depth);
|
|
});
|
|
}
|
|
else if (buf != null)
|
|
{
|
|
prev.Init(buf.name, buf.length, 0, 0, Math.Max(1, buf.structureSize));
|
|
IntPtr handle = prev.ThumbnailHandle;
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer rep) =>
|
|
{
|
|
m_Output.AddThumbnail(handle, ResourceId.Null);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
prev.Init();
|
|
}
|
|
|
|
prev.Tag = new Following(FollowType.Depth, 0);
|
|
prev.Visible = true;
|
|
}
|
|
else if (i < RTs.Length && RTs[i] != ResourceId.Null)
|
|
{
|
|
FetchTexture tex = null;
|
|
foreach (var t in m_Core.CurTextures)
|
|
if (t.ID == RTs[i])
|
|
tex = t;
|
|
|
|
FetchBuffer buf = null;
|
|
foreach (var b in m_Core.CurBuffers)
|
|
if (b.ID == RTs[i])
|
|
buf = b;
|
|
|
|
string bindName = "";
|
|
|
|
if (details != null && i >= firstuav)
|
|
{
|
|
foreach (var bind in details.Resources)
|
|
{
|
|
if (mapping.Resources[bind.bindPoint].bind == i && bind.IsUAV)
|
|
{
|
|
bindName = "<" + bind.name + ">";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tex != null)
|
|
{
|
|
prev.Init(!tex.customName && bindName != "" ? bindName : tex.name, tex.width, tex.height, tex.depth, tex.mips);
|
|
IntPtr handle = prev.ThumbnailHandle;
|
|
ResourceId id = RTs[i];
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer rep) =>
|
|
{
|
|
m_Output.AddThumbnail(handle, DisableThumbnails ? ResourceId.Null : id);
|
|
});
|
|
}
|
|
else if (buf != null)
|
|
{
|
|
prev.Init(!buf.customName && bindName != "" ? bindName : buf.name, buf.length, 0, 0, Math.Max(1, buf.structureSize));
|
|
IntPtr handle = prev.ThumbnailHandle;
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer rep) =>
|
|
{
|
|
m_Output.AddThumbnail(handle, ResourceId.Null);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
prev.Init();
|
|
}
|
|
|
|
prev.Tag = new Following(FollowType.RT_UAV, i);
|
|
|
|
if (i >= firstuav)
|
|
prev.SlotName = "U" + i;
|
|
else
|
|
prev.SlotName = i.ToString();
|
|
|
|
prev.Visible = true;
|
|
}
|
|
else if (prev.Selected)
|
|
{
|
|
prev.Init();
|
|
IntPtr handle = prev.ThumbnailHandle;
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer rep) =>
|
|
{
|
|
m_Output.AddThumbnail(handle, ResourceId.Null);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
prev.Init();
|
|
prev.Visible = false;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
rtPanel.RefreshLayout();
|
|
|
|
i = 0;
|
|
foreach (var prev in texPanel.Thumbnails)
|
|
{
|
|
if (i >= Texs.Length)
|
|
break;
|
|
|
|
bool used = false;
|
|
|
|
string bindName = "";
|
|
|
|
if (details != null)
|
|
{
|
|
foreach (var bind in details.Resources)
|
|
{
|
|
if (mapping.Resources[bind.bindPoint].bind == i && bind.IsSRV)
|
|
{
|
|
used = true;
|
|
bindName = "<" + bind.name + ">";
|
|
}
|
|
}
|
|
}
|
|
|
|
// show if
|
|
if (used || // it's referenced by the shader - regardless of empty or not
|
|
(showDisabled.Checked && !used && Texs[i] != ResourceId.Null) || // it's bound, but not referenced, and we have "show disabled"
|
|
(showEmpty.Checked && Texs[i] == ResourceId.Null) // it's empty, and we have "show empty"
|
|
)
|
|
{
|
|
FetchTexture tex = null;
|
|
foreach (var t in m_Core.CurTextures)
|
|
if (t.ID == Texs[i])
|
|
tex = t;
|
|
|
|
if (tex != null)
|
|
{
|
|
prev.Init(!tex.customName && bindName != "" ? bindName : tex.name, tex.width, tex.height, tex.depth, tex.mips);
|
|
IntPtr handle = prev.ThumbnailHandle;
|
|
ResourceId id = Texs[i];
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer rep) =>
|
|
{
|
|
m_Output.AddThumbnail(handle, DisableThumbnails ? ResourceId.Null : id);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
prev.Init();
|
|
}
|
|
|
|
prev.Tag = new Following(FollowType.PSResource, i);
|
|
prev.Visible = true;
|
|
}
|
|
else if (prev.Selected)
|
|
{
|
|
FetchTexture tex = null;
|
|
foreach (var t in m_Core.CurTextures)
|
|
if (t.ID == Texs[i])
|
|
tex = t;
|
|
|
|
IntPtr handle = prev.ThumbnailHandle;
|
|
if (Texs[i] == ResourceId.Null || tex == null)
|
|
prev.Init();
|
|
else
|
|
prev.Init("Unused", tex.width, tex.height, tex.depth, tex.mips);
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer rep) =>
|
|
{
|
|
m_Output.AddThumbnail(handle, ResourceId.Null);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
prev.Init();
|
|
prev.Visible = false;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
texPanel.RefreshLayout();
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
|
|
if(autoFit.Checked)
|
|
AutoFitRange();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Update UI state
|
|
|
|
private void UI_CreateThumbnails()
|
|
{
|
|
rtPanel.SuspendLayout();
|
|
texPanel.SuspendLayout();
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
var prev = new ResourcePreview(m_Core, m_Output);
|
|
prev.Anchor = AnchorStyles.Top | AnchorStyles.Bottom;
|
|
prev.SlotName = i.ToString();
|
|
prev.MouseClick += thumbsLayout_MouseClick;
|
|
prev.MouseDoubleClick += thumbsLayout_MouseDoubleClick;
|
|
rtPanel.AddThumbnail(prev);
|
|
|
|
if(i == 0)
|
|
prev.Selected = true;
|
|
}
|
|
|
|
{
|
|
var prev = new ResourcePreview(m_Core, m_Output);
|
|
prev.Anchor = AnchorStyles.Top | AnchorStyles.Bottom;
|
|
prev.SlotName = "D";
|
|
prev.MouseClick += thumbsLayout_MouseClick;
|
|
prev.MouseDoubleClick += thumbsLayout_MouseDoubleClick;
|
|
rtPanel.AddThumbnail(prev);
|
|
}
|
|
|
|
for (int i = 0; i < 128; i++)
|
|
{
|
|
var prev = new ResourcePreview(m_Core, m_Output);
|
|
prev.Anchor = AnchorStyles.Top | AnchorStyles.Bottom;
|
|
prev.SlotName = i.ToString();
|
|
prev.MouseClick += thumbsLayout_MouseClick;
|
|
prev.MouseDoubleClick += thumbsLayout_MouseDoubleClick;
|
|
texPanel.AddThumbnail(prev);
|
|
}
|
|
|
|
foreach (var c in rtPanel.Thumbnails)
|
|
c.Visible = false;
|
|
|
|
foreach (var c in texPanel.Thumbnails)
|
|
c.Visible = false;
|
|
|
|
rtPanel.ResumeLayout();
|
|
texPanel.ResumeLayout();
|
|
}
|
|
|
|
private void UI_OnTextureSelectionChanged()
|
|
{
|
|
FetchTexture tex = CurrentTexture;
|
|
|
|
if (tex == null) return;
|
|
|
|
if (m_TexDisplay.texid != tex.ID &&
|
|
m_Core.Config.TextureViewer_ResetRange)
|
|
{
|
|
rangeHistogram.RangeMin = 0.0f;
|
|
rangeHistogram.RangeMax = 1.0f;
|
|
rangeHistogram.BlackPoint = 0.0f;
|
|
rangeHistogram.WhitePoint = 1.0f;
|
|
}
|
|
|
|
m_TexDisplay.texid = tex.ID;
|
|
|
|
m_CurPixelValue = null;
|
|
m_CurRealValue = null;
|
|
|
|
ScrollPosition = ScrollPosition;
|
|
|
|
UI_UpdateStatusText();
|
|
|
|
mipLevel.Items.Clear();
|
|
sliceFace.Items.Clear();
|
|
|
|
if (tex.msSamp > 1)
|
|
{
|
|
for (int i = 0; i < tex.msSamp; i++)
|
|
mipLevel.Items.Add(String.Format("Sample {0}", i));
|
|
|
|
// add an option to display unweighted average resolved value,
|
|
// to get an idea of how the samples average
|
|
if(tex.format.compType != FormatComponentType.UInt &&
|
|
tex.format.compType != FormatComponentType.SInt &&
|
|
tex.format.compType != FormatComponentType.Depth &&
|
|
(tex.creationFlags & TextureCreationFlags.DSV) == 0)
|
|
mipLevel.Items.Add("Average val");
|
|
|
|
mipLevelLabel.Text = "Sample";
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < tex.mips; i++)
|
|
mipLevel.Items.Add(i + " - " + Math.Max(1, tex.width >> i) + "x" + Math.Max(1, tex.height >> i));
|
|
|
|
mipLevelLabel.Text = "Mip";
|
|
}
|
|
|
|
mipLevel.SelectedIndex = 0;
|
|
m_TexDisplay.mip = 0;
|
|
m_TexDisplay.sliceFace = 0;
|
|
|
|
if (tex.mips == 1 && tex.msSamp <= 1)
|
|
{
|
|
mipLevel.Enabled = false;
|
|
}
|
|
else
|
|
{
|
|
mipLevel.Enabled = true;
|
|
}
|
|
|
|
if (tex.numSubresources == tex.mips && tex.depth <= 1)
|
|
{
|
|
sliceFace.Enabled = false;
|
|
}
|
|
else
|
|
{
|
|
sliceFace.Enabled = true;
|
|
|
|
sliceFace.Visible = sliceFaceLabel.Visible = true;
|
|
|
|
String[] cubeFaces = { "X+", "X-", "Y+", "Y-", "Z+", "Z-" };
|
|
|
|
UInt32 numSlices = (Math.Max(1, tex.depth) * tex.numSubresources) / tex.mips;
|
|
|
|
for (UInt32 i = 0; i < numSlices; i++)
|
|
{
|
|
if (tex.cubemap)
|
|
{
|
|
String name = cubeFaces[i%6];
|
|
if (numSlices > 6)
|
|
name = string.Format("[{0}] {1}", (i / 6), cubeFaces[i%6]); // Front 1, Back 2, 3, 4 etc for cube arrays
|
|
sliceFace.Items.Add(name);
|
|
}
|
|
else
|
|
{
|
|
sliceFace.Items.Add("Slice " + i);
|
|
}
|
|
}
|
|
|
|
sliceFace.SelectedIndex = (int)m_Following.GetFirstArraySlice(m_Core);
|
|
}
|
|
|
|
UI_UpdateFittedScale();
|
|
|
|
//render.Width = (int)(CurrentTexDisplayWidth * m_TexDisplay.scale);
|
|
//render.Height = (int)(CurrentTexDisplayHeight * m_TexDisplay.scale);
|
|
|
|
UI_UpdateTextureDetails();
|
|
|
|
UI_UpdateChannels();
|
|
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
RT_UpdateVisualRange(r);
|
|
|
|
RT_UpdateAndDisplay(r);
|
|
|
|
if (tex.ID != ResourceId.Null)
|
|
{
|
|
var us = r.GetUsage(tex.ID);
|
|
|
|
var tb = m_Core.TimelineBar;
|
|
|
|
if (tb != null && tb.Visible && !tb.IsDisposed)
|
|
{
|
|
this.BeginInvoke(new Action(() =>
|
|
{
|
|
tb.HighlightResource(tex.ID, tex.name, us);
|
|
}));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void dockPanel_ActiveDocumentChanged(object sender, EventArgs e)
|
|
{
|
|
var d = dockPanel.ActiveDocument as DockContent;
|
|
|
|
if (d == null) return;
|
|
|
|
if (d.Visible)
|
|
d.Controls.Add(renderToolstripContainer);
|
|
|
|
UI_OnTextureSelectionChanged();
|
|
}
|
|
|
|
void PreviewPanel_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
if ((sender as Control).Visible == false && renderToolstripContainer.Parent != sender)
|
|
return;
|
|
|
|
var tabs = m_PreviewPanel.Pane.TabStripControl.Tabs;
|
|
|
|
for (int i = 0; i < tabs.Count; i++)
|
|
{
|
|
if (tabs[i].Content == sender)
|
|
{
|
|
var dc = m_PreviewPanel;
|
|
|
|
if (i > 0)
|
|
{
|
|
dc = (tabs[i - 1].Content as DockContent);
|
|
}
|
|
else if (i < tabs.Count - 1)
|
|
{
|
|
dc = (tabs[i + 1].Content as DockContent);
|
|
}
|
|
|
|
dc.Controls.Add(renderToolstripContainer);
|
|
dc.Show();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_PreviewPanel.Controls.Add(renderToolstripContainer);
|
|
m_PreviewPanel.Show();
|
|
}
|
|
|
|
private void UI_UpdateTextureDetails()
|
|
{
|
|
texStatusDim.Text = "";
|
|
|
|
if (m_Core.CurTextures == null || CurrentTexture == null)
|
|
{
|
|
m_PreviewPanel.Text = "Unbound";
|
|
|
|
texStatusDim.Text = "";
|
|
return;
|
|
}
|
|
|
|
FetchTexture current = CurrentTexture;
|
|
|
|
ResourceId followID = m_Following.GetResourceId(m_Core);
|
|
|
|
{
|
|
FetchTexture tex = null;
|
|
foreach (var t in m_Core.CurTextures)
|
|
if (t.ID == followID)
|
|
tex = t;
|
|
|
|
if (tex != null)
|
|
{
|
|
switch (m_Following.Type)
|
|
{
|
|
case FollowType.RT_UAV:
|
|
m_PreviewPanel.Text = string.Format("Cur OM Target {0} - {1}", m_Following.index, tex.name);
|
|
break;
|
|
case FollowType.Depth:
|
|
m_PreviewPanel.Text = string.Format("Cur DSV - {0}", tex.name);
|
|
break;
|
|
case FollowType.PSResource:
|
|
m_PreviewPanel.Text = string.Format("Cur PS SRV {0} - {1}", m_Following.index, tex.name);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_PreviewPanel.Text = "Current";
|
|
|
|
if (followID == ResourceId.Null)
|
|
m_PreviewPanel.Text = "Unbound";
|
|
}
|
|
}
|
|
|
|
texStatusDim.Text = current.name + " - ";
|
|
|
|
if (current.dimension >= 1)
|
|
texStatusDim.Text += current.width;
|
|
if (current.dimension >= 2)
|
|
texStatusDim.Text += "x" + current.height;
|
|
if (current.dimension >= 3)
|
|
texStatusDim.Text += "x" + current.depth;
|
|
|
|
if (current.arraysize > 1)
|
|
texStatusDim.Text += "[" + current.arraysize + "]";
|
|
|
|
if(current.msQual > 0 || current.msSamp > 1)
|
|
texStatusDim.Text += string.Format(" MS{{{0}x {1}Q}}", current.msSamp, current.msQual);
|
|
|
|
texStatusDim.Text += " " + current.mips + " mips";
|
|
|
|
texStatusDim.Text += " - " + current.format.ToString();
|
|
}
|
|
|
|
private bool PixelPicked
|
|
{
|
|
get
|
|
{
|
|
return (m_CurPixelValue != null);
|
|
}
|
|
|
|
set
|
|
{
|
|
if (value == true)
|
|
{
|
|
debugPixelContext.Enabled = true;
|
|
toolTip.RemoveAll();
|
|
toolTip.SetToolTip(debugPixelContext, "Debug this pixel");
|
|
toolTip.SetToolTip(pixelHistory, "Show history for this pixel");
|
|
|
|
pixelHistory.Enabled = true;
|
|
}
|
|
else
|
|
{
|
|
m_CurPixelValue = null;
|
|
m_CurRealValue = null;
|
|
|
|
debugPixelContext.Enabled = false;
|
|
toolTip.RemoveAll();
|
|
toolTip.SetToolTip(debugPixelContext, "Right Click to choose a pixel");
|
|
toolTip.SetToolTip(pixelHistory, "Right Click to choose a pixel");
|
|
|
|
pixelHistory.Enabled = false;
|
|
}
|
|
|
|
pixelContext.Invalidate();
|
|
}
|
|
}
|
|
|
|
private void UI_UpdateStatusText()
|
|
{
|
|
if (textureList.InvokeRequired)
|
|
{
|
|
this.BeginInvoke(new Action(UI_UpdateStatusText));
|
|
return;
|
|
}
|
|
|
|
FetchTexture tex = CurrentTexture;
|
|
|
|
if (tex == null) return;
|
|
|
|
bool dsv = ((tex.creationFlags & TextureCreationFlags.DSV) != 0);
|
|
bool uintTex = (tex.format.compType == FormatComponentType.UInt);
|
|
bool sintTex = (tex.format.compType == FormatComponentType.SInt);
|
|
|
|
if (m_TexDisplay.overlay == TextureDisplayOverlay.QuadOverdrawPass ||
|
|
m_TexDisplay.overlay == TextureDisplayOverlay.QuadOverdrawDraw)
|
|
{
|
|
dsv = false;
|
|
uintTex = false;
|
|
sintTex = true;
|
|
}
|
|
|
|
if (m_CurHoverValue != null)
|
|
{
|
|
if (dsv || uintTex || sintTex)
|
|
{
|
|
hoverSwatch.BackColor = Color.Black;
|
|
}
|
|
else
|
|
{
|
|
float r = Helpers.Clamp(m_CurHoverValue.value.f[0], 0.0f, 1.0f);
|
|
float g = Helpers.Clamp(m_CurHoverValue.value.f[1], 0.0f, 1.0f);
|
|
float b = Helpers.Clamp(m_CurHoverValue.value.f[2], 0.0f, 1.0f);
|
|
|
|
if (tex.format.srgbCorrected || (tex.creationFlags & TextureCreationFlags.SwapBuffer) > 0)
|
|
{
|
|
r = (float)Math.Pow(r, 1.0f / 2.2f);
|
|
g = (float)Math.Pow(g, 1.0f / 2.2f);
|
|
b = (float)Math.Pow(b, 1.0f / 2.2f);
|
|
}
|
|
|
|
hoverSwatch.BackColor = Color.FromArgb((int)(255.0f * r), (int)(255.0f * g), (int)(255.0f * b));
|
|
}
|
|
}
|
|
|
|
int y = m_CurHoverPixel.Y >> (int)m_TexDisplay.mip;
|
|
if (m_TexDisplay.FlipY)
|
|
y = (int)tex.height - y;
|
|
|
|
string hoverCoords = String.Format("{0}, {1}", m_CurHoverPixel.X >> (int)m_TexDisplay.mip, y);
|
|
string statusText = "Hover - " + hoverCoords;
|
|
|
|
if (m_CurHoverPixel.X > tex.width || m_CurHoverPixel.Y > tex.height || m_CurHoverPixel.X < 0 || m_CurHoverPixel.Y < 0)
|
|
statusText = "Hover - [" + hoverCoords + "]";
|
|
|
|
if (m_CurPixelValue != null)
|
|
{
|
|
y = m_PickedPoint.Y >> (int)m_TexDisplay.mip;
|
|
if (m_TexDisplay.FlipY)
|
|
y = (int)tex.height - y;
|
|
|
|
statusText += " - Right click - " +
|
|
(m_PickedPoint.X >> (int)m_TexDisplay.mip) + "," + y + ": ";
|
|
|
|
PixelValue val = m_CurPixelValue;
|
|
|
|
if (m_TexDisplay.CustomShader != ResourceId.Null && m_CurRealValue != null)
|
|
{
|
|
statusText += Formatter.Format(val.value.f[0]) + ", " +
|
|
Formatter.Format(val.value.f[1]) + ", " +
|
|
Formatter.Format(val.value.f[2]) + ", " +
|
|
Formatter.Format(val.value.f[3]);
|
|
|
|
val = m_CurRealValue;
|
|
|
|
statusText += " (Real: ";
|
|
}
|
|
|
|
if (dsv)
|
|
{
|
|
statusText += "Depth ";
|
|
if (uintTex)
|
|
{
|
|
if(tex.format.compByteWidth == 2)
|
|
statusText += Formatter.Format(val.value.u16[0]);
|
|
else
|
|
statusText += Formatter.Format(val.value.u[0]);
|
|
}
|
|
else
|
|
{
|
|
statusText += Formatter.Format(val.value.f[0]);
|
|
}
|
|
statusText += String.Format(", Stencil {0} / 0x{0:X2}", (int)(255.0f * val.value.f[1]));
|
|
}
|
|
else
|
|
{
|
|
if (uintTex)
|
|
{
|
|
statusText += val.value.u[0].ToString() + ", " +
|
|
val.value.u[1].ToString() + ", " +
|
|
val.value.u[2].ToString() + ", " +
|
|
val.value.u[3].ToString();
|
|
}
|
|
else if (sintTex)
|
|
{
|
|
statusText += val.value.i[0].ToString() + ", " +
|
|
val.value.i[1].ToString() + ", " +
|
|
val.value.i[2].ToString() + ", " +
|
|
val.value.i[3].ToString();
|
|
}
|
|
else
|
|
{
|
|
statusText += Formatter.Format(val.value.f[0]) + ", " +
|
|
Formatter.Format(val.value.f[1]) + ", " +
|
|
Formatter.Format(val.value.f[2]) + ", " +
|
|
Formatter.Format(val.value.f[3]);
|
|
}
|
|
}
|
|
|
|
if (m_TexDisplay.CustomShader != ResourceId.Null)
|
|
statusText += ")";
|
|
|
|
PixelPicked = true;
|
|
}
|
|
else
|
|
{
|
|
statusText += " - Right click to pick a pixel";
|
|
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
if (m_Output != null)
|
|
m_Output.DisablePixelContext();
|
|
});
|
|
|
|
PixelPicked = false;
|
|
}
|
|
|
|
statusLabel.Text = statusText;
|
|
}
|
|
|
|
private void UI_UpdateChannels()
|
|
{
|
|
FetchTexture tex = CurrentTexture;
|
|
|
|
channelStrip.SuspendLayout();
|
|
|
|
if (tex != null && !tex.format.srgbCorrected)
|
|
gammaDisplay.Enabled = true;
|
|
else
|
|
gammaDisplay.Enabled = false;
|
|
|
|
m_TexDisplay.linearDisplayAsGamma = gammaDisplay.Checked;
|
|
|
|
if (tex != null && (tex.creationFlags & TextureCreationFlags.DSV) > 0 &&
|
|
(string)channels.SelectedItem != "Custom")
|
|
{
|
|
customRed.Visible = false;
|
|
customGreen.Visible = false;
|
|
customBlue.Visible = false;
|
|
customAlpha.Visible = false;
|
|
mulLabel.Visible = false;
|
|
hdrMul.Visible = false;
|
|
customShader.Visible = false;
|
|
customCreate.Visible = false;
|
|
customEdit.Visible = false;
|
|
customDelete.Visible = false;
|
|
depthStencilToolstrip.Visible = true;
|
|
|
|
backcolorPick.Visible = false;
|
|
checkerBack.Visible = false;
|
|
|
|
mulSep.Visible = false;
|
|
|
|
m_TexDisplay.Red = depthDisplay.Checked;
|
|
m_TexDisplay.Green = stencilDisplay.Checked;
|
|
m_TexDisplay.Blue = false;
|
|
m_TexDisplay.Alpha = false;
|
|
|
|
m_TexDisplay.HDRMul = -1.0f;
|
|
if (m_TexDisplay.CustomShader != ResourceId.Null) { m_CurPixelValue = null; m_CurRealValue = null; UI_UpdateStatusText(); }
|
|
m_TexDisplay.CustomShader = ResourceId.Null;
|
|
}
|
|
else if ((string)channels.SelectedItem == "RGBA" || !m_Core.LogLoaded)
|
|
{
|
|
customRed.Visible = true;
|
|
customGreen.Visible = true;
|
|
customBlue.Visible = true;
|
|
customAlpha.Visible = true;
|
|
mulLabel.Visible = false;
|
|
hdrMul.Visible = false;
|
|
customShader.Visible = false;
|
|
customCreate.Visible = false;
|
|
customEdit.Visible = false;
|
|
customDelete.Visible = false;
|
|
depthStencilToolstrip.Visible = false;
|
|
|
|
backcolorPick.Visible = true;
|
|
checkerBack.Visible = true;
|
|
|
|
mulSep.Visible = false;
|
|
|
|
m_TexDisplay.Red = customRed.Checked;
|
|
m_TexDisplay.Green = customGreen.Checked;
|
|
m_TexDisplay.Blue = customBlue.Checked;
|
|
m_TexDisplay.Alpha = customAlpha.Checked;
|
|
|
|
m_TexDisplay.HDRMul = -1.0f;
|
|
if (m_TexDisplay.CustomShader != ResourceId.Null) { m_CurPixelValue = null; m_CurRealValue = null; UI_UpdateStatusText(); }
|
|
m_TexDisplay.CustomShader = ResourceId.Null;
|
|
}
|
|
else if ((string)channels.SelectedItem == "RGBM")
|
|
{
|
|
customRed.Visible = true;
|
|
customGreen.Visible = true;
|
|
customBlue.Visible = true;
|
|
customAlpha.Visible = false;
|
|
mulLabel.Visible = true;
|
|
hdrMul.Visible = true;
|
|
customShader.Visible = false;
|
|
customCreate.Visible = false;
|
|
customEdit.Visible = false;
|
|
customDelete.Visible = false;
|
|
depthStencilToolstrip.Visible = false;
|
|
|
|
backcolorPick.Visible = false;
|
|
checkerBack.Visible = false;
|
|
|
|
mulSep.Visible = true;
|
|
|
|
m_TexDisplay.Red = customRed.Checked;
|
|
m_TexDisplay.Green = customGreen.Checked;
|
|
m_TexDisplay.Blue = customBlue.Checked;
|
|
m_TexDisplay.Alpha = false;
|
|
|
|
float mul = 32.0f;
|
|
|
|
if (!float.TryParse(hdrMul.Text, out mul))
|
|
hdrMul.Text = mul.ToString();
|
|
|
|
m_TexDisplay.HDRMul = mul;
|
|
if (m_TexDisplay.CustomShader != ResourceId.Null) { m_CurPixelValue = null; m_CurRealValue = null; UI_UpdateStatusText(); }
|
|
m_TexDisplay.CustomShader = ResourceId.Null;
|
|
}
|
|
else if ((string)channels.SelectedItem == "Custom")
|
|
{
|
|
customRed.Visible = false;
|
|
customGreen.Visible = false;
|
|
customBlue.Visible = false;
|
|
customAlpha.Visible = false;
|
|
mulLabel.Visible = false;
|
|
hdrMul.Visible = false;
|
|
customShader.Visible = true;
|
|
customCreate.Visible = true;
|
|
customEdit.Visible = true;
|
|
customDelete.Visible = true;
|
|
depthStencilToolstrip.Visible = false;
|
|
|
|
backcolorPick.Visible = false;
|
|
checkerBack.Visible = false;
|
|
|
|
mulSep.Visible = false;
|
|
|
|
m_TexDisplay.Red = customRed.Checked;
|
|
m_TexDisplay.Green = customGreen.Checked;
|
|
m_TexDisplay.Blue = customBlue.Checked;
|
|
m_TexDisplay.Alpha = customAlpha.Checked;
|
|
|
|
m_TexDisplay.HDRMul = -1.0f;
|
|
|
|
m_TexDisplay.CustomShader = ResourceId.Null;
|
|
if (m_CustomShaders.ContainsKey(customShader.Text.ToLowerInvariant()))
|
|
{
|
|
if (m_TexDisplay.CustomShader == ResourceId.Null) { m_CurPixelValue = null; m_CurRealValue = null; UI_UpdateStatusText(); }
|
|
m_TexDisplay.CustomShader = m_CustomShaders[customShader.Text.ToLowerInvariant()];
|
|
customDelete.Enabled = customEdit.Enabled = true;
|
|
customCreate.Enabled = false;
|
|
}
|
|
else
|
|
{
|
|
customDelete.Enabled = customEdit.Enabled = false;
|
|
customCreate.Enabled = true;
|
|
}
|
|
}
|
|
|
|
m_TexDisplay.FlipY = flip_y.Checked;
|
|
|
|
channelStrip.ResumeLayout();
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateVisualRange);
|
|
}
|
|
|
|
private void RT_UpdateAndDisplay(ReplayRenderer r)
|
|
{
|
|
if (m_Output == null) return;
|
|
|
|
m_Output.SetTextureDisplay(m_TexDisplay);
|
|
|
|
render.Invalidate();
|
|
}
|
|
|
|
private void DrawCheckerboard(Graphics g, Rectangle rect)
|
|
{
|
|
int numX = (int)Math.Ceiling((float)rect.Width / 64.0f);
|
|
int numY = (int)Math.Ceiling((float)rect.Height / 64.0f);
|
|
|
|
Brush dark = new SolidBrush(Color.FromArgb((int)(255 * Math.Sqrt(Math.Sqrt(darkBack.x))),
|
|
(int)(255 * Math.Sqrt(Math.Sqrt(darkBack.y))),
|
|
(int)(255 * Math.Sqrt(Math.Sqrt(darkBack.z)))));
|
|
|
|
Brush light = new SolidBrush(Color.FromArgb((int)(255 * Math.Sqrt(Math.Sqrt(lightBack.x))),
|
|
(int)(255 * Math.Sqrt(Math.Sqrt(lightBack.y))),
|
|
(int)(255 * Math.Sqrt(Math.Sqrt(lightBack.z)))));
|
|
|
|
for (int x = 0; x < numX; x++)
|
|
{
|
|
for (int y = 0; y < numY; y++)
|
|
{
|
|
var brush = ((x%2) == (y%2)) ? dark : light;
|
|
g.FillRectangle(brush, x * 64, y * 64, 64, 64);
|
|
}
|
|
}
|
|
|
|
dark.Dispose();
|
|
light.Dispose();
|
|
}
|
|
|
|
private void pixelContext_Paint(object sender, PaintEventArgs e)
|
|
{
|
|
if (m_Output == null || m_Core.Renderer == null || PixelPicked == false)
|
|
{
|
|
if (backcolorPick.Checked)
|
|
e.Graphics.Clear(colorDialog.Color);
|
|
else
|
|
DrawCheckerboard(e.Graphics, pixelContext.DisplayRectangle);
|
|
return;
|
|
}
|
|
|
|
m_Core.Renderer.Invoke((ReplayRenderer r) => { if (m_Output != null) m_Output.Display(); });
|
|
}
|
|
|
|
private void render_Paint(object sender, PaintEventArgs e)
|
|
{
|
|
renderContainer.Invalidate();
|
|
if (m_Output == null || m_Core.Renderer == null)
|
|
{
|
|
if (backcolorPick.Checked)
|
|
e.Graphics.Clear(colorDialog.Color);
|
|
else
|
|
DrawCheckerboard(e.Graphics, render.DisplayRectangle);
|
|
return;
|
|
}
|
|
|
|
foreach (var prev in rtPanel.Thumbnails)
|
|
if (prev.Unbound) prev.Clear();
|
|
|
|
foreach (var prev in texPanel.Thumbnails)
|
|
if (prev.Unbound) prev.Clear();
|
|
|
|
m_Core.Renderer.Invoke((ReplayRenderer r) => { if (m_Output != null) m_Output.Display(); });
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Scale Handling
|
|
|
|
private FetchTexture FollowingTexture
|
|
{
|
|
get
|
|
{
|
|
if (!m_Core.LogLoaded || m_Core.CurTextures == null) return null;
|
|
|
|
ResourceId ID = m_Following.GetResourceId(m_Core);
|
|
|
|
if (ID == ResourceId.Null)
|
|
ID = m_TexDisplay.texid;
|
|
|
|
for (int i = 0; i < m_Core.CurTextures.Length; i++)
|
|
{
|
|
if (m_Core.CurTextures[i].ID == ID)
|
|
{
|
|
return m_Core.CurTextures[i];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
private FetchTexture CurrentTexture
|
|
{
|
|
get
|
|
{
|
|
var dc = renderToolstripContainer.Parent as DockContent;
|
|
|
|
if (dc != null && dc.Tag != null)
|
|
return dc.Tag as FetchTexture;
|
|
|
|
return FollowingTexture;
|
|
}
|
|
}
|
|
|
|
private UInt32 CurrentTexDisplayWidth
|
|
{
|
|
get
|
|
{
|
|
if (CurrentTexture == null)
|
|
return 1;
|
|
|
|
return CurrentTexture.width;
|
|
}
|
|
}
|
|
private UInt32 CurrentTexDisplayHeight
|
|
{
|
|
get
|
|
{
|
|
if (CurrentTexture == null)
|
|
return 1;
|
|
|
|
if (CurrentTexture.dimension == 1)
|
|
return 100;
|
|
|
|
return CurrentTexture.height;
|
|
}
|
|
}
|
|
|
|
private bool FitToWindow
|
|
{
|
|
get
|
|
{
|
|
return fitToWindow.Checked;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (!FitToWindow && value)
|
|
{
|
|
fitToWindow.Checked = true;
|
|
}
|
|
else if (FitToWindow && !value)
|
|
{
|
|
fitToWindow.Checked = false;
|
|
float curScale = m_TexDisplay.scale;
|
|
zoomOption.SelectedText = "";
|
|
CurrentZoomValue = curScale;
|
|
}
|
|
}
|
|
}
|
|
|
|
private float GetFitScale()
|
|
{
|
|
float xscale = (float)render.Width / (float)CurrentTexDisplayWidth;
|
|
float yscale = (float)render.Height / (float)CurrentTexDisplayHeight;
|
|
return Math.Min(xscale, yscale);
|
|
}
|
|
|
|
private void UI_UpdateFittedScale()
|
|
{
|
|
if (FitToWindow)
|
|
UI_SetScale(1.0f);
|
|
}
|
|
|
|
private void UI_SetScale(float s)
|
|
{
|
|
UI_SetScale(s, render.ClientRectangle.Width / 2, render.ClientRectangle.Height / 2);
|
|
}
|
|
|
|
bool ScrollUpdateScrollbars = true;
|
|
|
|
Point ScrollPosition
|
|
{
|
|
get
|
|
{
|
|
return new Point((int)m_TexDisplay.offx, (int)m_TexDisplay.offy);
|
|
}
|
|
|
|
set
|
|
{
|
|
m_TexDisplay.offx = Math.Max(render.Width - CurrentTexDisplayWidth * m_TexDisplay.scale, value.X);
|
|
m_TexDisplay.offy = Math.Max(render.Height - CurrentTexDisplayHeight * m_TexDisplay.scale, value.Y);
|
|
|
|
m_TexDisplay.offx = Math.Min(0.0f, (float)m_TexDisplay.offx);
|
|
m_TexDisplay.offy = Math.Min(0.0f, (float)m_TexDisplay.offy);
|
|
|
|
if (ScrollUpdateScrollbars)
|
|
{
|
|
if(renderHScroll.Enabled)
|
|
renderHScroll.Value = (int)Math.Min(renderHScroll.Maximum, (int)-m_TexDisplay.offx);
|
|
|
|
if (renderVScroll.Enabled)
|
|
renderVScroll.Value = (int)Math.Min(renderVScroll.Maximum, (int)-m_TexDisplay.offy);
|
|
}
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
}
|
|
}
|
|
|
|
private void UI_SetScale(float s, int x, int y)
|
|
{
|
|
if (FitToWindow)
|
|
s = GetFitScale();
|
|
|
|
float prevScale = m_TexDisplay.scale;
|
|
|
|
m_TexDisplay.scale = Math.Max(0.1f, Math.Min(8.0f, s));
|
|
|
|
FetchTexture tex = CurrentTexture;
|
|
|
|
if (tex == null)
|
|
{
|
|
if(m_Core.LogLoaded)
|
|
foreach (var t in m_Core.CurTextures)
|
|
if (t.ID == m_TexDisplay.texid)
|
|
tex = t;
|
|
|
|
if(tex == null)
|
|
return;
|
|
}
|
|
|
|
//render.Width = Math.Min(500, (int)(tex.width * m_TexDisplay.scale));
|
|
//render.Height = Math.Min(500, (int)(tex.height * m_TexDisplay.scale));
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
|
|
float scaleDelta = (m_TexDisplay.scale / prevScale);
|
|
|
|
Point newPos = ScrollPosition;
|
|
|
|
newPos -= new Size(x, y);
|
|
newPos = new Point((int)(newPos.X * scaleDelta), (int)(newPos.Y * scaleDelta));
|
|
newPos += new Size(x, y);
|
|
|
|
ScrollPosition = newPos;
|
|
|
|
CurrentZoomValue = m_TexDisplay.scale;
|
|
|
|
CalcScrollbars();
|
|
}
|
|
|
|
private void CalcScrollbars()
|
|
{
|
|
if (Math.Floor(CurrentTexDisplayWidth * m_TexDisplay.scale) <= render.Width)
|
|
{
|
|
renderHScroll.Enabled = false;
|
|
}
|
|
else
|
|
{
|
|
renderHScroll.Enabled = true;
|
|
|
|
renderHScroll.Maximum = (int)Math.Ceiling(CurrentTexDisplayWidth * m_TexDisplay.scale - (float)render.Width);
|
|
renderHScroll.LargeChange = Math.Max(1, renderHScroll.Maximum/6);
|
|
}
|
|
|
|
if (Math.Floor(CurrentTexDisplayHeight * m_TexDisplay.scale) <= render.Height)
|
|
{
|
|
renderVScroll.Enabled = false;
|
|
}
|
|
else
|
|
{
|
|
renderVScroll.Enabled = true;
|
|
|
|
renderVScroll.Maximum = (int)Math.Ceiling(CurrentTexDisplayHeight * m_TexDisplay.scale - (float)render.Height);
|
|
renderVScroll.LargeChange = Math.Max(1, renderVScroll.Maximum / 6);
|
|
}
|
|
}
|
|
|
|
private void render_Layout(object sender, LayoutEventArgs e)
|
|
{
|
|
UI_UpdateFittedScale();
|
|
CalcScrollbars();
|
|
|
|
renderContainer.Invalidate();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Mouse movement and scrolling
|
|
|
|
private Point m_DragStartScroll;
|
|
private Point m_DragStartPos;
|
|
|
|
private Point m_CurHoverPixel;
|
|
private Point m_PickedPoint;
|
|
|
|
private PixelValue m_CurRealValue = null;
|
|
private PixelValue m_CurPixelValue = null;
|
|
private PixelValue m_CurHoverValue = null;
|
|
|
|
private void RT_UpdateHoverColour(PixelValue v)
|
|
{
|
|
m_CurHoverValue = v;
|
|
|
|
this.BeginInvoke(new Action(UI_UpdateStatusText));
|
|
}
|
|
|
|
private void RT_PickPixelsAndUpdate(int x, int y, bool ctx)
|
|
{
|
|
FetchTexture tex = CurrentTexture;
|
|
|
|
if (tex == null) return;
|
|
|
|
if(ctx)
|
|
m_Output.SetPixelContextLocation((UInt32)x, (UInt32)y);
|
|
|
|
if (m_TexDisplay.FlipY)
|
|
y = (int)tex.height - y;
|
|
|
|
var pickValue = m_Output.PickPixel(m_TexDisplay.texid, true, (UInt32)x, (UInt32)y,
|
|
m_TexDisplay.sliceFace, m_TexDisplay.mip, m_TexDisplay.sampleIdx);
|
|
PixelValue realValue = null;
|
|
if (m_TexDisplay.CustomShader != ResourceId.Null)
|
|
realValue = m_Output.PickPixel(m_TexDisplay.texid, false, (UInt32)x, (UInt32)y,
|
|
m_TexDisplay.sliceFace, m_TexDisplay.mip, m_TexDisplay.sampleIdx);
|
|
|
|
RT_UpdatePixelColour(pickValue, realValue, false);
|
|
}
|
|
|
|
private void RT_UpdatePixelColour(PixelValue withCustom, PixelValue realValue, bool UpdateHover)
|
|
{
|
|
m_CurPixelValue = withCustom;
|
|
if (UpdateHover)
|
|
m_CurHoverValue = withCustom;
|
|
m_CurRealValue = realValue;
|
|
|
|
this.BeginInvoke(new Action(UI_UpdateStatusText));
|
|
}
|
|
|
|
private void render_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
bool nudged = false;
|
|
|
|
FetchTexture tex = CurrentTexture;
|
|
|
|
if (tex == null) return;
|
|
|
|
if (e.KeyCode == Keys.Up && m_PickedPoint.Y > 0)
|
|
{
|
|
m_PickedPoint = new Point(m_PickedPoint.X, m_PickedPoint.Y - 1);
|
|
nudged = true;
|
|
}
|
|
else if (e.KeyCode == Keys.Down && m_PickedPoint.Y < tex.height-1)
|
|
{
|
|
m_PickedPoint = new Point(m_PickedPoint.X, m_PickedPoint.Y + 1);
|
|
nudged = true;
|
|
}
|
|
else if (e.KeyCode == Keys.Left && m_PickedPoint.X > 0)
|
|
{
|
|
m_PickedPoint = new Point(m_PickedPoint.X - 1, m_PickedPoint.Y);
|
|
nudged = true;
|
|
}
|
|
else if (e.KeyCode == Keys.Right && m_PickedPoint.X < tex.width - 1)
|
|
{
|
|
m_PickedPoint = new Point(m_PickedPoint.X + 1, m_PickedPoint.Y);
|
|
nudged = true;
|
|
}
|
|
|
|
if(nudged)
|
|
{
|
|
e.Handled = true;
|
|
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
if (m_Output != null)
|
|
RT_PickPixelsAndUpdate(m_PickedPoint.X, m_PickedPoint.Y, true);
|
|
|
|
RT_UpdateAndDisplay(r);
|
|
});
|
|
|
|
UI_UpdateStatusText();
|
|
}
|
|
}
|
|
|
|
private void renderHScroll_Scroll(object sender, ScrollEventArgs e)
|
|
{
|
|
ScrollUpdateScrollbars = false;
|
|
|
|
if (e.Type != ScrollEventType.EndScroll)
|
|
ScrollPosition = new Point(ScrollPosition.X - (e.NewValue - e.OldValue), ScrollPosition.Y);
|
|
|
|
ScrollUpdateScrollbars = true;
|
|
}
|
|
|
|
private void renderVScroll_Scroll(object sender, ScrollEventArgs e)
|
|
{
|
|
ScrollUpdateScrollbars = false;
|
|
|
|
if(e.Type != ScrollEventType.EndScroll)
|
|
ScrollPosition = new Point(ScrollPosition.X, ScrollPosition.Y - (e.NewValue - e.OldValue));
|
|
|
|
ScrollUpdateScrollbars = true;
|
|
}
|
|
|
|
private void render_MouseLeave(object sender, EventArgs e)
|
|
{
|
|
Cursor = Cursors.Default;
|
|
}
|
|
|
|
private void render_MouseUp(object sender, MouseEventArgs e)
|
|
{
|
|
Cursor = Cursors.Default;
|
|
}
|
|
|
|
private void render_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
m_CurHoverPixel = render.PointToClient(Cursor.Position);
|
|
|
|
m_CurHoverPixel.X = (int)(((float)m_CurHoverPixel.X - m_TexDisplay.offx) / m_TexDisplay.scale);
|
|
m_CurHoverPixel.Y = (int)(((float)m_CurHoverPixel.Y - m_TexDisplay.offy) / m_TexDisplay.scale);
|
|
|
|
if (e.Button == MouseButtons.Right && m_TexDisplay.texid != ResourceId.Null)
|
|
{
|
|
FetchTexture tex = CurrentTexture;
|
|
|
|
if (tex != null)
|
|
{
|
|
m_PickedPoint = m_CurHoverPixel;
|
|
|
|
m_PickedPoint.X = Helpers.Clamp(m_PickedPoint.X, 0, (int)tex.width-1);
|
|
m_PickedPoint.Y = Helpers.Clamp(m_PickedPoint.Y, 0, (int)tex.height - 1);
|
|
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
if (m_Output != null)
|
|
RT_PickPixelsAndUpdate(m_PickedPoint.X, m_PickedPoint.Y, true);
|
|
});
|
|
}
|
|
|
|
Cursor = Cursors.Cross;
|
|
}
|
|
|
|
if (e.Button == MouseButtons.None && m_TexDisplay.texid != ResourceId.Null)
|
|
{
|
|
FetchTexture tex = CurrentTexture;
|
|
|
|
if (tex != null)
|
|
{
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
if (m_Output != null)
|
|
{
|
|
UInt32 y = (UInt32)m_CurHoverPixel.Y;
|
|
if (m_TexDisplay.FlipY)
|
|
y = tex.height - y;
|
|
RT_UpdateHoverColour(m_Output.PickPixel(m_TexDisplay.texid, true, (UInt32)m_CurHoverPixel.X, y,
|
|
m_TexDisplay.sliceFace, m_TexDisplay.mip, m_TexDisplay.sampleIdx));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
Panel p = renderContainer;
|
|
|
|
Point curpos = Cursor.Position;
|
|
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
if (Math.Abs(m_DragStartPos.X - curpos.X) > p.HorizontalScroll.SmallChange ||
|
|
Math.Abs(m_DragStartPos.Y - curpos.Y) > p.VerticalScroll.SmallChange)
|
|
{
|
|
ScrollPosition = new Point(m_DragStartScroll.X + (curpos.X - m_DragStartPos.X),
|
|
m_DragStartScroll.Y + (curpos.Y - m_DragStartPos.Y));
|
|
}
|
|
|
|
Cursor = Cursors.NoMove2D;
|
|
}
|
|
|
|
if (e.Button != MouseButtons.Left && e.Button != MouseButtons.Right)
|
|
{
|
|
Cursor = Cursors.Default;
|
|
}
|
|
|
|
UI_UpdateStatusText();
|
|
}
|
|
|
|
private void render_MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
render.Focus();
|
|
|
|
if (e.Button == MouseButtons.Right)
|
|
{
|
|
FetchTexture tex = CurrentTexture;
|
|
|
|
if (tex != null)
|
|
{
|
|
m_PickedPoint = m_CurHoverPixel;
|
|
|
|
m_PickedPoint.X = Helpers.Clamp(m_PickedPoint.X, 0, (int)tex.width - 1);
|
|
m_PickedPoint.Y = Helpers.Clamp(m_PickedPoint.Y, 0, (int)tex.height - 1);
|
|
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
if (m_Output != null)
|
|
RT_PickPixelsAndUpdate(m_PickedPoint.X, m_PickedPoint.Y, true);
|
|
});
|
|
}
|
|
|
|
Cursor = Cursors.Cross;
|
|
}
|
|
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
m_DragStartPos = Cursor.Position;
|
|
m_DragStartScroll = ScrollPosition;
|
|
|
|
Cursor = Cursors.NoMove2D;
|
|
}
|
|
}
|
|
|
|
private void render_MouseWheel(object sender, MouseEventArgs e)
|
|
{
|
|
Point cursorPos = renderContainer.PointToClient(Cursor.Position);
|
|
|
|
FitToWindow = false;
|
|
|
|
// scroll in logarithmic scale
|
|
double logScale = Math.Log(m_TexDisplay.scale);
|
|
logScale += e.Delta / 2500.0;
|
|
UI_SetScale((float)Math.Exp(logScale), cursorPos.X, cursorPos.Y);
|
|
|
|
((HandledMouseEventArgs)e).Handled = true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Texture Display Options
|
|
|
|
private float CurrentZoomValue
|
|
{
|
|
get
|
|
{
|
|
if (FitToWindow)
|
|
return m_TexDisplay.scale;
|
|
|
|
int zoom = 100;
|
|
Int32.TryParse(zoomOption.Text.ToString().Replace('%', ' '), out zoom);
|
|
return (float)(zoom) / 100.0f;
|
|
}
|
|
|
|
set
|
|
{
|
|
if(!zoomOption.IsDisposed)
|
|
zoomOption.Text = (Math.Ceiling(value * 100)).ToString() + "%";
|
|
}
|
|
}
|
|
|
|
private void zoomOption_KeyPress(object sender, KeyPressEventArgs e)
|
|
{
|
|
if (e.KeyChar == '\n' || e.KeyChar == '\r')
|
|
{
|
|
string txt = zoomOption.Text;
|
|
FitToWindow = false;
|
|
zoomOption.Text = txt;
|
|
|
|
UI_SetScale(CurrentZoomValue);
|
|
}
|
|
}
|
|
|
|
private void zoomOption_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.KeyCode == Keys.Enter)
|
|
{
|
|
string txt = zoomOption.Text;
|
|
FitToWindow = false;
|
|
zoomOption.Text = txt;
|
|
|
|
UI_SetScale(CurrentZoomValue);
|
|
}
|
|
}
|
|
|
|
private void zoomOption_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
if ((zoomOption.Focused || zoomOption.ContentRectangle.Contains(Cursor.Position))
|
|
&& zoomOption.SelectedItem != null)
|
|
{
|
|
var item = zoomOption.SelectedItem.ToString();
|
|
|
|
FitToWindow = false;
|
|
|
|
zoomOption.Text = item;
|
|
|
|
UI_SetScale(CurrentZoomValue);
|
|
}
|
|
}
|
|
|
|
private void zoomOption_DropDownClosed(object sender, EventArgs e)
|
|
{
|
|
if (zoomOption.SelectedItem != null)
|
|
{
|
|
var item = zoomOption.SelectedItem.ToString();
|
|
|
|
FitToWindow = false;
|
|
|
|
zoomOption.Text = item;
|
|
|
|
UI_SetScale(CurrentZoomValue);
|
|
}
|
|
}
|
|
|
|
|
|
private void fitToWindow_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
UI_UpdateFittedScale();
|
|
}
|
|
|
|
private void backcolorPick_Click(object sender, EventArgs e)
|
|
{
|
|
var result = colorDialog.ShowDialog();
|
|
|
|
if (result == DialogResult.OK || result == DialogResult.Yes)
|
|
{
|
|
darkBack = lightBack = m_TexDisplay.darkBackgroundColour =
|
|
m_TexDisplay.lightBackgroundColour = new FloatVector(
|
|
((float)colorDialog.Color.R) / 255.0f,
|
|
((float)colorDialog.Color.G) / 255.0f,
|
|
((float)colorDialog.Color.B) / 255.0f);
|
|
|
|
backcolorPick.Checked = true;
|
|
checkerBack.Checked = false;
|
|
}
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
|
|
if (m_Output == null)
|
|
{
|
|
render.Invalidate();
|
|
pixelContext.Invalidate();
|
|
}
|
|
}
|
|
|
|
private void checkerBack_Click(object sender, EventArgs e)
|
|
{
|
|
var defaults = new TextureDisplay();
|
|
|
|
darkBack = m_TexDisplay.darkBackgroundColour = defaults.darkBackgroundColour;
|
|
lightBack = m_TexDisplay.lightBackgroundColour = defaults.lightBackgroundColour;
|
|
|
|
backcolorPick.Checked = false;
|
|
checkerBack.Checked = true;
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
|
|
if (m_Output == null)
|
|
{
|
|
render.Invalidate();
|
|
pixelContext.Invalidate();
|
|
}
|
|
}
|
|
private void mipLevel_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
if (CurrentTexture == null) return;
|
|
|
|
if (CurrentTexture.mips > 1)
|
|
{
|
|
m_TexDisplay.mip = (UInt32)mipLevel.SelectedIndex;
|
|
m_TexDisplay.sampleIdx = 0;
|
|
}
|
|
else
|
|
{
|
|
m_TexDisplay.mip = 0;
|
|
m_TexDisplay.sampleIdx = (UInt32)mipLevel.SelectedIndex;
|
|
if (mipLevel.SelectedIndex == CurrentTexture.msSamp)
|
|
m_TexDisplay.sampleIdx = ~0U;
|
|
}
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateVisualRange);
|
|
|
|
if (m_Output != null && m_PickedPoint != null && m_PickedPoint.X > 0 && m_PickedPoint.Y > 0)
|
|
{
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
if (m_Output != null)
|
|
RT_PickPixelsAndUpdate(m_PickedPoint.X, m_PickedPoint.Y, true);
|
|
});
|
|
}
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
}
|
|
|
|
private void overlay_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
m_TexDisplay.overlay = TextureDisplayOverlay.None;
|
|
|
|
if (overlay.SelectedIndex > 0)
|
|
m_TexDisplay.overlay = (TextureDisplayOverlay)overlay.SelectedIndex;
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
}
|
|
|
|
private void sliceFace_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
m_TexDisplay.sliceFace = (UInt32)sliceFace.SelectedIndex;
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateVisualRange);
|
|
|
|
if (m_Output != null && m_PickedPoint != null && m_PickedPoint.X > 0 && m_PickedPoint.Y > 0)
|
|
{
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
if (m_Output != null)
|
|
RT_PickPixelsAndUpdate(m_PickedPoint.X, m_PickedPoint.Y, true);
|
|
});
|
|
}
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateAndDisplay);
|
|
}
|
|
|
|
private void updateChannelsHandler(object sender, EventArgs e)
|
|
{
|
|
UI_UpdateChannels();
|
|
}
|
|
|
|
private void channelButton_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Right && sender is ToolStripButton)
|
|
{
|
|
ToolStripButton me = (sender as ToolStripButton);
|
|
|
|
bool checkd = false;
|
|
|
|
var butts = new ToolStripButton[] { customRed, customGreen, customBlue, customAlpha };
|
|
|
|
foreach (var b in butts)
|
|
{
|
|
if(b.Checked && b != sender)
|
|
checkd = true;
|
|
if(!b.Checked && b == sender)
|
|
checkd = true;
|
|
}
|
|
|
|
customRed.Checked = !checkd;
|
|
customGreen.Checked = !checkd;
|
|
customBlue.Checked = !checkd;
|
|
customAlpha.Checked = !checkd;
|
|
(sender as ToolStripButton).Checked = checkd;
|
|
}
|
|
}
|
|
|
|
Thread rangePaintThread = null;
|
|
|
|
void rangeHistogram_RangeUpdated(object sender, Controls.RangeHistogramEventArgs e)
|
|
{
|
|
m_TexDisplay.rangemin = e.BlackPoint;
|
|
m_TexDisplay.rangemax = e.WhitePoint;
|
|
|
|
rangeBlack.Text = Formatter.Format(e.BlackPoint);
|
|
rangeWhite.Text = Formatter.Format(e.WhitePoint);
|
|
|
|
if (rangePaintThread != null &&
|
|
rangePaintThread.ThreadState != ThreadState.Aborted &&
|
|
rangePaintThread.ThreadState != ThreadState.Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
rangePaintThread = Helpers.NewThread(new ThreadStart(() =>
|
|
{
|
|
m_Core.Renderer.Invoke((ReplayRenderer r) => { RT_UpdateAndDisplay(r); if (m_Output != null) m_Output.Display(); });
|
|
Thread.Sleep(8);
|
|
}));
|
|
rangePaintThread.Start();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Handlers
|
|
|
|
private void zoomRange_Click(object sender, EventArgs e)
|
|
{
|
|
float black = rangeHistogram.BlackPoint;
|
|
float white = rangeHistogram.WhitePoint;
|
|
|
|
autoFit.Checked = false;
|
|
|
|
rangeHistogram.RangeMin = black;
|
|
rangeHistogram.RangeMax = white;
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateVisualRange);
|
|
}
|
|
|
|
private void reset01_Click(object sender, EventArgs e)
|
|
{
|
|
rangeHistogram.SetRange(0.0f, 1.0f);
|
|
|
|
autoFit.Checked = false;
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateVisualRange);
|
|
}
|
|
|
|
private void autoFit_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Right)
|
|
{
|
|
autoFit.Checked = !autoFit.Checked;
|
|
|
|
if (autoFit.Checked)
|
|
AutoFitRange();
|
|
}
|
|
}
|
|
|
|
private void autoFit_Click(object sender, EventArgs e)
|
|
{
|
|
AutoFitRange();
|
|
}
|
|
|
|
private void AutoFitRange()
|
|
{
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
PixelValue min, max;
|
|
bool success = r.GetMinMax(m_TexDisplay.texid, m_TexDisplay.sliceFace, m_TexDisplay.mip, m_TexDisplay.sampleIdx, out min, out max);
|
|
|
|
if (success)
|
|
{
|
|
float minval = float.MaxValue;
|
|
float maxval = -float.MaxValue;
|
|
|
|
bool changeRange = false;
|
|
|
|
ResourceFormat fmt = CurrentTexture.format;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (fmt.compType == FormatComponentType.UInt)
|
|
{
|
|
min.value.f[i] = min.value.u[i];
|
|
max.value.f[i] = max.value.u[i];
|
|
}
|
|
else if (fmt.compType == FormatComponentType.SInt)
|
|
{
|
|
min.value.f[i] = min.value.i[i];
|
|
max.value.f[i] = max.value.i[i];
|
|
}
|
|
}
|
|
|
|
if (m_TexDisplay.Red)
|
|
{
|
|
minval = Math.Min(minval, min.value.f[0]);
|
|
maxval = Math.Max(maxval, max.value.f[0]);
|
|
changeRange = true;
|
|
}
|
|
if (m_TexDisplay.Green && fmt.compCount > 1)
|
|
{
|
|
minval = Math.Min(minval, min.value.f[1]);
|
|
maxval = Math.Max(maxval, max.value.f[1]);
|
|
changeRange = true;
|
|
}
|
|
if (m_TexDisplay.Blue && fmt.compCount > 2)
|
|
{
|
|
minval = Math.Min(minval, min.value.f[2]);
|
|
maxval = Math.Max(maxval, max.value.f[2]);
|
|
changeRange = true;
|
|
}
|
|
if (m_TexDisplay.Alpha && fmt.compCount > 3)
|
|
{
|
|
minval = Math.Min(minval, min.value.f[3]);
|
|
maxval = Math.Max(maxval, max.value.f[3]);
|
|
changeRange = true;
|
|
}
|
|
|
|
if (changeRange)
|
|
{
|
|
this.BeginInvoke(new Action(() =>
|
|
{
|
|
rangeHistogram.SetRange(minval, maxval);
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateVisualRange);
|
|
}));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private bool m_Visualise = false;
|
|
|
|
private void visualiseRange_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
if (visualiseRange.Checked)
|
|
{
|
|
rangeHistogram.MinimumSize = new Size(300, 90);
|
|
|
|
m_Visualise = true;
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateVisualRange);
|
|
}
|
|
else
|
|
{
|
|
m_Visualise = false;
|
|
rangeHistogram.MinimumSize = new Size(200, 20);
|
|
|
|
rangeHistogram.HistogramData = null;
|
|
}
|
|
}
|
|
|
|
private void RT_UpdateVisualRange(ReplayRenderer r)
|
|
{
|
|
if (!m_Visualise || CurrentTexture == null) return;
|
|
|
|
ResourceFormat fmt = CurrentTexture.format;
|
|
|
|
bool success = true;
|
|
|
|
uint[] histogram;
|
|
success = r.GetHistogram(m_TexDisplay.texid, m_TexDisplay.sliceFace, m_TexDisplay.mip, m_TexDisplay.sampleIdx,
|
|
rangeHistogram.RangeMin, rangeHistogram.RangeMax,
|
|
m_TexDisplay.Red,
|
|
m_TexDisplay.Green && fmt.compCount > 1,
|
|
m_TexDisplay.Blue && fmt.compCount > 2,
|
|
m_TexDisplay.Alpha && fmt.compCount > 3,
|
|
out histogram);
|
|
|
|
if (success)
|
|
{
|
|
this.BeginInvoke(new Action(() =>
|
|
{
|
|
rangeHistogram.SetHistogramRange(rangeHistogram.RangeMin, rangeHistogram.RangeMax);
|
|
rangeHistogram.HistogramData = histogram;
|
|
}));
|
|
}
|
|
}
|
|
|
|
private void rangePoint_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.KeyCode == Keys.Enter)
|
|
{
|
|
float black = rangeHistogram.BlackPoint;
|
|
float white = rangeHistogram.WhitePoint;
|
|
|
|
float.TryParse(rangeBlack.Text, out black);
|
|
float.TryParse(rangeWhite.Text, out white);
|
|
|
|
rangeHistogram.SetRange(black, white);
|
|
|
|
m_Core.Renderer.BeginInvoke(RT_UpdateVisualRange);
|
|
}
|
|
}
|
|
|
|
private void tabContextMenu_Opening(object sender, CancelEventArgs e)
|
|
{
|
|
if (tabContextMenu.SourceControl == m_PreviewPanel.Pane.TabStripControl)
|
|
{
|
|
int idx = m_PreviewPanel.Pane.TabStripControl.Tabs.IndexOf(m_PreviewPanel.Pane.ActiveContent);
|
|
|
|
if (idx == -1)
|
|
e.Cancel = true;
|
|
|
|
if (m_PreviewPanel.Pane.ActiveContent == m_PreviewPanel)
|
|
closeTab.Enabled = false;
|
|
else
|
|
closeTab.Enabled = true;
|
|
}
|
|
}
|
|
|
|
private void closeTab_Click(object sender, EventArgs e)
|
|
{
|
|
if (tabContextMenu.SourceControl == m_PreviewPanel.Pane.TabStripControl)
|
|
{
|
|
int idx = m_PreviewPanel.Pane.TabStripControl.Tabs.IndexOf(m_PreviewPanel.Pane.ActiveContent);
|
|
|
|
if (m_PreviewPanel.Pane.ActiveContent != m_PreviewPanel)
|
|
{
|
|
(m_PreviewPanel.Pane.ActiveContent as DockContent).Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void closeOtherTabs_Click(object sender, EventArgs e)
|
|
{
|
|
if (tabContextMenu.SourceControl == m_PreviewPanel.Pane.TabStripControl)
|
|
{
|
|
IDockContent active = m_PreviewPanel.Pane.ActiveContent;
|
|
|
|
var tabs = m_PreviewPanel.Pane.TabStripControl.Tabs;
|
|
|
|
for(int i=0; i < tabs.Count; i++)
|
|
{
|
|
if (tabs[i].Content != active && tabs[i].Content != m_PreviewPanel)
|
|
{
|
|
(tabs[i].Content as DockContent).Close();
|
|
i--;
|
|
}
|
|
}
|
|
|
|
(active as DockContent).Show();
|
|
}
|
|
}
|
|
|
|
private void closeTabsToRight_Click(object sender, EventArgs e)
|
|
{
|
|
if (tabContextMenu.SourceControl == m_PreviewPanel.Pane.TabStripControl)
|
|
{
|
|
int idx = m_PreviewPanel.Pane.TabStripControl.Tabs.IndexOf(m_PreviewPanel.Pane.ActiveContent);
|
|
|
|
var tabs = m_PreviewPanel.Pane.TabStripControl.Tabs;
|
|
|
|
while (tabs.Count > idx+1)
|
|
{
|
|
(m_PreviewPanel.Pane.TabStripControl.Tabs[idx + 1].Content as DockContent).Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void TextureViewer_Resize(object sender, EventArgs e)
|
|
{
|
|
render.Invalidate();
|
|
}
|
|
|
|
private void pixelHistory_Click(object sender, EventArgs e)
|
|
{
|
|
PixelModification[] history = null;
|
|
|
|
PixelHistoryView hist = new PixelHistoryView(m_Core, CurrentTexture, m_PickedPoint,
|
|
m_TexDisplay.rangemin, m_TexDisplay.rangemax,
|
|
new bool[] { m_TexDisplay.Red, m_TexDisplay.Green, m_TexDisplay.Blue, m_TexDisplay.Alpha });
|
|
|
|
hist.Show(DockPanel);
|
|
|
|
// add a short delay so that controls repainting after a new panel appears can get at the
|
|
// render thread before we insert the long blocking pixel history task
|
|
var delayedHistory = new BackgroundWorker();
|
|
delayedHistory.DoWork += delegate
|
|
{
|
|
Thread.Sleep(100);
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
history = r.PixelHistory(CurrentTexture.ID, (UInt32)m_PickedPoint.X, (UInt32)m_PickedPoint.Y);
|
|
|
|
this.BeginInvoke(new Action(() =>
|
|
{
|
|
hist.SetHistory(history);
|
|
}));
|
|
});
|
|
};
|
|
delayedHistory.RunWorkerAsync();
|
|
}
|
|
|
|
private void debugPixel_Click(object sender, EventArgs e)
|
|
{
|
|
ShaderDebugTrace trace = null;
|
|
|
|
ShaderReflection shaderDetails = m_Core.CurPipelineState.GetShaderReflection(ShaderStageType.Pixel);
|
|
|
|
if(m_PickedPoint.X < 0 || m_PickedPoint.Y < 0)
|
|
return;
|
|
|
|
m_Core.Renderer.Invoke((ReplayRenderer r) =>
|
|
{
|
|
trace = r.DebugPixel((UInt32)m_PickedPoint.X, (UInt32)m_PickedPoint.Y, m_TexDisplay.sampleIdx, uint.MaxValue);
|
|
});
|
|
|
|
if (trace == null || trace.states.Length == 0)
|
|
{
|
|
MessageBox.Show("Couldn't find pixel to debug.\nEnsure the relevant drawcall is selected for this pixel.", "No Pixel Found",
|
|
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
return;
|
|
}
|
|
|
|
this.BeginInvoke(new Action(() =>
|
|
{
|
|
string debugContext = String.Format("Pixel {0},{1}", m_PickedPoint.X, m_PickedPoint.Y);
|
|
|
|
ShaderViewer s = new ShaderViewer(m_Core, shaderDetails, ShaderStageType.Pixel, trace, debugContext);
|
|
|
|
s.Show(this.DockPanel);
|
|
}));
|
|
}
|
|
|
|
private TextureSaveDialog m_SaveDialog = null;
|
|
|
|
private void saveTex_Click(object sender, EventArgs e)
|
|
{
|
|
if (m_SaveDialog == null)
|
|
m_SaveDialog = new TextureSaveDialog();
|
|
|
|
m_SaveDialog.saveData.id = m_TexDisplay.texid;
|
|
m_SaveDialog.saveData.slice.sliceIndex = (int)m_TexDisplay.sliceFace;
|
|
m_SaveDialog.saveData.mip = (int)m_TexDisplay.mip;
|
|
m_SaveDialog.saveData.comp.blackPoint = m_TexDisplay.rangemin;
|
|
m_SaveDialog.saveData.comp.whitePoint = m_TexDisplay.rangemax;
|
|
m_SaveDialog.saveData.alphaCol = m_TexDisplay.lightBackgroundColour;
|
|
m_SaveDialog.saveData.alpha = m_TexDisplay.Alpha ? AlphaMapping.BlendToCheckerboard : AlphaMapping.Discard;
|
|
if (m_TexDisplay.Alpha && !checkerBack.Checked) m_SaveDialog.saveData.alpha = AlphaMapping.BlendToColour;
|
|
m_SaveDialog.tex = CurrentTexture;
|
|
|
|
if(m_SaveDialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
bool ret = false;
|
|
|
|
m_Core.Renderer.Invoke((ReplayRenderer r) =>
|
|
{
|
|
ret = r.SaveTexture(m_SaveDialog.saveData, m_SaveDialog.Filename);
|
|
});
|
|
|
|
if(!ret)
|
|
MessageBox.Show(string.Format("Error saving texture {0}.\n\nCheck diagnostic log in Help menu for more details.", saveTextureDialog.FileName),
|
|
"Error saving texture", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
private void texturefilter_TextChanged(object sender, EventArgs e)
|
|
{
|
|
textureList.FillTextureList(texturefilter.SelectedIndex <= 0 ? texturefilter.Text : "",
|
|
texturefilter.SelectedIndex == 1,
|
|
texturefilter.SelectedIndex == 2);
|
|
}
|
|
|
|
private void texturefilter_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.KeyCode == Keys.Escape)
|
|
{
|
|
texturefilter.SelectedIndex = 0;
|
|
texturefilter.Text = "";
|
|
}
|
|
}
|
|
|
|
private void clearTexFilter_Click(object sender, EventArgs e)
|
|
{
|
|
texturefilter.SelectedIndex = 0;
|
|
texturefilter.Text = "";
|
|
}
|
|
|
|
private void toolstripEnabledChanged(object sender, EventArgs e)
|
|
{
|
|
overlayStrip.Visible = overlayStripEnabled.Checked;
|
|
overlayStrip.Visible = overlayStripEnabled.Checked;
|
|
channelStrip.Visible = channelsStripEnabled.Checked;
|
|
zoomStrip.Visible = zoomStripEnabled.Checked;
|
|
rangeStrip.Visible = rangeStripEnabled.Checked;
|
|
}
|
|
|
|
private void mainToolstrips_TopToolStripPanel_MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Right)
|
|
toolstripMenu.Show((Control)sender, e.Location);
|
|
}
|
|
|
|
private void texListShow_Click(object sender, EventArgs e)
|
|
{
|
|
if (!m_TexlistDockPanel.Visible)
|
|
{
|
|
texturefilter.SelectedIndex = 0;
|
|
texturefilter.Text = "";
|
|
|
|
m_TexlistDockPanel.Show();
|
|
}
|
|
else
|
|
{
|
|
m_TexlistDockPanel.Hide();
|
|
}
|
|
}
|
|
|
|
void textureList_GoIconClick(object sender, GoIconClickEventArgs e)
|
|
{
|
|
ViewTexture(e.ID, false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Thumbnail strip
|
|
|
|
private void AddResourceUsageEntry(List<ToolStripItem> items, uint start, uint end, ResourceUsage usage)
|
|
{
|
|
ToolStripItem item = null;
|
|
|
|
if (start == end)
|
|
item = new ToolStripLabel("EID " + start + ": " + usage.Str());
|
|
else
|
|
item = new ToolStripLabel("EID " + start + "-" + end + ": " + usage.Str());
|
|
|
|
item.Click += new EventHandler(resourceContextItem_Click);
|
|
item.Tag = end;
|
|
|
|
items.Add(item);
|
|
}
|
|
|
|
private void OpenResourceContextMenu(ResourceId id, bool thumbStripMenu, Control c, Point p)
|
|
{
|
|
var menuItems = new List<ToolStripItem>();
|
|
|
|
int i = 0;
|
|
for (i = 0; i < rightclickMenu.Items.Count; i++)
|
|
{
|
|
menuItems.Add(rightclickMenu.Items[i]);
|
|
if (rightclickMenu.Items[i] == usedStartLabel)
|
|
break;
|
|
|
|
menuItems[i].Visible = thumbStripMenu;
|
|
}
|
|
|
|
if (id != ResourceId.Null)
|
|
{
|
|
usedSep.Visible = true;
|
|
usedStartLabel.Visible = true;
|
|
openNewTab.Visible = true;
|
|
|
|
openNewTab.Tag = id;
|
|
|
|
m_Core.Renderer.BeginInvoke((ReplayRenderer r) =>
|
|
{
|
|
EventUsage[] usage = r.GetUsage(id);
|
|
|
|
this.BeginInvoke(new Action(() =>
|
|
{
|
|
uint start = 0;
|
|
uint end = 0;
|
|
ResourceUsage us = ResourceUsage.IA_IB;
|
|
|
|
foreach (var u in usage)
|
|
{
|
|
if (start == 0)
|
|
{
|
|
start = end = u.eventID;
|
|
us = u.usage;
|
|
continue;
|
|
}
|
|
|
|
var curDraw = m_Core.GetDrawcall(m_Core.CurFrame, u.eventID);
|
|
|
|
if (u.usage != us || curDraw.previous == null || curDraw.previous.eventID != end)
|
|
{
|
|
AddResourceUsageEntry(menuItems, start, end, us);
|
|
start = end = u.eventID;
|
|
us = u.usage;
|
|
}
|
|
|
|
end = u.eventID;
|
|
}
|
|
|
|
if (start != 0)
|
|
AddResourceUsageEntry(menuItems, start, end, us);
|
|
|
|
rightclickMenu.Items.Clear();
|
|
rightclickMenu.Items.AddRange(menuItems.ToArray());
|
|
|
|
rightclickMenu.Show(c, p);
|
|
}));
|
|
});
|
|
}
|
|
else
|
|
{
|
|
usedSep.Visible = false;
|
|
usedStartLabel.Visible = false;
|
|
openNewTab.Visible = false;
|
|
|
|
rightclickMenu.Show(c, p);
|
|
}
|
|
}
|
|
|
|
private void thumbsLayout_MouseDoubleClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Left && sender is ResourcePreview)
|
|
{
|
|
var prev = (ResourcePreview)sender;
|
|
|
|
var follow = (Following)prev.Tag;
|
|
|
|
var id = m_Following.GetResourceId(m_Core);
|
|
|
|
if (id != ResourceId.Null)
|
|
ViewTexture(id, false);
|
|
}
|
|
}
|
|
|
|
private void thumbsLayout_MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Left && sender is ResourcePreview)
|
|
{
|
|
var prev = (ResourcePreview)sender;
|
|
|
|
var follow = (Following)prev.Tag;
|
|
|
|
foreach (var p in rtPanel.Thumbnails)
|
|
p.Selected = false;
|
|
|
|
foreach (var p in texPanel.Thumbnails)
|
|
p.Selected = false;
|
|
|
|
m_Following = follow;
|
|
prev.Selected = true;
|
|
|
|
var id = m_Following.GetResourceId(m_Core);
|
|
|
|
if (id != ResourceId.Null)
|
|
{
|
|
UI_OnTextureSelectionChanged();
|
|
m_PreviewPanel.Show();
|
|
}
|
|
}
|
|
|
|
if (e.Button == MouseButtons.Right)
|
|
{
|
|
ResourceId id = ResourceId.Null;
|
|
|
|
if (sender is ResourcePreview)
|
|
{
|
|
var prev = (ResourcePreview)sender;
|
|
|
|
var tagdata = (Following)prev.Tag;
|
|
|
|
id = tagdata.GetResourceId(m_Core);
|
|
|
|
if (id == ResourceId.Null && tagdata == m_Following)
|
|
id = m_TexDisplay.texid;
|
|
}
|
|
|
|
OpenResourceContextMenu(id, true, (Control)sender, e.Location);
|
|
}
|
|
}
|
|
|
|
private void textureList_MouseUp(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Right)
|
|
OpenResourceContextMenu(FollowingTexture == null ? ResourceId.Null : FollowingTexture.ID, false,
|
|
(Control)sender, e.Location);
|
|
}
|
|
|
|
void resourceContextItem_Click(object sender, EventArgs e)
|
|
{
|
|
if (sender is ToolStripItem)
|
|
{
|
|
var c = (ToolStripItem)sender;
|
|
|
|
if (c.Tag is uint)
|
|
m_Core.SetEventID(null, m_Core.CurFrame, (uint)c.Tag);
|
|
else if (c.Tag is ResourceId)
|
|
ViewTexture((ResourceId)c.Tag, false);
|
|
}
|
|
}
|
|
|
|
private void showDisabled_Click(object sender, EventArgs e)
|
|
{
|
|
showDisabled.Checked = !showDisabled.Checked;
|
|
|
|
if (m_Core.LogLoaded)
|
|
OnEventSelected(m_Core.CurFrame, m_Core.CurEvent);
|
|
}
|
|
|
|
private void showEmpty_Click(object sender, EventArgs e)
|
|
{
|
|
showEmpty.Checked = !showEmpty.Checked;
|
|
|
|
if (m_Core.LogLoaded)
|
|
OnEventSelected(m_Core.CurFrame, m_Core.CurEvent);
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void TextureViewer_FormClosed(object sender, FormClosedEventArgs e)
|
|
{
|
|
m_Core.RemoveLogViewer(this);
|
|
}
|
|
}
|
|
} |