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