1 | /* |
2 | * Copyright 2016 Google Inc. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #include "src/gpu/GrOpsRenderPass.h" |
9 | |
10 | #include "include/core/SkRect.h" |
11 | #include "src/gpu/GrCaps.h" |
12 | #include "src/gpu/GrCpuBuffer.h" |
13 | #include "src/gpu/GrGpu.h" |
14 | #include "src/gpu/GrPrimitiveProcessor.h" |
15 | #include "src/gpu/GrProgramInfo.h" |
16 | #include "src/gpu/GrRenderTarget.h" |
17 | #include "src/gpu/GrScissorState.h" |
18 | #include "src/gpu/GrSimpleMesh.h" |
19 | #include "src/gpu/GrTexture.h" |
20 | |
21 | void GrOpsRenderPass::begin() { |
22 | fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured; |
23 | #ifdef SK_DEBUG |
24 | fScissorStatus = DynamicStateStatus::kDisabled; |
25 | fTextureBindingStatus = DynamicStateStatus::kDisabled; |
26 | fHasIndexBuffer = false; |
27 | fInstanceBufferStatus = DynamicStateStatus::kDisabled; |
28 | fVertexBufferStatus = DynamicStateStatus::kDisabled; |
29 | #endif |
30 | this->onBegin(); |
31 | } |
32 | |
33 | void GrOpsRenderPass::end() { |
34 | this->onEnd(); |
35 | this->resetActiveBuffers(); |
36 | } |
37 | |
38 | void GrOpsRenderPass::clear(const GrScissorState& scissor, const SkPMColor4f& color) { |
39 | SkASSERT(fRenderTarget); |
40 | // A clear at this level will always be a true clear, so make sure clears were not supposed to |
41 | // be redirected to draws instead |
42 | SkASSERT(!this->gpu()->caps()->performColorClearsAsDraws()); |
43 | SkASSERT(!scissor.enabled() || !this->gpu()->caps()->performPartialClearsAsDraws()); |
44 | fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured; |
45 | this->onClear(scissor, color); |
46 | } |
47 | |
48 | void GrOpsRenderPass::clearStencilClip(const GrScissorState& scissor, bool insideStencilMask) { |
49 | // As above, make sure the stencil clear wasn't supposed to be a draw rect with stencil settings |
50 | SkASSERT(!this->gpu()->caps()->performStencilClearsAsDraws()); |
51 | SkASSERT(!scissor.enabled() || !this->gpu()->caps()->performPartialClearsAsDraws()); |
52 | fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured; |
53 | this->onClearStencilClip(scissor, insideStencilMask); |
54 | } |
55 | |
56 | void GrOpsRenderPass::executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable) { |
57 | fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured; |
58 | this->onExecuteDrawable(std::move(drawable)); |
59 | } |
60 | |
61 | void GrOpsRenderPass::bindPipeline(const GrProgramInfo& programInfo, const SkRect& drawBounds) { |
62 | #ifdef SK_DEBUG |
63 | // Both the 'programInfo' and this renderPass have an origin. Since they come from the same |
64 | // place (i.e., the target renderTargetProxy) they had best agree. |
65 | SkASSERT(programInfo.origin() == fOrigin); |
66 | if (programInfo.primProc().hasInstanceAttributes()) { |
67 | SkASSERT(this->gpu()->caps()->drawInstancedSupport()); |
68 | } |
69 | if (programInfo.pipeline().usesConservativeRaster()) { |
70 | SkASSERT(this->gpu()->caps()->conservativeRasterSupport()); |
71 | // Conservative raster, by default, only supports triangles. Implementations can |
72 | // optionally indicate that they also support points and lines, but we don't currently |
73 | // query or track that info. |
74 | SkASSERT(GrIsPrimTypeTris(programInfo.primitiveType())); |
75 | } |
76 | if (programInfo.pipeline().isWireframe()) { |
77 | SkASSERT(this->gpu()->caps()->wireframeSupport()); |
78 | } |
79 | if (this->gpu()->caps()->twoSidedStencilRefsAndMasksMustMatch() && |
80 | programInfo.pipeline().isStencilEnabled()) { |
81 | const GrUserStencilSettings* stencil = programInfo.pipeline().getUserStencil(); |
82 | if (stencil->isTwoSided(programInfo.pipeline().hasStencilClip())) { |
83 | SkASSERT(stencil->fCCWFace.fRef == stencil->fCWFace.fRef); |
84 | SkASSERT(stencil->fCCWFace.fTestMask == stencil->fCWFace.fTestMask); |
85 | SkASSERT(stencil->fCCWFace.fWriteMask == stencil->fCWFace.fWriteMask); |
86 | } |
87 | } |
88 | if (GrPrimitiveType::kPatches == programInfo.primitiveType()) { |
89 | SkASSERT(this->gpu()->caps()->shaderCaps()->tessellationSupport()); |
90 | } |
91 | programInfo.checkAllInstantiated(); |
92 | programInfo.checkMSAAAndMIPSAreResolved(); |
93 | #endif |
94 | |
95 | this->resetActiveBuffers(); |
96 | |
97 | if (programInfo.primProc().numVertexAttributes() > this->gpu()->caps()->maxVertexAttributes()) { |
98 | fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind; |
99 | return; |
100 | } |
101 | |
102 | if (!this->onBindPipeline(programInfo, drawBounds)) { |
103 | fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind; |
104 | return; |
105 | } |
106 | |
107 | #ifdef SK_DEBUG |
108 | GrProcessor::CustomFeatures processorFeatures = programInfo.requestedFeatures(); |
109 | if (GrProcessor::CustomFeatures::kSampleLocations & processorFeatures) { |
110 | // Verify we always have the same sample pattern key, regardless of graphics state. |
111 | SkASSERT(this->gpu()->findOrAssignSamplePatternKey(fRenderTarget) == |
112 | fRenderTarget->getSamplePatternKey()); |
113 | } |
114 | fScissorStatus = (programInfo.pipeline().isScissorTestEnabled()) ? |
115 | DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled; |
116 | bool hasTextures = (programInfo.primProc().numTextureSamplers() > 0); |
117 | if (!hasTextures) { |
118 | programInfo.pipeline().visitProxies([&hasTextures](GrSurfaceProxy*, GrMipmapped) { |
119 | hasTextures = true; |
120 | }); |
121 | } |
122 | fTextureBindingStatus = (hasTextures) ? |
123 | DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled; |
124 | fHasIndexBuffer = false; |
125 | fInstanceBufferStatus = (programInfo.primProc().hasInstanceAttributes()) ? |
126 | DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled; |
127 | fVertexBufferStatus = (programInfo.primProc().hasVertexAttributes()) ? |
128 | DynamicStateStatus::kUninitialized : DynamicStateStatus::kDisabled; |
129 | #endif |
130 | |
131 | fDrawPipelineStatus = DrawPipelineStatus::kOk; |
132 | fXferBarrierType = programInfo.pipeline().xferBarrierType(fRenderTarget->asTexture(), |
133 | *this->gpu()->caps()); |
134 | } |
135 | |
136 | void GrOpsRenderPass::setScissorRect(const SkIRect& scissor) { |
137 | if (DrawPipelineStatus::kOk != fDrawPipelineStatus) { |
138 | SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus); |
139 | return; |
140 | } |
141 | SkASSERT(DynamicStateStatus::kDisabled != fScissorStatus); |
142 | this->onSetScissorRect(scissor); |
143 | SkDEBUGCODE(fScissorStatus = DynamicStateStatus::kConfigured); |
144 | } |
145 | |
146 | void GrOpsRenderPass::bindTextures(const GrPrimitiveProcessor& primProc, |
147 | const GrSurfaceProxy* const primProcTextures[], |
148 | const GrPipeline& pipeline) { |
149 | #ifdef SK_DEBUG |
150 | SkASSERT((primProc.numTextureSamplers() > 0) == SkToBool(primProcTextures)); |
151 | for (int i = 0; i < primProc.numTextureSamplers(); ++i) { |
152 | const auto& sampler = primProc.textureSampler(i); |
153 | const GrSurfaceProxy* proxy = primProcTextures[i]; |
154 | SkASSERT(proxy); |
155 | SkASSERT(proxy->backendFormat() == sampler.backendFormat()); |
156 | SkASSERT(proxy->backendFormat().textureType() == sampler.backendFormat().textureType()); |
157 | |
158 | const GrTexture* tex = proxy->peekTexture(); |
159 | SkASSERT(tex); |
160 | if (sampler.samplerState().mipmapped() == GrMipmapped::kYes && |
161 | (tex->width() != 1 || tex->height() != 1)) { |
162 | // There are some cases where we might be given a non-mipmapped texture with a mipmap |
163 | // filter. See skbug.com/7094. |
164 | SkASSERT(tex->mipmapped() != GrMipmapped::kYes || !tex->mipmapsAreDirty()); |
165 | } |
166 | } |
167 | #endif |
168 | |
169 | if (DrawPipelineStatus::kOk != fDrawPipelineStatus) { |
170 | SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus); |
171 | return; |
172 | } |
173 | |
174 | // Don't assert on fTextureBindingStatus. onBindTextures() just turns into a no-op when there |
175 | // aren't any textures, and it's hard to tell from the GrPipeline whether there are any. For |
176 | // many clients it is easier to just always call this method. |
177 | if (!this->onBindTextures(primProc, primProcTextures, pipeline)) { |
178 | fDrawPipelineStatus = DrawPipelineStatus::kFailedToBind; |
179 | return; |
180 | } |
181 | |
182 | SkDEBUGCODE(fTextureBindingStatus = DynamicStateStatus::kConfigured); |
183 | } |
184 | |
185 | void GrOpsRenderPass::bindBuffers(sk_sp<const GrBuffer> indexBuffer, |
186 | sk_sp<const GrBuffer> instanceBuffer, |
187 | sk_sp<const GrBuffer> vertexBuffer, |
188 | GrPrimitiveRestart primRestart) { |
189 | if (DrawPipelineStatus::kOk != fDrawPipelineStatus) { |
190 | SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus); |
191 | return; |
192 | } |
193 | |
194 | #ifdef SK_DEBUG |
195 | if (indexBuffer) { |
196 | fHasIndexBuffer = true; |
197 | } |
198 | |
199 | SkASSERT((DynamicStateStatus::kDisabled == fInstanceBufferStatus) != SkToBool(instanceBuffer)); |
200 | if (instanceBuffer) { |
201 | fInstanceBufferStatus = DynamicStateStatus::kConfigured; |
202 | } |
203 | |
204 | SkASSERT((DynamicStateStatus::kDisabled == fVertexBufferStatus) != SkToBool(vertexBuffer)); |
205 | if (vertexBuffer) { |
206 | fVertexBufferStatus = DynamicStateStatus::kConfigured; |
207 | } |
208 | |
209 | if (GrPrimitiveRestart::kYes == primRestart) { |
210 | SkASSERT(this->gpu()->caps()->usePrimitiveRestart()); |
211 | } |
212 | #endif |
213 | |
214 | this->onBindBuffers(std::move(indexBuffer), std::move(instanceBuffer), std::move(vertexBuffer), |
215 | primRestart); |
216 | } |
217 | |
218 | bool GrOpsRenderPass::prepareToDraw() { |
219 | if (DrawPipelineStatus::kOk != fDrawPipelineStatus) { |
220 | SkASSERT(DrawPipelineStatus::kNotConfigured != fDrawPipelineStatus); |
221 | this->gpu()->stats()->incNumFailedDraws(); |
222 | return false; |
223 | } |
224 | SkASSERT(DynamicStateStatus::kUninitialized != fScissorStatus); |
225 | SkASSERT(DynamicStateStatus::kUninitialized != fTextureBindingStatus); |
226 | |
227 | if (kNone_GrXferBarrierType != fXferBarrierType) { |
228 | this->gpu()->xferBarrier(fRenderTarget, fXferBarrierType); |
229 | } |
230 | return true; |
231 | } |
232 | |
233 | void GrOpsRenderPass::draw(int vertexCount, int baseVertex) { |
234 | if (!this->prepareToDraw()) { |
235 | return; |
236 | } |
237 | SkASSERT(!fHasIndexBuffer); |
238 | SkASSERT(DynamicStateStatus::kConfigured != fInstanceBufferStatus); |
239 | SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); |
240 | this->onDraw(vertexCount, baseVertex); |
241 | } |
242 | |
243 | void GrOpsRenderPass::drawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue, |
244 | uint16_t maxIndexValue, int baseVertex) { |
245 | if (!this->prepareToDraw()) { |
246 | return; |
247 | } |
248 | SkASSERT(fHasIndexBuffer); |
249 | SkASSERT(DynamicStateStatus::kConfigured != fInstanceBufferStatus); |
250 | SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); |
251 | this->onDrawIndexed(indexCount, baseIndex, minIndexValue, maxIndexValue, baseVertex); |
252 | } |
253 | |
254 | void GrOpsRenderPass::drawInstanced(int instanceCount, int baseInstance, int vertexCount, |
255 | int baseVertex) { |
256 | SkASSERT(this->gpu()->caps()->drawInstancedSupport()); |
257 | if (!this->prepareToDraw()) { |
258 | return; |
259 | } |
260 | SkASSERT(!fHasIndexBuffer); |
261 | SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus); |
262 | SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); |
263 | this->onDrawInstanced(instanceCount, baseInstance, vertexCount, baseVertex); |
264 | } |
265 | |
266 | void GrOpsRenderPass::drawIndexedInstanced(int indexCount, int baseIndex, int instanceCount, |
267 | int baseInstance, int baseVertex) { |
268 | SkASSERT(this->gpu()->caps()->drawInstancedSupport()); |
269 | if (!this->prepareToDraw()) { |
270 | return; |
271 | } |
272 | SkASSERT(fHasIndexBuffer); |
273 | SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus); |
274 | SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); |
275 | this->onDrawIndexedInstanced(indexCount, baseIndex, instanceCount, baseInstance, baseVertex); |
276 | } |
277 | |
278 | void GrOpsRenderPass::drawIndirect(const GrBuffer* drawIndirectBuffer, size_t bufferOffset, |
279 | int drawCount) { |
280 | SkASSERT(this->gpu()->caps()->drawInstancedSupport()); |
281 | SkASSERT(drawIndirectBuffer->isCpuBuffer() || |
282 | !static_cast<const GrGpuBuffer*>(drawIndirectBuffer)->isMapped()); |
283 | if (!this->prepareToDraw()) { |
284 | return; |
285 | } |
286 | SkASSERT(!fHasIndexBuffer); |
287 | SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus); |
288 | SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); |
289 | if (!this->gpu()->caps()->nativeDrawIndirectSupport()) { |
290 | // Polyfill indirect draws with looping instanced calls. |
291 | SkASSERT(drawIndirectBuffer->isCpuBuffer()); |
292 | auto cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer); |
293 | auto cmd = reinterpret_cast<const GrDrawIndirectCommand*>( |
294 | cpuIndirectBuffer->data() + bufferOffset); |
295 | auto end = cmd + drawCount; |
296 | for (; cmd != end; ++cmd) { |
297 | this->onDrawInstanced(cmd->fInstanceCount, cmd->fBaseInstance, cmd->fVertexCount, |
298 | cmd->fBaseVertex); |
299 | } |
300 | return; |
301 | } |
302 | this->onDrawIndirect(drawIndirectBuffer, bufferOffset, drawCount); |
303 | } |
304 | |
305 | void GrOpsRenderPass::drawIndexedIndirect(const GrBuffer* drawIndirectBuffer, size_t bufferOffset, |
306 | int drawCount) { |
307 | SkASSERT(this->gpu()->caps()->drawInstancedSupport()); |
308 | SkASSERT(drawIndirectBuffer->isCpuBuffer() || |
309 | !static_cast<const GrGpuBuffer*>(drawIndirectBuffer)->isMapped()); |
310 | if (!this->prepareToDraw()) { |
311 | return; |
312 | } |
313 | SkASSERT(fHasIndexBuffer); |
314 | SkASSERT(DynamicStateStatus::kUninitialized != fInstanceBufferStatus); |
315 | SkASSERT(DynamicStateStatus::kUninitialized != fVertexBufferStatus); |
316 | if (!this->gpu()->caps()->nativeDrawIndirectSupport() || |
317 | this->gpu()->caps()->nativeDrawIndexedIndirectIsBroken()) { |
318 | // Polyfill indexedIndirect draws with looping indexedInstanced calls. |
319 | SkASSERT(drawIndirectBuffer->isCpuBuffer()); |
320 | auto cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer); |
321 | auto cmd = reinterpret_cast<const GrDrawIndexedIndirectCommand*>( |
322 | cpuIndirectBuffer->data() + bufferOffset); |
323 | auto end = cmd + drawCount; |
324 | for (; cmd != end; ++cmd) { |
325 | this->onDrawIndexedInstanced(cmd->fIndexCount, cmd->fBaseIndex, cmd->fInstanceCount, |
326 | cmd->fBaseInstance, cmd->fBaseVertex); |
327 | } |
328 | return; |
329 | } |
330 | this->onDrawIndexedIndirect(drawIndirectBuffer, bufferOffset, drawCount); |
331 | } |
332 | |
333 | void GrOpsRenderPass::drawIndexPattern(int patternIndexCount, int patternRepeatCount, |
334 | int maxPatternRepetitionsInIndexBuffer, |
335 | int patternVertexCount, int baseVertex) { |
336 | int baseRepetition = 0; |
337 | while (baseRepetition < patternRepeatCount) { |
338 | int repeatCount = std::min(patternRepeatCount - baseRepetition, |
339 | maxPatternRepetitionsInIndexBuffer); |
340 | int drawIndexCount = repeatCount * patternIndexCount; |
341 | // A patterned index buffer must contain indices in the range [0..vertexCount]. |
342 | int minIndexValue = 0; |
343 | int maxIndexValue = patternVertexCount * repeatCount - 1; |
344 | this->drawIndexed(drawIndexCount, 0, minIndexValue, maxIndexValue, |
345 | patternVertexCount * baseRepetition + baseVertex); |
346 | baseRepetition += repeatCount; |
347 | } |
348 | } |
349 | |