xref: /aosp_15_r20/external/skia/tests/VkYcbcrSamplerTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 defined(SK_GANESH) && defined(SK_VULKAN)
11 #include "include/core/SkAlphaType.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkColorType.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkRefCnt.h"
17 #include "include/core/SkSurface.h"
18 #include "include/core/SkTypes.h"
19 #include "include/gpu/GpuTypes.h"
20 #include "include/gpu/ganesh/GrBackendSurface.h"
21 #include "include/gpu/ganesh/GrDirectContext.h"
22 #include "include/gpu/ganesh/GrTypes.h"
23 #include "include/gpu/ganesh/SkImageGanesh.h"
24 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
25 #include "include/gpu/ganesh/vk/GrVkBackendSurface.h"
26 #include "tests/CtsEnforcement.h"
27 #include "tests/Test.h"
28 #include "tools/gpu/vk/VkYcbcrSamplerHelper.h"
29 
30 #include <vulkan/vulkan_core.h>
31 
32 #include <cmath>
33 #include <cstddef>
34 #include <cstdint>
35 #include <vector>
36 
37 class SkImage;
38 struct GrContextOptions;
39 const size_t kImageWidth = 8;
40 const size_t kImageHeight = 8;
41 
round_and_clamp(float x)42 static int round_and_clamp(float x) {
43     int r = static_cast<int>(round(x));
44     if (r > 255) return 255;
45     if (r < 0) return 0;
46     return r;
47 }
48 
DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VkYCbcrSampler_DrawImageWithYcbcrSampler,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)49 DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VkYCbcrSampler_DrawImageWithYcbcrSampler,
50                                    reporter,
51                                    ctxInfo,
52                                    CtsEnforcement::kApiLevel_T) {
53     GrDirectContext* dContext = ctxInfo.directContext();
54 
55     VkYcbcrSamplerHelper ycbcrHelper(dContext);
56     if (!ycbcrHelper.isYCbCrSupported()) {
57         return;
58     }
59 
60     if (!ycbcrHelper.createGrBackendTexture(kImageWidth, kImageHeight)) {
61         ERRORF(reporter, "Failed to create I420 backend texture");
62         return;
63     }
64 
65     sk_sp<SkImage> srcImage = SkImages::BorrowTextureFrom(dContext,
66                                                           ycbcrHelper.grBackendTexture(),
67                                                           kTopLeft_GrSurfaceOrigin,
68                                                           kRGB_888x_SkColorType,
69                                                           kPremul_SkAlphaType,
70                                                           nullptr);
71     if (!srcImage) {
72         ERRORF(reporter, "Failed to create I420 image");
73         return;
74     }
75 
76     sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(
77             dContext,
78             skgpu::Budgeted::kNo,
79             SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType, kPremul_SkAlphaType));
80     if (!surface) {
81         ERRORF(reporter, "Failed to create target SkSurface");
82         return;
83     }
84     surface->getCanvas()->drawImage(srcImage, 0, 0);
85     dContext->flushAndSubmit(surface.get());
86 
87     std::vector<uint8_t> readbackData(kImageWidth * kImageHeight * 4);
88     if (!surface->readPixels(SkImageInfo::Make(kImageWidth, kImageHeight, kRGBA_8888_SkColorType,
89                                                kOpaque_SkAlphaType),
90                              readbackData.data(), kImageWidth * 4, 0, 0)) {
91         ERRORF(reporter, "Readback failed");
92         return;
93     }
94 
95     // Allow resulting color to be off by 1 in each channel as some Vulkan implementations do not
96     // round YCbCr sampler result properly.
97     const int kColorTolerance = 1;
98 
99     // Verify results only for pixels with even coordinates, since others use
100     // interpolated U & V channels.
101     for (size_t y = 0; y < kImageHeight; y += 2) {
102         for (size_t x = 0; x < kImageWidth; x += 2) {
103             auto y2 = VkYcbcrSamplerHelper::GetExpectedY(x, y, kImageWidth, kImageHeight);
104             auto [u, v] = VkYcbcrSamplerHelper::GetExpectedUV(x, y, kImageWidth, kImageHeight);
105 
106             // createI420Image() initializes the image with VK_SAMPLER_YCBCR_RANGE_ITU_NARROW.
107             float yChannel = (static_cast<float>(y2) - 16.0) / 219.0;
108             float uChannel = (static_cast<float>(u) - 128.0) / 224.0;
109             float vChannel = (static_cast<float>(v) - 128.0) / 224.0;
110 
111             // BR.709 conversion as specified in
112             // https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#MODEL_YUV
113             int expectedR = round_and_clamp((yChannel + 1.5748f * vChannel) * 255.0);
114             int expectedG = round_and_clamp((yChannel - 0.13397432f / 0.7152f * uChannel -
115                                              0.33480248f / 0.7152f * vChannel) *
116                                             255.0);
117             int expectedB = round_and_clamp((yChannel + 1.8556f * uChannel) * 255.0);
118 
119             int r = readbackData[(y * kImageWidth + x) * 4];
120             if (abs(r - expectedR) > kColorTolerance) {
121                 ERRORF(reporter, "R should be %d, but is %d at (%zu, %zu)", expectedR, r, x, y);
122             }
123 
124             int g = readbackData[(y * kImageWidth + x) * 4 + 1];
125             if (abs(g - expectedG) > kColorTolerance) {
126                 ERRORF(reporter, "G should be %d, but is %d at (%zu, %zu)", expectedG, g, x, y);
127             }
128 
129             int b = readbackData[(y * kImageWidth + x) * 4 + 2];
130             if (abs(b - expectedB) > kColorTolerance) {
131                 ERRORF(reporter, "B should be %d, but is %d at (%zu, %zu)", expectedB, b, x, y);
132             }
133         }
134     }
135 }
136 
137 // Verifies that it's not possible to allocate Ycbcr texture directly.
DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VkYCbcrSampler_NoYcbcrSurface,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)138 DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VkYCbcrSampler_NoYcbcrSurface,
139                                    reporter,
140                                    ctxInfo,
141                                    CtsEnforcement::kApiLevel_T) {
142     GrDirectContext* dContext = ctxInfo.directContext();
143 
144     VkYcbcrSamplerHelper ycbcrHelper(dContext);
145     if (!ycbcrHelper.isYCbCrSupported()) {
146         return;
147     }
148 
149     GrBackendTexture texture = dContext->createBackendTexture(
150             kImageWidth,
151             kImageHeight,
152             GrBackendFormats::MakeVk(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM),
153             skgpu::Mipmapped::kNo,
154             GrRenderable::kNo,
155             GrProtected::kNo);
156     if (texture.isValid()) {
157         ERRORF(reporter,
158                "GrDirectContext::createBackendTexture() didn't fail as expected for Ycbcr format.");
159     }
160 }
161 
162 #endif  // defined(SK_GANESH) && defined(SK_VULKAN)
163