public static bool IsManagedBlock(IntPtr block) { if (block == IntPtr.Zero) { throw new ArgumentNullException("block"); } BlockLiteral * literal = (BlockLiteral *)block; BlockDescriptor *descriptor = (BlockDescriptor *)xamarin_get_block_descriptor(); return(descriptor->copy_helper == ((BlockDescriptor *)literal->block_descriptor)->copy_helper); }
/// <summary> /// Creates a block descriptor with given minimal size /// </summary> /// <param name="size">The minimal size</param> /// <returns>The block descriptor</returns> private static unsafe BlockDescriptor *createBlockDescriptor(int size) { #if HEAP_DEBUG_DESCRIPTOR Console.WriteLine("[HEAP] Creating a new block descriptor"); #endif // Add size of Block and BlockDescriptor size += sizeof(Block) + sizeof(BlockDescriptor); // Allocate descriptor size = getRequiredPageCount(size) * 0x1000; BlockDescriptor *descriptor = (BlockDescriptor *)Paging.AllocateVirtual(size); #if HEAP_DEBUG_DESCRIPTOR Console.Write("[HEAP] New descriptor is at 0x"); Console.WriteHex((int)descriptor); Console.Write(", physical: 0x"); Console.WriteHex((int)Paging.GetPhysicalFromVirtual(descriptor)); Console.Write('\n'); #endif if (descriptor == null) { Panic.DoPanic("descriptor == null"); } // Setup block Block *first = (Block *)((int)descriptor + sizeof(BlockDescriptor)); first->Prev = null; first->Next = null; first->Size = size - sizeof(BlockDescriptor); first->Used = false; first->Descriptor = descriptor; #if HEAP_USE_MAGIC first->Magic = HEAP_MAGIC; #endif // Setup descriptor descriptor->FreeSpace = size - sizeof(BlockDescriptor); descriptor->First = first; descriptor->FirstFree = first; descriptor->Next = null; #if HEAP_USE_MAGIC descriptor->Magic = HEAP_MAGIC; #endif return(descriptor); }
/// <summary> /// Dumps a table of the descriptors /// </summary> private static unsafe void dumpDescriptors() { BlockDescriptor *descriptor = firstDescriptor; Console.WriteLine("---"); // Search for a big enough descriptor while (true) { if (descriptor->Next == null) { break; } descriptor = descriptor->Next; Console.Write("["); Console.WriteHex((int)descriptor); Console.WriteLine("]"); } Console.WriteLine("---"); }
/// <summary> /// Gets a sufficient block descriptor for the required size /// </summary> /// <param name="size">The required size</param> /// <returns>The block descriptor</returns> private static unsafe BlockDescriptor *getSufficientDescriptor(BlockDescriptor *first, int size) { BlockDescriptor *descriptor = first; if (descriptor == null) { descriptor = firstDescriptor; } // Search for a big enough descriptor while (true) { #if HEAP_USE_MAGIC if (descriptor->Magic != HEAP_MAGIC) { Panic.DoPanic("descriptor->magic != HEAP_MAGIC"); } #endif if (descriptor != first && descriptor->FreeSpace >= size) { return(descriptor); } if (descriptor->Next == null) { break; } descriptor = descriptor->Next; } // Create next descriptor because there is no descriptor that is big enough BlockDescriptor *newDescriptor = createBlockDescriptor(size); descriptor->Next = newDescriptor; return(newDescriptor); }
public MTLGraphicsDevice( GraphicsDeviceOptions options, SwapchainDescription?swapchainDesc) { _device = MTLDevice.MTLCreateSystemDefaultDevice(); MetalFeatures = new MTLFeatureSupport(_device); Features = new GraphicsDeviceFeatures( computeShader: true, geometryShader: false, tessellationShaders: false, multipleViewports: MetalFeatures.IsSupported(MTLFeatureSet.macOS_GPUFamily1_v3), samplerLodBias: false, drawBaseVertex: true, drawBaseInstance: true, drawIndirect: true, drawIndirectBaseInstance: true, fillModeWireframe: true, samplerAnisotropy: true, depthClipDisable: true, texture1D: true, // TODO: Should be macOS 10.11+ and iOS 11.0+. independentBlend: true, structuredBuffer: true, subsetTextureView: true, commandListDebugMarkers: true, bufferRangeBinding: true); ResourceBindingModel = options.ResourceBindingModel; _libSystem = new NativeLibrary("libSystem.dylib"); _concreteGlobalBlock = _libSystem.LoadFunction <IntPtr>("_NSConcreteGlobalBlock"); if (MetalFeatures.IsMacOS) { _completionHandler = OnCommandBufferCompleted; } else { _completionHandler = OnCommandBufferCompleted_Static; } _completionHandlerFuncPtr = Marshal.GetFunctionPointerForDelegate <MTLCommandBufferHandler>(_completionHandler); _completionBlockDescriptor = Marshal.AllocHGlobal(Unsafe.SizeOf <BlockDescriptor>()); BlockDescriptor *descriptorPtr = (BlockDescriptor *)_completionBlockDescriptor; descriptorPtr->reserved = 0; descriptorPtr->Block_size = (ulong)Unsafe.SizeOf <BlockDescriptor>(); _completionBlockLiteral = Marshal.AllocHGlobal(Unsafe.SizeOf <BlockLiteral>()); BlockLiteral *blockPtr = (BlockLiteral *)_completionBlockLiteral; blockPtr->isa = _concreteGlobalBlock; blockPtr->flags = 1 << 28 | 1 << 29; blockPtr->invoke = _completionHandlerFuncPtr; blockPtr->descriptor = descriptorPtr; if (!MetalFeatures.IsMacOS) { lock (s_aotRegisteredBlocks) { s_aotRegisteredBlocks.Add(_completionBlockLiteral, this); } } ResourceFactory = new MTLResourceFactory(this); _commandQueue = _device.newCommandQueue(); TextureSampleCount[] allSampleCounts = (TextureSampleCount[])Enum.GetValues(typeof(TextureSampleCount)); _supportedSampleCounts = new bool[allSampleCounts.Length]; for (int i = 0; i < allSampleCounts.Length; i++) { TextureSampleCount count = allSampleCounts[i]; uint uintValue = FormatHelpers.GetSampleCountUInt32(count); if (_device.supportsTextureSampleCount((UIntPtr)uintValue)) { _supportedSampleCounts[i] = true; } } if (swapchainDesc != null) { SwapchainDescription desc = swapchainDesc.Value; _mainSwapchain = new MTLSwapchain(this, ref desc); } PostDeviceCreated(); }
/// <summary> /// Sets up the real heap /// </summary> public static void InitRealHeap() { firstDescriptor = createBlockDescriptor(MINIMALPAGES * 0x1000); useRealHeap = true; Console.WriteLine("[HEAP] Initialized"); }
/// <summary> /// Frees a piece of memory /// </summary> /// <param name="ptr">The pointer</param> public static unsafe void Free(void *ptr) { #if HEAP_DEBUG if (ptr == null) { Panic.DoPanic("[HEAP] Free(null)"); } #endif Block * block = getBlockFromPtr(ptr); BlockDescriptor *descriptor = block->Descriptor; #if HEAP_DEBUG if (!block->Used) { Panic.DoPanic("[HEAP] Tried to free a block that was already freed"); } #endif Mutex.InternalLock(&descriptor->Lock); // Not used anymore block->Used = false; block->Descriptor->FreeSpace += block->Size; if ((int)block->Descriptor->FirstFree > (int)block) { block->Descriptor->FirstFree = block; } // Merge forward if (block->Next != null && !block->Next->Used) { Block *next = block->Next; block->Size += next->Size; block->Next = next->Next; if ((int)block->Descriptor->FirstFree > (int)next) { block->Descriptor->FirstFree = next; } if (next->Next != null) { next->Next->Prev = block; } } // Merge backwards if (block->Prev != null && !block->Prev->Used) { Block *prev = block->Prev; prev->Size += block->Size; prev->Next = block->Next; if ((int)block->Descriptor->FirstFree > (int)prev) { block->Descriptor->FirstFree = prev; } if (block->Next != null) { block->Next->Prev = prev; } } Mutex.InternalUnlock(&descriptor->Lock); }
/// <summary> /// Allocates an aligned piece of memory /// </summary> /// <param name="alignment">The alignment</param> /// <param name="size">The size</param> public static unsafe void *AlignedAlloc(int alignment, int size) { #if HEAP_DEBUG if (size <= 0) { Panic.DoPanic("[HEAP] size <= 0"); } if (alignment <= 0) { Panic.DoPanic("[HEAP] alignment <= 0"); } #endif if (useRealHeap) { // Find a descriptor that is big enough to hold the block header and its data // We need to look for something that can hold an aligned size if alignment is requested size += sizeof(Block); // Safe size if (size % alignment != 0) { size = size - (size % alignment); size += alignment; } Block * currentBlock = null; Block * previousBlock = null; BlockDescriptor *descriptor = getSufficientDescriptor(null, size); if (descriptor == null) { Panic.DoPanic("[HEAP] descriptor == null"); } retry: currentBlock = descriptor->FirstFree; previousBlock = null; // Lock descriptor Mutex.InternalLock(&descriptor->Lock); // Search in the descriptor while (true) { // Can fit in here if (currentBlock->Used || currentBlock->Size < size) { goto nextBlock; } #if HEAP_USE_MAGIC if (currentBlock->Magic != HEAP_MAGIC) { Panic.DoPanic("currentBlock->Magic != HEAP_MAGIC"); } #endif // Check if this block data would be aligned int currentData = (int)currentBlock + sizeof(Block); int remainder = currentData % alignment; // Not aligned if (remainder != 0) { // Split the current block into two // The first part is a padding // The second part is the block we want for allocation // This only happens if there is enough space left // Size of gap int gapSize = alignment - remainder; int newSize = currentBlock->Size - gapSize; // Would the new block be too small to fit our data into? if (newSize < size) { goto nextBlock; } // Store old data Block *newNext = currentBlock->Next; bool newUsed = currentBlock->Used; // Move block forward currentBlock = (Block *)((int)currentBlock + gapSize); currentBlock->Used = newUsed; currentBlock->Prev = previousBlock; currentBlock->Next = newNext; currentBlock->Size = newSize; currentBlock->Descriptor = descriptor; // Increase size of previous block if needed if (previousBlock != null) { previousBlock->Next = currentBlock; previousBlock->Size += gapSize; // If the block is used and the gap is merged // that means that the total free space in this descriptor decreases if (currentBlock->Used) { descriptor->FreeSpace -= gapSize; } } // There's space left to move this block and let the first block point to the new one else if (gapSize >= sizeof(Block)) { // Update first block Block *first = descriptor->First; descriptor->FreeSpace -= gapSize; first->Used = false; first->Prev = null; first->Next = currentBlock; first->Size = gapSize; first->Descriptor = descriptor; #if HEAP_USE_MAGIC first->Magic = HEAP_MAGIC; #endif currentBlock->Prev = first; } // This is the first block that was moved else { // Update header descriptor->First = currentBlock; descriptor->FirstFree = currentBlock; } } // Calculate leftover part for the next block int leftover = currentBlock->Size - size; // Update header currentBlock->Used = true; currentBlock->Descriptor = descriptor; currentBlock->Prev = previousBlock; #if HEAP_USE_MAGIC currentBlock->Magic = HEAP_MAGIC; #endif // Update descriptor descriptor->FreeSpace -= size; // If we have something left over, create a new block if (leftover > sizeof(Block) + 4) { // Update header Block *afterBlock = (Block *)((int)currentBlock + size); afterBlock->Size = leftover; afterBlock->Used = false; afterBlock->Next = currentBlock->Next; afterBlock->Prev = currentBlock; afterBlock->Descriptor = descriptor; #if HEAP_USE_MAGIC afterBlock->Magic = HEAP_MAGIC; #endif if (currentBlock->Next != null) { currentBlock->Next->Prev = afterBlock; } currentBlock->Next = afterBlock; currentBlock->Size = size; // Update descriptor if ((int)currentBlock < (int)descriptor->FirstFree) { descriptor->FirstFree = currentBlock; } } // Return block (skip header) Mutex.InternalUnlock(&descriptor->Lock); return((void *)((int)currentBlock + sizeof(Block))); // Next block nextBlock: { previousBlock = currentBlock; currentBlock = currentBlock->Next; Mutex.InternalUnlock(&descriptor->Lock); if (currentBlock == null) { // This was the last block in the descriptor // Due to alignment issues we haven't found a good place // Get another descriptor that has enough free space descriptor = getSufficientDescriptor(descriptor, size); goto retry; } } } } else { if (alignment == 0x1000) { return(KAlloc(size, true)); } else if (alignment == 4) { return(KAlloc(size, false)); } else { Panic.DoPanic("[HEAP] Unsupported alignment in early allocation"); return(null); } } }