diff --git a/Source/DFPSR/api/bufferAPI.cpp b/Source/DFPSR/api/bufferAPI.cpp index be9501a6..f3ced99d 100644 --- a/Source/DFPSR/api/bufferAPI.cpp +++ b/Source/DFPSR/api/bufferAPI.cpp @@ -26,6 +26,7 @@ #include "stringAPI.h" #include "../implementation/math/scalar.h" #include "../base/SafePointer.h" +#include "../base/heap.h" namespace dsr { @@ -35,9 +36,9 @@ Buffer buffer_create(intptr_t newSize) { return handle_createArray(AllocationInitialization::Zeroed, (uintptr_t)newSize); } -Buffer buffer_create(intptr_t newSize, int paddToAlignment) { +Buffer buffer_create(intptr_t newSize, uintptr_t paddToAlignment) { if (newSize < 0) newSize = 0; - if (paddToAlignment > DSR_MAXIMUM_ALIGNMENT) { + if (paddToAlignment > heap_getHeapAlignment()) { throwError(U"Maximum alignment exceeded when creating a buffer!\n"); return Handle(); } else { diff --git a/Source/DFPSR/api/bufferAPI.h b/Source/DFPSR/api/bufferAPI.h index e5abc58b..14250528 100644 --- a/Source/DFPSR/api/bufferAPI.h +++ b/Source/DFPSR/api/bufferAPI.h @@ -61,9 +61,9 @@ namespace dsr { Buffer buffer_create(intptr_t newSize); // Allocate a Buffer with padding. - // The buffer always align the start with DSR_MAXIMUM_ALIGNMENT, but this function makes sure that paddToAlignment does not exceed DSR_MAXIMUM_ALIGNMENT. - // Pre-condition: paddToAlignment <= DSR_MAXIMUM_ALIGNMENT - Buffer buffer_create(intptr_t newSize, int paddToAlignment); + // The buffer always align the start with heap alignment, but this function makes sure that paddToAlignment does not exceed heap alignment. + // Pre-condition: paddToAlignment <= heap_getHeapAlignment() + Buffer buffer_create(intptr_t newSize, uintptr_t paddToAlignment); // Sets the allocation's destructor, to be called when there are no more reference counted pointers to the buffer. // The destructor is not responsible for freeing the memory allocation itself, only calling destructors in the content. diff --git a/Source/DFPSR/api/fileAPI.h b/Source/DFPSR/api/fileAPI.h index 948ab0d2..aa4dca04 100644 --- a/Source/DFPSR/api/fileAPI.h +++ b/Source/DFPSR/api/fileAPI.h @@ -27,9 +27,7 @@ #include "stringAPI.h" #include "bufferAPI.h" #include "../base/Handle.h" -#if defined(WIN32) || defined(_WIN32) - #define USE_MICROSOFT_WINDOWS -#endif +#include "../settings.h" // The file API exists to save and load buffers of data for any type of file. // Any file format that is implemented against the Buffer type instead of hardcoding against the file stream can easily be @@ -38,12 +36,14 @@ namespace dsr { // The PathSyntax enum allow processing theoreical paths for other operating systems than the local. enum class PathSyntax { Windows, Posix }; - #ifdef USE_MICROSOFT_WINDOWS + #if defined(USE_MICROSOFT_WINDOWS) // Let the local syntax be for Windows. #define LOCAL_PATH_SYNTAX dsr::PathSyntax::Windows - #else + #elif defined(USE_POSIX) // Let the local syntax be for Posix. #define LOCAL_PATH_SYNTAX dsr::PathSyntax::Posix + #else + #error "The target platform was not recognized as one of the supported operating systems in the file API!\n" #endif // Define NO_IMPLICIT_PATH_SYNTAX before including the header if you want all PathSyntax arguments to be explicit. diff --git a/Source/DFPSR/api/imageAPI.cpp b/Source/DFPSR/api/imageAPI.cpp index deb38d50..1ba9cf04 100644 --- a/Source/DFPSR/api/imageAPI.cpp +++ b/Source/DFPSR/api/imageAPI.cpp @@ -47,7 +47,7 @@ IMAGE_TYPE image_create_template(const char * name, int32_t width, int32_t heigh } else { static const int32_t pixelSize = image_getPixelSize(); // Calculate the stride. - uintptr_t byteStride = memory_getPaddedSize(width * pixelSize, DSR_MAXIMUM_ALIGNMENT); + uintptr_t byteStride = memory_getPaddedSize(width * pixelSize, heap_getHeapAlignment()); uint32_t pixelStride = byteStride / pixelSize; // Create the image. return IMAGE_TYPE(buffer_create(byteStride * height).setName(name), 0, width, height, pixelStride, packOrderIndex); diff --git a/Source/DFPSR/base/heap.cpp b/Source/DFPSR/base/heap.cpp index 49c59dd3..6e63379f 100644 --- a/Source/DFPSR/base/heap.cpp +++ b/Source/DFPSR/base/heap.cpp @@ -29,9 +29,7 @@ #include "../api/timeAPI.h" #include #include - -// Get settings from here. -#include "../settings.h" +#include "simd.h" #ifndef DISABLE_MULTI_THREADING // Requires -pthread for linking @@ -39,7 +37,110 @@ #include #endif +#if defined(USE_MICROSOFT_WINDOWS) + #include uint32_t getCacheLineSize() { return (uint32_t) (sizeof(void*) * 8); } // Approximation for ARM +#elif defined(USE_MACOS) + #include +#elif defined(USE_LINUX) + #include + #include + #include +#endif + namespace dsr { + // The default cache line size is used when not known from asking the system. + static const uintptr_t defaultCacheLineSize = 128; + // There is no point in using a heap alignment smaller than the allocation heads, so we align to at least 64 bytes. + static const uintptr_t minimumHeapAlignment = 64; + #if defined(USE_LINUX) + static int32_t getCacheLineSizeFromIndices(int32_t cpuIndex, int32_t cacheLevel) { + char path[256]; + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%i/cache/index%i/coherency_line_size", (int)cpuIndex, (int)cacheLevel); + FILE *file = fopen(path, "r"); + if (file == nullptr) { + return -1; + } + int cacheLineSize; + if (fscanf(file, "%i", &cacheLineSize) != 1) { + cacheLineSize = -1; + } + fclose(file); + return int32_t(cacheLineSize); + } + static int32_t getCacheLineSize() { + int32_t result = 0; + int32_t cpuIndex = 0; + int32_t cacheLevel = 0; + while (true) { + int32_t newSize = getCacheLineSizeFromIndices(cpuIndex, cacheLevel); + if (newSize == -1) { + if (cacheLevel == 0) { + // CPU does not exist, so we are done. + break; + } else { + // Cache level does not exist. Go to the next CPU. + cpuIndex++; + cacheLevel = 0; + } + } else { + // We have found the cache line size for cpuIndex and cacheLevel, so we include it in a maximum. + //printf("CPU %i cache level %i is %i.\n", (int)cpuIndex, (int)cacheLevel, (int)newSize); + result = max(result, newSize); + cacheLevel++; + } + } + if (result <= 0) { + result = defaultCacheLineSize; + string_sendMessage(U"WARNING! Failed to read cache line size from Linux system folders. The application might not be thread-safe.\n", MessageType::Warning); + } + printf("Detected a cache line width of %i bytes from reading Linux system folders.\n", (int)result); + return result; + } + #elif defined(USE_MACOS) + static int32_t getCacheLineSize() { + int32_t result = 0; + int32_t line_size = 0; + size_t sizeof_line_size = sizeof(line_size); + if (sysctlbyname("hw.cachelinesize", &line_size, &sizeof_line_size, 0, 0) == 0) { + result = line_size; + printf("Detected a cache line width of %i bytes from reading hw.cachelinesize.\n", (int)result); + } else { + result = defaultCacheLineSize; + string_sendMessage(U"WARNING! Failed to read hw.cachelinesize on MacOS. The application might not be thread-safe.\n", MessageType::Warning); + } + return result; + } + /* + #elif defined(USE_MICROSOFT_WINDOWS) + static int32_t getCacheLineSize() { + int32_t result = ?; // TODO: Implement + return result; + }*/ + #else + static int32_t getCacheLineSize() { + int32_t result = 0; + result = defaultCacheLineSize; + string_sendMessage(U"WARNING! The target platform does not have a method for detecting cache line width in DFPSR/base/heap.cpp.\n", MessageType::Warning); + return result; + } + #endif + + static uintptr_t heapAlignmentAndMask = 0u; + uintptr_t heap_getHeapAlignment() { + static uintptr_t heapAlignment = 0u; + if (heapAlignment == 0) { + heapAlignment = getCacheLineSize(); + if (heapAlignment < sizeof(DSR_FLOAT_VECTOR_SIZE)) heapAlignment = sizeof(DSR_FLOAT_VECTOR_SIZE); + if (heapAlignment < minimumHeapAlignment) heapAlignment = minimumHeapAlignment; + heapAlignmentAndMask = memory_createAlignmentAndMask(heapAlignment); + } + return heapAlignment; + } + static uintptr_t heap_getHeapAlignmentAndMask() { + heap_getHeapAlignment(); + return heapAlignmentAndMask; + } + // Keep track of the program's state. enum class ProgramState { Starting, // A single thread does global construction without using any mutex. @@ -58,9 +159,9 @@ namespace dsr { threadCount++; if (threadCount > 1 && programState != ProgramState::Running) { if (programState == ProgramState::Starting) { - throwError(U"Tried to create another thread before construction of global variables was complete!\n"); + string_sendMessage(U"Tried to create another thread before construction of global variables was complete!\n", MessageType::Error); } else if (programState == ProgramState::Terminating) { - throwError(U"Tried to create another thread after destruction of global variables had begun!\n"); + string_sendMessage(U"Tried to create another thread after destruction of global variables had begun!\n", MessageType::Error); } } threadLock.unlock(); @@ -140,19 +241,22 @@ namespace dsr { using HeapFlag = uint16_t; using BinIndex = uint16_t; - // The framework's maximum memory alignment is either the largest float SIMD vector or the thread safe alignment. - static const uintptr_t heapAlignment = DSR_MAXIMUM_ALIGNMENT; - static const uintptr_t heapAlignmentAndMask = memory_createAlignmentAndMask(heapAlignment); - // The free function is hidden, because all allocations are forced to use reference counting, // so that a hard exit can disable recursive calls to heap_free by incrementing all reference counters. static void heap_free(void * const allocation); + // The bin size equals two to the power of binIndex multiplied by minimumHeapAlignment. + // This allow knowing the number of bins in compile time by leaving a number of unused bins specified by MIN_BIN_COUNT. + inline constexpr uintptr_t getBinSize(BinIndex binIndex) { + // Index 0 starts at the minimum alignment to know the number of bins in compile time, so some bins will be unused when the alignment is bigger. + return (uintptr_t(1) << uintptr_t(binIndex)) * minimumHeapAlignment; + } + // Calculates the largest power of two allocation size that does not overflow a pointer on the target platform. constexpr int calculateBinCount() { intptr_t p = 0; while (true) { - uintptr_t result = ((uintptr_t)1 << p) * heapAlignment; + uintptr_t result = getBinSize(p); // Once the value overflows in uintptr_t we have found the index that can not be used, which is also the bin count when including index zero. if (result == 0) { return p; @@ -160,14 +264,12 @@ namespace dsr { p++; } } - static const int MAX_BIN_COUNT = calculateBinCount(); - inline uintptr_t getBinSize(BinIndex index) { - return (((uintptr_t)1) << ((uintptr_t)index)) * heapAlignment; - } + // The index of the last used bin. + static const int MAX_BIN_COUNT = calculateBinCount(); - static BinIndex getBinIndex(uintptr_t minimumSize) { - for (intptr_t p = 0; p < MAX_BIN_COUNT; p++) { + static BinIndex getBinIndex(uintptr_t minimumSize, intptr_t minimumBin) { + for (intptr_t p = minimumBin; p < MAX_BIN_COUNT; p++) { uintptr_t result = getBinSize(p); if (result >= minimumSize) { return p; @@ -177,6 +279,9 @@ namespace dsr { return -1; } + // The index of the first used bin, which is also the number of unused bins. + static const int MIN_BIN_COUNT = getBinIndex(heap_getHeapAlignment(), 0); + static const HeapFlag heapFlag_recycled = 1 << 0; struct HeapHeader : public AllocationHeader { // Because nextRecycled and usedSize have mutually exclusive lifetimes, they can share memory location. @@ -235,21 +340,29 @@ namespace dsr { this->flags &= ~heapFlag_recycled; } }; - static const uintptr_t heapHeaderPaddedSize = memory_getPaddedSize(sizeof(HeapHeader), heapAlignment); + + // TODO: Allow using the header directly for manipulation in the API, now that the offset is not known in compile time. + inline uintptr_t heap_getHeapHeaderPaddedSize() { + static uintptr_t heapHeaderPaddedSize = 0; + if (heapHeaderPaddedSize == 0) { + heapHeaderPaddedSize = memory_getPaddedSize(sizeof(HeapHeader), heap_getHeapAlignment()); + } + return heapHeaderPaddedSize; + } AllocationHeader *heap_getHeader(void * const allocation) { - return (AllocationHeader*)((uint8_t*)allocation - heapHeaderPaddedSize); + return (AllocationHeader*)((uint8_t*)allocation - heap_getHeapHeaderPaddedSize()); } inline HeapHeader *headerFromAllocation(void const * const allocation) { - return (HeapHeader *)((uint8_t*)allocation - heapHeaderPaddedSize); + return (HeapHeader *)((uint8_t*)allocation - heap_getHeapHeaderPaddedSize()); } inline void *allocationFromHeader(HeapHeader * const header) { - return ((uint8_t*)header) + heapHeaderPaddedSize; + return ((uint8_t*)header) + heap_getHeapHeaderPaddedSize(); } inline void const *allocationFromHeader(HeapHeader const * const header) { - return ((uint8_t const *)header) + heapHeaderPaddedSize; + return ((uint8_t const *)header) + heap_getHeapHeaderPaddedSize(); } #ifdef SAFE_POINTER_CHECKS @@ -274,7 +387,7 @@ namespace dsr { return 0; } else { HeapHeader *header = headerFromAllocation(allocation); - return memory_getPaddedSize_usingAndMask(header->getUsedSize(), heapAlignmentAndMask); + return memory_getPaddedSize_usingAndMask(header->getUsedSize(), heap_getHeapAlignmentAndMask()); } } #endif @@ -316,7 +429,7 @@ namespace dsr { HeapHeader *header = headerFromAllocation(allocation); lockMemory(); if (header->useCount == 0) { - throwError(U"Heap error: Decreasing a count that is already zero!\n"); + string_sendMessage(U"Heap error: Decreasing a count that is already zero!\n", MessageType::Error); } else { header->useCount--; if (header->useCount == 0) { @@ -375,7 +488,7 @@ namespace dsr { ~HeapPool() { // The destruction should be ignored to manually terminate once all memory allocations have been freed. if (programState != ProgramState::Terminating) { - throwError(U"Heap error: Terminated the application without first calling heap_terminatingApplication or heap_hardExitCleaning!\n"); + string_sendMessage(U"Heap error: Terminated the application without first calling heap_terminatingApplication or heap_hardExitCleaning!\n", MessageType::Error); } } }; @@ -383,7 +496,7 @@ namespace dsr { static UnsafeAllocation tryToAllocate(HeapMemory &heap, uintptr_t paddedSize, uintptr_t alignmentAndMask) { UnsafeAllocation result(nullptr, nullptr); uint8_t *dataPointer = (uint8_t*)(((uintptr_t)(heap.allocationPointer) - paddedSize) & alignmentAndMask); - AllocationHeader *headerPointer = (AllocationHeader*)(dataPointer - heapHeaderPaddedSize); + AllocationHeader *headerPointer = (AllocationHeader*)(dataPointer - heap_getHeapHeaderPaddedSize()); if ((uint8_t*)headerPointer >= heap.top) { // There is enough space, so confirm the allocation. result = UnsafeAllocation(dataPointer, headerPointer); @@ -399,7 +512,7 @@ namespace dsr { // Start with the most recent heap, which is most likely to have enough space. HeapMemory *currentHeap = pool.lastHeap; while (currentHeap != nullptr) { - UnsafeAllocation result = tryToAllocate(*currentHeap, paddedSize, heapAlignmentAndMask); + UnsafeAllocation result = tryToAllocate(*currentHeap, paddedSize, alignmentAndMask); if (result.data != nullptr) { return result; } @@ -414,19 +527,19 @@ namespace dsr { pool.lastHeap = new HeapMemory(allocationSize); pool.lastHeap->prevHeap = previousHeap; // Make one last attempt at allocating the memory. - return tryToAllocate(*(pool.lastHeap), paddedSize, heapAlignmentAndMask); + return tryToAllocate(*(pool.lastHeap), paddedSize, alignmentAndMask); } static HeapPool defaultHeap; UnsafeAllocation heap_allocate(uintptr_t minimumSize, bool zeroed) { UnsafeAllocation result(nullptr, nullptr); - int32_t binIndex = getBinIndex(minimumSize); + int32_t binIndex = getBinIndex(minimumSize, MIN_BIN_COUNT); if (binIndex == -1) { // If the requested allocation is so big that there is no power of two that can contain it without overflowing the address space, then it can not be allocated. - throwError(U"Heap error: Exceeded the maximum size when trying to allocate!\n"); + string_sendMessage(U"Heap error: Exceeded the maximum size when trying to allocate!\n", MessageType::Error); } else { - uintptr_t paddedSize = ((uintptr_t)1 << binIndex) * heapAlignment; + uintptr_t paddedSize = getBinSize(binIndex); lockMemory(); allocationCount++; // Look for pre-existing allocations in the recycling bins. @@ -442,9 +555,9 @@ namespace dsr { result = UnsafeAllocation((uint8_t*)allocationFromHeader(binHeader), binHeader); } else { // Look for a heap with enough space for a new allocation. - result = tryToAllocate(defaultHeap, paddedSize, heapAlignmentAndMask); + result = tryToAllocate(defaultHeap, paddedSize, heap_getHeapAlignmentAndMask()); if (result.data == nullptr) { - throwError(U"Heap error: Failed to allocate more memory!\n"); + string_sendMessage(U"Heap error: Failed to allocate more memory!\n", MessageType::Error); } } unlockMemory(); @@ -473,7 +586,7 @@ namespace dsr { // Get the recycled allocation's header. HeapHeader *header = headerFromAllocation(allocation); if (header->isRecycled()) { - throwError(U"Heap error: A heap allocation was freed twice!\n"); + string_sendMessage(U"Heap error: A heap allocation was freed twice!\n", MessageType::Error); } else { // Call the destructor provided with any external resource that also needs to be freed. if (header->destructor.destructor) { @@ -483,7 +596,7 @@ namespace dsr { header->destructor = HeapDestructor(); int binIndex = header->binIndex; if (binIndex >= MAX_BIN_COUNT) { - throwError(U"Heap error: Out of bound recycling bin index in corrupted head of freed allocation!\n"); + string_sendMessage(U"Heap error: Out of bound recycling bin index in corrupted head of freed allocation!\n", MessageType::Error); } else { // Make any previous head from the bin into the new tail. HeapHeader *oldHeader = defaultHeap.recyclingBin[binIndex]; @@ -541,11 +654,11 @@ namespace dsr { } void impl_throwAllocationFailure() { - throwError(U"Failed to allocate memory for a new object!\n"); + string_sendMessage(U"Failed to allocate memory for a new object!\n", MessageType::Error); } void impl_throwNullException() { - throwError(U"Null handle exception!\n"); + string_sendMessage(U"Null handle exception!\n", MessageType::Error); } void impl_throwIdentityMismatch(uint64_t allocationIdentity, uint64_t pointerIdentity) { diff --git a/Source/DFPSR/base/heap.h b/Source/DFPSR/base/heap.h index a232ebd9..634961e5 100644 --- a/Source/DFPSR/base/heap.h +++ b/Source/DFPSR/base/heap.h @@ -64,7 +64,7 @@ namespace dsr { void heap_setAllocationName(void * const allocation, const char *name); // Get the ascii name of allocation, or "none" if allocation is nullptr. const char * heap_getAllocationName(void * const allocation); - // Gets the size padded out to whole blocks of DSR_MAXIMUM_ALIGNMENT, for constructing a SafePointer. + // Gets the size padded out to whole blocks of heap alignment, for constructing a SafePointer. uintptr_t heap_getPaddedSize(void const * const allocation); #endif @@ -130,6 +130,9 @@ namespace dsr { // If the element's size is a power of two, you can pre-compute a bit mask using memory_createAlignmentAndMask for rounding down. uintptr_t heap_getAllocationSize(void const * const allocation); + // Get the alignment of the heap, which depends on the largest cache line size. + uintptr_t heap_getHeapAlignment(); + // Pre-condition: The allocation pointer must point to the start of a payload allocated using heap_allocate, no offsets nor other allocators allowed. // Post-condition: Returns a pointer to the heap allocation's header, which is used to construct safe pointers. AllocationHeader *heap_getHeader(void * const allocation); diff --git a/Source/DFPSR/base/simd.h b/Source/DFPSR/base/simd.h index 36be221f..708ca4fc 100644 --- a/Source/DFPSR/base/simd.h +++ b/Source/DFPSR/base/simd.h @@ -91,6 +91,8 @@ #include "../settings.h" #include "../base/noSimd.h" + namespace dsr { + // Alignment in bytes #define ALIGN_BYTES(SIZE) __attribute__((aligned(SIZE))) #define ALIGN16 ALIGN_BYTES(16) // 128-bit alignment @@ -99,8 +101,6 @@ #define ALIGN128 ALIGN_BYTES(128) // 1024-bit alignment #define ALIGN256 ALIGN_BYTES(256) // 2048-bit alignment - namespace dsr { - // Everything declared in here handles things specific for SSE. // Direct use of the macros will not provide portability to all hardware. #ifdef USE_SSE2 diff --git a/Source/DFPSR/settings.h b/Source/DFPSR/settings.h index 896ef22d..900fec76 100644 --- a/Source/DFPSR/settings.h +++ b/Source/DFPSR/settings.h @@ -22,6 +22,30 @@ // Can be used to quickly rule out concurrency problems when debugging, by recreating the same error without extra threads. //#define DISABLE_MULTI_THREADING + // Identify operating systems in a somewhat future-proof way. + // More will have to be added later. + #if defined(_WIN32) || defined(_WIN64) || defined(_WIN128) || defined(_WIN256) || defined(_WIN512) || defined(_WIN1024) || defined(_WIN2048) + #define USE_MICROSOFT_WINDOWS + #elif defined(__gnu_linux__) || defined(__linux__) || defined(__linux) + #define USE_LINUX + #define USE_POSIX + #elif defined(__APPLE__) || defined(__MACH__) + #define USE_MACOS + #define USE_POSIX + #elif defined(__unix) || defined(__unix__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__BSD__) + // Trying to cover as many systems as possible using Posix for basic command line applications. + #define USE_POSIX + #else + #error "Could not identify the operating system!\n" + #endif + + // Identify processor types. + #if defined(__INTEL__) || defined(__SSE2__) + #define USE_INTEL + #elif defined(__ARM__) || defined(__ARM_NEON) + #define USE_ARM + #endif + // Determine which SIMD extensions to use in base/simd.h. // Use the standard compiler flags for enabling SIMD extensions. // If your compiler uses a different macro name to indicate the presence of a SIMD extension, you can add them here to enable the USE_* macros. @@ -79,13 +103,6 @@ #define USE_BASIC_SIMD #endif - // Get the largest size of SIMD vectors for memory alignment. - #ifdef USE_256BIT_F_SIMD - #define DSR_LARGEST_VECTOR_SIZE 32 - #else - #define DSR_LARGEST_VECTOR_SIZE 16 - #endif - // If using C++ 2020 or later. #if (__cplusplus >= 202002L) // Endianness @@ -103,23 +120,4 @@ #error "The DFPSR framework does not work on mixed-endian systems, because it relies on a linear relation between memory addresses and bit shifting!\n" #endif #endif - - // TODO: Create a function that checks CPUID when available on the platform, to give warnings if the computer does not meet the system requirements of the specific build. - // Size of cache lines used to protect different threads from accidental sharing cache line across independent memory allocations. - // Must be a power of two, and no less than the largest cache line among all CPU cores that might run the program. - // Can be assigned using the DSR_THREAD_SAFE_ALIGNMENT macro externally or changed here. - #ifndef DSR_THREAD_SAFE_ALIGNMENT - // 64 bytes is generally a good choice, because it is large enough to align with cache lines on most computers and large enough to store an allocation header. - // Note that Apple M1 has a cache line of 128 bytes, which exceeds this default value. - #define DSR_THREAD_SAFE_ALIGNMENT 64 - #endif - - // TODO: Allow having a dynamic largest vector size to support SVE vectors of 1024 or 2048 bits in the future. - // When allocating memory for being reused many times for different purposes, we need to know the maximum alignment that will be required ahead of time. - // Here we define it as the maximum of the largest SIMD vector and the thread safe alignment. - #if (DSR_LARGEST_VECTOR_SIZE > DSR_THREAD_SAFE_ALIGNMENT) - #define DSR_MAXIMUM_ALIGNMENT DSR_LARGEST_VECTOR_SIZE - #else - #define DSR_MAXIMUM_ALIGNMENT DSR_THREAD_SAFE_ALIGNMENT - #endif #endif diff --git a/Source/README.md b/Source/README.md new file mode 100644 index 00000000..bedafef1 --- /dev/null +++ b/Source/README.md @@ -0,0 +1,17 @@ +# Source folder +The source folder contains the framework, system dependent backends, code examples and tools. + +## System dependent code +Some of the code depends on different hardware and operating systems to abstract them away. + +* Source/DFPSR/api/fileAPI: The file API is implemented to hande file access on different operating systems. + +* Source/DFPSR/base/simd.h: The simd.h header is a SIMD abstraction layer that works without SIMD but can become faster for specific processor architectures by implementing the features. + +* Source/DFPSR/base/heap.cpp: The getCacheLineSize function depends on the operating system to get the cache line width for memory alignment, because aligning with cache lines is needed for thread safety and getting cache line width directly from hardware would require contemporary inline assembler hacks that will not work for future generations of hardware. + +* Source/windowManagers: Contains the integrations for displaying graphics and getting mouse and keyboard input on specific operating systems. +These are selected based on the operating system in Source/DFPSR/DFPSR.DsrHead + +* Source/soundManagers: Contains the integrations for sound on specific operating systems. +These are selected based on the operating system in Source/DFPSR/DFPSR.DsrHead