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