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