Index: src/gpu/gl/GrGLGpu.cpp |
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp |
index acaf8caca032c52a8a9205bf204ae94290e51444..6a61f3109ebee1777229beb534c013676a356104 100644 |
--- a/src/gpu/gl/GrGLGpu.cpp |
+++ b/src/gpu/gl/GrGLGpu.cpp |
@@ -12,6 +12,7 @@ |
#include "GrGLTextureRenderTarget.h" |
#include "GrGpuResourcePriv.h" |
#include "GrPipeline.h" |
+#include "GrPLSGeometryProcessor.h" |
#include "GrRenderTargetPriv.h" |
#include "GrSurfacePriv.h" |
#include "GrTexturePriv.h" |
@@ -20,6 +21,7 @@ |
#include "builders/GrGLShaderStringBuilder.h" |
#include "glsl/GrGLSL.h" |
#include "glsl/GrGLSLCaps.h" |
+#include "glsl/GrGLSLPLSPathRendering.h" |
#include "SkStrokeRec.h" |
#include "SkTemplates.h" |
@@ -38,7 +40,6 @@ |
#define CHECK_ALLOC_ERROR(iface) GR_GL_NO_ERROR |
#endif |
- |
/////////////////////////////////////////////////////////////////////////////// |
@@ -224,6 +225,14 @@ GrGLGpu::GrGLGpu(GrGLContext* ctx, GrContext* context) |
this->createCopyPrograms(); |
fWireRectProgram.fProgram = 0; |
fWireRectArrayBuffer = 0; |
+ if (this->glCaps().shaderCaps()->plsPathRenderingSupport()) { |
+ this->createPLSSetupProgram(); |
+ } |
+ else { |
+ memset(&fPLSSetupProgram, 0, sizeof(fPLSSetupProgram)); |
+ } |
+ fHWPLSEnabled = false; |
+ fPLSHasBeenUsed = false; |
} |
GrGLGpu::~GrGLGpu() { |
@@ -265,9 +274,106 @@ GrGLGpu::~GrGLGpu() { |
GL_CALL(DeleteBuffers(1, &fWireRectArrayBuffer)); |
} |
+ if (0 != fPLSSetupProgram.fArrayBuffer) { |
+ GL_CALL(DeleteBuffers(1, &fPLSSetupProgram.fArrayBuffer)); |
+ } |
+ |
+ if (0 != fPLSSetupProgram.fProgram) { |
+ GL_CALL(DeleteProgram(fPLSSetupProgram.fProgram)); |
+ } |
+ |
delete fProgramCache; |
} |
+void GrGLGpu::createPLSSetupProgram() { |
+ const char* version = this->glCaps().glslCaps()->versionDeclString(); |
+ |
+ GrGLSLShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier); |
+ GrGLSLShaderVar uTexCoordXform("u_texCoordXform", kVec4f_GrSLType, |
+ GrShaderVar::kUniform_TypeModifier); |
+ GrGLSLShaderVar uPosXform("u_posXform", kVec4f_GrSLType, GrShaderVar::kUniform_TypeModifier); |
+ GrGLSLShaderVar uTexture("u_texture", kSampler2D_GrSLType, GrShaderVar::kUniform_TypeModifier); |
+ GrGLSLShaderVar vTexCoord("v_texCoord", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier); |
+ |
+ SkString vshaderTxt(version); |
+ aVertex.appendDecl(this->glCaps().glslCaps(), &vshaderTxt); |
+ vshaderTxt.append(";"); |
+ uTexCoordXform.appendDecl(this->glCaps().glslCaps(), &vshaderTxt); |
+ vshaderTxt.append(";"); |
+ uPosXform.appendDecl(this->glCaps().glslCaps(), &vshaderTxt); |
+ vshaderTxt.append(";"); |
+ vTexCoord.appendDecl(this->glCaps().glslCaps(), &vshaderTxt); |
+ vshaderTxt.append(";"); |
+ |
+ vshaderTxt.append( |
+ "// PLS Setup Program VS\n" |
+ "void main() {" |
+ " gl_Position.xy = a_vertex * u_posXform.xy + u_posXform.zw;" |
+ " gl_Position.zw = vec2(0, 1);" |
+ "}" |
+ ); |
+ |
+ SkString fshaderTxt(version); |
+ fshaderTxt.append("#extension "); |
+ fshaderTxt.append(this->glCaps().glslCaps()->fbFetchExtensionString()); |
+ fshaderTxt.append(" : require\n"); |
+ fshaderTxt.append("#extension GL_EXT_shader_pixel_local_storage : require\n"); |
+ GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, |
+ *this->glCaps().glslCaps(), |
+ &fshaderTxt); |
+ vTexCoord.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier); |
+ vTexCoord.appendDecl(this->glCaps().glslCaps(), &fshaderTxt); |
+ fshaderTxt.append(";"); |
+ uTexture.appendDecl(this->glCaps().glslCaps(), &fshaderTxt); |
+ fshaderTxt.append(";"); |
+ |
+ fshaderTxt.appendf( |
+ "// PLS Setup Program FS\n" |
+ GR_GL_PLS_PATH_DATA_DECL |
+ "void main() {\n" |
+ " " GR_GL_PLS_DSTCOLOR_NAME " = gl_LastFragColorARM;\n" |
+ " pls.windings = ivec4(0, 0, 0, 0);\n" |
+ "}" |
+ ); |
+ GL_CALL_RET(fPLSSetupProgram.fProgram, CreateProgram()); |
+ const char* str; |
+ GrGLint length; |
+ |
+ str = vshaderTxt.c_str(); |
+ length = SkToInt(vshaderTxt.size()); |
+ GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fPLSSetupProgram.fProgram, |
+ GR_GL_VERTEX_SHADER, &str, &length, 1, &fStats); |
+ |
+ str = fshaderTxt.c_str(); |
+ length = SkToInt(fshaderTxt.size()); |
+ GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fPLSSetupProgram.fProgram, |
+ GR_GL_FRAGMENT_SHADER, &str, &length, 1, &fStats); |
+ |
+ GL_CALL(LinkProgram(fPLSSetupProgram.fProgram)); |
+ |
+ GL_CALL_RET(fPLSSetupProgram.fPosXformUniform, GetUniformLocation(fPLSSetupProgram.fProgram, |
+ "u_posXform")); |
+ |
+ GL_CALL(BindAttribLocation(fPLSSetupProgram.fProgram, 0, "a_vertex")); |
+ |
+ GL_CALL(DeleteShader(vshader)); |
+ GL_CALL(DeleteShader(fshader)); |
+ |
+ GL_CALL(GenBuffers(1, &fPLSSetupProgram.fArrayBuffer)); |
+ fHWGeometryState.setVertexBufferID(this, fPLSSetupProgram.fArrayBuffer); |
+ static const GrGLfloat vdata[] = { |
+ 0, 0, |
+ 0, 1, |
+ 1, 0, |
+ 1, 1 |
+ }; |
+ GL_ALLOC_CALL(this->glInterface(), |
+ BufferData(GR_GL_ARRAY_BUFFER, |
+ (GrGLsizeiptr) sizeof(vdata), |
+ vdata, // data ptr |
+ GR_GL_STATIC_DRAW)); |
+} |
+ |
void GrGLGpu::contextAbandoned() { |
INHERITED::contextAbandoned(); |
fProgramCache->abandon(); |
@@ -2268,8 +2374,28 @@ bool GrGLGpu::onReadPixels(GrSurface* surface, |
return true; |
} |
-void GrGLGpu::flushRenderTarget(GrGLRenderTarget* target, const SkIRect* bound) { |
+void GrGLGpu::performFlushWorkaround() { |
+ if (fPLSHasBeenUsed) { |
+ /* There is an ARM driver bug where if we use PLS, and then draw a frame which does not |
+ * use PLS, it leaves garbage all over the place. As a workaround, we use PLS in a |
+ * trivial way every frame. And since we use it every frame, there's never a point at which |
+ * it becomes safe to stop using this workaround once we start. |
+ */ |
+ this->disableScissor(); |
+ // using PLS in the presence of MSAA results in GL_INVALID_OPERATION |
+ this->flushHWAAState(nullptr, false); |
+ SkASSERT(!fHWPLSEnabled); |
+ SkASSERT(fMSAAEnabled != kYes_TriState); |
+ GL_CALL(Enable(GR_GL_SHADER_PIXEL_LOCAL_STORAGE)); |
+ this->stampRectUsingProgram(fPLSSetupProgram.fProgram, |
+ SkRect::MakeXYWH(-100.0f, -100.0f, 0.01f, 0.01f), |
+ fPLSSetupProgram.fPosXformUniform, |
+ fPLSSetupProgram.fArrayBuffer); |
+ GL_CALL(Disable(GR_GL_SHADER_PIXEL_LOCAL_STORAGE)); |
+ } |
+} |
+void GrGLGpu::flushRenderTarget(GrGLRenderTarget* target, const SkIRect* bound) { |
SkASSERT(target); |
uint32_t rtID = target->getUniqueID(); |
@@ -2352,6 +2478,19 @@ void GrGLGpu::onDraw(const DrawArgs& args, const GrNonInstancedVertices& vertice |
return; |
} |
+ GrPixelLocalStorageState plsState = args.fPrimitiveProcessor->getPixelLocalStorageState(); |
+ if (!fHWPLSEnabled && plsState != |
+ GrPixelLocalStorageState::kDisabled_GrPixelLocalStorageState) { |
+ GL_CALL(Enable(GR_GL_SHADER_PIXEL_LOCAL_STORAGE)); |
+ this->setupPixelLocalStorage(args); |
+ fHWPLSEnabled = true; |
+ } |
+ if (plsState == GrPixelLocalStorageState::kFinish_GrPixelLocalStorageState) { |
+ GrStencilSettings stencil; |
+ stencil.setDisabled(); |
+ this->flushStencil(stencil); |
+ } |
+ |
size_t indexOffsetInBytes = 0; |
this->setupGeometry(*args.fPrimitiveProcessor, vertices, &indexOffsetInBytes); |
@@ -2373,6 +2512,16 @@ void GrGLGpu::onDraw(const DrawArgs& args, const GrNonInstancedVertices& vertice |
GL_CALL(DrawArrays(gPrimitiveType2GLMode[vertices.primitiveType()], 0, |
vertices.vertexCount())); |
} |
+ |
+ if (fHWPLSEnabled && plsState == GrPixelLocalStorageState::kFinish_GrPixelLocalStorageState) { |
+ // PLS draws always involve multiple draws, finishing up with a non-PLS |
+ // draw that writes to the color buffer. That draw ends up here; we wait |
+ // until after it is complete to actually disable PLS. |
+ GL_CALL(Disable(GR_GL_SHADER_PIXEL_LOCAL_STORAGE)); |
+ fHWPLSEnabled = false; |
+ this->disableScissor(); |
+ } |
+ |
#if SWAP_PER_DRAW |
glFlush(); |
#if defined(SK_BUILD_FOR_MAC) |
@@ -2387,6 +2536,57 @@ void GrGLGpu::onDraw(const DrawArgs& args, const GrNonInstancedVertices& vertice |
#endif |
} |
+void GrGLGpu::stampRectUsingProgram(GrGLuint program, const SkRect& bounds, GrGLint posXformUniform, |
+ GrGLuint arrayBuffer) { |
+ GL_CALL(UseProgram(program)); |
+ this->fHWGeometryState.setVertexArrayID(this, 0); |
+ |
+ GrGLAttribArrayState* attribs = |
+ this->fHWGeometryState.bindArrayAndBufferToDraw(this, arrayBuffer); |
+ attribs->set(this, 0, arrayBuffer, 2, GR_GL_FLOAT, false, 2 * sizeof(GrGLfloat), 0); |
+ attribs->disableUnusedArrays(this, 0x1); |
+ |
+ GL_CALL(Uniform4f(posXformUniform, bounds.width(), bounds.height(), bounds.left(), |
+ bounds.top())); |
+ |
+ GrXferProcessor::BlendInfo blendInfo; |
+ blendInfo.reset(); |
+ this->flushBlend(blendInfo, GrSwizzle()); |
+ this->flushColorWrite(true); |
+ this->flushDrawFace(GrPipelineBuilder::kBoth_DrawFace); |
+ if (!fHWStencilSettings.isDisabled()) { |
+ GL_CALL(Disable(GR_GL_STENCIL_TEST)); |
+ } |
+ GL_CALL(DrawArrays(GR_GL_TRIANGLE_STRIP, 0, 4)); |
+ GL_CALL(UseProgram(fHWProgramID)); |
+ if (!fHWStencilSettings.isDisabled()) { |
+ GL_CALL(Enable(GR_GL_STENCIL_TEST)); |
+ } |
+} |
+ |
+void GrGLGpu::setupPixelLocalStorage(const DrawArgs& args) { |
+ fPLSHasBeenUsed = true; |
+ const SkRect& bounds = |
+ static_cast<const GrPLSGeometryProcessor*>(args.fPrimitiveProcessor)->getBounds(); |
+ // setup pixel local storage -- this means capturing and storing the current framebuffer color |
+ // and initializing the winding counts to zero |
+ GrRenderTarget* rt = args.fPipeline->getRenderTarget(); |
+ SkScalar width = SkIntToScalar(rt->width()); |
+ SkScalar height = SkIntToScalar(rt->height()); |
+ // dst rect edges in NDC (-1 to 1) |
+ // having some issues with rounding, just expand the bounds by 1 and trust the scissor to keep |
+ // it contained properly |
+ GrGLfloat dx0 = 2.0f * (bounds.left() - 1) / width - 1.0f; |
+ GrGLfloat dx1 = 2.0f * (bounds.right() + 1) / width - 1.0f; |
+ GrGLfloat dy0 = -2.0f * (bounds.top() - 1) / height + 1.0f; |
+ GrGLfloat dy1 = -2.0f * (bounds.bottom() + 1) / height + 1.0f; |
+ SkRect deviceBounds = SkRect::MakeXYWH(dx0, dy0, dx1 - dx0, dy1 - dy0); |
+ |
+ GL_CALL(Enable(GR_GL_FETCH_PER_SAMPLE_ARM)); |
+ this->stampRectUsingProgram(fPLSSetupProgram.fProgram, deviceBounds, |
+ fPLSSetupProgram.fPosXformUniform, fPLSSetupProgram.fArrayBuffer); |
+} |
+ |
void GrGLGpu::onResolveRenderTarget(GrRenderTarget* target) { |
GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(target); |
if (rt->needsResolve()) { |