blob: c37b4636e01398814a027fa3f5dc9b408c4bb92c [file] [log] [blame]
//
// 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.
//
#include "VmaUsage.h"
#include "Common.h"
#include <unordered_map>
static const int RESULT_EXCEPTION = -1000;
static const int RESULT_ERROR_COMMAND_LINE = -1;
static const int RESULT_ERROR_SOURCE_FILE = -2;
static const int RESULT_ERROR_FORMAT = -3;
static const int RESULT_ERROR_VULKAN = -4;
struct StrRange
{
const char* beg;
const char* end;
size_t length() const { return end - beg; }
};
static inline bool StrRangeEq(const StrRange& lhs, const char* rhsSz)
{
const size_t rhsLen = strlen(rhsSz);
return rhsLen == lhs.length() &&
memcmp(lhs.beg, rhsSz, rhsLen) == 0;
}
static inline bool StrRangeToUint(const StrRange& s, uint32_t& out)
{
char* end = (char*)s.end;
out = (uint32_t)strtoul(s.beg, &end, 10);
return end == s.end;
}
static inline bool StrRangeToUint(const StrRange& s, uint64_t& out)
{
char* end = (char*)s.end;
out = (uint64_t)strtoull(s.beg, &end, 10);
return end == s.end;
}
static inline bool StrRangeToPtr(const StrRange& s, uint64_t& out)
{
char* end = (char*)s.end;
out = (uint64_t)strtoull(s.beg, &end, 16);
return end == s.end;
}
////////////////////////////////////////////////////////////////////////////////
// LineSplit class
class LineSplit
{
public:
LineSplit(const char* data, size_t numBytes) :
m_Data(data),
m_NumBytes(numBytes),
m_NextLineBeg(0),
m_NextLineIndex(0)
{
}
bool GetNextLine(StrRange& out);
size_t GetNextLineIndex() const { return m_NextLineIndex; }
private:
const char* const m_Data;
const size_t m_NumBytes;
size_t m_NextLineBeg;
size_t m_NextLineIndex;
};
bool LineSplit::GetNextLine(StrRange& out)
{
if(m_NextLineBeg < m_NumBytes)
{
out.beg = m_Data + m_NextLineBeg;
size_t currLineEnd = m_NextLineBeg;
while(currLineEnd < m_NumBytes && m_Data[currLineEnd] != '\n')
++currLineEnd;
out.end = m_Data + currLineEnd;
m_NextLineBeg = currLineEnd + 1; // Past '\n'
++m_NextLineIndex;
return true;
}
else
return false;
}
////////////////////////////////////////////////////////////////////////////////
// CsvSplit class
class CsvSplit
{
public:
static const size_t RANGE_COUNT_MAX = 32;
void Set(const StrRange& line, size_t maxCount = RANGE_COUNT_MAX);
size_t GetCount() const { return m_Count; }
StrRange GetRange(size_t index) const
{
return StrRange {
m_Str + m_Ranges[index * 2],
m_Str + m_Ranges[index * 2 + 1] };
}
private:
const char* m_Str = nullptr;
size_t m_Count = 0;
size_t m_Ranges[RANGE_COUNT_MAX * 2]; // Pairs of begin-end.
};
void CsvSplit::Set(const StrRange& line, size_t maxCount)
{
assert(maxCount <= RANGE_COUNT_MAX);
m_Str = line.beg;
const size_t strLen = line.length();
size_t rangeIndex = 0;
size_t charIndex = 0;
while(charIndex < strLen && rangeIndex < maxCount)
{
m_Ranges[rangeIndex * 2] = charIndex;
while(charIndex < strLen && (rangeIndex + 1 == maxCount || m_Str[charIndex] != ','))
++charIndex;
m_Ranges[rangeIndex * 2 + 1] = charIndex;
++rangeIndex;
++charIndex; // Past ','
}
m_Count = rangeIndex;
}
////////////////////////////////////////////////////////////////////////////////
// class Statistics
class Statistics
{
public:
Statistics() { }
size_t GetImageCreationCount() const { return m_ImageCreationCount; }
size_t GetBufferCreationCount() const { return m_BufferCreationCount; }
size_t GetAllocationCreationCount() const { return m_AllocationCreationCount; }
void RegisterCreateImage();
void RegisterCreateBuffer();
private:
size_t m_ImageCreationCount = 0;
size_t m_BufferCreationCount = 0;
size_t m_AllocationCreationCount = 0; // Also includes buffers and images.
};
void Statistics::RegisterCreateImage()
{
++m_ImageCreationCount;
++m_AllocationCreationCount;
}
void Statistics::RegisterCreateBuffer()
{
++m_BufferCreationCount;
++m_AllocationCreationCount;
}
////////////////////////////////////////////////////////////////////////////////
// class Player
static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_LUNARG_standard_validation";
static bool g_MemoryAliasingWarningEnabled = true;
static bool g_EnableValidationLayer = true;
static bool VK_KHR_get_memory_requirements2_enabled = false;
static bool VK_KHR_dedicated_allocation_enabled = false;
static VKAPI_ATTR VkBool32 VKAPI_CALL MyDebugReportCallback(
VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object,
size_t location,
int32_t messageCode,
const char* pLayerPrefix,
const char* pMessage,
void* pUserData)
{
// "Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug."
if(!g_MemoryAliasingWarningEnabled && flags == VK_DEBUG_REPORT_WARNING_BIT_EXT &&
(strstr(pMessage, " is aliased with non-linear ") || strstr(pMessage, " is aliased with linear ")))
{
return VK_FALSE;
}
// Ignoring because when VK_KHR_dedicated_allocation extension is enabled,
// vkGetBufferMemoryRequirements2KHR function is used instead, while Validation
// Layer seems to be unaware of it.
if (strstr(pMessage, "but vkGetBufferMemoryRequirements() has not been called on that buffer") != nullptr)
{
return VK_FALSE;
}
if (strstr(pMessage, "but vkGetImageMemoryRequirements() has not been called on that image") != nullptr)
{
return VK_FALSE;
}
printf("%s \xBA %s\n", pLayerPrefix, pMessage);
return VK_FALSE;
}
static bool IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, const char* pLayerName)
{
const VkLayerProperties* propsEnd = pProps + propCount;
return std::find_if(
pProps,
propsEnd,
[pLayerName](const VkLayerProperties& prop) -> bool {
return strcmp(pLayerName, prop.layerName) == 0;
}) != propsEnd;
}
static const size_t FIRST_PARAM_INDEX = 4;
class Player
{
public:
Player();
int Init();
~Player();
void ExecuteLine(size_t lineNumber, const StrRange& line);
private:
static const size_t MAX_WARNINGS_TO_SHOW = 64;
size_t m_WarningCount = 0;
VkInstance m_VulkanInstance = VK_NULL_HANDLE;
VkPhysicalDevice m_PhysicalDevice = VK_NULL_HANDLE;
uint32_t m_GraphicsQueueFamilyIndex = UINT_MAX;
VkDevice m_Device = VK_NULL_HANDLE;
VmaAllocator m_Allocator = VK_NULL_HANDLE;
PFN_vkCreateDebugReportCallbackEXT m_pvkCreateDebugReportCallbackEXT;
PFN_vkDebugReportMessageEXT m_pvkDebugReportMessageEXT;
PFN_vkDestroyDebugReportCallbackEXT m_pvkDestroyDebugReportCallbackEXT;
VkDebugReportCallbackEXT m_hCallback;
uint32_t m_VmaFrameIndex = 0;
// Any of these handles null can mean it was created in original but couldn't be created now.
struct Pool
{
VmaPool pool;
};
struct Allocation
{
VmaAllocation allocation;
VkBuffer buffer;
VkImage image;
};
std::unordered_map<uint64_t, Pool> m_Pools;
std::unordered_map<uint64_t, Allocation> m_Allocations;
Statistics m_Stats;
void Destroy(const Allocation& alloc);
// Increments warning counter. Returns true if warning message should be printed.
bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; }
int InitVulkan();
void FinalizeVulkan();
void RegisterDebugCallbacks();
void PrintStats();
// If parmeter count doesn't match, issues warning and returns false.
bool ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound);
void ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit);
void ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit);
void ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit);
void ExecuteDestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit) { DestroyAllocation(lineNumber, csvSplit); }
void ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit);
void ExecuteDestroyImage(size_t lineNumber, const CsvSplit& csvSplit) { DestroyAllocation(lineNumber, csvSplit); }
void ExecuteFreeMemory(size_t lineNumber, const CsvSplit& csvSplit) { DestroyAllocation(lineNumber, csvSplit); }
void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit);
};
Player::Player()
{
}
int Player::Init()
{
return InitVulkan();
}
Player::~Player()
{
PrintStats();
FinalizeVulkan();
if(m_WarningCount > MAX_WARNINGS_TO_SHOW)
printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW);
}
void Player::ExecuteLine(size_t lineNumber, const StrRange& line)
{
CsvSplit csvSplit;
csvSplit.Set(line);
if(csvSplit.GetCount() >= FIRST_PARAM_INDEX)
{
// Update VMA current frame index.
StrRange frameIndexStr = csvSplit.GetRange(2);
uint32_t frameIndex;
if(StrRangeToUint(frameIndexStr, frameIndex))
{
if(frameIndex != m_VmaFrameIndex)
{
vmaSetCurrentFrameIndex(m_Allocator, frameIndex);
m_VmaFrameIndex = frameIndex;
}
}
else
{
if(IssueWarning())
printf("Line %zu: Incorrect frame index.\n", lineNumber);
}
StrRange functionName = csvSplit.GetRange(3);
if(StrRangeEq(functionName, "vmaCreateAllocator"))
{
if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false))
{
// Nothing to do.
}
}
else if(StrRangeEq(functionName, "vmaDestroyAllocator"))
{
if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false))
{
// Nothing to do.
}
}
else if(StrRangeEq(functionName, "vmaCreatePool"))
ExecuteCreatePool(lineNumber, csvSplit);
else if(StrRangeEq(functionName, "vmaDestroyPool"))
ExecuteDestroyPool(lineNumber, csvSplit);
else if(StrRangeEq(functionName, "vmaSetAllocationUserData"))
{
if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true))
{
// Ignore for now.
}
}
else if(StrRangeEq(functionName, "vmaCreateBuffer"))
ExecuteCreateBuffer(lineNumber, csvSplit);
else if(StrRangeEq(functionName, "vmaDestroyBuffer"))
ExecuteDestroyBuffer(lineNumber, csvSplit);
else if(StrRangeEq(functionName, "vmaCreateImage"))
ExecuteCreateImage(lineNumber, csvSplit);
else if(StrRangeEq(functionName, "vmaDestroyImage"))
ExecuteDestroyImage(lineNumber, csvSplit);
else if(StrRangeEq(functionName, "vmaFreeMemory"))
ExecuteFreeMemory(lineNumber, csvSplit);
else
{
if(IssueWarning())
printf("Line %zu: Unknown function.\n", lineNumber);
}
}
else
{
if(IssueWarning())
printf("Line %zu: Too few columns.\n", lineNumber);
}
}
void Player::Destroy(const Allocation& alloc)
{
if(alloc.buffer)
{
assert(alloc.image == VK_NULL_HANDLE);
vmaDestroyBuffer(m_Allocator, alloc.buffer, alloc.allocation);
}
else if(alloc.image)
{
vmaDestroyImage(m_Allocator, alloc.image, alloc.allocation);
}
else
vmaFreeMemory(m_Allocator, alloc.allocation);
}
int Player::InitVulkan()
{
printf("Initializing Vulkan...\n");
uint32_t instanceLayerPropCount = 0;
VkResult res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr);
assert(res == VK_SUCCESS);
std::vector<VkLayerProperties> instanceLayerProps(instanceLayerPropCount);
if(instanceLayerPropCount > 0)
{
res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data());
assert(res == VK_SUCCESS);
}
if(g_EnableValidationLayer == true)
{
if(IsLayerSupported(instanceLayerProps.data(), instanceLayerProps.size(), VALIDATION_LAYER_NAME) == false)
{
printf("WARNING: Layer \"%s\" not supported.\n", VALIDATION_LAYER_NAME);
g_EnableValidationLayer = false;
}
}
std::vector<const char*> instanceExtensions;
//instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
//instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
std::vector<const char*> instanceLayers;
if(g_EnableValidationLayer)
{
instanceLayers.push_back(VALIDATION_LAYER_NAME);
instanceExtensions.push_back("VK_EXT_debug_report");
}
VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
appInfo.pApplicationName = "VmaReplay";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "Vulkan Memory Allocator";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo instInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
instInfo.pApplicationInfo = &appInfo;
instInfo.enabledExtensionCount = (uint32_t)instanceExtensions.size();
instInfo.ppEnabledExtensionNames = instanceExtensions.data();
instInfo.enabledLayerCount = (uint32_t)instanceLayers.size();
instInfo.ppEnabledLayerNames = instanceLayers.data();
res = vkCreateInstance(&instInfo, NULL, &m_VulkanInstance);
if(res != VK_SUCCESS)
{
printf("ERROR: vkCreateInstance failed (%u)\n", res);
return RESULT_ERROR_VULKAN;
}
if(g_EnableValidationLayer)
RegisterDebugCallbacks();
// Find physical device
uint32_t deviceCount = 0;
res = vkEnumeratePhysicalDevices(m_VulkanInstance, &deviceCount, nullptr);
assert(res == VK_SUCCESS);
if(deviceCount == 0)
{
printf("ERROR: No Vulkan physical devices found.\n");
return RESULT_ERROR_VULKAN;
}
else if(deviceCount > 1)
printf("WARNING: %u Vulkan physical devices found. Choosing first one.\n", deviceCount);
std::vector<VkPhysicalDevice> physicalDevices(deviceCount);
res = vkEnumeratePhysicalDevices(m_VulkanInstance, &deviceCount, physicalDevices.data());
assert(res == VK_SUCCESS);
m_PhysicalDevice = physicalDevices[0];
// Find queue family index
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, nullptr);
if(queueFamilyCount)
{
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, queueFamilies.data());
for(uint32_t i = 0; i < queueFamilyCount; ++i)
{
if(queueFamilies[i].queueCount > 0 &&
(queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
{
m_GraphicsQueueFamilyIndex = i;
break;
}
}
}
if(m_GraphicsQueueFamilyIndex == UINT_MAX)
{
printf("ERROR: Couldn't find graphics queue.\n");
return RESULT_ERROR_VULKAN;
}
// Create logical device
const float queuePriority = 1.f;
VkDeviceQueueCreateInfo deviceQueueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
deviceQueueCreateInfo.queueFamilyIndex = m_GraphicsQueueFamilyIndex;
deviceQueueCreateInfo.queueCount = 1;
deviceQueueCreateInfo.pQueuePriorities = &queuePriority;
// Enable something what may interact with memory/buffer/image support.
VkPhysicalDeviceFeatures deviceFeatures = {};
deviceFeatures.fullDrawIndexUint32 = VK_TRUE;
deviceFeatures.imageCubeArray = VK_TRUE;
deviceFeatures.geometryShader = VK_TRUE;
deviceFeatures.tessellationShader = VK_TRUE;
deviceFeatures.multiDrawIndirect = VK_TRUE;
deviceFeatures.textureCompressionBC = VK_TRUE;
// Determine list of device extensions to enable.
std::vector<const char*> enabledDeviceExtensions;
//enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
{
uint32_t propertyCount = 0;
res = vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, nullptr);
assert(res == VK_SUCCESS);
if(propertyCount)
{
std::vector<VkExtensionProperties> properties{propertyCount};
res = vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, properties.data());
assert(res == VK_SUCCESS);
for(uint32_t i = 0; i < propertyCount; ++i)
{
if(strcmp(properties[i].extensionName, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME) == 0)
{
enabledDeviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
VK_KHR_get_memory_requirements2_enabled = true;
}
else if(strcmp(properties[i].extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME) == 0)
{
enabledDeviceExtensions.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
VK_KHR_dedicated_allocation_enabled = true;
}
}
}
}
VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size();
deviceCreateInfo.ppEnabledExtensionNames = !enabledDeviceExtensions.empty() ? enabledDeviceExtensions.data() : nullptr;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pQueueCreateInfos = &deviceQueueCreateInfo;
deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
res = vkCreateDevice(m_PhysicalDevice, &deviceCreateInfo, nullptr, &m_Device);
if(res != VK_SUCCESS)
{
printf("ERROR: vkCreateDevice failed (%u)\n", res);
return RESULT_ERROR_VULKAN;
}
// Create memory allocator
VmaAllocatorCreateInfo allocatorInfo = {};
allocatorInfo.physicalDevice = m_PhysicalDevice;
allocatorInfo.device = m_Device;
if(VK_KHR_dedicated_allocation_enabled)
{
allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
}
res = vmaCreateAllocator(&allocatorInfo, &m_Allocator);
if(res != VK_SUCCESS)
{
printf("ERROR: vmaCreateAllocator failed (%u)\n", res);
return RESULT_ERROR_VULKAN;
}
return 0;
}
void Player::FinalizeVulkan()
{
if(!m_Allocations.empty())
{
printf("WARNING: Allocations not destroyed: %zu.\n", m_Allocations.size());
for(const auto it : m_Allocations)
{
Destroy(it.second);
}
m_Allocations.clear();
}
if(!m_Pools.empty())
{
printf("WARNING: Pools not destroyed: %zu.\n", m_Pools.size());
for(const auto it : m_Pools)
vmaDestroyPool(m_Allocator, it.second.pool);
m_Pools.clear();
}
vkDeviceWaitIdle(m_Device);
if(m_Allocator != VK_NULL_HANDLE)
{
vmaDestroyAllocator(m_Allocator);
m_Allocator = nullptr;
}
if(m_Device != VK_NULL_HANDLE)
{
vkDestroyDevice(m_Device, nullptr);
m_Device = nullptr;
}
if(m_pvkDestroyDebugReportCallbackEXT && m_hCallback != VK_NULL_HANDLE)
{
m_pvkDestroyDebugReportCallbackEXT(m_VulkanInstance, m_hCallback, nullptr);
m_hCallback = VK_NULL_HANDLE;
}
if(m_VulkanInstance != VK_NULL_HANDLE)
{
vkDestroyInstance(m_VulkanInstance, NULL);
m_VulkanInstance = VK_NULL_HANDLE;
}
}
void Player::RegisterDebugCallbacks()
{
m_pvkCreateDebugReportCallbackEXT =
reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>
(vkGetInstanceProcAddr(m_VulkanInstance, "vkCreateDebugReportCallbackEXT"));
m_pvkDebugReportMessageEXT =
reinterpret_cast<PFN_vkDebugReportMessageEXT>
(vkGetInstanceProcAddr(m_VulkanInstance, "vkDebugReportMessageEXT"));
m_pvkDestroyDebugReportCallbackEXT =
reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>
(vkGetInstanceProcAddr(m_VulkanInstance, "vkDestroyDebugReportCallbackEXT"));
assert(m_pvkCreateDebugReportCallbackEXT);
assert(m_pvkDebugReportMessageEXT);
assert(m_pvkDestroyDebugReportCallbackEXT);
VkDebugReportCallbackCreateInfoEXT callbackCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT };
callbackCreateInfo.flags = //VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
VK_DEBUG_REPORT_ERROR_BIT_EXT |
VK_DEBUG_REPORT_WARNING_BIT_EXT |
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT /*|
VK_DEBUG_REPORT_DEBUG_BIT_EXT*/;
callbackCreateInfo.pfnCallback = &MyDebugReportCallback;
VkResult res = m_pvkCreateDebugReportCallbackEXT(m_VulkanInstance, &callbackCreateInfo, nullptr, &m_hCallback);
assert(res == VK_SUCCESS);
}
void Player::PrintStats()
{
printf("Statistics:\n");
printf(" Total allocations created: %zu\n", m_Stats.GetAllocationCreationCount());
printf(" Total buffers created: %zu\n", m_Stats.GetBufferCreationCount());
printf(" Total images created: %zu\n", m_Stats.GetImageCreationCount());
}
bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound)
{
bool ok;
if(lastUnbound)
ok = csvSplit.GetCount() >= FIRST_PARAM_INDEX + expectedParamCount - 1;
else
ok = csvSplit.GetCount() == FIRST_PARAM_INDEX + expectedParamCount;
if(!ok)
{
if(IssueWarning())
printf("Line %zu: Incorrect number of function parameters.\n", lineNumber);
}
return ok;
}
void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit)
{
if(ValidateFunctionParameterCount(lineNumber, csvSplit, 7, false))
{
VmaPoolCreateInfo poolCreateInfo = {};
uint64_t origPtr = 0;
if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), poolCreateInfo.memoryTypeIndex) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), poolCreateInfo.flags) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), poolCreateInfo.blockSize) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), poolCreateInfo.minBlockCount) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), poolCreateInfo.maxBlockCount) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), poolCreateInfo.frameInUseCount) &&
StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), origPtr))
{
Pool poolDesc = {};
VkResult res = vmaCreatePool(m_Allocator, &poolCreateInfo, &poolDesc.pool);
if(res != VK_SUCCESS)
{
if(IssueWarning())
printf("Line %zu: vmaCreatePool failed (%u).\n", lineNumber, res);
}
const auto existingIt = m_Pools.find(origPtr);
if(existingIt != m_Pools.end())
{
if(IssueWarning())
printf("Line %zu: Pool %llX already exists.\n", lineNumber, origPtr);
}
m_Pools[origPtr] = poolDesc;
}
else
{
if(IssueWarning())
printf("Line %zu: Invalid parameters for vmaCreatePool.\n", lineNumber);
}
}
}
void Player::ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit)
{
if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
{
uint64_t origPtr = 0;
if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr))
{
const auto it = m_Pools.find(origPtr);
if(it != m_Pools.end())
{
vmaDestroyPool(m_Allocator, it->second.pool);
m_Pools.erase(it);
}
else
{
if(IssueWarning())
printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr);
}
}
else
{
if(IssueWarning())
printf("Line %zu: Invalid parameters for vmaDestroyPool.\n", lineNumber);
}
}
}
void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit)
{
if(ValidateFunctionParameterCount(lineNumber, csvSplit, 12, true))
{
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
VmaAllocationCreateInfo allocCreateInfo = {};
uint64_t origPool = 0;
uint64_t origPtr = 0;
if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), bufCreateInfo.flags) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), bufCreateInfo.size) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), bufCreateInfo.usage) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), (uint32_t&)bufCreateInfo.sharingMode) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), allocCreateInfo.flags) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), (uint32_t&)allocCreateInfo.usage) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.requiredFlags) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.preferredFlags) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), allocCreateInfo.memoryTypeBits) &&
StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPool) &&
StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), origPtr))
{
if(origPool != 0)
{
const auto poolIt = m_Pools.find(origPool);
if(poolIt != m_Pools.end())
allocCreateInfo.pool = poolIt->second.pool;
else
{
if(IssueWarning())
printf("Line %zu: Pool %llX not found.\n", lineNumber, origPool);
}
}
Allocation allocDesc = {};
VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr);
if(res == VK_SUCCESS)
{
m_Stats.RegisterCreateBuffer();
}
else
{
if(IssueWarning())
printf("Line %zu: vmaCreateBuffer failed (%u).\n", lineNumber, res);
}
const auto existingIt = m_Allocations.find(origPtr);
if(existingIt != m_Allocations.end())
{
if(IssueWarning())
printf("Line %zu: Allocation %llX already exists.\n", lineNumber, origPtr);
}
m_Allocations[origPtr] = allocDesc;
}
else
{
if(IssueWarning())
printf("Line %zu: Invalid parameters for vmaCreateBuffer.\n", lineNumber);
}
}
}
void Player::DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit)
{
if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false))
{
uint64_t origAllocPtr = 0;
if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtr))
{
const auto it = m_Allocations.find(origAllocPtr);
if(it != m_Allocations.end())
{
Destroy(it->second);
m_Allocations.erase(it);
}
else
{
if(IssueWarning())
printf("Line %zu: Allocation %llX not found.\n", lineNumber, origAllocPtr);
}
}
else
{
if(IssueWarning())
printf("Line %zu: Invalid parameters for vmaDestroyBuffer.\n", lineNumber);
}
}
}
void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit)
{
if(ValidateFunctionParameterCount(lineNumber, csvSplit, 21, true))
{
VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
VmaAllocationCreateInfo allocCreateInfo = {};
uint64_t origPool = 0;
uint64_t origPtr = 0;
if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), imageCreateInfo.flags) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), (uint32_t&)imageCreateInfo.imageType) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), (uint32_t&)imageCreateInfo.format) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), imageCreateInfo.extent.width) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), imageCreateInfo.extent.height) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), imageCreateInfo.extent.depth) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), imageCreateInfo.mipLevels) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), imageCreateInfo.arrayLayers) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), (uint32_t&)imageCreateInfo.samples) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), (uint32_t&)imageCreateInfo.tiling) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), imageCreateInfo.usage) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 11), (uint32_t&)imageCreateInfo.sharingMode) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 12), (uint32_t&)imageCreateInfo.initialLayout) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 13), allocCreateInfo.flags) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 14), (uint32_t&)allocCreateInfo.usage) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 15), allocCreateInfo.requiredFlags) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 16), allocCreateInfo.preferredFlags) &&
StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 17), allocCreateInfo.memoryTypeBits) &&
StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 18), origPool) &&
StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 19), origPtr))
{
if(origPool != 0)
{
const auto poolIt = m_Pools.find(origPool);
if(poolIt != m_Pools.end())
allocCreateInfo.pool = poolIt->second.pool;
else
{
if(IssueWarning())
printf("Line %zu: Pool %llX not found.\n", lineNumber, origPool);
}
}
Allocation allocDesc = {};
VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &allocDesc.image, &allocDesc.allocation, nullptr);
if(res == VK_SUCCESS)
{
m_Stats.RegisterCreateImage();
}
else
{
if(IssueWarning())
printf("Line %zu: vmaCreateImage failed (%u).\n", lineNumber, res);
}
const auto existingIt = m_Allocations.find(origPtr);
if(existingIt != m_Allocations.end())
{
if(IssueWarning())
printf("Line %zu: Allocation %llX already exists.\n", lineNumber, origPtr);
}
m_Allocations[origPtr] = allocDesc;
}
else
{
if(IssueWarning())
printf("Line %zu: Invalid parameters for vmaCreateImage.\n", lineNumber);
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Main functions
static void PrintCommandLineSyntax()
{
printf("Command line syntax:\n"
" VmaReplay <SrcFile.csv>\n");
}
static int ProcessFile(const char* data, size_t numBytes)
{
// Begin stats.
printf("File size: %zu B\n", numBytes);
LineSplit lineSplit(data, numBytes);
StrRange line;
if(!lineSplit.GetNextLine(line) ||
!StrRangeEq(line, "Vulkan Memory Allocator,Calls recording"))
{
printf("ERROR: Incorrect file format.\n");
return RESULT_ERROR_FORMAT;
}
if(!lineSplit.GetNextLine(line) ||
!(StrRangeEq(line, "1,0") || StrRangeEq(line, "1,1")))
{
printf("ERROR: Incorrect file format version.\n");
return RESULT_ERROR_FORMAT;
}
Player player;
int result = player.Init();
if(result == 0)
{
printf("Playing...\n");
while(lineSplit.GetNextLine(line))
{
player.ExecuteLine(lineSplit.GetNextLineIndex(), line);
}
// End stats.
printf("Done.\n");
printf("File lines: %zu\n", lineSplit.GetNextLineIndex());
}
return result;
}
static int ProcessFile(const char* filePath)
{
printf("Loading file \"%s\"...\n", filePath);
int result = 0;
FILE* file = nullptr;
const errno_t err = fopen_s(&file, filePath, "rb");
if(err == 0)
{
_fseeki64(file, 0, SEEK_END);
const size_t fileSize = (size_t)_ftelli64(file);
_fseeki64(file, 0, SEEK_SET);
if(fileSize > 0)
{
std::vector<char> fileContents(fileSize);
fread(fileContents.data(), 1, fileSize, file);
ProcessFile(fileContents.data(), fileContents.size());
}
else
{
printf("ERROR: Source file is empty.\n");
result = RESULT_ERROR_SOURCE_FILE;
}
fclose(file);
}
else
{
printf("ERROR: Couldn't open file (%i).\n", err);
result = RESULT_ERROR_SOURCE_FILE;
}
return result;
}
static int main2(int argc, char** argv)
{
if(argc != 2)
{
PrintCommandLineSyntax();
return RESULT_ERROR_COMMAND_LINE;
}
return ProcessFile(argv[1]);
}
int main(int argc, char** argv)
{
try
{
return main2(argc, argv);
}
catch(const std::exception& e)
{
printf("ERROR: %s\n", e.what());
return RESULT_EXCEPTION;
}
catch(...)
{
printf("UNKNOWN ERROR\n");
return RESULT_EXCEPTION;
}
}