1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "src/gpu/GrStencilSettings.h"
9#include "src/gpu/mtl/GrMtlDepthStencil.h"
10#include "src/gpu/mtl/GrMtlGpu.h"
11
12#if !__has_feature(objc_arc)
13#error This file must be compiled with Arc. Use -fobjc-arc flag
14#endif
15
16GR_NORETAIN_BEGIN
17
18MTLStencilOperation skia_stencil_op_to_mtl(GrStencilOp op) {
19    switch (op) {
20        case GrStencilOp::kKeep:
21            return MTLStencilOperationKeep;
22        case GrStencilOp::kZero:
23            return MTLStencilOperationZero;
24        case GrStencilOp::kReplace:
25            return MTLStencilOperationReplace;
26        case GrStencilOp::kInvert:
27            return MTLStencilOperationInvert;
28        case GrStencilOp::kIncWrap:
29            return MTLStencilOperationIncrementWrap;
30        case GrStencilOp::kDecWrap:
31            return MTLStencilOperationDecrementWrap;
32        case GrStencilOp::kIncClamp:
33            return MTLStencilOperationIncrementClamp;
34        case GrStencilOp::kDecClamp:
35            return MTLStencilOperationDecrementClamp;
36    }
37}
38
39MTLStencilDescriptor* skia_stencil_to_mtl(GrStencilSettings::Face face) {
40    MTLStencilDescriptor* result = [[MTLStencilDescriptor alloc] init];
41    switch (face.fTest) {
42        case GrStencilTest::kAlways:
43            result.stencilCompareFunction = MTLCompareFunctionAlways;
44            break;
45        case GrStencilTest::kNever:
46            result.stencilCompareFunction = MTLCompareFunctionNever;
47            break;
48        case GrStencilTest::kGreater:
49            result.stencilCompareFunction = MTLCompareFunctionGreater;
50            break;
51        case GrStencilTest::kGEqual:
52            result.stencilCompareFunction = MTLCompareFunctionGreaterEqual;
53            break;
54        case GrStencilTest::kLess:
55            result.stencilCompareFunction = MTLCompareFunctionLess;
56            break;
57        case GrStencilTest::kLEqual:
58            result.stencilCompareFunction = MTLCompareFunctionLessEqual;
59            break;
60        case GrStencilTest::kEqual:
61            result.stencilCompareFunction = MTLCompareFunctionEqual;
62            break;
63        case GrStencilTest::kNotEqual:
64            result.stencilCompareFunction = MTLCompareFunctionNotEqual;
65            break;
66    }
67    result.readMask = face.fTestMask;
68    result.writeMask = face.fWriteMask;
69    result.depthStencilPassOperation = skia_stencil_op_to_mtl(face.fPassOp);
70    result.stencilFailureOperation = skia_stencil_op_to_mtl(face.fFailOp);
71    return result;
72}
73
74GrMtlDepthStencil* GrMtlDepthStencil::Create(const GrMtlGpu* gpu,
75                                             const GrStencilSettings& stencil,
76                                             GrSurfaceOrigin origin) {
77    MTLDepthStencilDescriptor* desc = [[MTLDepthStencilDescriptor alloc] init];
78    if (!stencil.isDisabled()) {
79        if (stencil.isTwoSided()) {
80            desc.frontFaceStencil = skia_stencil_to_mtl(stencil.postOriginCCWFace(origin));
81            desc.backFaceStencil = skia_stencil_to_mtl(stencil.postOriginCWFace(origin));
82        }
83        else {
84            desc.frontFaceStencil = skia_stencil_to_mtl(stencil.singleSidedFace());
85            desc.backFaceStencil = desc.frontFaceStencil;
86        }
87    }
88
89    return new GrMtlDepthStencil([gpu->device() newDepthStencilStateWithDescriptor: desc],
90                                 GenerateKey(stencil, origin));
91}
92
93void skia_stencil_to_key(GrStencilSettings::Face face, GrMtlDepthStencil::Key::Face* faceKey) {
94    const int kPassOpShift = 3;
95    const int kFailOpShift = 6;
96
97    faceKey->fReadMask = face.fTestMask;
98    faceKey->fWriteMask = face.fWriteMask;
99
100    SkASSERT(static_cast<int>(face.fTest) <= 7);
101    faceKey->fOps = static_cast<uint32_t>(face.fTest);
102
103    SkASSERT(static_cast<int>(face.fPassOp) <= 7);
104    faceKey->fOps |= (static_cast<uint32_t>(face.fPassOp) << kPassOpShift);
105
106    SkASSERT(static_cast<int>(face.fFailOp) <= 7);
107    faceKey->fOps |= (static_cast<uint32_t>(face.fFailOp) << kFailOpShift);
108}
109
110GrMtlDepthStencil::Key GrMtlDepthStencil::GenerateKey(const GrStencilSettings& stencil,
111                                                      GrSurfaceOrigin origin) {
112    Key depthStencilKey;
113
114    if (stencil.isDisabled()) {
115        memset(&depthStencilKey, 0, sizeof(Key));
116    } else {
117        if (stencil.isTwoSided()) {
118            skia_stencil_to_key(stencil.postOriginCCWFace(origin), &depthStencilKey.fFront);
119            skia_stencil_to_key(stencil.postOriginCWFace(origin), &depthStencilKey.fBack);
120        }
121        else {
122            skia_stencil_to_key(stencil.singleSidedFace(), &depthStencilKey.fFront);
123            memcpy(&depthStencilKey.fBack, &depthStencilKey.fFront, sizeof(Key::Face));
124        }
125    }
126
127    return depthStencilKey;
128}
129
130GR_NORETAIN_END
131