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/mtl/GrMtlPipelineState.h"
9
10#include "include/gpu/GrContext.h"
11#include "src/gpu/GrContextPriv.h"
12#include "src/gpu/GrPipeline.h"
13#include "src/gpu/GrRenderTarget.h"
14#include "src/gpu/GrRenderTargetPriv.h"
15#include "src/gpu/GrTexturePriv.h"
16#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
17#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
18#include "src/gpu/glsl/GrGLSLXferProcessor.h"
19#include "src/gpu/mtl/GrMtlBuffer.h"
20#include "src/gpu/mtl/GrMtlGpu.h"
21#include "src/gpu/mtl/GrMtlTexture.h"
22
23#if !__has_feature(objc_arc)
24#error This file must be compiled with Arc. Use -fobjc-arc flag
25#endif
26
27GrMtlPipelineState::SamplerBindings::SamplerBindings(const GrSamplerState& state,
28                                                     GrTexture* texture,
29                                                     GrMtlGpu* gpu)
30        : fTexture(static_cast<GrMtlTexture*>(texture)->mtlTexture()) {
31    fSampler = gpu->resourceProvider().findOrCreateCompatibleSampler(state);
32}
33
34GrMtlPipelineState::GrMtlPipelineState(
35        GrMtlGpu* gpu,
36        id<MTLRenderPipelineState> pipelineState,
37        MTLPixelFormat pixelFormat,
38        const GrGLSLBuiltinUniformHandles& builtinUniformHandles,
39        const UniformInfoArray& uniforms,
40        uint32_t uniformBufferSize,
41        uint32_t numSamplers,
42        std::unique_ptr<GrGLSLPrimitiveProcessor> geometryProcessor,
43        std::unique_ptr<GrGLSLXferProcessor> xferProcessor,
44        std::unique_ptr<std::unique_ptr<GrGLSLFragmentProcessor>[]> fragmentProcessors,
45        int fragmentProcessorCnt)
46        : fGpu(gpu)
47        , fPipelineState(pipelineState)
48        , fPixelFormat(pixelFormat)
49        , fBuiltinUniformHandles(builtinUniformHandles)
50        , fNumSamplers(numSamplers)
51        , fGeometryProcessor(std::move(geometryProcessor))
52        , fXferProcessor(std::move(xferProcessor))
53        , fFragmentProcessors(std::move(fragmentProcessors))
54        , fFragmentProcessorCnt(fragmentProcessorCnt)
55        , fDataManager(uniforms, uniformBufferSize) {
56    (void) fPixelFormat; // Suppress unused-var warning.
57}
58
59void GrMtlPipelineState::setData(const GrRenderTarget* renderTarget,
60                                 const GrProgramInfo& programInfo) {
61
62    // Note: the Metal backend currently only supports fixed primProc textures
63    SkASSERT(!programInfo.hasDynamicPrimProcTextures());
64    auto proxies = programInfo.hasFixedPrimProcTextures() ? programInfo.fixedPrimProcTextures()
65                                                          : nullptr;
66
67    this->setRenderTargetState(renderTarget, programInfo.origin());
68    fGeometryProcessor->setData(fDataManager, programInfo.primProc(),
69                                GrFragmentProcessor::CoordTransformIter(programInfo.pipeline()));
70    fSamplerBindings.reset();
71    for (int i = 0; i < programInfo.primProc().numTextureSamplers(); ++i) {
72        const auto& sampler = programInfo.primProc().textureSampler(i);
73        auto texture = static_cast<GrMtlTexture*>(proxies[i]->peekTexture());
74        fSamplerBindings.emplace_back(sampler.samplerState(), texture, fGpu);
75    }
76
77    GrFragmentProcessor::Iter iter(programInfo.pipeline());
78    GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
79    const GrFragmentProcessor* fp = iter.next();
80    GrGLSLFragmentProcessor* glslFP = glslIter.next();
81    while (fp && glslFP) {
82        glslFP->setData(fDataManager, *fp);
83        for (int i = 0; i < fp->numTextureSamplers(); ++i) {
84            const auto& sampler = fp->textureSampler(i);
85            fSamplerBindings.emplace_back(sampler.samplerState(), sampler.peekTexture(), fGpu);
86        }
87        fp = iter.next();
88        glslFP = glslIter.next();
89    }
90    SkASSERT(!fp && !glslFP);
91
92    {
93        SkIPoint offset;
94        GrTexture* dstTexture = programInfo.pipeline().peekDstTexture(&offset);
95
96        fXferProcessor->setData(fDataManager, programInfo.pipeline().getXferProcessor(),
97                                dstTexture, offset);
98    }
99
100    if (GrTextureProxy* dstTextureProxy = programInfo.pipeline().dstTextureProxy()) {
101        fSamplerBindings.emplace_back(GrSamplerState::ClampNearest(),
102                                      dstTextureProxy->peekTexture(),
103                                      fGpu);
104    }
105
106    SkASSERT(fNumSamplers == fSamplerBindings.count());
107    fDataManager.resetDirtyBits();
108
109    if (programInfo.pipeline().isStencilEnabled()) {
110        SkASSERT(renderTarget->renderTargetPriv().getStencilAttachment());
111        fStencil.reset(*programInfo.pipeline().getUserStencil(),
112                       programInfo.pipeline().hasStencilClip(),
113                       renderTarget->renderTargetPriv().numStencilBits());
114    }
115}
116
117void GrMtlPipelineState::setDrawState(id<MTLRenderCommandEncoder> renderCmdEncoder,
118                                      const GrSwizzle& outputSwizzle,
119                                      const GrXferProcessor& xferProcessor) {
120    [renderCmdEncoder pushDebugGroup:@"setDrawState"];
121    this->bind(renderCmdEncoder);
122    this->setBlendConstants(renderCmdEncoder, outputSwizzle, xferProcessor);
123    this->setDepthStencilState(renderCmdEncoder);
124    [renderCmdEncoder popDebugGroup];
125}
126
127void GrMtlPipelineState::bind(id<MTLRenderCommandEncoder> renderCmdEncoder) {
128    fDataManager.uploadAndBindUniformBuffers(fGpu, renderCmdEncoder);
129
130    SkASSERT(fNumSamplers == fSamplerBindings.count());
131    for (int index = 0; index < fNumSamplers; ++index) {
132        [renderCmdEncoder setFragmentTexture: fSamplerBindings[index].fTexture
133                                     atIndex: index];
134        [renderCmdEncoder setFragmentSamplerState: fSamplerBindings[index].fSampler->mtlSampler()
135                                          atIndex: index];
136    }
137}
138
139void GrMtlPipelineState::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrigin origin) {
140    // Load the RT height uniform if it is needed to y-flip gl_FragCoord.
141    if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
142        fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
143        fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
144    }
145
146    // set RT adjustment
147    SkISize size;
148    size.set(rt->width(), rt->height());
149    SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
150    if (fRenderTargetState.fRenderTargetOrigin != origin ||
151        fRenderTargetState.fRenderTargetSize != size) {
152        fRenderTargetState.fRenderTargetSize = size;
153        fRenderTargetState.fRenderTargetOrigin = origin;
154
155        float rtAdjustmentVec[4];
156        fRenderTargetState.getRTAdjustmentVec(rtAdjustmentVec);
157        fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, rtAdjustmentVec);
158    }
159}
160
161static bool blend_coeff_refs_constant(GrBlendCoeff coeff) {
162    switch (coeff) {
163        case kConstC_GrBlendCoeff:
164        case kIConstC_GrBlendCoeff:
165        case kConstA_GrBlendCoeff:
166        case kIConstA_GrBlendCoeff:
167            return true;
168        default:
169            return false;
170    }
171}
172
173void GrMtlPipelineState::setBlendConstants(id<MTLRenderCommandEncoder> renderCmdEncoder,
174                                           const GrSwizzle& swizzle,
175                                           const GrXferProcessor& xferProcessor) {
176    if (!renderCmdEncoder) {
177        return;
178    }
179
180    const GrXferProcessor::BlendInfo& blendInfo = xferProcessor.getBlendInfo();
181    GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
182    GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
183    if (blend_coeff_refs_constant(srcCoeff) || blend_coeff_refs_constant(dstCoeff)) {
184        // Swizzle the blend to match what the shader will output.
185        SkPMColor4f blendConst = swizzle.applyTo(blendInfo.fBlendConstant);
186
187        [renderCmdEncoder setBlendColorRed: blendConst.fR
188                                     green: blendConst.fG
189                                      blue: blendConst.fB
190                                     alpha: blendConst.fA];
191    }
192}
193
194void GrMtlPipelineState::setDepthStencilState(id<MTLRenderCommandEncoder> renderCmdEncoder) {
195    const GrSurfaceOrigin& origin = fRenderTargetState.fRenderTargetOrigin;
196    GrMtlDepthStencil* state =
197            fGpu->resourceProvider().findOrCreateCompatibleDepthStencilState(fStencil, origin);
198    if (!fStencil.isDisabled()) {
199        if (fStencil.isTwoSided()) {
200            if (@available(macOS 10.11, iOS 9.0, *)) {
201                [renderCmdEncoder setStencilFrontReferenceValue:fStencil.front(origin).fRef
202                                             backReferenceValue:fStencil.back(origin).fRef];
203            } else {
204                // Two-sided stencil not supported on older versions of iOS
205                // TODO: Find a way to recover from this
206                SkASSERT(false);
207            }
208        } else {
209            [renderCmdEncoder setStencilReferenceValue:fStencil.frontAndBack().fRef];
210        }
211    }
212    [renderCmdEncoder setDepthStencilState:state->mtlDepthStencil()];
213}
214
215void GrMtlPipelineState::SetDynamicScissorRectState(id<MTLRenderCommandEncoder> renderCmdEncoder,
216                                                    const GrRenderTarget* renderTarget,
217                                                    GrSurfaceOrigin rtOrigin,
218                                                    SkIRect scissorRect) {
219    if (!scissorRect.intersect(SkIRect::MakeWH(renderTarget->width(), renderTarget->height()))) {
220        scissorRect.setEmpty();
221    }
222
223    MTLScissorRect scissor;
224    scissor.x = scissorRect.fLeft;
225    scissor.width = scissorRect.width();
226    if (kTopLeft_GrSurfaceOrigin == rtOrigin) {
227        scissor.y = scissorRect.fTop;
228    } else {
229        SkASSERT(kBottomLeft_GrSurfaceOrigin == rtOrigin);
230        scissor.y = renderTarget->height() - scissorRect.fBottom;
231    }
232    scissor.height = scissorRect.height();
233
234    SkASSERT(scissor.x >= 0);
235    SkASSERT(scissor.y >= 0);
236
237    [renderCmdEncoder setScissorRect: scissor];
238}
239
240bool GrMtlPipelineState::doesntSampleAttachment(
241        const MTLRenderPassAttachmentDescriptor* attachment) const {
242    for (int i = 0; i < fSamplerBindings.count(); ++i) {
243        if (attachment.texture == fSamplerBindings[i].fTexture) {
244            return false;
245        }
246    }
247    return true;
248}
249