/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrDrawState.h"
#include "GrGpuVertex.h"
#include "GrPaint.h"
void GrDrawState::setFromPaint(const GrPaint& paint) {
for (int i = 0; i < GrPaint::kMaxColorStages; ++i) {
int s = i + GrPaint::kFirstColorStage;
if (paint.isColorStageEnabled(i)) {
fStages[s] = paint.getColorStage(i);
} else {
fStages[s].setEffect(NULL);
}
}
this->setFirstCoverageStage(GrPaint::kFirstCoverageStage);
for (int i = 0; i < GrPaint::kMaxCoverageStages; ++i) {
int s = i + GrPaint::kFirstCoverageStage;
if (paint.isCoverageStageEnabled(i)) {
fStages[s] = paint.getCoverageStage(i);
} else {
fStages[s].setEffect(NULL);
}
}
// disable all stages not accessible via the paint
for (int s = GrPaint::kTotalStages; s < GrDrawState::kNumStages; ++s) {
this->disableStage(s);
}
this->setColor(paint.getColor());
this->setState(GrDrawState::kDither_StateBit, paint.isDither());
this->setState(GrDrawState::kHWAntialias_StateBit, paint.isAntiAlias());
this->setBlendFunc(paint.getSrcBlendCoeff(), paint.getDstBlendCoeff());
this->setColorFilter(paint.getColorFilterColor(), paint.getColorFilterMode());
this->setCoverage(paint.getCoverage());
}
////////////////////////////////////////////////////////////////////////////////
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 GrDrawState.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] |= GrDrawState::StageTexCoordVertexLayoutBit(s, t);
}
}
for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
texCoordMasks[t] = 0;
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
texCoordMasks[t] |= GrDrawState::StageTexCoordVertexLayoutBit(s, t);
}
}
}
/**
* Uncomment and run the gen_globals function to generate
* the code that declares the global masks.
*
* #if 0'ed out to avoid unused function warning.
*/
#if 0
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");
}
#endif
/* 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));
#ifdef SK_DEBUG
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;
}
#endif
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 GrDrawState::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(SkScalar);
}
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 GrDrawState::VertexStageCoordOffset(int stageIdx, GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
if (!StageUsesTexCoords(vertexLayout, stageIdx)) {
return 0;
}
int tcIdx = VertexTexCoordsForStage(stageIdx, 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 GrDrawState::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 GrDrawState::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 GrDrawState::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 GrDrawState::VertexSizeAndOffsetsByIdx(
GrVertexLayout vertexLayout,
int texCoordOffsetsByIdx[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 < 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(SkScalar);
} else {
if (NULL != edgeOffset) {
*edgeOffset = -1;
}
}
return size;
}
int GrDrawState::VertexSizeAndOffsetsByStage(
GrVertexLayout vertexLayout,
int texCoordOffsetsByStage[GrDrawState::kNumStages],
int* colorOffset,
int* coverageOffset,
int* edgeOffset) {
GrAssert(check_layout(vertexLayout));
int texCoordOffsetsByIdx[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 GrDrawState::VertexUsesTexCoordIdx(int coordIndex,
GrVertexLayout vertexLayout) {
GrAssert(coordIndex < kMaxTexCoords);
GrAssert(check_layout(vertexLayout));
return !!(gTexCoordMasks[coordIndex] & vertexLayout);
}
int GrDrawState::VertexTexCoordsForStage(int stageIdx,
GrVertexLayout vertexLayout) {
GrAssert(stageIdx < GrDrawState::kNumStages);
GrAssert(check_layout(vertexLayout));
int bit = vertexLayout & gStageTexCoordMasks[stageIdx];
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 - SkCLZ(bit) - 1) / GrDrawState::kNumStages;
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////
void GrDrawState::VertexLayoutUnitTest() {
// Ensure that our globals mask arrays are correct
GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
GrVertexLayout texCoordMasks[kMaxTexCoords];
gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
GrAssert(stageTexCoordMasks[s] == gStageTexCoordMasks[s]);
}
for (int t = 0; t < 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 < kMaxTexCoords; ++t) {
stageMask |= StageTexCoordVertexLayoutBit(s,t);
}
GrAssert(1 == kMaxTexCoords ||
!check_layout(stageMask));
GrAssert(gStageTexCoordMasks[s] == stageMask);
GrAssert(!check_layout(stageMask));
}
for (int t = 0; t < 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));
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
bool GrDrawState::StageUsesTexCoords(GrVertexLayout layout, int stageIdx) {
return SkToBool(layout & gStageTexCoordMasks[stageIdx]);
}
bool GrDrawState::srcAlphaWillBeOne(GrVertexLayout layout) const {
uint32_t validComponentFlags;
GrColor color;
// Check if per-vertex or constant color may have partial alpha
if (layout & kColor_VertexLayoutBit) {
validComponentFlags = 0;
} else {
validComponentFlags = GrEffect::kAll_ValidComponentFlags;
color = this->getColor();
}
// Run through the color stages
int stageCnt = getFirstCoverageStage();
for (int s = 0; s < stageCnt; ++s) {
const GrEffectRef* effect = this->getStage(s).getEffect();
if (NULL != effect) {
(*effect)->getConstantColorComponents(&color, &validComponentFlags);
}
}
// Check if the color filter could introduce an alpha.
// We could skip the above work when this is true, but it is rare and the right fix is to make
// the color filter a GrEffect and implement getConstantColorComponents() for it.
if (SkXfermode::kDst_Mode != this->getColorFilterMode()) {
validComponentFlags = 0;
}
// Check whether coverage is treated as color. If so we run through the coverage computation.
if (this->isCoverageDrawing()) {
GrColor coverageColor = this->getCoverage();
GrColor oldColor = color;
color = 0;
for (int c = 0; c < 4; ++c) {
if (validComponentFlags & (1 << c)) {
U8CPU a = (oldColor >> (c * 8)) & 0xff;
U8CPU b = (coverageColor >> (c * 8)) & 0xff;
color |= (SkMulDiv255Round(a, b) << (c * 8));
}
}
for (int s = this->getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
const GrEffectRef* effect = this->getStage(s).getEffect();
if (NULL != effect) {
(*effect)->getConstantColorComponents(&color, &validComponentFlags);
}
}
}
return (GrEffect::kA_ValidComponentFlag & validComponentFlags) && 0xff == GrColorUnpackA(color);
}
bool GrDrawState::hasSolidCoverage(GrVertexLayout layout) const {
// If we're drawing coverage directly then coverage is effectively treated as color.
if (this->isCoverageDrawing()) {
return true;
}
GrColor coverage;
uint32_t validComponentFlags;
// Initialize to an unknown starting coverage if per-vertex coverage is specified.
if (layout & kCoverage_VertexLayoutBit) {
validComponentFlags = 0;
} else {
coverage = fCommon.fCoverage;
validComponentFlags = GrEffect::kAll_ValidComponentFlags;
}
// Run through the coverage stages and see if the coverage will be all ones at the end.
for (int s = this->getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
const GrEffectRef* effect = this->getStage(s).getEffect();
if (NULL != effect) {
(*effect)->getConstantColorComponents(&coverage, &validComponentFlags);
}
}
return (GrEffect::kAll_ValidComponentFlags == validComponentFlags) && (0xffffffff == coverage);
}
////////////////////////////////////////////////////////////////////////////////
void GrDrawState::AutoViewMatrixRestore::restore() {
if (NULL != fDrawState) {
fDrawState->setViewMatrix(fViewMatrix);
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
if (fRestoreMask & (1 << s)) {
fDrawState->fStages[s].restoreCoordChange(fSavedCoordChanges[s]);
}
}
}
fDrawState = NULL;
}
void GrDrawState::AutoViewMatrixRestore::set(GrDrawState* drawState,
const SkMatrix& preconcatMatrix,
uint32_t explicitCoordStageMask) {
this->restore();
fDrawState = drawState;
if (NULL == drawState) {
return;
}
fRestoreMask = 0;
fViewMatrix = drawState->getViewMatrix();
drawState->preConcatViewMatrix(preconcatMatrix);
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
if (!(explicitCoordStageMask & (1 << s)) && drawState->isStageEnabled(s)) {
fRestoreMask |= (1 << s);
fDrawState->fStages[s].saveCoordChange(&fSavedCoordChanges[s]);
drawState->fStages[s].preConcatCoordChange(preconcatMatrix);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void GrDrawState::AutoDeviceCoordDraw::restore() {
if (NULL != fDrawState) {
fDrawState->setViewMatrix(fViewMatrix);
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
if (fRestoreMask & (1 << s)) {
fDrawState->fStages[s].restoreCoordChange(fSavedCoordChanges[s]);
}
}
}
fDrawState = NULL;
}
bool GrDrawState::AutoDeviceCoordDraw::set(GrDrawState* drawState,
uint32_t explicitCoordStageMask) {
GrAssert(NULL != drawState);
this->restore();
fDrawState = drawState;
if (NULL == fDrawState) {
return false;
}
fViewMatrix = drawState->getViewMatrix();
fRestoreMask = 0;
SkMatrix invVM;
bool inverted = false;
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
if (!(explicitCoordStageMask & (1 << s)) && drawState->isStageEnabled(s)) {
if (!inverted && !fViewMatrix.invert(&invVM)) {
// sad trombone sound
fDrawState = NULL;
return false;
} else {
inverted = true;
}
fRestoreMask |= (1 << s);
GrEffectStage* stage = drawState->fStages + s;
stage->saveCoordChange(&fSavedCoordChanges[s]);
stage->preConcatCoordChange(invVM);
}
}
drawState->viewMatrix()->reset();
return true;
}