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
24{
25
26bool OverrideTextureCaps(const DisplayMtl *display, angle::FormatID formatId, gl::TextureCaps *caps)
27{
28    // NOTE(hqle): Auto generate this.
29    switch (formatId)
30    {
31        // NOTE: even though iOS devices don't support filtering depth textures, we still report as
32        // supported here in order for the OES_depth_texture extension to be enabled.
33        // During draw call, the filter modes will be converted to nearest.
34        case angle::FormatID::D16_UNORM:
35        case angle::FormatID::D24_UNORM_S8_UINT:
36        case angle::FormatID::D32_FLOAT_S8X24_UINT:
37        case angle::FormatID::D32_FLOAT:
38        case angle::FormatID::D32_UNORM:
39            caps->texturable = caps->filterable = caps->textureAttachment = caps->renderbuffer =
40                true;
41            return true;
42        default:
43            // NOTE(hqle): Handle more cases
44            return false;
45    }
46}
47
48void GenerateTextureCapsMap(const FormatTable &formatTable,
49                            const DisplayMtl *display,
50                            gl::TextureCapsMap *capsMapOut,
51                            std::vector<GLenum> *compressedFormatsOut,
52                            uint32_t *maxSamplesOut)
53{
54    auto &textureCapsMap    = *capsMapOut;
55    auto &compressedFormats = *compressedFormatsOut;
56
57    compressedFormats.clear();
58
59    auto formatVerifier = [&](const gl::InternalFormat &internalFormatInfo) {
60        angle::FormatID angleFormatId =
61            angle::Format::InternalFormatToID(internalFormatInfo.sizedInternalFormat);
62        const Format &mtlFormat = formatTable.getPixelFormat(angleFormatId);
63        if (!mtlFormat.valid())
64        {
65            return;
66        }
67        const FormatCaps &formatCaps = mtlFormat.getCaps();
68
69        const angle::Format &intendedAngleFormat = mtlFormat.intendedAngleFormat();
70        gl::TextureCaps textureCaps;
71
72        // First let check whether we can override certain special cases.
73        if (!OverrideTextureCaps(display, mtlFormat.intendedFormatId, &textureCaps))
74        {
75            // Fill the texture caps using pixel format's caps
76            textureCaps.filterable = mtlFormat.getCaps().filterable;
77            textureCaps.renderbuffer =
78                mtlFormat.getCaps().colorRenderable || mtlFormat.getCaps().depthRenderable;
79            textureCaps.texturable        = true;
80            textureCaps.blendable         = mtlFormat.getCaps().blendable;
81            textureCaps.textureAttachment = textureCaps.renderbuffer;
82        }
83
84        if (formatCaps.multisample)
85        {
86            constexpr uint32_t sampleCounts[] = {2, 4, 8};
87            for (auto sampleCount : sampleCounts)
88            {
89                if ([display->getMetalDevice() supportsTextureSampleCount:sampleCount])
90                {
91                    textureCaps.sampleCounts.insert(sampleCount);
92                    *maxSamplesOut = std::max(*maxSamplesOut, sampleCount);
93                }
94            }
95        }
96
97        textureCapsMap.set(mtlFormat.intendedFormatId, textureCaps);
98
99        if (intendedAngleFormat.isBlock)
100        {
101            compressedFormats.push_back(intendedAngleFormat.glInternalFormat);
102        }
103    };
104
105    // Texture caps map.
106    const gl::FormatSet &internalFormats = gl::GetAllSizedInternalFormats();
107    for (const auto internalFormat : internalFormats)
108    {
109        const gl::InternalFormat &internalFormatInfo =
110            gl::GetSizedInternalFormatInfo(internalFormat);
111
112        formatVerifier(internalFormatInfo);
113    }
114}
115
116}  // namespace
117
118// FormatBase implementation
119const angle::Format &FormatBase::actualAngleFormat() const
120{
121    return angle::Format::Get(actualFormatId);
122}
123
124const angle::Format &FormatBase::intendedAngleFormat() const
125{
126    return angle::Format::Get(intendedFormatId);
127}
128
129// Format implementation
130const gl::InternalFormat &Format::intendedInternalFormat() const
131{
132    return gl::GetSizedInternalFormatInfo(intendedAngleFormat().glInternalFormat);
133}
134
135const gl::InternalFormat &Format::actualInternalFormat() const
136{
137    return gl::GetSizedInternalFormatInfo(actualAngleFormat().glInternalFormat);
138}
139
140bool Format::needConversion(angle::FormatID srcFormatId) const
141{
142    if ((srcFormatId == angle::FormatID::BC1_RGB_UNORM_BLOCK &&
143         actualFormatId == angle::FormatID::BC1_RGBA_UNORM_BLOCK) ||
144        (srcFormatId == angle::FormatID::BC1_RGB_UNORM_SRGB_BLOCK &&
145         actualFormatId == angle::FormatID::BC1_RGBA_UNORM_SRGB_BLOCK))
146    {
147        // When texture swizzling is available, DXT1 RGB format will be swizzled with RGB1.
148        // WebGL allows unswizzled mapping when swizzling is not available. No need to convert.
149        return false;
150    }
151    if (srcFormatId == angle::FormatID::ETC1_R8G8B8_UNORM_BLOCK &&
152        actualFormatId == angle::FormatID::ETC2_R8G8B8_UNORM_BLOCK)
153    {
154        // ETC1 RGB & ETC2 RGB are technically the same.
155        return false;
156    }
157    return srcFormatId != actualFormatId;
158}
159
160bool Format::isPVRTC() const
161{
162    switch (metalFormat)
163    {
164#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
165        case MTLPixelFormatPVRTC_RGB_2BPP:
166        case MTLPixelFormatPVRTC_RGB_2BPP_sRGB:
167        case MTLPixelFormatPVRTC_RGB_4BPP:
168        case MTLPixelFormatPVRTC_RGB_4BPP_sRGB:
169        case MTLPixelFormatPVRTC_RGBA_2BPP:
170        case MTLPixelFormatPVRTC_RGBA_2BPP_sRGB:
171        case MTLPixelFormatPVRTC_RGBA_4BPP:
172        case MTLPixelFormatPVRTC_RGBA_4BPP_sRGB:
173            return true;
174#endif
175        default:
176            return false;
177    }
178}
179
180// FormatTable implementation
181angle::Result FormatTable::initialize(const DisplayMtl *display)
182{
183    mMaxSamples = 0;
184
185    // Initialize native format caps
186    initNativeFormatCaps(display);
187
188    for (size_t i = 0; i < angle::kNumANGLEFormats; ++i)
189    {
190        const auto formatId = static_cast<angle::FormatID>(i);
191
192        mPixelFormatTable[i].init(display, formatId);
193        mPixelFormatTable[i].caps = &mNativePixelFormatCapsTable[mPixelFormatTable[i].metalFormat];
194
195        if (!mPixelFormatTable[i].caps->depthRenderable &&
196            mPixelFormatTable[i].actualFormatId != mPixelFormatTable[i].intendedFormatId)
197        {
198            mPixelFormatTable[i].textureLoadFunctions = angle::GetLoadFunctionsMap(
199                mPixelFormatTable[i].intendedAngleFormat().glInternalFormat,
200                mPixelFormatTable[i].actualFormatId);
201        }
202
203        mVertexFormatTables[0][i].init(formatId, false);
204        mVertexFormatTables[1][i].init(formatId, true);
205    }
206
207    return angle::Result::Continue;
208}
209
210void FormatTable::generateTextureCaps(const DisplayMtl *display,
211                                      gl::TextureCapsMap *capsMapOut,
212                                      std::vector<GLenum> *compressedFormatsOut)
213{
214    GenerateTextureCapsMap(*this, display, capsMapOut, compressedFormatsOut, &mMaxSamples);
215}
216
217const Format &FormatTable::getPixelFormat(angle::FormatID angleFormatId) const
218{
219    return mPixelFormatTable[static_cast<size_t>(angleFormatId)];
220}
221const FormatCaps &FormatTable::getNativeFormatCaps(MTLPixelFormat mtlFormat) const
222{
223    ASSERT(mNativePixelFormatCapsTable.count(mtlFormat));
224    return mNativePixelFormatCapsTable.at(mtlFormat);
225}
226const VertexFormat &FormatTable::getVertexFormat(angle::FormatID angleFormatId,
227                                                 bool tightlyPacked) const
228{
229    auto tableIdx = tightlyPacked ? 1 : 0;
230    return mVertexFormatTables[tableIdx][static_cast<size_t>(angleFormatId)];
231}
232
233void FormatTable::setFormatCaps(MTLPixelFormat formatId,
234                                bool filterable,
235                                bool writable,
236                                bool blendable,
237                                bool multisample,
238                                bool resolve,
239                                bool colorRenderable)
240{
241    setFormatCaps(formatId, filterable, writable, blendable, multisample, resolve, colorRenderable,
242                  false);
243}
244
245void FormatTable::setFormatCaps(MTLPixelFormat id,
246                                bool filterable,
247                                bool writable,
248                                bool blendable,
249                                bool multisample,
250                                bool resolve,
251                                bool colorRenderable,
252                                bool depthRenderable)
253{
254    mNativePixelFormatCapsTable[id].filterable      = filterable;
255    mNativePixelFormatCapsTable[id].writable        = writable;
256    mNativePixelFormatCapsTable[id].colorRenderable = colorRenderable;
257    mNativePixelFormatCapsTable[id].depthRenderable = depthRenderable;
258    mNativePixelFormatCapsTable[id].blendable       = blendable;
259    mNativePixelFormatCapsTable[id].multisample     = multisample;
260    mNativePixelFormatCapsTable[id].resolve         = resolve;
261}
262
263void FormatTable::setCompressedFormatCaps(MTLPixelFormat formatId, bool filterable)
264{
265    setFormatCaps(formatId, filterable, false, false, false, false, false, false);
266}
267
268void FormatTable::initNativeFormatCaps(const DisplayMtl *display)
269{
270    initNativeFormatCapsAutogen(display);
271}
272
273}  // namespace mtl
274}  // namespace rx
275