From dad992fa12991b42e0662548fee1762e62cf637f Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Fri, 9 Mar 2018 15:48:18 +0100 Subject: [PATCH 1/3] Added VmaDumpVis tool. --- tools/VmaDumpVis/README.md | 34 +++ .../VmaDumpVis/README_files/ExampleOutput.png | Bin 0 -> 64489 bytes tools/VmaDumpVis/README_files/Legend_Bkg.png | Bin 0 -> 196 bytes .../VmaDumpVis/README_files/Legend_Buffer.png | Bin 0 -> 214 bytes .../README_files/Legend_Details.png | Bin 0 -> 194 bytes .../README_files/Legend_ImageLinear.png | Bin 0 -> 214 bytes .../README_files/Legend_ImageOptimal.png | Bin 0 -> 215 bytes tools/VmaDumpVis/VmaDumpVis.py | 243 ++++++++++++++++++ 8 files changed, 277 insertions(+) create mode 100644 tools/VmaDumpVis/README.md create mode 100644 tools/VmaDumpVis/README_files/ExampleOutput.png create mode 100644 tools/VmaDumpVis/README_files/Legend_Bkg.png create mode 100644 tools/VmaDumpVis/README_files/Legend_Buffer.png create mode 100644 tools/VmaDumpVis/README_files/Legend_Details.png create mode 100644 tools/VmaDumpVis/README_files/Legend_ImageLinear.png create mode 100644 tools/VmaDumpVis/README_files/Legend_ImageOptimal.png create mode 100644 tools/VmaDumpVis/VmaDumpVis.py diff --git a/tools/VmaDumpVis/README.md b/tools/VmaDumpVis/README.md new file mode 100644 index 0000000..c5a7537 --- /dev/null +++ b/tools/VmaDumpVis/README.md @@ -0,0 +1,34 @@ +# VMA Dump Vis + +Vulkan Memory Allocator Dump Visualization. It is an auxiliary tool that can visualize internal state of [Vulkan Memory Allocator](../README.md) library on a picture. It is a Python script that must be launched from command line with appropriate parameters. + +## Requirements + +- Python 3 installed +- [Pillow](http://python-pillow.org/) - Python Imaging Library (Fork) installed + +## Usage + +``` +python VmaDumpVis.py -o OUTPUT_FILE INPUT_FILE +``` + +* `INPUT_FILE` - path to source file to be read, containing dump of internal state of the VMA library in JSON format (encoding: UTF-8), generated using `vmaBuildStatsString()` function. +* `OUTPUT_FILE` - path to destination file to be written that will contain generated image. Image format is automatically recognized based on file extension. List of supported formats can be found [here](http://pillow.readthedocs.io/en/latest/handbook/image-file-formats.html) and includes: BMP, GIF, JPEG, PNG, TGA. + +You can also use typical options: + +* `-h` - to see help on command line syntax +* `-v` - to see program version number + +## Example output + +![Example output](README_files/ExampleOutput.png "Example output") + +## Legend + +* ![Free space](README_files/Legend_Bkg.png "Free space") Light gray without border - a space in Vulkan device memory block unused by any allocation. +* ![Buffer](README_files/Legend_Buffer.png "Buffer") Yellow rectangle - buffer. +* ![Image Optimal](README_files/Legend_ImageOptimal.png "Image Optimal") Aqua rectangle - image with TILING_OPTIMAL. +* ![Image Linear](README_files/Legend_ImageLinear.png "Image Linear") Green rectangle - image with TILING_LINEAR. +* ![Details](README_files/Legend_Details.png "Details") Black bar or rectangle - one or more allocations of any kind too small to be visualized as filled rectangles. diff --git a/tools/VmaDumpVis/README_files/ExampleOutput.png b/tools/VmaDumpVis/README_files/ExampleOutput.png new file mode 100644 index 0000000000000000000000000000000000000000..0aba87e65584242f5df52b77cec7deadc26bdf8c GIT binary patch literal 64489 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 literal 0 HcmV?d00001 diff --git a/tools/VmaDumpVis/README_files/Legend_Bkg.png b/tools/VmaDumpVis/README_files/Legend_Bkg.png new file mode 100644 index 0000000000000000000000000000000000000000..f8fd89f0623afee76ed639fea85a49c05496818d GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYYs>cdx@v7EBhlxaUNj?e&%Unfx)>bHL)Z$ zMWH;iBtya7(>EYRFO?lAuHot87~*j}c~9~4bMcdx@v7EBhlxaUKz-bv}}dfI_mFArU3c`MJ5Nc_lzD1A}u>YGO%h zib8p2Nrr;Er*A-tUMf3K+{)9%F~sBe+p8OS85DS!9h*0)_dN6A5><{^^h5qYc-0L% uuCPg4uJ!elM+ZLyo5aBIkKxtc`c>SjW$a)7vfeNNiF&&FxvXcdx@v7EBhlxaUNmr85hkD0)=EVLn2C?^K)}k^GbkR1_tM%)Wnk1 z6ovB4k_-iRPv3wPy;OFfxSFSnV~EG`w`Uz085DRJ7GC~0H=Xl)Gt-=`sFG+k0S*?X cMu!i_nctme)L+{&&jw_ar>mdKI;Vst0K)P$8UO$Q literal 0 HcmV?d00001 diff --git a/tools/VmaDumpVis/README_files/Legend_ImageLinear.png b/tools/VmaDumpVis/README_files/Legend_ImageLinear.png new file mode 100644 index 0000000000000000000000000000000000000000..4bd3672edc8a273bc07690c63f4dccc457b9a87f GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYYs>cdx@v7EBhlxaUNks=d6F?Kq1-8kcblJ{M_8syb>Unfx)>bHL)Z$ zMWH;iBtya7(>EYRFO?lAZsqCX7~*mK?bVIE3<^BVj?J6YCC~bBi7H1d`XPTHyy}J> uSJcdx@v7EBhlxaUNl#<5%{d1PaM!hD4M&=jZ08=9K`s3=Gaisfi`2 zDGKG8B^e6tp1uJoda3L{acfT(#}JR>$uS!e4%V|tF^RFUv9+;({AgkrllahyO^r>e sIgr~;S7K|zl_n^oAmRYi>c5$+49X?!rOYCa9)ryHboFyt=akR{0B2`9%m4rY literal 0 HcmV?d00001 diff --git a/tools/VmaDumpVis/VmaDumpVis.py b/tools/VmaDumpVis/VmaDumpVis.py new file mode 100644 index 0000000..5bf3d5b --- /dev/null +++ b/tools/VmaDumpVis/VmaDumpVis.py @@ -0,0 +1,243 @@ +# +# Copyright (c) 2018 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import argparse +import json +from PIL import Image, ImageDraw, ImageFont + + +PROGRAM_VERSION = 'VMA Dump Visualization 1.0.0' +IMG_SIZE_X = 800 +IMG_MARGIN = 8 +FONT_SIZE = 10 +MAP_SIZE = 24 +COLOR_TEXT_H1 = (0, 0, 0, 255) +COLOR_TEXT_H2 = (150, 150, 150, 255) +COLOR_OUTLINE = (160, 160, 160, 255) +COLOR_OUTLINE_HARD = (0, 0, 0, 255) +COLOR_GRID_LINE = (224, 224, 224, 255) + + +argParser = argparse.ArgumentParser(description='Visualization of Vulkan Memory Allocator JSON dump.') +argParser.add_argument('DumpFile', type=argparse.FileType(mode='r', encoding='UTF-8'), help='Path to source JSON file with memory dump created by Vulkan Memory Allocator library') +argParser.add_argument('-v', '--version', action='version', version=PROGRAM_VERSION) +argParser.add_argument('-o', '--output', required=True, help='Path to destination image file (e.g. PNG)') +args = argParser.parse_args() + +data = {} + + +def ProcessBlock(dstBlockList, objBlock): + iBlockSize = int(objBlock['TotalBytes']) + arrSuballocs = objBlock['Suballocations'] + dstBlockObj = {'Size':iBlockSize, 'Suballocations':[]} + dstBlockList.append(dstBlockObj) + for objSuballoc in arrSuballocs: + dstBlockObj['Suballocations'].append((objSuballoc['Type'], int(objSuballoc['Size']))) + + +def GetDataForMemoryType(iMemTypeIndex): + global data + if iMemTypeIndex in data: + return data[iMemTypeIndex] + else: + newMemTypeData = {'DedicatedAllocations':[], 'DefaultPoolBlocks':[], 'CustomPoolBlocks':[]} + data[iMemTypeIndex] = newMemTypeData + return newMemTypeData + + +# Returns tuple: +# [0] image height : integer +# [1] pixels per byte : float +def CalcParams(): + global data + iImgSizeY = IMG_MARGIN + iImgSizeY += FONT_SIZE + IMG_MARGIN # Grid lines legend - sizes + iMaxBlockSize = 0 + for dictMemType in data.values(): + iImgSizeY += IMG_MARGIN + FONT_SIZE + iImgSizeY += len(dictMemType['DedicatedAllocations']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE) + for tDedicatedAlloc in dictMemType['DedicatedAllocations']: + iMaxBlockSize = max(iMaxBlockSize, tDedicatedAlloc[1]) + iImgSizeY += len(dictMemType['DefaultPoolBlocks']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE) + for objBlock in dictMemType['DefaultPoolBlocks']: + iMaxBlockSize = max(iMaxBlockSize, objBlock['Size']) + iImgSizeY += len(dictMemType['CustomPoolBlocks']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE) + for objBlock in dictMemType['CustomPoolBlocks']: + iMaxBlockSize = max(iMaxBlockSize, objBlock['Size']) + fPixelsPerByte = (IMG_SIZE_X - IMG_MARGIN * 2) / float(iMaxBlockSize) + return iImgSizeY, fPixelsPerByte + + +def TypeToColor(sType): + if sType == 'FREE': + return 220, 220, 220, 255 + elif sType == 'BUFFER': + return 255, 255, 0, 255 + elif sType == 'IMAGE_OPTIMAL': + return 128, 255, 255, 255 + elif sType == 'IMAGE_LINEAR': + return 64, 255, 64, 255 + assert False + return 0, 0, 0, 255 + + +def DrawDedicatedAllocationBlock(draw, y, tDedicatedAlloc): + global fPixelsPerByte + iSizeBytes = tDedicatedAlloc[1] + iSizePixels = int(iSizeBytes * fPixelsPerByte) + draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + iSizePixels, y + MAP_SIZE], fill=TypeToColor(tDedicatedAlloc[0]), outline=COLOR_OUTLINE) + + +def DrawBlock(draw, y, objBlock): + global fPixelsPerByte + iSizeBytes = objBlock['Size'] + iSizePixels = int(iSizeBytes * fPixelsPerByte) + draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + iSizePixels, y + MAP_SIZE], fill=TypeToColor('FREE'), outline=None) + iByte = 0 + iX = 0 + iLastHardLineX = -1 + for tSuballoc in objBlock['Suballocations']: + sType = tSuballoc[0] + iByteEnd = iByte + tSuballoc[1] + iXEnd = int(iByteEnd * fPixelsPerByte) + if sType != 'FREE': + if iXEnd > iX + 1: + draw.rectangle([IMG_MARGIN + iX, y, IMG_MARGIN + iXEnd, y + MAP_SIZE], fill=TypeToColor(sType), outline=COLOR_OUTLINE) + # Hard line was been overwritten by rectangle outline: redraw it. + if iLastHardLineX == iX: + draw.line([IMG_MARGIN + iX, y, IMG_MARGIN + iX, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD) + else: + draw.line([IMG_MARGIN + iX, y, IMG_MARGIN + iX, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD) + iLastHardLineX = iX + iByte = iByteEnd + iX = iXEnd + + +def BytesToStr(iBytes): + if iBytes < 1024: + return "%d B" % iBytes + iBytes /= 1024 + if iBytes < 1024: + return "%d KiB" % iBytes + iBytes /= 1024 + if iBytes < 1024: + return "%d MiB" % iBytes + iBytes /= 1024 + return "%d GiB" % iBytes + + +jsonSrc = json.load(args.DumpFile) +if 'DedicatedAllocations' in jsonSrc: + for tType in jsonSrc['DedicatedAllocations'].items(): + sType = tType[0] + assert sType[:5] == 'Type ' + iType = int(sType[5:]) + typeData = GetDataForMemoryType(iType) + for objAlloc in tType[1]: + typeData['DedicatedAllocations'].append((objAlloc['Type'], int(objAlloc['Size']))) +if 'DefaultPools' in jsonSrc: + for tType in jsonSrc['DefaultPools'].items(): + sType = tType[0] + assert sType[:5] == 'Type ' + iType = int(sType[5:]) + typeData = GetDataForMemoryType(iType) + for objBlock in tType[1]['Blocks']: + ProcessBlock(typeData['DefaultPoolBlocks'], objBlock) +if 'Pools' in jsonSrc: + arrPools = jsonSrc['Pools'] + for objPool in arrPools: + iType = int(objPool['MemoryTypeIndex']) + typeData = GetDataForMemoryType(iType) + arrBlocks = objPool['Blocks'] + for objBlock in arrBlocks: + ProcessBlock(typeData['CustomPoolBlocks'], objBlock) + + +iImgSizeY, fPixelsPerByte = CalcParams() + +img = Image.new('RGB', (IMG_SIZE_X, iImgSizeY), 'white') +draw = ImageDraw.Draw(img) + +try: + font = ImageFont.truetype('segoeuib.ttf') +except: + font = ImageFont.load_default() + +y = IMG_MARGIN + +# Draw grid lines +iBytesBetweenGridLines = 32 +while iBytesBetweenGridLines * fPixelsPerByte < 64: + iBytesBetweenGridLines *= 2 +iByte = 0 +while True: + iX = int(iByte * fPixelsPerByte) + if iX > IMG_SIZE_X - 2 * IMG_MARGIN: + break + draw.line([iX + IMG_MARGIN, 0, iX + IMG_MARGIN, iImgSizeY], fill=COLOR_GRID_LINE) + if iX + 32 < IMG_SIZE_X - 2 * IMG_MARGIN: + draw.text((iX + IMG_MARGIN + FONT_SIZE/4, y), BytesToStr(iByte), fill=COLOR_TEXT_H2, font=font) + iByte += iBytesBetweenGridLines +y += FONT_SIZE + IMG_MARGIN + +# Draw main content +for iMemTypeIndex in sorted(data.keys()): + dictMemType = data[iMemTypeIndex] + draw.text((IMG_MARGIN, y), "Memory type %d" % iMemTypeIndex, fill=COLOR_TEXT_H1, font=font) + y += FONT_SIZE + IMG_MARGIN + index = 0 + for tDedicatedAlloc in dictMemType['DedicatedAllocations']: + draw.text((IMG_MARGIN, y), "Dedicated allocation %d" % index, fill=COLOR_TEXT_H2, font=font) + y += FONT_SIZE + IMG_MARGIN + DrawDedicatedAllocationBlock(draw, y, tDedicatedAlloc) + y += MAP_SIZE + IMG_MARGIN + index += 1 + index = 0 + for objBlock in dictMemType['DefaultPoolBlocks']: + draw.text((IMG_MARGIN, y), "Default pool block %d" % index, fill=COLOR_TEXT_H2, font=font) + y += FONT_SIZE + IMG_MARGIN + DrawBlock(draw, y, objBlock) + y += MAP_SIZE + IMG_MARGIN + index += 1 + index = 0 + for objBlock in dictMemType['CustomPoolBlocks']: + draw.text((IMG_MARGIN, y), "Custom pool block %d" % index, fill=COLOR_TEXT_H2, font=font) + y += FONT_SIZE + IMG_MARGIN + DrawBlock(draw, y, objBlock) + y += MAP_SIZE + IMG_MARGIN + index += 1 +del draw +img.save(args.output) + +""" +Main data structure - variable `data` - is a dictionary. Key is integer - memory type index. Value is dictionary of: +- Fixed key 'DedicatedAllocations'. Value is list of tuples, each containing: + - [0]: Type : string + - [1]: Size : integer +- Fixed key 'DefaultPoolBlocks'. Value is list of objects, each containing dictionary with: + - Fixed key 'Size'. Value is int. + - Fixed key 'Suballocations'. Value is list of tuples as above. +- Fixed key 'CustomPoolBlocks'. Value is list of objects, each containing dictionary with: + - Fixed key 'Size'. Value is int. + - Fixed key 'Suballocations'. Value is list of tuples as above. +""" From 3d9caf39f95138ec48c09aa2ff2244484e007af5 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Fri, 9 Mar 2018 15:55:57 +0100 Subject: [PATCH 2/3] README.md: Added link to VmaDumpVis. Fixed non-ANSI character to use UTF-8 not ASCII encoding. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9520a4f..eee94bc 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ Additional features: - Statistics: Obtain detailed statistics about the amount of memory used, unused, number of allocated blocks, number of allocations etc. - globally, per memory heap, and per memory type. - Debug annotations: Associate string with name or opaque pointer to your own data with every allocation. - JSON dump: Obtain a string in JSON format with detailed map of internal state, including list of allocations and gaps between them. +- Convert this JSON dump into a picture to visualize your memory. See [tools/VmaDumpVis](tools/VmaDumpVis/README.md). # Prequisites @@ -94,5 +95,5 @@ See **[Documentation](https://gpuopen-librariesandsdks.github.io/VulkanMemoryAll # See also - **[Awesome Vulkan](https://github.com/vinjn/awesome-vulkan)** - a curated list of awesome Vulkan libraries, debuggers and resources. -- **[PyVMA](https://github.com/realitix/pyvma)** - Python wrapper for this library. Author: Jean-Sébastien B. (@realitix). License: Apache 2.0. +- **[PyVMA](https://github.com/realitix/pyvma)** - Python wrapper for this library. Author: Jean-SĂ©bastien B. (@realitix). License: Apache 2.0. - **[vulkan-malloc](https://github.com/dylanede/vulkan-malloc)** - Vulkan memory allocation library for Rust. Based on version 1 of this library. Author: Dylan Ede (@dylanede). License: MIT / Apache 2.0. From 5ae219a98253e2153f4855484ec6dabc062bc284 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Fri, 9 Mar 2018 16:03:40 +0100 Subject: [PATCH 3/3] Updated version numer to 2.0.0 final in code and documentation. --- CHANGELOG.md | 8 +++----- src/vk_mem_alloc.h | 2 +- tools/VmaDumpVis/README.md | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec352cc..be4cb6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,13 @@ -# 2.0.0-alpha (2017-09-12) +# 2.0.0 (2018-03-19) A major release with many compatibility-breaking changes. -This code is work in progress and subject to changes. - Notable new features: - Introduction of `VmaAllocation` handle that you must retrieve from allocation functions and pass to deallocation functions next to normal `VkBuffer` and `VkImage`. - Introduction of `VmaAllocationInfo` structure that you can retrieve from `VmaAllocation` handle to access parameters of the allocation (like `VkDeviceMemory` and offset) instead of retrieving them directly from allocation functions. -- Support for persistently mapped allocations - see `VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT`. -- Support for custom memory pools - `VmaPool` handle, `VmaPoolCreateInfo` structure, `vmaCreatePool` function. +- Support for reference-counted mapping and persistently mapped allocations - see `vmaMapMemory`, `VMA_ALLOCATION_CREATE_MAPPED_BIT`. +- Support for custom memory pools - see `VmaPool` handle, `VmaPoolCreateInfo` structure, `vmaCreatePool` function. - Support for defragmentation (compaction) of allocations - see function `vmaDefragment` and related structures. - Support for "lost allocations" - see appropriate chapter on documentation Main Page. diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 21a3944..25b94ae 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -29,7 +29,7 @@ extern "C" { /** \mainpage Vulkan Memory Allocator -Version 2.0.0-alpha.8 (2018-03-05) +Version 2.0.0 (2018-03-19) Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n License: MIT diff --git a/tools/VmaDumpVis/README.md b/tools/VmaDumpVis/README.md index c5a7537..fee2392 100644 --- a/tools/VmaDumpVis/README.md +++ b/tools/VmaDumpVis/README.md @@ -1,6 +1,6 @@ # VMA Dump Vis -Vulkan Memory Allocator Dump Visualization. It is an auxiliary tool that can visualize internal state of [Vulkan Memory Allocator](../README.md) library on a picture. It is a Python script that must be launched from command line with appropriate parameters. +Vulkan Memory Allocator Dump Visualization. It is an auxiliary tool that can visualize internal state of [Vulkan Memory Allocator](../../README.md) library on a picture. It is a Python script that must be launched from command line with appropriate parameters. ## Requirements