aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu
diff options
context:
space:
mode:
authorGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-03-29 19:22:36 +0000
committerGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-03-29 19:22:36 +0000
commit26e18b593ab65e4d92dfbce92579d8bc180d4c2c (patch)
treeddc5eddf771019decfe6a3401ea9b2da79dbc68a /src/gpu
parente36f3baa78cbf4a7bab6e5f14702d6a4d259abde (diff)
Add support for reading the dst pixel value in an effect. Use in a new effect for the kDarken xfer mode.
The current implementation is to always make a copy of the entire dst before the draw. It will only succeed if the RT is also a texture. Obviously, there is lots of room for improvement. Review URL: https://codereview.chromium.org/13314002 git-svn-id: http://skia.googlecode.com/svn/trunk@8449 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/gpu')
-rw-r--r--src/gpu/GrDrawTarget.cpp72
-rw-r--r--src/gpu/GrDrawTarget.h35
-rw-r--r--src/gpu/GrGpu.cpp9
-rw-r--r--src/gpu/GrGpu.h6
-rw-r--r--src/gpu/SkGpuDevice.cpp18
-rw-r--r--src/gpu/gl/GrGLProgram.cpp79
-rw-r--r--src/gpu/gl/GrGLProgram.h18
-rw-r--r--src/gpu/gl/GrGLProgramDesc.cpp14
-rw-r--r--src/gpu/gl/GrGLProgramDesc.h17
-rw-r--r--src/gpu/gl/GrGLShaderBuilder.cpp105
-rw-r--r--src/gpu/gl/GrGLShaderBuilder.h46
-rw-r--r--src/gpu/gl/GrGpuGL.h2
-rw-r--r--src/gpu/gl/GrGpuGL_program.cpp5
13 files changed, 351 insertions, 75 deletions
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 64576afb7d..4d4ba49dff 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -9,6 +9,7 @@
#include "GrDrawTarget.h"
+#include "GrContext.h"
#include "GrDrawTargetCaps.h"
#include "GrRenderTarget.h"
#include "GrTexture.h"
@@ -38,6 +39,9 @@ GrDrawTarget::DrawInfo& GrDrawTarget::DrawInfo::operator =(const DrawInfo& di) {
} else {
fDevBounds = NULL;
}
+
+ fDstCopy = di.fDstCopy;
+
return *this;
}
@@ -402,6 +406,62 @@ bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex,
return true;
}
+bool GrDrawTarget::setupDstReadIfNecessary(DrawInfo* info) {
+ bool willReadDst = false;
+ for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+ const GrEffectRef* effect = this->drawState()->getStage(s).getEffect();
+ if (NULL != effect && (*effect)->willReadDst()) {
+ willReadDst = true;
+ break;
+ }
+ }
+ if (!willReadDst) {
+ return true;
+ }
+ GrRenderTarget* rt = this->drawState()->getRenderTarget();
+ // If the dst is not a texture then we don't currently have a way of copying the
+ // texture. TODO: make copying RT->Tex (or Surface->Surface) a GrDrawTarget operation that can
+ // be built on top of GL/D3D APIs.
+ if (NULL == rt->asTexture()) {
+ GrPrintf("Reading Dst of non-texture render target is not currently supported.\n");
+ return false;
+ }
+ // TODO: Consider bounds of draw and bounds of clip
+
+ GrDrawTarget::AutoGeometryAndStatePush agasp(this, kReset_ASRInit);
+
+ // The draw will resolve dst if it has MSAA. Two things to consider in the future:
+ // 1) to make the dst values be pre-resolve we'd need to be able to copy to MSAA
+ // texture and sample it correctly in the shader. 2) If 1 isn't available then we
+ // should just resolve and use the resolved texture directly rather than making a
+ // copy of it.
+ GrTextureDesc desc;
+ desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+ desc.fWidth = rt->width();
+ desc.fHeight = rt->height();
+ desc.fSampleCnt = 0;
+ desc.fConfig = rt->config();
+
+ GrAutoScratchTexture ast(fContext, desc, GrContext::kApprox_ScratchTexMatch);
+
+ if (NULL == ast.texture()) {
+ GrPrintf("Failed to create temporary copy of destination texture.\n");
+ return false;
+ }
+ this->drawState()->disableState(GrDrawState::kClip_StateBit);
+ this->drawState()->setRenderTarget(ast.texture()->asRenderTarget());
+ static const int kTextureStage = 0;
+ SkMatrix matrix;
+ matrix.setIDiv(rt->width(), rt->height());
+ this->drawState()->createTextureEffect(kTextureStage, rt->asTexture(), matrix);
+ SkRect copyRect = SkRect::MakeWH(SkIntToScalar(desc.fWidth),
+ SkIntToScalar(desc.fHeight));
+ this->drawRect(copyRect, NULL, &copyRect, NULL);
+ info->fDstCopy.setTexture(ast.texture());
+ info->fDstCopy.setOffset(0, 0);
+ return true;
+}
+
void GrDrawTarget::drawIndexed(GrPrimitiveType type,
int startVertex,
int startIndex,
@@ -423,6 +483,10 @@ void GrDrawTarget::drawIndexed(GrPrimitiveType type,
if (NULL != devBounds) {
info.setDevBounds(*devBounds);
}
+ // TODO: We should continue with incorrect blending.
+ if (!this->setupDstReadIfNecessary(&info)) {
+ return;
+ }
this->onDraw(info);
}
}
@@ -446,6 +510,10 @@ void GrDrawTarget::drawNonIndexed(GrPrimitiveType type,
if (NULL != devBounds) {
info.setDevBounds(*devBounds);
}
+ // TODO: We should continue with incorrect blending.
+ if (!this->setupDstReadIfNecessary(&info)) {
+ return;
+ }
this->onDraw(info);
}
}
@@ -508,6 +576,10 @@ void GrDrawTarget::drawIndexedInstances(GrPrimitiveType type,
if (NULL != devBounds) {
info.setDevBounds(*devBounds);
}
+ // TODO: We should continue with incorrect blending.
+ if (!this->setupDstReadIfNecessary(&info)) {
+ return;
+ }
while (instanceCount) {
info.fInstanceCount = GrMin(instanceCount, maxInstancesPerDraw);
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index e54a04d537..737ce8637d 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -655,24 +655,35 @@ protected:
}
const SkRect* getDevBounds() const { return fDevBounds; }
+ // NULL if no copy of the dst is needed for the draw.
+ const GrDeviceCoordTexture* getDstCopy() const {
+ if (NULL != fDstCopy.texture()) {
+ return &fDstCopy;
+ } else {
+ return NULL;
+ }
+ }
+
private:
DrawInfo() { fDevBounds = NULL; }
friend class GrDrawTarget;
- GrPrimitiveType fPrimitiveType;
+ GrPrimitiveType fPrimitiveType;
- int fStartVertex;
- int fStartIndex;
- int fVertexCount;
- int fIndexCount;
+ int fStartVertex;
+ int fStartIndex;
+ int fVertexCount;
+ int fIndexCount;
- int fInstanceCount;
- int fVerticesPerInstance;
- int fIndicesPerInstance;
+ int fInstanceCount;
+ int fVerticesPerInstance;
+ int fIndicesPerInstance;
- SkRect fDevBoundsStorage;
- SkRect* fDevBounds;
+ SkRect fDevBoundsStorage;
+ SkRect* fDevBounds;
+
+ GrDeviceCoordTexture fDstCopy;
};
private:
@@ -714,6 +725,10 @@ private:
void releasePreviousVertexSource();
void releasePreviousIndexSource();
+ // Makes a copy of the dst if it is necessary for the draw. Returns false if a copy is required
+ // but couldn't be made. Otherwise, returns true.
+ bool setupDstReadIfNecessary(DrawInfo* info);
+
enum {
kPreallocGeoSrcStateStackCnt = 4,
};
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 73e4d3e441..7fc2ab8c04 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -330,13 +330,13 @@ const GrVertexBuffer* GrGpu::getUnitSquareVertexBuffer() const {
////////////////////////////////////////////////////////////////////////////////
-bool GrGpu::setupClipAndFlushState(DrawType type) {
+bool GrGpu::setupClipAndFlushState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
if (!fClipMaskManager.setupClipping(this->getClip())) {
return false;
}
- if (!this->flushGraphicsState(type)) {
+ if (!this->flushGraphicsState(type, dstCopy)) {
return false;
}
@@ -374,7 +374,8 @@ void GrGpu::geometrySourceWillPop(const GeometrySrcState& restoredState) {
void GrGpu::onDraw(const DrawInfo& info) {
this->handleDirtyContext();
- if (!this->setupClipAndFlushState(PrimTypeToDrawType(info.primitiveType()))) {
+ if (!this->setupClipAndFlushState(PrimTypeToDrawType(info.primitiveType()),
+ info.getDstCopy())) {
return;
}
this->onGpuDraw(info);
@@ -387,7 +388,7 @@ void GrGpu::onStencilPath(const GrPath* path, const SkStrokeRec&, SkPath::FillTy
GrAutoTRestore<GrStencilSettings> asr(this->drawState()->stencil());
this->setStencilPathSettings(*path, fill, this->drawState()->stencil());
- if (!this->setupClipAndFlushState(kStencilPath_DrawType)) {
+ if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL)) {
return;
}
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 602197b582..d4e4e6b2fb 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -366,7 +366,7 @@ protected:
}
// prepares clip flushes gpu state before a draw
- bool setupClipAndFlushState(DrawType);
+ bool setupClipAndFlushState(DrawType, const GrDeviceCoordTexture* dstCopy);
// Functions used to map clip-respecting stencil tests into normal
// stencil funcs supported by GPUs.
@@ -482,9 +482,9 @@ private:
// The GrGpu typically records the clients requested state and then flushes
// deltas from previous state at draw time. This function does the
- // backend-specific flush of the state
+ // backend-specific flush of the state.
// returns false if current state is unsupported.
- virtual bool flushGraphicsState(DrawType) = 0;
+ virtual bool flushGraphicsState(DrawType, const GrDeviceCoordTexture* dstCopy) = 0;
// clears the entire stencil buffer to 0
virtual void clearStencil() = 0;
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 49d6562cea..45176cf8c9 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -493,17 +493,27 @@ inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,
grPaint->setDither(skPaint.isDither());
grPaint->setAntiAlias(skPaint.isAntiAlias());
- SkXfermode::Coeff sm = SkXfermode::kOne_Coeff;
- SkXfermode::Coeff dm = SkXfermode::kISA_Coeff;
+ SkXfermode::Coeff sm;
+ SkXfermode::Coeff dm;
SkXfermode* mode = skPaint.getXfermode();
GrEffectRef* xferEffect = NULL;
- if (SkXfermode::AsNewEffect(mode, dev->context(), &xferEffect, &sm, &dm)) {
- SkSafeUnref(grPaint->colorStage(kXfermodeEffectIdx)->setEffect(xferEffect));
+ if (SkXfermode::AsNewEffectOrCoeff(mode, dev->context(), &xferEffect, &sm, &dm)) {
+ if (NULL != xferEffect) {
+ grPaint->colorStage(kXfermodeEffectIdx)->setEffect(xferEffect)->unref();
+ // This may not be the right place to have this logic but we set the GPU blend to
+ // src-over so that fractional coverage will be accounted for correctly.
+ sm = SkXfermode::kOne_Coeff;
+ dm = SkXfermode::kISA_Coeff;
+ }
} else {
//SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
#if 0
return false;
+#else
+ // Fall back to src-over
+ sm = SkXfermode::kOne_Coeff;
+ dm = SkXfermode::kISA_Coeff;
#endif
}
grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 52eb970842..ed3a87c1b7 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -389,7 +389,9 @@ bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) {
return false;
}
- if (builder.fUsesGS) {
+ fGShaderID = 0;
+#if GR_GL_EXPERIMENTAL_GS
+ if (fDesc.fExperimentalGS) {
builder.getShader(GrGLShaderBuilder::kGeometry_ShaderType, &shader);
if (c_PrintShaders) {
GrPrintf(shader.c_str());
@@ -398,9 +400,8 @@ bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) {
if (!(fGShaderID = compile_shader(fContext, GR_GL_GEOMETRY_SHADER, shader))) {
return false;
}
- } else {
- fGShaderID = 0;
}
+#endif
builder.getShader(GrGLShaderBuilder::kFragment_ShaderType, &shader);
if (c_PrintShaders) {
@@ -417,14 +418,7 @@ bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) {
bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
GrAssert(0 == fProgramID);
- const GrAttribBindings& attribBindings = fDesc.fAttribBindings;
- bool hasExplicitLocalCoords =
- SkToBool(attribBindings & GrDrawState::kLocalCoords_AttribBindingsBit);
- GrGLShaderBuilder builder(fContext.info(), fUniformManager, hasExplicitLocalCoords);
-
-#if GR_GL_EXPERIMENTAL_GS
- builder.fUsesGS = fDesc.fExperimentalGS;
-#endif
+ GrGLShaderBuilder builder(fContext.info(), fUniformManager, fDesc);
SkXfermode::Coeff colorCoeff, uniformCoeff;
// The rest of transfer mode color filters have not been implemented
@@ -496,7 +490,11 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
}
// we output point size in the GS if present
- if (fDesc.fEmitsPointSize && !builder.fUsesGS){
+ if (fDesc.fEmitsPointSize
+#if GR_GL_EXPERIMENTAL_GS
+ && !fDesc.fExperimentalGS
+#endif
+ ) {
builder.vsCodeAppend("\tgl_PointSize = 1.0;\n");
}
@@ -519,7 +517,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
fDesc.fEffectKeys[s],
inColor.size() ? inColor.c_str() : NULL,
outColor.c_str(),
- &fUniformHandles.fSamplerUnis[s]);
+ &fUniformHandles.fEffectSamplerUnis[s]);
builder.setNonStage();
inColor = outColor;
}
@@ -598,7 +596,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
fDesc.fEffectKeys[s],
inCoverage.size() ? inCoverage.c_str() : NULL,
outCoverage.c_str(),
- &fUniformHandles.fSamplerUnis[s]);
+ &fUniformHandles.fEffectSamplerUnis[s]);
builder.setNonStage();
inCoverage = outCoverage;
}
@@ -674,8 +672,12 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
}
builder.finished(fProgramID);
- this->initSamplerUniforms();
fUniformHandles.fRTHeightUni = builder.getRTHeightUniform();
+ fUniformHandles.fDstCopyTopLeftUni = builder.getDstCopyTopLeftUniform();
+ fUniformHandles.fDstCopyScaleUni = builder.getDstCopyScaleUniform();
+ fUniformHandles.fDstCopySamplerUni = builder.getDstCopySamplerUniform();
+ // This must be called after we set fDstCopySamplerUni above.
+ this->initSamplerUniforms();
return true;
}
@@ -749,13 +751,18 @@ bool GrGLProgram::bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& buil
void GrGLProgram::initSamplerUniforms() {
GL_CALL(UseProgram(fProgramID));
- // We simply bind the uniforms to successive texture units beginning at 0. setData() assumes this
- // behavior.
+ // We simply bind the uniforms to successive texture units beginning at 0. setData() assumes
+ // this behavior.
GrGLint texUnitIdx = 0;
+ if (GrGLUniformManager::kInvalidUniformHandle != fUniformHandles.fDstCopySamplerUni) {
+ fUniformManager.setSampler(fUniformHandles.fDstCopySamplerUni, texUnitIdx);
+ ++texUnitIdx;
+ }
+
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
- int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+ int numSamplers = fUniformHandles.fEffectSamplerUnis[s].count();
for (int u = 0; u < numSamplers; ++u) {
- UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+ UniformHandle handle = fUniformHandles.fEffectSamplerUnis[s][u];
if (GrGLUniformManager::kInvalidUniformHandle != handle) {
fUniformManager.setSampler(handle, texUnitIdx);
++texUnitIdx;
@@ -769,6 +776,7 @@ void GrGLProgram::initSamplerUniforms() {
void GrGLProgram::setData(GrGpuGL* gpu,
GrColor color,
GrColor coverage,
+ const GrDeviceCoordTexture* dstCopy,
SharedGLState* sharedState) {
const GrDrawState& drawState = gpu->getDrawState();
@@ -786,6 +794,35 @@ void GrGLProgram::setData(GrGpuGL* gpu,
}
GrGLint texUnitIdx = 0;
+ if (NULL != dstCopy) {
+ if (GrGLUniformManager::kInvalidUniformHandle != fUniformHandles.fDstCopyTopLeftUni) {
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle != fUniformHandles.fDstCopyScaleUni);
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle !=
+ fUniformHandles.fDstCopySamplerUni);
+ fUniformManager.set2f(fUniformHandles.fDstCopyTopLeftUni,
+ static_cast<GrGLfloat>(dstCopy->offset().fX),
+ static_cast<GrGLfloat>(dstCopy->offset().fY));
+ fUniformManager.set2f(fUniformHandles.fDstCopyScaleUni,
+ 1.f / dstCopy->texture()->width(),
+ 1.f / dstCopy->texture()->height());
+ GrGLTexture* texture = static_cast<GrGLTexture*>(dstCopy->texture());
+ static GrTextureParams kParams; // the default is clamp, nearest filtering.
+ gpu->bindTexture(texUnitIdx, kParams, texture);
+ ++texUnitIdx;
+ } else {
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopyScaleUni);
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopySamplerUni);
+ }
+ } else {
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopyTopLeftUni);
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopyScaleUni);
+ GrAssert(GrGLUniformManager::kInvalidUniformHandle ==
+ fUniformHandles.fDstCopySamplerUni);
+ }
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
if (NULL != fEffects[s]) {
const GrEffectStage& stage = drawState.getStage(s);
@@ -795,9 +832,9 @@ void GrGLProgram::setData(GrGpuGL* gpu,
(fDesc.fAttribBindings & GrDrawState::kLocalCoords_AttribBindingsBit);
GrDrawEffect drawEffect(stage, explicitLocalCoords);
fEffects[s]->setData(fUniformManager, drawEffect);
- int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+ int numSamplers = fUniformHandles.fEffectSamplerUnis[s].count();
for (int u = 0; u < numSamplers; ++u) {
- UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+ UniformHandle handle = fUniformHandles.fEffectSamplerUnis[s][u];
if (GrGLUniformManager::kInvalidUniformHandle != handle) {
const GrTextureAccess& access = (*stage.getEffect())->textureAccess(u);
GrGLTexture* texture = static_cast<GrGLTexture*>(access.getTexture());
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index f5fd06edf8..27b6f802f5 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -102,7 +102,11 @@ public:
*
* The color and coverage params override the GrDrawState's getColor() and getCoverage() values.
*/
- void setData(GrGpuGL*, GrColor color, GrColor coverage, SharedGLState*);
+ void setData(GrGpuGL*,
+ GrColor color,
+ GrColor coverage,
+ const GrDeviceCoordTexture* dstCopy, // can be NULL
+ SharedGLState*);
private:
GrGLProgram(const GrGLContext& gl,
@@ -154,11 +158,18 @@ private:
UniformHandle fColorUni;
UniformHandle fCoverageUni;
UniformHandle fColorFilterUni;
+
// We use the render target height to provide a y-down frag coord when specifying
// origin_upper_left is not supported.
UniformHandle fRTHeightUni;
+
+ // Uniforms for computing texture coords to do the dst-copy lookup
+ UniformHandle fDstCopyTopLeftUni;
+ UniformHandle fDstCopyScaleUni;
+ UniformHandle fDstCopySamplerUni;
+
// An array of sampler uniform handles for each effect.
- SamplerUniSArray fSamplerUnis[GrDrawState::kNumStages];
+ SamplerUniSArray fEffectSamplerUnis[GrDrawState::kNumStages];
UniformHandles() {
fViewMatrixUni = GrGLUniformManager::kInvalidUniformHandle;
@@ -166,6 +177,9 @@ private:
fCoverageUni = GrGLUniformManager::kInvalidUniformHandle;
fColorFilterUni = GrGLUniformManager::kInvalidUniformHandle;
fRTHeightUni = GrGLUniformManager::kInvalidUniformHandle;
+ fDstCopyTopLeftUni = GrGLUniformManager::kInvalidUniformHandle;
+ fDstCopyScaleUni = GrGLUniformManager::kInvalidUniformHandle;
+ fDstCopySamplerUni = GrGLUniformManager::kInvalidUniformHandle;
}
};
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 98280e297c..6ed5473a39 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -9,6 +9,7 @@
#include "GrBackendEffectFactory.h"
#include "GrDrawEffect.h"
#include "GrEffect.h"
+#include "GrGLShaderBuilder.h"
#include "GrGpuGL.h"
void GrGLProgramDesc::Build(const GrDrawState& drawState,
@@ -17,6 +18,7 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState,
GrBlendCoeff srcCoeff,
GrBlendCoeff dstCoeff,
const GrGpuGL* gpu,
+ const GrDeviceCoordTexture* dstCopy,
GrGLProgramDesc* desc) {
// This should already have been caught
@@ -80,6 +82,7 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState,
desc->fCoverageInput = kAttribute_ColorInput;
}
+ bool readsDst = false;
int lastEnabledStage = -1;
for (int s = 0; s < GrDrawState::kNumStages; ++s) {
@@ -93,11 +96,22 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState,
GrDrawState::kLocalCoords_AttribBindingsBit);
GrDrawEffect drawEffect(drawState.getStage(s), explicitLocalCoords);
desc->fEffectKeys[s] = factory.glEffectKey(drawEffect, gpu->glCaps());
+ if (effect->willReadDst()) {
+ readsDst = true;
+ }
} else {
desc->fEffectKeys[s] = 0;
}
}
+ if (readsDst) {
+ GrAssert(NULL != dstCopy);
+ desc->fDstRead = GrGLShaderBuilder::KeyForDstRead(dstCopy->texture(), gpu->glCaps());
+ GrAssert(0 != desc->fDstRead);
+ } else {
+ desc->fDstRead = 0;
+ }
+
desc->fDualSrcOutput = kNone_DualSrcOutput;
// Currently the experimental GS will only work with triangle prims (and it doesn't do anything
diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h
index 135d46c74b..b1660d8585 100644
--- a/src/gpu/gl/GrGLProgramDesc.h
+++ b/src/gpu/gl/GrGLProgramDesc.h
@@ -10,6 +10,7 @@
#include "GrGLEffect.h"
#include "GrDrawState.h"
+#include "GrGLShaderBuilder.h"
class GrGpuGL;
@@ -18,9 +19,10 @@ class GrGpuGL;
#define GR_GL_EXPERIMENTAL_GS GR_DEBUG
-/** This class describes a program to generate. It also serves as a program cache key. The only
- thing GL-specific about this is the generation of GrGLEffect::EffectKeys. With some refactoring
- it could be made backend-neutral. */
+/** This class describes a program to generate. It also serves as a program cache key. Very little
+ of this is GL-specific. There is the generation of GrGLEffect::EffectKeys and the dst-read part
+ of the key set by GrGLShaderBuilder. If the interfaces that set those portions were abstracted
+ to be API-neutral then so could this class. */
class GrGLProgramDesc {
public:
GrGLProgramDesc() {
@@ -48,6 +50,7 @@ public:
GrBlendCoeff srcCoeff,
GrBlendCoeff dstCoeff,
const GrGpuGL* gpu,
+ const GrDeviceCoordTexture* dstCopy,
GrGLProgramDesc* outDesc);
private:
@@ -86,6 +89,10 @@ private:
#if GR_GL_EXPERIMENTAL_GS
bool fExperimentalGS;
#endif
+ GrGLShaderBuilder::DstReadKey fDstRead; // set by GrGLShaderBuilder if there
+ // are effects that must read the dst.
+ // Otherwise, 0.
+
uint8_t fColorInput; // casts to enum ColorInput
uint8_t fCoverageInput; // casts to enum ColorInput
uint8_t fDualSrcOutput; // casts to enum DualSrcOutput
@@ -98,8 +105,10 @@ private:
int8_t fCoverageAttributeIndex;
int8_t fLocalCoordsAttributeIndex;
- // GrGLProgram reads the private fields to generate code.
+ // GrGLProgram and GrGLShaderBuilder read the private fields to generate code. TODO: Move all
+ // code generation to GrGLShaderBuilder (and maybe add getters rather than friending).
friend class GrGLProgram;
+ friend class GrGLShaderBuilder;
};
#endif
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
index a5c96c7dd6..e777b318b3 100644
--- a/src/gpu/gl/GrGLShaderBuilder.cpp
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -35,15 +35,26 @@ inline const char* sample_function_name(GrSLType type, GrGLSLGeneration glslGen)
}
/**
- * Do we need to either map r,g,b->a or a->r.
+ * Do we need to either map r,g,b->a or a->r. configComponentMask indicates which channels are
+ * present in the texture's config. swizzleComponentMask indicates the channels present in the
+ * shader swizzle.
*/
inline bool swizzle_requires_alpha_remapping(const GrGLCaps& caps,
- const GrTextureAccess& access) {
- if (GrPixelConfigIsAlphaOnly(access.getTexture()->config())) {
- if (caps.textureRedSupport() && (kA_GrColorComponentFlag & access.swizzleMask())) {
+ uint32_t configComponentMask,
+ uint32_t swizzleComponentMask) {
+ if (caps.textureSwizzleSupport()) {
+ // Any remapping is handled using texture swizzling not shader modifications.
+ return false;
+ }
+ // check if the texture is alpha-only
+ if (kA_GrColorComponentFlag == configComponentMask) {
+ if (caps.textureRedSupport() && (kA_GrColorComponentFlag & swizzleComponentMask)) {
+ // we must map the swizzle 'a's to 'r'.
return true;
}
- if (kRGB_GrColorComponentFlags & access.swizzleMask()) {
+ if (kRGB_GrColorComponentFlags & swizzleComponentMask) {
+ // The 'r', 'g', and/or 'b's must be mapped to 'a' according to our semantics that
+ // alpha-only textures smear alpha across all four channels when read.
return true;
}
}
@@ -76,16 +87,13 @@ void append_swizzle(SkString* outAppend,
}
-///////////////////////////////////////////////////////////////////////////////
+static const char kDstColorName[] = "_dstColor";
-// Architectural assumption: always 2-d input coords.
-// Likely to become non-constant and non-static, perhaps even
-// varying by stage, if we use 1D textures for gradients!
-//const int GrGLShaderBuilder::fCoordDims = 2;
+///////////////////////////////////////////////////////////////////////////////
GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo,
GrGLUniformManager& uniformManager,
- bool explicitLocalCoords)
+ const GrGLProgramDesc& desc)
: fUniforms(kVarsPerBlock)
, fVSAttrs(kVarsPerBlock)
, fVSOutputs(kVarsPerBlock)
@@ -93,16 +101,22 @@ GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo,
, fGSOutputs(kVarsPerBlock)
, fFSInputs(kVarsPerBlock)
, fFSOutputs(kMaxFSOutputs)
- , fUsesGS(false)
, fCtxInfo(ctxInfo)
, fUniformManager(uniformManager)
, fCurrentStageIdx(kNonStageIdx)
+#if GR_GL_EXPERIMENTAL_GS
+ , fUsesGS(desc.fExperimentalGS)
+#else
+ , fUsesGS(false)
+#endif
, fSetupFragPosition(false)
- , fRTHeightUniform(GrGLUniformManager::kInvalidUniformHandle) {
+ , fRTHeightUniform(GrGLUniformManager::kInvalidUniformHandle)
+ , fDstCopyTopLeftUniform (GrGLUniformManager::kInvalidUniformHandle)
+ , fDstCopyScaleUniform (GrGLUniformManager::kInvalidUniformHandle) {
fPositionVar = &fVSAttrs.push_back();
fPositionVar->set(kVec2f_GrSLType, GrGLShaderVar::kAttribute_TypeModifier, "aPosition");
- if (explicitLocalCoords) {
+ if (desc.fAttribBindings & GrDrawState::kLocalCoords_AttribBindingsBit) {
fLocalCoordsVar = &fVSAttrs.push_back();
fLocalCoordsVar->set(kVec2f_GrSLType,
GrGLShaderVar::kAttribute_TypeModifier,
@@ -110,6 +124,45 @@ GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo,
} else {
fLocalCoordsVar = fPositionVar;
}
+ if (kNoDstRead_DstReadKey != desc.fDstRead) {
+ bool topDown = SkToBool(kTopLeftOrigin_DstReadKeyBit & desc.fDstRead);
+ const char* dstCopyTopLeftName;
+ const char* dstCopyCoordScaleName;
+ uint32_t configMask;
+ if (SkToBool(kUseAlphaConfig_DstReadKeyBit & desc.fDstRead)) {
+ configMask = kA_GrColorComponentFlag;
+ } else {
+ configMask = kRGBA_GrColorComponentFlags;
+ }
+ fDstCopySampler.init(this, configMask, "rgba", 0);
+
+ fDstCopyTopLeftUniform = this->addUniform(kFragment_ShaderType,
+ kVec2f_GrSLType,
+ "DstCopyUpperLeft",
+ &dstCopyTopLeftName);
+ fDstCopyScaleUniform = this->addUniform(kFragment_ShaderType,
+ kVec2f_GrSLType,
+ "DstCopyCoordScale",
+ &dstCopyCoordScaleName);
+ const char* fragPos = this->fragmentPosition();
+ this->fsCodeAppend("\t// Read color from copy of the destination.\n");
+ this->fsCodeAppendf("\tvec2 _dstTexCoord = (%s.xy - %s) * %s;\n",
+ fragPos, dstCopyTopLeftName, dstCopyCoordScaleName);
+ if (!topDown) {
+ this->fsCodeAppend("\t_dstTexCoord.y = 1.0 - _dstTexCoord.y;\n");
+ }
+ this->fsCodeAppendf("\tvec4 %s = ", kDstColorName);
+ this->appendTextureLookup(kFragment_ShaderType, fDstCopySampler, "_dstTexCoord");
+ this->fsCodeAppend(";\n\n");
+ }
+}
+
+const char* GrGLShaderBuilder::dstColor() const {
+ if (fDstCopySampler.isInitialized()) {
+ return kDstColorName;
+ } else {
+ return NULL;
+ }
}
void GrGLShaderBuilder::codeAppendf(ShaderType type, const char format[], va_list args) {
@@ -184,14 +237,26 @@ void GrGLShaderBuilder::appendTextureLookupAndModulate(
GrBackendEffectFactory::EffectKey GrGLShaderBuilder::KeyForTextureAccess(
const GrTextureAccess& access,
const GrGLCaps& caps) {
- GrBackendEffectFactory::EffectKey key = 0;
+ uint32_t configComponentMask = GrPixelConfigComponentMask(access.getTexture()->config());
+ if (swizzle_requires_alpha_remapping(caps, configComponentMask, access.swizzleMask())) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
- // Assume that swizzle support implies that we never have to modify a shader to adjust
- // for texture format/swizzle settings.
- if (!caps.textureSwizzleSupport() && swizzle_requires_alpha_remapping(caps, access)) {
- key = 1;
+GrGLShaderBuilder::DstReadKey GrGLShaderBuilder::KeyForDstRead(const GrTexture* dstCopy,
+ const GrGLCaps& caps) {
+ uint32_t key = kYesDstRead_DstReadKeyBit;
+ if (!caps.textureSwizzleSupport() && GrPixelConfigIsAlphaOnly(dstCopy->config())) {
+ // The fact that the config is alpha-only must be considered when generating code.
+ key |= kUseAlphaConfig_DstReadKeyBit;
+ }
+ if (kTopLeft_GrSurfaceOrigin == dstCopy->origin()) {
+ key |= kTopLeftOrigin_DstReadKeyBit;
}
- return key;
+ GrAssert(static_cast<DstReadKey>(key) == key);
+ return static_cast<DstReadKey>(key);
}
const GrGLenum* GrGLShaderBuilder::GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps) {
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
index d947771b3e..4e39f91e35 100644
--- a/src/gpu/gl/GrGLShaderBuilder.h
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -19,6 +19,7 @@
class GrGLContextInfo;
class GrEffectStage;
+class GrGLProgramDesc;
/**
Contains all the incremental state of a shader as it is being built,as well as helpers to
@@ -55,6 +56,8 @@ public:
const char* swizzle() const { return fSwizzle; }
+ bool isInitialized() const { return 0 != fConfigComponentMask; }
+
private:
// The idx param is used to ensure multiple samplers within a single effect have unique
// uniform names. swizzle is a four char max string made up of chars 'r', 'g', 'b', and 'a'.
@@ -62,7 +65,8 @@ public:
uint32_t configComponentMask,
const char* swizzle,
int idx) {
- GrAssert(0 == fConfigComponentMask);
+ GrAssert(!this->isInitialized());
+ GrAssert(0 != configComponentMask);
GrAssert(GrGLUniformManager::kInvalidUniformHandle == fSamplerUniform);
GrAssert(NULL != builder);
@@ -100,7 +104,7 @@ public:
kFragment_ShaderType = 0x4,
};
- GrGLShaderBuilder(const GrGLContextInfo&, GrGLUniformManager&, bool explicitLocalCoords);
+ GrGLShaderBuilder(const GrGLContextInfo&, GrGLUniformManager&, const GrGLProgramDesc&);
/**
* Called by GrGLEffects to add code to one of the shaders.
@@ -172,6 +176,13 @@ public:
static GrBackendEffectFactory::EffectKey KeyForTextureAccess(const GrTextureAccess&,
const GrGLCaps&);
+ typedef uint8_t DstReadKey;
+
+ /** Returns a key for adding code to read the copy-of-dst color in service of effects that
+ require reading the dst. It must not return 0 because 0 indicates that there is no dst
+ copy read at all. */
+ static DstReadKey KeyForDstRead(const GrTexture* dstCopy, const GrGLCaps&);
+
/** If texture swizzling is available using tex parameters then it is preferred over mangling
the generated shader code. This potentially allows greater reuse of cached shaders. */
static const GrGLenum* GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps);
@@ -230,6 +241,10 @@ public:
specified explicit local coords or not in the GrDrawState. */
const GrGLShaderVar& localCoordsAttribute() const { return *fLocalCoordsVar; }
+ /** Returns the color of the destination pixel. This may be NULL if no effect advertised
+ that it will read the destination. */
+ const char* dstColor() const;
+
/**
* Are explicit local coordinates provided as input to the vertex shader.
*/
@@ -254,7 +269,17 @@ public:
const char* fsInColor, // NULL means no incoming color
const char* fsOutColor,
SkTArray<GrGLUniformManager::UniformHandle, true>* samplerHandles);
+
GrGLUniformManager::UniformHandle getRTHeightUniform() const { return fRTHeightUniform; }
+ GrGLUniformManager::UniformHandle getDstCopyTopLeftUniform() const {
+ return fDstCopyTopLeftUniform;
+ }
+ GrGLUniformManager::UniformHandle getDstCopyScaleUniform() const {
+ return fDstCopyScaleUniform;
+ }
+ GrGLUniformManager::UniformHandle getDstCopySamplerUniform() const {
+ return fDstCopySampler.fSamplerUniform;
+ }
struct AttributePair {
void set(int index, const SkString& name) {
@@ -263,7 +288,7 @@ public:
int fIndex;
SkString fName;
};
- const SkSTArray<10, AttributePair, true>& getEffectAttributes() const {
+ const SkTArray<AttributePair, true>& getEffectAttributes() const {
return fEffectAttributes;
}
const SkString* getEffectAttributeName(int attributeIndex) const;
@@ -296,25 +321,38 @@ public:
VarArray fFSInputs;
SkString fGSHeader; // layout qualifiers specific to GS
VarArray fFSOutputs;
- bool fUsesGS;
private:
enum {
kNonStageIdx = -1,
};
+ // Interpretation of DstReadKey when generating code
+ enum {
+ kNoDstRead_DstReadKey = 0,
+ kYesDstRead_DstReadKeyBit = 0x1, // Set if we do a dst-copy-read.
+ kUseAlphaConfig_DstReadKeyBit = 0x2, // Set if dst-copy config is alpha only.
+ kTopLeftOrigin_DstReadKeyBit = 0x4, // Set if dst-copy origin is top-left.
+ };
+
const GrGLContextInfo& fCtxInfo;
GrGLUniformManager& fUniformManager;
int fCurrentStageIdx;
SkString fFSFunctions;
SkString fFSHeader;
+ bool fUsesGS;
+
SkString fFSCode;
SkString fVSCode;
SkString fGSCode;
bool fSetupFragPosition;
+ TextureSampler fDstCopySampler;
+
GrGLUniformManager::UniformHandle fRTHeightUniform;
+ GrGLUniformManager::UniformHandle fDstCopyTopLeftUniform;
+ GrGLUniformManager::UniformHandle fDstCopyScaleUniform;
SkSTArray<10, AttributePair, true> fEffectAttributes;
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index c95b40ad46..a71ff39e52 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -135,7 +135,7 @@ private:
virtual void clearStencil() SK_OVERRIDE;
virtual void clearStencilClip(const GrIRect& rect,
bool insideClip) SK_OVERRIDE;
- virtual bool flushGraphicsState(DrawType) SK_OVERRIDE;
+ virtual bool flushGraphicsState(DrawType, const GrDeviceCoordTexture* dstCopy) SK_OVERRIDE;
// binds texture unit in GL
void setTextureUnit(int unitIdx);
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
index 8f527f878d..833d811588 100644
--- a/src/gpu/gl/GrGpuGL_program.cpp
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -158,7 +158,7 @@ void GrGpuGL::flushPathStencilMatrix() {
}
}
-bool GrGpuGL::flushGraphicsState(DrawType type) {
+bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
const GrDrawState& drawState = this->getDrawState();
// GrGpu::setupClipAndFlushState should have already checked this and bailed if not true.
@@ -187,6 +187,7 @@ bool GrGpuGL::flushGraphicsState(DrawType type) {
srcCoeff,
dstCoeff,
this,
+ dstCopy,
&desc);
fCurrentProgram.reset(fProgramCache->getProgram(desc, stages));
@@ -217,7 +218,7 @@ bool GrGpuGL::flushGraphicsState(DrawType type) {
color = drawState.getColor();
coverage = drawState.getCoverage();
}
- fCurrentProgram->setData(this, color, coverage, &fSharedGLProgramState);
+ fCurrentProgram->setData(this, color, coverage, dstCopy, &fSharedGLProgramState);
}
this->flushStencil(type);
this->flushScissor();