sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context, const GrBackendTexture& tex, GrSurfaceOrigin origin, int sampleCnt, sk_sp<SkColorSpace> colorSpace, const SkSurfaceProps* props) { if (!context) { return nullptr; } if (!SkSurface_Gpu::Valid(context, tex.config(), colorSpace.get())) { return nullptr; } sampleCnt = SkTMax(1, sampleCnt); sk_sp<GrRenderTargetContext> rtc( context->contextPriv().makeBackendTextureAsRenderTargetRenderTargetContext( tex, origin, sampleCnt, std::move(colorSpace), props)); if (!rtc) { return nullptr; } sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), tex.width(), tex.height(), SkGpuDevice::kUninit_InitContents)); if (!device) { return nullptr; } return sk_make_sp<SkSurface_Gpu>(std::move(device)); }
void GrMockGpu::deleteBackendTexture(const GrBackendTexture& tex) { SkASSERT(GrBackendApi::kMock == tex.backend()); GrMockTextureInfo info; if (tex.getMockTextureInfo(&info)) { fOutstandingTestingOnlyTextureIDs.remove(info.fID); } }
bool GrMockGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const { SkASSERT(GrBackendApi::kMock == tex.backend()); GrMockTextureInfo info; if (!tex.getMockTextureInfo(&info)) { return false; } return fOutstandingTestingOnlyTextureIDs.contains(info.fID); }
sk_sp<GrRenderTarget> GrGpu::wrapBackendTextureAsRenderTarget(const GrBackendTexture& tex, int sampleCnt) { if (0 == this->caps()->getRenderTargetSampleCount(sampleCnt, tex.config())) { return nullptr; } int maxSize = this->caps()->maxTextureSize(); if (tex.width() > maxSize || tex.height() > maxSize) { return nullptr; } this->handleDirtyContext(); return this->onWrapBackendTextureAsRenderTarget(tex, sampleCnt); }
sk_sp<GrTexture> GrMockGpu::onWrapBackendTexture(const GrBackendTexture& tex, GrWrapOwnership ownership, GrWrapCacheable wrapType, GrIOType ioType) { GrSurfaceDesc desc; desc.fWidth = tex.width(); desc.fHeight = tex.height(); GrMockTextureInfo info; SkAssertResult(tex.getMockTextureInfo(&info)); desc.fConfig = info.fConfig; GrMipMapsStatus mipMapsStatus = tex.hasMipMaps() ? GrMipMapsStatus::kValid : GrMipMapsStatus::kNotAllocated; return sk_sp<GrTexture>(new GrMockTexture(this, desc, mipMapsStatus, info, wrapType, ioType)); }
void wrap_tex_test(skiatest::Reporter* reporter, GrContext* context) { GrVkGpu* gpu = static_cast<GrVkGpu*>(context->contextPriv().getGpu()); GrBackendTexture origBackendTex = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig, false, GrMipMapped::kNo); const GrVkImageInfo* imageInfo = origBackendTex.getVkImageInfo(); sk_sp<GrTexture> tex = gpu->wrapBackendTexture(origBackendTex, kBorrow_GrWrapOwnership); REPORTER_ASSERT(reporter, tex); // image is null { GrVkImageInfo backendCopy = *imageInfo; backendCopy.fImage = VK_NULL_HANDLE; GrBackendTexture backendTex = GrBackendTexture(kW, kH, backendCopy); tex = gpu->wrapBackendTexture(backendTex, kBorrow_GrWrapOwnership); REPORTER_ASSERT(reporter, !tex); tex = gpu->wrapBackendTexture(backendTex, kAdopt_GrWrapOwnership); REPORTER_ASSERT(reporter, !tex); } // alloc is null { GrVkImageInfo backendCopy = *imageInfo; backendCopy.fAlloc = GrVkAlloc(); GrBackendTexture backendTex = GrBackendTexture(kW, kH, backendCopy); tex = gpu->wrapBackendTexture(backendTex, kBorrow_GrWrapOwnership); REPORTER_ASSERT(reporter, !tex); tex = gpu->wrapBackendTexture(backendTex, kAdopt_GrWrapOwnership); REPORTER_ASSERT(reporter, !tex); } // check adopt creation { GrVkImageInfo backendCopy = *imageInfo; GrBackendTexture backendTex = GrBackendTexture(kW, kH, backendCopy); tex = gpu->wrapBackendTexture(backendTex, kAdopt_GrWrapOwnership); REPORTER_ASSERT(reporter, tex); } gpu->deleteTestingOnlyBackendTexture(&origBackendTex, true); }
sk_sp<GrTexture> GrGpu::wrapBackendTexture(const GrBackendTexture& backendTex, GrWrapOwnership ownership) { this->handleDirtyContext(); if (!this->caps()->isConfigTexturable(backendTex.config())) { return nullptr; } if (backendTex.width() > this->caps()->maxTextureSize() || backendTex.height() > this->caps()->maxTextureSize()) { return nullptr; } sk_sp<GrTexture> tex = this->onWrapBackendTexture(backendTex, ownership); if (tex && !backendTex.hasMipMaps()) { // Ganesh will not ever allocate mipmaps for a wrapped resource. By setting this flag here, // it will be propagated to any proxy that wraps this texture. tex->texturePriv().setDoesNotSupportMipMaps(); } return tex; }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReimportImageTextureWithMipLevels, reporter, ctxInfo) { auto* ctx = ctxInfo.grContext(); if (!ctx->priv().caps()->mipMapSupport()) { return; } static constexpr auto kCreateWithMipMaps = true; auto surf = SkSurface::MakeRenderTarget( ctx, SkBudgeted::kYes, SkImageInfo::Make(100, 100, kRGBA_8888_SkColorType, kPremul_SkAlphaType), 1, kTopLeft_GrSurfaceOrigin, nullptr, kCreateWithMipMaps); if (!surf) { return; } surf->getCanvas()->drawColor(SK_ColorDKGRAY); auto img = surf->makeImageSnapshot(); if (!img) { return; } surf.reset(); GrBackendTexture btex; SkImage::BackendTextureReleaseProc texRelease; if (!SkImage::MakeBackendTextureFromSkImage(ctx, std::move(img), &btex, &texRelease)) { // Not all backends support stealing textures yet. // ERRORF(reporter, "Could not turn image into texture"); return; } REPORTER_ASSERT(reporter, btex.hasMipMaps()); // Reimport the texture as an image and perform a downsampling draw with medium quality which // should use the upper MIP levels. img = SkImage::MakeFromTexture(ctx, btex, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); const auto singlePixelInfo = SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); surf = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, singlePixelInfo, 1, kTopLeft_GrSurfaceOrigin, nullptr); SkPaint paint; paint.setFilterQuality(kMedium_SkFilterQuality); surf->getCanvas()->drawImageRect(img, SkRect::MakeWH(1, 1), &paint); uint32_t pixel; surf->readPixels(singlePixelInfo, &pixel, sizeof(uint32_t), 0, 0); REPORTER_ASSERT(reporter, pixel == SkPreMultiplyColor(SK_ColorDKGRAY)); img.reset(); texRelease(btex); }
sk_sp<GrRenderTarget> GrMockGpu::onWrapBackendTextureAsRenderTarget(const GrBackendTexture& tex, int sampleCnt) { GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = tex.width(); desc.fHeight = tex.height(); GrMockTextureInfo texInfo; SkAssertResult(tex.getMockTextureInfo(&texInfo)); desc.fConfig = texInfo.fConfig; desc.fSampleCnt = sampleCnt; GrMockRenderTargetInfo rtInfo; rtInfo.fConfig = texInfo.fConfig; // The client gave us the texture ID but we supply the render target ID. rtInfo.fID = NextInternalRenderTargetID(); return sk_sp<GrRenderTarget>( new GrMockRenderTarget(this, GrMockRenderTarget::kWrapped, desc, rtInfo)); }
static sk_sp<GrTextureProxy> create_wrapped_backend(GrContext* context, SkBackingFit fit, sk_sp<GrTexture>* backingSurface) { GrProxyProvider* proxyProvider = context->priv().proxyProvider(); GrResourceProvider* resourceProvider = context->priv().resourceProvider(); const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags); *backingSurface = resourceProvider->createTexture(desc, SkBudgeted::kNo, GrResourceProvider::Flags::kNoPendingIO); if (!(*backingSurface)) { return nullptr; } GrBackendTexture backendTex = (*backingSurface)->getBackendTexture(); backendTex.setPixelConfig(desc.fConfig); return proxyProvider->wrapBackendTexture(backendTex, kBottomLeft_GrSurfaceOrigin, kBorrow_GrWrapOwnership, GrWrapCacheable::kYes, kRead_GrIOType); }
sk_sp<GrTexture> GrMockGpu::onWrapRenderableBackendTexture(const GrBackendTexture& tex, int sampleCnt, GrWrapOwnership ownership) { GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = tex.width(); desc.fHeight = tex.height(); GrMockTextureInfo texInfo; SkAssertResult(tex.getMockTextureInfo(&texInfo)); desc.fConfig = texInfo.fConfig; GrMipMapsStatus mipMapsStatus = tex.hasMipMaps() ? GrMipMapsStatus::kValid : GrMipMapsStatus::kNotAllocated; GrMockRenderTargetInfo rtInfo; rtInfo.fConfig = texInfo.fConfig; // The client gave us the texture ID but we supply the render target ID. rtInfo.fID = NextInternalRenderTargetID(); return sk_sp<GrTexture>( new GrMockTextureRenderTarget(this, desc, mipMapsStatus, texInfo, rtInfo)); }
sk_sp<GrTexture> GrGpu::wrapRenderableBackendTexture(const GrBackendTexture& backendTex, int sampleCnt, GrWrapOwnership ownership) { this->handleDirtyContext(); if (sampleCnt < 1) { return nullptr; } if (!this->caps()->isConfigTexturable(backendTex.config()) || !this->caps()->getRenderTargetSampleCount(sampleCnt, backendTex.config())) { return nullptr; } if (backendTex.width() > this->caps()->maxRenderTargetSize() || backendTex.height() > this->caps()->maxRenderTargetSize()) { return nullptr; } sk_sp<GrTexture> tex = this->onWrapRenderableBackendTexture(backendTex, sampleCnt, ownership); if (tex && !backendTex.hasMipMaps()) { // Ganesh will not ever allocate mipmaps for a wrapped resource. By setting this flag here, // it will be propagated to any proxy that wraps this texture. tex->texturePriv().setDoesNotSupportMipMaps(); } SkASSERT(!tex || tex->asRenderTarget()); return tex; }
void wrap_rt_test(skiatest::Reporter* reporter, GrContext* context) { GrVkGpu* gpu = static_cast<GrVkGpu*>(context->contextPriv().getGpu()); GrBackendTexture origBackendTex = gpu->createTestingOnlyBackendTexture(nullptr, kW, kH, kPixelConfig, true, GrMipMapped::kNo); const GrVkImageInfo* imageInfo = origBackendTex.getVkImageInfo(); GrBackendRenderTarget origBackendRT(kW, kH, 1, 0, *imageInfo); sk_sp<GrRenderTarget> rt = gpu->wrapBackendRenderTarget(origBackendRT); REPORTER_ASSERT(reporter, rt); // image is null { GrVkImageInfo backendCopy = *imageInfo; backendCopy.fImage = VK_NULL_HANDLE; GrBackendRenderTarget backendRT(kW, kH, 1, 0, backendCopy); rt = gpu->wrapBackendRenderTarget(backendRT); REPORTER_ASSERT(reporter, !rt); } // alloc is null { GrVkImageInfo backendCopy = *imageInfo; backendCopy.fAlloc = GrVkAlloc(); // can wrap null alloc GrBackendRenderTarget backendRT(kW, kH, 1, 0, backendCopy); rt = gpu->wrapBackendRenderTarget(backendRT); REPORTER_ASSERT(reporter, rt); } // When we wrapBackendRenderTarget it is always borrowed, so we must make sure to free the // resource when we're done. gpu->deleteTestingOnlyBackendTexture(&origBackendTex); }
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]); }
sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::makeProxy(GrContext* context) { if (context->abandoned()) { return nullptr; } GrPixelConfig pixelConfig; GrBackendFormat backendFormat = get_backend_format(context->contextPriv().getBackend(), fBufferFormat); if (!context->contextPriv().caps()->getConfigFromBackendFormat( backendFormat, this->getInfo().colorType(), &pixelConfig)) { return nullptr; } int width = this->getInfo().width(); int height = this->getInfo().height(); GrSurfaceDesc desc; desc.fWidth = width; desc.fHeight = height; desc.fConfig = pixelConfig; GrTextureType textureType = GrTextureType::k2D; if (context->contextPriv().getBackend() == kOpenGL_GrBackend) { textureType = GrTextureType::kExternal; } auto proxyProvider = context->contextPriv().proxyProvider(); AHardwareBuffer* hardwareBuffer = fHardwareBuffer; AHardwareBuffer_acquire(hardwareBuffer); const bool isProtectedContent = fIsProtectedContent; sk_sp<GrTextureProxy> texProxy = proxyProvider->createLazyProxy( [context, hardwareBuffer, width, height, pixelConfig, isProtectedContent, backendFormat] (GrResourceProvider* resourceProvider) { if (!resourceProvider) { AHardwareBuffer_release(hardwareBuffer); return sk_sp<GrTexture>(); } DeleteImageProc deleteImageProc = nullptr; DeleteImageCtx deleteImageCtx = nullptr; GrBackendTexture backendTex = make_backend_texture(context, hardwareBuffer, width, height, pixelConfig, &deleteImageProc, &deleteImageCtx, isProtectedContent, backendFormat); if (!backendTex.isValid()) { return sk_sp<GrTexture>(); } SkASSERT(deleteImageProc && deleteImageCtx); backendTex.fConfig = pixelConfig; sk_sp<GrTexture> tex = resourceProvider->wrapBackendTexture(backendTex); if (!tex) { deleteImageProc(deleteImageCtx); return sk_sp<GrTexture>(); } if (deleteImageProc) { sk_sp<GrReleaseProcHelper> releaseProcHelper( new GrReleaseProcHelper(deleteImageProc, deleteImageCtx)); tex->setRelease(releaseProcHelper); } return tex; }, desc, fSurfaceOrigin, GrMipMapped::kNo, textureType, SkBackingFit::kExact, SkBudgeted::kNo); if (!texProxy) { AHardwareBuffer_release(hardwareBuffer); } return texProxy; }
DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkImageLayoutTest, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); GrVkGpu* gpu = static_cast<GrVkGpu*>(context->contextPriv().getGpu()); GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(nullptr, 1, 1, kRGBA_8888_GrPixelConfig, false, GrMipMapped::kNo); REPORTER_ASSERT(reporter, backendTex.isValid()); GrVkImageInfo info; REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info)); VkImageLayout initLayout = info.fImageLayout; // Verify that setting that layout via a copy of a backendTexture is reflected in all the // backendTextures. GrBackendTexture backendTexCopy = backendTex; REPORTER_ASSERT(reporter, backendTexCopy.getVkImageInfo(&info)); REPORTER_ASSERT(reporter, initLayout == info.fImageLayout); backendTexCopy.setVkImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info)); REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == info.fImageLayout); REPORTER_ASSERT(reporter, backendTexCopy.getVkImageInfo(&info)); REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == info.fImageLayout); // Setting back the layout since we didn't actually change it backendTex.setVkImageLayout(initLayout); sk_sp<SkImage> wrappedImage = SkImage::MakeFromTexture(context, backendTex, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); REPORTER_ASSERT(reporter, wrappedImage.get()); sk_sp<GrTextureProxy> texProxy = as_IB(wrappedImage)->asTextureProxyRef(); REPORTER_ASSERT(reporter, texProxy.get()); REPORTER_ASSERT(reporter, texProxy->priv().isInstantiated()); GrTexture* texture = texProxy->priv().peekTexture(); REPORTER_ASSERT(reporter, texture); // Verify that modifying the layout via the GrVkTexture is reflected in the GrBackendTexture GrVkTexture* vkTexture = static_cast<GrVkTexture*>(texture); REPORTER_ASSERT(reporter, initLayout == vkTexture->currentLayout()); vkTexture->updateImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info)); REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == info.fImageLayout); GrBackendTexture backendTexImage = wrappedImage->getBackendTexture(false); REPORTER_ASSERT(reporter, backendTexImage.getVkImageInfo(&info)); REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == info.fImageLayout); // Verify that modifying the layout via the GrBackendTexutre is reflected in the GrVkTexture backendTexImage.setVkImageLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == vkTexture->currentLayout()); #ifdef SK_SUPPORT_LEGACY_BACKEND_OBJECTS // Verify that modifying the layout via the old textureHandle sitll works in is reflected in the // GrVkTexture and GrBackendTexture. GrVkImageInfo* backendInfo = (GrVkImageInfo*)wrappedImage->getTextureHandle(false); REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == backendInfo->fImageLayout); backendInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == vkTexture->currentLayout()); REPORTER_ASSERT(reporter, backendTexImage.getVkImageInfo(&info)); REPORTER_ASSERT(reporter, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == info.fImageLayout); #endif vkTexture->updateImageLayout(initLayout); REPORTER_ASSERT(reporter, backendTex.getVkImageInfo(&info)); REPORTER_ASSERT(reporter, initLayout == info.fImageLayout); REPORTER_ASSERT(reporter, backendTexCopy.getVkImageInfo(&info)); REPORTER_ASSERT(reporter, initLayout == info.fImageLayout); REPORTER_ASSERT(reporter, backendTexImage.getVkImageInfo(&info)); REPORTER_ASSERT(reporter, initLayout == info.fImageLayout); // Check that we can do things like assigning the backend texture to invalid one, assign an // invalid one, assin a backend texture to inself etc. Success here is that we don't hit any of // our ref counting asserts. REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(backendTex, backendTexCopy)); GrBackendTexture invalidTexture; REPORTER_ASSERT(reporter, !invalidTexture.isValid()); REPORTER_ASSERT(reporter, !GrBackendTexture::TestingOnly_Equals(invalidTexture, backendTexCopy)); backendTexCopy = invalidTexture; REPORTER_ASSERT(reporter, !backendTexCopy.isValid()); REPORTER_ASSERT(reporter, !GrBackendTexture::TestingOnly_Equals(invalidTexture, backendTexCopy)); invalidTexture = backendTex; REPORTER_ASSERT(reporter, invalidTexture.isValid()); REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(invalidTexture, backendTex)); invalidTexture = static_cast<decltype(invalidTexture)&>(invalidTexture); REPORTER_ASSERT(reporter, invalidTexture.isValid()); REPORTER_ASSERT(reporter, GrBackendTexture::TestingOnly_Equals(invalidTexture, invalidTexture)); gpu->deleteTestingOnlyBackendTexture(backendTex); }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyTest, reporter, ctxInfo) { GrProxyProvider* proxyProvider = ctxInfo.grContext()->priv().proxyProvider(); GrContext* context = ctxInfo.grContext(); GrResourceProvider* resourceProvider = context->priv().resourceProvider(); GrGpu* gpu = context->priv().getGpu(); const GrCaps& caps = *context->priv().caps(); static const int kWidthHeight = 100; for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) { for (auto colorType : { kAlpha_8_SkColorType, kRGBA_8888_SkColorType, kRGBA_1010102_SkColorType }) { // External on-screen render target. // Tests wrapBackendRenderTarget with a GrBackendRenderTarget // Our test-only function that creates a backend render target doesn't currently support // sample counts :(. if (ctxInfo.grContext()->colorTypeSupportedAsSurface(colorType)) { GrBackendRenderTarget backendRT = gpu->createTestingOnlyBackendRenderTarget( kWidthHeight, kWidthHeight, SkColorTypeToGrColorType(colorType)); sk_sp<GrSurfaceProxy> sProxy( proxyProvider->wrapBackendRenderTarget(backendRT, origin, nullptr, nullptr)); check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendRT.pixelConfig(), SkBudgeted::kNo); static constexpr int kExpectedNumSamples = 1; check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), kExpectedNumSamples, SkBackingFit::kExact, caps.maxWindowRectangles()); gpu->deleteTestingOnlyBackendRenderTarget(backendRT); } for (auto numSamples : {1, 4}) { GrPixelConfig config = SkColorType2GrPixelConfig(colorType); SkASSERT(kUnknown_GrPixelConfig != config); int supportedNumSamples = caps.getRenderTargetSampleCount(numSamples, config); if (!supportedNumSamples) { continue; } // Test wrapping FBO 0 (with made up properties). This tests sample count and the // special case where FBO 0 doesn't support window rectangles. if (GrBackendApi::kOpenGL == ctxInfo.backend()) { GrGLFramebufferInfo fboInfo; fboInfo.fFBOID = 0; fboInfo.fFormat = GR_GL_RGBA8; static constexpr int kStencilBits = 8; GrBackendRenderTarget backendRT(kWidthHeight, kWidthHeight, numSamples, kStencilBits, fboInfo); backendRT.setPixelConfig(config); sk_sp<GrSurfaceProxy> sProxy( proxyProvider->wrapBackendRenderTarget(backendRT, origin, nullptr, nullptr)); check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendRT.pixelConfig(), SkBudgeted::kNo); check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), supportedNumSamples, SkBackingFit::kExact, 0); } // Tests wrapBackendRenderTarget with a GrBackendTexture { GrBackendTexture backendTex = context->createBackendTexture(kWidthHeight, kWidthHeight, colorType, GrMipMapped::kNo, GrRenderable::kYes); sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapBackendTextureAsRenderTarget( backendTex, origin, supportedNumSamples); if (!sProxy) { context->deleteBackendTexture(backendTex); continue; // This can fail on Mesa } check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendTex.pixelConfig(), SkBudgeted::kNo); check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), supportedNumSamples, SkBackingFit::kExact, caps.maxWindowRectangles()); context->deleteBackendTexture(backendTex); } // Tests wrapBackendTexture that is only renderable { GrBackendTexture backendTex = context->createBackendTexture(kWidthHeight, kWidthHeight, colorType, GrMipMapped::kNo, GrRenderable::kYes); sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapRenderableBackendTexture( backendTex, origin, supportedNumSamples, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, nullptr, nullptr); if (!sProxy) { context->deleteBackendTexture(backendTex); continue; // This can fail on Mesa } check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendTex.pixelConfig(), SkBudgeted::kNo); check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), supportedNumSamples, SkBackingFit::kExact, caps.maxWindowRectangles()); context->deleteBackendTexture(backendTex); } // Tests wrapBackendTexture that is only textureable { // Internal offscreen texture GrBackendTexture backendTex = context->createBackendTexture(kWidthHeight, kWidthHeight, colorType, GrMipMapped::kNo, GrRenderable::kNo); sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapBackendTexture( backendTex, origin, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, kRead_GrIOType); if (!sProxy) { context->deleteBackendTexture(backendTex); continue; } check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendTex.pixelConfig(), SkBudgeted::kNo); check_texture(reporter, resourceProvider, sProxy->asTextureProxy(), SkBackingFit::kExact); context->deleteBackendTexture(backendTex); } } } } }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(PromiseImageTest, reporter, ctxInfo) { const int kWidth = 10; const int kHeight = 10; GrContext* ctx = ctxInfo.grContext(); GrGpu* gpu = ctx->contextPriv().getGpu(); for (bool releaseImageEarly : {true, false}) { GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture( nullptr, kWidth, kHeight, kRGBA_8888_GrPixelConfig, true, GrMipMapped::kNo); REPORTER_ASSERT(reporter, backendTex.isValid()); GrBackendFormat backendFormat = backendTex.format(); REPORTER_ASSERT(reporter, backendFormat.isValid()); PromiseTextureChecker promiseChecker(backendTex); GrSurfaceOrigin texOrigin = kTopLeft_GrSurfaceOrigin; sk_sp<SkImage> refImg( SkImage_Gpu::MakePromiseTexture(ctx, backendFormat, kWidth, kHeight, GrMipMapped::kNo, texOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr, PromiseTextureChecker::Fulfill, PromiseTextureChecker::Release, PromiseTextureChecker::Done, &promiseChecker)); SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight); sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info); SkCanvas* canvas = surface->getCanvas(); int expectedFulfillCnt = 0; int expectedReleaseCnt = 0; int expectedDoneCnt = 0; canvas->drawImage(refImg, 0, 0); REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, true, expectedFulfillCnt, expectedReleaseCnt, true, expectedDoneCnt, reporter)); bool isVulkan = kVulkan_GrBackend == ctx->contextPriv().getBackend(); canvas->flush(); expectedFulfillCnt++; expectedReleaseCnt++; REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, !isVulkan, expectedFulfillCnt, expectedReleaseCnt, !isVulkan, expectedDoneCnt, reporter)); gpu->testingOnly_flushGpuAndSync(); REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, true, expectedFulfillCnt, expectedReleaseCnt, true, expectedDoneCnt, reporter)); canvas->drawImage(refImg, 0, 0); canvas->drawImage(refImg, 0, 0); canvas->flush(); expectedFulfillCnt++; expectedReleaseCnt++; gpu->testingOnly_flushGpuAndSync(); REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, true, expectedFulfillCnt, expectedReleaseCnt, true, expectedDoneCnt, reporter)); // Now test code path on Vulkan where we released the texture, but the GPU isn't done with // resource yet and we do another draw. We should only call fulfill on the first draw and // use the cached GrBackendTexture on the second. Release should only be called after the // second draw is finished. canvas->drawImage(refImg, 0, 0); canvas->flush(); expectedFulfillCnt++; expectedReleaseCnt++; REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, !isVulkan, expectedFulfillCnt, expectedReleaseCnt, !isVulkan, expectedDoneCnt, reporter)); canvas->drawImage(refImg, 0, 0); if (releaseImageEarly) { refImg.reset(); } REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, !isVulkan, expectedFulfillCnt, expectedReleaseCnt, !isVulkan, expectedDoneCnt, reporter)); canvas->flush(); expectedFulfillCnt++; gpu->testingOnly_flushGpuAndSync(); expectedReleaseCnt++; if (releaseImageEarly) { expectedDoneCnt++; } REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, true, expectedFulfillCnt, expectedReleaseCnt, !isVulkan, expectedDoneCnt, reporter)); expectedFulfillCnt = promiseChecker.fFulfillCount; expectedReleaseCnt = promiseChecker.fReleaseCount; if (!releaseImageEarly) { refImg.reset(); expectedDoneCnt++; } REPORTER_ASSERT(reporter, check_fulfill_and_release_cnts(promiseChecker, true, expectedFulfillCnt, expectedReleaseCnt, true, expectedDoneCnt, reporter)); gpu->deleteTestingOnlyBackendTexture(backendTex); } }
GrBackendFormat GrCaps::createFormatFromBackendTexture(const GrBackendTexture& backendTex) const { if (!backendTex.isValid()) { return GrBackendFormat(); } return this->onCreateFormatFromBackendTexture(backendTex); }