1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "BsGLVertexArrayObjectManager.h" |
4 | #include "BsGLVertexBuffer.h" |
5 | #include "RenderAPI/BsVertexDeclaration.h" |
6 | #include "GLSL/BsGLSLGpuProgram.h" |
7 | #include "BsGLHardwareBufferManager.h" |
8 | #include "Profiling/BsRenderStats.h" |
9 | |
10 | #define VBO_BUFFER_OFFSET(i) ((char *)NULL + (i)) |
11 | |
12 | namespace bs { namespace ct |
13 | { |
14 | GLVertexArrayObject::GLVertexArrayObject(GLuint handle, UINT64 vertexProgramId, |
15 | GLVertexBuffer** attachedBuffers, UINT32 numBuffers) |
16 | :mHandle(handle), mVertProgId(vertexProgramId), mAttachedBuffers(attachedBuffers), mNumBuffers(numBuffers) |
17 | { } |
18 | |
19 | ::std::size_t GLVertexArrayObject::Hash::operator()(const GLVertexArrayObject &vao) const |
20 | { |
21 | std::size_t seed = 0; |
22 | bs_hash_combine(seed, vao.mVertProgId); |
23 | |
24 | for (UINT32 i = 0; i < vao.mNumBuffers; i++) |
25 | bs_hash_combine(seed, vao.mAttachedBuffers[i]->getGLBufferId()); |
26 | |
27 | return seed; |
28 | } |
29 | |
30 | bool GLVertexArrayObject::Equal::operator()(const GLVertexArrayObject &a, const GLVertexArrayObject &b) const |
31 | { |
32 | if (a.mVertProgId != b.mVertProgId) |
33 | return false; |
34 | |
35 | if (a.mNumBuffers != b.mNumBuffers) |
36 | return false; |
37 | |
38 | for (UINT32 i = 0; i < a.mNumBuffers; i++) |
39 | { |
40 | if (a.mAttachedBuffers[i]->getGLBufferId() != b.mAttachedBuffers[i]->getGLBufferId()) |
41 | return false; |
42 | } |
43 | |
44 | return true; |
45 | } |
46 | |
47 | bool GLVertexArrayObject::operator== (const GLVertexArrayObject& obj) |
48 | { |
49 | if (mVertProgId != obj.mVertProgId) |
50 | return false; |
51 | |
52 | if (mNumBuffers != obj.mNumBuffers) |
53 | return false; |
54 | |
55 | for (UINT32 i = 0; i < mNumBuffers; i++) |
56 | { |
57 | if (mAttachedBuffers[i]->getGLBufferId() != obj.mAttachedBuffers[i]->getGLBufferId()) |
58 | return false; |
59 | } |
60 | |
61 | return true; |
62 | } |
63 | |
64 | bool GLVertexArrayObject::operator!= (const GLVertexArrayObject& obj) |
65 | { |
66 | return !operator==(obj); |
67 | } |
68 | |
69 | GLVertexArrayObjectManager::~GLVertexArrayObjectManager() |
70 | { |
71 | assert(mVAObjects.size() == 0 && "VertexArrayObjectManager getting shut down but not all VA objects were released." ); |
72 | } |
73 | |
74 | const GLVertexArrayObject& GLVertexArrayObjectManager::getVAO(const SPtr<GLSLGpuProgram>& vertexProgram, |
75 | const SPtr<VertexDeclaration>& vertexDecl, const std::array<SPtr<VertexBuffer>, 32>& boundBuffers) |
76 | { |
77 | UINT16 maxStreamIdx = 0; |
78 | const Vector<VertexElement>& decl = vertexDecl->getProperties().getElements(); |
79 | for (auto& elem : decl) |
80 | maxStreamIdx = std::max(maxStreamIdx, elem.getStreamIdx()); |
81 | |
82 | UINT32 numStreams = maxStreamIdx + 1; |
83 | UINT32 numUsedBuffers = 0; |
84 | INT32* streamToSeqIdx = bs_stack_alloc<INT32>(numStreams); |
85 | GLVertexBuffer** usedBuffers = bs_stack_alloc<GLVertexBuffer*>((UINT32)boundBuffers.size()); |
86 | |
87 | memset(usedBuffers, 0, (UINT32)boundBuffers.size() * sizeof(GLVertexBuffer*)); |
88 | |
89 | for (UINT32 i = 0; i < numStreams; i++) |
90 | streamToSeqIdx[i] = -1; |
91 | |
92 | for (auto& elem : decl) |
93 | { |
94 | UINT16 streamIdx = elem.getStreamIdx(); |
95 | if (streamIdx >= (UINT32)boundBuffers.size()) |
96 | continue; |
97 | |
98 | if (streamToSeqIdx[streamIdx] != -1) // Already visited |
99 | continue; |
100 | |
101 | SPtr<VertexBuffer> vertexBuffer = boundBuffers[streamIdx]; |
102 | streamToSeqIdx[streamIdx] = (INT32)numUsedBuffers; |
103 | |
104 | if (vertexBuffer != nullptr) |
105 | usedBuffers[numUsedBuffers] = static_cast<GLVertexBuffer*>(vertexBuffer.get()); |
106 | else |
107 | usedBuffers[numUsedBuffers] = nullptr; |
108 | |
109 | numUsedBuffers++; |
110 | } |
111 | |
112 | GLVertexArrayObject wantedVAO(0, vertexProgram->getGLHandle(), usedBuffers, numUsedBuffers); |
113 | |
114 | auto findIter = mVAObjects.find(wantedVAO); |
115 | if (findIter != mVAObjects.end()) |
116 | { |
117 | bs_stack_free(usedBuffers); |
118 | bs_stack_free(streamToSeqIdx); |
119 | |
120 | return *findIter; // Found existing, return that |
121 | } |
122 | |
123 | // Need to create new VAO |
124 | const Vector<VertexElement>& inputAttributes = vertexProgram->getInputDeclaration()->getProperties().getElements(); |
125 | |
126 | glGenVertexArrays(1, &wantedVAO.mHandle); |
127 | BS_CHECK_GL_ERROR(); |
128 | |
129 | glBindVertexArray(wantedVAO.mHandle); |
130 | BS_CHECK_GL_ERROR(); |
131 | |
132 | for (auto& elem : decl) |
133 | { |
134 | UINT16 streamIdx = elem.getStreamIdx(); |
135 | INT32 seqIdx = streamToSeqIdx[streamIdx]; |
136 | |
137 | if (seqIdx == -1) |
138 | continue; |
139 | |
140 | bool foundSemantic = false; |
141 | GLint attribLocation = 0; |
142 | for (auto iter = inputAttributes.begin(); iter != inputAttributes.end(); ++iter) |
143 | { |
144 | if (iter->getSemantic() == elem.getSemantic() && iter->getSemanticIdx() == elem.getSemanticIdx()) |
145 | { |
146 | foundSemantic = true; |
147 | attribLocation = iter->getOffset(); |
148 | break; |
149 | } |
150 | } |
151 | |
152 | if (!foundSemantic) // Shader needs to have a matching input attribute, otherwise we skip it |
153 | continue; |
154 | |
155 | // TODO - We might also want to check the size of input and buffer, and make sure they match? Or does OpenGL handle that internally? |
156 | |
157 | GLVertexBuffer* vertexBuffer = usedBuffers[seqIdx]; |
158 | const VertexBufferProperties& vbProps = vertexBuffer->getProperties(); |
159 | |
160 | glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer->getGLBufferId()); |
161 | BS_CHECK_GL_ERROR(); |
162 | |
163 | void* bufferData = VBO_BUFFER_OFFSET(elem.getOffset()); |
164 | |
165 | UINT16 typeCount = VertexElement::getTypeCount(elem.getType()); |
166 | GLenum glType = GLHardwareBufferManager::getGLType(elem.getType()); |
167 | bool isInteger = glType == GL_SHORT || glType == GL_UNSIGNED_SHORT || glType == GL_INT |
168 | || glType == GL_UNSIGNED_INT || glType == GL_UNSIGNED_BYTE; |
169 | |
170 | GLboolean normalized = GL_FALSE; |
171 | switch (elem.getType()) |
172 | { |
173 | case VET_COLOR: |
174 | case VET_COLOR_ABGR: |
175 | case VET_COLOR_ARGB: |
176 | case VET_UBYTE4_NORM: |
177 | normalized = GL_TRUE; |
178 | isInteger = false; |
179 | break; |
180 | default: |
181 | break; |
182 | } |
183 | |
184 | GLsizei vertexSize = static_cast<GLsizei>(vbProps.getVertexSize()); |
185 | if(isInteger) |
186 | { |
187 | glVertexAttribIPointer(attribLocation, typeCount, glType, vertexSize, bufferData); |
188 | BS_CHECK_GL_ERROR(); |
189 | } |
190 | else |
191 | { |
192 | glVertexAttribPointer(attribLocation, typeCount, glType, normalized, vertexSize, bufferData); |
193 | BS_CHECK_GL_ERROR(); |
194 | } |
195 | |
196 | glVertexAttribDivisor(attribLocation, elem.getInstanceStepRate()); |
197 | BS_CHECK_GL_ERROR(); |
198 | |
199 | glEnableVertexAttribArray(attribLocation); |
200 | BS_CHECK_GL_ERROR(); |
201 | } |
202 | |
203 | wantedVAO.mAttachedBuffers = (GLVertexBuffer**)bs_alloc(numUsedBuffers * sizeof(GLVertexBuffer*)); |
204 | for (UINT32 i = 0; i < numUsedBuffers; i++) |
205 | { |
206 | wantedVAO.mAttachedBuffers[i] = usedBuffers[i]; |
207 | usedBuffers[i]->registerVAO(wantedVAO); |
208 | } |
209 | |
210 | bs_stack_free(usedBuffers); |
211 | bs_stack_free(streamToSeqIdx); |
212 | |
213 | auto iter = mVAObjects.insert(wantedVAO); |
214 | |
215 | BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_VertexArrayObject); |
216 | return *iter.first; |
217 | } |
218 | |
219 | // Note: This must receieve a copy and not a ref because original will be destroyed |
220 | void GLVertexArrayObjectManager::notifyBufferDestroyed(GLVertexArrayObject vao) |
221 | { |
222 | mVAObjects.erase(vao); |
223 | |
224 | for (UINT32 i = 0; i < vao.mNumBuffers; i++) |
225 | { |
226 | vao.mAttachedBuffers[i]->unregisterVAO(vao); |
227 | } |
228 | |
229 | glDeleteVertexArrays(1, &vao.mHandle); |
230 | BS_CHECK_GL_ERROR(); |
231 | |
232 | bs_free(vao.mAttachedBuffers); |
233 | |
234 | BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_VertexArrayObject); |
235 | } |
236 | }} |