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