/*
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 "GrDrawTarget.h"
#include "GrGpuVertex.h"
#include "GrTexture.h"
namespace {
// recursive helper for creating mask with all the tex coord bits set for
// one stage
template <int N>
int stage_mask_recur(int stage) {
return GrDrawTarget::StageTexCoordVertexLayoutBit(stage, N) |
stage_mask_recur<N+1>(stage);
}
template<>
int stage_mask_recur<GrDrawTarget::kNumStages>(int) { return 0; }
// mask of all tex coord indices for one stage
int stage_tex_coord_mask(int stage) {
return stage_mask_recur<0>(stage);
}
// mask of all bits relevant to one stage
int stage_mask(int stage) {
return stage_tex_coord_mask(stage) |
GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(stage);
}
// recursive helper for creating mask of with all bits set relevant to one
// texture coordinate index
template <int N>
int tex_coord_mask_recur(int texCoordIdx) {
return GrDrawTarget::StageTexCoordVertexLayoutBit(N, texCoordIdx) |
tex_coord_mask_recur<N+1>(texCoordIdx);
}
template<>
int tex_coord_mask_recur<GrDrawTarget::kMaxTexCoords>(int) { return 0; }
// mask of all bits relevant to one texture coordinate index
int tex_coord_idx_mask(int texCoordIdx) {
return tex_coord_mask_recur<0>(texCoordIdx);
}
bool check_layout(GrVertexLayout layout) {
// can only have 1 or 0 bits set for each stage.
for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
int stageBits = layout & stage_mask(s);
if (stageBits && !GrIsPow2(stageBits)) {
return false;
}
}
return true;
}
} //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
for (int t = 0; t < kMaxTexCoords; ++t) {
if (tex_coord_idx_mask(t) & vertexLayout) {
size += vecSize;
}
}
if (vertexLayout & kColor_VertexLayoutBit) {
size += sizeof(GrColor);
}
return size;
}
int GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
if (StagePosAsTexCoordVertexLayoutBit(stage) & vertexLayout) {
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 (tex_coord_idx_mask(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);
int offset = vecSize; // position
// figure out how many tex coordinates are present and precede this one.
for (int t = 0; t < kMaxTexCoords; ++t) {
if (tex_coord_idx_mask(t) & vertexLayout) {
offset += vecSize;
}
}
return offset;
}
return -1;
}
int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
int texCoordOffsetsByIdx[kMaxTexCoords],
int* colorOffset) {
GrAssert(check_layout(vertexLayout));
GrAssert(NULL != texCoordOffsetsByIdx);
GrAssert(NULL != colorOffset);
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
int size = vecSize; // position
for (int t = 0; t < kMaxTexCoords; ++t) {
if (tex_coord_idx_mask(t) & vertexLayout) {
texCoordOffsetsByIdx[t] = size;
size += vecSize;
} else {
texCoordOffsetsByIdx[t] = -1;
}
}
if (kColor_VertexLayoutBit & vertexLayout) {
*colorOffset = size;
size += sizeof(GrColor);
} else {
*colorOffset = -1;
}
return size;
}
int GrDrawTarget::VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
int texCoordOffsetsByStage[kNumStages],
int* colorOffset) {
GrAssert(check_layout(vertexLayout));
GrAssert(NULL != texCoordOffsetsByStage);
GrAssert(NULL != colorOffset);
int texCoordOffsetsByIdx[kMaxTexCoords];
int size = VertexSizeAndOffsetsByIdx(vertexLayout,
texCoordOffsetsByIdx,
colorOffset);
for (int s = 0; s < kNumStages; ++s) {
int tcIdx;
if (StagePosAsTexCoordVertexLayoutBit(s) & vertexLayout) {
texCoordOffsetsByStage[s] = 0;
} else if ((tcIdx = VertexTexCoordsForStage(s, vertexLayout)) >= 0) {
texCoordOffsetsByStage[s] = texCoordOffsetsByIdx[tcIdx];
} else {
texCoordOffsetsByStage[s] = -1;
}
}
return size;
}
bool GrDrawTarget::VertexUsesStage(int stage, GrVertexLayout vertexLayout) {
GrAssert(stage < kNumStages);
GrAssert(check_layout(vertexLayout));
return !!(stage_mask(stage) & vertexLayout);
}
bool GrDrawTarget::VertexUsesTexCoordIdx(int coordIndex,
GrVertexLayout vertexLayout) {
GrAssert(coordIndex < kMaxTexCoords);
GrAssert(check_layout(vertexLayout));
return !!(tex_coord_idx_mask(coordIndex) & vertexLayout);
}
int GrDrawTarget::VertexTexCoordsForStage(int stage, GrVertexLayout vertexLayout) {
GrAssert(stage < kNumStages);
GrAssert(check_layout(vertexLayout));
int bit = vertexLayout & stage_tex_coord_mask(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) / kNumStages;
}
return -1;
}
void GrDrawTarget::VertexLayoutUnitTest() {
// not necessarily exhaustive
static bool run;
if (!run) {
run = true;
for (int s = 0; s < kNumStages; ++s) {
GrAssert(!VertexUsesStage(s, 0));
GrAssert(-1 == VertexStageCoordOffset(s, 0));
GrVertexLayout stageMask = 0;
for (int t = 0; t < kMaxTexCoords; ++t) {
stageMask |= StageTexCoordVertexLayoutBit(s,t);
}
GrAssert(1 == kMaxTexCoords || !check_layout(stageMask));
GrAssert(stage_tex_coord_mask(s) == stageMask);
stageMask |= StagePosAsTexCoordVertexLayoutBit(s);
GrAssert(stage_mask(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 < kNumStages; ++s) {
tcMask |= StageTexCoordVertexLayoutBit(s,t);
GrAssert(VertexUsesStage(s, tcMask));
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 < kNumStages; ++s2) {
GrAssert(-1 == VertexStageCoordOffset(s2, tcMask));
GrAssert(!VertexUsesStage(s2, tcMask));
GrAssert(-1 == VertexTexCoordsForStage(s2, tcMask));
#if GR_DEBUG
GrVertexLayout posAsTex = tcMask | StagePosAsTexCoordVertexLayoutBit(s2);
#endif
GrAssert(0 == VertexStageCoordOffset(s2, posAsTex));
GrAssert(VertexUsesStage(s2, posAsTex));
GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
}
#if GR_DEBUG
GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
#endif
GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
}
GrAssert(tex_coord_idx_mask(t) == tcMask);
GrAssert(check_layout(tcMask));
int stageOffsets[kNumStages];
int colorOffset;
int size;
size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset);
GrAssert(2*sizeof(GrPoint) == size);
GrAssert(-1 == colorOffset);
for (int s = 0; s < kNumStages; ++s) {
GrAssert(VertexUsesStage(s, tcMask));
GrAssert(sizeof(GrPoint) == stageOffsets[s]);
GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
GrDrawTarget::GrDrawTarget() {
#if GR_DEBUG
VertexLayoutUnitTest();
#endif
fReservedGeometry.fLocked = false;
#if GR_DEBUG
fReservedGeometry.fVertexCount = ~0;
fReservedGeometry.fIndexCount = ~0;
#endif
fGeometrySrc.fVertexSrc = kReserved_GeometrySrcType;
fGeometrySrc.fIndexSrc = kReserved_GeometrySrcType;
}
void GrDrawTarget::setClip(const GrClip& clip) {
clipWillBeSet(clip);
fClip = clip;
}
const GrClip& GrDrawTarget::getClip() const {
return fClip;
}
void GrDrawTarget::setTexture(int stage, GrTexture* tex) {
GrAssert(stage >= 0 && stage < kNumStages);
fCurrDrawState.fTextures[stage] = tex;
}
const GrTexture* GrDrawTarget::getTexture(int stage) const {
GrAssert(stage >= 0 && stage < kNumStages);
return fCurrDrawState.fTextures[stage];
}
GrTexture* GrDrawTarget::getTexture(int stage) {
GrAssert(stage >= 0 && stage < kNumStages);
return fCurrDrawState.fTextures[stage];
}
void GrDrawTarget::setRenderTarget(GrRenderTarget* target) {
fCurrDrawState.fRenderTarget = target;
}
const GrRenderTarget* GrDrawTarget::getRenderTarget() const {
return fCurrDrawState.fRenderTarget;
}
GrRenderTarget* GrDrawTarget::getRenderTarget() {
return fCurrDrawState.fRenderTarget;
}
void GrDrawTarget::setViewMatrix(const GrMatrix& m) {
fCurrDrawState.fViewMatrix = m;
}
void GrDrawTarget::preConcatViewMatrix(const GrMatrix& matrix) {
fCurrDrawState.fViewMatrix.preConcat(matrix);
}
void GrDrawTarget::postConcatViewMatrix(const GrMatrix& matrix) {
fCurrDrawState.fViewMatrix.postConcat(matrix);
}
const GrMatrix& GrDrawTarget::getViewMatrix() const {
return fCurrDrawState.fViewMatrix;
}
bool GrDrawTarget::getViewInverse(GrMatrix* matrix) const {
// Mike: Can we cache this somewhere?
// Brian: Sure, do we use it often?
GrMatrix inverse;
if (fCurrDrawState.fViewMatrix.invert(&inverse)) {
if (matrix) {
*matrix = inverse;
}
return true;
}
return false;
}
void GrDrawTarget::setSamplerState(int stage, const GrSamplerState& state) {
GrAssert(stage >= 0 && stage < kNumStages);
fCurrDrawState.fSamplerStates[stage] = state;
}
void GrDrawTarget::enableState(uint32_t bits) {
fCurrDrawState.fFlagBits |= bits;
}
void GrDrawTarget::disableState(uint32_t bits) {
fCurrDrawState.fFlagBits &= ~(bits);
}
void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoeff,
GrBlendCoeff dstCoeff) {
fCurrDrawState.fSrcBlend = srcCoeff;
fCurrDrawState.fDstBlend = dstCoeff;
#if GR_DEBUG
switch (dstCoeff) {
case kDC_BlendCoeff:
case kIDC_BlendCoeff:
case kDA_BlendCoeff:
case kIDA_BlendCoeff:
GrPrintf("Unexpected dst blend coeff. Won't work correctly with"
"coverage stages.\n");
break;
default:
break;
}
switch (srcCoeff) {
case kSC_BlendCoeff:
case kISC_BlendCoeff:
case kSA_BlendCoeff:
case kISA_BlendCoeff:
GrPrintf("Unexpected src blend coeff. Won't work correctly with"
"coverage stages.\n");
break;
default:
break;
}
#endif
}
void GrDrawTarget::setColor(GrColor c) {
fCurrDrawState.fColor = c;
}
void GrDrawTarget::setColorFilter(GrColor c, SkXfermode::Mode mode) {
fCurrDrawState.fColorFilterColor = c;
fCurrDrawState.fColorFilterXfermode = mode;
}
void GrDrawTarget::setAlpha(uint8_t a) {
this->setColor((a << 24) | (a << 16) | (a << 8) | a);
}
void GrDrawTarget::saveCurrentDrawState(SavedDrawState* state) const {
state->fState = fCurrDrawState;
}
void GrDrawTarget::restoreDrawState(const SavedDrawState& state) {
fCurrDrawState = state.fState;
}
void GrDrawTarget::copyDrawState(const GrDrawTarget& srcTarget) {
fCurrDrawState = srcTarget.fCurrDrawState;
}
bool GrDrawTarget::reserveAndLockGeometry(GrVertexLayout vertexLayout,
uint32_t vertexCount,
uint32_t indexCount,
void** vertices,
void** indices) {
GrAssert(!fReservedGeometry.fLocked);
fReservedGeometry.fVertexCount = vertexCount;
fReservedGeometry.fIndexCount = indexCount;
fReservedGeometry.fLocked = this->onAcquireGeometry(vertexLayout,
vertices,
indices);
if (fReservedGeometry.fLocked) {
if (vertexCount) {
fGeometrySrc.fVertexSrc = kReserved_GeometrySrcType;
fGeometrySrc.fVertexLayout = vertexLayout;
} else if (NULL != vertices) {
*vertices = NULL;
}
if (indexCount) {
fGeometrySrc.fIndexSrc = kReserved_GeometrySrcType;
} else if (NULL != indices) {
*indices = NULL;
}
}
return fReservedGeometry.fLocked;
}
bool GrDrawTarget::geometryHints(GrVertexLayout vertexLayout,
int32_t* vertexCount,
int32_t* indexCount) const {
GrAssert(!fReservedGeometry.fLocked);
if (NULL != vertexCount) {
*vertexCount = -1;
}
if (NULL != indexCount) {
*indexCount = -1;
}
return false;
}
void GrDrawTarget::releaseReservedGeometry() {
GrAssert(fReservedGeometry.fLocked);
this->onReleaseGeometry();
fReservedGeometry.fLocked = false;
}
void GrDrawTarget::setVertexSourceToArray(GrVertexLayout vertexLayout,
const void* vertexArray,
int vertexCount) {
fGeometrySrc.fVertexSrc = kArray_GeometrySrcType;
fGeometrySrc.fVertexLayout = vertexLayout;
this->onSetVertexSourceToArray(vertexArray, vertexCount);
}
void GrDrawTarget::setIndexSourceToArray(const void* indexArray,
int indexCount) {
fGeometrySrc.fIndexSrc = kArray_GeometrySrcType;
this->onSetIndexSourceToArray(indexArray, indexCount);
}
void GrDrawTarget::setVertexSourceToBuffer(GrVertexLayout vertexLayout,
const GrVertexBuffer* buffer) {
fGeometrySrc.fVertexSrc = kBuffer_GeometrySrcType;
fGeometrySrc.fVertexBuffer = buffer;
fGeometrySrc.fVertexLayout = vertexLayout;
}
void GrDrawTarget::setIndexSourceToBuffer(const GrIndexBuffer* buffer) {
fGeometrySrc.fIndexSrc = kBuffer_GeometrySrcType;
fGeometrySrc.fIndexBuffer = buffer;
}
///////////////////////////////////////////////////////////////////////////////
bool GrDrawTarget::canDisableBlend() const {
// If we compute a coverage value (using edge AA or a coverage stage) then
// we can't force blending off.
if (fCurrDrawState.fEdgeAANumEdges > 0) {
return false;
}
for (int s = fCurrDrawState.fFirstCoverageStage; s < kNumStages; ++s) {
if (this->isStageEnabled(s)) {
return false;
}
}
if ((kOne_BlendCoeff == fCurrDrawState.fSrcBlend) &&
(kZero_BlendCoeff == fCurrDrawState.fDstBlend)) {
return true;
}
// If we have vertex color without alpha then we can't force blend off
if ((fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit) ||
0xff != GrColorUnpackA(fCurrDrawState.fColor)) {
return false;
}
// If the src coef will always be 1...
if (kSA_BlendCoeff != fCurrDrawState.fSrcBlend &&
kOne_BlendCoeff != fCurrDrawState.fSrcBlend) {
return false;
}
// ...and the dst coef is always 0...
if (kISA_BlendCoeff != fCurrDrawState.fDstBlend &&
kZero_BlendCoeff != fCurrDrawState.fDstBlend) {
return false;
}
// ...and there isn't a texture stage with an alpha channel...
for (int s = 0; s < fCurrDrawState.fFirstCoverageStage; ++s) {
if (this->isStageEnabled(s)) {
GrAssert(NULL != fCurrDrawState.fTextures[s]);
GrPixelConfig config = fCurrDrawState.fTextures[s]->config();
if (!GrPixelConfigIsOpaque(config)) {
return false;
}
}
}
// ...and there isn't an interesting color filter...
// TODO: Consider being more aggressive with regards to disabling
// blending when a color filter is used.
if (SkXfermode::kDst_Mode != fCurrDrawState.fColorFilterXfermode) {
return false;
}
// ...then we disable blend.
return true;
}
///////////////////////////////////////////////////////////////////////////////
void GrDrawTarget::setEdgeAAData(const Edge* edges, int numEdges) {
GrAssert(numEdges <= kMaxEdges);
memcpy(fCurrDrawState.fEdgeAAEdges, edges, numEdges * sizeof(Edge));
fCurrDrawState.fEdgeAANumEdges = numEdges;
}
///////////////////////////////////////////////////////////////////////////////
void GrDrawTarget::drawRect(const GrRect& rect,
const GrMatrix* matrix,
StageBitfield stageEnableBitfield,
const GrRect* srcRects[],
const GrMatrix* srcMatrices[]) {
GrVertexLayout layout = GetRectVertexLayout(stageEnableBitfield, srcRects);
AutoReleaseGeometry geo(this, layout, 4, 0);
SetRectVertices(rect, matrix, srcRects,
srcMatrices, layout, geo.vertices());
drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
}
GrVertexLayout GrDrawTarget::GetRectVertexLayout(StageBitfield stageEnableBitfield,
const GrRect* srcRects[]) {
GrVertexLayout layout = 0;
for (int i = 0; i < kNumStages; ++i) {
int numTC = 0;
if (stageEnableBitfield & (1 << i)) {
if (NULL != srcRects && NULL != srcRects[i]) {
layout |= StageTexCoordVertexLayoutBit(i, numTC);
++numTC;
} else {
layout |= StagePosAsTexCoordVertexLayoutBit(i);
}
}
}
return layout;
}
void GrDrawTarget::SetRectVertices(const GrRect& rect,
const GrMatrix* matrix,
const GrRect* srcRects[],
const GrMatrix* srcMatrices[],
GrVertexLayout layout,
void* vertices) {
#if GR_DEBUG
// check that the layout and srcRects agree
for (int i = 0; i < 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[kNumStages];
int colorOffset;
int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets, &colorOffset);
GrAssert(-1 == colorOffset);
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 < 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);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
GrDrawTarget::AutoStateRestore::AutoStateRestore() {
fDrawTarget = NULL;
}
GrDrawTarget::AutoStateRestore::AutoStateRestore(GrDrawTarget* target) {
fDrawTarget = target;
if (NULL != fDrawTarget) {
fDrawTarget->saveCurrentDrawState(&fDrawState);
}
}
GrDrawTarget::AutoStateRestore::~AutoStateRestore() {
if (NULL != fDrawTarget) {
fDrawTarget->restoreDrawState(fDrawState);
}
}
void GrDrawTarget::AutoStateRestore::set(GrDrawTarget* target) {
if (target != fDrawTarget) {
if (NULL != fDrawTarget) {
fDrawTarget->restoreDrawState(fDrawState);
}
if (NULL != target) {
fDrawTarget->saveCurrentDrawState(&fDrawState);
}
fDrawTarget = target;
}
}