Skip to content

Instantly share code, notes, and snippets.

@jkoenen
Last active April 8, 2019 07:47
Show Gist options
  • Save jkoenen/76282a98334c2bb272d3520fb8fc62c2 to your computer and use it in GitHub Desktop.
Save jkoenen/76282a98334c2bb272d3520fb8fc62c2 to your computer and use it in GitHub Desktop.
Qt Memory Allocator View+Model
#include "game_memory_allocator_model.hpp"
#include "keen/gameconnectionserver/game_connection_memory_data.hpp"
#include "keen/qtbase/numbers.hpp"
namespace keen
{
GameMemoryAllocatorModel::GameMemoryAllocatorModel( const GameMemoryDataAllocators* pAllocators, QObject* pParent /*= nullptr */ )
: QAbstractItemModel( pParent )
{
KEEN_ASSERTE( pAllocators != nullptr );
m_pAllocators = pAllocators;
m_allocationMode = AllocationMode_ShowLeft;
}
GameMemoryAllocatorModel::~GameMemoryAllocatorModel()
{
}
int GameMemoryAllocatorModel::columnCount( const QModelIndex & parent /*= QModelIndex() */ ) const
{
if( parent.isValid() )
{
return 0;
}
return Colums_Count;
}
int GameMemoryAllocatorModel::rowCount( const QModelIndex& parent /*= QModelIndex() */ ) const
{
if( parent.isValid() )
{
return 0;
}
return (int)m_pAllocators->allocators.size();
}
QVariant GameMemoryAllocatorModel::data( const QModelIndex& index, int role /*= Qt::DisplayRole */ ) const
{
if( !index.isValid() )
{
return QVariant();
}
const GameMemoryDataAllocatorPair& allocatorPair = m_pAllocators->allocators[ index.row() ];
const GameMemoryDataAllocator& allocator0 = allocatorPair.data[ 0u ];
const GameMemoryDataAllocator& allocator1 = allocatorPair.data[ 1u ];
switch( role )
{
case Qt::DisplayRole:
switch( index.column() )
{
case Colums_NameLeft:
return ( allocatorPair.dataMask & 1u ? QString( allocatorPair.name ) : QString() );
case Colums_Allocated:
return getAllocationString( allocator0.allocatedSize, allocator1.allocatedSize, allocatorPair.dataMask );
case Colums_AllocatedMax:
return getAllocationString( allocator0.maxAllocatedSize, allocator1.maxAllocatedSize, allocatorPair.dataMask );
case Colums_Capacity:
return getAllocationString( allocator0.totalSize, allocator1.totalSize, allocatorPair.dataMask );
case Colums_Allocations:
return getAllocationString( allocator0.allocationCount, allocator1.allocationCount, allocatorPair.dataMask );
case Colums_AllocationsMax:
return getAllocationString( allocator0.maxAllocationCount, allocator1.maxAllocationCount, allocatorPair.dataMask );
case Colums_TotalAllocations:
return getAllocationString( allocator0.totalAllocationCount, allocator1.totalAllocationCount, allocatorPair.dataMask );
case Colums_NameRight:
return ( allocatorPair.dataMask & 2u ? QString( allocatorPair.name ) : QString() );
default:
break;
}
break;
case Qt::TextColorRole:
switch( index.column() )
{
case Colums_Allocated:
return getAllocationColor( allocator0.allocatedSize, allocator1.allocatedSize, allocatorPair.dataMask );
case Colums_AllocatedMax:
return getAllocationColor( allocator0.maxAllocatedSize, allocator1.maxAllocatedSize, allocatorPair.dataMask );
case Colums_Capacity:
return getAllocationColor( allocator0.totalSize, allocator1.totalSize, allocatorPair.dataMask );
case Colums_Allocations:
return getAllocationColor( allocator0.allocationCount, allocator1.allocationCount, allocatorPair.dataMask );
case Colums_AllocationsMax:
return getAllocationColor( allocator0.maxAllocationCount, allocator1.maxAllocationCount, allocatorPair.dataMask );
case Colums_TotalAllocations:
return getAllocationColor( allocator0.totalAllocationCount, allocator1.totalAllocationCount, allocatorPair.dataMask );
default:
break;
}
break;
case Qt::TextAlignmentRole:
return ( ( index.column() == Colums_NameLeft ) || ( index.column() == Colums_NameRight ) ) ? Qt::AlignLeft : Qt::AlignRight;
case UserRole_Sort:
switch( index.column() )
{
case Colums_NameLeft:
return ( allocatorPair.dataMask & 1u ? QString( allocatorPair.name ) : QString() );
case Colums_Allocated:
return -(qlonglong)getAllocationValue( allocator0.allocatedSize, allocator1.allocatedSize, allocatorPair.dataMask );
case Colums_AllocatedMax:
return -(qlonglong)getAllocationValue( allocator0.maxAllocatedSize, allocator1.maxAllocatedSize, allocatorPair.dataMask );
case Colums_Capacity:
return -(qlonglong)getAllocationValue( allocator0.totalSize, allocator1.totalSize, allocatorPair.dataMask );
case Colums_Allocations:
return -(qlonglong)getAllocationValue( allocator0.allocationCount, allocator1.allocationCount, allocatorPair.dataMask );
case Colums_AllocationsMax:
return -(qlonglong)getAllocationValue( allocator0.maxAllocationCount, allocator1.maxAllocationCount, allocatorPair.dataMask );
case Colums_TotalAllocations:
return -(qlonglong)getAllocationValue( allocator0.totalAllocationCount, allocator1.totalAllocationCount, allocatorPair.dataMask );
case Colums_NameRight:
return ( allocatorPair.dataMask & 2u ? QString( allocatorPair.name ) : QString() );
default:
break;
}
break;
case UserRole_AllocatorIndex:
return index.row();
default:
break;
}
return QVariant();
}
QVariant GameMemoryAllocatorModel::headerData( int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole */ ) const
{
static const char* s_columnNames[] =
{
"Left Name", "Allocated", "Max Allocated", "Capacity", "Allocations", "Max Allocations", "Total Allocations", "Right Name"
};
KEEN_COMPILE_TIME_ASSERT( KEEN_COUNTOF( s_columnNames ) == Colums_Count );
static const char* s_columnToolTips[] =
{
"Left allocator name", "Allocated bytes", "Maximum allocated bytes", "Allocator capacity in bytes", "Allocation count", "Maximum allocation count", "Total allocation count", "Right allocator name",
};
KEEN_COMPILE_TIME_ASSERT( KEEN_COUNTOF( s_columnToolTips ) == Colums_Count );
switch( role )
{
case Qt::DisplayRole:
return s_columnNames[ section ];
case Qt::ToolTipRole:
return s_columnToolTips[ section ];
case Qt::TextAlignmentRole:
return ( ( section == Colums_NameLeft ) || ( section == Colums_NameRight ) ) ? Qt::AlignLeft : Qt::AlignRight;
default:
break;
}
return QAbstractItemModel::headerData( section, orientation, role );
}
QModelIndex GameMemoryAllocatorModel::index( int row, int column, const QModelIndex& ) const
{
return createIndex( row, column );
}
QModelIndex GameMemoryAllocatorModel::parent( const QModelIndex& ) const
{
return QModelIndex();
}
void GameMemoryAllocatorModel::updateMemoryData()
{
beginResetModel();
endResetModel();
}
void GameMemoryAllocatorModel::setAllocationMode( AllocationMode allocationMode )
{
beginResetModel();
m_allocationMode = allocationMode;
endResetModel();
}
sint64 GameMemoryAllocatorModel::getAllocationValue( uint64 leftValue, uint64 rightValue, uint32 pairMask ) const
{
switch( m_allocationMode )
{
case AllocationMode_ShowLeft:
return pairMask & 1u ? (sint64)leftValue : (sint64)0;
case AllocationMode_ShowRight:
return pairMask & 2u ? (sint64)rightValue : (sint64)0;
case AllocationMode_ShowDifference:
if( pairMask == 3u )
{
return (sint64)rightValue - (sint64)leftValue;
}
else
{
return pairMask & 1u ? -(sint64)leftValue : (sint64)rightValue;
}
default:
return 0;
}
}
QString GameMemoryAllocatorModel::getAllocationString( uint64 leftValue, uint64 rightValue, uint32 pairMask ) const
{
switch( m_allocationMode )
{
case AllocationMode_ShowLeft:
return pairMask & 1u ? QtBase::getNumberStringUint( leftValue ) : QString();
case AllocationMode_ShowRight:
return pairMask & 2u ? QtBase::getNumberStringUint( rightValue ) : QString();
case AllocationMode_ShowDifference:
if( pairMask == 3u )
{
return QtBase::getNumberStringDeltaInt( (sint64)rightValue - (sint64)leftValue );
}
else
{
return pairMask & 1u ? QtBase::getNumberStringDeltaInt( -(sint64)leftValue ) : QtBase::getNumberStringDeltaInt( rightValue );
}
default:
return QString();
}
}
QColor GameMemoryAllocatorModel::getAllocationColor( uint64 leftValue, uint64 rightValue, uint32 pairMask ) const
{
if( m_allocationMode == AllocationMode_ShowDifference )
{
const sint64 value = getAllocationValue( leftValue, rightValue, pairMask );
if( value < 0 )
{
return QColor( Qt::red );
}
else if( value > 0 )
{
return QColor( Qt::darkGreen );
}
}
return QColor( Qt::black );
}
}
#ifndef KEEN_GAME_MEMORY_ALLOCATOR_MODEL_HPP
#define KEEN_GAME_MEMORY_ALLOCATOR_MODEL_HPP
#include "keen/base/types.hpp"
#include <QAbstractItemModel>
namespace keen
{
struct GameMemoryDataAllocators;
class GameMemoryAllocatorModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit GameMemoryAllocatorModel( const GameMemoryDataAllocators* pAllocators, QObject* pParent = nullptr );
virtual ~GameMemoryAllocatorModel();
virtual int columnCount( const QModelIndex & parent = QModelIndex() ) const;
virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const;
virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const;
virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
virtual QModelIndex index( int row, int column, const QModelIndex& parent = QModelIndex() ) const;
virtual QModelIndex parent( const QModelIndex& index ) const;
enum AllocationMode
{
AllocationMode_ShowLeft,
AllocationMode_ShowRight,
AllocationMode_ShowDifference
};
void updateMemoryData();
void setAllocationMode( AllocationMode allocationMode );
sint64 getAllocationValue( uint64 leftValue, uint64 rightValue, uint32 pairMask ) const;
QString getAllocationString( uint64 leftValue, uint64 rightValue, uint32 pairMask ) const;
QColor getAllocationColor( uint64 leftValue, uint64 rightValue, uint32 pairMask ) const;
enum UserRole
{
UserRole_Sort = Qt::UserRole,
UserRole_AllocatorIndex
};
enum Colums
{
Colums_NameLeft,
Colums_Allocated,
Colums_AllocatedMax,
Colums_Capacity,
Colums_Allocations,
Colums_AllocationsMax,
Colums_TotalAllocations,
Colums_NameRight,
Colums_Count
};
private:
const GameMemoryDataAllocators* m_pAllocators;
AllocationMode m_allocationMode;
};
}
#endif
#include "game_memory_allocator_view.hpp"
#include "game_memory_allocator_model.hpp"
#include "game_memory_data_object.hpp"
#include "keen/gameconnectionserver/game_connection_memory_data.hpp"
#include <QSortFilterProxyModel>
#include <QHeaderView>
namespace keen
{
GameMemoryAllocatorView::GameMemoryAllocatorView( GameMemoryDataObject* pGameMemoryDataObject, QWidget* pParent /*= nullptr */ )
: QTreeView( pParent )
{
KEEN_ASSERTE( pGameMemoryDataObject != nullptr );
setUniformRowHeights( true );
setRootIsDecorated( false );
setSelectionBehavior( QAbstractItemView::SelectRows );
setContextMenuPolicy( Qt::CustomContextMenu );
setSortingEnabled( true );
setAlternatingRowColors( true );
m_pModel = new GameMemoryAllocatorModel( &pGameMemoryDataObject->getGameMemoryData()->allocators );
QSortFilterProxyModel* pFilter = new QSortFilterProxyModel();
pFilter->setSortRole( GameMemoryAllocatorModel::UserRole_Sort );
pFilter->setSourceModel( m_pModel );
setModel( pFilter );
header()->setSectionResizeMode( QHeaderView::Interactive );
header()->setStretchLastSection( false );
setColumnWidth( GameMemoryAllocatorModel::Colums_NameLeft, 250 );
setColumnWidth( GameMemoryAllocatorModel::Colums_NameRight, 250 );
sortByColumn( GameMemoryAllocatorModel::Colums_NameLeft, Qt::AscendingOrder );
KEEN_VERIFY( connect( pGameMemoryDataObject, SIGNAL( allocatorsChanged() ), SLOT( handleDataChanged() ) ) );
}
GameMemoryAllocatorView::~GameMemoryAllocatorView()
{
}
void GameMemoryAllocatorView::setAllocationMode( int mode )
{
m_pModel->setAllocationMode( (GameMemoryAllocatorModel::AllocationMode)mode );
}
void GameMemoryAllocatorView::handleDataChanged()
{
m_pModel->updateMemoryData();
}
void GameMemoryAllocatorView::selectionChanged( const QItemSelection& selected, const QItemSelection& deselected )
{
QTreeView::selectionChanged( selected, deselected );
const QModelIndexList indexes = selected.indexes();
emit allocatorSelected( indexes.empty() ? -1 : model()->data( indexes[ 0 ], GameMemoryAllocatorModel::UserRole_AllocatorIndex ).toInt() );
}
}
#ifndef KEEN_GAME_MEMORY_ALLOCATOR_VIEW_HPP
#define KEEN_GAME_MEMORY_ALLOCATOR_VIEW_HPP
#include "keen/base/types.hpp"
#include <QTreeView>
namespace keen
{
class GameMemoryDataObject;
class GameMemoryAllocatorModel;
class GameMemoryAllocatorView : public QTreeView
{
Q_OBJECT
public:
explicit GameMemoryAllocatorView( GameMemoryDataObject* pGameMemoryDataObject, QWidget* pParent = nullptr );
virtual ~GameMemoryAllocatorView();
signals:
void allocatorSelected( int index );
public slots:
void setAllocationMode( int mode );
private slots:
void handleDataChanged();
protected:
virtual void selectionChanged( const QItemSelection& selected, const QItemSelection& deselected );
private:
GameMemoryAllocatorModel* m_pModel;
};
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment