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
25#include "pxr/base/tf/diagnostic.h"
26
27#include "pxr/imaging/hgiMetal/hgi.h"
28#include "pxr/imaging/hgiMetal/conversions.h"
29#include "pxr/imaging/hgiMetal/diagnostic.h"
30#include "pxr/imaging/hgiMetal/graphicsPipeline.h"
31#include "pxr/imaging/hgiMetal/resourceBindings.h"
32#include "pxr/imaging/hgiMetal/shaderProgram.h"
33#include "pxr/imaging/hgiMetal/shaderFunction.h"
34
35PXR_NAMESPACE_OPEN_SCOPE
36
37HgiMetalGraphicsPipeline::HgiMetalGraphicsPipeline(
38    HgiMetal *hgi,
39    HgiGraphicsPipelineDesc const& desc)
40    : HgiGraphicsPipeline(desc)
41    , _vertexDescriptor(nil)
42    , _depthStencilState(nil)
43    , _renderPipelineState(nil)
44{
45    _CreateVertexDescriptor();
46    _CreateDepthStencilState(hgi->GetPrimaryDevice());
47    _CreateRenderPipelineState(hgi->GetPrimaryDevice());
48}
49
50HgiMetalGraphicsPipeline::~HgiMetalGraphicsPipeline()
51{
52    if (_renderPipelineState) {
53        [_renderPipelineState release];
54    }
55    if (_depthStencilState) {
56        [_depthStencilState release];
57    }
58    if (_vertexDescriptor) {
59        [_vertexDescriptor release];
60    }
61}
62
63void
64HgiMetalGraphicsPipeline::_CreateVertexDescriptor()
65{
66    _vertexDescriptor = [[MTLVertexDescriptor alloc] init];
67
68    int index = 0;
69    for (HgiVertexBufferDesc const& vbo : _descriptor.vertexBuffers) {
70
71        HgiVertexAttributeDescVector const& vas = vbo.vertexAttributes;
72
73        _vertexDescriptor.layouts[index].stepFunction =
74            MTLVertexStepFunctionPerVertex;
75        _vertexDescriptor.layouts[index].stepRate = 1;
76        _vertexDescriptor.layouts[index].stride = vbo.vertexStride;
77
78        // Describe each vertex attribute in the vertex buffer
79        for (size_t loc = 0; loc<vas.size(); loc++) {
80            HgiVertexAttributeDesc const& va = vas[loc];
81
82            uint32_t idx = va.shaderBindLocation;
83            _vertexDescriptor.attributes[idx].format =
84                HgiMetalConversions::GetVertexFormat(va.format);
85            _vertexDescriptor.attributes[idx].bufferIndex = vbo.bindingIndex;
86            _vertexDescriptor.attributes[idx].offset = va.offset;
87        }
88        index++;
89    }
90}
91
92void
93HgiMetalGraphicsPipeline::_CreateRenderPipelineState(id<MTLDevice> device)
94{
95    MTLRenderPipelineDescriptor *stateDesc =
96        [[MTLRenderPipelineDescriptor alloc] init];
97
98    // Create a new render pipeline state object
99    HGIMETAL_DEBUG_LABEL(stateDesc, _descriptor.debugName.c_str());
100    stateDesc.rasterSampleCount = 1;
101
102    stateDesc.inputPrimitiveTopology =
103        HgiMetalConversions::GetPrimitiveClass(_descriptor.primitiveType);
104
105    HgiMetalShaderProgram const *metalProgram =
106        static_cast<HgiMetalShaderProgram*>(_descriptor.shaderProgram.Get());
107
108    stateDesc.vertexFunction = metalProgram->GetVertexFunction();
109    id<MTLFunction> fragFunction = metalProgram->GetFragmentFunction();
110    if (fragFunction && _descriptor.rasterizationState.rasterizerEnabled) {
111        stateDesc.fragmentFunction = fragFunction;
112        stateDesc.rasterizationEnabled = YES;
113    }
114    else {
115        stateDesc.rasterizationEnabled = NO;
116    }
117
118    // Color attachments
119    for (size_t i=0; i<_descriptor.colorAttachmentDescs.size(); i++) {
120        HgiAttachmentDesc const &hgiColorAttachment =
121            _descriptor.colorAttachmentDescs[i];
122        MTLRenderPipelineColorAttachmentDescriptor *metalColorAttachment =
123            stateDesc.colorAttachments[i];
124
125        metalColorAttachment.pixelFormat = HgiMetalConversions::GetPixelFormat(
126            hgiColorAttachment.format);
127
128        if (hgiColorAttachment.blendEnabled) {
129            metalColorAttachment.blendingEnabled = YES;
130
131            metalColorAttachment.sourceRGBBlendFactor =
132                HgiMetalConversions::GetBlendFactor(
133                    hgiColorAttachment.srcColorBlendFactor);
134            metalColorAttachment.destinationRGBBlendFactor =
135                HgiMetalConversions::GetBlendFactor(
136                    hgiColorAttachment.dstColorBlendFactor);
137
138            metalColorAttachment.sourceAlphaBlendFactor =
139                HgiMetalConversions::GetBlendFactor(
140                    hgiColorAttachment.srcAlphaBlendFactor);
141            metalColorAttachment.destinationAlphaBlendFactor =
142                HgiMetalConversions::GetBlendFactor(
143                    hgiColorAttachment.dstAlphaBlendFactor);
144
145            metalColorAttachment.rgbBlendOperation =
146                HgiMetalConversions::GetBlendEquation(
147                    hgiColorAttachment.colorBlendOp);
148            metalColorAttachment.alphaBlendOperation =
149                HgiMetalConversions::GetBlendEquation(
150                    hgiColorAttachment.alphaBlendOp);
151        }
152        else {
153            metalColorAttachment.blendingEnabled = NO;
154        }
155    }
156
157    HgiAttachmentDesc const &hgiDepthAttachment =
158        _descriptor.depthAttachmentDesc;
159
160    stateDesc.depthAttachmentPixelFormat =
161        HgiMetalConversions::GetPixelFormat(hgiDepthAttachment.format);
162
163    stateDesc.sampleCount = _descriptor.multiSampleState.sampleCount;
164    if (_descriptor.multiSampleState.alphaToCoverageEnable) {
165        stateDesc.alphaToCoverageEnabled = YES;
166    } else {
167        stateDesc.alphaToCoverageEnabled = NO;
168    }
169
170    stateDesc.vertexDescriptor = _vertexDescriptor;
171
172    NSError *error = NULL;
173    _renderPipelineState = [device
174        newRenderPipelineStateWithDescriptor:stateDesc
175        error:&error];
176    [stateDesc release];
177
178    if (!_renderPipelineState) {
179        NSString *err = [error localizedDescription];
180        TF_WARN("Failed to created pipeline state, error %s",
181            [err UTF8String]);
182    }
183}
184
185void
186HgiMetalGraphicsPipeline::_CreateDepthStencilState(id<MTLDevice> device)
187{
188    MTLDepthStencilDescriptor *depthStencilStateDescriptor =
189        [[MTLDepthStencilDescriptor alloc] init];
190
191    HGIMETAL_DEBUG_LABEL(
192        depthStencilStateDescriptor, _descriptor.debugName.c_str());
193
194    if (_descriptor.depthState.depthWriteEnabled) {
195        depthStencilStateDescriptor.depthWriteEnabled = YES;
196    }
197    else {
198        depthStencilStateDescriptor.depthWriteEnabled = NO;
199    }
200    if (_descriptor.depthState.depthTestEnabled) {
201        MTLCompareFunction depthFn = HgiMetalConversions::GetDepthCompareFunction(
202            _descriptor.depthState.depthCompareFn);
203        depthStencilStateDescriptor.depthCompareFunction = depthFn;
204    }
205    else {
206        // Even if there is no depth attachment, some drivers may still perform
207        // the depth test. So we pick Always over Never.
208        depthStencilStateDescriptor.depthCompareFunction =
209            MTLCompareFunctionAlways;
210    }
211
212    if (_descriptor.depthState.stencilTestEnabled) {
213        TF_CODING_ERROR("Missing implementation stencil mask enabled");
214    } else {
215        depthStencilStateDescriptor.backFaceStencil = nil;
216        depthStencilStateDescriptor.frontFaceStencil = nil;
217    }
218
219    _depthStencilState = [device
220        newDepthStencilStateWithDescriptor:depthStencilStateDescriptor];
221    [depthStencilStateDescriptor release];
222
223    TF_VERIFY(_depthStencilState,
224        "Failed to created depth stencil state");
225}
226
227void
228HgiMetalGraphicsPipeline::BindPipeline(id<MTLRenderCommandEncoder> renderEncoder)
229{
230    [renderEncoder setRenderPipelineState:_renderPipelineState];
231
232    //
233    // Rasterization state
234    //
235    [renderEncoder setCullMode:HgiMetalConversions::GetCullMode(
236        _descriptor.rasterizationState.cullMode)];
237    [renderEncoder setTriangleFillMode:HgiMetalConversions::GetPolygonMode(
238        _descriptor.rasterizationState.polygonMode)];
239    [renderEncoder setFrontFacingWinding:HgiMetalConversions::GetWinding(
240        _descriptor.rasterizationState.winding)];
241    [renderEncoder setDepthStencilState:_depthStencilState];
242
243    TF_VERIFY(_descriptor.rasterizationState.lineWidth == 1.0f,
244        "Missing implementation buffers");
245}
246
247PXR_NAMESPACE_CLOSE_SCOPE
248