1/*
2 * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved.
3 *
4 * NVIDIA CORPORATION and its licensors retain all intellectual property
5 * and proprietary rights in and to this software, related documentation
6 * and any modifications thereto. Any use, reproduction, disclosure or
7 * distribution of this software and related documentation without an express
8 * license agreement from NVIDIA CORPORATION is strictly prohibited.
9 */
10
11#ifndef PX_PROFILE_EVENTS_H
12#define PX_PROFILE_EVENTS_H
13
14#include "physxprofilesdk/PxProfileBase.h"
15#include "physxprofilesdk/PxProfileEventId.h"
16
17#define UNION_1(a) physx::profile::TUnion<a, physx::profile::Empty>
18#define UNION_2(a,b) physx::profile::TUnion<a, UNION_1(b)>
19#define UNION_3(a,b,c) physx::profile::TUnion<a, UNION_2(b,c)>
20#define UNION_4(a,b,c,d) physx::profile::TUnion<a, UNION_3(b,c,d)>
21#define UNION_5(a,b,c,d,e) physx::profile::TUnion<a, UNION_4(b,c,d,e)>
22#define UNION_6(a,b,c,d,e,f) physx::profile::TUnion<a, UNION_5(b,c,d,e,f)>
23#define UNION_7(a,b,c,d,e,f,g) physx::profile::TUnion<a, UNION_6(b,c,d,e,f,g)>
24#define UNION_8(a,b,c,d,e,f,g,h) physx::profile::TUnion<a, UNION_7(b,c,d,e,f,g,h)>
25#define UNION_9(a,b,c,d,e,f,g,h,i) physx::profile::TUnion<a, UNION_8(b,c,d,e,f,g,h,i)>
26
27namespace physx { namespace profile {
28
29 struct Empty {};
30
31 template <typename T> struct Type2Type {};
32
33 template <typename U, typename V>
34 union TUnion
35 {
36 typedef U Head;
37 typedef V Tail;
38
39 Head head;
40 Tail tail;
41
42 template <typename TDataType>
43 void init(const TDataType& inData)
44 {
45 toType(Type2Type<TDataType>()).init(inData);
46 }
47
48 template <typename TDataType>
49 PX_FORCE_INLINE TDataType& toType(const Type2Type<TDataType>& outData) { return tail.toType(outData); }
50
51 PX_FORCE_INLINE Head& toType(const Type2Type<Head>&) { return head; }
52
53 template <typename TDataType>
54 PX_FORCE_INLINE const TDataType& toType(const Type2Type<TDataType>& outData) const { return tail.toType(outData); }
55
56 PX_FORCE_INLINE const Head& toType(const Type2Type<Head>&) const { return head; }
57 };
58
59 struct EventTypes
60 {
61 enum Enum
62 {
63 Unknown = 0,
64 StartEvent,
65 StopEvent,
66 RelativeStartEvent, //reuses context,id from the earlier event.
67 RelativeStopEvent, //reuses context,id from the earlier event.
68 EventValue,
69 CUDAProfileBuffer
70 };
71 };
72
73 struct EventStreamCompressionFlags
74 {
75 enum Enum
76 {
77 U8 = 0,
78 U16 = 1,
79 U32 = 2,
80 U64 = 3,
81 CompressionMask = 3
82 };
83 };
84
85
86 //Find the smallest value that will represent the incoming value without loss.
87 //We can enlarge the current compression value, but we can't make is smaller.
88 //In this way, we can use this function to find the smallest compression setting
89 //that will work for a set of values.
90 inline EventStreamCompressionFlags::Enum findCompressionValue( PxU64 inValue, EventStreamCompressionFlags::Enum inCurrentCompressionValue = EventStreamCompressionFlags::U8 )
91 {
92 //Fallthrough is intentional
93 switch( inCurrentCompressionValue )
94 {
95 case EventStreamCompressionFlags::U8:
96 if ( inValue <= PX_MAX_U8 )
97 return EventStreamCompressionFlags::U8;
98 case EventStreamCompressionFlags::U16:
99 if ( inValue <= PX_MAX_U16 )
100 return EventStreamCompressionFlags::U16;
101 case EventStreamCompressionFlags::U32:
102 if ( inValue <= PX_MAX_U32 )
103 return EventStreamCompressionFlags::U32;
104 case EventStreamCompressionFlags::U64:
105 default:
106 return EventStreamCompressionFlags::U64;
107 }
108 }
109
110 //Find the smallest value that will represent the incoming value without loss.
111 //We can enlarge the current compression value, but we can't make is smaller.
112 //In this way, we can use this function to find the smallest compression setting
113 //that will work for a set of values.
114 inline EventStreamCompressionFlags::Enum findCompressionValue( PxU32 inValue, EventStreamCompressionFlags::Enum inCurrentCompressionValue = EventStreamCompressionFlags::U8 )
115 {
116 //Fallthrough is intentional
117 switch( inCurrentCompressionValue )
118 {
119 case EventStreamCompressionFlags::U8:
120 if ( inValue <= PX_MAX_U8 )
121 return EventStreamCompressionFlags::U8;
122 case EventStreamCompressionFlags::U16:
123 if ( inValue <= PX_MAX_U16 )
124 return EventStreamCompressionFlags::U16;
125 case EventStreamCompressionFlags::U32:
126 case EventStreamCompressionFlags::U64:
127 default:
128 return EventStreamCompressionFlags::U32;
129 }
130 }
131
132 //Event header is 32 bytes and precedes all events.
133 struct EventHeader
134 {
135 PxU8 mEventType; //Used to parse the correct event out of the stream
136 PxU8 mStreamOptions; //Timestamp compression, etc.
137 PxU16 mEventId; //16 bit per-event-system event id
138 EventHeader( PxU8 type = 0, PxU16 id = 0 )
139 : mEventType( type )
140 , mStreamOptions( (PxU8)-1 )
141 , mEventId( id )
142 {
143 }
144
145 EventHeader( EventTypes::Enum type, PxU16 id )
146 : mEventType( static_cast<PxU8>( type ) )
147 , mStreamOptions( (PxU8)-1 )
148 , mEventId( id )
149 {
150 }
151
152 EventStreamCompressionFlags::Enum getTimestampCompressionFlags() const
153 {
154 return static_cast<EventStreamCompressionFlags::Enum> ( mStreamOptions & EventStreamCompressionFlags::CompressionMask );
155 }
156
157 PxU64 compressTimestamp( PxU64 inLastTimestamp, PxU64 inCurrentTimestamp )
158 {
159 mStreamOptions = EventStreamCompressionFlags::U64;
160 PxU64 retval = inCurrentTimestamp;
161 if ( inLastTimestamp )
162 {
163 retval = inCurrentTimestamp - inLastTimestamp;
164 EventStreamCompressionFlags::Enum compressionValue = findCompressionValue( retval );
165 mStreamOptions = static_cast<PxU8>( compressionValue );
166 if ( compressionValue == EventStreamCompressionFlags::U64 )
167 retval = inCurrentTimestamp; //just send the timestamp as is.
168 }
169 return retval;
170 }
171
172 PxU64 uncompressTimestamp( PxU64 inLastTimestamp, PxU64 inCurrentTimestamp ) const
173 {
174 if ( getTimestampCompressionFlags() != EventStreamCompressionFlags::U64 )
175 return inLastTimestamp + inCurrentTimestamp;
176 return inCurrentTimestamp;
177 }
178
179 void setContextIdCompressionFlags( PxU64 inContextId )
180 {
181 PxU8 options = static_cast<PxU8>( findCompressionValue( inContextId ) );
182 mStreamOptions = PxU8(mStreamOptions | options << 2);
183 }
184
185 EventStreamCompressionFlags::Enum getContextIdCompressionFlags() const
186 {
187 return static_cast< EventStreamCompressionFlags::Enum >( ( mStreamOptions >> 2 ) & EventStreamCompressionFlags::CompressionMask );
188 }
189
190 bool operator==( const EventHeader& inOther ) const
191 {
192 return mEventType == inOther.mEventType
193 && mStreamOptions == inOther.mStreamOptions
194 && mEventId == inOther.mEventId;
195 }
196
197 template<typename TStreamType>
198 inline void streamify( TStreamType& inStream )
199 {
200 inStream.streamify( "EventType", mEventType );
201 inStream.streamify( "StreamOptions", mStreamOptions ); //Timestamp compression, etc.
202 inStream.streamify( "EventId", mEventId ); //16 bit per-event-system event id
203 }
204 };
205
206 //Declaration of type level getEventType function that maps enumeration event types to datatypes
207 template<typename TDataType>
208 inline EventTypes::Enum getEventType() { PX_ASSERT( false ); return EventTypes::Unknown; }
209
210 //Relative profile event means this event is sharing the context and thread id
211 //with the event before it.
212 struct RelativeProfileEvent
213 {
214 PxU64 mTensOfNanoSeconds; //timestamp is in tensOfNanonseconds
215 void init( PxU64 inTs ) { mTensOfNanoSeconds = inTs; }
216 void init( const RelativeProfileEvent& inData ) { mTensOfNanoSeconds = inData.mTensOfNanoSeconds; }
217 bool operator==( const RelativeProfileEvent& other ) const
218 {
219 return mTensOfNanoSeconds == other.mTensOfNanoSeconds;
220 }
221 template<typename TStreamType>
222 void streamify( TStreamType& inStream, const EventHeader& inHeader )
223 {
224 inStream.streamify( "TensOfNanoSeconds", mTensOfNanoSeconds, inHeader.getTimestampCompressionFlags() );
225 }
226 PxU64 getTimestamp() const { return mTensOfNanoSeconds; }
227 void setTimestamp( PxU64 inTs ) { mTensOfNanoSeconds = inTs; }
228 void setupHeader( EventHeader& inHeader, PxU64 inLastTimestamp )
229 {
230 mTensOfNanoSeconds = inHeader.compressTimestamp( inLastTimestamp, mTensOfNanoSeconds );
231 }
232 };
233
234 //Start version of the relative event.
235 struct RelativeStartEvent : public RelativeProfileEvent
236 {
237 void init( PxU64 inTs = 0 ) { RelativeProfileEvent::init( inTs ); }
238 void init( const RelativeStartEvent& inData ) { RelativeProfileEvent::init( inData ); }
239 template<typename THandlerType>
240 void handle( THandlerType* inHdlr, PxU16 eventId, PxU32 thread, PxU64 context, PxU8 inCpuId, PxU8 threadPriority ) const
241 {
242 inHdlr->onStartEvent( PxProfileEventId( eventId ), thread, context, inCpuId, threadPriority, mTensOfNanoSeconds );
243 }
244 };
245
246 template<> inline EventTypes::Enum getEventType<RelativeStartEvent>() { return EventTypes::RelativeStartEvent; }
247
248 //Stop version of relative event.
249 struct RelativeStopEvent : public RelativeProfileEvent
250 {
251 void init( PxU64 inTs = 0 ) { RelativeProfileEvent::init( inTs ); }
252 void init( const RelativeStopEvent& inData ) { RelativeProfileEvent::init( inData ); }
253 template<typename THandlerType>
254 void handle( THandlerType* inHdlr, PxU16 eventId, PxU32 thread, PxU64 context, PxU8 inCpuId, PxU8 threadPriority ) const
255 {
256 inHdlr->onStopEvent( PxProfileEventId( eventId ), thread, context, inCpuId, threadPriority, mTensOfNanoSeconds );
257 }
258 };
259
260 template<> inline EventTypes::Enum getEventType<RelativeStopEvent>() { return EventTypes::RelativeStopEvent; }
261
262 struct EventContextInformation
263 {
264 PxU64 mContextId;
265 PxU32 mThreadId; //Thread this event was taken from
266 PxU8 mThreadPriority;
267 PxU8 mCpuId;
268
269 void init( PxU32 inThreadId = PX_MAX_U32
270 , PxU64 inContextId = ((PxU64) -1)
271 , PxU8 inPriority = PX_MAX_U8
272 , PxU8 inCpuId = PX_MAX_U8 )
273 {
274 mContextId = inContextId;
275 mThreadId = inThreadId;
276 mThreadPriority = inPriority;
277 mCpuId = inCpuId;
278 }
279
280 void init( const EventContextInformation& inData )
281 {
282 mContextId = inData.mContextId;
283 mThreadId = inData.mThreadId;
284 mThreadPriority = inData.mThreadPriority;
285 mCpuId = inData.mCpuId;
286 }
287
288 template<typename TStreamType>
289 void streamify( TStreamType& inStream, EventStreamCompressionFlags::Enum inContextIdFlags )
290 {
291 inStream.streamify( "ThreadId", mThreadId );
292 inStream.streamify( "ContextId", mContextId, inContextIdFlags );
293 inStream.streamify( "ThreadPriority", mThreadPriority );
294 inStream.streamify( "CpuId", mCpuId );
295 }
296
297 bool operator==( const EventContextInformation& other ) const
298 {
299 return mThreadId == other.mThreadId
300 && mContextId == other.mContextId
301 && mThreadPriority == other.mThreadPriority
302 && mCpuId == other.mCpuId;
303 }
304
305 void setToDefault()
306 {
307 *this = EventContextInformation();
308 }
309 };
310
311 //Profile event contains all the data required to tell the profile what is going
312 //on.
313 struct ProfileEvent
314 {
315 EventContextInformation mContextInformation;
316 RelativeProfileEvent mTimeData; //timestamp in seconds.
317 void init( PxU32 inThreadId, PxU64 inContextId, PxU8 inCpuId, PxU8 inPriority, PxU64 inTs )
318 {
319 mContextInformation.init( inThreadId, inContextId, inPriority, inCpuId );
320 mTimeData.init( inTs );
321 }
322
323 void init( const ProfileEvent& inData )
324 {
325 mContextInformation.init( inData.mContextInformation );
326 mTimeData.init( inData.mTimeData );
327 }
328
329 bool operator==( const ProfileEvent& other ) const
330 {
331 return mContextInformation == other.mContextInformation
332 && mTimeData == other.mTimeData;
333 }
334
335 template<typename TStreamType>
336 void streamify( TStreamType& inStream, const EventHeader& inHeader )
337 {
338 mContextInformation.streamify( inStream, inHeader.getContextIdCompressionFlags() );
339 mTimeData.streamify( inStream, inHeader );
340 }
341
342 PxU64 getTimestamp() const { return mTimeData.getTimestamp(); }
343 void setTimestamp( PxU64 inTs ) { mTimeData.setTimestamp( inTs ); }
344
345 void setupHeader( EventHeader& inHeader, PxU64 inLastTimestamp )
346 {
347 mTimeData.setupHeader( inHeader, inLastTimestamp );
348 inHeader.setContextIdCompressionFlags( mContextInformation.mContextId );
349 }
350 };
351
352 //profile start event starts the profile session.
353 struct StartEvent : public ProfileEvent
354 {
355 void init( PxU32 inThreadId = 0, PxU64 inContextId = 0, PxU8 inCpuId = 0, PxU8 inPriority = 0, PxU64 inTensOfNanoSeconds = 0 )
356 {
357 ProfileEvent::init( inThreadId, inContextId, inCpuId, inPriority, inTensOfNanoSeconds );
358 }
359 void init( const StartEvent& inData )
360 {
361 ProfileEvent::init( inData );
362 }
363
364 RelativeStartEvent getRelativeEvent() const { RelativeStartEvent theEvent; theEvent.init( mTimeData.mTensOfNanoSeconds ); return theEvent; }
365 EventTypes::Enum getRelativeEventType() const { return getEventType<RelativeStartEvent>(); }
366 };
367
368 template<> inline EventTypes::Enum getEventType<StartEvent>() { return EventTypes::StartEvent; }
369
370 //Profile stop event stops the profile session.
371 struct StopEvent : public ProfileEvent
372 {
373 void init( PxU32 inThreadId = 0, PxU64 inContextId = 0, PxU8 inCpuId = 0, PxU8 inPriority = 0, PxU64 inTensOfNanoSeconds = 0 )
374 {
375 ProfileEvent::init( inThreadId, inContextId, inCpuId, inPriority, inTensOfNanoSeconds );
376 }
377 void init( const StopEvent& inData )
378 {
379 ProfileEvent::init( inData );
380 }
381 RelativeStopEvent getRelativeEvent() const { RelativeStopEvent theEvent; theEvent.init( mTimeData.mTensOfNanoSeconds ); return theEvent; }
382 EventTypes::Enum getRelativeEventType() const { return getEventType<RelativeStopEvent>(); }
383 };
384
385 template<> inline EventTypes::Enum getEventType<StopEvent>() { return EventTypes::StopEvent; }
386
387 struct EventValue
388 {
389 PxU64 mValue;
390 PxU64 mContextId;
391 PxU32 mThreadId;
392 void init( PxI64 inValue = 0, PxU64 inContextId = 0, PxU32 inThreadId = 0 )
393 {
394 mValue = static_cast<PxU64>( inValue );
395 mContextId = inContextId;
396 mThreadId = inThreadId;
397 }
398
399 void init( const EventValue& inData )
400 {
401 mValue = inData.mValue;
402 mContextId = inData.mContextId;
403 mThreadId = inData.mThreadId;
404 }
405
406 PxI64 getValue() const { return static_cast<PxI16>( mValue ); }
407
408 void setupHeader( EventHeader& inHeader )
409 {
410 mValue = inHeader.compressTimestamp( 0, mValue );
411 inHeader.setContextIdCompressionFlags( mContextId );
412 }
413
414 template<typename TStreamType>
415 void streamify( TStreamType& inStream, const EventHeader& inHeader )
416 {
417 inStream.streamify( "Value", mValue, inHeader.getTimestampCompressionFlags() );
418 inStream.streamify( "ContextId", mContextId, inHeader.getContextIdCompressionFlags() );
419 inStream.streamify( "ThreadId", mThreadId );
420 }
421
422 bool operator==( const EventValue& other ) const
423 {
424 return mValue == other.mValue
425 && mContextId == other.mContextId
426 && mThreadId == other.mThreadId;
427 }
428
429 template<typename THandlerType>
430 void handle( THandlerType* inHdlr, PxU16 eventId ) const
431 {
432 inHdlr->onEventValue( PxProfileEventId( eventId ), mThreadId, mContextId, getValue() );
433 }
434
435 };
436 template<> inline EventTypes::Enum getEventType<EventValue>() { return EventTypes::EventValue; }
437
438 struct CUDAProfileBuffer
439 {
440 PxU64 mTimestamp;
441 PxF32 mTimespan;
442 const PxU8* mCudaData;
443 PxU32 mBufLen;
444 PxU32 mVersion;
445
446 void init( PxU64 timestamp = 0, PxF32 span = 0, const PxU8* cdata= 0, PxU32 buflen= 0, PxU32 version= 0 )
447 {
448 mTimestamp = timestamp;
449 mTimespan = span;
450 mCudaData = cdata;
451 mBufLen = buflen;
452 mVersion = version;
453 }
454
455 void init( const CUDAProfileBuffer& inData )
456 {
457 mTimestamp = inData.mTimestamp;
458 mTimespan = inData.mTimespan;
459 mCudaData = inData.mCudaData;
460 mBufLen = inData.mBufLen;
461 mVersion = inData.mVersion;
462 }
463
464 template<typename TStreamType>
465 void streamify( TStreamType& inStream, const EventHeader& )
466 {
467 inStream.streamify( "Timestamp", mTimestamp );
468 inStream.streamify( "Timespan", mTimespan );
469 inStream.streamify( "CudaData", mCudaData, mBufLen );
470 inStream.streamify( "BufLen", mBufLen );
471 inStream.streamify( "Version", mVersion );
472 }
473
474 bool operator==( const CUDAProfileBuffer& other ) const
475 {
476 return mTimestamp == other.mTimestamp
477 && mTimespan == other.mTimespan
478 && mBufLen == other.mBufLen
479 && memcmp( mCudaData, other.mCudaData, mBufLen ) == 0
480 && mVersion == other.mVersion;
481 }
482
483 template<typename THandlerType>
484 void handle( THandlerType* inHdlr ) const
485 {
486 inHdlr->onCUDAProfileBuffer( mTimestamp, mTimespan, mCudaData, mBufLen, mVersion );
487 }
488 };
489
490 template<> inline EventTypes::Enum getEventType<CUDAProfileBuffer>() { return EventTypes::CUDAProfileBuffer; }
491
492 //Provides a generic equal operation for event data objects.
493 template <typename TEventData>
494 struct EventDataEqualOperator
495 {
496 TEventData mData;
497 EventDataEqualOperator( const TEventData& inD ) : mData( inD ) {}
498 template<typename TDataType> bool operator()( const TDataType& inRhs ) const { return mData.toType( Type2Type<TDataType>() ) == inRhs; }
499 bool operator()() const { return false; }
500 };
501
502 /**
503 * Generic event container that combines and even header with the generic event data type.
504 * Provides unsafe and typesafe access to the event data.
505 */
506 class Event
507 {
508 public:
509 typedef UNION_7(StartEvent, StopEvent, RelativeStartEvent, RelativeStopEvent, EventValue, CUDAProfileBuffer, PxU8) EventData;
510
511 private:
512 EventHeader mHeader;
513 EventData mData;
514 public:
515 Event() {}
516
517 template <typename TDataType>
518 Event( EventHeader inHeader, const TDataType& inData )
519 : mHeader( inHeader )
520 {
521 mData.init<TDataType>(inData);
522 }
523
524 template<typename TDataType>
525 Event( PxU16 eventId, const TDataType& inData )
526 : mHeader( getEventType<TDataType>(), eventId )
527 {
528 mData.init<TDataType>(inData);
529 }
530 const EventHeader& getHeader() const { return mHeader; }
531 const EventData& getData() const { return mData; }
532
533 template<typename TDataType>
534 const TDataType& getValue() const { PX_ASSERT( mHeader.mEventType == getEventType<TDataType>() ); return mData.toType<TDataType>(); }
535
536 template<typename TDataType>
537 TDataType& getValue() { PX_ASSERT( mHeader.mEventType == getEventType<TDataType>() ); return mData.toType<TDataType>(); }
538
539 template<typename TRetVal, typename TOperator>
540 inline TRetVal visit( TOperator inOp ) const;
541
542 bool operator==( const Event& inOther ) const
543 {
544 if ( !(mHeader == inOther.mHeader ) ) return false;
545 if ( mHeader.mEventType )
546 return inOther.visit<bool>( EventDataEqualOperator<EventData>( mData ) );
547 return true;
548 }
549 };
550
551 //Combining the above union type with an event type means that an object can get the exact
552 //data out of the union. Using this function means that all callsites will be forced to
553 //deal with the newer datatypes and that the switch statement only exists in once place.
554 //Implements conversion from enum -> datatype
555 template<typename TRetVal, typename TOperator>
556 TRetVal visit( EventTypes::Enum inEventType, const Event::EventData& inData, TOperator inOperator )
557 {
558 switch( inEventType )
559 {
560 case EventTypes::StartEvent: return inOperator( inData.toType( Type2Type<StartEvent>() ) );
561 case EventTypes::StopEvent: return inOperator( inData.toType( Type2Type<StopEvent>() ) );
562 case EventTypes::RelativeStartEvent: return inOperator( inData.toType( Type2Type<RelativeStartEvent>() ) );
563 case EventTypes::RelativeStopEvent: return inOperator( inData.toType( Type2Type<RelativeStopEvent>() ) );
564 case EventTypes::EventValue: return inOperator( inData.toType( Type2Type<EventValue>() ) );
565 case EventTypes::CUDAProfileBuffer: return inOperator( inData.toType( Type2Type<CUDAProfileBuffer>() ) );
566 case EventTypes::Unknown:
567 default: return inOperator( static_cast<PxU8>( inEventType ) );
568 }
569 }
570
571 template<typename TRetVal, typename TOperator>
572 inline TRetVal Event::visit( TOperator inOp ) const
573 {
574 return physx::profile::visit<TRetVal>( static_cast<EventTypes::Enum>(mHeader.mEventType), mData, inOp );
575 }
576} }
577
578#endif // PX_PROFILE_EVENTS_H
579