mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator.git
synced 2024-11-21 19:50:05 +00:00
Fixes in JSON dump format. Fixes in GpuMemDumpVis.py regarding image height calculation.
Added GpuMemDump.schema.json.
This commit is contained in:
parent
2d7d710944
commit
19322e3194
@ -27,6 +27,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstdint>
|
||||||
#include <malloc.h> // for _aligned_malloc, _aligned_free
|
#include <malloc.h> // for _aligned_malloc, _aligned_free
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
@ -378,6 +379,14 @@ static T RoundDiv(T x, T y) { return (x + (y / (T)2)) / y; }
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
static T DivideRoundingUp(T x, T y) { return (x + y - 1) / y; }
|
static T DivideRoundingUp(T x, T y) { return (x + y - 1) / y; }
|
||||||
|
|
||||||
|
static WCHAR HexDigitToChar(UINT8 digit)
|
||||||
|
{
|
||||||
|
if(digit < 10)
|
||||||
|
return L'0' + digit;
|
||||||
|
else
|
||||||
|
return L'A' + (digit - 10);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Performs binary search and returns iterator to first element that is greater or
|
Performs binary search and returns iterator to first element that is greater or
|
||||||
equal to `key`, according to comparison `cmp`.
|
equal to `key`, according to comparison `cmp`.
|
||||||
@ -1180,6 +1189,7 @@ public:
|
|||||||
void AddNewLine() { Add(L'\n'); }
|
void AddNewLine() { Add(L'\n'); }
|
||||||
void AddNumber(UINT num);
|
void AddNumber(UINT num);
|
||||||
void AddNumber(UINT64 num);
|
void AddNumber(UINT64 num);
|
||||||
|
void AddPointer(const void* ptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector<WCHAR> m_Data;
|
Vector<WCHAR> m_Data;
|
||||||
@ -1224,6 +1234,22 @@ void StringBuilder::AddNumber(UINT64 num)
|
|||||||
while (num);
|
while (num);
|
||||||
Add(p);
|
Add(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StringBuilder::AddPointer(const void* ptr)
|
||||||
|
{
|
||||||
|
WCHAR buf[21];
|
||||||
|
uintptr_t num = (uintptr_t)ptr;
|
||||||
|
buf[20] = L'\0';
|
||||||
|
WCHAR *p = &buf[20];
|
||||||
|
do
|
||||||
|
{
|
||||||
|
*--p = HexDigitToChar((UINT8)(num & 0xF));
|
||||||
|
num >>= 4;
|
||||||
|
}
|
||||||
|
while (num);
|
||||||
|
Add(p);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // _D3D12MA_STRING_BUILDER_FUNCTIONS
|
#endif // _D3D12MA_STRING_BUILDER_FUNCTIONS
|
||||||
#endif // _D3D12MA_STRING_BUILDER
|
#endif // _D3D12MA_STRING_BUILDER
|
||||||
|
|
||||||
@ -1267,6 +1293,7 @@ public:
|
|||||||
// Posts next part of an open string. The number is converted to decimal characters.
|
// Posts next part of an open string. The number is converted to decimal characters.
|
||||||
void ContinueString(UINT num);
|
void ContinueString(UINT num);
|
||||||
void ContinueString(UINT64 num);
|
void ContinueString(UINT64 num);
|
||||||
|
void ContinueString_Pointer(const void* ptr);
|
||||||
// Posts next part of an open string. Pointer value is converted to characters
|
// Posts next part of an open string. Pointer value is converted to characters
|
||||||
// using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00
|
// using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00
|
||||||
// void ContinueString_Pointer(const void* ptr);
|
// void ContinueString_Pointer(const void* ptr);
|
||||||
@ -1452,6 +1479,12 @@ void JsonWriter::ContinueString(UINT64 num)
|
|||||||
m_SB.AddNumber(num);
|
m_SB.AddNumber(num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonWriter::ContinueString_Pointer(const void* ptr)
|
||||||
|
{
|
||||||
|
D3D12MA_ASSERT(m_InsideString);
|
||||||
|
m_SB.AddPointer(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
void JsonWriter::EndString(LPCWSTR pStr)
|
void JsonWriter::EndString(LPCWSTR pStr)
|
||||||
{
|
{
|
||||||
D3D12MA_ASSERT(m_InsideString);
|
D3D12MA_ASSERT(m_InsideString);
|
||||||
@ -1524,7 +1557,9 @@ void JsonWriter::AddAllocationToObject(const Allocation& alloc)
|
|||||||
if (privateData)
|
if (privateData)
|
||||||
{
|
{
|
||||||
WriteString(L"CustomData");
|
WriteString(L"CustomData");
|
||||||
WriteNumber((uintptr_t)privateData);
|
BeginString();
|
||||||
|
ContinueString_Pointer(privateData);
|
||||||
|
EndString();
|
||||||
}
|
}
|
||||||
|
|
||||||
LPCWSTR name = alloc.GetName();
|
LPCWSTR name = alloc.GetName();
|
||||||
@ -7196,9 +7231,6 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)
|
|||||||
json.WriteString(L"DEFAULT");
|
json.WriteString(L"DEFAULT");
|
||||||
json.BeginObject();
|
json.BeginObject();
|
||||||
{
|
{
|
||||||
json.WriteString(L"Flags");
|
|
||||||
json.BeginArray(true);
|
|
||||||
json.EndArray();
|
|
||||||
json.WriteString(L"Stats");
|
json.WriteString(L"Stats");
|
||||||
json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);
|
json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);
|
||||||
}
|
}
|
||||||
@ -7207,9 +7239,6 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)
|
|||||||
json.WriteString(L"UPLOAD");
|
json.WriteString(L"UPLOAD");
|
||||||
json.BeginObject();
|
json.BeginObject();
|
||||||
{
|
{
|
||||||
json.WriteString(L"Flags");
|
|
||||||
json.BeginArray(true);
|
|
||||||
json.EndArray();
|
|
||||||
json.WriteString(L"Stats");
|
json.WriteString(L"Stats");
|
||||||
json.AddDetailedStatisticsInfoObject(stats.HeapType[1]);
|
json.AddDetailedStatisticsInfoObject(stats.HeapType[1]);
|
||||||
}
|
}
|
||||||
@ -7218,9 +7247,6 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)
|
|||||||
json.WriteString(L"READBACK");
|
json.WriteString(L"READBACK");
|
||||||
json.BeginObject();
|
json.BeginObject();
|
||||||
{
|
{
|
||||||
json.WriteString(L"Flags");
|
|
||||||
json.BeginArray(true);
|
|
||||||
json.EndArray();
|
|
||||||
json.WriteString(L"Stats");
|
json.WriteString(L"Stats");
|
||||||
json.AddDetailedStatisticsInfoObject(stats.HeapType[2]);
|
json.AddDetailedStatisticsInfoObject(stats.HeapType[2]);
|
||||||
}
|
}
|
||||||
@ -7229,9 +7255,6 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)
|
|||||||
json.WriteString(L"CUSTOM");
|
json.WriteString(L"CUSTOM");
|
||||||
json.BeginObject();
|
json.BeginObject();
|
||||||
{
|
{
|
||||||
json.WriteString(L"Flags");
|
|
||||||
json.BeginArray(true);
|
|
||||||
json.EndArray();
|
|
||||||
json.WriteString(L"Stats");
|
json.WriteString(L"Stats");
|
||||||
json.AddDetailedStatisticsInfoObject(customHeaps[!IsUMA()]);
|
json.AddDetailedStatisticsInfoObject(customHeaps[!IsUMA()]);
|
||||||
}
|
}
|
||||||
@ -7245,13 +7268,6 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)
|
|||||||
json.WriteString(L"L1");
|
json.WriteString(L"L1");
|
||||||
json.BeginObject();
|
json.BeginObject();
|
||||||
{
|
{
|
||||||
json.WriteString(L"Flags");
|
|
||||||
json.BeginArray(true);
|
|
||||||
json.EndArray();
|
|
||||||
|
|
||||||
json.WriteString(L"Size");
|
|
||||||
json.WriteNumber(0U);
|
|
||||||
|
|
||||||
json.WriteString(L"Budget");
|
json.WriteString(L"Budget");
|
||||||
WriteBudgetToJson(json, localBudget);
|
WriteBudgetToJson(json, localBudget);
|
||||||
|
|
||||||
@ -7264,9 +7280,6 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)
|
|||||||
json.WriteString(L"DEFAULT");
|
json.WriteString(L"DEFAULT");
|
||||||
json.BeginObject();
|
json.BeginObject();
|
||||||
{
|
{
|
||||||
json.WriteString(L"Flags");
|
|
||||||
json.BeginArray(true);
|
|
||||||
json.EndArray();
|
|
||||||
json.WriteString(L"Stats");
|
json.WriteString(L"Stats");
|
||||||
json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);
|
json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);
|
||||||
}
|
}
|
||||||
@ -7275,9 +7288,6 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)
|
|||||||
json.WriteString(L"CUSTOM");
|
json.WriteString(L"CUSTOM");
|
||||||
json.BeginObject();
|
json.BeginObject();
|
||||||
{
|
{
|
||||||
json.WriteString(L"Flags");
|
|
||||||
json.BeginArray(true);
|
|
||||||
json.EndArray();
|
|
||||||
json.WriteString(L"Stats");
|
json.WriteString(L"Stats");
|
||||||
json.AddDetailedStatisticsInfoObject(customHeaps[0]);
|
json.AddDetailedStatisticsInfoObject(customHeaps[0]);
|
||||||
}
|
}
|
||||||
|
@ -322,10 +322,10 @@ static void ValidateAllocationsDataGPU(const TestContext& ctx, const ComPtr<D3D1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SaveStatsStringToFile(const TestContext& ctx, const wchar_t* dstFilePath)
|
static void SaveStatsStringToFile(const TestContext& ctx, const wchar_t* dstFilePath, BOOL detailed = TRUE)
|
||||||
{
|
{
|
||||||
WCHAR* s = nullptr;
|
WCHAR* s = nullptr;
|
||||||
ctx.allocator->BuildStatsString(&s, TRUE);
|
ctx.allocator->BuildStatsString(&s, detailed);
|
||||||
SaveFile(dstFilePath, s, wcslen(s) * sizeof(WCHAR));
|
SaveFile(dstFilePath, s, wcslen(s) * sizeof(WCHAR));
|
||||||
ctx.allocator->FreeStatsString(s);
|
ctx.allocator->FreeStatsString(s);
|
||||||
}
|
}
|
||||||
|
163
tools/GpuMemDumpVis/GpuMemDump.schema.json
Normal file
163
tools/GpuMemDumpVis/GpuMemDump.schema.json
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
{
|
||||||
|
"$id": "https://gpuopen.com/vulkan-memory-allocator/schemas/GpuMemDump",
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"General": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"API": {"type": "string", "enum": ["Vulkan", "Direct3D 12"]},
|
||||||
|
"GPU": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["API", "GPU"]
|
||||||
|
},
|
||||||
|
"Total": {"$ref": "#/$defs/Stats"},
|
||||||
|
"MemoryInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Flags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"}
|
||||||
|
},
|
||||||
|
"Size": {"type": "integer"},
|
||||||
|
"Budget": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"BudgetBytes": {"type": "integer"},
|
||||||
|
"UsageBytes": {"type": "integer"}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"Stats": {"$ref": "#/$defs/Stats"},
|
||||||
|
"MemoryPools": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Flags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"}
|
||||||
|
},
|
||||||
|
"Stats": {"$ref": "#/$defs/Stats"}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["Budget", "Stats"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DefaultPools": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"PreferredBlockSize": {"type": "integer"},
|
||||||
|
"Blocks": {
|
||||||
|
"type": "object",
|
||||||
|
"propertyNames": {"pattern": "[0-9]+"},
|
||||||
|
"additionalProperties": {"$ref": "#/$defs/Block"}
|
||||||
|
},
|
||||||
|
"DedicatedAllocations": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/DedicatedAllocation"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CustomPools": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Name": {"type": "string"},
|
||||||
|
"Flags": {"type": "array"},
|
||||||
|
"PreferredBlockSize": {"type": "integer"},
|
||||||
|
"Blocks": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {"$ref": "#/$defs/Block"}
|
||||||
|
},
|
||||||
|
"DedicatedAllocations": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/$defs/DedicatedAllocation"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["PreferredBlockSize"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["General", "Total", "MemoryInfo"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"$defs": {
|
||||||
|
"CustomData": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9a-zA-Z]+$"
|
||||||
|
},
|
||||||
|
"Stats": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"BlockCount": {"type": "integer"},
|
||||||
|
"BlockBytes": {"type": "integer"},
|
||||||
|
"AllocationCount": {"type": "integer"},
|
||||||
|
"AllocationBytes": {"type": "integer"},
|
||||||
|
"UnusedRangeCount": {"type": "integer"},
|
||||||
|
"AllocationSizeMin": {"type": "integer"},
|
||||||
|
"AllocationSizeMax": {"type": "integer"},
|
||||||
|
"UnusedRangeSizeMin": {"type": "integer"},
|
||||||
|
"UnusedRangeSizeMax": {"type": "integer"}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"BlockCount", "BlockBytes",
|
||||||
|
"AllocationCount", "AllocationBytes",
|
||||||
|
"UnusedRangeCount"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"Block": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"MapRefCount": {"type": "integer"},
|
||||||
|
"TotalBytes": {"type": "integer"},
|
||||||
|
"UnusedBytes": {"type": "integer"},
|
||||||
|
"Allocations": {"type": "integer"},
|
||||||
|
"UnusedRanges": {"type": "integer"},
|
||||||
|
"Suballocations": {"type": "array", "items": {"$ref": "#/$defs/Suballocation"}}
|
||||||
|
},
|
||||||
|
"required": ["TotalBytes", "UnusedBytes", "Allocations", "UnusedRanges"]
|
||||||
|
},
|
||||||
|
"DedicatedAllocation": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Type": {"type": "string"},
|
||||||
|
"Size": {"type": "integer"},
|
||||||
|
"Usage": {"type": "integer"},
|
||||||
|
"CustomData": {"$ref": "#/$defs/CustomData"},
|
||||||
|
"Name": {"type": "string"},
|
||||||
|
"Layout": {"type": "integer"}
|
||||||
|
},
|
||||||
|
"required": ["Type", "Size"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"Suballocation": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Offset": {"type": "integer"},
|
||||||
|
"Type": {"type": "string"},
|
||||||
|
"Size": {"type": "integer"},
|
||||||
|
"Usage": {"type": "integer"},
|
||||||
|
"CustomData": {"$ref": "#/$defs/CustomData"},
|
||||||
|
"Name": {"type": "string"},
|
||||||
|
"Layout": {"type": "integer"}
|
||||||
|
},
|
||||||
|
"required": ["Offset", "Type", "Size"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -106,9 +106,9 @@ def CalcParams():
|
|||||||
maxBlockSize = 0
|
maxBlockSize = 0
|
||||||
# Get height occupied by every memory pool
|
# Get height occupied by every memory pool
|
||||||
for poolData in data.values():
|
for poolData in data.values():
|
||||||
height += IMG_MARGIN + FONT_SIZE
|
height += FONT_SIZE + IMG_MARGIN # Memory pool title
|
||||||
height += len(poolData['DedicatedAllocations']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
|
height += len(poolData['Blocks']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
|
||||||
height += len(poolData['Blocks']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
|
height += len(poolData['DedicatedAllocations']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
|
||||||
# Get longest block size
|
# Get longest block size
|
||||||
for dedicatedAlloc in poolData['DedicatedAllocations']:
|
for dedicatedAlloc in poolData['DedicatedAllocations']:
|
||||||
maxBlockSize = max(maxBlockSize, dedicatedAlloc['Size'])
|
maxBlockSize = max(maxBlockSize, dedicatedAlloc['Size'])
|
||||||
@ -116,16 +116,15 @@ def CalcParams():
|
|||||||
maxBlockSize = max(maxBlockSize, block['Size'])
|
maxBlockSize = max(maxBlockSize, block['Size'])
|
||||||
# Same for custom pools
|
# Same for custom pools
|
||||||
for customPoolData in poolData['CustomPools'].values():
|
for customPoolData in poolData['CustomPools'].values():
|
||||||
height += len(customPoolData['DedicatedAllocations']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
|
height += len(customPoolData['Blocks']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
|
||||||
height += len(customPoolData['Blocks']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
|
height += len(customPoolData['DedicatedAllocations']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
|
||||||
height += FONT_SIZE * 2 + IMG_MARGIN if len(customPoolData['DedicatedAllocations']) == 0 else 0
|
|
||||||
# Get longest block size
|
# Get longest block size
|
||||||
for dedicatedAlloc in customPoolData['DedicatedAllocations']:
|
for dedicatedAlloc in customPoolData['DedicatedAllocations']:
|
||||||
maxBlockSize = max(maxBlockSize, dedicatedAlloc['Size'])
|
maxBlockSize = max(maxBlockSize, dedicatedAlloc['Size'])
|
||||||
for block in customPoolData['Blocks']:
|
for block in customPoolData['Blocks']:
|
||||||
maxBlockSize = max(maxBlockSize, block['Size'])
|
maxBlockSize = max(maxBlockSize, block['Size'])
|
||||||
|
|
||||||
return height + FONT_SIZE, (IMG_WIDTH - IMG_MARGIN * 2) / float(maxBlockSize)
|
return height, (IMG_WIDTH - IMG_MARGIN * 2) / float(maxBlockSize)
|
||||||
|
|
||||||
def BytesToStr(bytes):
|
def BytesToStr(bytes):
|
||||||
if bytes < 1024:
|
if bytes < 1024:
|
||||||
@ -306,7 +305,7 @@ if __name__ == '__main__':
|
|||||||
draw.text((IMG_MARGIN, y), "Custom pool %s block %s" % (poolName, block['ID']), fill=COLOR_TEXT_H2, font=font)
|
draw.text((IMG_MARGIN, y), "Custom pool %s block %s" % (poolName, block['ID']), fill=COLOR_TEXT_H2, font=font)
|
||||||
y += FONT_SIZE + IMG_MARGIN
|
y += FONT_SIZE + IMG_MARGIN
|
||||||
DrawBlock(draw, y, block, pixelsPerByte)
|
DrawBlock(draw, y, block, pixelsPerByte)
|
||||||
y += 2 * (FONT_SIZE + IMG_MARGIN)
|
y += MAP_SIZE + IMG_MARGIN
|
||||||
index = 0
|
index = 0
|
||||||
for dedicatedAlloc in pool['DedicatedAllocations']:
|
for dedicatedAlloc in pool['DedicatedAllocations']:
|
||||||
draw.text((IMG_MARGIN, y), "Custom pool %s dedicated allocation %d" % (poolName, index), fill=COLOR_TEXT_H2, font=font)
|
draw.text((IMG_MARGIN, y), "Custom pool %s dedicated allocation %d" % (poolName, index), fill=COLOR_TEXT_H2, font=font)
|
||||||
|
Loading…
Reference in New Issue
Block a user