| /* | |
| * Copyright 2015 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef GrMesh_DEFINED | |
| #define GrMesh_DEFINED | |
| #include "GrBuffer.h" | |
| #include "GrPendingIOResource.h" | |
| class GrPrimitiveProcessor; | |
| /** | |
| * Used to communicate index and vertex buffers, counts, and offsets for a draw from GrOp to | |
| * GrGpu. It also holds the primitive type for the draw. TODO: Consider moving ownership of this | |
| * and draw-issuing responsibility to GrPrimitiveProcessor. The rest of the vertex info lives there | |
| * already (stride, attribute mappings). | |
| */ | |
| class GrMesh { | |
| public: | |
| GrMesh(GrPrimitiveType primitiveType = GrPrimitiveType::kTriangles) | |
| : fPrimitiveType(primitiveType), fBaseVertex(0) { | |
| SkDEBUGCODE(fNonIndexNonInstanceData.fVertexCount = -1;) | |
| } | |
| void setPrimitiveType(GrPrimitiveType type) { fPrimitiveType = type; } | |
| GrPrimitiveType primitiveType() const { return fPrimitiveType; } | |
| bool isIndexed() const { return SkToBool(fIndexBuffer.get()); } | |
| bool isInstanced() const { return SkToBool(fInstanceBuffer.get()); } | |
| bool hasVertexData() const { return SkToBool(fVertexBuffer.get()); } | |
| void setNonIndexedNonInstanced(int vertexCount); | |
| void setIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex, | |
| uint16_t minIndexValue, uint16_t maxIndexValue, GrPrimitiveRestart); | |
| void setIndexedPatterned(const GrBuffer* indexBuffer, int indexCount, int vertexCount, | |
| int patternRepeatCount, int maxPatternRepetitionsInIndexBuffer); | |
| void setInstanced(const GrBuffer* instanceBuffer, int instanceCount, int baseInstance, | |
| int vertexCount); | |
| void setIndexedInstanced(const GrBuffer* indexBuffer, int indexCount, | |
| const GrBuffer* instanceBuffer, int instanceCount, int baseInstance, | |
| GrPrimitiveRestart); | |
| void setVertexData(const GrBuffer* vertexBuffer, int baseVertex = 0); | |
| class SendToGpuImpl { | |
| public: | |
| virtual void sendMeshToGpu(GrPrimitiveType, const GrBuffer* vertexBuffer, int vertexCount, | |
| int baseVertex) = 0; | |
| virtual void sendIndexedMeshToGpu(GrPrimitiveType, const GrBuffer* indexBuffer, | |
| int indexCount, int baseIndex, uint16_t minIndexValue, | |
| uint16_t maxIndexValue, const GrBuffer* vertexBuffer, | |
| int baseVertex, GrPrimitiveRestart) = 0; | |
| virtual void sendInstancedMeshToGpu(GrPrimitiveType, const GrBuffer* vertexBuffer, | |
| int vertexCount, int baseVertex, | |
| const GrBuffer* instanceBuffer, int instanceCount, | |
| int baseInstance) = 0; | |
| virtual void sendIndexedInstancedMeshToGpu(GrPrimitiveType, const GrBuffer* indexBuffer, | |
| int indexCount, int baseIndex, | |
| const GrBuffer* vertexBuffer, int baseVertex, | |
| const GrBuffer* instanceBuffer, | |
| int instanceCount, int baseInstance, | |
| GrPrimitiveRestart) = 0; | |
| virtual ~SendToGpuImpl() {} | |
| }; | |
| void sendToGpu(SendToGpuImpl*) const; | |
| private: | |
| using PendingBuffer = GrPendingIOResource<const GrBuffer, kRead_GrIOType>; | |
| GrPrimitiveType fPrimitiveType; | |
| PendingBuffer fIndexBuffer; | |
| PendingBuffer fInstanceBuffer; | |
| PendingBuffer fVertexBuffer; | |
| int fBaseVertex; | |
| GrPrimitiveRestart fPrimitiveRestart; | |
| union { | |
| struct { // When fIndexBuffer == nullptr and fInstanceBuffer == nullptr. | |
| int fVertexCount; | |
| } fNonIndexNonInstanceData; | |
| struct { // When fIndexBuffer != nullptr and fInstanceBuffer == nullptr. | |
| struct { | |
| int fIndexCount; | |
| int fPatternRepeatCount; | |
| } fIndexData; | |
| union { | |
| struct { // When fPatternRepeatCount == 0. | |
| int fBaseIndex; | |
| uint16_t fMinIndexValue; | |
| uint16_t fMaxIndexValue; | |
| } fNonPatternIndexData; | |
| struct { // When fPatternRepeatCount != 0. | |
| int fVertexCount; | |
| int fMaxPatternRepetitionsInIndexBuffer; | |
| } fPatternData; | |
| }; | |
| }; | |
| struct { // When fInstanceBuffer != nullptr. | |
| struct { | |
| int fInstanceCount; | |
| int fBaseInstance; | |
| } fInstanceData; | |
| union { // When fIndexBuffer == nullptr. | |
| struct { | |
| int fVertexCount; | |
| } fInstanceNonIndexData; | |
| struct { // When fIndexBuffer != nullptr. | |
| int fIndexCount; | |
| } fInstanceIndexData; | |
| }; | |
| }; | |
| }; | |
| }; | |
| inline void GrMesh::setNonIndexedNonInstanced(int vertexCount) { | |
| fIndexBuffer.reset(nullptr); | |
| fInstanceBuffer.reset(nullptr); | |
| fNonIndexNonInstanceData.fVertexCount = vertexCount; | |
| fPrimitiveRestart = GrPrimitiveRestart::kNo; | |
| } | |
| inline void GrMesh::setIndexed(const GrBuffer* indexBuffer, int indexCount, int baseIndex, | |
| uint16_t minIndexValue, uint16_t maxIndexValue, | |
| GrPrimitiveRestart primitiveRestart) { | |
| SkASSERT(indexBuffer); | |
| SkASSERT(indexCount >= 1); | |
| SkASSERT(baseIndex >= 0); | |
| SkASSERT(maxIndexValue >= minIndexValue); | |
| fIndexBuffer.reset(indexBuffer); | |
| fInstanceBuffer.reset(nullptr); | |
| fIndexData.fIndexCount = indexCount; | |
| fIndexData.fPatternRepeatCount = 0; | |
| fNonPatternIndexData.fBaseIndex = baseIndex; | |
| fNonPatternIndexData.fMinIndexValue = minIndexValue; | |
| fNonPatternIndexData.fMaxIndexValue = maxIndexValue; | |
| fPrimitiveRestart = primitiveRestart; | |
| } | |
| inline void GrMesh::setIndexedPatterned(const GrBuffer* indexBuffer, int indexCount, | |
| int vertexCount, int patternRepeatCount, | |
| int maxPatternRepetitionsInIndexBuffer) { | |
| SkASSERT(indexBuffer); | |
| SkASSERT(indexCount >= 1); | |
| SkASSERT(vertexCount >= 1); | |
| SkASSERT(patternRepeatCount >= 1); | |
| SkASSERT(maxPatternRepetitionsInIndexBuffer >= 1); | |
| fIndexBuffer.reset(indexBuffer); | |
| fInstanceBuffer.reset(nullptr); | |
| fIndexData.fIndexCount = indexCount; | |
| fIndexData.fPatternRepeatCount = patternRepeatCount; | |
| fPatternData.fVertexCount = vertexCount; | |
| fPatternData.fMaxPatternRepetitionsInIndexBuffer = maxPatternRepetitionsInIndexBuffer; | |
| fPrimitiveRestart = GrPrimitiveRestart::kNo; | |
| } | |
| inline void GrMesh::setInstanced(const GrBuffer* instanceBuffer, int instanceCount, | |
| int baseInstance, int vertexCount) { | |
| SkASSERT(instanceBuffer); | |
| SkASSERT(instanceCount >= 1); | |
| SkASSERT(baseInstance >= 0); | |
| fIndexBuffer.reset(nullptr); | |
| fInstanceBuffer.reset(instanceBuffer); | |
| fInstanceData.fInstanceCount = instanceCount; | |
| fInstanceData.fBaseInstance = baseInstance; | |
| fInstanceNonIndexData.fVertexCount = vertexCount; | |
| fPrimitiveRestart = GrPrimitiveRestart::kNo; | |
| } | |
| inline void GrMesh::setIndexedInstanced(const GrBuffer* indexBuffer, int indexCount, | |
| const GrBuffer* instanceBuffer, int instanceCount, | |
| int baseInstance, GrPrimitiveRestart primitiveRestart) { | |
| SkASSERT(indexBuffer); | |
| SkASSERT(indexCount >= 1); | |
| SkASSERT(instanceBuffer); | |
| SkASSERT(instanceCount >= 1); | |
| SkASSERT(baseInstance >= 0); | |
| fIndexBuffer.reset(indexBuffer); | |
| fInstanceBuffer.reset(instanceBuffer); | |
| fInstanceData.fInstanceCount = instanceCount; | |
| fInstanceData.fBaseInstance = baseInstance; | |
| fInstanceIndexData.fIndexCount = indexCount; | |
| fPrimitiveRestart = primitiveRestart; | |
| } | |
| inline void GrMesh::setVertexData(const GrBuffer* vertexBuffer, int baseVertex) { | |
| SkASSERT(baseVertex >= 0); | |
| fVertexBuffer.reset(vertexBuffer); | |
| fBaseVertex = baseVertex; | |
| } | |
| inline void GrMesh::sendToGpu(SendToGpuImpl* impl) const { | |
| if (this->isInstanced()) { | |
| if (!this->isIndexed()) { | |
| impl->sendInstancedMeshToGpu(fPrimitiveType, fVertexBuffer.get(), | |
| fInstanceNonIndexData.fVertexCount, fBaseVertex, | |
| fInstanceBuffer.get(), fInstanceData.fInstanceCount, | |
| fInstanceData.fBaseInstance); | |
| } else { | |
| impl->sendIndexedInstancedMeshToGpu( | |
| fPrimitiveType, fIndexBuffer.get(), fInstanceIndexData.fIndexCount, 0, | |
| fVertexBuffer.get(), fBaseVertex, fInstanceBuffer.get(), | |
| fInstanceData.fInstanceCount, fInstanceData.fBaseInstance, fPrimitiveRestart); | |
| } | |
| return; | |
| } | |
| if (!this->isIndexed()) { | |
| SkASSERT(fNonIndexNonInstanceData.fVertexCount > 0); | |
| impl->sendMeshToGpu(fPrimitiveType, fVertexBuffer.get(), | |
| fNonIndexNonInstanceData.fVertexCount, fBaseVertex); | |
| return; | |
| } | |
| if (0 == fIndexData.fPatternRepeatCount) { | |
| impl->sendIndexedMeshToGpu(fPrimitiveType, fIndexBuffer.get(), fIndexData.fIndexCount, | |
| fNonPatternIndexData.fBaseIndex, | |
| fNonPatternIndexData.fMinIndexValue, | |
| fNonPatternIndexData.fMaxIndexValue, fVertexBuffer.get(), | |
| fBaseVertex, fPrimitiveRestart); | |
| return; | |
| } | |
| SkASSERT(fIndexData.fPatternRepeatCount > 0); | |
| int baseRepetition = 0; | |
| do { | |
| int repeatCount = SkTMin(fPatternData.fMaxPatternRepetitionsInIndexBuffer, | |
| fIndexData.fPatternRepeatCount - baseRepetition); | |
| // A patterned index buffer must contain indices in the range [0..vertexCount]. | |
| int minIndexValue = 0; | |
| int maxIndexValue = fPatternData.fVertexCount * repeatCount - 1; | |
| SkASSERT(fPrimitiveRestart == GrPrimitiveRestart::kNo); | |
| impl->sendIndexedMeshToGpu( | |
| fPrimitiveType, fIndexBuffer.get(), fIndexData.fIndexCount * repeatCount, 0, | |
| minIndexValue, maxIndexValue, fVertexBuffer.get(), | |
| fBaseVertex + fPatternData.fVertexCount * baseRepetition, GrPrimitiveRestart::kNo); | |
| baseRepetition += repeatCount; | |
| } while (baseRepetition < fIndexData.fPatternRepeatCount); | |
| } | |
| #endif |