xref: /aosp_15_r20/external/skia/src/gpu/ganesh/mtl/GrMtlPipelineState.mm (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/ganesh/mtl/GrMtlPipelineState.h"
9
10#include "src/gpu/ganesh/GrBackendUtils.h"
11#include "src/gpu/ganesh/GrFragmentProcessor.h"
12#include "src/gpu/ganesh/GrGeometryProcessor.h"
13#include "src/gpu/ganesh/GrRenderTarget.h"
14#include "src/gpu/ganesh/GrTexture.h"
15#include "src/gpu/ganesh/GrXferProcessor.h"
16#include "src/gpu/ganesh/effects/GrTextureEffect.h"
17#include "src/gpu/ganesh/mtl/GrMtlBuffer.h"
18#include "src/gpu/ganesh/mtl/GrMtlFramebuffer.h"
19#include "src/gpu/ganesh/mtl/GrMtlGpu.h"
20#include "src/gpu/ganesh/mtl/GrMtlRenderCommandEncoder.h"
21#include "src/gpu/ganesh/mtl/GrMtlTexture.h"
22#include "src/sksl/SkSLCompiler.h"
23
24#if !__has_feature(objc_arc)
25#error This file must be compiled with Arc. Use -fobjc-arc flag
26#endif
27
28GR_NORETAIN_BEGIN
29
30GrMtlPipelineState::SamplerBindings::SamplerBindings(GrSamplerState state,
31                                                     GrTexture* texture,
32                                                     GrMtlGpu* gpu)
33        : fTexture(static_cast<GrMtlTexture*>(texture)->mtlTexture()) {
34    fSampler = gpu->resourceProvider().findOrCreateCompatibleSampler(state);
35    gpu->commandBuffer()->addResource(sk_ref_sp<GrManagedResource>(fSampler));
36    gpu->commandBuffer()->addGrSurface(
37            sk_ref_sp<GrSurface>(static_cast<GrMtlTexture*>(texture)->attachment()));
38}
39
40GrMtlPipelineState::GrMtlPipelineState(
41        GrMtlGpu* gpu,
42        sk_sp<GrMtlRenderPipeline> pipeline,
43        MTLPixelFormat pixelFormat,
44        const GrGLSLBuiltinUniformHandles& builtinUniformHandles,
45        const UniformInfoArray& uniforms,
46        uint32_t uniformBufferSize,
47        uint32_t numSamplers,
48        std::unique_ptr<GrGeometryProcessor::ProgramImpl> gpImpl,
49        std::unique_ptr<GrXferProcessor::ProgramImpl> xpImpl,
50        std::vector<std::unique_ptr<GrFragmentProcessor::ProgramImpl>> fpImpls)
51        : fGpu(gpu)
52        , fPipeline(std::move(pipeline))
53        , fPixelFormat(pixelFormat)
54        , fBuiltinUniformHandles(builtinUniformHandles)
55        , fNumSamplers(numSamplers)
56        , fGPImpl(std::move(gpImpl))
57        , fXPImpl(std::move(xpImpl))
58        , fFPImpls(std::move(fpImpls))
59        , fDataManager(uniforms, uniformBufferSize) {
60    (void) fPixelFormat; // Suppress unused-var warning.
61}
62
63void GrMtlPipelineState::setData(GrMtlFramebuffer* framebuffer,
64                                 const GrProgramInfo& programInfo) {
65    SkISize colorAttachmentDimensions = framebuffer->colorAttachment()->dimensions();
66
67    this->setRenderTargetState(colorAttachmentDimensions, programInfo.origin());
68    fGPImpl->setData(fDataManager, *fGpu->caps()->shaderCaps(), programInfo.geomProc());
69
70    for (int i = 0; i < programInfo.pipeline().numFragmentProcessors(); ++i) {
71        const auto& fp = programInfo.pipeline().getFragmentProcessor(i);
72        fp.visitWithImpls([&](const GrFragmentProcessor& fp,
73                              GrFragmentProcessor::ProgramImpl& impl) {
74            impl.setData(fDataManager, fp);
75        }, *fFPImpls[i]);
76    }
77
78    programInfo.pipeline().setDstTextureUniforms(fDataManager, &fBuiltinUniformHandles);
79    fXPImpl->setData(fDataManager, programInfo.pipeline().getXferProcessor());
80
81    fDataManager.resetDirtyBits();
82
83#ifdef SK_DEBUG
84    if (programInfo.isStencilEnabled()) {
85        SkDEBUGCODE(const GrAttachment* stencil = framebuffer->stencilAttachment());
86        SkASSERT(stencil);
87        SkASSERT(GrBackendFormatStencilBits(stencil->backendFormat()) == 8);
88    }
89#endif
90
91    fStencil = programInfo.nonGLStencilSettings();
92    fGpu->commandBuffer()->addResource(fPipeline);
93}
94
95void GrMtlPipelineState::setTextures(const GrGeometryProcessor& geomProc,
96                                     const GrPipeline& pipeline,
97                                     const GrSurfaceProxy* const geomProcTextures[]) {
98    fSamplerBindings.clear();
99    for (int i = 0; i < geomProc.numTextureSamplers(); ++i) {
100        SkASSERT(geomProcTextures[i]->asTextureProxy());
101        const auto& sampler = geomProc.textureSampler(i);
102        auto texture = static_cast<GrMtlTexture*>(geomProcTextures[i]->peekTexture());
103        fSamplerBindings.emplace_back(sampler.samplerState(), texture, fGpu);
104    }
105
106    if (GrTextureProxy* dstTextureProxy = pipeline.dstProxyView().asTextureProxy()) {
107        fSamplerBindings.emplace_back(
108                GrSamplerState::Filter::kNearest, dstTextureProxy->peekTexture(), fGpu);
109    }
110
111    pipeline.visitTextureEffects([&](const GrTextureEffect& te) {
112        fSamplerBindings.emplace_back(te.samplerState(), te.texture(), fGpu);
113    });
114
115    SkASSERT(fNumSamplers == fSamplerBindings.size());
116}
117
118void GrMtlPipelineState::setDrawState(GrMtlRenderCommandEncoder* renderCmdEncoder,
119                                      const skgpu::Swizzle& writeSwizzle,
120                                      const GrXferProcessor& xferProcessor) {
121    this->bindUniforms(renderCmdEncoder);
122    this->setBlendConstants(renderCmdEncoder, writeSwizzle, xferProcessor);
123    this->setDepthStencilState(renderCmdEncoder);
124}
125
126void GrMtlPipelineState::bindUniforms(GrMtlRenderCommandEncoder* renderCmdEncoder) {
127    fDataManager.uploadAndBindUniformBuffers(fGpu, renderCmdEncoder);
128}
129
130void GrMtlPipelineState::bindTextures(GrMtlRenderCommandEncoder* renderCmdEncoder) {
131    SkASSERT(fNumSamplers == fSamplerBindings.size());
132    for (int index = 0; index < fNumSamplers; ++index) {
133        renderCmdEncoder->setFragmentTexture(fSamplerBindings[index].fTexture, index);
134        renderCmdEncoder->setFragmentSamplerState(fSamplerBindings[index].fSampler, index);
135    }
136}
137
138void GrMtlPipelineState::setRenderTargetState(SkISize colorAttachmentDimensions,
139                                              GrSurfaceOrigin origin) {
140    SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
141    if (fRenderTargetState.fRenderTargetOrigin != origin ||
142        fRenderTargetState.fRenderTargetSize != colorAttachmentDimensions) {
143        fRenderTargetState.fRenderTargetSize = colorAttachmentDimensions;
144        fRenderTargetState.fRenderTargetOrigin = origin;
145
146        // The client will mark a swap buffer as kTopLeft when making a SkSurface because
147        // Metal's framebuffer space has (0, 0) at the top left. This agrees with Skia's device
148        // coords. However, in NDC (-1, -1) is the bottom left. So we flip when origin is kTopLeft.
149        bool flip = (origin == kTopLeft_GrSurfaceOrigin);
150        std::array<float, 4> v = SkSL::Compiler::GetRTAdjustVector(colorAttachmentDimensions, flip);
151        fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, v.data());
152        if (fBuiltinUniformHandles.fRTFlipUni.isValid()) {
153            // Note above that framebuffer space has origin top left. So we need !flip here.
154            std::array<float, 2> d =
155                    SkSL::Compiler::GetRTFlipVector(colorAttachmentDimensions.height(), !flip);
156            fDataManager.set2fv(fBuiltinUniformHandles.fRTFlipUni, 1, d.data());
157        }
158    }
159}
160
161void GrMtlPipelineState::setBlendConstants(GrMtlRenderCommandEncoder* renderCmdEncoder,
162                                           const skgpu::Swizzle& swizzle,
163                                           const GrXferProcessor& xferProcessor) {
164    if (!renderCmdEncoder) {
165        return;
166    }
167
168    const skgpu::BlendInfo& blendInfo = xferProcessor.getBlendInfo();
169    skgpu::BlendCoeff srcCoeff = blendInfo.fSrcBlend;
170    skgpu::BlendCoeff dstCoeff = blendInfo.fDstBlend;
171    if (skgpu::BlendCoeffRefsConstant(srcCoeff) || skgpu::BlendCoeffRefsConstant(dstCoeff)) {
172        // Swizzle the blend to match what the shader will output.
173        SkPMColor4f blendConst = swizzle.applyTo(blendInfo.fBlendConstant);
174
175        renderCmdEncoder->setBlendColor(blendConst);
176    }
177}
178
179void GrMtlPipelineState::setDepthStencilState(GrMtlRenderCommandEncoder* renderCmdEncoder) {
180    const GrSurfaceOrigin& origin = fRenderTargetState.fRenderTargetOrigin;
181    GrMtlDepthStencil* state =
182            fGpu->resourceProvider().findOrCreateCompatibleDepthStencilState(fStencil, origin);
183    if (!fStencil.isDisabled()) {
184        if (fStencil.isTwoSided()) {
185            if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) {
186                renderCmdEncoder->setStencilFrontBackReferenceValues(
187                        fStencil.postOriginCCWFace(origin).fRef,
188                        fStencil.postOriginCWFace(origin).fRef);
189            } else {
190                // Two-sided stencil not supported on older versions of iOS
191                // TODO: Find a way to recover from this
192                SkASSERT(false);
193            }
194        } else {
195            renderCmdEncoder->setStencilReferenceValue(fStencil.singleSidedFace().fRef);
196        }
197    }
198    renderCmdEncoder->setDepthStencilState(state->mtlDepthStencil());
199    fGpu->commandBuffer()->addResource(sk_ref_sp<GrManagedResource>(state));
200}
201
202void GrMtlPipelineState::SetDynamicScissorRectState(GrMtlRenderCommandEncoder* renderCmdEncoder,
203                                                    SkISize colorAttachmentDimensions,
204                                                    GrSurfaceOrigin rtOrigin,
205                                                    SkIRect scissorRect) {
206    if (!scissorRect.intersect(SkIRect::MakeWH(colorAttachmentDimensions.width(),
207                                               colorAttachmentDimensions.height()))) {
208        scissorRect.setEmpty();
209    }
210
211    MTLScissorRect scissor;
212    scissor.x = scissorRect.fLeft;
213    scissor.width = scissorRect.width();
214    if (kTopLeft_GrSurfaceOrigin == rtOrigin) {
215        scissor.y = scissorRect.fTop;
216    } else {
217        SkASSERT(kBottomLeft_GrSurfaceOrigin == rtOrigin);
218        scissor.y = colorAttachmentDimensions.height() - scissorRect.fBottom;
219    }
220    scissor.height = scissorRect.height();
221
222    SkASSERT(scissor.x >= 0);
223    SkASSERT(scissor.y >= 0);
224
225    renderCmdEncoder->setScissorRect(scissor);
226}
227
228bool GrMtlPipelineState::doesntSampleAttachment(
229        const MTLRenderPassAttachmentDescriptor* attachment) const {
230    for (int i = 0; i < fSamplerBindings.size(); ++i) {
231        if (attachment.texture == fSamplerBindings[i].fTexture ||
232            attachment.resolveTexture == fSamplerBindings[i].fTexture) {
233            return false;
234        }
235    }
236    return true;
237}
238
239GR_NORETAIN_END
240