From 3d99a1ca66060ebbee02072ed17353e9c5695d9b Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 25 Jun 2018 15:01:59 +0200 Subject: [PATCH 01/51] VmaJsonWriter::ContinueString: Fixed bug with incorrect JSON generated when pUserData string contains '\' character. --- src/vk_mem_alloc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 8981fdc..03a0e27 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -5103,7 +5103,7 @@ void VmaJsonWriter::ContinueString(const char* pStr) for(size_t i = 0; i < strLen; ++i) { char ch = pStr[i]; - if(ch == '\'') + if(ch == '\\') { m_SB.Add("\\\\"); } From 0d80dab2c09cb0bbeeaa075fa79b6c1356d1d37a Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Wed, 1 Aug 2018 16:20:24 +0200 Subject: [PATCH 02/51] Fixed minor documentation mistakes - issue #32 - thanks @dgough ! --- src/vk_mem_alloc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 03a0e27..149445e 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -298,7 +298,7 @@ The library provides following functions for mapping of a specific #VmaAllocatio They are safer and more convenient to use than standard Vulkan functions. You can map an allocation multiple times simultaneously - mapping is reference-counted internally. You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block. -They way it's implemented is that the library always maps entire memory block, not just region of the allocation. +The way it's implemented is that the library always maps entire memory block, not just region of the allocation. For further details, see description of vmaMapMemory() function. Example: @@ -312,7 +312,7 @@ struct ConstantBuffer ConstantBuffer constantBufferData; VmaAllocator allocator; -VmaBuffer constantBuffer; +VkBuffer constantBuffer; VmaAllocation constantBufferAllocation; // You can map and fill your buffer using following code: From 1d536111cc9f32d95cd6894574a234772003bc16 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 7 Aug 2018 12:23:55 +0200 Subject: [PATCH 03/51] Removed path to MathFu from premake5.lua, which is not longer included in the project. --- premake/premake5.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/premake/premake5.lua b/premake/premake5.lua index 8893601..9a5b723 100644 --- a/premake/premake5.lua +++ b/premake/premake5.lua @@ -12,7 +12,7 @@ startproject "VulkanSample" filter "platforms:x64" system "Windows" architecture "x64" -includedirs { "../third_party/mathfu-1.1.0/include", "$(VULKAN_SDK)/Include" } +includedirs { "$(VULKAN_SDK)/Include" } libdirs { "$(VULKAN_SDK)/Lib" } filter "platforms:Linux-x64" From 6d2e2e0cac9d0a1775683647e9b730aff57eed5b Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 7 Aug 2018 13:44:20 +0200 Subject: [PATCH 04/51] Created project VmaReplay, started coding it. --- premake/premake5.lua | 39 + src/VmaReplay/Common.cpp | 154 +++ src/VmaReplay/Common.h | 96 ++ src/VmaReplay/VmaReplay.cpp | 2018 +++++++++++++++++++++++++++++++++++ src/VmaReplay/VmaUsage.cpp | 2 + src/VmaReplay/VmaUsage.h | 27 + src/vk_mem_alloc.h | 184 +++- 7 files changed, 2519 insertions(+), 1 deletion(-) create mode 100644 src/VmaReplay/Common.cpp create mode 100644 src/VmaReplay/Common.h create mode 100644 src/VmaReplay/VmaReplay.cpp create mode 100644 src/VmaReplay/VmaUsage.cpp create mode 100644 src/VmaReplay/VmaUsage.h diff --git a/premake/premake5.lua b/premake/premake5.lua index 9a5b723..c6d2b93 100644 --- a/premake/premake5.lua +++ b/premake/premake5.lua @@ -21,6 +21,7 @@ architecture "x64" includedirs { "$(VULKAN_SDK)/include" } libdirs { "$(VULKAN_SDK)/lib" } + project "VulkanSample" kind "ConsoleApp" language "C++" @@ -57,3 +58,41 @@ buildoptions { "/MDd" } filter { "configurations:Release", "platforms:Windows-x64" } buildoptions { "/MD" } + + +project "VmaReplay" +kind "ConsoleApp" +language "C++" +location "../build" +filename ("VmaReplay_" .. _SUFFIX) +targetdir "../bin" +objdir "../build/Desktop_%{_SUFFIX}/%{cfg.platform}/%{cfg.buildcfg}" +floatingpoint "Fast" +files { "../src/VmaReplay/*.h", "../src/VmaReplay/*.cpp" } +flags { "NoPCH", "FatalWarnings" } +characterset "Default" + +filter "configurations:Debug" +defines { "_DEBUG", "DEBUG" } +flags { } +targetsuffix ("_Debug_" .. _SUFFIX) + +filter "configurations:Release" +defines { "NDEBUG" } +optimize "On" +flags { "LinkTimeOptimization" } +targetsuffix ("_Release_" .. _SUFFIX) + +filter { "platforms:x64" } +defines { "WIN32", "_CONSOLE", "PROFILE", "_WINDOWS", "_WIN32_WINNT=0x0601" } +links { "vulkan-1" } + +filter { "platforms:Linux-x64" } +buildoptions { "-std=c++0x" } +links { "vulkan" } + +filter { "configurations:Debug", "platforms:x64" } +buildoptions { "/MDd" } + +filter { "configurations:Release", "platforms:Windows-x64" } +buildoptions { "/MD" } diff --git a/src/VmaReplay/Common.cpp b/src/VmaReplay/Common.cpp new file mode 100644 index 0000000..8f2baf3 --- /dev/null +++ b/src/VmaReplay/Common.cpp @@ -0,0 +1,154 @@ +#include "Common.h" + +void ReadFile(std::vector& out, const char* fileName) +{ + std::ifstream file(fileName, std::ios::ate | std::ios::binary); + assert(file.is_open()); + size_t fileSize = (size_t)file.tellg(); + if(fileSize > 0) + { + out.resize(fileSize); + file.seekg(0); + file.read(out.data(), fileSize); + } + else + out.clear(); +} + +void SetConsoleColor(CONSOLE_COLOR color) +{ + WORD attr = 0; + switch(color) + { + case CONSOLE_COLOR::INFO: + attr = FOREGROUND_INTENSITY;; + break; + case CONSOLE_COLOR::NORMAL: + attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + break; + case CONSOLE_COLOR::WARNING: + attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + break; + case CONSOLE_COLOR::ERROR_: + attr = FOREGROUND_RED | FOREGROUND_INTENSITY; + break; + default: + assert(0); + } + + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(out, attr); +} + +void PrintMessage(CONSOLE_COLOR color, const char* msg) +{ + if(color != CONSOLE_COLOR::NORMAL) + SetConsoleColor(color); + + printf("%s\n", msg); + + if (color != CONSOLE_COLOR::NORMAL) + SetConsoleColor(CONSOLE_COLOR::NORMAL); +} + +void PrintMessage(CONSOLE_COLOR color, const wchar_t* msg) +{ + if(color != CONSOLE_COLOR::NORMAL) + SetConsoleColor(color); + + wprintf(L"%s\n", msg); + + if (color != CONSOLE_COLOR::NORMAL) + SetConsoleColor(CONSOLE_COLOR::NORMAL); +} + +static const size_t CONSOLE_SMALL_BUF_SIZE = 256; + +void PrintMessageV(CONSOLE_COLOR color, const char* format, va_list argList) +{ + size_t dstLen = (size_t)::_vscprintf(format, argList); + if(dstLen) + { + bool useSmallBuf = dstLen < CONSOLE_SMALL_BUF_SIZE; + char smallBuf[CONSOLE_SMALL_BUF_SIZE]; + std::vector bigBuf(useSmallBuf ? 0 : dstLen + 1); + char* bufPtr = useSmallBuf ? smallBuf : bigBuf.data(); + ::vsprintf_s(bufPtr, dstLen + 1, format, argList); + PrintMessage(color, bufPtr); + } +} + +void PrintMessageV(CONSOLE_COLOR color, const wchar_t* format, va_list argList) +{ + size_t dstLen = (size_t)::_vcwprintf(format, argList); + if(dstLen) + { + bool useSmallBuf = dstLen < CONSOLE_SMALL_BUF_SIZE; + wchar_t smallBuf[CONSOLE_SMALL_BUF_SIZE]; + std::vector bigBuf(useSmallBuf ? 0 : dstLen + 1); + wchar_t* bufPtr = useSmallBuf ? smallBuf : bigBuf.data(); + ::vswprintf_s(bufPtr, dstLen + 1, format, argList); + PrintMessage(color, bufPtr); + } +} + +void PrintMessageF(CONSOLE_COLOR color, const char* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(color, format, argList); + va_end(argList); +} + +void PrintMessageF(CONSOLE_COLOR color, const wchar_t* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(color, format, argList); + va_end(argList); +} + +void PrintWarningF(const char* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(CONSOLE_COLOR::WARNING, format, argList); + va_end(argList); +} + +void PrintWarningF(const wchar_t* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(CONSOLE_COLOR::WARNING, format, argList); + va_end(argList); +} + +void PrintErrorF(const char* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(CONSOLE_COLOR::WARNING, format, argList); + va_end(argList); +} + +void PrintErrorF(const wchar_t* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(CONSOLE_COLOR::WARNING, format, argList); + va_end(argList); +} + +void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize) +{ + FILE* f = nullptr; + _wfopen_s(&f, filePath, L"wb"); + if(f) + { + fwrite(data, 1, dataSize, f); + fclose(f); + } + else + assert(0); +} diff --git a/src/VmaReplay/Common.h b/src/VmaReplay/Common.h new file mode 100644 index 0000000..c4934b2 --- /dev/null +++ b/src/VmaReplay/Common.h @@ -0,0 +1,96 @@ +#pragma once + +#include "VmaUsage.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef std::chrono::high_resolution_clock::time_point time_point; +typedef std::chrono::high_resolution_clock::duration duration; + +#define ERR_GUARD_VULKAN(Expr) do { VkResult res__ = (Expr); if (res__ < 0) assert(0); } while(0) + +extern VkPhysicalDevice g_hPhysicalDevice; +extern VkDevice g_hDevice; +extern VmaAllocator g_hAllocator; +extern bool g_MemoryAliasingWarningEnabled; + +inline float ToFloatSeconds(duration d) +{ + return std::chrono::duration_cast>(d).count(); +} + +template +inline T ceil_div(T x, T y) +{ + return (x+y-1) / y; +} + +template +static inline T align_up(T val, T align) +{ + return (val + align - 1) / align * align; +} + +static const float PI = 3.14159265358979323846264338327950288419716939937510582f; + +class RandomNumberGenerator +{ +public: + RandomNumberGenerator() : m_Value{GetTickCount()} {} + RandomNumberGenerator(uint32_t seed) : m_Value{seed} { } + void Seed(uint32_t seed) { m_Value = seed; } + uint32_t Generate() { return GenerateFast() ^ (GenerateFast() >> 7); } + +private: + uint32_t m_Value; + uint32_t GenerateFast() { return m_Value = (m_Value * 196314165 + 907633515); } +}; + +void ReadFile(std::vector& out, const char* fileName); + +enum class CONSOLE_COLOR +{ + INFO, + NORMAL, + WARNING, + ERROR_, + COUNT +}; + +void SetConsoleColor(CONSOLE_COLOR color); + +void PrintMessage(CONSOLE_COLOR color, const char* msg); +void PrintMessage(CONSOLE_COLOR color, const wchar_t* msg); + +inline void Print(const char* msg) { PrintMessage(CONSOLE_COLOR::NORMAL, msg); } +inline void Print(const wchar_t* msg) { PrintMessage(CONSOLE_COLOR::NORMAL, msg); } +inline void PrintWarning(const char* msg) { PrintMessage(CONSOLE_COLOR::WARNING, msg); } +inline void PrintWarning(const wchar_t* msg) { PrintMessage(CONSOLE_COLOR::WARNING, msg); } +inline void PrintError(const char* msg) { PrintMessage(CONSOLE_COLOR::ERROR_, msg); } +inline void PrintError(const wchar_t* msg) { PrintMessage(CONSOLE_COLOR::ERROR_, msg); } + +void PrintMessageV(CONSOLE_COLOR color, const char* format, va_list argList); +void PrintMessageV(CONSOLE_COLOR color, const wchar_t* format, va_list argList); +void PrintMessageF(CONSOLE_COLOR color, const char* format, ...); +void PrintMessageF(CONSOLE_COLOR color, const wchar_t* format, ...); +void PrintWarningF(const char* format, ...); +void PrintWarningF(const wchar_t* format, ...); +void PrintErrorF(const char* format, ...); +void PrintErrorF(const wchar_t* format, ...); + +void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize); diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp new file mode 100644 index 0000000..3fd9b4b --- /dev/null +++ b/src/VmaReplay/VmaReplay.cpp @@ -0,0 +1,2018 @@ +// +// 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" + +#if 0 +static const char* const SHADER_PATH1 = "./"; +static const char* const SHADER_PATH2 = "../bin/"; +static const wchar_t* const WINDOW_CLASS_NAME = L"VULKAN_MEMORY_ALLOCATOR_SAMPLE"; +static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_LUNARG_standard_validation"; +static const char* const APP_TITLE_A = "Vulkan Memory Allocator Sample 2.0"; +static const wchar_t* const APP_TITLE_W = L"Vulkan Memory Allocator Sample 2.0"; + +static const bool VSYNC = true; +static const uint32_t COMMAND_BUFFER_COUNT = 2; +static void* const CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA = (void*)(intptr_t)43564544; +static const bool USE_CUSTOM_CPU_ALLOCATION_CALLBACKS = false; + +VkPhysicalDevice g_hPhysicalDevice; +VkDevice g_hDevice; +VmaAllocator g_hAllocator; +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 HINSTANCE g_hAppInstance; +static HWND g_hWnd; +static LONG g_SizeX = 1280, g_SizeY = 720; +static VkInstance g_hVulkanInstance; +static VkSurfaceKHR g_hSurface; +static VkQueue g_hPresentQueue; +static VkSurfaceFormatKHR g_SurfaceFormat; +static VkExtent2D g_Extent; +static VkSwapchainKHR g_hSwapchain; +static std::vector g_SwapchainImages; +static std::vector g_SwapchainImageViews; +static std::vector g_Framebuffers; +static VkCommandPool g_hCommandPool; +static VkCommandBuffer g_MainCommandBuffers[COMMAND_BUFFER_COUNT]; +static VkFence g_MainCommandBufferExecutedFances[COMMAND_BUFFER_COUNT]; +static uint32_t g_NextCommandBufferIndex; +static VkSemaphore g_hImageAvailableSemaphore; +static VkSemaphore g_hRenderFinishedSemaphore; +static uint32_t g_GraphicsQueueFamilyIndex = UINT_MAX; +static uint32_t g_PresentQueueFamilyIndex = UINT_MAX; +static VkDescriptorSetLayout g_hDescriptorSetLayout; +static VkDescriptorPool g_hDescriptorPool; +static VkDescriptorSet g_hDescriptorSet; // Automatically destroyed with m_DescriptorPool. +static VkSampler g_hSampler; +static VkFormat g_DepthFormat; +static VkImage g_hDepthImage; +static VmaAllocation g_hDepthImageAlloc; +static VkImageView g_hDepthImageView; + +static VkSurfaceCapabilitiesKHR g_SurfaceCapabilities; +static std::vector g_SurfaceFormats; +static std::vector g_PresentModes; + +static PFN_vkCreateDebugReportCallbackEXT g_pvkCreateDebugReportCallbackEXT; +static PFN_vkDebugReportMessageEXT g_pvkDebugReportMessageEXT; +static PFN_vkDestroyDebugReportCallbackEXT g_pvkDestroyDebugReportCallbackEXT; +static VkDebugReportCallbackEXT g_hCallback; + +static VkQueue g_hGraphicsQueue; +static VkCommandBuffer g_hTemporaryCommandBuffer; + +static VkPipelineLayout g_hPipelineLayout; +static VkRenderPass g_hRenderPass; +static VkPipeline g_hPipeline; + +static VkBuffer g_hVertexBuffer; +static VmaAllocation g_hVertexBufferAlloc; +static VkBuffer g_hIndexBuffer; +static VmaAllocation g_hIndexBufferAlloc; +static uint32_t g_VertexCount; +static uint32_t g_IndexCount; + +static VkImage g_hTextureImage; +static VmaAllocation g_hTextureImageAlloc; +static VkImageView g_hTextureImageView; + +static void* CustomCpuAllocation( + void* pUserData, size_t size, size_t alignment, + VkSystemAllocationScope allocationScope) +{ + assert(pUserData == CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA); + return _aligned_malloc(size, alignment); +} + +static void* CustomCpuReallocation( + void* pUserData, void* pOriginal, size_t size, size_t alignment, + VkSystemAllocationScope allocationScope) +{ + assert(pUserData == CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA); + return _aligned_realloc(pOriginal, size, alignment); +} + +static void CustomCpuFree(void* pUserData, void* pMemory) +{ + assert(pUserData == CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA); + _aligned_free(pMemory); +} + +static void BeginSingleTimeCommands() +{ + VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + ERR_GUARD_VULKAN( vkBeginCommandBuffer(g_hTemporaryCommandBuffer, &cmdBufBeginInfo) ); +} + +static void EndSingleTimeCommands() +{ + ERR_GUARD_VULKAN( vkEndCommandBuffer(g_hTemporaryCommandBuffer) ); + + VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &g_hTemporaryCommandBuffer; + + ERR_GUARD_VULKAN( vkQueueSubmit(g_hGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) ); + ERR_GUARD_VULKAN( vkQueueWaitIdle(g_hGraphicsQueue) ); +} + +static void LoadShader(std::vector& out, const char* fileName) +{ + std::ifstream file(std::string(SHADER_PATH1) + fileName, std::ios::ate | std::ios::binary); + if(file.is_open() == false) + file.open(std::string(SHADER_PATH2) + fileName, std::ios::ate | std::ios::binary); + assert(file.is_open()); + size_t fileSize = (size_t)file.tellg(); + if(fileSize > 0) + { + out.resize(fileSize); + file.seekg(0); + file.read(out.data(), fileSize); + file.close(); + } + else + out.clear(); +} + +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; + } + + switch(flags) + { + case VK_DEBUG_REPORT_WARNING_BIT_EXT: + SetConsoleColor(CONSOLE_COLOR::WARNING); + break; + case VK_DEBUG_REPORT_ERROR_BIT_EXT: + SetConsoleColor(CONSOLE_COLOR::ERROR_); + break; + default: + SetConsoleColor(CONSOLE_COLOR::INFO); + } + + printf("%s \xBA %s\n", pLayerPrefix, pMessage); + + SetConsoleColor(CONSOLE_COLOR::NORMAL); + + if(flags == VK_DEBUG_REPORT_WARNING_BIT_EXT || + flags == VK_DEBUG_REPORT_ERROR_BIT_EXT) + { + OutputDebugStringA(pMessage); + OutputDebugStringA("\n"); + } + + return VK_FALSE; +} + +static VkSurfaceFormatKHR ChooseSurfaceFormat() +{ + assert(!g_SurfaceFormats.empty()); + + if((g_SurfaceFormats.size() == 1) && (g_SurfaceFormats[0].format == VK_FORMAT_UNDEFINED)) + { + VkSurfaceFormatKHR result = { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; + return result; + } + + for(const auto& format : g_SurfaceFormats) + { + if((format.format == VK_FORMAT_B8G8R8A8_UNORM) && + (format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)) + { + return format; + } + } + + return g_SurfaceFormats[0]; +} + +VkPresentModeKHR ChooseSwapPresentMode() +{ + VkPresentModeKHR preferredMode = VSYNC ? VK_PRESENT_MODE_MAILBOX_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR; + + if(std::find(g_PresentModes.begin(), g_PresentModes.end(), preferredMode) != + g_PresentModes.end()) + { + return preferredMode; + } + + return VK_PRESENT_MODE_FIFO_KHR; +} + +static VkExtent2D ChooseSwapExtent() +{ + if(g_SurfaceCapabilities.currentExtent.width != UINT_MAX) + return g_SurfaceCapabilities.currentExtent; + + VkExtent2D result = { + std::max(g_SurfaceCapabilities.minImageExtent.width, + std::min(g_SurfaceCapabilities.maxImageExtent.width, (uint32_t)g_SizeX)), + std::max(g_SurfaceCapabilities.minImageExtent.height, + std::min(g_SurfaceCapabilities.maxImageExtent.height, (uint32_t)g_SizeY)) }; + return result; +} + +struct Vertex +{ + float pos[3]; + float color[3]; + float texCoord[2]; +}; + +static void CreateMesh() +{ + assert(g_hAllocator); + + static Vertex vertices[] = { + // -X + { { -1.f, -1.f, -1.f}, {1.0f, 0.0f, 0.0f}, {0.f, 0.f} }, + { { -1.f, -1.f, 1.f}, {1.0f, 0.0f, 0.0f}, {1.f, 0.f} }, + { { -1.f, 1.f, -1.f}, {1.0f, 0.0f, 0.0f}, {0.f, 1.f} }, + { { -1.f, 1.f, 1.f}, {1.0f, 0.0f, 0.0f}, {1.f, 1.f} }, + // +X + { { 1.f, -1.f, 1.f}, {0.0f, 1.0f, 0.0f}, {0.f, 0.f} }, + { { 1.f, -1.f, -1.f}, {0.0f, 1.0f, 0.0f}, {1.f, 0.f} }, + { { 1.f, 1.f, 1.f}, {0.0f, 1.0f, 0.0f}, {0.f, 1.f} }, + { { 1.f, 1.f, -1.f}, {0.0f, 1.0f, 0.0f}, {1.f, 1.f} }, + // -Z + { { 1.f, -1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {0.f, 0.f} }, + { {-1.f, -1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {1.f, 0.f} }, + { { 1.f, 1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {0.f, 1.f} }, + { {-1.f, 1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {1.f, 1.f} }, + // +Z + { {-1.f, -1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {0.f, 0.f} }, + { { 1.f, -1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {1.f, 0.f} }, + { {-1.f, 1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {0.f, 1.f} }, + { { 1.f, 1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {1.f, 1.f} }, + // -Y + { {-1.f, -1.f, -1.f}, {0.0f, 1.0f, 1.0f}, {0.f, 0.f} }, + { { 1.f, -1.f, -1.f}, {0.0f, 1.0f, 1.0f}, {1.f, 0.f} }, + { {-1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f}, {0.f, 1.f} }, + { { 1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f}, {1.f, 1.f} }, + // +Y + { { 1.f, 1.f, -1.f}, {1.0f, 0.0f, 1.0f}, {0.f, 0.f} }, + { {-1.f, 1.f, -1.f}, {1.0f, 0.0f, 1.0f}, {1.f, 0.f} }, + { { 1.f, 1.f, 1.f}, {1.0f, 0.0f, 1.0f}, {0.f, 1.f} }, + { {-1.f, 1.f, 1.f}, {1.0f, 0.0f, 1.0f}, {1.f, 1.f} }, + }; + static uint16_t indices[] = { + 0, 1, 2, 3, USHRT_MAX, + 4, 5, 6, 7, USHRT_MAX, + 8, 9, 10, 11, USHRT_MAX, + 12, 13, 14, 15, USHRT_MAX, + 16, 17, 18, 19, USHRT_MAX, + 20, 21, 22, 23, USHRT_MAX, + }; + + size_t vertexBufferSize = sizeof(Vertex) * _countof(vertices); + size_t indexBufferSize = sizeof(uint16_t) * _countof(indices); + g_IndexCount = (uint32_t)_countof(indices); + + // Create vertex buffer + + VkBufferCreateInfo vbInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + vbInfo.size = vertexBufferSize; + vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + vbInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VmaAllocationCreateInfo vbAllocCreateInfo = {}; + vbAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + vbAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + + VkBuffer stagingVertexBuffer = VK_NULL_HANDLE; + VmaAllocation stagingVertexBufferAlloc = VK_NULL_HANDLE; + VmaAllocationInfo stagingVertexBufferAllocInfo = {}; + ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbAllocCreateInfo, &stagingVertexBuffer, &stagingVertexBufferAlloc, &stagingVertexBufferAllocInfo) ); + + memcpy(stagingVertexBufferAllocInfo.pMappedData, vertices, vertexBufferSize); + + // No need to flush stagingVertexBuffer memory because CPU_ONLY memory is always HOST_COHERENT. + + vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + vbAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + vbAllocCreateInfo.flags = 0; + ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbAllocCreateInfo, &g_hVertexBuffer, &g_hVertexBufferAlloc, nullptr) ); + + // Create index buffer + + VkBufferCreateInfo ibInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + ibInfo.size = indexBufferSize; + ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + ibInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VmaAllocationCreateInfo ibAllocCreateInfo = {}; + ibAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + ibAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + + VkBuffer stagingIndexBuffer = VK_NULL_HANDLE; + VmaAllocation stagingIndexBufferAlloc = VK_NULL_HANDLE; + VmaAllocationInfo stagingIndexBufferAllocInfo = {}; + ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibAllocCreateInfo, &stagingIndexBuffer, &stagingIndexBufferAlloc, &stagingIndexBufferAllocInfo) ); + + memcpy(stagingIndexBufferAllocInfo.pMappedData, indices, indexBufferSize); + + // No need to flush stagingIndexBuffer memory because CPU_ONLY memory is always HOST_COHERENT. + + ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + ibAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + ibAllocCreateInfo.flags = 0; + ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibAllocCreateInfo, &g_hIndexBuffer, &g_hIndexBufferAlloc, nullptr) ); + + // Copy buffers + + BeginSingleTimeCommands(); + + VkBufferCopy vbCopyRegion = {}; + vbCopyRegion.srcOffset = 0; + vbCopyRegion.dstOffset = 0; + vbCopyRegion.size = vbInfo.size; + vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingVertexBuffer, g_hVertexBuffer, 1, &vbCopyRegion); + + VkBufferCopy ibCopyRegion = {}; + ibCopyRegion.srcOffset = 0; + ibCopyRegion.dstOffset = 0; + ibCopyRegion.size = ibInfo.size; + vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingIndexBuffer, g_hIndexBuffer, 1, &ibCopyRegion); + + EndSingleTimeCommands(); + + vmaDestroyBuffer(g_hAllocator, stagingIndexBuffer, stagingIndexBufferAlloc); + vmaDestroyBuffer(g_hAllocator, stagingVertexBuffer, stagingVertexBufferAlloc); +} + +static void CreateTexture(uint32_t sizeX, uint32_t sizeY) +{ + // Create Image + + const VkDeviceSize imageSize = sizeX * sizeY * 4; + + VkImageCreateInfo stagingImageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + stagingImageInfo.imageType = VK_IMAGE_TYPE_2D; + stagingImageInfo.extent.width = sizeX; + stagingImageInfo.extent.height = sizeY; + stagingImageInfo.extent.depth = 1; + stagingImageInfo.mipLevels = 1; + stagingImageInfo.arrayLayers = 1; + stagingImageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + stagingImageInfo.tiling = VK_IMAGE_TILING_LINEAR; + stagingImageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + stagingImageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + stagingImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + stagingImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + stagingImageInfo.flags = 0; + + VmaAllocationCreateInfo stagingImageAllocCreateInfo = {}; + stagingImageAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + stagingImageAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; + + VkImage stagingImage = VK_NULL_HANDLE; + VmaAllocation stagingImageAlloc = VK_NULL_HANDLE; + VmaAllocationInfo stagingImageAllocInfo = {}; + ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &stagingImageInfo, &stagingImageAllocCreateInfo, &stagingImage, &stagingImageAlloc, &stagingImageAllocInfo) ); + + VkImageSubresource imageSubresource = {}; + imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageSubresource.mipLevel = 0; + imageSubresource.arrayLayer = 0; + + VkSubresourceLayout imageLayout = {}; + vkGetImageSubresourceLayout(g_hDevice, stagingImage, &imageSubresource, &imageLayout); + + char* const pMipLevelData = (char*)stagingImageAllocInfo.pMappedData + imageLayout.offset; + uint8_t* pRowData = (uint8_t*)pMipLevelData; + for(uint32_t y = 0; y < sizeY; ++y) + { + uint32_t* pPixelData = (uint32_t*)pRowData; + for(uint32_t x = 0; x < sizeY; ++x) + { + *pPixelData = + ((x & 0x18) == 0x08 ? 0x000000FF : 0x00000000) | + ((x & 0x18) == 0x10 ? 0x0000FFFF : 0x00000000) | + ((y & 0x18) == 0x08 ? 0x0000FF00 : 0x00000000) | + ((y & 0x18) == 0x10 ? 0x00FF0000 : 0x00000000); + ++pPixelData; + } + pRowData += imageLayout.rowPitch; + } + + // No need to flush stagingImage memory because CPU_ONLY memory is always HOST_COHERENT. + + // Create g_hTextureImage in GPU memory. + + VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.extent.width = sizeX; + imageInfo.extent.height = sizeY; + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.flags = 0; + + VmaAllocationCreateInfo imageAllocCreateInfo = {}; + imageAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + + ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &imageInfo, &imageAllocCreateInfo, &g_hTextureImage, &g_hTextureImageAlloc, nullptr) ); + + // Transition image layouts, copy image. + + BeginSingleTimeCommands(); + + VkImageMemoryBarrier imgMemBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + imgMemBarrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + imgMemBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + imgMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imgMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imgMemBarrier.image = stagingImage; + imgMemBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imgMemBarrier.subresourceRange.baseMipLevel = 0; + imgMemBarrier.subresourceRange.levelCount = 1; + imgMemBarrier.subresourceRange.baseArrayLayer = 0; + imgMemBarrier.subresourceRange.layerCount = 1; + imgMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + vkCmdPipelineBarrier( + g_hTemporaryCommandBuffer, + VK_PIPELINE_STAGE_HOST_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &imgMemBarrier); + + imgMemBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imgMemBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imgMemBarrier.image = g_hTextureImage; + imgMemBarrier.srcAccessMask = 0; + imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + vkCmdPipelineBarrier( + g_hTemporaryCommandBuffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &imgMemBarrier); + + VkImageCopy imageCopy = {}; + imageCopy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopy.srcSubresource.baseArrayLayer = 0; + imageCopy.srcSubresource.mipLevel = 0; + imageCopy.srcSubresource.layerCount = 1; + imageCopy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageCopy.dstSubresource.baseArrayLayer = 0; + imageCopy.dstSubresource.mipLevel = 0; + imageCopy.dstSubresource.layerCount = 1; + imageCopy.srcOffset.x = 0; + imageCopy.srcOffset.y = 0; + imageCopy.srcOffset.z = 0; + imageCopy.dstOffset.x = 0; + imageCopy.dstOffset.y = 0; + imageCopy.dstOffset.z = 0; + imageCopy.extent.width = sizeX; + imageCopy.extent.height = sizeY; + imageCopy.extent.depth = 1; + vkCmdCopyImage( + g_hTemporaryCommandBuffer, + stagingImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + g_hTextureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, &imageCopy); + + imgMemBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imgMemBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imgMemBarrier.image = g_hTextureImage; + imgMemBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + imgMemBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier( + g_hTemporaryCommandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &imgMemBarrier); + + EndSingleTimeCommands(); + + vmaDestroyImage(g_hAllocator, stagingImage, stagingImageAlloc); + + // Create ImageView + + VkImageViewCreateInfo textureImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + textureImageViewInfo.image = g_hTextureImage; + textureImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + textureImageViewInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + textureImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + textureImageViewInfo.subresourceRange.baseMipLevel = 0; + textureImageViewInfo.subresourceRange.levelCount = 1; + textureImageViewInfo.subresourceRange.baseArrayLayer = 0; + textureImageViewInfo.subresourceRange.layerCount = 1; + ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &textureImageViewInfo, nullptr, &g_hTextureImageView) ); +} + +struct UniformBufferObject +{ + mat4 ModelViewProj; +}; + +static void RegisterDebugCallbacks() +{ + g_pvkCreateDebugReportCallbackEXT = + reinterpret_cast + (vkGetInstanceProcAddr(g_hVulkanInstance, "vkCreateDebugReportCallbackEXT")); + g_pvkDebugReportMessageEXT = + reinterpret_cast + (vkGetInstanceProcAddr(g_hVulkanInstance, "vkDebugReportMessageEXT")); + g_pvkDestroyDebugReportCallbackEXT = + reinterpret_cast + (vkGetInstanceProcAddr(g_hVulkanInstance, "vkDestroyDebugReportCallbackEXT")); + assert(g_pvkCreateDebugReportCallbackEXT); + assert(g_pvkDebugReportMessageEXT); + assert(g_pvkDestroyDebugReportCallbackEXT); + + VkDebugReportCallbackCreateInfoEXT callbackCreateInfo; + callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + callbackCreateInfo.pNext = nullptr; + 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; + callbackCreateInfo.pUserData = nullptr; + + ERR_GUARD_VULKAN( g_pvkCreateDebugReportCallbackEXT(g_hVulkanInstance, &callbackCreateInfo, nullptr, &g_hCallback) ); +} + +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 VkFormat FindSupportedFormat( + const std::vector& candidates, + VkImageTiling tiling, + VkFormatFeatureFlags features) +{ + for (VkFormat format : candidates) + { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(g_hPhysicalDevice, format, &props); + + if ((tiling == VK_IMAGE_TILING_LINEAR) && + ((props.linearTilingFeatures & features) == features)) + { + return format; + } + else if ((tiling == VK_IMAGE_TILING_OPTIMAL) && + ((props.optimalTilingFeatures & features) == features)) + { + return format; + } + } + return VK_FORMAT_UNDEFINED; +} + +static VkFormat FindDepthFormat() +{ + std::vector formats; + formats.push_back(VK_FORMAT_D32_SFLOAT); + formats.push_back(VK_FORMAT_D32_SFLOAT_S8_UINT); + formats.push_back(VK_FORMAT_D24_UNORM_S8_UINT); + + return FindSupportedFormat( + formats, + VK_IMAGE_TILING_OPTIMAL, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); +} + +static void CreateSwapchain() +{ + // Query surface formats. + + ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_hPhysicalDevice, g_hSurface, &g_SurfaceCapabilities) ); + + uint32_t formatCount = 0; + ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceFormatsKHR(g_hPhysicalDevice, g_hSurface, &formatCount, nullptr) ); + g_SurfaceFormats.resize(formatCount); + ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceFormatsKHR(g_hPhysicalDevice, g_hSurface, &formatCount, g_SurfaceFormats.data()) ); + + uint32_t presentModeCount = 0; + ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfacePresentModesKHR(g_hPhysicalDevice, g_hSurface, &presentModeCount, nullptr) ); + g_PresentModes.resize(presentModeCount); + ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfacePresentModesKHR(g_hPhysicalDevice, g_hSurface, &presentModeCount, g_PresentModes.data()) ); + + // Create swap chain + + g_SurfaceFormat = ChooseSurfaceFormat(); + VkPresentModeKHR presentMode = ChooseSwapPresentMode(); + g_Extent = ChooseSwapExtent(); + + uint32_t imageCount = g_SurfaceCapabilities.minImageCount + 1; + if((g_SurfaceCapabilities.maxImageCount > 0) && + (imageCount > g_SurfaceCapabilities.maxImageCount)) + { + imageCount = g_SurfaceCapabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR swapChainInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; + swapChainInfo.surface = g_hSurface; + swapChainInfo.minImageCount = imageCount; + swapChainInfo.imageFormat = g_SurfaceFormat.format; + swapChainInfo.imageColorSpace = g_SurfaceFormat.colorSpace; + swapChainInfo.imageExtent = g_Extent; + swapChainInfo.imageArrayLayers = 1; + swapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapChainInfo.preTransform = g_SurfaceCapabilities.currentTransform; + swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapChainInfo.presentMode = presentMode; + swapChainInfo.clipped = VK_TRUE; + swapChainInfo.oldSwapchain = g_hSwapchain; + + uint32_t queueFamilyIndices[] = { g_GraphicsQueueFamilyIndex, g_PresentQueueFamilyIndex }; + if(g_PresentQueueFamilyIndex != g_GraphicsQueueFamilyIndex) + { + swapChainInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + swapChainInfo.queueFamilyIndexCount = 2; + swapChainInfo.pQueueFamilyIndices = queueFamilyIndices; + } + else + { + swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + VkSwapchainKHR hNewSwapchain = VK_NULL_HANDLE; + ERR_GUARD_VULKAN( vkCreateSwapchainKHR(g_hDevice, &swapChainInfo, nullptr, &hNewSwapchain) ); + if(g_hSwapchain != VK_NULL_HANDLE) + vkDestroySwapchainKHR(g_hDevice, g_hSwapchain, nullptr); + g_hSwapchain = hNewSwapchain; + + // Retrieve swapchain images. + + uint32_t swapchainImageCount = 0; + ERR_GUARD_VULKAN( vkGetSwapchainImagesKHR(g_hDevice, g_hSwapchain, &swapchainImageCount, nullptr) ); + g_SwapchainImages.resize(swapchainImageCount); + ERR_GUARD_VULKAN( vkGetSwapchainImagesKHR(g_hDevice, g_hSwapchain, &swapchainImageCount, g_SwapchainImages.data()) ); + + // Create swapchain image views. + + for(size_t i = g_SwapchainImageViews.size(); i--; ) + vkDestroyImageView(g_hDevice, g_SwapchainImageViews[i], nullptr); + g_SwapchainImageViews.clear(); + + VkImageViewCreateInfo swapchainImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + g_SwapchainImageViews.resize(swapchainImageCount); + for(uint32_t i = 0; i < swapchainImageCount; ++i) + { + swapchainImageViewInfo.image = g_SwapchainImages[i]; + swapchainImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + swapchainImageViewInfo.format = g_SurfaceFormat.format; + swapchainImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + swapchainImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + swapchainImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + swapchainImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + swapchainImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + swapchainImageViewInfo.subresourceRange.baseMipLevel = 0; + swapchainImageViewInfo.subresourceRange.levelCount = 1; + swapchainImageViewInfo.subresourceRange.baseArrayLayer = 0; + swapchainImageViewInfo.subresourceRange.layerCount = 1; + ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &swapchainImageViewInfo, nullptr, &g_SwapchainImageViews[i]) ); + } + + // Create depth buffer + + g_DepthFormat = FindDepthFormat(); + assert(g_DepthFormat != VK_FORMAT_UNDEFINED); + + VkImageCreateInfo depthImageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + depthImageInfo.imageType = VK_IMAGE_TYPE_2D; + depthImageInfo.extent.width = g_Extent.width; + depthImageInfo.extent.height = g_Extent.height; + depthImageInfo.extent.depth = 1; + depthImageInfo.mipLevels = 1; + depthImageInfo.arrayLayers = 1; + depthImageInfo.format = g_DepthFormat; + depthImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + depthImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + depthImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + depthImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + depthImageInfo.flags = 0; + + VmaAllocationCreateInfo depthImageAllocCreateInfo = {}; + depthImageAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + + ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &depthImageInfo, &depthImageAllocCreateInfo, &g_hDepthImage, &g_hDepthImageAlloc, nullptr) ); + + VkImageViewCreateInfo depthImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + depthImageViewInfo.image = g_hDepthImage; + depthImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + depthImageViewInfo.format = g_DepthFormat; + depthImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + depthImageViewInfo.subresourceRange.baseMipLevel = 0; + depthImageViewInfo.subresourceRange.levelCount = 1; + depthImageViewInfo.subresourceRange.baseArrayLayer = 0; + depthImageViewInfo.subresourceRange.layerCount = 1; + + ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &depthImageViewInfo, nullptr, &g_hDepthImageView) ); + + // Create pipeline layout + { + if(g_hPipelineLayout != VK_NULL_HANDLE) + { + vkDestroyPipelineLayout(g_hDevice, g_hPipelineLayout, nullptr); + g_hPipelineLayout = VK_NULL_HANDLE; + } + + VkPushConstantRange pushConstantRanges[1]; + ZeroMemory(&pushConstantRanges, sizeof pushConstantRanges); + pushConstantRanges[0].offset = 0; + pushConstantRanges[0].size = sizeof(UniformBufferObject); + pushConstantRanges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + VkDescriptorSetLayout descriptorSetLayouts[] = { g_hDescriptorSetLayout }; + VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts; + pipelineLayoutInfo.pushConstantRangeCount = 1; + pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges; + ERR_GUARD_VULKAN( vkCreatePipelineLayout(g_hDevice, &pipelineLayoutInfo, nullptr, &g_hPipelineLayout) ); + } + + // Create render pass + { + if(g_hRenderPass != VK_NULL_HANDLE) + { + vkDestroyRenderPass(g_hDevice, g_hRenderPass, nullptr); + g_hRenderPass = VK_NULL_HANDLE; + } + + VkAttachmentDescription attachments[2]; + ZeroMemory(attachments, sizeof(attachments)); + + attachments[0].format = g_SurfaceFormat.format; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + attachments[1].format = g_DepthFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference colorAttachmentRef = {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depthStencilAttachmentRef = {}; + depthStencilAttachmentRef.attachment = 1; + depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpassDesc = {}; + subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDesc.colorAttachmentCount = 1; + subpassDesc.pColorAttachments = &colorAttachmentRef; + subpassDesc.pDepthStencilAttachment = &depthStencilAttachmentRef; + + VkRenderPassCreateInfo renderPassInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + renderPassInfo.attachmentCount = (uint32_t)_countof(attachments); + renderPassInfo.pAttachments = attachments; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpassDesc; + renderPassInfo.dependencyCount = 0; + ERR_GUARD_VULKAN( vkCreateRenderPass(g_hDevice, &renderPassInfo, nullptr, &g_hRenderPass) ); + } + + // Create pipeline + { + std::vector vertShaderCode; + LoadShader(vertShaderCode, "Shader.vert.spv"); + VkShaderModuleCreateInfo shaderModuleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; + shaderModuleInfo.codeSize = vertShaderCode.size(); + shaderModuleInfo.pCode = (const uint32_t*)vertShaderCode.data(); + VkShaderModule hVertShaderModule = VK_NULL_HANDLE; + ERR_GUARD_VULKAN( vkCreateShaderModule(g_hDevice, &shaderModuleInfo, nullptr, &hVertShaderModule) ); + + std::vector hFragShaderCode; + LoadShader(hFragShaderCode, "Shader.frag.spv"); + shaderModuleInfo.codeSize = hFragShaderCode.size(); + shaderModuleInfo.pCode = (const uint32_t*)hFragShaderCode.data(); + VkShaderModule fragShaderModule = VK_NULL_HANDLE; + ERR_GUARD_VULKAN( vkCreateShaderModule(g_hDevice, &shaderModuleInfo, nullptr, &fragShaderModule) ); + + VkPipelineShaderStageCreateInfo vertPipelineShaderStageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; + vertPipelineShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertPipelineShaderStageInfo.module = hVertShaderModule; + vertPipelineShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragPipelineShaderStageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; + fragPipelineShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragPipelineShaderStageInfo.module = fragShaderModule; + fragPipelineShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo pipelineShaderStageInfos[] = { + vertPipelineShaderStageInfo, + fragPipelineShaderStageInfo + }; + + VkVertexInputBindingDescription bindingDescription = {}; + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription attributeDescriptions[3]; + ZeroMemory(attributeDescriptions, sizeof(attributeDescriptions)); + + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, pos); + + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + + attributeDescriptions[2].binding = 0; + attributeDescriptions[2].location = 2; + attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[2].offset = offsetof(Vertex, texCoord); + + VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; + pipelineVertexInputStateInfo.vertexBindingDescriptionCount = 1; + pipelineVertexInputStateInfo.pVertexBindingDescriptions = &bindingDescription; + pipelineVertexInputStateInfo.vertexAttributeDescriptionCount = _countof(attributeDescriptions); + pipelineVertexInputStateInfo.pVertexAttributeDescriptions = attributeDescriptions; + + VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + pipelineInputAssemblyStateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + pipelineInputAssemblyStateInfo.primitiveRestartEnable = VK_TRUE; + + VkViewport viewport = {}; + viewport.x = 0.f; + viewport.y = 0.f; + viewport.width = (float)g_Extent.width; + viewport.height = (float)g_Extent.height; + viewport.minDepth = 0.f; + viewport.maxDepth = 1.f; + + VkRect2D scissor = {}; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent = g_Extent; + + VkPipelineViewportStateCreateInfo pipelineViewportStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; + pipelineViewportStateInfo.viewportCount = 1; + pipelineViewportStateInfo.pViewports = &viewport; + pipelineViewportStateInfo.scissorCount = 1; + pipelineViewportStateInfo.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + pipelineRasterizationStateInfo.depthClampEnable = VK_FALSE; + pipelineRasterizationStateInfo.rasterizerDiscardEnable = VK_FALSE; + pipelineRasterizationStateInfo.polygonMode = VK_POLYGON_MODE_FILL; + pipelineRasterizationStateInfo.lineWidth = 1.f; + pipelineRasterizationStateInfo.cullMode = VK_CULL_MODE_BACK_BIT; + pipelineRasterizationStateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + pipelineRasterizationStateInfo.depthBiasEnable = VK_FALSE; + pipelineRasterizationStateInfo.depthBiasConstantFactor = 0.f; + pipelineRasterizationStateInfo.depthBiasClamp = 0.f; + pipelineRasterizationStateInfo.depthBiasSlopeFactor = 0.f; + + VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + pipelineMultisampleStateInfo.sampleShadingEnable = VK_FALSE; + pipelineMultisampleStateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + pipelineMultisampleStateInfo.minSampleShading = 1.f; + pipelineMultisampleStateInfo.pSampleMask = nullptr; + pipelineMultisampleStateInfo.alphaToCoverageEnable = VK_FALSE; + pipelineMultisampleStateInfo.alphaToOneEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = {}; + pipelineColorBlendAttachmentState.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT; + pipelineColorBlendAttachmentState.blendEnable = VK_FALSE; + pipelineColorBlendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + pipelineColorBlendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + pipelineColorBlendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; // Optional + pipelineColorBlendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional + pipelineColorBlendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional + pipelineColorBlendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; // Optional + + VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; + pipelineColorBlendStateInfo.logicOpEnable = VK_FALSE; + pipelineColorBlendStateInfo.logicOp = VK_LOGIC_OP_COPY; + pipelineColorBlendStateInfo.attachmentCount = 1; + pipelineColorBlendStateInfo.pAttachments = &pipelineColorBlendAttachmentState; + + VkPipelineDepthStencilStateCreateInfo depthStencilStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; + depthStencilStateInfo.depthTestEnable = VK_TRUE; + depthStencilStateInfo.depthWriteEnable = VK_TRUE; + depthStencilStateInfo.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencilStateInfo.depthBoundsTestEnable = VK_FALSE; + depthStencilStateInfo.stencilTestEnable = VK_FALSE; + + VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = pipelineShaderStageInfos; + pipelineInfo.pVertexInputState = &pipelineVertexInputStateInfo; + pipelineInfo.pInputAssemblyState = &pipelineInputAssemblyStateInfo; + pipelineInfo.pViewportState = &pipelineViewportStateInfo; + pipelineInfo.pRasterizationState = &pipelineRasterizationStateInfo; + pipelineInfo.pMultisampleState = &pipelineMultisampleStateInfo; + pipelineInfo.pDepthStencilState = &depthStencilStateInfo; + pipelineInfo.pColorBlendState = &pipelineColorBlendStateInfo; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = g_hPipelineLayout; + pipelineInfo.renderPass = g_hRenderPass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + ERR_GUARD_VULKAN( vkCreateGraphicsPipelines( + g_hDevice, + VK_NULL_HANDLE, + 1, + &pipelineInfo, nullptr, + &g_hPipeline) ); + + vkDestroyShaderModule(g_hDevice, fragShaderModule, nullptr); + vkDestroyShaderModule(g_hDevice, hVertShaderModule, nullptr); + } + + // Create frambuffers + + for(size_t i = g_Framebuffers.size(); i--; ) + vkDestroyFramebuffer(g_hDevice, g_Framebuffers[i], nullptr); + g_Framebuffers.clear(); + + g_Framebuffers.resize(g_SwapchainImageViews.size()); + for(size_t i = 0; i < g_SwapchainImages.size(); ++i) + { + VkImageView attachments[] = { g_SwapchainImageViews[i], g_hDepthImageView }; + + VkFramebufferCreateInfo framebufferInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + framebufferInfo.renderPass = g_hRenderPass; + framebufferInfo.attachmentCount = (uint32_t)_countof(attachments); + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = g_Extent.width; + framebufferInfo.height = g_Extent.height; + framebufferInfo.layers = 1; + ERR_GUARD_VULKAN( vkCreateFramebuffer(g_hDevice, &framebufferInfo, nullptr, &g_Framebuffers[i]) ); + } + + // Create semaphores + + if(g_hImageAvailableSemaphore != VK_NULL_HANDLE) + { + vkDestroySemaphore(g_hDevice, g_hImageAvailableSemaphore, nullptr); + g_hImageAvailableSemaphore = VK_NULL_HANDLE; + } + if(g_hRenderFinishedSemaphore != VK_NULL_HANDLE) + { + vkDestroySemaphore(g_hDevice, g_hRenderFinishedSemaphore, nullptr); + g_hRenderFinishedSemaphore = VK_NULL_HANDLE; + } + + VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + ERR_GUARD_VULKAN( vkCreateSemaphore(g_hDevice, &semaphoreInfo, nullptr, &g_hImageAvailableSemaphore) ); + ERR_GUARD_VULKAN( vkCreateSemaphore(g_hDevice, &semaphoreInfo, nullptr, &g_hRenderFinishedSemaphore) ); +} + +static void DestroySwapchain(bool destroyActualSwapchain) +{ + if(g_hImageAvailableSemaphore != VK_NULL_HANDLE) + { + vkDestroySemaphore(g_hDevice, g_hImageAvailableSemaphore, nullptr); + g_hImageAvailableSemaphore = VK_NULL_HANDLE; + } + if(g_hRenderFinishedSemaphore != VK_NULL_HANDLE) + { + vkDestroySemaphore(g_hDevice, g_hRenderFinishedSemaphore, nullptr); + g_hRenderFinishedSemaphore = VK_NULL_HANDLE; + } + + for(size_t i = g_Framebuffers.size(); i--; ) + vkDestroyFramebuffer(g_hDevice, g_Framebuffers[i], nullptr); + g_Framebuffers.clear(); + + if(g_hDepthImageView != VK_NULL_HANDLE) + { + vkDestroyImageView(g_hDevice, g_hDepthImageView, nullptr); + g_hDepthImageView = VK_NULL_HANDLE; + } + if(g_hDepthImage != VK_NULL_HANDLE) + { + vmaDestroyImage(g_hAllocator, g_hDepthImage, g_hDepthImageAlloc); + g_hDepthImage = VK_NULL_HANDLE; + } + + if(g_hPipeline != VK_NULL_HANDLE) + { + vkDestroyPipeline(g_hDevice, g_hPipeline, nullptr); + g_hPipeline = VK_NULL_HANDLE; + } + + if(g_hRenderPass != VK_NULL_HANDLE) + { + vkDestroyRenderPass(g_hDevice, g_hRenderPass, nullptr); + g_hRenderPass = VK_NULL_HANDLE; + } + + if(g_hPipelineLayout != VK_NULL_HANDLE) + { + vkDestroyPipelineLayout(g_hDevice, g_hPipelineLayout, nullptr); + g_hPipelineLayout = VK_NULL_HANDLE; + } + + for(size_t i = g_SwapchainImageViews.size(); i--; ) + vkDestroyImageView(g_hDevice, g_SwapchainImageViews[i], nullptr); + g_SwapchainImageViews.clear(); + + if(destroyActualSwapchain && (g_hSwapchain != VK_NULL_HANDLE)) + { + vkDestroySwapchainKHR(g_hDevice, g_hSwapchain, nullptr); + g_hSwapchain = VK_NULL_HANDLE; + } +} + +static void InitializeApplication() +{ + uint32_t instanceLayerPropCount = 0; + ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr) ); + std::vector instanceLayerProps(instanceLayerPropCount); + if(instanceLayerPropCount > 0) + { + ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data()) ); + } + + if(g_EnableValidationLayer == true) + { + if(IsLayerSupported(instanceLayerProps.data(), instanceLayerProps.size(), VALIDATION_LAYER_NAME) == false) + { + printf("Layer \"%s\" not supported.", VALIDATION_LAYER_NAME); + g_EnableValidationLayer = false; + } + } + + std::vector instanceExtensions; + instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); + + std::vector instanceLayers; + if(g_EnableValidationLayer == true) + { + instanceLayers.push_back(VALIDATION_LAYER_NAME); + instanceExtensions.push_back("VK_EXT_debug_report"); + } + + VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + appInfo.pApplicationName = APP_TITLE_A; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.pEngineName = "Adam Sawicki Engine"; + 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 = static_cast(instanceExtensions.size()); + instInfo.ppEnabledExtensionNames = instanceExtensions.data(); + instInfo.enabledLayerCount = static_cast(instanceLayers.size()); + instInfo.ppEnabledLayerNames = instanceLayers.data(); + + ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, NULL, &g_hVulkanInstance) ); + + // Create VkSurfaceKHR. + VkWin32SurfaceCreateInfoKHR surfaceInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR }; + surfaceInfo.hinstance = g_hAppInstance; + surfaceInfo.hwnd = g_hWnd; + VkResult result = vkCreateWin32SurfaceKHR(g_hVulkanInstance, &surfaceInfo, NULL, &g_hSurface); + assert(result == VK_SUCCESS); + + if(g_EnableValidationLayer == true) + RegisterDebugCallbacks(); + + // Find physical device + + uint32_t deviceCount = 0; + ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, nullptr) ); + assert(deviceCount > 0); + + std::vector physicalDevices(deviceCount); + ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, physicalDevices.data()) ); + + g_hPhysicalDevice = physicalDevices[0]; + + // Query for features + + VkPhysicalDeviceProperties physicalDeviceProperties = {}; + vkGetPhysicalDeviceProperties(g_hPhysicalDevice, &physicalDeviceProperties); + + //VkPhysicalDeviceFeatures physicalDeviceFreatures = {}; + //vkGetPhysicalDeviceFeatures(g_PhysicalDevice, &physicalDeviceFreatures); + + // Find queue family index + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(g_hPhysicalDevice, &queueFamilyCount, nullptr); + assert(queueFamilyCount > 0); + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(g_hPhysicalDevice, &queueFamilyCount, queueFamilies.data()); + for(uint32_t i = 0; + (i < queueFamilyCount) && + (g_GraphicsQueueFamilyIndex == UINT_MAX || g_PresentQueueFamilyIndex == UINT_MAX); + ++i) + { + if(queueFamilies[i].queueCount > 0) + { + if((g_GraphicsQueueFamilyIndex != 0) && + ((queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) + { + g_GraphicsQueueFamilyIndex = i; + } + + VkBool32 surfaceSupported = 0; + VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(g_hPhysicalDevice, i, g_hSurface, &surfaceSupported); + if((res >= 0) && (surfaceSupported == VK_TRUE)) + { + g_PresentQueueFamilyIndex = i; + } + } + } + assert(g_GraphicsQueueFamilyIndex != UINT_MAX); + + // Create logical device + + const float queuePriority = 1.f; + + VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; + deviceQueueCreateInfo[0].queueFamilyIndex = g_GraphicsQueueFamilyIndex; + deviceQueueCreateInfo[0].queueCount = 1; + deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority; + deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfo[1].queueFamilyIndex = g_PresentQueueFamilyIndex; + deviceQueueCreateInfo[1].queueCount = 1; + deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority; + + VkPhysicalDeviceFeatures deviceFeatures = {}; + deviceFeatures.fillModeNonSolid = VK_TRUE; + deviceFeatures.samplerAnisotropy = VK_TRUE; + + // Determine list of device extensions to enable. + std::vector enabledDeviceExtensions; + enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + { + uint32_t propertyCount = 0; + ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(g_hPhysicalDevice, nullptr, &propertyCount, nullptr) ); + + if(propertyCount) + { + std::vector properties{propertyCount}; + ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(g_hPhysicalDevice, nullptr, &propertyCount, properties.data()) ); + + 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.enabledLayerCount = 0; + deviceCreateInfo.ppEnabledLayerNames = nullptr; + deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size(); + deviceCreateInfo.ppEnabledExtensionNames = !enabledDeviceExtensions.empty() ? enabledDeviceExtensions.data() : nullptr; + deviceCreateInfo.queueCreateInfoCount = g_PresentQueueFamilyIndex != g_GraphicsQueueFamilyIndex ? 2 : 1; + deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; + deviceCreateInfo.pEnabledFeatures = &deviceFeatures; + + ERR_GUARD_VULKAN( vkCreateDevice(g_hPhysicalDevice, &deviceCreateInfo, nullptr, &g_hDevice) ); + + // Create memory allocator + + VmaAllocatorCreateInfo allocatorInfo = {}; + allocatorInfo.physicalDevice = g_hPhysicalDevice; + allocatorInfo.device = g_hDevice; + + if(VK_KHR_dedicated_allocation_enabled) + { + allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; + } + + VkAllocationCallbacks cpuAllocationCallbacks = {}; + if(USE_CUSTOM_CPU_ALLOCATION_CALLBACKS) + { + cpuAllocationCallbacks.pUserData = CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA; + cpuAllocationCallbacks.pfnAllocation = &CustomCpuAllocation; + cpuAllocationCallbacks.pfnReallocation = &CustomCpuReallocation; + cpuAllocationCallbacks.pfnFree = &CustomCpuFree; + allocatorInfo.pAllocationCallbacks = &cpuAllocationCallbacks; + } + + ERR_GUARD_VULKAN( vmaCreateAllocator(&allocatorInfo, &g_hAllocator) ); + + // Retrieve queue (doesn't need to be destroyed) + + vkGetDeviceQueue(g_hDevice, g_GraphicsQueueFamilyIndex, 0, &g_hGraphicsQueue); + vkGetDeviceQueue(g_hDevice, g_PresentQueueFamilyIndex, 0, &g_hPresentQueue); + assert(g_hGraphicsQueue); + assert(g_hPresentQueue); + + // Create command pool + + VkCommandPoolCreateInfo commandPoolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; + commandPoolInfo.queueFamilyIndex = g_GraphicsQueueFamilyIndex; + commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + ERR_GUARD_VULKAN( vkCreateCommandPool(g_hDevice, &commandPoolInfo, nullptr, &g_hCommandPool) ); + + VkCommandBufferAllocateInfo commandBufferInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + commandBufferInfo.commandPool = g_hCommandPool; + commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandBufferInfo.commandBufferCount = COMMAND_BUFFER_COUNT; + ERR_GUARD_VULKAN( vkAllocateCommandBuffers(g_hDevice, &commandBufferInfo, g_MainCommandBuffers) ); + + VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + for(size_t i = 0; i < COMMAND_BUFFER_COUNT; ++i) + { + ERR_GUARD_VULKAN( vkCreateFence(g_hDevice, &fenceInfo, nullptr, &g_MainCommandBufferExecutedFances[i]) ); + } + + commandBufferInfo.commandBufferCount = 1; + ERR_GUARD_VULKAN( vkAllocateCommandBuffers(g_hDevice, &commandBufferInfo, &g_hTemporaryCommandBuffer) ); + + // Create texture sampler + + VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = 16; + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.f; + samplerInfo.minLod = 0.f; + samplerInfo.maxLod = FLT_MAX; + ERR_GUARD_VULKAN( vkCreateSampler(g_hDevice, &samplerInfo, nullptr, &g_hSampler) ); + + CreateTexture(128, 128); + CreateMesh(); + + VkDescriptorSetLayoutBinding samplerLayoutBinding = {}; + samplerLayoutBinding.binding = 1; + samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + samplerLayoutBinding.descriptorCount = 1; + samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + descriptorSetLayoutInfo.bindingCount = 1; + descriptorSetLayoutInfo.pBindings = &samplerLayoutBinding; + ERR_GUARD_VULKAN( vkCreateDescriptorSetLayout(g_hDevice, &descriptorSetLayoutInfo, nullptr, &g_hDescriptorSetLayout) ); + + // Create descriptor pool + + VkDescriptorPoolSize descriptorPoolSizes[2]; + ZeroMemory(descriptorPoolSizes, sizeof(descriptorPoolSizes)); + descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorPoolSizes[0].descriptorCount = 1; + descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorPoolSizes[1].descriptorCount = 1; + + VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + descriptorPoolInfo.poolSizeCount = (uint32_t)_countof(descriptorPoolSizes); + descriptorPoolInfo.pPoolSizes = descriptorPoolSizes; + descriptorPoolInfo.maxSets = 1; + ERR_GUARD_VULKAN( vkCreateDescriptorPool(g_hDevice, &descriptorPoolInfo, nullptr, &g_hDescriptorPool) ); + + // Create descriptor set layout + + VkDescriptorSetLayout descriptorSetLayouts[] = { g_hDescriptorSetLayout }; + VkDescriptorSetAllocateInfo descriptorSetInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + descriptorSetInfo.descriptorPool = g_hDescriptorPool; + descriptorSetInfo.descriptorSetCount = 1; + descriptorSetInfo.pSetLayouts = descriptorSetLayouts; + ERR_GUARD_VULKAN( vkAllocateDescriptorSets(g_hDevice, &descriptorSetInfo, &g_hDescriptorSet) ); + + VkDescriptorImageInfo descriptorImageInfo = {}; + descriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + descriptorImageInfo.imageView = g_hTextureImageView; + descriptorImageInfo.sampler = g_hSampler; + + VkWriteDescriptorSet writeDescriptorSet = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + writeDescriptorSet.dstSet = g_hDescriptorSet; + writeDescriptorSet.dstBinding = 1; + writeDescriptorSet.dstArrayElement = 0; + writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writeDescriptorSet.descriptorCount = 1; + writeDescriptorSet.pImageInfo = &descriptorImageInfo; + + vkUpdateDescriptorSets(g_hDevice, 1, &writeDescriptorSet, 0, nullptr); + + CreateSwapchain(); +} + +static void FinalizeApplication() +{ + vkDeviceWaitIdle(g_hDevice); + + DestroySwapchain(true); + + if(g_hDescriptorPool != VK_NULL_HANDLE) + { + vkDestroyDescriptorPool(g_hDevice, g_hDescriptorPool, nullptr); + g_hDescriptorPool = VK_NULL_HANDLE; + } + + if(g_hDescriptorSetLayout != VK_NULL_HANDLE) + { + vkDestroyDescriptorSetLayout(g_hDevice, g_hDescriptorSetLayout, nullptr); + g_hDescriptorSetLayout = VK_NULL_HANDLE; + } + + if(g_hTextureImageView != VK_NULL_HANDLE) + { + vkDestroyImageView(g_hDevice, g_hTextureImageView, nullptr); + g_hTextureImageView = VK_NULL_HANDLE; + } + if(g_hTextureImage != VK_NULL_HANDLE) + { + vmaDestroyImage(g_hAllocator, g_hTextureImage, g_hTextureImageAlloc); + g_hTextureImage = VK_NULL_HANDLE; + } + + if(g_hIndexBuffer != VK_NULL_HANDLE) + { + vmaDestroyBuffer(g_hAllocator, g_hIndexBuffer, g_hIndexBufferAlloc); + g_hIndexBuffer = VK_NULL_HANDLE; + } + if(g_hVertexBuffer != VK_NULL_HANDLE) + { + vmaDestroyBuffer(g_hAllocator, g_hVertexBuffer, g_hVertexBufferAlloc); + g_hVertexBuffer = VK_NULL_HANDLE; + } + + if(g_hSampler != VK_NULL_HANDLE) + { + vkDestroySampler(g_hDevice, g_hSampler, nullptr); + g_hSampler = VK_NULL_HANDLE; + } + + for(size_t i = COMMAND_BUFFER_COUNT; i--; ) + { + if(g_MainCommandBufferExecutedFances[i] != VK_NULL_HANDLE) + { + vkDestroyFence(g_hDevice, g_MainCommandBufferExecutedFances[i], nullptr); + g_MainCommandBufferExecutedFances[i] = VK_NULL_HANDLE; + } + } + if(g_MainCommandBuffers[0] != VK_NULL_HANDLE) + { + vkFreeCommandBuffers(g_hDevice, g_hCommandPool, COMMAND_BUFFER_COUNT, g_MainCommandBuffers); + ZeroMemory(g_MainCommandBuffers, sizeof(g_MainCommandBuffers)); + } + if(g_hTemporaryCommandBuffer != VK_NULL_HANDLE) + { + vkFreeCommandBuffers(g_hDevice, g_hCommandPool, 1, &g_hTemporaryCommandBuffer); + g_hTemporaryCommandBuffer = VK_NULL_HANDLE; + } + + if(g_hCommandPool != VK_NULL_HANDLE) + { + vkDestroyCommandPool(g_hDevice, g_hCommandPool, nullptr); + g_hCommandPool = VK_NULL_HANDLE; + } + + if(g_hAllocator != VK_NULL_HANDLE) + { + vmaDestroyAllocator(g_hAllocator); + g_hAllocator = nullptr; + } + + if(g_hDevice != VK_NULL_HANDLE) + { + vkDestroyDevice(g_hDevice, nullptr); + g_hDevice = nullptr; + } + + if(g_pvkDestroyDebugReportCallbackEXT && g_hCallback != VK_NULL_HANDLE) + { + g_pvkDestroyDebugReportCallbackEXT(g_hVulkanInstance, g_hCallback, nullptr); + g_hCallback = VK_NULL_HANDLE; + } + + if(g_hSurface != VK_NULL_HANDLE) + { + vkDestroySurfaceKHR(g_hVulkanInstance, g_hSurface, NULL); + g_hSurface = VK_NULL_HANDLE; + } + + if(g_hVulkanInstance != VK_NULL_HANDLE) + { + vkDestroyInstance(g_hVulkanInstance, NULL); + g_hVulkanInstance = VK_NULL_HANDLE; + } +} + +static void PrintAllocatorStats() +{ +#if VMA_STATS_STRING_ENABLED + char* statsString = nullptr; + vmaBuildStatsString(g_hAllocator, &statsString, true); + printf("%s\n", statsString); + vmaFreeStatsString(g_hAllocator, statsString); +#endif +} + +static void RecreateSwapChain() +{ + vkDeviceWaitIdle(g_hDevice); + DestroySwapchain(false); + CreateSwapchain(); +} + +static void DrawFrame() +{ + // Begin main command buffer + size_t cmdBufIndex = (g_NextCommandBufferIndex++) % COMMAND_BUFFER_COUNT; + VkCommandBuffer hCommandBuffer = g_MainCommandBuffers[cmdBufIndex]; + VkFence hCommandBufferExecutedFence = g_MainCommandBufferExecutedFances[cmdBufIndex]; + + ERR_GUARD_VULKAN( vkWaitForFences(g_hDevice, 1, &hCommandBufferExecutedFence, VK_TRUE, UINT64_MAX) ); + ERR_GUARD_VULKAN( vkResetFences(g_hDevice, 1, &hCommandBufferExecutedFence) ); + + VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + ERR_GUARD_VULKAN( vkBeginCommandBuffer(hCommandBuffer, &commandBufferBeginInfo) ); + + // Acquire swapchain image + uint32_t imageIndex = 0; + VkResult res = vkAcquireNextImageKHR(g_hDevice, g_hSwapchain, UINT64_MAX, g_hImageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); + if(res == VK_ERROR_OUT_OF_DATE_KHR) + { + RecreateSwapChain(); + return; + } + else if(res < 0) + { + ERR_GUARD_VULKAN(res); + } + + // Record geometry pass + + VkClearValue clearValues[2]; + ZeroMemory(clearValues, sizeof(clearValues)); + clearValues[0].color.float32[0] = 0.25f; + clearValues[0].color.float32[1] = 0.25f; + clearValues[0].color.float32[2] = 0.5f; + clearValues[0].color.float32[3] = 1.0f; + clearValues[1].depthStencil.depth = 1.0f; + + VkRenderPassBeginInfo renderPassBeginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + renderPassBeginInfo.renderPass = g_hRenderPass; + renderPassBeginInfo.framebuffer = g_Framebuffers[imageIndex]; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent = g_Extent; + renderPassBeginInfo.clearValueCount = (uint32_t)_countof(clearValues); + renderPassBeginInfo.pClearValues = clearValues; + vkCmdBeginRenderPass(hCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline( + hCommandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + g_hPipeline); + + mat4 view = mat4::LookAt( + vec3(0.f, 0.f, 0.f), + vec3(0.f, -2.f, 4.f), + vec3(0.f, 1.f, 0.f)); + mat4 proj = mat4::Perspective( + 1.0471975511966f, // 60 degrees + (float)g_Extent.width / (float)g_Extent.height, + 0.1f, + 1000.f); + mat4 viewProj = view * proj; + + vkCmdBindDescriptorSets( + hCommandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + g_hPipelineLayout, + 0, + 1, + &g_hDescriptorSet, + 0, + nullptr); + + float rotationAngle = (float)GetTickCount() * 0.001f * (float)PI * 0.2f; + mat4 model = mat4::RotationY(rotationAngle); + + UniformBufferObject ubo = {}; + ubo.ModelViewProj = model * viewProj; + vkCmdPushConstants(hCommandBuffer, g_hPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(UniformBufferObject), &ubo); + + VkBuffer vertexBuffers[] = { g_hVertexBuffer }; + VkDeviceSize offsets[] = { 0 }; + vkCmdBindVertexBuffers(hCommandBuffer, 0, 1, vertexBuffers, offsets); + + vkCmdBindIndexBuffer(hCommandBuffer, g_hIndexBuffer, 0, VK_INDEX_TYPE_UINT16); + + vkCmdDrawIndexed(hCommandBuffer, g_IndexCount, 1, 0, 0, 0); + + vkCmdEndRenderPass(hCommandBuffer); + + vkEndCommandBuffer(hCommandBuffer); + + // Submit command buffer + + VkSemaphore submitWaitSemaphores[] = { g_hImageAvailableSemaphore }; + VkPipelineStageFlags submitWaitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + VkSemaphore submitSignalSemaphores[] = { g_hRenderFinishedSemaphore }; + VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = submitWaitSemaphores; + submitInfo.pWaitDstStageMask = submitWaitStages; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &hCommandBuffer; + submitInfo.signalSemaphoreCount = _countof(submitSignalSemaphores); + submitInfo.pSignalSemaphores = submitSignalSemaphores; + ERR_GUARD_VULKAN( vkQueueSubmit(g_hGraphicsQueue, 1, &submitInfo, hCommandBufferExecutedFence) ); + + VkSemaphore presentWaitSemaphores[] = { g_hRenderFinishedSemaphore }; + + VkSwapchainKHR swapchains[] = { g_hSwapchain }; + VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; + presentInfo.waitSemaphoreCount = _countof(presentWaitSemaphores); + presentInfo.pWaitSemaphores = presentWaitSemaphores; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapchains; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = nullptr; + res = vkQueuePresentKHR(g_hPresentQueue, &presentInfo); + if(res == VK_ERROR_OUT_OF_DATE_KHR) + { + RecreateSwapChain(); + } + else + ERR_GUARD_VULKAN(res); +} + +static void HandlePossibleSizeChange() +{ + RECT clientRect; + GetClientRect(g_hWnd, &clientRect); + LONG newSizeX = clientRect.right - clientRect.left; + LONG newSizeY = clientRect.bottom - clientRect.top; + if((newSizeX > 0) && + (newSizeY > 0) && + ((newSizeX != g_SizeX) || (newSizeY != g_SizeY))) + { + g_SizeX = newSizeX; + g_SizeY = newSizeY; + + RecreateSwapChain(); + } +} + +static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { + case WM_CREATE: + // This is intentionally assigned here because we are now inside CreateWindow, before it returns. + g_hWnd = hWnd; + InitializeApplication(); + PrintAllocatorStats(); + return 0; + + case WM_DESTROY: + FinalizeApplication(); + PostQuitMessage(0); + return 0; + + // This prevents app from freezing when left Alt is pressed + // (which normally enters modal menu loop). + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + return 0; + + case WM_SIZE: + if((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)) + HandlePossibleSizeChange(); + return 0; + + case WM_EXITSIZEMOVE: + HandlePossibleSizeChange(); + return 0; + + case WM_KEYDOWN: + switch(wParam) + { + case VK_ESCAPE: + PostMessage(hWnd, WM_CLOSE, 0, 0); + break; + case 'T': + Test(); + break; + } + return 0; + + default: + break; + } + + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +int main() +{ + g_hAppInstance = (HINSTANCE)GetModuleHandle(NULL); + + WNDCLASSEX wndClassDesc = { sizeof(WNDCLASSEX) }; + wndClassDesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS; + wndClassDesc.hbrBackground = NULL; + wndClassDesc.hCursor = LoadCursor(NULL, IDC_CROSS); + wndClassDesc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndClassDesc.hInstance = g_hAppInstance; + wndClassDesc.lpfnWndProc = WndProc; + wndClassDesc.lpszClassName = WINDOW_CLASS_NAME; + + const ATOM hWndClass = RegisterClassEx(&wndClassDesc); + assert(hWndClass); + + const DWORD style = WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; + const DWORD exStyle = 0; + + RECT rect = { 0, 0, g_SizeX, g_SizeY }; + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + + CreateWindowEx( + exStyle, WINDOW_CLASS_NAME, APP_TITLE_W, style, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, g_hAppInstance, NULL); + + MSG msg; + for(;;) + { + if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + if(msg.message == WM_QUIT) + break; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + if(g_hDevice != VK_NULL_HANDLE) + DrawFrame(); + } + + return 0; +} +#endif + +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; + +//////////////////////////////////////////////////////////////////////////////// +// 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(const char *&outBeg, const char*&outEnd); + 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(const char *&outBeg, const char*&outEnd) +{ + if(m_NextLineBeg < m_NumBytes) + { + outBeg = m_Data + m_NextLineBeg; + size_t currLineEnd = m_NextLineBeg; + while(currLineEnd < m_NumBytes && m_Data[currLineEnd] != '\n') + ++currLineEnd; + outEnd = 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 char* beg, const char* end, size_t maxCount = RANGE_COUNT_MAX); + + size_t GetCount() const { return m_Count; } + void GetRange(size_t index, const char*& outBeg, const char*& outEnd) const + { + outBeg = m_Str + m_Ranges[index * 2]; + outEnd = 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 char* beg, const char* end, size_t maxCount) +{ + assert(maxCount <= RANGE_COUNT_MAX); + m_Str = beg; + const size_t strLen = end - beg; + 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 Player + +class Player +{ +public: + Player(); + void Init(); + ~Player(); + + void ExecuteLine(size_t lineNumber, const char* lineBeg, const char* lineEnd); + +private: + static const size_t MAX_WARNINGS_TO_SHOW = 16; + + // Increments warning counter. Returns true if warning message should be printed. + bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; } + + size_t m_WarningCount = 0; +}; + +Player::Player() +{ +} + +void Player::Init() +{ +} + +Player::~Player() +{ + if(m_WarningCount > 0) + printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW); +} + +void Player::ExecuteLine(size_t lineNumber, const char* lineBeg, const char* lineEnd) +{ + CsvSplit csvSplit; + csvSplit.Set(lineBeg, lineEnd); + + if(csvSplit.GetCount() >= 4) + { + } + else + { + if(IssueWarning()) + printf("Line %zu: Too few columns.\n", lineNumber); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// Main functions + +static void PrintCommandLineSyntax() +{ + printf("Command line syntax:\n" + " VmaReplay \n"); +} + +static inline bool StrRangeEq(const char* lhsBeg, const char* lhsEnd, const char* rhsSz) +{ + const size_t rhsLen = strlen(rhsSz); + return rhsLen == lhsEnd - lhsBeg && + memcmp(lhsBeg, rhsSz, rhsLen) == 0; +} + +static int ProcessFile(const char* data, size_t numBytes) +{ + // Begin stats. + printf("File size: %zu B\n", numBytes); + + LineSplit lineSplit(data, numBytes); + const char* lineBeg; + const char* lineEnd; + + if(!lineSplit.GetNextLine(lineBeg, lineEnd) || + !StrRangeEq(lineBeg, lineEnd, "Vulkan Memory Allocator,Calls recording")) + { + printf("ERROR: Incorrect file format.\n"); + return RESULT_ERROR_FORMAT; + } + + if(!lineSplit.GetNextLine(lineBeg, lineEnd) || + !StrRangeEq(lineBeg, lineEnd, "1,0")) + { + printf("ERROR: Incorrect file format version.\n"); + return RESULT_ERROR_FORMAT; + } + + Player player; + player.Init(); + + while(lineSplit.GetNextLine(lineBeg, lineEnd)) + { + player.ExecuteLine(lineSplit.GetNextLineIndex(), lineBeg, lineEnd); + } + + // End stats. + printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); + + return 0; +} + +static int ProcessFile(const char* filePath) +{ + printf("Replaying 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 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 + { + 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; + } + + return 0; +} diff --git a/src/VmaReplay/VmaUsage.cpp b/src/VmaReplay/VmaUsage.cpp new file mode 100644 index 0000000..d2d035b --- /dev/null +++ b/src/VmaReplay/VmaUsage.cpp @@ -0,0 +1,2 @@ +#define VMA_IMPLEMENTATION +#include "VmaUsage.h" diff --git a/src/VmaReplay/VmaUsage.h b/src/VmaReplay/VmaUsage.h new file mode 100644 index 0000000..182132b --- /dev/null +++ b/src/VmaReplay/VmaUsage.h @@ -0,0 +1,27 @@ +#pragma once + +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#define VK_USE_PLATFORM_WIN32_KHR + +#include + +//#define VMA_USE_STL_CONTAINERS 1 + +//#define VMA_HEAVY_ASSERT(expr) assert(expr) + +//#define VMA_DEDICATED_ALLOCATION 0 + +//#define VMA_DEBUG_MARGIN 16 +//#define VMA_DEBUG_DETECT_CORRUPTION 1 +//#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 + +#pragma warning(push, 4) +#pragma warning(disable: 4127) // conditional expression is constant +#pragma warning(disable: 4100) // unreferenced formal parameter +#pragma warning(disable: 4189) // local variable is initialized but not referenced + +#include "../vk_mem_alloc.h" + +#pragma warning(pop) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 149445e..5dbad64 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -2649,6 +2649,29 @@ static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF; END OF CONFIGURATION */ +// TEMP ADDED + +void Crash() +{ + int* i = 0; *i = 0; +} + +FILE* g_File; +LARGE_INTEGER g_Freq, g_StartCounter; +VMA_MUTEX g_FileMutex; + +void EnsureFile() +{ + if(!g_File) + { + fopen_s(&g_File, "VMA_Usage_Dump", "wb"); + fprintf(g_File, "%s\n", "Vulkan Memory Allocator,Calls recording"); + fprintf(g_File, "%s\n", "1,0"); + QueryPerformanceFrequency(&g_Freq); + QueryPerformanceCounter(&g_StartCounter); + } +} + static VkAllocationCallbacks VmaEmptyAllocationCallbacks = { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL }; @@ -4785,6 +4808,7 @@ public: void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats); void SetCurrentFrameIndex(uint32_t frameIndex); + uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); } void MakePoolAllocationsLost( VmaPool hPool, @@ -9338,6 +9362,18 @@ VkResult vmaCreateAllocator( VMA_ASSERT(pCreateInfo && pAllocator); VMA_DEBUG_LOG("vmaCreateAllocator"); *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo); + + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = (*pAllocator)->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaCreateAllocator\n", threadId, time, frameIndex); + fflush(g_File); + } + return VK_SUCCESS; } @@ -9346,6 +9382,17 @@ void vmaDestroyAllocator( { if(allocator != VK_NULL_HANDLE) { + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaDestroyAllocator\n", threadId, time, frameIndex); + fflush(g_File); + } + VMA_DEBUG_LOG("vmaDestroyAllocator"); VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; vma_delete(&allocationCallbacks, allocator); @@ -9688,7 +9735,27 @@ VkResult vmaCreatePool( VMA_DEBUG_GLOBAL_MUTEX_LOCK - return allocator->CreatePool(pCreateInfo, pPool); + VkResult res = allocator->CreatePool(pCreateInfo, pPool); + + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", threadId, time, frameIndex, + pCreateInfo->memoryTypeIndex, + pCreateInfo->flags, + pCreateInfo->blockSize, + pCreateInfo->minBlockCount, + pCreateInfo->maxBlockCount, + pCreateInfo->frameInUseCount, + (*pPool)); + fflush(g_File); + } + + return res; } void vmaDestroyPool( @@ -9702,6 +9769,18 @@ void vmaDestroyPool( return; } + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", threadId, time, frameIndex, + pool); + fflush(g_File); + } + VMA_DEBUG_LOG("vmaDestroyPool"); VMA_DEBUG_GLOBAL_MUTEX_LOCK @@ -9751,6 +9830,7 @@ VkResult vmaAllocateMemory( VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { + Crash(); VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation); VMA_DEBUG_LOG("vmaAllocateMemory"); @@ -9782,6 +9862,7 @@ VkResult vmaAllocateMemoryForBuffer( VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { + Crash(); VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation); VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer"); @@ -9820,6 +9901,7 @@ VkResult vmaAllocateMemoryForImage( VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { + Crash(); VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation); VMA_DEBUG_LOG("vmaAllocateMemoryForImage"); @@ -9845,6 +9927,7 @@ void vmaFreeMemory( VmaAllocator allocator, VmaAllocation allocation) { + Crash(); VMA_ASSERT(allocator); VMA_DEBUG_LOG("vmaFreeMemory"); VMA_DEBUG_GLOBAL_MUTEX_LOCK @@ -9886,6 +9969,19 @@ void vmaSetAllocationUserData( VMA_DEBUG_GLOBAL_MUTEX_LOCK + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", threadId, time, frameIndex, + allocation, + (const char*)pUserData); + fflush(g_File); + } + allocation->SetUserData(allocator, pUserData); } @@ -9893,6 +9989,7 @@ void vmaCreateLostAllocation( VmaAllocator allocator, VmaAllocation* pAllocation) { + Crash(); VMA_ASSERT(allocator && pAllocation); VMA_DEBUG_GLOBAL_MUTEX_LOCK; @@ -9978,6 +10075,7 @@ VkResult vmaBindBufferMemory( VmaAllocation allocation, VkBuffer buffer) { + Crash(); VMA_ASSERT(allocator && allocation && buffer); VMA_DEBUG_LOG("vmaBindBufferMemory"); @@ -9992,6 +10090,7 @@ VkResult vmaBindImageMemory( VmaAllocation allocation, VkImage image) { + Crash(); VMA_ASSERT(allocator && allocation && image); VMA_DEBUG_LOG("vmaBindImageMemory"); @@ -10075,6 +10174,30 @@ VkResult vmaCreateBuffer( { allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } + + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, + pBufferCreateInfo->flags, + pBufferCreateInfo->size, + pBufferCreateInfo->usage, + pBufferCreateInfo->sharingMode, + pAllocationCreateInfo->flags, + pAllocationCreateInfo->usage, + pAllocationCreateInfo->requiredFlags, + pAllocationCreateInfo->preferredFlags, + pAllocationCreateInfo->memoryTypeBits, + pAllocationCreateInfo->pool, + (*pAllocation), + pAllocationCreateInfo->pUserData ? (const char*)pAllocationCreateInfo->pUserData : ""); + fflush(g_File); + } + return VK_SUCCESS; } allocator->FreeMemory(*pAllocation); @@ -10096,6 +10219,19 @@ void vmaDestroyBuffer( VmaAllocation allocation) { VMA_ASSERT(allocator); + + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", threadId, time, frameIndex, + allocation); + fflush(g_File); + } + VMA_DEBUG_LOG("vmaDestroyBuffer"); VMA_DEBUG_GLOBAL_MUTEX_LOCK if(buffer != VK_NULL_HANDLE) @@ -10153,6 +10289,39 @@ VkResult vmaCreateImage( { allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } + + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, + pImageCreateInfo->flags, + pImageCreateInfo->imageType, + pImageCreateInfo->format, + pImageCreateInfo->extent.width, + pImageCreateInfo->extent.height, + pImageCreateInfo->extent.depth, + pImageCreateInfo->mipLevels, + pImageCreateInfo->arrayLayers, + pImageCreateInfo->samples, + pImageCreateInfo->tiling, + pImageCreateInfo->usage, + pImageCreateInfo->sharingMode, + pImageCreateInfo->initialLayout, + pAllocationCreateInfo->flags, + pAllocationCreateInfo->usage, + pAllocationCreateInfo->requiredFlags, + pAllocationCreateInfo->preferredFlags, + pAllocationCreateInfo->memoryTypeBits, + pAllocationCreateInfo->pool, + (*pAllocation), + pAllocationCreateInfo->pUserData ? (const char*)pAllocationCreateInfo->pUserData : ""); + fflush(g_File); + } + return VK_SUCCESS; } allocator->FreeMemory(*pAllocation); @@ -10174,6 +10343,19 @@ void vmaDestroyImage( VmaAllocation allocation) { VMA_ASSERT(allocator); + + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", threadId, time, frameIndex, + allocation); + fflush(g_File); + } + VMA_DEBUG_LOG("vmaDestroyImage"); VMA_DEBUG_GLOBAL_MUTEX_LOCK if(image != VK_NULL_HANDLE) From 0b9bcf0340f0a35a72113455ebb27c7888aa3f58 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 7 Aug 2018 13:50:50 +0200 Subject: [PATCH 05/51] Added struct StrRange for convenience. --- src/VmaReplay/VmaReplay.cpp | 51 +++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 3fd9b4b..e69cf59 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -1762,6 +1762,14 @@ static const int RESULT_ERROR_COMMAND_LINE = -1; static const int RESULT_ERROR_SOURCE_FILE = -2; static const int RESULT_ERROR_FORMAT = -3; +struct StrRange +{ + const char* beg; + const char* end; + + size_t length() const { return end - beg; } +}; + //////////////////////////////////////////////////////////////////////////////// // LineSplit class @@ -1776,7 +1784,7 @@ public: { } - bool GetNextLine(const char *&outBeg, const char*&outEnd); + bool GetNextLine(StrRange& out); size_t GetNextLineIndex() const { return m_NextLineIndex; } private: @@ -1786,15 +1794,15 @@ private: size_t m_NextLineIndex; }; -bool LineSplit::GetNextLine(const char *&outBeg, const char*&outEnd) +bool LineSplit::GetNextLine(StrRange& out) { if(m_NextLineBeg < m_NumBytes) { - outBeg = m_Data + m_NextLineBeg; + out.beg = m_Data + m_NextLineBeg; size_t currLineEnd = m_NextLineBeg; while(currLineEnd < m_NumBytes && m_Data[currLineEnd] != '\n') ++currLineEnd; - outEnd = m_Data + currLineEnd; + out.end = m_Data + currLineEnd; m_NextLineBeg = currLineEnd + 1; // Past '\n' ++m_NextLineIndex; return true; @@ -1812,7 +1820,7 @@ class CsvSplit public: static const size_t RANGE_COUNT_MAX = 32; - void Set(const char* beg, const char* end, size_t maxCount = RANGE_COUNT_MAX); + void Set(const StrRange& line, size_t maxCount = RANGE_COUNT_MAX); size_t GetCount() const { return m_Count; } void GetRange(size_t index, const char*& outBeg, const char*& outEnd) const @@ -1827,11 +1835,11 @@ private: size_t m_Ranges[RANGE_COUNT_MAX * 2]; // Pairs of begin-end. }; -void CsvSplit::Set(const char* beg, const char* end, size_t maxCount) +void CsvSplit::Set(const StrRange& line, size_t maxCount) { assert(maxCount <= RANGE_COUNT_MAX); - m_Str = beg; - const size_t strLen = end - beg; + m_Str = line.beg; + const size_t strLen = line.length(); size_t rangeIndex = 0; size_t charIndex = 0; while(charIndex < strLen && rangeIndex < maxCount) @@ -1856,7 +1864,7 @@ public: void Init(); ~Player(); - void ExecuteLine(size_t lineNumber, const char* lineBeg, const char* lineEnd); + void ExecuteLine(size_t lineNumber, const StrRange& line); private: static const size_t MAX_WARNINGS_TO_SHOW = 16; @@ -1881,10 +1889,10 @@ Player::~Player() printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW); } -void Player::ExecuteLine(size_t lineNumber, const char* lineBeg, const char* lineEnd) +void Player::ExecuteLine(size_t lineNumber, const StrRange& line) { CsvSplit csvSplit; - csvSplit.Set(lineBeg, lineEnd); + csvSplit.Set(line); if(csvSplit.GetCount() >= 4) { @@ -1906,11 +1914,11 @@ static void PrintCommandLineSyntax() " VmaReplay \n"); } -static inline bool StrRangeEq(const char* lhsBeg, const char* lhsEnd, const char* rhsSz) +static inline bool StrRangeEq(const StrRange& lhs, const char* rhsSz) { const size_t rhsLen = strlen(rhsSz); - return rhsLen == lhsEnd - lhsBeg && - memcmp(lhsBeg, rhsSz, rhsLen) == 0; + return rhsLen == lhs.length() && + memcmp(lhs.beg, rhsSz, rhsLen) == 0; } static int ProcessFile(const char* data, size_t numBytes) @@ -1919,18 +1927,17 @@ static int ProcessFile(const char* data, size_t numBytes) printf("File size: %zu B\n", numBytes); LineSplit lineSplit(data, numBytes); - const char* lineBeg; - const char* lineEnd; + StrRange line; - if(!lineSplit.GetNextLine(lineBeg, lineEnd) || - !StrRangeEq(lineBeg, lineEnd, "Vulkan Memory Allocator,Calls recording")) + if(!lineSplit.GetNextLine(line) || + !StrRangeEq(line, "Vulkan Memory Allocator,Calls recording")) { printf("ERROR: Incorrect file format.\n"); return RESULT_ERROR_FORMAT; } - if(!lineSplit.GetNextLine(lineBeg, lineEnd) || - !StrRangeEq(lineBeg, lineEnd, "1,0")) + if(!lineSplit.GetNextLine(line) || + !StrRangeEq(line, "1,0")) { printf("ERROR: Incorrect file format version.\n"); return RESULT_ERROR_FORMAT; @@ -1939,9 +1946,9 @@ static int ProcessFile(const char* data, size_t numBytes) Player player; player.Init(); - while(lineSplit.GetNextLine(lineBeg, lineEnd)) + while(lineSplit.GetNextLine(line)) { - player.ExecuteLine(lineSplit.GetNextLineIndex(), lineBeg, lineEnd); + player.ExecuteLine(lineSplit.GetNextLineIndex(), line); } // End stats. From 9d7d848658563778f9dd94a21edc46800eb7fd36 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 7 Aug 2018 14:13:06 +0200 Subject: [PATCH 06/51] VmaReplay: Initialized Vulkan. --- src/VmaReplay/Common.h | 7 - src/VmaReplay/VmaReplay.cpp | 2012 +++++------------------------------ 2 files changed, 278 insertions(+), 1741 deletions(-) diff --git a/src/VmaReplay/Common.h b/src/VmaReplay/Common.h index c4934b2..70e7dad 100644 --- a/src/VmaReplay/Common.h +++ b/src/VmaReplay/Common.h @@ -24,11 +24,6 @@ typedef std::chrono::high_resolution_clock::duration duration; #define ERR_GUARD_VULKAN(Expr) do { VkResult res__ = (Expr); if (res__ < 0) assert(0); } while(0) -extern VkPhysicalDevice g_hPhysicalDevice; -extern VkDevice g_hDevice; -extern VmaAllocator g_hAllocator; -extern bool g_MemoryAliasingWarningEnabled; - inline float ToFloatSeconds(duration d) { return std::chrono::duration_cast>(d).count(); @@ -46,8 +41,6 @@ static inline T align_up(T val, T align) return (val + align - 1) / align * align; } -static const float PI = 3.14159265358979323846264338327950288419716939937510582f; - class RandomNumberGenerator { public: diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index e69cf59..09e38f7 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -23,1740 +23,6 @@ #include "VmaUsage.h" #include "Common.h" -#if 0 -static const char* const SHADER_PATH1 = "./"; -static const char* const SHADER_PATH2 = "../bin/"; -static const wchar_t* const WINDOW_CLASS_NAME = L"VULKAN_MEMORY_ALLOCATOR_SAMPLE"; -static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_LUNARG_standard_validation"; -static const char* const APP_TITLE_A = "Vulkan Memory Allocator Sample 2.0"; -static const wchar_t* const APP_TITLE_W = L"Vulkan Memory Allocator Sample 2.0"; - -static const bool VSYNC = true; -static const uint32_t COMMAND_BUFFER_COUNT = 2; -static void* const CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA = (void*)(intptr_t)43564544; -static const bool USE_CUSTOM_CPU_ALLOCATION_CALLBACKS = false; - -VkPhysicalDevice g_hPhysicalDevice; -VkDevice g_hDevice; -VmaAllocator g_hAllocator; -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 HINSTANCE g_hAppInstance; -static HWND g_hWnd; -static LONG g_SizeX = 1280, g_SizeY = 720; -static VkInstance g_hVulkanInstance; -static VkSurfaceKHR g_hSurface; -static VkQueue g_hPresentQueue; -static VkSurfaceFormatKHR g_SurfaceFormat; -static VkExtent2D g_Extent; -static VkSwapchainKHR g_hSwapchain; -static std::vector g_SwapchainImages; -static std::vector g_SwapchainImageViews; -static std::vector g_Framebuffers; -static VkCommandPool g_hCommandPool; -static VkCommandBuffer g_MainCommandBuffers[COMMAND_BUFFER_COUNT]; -static VkFence g_MainCommandBufferExecutedFances[COMMAND_BUFFER_COUNT]; -static uint32_t g_NextCommandBufferIndex; -static VkSemaphore g_hImageAvailableSemaphore; -static VkSemaphore g_hRenderFinishedSemaphore; -static uint32_t g_GraphicsQueueFamilyIndex = UINT_MAX; -static uint32_t g_PresentQueueFamilyIndex = UINT_MAX; -static VkDescriptorSetLayout g_hDescriptorSetLayout; -static VkDescriptorPool g_hDescriptorPool; -static VkDescriptorSet g_hDescriptorSet; // Automatically destroyed with m_DescriptorPool. -static VkSampler g_hSampler; -static VkFormat g_DepthFormat; -static VkImage g_hDepthImage; -static VmaAllocation g_hDepthImageAlloc; -static VkImageView g_hDepthImageView; - -static VkSurfaceCapabilitiesKHR g_SurfaceCapabilities; -static std::vector g_SurfaceFormats; -static std::vector g_PresentModes; - -static PFN_vkCreateDebugReportCallbackEXT g_pvkCreateDebugReportCallbackEXT; -static PFN_vkDebugReportMessageEXT g_pvkDebugReportMessageEXT; -static PFN_vkDestroyDebugReportCallbackEXT g_pvkDestroyDebugReportCallbackEXT; -static VkDebugReportCallbackEXT g_hCallback; - -static VkQueue g_hGraphicsQueue; -static VkCommandBuffer g_hTemporaryCommandBuffer; - -static VkPipelineLayout g_hPipelineLayout; -static VkRenderPass g_hRenderPass; -static VkPipeline g_hPipeline; - -static VkBuffer g_hVertexBuffer; -static VmaAllocation g_hVertexBufferAlloc; -static VkBuffer g_hIndexBuffer; -static VmaAllocation g_hIndexBufferAlloc; -static uint32_t g_VertexCount; -static uint32_t g_IndexCount; - -static VkImage g_hTextureImage; -static VmaAllocation g_hTextureImageAlloc; -static VkImageView g_hTextureImageView; - -static void* CustomCpuAllocation( - void* pUserData, size_t size, size_t alignment, - VkSystemAllocationScope allocationScope) -{ - assert(pUserData == CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA); - return _aligned_malloc(size, alignment); -} - -static void* CustomCpuReallocation( - void* pUserData, void* pOriginal, size_t size, size_t alignment, - VkSystemAllocationScope allocationScope) -{ - assert(pUserData == CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA); - return _aligned_realloc(pOriginal, size, alignment); -} - -static void CustomCpuFree(void* pUserData, void* pMemory) -{ - assert(pUserData == CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA); - _aligned_free(pMemory); -} - -static void BeginSingleTimeCommands() -{ - VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - ERR_GUARD_VULKAN( vkBeginCommandBuffer(g_hTemporaryCommandBuffer, &cmdBufBeginInfo) ); -} - -static void EndSingleTimeCommands() -{ - ERR_GUARD_VULKAN( vkEndCommandBuffer(g_hTemporaryCommandBuffer) ); - - VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &g_hTemporaryCommandBuffer; - - ERR_GUARD_VULKAN( vkQueueSubmit(g_hGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) ); - ERR_GUARD_VULKAN( vkQueueWaitIdle(g_hGraphicsQueue) ); -} - -static void LoadShader(std::vector& out, const char* fileName) -{ - std::ifstream file(std::string(SHADER_PATH1) + fileName, std::ios::ate | std::ios::binary); - if(file.is_open() == false) - file.open(std::string(SHADER_PATH2) + fileName, std::ios::ate | std::ios::binary); - assert(file.is_open()); - size_t fileSize = (size_t)file.tellg(); - if(fileSize > 0) - { - out.resize(fileSize); - file.seekg(0); - file.read(out.data(), fileSize); - file.close(); - } - else - out.clear(); -} - -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; - } - - switch(flags) - { - case VK_DEBUG_REPORT_WARNING_BIT_EXT: - SetConsoleColor(CONSOLE_COLOR::WARNING); - break; - case VK_DEBUG_REPORT_ERROR_BIT_EXT: - SetConsoleColor(CONSOLE_COLOR::ERROR_); - break; - default: - SetConsoleColor(CONSOLE_COLOR::INFO); - } - - printf("%s \xBA %s\n", pLayerPrefix, pMessage); - - SetConsoleColor(CONSOLE_COLOR::NORMAL); - - if(flags == VK_DEBUG_REPORT_WARNING_BIT_EXT || - flags == VK_DEBUG_REPORT_ERROR_BIT_EXT) - { - OutputDebugStringA(pMessage); - OutputDebugStringA("\n"); - } - - return VK_FALSE; -} - -static VkSurfaceFormatKHR ChooseSurfaceFormat() -{ - assert(!g_SurfaceFormats.empty()); - - if((g_SurfaceFormats.size() == 1) && (g_SurfaceFormats[0].format == VK_FORMAT_UNDEFINED)) - { - VkSurfaceFormatKHR result = { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; - return result; - } - - for(const auto& format : g_SurfaceFormats) - { - if((format.format == VK_FORMAT_B8G8R8A8_UNORM) && - (format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)) - { - return format; - } - } - - return g_SurfaceFormats[0]; -} - -VkPresentModeKHR ChooseSwapPresentMode() -{ - VkPresentModeKHR preferredMode = VSYNC ? VK_PRESENT_MODE_MAILBOX_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR; - - if(std::find(g_PresentModes.begin(), g_PresentModes.end(), preferredMode) != - g_PresentModes.end()) - { - return preferredMode; - } - - return VK_PRESENT_MODE_FIFO_KHR; -} - -static VkExtent2D ChooseSwapExtent() -{ - if(g_SurfaceCapabilities.currentExtent.width != UINT_MAX) - return g_SurfaceCapabilities.currentExtent; - - VkExtent2D result = { - std::max(g_SurfaceCapabilities.minImageExtent.width, - std::min(g_SurfaceCapabilities.maxImageExtent.width, (uint32_t)g_SizeX)), - std::max(g_SurfaceCapabilities.minImageExtent.height, - std::min(g_SurfaceCapabilities.maxImageExtent.height, (uint32_t)g_SizeY)) }; - return result; -} - -struct Vertex -{ - float pos[3]; - float color[3]; - float texCoord[2]; -}; - -static void CreateMesh() -{ - assert(g_hAllocator); - - static Vertex vertices[] = { - // -X - { { -1.f, -1.f, -1.f}, {1.0f, 0.0f, 0.0f}, {0.f, 0.f} }, - { { -1.f, -1.f, 1.f}, {1.0f, 0.0f, 0.0f}, {1.f, 0.f} }, - { { -1.f, 1.f, -1.f}, {1.0f, 0.0f, 0.0f}, {0.f, 1.f} }, - { { -1.f, 1.f, 1.f}, {1.0f, 0.0f, 0.0f}, {1.f, 1.f} }, - // +X - { { 1.f, -1.f, 1.f}, {0.0f, 1.0f, 0.0f}, {0.f, 0.f} }, - { { 1.f, -1.f, -1.f}, {0.0f, 1.0f, 0.0f}, {1.f, 0.f} }, - { { 1.f, 1.f, 1.f}, {0.0f, 1.0f, 0.0f}, {0.f, 1.f} }, - { { 1.f, 1.f, -1.f}, {0.0f, 1.0f, 0.0f}, {1.f, 1.f} }, - // -Z - { { 1.f, -1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {0.f, 0.f} }, - { {-1.f, -1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {1.f, 0.f} }, - { { 1.f, 1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {0.f, 1.f} }, - { {-1.f, 1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {1.f, 1.f} }, - // +Z - { {-1.f, -1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {0.f, 0.f} }, - { { 1.f, -1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {1.f, 0.f} }, - { {-1.f, 1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {0.f, 1.f} }, - { { 1.f, 1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {1.f, 1.f} }, - // -Y - { {-1.f, -1.f, -1.f}, {0.0f, 1.0f, 1.0f}, {0.f, 0.f} }, - { { 1.f, -1.f, -1.f}, {0.0f, 1.0f, 1.0f}, {1.f, 0.f} }, - { {-1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f}, {0.f, 1.f} }, - { { 1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f}, {1.f, 1.f} }, - // +Y - { { 1.f, 1.f, -1.f}, {1.0f, 0.0f, 1.0f}, {0.f, 0.f} }, - { {-1.f, 1.f, -1.f}, {1.0f, 0.0f, 1.0f}, {1.f, 0.f} }, - { { 1.f, 1.f, 1.f}, {1.0f, 0.0f, 1.0f}, {0.f, 1.f} }, - { {-1.f, 1.f, 1.f}, {1.0f, 0.0f, 1.0f}, {1.f, 1.f} }, - }; - static uint16_t indices[] = { - 0, 1, 2, 3, USHRT_MAX, - 4, 5, 6, 7, USHRT_MAX, - 8, 9, 10, 11, USHRT_MAX, - 12, 13, 14, 15, USHRT_MAX, - 16, 17, 18, 19, USHRT_MAX, - 20, 21, 22, 23, USHRT_MAX, - }; - - size_t vertexBufferSize = sizeof(Vertex) * _countof(vertices); - size_t indexBufferSize = sizeof(uint16_t) * _countof(indices); - g_IndexCount = (uint32_t)_countof(indices); - - // Create vertex buffer - - VkBufferCreateInfo vbInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - vbInfo.size = vertexBufferSize; - vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - vbInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VmaAllocationCreateInfo vbAllocCreateInfo = {}; - vbAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; - vbAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - - VkBuffer stagingVertexBuffer = VK_NULL_HANDLE; - VmaAllocation stagingVertexBufferAlloc = VK_NULL_HANDLE; - VmaAllocationInfo stagingVertexBufferAllocInfo = {}; - ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbAllocCreateInfo, &stagingVertexBuffer, &stagingVertexBufferAlloc, &stagingVertexBufferAllocInfo) ); - - memcpy(stagingVertexBufferAllocInfo.pMappedData, vertices, vertexBufferSize); - - // No need to flush stagingVertexBuffer memory because CPU_ONLY memory is always HOST_COHERENT. - - vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - vbAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - vbAllocCreateInfo.flags = 0; - ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbAllocCreateInfo, &g_hVertexBuffer, &g_hVertexBufferAlloc, nullptr) ); - - // Create index buffer - - VkBufferCreateInfo ibInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - ibInfo.size = indexBufferSize; - ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - ibInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VmaAllocationCreateInfo ibAllocCreateInfo = {}; - ibAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; - ibAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - - VkBuffer stagingIndexBuffer = VK_NULL_HANDLE; - VmaAllocation stagingIndexBufferAlloc = VK_NULL_HANDLE; - VmaAllocationInfo stagingIndexBufferAllocInfo = {}; - ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibAllocCreateInfo, &stagingIndexBuffer, &stagingIndexBufferAlloc, &stagingIndexBufferAllocInfo) ); - - memcpy(stagingIndexBufferAllocInfo.pMappedData, indices, indexBufferSize); - - // No need to flush stagingIndexBuffer memory because CPU_ONLY memory is always HOST_COHERENT. - - ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - ibAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - ibAllocCreateInfo.flags = 0; - ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibAllocCreateInfo, &g_hIndexBuffer, &g_hIndexBufferAlloc, nullptr) ); - - // Copy buffers - - BeginSingleTimeCommands(); - - VkBufferCopy vbCopyRegion = {}; - vbCopyRegion.srcOffset = 0; - vbCopyRegion.dstOffset = 0; - vbCopyRegion.size = vbInfo.size; - vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingVertexBuffer, g_hVertexBuffer, 1, &vbCopyRegion); - - VkBufferCopy ibCopyRegion = {}; - ibCopyRegion.srcOffset = 0; - ibCopyRegion.dstOffset = 0; - ibCopyRegion.size = ibInfo.size; - vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingIndexBuffer, g_hIndexBuffer, 1, &ibCopyRegion); - - EndSingleTimeCommands(); - - vmaDestroyBuffer(g_hAllocator, stagingIndexBuffer, stagingIndexBufferAlloc); - vmaDestroyBuffer(g_hAllocator, stagingVertexBuffer, stagingVertexBufferAlloc); -} - -static void CreateTexture(uint32_t sizeX, uint32_t sizeY) -{ - // Create Image - - const VkDeviceSize imageSize = sizeX * sizeY * 4; - - VkImageCreateInfo stagingImageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - stagingImageInfo.imageType = VK_IMAGE_TYPE_2D; - stagingImageInfo.extent.width = sizeX; - stagingImageInfo.extent.height = sizeY; - stagingImageInfo.extent.depth = 1; - stagingImageInfo.mipLevels = 1; - stagingImageInfo.arrayLayers = 1; - stagingImageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; - stagingImageInfo.tiling = VK_IMAGE_TILING_LINEAR; - stagingImageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; - stagingImageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - stagingImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - stagingImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - stagingImageInfo.flags = 0; - - VmaAllocationCreateInfo stagingImageAllocCreateInfo = {}; - stagingImageAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; - stagingImageAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - - VkImage stagingImage = VK_NULL_HANDLE; - VmaAllocation stagingImageAlloc = VK_NULL_HANDLE; - VmaAllocationInfo stagingImageAllocInfo = {}; - ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &stagingImageInfo, &stagingImageAllocCreateInfo, &stagingImage, &stagingImageAlloc, &stagingImageAllocInfo) ); - - VkImageSubresource imageSubresource = {}; - imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageSubresource.mipLevel = 0; - imageSubresource.arrayLayer = 0; - - VkSubresourceLayout imageLayout = {}; - vkGetImageSubresourceLayout(g_hDevice, stagingImage, &imageSubresource, &imageLayout); - - char* const pMipLevelData = (char*)stagingImageAllocInfo.pMappedData + imageLayout.offset; - uint8_t* pRowData = (uint8_t*)pMipLevelData; - for(uint32_t y = 0; y < sizeY; ++y) - { - uint32_t* pPixelData = (uint32_t*)pRowData; - for(uint32_t x = 0; x < sizeY; ++x) - { - *pPixelData = - ((x & 0x18) == 0x08 ? 0x000000FF : 0x00000000) | - ((x & 0x18) == 0x10 ? 0x0000FFFF : 0x00000000) | - ((y & 0x18) == 0x08 ? 0x0000FF00 : 0x00000000) | - ((y & 0x18) == 0x10 ? 0x00FF0000 : 0x00000000); - ++pPixelData; - } - pRowData += imageLayout.rowPitch; - } - - // No need to flush stagingImage memory because CPU_ONLY memory is always HOST_COHERENT. - - // Create g_hTextureImage in GPU memory. - - VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.extent.width = sizeX; - imageInfo.extent.height = sizeY; - imageInfo.extent.depth = 1; - imageInfo.mipLevels = 1; - imageInfo.arrayLayers = 1; - imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; - imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.flags = 0; - - VmaAllocationCreateInfo imageAllocCreateInfo = {}; - imageAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - - ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &imageInfo, &imageAllocCreateInfo, &g_hTextureImage, &g_hTextureImageAlloc, nullptr) ); - - // Transition image layouts, copy image. - - BeginSingleTimeCommands(); - - VkImageMemoryBarrier imgMemBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - imgMemBarrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; - imgMemBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - imgMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - imgMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - imgMemBarrier.image = stagingImage; - imgMemBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imgMemBarrier.subresourceRange.baseMipLevel = 0; - imgMemBarrier.subresourceRange.levelCount = 1; - imgMemBarrier.subresourceRange.baseArrayLayer = 0; - imgMemBarrier.subresourceRange.layerCount = 1; - imgMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; - imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; - - vkCmdPipelineBarrier( - g_hTemporaryCommandBuffer, - VK_PIPELINE_STAGE_HOST_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, - 0, nullptr, - 0, nullptr, - 1, &imgMemBarrier); - - imgMemBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imgMemBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imgMemBarrier.image = g_hTextureImage; - imgMemBarrier.srcAccessMask = 0; - imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - - vkCmdPipelineBarrier( - g_hTemporaryCommandBuffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, - 0, nullptr, - 0, nullptr, - 1, &imgMemBarrier); - - VkImageCopy imageCopy = {}; - imageCopy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageCopy.srcSubresource.baseArrayLayer = 0; - imageCopy.srcSubresource.mipLevel = 0; - imageCopy.srcSubresource.layerCount = 1; - imageCopy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageCopy.dstSubresource.baseArrayLayer = 0; - imageCopy.dstSubresource.mipLevel = 0; - imageCopy.dstSubresource.layerCount = 1; - imageCopy.srcOffset.x = 0; - imageCopy.srcOffset.y = 0; - imageCopy.srcOffset.z = 0; - imageCopy.dstOffset.x = 0; - imageCopy.dstOffset.y = 0; - imageCopy.dstOffset.z = 0; - imageCopy.extent.width = sizeX; - imageCopy.extent.height = sizeY; - imageCopy.extent.depth = 1; - vkCmdCopyImage( - g_hTemporaryCommandBuffer, - stagingImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - g_hTextureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, &imageCopy); - - imgMemBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imgMemBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imgMemBarrier.image = g_hTextureImage; - imgMemBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - imgMemBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - vkCmdPipelineBarrier( - g_hTemporaryCommandBuffer, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - 0, - 0, nullptr, - 0, nullptr, - 1, &imgMemBarrier); - - EndSingleTimeCommands(); - - vmaDestroyImage(g_hAllocator, stagingImage, stagingImageAlloc); - - // Create ImageView - - VkImageViewCreateInfo textureImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - textureImageViewInfo.image = g_hTextureImage; - textureImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - textureImageViewInfo.format = VK_FORMAT_R8G8B8A8_UNORM; - textureImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - textureImageViewInfo.subresourceRange.baseMipLevel = 0; - textureImageViewInfo.subresourceRange.levelCount = 1; - textureImageViewInfo.subresourceRange.baseArrayLayer = 0; - textureImageViewInfo.subresourceRange.layerCount = 1; - ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &textureImageViewInfo, nullptr, &g_hTextureImageView) ); -} - -struct UniformBufferObject -{ - mat4 ModelViewProj; -}; - -static void RegisterDebugCallbacks() -{ - g_pvkCreateDebugReportCallbackEXT = - reinterpret_cast - (vkGetInstanceProcAddr(g_hVulkanInstance, "vkCreateDebugReportCallbackEXT")); - g_pvkDebugReportMessageEXT = - reinterpret_cast - (vkGetInstanceProcAddr(g_hVulkanInstance, "vkDebugReportMessageEXT")); - g_pvkDestroyDebugReportCallbackEXT = - reinterpret_cast - (vkGetInstanceProcAddr(g_hVulkanInstance, "vkDestroyDebugReportCallbackEXT")); - assert(g_pvkCreateDebugReportCallbackEXT); - assert(g_pvkDebugReportMessageEXT); - assert(g_pvkDestroyDebugReportCallbackEXT); - - VkDebugReportCallbackCreateInfoEXT callbackCreateInfo; - callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; - callbackCreateInfo.pNext = nullptr; - 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; - callbackCreateInfo.pUserData = nullptr; - - ERR_GUARD_VULKAN( g_pvkCreateDebugReportCallbackEXT(g_hVulkanInstance, &callbackCreateInfo, nullptr, &g_hCallback) ); -} - -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 VkFormat FindSupportedFormat( - const std::vector& candidates, - VkImageTiling tiling, - VkFormatFeatureFlags features) -{ - for (VkFormat format : candidates) - { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(g_hPhysicalDevice, format, &props); - - if ((tiling == VK_IMAGE_TILING_LINEAR) && - ((props.linearTilingFeatures & features) == features)) - { - return format; - } - else if ((tiling == VK_IMAGE_TILING_OPTIMAL) && - ((props.optimalTilingFeatures & features) == features)) - { - return format; - } - } - return VK_FORMAT_UNDEFINED; -} - -static VkFormat FindDepthFormat() -{ - std::vector formats; - formats.push_back(VK_FORMAT_D32_SFLOAT); - formats.push_back(VK_FORMAT_D32_SFLOAT_S8_UINT); - formats.push_back(VK_FORMAT_D24_UNORM_S8_UINT); - - return FindSupportedFormat( - formats, - VK_IMAGE_TILING_OPTIMAL, - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); -} - -static void CreateSwapchain() -{ - // Query surface formats. - - ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_hPhysicalDevice, g_hSurface, &g_SurfaceCapabilities) ); - - uint32_t formatCount = 0; - ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceFormatsKHR(g_hPhysicalDevice, g_hSurface, &formatCount, nullptr) ); - g_SurfaceFormats.resize(formatCount); - ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceFormatsKHR(g_hPhysicalDevice, g_hSurface, &formatCount, g_SurfaceFormats.data()) ); - - uint32_t presentModeCount = 0; - ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfacePresentModesKHR(g_hPhysicalDevice, g_hSurface, &presentModeCount, nullptr) ); - g_PresentModes.resize(presentModeCount); - ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfacePresentModesKHR(g_hPhysicalDevice, g_hSurface, &presentModeCount, g_PresentModes.data()) ); - - // Create swap chain - - g_SurfaceFormat = ChooseSurfaceFormat(); - VkPresentModeKHR presentMode = ChooseSwapPresentMode(); - g_Extent = ChooseSwapExtent(); - - uint32_t imageCount = g_SurfaceCapabilities.minImageCount + 1; - if((g_SurfaceCapabilities.maxImageCount > 0) && - (imageCount > g_SurfaceCapabilities.maxImageCount)) - { - imageCount = g_SurfaceCapabilities.maxImageCount; - } - - VkSwapchainCreateInfoKHR swapChainInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; - swapChainInfo.surface = g_hSurface; - swapChainInfo.minImageCount = imageCount; - swapChainInfo.imageFormat = g_SurfaceFormat.format; - swapChainInfo.imageColorSpace = g_SurfaceFormat.colorSpace; - swapChainInfo.imageExtent = g_Extent; - swapChainInfo.imageArrayLayers = 1; - swapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - swapChainInfo.preTransform = g_SurfaceCapabilities.currentTransform; - swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swapChainInfo.presentMode = presentMode; - swapChainInfo.clipped = VK_TRUE; - swapChainInfo.oldSwapchain = g_hSwapchain; - - uint32_t queueFamilyIndices[] = { g_GraphicsQueueFamilyIndex, g_PresentQueueFamilyIndex }; - if(g_PresentQueueFamilyIndex != g_GraphicsQueueFamilyIndex) - { - swapChainInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - swapChainInfo.queueFamilyIndexCount = 2; - swapChainInfo.pQueueFamilyIndices = queueFamilyIndices; - } - else - { - swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - } - - VkSwapchainKHR hNewSwapchain = VK_NULL_HANDLE; - ERR_GUARD_VULKAN( vkCreateSwapchainKHR(g_hDevice, &swapChainInfo, nullptr, &hNewSwapchain) ); - if(g_hSwapchain != VK_NULL_HANDLE) - vkDestroySwapchainKHR(g_hDevice, g_hSwapchain, nullptr); - g_hSwapchain = hNewSwapchain; - - // Retrieve swapchain images. - - uint32_t swapchainImageCount = 0; - ERR_GUARD_VULKAN( vkGetSwapchainImagesKHR(g_hDevice, g_hSwapchain, &swapchainImageCount, nullptr) ); - g_SwapchainImages.resize(swapchainImageCount); - ERR_GUARD_VULKAN( vkGetSwapchainImagesKHR(g_hDevice, g_hSwapchain, &swapchainImageCount, g_SwapchainImages.data()) ); - - // Create swapchain image views. - - for(size_t i = g_SwapchainImageViews.size(); i--; ) - vkDestroyImageView(g_hDevice, g_SwapchainImageViews[i], nullptr); - g_SwapchainImageViews.clear(); - - VkImageViewCreateInfo swapchainImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - g_SwapchainImageViews.resize(swapchainImageCount); - for(uint32_t i = 0; i < swapchainImageCount; ++i) - { - swapchainImageViewInfo.image = g_SwapchainImages[i]; - swapchainImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - swapchainImageViewInfo.format = g_SurfaceFormat.format; - swapchainImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - swapchainImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - swapchainImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - swapchainImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - swapchainImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - swapchainImageViewInfo.subresourceRange.baseMipLevel = 0; - swapchainImageViewInfo.subresourceRange.levelCount = 1; - swapchainImageViewInfo.subresourceRange.baseArrayLayer = 0; - swapchainImageViewInfo.subresourceRange.layerCount = 1; - ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &swapchainImageViewInfo, nullptr, &g_SwapchainImageViews[i]) ); - } - - // Create depth buffer - - g_DepthFormat = FindDepthFormat(); - assert(g_DepthFormat != VK_FORMAT_UNDEFINED); - - VkImageCreateInfo depthImageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - depthImageInfo.imageType = VK_IMAGE_TYPE_2D; - depthImageInfo.extent.width = g_Extent.width; - depthImageInfo.extent.height = g_Extent.height; - depthImageInfo.extent.depth = 1; - depthImageInfo.mipLevels = 1; - depthImageInfo.arrayLayers = 1; - depthImageInfo.format = g_DepthFormat; - depthImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - depthImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - depthImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - depthImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - depthImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - depthImageInfo.flags = 0; - - VmaAllocationCreateInfo depthImageAllocCreateInfo = {}; - depthImageAllocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - - ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &depthImageInfo, &depthImageAllocCreateInfo, &g_hDepthImage, &g_hDepthImageAlloc, nullptr) ); - - VkImageViewCreateInfo depthImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - depthImageViewInfo.image = g_hDepthImage; - depthImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - depthImageViewInfo.format = g_DepthFormat; - depthImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - depthImageViewInfo.subresourceRange.baseMipLevel = 0; - depthImageViewInfo.subresourceRange.levelCount = 1; - depthImageViewInfo.subresourceRange.baseArrayLayer = 0; - depthImageViewInfo.subresourceRange.layerCount = 1; - - ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &depthImageViewInfo, nullptr, &g_hDepthImageView) ); - - // Create pipeline layout - { - if(g_hPipelineLayout != VK_NULL_HANDLE) - { - vkDestroyPipelineLayout(g_hDevice, g_hPipelineLayout, nullptr); - g_hPipelineLayout = VK_NULL_HANDLE; - } - - VkPushConstantRange pushConstantRanges[1]; - ZeroMemory(&pushConstantRanges, sizeof pushConstantRanges); - pushConstantRanges[0].offset = 0; - pushConstantRanges[0].size = sizeof(UniformBufferObject); - pushConstantRanges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - - VkDescriptorSetLayout descriptorSetLayouts[] = { g_hDescriptorSetLayout }; - VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; - pipelineLayoutInfo.setLayoutCount = 1; - pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts; - pipelineLayoutInfo.pushConstantRangeCount = 1; - pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges; - ERR_GUARD_VULKAN( vkCreatePipelineLayout(g_hDevice, &pipelineLayoutInfo, nullptr, &g_hPipelineLayout) ); - } - - // Create render pass - { - if(g_hRenderPass != VK_NULL_HANDLE) - { - vkDestroyRenderPass(g_hDevice, g_hRenderPass, nullptr); - g_hRenderPass = VK_NULL_HANDLE; - } - - VkAttachmentDescription attachments[2]; - ZeroMemory(attachments, sizeof(attachments)); - - attachments[0].format = g_SurfaceFormat.format; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - attachments[1].format = g_DepthFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkAttachmentReference colorAttachmentRef = {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkAttachmentReference depthStencilAttachmentRef = {}; - depthStencilAttachmentRef.attachment = 1; - depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpassDesc = {}; - subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDesc.colorAttachmentCount = 1; - subpassDesc.pColorAttachments = &colorAttachmentRef; - subpassDesc.pDepthStencilAttachment = &depthStencilAttachmentRef; - - VkRenderPassCreateInfo renderPassInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - renderPassInfo.attachmentCount = (uint32_t)_countof(attachments); - renderPassInfo.pAttachments = attachments; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpassDesc; - renderPassInfo.dependencyCount = 0; - ERR_GUARD_VULKAN( vkCreateRenderPass(g_hDevice, &renderPassInfo, nullptr, &g_hRenderPass) ); - } - - // Create pipeline - { - std::vector vertShaderCode; - LoadShader(vertShaderCode, "Shader.vert.spv"); - VkShaderModuleCreateInfo shaderModuleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; - shaderModuleInfo.codeSize = vertShaderCode.size(); - shaderModuleInfo.pCode = (const uint32_t*)vertShaderCode.data(); - VkShaderModule hVertShaderModule = VK_NULL_HANDLE; - ERR_GUARD_VULKAN( vkCreateShaderModule(g_hDevice, &shaderModuleInfo, nullptr, &hVertShaderModule) ); - - std::vector hFragShaderCode; - LoadShader(hFragShaderCode, "Shader.frag.spv"); - shaderModuleInfo.codeSize = hFragShaderCode.size(); - shaderModuleInfo.pCode = (const uint32_t*)hFragShaderCode.data(); - VkShaderModule fragShaderModule = VK_NULL_HANDLE; - ERR_GUARD_VULKAN( vkCreateShaderModule(g_hDevice, &shaderModuleInfo, nullptr, &fragShaderModule) ); - - VkPipelineShaderStageCreateInfo vertPipelineShaderStageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; - vertPipelineShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertPipelineShaderStageInfo.module = hVertShaderModule; - vertPipelineShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo fragPipelineShaderStageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO }; - fragPipelineShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragPipelineShaderStageInfo.module = fragShaderModule; - fragPipelineShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo pipelineShaderStageInfos[] = { - vertPipelineShaderStageInfo, - fragPipelineShaderStageInfo - }; - - VkVertexInputBindingDescription bindingDescription = {}; - bindingDescription.binding = 0; - bindingDescription.stride = sizeof(Vertex); - bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - VkVertexInputAttributeDescription attributeDescriptions[3]; - ZeroMemory(attributeDescriptions, sizeof(attributeDescriptions)); - - attributeDescriptions[0].binding = 0; - attributeDescriptions[0].location = 0; - attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[0].offset = offsetof(Vertex, pos); - - attributeDescriptions[1].binding = 0; - attributeDescriptions[1].location = 1; - attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[1].offset = offsetof(Vertex, color); - - attributeDescriptions[2].binding = 0; - attributeDescriptions[2].location = 2; - attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; - attributeDescriptions[2].offset = offsetof(Vertex, texCoord); - - VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; - pipelineVertexInputStateInfo.vertexBindingDescriptionCount = 1; - pipelineVertexInputStateInfo.pVertexBindingDescriptions = &bindingDescription; - pipelineVertexInputStateInfo.vertexAttributeDescriptionCount = _countof(attributeDescriptions); - pipelineVertexInputStateInfo.pVertexAttributeDescriptions = attributeDescriptions; - - VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; - pipelineInputAssemblyStateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; - pipelineInputAssemblyStateInfo.primitiveRestartEnable = VK_TRUE; - - VkViewport viewport = {}; - viewport.x = 0.f; - viewport.y = 0.f; - viewport.width = (float)g_Extent.width; - viewport.height = (float)g_Extent.height; - viewport.minDepth = 0.f; - viewport.maxDepth = 1.f; - - VkRect2D scissor = {}; - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent = g_Extent; - - VkPipelineViewportStateCreateInfo pipelineViewportStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; - pipelineViewportStateInfo.viewportCount = 1; - pipelineViewportStateInfo.pViewports = &viewport; - pipelineViewportStateInfo.scissorCount = 1; - pipelineViewportStateInfo.pScissors = &scissor; - - VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; - pipelineRasterizationStateInfo.depthClampEnable = VK_FALSE; - pipelineRasterizationStateInfo.rasterizerDiscardEnable = VK_FALSE; - pipelineRasterizationStateInfo.polygonMode = VK_POLYGON_MODE_FILL; - pipelineRasterizationStateInfo.lineWidth = 1.f; - pipelineRasterizationStateInfo.cullMode = VK_CULL_MODE_BACK_BIT; - pipelineRasterizationStateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - pipelineRasterizationStateInfo.depthBiasEnable = VK_FALSE; - pipelineRasterizationStateInfo.depthBiasConstantFactor = 0.f; - pipelineRasterizationStateInfo.depthBiasClamp = 0.f; - pipelineRasterizationStateInfo.depthBiasSlopeFactor = 0.f; - - VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; - pipelineMultisampleStateInfo.sampleShadingEnable = VK_FALSE; - pipelineMultisampleStateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - pipelineMultisampleStateInfo.minSampleShading = 1.f; - pipelineMultisampleStateInfo.pSampleMask = nullptr; - pipelineMultisampleStateInfo.alphaToCoverageEnable = VK_FALSE; - pipelineMultisampleStateInfo.alphaToOneEnable = VK_FALSE; - - VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = {}; - pipelineColorBlendAttachmentState.colorWriteMask = - VK_COLOR_COMPONENT_R_BIT | - VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | - VK_COLOR_COMPONENT_A_BIT; - pipelineColorBlendAttachmentState.blendEnable = VK_FALSE; - pipelineColorBlendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional - pipelineColorBlendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional - pipelineColorBlendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; // Optional - pipelineColorBlendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional - pipelineColorBlendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional - pipelineColorBlendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; // Optional - - VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; - pipelineColorBlendStateInfo.logicOpEnable = VK_FALSE; - pipelineColorBlendStateInfo.logicOp = VK_LOGIC_OP_COPY; - pipelineColorBlendStateInfo.attachmentCount = 1; - pipelineColorBlendStateInfo.pAttachments = &pipelineColorBlendAttachmentState; - - VkPipelineDepthStencilStateCreateInfo depthStencilStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; - depthStencilStateInfo.depthTestEnable = VK_TRUE; - depthStencilStateInfo.depthWriteEnable = VK_TRUE; - depthStencilStateInfo.depthCompareOp = VK_COMPARE_OP_LESS; - depthStencilStateInfo.depthBoundsTestEnable = VK_FALSE; - depthStencilStateInfo.stencilTestEnable = VK_FALSE; - - VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = pipelineShaderStageInfos; - pipelineInfo.pVertexInputState = &pipelineVertexInputStateInfo; - pipelineInfo.pInputAssemblyState = &pipelineInputAssemblyStateInfo; - pipelineInfo.pViewportState = &pipelineViewportStateInfo; - pipelineInfo.pRasterizationState = &pipelineRasterizationStateInfo; - pipelineInfo.pMultisampleState = &pipelineMultisampleStateInfo; - pipelineInfo.pDepthStencilState = &depthStencilStateInfo; - pipelineInfo.pColorBlendState = &pipelineColorBlendStateInfo; - pipelineInfo.pDynamicState = nullptr; - pipelineInfo.layout = g_hPipelineLayout; - pipelineInfo.renderPass = g_hRenderPass; - pipelineInfo.subpass = 0; - pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - pipelineInfo.basePipelineIndex = -1; - ERR_GUARD_VULKAN( vkCreateGraphicsPipelines( - g_hDevice, - VK_NULL_HANDLE, - 1, - &pipelineInfo, nullptr, - &g_hPipeline) ); - - vkDestroyShaderModule(g_hDevice, fragShaderModule, nullptr); - vkDestroyShaderModule(g_hDevice, hVertShaderModule, nullptr); - } - - // Create frambuffers - - for(size_t i = g_Framebuffers.size(); i--; ) - vkDestroyFramebuffer(g_hDevice, g_Framebuffers[i], nullptr); - g_Framebuffers.clear(); - - g_Framebuffers.resize(g_SwapchainImageViews.size()); - for(size_t i = 0; i < g_SwapchainImages.size(); ++i) - { - VkImageView attachments[] = { g_SwapchainImageViews[i], g_hDepthImageView }; - - VkFramebufferCreateInfo framebufferInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - framebufferInfo.renderPass = g_hRenderPass; - framebufferInfo.attachmentCount = (uint32_t)_countof(attachments); - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = g_Extent.width; - framebufferInfo.height = g_Extent.height; - framebufferInfo.layers = 1; - ERR_GUARD_VULKAN( vkCreateFramebuffer(g_hDevice, &framebufferInfo, nullptr, &g_Framebuffers[i]) ); - } - - // Create semaphores - - if(g_hImageAvailableSemaphore != VK_NULL_HANDLE) - { - vkDestroySemaphore(g_hDevice, g_hImageAvailableSemaphore, nullptr); - g_hImageAvailableSemaphore = VK_NULL_HANDLE; - } - if(g_hRenderFinishedSemaphore != VK_NULL_HANDLE) - { - vkDestroySemaphore(g_hDevice, g_hRenderFinishedSemaphore, nullptr); - g_hRenderFinishedSemaphore = VK_NULL_HANDLE; - } - - VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - ERR_GUARD_VULKAN( vkCreateSemaphore(g_hDevice, &semaphoreInfo, nullptr, &g_hImageAvailableSemaphore) ); - ERR_GUARD_VULKAN( vkCreateSemaphore(g_hDevice, &semaphoreInfo, nullptr, &g_hRenderFinishedSemaphore) ); -} - -static void DestroySwapchain(bool destroyActualSwapchain) -{ - if(g_hImageAvailableSemaphore != VK_NULL_HANDLE) - { - vkDestroySemaphore(g_hDevice, g_hImageAvailableSemaphore, nullptr); - g_hImageAvailableSemaphore = VK_NULL_HANDLE; - } - if(g_hRenderFinishedSemaphore != VK_NULL_HANDLE) - { - vkDestroySemaphore(g_hDevice, g_hRenderFinishedSemaphore, nullptr); - g_hRenderFinishedSemaphore = VK_NULL_HANDLE; - } - - for(size_t i = g_Framebuffers.size(); i--; ) - vkDestroyFramebuffer(g_hDevice, g_Framebuffers[i], nullptr); - g_Framebuffers.clear(); - - if(g_hDepthImageView != VK_NULL_HANDLE) - { - vkDestroyImageView(g_hDevice, g_hDepthImageView, nullptr); - g_hDepthImageView = VK_NULL_HANDLE; - } - if(g_hDepthImage != VK_NULL_HANDLE) - { - vmaDestroyImage(g_hAllocator, g_hDepthImage, g_hDepthImageAlloc); - g_hDepthImage = VK_NULL_HANDLE; - } - - if(g_hPipeline != VK_NULL_HANDLE) - { - vkDestroyPipeline(g_hDevice, g_hPipeline, nullptr); - g_hPipeline = VK_NULL_HANDLE; - } - - if(g_hRenderPass != VK_NULL_HANDLE) - { - vkDestroyRenderPass(g_hDevice, g_hRenderPass, nullptr); - g_hRenderPass = VK_NULL_HANDLE; - } - - if(g_hPipelineLayout != VK_NULL_HANDLE) - { - vkDestroyPipelineLayout(g_hDevice, g_hPipelineLayout, nullptr); - g_hPipelineLayout = VK_NULL_HANDLE; - } - - for(size_t i = g_SwapchainImageViews.size(); i--; ) - vkDestroyImageView(g_hDevice, g_SwapchainImageViews[i], nullptr); - g_SwapchainImageViews.clear(); - - if(destroyActualSwapchain && (g_hSwapchain != VK_NULL_HANDLE)) - { - vkDestroySwapchainKHR(g_hDevice, g_hSwapchain, nullptr); - g_hSwapchain = VK_NULL_HANDLE; - } -} - -static void InitializeApplication() -{ - uint32_t instanceLayerPropCount = 0; - ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr) ); - std::vector instanceLayerProps(instanceLayerPropCount); - if(instanceLayerPropCount > 0) - { - ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data()) ); - } - - if(g_EnableValidationLayer == true) - { - if(IsLayerSupported(instanceLayerProps.data(), instanceLayerProps.size(), VALIDATION_LAYER_NAME) == false) - { - printf("Layer \"%s\" not supported.", VALIDATION_LAYER_NAME); - g_EnableValidationLayer = false; - } - } - - std::vector instanceExtensions; - instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); - instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); - - std::vector instanceLayers; - if(g_EnableValidationLayer == true) - { - instanceLayers.push_back(VALIDATION_LAYER_NAME); - instanceExtensions.push_back("VK_EXT_debug_report"); - } - - VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; - appInfo.pApplicationName = APP_TITLE_A; - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.pEngineName = "Adam Sawicki Engine"; - 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 = static_cast(instanceExtensions.size()); - instInfo.ppEnabledExtensionNames = instanceExtensions.data(); - instInfo.enabledLayerCount = static_cast(instanceLayers.size()); - instInfo.ppEnabledLayerNames = instanceLayers.data(); - - ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, NULL, &g_hVulkanInstance) ); - - // Create VkSurfaceKHR. - VkWin32SurfaceCreateInfoKHR surfaceInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR }; - surfaceInfo.hinstance = g_hAppInstance; - surfaceInfo.hwnd = g_hWnd; - VkResult result = vkCreateWin32SurfaceKHR(g_hVulkanInstance, &surfaceInfo, NULL, &g_hSurface); - assert(result == VK_SUCCESS); - - if(g_EnableValidationLayer == true) - RegisterDebugCallbacks(); - - // Find physical device - - uint32_t deviceCount = 0; - ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, nullptr) ); - assert(deviceCount > 0); - - std::vector physicalDevices(deviceCount); - ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, physicalDevices.data()) ); - - g_hPhysicalDevice = physicalDevices[0]; - - // Query for features - - VkPhysicalDeviceProperties physicalDeviceProperties = {}; - vkGetPhysicalDeviceProperties(g_hPhysicalDevice, &physicalDeviceProperties); - - //VkPhysicalDeviceFeatures physicalDeviceFreatures = {}; - //vkGetPhysicalDeviceFeatures(g_PhysicalDevice, &physicalDeviceFreatures); - - // Find queue family index - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(g_hPhysicalDevice, &queueFamilyCount, nullptr); - assert(queueFamilyCount > 0); - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(g_hPhysicalDevice, &queueFamilyCount, queueFamilies.data()); - for(uint32_t i = 0; - (i < queueFamilyCount) && - (g_GraphicsQueueFamilyIndex == UINT_MAX || g_PresentQueueFamilyIndex == UINT_MAX); - ++i) - { - if(queueFamilies[i].queueCount > 0) - { - if((g_GraphicsQueueFamilyIndex != 0) && - ((queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) - { - g_GraphicsQueueFamilyIndex = i; - } - - VkBool32 surfaceSupported = 0; - VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(g_hPhysicalDevice, i, g_hSurface, &surfaceSupported); - if((res >= 0) && (surfaceSupported == VK_TRUE)) - { - g_PresentQueueFamilyIndex = i; - } - } - } - assert(g_GraphicsQueueFamilyIndex != UINT_MAX); - - // Create logical device - - const float queuePriority = 1.f; - - VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; - deviceQueueCreateInfo[0].queueFamilyIndex = g_GraphicsQueueFamilyIndex; - deviceQueueCreateInfo[0].queueCount = 1; - deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority; - deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - deviceQueueCreateInfo[1].queueFamilyIndex = g_PresentQueueFamilyIndex; - deviceQueueCreateInfo[1].queueCount = 1; - deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority; - - VkPhysicalDeviceFeatures deviceFeatures = {}; - deviceFeatures.fillModeNonSolid = VK_TRUE; - deviceFeatures.samplerAnisotropy = VK_TRUE; - - // Determine list of device extensions to enable. - std::vector enabledDeviceExtensions; - enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - { - uint32_t propertyCount = 0; - ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(g_hPhysicalDevice, nullptr, &propertyCount, nullptr) ); - - if(propertyCount) - { - std::vector properties{propertyCount}; - ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(g_hPhysicalDevice, nullptr, &propertyCount, properties.data()) ); - - 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.enabledLayerCount = 0; - deviceCreateInfo.ppEnabledLayerNames = nullptr; - deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size(); - deviceCreateInfo.ppEnabledExtensionNames = !enabledDeviceExtensions.empty() ? enabledDeviceExtensions.data() : nullptr; - deviceCreateInfo.queueCreateInfoCount = g_PresentQueueFamilyIndex != g_GraphicsQueueFamilyIndex ? 2 : 1; - deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; - deviceCreateInfo.pEnabledFeatures = &deviceFeatures; - - ERR_GUARD_VULKAN( vkCreateDevice(g_hPhysicalDevice, &deviceCreateInfo, nullptr, &g_hDevice) ); - - // Create memory allocator - - VmaAllocatorCreateInfo allocatorInfo = {}; - allocatorInfo.physicalDevice = g_hPhysicalDevice; - allocatorInfo.device = g_hDevice; - - if(VK_KHR_dedicated_allocation_enabled) - { - allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; - } - - VkAllocationCallbacks cpuAllocationCallbacks = {}; - if(USE_CUSTOM_CPU_ALLOCATION_CALLBACKS) - { - cpuAllocationCallbacks.pUserData = CUSTOM_CPU_ALLOCATION_CALLBACK_USER_DATA; - cpuAllocationCallbacks.pfnAllocation = &CustomCpuAllocation; - cpuAllocationCallbacks.pfnReallocation = &CustomCpuReallocation; - cpuAllocationCallbacks.pfnFree = &CustomCpuFree; - allocatorInfo.pAllocationCallbacks = &cpuAllocationCallbacks; - } - - ERR_GUARD_VULKAN( vmaCreateAllocator(&allocatorInfo, &g_hAllocator) ); - - // Retrieve queue (doesn't need to be destroyed) - - vkGetDeviceQueue(g_hDevice, g_GraphicsQueueFamilyIndex, 0, &g_hGraphicsQueue); - vkGetDeviceQueue(g_hDevice, g_PresentQueueFamilyIndex, 0, &g_hPresentQueue); - assert(g_hGraphicsQueue); - assert(g_hPresentQueue); - - // Create command pool - - VkCommandPoolCreateInfo commandPoolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; - commandPoolInfo.queueFamilyIndex = g_GraphicsQueueFamilyIndex; - commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - ERR_GUARD_VULKAN( vkCreateCommandPool(g_hDevice, &commandPoolInfo, nullptr, &g_hCommandPool) ); - - VkCommandBufferAllocateInfo commandBufferInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - commandBufferInfo.commandPool = g_hCommandPool; - commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - commandBufferInfo.commandBufferCount = COMMAND_BUFFER_COUNT; - ERR_GUARD_VULKAN( vkAllocateCommandBuffers(g_hDevice, &commandBufferInfo, g_MainCommandBuffers) ); - - VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - for(size_t i = 0; i < COMMAND_BUFFER_COUNT; ++i) - { - ERR_GUARD_VULKAN( vkCreateFence(g_hDevice, &fenceInfo, nullptr, &g_MainCommandBufferExecutedFances[i]) ); - } - - commandBufferInfo.commandBufferCount = 1; - ERR_GUARD_VULKAN( vkAllocateCommandBuffers(g_hDevice, &commandBufferInfo, &g_hTemporaryCommandBuffer) ); - - // Create texture sampler - - VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - samplerInfo.magFilter = VK_FILTER_LINEAR; - samplerInfo.minFilter = VK_FILTER_LINEAR; - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.anisotropyEnable = VK_TRUE; - samplerInfo.maxAnisotropy = 16; - samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - samplerInfo.unnormalizedCoordinates = VK_FALSE; - samplerInfo.compareEnable = VK_FALSE; - samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerInfo.mipLodBias = 0.f; - samplerInfo.minLod = 0.f; - samplerInfo.maxLod = FLT_MAX; - ERR_GUARD_VULKAN( vkCreateSampler(g_hDevice, &samplerInfo, nullptr, &g_hSampler) ); - - CreateTexture(128, 128); - CreateMesh(); - - VkDescriptorSetLayoutBinding samplerLayoutBinding = {}; - samplerLayoutBinding.binding = 1; - samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerLayoutBinding.descriptorCount = 1; - samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; - descriptorSetLayoutInfo.bindingCount = 1; - descriptorSetLayoutInfo.pBindings = &samplerLayoutBinding; - ERR_GUARD_VULKAN( vkCreateDescriptorSetLayout(g_hDevice, &descriptorSetLayoutInfo, nullptr, &g_hDescriptorSetLayout) ); - - // Create descriptor pool - - VkDescriptorPoolSize descriptorPoolSizes[2]; - ZeroMemory(descriptorPoolSizes, sizeof(descriptorPoolSizes)); - descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorPoolSizes[0].descriptorCount = 1; - descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorPoolSizes[1].descriptorCount = 1; - - VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - descriptorPoolInfo.poolSizeCount = (uint32_t)_countof(descriptorPoolSizes); - descriptorPoolInfo.pPoolSizes = descriptorPoolSizes; - descriptorPoolInfo.maxSets = 1; - ERR_GUARD_VULKAN( vkCreateDescriptorPool(g_hDevice, &descriptorPoolInfo, nullptr, &g_hDescriptorPool) ); - - // Create descriptor set layout - - VkDescriptorSetLayout descriptorSetLayouts[] = { g_hDescriptorSetLayout }; - VkDescriptorSetAllocateInfo descriptorSetInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; - descriptorSetInfo.descriptorPool = g_hDescriptorPool; - descriptorSetInfo.descriptorSetCount = 1; - descriptorSetInfo.pSetLayouts = descriptorSetLayouts; - ERR_GUARD_VULKAN( vkAllocateDescriptorSets(g_hDevice, &descriptorSetInfo, &g_hDescriptorSet) ); - - VkDescriptorImageInfo descriptorImageInfo = {}; - descriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - descriptorImageInfo.imageView = g_hTextureImageView; - descriptorImageInfo.sampler = g_hSampler; - - VkWriteDescriptorSet writeDescriptorSet = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - writeDescriptorSet.dstSet = g_hDescriptorSet; - writeDescriptorSet.dstBinding = 1; - writeDescriptorSet.dstArrayElement = 0; - writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - writeDescriptorSet.descriptorCount = 1; - writeDescriptorSet.pImageInfo = &descriptorImageInfo; - - vkUpdateDescriptorSets(g_hDevice, 1, &writeDescriptorSet, 0, nullptr); - - CreateSwapchain(); -} - -static void FinalizeApplication() -{ - vkDeviceWaitIdle(g_hDevice); - - DestroySwapchain(true); - - if(g_hDescriptorPool != VK_NULL_HANDLE) - { - vkDestroyDescriptorPool(g_hDevice, g_hDescriptorPool, nullptr); - g_hDescriptorPool = VK_NULL_HANDLE; - } - - if(g_hDescriptorSetLayout != VK_NULL_HANDLE) - { - vkDestroyDescriptorSetLayout(g_hDevice, g_hDescriptorSetLayout, nullptr); - g_hDescriptorSetLayout = VK_NULL_HANDLE; - } - - if(g_hTextureImageView != VK_NULL_HANDLE) - { - vkDestroyImageView(g_hDevice, g_hTextureImageView, nullptr); - g_hTextureImageView = VK_NULL_HANDLE; - } - if(g_hTextureImage != VK_NULL_HANDLE) - { - vmaDestroyImage(g_hAllocator, g_hTextureImage, g_hTextureImageAlloc); - g_hTextureImage = VK_NULL_HANDLE; - } - - if(g_hIndexBuffer != VK_NULL_HANDLE) - { - vmaDestroyBuffer(g_hAllocator, g_hIndexBuffer, g_hIndexBufferAlloc); - g_hIndexBuffer = VK_NULL_HANDLE; - } - if(g_hVertexBuffer != VK_NULL_HANDLE) - { - vmaDestroyBuffer(g_hAllocator, g_hVertexBuffer, g_hVertexBufferAlloc); - g_hVertexBuffer = VK_NULL_HANDLE; - } - - if(g_hSampler != VK_NULL_HANDLE) - { - vkDestroySampler(g_hDevice, g_hSampler, nullptr); - g_hSampler = VK_NULL_HANDLE; - } - - for(size_t i = COMMAND_BUFFER_COUNT; i--; ) - { - if(g_MainCommandBufferExecutedFances[i] != VK_NULL_HANDLE) - { - vkDestroyFence(g_hDevice, g_MainCommandBufferExecutedFances[i], nullptr); - g_MainCommandBufferExecutedFances[i] = VK_NULL_HANDLE; - } - } - if(g_MainCommandBuffers[0] != VK_NULL_HANDLE) - { - vkFreeCommandBuffers(g_hDevice, g_hCommandPool, COMMAND_BUFFER_COUNT, g_MainCommandBuffers); - ZeroMemory(g_MainCommandBuffers, sizeof(g_MainCommandBuffers)); - } - if(g_hTemporaryCommandBuffer != VK_NULL_HANDLE) - { - vkFreeCommandBuffers(g_hDevice, g_hCommandPool, 1, &g_hTemporaryCommandBuffer); - g_hTemporaryCommandBuffer = VK_NULL_HANDLE; - } - - if(g_hCommandPool != VK_NULL_HANDLE) - { - vkDestroyCommandPool(g_hDevice, g_hCommandPool, nullptr); - g_hCommandPool = VK_NULL_HANDLE; - } - - if(g_hAllocator != VK_NULL_HANDLE) - { - vmaDestroyAllocator(g_hAllocator); - g_hAllocator = nullptr; - } - - if(g_hDevice != VK_NULL_HANDLE) - { - vkDestroyDevice(g_hDevice, nullptr); - g_hDevice = nullptr; - } - - if(g_pvkDestroyDebugReportCallbackEXT && g_hCallback != VK_NULL_HANDLE) - { - g_pvkDestroyDebugReportCallbackEXT(g_hVulkanInstance, g_hCallback, nullptr); - g_hCallback = VK_NULL_HANDLE; - } - - if(g_hSurface != VK_NULL_HANDLE) - { - vkDestroySurfaceKHR(g_hVulkanInstance, g_hSurface, NULL); - g_hSurface = VK_NULL_HANDLE; - } - - if(g_hVulkanInstance != VK_NULL_HANDLE) - { - vkDestroyInstance(g_hVulkanInstance, NULL); - g_hVulkanInstance = VK_NULL_HANDLE; - } -} - -static void PrintAllocatorStats() -{ -#if VMA_STATS_STRING_ENABLED - char* statsString = nullptr; - vmaBuildStatsString(g_hAllocator, &statsString, true); - printf("%s\n", statsString); - vmaFreeStatsString(g_hAllocator, statsString); -#endif -} - -static void RecreateSwapChain() -{ - vkDeviceWaitIdle(g_hDevice); - DestroySwapchain(false); - CreateSwapchain(); -} - -static void DrawFrame() -{ - // Begin main command buffer - size_t cmdBufIndex = (g_NextCommandBufferIndex++) % COMMAND_BUFFER_COUNT; - VkCommandBuffer hCommandBuffer = g_MainCommandBuffers[cmdBufIndex]; - VkFence hCommandBufferExecutedFence = g_MainCommandBufferExecutedFances[cmdBufIndex]; - - ERR_GUARD_VULKAN( vkWaitForFences(g_hDevice, 1, &hCommandBufferExecutedFence, VK_TRUE, UINT64_MAX) ); - ERR_GUARD_VULKAN( vkResetFences(g_hDevice, 1, &hCommandBufferExecutedFence) ); - - VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - ERR_GUARD_VULKAN( vkBeginCommandBuffer(hCommandBuffer, &commandBufferBeginInfo) ); - - // Acquire swapchain image - uint32_t imageIndex = 0; - VkResult res = vkAcquireNextImageKHR(g_hDevice, g_hSwapchain, UINT64_MAX, g_hImageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); - if(res == VK_ERROR_OUT_OF_DATE_KHR) - { - RecreateSwapChain(); - return; - } - else if(res < 0) - { - ERR_GUARD_VULKAN(res); - } - - // Record geometry pass - - VkClearValue clearValues[2]; - ZeroMemory(clearValues, sizeof(clearValues)); - clearValues[0].color.float32[0] = 0.25f; - clearValues[0].color.float32[1] = 0.25f; - clearValues[0].color.float32[2] = 0.5f; - clearValues[0].color.float32[3] = 1.0f; - clearValues[1].depthStencil.depth = 1.0f; - - VkRenderPassBeginInfo renderPassBeginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - renderPassBeginInfo.renderPass = g_hRenderPass; - renderPassBeginInfo.framebuffer = g_Framebuffers[imageIndex]; - renderPassBeginInfo.renderArea.offset.x = 0; - renderPassBeginInfo.renderArea.offset.y = 0; - renderPassBeginInfo.renderArea.extent = g_Extent; - renderPassBeginInfo.clearValueCount = (uint32_t)_countof(clearValues); - renderPassBeginInfo.pClearValues = clearValues; - vkCmdBeginRenderPass(hCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - - vkCmdBindPipeline( - hCommandBuffer, - VK_PIPELINE_BIND_POINT_GRAPHICS, - g_hPipeline); - - mat4 view = mat4::LookAt( - vec3(0.f, 0.f, 0.f), - vec3(0.f, -2.f, 4.f), - vec3(0.f, 1.f, 0.f)); - mat4 proj = mat4::Perspective( - 1.0471975511966f, // 60 degrees - (float)g_Extent.width / (float)g_Extent.height, - 0.1f, - 1000.f); - mat4 viewProj = view * proj; - - vkCmdBindDescriptorSets( - hCommandBuffer, - VK_PIPELINE_BIND_POINT_GRAPHICS, - g_hPipelineLayout, - 0, - 1, - &g_hDescriptorSet, - 0, - nullptr); - - float rotationAngle = (float)GetTickCount() * 0.001f * (float)PI * 0.2f; - mat4 model = mat4::RotationY(rotationAngle); - - UniformBufferObject ubo = {}; - ubo.ModelViewProj = model * viewProj; - vkCmdPushConstants(hCommandBuffer, g_hPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(UniformBufferObject), &ubo); - - VkBuffer vertexBuffers[] = { g_hVertexBuffer }; - VkDeviceSize offsets[] = { 0 }; - vkCmdBindVertexBuffers(hCommandBuffer, 0, 1, vertexBuffers, offsets); - - vkCmdBindIndexBuffer(hCommandBuffer, g_hIndexBuffer, 0, VK_INDEX_TYPE_UINT16); - - vkCmdDrawIndexed(hCommandBuffer, g_IndexCount, 1, 0, 0, 0); - - vkCmdEndRenderPass(hCommandBuffer); - - vkEndCommandBuffer(hCommandBuffer); - - // Submit command buffer - - VkSemaphore submitWaitSemaphores[] = { g_hImageAvailableSemaphore }; - VkPipelineStageFlags submitWaitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - VkSemaphore submitSignalSemaphores[] = { g_hRenderFinishedSemaphore }; - VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = submitWaitSemaphores; - submitInfo.pWaitDstStageMask = submitWaitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &hCommandBuffer; - submitInfo.signalSemaphoreCount = _countof(submitSignalSemaphores); - submitInfo.pSignalSemaphores = submitSignalSemaphores; - ERR_GUARD_VULKAN( vkQueueSubmit(g_hGraphicsQueue, 1, &submitInfo, hCommandBufferExecutedFence) ); - - VkSemaphore presentWaitSemaphores[] = { g_hRenderFinishedSemaphore }; - - VkSwapchainKHR swapchains[] = { g_hSwapchain }; - VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; - presentInfo.waitSemaphoreCount = _countof(presentWaitSemaphores); - presentInfo.pWaitSemaphores = presentWaitSemaphores; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = swapchains; - presentInfo.pImageIndices = &imageIndex; - presentInfo.pResults = nullptr; - res = vkQueuePresentKHR(g_hPresentQueue, &presentInfo); - if(res == VK_ERROR_OUT_OF_DATE_KHR) - { - RecreateSwapChain(); - } - else - ERR_GUARD_VULKAN(res); -} - -static void HandlePossibleSizeChange() -{ - RECT clientRect; - GetClientRect(g_hWnd, &clientRect); - LONG newSizeX = clientRect.right - clientRect.left; - LONG newSizeY = clientRect.bottom - clientRect.top; - if((newSizeX > 0) && - (newSizeY > 0) && - ((newSizeX != g_SizeX) || (newSizeY != g_SizeY))) - { - g_SizeX = newSizeX; - g_SizeY = newSizeY; - - RecreateSwapChain(); - } -} - -static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch(msg) - { - case WM_CREATE: - // This is intentionally assigned here because we are now inside CreateWindow, before it returns. - g_hWnd = hWnd; - InitializeApplication(); - PrintAllocatorStats(); - return 0; - - case WM_DESTROY: - FinalizeApplication(); - PostQuitMessage(0); - return 0; - - // This prevents app from freezing when left Alt is pressed - // (which normally enters modal menu loop). - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - return 0; - - case WM_SIZE: - if((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)) - HandlePossibleSizeChange(); - return 0; - - case WM_EXITSIZEMOVE: - HandlePossibleSizeChange(); - return 0; - - case WM_KEYDOWN: - switch(wParam) - { - case VK_ESCAPE: - PostMessage(hWnd, WM_CLOSE, 0, 0); - break; - case 'T': - Test(); - break; - } - return 0; - - default: - break; - } - - return DefWindowProc(hWnd, msg, wParam, lParam); -} - -int main() -{ - g_hAppInstance = (HINSTANCE)GetModuleHandle(NULL); - - WNDCLASSEX wndClassDesc = { sizeof(WNDCLASSEX) }; - wndClassDesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS; - wndClassDesc.hbrBackground = NULL; - wndClassDesc.hCursor = LoadCursor(NULL, IDC_CROSS); - wndClassDesc.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wndClassDesc.hInstance = g_hAppInstance; - wndClassDesc.lpfnWndProc = WndProc; - wndClassDesc.lpszClassName = WINDOW_CLASS_NAME; - - const ATOM hWndClass = RegisterClassEx(&wndClassDesc); - assert(hWndClass); - - const DWORD style = WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; - const DWORD exStyle = 0; - - RECT rect = { 0, 0, g_SizeX, g_SizeY }; - AdjustWindowRectEx(&rect, style, FALSE, exStyle); - - CreateWindowEx( - exStyle, WINDOW_CLASS_NAME, APP_TITLE_W, style, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - NULL, NULL, g_hAppInstance, NULL); - - MSG msg; - for(;;) - { - if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - if(msg.message == WM_QUIT) - break; - TranslateMessage(&msg); - DispatchMessage(&msg); - } - if(g_hDevice != VK_NULL_HANDLE) - DrawFrame(); - } - - return 0; -} -#endif - static const int RESULT_EXCEPTION = -1000; static const int RESULT_ERROR_COMMAND_LINE = -1; static const int RESULT_ERROR_SOURCE_FILE = -2; @@ -1857,6 +123,58 @@ void CsvSplit::Set(const StrRange& line, size_t maxCount) //////////////////////////////////////////////////////////////////////////////// // 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; +} + class Player { public: @@ -1873,6 +191,21 @@ private: bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; } 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; + + void InitVulkan(); + void FinalizeVulkan(); + void RegisterDebugCallbacks(); }; Player::Player() @@ -1881,10 +214,13 @@ Player::Player() void Player::Init() { + InitVulkan(); } Player::~Player() { + FinalizeVulkan(); + if(m_WarningCount > 0) printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW); } @@ -1896,6 +232,7 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) if(csvSplit.GetCount() >= 4) { + // TODO } else { @@ -1904,6 +241,211 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) } } +void Player::InitVulkan() +{ + printf("Initializing Vulkan...\n"); + + uint32_t instanceLayerPropCount = 0; + ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr) ); + std::vector instanceLayerProps(instanceLayerPropCount); + if(instanceLayerPropCount > 0) + { + ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data()) ); + } + + 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 instanceExtensions; + //instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + //instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); + + std::vector instanceLayers; + if(g_EnableValidationLayer == true) + { + 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(); + + ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, NULL, &m_VulkanInstance) ); + + // Find physical device + + uint32_t deviceCount = 0; + ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(m_VulkanInstance, &deviceCount, nullptr) ); + assert(deviceCount > 0); + + std::vector physicalDevices(deviceCount); + ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(m_VulkanInstance, &deviceCount, physicalDevices.data()) ); + + m_PhysicalDevice = physicalDevices[0]; + + // Find queue family index + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, nullptr); + assert(queueFamilyCount > 0); + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, queueFamilies.data()); + for(uint32_t i = 0; i < queueFamilyCount; ++i) + { + if(queueFamilies[i].queueCount > 0) + { + if((queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) + { + m_GraphicsQueueFamilyIndex = i; + break; + } + } + } + assert(m_GraphicsQueueFamilyIndex != UINT_MAX); + + // 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 enabledDeviceExtensions; + enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + { + uint32_t propertyCount = 0; + ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, nullptr) ); + + if(propertyCount) + { + std::vector properties{propertyCount}; + ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, properties.data()) ); + + 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; + + ERR_GUARD_VULKAN( vkCreateDevice(m_PhysicalDevice, &deviceCreateInfo, nullptr, &m_Device) ); + + // 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; + } + + ERR_GUARD_VULKAN( vmaCreateAllocator(&allocatorInfo, &m_Allocator) ); +} + +void Player::FinalizeVulkan() +{ + 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 + (vkGetInstanceProcAddr(m_VulkanInstance, "vkCreateDebugReportCallbackEXT")); + m_pvkDebugReportMessageEXT = + reinterpret_cast + (vkGetInstanceProcAddr(m_VulkanInstance, "vkDebugReportMessageEXT")); + m_pvkDestroyDebugReportCallbackEXT = + reinterpret_cast + (vkGetInstanceProcAddr(m_VulkanInstance, "vkDestroyDebugReportCallbackEXT")); + assert(m_pvkCreateDebugReportCallbackEXT); + assert(m_pvkDebugReportMessageEXT); + assert(m_pvkDestroyDebugReportCallbackEXT); + + VkDebugReportCallbackCreateInfoEXT callbackCreateInfo; + callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + callbackCreateInfo.pNext = nullptr; + 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; + callbackCreateInfo.pUserData = nullptr; + + ERR_GUARD_VULKAN( m_pvkCreateDebugReportCallbackEXT(m_VulkanInstance, &callbackCreateInfo, nullptr, &m_hCallback) ); +} //////////////////////////////////////////////////////////////////////////////// // Main functions @@ -1990,6 +532,8 @@ static int ProcessFile(const char* filePath) result = RESULT_ERROR_SOURCE_FILE; } + printf("Done.\n"); + return result; } From ec5fecf51a5ad6921caf089f1d1f99cab00fd1be Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 7 Aug 2018 16:20:34 +0200 Subject: [PATCH 07/51] Implemented playback of current format. --- src/VmaReplay/VmaReplay.cpp | 454 ++++++++++++++++++++++++++++++++++-- 1 file changed, 430 insertions(+), 24 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 09e38f7..6fc13b0 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -22,6 +22,7 @@ #include "VmaUsage.h" #include "Common.h" +#include static const int RESULT_EXCEPTION = -1000; static const int RESULT_ERROR_COMMAND_LINE = -1; @@ -36,6 +37,35 @@ struct StrRange 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) +{ + // TODO handle failure. + char* end = (char*)s.end; + out = (uint32_t)strtoul(s.beg, &end, 10); + return true; +} +static inline bool StrRangeToUint(const StrRange& s, uint64_t& out) +{ + // TODO handle failure. + char* end = (char*)s.end; + out = (uint64_t)strtoull(s.beg, &end, 10); + return true; +} +static inline bool StrRangeToPtr(const StrRange& s, uint64_t& out) +{ + // TODO handle failure. + char* end = (char*)s.end; + out = (uint64_t)strtoull(s.beg, &end, 16); + return true; +} + //////////////////////////////////////////////////////////////////////////////// // LineSplit class @@ -89,10 +119,11 @@ public: void Set(const StrRange& line, size_t maxCount = RANGE_COUNT_MAX); size_t GetCount() const { return m_Count; } - void GetRange(size_t index, const char*& outBeg, const char*& outEnd) const + StrRange GetRange(size_t index) const { - outBeg = m_Str + m_Ranges[index * 2]; - outEnd = m_Str + m_Ranges[index * 2 + 1]; + return StrRange { + m_Str + m_Ranges[index * 2], + m_Str + m_Ranges[index * 2 + 1] }; } private: @@ -175,6 +206,8 @@ static bool IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, }) != propsEnd; } +static const size_t FIRST_PARAM_INDEX = 4; + class Player { public: @@ -185,10 +218,7 @@ public: void ExecuteLine(size_t lineNumber, const StrRange& line); private: - static const size_t MAX_WARNINGS_TO_SHOW = 16; - - // Increments warning counter. Returns true if warning message should be printed. - bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; } + static const size_t MAX_WARNINGS_TO_SHOW = 64; size_t m_WarningCount = 0; @@ -203,9 +233,44 @@ private: PFN_vkDestroyDebugReportCallbackEXT m_pvkDestroyDebugReportCallbackEXT; VkDebugReportCallbackEXT m_hCallback; + uint32_t m_VmaFrameIndex = 0; + + // Any of these handles null means it was created in original but couldn't be created now. + struct Pool + { + VmaPool pool; + }; + struct Buffer + { + VkBuffer buffer; + VmaAllocation allocation; + }; + struct Image + { + VkImage image; + VmaAllocation allocation; + }; + + std::unordered_map m_Pools; + std::unordered_map m_Buffers; + std::unordered_map m_Images; + + // Increments warning counter. Returns true if warning message should be printed. + bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; } + void InitVulkan(); void FinalizeVulkan(); void RegisterDebugCallbacks(); + + // 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 CreatePool(size_t lineNumber, const CsvSplit& csvSplit); + void DestroyPool(size_t lineNumber, const CsvSplit& csvSplit); + void CreateBuffer(size_t lineNumber, const CsvSplit& csvSplit); + void DestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit); + void CreateImage(size_t lineNumber, const CsvSplit& csvSplit); + void DestroyImage(size_t lineNumber, const CsvSplit& csvSplit); }; Player::Player() @@ -221,7 +286,7 @@ Player::~Player() { FinalizeVulkan(); - if(m_WarningCount > 0) + if(m_WarningCount > MAX_WARNINGS_TO_SHOW) printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW); } @@ -230,9 +295,58 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) CsvSplit csvSplit; csvSplit.Set(line); - if(csvSplit.GetCount() >= 4) + if(csvSplit.GetCount() >= FIRST_PARAM_INDEX) { - // TODO + // 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, "vmaCreatePool")) + CreatePool(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaDestroyPool")) + DestroyPool(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaSetAllocationUserData")) + { + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true)) + { + // Ignore for now. + } + } + else if(StrRangeEq(functionName, "vmaCreateBuffer")) + CreateBuffer(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaDestroyBuffer")) + DestroyBuffer(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaCreateImage")) + CreateImage(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaDestroyImage")) + DestroyImage(lineNumber, csvSplit); + else + { + if(IssueWarning()) + printf("Line %zu: Unknown function.\n", lineNumber); + } } else { @@ -289,6 +403,8 @@ void Player::InitVulkan() ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, NULL, &m_VulkanInstance) ); + RegisterDebugCallbacks(); + // Find physical device uint32_t deviceCount = 0; @@ -340,7 +456,7 @@ void Player::InitVulkan() // Determine list of device extensions to enable. std::vector enabledDeviceExtensions; - enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + //enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); { uint32_t propertyCount = 0; ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, nullptr) ); @@ -391,6 +507,36 @@ void Player::InitVulkan() void Player::FinalizeVulkan() { + if(!m_Images.empty()) + { + printf("WARNING: Images not destroyed: %zu.\n", m_Images.size()); + + for(const auto it : m_Images) + { + vmaDestroyImage(m_Allocator, it.second.image, it.second.allocation); + } + } + + if(!m_Buffers.empty()) + { + printf("WARNING: Buffers not destroyed: %zu.\n", m_Buffers.size()); + + for(const auto it : m_Buffers) + { + vmaDestroyBuffer(m_Allocator, it.second.buffer, it.second.allocation); + } + } + + 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); + } + } + vkDeviceWaitIdle(m_Device); if(m_Allocator != VK_NULL_HANDLE) @@ -433,20 +579,287 @@ void Player::RegisterDebugCallbacks() assert(m_pvkDebugReportMessageEXT); assert(m_pvkDestroyDebugReportCallbackEXT); - VkDebugReportCallbackCreateInfoEXT callbackCreateInfo; - callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; - callbackCreateInfo.pNext = nullptr; + 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; - callbackCreateInfo.pUserData = nullptr; ERR_GUARD_VULKAN( m_pvkCreateDebugReportCallbackEXT(m_VulkanInstance, &callbackCreateInfo, nullptr, &m_hCallback) ); } +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::CreatePool(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) + { + const auto existingIt = m_Pools.find(origPtr); + if(existingIt != m_Pools.end()) + { + if(IssueWarning()) + printf("Line %zu: Pool %llX already exists.\n", lineNumber, origPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: vmaCreatePool failed (%u).\n", lineNumber, res); + } + m_Pools[origPtr] = poolDesc; + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaCreatePool.\n", lineNumber); + } + } +} + +void Player::DestroyPool(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::CreateBuffer(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); + } + } + + Buffer bufDesc = {}; + VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &bufDesc.buffer, &bufDesc.allocation, nullptr); + if(res == VK_SUCCESS) + { + const auto existingIt = m_Buffers.find(origPtr); + if(existingIt != m_Buffers.end()) + { + if(IssueWarning()) + printf("Line %zu: Allocation %llX for buffer already exists.\n", lineNumber, origPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: vmaCreateBuffer failed (%u).\n", lineNumber, res); + } + m_Buffers[origPtr] = bufDesc; + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaCreateBuffer.\n", lineNumber); + } + } +} + +void Player::DestroyBuffer(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_Buffers.find(origAllocPtr); + if(it != m_Buffers.end()) + { + vmaDestroyBuffer(m_Allocator, it->second.buffer, it->second.allocation); + m_Buffers.erase(it); + } + else + { + if(IssueWarning()) + printf("Line %zu: Allocation %llX for buffer not found.\n", lineNumber, origAllocPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaDestroyBuffer.\n", lineNumber); + } + } +} + +void Player::CreateImage(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); + } + } + + Image imageDesc = {}; + VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &imageDesc.image, &imageDesc.allocation, nullptr); + if(res == VK_SUCCESS) + { + const auto existingIt = m_Images.find(origPtr); + if(existingIt != m_Images.end()) + { + if(IssueWarning()) + printf("Line %zu: Allocation %llX for image already exists.\n", lineNumber, origPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: vmaCreateImage failed (%u).\n", lineNumber, res); + } + m_Images[origPtr] = imageDesc; + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaCreateImage.\n", lineNumber); + } + } +} + +void Player::DestroyImage(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_Images.find(origAllocPtr); + if(it != m_Images.end()) + { + vmaDestroyImage(m_Allocator, it->second.image, it->second.allocation); + m_Images.erase(it); + } + else + { + if(IssueWarning()) + printf("Line %zu: Allocation %llX for image not found.\n", lineNumber, origAllocPtr); + } + } + else + { + if(IssueWarning()) + printf("Line %zu: Invalid parameters for vmaDestroyBuffer.\n", lineNumber); + } + } +} + + //////////////////////////////////////////////////////////////////////////////// // Main functions @@ -456,13 +869,6 @@ static void PrintCommandLineSyntax() " VmaReplay \n"); } -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 int ProcessFile(const char* data, size_t numBytes) { // Begin stats. @@ -488,12 +894,14 @@ static int ProcessFile(const char* data, size_t numBytes) Player player; player.Init(); + 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 0; @@ -532,8 +940,6 @@ static int ProcessFile(const char* filePath) result = RESULT_ERROR_SOURCE_FILE; } - printf("Done.\n"); - return result; } From 4d63e9d886d4412afbe8115fd74a66e21a5cd636 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 7 Aug 2018 16:52:32 +0200 Subject: [PATCH 08/51] Improved error handling. --- src/VmaReplay/Common.cpp | 29 +-------- src/VmaReplay/Common.h | 8 +-- src/VmaReplay/VmaReplay.cpp | 120 +++++++++++++++++++++++------------- 3 files changed, 82 insertions(+), 75 deletions(-) diff --git a/src/VmaReplay/Common.cpp b/src/VmaReplay/Common.cpp index 8f2baf3..f04aece 100644 --- a/src/VmaReplay/Common.cpp +++ b/src/VmaReplay/Common.cpp @@ -1,19 +1,6 @@ #include "Common.h" -void ReadFile(std::vector& out, const char* fileName) -{ - std::ifstream file(fileName, std::ios::ate | std::ios::binary); - assert(file.is_open()); - size_t fileSize = (size_t)file.tellg(); - if(fileSize > 0) - { - out.resize(fileSize); - file.seekg(0); - file.read(out.data(), fileSize); - } - else - out.clear(); -} +/* void SetConsoleColor(CONSOLE_COLOR color) { @@ -139,16 +126,4 @@ void PrintErrorF(const wchar_t* format, ...) PrintMessageV(CONSOLE_COLOR::WARNING, format, argList); va_end(argList); } - -void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize) -{ - FILE* f = nullptr; - _wfopen_s(&f, filePath, L"wb"); - if(f) - { - fwrite(data, 1, dataSize, f); - fclose(f); - } - else - assert(0); -} +*/ diff --git a/src/VmaReplay/Common.h b/src/VmaReplay/Common.h index 70e7dad..d3ac260 100644 --- a/src/VmaReplay/Common.h +++ b/src/VmaReplay/Common.h @@ -22,8 +22,6 @@ typedef std::chrono::high_resolution_clock::time_point time_point; typedef std::chrono::high_resolution_clock::duration duration; -#define ERR_GUARD_VULKAN(Expr) do { VkResult res__ = (Expr); if (res__ < 0) assert(0); } while(0) - inline float ToFloatSeconds(duration d) { return std::chrono::duration_cast>(d).count(); @@ -41,6 +39,7 @@ static inline T align_up(T val, T align) return (val + align - 1) / align * align; } +/* class RandomNumberGenerator { public: @@ -54,8 +53,6 @@ private: uint32_t GenerateFast() { return m_Value = (m_Value * 196314165 + 907633515); } }; -void ReadFile(std::vector& out, const char* fileName); - enum class CONSOLE_COLOR { INFO, @@ -85,5 +82,4 @@ void PrintWarningF(const char* format, ...); void PrintWarningF(const wchar_t* format, ...); void PrintErrorF(const char* format, ...); void PrintErrorF(const wchar_t* format, ...); - -void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize); +*/ diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 6fc13b0..b4e24ac 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -28,6 +28,7 @@ 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 { @@ -46,24 +47,21 @@ static inline bool StrRangeEq(const StrRange& lhs, const char* rhsSz) static inline bool StrRangeToUint(const StrRange& s, uint32_t& out) { - // TODO handle failure. char* end = (char*)s.end; out = (uint32_t)strtoul(s.beg, &end, 10); - return true; + return end == s.end; } static inline bool StrRangeToUint(const StrRange& s, uint64_t& out) { - // TODO handle failure. char* end = (char*)s.end; out = (uint64_t)strtoull(s.beg, &end, 10); - return true; + return end == s.end; } static inline bool StrRangeToPtr(const StrRange& s, uint64_t& out) { - // TODO handle failure. char* end = (char*)s.end; out = (uint64_t)strtoull(s.beg, &end, 16); - return true; + return end == s.end; } //////////////////////////////////////////////////////////////////////////////// @@ -212,7 +210,7 @@ class Player { public: Player(); - void Init(); + int Init(); ~Player(); void ExecuteLine(size_t lineNumber, const StrRange& line); @@ -258,7 +256,7 @@ private: // Increments warning counter. Returns true if warning message should be printed. bool IssueWarning() { return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; } - void InitVulkan(); + int InitVulkan(); void FinalizeVulkan(); void RegisterDebugCallbacks(); @@ -277,9 +275,9 @@ Player::Player() { } -void Player::Init() +int Player::Init() { - InitVulkan(); + return InitVulkan(); } Player::~Player() @@ -355,16 +353,19 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) } } -void Player::InitVulkan() +int Player::InitVulkan() { printf("Initializing Vulkan...\n"); uint32_t instanceLayerPropCount = 0; - ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr) ); + VkResult res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr); + assert(res == VK_SUCCESS); + std::vector instanceLayerProps(instanceLayerPropCount); if(instanceLayerPropCount > 0) { - ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data()) ); + res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data()); + assert(res == VK_SUCCESS); } if(g_EnableValidationLayer == true) @@ -381,7 +382,7 @@ void Player::InitVulkan() //instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); std::vector instanceLayers; - if(g_EnableValidationLayer == true) + if(g_EnableValidationLayer) { instanceLayers.push_back(VALIDATION_LAYER_NAME); instanceExtensions.push_back("VK_EXT_debug_report"); @@ -401,18 +402,32 @@ void Player::InitVulkan() instInfo.enabledLayerCount = (uint32_t)instanceLayers.size(); instInfo.ppEnabledLayerNames = instanceLayers.data(); - ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, NULL, &m_VulkanInstance) ); + res = vkCreateInstance(&instInfo, NULL, &m_VulkanInstance); + if(res != VK_SUCCESS) + { + printf("ERROR: vkCreateInstance failed (%u)\n", res); + return RESULT_ERROR_VULKAN; + } - RegisterDebugCallbacks(); + if(g_EnableValidationLayer) + RegisterDebugCallbacks(); // Find physical device uint32_t deviceCount = 0; - ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(m_VulkanInstance, &deviceCount, nullptr) ); - assert(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 physicalDevices(deviceCount); - ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(m_VulkanInstance, &deviceCount, physicalDevices.data()) ); + res = vkEnumeratePhysicalDevices(m_VulkanInstance, &deviceCount, physicalDevices.data()); + assert(res == VK_SUCCESS); m_PhysicalDevice = physicalDevices[0]; @@ -420,21 +435,25 @@ void Player::InitVulkan() uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, nullptr); - assert(queueFamilyCount > 0); - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, queueFamilies.data()); - for(uint32_t i = 0; i < queueFamilyCount; ++i) + if(queueFamilyCount) { - if(queueFamilies[i].queueCount > 0) + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(m_PhysicalDevice, &queueFamilyCount, queueFamilies.data()); + for(uint32_t i = 0; i < queueFamilyCount; ++i) { - if((queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) + if(queueFamilies[i].queueCount > 0 && + (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { m_GraphicsQueueFamilyIndex = i; break; } } } - assert(m_GraphicsQueueFamilyIndex != UINT_MAX); + if(m_GraphicsQueueFamilyIndex == UINT_MAX) + { + printf("ERROR: Couldn't find graphics queue.\n"); + return RESULT_ERROR_VULKAN; + } // Create logical device @@ -459,12 +478,14 @@ void Player::InitVulkan() //enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); { uint32_t propertyCount = 0; - ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, nullptr) ); + res = vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, nullptr); + assert(res == VK_SUCCESS); if(propertyCount) { std::vector properties{propertyCount}; - ERR_GUARD_VULKAN( vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, properties.data()) ); + res = vkEnumerateDeviceExtensionProperties(m_PhysicalDevice, nullptr, &propertyCount, properties.data()); + assert(res == VK_SUCCESS); for(uint32_t i = 0; i < propertyCount; ++i) { @@ -489,7 +510,12 @@ void Player::InitVulkan() deviceCreateInfo.pQueueCreateInfos = &deviceQueueCreateInfo; deviceCreateInfo.pEnabledFeatures = &deviceFeatures; - ERR_GUARD_VULKAN( vkCreateDevice(m_PhysicalDevice, &deviceCreateInfo, nullptr, &m_Device) ); + 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 @@ -502,7 +528,14 @@ void Player::InitVulkan() allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; } - ERR_GUARD_VULKAN( vmaCreateAllocator(&allocatorInfo, &m_Allocator) ); + 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() @@ -587,7 +620,8 @@ void Player::RegisterDebugCallbacks() VK_DEBUG_REPORT_DEBUG_BIT_EXT*/; callbackCreateInfo.pfnCallback = &MyDebugReportCallback; - ERR_GUARD_VULKAN( m_pvkCreateDebugReportCallbackEXT(m_VulkanInstance, &callbackCreateInfo, nullptr, &m_hCallback) ); + VkResult res = m_pvkCreateDebugReportCallbackEXT(m_VulkanInstance, &callbackCreateInfo, nullptr, &m_hCallback); + assert(res == VK_SUCCESS); } bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound) @@ -892,24 +926,26 @@ static int ProcessFile(const char* data, size_t numBytes) } Player player; - player.Init(); - - printf("Playing...\n"); - while(lineSplit.GetNextLine(line)) + int result = player.Init(); + if(result == 0) { - player.ExecuteLine(lineSplit.GetNextLineIndex(), line); + printf("Playing...\n"); + while(lineSplit.GetNextLine(line)) + { + player.ExecuteLine(lineSplit.GetNextLineIndex(), line); + } + + // End stats. + printf("Done.\n"); + printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); } - // End stats. - printf("Done.\n"); - printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); - - return 0; + return result; } static int ProcessFile(const char* filePath) { - printf("Replaying file \"%s\"...\n", filePath); + printf("Loading file \"%s\"...\n", filePath); int result = 0; FILE* file = nullptr; From 71f3d067c52480bbec40e1f0f6e2115339a687d1 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 7 Aug 2018 17:09:51 +0200 Subject: [PATCH 09/51] Better tracking of allocations. --- src/VmaReplay/VmaReplay.cpp | 153 ++++++++++++++++-------------------- src/vk_mem_alloc.h | 14 +++- 2 files changed, 80 insertions(+), 87 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index b4e24ac..24222c5 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -233,25 +233,22 @@ private: uint32_t m_VmaFrameIndex = 0; - // Any of these handles null means it was created in original but couldn't be created now. + // Any of these handles null can mean it was created in original but couldn't be created now. struct Pool { VmaPool pool; }; - struct Buffer + struct Allocation { + VmaAllocation allocation; VkBuffer buffer; - VmaAllocation allocation; - }; - struct Image - { VkImage image; - VmaAllocation allocation; }; std::unordered_map m_Pools; - std::unordered_map m_Buffers; - std::unordered_map m_Images; + std::unordered_map m_Allocations; + + 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; } @@ -263,12 +260,15 @@ private: // 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 CreatePool(size_t lineNumber, const CsvSplit& csvSplit); - void DestroyPool(size_t lineNumber, const CsvSplit& csvSplit); - void CreateBuffer(size_t lineNumber, const CsvSplit& csvSplit); - void DestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit); - void CreateImage(size_t lineNumber, const CsvSplit& csvSplit); - void DestroyImage(size_t lineNumber, const CsvSplit& csvSplit); + 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() @@ -322,9 +322,9 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) } } else if(StrRangeEq(functionName, "vmaCreatePool")) - CreatePool(lineNumber, csvSplit); + ExecuteCreatePool(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaDestroyPool")) - DestroyPool(lineNumber, csvSplit); + ExecuteDestroyPool(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaSetAllocationUserData")) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true)) @@ -333,13 +333,15 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) } } else if(StrRangeEq(functionName, "vmaCreateBuffer")) - CreateBuffer(lineNumber, csvSplit); + ExecuteCreateBuffer(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaDestroyBuffer")) - DestroyBuffer(lineNumber, csvSplit); + ExecuteDestroyBuffer(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaCreateImage")) - CreateImage(lineNumber, csvSplit); + ExecuteCreateImage(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaDestroyImage")) - DestroyImage(lineNumber, csvSplit); + ExecuteDestroyImage(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaFreeMemory")) + ExecuteFreeMemory(lineNumber, csvSplit); else { if(IssueWarning()) @@ -353,6 +355,21 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) } } +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"); @@ -540,24 +557,16 @@ int Player::InitVulkan() void Player::FinalizeVulkan() { - if(!m_Images.empty()) + if(!m_Allocations.empty()) { - printf("WARNING: Images not destroyed: %zu.\n", m_Images.size()); + printf("WARNING: Allocations not destroyed: %zu.\n", m_Allocations.size()); - for(const auto it : m_Images) + for(const auto it : m_Allocations) { - vmaDestroyImage(m_Allocator, it.second.image, it.second.allocation); + Destroy(it.second); } - } - if(!m_Buffers.empty()) - { - printf("WARNING: Buffers not destroyed: %zu.\n", m_Buffers.size()); - - for(const auto it : m_Buffers) - { - vmaDestroyBuffer(m_Allocator, it.second.buffer, it.second.allocation); - } + m_Allocations.clear(); } if(!m_Pools.empty()) @@ -565,9 +574,9 @@ void Player::FinalizeVulkan() 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); @@ -641,7 +650,7 @@ bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& c return ok; } -void Player::CreatePool(size_t lineNumber, const CsvSplit& csvSplit) +void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 7, false)) { @@ -682,7 +691,7 @@ void Player::CreatePool(size_t lineNumber, const CsvSplit& csvSplit) } } -void Player::DestroyPool(size_t lineNumber, const CsvSplit& csvSplit) +void Player::ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false)) { @@ -710,7 +719,7 @@ void Player::DestroyPool(size_t lineNumber, const CsvSplit& csvSplit) } } -void Player::CreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) +void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 12, true)) { @@ -743,15 +752,15 @@ void Player::CreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) } } - Buffer bufDesc = {}; - VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &bufDesc.buffer, &bufDesc.allocation, nullptr); + Allocation allocDesc = {}; + VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr); if(res == VK_SUCCESS) { - const auto existingIt = m_Buffers.find(origPtr); - if(existingIt != m_Buffers.end()) + const auto existingIt = m_Allocations.find(origPtr); + if(existingIt != m_Allocations.end()) { if(IssueWarning()) - printf("Line %zu: Allocation %llX for buffer already exists.\n", lineNumber, origPtr); + printf("Line %zu: Allocation %llX already exists.\n", lineNumber, origPtr); } } else @@ -759,7 +768,7 @@ void Player::CreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) if(IssueWarning()) printf("Line %zu: vmaCreateBuffer failed (%u).\n", lineNumber, res); } - m_Buffers[origPtr] = bufDesc; + m_Allocations[origPtr] = allocDesc; } else { @@ -769,7 +778,7 @@ void Player::CreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) } } -void Player::DestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit) +void Player::DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false)) { @@ -777,16 +786,16 @@ void Player::DestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit) if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtr)) { - const auto it = m_Buffers.find(origAllocPtr); - if(it != m_Buffers.end()) + const auto it = m_Allocations.find(origAllocPtr); + if(it != m_Allocations.end()) { - vmaDestroyBuffer(m_Allocator, it->second.buffer, it->second.allocation); - m_Buffers.erase(it); + Destroy(it->second); + m_Allocations.erase(it); } else { if(IssueWarning()) - printf("Line %zu: Allocation %llX for buffer not found.\n", lineNumber, origAllocPtr); + printf("Line %zu: Allocation %llX not found.\n", lineNumber, origAllocPtr); } } else @@ -797,7 +806,7 @@ void Player::DestroyBuffer(size_t lineNumber, const CsvSplit& csvSplit) } } -void Player::CreateImage(size_t lineNumber, const CsvSplit& csvSplit) +void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 21, true)) { @@ -839,15 +848,15 @@ void Player::CreateImage(size_t lineNumber, const CsvSplit& csvSplit) } } - Image imageDesc = {}; - VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &imageDesc.image, &imageDesc.allocation, nullptr); + Allocation allocDesc = {}; + VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &allocDesc.image, &allocDesc.allocation, nullptr); if(res == VK_SUCCESS) { - const auto existingIt = m_Images.find(origPtr); - if(existingIt != m_Images.end()) + const auto existingIt = m_Allocations.find(origPtr); + if(existingIt != m_Allocations.end()) { if(IssueWarning()) - printf("Line %zu: Allocation %llX for image already exists.\n", lineNumber, origPtr); + printf("Line %zu: Allocation %llX already exists.\n", lineNumber, origPtr); } } else @@ -855,7 +864,7 @@ void Player::CreateImage(size_t lineNumber, const CsvSplit& csvSplit) if(IssueWarning()) printf("Line %zu: vmaCreateImage failed (%u).\n", lineNumber, res); } - m_Images[origPtr] = imageDesc; + m_Allocations[origPtr] = allocDesc; } else { @@ -865,34 +874,6 @@ void Player::CreateImage(size_t lineNumber, const CsvSplit& csvSplit) } } -void Player::DestroyImage(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_Images.find(origAllocPtr); - if(it != m_Images.end()) - { - vmaDestroyImage(m_Allocator, it->second.image, it->second.allocation); - m_Images.erase(it); - } - else - { - if(IssueWarning()) - printf("Line %zu: Allocation %llX for image not found.\n", lineNumber, origAllocPtr); - } - } - else - { - if(IssueWarning()) - printf("Line %zu: Invalid parameters for vmaDestroyBuffer.\n", lineNumber); - } - } -} - //////////////////////////////////////////////////////////////////////////////// // Main functions diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 5dbad64..53943bd 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -9927,8 +9927,20 @@ void vmaFreeMemory( VmaAllocator allocator, VmaAllocation allocation) { - Crash(); VMA_ASSERT(allocator); + + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", threadId, time, frameIndex, + allocation); + fflush(g_File); + } + VMA_DEBUG_LOG("vmaFreeMemory"); VMA_DEBUG_GLOBAL_MUTEX_LOCK if(allocation != VK_NULL_HANDLE) From deeb817d7a071904ea33ed32ac69c184a9b4b652 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 7 Aug 2018 17:30:29 +0200 Subject: [PATCH 10/51] Minor fixes. Added basic statistics. --- src/VmaReplay/VmaReplay.cpp | 85 +++++++++++++++++++++++++++++++------ src/vk_mem_alloc.h | 2 +- 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 24222c5..c0144bc 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -149,6 +149,39 @@ void CsvSplit::Set(const StrRange& line, size_t maxCount) 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 @@ -248,6 +281,8 @@ private: std::unordered_map m_Pools; std::unordered_map m_Allocations; + Statistics m_Stats; + void Destroy(const Allocation& alloc); // Increments warning counter. Returns true if warning message should be printed. @@ -256,6 +291,7 @@ private: 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); @@ -282,6 +318,8 @@ int Player::Init() Player::~Player() { + PrintStats(); + FinalizeVulkan(); if(m_WarningCount > MAX_WARNINGS_TO_SHOW) @@ -321,6 +359,13 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) // 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")) @@ -633,6 +678,14 @@ void Player::RegisterDebugCallbacks() 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; @@ -756,18 +809,21 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr); if(res == VK_SUCCESS) { - 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_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 @@ -852,18 +908,21 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &allocDesc.image, &allocDesc.allocation, nullptr); if(res == VK_SUCCESS) { - 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_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 @@ -900,7 +959,7 @@ static int ProcessFile(const char* data, size_t numBytes) } if(!lineSplit.GetNextLine(line) || - !StrRangeEq(line, "1,0")) + !(StrRangeEq(line, "1,0") || StrRangeEq(line, "1,1"))) { printf("ERROR: Incorrect file format version.\n"); return RESULT_ERROR_FORMAT; diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 53943bd..834adc5 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -2666,7 +2666,7 @@ void EnsureFile() { fopen_s(&g_File, "VMA_Usage_Dump", "wb"); fprintf(g_File, "%s\n", "Vulkan Memory Allocator,Calls recording"); - fprintf(g_File, "%s\n", "1,0"); + fprintf(g_File, "%s\n", "1,1"); QueryPerformanceFrequency(&g_Freq); QueryPerformanceCounter(&g_StartCounter); } From f188a152de0df5bfbf547997e6d6d989d40643aa Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 7 Aug 2018 17:58:46 +0200 Subject: [PATCH 11/51] Minor fixes. --- src/VmaReplay/VmaReplay.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index c0144bc..c37b463 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -720,20 +720,19 @@ void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit) { Pool poolDesc = {}; VkResult res = vmaCreatePool(m_Allocator, &poolCreateInfo, &poolDesc.pool); - if(res == VK_SUCCESS) - { - const auto existingIt = m_Pools.find(origPtr); - if(existingIt != m_Pools.end()) - { - if(IssueWarning()) - printf("Line %zu: Pool %llX already exists.\n", lineNumber, origPtr); - } - } - else + 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 @@ -1034,7 +1033,7 @@ int main(int argc, char** argv) { try { - main2(argc, argv); + return main2(argc, argv); } catch(const std::exception& e) { @@ -1046,6 +1045,4 @@ int main(int argc, char** argv) printf("UNKNOWN ERROR\n"); return RESULT_EXCEPTION; } - - return 0; } From 5b48b5efcfc36d7f595d2b8ad51e431413e06c14 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 13 Aug 2018 12:15:50 +0200 Subject: [PATCH 12/51] Code formatting --- src/VmaReplay/VmaReplay.cpp | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index c37b463..6462461 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -347,7 +347,9 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) else { if(IssueWarning()) + { printf("Line %zu: Incorrect frame index.\n", lineNumber); + } } StrRange functionName = csvSplit.GetRange(3); @@ -390,13 +392,17 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) else { if(IssueWarning()) + { printf("Line %zu: Unknown function.\n", lineNumber); + } } } else { if(IssueWarning()) + { printf("Line %zu: Too few columns.\n", lineNumber); + } } } @@ -697,7 +703,9 @@ bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& c if(!ok) { if(IssueWarning()) + { printf("Line %zu: Incorrect number of function parameters.\n", lineNumber); + } } return ok; @@ -723,14 +731,18 @@ void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit) 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; @@ -738,7 +750,9 @@ void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit) else { if(IssueWarning()) + { printf("Line %zu: Invalid parameters for vmaCreatePool.\n", lineNumber); + } } } } @@ -760,13 +774,17 @@ void Player::ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit) 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); + } } } } @@ -800,7 +818,9 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) else { if(IssueWarning()) + { printf("Line %zu: Pool %llX not found.\n", lineNumber, origPool); + } } } @@ -813,14 +833,18 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) 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; @@ -828,7 +852,9 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) else { if(IssueWarning()) + { printf("Line %zu: Invalid parameters for vmaCreateBuffer.\n", lineNumber); + } } } } @@ -850,13 +876,17 @@ void Player::DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit) 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); + } } } } @@ -895,11 +925,15 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) { 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); + } } } @@ -912,14 +946,18 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) 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; @@ -927,7 +965,9 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) else { if(IssueWarning()) + { printf("Line %zu: Invalid parameters for vmaCreateImage.\n", lineNumber); + } } } } From db1f73fee4e21f478e8411bbca6184135f79058e Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 13 Aug 2018 12:22:37 +0200 Subject: [PATCH 13/51] Improved handling of destruction calls with null handle, in vk_mem_alloc as well as VmaReplay. --- src/VmaReplay/VmaReplay.cpp | 42 +++++++++++++++++++++---------------- src/vk_mem_alloc.h | 21 +++++++++++++++---- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 6462461..eaac603 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -765,17 +765,20 @@ void Player::ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit) if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr)) { - const auto it = m_Pools.find(origPtr); - if(it != m_Pools.end()) + if(origPtr != 0) { - vmaDestroyPool(m_Allocator, it->second.pool); - m_Pools.erase(it); - } - else - { - if(IssueWarning()) + const auto it = m_Pools.find(origPtr); + if(it != m_Pools.end()) { - printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr); + vmaDestroyPool(m_Allocator, it->second.pool); + m_Pools.erase(it); + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Pool %llX not found.\n", lineNumber, origPtr); + } } } } @@ -867,17 +870,20 @@ void Player::DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit) if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origAllocPtr)) { - const auto it = m_Allocations.find(origAllocPtr); - if(it != m_Allocations.end()) + if(origAllocPtr != 0) { - Destroy(it->second); - m_Allocations.erase(it); - } - else - { - if(IssueWarning()) + const auto it = m_Allocations.find(origAllocPtr); + if(it != m_Allocations.end()) { - printf("Line %zu: Allocation %llX not found.\n", lineNumber, origAllocPtr); + Destroy(it->second); + m_Allocations.erase(it); + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Allocation %llX not found.\n", lineNumber, origAllocPtr); + } } } } diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 834adc5..6250dd6 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -9929,6 +9929,11 @@ void vmaFreeMemory( { VMA_ASSERT(allocator); + if(allocation == VK_NULL_HANDLE) + { + return; + } + { VmaMutexLock lock(g_FileMutex, true); EnsureFile(); @@ -9943,10 +9948,7 @@ void vmaFreeMemory( VMA_DEBUG_LOG("vmaFreeMemory"); VMA_DEBUG_GLOBAL_MUTEX_LOCK - if(allocation != VK_NULL_HANDLE) - { - allocator->FreeMemory(allocation); - } + allocator->FreeMemory(allocation); } void vmaGetAllocationInfo( @@ -10232,6 +10234,11 @@ void vmaDestroyBuffer( { VMA_ASSERT(allocator); + if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + { VmaMutexLock lock(g_FileMutex, true); EnsureFile(); @@ -10356,6 +10363,12 @@ void vmaDestroyImage( { VMA_ASSERT(allocator); + if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + { VmaMutexLock lock(g_FileMutex, true); EnsureFile(); From 652ac275470c9aa9c068cc7627ef647643002b7e Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 13 Aug 2018 13:13:13 +0200 Subject: [PATCH 14/51] Added CmdLineParser, proper parsing of command line parameters, and -v parameter for verbosity. --- src/VmaReplay/Common.cpp | 431 ++++++++++++++++++++++++++++++++++++ src/VmaReplay/Common.h | 61 +++++ src/VmaReplay/VmaReplay.cpp | 139 ++++++++++-- 3 files changed, 616 insertions(+), 15 deletions(-) diff --git a/src/VmaReplay/Common.cpp b/src/VmaReplay/Common.cpp index f04aece..4c1a6b7 100644 --- a/src/VmaReplay/Common.cpp +++ b/src/VmaReplay/Common.cpp @@ -1,5 +1,436 @@ #include "Common.h" +//////////////////////////////////////////////////////////////////////////////// +// class CmdLineParser + +bool CmdLineParser::ReadNextArg(std::string *OutArg) +{ + if (m_argv != NULL) + { + if (m_ArgIndex >= (size_t)m_argc) return false; + + *OutArg = m_argv[m_ArgIndex]; + m_ArgIndex++; + return true; + } + else + { + if (m_ArgIndex >= m_CmdLineLength) return false; + + OutArg->clear(); + bool InsideQuotes = false; + while (m_ArgIndex < m_CmdLineLength) + { + char Ch = m_CmdLine[m_ArgIndex]; + if (Ch == '\\') + { + bool FollowedByQuote = false; + size_t BackslashCount = 1; + size_t TmpIndex = m_ArgIndex + 1; + while (TmpIndex < m_CmdLineLength) + { + char TmpCh = m_CmdLine[TmpIndex]; + if (TmpCh == '\\') + { + BackslashCount++; + TmpIndex++; + } + else if (TmpCh == '"') + { + FollowedByQuote = true; + break; + } + else + break; + } + + if (FollowedByQuote) + { + if (BackslashCount % 2 == 0) + { + for (size_t i = 0; i < BackslashCount / 2; i++) + *OutArg += '\\'; + m_ArgIndex += BackslashCount + 1; + InsideQuotes = !InsideQuotes; + } + else + { + for (size_t i = 0; i < BackslashCount / 2; i++) + *OutArg += '\\'; + *OutArg += '"'; + m_ArgIndex += BackslashCount + 1; + } + } + else + { + for (size_t i = 0; i < BackslashCount; i++) + *OutArg += '\\'; + m_ArgIndex += BackslashCount; + } + } + else if (Ch == '"') + { + InsideQuotes = !InsideQuotes; + m_ArgIndex++; + } + else if (isspace(Ch)) + { + if (InsideQuotes) + { + *OutArg += Ch; + m_ArgIndex++; + } + else + { + m_ArgIndex++; + break; + } + } + else + { + *OutArg += Ch; + m_ArgIndex++; + } + } + + while (m_ArgIndex < m_CmdLineLength && isspace(m_CmdLine[m_ArgIndex])) + m_ArgIndex++; + + return true; + } +} + +CmdLineParser::SHORT_OPT * CmdLineParser::FindShortOpt(char Opt) +{ + for (size_t i = 0; i < m_ShortOpts.size(); i++) + if (m_ShortOpts[i].Opt == Opt) + return &m_ShortOpts[i]; + return NULL; +} + +CmdLineParser::LONG_OPT * CmdLineParser::FindLongOpt(const std::string &Opt) +{ + for (size_t i = 0; i < m_LongOpts.size(); i++) + if (m_LongOpts[i].Opt == Opt) + return &m_LongOpts[i]; + return NULL; +} + +CmdLineParser::CmdLineParser(int argc, char **argv) : + m_argv(argv), + m_CmdLine(NULL), + m_argc(argc), + m_CmdLineLength(0), + m_ArgIndex(1), + m_InsideMultioption(false), + m_LastArgIndex(0), + m_LastOptId(0) +{ + assert(argc > 0); + assert(argv != NULL); +} + +CmdLineParser::CmdLineParser(const char *CmdLine) : + m_argv(NULL), + m_CmdLine(CmdLine), + m_argc(0), + m_ArgIndex(0), + m_InsideMultioption(false), + m_LastArgIndex(0), + m_LastOptId(0) +{ + assert(CmdLine != NULL); + + m_CmdLineLength = strlen(m_CmdLine); + + while (m_ArgIndex < m_CmdLineLength && isspace(m_CmdLine[m_ArgIndex])) + m_ArgIndex++; +} + +void CmdLineParser::RegisterOpt(uint32_t Id, char Opt, bool Parameter) +{ + assert(Opt != '\0'); + + m_ShortOpts.push_back(SHORT_OPT(Id, Opt, Parameter)); +} + +void CmdLineParser::RegisterOpt(uint32_t Id, const std::string &Opt, bool Parameter) +{ + assert(!Opt.empty()); + + m_LongOpts.push_back(LONG_OPT(Id, Opt, Parameter)); +} + +CmdLineParser::RESULT CmdLineParser::ReadNext() +{ + if (m_InsideMultioption) + { + assert(m_LastArgIndex < m_LastArg.length()); + SHORT_OPT *so = FindShortOpt(m_LastArg[m_LastArgIndex]); + if (so == NULL) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + if (so->Parameter) + { + if (m_LastArg.length() == m_LastArgIndex+1) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_InsideMultioption = false; + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else if (m_LastArg[m_LastArgIndex+1] == '=') + { + m_InsideMultioption = false; + m_LastParameter = m_LastArg.substr(m_LastArgIndex+2); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + m_InsideMultioption = false; + m_LastParameter = m_LastArg.substr(m_LastArgIndex+1); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + else + { + if (m_LastArg.length() == m_LastArgIndex+1) + { + m_InsideMultioption = false; + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + m_LastArgIndex++; + + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + } + else + { + if (!ReadNextArg(&m_LastArg)) + { + m_LastParameter.clear(); + m_LastOptId = 0; + return CmdLineParser::RESULT_END; + } + + if (!m_LastArg.empty() && m_LastArg[0] == '-') + { + if (m_LastArg.length() > 1 && m_LastArg[1] == '-') + { + size_t EqualIndex = m_LastArg.find('=', 2); + if (EqualIndex != std::string::npos) + { + LONG_OPT *lo = FindLongOpt(m_LastArg.substr(2, EqualIndex-2)); + if (lo == NULL || lo->Parameter == false) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_LastParameter = m_LastArg.substr(EqualIndex+1); + m_LastOptId = lo->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + LONG_OPT *lo = FindLongOpt(m_LastArg.substr(2)); + if (lo == NULL) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + if (lo->Parameter) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + } + else + m_LastParameter.clear(); + m_LastOptId = lo->Id; + return CmdLineParser::RESULT_OPT; + } + } + else + { + if (m_LastArg.length() < 2) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + SHORT_OPT *so = FindShortOpt(m_LastArg[1]); + if (so == NULL) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + if (so->Parameter) + { + if (m_LastArg.length() == 2) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else if (m_LastArg[2] == '=') + { + m_LastParameter = m_LastArg.substr(3); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + m_LastParameter = m_LastArg.substr(2); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + else + { + if (m_LastArg.length() == 2) + { + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + m_InsideMultioption = true; + m_LastArgIndex = 2; + + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + } + } + else if (!m_LastArg.empty() && m_LastArg[0] == '/') + { + size_t EqualIndex = m_LastArg.find('=', 1); + if (EqualIndex != std::string::npos) + { + if (EqualIndex == 2) + { + SHORT_OPT *so = FindShortOpt(m_LastArg[1]); + if (so != NULL) + { + if (so->Parameter == false) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_LastParameter = m_LastArg.substr(EqualIndex+1); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + LONG_OPT *lo = FindLongOpt(m_LastArg.substr(1, EqualIndex-1)); + if (lo == NULL || lo->Parameter == false) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + m_LastParameter = m_LastArg.substr(EqualIndex+1); + m_LastOptId = lo->Id; + return CmdLineParser::RESULT_OPT; + } + else + { + if (m_LastArg.length() == 2) + { + SHORT_OPT *so = FindShortOpt(m_LastArg[1]); + if (so != NULL) + { + if (so->Parameter) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + } + else + m_LastParameter.clear(); + m_LastOptId = so->Id; + return CmdLineParser::RESULT_OPT; + } + } + LONG_OPT *lo = FindLongOpt(m_LastArg.substr(1)); + if (lo == NULL) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + if (lo->Parameter) + { + if (!ReadNextArg(&m_LastParameter)) + { + m_LastOptId = 0; + m_LastParameter.clear(); + return CmdLineParser::RESULT_ERROR; + } + } + else + m_LastParameter.clear(); + m_LastOptId = lo->Id; + return CmdLineParser::RESULT_OPT; + } + } + else + { + m_LastOptId = 0; + m_LastParameter = m_LastArg; + return CmdLineParser::RESULT_PARAMETER; + } + } +} + +uint32_t CmdLineParser::GetOptId() +{ + return m_LastOptId; +} + +const std::string & CmdLineParser::GetParameter() +{ + return m_LastParameter; +} + +//////////////////////////////////////////////////////////////////////////////// +// Glolals + /* void SetConsoleColor(CONSOLE_COLOR color) diff --git a/src/VmaReplay/Common.h b/src/VmaReplay/Common.h index d3ac260..afd52c1 100644 --- a/src/VmaReplay/Common.h +++ b/src/VmaReplay/Common.h @@ -39,6 +39,67 @@ static inline T align_up(T val, T align) return (val + align - 1) / align * align; } +class CmdLineParser +{ +public: + enum RESULT + { + RESULT_OPT, + RESULT_PARAMETER, + RESULT_END, + RESULT_ERROR, + }; + + CmdLineParser(int argc, char **argv); + CmdLineParser(const char *CmdLine); + + void RegisterOpt(uint32_t Id, char Opt, bool Parameter); + void RegisterOpt(uint32_t Id, const std::string &Opt, bool Parameter); + + RESULT ReadNext(); + uint32_t GetOptId(); + const std::string & GetParameter(); + +private: + struct SHORT_OPT + { + uint32_t Id; + char Opt; + bool Parameter; + + SHORT_OPT(uint32_t Id, char Opt, bool Parameter) : Id(Id), Opt(Opt), Parameter(Parameter) { } + }; + + struct LONG_OPT + { + uint32_t Id; + std::string Opt; + bool Parameter; + + LONG_OPT(uint32_t Id, std::string Opt, bool Parameter) : Id(Id), Opt(Opt), Parameter(Parameter) { } + }; + + char **m_argv; + const char *m_CmdLine; + int m_argc; + size_t m_CmdLineLength; + size_t m_ArgIndex; + + bool ReadNextArg(std::string *OutArg); + + std::vector m_ShortOpts; + std::vector m_LongOpts; + + SHORT_OPT * FindShortOpt(char Opt); + LONG_OPT * FindLongOpt(const std::string &Opt); + + bool m_InsideMultioption; + std::string m_LastArg; + size_t m_LastArgIndex; + uint32_t m_LastOptId; + std::string m_LastParameter; +}; + /* class RandomNumberGenerator { diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index eaac603..dd88f42 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -30,11 +30,31 @@ static const int RESULT_ERROR_SOURCE_FILE = -2; static const int RESULT_ERROR_FORMAT = -3; static const int RESULT_ERROR_VULKAN = -4; +enum CMD_LINE_OPT +{ + CMD_LINE_OPT_VERBOSITY, +}; + +static enum class VERBOSITY +{ + MINIMUM = 0, + DEFAULT, + MAXIMUM, + COUNT, +} g_Verbosity = VERBOSITY::DEFAULT; + +static std::string g_FilePath; + struct StrRange { const char* beg; const char* end; + StrRange() { } + StrRange(const char* beg, const char* end) : beg(beg), end(end) { } + explicit StrRange(const char* sz) : beg(sz), end(sz + strlen(sz)) { } + explicit StrRange(const std::string& s) : beg(s.data()), end(s.data() + s.length()) { } + size_t length() const { return end - beg; } }; @@ -286,7 +306,7 @@ private: 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; } + bool IssueWarning(); int InitVulkan(); void FinalizeVulkan(); @@ -318,11 +338,14 @@ int Player::Init() Player::~Player() { - PrintStats(); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + PrintStats(); + } FinalizeVulkan(); - if(m_WarningCount > MAX_WARNINGS_TO_SHOW) + if(g_Verbosity < VERBOSITY::MAXIMUM && m_WarningCount > MAX_WARNINGS_TO_SHOW) printf("WARNING: %zu more warnings not shown.\n", m_WarningCount - MAX_WARNINGS_TO_SHOW); } @@ -421,9 +444,25 @@ void Player::Destroy(const Allocation& alloc) vmaFreeMemory(m_Allocator, alloc.allocation); } +bool Player::IssueWarning() +{ + if(g_Verbosity < VERBOSITY::MAXIMUM) + { + return m_WarningCount++ < MAX_WARNINGS_TO_SHOW; + } + else + { + ++m_WarningCount; + return true; + } +} + int Player::InitVulkan() { - printf("Initializing Vulkan...\n"); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("Initializing Vulkan...\n"); + } uint32_t instanceLayerPropCount = 0; VkResult res = vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr); @@ -984,14 +1023,24 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) static void PrintCommandLineSyntax() { - printf("Command line syntax:\n" - " VmaReplay \n"); + printf( + "Command line syntax:\n" + " VmaReplay [Options] \n" + "Available options:\n" + " -v - Verbosity level:\n" + " 0 - Minimum verbosity. Prints only warnings and errors.\n" + " 1 - Default verbosity. Prints important messages and statistics.\n" + " 2 - Maximum verbosity. Prints a lot of information.\n" + ); } static int ProcessFile(const char* data, size_t numBytes) { // Begin stats. - printf("File size: %zu B\n", numBytes); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("File size: %zu B\n", numBytes); + } LineSplit lineSplit(data, numBytes); StrRange line; @@ -1014,27 +1063,36 @@ static int ProcessFile(const char* data, size_t numBytes) int result = player.Init(); if(result == 0) { - printf("Playing...\n"); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("Playing...\n"); + } while(lineSplit.GetNextLine(line)) { player.ExecuteLine(lineSplit.GetNextLineIndex(), line); } // End stats. - printf("Done.\n"); - printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("Done.\n"); + printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); + } } return result; } -static int ProcessFile(const char* filePath) +static int ProcessFile() { - printf("Loading file \"%s\"...\n", filePath); + if(g_Verbosity > VERBOSITY::MINIMUM) + { + printf("Loading file \"%s\"...\n", g_FilePath.c_str()); + } int result = 0; FILE* file = nullptr; - const errno_t err = fopen_s(&file, filePath, "rb"); + const errno_t err = fopen_s(&file, g_FilePath.c_str(), "rb"); if(err == 0) { _fseeki64(file, 0, SEEK_END); @@ -1066,13 +1124,64 @@ static int ProcessFile(const char* filePath) static int main2(int argc, char** argv) { - if(argc != 2) + CmdLineParser cmdLineParser(argc, argv); + + cmdLineParser.RegisterOpt(CMD_LINE_OPT_VERBOSITY, 'v', true); + + CmdLineParser::RESULT res; + while((res = cmdLineParser.ReadNext()) != CmdLineParser::RESULT_END) + { + switch(res) + { + case CmdLineParser::RESULT_OPT: + switch(cmdLineParser.GetOptId()) + { + case CMD_LINE_OPT_VERBOSITY: + { + uint32_t verbosityVal = UINT32_MAX; + if(StrRangeToUint(StrRange(cmdLineParser.GetParameter()), verbosityVal) && + verbosityVal < (uint32_t)VERBOSITY::COUNT) + { + g_Verbosity = (VERBOSITY)verbosityVal; + } + else + { + PrintCommandLineSyntax(); + return RESULT_ERROR_COMMAND_LINE; + } + } + break; + default: + assert(0); + } + break; + case CmdLineParser::RESULT_PARAMETER: + if(g_FilePath.empty()) + { + g_FilePath = cmdLineParser.GetParameter(); + } + else + { + PrintCommandLineSyntax(); + return RESULT_ERROR_COMMAND_LINE; + } + break; + case CmdLineParser::RESULT_ERROR: + PrintCommandLineSyntax(); + return RESULT_ERROR_COMMAND_LINE; + break; + default: + assert(0); + } + } + + if(g_FilePath.empty()) { PrintCommandLineSyntax(); return RESULT_ERROR_COMMAND_LINE; } - return ProcessFile(argv[1]); + return ProcessFile(); } int main(int argc, char** argv) From 1016cc682c2d9cc65c5406638d803f9add1a25ad Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 13 Aug 2018 13:30:04 +0200 Subject: [PATCH 15/51] Added function SecondsToFriendlyStr. Printing playback duration. --- src/VmaReplay/Common.cpp | 67 +++++++++++++++++++++++++++++++++++++ src/VmaReplay/Common.h | 2 ++ src/VmaReplay/VmaReplay.cpp | 9 +++++ 3 files changed, 78 insertions(+) diff --git a/src/VmaReplay/Common.cpp b/src/VmaReplay/Common.cpp index 4c1a6b7..dffd2af 100644 --- a/src/VmaReplay/Common.cpp +++ b/src/VmaReplay/Common.cpp @@ -558,3 +558,70 @@ void PrintErrorF(const wchar_t* format, ...) va_end(argList); } */ + +void SecondsToFriendlyStr(float seconds, std::string& out) +{ + if(seconds == 0.f) + { + out = "0"; + return; + } + + if (seconds < 0.f) + { + out = "-"; + seconds = -seconds; + } + else + { + out.clear(); + } + + char s[32]; + + // #.# ns + if(seconds < 1e-6) + { + sprintf_s(s, "%.4f ns", seconds * 1e-9); + out += s; + } + // #.# us + else if(seconds < 1e-3) + { + sprintf_s(s, "%.4f us", seconds * 1e-6); + out += s; + } + // #.# ms + else if(seconds < 1.f) + { + sprintf_s(s, "%.4f ms", seconds * 1e-3); + out += s; + } + else if(seconds < 60.f) + { + sprintf_s(s, "%.4f s", seconds); + out += s; + } + else + { + uint64_t seconds_u = (uint64_t)seconds; + // "#:## min" + if (seconds_u < 3600) + { + uint64_t minutes = seconds_u / 60; + seconds_u -= minutes * 60; + sprintf_s(s, "%llu:%02llu min", minutes, seconds_u); + out += s; + } + // "#:##:## h" + else + { + uint64_t minutes = seconds_u / 60; + seconds_u -= minutes * 60; + uint64_t hours = minutes / 60; + minutes -= hours * 60; + sprintf_s(s, "%llu:%02llu:%02llu h", hours, minutes, seconds_u); + out += s; + } + } +} diff --git a/src/VmaReplay/Common.h b/src/VmaReplay/Common.h index afd52c1..f5f4c44 100644 --- a/src/VmaReplay/Common.h +++ b/src/VmaReplay/Common.h @@ -27,6 +27,8 @@ inline float ToFloatSeconds(duration d) return std::chrono::duration_cast>(d).count(); } +void SecondsToFriendlyStr(float seconds, std::string& out); + template inline T ceil_div(T x, T y) { diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index dd88f42..7470e48 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -1067,15 +1067,24 @@ static int ProcessFile(const char* data, size_t numBytes) { printf("Playing...\n"); } + + const time_point timeBeg = std::chrono::high_resolution_clock::now(); + while(lineSplit.GetNextLine(line)) { player.ExecuteLine(lineSplit.GetNextLineIndex(), line); } + const duration playDuration = std::chrono::high_resolution_clock::now() - timeBeg; + // End stats. if(g_Verbosity > VERBOSITY::MINIMUM) { + std::string playDurationStr; + SecondsToFriendlyStr(ToFloatSeconds(playDuration), playDurationStr); + printf("Done.\n"); + printf("Playback took: %s\n", playDurationStr.c_str()); printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); } } From 03764c6ea016abd585da7d289364b3149ff281dd Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 13 Aug 2018 13:38:46 +0200 Subject: [PATCH 16/51] Statistics: added printing of original recording time. --- src/VmaReplay/Common.cpp | 15 ++++++++------- src/VmaReplay/VmaReplay.cpp | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/VmaReplay/Common.cpp b/src/VmaReplay/Common.cpp index dffd2af..cfb44dc 100644 --- a/src/VmaReplay/Common.cpp +++ b/src/VmaReplay/Common.cpp @@ -579,27 +579,28 @@ void SecondsToFriendlyStr(float seconds, std::string& out) char s[32]; - // #.# ns + // #.### ns if(seconds < 1e-6) { - sprintf_s(s, "%.4f ns", seconds * 1e-9); + sprintf_s(s, "%.3f ns", seconds * 1e-9); out += s; } - // #.# us + // #.### us else if(seconds < 1e-3) { - sprintf_s(s, "%.4f us", seconds * 1e-6); + sprintf_s(s, "%.3f us", seconds * 1e-6); out += s; } - // #.# ms + // #.### ms else if(seconds < 1.f) { - sprintf_s(s, "%.4f ms", seconds * 1e-3); + sprintf_s(s, "%.3f ms", seconds * 1e-3); out += s; } + // #.### s else if(seconds < 60.f) { - sprintf_s(s, "%.4f s", seconds); + sprintf_s(s, "%.3f s", seconds); out += s; } else diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 7470e48..e74ba53 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -56,6 +56,7 @@ struct StrRange explicit StrRange(const std::string& s) : beg(s.data()), end(s.data() + s.length()) { } size_t length() const { return end - beg; } + void to_str(std::string& out) { out.assign(beg, end); } }; static inline bool StrRangeEq(const StrRange& lhs, const char* rhsSz) @@ -83,6 +84,12 @@ static inline bool StrRangeToPtr(const StrRange& s, uint64_t& out) out = (uint64_t)strtoull(s.beg, &end, 16); return end == s.end; } +static inline bool StrRangeToFloat(const StrRange& s, float& out) +{ + char* end = (char*)s.end; + out = strtof(s.beg, &end); + return end == s.end; +} //////////////////////////////////////////////////////////////////////////////// // LineSplit class @@ -301,6 +308,8 @@ private: std::unordered_map m_Pools; std::unordered_map m_Allocations; + // Copy of column [1] from previously parsed line. + std::string m_LastLineTimeStr; Statistics m_Stats; void Destroy(const Allocation& alloc); @@ -356,6 +365,8 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) if(csvSplit.GetCount() >= FIRST_PARAM_INDEX) { + csvSplit.GetRange(1).to_str(m_LastLineTimeStr); + // Update VMA current frame index. StrRange frameIndexStr = csvSplit.GetRange(2); uint32_t frameIndex; @@ -729,6 +740,14 @@ void Player::PrintStats() 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()); + + float lastTime; + if(!m_LastLineTimeStr.empty() && StrRangeToFloat(StrRange(m_LastLineTimeStr), lastTime)) + { + std::string origTimeStr; + SecondsToFriendlyStr(lastTime, origTimeStr); + printf(" Original recording time: %s\n", origTimeStr.c_str()); + } } bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound) From 5765b8ac83e1d6e2a14544f35362fe352a269b33 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 13 Aug 2018 13:51:33 +0200 Subject: [PATCH 17/51] Added statistics about thread usage. --- src/VmaReplay/VmaReplay.cpp | 44 ++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index e74ba53..e3ef4a3 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -304,10 +304,15 @@ private: VkBuffer buffer; VkImage image; }; - std::unordered_map m_Pools; std::unordered_map m_Allocations; + struct Thread + { + uint32_t callCount; + }; + std::unordered_map m_Threads; + // Copy of column [1] from previously parsed line. std::string m_LastLineTimeStr; Statistics m_Stats; @@ -365,6 +370,31 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) if(csvSplit.GetCount() >= FIRST_PARAM_INDEX) { + // Check thread ID. + uint32_t threadId; + if(StrRangeToUint(csvSplit.GetRange(0), threadId)) + { + const auto it = m_Threads.find(threadId); + if(it != m_Threads.end()) + { + ++it->second.callCount; + } + else + { + Thread threadInfo{}; + threadInfo.callCount = 1; + m_Threads[threadId] = threadInfo; + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Incorrect thread ID.\n", lineNumber); + } + } + + // Save time. csvSplit.GetRange(1).to_str(m_LastLineTimeStr); // Update VMA current frame index. @@ -748,6 +778,18 @@ void Player::PrintStats() SecondsToFriendlyStr(lastTime, origTimeStr); printf(" Original recording time: %s\n", origTimeStr.c_str()); } + + // Thread statistics. + uint32_t threadCallCountMax = 0; + uint32_t threadCallCountSum = 0; + for(const auto& it : m_Threads) + { + threadCallCountMax = std::max(threadCallCountMax, it.second.callCount); + threadCallCountSum += it.second.callCount; + } + printf(" Threads making calls to VMA: %zu\n", m_Threads.size()); + printf(" %.2f%% calls from most active thread.\n", + (float)threadCallCountMax * 100.f / (float)threadCallCountSum); } bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound) From 385b716276c660ab0e5ab875ffffd0bea1d0f338 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 13 Aug 2018 14:06:17 +0200 Subject: [PATCH 18/51] Added statistics for number of VmaAllocator nad VmaPool objects. --- src/VmaReplay/VmaReplay.cpp | 92 +++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 15 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index e3ef4a3..2208e87 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -187,16 +187,39 @@ public: size_t GetImageCreationCount() const { return m_ImageCreationCount; } size_t GetBufferCreationCount() const { return m_BufferCreationCount; } size_t GetAllocationCreationCount() const { return m_AllocationCreationCount; } + size_t GetAllocatorCreationCount() const { return m_AllocatorCreationCount; } + size_t GetAllocatorPeakCount() const { return m_AllocatorPeakCount; } + size_t GetPoolCreationCount() const { return m_PoolCreationCount; } + void RegisterCreateAllocator(); + void RegisterDestroyAllocator(); void RegisterCreateImage(); void RegisterCreateBuffer(); + void RegisterCreatePool(); private: size_t m_ImageCreationCount = 0; size_t m_BufferCreationCount = 0; size_t m_AllocationCreationCount = 0; // Also includes buffers and images. + size_t m_AllocatorCreationCount = 0; + size_t m_AllocatorCurrCount = 0; + size_t m_AllocatorPeakCount = 0; + size_t m_PoolCreationCount = 0; }; +void Statistics::RegisterCreateAllocator() +{ + ++m_AllocatorCreationCount; + ++m_AllocatorCurrCount; + m_AllocatorPeakCount = std::max(m_AllocatorPeakCount, m_AllocatorCurrCount); +} + +void Statistics::RegisterDestroyAllocator() +{ + if(m_AllocatorCurrCount > 0) + --m_AllocatorCurrCount; +} + void Statistics::RegisterCreateImage() { ++m_ImageCreationCount; @@ -209,6 +232,11 @@ void Statistics::RegisterCreateBuffer() ++m_AllocationCreationCount; } +void Statistics::RegisterCreatePool() +{ + ++m_PoolCreationCount; +} + //////////////////////////////////////////////////////////////////////////////// // class Player @@ -352,6 +380,14 @@ int Player::Init() Player::~Player() { + if(m_Stats.GetAllocatorCreationCount() > 1) + { + printf("WARNING: %zu VmaAllocator objects were created. It is recommended to use just one.\n", + m_Stats.GetAllocatorCreationCount()); + printf(" At most %zu allocators existed simultaneously.\n", + m_Stats.GetAllocatorPeakCount()); + } + if(g_Verbosity > VERBOSITY::MINIMUM) { PrintStats(); @@ -422,14 +458,14 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false)) { - // Nothing to do. + m_Stats.RegisterCreateAllocator(); } } else if(StrRangeEq(functionName, "vmaDestroyAllocator")) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false)) { - // Nothing to do. + m_Stats.RegisterDestroyAllocator(); } } else if(StrRangeEq(functionName, "vmaCreatePool")) @@ -702,7 +738,7 @@ void Player::FinalizeVulkan() if(!m_Pools.empty()) { - printf("WARNING: Pools not destroyed: %zu.\n", m_Pools.size()); + printf("WARNING: Custom pools not destroyed: %zu.\n", m_Pools.size()); for(const auto it : m_Pools) vmaDestroyPool(m_Allocator, it.second.pool); @@ -767,9 +803,22 @@ void Player::RegisterDebugCallbacks() 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()); + if(m_Stats.GetAllocationCreationCount() > 0) + { + printf(" Total allocations created: %zu\n", m_Stats.GetAllocationCreationCount()); + } + if(m_Stats.GetBufferCreationCount() > 0) + { + printf(" Total buffers created: %zu\n", m_Stats.GetBufferCreationCount()); + } + if(m_Stats.GetImageCreationCount() > 0) + { + printf(" Total images created: %zu\n", m_Stats.GetImageCreationCount()); + } + if(m_Stats.GetPoolCreationCount() > 0) + { + printf(" Total custom pools created: %zu\n", m_Stats.GetPoolCreationCount()); + } float lastTime; if(!m_LastLineTimeStr.empty() && StrRangeToFloat(StrRange(m_LastLineTimeStr), lastTime)) @@ -780,16 +829,24 @@ void Player::PrintStats() } // Thread statistics. - uint32_t threadCallCountMax = 0; - uint32_t threadCallCountSum = 0; - for(const auto& it : m_Threads) + const size_t threadCount = m_Threads.size(); + if(threadCount > 1) { - threadCallCountMax = std::max(threadCallCountMax, it.second.callCount); - threadCallCountSum += it.second.callCount; + uint32_t threadCallCountMax = 0; + uint32_t threadCallCountSum = 0; + for(const auto& it : m_Threads) + { + threadCallCountMax = std::max(threadCallCountMax, it.second.callCount); + threadCallCountSum += it.second.callCount; + } + printf(" Threads making calls to VMA: %zu\n", threadCount); + printf(" %.2f%% calls from most active thread.\n", + (float)threadCallCountMax * 100.f / (float)threadCallCountSum); + } + else + { + printf(" VMA used from only one thread.\n"); } - printf(" Threads making calls to VMA: %zu\n", m_Threads.size()); - printf(" %.2f%% calls from most active thread.\n", - (float)threadCallCountMax * 100.f / (float)threadCallCountSum); } bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound) @@ -826,6 +883,8 @@ void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit) StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), poolCreateInfo.frameInUseCount) && StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), origPtr)) { + m_Stats.RegisterCreatePool(); + Pool poolDesc = {}; VkResult res = vmaCreatePool(m_Allocator, &poolCreateInfo, &poolDesc.pool); if(res != VK_SUCCESS) @@ -1098,7 +1157,7 @@ static void PrintCommandLineSyntax() static int ProcessFile(const char* data, size_t numBytes) { // Begin stats. - if(g_Verbosity > VERBOSITY::MINIMUM) + if(g_Verbosity == VERBOSITY::MAXIMUM) { printf("File size: %zu B\n", numBytes); } @@ -1146,6 +1205,9 @@ static int ProcessFile(const char* data, size_t numBytes) printf("Done.\n"); printf("Playback took: %s\n", playDurationStr.c_str()); + } + if(g_Verbosity == VERBOSITY::MAXIMUM) + { printf("File lines: %zu\n", lineSplit.GetNextLineIndex()); } } From ab8550340cd81439f463d4f478ffb03342e7e9b8 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 13 Aug 2018 15:15:36 +0200 Subject: [PATCH 19/51] Added more detailed statistics about created buffers and images based on human-friendly classification (see http://asawicki.info/news_1682_human-friendly_classification_of_vulkan_resources.html) --- src/VmaReplay/VmaReplay.cpp | 151 +++++++++++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 19 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 2208e87..0da31e6 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -182,10 +182,14 @@ void CsvSplit::Set(const StrRange& line, size_t maxCount) class Statistics { public: + static uint32_t BufferUsageToClass(uint32_t usage); + static uint32_t ImageUsageToClass(uint32_t usage); + Statistics() { } - size_t GetImageCreationCount() const { return m_ImageCreationCount; } - size_t GetBufferCreationCount() const { return m_BufferCreationCount; } + size_t GetImageCreationCount(uint32_t imgClass) const { return m_ImageCreationCount[imgClass]; } + size_t GetLinearImageCreationCount() const { return m_LinearImageCreationCount; } + size_t GetBufferCreationCount(uint32_t bufClass) const { return m_BufferCreationCount[bufClass]; } size_t GetAllocationCreationCount() const { return m_AllocationCreationCount; } size_t GetAllocatorCreationCount() const { return m_AllocatorCreationCount; } size_t GetAllocatorPeakCount() const { return m_AllocatorPeakCount; } @@ -193,13 +197,14 @@ public: void RegisterCreateAllocator(); void RegisterDestroyAllocator(); - void RegisterCreateImage(); - void RegisterCreateBuffer(); + void RegisterCreateImage(uint32_t usage, uint32_t tiling); + void RegisterCreateBuffer(uint32_t usage); void RegisterCreatePool(); private: - size_t m_ImageCreationCount = 0; - size_t m_BufferCreationCount = 0; + size_t m_ImageCreationCount[4] = { }; + size_t m_LinearImageCreationCount = 0; + size_t m_BufferCreationCount[4] = { }; size_t m_AllocationCreationCount = 0; // Also includes buffers and images. size_t m_AllocatorCreationCount = 0; size_t m_AllocatorCurrCount = 0; @@ -207,6 +212,69 @@ private: size_t m_PoolCreationCount = 0; }; +uint32_t Statistics::BufferUsageToClass(uint32_t usage) +{ + // Buffer is used as source of data for fixed-function stage of graphics pipeline. + // It's indirect, vertex, or index buffer. + if ((usage & (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_INDEX_BUFFER_BIT)) != 0) + { + return 0; + } + // Buffer is accessed by shaders for load/store/atomic. + // Aka "UAV" + else if ((usage & (VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) != 0) + { + return 1; + } + // Buffer is accessed by shaders for reading uniform data. + // Aka "constant buffer" + else if ((usage & (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | + VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT)) != 0) + { + return 2; + } + // Any other type of buffer. + // Notice that VK_BUFFER_USAGE_TRANSFER_SRC_BIT and VK_BUFFER_USAGE_TRANSFER_DST_BIT + // flags are intentionally ignored. + else + { + return 3; + } +} + +uint32_t Statistics::ImageUsageToClass(uint32_t usage) +{ + // Image is used as depth/stencil "texture/surface". + if ((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) + { + return 0; + } + // Image is used as other type of attachment. + // Aka "render target" + else if ((usage & (VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | + VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) != 0) + { + return 1; + } + // Image is accessed by shaders for sampling. + // Aka "texture" + else if ((usage & VK_IMAGE_USAGE_SAMPLED_BIT) != 0) + { + return 2; + } + // Any other type of image. + // Notice that VK_IMAGE_USAGE_TRANSFER_SRC_BIT and VK_IMAGE_USAGE_TRANSFER_DST_BIT + // flags are intentionally ignored. + else + { + return 3; + } +} + void Statistics::RegisterCreateAllocator() { ++m_AllocatorCreationCount; @@ -220,15 +288,24 @@ void Statistics::RegisterDestroyAllocator() --m_AllocatorCurrCount; } -void Statistics::RegisterCreateImage() +void Statistics::RegisterCreateImage(uint32_t usage, uint32_t tiling) { - ++m_ImageCreationCount; + if(tiling == VK_IMAGE_TILING_LINEAR) + ++m_LinearImageCreationCount; + else + { + const uint32_t imgClass = ImageUsageToClass(usage); + ++m_ImageCreationCount[imgClass]; + } + ++m_AllocationCreationCount; } -void Statistics::RegisterCreateBuffer() +void Statistics::RegisterCreateBuffer(uint32_t usage) { - ++m_BufferCreationCount; + const uint32_t bufClass = BufferUsageToClass(usage); + ++m_BufferCreationCount[bufClass]; + ++m_AllocationCreationCount; } @@ -807,14 +884,48 @@ void Player::PrintStats() { printf(" Total allocations created: %zu\n", m_Stats.GetAllocationCreationCount()); } - if(m_Stats.GetBufferCreationCount() > 0) + + // Buffers + const size_t bufferCreationCount = + m_Stats.GetBufferCreationCount(0) + + m_Stats.GetBufferCreationCount(1) + + m_Stats.GetBufferCreationCount(2) + + m_Stats.GetBufferCreationCount(3); + if(bufferCreationCount > 0) { - printf(" Total buffers created: %zu\n", m_Stats.GetBufferCreationCount()); + printf(" Total buffers created: %zu\n", bufferCreationCount); + if(g_Verbosity == VERBOSITY::MAXIMUM) + { + printf(" Class 0 (indirect/vertex/index): %zu\n", m_Stats.GetBufferCreationCount(0)); + printf(" Class 1 (storage): %zu\n", m_Stats.GetBufferCreationCount(1)); + printf(" Class 2 (uniform): %zu\n", m_Stats.GetBufferCreationCount(2)); + printf(" Class 3 (other): %zu\n", m_Stats.GetBufferCreationCount(3)); + } } - if(m_Stats.GetImageCreationCount() > 0) + + // Images + const size_t imageCreationCount = + m_Stats.GetImageCreationCount(0) + + m_Stats.GetImageCreationCount(1) + + m_Stats.GetImageCreationCount(2) + + m_Stats.GetImageCreationCount(3) + + m_Stats.GetLinearImageCreationCount(); + if(imageCreationCount > 0) { - printf(" Total images created: %zu\n", m_Stats.GetImageCreationCount()); + printf(" Total images created: %zu\n", imageCreationCount); + if(g_Verbosity == VERBOSITY::MAXIMUM) + { + printf(" Class 0 (depth/stencil): %zu\n", m_Stats.GetImageCreationCount(0)); + printf(" Class 1 (attachment): %zu\n", m_Stats.GetImageCreationCount(1)); + printf(" Class 2 (sampled): %zu\n", m_Stats.GetImageCreationCount(2)); + printf(" Class 3 (other): %zu\n", m_Stats.GetImageCreationCount(3)); + if(m_Stats.GetLinearImageCreationCount() > 0) + { + printf(" LINEAR tiling: %zu\n", m_Stats.GetLinearImageCreationCount()); + } + } } + if(m_Stats.GetPoolCreationCount() > 0) { printf(" Total custom pools created: %zu\n", m_Stats.GetPoolCreationCount()); @@ -883,11 +994,13 @@ void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit) StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), poolCreateInfo.frameInUseCount) && StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), origPtr)) { - m_Stats.RegisterCreatePool(); - Pool poolDesc = {}; VkResult res = vmaCreatePool(m_Allocator, &poolCreateInfo, &poolDesc.pool); - if(res != VK_SUCCESS) + if(res == VK_SUCCESS) + { + m_Stats.RegisterCreatePool(); + } + else { if(IssueWarning()) { @@ -990,7 +1103,7 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr); if(res == VK_SUCCESS) { - m_Stats.RegisterCreateBuffer(); + m_Stats.RegisterCreateBuffer(bufCreateInfo.usage); } else { @@ -1106,7 +1219,7 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &allocDesc.image, &allocDesc.allocation, nullptr); if(res == VK_SUCCESS) { - m_Stats.RegisterCreateImage(); + m_Stats.RegisterCreateImage(imageCreateInfo.usage, imageCreateInfo.tiling); } else { From 87c818900253ee6846e06bdfd1daccfec3dc9b03 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 10:52:54 +0200 Subject: [PATCH 20/51] Foo --- src/VmaReplay/VmaReplay.cpp | 91 ++++++++++++++++++++++++++++++++++++- src/vk_mem_alloc.h | 17 +++++-- 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 0da31e6..8433610 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -44,6 +44,15 @@ static enum class VERBOSITY } g_Verbosity = VERBOSITY::DEFAULT; static std::string g_FilePath; +// Most significant 16 bits are major version, least significant 16 bits are minor version. +static uint32_t g_FileVersion; + +static bool ValidateFileVersion() +{ + const uint32_t major = g_FileVersion >> 16; + const uint32_t minor = g_FileVersion & 0xFFFF; + return major == 1 && minor <= 2; +} struct StrRange { @@ -176,6 +185,24 @@ void CsvSplit::Set(const StrRange& line, size_t maxCount) m_Count = rangeIndex; } +static bool ParseFileVersion(const StrRange& s) +{ + CsvSplit csvSplit; + csvSplit.Set(s, 2); + uint32_t major, minor; + if(csvSplit.GetCount() == 2 && + StrRangeToUint(csvSplit.GetRange(0), major) && + StrRangeToUint(csvSplit.GetRange(1), minor)) + { + g_FileVersion = (major << 16) | minor; + return true; + } + else + { + return false; + } +} + //////////////////////////////////////////////////////////////////////////////// // class Statistics @@ -442,6 +469,7 @@ private: 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 ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit); void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit); }; @@ -566,6 +594,8 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) ExecuteDestroyImage(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaFreeMemory")) ExecuteFreeMemory(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaCreateLostAllocation")) + ExecuteCreateLostAllocation(lineNumber, csvSplit); else { if(IssueWarning()) @@ -1250,6 +1280,64 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) } } +void Player::ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false)) + { + uint64_t origPtr = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr)) + { + // TODO + 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(bufCreateInfo.usage); + } + 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 vmaCreateLostAllocation.\n", lineNumber); + } + } + } +} + //////////////////////////////////////////////////////////////////////////////// // Main functions @@ -1285,8 +1373,7 @@ static int ProcessFile(const char* data, size_t numBytes) return RESULT_ERROR_FORMAT; } - if(!lineSplit.GetNextLine(line) || - !(StrRangeEq(line, "1,0") || StrRangeEq(line, "1,1"))) + if(!lineSplit.GetNextLine(line) || !ParseFileVersion(line) || !ValidateFileVersion()) { printf("ERROR: Incorrect file format version.\n"); return RESULT_ERROR_FORMAT; diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 6250dd6..95aa1c8 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -2666,7 +2666,7 @@ void EnsureFile() { fopen_s(&g_File, "VMA_Usage_Dump", "wb"); fprintf(g_File, "%s\n", "Vulkan Memory Allocator,Calls recording"); - fprintf(g_File, "%s\n", "1,1"); + fprintf(g_File, "%s\n", "1,2"); QueryPerformanceFrequency(&g_Freq); QueryPerformanceCounter(&g_StartCounter); } @@ -10003,12 +10003,23 @@ void vmaCreateLostAllocation( VmaAllocator allocator, VmaAllocation* pAllocation) { - Crash(); VMA_ASSERT(allocator && pAllocation); VMA_DEBUG_GLOBAL_MUTEX_LOCK; allocator->CreateLostAllocation(pAllocation); + + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", threadId, time, frameIndex, + (*pAllocation)); + fflush(g_File); + } } VkResult vmaMapMemory( @@ -10089,7 +10100,6 @@ VkResult vmaBindBufferMemory( VmaAllocation allocation, VkBuffer buffer) { - Crash(); VMA_ASSERT(allocator && allocation && buffer); VMA_DEBUG_LOG("vmaBindBufferMemory"); @@ -10104,7 +10114,6 @@ VkResult vmaBindImageMemory( VmaAllocation allocation, VkImage image) { - Crash(); VMA_ASSERT(allocator && allocation && image); VMA_DEBUG_LOG("vmaBindImageMemory"); From 94e82065985cefd5f8791af50fcdc89b55afc0ac Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 11:12:51 +0200 Subject: [PATCH 21/51] Added new validation layer message to ignores, and documented it. "Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used." Issue #34 thanks @victorbush ! --- src/VulkanSample.cpp | 12 ++++++++++++ src/vk_mem_alloc.h | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/VulkanSample.cpp b/src/VulkanSample.cpp index 91f2bf2..d33861e 100644 --- a/src/VulkanSample.cpp +++ b/src/VulkanSample.cpp @@ -190,6 +190,18 @@ VKAPI_ATTR VkBool32 VKAPI_CALL MyDebugReportCallback( { return VK_FALSE; } + + /* + "Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used." + Ignoring because we map entire VkDeviceMemory blocks, where different types of + images and buffers may end up together, especially on GPUs with unified memory + like Intel. + */ + if(strstr(pMessage, "Mapping an image with layout") != nullptr && + strstr(pMessage, "can result in undefined behavior if this memory is used by the device") != nullptr) + { + return VK_FALSE; + } switch(flags) { diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 149445e..7e24916 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -323,6 +323,16 @@ memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); vmaUnmapMemory(allocator, constantBufferAllocation); \endcode +When mapping, you may see a warning from Vulkan validation layer similar to this one: + +Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used. + +It happens because the library maps entire `VkDeviceMemory` block, where different +types of images and buffers may end up together, especially on GPUs with unified memory like Intel. +You can safely ignore it if you are sure you access only memory of the intended +object that you wanted to map. + + \section memory_mapping_persistently_mapped_memory Persistently mapped memory Kepping your memory persistently mapped is generally OK in Vulkan. @@ -1150,6 +1160,23 @@ To learn more about this extension, see: threads at the same time if you pass the same #VmaAllocation object to these functions. +\section general_considerations_validation_layer_warnings Validation layer warnings + +When using this library, you can meet following types of warnings issued by +Vulkan validation layer. They don't necessarily indicate a bug, so you may need +to just ignore them. + +- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.* + - It happens when VK_KHR_dedicated_allocation extension is enabled. + `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it. +- *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.* + - It happens when you map a buffer or image, because the library maps entire + `VkDeviceMemory` block, where different types of images and buffers may end + up together, especially on GPUs with unified memory like Intel. +- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.* + - It happens when you use lost allocations, and a new image or buffer is + created in place of an existing object that bacame lost. + \section general_considerations_allocation_algorithm Allocation algorithm The library uses following algorithm for allocation, in order: From 404d12e18ef54ef1b3a6b998190cf43df7ec779f Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 13:33:34 +0200 Subject: [PATCH 22/51] Format version 1,2. Added support for vmaAllocateMemory, vmaAllocateMemoryForBuffer, vmaAllocateMemoryForImage, vmaCreateLostAllocation. --- src/VmaReplay/VmaReplay.cpp | 193 ++++++++++++++++++++++++++++++++++-- src/vk_mem_alloc.h | 131 ++++++++++++++++++------ 2 files changed, 285 insertions(+), 39 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 8433610..8eeb51b 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -43,6 +43,8 @@ static enum class VERBOSITY COUNT, } g_Verbosity = VERBOSITY::DEFAULT; +enum class OBJECT_TYPE { BUFFER, IMAGE }; + static std::string g_FilePath; // Most significant 16 bits are major version, least significant 16 bits are minor version. static uint32_t g_FileVersion; @@ -99,6 +101,30 @@ static inline bool StrRangeToFloat(const StrRange& s, float& out) out = strtof(s.beg, &end); return end == s.end; } +static inline bool StrRangeToBool(const StrRange& s, bool& out) +{ + if(s.end - s.beg == 1) + { + if(*s.beg == '1') + { + out = true; + } + else if(*s.beg == '0') + { + out = false; + } + else + { + return false; + } + } + else + { + return false; + } + + return true; +} //////////////////////////////////////////////////////////////////////////////// // LineSplit class @@ -227,12 +253,13 @@ public: void RegisterCreateImage(uint32_t usage, uint32_t tiling); void RegisterCreateBuffer(uint32_t usage); void RegisterCreatePool(); + void RegisterCreateAllocation(); private: size_t m_ImageCreationCount[4] = { }; size_t m_LinearImageCreationCount = 0; size_t m_BufferCreationCount[4] = { }; - size_t m_AllocationCreationCount = 0; // Also includes buffers and images. + size_t m_AllocationCreationCount = 0; // Also includes buffers and images, and lost allocations. size_t m_AllocatorCreationCount = 0; size_t m_AllocatorCurrCount = 0; size_t m_AllocatorPeakCount = 0; @@ -341,6 +368,11 @@ void Statistics::RegisterCreatePool() ++m_PoolCreationCount; } +void Statistics::RegisterCreateAllocation() +{ + ++m_AllocationCreationCount; +} + //////////////////////////////////////////////////////////////////////////////// // class Player @@ -380,6 +412,18 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL MyDebugReportCallback( return VK_FALSE; } + /* + "Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used." + Ignoring because we map entire VkDeviceMemory blocks, where different types of + images and buffers may end up together, especially on GPUs with unified memory + like Intel. + */ + if(strstr(pMessage, "Mapping an image with layout") != nullptr && + strstr(pMessage, "can result in undefined behavior if this memory is used by the device") != nullptr) + { + return VK_FALSE; + } + printf("%s \xBA %s\n", pLayerPrefix, pMessage); return VK_FALSE; @@ -411,6 +455,7 @@ private: static const size_t MAX_WARNINGS_TO_SHOW = 64; size_t m_WarningCount = 0; + bool m_AllocateForBufferImageWarningIssued = false; VkInstance m_VulkanInstance = VK_NULL_HANDLE; VkPhysicalDevice m_PhysicalDevice = VK_NULL_HANDLE; @@ -470,6 +515,8 @@ private: void ExecuteDestroyImage(size_t lineNumber, const CsvSplit& csvSplit) { DestroyAllocation(lineNumber, csvSplit); } void ExecuteFreeMemory(size_t lineNumber, const CsvSplit& csvSplit) { DestroyAllocation(lineNumber, csvSplit); } void ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit); + void ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit); + void ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvSplit& csvSplit, OBJECT_TYPE objType); void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit); }; @@ -596,6 +643,12 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) ExecuteFreeMemory(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaCreateLostAllocation")) ExecuteCreateLostAllocation(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaAllocateMemory")) + ExecuteAllocateMemory(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaAllocateMemoryForBuffer")) + ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::BUFFER); + else if(StrRangeEq(functionName, "vmaAllocateMemoryForImage")) + ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::IMAGE); else { if(IssueWarning()) @@ -1288,7 +1341,51 @@ void Player::ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvS if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr)) { - // TODO + Allocation allocDesc = {}; + vmaCreateLostAllocation(m_Allocator, &allocDesc.allocation); + m_Stats.RegisterCreateAllocation(); + + 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 vmaCreateLostAllocation.\n", lineNumber); + } + } + } +} + +void Player::ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 11, true)) + { + VkMemoryRequirements memReq = {}; + VmaAllocationCreateInfo allocCreateInfo = {}; + uint64_t origPool = 0; + uint64_t origPtr = 0; + + if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), memReq.size) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), memReq.alignment) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), memReq.memoryTypeBits) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), allocCreateInfo.flags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), (uint32_t&)allocCreateInfo.usage) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), allocCreateInfo.requiredFlags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), allocCreateInfo.preferredFlags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.memoryTypeBits) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), origPool) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), origPtr)) + { if(origPool != 0) { const auto poolIt = m_Pools.find(origPool); @@ -1304,16 +1401,16 @@ void Player::ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvS } Allocation allocDesc = {}; - VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr); + VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr); if(res == VK_SUCCESS) { - m_Stats.RegisterCreateBuffer(bufCreateInfo.usage); + m_Stats.RegisterCreateAllocation(); } else { if(IssueWarning()) { - printf("Line %zu: vmaCreateBuffer failed (%u).\n", lineNumber, res); + printf("Line %zu: vmaAllocateMemory failed (%u).\n", lineNumber, res); } } @@ -1332,7 +1429,91 @@ void Player::ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvS { if(IssueWarning()) { - printf("Line %zu: Invalid parameters for vmaCreateLostAllocation.\n", lineNumber); + printf("Line %zu: Invalid parameters for vmaAllocateMemory.\n", lineNumber); + } + } + } +} + +void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvSplit& csvSplit, OBJECT_TYPE objType) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 13, true)) + { + VkMemoryRequirements memReq = {}; + VmaAllocationCreateInfo allocCreateInfo = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + uint64_t origPool = 0; + uint64_t origPtr = 0; + + if(StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX), memReq.size) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), memReq.alignment) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), memReq.memoryTypeBits) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 3), allocCreateInfo.flags) && + StrRangeToBool(csvSplit.GetRange(FIRST_PARAM_INDEX + 4), requiresDedicatedAllocation) && + StrRangeToBool(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), prefersDedicatedAllocation) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), (uint32_t&)allocCreateInfo.usage) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 7), allocCreateInfo.requiredFlags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), allocCreateInfo.preferredFlags) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), allocCreateInfo.memoryTypeBits) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), origPool) && + StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 11), 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); + } + } + } + + if(requiresDedicatedAllocation || prefersDedicatedAllocation) + { + allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + if(!m_AllocateForBufferImageWarningIssued) + { + if(IssueWarning()) + { + printf("Line %zu: vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage cannot be replayed accurately. Using vmaCreateAllocation instead.\n", lineNumber); + } + m_AllocateForBufferImageWarningIssued = true; + } + + Allocation allocDesc = {}; + VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr); + if(res == VK_SUCCESS) + { + m_Stats.RegisterCreateAllocation(); + } + else + { + printf("Line %zu: vmaAllocateMemory (called as vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage) 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 vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage.\n", lineNumber); } } } diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index b8f4135..b3223d2 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -9353,32 +9353,6 @@ void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) #endif // #if VMA_STATS_STRING_ENABLED -static VkResult AllocateMemoryForImage( - VmaAllocator allocator, - VkImage image, - const VmaAllocationCreateInfo* pAllocationCreateInfo, - VmaSuballocationType suballocType, - VmaAllocation* pAllocation) -{ - VMA_ASSERT(allocator && (image != VK_NULL_HANDLE) && pAllocationCreateInfo && pAllocation); - - VkMemoryRequirements vkMemReq = {}; - bool requiresDedicatedAllocation = false; - bool prefersDedicatedAllocation = false; - allocator->GetImageMemoryRequirements(image, vkMemReq, - requiresDedicatedAllocation, prefersDedicatedAllocation); - - return allocator->AllocateMemory( - vkMemReq, - requiresDedicatedAllocation, - prefersDedicatedAllocation, - VK_NULL_HANDLE, // dedicatedBuffer - image, // dedicatedImage - *pAllocationCreateInfo, - suballocType, - pAllocation); -} - //////////////////////////////////////////////////////////////////////////////// // Public interface @@ -9857,7 +9831,6 @@ VkResult vmaAllocateMemory( VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { - Crash(); VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation); VMA_DEBUG_LOG("vmaAllocateMemory"); @@ -9874,6 +9847,28 @@ VkResult vmaAllocateMemory( VMA_SUBALLOCATION_TYPE_UNKNOWN, pAllocation); + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, + pVkMemoryRequirements->size, + pVkMemoryRequirements->alignment, + pVkMemoryRequirements->memoryTypeBits, + pCreateInfo->flags, + pCreateInfo->usage, + pCreateInfo->requiredFlags, + pCreateInfo->preferredFlags, + pCreateInfo->memoryTypeBits, + pCreateInfo->pool, + *pAllocation, + pCreateInfo->pUserData ? (const char*)pCreateInfo->pUserData : ""); + fflush(g_File); + } + if(pAllocationInfo && result == VK_SUCCESS) { allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); @@ -9889,7 +9884,6 @@ VkResult vmaAllocateMemoryForBuffer( VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { - Crash(); VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation); VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer"); @@ -9913,6 +9907,30 @@ VkResult vmaAllocateMemoryForBuffer( VMA_SUBALLOCATION_TYPE_BUFFER, pAllocation); + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, + vkMemReq.size, + vkMemReq.alignment, + vkMemReq.memoryTypeBits, + requiresDedicatedAllocation ? 1 : 0, + prefersDedicatedAllocation ? 1 : 0, + pCreateInfo->flags, + pCreateInfo->usage, + pCreateInfo->requiredFlags, + pCreateInfo->preferredFlags, + pCreateInfo->memoryTypeBits, + pCreateInfo->pool, + *pAllocation, + pCreateInfo->pUserData ? (const char*)pCreateInfo->pUserData : ""); + fflush(g_File); + } + if(pAllocationInfo && result == VK_SUCCESS) { allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); @@ -9935,13 +9953,46 @@ VkResult vmaAllocateMemoryForImage( VMA_DEBUG_GLOBAL_MUTEX_LOCK - VkResult result = AllocateMemoryForImage( - allocator, - image, - pCreateInfo, + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(image, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + image, // dedicatedImage + *pCreateInfo, VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, pAllocation); + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, + vkMemReq.size, + vkMemReq.alignment, + vkMemReq.memoryTypeBits, + requiresDedicatedAllocation ? 1 : 0, + prefersDedicatedAllocation ? 1 : 0, + pCreateInfo->flags, + pCreateInfo->usage, + pCreateInfo->requiredFlags, + pCreateInfo->preferredFlags, + pCreateInfo->memoryTypeBits, + pCreateInfo->pool, + *pAllocation, + pCreateInfo->pUserData ? (const char*)pCreateInfo->pUserData : ""); + fflush(g_File); + } + if(pAllocationInfo && result == VK_SUCCESS) { allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); @@ -10329,7 +10380,21 @@ VkResult vmaCreateImage( VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR; // 2. Allocate memory using allocator. - res = AllocateMemoryForImage(allocator, *pImage, pAllocationCreateInfo, suballocType, pAllocation); + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(*pImage, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + *pImage, // dedicatedImage + *pAllocationCreateInfo, + suballocType, + pAllocation); if(res >= 0) { // 3. Bind image with memory. From 29785d1169e88bccd85dd4e058d86dd77d084ba5 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 13:42:13 +0200 Subject: [PATCH 23/51] VmaReplay.cpp, Player class: Small refactoring. --- src/VmaReplay/VmaReplay.cpp | 158 ++++++++++++------------------------ 1 file changed, 54 insertions(+), 104 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 8eeb51b..0bf3396 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -496,6 +496,13 @@ private: void Destroy(const Allocation& alloc); + // Finds VmaPool bu original pointer. + // If origPool = null, returns true and outPool = null. + // If failed, prints warning, returns false and outPool = null. + bool FindPool(size_t lineNumber, uint64_t origPool, VmaPool& outPool); + // If allocation with that origPtr already exists, prints warning and replaces it. + void AddAllocation(size_t lineNumber, uint64_t origPtr, Allocation&& allocDesc); + // Increments warning counter. Returns true if warning message should be printed. bool IssueWarning(); @@ -681,6 +688,44 @@ void Player::Destroy(const Allocation& alloc) vmaFreeMemory(m_Allocator, alloc.allocation); } +bool Player::FindPool(size_t lineNumber, uint64_t origPool, VmaPool& outPool) +{ + outPool = VK_NULL_HANDLE; + + if(origPool != 0) + { + const auto poolIt = m_Pools.find(origPool); + if(poolIt != m_Pools.end()) + { + outPool = poolIt->second.pool; + return true; + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Pool %llX not found.\n", lineNumber, origPool); + } + } + } + + return true; +} + +void Player::AddAllocation(size_t lineNumber, uint64_t origPtr, Allocation&& allocDesc) +{ + 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] = std::move(allocDesc); +} + bool Player::IssueWarning() { if(g_Verbosity < VERBOSITY::MAXIMUM) @@ -1168,19 +1213,7 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) 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); - } - } - } + FindPool(lineNumber, origPool, allocCreateInfo.pool); Allocation allocDesc = {}; VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr); @@ -1196,16 +1229,7 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) } } - 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; + AddAllocation(lineNumber, origPtr, std::move(allocDesc)); } else { @@ -1282,21 +1306,7 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) 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); - } - } - } + FindPool(lineNumber, origPool, allocCreateInfo.pool); Allocation allocDesc = {}; VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &allocDesc.image, &allocDesc.allocation, nullptr); @@ -1312,16 +1322,7 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) } } - 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; + AddAllocation(lineNumber, origPtr, std::move(allocDesc)); } else { @@ -1345,16 +1346,7 @@ void Player::ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvS vmaCreateLostAllocation(m_Allocator, &allocDesc.allocation); m_Stats.RegisterCreateAllocation(); - 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; + AddAllocation(lineNumber, origPtr, std::move(allocDesc)); } else { @@ -1386,19 +1378,7 @@ void Player::ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit) StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 8), origPool) && StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 9), 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); - } - } - } + FindPool(lineNumber, origPool, allocCreateInfo.pool); Allocation allocDesc = {}; VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr); @@ -1414,16 +1394,7 @@ void Player::ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit) } } - 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; + AddAllocation(lineNumber, origPtr, std::move(allocDesc)); } else { @@ -1459,19 +1430,7 @@ void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvS StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 10), origPool) && StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 11), 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); - } - } - } + FindPool(lineNumber, origPool, allocCreateInfo.pool); if(requiresDedicatedAllocation || prefersDedicatedAllocation) { @@ -1498,16 +1457,7 @@ void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvS printf("Line %zu: vmaAllocateMemory (called as vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage) 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; + AddAllocation(lineNumber, origPtr, std::move(allocDesc)); } else { From 5557b0ed63354931222e5de48494c9b3fb5e9e6d Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 13:54:54 +0200 Subject: [PATCH 24/51] Added support for vmaMapMemory, vmaUnmapMemory, vmaFlushAllocation, vmaInvalidateAllocation. --- src/VmaReplay/VmaReplay.cpp | 160 ++++++++++++++++++++++++++++++++++++ src/vk_mem_alloc.h | 52 ++++++++++++ 2 files changed, 212 insertions(+) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 0bf3396..7cf827a 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -524,6 +524,10 @@ private: void ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvSplit); void ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit); void ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvSplit& csvSplit, OBJECT_TYPE objType); + void ExecuteMapMemory(size_t lineNumber, const CsvSplit& csvSplit); + void ExecuteUnmapMemory(size_t lineNumber, const CsvSplit& csvSplit); + void ExecuteFlushAllocation(size_t lineNumber, const CsvSplit& csvSplit); + void ExecuteInvalidateAllocation(size_t lineNumber, const CsvSplit& csvSplit); void DestroyAllocation(size_t lineNumber, const CsvSplit& csvSplit); }; @@ -656,6 +660,14 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::BUFFER); else if(StrRangeEq(functionName, "vmaAllocateMemoryForImage")) ExecuteAllocateMemoryForBufferOrImage(lineNumber, csvSplit, OBJECT_TYPE::IMAGE); + else if(StrRangeEq(functionName, "vmaMapMemory")) + ExecuteMapMemory(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaUnmapMemory")) + ExecuteUnmapMemory(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaFlushAllocation")) + ExecuteFlushAllocation(lineNumber, csvSplit); + else if(StrRangeEq(functionName, "vmaInvalidateAllocation")) + ExecuteInvalidateAllocation(lineNumber, csvSplit); else { if(IssueWarning()) @@ -1469,6 +1481,154 @@ void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvS } } +void Player::ExecuteMapMemory(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false)) + { + uint64_t origPtr = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr)) + { + if(origPtr != 0) + { + const auto it = m_Allocations.find(origPtr); + if(it != m_Allocations.end()) + { + void* pData; + VkResult res = vmaMapMemory(m_Allocator, it->second.allocation, &pData); + if(res != VK_SUCCESS) + { + printf("Line %zu: vmaMapMemory failed (%u)\n", lineNumber, res); + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr); + } + } + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Invalid parameters for vmaMapMemory.\n", lineNumber); + } + } + } +} + +void Player::ExecuteUnmapMemory(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 1, false)) + { + uint64_t origPtr = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr)) + { + if(origPtr != 0) + { + const auto it = m_Allocations.find(origPtr); + if(it != m_Allocations.end()) + { + vmaUnmapMemory(m_Allocator, it->second.allocation); + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr); + } + } + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Invalid parameters for vmaMapMemory.\n", lineNumber); + } + } + } +} + +void Player::ExecuteFlushAllocation(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 3, false)) + { + uint64_t origPtr = 0; + uint64_t offset = 0; + uint64_t size = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), offset) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), size)) + { + if(origPtr != 0) + { + const auto it = m_Allocations.find(origPtr); + if(it != m_Allocations.end()) + { + vmaFlushAllocation(m_Allocator, it->second.allocation, offset, size); + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr); + } + } + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Invalid parameters for vmaFlushAllocation.\n", lineNumber); + } + } + } +} + +void Player::ExecuteInvalidateAllocation(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 3, false)) + { + uint64_t origPtr = 0; + uint64_t offset = 0; + uint64_t size = 0; + + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 1), offset) && + StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 2), size)) + { + if(origPtr != 0) + { + const auto it = m_Allocations.find(origPtr); + if(it != m_Allocations.end()) + { + vmaInvalidateAllocation(m_Allocator, it->second.allocation, offset, size); + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr); + } + } + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Invalid parameters for vmaInvalidateAllocation.\n", lineNumber); + } + } + } +} //////////////////////////////////////////////////////////////////////////////// // Main functions diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index b3223d2..1692147 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -10107,6 +10107,18 @@ VkResult vmaMapMemory( { VMA_ASSERT(allocator && allocation && ppData); + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaMapMemory,%p\n", threadId, time, frameIndex, + allocation); + fflush(g_File); + } + VMA_DEBUG_GLOBAL_MUTEX_LOCK return allocator->Map(allocation, ppData); @@ -10118,6 +10130,18 @@ void vmaUnmapMemory( { VMA_ASSERT(allocator && allocation); + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", threadId, time, frameIndex, + allocation); + fflush(g_File); + } + VMA_DEBUG_GLOBAL_MUTEX_LOCK allocator->Unmap(allocation); @@ -10129,6 +10153,20 @@ void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDevi VMA_DEBUG_LOG("vmaFlushAllocation"); + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", threadId, time, frameIndex, + allocation, + offset, + size); + fflush(g_File); + } + VMA_DEBUG_GLOBAL_MUTEX_LOCK allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH); @@ -10140,6 +10178,20 @@ void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, V VMA_DEBUG_LOG("vmaInvalidateAllocation"); + { + VmaMutexLock lock(g_FileMutex, true); + EnsureFile(); + LARGE_INTEGER counter; QueryPerformanceCounter(&counter); + const DWORD threadId = GetCurrentThreadId(); + const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; + const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); + fprintf(g_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", threadId, time, frameIndex, + allocation, + offset, + size); + fflush(g_File); + } + VMA_DEBUG_GLOBAL_MUTEX_LOCK allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE); From daec26e5eafe06102d30040985b4f47e3f5747fa Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 14:03:46 +0200 Subject: [PATCH 25/51] Minor fixes. VkResult is printed as signed int. --- src/VmaReplay/VmaReplay.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index 7cf827a..cf06fd4 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -806,7 +806,7 @@ int Player::InitVulkan() res = vkCreateInstance(&instInfo, NULL, &m_VulkanInstance); if(res != VK_SUCCESS) { - printf("ERROR: vkCreateInstance failed (%u)\n", res); + printf("ERROR: vkCreateInstance failed (%d)\n", res); return RESULT_ERROR_VULKAN; } @@ -914,7 +914,7 @@ int Player::InitVulkan() res = vkCreateDevice(m_PhysicalDevice, &deviceCreateInfo, nullptr, &m_Device); if(res != VK_SUCCESS) { - printf("ERROR: vkCreateDevice failed (%u)\n", res); + printf("ERROR: vkCreateDevice failed (%d)\n", res); return RESULT_ERROR_VULKAN; } @@ -932,7 +932,7 @@ int Player::InitVulkan() res = vmaCreateAllocator(&allocatorInfo, &m_Allocator); if(res != VK_SUCCESS) { - printf("ERROR: vmaCreateAllocator failed (%u)\n", res); + printf("ERROR: vmaCreateAllocator failed (%d)\n", res); return RESULT_ERROR_VULKAN; } @@ -1144,7 +1144,7 @@ void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit) { if(IssueWarning()) { - printf("Line %zu: vmaCreatePool failed (%u).\n", lineNumber, res); + printf("Line %zu: vmaCreatePool failed (%d).\n", lineNumber, res); } } @@ -1237,7 +1237,7 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) { if(IssueWarning()) { - printf("Line %zu: vmaCreateBuffer failed (%u).\n", lineNumber, res); + printf("Line %zu: vmaCreateBuffer failed (%d).\n", lineNumber, res); } } @@ -1330,7 +1330,7 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) { if(IssueWarning()) { - printf("Line %zu: vmaCreateImage failed (%u).\n", lineNumber, res); + printf("Line %zu: vmaCreateImage failed (%d).\n", lineNumber, res); } } @@ -1402,7 +1402,7 @@ void Player::ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit) { if(IssueWarning()) { - printf("Line %zu: vmaAllocateMemory failed (%u).\n", lineNumber, res); + printf("Line %zu: vmaAllocateMemory failed (%d).\n", lineNumber, res); } } @@ -1466,7 +1466,7 @@ void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvS } else { - printf("Line %zu: vmaAllocateMemory (called as vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage) failed (%u).\n", lineNumber, res); + printf("Line %zu: vmaAllocateMemory (called as vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage) failed (%d).\n", lineNumber, res); } AddAllocation(lineNumber, origPtr, std::move(allocDesc)); @@ -1498,7 +1498,7 @@ void Player::ExecuteMapMemory(size_t lineNumber, const CsvSplit& csvSplit) VkResult res = vmaMapMemory(m_Allocator, it->second.allocation, &pData); if(res != VK_SUCCESS) { - printf("Line %zu: vmaMapMemory failed (%u)\n", lineNumber, res); + printf("Line %zu: vmaMapMemory failed (%d)\n", lineNumber, res); } } else @@ -1670,6 +1670,11 @@ static int ProcessFile(const char* data, size_t numBytes) return RESULT_ERROR_FORMAT; } + if(g_Verbosity == VERBOSITY::MAXIMUM) + { + printf("Format version: %u,%u\n", g_FileVersion >> 16, g_FileVersion & 0xFFFF); + } + Player player; int result = player.Init(); if(result == 0) From 0e0f20fd5709b63bae572d46e90bda50aa352b19 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 15:34:55 +0200 Subject: [PATCH 26/51] Major refactoring: Defined clean API for recording, added VmaRecordFlagBits, VmaRecordFlags, VmaRecordSettings, VmaAllocatorCreateInfo::pRecordSettings. Created VmaRecorder class. --- src/vk_mem_alloc.h | 925 ++++++++++++++++++++++++++++++--------------- 1 file changed, 616 insertions(+), 309 deletions(-) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 1692147..3167490 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -1317,6 +1317,19 @@ typedef struct VmaVulkanFunctions { #endif } VmaVulkanFunctions; +typedef enum VmaRecordFlagBits { + VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001, + VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaRecordFlagBits; +typedef VkFlags VmaRecordFlags; + +/// TODO +typedef struct VmaRecordSettings +{ + VmaRecordFlags flags; + const char* pFilePath; +} VmaRecordSettings; + /// Description of a Allocator to be created. typedef struct VmaAllocatorCreateInfo { @@ -1387,6 +1400,9 @@ typedef struct VmaAllocatorCreateInfo e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`. */ const VmaVulkanFunctions* pVulkanFunctions; + /** TODO + */ + const VmaRecordSettings* pRecordSettings; } VmaAllocatorCreateInfo; /// Creates Allocator object. @@ -2676,29 +2692,6 @@ static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF; END OF CONFIGURATION */ -// TEMP ADDED - -void Crash() -{ - int* i = 0; *i = 0; -} - -FILE* g_File; -LARGE_INTEGER g_Freq, g_StartCounter; -VMA_MUTEX g_FileMutex; - -void EnsureFile() -{ - if(!g_File) - { - fopen_s(&g_File, "VMA_Usage_Dump", "wb"); - fprintf(g_File, "%s\n", "Vulkan Memory Allocator,Calls recording"); - fprintf(g_File, "%s\n", "1,2"); - QueryPerformanceFrequency(&g_Freq); - QueryPerformanceCounter(&g_StartCounter); - } -} - static VkAllocationCallbacks VmaEmptyAllocationCallbacks = { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL }; @@ -2735,6 +2728,11 @@ inline T VmaRoundDiv(T x, T y) return (x + (y / (T)2)) / y; } +static inline bool VmaStrIsEmpty(const char* pStr) +{ + return pStr == VMA_NULL || *pStr == '\0'; +} + #ifndef VMA_SORT template @@ -4716,6 +4714,81 @@ public: uint32_t maxAllocationsToMove); }; +class VmaRecorder +{ +public: + VmaRecorder(); + VkResult Init(const VmaRecordSettings& settings, bool useMutex); + ~VmaRecorder(); + + void RecordCreateAllocator(uint32_t frameIndex); + void RecordDestroyAllocator(uint32_t frameIndex); + void RecordCreatePool(uint32_t frameIndex, + const VmaPoolCreateInfo& createInfo, + VmaPool pool); + void RecordDestroyPool(uint32_t frameIndex, VmaPool pool); + void RecordAllocateMemory(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation); + void RecordAllocateMemoryForBuffer(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation); + void RecordAllocateMemoryForImage(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation); + void RecordFreeMemory(uint32_t frameIndex, + VmaAllocation allocation); + void RecordSetAllocationUserData(uint32_t frameIndex, + VmaAllocation allocation, + const void* pUserData); + void RecordCreateLostAllocation(uint32_t frameIndex, + VmaAllocation allocation); + void RecordMapMemory(uint32_t frameIndex, + VmaAllocation allocation); + void RecordUnmapMemory(uint32_t frameIndex, + VmaAllocation allocation); + void RecordFlushAllocation(uint32_t frameIndex, + VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); + void RecordInvalidateAllocation(uint32_t frameIndex, + VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size); + void RecordCreateBuffer(uint32_t frameIndex, + const VkBufferCreateInfo& bufCreateInfo, + const VmaAllocationCreateInfo& allocCreateInfo, + VmaAllocation allocation); + void RecordCreateImage(uint32_t frameIndex, + const VkImageCreateInfo& imageCreateInfo, + const VmaAllocationCreateInfo& allocCreateInfo, + VmaAllocation allocation); + void RecordDestroyBuffer(uint32_t frameIndex, + VmaAllocation allocation); + void RecordDestroyImage(uint32_t frameIndex, + VmaAllocation allocation); + +private: + struct CallParams + { + uint32_t threadId; + double time; + }; + + bool m_UseMutex; + VmaRecordFlags m_Flags; + FILE* m_File; + VMA_MUTEX m_FileMutex; + int64_t m_Freq; + int64_t m_StartCounter; + + void GetBasicParams(CallParams& outParams); + void Flush(); +}; + // Main allocator object. struct VmaAllocator_T { @@ -4744,6 +4817,7 @@ public: VMA_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES]; VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); + VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo); ~VmaAllocator_T(); const VkAllocationCallbacks* GetAllocationCallbacks() const @@ -4789,6 +4863,8 @@ public: return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; } + VmaRecorder* GetRecorder() const { return m_pRecorder; } + void GetBufferMemoryRequirements( VkBuffer hBuffer, VkMemoryRequirements& memReq, @@ -4874,6 +4950,8 @@ private: VmaVulkanFunctions m_VulkanFunctions; + VmaRecorder* m_pRecorder; + void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions); VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex); @@ -7951,6 +8029,366 @@ bool VmaDefragmentator::MoveMakesSense( return false; } +//////////////////////////////////////////////////////////////////////////////// +// VmaRecorder + +VmaRecorder::VmaRecorder() : + m_UseMutex(true), + m_Flags(0), + m_File(VMA_NULL), + m_Freq(INT64_MAX), + m_StartCounter(INT64_MAX) +{ +} + +VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex) +{ + m_UseMutex = useMutex; + m_Flags = settings.flags; + + QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq); + QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter); + + // Open file for writing. + errno_t err = fopen_s(&m_File, settings.pFilePath, "wb"); + if(err != 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + // Write header. + fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording"); + fprintf(m_File, "%s\n", "1,2"); + + return VK_SUCCESS; +} + +VmaRecorder::~VmaRecorder() +{ + if(m_File != VMA_NULL) + { + fclose(m_File); + } +} + +void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex); + Flush(); +} + +void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex); + Flush(); +} + +void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex, + createInfo.memoryTypeIndex, + createInfo.flags, + createInfo.blockSize, + createInfo.minBlockCount, + createInfo.maxBlockCount, + createInfo.frameInUseCount, + pool); + Flush(); +} + +void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex, + pool); + Flush(); +} + +void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + vkMemReq.size, + vkMemReq.alignment, + vkMemReq.memoryTypeBits, + createInfo.flags, + createInfo.usage, + createInfo.requiredFlags, + createInfo.preferredFlags, + createInfo.memoryTypeBits, + createInfo.pool, + allocation, + createInfo.pUserData ? (const char*)createInfo.pUserData : ""); + Flush(); +} + +void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + vkMemReq.size, + vkMemReq.alignment, + vkMemReq.memoryTypeBits, + requiresDedicatedAllocation ? 1 : 0, + prefersDedicatedAllocation ? 1 : 0, + createInfo.flags, + createInfo.usage, + createInfo.requiredFlags, + createInfo.preferredFlags, + createInfo.memoryTypeBits, + createInfo.pool, + allocation, + createInfo.pUserData ? (const char*)createInfo.pUserData : ""); + Flush(); +} + +void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex, + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + const VmaAllocationCreateInfo& createInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + vkMemReq.size, + vkMemReq.alignment, + vkMemReq.memoryTypeBits, + requiresDedicatedAllocation ? 1 : 0, + prefersDedicatedAllocation ? 1 : 0, + createInfo.flags, + createInfo.usage, + createInfo.requiredFlags, + createInfo.preferredFlags, + createInfo.memoryTypeBits, + createInfo.pool, + allocation, + createInfo.pUserData ? (const char*)createInfo.pUserData : ""); + Flush(); +} + +void VmaRecorder::RecordFreeMemory(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex, + VmaAllocation allocation, + const void* pUserData) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + allocation, + pUserData ? (const char*)pUserData : ""); + Flush(); +} + +void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordMapMemory(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex, + VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex, + allocation, + offset, + size); + Flush(); +} + +void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex, + VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex, + allocation, + offset, + size); + Flush(); +} + +void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex, + const VkBufferCreateInfo& bufCreateInfo, + const VmaAllocationCreateInfo& allocCreateInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + bufCreateInfo.flags, + bufCreateInfo.size, + bufCreateInfo.usage, + bufCreateInfo.sharingMode, + allocCreateInfo.flags, + allocCreateInfo.usage, + allocCreateInfo.requiredFlags, + allocCreateInfo.preferredFlags, + allocCreateInfo.memoryTypeBits, + allocCreateInfo.pool, + allocation, + allocCreateInfo.pUserData ? (const char*)allocCreateInfo.pUserData : ""); + Flush(); +} + +void VmaRecorder::RecordCreateImage(uint32_t frameIndex, + const VkImageCreateInfo& imageCreateInfo, + const VmaAllocationCreateInfo& allocCreateInfo, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, + imageCreateInfo.flags, + imageCreateInfo.imageType, + imageCreateInfo.format, + imageCreateInfo.extent.width, + imageCreateInfo.extent.height, + imageCreateInfo.extent.depth, + imageCreateInfo.mipLevels, + imageCreateInfo.arrayLayers, + imageCreateInfo.samples, + imageCreateInfo.tiling, + imageCreateInfo.usage, + imageCreateInfo.sharingMode, + imageCreateInfo.initialLayout, + allocCreateInfo.flags, + allocCreateInfo.usage, + allocCreateInfo.requiredFlags, + allocCreateInfo.preferredFlags, + allocCreateInfo.memoryTypeBits, + allocCreateInfo.pool, + allocation, + allocCreateInfo.pUserData ? (const char*)allocCreateInfo.pUserData : ""); + Flush(); +} + +void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::RecordDestroyImage(uint32_t frameIndex, + VmaAllocation allocation) +{ + CallParams callParams; + GetBasicParams(callParams); + + VmaMutexLock lock(m_FileMutex, m_UseMutex); + fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex, + allocation); + Flush(); +} + +void VmaRecorder::GetBasicParams(CallParams& outParams) +{ + outParams.threadId = GetCurrentThreadId(); + + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq; +} + +void VmaRecorder::Flush() +{ + if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0) + { + fflush(m_File); + } +} + //////////////////////////////////////////////////////////////////////////////// // VmaAllocator_T @@ -7965,7 +8403,8 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : m_PhysicalDevice(pCreateInfo->physicalDevice), m_CurrentFrameIndex(0), m_Pools(VmaStlAllocator(GetAllocationCallbacks())), - m_NextPoolId(0) + m_NextPoolId(0), + m_pRecorder(VMA_NULL) { if(VMA_DEBUG_DETECT_CORRUPTION) { @@ -8044,8 +8483,37 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : } } +VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) +{ + VkResult res = VK_SUCCESS; + + if(pCreateInfo->pRecordSettings != VMA_NULL && + !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath)) + { + m_pRecorder = vma_new(this, VmaRecorder)(); + res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex); + if(res != VK_SUCCESS) + { + return res; + } + } + + if(m_pRecorder != VMA_NULL) + { + m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex()); + } + + return res; +} + VmaAllocator_T::~VmaAllocator_T() { + if(m_pRecorder != VMA_NULL) + { + m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex()); + vma_delete(this, m_pRecorder); + } + VMA_ASSERT(m_Pools.empty()); for(size_t i = GetMemoryTypeCount(); i--; ) @@ -9363,19 +9831,7 @@ VkResult vmaCreateAllocator( VMA_ASSERT(pCreateInfo && pAllocator); VMA_DEBUG_LOG("vmaCreateAllocator"); *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo); - - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = (*pAllocator)->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaCreateAllocator\n", threadId, time, frameIndex); - fflush(g_File); - } - - return VK_SUCCESS; + return (*pAllocator)->Init(pCreateInfo); } void vmaDestroyAllocator( @@ -9383,17 +9839,6 @@ void vmaDestroyAllocator( { if(allocator != VK_NULL_HANDLE) { - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaDestroyAllocator\n", threadId, time, frameIndex); - fflush(g_File); - } - VMA_DEBUG_LOG("vmaDestroyAllocator"); VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; vma_delete(&allocationCallbacks, allocator); @@ -9731,31 +10176,18 @@ VkResult vmaCreatePool( VmaPool* pPool) { VMA_ASSERT(allocator && pCreateInfo && pPool); - + VMA_DEBUG_LOG("vmaCreatePool"); - + VMA_DEBUG_GLOBAL_MUTEX_LOCK - + VkResult res = allocator->CreatePool(pCreateInfo, pPool); - + + if(res == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", threadId, time, frameIndex, - pCreateInfo->memoryTypeIndex, - pCreateInfo->flags, - pCreateInfo->blockSize, - pCreateInfo->minBlockCount, - pCreateInfo->maxBlockCount, - pCreateInfo->frameInUseCount, - (*pPool)); - fflush(g_File); + allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool); } - + return res; } @@ -9764,27 +10196,20 @@ void vmaDestroyPool( VmaPool pool) { VMA_ASSERT(allocator); - + if(pool == VK_NULL_HANDLE) { return; } - - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", threadId, time, frameIndex, - pool); - fflush(g_File); - } - + VMA_DEBUG_LOG("vmaDestroyPool"); - + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool); + } allocator->DestroyPool(pool); } @@ -9847,29 +10272,16 @@ VkResult vmaAllocateMemory( VMA_SUBALLOCATION_TYPE_UNKNOWN, pAllocation); + if(result == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, - pVkMemoryRequirements->size, - pVkMemoryRequirements->alignment, - pVkMemoryRequirements->memoryTypeBits, - pCreateInfo->flags, - pCreateInfo->usage, - pCreateInfo->requiredFlags, - pCreateInfo->preferredFlags, - pCreateInfo->memoryTypeBits, - pCreateInfo->pool, - *pAllocation, - pCreateInfo->pUserData ? (const char*)pCreateInfo->pUserData : ""); - fflush(g_File); + allocator->GetRecorder()->RecordAllocateMemory( + allocator->GetCurrentFrameIndex(), + *pVkMemoryRequirements, + *pCreateInfo, + *pAllocation); } - - if(pAllocationInfo && result == VK_SUCCESS) + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) { allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } @@ -9907,28 +10319,15 @@ VkResult vmaAllocateMemoryForBuffer( VMA_SUBALLOCATION_TYPE_BUFFER, pAllocation); + if(result == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, - vkMemReq.size, - vkMemReq.alignment, - vkMemReq.memoryTypeBits, - requiresDedicatedAllocation ? 1 : 0, - prefersDedicatedAllocation ? 1 : 0, - pCreateInfo->flags, - pCreateInfo->usage, - pCreateInfo->requiredFlags, - pCreateInfo->preferredFlags, - pCreateInfo->memoryTypeBits, - pCreateInfo->pool, - *pAllocation, - pCreateInfo->pUserData ? (const char*)pCreateInfo->pUserData : ""); - fflush(g_File); + allocator->GetRecorder()->RecordAllocateMemoryForBuffer( + allocator->GetCurrentFrameIndex(), + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pCreateInfo, + *pAllocation); } if(pAllocationInfo && result == VK_SUCCESS) @@ -9946,7 +10345,6 @@ VkResult vmaAllocateMemoryForImage( VmaAllocation* pAllocation, VmaAllocationInfo* pAllocationInfo) { - Crash(); VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation); VMA_DEBUG_LOG("vmaAllocateMemoryForImage"); @@ -9969,28 +10367,15 @@ VkResult vmaAllocateMemoryForImage( VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, pAllocation); + if(result == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, - vkMemReq.size, - vkMemReq.alignment, - vkMemReq.memoryTypeBits, - requiresDedicatedAllocation ? 1 : 0, - prefersDedicatedAllocation ? 1 : 0, - pCreateInfo->flags, - pCreateInfo->usage, - pCreateInfo->requiredFlags, - pCreateInfo->preferredFlags, - pCreateInfo->memoryTypeBits, - pCreateInfo->pool, - *pAllocation, - pCreateInfo->pUserData ? (const char*)pCreateInfo->pUserData : ""); - fflush(g_File); + allocator->GetRecorder()->RecordAllocateMemoryForImage( + allocator->GetCurrentFrameIndex(), + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pCreateInfo, + *pAllocation); } if(pAllocationInfo && result == VK_SUCCESS) @@ -10006,26 +10391,23 @@ void vmaFreeMemory( VmaAllocation allocation) { VMA_ASSERT(allocator); - + if(allocation == VK_NULL_HANDLE) { return; } - - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", threadId, time, frameIndex, - allocation); - fflush(g_File); - } - + VMA_DEBUG_LOG("vmaFreeMemory"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordFreeMemory( + allocator->GetCurrentFrameIndex(), + allocation); + } + allocator->FreeMemory(allocation); } @@ -10061,20 +10443,15 @@ void vmaSetAllocationUserData( VMA_DEBUG_GLOBAL_MUTEX_LOCK - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", threadId, time, frameIndex, - allocation, - (const char*)pUserData); - fflush(g_File); - } - allocation->SetUserData(allocator, pUserData); + + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordSetAllocationUserData( + allocator->GetCurrentFrameIndex(), + allocation, + pUserData); + } } void vmaCreateLostAllocation( @@ -10087,16 +10464,11 @@ void vmaCreateLostAllocation( allocator->CreateLostAllocation(pAllocation); + if(allocator->GetRecorder() != VMA_NULL) { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", threadId, time, frameIndex, - (*pAllocation)); - fflush(g_File); + allocator->GetRecorder()->RecordCreateLostAllocation( + allocator->GetCurrentFrameIndex(), + *pAllocation); } } @@ -10107,21 +10479,18 @@ VkResult vmaMapMemory( { VMA_ASSERT(allocator && allocation && ppData); - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaMapMemory,%p\n", threadId, time, frameIndex, - allocation); - fflush(g_File); - } - VMA_DEBUG_GLOBAL_MUTEX_LOCK - return allocator->Map(allocation, ppData); + VkResult res = allocator->Map(allocation, ppData); + + if(res == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordMapMemory( + allocator->GetCurrentFrameIndex(), + allocation); + } + + return res; } void vmaUnmapMemory( @@ -10130,20 +10499,15 @@ void vmaUnmapMemory( { VMA_ASSERT(allocator && allocation); - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", threadId, time, frameIndex, - allocation); - fflush(g_File); - } - VMA_DEBUG_GLOBAL_MUTEX_LOCK + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordUnmapMemory( + allocator->GetCurrentFrameIndex(), + allocation); + } + allocator->Unmap(allocation); } @@ -10153,23 +10517,16 @@ void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDevi VMA_DEBUG_LOG("vmaFlushAllocation"); - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", threadId, time, frameIndex, - allocation, - offset, - size); - fflush(g_File); - } - VMA_DEBUG_GLOBAL_MUTEX_LOCK allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH); + + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordFlushAllocation( + allocator->GetCurrentFrameIndex(), + allocation, offset, size); + } } void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) @@ -10178,23 +10535,16 @@ void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, V VMA_DEBUG_LOG("vmaInvalidateAllocation"); - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", threadId, time, frameIndex, - allocation, - offset, - size); - fflush(g_File); - } - VMA_DEBUG_GLOBAL_MUTEX_LOCK allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE); + + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordInvalidateAllocation( + allocator->GetCurrentFrameIndex(), + allocation, offset, size); + } } VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits) @@ -10328,28 +10678,14 @@ VkResult vmaCreateBuffer( allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, - pBufferCreateInfo->flags, - pBufferCreateInfo->size, - pBufferCreateInfo->usage, - pBufferCreateInfo->sharingMode, - pAllocationCreateInfo->flags, - pAllocationCreateInfo->usage, - pAllocationCreateInfo->requiredFlags, - pAllocationCreateInfo->preferredFlags, - pAllocationCreateInfo->memoryTypeBits, - pAllocationCreateInfo->pool, - (*pAllocation), - pAllocationCreateInfo->pUserData ? (const char*)pAllocationCreateInfo->pUserData : ""); - fflush(g_File); - } + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordCreateBuffer( + allocator->GetCurrentFrameIndex(), + *pBufferCreateInfo, + *pAllocationCreateInfo, + *pAllocation); + } return VK_SUCCESS; } @@ -10378,24 +10714,22 @@ void vmaDestroyBuffer( return; } + VMA_DEBUG_LOG("vmaDestroyBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(allocator->GetRecorder() != VMA_NULL) { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", threadId, time, frameIndex, + allocator->GetRecorder()->RecordDestroyBuffer( + allocator->GetCurrentFrameIndex(), allocation); - fflush(g_File); } - VMA_DEBUG_LOG("vmaDestroyBuffer"); - VMA_DEBUG_GLOBAL_MUTEX_LOCK if(buffer != VK_NULL_HANDLE) { (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks()); } + if(allocation != VK_NULL_HANDLE) { allocator->FreeMemory(allocation); @@ -10462,37 +10796,14 @@ VkResult vmaCreateImage( allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } - { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", threadId, time, frameIndex, - pImageCreateInfo->flags, - pImageCreateInfo->imageType, - pImageCreateInfo->format, - pImageCreateInfo->extent.width, - pImageCreateInfo->extent.height, - pImageCreateInfo->extent.depth, - pImageCreateInfo->mipLevels, - pImageCreateInfo->arrayLayers, - pImageCreateInfo->samples, - pImageCreateInfo->tiling, - pImageCreateInfo->usage, - pImageCreateInfo->sharingMode, - pImageCreateInfo->initialLayout, - pAllocationCreateInfo->flags, - pAllocationCreateInfo->usage, - pAllocationCreateInfo->requiredFlags, - pAllocationCreateInfo->preferredFlags, - pAllocationCreateInfo->memoryTypeBits, - pAllocationCreateInfo->pool, - (*pAllocation), - pAllocationCreateInfo->pUserData ? (const char*)pAllocationCreateInfo->pUserData : ""); - fflush(g_File); - } + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordCreateImage( + allocator->GetCurrentFrameIndex(), + *pImageCreateInfo, + *pAllocationCreateInfo, + *pAllocation); + } return VK_SUCCESS; } @@ -10521,21 +10832,17 @@ void vmaDestroyImage( return; } + VMA_DEBUG_LOG("vmaDestroyImage"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(allocator->GetRecorder() != VMA_NULL) { - VmaMutexLock lock(g_FileMutex, true); - EnsureFile(); - LARGE_INTEGER counter; QueryPerformanceCounter(&counter); - const DWORD threadId = GetCurrentThreadId(); - const double time = (double)(counter.QuadPart - g_StartCounter.QuadPart) / (double)g_Freq.QuadPart; - const uint32_t frameIndex = allocator->GetCurrentFrameIndex(); - fprintf(g_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", threadId, time, frameIndex, + allocator->GetRecorder()->RecordDestroyImage( + allocator->GetCurrentFrameIndex(), allocation); - fflush(g_File); } - VMA_DEBUG_LOG("vmaDestroyImage"); - VMA_DEBUG_GLOBAL_MUTEX_LOCK if(image != VK_NULL_HANDLE) { (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks()); From e5d9b01a3acf14d637be43379f662b36c1f7f3fb Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 15:50:03 +0200 Subject: [PATCH 27/51] Added macro VMA_RECORDING_ENABLED, enabled only on Windows for now. --- src/VmaUsage.h | 1 + src/vk_mem_alloc.h | 72 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/VmaUsage.h b/src/VmaUsage.h index 79daaf8..e788a30 100644 --- a/src/VmaUsage.h +++ b/src/VmaUsage.h @@ -25,6 +25,7 @@ include all public interface declarations. Example: //#define VMA_DEBUG_MARGIN 16 //#define VMA_DEBUG_DETECT_CORRUPTION 1 //#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 +//#define VMA_RECORDING_ENABLED 0 #pragma warning(push, 4) #pragma warning(disable: 4127) // conditional expression is constant diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 3167490..7dfdd81 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -1323,6 +1323,18 @@ typedef enum VmaRecordFlagBits { } VmaRecordFlagBits; typedef VkFlags VmaRecordFlags; +/* +Define this macro to 0/1 to disable/enable support for recording functionality, +available through VmaAllocatorCreateInfo::pRecordSettings. +*/ +#ifndef VMA_RECORDING_ENABLED + #ifdef _WIN32 + #define VMA_RECORDING_ENABLED 1 + #else + #define VMA_RECORDING_ENABLED 0 + #endif +#endif + /// TODO typedef struct VmaRecordSettings { @@ -4714,6 +4726,8 @@ public: uint32_t maxAllocationsToMove); }; +#if VMA_RECORDING_ENABLED + class VmaRecorder { public: @@ -4789,6 +4803,8 @@ private: void Flush(); }; +#endif // #if VMA_RECORDING_ENABLED + // Main allocator object. struct VmaAllocator_T { @@ -4863,7 +4879,9 @@ public: return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; } +#if VMA_RECORDING_ENABLED VmaRecorder* GetRecorder() const { return m_pRecorder; } +#endif void GetBufferMemoryRequirements( VkBuffer hBuffer, @@ -4950,7 +4968,9 @@ private: VmaVulkanFunctions m_VulkanFunctions; +#if VMA_RECORDING_ENABLED VmaRecorder* m_pRecorder; +#endif void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions); @@ -8032,6 +8052,8 @@ bool VmaDefragmentator::MoveMakesSense( //////////////////////////////////////////////////////////////////////////////// // VmaRecorder +#if VMA_RECORDING_ENABLED + VmaRecorder::VmaRecorder() : m_UseMutex(true), m_Flags(0), @@ -8389,6 +8411,8 @@ void VmaRecorder::Flush() } } +#endif // #if VMA_RECORDING_ENABLED + //////////////////////////////////////////////////////////////////////////////// // VmaAllocator_T @@ -8403,8 +8427,10 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : m_PhysicalDevice(pCreateInfo->physicalDevice), m_CurrentFrameIndex(0), m_Pools(VmaStlAllocator(GetAllocationCallbacks())), - m_NextPoolId(0), - m_pRecorder(VMA_NULL) + m_NextPoolId(0) +#if VMA_RECORDING_ENABLED + ,m_pRecorder(VMA_NULL) +#endif { if(VMA_DEBUG_DETECT_CORRUPTION) { @@ -8490,17 +8516,17 @@ VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) if(pCreateInfo->pRecordSettings != VMA_NULL && !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath)) { +#if VMA_RECORDING_ENABLED m_pRecorder = vma_new(this, VmaRecorder)(); res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex); if(res != VK_SUCCESS) { return res; } - } - - if(m_pRecorder != VMA_NULL) - { m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex()); +#else + return VK_ERROR_FEATURE_NOT_PRESENT; +#endif } return res; @@ -8508,11 +8534,13 @@ VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) VmaAllocator_T::~VmaAllocator_T() { +#if VMA_RECORDING_ENABLED if(m_pRecorder != VMA_NULL) { m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex()); vma_delete(this, m_pRecorder); } +#endif VMA_ASSERT(m_Pools.empty()); @@ -10183,10 +10211,12 @@ VkResult vmaCreatePool( VkResult res = allocator->CreatePool(pCreateInfo, pPool); +#if VMA_RECORDING_ENABLED if(res == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool); } +#endif return res; } @@ -10206,10 +10236,12 @@ void vmaDestroyPool( VMA_DEBUG_GLOBAL_MUTEX_LOCK +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool); } +#endif allocator->DestroyPool(pool); } @@ -10272,6 +10304,7 @@ VkResult vmaAllocateMemory( VMA_SUBALLOCATION_TYPE_UNKNOWN, pAllocation); +#if VMA_RECORDING_ENABLED if(result == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordAllocateMemory( @@ -10280,6 +10313,7 @@ VkResult vmaAllocateMemory( *pCreateInfo, *pAllocation); } +#endif if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) { @@ -10319,6 +10353,7 @@ VkResult vmaAllocateMemoryForBuffer( VMA_SUBALLOCATION_TYPE_BUFFER, pAllocation); +#if VMA_RECORDING_ENABLED if(result == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordAllocateMemoryForBuffer( @@ -10329,6 +10364,7 @@ VkResult vmaAllocateMemoryForBuffer( *pCreateInfo, *pAllocation); } +#endif if(pAllocationInfo && result == VK_SUCCESS) { @@ -10367,6 +10403,7 @@ VkResult vmaAllocateMemoryForImage( VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, pAllocation); +#if VMA_RECORDING_ENABLED if(result == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordAllocateMemoryForImage( @@ -10377,6 +10414,7 @@ VkResult vmaAllocateMemoryForImage( *pCreateInfo, *pAllocation); } +#endif if(pAllocationInfo && result == VK_SUCCESS) { @@ -10401,12 +10439,14 @@ void vmaFreeMemory( VMA_DEBUG_GLOBAL_MUTEX_LOCK +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordFreeMemory( allocator->GetCurrentFrameIndex(), allocation); } +#endif allocator->FreeMemory(allocation); } @@ -10445,6 +10485,7 @@ void vmaSetAllocationUserData( allocation->SetUserData(allocator, pUserData); +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordSetAllocationUserData( @@ -10452,6 +10493,7 @@ void vmaSetAllocationUserData( allocation, pUserData); } +#endif } void vmaCreateLostAllocation( @@ -10464,12 +10506,14 @@ void vmaCreateLostAllocation( allocator->CreateLostAllocation(pAllocation); +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordCreateLostAllocation( allocator->GetCurrentFrameIndex(), *pAllocation); } +#endif } VkResult vmaMapMemory( @@ -10483,12 +10527,14 @@ VkResult vmaMapMemory( VkResult res = allocator->Map(allocation, ppData); +#if VMA_RECORDING_ENABLED if(res == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordMapMemory( allocator->GetCurrentFrameIndex(), allocation); } +#endif return res; } @@ -10501,12 +10547,14 @@ void vmaUnmapMemory( VMA_DEBUG_GLOBAL_MUTEX_LOCK +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordUnmapMemory( allocator->GetCurrentFrameIndex(), allocation); } +#endif allocator->Unmap(allocation); } @@ -10521,12 +10569,14 @@ void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDevi allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH); +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordFlushAllocation( allocator->GetCurrentFrameIndex(), allocation, offset, size); } +#endif } void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size) @@ -10539,12 +10589,14 @@ void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, V allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE); +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordInvalidateAllocation( allocator->GetCurrentFrameIndex(), allocation, offset, size); } +#endif } VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits) @@ -10678,6 +10730,7 @@ VkResult vmaCreateBuffer( allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordCreateBuffer( @@ -10686,6 +10739,7 @@ VkResult vmaCreateBuffer( *pAllocationCreateInfo, *pAllocation); } +#endif return VK_SUCCESS; } @@ -10718,12 +10772,14 @@ void vmaDestroyBuffer( VMA_DEBUG_GLOBAL_MUTEX_LOCK +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordDestroyBuffer( allocator->GetCurrentFrameIndex(), allocation); } +#endif if(buffer != VK_NULL_HANDLE) { @@ -10796,6 +10852,7 @@ VkResult vmaCreateImage( allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordCreateImage( @@ -10804,6 +10861,7 @@ VkResult vmaCreateImage( *pAllocationCreateInfo, *pAllocation); } +#endif return VK_SUCCESS; } @@ -10836,12 +10894,14 @@ void vmaDestroyImage( VMA_DEBUG_GLOBAL_MUTEX_LOCK +#if VMA_RECORDING_ENABLED if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordDestroyImage( allocator->GetCurrentFrameIndex(), allocation); } +#endif if(image != VK_NULL_HANDLE) { From 5c49bebe101a2d15961e171bd2d131739baaf768 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 16:10:11 +0200 Subject: [PATCH 28/51] Added support for recording and replaying object creation also when it originally failed. --- src/VmaReplay/VmaReplay.cpp | 180 +++++++++++++++++++++--------------- src/vk_mem_alloc.h | 61 ++++++------ 2 files changed, 139 insertions(+), 102 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index cf06fd4..d3a991f 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -501,7 +501,7 @@ private: // If failed, prints warning, returns false and outPool = null. bool FindPool(size_t lineNumber, uint64_t origPool, VmaPool& outPool); // If allocation with that origPtr already exists, prints warning and replaces it. - void AddAllocation(size_t lineNumber, uint64_t origPtr, Allocation&& allocDesc); + void AddAllocation(size_t lineNumber, uint64_t origPtr, VkResult res, const char* functionName, Allocation&& allocDesc); // Increments warning counter. Returns true if warning message should be printed. bool IssueWarning(); @@ -724,18 +724,58 @@ bool Player::FindPool(size_t lineNumber, uint64_t origPool, VmaPool& outPool) return true; } -void Player::AddAllocation(size_t lineNumber, uint64_t origPtr, Allocation&& allocDesc) +void Player::AddAllocation(size_t lineNumber, uint64_t origPtr, VkResult res, const char* functionName, Allocation&& allocDesc) { - const auto existingIt = m_Allocations.find(origPtr); - if(existingIt != m_Allocations.end()) + if(origPtr) { - if(IssueWarning()) + if(res == VK_SUCCESS) { - printf("Line %zu: Allocation %llX already exists.\n", lineNumber, origPtr); + // Originally succeeded, currently succeeded. + // Just save pointer (done below). + } + else + { + // Originally succeeded, currently failed. + // Print warning. Save null pointer. + if(IssueWarning()) + { + printf("Line %zu: %s failed (%d), while originally succeeded.\n", lineNumber, functionName, 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] = std::move(allocDesc); + } + else + { + if(res == VK_SUCCESS) + { + // Originally failed, currently succeeded. + // Print warning, destroy the object. + if(IssueWarning()) + { + printf("Line %zu: %s succeeded, originally failed.\n", lineNumber, functionName); + } + + Destroy(allocDesc); + } + else + { + // Originally failed, currently failed. + // Print warning. + if(IssueWarning()) + { + printf("Line %zu: %s failed (%d), originally also failed.\n", lineNumber, functionName, res); + } } } - - m_Allocations[origPtr] = std::move(allocDesc); } bool Player::IssueWarning() @@ -1134,30 +1174,61 @@ void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit) StrRangeToUint(csvSplit.GetRange(FIRST_PARAM_INDEX + 5), poolCreateInfo.frameInUseCount) && StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX + 6), origPtr)) { + m_Stats.RegisterCreatePool(); + Pool poolDesc = {}; VkResult res = vmaCreatePool(m_Allocator, &poolCreateInfo, &poolDesc.pool); - if(res == VK_SUCCESS) + + if(origPtr) { - m_Stats.RegisterCreatePool(); + if(res == VK_SUCCESS) + { + // Originally succeeded, currently succeeded. + // Just save pointer (done below). + } + else + { + // Originally succeeded, currently failed. + // Print warning. Save null pointer. + if(IssueWarning()) + { + printf("Line %zu: vmaCreatePool failed (%d), while originally succeeded.\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()) + if(res == VK_SUCCESS) { - printf("Line %zu: vmaCreatePool failed (%d).\n", lineNumber, res); + // Originally failed, currently succeeded. + // Print warning, destroy the pool. + if(IssueWarning()) + { + printf("Line %zu: vmaCreatePool succeeded, originally failed.\n", lineNumber); + } + + vmaDestroyPool(m_Allocator, poolDesc.pool); + } + else + { + // Originally failed, currently failed. + // Print warning. + if(IssueWarning()) + { + printf("Line %zu: vmaCreatePool failed (%d), originally also failed.\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 { @@ -1227,21 +1298,11 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) { FindPool(lineNumber, origPool, allocCreateInfo.pool); + m_Stats.RegisterCreateBuffer(bufCreateInfo.usage); + Allocation allocDesc = {}; VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr); - if(res == VK_SUCCESS) - { - m_Stats.RegisterCreateBuffer(bufCreateInfo.usage); - } - else - { - if(IssueWarning()) - { - printf("Line %zu: vmaCreateBuffer failed (%d).\n", lineNumber, res); - } - } - - AddAllocation(lineNumber, origPtr, std::move(allocDesc)); + AddAllocation(lineNumber, origPtr, res, "vmaCreateBuffer", std::move(allocDesc)); } else { @@ -1320,21 +1381,11 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) { FindPool(lineNumber, origPool, allocCreateInfo.pool); + m_Stats.RegisterCreateImage(imageCreateInfo.usage, imageCreateInfo.tiling); + Allocation allocDesc = {}; VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &allocDesc.image, &allocDesc.allocation, nullptr); - if(res == VK_SUCCESS) - { - m_Stats.RegisterCreateImage(imageCreateInfo.usage, imageCreateInfo.tiling); - } - else - { - if(IssueWarning()) - { - printf("Line %zu: vmaCreateImage failed (%d).\n", lineNumber, res); - } - } - - AddAllocation(lineNumber, origPtr, std::move(allocDesc)); + AddAllocation(lineNumber, origPtr, res, "vmaCreateImage", std::move(allocDesc)); } else { @@ -1358,7 +1409,7 @@ void Player::ExecuteCreateLostAllocation(size_t lineNumber, const CsvSplit& csvS vmaCreateLostAllocation(m_Allocator, &allocDesc.allocation); m_Stats.RegisterCreateAllocation(); - AddAllocation(lineNumber, origPtr, std::move(allocDesc)); + AddAllocation(lineNumber, origPtr, VK_SUCCESS, "vmaCreateLostAllocation", std::move(allocDesc)); } else { @@ -1392,21 +1443,11 @@ void Player::ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit) { FindPool(lineNumber, origPool, allocCreateInfo.pool); + m_Stats.RegisterCreateAllocation(); + Allocation allocDesc = {}; VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr); - if(res == VK_SUCCESS) - { - m_Stats.RegisterCreateAllocation(); - } - else - { - if(IssueWarning()) - { - printf("Line %zu: vmaAllocateMemory failed (%d).\n", lineNumber, res); - } - } - - AddAllocation(lineNumber, origPtr, std::move(allocDesc)); + AddAllocation(lineNumber, origPtr, res, "vmaAllocateMemory", std::move(allocDesc)); } else { @@ -1458,18 +1499,11 @@ void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvS m_AllocateForBufferImageWarningIssued = true; } + m_Stats.RegisterCreateAllocation(); + Allocation allocDesc = {}; VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr); - if(res == VK_SUCCESS) - { - m_Stats.RegisterCreateAllocation(); - } - else - { - printf("Line %zu: vmaAllocateMemory (called as vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage) failed (%d).\n", lineNumber, res); - } - - AddAllocation(lineNumber, origPtr, std::move(allocDesc)); + AddAllocation(lineNumber, origPtr, res, "vmaAllocateMemory (called as vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage)", std::move(allocDesc)); } else { diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 7dfdd81..591c5c7 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -8525,6 +8525,7 @@ VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) } m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex()); #else + VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1."); return VK_ERROR_FEATURE_NOT_PRESENT; #endif } @@ -10212,7 +10213,7 @@ VkResult vmaCreatePool( VkResult res = allocator->CreatePool(pCreateInfo, pPool); #if VMA_RECORDING_ENABLED - if(res == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) + if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool); } @@ -10305,7 +10306,7 @@ VkResult vmaAllocateMemory( pAllocation); #if VMA_RECORDING_ENABLED - if(result == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) + if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordAllocateMemory( allocator->GetCurrentFrameIndex(), @@ -10354,7 +10355,7 @@ VkResult vmaAllocateMemoryForBuffer( pAllocation); #if VMA_RECORDING_ENABLED - if(result == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) + if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordAllocateMemoryForBuffer( allocator->GetCurrentFrameIndex(), @@ -10404,7 +10405,7 @@ VkResult vmaAllocateMemoryForImage( pAllocation); #if VMA_RECORDING_ENABLED - if(result == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) + if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordAllocateMemoryForImage( allocator->GetCurrentFrameIndex(), @@ -10528,7 +10529,7 @@ VkResult vmaMapMemory( VkResult res = allocator->Map(allocation, ppData); #if VMA_RECORDING_ENABLED - if(res == VK_SUCCESS && allocator->GetRecorder() != VMA_NULL) + if(allocator->GetRecorder() != VMA_NULL) { allocator->GetRecorder()->RecordMapMemory( allocator->GetCurrentFrameIndex(), @@ -10687,8 +10688,8 @@ VkResult vmaCreateBuffer( allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, requiresDedicatedAllocation, prefersDedicatedAllocation); - // Make sure alignment requirements for specific buffer usages reported - // in Physical Device Properties are included in alignment reported by memory requirements. + // Make sure alignment requirements for specific buffer usages reported + // in Physical Device Properties are included in alignment reported by memory requirements. if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0) { VMA_ASSERT(vkMemReq.alignment % @@ -10715,6 +10716,18 @@ VkResult vmaCreateBuffer( *pAllocationCreateInfo, VMA_SUBALLOCATION_TYPE_BUFFER, pAllocation); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordCreateBuffer( + allocator->GetCurrentFrameIndex(), + *pBufferCreateInfo, + *pAllocationCreateInfo, + *pAllocation); + } +#endif + if(res >= 0) { // 3. Bind buffer with memory. @@ -10730,17 +10743,6 @@ VkResult vmaCreateBuffer( allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } -#if VMA_RECORDING_ENABLED - if(allocator->GetRecorder() != VMA_NULL) - { - allocator->GetRecorder()->RecordCreateBuffer( - allocator->GetCurrentFrameIndex(), - *pBufferCreateInfo, - *pAllocationCreateInfo, - *pAllocation); - } -#endif - return VK_SUCCESS; } allocator->FreeMemory(*pAllocation); @@ -10837,6 +10839,18 @@ VkResult vmaCreateImage( *pAllocationCreateInfo, suballocType, pAllocation); + +#if VMA_RECORDING_ENABLED + if(allocator->GetRecorder() != VMA_NULL) + { + allocator->GetRecorder()->RecordCreateImage( + allocator->GetCurrentFrameIndex(), + *pImageCreateInfo, + *pAllocationCreateInfo, + *pAllocation); + } +#endif + if(res >= 0) { // 3. Bind image with memory. @@ -10852,17 +10866,6 @@ VkResult vmaCreateImage( allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); } -#if VMA_RECORDING_ENABLED - if(allocator->GetRecorder() != VMA_NULL) - { - allocator->GetRecorder()->RecordCreateImage( - allocator->GetCurrentFrameIndex(), - *pImageCreateInfo, - *pAllocationCreateInfo, - *pAllocation); - } -#endif - return VK_SUCCESS; } allocator->FreeMemory(*pAllocation); From c49eb6282fafedbadb304167ab3b47cac733d4b5 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 16:36:07 +0200 Subject: [PATCH 29/51] Deleted calculation of number of vmaCreateAllocator calls, as there is always at most one. --- src/VmaReplay/VmaReplay.cpp | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index d3a991f..db0db95 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -244,12 +244,8 @@ public: size_t GetLinearImageCreationCount() const { return m_LinearImageCreationCount; } size_t GetBufferCreationCount(uint32_t bufClass) const { return m_BufferCreationCount[bufClass]; } size_t GetAllocationCreationCount() const { return m_AllocationCreationCount; } - size_t GetAllocatorCreationCount() const { return m_AllocatorCreationCount; } - size_t GetAllocatorPeakCount() const { return m_AllocatorPeakCount; } size_t GetPoolCreationCount() const { return m_PoolCreationCount; } - void RegisterCreateAllocator(); - void RegisterDestroyAllocator(); void RegisterCreateImage(uint32_t usage, uint32_t tiling); void RegisterCreateBuffer(uint32_t usage); void RegisterCreatePool(); @@ -260,9 +256,6 @@ private: size_t m_LinearImageCreationCount = 0; size_t m_BufferCreationCount[4] = { }; size_t m_AllocationCreationCount = 0; // Also includes buffers and images, and lost allocations. - size_t m_AllocatorCreationCount = 0; - size_t m_AllocatorCurrCount = 0; - size_t m_AllocatorPeakCount = 0; size_t m_PoolCreationCount = 0; }; @@ -329,19 +322,6 @@ uint32_t Statistics::ImageUsageToClass(uint32_t usage) } } -void Statistics::RegisterCreateAllocator() -{ - ++m_AllocatorCreationCount; - ++m_AllocatorCurrCount; - m_AllocatorPeakCount = std::max(m_AllocatorPeakCount, m_AllocatorCurrCount); -} - -void Statistics::RegisterDestroyAllocator() -{ - if(m_AllocatorCurrCount > 0) - --m_AllocatorCurrCount; -} - void Statistics::RegisterCreateImage(uint32_t usage, uint32_t tiling) { if(tiling == VK_IMAGE_TILING_LINEAR) @@ -543,14 +523,6 @@ int Player::Init() Player::~Player() { - if(m_Stats.GetAllocatorCreationCount() > 1) - { - printf("WARNING: %zu VmaAllocator objects were created. It is recommended to use just one.\n", - m_Stats.GetAllocatorCreationCount()); - printf(" At most %zu allocators existed simultaneously.\n", - m_Stats.GetAllocatorPeakCount()); - } - if(g_Verbosity > VERBOSITY::MINIMUM) { PrintStats(); @@ -621,14 +593,14 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false)) { - m_Stats.RegisterCreateAllocator(); + // Nothing. } } else if(StrRangeEq(functionName, "vmaDestroyAllocator")) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 0, false)) { - m_Stats.RegisterDestroyAllocator(); + // Nothing. } } else if(StrRangeEq(functionName, "vmaCreatePool")) From dd715fbf9fdd003d50e3621729272fbabdd13b58 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 17:12:24 +0200 Subject: [PATCH 30/51] Fixed support for pUserData in object creation functions, as well as vmaSetAllocationUserData, whether it's null, custom pointer, or character string. --- src/VmaReplay/VmaReplay.cpp | 144 +++++++++++++++++++++++++++++++++--- src/vk_mem_alloc.h | 51 +++++++++++-- 2 files changed, 177 insertions(+), 18 deletions(-) diff --git a/src/VmaReplay/VmaReplay.cpp b/src/VmaReplay/VmaReplay.cpp index db0db95..d3dc579 100644 --- a/src/VmaReplay/VmaReplay.cpp +++ b/src/VmaReplay/VmaReplay.cpp @@ -178,16 +178,18 @@ public: void Set(const StrRange& line, size_t maxCount = RANGE_COUNT_MAX); + const StrRange& GetLine() const { return m_Line; } + 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] }; + m_Line.beg + m_Ranges[index * 2], + m_Line.beg + m_Ranges[index * 2 + 1] }; } private: - const char* m_Str = nullptr; + StrRange m_Line = { nullptr, nullptr }; size_t m_Count = 0; size_t m_Ranges[RANGE_COUNT_MAX * 2]; // Pairs of begin-end. }; @@ -195,14 +197,14 @@ private: void CsvSplit::Set(const StrRange& line, size_t maxCount) { assert(maxCount <= RANGE_COUNT_MAX); - m_Str = line.beg; + m_Line = line; 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] != ',')) + while(charIndex < strLen && (rangeIndex + 1 == maxCount || m_Line.beg[charIndex] != ',')) ++charIndex; m_Ranges[rangeIndex * 2 + 1] = charIndex; ++rangeIndex; @@ -457,6 +459,7 @@ private: }; struct Allocation { + uint32_t allocationFlags; VmaAllocation allocation; VkBuffer buffer; VkImage image; @@ -474,6 +477,8 @@ private: std::string m_LastLineTimeStr; Statistics m_Stats; + std::vector m_UserDataTmpStr; + void Destroy(const Allocation& alloc); // Finds VmaPool bu original pointer. @@ -494,8 +499,12 @@ private: // If parmeter count doesn't match, issues warning and returns false. bool ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& csvSplit, size_t expectedParamCount, bool lastUnbound); + // If failed, prints warning, returns false, and sets allocCreateInfo.pUserData to null. + bool PrepareUserData(size_t lineNumber, uint32_t allocCreateFlags, const StrRange& userDataColumn, const StrRange& wholeLine, void*& outUserData); + void ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit); void ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit); + void ExecuteSetAllocationUserData(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); @@ -608,12 +617,7 @@ void Player::ExecuteLine(size_t lineNumber, const StrRange& line) else if(StrRangeEq(functionName, "vmaDestroyPool")) ExecuteDestroyPool(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaSetAllocationUserData")) - { - if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true)) - { - // Ignore for now. - } - } + ExecuteSetAllocationUserData(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaCreateBuffer")) ExecuteCreateBuffer(lineNumber, csvSplit); else if(StrRangeEq(functionName, "vmaDestroyBuffer")) @@ -1131,6 +1135,37 @@ bool Player::ValidateFunctionParameterCount(size_t lineNumber, const CsvSplit& c return ok; } +bool Player::PrepareUserData(size_t lineNumber, uint32_t allocCreateFlags, const StrRange& userDataColumn, const StrRange& wholeLine, void*& outUserData) +{ + // String + if((allocCreateFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0) + { + const size_t len = wholeLine.end - userDataColumn.beg; + m_UserDataTmpStr.resize(len + 1); + memcpy(m_UserDataTmpStr.data(), userDataColumn.beg, len); + m_UserDataTmpStr[len] = '\0'; + outUserData = m_UserDataTmpStr.data(); + return true; + } + // Pointer + else + { + uint64_t pUserData = 0; + if(StrRangeToPtr(userDataColumn, pUserData)) + { + outUserData = (void*)(uintptr_t)pUserData; + return true; + } + } + + if(IssueWarning()) + { + printf("Line %zu: Invalid pUserData.\n", lineNumber); + } + outUserData = 0; + return false; +} + void Player::ExecuteCreatePool(size_t lineNumber, const CsvSplit& csvSplit) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 7, false)) @@ -1247,6 +1282,47 @@ void Player::ExecuteDestroyPool(size_t lineNumber, const CsvSplit& csvSplit) } } +void Player::ExecuteSetAllocationUserData(size_t lineNumber, const CsvSplit& csvSplit) +{ + if(ValidateFunctionParameterCount(lineNumber, csvSplit, 2, true)) + { + uint64_t origPtr = 0; + if(StrRangeToPtr(csvSplit.GetRange(FIRST_PARAM_INDEX), origPtr)) + { + const auto it = m_Allocations.find(origPtr); + if(it != m_Allocations.end()) + { + void* pUserData = nullptr; + if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 1) + { + PrepareUserData( + lineNumber, + it->second.allocationFlags, + csvSplit.GetRange(FIRST_PARAM_INDEX + 1), + csvSplit.GetLine(), + pUserData); + } + + vmaSetAllocationUserData(m_Allocator, it->second.allocation, pUserData); + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Allocation %llX not found.\n", lineNumber, origPtr); + } + } + } + else + { + if(IssueWarning()) + { + printf("Line %zu: Invalid parameters for vmaSetAllocationUserData.\n", lineNumber); + } + } + } +} + void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) { if(ValidateFunctionParameterCount(lineNumber, csvSplit, 12, true)) @@ -1270,9 +1346,20 @@ void Player::ExecuteCreateBuffer(size_t lineNumber, const CsvSplit& csvSplit) { FindPool(lineNumber, origPool, allocCreateInfo.pool); + if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 11) + { + PrepareUserData( + lineNumber, + allocCreateInfo.flags, + csvSplit.GetRange(FIRST_PARAM_INDEX + 11), + csvSplit.GetLine(), + allocCreateInfo.pUserData); + } + m_Stats.RegisterCreateBuffer(bufCreateInfo.usage); - Allocation allocDesc = {}; + Allocation allocDesc = { }; + allocDesc.allocationFlags = allocCreateInfo.flags; VkResult res = vmaCreateBuffer(m_Allocator, &bufCreateInfo, &allocCreateInfo, &allocDesc.buffer, &allocDesc.allocation, nullptr); AddAllocation(lineNumber, origPtr, res, "vmaCreateBuffer", std::move(allocDesc)); } @@ -1353,9 +1440,20 @@ void Player::ExecuteCreateImage(size_t lineNumber, const CsvSplit& csvSplit) { FindPool(lineNumber, origPool, allocCreateInfo.pool); + if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 20) + { + PrepareUserData( + lineNumber, + allocCreateInfo.flags, + csvSplit.GetRange(FIRST_PARAM_INDEX + 20), + csvSplit.GetLine(), + allocCreateInfo.pUserData); + } + m_Stats.RegisterCreateImage(imageCreateInfo.usage, imageCreateInfo.tiling); Allocation allocDesc = {}; + allocDesc.allocationFlags = allocCreateInfo.flags; VkResult res = vmaCreateImage(m_Allocator, &imageCreateInfo, &allocCreateInfo, &allocDesc.image, &allocDesc.allocation, nullptr); AddAllocation(lineNumber, origPtr, res, "vmaCreateImage", std::move(allocDesc)); } @@ -1415,9 +1513,20 @@ void Player::ExecuteAllocateMemory(size_t lineNumber, const CsvSplit& csvSplit) { FindPool(lineNumber, origPool, allocCreateInfo.pool); + if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 10) + { + PrepareUserData( + lineNumber, + allocCreateInfo.flags, + csvSplit.GetRange(FIRST_PARAM_INDEX + 10), + csvSplit.GetLine(), + allocCreateInfo.pUserData); + } + m_Stats.RegisterCreateAllocation(); Allocation allocDesc = {}; + allocDesc.allocationFlags = allocCreateInfo.flags; VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr); AddAllocation(lineNumber, origPtr, res, "vmaAllocateMemory", std::move(allocDesc)); } @@ -1457,6 +1566,16 @@ void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvS { FindPool(lineNumber, origPool, allocCreateInfo.pool); + if(csvSplit.GetCount() > FIRST_PARAM_INDEX + 12) + { + PrepareUserData( + lineNumber, + allocCreateInfo.flags, + csvSplit.GetRange(FIRST_PARAM_INDEX + 12), + csvSplit.GetLine(), + allocCreateInfo.pUserData); + } + if(requiresDedicatedAllocation || prefersDedicatedAllocation) { allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; @@ -1474,6 +1593,7 @@ void Player::ExecuteAllocateMemoryForBufferOrImage(size_t lineNumber, const CsvS m_Stats.RegisterCreateAllocation(); Allocation allocDesc = {}; + allocDesc.allocationFlags = allocCreateInfo.flags; VkResult res = vmaAllocateMemory(m_Allocator, &memReq, &allocCreateInfo, &allocDesc.allocation, nullptr); AddAllocation(lineNumber, origPtr, res, "vmaAllocateMemory (called as vmaAllocateMemoryForBuffer or vmaAllocateMemoryForImage)", std::move(allocDesc)); } diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 591c5c7..d9a8029 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -4792,6 +4792,17 @@ private: double time; }; + class UserDataString + { + public: + UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData); + const char* GetString() const { return m_Str; } + + private: + char m_PtrStr[17]; + const char* m_Str; + }; + bool m_UseMutex; VmaRecordFlags m_Flags; FILE* m_File; @@ -8150,6 +8161,7 @@ void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex, GetBasicParams(callParams); VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(createInfo.flags, createInfo.pUserData); fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, vkMemReq.size, vkMemReq.alignment, @@ -8161,7 +8173,7 @@ void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex, createInfo.memoryTypeBits, createInfo.pool, allocation, - createInfo.pUserData ? (const char*)createInfo.pUserData : ""); + userDataStr.GetString()); Flush(); } @@ -8176,6 +8188,7 @@ void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex, GetBasicParams(callParams); VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(createInfo.flags, createInfo.pUserData); fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, vkMemReq.size, vkMemReq.alignment, @@ -8189,7 +8202,7 @@ void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex, createInfo.memoryTypeBits, createInfo.pool, allocation, - createInfo.pUserData ? (const char*)createInfo.pUserData : ""); + userDataStr.GetString()); Flush(); } @@ -8204,6 +8217,7 @@ void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex, GetBasicParams(callParams); VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(createInfo.flags, createInfo.pUserData); fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, vkMemReq.size, vkMemReq.alignment, @@ -8217,7 +8231,7 @@ void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex, createInfo.memoryTypeBits, createInfo.pool, allocation, - createInfo.pUserData ? (const char*)createInfo.pUserData : ""); + userDataStr.GetString()); Flush(); } @@ -8241,9 +8255,12 @@ void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex, GetBasicParams(callParams); VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr( + allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0, + pUserData); fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex, allocation, - pUserData ? (const char*)pUserData : ""); + userDataStr.GetString()); Flush(); } @@ -8320,6 +8337,7 @@ void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex, GetBasicParams(callParams); VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData); fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, bufCreateInfo.flags, bufCreateInfo.size, @@ -8332,7 +8350,7 @@ void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex, allocCreateInfo.memoryTypeBits, allocCreateInfo.pool, allocation, - allocCreateInfo.pUserData ? (const char*)allocCreateInfo.pUserData : ""); + userDataStr.GetString()); Flush(); } @@ -8345,6 +8363,7 @@ void VmaRecorder::RecordCreateImage(uint32_t frameIndex, GetBasicParams(callParams); VmaMutexLock lock(m_FileMutex, m_UseMutex); + UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData); fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex, imageCreateInfo.flags, imageCreateInfo.imageType, @@ -8366,7 +8385,7 @@ void VmaRecorder::RecordCreateImage(uint32_t frameIndex, allocCreateInfo.memoryTypeBits, allocCreateInfo.pool, allocation, - allocCreateInfo.pUserData ? (const char*)allocCreateInfo.pUserData : ""); + userDataStr.GetString()); Flush(); } @@ -8394,6 +8413,26 @@ void VmaRecorder::RecordDestroyImage(uint32_t frameIndex, Flush(); } +VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData) +{ + if(pUserData != VMA_NULL) + { + if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0) + { + m_Str = (const char*)pUserData; + } + else + { + sprintf_s(m_PtrStr, "%p", pUserData); + m_Str = m_PtrStr; + } + } + else + { + m_Str = ""; + } +} + void VmaRecorder::GetBasicParams(CallParams& outParams) { outParams.threadId = GetCurrentThreadId(); From 3eb870d6d6c548b706a7b001dcee155d8b79114d Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Mon, 20 Aug 2018 17:53:02 +0200 Subject: [PATCH 31/51] Added documentation for new code elements related to recording. Added "Recording file format.md" file. --- docs/Recording file format.md | 176 ++++++++++++++ docs/html/annotated.html | 7 +- docs/html/classes.html | 8 +- docs/html/functions.html | 7 + docs/html/functions_vars.html | 7 + docs/html/general_considerations.html | 17 ++ docs/html/globals.html | 18 ++ docs/html/globals_defs.html | 3 + docs/html/globals_enum.html | 3 + docs/html/globals_eval.html | 6 + docs/html/globals_type.html | 9 + docs/html/memory_mapping.html | 7 +- docs/html/search/all_4.js | 2 +- docs/html/search/all_9.js | 2 + docs/html/search/all_f.js | 6 + docs/html/search/classes_0.js | 1 + docs/html/search/defines_0.js | 1 + docs/html/search/enums_0.js | 3 +- docs/html/search/enumvalues_0.js | 4 +- docs/html/search/typedefs_1.js | 3 + docs/html/search/variables_3.js | 2 +- docs/html/search/variables_6.js | 2 + ...uct_vma_allocator_create_info-members.html | 5 +- .../struct_vma_allocator_create_info.html | 20 ++ .../struct_vma_record_settings-members.html | 78 ++++++ docs/html/struct_vma_record_settings.html | 130 ++++++++++ docs/html/vk__mem__alloc_8h.html | 100 ++++++++ docs/html/vk__mem__alloc_8h_source.html | 228 +++++++++--------- src/vk_mem_alloc.h | 23 +- 29 files changed, 752 insertions(+), 126 deletions(-) create mode 100644 docs/Recording file format.md create mode 100644 docs/html/struct_vma_record_settings-members.html create mode 100644 docs/html/struct_vma_record_settings.html diff --git a/docs/Recording file format.md b/docs/Recording file format.md new file mode 100644 index 0000000..882a294 --- /dev/null +++ b/docs/Recording file format.md @@ -0,0 +1,176 @@ +This is an official documentation for file format used by Vulkan Memory Allocator library +to record and replay sequence of calls to its functions. +This feature can be enabled by using `VmaAllocatorCreateInfo::pRecordSettings` structure members. +For details, see main documentation of the library. +Playback can be launched using **VmaReplay** console application. + +Recording is a text file. +Line endings: Unix `'\n'`. +Character encoding: single-byte (can be ASCII or UTF-8, whaterver you use in custom strings). +Suggested file extension: **csv**. File can be processed sequentially - no random access is needed. +Each line forms a separate entry. +Each line consists of a set of values (also called columns), separated by comma `','` (hence "CSV" format - Comma Separated Values). +Number of columns is different in different lines. + +# Header + +First line identifies file format. It must always be: + + Vulkan Memory Allocator,Calls recording + +Second line identifies format version, where first column is major version and second column is minor version. +Formats with only minor version incremented are backward compatible. +VmaReplay application supports all older versions. +Current version is: + + 1,2 + +# Function calls + +Remaining lines contain recorded calls to VMA functions. First columns are always: + +- Thread ID : uint32 +- Time since first call : float, in seconds +- VMA frame index : uint32 +- Function name : string + +Remaining columns are function parameters and output, depending on function name, which can be: + +**vmaCreateAllocator, vmaDestroyAllocator** + +No parameters. + +**vmaCreatePool** + +- memoryTypeIndex : uint32 +- flags : uint32 +- blockSize : uint64 +- minBlockCount : uint64 +- maxBlockCount : uint64 +- frameInUseCount : uint32 +- pool (output) : pointer + +**vmaDestroyPool** + +- pool : pointer + +**vmaSetAllocationUserData** + +- allocation : pointer +- pUserData : string (may contain additional commas) + +**vmaCreateBuffer** + +- bufferCreateInfo.flags : uint32 +- bufferCreateInfo.size : uint64 +- bufferCreateInfo.usage : uint32 +- bufferCreateInfo.sharingMode : uint32 +- allocationCreateInfo.flags : uint32 +- allocationCreateInfo.usage : uint32 +- allocationCreateInfo.requiredFlags : uint32 +- allocationCreateInfo.preferredFlags : uint32 +- allocationCreateInfo.memoryTypeBits : uint32 +- allocationCreateInfo.pool : pointer +- allocation (output) : pointer +- allocationCreateInfo.pUserData : string (may contain additional commas) + +**vmaDestroyBuffer** + +- allocation : pointer + +**vmaCreateImage** + +- imageCreateInfo.flags : uint32 +- imageCreateInfo.imageType : uint32 +- imageCreateInfo.format : uint32 +- imageCreateInfo.extent.width : uint32 +- imageCreateInfo.extent.height : uint32 +- imageCreateInfo.extent.depth : uint32 +- imageCreateInfo.mipLevels : uint32 +- imageCreateInfo.arrayLayers : uint32 +- imageCreateInfo.samples : uint32 +- imageCreateInfo.tiling : uint32 +- imageCreateInfo.usage : uint32 +- imageCreateInfo.sharingMode : uint32 +- imageCreateInfo.initialLayout : uint32 +- allocationCreateInfo.flags : uint32 +- allocationCreateInfo.usage : uint32 +- allocationCreateInfo.requiredFlags : uint32 +- allocationCreateInfo.preferredFlags : uint32 +- allocationCreateInfo.memoryTypeBits : uint32 +- allocationCreateInfo.pool : pointer +- allocation (output) : pointer +- allocationCreateInfo.pUserData : string (may contain additional commas) + +**vmaDestroyImage** + +- allocation : pointer + +**vmaFreeMemory** (min format version 1.1) + +- allocation : pointer + +**vmaCreateLostAllocation** (min format version 1.2) + +- allocation (output) : pointer + +**vmaAllocateMemory** (min format version 1.2) + +- vkMemoryRequirements.size : uint64 +- vkMemoryRequirements.alignment : uint64 +- vkMemoryRequirements.memoryTypeBits : uint32 +- allocationCreateInfo.flags : uint32 +- allocationCreateInfo.usage : uint32 +- allocationCreateInfo.requiredFlags : uint32 +- allocationCreateInfo.preferredFlags : uint32 +- allocationCreateInfo.memoryTypeBits : uint32 +- allocationCreateInfo.pool : pointer +- allocation (output) : pointer +- allocationCreateInfo.pUserData : string (may contain additional commas) + +**vmaAllocateMemoryForBuffer, vmaAllocateMemoryForImage** (min format version 1.2) + +- vkMemoryRequirements.size : uint64 +- vkMemoryRequirements.alignment : uint64 +- vkMemoryRequirements.memoryTypeBits : uint32 +- requiresDedicatedAllocation : bool +- prefersDedicatedAllocation : bool +- allocationCreateInfo.flags : uint32 +- allocationCreateInfo.usage : uint32 +- allocationCreateInfo.requiredFlags : uint32 +- allocationCreateInfo.preferredFlags : uint32 +- allocationCreateInfo.memoryTypeBits : uint32 +- allocationCreateInfo.pool : pointer +- allocation (output) : pointer +- allocationCreateInfo.pUserData : string (may contain additional commas) + +**vmaMapMemory, vmaUnmapMemory** (min format version 1.2) + +- allocation : pointer + +**vmaFlushAllocation, vmaInvalidateAllocation** (min format version 1.2) + +- allocation : pointer +- offset : uint64 +- size : uint64 + +## Data types + +**bool** + +Encoded as `0` for false or `1` for true. + +**uint32, uint64** + +Encoded in decimal format. + +**pointer** + +Encoded in hexadecimal format. + +**pUserData** + +If `pUserData` was a pointer, it is encoded as hexadecimal string. +If `VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT` was used with the allocation, the string is written as-is. +It may contain additional commas. +It should not contain end-of-line characters - results are then undefined. diff --git a/docs/html/annotated.html b/docs/html/annotated.html index 18ddbd5..37ef74d 100644 --- a/docs/html/annotated.html +++ b/docs/html/annotated.html @@ -75,9 +75,10 @@ $(function() {  CVmaPoolRepresents custom memory pool  CVmaPoolCreateInfoDescribes parameter of created VmaPool  CVmaPoolStatsDescribes parameter of existing VmaPool - CVmaStatInfoCalculated statistics of memory usage in entire allocator - CVmaStatsGeneral statistics from current state of Allocator - CVmaVulkanFunctionsPointers to some Vulkan functions - a subset used by the library + CVmaRecordSettingsParameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings + CVmaStatInfoCalculated statistics of memory usage in entire allocator + CVmaStatsGeneral statistics from current state of Allocator + CVmaVulkanFunctionsPointers to some Vulkan functions - a subset used by the library diff --git a/docs/html/classes.html b/docs/html/classes.html index b900f2a..099f7e3 100644 --- a/docs/html/classes.html +++ b/docs/html/classes.html @@ -65,10 +65,10 @@ $(function() { - - - + + + +
  v  
-
VmaAllocationInfo   VmaDefragmentationStats   VmaPoolStats   
VmaAllocator   VmaDeviceMemoryCallbacks   VmaStatInfo   
VmaAllocation   VmaAllocatorCreateInfo   VmaPool   VmaStats   
VmaAllocationCreateInfo   VmaDefragmentationInfo   VmaPoolCreateInfo   VmaVulkanFunctions   
VmaAllocationInfo   VmaDefragmentationStats   VmaPoolStats   VmaVulkanFunctions   
VmaAllocator   VmaDeviceMemoryCallbacks   VmaRecordSettings   
VmaAllocation   VmaAllocatorCreateInfo   VmaPool   VmaStatInfo   
VmaAllocationCreateInfo   VmaDefragmentationInfo   VmaPoolCreateInfo   VmaStats   
diff --git a/docs/html/functions.html b/docs/html/functions.html index 0751d69..c8ce62d 100644 --- a/docs/html/functions.html +++ b/docs/html/functions.html @@ -114,6 +114,7 @@ $(function() { : VmaAllocationCreateInfo , VmaAllocatorCreateInfo , VmaPoolCreateInfo +, VmaRecordSettings
  • frameInUseCount : VmaAllocatorCreateInfo @@ -165,6 +166,9 @@ $(function() {
  • pDeviceMemoryCallbacks : VmaAllocatorCreateInfo
  • +
  • pFilePath +: VmaRecordSettings +
  • pfnAllocate : VmaDeviceMemoryCallbacks
  • @@ -183,6 +187,9 @@ $(function() {
  • pool : VmaAllocationCreateInfo
  • +
  • pRecordSettings +: VmaAllocatorCreateInfo +
  • preferredFlags : VmaAllocationCreateInfo
  • diff --git a/docs/html/functions_vars.html b/docs/html/functions_vars.html index 6ee6243..de9e9a0 100644 --- a/docs/html/functions_vars.html +++ b/docs/html/functions_vars.html @@ -114,6 +114,7 @@ $(function() { : VmaAllocationCreateInfo , VmaAllocatorCreateInfo , VmaPoolCreateInfo +, VmaRecordSettings
  • frameInUseCount : VmaAllocatorCreateInfo @@ -165,6 +166,9 @@ $(function() {
  • pDeviceMemoryCallbacks : VmaAllocatorCreateInfo
  • +
  • pFilePath +: VmaRecordSettings +
  • pfnAllocate : VmaDeviceMemoryCallbacks
  • @@ -183,6 +187,9 @@ $(function() {
  • pool : VmaAllocationCreateInfo
  • +
  • pRecordSettings +: VmaAllocatorCreateInfo +
  • preferredFlags : VmaAllocationCreateInfo
  • diff --git a/docs/html/general_considerations.html b/docs/html/general_considerations.html index 5d4362d..300ab1b 100644 --- a/docs/html/general_considerations.html +++ b/docs/html/general_considerations.html @@ -74,6 +74,23 @@ Thread safety
  • When the allocator is created with VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT flag, calls to functions that take such VmaAllocator object must be synchronized externally.
  • Access to a VmaAllocation object must be externally synchronized. For example, you must not call vmaGetAllocationInfo() and vmaMapMemory() from different threads at the same time if you pass the same VmaAllocation object to these functions.
  • +

    +Validation layer warnings

    +

    When using this library, you can meet following types of warnings issued by Vulkan validation layer. They don't necessarily indicate a bug, so you may need to just ignore them.

    +
      +
    • vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.
        +
      • It happens when VK_KHR_dedicated_allocation extension is enabled. vkGetBufferMemoryRequirements2KHR function is used instead, while validation layer seems to be unaware of it.
      • +
      +
    • +
    • Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.
        +
      • It happens when you map a buffer or image, because the library maps entire VkDeviceMemory block, where different types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
      • +
      +
    • +
    • Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.
        +
      • It happens when you use lost allocations, and a new image or buffer is created in place of an existing object that bacame lost.
      • +
      +
    • +

    Allocation algorithm

    The library uses following algorithm for allocation, in order:

    diff --git a/docs/html/globals.html b/docs/html/globals.html index 08a3076..2f1f2fb 100644 --- a/docs/html/globals.html +++ b/docs/html/globals.html @@ -128,6 +128,15 @@ $(function() {
  • VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT : vk_mem_alloc.h
  • +
  • VMA_RECORD_FLAG_BITS_MAX_ENUM +: vk_mem_alloc.h +
  • +
  • VMA_RECORD_FLUSH_AFTER_CALL_BIT +: vk_mem_alloc.h +
  • +
  • VMA_RECORDING_ENABLED +: vk_mem_alloc.h +
  • VMA_STATS_STRING_ENABLED : vk_mem_alloc.h
  • @@ -275,6 +284,15 @@ $(function() {
  • VmaPoolStats : vk_mem_alloc.h
  • +
  • VmaRecordFlagBits +: vk_mem_alloc.h +
  • +
  • VmaRecordFlags +: vk_mem_alloc.h +
  • +
  • VmaRecordSettings +: vk_mem_alloc.h +
  • vmaSetAllocationUserData() : vk_mem_alloc.h
  • diff --git a/docs/html/globals_defs.html b/docs/html/globals_defs.html index aa8115b..ca5de3f 100644 --- a/docs/html/globals_defs.html +++ b/docs/html/globals_defs.html @@ -62,6 +62,9 @@ $(function() {
  • VMA_DEDICATED_ALLOCATION : vk_mem_alloc.h
  • +
  • VMA_RECORDING_ENABLED +: vk_mem_alloc.h +
  • VMA_STATS_STRING_ENABLED : vk_mem_alloc.h
  • diff --git a/docs/html/globals_enum.html b/docs/html/globals_enum.html index 1df06f1..d295ccd 100644 --- a/docs/html/globals_enum.html +++ b/docs/html/globals_enum.html @@ -71,6 +71,9 @@ $(function() {
  • VmaPoolCreateFlagBits : vk_mem_alloc.h
  • +
  • VmaRecordFlagBits +: vk_mem_alloc.h +
  • diff --git a/docs/html/globals_eval.html b/docs/html/globals_eval.html index 2035414..091f76b 100644 --- a/docs/html/globals_eval.html +++ b/docs/html/globals_eval.html @@ -113,6 +113,12 @@ $(function() {
  • VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT : vk_mem_alloc.h
  • +
  • VMA_RECORD_FLAG_BITS_MAX_ENUM +: vk_mem_alloc.h +
  • +
  • VMA_RECORD_FLUSH_AFTER_CALL_BIT +: vk_mem_alloc.h +
  • diff --git a/docs/html/globals_type.html b/docs/html/globals_type.html index 0b3156b..68106a1 100644 --- a/docs/html/globals_type.html +++ b/docs/html/globals_type.html @@ -110,6 +110,15 @@ $(function() {
  • VmaPoolStats : vk_mem_alloc.h
  • +
  • VmaRecordFlagBits +: vk_mem_alloc.h +
  • +
  • VmaRecordFlags +: vk_mem_alloc.h +
  • +
  • VmaRecordSettings +: vk_mem_alloc.h +
  • VmaStatInfo : vk_mem_alloc.h
  • diff --git a/docs/html/memory_mapping.html b/docs/html/memory_mapping.html index fa97ad7..7e2c263 100644 --- a/docs/html/memory_mapping.html +++ b/docs/html/memory_mapping.html @@ -69,8 +69,11 @@ $(function() {

    To "map memory" in Vulkan means to obtain a CPU pointer to VkDeviceMemory, to be able to read from it or write to it in CPU code. Mapping is possible only of memory allocated from a memory type that has VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT flag. Functions vkMapMemory(), vkUnmapMemory() are designed for this purpose. You can use them directly with memory allocated by this library, but it is not recommended because of following issue: Mapping the same VkDeviceMemory block multiple times is illegal - only one mapping at a time is allowed. This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan. Because of this, Vulkan Memory Allocator provides following facilities:

    Mapping functions

    -

    The library provides following functions for mapping of a specific VmaAllocation: vmaMapMemory(), vmaUnmapMemory(). They are safer and more convenient to use than standard Vulkan functions. You can map an allocation multiple times simultaneously - mapping is reference-counted internally. You can also map different allocations simultaneously regardless of whether they use the same VkDeviceMemory block. They way it's implemented is that the library always maps entire memory block, not just region of the allocation. For further details, see description of vmaMapMemory() function. Example:

    -
    // Having these objects initialized:
    struct ConstantBuffer
    {
    ...
    };
    ConstantBuffer constantBufferData;
    VmaAllocator allocator;
    VmaBuffer constantBuffer;
    VmaAllocation constantBufferAllocation;
    // You can map and fill your buffer using following code:
    void* mappedData;
    vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
    memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
    vmaUnmapMemory(allocator, constantBufferAllocation);

    +

    The library provides following functions for mapping of a specific VmaAllocation: vmaMapMemory(), vmaUnmapMemory(). They are safer and more convenient to use than standard Vulkan functions. You can map an allocation multiple times simultaneously - mapping is reference-counted internally. You can also map different allocations simultaneously regardless of whether they use the same VkDeviceMemory block. The way it's implemented is that the library always maps entire memory block, not just region of the allocation. For further details, see description of vmaMapMemory() function. Example:

    +
    // Having these objects initialized:
    struct ConstantBuffer
    {
    ...
    };
    ConstantBuffer constantBufferData;
    VmaAllocator allocator;
    VkBuffer constantBuffer;
    VmaAllocation constantBufferAllocation;
    // You can map and fill your buffer using following code:
    void* mappedData;
    vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
    memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
    vmaUnmapMemory(allocator, constantBufferAllocation);

    When mapping, you may see a warning from Vulkan validation layer similar to this one:

    +

    Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.

    +

    It happens because the library maps entire VkDeviceMemory block, where different types of images and buffers may end up together, especially on GPUs with unified memory like Intel. You can safely ignore it if you are sure you access only memory of the intended object that you wanted to map.

    +

    Persistently mapped memory

    Kepping your memory persistently mapped is generally OK in Vulkan. You don't need to unmap it before using its data on the GPU. The library provides a special feature designed for that: Allocations made with VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in VmaAllocationCreateInfo::flags stay mapped all the time, so you can just access CPU pointer to it any time without a need to call any "map" or "unmap" function. Example:

    VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
    bufCreateInfo.size = sizeof(ConstantBuffer);
    bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
    VmaAllocationCreateInfo allocCreateInfo = {};
    allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
    VkBuffer buf;
    vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
    // Buffer is already mapped. You can access its memory.
    memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));

    There are some exceptions though, when you should consider mapping memory only for a short period of time:

    diff --git a/docs/html/search/all_4.js b/docs/html/search/all_4.js index 7bacfcc..347c9f3 100644 --- a/docs/html/search/all_4.js +++ b/docs/html/search/all_4.js @@ -1,5 +1,5 @@ var searchData= [ - ['flags',['flags',['../struct_vma_allocator_create_info.html#a392ea2ecbaff93f91a7c49f735ad4346',1,'VmaAllocatorCreateInfo::flags()'],['../struct_vma_allocation_create_info.html#add09658ac14fe290ace25470ddd6d41b',1,'VmaAllocationCreateInfo::flags()'],['../struct_vma_pool_create_info.html#a8405139f63d078340ae74513a59f5446',1,'VmaPoolCreateInfo::flags()']]], + ['flags',['flags',['../struct_vma_record_settings.html#ad8fdcc92119ae7a8c08c1a564c01d63a',1,'VmaRecordSettings::flags()'],['../struct_vma_allocator_create_info.html#a392ea2ecbaff93f91a7c49f735ad4346',1,'VmaAllocatorCreateInfo::flags()'],['../struct_vma_allocation_create_info.html#add09658ac14fe290ace25470ddd6d41b',1,'VmaAllocationCreateInfo::flags()'],['../struct_vma_pool_create_info.html#a8405139f63d078340ae74513a59f5446',1,'VmaPoolCreateInfo::flags()']]], ['frameinusecount',['frameInUseCount',['../struct_vma_allocator_create_info.html#a21ea188dd212b8171cb9ecbed4a2a3a7',1,'VmaAllocatorCreateInfo::frameInUseCount()'],['../struct_vma_pool_create_info.html#a9437e43ffbb644dbbf7fc4e50cfad6aa',1,'VmaPoolCreateInfo::frameInUseCount()']]] ]; diff --git a/docs/html/search/all_9.js b/docs/html/search/all_9.js index 590465a..33e01da 100644 --- a/docs/html/search/all_9.js +++ b/docs/html/search/all_9.js @@ -2,6 +2,7 @@ var searchData= [ ['pallocationcallbacks',['pAllocationCallbacks',['../struct_vma_allocator_create_info.html#a6e409087e3be55400d0e4ccbe43c608d',1,'VmaAllocatorCreateInfo']]], ['pdevicememorycallbacks',['pDeviceMemoryCallbacks',['../struct_vma_allocator_create_info.html#af1380969b5e1ea4c3184a877892d260e',1,'VmaAllocatorCreateInfo']]], + ['pfilepath',['pFilePath',['../struct_vma_record_settings.html#a6cb1fdbf6bcb610b68f2010dd629e89d',1,'VmaRecordSettings']]], ['pfn_5fvmaallocatedevicememoryfunction',['PFN_vmaAllocateDeviceMemoryFunction',['../vk__mem__alloc_8h.html#ab6a6477cda1ce775b30bde96d766203b',1,'vk_mem_alloc.h']]], ['pfn_5fvmafreedevicememoryfunction',['PFN_vmaFreeDeviceMemoryFunction',['../vk__mem__alloc_8h.html#aef2545dc2e9dd4f29ab9ba6ac6fe2f49',1,'vk_mem_alloc.h']]], ['pfnallocate',['pfnAllocate',['../struct_vma_device_memory_callbacks.html#a4f17f7b255101e733b44d5633aceabfb',1,'VmaDeviceMemoryCallbacks']]], @@ -10,6 +11,7 @@ var searchData= ['physicaldevice',['physicalDevice',['../struct_vma_allocator_create_info.html#a08230f04ae6ccf8a78150a9e829a7156',1,'VmaAllocatorCreateInfo']]], ['pmappeddata',['pMappedData',['../struct_vma_allocation_info.html#a5eeffbe2d2f30f53370ff14aefbadbe2',1,'VmaAllocationInfo']]], ['pool',['pool',['../struct_vma_allocation_create_info.html#a6272c0555cfd1fe28bff1afeb6190150',1,'VmaAllocationCreateInfo']]], + ['precordsettings',['pRecordSettings',['../struct_vma_allocator_create_info.html#ace2aa4877b16a42b0b7673d4e26000ee',1,'VmaAllocatorCreateInfo']]], ['preferredflags',['preferredFlags',['../struct_vma_allocation_create_info.html#a7fe8d81a1ad10b2a2faacacee5b15d6d',1,'VmaAllocationCreateInfo']]], ['preferredlargeheapblocksize',['preferredLargeHeapBlockSize',['../struct_vma_allocator_create_info.html#a8e4714298e3121cdd8b214a1ae7a637a',1,'VmaAllocatorCreateInfo']]], ['puserdata',['pUserData',['../struct_vma_allocation_create_info.html#a8259e85c272683434f4abb4ddddffe19',1,'VmaAllocationCreateInfo::pUserData()'],['../struct_vma_allocation_info.html#adc507656149c04de7ed95d0042ba2a13',1,'VmaAllocationInfo::pUserData()']]], diff --git a/docs/html/search/all_f.js b/docs/html/search/all_f.js index a80478e..60dbc10 100644 --- a/docs/html/search/all_f.js +++ b/docs/html/search/all_f.js @@ -38,6 +38,9 @@ var searchData= ['vma_5fmemory_5fusage_5funknown',['VMA_MEMORY_USAGE_UNKNOWN',['../vk__mem__alloc_8h.html#aa5846affa1e9da3800e3e78fae2305ccaf50d27e34e0925cf3a63db8c839121dd',1,'vk_mem_alloc.h']]], ['vma_5fpool_5fcreate_5fflag_5fbits_5fmax_5fenum',['VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM',['../vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a1c7312bea9ea246846b9054fd6bd6aec',1,'vk_mem_alloc.h']]], ['vma_5fpool_5fcreate_5fignore_5fbuffer_5fimage_5fgranularity_5fbit',['VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT',['../vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a9f1a499508a8edb4e8ba40aa0290a3d2',1,'vk_mem_alloc.h']]], + ['vma_5frecord_5fflag_5fbits_5fmax_5fenum',['VMA_RECORD_FLAG_BITS_MAX_ENUM',['../vk__mem__alloc_8h.html#a4dd2c44642312a147a4e93373a6e64d2a20dd17d69966dbffa054739d6090b85e',1,'vk_mem_alloc.h']]], + ['vma_5frecord_5fflush_5fafter_5fcall_5fbit',['VMA_RECORD_FLUSH_AFTER_CALL_BIT',['../vk__mem__alloc_8h.html#a4dd2c44642312a147a4e93373a6e64d2a8e7ab322e8732654be627c4ea8f36cc7',1,'vk_mem_alloc.h']]], + ['vma_5frecording_5fenabled',['VMA_RECORDING_ENABLED',['../vk__mem__alloc_8h.html#a1f0c126759fc96ccb6e2d23c101d770c',1,'vk_mem_alloc.h']]], ['vma_5fstats_5fstring_5fenabled',['VMA_STATS_STRING_ENABLED',['../vk__mem__alloc_8h.html#ae25f0d55fd91cb166f002b63244800e1',1,'vk_mem_alloc.h']]], ['vmaallocatememory',['vmaAllocateMemory',['../vk__mem__alloc_8h.html#abf28077dbf82d0908b8acbe8ee8dd9b8',1,'vk_mem_alloc.h']]], ['vmaallocatememoryforbuffer',['vmaAllocateMemoryForBuffer',['../vk__mem__alloc_8h.html#a7fdf64415b6c3d83c454f28d2c53df7b',1,'vk_mem_alloc.h']]], @@ -90,6 +93,9 @@ var searchData= ['vmapoolcreateflags',['VmaPoolCreateFlags',['../vk__mem__alloc_8h.html#a2770e325ea42e087c1b91fdf46d0292a',1,'vk_mem_alloc.h']]], ['vmapoolcreateinfo',['VmaPoolCreateInfo',['../struct_vma_pool_create_info.html',1,'VmaPoolCreateInfo'],['../vk__mem__alloc_8h.html#a211706e9348dcee25a843ed4ea69bce7',1,'VmaPoolCreateInfo(): vk_mem_alloc.h']]], ['vmapoolstats',['VmaPoolStats',['../struct_vma_pool_stats.html',1,'VmaPoolStats'],['../vk__mem__alloc_8h.html#a2e5612d871d64c5624087b837a338c34',1,'VmaPoolStats(): vk_mem_alloc.h']]], + ['vmarecordflagbits',['VmaRecordFlagBits',['../vk__mem__alloc_8h.html#a4dd2c44642312a147a4e93373a6e64d2',1,'VmaRecordFlagBits(): vk_mem_alloc.h'],['../vk__mem__alloc_8h.html#ade20b626a6635ce1bf30ea53dea774e4',1,'VmaRecordFlagBits(): vk_mem_alloc.h']]], + ['vmarecordflags',['VmaRecordFlags',['../vk__mem__alloc_8h.html#af3929a1a4547c592fc0b0e55ef452828',1,'vk_mem_alloc.h']]], + ['vmarecordsettings',['VmaRecordSettings',['../struct_vma_record_settings.html',1,'VmaRecordSettings'],['../vk__mem__alloc_8h.html#a0ab61e87ff6365f1d59915eadc37a9f0',1,'VmaRecordSettings(): vk_mem_alloc.h']]], ['vmasetallocationuserdata',['vmaSetAllocationUserData',['../vk__mem__alloc_8h.html#af9147d31ffc11d62fc187bde283ed14f',1,'vk_mem_alloc.h']]], ['vmasetcurrentframeindex',['vmaSetCurrentFrameIndex',['../vk__mem__alloc_8h.html#ade56bf8dc9f5a5eaddf5f119ed525236',1,'vk_mem_alloc.h']]], ['vmastatinfo',['VmaStatInfo',['../struct_vma_stat_info.html',1,'VmaStatInfo'],['../vk__mem__alloc_8h.html#a810b009a788ee8aac72a25b42ffbe31c',1,'VmaStatInfo(): vk_mem_alloc.h']]], diff --git a/docs/html/search/classes_0.js b/docs/html/search/classes_0.js index 152fb2b..7fe2da1 100644 --- a/docs/html/search/classes_0.js +++ b/docs/html/search/classes_0.js @@ -11,6 +11,7 @@ var searchData= ['vmapool',['VmaPool',['../struct_vma_pool.html',1,'']]], ['vmapoolcreateinfo',['VmaPoolCreateInfo',['../struct_vma_pool_create_info.html',1,'']]], ['vmapoolstats',['VmaPoolStats',['../struct_vma_pool_stats.html',1,'']]], + ['vmarecordsettings',['VmaRecordSettings',['../struct_vma_record_settings.html',1,'']]], ['vmastatinfo',['VmaStatInfo',['../struct_vma_stat_info.html',1,'']]], ['vmastats',['VmaStats',['../struct_vma_stats.html',1,'']]], ['vmavulkanfunctions',['VmaVulkanFunctions',['../struct_vma_vulkan_functions.html',1,'']]] diff --git a/docs/html/search/defines_0.js b/docs/html/search/defines_0.js index 336effe..bf18592 100644 --- a/docs/html/search/defines_0.js +++ b/docs/html/search/defines_0.js @@ -1,5 +1,6 @@ var searchData= [ ['vma_5fdedicated_5fallocation',['VMA_DEDICATED_ALLOCATION',['../vk__mem__alloc_8h.html#af7b860e63b96d11e44ae8587ba06bbf4',1,'vk_mem_alloc.h']]], + ['vma_5frecording_5fenabled',['VMA_RECORDING_ENABLED',['../vk__mem__alloc_8h.html#a1f0c126759fc96ccb6e2d23c101d770c',1,'vk_mem_alloc.h']]], ['vma_5fstats_5fstring_5fenabled',['VMA_STATS_STRING_ENABLED',['../vk__mem__alloc_8h.html#ae25f0d55fd91cb166f002b63244800e1',1,'vk_mem_alloc.h']]] ]; diff --git a/docs/html/search/enums_0.js b/docs/html/search/enums_0.js index 9bd6e39..c5ed539 100644 --- a/docs/html/search/enums_0.js +++ b/docs/html/search/enums_0.js @@ -3,5 +3,6 @@ var searchData= ['vmaallocationcreateflagbits',['VmaAllocationCreateFlagBits',['../vk__mem__alloc_8h.html#ad9889c10c798b040d59c92f257cae597',1,'vk_mem_alloc.h']]], ['vmaallocatorcreateflagbits',['VmaAllocatorCreateFlagBits',['../vk__mem__alloc_8h.html#a4f87c9100d154a65a4ad495f7763cf7c',1,'vk_mem_alloc.h']]], ['vmamemoryusage',['VmaMemoryUsage',['../vk__mem__alloc_8h.html#aa5846affa1e9da3800e3e78fae2305cc',1,'vk_mem_alloc.h']]], - ['vmapoolcreateflagbits',['VmaPoolCreateFlagBits',['../vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7',1,'vk_mem_alloc.h']]] + ['vmapoolcreateflagbits',['VmaPoolCreateFlagBits',['../vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7',1,'vk_mem_alloc.h']]], + ['vmarecordflagbits',['VmaRecordFlagBits',['../vk__mem__alloc_8h.html#a4dd2c44642312a147a4e93373a6e64d2',1,'vk_mem_alloc.h']]] ]; diff --git a/docs/html/search/enumvalues_0.js b/docs/html/search/enumvalues_0.js index ea47966..9f462fc 100644 --- a/docs/html/search/enumvalues_0.js +++ b/docs/html/search/enumvalues_0.js @@ -17,5 +17,7 @@ var searchData= ['vma_5fmemory_5fusage_5fmax_5fenum',['VMA_MEMORY_USAGE_MAX_ENUM',['../vk__mem__alloc_8h.html#aa5846affa1e9da3800e3e78fae2305cca091e69437ef693e8d0d287f1c719ba6e',1,'vk_mem_alloc.h']]], ['vma_5fmemory_5fusage_5funknown',['VMA_MEMORY_USAGE_UNKNOWN',['../vk__mem__alloc_8h.html#aa5846affa1e9da3800e3e78fae2305ccaf50d27e34e0925cf3a63db8c839121dd',1,'vk_mem_alloc.h']]], ['vma_5fpool_5fcreate_5fflag_5fbits_5fmax_5fenum',['VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM',['../vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a1c7312bea9ea246846b9054fd6bd6aec',1,'vk_mem_alloc.h']]], - ['vma_5fpool_5fcreate_5fignore_5fbuffer_5fimage_5fgranularity_5fbit',['VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT',['../vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a9f1a499508a8edb4e8ba40aa0290a3d2',1,'vk_mem_alloc.h']]] + ['vma_5fpool_5fcreate_5fignore_5fbuffer_5fimage_5fgranularity_5fbit',['VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT',['../vk__mem__alloc_8h.html#a9a7c45f9c863695d98c83fa5ac940fe7a9f1a499508a8edb4e8ba40aa0290a3d2',1,'vk_mem_alloc.h']]], + ['vma_5frecord_5fflag_5fbits_5fmax_5fenum',['VMA_RECORD_FLAG_BITS_MAX_ENUM',['../vk__mem__alloc_8h.html#a4dd2c44642312a147a4e93373a6e64d2a20dd17d69966dbffa054739d6090b85e',1,'vk_mem_alloc.h']]], + ['vma_5frecord_5fflush_5fafter_5fcall_5fbit',['VMA_RECORD_FLUSH_AFTER_CALL_BIT',['../vk__mem__alloc_8h.html#a4dd2c44642312a147a4e93373a6e64d2a8e7ab322e8732654be627c4ea8f36cc7',1,'vk_mem_alloc.h']]] ]; diff --git a/docs/html/search/typedefs_1.js b/docs/html/search/typedefs_1.js index 716f5e3..5d53a90 100644 --- a/docs/html/search/typedefs_1.js +++ b/docs/html/search/typedefs_1.js @@ -15,6 +15,9 @@ var searchData= ['vmapoolcreateflags',['VmaPoolCreateFlags',['../vk__mem__alloc_8h.html#a2770e325ea42e087c1b91fdf46d0292a',1,'vk_mem_alloc.h']]], ['vmapoolcreateinfo',['VmaPoolCreateInfo',['../vk__mem__alloc_8h.html#a211706e9348dcee25a843ed4ea69bce7',1,'vk_mem_alloc.h']]], ['vmapoolstats',['VmaPoolStats',['../vk__mem__alloc_8h.html#a2e5612d871d64c5624087b837a338c34',1,'vk_mem_alloc.h']]], + ['vmarecordflagbits',['VmaRecordFlagBits',['../vk__mem__alloc_8h.html#ade20b626a6635ce1bf30ea53dea774e4',1,'vk_mem_alloc.h']]], + ['vmarecordflags',['VmaRecordFlags',['../vk__mem__alloc_8h.html#af3929a1a4547c592fc0b0e55ef452828',1,'vk_mem_alloc.h']]], + ['vmarecordsettings',['VmaRecordSettings',['../vk__mem__alloc_8h.html#a0ab61e87ff6365f1d59915eadc37a9f0',1,'vk_mem_alloc.h']]], ['vmastatinfo',['VmaStatInfo',['../vk__mem__alloc_8h.html#a810b009a788ee8aac72a25b42ffbe31c',1,'vk_mem_alloc.h']]], ['vmastats',['VmaStats',['../vk__mem__alloc_8h.html#a732be855fb4a7c248e6853d928a729af',1,'vk_mem_alloc.h']]], ['vmavulkanfunctions',['VmaVulkanFunctions',['../vk__mem__alloc_8h.html#a97064a1a271b0061ebfc3a079862d0c5',1,'vk_mem_alloc.h']]] diff --git a/docs/html/search/variables_3.js b/docs/html/search/variables_3.js index 7bacfcc..347c9f3 100644 --- a/docs/html/search/variables_3.js +++ b/docs/html/search/variables_3.js @@ -1,5 +1,5 @@ var searchData= [ - ['flags',['flags',['../struct_vma_allocator_create_info.html#a392ea2ecbaff93f91a7c49f735ad4346',1,'VmaAllocatorCreateInfo::flags()'],['../struct_vma_allocation_create_info.html#add09658ac14fe290ace25470ddd6d41b',1,'VmaAllocationCreateInfo::flags()'],['../struct_vma_pool_create_info.html#a8405139f63d078340ae74513a59f5446',1,'VmaPoolCreateInfo::flags()']]], + ['flags',['flags',['../struct_vma_record_settings.html#ad8fdcc92119ae7a8c08c1a564c01d63a',1,'VmaRecordSettings::flags()'],['../struct_vma_allocator_create_info.html#a392ea2ecbaff93f91a7c49f735ad4346',1,'VmaAllocatorCreateInfo::flags()'],['../struct_vma_allocation_create_info.html#add09658ac14fe290ace25470ddd6d41b',1,'VmaAllocationCreateInfo::flags()'],['../struct_vma_pool_create_info.html#a8405139f63d078340ae74513a59f5446',1,'VmaPoolCreateInfo::flags()']]], ['frameinusecount',['frameInUseCount',['../struct_vma_allocator_create_info.html#a21ea188dd212b8171cb9ecbed4a2a3a7',1,'VmaAllocatorCreateInfo::frameInUseCount()'],['../struct_vma_pool_create_info.html#a9437e43ffbb644dbbf7fc4e50cfad6aa',1,'VmaPoolCreateInfo::frameInUseCount()']]] ]; diff --git a/docs/html/search/variables_6.js b/docs/html/search/variables_6.js index 731ce38..46cab85 100644 --- a/docs/html/search/variables_6.js +++ b/docs/html/search/variables_6.js @@ -2,12 +2,14 @@ var searchData= [ ['pallocationcallbacks',['pAllocationCallbacks',['../struct_vma_allocator_create_info.html#a6e409087e3be55400d0e4ccbe43c608d',1,'VmaAllocatorCreateInfo']]], ['pdevicememorycallbacks',['pDeviceMemoryCallbacks',['../struct_vma_allocator_create_info.html#af1380969b5e1ea4c3184a877892d260e',1,'VmaAllocatorCreateInfo']]], + ['pfilepath',['pFilePath',['../struct_vma_record_settings.html#a6cb1fdbf6bcb610b68f2010dd629e89d',1,'VmaRecordSettings']]], ['pfnallocate',['pfnAllocate',['../struct_vma_device_memory_callbacks.html#a4f17f7b255101e733b44d5633aceabfb',1,'VmaDeviceMemoryCallbacks']]], ['pfnfree',['pfnFree',['../struct_vma_device_memory_callbacks.html#abe8a3328bbc916f6f712fdb6b299444c',1,'VmaDeviceMemoryCallbacks']]], ['pheapsizelimit',['pHeapSizeLimit',['../struct_vma_allocator_create_info.html#a31c192aa6cbffa33279f6d9f0c47c44b',1,'VmaAllocatorCreateInfo']]], ['physicaldevice',['physicalDevice',['../struct_vma_allocator_create_info.html#a08230f04ae6ccf8a78150a9e829a7156',1,'VmaAllocatorCreateInfo']]], ['pmappeddata',['pMappedData',['../struct_vma_allocation_info.html#a5eeffbe2d2f30f53370ff14aefbadbe2',1,'VmaAllocationInfo']]], ['pool',['pool',['../struct_vma_allocation_create_info.html#a6272c0555cfd1fe28bff1afeb6190150',1,'VmaAllocationCreateInfo']]], + ['precordsettings',['pRecordSettings',['../struct_vma_allocator_create_info.html#ace2aa4877b16a42b0b7673d4e26000ee',1,'VmaAllocatorCreateInfo']]], ['preferredflags',['preferredFlags',['../struct_vma_allocation_create_info.html#a7fe8d81a1ad10b2a2faacacee5b15d6d',1,'VmaAllocationCreateInfo']]], ['preferredlargeheapblocksize',['preferredLargeHeapBlockSize',['../struct_vma_allocator_create_info.html#a8e4714298e3121cdd8b214a1ae7a637a',1,'VmaAllocatorCreateInfo']]], ['puserdata',['pUserData',['../struct_vma_allocation_create_info.html#a8259e85c272683434f4abb4ddddffe19',1,'VmaAllocationCreateInfo::pUserData()'],['../struct_vma_allocation_info.html#adc507656149c04de7ed95d0042ba2a13',1,'VmaAllocationInfo::pUserData()']]], diff --git a/docs/html/struct_vma_allocator_create_info-members.html b/docs/html/struct_vma_allocator_create_info-members.html index 9340920..598ecde 100644 --- a/docs/html/struct_vma_allocator_create_info-members.html +++ b/docs/html/struct_vma_allocator_create_info-members.html @@ -72,8 +72,9 @@ $(function() { pDeviceMemoryCallbacksVmaAllocatorCreateInfo pHeapSizeLimitVmaAllocatorCreateInfo physicalDeviceVmaAllocatorCreateInfo - preferredLargeHeapBlockSizeVmaAllocatorCreateInfo - pVulkanFunctionsVmaAllocatorCreateInfo + pRecordSettingsVmaAllocatorCreateInfo + preferredLargeHeapBlockSizeVmaAllocatorCreateInfo + pVulkanFunctionsVmaAllocatorCreateInfo