/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/ganesh/GrOpsRenderPass.h" #include "include/gpu/GpuTypes.h" #include "include/gpu/ganesh/GrBackendSurface.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkTo.h" #include "src/gpu/ganesh/GrCaps.h" #include "src/gpu/ganesh/GrCpuBuffer.h" #include "src/gpu/ganesh/GrDrawIndirectCommand.h" #include "src/gpu/ganesh/GrGeometryProcessor.h" #include "src/gpu/ganesh/GrGpu.h" #include "src/gpu/ganesh/GrGpuBuffer.h" #include "src/gpu/ganesh/GrPipeline.h" #include "src/gpu/ganesh/GrProgramInfo.h" #include "src/gpu/ganesh/GrSamplerState.h" #include "src/gpu/ganesh/GrScissorState.h" #include "src/gpu/ganesh/GrSurfaceProxy.h" #include "src/gpu/ganesh/GrTexture.h" #include "src/gpu/ganesh/GrUserStencilSettings.h" #include #include #include void GrOpsRenderPass::begin() { fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured; #ifdef SK_DEBUG fScissorStatus = DynamicStateStatus::kDisabled; fTextureBindingStatus = DynamicStateStatus::kDisabled; fHasIndexBuffer = false; fInstanceBufferStatus = DynamicStateStatus::kDisabled; fVertexBufferStatus = DynamicStateStatus::kDisabled; #endif this->onBegin(); } void GrOpsRenderPass::end() { this->onEnd(); this->resetActiveBuffers(); } void GrOpsRenderPass::clear(const GrScissorState& scissor, std::array color) { SkASSERT(fRenderTarget); // A clear at this level will always be a true clear, so make sure clears were not supposed to // be redirected to draws instead SkASSERT(!this->gpu()->caps()->performColorClearsAsDraws()); SkASSERT(!scissor.enabled() || !this->gpu()->caps()->performPartialClearsAsDraws()); fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured; this->onClear(scissor, color); } void GrOpsRenderPass::clearStencilClip(const GrScissorState& scissor, bool insideStencilMask) { // As above, make sure the stencil clear wasn't supposed to be a draw rect with stencil settings SkASSERT(!this->gpu()->caps()->performStencilClearsAsDraws()); SkASSERT(!scissor.enabled() || !this->gpu()->caps()->performPartialClearsAsDraws()); fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured; this->onClearStencilClip(scissor, insideStencilMask); } void GrOpsRenderPass::executeDrawable(std::unique_ptr drawable) { fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured; this->onExecuteDrawable(std::move(drawable)); } void GrOpsRenderPass::bindPipeline(const GrProgramInfo& programInfo, const SkRect& drawBounds) { #ifdef SK_DEBUG // Both the 'programInfo' and this renderPass have an origin. Since they come from the same // place (i.e., the target renderTargetProxy) they had best agree. SkASSERT(programInfo.origin() == fOrigin); if (programInfo.geomProc().hasInstanceAttributes()) { SkASSERT(this->gpu()->caps()->drawInstancedSupport()); } if (programInfo.pipeline().usesConservativeRaster()) { SkASSERT(this->gpu()->caps()->conservativeRasterSupport()); } if (programInfo.pipeline().isWireframe()) { SkASSERT(this->gpu()->caps()->wireframeSupport()); } if (this->gpu()->caps()->twoSidedStencilRefsAndMasksMustMatch() && programInfo.isStencilEnabled()) { const GrUserStencilSettings* stencil = programInfo.userStencilSettings(); if (stencil->isTwoSided(programInfo.pipeline().hasStencilClip())) { SkASSERT(stencil->fCCWFace.fRef == stencil->fCWFace.fRef); SkASSERT(stencil->fCCWFace.fTestMask == stencil->fCWFace.fTestMask); SkASSERT(stencil->fCCWFace.fWriteMask == stencil->fCWFace.fWriteMask); } } programInfo.checkAllInstantiated(); programInfo.checkMSAAAndMIPSAreResolved(); #endif this->resetActiveBuffers(); if (programInfo.geomProc().numVertexAttributes() > this->gpu()->caps()->maxVertexAttributes()) { fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind; return; } if (!this->onBindPipeline(programInfo, drawBounds)) { fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind; return; } #ifdef SK_DEBUG fScissorStatus = (programInfo.pipeline().isScissorTestEnabled()) ? DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled; bool hasTextures = (programInfo.geomProc().numTextureSamplers() > 0); if (!hasTextures) { programInfo.pipeline().visitProxies( [&hasTextures](GrSurfaceProxy*, skgpu::Mipmapped) { hasTextures = true; }); } fTextureBindingStatus = (hasTextures) ? DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled; fHasIndexBuffer = false; fInstanceBufferStatus = (programInfo.geomProc().hasInstanceAttributes()) ? DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled; fVertexBufferStatus = (programInfo.geomProc().hasVertexAttributes()) ? DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled; #endif fDrawPipelineStatus = DrawPipelineStatus::kOk; fXferBarrierType = programInfo.pipeline().xferBarrierType(*this->gpu()->caps()); } void GrOpsRenderPass::setScissorRect(const SkIRect& scissor) { if (DrawPipelineStatus::kOk != fDrawPipelineStatus) { SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus); return; } SkASSERT(DynamicStateStatus::kDisabled != fScissorStatus); this->onSetScissorRect(scissor); SkDEBUGCODE(fScissorStatus = DynamicStateStatus::kConfigured); } void GrOpsRenderPass::bindTextures(const GrGeometryProcessor& geomProc, const GrSurfaceProxy* const geomProcTextures[], const GrPipeline& pipeline) { #ifdef SK_DEBUG SkASSERT((geomProc.numTextureSamplers() > 0) == SkToBool(geomProcTextures)); for (int i = 0; i < geomProc.numTextureSamplers(); ++i) { const auto& sampler = geomProc.textureSampler(i); const GrSurfaceProxy* proxy = geomProcTextures[i]; SkASSERT(proxy); SkASSERT(proxy->backendFormat() == sampler.backendFormat()); SkASSERT(proxy->backendFormat().textureType() == sampler.backendFormat().textureType()); const GrTexture* tex = proxy->peekTexture(); SkASSERT(tex); if (sampler.samplerState().mipmapped() == skgpu::Mipmapped::kYes && (tex->width() != 1 || tex->height() != 1)) { // There are some cases where we might be given a non-mipmapped texture with a mipmap // filter. See skbug.com/7094. SkASSERT(tex->mipmapped() != skgpu::Mipmapped::kYes || !tex->mipmapsAreDirty()); } } #endif if (DrawPipelineStatus::kOk != fDrawPipelineStatus) { SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus); return; } // Don't assert on fTextureBindingStatus. onBindTextures() just turns into a no-op when there // aren't any textures, and it's hard to tell from the GrPipeline whether there are any. For // many clients it is easier to just always call this method. if (!this->onBindTextures(geomProc, geomProcTextures, pipeline)) { fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind; return; } SkDEBUGCODE(fTextureBindingStatus = DynamicStateStatus::kConfigured); } void GrOpsRenderPass::bindBuffers(sk_sp indexBuffer, sk_sp instanceBuffer, sk_sp vertexBuffer, GrPrimitiveRestart primRestart) { if (DrawPipelineStatus::kOk != fDrawPipelineStatus) { SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus); return; } #ifdef SK_DEBUG if (indexBuffer) { fHasIndexBuffer = true; } SkASSERT((DynamicStateStatus::kDisabled == fInstanceBufferStatus) != SkToBool(instanceBuffer)); if (instanceBuffer) { fInstanceBufferStatus = DynamicStateStatus::kConfigured; } SkASSERT((DynamicStateStatus::kDisabled == fVertexBufferStatus) != SkToBool(vertexBuffer)); if (vertexBuffer) { fVertexBufferStatus = DynamicStateStatus::kConfigured; } if (GrPrimitiveRestart::kYes == primRestart) { SkASSERT(this->gpu()->caps()->usePrimitiveRestart()); } #endif this->onBindBuffers(std::move(indexBuffer), std::move(instanceBuffer), std::move(vertexBuffer), primRestart); } bool GrOpsRenderPass::prepareToDraw() { if (DrawPipelineStatus::kOk != fDrawPipelineStatus) { SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus); this->gpu()->stats()->incNumFailedDraws(); return false; } SkASSERT(DynamicStateStatus::kUninitialized != fScissorStatus); SkASSERT(DynamicStateStatus::kUninitialized != fTextureBindingStatus); if (kNone_GrXferBarrierType != fXferBarrierType) { this->gpu()->xferBarrier(fRenderTarget, fXferBarrierType); } return true; } void GrOpsRenderPass::draw(int vertexCount, int baseVertex) { if (!this->prepareToDraw()) { return; } SkASSERT(!fHasIndexBuffer); SkASSERT(DynamicStateStatus::kConfigured != fInstanceBufferStatus); SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); this->onDraw(vertexCount, baseVertex); } void GrOpsRenderPass::drawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue, uint16_t maxIndexValue, int baseVertex) { if (!this->prepareToDraw()) { return; } SkASSERT(fHasIndexBuffer); SkASSERT(DynamicStateStatus::kConfigured != fInstanceBufferStatus); SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); this->onDrawIndexed(indexCount, baseIndex, minIndexValue, maxIndexValue, baseVertex); } void GrOpsRenderPass::drawInstanced(int instanceCount, int baseInstance, int vertexCount, int baseVertex) { SkASSERT(this->gpu()->caps()->drawInstancedSupport()); if (!this->prepareToDraw()) { return; } SkASSERT(!fHasIndexBuffer); SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus); SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); this->onDrawInstanced(instanceCount, baseInstance, vertexCount, baseVertex); } void GrOpsRenderPass::drawIndexedInstanced(int indexCount, int baseIndex, int instanceCount, int baseInstance, int baseVertex) { SkASSERT(this->gpu()->caps()->drawInstancedSupport()); if (!this->prepareToDraw()) { return; } SkASSERT(fHasIndexBuffer); SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus); SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); this->onDrawIndexedInstanced(indexCount, baseIndex, instanceCount, baseInstance, baseVertex); } void GrOpsRenderPass::drawIndirect(const GrBuffer* drawIndirectBuffer, size_t bufferOffset, int drawCount) { SkASSERT(this->gpu()->caps()->drawInstancedSupport()); SkASSERT(drawIndirectBuffer->isCpuBuffer() || !static_cast(drawIndirectBuffer)->isMapped()); if (!this->prepareToDraw()) { return; } SkASSERT(!fHasIndexBuffer); SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus); SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); if (!this->gpu()->caps()->nativeDrawIndirectSupport()) { // Polyfill indirect draws with looping instanced calls. SkASSERT(drawIndirectBuffer->isCpuBuffer()); auto* cpuIndirectBuffer = static_cast(drawIndirectBuffer); auto* cmds = reinterpret_cast( cpuIndirectBuffer->data() + bufferOffset); for (int i = 0; i < drawCount; ++i) { auto [vertexCount, instanceCount, baseVertex, baseInstance] = cmds[i]; this->onDrawInstanced(instanceCount, baseInstance, vertexCount, baseVertex); } return; } this->onDrawIndirect(drawIndirectBuffer, bufferOffset, drawCount); } void GrOpsRenderPass::drawIndexedIndirect(const GrBuffer* drawIndirectBuffer, size_t bufferOffset, int drawCount) { SkASSERT(this->gpu()->caps()->drawInstancedSupport()); SkASSERT(drawIndirectBuffer->isCpuBuffer() || !static_cast(drawIndirectBuffer)->isMapped()); if (!this->prepareToDraw()) { return; } SkASSERT(fHasIndexBuffer); SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus); SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); if (!this->gpu()->caps()->nativeDrawIndirectSupport() || this->gpu()->caps()->nativeDrawIndexedIndirectIsBroken()) { // Polyfill indexedIndirect draws with looping indexedInstanced calls. SkASSERT(drawIndirectBuffer->isCpuBuffer()); auto* cpuIndirectBuffer = static_cast(drawIndirectBuffer); auto* cmds = reinterpret_cast( cpuIndirectBuffer->data() + bufferOffset); for (int i = 0; i < drawCount; ++i) { auto [indexCount, instanceCount, baseIndex, baseVertex, baseInstance] = cmds[i]; this->onDrawIndexedInstanced(indexCount, baseIndex, instanceCount, baseInstance, baseVertex); } return; } this->onDrawIndexedIndirect(drawIndirectBuffer, bufferOffset, drawCount); } void GrOpsRenderPass::drawIndexPattern(int patternIndexCount, int patternRepeatCount, int maxPatternRepetitionsInIndexBuffer, int patternVertexCount, int baseVertex) { int baseRepetition = 0; while (baseRepetition < patternRepeatCount) { int repeatCount = std::min(patternRepeatCount - baseRepetition, maxPatternRepetitionsInIndexBuffer); int drawIndexCount = repeatCount * patternIndexCount; // A patterned index buffer must contain indices in the range [0..vertexCount]. int minIndexValue = 0; int maxIndexValue = patternVertexCount * repeatCount - 1; this->drawIndexed(drawIndexCount, 0, minIndexValue, maxIndexValue, patternVertexCount * baseRepetition + baseVertex); baseRepetition += repeatCount; } }