| /* |
| * Copyright 2021 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/gpu/graphite/Context.h" |
| |
| #include "include/core/SkColorSpace.h" |
| #include "include/core/SkPathTypes.h" |
| #include "include/effects/SkRuntimeEffect.h" |
| #include "include/gpu/graphite/BackendTexture.h" |
| #include "include/gpu/graphite/Recorder.h" |
| #include "include/gpu/graphite/Recording.h" |
| #include "include/gpu/graphite/Surface.h" |
| #include "include/gpu/graphite/TextureInfo.h" |
| #include "src/base/SkRectMemcpy.h" |
| #include "src/core/SkConvertPixels.h" |
| #include "src/core/SkYUVMath.h" |
| #include "src/gpu/RefCntedCallback.h" |
| #include "src/gpu/graphite/BufferManager.h" |
| #include "src/gpu/graphite/Caps.h" |
| #include "src/gpu/graphite/ClientMappedBufferManager.h" |
| #include "src/gpu/graphite/CommandBuffer.h" |
| #include "src/gpu/graphite/ContextPriv.h" |
| #include "src/gpu/graphite/CopyTask.h" |
| #include "src/gpu/graphite/Device.h" |
| #include "src/gpu/graphite/DrawAtlas.h" |
| #include "src/gpu/graphite/GlobalCache.h" |
| #include "src/gpu/graphite/GraphicsPipeline.h" |
| #include "src/gpu/graphite/GraphicsPipelineDesc.h" |
| #include "src/gpu/graphite/Image_Graphite.h" |
| #include "src/gpu/graphite/KeyContext.h" |
| #include "src/gpu/graphite/Log.h" |
| #include "src/gpu/graphite/QueueManager.h" |
| #include "src/gpu/graphite/RecorderPriv.h" |
| #include "src/gpu/graphite/RecordingPriv.h" |
| #include "src/gpu/graphite/Renderer.h" |
| #include "src/gpu/graphite/RendererProvider.h" |
| #include "src/gpu/graphite/ResourceProvider.h" |
| #include "src/gpu/graphite/RuntimeEffectDictionary.h" |
| #include "src/gpu/graphite/ShaderCodeDictionary.h" |
| #include "src/gpu/graphite/SharedContext.h" |
| #include "src/gpu/graphite/Surface_Graphite.h" |
| #include "src/gpu/graphite/SynchronizeToCpuTask.h" |
| #include "src/gpu/graphite/TextureProxyView.h" |
| #include "src/gpu/graphite/UploadTask.h" |
| |
| namespace skgpu::graphite { |
| |
| #define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(this->singleOwner()) |
| |
| Context::ContextID Context::ContextID::Next() { |
| static std::atomic<uint32_t> nextID{1}; |
| uint32_t id; |
| do { |
| id = nextID.fetch_add(1, std::memory_order_relaxed); |
| } while (id == SK_InvalidUniqueID); |
| return ContextID(id); |
| } |
| |
| //-------------------------------------------------------------------------------------------------- |
| Context::Context(sk_sp<SharedContext> sharedContext, |
| std::unique_ptr<QueueManager> queueManager, |
| const ContextOptions& options) |
| : fSharedContext(std::move(sharedContext)) |
| , fQueueManager(std::move(queueManager)) |
| #if GRAPHITE_TEST_UTILS |
| , fStoreContextRefInRecorder(options.fStoreContextRefInRecorder) |
| #endif |
| , fContextID(ContextID::Next()) { |
| // We have to create this outside the initializer list because we need to pass in the Context's |
| // SingleOwner object and it is declared last |
| fResourceProvider = fSharedContext->makeResourceProvider(&fSingleOwner, SK_InvalidGenID); |
| fMappedBufferManager = std::make_unique<ClientMappedBufferManager>(this->contextID()); |
| fPlotUploadTracker = std::make_unique<PlotUploadTracker>(); |
| } |
| |
| Context::~Context() { |
| #if GRAPHITE_TEST_UTILS |
| ASSERT_SINGLE_OWNER |
| for (auto& recorder : fTrackedRecorders) { |
| recorder->priv().setContext(nullptr); |
| } |
| #endif |
| } |
| |
| bool Context::finishInitialization() { |
| SkASSERT(!fSharedContext->rendererProvider()); // Can only initialize once |
| |
| StaticBufferManager bufferManager{fResourceProvider.get(), fSharedContext->caps()}; |
| std::unique_ptr<RendererProvider> renderers{ |
| new RendererProvider(fSharedContext->caps(), &bufferManager)}; |
| |
| auto result = bufferManager.finalize(this, fQueueManager.get(), fSharedContext->globalCache()); |
| if (result == StaticBufferManager::FinishResult::kFailure) { |
| // If something went wrong filling out the static vertex buffers, any Renderer that would |
| // use it will draw incorrectly, so it's better to fail the Context creation. |
| return false; |
| } |
| if (result == StaticBufferManager::FinishResult::kSuccess && |
| !fQueueManager->submitToGpu()) { |
| SKGPU_LOG_W("Failed to submit initial command buffer for Context creation.\n"); |
| return false; |
| } // else result was kNoWork so skip submitting to the GPU |
| fSharedContext->setRendererProvider(std::move(renderers)); |
| return true; |
| } |
| |
| BackendApi Context::backend() const { return fSharedContext->backend(); } |
| |
| std::unique_ptr<Recorder> Context::makeRecorder(const RecorderOptions& options) { |
| ASSERT_SINGLE_OWNER |
| |
| auto recorder = std::unique_ptr<Recorder>(new Recorder(fSharedContext, options)); |
| #if GRAPHITE_TEST_UTILS |
| if (fStoreContextRefInRecorder) { |
| recorder->priv().setContext(this); |
| } |
| #endif |
| return recorder; |
| } |
| |
| bool Context::insertRecording(const InsertRecordingInfo& info) { |
| ASSERT_SINGLE_OWNER |
| |
| return fQueueManager->addRecording(info, this); |
| } |
| |
| bool Context::submit(SyncToCpu syncToCpu) { |
| ASSERT_SINGLE_OWNER |
| |
| bool success = fQueueManager->submitToGpu(); |
| fQueueManager->checkForFinishedWork(syncToCpu); |
| return success; |
| } |
| |
| void Context::asyncRescaleAndReadPixels(const SkImage* image, |
| const SkImageInfo& dstImageInfo, |
| const SkIRect& srcRect, |
| SkImage::RescaleGamma rescaleGamma, |
| SkImage::RescaleMode rescaleMode, |
| SkImage::ReadPixelsCallback callback, |
| SkImage::ReadPixelsContext callbackContext) { |
| if (!image || !as_IB(image)->isGraphiteBacked()) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| // TODO(b/238756380): YUVA read not supported right now |
| if (as_IB(image)->isYUVA()) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| const SkImageInfo& srcImageInfo = image->imageInfo(); |
| if (!SkIRect::MakeSize(image->imageInfo().dimensions()).contains(srcRect)) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| if (srcRect.size() == dstImageInfo.bounds().size()) { |
| // No need for rescale |
| auto graphiteImage = reinterpret_cast<const skgpu::graphite::Image*>(image); |
| TextureProxyView proxyView = graphiteImage->textureProxyView(); |
| return this->asyncReadPixels(proxyView.proxy(), |
| image->imageInfo(), |
| dstImageInfo.colorInfo(), |
| srcRect, |
| callback, |
| callbackContext); |
| } |
| |
| // Make a recorder to record drawing commands into |
| std::unique_ptr<Recorder> recorder = this->makeRecorder(); |
| |
| // Make Device from Recorder |
| auto graphiteImage = reinterpret_cast<const skgpu::graphite::Image*>(image); |
| TextureProxyView proxyView = graphiteImage->textureProxyView(); |
| SkColorInfo colorInfo = srcImageInfo.colorInfo().makeAlphaType(kPremul_SkAlphaType); |
| sk_sp<Device> device = Device::Make(recorder.get(), |
| proxyView.refProxy(), |
| image->dimensions(), |
| colorInfo, |
| SkSurfaceProps{}, |
| false); |
| if (!device) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| sk_sp<SkImage> scaledImage = device->rescale(srcRect, dstImageInfo, rescaleGamma, rescaleMode); |
| if (!scaledImage) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| // Add draw commands to queue before starting the transfer |
| std::unique_ptr<Recording> recording = recorder->snap(); |
| if (!recording) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| InsertRecordingInfo recordingInfo; |
| recordingInfo.fRecording = recording.get(); |
| if (!this->insertRecording(recordingInfo)) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| SkASSERT(scaledImage->imageInfo() == dstImageInfo); |
| |
| auto scaledGraphiteImage = reinterpret_cast<const skgpu::graphite::Image*>(scaledImage.get()); |
| TextureProxyView scaledProxyView = scaledGraphiteImage->textureProxyView(); |
| |
| this->asyncReadPixels(scaledProxyView.proxy(), |
| dstImageInfo, |
| dstImageInfo.colorInfo(), |
| dstImageInfo.bounds(), |
| callback, |
| callbackContext); |
| } |
| |
| void Context::asyncRescaleAndReadPixels(const SkSurface* surface, |
| const SkImageInfo& dstImageInfo, |
| const SkIRect& srcRect, |
| SkImage::RescaleGamma rescaleGamma, |
| SkImage::RescaleMode rescaleMode, |
| SkImage::ReadPixelsCallback callback, |
| SkImage::ReadPixelsContext callbackContext) { |
| if (!static_cast<const SkSurface_Base*>(surface)->isGraphiteBacked()) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| sk_sp<SkImage> surfaceImage = SkSurfaces::AsImage(sk_ref_sp(surface)); |
| this->asyncRescaleAndReadPixels(surfaceImage.get(), |
| dstImageInfo, |
| srcRect, |
| rescaleGamma, |
| rescaleMode, |
| callback, |
| callbackContext); |
| } |
| |
| void Context::asyncReadPixels(const TextureProxy* proxy, |
| const SkImageInfo& srcImageInfo, |
| const SkColorInfo& dstColorInfo, |
| const SkIRect& srcRect, |
| SkImage::ReadPixelsCallback callback, |
| SkImage::ReadPixelsContext callbackContext) { |
| if (!proxy || proxy->textureInfo().isProtected() == Protected::kYes) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| if (!SkImageInfoIsValid(srcImageInfo) || !SkColorInfoIsValid(dstColorInfo)) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| if (!SkIRect::MakeSize(srcImageInfo.dimensions()).contains(srcRect)) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| const Caps* caps = fSharedContext->caps(); |
| if (!caps->supportsReadPixels(proxy->textureInfo())) { |
| // TODO: try to copy to a readable texture instead |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| PixelTransferResult transferResult = this->transferPixels(proxy, srcImageInfo, |
| dstColorInfo, srcRect); |
| |
| if (!transferResult.fTransferBuffer) { |
| // TODO: try to do a synchronous readPixels instead |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| using AsyncReadResult = skgpu::TAsyncReadResult<Buffer, ContextID, PixelTransferResult>; |
| struct FinishContext { |
| SkImage::ReadPixelsCallback* fClientCallback; |
| SkImage::ReadPixelsContext fClientContext; |
| SkISize fSize; |
| ClientMappedBufferManager* fMappedBufferManager; |
| PixelTransferResult fTransferResult; |
| }; |
| auto* finishContext = new FinishContext{callback, |
| callbackContext, |
| srcRect.size(), |
| fMappedBufferManager.get(), |
| std::move(transferResult)}; |
| GpuFinishedProc finishCallback = [](GpuFinishedContext c, CallbackResult status) { |
| const auto* context = reinterpret_cast<const FinishContext*>(c); |
| if (status == CallbackResult::kSuccess) { |
| ClientMappedBufferManager* manager = context->fMappedBufferManager; |
| auto result = std::make_unique<AsyncReadResult>(manager->ownerID()); |
| if (!result->addTransferResult(context->fTransferResult, context->fSize, |
| context->fTransferResult.fRowBytes, manager)) { |
| result.reset(); |
| } |
| (*context->fClientCallback)(context->fClientContext, std::move(result)); |
| } else { |
| (*context->fClientCallback)(context->fClientContext, nullptr); |
| } |
| delete context; |
| }; |
| |
| InsertFinishInfo info; |
| info.fFinishedContext = finishContext; |
| info.fFinishedProc = finishCallback; |
| // If addFinishInfo() fails, it invokes the finish callback automatically, which handles all the |
| // required clean up for us, just log an error message. |
| if (!fQueueManager->addFinishInfo(info, fResourceProvider.get())) { |
| SKGPU_LOG_E("Failed to register finish callbacks for asyncReadPixels."); |
| } |
| } |
| |
| void Context::asyncRescaleAndReadPixelsYUV420(const SkImage* image, |
| SkYUVColorSpace yuvColorSpace, |
| sk_sp<SkColorSpace> dstColorSpace, |
| const SkIRect& srcRect, |
| const SkISize& dstSize, |
| SkImage::RescaleGamma rescaleGamma, |
| SkImage::RescaleMode rescaleMode, |
| SkImage::ReadPixelsCallback callback, |
| SkImage::ReadPixelsContext callbackContext) { |
| this->asyncRescaleAndReadPixelsYUV420Impl(image, |
| yuvColorSpace, |
| /*readAlpha=*/false, |
| dstColorSpace, |
| srcRect, |
| dstSize, |
| rescaleGamma, |
| rescaleMode, |
| callback, |
| callbackContext); |
| } |
| |
| void Context::asyncRescaleAndReadPixelsYUV420(const SkSurface* surface, |
| SkYUVColorSpace yuvColorSpace, |
| sk_sp<SkColorSpace> dstColorSpace, |
| const SkIRect& srcRect, |
| const SkISize& dstSize, |
| SkImage::RescaleGamma rescaleGamma, |
| SkImage::RescaleMode rescaleMode, |
| SkImage::ReadPixelsCallback callback, |
| SkImage::ReadPixelsContext callbackContext) { |
| if (!static_cast<const SkSurface_Base*>(surface)->isGraphiteBacked()) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| sk_sp<SkImage> surfaceImage = SkSurfaces::AsImage(sk_ref_sp(surface)); |
| this->asyncRescaleAndReadPixelsYUV420(surfaceImage.get(), |
| yuvColorSpace, |
| dstColorSpace, |
| srcRect, |
| dstSize, |
| rescaleGamma, |
| rescaleMode, |
| callback, |
| callbackContext); |
| } |
| |
| void Context::asyncRescaleAndReadPixelsYUVA420(const SkImage* image, |
| SkYUVColorSpace yuvColorSpace, |
| sk_sp<SkColorSpace> dstColorSpace, |
| const SkIRect& srcRect, |
| const SkISize& dstSize, |
| SkImage::RescaleGamma rescaleGamma, |
| SkImage::RescaleMode rescaleMode, |
| SkImage::ReadPixelsCallback callback, |
| SkImage::ReadPixelsContext callbackContext) { |
| this->asyncRescaleAndReadPixelsYUV420Impl(image, |
| yuvColorSpace, |
| /*readAlpha=*/true, |
| dstColorSpace, |
| srcRect, |
| dstSize, |
| rescaleGamma, |
| rescaleMode, |
| callback, |
| callbackContext); |
| } |
| |
| void Context::asyncRescaleAndReadPixelsYUVA420(const SkSurface* surface, |
| SkYUVColorSpace yuvColorSpace, |
| sk_sp<SkColorSpace> dstColorSpace, |
| const SkIRect& srcRect, |
| const SkISize& dstSize, |
| SkImage::RescaleGamma rescaleGamma, |
| SkImage::RescaleMode rescaleMode, |
| SkImage::ReadPixelsCallback callback, |
| SkImage::ReadPixelsContext callbackContext) { |
| if (!static_cast<const SkSurface_Base*>(surface)->isGraphiteBacked()) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| sk_sp<SkImage> surfaceImage = SkSurfaces::AsImage(sk_ref_sp(surface)); |
| this->asyncRescaleAndReadPixelsYUVA420(surfaceImage.get(), |
| yuvColorSpace, |
| dstColorSpace, |
| srcRect, |
| dstSize, |
| rescaleGamma, |
| rescaleMode, |
| callback, |
| callbackContext); |
| } |
| |
| void Context::asyncRescaleAndReadPixelsYUV420Impl(const SkImage* image, |
| SkYUVColorSpace yuvColorSpace, |
| bool readAlpha, |
| sk_sp<SkColorSpace> dstColorSpace, |
| const SkIRect& srcRect, |
| const SkISize& dstSize, |
| SkImage::RescaleGamma rescaleGamma, |
| SkImage::RescaleMode rescaleMode, |
| SkImage::ReadPixelsCallback callback, |
| SkImage::ReadPixelsContext callbackContext) { |
| if (!image || !as_IB(image)->isGraphiteBacked()) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| const SkImageInfo& srcImageInfo = image->imageInfo(); |
| if (!SkIRect::MakeSize(image->imageInfo().dimensions()).contains(srcRect)) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| // Make a recorder to record drawing commands into |
| std::unique_ptr<Recorder> recorder = this->makeRecorder(); |
| |
| if (srcRect.size() == dstSize && |
| SkColorSpace::Equals(srcImageInfo.colorInfo().colorSpace(), |
| dstColorSpace.get())) { |
| // No need for rescale |
| return this->asyncReadPixelsYUV420(recorder.get(), |
| image, |
| yuvColorSpace, |
| readAlpha, |
| srcRect, |
| callback, |
| callbackContext); |
| } |
| |
| // Make Device from Recorder |
| auto graphiteImage = reinterpret_cast<const skgpu::graphite::Image*>(image); |
| TextureProxyView proxyView = graphiteImage->textureProxyView(); |
| sk_sp<Device> device = Device::Make(recorder.get(), |
| proxyView.refProxy(), |
| image->dimensions(), |
| srcImageInfo.colorInfo(), |
| SkSurfaceProps{}, |
| false); |
| if (!device) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| SkImageInfo dstImageInfo = SkImageInfo::Make(dstSize, |
| kRGBA_8888_SkColorType, |
| srcImageInfo.colorInfo().alphaType(), |
| dstColorSpace); |
| sk_sp<SkImage> scaledImage = device->rescale(srcRect, |
| dstImageInfo, |
| rescaleGamma, |
| rescaleMode); |
| if (!scaledImage) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| this->asyncReadPixelsYUV420(recorder.get(), |
| scaledImage.get(), |
| yuvColorSpace, |
| readAlpha, |
| SkIRect::MakeSize(dstSize), |
| callback, |
| callbackContext); |
| } |
| |
| void Context::asyncReadPixelsYUV420(Recorder* recorder, |
| const SkImage* srcImage, |
| SkYUVColorSpace yuvColorSpace, |
| bool readAlpha, |
| const SkIRect& srcRect, |
| SkImage::ReadPixelsCallback callback, |
| SkImage::ReadPixelsContext callbackContext) { |
| // Make three or four Surfaces to draw the YUV[A] planes into |
| SkImageInfo yaInfo = SkImageInfo::MakeA8(srcRect.size()); |
| sk_sp<SkSurface> ySurface = Surface::MakeGraphite(recorder, yaInfo, Budgeted::kNo); |
| sk_sp<SkSurface> aSurface; |
| if (readAlpha) { |
| aSurface = Surface::MakeGraphite(recorder, yaInfo, Budgeted::kNo); |
| } |
| |
| SkImageInfo uvInfo = yaInfo.makeWH(yaInfo.width()/2, yaInfo.height()/2); |
| sk_sp<SkSurface> uSurface = Surface::MakeGraphite(recorder, uvInfo, Budgeted::kNo); |
| sk_sp<SkSurface> vSurface = Surface::MakeGraphite(recorder, uvInfo, Budgeted::kNo); |
| |
| if (!ySurface || !uSurface || !vSurface || (readAlpha && !aSurface)) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| // Set up draws and transfers |
| // TODO: Use one transfer buffer for all three planes to reduce map/unmap cost? |
| auto drawPlane = [](SkSurface* dstSurface, |
| const SkImage* srcImage, |
| float rgb2yuv[20], |
| const SkMatrix& texMatrix) { |
| // Render the plane defined by rgb2yuv from srcImage into dstSurface |
| SkPaint paint; |
| const SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNone); |
| sk_sp<SkShader> imgShader = srcImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, |
| sampling, texMatrix); |
| paint.setShader(std::move(imgShader)); |
| |
| if (rgb2yuv) { |
| sk_sp<SkColorFilter> matrixFilter = SkColorFilters::Matrix(rgb2yuv); |
| paint.setColorFilter(std::move(matrixFilter)); |
| } |
| |
| SkCanvas* canvas = dstSurface->getCanvas(); |
| canvas->drawPaint(paint); |
| }; |
| |
| auto copyPlane = [this](SkSurface* surface) { |
| // Transfer result from dstSurface |
| auto graphiteSurface = reinterpret_cast<const skgpu::graphite::Surface*>(surface); |
| TextureProxyView proxyView = graphiteSurface->readSurfaceView(); |
| |
| auto srcImageInfo = surface->imageInfo(); |
| auto dstColorInfo = srcImageInfo.colorInfo().makeColorType(kAlpha_8_SkColorType); |
| return this->transferPixels(proxyView.proxy(), |
| srcImageInfo, |
| dstColorInfo, |
| SkIRect::MakeWH(surface->width(), surface->height())); |
| }; |
| |
| float baseM[20]; |
| SkColorMatrix_RGB2YUV(yuvColorSpace, baseM); |
| SkMatrix texMatrix = SkMatrix::Translate(srcRect.fLeft, srcRect.fTop); |
| |
| // This matrix generates (r,g,b,a) = (0, 0, 0, y) |
| float yM[20]; |
| std::fill_n(yM, 15, 0.f); |
| std::copy_n(baseM + 0, 5, yM + 15); |
| drawPlane(ySurface.get(), srcImage, yM, texMatrix); |
| if (readAlpha) { |
| // No matrix, straight copy of alpha channel |
| SkASSERT(baseM[15] == 0 && |
| baseM[16] == 0 && |
| baseM[17] == 0 && |
| baseM[18] == 1 && |
| baseM[19] == 0); |
| drawPlane(aSurface.get(), srcImage, nullptr, texMatrix); |
| } |
| |
| texMatrix.preScale(0.5f, 0.5f); |
| // This matrix generates (r,g,b,a) = (0, 0, 0, u) |
| float uM[20]; |
| std::fill_n(uM, 15, 0.f); |
| std::copy_n(baseM + 5, 5, uM + 15); |
| drawPlane(uSurface.get(), srcImage, uM, texMatrix); |
| |
| // This matrix generates (r,g,b,a) = (0, 0, 0, v) |
| float vM[20]; |
| std::fill_n(vM, 15, 0.f); |
| std::copy_n(baseM + 10, 5, vM + 15); |
| drawPlane(vSurface.get(), srcImage, vM, texMatrix); |
| |
| // Add draw commands to queue |
| std::unique_ptr<Recording> recording = recorder->snap(); |
| if (!recording) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| InsertRecordingInfo recordingInfo; |
| recordingInfo.fRecording = recording.get(); |
| if (!this->insertRecording(recordingInfo)) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| |
| // Now set up transfers |
| PixelTransferResult yTransfer, uTransfer, vTransfer, aTransfer; |
| yTransfer = copyPlane(ySurface.get()); |
| if (!yTransfer.fTransferBuffer) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| uTransfer = copyPlane(uSurface.get()); |
| if (!uTransfer.fTransferBuffer) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| vTransfer = copyPlane(vSurface.get()); |
| if (!vTransfer.fTransferBuffer) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| if (readAlpha) { |
| aTransfer = copyPlane(aSurface.get()); |
| if (!aTransfer.fTransferBuffer) { |
| callback(callbackContext, nullptr); |
| return; |
| } |
| } |
| |
| // Set up FinishContext and add transfer commands to queue |
| using AsyncReadResult = skgpu::TAsyncReadResult<Buffer, ContextID, PixelTransferResult>; |
| struct FinishContext { |
| SkImage::ReadPixelsCallback* fClientCallback; |
| SkImage::ReadPixelsContext fClientContext; |
| SkISize fSize; |
| ClientMappedBufferManager* fMappedBufferManager; |
| PixelTransferResult fYTransfer; |
| PixelTransferResult fUTransfer; |
| PixelTransferResult fVTransfer; |
| PixelTransferResult fATransfer; |
| }; |
| auto* finishContext = new FinishContext{callback, |
| callbackContext, |
| srcRect.size(), |
| fMappedBufferManager.get(), |
| std::move(yTransfer), |
| std::move(uTransfer), |
| std::move(vTransfer), |
| std::move(aTransfer)}; |
| GpuFinishedProc finishCallback = [](GpuFinishedContext c, CallbackResult status) { |
| const auto* context = reinterpret_cast<const FinishContext*>(c); |
| if (status == CallbackResult::kSuccess) { |
| auto manager = context->fMappedBufferManager; |
| auto result = std::make_unique<AsyncReadResult>(manager->ownerID()); |
| if (!result->addTransferResult(context->fYTransfer, context->fSize, |
| context->fYTransfer.fRowBytes, manager)) { |
| result.reset(); |
| } |
| SkISize uvSize = {context->fSize.width() / 2, context->fSize.height() / 2}; |
| if (result && !result->addTransferResult(context->fUTransfer, uvSize, |
| context->fUTransfer.fRowBytes, manager)) { |
| result.reset(); |
| } |
| if (result && !result->addTransferResult(context->fVTransfer, uvSize, |
| context->fVTransfer.fRowBytes, manager)) { |
| result.reset(); |
| } |
| if (result && context->fATransfer.fTransferBuffer && |
| !result->addTransferResult(context->fATransfer, context->fSize, |
| context->fATransfer.fRowBytes, manager)) { |
| result.reset(); |
| } |
| (*context->fClientCallback)(context->fClientContext, std::move(result)); |
| } else { |
| (*context->fClientCallback)(context->fClientContext, nullptr); |
| } |
| delete context; |
| }; |
| |
| InsertFinishInfo finishInfo; |
| finishInfo.fFinishedContext = finishContext; |
| finishInfo.fFinishedProc = finishCallback; |
| // If addFinishInfo() fails, it invokes the finish callback automatically, which handles all the |
| // required clean up for us, just log an error message. |
| if (!fQueueManager->addFinishInfo(finishInfo, fResourceProvider.get())) { |
| SKGPU_LOG_E("Failed to register finish callbacks for asyncReadPixels."); |
| } |
| } |
| |
| Context::PixelTransferResult Context::transferPixels(const TextureProxy* proxy, |
| const SkImageInfo& srcImageInfo, |
| const SkColorInfo& dstColorInfo, |
| const SkIRect& srcRect) { |
| SkASSERT(srcImageInfo.bounds().contains(srcRect)); |
| |
| const Caps* caps = fSharedContext->caps(); |
| SkColorType supportedColorType = |
| caps->supportedReadPixelsColorType(srcImageInfo.colorType(), |
| proxy->textureInfo(), |
| dstColorInfo.colorType()); |
| if (supportedColorType == kUnknown_SkColorType) { |
| return {}; |
| } |
| |
| // Fail if read color type does not have all of dstCT's color channels and those missing color |
| // channels are in the src. |
| uint32_t dstChannels = SkColorTypeChannelFlags(dstColorInfo.colorType()); |
| uint32_t legalReadChannels = SkColorTypeChannelFlags(supportedColorType); |
| uint32_t srcChannels = SkColorTypeChannelFlags(srcImageInfo.colorType()); |
| if ((~legalReadChannels & dstChannels) & srcChannels) { |
| return {}; |
| } |
| |
| size_t rowBytes = caps->getAlignedTextureDataRowBytes( |
| SkColorTypeBytesPerPixel(supportedColorType) * srcRect.width()); |
| size_t size = SkAlignTo(rowBytes * srcRect.height(), caps->requiredTransferBufferAlignment()); |
| sk_sp<Buffer> buffer = fResourceProvider->findOrCreateBuffer( |
| size, BufferType::kXferGpuToCpu, AccessPattern::kHostVisible); |
| if (!buffer) { |
| return {}; |
| } |
| |
| // Set up copy task. Since we always use a new buffer the offset can be 0 and we don't need to |
| // worry about aligning it to the required transfer buffer alignment. |
| sk_sp<CopyTextureToBufferTask> copyTask = CopyTextureToBufferTask::Make(sk_ref_sp(proxy), |
| srcRect, |
| buffer, |
| /*bufferOffset=*/0, |
| rowBytes); |
| if (!copyTask || !fQueueManager->addTask(copyTask.get(), this)) { |
| return {}; |
| } |
| sk_sp<SynchronizeToCpuTask> syncTask = SynchronizeToCpuTask::Make(buffer); |
| if (!syncTask || !fQueueManager->addTask(syncTask.get(), this)) { |
| return {}; |
| } |
| |
| PixelTransferResult result; |
| result.fTransferBuffer = std::move(buffer); |
| if (srcImageInfo.colorInfo() != dstColorInfo) { |
| SkISize dims = srcRect.size(); |
| SkImageInfo srcInfo = SkImageInfo::Make(dims, srcImageInfo.colorInfo()); |
| SkImageInfo dstInfo = SkImageInfo::Make(dims, dstColorInfo); |
| result.fRowBytes = dstInfo.minRowBytes(); |
| result.fPixelConverter = [dstInfo, srcInfo, rowBytes]( |
| void* dst, const void* src) { |
| SkAssertResult(SkConvertPixels(dstInfo, dst, dstInfo.minRowBytes(), |
| srcInfo, src, rowBytes)); |
| }; |
| } else { |
| result.fRowBytes = rowBytes; |
| } |
| |
| return result; |
| } |
| |
| |
| void Context::checkAsyncWorkCompletion() { |
| ASSERT_SINGLE_OWNER |
| |
| fQueueManager->checkForFinishedWork(SyncToCpu::kNo); |
| } |
| |
| void Context::deleteBackendTexture(BackendTexture& texture) { |
| ASSERT_SINGLE_OWNER |
| |
| if (!texture.isValid() || texture.backend() != this->backend()) { |
| return; |
| } |
| fResourceProvider->deleteBackendTexture(texture); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////// |
| |
| #if GRAPHITE_TEST_UTILS |
| bool ContextPriv::readPixels(const SkPixmap& pm, |
| const TextureProxy* textureProxy, |
| const SkImageInfo& srcImageInfo, |
| int srcX, int srcY) { |
| auto rect = SkIRect::MakeXYWH(srcX, srcY, pm.width(), pm.height()); |
| struct AsyncContext { |
| bool fCalled = false; |
| std::unique_ptr<const SkImage::AsyncReadResult> fResult; |
| } asyncContext; |
| fContext->asyncReadPixels(textureProxy, srcImageInfo, pm.info().colorInfo(), rect, |
| [](void* c, std::unique_ptr<const SkImage::AsyncReadResult> result) { |
| auto context = static_cast<AsyncContext*>(c); |
| context->fResult = std::move(result); |
| context->fCalled = true; |
| }, |
| &asyncContext); |
| |
| if (!asyncContext.fCalled) { |
| fContext->submit(SyncToCpu::kYes); |
| } |
| SkASSERT(asyncContext.fCalled); |
| if (!asyncContext.fResult) { |
| return false; |
| } |
| SkRectMemcpy(pm.writable_addr(), pm.rowBytes(), asyncContext.fResult->data(0), |
| asyncContext.fResult->rowBytes(0), pm.info().minRowBytes(), |
| pm.height()); |
| return true; |
| } |
| |
| void ContextPriv::deregisterRecorder(const Recorder* recorder) { |
| SKGPU_ASSERT_SINGLE_OWNER(fContext->singleOwner()) |
| for (auto it = fContext->fTrackedRecorders.begin(); |
| it != fContext->fTrackedRecorders.end(); |
| it++) { |
| if (*it == recorder) { |
| fContext->fTrackedRecorders.erase(it); |
| return; |
| } |
| } |
| } |
| |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////////// |
| |
| std::unique_ptr<Context> ContextCtorAccessor::MakeContext( |
| sk_sp<SharedContext> sharedContext, |
| std::unique_ptr<QueueManager> queueManager, |
| const ContextOptions& options) { |
| auto context = std::unique_ptr<Context>(new Context(std::move(sharedContext), |
| std::move(queueManager), |
| options)); |
| if (context && context->finishInitialization()) { |
| return context; |
| } else { |
| return nullptr; |
| } |
| } |
| |
| } // namespace skgpu::graphite |