소스 검색

Updated Remotery.

Branimir Karadžić 9 년 전
부모
커밋
8914ad09e5

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 460 - 210
3rdparty/remotery/lib/Remotery.c


+ 60 - 3
3rdparty/remotery/lib/Remotery.h

@@ -67,6 +67,11 @@ documented just below this comment.
 #define RMT_USE_OPENGL 0
 #endif
 
+// Allow Metal profiling
+#ifndef RMT_USE_METAL
+#define RMT_USE_METAL 0
+#endif
+
 // Initially use POSIX thread names to name threads instead of Thread0, 1, ...
 #ifndef RMT_USE_POSIX_THREADNAMES
 #define RMT_USE_POSIX_THREADNAMES 0
@@ -129,9 +134,14 @@ documented just below this comment.
     #define IFDEF_RMT_USE_D3D11(t, f) f
 #endif
 #if RMT_ENABLED && RMT_USE_OPENGL
-#define IFDEF_RMT_USE_OPENGL(t, f) t
+    #define IFDEF_RMT_USE_OPENGL(t, f) t
+#else
+    #define IFDEF_RMT_USE_OPENGL(t, f) f
+#endif
+#if RMT_ENABLED && RMT_USE_METAL
+    #define IFDEF_RMT_USE_METAL(t, f) t
 #else
-#define IFDEF_RMT_USE_OPENGL(t, f) f
+    #define IFDEF_RMT_USE_METAL(t, f) f
 #endif
 
 
@@ -237,7 +247,7 @@ typedef enum rmtError
     RMT_ERROR_D3D11_FAILED_TO_CREATE_QUERY,     // Failed to create query for sample
 
     // OpenGL error messages
-    RMT_ERROR_OPENGL_ERROR,                     // Generic OpenGL error, no real need to expose more detail since app will probably have an OpenGL error callback registered
+    RMT_ERROR_OPENGL_ERROR,                     // Generic OpenGL error, no need to expose detail since app will need an OpenGL error callback registered
 
     RMT_ERROR_CUDA_UNKNOWN,
 } rmtError;
@@ -424,6 +434,26 @@ typedef struct rmtCUDABind
     RMT_OPTIONAL(RMT_USE_OPENGL, _rmt_EndOpenGLSample())
 
 
