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