This commit is contained in:
Branimir Karadžić 2017-01-18 09:47:13 -08:00
Родитель fd6ab494ff
Коммит 8914ad09e5
11 изменённых файлов: 1386 добавлений и 507 удалений

1529
3rdparty/remotery/lib/Remotery.c поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

63
3rdparty/remotery/lib/Remotery.h поставляемый
Просмотреть файл

@ -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

49
3rdparty/remotery/lib/RemoteryMetal.mm поставляемый Normal file
Просмотреть файл

@ -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
3rdparty/remotery/readme.md поставляемый
Просмотреть файл

@ -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
------------------------------- -------------------------------

8
3rdparty/remotery/vis/Code/Console.js поставляемый
Просмотреть файл

@ -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);
} }

47
3rdparty/remotery/vis/Code/DataViewReader.js поставляемый Normal file
Просмотреть файл

@ -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;
})();

117
3rdparty/remotery/vis/Code/Remotery.js поставляемый
Просмотреть файл

@ -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

34
3rdparty/remotery/vis/Code/SampleWindow.js поставляемый
Просмотреть файл

@ -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 + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"); index = UpdateAllSampleFields(parent_row, sample.children, index, indent + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
} }
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 + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
} }
} }

5
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); 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);
} }

1
3rdparty/remotery/vis/index.html поставляемый
Просмотреть файл

@ -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>