From 4935511bc3a6704e040bcfddd1f80d1e7debf38d Mon Sep 17 00:00:00 2001 From: Georg Hagen Date: Tue, 17 Sep 2024 16:42:49 +0200 Subject: [PATCH 01/11] Remove opencv file --- 3rdParty/opencv/CMakeLists.txt | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 3rdParty/opencv/CMakeLists.txt diff --git a/3rdParty/opencv/CMakeLists.txt b/3rdParty/opencv/CMakeLists.txt deleted file mode 100644 index ca8c7c8..0000000 --- a/3rdParty/opencv/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -Find_Package(OpenCV) -include(FetchContent) - -if(NOT DEFINED OPENCV_REPO) - set(OPENCV_REPO https://github.com/opencv/opencv.git) -endif () - -if (NOT OpenCV_FOUND) - FetchContent_Declare( - opencv - EXCLUDE_FROM_ALL - GIT_REPOSITORY ${OPENCV_REPO} - GIT_TAG v4.8.1 - GIT_SHALLOW TRUE - ) - FetchContent_MakeAvailable(opencv) -endif() \ No newline at end of file From 36d1dc2bcab2fbbbf3e66d8e6552a5614ea46ee8 Mon Sep 17 00:00:00 2001 From: Georg Hagen Date: Tue, 17 Sep 2024 16:43:22 +0200 Subject: [PATCH 02/11] Make tracy optional --- openVulkanoCpp/Host/GraphicsAppManager.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/openVulkanoCpp/Host/GraphicsAppManager.cpp b/openVulkanoCpp/Host/GraphicsAppManager.cpp index f9cd1bb..cbefc3a 100644 --- a/openVulkanoCpp/Host/GraphicsAppManager.cpp +++ b/openVulkanoCpp/Host/GraphicsAppManager.cpp @@ -15,7 +15,10 @@ #include #include -#include +#if __has_include("tracy/Tracy.hpp") + #include + #define HAS_TRACY +#endif namespace OpenVulkano { @@ -23,7 +26,10 @@ namespace OpenVulkano : app(app), renderApi(renderApi) { Utils::SetThreadName("Main"); +#ifdef HAS_TRACY ZoneScoped; +#endif + Logger::SetupLogger(); if (!app) @@ -43,7 +49,9 @@ namespace OpenVulkano : app(app), renderApi(renderApi) { Utils::SetThreadName("Main"); +#ifdef HAS_TRACY ZoneScoped; +#endif Logger::SetupLogger(); if (!app) @@ -96,7 +104,9 @@ namespace OpenVulkano void GraphicsAppManager::StartUp() { +#ifdef HAS_TRACY ZoneScoped; +#endif try { Logger::MANAGER->info("Initializing ..."); @@ -129,8 +139,11 @@ namespace OpenVulkano void GraphicsAppManager::LoopTick() { +#ifdef HAS_TRACY FrameMark; ZoneScoped; +#endif + if (platform) platform->Tick(); if (paused) { // The rendering is paused @@ -152,7 +165,9 @@ namespace OpenVulkano void GraphicsAppManager::ShutDown() { +#ifdef HAS_TRACY ZoneScoped; +#endif Logger::MANAGER->info("Shutting down ..."); app->Close(); renderer->Close(); From ba7e2c2611ac1bad4e4a716330b2df540d3e33de Mon Sep 17 00:00:00 2001 From: Georg Hagen Date: Tue, 17 Sep 2024 16:43:58 +0200 Subject: [PATCH 03/11] Disable moving camera in arcbal if using ortho cam --- openVulkanoCpp/Controller/ArcballCameraController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openVulkanoCpp/Controller/ArcballCameraController.cpp b/openVulkanoCpp/Controller/ArcballCameraController.cpp index 3fb67b9..212e406 100644 --- a/openVulkanoCpp/Controller/ArcballCameraController.cpp +++ b/openVulkanoCpp/Controller/ArcballCameraController.cpp @@ -108,7 +108,7 @@ namespace OpenVulkano if (GetCamera()->IsOrtho()) { HandleMovementOrtho(); - if (GetCamera()->GetZoom() > 50) + /*if (GetCamera()->GetZoom() > 50) { float nDist = std::max(((ZOOM_RANGE - sqrt(GetCamera()->GetZoom())) / ZOOM_RANGE) * m_distance, 0.1f); if (nDist != m_frameDistance) @@ -116,7 +116,7 @@ namespace OpenVulkano m_distUpated = true; m_frameDistance = nDist; } - } + }*/ } else { From b27764b3db6713e3c7164784b23f0541b6b40cfc Mon Sep 17 00:00:00 2001 From: Georg Hagen Date: Tue, 17 Sep 2024 16:44:10 +0200 Subject: [PATCH 04/11] Fix random crashes --- openVulkanoCpp/Base/EngineConfiguration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openVulkanoCpp/Base/EngineConfiguration.cpp b/openVulkanoCpp/Base/EngineConfiguration.cpp index bfc1340..f8af39d 100644 --- a/openVulkanoCpp/Base/EngineConfiguration.cpp +++ b/openVulkanoCpp/Base/EngineConfiguration.cpp @@ -29,7 +29,7 @@ namespace OpenVulkano EngineConfiguration::EngineConfiguration() { const std::string filePath = (AppFolders::GetAppConfigHomeDir() / "EngineConfig.yml").string(); - Array fileContents = Utils::ReadFile(filePath, true); + Array fileContents = Utils::ReadFile(filePath, true, true); if(fileContents.Size() == 0) return; From 323e0feb4dd2424be85e52ba620f44e8f15a3549 Mon Sep 17 00:00:00 2001 From: Georg Hagen Date: Tue, 17 Sep 2024 16:44:21 +0200 Subject: [PATCH 05/11] Fix memory leak --- openVulkanoCpp/AR/ArRecorder.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/openVulkanoCpp/AR/ArRecorder.cpp b/openVulkanoCpp/AR/ArRecorder.cpp index 7c1a0a5..5d42e6c 100644 --- a/openVulkanoCpp/AR/ArRecorder.cpp +++ b/openVulkanoCpp/AR/ArRecorder.cpp @@ -109,6 +109,7 @@ namespace OpenVulkano::AR } } tjFree(outBuffer); + tjDestroy(handle); #endif } From 53ccf4e0f1d8808fd17f53291486f3d46d82f7c6 Mon Sep 17 00:00:00 2001 From: Georg Hagen Date: Tue, 17 Sep 2024 16:45:52 +0200 Subject: [PATCH 06/11] Fix issues with image loader jpeg --- openVulkanoCpp/Image/ImageLoaderJpeg.cpp | 32 ++++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/openVulkanoCpp/Image/ImageLoaderJpeg.cpp b/openVulkanoCpp/Image/ImageLoaderJpeg.cpp index f9daec4..3d1275b 100644 --- a/openVulkanoCpp/Image/ImageLoaderJpeg.cpp +++ b/openVulkanoCpp/Image/ImageLoaderJpeg.cpp @@ -5,7 +5,9 @@ */ #include "ImageLoaderJpeg.hpp" -#include +#include "Base/Utils.hpp" +#include "Base/Logger.hpp" +#include "Data/Containers/Array.hpp" #include #include @@ -20,10 +22,8 @@ namespace OpenVulkano::Image { std::unique_ptr ImageLoaderJpeg::loadFromFile(const std::string& filePath) { - std::ifstream file(filePath, std::ios::binary); - if (!file) { throw std::runtime_error("Could not open file: " + filePath); } - std::vector buffer((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - return loadJpeg(buffer.data(), buffer.size()); + const auto file = Utils::ReadFile(filePath); + return loadJpeg(reinterpret_cast(file.Data()), file.Size()); } std::unique_ptr ImageLoaderJpeg::loadFromMemory(const std::vector& buffer) @@ -35,21 +35,31 @@ namespace OpenVulkano::Image { Image result; - int rows, cols; + int rows = 0, cols = 0; #if __has_include("turbojpeg.h") { unsigned char* compressedImage = const_cast(data); - int jpegSubsamp; + int jpegSubsamp = 0; tjhandle jpegDecompressor = tjInitDecompress(); - tjDecompressHeader2(jpegDecompressor, compressedImage, size, &cols, &rows, &jpegSubsamp); + if (!jpegDecompressor) + { + Logger::FILESYS->error("Failed to read jpeg header. Error: {}", tjGetErrorStr()); + return nullptr; + } + int status = tjDecompressHeader2(jpegDecompressor, compressedImage, size, &cols, &rows, &jpegSubsamp); + if (status != 0) + { + Logger::FILESYS->error("Failed to read jpeg header. Error: {}", tjGetErrorStr()); + return nullptr; + } const int channels = 4; result.data = OpenVulkano::Array(cols * rows * channels); - result.dataFormat = OpenVulkano::DataFormat::R8G8B8A8_UINT; + result.dataFormat = OpenVulkano::DataFormat::B8G8R8A8_UINT; tjDecompress2(jpegDecompressor, compressedImage, size, result.data.Data(), cols, 0 /*pitch*/, rows, - TJPF_RGBA, TJFLAG_FASTDCT); + TJPF_BGRA, TJFLAG_FASTDCT); tjDestroy(jpegDecompressor); } #else @@ -69,4 +79,4 @@ namespace OpenVulkano::Image return std::make_unique(std::move(result)); } -} \ No newline at end of file +} From d744d5fdd7fdc99b3e76f9cc75cbc46b8e328a61 Mon Sep 17 00:00:00 2001 From: Georg Hagen Date: Wed, 18 Sep 2024 22:16:41 +0200 Subject: [PATCH 07/11] Fix to string --- openVulkanoCpp/Math/DenseVector3i.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/openVulkanoCpp/Math/DenseVector3i.hpp b/openVulkanoCpp/Math/DenseVector3i.hpp index 6e41b50..35d06bd 100644 --- a/openVulkanoCpp/Math/DenseVector3i.hpp +++ b/openVulkanoCpp/Math/DenseVector3i.hpp @@ -7,6 +7,7 @@ #pragma once #include "Math.hpp" +#include #include #include #include @@ -109,9 +110,14 @@ namespace OpenVulkano::Math data = (data & !(BITMASK << BITS)) | ((z & BITMASK) << BITS); } - [[nodiscard]] std::string ToString(const std::string& separator = ", ") const + [[nodiscard]] std::string ToString(const std::string& separator) const { - return std::to_string(X()) + "," + std::to_string(Y()) + "," + std::to_string(Z()); + return std::to_string(X()) + separator + std::to_string(Y()) + separator + std::to_string(Z()); + } + + [[nodiscard]] std::string ToString() const + { + return fmt::format("{},{},{}", X(), Y(), Z()); } [[nodiscard]] constexpr operator T() const { return data; } From 95ddd4b23f2f3161dec5d85f466efc8b676b8231 Mon Sep 17 00:00:00 2001 From: mtuncbilek Date: Sat, 21 Sep 2024 14:46:39 +0200 Subject: [PATCH 08/11] memmappedfile-fix (#115) Co-authored-by: Metehan Tuncbilek Reviewed-by: Georg Hagen Co-authored-by: mtuncbilek Co-committed-by: mtuncbilek --- 3rdParty/tinyusdz/CMakeLists.txt | 7 + openVulkanoCpp/Host/Windows/ErrorUtils.cpp | 45 +++ openVulkanoCpp/Host/Windows/ErrorUtils.hpp | 18 + openVulkanoCpp/IO/MemMappedFile.cpp | 114 +----- openVulkanoCpp/IO/MemMappedFile.hpp | 12 +- openVulkanoCpp/IO/MemMappedFileInternal.cpp | 125 ++++++ openVulkanoCpp/IO/MemMappedFileInternal.hpp | 50 +++ .../IO/MemMappedFileWriteHelper.cpp | 367 ++++++++++++++++++ .../IO/MemMappedFileWriteHelper.hpp | 48 +++ openVulkanoCpp/Scene/AtlasData.hpp | 3 +- tests/MemFileTests.cpp | 126 ++++++ 11 files changed, 801 insertions(+), 114 deletions(-) create mode 100644 openVulkanoCpp/Host/Windows/ErrorUtils.cpp create mode 100644 openVulkanoCpp/Host/Windows/ErrorUtils.hpp create mode 100644 openVulkanoCpp/IO/MemMappedFileInternal.cpp create mode 100644 openVulkanoCpp/IO/MemMappedFileInternal.hpp create mode 100644 openVulkanoCpp/IO/MemMappedFileWriteHelper.cpp create mode 100644 openVulkanoCpp/IO/MemMappedFileWriteHelper.hpp create mode 100644 tests/MemFileTests.cpp diff --git a/3rdParty/tinyusdz/CMakeLists.txt b/3rdParty/tinyusdz/CMakeLists.txt index ac968c7..29799ce 100644 --- a/3rdParty/tinyusdz/CMakeLists.txt +++ b/3rdParty/tinyusdz/CMakeLists.txt @@ -18,6 +18,13 @@ FetchContent_Declare( -DTINYUSDZ_BUILD_TESTS:BOOL=OFF -DTINYUSDZ_BUILD_EXAMPLES:BOOL=OFF ) + +set(TINYUSDZ_NO_WERROR ON) +set(TINYUSDZ_BUILD_TESTS OFF) +set(TINYUSDZ_BUILD_BENCHMARKS OFF) +set(TINYUSDZ_BUILD_EXAMPLES OFF) +set(TINYUSDZ_WITH_BUILTIN_IMAGE_LOADER OFF) + FetchContent_MakeAvailable(tinyusdz) function (LinkTinyUSDZ TARGET) diff --git a/openVulkanoCpp/Host/Windows/ErrorUtils.cpp b/openVulkanoCpp/Host/Windows/ErrorUtils.cpp new file mode 100644 index 0000000..04f8245 --- /dev/null +++ b/openVulkanoCpp/Host/Windows/ErrorUtils.cpp @@ -0,0 +1,45 @@ +/* +* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "ErrorUtils.hpp" + +#if defined(_MSC_VER) +#include "Windows.h" +#endif + +namespace OpenVulkano +{ +#if defined(_MSC_VER) + std::string ErrorUtils::GetLastErrorMessage() + { + // Get the error message ID, if any. + DWORD errorID = GetLastError(); + if (errorID == 0) + { + return std::string(); + } + + LPSTR messageBuffer = nullptr; + // Format the error message + uint64_t size = + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, NULL); + + // Buffer it up + std::string message(messageBuffer, size); + + // Free the buffer and return the message + LocalFree(messageBuffer); + return message; + } +#else + std::string ErrorUtils::GetLastErrorMessage() + { + return std::string(); + } +#endif + +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/Windows/ErrorUtils.hpp b/openVulkanoCpp/Host/Windows/ErrorUtils.hpp new file mode 100644 index 0000000..7ee864d --- /dev/null +++ b/openVulkanoCpp/Host/Windows/ErrorUtils.hpp @@ -0,0 +1,18 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include + +namespace OpenVulkano +{ + class ErrorUtils + { + public: + static std::string GetLastErrorMessage(); + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/IO/MemMappedFile.cpp b/openVulkanoCpp/IO/MemMappedFile.cpp index f298e7c..dbb6963 100644 --- a/openVulkanoCpp/IO/MemMappedFile.cpp +++ b/openVulkanoCpp/IO/MemMappedFile.cpp @@ -6,121 +6,15 @@ #include "MemMappedFile.hpp" #include "Base/Logger.hpp" -#ifdef _MSC_VER -#include -#else -#include -#include -#include -#include -#include -#endif +#include "MemMappedFileInternal.hpp" namespace OpenVulkano { - class MemMappedFile::Internal final - { - public: - void* address = nullptr; - size_t size = 0; -#ifdef _MSC_VER - HANDLE fileHandle; - HANDLE fileMappingHandle; - - Internal(const char* file, FileMode fileMode) - { //TODO handle other file modes than read - fileHandle = CreateFile( - TEXT(file), // file to open - GENERIC_READ, // open for reading - 0, // do not share - NULL, // default security - OPEN_EXISTING, // existing file only - FILE_ATTRIBUTE_NORMAL, // normal file - NULL); // no attribute template - if (fileHandle == INVALID_HANDLE_VALUE) - { - Logger::FILESYS->critical("Failed to open file: {}", file); - return; - } - - LARGE_INTEGER fileSize; - if (!GetFileSizeEx(fileHandle, &fileSize)) - { - Logger::FILESYS->critical("Failed to get size of file: {}", file); - CloseHandle(fileHandle); - return; - } - size = fileSize.QuadPart; - - fileMappingHandle = CreateFileMapping( - fileHandle, - NULL, // default security - PAGE_READONLY, // read-only access - 0, // maximum object size (high-order) - 0, // maximum object size (low-order) - NULL); // name of the mapping object - if (fileMappingHandle == 0) - { - Logger::FILESYS->critical("Failed to create file mapping handle: {}", file); - CloseHandle(fileHandle); - size = 0; - return; - } - - address = MapViewOfFile( - fileMappingHandle, // handle to the map object - FILE_MAP_READ, // read access - 0, // max. object size - 0, // size of the hFile - 0); // map the entire file - - if (address == nullptr) - { - Logger::FILESYS->critical("Failed to create file mapping: {}", file); - CloseHandle(fileMappingHandle); - CloseHandle(fileHandle); - size = 0; - } - } - - ~Internal() - { - UnmapViewOfFile(address); - CloseHandle(fileMappingHandle); - CloseHandle(fileHandle); - } -#else - int fileHandle; - struct stat fileStat; - - Internal(const char* file, FileMode fileMode) - { - fileHandle = open(file, fileMode); - if (fstat(fileHandle, &fileStat) == -1) - { - Logger::FILESYS->critical("Failed to query stats for file: {}", file); - return; - } - address = mmap(nullptr, fileStat.st_size, fileMode + 1, MAP_PRIVATE, fileHandle, 0); - if (address == MAP_FAILED) - { - Logger::FILESYS->critical("Failed to memory map file: {}", file); - address = nullptr; - } - size = fileStat.st_size; - } - - ~Internal() - { - munmap(address, fileStat.st_size); - close(fileHandle); - } -#endif - }; - MemMappedFile::MemMappedFile(const std::filesystem::path& path, FileMode fileMode) + : m_data(nullptr), m_size(0) { - m_internal = std::make_shared(path.string().c_str(), fileMode); + std::string file = path.string(); + m_internal = std::make_shared(file.c_str(), fileMode); m_data = m_internal->address; m_size = (m_data) ? m_internal->size : 0; } diff --git a/openVulkanoCpp/IO/MemMappedFile.hpp b/openVulkanoCpp/IO/MemMappedFile.hpp index 42ae856..72ef781 100644 --- a/openVulkanoCpp/IO/MemMappedFile.hpp +++ b/openVulkanoCpp/IO/MemMappedFile.hpp @@ -10,16 +10,22 @@ namespace OpenVulkano { + class MemMappedFileInternal; class MemMappedFile { - class Internal; + friend class MemMappedFileWriteHelper; - std::shared_ptr m_internal; + std::shared_ptr m_internal; void* m_data; size_t m_size; public: - enum FileMode : int { READ_ONLY = 0, WRITE_ONLY, READ_WRITE }; + enum FileMode : int + { + READ_ONLY = 0, + WRITE_ONLY, + READ_WRITE + }; MemMappedFile(const std::filesystem::path& path, FileMode fileMode = READ_ONLY); diff --git a/openVulkanoCpp/IO/MemMappedFileInternal.cpp b/openVulkanoCpp/IO/MemMappedFileInternal.cpp new file mode 100644 index 0000000..460552e --- /dev/null +++ b/openVulkanoCpp/IO/MemMappedFileInternal.cpp @@ -0,0 +1,125 @@ +/* +* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "MemMappedFileInternal.hpp" + +#include "Base/Logger.hpp" +#include "Host/Windows/ErrorUtils.hpp" + +namespace OpenVulkano +{ +#ifdef _MSC_VER + MemMappedFileInternal::MemMappedFileInternal(const char* file, MemMappedFile::FileMode fileMode) + : size(0), fileHandle(nullptr), fileMappingHandle(nullptr) + { + DWORD openMode { 0 }, mapMode { 0 }, viewMode { 0 }; + + switch (fileMode) + { + case MemMappedFile::FileMode::READ_ONLY: + openMode = GENERIC_READ; + mapMode = PAGE_READONLY; + viewMode = FILE_MAP_READ; + break; + case MemMappedFile::FileMode::WRITE_ONLY: + openMode = GENERIC_WRITE; + mapMode = PAGE_READWRITE; + viewMode = FILE_MAP_WRITE; + break; + case MemMappedFile::FileMode::READ_WRITE: + openMode = GENERIC_ALL; + mapMode = PAGE_READWRITE; + viewMode = FILE_MAP_ALL_ACCESS; + break; + default: + break; + } + + fileHandle = CreateFileA(file, // file to open + openMode, // open for switch case + NULL, // share mode + NULL, // default security + OPEN_ALWAYS, // always + FILE_ATTRIBUTE_NORMAL, // normal file + NULL); // no attribute template + + if (fileHandle == INVALID_HANDLE_VALUE || fileHandle == NULL) + { + Logger::FILESYS->critical("Failed to open file: {}.", ErrorUtils::GetLastErrorMessage()); + return; + } + + LARGE_INTEGER fileSize; + if (!GetFileSizeEx(fileHandle, &fileSize)) + { + Logger::FILESYS->critical("Failed to get size of file: {}", ErrorUtils::GetLastErrorMessage()); + CloseHandle(fileHandle); + return; + } + size = fileSize.QuadPart; + + fileMappingHandle = CreateFileMappingA(fileHandle, + NULL, // default security + mapMode, // mod for switch case + 0, // maximum object size (high-order) + 0, // maximum object size (low-order) + NULL); // name of the mapping object + if (fileMappingHandle == INVALID_HANDLE_VALUE || fileMappingHandle == NULL) + { + Logger::FILESYS->critical("Failed to create file mapping handle: {}", ErrorUtils::GetLastErrorMessage()); + CloseHandle(fileHandle); + size = 0; + return; + } + + address = MapViewOfFile(fileMappingHandle, // handle to the map object + viewMode, // access for switch case + 0, // max. object size + 0, // size of the hFile + 0); // map the entire file + + if (address == nullptr) + { + Logger::FILESYS->critical("Failed to create file mapping: {}", ErrorUtils::GetLastErrorMessage()); + CloseHandle(fileMappingHandle); + CloseHandle(fileHandle); + size = 0; + } + } + + MemMappedFileInternal::~MemMappedFileInternal() + { + UnmapViewOfFile(address); + CloseHandle(fileMappingHandle); + CloseHandle(fileHandle); + } +#else + MemMappedFileInternal::MemMappedFileInternal(const char* file, MemMappedFile::FileMode fileMode) + { + fileHandle = open(file, fileMode); + if (fstat(fileHandle, &fileStat) == -1) + { + Logger::FILESYS->critical("Failed to query stats for file: {}", file); + return; + } + address = mmap(nullptr, fileStat.st_size, fileMode + 1, MAP_SHARED, fileHandle, 0); + if (address == MAP_FAILED) + { + Logger::FILESYS->critical("Failed to memory map file: {}", file); + address = nullptr; + } + size = fileStat.st_size; + } + + MemMappedFileInternal::~MemMappedFileInternal() + { + munmap(address, fileStat.st_size); + close(fileHandle); + } +#endif + + +} \ No newline at end of file diff --git a/openVulkanoCpp/IO/MemMappedFileInternal.hpp b/openVulkanoCpp/IO/MemMappedFileInternal.hpp new file mode 100644 index 0000000..2d7852b --- /dev/null +++ b/openVulkanoCpp/IO/MemMappedFileInternal.hpp @@ -0,0 +1,50 @@ +/* +* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "MemMappedFile.hpp" + +#ifdef _MSC_VER + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace OpenVulkano +{ + class MemMappedFileInternal + { + friend class MemMappedFileWriteHelper; + friend class MemMappedFile; + + public: + MemMappedFileInternal(const char* file, MemMappedFile::FileMode fileMode); + ~MemMappedFileInternal(); + + private: +#if defined(_MSC_VER) + bool InvalidHandle() const { return fileHandle == INVALID_HANDLE_VALUE || fileHandle == NULL; } +#else + [[nodiscard]] bool InvalidHandle() const { return fileHandle == -1; } +#endif + + private: + void* address = nullptr; + size_t size = 0; + +#ifdef _MSC_VER + HANDLE fileHandle; + HANDLE fileMappingHandle; +#else + int fileHandle; + struct stat fileStat; +#endif + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/IO/MemMappedFileWriteHelper.cpp b/openVulkanoCpp/IO/MemMappedFileWriteHelper.cpp new file mode 100644 index 0000000..4c51dc7 --- /dev/null +++ b/openVulkanoCpp/IO/MemMappedFileWriteHelper.cpp @@ -0,0 +1,367 @@ +/* +* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "MemMappedFileWriteHelper.hpp" + +#include "MemMappedFile.hpp" +#include "MemMappedFileInternal.hpp" + +#if defined(_WIN32) + #include + #include +#else + #include + #include + #include + #include + #include +#endif + +#include "Base/Logger.hpp" +#include + +namespace OpenVulkano +{ +#if defined(_WIN32) + + MemMappedFileWriteHelper::MemMappedFileWriteHelper(const std::filesystem::path& path, size_t maxFileSize) + : m_path(path), m_file(nullptr) + { + // Check if file exists, so we can open it in the correct mode + DWORD openMode = CREATE_ALWAYS; + if (std::filesystem::exists(path)) + { + openMode = OPEN_ALWAYS; + } + + // Open the file + HANDLE fileHandle = + CreateFileA(path.string().c_str(), GENERIC_ALL, FILE_SHARE_WRITE, NULL, openMode, FILE_ATTRIBUTE_NORMAL, NULL); + + if (fileHandle == INVALID_HANDLE_VALUE || fileHandle == NULL) + { + throw std::runtime_error(ErrorUtils::GetLastErrorMessage().c_str()); + } + + // Control the file size + LARGE_INTEGER currentSize {}; + if (fileHandle) + { + if (!GetFileSizeEx(fileHandle, ¤tSize)) + { + throw std::runtime_error(ErrorUtils::GetLastErrorMessage().c_str()); + } + } + + if (currentSize.QuadPart <= 0 && maxFileSize == 0) + { + throw std::runtime_error("Either the file size has to be greater than 0 or Initial Size!"); + } + + // Set the file size + if (maxFileSize > 0) + { + if (fileHandle) + { + // Set the file size + if (!SetFilePointer(fileHandle, DWORD(maxFileSize), NULL, FILE_BEGIN)) + { + throw std::runtime_error(ErrorUtils::GetLastErrorMessage().c_str()); + } + + if (!SetEndOfFile(fileHandle)) + { + throw std::runtime_error(ErrorUtils::GetLastErrorMessage().c_str()); + } + } + } + + if (fileHandle) + { + CloseHandle(fileHandle); + } + + m_file = new MemMappedFile(path.string().c_str(), MemMappedFile::FileMode::READ_WRITE); + } + + MemMappedFileWriteHelper::~MemMappedFileWriteHelper() = default; + + bool MemMappedFileWriteHelper::Resize(const size_t newSize) + { + if (newSize == 0) + { + // Handle the case where the new size is zero + Logger::FILESYS->critical("New size is zero."); + return false; + } + + // Get the current file handle + if (m_file->m_internal->fileHandle == INVALID_HANDLE_VALUE) + { + Logger::FILESYS->critical("Invalid file handle."); + return false; + } + + // Unmap the old view of the file and close the file mapping handle + if (!(m_file->m_internal->fileMappingHandle == NULL) + && !(m_file->m_internal->fileMappingHandle == INVALID_HANDLE_VALUE)) + { + UnmapViewOfFile(m_file->m_internal->address); + CloseHandle(m_file->m_internal->fileMappingHandle); + } + + // Resize the file + if (!SetFilePointer(m_file->m_internal->fileHandle, static_cast(newSize), NULL, FILE_BEGIN)) + { + Logger::FILESYS->critical("Failed to set file pointer. Reason: {}", GetLastError()); + return false; + } + + if (!SetEndOfFile(m_file->m_internal->fileHandle)) + { + Logger::FILESYS->critical("Failed to set end of file."); + return false; + } + + // Create a new file mapping handle + m_file->m_internal->fileMappingHandle = + CreateFileMappingW(m_file->m_internal->fileHandle, NULL, PAGE_READWRITE, 0, 0, NULL); + if (m_file->m_internal->fileMappingHandle == NULL) + { + Logger::FILESYS->critical("Failed to create file mapping."); + return false; + } + + // Map the file to a new view + m_file->m_internal->address = + MapViewOfFile(m_file->m_internal->fileMappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (m_file->m_internal->address == NULL || m_file->m_internal->address == INVALID_HANDLE_VALUE) + { + Logger::FILESYS->critical("Failed to map view of file."); + return false; + } + + return true; + } + + + bool MemMappedFileWriteHelper::Trim(const size_t finalFileSize) + { + HANDLE fileHandle {}; + if (m_file->m_internal->InvalidHandle()) + { + auto file = m_path.string(); + fileHandle = CreateFileA(file.c_str(), // file to open + GENERIC_WRITE, // open for reading + 0, // do not share + NULL, // default security + CREATE_ALWAYS, // existing file only + FILE_ATTRIBUTE_NORMAL, // normal file + NULL); // no attribute template + } + else + { + fileHandle = m_file->m_internal->fileHandle; + } + + if (m_file) + { + if (!UnmapViewOfFile(m_file->m_internal->address)) + { + Logger::FILESYS->critical("Failed to unmap view of file on trim."); + return false; + } + + if (!CloseHandle(m_file->m_internal->fileMappingHandle)) + { + Logger::FILESYS->critical("Failed to close file mapping handle."); + return false; + } + } + + LARGE_INTEGER liSize; + liSize.QuadPart = finalFileSize; + if (!SetFilePointerEx(fileHandle, liSize, NULL, FILE_BEGIN) || !SetEndOfFile(fileHandle)) + { + Logger::FILESYS->critical("Failed to trim file: {}", ErrorUtils::GetLastErrorMessage()); + return false; + } + + // Create a new file mapping handle + if (m_file) + { + m_file->m_internal->fileMappingHandle = CreateFileMappingW(fileHandle, NULL, PAGE_READWRITE, 0, 0, NULL); + if (m_file->m_internal->fileMappingHandle == NULL) + { + Logger::FILESYS->critical("Failed to create file mapping."); + return false; + } + + // Map the file to a new view + m_file->m_internal->address = + MapViewOfFile(m_file->m_internal->fileMappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (m_file->m_internal->address == NULL || m_file->m_internal->address == INVALID_HANDLE_VALUE) + { + Logger::FILESYS->critical("Failed to map view of file."); + return false; + } + } + + if (m_file) + { + m_file->m_size = finalFileSize; + m_file->m_internal->size = finalFileSize; + } + + return true; + } +#else + MemMappedFileWriteHelper::MemMappedFileWriteHelper(const std::filesystem::path& path, size_t maxFileSize) + { + const auto file = path.string(); + const int fileHandle = open(file.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fileHandle == -1) + { + throw std::runtime_error("Failed to open file: " + file); + } + + struct stat fileStat + { + }; + if (fstat(fileHandle, &fileStat) == -1) + { + close(fileHandle); + throw std::runtime_error("Failed to get file size: " + file); + } + + if (const size_t fileSize = fileStat.st_size; fileSize < maxFileSize) + { + if (ftruncate(fileHandle, maxFileSize) == -1) + { + close(fileHandle); + throw std::runtime_error("Failed to resize file: " + file); + } + } + + close(fileHandle); + m_file = new MemMappedFile(file.c_str(), MemMappedFile::FileMode::READ_WRITE); + } + + MemMappedFileWriteHelper::~MemMappedFileWriteHelper() = default; + + bool MemMappedFileWriteHelper::Resize(const size_t newSize) + { + if (newSize == 0) + { + // Handle the case where the new size is zero + Logger::FILESYS->error("New size is zero. New size must be greater than zero."); + return false; + } + + // Get the current file handle + if (m_file->m_internal->fileHandle == -1) + { + Logger::FILESYS->critical("Invalid file handle."); + return false; + } + + // Resize the file + if (ftruncate(m_file->m_internal->fileHandle, newSize) == -1) + { + Logger::FILESYS->critical("Failed to resize file."); + return false; + } + + // Unmap the old view and map a new view of the resized file + if (m_file->m_internal->address != nullptr) + { + if (munmap(m_file->m_internal->address, m_file->m_internal->size) == -1) + { + Logger::FILESYS->critical("Failed to unmap view of file."); + return false; + } + } + + // Map the file to a new view + m_file->m_internal->address = + mmap(nullptr, newSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_file->m_internal->fileHandle, 0); + if (m_file->m_internal->address == MAP_FAILED) + { + Logger::FILESYS->critical("Failed to map file."); + return false; + } + + // Update the size of the file + m_file->m_internal->size = newSize; + return true; + } + + bool MemMappedFileWriteHelper::Trim(const size_t finalFileSize) + { + // If getting handle returns invalid file recreate the file + if (m_file->m_internal->InvalidHandle()) + { + const auto file = m_path.string(); + m_file->m_internal->fileHandle = open(file.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + } + + // Unmap the old view of the file + if(munmap(m_file->m_internal->address, m_file->m_internal->size) == -1) + { + Logger::FILESYS->critical("Failed to unmap address."); + } + + // Trim the file + if (ftruncate(m_file->m_internal->fileHandle, finalFileSize) == -1) + { + Logger::FILESYS->critical("Failed to trim file."); + return false; + } + + // Remap the file with the new size + m_file->m_internal->address = mmap(nullptr, finalFileSize, PROT_READ | PROT_WRITE, MAP_SHARED, + m_file->m_internal->fileHandle, 0); + if (m_file->m_internal->address == MAP_FAILED) + { + Logger::FILESYS->critical("Failed to map file."); + return false; + } + + // Update the size in MemMappedFile structure + m_file->m_size = finalFileSize; + return true; + } + +#endif + + void MemMappedFileWriteHelper::Close() + { + if (m_file->IsOpen()) + { + delete m_file; + } + } + + MemMappedFileWriter::MemMappedFileWriter(const std::filesystem::path& path, size_t maxFileSize) + : m_helper(MemMappedFileWriteHelper(path, maxFileSize)) + { + } + + MemMappedFileWriter::~MemMappedFileWriter() { m_helper.Close(); } + + void MemMappedFileWriter::Write(const char* data, const size_t size) + { + // If the new size is greater than the current size, resize the file + if (m_writtenSize + size > m_helper.Size()) + { + m_helper.Resize(m_writtenSize + size); + } + + // Write to the end of the file + memcpy(static_cast(m_helper.Data()) + m_writtenSize, data, size); + m_writtenSize += size; + } +} \ No newline at end of file diff --git a/openVulkanoCpp/IO/MemMappedFileWriteHelper.hpp b/openVulkanoCpp/IO/MemMappedFileWriteHelper.hpp new file mode 100644 index 0000000..7b3ac03 --- /dev/null +++ b/openVulkanoCpp/IO/MemMappedFileWriteHelper.hpp @@ -0,0 +1,48 @@ +/* +* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "IO/MemMappedFile.hpp" +#include + +namespace OpenVulkano +{ + class MemMappedFileWriteHelper + { + public: + MemMappedFileWriteHelper(const std::filesystem::path& path, size_t maxFileSize); + ~MemMappedFileWriteHelper(); + + [[nodiscard]] void* Data() const { return m_file->Data(); } + [[nodiscard]] size_t Size() const { return m_file->Size(); } + + bool Resize(size_t newSize); + void Close(); + bool Trim(size_t finalFileSize); + + static constexpr size_t USE_CURRENT_FILE_SIZE = 0; + + private: + MemMappedFile* m_file; + std::filesystem::path m_path; + }; + + class MemMappedFileWriter final + { + public: + MemMappedFileWriter(const std::filesystem::path& path, size_t maxFileSize); + ~MemMappedFileWriter(); + + void Write(const char* data, size_t size); + void SetOffset(size_t offset) { m_writtenSize = offset; } + [[nodiscard]] size_t GetOffset() const { return m_writtenSize; } + + private: + MemMappedFileWriteHelper m_helper; + size_t m_writtenSize = 0; + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/AtlasData.hpp b/openVulkanoCpp/Scene/AtlasData.hpp index 7bfd8f7..08dc6a6 100644 --- a/openVulkanoCpp/Scene/AtlasData.hpp +++ b/openVulkanoCpp/Scene/AtlasData.hpp @@ -6,6 +6,7 @@ #pragma once +#include "Base/Wrapper.hpp" #include "Math/Math.hpp" #include "Image/Image.hpp" #include "Scene/Texture.hpp" @@ -56,7 +57,7 @@ namespace OpenVulkano::Scene { std::map glyphs; AtlasMetadata meta; - std::unique_ptr img; + Unique img; Texture texture; }; diff --git a/tests/MemFileTests.cpp b/tests/MemFileTests.cpp new file mode 100644 index 0000000..117ffa5 --- /dev/null +++ b/tests/MemFileTests.cpp @@ -0,0 +1,126 @@ +#include + +#include "IO/MemMappedFile.hpp" +#include "IO/MemMappedFileWriteHelper.hpp" +#include "IO/AppFolders.hpp" +#include "Base/Logger.hpp" + +#include +#include + +namespace fs = std::filesystem; + +TEST_CASE("MemMappedFileWrite") +{ + OpenVulkano::Logger::SetupLogger("", "tests.log"); + + auto path = OpenVulkano::AppFolders::GetAppTempDir(); + path += "/MemFileTest.txt"; + + std::string data = "Hello World X\n" + "Hello World Y\n" + "Hello World Z\n" + "Hello World W\n" + "Hello World A\n" + "Hello World B\n" + "Hello World C\n" + "Hello World D\n" + "Hello World E\n" + "Hello World F\n" + "Hello World G\n" + "Hello World H\n" + "Hello World I\n" + "Hello World J\n" + "Hello World K\n" + "Hello World L\n" + "Hello World M\n" + "Hello World N\n"; + + + SECTION("Write to MemMappedFile") + { + OpenVulkano::MemMappedFileWriter writer(path, 1024); + writer.Write(data.data(), data.size()); + + REQUIRE(writer.GetOffset() == data.size()); + } + + SECTION("Control Size") + { + OpenVulkano::MemMappedFile memFile(path, OpenVulkano::MemMappedFile::READ_ONLY); + REQUIRE(memFile.Size() == 1024); // The size that comes from the first Test. + } + + SECTION("Compare String Data for per char") + { + OpenVulkano::MemMappedFileWriteHelper memFileHelper(path, OpenVulkano::MemMappedFile::READ_ONLY); + REQUIRE(memFileHelper.Trim(data.size())); + + std::string testData((char*) memFileHelper.Data()); + + for (size_t i = 0; i < data.size(); i++) + { + if (data[i] != testData[i]) + { + REQUIRE(false); + } + } + + memFileHelper.Close(); + } + + SECTION("Trim File") + { + OpenVulkano::MemMappedFileWriteHelper helper(path, + OpenVulkano::MemMappedFileWriteHelper::USE_CURRENT_FILE_SIZE); + REQUIRE(helper.Trim(100)); + REQUIRE(helper.Size() == 100); + + helper.Close(); + } + + SECTION("Write Data 2") + { + OpenVulkano::MemMappedFileWriter writer(path, OpenVulkano::MemMappedFileWriteHelper::USE_CURRENT_FILE_SIZE); + writer.Write(data.data(), data.size()); + writer.Write(data.data(), data.size()); + writer.Write(data.data(), data.size()); + + REQUIRE(writer.GetOffset() == data.size() * 3); + } + + SECTION("Compare Data") + { + OpenVulkano::MemMappedFileWriteHelper helper(path, + OpenVulkano::MemMappedFileWriteHelper::USE_CURRENT_FILE_SIZE); + REQUIRE(helper.Data() != nullptr); + std::string testData((char*) helper.Data()); + printf("size: %llu", helper.Size()); + helper.Close(); + + std::ifstream file(path, std::ios::binary); + std::string streamData; + std::getline(file, streamData, '\0'); + file.close(); + + for (size_t i = 0; i < testData.size(); i++) + { + if (streamData[i] != testData[i]) + { + REQUIRE(false); + } + } + + REQUIRE(streamData.size() == testData.size()); + + printf("helper size: %llu\n", helper.Size()); + } + + SECTION("Actual Size") + { + std::error_code ec; + std::uintmax_t size = fs::file_size(path, ec); + + REQUIRE(size == 756); + } +} From 653be0a4039245a43829383838dd58f19d690fb6 Mon Sep 17 00:00:00 2001 From: GeorgH93 Date: Sat, 21 Sep 2024 14:51:11 +0200 Subject: [PATCH 09/11] Fix build issues --- 3rdParty/msdf/CMakeLists.txt | 1 + openVulkanoCpp/Base/Render/RenderResource.hpp | 5 +++++ openVulkanoCpp/Scene/TextDrawable.cpp | 1 - .../Vulkan/Scene/LabelDrawableVulkanEncoder.cpp | 12 ++++++------ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/3rdParty/msdf/CMakeLists.txt b/3rdParty/msdf/CMakeLists.txt index 1b64a28..c43c63b 100644 --- a/3rdParty/msdf/CMakeLists.txt +++ b/3rdParty/msdf/CMakeLists.txt @@ -93,6 +93,7 @@ function(LinkMsdf TARGET) if (FREETYPE_BUILT_FROM_SOURCES) target_include_directories(${TARGET} PUBLIC ${FREETYPE_INCLUDE_DIR}) else() + find_package(Freetype REQUIRED) target_include_directories(${TARGET} PUBLIC ${FREETYPE_INCLUDE_DIRS}) endif() endif() diff --git a/openVulkanoCpp/Base/Render/RenderResource.hpp b/openVulkanoCpp/Base/Render/RenderResource.hpp index 93e62a6..03f1f05 100644 --- a/openVulkanoCpp/Base/Render/RenderResource.hpp +++ b/openVulkanoCpp/Base/Render/RenderResource.hpp @@ -167,6 +167,11 @@ namespace OpenVulkano bool HasRenderResource() const { return renderResource; } + RenderResourceHolder& operator =(const RenderResourceHolder& copy) noexcept + { + renderResource.Release(); + } + RenderResourceHolder& operator =(RenderResourceHolder&& move) noexcept { renderResource = std::move(move.renderResource); diff --git a/openVulkanoCpp/Scene/TextDrawable.cpp b/openVulkanoCpp/Scene/TextDrawable.cpp index d9fd170..5413368 100644 --- a/openVulkanoCpp/Scene/TextDrawable.cpp +++ b/openVulkanoCpp/Scene/TextDrawable.cpp @@ -93,7 +93,6 @@ namespace OpenVulkano::Scene m_atlasData = std::make_shared(); if (isPacked) { - m_atlasData->texture = Texture(); m_material.texture = &m_atlasData->texture; m_atlasData->img = Image::IImageLoader::loadData((const uint8_t*) atlasMetadata.Data(), offsetToMetadata); diff --git a/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp b/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp index e7ff24b..7371174 100644 --- a/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp +++ b/openVulkanoCpp/Vulkan/Scene/LabelDrawableVulkanEncoder.cpp @@ -23,7 +23,7 @@ namespace OpenVulkano::Vulkan if (labelDrawable->IsBillboard()) { OpenVulkano::Scene::UniformBuffer* buffer = labelDrawable->GetBillboardBuffer(); - VulkanUniformBuffer* vkBuffer = static_cast(buffer->renderBuffer); + VulkanUniformBuffer* vkBuffer = buffer->GetRenderResource(); if (!vkBuffer) { vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(buffer); @@ -32,7 +32,7 @@ namespace OpenVulkano::Vulkan } OpenVulkano::Scene::UniformBuffer* labelBuffer = labelDrawable->GetLabelBuffer(); - VulkanUniformBuffer* vkBuffer = static_cast(labelBuffer->renderBuffer); + VulkanUniformBuffer* vkBuffer = labelBuffer->GetRenderResource(); if (!vkBuffer) { vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(labelBuffer); @@ -57,7 +57,7 @@ namespace OpenVulkano::Vulkan OpenVulkano::Scene::Shader* shader = entry.GetShader(); drawContext->EncodeShader(shader); Geometry* mesh = entry.GetMesh(); - VulkanGeometry* renderGeo = static_cast(mesh->renderGeo); + VulkanGeometry* renderGeo = mesh->GetRenderResource(); if (!renderGeo) { renderGeo = drawContext->renderer->GetResourceManager().PrepareGeometry(mesh); @@ -77,7 +77,7 @@ namespace OpenVulkano::Vulkan { if (buffer) { - VulkanUniformBuffer* vkBuffer = static_cast(buffer->renderBuffer); + VulkanUniformBuffer* vkBuffer = buffer->GetRenderResource(); if (!vkBuffer) { vkBuffer = drawContext->renderer->GetResourceManager().PrepareUniformBuffer(buffer); @@ -90,11 +90,11 @@ namespace OpenVulkano::Vulkan { if (Texture* texture = material->texture) { - VulkanTexture* renderTexture = static_cast(texture->renderTexture); + VulkanTexture* renderTexture = texture->GetRenderResource(); if (!renderTexture) { drawContext->renderer->GetResourceManager().PrepareMaterial(entry.GetMaterial()); - renderTexture = static_cast(texture->renderTexture); + renderTexture = texture->GetRenderResource(); } renderTexture->Record(drawContext); } From 35b2f49c2e9b47e27a0951551b343e3e87dfbeff Mon Sep 17 00:00:00 2001 From: "metehan.tuncbilek" Date: Sat, 21 Sep 2024 14:51:46 +0200 Subject: [PATCH 10/11] stable-vector (#72) Co-authored-by: Metehan Tuncbilek Reviewed-by: Georg Hagen Co-authored-by: metehan.tuncbilek Co-committed-by: metehan.tuncbilek --- .../Data/Containers/StableVector.hpp | 464 ++++++++++++++++++ tests/StableVectorTest.cpp | 190 +++++++ 2 files changed, 654 insertions(+) create mode 100644 openVulkanoCpp/Data/Containers/StableVector.hpp create mode 100644 tests/StableVectorTest.cpp diff --git a/openVulkanoCpp/Data/Containers/StableVector.hpp b/openVulkanoCpp/Data/Containers/StableVector.hpp new file mode 100644 index 0000000..ea57fa3 --- /dev/null +++ b/openVulkanoCpp/Data/Containers/StableVector.hpp @@ -0,0 +1,464 @@ +#pragma once + +#include "Base/Wrapper.hpp" + +#include +#include +#include +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable : 4200) +#pragma warning(disable : 6011) +#pragma warning(disable : 6386) + +namespace OpenVulkano +{ + /** + * @class Stable Vector + * @brief Stable Vector is an alternative version for std::vector that provides chunk based memory allocation without re-aligning + * + * @throw Please know that this vector creates array gaps when you remove an element. + */ + template class StableVector + { + struct VectorChunk + { + VectorChunk* m_prev = nullptr; + VectorChunk* m_next = nullptr; + size_t m_chunkSize; + int64_t m_lastUsedIndex; + bool* m_fill; + T m_data[0]; + + VectorChunk(size_t size) : m_chunkSize(size), m_lastUsedIndex(-1) + { + m_fill = reinterpret_cast(m_data + size); + memset(m_fill, 0, size * sizeof(bool)); + } + + ~VectorChunk() + { + for (size_t i = 0; i < m_chunkSize; i++) + { + if (m_fill[i]) + { + m_data[i].~T(); + } + } + m_prev = nullptr; + m_next = nullptr; + } + }; + + public: + class Iterator + { + public: + Iterator(VectorChunk* ptr, size_t index = 0) : m_ptr(ptr), m_index(index) {} + + T& operator*() { return m_ptr->m_data[m_index]; } + T* operator->() { return &m_ptr->m_data[m_index]; } + + Iterator operator++() + { + ++m_index; + MovetoNextValidChunk(); + return *this; + } + + Iterator operator++(int) + { + Iterator temp = *this; + ++(*this); + return temp; + } + + bool operator==(const Iterator& other) const { return m_ptr == other.m_ptr && m_index == other.m_index; } + bool operator!=(const Iterator& other) const { return !(*this == other); } + + private: + void MovetoNextValidChunk() + { + while (m_ptr && (m_index > m_ptr->m_chunkSize || !m_ptr->m_fill[m_index])) + { + if (m_index >= m_ptr->m_chunkSize) + { + m_ptr = m_ptr->m_next; + m_index = 0; + } + else ++m_index; + } + + if (m_ptr && m_index >= m_ptr->m_chunkSize) + { + m_ptr = m_ptr->m_next; + m_index = 0; + MovetoNextValidChunk(); + } + } + + private: + VectorChunk* m_ptr; + size_t m_index; + }; + + public: + StableVector() : m_firstChunk(nullptr), m_lastChunk(nullptr) + { + VectorChunk* chunk = SpawnChunk(DEFAULT_CHUNK_SIZE); + m_firstChunk = chunk; + m_lastChunk = chunk; + m_currentSize = 0; + m_totalCap = DEFAULT_CHUNK_SIZE; + } + + StableVector(const StableVector& copy) + { + m_firstChunk = nullptr; + m_lastChunk = nullptr; + m_currentSize = 0; + m_totalCap = 0; + + VectorChunk* currentChunk = copy.m_firstChunk; + while (currentChunk) + { + for (size_t i = 0; i < currentChunk->m_chunkSize; i++) + { + if (currentChunk->m_fill[i]) { PushBack(currentChunk->m_data[i]); } + } + + currentChunk = currentChunk->m_next; + } + } + + StableVector(StableVector&& move) noexcept + { + m_firstChunk = move.m_firstChunk; + m_lastChunk = move.m_lastChunk; + m_currentSize = move.m_currentSize; + m_totalCap = move.m_totalCap; + + move.m_firstChunk = nullptr; + move.m_lastChunk = nullptr; + move.m_currentSize = 0; + move.m_totalCap = 0; + } + + ~StableVector() + { + VectorChunk* currentChunk = m_firstChunk; + while (currentChunk) + { + VectorChunk* temp = currentChunk; + currentChunk = currentChunk->m_next; + temp->~VectorChunk(); + ::operator delete(temp); + } + } + + /** + * Adds the value to the first empty slot in the StableVector + * + * @param value - The value to be added + */ + void Add(const T& value) + { + VectorChunk* currentChunk = m_firstChunk; + while (currentChunk) + { + for (size_t i = 0; i < currentChunk->m_chunkSize; i++) + { + if (!currentChunk->m_fill[i]) + { + currentChunk->m_data[i] = value; + currentChunk->m_fill[i] = true; + m_currentSize++; + + if (i > currentChunk->m_lastUsedIndex) currentChunk->m_lastUsedIndex = i; + return; + } + } + + currentChunk = currentChunk->m_next; + } + + VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR)); + + new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(value); + m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true; + m_currentSize++; + } + + void PushBack(const T& value) + { + if (m_lastChunk->m_lastUsedIndex + 1 == m_lastChunk->m_chunkSize) + { + VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR)); + } + + new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(value); + m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true; + m_currentSize++; + } + + void PushBack(T&& value) noexcept + { + if (m_lastChunk->m_lastUsedIndex + 1 == m_lastChunk->m_chunkSize) + { + VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR)); + } + + new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(std::move(value)); + m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true; + m_currentSize++; + } + + template void Emplace(Args&&... args) + { + VectorChunk* currentChunk = m_firstChunk; + + while (currentChunk) + { + for (size_t i = 0; i < currentChunk->m_chunkSize; i++) + { + if (!currentChunk->m_fill[i]) + { + currentChunk->m_data[i] = T(std::forward(args)...); + currentChunk->m_fill[i] = true; + m_currentSize++; + + if (i > currentChunk->m_lastUsedIndex) currentChunk->m_lastUsedIndex = i; + return; + } + } + + currentChunk = currentChunk->m_next; + } + + VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR)); + + new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(std::forward(args)...); + m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true; + m_currentSize++; + } + + template void EmplaceBack(Args&&... args) + { + if (m_lastChunk->m_lastUsedIndex + 1 == m_lastChunk->m_chunkSize) + VectorChunk* chunk = SpawnChunk(size_t(m_lastChunk->m_chunkSize * GROWTH_FACTOR)); + + new (&m_lastChunk->m_data[++m_lastChunk->m_lastUsedIndex]) T(std::forward(args)...); + m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = true; + m_currentSize++; + } + + /** + * Pops the last element of the StableVector + * + * @throw Please know that this pop function also reduces the chunk's lastUsedIndex + */ + void PopBack() + { + if (m_currentSize == 0) return; + + if (m_lastChunk->m_lastUsedIndex == -1) + { + VectorChunk* temp = m_lastChunk; + m_lastChunk = m_lastChunk->m_prev; + m_lastChunk->m_next = nullptr; + temp->~VectorChunk(); + ::operator delete(temp); + } + + m_lastChunk->m_data[m_lastChunk->m_lastUsedIndex].~T(); + m_lastChunk->m_fill[m_lastChunk->m_lastUsedIndex] = false; + m_lastChunk->m_lastUsedIndex--; + m_currentSize--; + } + + void Remove(size_t index) + { + size_t localIndex = index; + VectorChunk* chunk = GetChunk(localIndex); + + if (chunk) + { + chunk->m_data[localIndex].~T(); + chunk->m_fill[localIndex] = false; + m_currentSize--; + } + else throw std::out_of_range("Index out of range!"); + } + + void Remove(const T& value) + { + VectorChunk* currentChunk = m_firstChunk; + while (currentChunk) + { + for (size_t i = 0; i < currentChunk->m_chunkSize; i++) + { + if (currentChunk->m_fill[i] && currentChunk->m_data[i] == value) + { + currentChunk->m_data[i].~T(); + currentChunk->m_fill[i] = false; + m_currentSize--; + return; + } + } + + currentChunk = currentChunk->m_next; + } + } + + std::vector ToVector() const + { + std::vector vec; + VectorChunk* currentChunk = m_firstChunk; + while (currentChunk) + { + for (size_t i = 0; i < currentChunk->m_chunkSize; i++) + { + if (currentChunk->m_fill[i]) + { + vec.push_back(currentChunk->m_data[i]); + } + } + + currentChunk = currentChunk->m_next; + } + + return vec; + } + T& At(size_t index) const + { + if (index >= Size()) [[unlikely]] + throw std::out_of_range("Index out of range!"); + return (*this)[index]; + } + + T& operator[](size_t index) const + { + VectorChunk* chunk = m_firstChunk; + size_t localIndex = index; + while (chunk) + { + if (localIndex > chunk->m_chunkSize - 1) + { + localIndex -= (chunk->m_chunkSize); + chunk = chunk->m_next; + } + else break; + } + + return chunk->m_data[localIndex]; + } + + size_t Size() const { return m_currentSize; } + size_t Capacity() const { return m_totalCap; } + + void Clear() + { + VectorChunk* currentChunk = m_firstChunk; + while (currentChunk) + { + VectorChunk* temp = currentChunk; + currentChunk = currentChunk->m_next; + delete temp; + } + + m_firstChunk = nullptr; + m_lastChunk = nullptr; + m_currentSize = 0; + m_totalCap = DEFAULT_CHUNK_SIZE; + + m_firstChunk = SpawnChunk(DEFAULT_CHUNK_SIZE); + m_lastChunk = m_firstChunk; + } + + StableVector& operator=(const StableVector& copy) + { + if (this == ©) return *this; + + Clear(); + + m_firstChunk = nullptr; + m_lastChunk = nullptr; + m_currentSize = 0; + m_totalCap = 0; + + VectorChunk* currentChunk = copy.m_firstChunk; + + for (auto it = copy.begin(); it != copy.end(); ++it) PushBack(*it); + } + + StableVector& operator=(StableVector&& move) noexcept + { + if (this == &move) return *this; + + Clear(); + + m_firstChunk = move.m_firstChunk; + m_lastChunk = move.m_lastChunk; + m_currentSize = move.m_currentSize; + m_totalCap = move.m_totalCap; + + move.m_firstChunk = nullptr; + move.m_lastChunk = nullptr; + move.m_currentSize = 0; + move.m_totalCap = 0; + + return *this; + } + + Iterator begin() { return Iterator(m_firstChunk, 0); } + Iterator end() { return Iterator(m_lastChunk, m_lastChunk->m_lastUsedIndex + 1); } + + const Iterator& cbegin() const { return Iterator(m_firstChunk, 0); } + const Iterator& cend() const { return Iterator(m_lastChunk, m_lastChunk->m_lastUsedIndex + 1); } + + private: + VectorChunk* SpawnChunk(size_t requestedSize) + { + VectorChunk* chunk = static_cast( + ::operator new(sizeof(VectorChunk) + requestedSize * sizeof(T) + requestedSize * sizeof(bool))); + new (chunk) VectorChunk(requestedSize); + + if (m_lastChunk) + { + chunk->m_prev = m_lastChunk; + m_lastChunk->m_next = chunk; + m_lastChunk = chunk; + m_totalCap += m_lastChunk->m_chunkSize; + } + + return chunk; + } + + VectorChunk* GetChunk(size_t& localIndex) + { + VectorChunk* chunk = m_firstChunk; + while (chunk) + { + if (localIndex > chunk->m_chunkSize - 1) + { + localIndex -= (chunk->m_chunkSize); + chunk = chunk->m_next; + } + else break; + } + + return chunk; + } + + private: + VectorChunk* m_firstChunk; + VectorChunk* m_lastChunk; + size_t m_currentSize; + size_t m_totalCap; + }; +} + +#pragma warning(pop) \ No newline at end of file diff --git a/tests/StableVectorTest.cpp b/tests/StableVectorTest.cpp new file mode 100644 index 0000000..51b15b3 --- /dev/null +++ b/tests/StableVectorTest.cpp @@ -0,0 +1,190 @@ +#include + +#include "Data/Containers/StableVector.hpp" + +using namespace OpenVulkano; + +struct Test +{ + uint32_t mValue; + Test(uint32_t value) : mValue(value) {} + Test() : mValue(0) {} +}; + +static int incrementCounter = 0; +static int decrementCounter = 0; + +struct TestCount +{ + TestCount() : m_value("") { ++incrementCounter; } + TestCount(const std::string& val) : m_value(val) { ++incrementCounter; } + TestCount(TestCount&& other) noexcept : m_value(std::move(other.m_value)) { ++incrementCounter; } + TestCount(const TestCount& other) : m_value(other.m_value) { ++incrementCounter; } + + TestCount& operator=(const TestCount& other) + { + m_value = other.m_value; + return *this; + } + + TestCount& operator=(TestCount&& other) noexcept + { + m_value = std::move(other.m_value); + return *this; + } + + ~TestCount() { ++decrementCounter; } + + std::string m_value; +}; + +TEST_CASE("ChunkVector") +{ + SECTION("PushBack") + { + StableVector vec; + + REQUIRE(vec.Size() == 0); + REQUIRE(vec.Capacity() == 32); + + for (uint32_t i = 0; i < 100; ++i) + { + vec.PushBack(i); + } + + REQUIRE(vec.Size() == 100); + REQUIRE(vec.Capacity() == 224); // 32 + 64 + 128 = 3 | previous chunk size multiplied by 2 + } + + SECTION("EmplaceBack") + { + StableVector vec; + + REQUIRE(vec.Size() == 0); + REQUIRE(vec.Capacity() == 32); + + for (uint32_t i = 0; i < 100; ++i) + { + vec.EmplaceBack(i); + } + + REQUIRE(vec.Size() == 100); + + for (uint32_t i = 0; i < 100; ++i) + { + REQUIRE(vec[i].mValue == i); + } + } + + SECTION("PopBack") + { + StableVector vec; + + REQUIRE(vec.Size() == 0); + REQUIRE(vec.Capacity() == 32); + + for (uint32_t i = 0; i < 100; ++i) + { + vec.PushBack(i); + } + + REQUIRE(vec.Size() == 100); + + uint64_t tempVal = vec.Capacity(); + + for (uint32_t i = 0; i < 50; ++i) + { + vec.PopBack(); + } + + REQUIRE(vec.Size() == 50); + REQUIRE(vec.Capacity() == tempVal); + } + + SECTION("Clear") + { + StableVector vec; + + REQUIRE(vec.Size() == 0); + REQUIRE(vec.Capacity() == 32); + + for (uint32_t i = 0; i < 100; ++i) + { + vec.PushBack(i); + } + + REQUIRE(vec.Size() == 100); + + vec.Clear(); + + REQUIRE(vec.Size() == 0); + REQUIRE(vec.Capacity() == 32); + } + + SECTION("Add/Fill") + { + StableVector vec; + + REQUIRE(vec.Size() == 0); + REQUIRE(vec.Capacity() == 32); + + for (uint32_t i = 0; i < 100; ++i) + { + vec.PushBack("a"); + } + + REQUIRE(vec.Size() == 100); + + vec.Remove(56); + vec.Add("z"); + + REQUIRE(vec.Size() == 100); + REQUIRE(vec[56] == "z"); + } + + SECTION("Correct Initialization") + { + StableVector vec; + + REQUIRE(incrementCounter == 0); + REQUIRE(decrementCounter == 0); + + vec.EmplaceBack("a"); + + REQUIRE(incrementCounter == 1); + REQUIRE(decrementCounter == 0); + + vec.PushBack(TestCount("b")); + + REQUIRE(incrementCounter == 3); + REQUIRE(decrementCounter == 1); + + TestCount testClass = TestCount("c"); + vec.PushBack(std::move(testClass)); + + REQUIRE(incrementCounter == 5); + REQUIRE(decrementCounter == 1); + + vec.Clear(); + + REQUIRE(incrementCounter == 5); + REQUIRE(decrementCounter == 4); + + vec.PushBack(TestCount("d")); + + REQUIRE(incrementCounter == 7); + REQUIRE(decrementCounter == 5); + + TestCount testClass2 = TestCount("e"); + vec.PushBack(testClass2); + + REQUIRE(incrementCounter == 9); + REQUIRE(decrementCounter == 5); + } + + SECTION("Out of scope check") + { + REQUIRE(incrementCounter == 9); + REQUIRE(decrementCounter == 9); + } +} From b7f640fe53aaf1ef4574b1fa979d01ff3b9aa26a Mon Sep 17 00:00:00 2001 From: GeorgH93 Date: Sat, 21 Sep 2024 15:28:59 +0200 Subject: [PATCH 11/11] Fix windows build error --- openVulkanoCpp/Base/Render/RenderResource.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/openVulkanoCpp/Base/Render/RenderResource.hpp b/openVulkanoCpp/Base/Render/RenderResource.hpp index 03f1f05..9acf08a 100644 --- a/openVulkanoCpp/Base/Render/RenderResource.hpp +++ b/openVulkanoCpp/Base/Render/RenderResource.hpp @@ -170,6 +170,7 @@ namespace OpenVulkano RenderResourceHolder& operator =(const RenderResourceHolder& copy) noexcept { renderResource.Release(); + return *this; } RenderResourceHolder& operator =(RenderResourceHolder&& move) noexcept