• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
2# Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
3#
4# Permission is hereby granted, free of charge, to any person obtaining a copy
5# of this software and associated documentation files (the "Software"), to deal
6# in the Software without restriction, including without limitation the rights
7# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8# copies of the Software, and to permit persons to whom the Software is
9# furnished to do so, subject to the following conditions:
10#
11# The above copyright notice and this permission notice shall be included in
12# all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20# THE SOFTWARE.
21#
22
23import argparse
24import json
25from PIL import Image, ImageDraw, ImageFont
26
27
28PROGRAM_VERSION = 'D3D12MA Dump Visualization 1.0.0'
29IMG_SIZE_X = 1200
30IMG_MARGIN = 8
31FONT_SIZE = 10
32MAP_SIZE = 24
33COLOR_TEXT_H1 = (0, 0, 0, 255)
34COLOR_TEXT_H2 = (150, 150, 150, 255)
35COLOR_OUTLINE = (155, 155, 155, 255)
36COLOR_OUTLINE_HARD = (0, 0, 0, 255)
37COLOR_GRID_LINE = (224, 224, 224, 255)
38
39
40argParser = argparse.ArgumentParser(description='Visualization of D3D12 Memory Allocator JSON dump.')
41argParser.add_argument('DumpFile', type=argparse.FileType(mode='r', encoding='utf_16_le'), help='Path to source JSON file with memory dump created by D3D12 Memory Allocator library')
42argParser.add_argument('-v', '--version', action='version', version=PROGRAM_VERSION)
43argParser.add_argument('-o', '--output', required=True, help='Path to destination image file (e.g. PNG)')
44args = argParser.parse_args()
45
46data = {}
47
48
49def ProcessBlock(dstBlockList, iBlockId, objBlock, sAlgorithm):
50    iBlockSize = int(objBlock['TotalBytes'])
51    arrSuballocs = objBlock['Suballocations']
52    dstBlockObj = {'ID': iBlockId, 'Size':iBlockSize, 'Suballocations':[]}
53    dstBlockObj['Algorithm'] = sAlgorithm
54    for objSuballoc in arrSuballocs:
55        dstBlockObj['Suballocations'].append((objSuballoc['Type'], int(objSuballoc['Size']), int(objSuballoc.get('Flags', 0)), int(objSuballoc.get('Layout', 0))))
56    dstBlockList.append(dstBlockObj)
57
58
59def GetDataForHeapType(sHeapType):
60    global data
61    if sHeapType in data:
62        return data[sHeapType]
63    else:
64        newHeapTypeData = {'CommittedAllocations':[], 'DefaultPoolBlocks':[], 'CustomPools':{}}
65        data[sHeapType] = newHeapTypeData
66        return newHeapTypeData
67
68
69# Returns tuple:
70# [0] image height : integer
71# [1] pixels per byte : float
72def CalcParams():
73    global data
74    iImgSizeY = IMG_MARGIN
75    iImgSizeY += FONT_SIZE + IMG_MARGIN # Grid lines legend - sizes
76    iMaxBlockSize = 0
77    for dictMemType in data.values():
78        iImgSizeY += IMG_MARGIN + FONT_SIZE
79        lDedicatedAllocations = dictMemType['CommittedAllocations']
80        iImgSizeY += len(lDedicatedAllocations) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
81        for tDedicatedAlloc in lDedicatedAllocations:
82            iMaxBlockSize = max(iMaxBlockSize, tDedicatedAlloc[1])
83        lDefaultPoolBlocks = dictMemType['DefaultPoolBlocks']
84        iImgSizeY += len(lDefaultPoolBlocks) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
85        for objBlock in lDefaultPoolBlocks:
86            iMaxBlockSize = max(iMaxBlockSize, objBlock['Size'])
87        """
88        dCustomPools = dictMemType['CustomPools']
89        for lBlocks in dCustomPools.values():
90            iImgSizeY += len(lBlocks) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
91            for objBlock in lBlocks:
92                iMaxBlockSize = max(iMaxBlockSize, objBlock['Size'])
93        """
94    fPixelsPerByte = (IMG_SIZE_X - IMG_MARGIN * 2) / float(iMaxBlockSize)
95    return iImgSizeY, fPixelsPerByte
96
97
98def TypeToColor(sType, iFlags, iLayout):
99    if sType == 'FREE':
100        return 220, 220, 220, 255
101    elif sType == 'BUFFER':
102        return 255, 255, 0, 255 # Yellow
103    elif sType == 'TEXTURE2D' or sType == 'TEXTURE1D' or sType == 'TEXTURE3D':
104        if iLayout != 0: # D3D12_TEXTURE_LAYOUT_UNKNOWN
105            return 0, 255, 0, 255 # Green
106        else:
107            if (iFlags & 0x2) != 0: # D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL
108                return 246, 128, 255, 255 # Pink
109            elif (iFlags & 0x5) != 0: # D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS
110                return 179, 179, 255, 255 # Blue
111            elif (iFlags & 0x8) == 0: # Not having D3D12_RESOURCE_FLAG_DENY_SHARED_RESOURCE
112                return 0, 255, 255, 255 # Aqua
113            else:
114                return 183, 255, 255, 255 # Light aqua
115    else:
116        return 175, 175, 175, 255 # Gray
117    assert False
118    return 0, 0, 0, 255
119
120
121def DrawCommittedAllocationBlock(draw, y, tAlloc):
122    global fPixelsPerByte
123    iSizeBytes = tAlloc[1]
124    iSizePixels = int(iSizeBytes * fPixelsPerByte)
125    draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + iSizePixels, y + MAP_SIZE], fill=TypeToColor(tAlloc[0], tAlloc[2], tAlloc[3]), outline=COLOR_OUTLINE)
126
127
128def DrawBlock(draw, y, objBlock):
129    global fPixelsPerByte
130    iSizeBytes = objBlock['Size']
131    iSizePixels = int(iSizeBytes * fPixelsPerByte)
132    draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + iSizePixels, y + MAP_SIZE], fill=TypeToColor('FREE', 0, 0), outline=None)
133    iByte = 0
134    iX = 0
135    iLastHardLineX = -1
136    for tSuballoc in objBlock['Suballocations']:
137        sType = tSuballoc[0]
138        iByteEnd = iByte + tSuballoc[1]
139        iXEnd = int(iByteEnd * fPixelsPerByte)
140        if sType != 'FREE':
141            if iXEnd > iX + 1:
142                iFlags = tSuballoc[2]
143                iLayout = tSuballoc[3]
144                draw.rectangle([IMG_MARGIN + iX, y, IMG_MARGIN + iXEnd, y + MAP_SIZE], fill=TypeToColor(sType, iFlags, iLayout), outline=COLOR_OUTLINE)
145                # Hard line was been overwritten by rectangle outline: redraw it.
146                if iLastHardLineX == iX:
147                    draw.line([IMG_MARGIN + iX, y, IMG_MARGIN + iX, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD)
148            else:
149                draw.line([IMG_MARGIN + iX, y, IMG_MARGIN + iX, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD)
150                iLastHardLineX = iX
151        iByte = iByteEnd
152        iX = iXEnd
153
154
155def BytesToStr(iBytes):
156    if iBytes < 1024:
157        return "%d B" % iBytes
158    iBytes /= 1024
159    if iBytes < 1024:
160        return "%d KB" % iBytes
161    iBytes /= 1024
162    if iBytes < 1024:
163        return "%d MB" % iBytes
164    iBytes /= 1024
165    return "%d GB" % iBytes
166
167
168jsonSrc = json.load(args.DumpFile)
169objDetailedMap = jsonSrc['DetailedMap']
170if 'CommittedAllocations' in objDetailedMap:
171    for tType in objDetailedMap['CommittedAllocations'].items():
172        sHeapType = tType[0]
173        typeData = GetDataForHeapType(sHeapType)
174        for objAlloc in tType[1]:
175            typeData['CommittedAllocations'].append((objAlloc['Type'], int(objAlloc['Size']), int(objAlloc.get('Flags', 0)), int(objAlloc.get('Layout', 0))))
176if 'DefaultPools' in objDetailedMap:
177    for tType in objDetailedMap['DefaultPools'].items():
178        sHeapType = tType[0]
179        typeData = GetDataForHeapType(sHeapType)
180        for sBlockId, objBlock in tType[1]['Blocks'].items():
181            ProcessBlock(typeData['DefaultPoolBlocks'], int(sBlockId), objBlock, '')
182"""
183if 'Pools' in jsonSrc:
184    objPools = jsonSrc['Pools']
185    for sPoolId, objPool in objPools.items():
186        iType = int(objPool['MemoryTypeIndex'])
187        typeData = GetDataForHeapType(iType)
188        objBlocks = objPool['Blocks']
189        sAlgorithm = objPool.get('Algorithm', '')
190        sName = objPool.get('Name', None)
191        if sName:
192            sFullName = sPoolId + ' "' + sName + '"'
193        else:
194            sFullName = sPoolId
195        dstBlockArray = []
196        typeData['CustomPools'][sFullName] = dstBlockArray
197        for sBlockId, objBlock in objBlocks.items():
198            ProcessBlock(dstBlockArray, int(sBlockId), objBlock, sAlgorithm)
199"""
200
201iImgSizeY, fPixelsPerByte = CalcParams()
202
203img = Image.new('RGB', (IMG_SIZE_X, iImgSizeY), 'white')
204draw = ImageDraw.Draw(img)
205
206try:
207    font = ImageFont.truetype('segoeuib.ttf')
208except:
209    font = ImageFont.load_default()
210
211y = IMG_MARGIN
212
213# Draw grid lines
214iBytesBetweenGridLines = 32
215while iBytesBetweenGridLines * fPixelsPerByte < 64:
216    iBytesBetweenGridLines *= 2
217iByte = 0
218TEXT_MARGIN = 4
219while True:
220    iX = int(iByte * fPixelsPerByte)
221    if iX > IMG_SIZE_X - 2 * IMG_MARGIN:
222        break
223    draw.line([iX + IMG_MARGIN, 0, iX + IMG_MARGIN, iImgSizeY], fill=COLOR_GRID_LINE)
224    if iByte == 0:
225        draw.text((iX + IMG_MARGIN + TEXT_MARGIN, y), "0", fill=COLOR_TEXT_H2, font=font)
226    else:
227        text = BytesToStr(iByte)
228        textSize = draw.textsize(text, font=font)
229        draw.text((iX + IMG_MARGIN - textSize[0] - TEXT_MARGIN, y), text, fill=COLOR_TEXT_H2, font=font)
230    iByte += iBytesBetweenGridLines
231y += FONT_SIZE + IMG_MARGIN
232
233# Draw main content
234for sHeapType in data.keys():
235    dictMemType = data[sHeapType]
236    draw.text((IMG_MARGIN, y), sHeapType, fill=COLOR_TEXT_H1, font=font)
237    y += FONT_SIZE + IMG_MARGIN
238    index = 0
239    for tCommittedAlloc in dictMemType['CommittedAllocations']:
240        draw.text((IMG_MARGIN, y), "Committed allocation %d" % index, fill=COLOR_TEXT_H2, font=font)
241        y += FONT_SIZE + IMG_MARGIN
242        DrawCommittedAllocationBlock(draw, y, tCommittedAlloc)
243        y += MAP_SIZE + IMG_MARGIN
244        index += 1
245    for objBlock in dictMemType['DefaultPoolBlocks']:
246        draw.text((IMG_MARGIN, y), "Default pool block %d" % objBlock['ID'], fill=COLOR_TEXT_H2, font=font)
247        y += FONT_SIZE + IMG_MARGIN
248        DrawBlock(draw, y, objBlock)
249        y += MAP_SIZE + IMG_MARGIN
250    """
251    index = 0
252    for sPoolName, listPool in dictMemType['CustomPools'].items():
253        for objBlock in listPool:
254            if 'Algorithm' in objBlock and objBlock['Algorithm']:
255                sAlgorithm = ' (Algorithm: %s)' % (objBlock['Algorithm'])
256            else:
257                sAlgorithm = ''
258            draw.text((IMG_MARGIN, y), "Custom pool %s%s block %d" % (sPoolName, sAlgorithm, objBlock['ID']), fill=COLOR_TEXT_H2, font=font)
259            y += FONT_SIZE + IMG_MARGIN
260            DrawBlock(draw, y, objBlock)
261            y += MAP_SIZE + IMG_MARGIN
262            index += 1
263    """
264del draw
265img.save(args.output)
266
267"""
268Main data structure - variable `data` - is a dictionary. Key is string - heap type ('DEFAULT', 'UPLOAD', or 'READBACK'). Value is dictionary of:
269- Fixed key 'CommittedAllocations'. Value is list of tuples, each containing:
270    - [0]: Type : string
271    - [1]: Size : integer
272    - [2]: Flags : integer (0 if unknown)
273    - [3]: Layout : integer (0 if unknown)
274- Fixed key 'DefaultPoolBlocks'. Value is list of objects, each containing dictionary with:
275    - Fixed key 'ID'. Value is int.
276    - Fixed key 'Size'. Value is int.
277    - Fixed key 'Suballocations'. Value is list of tuples as above.
278X- Fixed key 'CustomPools'. Value is dictionary.
279  - Key is string with pool ID/name. Value is list of objects representing memory blocks, each containing dictionary with:
280    - Fixed key 'ID'. Value is int.
281    - Fixed key 'Size'. Value is int.
282    - Fixed key 'Algorithm'. Optional. Value is string.
283    - Fixed key 'Suballocations'. Value is list of tuples as above.
284"""
285