1 /*
2  * Copyright 2017 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/gl/GrGLOpsRenderPass.h"
9 
10 #include "src/gpu/GrProgramInfo.h"
11 #include "src/gpu/GrRenderTarget.h"
12 
13 #ifdef SK_DEBUG
14 #include "include/gpu/GrDirectContext.h"
15 #include "src/gpu/GrDirectContextPriv.h"
16 #endif
17 
18 #define GL_CALL(X) GR_GL_CALL(fGpu->glInterface(), X)
19 
set(GrRenderTarget * rt,const SkIRect & contentBounds,GrSurfaceOrigin origin,const LoadAndStoreInfo & colorInfo,const StencilLoadAndStoreInfo & stencilInfo)20 void GrGLOpsRenderPass::set(GrRenderTarget* rt, const SkIRect& contentBounds,
21                             GrSurfaceOrigin origin, const LoadAndStoreInfo& colorInfo,
22                             const StencilLoadAndStoreInfo& stencilInfo) {
23     SkASSERT(fGpu);
24     SkASSERT(!fRenderTarget);
25     SkASSERT(fGpu == rt->getContext()->priv().getGpu());
26 
27     this->INHERITED::set(rt, origin);
28     fContentBounds = contentBounds;
29     fColorLoadAndStoreInfo = colorInfo;
30     fStencilLoadAndStoreInfo = stencilInfo;
31 }
32 
onBegin()33 void GrGLOpsRenderPass::onBegin() {
34     fGpu->beginCommandBuffer(fRenderTarget, fContentBounds, fOrigin, fColorLoadAndStoreInfo,
35                              fStencilLoadAndStoreInfo);
36 }
37 
onEnd()38 void GrGLOpsRenderPass::onEnd() {
39     fGpu->endCommandBuffer(fRenderTarget, fColorLoadAndStoreInfo, fStencilLoadAndStoreInfo);
40 }
41 
onBindPipeline(const GrProgramInfo & programInfo,const SkRect & drawBounds)42 bool GrGLOpsRenderPass::onBindPipeline(const GrProgramInfo& programInfo,
43                                        const SkRect& drawBounds) {
44     fPrimitiveType = programInfo.primitiveType();
45     return fGpu->flushGLState(fRenderTarget, programInfo);
46 }
47 
onSetScissorRect(const SkIRect & scissor)48 void GrGLOpsRenderPass::onSetScissorRect(const SkIRect& scissor) {
49     fGpu->flushScissorRect(scissor, fRenderTarget->width(), fRenderTarget->height(), fOrigin);
50 }
51 
onBindTextures(const GrPrimitiveProcessor & primProc,const GrSurfaceProxy * const primProcTextures[],const GrPipeline & pipeline)52 bool GrGLOpsRenderPass::onBindTextures(const GrPrimitiveProcessor& primProc,
53                                        const GrSurfaceProxy* const primProcTextures[],
54                                        const GrPipeline& pipeline) {
55     GrGLProgram* program = fGpu->currentProgram();
56     SkASSERT(program);
57     program->bindTextures(primProc, primProcTextures, pipeline);
58     return true;
59 }
60 
onBindBuffers(sk_sp<const GrBuffer> indexBuffer,sk_sp<const GrBuffer> instanceBuffer,sk_sp<const GrBuffer> vertexBuffer,GrPrimitiveRestart primitiveRestart)61 void GrGLOpsRenderPass::onBindBuffers(sk_sp<const GrBuffer> indexBuffer,
62                                       sk_sp<const GrBuffer> instanceBuffer,
63                                       sk_sp<const GrBuffer> vertexBuffer,
64                                       GrPrimitiveRestart primitiveRestart) {
65     SkASSERT((primitiveRestart == GrPrimitiveRestart::kNo) || indexBuffer);
66     GrGLProgram* program = fGpu->currentProgram();
67     SkASSERT(program);
68 
69 #ifdef SK_DEBUG
70     fDidBindInstanceBuffer = false;
71     fDidBindVertexBuffer = false;
72 #endif
73 
74     int numAttribs = program->numVertexAttributes() + program->numInstanceAttributes();
75     fAttribArrayState = fGpu->bindInternalVertexArray(indexBuffer.get(), numAttribs,
76                                                       primitiveRestart);
77 
78     if (indexBuffer) {
79         if (indexBuffer->isCpuBuffer()) {
80             auto* cpuIndexBuffer = static_cast<const GrCpuBuffer*>(indexBuffer.get());
81             fIndexPointer = reinterpret_cast<const uint16_t*>(cpuIndexBuffer->data());
82         } else {
83             fIndexPointer = nullptr;
84         }
85     }
86 
87     // If this platform does not support baseInstance, defer binding of the instance buffer.
88     if (fGpu->glCaps().baseVertexBaseInstanceSupport()) {
89         this->bindInstanceBuffer(instanceBuffer.get(), 0);
90         SkDEBUGCODE(fDidBindInstanceBuffer = true;)
91     }
92     fActiveInstanceBuffer = std::move(instanceBuffer);
93 
94     // We differ binding the vertex buffer for one of two situations:
95     // 1) This platform does not support baseVertex with indexed draws.
96     // 2) There is a driver bug affecting glDrawArrays.
97     if ((indexBuffer && fGpu->glCaps().baseVertexBaseInstanceSupport()) ||
98         (!indexBuffer && !fGpu->glCaps().drawArraysBaseVertexIsBroken())) {
99             this->bindVertexBuffer(vertexBuffer.get(), 0);
100             SkDEBUGCODE(fDidBindVertexBuffer = true;)
101     }
102     fActiveVertexBuffer = std::move(vertexBuffer);
103     fActiveIndexBuffer = std::move(indexBuffer);
104 }
105 
bindInstanceBuffer(const GrBuffer * instanceBuffer,int baseInstance)106 void GrGLOpsRenderPass::bindInstanceBuffer(const GrBuffer* instanceBuffer, int baseInstance) {
107     GrGLProgram* program = fGpu->currentProgram();
108     SkASSERT(program);
109     if (int instanceStride = program->instanceStride()) {
110         SkASSERT(instanceBuffer);
111         SkASSERT(instanceBuffer->isCpuBuffer() ||
112                  !static_cast<const GrGpuBuffer*>(instanceBuffer)->isMapped());
113         size_t bufferOffset = baseInstance * static_cast<size_t>(instanceStride);
114         int attribIdx = program->numVertexAttributes();
115         for (int i = 0; i < program->numInstanceAttributes(); ++i, ++attribIdx) {
116             const auto& attrib = program->instanceAttribute(i);
117             static constexpr int kDivisor = 1;
118             fAttribArrayState->set(fGpu, attrib.fLocation, instanceBuffer, attrib.fCPUType,
119                                    attrib.fGPUType, instanceStride, bufferOffset + attrib.fOffset,
120                                    kDivisor);
121         }
122     }
123 }
124 
bindVertexBuffer(const GrBuffer * vertexBuffer,int baseVertex)125 void GrGLOpsRenderPass::bindVertexBuffer(const GrBuffer* vertexBuffer, int baseVertex) {
126     GrGLProgram* program = fGpu->currentProgram();
127     SkASSERT(program);
128     if (int vertexStride = program->vertexStride()) {
129         SkASSERT(vertexBuffer);
130         SkASSERT(vertexBuffer->isCpuBuffer() ||
131                  !static_cast<const GrGpuBuffer*>(vertexBuffer)->isMapped());
132         size_t bufferOffset = baseVertex * static_cast<size_t>(vertexStride);
133         for (int i = 0; i < program->numVertexAttributes(); ++i) {
134             const auto& attrib = program->vertexAttribute(i);
135             static constexpr int kDivisor = 0;
136             fAttribArrayState->set(fGpu, attrib.fLocation, vertexBuffer, attrib.fCPUType,
137                                    attrib.fGPUType, vertexStride, bufferOffset + attrib.fOffset,
138                                    kDivisor);
139         }
140     }
141 }
142 
onDraw(int vertexCount,int baseVertex)143 void GrGLOpsRenderPass::onDraw(int vertexCount, int baseVertex) {
144     SkASSERT(fDidBindVertexBuffer || fGpu->glCaps().drawArraysBaseVertexIsBroken());
145     GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
146     if (fGpu->glCaps().drawArraysBaseVertexIsBroken()) {
147         this->bindVertexBuffer(fActiveVertexBuffer.get(), baseVertex);
148         baseVertex = 0;
149     }
150     GL_CALL(DrawArrays(glPrimType, baseVertex, vertexCount));
151 }
152 
onDrawIndexed(int indexCount,int baseIndex,uint16_t minIndexValue,uint16_t maxIndexValue,int baseVertex)153 void GrGLOpsRenderPass::onDrawIndexed(int indexCount, int baseIndex, uint16_t minIndexValue,
154                                       uint16_t maxIndexValue, int baseVertex) {
155     GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
156     if (fGpu->glCaps().baseVertexBaseInstanceSupport()) {
157         SkASSERT(fGpu->glCaps().drawInstancedSupport());
158         SkASSERT(fDidBindVertexBuffer);
159         if (baseVertex != 0) {
160             GL_CALL(DrawElementsInstancedBaseVertexBaseInstance(
161                     glPrimType, indexCount, GR_GL_UNSIGNED_SHORT,
162                     this->offsetForBaseIndex(baseIndex), 1, baseVertex, 0));
163             return;
164         }
165     } else {
166         this->bindVertexBuffer(fActiveVertexBuffer.get(), baseVertex);
167     }
168 
169     if (fGpu->glCaps().drawRangeElementsSupport()) {
170         GL_CALL(DrawRangeElements(glPrimType, minIndexValue, maxIndexValue, indexCount,
171                                   GR_GL_UNSIGNED_SHORT, this->offsetForBaseIndex(baseIndex)));
172     } else {
173         GL_CALL(DrawElements(glPrimType, indexCount, GR_GL_UNSIGNED_SHORT,
174                              this->offsetForBaseIndex(baseIndex)));
175     }
176 }
177 
onDrawInstanced(int instanceCount,int baseInstance,int vertexCount,int baseVertex)178 void GrGLOpsRenderPass::onDrawInstanced(int instanceCount, int baseInstance, int vertexCount,
179                                         int baseVertex) {
180     SkASSERT(fDidBindVertexBuffer || fGpu->glCaps().drawArraysBaseVertexIsBroken());
181     if (fGpu->glCaps().drawArraysBaseVertexIsBroken()) {
182         // We weren't able to bind the vertex buffer during onBindBuffers because of a driver bug
183         // affecting glDrawArrays.
184         this->bindVertexBuffer(fActiveVertexBuffer.get(), 0);
185     }
186     int maxInstances = fGpu->glCaps().maxInstancesPerDrawWithoutCrashing(instanceCount);
187     for (int i = 0; i < instanceCount; i += maxInstances) {
188         GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
189         int instanceCountForDraw = std::min(instanceCount - i, maxInstances);
190         int baseInstanceForDraw = baseInstance + i;
191         if (fGpu->glCaps().baseVertexBaseInstanceSupport()) {
192             SkASSERT(fDidBindInstanceBuffer);
193             GL_CALL(DrawArraysInstancedBaseInstance(glPrimType, baseVertex, vertexCount,
194                                                     instanceCountForDraw, baseInstanceForDraw));
195         } else {
196             this->bindInstanceBuffer(fActiveInstanceBuffer.get(), baseInstanceForDraw);
197             GL_CALL(DrawArraysInstanced(glPrimType, baseVertex, vertexCount, instanceCountForDraw));
198         }
199     }
200 }
201 
onDrawIndexedInstanced(int indexCount,int baseIndex,int instanceCount,int baseInstance,int baseVertex)202 void GrGLOpsRenderPass::onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount,
203                                                int baseInstance, int baseVertex) {
204     int maxInstances = fGpu->glCaps().maxInstancesPerDrawWithoutCrashing(instanceCount);
205     for (int i = 0; i < instanceCount; i += maxInstances) {
206         GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
207         int instanceCountForDraw = std::min(instanceCount - i, maxInstances);
208         int baseInstanceForDraw = baseInstance + i;
209         if (fGpu->glCaps().baseVertexBaseInstanceSupport()) {
210             SkASSERT(fDidBindInstanceBuffer);
211             SkASSERT(fDidBindVertexBuffer);
212             GL_CALL(DrawElementsInstancedBaseVertexBaseInstance(
213                     glPrimType, indexCount, GR_GL_UNSIGNED_SHORT,
214                     this->offsetForBaseIndex(baseIndex), instanceCountForDraw, baseVertex,
215                     baseInstanceForDraw));
216         } else {
217             this->bindInstanceBuffer(fActiveInstanceBuffer.get(), baseInstanceForDraw);
218             this->bindVertexBuffer(fActiveVertexBuffer.get(), baseVertex);
219             GL_CALL(DrawElementsInstanced(glPrimType, indexCount, GR_GL_UNSIGNED_SHORT,
220                                         this->offsetForBaseIndex(baseIndex), instanceCountForDraw));
221         }
222     }
223 }
224 
buffer_offset_to_gl_address(const GrBuffer * drawIndirectBuffer,size_t offset)225 static const void* buffer_offset_to_gl_address(const GrBuffer* drawIndirectBuffer, size_t offset) {
226     if (drawIndirectBuffer->isCpuBuffer()) {
227         return static_cast<const GrCpuBuffer*>(drawIndirectBuffer)->data() + offset;
228     } else {
229         return (offset) ? reinterpret_cast<const void*>(offset) : nullptr;
230     }
231 }
232 
onDrawIndirect(const GrBuffer * drawIndirectBuffer,size_t offset,int drawCount)233 void GrGLOpsRenderPass::onDrawIndirect(const GrBuffer* drawIndirectBuffer, size_t offset,
234                                        int drawCount) {
235     using MultiDrawType = GrGLCaps::MultiDrawType;
236 
237     SkASSERT(fGpu->caps()->nativeDrawIndirectSupport());
238     SkASSERT(fGpu->glCaps().baseVertexBaseInstanceSupport());
239     SkASSERT(fDidBindVertexBuffer || fGpu->glCaps().drawArraysBaseVertexIsBroken());
240 
241     if (fGpu->glCaps().drawArraysBaseVertexIsBroken()) {
242         // We weren't able to bind the vertex buffer during onBindBuffers because of a driver bug
243         // affecting glDrawArrays.
244         this->bindVertexBuffer(fActiveVertexBuffer.get(), 0);
245     }
246 
247     if (fGpu->glCaps().multiDrawType() == MultiDrawType::kANGLEOrWebGL) {
248         // ANGLE and WebGL don't support glDrawElementsIndirect. We draw everything as a multi draw.
249         this->multiDrawArraysANGLEOrWebGL(drawIndirectBuffer, offset, drawCount);
250         return;
251     }
252 
253     fGpu->bindBuffer(GrGpuBufferType::kDrawIndirect, drawIndirectBuffer);
254 
255     if (drawCount > 1 && fGpu->glCaps().multiDrawType() == MultiDrawType::kMultiDrawIndirect) {
256         GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
257         GL_CALL(MultiDrawArraysIndirect(glPrimType,
258                                         buffer_offset_to_gl_address(drawIndirectBuffer, offset),
259                                         drawCount, sizeof(GrDrawIndirectCommand)));
260         return;
261     }
262 
263     for (int i = 0; i < drawCount; ++i) {
264         GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
265         GL_CALL(DrawArraysIndirect(glPrimType,
266                                    buffer_offset_to_gl_address(drawIndirectBuffer, offset)));
267         offset += sizeof(GrDrawIndirectCommand);
268     }
269 }
270 
multiDrawArraysANGLEOrWebGL(const GrBuffer * drawIndirectBuffer,size_t offset,int drawCount)271 void GrGLOpsRenderPass::multiDrawArraysANGLEOrWebGL(const GrBuffer* drawIndirectBuffer,
272                                                     size_t offset, int drawCount) {
273     SkASSERT(fGpu->glCaps().multiDrawType() == GrGLCaps::MultiDrawType::kANGLEOrWebGL);
274     SkASSERT(drawIndirectBuffer->isCpuBuffer());
275 
276     constexpr static int kMaxDrawCountPerBatch = 128;
277     GrGLint fFirsts[kMaxDrawCountPerBatch];
278     GrGLsizei fCounts[kMaxDrawCountPerBatch];
279     GrGLsizei fInstanceCounts[kMaxDrawCountPerBatch];
280     GrGLuint fBaseInstances[kMaxDrawCountPerBatch];
281 
282     GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
283     auto* cpuBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
284     auto* cmds = reinterpret_cast<const GrDrawIndirectCommand*>(cpuBuffer->data() + offset);
285 
286     while (drawCount) {
287         int countInBatch = std::min(drawCount, kMaxDrawCountPerBatch);
288         for (int i = 0; i < countInBatch; ++i) {
289             const auto& cmd = cmds[i];
290             fFirsts[i] = cmd.fBaseVertex;
291             fCounts[i] = cmd.fVertexCount;
292             fInstanceCounts[i] = cmd.fInstanceCount;
293             fBaseInstances[i] = cmd.fBaseInstance;
294         }
295         if (countInBatch == 1) {
296             GL_CALL(DrawArraysInstancedBaseInstance(glPrimType, fFirsts[0], fCounts[0],
297                                                     fInstanceCounts[0], fBaseInstances[0]));
298         } else {
299             GL_CALL(MultiDrawArraysInstancedBaseInstance(glPrimType, fFirsts, fCounts,
300                                                          fInstanceCounts, fBaseInstances,
301                                                          countInBatch));
302         }
303         drawCount -= countInBatch;
304         cmds += countInBatch;
305     }
306 }
307 
onDrawIndexedIndirect(const GrBuffer * drawIndirectBuffer,size_t offset,int drawCount)308 void GrGLOpsRenderPass::onDrawIndexedIndirect(const GrBuffer* drawIndirectBuffer, size_t offset,
309                                               int drawCount) {
310     using MultiDrawType = GrGLCaps::MultiDrawType;
311 
312     SkASSERT(fGpu->caps()->nativeDrawIndirectSupport());
313     SkASSERT(!fGpu->caps()->nativeDrawIndexedIndirectIsBroken());
314     SkASSERT(fGpu->glCaps().baseVertexBaseInstanceSupport());
315     // The vertex buffer should have already gotten bound (as opposed us stashing it away during
316     // onBindBuffers and not expecting to bind it until this point).
317     SkASSERT(fDidBindVertexBuffer);
318 
319     if (fGpu->glCaps().multiDrawType() == MultiDrawType::kANGLEOrWebGL) {
320         // ANGLE and WebGL don't support glDrawElementsIndirect. We draw everything as a multi draw.
321         this->multiDrawElementsANGLEOrWebGL(drawIndirectBuffer, offset, drawCount);
322         return;
323     }
324 
325     fGpu->bindBuffer(GrGpuBufferType::kDrawIndirect, drawIndirectBuffer);
326 
327     if (drawCount > 1 && fGpu->glCaps().multiDrawType() == MultiDrawType::kMultiDrawIndirect) {
328         GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
329         GL_CALL(MultiDrawElementsIndirect(glPrimType, GR_GL_UNSIGNED_SHORT,
330                                           buffer_offset_to_gl_address(drawIndirectBuffer, offset),
331                                           drawCount, sizeof(GrDrawIndexedIndirectCommand)));
332         return;
333     }
334 
335     for (int i = 0; i < drawCount; ++i) {
336         GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
337         GL_CALL(DrawElementsIndirect(glPrimType, GR_GL_UNSIGNED_SHORT,
338                                      buffer_offset_to_gl_address(drawIndirectBuffer, offset)));
339         offset += sizeof(GrDrawIndexedIndirectCommand);
340     }
341 }
342 
multiDrawElementsANGLEOrWebGL(const GrBuffer * drawIndirectBuffer,size_t offset,int drawCount)343 void GrGLOpsRenderPass::multiDrawElementsANGLEOrWebGL(const GrBuffer* drawIndirectBuffer,
344                                                       size_t offset, int drawCount) {
345     SkASSERT(fGpu->glCaps().multiDrawType() == GrGLCaps::MultiDrawType::kANGLEOrWebGL);
346     SkASSERT(drawIndirectBuffer->isCpuBuffer());
347 
348     constexpr static int kMaxDrawCountPerBatch = 128;
349     GrGLint fCounts[kMaxDrawCountPerBatch];
350     const void* fIndices[kMaxDrawCountPerBatch];
351     GrGLsizei fInstanceCounts[kMaxDrawCountPerBatch];
352     GrGLint fBaseVertices[kMaxDrawCountPerBatch];
353     GrGLuint fBaseInstances[kMaxDrawCountPerBatch];
354 
355     GrGLenum glPrimType = fGpu->prepareToDraw(fPrimitiveType);
356     auto* cpuBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
357     auto* cmds = reinterpret_cast<const GrDrawIndexedIndirectCommand*>(cpuBuffer->data() + offset);
358 
359     while (drawCount) {
360         int countInBatch = std::min(drawCount, kMaxDrawCountPerBatch);
361         for (int i = 0; i < countInBatch; ++i) {
362             const auto& cmd = cmds[i];
363             fCounts[i] = cmd.fIndexCount;
364             fIndices[i] = this->offsetForBaseIndex(cmd.fBaseIndex);
365             fInstanceCounts[i] = cmd.fInstanceCount;
366             fBaseVertices[i] = cmd.fBaseVertex;
367             fBaseInstances[i] = cmd.fBaseInstance;
368         }
369         if (countInBatch == 1) {
370             GL_CALL(DrawElementsInstancedBaseVertexBaseInstance(glPrimType, fCounts[0],
371                                                                 GR_GL_UNSIGNED_SHORT, fIndices[0],
372                                                                 fInstanceCounts[0],
373                                                                 fBaseVertices[0],
374                                                                 fBaseInstances[0]));
375         } else {
376             GL_CALL(MultiDrawElementsInstancedBaseVertexBaseInstance(glPrimType, fCounts,
377                                                                      GR_GL_UNSIGNED_SHORT, fIndices,
378                                                                      fInstanceCounts, fBaseVertices,
379                                                                      fBaseInstances, countInBatch));
380         }
381         drawCount -= countInBatch;
382         cmds += countInBatch;
383     }
384 }
385 
onClear(const GrScissorState & scissor,const SkPMColor4f & color)386 void GrGLOpsRenderPass::onClear(const GrScissorState& scissor, const SkPMColor4f& color) {
387     fGpu->clear(scissor, color, fRenderTarget, fOrigin);
388 }
389 
onClearStencilClip(const GrScissorState & scissor,bool insideStencilMask)390 void GrGLOpsRenderPass::onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) {
391     fGpu->clearStencilClip(scissor, insideStencilMask, fRenderTarget, fOrigin);
392 }
393