1/*
2 * Copyright 2020 Google LLC
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/GrVkMSAALoadManager.h"
9
10#include "include/gpu/GrDirectContext.h"
11#include "src/core/SkTraceEvent.h"
12#include "src/gpu/GrDirectContextPriv.h"
13#include "src/gpu/GrResourceProvider.h"
14#include "src/gpu/vk/GrVkBuffer.h"
15#include "src/gpu/vk/GrVkCommandBuffer.h"
16#include "src/gpu/vk/GrVkDescriptorSet.h"
17#include "src/gpu/vk/GrVkGpu.h"
18#include "src/gpu/vk/GrVkImageView.h"
19#include "src/gpu/vk/GrVkPipeline.h"
20#include "src/gpu/vk/GrVkRenderTarget.h"
21#include "src/gpu/vk/GrVkResourceProvider.h"
22#include "src/gpu/vk/GrVkUtil.h"
23
24GrVkMSAALoadManager::GrVkMSAALoadManager()
25        : fVertShaderModule(VK_NULL_HANDLE)
26        , fFragShaderModule(VK_NULL_HANDLE)
27        , fPipelineLayout(VK_NULL_HANDLE) {}
28
29GrVkMSAALoadManager::~GrVkMSAALoadManager() {}
30
31bool GrVkMSAALoadManager::createMSAALoadProgram(GrVkGpu* gpu) {
32    TRACE_EVENT0("skia", TRACE_FUNC);
33
34    SkSL::String vertShaderText;
35    vertShaderText.append(
36            "layout(set = 0, binding = 0) uniform vertexUniformBuffer {"
37            "half4 uPosXform;"
38            "};"
39
40            "// MSAA Load Program VS\n"
41            "void main() {"
42            "float2 position = float2(sk_VertexID >> 1, sk_VertexID & 1);"
43            "sk_Position.xy = position * uPosXform.xy + uPosXform.zw;"
44            "sk_Position.zw = half2(0, 1);"
45            "}");
46
47    SkSL::String fragShaderText;
48    fragShaderText.append(
49            "layout(input_attachment_index = 0, set = 2, binding = 0) uniform subpassInput uInput;"
50
51            "// MSAA Load Program FS\n"
52            "void main() {"
53            "sk_FragColor = subpassLoad(uInput);"
54            "}");
55
56    SkSL::Program::Settings settings;
57    SkSL::String spirv;
58    SkSL::Program::Inputs inputs;
59    if (!GrCompileVkShaderModule(gpu, vertShaderText, VK_SHADER_STAGE_VERTEX_BIT,
60                                 &fVertShaderModule, &fShaderStageInfo[0], settings, &spirv,
61                                 &inputs)) {
62        this->destroyResources(gpu);
63        return false;
64    }
65    SkASSERT(inputs == SkSL::Program::Inputs());
66
67    if (!GrCompileVkShaderModule(gpu, fragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT,
68                                 &fFragShaderModule, &fShaderStageInfo[1], settings, &spirv,
69                                 &inputs)) {
70        this->destroyResources(gpu);
71        return false;
72    }
73    SkASSERT(inputs == SkSL::Program::Inputs());
74
75    VkDescriptorSetLayout dsLayout[GrVkUniformHandler::kDescSetCount];
76
77    GrVkResourceProvider& resourceProvider = gpu->resourceProvider();
78
79    dsLayout[GrVkUniformHandler::kUniformBufferDescSet] = resourceProvider.getUniformDSLayout();
80
81    // Even though we don't have a sampler we need to put a valid handle here (of zero samplers)
82    // since we set up our descriptor layout to be uniform, sampler, input.
83    //
84    // TODO: We should have a more general way for different pipelines to describe their descriptor
85    // layouts so that we don't have to use the compile time constants for the sets.
86    GrVkDescriptorSetManager::Handle samplerHandle;
87    resourceProvider.getZeroSamplerDescriptorSetHandle(&samplerHandle);
88
89    dsLayout[GrVkUniformHandler::kSamplerDescSet] =
90            resourceProvider.getSamplerDSLayout(samplerHandle);
91
92    dsLayout[GrVkUniformHandler::kInputDescSet] = resourceProvider.getInputDSLayout();
93
94    // Create the VkPipelineLayout
95    VkPipelineLayoutCreateInfo layoutCreateInfo;
96    memset(&layoutCreateInfo, 0, sizeof(VkPipelineLayoutCreateFlags));
97    layoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
98    layoutCreateInfo.pNext = nullptr;
99    layoutCreateInfo.flags = 0;
100    layoutCreateInfo.setLayoutCount = GrVkUniformHandler::kDescSetCount;
101    layoutCreateInfo.pSetLayouts = dsLayout;
102    layoutCreateInfo.pushConstantRangeCount = 0;
103    layoutCreateInfo.pPushConstantRanges = nullptr;
104
105    VkResult err = GR_VK_CALL(
106            gpu->vkInterface(),
107            CreatePipelineLayout(gpu->device(), &layoutCreateInfo, nullptr, &fPipelineLayout));
108    if (err) {
109        this->destroyResources(gpu);
110        return false;
111    }
112
113    return true;
114}
115
116bool GrVkMSAALoadManager::loadMSAAFromResolve(GrVkGpu* gpu,
117                                              GrVkCommandBuffer* commandBuffer,
118                                              const GrVkRenderPass& renderPass,
119                                              GrAttachment* dst,
120                                              GrVkImage* src,
121                                              const SkIRect& rect) {
122    if (!dst) {
123        return false;
124    }
125    if (!src || !src->supportsInputAttachmentUsage()) {
126        return false;
127    }
128
129    if (VK_NULL_HANDLE == fVertShaderModule) {
130        SkASSERT(fFragShaderModule == VK_NULL_HANDLE && fPipelineLayout == VK_NULL_HANDLE);
131        if (!this->createMSAALoadProgram(gpu)) {
132            SkDebugf("Failed to create copy program.\n");
133            return false;
134        }
135    }
136    SkASSERT(fPipelineLayout != VK_NULL_HANDLE);
137
138    GrVkResourceProvider& resourceProv = gpu->resourceProvider();
139
140    sk_sp<const GrVkPipeline> pipeline =
141            resourceProv.findOrCreateMSAALoadPipeline(renderPass, dst->numSamples(),
142                                                      fShaderStageInfo, fPipelineLayout);
143    if (!pipeline) {
144        return false;
145    }
146    commandBuffer->bindPipeline(gpu, std::move(pipeline));
147
148    // Set Dynamic viewport and stencil
149    // We always use one viewport the size of the RT
150    VkViewport viewport;
151    viewport.x = 0.0f;
152    viewport.y = 0.0f;
153    viewport.width = SkIntToScalar(dst->width());
154    viewport.height = SkIntToScalar(dst->height());
155    viewport.minDepth = 0.0f;
156    viewport.maxDepth = 1.0f;
157    commandBuffer->setViewport(gpu, 0, 1, &viewport);
158
159    // We assume the scissor is not enabled so just set it to the whole RT
160    VkRect2D scissor;
161    scissor.extent.width = dst->width();
162    scissor.extent.height = dst->height();
163    scissor.offset.x = 0;
164    scissor.offset.y = 0;
165    commandBuffer->setScissor(gpu, 0, 1, &scissor);
166
167    // Update and bind uniform descriptor set
168    int w = rect.width();
169    int h = rect.height();
170
171    // dst rect edges in NDC (-1 to 1)
172    int dw = dst->width();
173    int dh = dst->height();
174    float dx0 = 2.f * rect.fLeft / dw - 1.f;
175    float dx1 = 2.f * (rect.fLeft + w) / dw - 1.f;
176    float dy0 = 2.f * rect.fTop / dh - 1.f;
177    float dy1 = 2.f * (rect.fTop + h) / dh - 1.f;
178
179    float uniData[] = {dx1 - dx0, dy1 - dy0, dx0, dy0};  // posXform
180
181    GrResourceProvider* resourceProvider = gpu->getContext()->priv().resourceProvider();
182    // TODO: Is it worth holding onto the last used uniform buffer and tracking the width, height,
183    // dst width, and dst height so that we can use the buffer again without having to update the
184    // data?
185    sk_sp<GrGpuBuffer> uniformBuffer = resourceProvider->createBuffer(
186            4 * sizeof(float), GrGpuBufferType::kUniform, kDynamic_GrAccessPattern, uniData);
187    if (!uniformBuffer) {
188        return false;
189    }
190    GrVkBuffer* vkUniformBuffer = static_cast<GrVkBuffer*>(uniformBuffer.get());
191    static_assert(GrVkUniformHandler::kUniformBufferDescSet < GrVkUniformHandler::kInputDescSet);
192    commandBuffer->bindDescriptorSets(gpu, fPipelineLayout,
193                                      GrVkUniformHandler::kUniformBufferDescSet,
194                                      /*setCount=*/1, vkUniformBuffer->uniformDescriptorSet(),
195                                      /*dynamicOffsetCount=*/0, /*dynamicOffsets=*/nullptr);
196    commandBuffer->addGrBuffer(std::move(uniformBuffer));
197
198    // Update the input descriptor set
199    gr_rp<const GrVkDescriptorSet> inputDS = src->inputDescSetForMSAALoad(gpu);
200    if (!inputDS) {
201        return false;
202    }
203    commandBuffer->bindDescriptorSets(gpu, fPipelineLayout,
204                                      GrVkUniformHandler::kInputDescSet, /*setCount=*/1,
205                                      inputDS->descriptorSet(),
206                                      /*dynamicOffsetCount=*/0, /*dynamicOffsets=*/nullptr);
207
208    // We don't need to add the src and dst resources here since those are all tracked by the main
209    // render pass code out in GrVkOpsRenderPass and GrVkRenderTarget::adResources.
210    commandBuffer->addRecycledResource(std::move(inputDS));
211
212    commandBuffer->draw(gpu, 4, 1, 0, 0);
213
214    return true;
215}
216
217void GrVkMSAALoadManager::destroyResources(GrVkGpu* gpu) {
218    if (fVertShaderModule != VK_NULL_HANDLE) {
219        GR_VK_CALL(gpu->vkInterface(),
220                   DestroyShaderModule(gpu->device(), fVertShaderModule, nullptr));
221        fVertShaderModule = VK_NULL_HANDLE;
222    }
223
224    if (fFragShaderModule != VK_NULL_HANDLE) {
225        GR_VK_CALL(gpu->vkInterface(),
226                   DestroyShaderModule(gpu->device(), fFragShaderModule, nullptr));
227        fFragShaderModule = VK_NULL_HANDLE;
228    }
229
230    if (fPipelineLayout != VK_NULL_HANDLE) {
231        GR_VK_CALL(gpu->vkInterface(),
232                   DestroyPipelineLayout(gpu->device(), fPipelineLayout, nullptr));
233        fPipelineLayout = VK_NULL_HANDLE;
234    }
235}
236
237