+#define rmt_BindMetal(command_buffer)                                       \
+    RMT_OPTIONAL(RMT_USE_METAL, _rmt_BindMetal(command_buffer));
+
+#define rmt_UnbindMetal()                                                   \
+    RMT_OPTIONAL(RMT_USE_METAL, _rmt_UnbindMetal());
+
+#define rmt_BeginMetalSample(name)                                          \
+    RMT_OPTIONAL(RMT_USE_METAL, {                                           \
+        static rmtU32 rmt_sample_hash_##name = 0;                           \
+        _rmt_BeginMetalSample(#name, &rmt_sample_hash_##name);              \
+    })
+
+#define rmt_BeginMetalSampleDynamic(namestr)                                \
+    RMT_OPTIONAL(RMT_USE_METAL, _rmt_BeginMetalSample(namestr, NULL))
+
+#define rmt_EndMetalSample()                                                \
+    RMT_OPTIONAL(RMT_USE_METAL, _rmt_EndMetalSample())
+
+
+
 
 /*
 ------------------------------------------------------------------------------------------------------------------------
@@ -485,6 +515,17 @@ struct rmt_EndOpenGLSampleOnScopeExit
 };
 #endif
 
+#if RMT_USE_METAL
+extern "C" RMT_API void _rmt_EndMetalSample(void);
+struct rmt_EndMetalSampleOnScopeExit
+{
+    ~rmt_EndMetalSampleOnScopeExit()
+    {
+        _rmt_EndMetalSample();
+    }
+};
+#endif
+
 #endif
 
 
@@ -502,6 +543,9 @@ struct rmt_EndOpenGLSampleOnScopeExit
 #define rmt_ScopedOpenGLSample(name)                                                                    \
         RMT_OPTIONAL(RMT_USE_OPENGL, rmt_BeginOpenGLSample(name));                                      \
         RMT_OPTIONAL(RMT_USE_OPENGL, rmt_EndOpenGLSampleOnScopeExit rmt_ScopedOpenGLSample##name);
+#define rmt_ScopedMetalSample(name)                                                                     \
+        RMT_OPTIONAL(RMT_USE_METAL, rmt_BeginMetalSample(name));                                        \
+        RMT_OPTIONAL(RMT_USE_METAL, rmt_EndMetalSampleOnScopeExit rmt_ScopedMetalSample##name);
 
 #endif
 
@@ -553,10 +597,23 @@ RMT_API void _rmt_BeginOpenGLSample(rmtPStr name, rmtU32* hash_cache);
 RMT_API void _rmt_EndOpenGLSample(void);
 #endif
 
+#if RMT_USE_METAL
+RMT_API void _rmt_BeginMetalSample(rmtPStr name, rmtU32* hash_cache);
+RMT_API void _rmt_EndMetalSample(void);
+#endif
+
 #ifdef __cplusplus
+
 }
 #endif
 
+#if RMT_USE_METAL
+#ifdef __OBJC__
+RMT_API void _rmt_BindMetal(id command_buffer);
+RMT_API void _rmt_UnbindMetal();
+#endif
+#endif
+
 #endif  // RMT_ENABLED
 
 

+ 49 - 0
3rdparty/remotery/lib/RemoteryMetal.mm

@@ -0,0 +1,49 @@
+
+#include <Foundation/NSThread.h>
+#include <Foundation/NSDictionary.h>
+#include <Foundation/NSString.h>
+
+#import <Metal/Metal.h>
+
+
+// Store command buffer in thread-local so that each thread can point to its own
+void SetCommandBuffer(id command_buffer)
+{
+    NSMutableDictionary* thread_data = [[NSThread currentThread] threadDictionary];
+    thread_data[@"rmtMTLCommandBuffer"] = command_buffer;
+}
+id GetCommandBuffer()
+{
+    NSMutableDictionary* thread_data = [[NSThread currentThread] threadDictionary];
+    return thread_data[@"rmtMTLCommandBuffer"];
+}
+
+
+void _rmt_BindMetal(id command_buffer)
+{
+    SetCommandBuffer(command_buffer);
+}
+
+
+void _rmt_UnbindMetal()
+{
+    SetCommandBuffer(0);
+}
+
+
+// Needs to be in the same lib for this to work
+unsigned long long rmtMetal_usGetTime();
+
+
+static void SetTimestamp(void* data)
+{
+    *((unsigned long long*)data) = rmtMetal_usGetTime();
+}
+
+
+void rmtMetal_MeasureCommandBuffer(unsigned long long* out_start, unsigned long long* out_end, unsigned int* out_ready)
+{
+    id command_buffer = GetCommandBuffer();
+    [command_buffer addScheduledHandler:^(id <MTLCommandBuffer>){ SetTimestamp(out_start); }];
+    [command_buffer addCompletedHandler:^(id <MTLCommandBuffer>){ SetTimestamp(out_end); *out_ready = 1; }];
+}

+ 21 - 2
3rdparty/remotery/readme.md

@@ -23,7 +23,7 @@ Compiling
   directories to add Remotery/lib path. The required library ws2_32.lib should be picked
   up through the use of the #pragma comment(lib, "ws2_32.lib") directive in Remotery.c.
 
-* Mac OS X (XCode) - simply add lib/Remotery.c and lib/Remotery.h to your program.
+* Mac OS X (XCode) - simply add lib/Remotery.c, lib/Remotery.h and lib/Remotery.mm to your program.
 
 * Linux (GCC) - add the source in lib folder. Compilation of the code requires -pthreads for
   library linkage. For example to compile the same run: cc lib/Remotery.c sample/sample.c
@@ -37,7 +37,8 @@ You can define some extra macros to modify what features are compiled into Remot
     RMT_USE_TINYCRT     0           Used by the Celtoys TinyCRT library (not released yet)
     RMT_USE_CUDA        0           Assuming CUDA headers/libs are setup, allow CUDA profiling
     RMT_USE_D3D11       0           Assuming Direct3D 11 headers/libs are setup, allow D3D11 GPU profiling
-    RMT_USE_OPENGL      0           Allow OpenGL GPU profiling (standalone except you must link to OpenGL which you already do if you use it)
+    RMT_USE_OPENGL      0           Allow OpenGL GPU profiling (dynamically links OpenGL libraries on available platforms)
+    RMT_USE_METAL       0           Allow Metal profiling of command buffers
 
 
 Basic Use
@@ -176,6 +177,24 @@ your OpenGL device and context, ensure you notify Remotery before shutting down
     rmt_UnbindOpenGL();
 
 
+Sampling Metal GPU activity
+---------------------------
+
+Remotery can sample Metal command buffers issued to the GPU from multiple threads. As the Metal API does not
+support finer grained profiling, samples will return only the timing of the bound command buffer, irrespective
+of how many you issue. As such, make sure you bind and sample the command buffer for each call site:
+
+    rmt_BindMetal(mtl_command_buffer);
+    rmt_ScopedMetalSample(command_buffer_name);
+
+The C API supports begin/end also:
+
+    rmt_BindMetal(mtl_command_buffer);
+    rmt_BeginMetalSample(command_buffer_name);
+    ...
+    rmt_EndMetalSample();
+
+
 Applying Configuration Settings
 -------------------------------
 

+ 5 - 3
3rdparty/remotery/vis/Code/Console.js

@@ -36,7 +36,7 @@ Console = (function()
 		// Setup log requests from the server
 		this.Server = server;
 		server.SetConsole(this);
-		server.AddMessageHandler("LOG", Bind(OnLog, this));
+		server.AddMessageHandler("LOGM", Bind(OnLog, this));
 	}
 
 
@@ -65,9 +65,11 @@ Console = (function()
 	}
 
 
-	function OnLog(self, socket, message)
+	function OnLog(self, socket, data_view)
 	{
-		self.AppTextBuffer = LogText(self.AppTextBuffer, message.text);
+	    var data_view_reader = new DataViewReader(data_view, 4);
+	    var text = data_view_reader.GetString();
+	    self.AppTextBuffer = LogText(self.AppTextBuffer, text);
 	}
 
 

+ 47 - 0
3rdparty/remotery/vis/Code/DataViewReader.js

@@ -0,0 +1,47 @@
+
+//
+// Simple wrapper around DataView that auto-advances the read offset and provides
+// a few common data type conversions specific to this app
+//
+DataViewReader = (function ()
+{
+    function DataViewReader(data_view, offset)
+    {
+        this.DataView = data_view;
+        this.Offset = offset;
+    }
+
+    DataViewReader.prototype.GetUInt32 = function ()
+    {
+        var v = this.DataView.getUint32(this.Offset, true);
+        this.Offset += 4;
+        return v;
+    }
+
+    DataViewReader.prototype.GetUInt64 = function ()
+    {
+        var v = this.DataView.getFloat64(this.Offset, true);
+        this.Offset += 8;
+        return v;
+    }
+
+    DataViewReader.prototype.GetStringOfLength = function (string_length)
+    {
+        var string = "";
+        for (var i = 0; i < string_length; i++)
+        {
+            string += String.fromCharCode(this.DataView.getInt8(this.Offset));
+            this.Offset++;
+        }
+
+        return string;
+    }
+
+    DataViewReader.prototype.GetString = function ()
+    {
+        var string_length = this.GetUInt32();
+        return this.GetStringOfLength(string_length);
+    }
+
+    return DataViewReader;
+})();

+ 105 - 12
3rdparty/remotery/vis/Code/Remotery.js

@@ -20,12 +20,37 @@ Settings = (function()
 
 Remotery = (function()
 {
+	// crack the url and get the parameter we want
+	var getUrlParameter = function getUrlParameter( search_param) 
+	{
+		var page_url = decodeURIComponent( window.location.search.substring(1) ),
+						url_vars = page_url.split('&'),
+						param_name,
+						i;
+
+		for (i = 0; i < url_vars.length; i++) 
+		{
+			param_name = url_vars[i].split('=');
+
+			if (param_name[0] === search_param) 
+			{
+				return param_name[1] === undefined ? true : param_name[1];
+			}
+		}
+	};
+
 	function Remotery()
 	{
 		this.WindowManager = new WM.WindowManager();
 		this.Settings = new Settings();
 
-		this.ConnectionAddress = LocalStore.Get("App", "Global", "ConnectionAddress", "ws://127.0.0.1:17815/rmt");
+		// "addr" param is ip:port and will override the local store version if passed in the URL
+		var addr = getUrlParameter( "addr" );
+		if ( addr != null )
+			this.ConnectionAddress = "ws://" + addr + "/rmt";
+		else
+			this.ConnectionAddress = LocalStore.Get("App", "Global", "ConnectionAddress", "ws://127.0.0.1:17815/rmt");
+
 		this.Server = new WebSocketConnection();
 		this.Server.AddConnectHandler(Bind(OnConnect, this));
 
@@ -43,8 +68,10 @@ Remotery = (function()
 		this.SampleWindows = { };
 		this.FrameHistory = { };
 		this.SelectedFrames = { };
+		this.NameMap = { };
 
-		this.Server.AddMessageHandler("SAMPLES", Bind(OnSamples, this));
+		this.Server.AddMessageHandler("SMPL", Bind(OnSamples, this));
+		this.Server.AddMessageHandler("SSMP", Bind(OnSampleName, this));
 
 		// Kick-off the auto-connect loop
 		AutoConnect(this);
@@ -80,10 +107,6 @@ Remotery = (function()
 	{
 		// Connection address has been validated
 		LocalStore.Set("App", "Global", "ConnectionAddress", self.ConnectionAddress);
-
-		self.TimelineWindow.ResetTimeRange();
-		self.FrameHistory = { };
-		self.SelectedFrames = { };
 	}
 
 
@@ -115,7 +138,8 @@ Remotery = (function()
 
 		// requestAnimationFrame can run up to 60hz which is way too much for drawing the timeline
 		// Assume it's running at 60hz and skip frames to achieve 10hz instead
-		// Doing this instead of using setTimeout because it's better for browser rendering (or; will be once WebGL is in use)
+	    // Doing this instead of using setTimeout because it's better for browser rendering (or; will be once WebGL is in use)
+        // TODO: Expose as config variable because high refresh rate is great when using a separate viewiing machine
 		if ((self.DisplayFrame % 10) == 0)
 			self.TimelineWindow.DrawAllRows();
 
@@ -123,15 +147,74 @@ Remotery = (function()
 	}
 
 
-	function OnSamples(self, socket, message)
+	function DecodeSample(self, data_view_reader)
 	{
-		var name = message.thread_name;
+	    var sample = {};
+
+        // Get name hash and lookup name it map
+	    sample.name_hash = data_view_reader.GetUInt32();
+	    sample.name = self.NameMap[sample.name_hash];
+
+        // If the name doesn't exist in the map yet, request it from the server
+	    if (sample.name == undefined)
+	    {
+            // Meanwhile, store the hash as the name
+	        sample.name = sample.name_hash;
+	        self.Server.Send("GSMP" + sample.name);
+	    }
+
+        // Get the rest of the sample data
+	    sample.id = data_view_reader.GetUInt32();
+	    sample.colour = data_view_reader.GetStringOfLength(7);
+	    sample.us_start = data_view_reader.GetUInt64();
+	    sample.us_length = data_view_reader.GetUInt64();
+
+        // Recurse into children
+	    sample.children = [];
+	    DecodeSampleArray(self, data_view_reader, sample.children);
+
+	    return sample;
+	}
+
+
+	function DecodeSampleArray(self, data_view_reader, samples)
+	{
+	    var nb_samples = data_view_reader.GetUInt32();
+	    for (var i = 0; i < nb_samples; i++)
+	    {
+	        var sample = DecodeSample(self, data_view_reader);
+            samples.push(sample)
+	    }
+	}
 
+
+	function DecodeSamples(self, data_view_reader)
+	{
+        // Message-specific header
+	    var message = { };
+	    message.thread_name = data_view_reader.GetString();
+	    message.nb_samples = data_view_reader.GetUInt32();
+	    message.sample_digest = data_view_reader.GetUInt32();
+
+        // Read samples
+	    message.samples = [];
+	    message.samples.push(DecodeSample(self, data_view_reader));
+
+	    return message;
+	}
+
+
+	function OnSamples(self, socket, data_view)
+	{
 		// Discard any new samples while paused
 		if (self.Settings.IsPaused)
-			return;
+		    return;
+
+        // Binary decode incoming sample data
+		var message = DecodeSamples(self, new DataViewReader(data_view, 8));
+		var name = message.thread_name;
 
-		// Add to frame history for this thread
+	    // Add to frame history for this thread
 		var thread_frame = new ThreadFrame(message);
 		if (!(name in self.FrameHistory))
 			self.FrameHistory[name] = [ ];
@@ -142,7 +225,7 @@ Remotery = (function()
 		var max_nb_frames = 10000;
 		var extra_frames = frame_history.length - max_nb_frames;
 		if (extra_frames > 0)
-			frame_history.splice(0, extra_frames);
+		    frame_history.splice(0, extra_frames);
 
 		// Create sample windows on-demand
 		if (!(name in self.SampleWindows))
@@ -159,6 +242,16 @@ Remotery = (function()
 	}
 
 
+	function OnSampleName(self, socket, data_view)
+	{
+        // Add any names sent by the server to the local map
+	    var data_view_reader = new DataViewReader(data_view, 4);
+	    var name_hash = data_view_reader.GetUInt32();
+	    var name = data_view_reader.GetString();
+	    self.NameMap[name_hash] = name;
+	}
+
+
 	function OnTimelineCheck(self, name, evt)
 	{
 		// Show/hide the equivalent sample window and move all the others to occupy any left-over space

+ 24 - 10
3rdparty/remotery/vis/Code/SampleWindow.js

@@ -72,7 +72,7 @@ SampleWindow = (function()
 		if (this.SampleDigest != sample_digest)
 		{
 			this.RootRow.Rows.ClearIndex("_ID");
-			var index = UpdateSamples(this.RootRow, samples, 0, "");
+			var index = UpdateAllSampleFields(this.RootRow, samples, 0, "");
 			this.SampleDigest = sample_digest;
 
 			// Clear out any left-over rows
@@ -85,8 +85,8 @@ SampleWindow = (function()
 
 		else if (this.Visible)
 		{
-			// Otherwise just update the existing sample times
-			UpdateSampleTimes(this.RootRow, samples);
+			// Otherwise just update the existing sample fields
+		    UpdateChangedSampleFields(this.RootRow, samples, "");
 		}
 	}
 
@@ -99,7 +99,7 @@ SampleWindow = (function()
 		{
 			var cell_data =
 			{
-				_ID: i,
+			    _ID: i,
 				Name: "",
 				Control: new WM.Label()
 			};
@@ -114,7 +114,7 @@ SampleWindow = (function()
 	}
 
 
-	function UpdateSamples(parent_row, samples, index, indent)
+	function UpdateAllSampleFields(parent_row, samples, index, indent)
 	{
 		for (var i in samples)
 		{
@@ -131,21 +131,24 @@ SampleWindow = (function()
 			row.CellData._ID = sample.id;
 			parent_row.Rows.AddRowToIndex("_ID", sample.id, row);
 
-			// Set sample name and colour
+            // Record sample name for later comparison
+			row.CellData.Name = sample.name;
+			
+		    // Set sample name and colour
 			var name_node = row.CellNodes["Name"];
 			name_node.innerHTML = indent + sample.name;
 			DOM.Node.SetColour(name_node, sample.colour);
 
 			row.CellData.Control.SetText(sample.us_length);
 
-			index = UpdateSamples(parent_row, sample.children, index, indent + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
+			index = UpdateAllSampleFields(parent_row, sample.children, index, indent + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
 		}
 
 		return index;
 	}
 
 
-	function UpdateSampleTimes(parent_row, samples)
+	function UpdateChangedSampleFields(parent_row, samples, indent)
 	{
 		for (var i in samples)
 		{
@@ -153,9 +156,20 @@ SampleWindow = (function()
 
 			var row = parent_row.Rows.GetBy("_ID", sample.id);
 			if (row)
-				row.CellData.Control.SetText(sample.us_length);
+			{
+			    row.CellData.Control.SetText(sample.us_length);
+
+			    // Sample name will change when it switches from hash ID to network-retrieved 
+                // name. Quickly check that before re-applying the HTML for the name.
+			    if (row.CellData.Name != sample.name)
+			    {
+			        var name_node = row.CellNodes["Name"];
+			        row.CellData.Name = sample.name;
+			        name_node.innerHTML = indent + sample.name;
+			    }
+			}
 
-			UpdateSampleTimes(parent_row, sample.children);
+			UpdateChangedSampleFields(parent_row, sample.children, indent + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
 		}
 	}
 

+ 3 - 2
3rdparty/remotery/vis/Code/TimelineRow.js

@@ -356,8 +356,9 @@ TimelineRow = (function()
 			ctx.strokeRect(offset_x + 0.5, offset_y + 0.5, size_x - 1, size_y - 1);
 		}
 
-		// Draw sample names clipped to the bounds of the sample
-		if (draw_text)
+	    // Draw sample names clipped to the bounds of the sample
+	    // Also reject tiny samples with no space to render text
+		if (draw_text && size_x > 8)
 		{
 			ctx.save();
 			ctx.beginPath();

+ 12 - 5
3rdparty/remotery/vis/Code/WebSocketConnection.js

@@ -52,6 +52,7 @@ WebSocketConnection = (function()
 		Log(this, "Connecting to " + address);
 
 		this.Socket = new WebSocket(address);
+		this.Socket.binaryType = "arraybuffer";
 		this.Socket.onopen = Bind(OnOpen, this);
 		this.Socket.onmessage = Bind(OnMessage, this);
 		this.Socket.onclose = Bind(OnClose, this);
@@ -80,13 +81,13 @@ WebSocketConnection = (function()
 	}
 
 
-	function CallMessageHandlers(self, message_name, message)
+	function CallMessageHandlers(self, message_name, data_view)
 	{
 		if (message_name in self.MessageHandlers)
 		{
 			var handlers = self.MessageHandlers[message_name];
 			for (var i in handlers)
-				handlers[i](self, message);
+			    handlers[i](self, data_view);
 		}
 	}
 
@@ -120,9 +121,15 @@ WebSocketConnection = (function()
 
 	function OnMessage(self, event)
 	{
-		var message = JSON.parse(event.data);
-		if ("id" in message)
-			CallMessageHandlers(self, message.id, message);
+	    var data_view = new DataView(event.data);
+
+	    var id = String.fromCharCode(
+            data_view.getInt8(0),
+            data_view.getInt8(1),
+            data_view.getInt8(2),
+            data_view.getInt8(3));
+
+        CallMessageHandlers(self, id, data_view);
 	}
 
 

+ 1 - 0
3rdparty/remotery/vis/index.html

@@ -29,6 +29,7 @@
 		<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/Button.js"></script>
 
 		<!-- Main Application -->
+        <script type="text/javascript" src="Code/DataViewReader.js"></script>
 		<script type="text/javascript" src="Code/Console.js"></script>
 		<script type="text/javascript" src="Code/WebSocketConnection.js"></script>
 		<script type="text/javascript" src="Code/TitleWindow.js"></script>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.