Files
UnrealEngineUWP/Engine/Source/Developer/MeshUtilitiesCommon/Private/Allocator2D.cpp
Chris Gagnon 80918bea22 Merging //UE4/Dev-Main to Dev-Editor (//UE4/Dev-Editor)
#rb none

[CL 5110714 by Chris Gagnon in Dev-Editor branch]
2019-02-21 13:05:30 -05:00

495 lines
9.7 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "Allocator2D.h"
FAllocator2D::FAllocator2D( uint32 InWidth, uint32 InHeight )
: Width( InWidth )
, Height( InHeight )
, LastRowFail( -1 )
{
Pitch = ( Width + 63 ) / 64;
Rows.Reserve( Height );
for ( uint32 Y = 0; Y < Height; ++Y )
{
FRow Row;
Row.Index = Y;
Rows.Add( Row );
}
Clear();
}
void FAllocator2D::Clear()
{
InitSegments();
Bits.Reset();
Bits.SetNumZeroed(Pitch * Height + 1); // alloc +1 to avoid buffer overrun
}
bool FAllocator2D::Find( FRect& Rect )
{
FRect TestRect = Rect;
for( TestRect.X = 0; TestRect.X <= Width - TestRect.W; TestRect.X++ )
{
for( TestRect.Y = 0; TestRect.Y <= Height - TestRect.H; TestRect.Y++ )
{
if( Test( TestRect ) )
{
Rect = TestRect;
return true;
}
}
}
return false;
}
bool FAllocator2D::FindBitByBit( FRect& Rect, const FAllocator2D& Other )
{
FRect TestRect = Rect;
for( TestRect.X = 0; TestRect.X <= Width - TestRect.W; TestRect.X++ )
{
for( TestRect.Y = 0; TestRect.Y <= Height - TestRect.H; TestRect.Y++ )
{
if( Test( TestRect, Other ) )
{
Rect = TestRect;
return true;
}
}
}
return false;
}
bool FAllocator2D::FindWithSegments( FRect& Rect, FRect BestRect, const FAllocator2D& Other )
{
LastRowFail = -1;
FRect TestRect = Rect;
for( TestRect.Y = 0; TestRect.Y <= Height - TestRect.H; ++TestRect.Y )
{
uint32 FailedLength = 1;
for( TestRect.X = 0; TestRect.X <= Width - TestRect.W; TestRect.X += FailedLength )
{
if( TestRect.X + TestRect.Y * Height >= BestRect.X + BestRect.Y * Height )
{
// We've moved past the BestRect
return false;
}
if( TestAllRows( TestRect, Other, FailedLength ) )
{
Rect = TestRect;
return true;
}
}
}
return false;
}
void FAllocator2D::Alloc( FRect Rect )
{
for( uint32 y = Rect.Y; y < Rect.Y + Rect.H; y++ )
{
for( uint32 x = Rect.X; x < Rect.X + Rect.W; x++ )
{
SetBit( x, y );
}
}
}
void FAllocator2D::Alloc( FRect Rect, const FAllocator2D& Other )
{
for( uint32 y = 0; y < Rect.H; y++ )
{
for( uint32 x = 0; x < Rect.W; x++ )
{
if( Other.GetBit( x, y ) )
{
SetBit( x + Rect.X, y + Rect.Y );
}
}
}
MergeSegments( Rect, Other );
}
bool FAllocator2D::TestAllRows( FRect Rect, const FAllocator2D& Other, uint32& FailedLength )
{
FailedLength = 0.f;
bool bSuccess = true;
if ( LastRowFail > 0 )
{
const FRow& ThisRow = Rows[ Rect.Y + LastRowFail ];
const FRow& OtherRow = Other.Rows[ LastRowFail ];
if ( !TestRow( ThisRow, OtherRow, Rect, FailedLength ) )
{
return false;
}
}
LastRowFail = -1;
for ( uint32 y = 0; y < Rect.H; ++y )
{
const FRow& ThisRow = Rows[ Rect.Y + y ];
const FRow& OtherRow = Other.Rows[ y ];
uint32 CurrentFailedLength = 0;
bool bThisRowSuccess = TestRow( ThisRow, OtherRow, Rect, CurrentFailedLength );
bSuccess = bThisRowSuccess && bSuccess;
if ( !bThisRowSuccess && CurrentFailedLength > FailedLength )
{
LastRowFail = y;
FailedLength = CurrentFailedLength;
}
if ( FailedLength >= Width )
{
return false;
}
}
return bSuccess;
}
bool FAllocator2D::TestRow( const FRow& ThisRow, const FRow& OtherRow, FRect Rect, uint32& FailedLength )
{
if ( ThisRow.LongestSegment < OtherRow.LongestSegment )
{
FailedLength = Width;
return false;
}
uint32 StartFreeSegmentIndex = 0;
for ( const FSegment& OtherUsedSegment : OtherRow.UsedSegments )
{
if ( OtherUsedSegment.StartPos >= Rect.W )
{
break;
}
bool bFoundSpaceForSegment = false;
bool bFoundFutureSpaceForSegment = false;
const uint32 StartPos = Rect.X + OtherUsedSegment.StartPos;
const uint32 EndPos = Rect.X + FMath::Min( OtherUsedSegment.StartPos + OtherUsedSegment.Length, Rect.W );
for ( int32 i = StartFreeSegmentIndex; i < ThisRow.FreeSegments.Num(); ++i )
{
const FSegment& ThisFreeSegment = ThisRow.FreeSegments[i];
if ( StartPos >= ThisFreeSegment.StartPos &&
EndPos <= ThisFreeSegment.StartPos + ThisFreeSegment.Length )
{
StartFreeSegmentIndex = i;
bFoundSpaceForSegment = true;
break;
}
else if ( !bFoundFutureSpaceForSegment &&
StartPos < ThisFreeSegment.StartPos &&
OtherUsedSegment.Length <= ThisFreeSegment.Length )
{
bFoundFutureSpaceForSegment = true;
FailedLength = ThisFreeSegment.StartPos - StartPos;
break;
}
}
if ( !bFoundSpaceForSegment )
{
if ( !bFoundFutureSpaceForSegment )
{
FailedLength = Width;
}
return false;
}
}
return true;
}
void FAllocator2D::FlipX( FRect Rect, ELightmapUVVersion LayoutVersion )
{
// If we have empty padding around the Rect, keep it there
uint32 MaxY = Rect.H - 1;
for ( int32 Y = Rect.H - 1; Y > 0; --Y )
{
if ( Rows[ Y ].UsedSegments.Num() > 0 )
{
break;
}
--MaxY;
}
uint32 MaxX = 0;
if ( LayoutVersion >= ELightmapUVVersion::Allocator2DFlipFix )
{
for ( uint32 Y = 0; Y <= MaxY; ++Y )
{
for ( const FSegment& UsedSegment : Rows[ Y ].UsedSegments )
{
MaxX = FMath::Max( MaxX, UsedSegment.StartPos + UsedSegment.Length - 1 );
}
}
}
else
{
MaxX = Rect.W - 1;
}
for ( uint32 Y = 0; Y <= MaxY; ++Y )
{
for ( uint32 LowX = 0; LowX < ( MaxX + 1 ) / 2; ++LowX )
{
uint32 HighX = MaxX - LowX;
const uint64 BitLow = GetBit( LowX, Y );
const uint64 BitHigh = GetBit( HighX, Y );
if ( BitLow != 0ull )
{
SetBit( HighX, Y );
}
else
{
ClearBit( HighX, Y );
}
if ( BitHigh != 0ull )
{
SetBit( LowX, Y );
}
else
{
ClearBit( LowX, Y );
}
}
}
CreateUsedSegments();
}
void FAllocator2D::FlipY( FRect Rect )
{
uint32 MinY = 0;
uint32 MaxY = Rect.H - 1;
for ( int32 Y = Rect.H - 1; Y > 0; --Y )
{
if ( Rows[ Y ].UsedSegments.Num() > 0 )
break;
--MaxY;
}
for ( uint32 LowY = 0; LowY < ( MaxY + 1 ) / 2; ++LowY )
{
for ( uint32 X = 0; X < Rect.W; ++X )
{
uint32 HighY = MaxY - LowY;
const uint64 BitLow = GetBit( X, LowY );
const uint64 BitHigh = GetBit( X, HighY );
if ( BitLow != 0ull )
{
SetBit( X, HighY );
}
else
{
ClearBit( X, HighY );
}
if ( BitHigh != 0ull )
{
SetBit( X, LowY );
}
else
{
ClearBit( X, LowY );
}
}
}
for ( uint32 LowY = MinY; LowY < ( MaxY + 1 ) / 2; ++LowY )
{
const int32 HighY = MaxY - LowY;
Rows.Swap( LowY, HighY );
Rows[ LowY ].Index = LowY;
Rows[ HighY ].Index = HighY;
}
}
void FAllocator2D::InitSegments()
{
FSegment FreeSegment;
FreeSegment.StartPos = 0;
FreeSegment.Length = Width;
for ( FRow& Row : Rows )
{
Row.FreeSegments.Empty( 1 );
Row.FreeSegments.Add( FreeSegment );
Row.LongestSegment = FreeSegment.Length;
Row.UsedSegments.Empty();
}
}
void FAllocator2D::CreateUsedSegments()
{
for ( uint32 y = 0; y < Height; ++y )
{
FRow& CurrentRow = Rows[y];
CurrentRow.LongestSegment = 0;
CurrentRow.UsedSegments.Empty();
int32 FirstUsedX = -1;
for ( uint32 k = 0; k < Pitch; ++k )
{
uint32 x = k * 64;
// If all bits are set
if ( Bits[k + y * Pitch] == ~0ull )
{
if ( FirstUsedX < 0 )
{
FirstUsedX = x;
}
if ( k == Pitch - 1 )
{
AddUsedSegment( CurrentRow, FirstUsedX, x + 64 - FirstUsedX );
FirstUsedX = -1;
}
}
// No bits are set
else if ( Bits[k + y * Pitch] == 0ull )
{
if ( FirstUsedX >= 0 )
{
AddUsedSegment( CurrentRow, FirstUsedX, x - FirstUsedX );
FirstUsedX = -1;
}
}
// Some bits are set
else
{
for ( uint32 i = 0; i < 64; ++i )
{
const uint32 SubX = x + i;
if ( GetBit(SubX, y) )
{
if ( FirstUsedX < 0 )
{
FirstUsedX = SubX;
}
if ( SubX == Width - 1 )
{
AddUsedSegment( CurrentRow, FirstUsedX, SubX + 1 - FirstUsedX );
FirstUsedX = -1;
}
}
else if ( FirstUsedX >= 0 )
{
AddUsedSegment( CurrentRow, FirstUsedX, SubX - FirstUsedX );
FirstUsedX = -1;
}
}
}
}
}
}
void FAllocator2D::AddUsedSegment( FRow& Row, uint32 StartPos, uint32 Length )
{
FSegment UsedSegment;
UsedSegment.StartPos = StartPos;
UsedSegment.Length = Length;
Row.UsedSegments.Add(UsedSegment);
if ( UsedSegment.Length > Row.LongestSegment )
{
Row.LongestSegment = UsedSegment.Length;
}
}
void FAllocator2D::MergeSegments( FRect Rect, const FAllocator2D& Other )
{
for ( uint32 y = 0; y < Rect.H; ++y )
{
FRow& ThisRow = Rows[Rect.Y + y];
const FRow& OtherRow = Other.Rows[y];
for ( const FSegment& OtherUsedSegment : OtherRow.UsedSegments )
{
for ( int32 i = 0; i < ThisRow.FreeSegments.Num(); ++i )
{
FSegment& ThisFreeSegment = ThisRow.FreeSegments[i];
uint32 StartPos = Rect.X + OtherUsedSegment.StartPos;
if ( StartPos >= ThisFreeSegment.StartPos &&
StartPos < ThisFreeSegment.StartPos + ThisFreeSegment.Length )
{
if ( ThisFreeSegment.Length == 1 )
{
ThisRow.FreeSegments.RemoveAtSwap(i);
break;
}
FSegment FirstSegment;
FirstSegment.StartPos = ThisFreeSegment.StartPos;
FirstSegment.Length = StartPos - ThisFreeSegment.StartPos;
uint32 EndPos = Rect.X + FMath::Min( OtherUsedSegment.StartPos + OtherUsedSegment.Length, Rect.W ) - 1;
FSegment SecondSegment;
SecondSegment.StartPos = EndPos + 1;
SecondSegment.Length = ThisFreeSegment.StartPos + ThisFreeSegment.Length - SecondSegment.StartPos;
ThisRow.FreeSegments.RemoveAtSwap(i);
if ( FirstSegment.Length > 0 )
{
ThisRow.FreeSegments.Add( FirstSegment );
}
if ( SecondSegment.Length > 0 )
{
ThisRow.FreeSegments.Add( SecondSegment );
}
break;
}
}
}
ThisRow.FreeSegments.Sort();
ThisRow.LongestSegment = 0;
for ( const FSegment& Segment : ThisRow.FreeSegments )
{
ThisRow.LongestSegment = FMath::Max( Segment.Length, ThisRow.LongestSegment );
}
}
}