| 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 |  | 
|---|