1 /* 2 * Copyright 2017 Google Inc. 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 "Test.h" 9 10 #if SK_SUPPORT_GPU 11 12 #include "GrSurfaceProxy.h" 13 #include "GrTextureProducer.h" 14 #include "GrTextureProxy.h" 15 16 // For DetermineDomainMode (in the MDB world) we have 4 rects: 17 // 1) the final instantiated backing storage (i.e., the actual GrTexture's extent) 18 // 2) the proxy's extent, which may or may not match the GrTexture's extent 19 // 3) the content rect, which can be a subset of the proxy's extent or null 20 // 4) the constraint rect, which can optionally be hard or soft 21 // This test "fuzzes" all the combinations of these rects. 22 class GrTextureProducer_TestAccess { 23 public: 24 using DomainMode = GrTextureProducer::DomainMode; 25 26 static DomainMode DetermineDomainMode( 27 const SkRect& constraintRect, 28 GrTextureProducer::FilterConstraint filterConstraint, 29 bool coordsLimitedToConstraintRect, 30 GrTextureProxy* proxy, 31 const SkIRect* textureContentArea, 32 const GrSamplerParams::FilterMode* filterModeOrNullForBicubic, 33 SkRect* domainRect) { 34 return GrTextureProducer::DetermineDomainMode(constraintRect, 35 filterConstraint, 36 coordsLimitedToConstraintRect, 37 proxy, 38 textureContentArea, 39 filterModeOrNullForBicubic, 40 domainRect); 41 } 42 }; 43 44 using DomainMode = GrTextureProducer_TestAccess::DomainMode; 45 46 #ifdef SK_DEBUG 47 static bool is_irect(const SkRect& r) { 48 return SkScalarIsInt(r.fLeft) && SkScalarIsInt(r.fTop) && 49 SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom); 50 } 51 #endif 52 53 static SkIRect to_irect(const SkRect& r) { 54 SkASSERT(is_irect(r)); 55 return SkIRect::MakeLTRB(SkScalarRoundToInt(r.fLeft), 56 SkScalarRoundToInt(r.fTop), 57 SkScalarRoundToInt(r.fRight), 58 SkScalarRoundToInt(r.fBottom)); 59 } 60 61 62 class RectInfo { 63 public: 64 enum Side { kLeft = 0, kTop = 1, kRight = 2, kBot = 3 }; 65 66 enum EdgeType { 67 kSoft = 0, // there is data on the other side of this edge that we are allowed to sample 68 kHard = 1, // the backing resource ends at this edge 69 kBad = 2 // we can't sample across this edge 70 }; 71 72 void set(const SkRect& rect, EdgeType left, EdgeType top, EdgeType right, EdgeType bot, 73 const char* name) { 74 fRect = rect; 75 fTypes[kLeft] = left; 76 fTypes[kTop] = top; 77 fTypes[kRight] = right; 78 fTypes[kBot] = bot; 79 fName = name; 80 } 81 82 const SkRect& rect() const { return fRect; } 83 EdgeType edgeType(Side side) const { return fTypes[side]; } 84 const char* name() const { return fName; } 85 86 #ifdef SK_DEBUG 87 bool isHardOrBadAllAround() const { 88 for (int i = 0; i < 4; ++i) { 89 if (kHard != fTypes[i] && kBad != fTypes[i]) { 90 return false; 91 } 92 } 93 return true; 94 } 95 #endif 96 97 bool hasABad() const { 98 for (int i = 0; i < 4; ++i) { 99 if (kBad == fTypes[i]) { 100 return true; 101 } 102 } 103 return false; 104 } 105 106 #ifdef SK_DEBUG 107 void print(const char* label) const { 108 SkDebugf("%s: %s (%.1f, %.1f, %.1f, %.1f), L: %s T: %s R: %s B: %s\n", 109 label, fName, 110 fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, 111 ToStr(fTypes[kLeft]), ToStr(fTypes[kTop]), 112 ToStr(fTypes[kRight]), ToStr(fTypes[kBot])); 113 } 114 #endif 115 116 private: 117 #ifdef SK_DEBUG 118 static const char* ToStr(EdgeType type) { 119 static const char* names[] = { "soft", "hard", "bad" }; 120 return names[type]; 121 } 122 #endif 123 124 RectInfo operator=(const RectInfo& other); // disallow 125 126 SkRect fRect; 127 EdgeType fTypes[4]; 128 const char* fName; 129 130 }; 131 132 static sk_sp<GrTextureProxy> create_proxy(GrResourceProvider* resourceProvider, 133 bool isPowerOfTwo, 134 bool isExact, 135 RectInfo* rect) { 136 int size = isPowerOfTwo ? 128 : 100; 137 SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox; 138 139 GrSurfaceDesc desc; 140 desc.fConfig = kRGBA_8888_GrPixelConfig; 141 desc.fWidth = size; 142 desc.fHeight = size; 143 144 static const char* name = "proxy"; 145 146 // Proxies are always hard on the left and top but can be bad on the right and bottom 147 rect->set(SkRect::MakeWH(size, size), 148 RectInfo::kHard, 149 RectInfo::kHard, 150 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad, 151 (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad, 152 name); 153 154 sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(resourceProvider, 155 desc, fit, 156 SkBudgeted::kYes); 157 return proxy; 158 } 159 160 static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous, 161 bool isInsetHard, bool coordsAreLimitedToRect, 162 float insetAmount, float halfFilterWidth) { 163 if (isInsetHard) { 164 if (coordsAreLimitedToRect) { 165 SkASSERT(halfFilterWidth >= 0.0f); 166 if (0.0f == halfFilterWidth) { 167 return RectInfo::kSoft; 168 } 169 } 170 171 if (0.0f == insetAmount && RectInfo::kHard == previous) { 172 return RectInfo::kHard; 173 } 174 175 return RectInfo::kBad; 176 } 177 178 if (RectInfo::kHard == previous) { 179 return RectInfo::kHard; 180 } 181 182 if (coordsAreLimitedToRect) { 183 SkASSERT(halfFilterWidth >= 0.0f); 184 if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) { 185 return RectInfo::kSoft; 186 } 187 } 188 189 return previous; 190 } 191 192 static const int kInsetLeft_Flag = 0x1; 193 static const int kInsetTop_Flag = 0x2; 194 static const int kInsetRight_Flag = 0x4; 195 static const int kInsetBot_Flag = 0x8; 196 197 // If 'isInsetHard' is true we can't sample across the inset boundary. 198 // If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset. 199 static const SkRect* generic_inset(const RectInfo& enclosing, 200 RectInfo* result, 201 bool isInsetHard, 202 bool areCoordsLimitedToRect, 203 float insetAmount, 204 float halfFilterWidth, 205 uint32_t flags, 206 const char* name) { 207 SkRect newR = enclosing.rect(); 208 209 RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft); 210 if (flags & kInsetLeft_Flag) { 211 newR.fLeft += insetAmount; 212 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect, 213 insetAmount, halfFilterWidth); 214 } else { 215 left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect, 216 0.0f, halfFilterWidth); 217 } 218 219 RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop); 220 if (flags & kInsetTop_Flag) { 221 newR.fTop += insetAmount; 222 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect, 223 insetAmount, halfFilterWidth); 224 } else { 225 top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect, 226 0.0f, halfFilterWidth); 227 } 228 229 RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight); 230 if (flags & kInsetRight_Flag) { 231 newR.fRight -= insetAmount; 232 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect, 233 insetAmount, halfFilterWidth); 234 } else { 235 right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect, 236 0.0f, halfFilterWidth); 237 } 238 239 RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot); 240 if (flags & kInsetBot_Flag) { 241 newR.fBottom -= insetAmount; 242 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect, 243 insetAmount, halfFilterWidth); 244 } else { 245 bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect, 246 0.0f, halfFilterWidth); 247 } 248 249 result->set(newR, left, top, right, bot, name); 250 return &result->rect(); 251 } 252 253 // Make a rect that only touches the enclosing rect on the left. 254 static const SkRect* left_only(const RectInfo& enclosing, 255 RectInfo* result, 256 bool isInsetHard, 257 bool areCoordsLimitedToRect, 258 float insetAmount, 259 float halfFilterWidth) { 260 static const char* name = "left"; 261 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, 262 insetAmount, halfFilterWidth, 263 kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name); 264 } 265 266 // Make a rect that only touches the enclosing rect on the top. 267 static const SkRect* top_only(const RectInfo& enclosing, 268 RectInfo* result, 269 bool isInsetHard, 270 bool areCoordsLimitedToRect, 271 float insetAmount, 272 float halfFilterWidth) { 273 static const char* name = "top"; 274 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, 275 insetAmount, halfFilterWidth, 276 kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name); 277 } 278 279 // Make a rect that only touches the enclosing rect on the right. 280 static const SkRect* right_only(const RectInfo& enclosing, 281 RectInfo* result, 282 bool isInsetHard, 283 bool areCoordsLimitedToRect, 284 float insetAmount, 285 float halfFilterWidth) { 286 static const char* name = "right"; 287 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, 288 insetAmount, halfFilterWidth, 289 kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name); 290 } 291 292 // Make a rect that only touches the enclosing rect on the bottom. 293 static const SkRect* bot_only(const RectInfo& enclosing, 294 RectInfo* result, 295 bool isInsetHard, 296 bool areCoordsLimitedToRect, 297 float insetAmount, 298 float halfFilterWidth) { 299 static const char* name = "bot"; 300 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, 301 insetAmount, halfFilterWidth, 302 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name); 303 } 304 305 // Make a rect that is inset all around. 306 static const SkRect* full_inset(const RectInfo& enclosing, 307 RectInfo* result, 308 bool isInsetHard, 309 bool areCoordsLimitedToRect, 310 float insetAmount, 311 float halfFilterWidth) { 312 static const char* name = "all"; 313 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, 314 insetAmount, halfFilterWidth, 315 kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name); 316 } 317 318 // This is only used for content rect creation. We ensure 'result' is correct but 319 // return null to indicate no content area (other than what the proxy specifies). 320 static const SkRect* null_rect(const RectInfo& enclosing, 321 RectInfo* result, 322 bool isInsetHard, 323 bool areCoordsLimitedToRect, 324 float insetAmount, 325 float halfFilterWidth) { 326 static const char* name = "null"; 327 generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, 328 insetAmount, halfFilterWidth, 0, name); 329 return nullptr; 330 } 331 332 // Make a rect with no inset. This is only used for constraint rect creation. 333 static const SkRect* no_inset(const RectInfo& enclosing, 334 RectInfo* result, 335 bool isInsetHard, 336 bool areCoordsLimitedToRect, 337 float insetAmount, 338 float halfFilterWidth) { 339 static const char* name = "none"; 340 return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect, 341 insetAmount, halfFilterWidth, 0, name); 342 } 343 344 static void proxy_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider) { 345 GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode; 346 SkRect actualDomainRect; 347 348 static const GrSamplerParams::FilterMode gModes[] = { 349 GrSamplerParams::kNone_FilterMode, 350 GrSamplerParams::kBilerp_FilterMode, 351 GrSamplerParams::kMipMap_FilterMode, 352 }; 353 354 static const GrSamplerParams::FilterMode* gModePtrs[] = { 355 &gModes[0], &gModes[1], nullptr, &gModes[2] 356 }; 357 358 static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f }; 359 360 for (auto isPowerOfTwoSized : { true, false }) { 361 for (auto isExact : { true, false }) { 362 RectInfo outermost; 363 364 sk_sp<GrTextureProxy> proxy = create_proxy(resourceProvider, isPowerOfTwoSized, 365 isExact, &outermost); 366 SkASSERT(outermost.isHardOrBadAllAround()); 367 368 for (auto contentRectMaker : { left_only, top_only, right_only, 369 bot_only, full_inset, null_rect}) { 370 RectInfo contentRectStorage; 371 const SkRect* contentRect = (*contentRectMaker)(outermost, 372 &contentRectStorage, 373 true, false, 5.0f, -1.0f); 374 if (contentRect) { 375 // We only have content rects if they actually reduce the extent of the content 376 SkASSERT(!contentRect->contains(outermost.rect())); 377 SkASSERT(outermost.rect().contains(*contentRect)); 378 SkASSERT(is_irect(*contentRect)); 379 } 380 SkASSERT(contentRectStorage.isHardOrBadAllAround()); 381 382 for (auto isConstraintRectHard : { true, false }) { 383 for (auto areCoordsLimitedToConstraintRect : { true, false }) { 384 for (int filterMode = 0; filterMode < 4; ++filterMode) { 385 for (auto constraintRectMaker : { left_only, top_only, right_only, 386 bot_only, full_inset, no_inset }) { 387 for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) { 388 RectInfo constraintRectStorage; 389 const SkRect* constraintRect = (*constraintRectMaker)( 390 contentRect ? contentRectStorage : outermost, 391 &constraintRectStorage, 392 isConstraintRectHard, 393 areCoordsLimitedToConstraintRect, 394 insetAmt, 395 gHalfFilterWidth[filterMode]); 396 SkASSERT(constraintRect); // always need one of these 397 if (contentRect) { 398 SkASSERT(contentRect->contains(*constraintRect)); 399 } else { 400 SkASSERT(outermost.rect().contains(*constraintRect)); 401 } 402 403 SkIRect contentIRect; 404 if (contentRect) { 405 contentIRect = to_irect(*contentRect); 406 } 407 408 actualMode = GrTextureProducer_TestAccess::DetermineDomainMode( 409 *constraintRect, 410 isConstraintRectHard 411 ? GrTextureProducer::kYes_FilterConstraint 412 : GrTextureProducer::kNo_FilterConstraint, 413 areCoordsLimitedToConstraintRect, 414 proxy.get(), 415 contentRect ? &contentIRect : nullptr, 416 gModePtrs[filterMode], 417 &actualDomainRect); 418 419 expectedMode = DomainMode::kNoDomain_DomainMode; 420 if (constraintRectStorage.hasABad()) { 421 if (3 == filterMode) { 422 expectedMode = DomainMode::kTightCopy_DomainMode; 423 } else { 424 expectedMode = DomainMode::kDomain_DomainMode; 425 } 426 } 427 428 REPORTER_ASSERT(reporter, expectedMode == actualMode); 429 // TODO: add a check that the returned domain rect is correct 430 } 431 } 432 } 433 } 434 } 435 } 436 } 437 } 438 } 439 440 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) { 441 GrContext* context = ctxInfo.grContext(); 442 443 proxy_test(reporter, context->resourceProvider()); 444 } 445 446 #endif 447