sk_sp<SkSurface> WindowContext::createSurface( sk_sp<GrRenderTarget> rt, int colorBits, bool offscreen, bool forceSRGB) { auto flags = (fSurfaceProps.flags() & ~SkSurfaceProps::kGammaCorrect_Flag) | (GrPixelConfigIsSRGB(fPixelConfig) || forceSRGB ? SkSurfaceProps::kGammaCorrect_Flag : 0); SkSurfaceProps props(flags, fSurfaceProps.pixelGeometry()); if (!this->isGpuContext() || colorBits > 24 || offscreen || kRGBA_F16_SkColorType == fDisplayParams.fColorType) { // If we're rendering to F16, we need an off-screen surface - the current render // target is most likely the wrong format. // // If we're rendering raster data or using a deep (10-bit or higher) surface, we probably // need an off-screen surface. 10-bit, in particular, has strange gamma behavior. SkImageInfo info = SkImageInfo::Make( fWidth, fHeight, fDisplayParams.fColorType, kPremul_SkAlphaType, forceSRGB ? SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) : fDisplayParams.fColorSpace ); if (this->isGpuContext()) { return SkSurface::MakeRenderTarget(fContext, SkBudgeted::kNo, info, fDisplayParams.fMSAASampleCount, &props); } else { return SkSurface::MakeRaster(info, &props); } } else { return SkSurface::MakeRenderTargetDirect(rt.get(), &props); } }
bool onGetROPixels(SkBitmap* dst) const override { const auto desc = SkBitmapCacheDesc::Make(this->uniqueID(), this->width(), this->height()); if (SkBitmapCache::Find(desc, dst)) { SkASSERT(dst->getGenerationID() == this->uniqueID()); SkASSERT(dst->isImmutable()); SkASSERT(dst->getPixels()); return true; } SkPixmap pmap; SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(), this->alphaType(), fColorSpace); auto rec = SkBitmapCache::Alloc(desc, info, &pmap); if (!rec) { return false; } sk_sp<SkColorSpace> colorSpace; if (GrPixelConfigIsSRGB(fTextureProxy->config())) { colorSpace = SkColorSpace::MakeSRGB(); } sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext( fTextureProxy, std::move(colorSpace)); if (!sContext) { return false; } if (!sContext->readPixels(info, pmap.writable_addr(), pmap.rowBytes(), 0, 0)) { return false; } SkBitmapCache::Add(std::move(rec), dst); fAddedRasterVersionToCache.store(true); return true; }
sk_sp<GrTextureProxy> GrCopyBaseMipMapToTextureProxy(GrContext* ctx, GrTextureProxy* baseProxy) { SkASSERT(baseProxy); if (!ctx->caps()->isConfigCopyable(baseProxy->config())) { return nullptr; } GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider(); GrSurfaceDesc desc; desc.fFlags = kNone_GrSurfaceFlags; desc.fOrigin = baseProxy->origin(); desc.fWidth = baseProxy->width(); desc.fHeight = baseProxy->height(); desc.fConfig = baseProxy->config(); desc.fSampleCnt = 1; sk_sp<GrTextureProxy> proxy = proxyProvider->createMipMapProxy(desc, SkBudgeted::kYes); if (!proxy) { return nullptr; } // Copy the base layer to our proxy sk_sp<SkColorSpace> colorSpace; if (GrPixelConfigIsSRGB(proxy->config())) { colorSpace = SkColorSpace::MakeSRGB(); } sk_sp<GrSurfaceContext> sContext = ctx->contextPriv().makeWrappedSurfaceContext(proxy, std::move(colorSpace)); SkASSERT(sContext); SkAssertResult(sContext->copy(baseProxy)); return proxy; }
void GrVkCaps::initConfigTable(const GrVkInterface* interface, VkPhysicalDevice physDev) { for (int i = 0; i < kGrPixelConfigCnt; ++i) { VkFormat format; if (GrPixelConfigToVkFormat(static_cast<GrPixelConfig>(i), &format)) { if (!GrPixelConfigIsSRGB(static_cast<GrPixelConfig>(i)) || fSRGBSupport) { fConfigTable[i].init(interface, physDev, format); } } } }
sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context, sk_sp<GrTextureProxy> inputProxy, const CopyParams& copyParams, bool dstWillRequireMipMaps) { SkASSERT(context); const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight); GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo; sk_sp<SkColorSpace> colorSpace; if (GrPixelConfigIsSRGB(inputProxy->config())) { colorSpace = SkColorSpace::MakeSRGB(); } sk_sp<GrRenderTargetContext> copyRTC = context->contextPriv().makeDeferredRenderTargetContextWithFallback( SkBackingFit::kExact, dstRect.width(), dstRect.height(), inputProxy->config(), std::move(colorSpace), 1, mipMapped, inputProxy->origin()); if (!copyRTC) { return nullptr; } GrPaint paint; paint.setGammaCorrect(true); SkRect localRect = SkRect::MakeWH(inputProxy->width(), inputProxy->height()); bool needsDomain = false; if (copyParams.fFilter != GrSamplerState::Filter::kNearest) { bool resizing = localRect.width() != dstRect.width() || localRect.height() != dstRect.height(); needsDomain = resizing && !GrProxyProvider::IsFunctionallyExact(inputProxy.get()); } if (needsDomain) { const SkRect domain = localRect.makeInset(0.5f, 0.5f); // This would cause us to read values from outside the subset. Surely, the caller knows // better! SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap); paint.addColorFragmentProcessor( GrTextureDomainEffect::Make(std::move(inputProxy), SkMatrix::I(), domain, GrTextureDomain::kClamp_Mode, copyParams.fFilter)); } else { GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter); paint.addColorTextureProcessor(std::move(inputProxy), SkMatrix::I(), samplerState); } paint.setPorterDuffXPFactory(SkBlendMode::kSrc); copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect, localRect); return copyRTC->asTextureProxyRef(); }
// In MDB mode the reffing of the 'getLastOpList' call's result allows in-progress // GrOpLists to be picked up and added to by renderTargetContexts lower in the call // stack. When this occurs with a closed GrOpList, a new one will be allocated // when the renderTargetContext attempts to use it (via getOpList). GrSurfaceContext::GrSurfaceContext(GrContext* context, GrDrawingManager* drawingMgr, GrPixelConfig config, sk_sp<SkColorSpace> colorSpace, GrAuditTrail* auditTrail, GrSingleOwner* singleOwner) : fContext(context) , fAuditTrail(auditTrail) , fColorSpaceInfo(std::move(colorSpace), config) , fDrawingManager(drawingMgr) #ifdef SK_DEBUG , fSingleOwner(singleOwner) #endif { // We never should have a sRGB pixel config with a non-SRGB gamma color space. SkASSERT(!GrPixelConfigIsSRGB(config) || (fColorSpaceInfo.colorSpace() && fColorSpaceInfo.colorSpace()->gammaCloseToSRGB())); }
sk_sp<GrTexture> GrYUVProvider::refAsTexture(GrContext* ctx, const GrSurfaceDesc& desc, bool useCache) { SkYUVPlanesCache::Info yuvInfo; void* planes[3]; YUVScoper scoper; if (!scoper.init(this, &yuvInfo, planes, useCache)) { return nullptr; } GrSurfaceDesc yuvDesc; yuvDesc.fConfig = kAlpha_8_GrPixelConfig; SkAutoTUnref<GrTexture> yuvTextures[3]; for (int i = 0; i < 3; i++) { yuvDesc.fWidth = yuvInfo.fSizeInfo.fSizes[i].fWidth; yuvDesc.fHeight = yuvInfo.fSizeInfo.fSizes[i].fHeight; // TODO: why do we need this check? bool needsExactTexture = (yuvDesc.fWidth != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth) || (yuvDesc.fHeight != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); if (needsExactTexture) { yuvTextures[i].reset(ctx->textureProvider()->createTexture(yuvDesc, SkBudgeted::kYes)); } else { yuvTextures[i].reset(ctx->textureProvider()->createApproxTexture(yuvDesc)); } if (!yuvTextures[i] || !yuvTextures[i]->writePixels(0, 0, yuvDesc.fWidth, yuvDesc.fHeight, yuvDesc.fConfig, planes[i], yuvInfo.fSizeInfo.fWidthBytes[i])) { return nullptr; } } sk_sp<GrDrawContext> drawContext(ctx->newDrawContext(SkBackingFit::kExact, desc.fWidth, desc.fHeight, desc.fConfig, desc.fSampleCnt)); if (!drawContext) { return nullptr; } GrPaint paint; sk_sp<GrFragmentProcessor> yuvToRgbProcessor( GrYUVEffect::MakeYUVToRGB(yuvTextures[0], yuvTextures[1], yuvTextures[2], yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace, false)); paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor)); // If we're decoding an sRGB image, the result of our linear math on the YUV planes is already // in sRGB. (The encoding is just math on bytes, with no concept of color spaces.) So, we need // to output the results of that math directly to the buffer that we will then consider sRGB. // If we have sRGB write control, we can just tell the HW not to do the Linear -> sRGB step. // Otherwise, we do our shader math to go from YUV -> sRGB, manually convert sRGB -> Linear, // then let the HW convert Linear -> sRGB. if (GrPixelConfigIsSRGB(desc.fConfig)) { if (ctx->caps()->srgbWriteControl()) { paint.setDisableOutputConversionToSRGB(true); } else { paint.addColorFragmentProcessor(GrGammaEffect::Make(2.2f)); } } paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth, yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); drawContext->drawRect(GrNoClip(), paint, SkMatrix::I(), r); return drawContext->asTexture(); }
sk_sp<GrColorSpaceXform> GrColorSpaceXform::Make(const SkColorSpace* src, GrPixelConfig srcConfig, const SkColorSpace* dst) { if (!dst) { // No transformation is performed in legacy mode return nullptr; } // Treat null sources as sRGB if (!src) { if (GrPixelConfigIsFloatingPoint(srcConfig)) { src = SkColorSpace::MakeSRGBLinear().get(); } else { src = SkColorSpace::MakeSRGB().get(); } } uint32_t flags = 0; SkColorSpaceTransferFn srcTransferFn; // kUnknown_GrPixelConfig is a sentinel that means we don't care about transfer functions, // just the gamut xform. if (kUnknown_GrPixelConfig != srcConfig) { // Determine if src transfer function is needed, based on src config and color space if (GrPixelConfigIsSRGB(srcConfig)) { // Source texture is sRGB, will be converted to linear when we sample if (src->gammaCloseToSRGB()) { // Hardware linearize does the right thing } else if (src->gammaIsLinear()) { // Oops, need to undo the (extra) linearize flags |= kApplyInverseSRGB_Flag; } else if (src->isNumericalTransferFn(&srcTransferFn)) { // Need to undo the (extra) linearize, then apply the correct transfer function flags |= (kApplyInverseSRGB_Flag | kApplyTransferFn_Flag); } else { // We don't (yet) support more complex transfer functions return nullptr; } } else { // Source texture is some non-sRGB format, we consider it linearly encoded if (src->gammaIsLinear()) { // Linear sampling does the right thing } else if (src->isNumericalTransferFn(&srcTransferFn)) { // Need to manually apply some transfer function (including sRGB) flags |= kApplyTransferFn_Flag; } else { // We don't (yet) support more complex transfer functions return nullptr; } } } if (src == dst && (0 == flags)) { // Quick equality check - no conversion (or transfer function) needed in this case return nullptr; } const SkMatrix44* toXYZD50 = src->toXYZD50(); const SkMatrix44* fromXYZD50 = dst->fromXYZD50(); if (!toXYZD50 || !fromXYZD50) { // Unsupported colour spaces -- cannot specify gamut as a matrix return nullptr; } // Determine if a gamut xform is needed uint32_t srcHash = src->toXYZD50Hash(); uint32_t dstHash = dst->toXYZD50Hash(); if (srcHash != dstHash) { flags |= kApplyGamutXform_Flag; } else { SkASSERT(*toXYZD50 == *dst->toXYZD50() && "Hash collision"); } if (0 == flags) { // Identical gamut and no transfer function - no conversion needed in this case return nullptr; } auto makeXform = [srcTransferFn, fromXYZD50, toXYZD50, flags]() { SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); if (SkToBool(flags & kApplyGamutXform_Flag)) { srcToDst.setConcat(*fromXYZD50, *toXYZD50); } else { srcToDst.setIdentity(); } return sk_make_sp<GrColorSpaceXform>(srcTransferFn, srcToDst, flags); }; // For now, we only cache pure gamut xforms (no transfer functions) // TODO: Fold a hash of the transfer function into the cache key if ((kApplyGamutXform_Flag == flags) && gColorSpaceXformCacheSpinlock.tryAcquire()) { static GrColorSpaceXformCache* gCache; if (nullptr == gCache) { gCache = new GrColorSpaceXformCache(); } uint64_t key = static_cast<uint64_t>(srcHash) << 32 | static_cast<uint64_t>(dstHash); sk_sp<GrColorSpaceXform> xform = gCache->findOrAdd(key, makeXform); gColorSpaceXformCacheSpinlock.release(); return xform; } else { // If our xform has non-gamut components, or we can't get the spin lock, just build it return makeXform(); } }
bool VulkanWindowContext::createSwapchain(uint32_t width, uint32_t height, const DisplayParams& params) { // check for capabilities VkSurfaceCapabilitiesKHR caps; VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fBackendContext->fPhysicalDevice, fSurface, &caps); if (VK_SUCCESS != res) { return false; } uint32_t surfaceFormatCount; res = fGetPhysicalDeviceSurfaceFormatsKHR(fBackendContext->fPhysicalDevice, fSurface, &surfaceFormatCount, nullptr); if (VK_SUCCESS != res) { return false; } SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR)); VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get(); res = fGetPhysicalDeviceSurfaceFormatsKHR(fBackendContext->fPhysicalDevice, fSurface, &surfaceFormatCount, surfaceFormats); if (VK_SUCCESS != res) { return false; } uint32_t presentModeCount; res = fGetPhysicalDeviceSurfacePresentModesKHR(fBackendContext->fPhysicalDevice, fSurface, &presentModeCount, nullptr); if (VK_SUCCESS != res) { return false; } SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR)); VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get(); res = fGetPhysicalDeviceSurfacePresentModesKHR(fBackendContext->fPhysicalDevice, fSurface, &presentModeCount, presentModes); if (VK_SUCCESS != res) { return false; } VkExtent2D extent = caps.currentExtent; // use the hints if (extent.width == (uint32_t)-1) { extent.width = width; extent.height = height; } // clamp width; to protect us from broken hints if (extent.width < caps.minImageExtent.width) { extent.width = caps.minImageExtent.width; } else if (extent.width > caps.maxImageExtent.width) { extent.width = caps.maxImageExtent.width; } // clamp height if (extent.height < caps.minImageExtent.height) { extent.height = caps.minImageExtent.height; } else if (extent.height > caps.maxImageExtent.height) { extent.height = caps.maxImageExtent.height; } fWidth = (int)extent.width; fHeight = (int)extent.height; uint32_t imageCount = caps.minImageCount + 2; if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) { // Application must settle for fewer images than desired: imageCount = caps.maxImageCount; } VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags); SkASSERT(caps.supportedTransforms & caps.currentTransform); SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)); VkCompositeAlphaFlagBitsKHR composite_alpha = (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // Pick our surface format. For now, just make sure it matches our sRGB request: VkFormat surfaceFormat = VK_FORMAT_UNDEFINED; VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; bool wantSRGB = kSRGB_SkColorProfileType == params.fProfileType; for (uint32_t i = 0; i < surfaceFormatCount; ++i) { GrPixelConfig config; if (GrVkFormatToPixelConfig(surfaceFormats[i].format, &config) && GrPixelConfigIsSRGB(config) == wantSRGB) { surfaceFormat = surfaceFormats[i].format; colorSpace = surfaceFormats[i].colorSpace; break; } } fDisplayParams = params; if (VK_FORMAT_UNDEFINED == surfaceFormat) { return false; } // If mailbox mode is available, use it, as it is the lowest-latency non- // tearing mode. If not, fall back to FIFO which is always available. VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR; for (uint32_t i = 0; i < presentModeCount; ++i) { // use mailbox if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) { mode = presentModes[i]; break; } } VkSwapchainCreateInfoKHR swapchainCreateInfo; memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR)); swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchainCreateInfo.surface = fSurface; swapchainCreateInfo.minImageCount = imageCount; swapchainCreateInfo.imageFormat = surfaceFormat; swapchainCreateInfo.imageColorSpace = colorSpace; swapchainCreateInfo.imageExtent = extent; swapchainCreateInfo.imageArrayLayers = 1; swapchainCreateInfo.imageUsage = usageFlags; uint32_t queueFamilies[] = { fBackendContext->fGraphicsQueueIndex, fPresentQueueIndex }; if (fBackendContext->fGraphicsQueueIndex != fPresentQueueIndex) { swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; swapchainCreateInfo.queueFamilyIndexCount = 2; swapchainCreateInfo.pQueueFamilyIndices = queueFamilies; } else { swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchainCreateInfo.queueFamilyIndexCount = 0; swapchainCreateInfo.pQueueFamilyIndices = nullptr; } swapchainCreateInfo.preTransform = caps.currentTransform;; swapchainCreateInfo.compositeAlpha = composite_alpha; swapchainCreateInfo.presentMode = mode; swapchainCreateInfo.clipped = true; swapchainCreateInfo.oldSwapchain = fSwapchain; res = fCreateSwapchainKHR(fBackendContext->fDevice, &swapchainCreateInfo, nullptr, &fSwapchain); if (VK_SUCCESS != res) { return false; } // destroy the old swapchain if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) { GR_VK_CALL(fBackendContext->fInterface, DeviceWaitIdle(fBackendContext->fDevice)); this->destroyBuffers(); fDestroySwapchainKHR(fBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr); } this->createBuffers(swapchainCreateInfo.imageFormat); return true; }
void VulkanWindowContext::createBuffers(VkFormat format) { GrVkFormatToPixelConfig(format, &fPixelConfig); fGetSwapchainImagesKHR(fBackendContext->fDevice, fSwapchain, &fImageCount, nullptr); SkASSERT(fImageCount); fImages = new VkImage[fImageCount]; fGetSwapchainImagesKHR(fBackendContext->fDevice, fSwapchain, &fImageCount, fImages); // set up initial image layouts and create surfaces fImageLayouts = new VkImageLayout[fImageCount]; fSurfaces = new sk_sp<SkSurface>[fImageCount]; for (uint32_t i = 0; i < fImageCount; ++i) { fImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED; GrBackendRenderTargetDesc desc; GrVkImageInfo info; info.fImage = fImages[i]; info.fAlloc = VK_NULL_HANDLE; info.fImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; info.fImageTiling = VK_IMAGE_TILING_OPTIMAL; info.fFormat = format; info.fLevelCount = 1; desc.fWidth = fWidth; desc.fHeight = fHeight; desc.fConfig = fPixelConfig; desc.fOrigin = kTopLeft_GrSurfaceOrigin; desc.fSampleCnt = 0; desc.fStencilBits = 0; desc.fRenderTargetHandle = (GrBackendObject) &info; SkSurfaceProps props(GrPixelConfigIsSRGB(fPixelConfig) ? SkSurfaceProps::kGammaCorrect_Flag : 0, kUnknown_SkPixelGeometry); fSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(fContext, desc, &props); } // create the command pool for the command buffers if (VK_NULL_HANDLE == fCommandPool) { VkCommandPoolCreateInfo commandPoolInfo; memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo)); commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; // this needs to be on the render queue commandPoolInfo.queueFamilyIndex = fBackendContext->fGraphicsQueueIndex; commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; GR_VK_CALL_ERRCHECK(fBackendContext->fInterface, CreateCommandPool(fBackendContext->fDevice, &commandPoolInfo, nullptr, &fCommandPool)); } // set up the backbuffers VkSemaphoreCreateInfo semaphoreInfo; memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo)); semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreInfo.pNext = nullptr; semaphoreInfo.flags = 0; VkCommandBufferAllocateInfo commandBuffersInfo; memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo)); commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; commandBuffersInfo.pNext = nullptr; commandBuffersInfo.commandPool = fCommandPool; commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; commandBuffersInfo.commandBufferCount = 2; VkFenceCreateInfo fenceInfo; memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo)); fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.pNext = nullptr; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; // we create one additional backbuffer structure here, because we want to // give the command buffers they contain a chance to finish before we cycle back fBackbuffers = new BackbufferInfo[fImageCount + 1]; for (uint32_t i = 0; i < fImageCount + 1; ++i) { fBackbuffers[i].fImageIndex = -1; GR_VK_CALL_ERRCHECK(fBackendContext->fInterface, CreateSemaphore(fBackendContext->fDevice, &semaphoreInfo, nullptr, &fBackbuffers[i].fAcquireSemaphore)); GR_VK_CALL_ERRCHECK(fBackendContext->fInterface, CreateSemaphore(fBackendContext->fDevice, &semaphoreInfo, nullptr, &fBackbuffers[i].fRenderSemaphore)); GR_VK_CALL_ERRCHECK(fBackendContext->fInterface, AllocateCommandBuffers(fBackendContext->fDevice, &commandBuffersInfo, fBackbuffers[i].fTransitionCmdBuffers)); GR_VK_CALL_ERRCHECK(fBackendContext->fInterface, CreateFence(fBackendContext->fDevice, &fenceInfo, nullptr, &fBackbuffers[i].fUsageFences[0])); GR_VK_CALL_ERRCHECK(fBackendContext->fInterface, CreateFence(fBackendContext->fDevice, &fenceInfo, nullptr, &fBackbuffers[i].fUsageFences[1])); } fCurrentBackbufferIndex = fImageCount; }
sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrContext* ctx, const GrSurfaceDesc& desc, const SkColorSpace* srcColorSpace, const SkColorSpace* dstColorSpace) { SkYUVPlanesCache::Info yuvInfo; void* planes[3]; sk_sp<SkCachedData> dataStorage = init_provider(this, &yuvInfo, planes); if (!dataStorage) { return nullptr; } sk_sp<GrTextureProxy> yuvTextureProxies[3]; for (int i = 0; i < 3; i++) { int componentWidth = yuvInfo.fSizeInfo.fSizes[i].fWidth; int componentHeight = yuvInfo.fSizeInfo.fSizes[i].fHeight; // If the sizes of the components are not all the same we choose to create exact-match // textures for the smaller onces rather than add a texture domain to the draw. // TODO: revisit this decision to imporve texture reuse? SkBackingFit fit = (componentWidth != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth) || (componentHeight != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight) ? SkBackingFit::kExact : SkBackingFit::kApprox; SkImageInfo imageInfo = SkImageInfo::MakeA8(componentWidth, componentHeight); SkPixmap pixmap(imageInfo, planes[i], yuvInfo.fSizeInfo.fWidthBytes[i]); SkCachedData* dataStoragePtr = dataStorage.get(); // We grab a ref to cached yuv data. When the SkImage we create below goes away it will call // the YUVGen_DataReleaseProc which will release this ref. // DDL TODO: Currently we end up creating a lazy proxy that will hold onto a ref to the // SkImage in its lambda. This means that we'll keep the ref on the YUV data around for the // life time of the proxy and not just upload. For non-DDL draws we should look into // releasing this SkImage after uploads (by deleting the lambda after instantiation). dataStoragePtr->ref(); sk_sp<SkImage> yuvImage = SkImage::MakeFromRaster(pixmap, YUVGen_DataReleaseProc, dataStoragePtr); auto proxyProvider = ctx->contextPriv().proxyProvider(); yuvTextureProxies[i] = proxyProvider->createTextureProxy(yuvImage, kNone_GrSurfaceFlags, kTopLeft_GrSurfaceOrigin, 1, SkBudgeted::kYes, fit); } // We never want to perform color-space conversion during the decode. However, if the proxy // config is sRGB then we must use a sRGB color space. sk_sp<SkColorSpace> colorSpace; if (GrPixelConfigIsSRGB(desc.fConfig)) { colorSpace = SkColorSpace::MakeSRGB(); } // TODO: investigate preallocating mip maps here sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeDeferredRenderTargetContext( SkBackingFit::kExact, desc.fWidth, desc.fHeight, desc.fConfig, std::move(colorSpace), desc.fSampleCnt, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin)); if (!renderTargetContext) { return nullptr; } GrPaint paint; auto yuvToRgbProcessor = GrYUVtoRGBEffect::Make(std::move(yuvTextureProxies[0]), std::move(yuvTextureProxies[1]), std::move(yuvTextureProxies[2]), yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace, false); paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor)); // If we're decoding an sRGB image, the result of our linear math on the YUV planes is already // in sRGB. (The encoding is just math on bytes, with no concept of color spaces.) So, we need // to output the results of that math directly to the buffer that we will then consider sRGB. // If we have sRGB write control, we can just tell the HW not to do the Linear -> sRGB step. // Otherwise, we do our shader math to go from YUV -> sRGB, manually convert sRGB -> Linear, // then let the HW convert Linear -> sRGB. if (GrPixelConfigIsSRGB(desc.fConfig)) { if (ctx->caps()->srgbWriteControl()) { paint.setDisableOutputConversionToSRGB(true); } else { paint.addColorFragmentProcessor(GrSRGBEffect::Make(GrSRGBEffect::Mode::kSRGBToLinear, GrSRGBEffect::Alpha::kOpaque)); } } // If the caller expects the pixels in a different color space than the one from the image, // apply a color conversion to do this. std::unique_ptr<GrFragmentProcessor> colorConversionProcessor = GrNonlinearColorSpaceXformEffect::Make(srcColorSpace, dstColorSpace); if (colorConversionProcessor) { paint.addColorFragmentProcessor(std::move(colorConversionProcessor)); } paint.setPorterDuffXPFactory(SkBlendMode::kSrc); const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth, yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight); renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r); return renderTargetContext->asTextureProxyRef(); }
bool GrGpu::getWritePixelsInfo(GrSurface* dstSurface, GrSurfaceOrigin dstOrigin, int width, int height, GrColorType srcColorType, GrSRGBConversion srgbConversion, DrawPreference* drawPreference, WritePixelTempDrawInfo* tempDrawInfo) { SkASSERT(drawPreference); SkASSERT(tempDrawInfo); SkASSERT(dstSurface); SkASSERT(kGpuPrefersDraw_DrawPreference != *drawPreference); GrPixelConfig tempSurfaceConfig = kUnknown_GrPixelConfig; // GrGpu::writePixels doesn't do any sRGB conversions, so we must draw if there is one. switch (srgbConversion) { case GrSRGBConversion::kNone: // We support writing just A to a RGBA. In that case there is no sRGB version of the // src format but we still want to succeed. if (GrColorTypeIsAlphaOnly(srcColorType)) { tempSurfaceConfig = GrColorTypeToPixelConfig(srcColorType, GrSRGBEncoded::kNo); } else { tempSurfaceConfig = GrColorTypeToPixelConfig( srcColorType, GrPixelConfigIsSRGBEncoded(dstSurface->config())); } break; case GrSRGBConversion::kLinearToSRGB: SkASSERT(this->caps()->srgbSupport()); // This assert goes away when we start referring to CPU data using color type. tempSurfaceConfig = GrColorTypeToPixelConfig(srcColorType, GrSRGBEncoded::kNo); // We don't currently support storing sRGB encoded data in a surface unless it is // an SRGB-encoded config. That is likely to change when we need to store sRGB encoded // data in 101010102 and F16 textures. We'll have to provoke the caller to do the // conversion in a shader. if (!GrPixelConfigIsSRGB(dstSurface->config())) { return false; } ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference); break; case GrSRGBConversion::kSRGBToLinear: SkASSERT(this->caps()->srgbSupport()); tempSurfaceConfig = GrColorTypeToPixelConfig(srcColorType, GrSRGBEncoded::kYes); // Currently we don't expect to make a SRGB encoded surface and then succeed at // treating it as though it were linear and then convert to sRGB. if (GrSRGBEncoded::kYes == GrPixelConfigIsSRGBEncoded(dstSurface->config())) { return false; } ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference); break; } if (kUnknown_GrPixelConfig == tempSurfaceConfig) { return false; } // Default values for intermediate draws. The intermediate texture config matches the dst's // config, is approx sized to the write rect, no swizzling or sppofing of the src config. tempDrawInfo->fTempSurfaceDesc.fFlags = kNone_GrSurfaceFlags; tempDrawInfo->fTempSurfaceDesc.fConfig = tempSurfaceConfig; tempDrawInfo->fTempSurfaceDesc.fWidth = width; tempDrawInfo->fTempSurfaceDesc.fHeight = height; tempDrawInfo->fTempSurfaceDesc.fSampleCnt = 1; tempDrawInfo->fSwizzle = GrSwizzle::RGBA(); tempDrawInfo->fWriteColorType = srcColorType; if (!this->onGetWritePixelsInfo(dstSurface, dstOrigin, width, height, srcColorType, drawPreference, tempDrawInfo)) { return false; } // Check to see if we're going to request that the caller draw when drawing is not possible. if (!dstSurface->asRenderTarget() || !this->caps()->isConfigTexturable(tempDrawInfo->fTempSurfaceDesc.fConfig)) { // If we don't have a fallback to a straight upload then fail. if (kRequireDraw_DrawPreference == *drawPreference /*TODO || !this->caps()->isConfigTexturable(srcConfig)*/) { return false; } *drawPreference = kNoDraw_DrawPreference; } return true; }
bool GrGpu::getReadPixelsInfo(GrSurface* srcSurface, GrSurfaceOrigin srcOrigin, int width, int height, size_t rowBytes, GrColorType dstColorType, GrSRGBConversion srgbConversion, DrawPreference* drawPreference, ReadPixelTempDrawInfo* tempDrawInfo) { SkASSERT(drawPreference); SkASSERT(tempDrawInfo); SkASSERT(srcSurface); SkASSERT(kGpuPrefersDraw_DrawPreference != *drawPreference); // We currently do not support reading into the packed formats 565 or 4444 as they are not // required to have read back support on all devices and backends. if (GrColorType::kRGB_565 == dstColorType || GrColorType::kABGR_4444 == dstColorType) { return false; } GrPixelConfig tempSurfaceConfig = kUnknown_GrPixelConfig; // GrGpu::readPixels doesn't do any sRGB conversions, so we must draw if there is one. switch (srgbConversion) { case GrSRGBConversion::kNone: // We support reading from RGBA to just A. In that case there is no sRGB version of the // dst format but we still want to succeed. if (GrColorTypeIsAlphaOnly(dstColorType)) { tempSurfaceConfig = GrColorTypeToPixelConfig(dstColorType, GrSRGBEncoded::kNo); } else { tempSurfaceConfig = GrColorTypeToPixelConfig( dstColorType, GrPixelConfigIsSRGBEncoded(srcSurface->config())); } break; case GrSRGBConversion::kLinearToSRGB: SkASSERT(this->caps()->srgbSupport()); tempSurfaceConfig = GrColorTypeToPixelConfig(dstColorType, GrSRGBEncoded::kYes); // Currently we don't expect to make a SRGB encoded surface and then read data from it // such that we treat it as though it were linear and is then converted to sRGB. if (GrPixelConfigIsSRGB(srcSurface->config())) { return false; } ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference); break; case GrSRGBConversion::kSRGBToLinear: SkASSERT(this->caps()->srgbSupport()); tempSurfaceConfig = GrColorTypeToPixelConfig(dstColorType, GrSRGBEncoded::kNo); // We don't currently support reading sRGB encoded data into linear from a surface // unless it is an sRGB-encoded config. That is likely to change when we need to store // sRGB encoded data in 101010102 and F16 textures. We'll have to provoke the caller to // do the conversion in a shader. if (GrSRGBEncoded::kNo == GrPixelConfigIsSRGBEncoded(srcSurface->config())) { return false; } ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference); break; } if (kUnknown_GrPixelConfig == tempSurfaceConfig) { return false; } // Default values for intermediate draws. The intermediate texture config matches the dst's // config, is approx sized to the read rect, no swizzling or spoofing of the dst config. tempDrawInfo->fTempSurfaceDesc.fFlags = kRenderTarget_GrSurfaceFlag; tempDrawInfo->fTempSurfaceDesc.fWidth = width; tempDrawInfo->fTempSurfaceDesc.fHeight = height; tempDrawInfo->fTempSurfaceDesc.fSampleCnt = 1; tempDrawInfo->fTempSurfaceDesc.fConfig = tempSurfaceConfig; tempDrawInfo->fTempSurfaceFit = SkBackingFit::kApprox; tempDrawInfo->fSwizzle = GrSwizzle::RGBA(); tempDrawInfo->fReadColorType = dstColorType; if (!this->onGetReadPixelsInfo(srcSurface, srcOrigin, width, height, rowBytes, dstColorType, drawPreference, tempDrawInfo)) { return false; } // Check to see if we're going to request that the caller draw when drawing is not possible. if (!srcSurface->asTexture() || !this->caps()->isConfigRenderable(tempDrawInfo->fTempSurfaceDesc.fConfig)) { // If we don't have a fallback to a straight read then fail. if (kRequireDraw_DrawPreference == *drawPreference) { return false; } *drawPreference = kNoDraw_DrawPreference; } return true; }