/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrGLVertexArray.h" #include "GrGLBuffer.h" #include "GrGLGpu.h" struct AttribLayout { bool fNormalized; // Only used by floating point types. uint8_t fCount; uint16_t fType; }; GR_STATIC_ASSERT(4 == sizeof(AttribLayout)); static AttribLayout attrib_layout(GrVertexAttribType type) { switch (type) { case kFloat_GrVertexAttribType: return {false, 1, GR_GL_FLOAT}; case kFloat2_GrVertexAttribType: return {false, 2, GR_GL_FLOAT}; case kFloat3_GrVertexAttribType: return {false, 3, GR_GL_FLOAT}; case kFloat4_GrVertexAttribType: return {false, 4, GR_GL_FLOAT}; case kHalf_GrVertexAttribType: return {false, 1, GR_GL_FLOAT}; case kHalf2_GrVertexAttribType: return {false, 2, GR_GL_FLOAT}; case kHalf3_GrVertexAttribType: return {false, 3, GR_GL_FLOAT}; case kHalf4_GrVertexAttribType: return {false, 4, GR_GL_FLOAT}; case kInt2_GrVertexAttribType: return {false, 2, GR_GL_INT}; case kInt3_GrVertexAttribType: return {false, 3, GR_GL_INT}; case kInt4_GrVertexAttribType: return {false, 4, GR_GL_INT}; case kUByte_norm_GrVertexAttribType: return {true, 1, GR_GL_UNSIGNED_BYTE}; case kUByte4_norm_GrVertexAttribType: return {true, 4, GR_GL_UNSIGNED_BYTE}; case kShort2_GrVertexAttribType: return {false, 2, GR_GL_SHORT}; case kUShort2_GrVertexAttribType: return {false, 2, GR_GL_UNSIGNED_SHORT}; case kUShort2_norm_GrVertexAttribType: return {true, 2, GR_GL_UNSIGNED_SHORT}; case kInt_GrVertexAttribType: return {false, 1, GR_GL_INT}; case kUint_GrVertexAttribType: return {false, 1, GR_GL_UNSIGNED_INT}; } SK_ABORT("Unknown vertex attrib type"); return {false, 0, 0}; }; static bool GrVertexAttribTypeIsIntType(const GrShaderCaps* shaderCaps, GrVertexAttribType type) { switch (type) { case kFloat_GrVertexAttribType: return false; case kFloat2_GrVertexAttribType: return false; case kFloat3_GrVertexAttribType: return false; case kFloat4_GrVertexAttribType: return false; case kHalf_GrVertexAttribType: return false; case kHalf2_GrVertexAttribType: return false; case kHalf3_GrVertexAttribType: return false; case kHalf4_GrVertexAttribType: return false; case kInt2_GrVertexAttribType: return true; case kInt3_GrVertexAttribType: return true; case kInt4_GrVertexAttribType: return true; case kUByte_norm_GrVertexAttribType: return false; case kUByte4_norm_GrVertexAttribType: return false; case kShort2_GrVertexAttribType: return true; case kUShort2_GrVertexAttribType: return shaderCaps->integerSupport(); // FIXME: caller should handle this. case kUShort2_norm_GrVertexAttribType: return false; case kInt_GrVertexAttribType: return true; case kUint_GrVertexAttribType: return true; } SK_ABORT("Unexpected attribute type"); return false; } void GrGLAttribArrayState::set(GrGLGpu* gpu, int index, const GrBuffer* vertexBuffer, GrVertexAttribType type, GrGLsizei stride, size_t offsetInBytes, int divisor) { SkASSERT(index >= 0 && index < fAttribArrayStates.count()); SkASSERT(0 == divisor || gpu->caps()->instanceAttribSupport()); AttribArrayState* array = &fAttribArrayStates[index]; if (array->fVertexBufferUniqueID != vertexBuffer->uniqueID() || array->fType != type || array->fStride != stride || array->fOffset != offsetInBytes) { gpu->bindBuffer(kVertex_GrBufferType, vertexBuffer); const AttribLayout& layout = attrib_layout(type); const GrGLvoid* offsetAsPtr = reinterpret_cast<const GrGLvoid*>(offsetInBytes); if (!GrVertexAttribTypeIsIntType(gpu->caps()->shaderCaps(), type)) { GR_GL_CALL(gpu->glInterface(), VertexAttribPointer(index, layout.fCount, layout.fType, layout.fNormalized, stride, offsetAsPtr)); } else { SkASSERT(gpu->caps()->shaderCaps()->integerSupport()); SkASSERT(!layout.fNormalized); GR_GL_CALL(gpu->glInterface(), VertexAttribIPointer(index, layout.fCount, layout.fType, stride, offsetAsPtr)); } array->fVertexBufferUniqueID = vertexBuffer->uniqueID(); array->fType = type; array->fStride = stride; array->fOffset = offsetInBytes; } if (gpu->caps()->instanceAttribSupport() && array->fDivisor != divisor) { SkASSERT(0 == divisor || 1 == divisor); // not necessarily a requirement but what we expect. GR_GL_CALL(gpu->glInterface(), VertexAttribDivisor(index, divisor)); array->fDivisor = divisor; } } void GrGLAttribArrayState::enableVertexArrays(const GrGLGpu* gpu, int enabledCount, EnablePrimitiveRestart enablePrimitiveRestart) { SkASSERT(enabledCount <= fAttribArrayStates.count()); if (!fEnableStateIsValid || enabledCount != fNumEnabledArrays) { int firstIdxToEnable = fEnableStateIsValid ? fNumEnabledArrays : 0; for (int i = firstIdxToEnable; i < enabledCount; ++i) { GR_GL_CALL(gpu->glInterface(), EnableVertexAttribArray(i)); } int endIdxToDisable = fEnableStateIsValid ? fNumEnabledArrays : fAttribArrayStates.count(); for (int i = enabledCount; i < endIdxToDisable; ++i) { GR_GL_CALL(gpu->glInterface(), DisableVertexAttribArray(i)); } fNumEnabledArrays = enabledCount; } SkASSERT(EnablePrimitiveRestart::kNo == enablePrimitiveRestart || gpu->caps()->usePrimitiveRestart()); if (gpu->caps()->usePrimitiveRestart() && (!fEnableStateIsValid || enablePrimitiveRestart != fPrimitiveRestartEnabled)) { if (EnablePrimitiveRestart::kYes == enablePrimitiveRestart) { GR_GL_CALL(gpu->glInterface(), Enable(GR_GL_PRIMITIVE_RESTART_FIXED_INDEX)); } else { GR_GL_CALL(gpu->glInterface(), Disable(GR_GL_PRIMITIVE_RESTART_FIXED_INDEX)); } fPrimitiveRestartEnabled = enablePrimitiveRestart; } fEnableStateIsValid = true; } /////////////////////////////////////////////////////////////////////////////////////////////////// GrGLVertexArray::GrGLVertexArray(GrGLint id, int attribCount) : fID(id) , fAttribArrays(attribCount) , fIndexBufferUniqueID(SK_InvalidUniqueID) { } GrGLAttribArrayState* GrGLVertexArray::bind(GrGLGpu* gpu) { if (0 == fID) { return nullptr; } gpu->bindVertexArray(fID); return &fAttribArrays; } GrGLAttribArrayState* GrGLVertexArray::bindWithIndexBuffer(GrGLGpu* gpu, const GrBuffer* ibuff) { GrGLAttribArrayState* state = this->bind(gpu); if (state && fIndexBufferUniqueID != ibuff->uniqueID()) { if (ibuff->isCPUBacked()) { GR_GL_CALL(gpu->glInterface(), BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, 0)); } else { const GrGLBuffer* glBuffer = static_cast<const GrGLBuffer*>(ibuff); GR_GL_CALL(gpu->glInterface(), BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, glBuffer->bufferID())); } fIndexBufferUniqueID = ibuff->uniqueID(); } return state; } void GrGLVertexArray::invalidateCachedState() { fAttribArrays.invalidate(); fIndexBufferUniqueID.makeInvalid(); }