DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); SkBitmap bm = create_bm(); const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps()); sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kNo, bm.getPixels(), bm.rowBytes())); if (!proxy) { return; } sk_sp<SkSpecialImage> fullSImg(SkSpecialImage::MakeDeferredFromGpu( context, SkIRect::MakeWH(kFullSize, kFullSize), kNeedNewImageUniqueID_SpecialImage, proxy, nullptr)); const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); { sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeDeferredFromGpu( context, subset, kNeedNewImageUniqueID_SpecialImage, std::move(proxy), nullptr)); test_image(subSImg1, reporter, context, true, kPad, kFullSize); } { sk_sp<SkSpecialImage> subSImg2(fullSImg->makeSubset(subset)); test_image(subSImg2, reporter, context, true, kPad, kFullSize); } }
void onDraw(SkCanvas* canvas) override { GrRenderTargetContext* renderTargetContext = canvas->internal_private_accessTopLayerRenderTargetContext(); if (!renderTargetContext) { skiagm::GM::DrawGpuOnlyMessage(canvas); return; } GrContext* context = canvas->getGrContext(); if (!context) { return; } GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); sk_sp<GrTextureProxy> proxy[3]; for (int i = 0; i < 3; ++i) { int index = (0 == i) ? 0 : 1; GrSurfaceDesc desc; desc.fWidth = fBmp[index].width(); desc.fHeight = fBmp[index].height(); desc.fConfig = SkImageInfo2GrPixelConfig(fBmp[index].info(), *context->caps()); SkASSERT(kUnknown_GrPixelConfig != desc.fConfig); proxy[i] = proxyProvider->createTextureProxy( desc, SkBudgeted::kYes, fBmp[index].getPixels(), fBmp[index].rowBytes()); if (!proxy[i]) { return; } } constexpr SkScalar kDrawPad = 10.f; constexpr SkScalar kTestPad = 10.f; constexpr SkScalar kColorSpaceOffset = 36.f; SkISize sizes[3] = {{YSIZE, YSIZE}, {USIZE, USIZE}, {VSIZE, VSIZE}}; for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) { SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBmp[0].width()), SkIntToScalar(fBmp[0].height())); renderRect.outset(kDrawPad, kDrawPad); SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset; SkScalar x = kDrawPad + kTestPad; GrPaint grPaint; grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); auto fp = GrYUVtoRGBEffect::Make(proxy[0], proxy[1], proxy[2], sizes, static_cast<SkYUVColorSpace>(space), true); if (fp) { SkMatrix viewMatrix; viewMatrix.setTranslate(x, y); grPaint.addColorFragmentProcessor(std::move(fp)); std::unique_ptr<GrDrawOp> op(GrRectOpFactory::MakeNonAAFill( std::move(grPaint), viewMatrix, renderRect, GrAAType::kNone)); renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); } } }
// Test out the SkSpecialImage::makeTextureImage entry point DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_MakeTexture, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); SkBitmap bm = create_bm(); const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); { // raster sk_sp<SkSpecialImage> rasterImage(SkSpecialImage::MakeFromRaster( SkIRect::MakeWH(kFullSize, kFullSize), bm)); { sk_sp<SkSpecialImage> fromRaster(rasterImage->makeTextureImage(context)); test_texture_backed(reporter, rasterImage, fromRaster); } { sk_sp<SkSpecialImage> subRasterImage(rasterImage->makeSubset(subset)); sk_sp<SkSpecialImage> fromSubRaster(subRasterImage->makeTextureImage(context)); test_texture_backed(reporter, subRasterImage, fromSubRaster); } } { // gpu const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bm.info(), *context->caps()); sk_sp<GrTextureProxy> proxy(GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kNo, bm.getPixels(), bm.rowBytes())); if (!proxy) { return; } sk_sp<SkSpecialImage> gpuImage(SkSpecialImage::MakeDeferredFromGpu( context, SkIRect::MakeWH(kFullSize, kFullSize), kNeedNewImageUniqueID_SpecialImage, std::move(proxy), nullptr)); { sk_sp<SkSpecialImage> fromGPU(gpuImage->makeTextureImage(context)); test_texture_backed(reporter, gpuImage, fromGPU); } { sk_sp<SkSpecialImage> subGPUImage(gpuImage->makeSubset(subset)); sk_sp<SkSpecialImage> fromSubGPU(subGPUImage->makeTextureImage(context)); test_texture_backed(reporter, subGPUImage, fromSubGPU); } } }
// This tests that GrTextureStripAtlas flushes pending IO on the texture it acquires. DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrTextureStripAtlasFlush, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); GrSurfaceDesc desc; desc.fWidth = 32; desc.fHeight = 32; desc.fConfig = kRGBA_8888_GrPixelConfig; GrTexture* texture = context->textureProvider()->createTexture(desc, SkBudgeted::kYes, nullptr, 0); GrSurfaceDesc targetDesc = desc; targetDesc.fFlags = kRenderTarget_GrSurfaceFlag; GrTexture* target = context->textureProvider()->createTexture(targetDesc, SkBudgeted::kYes, nullptr, 0); SkAutoTMalloc<uint32_t> pixels(desc.fWidth * desc.fHeight); memset(pixels.get(), 0xFF, sizeof(uint32_t) * desc.fWidth * desc.fHeight); texture->writePixels(0, 0, desc.fWidth, desc.fHeight, kRGBA_8888_GrPixelConfig, pixels.get()); // Add a pending read to the texture, and then make it available for reuse. context->copySurface(target, texture); texture->unref(); // Create an atlas with parameters that allow it to reuse the texture. GrTextureStripAtlas::Desc atlasDesc; atlasDesc.fContext = context; atlasDesc.fConfig = desc.fConfig; atlasDesc.fWidth = desc.fWidth; atlasDesc.fHeight = desc.fHeight; atlasDesc.fRowHeight = 1; GrTextureStripAtlas* atlas = GrTextureStripAtlas::GetAtlas(atlasDesc); // Write to the atlas' texture. SkImageInfo info = SkImageInfo::MakeN32(desc.fWidth, desc.fHeight, kPremul_SkAlphaType); size_t rowBytes = desc.fWidth * GrBytesPerPixel(desc.fConfig); SkBitmap bitmap; bitmap.allocPixels(info, rowBytes); memset(bitmap.getPixels(), 1, rowBytes * desc.fHeight); int row = atlas->lockRow(bitmap); if (!context->caps()->preferVRAMUseOverFlushes()) REPORTER_ASSERT(reporter, texture == atlas->getTexture()); // The atlas' use of its texture shouldn't change which pixels got copied to the target. SkAutoTMalloc<uint32_t> actualPixels(desc.fWidth * desc.fHeight); bool success = target->readPixels(0, 0, desc.fWidth, desc.fHeight, kRGBA_8888_GrPixelConfig, actualPixels.get()); REPORTER_ASSERT(reporter, success); REPORTER_ASSERT(reporter, !memcmp(pixels.get(), actualPixels.get(), sizeof(uint32_t) * desc.fWidth * desc.fHeight)); target->unref(); atlas->unlockRow(row); }
SkSurface* Request::createGPUSurface() { GrContext* context = fContextFactory->get(GrContextFactory::kNative_GLContextType, GrContextFactory::kNone_GLContextOptions); int maxRTSize = context->caps()->maxRenderTargetSize(); SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize), SkTMin(kImageHeight, maxRTSize), kN32_SkColorType, kPremul_SkAlphaType); uint32_t flags = 0; SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); SkSurface* surface = SkSurface::NewRenderTarget(context, SkBudgeted::kNo, info, 0, &props); return surface; }
DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) { GrContext* context = ctxInfo.fGrContext; GrTextureDesc desc; desc.fHeight = 1; desc.fWidth = 1; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fConfig = kRGBA_8888_GrPixelConfig; SkAutoTUnref<GrTexture> target(context->textureProvider()->createTexture(desc, SkBudgeted::kYes)); if (!target) { ERRORF(reporter, "Could not create render target."); return; } SkAutoTUnref<GrDrawContext> dc(context->drawContext(target->asRenderTarget())); if (!dc) { ERRORF(reporter, "Could not create draw context."); return; } int attribCnt = context->caps()->maxVertexAttributes(); if (!attribCnt) { ERRORF(reporter, "No attributes allowed?!"); return; } context->flush(); context->resetGpuStats(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); #endif SkAutoTUnref<GrDrawBatch> batch; GrPipelineBuilder pb; pb.setRenderTarget(target->asRenderTarget()); // This one should succeed. batch.reset(new Batch(attribCnt)); dc->drawContextPriv().testingOnly_drawBatch(pb, batch); context->flush(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 1); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); #endif context->resetGpuStats(); // This one should fail. batch.reset(new Batch(attribCnt+1)); dc->drawContextPriv().testingOnly_drawBatch(pb, batch); context->flush(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 1); #endif }
// TODO factor this out into functions, also handle CPU path SkSurface* setupSurface(GrContextFactory* factory) { GrContext* context = factory->get(GrContextFactory::kNative_GLContextType, GrContextFactory::kNone_GLContextOptions); int maxRTSize = context->caps()->maxRenderTargetSize(); SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize), SkTMin(kImageHeight, maxRTSize), kN32_SkColorType, kPremul_SkAlphaType); uint32_t flags = 0; SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); SkSurface* surface = SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0, &props); SkASSERT(surface); SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType, GrContextFactory::kNone_GLContextOptions).fGLContext; gl->makeCurrent(); return surface; }
DEF_GPUTEST_FOR_ALL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); sk_sp<GrDrawContext> drawContext(context->newDrawContext(SkBackingFit::kApprox, 1, 1, kRGBA_8888_GrPixelConfig)); if (!drawContext) { ERRORF(reporter, "Could not create draw context."); return; } int attribCnt = context->caps()->maxVertexAttributes(); if (!attribCnt) { ERRORF(reporter, "No attributes allowed?!"); return; } context->flush(); context->resetGpuStats(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); #endif SkAutoTUnref<GrDrawBatch> batch; GrPaint grPaint; // This one should succeed. batch.reset(new Batch(attribCnt)); drawContext->drawContextPriv().testingOnly_drawBatch(grPaint, batch); context->flush(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 1); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); #endif context->resetGpuStats(); // This one should fail. batch.reset(new Batch(attribCnt+1)); drawContext->drawContextPriv().testingOnly_drawBatch(grPaint, batch); context->flush(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 1); #endif }
DEF_GPUTEST_FOR_ALL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext( SkBackingFit::kApprox, 1, 1, kRGBA_8888_GrPixelConfig, nullptr)); if (!renderTargetContext) { ERRORF(reporter, "Could not create render target context."); return; } int attribCnt = context->caps()->maxVertexAttributes(); if (!attribCnt) { ERRORF(reporter, "No attributes allowed?!"); return; } context->flush(); context->resetGpuStats(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); #endif GrPaint grPaint; // This one should succeed. renderTargetContext->priv().testingOnly_addDrawOp(Op::Make(attribCnt)); context->flush(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 1); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); #endif context->resetGpuStats(); renderTargetContext->priv().testingOnly_addDrawOp(Op::Make(attribCnt + 1)); context->flush(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 1); #endif }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(EmptySurfaceSemaphoreTest, reporter, ctxInfo) { GrContext* ctx = ctxInfo.grContext(); if (!ctx->caps()->fenceSyncSupport()) { return; } const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType, kPremul_SkAlphaType); sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, ii, 0, kTopLeft_GrSurfaceOrigin, nullptr)); // Flush surface once without semaphores to make sure there is no peneding IO for it. mainSurface->flush(); GrBackendSemaphore semaphore; GrSemaphoresSubmitted submitted = mainSurface->flushAndSignalSemaphores(1, &semaphore); REPORTER_ASSERT(reporter, GrSemaphoresSubmitted::kYes == submitted); if (kOpenGL_GrBackend == ctxInfo.backend()) { GrGLGpu* gpu = static_cast<GrGLGpu*>(ctx->contextPriv().getGpu()); const GrGLInterface* interface = gpu->glInterface(); GrGLsync sync = semaphore.glSync(); REPORTER_ASSERT(reporter, sync); bool result; GR_GL_CALL_RET(interface, result, IsSync(sync)); REPORTER_ASSERT(reporter, result); } #ifdef SK_VULKAN if (kVulkan_GrBackend == ctxInfo.backend()) { GrVkGpu* gpu = static_cast<GrVkGpu*>(ctx->contextPriv().getGpu()); const GrVkInterface* interface = gpu->vkInterface(); VkDevice device = gpu->device(); VkQueue queue = gpu->queue(); VkCommandPool cmdPool = gpu->cmdPool(); VkCommandBuffer cmdBuffer; // Create Command Buffer const VkCommandBufferAllocateInfo cmdInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType nullptr, // pNext cmdPool, // commandPool VK_COMMAND_BUFFER_LEVEL_PRIMARY, // level 1 // bufferCount }; VkResult err = GR_VK_CALL(interface, AllocateCommandBuffers(device, &cmdInfo, &cmdBuffer)); if (err) { return; } VkCommandBufferBeginInfo cmdBufferBeginInfo; memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo)); cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cmdBufferBeginInfo.pNext = nullptr; cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; cmdBufferBeginInfo.pInheritanceInfo = nullptr; GR_VK_CALL_ERRCHECK(interface, BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo)); GR_VK_CALL_ERRCHECK(interface, EndCommandBuffer(cmdBuffer)); VkFenceCreateInfo fenceInfo; VkFence fence; memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo)); fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; err = GR_VK_CALL(interface, CreateFence(device, &fenceInfo, nullptr, &fence)); SkASSERT(!err); VkPipelineStageFlags waitStages = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; VkSubmitInfo submitInfo; memset(&submitInfo, 0, sizeof(VkSubmitInfo)); submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 1; VkSemaphore vkSem = semaphore.vkSemaphore(); submitInfo.pWaitSemaphores = &vkSem; submitInfo.pWaitDstStageMask = &waitStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &cmdBuffer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; GR_VK_CALL_ERRCHECK(interface, QueueSubmit(queue, 1, &submitInfo, fence)); err = GR_VK_CALL(interface, WaitForFences(device, 1, &fence, true, 3000000000)); REPORTER_ASSERT(reporter, err != VK_TIMEOUT); GR_VK_CALL(interface, DestroyFence(device, fence, nullptr)); GR_VK_CALL(interface, DestroySemaphore(device, vkSem, nullptr)); // If the above test fails the wait semaphore will never be signaled which can cause the // device to hang when tearing down (even if just tearing down GL). So we Fail here to // kill things. if (err == VK_TIMEOUT) { SK_ABORT("Waiting on semaphore indefinitely"); } } #endif }
void surface_semaphore_test(skiatest::Reporter* reporter, const sk_gpu_test::ContextInfo& mainInfo, const sk_gpu_test::ContextInfo& childInfo1, const sk_gpu_test::ContextInfo& childInfo2, bool flushContext) { GrContext* mainCtx = mainInfo.grContext(); if (!mainCtx->caps()->fenceSyncSupport()) { return; } const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType, kPremul_SkAlphaType); sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(mainCtx, SkBudgeted::kNo, ii, 0, kTopLeft_GrSurfaceOrigin, nullptr)); SkCanvas* mainCanvas = mainSurface->getCanvas(); mainCanvas->clear(SK_ColorBLUE); SkAutoTArray<GrBackendSemaphore> semaphores(2); #ifdef SK_VULKAN if (kVulkan_GrBackend == mainInfo.backend()) { // Initialize the secondary semaphore instead of having Ganesh create one internally GrVkGpu* gpu = static_cast<GrVkGpu*>(mainCtx->contextPriv().getGpu()); const GrVkInterface* interface = gpu->vkInterface(); VkDevice device = gpu->device(); VkSemaphore vkSem; VkSemaphoreCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0; GR_VK_CALL_ERRCHECK(interface, CreateSemaphore(device, &createInfo, nullptr, &vkSem)); semaphores[1].initVulkan(vkSem); } #endif if (flushContext) { mainCtx->flushAndSignalSemaphores(2, semaphores.get()); } else { mainSurface->flushAndSignalSemaphores(2, semaphores.get()); } sk_sp<SkImage> mainImage = mainSurface->makeImageSnapshot(); GrBackendTexture backendTexture = mainImage->getBackendTexture(false); draw_child(reporter, childInfo1, backendTexture, semaphores[0]); #ifdef SK_VULKAN if (kVulkan_GrBackend == mainInfo.backend()) { // In Vulkan we need to make sure we are sending the correct VkImageLayout in with the // backendImage. After the first child draw the layout gets changed to SHADER_READ, so // we just manually set that here. GrVkImageInfo vkInfo; SkAssertResult(backendTexture.getVkImageInfo(&vkInfo)); vkInfo.updateImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } #endif draw_child(reporter, childInfo2, backendTexture, semaphores[1]); }
static GrTexture* copy_on_gpu(GrTexture* inputTexture, const SkIRect* subset, const CopyParams& copyParams) { SkASSERT(!subset || !subset->isEmpty()); GrContext* context = inputTexture->getContext(); SkASSERT(context); const GrCaps* caps = context->caps(); // Either it's a cache miss or the original wasn't cached to begin with. GrSurfaceDesc rtDesc = inputTexture->desc(); rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; rtDesc.fWidth = copyParams.fWidth; rtDesc.fHeight = copyParams.fHeight; rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig); // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise, // fail. if (!caps->isConfigRenderable(rtDesc.fConfig, false)) { if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) { if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { rtDesc.fConfig = kAlpha_8_GrPixelConfig; } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else if (kRGB_GrColorComponentFlags == (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else { return nullptr; } } SkAutoTUnref<GrTexture> copy(context->textureProvider()->createTexture(rtDesc, SkBudgeted::kYes)); if (!copy) { return nullptr; } // TODO: If no scaling is being performed then use copySurface. GrPaint paint; paint.setGammaCorrect(true); // TODO: Initializing these values for no reason cause the compiler is complaining SkScalar sx = 0.f; SkScalar sy = 0.f; if (subset) { sx = 1.f / inputTexture->width(); sy = 1.f / inputTexture->height(); } if (copyParams.fFilter != GrTextureParams::kNone_FilterMode && subset && (subset->width() != copyParams.fWidth || subset->height() != copyParams.fHeight)) { SkRect domain; domain.fLeft = (subset->fLeft + 0.5f) * sx; domain.fTop = (subset->fTop + 0.5f)* sy; domain.fRight = (subset->fRight - 0.5f) * sx; domain.fBottom = (subset->fBottom - 0.5f) * sy; // This would cause us to read values from outside the subset. Surely, the caller knows // better! SkASSERT(copyParams.fFilter != GrTextureParams::kMipMap_FilterMode); paint.addColorFragmentProcessor( GrTextureDomainEffect::Make(inputTexture, SkMatrix::I(), domain, GrTextureDomain::kClamp_Mode, copyParams.fFilter)); } else { GrTextureParams params(SkShader::kClamp_TileMode, copyParams.fFilter); paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); SkRect localRect; if (subset) { localRect = SkRect::Make(*subset); localRect.fLeft *= sx; localRect.fTop *= sy; localRect.fRight *= sx; localRect.fBottom *= sy; } else { localRect = SkRect::MakeWH(1.f, 1.f); } sk_sp<GrDrawContext> drawContext(context->drawContext(sk_ref_sp(copy->asRenderTarget()))); if (!drawContext) { return nullptr; } SkRect dstRect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); drawContext->fillRectToRect(GrNoClip(), paint, SkMatrix::I(), dstRect, localRect); return copy.release(); }
// creates a new texture that is the input texture scaled up. If optionalKey is valid it will be // set on the new texture. stretch controls whether the scaling is done using nearest or bilerp // filtering and the size to stretch the texture to. GrTexture* stretch_texture(GrTexture* inputTexture, const SkGrStretch& stretch, SkPixelRef* pixelRef, const GrUniqueKey& optionalKey) { SkASSERT(SkGrStretch::kNone_Type != stretch.fType); GrContext* context = inputTexture->getContext(); SkASSERT(context); const GrCaps* caps = context->caps(); // Either it's a cache miss or the original wasn't cached to begin with. GrSurfaceDesc rtDesc = inputTexture->desc(); rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag; rtDesc.fWidth = stretch.fWidth; rtDesc.fHeight = stretch.fHeight; rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig); // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise, // fail. if (!caps->isConfigRenderable(rtDesc.fConfig, false)) { if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) { if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { rtDesc.fConfig = kAlpha_8_GrPixelConfig; } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else if (kRGB_GrColorComponentFlags == (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { rtDesc.fConfig = kSkia8888_GrPixelConfig; } else { return nullptr; } } else { return nullptr; } } SkAutoTUnref<GrTexture> stretched(GrCreateTextureForPixels(context, optionalKey, rtDesc, pixelRef, nullptr,0)); if (!stretched) { return nullptr; } GrPaint paint; // If filtering is not desired then we want to ensure all texels in the resampled image are // copies of texels from the original. GrTextureParams params(SkShader::kClamp_TileMode, SkGrStretch::kBilerp_Type == stretch.fType ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode); paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); SkRect localRect = SkRect::MakeWH(1.f, 1.f); SkAutoTUnref<GrDrawContext> drawContext(context->drawContext()); if (!drawContext) { return nullptr; } drawContext->drawNonAARectToRect(stretched->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect); return stretched.detach(); }
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SRGBMipMaps, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); if (!context->caps()->srgbSupport()) { return; } const int rtS = 16; const int texS = rtS * 2; // Fill texture with a dither of black and 60% sRGB (~ 32.5% linear) gray. Although there is // only one likely failure mode (doing a direct downsample of the sRGB values), this pattern // maximizes the minimum error across all three conceivable failure modes: // 1) Likely incorrect: // (A + B) / 2 // 2) No input decode, decode output: // linear_to_srgb((A + B) / 2) // 3) Decode input, no output encode: // (srgb_to_linear(A) + srgb_to_linear(B)) / 2 const U8CPU srgb60 = sk_float_round2int(0.6f * 255.0f); static const SkPMColor colors[2] = { SkPackARGB32(0xFF, srgb60, srgb60, srgb60), SkPackARGB32(0xFF, 0x00, 0x00, 0x00) }; uint32_t texData[texS * texS]; for (int y = 0; y < texS; ++y) { for (int x = 0; x < texS; ++x) { texData[y * texS + x] = colors[(x + y) % 2]; } } // We can be pretty generous with the error detection, thanks to the choice of input. // The closest likely failure mode is off by > 0.1, so anything that encodes within // 10/255 of optimal is more than good enough for this test. const U8CPU expectedSRGB = sk_float_round2int( linear_to_srgb(srgb_to_linear(srgb60 / 255.0f) / 2.0f) * 255.0f); const U8CPU expectedLinear = srgb60 / 2; const U8CPU error = 10; // Create our test texture GrSurfaceDesc desc; desc.fFlags = kNone_GrSurfaceFlags; desc.fConfig = kSRGBA_8888_GrPixelConfig; desc.fWidth = texS; desc.fHeight = texS; GrTextureProvider* texProvider = context->textureProvider(); SkAutoTUnref<GrTexture> texture(texProvider->createTexture(desc, SkBudgeted::kNo, texData, 0)); // Create two render target contexts (L32 and S32) sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); sk_sp<GrRenderTargetContext> l32RenderTargetContext = context->makeRenderTargetContext( SkBackingFit::kExact, rtS, rtS, kRGBA_8888_GrPixelConfig, nullptr); sk_sp<GrRenderTargetContext> s32RenderTargetContext = context->makeRenderTargetContext( SkBackingFit::kExact, rtS, rtS, kSRGBA_8888_GrPixelConfig, std::move(srgbColorSpace)); SkRect rect = SkRect::MakeWH(SkIntToScalar(rtS), SkIntToScalar(rtS)); GrNoClip noClip; GrPaint paint; paint.setPorterDuffXPFactory(SkBlendMode::kSrc); GrTextureParams mipMapParams(SkShader::kRepeat_TileMode, GrTextureParams::kMipMap_FilterMode); paint.addColorTextureProcessor(texture, nullptr, SkMatrix::MakeScale(0.5f), mipMapParams); // 1) Draw texture to S32 surface (should generate/use sRGB mips) paint.setGammaCorrect(true); s32RenderTargetContext->drawRect(noClip, paint, SkMatrix::I(), rect); read_and_check_pixels(reporter, s32RenderTargetContext->asTexture().get(), expectedSRGB, error, "first render of sRGB"); // 2) Draw texture to L32 surface (should generate/use linear mips) paint.setGammaCorrect(false); l32RenderTargetContext->drawRect(noClip, paint, SkMatrix::I(), rect); read_and_check_pixels(reporter, l32RenderTargetContext->asTexture().get(), expectedLinear, error, "re-render as linear"); // 3) Go back to sRGB paint.setGammaCorrect(true); s32RenderTargetContext->drawRect(noClip, paint, SkMatrix::I(), rect); read_and_check_pixels(reporter, s32RenderTargetContext->asTexture().get(), expectedSRGB, error, "re-render as sRGB"); }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ApplyGamma, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); static const int kW = 256; static const int kH = 256; static const size_t kRowBytes = sizeof(uint32_t) * kW; GrSurfaceDesc baseDesc; baseDesc.fConfig = kRGBA_8888_GrPixelConfig; baseDesc.fWidth = kW; baseDesc.fHeight = kH; const SkImageInfo ii = SkImageInfo::MakeN32Premul(kW, kH); SkAutoTMalloc<uint32_t> srcPixels(kW * kH); for (int y = 0; y < kH; ++y) { for (int x = 0; x < kW; ++x) { srcPixels.get()[y*kW+x] = SkPreMultiplyARGB(x, y, x, 0xFF); } } SkBitmap bm; bm.installPixels(ii, srcPixels.get(), kRowBytes); SkAutoTMalloc<uint32_t> read(kW * kH); // We allow more error on GPUs with lower precision shader variables. float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f : 0.5f; for (auto toSRGB : { false, true }) { sk_sp<SkSurface> dst(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii)); if (!dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } SkCanvas* dstCanvas = dst->getCanvas(); dstCanvas->clear(SK_ColorRED); dstCanvas->flush(); SkPaint gammaPaint; gammaPaint.setBlendMode(SkBlendMode::kSrc); gammaPaint.setColorFilter(toSRGB ? SkColorFilter::MakeLinearToSRGBGamma() : SkColorFilter::MakeSRGBToLinearGamma()); dstCanvas->drawBitmap(bm, 0, 0, &gammaPaint); dstCanvas->flush(); sk_memset32(read.get(), 0, kW * kH); if (!dstCanvas->readPixels(ii, read.get(), kRowBytes, 0, 0)) { ERRORF(reporter, "Error calling readPixels"); continue; } bool abort = false; // Validate that pixels were copied/transformed correctly. for (int y = 0; y < kH && !abort; ++y) { for (int x = 0; x < kW && !abort; ++x) { uint32_t r = read.get()[y * kW + x]; uint32_t s = srcPixels.get()[y * kW + x]; uint32_t expected; if (!check_gamma(s, r, toSRGB, error, &expected)) { ERRORF(reporter, "Expected dst %d,%d to contain 0x%08x " "from src 0x%08x and mode %s. Got %08x", x, y, expected, s, toSRGB ? "ToSRGB" : "ToLinear", r); abort = true; break; } } } } }