static IntersectionType intersection(const SkPoint& p1, const SkPoint& p2, const SkPoint& p3, const SkPoint& p4, SkPoint& res) { // Store the values for fast access and easy // equations-to-code conversion SkScalar x1 = p1.x(), x2 = p2.x(), x3 = p3.x(), x4 = p4.x(); SkScalar y1 = p1.y(), y2 = p2.y(), y3 = p3.y(), y4 = p4.y(); SkScalar d = SkScalarMul(x1 - x2, y3 - y4) - SkScalarMul(y1 - y2, x3 - x4); // If d is zero, there is no intersection if (SkScalarNearlyZero(d)) { return kNone_IntersectionType; } // Get the x and y SkScalar pre = SkScalarMul(x1, y2) - SkScalarMul(y1, x2), post = SkScalarMul(x3, y4) - SkScalarMul(y3, x4); // Compute the point of intersection res.set(SkScalarDiv(SkScalarMul(pre, x3 - x4) - SkScalarMul(x1 - x2, post), d), SkScalarDiv(SkScalarMul(pre, y3 - y4) - SkScalarMul(y1 - y2, post), d)); // Check if the x and y coordinates are within both lines return (res.x() < GrMin(x1, x2) || res.x() > GrMax(x1, x2) || res.x() < GrMin(x3, x4) || res.x() > GrMax(x3, x4) || res.y() < GrMin(y1, y2) || res.y() > GrMax(y1, y2) || res.y() < GrMin(y3, y4) || res.y() > GrMax(y3, y4)) ? kOut_IntersectionType : kIn_IntersectionType; }
void GrAAHairLinePathRenderer::drawPath(GrDrawState::StageMask stageMask) { if (!this->createGeom(stageMask)) { return; } GrDrawState* drawState = fTarget->drawState(); GrDrawTarget::AutoStateRestore asr; if (!drawState->getViewMatrix().hasPerspective()) { asr.set(fTarget); GrMatrix ivm; if (drawState->getViewInverse(&ivm)) { drawState->preConcatSamplerMatrices(stageMask, ivm); } drawState->setViewMatrix(GrMatrix::I()); } // TODO: See whether rendering lines as degenerate quads improves perf // when we have a mix fTarget->setIndexSourceToBuffer(fLinesIndexBuffer); int lines = 0; int nBufLines = fLinesIndexBuffer->maxQuads(); while (lines < fLineSegmentCnt) { int n = GrMin(fLineSegmentCnt-lines, nBufLines); drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType); fTarget->drawIndexed(kTriangles_PrimitiveType, kVertsPerLineSeg*lines, // startV 0, // startI kVertsPerLineSeg*n, // vCount kIdxsPerLineSeg*n); // iCount lines += n; } fTarget->setIndexSourceToBuffer(fQuadsIndexBuffer); int quads = 0; while (quads < fQuadCnt) { int n = GrMin(fQuadCnt-quads, kNumQuadsInIdxBuffer); drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType); fTarget->drawIndexed(kTriangles_PrimitiveType, 4*fLineSegmentCnt + kVertsPerQuad*quads, // startV 0, // startI kVertsPerQuad*n, // vCount kIdxsPerQuad*n); // iCount quads += n; } }
uint32_t GrPathUtils::cubicPointCount(const GrPoint points[], GrScalar tol) { if (tol < gMinCurveTol) { tol == gMinCurveTol; } GrAssert(tol > 0); GrScalar d = GrMax( points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]), points[2].distanceToLineSegmentBetweenSqd(points[0], points[3])); d = SkScalarSqrt(d); if (d <= tol) { return 1; } else { int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol))); int pow2 = GrNextPow2(temp); // Because of NaNs & INFs we can wind up with a degenerate temp // such that pow2 comes out negative. Also, our point generator // will always output at least one pt. if (pow2 < 1) { pow2 = 1; } return GrMin(pow2, MAX_POINTS_PER_CURVE); } }
uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[], GrScalar tol) { if (tol < gMinCurveTol) { tol == gMinCurveTol; } GrAssert(tol > 0); GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]); if (d <= tol) { return 1; } else { // Each time we subdivide, d should be cut in 4. So we need to // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x) // points. // 2^(log4(x)) = sqrt(x); int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol))); int pow2 = GrNextPow2(temp); // Because of NaNs & INFs we can wind up with a degenerate temp // such that pow2 comes out negative. Also, our point generator // will always output at least one pt. if (pow2 < 1) { pow2 = 1; } return GrMin(pow2, MAX_POINTS_PER_CURVE); } }
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; } }
size_t release(size_t bytes) { GrAssert(bytes > 0); size_t free = GrMin(bytes, fBytesTotal - fBytesFree); fBytesFree += free; fPtr -= free; return bytes - free; }
static uint32_t cubic_point_count(const GrPoint points[], GrScalar tol) { GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]), points[2].distanceToLineSegmentBetweenSqd(points[0], points[3])); d = sqrtf(d); if (d < tol) { return 1; } else { d = ceilf(sqrtf(d/tol)); return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE); } }
int GrInOrderDrawBuffer::concatInstancedDraw(const DrawInfo& info) { GrAssert(info.isInstanced()); const GeometrySrcState& geomSrc = this->getGeomSrc(); const GrDrawState& drawState = this->getDrawState(); // we only attempt to concat the case when reserved verts are used with a client-specified index // buffer. To make this work with client-specified VBs we'd need to know if the VB was updated // between draws. if (kReserved_GeometrySrcType != geomSrc.fVertexSrc || kBuffer_GeometrySrcType != geomSrc.fIndexSrc) { return 0; } // Check if there is a draw info that is compatible that uses the same VB from the pool and // the same IB if (kDraw_Cmd != fCmds.back()) { return 0; } DrawRecord* draw = &fDraws.back(); GeometryPoolState& poolState = fGeoPoolStateStack.back(); const GrVertexBuffer* vertexBuffer = poolState.fPoolVertexBuffer; if (!draw->isInstanced() || draw->verticesPerInstance() != info.verticesPerInstance() || draw->indicesPerInstance() != info.indicesPerInstance() || draw->fVertexBuffer != vertexBuffer || draw->fIndexBuffer != geomSrc.fIndexBuffer) { return 0; } // info does not yet account for the offset from the start of the pool's VB while the previous // draw record does. int adjustedStartVertex = poolState.fPoolStartVertex + info.startVertex(); if (draw->startVertex() + draw->vertexCount() != adjustedStartVertex) { return 0; } GrAssert(poolState.fPoolStartVertex == draw->startVertex() + draw->vertexCount()); // how many instances can be concat'ed onto draw given the size of the index buffer int instancesToConcat = this->indexCountInCurrentSource() / info.indicesPerInstance(); instancesToConcat -= draw->instanceCount(); instancesToConcat = GrMin(instancesToConcat, info.instanceCount()); // update the amount of reserved vertex data actually referenced in draws size_t vertexBytes = instancesToConcat * info.verticesPerInstance() * drawState.getVertexSize(); poolState.fUsedPoolVertexBytes = GrMax(poolState.fUsedPoolVertexBytes, vertexBytes); draw->adjustInstanceCount(instancesToConcat); return instancesToConcat; }
static uint32_t quadratic_point_count(const GrPoint points[], GrScalar tol) { GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]); if (d < tol) { return 1; } else { // Each time we subdivide, d should be cut in 4. So we need to // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x) // points. // 2^(log4(x)) = sqrt(x); d = ceilf(sqrtf(d/tol)); return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE); } }
void GrDrawTarget::drawIndexedInstances(GrPrimitiveType type, int instanceCount, int verticesPerInstance, int indicesPerInstance, const SkRect* devBounds) { if (!verticesPerInstance || !indicesPerInstance) { return; } int maxInstancesPerDraw = this->indexCountInCurrentSource() / indicesPerInstance; if (!maxInstancesPerDraw) { return; } DrawInfo info; info.fPrimitiveType = type; info.fStartIndex = 0; info.fStartVertex = 0; info.fIndicesPerInstance = indicesPerInstance; info.fVerticesPerInstance = verticesPerInstance; // Set the same bounds for all the draws. 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); info.fVertexCount = info.fInstanceCount * verticesPerInstance; info.fIndexCount = info.fInstanceCount * indicesPerInstance; if (this->checkDraw(type, info.fStartVertex, info.fStartIndex, info.fVertexCount, info.fIndexCount)) { this->onDraw(info); } info.fStartVertex += info.fVertexCount; instanceCount -= info.fInstanceCount; } }
const GrGLCaps::MSAACoverageMode& GrGLCaps::getMSAACoverageMode(int desiredSampleCount) const { static const MSAACoverageMode kNoneMode = {0, 0}; if (0 == fMSAACoverageModes.count()) { return kNoneMode; } else { GrAssert(kNone_CoverageAAType != fCoverageAAType); int max = (fMSAACoverageModes.end() - 1)->fCoverageSampleCnt; desiredSampleCount = GrMin(desiredSampleCount, max); MSAACoverageMode desiredMode = {desiredSampleCount, 0}; int idx = SkTSearch<MSAACoverageMode>(&fMSAACoverageModes[0], fMSAACoverageModes.count(), desiredMode, sizeof(MSAACoverageMode), &coverage_mode_compare); if (idx < 0) { idx = ~idx; } GrAssert(idx >= 0 && idx < fMSAACoverageModes.count()); return fMSAACoverageModes[idx]; } }
bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path, const SkStrokeRec&, GrDrawTarget* target, bool antiAlias) { int lineCnt; int quadCnt; GrDrawTarget::AutoReleaseGeometry arg; if (!this->createGeom(path, target, &lineCnt, &quadCnt, &arg)) { return false; } GrDrawState::AutoDeviceCoordDraw adcd; GrDrawState* drawState = target->drawState(); // createGeom transforms the geometry to device space when the matrix does not have // perspective. if (!drawState->getViewMatrix().hasPerspective()) { adcd.set(drawState); if (!adcd.succeeded()) { return false; } } // TODO: See whether rendering lines as degenerate quads improves perf // when we have a mix GrDrawState::VertexEdgeType oldEdgeType = drawState->getVertexEdgeType(); target->setIndexSourceToBuffer(fLinesIndexBuffer); int lines = 0; int nBufLines = fLinesIndexBuffer->maxQuads(); drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType); while (lines < lineCnt) { int n = GrMin(lineCnt - lines, nBufLines); target->drawIndexed(kTriangles_GrPrimitiveType, kVertsPerLineSeg*lines, // startV 0, // startI kVertsPerLineSeg*n, // vCount kIdxsPerLineSeg*n); // iCount lines += n; } target->setIndexSourceToBuffer(fQuadsIndexBuffer); int quads = 0; drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType); while (quads < quadCnt) { int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer); target->drawIndexed(kTriangles_GrPrimitiveType, 4 * lineCnt + kVertsPerQuad*quads, // startV 0, // startI kVertsPerQuad*n, // vCount kIdxsPerQuad*n); // iCount quads += n; } drawState->setVertexEdgeType(oldEdgeType); return true; }
void GrAARectRenderer::strokeAARect(GrGpu* gpu, GrDrawTarget* target, const GrRect& devRect, const GrVec& devStrokeSize, bool useVertexCoverage) { const SkScalar& dx = devStrokeSize.fX; const SkScalar& dy = devStrokeSize.fY; const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf); const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf); SkScalar spare; { SkScalar w = devRect.width() - dx; SkScalar h = devRect.height() - dy; spare = GrMin(w, h); } if (spare <= 0) { GrRect r(devRect); r.inset(-rx, -ry); this->fillAARect(gpu, target, r, useVertexCoverage); return; } GrVertexLayout layout = aa_rect_layout(useVertexCoverage); size_t vsize = GrDrawTarget::VertexSize(layout); GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0); if (!geo.succeeded()) { GrPrintf("Failed to get space for vertices!\n"); return; } GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(gpu); if (NULL == indexBuffer) { GrPrintf("Failed to create index buffer!\n"); return; } intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices()); // We create vertices for four nested rectangles. There are two ramps from 0 to full // coverage, one on the exterior of the stroke and the other on the interior. // The following pointers refer to the four rects, from outermost to innermost. GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts); GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize); GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 8 * vsize); GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + 12 * vsize); set_inset_fan(fan0Pos, vsize, devRect, -rx - SK_ScalarHalf, -ry - SK_ScalarHalf); set_inset_fan(fan1Pos, vsize, devRect, -rx + SK_ScalarHalf, -ry + SK_ScalarHalf); set_inset_fan(fan2Pos, vsize, devRect, rx - SK_ScalarHalf, ry - SK_ScalarHalf); set_inset_fan(fan3Pos, vsize, devRect, rx + SK_ScalarHalf, ry + SK_ScalarHalf); // The outermost rect has 0 coverage verts += sizeof(GrPoint); for (int i = 0; i < 4; ++i) { *reinterpret_cast<GrColor*>(verts + i * vsize) = 0; } // The inner two rects have full coverage GrColor innerColor; if (useVertexCoverage) { innerColor = 0xffffffff; } else { innerColor = target->getDrawState().getColor(); } verts += 4 * vsize; for (int i = 0; i < 8; ++i) { *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor; } // The innermost rect has full coverage verts += 8 * vsize; for (int i = 0; i < 4; ++i) { *reinterpret_cast<GrColor*>(verts + i * vsize) = 0; } target->setIndexSourceToBuffer(indexBuffer); target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, aaStrokeRectIndexCount()); }
void GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) { this->reset(); if (!ctxInfo.isInitialized()) { return; } GrGLStandard standard = ctxInfo.standard(); GrGLVersion version = ctxInfo.version(); /************************************************************************** * Caps specific to GrGLCaps **************************************************************************/ if (kGLES_GrGLStandard == standard) { GR_GL_GetIntegerv(gli, GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &fMaxFragmentUniformVectors); } else { SkASSERT(kGL_GrGLStandard == standard); GrGLint max; GR_GL_GetIntegerv(gli, GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max); fMaxFragmentUniformVectors = max / 4; if (version >= GR_GL_VER(3, 2)) { GrGLint profileMask; GR_GL_GetIntegerv(gli, GR_GL_CONTEXT_PROFILE_MASK, &profileMask); fIsCoreProfile = SkToBool(profileMask & GR_GL_CONTEXT_CORE_PROFILE_BIT); } if (!fIsCoreProfile) { fFixedFunctionSupport = true; GR_GL_GetIntegerv(gli, GR_GL_MAX_TEXTURE_COORDS, &fMaxFixedFunctionTextureCoords); // Sanity check SkASSERT(fMaxFixedFunctionTextureCoords > 0 && fMaxFixedFunctionTextureCoords < 128); } } GR_GL_GetIntegerv(gli, GR_GL_MAX_VERTEX_ATTRIBS, &fMaxVertexAttributes); GR_GL_GetIntegerv(gli, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &fMaxFragmentTextureUnits); if (kGL_GrGLStandard == standard) { fRGBA8RenderbufferSupport = true; } else { fRGBA8RenderbufferSupport = version >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_OES_rgb8_rgba8") || ctxInfo.hasExtension("GL_ARM_rgba8"); } if (kGL_GrGLStandard == standard) { fBGRAFormatSupport = version >= GR_GL_VER(1,2) || ctxInfo.hasExtension("GL_EXT_bgra"); } else { if (ctxInfo.hasExtension("GL_APPLE_texture_format_BGRA8888")) { fBGRAFormatSupport = true; } else if (ctxInfo.hasExtension("GL_EXT_texture_format_BGRA8888")) { fBGRAFormatSupport = true; fBGRAIsInternalFormat = true; } SkASSERT(fBGRAFormatSupport || kSkia8888_GrPixelConfig != kBGRA_8888_GrPixelConfig); } if (kGL_GrGLStandard == standard) { fTextureSwizzleSupport = version >= GR_GL_VER(3,3) || ctxInfo.hasExtension("GL_ARB_texture_swizzle"); } else { fTextureSwizzleSupport = version >= GR_GL_VER(3,0); } if (kGL_GrGLStandard == standard) { fUnpackRowLengthSupport = true; fUnpackFlipYSupport = false; fPackRowLengthSupport = true; fPackFlipYSupport = false; } else { fUnpackRowLengthSupport = version >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_EXT_unpack_subimage"); fUnpackFlipYSupport = ctxInfo.hasExtension("GL_CHROMIUM_flipy"); fPackRowLengthSupport = version >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_NV_pack_subimage"); fPackFlipYSupport = ctxInfo.hasExtension("GL_ANGLE_pack_reverse_row_order"); } fTextureUsageSupport = (kGLES_GrGLStandard == standard) && ctxInfo.hasExtension("GL_ANGLE_texture_usage"); if (kGL_GrGLStandard == standard) { // The EXT version can apply to either GL or GLES. fTexStorageSupport = version >= GR_GL_VER(4,2) || ctxInfo.hasExtension("GL_ARB_texture_storage") || ctxInfo.hasExtension("GL_EXT_texture_storage"); } else { // Qualcomm Adreno drivers appear to have issues with texture storage. fTexStorageSupport = (version >= GR_GL_VER(3,0) && kQualcomm_GrGLVendor != ctxInfo.vendor()) || ctxInfo.hasExtension("GL_EXT_texture_storage"); } // ARB_texture_rg is part of OpenGL 3.0, but mesa doesn't support it if // it doesn't have ARB_texture_rg extension. if (kGL_GrGLStandard == standard) { if (ctxInfo.isMesa()) { fTextureRedSupport = ctxInfo.hasExtension("GL_ARB_texture_rg"); } else { fTextureRedSupport = version >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_ARB_texture_rg"); } } else { fTextureRedSupport = version >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_EXT_texture_rg"); } fImagingSupport = kGL_GrGLStandard == standard && ctxInfo.hasExtension("GL_ARB_imaging"); // ES 2 only guarantees RGBA/uchar + one other format/type combo for // ReadPixels. The other format has to checked at run-time since it // can change based on which render target is bound fTwoFormatLimit = kGLES_GrGLStandard == standard; // Known issue on at least some Intel platforms: // http://code.google.com/p/skia/issues/detail?id=946 if (kIntel_GrGLVendor != ctxInfo.vendor()) { fFragCoordsConventionSupport = ctxInfo.glslGeneration() >= k150_GrGLSLGeneration || ctxInfo.hasExtension("GL_ARB_fragment_coord_conventions"); } // SGX and Mali GPUs that are based on a tiled-deferred architecture that have trouble with // frequently changing VBOs. We've measured a performance increase using non-VBO vertex // data for dynamic content on these GPUs. Perhaps we should read the renderer string and // limit this decision to specific GPU families rather than basing it on the vendor alone. if (!GR_GL_MUST_USE_VBO && (kARM_GrGLVendor == ctxInfo.vendor() || kImagination_GrGLVendor == ctxInfo.vendor())) { fUseNonVBOVertexAndIndexDynamicData = true; } fDiscardFBSupport = ctxInfo.hasExtension("GL_EXT_discard_framebuffer"); if (kARM_GrGLVendor == ctxInfo.vendor() || kImagination_GrGLVendor == ctxInfo.vendor()) { fFullClearIsFree = true; } if (kGL_GrGLStandard == standard) { fVertexArrayObjectSupport = version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_ARB_vertex_array_object"); } else { fVertexArrayObjectSupport = version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_OES_vertex_array_object"); } if (kGLES_GrGLStandard == standard) { if (ctxInfo.hasExtension("GL_EXT_shader_framebuffer_fetch")) { fFBFetchType = kEXT_FBFetchType; } else if (ctxInfo.hasExtension("GL_NV_shader_framebuffer_fetch")) { fFBFetchType = kNV_FBFetchType; } } this->initFSAASupport(ctxInfo, gli); this->initStencilFormats(ctxInfo); /************************************************************************** * GrDrawTargetCaps fields **************************************************************************/ GrGLint numFormats; GR_GL_GetIntegerv(gli, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats); if (numFormats) { SkAutoSTMalloc<10, GrGLint> formats(numFormats); GR_GL_GetIntegerv(gli, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats); for (int i = 0; i < numFormats; ++i) { if (formats[i] == GR_GL_PALETTE8_RGBA8) { f8BitPaletteSupport = true; break; } } } if (kGL_GrGLStandard == standard) { // we could also look for GL_ATI_separate_stencil extension or // GL_EXT_stencil_two_side but they use different function signatures // than GL2.0+ (and than each other). fTwoSidedStencilSupport = (ctxInfo.version() >= GR_GL_VER(2,0)); // supported on GL 1.4 and higher or by extension fStencilWrapOpsSupport = (ctxInfo.version() >= GR_GL_VER(1,4)) || ctxInfo.hasExtension("GL_EXT_stencil_wrap"); } else { // ES 2 has two sided stencil and stencil wrap fTwoSidedStencilSupport = true; fStencilWrapOpsSupport = true; } if (kGL_GrGLStandard == standard) { fBufferLockSupport = true; // we require VBO support and the desktop VBO extension includes // glMapBuffer. } else { fBufferLockSupport = ctxInfo.hasExtension("GL_OES_mapbuffer"); } if (kGL_GrGLStandard == standard) { SkASSERT(ctxInfo.version() >= GR_GL_VER(2,0) || ctxInfo.hasExtension("GL_ARB_texture_non_power_of_two")); fNPOTTextureTileSupport = true; fMipMapSupport = true; } else { // Unextended ES2 supports NPOT textures with clamp_to_edge and non-mip filters only // ES3 has no limitations. fNPOTTextureTileSupport = ctxInfo.version() >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_OES_texture_npot"); // ES2 supports MIP mapping for POT textures but our caps don't allow for limited MIP // support. The OES extension or ES 3.0 allow for MIPS on NPOT textures. So, apparently, // does the undocumented GL_IMG_texture_npot extension. This extension does not seem to // to alllow arbitrary wrap modes, however. fMipMapSupport = fNPOTTextureTileSupport || ctxInfo.hasExtension("GL_IMG_texture_npot"); } fHWAALineSupport = (kGL_GrGLStandard == standard); GR_GL_GetIntegerv(gli, GR_GL_MAX_TEXTURE_SIZE, &fMaxTextureSize); GR_GL_GetIntegerv(gli, GR_GL_MAX_RENDERBUFFER_SIZE, &fMaxRenderTargetSize); // Our render targets are always created with textures as the color // attachment, hence this min: fMaxRenderTargetSize = GrMin(fMaxTextureSize, fMaxRenderTargetSize); fPathRenderingSupport = ctxInfo.hasExtension("GL_NV_path_rendering"); SkASSERT(!fPathRenderingSupport || fFixedFunctionSupport); fDstReadInShaderSupport = kNone_FBFetchType != fFBFetchType; // Disable scratch texture reuse on Mali and Adreno devices fReuseScratchTextures = kARM_GrGLVendor != ctxInfo.vendor() && kQualcomm_GrGLVendor != ctxInfo.vendor(); // Enable supported shader-related caps if (kGL_GrGLStandard == standard) { fDualSourceBlendingSupport = ctxInfo.version() >= GR_GL_VER(3,3) || ctxInfo.hasExtension("GL_ARB_blend_func_extended"); fShaderDerivativeSupport = true; // we don't support GL_ARB_geometry_shader4, just GL 3.2+ GS fGeometryShaderSupport = ctxInfo.version() >= GR_GL_VER(3,2) && ctxInfo.glslGeneration() >= k150_GrGLSLGeneration; } else { fShaderDerivativeSupport = ctxInfo.hasExtension("GL_OES_standard_derivatives"); } if (GrGLCaps::kES_IMG_MsToTexture_MSFBOType == fMSFBOType) { GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES_IMG, &fMaxSampleCount); } else if (GrGLCaps::kNone_MSFBOType != fMSFBOType) { GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES, &fMaxSampleCount); } this->initConfigRenderableTable(ctxInfo); }
void GrGLCaps::init(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) { this->reset(); if (!ctxInfo.isInitialized()) { return; } GrGLBinding binding = ctxInfo.binding(); GrGLVersion version = ctxInfo.version(); /************************************************************************** * Caps specific to GrGLCaps **************************************************************************/ if (kES2_GrGLBinding == binding) { GR_GL_GetIntegerv(gli, GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &fMaxFragmentUniformVectors); } else { GrAssert(kDesktop_GrGLBinding == binding); GrGLint max; GR_GL_GetIntegerv(gli, GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max); fMaxFragmentUniformVectors = max / 4; } GR_GL_GetIntegerv(gli, GR_GL_MAX_VERTEX_ATTRIBS, &fMaxVertexAttributes); if (kDesktop_GrGLBinding == binding) { fRGBA8RenderbufferSupport = true; } else { fRGBA8RenderbufferSupport = ctxInfo.hasExtension("GL_OES_rgb8_rgba8") || ctxInfo.hasExtension("GL_ARM_rgba8"); } if (kDesktop_GrGLBinding == binding) { fBGRAFormatSupport = version >= GR_GL_VER(1,2) || ctxInfo.hasExtension("GL_EXT_bgra"); } else { if (ctxInfo.hasExtension("GL_APPLE_texture_format_BGRA8888")) { fBGRAFormatSupport = true; } else if (ctxInfo.hasExtension("GL_EXT_texture_format_BGRA8888")) { fBGRAFormatSupport = true; fBGRAIsInternalFormat = true; } GrAssert(fBGRAFormatSupport || kSkia8888_GrPixelConfig != kBGRA_8888_GrPixelConfig); } if (kDesktop_GrGLBinding == binding) { fTextureSwizzleSupport = version >= GR_GL_VER(3,3) || ctxInfo.hasExtension("GL_ARB_texture_swizzle"); } else { fTextureSwizzleSupport = false; } if (kDesktop_GrGLBinding == binding) { fUnpackRowLengthSupport = true; fUnpackFlipYSupport = false; fPackRowLengthSupport = true; fPackFlipYSupport = false; } else { fUnpackRowLengthSupport =ctxInfo.hasExtension("GL_EXT_unpack_subimage"); fUnpackFlipYSupport = ctxInfo.hasExtension("GL_CHROMIUM_flipy"); // no extension for pack row length fPackRowLengthSupport = false; fPackFlipYSupport = ctxInfo.hasExtension("GL_ANGLE_pack_reverse_row_order"); } fTextureUsageSupport = (kES2_GrGLBinding == binding) && ctxInfo.hasExtension("GL_ANGLE_texture_usage"); // Tex storage is in desktop 4.2 and can be an extension to desktop or ES. fTexStorageSupport = (kDesktop_GrGLBinding == binding && version >= GR_GL_VER(4,2)) || ctxInfo.hasExtension("GL_ARB_texture_storage") || ctxInfo.hasExtension("GL_EXT_texture_storage"); // ARB_texture_rg is part of OpenGL 3.0 if (kDesktop_GrGLBinding == binding) { fTextureRedSupport = version >= GR_GL_VER(3,0) || ctxInfo.hasExtension("GL_ARB_texture_rg"); } else { fTextureRedSupport = ctxInfo.hasExtension("GL_EXT_texture_rg"); } fImagingSupport = kDesktop_GrGLBinding == binding && ctxInfo.hasExtension("GL_ARB_imaging"); // ES 2 only guarantees RGBA/uchar + one other format/type combo for // ReadPixels. The other format has to checked at run-time since it // can change based on which render target is bound fTwoFormatLimit = kES2_GrGLBinding == binding; // Known issue on at least some Intel platforms: // http://code.google.com/p/skia/issues/detail?id=946 if (kIntel_GrGLVendor != ctxInfo.vendor()) { fFragCoordsConventionSupport = ctxInfo.glslGeneration() >= k150_GrGLSLGeneration || ctxInfo.hasExtension("GL_ARB_fragment_coord_conventions"); } // SGX and Mali GPUs that are based on a tiled-deferred architecture that have trouble with // frequently changing VBOs. We've measured a performance increase using non-VBO vertex // data for dynamic content on these GPUs. Perhaps we should read the renderer string and // limit this decision to specific GPU families rather than basing it on the vendor alone. if (!GR_GL_MUST_USE_VBO && (kARM_GrGLVendor == ctxInfo.vendor() || kImagination_GrGLVendor == ctxInfo.vendor())) { fUseNonVBOVertexAndIndexDynamicData = true; } if (kDesktop_GrGLBinding == binding && version >= GR_GL_VER(3, 2)) { GrGLint profileMask; GR_GL_GetIntegerv(gli, GR_GL_CONTEXT_PROFILE_MASK, &profileMask); fIsCoreProfile = SkToBool(profileMask & GR_GL_CONTEXT_CORE_PROFILE_BIT); } fDiscardFBSupport = ctxInfo.hasExtension("GL_EXT_discard_framebuffer"); if (kDesktop_GrGLBinding == binding) { fVertexArrayObjectSupport = version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_ARB_vertex_array_object"); } else { fVertexArrayObjectSupport = ctxInfo.hasExtension("GL_OES_vertex_array_object"); } this->initFSAASupport(ctxInfo, gli); this->initStencilFormats(ctxInfo); /************************************************************************** * GrDrawTargetCaps fields **************************************************************************/ GrGLint maxTextureUnits; // check FS and fixed-function texture unit limits // we only use textures in the fragment stage currently. // checks are > to make sure we have a spare unit. GR_GL_GetIntegerv(gli, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits); GrGLint numFormats; GR_GL_GetIntegerv(gli, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats); SkAutoSTMalloc<10, GrGLint> formats(numFormats); GR_GL_GetIntegerv(gli, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats); for (int i = 0; i < numFormats; ++i) { if (formats[i] == GR_GL_PALETTE8_RGBA8) { f8BitPaletteSupport = true; break; } } if (kDesktop_GrGLBinding == binding) { // we could also look for GL_ATI_separate_stencil extension or // GL_EXT_stencil_two_side but they use different function signatures // than GL2.0+ (and than each other). fTwoSidedStencilSupport = (ctxInfo.version() >= GR_GL_VER(2,0)); // supported on GL 1.4 and higher or by extension fStencilWrapOpsSupport = (ctxInfo.version() >= GR_GL_VER(1,4)) || ctxInfo.hasExtension("GL_EXT_stencil_wrap"); } else { // ES 2 has two sided stencil and stencil wrap fTwoSidedStencilSupport = true; fStencilWrapOpsSupport = true; } if (kDesktop_GrGLBinding == binding) { fBufferLockSupport = true; // we require VBO support and the desktop VBO extension includes // glMapBuffer. } else { fBufferLockSupport = ctxInfo.hasExtension("GL_OES_mapbuffer"); } if (kDesktop_GrGLBinding == binding) { if (ctxInfo.version() >= GR_GL_VER(2,0) || ctxInfo.hasExtension("GL_ARB_texture_non_power_of_two")) { fNPOTTextureTileSupport = true; } else { fNPOTTextureTileSupport = false; } } else { // Unextended ES2 supports NPOT textures with clamp_to_edge and non-mip filters only fNPOTTextureTileSupport = ctxInfo.hasExtension("GL_OES_texture_npot"); } fHWAALineSupport = (kDesktop_GrGLBinding == binding); GR_GL_GetIntegerv(gli, GR_GL_MAX_TEXTURE_SIZE, &fMaxTextureSize); GR_GL_GetIntegerv(gli, GR_GL_MAX_RENDERBUFFER_SIZE, &fMaxRenderTargetSize); // Our render targets are always created with textures as the color // attachment, hence this min: fMaxRenderTargetSize = GrMin(fMaxTextureSize, fMaxRenderTargetSize); fPathStencilingSupport = GR_GL_USE_NV_PATH_RENDERING && ctxInfo.hasExtension("GL_NV_path_rendering"); // Enable supported shader-related caps if (kDesktop_GrGLBinding == binding) { fDualSourceBlendingSupport = ctxInfo.version() >= GR_GL_VER(3,3) || ctxInfo.hasExtension("GL_ARB_blend_func_extended"); fShaderDerivativeSupport = true; // we don't support GL_ARB_geometry_shader4, just GL 3.2+ GS fGeometryShaderSupport = ctxInfo.version() >= GR_GL_VER(3,2) && ctxInfo.glslGeneration() >= k150_GrGLSLGeneration; } else { fShaderDerivativeSupport = ctxInfo.hasExtension("GL_OES_standard_derivatives"); } if (GrGLCaps::kImaginationES_MSFBOType == fMSFBOType) { GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES_IMG, &fMaxSampleCount); } else if (GrGLCaps::kNone_MSFBOType != fMSFBOType) { GR_GL_GetIntegerv(gli, GR_GL_MAX_SAMPLES, &fMaxSampleCount); } }
bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path, GrPathFill fill, const GrVec* translate, GrDrawTarget* target, GrDrawState::StageMask stageMask, bool antiAlias) { int lineCnt; int quadCnt; GrDrawTarget::AutoReleaseGeometry arg; if (!this->createGeom(path, translate, target, stageMask, &lineCnt, &quadCnt, &arg)) { return false; } GrDrawTarget::AutoStateRestore asr; GrDrawState* drawState = target->drawState(); if (!drawState->getViewMatrix().hasPerspective()) { // we are going to whack the view matrix to identity to remove // perspective. asr.set(target, GrDrawTarget::kPreserve_ASRInit); drawState = target->drawState(); GrMatrix ivm; if (drawState->getViewInverse(&ivm)) { drawState->preConcatSamplerMatrices(stageMask, ivm); } drawState->viewMatrix()->reset(); } // TODO: See whether rendering lines as degenerate quads improves perf // when we have a mix target->setIndexSourceToBuffer(fLinesIndexBuffer); int lines = 0; int nBufLines = fLinesIndexBuffer->maxQuads(); while (lines < lineCnt) { int n = GrMin(lineCnt - lines, nBufLines); drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType); target->drawIndexed(kTriangles_GrPrimitiveType, kVertsPerLineSeg*lines, // startV 0, // startI kVertsPerLineSeg*n, // vCount kIdxsPerLineSeg*n); // iCount lines += n; } target->setIndexSourceToBuffer(fQuadsIndexBuffer); int quads = 0; while (quads < quadCnt) { int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer); drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType); target->drawIndexed(kTriangles_GrPrimitiveType, 4 * lineCnt + kVertsPerQuad*quads, // startV 0, // startI kVertsPerQuad*n, // vCount kIdxsPerQuad*n); // iCount quads += n; } return true; }