• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2019 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 // mtl_state_cache.h:
7 //    Defines the class interface for StateCache, RenderPipelineCache and various
8 //    C struct versions of Metal sampler, depth stencil, render pass, render pipeline descriptors.
9 //
10 
11 #ifndef LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_
12 #define LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_
13 
14 #import <Metal/Metal.h>
15 
16 #include <unordered_map>
17 
18 #include "libANGLE/State.h"
19 #include "libANGLE/angletypes.h"
20 #include "libANGLE/renderer/metal/mtl_common.h"
21 #include "libANGLE/renderer/metal/mtl_context_device.h"
22 #include "libANGLE/renderer/metal/mtl_resources.h"
23 
24 static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs);
25 
26 namespace angle
27 {
28 struct FeaturesMtl;
29 }
30 
31 namespace rx
32 {
33 class ContextMtl;
34 
35 namespace mtl
36 {
37 struct alignas(1) StencilDesc
38 {
39     bool operator==(const StencilDesc &rhs) const;
40 
41     // Set default values
42     void reset();
43 
44     // Use uint8_t instead of MTLStencilOperation to compact space
45     uint8_t stencilFailureOperation : 3;
46     uint8_t depthFailureOperation : 3;
47     uint8_t depthStencilPassOperation : 3;
48 
49     // Use uint8_t instead of MTLCompareFunction to compact space
50     uint8_t stencilCompareFunction : 3;
51 
52     uint8_t readMask : 8;
53     uint8_t writeMask : 8;
54 };
55 
56 struct alignas(4) DepthStencilDesc
57 {
58     DepthStencilDesc();
59     DepthStencilDesc(const DepthStencilDesc &src);
60     DepthStencilDesc(DepthStencilDesc &&src);
61 
62     DepthStencilDesc &operator=(const DepthStencilDesc &src);
63 
64     bool operator==(const DepthStencilDesc &rhs) const;
65 
66     // Set default values.
67     // Default is depth/stencil test disabled. Depth/stencil write enabled.
68     void reset();
69 
70     size_t hash() const;
71 
72     void updateDepthTestEnabled(const gl::DepthStencilState &dsState);
73     void updateDepthWriteEnabled(const gl::DepthStencilState &dsState);
74     void updateDepthCompareFunc(const gl::DepthStencilState &dsState);
75     void updateStencilTestEnabled(const gl::DepthStencilState &dsState);
76     void updateStencilFrontOps(const gl::DepthStencilState &dsState);
77     void updateStencilBackOps(const gl::DepthStencilState &dsState);
78     void updateStencilFrontFuncs(const gl::DepthStencilState &dsState);
79     void updateStencilBackFuncs(const gl::DepthStencilState &dsState);
80     void updateStencilFrontWriteMask(const gl::DepthStencilState &dsState);
81     void updateStencilBackWriteMask(const gl::DepthStencilState &dsState);
82 
83     StencilDesc backFaceStencil;
84     StencilDesc frontFaceStencil;
85 
86     // Use uint8_t instead of MTLCompareFunction to compact space
87     uint8_t depthCompareFunction : 3;
88     bool depthWriteEnabled : 1;
89 };
90 
91 struct alignas(4) SamplerDesc
92 {
93     SamplerDesc();
94     SamplerDesc(const SamplerDesc &src);
95     SamplerDesc(SamplerDesc &&src);
96 
97     explicit SamplerDesc(const gl::SamplerState &glState);
98 
99     SamplerDesc &operator=(const SamplerDesc &src);
100 
101     // Set default values. All filters are nearest, and addresModes are clamp to edge.
102     void reset();
103 
104     bool operator==(const SamplerDesc &rhs) const;
105 
106     size_t hash() const;
107 
108     // Use uint8_t instead of MTLSamplerAddressMode to compact space
109     uint8_t rAddressMode : 3;
110     uint8_t sAddressMode : 3;
111     uint8_t tAddressMode : 3;
112 
113     // Use uint8_t instead of MTLSamplerMinMagFilter to compact space
114     uint8_t minFilter : 1;
115     uint8_t magFilter : 1;
116     uint8_t mipFilter : 2;
117 
118     uint8_t maxAnisotropy : 5;
119 
120     // Use uint8_t instead of MTLCompareFunction to compact space
121     uint8_t compareFunction : 3;
122 };
123 
124 struct VertexAttributeDesc
125 {
126     inline bool operator==(const VertexAttributeDesc &rhs) const
127     {
128         return format == rhs.format && offset == rhs.offset && bufferIndex == rhs.bufferIndex;
129     }
130     inline bool operator!=(const VertexAttributeDesc &rhs) const { return !(*this == rhs); }
131 
132     // Use uint8_t instead of MTLVertexFormat to compact space
133     uint8_t format : 6;
134     // Offset is only used for default attributes buffer. So 8 bits are enough.
135     uint8_t offset : 8;
136     uint8_t bufferIndex : 5;
137 };
138 
139 struct VertexBufferLayoutDesc
140 {
141     inline bool operator==(const VertexBufferLayoutDesc &rhs) const
142     {
143         return stepFunction == rhs.stepFunction && stepRate == rhs.stepRate && stride == rhs.stride;
144     }
145     inline bool operator!=(const VertexBufferLayoutDesc &rhs) const { return !(*this == rhs); }
146 
147     uint32_t stepRate;
148     uint32_t stride;
149 
150     // Use uint8_t instead of MTLVertexStepFunction to compact space
151     uint8_t stepFunction;
152 };
153 
154 struct VertexDesc
155 {
156     VertexAttributeDesc attributes[kMaxVertexAttribs];
157     VertexBufferLayoutDesc layouts[kMaxVertexAttribs];
158 
159     uint8_t numAttribs;
160     uint8_t numBufferLayouts;
161 };
162 
163 struct BlendDesc
164 {
165     bool operator==(const BlendDesc &rhs) const;
166     BlendDesc &operator=(const BlendDesc &src) = default;
167 
168     // Set default values
169     void reset();
170     void reset(MTLColorWriteMask writeMask);
171 
172     void updateWriteMask(const uint8_t angleMask);
173 
174     // Use uint8_t instead of MTLColorWriteMask to compact space
175     uint8_t writeMask : 4;
176 
177     // Use uint8_t instead of MTLBlendOperation to compact space
178     uint8_t alphaBlendOperation : 3;
179     uint8_t rgbBlendOperation : 3;
180 
181     // Use uint8_t instead of MTLBlendFactor to compact space
182     uint8_t destinationAlphaBlendFactor : 5;
183     uint8_t destinationRGBBlendFactor : 5;
184     uint8_t sourceAlphaBlendFactor : 5;
185     uint8_t sourceRGBBlendFactor : 5;
186 
187     bool blendingEnabled : 1;
188 };
189 
190 using BlendDescArray = std::array<BlendDesc, kMaxRenderTargets>;
191 using WriteMaskArray = std::array<uint8_t, kMaxRenderTargets>;
192 
193 struct alignas(2) RenderPipelineColorAttachmentDesc : public BlendDesc
194 {
195     bool operator==(const RenderPipelineColorAttachmentDesc &rhs) const;
196     inline bool operator!=(const RenderPipelineColorAttachmentDesc &rhs) const
197     {
198         return !(*this == rhs);
199     }
200 
201     // Set default values
202     void reset();
203     void reset(MTLPixelFormat format);
204     void reset(MTLPixelFormat format, MTLColorWriteMask writeMask);
205     void reset(MTLPixelFormat format, const BlendDesc &blendDesc);
206 
207     // Use uint16_t instead of MTLPixelFormat to compact space
208     uint16_t pixelFormat : 16;
209 };
210 
211 struct RenderPipelineOutputDesc
212 {
213     bool operator==(const RenderPipelineOutputDesc &rhs) const;
214 
215     void updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers);
216 
217     std::array<RenderPipelineColorAttachmentDesc, kMaxRenderTargets> colorAttachments;
218 
219     // Use uint16_t instead of MTLPixelFormat to compact space
220     uint16_t depthAttachmentPixelFormat : 16;
221     uint16_t stencilAttachmentPixelFormat : 16;
222 
223     uint8_t numColorAttachments;
224     uint8_t sampleCount;
225 };
226 
227 // Some SDK levels don't declare MTLPrimitiveTopologyClass. Needs to do compile time check here:
228 #if !(TARGET_OS_OSX || TARGET_OS_MACCATALYST) && \
229     (!defined(__IPHONE_12_0) || ANGLE_IOS_DEPLOY_TARGET < __IPHONE_12_0)
230 #    define ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 0
231 using PrimitiveTopologyClass                                     = uint32_t;
232 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassTriangle = 0;
233 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassPoint    = 0;
234 #else
235 #    define ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 1
236 using PrimitiveTopologyClass = MTLPrimitiveTopologyClass;
237 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassTriangle =
238     MTLPrimitiveTopologyClassTriangle;
239 constexpr PrimitiveTopologyClass kPrimitiveTopologyClassPoint = MTLPrimitiveTopologyClassPoint;
240 #endif
241 
242 enum class RenderPipelineRasterization : uint32_t
243 {
244     // This flag is used for vertex shader not writing any stage output (e.g gl_Position).
245     // This will disable fragment shader stage. This is useful for transform feedback ouput vertex
246     // shader.
247     Disabled,
248 
249     // Fragment shader is enabled.
250     Enabled,
251 
252     // This flag is for rasterization discard emulation when vertex shader still writes to stage
253     // output. Disabled flag cannot be used in this case since Metal doesn't allow that. The
254     // emulation would insert a code snippet to move gl_Position out of clip space's visible area to
255     // simulate the discard.
256     EmulatedDiscard,
257 
258     EnumCount,
259 };
260 
261 template <typename T>
262 using RenderPipelineRasterStateMap = angle::PackedEnumMap<RenderPipelineRasterization, T>;
263 
264 struct alignas(4) RenderPipelineDesc
265 {
266     RenderPipelineDesc();
267     RenderPipelineDesc(const RenderPipelineDesc &src);
268     RenderPipelineDesc(RenderPipelineDesc &&src);
269 
270     RenderPipelineDesc &operator=(const RenderPipelineDesc &src);
271 
272     bool operator==(const RenderPipelineDesc &rhs) const;
273     size_t hash() const;
274     bool rasterizationEnabled() const;
275 
276     AutoObjCPtr<MTLRenderPipelineDescriptor *> createMetalDesc(
277         id<MTLFunction> vertexShader,
278         id<MTLFunction> fragmentShader) const;
279 
280     VertexDesc vertexDescriptor;
281 
282     RenderPipelineOutputDesc outputDescriptor;
283 
284     // Use uint8_t instead of PrimitiveTopologyClass to compact space.
285     uint8_t inputPrimitiveTopology : 2;
286 
287     bool alphaToCoverageEnabled : 1;
288 
289     // These flags are for emulation and do not correspond to any flags in
290     // MTLRenderPipelineDescriptor descriptor. These flags should be used by
291     // RenderPipelineCacheSpecializeShaderFactory.
292     RenderPipelineRasterization rasterizationType : 2;
293 };
294 
295 struct alignas(4) ProvokingVertexComputePipelineDesc
296 {
297     ProvokingVertexComputePipelineDesc();
298     ProvokingVertexComputePipelineDesc(const ProvokingVertexComputePipelineDesc &src);
299     ProvokingVertexComputePipelineDesc(ProvokingVertexComputePipelineDesc &&src);
300 
301     ProvokingVertexComputePipelineDesc &operator=(const ProvokingVertexComputePipelineDesc &src);
302 
303     bool operator==(const ProvokingVertexComputePipelineDesc &rhs) const;
304     bool operator!=(const ProvokingVertexComputePipelineDesc &rhs) const;
305     size_t hash() const;
306 
307     gl::PrimitiveMode primitiveMode;
308     uint8_t elementType;
309     bool primitiveRestartEnabled;
310     bool generateIndices;
311 };
312 
313 struct RenderPassAttachmentDesc
314 {
315     RenderPassAttachmentDesc();
316     // Set default values
317     void reset();
318 
319     bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const;
320     bool operator==(const RenderPassAttachmentDesc &other) const;
321 
hasImplicitMSTextureRenderPassAttachmentDesc322     ANGLE_INLINE bool hasImplicitMSTexture() const { return implicitMSTexture.get(); }
323 
324     TextureRef texture;
325     // Implicit multisample texture that will be rendered into and discarded at the end of
326     // a render pass. Its result will be resolved into normal texture above.
327     TextureRef implicitMSTexture;
328     MipmapNativeLevel level;
329     uint32_t sliceOrDepth;
330 
331     // This attachment is blendable or not.
332     bool blendable;
333     MTLLoadAction loadAction;
334     MTLStoreAction storeAction;
335     MTLStoreActionOptions storeActionOptions;
336 };
337 
338 struct RenderPassColorAttachmentDesc : public RenderPassAttachmentDesc
339 {
340     inline bool operator==(const RenderPassColorAttachmentDesc &other) const
341     {
342         return RenderPassAttachmentDesc::operator==(other) && clearColor == other.clearColor;
343     }
344     inline bool operator!=(const RenderPassColorAttachmentDesc &other) const
345     {
346         return !(*this == other);
347     }
348     MTLClearColor clearColor = {0, 0, 0, 0};
349 };
350 
351 struct RenderPassDepthAttachmentDesc : public RenderPassAttachmentDesc
352 {
353     inline bool operator==(const RenderPassDepthAttachmentDesc &other) const
354     {
355         return RenderPassAttachmentDesc::operator==(other) && clearDepth == other.clearDepth;
356     }
357     inline bool operator!=(const RenderPassDepthAttachmentDesc &other) const
358     {
359         return !(*this == other);
360     }
361 
362     double clearDepth = 0;
363 };
364 
365 struct RenderPassStencilAttachmentDesc : public RenderPassAttachmentDesc
366 {
367     inline bool operator==(const RenderPassStencilAttachmentDesc &other) const
368     {
369         return RenderPassAttachmentDesc::operator==(other) && clearStencil == other.clearStencil;
370     }
371     inline bool operator!=(const RenderPassStencilAttachmentDesc &other) const
372     {
373         return !(*this == other);
374     }
375     uint32_t clearStencil = 0;
376 };
377 
378 //
379 // This is C++ equivalent of Objective-C MTLRenderPassDescriptor.
380 // We could use MTLRenderPassDescriptor directly, however, using C++ struct has benefits of fast
381 // copy, stack allocation, inlined comparing function, etc.
382 //
383 struct RenderPassDesc
384 {
385     std::array<RenderPassColorAttachmentDesc, kMaxRenderTargets> colorAttachments;
386     RenderPassDepthAttachmentDesc depthAttachment;
387     RenderPassStencilAttachmentDesc stencilAttachment;
388 
389     void convertToMetalDesc(MTLRenderPassDescriptor *objCDesc,
390                             uint32_t deviceMaxRenderTargets) const;
391 
392     // This will populate the RenderPipelineOutputDesc with default blend state and
393     // MTLColorWriteMaskAll
394     void populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const;
395     // This will populate the RenderPipelineOutputDesc with default blend state and the specified
396     // MTLColorWriteMask
397     void populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray,
398                                           RenderPipelineOutputDesc *outDesc) const;
399     // This will populate the RenderPipelineOutputDesc with the specified blend state
400     void populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray,
401                                           RenderPipelineOutputDesc *outDesc) const;
402 
403     bool equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const;
404     bool operator==(const RenderPassDesc &other) const;
405     inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); }
406 
407     uint32_t numColorAttachments = 0;
408     uint32_t sampleCount         = 1;
409     uint32_t defaultWidth        = 0;
410     uint32_t defaultHeight       = 0;
411 };
412 
413 }  // namespace mtl
414 }  // namespace rx
415 
416 namespace std
417 {
418 
419 template <>
420 struct hash<rx::mtl::DepthStencilDesc>
421 {
422     size_t operator()(const rx::mtl::DepthStencilDesc &key) const { return key.hash(); }
423 };
424 
425 template <>
426 struct hash<rx::mtl::SamplerDesc>
427 {
428     size_t operator()(const rx::mtl::SamplerDesc &key) const { return key.hash(); }
429 };
430 
431 template <>
432 struct hash<rx::mtl::RenderPipelineDesc>
433 {
434     size_t operator()(const rx::mtl::RenderPipelineDesc &key) const { return key.hash(); }
435 };
436 
437 template <>
438 struct hash<rx::mtl::ProvokingVertexComputePipelineDesc>
439 {
440     size_t operator()(const rx::mtl::ProvokingVertexComputePipelineDesc &key) const
441     {
442         return key.hash();
443     }
444 };
445 }  // namespace std
446 
447 namespace rx
448 {
449 namespace mtl
450 {
451 
452 class StateCache final : angle::NonCopyable
453 {
454   public:
455     StateCache(const angle::FeaturesMtl &features);
456     ~StateCache();
457 
458     // Null depth stencil state has depth/stecil read & write disabled.
459     AutoObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState(
460         const mtl::ContextDevice &device);
461     AutoObjCPtr<id<MTLDepthStencilState>> getDepthStencilState(const mtl::ContextDevice &device,
462                                                                const DepthStencilDesc &desc);
463     AutoObjCPtr<id<MTLSamplerState>> getSamplerState(const mtl::ContextDevice &device,
464                                                      const SamplerDesc &desc);
465     // Null sampler state uses default SamplerDesc
466     AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(ContextMtl *context);
467     AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(const mtl::ContextDevice &device);
468     void clear();
469 
470   private:
471     const angle::FeaturesMtl &mFeatures;
472 
473     AutoObjCPtr<id<MTLDepthStencilState>> mNullDepthStencilState = nil;
474     angle::HashMap<DepthStencilDesc, AutoObjCPtr<id<MTLDepthStencilState>>> mDepthStencilStates;
475     angle::HashMap<SamplerDesc, AutoObjCPtr<id<MTLSamplerState>>> mSamplerStates;
476 };
477 
478 }  // namespace mtl
479 }  // namespace rx
480 
481 static inline bool operator==(const rx::mtl::VertexDesc &lhs, const rx::mtl::VertexDesc &rhs)
482 {
483     if (lhs.numAttribs != rhs.numAttribs || lhs.numBufferLayouts != rhs.numBufferLayouts)
484     {
485         return false;
486     }
487     for (uint8_t i = 0; i < lhs.numAttribs; ++i)
488     {
489         if (lhs.attributes[i] != rhs.attributes[i])
490         {
491             return false;
492         }
493     }
494     for (uint8_t i = 0; i < lhs.numBufferLayouts; ++i)
495     {
496         if (lhs.layouts[i] != rhs.layouts[i])
497         {
498             return false;
499         }
500     }
501     return true;
502 }
503 
504 static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs)
505 {
506     return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue &&
507            lhs.alpha == rhs.alpha;
508 }
509 
510 #endif /* LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ */
511