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),