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