1
2/*
3 * Copyright 2010 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "GrGpu.h"
11
12#include "GrBufferAllocPool.h"
13#include "GrContext.h"
14#include "GrDrawTargetCaps.h"
15#include "GrIndexBuffer.h"
16#include "GrStencilBuffer.h"
17#include "GrVertexBuffer.h"
18
19// probably makes no sense for this to be less than a page
20static const size_t VERTEX_POOL_VB_SIZE = 1 << 18;
21static const int VERTEX_POOL_VB_COUNT = 4;
22static const size_t INDEX_POOL_IB_SIZE = 1 << 16;
23static const int INDEX_POOL_IB_COUNT = 4;
24
25////////////////////////////////////////////////////////////////////////////////
26
27#define DEBUG_INVAL_BUFFER    0xdeadcafe
28#define DEBUG_INVAL_START_IDX -1
29
30GrGpu::GrGpu(GrContext* context)
31    : GrDrawTarget(context)
32    , fResetTimestamp(kExpiredTimestamp+1)
33    , fResetBits(kAll_GrBackendState)
34    , fVertexPool(NULL)
35    , fIndexPool(NULL)
36    , fVertexPoolUseCnt(0)
37    , fIndexPoolUseCnt(0)
38    , fQuadIndexBuffer(NULL) {
39
40    fClipMaskManager.setGpu(this);
41
42    fGeomPoolStateStack.push_back();
43#ifdef SK_DEBUG
44    GeometryPoolState& poolState = fGeomPoolStateStack.back();
45    poolState.fPoolVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
46    poolState.fPoolStartVertex = DEBUG_INVAL_START_IDX;
47    poolState.fPoolIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
48    poolState.fPoolStartIndex = DEBUG_INVAL_START_IDX;
49#endif
50}
51
52GrGpu::~GrGpu() {
53    SkSafeSetNull(fQuadIndexBuffer);
54    delete fVertexPool;
55    fVertexPool = NULL;
56    delete fIndexPool;
57    fIndexPool = NULL;
58}
59
60void GrGpu::contextAbandoned() {}
61
62////////////////////////////////////////////////////////////////////////////////
63
64GrTexture* GrGpu::createTexture(const GrTextureDesc& desc,
65                                const void* srcData, size_t rowBytes) {
66    if (!this->caps()->isConfigTexturable(desc.fConfig)) {
67        return NULL;
68    }
69
70    if ((desc.fFlags & kRenderTarget_GrTextureFlagBit) &&
71        !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
72        return NULL;
73    }
74
75    GrTexture *tex = NULL;
76    if (GrPixelConfigIsCompressed(desc.fConfig)) {
77        // We shouldn't be rendering into this
78        SkASSERT((desc.fFlags & kRenderTarget_GrTextureFlagBit) == 0);
79
80        if (!this->caps()->npotTextureTileSupport() &&
81            (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) {
82            return NULL;
83        }
84
85        this->handleDirtyContext();
86        tex = this->onCreateCompressedTexture(desc, srcData);
87    } else {
88        this->handleDirtyContext();
89        tex = this->onCreateTexture(desc, srcData, rowBytes);
90        if (tex &&
91            (kRenderTarget_GrTextureFlagBit & desc.fFlags) &&
92            !(kNoStencil_GrTextureFlagBit & desc.fFlags)) {
93            SkASSERT(tex->asRenderTarget());
94            // TODO: defer this and attach dynamically
95            if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) {
96                tex->unref();
97                return NULL;
98            }
99        }
100    }
101    return tex;
102}
103
104bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) {
105    SkASSERT(NULL == rt->getStencilBuffer());
106    GrStencilBuffer* sb =
107        this->getContext()->findStencilBuffer(rt->width(),
108                                              rt->height(),
109                                              rt->numSamples());
110    if (sb) {
111        rt->setStencilBuffer(sb);
112        bool attached = this->attachStencilBufferToRenderTarget(sb, rt);
113        if (!attached) {
114            rt->setStencilBuffer(NULL);
115        }
116        return attached;
117    }
118    if (this->createStencilBufferForRenderTarget(rt,
119                                                 rt->width(), rt->height())) {
120        // Right now we're clearing the stencil buffer here after it is
121        // attached to an RT for the first time. When we start matching
122        // stencil buffers with smaller color targets this will no longer
123        // be correct because it won't be guaranteed to clear the entire
124        // sb.
125        // We used to clear down in the GL subclass using a special purpose
126        // FBO. But iOS doesn't allow a stencil-only FBO. It reports unsupported
127        // FBO status.
128        this->clearStencil(rt);
129        return true;
130    } else {
131        return false;
132    }
133}
134
135GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc) {
136    this->handleDirtyContext();
137    GrTexture* tex = this->onWrapBackendTexture(desc);
138    if (NULL == tex) {
139        return NULL;
140    }
141    // TODO: defer this and attach dynamically
142    GrRenderTarget* tgt = tex->asRenderTarget();
143    if (tgt &&
144        !this->attachStencilBufferToRenderTarget(tgt)) {
145        tex->unref();
146        return NULL;
147    } else {
148        return tex;
149    }
150}
151
152GrRenderTarget* GrGpu::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) {
153    this->handleDirtyContext();
154    return this->onWrapBackendRenderTarget(desc);
155}
156
157GrVertexBuffer* GrGpu::createVertexBuffer(size_t size, bool dynamic) {
158    this->handleDirtyContext();
159    return this->onCreateVertexBuffer(size, dynamic);
160}
161
162GrIndexBuffer* GrGpu::createIndexBuffer(size_t size, bool dynamic) {
163    this->handleDirtyContext();
164    return this->onCreateIndexBuffer(size, dynamic);
165}
166
167void GrGpu::clear(const SkIRect* rect,
168                  GrColor color,
169                  bool canIgnoreRect,
170                  GrRenderTarget* renderTarget) {
171    if (NULL == renderTarget) {
172        renderTarget = this->getDrawState().getRenderTarget();
173    }
174    if (NULL == renderTarget) {
175        SkASSERT(0);
176        return;
177    }
178    this->handleDirtyContext();
179    this->onClear(renderTarget, rect, color, canIgnoreRect);
180}
181
182bool GrGpu::readPixels(GrRenderTarget* target,
183                       int left, int top, int width, int height,
184                       GrPixelConfig config, void* buffer,
185                       size_t rowBytes) {
186    this->handleDirtyContext();
187    return this->onReadPixels(target, left, top, width, height,
188                              config, buffer, rowBytes);
189}
190
191bool GrGpu::writeTexturePixels(GrTexture* texture,
192                               int left, int top, int width, int height,
193                               GrPixelConfig config, const void* buffer,
194                               size_t rowBytes) {
195    this->handleDirtyContext();
196    return this->onWriteTexturePixels(texture, left, top, width, height,
197                                      config, buffer, rowBytes);
198}
199
200void GrGpu::resolveRenderTarget(GrRenderTarget* target) {
201    SkASSERT(target);
202    this->handleDirtyContext();
203    this->onResolveRenderTarget(target);
204}
205
206static const GrStencilSettings& winding_path_stencil_settings() {
207    GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
208        kIncClamp_StencilOp,
209        kIncClamp_StencilOp,
210        kAlwaysIfInClip_StencilFunc,
211        0xFFFF, 0xFFFF, 0xFFFF);
212    return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
213}
214
215static const GrStencilSettings& even_odd_path_stencil_settings() {
216    GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
217        kInvert_StencilOp,
218        kInvert_StencilOp,
219        kAlwaysIfInClip_StencilFunc,
220        0xFFFF, 0xFFFF, 0xFFFF);
221    return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
222}
223
224void GrGpu::getPathStencilSettingsForFillType(SkPath::FillType fill, GrStencilSettings* outStencilSettings) {
225
226    switch (fill) {
227        default:
228            SkFAIL("Unexpected path fill.");
229            /* fallthrough */;
230        case SkPath::kWinding_FillType:
231        case SkPath::kInverseWinding_FillType:
232            *outStencilSettings = winding_path_stencil_settings();
233            break;
234        case SkPath::kEvenOdd_FillType:
235        case SkPath::kInverseEvenOdd_FillType:
236            *outStencilSettings = even_odd_path_stencil_settings();
237            break;
238    }
239    fClipMaskManager.adjustPathStencilParams(outStencilSettings);
240}
241
242
243////////////////////////////////////////////////////////////////////////////////
244
245static const int MAX_QUADS = 1 << 12; // max possible: (1 << 14) - 1;
246
247GR_STATIC_ASSERT(4 * MAX_QUADS <= 65535);
248
249static inline void fill_indices(uint16_t* indices, int quadCount) {
250    for (int i = 0; i < quadCount; ++i) {
251        indices[6 * i + 0] = 4 * i + 0;
252        indices[6 * i + 1] = 4 * i + 1;
253        indices[6 * i + 2] = 4 * i + 2;
254        indices[6 * i + 3] = 4 * i + 0;
255        indices[6 * i + 4] = 4 * i + 2;
256        indices[6 * i + 5] = 4 * i + 3;
257    }
258}
259
260const GrIndexBuffer* GrGpu::getQuadIndexBuffer() const {
261    if (NULL == fQuadIndexBuffer || fQuadIndexBuffer->wasDestroyed()) {
262        SkSafeUnref(fQuadIndexBuffer);
263        static const int SIZE = sizeof(uint16_t) * 6 * MAX_QUADS;
264        GrGpu* me = const_cast<GrGpu*>(this);
265        fQuadIndexBuffer = me->createIndexBuffer(SIZE, false);
266        if (fQuadIndexBuffer) {
267            uint16_t* indices = (uint16_t*)fQuadIndexBuffer->map();
268            if (indices) {
269                fill_indices(indices, MAX_QUADS);
270                fQuadIndexBuffer->unmap();
271            } else {
272                indices = (uint16_t*)sk_malloc_throw(SIZE);
273                fill_indices(indices, MAX_QUADS);
274                if (!fQuadIndexBuffer->updateData(indices, SIZE)) {
275                    fQuadIndexBuffer->unref();
276                    fQuadIndexBuffer = NULL;
277                    SkFAIL("Can't get indices into buffer!");
278                }
279                sk_free(indices);
280            }
281        }
282    }
283
284    return fQuadIndexBuffer;
285}
286
287////////////////////////////////////////////////////////////////////////////////
288
289bool GrGpu::setupClipAndFlushState(DrawType type, const GrDeviceCoordTexture* dstCopy,
290                                   GrDrawState::AutoRestoreEffects* are,
291                                   const SkRect* devBounds) {
292    if (!fClipMaskManager.setupClipping(this->getClip(), are, devBounds)) {
293        return false;
294    }
295
296    if (!this->flushGraphicsState(type, dstCopy)) {
297        return false;
298    }
299
300    return true;
301}
302
303////////////////////////////////////////////////////////////////////////////////
304
305void GrGpu::geometrySourceWillPush() {
306    const GeometrySrcState& geoSrc = this->getGeomSrc();
307    if (kArray_GeometrySrcType == geoSrc.fVertexSrc ||
308        kReserved_GeometrySrcType == geoSrc.fVertexSrc) {
309        this->finalizeReservedVertices();
310    }
311    if (kArray_GeometrySrcType == geoSrc.fIndexSrc ||
312        kReserved_GeometrySrcType == geoSrc.fIndexSrc) {
313        this->finalizeReservedIndices();
314    }
315    GeometryPoolState& newState = fGeomPoolStateStack.push_back();
316#ifdef SK_DEBUG
317    newState.fPoolVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
318    newState.fPoolStartVertex = DEBUG_INVAL_START_IDX;
319    newState.fPoolIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
320    newState.fPoolStartIndex = DEBUG_INVAL_START_IDX;
321#else
322    (void) newState; // silence compiler warning
323#endif
324}
325
326void GrGpu::geometrySourceWillPop(const GeometrySrcState& restoredState) {
327    // if popping last entry then pops are unbalanced with pushes
328    SkASSERT(fGeomPoolStateStack.count() > 1);
329    fGeomPoolStateStack.pop_back();
330}
331
332void GrGpu::onDraw(const DrawInfo& info) {
333    this->handleDirtyContext();
334    GrDrawState::AutoRestoreEffects are;
335    if (!this->setupClipAndFlushState(PrimTypeToDrawType(info.primitiveType()),
336                                      info.getDstCopy(), &are, info.getDevBounds())) {
337        return;
338    }
339    this->onGpuDraw(info);
340}
341
342void GrGpu::onStencilPath(const GrPath* path, SkPath::FillType fill) {
343    this->handleDirtyContext();
344
345    GrDrawState::AutoRestoreEffects are;
346    if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL, &are, NULL)) {
347        return;
348    }
349
350    this->pathRendering()->stencilPath(path, fill);
351}
352
353
354void GrGpu::onDrawPath(const GrPath* path, SkPath::FillType fill,
355                       const GrDeviceCoordTexture* dstCopy) {
356    this->handleDirtyContext();
357
358    drawState()->setDefaultVertexAttribs();
359
360    GrDrawState::AutoRestoreEffects are;
361    if (!this->setupClipAndFlushState(kDrawPath_DrawType, dstCopy, &are, NULL)) {
362        return;
363    }
364
365    this->pathRendering()->drawPath(path, fill);
366}
367
368void GrGpu::onDrawPaths(const GrPathRange* pathRange,
369                        const uint32_t indices[], int count,
370                        const float transforms[], PathTransformType transformsType,
371                        SkPath::FillType fill, const GrDeviceCoordTexture* dstCopy) {
372    this->handleDirtyContext();
373
374    drawState()->setDefaultVertexAttribs();
375
376    GrDrawState::AutoRestoreEffects are;
377    if (!this->setupClipAndFlushState(kDrawPaths_DrawType, dstCopy, &are, NULL)) {
378        return;
379    }
380
381    pathRange->willDrawPaths(indices, count);
382    this->pathRendering()->drawPaths(pathRange, indices, count, transforms, transformsType, fill);
383}
384
385void GrGpu::finalizeReservedVertices() {
386    SkASSERT(fVertexPool);
387    fVertexPool->unmap();
388}
389
390void GrGpu::finalizeReservedIndices() {
391    SkASSERT(fIndexPool);
392    fIndexPool->unmap();
393}
394
395void GrGpu::prepareVertexPool() {
396    if (NULL == fVertexPool) {
397        SkASSERT(0 == fVertexPoolUseCnt);
398        fVertexPool = SkNEW_ARGS(GrVertexBufferAllocPool, (this, true,
399                                                  VERTEX_POOL_VB_SIZE,
400                                                  VERTEX_POOL_VB_COUNT));
401        fVertexPool->releaseGpuRef();
402    } else if (!fVertexPoolUseCnt) {
403        // the client doesn't have valid data in the pool
404        fVertexPool->reset();
405    }
406}
407
408void GrGpu::prepareIndexPool() {
409    if (NULL == fIndexPool) {
410        SkASSERT(0 == fIndexPoolUseCnt);
411        fIndexPool = SkNEW_ARGS(GrIndexBufferAllocPool, (this, true,
412                                                INDEX_POOL_IB_SIZE,
413                                                INDEX_POOL_IB_COUNT));
414        fIndexPool->releaseGpuRef();
415    } else if (!fIndexPoolUseCnt) {
416        // the client doesn't have valid data in the pool
417        fIndexPool->reset();
418    }
419}
420
421bool GrGpu::onReserveVertexSpace(size_t vertexSize,
422                                 int vertexCount,
423                                 void** vertices) {
424    GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
425
426    SkASSERT(vertexCount > 0);
427    SkASSERT(vertices);
428
429    this->prepareVertexPool();
430
431    *vertices = fVertexPool->makeSpace(vertexSize,
432                                       vertexCount,
433                                       &geomPoolState.fPoolVertexBuffer,
434                                       &geomPoolState.fPoolStartVertex);
435    if (NULL == *vertices) {
436        return false;
437    }
438    ++fVertexPoolUseCnt;
439    return true;
440}
441
442bool GrGpu::onReserveIndexSpace(int indexCount, void** indices) {
443    GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
444
445    SkASSERT(indexCount > 0);
446    SkASSERT(indices);
447
448    this->prepareIndexPool();
449
450    *indices = fIndexPool->makeSpace(indexCount,
451                                     &geomPoolState.fPoolIndexBuffer,
452                                     &geomPoolState.fPoolStartIndex);
453    if (NULL == *indices) {
454        return false;
455    }
456    ++fIndexPoolUseCnt;
457    return true;
458}
459
460void GrGpu::releaseReservedVertexSpace() {
461    const GeometrySrcState& geoSrc = this->getGeomSrc();
462    SkASSERT(kReserved_GeometrySrcType == geoSrc.fVertexSrc);
463    size_t bytes = geoSrc.fVertexCount * geoSrc.fVertexSize;
464    fVertexPool->putBack(bytes);
465    --fVertexPoolUseCnt;
466}
467
468void GrGpu::releaseReservedIndexSpace() {
469    const GeometrySrcState& geoSrc = this->getGeomSrc();
470    SkASSERT(kReserved_GeometrySrcType == geoSrc.fIndexSrc);
471    size_t bytes = geoSrc.fIndexCount * sizeof(uint16_t);
472    fIndexPool->putBack(bytes);
473    --fIndexPoolUseCnt;
474}
475
476void GrGpu::onSetVertexSourceToArray(const void* vertexArray, int vertexCount) {
477    this->prepareVertexPool();
478    GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
479#ifdef SK_DEBUG
480    bool success =
481#endif
482    fVertexPool->appendVertices(this->getVertexSize(),
483                                vertexCount,
484                                vertexArray,
485                                &geomPoolState.fPoolVertexBuffer,
486                                &geomPoolState.fPoolStartVertex);
487    ++fVertexPoolUseCnt;
488    GR_DEBUGASSERT(success);
489}
490
491void GrGpu::onSetIndexSourceToArray(const void* indexArray, int indexCount) {
492    this->prepareIndexPool();
493    GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
494#ifdef SK_DEBUG
495    bool success =
496#endif
497    fIndexPool->appendIndices(indexCount,
498                              indexArray,
499                              &geomPoolState.fPoolIndexBuffer,
500                              &geomPoolState.fPoolStartIndex);
501    ++fIndexPoolUseCnt;
502    GR_DEBUGASSERT(success);
503}
504
505void GrGpu::releaseVertexArray() {
506    // if vertex source was array, we stowed data in the pool
507    const GeometrySrcState& geoSrc = this->getGeomSrc();
508    SkASSERT(kArray_GeometrySrcType == geoSrc.fVertexSrc);
509    size_t bytes = geoSrc.fVertexCount * geoSrc.fVertexSize;
510    fVertexPool->putBack(bytes);
511    --fVertexPoolUseCnt;
512}
513
514void GrGpu::releaseIndexArray() {
515    // if index source was array, we stowed data in the pool
516    const GeometrySrcState& geoSrc = this->getGeomSrc();
517    SkASSERT(kArray_GeometrySrcType == geoSrc.fIndexSrc);
518    size_t bytes = geoSrc.fIndexCount * sizeof(uint16_t);
519    fIndexPool->putBack(bytes);
520    --fIndexPoolUseCnt;
521}
522