// Burst/Thread safe internal static unsafe string MarkerGetStringName(IntPtr markerPtr) { MarkerBucketNode *marker = (MarkerBucketNode *)markerPtr; int charCount = UTF8.GetCharCount(marker->nameUtf8, marker->nameBytes); char *chars = stackalloc char[charCount]; UTF8.GetChars(marker->nameUtf8, marker->nameBytes, chars, charCount); return(new string(chars, 0, charCount)); }
internal MarkerBucketNode *next; // offset 120 internal static unsafe bool SearchFor(MarkerBucketNode *startNode, byte *nameUtf8, int nameBytes, ref MarkerBucketNode *bucketNode) { MarkerBucketNode *prev = startNode; while (startNode != null) { if (startNode->nameBytes == nameBytes) { if (UnsafeUtility.MemCmp(nameUtf8, startNode->nameUtf8, nameBytes) == 0) { bucketNode = startNode; return(true); } } prev = startNode; startNode = startNode->next; } bucketNode = prev; return(false); }
// Burst/thread safe internal static unsafe void *MarkerGetOrCreate(ushort categoryId, byte *name, int nameBytes, ushort flags) { if (nameBytes <= 0) { return(null); } MarkerBucketNode *marker = null; int bucket = (((nameBytes << 5) + (nameBytes >> 2)) ^ name[0]) & 255; if (markerHashTableHead != null) { // No need for locking yet - read operations on hash table are thread safe as long as we are careful about // modification during write and only allow one thread to write at a time. if (MarkerBucketNode.SearchFor(&markerHashTableHead->MarkersBuffer[bucket], name, nameBytes, ref marker)) { return(marker); } } // The marker didn't exist in hash table. Need to lock so only one thread can modify at a time. // This path will usually only be taken during startup - after which markers should be found in the // above loop instead of needing to be created. Even this is a worse-case scenario because correct ProfilerMarker // usage will create/get them once, and they will exist as an instance which only calls MarkerBegin() and MarkerEnd() // when needed. PlayerConnectionMt_LockProfilerHashTables(); if (marker == null) { if (markerHashTableHead == null) { markerHashTableHead = FastHashTableBufferNode.Allocate(sizeof(MarkerBucketNode)); markerHashTableTail = markerHashTableHead; } marker = &markerHashTableHead->MarkersBuffer[bucket]; } // In case this bucket was added to while another thread had the lock, the end-of-bucket // pointer needs to be increased. Also, it's possible the same exact name appears now. if (MarkerBucketNode.SearchFor(marker, name, nameBytes, ref marker)) { PlayerConnectionMt_UnlockProfilerHashTables(); return(marker); } MarkerBucketNode *oldMarker = null; if (marker->nameBytes > 0) { // There is already a valid marker here at the end of the linked list - add a new one markerHashTableTail = markerHashTableTail->ExtendPoolIfFull(sizeof(MarkerBucketNode)); MarkerBucketNode *newMarker = &markerHashTableTail->MarkersBuffer[markerHashTableTail->size]; markerHashTableTail->size++; oldMarker = marker; marker = newMarker; } marker->init = false; marker->categoryId = categoryId; marker->flags = flags; marker->markerId = nextMarkerId++; marker->nameBytes = nameBytes; // Todo: When Burst printing works we should warn the user if a name is too long and will be truncated UnsafeUtility.MemCpy(marker->nameUtf8, name, Math.Min(nameBytes, k_MaxMarkerNameLength)); // Do this last so if we find the node before locking, we don't have access to it unless it is otherwise fully assigned if (oldMarker != null) { oldMarker->next = marker; } PlayerConnectionMt_UnlockProfilerHashTables(); NeedsUpdate = true; return(marker); }
// Burst/Thread safe internal static unsafe void MarkerEnd(void *markerPtr) { MarkerBucketNode *marker = (MarkerBucketNode *)markerPtr; ProfilerProtocolThread.SendEndSample(marker->markerId, GetProfilerTime()); }
// Burst/Thread safe internal static unsafe void MarkerBegin(void *markerPtr, void *metadata, int metadataBytes) { MarkerBucketNode *marker = (MarkerBucketNode *)markerPtr; ProfilerProtocolThread.SendBeginSample(marker->markerId, GetProfilerTime()); }