1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2020 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "tools/gpu/vk/VkYcbcrSamplerHelper.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#ifdef SK_VULKAN
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h"
13cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h"
14cb93a386Sopenharmony_ci#include "src/gpu/vk/GrVkGpu.h"
15cb93a386Sopenharmony_ci#include "src/gpu/vk/GrVkUtil.h"
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_ciint VkYcbcrSamplerHelper::GetExpectedY(int x, int y, int width, int height) {
18cb93a386Sopenharmony_ci    return 16 + (x + y) * 219 / (width + height - 2);
19cb93a386Sopenharmony_ci}
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_cistd::pair<int, int> VkYcbcrSamplerHelper::GetExpectedUV(int x, int y, int width, int height) {
22cb93a386Sopenharmony_ci    return { 16 + x * 224 / (width - 1), 16 + y * 224 / (height - 1) };
23cb93a386Sopenharmony_ci}
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ciGrVkGpu* VkYcbcrSamplerHelper::vkGpu() {
26cb93a386Sopenharmony_ci    return (GrVkGpu*) fDContext->priv().getGpu();
27cb93a386Sopenharmony_ci}
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ciVkYcbcrSamplerHelper::VkYcbcrSamplerHelper(GrDirectContext* dContext) : fDContext(dContext) {
30cb93a386Sopenharmony_ci    SkASSERT_RELEASE(dContext->backend() == GrBackendApi::kVulkan);
31cb93a386Sopenharmony_ci}
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ciVkYcbcrSamplerHelper::~VkYcbcrSamplerHelper() {
34cb93a386Sopenharmony_ci    GrVkGpu* vkGpu = this->vkGpu();
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_ci    if (fImage != VK_NULL_HANDLE) {
37cb93a386Sopenharmony_ci        GR_VK_CALL(vkGpu->vkInterface(), DestroyImage(vkGpu->device(), fImage, nullptr));
38cb93a386Sopenharmony_ci        fImage = VK_NULL_HANDLE;
39cb93a386Sopenharmony_ci    }
40cb93a386Sopenharmony_ci    if (fImageMemory != VK_NULL_HANDLE) {
41cb93a386Sopenharmony_ci        GR_VK_CALL(vkGpu->vkInterface(), FreeMemory(vkGpu->device(), fImageMemory, nullptr));
42cb93a386Sopenharmony_ci        fImageMemory = VK_NULL_HANDLE;
43cb93a386Sopenharmony_ci    }
44cb93a386Sopenharmony_ci}
45cb93a386Sopenharmony_ci
46cb93a386Sopenharmony_cibool VkYcbcrSamplerHelper::isYCbCrSupported() {
47cb93a386Sopenharmony_ci    GrVkGpu* vkGpu = this->vkGpu();
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_ci    return vkGpu->vkCaps().supportsYcbcrConversion();
50cb93a386Sopenharmony_ci}
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_cibool VkYcbcrSamplerHelper::createBackendTexture(uint32_t width, uint32_t height) {
53cb93a386Sopenharmony_ci    GrVkGpu* vkGpu = this->vkGpu();
54cb93a386Sopenharmony_ci    VkResult result;
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci    // Verify that the image format is supported.
57cb93a386Sopenharmony_ci    VkFormatProperties formatProperties;
58cb93a386Sopenharmony_ci    GR_VK_CALL(vkGpu->vkInterface(),
59cb93a386Sopenharmony_ci               GetPhysicalDeviceFormatProperties(vkGpu->physicalDevice(),
60cb93a386Sopenharmony_ci                                                 VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
61cb93a386Sopenharmony_ci                                                 &formatProperties));
62cb93a386Sopenharmony_ci    if (!(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
63cb93a386Sopenharmony_ci        // VK_FORMAT_G8_B8R8_2PLANE_420_UNORM is not supported
64cb93a386Sopenharmony_ci        return false;
65cb93a386Sopenharmony_ci    }
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci    // Create YCbCr image.
68cb93a386Sopenharmony_ci    VkImageCreateInfo vkImageInfo = {};
69cb93a386Sopenharmony_ci    vkImageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
70cb93a386Sopenharmony_ci    vkImageInfo.imageType = VK_IMAGE_TYPE_2D;
71cb93a386Sopenharmony_ci    vkImageInfo.format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
72cb93a386Sopenharmony_ci    vkImageInfo.extent = VkExtent3D{width, height, 1};
73cb93a386Sopenharmony_ci    vkImageInfo.mipLevels = 1;
74cb93a386Sopenharmony_ci    vkImageInfo.arrayLayers = 1;
75cb93a386Sopenharmony_ci    vkImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
76cb93a386Sopenharmony_ci    vkImageInfo.tiling = VK_IMAGE_TILING_LINEAR;
77cb93a386Sopenharmony_ci    vkImageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
78cb93a386Sopenharmony_ci                        VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
79cb93a386Sopenharmony_ci    vkImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
80cb93a386Sopenharmony_ci    vkImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
81cb93a386Sopenharmony_ci
82cb93a386Sopenharmony_ci    SkASSERT(fImage == VK_NULL_HANDLE);
83cb93a386Sopenharmony_ci    GR_VK_CALL_RESULT(vkGpu, result, CreateImage(vkGpu->device(), &vkImageInfo, nullptr, &fImage));
84cb93a386Sopenharmony_ci    if (result != VK_SUCCESS) {
85cb93a386Sopenharmony_ci        return false;
86cb93a386Sopenharmony_ci    }
87cb93a386Sopenharmony_ci
88cb93a386Sopenharmony_ci    VkMemoryRequirements requirements;
89cb93a386Sopenharmony_ci    GR_VK_CALL(vkGpu->vkInterface(), GetImageMemoryRequirements(vkGpu->device(),
90cb93a386Sopenharmony_ci                                                                fImage,
91cb93a386Sopenharmony_ci                                                                &requirements));
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_ci    uint32_t memoryTypeIndex = 0;
94cb93a386Sopenharmony_ci    bool foundHeap = false;
95cb93a386Sopenharmony_ci    VkPhysicalDeviceMemoryProperties phyDevMemProps;
96cb93a386Sopenharmony_ci    GR_VK_CALL(vkGpu->vkInterface(), GetPhysicalDeviceMemoryProperties(vkGpu->physicalDevice(),
97cb93a386Sopenharmony_ci                                                                       &phyDevMemProps));
98cb93a386Sopenharmony_ci    for (uint32_t i = 0; i < phyDevMemProps.memoryTypeCount && !foundHeap; ++i) {
99cb93a386Sopenharmony_ci        if (requirements.memoryTypeBits & (1 << i)) {
100cb93a386Sopenharmony_ci            // Map host-visible memory.
101cb93a386Sopenharmony_ci            if (phyDevMemProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
102cb93a386Sopenharmony_ci                memoryTypeIndex = i;
103cb93a386Sopenharmony_ci                foundHeap = true;
104cb93a386Sopenharmony_ci            }
105cb93a386Sopenharmony_ci        }
106cb93a386Sopenharmony_ci    }
107cb93a386Sopenharmony_ci    if (!foundHeap) {
108cb93a386Sopenharmony_ci        return false;
109cb93a386Sopenharmony_ci    }
110cb93a386Sopenharmony_ci
111cb93a386Sopenharmony_ci    VkMemoryAllocateInfo allocInfo = {};
112cb93a386Sopenharmony_ci    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
113cb93a386Sopenharmony_ci    allocInfo.allocationSize = requirements.size;
114cb93a386Sopenharmony_ci    allocInfo.memoryTypeIndex = memoryTypeIndex;
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci    SkASSERT(fImageMemory == VK_NULL_HANDLE);
117cb93a386Sopenharmony_ci    GR_VK_CALL_RESULT(vkGpu, result, AllocateMemory(vkGpu->device(), &allocInfo,
118cb93a386Sopenharmony_ci                                                    nullptr, &fImageMemory));
119cb93a386Sopenharmony_ci    if (result != VK_SUCCESS) {
120cb93a386Sopenharmony_ci        return false;
121cb93a386Sopenharmony_ci    }
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_ci    void* mappedBuffer;
124cb93a386Sopenharmony_ci    GR_VK_CALL_RESULT(vkGpu, result, MapMemory(vkGpu->device(), fImageMemory, 0u,
125cb93a386Sopenharmony_ci                                               requirements.size, 0u, &mappedBuffer));
126cb93a386Sopenharmony_ci    if (result != VK_SUCCESS) {
127cb93a386Sopenharmony_ci        return false;
128cb93a386Sopenharmony_ci    }
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci    // Write Y channel.
131cb93a386Sopenharmony_ci    VkImageSubresource subresource;
132cb93a386Sopenharmony_ci    subresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT;
133cb93a386Sopenharmony_ci    subresource.mipLevel = 0;
134cb93a386Sopenharmony_ci    subresource.arrayLayer = 0;
135cb93a386Sopenharmony_ci
136cb93a386Sopenharmony_ci    VkSubresourceLayout yLayout;
137cb93a386Sopenharmony_ci    GR_VK_CALL(vkGpu->vkInterface(), GetImageSubresourceLayout(vkGpu->device(), fImage,
138cb93a386Sopenharmony_ci                                                               &subresource, &yLayout));
139cb93a386Sopenharmony_ci    uint8_t* bufferData = reinterpret_cast<uint8_t*>(mappedBuffer) + yLayout.offset;
140cb93a386Sopenharmony_ci    for (size_t y = 0; y < height; ++y) {
141cb93a386Sopenharmony_ci        for (size_t x = 0; x < width; ++x) {
142cb93a386Sopenharmony_ci            bufferData[y * yLayout.rowPitch + x] = GetExpectedY(x, y, width, height);
143cb93a386Sopenharmony_ci        }
144cb93a386Sopenharmony_ci    }
145cb93a386Sopenharmony_ci
146cb93a386Sopenharmony_ci    // Write UV channels.
147cb93a386Sopenharmony_ci    subresource.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT;
148cb93a386Sopenharmony_ci    VkSubresourceLayout uvLayout;
149cb93a386Sopenharmony_ci    GR_VK_CALL(vkGpu->vkInterface(), GetImageSubresourceLayout(vkGpu->device(), fImage,
150cb93a386Sopenharmony_ci                                                               &subresource, &uvLayout));
151cb93a386Sopenharmony_ci    bufferData = reinterpret_cast<uint8_t*>(mappedBuffer) + uvLayout.offset;
152cb93a386Sopenharmony_ci    for (size_t y = 0; y < height / 2; ++y) {
153cb93a386Sopenharmony_ci        for (size_t x = 0; x < width / 2; ++x) {
154cb93a386Sopenharmony_ci            auto [u, v] = GetExpectedUV(2*x, 2*y, width, height);
155cb93a386Sopenharmony_ci            bufferData[y * uvLayout.rowPitch + x * 2] = u;
156cb93a386Sopenharmony_ci            bufferData[y * uvLayout.rowPitch + x * 2 + 1] = v;
157cb93a386Sopenharmony_ci        }
158cb93a386Sopenharmony_ci    }
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ci    VkMappedMemoryRange flushRange;
161cb93a386Sopenharmony_ci    flushRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
162cb93a386Sopenharmony_ci    flushRange.pNext = nullptr;
163cb93a386Sopenharmony_ci    flushRange.memory = fImageMemory;
164cb93a386Sopenharmony_ci    flushRange.offset = 0;
165cb93a386Sopenharmony_ci    flushRange.size = VK_WHOLE_SIZE;
166cb93a386Sopenharmony_ci    GR_VK_CALL_RESULT(vkGpu, result, FlushMappedMemoryRanges(vkGpu->device(), 1, &flushRange));
167cb93a386Sopenharmony_ci    if (result != VK_SUCCESS) {
168cb93a386Sopenharmony_ci        return false;
169cb93a386Sopenharmony_ci    }
170cb93a386Sopenharmony_ci    GR_VK_CALL(vkGpu->vkInterface(), UnmapMemory(vkGpu->device(), fImageMemory));
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_ci    // Bind image memory.
173cb93a386Sopenharmony_ci    GR_VK_CALL_RESULT(vkGpu, result, BindImageMemory(vkGpu->device(), fImage, fImageMemory, 0u));
174cb93a386Sopenharmony_ci    if (result != VK_SUCCESS) {
175cb93a386Sopenharmony_ci        return false;
176cb93a386Sopenharmony_ci    }
177cb93a386Sopenharmony_ci
178cb93a386Sopenharmony_ci    // Wrap the image into SkImage.
179cb93a386Sopenharmony_ci    GrVkYcbcrConversionInfo ycbcrInfo = {vkImageInfo.format,
180cb93a386Sopenharmony_ci                                         /*externalFormat=*/0,
181cb93a386Sopenharmony_ci                                         VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709,
182cb93a386Sopenharmony_ci                                         VK_SAMPLER_YCBCR_RANGE_ITU_NARROW,
183cb93a386Sopenharmony_ci                                         VK_CHROMA_LOCATION_COSITED_EVEN,
184cb93a386Sopenharmony_ci                                         VK_CHROMA_LOCATION_COSITED_EVEN,
185cb93a386Sopenharmony_ci                                         VK_FILTER_LINEAR,
186cb93a386Sopenharmony_ci                                         false,
187cb93a386Sopenharmony_ci                                         formatProperties.linearTilingFeatures};
188cb93a386Sopenharmony_ci    GrVkAlloc alloc;
189cb93a386Sopenharmony_ci    alloc.fMemory = fImageMemory;
190cb93a386Sopenharmony_ci    alloc.fOffset = 0;
191cb93a386Sopenharmony_ci    alloc.fSize = requirements.size;
192cb93a386Sopenharmony_ci
193cb93a386Sopenharmony_ci    GrVkImageInfo imageInfo = {fImage,
194cb93a386Sopenharmony_ci                               alloc,
195cb93a386Sopenharmony_ci                               VK_IMAGE_TILING_LINEAR,
196cb93a386Sopenharmony_ci                               VK_IMAGE_LAYOUT_UNDEFINED,
197cb93a386Sopenharmony_ci                               vkImageInfo.format,
198cb93a386Sopenharmony_ci                               vkImageInfo.usage,
199cb93a386Sopenharmony_ci                               1 /* sample count */,
200cb93a386Sopenharmony_ci                               1 /* levelCount */,
201cb93a386Sopenharmony_ci                               VK_QUEUE_FAMILY_IGNORED,
202cb93a386Sopenharmony_ci                               GrProtected::kNo,
203cb93a386Sopenharmony_ci                               ycbcrInfo};
204cb93a386Sopenharmony_ci
205cb93a386Sopenharmony_ci    fTexture = GrBackendTexture(width, height, imageInfo);
206cb93a386Sopenharmony_ci    return true;
207cb93a386Sopenharmony_ci}
208cb93a386Sopenharmony_ci
209cb93a386Sopenharmony_ci#endif // SK_VULKAN
210