1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2019 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 "include/core/SkTypes.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#if SK_SUPPORT_GPU && defined(SK_VULKAN)
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
13cb93a386Sopenharmony_ci#include "include/core/SkImage.h"
14cb93a386Sopenharmony_ci#include "include/core/SkSurface.h"
15cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h"
16cb93a386Sopenharmony_ci#include "tests/Test.h"
17cb93a386Sopenharmony_ci#include "tools/gpu/vk/VkTestHelper.h"
18cb93a386Sopenharmony_ci#include "tools/gpu/vk/VkYcbcrSamplerHelper.h"
19cb93a386Sopenharmony_ci
20cb93a386Sopenharmony_ciconst size_t kImageWidth = 8;
21cb93a386Sopenharmony_ciconst size_t kImageHeight = 8;
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_cistatic int round_and_clamp(float x) {
24cb93a386Sopenharmony_ci    int r = static_cast<int>(round(x));
25cb93a386Sopenharmony_ci    if (r > 255) return 255;
26cb93a386Sopenharmony_ci    if (r < 0) return 0;
27cb93a386Sopenharmony_ci    return r;
28cb93a386Sopenharmony_ci}
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_ciDEF_GPUTEST(VkYCbcrSampler_DrawImageWithYcbcrSampler, reporter, options) {
31cb93a386Sopenharmony_ci    VkTestHelper testHelper(false);
32cb93a386Sopenharmony_ci    if (!testHelper.init()) {
33cb93a386Sopenharmony_ci        ERRORF(reporter, "VkTestHelper initialization failed.");
34cb93a386Sopenharmony_ci        return;
35cb93a386Sopenharmony_ci    }
36cb93a386Sopenharmony_ci
37cb93a386Sopenharmony_ci    VkYcbcrSamplerHelper ycbcrHelper(testHelper.directContext());
38cb93a386Sopenharmony_ci    if (!ycbcrHelper.isYCbCrSupported()) {
39cb93a386Sopenharmony_ci        return;
40cb93a386Sopenharmony_ci    }
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_ci    if (!ycbcrHelper.createBackendTexture(kImageWidth, kImageHeight)) {
43cb93a386Sopenharmony_ci        ERRORF(reporter, "Failed to create I420 backend texture");
44cb93a386Sopenharmony_ci        return;
45cb93a386Sopenharmony_ci    }
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ci    sk_sp<SkImage> srcImage = SkImage::MakeFromTexture(testHelper.directContext(),
48cb93a386Sopenharmony_ci                                                       ycbcrHelper.backendTexture(),
49cb93a386Sopenharmony_ci                                                       kTopLeft_GrSurfaceOrigin,
50cb93a386Sopenharmony_ci                                                       kRGB_888x_SkColorType,
51cb93a386Sopenharmony_ci                                                       kPremul_SkAlphaType,
52cb93a386Sopenharmony_ci                                                       nullptr);
53cb93a386Sopenharmony_ci    if (!srcImage) {
54cb93a386Sopenharmony_ci        ERRORF(reporter, "Failed to create I420 image");
55cb93a386Sopenharmony_ci        return;
56cb93a386Sopenharmony_ci    }
57cb93a386Sopenharmony_ci
58cb93a386Sopenharmony_ci    sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
59cb93a386Sopenharmony_ci            testHelper.directContext(), SkBudgeted::kNo,
60cb93a386Sopenharmony_ci            SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType, kPremul_SkAlphaType));
61cb93a386Sopenharmony_ci    if (!surface) {
62cb93a386Sopenharmony_ci        ERRORF(reporter, "Failed to create target SkSurface");
63cb93a386Sopenharmony_ci        return;
64cb93a386Sopenharmony_ci    }
65cb93a386Sopenharmony_ci    surface->getCanvas()->drawImage(srcImage, 0, 0);
66cb93a386Sopenharmony_ci    surface->flushAndSubmit();
67cb93a386Sopenharmony_ci
68cb93a386Sopenharmony_ci    std::vector<uint8_t> readbackData(kImageWidth * kImageHeight * 4);
69cb93a386Sopenharmony_ci    if (!surface->readPixels(SkImageInfo::Make(kImageWidth, kImageHeight, kRGBA_8888_SkColorType,
70cb93a386Sopenharmony_ci                                               kOpaque_SkAlphaType),
71cb93a386Sopenharmony_ci                             readbackData.data(), kImageWidth * 4, 0, 0)) {
72cb93a386Sopenharmony_ci        ERRORF(reporter, "Readback failed");
73cb93a386Sopenharmony_ci        return;
74cb93a386Sopenharmony_ci    }
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_ci    // Allow resulting color to be off by 1 in each channel as some Vulkan implementations do not
77cb93a386Sopenharmony_ci    // round YCbCr sampler result properly.
78cb93a386Sopenharmony_ci    const int kColorTolerance = 1;
79cb93a386Sopenharmony_ci
80cb93a386Sopenharmony_ci    // Verify results only for pixels with even coordinates, since others use
81cb93a386Sopenharmony_ci    // interpolated U & V channels.
82cb93a386Sopenharmony_ci    for (size_t y = 0; y < kImageHeight; y += 2) {
83cb93a386Sopenharmony_ci        for (size_t x = 0; x < kImageWidth; x += 2) {
84cb93a386Sopenharmony_ci            auto y2 = VkYcbcrSamplerHelper::GetExpectedY(x, y, kImageWidth, kImageHeight);
85cb93a386Sopenharmony_ci            auto [u, v] = VkYcbcrSamplerHelper::GetExpectedUV(x, y, kImageWidth, kImageHeight);
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ci            // createI420Image() initializes the image with VK_SAMPLER_YCBCR_RANGE_ITU_NARROW.
88cb93a386Sopenharmony_ci            float yChannel = (static_cast<float>(y2) - 16.0) / 219.0;
89cb93a386Sopenharmony_ci            float uChannel = (static_cast<float>(u) - 128.0) / 224.0;
90cb93a386Sopenharmony_ci            float vChannel = (static_cast<float>(v) - 128.0) / 224.0;
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci            // BR.709 conversion as specified in
93cb93a386Sopenharmony_ci            // https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#MODEL_YUV
94cb93a386Sopenharmony_ci            int expectedR = round_and_clamp((yChannel + 1.5748f * vChannel) * 255.0);
95cb93a386Sopenharmony_ci            int expectedG = round_and_clamp((yChannel - 0.13397432f / 0.7152f * uChannel -
96cb93a386Sopenharmony_ci                                             0.33480248f / 0.7152f * vChannel) *
97cb93a386Sopenharmony_ci                                            255.0);
98cb93a386Sopenharmony_ci            int expectedB = round_and_clamp((yChannel + 1.8556f * uChannel) * 255.0);
99cb93a386Sopenharmony_ci
100cb93a386Sopenharmony_ci            int r = readbackData[(y * kImageWidth + x) * 4];
101cb93a386Sopenharmony_ci            if (abs(r - expectedR) > kColorTolerance) {
102cb93a386Sopenharmony_ci                ERRORF(reporter, "R should be %d, but is %d at (%d, %d)", expectedR, r, x, y);
103cb93a386Sopenharmony_ci            }
104cb93a386Sopenharmony_ci
105cb93a386Sopenharmony_ci            int g = readbackData[(y * kImageWidth + x) * 4 + 1];
106cb93a386Sopenharmony_ci            if (abs(g - expectedG) > kColorTolerance) {
107cb93a386Sopenharmony_ci                ERRORF(reporter, "G should be %d, but is %d at (%d, %d)", expectedG, g, x, y);
108cb93a386Sopenharmony_ci            }
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci            int b = readbackData[(y * kImageWidth + x) * 4 + 2];
111cb93a386Sopenharmony_ci            if (abs(b - expectedB) > kColorTolerance) {
112cb93a386Sopenharmony_ci                ERRORF(reporter, "B should be %d, but is %d at (%d, %d)", expectedB, b, x, y);
113cb93a386Sopenharmony_ci            }
114cb93a386Sopenharmony_ci        }
115cb93a386Sopenharmony_ci    }
116cb93a386Sopenharmony_ci}
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_ci// Verifies that it's not possible to allocate Ycbcr texture directly.
119cb93a386Sopenharmony_ciDEF_GPUTEST(VkYCbcrSampler_NoYcbcrSurface, reporter, options) {
120cb93a386Sopenharmony_ci    VkTestHelper testHelper(false);
121cb93a386Sopenharmony_ci    if (!testHelper.init()) {
122cb93a386Sopenharmony_ci        ERRORF(reporter, "VkTestHelper initialization failed.");
123cb93a386Sopenharmony_ci        return;
124cb93a386Sopenharmony_ci    }
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ci    VkYcbcrSamplerHelper ycbcrHelper(testHelper.directContext());
127cb93a386Sopenharmony_ci    if (!ycbcrHelper.isYCbCrSupported()) {
128cb93a386Sopenharmony_ci        return;
129cb93a386Sopenharmony_ci    }
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_ci    GrBackendTexture texture = testHelper.directContext()->createBackendTexture(
132cb93a386Sopenharmony_ci            kImageWidth, kImageHeight, GrBackendFormat::MakeVk(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM),
133cb93a386Sopenharmony_ci            GrMipmapped::kNo, GrRenderable::kNo, GrProtected::kNo);
134cb93a386Sopenharmony_ci    if (texture.isValid()) {
135cb93a386Sopenharmony_ci        ERRORF(reporter,
136cb93a386Sopenharmony_ci               "GrDirectContext::createBackendTexture() didn't fail as expected for Ycbcr format.");
137cb93a386Sopenharmony_ci    }
138cb93a386Sopenharmony_ci}
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ci#endif  // SK_SUPPORT_GPU && defined(SK_VULKAN)
141