xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/mtl_format_utils.mm (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_format_utils.mm:
7//      Implements Format conversion utilities classes that convert from angle formats
8//      to respective MTLPixelFormat and MTLVertexFormat.
9//
10
11#include "libANGLE/renderer/metal/mtl_format_utils.h"
12
13#include "common/debug.h"
14#include "libANGLE/renderer/Format.h"
15#include "libANGLE/renderer/load_functions_table.h"
16#include "libANGLE/renderer/metal/DisplayMtl.h"
17
18namespace rx
19{
20namespace mtl
21{
22
23namespace priv
24{
25
26template <typename T>
27inline T *OffsetDataPointer(uint8_t *data, size_t y, size_t z, size_t rowPitch, size_t depthPitch)
28{
29    return reinterpret_cast<T *>(data + (y * rowPitch) + (z * depthPitch));
30}
31
32template <typename T>
33inline const T *OffsetDataPointer(const uint8_t *data,
34                                  size_t y,
35                                  size_t z,
36                                  size_t rowPitch,
37                                  size_t depthPitch)
38{
39    return reinterpret_cast<const T *>(data + (y * rowPitch) + (z * depthPitch));
40}
41
42}  // namespace priv
43
44namespace
45{
46
47bool OverrideTextureCaps(const DisplayMtl *display, angle::FormatID formatId, gl::TextureCaps *caps)
48{
49    // NOTE(hqle): Auto generate this.
50    switch (formatId)
51    {
52        // NOTE: even though iOS devices don't support filtering depth textures, we still report as
53        // supported here in order for the OES_depth_texture extension to be enabled.
54        // During draw call, the filter modes will be converted to nearest.
55        case angle::FormatID::D16_UNORM:
56        case angle::FormatID::D24_UNORM_S8_UINT:
57        case angle::FormatID::D32_FLOAT_S8X24_UINT:
58        case angle::FormatID::D32_FLOAT:
59        case angle::FormatID::D32_UNORM:
60            caps->texturable = caps->filterable = caps->textureAttachment = caps->renderbuffer =
61                true;
62            return true;
63        default:
64            // NOTE(hqle): Handle more cases
65            return false;
66    }
67}
68
69void GenerateTextureCapsMap(const FormatTable &formatTable,
70                            const DisplayMtl *display,
71                            gl::TextureCapsMap *capsMapOut,
72                            uint32_t *maxSamplesOut)
73{
74    auto &textureCapsMap = *capsMapOut;
75    auto formatVerifier  = [&](const gl::InternalFormat &internalFormatInfo) {
76        angle::FormatID angleFormatId =
77            angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat);
78        const Format &mtlFormat = formatTable.getPixelFormat(angleFormatId);
79        if (!mtlFormat.valid())
80        {
81            return;
82        }
83        const FormatCaps &formatCaps = mtlFormat.getCaps();
84
85        gl::TextureCaps textureCaps;
86
87        // First let check whether we can override certain special cases.
88        if (!OverrideTextureCaps(display, mtlFormat.intendedFormatId, &textureCaps))
89        {
90            // Fill the texture caps using pixel format's caps
91            textureCaps.filterable = mtlFormat.getCaps().filterable;
92            textureCaps.renderbuffer =
93                mtlFormat.getCaps().colorRenderable || mtlFormat.getCaps().depthRenderable;
94            textureCaps.texturable        = true;
95            textureCaps.textureAttachment = textureCaps.renderbuffer;
96            textureCaps.blendable         = mtlFormat.getCaps().blendable;
97        }
98
99        if (formatCaps.multisample)
100        {
101            constexpr uint32_t sampleCounts[] = {2, 4, 8};
102            for (auto sampleCount : sampleCounts)
103            {
104                if ([display->getMetalDevice() supportsTextureSampleCount:sampleCount])
105                {
106                    textureCaps.sampleCounts.insert(sampleCount);
107                    *maxSamplesOut = std::max(*maxSamplesOut, sampleCount);
108                }
109            }
110        }
111
112        textureCapsMap.set(mtlFormat.intendedFormatId, textureCaps);
113    };
114
115    // Texture caps map.
116    const gl::FormatSet &internalFormats = gl::GetAllSizedInternalFormats();
117    for (const auto internalFormat : internalFormats)
118    {
119        const gl::InternalFormat &internalFormatInfo =
120            gl::GetSizedInternalFormatInfo(internalFormat);
121
122        formatVerifier(internalFormatInfo);
123    }
124}
125
126}  // namespace
127
128// FormatBase implementation
129const angle::Format &FormatBase::actualAngleFormat() const
130{
131    return angle::Format::Get(actualFormatId);
132}
133
134const angle::Format &FormatBase::intendedAngleFormat() const
135{
136    return angle::Format::Get(intendedFormatId);
137}
138
139// Format implementation
140const gl::InternalFormat &Format::intendedInternalFormat() const
141{
142    return gl::GetSizedInternalFormatInfo(intendedAngleFormat().glInternalFormat);
143}
144
145const gl::InternalFormat &Format::actualInternalFormat() const
146{
147    return gl::GetSizedInternalFormatInfo(actualAngleFormat().glInternalFormat);
148}
149
150bool Format::needConversion(angle::FormatID srcFormatId) const
151{
152    if ((srcFormatId == angle::FormatID::BC1_RGB_UNORM_BLOCK &&
153         actualFormatId == angle::FormatID::BC1_RGBA_UNORM_BLOCK) ||
154        (srcFormatId == angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK &&
155         actualFormatId == angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK))
156    {
157        // When texture swizzling is available, DXT1 RGB format will be swizzled with RGB1.
158        // WebGL allows unswizzled mapping when swizzling is not available. No need to convert.
159        return false;
160    }
161    return srcFormatId != actualFormatId;
162}
163
164bool Format::isPVRTC() const
165{
166    switch (metalFormat)
167    {
168        case MTLPixelFormatPVRTC_RGB_2BPP:
169        case MTLPixelFormatPVRTC_RGB_2BPP_sRGB:
170        case MTLPixelFormatPVRTC_RGB_4BPP:
171        case MTLPixelFormatPVRTC_RGB_4BPP_sRGB:
172        case MTLPixelFormatPVRTC_RGBA_2BPP:
173        case MTLPixelFormatPVRTC_RGBA_2BPP_sRGB:
174        case MTLPixelFormatPVRTC_RGBA_4BPP:
175        case MTLPixelFormatPVRTC_RGBA_4BPP_sRGB:
176            return true;
177        default:
178            return false;
179    }
180}
181
182// FormatTable implementation
183angle::Result FormatTable::initialize(const DisplayMtl *display)
184{
185    mMaxSamples = 0;
186
187    // Initialize native format caps
188    initNativeFormatCaps(display);
189
190    for (size_t i = 0; i < angle::kNumANGLEFormats; ++i)
191    {
192        const auto formatId = static_cast<angle::FormatID>(i);
193
194        mPixelFormatTable[i].init(display, formatId);
195        mPixelFormatTable[i].caps = mNativePixelFormatCapsTable[mPixelFormatTable[i].metalFormat];
196
197        if (mPixelFormatTable[i].actualFormatId != mPixelFormatTable[i].intendedFormatId)
198        {
199            mPixelFormatTable[i].textureLoadFunctions = angle::GetLoadFunctionsMap(
200                mPixelFormatTable[i].intendedAngleFormat().glInternalFormat,
201                mPixelFormatTable[i].actualFormatId);
202        }
203
204        mVertexFormatTables[0][i].init(formatId, false);
205        mVertexFormatTables[1][i].init(formatId, true);
206    }
207
208    // TODO(anglebug.com/40096755): unmerged change from WebKit was here -
209    // D24S8 fallback to D32_FLOAT_S8X24_UINT, since removed.
210
211    return angle::Result::Continue;
212}
213
214void FormatTable::generateTextureCaps(const DisplayMtl *display, gl::TextureCapsMap *capsMapOut)
215{
216    GenerateTextureCapsMap(*this, display, capsMapOut, &mMaxSamples);
217}
218
219const Format &FormatTable::getPixelFormat(angle::FormatID angleFormatId) const
220{
221    return mPixelFormatTable[static_cast<size_t>(angleFormatId)];
222}
223const FormatCaps &FormatTable::getNativeFormatCaps(MTLPixelFormat mtlFormat) const
224{
225    ASSERT(mNativePixelFormatCapsTable.count(mtlFormat));
226    return mNativePixelFormatCapsTable.at(mtlFormat);
227}
228const VertexFormat &FormatTable::getVertexFormat(angle::FormatID angleFormatId,
229                                                 bool tightlyPacked) const
230{
231    auto tableIdx = tightlyPacked ? 1 : 0;
232    return mVertexFormatTables[tableIdx][static_cast<size_t>(angleFormatId)];
233}
234
235void FormatTable::setFormatCaps(MTLPixelFormat formatId,
236                                bool filterable,
237                                bool writable,
238                                bool blendable,
239                                bool multisample,
240                                bool resolve,
241                                bool colorRenderable)
242{
243    setFormatCaps(formatId, filterable, writable, blendable, multisample, resolve, colorRenderable,
244                  false, 0);
245}
246void FormatTable::setFormatCaps(MTLPixelFormat formatId,
247                                bool filterable,
248                                bool writable,
249                                bool blendable,
250                                bool multisample,
251                                bool resolve,
252                                bool colorRenderable,
253                                NSUInteger pixelBytes,
254                                NSUInteger channels)
255{
256    setFormatCaps(formatId, filterable, writable, blendable, multisample, resolve, colorRenderable,
257                  false, pixelBytes, channels);
258}
259
260void FormatTable::setFormatCaps(MTLPixelFormat formatId,
261                                bool filterable,
262                                bool writable,
263                                bool blendable,
264                                bool multisample,
265                                bool resolve,
266                                bool colorRenderable,
267                                bool depthRenderable)
268{
269    setFormatCaps(formatId, filterable, writable, blendable, multisample, resolve, colorRenderable,
270                  depthRenderable, 0, 0);
271}
272void FormatTable::setFormatCaps(MTLPixelFormat id,
273                                bool filterable,
274                                bool writable,
275                                bool blendable,
276                                bool multisample,
277                                bool resolve,
278                                bool colorRenderable,
279                                bool depthRenderable,
280                                NSUInteger pixelBytes,
281                                NSUInteger channels)
282{
283    mNativePixelFormatCapsTable[id].filterable      = filterable;
284    mNativePixelFormatCapsTable[id].writable        = writable;
285    mNativePixelFormatCapsTable[id].colorRenderable = colorRenderable;
286    mNativePixelFormatCapsTable[id].depthRenderable = depthRenderable;
287    mNativePixelFormatCapsTable[id].blendable       = blendable;
288    mNativePixelFormatCapsTable[id].multisample     = multisample;
289    mNativePixelFormatCapsTable[id].resolve         = resolve;
290    mNativePixelFormatCapsTable[id].pixelBytes      = pixelBytes;
291    mNativePixelFormatCapsTable[id].pixelBytesMSAA  = pixelBytes;
292    mNativePixelFormatCapsTable[id].channels        = channels;
293    if (channels != 0)
294        mNativePixelFormatCapsTable[id].alignment = MAX(pixelBytes / channels, 1U);
295}
296
297void FormatTable::setCompressedFormatCaps(MTLPixelFormat formatId, bool filterable)
298{
299    setFormatCaps(formatId, filterable, false, false, false, false, false, false);
300}
301
302void FormatTable::adjustFormatCapsForDevice(const mtl::ContextDevice &device,
303                                            MTLPixelFormat id,
304                                            bool supportsiOS2,
305                                            bool supportsiOS4)
306{
307#if !(TARGET_OS_OSX || TARGET_OS_MACCATALYST)
308
309    NSUInteger pixelBytesRender     = mNativePixelFormatCapsTable[id].pixelBytes;
310    NSUInteger pixelBytesRenderMSAA = mNativePixelFormatCapsTable[id].pixelBytesMSAA;
311    NSUInteger alignment            = mNativePixelFormatCapsTable[id].alignment;
312
313// Override the current pixelBytesRender
314#    define SPECIFIC(_pixelFormat, _pixelBytesRender)                                            \
315        case _pixelFormat:                                                                       \
316            pixelBytesRender     = _pixelBytesRender;                                            \
317            pixelBytesRenderMSAA = _pixelBytesRender;                                            \
318            alignment =                                                                          \
319                supportsiOS4 ? _pixelBytesRender / mNativePixelFormatCapsTable[id].channels : 4; \
320            break
321// Override the current pixel bytes render, and MSAA
322#    define SPECIFIC_MSAA(_pixelFormat, _pixelBytesRender, _pixelBytesRenderMSAA)                \
323        case _pixelFormat:                                                                       \
324            pixelBytesRender     = _pixelBytesRender;                                            \
325            pixelBytesRenderMSAA = _pixelBytesRenderMSAA;                                        \
326            alignment =                                                                          \
327                supportsiOS4 ? _pixelBytesRender / mNativePixelFormatCapsTable[id].channels : 4; \
328            break
329// Override the current pixelBytesRender, and alignment
330#    define SPECIFIC_ALIGN(_pixelFormat, _pixelBytesRender, _alignment) \
331        case _pixelFormat:                                              \
332            pixelBytesRender     = _pixelBytesRender;                   \
333            pixelBytesRenderMSAA = _pixelBytesRender;                   \
334            alignment            = _alignment;                          \
335            break
336
337    if (!mNativePixelFormatCapsTable[id].compressed)
338    {
339        // On AppleGPUFamily4+, there is no 4byte minimum requirement for render targets
340        uint32_t minSize     = supportsiOS4 ? 1U : 4U;
341        pixelBytesRender     = MAX(mNativePixelFormatCapsTable[id].pixelBytes, minSize);
342        pixelBytesRenderMSAA = pixelBytesRender;
343        alignment =
344            supportsiOS4 ? MAX(pixelBytesRender / mNativePixelFormatCapsTable[id].channels, 1U) : 4;
345    }
346
347    // This list of tables starts from a general multi-platform table,
348    // to specific platforms (i.e. ios2, ios4) inheriting from the previous tables
349
350    // Start off with the general case
351    switch ((NSUInteger)id)
352    {
353        SPECIFIC(MTLPixelFormatB5G6R5Unorm, 4U);
354        SPECIFIC(MTLPixelFormatA1BGR5Unorm, 4U);
355        SPECIFIC(MTLPixelFormatABGR4Unorm, 4U);
356        SPECIFIC(MTLPixelFormatBGR5A1Unorm, 4U);
357
358        SPECIFIC(MTLPixelFormatRGBA8Unorm, 4U);
359        SPECIFIC(MTLPixelFormatBGRA8Unorm, 4U);
360
361        SPECIFIC_MSAA(MTLPixelFormatRGBA8Unorm_sRGB, 4U, 8U);
362        SPECIFIC_MSAA(MTLPixelFormatBGRA8Unorm_sRGB, 4U, 8U);
363        SPECIFIC_MSAA(MTLPixelFormatRGBA8Snorm, 4U, 8U);
364        SPECIFIC_MSAA(MTLPixelFormatRGB10A2Uint, 4U, 8U);
365
366        SPECIFIC(MTLPixelFormatRGB10A2Unorm, 8U);
367        SPECIFIC(MTLPixelFormatBGR10A2Unorm, 8U);
368
369        SPECIFIC(MTLPixelFormatRG11B10Float, 8U);
370
371        SPECIFIC(MTLPixelFormatRGB9E5Float, 8U);
372
373        SPECIFIC(MTLPixelFormatStencil8, 1U);
374    }
375
376    // Override based ios2
377    if (supportsiOS2)
378    {
379        switch ((NSUInteger)id)
380        {
381            SPECIFIC(MTLPixelFormatB5G6R5Unorm, 8U);
382            SPECIFIC(MTLPixelFormatA1BGR5Unorm, 8U);
383            SPECIFIC(MTLPixelFormatABGR4Unorm, 8U);
384            SPECIFIC(MTLPixelFormatBGR5A1Unorm, 8U);
385            SPECIFIC_MSAA(MTLPixelFormatRGBA8Unorm, 4U, 8U);
386            SPECIFIC_MSAA(MTLPixelFormatBGRA8Unorm, 4U, 8U);
387        }
388    }
389
390    // Override based on ios4
391    if (supportsiOS4)
392    {
393        switch ((NSUInteger)id)
394        {
395            SPECIFIC_ALIGN(MTLPixelFormatB5G6R5Unorm, 6U, 2U);
396            SPECIFIC(MTLPixelFormatRGBA8Unorm, 4U);
397            SPECIFIC(MTLPixelFormatBGRA8Unorm, 4U);
398
399            SPECIFIC(MTLPixelFormatRGBA8Unorm_sRGB, 4U);
400            SPECIFIC(MTLPixelFormatBGRA8Unorm_sRGB, 4U);
401
402            SPECIFIC(MTLPixelFormatRGBA8Snorm, 4U);
403
404            SPECIFIC_ALIGN(MTLPixelFormatRGB10A2Unorm, 4U, 4U);
405            SPECIFIC_ALIGN(MTLPixelFormatBGR10A2Unorm, 4U, 4U);
406            SPECIFIC(MTLPixelFormatRGB10A2Uint, 8U);
407
408            SPECIFIC_ALIGN(MTLPixelFormatRG11B10Float, 4U, 4U);
409
410            SPECIFIC_ALIGN(MTLPixelFormatRGB9E5Float, 4U, 4U);
411        }
412    }
413    mNativePixelFormatCapsTable[id].pixelBytes     = pixelBytesRender;
414    mNativePixelFormatCapsTable[id].pixelBytesMSAA = pixelBytesRenderMSAA;
415    mNativePixelFormatCapsTable[id].alignment      = alignment;
416
417#    undef SPECIFIC
418#    undef SPECIFIC_ALIGN
419#    undef SPECIFIC_MSAA
420#endif
421    // macOS does not need to perform any additoinal adjustment. These values are only used to check
422    // valid MRT sizes on iOS.
423}
424
425void FormatTable::initNativeFormatCaps(const DisplayMtl *display)
426{
427    initNativeFormatCapsAutogen(display);
428}
429
430}  // namespace mtl
431}  // namespace rx
432