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#if (TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || \ 169 (TARGET_OS_OSX && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 110000)) 170 case MTLPixelFormatPVRTC_RGB_2BPP: 171 case MTLPixelFormatPVRTC_RGB_2BPP_sRGB: 172 case MTLPixelFormatPVRTC_RGB_4BPP: 173 case MTLPixelFormatPVRTC_RGB_4BPP_sRGB: 174 case MTLPixelFormatPVRTC_RGBA_2BPP: 175 case MTLPixelFormatPVRTC_RGBA_2BPP_sRGB: 176 case MTLPixelFormatPVRTC_RGBA_4BPP: 177 case MTLPixelFormatPVRTC_RGBA_4BPP_sRGB: 178 return true; 179#endif 180 default: 181 return false; 182 } 183} 184 185// FormatTable implementation 186angle::Result FormatTable::initialize(const DisplayMtl *display) 187{ 188 mMaxSamples = 0; 189 190 // Initialize native format caps 191 initNativeFormatCaps(display); 192 193 for (size_t i = 0; i < angle::kNumANGLEFormats; ++i) 194 { 195 const auto formatId = static_cast<angle::FormatID>(i); 196 197 mPixelFormatTable[i].init(display, formatId); 198 mPixelFormatTable[i].caps = mNativePixelFormatCapsTable[mPixelFormatTable[i].metalFormat]; 199 200 if (mPixelFormatTable[i].actualFormatId != mPixelFormatTable[i].intendedFormatId) 201 { 202 mPixelFormatTable[i].textureLoadFunctions = angle::GetLoadFunctionsMap( 203 mPixelFormatTable[i].intendedAngleFormat().glInternalFormat, 204 mPixelFormatTable[i].actualFormatId); 205 } 206 207 mVertexFormatTables[0][i].init(formatId, false); 208 mVertexFormatTables[1][i].init(formatId, true); 209 } 210 211 // TODO(anglebug.com/5505): unmerged change from WebKit was here - 212 // D24S8 fallback to D32_FLOAT_S8X24_UINT, since removed. 213 214 return angle::Result::Continue; 215} 216 217void FormatTable::generateTextureCaps(const DisplayMtl *display, gl::TextureCapsMap *capsMapOut) 218{ 219 GenerateTextureCapsMap(*this, display, capsMapOut, &mMaxSamples); 220} 221 222const Format &FormatTable::getPixelFormat(angle::FormatID angleFormatId) const 223{ 224 return mPixelFormatTable[static_cast<size_t>(angleFormatId)]; 225} 226const FormatCaps &FormatTable::getNativeFormatCaps(MTLPixelFormat mtlFormat) const 227{ 228 ASSERT(mNativePixelFormatCapsTable.count(mtlFormat)); 229 return mNativePixelFormatCapsTable.at(mtlFormat); 230} 231const VertexFormat &FormatTable::getVertexFormat(angle::FormatID angleFormatId, 232 bool tightlyPacked) const 233{ 234 auto tableIdx = tightlyPacked ? 1 : 0; 235 return mVertexFormatTables[tableIdx][static_cast<size_t>(angleFormatId)]; 236} 237 238void FormatTable::setFormatCaps(MTLPixelFormat formatId, 239 bool filterable, 240 bool writable, 241 bool blendable, 242 bool multisample, 243 bool resolve, 244 bool colorRenderable) 245{ 246 setFormatCaps(formatId, filterable, writable, blendable, multisample, resolve, colorRenderable, 247 false, 0); 248} 249void FormatTable::setFormatCaps(MTLPixelFormat formatId, 250 bool filterable, 251 bool writable, 252 bool blendable, 253 bool multisample, 254 bool resolve, 255 bool colorRenderable, 256 NSUInteger pixelBytes, 257 NSUInteger channels) 258{ 259 setFormatCaps(formatId, filterable, writable, blendable, multisample, resolve, colorRenderable, 260 false, pixelBytes, channels); 261} 262 263void FormatTable::setFormatCaps(MTLPixelFormat formatId, 264 bool filterable, 265 bool writable, 266 bool blendable, 267 bool multisample, 268 bool resolve, 269 bool colorRenderable, 270 bool depthRenderable) 271{ 272 setFormatCaps(formatId, filterable, writable, blendable, multisample, resolve, colorRenderable, 273 depthRenderable, 0, 0); 274} 275void FormatTable::setFormatCaps(MTLPixelFormat id, 276 bool filterable, 277 bool writable, 278 bool blendable, 279 bool multisample, 280 bool resolve, 281 bool colorRenderable, 282 bool depthRenderable, 283 NSUInteger pixelBytes, 284 NSUInteger channels) 285{ 286 mNativePixelFormatCapsTable[id].filterable = filterable; 287 mNativePixelFormatCapsTable[id].writable = writable; 288 mNativePixelFormatCapsTable[id].colorRenderable = colorRenderable; 289 mNativePixelFormatCapsTable[id].depthRenderable = depthRenderable; 290 mNativePixelFormatCapsTable[id].blendable = blendable; 291 mNativePixelFormatCapsTable[id].multisample = multisample; 292 mNativePixelFormatCapsTable[id].resolve = resolve; 293 mNativePixelFormatCapsTable[id].pixelBytes = pixelBytes; 294 mNativePixelFormatCapsTable[id].pixelBytesMSAA = pixelBytes; 295 mNativePixelFormatCapsTable[id].channels = channels; 296 if (channels != 0) 297 mNativePixelFormatCapsTable[id].alignment = MAX(pixelBytes / channels, 1U); 298} 299 300void FormatTable::setCompressedFormatCaps(MTLPixelFormat formatId, bool filterable) 301{ 302 setFormatCaps(formatId, filterable, false, false, false, false, false, false); 303} 304 305void FormatTable::adjustFormatCapsForDevice(const mtl::ContextDevice &device, 306 MTLPixelFormat id, 307 bool supportsiOS2, 308 bool supportsiOS4) 309{ 310#if !(TARGET_OS_OSX || TARGET_OS_MACCATALYST) 311 312 NSUInteger pixelBytesRender = mNativePixelFormatCapsTable[id].pixelBytes; 313 NSUInteger pixelBytesRenderMSAA = mNativePixelFormatCapsTable[id].pixelBytesMSAA; 314 NSUInteger alignment = mNativePixelFormatCapsTable[id].alignment; 315 316// Override the current pixelBytesRender 317# define SPECIFIC(_pixelFormat, _pixelBytesRender) \ 318 case _pixelFormat: \ 319 pixelBytesRender = _pixelBytesRender; \ 320 pixelBytesRenderMSAA = _pixelBytesRender; \ 321 alignment = \ 322 supportsiOS4 ? _pixelBytesRender / mNativePixelFormatCapsTable[id].channels : 4; \ 323 break 324// Override the current pixel bytes render, and MSAA 325# define SPECIFIC_MSAA(_pixelFormat, _pixelBytesRender, _pixelBytesRenderMSAA) \ 326 case _pixelFormat: \ 327 pixelBytesRender = _pixelBytesRender; \ 328 pixelBytesRenderMSAA = _pixelBytesRenderMSAA; \ 329 alignment = \ 330 supportsiOS4 ? _pixelBytesRender / mNativePixelFormatCapsTable[id].channels : 4; \ 331 break 332// Override the current pixelBytesRender, and alignment 333# define SPECIFIC_ALIGN(_pixelFormat, _pixelBytesRender, _alignment) \ 334 case _pixelFormat: \ 335 pixelBytesRender = _pixelBytesRender; \ 336 pixelBytesRenderMSAA = _pixelBytesRender; \ 337 alignment = _alignment; \ 338 break 339 340 if (!mNativePixelFormatCapsTable[id].compressed) 341 { 342 // On AppleGPUFamily4+, there is no 4byte minimum requirement for render targets 343 uint32_t minSize = supportsiOS4 ? 1U : 4U; 344 pixelBytesRender = MAX(mNativePixelFormatCapsTable[id].pixelBytes, minSize); 345 pixelBytesRenderMSAA = pixelBytesRender; 346 alignment = 347 supportsiOS4 ? MAX(pixelBytesRender / mNativePixelFormatCapsTable[id].channels, 1U) : 4; 348 } 349 350 // This list of tables starts from a general multi-platform table, 351 // to specific platforms (i.e. ios2, ios4) inheriting from the previous tables 352 353 // Start off with the general case 354 switch ((NSUInteger)id) 355 { 356 SPECIFIC(MTLPixelFormatB5G6R5Unorm, 4U); 357 SPECIFIC(MTLPixelFormatA1BGR5Unorm, 4U); 358 SPECIFIC(MTLPixelFormatABGR4Unorm, 4U); 359 SPECIFIC(MTLPixelFormatBGR5A1Unorm, 4U); 360 361 SPECIFIC(MTLPixelFormatRGBA8Unorm, 4U); 362 SPECIFIC(MTLPixelFormatBGRA8Unorm, 4U); 363 364 SPECIFIC_MSAA(MTLPixelFormatRGBA8Unorm_sRGB, 4U, 8U); 365 SPECIFIC_MSAA(MTLPixelFormatBGRA8Unorm_sRGB, 4U, 8U); 366 SPECIFIC_MSAA(MTLPixelFormatRGBA8Snorm, 4U, 8U); 367 SPECIFIC_MSAA(MTLPixelFormatRGB10A2Uint, 4U, 8U); 368 369 SPECIFIC(MTLPixelFormatRGB10A2Unorm, 8U); 370 SPECIFIC(MTLPixelFormatBGR10A2Unorm, 8U); 371 372 SPECIFIC(MTLPixelFormatRG11B10Float, 8U); 373 374 SPECIFIC(MTLPixelFormatRGB9E5Float, 8U); 375 376 SPECIFIC(MTLPixelFormatStencil8, 1U); 377 } 378 379 // Override based ios2 380 if (supportsiOS2) 381 { 382 switch ((NSUInteger)id) 383 { 384 SPECIFIC(MTLPixelFormatB5G6R5Unorm, 8U); 385 SPECIFIC(MTLPixelFormatA1BGR5Unorm, 8U); 386 SPECIFIC(MTLPixelFormatABGR4Unorm, 8U); 387 SPECIFIC(MTLPixelFormatBGR5A1Unorm, 8U); 388 SPECIFIC_MSAA(MTLPixelFormatRGBA8Unorm, 4U, 8U); 389 SPECIFIC_MSAA(MTLPixelFormatBGRA8Unorm, 4U, 8U); 390 } 391 } 392 393 // Override based on ios4 394 if (supportsiOS4) 395 { 396 switch ((NSUInteger)id) 397 { 398 SPECIFIC_ALIGN(MTLPixelFormatB5G6R5Unorm, 6U, 2U); 399 SPECIFIC(MTLPixelFormatRGBA8Unorm, 4U); 400 SPECIFIC(MTLPixelFormatBGRA8Unorm, 4U); 401 402 SPECIFIC(MTLPixelFormatRGBA8Unorm_sRGB, 4U); 403 SPECIFIC(MTLPixelFormatBGRA8Unorm_sRGB, 4U); 404 405 SPECIFIC(MTLPixelFormatRGBA8Snorm, 4U); 406 407 SPECIFIC_ALIGN(MTLPixelFormatRGB10A2Unorm, 4U, 4U); 408 SPECIFIC_ALIGN(MTLPixelFormatBGR10A2Unorm, 4U, 4U); 409 SPECIFIC(MTLPixelFormatRGB10A2Uint, 8U); 410 411 SPECIFIC_ALIGN(MTLPixelFormatRG11B10Float, 4U, 4U); 412 413 SPECIFIC_ALIGN(MTLPixelFormatRGB9E5Float, 4U, 4U); 414 } 415 } 416 mNativePixelFormatCapsTable[id].pixelBytes = pixelBytesRender; 417 mNativePixelFormatCapsTable[id].pixelBytesMSAA = pixelBytesRenderMSAA; 418 mNativePixelFormatCapsTable[id].alignment = alignment; 419 420# undef SPECIFIC 421# undef SPECIFIC_ALIGN 422# undef SPECIFIC_MSAA 423#endif 424 // macOS does not need to perform any additoinal adjustment. These values are only used to check 425 // valid MRT sizes on iOS. 426} 427 428void FormatTable::initNativeFormatCaps(const DisplayMtl *display) 429{ 430 initNativeFormatCapsAutogen(display); 431} 432 433} // namespace mtl 434} // namespace rx 435