blob: 57d313ce152f98f6c3933c9adb90ab8e6a000af5 [file] [log] [blame]
/*
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "GrGpu.h"
#include "GrMemory.h"
#include "GrTextStrike.h"
#include "GrTextureCache.h"
#include "GrClipIterator.h"
#include "GrIndexBuffer.h"
#include "GrVertexBuffer.h"
#include "GrBufferAllocPool.h"
// probably makes no sense for this to be less than a page
static size_t VERTEX_POOL_VB_SIZE = 1 << 12;
///////////////////////////////////////////////////////////////////////////////
size_t GrTexture::BytesPerPixel(PixelConfig config) {
switch (config) {
case kAlpha_8_PixelConfig:
case kIndex_8_PixelConfig:
return 1;
case kRGB_565_PixelConfig:
case kRGBA_4444_PixelConfig:
return 2;
case kRGBA_8888_PixelConfig:
case kRGBX_8888_PixelConfig:
return 4;
default:
return 0;
}
}
bool GrTexture::PixelConfigIsOpaque(PixelConfig config) {
switch (config) {
case GrTexture::kRGB_565_PixelConfig:
case GrTexture::kRGBX_8888_PixelConfig:
return true;
default:
return false;
}
}
///////////////////////////////////////////////////////////////////////////////
extern void gr_run_unittests();
GrGpu::GrGpu() : f8bitPaletteSupport(false),
fCurrPoolVertexBuffer(NULL),
fCurrPoolStartVertex(0),
fCurrPoolIndexBuffer(NULL),
fCurrPoolStartIndex(0),
fVertexPool(NULL),
fIndexPool(NULL),
fQuadIndexBuffer(NULL),
fUnitSquareVertexBuffer(NULL) {
#if GR_DEBUG
// gr_run_unittests();
#endif
resetStats();
}
GrGpu::~GrGpu() {
GrSafeUnref(fQuadIndexBuffer);
GrSafeUnref(fUnitSquareVertexBuffer);
delete fVertexPool;
delete fIndexPool;
}
void GrGpu::resetContext() {
}
void GrGpu::unimpl(const char msg[]) {
#if GR_DEBUG
GrPrintf("--- GrGpu unimplemented(\"%s\")\n", msg);
#endif
}
///////////////////////////////////////////////////////////////////////////////
static const int MAX_QUADS = 1 << 12; // max possible: (1 << 14) - 1;
GR_STATIC_ASSERT(4 * MAX_QUADS <= 65535);
static inline void fill_indices(uint16_t* indices, int quadCount) {
for (int i = 0; i < quadCount; ++i) {
indices[6 * i + 0] = 4 * i + 0;
indices[6 * i + 1] = 4 * i + 1;
indices[6 * i + 2] = 4 * i + 2;
indices[6 * i + 3] = 4 * i + 0;
indices[6 * i + 4] = 4 * i + 2;
indices[6 * i + 5] = 4 * i + 3;
}
}
const GrIndexBuffer* GrGpu::getQuadIndexBuffer() const {
if (NULL == fQuadIndexBuffer) {
static const int SIZE = sizeof(uint16_t) * 6 * MAX_QUADS;
GrGpu* me = const_cast<GrGpu*>(this);
fQuadIndexBuffer = me->createIndexBuffer(SIZE, false);
if (NULL != fQuadIndexBuffer) {
uint16_t* indices = (uint16_t*)fQuadIndexBuffer->lock();
if (NULL != indices) {
fill_indices(indices, MAX_QUADS);
fQuadIndexBuffer->unlock();
} else {
indices = (uint16_t*)GrMalloc(SIZE);
fill_indices(indices, MAX_QUADS);
if (!fQuadIndexBuffer->updateData(indices, SIZE)) {
fQuadIndexBuffer->unref();
fQuadIndexBuffer = NULL;
GrCrash("Can't get indices into buffer!");
}
GrFree(indices);
}
}
}
return fQuadIndexBuffer;
}
const GrVertexBuffer* GrGpu::getUnitSquareVertexBuffer() const {
if (NULL == fUnitSquareVertexBuffer) {
static const GrPoint DATA[] = {
GrPoint(0, 0),
GrPoint(GR_Scalar1,0),
GrPoint(GR_Scalar1,GR_Scalar1),
GrPoint(0, GR_Scalar1)
};
static const size_t SIZE = sizeof(DATA);
GrGpu* me = const_cast<GrGpu*>(this);
fUnitSquareVertexBuffer = me->createVertexBuffer(SIZE, false);
if (NULL != fUnitSquareVertexBuffer) {
if (!fUnitSquareVertexBuffer->updateData(DATA, SIZE)) {
fUnitSquareVertexBuffer->unref();
fUnitSquareVertexBuffer = NULL;
GrCrash("Can't get vertices into buffer!");
}
}
}
return fUnitSquareVertexBuffer;
}
///////////////////////////////////////////////////////////////////////////////
void GrGpu::clipWillBeSet(const GrClip& newClip) {
if (newClip != fClip) {
fClipState.fClipIsDirty = true;
}
}
bool GrGpu::setupClipAndFlushState(PrimitiveType type) {
const GrIRect* r = NULL;
// we check this early because we need a valid
// render target to setup stencil clipping
// before even going into flushGraphicsState
if (NULL == fCurrDrawState.fRenderTarget) {
GrAssert(!"No render target bound.");
return false;
}
if (fCurrDrawState.fFlagBits & kClip_StateBit) {
fClipState.fClipInStencil = fClip.countRects() > 1;
if (fClipState.fClipInStencil &&
(fClipState.fClipIsDirty ||
fClipState.fStencilClipTarget != fCurrDrawState.fRenderTarget)) {
AutoStateRestore asr(this);
AutoGeometrySrcRestore agsr(this);
// We have to use setVertexSourceToBuffer (and index) in order
// to ensure we correctly restore the client's geom sources.
// We tack the clip verts onto the vertex pool but we don't
// use the various helper functions because of their side effects.
int rectTotal = fClip.countRects();
if (NULL == fVertexPool) {
fVertexPool = new GrVertexBufferAllocPool(this,
true,
VERTEX_POOL_VB_SIZE,
1);
}
const GrVertexBuffer* vertexBuffer;
int vStart;
GrPoint* rectVertices =
reinterpret_cast<GrPoint*>(fVertexPool->makeSpace(0,
rectTotal * 4,
&vertexBuffer,
&vStart));
for (int r = 0; r < rectTotal; ++r) {
const GrIRect& rect = fClip.getRects()[r];
rectVertices[4 * r].setIRectFan(rect.fLeft, rect.fTop,
rect.fRight, rect.fBottom);
}
fVertexPool->unlock();
this->setVertexSourceToBuffer(0, vertexBuffer);
this->setIndexSourceToBuffer(getQuadIndexBuffer());
this->setViewMatrix(GrMatrix::I());
// don't clip the clip or recurse!
this->disableState(kClip_StateBit);
this->eraseStencilClip();
this->setStencilPass((GrDrawTarget::StencilPass)kSetClip_StencilPass);
int currRect = 0;
while (currRect < rectTotal) {
int rectCount = GrMin(MAX_QUADS,
rectTotal - currRect);
this->drawIndexed(kTriangles_PrimitiveType,
vStart + currRect * 4,
0,
rectCount*4,
rectCount*6);
currRect += rectCount;
}
fClipState.fStencilClipTarget = fCurrDrawState.fRenderTarget;
}
fClipState.fClipIsDirty = false;
if (!fClipState.fClipInStencil) {
r = &fClip.getBounds();
}
}
// Must flush the scissor after graphics state
if (!flushGraphicsState(type)) {
return false;
}
flushScissor(r);
return true;
}
///////////////////////////////////////////////////////////////////////////////
void GrGpu::drawIndexed(PrimitiveType type,
int startVertex,
int startIndex,
int vertexCount,
int indexCount) {
GrAssert(kReserved_GeometrySrcType != fGeometrySrc.fVertexSrc ||
fReservedGeometry.fLocked);
GrAssert(kReserved_GeometrySrcType != fGeometrySrc.fIndexSrc ||
fReservedGeometry.fLocked);
if (!setupClipAndFlushState(type)) {
return;
}
#if GR_COLLECT_STATS
fStats.fVertexCnt += vertexCount;
fStats.fIndexCnt += indexCount;
fStats.fDrawCnt += 1;
#endif
int sVertex = startVertex;
int sIndex = startIndex;
setupGeometry(&sVertex, &sIndex, vertexCount, indexCount);
drawIndexedHelper(type, sVertex, sIndex,
vertexCount, indexCount);
}
void GrGpu::drawNonIndexed(PrimitiveType type,
int startVertex,
int vertexCount) {
GrAssert(kReserved_GeometrySrcType != fGeometrySrc.fVertexSrc ||
fReservedGeometry.fLocked);
if (!setupClipAndFlushState(type)) {
return;
}
#if GR_COLLECT_STATS
fStats.fVertexCnt += vertexCount;
fStats.fDrawCnt += 1;
#endif
int sVertex = startVertex;
setupGeometry(&sVertex, NULL, vertexCount, 0);
drawNonIndexedHelper(type, sVertex, vertexCount);
}
void GrGpu::finalizeReservedVertices() {
GrAssert(NULL != fVertexPool);
fVertexPool->unlock();
}
void GrGpu::finalizeReservedIndices() {
GrAssert(NULL != fIndexPool);
fIndexPool->unlock();
}
void GrGpu::prepareVertexPool() {
if (NULL == fVertexPool) {
fVertexPool = new GrVertexBufferAllocPool(this, true, VERTEX_POOL_VB_SIZE, 1);
} else {
fVertexPool->reset();
}
}
void GrGpu::prepareIndexPool() {
if (NULL == fVertexPool) {
fIndexPool = new GrIndexBufferAllocPool(this, true, 0, 1);
} else {
fIndexPool->reset();
}
}
bool GrGpu::acquireGeometryHelper(GrVertexLayout vertexLayout,
void** vertices,
void** indices) {
GrAssert(!fReservedGeometry.fLocked);
size_t reservedVertexSpace = 0;
if (fReservedGeometry.fVertexCount) {
GrAssert(NULL != vertices);
prepareVertexPool();
*vertices = fVertexPool->makeSpace(vertexLayout,
fReservedGeometry.fVertexCount,
&fCurrPoolVertexBuffer,
&fCurrPoolStartVertex);
if (NULL == *vertices) {
return false;
}
reservedVertexSpace = VertexSize(vertexLayout) *
fReservedGeometry.fVertexCount;
}
if (fReservedGeometry.fIndexCount) {
GrAssert(NULL != indices);
prepareIndexPool();
*indices = fIndexPool->makeSpace(fReservedGeometry.fIndexCount,
&fCurrPoolIndexBuffer,
&fCurrPoolStartIndex);
if (NULL == *indices) {
fVertexPool->putBack(reservedVertexSpace);
fCurrPoolVertexBuffer = NULL;
return false;
}
}
return true;
}
void GrGpu::releaseGeometryHelper() {}
void GrGpu::setVertexSourceToArrayHelper(const void* vertexArray, int vertexCount) {
GrAssert(!fReservedGeometry.fLocked || !fReservedGeometry.fVertexCount);
prepareVertexPool();
#if GR_DEBUG
bool success =
#endif
fVertexPool->appendVertices(fGeometrySrc.fVertexLayout,
vertexCount,
vertexArray,
&fCurrPoolVertexBuffer,
&fCurrPoolStartVertex);
GR_DEBUGASSERT(success);
}
void GrGpu::setIndexSourceToArrayHelper(const void* indexArray, int indexCount) {
GrAssert(!fReservedGeometry.fLocked || !fReservedGeometry.fIndexCount);
prepareIndexPool();
#if GR_DEBUG
bool success =
#endif
fIndexPool->appendIndices(indexCount,
indexArray,
&fCurrPoolIndexBuffer,
&fCurrPoolStartIndex);
GR_DEBUGASSERT(success);
}
///////////////////////////////////////////////////////////////////////////////
const GrGpu::Stats& GrGpu::getStats() const {
return fStats;
}
void GrGpu::resetStats() {
memset(&fStats, 0, sizeof(fStats));
}
void GrGpu::printStats() const {
if (GR_COLLECT_STATS) {
GrPrintf(
"-v-------------------------GPU STATS----------------------------v-\n"
"Stats collection is: %s\n"
"Draws: %04d, Verts: %04d, Indices: %04d\n"
"ProgChanges: %04d, TexChanges: %04d, RTChanges: %04d\n"
"TexCreates: %04d, RTCreates:%04d\n"
"-^--------------------------------------------------------------^-\n",
(GR_COLLECT_STATS ? "ON" : "OFF"),
fStats.fDrawCnt, fStats.fVertexCnt, fStats.fIndexCnt,
fStats.fProgChngCnt, fStats.fTextureChngCnt, fStats.fRenderTargetChngCnt,
fStats.fTextureCreateCnt, fStats.fRenderTargetCreateCnt);
}
}
////////////////////////////////////////////////////////////////////////////////
GrTexture::~GrTexture() {
// use this to set a break-point if needed
// Gr_clz(3);
}
const GrSamplerState GrSamplerState::gClampNoFilter(
GrSamplerState::kClamp_WrapMode,
GrSamplerState::kClamp_WrapMode,
GrSamplerState::kNormal_SampleMode,
GrMatrix::I(),
false);