|  | /* | 
|  | * 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 "src/gpu/GrBuffer.h" | 
|  | #include "src/gpu/GrGpuBuffer.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()); } | 
|  | GrPrimitiveRestart primitiveRestart() const { | 
|  | return GrPrimitiveRestart(fFlags & Flags::kUsePrimitiveRestart); | 
|  | } | 
|  | bool isInstanced() const { return fFlags & Flags::kIsInstanced; } | 
|  | bool hasInstanceData() const { return SkToBool(fInstanceBuffer.get()); } | 
|  | bool hasVertexData() const { return SkToBool(fVertexBuffer.get()); } | 
|  |  | 
|  | void setNonIndexedNonInstanced(int vertexCount); | 
|  |  | 
|  | void setIndexed(sk_sp<const GrBuffer> indexBuffer, int indexCount, int baseIndex, | 
|  | uint16_t minIndexValue, uint16_t maxIndexValue, GrPrimitiveRestart); | 
|  | void setIndexedPatterned(sk_sp<const GrBuffer> indexBuffer, int indexCount, int vertexCount, | 
|  | int patternRepeatCount, int maxPatternRepetitionsInIndexBuffer); | 
|  |  | 
|  | void setInstanced(sk_sp<const GrBuffer> instanceBuffer, int instanceCount, int baseInstance, | 
|  | int vertexCount); | 
|  | void setIndexedInstanced(sk_sp<const GrBuffer> indexBuffer, int indexCount, | 
|  | sk_sp<const GrBuffer> instanceBuffer, int instanceCount, | 
|  | int baseInstance, GrPrimitiveRestart); | 
|  |  | 
|  | void setVertexData(sk_sp<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: | 
|  | enum class Flags { | 
|  | kNone = 0, | 
|  | kUsePrimitiveRestart = 1 << 0, | 
|  | kIsInstanced = 1 << 1, | 
|  | }; | 
|  |  | 
|  | GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(Flags); | 
|  | GR_STATIC_ASSERT(Flags(GrPrimitiveRestart::kNo) == Flags::kNone); | 
|  | GR_STATIC_ASSERT(Flags(GrPrimitiveRestart::kYes) == Flags::kUsePrimitiveRestart); | 
|  |  | 
|  | GrPrimitiveType fPrimitiveType; | 
|  | sk_sp<const GrBuffer> fIndexBuffer; | 
|  | sk_sp<const GrBuffer> fInstanceBuffer; | 
|  | sk_sp<const GrBuffer> fVertexBuffer; | 
|  | int fBaseVertex; | 
|  | Flags fFlags; | 
|  |  | 
|  | union { | 
|  | struct { // When fIndexBuffer == nullptr and isInstanced() == false. | 
|  | int   fVertexCount; | 
|  | } fNonIndexNonInstanceData; | 
|  |  | 
|  | struct { // When fIndexBuffer != nullptr and isInstanced() == false. | 
|  | 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 isInstanced() != false. | 
|  | struct { | 
|  | int   fInstanceCount; | 
|  | int   fBaseInstance; | 
|  | } fInstanceData; | 
|  |  | 
|  | union { // When fIndexBuffer == nullptr. | 
|  | struct { | 
|  | int   fVertexCount; | 
|  | } fInstanceNonIndexData; | 
|  |  | 
|  | struct { // When fIndexBuffer != nullptr. | 
|  | int   fIndexCount; | 
|  | } fInstanceIndexData; | 
|  | }; | 
|  | }; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | GR_MAKE_BITFIELD_CLASS_OPS(GrMesh::Flags); | 
|  |  | 
|  | inline void GrMesh::setNonIndexedNonInstanced(int vertexCount) { | 
|  | fIndexBuffer.reset(); | 
|  | fInstanceBuffer.reset(); | 
|  | fNonIndexNonInstanceData.fVertexCount = vertexCount; | 
|  | fFlags = Flags::kNone; | 
|  | } | 
|  |  | 
|  | inline void GrMesh::setIndexed(sk_sp<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 = std::move(indexBuffer); | 
|  | fInstanceBuffer.reset(); | 
|  | fIndexData.fIndexCount = indexCount; | 
|  | fIndexData.fPatternRepeatCount = 0; | 
|  | fNonPatternIndexData.fBaseIndex = baseIndex; | 
|  | fNonPatternIndexData.fMinIndexValue = minIndexValue; | 
|  | fNonPatternIndexData.fMaxIndexValue = maxIndexValue; | 
|  | fFlags = Flags(primitiveRestart); | 
|  | } | 
|  |  | 
|  | inline void GrMesh::setIndexedPatterned(sk_sp<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 = std::move(indexBuffer); | 
|  | fInstanceBuffer.reset(); | 
|  | fIndexData.fIndexCount = indexCount; | 
|  | fIndexData.fPatternRepeatCount = patternRepeatCount; | 
|  | fPatternData.fVertexCount = vertexCount; | 
|  | fPatternData.fMaxPatternRepetitionsInIndexBuffer = maxPatternRepetitionsInIndexBuffer; | 
|  | fFlags = Flags::kNone; | 
|  | } | 
|  |  | 
|  | inline void GrMesh::setInstanced(sk_sp<const GrBuffer> instanceBuffer, int instanceCount, | 
|  | int baseInstance, int vertexCount) { | 
|  | SkASSERT(instanceCount >= 1); | 
|  | SkASSERT(baseInstance >= 0); | 
|  | fIndexBuffer.reset(); | 
|  | fInstanceBuffer = std::move(instanceBuffer); | 
|  | fInstanceData.fInstanceCount = instanceCount; | 
|  | fInstanceData.fBaseInstance = baseInstance; | 
|  | fInstanceNonIndexData.fVertexCount = vertexCount; | 
|  | fFlags = Flags::kIsInstanced; | 
|  | } | 
|  |  | 
|  | inline void GrMesh::setIndexedInstanced(sk_sp<const GrBuffer> indexBuffer, int indexCount, | 
|  | sk_sp<const GrBuffer> instanceBuffer, int instanceCount, | 
|  | int baseInstance, GrPrimitiveRestart primitiveRestart) { | 
|  | SkASSERT(indexBuffer); | 
|  | SkASSERT(indexCount >= 1); | 
|  | SkASSERT(instanceCount >= 1); | 
|  | SkASSERT(baseInstance >= 0); | 
|  | fIndexBuffer = std::move(indexBuffer); | 
|  | fInstanceBuffer = std::move(instanceBuffer); | 
|  | fInstanceData.fInstanceCount = instanceCount; | 
|  | fInstanceData.fBaseInstance = baseInstance; | 
|  | fInstanceIndexData.fIndexCount = indexCount; | 
|  | fFlags = Flags::kIsInstanced | Flags(primitiveRestart); | 
|  | } | 
|  |  | 
|  | inline void GrMesh::setVertexData(sk_sp<const GrBuffer> vertexBuffer, int baseVertex) { | 
|  | SkASSERT(baseVertex >= 0); | 
|  | fVertexBuffer = std::move(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, | 
|  | this->primitiveRestart()); | 
|  | } | 
|  | 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, | 
|  | this->primitiveRestart()); | 
|  | 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(!(fFlags & Flags::kUsePrimitiveRestart)); | 
|  | 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 |