From 17168df7798d0d12684f18df0556dc19e65b32e6 Mon Sep 17 00:00:00 2001 From: bsalomon Date: Tue, 9 Dec 2014 09:00:49 -0800 Subject: Use texture size to determine precision of texture coord varyings. Review URL: https://codereview.chromium.org/778783002 --- src/gpu/GrCoordTransform.cpp | 61 +++++++++++++++++++++++++ src/gpu/GrDrawTarget.cpp | 49 ++++++++++++++++++++ src/gpu/GrDrawTargetCaps.h | 58 ++++++++++++++++++++++++ src/gpu/gl/GrGLCaps.cpp | 72 ++++++++++++++++++++++++++++++ src/gpu/gl/GrGLCaps.h | 7 +-- src/gpu/gl/GrGLProgramDesc.cpp | 53 +++++++++++++--------- src/gpu/gl/GrGLUtil.h | 9 ++++ src/gpu/gl/builders/GrGLProgramBuilder.cpp | 3 +- src/gpu/gl/builders/GrGLProgramBuilder.h | 12 ++--- 9 files changed, 290 insertions(+), 34 deletions(-) create mode 100644 src/gpu/GrCoordTransform.cpp (limited to 'src/gpu') diff --git a/src/gpu/GrCoordTransform.cpp b/src/gpu/GrCoordTransform.cpp new file mode 100644 index 0000000000..53285a27eb --- /dev/null +++ b/src/gpu/GrCoordTransform.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrCoordTransform.h" +#include "GrContext.h" +#include "GrDrawTargetCaps.h" +#include "GrGpu.h" + +void GrCoordTransform::reset(GrCoordSet sourceCoords, const SkMatrix& m, const GrTexture* texture) { + SkASSERT(texture); + SkASSERT(!fInProcessor); + + fSourceCoords = sourceCoords; + fMatrix = m; + fReverseY = kBottomLeft_GrSurfaceOrigin == texture->origin(); + + // Always start at kDefault. Then if precisions differ we see if the precision needs to be + // increased. Our rule is that we want at least 4 subpixel values in the representation for + // coords between 0 to 1. Note that this still might not be enough when drawing with repeat + // or mirror-repeat modes but that case can be arbitrarily bad. + fPrecision = GrShaderVar::kDefault_Precision; + if (texture->getContext()) { + const GrDrawTargetCaps* caps = texture->getContext()->getGpu()->caps(); + if (caps->floatPrecisionVaries()) { + int maxD = SkTMax(texture->width(), texture->height()); + const GrDrawTargetCaps::PrecisionInfo* info; + info = &caps->getFloatShaderPrecisionInfo(kFragment_GrShaderType, fPrecision); + do { + SkASSERT(info->supported()); + // Make sure there is at least 2 bits of subpixel precision in the range of + // texture coords from 0.5 to 1.0. + if ((2 << info->fBits) / maxD > 4) { + break; + } + if (GrShaderVar::kHigh_Precision == fPrecision) { + break; + } + GrShaderVar::Precision nextP = static_cast(fPrecision + 1); + info = &caps->getFloatShaderPrecisionInfo(kFragment_GrShaderType, nextP); + if (!info->supported()) { + break; + } + fPrecision = nextP; + } while (true); + } + } +} + +void GrCoordTransform::reset(GrCoordSet sourceCoords, + const SkMatrix& m, + GrShaderVar::Precision precision) { + SkASSERT(!fInProcessor); + fSourceCoords = sourceCoords; + fMatrix = m; + fReverseY = false; + fPrecision = precision; +} diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index 65764b88ce..ed54bf9514 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -1011,6 +1011,8 @@ void GrDrawTargetCaps::reset() { fMaxTextureSize = 0; fMaxSampleCount = 0; + fShaderPrecisionVaries = false; + memset(fConfigRenderSupport, 0, sizeof(fConfigRenderSupport)); memset(fConfigTextureSupport, 0, sizeof(fConfigTextureSupport)); } @@ -1042,6 +1044,12 @@ GrDrawTargetCaps& GrDrawTargetCaps::operator=(const GrDrawTargetCaps& other) { memcpy(fConfigRenderSupport, other.fConfigRenderSupport, sizeof(fConfigRenderSupport)); memcpy(fConfigTextureSupport, other.fConfigTextureSupport, sizeof(fConfigTextureSupport)); + fShaderPrecisionVaries = other.fShaderPrecisionVaries; + for (int s = 0; s < kGrShaderTypeCount; ++s) { + for (int p = 0; p < GrShaderVar::kPrecisionCount; ++p) { + fFloatPrecisions[s][p] = other.fFloatPrecisions[s][p]; + } + } return *this; } @@ -1065,6 +1073,30 @@ static SkString map_flags_to_string(uint32_t flags) { return str; } +static const char* shader_type_to_string(GrShaderType type) { + switch (type) { + case kVertex_GrShaderType: + return "vertex"; + case kGeometry_GrShaderType: + return "geometry"; + case kFragment_GrShaderType: + return "fragment"; + } + return ""; +} + +static const char* precision_to_string(GrShaderVar::Precision p) { + switch (p) { + case GrShaderVar::kLow_Precision: + return "low"; + case GrShaderVar::kMedium_Precision: + return "medium"; + case GrShaderVar::kHigh_Precision: + return "high"; + } + return ""; +} + SkString GrDrawTargetCaps::dump() const { SkString r; static const char* gNY[] = {"NO", "YES"}; @@ -1140,6 +1172,23 @@ SkString GrDrawTargetCaps::dump() const { gNY[fConfigTextureSupport[i]]); } + r.appendf("Shader Float Precisions (varies: %s):\n", gNY[fShaderPrecisionVaries]); + + for (int s = 0; s < kGrShaderTypeCount; ++s) { + GrShaderType shaderType = static_cast(s); + r.appendf("\t%s:\n", shader_type_to_string(shaderType)); + for (int p = 0; p < GrShaderVar::kPrecisionCount; ++p) { + if (fFloatPrecisions[s][p].supported()) { + GrShaderVar::Precision precision = static_cast(p); + r.appendf("\t\t%s: log_low: %d log_high: %d bits: %d\n", + precision_to_string(precision), + fFloatPrecisions[s][p].fLogRangeLow, + fFloatPrecisions[s][p].fLogRangeHigh, + fFloatPrecisions[s][p].fBits); + } + } + } + return r; } diff --git a/src/gpu/GrDrawTargetCaps.h b/src/gpu/GrDrawTargetCaps.h index f13aa53c80..ae6a9ed749 100644 --- a/src/gpu/GrDrawTargetCaps.h +++ b/src/gpu/GrDrawTargetCaps.h @@ -9,6 +9,8 @@ #define GrDrawTargetCaps_DEFINED #include "GrTypes.h" +#include "GrTypesPriv.h" +#include "GrShaderVar.h" #include "SkRefCnt.h" #include "SkString.h" @@ -19,6 +21,41 @@ class GrDrawTargetCaps : public SkRefCnt { public: SK_DECLARE_INST_COUNT(GrDrawTargetCaps) + /** Info about shader variable precision within a given shader stage. That is, this info + is relevant to a float (or vecNf) variable declared with a GrShaderVar::Precision + in a given GrShaderType. The info here is hoisted from the OpenGL spec. */ + struct PrecisionInfo { + PrecisionInfo() { + fLogRangeLow = 0; + fLogRangeHigh = 0; + fBits = 0; + } + + /** Is this precision level allowed in the shader stage? */ + bool supported() const { return 0 != fBits; } + + bool operator==(const PrecisionInfo& that) const { + return fLogRangeLow == that.fLogRangeLow && fLogRangeHigh == that.fLogRangeHigh && + fBits == that.fBits; + } + bool operator!=(const PrecisionInfo& that) const { return !(*this == that); } + + /** floor(log2(|min_value|)) */ + int fLogRangeLow; + /** floor(log2(|max_value|)) */ + int fLogRangeHigh; + /** Number of bits of precision. As defined in OpenGL (with names modified to reflect this + struct) : + """ + If the smallest representable value greater than 1 is 1 + e, then fBits will + contain floor(log2(e)), and every value in the range [2^fLogRangeLow, + 2^fLogRangeHigh] can be represented to at least one part in 2^fBits. + """ + */ + int fBits; + }; + + GrDrawTargetCaps() : fUniqueID(CreateUniqueID()) { this->reset(); } @@ -83,6 +120,24 @@ public: return fConfigTextureSupport[config]; } + /** + * Get the precision info for a variable of type kFloat_GrSLType, kVec2f_GrSLType, etc in a + * given shader type. If the shader type is not supported or the precision level is not + * supported in that shader type then the returned struct will report false when supported() is + * called. + */ + const PrecisionInfo& getFloatShaderPrecisionInfo(GrShaderType shaderType, + GrShaderVar::Precision precision) const { + return fFloatPrecisions[shaderType][precision]; + }; + + /** + * Is there any difference between the float shader variable precision types? If this is true + * then unless the shader type is not supported, any call to getFloatShaderPrecisionInfo() would + * report the same info for all precisions in all shader types. + */ + bool floatPrecisionVaries() const { return fShaderPrecisionVaries; } + /** * Gets an id that is unique for this GrDrawTargetCaps object. It is static in that it does * not change when the content of the GrDrawTargetCaps object changes. This will never return @@ -119,6 +174,9 @@ protected: bool fConfigRenderSupport[kGrPixelConfigCnt][2]; bool fConfigTextureSupport[kGrPixelConfigCnt]; + bool fShaderPrecisionVaries; + PrecisionInfo fFloatPrecisions[kGrShaderTypeCount][GrShaderVar::kPrecisionCount]; + private: static uint32_t CreateUniqueID(); diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index cf442a0430..a510cb456b 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -386,6 +386,8 @@ bool GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) { this->initConfigTexturableTable(ctxInfo, gli); this->initConfigRenderableTable(ctxInfo); + this->initShaderPrecisionTable(ctxInfo, gli); + return true; } @@ -774,6 +776,76 @@ void GrGLCaps::initStencilFormats(const GrGLContextInfo& ctxInfo) { fStencilVerifiedColorConfigs.push_back_n(fStencilFormats.count()); } +static GrGLenum precision_to_gl_float_type(GrShaderVar::Precision p) { + switch (p) { + case GrShaderVar::kLow_Precision: + return GR_GL_LOW_FLOAT; + case GrShaderVar::kMedium_Precision: + return GR_GL_MEDIUM_FLOAT; + case GrShaderVar::kHigh_Precision: + return GR_GL_HIGH_FLOAT; + } + SkFAIL("Unknown precision."); + return -1; +} + +static GrGLenum shader_type_to_gl_shader(GrShaderType type) { + switch (type) { + case kVertex_GrShaderType: + return GR_GL_VERTEX_SHADER; + case kGeometry_GrShaderType: + return GR_GL_GEOMETRY_SHADER; + case kFragment_GrShaderType: + return GR_GL_FRAGMENT_SHADER; + } + SkFAIL("Unknown shader type."); + return -1; +} + +void GrGLCaps::initShaderPrecisionTable(const GrGLContextInfo& ctxInfo, const GrGLInterface* intf) { + if (kGLES_GrGLStandard == ctxInfo.standard() || ctxInfo.version() >= GR_GL_VER(4,1) || + ctxInfo.hasExtension("GL_ARB_ES2_compatibility")) { + for (int s = 0; s < kGrShaderTypeCount; ++s) { + if (kGeometry_GrShaderType != s || fGeometryShaderSupport) { + GrShaderType shaderType = static_cast(s); + GrGLenum glShader = shader_type_to_gl_shader(shaderType); + PrecisionInfo* first = NULL; + fShaderPrecisionVaries = false; + for (int p = 0; p < GrShaderVar::kPrecisionCount; ++p) { + GrShaderVar::Precision precision = static_cast(p); + GrGLenum glPrecision = precision_to_gl_float_type(precision); + GrGLint range[2]; + GrGLint bits; + GR_GL_GetShaderPrecisionFormat(intf, glShader, glPrecision, range, &bits); + if (bits) { + fFloatPrecisions[s][p].fLogRangeLow = range[0]; + fFloatPrecisions[s][p].fLogRangeHigh = range[1]; + fFloatPrecisions[s][p].fBits = bits; + if (!first) { + first = &fFloatPrecisions[s][p]; + } else if (!fShaderPrecisionVaries) { + fShaderPrecisionVaries = (*first != fFloatPrecisions[s][p]); + } + } + } + } + } + } else { + // We're on a desktop GL that doesn't have precision info. Assume they're all 32bit float. + fShaderPrecisionVaries = false; + for (int s = 0; s < kGrShaderTypeCount; ++s) { + if (kGeometry_GrShaderType != s || fGeometryShaderSupport) { + for (int p = 0; p < GrShaderVar::kPrecisionCount; ++p) { + fFloatPrecisions[s][p].fLogRangeLow = 127; + fFloatPrecisions[s][p].fLogRangeHigh = 127; + fFloatPrecisions[s][p].fBits = 23; + } + } + } + } +} + + void GrGLCaps::markColorConfigAndStencilFormatAsVerified( GrPixelConfig config, const GrGLStencilBuffer::Format& format) { diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h index f84019335c..19e9b878de 100644 --- a/src/gpu/gl/GrGLCaps.h +++ b/src/gpu/gl/GrGLCaps.h @@ -325,9 +325,10 @@ private: void initConfigRenderableTable(const GrGLContextInfo&); void initConfigTexturableTable(const GrGLContextInfo&, const GrGLInterface*); - bool doReadPixelsSupported(const GrGLInterface* intf, - GrGLenum format, - GrGLenum type) const; + // Must be called after fGeometryShaderSupport is initialized. + void initShaderPrecisionTable(const GrGLContextInfo&, const GrGLInterface*); + + bool doReadPixelsSupported(const GrGLInterface* intf, GrGLenum format, GrGLenum type) const; // tracks configs that have been verified to pass the FBO completeness when // used as a color attachment diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp index 6867a1329b..16de1eab22 100644 --- a/src/gpu/gl/GrGLProgramDesc.cpp +++ b/src/gpu/gl/GrGLProgramDesc.cpp @@ -13,25 +13,6 @@ #include "SkChecksum.h" #include "gl/builders/GrGLFragmentShaderBuilder.h" -/** - * The key for an individual coord transform is made up of a matrix type and a bit that - * indicates the source of the input coords. - */ -enum { - kMatrixTypeKeyBits = 1, - kMatrixTypeKeyMask = (1 << kMatrixTypeKeyBits) - 1, - kPositionCoords_Flag = (1 << kMatrixTypeKeyBits), - kTransformKeyBits = kMatrixTypeKeyBits + 1, -}; - -/** - * We specialize the vertex code for each of these matrix types. - */ -enum MatrixType { - kNoPersp_MatrixType = 0, - kGeneral_MatrixType = 1, -}; - /** * 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 @@ -72,8 +53,33 @@ static uint32_t gen_attrib_key(const GrGeometryProcessor& proc) { return key; } -static uint32_t gen_transform_key(const GrPendingFragmentStage& stage, - bool useExplicitLocalCoords) { +/** + * The key for an individual coord transform is made up of a matrix type, a precision, and a bit + * that indicates the source of the input coords. + */ +enum { + kMatrixTypeKeyBits = 1, + kMatrixTypeKeyMask = (1 << kMatrixTypeKeyBits) - 1, + + kPrecisionBits = 2, + kPrecisionShift = kMatrixTypeKeyBits, + + kPositionCoords_Flag = (1 << (kPrecisionShift + kPrecisionBits)), + + kTransformKeyBits = kMatrixTypeKeyBits + kPrecisionBits + 1, +}; + +GR_STATIC_ASSERT(GrShaderVar::kHigh_Precision < (1 << kPrecisionBits)); + +/** + * We specialize the vertex code for each of these matrix types. + */ +enum MatrixType { + kNoPersp_MatrixType = 0, + kGeneral_MatrixType = 1, +}; + +static uint32_t gen_transform_key(const GrPendingFragmentStage& stage, bool useExplicitLocalCoords) { uint32_t totalKey = 0; int numTransforms = stage.getProcessor()->numTransforms(); for (int t = 0; t < numTransforms; ++t) { @@ -88,7 +94,12 @@ static uint32_t gen_transform_key(const GrPendingFragmentStage& stage, if (kLocal_GrCoordSet != coordTransform.sourceCoords() && useExplicitLocalCoords) { key |= kPositionCoords_Flag; } + + GR_STATIC_ASSERT(GrShaderVar::kPrecisionCount <= (1 << kPrecisionBits)); + key |= (coordTransform.precision() << kPrecisionShift); + key <<= kTransformKeyBits * t; + SkASSERT(0 == (totalKey & key)); // keys for each transform ought not to overlap totalKey |= key; } diff --git a/src/gpu/gl/GrGLUtil.h b/src/gpu/gl/GrGLUtil.h index bc6fdf1757..570694af31 100644 --- a/src/gpu/gl/GrGLUtil.h +++ b/src/gpu/gl/GrGLUtil.h @@ -71,12 +71,21 @@ enum GrGLRenderer { *(p) = GR_GL_INIT_ZERO; \ GR_GL_CALL(gl, GetRenderbufferParameteriv(t, pname, p)); \ } while (0) + #define GR_GL_GetTexLevelParameteriv(gl, t, l, pname, p) \ do { \ *(p) = GR_GL_INIT_ZERO; \ GR_GL_CALL(gl, GetTexLevelParameteriv(t, l, pname, p)); \ } while (0) +#define GR_GL_GetShaderPrecisionFormat(gl, st, pt, range, precision) \ + do { \ + (range)[0] = GR_GL_INIT_ZERO; \ + (range)[1] = GR_GL_INIT_ZERO; \ + (*precision) = GR_GL_INIT_ZERO; \ + GR_GL_CALL(gl, GetShaderPrecisionFormat(st, pt, range, precision)); \ + } while (0) + //////////////////////////////////////////////////////////////////////////////// /** diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp index 4d8011cd97..5ed566fa33 100644 --- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp +++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp @@ -413,7 +413,8 @@ void GrGLProgramBuilder::emitTransforms(const GrPendingFragmentStage& stage, const char* coords = useLocalCoords ? fVS.localCoords() : fVS.positionCoords(); GrGLVertToFrag v(varyingType); - this->addCoordVarying(varyingName, &v, uniName, coords); + this->addVarying(varyingName, &v, processor->coordTransform(t).precision()); + fCoordVaryings.push_back(TransformVarying(v, uniName, coords)); SkASSERT(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType); SkNEW_APPEND_TO_TARRAY(outCoords, GrGLProcessor::TransformedCoords, diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h index 973ae5e078..b7458b3e8f 100644 --- a/src/gpu/gl/builders/GrGLProgramBuilder.h +++ b/src/gpu/gl/builders/GrGLProgramBuilder.h @@ -30,9 +30,9 @@ class GrGLUniformBuilder { public: enum ShaderVisibility { - kVertex_Visibility = 0x1, - kGeometry_Visibility = 0x2, - kFragment_Visibility = 0x4, + kVertex_Visibility = 1 << kVertex_GrShaderType, + kGeometry_Visibility = 1 << kGeometry_GrShaderType, + kFragment_Visibility = 1 << kFragment_GrShaderType, }; virtual ~GrGLUniformBuilder() {} @@ -344,12 +344,6 @@ protected: SkString fSourceCoords; }; - void addCoordVarying(const char* name, GrGLVarying* v, const char* uniName, - const char* sourceCoords) { - this->addVarying(name, v); - fCoordVaryings.push_back(TransformVarying(*v, uniName, sourceCoords)); - } - const char* rtAdjustment() const { return "rtAdjustment"; } // number of each input/output type in a single allocation block, used by many builders -- cgit v1.2.3