CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/GPU/OpenGL/GLMemory.h
Views: 1401
1
#pragma once
2
3
#include <vector>
4
#include <cstdint>
5
#include <cstring>
6
7
#include "Common/GPU/GPUBackendCommon.h"
8
#include "Common/GPU/OpenGL/GLCommon.h"
9
#include "Common/Log.h"
10
11
enum class GLBufferStrategy {
12
SUBDATA = 0,
13
14
MASK_FLUSH = 0x10,
15
MASK_INVALIDATE = 0x20,
16
17
// Map/unmap the buffer each frame.
18
FRAME_UNMAP = 1,
19
// Map/unmap and also invalidate the buffer on map.
20
INVALIDATE_UNMAP = MASK_INVALIDATE,
21
// Map/unmap and explicitly flushed changed ranges.
22
FLUSH_UNMAP = MASK_FLUSH,
23
// Map/unmap, invalidate on map, and explicit flush.
24
FLUSH_INVALIDATE_UNMAP = MASK_FLUSH | MASK_INVALIDATE,
25
};
26
27
static inline int operator &(const GLBufferStrategy &lhs, const GLBufferStrategy &rhs) {
28
return (int)lhs & (int)rhs;
29
}
30
31
class GLRBuffer {
32
public:
33
GLRBuffer(GLuint target, size_t size) : target_(target), size_((int)size) {}
34
~GLRBuffer() {
35
if (buffer_) {
36
glDeleteBuffers(1, &buffer_);
37
}
38
}
39
40
void *Map(GLBufferStrategy strategy);
41
bool Unmap();
42
43
bool Mapped() const {
44
return mapped_;
45
}
46
47
GLuint buffer_ = 0;
48
GLuint target_;
49
int size_;
50
51
private:
52
bool mapped_ = false;
53
bool hasStorage_ = false;
54
};
55
56
class GLRenderManager;
57
58
// Similar to VulkanPushBuffer but is currently less efficient - it collects all the data in
59
// RAM then does a big memcpy/buffer upload at the end of the frame. This is at least a lot
60
// faster than the hundreds of buffer uploads or memory array buffers we used before.
61
// On modern GL we could avoid the copy using glBufferStorage but not sure it's worth the
62
// trouble.
63
// We need to manage the lifetime of this together with the other resources so its destructor
64
// runs on the render thread.
65
class GLPushBuffer : public GPUMemoryManager {
66
public:
67
friend class GLRenderManager;
68
69
struct BufInfo {
70
GLRBuffer *buffer = nullptr;
71
uint8_t *localMemory = nullptr;
72
uint8_t *deviceMemory = nullptr;
73
size_t flushOffset = 0;
74
size_t size;
75
};
76
77
GLPushBuffer(GLRenderManager *render, GLuint target, size_t size, const char *tag);
78
~GLPushBuffer();
79
80
void Reset() { offset_ = 0; }
81
82
void GetDebugString(char *buffer, size_t bufSize) const override;
83
84
const char *Name() const override { return tag_; }; // for sorting
85
86
// Utility for users of this class, not used internally.
87
enum { INVALID_OFFSET = 0xFFFFFFFF };
88
89
// Needs context in case of defragment.
90
void Begin() {
91
buf_ = 0;
92
offset_ = 0;
93
// Note: we must defrag because some buffers may be smaller than size_.
94
Defragment();
95
Map();
96
_dbg_assert_(writePtr_);
97
}
98
99
void BeginNoReset() {
100
Map();
101
}
102
103
void End() {
104
Unmap();
105
}
106
107
void Map();
108
void Unmap();
109
110
bool IsReady() const {
111
return writePtr_ != nullptr;
112
}
113
114
// Recommended - lets you write directly into the buffer through the returned pointer.
115
// If you didn't end up using all the memory you grabbed here, then before calling Allocate or Push
116
// again, call Rewind (see below).
117
uint8_t *Allocate(uint32_t numBytes, uint32_t alignment, GLRBuffer **buf, uint32_t *bindOffset) {
118
uint32_t offset = ((uint32_t)offset_ + alignment - 1) & ~(alignment - 1);
119
if (offset + numBytes <= size_) {
120
// Common path.
121
offset_ = offset + numBytes;
122
*buf = buffers_[buf_].buffer;
123
*bindOffset = offset;
124
return writePtr_ + offset;
125
}
126
127
NextBuffer(numBytes);
128
*bindOffset = 0;
129
*buf = buffers_[buf_].buffer;
130
// Need to mark the allocated range used in the new buffer. How did things work before this?
131
offset_ = numBytes;
132
return writePtr_;
133
}
134
135
// For convenience if all you'll do is to copy.
136
uint32_t Push(const void *data, uint32_t numBytes, int alignment, GLRBuffer **buf) {
137
uint32_t bindOffset;
138
uint8_t *ptr = Allocate(numBytes, alignment, buf, &bindOffset);
139
memcpy(ptr, data, numBytes);
140
return bindOffset;
141
}
142
143
uint8_t *GetPtr(uint32_t offset) {
144
return writePtr_ + offset;
145
}
146
147
// If you didn't use all of the previous allocation you just made (obviously can't be another one),
148
// you can return memory to the buffer by specifying the offset up until which you wrote data.
149
// Pass in the buffer you got last time. If that buffer has been filled already, no rewind can be safely done.
150
// (well technically would be possible but not worth the trouble).
151
void Rewind(GLRBuffer *buffer, uint32_t offset) {
152
if (buffer == buffers_[buf_].buffer) {
153
_dbg_assert_(offset != INVALID_OFFSET);
154
_dbg_assert_(offset <= offset_);
155
offset_ = offset;
156
}
157
}
158
159
size_t GetOffset() const { return offset_; }
160
size_t GetTotalSize() const;
161
162
void Destroy(bool onRenderThread);
163
void Flush();
164
165
protected:
166
void MapDevice(GLBufferStrategy strategy);
167
void UnmapDevice();
168
169
private:
170
void AddBuffer(); // asserts on failure
171
void NextBuffer(size_t minSize);
172
void Defragment();
173
174
GLRenderManager *render_;
175
std::vector<BufInfo> buffers_;
176
size_t buf_ = 0;
177
size_t offset_ = 0;
178
size_t size_ = 0;
179
uint8_t *writePtr_ = nullptr;
180
GLuint target_;
181
GLBufferStrategy strategy_ = GLBufferStrategy::SUBDATA;
182
const char *tag_;
183
};
184
185