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