-
Notifications
You must be signed in to change notification settings - Fork 274
/
Copy pathRemotery.h
1110 lines (871 loc) · 45.2 KB
/
Remotery.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Copyright 2014-2022 Celtoys Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Compiling
---------
* Windows (MSVC) - add lib/Remotery.c and lib/Remotery.h to your program. Set include
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.
* 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
-I lib -pthread -lm
You can define some extra macros to modify what features are compiled into Remotery. These are
documented just below this comment.
*/
#ifndef RMT_INCLUDED_H
#define RMT_INCLUDED_H
// Set to 0 to not include any bits of Remotery in your build
#ifndef RMT_ENABLED
#define RMT_ENABLED 1
#endif
// Help performance of the server sending data to the client by marking this machine as little-endian
#ifndef RMT_ASSUME_LITTLE_ENDIAN
#define RMT_ASSUME_LITTLE_ENDIAN 0
#endif
// Used by the Celtoys TinyCRT library (not released yet)
#ifndef RMT_USE_TINYCRT
#define RMT_USE_TINYCRT 0
#endif
// Assuming CUDA headers/libs are setup, allow CUDA profiling
#ifndef RMT_USE_CUDA
#define RMT_USE_CUDA 0
#endif
// Assuming Direct3D 11 headers/libs are setup, allow D3D11 profiling
#ifndef RMT_USE_D3D11
#define RMT_USE_D3D11 0
#endif
// Allow D3D12 profiling
#ifndef RMT_USE_D3D12
#define RMT_USE_D3D12 0
#endif
// Allow OpenGL profiling
#ifndef RMT_USE_OPENGL
#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
#endif
// How many times we spin data back and forth between CPU & GPU
// to calculate average RTT (Roundtrip Time). Cannot be 0.
// Affects OpenGL & D3D11
#ifndef RMT_GPU_CPU_SYNC_NUM_ITERATIONS
#define RMT_GPU_CPU_SYNC_NUM_ITERATIONS 16
#endif
// Time in seconds between each resync to compensate for drifting between GPU & CPU timers,
// effects of power saving, etc. Resyncs can cause stutter, lag spikes, stalls.
// Set to 0 for never.
// Affects OpenGL & D3D11
#ifndef RMT_GPU_CPU_SYNC_SECONDS
#define RMT_GPU_CPU_SYNC_SECONDS 30
#endif
// Whether we should automatically resync if we detect a timer disjoint (e.g.
// changed from AC power to battery, GPU is overheating, or throttling up/down
// due to laptop savings events). Set it to 0 to avoid resync in such events.
// Useful if for some odd reason a driver reports a lot of disjoints.
// Affects D3D11
#ifndef RMT_D3D11_RESYNC_ON_DISJOINT
#define RMT_D3D11_RESYNC_ON_DISJOINT 1
#endif
// If RMT_USE_INTERNAL_HASH_FUNCTION is defined to 1, the internal hash function for strings is used.
// This is the default setting.
// If you set RMT_USE_INTERNAL_HASH_FUNCTION to 0, you must implement rmt_HashString32 yourself.
#ifndef RMT_USE_INTERNAL_HASH_FUNCTION
#define RMT_USE_INTERNAL_HASH_FUNCTION 1
#endif
// If RMT_USE_LEGACY_ATOMICS is defined to 1, the implementation will use the legacy fallback atomic functions
// The default setting is 0
#ifndef RMT_USE_LEGACY_ATOMICS
#define RMT_USE_LEGACY_ATOMICS 0
#endif
/*--------------------------------------------------------------------------------------------------------------------------------
Compiler/Platform Detection and Preprocessor Utilities
---------------------------------------------------------------------------------------------------------------------------------*/
// Platform identification
#if defined(_WINDOWS) || defined(_WIN32)
#define RMT_PLATFORM_WINDOWS
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#define RMT_PLATFORM_LINUX
#define RMT_PLATFORM_POSIX
#elif defined(__APPLE__)
#define RMT_PLATFORM_MACOS
#define RMT_PLATFORM_POSIX
#endif
// Architecture identification
#ifdef RMT_PLATFORM_WINDOWS
#if defined(_M_AMD64) || defined(__x86_64__) // MSVC defines _M_AMD64 and MinGW-64 defines __x86_64__
#define RMT_ARCH_64BIT
#else
#define RMT_ARCH_32BIT
#endif
#endif
#if __GNUC__ || __clang__
#if __x86_64__ || __ppc64__ || __amd64__
#define RMT_ARCH_64BIT
#else
#define RMT_ARCH_32BIT
#endif
#endif
#ifdef RMT_DLL
#if defined (RMT_PLATFORM_WINDOWS)
#if defined (RMT_IMPL)
#define RMT_API __declspec(dllexport)
#else
#define RMT_API __declspec(dllimport)
#endif
#elif defined (RMT_PLATFORM_POSIX)
#if defined (RMT_IMPL)
#define RMT_API __attribute__((visibility("default")))
#else
#define RMT_API
#endif
#endif
#else
#define RMT_API
#endif
// Allows macros to be written that can work around the inability to do: #define(x) #ifdef x
// with the C preprocessor.
#if RMT_ENABLED
#define IFDEF_RMT_ENABLED(t, f) t
#else
#define IFDEF_RMT_ENABLED(t, f) f
#endif
#if RMT_ENABLED && RMT_USE_CUDA
#define IFDEF_RMT_USE_CUDA(t, f) t
#else
#define IFDEF_RMT_USE_CUDA(t, f) f
#endif
#if RMT_ENABLED && RMT_USE_D3D11
#define IFDEF_RMT_USE_D3D11(t, f) t
#else
#define IFDEF_RMT_USE_D3D11(t, f) f
#endif
#if RMT_ENABLED && RMT_USE_D3D12
#define IFDEF_RMT_USE_D3D12(t, f) t
#else
#define IFDEF_RMT_USE_D3D12(t, f) f
#endif
#if RMT_ENABLED && RMT_USE_OPENGL
#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_METAL(t, f) f
#endif
// Public interface is written in terms of these macros to easily enable/disable itself
#define RMT_OPTIONAL(macro, x) IFDEF_ ## macro(x, )
#define RMT_OPTIONAL_RET(macro, x, y) IFDEF_ ## macro(x, (y))
/*--------------------------------------------------------------------------------------------------------------------------------
Types
--------------------------------------------------------------------------------------------------------------------------------*/
// Boolean
typedef unsigned int rmtBool;
#define RMT_TRUE ((rmtBool)1)
#define RMT_FALSE ((rmtBool)0)
// Unsigned integer types
typedef unsigned char rmtU8;
typedef unsigned short rmtU16;
typedef unsigned int rmtU32;
typedef unsigned long long rmtU64;
// Signed integer types
typedef char rmtS8;
typedef short rmtS16;
typedef int rmtS32;
typedef long long rmtS64;
// Float types
typedef float rmtF32;
typedef double rmtF64;
// Const, null-terminated string pointer
typedef const char* rmtPStr;
// Opaque pointer for a sample graph tree
typedef struct Msg_SampleTree rmtSampleTree;
// Opaque pointer to a node in the sample graph tree
typedef struct Sample rmtSample;
// Handle to the main remotery instance
typedef struct Remotery Remotery;
// Forward declaration
struct rmtProperty;
typedef enum rmtSampleType
{
RMT_SampleType_CPU,
RMT_SampleType_CUDA,
RMT_SampleType_D3D11,
RMT_SampleType_D3D12,
RMT_SampleType_OpenGL,
RMT_SampleType_Metal,
RMT_SampleType_Count,
} rmtSampleType;
// All possible error codes
// clang-format off
typedef enum rmtError
{
RMT_ERROR_NONE,
RMT_ERROR_RECURSIVE_SAMPLE, // Not an error but an internal message to calling code
RMT_ERROR_UNKNOWN, // An error with a message yet to be defined, only for internal error handling
RMT_ERROR_INVALID_INPUT, // An invalid input to a function call was provided
RMT_ERROR_RESOURCE_CREATE_FAIL, // Creation of an internal resource failed
RMT_ERROR_RESOURCE_ACCESS_FAIL, // Access of an internal resource failed
RMT_ERROR_TIMEOUT, // Internal system timeout
// System errors
RMT_ERROR_MALLOC_FAIL, // Malloc call within remotery failed
RMT_ERROR_TLS_ALLOC_FAIL, // Attempt to allocate thread local storage failed
RMT_ERROR_VIRTUAL_MEMORY_BUFFER_FAIL, // Failed to create a virtual memory mirror buffer
RMT_ERROR_CREATE_THREAD_FAIL, // Failed to create a thread for the server
RMT_ERROR_OPEN_THREAD_HANDLE_FAIL, // Failed to open a thread handle, given a thread id
// Network TCP/IP socket errors
RMT_ERROR_SOCKET_INVALID_POLL, // Poll attempt on an invalid socket
RMT_ERROR_SOCKET_SELECT_FAIL, // Server failed to call select on socket
RMT_ERROR_SOCKET_POLL_ERRORS, // Poll notified that the socket has errors
RMT_ERROR_SOCKET_SEND_FAIL, // Unrecoverable error occured while client/server tried to send data
RMT_ERROR_SOCKET_RECV_NO_DATA, // No data available when attempting a receive
RMT_ERROR_SOCKET_RECV_TIMEOUT, // Timed out trying to receive data
RMT_ERROR_SOCKET_RECV_FAILED, // Unrecoverable error occured while client/server tried to receive data
// WebSocket errors
RMT_ERROR_WEBSOCKET_HANDSHAKE_NOT_GET, // WebSocket server handshake failed, not HTTP GET
RMT_ERROR_WEBSOCKET_HANDSHAKE_NO_VERSION, // WebSocket server handshake failed, can't locate WebSocket version
RMT_ERROR_WEBSOCKET_HANDSHAKE_BAD_VERSION, // WebSocket server handshake failed, unsupported WebSocket version
RMT_ERROR_WEBSOCKET_HANDSHAKE_NO_HOST, // WebSocket server handshake failed, can't locate host
RMT_ERROR_WEBSOCKET_HANDSHAKE_BAD_HOST, // WebSocket server handshake failed, host is not allowed to connect
RMT_ERROR_WEBSOCKET_HANDSHAKE_NO_KEY, // WebSocket server handshake failed, can't locate WebSocket key
RMT_ERROR_WEBSOCKET_HANDSHAKE_BAD_KEY, // WebSocket server handshake failed, WebSocket key is ill-formed
RMT_ERROR_WEBSOCKET_HANDSHAKE_STRING_FAIL, // WebSocket server handshake failed, internal error, bad string code
RMT_ERROR_WEBSOCKET_DISCONNECTED, // WebSocket server received a disconnect request and closed the socket
RMT_ERROR_WEBSOCKET_BAD_FRAME_HEADER, // Couldn't parse WebSocket frame header
RMT_ERROR_WEBSOCKET_BAD_FRAME_HEADER_SIZE, // Partially received wide frame header size
RMT_ERROR_WEBSOCKET_BAD_FRAME_HEADER_MASK, // Partially received frame header data mask
RMT_ERROR_WEBSOCKET_RECEIVE_TIMEOUT, // Timeout receiving frame header
RMT_ERROR_REMOTERY_NOT_CREATED, // Remotery object has not been created
RMT_ERROR_SEND_ON_INCOMPLETE_PROFILE, // An attempt was made to send an incomplete profile tree to the client
// CUDA error messages
RMT_ERROR_CUDA_DEINITIALIZED, // This indicates that the CUDA driver is in the process of shutting down
RMT_ERROR_CUDA_NOT_INITIALIZED, // This indicates that the CUDA driver has not been initialized with cuInit() or that initialization has failed
RMT_ERROR_CUDA_INVALID_CONTEXT, // This most frequently indicates that there is no context bound to the current thread
RMT_ERROR_CUDA_INVALID_VALUE, // This indicates that one or more of the parameters passed to the API call is not within an acceptable range of values
RMT_ERROR_CUDA_INVALID_HANDLE, // This indicates that a resource handle passed to the API call was not valid
RMT_ERROR_CUDA_OUT_OF_MEMORY, // The API call failed because it was unable to allocate enough memory to perform the requested operation
RMT_ERROR_ERROR_NOT_READY, // This indicates that a resource handle passed to the API call was not valid
// Direct3D 11 error messages
RMT_ERROR_D3D11_FAILED_TO_CREATE_QUERY, // Failed to create query for sample
// OpenGL error messages
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;
// clang-format on
// Gets the last error message issued on the calling thread
RMT_API rmtPStr rmt_GetLastErrorMessage();
/*--------------------------------------------------------------------------------------------------------------------------------
Runtime Settings
--------------------------------------------------------------------------------------------------------------------------------*/
// Callback function pointer types
typedef void* (*rmtMallocPtr)(void* mm_context, rmtU32 size);
typedef void* (*rmtReallocPtr)(void* mm_context, void* ptr, rmtU32 size);
typedef void (*rmtFreePtr)(void* mm_context, void* ptr);
typedef void (*rmtInputHandlerPtr)(const char* text, void* context);
typedef void (*rmtSampleTreeHandlerPtr)(void* cbk_context, rmtSampleTree* sample_tree);
typedef void (*rmtPropertyHandlerPtr)(void* cbk_context, struct rmtProperty* root);
// Struture to fill in to modify Remotery default settings
typedef struct rmtSettings
{
// Which port to listen for incoming connections on
rmtU16 port;
// When this server exits it can leave the port open in TIME_WAIT state for a while. This forces
// subsequent server bind attempts to fail when restarting. If you find restarts fail repeatedly
// with bind attempts, set this to true to forcibly reuse the open port.
rmtBool reuse_open_port;
// Only allow connections on localhost?
// For dev builds you may want to access your game from other devices but if
// you distribute a game to your players with Remotery active, probably best
// to limit connections to localhost.
rmtBool limit_connections_to_localhost;
// Whether to enable runtime thread sampling that discovers which processors a thread is running
// on. This will suspend and resume threads from outside repeatdly and inject code into each
// thread that automatically instruments the processor.
// Default: Enabled
rmtBool enableThreadSampler;
// How long to sleep between server updates, hopefully trying to give
// a little CPU back to other threads.
rmtU32 msSleepBetweenServerUpdates;
// Size of the internal message queues Remotery uses
// Will be rounded to page granularity of 64k
rmtU32 messageQueueSizeInBytes;
// If the user continuously pushes to the message queue, the server network
// code won't get a chance to update unless there's an upper-limit on how
// many messages can be consumed per loop.
rmtU32 maxNbMessagesPerUpdate;
// Callback pointers for memory allocation
rmtMallocPtr malloc;
rmtReallocPtr realloc;
rmtFreePtr free;
void* mm_context;
// Callback pointer for receiving input from the Remotery console
rmtInputHandlerPtr input_handler;
// Callback pointer for traversing the sample tree graph
rmtSampleTreeHandlerPtr sampletree_handler;
void* sampletree_context;
// Callback pointer for traversing the prpperty graph
rmtPropertyHandlerPtr snapshot_callback;
void* snapshot_context;
// Context pointer that gets sent to Remotery console callback function
void* input_handler_context;
rmtPStr logPath;
} rmtSettings;
// Retrieve and configure the global rmtSettings object; returns `rmtSettings*`.
// This can be done before or after Remotery is initialised, however some fields are only referenced on initialisation.
#define rmt_Settings() \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_Settings(), NULL )
/*--------------------------------------------------------------------------------------------------------------------------------
Initialisation/Shutdown
--------------------------------------------------------------------------------------------------------------------------------*/
// Can call remotery functions on a null pointer
// TODO: Can embed extern "C" in these macros?
// Initialises Remotery and sets its internal global instance pointer.
// Parameter is `Remotery**`, returning you the pointer for further use.
#define rmt_CreateGlobalInstance(rmt) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_CreateGlobalInstance(rmt), RMT_ERROR_NONE)
// Shutsdown Remotery, requiring its pointer to be passed to ensure you are destroying the correct instance.
#define rmt_DestroyGlobalInstance(rmt) \
RMT_OPTIONAL(RMT_ENABLED, _rmt_DestroyGlobalInstance(rmt))
// For use in the presence of DLLs/SOs if each of them are linking Remotery statically.
// If Remotery is hosted in its own DLL and linked dynamically then there is no need to use this.
// Otherwise, pass the result of `rmt_CreateGlobalInstance` from your main DLL to this in your other DLLs.
#define rmt_SetGlobalInstance(rmt) \
RMT_OPTIONAL(RMT_ENABLED, _rmt_SetGlobalInstance(rmt))
// Get a pointer to the current global Remotery instance.
#define rmt_GetGlobalInstance() \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_GetGlobalInstance(), NULL)
/*--------------------------------------------------------------------------------------------------------------------------------
CPU Sampling
--------------------------------------------------------------------------------------------------------------------------------*/
#define rmt_SetCurrentThreadName(rmt) \
RMT_OPTIONAL(RMT_ENABLED, _rmt_SetCurrentThreadName(rmt))
#define rmt_LogText(text) \
RMT_OPTIONAL(RMT_ENABLED, _rmt_LogText(text))
#define rmt_BeginCPUSample(name, flags) \
RMT_OPTIONAL(RMT_ENABLED, { \
static rmtU32 rmt_sample_hash_##name = 0; \
_rmt_BeginCPUSample(#name, flags, &rmt_sample_hash_##name); \
})
#define rmt_BeginCPUSampleDynamic(namestr, flags) \
RMT_OPTIONAL(RMT_ENABLED, _rmt_BeginCPUSample(namestr, flags, NULL))
#define rmt_EndCPUSample() \
RMT_OPTIONAL(RMT_ENABLED, _rmt_EndCPUSample())
// Used for both CPU and GPU profiling
// Essential to call this every frame, ever since D3D12 support was added
// D3D12 Requirements: Don't sample any command lists that begin before this call and end after it
#define rmt_MarkFrame() \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_MarkFrame(), RMT_ERROR_NONE)
/*--------------------------------------------------------------------------------------------------------------------------------
GPU Sampling
--------------------------------------------------------------------------------------------------------------------------------*/
// Structure to fill in when binding CUDA to Remotery
typedef struct rmtCUDABind
{
// The main context that all driver functions apply before each call
void* context;
// Driver API function pointers that need to be pointed to
// Untyped so that the CUDA headers are not required in this file
// NOTE: These are named differently to the CUDA functions because the CUDA API has a habit of using
// macros to point function calls to different versions, e.g. cuEventDestroy is a macro for
// cuEventDestroy_v2.
void* CtxSetCurrent;
void* CtxGetCurrent;
void* EventCreate;
void* EventDestroy;
void* EventRecord;
void* EventQuery;
void* EventElapsedTime;
} rmtCUDABind;
// Call once after you've initialised CUDA to bind it to Remotery
#define rmt_BindCUDA(bind) \
RMT_OPTIONAL(RMT_USE_CUDA, _rmt_BindCUDA(bind))
// Mark the beginning of a CUDA sample on the specified asynchronous stream
#define rmt_BeginCUDASample(name, stream) \
RMT_OPTIONAL(RMT_USE_CUDA, { \
static rmtU32 rmt_sample_hash_##name = 0; \
_rmt_BeginCUDASample(#name, &rmt_sample_hash_##name, stream); \
})
// Mark the end of a CUDA sample on the specified asynchronous stream
#define rmt_EndCUDASample(stream) \
RMT_OPTIONAL(RMT_USE_CUDA, _rmt_EndCUDASample(stream))
#define rmt_BindD3D11(device, context) \
RMT_OPTIONAL(RMT_USE_D3D11, _rmt_BindD3D11(device, context))
#define rmt_UnbindD3D11() \
RMT_OPTIONAL(RMT_USE_D3D11, _rmt_UnbindD3D11())
#define rmt_BeginD3D11Sample(name) \
RMT_OPTIONAL(RMT_USE_D3D11, { \
static rmtU32 rmt_sample_hash_##name = 0; \
_rmt_BeginD3D11Sample(#name, &rmt_sample_hash_##name); \
})
#define rmt_BeginD3D11SampleDynamic(namestr) \
RMT_OPTIONAL(RMT_USE_D3D11, _rmt_BeginD3D11Sample(namestr, NULL))
#define rmt_EndD3D11Sample() \
RMT_OPTIONAL(RMT_USE_D3D11, _rmt_EndD3D11Sample())
typedef struct rmtD3D12Bind
{
// The main device shared by all threads
void* device;
// The queue command lists are executed on for profiling
void* queue;
} rmtD3D12Bind;
// Create a D3D12 binding for the given device/queue pair
#define rmt_BindD3D12(device, queue, out_bind) \
RMT_OPTIONAL_RET(RMT_USE_D3D12, _rmt_BindD3D12(device, queue, out_bind), NULL)
#define rmt_UnbindD3D12(bind) \
RMT_OPTIONAL(RMT_USE_D3D12, _rmt_UnbindD3D12(bind))
#define rmt_BeginD3D12Sample(bind, command_list, name) \
RMT_OPTIONAL(RMT_USE_D3D12, { \
static rmtU32 rmt_sample_hash_##name = 0; \
_rmt_BeginD3D12Sample(bind, command_list, #name, &rmt_sample_hash_##name); \
})
#define rmt_BeginD3D12SampleDynamic(bind, command_list, namestr) \
RMT_OPTIONAL(RMT_USE_D3D12, _rmt_BeginD3D12Sample(bind, command_list, namestr, NULL))
#define rmt_EndD3D12Sample() \
RMT_OPTIONAL(RMT_USE_D3D12, _rmt_EndD3D12Sample())
#define rmt_BindOpenGL() \
RMT_OPTIONAL(RMT_USE_OPENGL, _rmt_BindOpenGL())
#define rmt_UnbindOpenGL() \
RMT_OPTIONAL(RMT_USE_OPENGL, _rmt_UnbindOpenGL())
#define rmt_BeginOpenGLSample(name) \
RMT_OPTIONAL(RMT_USE_OPENGL, { \
static rmtU32 rmt_sample_hash_##name = 0; \
_rmt_BeginOpenGLSample(#name, &rmt_sample_hash_##name); \
})
#define rmt_BeginOpenGLSampleDynamic(namestr) \
RMT_OPTIONAL(RMT_USE_OPENGL, _rmt_BeginOpenGLSample(namestr, NULL))
#define 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())
/*--------------------------------------------------------------------------------------------------------------------------------
Runtime Properties
--------------------------------------------------------------------------------------------------------------------------------*/
/* --- Public API --------------------------------------------------------------------------------------------------------------*/
// Flags that control property behaviour
typedef enum
{
RMT_PropertyFlags_NoFlags = 0,
// Reset property back to its default value on each new frame
RMT_PropertyFlags_FrameReset = 1,
} rmtPropertyFlags;
// All possible property types that can be recorded and sent to the viewer
typedef enum
{
RMT_PropertyType_rmtGroup,
RMT_PropertyType_rmtBool,
RMT_PropertyType_rmtS32,
RMT_PropertyType_rmtU32,
RMT_PropertyType_rmtF32,
RMT_PropertyType_rmtS64,
RMT_PropertyType_rmtU64,
RMT_PropertyType_rmtF64,
} rmtPropertyType;
// A property value as a union of all its possible types
typedef union rmtPropertyValue
{
// C++ requires function-based construction of property values because it has no designated initialiser support until C++20
#ifdef __cplusplus
// These are static Make calls, rather than overloaded constructors, because `rmtBool` is the same type as `rmtU32`
static rmtPropertyValue MakeBool(rmtBool v) { rmtPropertyValue pv; pv.Bool = v; return pv; }
static rmtPropertyValue MakeS32(rmtS32 v) { rmtPropertyValue pv; pv.S32 = v; return pv; }
static rmtPropertyValue MakeU32(rmtU32 v) { rmtPropertyValue pv; pv.U32 = v; return pv; }
static rmtPropertyValue MakeF32(rmtF32 v) { rmtPropertyValue pv; pv.F32 = v; return pv; }
static rmtPropertyValue MakeS64(rmtS64 v) { rmtPropertyValue pv; pv.S64 = v; return pv; }
static rmtPropertyValue MakeU64(rmtU64 v) { rmtPropertyValue pv; pv.U64 = v; return pv; }
static rmtPropertyValue MakeF64(rmtF64 v) { rmtPropertyValue pv; pv.F64 = v; return pv; }
#endif
rmtBool Bool;
rmtS32 S32;
rmtU32 U32;
rmtF32 F32;
rmtS64 S64;
rmtU64 U64;
rmtF64 F64;
} rmtPropertyValue;
// Definition of a property that should be stored globally
// Note:
// Use the callback api and the rmt_PropertyGetxxx accessors to traverse this structure
typedef struct rmtProperty
{
// Gets set to RMT_TRUE after a property has been modified, when it gets initialised for the first time
rmtBool initialised;
// Runtime description
rmtPropertyType type;
rmtPropertyFlags flags;
// Current value
rmtPropertyValue value;
// Last frame value to see if previous value needs to be updated
rmtPropertyValue lastFrameValue;
// Previous value only if it's different from the current value, and when it changed
rmtPropertyValue prevValue;
rmtU32 prevValueFrame;
// Text description
const char* name;
const char* description;
// Default value for Reset calls
rmtPropertyValue defaultValue;
// Parent link specifically placed after default value so that variadic macro can initialise it
struct rmtProperty* parent;
// Links within the property tree
struct rmtProperty* firstChild;
struct rmtProperty* lastChild;
struct rmtProperty* nextSibling;
// Hash for efficient sending of properties to the viewer
rmtU32 nameHash;
// Unique, persistent ID among all properties
rmtU32 uniqueID;
} rmtProperty;
// Define properties of different types at global scope:
//
// * Never define properties in a header file that gets included multiple times.
// * The property gets defined exactly as `name` in the global scope.
// * `flag` is specified without the `RMT_PropertyFlags_` prefix.
// * Property parents are optional and can be specified as the last parameter, referencing `&name`.
//
#define rmt_PropertyDefine_Group(name, desc, ...) _rmt_PropertyDefine(rmtGroup, name, _rmt_MakePropertyValue(Bool, 0), RMT_PropertyFlags_NoFlags, desc, __VA_ARGS__)
#define rmt_PropertyDefine_Bool(name, default_value, flag, desc, ...) _rmt_PropertyDefine(rmtBool, name, _rmt_MakePropertyValue(Bool, default_value), RMT_PropertyFlags_##flag, desc, __VA_ARGS__)
#define rmt_PropertyDefine_S32(name, default_value, flag, desc, ...) _rmt_PropertyDefine(rmtS32, name, _rmt_MakePropertyValue(S32, default_value), RMT_PropertyFlags_##flag, desc, __VA_ARGS__)
#define rmt_PropertyDefine_U32(name, default_value, flag, desc, ...) _rmt_PropertyDefine(rmtU32, name, _rmt_MakePropertyValue(U32, default_value), RMT_PropertyFlags_##flag, desc, __VA_ARGS__)
#define rmt_PropertyDefine_F32(name, default_value, flag, desc, ...) _rmt_PropertyDefine(rmtF32, name, _rmt_MakePropertyValue(F32, default_value), RMT_PropertyFlags_##flag, desc, __VA_ARGS__)
#define rmt_PropertyDefine_S64(name, default_value, flag, desc, ...) _rmt_PropertyDefine(rmtS64, name, _rmt_MakePropertyValue(S64, default_value), RMT_PropertyFlags_##flag, desc, __VA_ARGS__)
#define rmt_PropertyDefine_U64(name, default_value, flag, desc, ...) _rmt_PropertyDefine(rmtU64, name, _rmt_MakePropertyValue(U64, default_value), RMT_PropertyFlags_##flag, desc, __VA_ARGS__)
#define rmt_PropertyDefine_F64(name, default_value, flag, desc, ...) _rmt_PropertyDefine(rmtF64, name, _rmt_MakePropertyValue(F64, default_value), RMT_PropertyFlags_##flag, desc, __VA_ARGS__)
// As properties need to be defined at global scope outside header files, use this to declare properties in header files to be
// modified in other translation units.
//
// If you don't want to include Remotery.h in your shared header you can forward declare the `rmtProperty` type and then forward
// declare the property name yourself.
#define rmt_PropertyExtern(name) extern rmtProperty name;
// Set properties to the given value
#define rmt_PropertySet_Bool(name, set_value) _rmt_PropertySet(Bool, name, set_value)
#define rmt_PropertySet_S32(name, set_value) _rmt_PropertySet(S32, name, set_value)
#define rmt_PropertySet_U32(name, set_value) _rmt_PropertySet(U32, name, set_value)
#define rmt_PropertySet_F32(name, set_value) _rmt_PropertySet(F32, name, set_value)
#define rmt_PropertySet_S64(name, set_value) _rmt_PropertySet(S64, name, set_value)
#define rmt_PropertySet_U64(name, set_value) _rmt_PropertySet(U64, name, set_value)
#define rmt_PropertySet_F64(name, set_value) _rmt_PropertySet(F64, name, set_value)
// Add the given value to properties
#define rmt_PropertyAdd_S32(name, add_value) _rmt_PropertyAdd(S32, name, add_value)
#define rmt_PropertyAdd_U32(name, add_value) _rmt_PropertyAdd(U32, name, add_value)
#define rmt_PropertyAdd_F32(name, add_value) _rmt_PropertyAdd(F32, name, add_value)
#define rmt_PropertyAdd_S64(name, add_value) _rmt_PropertyAdd(S64, name, add_value)
#define rmt_PropertyAdd_U64(name, add_value) _rmt_PropertyAdd(U64, name, add_value)
#define rmt_PropertyAdd_F64(name, add_value) _rmt_PropertyAdd(F64, name, add_value)
// Reset properties to their default value
#define rmt_PropertyReset(name) \
{ \
name.value = name.defaultValue; \
_rmt_PropertySetValue(&name); \
}
// Send all properties and their values to the viewer and log to file
#define rmt_PropertySnapshotAll() _rmt_PropertySnapshotAll()
// Reset all RMT_PropertyFlags_FrameReset properties to their default value
#define rmt_PropertyFrameResetAll() _rmt_PropertyFrameResetAll()
/* --- Private Details ---------------------------------------------------------------------------------------------------------*/
// Used to define properties from typed macro callers
#define _rmt_PropertyDefine(type, name, default_value, flags, desc, ...) \
rmtProperty name = { RMT_FALSE, RMT_PropertyType_##type, flags, default_value, default_value, default_value, 0, #name, desc, default_value, __VA_ARGS__ };
// C++ doesn't support designated initialisers until C++20
// Worth checking for C++ designated initialisers to remove the function call in debug builds
#ifdef __cplusplus
#define _rmt_MakePropertyValue(field, value) rmtPropertyValue::Make##field(value)
#else
#define _rmt_MakePropertyValue(field, value) { .field = value }
#endif
// Used to set properties from typed macro callers
#define _rmt_PropertySet(field, name, set_value) \
{ \
name.value.field = set_value; \
_rmt_PropertySetValue(&name); \
}
// Used to add properties from typed macro callers
#define _rmt_PropertyAdd(field, name, add_value) \
{ \
name.value.field += add_value; \
rmtPropertyValue delta_value = _rmt_MakePropertyValue(field, add_value); \
_rmt_PropertyAddValue(&name, delta_value); \
}
#ifdef __cplusplus
extern "C" {
#endif
RMT_API void _rmt_PropertySetValue(rmtProperty* property);
RMT_API void _rmt_PropertyAddValue(rmtProperty* property, rmtPropertyValue add_value);
RMT_API rmtError _rmt_PropertySnapshotAll();
RMT_API void _rmt_PropertyFrameResetAll();
RMT_API rmtU32 _rmt_HashString32(const char* s, int len, rmtU32 seed);
#ifdef __cplusplus
}
#endif
/*--------------------------------------------------------------------------------------------------------------------------------
Sample Tree API for walking `rmtSampleTree` Objects in the Sample Tree Handler.
--------------------------------------------------------------------------------------------------------------------------------*/
typedef enum rmtSampleFlags
{
// Default behaviour
RMTSF_None = 0,
// Search parent for same-named samples and merge timing instead of adding a new sample
RMTSF_Aggregate = 1,
// Merge sample with parent if it's the same sample
RMTSF_Recursive = 2,
// Set this flag on any of your root samples so that Remotery will assert if it ends up *not* being the root sample.
// This will quickly allow you to detect Begin/End mismatches causing a sample tree imbalance.
RMTSF_Root = 4,
// Mainly for platforms other than Windows that don't support the thread sampler and can't detect stalling samples.
// Where you have a non-root sample that stays open indefinitely and never sends its contents to log/viewer.
// Send this sample to log/viewer when it closes.
// You can not have more than one sample open with this flag on the same thread at a time.
// This flag will be removed in a future version when all platforms support stalling samples.
RMTSF_SendOnClose = 8,
} rmtSampleFlags;
// Struct to hold iterator info
typedef struct rmtSampleIterator
{
// public
rmtSample* sample;
// private
rmtSample* initial;
} rmtSampleIterator;
#define rmt_IterateChildren(iter, sample) \
RMT_OPTIONAL(RMT_ENABLED, _rmt_IterateChildren(iter, sample))
#define rmt_IterateNext(iter) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_IterateNext(iter), RMT_FALSE)
#define rmt_SampleTreeGetThreadName(sample_tree) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_SampleTreeGetThreadName(sample_tree), NULL)
#define rmt_SampleTreeGetRootSample(sample_tree) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_SampleTreeGetRootSample(sample_tree), NULL)
// Should only called from within the sample tree callback,
// when the internal string lookup table is valid (i.e. on the main Remotery thread)
#define rmt_SampleGetName(sample) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_SampleGetName(sample), NULL)
#define rmt_SampleGetNameHash(sample) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_SampleGetNameHash(sample), 0U)
#define rmt_SampleGetCallCount(sample) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_SampleGetCallCount(sample), 0U)
#define rmt_SampleGetStart(sample) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_SampleGetStart(sample), 0LLU)
#define rmt_SampleGetTime(sample) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_SampleGetTime(sample), 0LLU)
#define rmt_SampleGetSelfTime(sample) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_SampleGetSelfTime(sample), 0LLU)
#define rmt_SampleGetColour(sample, r, g, b) \
RMT_OPTIONAL(RMT_ENABLED, _rmt_SampleGetColour(sample, r, g, b))
#define rmt_SampleGetType(sample) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_SampleGetType(sample), RMT_SampleType_Count)
// Struct to hold iterator info
typedef struct rmtPropertyIterator
{
// public
rmtProperty* property;
// private
rmtProperty* initial;
} rmtPropertyIterator;
#define rmt_PropertyIterateChildren(iter, property) \
RMT_OPTIONAL(RMT_ENABLED, _rmt_PropertyIterateChildren(iter, property))
#define rmt_PropertyIterateNext(iter) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_PropertyIterateNext(iter), RMT_FALSE)
// Should only called from within the property callback,
// when the internal string lookup table is valid (i.e. on the main Remotery thread)
#define rmt_PropertyGetType(property) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_PropertyGetType(property), RMT_PropertyType_Count)
#define rmt_PropertyGetName(property) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_PropertyGetName(property), NULL)
#define rmt_PropertyGetDescription(property) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_PropertyGetDescription(property), 0U)
#define rmt_PropertyGetValue(property) \
RMT_OPTIONAL_RET(RMT_ENABLED, _rmt_PropertyGetValue(property), 0U)
/*--------------------------------------------------------------------------------------------------------------------------------
C++ Public Interface Extensions
--------------------------------------------------------------------------------------------------------------------------------*/
#ifdef __cplusplus
#if RMT_ENABLED
// Types that end samples in their destructors
extern "C" RMT_API void _rmt_EndCPUSample(void);
struct rmt_EndCPUSampleOnScopeExit
{
~rmt_EndCPUSampleOnScopeExit()
{
_rmt_EndCPUSample();
}
};
#if RMT_USE_CUDA
extern "C" RMT_API void _rmt_EndCUDASample(void* stream);
struct rmt_EndCUDASampleOnScopeExit
{
rmt_EndCUDASampleOnScopeExit(void* stream) : stream(stream)
{
}
~rmt_EndCUDASampleOnScopeExit()
{
_rmt_EndCUDASample(stream);
}
void* stream;
};
#endif
#if RMT_USE_D3D11
extern "C" RMT_API void _rmt_EndD3D11Sample(void);
struct rmt_EndD3D11SampleOnScopeExit
{
~rmt_EndD3D11SampleOnScopeExit()
{
_rmt_EndD3D11Sample();
}
};
#endif
#if RMT_USE_D3D12
extern "C" RMT_API void _rmt_EndD3D12Sample();
struct rmt_EndD3D12SampleOnScopeExit
{
~rmt_EndD3D12SampleOnScopeExit()
{
_rmt_EndD3D12Sample();
}
};
#endif
#if RMT_USE_OPENGL
extern "C" RMT_API void _rmt_EndOpenGLSample(void);
struct rmt_EndOpenGLSampleOnScopeExit
{
~rmt_EndOpenGLSampleOnScopeExit()
{
_rmt_EndOpenGLSample();
}
};
#endif
#if RMT_USE_METAL
extern "C" RMT_API void _rmt_EndMetalSample(void);
struct rmt_EndMetalSampleOnScopeExit
{
~rmt_EndMetalSampleOnScopeExit()
{
_rmt_EndMetalSample();
}
};
#endif
#endif
// Pairs a call to rmt_Begin<TYPE>Sample with its call to rmt_End<TYPE>Sample when leaving scope
#define rmt_ScopedCPUSample(name, flags) \
RMT_OPTIONAL(RMT_ENABLED, rmt_BeginCPUSample(name, flags)); \
RMT_OPTIONAL(RMT_ENABLED, rmt_EndCPUSampleOnScopeExit rmt_ScopedCPUSample##name);
#define rmt_ScopedCUDASample(name, stream) \
RMT_OPTIONAL(RMT_USE_CUDA, rmt_BeginCUDASample(name, stream)); \
RMT_OPTIONAL(RMT_USE_CUDA, rmt_EndCUDASampleOnScopeExit rmt_ScopedCUDASample##name(stream));
#define rmt_ScopedD3D11Sample(name) \
RMT_OPTIONAL(RMT_USE_D3D11, rmt_BeginD3D11Sample(name)); \
RMT_OPTIONAL(RMT_USE_D3D11, rmt_EndD3D11SampleOnScopeExit rmt_ScopedD3D11Sample##name);
#define rmt_ScopedD3D12Sample(bind, command_list, name) \
RMT_OPTIONAL(RMT_USE_D3D12, rmt_BeginD3D12Sample(bind, command_list, name)); \