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/base/arch/defines.h" 25 26#include "pxr/imaging/hgiMetal/hgi.h" 27#include "pxr/imaging/hgiMetal/buffer.h" 28#include "pxr/imaging/hgiMetal/blitCmds.h" 29#include "pxr/imaging/hgiMetal/computeCmds.h" 30#include "pxr/imaging/hgiMetal/computePipeline.h" 31#include "pxr/imaging/hgiMetal/capabilities.h" 32#include "pxr/imaging/hgiMetal/conversions.h" 33#include "pxr/imaging/hgiMetal/diagnostic.h" 34#include "pxr/imaging/hgiMetal/graphicsCmds.h" 35#include "pxr/imaging/hgiMetal/graphicsPipeline.h" 36#include "pxr/imaging/hgiMetal/resourceBindings.h" 37#include "pxr/imaging/hgiMetal/sampler.h" 38#include "pxr/imaging/hgiMetal/shaderFunction.h" 39#include "pxr/imaging/hgiMetal/shaderProgram.h" 40#include "pxr/imaging/hgiMetal/texture.h" 41 42#include "pxr/base/trace/trace.h" 43 44#include "pxr/base/tf/getenv.h" 45#include "pxr/base/tf/registryManager.h" 46#include "pxr/base/tf/type.h" 47 48PXR_NAMESPACE_OPEN_SCOPE 49 50TF_REGISTRY_FUNCTION(TfType) 51{ 52 TfType t = TfType::Define<HgiMetal, TfType::Bases<Hgi> >(); 53 t.SetFactory<HgiFactory<HgiMetal>>(); 54} 55 56static int _GetAPIVersion() 57{ 58 if (@available(macOS 10.15, ios 13.0, *)) { 59 return APIVersion_Metal3_0; 60 } 61 if (@available(macOS 10.13, ios 11.0, *)) { 62 return APIVersion_Metal2_0; 63 } 64 65 return APIVersion_Metal1_0; 66} 67 68HgiMetal::HgiMetal(id<MTLDevice> device) 69: _device(device) 70, _currentCmds(nullptr) 71, _frameDepth(0) 72, _apiVersion(_GetAPIVersion()) 73, _workToFlush(false) 74{ 75 if (!_device) { 76 if( TfGetenvBool("HGIMETAL_USE_INTEGRATED_GPU", false)) { 77 _device = MTLCopyAllDevices()[1]; 78 } 79 80 if (!_device) { 81 _device = MTLCreateSystemDefaultDevice(); 82 } 83 } 84 85 static int const commandBufferPoolSize = 256; 86 _commandQueue = [_device newCommandQueueWithMaxCommandBufferCount: 87 commandBufferPoolSize]; 88 _commandBuffer = [_commandQueue commandBuffer]; 89 [_commandBuffer retain]; 90 91 _capabilities.reset(new HgiMetalCapabilities(_device)); 92 93 HgiMetalSetupMetalDebug(); 94 95 _captureScopeFullFrame = 96 [[MTLCaptureManager sharedCaptureManager] 97 newCaptureScopeWithDevice:_device]; 98 _captureScopeFullFrame.label = 99 [NSString stringWithFormat:@"Full Hydra Frame"]; 100 101 [[MTLCaptureManager sharedCaptureManager] 102 setDefaultCaptureScope:_captureScopeFullFrame]; 103} 104 105HgiMetal::~HgiMetal() 106{ 107 [_commandBuffer commit]; 108 [_commandBuffer release]; 109 [_captureScopeFullFrame release]; 110 [_commandQueue release]; 111} 112 113id<MTLDevice> 114HgiMetal::GetPrimaryDevice() const 115{ 116 return _device; 117} 118 119HgiGraphicsCmdsUniquePtr 120HgiMetal::CreateGraphicsCmds( 121 HgiGraphicsCmdsDesc const& desc) 122{ 123 // XXX We should TF_CODING_ERROR here when there are no attachments, but 124 // during the Hgi transition we allow it to render to global gl framebuffer. 125 if (!desc.HasAttachments()) { 126 // TF_CODING_ERROR("Graphics encoder desc has no attachments"); 127 return nullptr; 128 } 129 130 HgiMetalGraphicsCmds* encoder( 131 new HgiMetalGraphicsCmds(this, desc)); 132 133 return HgiGraphicsCmdsUniquePtr(encoder); 134} 135 136HgiComputeCmdsUniquePtr 137HgiMetal::CreateComputeCmds() 138{ 139 HgiComputeCmds* computeCmds = new HgiMetalComputeCmds(this); 140 if (!_currentCmds) { 141 _currentCmds = computeCmds; 142 } 143 return HgiComputeCmdsUniquePtr(computeCmds); 144} 145 146HgiBlitCmdsUniquePtr 147HgiMetal::CreateBlitCmds() 148{ 149 HgiMetalBlitCmds* blitCmds = new HgiMetalBlitCmds(this); 150 if (!_currentCmds) { 151 _currentCmds = blitCmds; 152 } 153 return HgiBlitCmdsUniquePtr(blitCmds); 154} 155 156HgiTextureHandle 157HgiMetal::CreateTexture(HgiTextureDesc const & desc) 158{ 159 return HgiTextureHandle(new HgiMetalTexture(this, desc), GetUniqueId()); 160} 161 162void 163HgiMetal::DestroyTexture(HgiTextureHandle* texHandle) 164{ 165 _TrashObject(texHandle); 166} 167 168HgiTextureViewHandle 169HgiMetal::CreateTextureView(HgiTextureViewDesc const & desc) 170{ 171 if (!desc.sourceTexture) { 172 TF_CODING_ERROR("Source texture is null"); 173 } 174 175 HgiTextureHandle src = 176 HgiTextureHandle(new HgiMetalTexture(this, desc), GetUniqueId()); 177 HgiTextureView* view = new HgiTextureView(desc); 178 view->SetViewTexture(src); 179 return HgiTextureViewHandle(view, GetUniqueId()); 180} 181 182void 183HgiMetal::DestroyTextureView(HgiTextureViewHandle* viewHandle) 184{ 185 // Trash the texture inside the view and invalidate the view handle. 186 HgiTextureHandle texHandle = (*viewHandle)->GetViewTexture(); 187 _TrashObject(&texHandle); 188 (*viewHandle)->SetViewTexture(HgiTextureHandle()); 189 delete viewHandle->Get(); 190 *viewHandle = HgiTextureViewHandle(); 191} 192 193HgiSamplerHandle 194HgiMetal::CreateSampler(HgiSamplerDesc const & desc) 195{ 196 return HgiSamplerHandle(new HgiMetalSampler(this, desc), GetUniqueId()); 197} 198 199void 200HgiMetal::DestroySampler(HgiSamplerHandle* smpHandle) 201{ 202 _TrashObject(smpHandle); 203} 204 205HgiBufferHandle 206HgiMetal::CreateBuffer(HgiBufferDesc const & desc) 207{ 208 return HgiBufferHandle(new HgiMetalBuffer(this, desc), GetUniqueId()); 209} 210 211void 212HgiMetal::DestroyBuffer(HgiBufferHandle* bufHandle) 213{ 214 _TrashObject(bufHandle); 215} 216 217HgiShaderFunctionHandle 218HgiMetal::CreateShaderFunction(HgiShaderFunctionDesc const& desc) 219{ 220 return HgiShaderFunctionHandle( 221 new HgiMetalShaderFunction(this, desc), GetUniqueId()); 222} 223 224void 225HgiMetal::DestroyShaderFunction(HgiShaderFunctionHandle* shaderFunctionHandle) 226{ 227 _TrashObject(shaderFunctionHandle); 228} 229 230HgiShaderProgramHandle 231HgiMetal::CreateShaderProgram(HgiShaderProgramDesc const& desc) 232{ 233 return HgiShaderProgramHandle( 234 new HgiMetalShaderProgram(desc), GetUniqueId()); 235} 236 237void 238HgiMetal::DestroyShaderProgram(HgiShaderProgramHandle* shaderProgramHandle) 239{ 240 _TrashObject(shaderProgramHandle); 241} 242 243 244HgiResourceBindingsHandle 245HgiMetal::CreateResourceBindings(HgiResourceBindingsDesc const& desc) 246{ 247 return HgiResourceBindingsHandle( 248 new HgiMetalResourceBindings(desc), GetUniqueId()); 249} 250 251void 252HgiMetal::DestroyResourceBindings(HgiResourceBindingsHandle* resHandle) 253{ 254 _TrashObject(resHandle); 255} 256 257HgiGraphicsPipelineHandle 258HgiMetal::CreateGraphicsPipeline(HgiGraphicsPipelineDesc const& desc) 259{ 260 return HgiGraphicsPipelineHandle( 261 new HgiMetalGraphicsPipeline(this, desc), GetUniqueId()); 262} 263 264void 265HgiMetal::DestroyGraphicsPipeline(HgiGraphicsPipelineHandle* pipeHandle) 266{ 267 _TrashObject(pipeHandle); 268} 269 270HgiComputePipelineHandle 271HgiMetal::CreateComputePipeline(HgiComputePipelineDesc const& desc) 272{ 273 return HgiComputePipelineHandle( 274 new HgiMetalComputePipeline(this, desc), GetUniqueId()); 275} 276 277void 278HgiMetal::DestroyComputePipeline(HgiComputePipelineHandle* pipeHandle) 279{ 280 _TrashObject(pipeHandle); 281} 282 283TfToken const& 284HgiMetal::GetAPIName() const { 285 return HgiTokens->Metal; 286} 287 288HgiMetalCapabilities const* 289HgiMetal::GetCapabilities() const 290{ 291 return _capabilities.get(); 292} 293 294void 295HgiMetal::StartFrame() 296{ 297 if (_frameDepth++ == 0) { 298 [_captureScopeFullFrame beginScope]; 299 300 if ([[MTLCaptureManager sharedCaptureManager] isCapturing]) { 301 // We need to grab a new command buffer otherwise the previous one 302 // (if it was allocated at the end of the last frame) won't appear in 303 // this frame's capture, and it will confuse us! 304 CommitPrimaryCommandBuffer(CommitCommandBuffer_NoWait, true); 305 } 306 } 307} 308 309void 310HgiMetal::EndFrame() 311{ 312 if (--_frameDepth == 0) { 313 [_captureScopeFullFrame endScope]; 314 } 315} 316 317id<MTLCommandQueue> 318HgiMetal::GetQueue() const 319{ 320 return _commandQueue; 321} 322 323id<MTLCommandBuffer> 324HgiMetal::GetPrimaryCommandBuffer(bool flush) 325{ 326 if (_workToFlush) { 327 if (_currentCmds) { 328 return nil; 329 } 330 } 331 if (flush) { 332 _workToFlush = true; 333 } 334 return _commandBuffer; 335} 336 337id<MTLCommandBuffer> 338HgiMetal::GetSecondaryCommandBuffer() 339{ 340 id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer]; 341 [commandBuffer retain]; 342 return commandBuffer; 343} 344 345int 346HgiMetal::GetAPIVersion() const 347{ 348 return _apiVersion; 349} 350 351void 352HgiMetal::CommitPrimaryCommandBuffer(CommitCommandBufferWaitType waitType, 353 bool forceNewBuffer) 354{ 355 if (!_workToFlush && !forceNewBuffer) { 356 return; 357 } 358 359 CommitSecondaryCommandBuffer(_commandBuffer, waitType); 360 [_commandBuffer release]; 361 _commandBuffer = [_commandQueue commandBuffer]; 362 [_commandBuffer retain]; 363 364 _workToFlush = false; 365} 366 367void 368HgiMetal::CommitSecondaryCommandBuffer( 369 id<MTLCommandBuffer> commandBuffer, 370 CommitCommandBufferWaitType waitType) 371{ 372 [commandBuffer commit]; 373 if (waitType == CommitCommandBuffer_WaitUntilScheduled) { 374 [commandBuffer waitUntilScheduled]; 375 } 376 else if (waitType == CommitCommandBuffer_WaitUntilCompleted) { 377 [commandBuffer waitUntilCompleted]; 378 } 379} 380 381void 382HgiMetal::ReleaseSecondaryCommandBuffer(id<MTLCommandBuffer> commandBuffer) 383{ 384 [commandBuffer release]; 385} 386 387bool 388HgiMetal::_SubmitCmds(HgiCmds* cmds, HgiSubmitWaitType wait) 389{ 390 TRACE_FUNCTION(); 391 392 if (cmds) { 393 _workToFlush = Hgi::_SubmitCmds(cmds, wait); 394 if (cmds == _currentCmds) { 395 _currentCmds = nullptr; 396 } 397 } 398 399 return _workToFlush; 400} 401 402PXR_NAMESPACE_CLOSE_SCOPE 403