GrMemoryPool always standard_layout for offsetof

GrMemoryPool in debug has extra fields and so wants to make use of
offsetof. The offsetof can only be taken on standard layout objects.
However, in debug std::unique_ptr may not be standard layout, which
makes SkAutoTArray not standard layout, which makes SkTHashTable not
standard layout, which makes SkTHashSet not standard layout, which makes
GrMemoryPool not standard layout, producing a compile error.

Since this is debug only, move the debug only fields behind a pointer
(which unfortunately cannot be a std::unique_ptr, since it may not be
standard layout). This allows building with recent libc++ headers and
clang.

Change-Id: Id9d312d7939808399d0796428de218d30263c26f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/482004
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
This commit is contained in:
Ben Wagner 2021-12-09 12:44:06 -05:00 committed by SkCQ
parent b05732e102
commit 15f186d277
2 changed files with 23 additions and 12 deletions

View File

@ -32,20 +32,24 @@ std::unique_ptr<GrMemoryPool> GrMemoryPool::Make(size_t preallocSize, size_t min
GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize)
: fAllocator(SkBlockAllocator::GrowthPolicy::kFixed, minAllocSize,
preallocSize - offsetof(GrMemoryPool, fAllocator) - sizeof(SkBlockAllocator)) {
SkDEBUGCODE(fAllocationCount = 0;)
SkDEBUGCODE(
fDebug = new Debug;
fDebug->fAllocationCount = 0;
)
}
GrMemoryPool::~GrMemoryPool() {
this->reportLeaks();
SkASSERT(0 == fAllocationCount);
SkASSERT(0 == fDebug->fAllocationCount);
SkASSERT(this->isEmpty());
SkDEBUGCODE(delete fDebug;)
}
void GrMemoryPool::reportLeaks() const {
#ifdef SK_DEBUG
int i = 0;
int n = fAllocatedIDs.count();
for (int id : fAllocatedIDs) {
int n = fDebug->fAllocatedIDs.count();
for (int id : fDebug->fAllocatedIDs) {
if (++i == 1) {
SkDebugf("Leaked %d IDs (in no particular order): %d%s", n, id, (n == i) ? "\n" : "");
} else if (i < 11) {
@ -85,8 +89,8 @@ void* GrMemoryPool::allocate(size_t size) {
}();
// You can set a breakpoint here when a leaked ID is allocated to see the stack frame.
fAllocatedIDs.add(header->fID);
fAllocationCount++;
fDebug->fAllocatedIDs.add(header->fID);
fDebug->fAllocationCount++;
#endif
// User-facing pointer is after the header padding
@ -104,8 +108,8 @@ void GrMemoryPool::release(void* p) {
#endif
#if defined(SK_DEBUG)
fAllocatedIDs.remove(header->fID);
fAllocationCount--;
fDebug->fAllocatedIDs.remove(header->fID);
fDebug->fAllocationCount--;
#endif
SkBlockAllocator::Block* block = fAllocator.owningBlock<kAlignment>(header, header->fStart);
@ -138,8 +142,8 @@ void GrMemoryPool::validate() const {
for (const auto* b : fAllocator.blocks()) {
allocCount += b->metadata();
}
SkASSERT(allocCount == fAllocationCount);
SkASSERT(fAllocationCount == fAllocatedIDs.count());
SkASSERT(allocCount == fDebug->fAllocationCount);
SkASSERT(fDebug->fAllocationCount == fDebug->fAllocatedIDs.count());
SkASSERT(allocCount > 0 || this->isEmpty());
}
#endif

View File

@ -88,6 +88,7 @@ public:
*/
size_t preallocSize() const {
// Account for the debug-only fields in this count, the offset is 0 for release builds
static_assert(std::is_standard_layout<GrMemoryPool>::value, "");
return offsetof(GrMemoryPool, fAllocator) + fAllocator.preallocSize();
}
@ -119,8 +120,14 @@ private:
GrMemoryPool(size_t preallocSize, size_t minAllocSize);
#ifdef SK_DEBUG
SkTHashSet<int> fAllocatedIDs;
int fAllocationCount;
// Because this exists preallocSize wants to use offsetof, so keep GrMemoryPool standard layout
// without depending on SkTHashSet being standard layout. Note that std::unique_ptr may not be
// standard layout.
struct Debug{
SkTHashSet<int> fAllocatedIDs;
int fAllocationCount;
};
Debug* fDebug{nullptr};
#endif
SkBlockAllocator fAllocator; // Must be the last field, in order to use extra allocated space