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