1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright (c) 2012-2017 VideoStitch SAS
// Copyright (c) 2018 stitchEm
#pragma once
#include "buffer.hpp"
#include <cassert>
namespace VideoStitch {
namespace GPU {
/**
* A UniqueBuffer is a smart wrapper for GPU Buffers that retains sole ownership
* of GPU resources and frees the GPU memory when the UniqueBuffer goes out of scope.
* No two UniqueBuffer instances can manage the same GPU memory.
*
* Reminder: the GPU Buffer is a dumb reference around GPU memory which requires manual memory
* management (with alloc and release).
*
* The UniqueBuffer facilitates the memory management by automating the destruction,
* similar to unique_ptr.
*/
template <typename T>
class UniqueBuffer {
public:
UniqueBuffer() : buf() {}
/**
* Takes ownership of the GPU Buffer, to release its memory when UniqueBuffer is destroyed
*/
explicit UniqueBuffer(Buffer<T> buf) : buf(buf) {}
UniqueBuffer(const UniqueBuffer& other) = delete;
UniqueBuffer(UniqueBuffer&& other) {
if (hasValidBuffer) {
buf.release();
}
buf = other.buf;
hasValidBuffer = other.hasValidBuffer;
other.buf = Buffer<T>();
other.hasValidBuffer = false;
}
/** Move assignment operator */
UniqueBuffer& operator=(UniqueBuffer&& other) {
if (hasValidBuffer) {
buf.release();
}
this->buf = other.buf;
this->hasValidBuffer = other.hasValidBuffer;
other.buf = Buffer<T>();
other.hasValidBuffer = false;
return *this;
}
operator bool() const { return buf.byteSize() > 0; }
/**
* Returns the GPU Buffer but keeps ownership
*/
Buffer<T> borrow() const { return buf; }
/**
* Returns the const GPU Buffer but keeps ownership
*/
Buffer<const T> borrow_const() const { return buf; }
/**
* Releases ownership
*/
Buffer<T> releaseOwnership() {
Buffer<T> res = buf;
buf = Buffer<T>();
hasValidBuffer = false;
return res;
}
/**
* Allocates a GPU Buffer for the first time after creating the object.
* Allocating a UniqueBuffer that is already allocated is an error, use .recreate()
* @param size Size (in elements of type T).
* @param name pool name.
*/
Status alloc(size_t numElements, const char* name) {
if (hasValidBuffer) {
return {Origin::GPU, ErrType::ImplementationError,
"Attempting to allocate a UniqueBuffer that is already allocated"};
}
PotentialValue<Buffer<T>> potBuf = Buffer<T>::allocate(numElements, name);
if (potBuf.ok()) {
buf = potBuf.value();
hasValidBuffer = true;
}
return potBuf.status();
}
/**
* Replaces current buffer with a new GPU Buffer of different size
* Calling recreate on an uninitialized unique buffer is a valid operation.
*
* Note: the content of the GPU memory is not preserved, a new buffer is created
*
* @param size Size (in elements of type T).
* @param name pool name.
*/
Status recreate(size_t numElements, const char* name) {
if (hasValidBuffer) {
buf.release();
hasValidBuffer = false;
}
return this->alloc(numElements, name);
}
~UniqueBuffer() {
if (hasValidBuffer) {
buf.release();
}
}
private:
Buffer<T> buf;
bool hasValidBuffer = false;
};
template <typename T>
class PotentialUniqueBuffer : public PotentialValue<UniqueBuffer<T>> {
public:
PotentialUniqueBuffer(const Status& status) : PotentialValue<UniqueBuffer<T>>(status) {}
PotentialUniqueBuffer(UniqueBuffer<T>&& value) : PotentialValue<UniqueBuffer<T>>(std::move(value)) {}
PotentialUniqueBuffer(const PotentialUniqueBuffer&& other) = delete;
PotentialUniqueBuffer(PotentialUniqueBuffer&& other) : PotentialValue<UniqueBuffer<T>>(std::move(other)) {}
Buffer<T> borrow() const {
const UniqueBuffer<T>& ub = this->value_;
return ub.borrow();
}
Buffer<const T> borrow_const() const {
const UniqueBuffer<T>& ub = this->value_;
return ub.borrow_const();
}
Buffer<T> releaseOwnership() {
UniqueBuffer<T>& ub = this->value_;
return ub.releaseOwnership();
}
};
/**
* Helper function to create UniqueBuffers with a single call
* Name doesn't start with 'allocate' to make it stand out in the source code
* as it has a different lifetime
*/
template <typename T>
PotentialUniqueBuffer<T> uniqueBuffer(size_t numElements, const char* name) {
UniqueBuffer<T> buf;
const Status allocationStatus = buf.alloc(numElements, name);
if (!allocationStatus.ok()) {
return PotentialUniqueBuffer<T>(allocationStatus);
}
return PotentialUniqueBuffer<T>(std::move(buf));
}
} // namespace GPU
} // namespace VideoStitch