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