• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//
2// Copyright 2021 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// ProvokingVertexHelper.mm:
7//    Implements the class methods for ProvokingVertexHelper.
8//
9
10#include "libANGLE/renderer/metal/ProvokingVertexHelper.h"
11#import <Foundation/Foundation.h>
12#include "libANGLE/Display.h"
13#include "libANGLE/renderer/metal/ContextMtl.h"
14#include "libANGLE/renderer/metal/DisplayMtl.h"
15#include "libANGLE/renderer/metal/mtl_common.h"
16#include "libANGLE/renderer/metal/shaders/rewrite_indices_shared.h"
17namespace rx
18{
19
20namespace
21{
22constexpr size_t kInitialIndexBufferSize = 0xFFFF;  // Initial 64k pool.
23}
24static inline uint primCountForIndexCount(const uint fixIndexBufferKey, const uint indexCount)
25{
26    const uint fixIndexBufferMode =
27        (fixIndexBufferKey >> MtlFixIndexBufferKeyModeShift) & MtlFixIndexBufferKeyModeMask;
28
29    switch (fixIndexBufferMode)
30    {
31        case MtlFixIndexBufferKeyPoints:
32            return indexCount;
33        case MtlFixIndexBufferKeyLines:
34            return indexCount / 2;
35        case MtlFixIndexBufferKeyLineStrip:
36            return (uint)MAX(0, (int)indexCount - 1);
37        case MtlFixIndexBufferKeyLineLoop:
38            return (uint)MAX(0, (int)indexCount);
39        case MtlFixIndexBufferKeyTriangles:
40            return indexCount / 3;
41        case MtlFixIndexBufferKeyTriangleStrip:
42            return (uint)MAX(0, (int)indexCount - 2);
43        case MtlFixIndexBufferKeyTriangleFan:
44            return (uint)MAX(0, (int)indexCount - 2);
45        default:
46            ASSERT(false);
47            return 0;
48    }
49}
50
51static inline uint indexCountForPrimCount(const uint fixIndexBufferKey, const uint primCount)
52{
53    const uint fixIndexBufferMode =
54        (fixIndexBufferKey >> MtlFixIndexBufferKeyModeShift) & MtlFixIndexBufferKeyModeMask;
55    switch (fixIndexBufferMode)
56    {
57        case MtlFixIndexBufferKeyPoints:
58            return primCount;
59        case MtlFixIndexBufferKeyLines:
60            return primCount * 2;
61        case MtlFixIndexBufferKeyLineStrip:
62            return primCount * 2;
63        case MtlFixIndexBufferKeyLineLoop:
64            return primCount * 2;
65        case MtlFixIndexBufferKeyTriangles:
66            return primCount * 3;
67        case MtlFixIndexBufferKeyTriangleStrip:
68            return primCount * 3;
69        case MtlFixIndexBufferKeyTriangleFan:
70            return primCount * 3;
71        default:
72            ASSERT(false);
73            return 0;
74    }
75}
76
77static inline gl::PrimitiveMode getNewPrimitiveMode(const uint fixIndexBufferKey)
78{
79    const uint fixIndexBufferMode =
80        (fixIndexBufferKey >> MtlFixIndexBufferKeyModeShift) & MtlFixIndexBufferKeyModeMask;
81    switch (fixIndexBufferMode)
82    {
83        case MtlFixIndexBufferKeyPoints:
84            return gl::PrimitiveMode::Points;
85        case MtlFixIndexBufferKeyLines:
86            return gl::PrimitiveMode::Lines;
87        case MtlFixIndexBufferKeyLineStrip:
88            return gl::PrimitiveMode::Lines;
89        case MtlFixIndexBufferKeyLineLoop:
90            return gl::PrimitiveMode::Lines;
91        case MtlFixIndexBufferKeyTriangles:
92            return gl::PrimitiveMode::Triangles;
93        case MtlFixIndexBufferKeyTriangleStrip:
94            return gl::PrimitiveMode::Triangles;
95        case MtlFixIndexBufferKeyTriangleFan:
96            return gl::PrimitiveMode::Triangles;
97        default:
98            ASSERT(false);
99            return gl::PrimitiveMode::InvalidEnum;
100    }
101}
102ProvokingVertexHelper::ProvokingVertexHelper(ContextMtl *context,
103                                             mtl::CommandQueue *commandQueue,
104                                             DisplayMtl *display)
105    : mCommandBuffer(commandQueue),
106      mIndexBuffers(false),
107      mPipelineCache(this),
108      mCurrentEncoder(&mCommandBuffer)
109{
110    id<MTLLibrary> mtlLib   = display->getDefaultShadersLib();
111    mProvokingVertexLibrary = mtlLib;
112    mIndexBuffers.initialize(context, kInitialIndexBufferSize, mtl::kIndexBufferOffsetAlignment, 0);
113}
114
115void ProvokingVertexHelper::onDestroy(ContextMtl *context)
116{
117    mIndexBuffers.destroy(context);
118    mPipelineCache.clear();
119}
120
121void ProvokingVertexHelper::commitPreconditionCommandBuffer(ContextMtl *contextMtl)
122{
123    if (mCurrentEncoder.valid())
124    {
125        mCurrentEncoder.endEncoding();
126    }
127    mCommandBuffer.commit(mtl::NoWait);
128
129    mIndexBuffers.releaseInFlightBuffers(contextMtl);
130}
131
132mtl::ComputeCommandEncoder *ProvokingVertexHelper::getComputeCommandEncoder()
133{
134    if (mCurrentEncoder.valid())
135    {
136        return &mCurrentEncoder;
137    }
138
139    ensureCommandBufferReady();
140    return &mCurrentEncoder.restart();
141}
142
143void ProvokingVertexHelper::ensureCommandBufferReady()
144{
145    if (!mCommandBuffer.ready())
146    {
147        mCommandBuffer.restart();
148    }
149    ASSERT(mCommandBuffer.ready());
150}
151
152static uint buildIndexBufferKey(const mtl::ProvokingVertexComputePipelineDesc &pipelineDesc)
153{
154    uint indexBufferKey              = 0;
155    gl::DrawElementsType elementType = (gl::DrawElementsType)pipelineDesc.elementType;
156    bool doPrimPrestart              = pipelineDesc.primitiveRestartEnabled;
157    gl::PrimitiveMode primMode       = pipelineDesc.primitiveMode;
158    switch (elementType)
159    {
160        case gl::DrawElementsType::UnsignedShort:
161            indexBufferKey |= MtlFixIndexBufferKeyUint16 << MtlFixIndexBufferKeyInShift;
162            indexBufferKey |= MtlFixIndexBufferKeyUint16 << MtlFixIndexBufferKeyOutShift;
163            break;
164        case gl::DrawElementsType::UnsignedInt:
165            indexBufferKey |= MtlFixIndexBufferKeyUint32 << MtlFixIndexBufferKeyInShift;
166            indexBufferKey |= MtlFixIndexBufferKeyUint32 << MtlFixIndexBufferKeyOutShift;
167            break;
168        default:
169            ASSERT(false);  // Index type should only be short or int.
170            break;
171    }
172    indexBufferKey |= (uint)primMode << MtlFixIndexBufferKeyModeShift;
173    indexBufferKey |= doPrimPrestart ? MtlFixIndexBufferKeyPrimRestart : 0;
174    // We only rewrite indices if we're switching the provoking vertex mode.
175    indexBufferKey |= MtlFixIndexBufferKeyProvokingVertexLast;
176    return indexBufferKey;
177}
178
179bool ProvokingVertexHelper::hasSpecializedShader(
180    gl::ShaderType shaderType,
181    const mtl::ProvokingVertexComputePipelineDesc &renderPipelineDesc)
182{
183    return true;
184}
185
186angle::Result ProvokingVertexHelper::getSpecializedShader(
187    rx::mtl::Context *context,
188    gl::ShaderType shaderType,
189    const mtl::ProvokingVertexComputePipelineDesc &pipelineDesc,
190    id<MTLFunction> *shaderOut)
191{
192    uint indexBufferKey = buildIndexBufferKey(pipelineDesc);
193    auto fcValues       = mtl::adoptObjCObj([[MTLFunctionConstantValues alloc] init]);
194    [fcValues setConstantValue:&indexBufferKey type:MTLDataTypeUInt withName:@"fixIndexBufferKey"];
195    if (pipelineDesc.generateIndices)
196    {
197        return CreateMslShader(context, mProvokingVertexLibrary, @"genIndexBuffer", fcValues.get(),
198                               shaderOut);
199    }
200    else
201    {
202        return CreateMslShader(context, mProvokingVertexLibrary, @"fixIndexBuffer", fcValues.get(),
203                               shaderOut);
204    }
205}
206
207void ProvokingVertexHelper::prepareCommandEncoderForDescriptor(
208    ContextMtl *context,
209    mtl::ComputeCommandEncoder *encoder,
210    mtl::ProvokingVertexComputePipelineDesc desc)
211{
212    auto pipelineState = mPipelineCache.getComputePipelineState(context, desc);
213
214    encoder->setComputePipelineState(pipelineState);
215    mCachedDesc = desc;
216}
217mtl::BufferRef ProvokingVertexHelper::preconditionIndexBuffer(ContextMtl *context,
218                                                              mtl::BufferRef indexBuffer,
219                                                              size_t indexCount,
220                                                              size_t indexOffset,
221                                                              bool primitiveRestartEnabled,
222                                                              gl::PrimitiveMode primitiveMode,
223                                                              gl::DrawElementsType elementsType,
224                                                              size_t &outIndexCount,
225                                                              size_t &outIndexOffset,
226                                                              gl::PrimitiveMode &outPrimitiveMode)
227{
228    // Get specialized program
229    // Upload index buffer
230    // dispatch per-primitive?
231    ensureCommandBufferReady();
232    mtl::ProvokingVertexComputePipelineDesc pipelineDesc;
233    pipelineDesc.elementType             = (uint8_t)elementsType;
234    pipelineDesc.primitiveMode           = primitiveMode;
235    pipelineDesc.primitiveRestartEnabled = primitiveRestartEnabled;
236    pipelineDesc.generateIndices         = false;
237    uint indexBufferKey                  = buildIndexBufferKey(pipelineDesc);
238    uint primCount     = primCountForIndexCount(indexBufferKey, (uint32_t)indexCount);
239    uint newIndexCount = indexCountForPrimCount(indexBufferKey, primCount);
240    size_t indexSize   = gl::GetDrawElementsTypeSize(elementsType);
241    size_t newOffset   = 0;
242    mtl::BufferRef newBuffer;
243    if (mIndexBuffers.allocate(context, newIndexCount * indexSize + indexOffset, nullptr,
244                               &newBuffer, &newOffset) == angle::Result::Stop)
245    {
246        return nullptr;
247    }
248    uint indexCountEncoded     = (uint)indexCount;
249    auto threadsPerThreadgroup = MTLSizeMake(MIN(primCount, 64u), 1, 1);
250
251    mtl::ComputeCommandEncoder *encoder = getComputeCommandEncoder();
252    prepareCommandEncoderForDescriptor(context, encoder, pipelineDesc);
253    encoder->setBuffer(indexBuffer, static_cast<uint32_t>(indexOffset), 0);
254    encoder->setBufferForWrite(
255        newBuffer, static_cast<uint32_t>(indexOffset) + static_cast<uint32_t>(newOffset), 1);
256    encoder->setData(&indexCountEncoded, 2);
257    encoder->setData(&primCount, 3);
258    encoder->dispatch(
259        MTLSizeMake((primCount + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width, 1,
260                    1),
261        threadsPerThreadgroup);
262    outIndexCount    = newIndexCount;
263    outIndexOffset   = newOffset;
264    outPrimitiveMode = getNewPrimitiveMode(indexBufferKey);
265    return newBuffer;
266}
267
268mtl::BufferRef ProvokingVertexHelper::generateIndexBuffer(ContextMtl *context,
269                                                          size_t first,
270                                                          size_t indexCount,
271                                                          gl::PrimitiveMode primitiveMode,
272                                                          gl::DrawElementsType elementsType,
273                                                          size_t &outIndexCount,
274                                                          size_t &outIndexOffset,
275                                                          gl::PrimitiveMode &outPrimitiveMode)
276{
277    // Get specialized program
278    // Upload index buffer
279    // dispatch per-primitive?
280    ensureCommandBufferReady();
281    mtl::ProvokingVertexComputePipelineDesc pipelineDesc;
282    pipelineDesc.elementType             = (uint8_t)elementsType;
283    pipelineDesc.primitiveMode           = primitiveMode;
284    pipelineDesc.primitiveRestartEnabled = false;
285    pipelineDesc.generateIndices         = true;
286    uint indexBufferKey                  = buildIndexBufferKey(pipelineDesc);
287    uint primCount        = primCountForIndexCount(indexBufferKey, (uint32_t)indexCount);
288    uint newIndexCount    = indexCountForPrimCount(indexBufferKey, primCount);
289    size_t indexSize      = gl::GetDrawElementsTypeSize(elementsType);
290    size_t newIndexOffset = 0;
291    mtl::BufferRef newBuffer;
292    if (mIndexBuffers.allocate(context, newIndexCount * indexSize, nullptr, &newBuffer,
293                               &newIndexOffset) == angle::Result::Stop)
294    {
295        return nullptr;
296    }
297    uint indexCountEncoded     = static_cast<uint>(indexCount);
298    uint firstVertexEncoded    = static_cast<uint>(first);
299    uint indexOffsetEncoded    = static_cast<uint>(newIndexOffset);
300    auto threadsPerThreadgroup = MTLSizeMake(MIN(primCount, 64u), 1, 1);
301
302    mtl::ComputeCommandEncoder *encoder = getComputeCommandEncoder();
303    prepareCommandEncoderForDescriptor(context, encoder, pipelineDesc);
304    encoder->setBufferForWrite(newBuffer, indexOffsetEncoded, 1);
305    encoder->setData(indexCountEncoded, 2);
306    encoder->setData(primCount, 3);
307    encoder->setData(firstVertexEncoded, 4);
308    encoder->dispatch(
309        MTLSizeMake((primCount + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width, 1,
310                    1),
311        threadsPerThreadgroup);
312    outIndexCount    = newIndexCount;
313    outIndexOffset   = newIndexOffset;
314    outPrimitiveMode = getNewPrimitiveMode(indexBufferKey);
315    return newBuffer;
316}
317
318}  // namespace rx
319