diff --git a/trinity/Eve/EveSpaceScene.cpp b/trinity/Eve/EveSpaceScene.cpp index dad66eb2d..e91e9b013 100644 --- a/trinity/Eve/EveSpaceScene.cpp +++ b/trinity/Eve/EveSpaceScene.cpp @@ -440,6 +440,24 @@ bool EveSpaceScene::OnPrepareResources() return true; } +#if TBB_ENABLED +extern size_t g_currentThreadCount; +#endif + +namespace +{ +// Blocks of up to maxGrainSize objects, but never fewer than four tasks per thread so small scenes still spread out. +size_t GrainSize( size_t count, size_t maxGrainSize ) +{ +#if TBB_ENABLED + size_t grain = count / ( 4 * std::max( g_currentThreadCount, 1 ) ); + return std::clamp( grain, 1, maxGrainSize ); +#else + return maxGrainSize; +#endif +} +} + void EveSpaceScene::Update( Be::Time realTime, Be::Time simTime ) { TRINITY_STATS_ZONE( __FUNCTION__ ); @@ -572,12 +590,12 @@ void EveSpaceScene::Update( Be::Time realTime, Be::Time simTime ) Tr2ParallelTaskGroup taskGroup = {}; m_updateContext.SetTaskGroup( &taskGroup ); - for( auto& object : m_objects ) - { - taskGroup.run( [object, this] { - object->UpdateAsyncronous( m_updateContext ); - } ); - } + Tr2ParallelFor( Tr2BlockedRange( 0, m_objects.size(), GrainSize( m_objects.size(), 32 ) ), [&]( Tr2BlockedRange range ) { + for( size_t i = range.begin(); i != range.end(); ++i ) + { + m_objects[i]->UpdateAsyncronous( m_updateContext ); + } + } ); for( auto& object : m_uiObjects ) { taskGroup.run( [object, this] { @@ -883,6 +901,15 @@ void GetBatchesFromRenderables( ITr2Renderable** const objectRenderables, const } } +namespace +{ +enum PerObjectFlags : uint8_t +{ + PER_OBJECT_DEFERRED = 1, + PER_OBJECT_TRANSPARENT = 2, +}; +} + void GetBatchesFromRenderables( ITr2Renderable** const objectRenderables, const unsigned renderableCount, @@ -895,27 +922,54 @@ void GetBatchesFromRenderables( { TRINITY_STATS_ZONE( __FUNCTION__ ); - std::vector perObjectData; - perObjectData.reserve( renderableCount ); + std::vector perObjectData( renderableCount, nullptr ); + std::vector flags( renderableCount, 0 ); + ITriRenderBatchAccumulator* const opaqueBatches = batches[TRIBATCHTYPE_OPAQUE]; + const bool wantTransparencies = objectsWithTransparencies != nullptr; { TRINITY_STATS_ZONE( "PerObjectData" ); + Tr2ParallelFor( Tr2BlockedRange( 0, renderableCount, unsigned( GrainSize( renderableCount, 64 ) ) ), [&]( Tr2BlockedRange range ) { + for( unsigned i = range.begin(); i != range.end(); ++i ) + { + ITr2Renderable* r = objectRenderables[i]; + if( !r->SupportsParallelPerObjectData() ) + { + flags[i] = PER_OBJECT_DEFERRED; + continue; + } + perObjectData[i] = r->GetPerObjectData( opaqueBatches ); + if( wantTransparencies && r->HasTransparentBatches() ) + { + flags[i] = PER_OBJECT_TRANSPARENT; + } + } + } ); for( unsigned i = 0; i != renderableCount; ++i ) { + if( flags[i] != PER_OBJECT_DEFERRED ) + { + continue; + } ITr2Renderable* r = objectRenderables[i]; - - perObjectData.push_back( r->GetPerObjectData( batches[TRIBATCHTYPE_OPAQUE] ) ); + perObjectData[i] = r->GetPerObjectData( opaqueBatches ); + flags[i] = wantTransparencies && r->HasTransparentBatches() ? PER_OBJECT_TRANSPARENT : 0; } } { TRINITY_STATS_ZONE( "GetBatches" ); + Tr2EnumerableThreadSpecific* threadBatches[TRIBATCHTYPE_COUNT_OF_BATCH_TYPES]; + for( unsigned type = 0; type != batchTypeCount; ++type ) + { + threadBatches[type] = &perThreadBatches[batchTypes[type]]; + } Tr2ParallelFor( unsigned( 0 ), renderableCount, [&]( unsigned i ) { ITr2Renderable* r = objectRenderables[i]; for( unsigned type = 0; type != batchTypeCount; ++type ) { - r->GetBatches( &perThreadBatches[batchTypes[type]].local(), batchTypes[type], perObjectData[i], reason ); + r->GetBatches( &threadBatches[type]->local(), batchTypes[type], perObjectData[i], reason ); } } ); } @@ -935,13 +989,11 @@ void GetBatchesFromRenderables( { TRINITY_STATS_ZONE( "Transparencies" ); - objectsWithTransparencies->reserve( renderableCount ); - for( unsigned i = 0; i != renderableCount; ++i ) { - ITr2Renderable* r = objectRenderables[i]; - if( r->HasTransparentBatches() ) + if( flags[i] == PER_OBJECT_TRANSPARENT ) { + ITr2Renderable* r = objectRenderables[i]; ITr2RenderableEntry entry; entry.m_object = r; entry.m_distance = r->GetSortValue(); @@ -1440,8 +1492,11 @@ void EveSpaceScene::GatherBatches( bool includeDistortions, Tr2RenderContext& re { TRINITY_STATS_ZONE( "UpdateVisibility" ); - Tr2ParallelDo( m_objects.begin(), m_objects.end(), [&]( IEveSpaceObject2* obj ) { - obj->UpdateVisibility( m_updateContext, identity ); + Tr2ParallelFor( Tr2BlockedRange( 0, m_objects.size(), GrainSize( m_objects.size(), 32 ) ), [&]( Tr2BlockedRange range ) { + for( size_t i = range.begin(); i != range.end(); ++i ) + { + m_objects[i]->UpdateVisibility( m_updateContext, identity ); + } } ); m_cameraAttachmentParent->SetTransform( Tr2Renderer::GetInverseViewTransform() ); @@ -1540,9 +1595,15 @@ void EveSpaceScene::PrepareRaytracedShadows( Tr2RenderContext& renderContext ) ProcessOutdatedRTAnimations( renderContext ); auto& shadowCasters = m_componentRegistry->GetComponents(); - Tr2ParallelDo( begin( shadowCasters ), end( shadowCasters ), [&]( auto caster ) { - caster->PushRtGeometry( *m_rtManager ); - } ); + { + TRINITY_STATS_ZONE( "PushRtGeometry" ); + Tr2ParallelFor( Tr2BlockedRange( 0, shadowCasters.size(), GrainSize( shadowCasters.size(), 32 ) ), [&]( Tr2BlockedRange range ) { + for( size_t i = range.begin(); i != range.end(); ++i ) + { + shadowCasters[i]->PushRtGeometry( *m_rtManager ); + } + } ); + } Tr2RtShaderTableDescriptionAL* shaderTableDescs[3]; Tr2RaytracingPipelineStateManager* pipelineManagers[3]; @@ -2391,9 +2452,9 @@ void EveSpaceScene::RenderIntoCloudShadowMap( Tr2RenderContext& renderContext, c { TRINITY_STATS_ZONE( "get shadowbatches for volumetrics" ); auto shadowFrustum = TriShadowOrthoFrustum( cloudShadowInformation->shadowFrustum, cloudShadowInformation->shadowMapSize, m_sunData.DirWorld ); - float sizeInShadow = 0.0f; for( auto& caster : shadowCasters ) { + float sizeInShadow = 0.0f; caster->IsCastingShadow( frustum, shadowFrustum, TR2RENDERREASON_NORMAL, sizeInShadow ); // special threshold check if( sizeInShadow > 5.0f ) @@ -2517,9 +2578,9 @@ void EveSpaceScene::RenderShadowMapForSpotLight( shadowFrustum.DeriveFrustum( &view, &lightPosition, &projection, renderContext.m_esm.GetViewport() ); { TRINITY_STATS_ZONE( "get shadowbatches for light" ); - float sizeInShadow = 0.0f; for( auto& caster : shadowCasters ) { + float sizeInShadow = 0.0f; caster->IsCastingShadow( m_updateContext.GetFrustum(), TriShadowFrustum( shadowFrustum ), TR2RENDERREASON_NORMAL, sizeInShadow ); // special threshold check if( sizeInShadow > 5.0f ) @@ -4183,6 +4244,7 @@ void EveSpaceScene::GetLightMatrices( const Tr2LightManager::PerLightData& light void EveSpaceScene::ProcessOutdatedRTAnimations( Tr2RenderContext& renderContext ) { + TRINITY_STATS_ZONE( __FUNCTION__ ); TriFrustumOrtho sunShadowFrustum; auto sunDir = m_sunData.DirWorld; @@ -4245,7 +4307,12 @@ void EveSpaceScene::ProcessOutdatedRTAnimations( Tr2RenderContext& renderContext } // Find all casters and mark those that are casting shadows as dirty so RT can rebuild it - m_componentRegistry->ProcessComponents( [&]( IEveShadowCaster* caster ) -> void { + const TriFrustum& cameraFrustum = m_updateContext.GetFrustum(); + const TriShadowOrthoFrustum sunOrthoFrustum( sunShadowFrustum, SHADOW_MAP_SIZE, sunDir ); + std::vector spotShadowFrustums( begin( spotLightFrustums ), end( spotLightFrustums ) ); + + auto& casters = m_componentRegistry->GetComponents(); + Tr2ParallelDo( begin( casters ), end( casters ), [&]( IEveShadowCaster* caster ) { if( caster->IsShadowCastingDirty() ) { return; @@ -4253,7 +4320,7 @@ void EveSpaceScene::ProcessOutdatedRTAnimations( Tr2RenderContext& renderContext { float radius; - if( caster->IsCastingShadow( m_updateContext.GetFrustum(), TriShadowOrthoFrustum( sunShadowFrustum, SHADOW_MAP_SIZE, sunDir ), TR2RENDERREASON_NORMAL, radius ) ) + if( caster->IsCastingShadow( cameraFrustum, sunOrthoFrustum, TR2RENDERREASON_NORMAL, radius ) ) { caster->MarkRtDirty(); return; @@ -4263,17 +4330,17 @@ void EveSpaceScene::ProcessOutdatedRTAnimations( Tr2RenderContext& renderContext for( auto lightData : pointsLightDatas ) { - if( caster->IsCastingShadow( m_updateContext.GetFrustum(), lightData->position, lightData->radius, TR2RENDERREASON_NORMAL ) ) + if( caster->IsCastingShadow( cameraFrustum, lightData->position, lightData->radius, TR2RENDERREASON_NORMAL ) ) { caster->MarkRtDirty(); return; } } - for( const auto& shadowFrustum : spotLightFrustums ) + for( const auto& shadowFrustum : spotShadowFrustums ) { float sizeInShadow = 0.0f; - caster->IsCastingShadow( m_updateContext.GetFrustum(), TriShadowFrustum( shadowFrustum ), TR2RENDERREASON_NORMAL, sizeInShadow ); + caster->IsCastingShadow( cameraFrustum, shadowFrustum, TR2RENDERREASON_NORMAL, sizeInShadow ); // special threshold check if( sizeInShadow > 5.0f ) { diff --git a/trinity/Eve/SpaceObject/Children/EveChildMesh.h b/trinity/Eve/SpaceObject/Children/EveChildMesh.h index 130d9f8c7..c7ab67c09 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildMesh.h +++ b/trinity/Eve/SpaceObject/Children/EveChildMesh.h @@ -113,6 +113,10 @@ BLUE_CLASS( EveChildMesh ) : void GetBatchesFromOverlayVector( ITriRenderBatchAccumulator * batches, const Tr2PerObjectData* perObjectData, TriBatchType batchType ); virtual float GetSortValue(); virtual Tr2PerObjectData* GetPerObjectData( ITriRenderBatchAccumulator * accumulator ); + bool SupportsParallelPerObjectData() const override + { + return true; + } virtual bool IsVisible( const EveUpdateContext& updateContext ) const override; ///////////////////////////////////////////////////////////////////////////////////// diff --git a/trinity/Eve/SpaceObject/EveSpaceObject2.h b/trinity/Eve/SpaceObject/EveSpaceObject2.h index a1ce39d09..b603c40fc 100644 --- a/trinity/Eve/SpaceObject/EveSpaceObject2.h +++ b/trinity/Eve/SpaceObject/EveSpaceObject2.h @@ -334,6 +334,10 @@ BLUE_CLASS( EveSpaceObject2 ) : virtual void GetBatches( ITriRenderBatchAccumulator * batches, TriBatchType batchType, const Tr2PerObjectData* perObjectData, Tr2RenderReason reason = TR2RENDERREASON_NORMAL ); virtual float GetSortValue(); virtual Tr2PerObjectData* GetPerObjectData( ITriRenderBatchAccumulator * accumulator ); + bool SupportsParallelPerObjectData() const override + { + return true; + } virtual bool IsVisible( const EveUpdateContext& updateContext ) const override; ///////////////////////////////////////////////////////////////////////////////////// diff --git a/trinity/ITr2Renderable.h b/trinity/ITr2Renderable.h index 3c08d247f..ff236fc4e 100644 --- a/trinity/ITr2Renderable.h +++ b/trinity/ITr2Renderable.h @@ -54,6 +54,12 @@ BLUE_INTERFACE( ITr2Renderable ) : virtual float GetSortValue() = 0; virtual Tr2PerObjectData* GetPerObjectData( ITriRenderBatchAccumulator * accumulator ) = 0; + + // True when GetPerObjectData and HasTransparentBatches only touch this object's own state and may run on worker threads. + virtual bool SupportsParallelPerObjectData() const + { + return false; + } }; REGISTER_COMPONENT_TYPE( "ReflectionRenderable", ITr2Renderable ); diff --git a/trinity/Tr2RingBuffer.cpp b/trinity/Tr2RingBuffer.cpp index 7c5fa6d5b..8cc7fb53f 100644 --- a/trinity/Tr2RingBuffer.cpp +++ b/trinity/Tr2RingBuffer.cpp @@ -56,6 +56,10 @@ uint32_t Tr2RingBuffer::UploadTransforms( const T* data, uint32_t dataCount ) void Tr2RingBuffer::PrepareBuffer( Tr2RenderContext& renderContext ) { + if( m_gpuBufferStale ) + { + CreateGpuBuffer( renderContext.GetPrimaryRenderContext() ); + } for( auto& region : m_dirtyRegions ) { if( region.size ) @@ -102,10 +106,14 @@ void Tr2RingBuffer::Resize( uint32_t size ) m_head = m_size; m_size = size; m_tail = m_size; + m_gpuBufferStale = true; +} - USE_MAIN_THREAD_RENDER_CONTEXT(); - m_buffer.Create( m_stride, size, Tr2GpuUsage::SHADER_RESOURCE, Tr2CpuUsage::WRITE_OFTEN | Tr2CpuUsage::NON_SYNCRONIZED_WRITE, nullptr, renderContext ); +void Tr2RingBuffer::CreateGpuBuffer( Tr2PrimaryRenderContextAL& renderContext ) +{ + m_buffer.Create( m_stride, m_size, Tr2GpuUsage::SHADER_RESOURCE, Tr2CpuUsage::WRITE_OFTEN | Tr2CpuUsage::NON_SYNCRONIZED_WRITE, nullptr, renderContext ); m_buffer.SetName( m_name.c_str() ); + m_gpuBufferStale = false; } template @@ -120,6 +128,7 @@ Tr2RingBuffer& Tr2RingBuffer::GetInstance() self->m_stride = sizeof( T ); self->SetFrameNumbers( renderContext.GetRecordingFrameNumber(), renderContext.GetRenderedFrameNumber() ); self->Resize( INITIAL_SIZE ); + self->CreateGpuBuffer( renderContext ); } CCP_ASSERT_M( sizeof( T ) == self->m_stride, "Stride has to match size of datatype!" ); @@ -147,6 +156,7 @@ bool Tr2RingBuffer::OnPrepareResources() USE_MAIN_THREAD_RENDER_CONTEXT(); m_buffer.Create( m_stride, m_size, Tr2GpuUsage::SHADER_RESOURCE, Tr2CpuUsage::WRITE_OFTEN | Tr2CpuUsage::NON_SYNCRONIZED_WRITE, m_mirror.data(), renderContext ); m_buffer.SetName( m_name.c_str() ); + m_gpuBufferStale = false; } return true; } diff --git a/trinity/Tr2RingBuffer.h b/trinity/Tr2RingBuffer.h index 575663a08..b08c83543 100644 --- a/trinity/Tr2RingBuffer.h +++ b/trinity/Tr2RingBuffer.h @@ -48,13 +48,17 @@ BLUE_CLASS( Tr2RingBuffer ) : void ReleaseResources( TriStorage s ) override; bool OnPrepareResources() override; + // UploadTransforms may run on worker threads, so Resize only grows the mirror; + // the GPU buffer is recreated in PrepareBuffer on the main thread. void Resize( uint32_t size ); + void CreateGpuBuffer( Tr2PrimaryRenderContextAL & renderContext ); std::string m_name; uint32_t m_stride; std::mutex m_mutex; Tr2BufferAL m_buffer; + bool m_gpuBufferStale = false; std::vector m_mirror; uint64_t m_frame = 0; diff --git a/trinity/TriPoolAllocator.cpp b/trinity/TriPoolAllocator.cpp index d9809e71e..3be819af6 100644 --- a/trinity/TriPoolAllocator.cpp +++ b/trinity/TriPoolAllocator.cpp @@ -3,123 +3,170 @@ #include "StdAfx.h" #include "TriPoolAllocator.h" +namespace +{ +size_t RoundChunkSize( size_t size ) +{ + size >>= 8; + size += 1; + size <<= 8; + return size; +} +} + TriPoolAllocator::TriPoolAllocator() : - m_totalBytesAllocated( 0 ), m_totalSystemMemoryAllocated( 0 ), - m_pool( NULL ), - m_poolEnd( NULL ), - m_poolCurrent( NULL ), - m_chunkSize( 256 * 1024 ) + m_chunkSize( 256 * 1024 ), + m_chunk( nullptr ) { } +TriPoolAllocator::TriPoolAllocator( TriPoolAllocator&& other ) noexcept : + m_totalSystemMemoryAllocated( other.m_totalSystemMemoryAllocated ), + m_chunkSize( other.m_chunkSize ), + m_chunk( other.m_chunk.load() ), + m_chunks( std::move( other.m_chunks ) ) +{ + other.m_chunk = nullptr; + other.m_chunks.clear(); + other.m_totalSystemMemoryAllocated = 0; +} + +TriPoolAllocator& TriPoolAllocator::operator=( TriPoolAllocator&& other ) noexcept +{ + if( this != &other ) + { + for( Chunk* chunk : m_chunks ) + { + FreeChunk( chunk ); + } + m_totalSystemMemoryAllocated = other.m_totalSystemMemoryAllocated; + m_chunkSize = other.m_chunkSize; + m_chunk = other.m_chunk.load(); + m_chunks = std::move( other.m_chunks ); + other.m_chunk = nullptr; + other.m_chunks.clear(); + other.m_totalSystemMemoryAllocated = 0; + } + return *this; +} + TriPoolAllocator::~TriPoolAllocator() { - for( ChunkList_t::const_iterator it = m_previousPools.begin(); it != m_previousPools.end(); ++it ) + for( Chunk* chunk : m_chunks ) { - CCP_FREE( *it ); + FreeChunk( chunk ); } - CCP_FREE( m_pool ); - m_pool = m_poolCurrent = m_poolEnd = NULL; + m_chunks.clear(); + m_chunk = nullptr; } void* TriPoolAllocator::Allocate( size_t size ) { - // Align size to 16 bytes - pool starts out 16byte aligned - this ensures + // Align size to 16 bytes - chunks start out 16byte aligned - this ensures // that all allocations are always 16 byte aligned. size = CCP_ALIGN( size, 16 ); - if( m_poolCurrent + size > m_poolEnd ) + for( ;; ) { - GetMoreSystemMemory( size ); - if( m_poolCurrent + size > m_poolEnd ) + Chunk* chunk = m_chunk.load( std::memory_order_acquire ); + if( chunk ) + { + uint8_t* p = chunk->current.load( std::memory_order_relaxed ); + while( size_t( chunk->end - p ) >= size ) + { + if( chunk->current.compare_exchange_weak( p, p + size, std::memory_order_relaxed ) ) + { + return p; + } + } + } + + std::lock_guard lock( m_mutex ); + if( m_chunk.load( std::memory_order_relaxed ) == chunk && !AddChunk( size ) ) { return NULL; } } - - void* ret = m_poolCurrent; - - m_poolCurrent += size; - m_totalBytesAllocated += size; - - return ret; } void TriPoolAllocator::Clear() { - if( !m_previousPools.empty() ) + size_t totalBytesAllocated = 0; + for( Chunk* chunk : m_chunks ) { - for( ChunkList_t::const_iterator it = m_previousPools.begin(); it != m_previousPools.end(); ++it ) + totalBytesAllocated += UsedBytes( *chunk ); + } + + Chunk* last = m_chunks.empty() ? nullptr : m_chunks.back(); + for( Chunk* chunk : m_chunks ) + { + if( chunk != last ) { - // doing the explicit iterator cast allows you to debug this more easily - uint8_t* previousPool = *it; - CCP_FREE( previousPool ); + FreeChunk( chunk ); } - m_previousPools.clear(); } + m_chunks.clear(); + m_chunk = nullptr; + m_totalSystemMemoryAllocated = 0; - size_t curChunkSize = m_poolEnd - m_pool; - if( m_totalBytesAllocated < curChunkSize / 2 ) + if( !last ) + { + return; + } + if( totalBytesAllocated < last->size / 2 ) { // Pool is too large - free it and shrink the chunk size - CCP_FREE( m_pool ); - m_pool = m_poolCurrent = m_poolEnd = NULL; - m_chunkSize = curChunkSize / 2; - m_chunkSize >>= 8; - m_chunkSize += 1; - m_chunkSize <<= 8; - m_totalSystemMemoryAllocated = 0; + m_chunkSize = RoundChunkSize( last->size / 2 ); + FreeChunk( last ); } - else if( m_totalBytesAllocated > m_chunkSize ) + else if( totalBytesAllocated > m_chunkSize ) { // Pool is too small - free it and grow the chunk size - CCP_FREE( m_pool ); - m_pool = m_poolCurrent = m_poolEnd = NULL; - m_chunkSize = m_totalBytesAllocated; - m_chunkSize >>= 8; - m_chunkSize += 1; - m_chunkSize <<= 8; - m_totalSystemMemoryAllocated = 0; + m_chunkSize = RoundChunkSize( totalBytesAllocated ); + FreeChunk( last ); } else { // Pool seems to be of the right size. - // Align pool to 16 bytes - size of allocations are also aligned to - // 16 bytes - this ensures that all allocations are always 16 byte aligned. - m_poolCurrent = (uint8_t*)( CCP_ALIGN( (uintptr_t)m_pool, 16 ) ); - m_totalSystemMemoryAllocated = curChunkSize; + last->current = (uint8_t*)( CCP_ALIGN( (uintptr_t)last->memory, 16 ) ); + m_chunks.push_back( last ); + m_chunk = last; + m_totalSystemMemoryAllocated = last->size; } - - m_totalBytesAllocated = 0; } -void TriPoolAllocator::GetMoreSystemMemory( size_t size ) +TriPoolAllocator::Chunk* TriPoolAllocator::AddChunk( size_t size ) { - if( m_pool ) - { - m_previousPools.push_back( m_pool ); - } - size_t sizeToRequest = m_chunkSize; - while( sizeToRequest < size ) + while( sizeToRequest < size + 16 ) { sizeToRequest += m_chunkSize; } - // Align pool to 16 bytes - size of allocations are also aligned to - // 16 bytes - this ensures that all allocations are always 16 byte aligned. - m_pool = (uint8_t*)CCP_MALLOC( "TriPoolAllocator/chunk", sizeToRequest ); - - if( m_pool ) + uint8_t* memory = (uint8_t*)CCP_MALLOC( "TriPoolAllocator/chunk", sizeToRequest ); + if( !memory ) { - m_poolEnd = m_pool + sizeToRequest; - m_poolCurrent = (uint8_t*)( CCP_ALIGN( (uintptr_t)m_pool, 16 ) ); - - m_totalSystemMemoryAllocated += sizeToRequest; - } - else - { - m_poolCurrent = m_poolEnd = NULL; + return nullptr; } + Chunk* chunk = new Chunk; + chunk->memory = memory; + chunk->end = memory + sizeToRequest; + chunk->size = sizeToRequest; + chunk->current = (uint8_t*)( CCP_ALIGN( (uintptr_t)memory, 16 ) ); + m_chunks.push_back( chunk ); + m_totalSystemMemoryAllocated += sizeToRequest; + m_chunk.store( chunk, std::memory_order_release ); + return chunk; +} + +size_t TriPoolAllocator::UsedBytes( const Chunk& chunk ) +{ + return size_t( chunk.current.load( std::memory_order_relaxed ) - chunk.memory ); +} + +void TriPoolAllocator::FreeChunk( Chunk* chunk ) +{ + CCP_FREE( chunk->memory ); + delete chunk; } diff --git a/trinity/TriPoolAllocator.h b/trinity/TriPoolAllocator.h index 42b81069f..1489bb9b3 100644 --- a/trinity/TriPoolAllocator.h +++ b/trinity/TriPoolAllocator.h @@ -4,12 +4,19 @@ #ifndef TRIPOOLALLOCATOR_H #define TRIPOOLALLOCATOR_H +#include +#include +#include + // See http://core/wiki/TriPoolAllocator +// Allocate is safe to call from several threads at once; Clear is not. class TriPoolAllocator { public: TriPoolAllocator(); + TriPoolAllocator( TriPoolAllocator&& other ) noexcept; + TriPoolAllocator& operator=( TriPoolAllocator&& other ) noexcept; ~TriPoolAllocator(); // Allocates 'size' bytes, aligned to 16 bytes @@ -33,21 +40,27 @@ class TriPoolAllocator void Clear(); private: - void GetMoreSystemMemory( size_t size ); + struct Chunk + { + uint8_t* memory; + uint8_t* end; + size_t size; + std::atomic current; + }; + + Chunk* AddChunk( size_t size ); + static size_t UsedBytes( const Chunk& chunk ); + static void FreeChunk( Chunk* chunk ); private: - size_t m_totalBytesAllocated; size_t m_totalSystemMemoryAllocated; size_t m_chunkSize; - uint8_t* m_pool; - uint8_t* m_poolEnd; - uint8_t* m_poolCurrent; - - typedef std::list ChunkList_t; - ChunkList_t m_previousPools; + std::atomic m_chunk; + std::vector m_chunks; + std::mutex m_mutex; }; -#endif \ No newline at end of file +#endif