DetermineDomainModeTest.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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. #include "include/core/SkImageInfo.h"
  8. #include "include/core/SkRect.h"
  9. #include "include/core/SkRefCnt.h"
  10. #include "include/core/SkTypes.h"
  11. #include "include/gpu/GrBackendSurface.h"
  12. #include "include/gpu/GrContext.h"
  13. #include "include/gpu/GrSamplerState.h"
  14. #include "include/gpu/GrTypes.h"
  15. #include "include/private/GrTypesPriv.h"
  16. #include "src/gpu/GrCaps.h"
  17. #include "src/gpu/GrContextPriv.h"
  18. #include "src/gpu/GrProxyProvider.h"
  19. #include "src/gpu/GrTextureProducer.h"
  20. #include "src/gpu/GrTextureProxy.h"
  21. #include "tests/Test.h"
  22. #include "tools/gpu/GrContextFactory.h"
  23. #include <initializer_list>
  24. // For DetermineDomainMode (in the MDB world) we have 3 rects:
  25. // 1) the final instantiated backing storage (i.e., the actual GrTexture's extent)
  26. // 2) the proxy's extent, which may or may not match the GrTexture's extent
  27. // 3) the constraint rect, which can optionally be hard or soft
  28. // This test "fuzzes" all the combinations of these rects.
  29. class GrTextureProducer_TestAccess {
  30. public:
  31. using DomainMode = GrTextureProducer::DomainMode;
  32. static DomainMode DetermineDomainMode(const SkRect& constraintRect,
  33. GrTextureProducer::FilterConstraint filterConstraint,
  34. bool coordsLimitedToConstraintRect,
  35. GrTextureProxy* proxy,
  36. const GrSamplerState::Filter* filterModeOrNullForBicubic,
  37. SkRect* domainRect) {
  38. return GrTextureProducer::DetermineDomainMode(constraintRect,
  39. filterConstraint,
  40. coordsLimitedToConstraintRect,
  41. proxy,
  42. filterModeOrNullForBicubic,
  43. domainRect);
  44. }
  45. };
  46. using DomainMode = GrTextureProducer_TestAccess::DomainMode;
  47. class RectInfo {
  48. public:
  49. enum Side { kLeft = 0, kTop = 1, kRight = 2, kBot = 3 };
  50. enum EdgeType {
  51. kSoft = 0, // there is data on the other side of this edge that we are allowed to sample
  52. kHard = 1, // the backing resource ends at this edge
  53. kBad = 2 // we can't sample across this edge
  54. };
  55. void set(const SkRect& rect, EdgeType left, EdgeType top, EdgeType right, EdgeType bot,
  56. const char* name) {
  57. fRect = rect;
  58. fTypes[kLeft] = left;
  59. fTypes[kTop] = top;
  60. fTypes[kRight] = right;
  61. fTypes[kBot] = bot;
  62. fName = name;
  63. }
  64. const SkRect& rect() const { return fRect; }
  65. EdgeType edgeType(Side side) const { return fTypes[side]; }
  66. const char* name() const { return fName; }
  67. #ifdef SK_DEBUG
  68. bool isHardOrBadAllAround() const {
  69. for (int i = 0; i < 4; ++i) {
  70. if (kHard != fTypes[i] && kBad != fTypes[i]) {
  71. return false;
  72. }
  73. }
  74. return true;
  75. }
  76. #endif
  77. bool hasABad() const {
  78. for (int i = 0; i < 4; ++i) {
  79. if (kBad == fTypes[i]) {
  80. return true;
  81. }
  82. }
  83. return false;
  84. }
  85. #ifdef SK_DEBUG
  86. void print(const char* label) const {
  87. SkDebugf("%s: %s (%.1f, %.1f, %.1f, %.1f), L: %s T: %s R: %s B: %s\n",
  88. label, fName,
  89. fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
  90. ToStr(fTypes[kLeft]), ToStr(fTypes[kTop]),
  91. ToStr(fTypes[kRight]), ToStr(fTypes[kBot]));
  92. }
  93. #endif
  94. private:
  95. #ifdef SK_DEBUG
  96. static const char* ToStr(EdgeType type) {
  97. static const char* names[] = { "soft", "hard", "bad" };
  98. return names[type];
  99. }
  100. #endif
  101. RectInfo operator=(const RectInfo& other); // disallow
  102. SkRect fRect;
  103. EdgeType fTypes[4];
  104. const char* fName;
  105. };
  106. static sk_sp<GrTextureProxy> create_proxy(GrContext* ctx,
  107. bool isPowerOfTwo,
  108. bool isExact,
  109. RectInfo* rect) {
  110. GrProxyProvider* proxyProvider = ctx->priv().proxyProvider();
  111. int size = isPowerOfTwo ? 128 : 100;
  112. SkBackingFit fit = isExact ? SkBackingFit::kExact : SkBackingFit::kApprox;
  113. GrSurfaceDesc desc;
  114. desc.fWidth = size;
  115. desc.fHeight = size;
  116. desc.fConfig = kRGBA_8888_GrPixelConfig;
  117. GrBackendFormat format =
  118. ctx->priv().caps()->getBackendFormatFromColorType(GrColorType::kRGBA_8888);
  119. static const char* name = "proxy";
  120. // Proxies are always hard on the left and top but can be bad on the right and bottom
  121. rect->set(SkRect::MakeWH(size, size),
  122. RectInfo::kHard,
  123. RectInfo::kHard,
  124. (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
  125. (isPowerOfTwo || isExact) ? RectInfo::kHard : RectInfo::kBad,
  126. name);
  127. return proxyProvider->createProxy(format, desc, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin,
  128. fit, SkBudgeted::kYes, GrProtected::kNo);
  129. }
  130. static RectInfo::EdgeType compute_inset_edgetype(RectInfo::EdgeType previous,
  131. bool isInsetHard, bool coordsAreLimitedToRect,
  132. float insetAmount, float halfFilterWidth) {
  133. if (isInsetHard) {
  134. if (coordsAreLimitedToRect) {
  135. SkASSERT(halfFilterWidth >= 0.0f);
  136. if (0.0f == halfFilterWidth) {
  137. return RectInfo::kSoft;
  138. }
  139. }
  140. if (0.0f == insetAmount && RectInfo::kHard == previous) {
  141. return RectInfo::kHard;
  142. }
  143. return RectInfo::kBad;
  144. }
  145. if (RectInfo::kHard == previous) {
  146. return RectInfo::kHard;
  147. }
  148. if (coordsAreLimitedToRect) {
  149. SkASSERT(halfFilterWidth >= 0.0f);
  150. if (0.0 == halfFilterWidth || insetAmount > halfFilterWidth) {
  151. return RectInfo::kSoft;
  152. }
  153. }
  154. return previous;
  155. }
  156. static const int kInsetLeft_Flag = 0x1;
  157. static const int kInsetTop_Flag = 0x2;
  158. static const int kInsetRight_Flag = 0x4;
  159. static const int kInsetBot_Flag = 0x8;
  160. // If 'isInsetHard' is true we can't sample across the inset boundary.
  161. // If 'areCoordsLimitedToRect' is true the client promises to never sample outside the inset.
  162. static const SkRect* generic_inset(const RectInfo& enclosing,
  163. RectInfo* result,
  164. bool isInsetHard,
  165. bool areCoordsLimitedToRect,
  166. float insetAmount,
  167. float halfFilterWidth,
  168. uint32_t flags,
  169. const char* name) {
  170. SkRect newR = enclosing.rect();
  171. RectInfo::EdgeType left = enclosing.edgeType(RectInfo::kLeft);
  172. if (flags & kInsetLeft_Flag) {
  173. newR.fLeft += insetAmount;
  174. left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
  175. insetAmount, halfFilterWidth);
  176. } else {
  177. left = compute_inset_edgetype(left, isInsetHard, areCoordsLimitedToRect,
  178. 0.0f, halfFilterWidth);
  179. }
  180. RectInfo::EdgeType top = enclosing.edgeType(RectInfo::kTop);
  181. if (flags & kInsetTop_Flag) {
  182. newR.fTop += insetAmount;
  183. top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
  184. insetAmount, halfFilterWidth);
  185. } else {
  186. top = compute_inset_edgetype(top, isInsetHard, areCoordsLimitedToRect,
  187. 0.0f, halfFilterWidth);
  188. }
  189. RectInfo::EdgeType right = enclosing.edgeType(RectInfo::kRight);
  190. if (flags & kInsetRight_Flag) {
  191. newR.fRight -= insetAmount;
  192. right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
  193. insetAmount, halfFilterWidth);
  194. } else {
  195. right = compute_inset_edgetype(right, isInsetHard, areCoordsLimitedToRect,
  196. 0.0f, halfFilterWidth);
  197. }
  198. RectInfo::EdgeType bot = enclosing.edgeType(RectInfo::kBot);
  199. if (flags & kInsetBot_Flag) {
  200. newR.fBottom -= insetAmount;
  201. bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
  202. insetAmount, halfFilterWidth);
  203. } else {
  204. bot = compute_inset_edgetype(bot, isInsetHard, areCoordsLimitedToRect,
  205. 0.0f, halfFilterWidth);
  206. }
  207. result->set(newR, left, top, right, bot, name);
  208. return &result->rect();
  209. }
  210. // Make a rect that only touches the enclosing rect on the left.
  211. static const SkRect* left_only(const RectInfo& enclosing,
  212. RectInfo* result,
  213. bool isInsetHard,
  214. bool areCoordsLimitedToRect,
  215. float insetAmount,
  216. float halfFilterWidth) {
  217. static const char* name = "left";
  218. return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
  219. insetAmount, halfFilterWidth,
  220. kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
  221. }
  222. // Make a rect that only touches the enclosing rect on the top.
  223. static const SkRect* top_only(const RectInfo& enclosing,
  224. RectInfo* result,
  225. bool isInsetHard,
  226. bool areCoordsLimitedToRect,
  227. float insetAmount,
  228. float halfFilterWidth) {
  229. static const char* name = "top";
  230. return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
  231. insetAmount, halfFilterWidth,
  232. kInsetLeft_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
  233. }
  234. // Make a rect that only touches the enclosing rect on the right.
  235. static const SkRect* right_only(const RectInfo& enclosing,
  236. RectInfo* result,
  237. bool isInsetHard,
  238. bool areCoordsLimitedToRect,
  239. float insetAmount,
  240. float halfFilterWidth) {
  241. static const char* name = "right";
  242. return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
  243. insetAmount, halfFilterWidth,
  244. kInsetLeft_Flag|kInsetTop_Flag|kInsetBot_Flag, name);
  245. }
  246. // Make a rect that only touches the enclosing rect on the bottom.
  247. static const SkRect* bot_only(const RectInfo& enclosing,
  248. RectInfo* result,
  249. bool isInsetHard,
  250. bool areCoordsLimitedToRect,
  251. float insetAmount,
  252. float halfFilterWidth) {
  253. static const char* name = "bot";
  254. return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
  255. insetAmount, halfFilterWidth,
  256. kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag, name);
  257. }
  258. // Make a rect that is inset all around.
  259. static const SkRect* full_inset(const RectInfo& enclosing,
  260. RectInfo* result,
  261. bool isInsetHard,
  262. bool areCoordsLimitedToRect,
  263. float insetAmount,
  264. float halfFilterWidth) {
  265. static const char* name = "all";
  266. return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
  267. insetAmount, halfFilterWidth,
  268. kInsetLeft_Flag|kInsetTop_Flag|kInsetRight_Flag|kInsetBot_Flag, name);
  269. }
  270. // Make a rect with no inset. This is only used for constraint rect creation.
  271. static const SkRect* no_inset(const RectInfo& enclosing,
  272. RectInfo* result,
  273. bool isInsetHard,
  274. bool areCoordsLimitedToRect,
  275. float insetAmount,
  276. float halfFilterWidth) {
  277. static const char* name = "none";
  278. return generic_inset(enclosing, result, isInsetHard, areCoordsLimitedToRect,
  279. insetAmount, halfFilterWidth, 0, name);
  280. }
  281. static void proxy_test(skiatest::Reporter* reporter, GrContext* context) {
  282. GrTextureProducer_TestAccess::DomainMode actualMode, expectedMode;
  283. SkRect actualDomainRect;
  284. static const GrSamplerState::Filter gModes[] = {
  285. GrSamplerState::Filter::kNearest,
  286. GrSamplerState::Filter::kBilerp,
  287. GrSamplerState::Filter::kMipMap,
  288. };
  289. static const GrSamplerState::Filter* gModePtrs[] = {&gModes[0], &gModes[1], nullptr,
  290. &gModes[2]};
  291. static const float gHalfFilterWidth[] = { 0.0f, 0.5f, 1.5f, 10000.0f };
  292. for (auto isPowerOfTwoSized : { true, false }) {
  293. for (auto isExact : { true, false }) {
  294. RectInfo outermost;
  295. sk_sp<GrTextureProxy> proxy = create_proxy(context, isPowerOfTwoSized,
  296. isExact, &outermost);
  297. SkASSERT(outermost.isHardOrBadAllAround());
  298. for (auto isConstraintRectHard : { true, false }) {
  299. for (auto areCoordsLimitedToConstraintRect : { true, false }) {
  300. for (int filterMode = 0; filterMode < 4; ++filterMode) {
  301. for (auto constraintRectMaker : { left_only, top_only, right_only,
  302. bot_only, full_inset, no_inset }) {
  303. for (auto insetAmt : { 0.25f, 0.75f, 1.25f, 1.75f, 5.0f }) {
  304. RectInfo constraintRectStorage;
  305. const SkRect* constraintRect = (*constraintRectMaker)(
  306. outermost,
  307. &constraintRectStorage,
  308. isConstraintRectHard,
  309. areCoordsLimitedToConstraintRect,
  310. insetAmt,
  311. gHalfFilterWidth[filterMode]);
  312. SkASSERT(constraintRect); // always need one of these
  313. SkASSERT(outermost.rect().contains(*constraintRect));
  314. actualMode = GrTextureProducer_TestAccess::DetermineDomainMode(
  315. *constraintRect,
  316. isConstraintRectHard
  317. ? GrTextureProducer::kYes_FilterConstraint
  318. : GrTextureProducer::kNo_FilterConstraint,
  319. areCoordsLimitedToConstraintRect,
  320. proxy.get(),
  321. gModePtrs[filterMode],
  322. &actualDomainRect);
  323. expectedMode = DomainMode::kNoDomain_DomainMode;
  324. if (constraintRectStorage.hasABad()) {
  325. if (3 == filterMode) {
  326. expectedMode = DomainMode::kTightCopy_DomainMode;
  327. } else {
  328. expectedMode = DomainMode::kDomain_DomainMode;
  329. }
  330. }
  331. REPORTER_ASSERT(reporter, expectedMode == actualMode);
  332. // TODO: add a check that the returned domain rect is correct
  333. }
  334. }
  335. }
  336. }
  337. }
  338. }
  339. }
  340. }
  341. DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DetermineDomainModeTest, reporter, ctxInfo) {
  342. GrContext* context = ctxInfo.grContext();
  343. proxy_test(reporter, context);
  344. }