1/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrMeshDrawOp.h"
9#include "GrOpFlushState.h"
10#include "GrResourceProvider.h"
11
12GrMeshDrawOp::GrMeshDrawOp(uint32_t classID)
13    : INHERITED(classID), fBaseDrawToken(GrDrawOpUploadToken::AlreadyFlushedToken()) {}
14
15void GrMeshDrawOp::onPrepare(GrOpFlushState* state) {
16    Target target(state, this);
17    this->onPrepareDraws(&target);
18}
19
20void* GrMeshDrawOp::InstancedHelper::init(Target* target, GrPrimitiveType primType,
21                                          size_t vertexStride, const GrBuffer* indexBuffer,
22                                          int verticesPerInstance, int indicesPerInstance,
23                                          int instancesToDraw) {
24    SkASSERT(target);
25    if (!indexBuffer) {
26        return nullptr;
27    }
28    const GrBuffer* vertexBuffer;
29    int firstVertex;
30    int vertexCount = verticesPerInstance * instancesToDraw;
31    void* vertices =
32            target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
33    if (!vertices) {
34        SkDebugf("Vertices could not be allocated for instanced rendering.");
35        return nullptr;
36    }
37    SkASSERT(vertexBuffer);
38    size_t ibSize = indexBuffer->gpuMemorySize();
39    int maxInstancesPerDraw = static_cast<int>(ibSize / (sizeof(uint16_t) * indicesPerInstance));
40
41    fMesh.initInstanced(primType, vertexBuffer, indexBuffer, firstVertex, verticesPerInstance,
42                        indicesPerInstance, instancesToDraw, maxInstancesPerDraw);
43    return vertices;
44}
45
46void GrMeshDrawOp::InstancedHelper::recordDraw(Target* target, const GrGeometryProcessor* gp) {
47    SkASSERT(fMesh.instanceCount());
48    target->draw(gp, fMesh);
49}
50
51void* GrMeshDrawOp::QuadHelper::init(Target* target, size_t vertexStride, int quadsToDraw) {
52    sk_sp<const GrBuffer> quadIndexBuffer(target->resourceProvider()->refQuadIndexBuffer());
53    if (!quadIndexBuffer) {
54        SkDebugf("Could not get quad index buffer.");
55        return nullptr;
56    }
57    return this->INHERITED::init(target, kTriangles_GrPrimitiveType, vertexStride,
58                                 quadIndexBuffer.get(), kVerticesPerQuad, kIndicesPerQuad,
59                                 quadsToDraw);
60}
61
62void GrMeshDrawOp::onExecute(GrOpFlushState* state) {
63    SkASSERT(!state->drawOpArgs().fAppliedClip);
64    SkASSERT(!state->drawOpArgs().fDstTexture.texture());
65    SkASSERT(state->drawOpArgs().fRenderTarget == this->pipeline()->getRenderTarget());
66    int currUploadIdx = 0;
67    int currMeshIdx = 0;
68
69    SkASSERT(fQueuedDraws.empty() || fBaseDrawToken == state->nextTokenToFlush());
70
71    for (int currDrawIdx = 0; currDrawIdx < fQueuedDraws.count(); ++currDrawIdx) {
72        GrDrawOpUploadToken drawToken = state->nextTokenToFlush();
73        while (currUploadIdx < fInlineUploads.count() &&
74               fInlineUploads[currUploadIdx].fUploadBeforeToken == drawToken) {
75            state->commandBuffer()->inlineUpload(state, fInlineUploads[currUploadIdx++].fUpload,
76                                                 this->pipeline()->getRenderTarget());
77        }
78        const QueuedDraw& draw = fQueuedDraws[currDrawIdx];
79        state->commandBuffer()->draw(*this->pipeline(), *draw.fGeometryProcessor.get(),
80                                     fMeshes.begin() + currMeshIdx, draw.fMeshCnt, this->bounds());
81        currMeshIdx += draw.fMeshCnt;
82        state->flushToken();
83    }
84    SkASSERT(currUploadIdx == fInlineUploads.count());
85    SkASSERT(currMeshIdx == fMeshes.count());
86    fQueuedDraws.reset();
87    fInlineUploads.reset();
88}
89
90//////////////////////////////////////////////////////////////////////////////
91
92void GrMeshDrawOp::Target::draw(const GrGeometryProcessor* gp, const GrMesh& mesh) {
93    GrMeshDrawOp* op = this->meshDrawOp();
94    op->fMeshes.push_back(mesh);
95    if (!op->fQueuedDraws.empty()) {
96        // If the last draw shares a geometry processor and there are no intervening uploads,
97        // add this mesh to it.
98        GrMeshDrawOp::QueuedDraw& lastDraw = op->fQueuedDraws.back();
99        if (lastDraw.fGeometryProcessor == gp &&
100            (op->fInlineUploads.empty() ||
101             op->fInlineUploads.back().fUploadBeforeToken != this->nextDrawToken())) {
102            ++lastDraw.fMeshCnt;
103            return;
104        }
105    }
106    GrMeshDrawOp::QueuedDraw& draw = op->fQueuedDraws.push_back();
107    GrDrawOpUploadToken token = this->state()->issueDrawToken();
108    draw.fGeometryProcessor.reset(gp);
109    draw.fMeshCnt = 1;
110    if (op->fQueuedDraws.count() == 1) {
111        op->fBaseDrawToken = token;
112    }
113}
114