TransferPixelsTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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. // This is a GPU-backend specific test. It relies on static intializers to work
  8. #include "include/core/SkTypes.h"
  9. #include "TestUtils.h"
  10. #include "include/core/SkSurface.h"
  11. #include "include/gpu/GrTexture.h"
  12. #include "src/gpu/GrContextPriv.h"
  13. #include "src/gpu/GrGpu.h"
  14. #include "src/gpu/GrResourceProvider.h"
  15. #include "src/gpu/GrSurfaceProxy.h"
  16. #include "src/gpu/SkGr.h"
  17. #include "tests/Test.h"
  18. #include "tools/gpu/GrContextFactory.h"
  19. using sk_gpu_test::GrContextFactory;
  20. void fill_transfer_data(int left, int top, int width, int height, int bufferWidth,
  21. GrColorType dstType, char* dst) {
  22. size_t dstBpp = GrColorTypeBytesPerPixel(dstType);
  23. auto dstLocation = [dst, dstBpp, bufferWidth](int x, int y) {
  24. return dst + y * dstBpp * bufferWidth + x * dstBpp;
  25. };
  26. // build red-green gradient
  27. for (int j = top; j < top + height; ++j) {
  28. for (int i = left; i < left + width; ++i) {
  29. unsigned int red = (unsigned int)(256.f*((i - left) / (float)width));
  30. unsigned int green = (unsigned int)(256.f*((j - top) / (float)height));
  31. uint32_t srcPixel = GrColorPackRGBA(red - (red>>8),
  32. green - (green>>8), 0xff, 0xff);
  33. GrPixelInfo srcInfo(GrColorType::kRGBA_8888, kPremul_SkAlphaType, nullptr, 1, 1);
  34. GrPixelInfo dstInfo(dstType, kPremul_SkAlphaType, nullptr, 1, 1);
  35. GrConvertPixels(dstInfo, dstLocation(i, j), dstBpp, srcInfo, &srcPixel, 4);
  36. }
  37. }
  38. }
  39. bool read_pixels_from_texture(GrTexture* texture, GrColorType dstColorType, char* dst) {
  40. auto* context = texture->getContext();
  41. auto* gpu = context->priv().getGpu();
  42. auto* caps = context->priv().caps();
  43. int w = texture->width();
  44. int h = texture->height();
  45. size_t rowBytes = GrColorTypeBytesPerPixel(dstColorType) * w;
  46. GrColorType srcCT = GrPixelConfigToColorType(texture->config());
  47. GrCaps::SupportedRead supportedRead = caps->supportedReadPixelsColorType(
  48. srcCT, texture->backendFormat(), dstColorType);
  49. if (supportedRead.fColorType != dstColorType || supportedRead.fSwizzle != GrSwizzle("rgba")) {
  50. size_t tmpRowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * w;
  51. std::unique_ptr<char[]> tmpPixels(new char[tmpRowBytes * h]);
  52. if (!gpu->readPixels(texture, 0, 0, w, h,
  53. supportedRead.fColorType, tmpPixels.get(), tmpRowBytes)) {
  54. return false;
  55. }
  56. GrPixelInfo tmpInfo(supportedRead.fColorType, kPremul_SkAlphaType, nullptr, w, h);
  57. GrPixelInfo dstInfo(dstColorType, kPremul_SkAlphaType, nullptr, w, h);
  58. return GrConvertPixels(dstInfo, dst, rowBytes, tmpInfo, tmpPixels.get(), tmpRowBytes, false,
  59. supportedRead.fSwizzle);
  60. }
  61. return gpu->readPixels(texture, 0, 0, w, h, supportedRead.fColorType, dst, rowBytes);
  62. }
  63. void basic_transfer_to_test(skiatest::Reporter* reporter, GrContext* context, GrColorType colorType,
  64. GrRenderable renderable) {
  65. if (GrCaps::kNone_MapFlags == context->priv().caps()->mapBufferFlags()) {
  66. return;
  67. }
  68. auto* caps = context->priv().caps();
  69. auto backendFormat = caps->getBackendFormatFromColorType(colorType);
  70. if (!caps->isFormatTexturable(colorType, backendFormat)) {
  71. return;
  72. }
  73. if ((renderable == GrRenderable::kYes && !caps->isFormatRenderable(colorType, backendFormat))) {
  74. return;
  75. }
  76. auto resourceProvider = context->priv().resourceProvider();
  77. GrGpu* gpu = context->priv().getGpu();
  78. const int kTextureWidth = 16;
  79. const int kTextureHeight = 16;
  80. int srcBufferWidth = caps->writePixelsRowBytesSupport() ? 16 : 20;
  81. const int kBufferHeight = 16;
  82. GrSurfaceDesc desc;
  83. desc.fWidth = kTextureWidth;
  84. desc.fHeight = kTextureHeight;
  85. desc.fConfig = GrColorTypeToPixelConfig(colorType);
  86. sk_sp<GrTexture> tex =
  87. resourceProvider->createTexture(desc, renderable, 1, SkBudgeted::kNo, GrProtected::kNo,
  88. GrResourceProvider::Flags::kNoPendingIO);
  89. if (!tex) {
  90. ERRORF(reporter, "Could not create texture");
  91. return;
  92. }
  93. // The caps tell us what color type we are allowed to upload and read back from this texture,
  94. // either of which may differ from 'colorType'.
  95. GrColorType allowedSrc = caps->supportedWritePixelsColorType(desc.fConfig, colorType);
  96. size_t srcRowBytes = GrColorTypeBytesPerPixel(allowedSrc) * kTextureWidth;
  97. std::unique_ptr<char[]> srcData(new char[kTextureWidth * srcRowBytes]);
  98. fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, srcBufferWidth, allowedSrc,
  99. srcData.get());
  100. // create and fill transfer buffer
  101. size_t size = srcRowBytes * kBufferHeight;
  102. sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(size, GrGpuBufferType::kXferCpuToGpu,
  103. kDynamic_GrAccessPattern));
  104. if (!buffer) {
  105. return;
  106. }
  107. void* data = buffer->map();
  108. if (!buffer) {
  109. ERRORF(reporter, "Could not map buffer");
  110. return;
  111. }
  112. memcpy(data, srcData.get(), size);
  113. buffer->unmap();
  114. //////////////////////////
  115. // transfer full data
  116. bool result;
  117. result = gpu->transferPixelsTo(tex.get(), 0, 0, kTextureWidth, kTextureHeight, allowedSrc,
  118. buffer.get(), 0, srcRowBytes);
  119. REPORTER_ASSERT(reporter, result);
  120. size_t dstRowBytes = GrColorTypeBytesPerPixel(colorType) * kTextureWidth;
  121. std::unique_ptr<char[]> dstBuffer(new char[dstRowBytes * kTextureHeight]());
  122. result = read_pixels_from_texture(tex.get(), colorType, dstBuffer.get());
  123. if (!result) {
  124. ERRORF(reporter, "Could not read pixels from texture");
  125. return;
  126. }
  127. static constexpr float kTol[4] = {};
  128. auto error = std::function<ComparePixmapsErrorReporter>(
  129. [reporter, colorType](int x, int y, const float diffs[4]) {
  130. ERRORF(reporter, "Error at (%d %d) in transfer, color type: %d", x, y, colorType);
  131. });
  132. GrPixelInfo srcInfo(allowedSrc, kPremul_SkAlphaType, nullptr, tex->width(), tex->height());
  133. GrPixelInfo dstInfo(colorType, kPremul_SkAlphaType, nullptr, tex->width(), tex->height());
  134. compare_pixels(srcInfo, srcData.get(), srcRowBytes, dstInfo, dstBuffer.get(), dstRowBytes, kTol,
  135. error);
  136. //////////////////////////
  137. // transfer partial data
  138. // We're relying on this cap to write partial texture data
  139. if (!caps->writePixelsRowBytesSupport()) {
  140. return;
  141. }
  142. const int kLeft = 2;
  143. const int kTop = 10;
  144. const int kWidth = 10;
  145. const int kHeight = 2;
  146. // change color of subrectangle
  147. fill_transfer_data(kLeft, kTop, kWidth, kHeight, srcBufferWidth, allowedSrc, srcData.get());
  148. data = buffer->map();
  149. memcpy(data, srcData.get(), size);
  150. buffer->unmap();
  151. size_t offset = kTop * srcRowBytes + kLeft * GrColorTypeBytesPerPixel(allowedSrc);
  152. result = gpu->transferPixelsTo(tex.get(), kLeft, kTop, kWidth, kHeight, allowedSrc,
  153. buffer.get(), offset, srcRowBytes);
  154. REPORTER_ASSERT(reporter, result);
  155. result = read_pixels_from_texture(tex.get(), colorType, dstBuffer.get());
  156. if (!result) {
  157. ERRORF(reporter, "Could not read pixels from texture");
  158. return;
  159. }
  160. compare_pixels(srcInfo, srcData.get(), srcRowBytes, dstInfo, dstBuffer.get(), dstRowBytes, kTol,
  161. error);
  162. }
  163. void basic_transfer_from_test(skiatest::Reporter* reporter, const sk_gpu_test::ContextInfo& ctxInfo,
  164. GrColorType colorType, GrRenderable renderable) {
  165. auto context = ctxInfo.grContext();
  166. auto caps = context->priv().caps();
  167. if (GrCaps::kNone_MapFlags == caps->mapBufferFlags()) {
  168. return;
  169. }
  170. auto resourceProvider = context->priv().resourceProvider();
  171. GrGpu* gpu = context->priv().getGpu();
  172. const int kTextureWidth = 16;
  173. const int kTextureHeight = 16;
  174. // We'll do a full texture read into the buffer followed by a partial read. These values
  175. // describe the partial read subrect.
  176. const int kPartialLeft = 2;
  177. const int kPartialTop = 10;
  178. const int kPartialWidth = 10;
  179. const int kPartialHeight = 2;
  180. // create texture
  181. GrSurfaceDesc desc;
  182. desc.fWidth = kTextureWidth;
  183. desc.fHeight = kTextureHeight;
  184. desc.fConfig = GrColorTypeToPixelConfig(colorType);
  185. if (!context->priv().caps()->isConfigTexturable(desc.fConfig) ||
  186. (renderable == GrRenderable::kYes &&
  187. !context->priv().caps()->isConfigRenderable(desc.fConfig))) {
  188. return;
  189. }
  190. size_t textureDataBpp = GrColorTypeBytesPerPixel(colorType);
  191. size_t textureDataRowBytes = kTextureWidth * textureDataBpp;
  192. std::unique_ptr<char[]> textureData(new char[kTextureHeight * textureDataRowBytes]);
  193. fill_transfer_data(0, 0, kTextureWidth, kTextureHeight, kTextureWidth, colorType,
  194. textureData.get());
  195. GrMipLevel data;
  196. data.fPixels = textureData.get();
  197. data.fRowBytes = textureDataRowBytes;
  198. sk_sp<GrTexture> tex = resourceProvider->createTexture(desc, renderable, 1, SkBudgeted::kNo,
  199. GrProtected::kNo, &data, 1);
  200. if (!tex) {
  201. return;
  202. }
  203. // Create the transfer buffer.
  204. auto allowedRead =
  205. caps->supportedReadPixelsColorType(colorType, tex->backendFormat(), colorType);
  206. GrPixelInfo readInfo(allowedRead.fColorType, kPremul_SkAlphaType, nullptr, kTextureWidth,
  207. kTextureHeight);
  208. size_t bpp = GrColorTypeBytesPerPixel(allowedRead.fColorType);
  209. size_t fullBufferRowBytes = kTextureWidth * bpp;
  210. size_t partialBufferRowBytes = kPartialWidth * bpp;
  211. size_t offsetAlignment = caps->transferFromOffsetAlignment(allowedRead.fColorType);
  212. SkASSERT(offsetAlignment);
  213. size_t bufferSize = fullBufferRowBytes * kTextureHeight;
  214. // Arbitrary starting offset for the partial read.
  215. size_t partialReadOffset = GrSizeAlignUp(11, offsetAlignment);
  216. bufferSize = SkTMax(bufferSize, partialReadOffset + partialBufferRowBytes * kPartialHeight);
  217. sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(
  218. bufferSize, GrGpuBufferType::kXferGpuToCpu, kDynamic_GrAccessPattern));
  219. REPORTER_ASSERT(reporter, buffer);
  220. if (!buffer) {
  221. return;
  222. }
  223. int expectedTransferCnt = 0;
  224. gpu->stats()->reset();
  225. //////////////////////////
  226. // transfer full data
  227. bool result = gpu->transferPixelsFrom(tex.get(), 0, 0, kTextureWidth, kTextureHeight,
  228. allowedRead.fColorType, buffer.get(), 0);
  229. if (!result) {
  230. ERRORF(reporter, "transferPixelsFrom failed.");
  231. return;
  232. }
  233. ++expectedTransferCnt;
  234. GrFlushInfo flushInfo;
  235. flushInfo.fFlags = kSyncCpu_GrFlushFlag;
  236. if (context->priv().caps()->mapBufferFlags() & GrCaps::kAsyncRead_MapFlag) {
  237. gpu->finishFlush(nullptr, 0, SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo,
  238. GrPrepareForExternalIORequests());
  239. }
  240. // Copy the transfer buffer contents to a temporary so we can manipulate it.
  241. const auto* map = reinterpret_cast<const char*>(buffer->map());
  242. REPORTER_ASSERT(reporter, map);
  243. if (!map) {
  244. ERRORF(reporter, "Failed to map transfer buffer.");
  245. return;
  246. }
  247. std::unique_ptr<char[]> transferData(new char[kTextureHeight * fullBufferRowBytes]);
  248. memcpy(transferData.get(), map, fullBufferRowBytes * kTextureHeight);
  249. buffer->unmap();
  250. GrPixelInfo transferInfo(allowedRead.fColorType, kPremul_SkAlphaType, nullptr, kTextureWidth,
  251. kTextureHeight);
  252. // Caps may indicate that we should swizzle this data before we compare it.
  253. if (allowedRead.fSwizzle != GrSwizzle("rgba")) {
  254. GrConvertPixels(transferInfo, transferData.get(), fullBufferRowBytes, transferInfo,
  255. transferData.get(), fullBufferRowBytes, false, allowedRead.fSwizzle);
  256. }
  257. static constexpr float kTol[4] = {};
  258. auto error = std::function<ComparePixmapsErrorReporter>(
  259. [reporter, colorType](int x, int y, const float diffs[4]) {
  260. ERRORF(reporter, "Error at (%d %d) in transfer, color type: %d", x, y, colorType);
  261. });
  262. GrPixelInfo textureDataInfo(colorType, kPremul_SkAlphaType, nullptr, kTextureWidth,
  263. kTextureHeight);
  264. compare_pixels(textureDataInfo, textureData.get(), textureDataRowBytes, transferInfo,
  265. transferData.get(), fullBufferRowBytes, kTol, error);
  266. ///////////////////////
  267. // Now test a partial read at an offset into the buffer.
  268. result = gpu->transferPixelsFrom(tex.get(), kPartialLeft, kPartialTop, kPartialWidth,
  269. kPartialHeight, allowedRead.fColorType, buffer.get(),
  270. partialReadOffset);
  271. if (!result) {
  272. ERRORF(reporter, "transferPixelsFrom failed.");
  273. return;
  274. }
  275. ++expectedTransferCnt;
  276. if (context->priv().caps()->mapBufferFlags() & GrCaps::kAsyncRead_MapFlag) {
  277. gpu->finishFlush(nullptr, 0, SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo,
  278. GrPrepareForExternalIORequests());
  279. }
  280. map = reinterpret_cast<const char*>(buffer->map());
  281. REPORTER_ASSERT(reporter, map);
  282. if (!map) {
  283. ERRORF(reporter, "Failed to map transfer buffer.");
  284. return;
  285. }
  286. const char* bufferStart = reinterpret_cast<const char*>(map) + partialReadOffset;
  287. memcpy(transferData.get(), bufferStart, partialBufferRowBytes * kTextureHeight);
  288. buffer->unmap();
  289. transferInfo = transferInfo.makeWH(kPartialWidth, kPartialHeight);
  290. if (allowedRead.fSwizzle != GrSwizzle("rgba")) {
  291. GrConvertPixels(transferInfo, transferData.get(), fullBufferRowBytes, transferInfo,
  292. transferData.get(), fullBufferRowBytes, false, allowedRead.fSwizzle);
  293. }
  294. const char* textureDataStart =
  295. textureData.get() + textureDataRowBytes * kPartialTop + textureDataBpp * kPartialLeft;
  296. textureDataInfo = textureDataInfo.makeWH(kPartialWidth, kPartialHeight);
  297. compare_pixels(textureDataInfo, textureDataStart, textureDataRowBytes, transferInfo,
  298. transferData.get(), partialBufferRowBytes, kTol, error);
  299. #if GR_GPU_STATS
  300. REPORTER_ASSERT(reporter, gpu->stats()->transfersFromSurface() == expectedTransferCnt);
  301. #else
  302. (void)expectedTransferCnt;
  303. #endif
  304. }
  305. DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsToTest, reporter, ctxInfo) {
  306. if (!ctxInfo.grContext()->priv().caps()->transferBufferSupport()) {
  307. return;
  308. }
  309. for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
  310. for (auto colorType :
  311. {GrColorType::kRGBA_8888, GrColorType::kRGBA_8888_SRGB, GrColorType::kBGRA_8888}) {
  312. basic_transfer_to_test(reporter, ctxInfo.grContext(), colorType, renderable);
  313. }
  314. }
  315. }
  316. // TODO(bsalomon): Metal
  317. DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsFromTest, reporter, ctxInfo) {
  318. if (!ctxInfo.grContext()->priv().caps()->transferBufferSupport()) {
  319. return;
  320. }
  321. for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
  322. for (auto colorType :
  323. {GrColorType::kRGBA_8888, GrColorType::kRGBA_8888_SRGB, GrColorType::kBGRA_8888}) {
  324. basic_transfer_from_test(reporter, ctxInfo, colorType, renderable);
  325. }
  326. }
  327. }