|  | /* | 
|  | * Copyright 2019 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "include/core/SkTypes.h" | 
|  |  | 
|  | #if defined(SK_GANESH) && defined(SK_VULKAN) | 
|  | #include "include/core/SkAlphaType.h" | 
|  | #include "include/core/SkCanvas.h" | 
|  | #include "include/core/SkColorSpace.h" | 
|  | #include "include/core/SkColorType.h" | 
|  | #include "include/core/SkImageInfo.h" | 
|  | #include "include/core/SkRefCnt.h" | 
|  | #include "include/core/SkSurface.h" | 
|  | #include "include/core/SkTypes.h" | 
|  | #include "include/gpu/GpuTypes.h" | 
|  | #include "include/gpu/GrBackendSurface.h" | 
|  | #include "include/gpu/GrDirectContext.h" | 
|  | #include "include/gpu/GrTypes.h" | 
|  | #include "include/gpu/ganesh/SkImageGanesh.h" | 
|  | #include "include/gpu/ganesh/SkSurfaceGanesh.h" | 
|  | #include "include/gpu/ganesh/vk/GrVkBackendSurface.h" | 
|  | #include "tests/CtsEnforcement.h" | 
|  | #include "tests/Test.h" | 
|  | #include "tools/gpu/vk/VkYcbcrSamplerHelper.h" | 
|  |  | 
|  | #include <vulkan/vulkan_core.h> | 
|  |  | 
|  | #include <cmath> | 
|  | #include <cstddef> | 
|  | #include <cstdint> | 
|  | #include <vector> | 
|  |  | 
|  | class SkImage; | 
|  | struct GrContextOptions; | 
|  | const size_t kImageWidth = 8; | 
|  | const size_t kImageHeight = 8; | 
|  |  | 
|  | static int round_and_clamp(float x) { | 
|  | int r = static_cast<int>(round(x)); | 
|  | if (r > 255) return 255; | 
|  | if (r < 0) return 0; | 
|  | return r; | 
|  | } | 
|  |  | 
|  | DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VkYCbcrSampler_DrawImageWithYcbcrSampler, | 
|  | reporter, | 
|  | ctxInfo, | 
|  | CtsEnforcement::kApiLevel_T) { | 
|  | GrDirectContext* dContext = ctxInfo.directContext(); | 
|  |  | 
|  | VkYcbcrSamplerHelper ycbcrHelper(dContext); | 
|  | if (!ycbcrHelper.isYCbCrSupported()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!ycbcrHelper.createBackendTexture(kImageWidth, kImageHeight)) { | 
|  | ERRORF(reporter, "Failed to create I420 backend texture"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | sk_sp<SkImage> srcImage = SkImages::BorrowTextureFrom(dContext, | 
|  | ycbcrHelper.backendTexture(), | 
|  | kTopLeft_GrSurfaceOrigin, | 
|  | kRGB_888x_SkColorType, | 
|  | kPremul_SkAlphaType, | 
|  | nullptr); | 
|  | if (!srcImage) { | 
|  | ERRORF(reporter, "Failed to create I420 image"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | sk_sp<SkSurface> surface = SkSurfaces::RenderTarget( | 
|  | dContext, | 
|  | skgpu::Budgeted::kNo, | 
|  | SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType, kPremul_SkAlphaType)); | 
|  | if (!surface) { | 
|  | ERRORF(reporter, "Failed to create target SkSurface"); | 
|  | return; | 
|  | } | 
|  | surface->getCanvas()->drawImage(srcImage, 0, 0); | 
|  | dContext->flushAndSubmit(surface.get()); | 
|  |  | 
|  | std::vector<uint8_t> readbackData(kImageWidth * kImageHeight * 4); | 
|  | if (!surface->readPixels(SkImageInfo::Make(kImageWidth, kImageHeight, kRGBA_8888_SkColorType, | 
|  | kOpaque_SkAlphaType), | 
|  | readbackData.data(), kImageWidth * 4, 0, 0)) { | 
|  | ERRORF(reporter, "Readback failed"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Allow resulting color to be off by 1 in each channel as some Vulkan implementations do not | 
|  | // round YCbCr sampler result properly. | 
|  | const int kColorTolerance = 1; | 
|  |  | 
|  | // Verify results only for pixels with even coordinates, since others use | 
|  | // interpolated U & V channels. | 
|  | for (size_t y = 0; y < kImageHeight; y += 2) { | 
|  | for (size_t x = 0; x < kImageWidth; x += 2) { | 
|  | auto y2 = VkYcbcrSamplerHelper::GetExpectedY(x, y, kImageWidth, kImageHeight); | 
|  | auto [u, v] = VkYcbcrSamplerHelper::GetExpectedUV(x, y, kImageWidth, kImageHeight); | 
|  |  | 
|  | // createI420Image() initializes the image with VK_SAMPLER_YCBCR_RANGE_ITU_NARROW. | 
|  | float yChannel = (static_cast<float>(y2) - 16.0) / 219.0; | 
|  | float uChannel = (static_cast<float>(u) - 128.0) / 224.0; | 
|  | float vChannel = (static_cast<float>(v) - 128.0) / 224.0; | 
|  |  | 
|  | // BR.709 conversion as specified in | 
|  | // https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html#MODEL_YUV | 
|  | int expectedR = round_and_clamp((yChannel + 1.5748f * vChannel) * 255.0); | 
|  | int expectedG = round_and_clamp((yChannel - 0.13397432f / 0.7152f * uChannel - | 
|  | 0.33480248f / 0.7152f * vChannel) * | 
|  | 255.0); | 
|  | int expectedB = round_and_clamp((yChannel + 1.8556f * uChannel) * 255.0); | 
|  |  | 
|  | int r = readbackData[(y * kImageWidth + x) * 4]; | 
|  | if (abs(r - expectedR) > kColorTolerance) { | 
|  | ERRORF(reporter, "R should be %d, but is %d at (%zu, %zu)", expectedR, r, x, y); | 
|  | } | 
|  |  | 
|  | int g = readbackData[(y * kImageWidth + x) * 4 + 1]; | 
|  | if (abs(g - expectedG) > kColorTolerance) { | 
|  | ERRORF(reporter, "G should be %d, but is %d at (%zu, %zu)", expectedG, g, x, y); | 
|  | } | 
|  |  | 
|  | int b = readbackData[(y * kImageWidth + x) * 4 + 2]; | 
|  | if (abs(b - expectedB) > kColorTolerance) { | 
|  | ERRORF(reporter, "B should be %d, but is %d at (%zu, %zu)", expectedB, b, x, y); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Verifies that it's not possible to allocate Ycbcr texture directly. | 
|  | DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VkYCbcrSampler_NoYcbcrSurface, | 
|  | reporter, | 
|  | ctxInfo, | 
|  | CtsEnforcement::kApiLevel_T) { | 
|  | GrDirectContext* dContext = ctxInfo.directContext(); | 
|  |  | 
|  | VkYcbcrSamplerHelper ycbcrHelper(dContext); | 
|  | if (!ycbcrHelper.isYCbCrSupported()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | GrBackendTexture texture = dContext->createBackendTexture( | 
|  | kImageWidth, | 
|  | kImageHeight, | 
|  | GrBackendFormats::MakeVk(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM), | 
|  | skgpu::Mipmapped::kNo, | 
|  | GrRenderable::kNo, | 
|  | GrProtected::kNo); | 
|  | if (texture.isValid()) { | 
|  | ERRORF(reporter, | 
|  | "GrDirectContext::createBackendTexture() didn't fail as expected for Ycbcr format."); | 
|  | } | 
|  | } | 
|  |  | 
|  | #endif  // defined(SK_GANESH) && defined(SK_VULKAN) |