diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2018-04-02 19:59:33 -0400 |
|---|---|---|
| committer | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-04-02 16:59:33 -0700 |
| commit | 38d5ef4764e9271ce2360f42b0759a236cddd9fe (patch) | |
| tree | 6c8eafb72eb997167dd77e3d1d5abc3582c5789a /source | |
| parent | 5a0273848aa4b854bb3c99a81701b044a8929bf8 (diff) | |
Feature/dx12 (#469)
* Fix signed/unsigned comparison warning.
* Split out d3d functions that will work across dx11 and 12.
* Improve slang-test/README.md around command line options.
* Make Guid comparison honor alignment for comparisons, such that mechanism work on architectures that can only do aligned accesses.
* Initial setup of D3D12 Renderer, with presentFrame and clearFrame.
* More support for D3D12
* Added FreeList
* Added D3D12CircularResourceHeap
* First attempt at createBuffer
* First pass at map/unmap.
* First pass binding vertex/constant buffers, and setting up InputLayout. Note that memory is not kept in scope on binding yet.
* First pass of D3DDescriptorHeap
* Small tidy up in render-d3d11. Added D3DDescriptorHeap to project.
* First pass at D3D12 bind state.
* Fix typos in D3D12Resource
* Tidy up Dx11 render binding a little to match more with Dx12 style.
* First pass at Dx12 BindingState
* Handling of the command list d3d12. Support for submitGpuWork and waitForGpu.
* First attempt at Dx12 capture of backbuffer to file.
* First attempt at D3D12 binding for graphics.
* D3D12 setup viewport etc - does now render triangle in render0.hlsl.
* First pass at support for compute on D3D12Renderer
* Use spaces over tabs in D3DUtil
* Tabs to spaces in D3D12DescriptorHeap
* Convert tab->spaces on render-d3d12.cpp
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/core.vcxproj | 2 | ||||
| -rw-r--r-- | source/core/slang-com-ptr.h | 19 | ||||
| -rw-r--r-- | source/core/slang-free-list.cpp | 216 | ||||
| -rw-r--r-- | source/core/slang-free-list.h | 142 |
4 files changed, 368 insertions, 11 deletions
diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj index 2a9a9a4fe..77e91b32f 100644 --- a/source/core/core.vcxproj +++ b/source/core/core.vcxproj @@ -32,6 +32,7 @@ <ClInclude Include="secure-crt.h" /> <ClInclude Include="slang-com-ptr.h" /> <ClInclude Include="slang-defines.h" /> + <ClInclude Include="slang-free-list.h" /> <ClInclude Include="slang-io.h" /> <ClInclude Include="slang-math.h" /> <ClInclude Include="slang-result.h" /> @@ -44,6 +45,7 @@ </ItemGroup> <ItemGroup> <ClCompile Include="platform.cpp" /> + <ClCompile Include="slang-free-list.cpp" /> <ClCompile Include="slang-io.cpp" /> <ClCompile Include="slang-string.cpp" /> <ClCompile Include="stream.cpp" /> diff --git a/source/core/slang-com-ptr.h b/source/core/slang-com-ptr.h index 729b6b266..9f6651306 100644 --- a/source/core/slang-com-ptr.h +++ b/source/core/slang-com-ptr.h @@ -51,21 +51,18 @@ struct Guid SLANG_FORCE_INLINE bool operator==(const Guid& aIn, const Guid& bIn) { + // Use the largest type the honors the alignment of Guid + typedef uint32_t CmpType; struct GuidCompare { - enum { kNum = sizeof(Guid) / sizeof(size_t) }; Guid guid; - size_t data[kNum]; + CmpType data[sizeof(Guid) / sizeof(CmpType)]; }; - const GuidCompare& a = reinterpret_cast<const GuidCompare&>(aIn); - const GuidCompare& b = reinterpret_cast<const GuidCompare&>(bIn); - - switch (GuidCompare::kNum) - { - case 2: return ((a.data[0] ^ b.data[0]) | (a.data[1] ^ b.data[1])) == 0; - case 4: return ((a.data[0] ^ b.data[0]) | (a.data[1] ^ b.data[1]) | (a.data[2] ^ b.data[2]) | (a.data[3] ^ b.data[3]) ) == 0; - default: return false; - } + // Type pun - so compiler can 'see' the pun and not break aliasing rules + const CmpType* a = reinterpret_cast<const GuidCompare&>(aIn).data; + const CmpType* b = reinterpret_cast<const GuidCompare&>(bIn).data; + // Make the guid comparison a single branch, by not using short circuit + return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]) | (a[3] ^ b[3])) == 0; } SLANG_FORCE_INLINE bool operator!=(const Guid& a, const Guid& b) diff --git a/source/core/slang-free-list.cpp b/source/core/slang-free-list.cpp new file mode 100644 index 000000000..0701558aa --- /dev/null +++ b/source/core/slang-free-list.cpp @@ -0,0 +1,216 @@ +#include "slang-free-list.h" + +//#include "list.h" + +namespace Slang { + +FreeList::~FreeList() +{ + _deallocateBlocks(m_activeBlocks); + _deallocateBlocks(m_freeBlocks); +} + +void FreeList::_init() +{ + m_top = nullptr; + m_end = nullptr; + + m_activeBlocks = nullptr; + m_freeBlocks = nullptr; + + m_freeElements = nullptr; + + m_elementSize = 0; + m_alignment = 1; + m_blockSize = 0; + m_blockAllocationSize = 0; +} + +void FreeList::_init(size_t elementSize, size_t alignment, size_t elemsPerBlock) +{ + alignment = (alignment < sizeof(void*)) ? sizeof(void*) : alignment; + + // Alignment must be a power of 2 + assert(((alignment - 1) & alignment) == 0); + + // The elementSize must at least be + elementSize = (elementSize >= alignment) ? elementSize : alignment; + m_blockSize = elementSize * elemsPerBlock; + m_elementSize = elementSize; + m_alignment = alignment; + + // Calculate the block size need, correcting for alignment + const size_t alignedBlockSize = (alignment <= DEFAULT_ALIGNMENT) ? + _calcAlignedBlockSize(DEFAULT_ALIGNMENT) : + _calcAlignedBlockSize(alignment); + + // Make the block struct size aligned + m_blockAllocationSize = m_blockSize + alignedBlockSize; + + m_top = nullptr; + m_end = nullptr; + + m_activeBlocks = nullptr; + m_freeBlocks = nullptr; ///< Blocks that there are no allocations in + + m_freeElements = nullptr; +} + +void FreeList::init(size_t elementSize, size_t alignment, size_t elemsPerBlock) +{ + _deallocateBlocks(m_activeBlocks); + _deallocateBlocks(m_freeBlocks); + _init(elementSize, alignment, elemsPerBlock); +} + +void FreeList::_deallocateBlocks(Block* block) +{ + while (block) + { + Block* next = block->m_next; + +#ifdef SLANG_FREE_LIST_INIT_MEM + Memory::set(block, 0xfd, m_blockAllocationSize); +#endif + + ::free(block); // deallocate(block, m_blockAllocationSize); + block = next; + } +} + +bool FreeList::isValidAllocation(const void* dataIn) const +{ + uint8_t* data = (uint8_t*)dataIn; + + Block* block = m_activeBlocks; + while (block) + { + uint8_t* start = block->m_data; + uint8_t* end = start + m_blockSize; + + if (data >= start && data < end) + { + // Check it's aligned correctly + if ((data - start) % m_elementSize) + { + return false; + } + + // Non allocated data is between top and end + if (data >= m_top && data < m_end) + { + return false; + } + + // It can't be in the free list + Element* ele = m_freeElements; + while (ele) + { + if (ele == (Element*)data) + { + return false; + } + + ele = ele->m_next; + } + return true; + } + + block = block->m_next; + } + // It's not in an active block -> it cannot be a valid allocation + return false; +} + +void* FreeList::_allocate() +{ + Block* block = m_freeBlocks; + if (block) + { + /// Remove from the free blocks + m_freeBlocks = block->m_next; + } + else + { + //block = (Block*)m_allocator->allocate(m_blockAllocationSize); + block = (Block*)::malloc(m_blockAllocationSize); + if (!block) + { + // Allocation failed... doh + return nullptr; + } + // Do the alignment + { + size_t fix = (size_t(block) + sizeof(Block) + m_alignment - 1) & ~(m_alignment - 1); + block->m_data = (uint8_t*)fix; + } + } + + // Attach to the active blocks + block->m_next = m_activeBlocks; + m_activeBlocks = block; + + // Set up top and end + m_end = block->m_data + m_blockSize; + + // Return the first element + uint8_t* element = block->m_data; + m_top = element + m_elementSize; + + SLANG_FREE_LIST_INIT_ALLOCATE(element) + + return element; +} + +void FreeList::deallocateAll() +{ + Block* block = m_activeBlocks; + if (block) + { + // Find the end block + while (block->m_next) + { +#ifdef SLANG_FREE_LIST_INIT_MEM + Memory::set(block->m_data, 0xfd, m_blockSize); +#endif + block = block->m_next; + } + // Attach to the freeblocks + block->m_next = m_freeBlocks; + // The list is now all freelists + m_freeBlocks = m_activeBlocks; + // There are no active blocks + m_activeBlocks = nullptr; + } + + m_top = nullptr; + m_end = nullptr; +} + +void FreeList::reset() +{ + _deallocateBlocks(m_activeBlocks); + _deallocateBlocks(m_freeBlocks); + + m_top = nullptr; + m_end = nullptr; + + m_activeBlocks = nullptr; + m_freeBlocks = nullptr; + + m_freeElements = nullptr; +} + + +void FreeList::_initAllocate(void* mem) +{ + ::memset(mem, 0xcd, m_elementSize); +} + +void FreeList::_initDeallocate(void* mem) +{ + ::memset(mem, 0xfd, m_elementSize); +} + +} // namespace Slang + diff --git a/source/core/slang-free-list.h b/source/core/slang-free-list.h new file mode 100644 index 000000000..10f0a39ba --- /dev/null +++ b/source/core/slang-free-list.h @@ -0,0 +1,142 @@ +#ifndef SLANG_FREE_LIST_H +#define SLANG_FREE_LIST_H + +#include "slang-defines.h" +#include "slang-result.h" +#include <stdlib.h> +#include <string.h> + +namespace Slang { + +#if SLANG_DEBUG +# define SLANG_FREE_LIST_INIT_MEM +#endif + +#ifdef SLANG_FREE_LIST_INIT_MEM +# define SLANG_FREE_LIST_INIT_ALLOCATE(ptr) _initAllocate(ptr); +# define SLANG_FREE_LIST_INIT_DEALLOCATE(ptr) _initDeallocate(ptr); +#else +# define SLANG_FREE_LIST_INIT_ALLOCATE(ptr) +# define SLANG_FREE_LIST_INIT_DEALLOCATE(ptr) +#endif + +/*! \brief A freelist is a simple and fast memory allocator that can allocate and free in any order identically sized blocks. + +\details A free list is a memory allocation system that performs allocations/deallocations very quickly for elements which are +all the same size. +In a freelist all elements are the same size, and elements can be allocated and freed in any order, as long as every deallocation +matches every allocation. Both allocation and deallocation are O(1), and generally just a few instructions. The underlying +memory allocator will allocate in large blocks, with multiple elements amortizing a more costly large allocation against lots +of fast small element allocations. */ +class FreeList +{ + public: + typedef FreeList ThisType; + + enum { DEFAULT_ALIGNMENT = sizeof(void*) }; + + /// Free elements are held in a singly linked list. The minimum size of an element is therefore a pointer + struct Element + { + Element* m_next; + }; + struct Block + { + Block* m_next; ///< The next block + uint8_t* m_data; ///< The list of the elements each m_elementSize in size + }; + + /// Allocate a single element + SLANG_FORCE_INLINE void* allocate(); + /// Deallocate a block that was previously allocated with allocate + SLANG_FORCE_INLINE void deallocate(void* data); + + /// Returns true if this is from a valid allocation + bool isValidAllocation(const void* dataIn) const; + + /// Get the element size + SLANG_FORCE_INLINE size_t getElementSize() const { return m_elementSize; } + /// Get the total size of each individual block allocation in bytes + SLANG_FORCE_INLINE size_t getBlockSize() const { return m_blockSize; } + + /// Deallocates all elements + void deallocateAll(); + /// Deallocates all, and frees any backing memory (put in initial state) + void reset(); + + /// Initialize. If called on an already initialized heap, the heap will be deallocated. + void init(size_t elementSize, size_t alignment, size_t elemsPerBlock); + + /// Default Ctor + FreeList() { _init(); } + /// Ctor + FreeList(size_t elementSize, size_t alignment, size_t elemsPerBlock) { _init(elementSize, alignment, elemsPerBlock); } + /// Dtor + ~FreeList(); + + protected: + /// Initializes assuming freelist is not constructed + void _init(size_t elementSize, size_t alignment, size_t elemsPerBlock); + void* _allocate(); + void _deallocateBlocks(Block* block); + /// Initializes setting everything to empty (doesn't free anything if already allocated) + void _init(); + + SLANG_FORCE_INLINE static size_t _calcAlignedBlockSize(size_t align) { return (sizeof(Block) + align - 1) & ~(align - 1); } + + void _initAllocate(void* mem); + void _initDeallocate(void* mem); + + uint8_t* m_top; ///< The top position of the current block + uint8_t* m_end; ///< The end of the current block + + Block* m_activeBlocks; ///< The blocks there are potentially allocations from + Block* m_freeBlocks; ///< Blocks that there are no allocations in + + Element* m_freeElements; ///< A singly linked list of elements available + + size_t m_elementSize; + size_t m_alignment; + size_t m_blockSize; + size_t m_blockAllocationSize; ///< The actual allocation size. Maybe bigger than m_blockSize if alignment requires it. +}; + +// -------------------------------------------------------------------------- +SLANG_FORCE_INLINE void* FreeList::allocate() +{ + // First see if there are any freeElements ready to go + { + Element* element = m_freeElements; + if (element) + { + m_freeElements = element->m_next; + SLANG_FREE_LIST_INIT_ALLOCATE(element) + return element; + } + } + if (m_top >= m_end) + { + return _allocate(); + } + void* data = (void*)m_top; + SLANG_FREE_LIST_INIT_ALLOCATE(data) + + m_top += m_elementSize; + return data; +} +// -------------------------------------------------------------------------- +SLANG_FORCE_INLINE void FreeList::deallocate(void* data) +{ + assert(isValidAllocation(data)); + + SLANG_FREE_LIST_INIT_DEALLOCATE(data) + + // Put onto the singly linked free element list + Element* ele = (Element*)data; + ele->m_next = m_freeElements; + m_freeElements = ele; +} + +} // namespace Slang + +#endif // SLANG_FREE_LIST_H
\ No newline at end of file |
