2019-08-12 05:46:33 +00:00
|
|
|
/*
|
|
|
|
* 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 SK_SUPPORT_GPU && defined(SK_VULKAN)
|
|
|
|
|
2020-03-30 19:57:14 +00:00
|
|
|
#include "include/core/SkCanvas.h"
|
2022-03-31 19:07:44 +00:00
|
|
|
#include "include/core/SkColorSpace.h"
|
2019-08-12 05:46:33 +00:00
|
|
|
#include "include/core/SkImage.h"
|
|
|
|
#include "include/core/SkSurface.h"
|
2020-07-17 19:40:13 +00:00
|
|
|
#include "include/gpu/GrDirectContext.h"
|
2019-08-12 05:46:33 +00:00
|
|
|
#include "tests/Test.h"
|
2020-06-11 14:54:43 +00:00
|
|
|
#include "tools/gpu/vk/VkTestHelper.h"
|
2020-06-10 15:04:51 +00:00
|
|
|
#include "tools/gpu/vk/VkYcbcrSamplerHelper.h"
|
2019-08-12 05:46:33 +00:00
|
|
|
|
|
|
|
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_GPUTEST(VkYCbcrSampler_DrawImageWithYcbcrSampler, reporter, options) {
|
2020-06-11 14:54:43 +00:00
|
|
|
VkTestHelper testHelper(false);
|
|
|
|
if (!testHelper.init()) {
|
|
|
|
ERRORF(reporter, "VkTestHelper initialization failed.");
|
2019-08-12 05:46:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-06 19:50:15 +00:00
|
|
|
VkYcbcrSamplerHelper ycbcrHelper(testHelper.directContext());
|
2020-06-11 14:54:43 +00:00
|
|
|
if (!ycbcrHelper.isYCbCrSupported()) {
|
2020-06-10 15:04:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-15 18:26:58 +00:00
|
|
|
if (!ycbcrHelper.createBackendTexture(kImageWidth, kImageHeight)) {
|
|
|
|
ERRORF(reporter, "Failed to create I420 backend texture");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-06 19:50:15 +00:00
|
|
|
sk_sp<SkImage> srcImage = SkImage::MakeFromTexture(testHelper.directContext(),
|
2020-06-15 18:26:58 +00:00
|
|
|
ycbcrHelper.backendTexture(),
|
|
|
|
kTopLeft_GrSurfaceOrigin,
|
|
|
|
kRGB_888x_SkColorType,
|
|
|
|
kPremul_SkAlphaType,
|
|
|
|
nullptr);
|
2019-08-12 05:46:33 +00:00
|
|
|
if (!srcImage) {
|
2020-06-10 15:04:51 +00:00
|
|
|
ERRORF(reporter, "Failed to create I420 image");
|
2019-08-12 05:46:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
|
2020-07-06 19:50:15 +00:00
|
|
|
testHelper.directContext(), SkBudgeted::kNo,
|
2019-08-12 05:46:33 +00:00
|
|
|
SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType, kPremul_SkAlphaType));
|
|
|
|
if (!surface) {
|
|
|
|
ERRORF(reporter, "Failed to create target SkSurface");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
surface->getCanvas()->drawImage(srcImage, 0, 0);
|
2020-05-14 19:45:44 +00:00
|
|
|
surface->flushAndSubmit();
|
2019-08-12 05:46:33 +00:00
|
|
|
|
|
|
|
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) {
|
2020-06-10 15:04:51 +00:00
|
|
|
auto y2 = VkYcbcrSamplerHelper::GetExpectedY(x, y, kImageWidth, kImageHeight);
|
|
|
|
auto [u, v] = VkYcbcrSamplerHelper::GetExpectedUV(x, y, kImageWidth, kImageHeight);
|
|
|
|
|
2019-08-12 05:46:33 +00:00
|
|
|
// createI420Image() initializes the image with VK_SAMPLER_YCBCR_RANGE_ITU_NARROW.
|
2020-06-10 15:04:51 +00:00
|
|
|
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;
|
2019-08-12 05:46:33 +00:00
|
|
|
|
|
|
|
// 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) {
|
2022-02-10 15:06:54 +00:00
|
|
|
ERRORF(reporter, "R should be %d, but is %d at (%zu, %zu)", expectedR, r, x, y);
|
2019-08-12 05:46:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int g = readbackData[(y * kImageWidth + x) * 4 + 1];
|
|
|
|
if (abs(g - expectedG) > kColorTolerance) {
|
2022-02-10 15:06:54 +00:00
|
|
|
ERRORF(reporter, "G should be %d, but is %d at (%zu, %zu)", expectedG, g, x, y);
|
2019-08-12 05:46:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int b = readbackData[(y * kImageWidth + x) * 4 + 2];
|
|
|
|
if (abs(b - expectedB) > kColorTolerance) {
|
2022-02-10 15:06:54 +00:00
|
|
|
ERRORF(reporter, "B should be %d, but is %d at (%zu, %zu)", expectedB, b, x, y);
|
2019-08-12 05:46:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verifies that it's not possible to allocate Ycbcr texture directly.
|
|
|
|
DEF_GPUTEST(VkYCbcrSampler_NoYcbcrSurface, reporter, options) {
|
2020-06-11 14:54:43 +00:00
|
|
|
VkTestHelper testHelper(false);
|
|
|
|
if (!testHelper.init()) {
|
|
|
|
ERRORF(reporter, "VkTestHelper initialization failed.");
|
2020-06-10 15:04:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-06 19:50:15 +00:00
|
|
|
VkYcbcrSamplerHelper ycbcrHelper(testHelper.directContext());
|
2020-06-11 14:54:43 +00:00
|
|
|
if (!ycbcrHelper.isYCbCrSupported()) {
|
2019-08-12 05:46:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-06 19:50:15 +00:00
|
|
|
GrBackendTexture texture = testHelper.directContext()->createBackendTexture(
|
2019-08-12 05:46:33 +00:00
|
|
|
kImageWidth, kImageHeight, GrBackendFormat::MakeVk(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM),
|
2020-07-21 13:27:25 +00:00
|
|
|
GrMipmapped::kNo, GrRenderable::kNo, GrProtected::kNo);
|
2019-08-12 05:46:33 +00:00
|
|
|
if (texture.isValid()) {
|
|
|
|
ERRORF(reporter,
|
2020-07-23 17:54:35 +00:00
|
|
|
"GrDirectContext::createBackendTexture() didn't fail as expected for Ycbcr format.");
|
2019-08-12 05:46:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // SK_SUPPORT_GPU && defined(SK_VULKAN)
|