Merge branch 'v2_release'
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/README.md b/README.md
index 656f02b..e23c8e8 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,7 @@
 - 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
 
diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h
index 55ebdf6..b621fe7 100644
--- a/src/vk_mem_alloc.h
+++ b/src/vk_mem_alloc.h
@@ -29,7 +29,7 @@
 

 /** \mainpage Vulkan Memory Allocator

 

-<b>Version 2.0.0-alpha.8</b> (2018-03-05)

+<b>Version 2.0.0</b> (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
new file mode 100644
index 0000000..fee2392
--- /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 0000000..0aba87e
--- /dev/null
+++ b/tools/VmaDumpVis/README_files/ExampleOutput.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Bkg.png b/tools/VmaDumpVis/README_files/Legend_Bkg.png
new file mode 100644
index 0000000..f8fd89f
--- /dev/null
+++ b/tools/VmaDumpVis/README_files/Legend_Bkg.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Buffer.png b/tools/VmaDumpVis/README_files/Legend_Buffer.png
new file mode 100644
index 0000000..3acc825
--- /dev/null
+++ b/tools/VmaDumpVis/README_files/Legend_Buffer.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Details.png b/tools/VmaDumpVis/README_files/Legend_Details.png
new file mode 100644
index 0000000..450c7c7
--- /dev/null
+++ b/tools/VmaDumpVis/README_files/Legend_Details.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_ImageLinear.png b/tools/VmaDumpVis/README_files/Legend_ImageLinear.png
new file mode 100644
index 0000000..4bd3672
--- /dev/null
+++ b/tools/VmaDumpVis/README_files/Legend_ImageLinear.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_ImageOptimal.png b/tools/VmaDumpVis/README_files/Legend_ImageOptimal.png
new file mode 100644
index 0000000..8d74288
--- /dev/null
+++ b/tools/VmaDumpVis/README_files/Legend_ImageOptimal.png
Binary files differ
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.

+"""