Updated Remotery.
This commit is contained in:
Родитель
fd6ab494ff
Коммит
8914ad09e5
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -67,6 +67,11 @@ documented just below this comment.
|
||||||
#define RMT_USE_OPENGL 0
|
#define RMT_USE_OPENGL 0
|
||||||
#endif
|
#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, ...
|
// Initially use POSIX thread names to name threads instead of Thread0, 1, ...
|
||||||
#ifndef RMT_USE_POSIX_THREADNAMES
|
#ifndef RMT_USE_POSIX_THREADNAMES
|
||||||
#define RMT_USE_POSIX_THREADNAMES 0
|
#define RMT_USE_POSIX_THREADNAMES 0
|
||||||
|
@ -129,9 +134,14 @@ documented just below this comment.
|
||||||
#define IFDEF_RMT_USE_D3D11(t, f) f
|
#define IFDEF_RMT_USE_D3D11(t, f) f
|
||||||
#endif
|
#endif
|
||||||
#if RMT_ENABLED && RMT_USE_OPENGL
|
#if RMT_ENABLED && RMT_USE_OPENGL
|
||||||
#define IFDEF_RMT_USE_OPENGL(t, f) t
|
#define IFDEF_RMT_USE_OPENGL(t, f) t
|
||||||
#else
|
#else
|
||||||
#define IFDEF_RMT_USE_OPENGL(t, f) f
|
#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_METAL(t, f) f
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -237,7 +247,7 @@ typedef enum rmtError
|
||||||
RMT_ERROR_D3D11_FAILED_TO_CREATE_QUERY, // Failed to create query for sample
|
RMT_ERROR_D3D11_FAILED_TO_CREATE_QUERY, // Failed to create query for sample
|
||||||
|
|
||||||
// OpenGL error messages
|
// 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,
|
RMT_ERROR_CUDA_UNKNOWN,
|
||||||
} rmtError;
|
} rmtError;
|
||||||
|
@ -424,6 +434,26 @@ typedef struct rmtCUDABind
|
||||||
RMT_OPTIONAL(RMT_USE_OPENGL, _rmt_EndOpenGLSample())
|
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
|
#endif
|
||||||
|
|
||||||
|
#if RMT_USE_METAL
|
||||||
|
extern "C" RMT_API void _rmt_EndMetalSample(void);
|
||||||
|
struct rmt_EndMetalSampleOnScopeExit
|
||||||
|
{
|
||||||
|
~rmt_EndMetalSampleOnScopeExit()
|
||||||
|
{
|
||||||
|
_rmt_EndMetalSample();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -502,6 +543,9 @@ struct rmt_EndOpenGLSampleOnScopeExit
|
||||||
#define rmt_ScopedOpenGLSample(name) \
|
#define rmt_ScopedOpenGLSample(name) \
|
||||||
RMT_OPTIONAL(RMT_USE_OPENGL, rmt_BeginOpenGLSample(name)); \
|
RMT_OPTIONAL(RMT_USE_OPENGL, rmt_BeginOpenGLSample(name)); \
|
||||||
RMT_OPTIONAL(RMT_USE_OPENGL, rmt_EndOpenGLSampleOnScopeExit rmt_ScopedOpenGLSample##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
|
#endif
|
||||||
|
|
||||||
|
@ -553,10 +597,23 @@ RMT_API void _rmt_BeginOpenGLSample(rmtPStr name, rmtU32* hash_cache);
|
||||||
RMT_API void _rmt_EndOpenGLSample(void);
|
RMT_API void _rmt_EndOpenGLSample(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if RMT_USE_METAL
|
||||||
|
RMT_API void _rmt_BeginMetalSample(rmtPStr name, rmtU32* hash_cache);
|
||||||
|
RMT_API void _rmt_EndMetalSample(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#endif // RMT_ENABLED
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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; }];
|
||||||
|
}
|
|
@ -23,7 +23,7 @@ Compiling
|
||||||
directories to add Remotery/lib path. The required library ws2_32.lib should be picked
|
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.
|
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
|
* 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
|
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_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_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_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
|
Basic Use
|
||||||
|
@ -176,6 +177,24 @@ your OpenGL device and context, ensure you notify Remotery before shutting down
|
||||||
rmt_UnbindOpenGL();
|
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
|
Applying Configuration Settings
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ Console = (function()
|
||||||
// Setup log requests from the server
|
// Setup log requests from the server
|
||||||
this.Server = server;
|
this.Server = server;
|
||||||
server.SetConsole(this);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
})();
|
|
@ -20,12 +20,37 @@ Settings = (function()
|
||||||
|
|
||||||
Remotery = (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()
|
function Remotery()
|
||||||
{
|
{
|
||||||
this.WindowManager = new WM.WindowManager();
|
this.WindowManager = new WM.WindowManager();
|
||||||
this.Settings = new Settings();
|
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 = new WebSocketConnection();
|
||||||
this.Server.AddConnectHandler(Bind(OnConnect, this));
|
this.Server.AddConnectHandler(Bind(OnConnect, this));
|
||||||
|
|
||||||
|
@ -43,8 +68,10 @@ Remotery = (function()
|
||||||
this.SampleWindows = { };
|
this.SampleWindows = { };
|
||||||
this.FrameHistory = { };
|
this.FrameHistory = { };
|
||||||
this.SelectedFrames = { };
|
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
|
// Kick-off the auto-connect loop
|
||||||
AutoConnect(this);
|
AutoConnect(this);
|
||||||
|
@ -80,10 +107,6 @@ Remotery = (function()
|
||||||
{
|
{
|
||||||
// Connection address has been validated
|
// Connection address has been validated
|
||||||
LocalStore.Set("App", "Global", "ConnectionAddress", self.ConnectionAddress);
|
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
|
// 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
|
// 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)
|
if ((self.DisplayFrame % 10) == 0)
|
||||||
self.TimelineWindow.DrawAllRows();
|
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
|
// Discard any new samples while paused
|
||||||
if (self.Settings.IsPaused)
|
if (self.Settings.IsPaused)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Add to frame history for this thread
|
// 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
|
||||||
var thread_frame = new ThreadFrame(message);
|
var thread_frame = new ThreadFrame(message);
|
||||||
if (!(name in self.FrameHistory))
|
if (!(name in self.FrameHistory))
|
||||||
self.FrameHistory[name] = [ ];
|
self.FrameHistory[name] = [ ];
|
||||||
|
@ -142,7 +225,7 @@ Remotery = (function()
|
||||||
var max_nb_frames = 10000;
|
var max_nb_frames = 10000;
|
||||||
var extra_frames = frame_history.length - max_nb_frames;
|
var extra_frames = frame_history.length - max_nb_frames;
|
||||||
if (extra_frames > 0)
|
if (extra_frames > 0)
|
||||||
frame_history.splice(0, extra_frames);
|
frame_history.splice(0, extra_frames);
|
||||||
|
|
||||||
// Create sample windows on-demand
|
// Create sample windows on-demand
|
||||||
if (!(name in self.SampleWindows))
|
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)
|
function OnTimelineCheck(self, name, evt)
|
||||||
{
|
{
|
||||||
// Show/hide the equivalent sample window and move all the others to occupy any left-over space
|
// Show/hide the equivalent sample window and move all the others to occupy any left-over space
|
||||||
|
|
|
@ -72,7 +72,7 @@ SampleWindow = (function()
|
||||||
if (this.SampleDigest != sample_digest)
|
if (this.SampleDigest != sample_digest)
|
||||||
{
|
{
|
||||||
this.RootRow.Rows.ClearIndex("_ID");
|
this.RootRow.Rows.ClearIndex("_ID");
|
||||||
var index = UpdateSamples(this.RootRow, samples, 0, "");
|
var index = UpdateAllSampleFields(this.RootRow, samples, 0, "");
|
||||||
this.SampleDigest = sample_digest;
|
this.SampleDigest = sample_digest;
|
||||||
|
|
||||||
// Clear out any left-over rows
|
// Clear out any left-over rows
|
||||||
|
@ -85,8 +85,8 @@ SampleWindow = (function()
|
||||||
|
|
||||||
else if (this.Visible)
|
else if (this.Visible)
|
||||||
{
|
{
|
||||||
// Otherwise just update the existing sample times
|
// Otherwise just update the existing sample fields
|
||||||
UpdateSampleTimes(this.RootRow, samples);
|
UpdateChangedSampleFields(this.RootRow, samples, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ SampleWindow = (function()
|
||||||
{
|
{
|
||||||
var cell_data =
|
var cell_data =
|
||||||
{
|
{
|
||||||
_ID: i,
|
_ID: i,
|
||||||
Name: "",
|
Name: "",
|
||||||
Control: new WM.Label()
|
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)
|
for (var i in samples)
|
||||||
{
|
{
|
||||||
|
@ -131,21 +131,24 @@ SampleWindow = (function()
|
||||||
row.CellData._ID = sample.id;
|
row.CellData._ID = sample.id;
|
||||||
parent_row.Rows.AddRowToIndex("_ID", sample.id, row);
|
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"];
|
var name_node = row.CellNodes["Name"];
|
||||||
name_node.innerHTML = indent + sample.name;
|
name_node.innerHTML = indent + sample.name;
|
||||||
DOM.Node.SetColour(name_node, sample.colour);
|
DOM.Node.SetColour(name_node, sample.colour);
|
||||||
|
|
||||||
row.CellData.Control.SetText(sample.us_length);
|
row.CellData.Control.SetText(sample.us_length);
|
||||||
|
|
||||||
index = UpdateSamples(parent_row, sample.children, index, indent + " ");
|
index = UpdateAllSampleFields(parent_row, sample.children, index, indent + " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function UpdateSampleTimes(parent_row, samples)
|
function UpdateChangedSampleFields(parent_row, samples, indent)
|
||||||
{
|
{
|
||||||
for (var i in samples)
|
for (var i in samples)
|
||||||
{
|
{
|
||||||
|
@ -153,9 +156,20 @@ SampleWindow = (function()
|
||||||
|
|
||||||
var row = parent_row.Rows.GetBy("_ID", sample.id);
|
var row = parent_row.Rows.GetBy("_ID", sample.id);
|
||||||
if (row)
|
if (row)
|
||||||
row.CellData.Control.SetText(sample.us_length);
|
{
|
||||||
|
row.CellData.Control.SetText(sample.us_length);
|
||||||
|
|
||||||
UpdateSampleTimes(parent_row, sample.children);
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateChangedSampleFields(parent_row, sample.children, indent + " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -356,8 +356,9 @@ TimelineRow = (function()
|
||||||
ctx.strokeRect(offset_x + 0.5, offset_y + 0.5, size_x - 1, size_y - 1);
|
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
|
// Draw sample names clipped to the bounds of the sample
|
||||||
if (draw_text)
|
// Also reject tiny samples with no space to render text
|
||||||
|
if (draw_text && size_x > 8)
|
||||||
{
|
{
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|
|
@ -52,6 +52,7 @@ WebSocketConnection = (function()
|
||||||
Log(this, "Connecting to " + address);
|
Log(this, "Connecting to " + address);
|
||||||
|
|
||||||
this.Socket = new WebSocket(address);
|
this.Socket = new WebSocket(address);
|
||||||
|
this.Socket.binaryType = "arraybuffer";
|
||||||
this.Socket.onopen = Bind(OnOpen, this);
|
this.Socket.onopen = Bind(OnOpen, this);
|
||||||
this.Socket.onmessage = Bind(OnMessage, this);
|
this.Socket.onmessage = Bind(OnMessage, this);
|
||||||
this.Socket.onclose = Bind(OnClose, 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)
|
if (message_name in self.MessageHandlers)
|
||||||
{
|
{
|
||||||
var handlers = self.MessageHandlers[message_name];
|
var handlers = self.MessageHandlers[message_name];
|
||||||
for (var i in handlers)
|
for (var i in handlers)
|
||||||
handlers[i](self, message);
|
handlers[i](self, data_view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,9 +121,15 @@ WebSocketConnection = (function()
|
||||||
|
|
||||||
function OnMessage(self, event)
|
function OnMessage(self, event)
|
||||||
{
|
{
|
||||||
var message = JSON.parse(event.data);
|
var data_view = new DataView(event.data);
|
||||||
if ("id" in message)
|
|
||||||
CallMessageHandlers(self, message.id, message);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/Button.js"></script>
|
<script type="text/javascript" src="extern/BrowserLib/WindowManager/Code/Button.js"></script>
|
||||||
|
|
||||||
<!-- Main Application -->
|
<!-- 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/Console.js"></script>
|
||||||
<script type="text/javascript" src="Code/WebSocketConnection.js"></script>
|
<script type="text/javascript" src="Code/WebSocketConnection.js"></script>
|
||||||
<script type="text/javascript" src="Code/TitleWindow.js"></script>
|
<script type="text/javascript" src="Code/TitleWindow.js"></script>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче