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
21void 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
33void GrOpsRenderPass::end() {
34 this->onEnd();
35 this->resetActiveBuffers();
36}
37
38void 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
48void 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
56void GrOpsRenderPass::executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable) {
57 fDrawPipelineStatus = DrawPipelineStatus::kNotConfigured;
58 this->onExecuteDrawable(std::move(drawable));
59}
60
61void 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
136void 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
146void 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
185void 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
218bool 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
233void 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
243void 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
254void 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
266void 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
278void 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
305void 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
333void 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