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
12namespace 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}}