Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 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 | // This is a GPU-backend specific test. It relies on static intializers to work |
| 9 | |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 10 | #include "include/core/SkTypes.h" |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 11 | |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 12 | #include "include/core/SkSurface.h" |
Robert Phillips | 6d344c3 | 2020-07-06 10:56:46 -0400 | [diff] [blame] | 13 | #include "include/gpu/GrDirectContext.h" |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 14 | #include "src/gpu/GrContextPriv.h" |
| 15 | #include "src/gpu/GrGpu.h" |
Brian Salomon | f2ebdd9 | 2019-09-30 12:15:30 -0400 | [diff] [blame] | 16 | #include "src/gpu/GrImageInfo.h" |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 17 | #include "src/gpu/GrResourceProvider.h" |
Greg Daniel | f91aeb2 | 2019-06-18 09:58:02 -0400 | [diff] [blame] | 18 | #include "src/gpu/GrSurfaceProxy.h" |
Greg Daniel | 456f9b5 | 2020-03-05 19:14:18 +0000 | [diff] [blame] | 19 | #include "src/gpu/GrTexture.h" |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 20 | #include "src/gpu/SkGr.h" |
| 21 | #include "tests/Test.h" |
Mike Klein | 52337de | 2019-07-25 09:00:52 -0500 | [diff] [blame] | 22 | #include "tests/TestUtils.h" |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 23 | #include "tools/gpu/GrContextFactory.h" |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 24 | |
| 25 | using sk_gpu_test::GrContextFactory; |
| 26 | |
| 27 | void fill_transfer_data(int left, int top, int width, int height, int bufferWidth, |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 28 | GrColorType dstType, char* dst) { |
| 29 | size_t dstBpp = GrColorTypeBytesPerPixel(dstType); |
| 30 | auto dstLocation = [dst, dstBpp, bufferWidth](int x, int y) { |
| 31 | return dst + y * dstBpp * bufferWidth + x * dstBpp; |
| 32 | }; |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 33 | // build red-green gradient |
| 34 | for (int j = top; j < top + height; ++j) { |
| 35 | for (int i = left; i < left + width; ++i) { |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 36 | auto r = (unsigned int)(256.f*((i - left) / (float)width)); |
| 37 | auto g = (unsigned int)(256.f*((j - top) / (float)height)); |
| 38 | r -= (r >> 8); |
| 39 | g -= (g >> 8); |
| 40 | // set b and a channels to be inverse of r and g just to have interesting values to |
| 41 | // test. |
| 42 | uint32_t srcPixel = GrColorPackRGBA(r, g, 0xff - r, 0xff - g); |
Brian Salomon | f2ebdd9 | 2019-09-30 12:15:30 -0400 | [diff] [blame] | 43 | GrImageInfo srcInfo(GrColorType::kRGBA_8888, kUnpremul_SkAlphaType, nullptr, 1, 1); |
| 44 | GrImageInfo dstInfo(dstType, kUnpremul_SkAlphaType, nullptr, 1, 1); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 45 | GrConvertPixels(dstInfo, dstLocation(i, j), dstBpp, srcInfo, &srcPixel, 4); |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 46 | } |
| 47 | } |
| 48 | } |
| 49 | |
Brian Salomon | 6f6d2de | 2019-07-26 14:10:33 -0400 | [diff] [blame] | 50 | void determine_tolerances(GrColorType a, GrColorType b, float tolerances[4]) { |
Greg Daniel | 7bf37e0 | 2019-07-29 10:09:21 -0400 | [diff] [blame] | 51 | std::fill_n(tolerances, 4, 0); |
| 52 | |
Brian Salomon | 6f6d2de | 2019-07-26 14:10:33 -0400 | [diff] [blame] | 53 | auto descA = GrGetColorTypeDesc(a); |
| 54 | auto descB = GrGetColorTypeDesc(b); |
| 55 | // For each channel x set the tolerance to 1 / (2^min(bits_in_a, bits_in_b) - 1) unless |
| 56 | // one color type is missing the channel. In that case leave it at 0. If the other color |
| 57 | // has the channel then it better be exactly 1 for alpha or 0 for rgb. |
| 58 | for (int i = 0; i < 4; ++i) { |
| 59 | if (descA[i] != descB[i]) { |
| 60 | auto m = std::min(descA[i], descB[i]); |
| 61 | if (m) { |
| 62 | tolerances[i] = 1.f / (m - 1); |
| 63 | } |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | |
Brian Salomon | f77c146 | 2019-08-01 15:19:29 -0400 | [diff] [blame] | 68 | bool read_pixels_from_texture(GrTexture* texture, GrColorType colorType, char* dst, |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 69 | float tolerances[4]) { |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 70 | auto* context = texture->getContext(); |
| 71 | auto* gpu = context->priv().getGpu(); |
| 72 | auto* caps = context->priv().caps(); |
| 73 | |
| 74 | int w = texture->width(); |
| 75 | int h = texture->height(); |
Brian Salomon | f77c146 | 2019-08-01 15:19:29 -0400 | [diff] [blame] | 76 | size_t rowBytes = GrColorTypeBytesPerPixel(colorType) * w; |
Greg Daniel | c6dc5cf | 2019-07-17 16:02:00 -0400 | [diff] [blame] | 77 | |
Brian Salomon | f77c146 | 2019-08-01 15:19:29 -0400 | [diff] [blame] | 78 | GrCaps::SupportedRead supportedRead = |
Greg Daniel | d51fa2f | 2020-01-22 16:53:38 -0500 | [diff] [blame] | 79 | caps->supportedReadPixelsColorType(colorType, texture->backendFormat(), colorType); |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 80 | std::fill_n(tolerances, 4, 0); |
Brian Salomon | f77c146 | 2019-08-01 15:19:29 -0400 | [diff] [blame] | 81 | if (supportedRead.fColorType != colorType) { |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 82 | size_t tmpRowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * w; |
| 83 | std::unique_ptr<char[]> tmpPixels(new char[tmpRowBytes * h]); |
Brian Salomon | f77c146 | 2019-08-01 15:19:29 -0400 | [diff] [blame] | 84 | if (!gpu->readPixels(texture, 0, 0, w, h, colorType, supportedRead.fColorType, |
| 85 | tmpPixels.get(), tmpRowBytes)) { |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 86 | return false; |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 87 | } |
Brian Salomon | f2ebdd9 | 2019-09-30 12:15:30 -0400 | [diff] [blame] | 88 | GrImageInfo tmpInfo(supportedRead.fColorType, kUnpremul_SkAlphaType, nullptr, w, h); |
| 89 | GrImageInfo dstInfo(colorType, kUnpremul_SkAlphaType, nullptr, w, h); |
Brian Salomon | 6f6d2de | 2019-07-26 14:10:33 -0400 | [diff] [blame] | 90 | determine_tolerances(tmpInfo.colorType(), dstInfo.colorType(), tolerances); |
Brian Salomon | 8f8354a | 2019-07-31 20:12:02 -0400 | [diff] [blame] | 91 | return GrConvertPixels(dstInfo, dst, rowBytes, tmpInfo, tmpPixels.get(), tmpRowBytes, |
| 92 | false); |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 93 | } |
Brian Salomon | f77c146 | 2019-08-01 15:19:29 -0400 | [diff] [blame] | 94 | return gpu->readPixels(texture, 0, 0, w, h, colorType, supportedRead.fColorType, dst, rowBytes); |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 95 | } |
| 96 | |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 97 | void basic_transfer_to_test(skiatest::Reporter* reporter, GrContext* context, GrColorType colorType, |
Brian Salomon | f2c2ba9 | 2019-07-17 09:59:59 -0400 | [diff] [blame] | 98 | GrRenderable renderable) { |
Robert Phillips | 9da87e0 | 2019-02-04 13:26:26 -0500 | [diff] [blame] | 99 | if (GrCaps::kNone_MapFlags == context->priv().caps()->mapBufferFlags()) { |
Brian Salomon | 7f56d3d | 2017-10-09 13:02:49 -0400 | [diff] [blame] | 100 | return; |
| 101 | } |
| 102 | |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 103 | auto* caps = context->priv().caps(); |
Robert Phillips | 6be756b | 2018-01-16 15:07:54 -0500 | [diff] [blame] | 104 | |
Robert Phillips | 0a15cc6 | 2019-07-30 12:49:10 -0400 | [diff] [blame] | 105 | auto backendFormat = caps->getDefaultBackendFormat(colorType, renderable); |
| 106 | if (!backendFormat.isValid()) { |
Brian Salomon | f2c2ba9 | 2019-07-17 09:59:59 -0400 | [diff] [blame] | 107 | return; |
Greg Daniel | 6432909 | 2019-07-16 16:16:21 -0400 | [diff] [blame] | 108 | } |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 109 | |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 110 | auto resourceProvider = context->priv().resourceProvider(); |
| 111 | GrGpu* gpu = context->priv().getGpu(); |
| 112 | |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 113 | static constexpr SkISize kTexDims = {16, 16}; |
Brian Salomon | f27e51f | 2019-07-25 13:20:58 -0400 | [diff] [blame] | 114 | int srcBufferWidth = caps->writePixelsRowBytesSupport() ? 20 : 16; |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 115 | const int kBufferHeight = 16; |
| 116 | |
Brian Salomon | a90382f | 2019-09-17 09:01:56 -0400 | [diff] [blame] | 117 | sk_sp<GrTexture> tex = |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 118 | resourceProvider->createTexture(kTexDims, backendFormat, renderable, 1, |
| 119 | GrMipMapped::kNo, SkBudgeted::kNo, GrProtected::kNo); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 120 | if (!tex) { |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 121 | ERRORF(reporter, "Could not create texture"); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 122 | return; |
| 123 | } |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 124 | |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 125 | // We validate the results using GrGpu::readPixels, so exit if this is not supported. |
| 126 | // TODO: Do this through GrSurfaceContext once it works for all color types or support |
| 127 | // kCopyToTexture2D here. |
| 128 | if (GrCaps::SurfaceReadPixelsSupport::kSupported != |
| 129 | caps->surfaceSupportsReadPixels(tex.get())) { |
| 130 | return; |
| 131 | } |
| 132 | // GL requires a texture to be framebuffer bindable to call glReadPixels. However, we have not |
| 133 | // incorporated that test into surfaceSupportsReadPixels(). TODO: Remove this once we handle |
| 134 | // drawing to a bindable format. |
Greg Daniel | 900583a | 2019-08-06 12:05:31 -0400 | [diff] [blame] | 135 | if (!caps->isFormatAsColorTypeRenderable(colorType, tex->backendFormat())) { |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 136 | return; |
| 137 | } |
| 138 | |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 139 | // The caps tell us what color type we are allowed to upload and read back from this texture, |
| 140 | // either of which may differ from 'colorType'. |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 141 | GrCaps::SupportedWrite allowedSrc = |
Brian Salomon | 01915c0 | 2019-08-02 09:57:21 -0400 | [diff] [blame] | 142 | caps->supportedWritePixelsColorType(colorType, tex->backendFormat(), colorType); |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 143 | size_t srcRowBytes = GrColorTypeBytesPerPixel(allowedSrc.fColorType) * srcBufferWidth; |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 144 | std::unique_ptr<char[]> srcData(new char[kTexDims.fHeight * srcRowBytes]); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 145 | |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 146 | fill_transfer_data(0, 0, kTexDims.fWidth, kTexDims.fHeight, srcBufferWidth, |
| 147 | allowedSrc.fColorType, srcData.get()); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 148 | |
| 149 | // create and fill transfer buffer |
| 150 | size_t size = srcRowBytes * kBufferHeight; |
| 151 | sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer(size, GrGpuBufferType::kXferCpuToGpu, |
| 152 | kDynamic_GrAccessPattern)); |
| 153 | if (!buffer) { |
| 154 | return; |
| 155 | } |
| 156 | void* data = buffer->map(); |
| 157 | if (!buffer) { |
| 158 | ERRORF(reporter, "Could not map buffer"); |
| 159 | return; |
| 160 | } |
| 161 | memcpy(data, srcData.get(), size); |
| 162 | buffer->unmap(); |
| 163 | |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 164 | ////////////////////////// |
| 165 | // transfer full data |
Robert Phillips | 16d8ec6 | 2017-07-27 16:16:25 -0400 | [diff] [blame] | 166 | |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 167 | bool result; |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 168 | result = gpu->transferPixelsTo(tex.get(), 0, 0, kTexDims.fWidth, kTexDims.fHeight, colorType, |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 169 | allowedSrc.fColorType, buffer.get(), 0, srcRowBytes); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 170 | REPORTER_ASSERT(reporter, result); |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 171 | |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 172 | size_t dstRowBytes = GrColorTypeBytesPerPixel(colorType) * kTexDims.fWidth; |
| 173 | std::unique_ptr<char[]> dstBuffer(new char[dstRowBytes * kTexDims.fHeight]()); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 174 | |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 175 | float compareTolerances[4] = {}; |
| 176 | result = read_pixels_from_texture(tex.get(), colorType, dstBuffer.get(), compareTolerances); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 177 | if (!result) { |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 178 | ERRORF(reporter, "Could not read pixels from texture, color type: %d", |
| 179 | static_cast<int>(colorType)); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 180 | return; |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 181 | } |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 182 | |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 183 | auto error = std::function<ComparePixmapsErrorReporter>( |
| 184 | [reporter, colorType](int x, int y, const float diffs[4]) { |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 185 | ERRORF(reporter, |
Robert Phillips | 9a30ee0 | 2020-04-29 08:58:39 -0400 | [diff] [blame] | 186 | "Error at (%d %d) in transfer, color type: %s, diffs: (%f, %f, %f, %f)", |
| 187 | x, y, GrColorTypeToStr(colorType), |
| 188 | diffs[0], diffs[1], diffs[2], diffs[3]); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 189 | }); |
Brian Salomon | f2ebdd9 | 2019-09-30 12:15:30 -0400 | [diff] [blame] | 190 | GrImageInfo srcInfo(allowedSrc.fColorType, kUnpremul_SkAlphaType, nullptr, tex->width(), |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 191 | tex->height()); |
Brian Salomon | f2ebdd9 | 2019-09-30 12:15:30 -0400 | [diff] [blame] | 192 | GrImageInfo dstInfo(colorType, kUnpremul_SkAlphaType, nullptr, tex->width(), tex->height()); |
Brian Salomon | 28a8f28 | 2019-10-24 20:07:39 -0400 | [diff] [blame] | 193 | ComparePixels(srcInfo, srcData.get(), srcRowBytes, dstInfo, dstBuffer.get(), dstRowBytes, |
| 194 | compareTolerances, error); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 195 | |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 196 | ////////////////////////// |
| 197 | // transfer partial data |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 198 | |
| 199 | // We're relying on this cap to write partial texture data |
| 200 | if (!caps->writePixelsRowBytesSupport()) { |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 201 | return; |
| 202 | } |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 203 | // We keep a 1 to 1 correspondence between pixels in the buffer and the entire texture. We |
| 204 | // update the contents of a sub-rect of the buffer and push that rect to the texture. We start |
| 205 | // with a left sub-rect inset of 2 but may adjust that so we can fulfill the transfer buffer |
| 206 | // offset alignment requirement. |
| 207 | int left = 2; |
| 208 | const int top = 10; |
| 209 | const int width = 10; |
| 210 | const int height = 2; |
| 211 | size_t offset = top * srcRowBytes + left * GrColorTypeBytesPerPixel(allowedSrc.fColorType); |
| 212 | while (offset % allowedSrc.fOffsetAlignmentForTransferBuffer) { |
| 213 | offset += GrColorTypeBytesPerPixel(allowedSrc.fColorType); |
| 214 | ++left; |
| 215 | // We're assuming that the required alignment is 1 or a small multiple of the bpp, which |
| 216 | // it is currently for all color types across all backends. |
| 217 | SkASSERT(left + width <= tex->width()); |
| 218 | } |
Brian Salomon | c320b15 | 2018-02-20 14:05:36 -0500 | [diff] [blame] | 219 | |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 220 | // change color of subrectangle |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 221 | fill_transfer_data(left, top, width, height, srcBufferWidth, allowedSrc.fColorType, |
| 222 | srcData.get()); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 223 | data = buffer->map(); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 224 | memcpy(data, srcData.get(), size); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 225 | buffer->unmap(); |
Brian Salomon | c320b15 | 2018-02-20 14:05:36 -0500 | [diff] [blame] | 226 | |
Brian Salomon | f77c146 | 2019-08-01 15:19:29 -0400 | [diff] [blame] | 227 | result = gpu->transferPixelsTo(tex.get(), left, top, width, height, colorType, |
| 228 | allowedSrc.fColorType, buffer.get(), offset, srcRowBytes); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 229 | if (!result) { |
Brian Salomon | f77c146 | 2019-08-01 15:19:29 -0400 | [diff] [blame] | 230 | gpu->transferPixelsTo(tex.get(), left, top, width, height, colorType, allowedSrc.fColorType, |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 231 | buffer.get(), offset, srcRowBytes); |
| 232 | ERRORF(reporter, "Could not transfer pixels to texture, color type: %d", |
| 233 | static_cast<int>(colorType)); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 234 | return; |
Jim Van Verth | 52fb02e | 2017-06-27 10:36:56 -0400 | [diff] [blame] | 235 | } |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 236 | |
| 237 | result = read_pixels_from_texture(tex.get(), colorType, dstBuffer.get(), compareTolerances); |
| 238 | if (!result) { |
| 239 | ERRORF(reporter, "Could not read pixels from texture, color type: %d", |
| 240 | static_cast<int>(colorType)); |
| 241 | return; |
| 242 | } |
Brian Salomon | 28a8f28 | 2019-10-24 20:07:39 -0400 | [diff] [blame] | 243 | ComparePixels(srcInfo, srcData.get(), srcRowBytes, dstInfo, dstBuffer.get(), dstRowBytes, |
| 244 | compareTolerances, error); |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 245 | } |
| 246 | |
Brian Salomon | 42cbedd | 2019-04-09 15:00:05 -0400 | [diff] [blame] | 247 | void basic_transfer_from_test(skiatest::Reporter* reporter, const sk_gpu_test::ContextInfo& ctxInfo, |
Brian Salomon | f2c2ba9 | 2019-07-17 09:59:59 -0400 | [diff] [blame] | 248 | GrColorType colorType, GrRenderable renderable) { |
Robert Phillips | 6d344c3 | 2020-07-06 10:56:46 -0400 | [diff] [blame] | 249 | auto context = ctxInfo.directContext(); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 250 | auto caps = context->priv().caps(); |
| 251 | if (GrCaps::kNone_MapFlags == caps->mapBufferFlags()) { |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 252 | return; |
| 253 | } |
Jim Van Verth | 686046b | 2019-03-18 15:39:22 -0400 | [diff] [blame] | 254 | |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 255 | auto resourceProvider = context->priv().resourceProvider(); |
| 256 | GrGpu* gpu = context->priv().getGpu(); |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 257 | |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 258 | static constexpr SkISize kTexDims = {16, 16}; |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 259 | |
| 260 | // We'll do a full texture read into the buffer followed by a partial read. These values |
| 261 | // describe the partial read subrect. |
| 262 | const int kPartialLeft = 2; |
| 263 | const int kPartialTop = 10; |
| 264 | const int kPartialWidth = 10; |
| 265 | const int kPartialHeight = 2; |
| 266 | |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 267 | // create texture |
Brian Salomon | 4eb38b7 | 2019-08-05 12:58:39 -0400 | [diff] [blame] | 268 | auto format = context->priv().caps()->getDefaultBackendFormat(colorType, renderable); |
| 269 | if (!format.isValid()) { |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 270 | return; |
| 271 | } |
| 272 | |
| 273 | size_t textureDataBpp = GrColorTypeBytesPerPixel(colorType); |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 274 | size_t textureDataRowBytes = kTexDims.fWidth * textureDataBpp; |
| 275 | std::unique_ptr<char[]> textureData(new char[kTexDims.fHeight * textureDataRowBytes]); |
| 276 | fill_transfer_data(0, 0, kTexDims.fWidth, kTexDims.fHeight, kTexDims.fHeight, colorType, |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 277 | textureData.get()); |
| 278 | GrMipLevel data; |
| 279 | data.fPixels = textureData.get(); |
| 280 | data.fRowBytes = textureDataRowBytes; |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 281 | sk_sp<GrTexture> tex = |
| 282 | resourceProvider->createTexture(kTexDims, format, colorType, renderable, 1, |
| 283 | SkBudgeted::kNo, GrProtected::kNo, &data, 1); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 284 | if (!tex) { |
| 285 | return; |
| 286 | } |
| 287 | |
Brian Salomon | 6f6d2de | 2019-07-26 14:10:33 -0400 | [diff] [blame] | 288 | if (GrCaps::SurfaceReadPixelsSupport::kSupported != |
| 289 | caps->surfaceSupportsReadPixels(tex.get())) { |
| 290 | return; |
| 291 | } |
| 292 | // GL requires a texture to be framebuffer bindable to call glReadPixels. However, we have not |
| 293 | // incorporated that test into surfaceSupportsReadPixels(). TODO: Remove this once we handle |
| 294 | // drawing to a bindable format. |
Greg Daniel | 900583a | 2019-08-06 12:05:31 -0400 | [diff] [blame] | 295 | if (!caps->isFormatAsColorTypeRenderable(colorType, tex->backendFormat())) { |
Brian Salomon | 6f6d2de | 2019-07-26 14:10:33 -0400 | [diff] [blame] | 296 | return; |
| 297 | } |
| 298 | |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 299 | // Create the transfer buffer. |
| 300 | auto allowedRead = |
Greg Daniel | 00fb724 | 2019-07-18 14:28:01 -0400 | [diff] [blame] | 301 | caps->supportedReadPixelsColorType(colorType, tex->backendFormat(), colorType); |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 302 | GrImageInfo readInfo(allowedRead.fColorType, kUnpremul_SkAlphaType, nullptr, kTexDims); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 303 | |
| 304 | size_t bpp = GrColorTypeBytesPerPixel(allowedRead.fColorType); |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 305 | size_t fullBufferRowBytes = kTexDims.fWidth * bpp; |
Brian Salomon | 26de56e | 2019-04-10 12:14:26 -0400 | [diff] [blame] | 306 | size_t partialBufferRowBytes = kPartialWidth * bpp; |
Greg Daniel | ba88ab6 | 2019-07-26 09:14:01 -0400 | [diff] [blame] | 307 | size_t offsetAlignment = allowedRead.fOffsetAlignmentForTransferBuffer; |
Brian Salomon | 26de56e | 2019-04-10 12:14:26 -0400 | [diff] [blame] | 308 | SkASSERT(offsetAlignment); |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 309 | |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 310 | size_t bufferSize = fullBufferRowBytes * kTexDims.fHeight; |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 311 | // Arbitrary starting offset for the partial read. |
Brian Salomon | e994380 | 2020-01-03 13:07:07 -0500 | [diff] [blame] | 312 | size_t partialReadOffset = GrAlignTo(11, offsetAlignment); |
Brian Osman | 788b916 | 2020-02-07 10:36:46 -0500 | [diff] [blame] | 313 | bufferSize = std::max(bufferSize, partialReadOffset + partialBufferRowBytes * kPartialHeight); |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 314 | |
| 315 | sk_sp<GrGpuBuffer> buffer(resourceProvider->createBuffer( |
| 316 | bufferSize, GrGpuBufferType::kXferGpuToCpu, kDynamic_GrAccessPattern)); |
| 317 | REPORTER_ASSERT(reporter, buffer); |
| 318 | if (!buffer) { |
| 319 | return; |
| 320 | } |
| 321 | |
| 322 | int expectedTransferCnt = 0; |
| 323 | gpu->stats()->reset(); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 324 | |
| 325 | ////////////////////////// |
| 326 | // transfer full data |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 327 | bool result = gpu->transferPixelsFrom(tex.get(), 0, 0, kTexDims.fWidth, kTexDims.fHeight, |
| 328 | colorType, allowedRead.fColorType, buffer.get(), 0); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 329 | if (!result) { |
| 330 | ERRORF(reporter, "transferPixelsFrom failed."); |
| 331 | return; |
| 332 | } |
| 333 | ++expectedTransferCnt; |
| 334 | |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 335 | if (context->priv().caps()->mapBufferFlags() & GrCaps::kAsyncRead_MapFlag) { |
Greg Daniel | fe15962 | 2020-04-10 17:43:51 +0000 | [diff] [blame] | 336 | gpu->submitToGpu(true); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 337 | } |
| 338 | |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 339 | // Copy the transfer buffer contents to a temporary so we can manipulate it. |
| 340 | const auto* map = reinterpret_cast<const char*>(buffer->map()); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 341 | REPORTER_ASSERT(reporter, map); |
| 342 | if (!map) { |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 343 | ERRORF(reporter, "Failed to map transfer buffer."); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 344 | return; |
| 345 | } |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 346 | std::unique_ptr<char[]> transferData(new char[kTexDims.fHeight * fullBufferRowBytes]); |
| 347 | memcpy(transferData.get(), map, fullBufferRowBytes * kTexDims.fHeight); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 348 | buffer->unmap(); |
| 349 | |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 350 | GrImageInfo transferInfo(allowedRead.fColorType, kUnpremul_SkAlphaType, nullptr, kTexDims); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 351 | |
Brian Salomon | 6f6d2de | 2019-07-26 14:10:33 -0400 | [diff] [blame] | 352 | float tol[4]; |
| 353 | determine_tolerances(allowedRead.fColorType, colorType, tol); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 354 | auto error = std::function<ComparePixmapsErrorReporter>( |
| 355 | [reporter, colorType](int x, int y, const float diffs[4]) { |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 356 | ERRORF(reporter, |
Robert Phillips | 9a30ee0 | 2020-04-29 08:58:39 -0400 | [diff] [blame] | 357 | "Error at (%d %d) in transfer, color type: %s, diffs: (%f, %f, %f, %f)", |
| 358 | x, y, GrColorTypeToStr(colorType), |
| 359 | diffs[0], diffs[1], diffs[2], diffs[3]); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 360 | }); |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 361 | GrImageInfo textureDataInfo(colorType, kUnpremul_SkAlphaType, nullptr, kTexDims); |
Brian Salomon | 28a8f28 | 2019-10-24 20:07:39 -0400 | [diff] [blame] | 362 | ComparePixels(textureDataInfo, textureData.get(), textureDataRowBytes, transferInfo, |
| 363 | transferData.get(), fullBufferRowBytes, tol, error); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 364 | |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 365 | /////////////////////// |
| 366 | // Now test a partial read at an offset into the buffer. |
| 367 | result = gpu->transferPixelsFrom(tex.get(), kPartialLeft, kPartialTop, kPartialWidth, |
Brian Salomon | f77c146 | 2019-08-01 15:19:29 -0400 | [diff] [blame] | 368 | kPartialHeight, colorType, allowedRead.fColorType, |
| 369 | buffer.get(), partialReadOffset); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 370 | if (!result) { |
| 371 | ERRORF(reporter, "transferPixelsFrom failed."); |
| 372 | return; |
| 373 | } |
| 374 | ++expectedTransferCnt; |
| 375 | |
| 376 | if (context->priv().caps()->mapBufferFlags() & GrCaps::kAsyncRead_MapFlag) { |
Greg Daniel | fe15962 | 2020-04-10 17:43:51 +0000 | [diff] [blame] | 377 | gpu->submitToGpu(true); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 378 | } |
| 379 | |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 380 | map = reinterpret_cast<const char*>(buffer->map()); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 381 | REPORTER_ASSERT(reporter, map); |
| 382 | if (!map) { |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 383 | ERRORF(reporter, "Failed to map transfer buffer."); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 384 | return; |
| 385 | } |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 386 | const char* bufferStart = reinterpret_cast<const char*>(map) + partialReadOffset; |
Brian Salomon | a56a746 | 2020-02-07 14:17:25 -0500 | [diff] [blame] | 387 | memcpy(transferData.get(), bufferStart, partialBufferRowBytes * kTexDims.fHeight); |
Greg Daniel | e877dce | 2019-07-11 10:52:43 -0400 | [diff] [blame] | 388 | buffer->unmap(); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 389 | |
| 390 | transferInfo = transferInfo.makeWH(kPartialWidth, kPartialHeight); |
Brian Salomon | 6aecd5e | 2019-07-16 09:47:32 -0400 | [diff] [blame] | 391 | const char* textureDataStart = |
| 392 | textureData.get() + textureDataRowBytes * kPartialTop + textureDataBpp * kPartialLeft; |
| 393 | textureDataInfo = textureDataInfo.makeWH(kPartialWidth, kPartialHeight); |
Brian Salomon | 28a8f28 | 2019-10-24 20:07:39 -0400 | [diff] [blame] | 394 | ComparePixels(textureDataInfo, textureDataStart, textureDataRowBytes, transferInfo, |
| 395 | transferData.get(), partialBufferRowBytes, tol, error); |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 396 | #if GR_GPU_STATS |
| 397 | REPORTER_ASSERT(reporter, gpu->stats()->transfersFromSurface() == expectedTransferCnt); |
| 398 | #else |
| 399 | (void)expectedTransferCnt; |
| 400 | #endif |
Jim Van Verth | 2e5eaf0 | 2017-06-21 15:55:46 -0400 | [diff] [blame] | 401 | } |
Jim Van Verth | 686046b | 2019-03-18 15:39:22 -0400 | [diff] [blame] | 402 | |
Brian Salomon | fb28c6f | 2020-01-10 13:04:45 -0500 | [diff] [blame] | 403 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsToTextureTest, reporter, ctxInfo) { |
Robert Phillips | 6d344c3 | 2020-07-06 10:56:46 -0400 | [diff] [blame] | 404 | if (!ctxInfo.directContext()->priv().caps()->transferFromBufferToTextureSupport()) { |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 405 | return; |
| 406 | } |
Brian Salomon | f2c2ba9 | 2019-07-17 09:59:59 -0400 | [diff] [blame] | 407 | for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) { |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 408 | for (auto colorType : { |
| 409 | GrColorType::kAlpha_8, |
| 410 | GrColorType::kBGR_565, |
| 411 | GrColorType::kABGR_4444, |
| 412 | GrColorType::kRGBA_8888, |
| 413 | GrColorType::kRGBA_8888_SRGB, |
| 414 | // GrColorType::kRGB_888x, Broken in GL until we have kRGB_888 |
| 415 | GrColorType::kRG_88, |
| 416 | GrColorType::kBGRA_8888, |
| 417 | GrColorType::kRGBA_1010102, |
Robert Phillips | 9a30ee0 | 2020-04-29 08:58:39 -0400 | [diff] [blame] | 418 | GrColorType::kBGRA_1010102, |
Brian Salomon | b3bd864 | 2019-11-04 16:59:29 -0500 | [diff] [blame] | 419 | GrColorType::kGray_8, |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 420 | GrColorType::kAlpha_F16, |
| 421 | GrColorType::kRGBA_F16, |
| 422 | GrColorType::kRGBA_F16_Clamped, |
| 423 | GrColorType::kRGBA_F32, |
Robert Phillips | 429f0d3 | 2019-09-11 17:03:28 -0400 | [diff] [blame] | 424 | GrColorType::kAlpha_16, |
Brian Salomon | 42be09d | 2019-07-26 12:12:26 -0400 | [diff] [blame] | 425 | GrColorType::kRG_1616, |
| 426 | GrColorType::kRGBA_16161616, |
| 427 | GrColorType::kRG_F16, |
| 428 | }) { |
Robert Phillips | 6d344c3 | 2020-07-06 10:56:46 -0400 | [diff] [blame] | 429 | basic_transfer_to_test(reporter, ctxInfo.directContext(), colorType, renderable); |
Brian Salomon | f2c2ba9 | 2019-07-17 09:59:59 -0400 | [diff] [blame] | 430 | } |
| 431 | } |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 432 | } |
| 433 | |
Brian Salomon | a585fe9 | 2019-04-09 14:57:00 -0400 | [diff] [blame] | 434 | // TODO(bsalomon): Metal |
Brian Salomon | fb28c6f | 2020-01-10 13:04:45 -0500 | [diff] [blame] | 435 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsFromTextureTest, reporter, ctxInfo) { |
Robert Phillips | 6d344c3 | 2020-07-06 10:56:46 -0400 | [diff] [blame] | 436 | if (!ctxInfo.directContext()->priv().caps()->transferFromSurfaceToBufferSupport()) { |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 437 | return; |
| 438 | } |
Brian Salomon | f2c2ba9 | 2019-07-17 09:59:59 -0400 | [diff] [blame] | 439 | for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) { |
Brian Salomon | 6f6d2de | 2019-07-26 14:10:33 -0400 | [diff] [blame] | 440 | for (auto colorType : { |
Brian Salomon | b3bd864 | 2019-11-04 16:59:29 -0500 | [diff] [blame] | 441 | GrColorType::kAlpha_8, |
| 442 | GrColorType::kAlpha_16, |
| 443 | GrColorType::kBGR_565, |
| 444 | GrColorType::kABGR_4444, |
| 445 | GrColorType::kRGBA_8888, |
| 446 | GrColorType::kRGBA_8888_SRGB, |
| 447 | // GrColorType::kRGB_888x, Broken in GL until we have kRGB_888 |
| 448 | GrColorType::kRG_88, |
| 449 | GrColorType::kBGRA_8888, |
| 450 | GrColorType::kRGBA_1010102, |
Robert Phillips | 9a30ee0 | 2020-04-29 08:58:39 -0400 | [diff] [blame] | 451 | GrColorType::kBGRA_1010102, |
Brian Salomon | b3bd864 | 2019-11-04 16:59:29 -0500 | [diff] [blame] | 452 | GrColorType::kGray_8, |
| 453 | GrColorType::kAlpha_F16, |
| 454 | GrColorType::kRGBA_F16, |
| 455 | GrColorType::kRGBA_F16_Clamped, |
| 456 | GrColorType::kRGBA_F32, |
| 457 | GrColorType::kRG_1616, |
| 458 | GrColorType::kRGBA_16161616, |
| 459 | GrColorType::kRG_F16, |
| 460 | }) { |
Brian Salomon | f2c2ba9 | 2019-07-17 09:59:59 -0400 | [diff] [blame] | 461 | basic_transfer_from_test(reporter, ctxInfo, colorType, renderable); |
| 462 | } |
| 463 | } |
Brian Salomon | e05ba5a | 2019-04-08 11:59:07 -0400 | [diff] [blame] | 464 | } |