2 * Copyright 2019 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "include/core/SkTypes.h"
10 #if SK_SUPPORT_GPU && defined(SK_VULKAN)
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkSurface.h"
16 #include "include/gpu/GrDirectContext.h"
17 #include "tests/Test.h"
18 #include "tools/gpu/vk/VkTestHelper.h"
19 #include "tools/gpu/vk/VkYcbcrSamplerHelper.h"
21 const size_t kImageWidth = 8;
22 const size_t kImageHeight = 8;
24 static int round_and_clamp(float x) {
25 int r = static_cast<int>(round(x));
26 if (r > 255) return 255;
31 DEF_GPUTEST(VkYCbcrSampler_DrawImageWithYcbcrSampler, reporter, options) {
32 VkTestHelper testHelper(false);
33 if (!testHelper.init()) {
34 ERRORF(reporter, "VkTestHelper initialization failed.");
38 VkYcbcrSamplerHelper ycbcrHelper(testHelper.directContext());
39 if (!ycbcrHelper.isYCbCrSupported()) {
43 if (!ycbcrHelper.createBackendTexture(kImageWidth, kImageHeight)) {
44 ERRORF(reporter, "Failed to create I420 backend texture");
48 sk_sp<SkImage> srcImage = SkImage::MakeFromTexture(testHelper.directContext(),
49 ycbcrHelper.backendTexture(),
50 kTopLeft_GrSurfaceOrigin,
51 kRGB_888x_SkColorType,
55 ERRORF(reporter, "Failed to create I420 image");
59 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
60 testHelper.directContext(), SkBudgeted::kNo,
61 SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType, kPremul_SkAlphaType));
63 ERRORF(reporter, "Failed to create target SkSurface");
66 surface->getCanvas()->drawImage(srcImage, 0, 0);
67 surface->flushAndSubmit();
69 std::vector<uint8_t> readbackData(kImageWidth * kImageHeight * 4);
70 if (!surface->readPixels(SkImageInfo::Make(kImageWidth, kImageHeight, kRGBA_8888_SkColorType,
72 readbackData.data(), kImageWidth * 4, 0, 0)) {
73 ERRORF(reporter, "Readback failed");
77 // Allow resulting color to be off by 1 in each channel as some Vulkan implementations do not
78 // round YCbCr sampler result properly.
79 const int kColorTolerance = 1;
81 // Verify results only for pixels with even coordinates, since others use
82 // interpolated U & V channels.
83 for (size_t y = 0; y < kImageHeight; y += 2) {
84 for (size_t x = 0; x < kImageWidth; x += 2) {
85 auto y2 = VkYcbcrSamplerHelper::GetExpectedY(x, y, kImageWidth, kImageHeight);
86 auto [u, v] = VkYcbcrSamplerHelper::GetExpectedUV(x, y, kImageWidth, kImageHeight);
88 // createI420Image() initializes the image with VK_SAMPLER_YCBCR_RANGE_ITU_NARROW.
89 float yChannel = (static_cast<float>(y2) - 16.0) / 219.0;
90 float uChannel = (static_cast<float>(u) - 128.0) / 224.0;
91 float vChannel = (static_cast<float>(v) - 128.0) / 224.0;
93 // BR.709 conversion as specified in
94 // https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#MODEL_YUV
95 int expectedR = round_and_clamp((yChannel + 1.5748f * vChannel) * 255.0);
96 int expectedG = round_and_clamp((yChannel - 0.13397432f / 0.7152f * uChannel -
97 0.33480248f / 0.7152f * vChannel) *
99 int expectedB = round_and_clamp((yChannel + 1.8556f * uChannel) * 255.0);
101 int r = readbackData[(y * kImageWidth + x) * 4];
102 if (abs(r - expectedR) > kColorTolerance) {
103 ERRORF(reporter, "R should be %d, but is %d at (%zu, %zu)", expectedR, r, x, y);
106 int g = readbackData[(y * kImageWidth + x) * 4 + 1];
107 if (abs(g - expectedG) > kColorTolerance) {
108 ERRORF(reporter, "G should be %d, but is %d at (%zu, %zu)", expectedG, g, x, y);
111 int b = readbackData[(y * kImageWidth + x) * 4 + 2];
112 if (abs(b - expectedB) > kColorTolerance) {
113 ERRORF(reporter, "B should be %d, but is %d at (%zu, %zu)", expectedB, b, x, y);
119 // Verifies that it's not possible to allocate Ycbcr texture directly.
120 DEF_GPUTEST(VkYCbcrSampler_NoYcbcrSurface, reporter, options) {
121 VkTestHelper testHelper(false);
122 if (!testHelper.init()) {
123 ERRORF(reporter, "VkTestHelper initialization failed.");
127 VkYcbcrSamplerHelper ycbcrHelper(testHelper.directContext());
128 if (!ycbcrHelper.isYCbCrSupported()) {
132 GrBackendTexture texture = testHelper.directContext()->createBackendTexture(
133 kImageWidth, kImageHeight, GrBackendFormat::MakeVk(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM),
134 GrMipmapped::kNo, GrRenderable::kNo, GrProtected::kNo);
135 if (texture.isValid()) {
137 "GrDirectContext::createBackendTexture() didn't fail as expected for Ycbcr format.");
141 #endif // SK_SUPPORT_GPU && defined(SK_VULKAN)