• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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