1/*
2 * Copyright 2016 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/vk/GrVkPipelineState.h"
9
10#include "src/core/SkMipmap.h"
11#include "src/gpu/GrFragmentProcessor.h"
12#include "src/gpu/GrGeometryProcessor.h"
13#include "src/gpu/GrPipeline.h"
14#include "src/gpu/GrRenderTarget.h"
15#include "src/gpu/GrTexture.h"
16#include "src/gpu/GrXferProcessor.h"
17#include "src/gpu/effects/GrTextureEffect.h"
18#include "src/gpu/vk/GrVkBuffer.h"
19#include "src/gpu/vk/GrVkCommandBuffer.h"
20#include "src/gpu/vk/GrVkDescriptorPool.h"
21#include "src/gpu/vk/GrVkDescriptorSet.h"
22#include "src/gpu/vk/GrVkGpu.h"
23#include "src/gpu/vk/GrVkImageView.h"
24#include "src/gpu/vk/GrVkMemory.h"
25#include "src/gpu/vk/GrVkPipeline.h"
26#include "src/gpu/vk/GrVkRenderTarget.h"
27#include "src/gpu/vk/GrVkSampler.h"
28#include "src/gpu/vk/GrVkTexture.h"
29
30GrVkPipelineState::GrVkPipelineState(
31        GrVkGpu* gpu,
32        sk_sp<const GrVkPipeline> pipeline,
33        const GrVkDescriptorSetManager::Handle& samplerDSHandle,
34        const GrGLSLBuiltinUniformHandles& builtinUniformHandles,
35        const UniformInfoArray& uniforms,
36        uint32_t uniformSize,
37        bool usePushConstants,
38        const UniformInfoArray& samplers,
39        std::unique_ptr<GrGeometryProcessor::ProgramImpl> gpImpl,
40        std::unique_ptr<GrXferProcessor::ProgramImpl> xpImpl,
41        std::vector<std::unique_ptr<GrFragmentProcessor::ProgramImpl>> fpImpls)
42        : fPipeline(std::move(pipeline))
43        , fSamplerDSHandle(samplerDSHandle)
44        , fBuiltinUniformHandles(builtinUniformHandles)
45        , fGPImpl(std::move(gpImpl))
46        , fXPImpl(std::move(xpImpl))
47        , fFPImpls(std::move(fpImpls))
48        , fDataManager(uniforms, uniformSize, usePushConstants) {
49    fNumSamplers = samplers.count();
50    for (const auto& sampler : samplers.items()) {
51        // We store the immutable samplers here and take a ref on the sampler. Once we switch to
52        // using sk_sps here we should just move the immutable samplers to save the extra ref/unref.
53        if (sampler.fImmutableSampler) {
54            sampler.fImmutableSampler->ref();
55        }
56        fImmutableSamplers.push_back(sampler.fImmutableSampler);
57    }
58}
59
60GrVkPipelineState::~GrVkPipelineState() {
61    // Must have freed all GPU resources before this is destroyed
62    SkASSERT(!fPipeline);
63}
64
65void GrVkPipelineState::freeGPUResources(GrVkGpu* gpu) {
66    fPipeline.reset();
67    fDataManager.releaseData();
68    for (int i = 0; i < fImmutableSamplers.count(); ++i) {
69        if (fImmutableSamplers[i]) {
70            fImmutableSamplers[i]->unref();
71            fImmutableSamplers[i] = nullptr;
72        }
73    }
74}
75
76bool GrVkPipelineState::setAndBindUniforms(GrVkGpu* gpu,
77                                           SkISize colorAttachmentDimensions,
78                                           const GrProgramInfo& programInfo,
79                                           GrVkCommandBuffer* commandBuffer) {
80    this->setRenderTargetState(colorAttachmentDimensions, programInfo.origin());
81
82    fGPImpl->setData(fDataManager, *gpu->caps()->shaderCaps(), programInfo.geomProc());
83
84    for (int i = 0; i < programInfo.pipeline().numFragmentProcessors(); ++i) {
85        const auto& fp = programInfo.pipeline().getFragmentProcessor(i);
86        fp.visitWithImpls([&](const GrFragmentProcessor& fp,
87                              GrFragmentProcessor::ProgramImpl& impl) {
88            impl.setData(fDataManager, fp);
89        }, *fFPImpls[i]);
90    }
91
92    programInfo.pipeline().setDstTextureUniforms(fDataManager, &fBuiltinUniformHandles);
93    fXPImpl->setData(fDataManager, programInfo.pipeline().getXferProcessor());
94
95    // Upload uniform data and bind descriptor set.
96    auto [uniformBuffer, success] = fDataManager.uploadUniforms(gpu, fPipeline->layout(),
97                                                                commandBuffer);
98    if (!success) {
99        return false;
100    }
101    if (uniformBuffer) {
102        const GrVkBuffer* vkBuffer = static_cast<GrVkBuffer*>(uniformBuffer.get());
103        static const int kUniformDSIdx = GrVkUniformHandler::kUniformBufferDescSet;
104        commandBuffer->bindDescriptorSets(gpu, fPipeline->layout(), kUniformDSIdx, /*setCount=*/1,
105                                          vkBuffer->uniformDescriptorSet(),
106                                          /*dynamicOffsetCount=*/0, /*dynamicOffsets=*/nullptr);
107        commandBuffer->addGrBuffer(std::move(uniformBuffer));
108    }
109    return true;
110}
111
112bool GrVkPipelineState::setAndBindTextures(GrVkGpu* gpu,
113                                           const GrGeometryProcessor& geomProc,
114                                           const GrPipeline& pipeline,
115                                           const GrSurfaceProxy* const geomProcTextures[],
116                                           GrVkCommandBuffer* commandBuffer) {
117    SkASSERT(geomProcTextures || !geomProc.numTextureSamplers());
118    if (!fNumSamplers) {
119        return true;
120    }
121    struct SamplerBindings {
122        GrSamplerState fState;
123        GrVkTexture* fTexture;
124    };
125    SkAutoSTArray<8, SamplerBindings> samplerBindings(fNumSamplers);
126    int currTextureBinding = 0;
127
128    for (int i = 0; i < geomProc.numTextureSamplers(); ++i) {
129        SkASSERT(geomProcTextures[i]->asTextureProxy());
130        const auto& sampler = geomProc.textureSampler(i);
131        auto texture = static_cast<GrVkTexture*>(geomProcTextures[i]->peekTexture());
132        samplerBindings[currTextureBinding++] = {sampler.samplerState(), texture};
133    }
134
135
136    if (GrTexture* dstTexture = pipeline.peekDstTexture()) {
137        samplerBindings[currTextureBinding++] = {GrSamplerState::Filter::kNearest,
138                                                 static_cast<GrVkTexture*>(dstTexture)};
139    }
140
141    pipeline.visitTextureEffects([&](const GrTextureEffect& te) {
142        GrSamplerState samplerState = te.samplerState();
143        auto* texture = static_cast<GrVkTexture*>(te.texture());
144        samplerBindings[currTextureBinding++] = {samplerState, texture};
145    });
146
147    // Get new descriptor set
148    SkASSERT(fNumSamplers == currTextureBinding);
149    static const int kSamplerDSIdx = GrVkUniformHandler::kSamplerDescSet;
150
151    if (fNumSamplers == 1) {
152        auto texture = samplerBindings[0].fTexture;
153        auto texAttachment = texture->textureImage();
154        const auto& samplerState = samplerBindings[0].fState;
155        const GrVkDescriptorSet* descriptorSet = texture->cachedSingleDescSet(samplerState);
156        if (descriptorSet) {
157            commandBuffer->addGrSurface(sk_ref_sp<const GrSurface>(texture));
158            commandBuffer->addResource(texAttachment->textureView());
159            commandBuffer->addResource(texAttachment->resource());
160            commandBuffer->addRecycledResource(descriptorSet);
161            commandBuffer->bindDescriptorSets(gpu, fPipeline->layout(), kSamplerDSIdx,
162                                              /*setCount=*/1, descriptorSet->descriptorSet(),
163                                              /*dynamicOffsetCount=*/0,
164                                              /*dynamicOffsets=*/nullptr);
165            return true;
166        }
167    }
168
169    const GrVkDescriptorSet* descriptorSet =
170            gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle);
171    if (!descriptorSet) {
172        return false;
173    }
174
175    for (int i = 0; i < fNumSamplers; ++i) {
176        GrSamplerState state = samplerBindings[i].fState;
177        GrVkTexture* texture = samplerBindings[i].fTexture;
178        auto texAttachment = texture->textureImage();
179
180        const GrVkImageView* textureView = texAttachment->textureView();
181        const GrVkSampler* sampler = nullptr;
182        if (fImmutableSamplers[i]) {
183            sampler = fImmutableSamplers[i];
184        } else {
185            sampler = gpu->resourceProvider().findOrCreateCompatibleSampler(
186                    state, texAttachment->ycbcrConversionInfo());
187            if (!sampler) {
188                descriptorSet->recycle();
189                return false;
190            }
191        }
192        SkASSERT(sampler);
193
194        VkDescriptorImageInfo imageInfo;
195        memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
196        imageInfo.sampler = fImmutableSamplers[i] ? VK_NULL_HANDLE : sampler->sampler();
197        imageInfo.imageView = textureView->imageView();
198        imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
199
200        VkWriteDescriptorSet writeInfo;
201        memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
202        writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
203        writeInfo.pNext = nullptr;
204        writeInfo.dstSet = *descriptorSet->descriptorSet();
205        writeInfo.dstBinding = i;
206        writeInfo.dstArrayElement = 0;
207        writeInfo.descriptorCount = 1;
208        writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
209        writeInfo.pImageInfo = &imageInfo;
210        writeInfo.pBufferInfo = nullptr;
211        writeInfo.pTexelBufferView = nullptr;
212
213        GR_VK_CALL(gpu->vkInterface(),
214                   UpdateDescriptorSets(gpu->device(), 1, &writeInfo, 0, nullptr));
215        commandBuffer->addResource(sampler);
216        if (!fImmutableSamplers[i]) {
217            sampler->unref();
218        }
219        commandBuffer->addResource(textureView);
220        commandBuffer->addResource(texAttachment->resource());
221    }
222    if (fNumSamplers == 1) {
223        GrSamplerState state = samplerBindings[0].fState;
224        GrVkTexture* texture = samplerBindings[0].fTexture;
225        texture->addDescriptorSetToCache(descriptorSet, state);
226    }
227
228    commandBuffer->bindDescriptorSets(gpu, fPipeline->layout(), kSamplerDSIdx, /*setCount=*/1,
229                                      descriptorSet->descriptorSet(),
230                                      /*dynamicOffsetCount=*/0, /*dynamicOffsets=*/nullptr);
231    commandBuffer->addRecycledResource(descriptorSet);
232    descriptorSet->recycle();
233    return true;
234}
235
236bool GrVkPipelineState::setAndBindInputAttachment(GrVkGpu* gpu,
237                                                  gr_rp<const GrVkDescriptorSet> inputDescSet,
238                                                  GrVkCommandBuffer* commandBuffer) {
239    SkASSERT(inputDescSet);
240    commandBuffer->bindDescriptorSets(gpu, fPipeline->layout(), GrVkUniformHandler::kInputDescSet,
241                                      /*setCount=*/1, inputDescSet->descriptorSet(),
242                                      /*dynamicOffsetCount=*/0, /*dynamicOffsets=*/nullptr);
243    // We don't add the input resource to the command buffer to track since the input will be
244    // the same as the color attachment which is already tracked on the command buffer.
245    commandBuffer->addRecycledResource(std::move(inputDescSet));
246    return true;
247}
248
249void GrVkPipelineState::setRenderTargetState(SkISize colorAttachmentDimensions,
250                                             GrSurfaceOrigin origin) {
251    // Set RT adjustment and RT flip
252    SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
253    if (fRenderTargetState.fRenderTargetOrigin != origin ||
254        fRenderTargetState.fRenderTargetSize != colorAttachmentDimensions) {
255        fRenderTargetState.fRenderTargetSize = colorAttachmentDimensions;
256        fRenderTargetState.fRenderTargetOrigin = origin;
257
258        // The client will mark a swap buffer as kTopLeft when making a SkSurface because
259        // Vulkan's framebuffer space has (0, 0) at the top left. This agrees with Skia's device
260        // coords and with Vulkan's NDC that has (-1, -1) in the top left. So a flip is needed when
261        // surface origin is kBottomLeft rather than kTopLeft.
262        bool flip = (origin == kBottomLeft_GrSurfaceOrigin);
263        std::array<float, 4> v = SkSL::Compiler::GetRTAdjustVector(colorAttachmentDimensions, flip);
264        fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, v.data());
265        if (fBuiltinUniformHandles.fRTFlipUni.isValid()) {
266            std::array<float, 2> d =
267                    SkSL::Compiler::GetRTFlipVector(colorAttachmentDimensions.height(), flip);
268            fDataManager.set2fv(fBuiltinUniformHandles.fRTFlipUni, 1, d.data());
269        }
270    }
271}
272
273void GrVkPipelineState::bindPipeline(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer) {
274    commandBuffer->bindPipeline(gpu, fPipeline);
275}
276