blob: 3b27172fb42bbe6811a7c3e2f4c7754624d3b649 [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrDrawTarget.h"
#include "GrGpuVertex.h"
#include "GrIndexBuffer.h"
#include "GrRenderTarget.h"
#include "GrTexture.h"
#include "GrVertexBuffer.h"
SK_DEFINE_INST_COUNT(GrDrawTarget)
namespace {
/**
* This function generates some masks that we like to have known at compile
* time. When the number of stages or tex coords is bumped or the way bits
* are defined in GrDrawTarget.h changes this function should be rerun to
* generate the new masks. (We attempted to force the compiler to generate the
* masks using recursive templates but always wound up with static initializers
* under gcc, even if they were just a series of immediate->memory moves.)
*
*/
void gen_mask_arrays(GrVertexLayout* stageTexCoordMasks,
GrVertexLayout* texCoordMasks) {
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
stageTexCoordMasks[s] = 0;
for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
stageTexCoordMasks[s] |= GrDrawTarget::StageTexCoordVertexLayoutBit(s, t);
}
}
for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
texCoordMasks[t] = 0;
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
texCoordMasks[t] |= GrDrawTarget::StageTexCoordVertexLayoutBit(s, t);
}
}
}
/**
* Run this function to generate the code that declares the global masks.
*/
void gen_globals() {
GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
GrVertexLayout texCoordMasks[GrDrawState::kMaxTexCoords];
gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
GrPrintf("const GrVertexLayout gStageTexCoordMasks[] = {\n");
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
GrPrintf(" 0x%x,\n", stageTexCoordMasks[s]);
}
GrPrintf("};\n");
GrPrintf("GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));\n\n");
GrPrintf("const GrVertexLayout gTexCoordMasks[] = {\n");
for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
GrPrintf(" 0x%x,\n", texCoordMasks[t]);
}
GrPrintf("};\n");
GrPrintf("GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));\n");
}
/* These values were generated by the above function */
const GrVertexLayout gStageTexCoordMasks[] = {
0x108421,
0x210842,
0x421084,
0x842108,
0x1084210,
};
GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));
const GrVertexLayout gTexCoordMasks[] = {
0x1f,
0x3e0,
0x7c00,
0xf8000,
0x1f00000,
};
GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));
bool check_layout(GrVertexLayout layout) {
// can only have 1 or 0 bits set for each stage.
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
int stageBits = layout & gStageTexCoordMasks[s];
if (stageBits && !GrIsPow2(stageBits)) {
return false;
}
}
return true;
}
int num_tex_coords(GrVertexLayout layout) {
int cnt = 0;
// figure out how many tex coordinates are present
for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
if (gTexCoordMasks[t] & layout) {
++cnt;
}
}
return cnt;
}
} //unnamed namespace
size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
size_t vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
size_t size = vecSize; // position
size += num_tex_coords(vertexLayout) * vecSize;
if (vertexLayout & kColor_VertexLayoutBit) {
size += sizeof(GrColor);
}
if (vertexLayout & kCoverage_VertexLayoutBit) {
size += sizeof(GrColor);
}
if (vertexLayout & kEdge_VertexLayoutBit) {
size += 4 * sizeof(GrScalar);
}
return size;
}
////////////////////////////////////////////////////////////////////////////////
/**
* Functions for computing offsets of various components from the layout
* bitfield.
*
* Order of vertex components:
* Position
* Tex Coord 0
* ...
* Tex Coord GrDrawState::kMaxTexCoords-1
* Color
* Coverage
*/
int GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
if (!StageUsesTexCoords(vertexLayout, stage)) {
return 0;
}
int tcIdx = VertexTexCoordsForStage(stage, vertexLayout);
if (tcIdx >= 0) {
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
int offset = vecSize; // position
// figure out how many tex coordinates are present and precede this one.
for (int t = 0; t < tcIdx; ++t) {
if (gTexCoordMasks[t] & vertexLayout) {
offset += vecSize;
}
}
return offset;
}
return -1;
}
int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
if (vertexLayout & kColor_VertexLayoutBit) {
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
return vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
}
return -1;
}
int GrDrawTarget::VertexCoverageOffset(GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
if (vertexLayout & kCoverage_VertexLayoutBit) {
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
int offset = vecSize * (num_tex_coords(vertexLayout) + 1);
if (vertexLayout & kColor_VertexLayoutBit) {
offset += sizeof(GrColor);
}
return offset;
}
return -1;
}
int GrDrawTarget::VertexEdgeOffset(GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
// edge pts are after the pos, tex coords, and color
if (vertexLayout & kEdge_VertexLayoutBit) {
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
int offset = vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
if (vertexLayout & kColor_VertexLayoutBit) {
offset += sizeof(GrColor);
}
if (vertexLayout & kCoverage_VertexLayoutBit) {
offset += sizeof(GrColor);
}
return offset;
}
return -1;
}
int GrDrawTarget::VertexSizeAndOffsetsByIdx(
GrVertexLayout vertexLayout,
int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords],
int* colorOffset,
int* coverageOffset,
int* edgeOffset) {
GrAssert(check_layout(vertexLayout));
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
int size = vecSize; // position
for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
if (gTexCoordMasks[t] & vertexLayout) {
if (NULL != texCoordOffsetsByIdx) {
texCoordOffsetsByIdx[t] = size;
}
size += vecSize;
} else {
if (NULL != texCoordOffsetsByIdx) {
texCoordOffsetsByIdx[t] = -1;
}
}
}
if (kColor_VertexLayoutBit & vertexLayout) {
if (NULL != colorOffset) {
*colorOffset = size;
}
size += sizeof(GrColor);
} else {
if (NULL != colorOffset) {
*colorOffset = -1;
}
}
if (kCoverage_VertexLayoutBit & vertexLayout) {
if (NULL != coverageOffset) {
*coverageOffset = size;
}
size += sizeof(GrColor);
} else {
if (NULL != coverageOffset) {
*coverageOffset = -1;
}
}
if (kEdge_VertexLayoutBit & vertexLayout) {
if (NULL != edgeOffset) {
*edgeOffset = size;
}
size += 4 * sizeof(GrScalar);
} else {
if (NULL != edgeOffset) {
*edgeOffset = -1;
}
}
return size;
}
int GrDrawTarget::VertexSizeAndOffsetsByStage(
GrVertexLayout vertexLayout,
int texCoordOffsetsByStage[GrDrawState::kNumStages],
int* colorOffset,
int* coverageOffset,
int* edgeOffset) {
GrAssert(check_layout(vertexLayout));
int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords];
int size = VertexSizeAndOffsetsByIdx(vertexLayout,
(NULL == texCoordOffsetsByStage) ?
NULL :
texCoordOffsetsByIdx,
colorOffset,
coverageOffset,
edgeOffset);
if (NULL != texCoordOffsetsByStage) {
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
int tcIdx = VertexTexCoordsForStage(s, vertexLayout);
texCoordOffsetsByStage[s] =
tcIdx < 0 ? 0 : texCoordOffsetsByIdx[tcIdx];
}
}
return size;
}
////////////////////////////////////////////////////////////////////////////////
bool GrDrawTarget::VertexUsesTexCoordIdx(int coordIndex,
GrVertexLayout vertexLayout) {
GrAssert(coordIndex < GrDrawState::kMaxTexCoords);
GrAssert(check_layout(vertexLayout));
return !!(gTexCoordMasks[coordIndex] & vertexLayout);
}
int GrDrawTarget::VertexTexCoordsForStage(int stage,
GrVertexLayout vertexLayout) {
GrAssert(stage < GrDrawState::kNumStages);
GrAssert(check_layout(vertexLayout));
int bit = vertexLayout & gStageTexCoordMasks[stage];
if (bit) {
// figure out which set of texture coordates is used
// bits are ordered T0S0, T0S1, T0S2, ..., T1S0, T1S1, ...
// and start at bit 0.
GR_STATIC_ASSERT(sizeof(GrVertexLayout) <= sizeof(uint32_t));
return (32 - Gr_clz(bit) - 1) / GrDrawState::kNumStages;
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
void GrDrawTarget::VertexLayoutUnitTest() {
// Ensure that our globals mask arrays are correct
GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
GrVertexLayout texCoordMasks[GrDrawState::kMaxTexCoords];
gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
GrAssert(stageTexCoordMasks[s] == gStageTexCoordMasks[s]);
}
for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
GrAssert(texCoordMasks[t] == gTexCoordMasks[t]);
}
// not necessarily exhaustive
static bool run;
if (!run) {
run = true;
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
GrVertexLayout stageMask = 0;
for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
stageMask |= StageTexCoordVertexLayoutBit(s,t);
}
GrAssert(1 == GrDrawState::kMaxTexCoords ||
!check_layout(stageMask));
GrAssert(gStageTexCoordMasks[s] == stageMask);
GrAssert(!check_layout(stageMask));
}
for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
GrVertexLayout tcMask = 0;
GrAssert(!VertexUsesTexCoordIdx(t, 0));
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
tcMask |= StageTexCoordVertexLayoutBit(s,t);
GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
GrAssert(VertexUsesTexCoordIdx(t, tcMask));
GrAssert(2*sizeof(GrPoint) == VertexSize(tcMask));
GrAssert(t == VertexTexCoordsForStage(s, tcMask));
for (int s2 = s + 1; s2 < GrDrawState::kNumStages; ++s2) {
GrAssert(-1 == VertexTexCoordsForStage(s2, tcMask));
#if GR_DEBUG
GrVertexLayout posAsTex = tcMask;
#endif
GrAssert(0 == VertexStageCoordOffset(s2, posAsTex));
GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
GrAssert(-1 == VertexEdgeOffset(posAsTex));
}
GrAssert(-1 == VertexEdgeOffset(tcMask));
GrAssert(-1 == VertexColorOffset(tcMask));
GrAssert(-1 == VertexCoverageOffset(tcMask));
#if GR_DEBUG
GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
#endif
GrAssert(-1 == VertexCoverageOffset(withColor));
GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
#if GR_DEBUG
GrVertexLayout withEdge = tcMask | kEdge_VertexLayoutBit;
#endif
GrAssert(-1 == VertexColorOffset(withEdge));
GrAssert(2*sizeof(GrPoint) == VertexEdgeOffset(withEdge));
GrAssert(4*sizeof(GrPoint) == VertexSize(withEdge));
#if GR_DEBUG
GrVertexLayout withColorAndEdge = withColor | kEdge_VertexLayoutBit;
#endif
GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge));
GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge));
GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge));
#if GR_DEBUG
GrVertexLayout withCoverage = tcMask | kCoverage_VertexLayoutBit;
#endif
GrAssert(-1 == VertexColorOffset(withCoverage));
GrAssert(2*sizeof(GrPoint) == VertexCoverageOffset(withCoverage));
GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withCoverage));
#if GR_DEBUG
GrVertexLayout withCoverageAndColor = tcMask | kCoverage_VertexLayoutBit |
kColor_VertexLayoutBit;
#endif
GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withCoverageAndColor));
GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexCoverageOffset(withCoverageAndColor));
GrAssert(2*sizeof(GrPoint) + 2 * sizeof(GrColor) == VertexSize(withCoverageAndColor));
}
GrAssert(gTexCoordMasks[t] == tcMask);
GrAssert(check_layout(tcMask));
int stageOffsets[GrDrawState::kNumStages];
int colorOffset;
int edgeOffset;
int coverageOffset;
int size;
size = VertexSizeAndOffsetsByStage(tcMask,
stageOffsets, &colorOffset,
&coverageOffset, &edgeOffset);
GrAssert(2*sizeof(GrPoint) == size);
GrAssert(-1 == colorOffset);
GrAssert(-1 == coverageOffset);
GrAssert(-1 == edgeOffset);
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
GrAssert(sizeof(GrPoint) == stageOffsets[s]);
GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
#define DEBUG_INVAL_BUFFER 0xdeadcafe
#define DEBUG_INVAL_START_IDX -1
GrDrawTarget::GrDrawTarget() : fClip(NULL) {
#if GR_DEBUG
VertexLayoutUnitTest();
#endif
fDrawState = &fDefaultDrawState;
// We assume that fDrawState always owns a ref to the object it points at.
fDefaultDrawState.ref();
GeometrySrcState& geoSrc = fGeoSrcStateStack.push_back();
#if GR_DEBUG
geoSrc.fVertexCount = DEBUG_INVAL_START_IDX;
geoSrc.fVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
geoSrc.fIndexCount = DEBUG_INVAL_START_IDX;
geoSrc.fIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
#endif
geoSrc.fVertexSrc = kNone_GeometrySrcType;
geoSrc.fIndexSrc = kNone_GeometrySrcType;
}
GrDrawTarget::~GrDrawTarget() {
GrAssert(1 == fGeoSrcStateStack.count());
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
GrAssert(kNone_GeometrySrcType == geoSrc.fIndexSrc);
GrAssert(kNone_GeometrySrcType == geoSrc.fVertexSrc);
fDrawState->unref();
}
void GrDrawTarget::releaseGeometry() {
int popCnt = fGeoSrcStateStack.count() - 1;
while (popCnt) {
this->popGeometrySource();
--popCnt;
}
this->resetVertexSource();
this->resetIndexSource();
}
void GrDrawTarget::setClip(const GrClipData* clip) {
clipWillBeSet(clip);
fClip = clip;
}
const GrClipData* GrDrawTarget::getClip() const {
return fClip;
}
void GrDrawTarget::setDrawState(GrDrawState* drawState) {
GrAssert(NULL != fDrawState);
if (NULL == drawState) {
drawState = &fDefaultDrawState;
}
if (fDrawState != drawState) {
fDrawState->unref();
drawState->ref();
fDrawState = drawState;
}
}
bool GrDrawTarget::reserveVertexSpace(GrVertexLayout vertexLayout,
int vertexCount,
void** vertices) {
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
bool acquired = false;
if (vertexCount > 0) {
GrAssert(NULL != vertices);
this->releasePreviousVertexSource();
geoSrc.fVertexSrc = kNone_GeometrySrcType;
acquired = this->onReserveVertexSpace(vertexLayout,
vertexCount,
vertices);
}
if (acquired) {
geoSrc.fVertexSrc = kReserved_GeometrySrcType;
geoSrc.fVertexCount = vertexCount;
geoSrc.fVertexLayout = vertexLayout;
} else if (NULL != vertices) {
*vertices = NULL;
}
return acquired;
}
bool GrDrawTarget::reserveIndexSpace(int indexCount,
void** indices) {
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
bool acquired = false;
if (indexCount > 0) {
GrAssert(NULL != indices);
this->releasePreviousIndexSource();
geoSrc.fIndexSrc = kNone_GeometrySrcType;
acquired = this->onReserveIndexSpace(indexCount, indices);
}
if (acquired) {
geoSrc.fIndexSrc = kReserved_GeometrySrcType;
geoSrc.fIndexCount = indexCount;
} else if (NULL != indices) {
*indices = NULL;
}
return acquired;
}
bool GrDrawTarget::StageUsesTexCoords(GrVertexLayout layout, int stage) {
return SkToBool(layout & gStageTexCoordMasks[stage]);
}
bool GrDrawTarget::reserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
int vertexCount,
int indexCount,
void** vertices,
void** indices) {
this->willReserveVertexAndIndexSpace(vertexLayout, vertexCount, indexCount);
if (vertexCount) {
if (!this->reserveVertexSpace(vertexLayout, vertexCount, vertices)) {
if (indexCount) {
this->resetIndexSource();
}
return false;
}
}
if (indexCount) {
if (!this->reserveIndexSpace(indexCount, indices)) {
if (vertexCount) {
this->resetVertexSource();
}
return false;
}
}
return true;
}
bool GrDrawTarget::geometryHints(GrVertexLayout vertexLayout,
int32_t* vertexCount,
int32_t* indexCount) const {
if (NULL != vertexCount) {
*vertexCount = -1;
}
if (NULL != indexCount) {
*indexCount = -1;
}
return false;
}
void GrDrawTarget::releasePreviousVertexSource() {
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
switch (geoSrc.fVertexSrc) {
case kNone_GeometrySrcType:
break;
case kArray_GeometrySrcType:
this->releaseVertexArray();
break;
case kReserved_GeometrySrcType:
this->releaseReservedVertexSpace();
break;
case kBuffer_GeometrySrcType:
geoSrc.fVertexBuffer->unref();
#if GR_DEBUG
geoSrc.fVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
#endif
break;
default:
GrCrash("Unknown Vertex Source Type.");
break;
}
}
void GrDrawTarget::releasePreviousIndexSource() {
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
switch (geoSrc.fIndexSrc) {
case kNone_GeometrySrcType: // these two don't require
break;
case kArray_GeometrySrcType:
this->releaseIndexArray();
break;
case kReserved_GeometrySrcType:
this->releaseReservedIndexSpace();
break;
case kBuffer_GeometrySrcType:
geoSrc.fIndexBuffer->unref();
#if GR_DEBUG
geoSrc.fIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
#endif
break;
default:
GrCrash("Unknown Index Source Type.");
break;
}
}
void GrDrawTarget::setVertexSourceToArray(GrVertexLayout vertexLayout,
const void* vertexArray,
int vertexCount) {
this->releasePreviousVertexSource();
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
geoSrc.fVertexSrc = kArray_GeometrySrcType;
geoSrc.fVertexLayout = vertexLayout;
geoSrc.fVertexCount = vertexCount;
this->onSetVertexSourceToArray(vertexArray, vertexCount);
}
void GrDrawTarget::setIndexSourceToArray(const void* indexArray,
int indexCount) {
this->releasePreviousIndexSource();
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
geoSrc.fIndexSrc = kArray_GeometrySrcType;
geoSrc.fIndexCount = indexCount;
this->onSetIndexSourceToArray(indexArray, indexCount);
}
void GrDrawTarget::setVertexSourceToBuffer(GrVertexLayout vertexLayout,
const GrVertexBuffer* buffer) {
this->releasePreviousVertexSource();
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
geoSrc.fVertexSrc = kBuffer_GeometrySrcType;
geoSrc.fVertexBuffer = buffer;
buffer->ref();
geoSrc.fVertexLayout = vertexLayout;
}
void GrDrawTarget::setIndexSourceToBuffer(const GrIndexBuffer* buffer) {
this->releasePreviousIndexSource();
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
geoSrc.fIndexSrc = kBuffer_GeometrySrcType;
geoSrc.fIndexBuffer = buffer;
buffer->ref();
}
void GrDrawTarget::resetVertexSource() {
this->releasePreviousVertexSource();
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
geoSrc.fVertexSrc = kNone_GeometrySrcType;
}
void GrDrawTarget::resetIndexSource() {
this->releasePreviousIndexSource();
GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
geoSrc.fIndexSrc = kNone_GeometrySrcType;
}
void GrDrawTarget::pushGeometrySource() {
this->geometrySourceWillPush();
GeometrySrcState& newState = fGeoSrcStateStack.push_back();
newState.fIndexSrc = kNone_GeometrySrcType;
newState.fVertexSrc = kNone_GeometrySrcType;
#if GR_DEBUG
newState.fVertexCount = ~0;
newState.fVertexBuffer = (GrVertexBuffer*)~0;
newState.fIndexCount = ~0;
newState.fIndexBuffer = (GrIndexBuffer*)~0;
#endif
}
void GrDrawTarget::popGeometrySource() {
// if popping last element then pops are unbalanced with pushes
GrAssert(fGeoSrcStateStack.count() > 1);
this->geometrySourceWillPop(fGeoSrcStateStack.fromBack(1));
this->releasePreviousVertexSource();
this->releasePreviousIndexSource();
fGeoSrcStateStack.pop_back();
}
////////////////////////////////////////////////////////////////////////////////
bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex,
int startIndex, int vertexCount,
int indexCount) const {
const GrDrawState& drawState = this->getDrawState();
#if GR_DEBUG
const GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
int maxVertex = startVertex + vertexCount;
int maxValidVertex;
switch (geoSrc.fVertexSrc) {
case kNone_GeometrySrcType:
GrCrash("Attempting to draw without vertex src.");
case kReserved_GeometrySrcType: // fallthrough
case kArray_GeometrySrcType:
maxValidVertex = geoSrc.fVertexCount;
break;
case kBuffer_GeometrySrcType:
maxValidVertex = geoSrc.fVertexBuffer->sizeInBytes() / VertexSize(geoSrc.fVertexLayout);
break;
}
if (maxVertex > maxValidVertex) {
GrCrash("Drawing outside valid vertex range.");
}
if (indexCount > 0) {
int maxIndex = startIndex + indexCount;
int maxValidIndex;
switch (geoSrc.fIndexSrc) {
case kNone_GeometrySrcType:
GrCrash("Attempting to draw indexed geom without index src.");
case kReserved_GeometrySrcType: // fallthrough
case kArray_GeometrySrcType:
maxValidIndex = geoSrc.fIndexCount;
break;
case kBuffer_GeometrySrcType:
maxValidIndex = geoSrc.fIndexBuffer->sizeInBytes() / sizeof(uint16_t);
break;
}
if (maxIndex > maxValidIndex) {
GrCrash("Index reads outside valid index range.");
}
}
GrAssert(NULL != drawState.getRenderTarget());
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
if (drawState.isStageEnabled(s)) {
const GrCustomStage* stage = drawState.getSampler(s).getCustomStage();
int numTextures = stage->numTextures();
for (int t = 0; t < numTextures; ++t) {
GrTexture* texture = stage->texture(t);
GrAssert(texture->asRenderTarget() != drawState.getRenderTarget());
}
}
}
#endif
if (NULL == drawState.getRenderTarget()) {
return false;
}
return true;
}
void GrDrawTarget::drawIndexed(GrPrimitiveType type, int startVertex,
int startIndex, int vertexCount,
int indexCount) {
if (indexCount > 0 &&
this->checkDraw(type, startVertex, startIndex,
vertexCount, indexCount)) {
this->onDrawIndexed(type, startVertex, startIndex,
vertexCount, indexCount);
}
}
void GrDrawTarget::drawNonIndexed(GrPrimitiveType type,
int startVertex,
int vertexCount) {
if (vertexCount > 0 &&
this->checkDraw(type, startVertex, -1, vertexCount, -1)) {
this->onDrawNonIndexed(type, startVertex, vertexCount);
}
}
void GrDrawTarget::stencilPath(const GrPath* path, GrPathFill fill) {
// TODO: extract portions of checkDraw that are relevant to path stenciling.
GrAssert(NULL != path);
GrAssert(fCaps.pathStencilingSupport());
GrAssert(kHairLine_GrPathFill != fill);
GrAssert(!GrIsFillInverted(fill));
this->onStencilPath(path, fill);
}
////////////////////////////////////////////////////////////////////////////////
// Some blend modes allow folding a partial coverage value into the color's
// alpha channel, while others will blend incorrectly.
bool GrDrawTarget::canTweakAlphaForCoverage() const {
/**
* The fractional coverage is f
* The src and dst coeffs are Cs and Cd
* The dst and src colors are S and D
* We want the blend to compute: f*Cs*S + (f*Cd + (1-f))D
* By tweaking the source color's alpha we're replacing S with S'=fS. It's
* obvious that that first term will always be ok. The second term can be
* rearranged as [1-(1-Cd)f]D. By substituing in the various possbilities
* for Cd we find that only 1, ISA, and ISC produce the correct depth
* coeffecient in terms of S' and D.
*/
GrBlendCoeff dstCoeff = this->getDrawState().getDstBlendCoeff();
return kOne_GrBlendCoeff == dstCoeff ||
kISA_GrBlendCoeff == dstCoeff ||
kISC_GrBlendCoeff == dstCoeff;
}
bool GrDrawTarget::srcAlphaWillBeOne(GrVertexLayout layout) const {
const GrDrawState& drawState = this->getDrawState();
// Check if per-vertex or constant color may have partial alpha
if ((layout & kColor_VertexLayoutBit) ||
0xff != GrColorUnpackA(drawState.getColor())) {
return false;
}
// Check if color filter could introduce an alpha
// (TODO: Consider being more aggressive with regards to detecting 0xff
// final alpha from color filter).
if (SkXfermode::kDst_Mode != drawState.getColorFilterMode()) {
return false;
}
// Check if a color stage could create a partial alpha
for (int s = 0; s < drawState.getFirstCoverageStage(); ++s) {
if (this->isStageEnabled(s)) {
const GrCustomStage* stage = drawState.getSampler(s).getCustomStage();
// FIXME: The param indicates whether the texture is opaque or not. However, the stage
// already controls its textures. It really needs to know whether the incoming color
// (from a uni, per-vertex colors, or previous stage) is opaque or not.
if (!stage->isOpaque(true)) {
return false;
}
}
}
return true;
}
namespace {
GrVertexLayout default_blend_opts_vertex_layout() {
GrVertexLayout layout = 0;
return layout;
}
}
GrDrawTarget::BlendOptFlags
GrDrawTarget::getBlendOpts(bool forceCoverage,
GrBlendCoeff* srcCoeff,
GrBlendCoeff* dstCoeff) const {
GrVertexLayout layout;
if (kNone_GeometrySrcType == this->getGeomSrc().fVertexSrc) {
layout = default_blend_opts_vertex_layout();
} else {
layout = this->getVertexLayout();
}
const GrDrawState& drawState = this->getDrawState();
GrBlendCoeff bogusSrcCoeff, bogusDstCoeff;
if (NULL == srcCoeff) {
srcCoeff = &bogusSrcCoeff;
}
*srcCoeff = drawState.getSrcBlendCoeff();
if (NULL == dstCoeff) {
dstCoeff = &bogusDstCoeff;
}
*dstCoeff = drawState.getDstBlendCoeff();
// We don't ever expect source coeffecients to reference the source
GrAssert(kSA_GrBlendCoeff != *srcCoeff &&
kISA_GrBlendCoeff != *srcCoeff &&
kSC_GrBlendCoeff != *srcCoeff &&
kISC_GrBlendCoeff != *srcCoeff);
// same for dst
GrAssert(kDA_GrBlendCoeff != *dstCoeff &&
kIDA_GrBlendCoeff != *dstCoeff &&
kDC_GrBlendCoeff != *dstCoeff &&
kIDC_GrBlendCoeff != *dstCoeff);
if (drawState.isColorWriteDisabled()) {
*srcCoeff = kZero_GrBlendCoeff;
*dstCoeff = kOne_GrBlendCoeff;
}
bool srcAIsOne = this->srcAlphaWillBeOne(layout);
bool dstCoeffIsOne = kOne_GrBlendCoeff == *dstCoeff ||
(kSA_GrBlendCoeff == *dstCoeff && srcAIsOne);
bool dstCoeffIsZero = kZero_GrBlendCoeff == *dstCoeff ||
(kISA_GrBlendCoeff == *dstCoeff && srcAIsOne);
// When coeffs are (0,1) there is no reason to draw at all, unless
// stenciling is enabled. Having color writes disabled is effectively
// (0,1). The same applies when coverage is known to be 0.
if ((kZero_GrBlendCoeff == *srcCoeff && dstCoeffIsOne) ||
(!(layout & kCoverage_VertexLayoutBit) &&
0 == drawState.getCoverage())) {
if (drawState.getStencil().doesWrite()) {
return kDisableBlend_BlendOptFlag |
kEmitTransBlack_BlendOptFlag;
} else {
return kSkipDraw_BlendOptFlag;
}
}
// check for coverage due to constant coverage, per-vertex coverage,
// edge aa or coverage texture stage
bool hasCoverage = forceCoverage ||
0xffffffff != drawState.getCoverage() ||
(layout & kCoverage_VertexLayoutBit) ||
(layout & kEdge_VertexLayoutBit);
for (int s = drawState.getFirstCoverageStage();
!hasCoverage && s < GrDrawState::kNumStages;
++s) {
if (this->isStageEnabled(s)) {
hasCoverage = true;
}
}
// if we don't have coverage we can check whether the dst
// has to read at all. If not, we'll disable blending.
if (!hasCoverage) {
if (dstCoeffIsZero) {
if (kOne_GrBlendCoeff == *srcCoeff) {
// if there is no coverage and coeffs are (1,0) then we
// won't need to read the dst at all, it gets replaced by src
return kDisableBlend_BlendOptFlag;
} else if (kZero_GrBlendCoeff == *srcCoeff) {
// if the op is "clear" then we don't need to emit a color
// or blend, just write transparent black into the dst.
*srcCoeff = kOne_GrBlendCoeff;
*dstCoeff = kZero_GrBlendCoeff;
return kDisableBlend_BlendOptFlag |
kEmitTransBlack_BlendOptFlag;
}
}
} else {
// check whether coverage can be safely rolled into alpha
// of if we can skip color computation and just emit coverage
if (this->canTweakAlphaForCoverage()) {
return kCoverageAsAlpha_BlendOptFlag;
}
if (dstCoeffIsZero) {
if (kZero_GrBlendCoeff == *srcCoeff) {
// the source color is not included in the blend
// the dst coeff is effectively zero so blend works out to:
// (c)(0)D + (1-c)D = (1-c)D.
*dstCoeff = kISA_GrBlendCoeff;
return kEmitCoverage_BlendOptFlag;
} else if (srcAIsOne) {
// the dst coeff is effectively zero so blend works out to:
// cS + (c)(0)D + (1-c)D = cS + (1-c)D.
// If Sa is 1 then we can replace Sa with c
// and set dst coeff to 1-Sa.
*dstCoeff = kISA_GrBlendCoeff;
return kCoverageAsAlpha_BlendOptFlag;
}
} else if (dstCoeffIsOne) {
// the dst coeff is effectively one so blend works out to:
// cS + (c)(1)D + (1-c)D = cS + D.
*dstCoeff = kOne_GrBlendCoeff;
return kCoverageAsAlpha_BlendOptFlag;
}
}
return kNone_BlendOpt;
}
bool GrDrawTarget::willUseHWAALines() const {
// there is a conflict between using smooth lines and our use of
// premultiplied alpha. Smooth lines tweak the incoming alpha value
// but not in a premul-alpha way. So we only use them when our alpha
// is 0xff and tweaking the color for partial coverage is OK
if (!fCaps.hwAALineSupport() ||
!this->getDrawState().isHWAntialiasState()) {
return false;
}
BlendOptFlags opts = this->getBlendOpts();
return (kDisableBlend_BlendOptFlag & opts) &&
(kCoverageAsAlpha_BlendOptFlag & opts);
}
bool GrDrawTarget::canApplyCoverage() const {
// we can correctly apply coverage if a) we have dual source blending
// or b) one of our blend optimizations applies.
return this->getCaps().dualSourceBlendingSupport() ||
kNone_BlendOpt != this->getBlendOpts(true);
}
////////////////////////////////////////////////////////////////////////////////
void GrDrawTarget::drawIndexedInstances(GrPrimitiveType type,
int instanceCount,
int verticesPerInstance,
int indicesPerInstance) {
if (!verticesPerInstance || !indicesPerInstance) {
return;
}
int instancesPerDraw = this->indexCountInCurrentSource() /
indicesPerInstance;
if (!instancesPerDraw) {
return;
}
instancesPerDraw = GrMin(instanceCount, instancesPerDraw);
int startVertex = 0;
while (instanceCount) {
this->drawIndexed(type,
startVertex,
0,
verticesPerInstance * instancesPerDraw,
indicesPerInstance * instancesPerDraw);
startVertex += verticesPerInstance;
instanceCount -= instancesPerDraw;
}
}
////////////////////////////////////////////////////////////////////////////////
void GrDrawTarget::drawRect(const GrRect& rect,
const GrMatrix* matrix,
const GrRect* srcRects[],
const GrMatrix* srcMatrices[]) {
GrVertexLayout layout = GetRectVertexLayout(srcRects);
AutoReleaseGeometry geo(this, layout, 4, 0);
if (!geo.succeeded()) {
GrPrintf("Failed to get space for vertices!\n");
return;
}
SetRectVertices(rect, matrix, srcRects,
srcMatrices, SK_ColorBLACK, layout, geo.vertices());
drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
}
GrVertexLayout GrDrawTarget::GetRectVertexLayout(const GrRect* srcRects[]) {
if (NULL == srcRects) {
return 0;
}
GrVertexLayout layout = 0;
for (int i = 0; i < GrDrawState::kNumStages; ++i) {
int numTC = 0;
if (NULL != srcRects[i]) {
layout |= StageTexCoordVertexLayoutBit(i, numTC);
++numTC;
}
}
return layout;
}
// This method fills int the four vertices for drawing 'rect'.
// matrix - is applied to each vertex
// srcRects - provide the uvs for each vertex
// srcMatrices - are applied to the corresponding 'srcRect'
// color - vertex color (replicated in each vertex)
// layout - specifies which uvs and/or color are present
// vertices - storage for the resulting vertices
// Note: the color parameter will only be used when kColor_VertexLayoutBit
// is present in 'layout'
void GrDrawTarget::SetRectVertices(const GrRect& rect,
const GrMatrix* matrix,
const GrRect* srcRects[],
const GrMatrix* srcMatrices[],
GrColor color,
GrVertexLayout layout,
void* vertices) {
#if GR_DEBUG
// check that the layout and srcRects agree
for (int i = 0; i < GrDrawState::kNumStages; ++i) {
if (VertexTexCoordsForStage(i, layout) >= 0) {
GR_DEBUGASSERT(NULL != srcRects && NULL != srcRects[i]);
} else {
GR_DEBUGASSERT(NULL == srcRects || NULL == srcRects[i]);
}
}
#endif
int stageOffsets[GrDrawState::kNumStages], colorOffset;
int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets,
&colorOffset, NULL, NULL);
GrTCast<GrPoint*>(vertices)->setRectFan(rect.fLeft, rect.fTop,
rect.fRight, rect.fBottom,
vsize);
if (NULL != matrix) {
matrix->mapPointsWithStride(GrTCast<GrPoint*>(vertices), vsize, 4);
}
for (int i = 0; i < GrDrawState::kNumStages; ++i) {
if (stageOffsets[i] > 0) {
GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(vertices) +
stageOffsets[i]);
coords->setRectFan(srcRects[i]->fLeft, srcRects[i]->fTop,
srcRects[i]->fRight, srcRects[i]->fBottom,
vsize);
if (NULL != srcMatrices && NULL != srcMatrices[i]) {
srcMatrices[i]->mapPointsWithStride(coords, vsize, 4);
}
}
}
if (layout & kColor_VertexLayoutBit) {
GrColor* vertCol = GrTCast<GrColor*>(GrTCast<intptr_t>(vertices) + colorOffset);
for (int i = 0; i < 4; ++i) {
*vertCol = color;
vertCol = (GrColor*) ((intptr_t) vertCol + vsize);
}
}
}
////////////////////////////////////////////////////////////////////////////////
GrDrawTarget::AutoStateRestore::AutoStateRestore() {
fDrawTarget = NULL;
}
GrDrawTarget::AutoStateRestore::AutoStateRestore(GrDrawTarget* target,
ASRInit init) {
fDrawTarget = NULL;
this->set(target, init);
}
GrDrawTarget::AutoStateRestore::~AutoStateRestore() {
if (NULL != fDrawTarget) {
fDrawTarget->setDrawState(fSavedState);
fSavedState->unref();
}
}
void GrDrawTarget::AutoStateRestore::set(GrDrawTarget* target, ASRInit init) {
GrAssert(NULL == fDrawTarget);
fDrawTarget = target;
fSavedState = target->drawState();
GrAssert(fSavedState);
fSavedState->ref();
if (kReset_ASRInit == init) {
// calls the default cons
fTempState.init();
} else {
GrAssert(kPreserve_ASRInit == init);
// calls the copy cons
fTempState.set(*fSavedState);
}
target->setDrawState(fTempState.get());
}
////////////////////////////////////////////////////////////////////////////////
GrDrawTarget::AutoReleaseGeometry::AutoReleaseGeometry(
GrDrawTarget* target,
GrVertexLayout vertexLayout,
int vertexCount,
int indexCount) {
fTarget = NULL;
this->set(target, vertexLayout, vertexCount, indexCount);
}
GrDrawTarget::AutoReleaseGeometry::AutoReleaseGeometry() {
fTarget = NULL;
}
GrDrawTarget::AutoReleaseGeometry::~AutoReleaseGeometry() {
this->reset();
}
bool GrDrawTarget::AutoReleaseGeometry::set(GrDrawTarget* target,
GrVertexLayout vertexLayout,
int vertexCount,
int indexCount) {
this->reset();
fTarget = target;
bool success = true;
if (NULL != fTarget) {
fTarget = target;
success = target->reserveVertexAndIndexSpace(vertexLayout,
vertexCount,
indexCount,
&fVertices,
&fIndices);
if (!success) {
fTarget = NULL;
this->reset();
}
}
GrAssert(success == (NULL != fTarget));
return success;
}
void GrDrawTarget::AutoReleaseGeometry::reset() {
if (NULL != fTarget) {
if (NULL != fVertices) {
fTarget->resetVertexSource();
}
if (NULL != fIndices) {
fTarget->resetIndexSource();
}
fTarget = NULL;
}
fVertices = NULL;
fIndices = NULL;
}
void GrDrawTarget::Caps::print() const {
static const char* gNY[] = {"NO", "YES"};
GrPrintf("8 Bit Palette Support : %s\n", gNY[fInternals.f8BitPaletteSupport]);
GrPrintf("NPOT Texture Tile Support : %s\n", gNY[fInternals.fNPOTTextureTileSupport]);
GrPrintf("Two Sided Stencil Support : %s\n", gNY[fInternals.fTwoSidedStencilSupport]);
GrPrintf("Stencil Wrap Ops Support : %s\n", gNY[fInternals.fStencilWrapOpsSupport]);
GrPrintf("HW AA Lines Support : %s\n", gNY[fInternals.fHWAALineSupport]);
GrPrintf("Shader Derivative Support : %s\n", gNY[fInternals.fShaderDerivativeSupport]);
GrPrintf("Geometry Shader Support : %s\n", gNY[fInternals.fGeometryShaderSupport]);
GrPrintf("FSAA Support : %s\n", gNY[fInternals.fFSAASupport]);
GrPrintf("Dual Source Blending Support: %s\n", gNY[fInternals.fDualSourceBlendingSupport]);
GrPrintf("Buffer Lock Support : %s\n", gNY[fInternals.fBufferLockSupport]);
GrPrintf("Path Stenciling Support : %s\n", gNY[fInternals.fPathStencilingSupport]);
GrPrintf("Max Texture Size : %d\n", fInternals.fMaxTextureSize);
GrPrintf("Max Render Target Size : %d\n", fInternals.fMaxRenderTargetSize);
}