xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/mtl_state_cache.h (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 rasterSampleCount;
225 };
226 
227 enum class RenderPipelineRasterization : uint32_t
228 {
229     // This flag is used for vertex shader not writing any stage output (e.g gl_Position).
230     // This will disable fragment shader stage. This is useful for transform feedback ouput vertex
231     // shader.
232     Disabled,
233 
234     // Fragment shader is enabled.
235     Enabled,
236 
237     // This flag is for rasterization discard emulation when vertex shader still writes to stage
238     // output. Disabled flag cannot be used in this case since Metal doesn't allow that. The
239     // emulation would insert a code snippet to move gl_Position out of clip space's visible area to
240     // simulate the discard.
241     EmulatedDiscard,
242 
243     EnumCount,
244 };
245 
246 template <typename T>
247 using RenderPipelineRasterStateMap = angle::PackedEnumMap<RenderPipelineRasterization, T>;
248 
249 struct alignas(4) RenderPipelineDesc
250 {
251     RenderPipelineDesc();
252     RenderPipelineDesc(const RenderPipelineDesc &src);
253     RenderPipelineDesc(RenderPipelineDesc &&src);
254 
255     RenderPipelineDesc &operator=(const RenderPipelineDesc &src);
256 
257     bool operator==(const RenderPipelineDesc &rhs) const;
258     size_t hash() const;
259     bool rasterizationEnabled() const;
260 
261     AutoObjCPtr<MTLRenderPipelineDescriptor *> createMetalDesc(
262         id<MTLFunction> vertexShader,
263         id<MTLFunction> fragmentShader) const;
264 
265     VertexDesc vertexDescriptor;
266 
267     RenderPipelineOutputDesc outputDescriptor;
268 
269     // Use uint8_t instead of MTLPrimitiveTopologyClass to compact space.
270     uint8_t inputPrimitiveTopology : 2;
271 
272     bool alphaToCoverageEnabled : 1;
273 
274     // These flags are for emulation and do not correspond to any flags in
275     // MTLRenderPipelineDescriptor descriptor. These flags should be used by
276     // RenderPipelineCacheSpecializeShaderFactory.
277     RenderPipelineRasterization rasterizationType : 2;
278 };
279 
280 struct alignas(4) ProvokingVertexComputePipelineDesc
281 {
282     ProvokingVertexComputePipelineDesc();
283     ProvokingVertexComputePipelineDesc(const ProvokingVertexComputePipelineDesc &src);
284     ProvokingVertexComputePipelineDesc(ProvokingVertexComputePipelineDesc &&src);
285 
286     ProvokingVertexComputePipelineDesc &operator=(const ProvokingVertexComputePipelineDesc &src);
287 
288     bool operator==(const ProvokingVertexComputePipelineDesc &rhs) const;
289     bool operator!=(const ProvokingVertexComputePipelineDesc &rhs) const;
290     size_t hash() const;
291 
292     gl::PrimitiveMode primitiveMode;
293     uint8_t elementType;
294     bool primitiveRestartEnabled;
295     bool generateIndices;
296 };
297 
298 struct RenderPassAttachmentDesc
299 {
300     RenderPassAttachmentDesc();
301     // Set default values
302     void reset();
303 
304     bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const;
305     bool operator==(const RenderPassAttachmentDesc &other) const;
306 
hasImplicitMSTextureRenderPassAttachmentDesc307     ANGLE_INLINE bool hasImplicitMSTexture() const { return implicitMSTexture.get(); }
308 
getImplicitMSTextureIfAvailOrTextureRenderPassAttachmentDesc309     const TextureRef &getImplicitMSTextureIfAvailOrTexture() const
310     {
311         return hasImplicitMSTexture() ? implicitMSTexture : texture;
312     }
313 
314     TextureRef texture;
315     // Implicit multisample texture that will be rendered into and discarded at the end of
316     // a render pass. Its result will be resolved into normal texture above.
317     TextureRef implicitMSTexture;
318     MipmapNativeLevel level;
319     uint32_t sliceOrDepth;
320 
321     // This attachment is blendable or not.
322     bool blendable;
323     MTLLoadAction loadAction;
324     MTLStoreAction storeAction;
325     MTLStoreActionOptions storeActionOptions;
326 };
327 
328 struct RenderPassColorAttachmentDesc : public RenderPassAttachmentDesc
329 {
330     inline bool operator==(const RenderPassColorAttachmentDesc &other) const
331     {
332         return RenderPassAttachmentDesc::operator==(other) && clearColor == other.clearColor;
333     }
334     inline bool operator!=(const RenderPassColorAttachmentDesc &other) const
335     {
336         return !(*this == other);
337     }
338     MTLClearColor clearColor = {0, 0, 0, 0};
339 };
340 
341 struct RenderPassDepthAttachmentDesc : public RenderPassAttachmentDesc
342 {
343     inline bool operator==(const RenderPassDepthAttachmentDesc &other) const
344     {
345         return RenderPassAttachmentDesc::operator==(other) && clearDepth == other.clearDepth;
346     }
347     inline bool operator!=(const RenderPassDepthAttachmentDesc &other) const
348     {
349         return !(*this == other);
350     }
351 
352     double clearDepth = 1.0;
353 };
354 
355 struct RenderPassStencilAttachmentDesc : public RenderPassAttachmentDesc
356 {
357     inline bool operator==(const RenderPassStencilAttachmentDesc &other) const
358     {
359         return RenderPassAttachmentDesc::operator==(other) && clearStencil == other.clearStencil;
360     }
361     inline bool operator!=(const RenderPassStencilAttachmentDesc &other) const
362     {
363         return !(*this == other);
364     }
365     uint32_t clearStencil = 0;
366 };
367 
368 //
369 // This is C++ equivalent of Objective-C MTLRenderPassDescriptor.
370 // We could use MTLRenderPassDescriptor directly, however, using C++ struct has benefits of fast
371 // copy, stack allocation, inlined comparing function, etc.
372 //
373 struct RenderPassDesc
374 {
375     std::array<RenderPassColorAttachmentDesc, kMaxRenderTargets> colorAttachments;
376     RenderPassDepthAttachmentDesc depthAttachment;
377     RenderPassStencilAttachmentDesc stencilAttachment;
378 
379     void convertToMetalDesc(MTLRenderPassDescriptor *objCDesc,
380                             uint32_t deviceMaxRenderTargets) const;
381 
382     // This will populate the RenderPipelineOutputDesc with default blend state and
383     // MTLColorWriteMaskAll
384     void populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const;
385     // This will populate the RenderPipelineOutputDesc with default blend state and the specified
386     // MTLColorWriteMask
387     void populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray,
388                                           RenderPipelineOutputDesc *outDesc) const;
389     // This will populate the RenderPipelineOutputDesc with the specified blend state
390     void populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray,
391                                           RenderPipelineOutputDesc *outDesc) const;
392 
393     bool equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const;
394     bool operator==(const RenderPassDesc &other) const;
395     inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); }
396 
397     uint32_t numColorAttachments = 0;
398     uint32_t rasterSampleCount   = 1;
399     uint32_t defaultWidth        = 0;
400     uint32_t defaultHeight       = 0;
401 };
402 
403 }  // namespace mtl
404 }  // namespace rx
405 
406 namespace std
407 {
408 
409 template <>
410 struct hash<rx::mtl::DepthStencilDesc>
411 {
412     size_t operator()(const rx::mtl::DepthStencilDesc &key) const { return key.hash(); }
413 };
414 
415 template <>
416 struct hash<rx::mtl::SamplerDesc>
417 {
418     size_t operator()(const rx::mtl::SamplerDesc &key) const { return key.hash(); }
419 };
420 
421 template <>
422 struct hash<rx::mtl::RenderPipelineDesc>
423 {
424     size_t operator()(const rx::mtl::RenderPipelineDesc &key) const { return key.hash(); }
425 };
426 
427 template <>
428 struct hash<rx::mtl::ProvokingVertexComputePipelineDesc>
429 {
430     size_t operator()(const rx::mtl::ProvokingVertexComputePipelineDesc &key) const
431     {
432         return key.hash();
433     }
434 };
435 }  // namespace std
436 
437 namespace rx
438 {
439 namespace mtl
440 {
441 
442 class StateCache final : angle::NonCopyable
443 {
444   public:
445     StateCache(const angle::FeaturesMtl &features);
446     ~StateCache();
447 
448     // Null depth stencil state has depth/stecil read & write disabled.
449     AutoObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState(
450         const mtl::ContextDevice &device);
451     AutoObjCPtr<id<MTLDepthStencilState>> getDepthStencilState(const mtl::ContextDevice &device,
452                                                                const DepthStencilDesc &desc);
453     AutoObjCPtr<id<MTLSamplerState>> getSamplerState(const mtl::ContextDevice &device,
454                                                      const SamplerDesc &desc);
455     // Null sampler state uses default SamplerDesc
456     AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(ContextMtl *context);
457     AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(const mtl::ContextDevice &device);
458     void clear();
459 
460   private:
461     const angle::FeaturesMtl &mFeatures;
462 
463     AutoObjCPtr<id<MTLDepthStencilState>> mNullDepthStencilState = nil;
464     angle::HashMap<DepthStencilDesc, AutoObjCPtr<id<MTLDepthStencilState>>> mDepthStencilStates;
465     angle::HashMap<SamplerDesc, AutoObjCPtr<id<MTLSamplerState>>> mSamplerStates;
466 };
467 
468 }  // namespace mtl
469 }  // namespace rx
470 
471 static inline bool operator==(const rx::mtl::VertexDesc &lhs, const rx::mtl::VertexDesc &rhs)
472 {
473     if (lhs.numAttribs != rhs.numAttribs || lhs.numBufferLayouts != rhs.numBufferLayouts)
474     {
475         return false;
476     }
477     for (uint8_t i = 0; i < lhs.numAttribs; ++i)
478     {
479         if (lhs.attributes[i] != rhs.attributes[i])
480         {
481             return false;
482         }
483     }
484     for (uint8_t i = 0; i < lhs.numBufferLayouts; ++i)
485     {
486         if (lhs.layouts[i] != rhs.layouts[i])
487         {
488             return false;
489         }
490     }
491     return true;
492 }
493 
494 static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs)
495 {
496     return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue &&
497            lhs.alpha == rhs.alpha;
498 }
499 
500 #endif /* LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ */
501