1// 2// Copyright 2020 Pixar 3// 4// Licensed under the Apache License, Version 2.0 (the "Apache License") 5// with the following modification; you may not use this file except in 6// compliance with the Apache License and the following modification to it: 7// Section 6. Trademarks. is deleted and replaced with: 8// 9// 6. Trademarks. This License does not grant permission to use the trade 10// names, trademarks, service marks, or product names of the Licensor 11// and its affiliates, except as required to comply with Section 4(c) of 12// the License and to reproduce the content of the NOTICE file. 13// 14// You may obtain a copy of the Apache License at 15// 16// http://www.apache.org/licenses/LICENSE-2.0 17// 18// Unless required by applicable law or agreed to in writing, software 19// distributed under the Apache License with the above modification is 20// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21// KIND, either express or implied. See the Apache License for the specific 22// language governing permissions and limitations under the Apache License. 23// 24#include "pxr/imaging/hgi/graphicsCmdsDesc.h" 25#include "pxr/imaging/hgiMetal/buffer.h" 26#include "pxr/imaging/hgiMetal/conversions.h" 27#include "pxr/imaging/hgiMetal/diagnostic.h" 28#include "pxr/imaging/hgiMetal/graphicsCmds.h" 29#include "pxr/imaging/hgiMetal/hgi.h" 30#include "pxr/imaging/hgiMetal/graphicsPipeline.h" 31#include "pxr/imaging/hgiMetal/resourceBindings.h" 32#include "pxr/imaging/hgiMetal/texture.h" 33 34#include "pxr/base/arch/defines.h" 35 36PXR_NAMESPACE_OPEN_SCOPE 37 38HgiMetalGraphicsCmds::HgiMetalGraphicsCmds( 39 HgiMetal* hgi, 40 HgiGraphicsCmdsDesc const& desc) 41 : HgiGraphicsCmds() 42 , _hgi(hgi) 43 , _renderPassDescriptor(nil) 44 , _encoder(nil) 45 , _descriptor(desc) 46 , _debugLabel(nil) 47 , _viewportSet(false) 48{ 49 TF_VERIFY(desc.colorTextures.size() == desc.colorAttachmentDescs.size()); 50 51 if (!desc.colorResolveTextures.empty() && 52 desc.colorResolveTextures.size() != 53 desc.colorTextures.size()) { 54 TF_CODING_ERROR("color and resolve texture count mismatch."); 55 return; 56 } 57 58 if (desc.depthResolveTexture && !desc.depthTexture) { 59 TF_CODING_ERROR("DepthResolve texture without depth texture."); 60 return; 61 } 62 63 _renderPassDescriptor = [[MTLRenderPassDescriptor alloc] init]; 64 65 // Color attachments 66 bool resolvingColor = !desc.colorResolveTextures.empty(); 67 bool hasClear = false; 68 for (size_t i=0; i<desc.colorAttachmentDescs.size(); i++) { 69 HgiAttachmentDesc const &hgiColorAttachment = 70 desc.colorAttachmentDescs[i]; 71 MTLRenderPassColorAttachmentDescriptor *metalColorAttachment = 72 _renderPassDescriptor.colorAttachments[i]; 73 74 if (hgiColorAttachment.loadOp == HgiAttachmentLoadOpClear) { 75 hasClear = true; 76 } 77 78 if (@available(macos 100.100, ios 8.0, *)) { 79 metalColorAttachment.loadAction = MTLLoadActionLoad; 80 } 81 else { 82 metalColorAttachment.loadAction = 83 HgiMetalConversions::GetAttachmentLoadOp( 84 hgiColorAttachment.loadOp); 85 } 86 87 metalColorAttachment.storeAction = 88 HgiMetalConversions::GetAttachmentStoreOp( 89 hgiColorAttachment.storeOp); 90 if (hgiColorAttachment.loadOp == HgiAttachmentLoadOpClear) { 91 GfVec4f const& clearColor = hgiColorAttachment.clearValue; 92 metalColorAttachment.clearColor = 93 MTLClearColorMake( 94 clearColor[0], clearColor[1], clearColor[2], clearColor[3]); 95 } 96 97 HgiMetalTexture *colorTexture = 98 static_cast<HgiMetalTexture*>(desc.colorTextures[i].Get()); 99 100 TF_VERIFY( 101 colorTexture->GetDescriptor().format == hgiColorAttachment.format); 102 metalColorAttachment.texture = colorTexture->GetTextureId(); 103 104 if (resolvingColor) { 105 HgiMetalTexture *resolveTexture = 106 static_cast<HgiMetalTexture*>(desc.colorResolveTextures[i].Get()); 107 108 metalColorAttachment.resolveTexture = 109 resolveTexture->GetTextureId(); 110 111 if (hgiColorAttachment.storeOp == HgiAttachmentStoreOpStore) { 112 metalColorAttachment.storeAction = 113 MTLStoreActionStoreAndMultisampleResolve; 114 } 115 else { 116 metalColorAttachment.storeAction = 117 MTLStoreActionMultisampleResolve; 118 } 119 } 120 } 121 122 // Depth attachment 123 if (desc.depthTexture) { 124 HgiAttachmentDesc const &hgiDepthAttachment = 125 desc.depthAttachmentDesc; 126 MTLRenderPassDepthAttachmentDescriptor *metalDepthAttachment = 127 _renderPassDescriptor.depthAttachment; 128 129 if (hgiDepthAttachment.loadOp == HgiAttachmentLoadOpClear) { 130 hasClear = true; 131 } 132 133 metalDepthAttachment.loadAction = 134 HgiMetalConversions::GetAttachmentLoadOp( 135 hgiDepthAttachment.loadOp); 136 metalDepthAttachment.storeAction = 137 HgiMetalConversions::GetAttachmentStoreOp( 138 hgiDepthAttachment.storeOp); 139 140 metalDepthAttachment.clearDepth = hgiDepthAttachment.clearValue[0]; 141 142 HgiMetalTexture *depthTexture = 143 static_cast<HgiMetalTexture*>(desc.depthTexture.Get()); 144 145 TF_VERIFY( 146 depthTexture->GetDescriptor().format == hgiDepthAttachment.format); 147 metalDepthAttachment.texture = depthTexture->GetTextureId(); 148 149 if (desc.depthResolveTexture) { 150 HgiMetalTexture *resolveTexture = 151 static_cast<HgiMetalTexture*>(desc.depthResolveTexture.Get()); 152 153 metalDepthAttachment.resolveTexture = 154 resolveTexture->GetTextureId(); 155 156 if (hgiDepthAttachment.storeOp == HgiAttachmentStoreOpStore) { 157 metalDepthAttachment.storeAction = 158 MTLStoreActionStoreAndMultisampleResolve; 159 } 160 else { 161 metalDepthAttachment.storeAction = 162 MTLStoreActionMultisampleResolve; 163 } 164 } 165 } 166 167 if (hasClear) { 168 _CreateEncoder(); 169 } 170 171} 172 173HgiMetalGraphicsCmds::~HgiMetalGraphicsCmds() 174{ 175 TF_VERIFY(_encoder == nil, "Encoder created, but never commited."); 176 177 [_renderPassDescriptor release]; 178 if (_debugLabel) { 179 [_debugLabel release]; 180 } 181} 182 183void 184HgiMetalGraphicsCmds::_CreateEncoder() 185{ 186 if (!_encoder) { 187 _encoder = [ 188 _hgi->GetPrimaryCommandBuffer(false) 189 renderCommandEncoderWithDescriptor:_renderPassDescriptor]; 190 191 if (_debugLabel) { 192 [_encoder setLabel:_debugLabel]; 193 } 194 if (_viewportSet) { 195 [_encoder setViewport:_viewport]; 196 } 197 } 198} 199 200void 201HgiMetalGraphicsCmds::SetViewport(GfVec4i const& vp) 202{ 203 double x = vp[0]; 204 double y = vp[1]; 205 double w = vp[2]; 206 double h = vp[3]; 207 if (_encoder) { 208 [_encoder setViewport:(MTLViewport){x, y, w, h, 0.0, 1.0}]; 209 } 210 else { 211 _viewport = (MTLViewport){x, y, w, h, 0.0, 1.0}; 212 } 213 _viewportSet = true; 214} 215 216void 217HgiMetalGraphicsCmds::SetScissor(GfVec4i const& sc) 218{ 219 uint32_t x = sc[0]; 220 uint32_t y = sc[1]; 221 uint32_t w = sc[2]; 222 uint32_t h = sc[3]; 223 224 _CreateEncoder(); 225 226 [_encoder setScissorRect:(MTLScissorRect){x, y, w, h}]; 227} 228 229void 230HgiMetalGraphicsCmds::BindPipeline(HgiGraphicsPipelineHandle pipeline) 231{ 232 _CreateEncoder(); 233 234 _primitiveType = pipeline->GetDescriptor().primitiveType; 235 if (HgiMetalGraphicsPipeline* p = 236 static_cast<HgiMetalGraphicsPipeline*>(pipeline.Get())) { 237 p->BindPipeline(_encoder); 238 } 239} 240 241void 242HgiMetalGraphicsCmds::BindResources(HgiResourceBindingsHandle r) 243{ 244 _CreateEncoder(); 245 246 if (HgiMetalResourceBindings* rb= 247 static_cast<HgiMetalResourceBindings*>(r.Get())) 248 { 249 rb->BindResources(_encoder); 250 } 251} 252 253void 254HgiMetalGraphicsCmds::SetConstantValues( 255 HgiGraphicsPipelineHandle pipeline, 256 HgiShaderStage stages, 257 uint32_t bindIndex, 258 uint32_t byteSize, 259 const void* data) 260{ 261 _CreateEncoder(); 262 263 if (stages & HgiShaderStageVertex) { 264 [_encoder setVertexBytes:data 265 length:byteSize 266 atIndex:bindIndex]; 267 } 268 if (stages & HgiShaderStageFragment) { 269 [_encoder setFragmentBytes:data 270 length:byteSize 271 atIndex:bindIndex]; 272 } 273} 274 275void 276HgiMetalGraphicsCmds::BindVertexBuffers( 277 uint32_t firstBinding, 278 HgiBufferHandleVector const& vertexBuffers, 279 std::vector<uint32_t> const& byteOffsets) 280{ 281 TF_VERIFY(byteOffsets.size() == vertexBuffers.size()); 282 TF_VERIFY(byteOffsets.size() == vertexBuffers.size()); 283 284 _CreateEncoder(); 285 286 for (size_t i=0; i<vertexBuffers.size(); i++) { 287 HgiBufferHandle bufHandle = vertexBuffers[i]; 288 HgiMetalBuffer* buf = static_cast<HgiMetalBuffer*>(bufHandle.Get()); 289 HgiBufferDesc const& desc = buf->GetDescriptor(); 290 291 TF_VERIFY(desc.usage & HgiBufferUsageVertex); 292 293 [_encoder setVertexBuffer:buf->GetBufferId() 294 offset:byteOffsets[i] 295 atIndex:firstBinding + i]; 296 } 297} 298 299void 300HgiMetalGraphicsCmds::Draw( 301 uint32_t vertexCount, 302 uint32_t firstVertex, 303 uint32_t instanceCount) 304{ 305 TF_VERIFY(instanceCount>0); 306 307 _CreateEncoder(); 308 309 MTLPrimitiveType type=HgiMetalConversions::GetPrimitiveType(_primitiveType); 310 311 if (instanceCount == 1) { 312 [_encoder drawPrimitives:type 313 vertexStart:firstVertex 314 vertexCount:vertexCount]; 315 } else { 316 [_encoder drawPrimitives:type 317 vertexStart:firstVertex 318 vertexCount:vertexCount 319 instanceCount:instanceCount]; 320 } 321 322 _hasWork = true; 323} 324 325void 326HgiMetalGraphicsCmds::DrawIndirect( 327 HgiBufferHandle const& drawParameterBuffer, 328 uint32_t bufferOffset, 329 uint32_t drawCount, 330 uint32_t stride) 331{ 332 _CreateEncoder(); 333 334 HgiMetalBuffer* drawBuf = 335 static_cast<HgiMetalBuffer*>(drawParameterBuffer.Get()); 336 337 MTLPrimitiveType type=HgiMetalConversions::GetPrimitiveType(_primitiveType); 338 339 for (uint32_t i = 0; i < drawCount; i++) { 340 [_encoder drawPrimitives:type 341 indirectBuffer:drawBuf->GetBufferId() 342 indirectBufferOffset:bufferOffset + (i * stride)]; 343 } 344} 345 346void 347HgiMetalGraphicsCmds::DrawIndexed( 348 HgiBufferHandle const& indexBuffer, 349 uint32_t indexCount, 350 uint32_t indexBufferByteOffset, 351 uint32_t vertexOffset, 352 uint32_t instanceCount) 353{ 354 TF_VERIFY(instanceCount>0); 355 356 _CreateEncoder(); 357 358 HgiMetalBuffer* indexBuf = static_cast<HgiMetalBuffer*>(indexBuffer.Get()); 359 HgiBufferDesc const& indexDesc = indexBuf->GetDescriptor(); 360 361 // We assume 32bit indices: GL_UNSIGNED_INT 362 TF_VERIFY(indexDesc.usage & HgiBufferUsageIndex32); 363 364 MTLPrimitiveType type=HgiMetalConversions::GetPrimitiveType(_primitiveType); 365 366 [_encoder drawIndexedPrimitives:type 367 indexCount:indexCount 368 indexType:MTLIndexTypeUInt32 369 indexBuffer:indexBuf->GetBufferId() 370 indexBufferOffset:indexBufferByteOffset 371 instanceCount:instanceCount 372 baseVertex:vertexOffset 373 baseInstance:0]; 374 375 _hasWork = true; 376} 377 378void 379HgiMetalGraphicsCmds::DrawIndexedIndirect( 380 HgiBufferHandle const& indexBuffer, 381 HgiBufferHandle const& drawParameterBuffer, 382 uint32_t drawBufferOffset, 383 uint32_t drawCount, 384 uint32_t stride) 385{ 386 _CreateEncoder(); 387 388 HgiMetalBuffer* indexBuf = static_cast<HgiMetalBuffer*>(indexBuffer.Get()); 389 HgiBufferDesc const& indexDesc = indexBuf->GetDescriptor(); 390 391 // We assume 32bit indices: GL_UNSIGNED_INT 392 TF_VERIFY(indexDesc.usage & HgiBufferUsageIndex32); 393 394 HgiMetalBuffer* drawBuf = 395 static_cast<HgiMetalBuffer*>(drawParameterBuffer.Get()); 396 397 MTLPrimitiveType type=HgiMetalConversions::GetPrimitiveType(_primitiveType); 398 399 for (uint32_t i = 0; i < drawCount; i++) { 400 [_encoder drawIndexedPrimitives:type 401 indexType:MTLIndexTypeUInt32 402 indexBuffer:indexBuf->GetBufferId() 403 indexBufferOffset:0 404 indirectBuffer:drawBuf->GetBufferId() 405 indirectBufferOffset:drawBufferOffset + (i * stride)]; 406 } 407} 408 409void 410HgiMetalGraphicsCmds::PushDebugGroup(const char* label) 411{ 412 if (_encoder) { 413 HGIMETAL_DEBUG_LABEL(_encoder, label) 414 } 415 else if (HgiMetalDebugEnabled()) { 416 _debugLabel = [@(label) copy]; 417 } 418} 419 420void 421HgiMetalGraphicsCmds::PopDebugGroup() 422{ 423 if (_debugLabel) { 424 [_debugLabel release]; 425 _debugLabel = nil; 426 } 427} 428 429void 430HgiMetalGraphicsCmds::MemoryBarrier(HgiMemoryBarrier barrier) 431{ 432 TF_VERIFY(barrier==HgiMemoryBarrierAll, "Unknown barrier"); 433 434 MTLBarrierScope scope = 435 MTLBarrierScopeBuffers | 436 MTLBarrierScopeTextures | 437 MTLBarrierScopeRenderTargets; 438 439 MTLRenderStages srcStages = MTLRenderStageVertex | MTLRenderStageFragment; 440 MTLRenderStages dstStages = MTLRenderStageVertex | MTLRenderStageFragment; 441 442 [_encoder memoryBarrierWithScope:scope 443 afterStages:srcStages 444 beforeStages:dstStages]; 445} 446 447static 448HgiMetal::CommitCommandBufferWaitType 449_ToHgiMetal(const HgiSubmitWaitType wait) 450{ 451 switch(wait) { 452 case HgiSubmitWaitTypeNoWait: 453 return HgiMetal::CommitCommandBuffer_NoWait; 454 case HgiSubmitWaitTypeWaitUntilCompleted: 455 return HgiMetal::CommitCommandBuffer_WaitUntilCompleted; 456 } 457 458 TF_CODING_ERROR("Bad enum value for HgiSubmitWaitType"); 459 return HgiMetal::CommitCommandBuffer_WaitUntilCompleted; 460} 461 462bool 463HgiMetalGraphicsCmds::_Submit(Hgi* hgi, HgiSubmitWaitType wait) 464{ 465 if (_encoder) { 466 [_encoder endEncoding]; 467 _encoder = nil; 468 469 _hgi->CommitPrimaryCommandBuffer(_ToHgiMetal(wait)); 470 } 471 472 return _hasWork; 473} 474 475PXR_NAMESPACE_CLOSE_SCOPE 476