diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a9179c1f9..b0228ab24b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -566,6 +566,7 @@ add_library(Common STATIC Common/Data/Collections/FixedSizeQueue.h Common/Data/Collections/Hashmaps.h Common/Data/Collections/TinySet.h + Common/Data/Collections/FastVec.h Common/Data/Collections/ThreadSafeList.h Common/Data/Color/RGBAUtil.cpp Common/Data/Color/RGBAUtil.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index eec456c3f5..3ecf5c41ed 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -859,6 +859,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 776cefaf35..719cbe3e64 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -938,6 +938,9 @@ GPU\OpenGL + + Data\Collections + @@ -1086,4 +1089,4 @@ ext\basis_universal - \ No newline at end of file + diff --git a/Common/Data/Collections/FastVec.h b/Common/Data/Collections/FastVec.h new file mode 100644 index 0000000000..3267347960 --- /dev/null +++ b/Common/Data/Collections/FastVec.h @@ -0,0 +1,95 @@ +#pragma once + +// Yet another replacement for std::vector, this time for use in graphics queues. +// Its major difference is that you can append uninitialized structures and initialize them after. +// This is not allows by std::vector but is very useful for our sometimes oversized unions. +// Also, copies during resize are done by memcpy, not by any move constructor or similar. + +#include +#include + +template +class FastVec { +public: + FastVec() {} + FastVec(size_t initialCapacity) { + capacity_ = initialCapacity; + data_ = (T *)malloc(initialCapacity * sizeof(T)); + } + ~FastVec() { if (data_) free(data_); } + + T *push_back() { + if (size_ < capacity_) { + size_++; + return data_ + size_ - 1; + } else { + T *oldData = data_; + size_t newCapacity = capacity_ * 2; + if (newCapacity < 16) { + newCapacity = 16; + } + data_ = (T *)malloc(sizeof(T) * newCapacity); + if (capacity_ != 0) { + memcpy(data_, oldData, sizeof(T) * size_); + free(oldData); + } + size_++; + capacity_ = newCapacity; + return data_ + size_ - 1; + } + } + + void push_back(const T &t) { + T *dest = push_back(); + *dest = t; + } + + // Move constructor + FastVec(FastVec &&other) { + data_ = other.data_; + size_ = other.size_; + capacity_ = other.capacity_; + other.data_ = nullptr; + other.size_ = 0; + other.capacity_ = 0; + } + + FastVec &operator=(FastVec &&other) { + if (this != &other) { + delete[] data_; + data_ = other.data_; + size_ = other.size_; + capacity_ = other.capacity_; + other.data_ = nullptr; + other.size_ = 0; + other.capacity_ = 0; + } + return *this; + } + + // No copy constructor. + FastVec(const FastVec &other) = delete; + FastVec &operator=(const FastVec &other) = delete; + + size_t size() const { return size_; } + size_t capacity() const { return capacity_; } + void clear() { size_ = 0; } + + T *begin() { return data_; } + T *end() { return data_ + size_; } + const T *begin() const { return data_; } + const T *end() const { return data_ + size_; } + + // Out of bounds (past size() - 1) is undefined behavior. + T &operator[] (const size_t index) { return data_[index]; } + const T &operator[] (const size_t index) const { return data_[index]; } + + // These two are invalid if empty(). + const T &back() const { return (*this)[size() - 1]; } + const T &front() const { return (*this)[0]; } + +private: + T *data_ = nullptr; + size_t size_ = 0; + size_t capacity_ = 0; +}; diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index c5d562a5dc..757f59d95f 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -118,7 +118,7 @@ static std::string GetStereoBufferLayout(const char *uniformName) { else return "undefined"; } -void GLQueueRunner::RunInitSteps(const std::vector &steps, bool skipGLCalls) { +void GLQueueRunner::RunInitSteps(const FastVec &steps, bool skipGLCalls) { if (skipGLCalls) { // Some bookkeeping still needs to be done. for (size_t i = 0; i < steps.size(); i++) { diff --git a/Common/GPU/OpenGL/GLQueueRunner.h b/Common/GPU/OpenGL/GLQueueRunner.h index 3da2ce4de0..9ceedf1a86 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.h +++ b/Common/GPU/OpenGL/GLQueueRunner.h @@ -11,7 +11,7 @@ #include "Common/GPU/Shader.h" #include "Common/GPU/thin3d.h" #include "Common/Data/Collections/TinySet.h" - +#include "Common/Data/Collections/FastVec.h" struct GLRViewport { float x, y, w, h, minZ, maxZ; @@ -354,7 +354,7 @@ public: caps_ = caps; } - void RunInitSteps(const std::vector &steps, bool skipGLCalls); + void RunInitSteps(const FastVec &steps, bool skipGLCalls); void RunSteps(const std::vector &steps, bool skipGLCalls, bool keepSteps, bool useVR); diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index 8e259a722c..9ac14dfcec 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -135,13 +135,12 @@ bool GLRenderManager::ThreadFrame() { while (true) { // Pop a task of the queue and execute it. // NOTE: We need to actually wait for a task, we can't just bail! - { std::unique_lock lock(pushMutex_); while (renderThreadQueue_.empty()) { pushCondVar_.wait(lock); } - task = renderThreadQueue_.front(); + task = std::move(renderThreadQueue_.front()); renderThreadQueue_.pop(); } diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 71304eddfe..5d5ece924c 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -208,7 +208,7 @@ struct GLRRenderThreadTask { GLRRenderThreadTask(GLRRunType _runType) : runType(_runType) {} std::vector steps; - std::vector initSteps; + FastVec initSteps; int frame = -1; GLRRunType runType; @@ -226,6 +226,9 @@ public: GLRenderManager(); ~GLRenderManager(); + GLRenderManager(GLRenderManager &) = delete; + GLRenderManager &operator=(GLRenderManager &) = delete; + void SetInvalidationCallback(InvalidationCallback callback) { invalidationCallback_ = callback; } @@ -859,7 +862,7 @@ private: GLRStep *curRenderStep_ = nullptr; std::vector steps_; - std::vector initSteps_; + FastVec initSteps_; // Execution time state bool run_ = true; diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 047f32129d..b2942d2ac8 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -47,9 +47,11 @@ #endif #include "Common/Data/Collections/TinySet.h" +#include "Common/Data/Collections/FastVec.h" #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Data/Text/Parsers.h" #include "Common/Data/Text/WrapText.h" +#include "Common/Data/Collections/FastVec.h" #include "Common/Data/Encoding/Utf8.h" #include "Common/File/Path.h" #include "Common/Input/InputState.h" @@ -365,6 +367,25 @@ bool TestTinySet() { return true; } +bool TestFastVec() { + FastVec a; + EXPECT_EQ_INT((int)a.size(), 0); + a.push_back(1); + EXPECT_EQ_INT((int)a.size(), 1); + a.push_back(2); + EXPECT_EQ_INT((int)a.size(), 2); + FastVec b; + b.push_back(8); + b.push_back(9); + b.push_back(10); + EXPECT_EQ_INT((int)b.size(), 3); + for (int i = 0; i < 100; i++) { + b.push_back(33); + } + EXPECT_EQ_INT((int)b.size(), 103); + return true; +} + bool TestVFPUSinCos() { float sine, cosine; // Needed for VFPU tables. @@ -977,6 +998,7 @@ TestItem availableTests[] = { TEST_ITEM(ThreadManager), TEST_ITEM(WrapText), TEST_ITEM(TinySet), + TEST_ITEM(FastVec), TEST_ITEM(SmallDataConvert), TEST_ITEM(DepthMath), TEST_ITEM(InputMapping),