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_utils.mm: 7// Implements utilities functions that create Metal shaders, convert from angle enums 8// to Metal enums and so on. 9// 10 11#include "libANGLE/renderer/metal/mtl_utils.h" 12 13#include <TargetConditionals.h> 14 15#include "common/MemoryBuffer.h" 16#include "libANGLE/renderer/metal/ContextMtl.h" 17 18namespace rx 19{ 20namespace mtl 21{ 22 23angle::Result InitializeTextureContents(const gl::Context *context, 24 const TextureRef &texture, 25 const Format &textureObjFormat, 26 const gl::ImageIndex &index) 27{ 28 ASSERT(texture && texture->valid()); 29 ASSERT(texture->textureType() == MTLTextureType2D || 30 texture->textureType() == MTLTextureTypeCube); 31 ContextMtl *contextMtl = mtl::GetImpl(context); 32 33 const gl::InternalFormat &intendedInternalFormat = textureObjFormat.intendedInternalFormat(); 34 35 // This function is called in many places to initialize the content of a texture. 36 // So it's better we do the sanity check here instead of let the callers do it themselves: 37 if (!textureObjFormat.valid() || intendedInternalFormat.compressed || 38 intendedInternalFormat.depthBits > 0 || intendedInternalFormat.stencilBits > 0) 39 { 40 return angle::Result::Continue; 41 } 42 43 gl::Extents size = texture->size(index); 44 45 // Intialize the content to black 46 const angle::Format &srcFormat = 47 angle::Format::Get(intendedInternalFormat.alphaBits > 0 ? angle::FormatID::R8G8B8A8_UNORM 48 : angle::FormatID::R8G8B8_UNORM); 49 const size_t srcRowPitch = srcFormat.pixelBytes * size.width; 50 angle::MemoryBuffer srcRow; 51 ANGLE_CHECK_GL_ALLOC(contextMtl, srcRow.resize(srcRowPitch)); 52 memset(srcRow.data(), 0, srcRowPitch); 53 54 const angle::Format &dstFormat = angle::Format::Get(textureObjFormat.actualFormatId); 55 const size_t dstRowPitch = dstFormat.pixelBytes * size.width; 56 angle::MemoryBuffer conversionRow; 57 ANGLE_CHECK_GL_ALLOC(contextMtl, conversionRow.resize(dstRowPitch)); 58 59 CopyImageCHROMIUM(srcRow.data(), srcRowPitch, srcFormat.pixelBytes, 0, 60 srcFormat.pixelReadFunction, conversionRow.data(), dstRowPitch, 61 dstFormat.pixelBytes, 0, dstFormat.pixelWriteFunction, 62 intendedInternalFormat.format, dstFormat.componentType, size.width, 1, 1, 63 false, false, false); 64 65 auto mtlRowRegion = MTLRegionMake2D(0, 0, size.width, 1); 66 67 for (NSUInteger r = 0; r < static_cast<NSUInteger>(size.height); ++r) 68 { 69 mtlRowRegion.origin.y = r; 70 71 // Upload to texture 72 texture->replaceRegion(contextMtl, mtlRowRegion, index.getLevelIndex(), 73 index.hasLayer() ? index.cubeMapFaceIndex() : 0, 74 conversionRow.data(), dstRowPitch); 75 } 76 77 return angle::Result::Continue; 78} 79 80MTLViewport GetViewport(const gl::Rectangle &rect, double znear, double zfar) 81{ 82 MTLViewport re; 83 84 re.originX = rect.x; 85 re.originY = rect.y; 86 re.width = rect.width; 87 re.height = rect.height; 88 re.znear = znear; 89 re.zfar = zfar; 90 91 return re; 92} 93 94MTLViewport GetViewportFlipY(const gl::Rectangle &rect, 95 NSUInteger screenHeight, 96 double znear, 97 double zfar) 98{ 99 MTLViewport re; 100 101 re.originX = rect.x; 102 re.originY = static_cast<double>(screenHeight) - rect.y1(); 103 re.width = rect.width; 104 re.height = rect.height; 105 re.znear = znear; 106 re.zfar = zfar; 107 108 return re; 109} 110 111MTLViewport GetViewport(const gl::Rectangle &rect, 112 NSUInteger screenHeight, 113 bool flipY, 114 double znear, 115 double zfar) 116{ 117 if (flipY) 118 { 119 return GetViewportFlipY(rect, screenHeight, znear, zfar); 120 } 121 122 return GetViewport(rect, znear, zfar); 123} 124 125MTLScissorRect GetScissorRect(const gl::Rectangle &rect, NSUInteger screenHeight, bool flipY) 126{ 127 MTLScissorRect re; 128 129 re.x = rect.x; 130 re.y = flipY ? (screenHeight - rect.y1()) : rect.y; 131 re.width = rect.width; 132 re.height = rect.height; 133 134 return re; 135} 136 137AutoObjCPtr<id<MTLLibrary>> CreateShaderLibrary(id<MTLDevice> metalDevice, 138 const std::string &source, 139 AutoObjCPtr<NSError *> *error) 140{ 141 return CreateShaderLibrary(metalDevice, source.c_str(), source.size(), error); 142} 143 144AutoObjCPtr<id<MTLLibrary>> CreateShaderLibrary(id<MTLDevice> metalDevice, 145 const char *source, 146 size_t sourceLen, 147 AutoObjCPtr<NSError *> *errorOut) 148{ 149 ANGLE_MTL_OBJC_SCOPE 150 { 151 NSError *nsError = nil; 152 auto nsSource = [[NSString alloc] initWithBytesNoCopy:const_cast<char *>(source) 153 length:sourceLen 154 encoding:NSUTF8StringEncoding 155 freeWhenDone:NO]; 156 auto options = [[[MTLCompileOptions alloc] init] ANGLE_MTL_AUTORELEASE]; 157 auto library = [metalDevice newLibraryWithSource:nsSource options:options error:&nsError]; 158 159 [nsSource ANGLE_MTL_AUTORELEASE]; 160 161 *errorOut = std::move(nsError); 162 163 return [library ANGLE_MTL_AUTORELEASE]; 164 } 165} 166 167AutoObjCPtr<id<MTLLibrary>> CreateShaderLibraryFromBinary(id<MTLDevice> metalDevice, 168 const uint8_t *binarySource, 169 size_t binarySourceLen, 170 AutoObjCPtr<NSError *> *errorOut) 171{ 172 ANGLE_MTL_OBJC_SCOPE 173 { 174 NSError *nsError = nil; 175 auto shaderSourceData = 176 dispatch_data_create(binarySource, binarySourceLen, dispatch_get_main_queue(), 177 ^{ 178 }); 179 180 auto library = [metalDevice newLibraryWithData:shaderSourceData error:&nsError]; 181 182 [shaderSourceData ANGLE_MTL_AUTORELEASE]; 183 184 *errorOut = std::move(nsError); 185 186 return [library ANGLE_MTL_AUTORELEASE]; 187 } 188} 189 190MTLTextureType GetTextureType(gl::TextureType glType) 191{ 192 switch (glType) 193 { 194 case gl::TextureType::_2D: 195 return MTLTextureType2D; 196 case gl::TextureType::CubeMap: 197 return MTLTextureTypeCube; 198 default: 199 return MTLTextureTypeInvalid; 200 } 201} 202 203MTLSamplerMinMagFilter GetFilter(GLenum filter) 204{ 205 switch (filter) 206 { 207 case GL_LINEAR_MIPMAP_LINEAR: 208 case GL_LINEAR_MIPMAP_NEAREST: 209 case GL_LINEAR: 210 return MTLSamplerMinMagFilterLinear; 211 case GL_NEAREST_MIPMAP_LINEAR: 212 case GL_NEAREST_MIPMAP_NEAREST: 213 case GL_NEAREST: 214 return MTLSamplerMinMagFilterNearest; 215 default: 216 UNIMPLEMENTED(); 217 return MTLSamplerMinMagFilterNearest; 218 } 219} 220 221MTLSamplerMipFilter GetMipmapFilter(GLenum filter) 222{ 223 switch (filter) 224 { 225 case GL_LINEAR: 226 case GL_NEAREST: 227 return MTLSamplerMipFilterNotMipmapped; 228 case GL_LINEAR_MIPMAP_LINEAR: 229 case GL_NEAREST_MIPMAP_LINEAR: 230 return MTLSamplerMipFilterLinear; 231 case GL_NEAREST_MIPMAP_NEAREST: 232 case GL_LINEAR_MIPMAP_NEAREST: 233 return MTLSamplerMipFilterNearest; 234 default: 235 UNIMPLEMENTED(); 236 return MTLSamplerMipFilterNotMipmapped; 237 } 238} 239 240MTLSamplerAddressMode GetSamplerAddressMode(GLenum wrap) 241{ 242 switch (wrap) 243 { 244 case GL_REPEAT: 245 return MTLSamplerAddressModeRepeat; 246 case GL_MIRRORED_REPEAT: 247 return MTLSamplerAddressModeMirrorRepeat; 248 case GL_CLAMP_TO_BORDER: 249 // ES doesn't have border support 250 return MTLSamplerAddressModeClampToEdge; 251 case GL_CLAMP_TO_EDGE: 252 return MTLSamplerAddressModeClampToEdge; 253 default: 254 UNIMPLEMENTED(); 255 return MTLSamplerAddressModeClampToEdge; 256 } 257} 258 259MTLBlendFactor GetBlendFactor(GLenum factor) 260{ 261 switch (factor) 262 { 263 case GL_ZERO: 264 return MTLBlendFactorZero; 265 case GL_ONE: 266 return MTLBlendFactorOne; 267 case GL_SRC_COLOR: 268 return MTLBlendFactorSourceColor; 269 case GL_DST_COLOR: 270 return MTLBlendFactorDestinationColor; 271 case GL_ONE_MINUS_SRC_COLOR: 272 return MTLBlendFactorOneMinusSourceColor; 273 case GL_SRC_ALPHA: 274 return MTLBlendFactorSourceAlpha; 275 case GL_ONE_MINUS_SRC_ALPHA: 276 return MTLBlendFactorOneMinusSourceAlpha; 277 case GL_DST_ALPHA: 278 return MTLBlendFactorDestinationAlpha; 279 case GL_ONE_MINUS_DST_ALPHA: 280 return MTLBlendFactorOneMinusDestinationAlpha; 281 case GL_ONE_MINUS_DST_COLOR: 282 return MTLBlendFactorOneMinusDestinationColor; 283 case GL_SRC_ALPHA_SATURATE: 284 return MTLBlendFactorSourceAlphaSaturated; 285 case GL_CONSTANT_COLOR: 286 return MTLBlendFactorBlendColor; 287 case GL_CONSTANT_ALPHA: 288 return MTLBlendFactorBlendAlpha; 289 case GL_ONE_MINUS_CONSTANT_COLOR: 290 return MTLBlendFactorOneMinusBlendColor; 291 case GL_ONE_MINUS_CONSTANT_ALPHA: 292 return MTLBlendFactorOneMinusBlendAlpha; 293 default: 294 UNREACHABLE(); 295 return MTLBlendFactorZero; 296 } 297} 298 299MTLBlendOperation GetBlendOp(GLenum op) 300{ 301 switch (op) 302 { 303 case GL_FUNC_ADD: 304 return MTLBlendOperationAdd; 305 case GL_FUNC_SUBTRACT: 306 return MTLBlendOperationSubtract; 307 case GL_FUNC_REVERSE_SUBTRACT: 308 return MTLBlendOperationReverseSubtract; 309 case GL_MIN: 310 return MTLBlendOperationMin; 311 case GL_MAX: 312 return MTLBlendOperationMax; 313 default: 314 UNREACHABLE(); 315 return MTLBlendOperationAdd; 316 } 317} 318 319MTLCompareFunction GetCompareFunc(GLenum func) 320{ 321 switch (func) 322 { 323 case GL_NEVER: 324 return MTLCompareFunctionNever; 325 case GL_ALWAYS: 326 return MTLCompareFunctionAlways; 327 case GL_LESS: 328 return MTLCompareFunctionLess; 329 case GL_LEQUAL: 330 return MTLCompareFunctionLessEqual; 331 case GL_EQUAL: 332 return MTLCompareFunctionEqual; 333 case GL_GREATER: 334 return MTLCompareFunctionGreater; 335 case GL_GEQUAL: 336 return MTLCompareFunctionGreaterEqual; 337 case GL_NOTEQUAL: 338 return MTLCompareFunctionNotEqual; 339 default: 340 UNREACHABLE(); 341 return MTLCompareFunctionAlways; 342 } 343} 344 345MTLStencilOperation GetStencilOp(GLenum op) 346{ 347 switch (op) 348 { 349 case GL_KEEP: 350 return MTLStencilOperationKeep; 351 case GL_ZERO: 352 return MTLStencilOperationZero; 353 case GL_REPLACE: 354 return MTLStencilOperationReplace; 355 case GL_INCR: 356 return MTLStencilOperationIncrementClamp; 357 case GL_DECR: 358 return MTLStencilOperationDecrementClamp; 359 case GL_INCR_WRAP: 360 return MTLStencilOperationIncrementWrap; 361 case GL_DECR_WRAP: 362 return MTLStencilOperationDecrementWrap; 363 case GL_INVERT: 364 return MTLStencilOperationInvert; 365 default: 366 UNREACHABLE(); 367 return MTLStencilOperationKeep; 368 } 369} 370 371MTLWinding GetFontfaceWinding(GLenum frontFaceMode, bool invert) 372{ 373 switch (frontFaceMode) 374 { 375 case GL_CW: 376 return invert ? MTLWindingCounterClockwise : MTLWindingClockwise; 377 case GL_CCW: 378 return invert ? MTLWindingClockwise : MTLWindingCounterClockwise; 379 default: 380 UNREACHABLE(); 381 return MTLWindingClockwise; 382 } 383} 384 385#if ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 386PrimitiveTopologyClass GetPrimitiveTopologyClass(gl::PrimitiveMode mode) 387{ 388 // NOTE(hqle): Support layered renderring in future. 389 // In non-layered rendering mode, unspecified is enough. 390 return MTLPrimitiveTopologyClassUnspecified; 391} 392#else // ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 393PrimitiveTopologyClass GetPrimitiveTopologyClass(gl::PrimitiveMode mode) 394{ 395 return kPrimitiveTopologyClassTriangle; 396} 397#endif 398 399MTLPrimitiveType GetPrimitiveType(gl::PrimitiveMode mode) 400{ 401 switch (mode) 402 { 403 case gl::PrimitiveMode::Triangles: 404 return MTLPrimitiveTypeTriangle; 405 case gl::PrimitiveMode::Points: 406 return MTLPrimitiveTypePoint; 407 case gl::PrimitiveMode::Lines: 408 return MTLPrimitiveTypeLine; 409 case gl::PrimitiveMode::LineStrip: 410 case gl::PrimitiveMode::LineLoop: 411 return MTLPrimitiveTypeLineStrip; 412 case gl::PrimitiveMode::TriangleStrip: 413 return MTLPrimitiveTypeTriangleStrip; 414 case gl::PrimitiveMode::TriangleFan: 415 // NOTE(hqle): Emulate triangle fan. 416 default: 417 return MTLPrimitiveTypeInvalid; 418 } 419} 420 421MTLIndexType GetIndexType(gl::DrawElementsType type) 422{ 423 switch (type) 424 { 425 case gl::DrawElementsType::UnsignedShort: 426 return MTLIndexTypeUInt16; 427 case gl::DrawElementsType::UnsignedInt: 428 return MTLIndexTypeUInt32; 429 case gl::DrawElementsType::UnsignedByte: 430 // NOTE(hqle): Convert to supported type 431 default: 432 return MTLIndexTypeInvalid; 433 } 434} 435 436MTLClearColor EmulatedAlphaClearColor(MTLClearColor color, MTLColorWriteMask colorMask) 437{ 438 MTLClearColor re = color; 439 440 if (!(colorMask & MTLColorWriteMaskAlpha)) 441 { 442 re.alpha = kEmulatedAlphaValue; 443 } 444 445 return re; 446} 447 448} // namespace mtl 449} // namespace rx 450