Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2022 Google LLC. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "include/core/SkAlphaType.h" |
Michael Ludwig | fbd683d | 2023-01-03 10:34:29 -0500 | [diff] [blame] | 9 | #include "include/core/SkCanvas.h" |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 10 | #include "include/core/SkColorSpace.h" |
| 11 | #include "include/core/SkColorType.h" |
| 12 | #include "include/core/SkPixmap.h" |
| 13 | #include "include/core/SkSurface.h" |
| 14 | #include "include/effects/SkGradientShader.h" |
| 15 | #include "include/gpu/GpuTypes.h" |
| 16 | #include "include/gpu/graphite/BackendTexture.h" |
| 17 | #include "include/gpu/graphite/Context.h" |
Kevin Lubick | f95da88 | 2023-05-12 11:16:12 -0400 | [diff] [blame] | 18 | #include "include/gpu/graphite/Image.h" |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 19 | #include "include/gpu/graphite/Recorder.h" |
| 20 | #include "include/gpu/graphite/Recording.h" |
Kevin Lubick | 5c93acf | 2023-05-09 12:11:43 -0400 | [diff] [blame] | 21 | #include "include/gpu/graphite/Surface.h" |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 22 | #include "include/gpu/graphite/TextureInfo.h" |
Kevin Lubick | 406dacd | 2023-03-23 10:15:46 -0400 | [diff] [blame] | 23 | #include "src/base/SkRectMemcpy.h" |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 24 | #include "src/core/SkAutoPixmapStorage.h" |
Brian Osman | 699841c | 2023-01-04 10:32:03 -0500 | [diff] [blame] | 25 | #include "src/core/SkImageInfoPriv.h" |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 26 | #include "src/gpu/graphite/Caps.h" |
| 27 | #include "src/gpu/graphite/ContextPriv.h" |
| 28 | #include "src/gpu/graphite/RecorderPriv.h" |
| 29 | #include "src/gpu/graphite/ResourceTypes.h" |
| 30 | #include "tests/Test.h" |
| 31 | #include "tests/TestUtils.h" |
| 32 | #include "tools/ToolUtils.h" |
Nicolette Prevost | 4731ccd | 2023-09-26 10:24:46 -0400 | [diff] [blame] | 33 | #include "tools/gpu/BackendTextureImageFactory.h" |
| 34 | #include "tools/gpu/ManagedBackendTexture.h" |
Brian Salomon | c759bbf | 2023-12-05 11:11:27 -0500 | [diff] [blame] | 35 | #include "tools/graphite/GraphiteTestContext.h" |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 36 | |
Jim Van Verth | fa62855 | 2023-02-15 10:21:50 -0500 | [diff] [blame] | 37 | using Mipmapped = skgpu::Mipmapped; |
| 38 | |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 39 | static constexpr int min_rgb_channel_bits(SkColorType ct) { |
| 40 | switch (ct) { |
| 41 | case kUnknown_SkColorType: return 0; |
| 42 | case kAlpha_8_SkColorType: return 0; |
| 43 | case kA16_unorm_SkColorType: return 0; |
| 44 | case kA16_float_SkColorType: return 0; |
| 45 | case kRGB_565_SkColorType: return 5; |
| 46 | case kARGB_4444_SkColorType: return 4; |
| 47 | case kR8G8_unorm_SkColorType: return 8; |
| 48 | case kR16G16_unorm_SkColorType: return 16; |
| 49 | case kR16G16_float_SkColorType: return 16; |
| 50 | case kRGBA_8888_SkColorType: return 8; |
| 51 | case kSRGBA_8888_SkColorType: return 8; |
| 52 | case kRGB_888x_SkColorType: return 8; |
| 53 | case kBGRA_8888_SkColorType: return 8; |
| 54 | case kRGBA_1010102_SkColorType: return 10; |
| 55 | case kRGB_101010x_SkColorType: return 10; |
| 56 | case kBGRA_1010102_SkColorType: return 10; |
| 57 | case kBGR_101010x_SkColorType: return 10; |
Aaron Clarke | fe37efc | 2023-02-16 14:40:00 -0800 | [diff] [blame] | 58 | case kBGR_101010x_XR_SkColorType: return 10; |
Brian Osman | 211d63b | 2023-09-01 20:39:58 +0000 | [diff] [blame] | 59 | case kRGBA_10x6_SkColorType: return 10; |
Aaron Clarke | a0bbf72 | 2024-04-08 10:48:15 -0700 | [diff] [blame] | 60 | case kBGRA_10101010_XR_SkColorType: return 10; |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 61 | case kGray_8_SkColorType: return 8; // counting gray as "rgb" |
| 62 | case kRGBA_F16Norm_SkColorType: return 10; // just counting the mantissa |
| 63 | case kRGBA_F16_SkColorType: return 10; // just counting the mantissa |
| 64 | case kRGBA_F32_SkColorType: return 23; // just counting the mantissa |
| 65 | case kR16G16B16A16_unorm_SkColorType: return 16; |
| 66 | case kR8_unorm_SkColorType: return 8; |
| 67 | } |
| 68 | SkUNREACHABLE; |
| 69 | } |
| 70 | |
| 71 | static constexpr int alpha_channel_bits(SkColorType ct) { |
| 72 | switch (ct) { |
| 73 | case kUnknown_SkColorType: return 0; |
| 74 | case kAlpha_8_SkColorType: return 8; |
| 75 | case kA16_unorm_SkColorType: return 16; |
| 76 | case kA16_float_SkColorType: return 16; |
| 77 | case kRGB_565_SkColorType: return 0; |
| 78 | case kARGB_4444_SkColorType: return 4; |
| 79 | case kR8G8_unorm_SkColorType: return 0; |
| 80 | case kR16G16_unorm_SkColorType: return 0; |
| 81 | case kR16G16_float_SkColorType: return 0; |
| 82 | case kRGBA_8888_SkColorType: return 8; |
| 83 | case kSRGBA_8888_SkColorType: return 8; |
| 84 | case kRGB_888x_SkColorType: return 0; |
| 85 | case kBGRA_8888_SkColorType: return 8; |
| 86 | case kRGBA_1010102_SkColorType: return 2; |
| 87 | case kRGB_101010x_SkColorType: return 0; |
| 88 | case kBGRA_1010102_SkColorType: return 2; |
| 89 | case kBGR_101010x_SkColorType: return 0; |
Aaron Clarke | fe37efc | 2023-02-16 14:40:00 -0800 | [diff] [blame] | 90 | case kBGR_101010x_XR_SkColorType: return 0; |
Brian Osman | 211d63b | 2023-09-01 20:39:58 +0000 | [diff] [blame] | 91 | case kRGBA_10x6_SkColorType: return 10; |
Aaron Clarke | a0bbf72 | 2024-04-08 10:48:15 -0700 | [diff] [blame] | 92 | case kBGRA_10101010_XR_SkColorType: return 10; |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 93 | case kGray_8_SkColorType: return 0; |
| 94 | case kRGBA_F16Norm_SkColorType: return 10; // just counting the mantissa |
| 95 | case kRGBA_F16_SkColorType: return 10; // just counting the mantissa |
| 96 | case kRGBA_F32_SkColorType: return 23; // just counting the mantissa |
| 97 | case kR16G16B16A16_unorm_SkColorType: return 16; |
| 98 | case kR8_unorm_SkColorType: return 0; |
| 99 | } |
| 100 | SkUNREACHABLE; |
| 101 | } |
| 102 | |
| 103 | namespace { |
| 104 | std::vector<SkIRect> make_long_rect_array(int w, int h) { |
| 105 | return { |
| 106 | // entire thing |
| 107 | SkIRect::MakeWH(w, h), |
| 108 | // larger on all sides |
| 109 | SkIRect::MakeLTRB(-10, -10, w + 10, h + 10), |
| 110 | // fully contained |
| 111 | SkIRect::MakeLTRB(w/4, h/4, 3*w/4, 3*h/4), |
| 112 | // outside top left |
| 113 | SkIRect::MakeLTRB(-10, -10, -1, -1), |
| 114 | // touching top left corner |
| 115 | SkIRect::MakeLTRB(-10, -10, 0, 0), |
| 116 | // overlapping top left corner |
| 117 | SkIRect::MakeLTRB(-10, -10, w/4, h/4), |
| 118 | // overlapping top left and top right corners |
| 119 | SkIRect::MakeLTRB(-10, -10, w + 10, h/4), |
| 120 | // touching entire top edge |
| 121 | SkIRect::MakeLTRB(-10, -10, w + 10, 0), |
| 122 | // overlapping top right corner |
| 123 | SkIRect::MakeLTRB(3*w/4, -10, w + 10, h/4), |
| 124 | // contained in x, overlapping top edge |
| 125 | SkIRect::MakeLTRB(w/4, -10, 3*w/4, h/4), |
| 126 | // outside top right corner |
| 127 | SkIRect::MakeLTRB(w + 1, -10, w + 10, -1), |
| 128 | // touching top right corner |
| 129 | SkIRect::MakeLTRB(w, -10, w + 10, 0), |
| 130 | // overlapping top left and bottom left corners |
| 131 | SkIRect::MakeLTRB(-10, -10, w/4, h + 10), |
| 132 | // touching entire left edge |
| 133 | SkIRect::MakeLTRB(-10, -10, 0, h + 10), |
| 134 | // overlapping bottom left corner |
| 135 | SkIRect::MakeLTRB(-10, 3*h/4, w/4, h + 10), |
| 136 | // contained in y, overlapping left edge |
| 137 | SkIRect::MakeLTRB(-10, h/4, w/4, 3*h/4), |
| 138 | // outside bottom left corner |
| 139 | SkIRect::MakeLTRB(-10, h + 1, -1, h + 10), |
| 140 | // touching bottom left corner |
| 141 | SkIRect::MakeLTRB(-10, h, 0, h + 10), |
| 142 | // overlapping bottom left and bottom right corners |
| 143 | SkIRect::MakeLTRB(-10, 3*h/4, w + 10, h + 10), |
| 144 | // touching entire left edge |
| 145 | SkIRect::MakeLTRB(0, h, w, h + 10), |
| 146 | // overlapping bottom right corner |
| 147 | SkIRect::MakeLTRB(3*w/4, 3*h/4, w + 10, h + 10), |
| 148 | // overlapping top right and bottom right corners |
| 149 | SkIRect::MakeLTRB(3*w/4, -10, w + 10, h + 10), |
| 150 | }; |
| 151 | } |
| 152 | |
| 153 | std::vector<SkIRect> make_short_rect_array(int w, int h) { |
| 154 | return { |
| 155 | // entire thing |
| 156 | SkIRect::MakeWH(w, h), |
| 157 | // fully contained |
| 158 | SkIRect::MakeLTRB(w/4, h/4, 3*w/4, 3*h/4), |
| 159 | // overlapping top right corner |
| 160 | SkIRect::MakeLTRB(3*w/4, -10, w + 10, h/4), |
| 161 | }; |
| 162 | } |
| 163 | |
| 164 | struct GraphiteReadPixelTestRules { |
| 165 | // Test unpremul sources? We could omit this and detect that creating the source of the read |
| 166 | // failed but having it lets us skip generating reference color data. |
| 167 | bool fAllowUnpremulSrc = true; |
| 168 | // Are reads that are overlapping but not contained by the src bounds expected to succeed? |
| 169 | bool fUncontainedRectSucceeds = true; |
| 170 | }; |
| 171 | |
| 172 | // Makes a src populated with the pixmap. The src should get its image info (or equivalent) from |
| 173 | // the pixmap. |
Robert Phillips | 5fa5d59 | 2023-03-23 15:08:28 -0400 | [diff] [blame] | 174 | template <typename T> using GraphiteSrcFactory = T(skgpu::graphite::Recorder*, SkPixmap&); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 175 | |
| 176 | enum class Result { |
| 177 | kFail, |
| 178 | kSuccess, |
| 179 | kExcusedFailure, |
| 180 | }; |
| 181 | |
| 182 | // Does a read from the T into the pixmap. |
| 183 | template <typename T> |
| 184 | using GraphiteReadSrcFn = Result(const T&, const SkIPoint& offset, const SkPixmap&); |
| 185 | |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 186 | static SkAutoPixmapStorage make_ref_data(const SkImageInfo& info, bool forceOpaque) { |
| 187 | SkAutoPixmapStorage result; |
Jim Van Verth | bbbbfb1 | 2023-08-18 12:12:30 -0400 | [diff] [blame] | 188 | if (info.alphaType() == kUnknown_SkAlphaType) { |
| 189 | result.alloc(info.makeAlphaType(kUnpremul_SkAlphaType)); |
| 190 | } else { |
| 191 | result.alloc(info); |
| 192 | } |
| 193 | auto surface = SkSurfaces::WrapPixels(result); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 194 | if (!surface) { |
| 195 | return result; |
| 196 | } |
| 197 | |
| 198 | SkPoint pts1[] = {{0, 0}, {float(info.width()), float(info.height())}}; |
| 199 | static constexpr SkColor kColors1[] = {SK_ColorGREEN, SK_ColorRED}; |
| 200 | SkPaint paint; |
| 201 | paint.setShader(SkGradientShader::MakeLinear(pts1, kColors1, nullptr, 2, SkTileMode::kClamp)); |
| 202 | surface->getCanvas()->drawPaint(paint); |
| 203 | |
| 204 | SkPoint pts2[] = {{float(info.width()), 0}, {0, float(info.height())}}; |
| 205 | static constexpr SkColor kColors2[] = {SK_ColorBLUE, SK_ColorBLACK}; |
| 206 | paint.setShader(SkGradientShader::MakeLinear(pts2, kColors2, nullptr, 2, SkTileMode::kClamp)); |
| 207 | paint.setBlendMode(SkBlendMode::kPlus); |
| 208 | surface->getCanvas()->drawPaint(paint); |
| 209 | |
| 210 | // If not opaque add some fractional alpha. |
| 211 | if (info.alphaType() != kOpaque_SkAlphaType && !forceOpaque) { |
| 212 | static constexpr SkColor kColors3[] = {SK_ColorWHITE, |
| 213 | SK_ColorWHITE, |
| 214 | 0x60FFFFFF, |
| 215 | SK_ColorWHITE, |
| 216 | SK_ColorWHITE}; |
| 217 | static constexpr SkScalar kPos3[] = {0.f, 0.15f, 0.5f, 0.85f, 1.f}; |
| 218 | paint.setShader(SkGradientShader::MakeRadial({info.width()/2.f, info.height()/2.f}, |
| 219 | (info.width() + info.height())/10.f, |
| 220 | kColors3, kPos3, 5, SkTileMode::kMirror)); |
| 221 | paint.setBlendMode(SkBlendMode::kDstIn); |
| 222 | surface->getCanvas()->drawPaint(paint); |
| 223 | } |
| 224 | return result; |
| 225 | }; |
| 226 | } // anonymous namespace |
| 227 | |
| 228 | template <typename T> |
| 229 | static void graphite_read_pixels_test_driver(skiatest::Reporter* reporter, |
Robert Phillips | 5fa5d59 | 2023-03-23 15:08:28 -0400 | [diff] [blame] | 230 | skgpu::graphite::Context* context, |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 231 | const GraphiteReadPixelTestRules& rules, |
| 232 | const std::function<GraphiteSrcFactory<T>>& srcFactory, |
| 233 | const std::function<GraphiteReadSrcFn<T>>& read, |
| 234 | SkString label) { |
| 235 | if (!label.isEmpty()) { |
| 236 | // Add space for printing. |
| 237 | label.append(" "); |
| 238 | } |
| 239 | // Separate this out just to give it some line width to breathe. Note 'srcPixels' should have |
| 240 | // the same image info as src. We will do a converting readPixels() on it to get the data |
| 241 | // to compare with the results of 'read'. |
| 242 | auto runTest = [&](const T& src, |
| 243 | const SkPixmap& srcPixels, |
| 244 | const SkImageInfo& readInfo, |
| 245 | SkIPoint offset) { |
| 246 | const bool csConversion = |
| 247 | !SkColorSpace::Equals(readInfo.colorSpace(), srcPixels.info().colorSpace()); |
| 248 | const auto readCT = readInfo.colorType(); |
| 249 | const auto readAT = readInfo.alphaType(); |
| 250 | const auto srcCT = srcPixels.info().colorType(); |
| 251 | const auto srcAT = srcPixels.info().alphaType(); |
| 252 | const auto rect = SkIRect::MakeWH(readInfo.width(), readInfo.height()).makeOffset(offset); |
| 253 | const auto surfBounds = SkIRect::MakeWH(srcPixels.width(), srcPixels.height()); |
| 254 | const size_t readBpp = SkColorTypeBytesPerPixel(readCT); |
| 255 | |
| 256 | // Make the row bytes in the dst be loose for extra stress. |
| 257 | const size_t dstRB = readBpp * readInfo.width() + 10 * readBpp; |
| 258 | // This will make the last row tight. |
| 259 | const size_t dstSize = readInfo.computeByteSize(dstRB); |
| 260 | std::unique_ptr<char[]> dstData(new char[dstSize]); |
| 261 | SkPixmap dstPixels(readInfo, dstData.get(), dstRB); |
| 262 | // Initialize with an arbitrary value for each byte. Later we will check that only the |
| 263 | // correct part of the destination gets overwritten by 'read'. |
| 264 | static constexpr auto kInitialByte = static_cast<char>(0x1B); |
| 265 | std::fill_n(static_cast<char*>(dstPixels.writable_addr()), |
| 266 | dstPixels.computeByteSize(), |
| 267 | kInitialByte); |
| 268 | |
| 269 | const Result result = read(src, offset, dstPixels); |
| 270 | |
| 271 | if (!SkIRect::Intersects(rect, surfBounds)) { |
| 272 | REPORTER_ASSERT(reporter, result != Result::kSuccess); |
| 273 | } else if (readCT == kUnknown_SkColorType) { |
| 274 | REPORTER_ASSERT(reporter, result != Result::kSuccess); |
Jim Van Verth | 1272b52 | 2022-11-02 10:57:43 -0400 | [diff] [blame] | 275 | } else if (readAT == kUnknown_SkAlphaType) { |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 276 | REPORTER_ASSERT(reporter, result != Result::kSuccess); |
| 277 | } else if (!rules.fUncontainedRectSucceeds && !surfBounds.contains(rect)) { |
| 278 | REPORTER_ASSERT(reporter, result != Result::kSuccess); |
| 279 | } else if (result == Result::kFail) { |
| 280 | // TODO: Support RGB/BGR 101010x, BGRA 1010102 on the GPU. |
Jim Van Verth | d853295b | 2022-11-01 15:48:15 -0400 | [diff] [blame] | 281 | ERRORF(reporter, |
| 282 | "Read failed. %sSrc CT: %s, Src AT: %s Read CT: %s, Read AT: %s, " |
| 283 | "Rect [%d, %d, %d, %d], CS conversion: %d\n", |
| 284 | label.c_str(), |
| 285 | ToolUtils::colortype_name(srcCT), ToolUtils::alphatype_name(srcAT), |
| 286 | ToolUtils::colortype_name(readCT), ToolUtils::alphatype_name(readAT), |
| 287 | rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, csConversion); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 288 | return result; |
| 289 | } |
| 290 | |
| 291 | bool guardOk = true; |
| 292 | auto guardCheck = [](char x) { return x == kInitialByte; }; |
| 293 | |
| 294 | // Considering the rect we tried to read and the surface bounds figure out which pixels in |
| 295 | // both src and dst space should actually have been read and written. |
| 296 | SkIRect srcReadRect; |
| 297 | if (result == Result::kSuccess && srcReadRect.intersect(surfBounds, rect)) { |
| 298 | SkIRect dstWriteRect = srcReadRect.makeOffset(-rect.fLeft, -rect.fTop); |
| 299 | |
| 300 | const bool lumConversion = |
| 301 | !(SkColorTypeChannelFlags(srcCT) & kGray_SkColorChannelFlag) && |
| 302 | (SkColorTypeChannelFlags(readCT) & kGray_SkColorChannelFlag); |
| 303 | // A CS or luminance conversion allows a 3 value difference and otherwise a 2 value |
| 304 | // difference. Note that sometimes read back on GPU can be lossy even when there no |
| 305 | // conversion at all because GPU->CPU read may go to a lower bit depth format and then |
| 306 | // be promoted back to the original type. For example, GL ES cannot read to 1010102, so |
| 307 | // we go through 8888. |
| 308 | float numer = (lumConversion || csConversion) ? 3.f : 2.f; |
| 309 | // Allow some extra tolerance if unpremuling. |
| 310 | if (srcAT == kPremul_SkAlphaType && readAT == kUnpremul_SkAlphaType) { |
| 311 | numer += 1; |
| 312 | } |
| 313 | int rgbBits = std::min({min_rgb_channel_bits(readCT), min_rgb_channel_bits(srcCT), 8}); |
| 314 | float tol = numer / (1 << rgbBits); |
| 315 | float alphaTol = 0; |
| 316 | if (readAT != kOpaque_SkAlphaType && srcAT != kOpaque_SkAlphaType) { |
| 317 | // Alpha can also get squashed down to 8 bits going through an intermediate |
| 318 | // color format. |
| 319 | const int alphaBits = std::min({alpha_channel_bits(readCT), |
| 320 | alpha_channel_bits(srcCT), |
| 321 | 8}); |
| 322 | alphaTol = 2.f / (1 << alphaBits); |
| 323 | } |
| 324 | |
| 325 | const float tols[4] = {tol, tol, tol, alphaTol}; |
| 326 | auto error = std::function<ComparePixmapsErrorReporter>([&](int x, int y, |
| 327 | const float diffs[4]) { |
| 328 | SkASSERT(x >= 0 && y >= 0); |
| 329 | ERRORF(reporter, |
| 330 | "%sSrc CT: %s, Src AT: %s, Read CT: %s, Read AT: %s, Rect [%d, %d, %d, %d]" |
| 331 | ", CS conversion: %d\n" |
| 332 | "Error at %d, %d. Diff in floats: (%f, %f, %f, %f)", |
| 333 | label.c_str(), |
| 334 | ToolUtils::colortype_name(srcCT), ToolUtils::alphatype_name(srcAT), |
| 335 | ToolUtils::colortype_name(readCT), ToolUtils::alphatype_name(readAT), |
| 336 | rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, csConversion, x, y, |
| 337 | diffs[0], diffs[1], diffs[2], diffs[3]); |
| 338 | }); |
| 339 | SkAutoPixmapStorage ref; |
| 340 | SkImageInfo refInfo = readInfo.makeDimensions(dstWriteRect.size()); |
| 341 | ref.alloc(refInfo); |
| 342 | if (readAT == kUnknown_SkAlphaType) { |
| 343 | // Do a spoofed read where src and dst alpha type are both kUnpremul. This will |
| 344 | // allow SkPixmap readPixels to succeed and won't do any alpha type conversion. |
| 345 | SkPixmap unpremulRef(refInfo.makeAlphaType(kUnpremul_SkAlphaType), |
| 346 | ref.addr(), |
| 347 | ref.rowBytes()); |
Jim Van Verth | d853295b | 2022-11-01 15:48:15 -0400 | [diff] [blame] | 348 | SkPixmap unpremulSrc(srcPixels.info().makeAlphaType(kUnpremul_SkAlphaType), |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 349 | srcPixels.addr(), |
| 350 | srcPixels.rowBytes()); |
| 351 | |
Jim Van Verth | d853295b | 2022-11-01 15:48:15 -0400 | [diff] [blame] | 352 | unpremulSrc.readPixels(unpremulRef, srcReadRect.x(), srcReadRect.y()); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 353 | } else { |
| 354 | srcPixels.readPixels(ref, srcReadRect.x(), srcReadRect.y()); |
| 355 | } |
| 356 | // This is the part of dstPixels that should have been updated. |
| 357 | SkPixmap actual; |
| 358 | SkAssertResult(dstPixels.extractSubset(&actual, dstWriteRect)); |
| 359 | ComparePixels(ref, actual, tols, error); |
| 360 | |
| 361 | const auto* v = dstData.get(); |
| 362 | const auto* end = dstData.get() + dstSize; |
| 363 | guardOk = std::all_of(v, v + dstWriteRect.top() * dstPixels.rowBytes(), guardCheck); |
| 364 | v += dstWriteRect.top() * dstPixels.rowBytes(); |
| 365 | for (int y = dstWriteRect.top(); y < dstWriteRect.bottom(); ++y) { |
| 366 | guardOk |= std::all_of(v, v + dstWriteRect.left() * readBpp, guardCheck); |
| 367 | auto pad = v + dstWriteRect.right() * readBpp; |
| 368 | auto rowEnd = std::min(end, v + dstPixels.rowBytes()); |
| 369 | // min protects against reading past the end of the tight last row. |
| 370 | guardOk |= std::all_of(pad, rowEnd, guardCheck); |
| 371 | v = rowEnd; |
| 372 | } |
| 373 | guardOk |= std::all_of(v, end, guardCheck); |
| 374 | } else { |
| 375 | guardOk = std::all_of(dstData.get(), dstData.get() + dstSize, guardCheck); |
| 376 | } |
| 377 | if (!guardOk) { |
| 378 | ERRORF(reporter, |
| 379 | "Result pixels modified result outside read rect [%d, %d, %d, %d]. " |
| 380 | "%sSrc CT: %s, Read CT: %s, CS conversion: %d", |
| 381 | rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, label.c_str(), |
| 382 | ToolUtils::colortype_name(srcCT), ToolUtils::colortype_name(readCT), |
| 383 | csConversion); |
| 384 | } |
| 385 | return result; |
| 386 | }; |
| 387 | |
| 388 | static constexpr int kW = 16; |
| 389 | static constexpr int kH = 16; |
| 390 | |
| 391 | const std::vector<SkIRect> longRectArray = make_long_rect_array(kW, kH); |
| 392 | const std::vector<SkIRect> shortRectArray = make_short_rect_array(kW, kH); |
| 393 | |
| 394 | // We ensure we use the long array once per src and read color type and otherwise use the |
| 395 | // short array to improve test run time. |
| 396 | // Also, some color types have no alpha values and thus Opaque Premul and Unpremul are |
| 397 | // equivalent. Just ensure each redundant AT is tested once with each CT (src and read). |
| 398 | // Similarly, alpha-only color types behave the same for all alpha types so just test premul |
| 399 | // after one iter. |
| 400 | // We consider a src or read CT thoroughly tested once it has run through the long rect array |
| 401 | // and full complement of alpha types with one successful read in the loop. |
| 402 | std::array<bool, kLastEnum_SkColorType + 1> srcCTTestedThoroughly = {}, |
| 403 | readCTTestedThoroughly = {}; |
Jim Van Verth | d853295b | 2022-11-01 15:48:15 -0400 | [diff] [blame] | 404 | for (int sat = 0; sat <= kLastEnum_SkAlphaType; ++sat) { |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 405 | const auto srcAT = static_cast<SkAlphaType>(sat); |
| 406 | if (srcAT == kUnpremul_SkAlphaType && !rules.fAllowUnpremulSrc) { |
| 407 | continue; |
| 408 | } |
| 409 | for (int sct = 0; sct <= kLastEnum_SkColorType; ++sct) { |
| 410 | const auto srcCT = static_cast<SkColorType>(sct); |
| 411 | // We always make our ref data as F32 |
| 412 | auto refInfo = SkImageInfo::Make(kW, kH, |
| 413 | kRGBA_F32_SkColorType, |
| 414 | srcAT, |
| 415 | SkColorSpace::MakeSRGB()); |
| 416 | // 1010102 formats have an issue where it's easy to make a resulting |
| 417 | // color where r, g, or b is greater than a. CPU/GPU differ in whether the stored color |
| 418 | // channels are clipped to the alpha value. CPU clips but GPU does not. |
| 419 | // Note that we only currently use srcCT for the 1010102 workaround. If we remove this |
| 420 | // we can also put the ref data setup above the srcCT loop. |
| 421 | bool forceOpaque = srcAT == kPremul_SkAlphaType && |
| 422 | (srcCT == kRGBA_1010102_SkColorType || srcCT == kBGRA_1010102_SkColorType); |
| 423 | |
| 424 | SkAutoPixmapStorage refPixels = make_ref_data(refInfo, forceOpaque); |
| 425 | // Convert the ref data to our desired src color type. |
| 426 | const auto srcInfo = SkImageInfo::Make(kW, kH, srcCT, srcAT, SkColorSpace::MakeSRGB()); |
| 427 | SkAutoPixmapStorage srcPixels; |
| 428 | srcPixels.alloc(srcInfo); |
| 429 | { |
| 430 | SkPixmap readPixmap = srcPixels; |
| 431 | // Spoof the alpha type to kUnpremul so the read will succeed without doing any |
| 432 | // conversion (because we made our surface also use kUnpremul). |
| 433 | if (srcAT == kUnknown_SkAlphaType) { |
| 434 | readPixmap.reset(srcPixels.info().makeAlphaType(kUnpremul_SkAlphaType), |
| 435 | srcPixels.addr(), |
| 436 | srcPixels.rowBytes()); |
| 437 | } |
| 438 | refPixels.readPixels(readPixmap, 0, 0); |
| 439 | } |
| 440 | |
Robert Phillips | 5fa5d59 | 2023-03-23 15:08:28 -0400 | [diff] [blame] | 441 | std::unique_ptr<skgpu::graphite::Recorder> recorder = context->makeRecorder(); |
| 442 | |
| 443 | auto src = srcFactory(recorder.get(), srcPixels); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 444 | if (!src) { |
| 445 | continue; |
| 446 | } |
| 447 | if (SkColorTypeIsAlwaysOpaque(srcCT) && srcCTTestedThoroughly[srcCT] && |
| 448 | (kPremul_SkAlphaType == srcAT || kUnpremul_SkAlphaType == srcAT)) { |
| 449 | continue; |
| 450 | } |
| 451 | if (SkColorTypeIsAlphaOnly(srcCT) && srcCTTestedThoroughly[srcCT] && |
| 452 | (kUnpremul_SkAlphaType == srcAT || |
| 453 | kOpaque_SkAlphaType == srcAT || |
| 454 | kUnknown_SkAlphaType == srcAT)) { |
| 455 | continue; |
| 456 | } |
| 457 | for (int rct = 0; rct <= kLastEnum_SkColorType; ++rct) { |
| 458 | const auto readCT = static_cast<SkColorType>(rct); |
Jim Van Verth | d853295b | 2022-11-01 15:48:15 -0400 | [diff] [blame] | 459 | // ComparePixels will end up converting these types to kUnknown |
| 460 | // because there's no corresponding GrColorType, and hence it will fail |
| 461 | if (readCT == kRGB_101010x_SkColorType || |
Aaron Clarke | fe37efc | 2023-02-16 14:40:00 -0800 | [diff] [blame] | 462 | readCT == kBGR_101010x_XR_SkColorType || |
Aaron Clarke | a0bbf72 | 2024-04-08 10:48:15 -0700 | [diff] [blame] | 463 | readCT == kBGRA_10101010_XR_SkColorType || |
Jim Van Verth | d853295b | 2022-11-01 15:48:15 -0400 | [diff] [blame] | 464 | readCT == kBGR_101010x_SkColorType) { |
| 465 | continue; |
| 466 | } |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 467 | for (const sk_sp<SkColorSpace>& readCS : |
| 468 | {SkColorSpace::MakeSRGB(), SkColorSpace::MakeSRGBLinear()}) { |
| 469 | for (int at = 0; at <= kLastEnum_SkAlphaType; ++at) { |
| 470 | const auto readAT = static_cast<SkAlphaType>(at); |
| 471 | if (srcAT != kOpaque_SkAlphaType && readAT == kOpaque_SkAlphaType) { |
| 472 | // This doesn't make sense. |
| 473 | continue; |
| 474 | } |
| 475 | if (SkColorTypeIsAlwaysOpaque(readCT) && readCTTestedThoroughly[readCT] && |
| 476 | (kPremul_SkAlphaType == readAT || kUnpremul_SkAlphaType == readAT)) { |
| 477 | continue; |
| 478 | } |
| 479 | if (SkColorTypeIsAlphaOnly(readCT) && readCTTestedThoroughly[readCT] && |
| 480 | (kUnpremul_SkAlphaType == readAT || |
| 481 | kOpaque_SkAlphaType == readAT || |
| 482 | kUnknown_SkAlphaType == readAT)) { |
| 483 | continue; |
| 484 | } |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 485 | const auto& rects = |
| 486 | srcCTTestedThoroughly[sct] && readCTTestedThoroughly[rct] |
| 487 | ? shortRectArray |
| 488 | : longRectArray; |
| 489 | for (const auto& rect : rects) { |
| 490 | const auto readInfo = SkImageInfo::Make(rect.width(), rect.height(), |
| 491 | readCT, readAT, readCS); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 492 | const SkIPoint offset = rect.topLeft(); |
| 493 | Result r = runTest(src, srcPixels, readInfo, offset); |
| 494 | if (r == Result::kSuccess) { |
| 495 | srcCTTestedThoroughly[sct] = true; |
| 496 | readCTTestedThoroughly[rct] = true; |
| 497 | } |
| 498 | } |
| 499 | } |
| 500 | } |
| 501 | } |
| 502 | } |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | namespace { |
| 507 | struct AsyncContext { |
| 508 | bool fCalled = false; |
| 509 | std::unique_ptr<const SkImage::AsyncReadResult> fResult; |
| 510 | }; |
| 511 | } // anonymous namespace |
| 512 | |
| 513 | // Making this a lambda in the test functions caused: |
| 514 | // "error: cannot compile this forwarded non-trivially copyable parameter yet" |
| 515 | // on x86/Win/Clang bot, referring to 'result'. |
| 516 | static void async_callback(void* c, std::unique_ptr<const SkImage::AsyncReadResult> result) { |
| 517 | auto context = static_cast<AsyncContext*>(c); |
| 518 | context->fResult = std::move(result); |
| 519 | context->fCalled = true; |
| 520 | }; |
| 521 | |
Brian Salomon | c759bbf | 2023-12-05 11:11:27 -0500 | [diff] [blame] | 522 | DEF_CONDITIONAL_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(ImageAsyncReadPixelsGraphite, |
| 523 | reporter, |
| 524 | context, |
| 525 | testContext, |
| 526 | true, |
| 527 | CtsEnforcement::kNextRelease) { |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 528 | using Image = sk_sp<SkImage>; |
Jim Van Verth | d8d3341 | 2023-03-15 14:41:28 -0400 | [diff] [blame] | 529 | using Renderable = skgpu::Renderable; |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 530 | using TextureInfo = skgpu::graphite::TextureInfo; |
| 531 | |
Brian Salomon | c759bbf | 2023-12-05 11:11:27 -0500 | [diff] [blame] | 532 | auto reader = std::function<GraphiteReadSrcFn<Image>>([context, testContext]( |
| 533 | const Image& image, |
| 534 | const SkIPoint& offset, |
| 535 | const SkPixmap& pixels) { |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 536 | AsyncContext asyncContext; |
| 537 | auto rect = SkIRect::MakeSize(pixels.dimensions()).makeOffset(offset); |
| 538 | // The GPU implementation is based on rendering and will fail for non-renderable color |
| 539 | // types. |
| 540 | TextureInfo texInfo = context->priv().caps()->getDefaultSampledTextureInfo( |
| 541 | image->colorType(), |
Jim Van Verth | fa62855 | 2023-02-15 10:21:50 -0500 | [diff] [blame] | 542 | Mipmapped::kNo, |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 543 | skgpu::Protected::kNo, |
| 544 | Renderable::kYes); |
| 545 | if (!context->priv().caps()->isRenderable(texInfo)) { |
| 546 | return Result::kExcusedFailure; |
| 547 | } |
| 548 | |
Jim Van Verth | 6aaef9c | 2023-07-06 10:22:01 -0400 | [diff] [blame] | 549 | context->asyncRescaleAndReadPixels(image.get(), |
| 550 | pixels.info(), |
| 551 | rect, |
| 552 | SkImage::RescaleGamma::kSrc, |
| 553 | SkImage::RescaleMode::kRepeatedLinear, |
| 554 | async_callback, |
| 555 | &asyncContext); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 556 | if (!asyncContext.fCalled) { |
| 557 | context->submit(); |
| 558 | } |
| 559 | while (!asyncContext.fCalled) { |
Brian Salomon | c759bbf | 2023-12-05 11:11:27 -0500 | [diff] [blame] | 560 | testContext->tick(); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 561 | context->checkAsyncWorkCompletion(); |
| 562 | } |
| 563 | if (!asyncContext.fResult) { |
| 564 | return Result::kFail; |
| 565 | } |
| 566 | SkRectMemcpy(pixels.writable_addr(), pixels.rowBytes(), asyncContext.fResult->data(0), |
| 567 | asyncContext.fResult->rowBytes(0), pixels.info().minRowBytes(), |
| 568 | pixels.height()); |
| 569 | return Result::kSuccess; |
| 570 | }); |
| 571 | |
| 572 | GraphiteReadPixelTestRules rules; |
| 573 | rules.fAllowUnpremulSrc = true; |
| 574 | rules.fUncontainedRectSucceeds = false; |
| 575 | |
| 576 | for (auto renderable : {Renderable::kNo, Renderable::kYes}) { |
Robert Phillips | 5fa5d59 | 2023-03-23 15:08:28 -0400 | [diff] [blame] | 577 | auto factory = std::function<GraphiteSrcFactory<Image>>([&]( |
| 578 | skgpu::graphite::Recorder* recorder, |
| 579 | const SkPixmap& src) { |
Nicolette Prevost | 4731ccd | 2023-09-26 10:24:46 -0400 | [diff] [blame] | 580 | Image image = sk_gpu_test::MakeBackendTextureImage(recorder, |
| 581 | src, |
| 582 | Mipmapped::kNo, |
| 583 | renderable, |
| 584 | skgpu::Origin::kTopLeft, |
| 585 | skgpu::Protected::kNo); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 586 | |
| 587 | std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap(); |
| 588 | skgpu::graphite::InsertRecordingInfo recordingInfo; |
| 589 | recordingInfo.fRecording = recording.get(); |
| 590 | context->insertRecording(recordingInfo); |
| 591 | |
| 592 | return image; |
| 593 | }); |
| 594 | auto label = SkStringPrintf("Renderable: %d", (int)renderable); |
Robert Phillips | 5fa5d59 | 2023-03-23 15:08:28 -0400 | [diff] [blame] | 595 | graphite_read_pixels_test_driver(reporter, context, rules, factory, reader, label); |
Jim Van Verth | 1031497 | 2022-10-26 10:23:16 -0400 | [diff] [blame] | 596 | } |
| 597 | |
| 598 | // It's possible that we've created an Image using the factory, but then don't try to do |
| 599 | // readPixels on it, leaving a hanging command buffer. So we submit here to clean up. |
| 600 | context->submit(); |
| 601 | } |
Jim Van Verth | 35ab5b4 | 2022-10-31 12:18:15 -0400 | [diff] [blame] | 602 | |
Brian Salomon | c759bbf | 2023-12-05 11:11:27 -0500 | [diff] [blame] | 603 | DEF_CONDITIONAL_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(SurfaceAsyncReadPixelsGraphite, |
| 604 | reporter, |
| 605 | context, |
| 606 | testContext, |
| 607 | true, |
| 608 | CtsEnforcement::kNextRelease) { |
Jim Van Verth | 35ab5b4 | 2022-10-31 12:18:15 -0400 | [diff] [blame] | 609 | using Surface = sk_sp<SkSurface>; |
| 610 | |
Brian Salomon | c759bbf | 2023-12-05 11:11:27 -0500 | [diff] [blame] | 611 | auto reader = std::function<GraphiteReadSrcFn<Surface>>([context, testContext]( |
| 612 | const Surface& surface, |
| 613 | const SkIPoint& offset, |
| 614 | const SkPixmap& pixels) { |
Jim Van Verth | 35ab5b4 | 2022-10-31 12:18:15 -0400 | [diff] [blame] | 615 | AsyncContext asyncContext; |
| 616 | auto rect = SkIRect::MakeSize(pixels.dimensions()).makeOffset(offset); |
| 617 | |
Jim Van Verth | 6aaef9c | 2023-07-06 10:22:01 -0400 | [diff] [blame] | 618 | context->asyncRescaleAndReadPixels(surface.get(), |
| 619 | pixels.info(), |
| 620 | rect, |
| 621 | SkImage::RescaleGamma::kSrc, |
| 622 | SkImage::RescaleMode::kRepeatedLinear, |
| 623 | async_callback, |
| 624 | &asyncContext); |
Jim Van Verth | 35ab5b4 | 2022-10-31 12:18:15 -0400 | [diff] [blame] | 625 | if (!asyncContext.fCalled) { |
| 626 | context->submit(); |
| 627 | } |
| 628 | while (!asyncContext.fCalled) { |
Brian Salomon | c759bbf | 2023-12-05 11:11:27 -0500 | [diff] [blame] | 629 | testContext->tick(); |
Jim Van Verth | 35ab5b4 | 2022-10-31 12:18:15 -0400 | [diff] [blame] | 630 | context->checkAsyncWorkCompletion(); |
| 631 | } |
| 632 | if (!asyncContext.fResult) { |
| 633 | return Result::kFail; |
| 634 | } |
| 635 | SkRectMemcpy(pixels.writable_addr(), pixels.rowBytes(), asyncContext.fResult->data(0), |
| 636 | asyncContext.fResult->rowBytes(0), pixels.info().minRowBytes(), |
| 637 | pixels.height()); |
| 638 | return Result::kSuccess; |
| 639 | }); |
| 640 | |
| 641 | GraphiteReadPixelTestRules rules; |
| 642 | rules.fAllowUnpremulSrc = true; |
| 643 | rules.fUncontainedRectSucceeds = false; |
| 644 | |
Kevin Lubick | 5c93acf | 2023-05-09 12:11:43 -0400 | [diff] [blame] | 645 | auto factory = std::function<GraphiteSrcFactory<Surface>>( |
| 646 | [&](skgpu::graphite::Recorder* recorder, const SkPixmap& src) { |
| 647 | Surface surface = SkSurfaces::RenderTarget(recorder, |
| 648 | src.info(), |
| 649 | Mipmapped::kNo, |
| 650 | /*surfaceProps=*/nullptr); |
| 651 | if (surface) { |
| 652 | surface->writePixels(src, 0, 0); |
Jim Van Verth | 35ab5b4 | 2022-10-31 12:18:15 -0400 | [diff] [blame] | 653 | |
Kevin Lubick | 5c93acf | 2023-05-09 12:11:43 -0400 | [diff] [blame] | 654 | std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap(); |
| 655 | skgpu::graphite::InsertRecordingInfo recordingInfo; |
| 656 | recordingInfo.fRecording = recording.get(); |
| 657 | context->insertRecording(recordingInfo); |
| 658 | } |
Jim Van Verth | 35ab5b4 | 2022-10-31 12:18:15 -0400 | [diff] [blame] | 659 | |
Kevin Lubick | 5c93acf | 2023-05-09 12:11:43 -0400 | [diff] [blame] | 660 | return surface; |
| 661 | }); |
Robert Phillips | 5fa5d59 | 2023-03-23 15:08:28 -0400 | [diff] [blame] | 662 | graphite_read_pixels_test_driver(reporter, context, rules, factory, reader, {}); |
Jim Van Verth | 35ab5b4 | 2022-10-31 12:18:15 -0400 | [diff] [blame] | 663 | |
| 664 | // It's possible that we've created an Image using the factory, but then don't try to do |
| 665 | // readPixels on it, leaving a hanging command buffer. So we submit here to clean up. |
| 666 | context->submit(); |
| 667 | } |