/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrVkTexture.h"
#include "GrVkGpu.h"
#include "GrVkImageView.h"
#include "GrTexturePriv.h"
#include "GrVkTextureRenderTarget.h"
#include "GrVkUtil.h"
#include "vk/GrVkTypes.h"
#define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
// Because this class is virtually derived from GrSurface we must explicitly call its constructor.
GrVkTexture::GrVkTexture(GrVkGpu* gpu,
SkBudgeted budgeted,
const GrSurfaceDesc& desc,
const GrVkImageInfo& info,
const GrVkImageView* view)
: GrSurface(gpu, desc)
, GrVkImage(info, GrVkImage::kNot_Wrapped)
, INHERITED(gpu, desc, kTexture2DSampler_GrSLType, GrSamplerParams::kMipMap_FilterMode,
desc.fIsMipMapped)
, fTextureView(view)
, fLinearTextureView(nullptr) {
this->registerWithCache(budgeted);
}
GrVkTexture::GrVkTexture(GrVkGpu* gpu,
Wrapped,
const GrSurfaceDesc& desc,
const GrVkImageInfo& info,
const GrVkImageView* view,
GrVkImage::Wrapped wrapped)
: GrSurface(gpu, desc)
, GrVkImage(info, wrapped)
, INHERITED(gpu, desc, kTexture2DSampler_GrSLType, GrSamplerParams::kMipMap_FilterMode,
desc.fIsMipMapped)
, fTextureView(view)
, fLinearTextureView(nullptr) {
this->registerWithCacheWrapped();
}
// Because this class is virtually derived from GrSurface we must explicitly call its constructor.
GrVkTexture::GrVkTexture(GrVkGpu* gpu,
const GrSurfaceDesc& desc,
const GrVkImageInfo& info,
const GrVkImageView* view,
GrVkImage::Wrapped wrapped)
: GrSurface(gpu, desc)
, GrVkImage(info, wrapped)
, INHERITED(gpu, desc, kTexture2DSampler_GrSLType, GrSamplerParams::kMipMap_FilterMode,
desc.fIsMipMapped)
, fTextureView(view)
, fLinearTextureView(nullptr) {
}
GrVkTexture* GrVkTexture::CreateNewTexture(GrVkGpu* gpu, SkBudgeted budgeted,
const GrSurfaceDesc& desc,
const GrVkImage::ImageDesc& imageDesc) {
SkASSERT(imageDesc.fUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT);
GrVkImageInfo info;
if (!GrVkImage::InitImageInfo(gpu, imageDesc, &info)) {
return nullptr;
}
const GrVkImageView* imageView = GrVkImageView::Create(gpu, info.fImage, info.fFormat,
GrVkImageView::kColor_Type,
info.fLevelCount);
if (!imageView) {
GrVkImage::DestroyImageInfo(gpu, &info);
return nullptr;
}
return new GrVkTexture(gpu, budgeted, desc, info, imageView);
}
sk_sp<GrVkTexture> GrVkTexture::MakeWrappedTexture(GrVkGpu* gpu,
const GrSurfaceDesc& desc,
GrWrapOwnership ownership,
const GrVkImageInfo* info) {
SkASSERT(info);
// Wrapped textures require both image and allocation (because they can be mapped)
SkASSERT(VK_NULL_HANDLE != info->fImage && VK_NULL_HANDLE != info->fAlloc.fMemory);
const GrVkImageView* imageView = GrVkImageView::Create(gpu, info->fImage, info->fFormat,
GrVkImageView::kColor_Type,
info->fLevelCount);
if (!imageView) {
return nullptr;
}
if (kAdoptAndCache_GrWrapOwnership == ownership) {
return sk_sp<GrVkTexture>(new GrVkTexture(gpu, SkBudgeted::kYes, desc, *info, imageView));
} else {
GrVkImage::Wrapped wrapped = kBorrow_GrWrapOwnership == ownership
? GrVkImage::kBorrowed_Wrapped : GrVkImage::kAdopted_Wrapped;
return sk_sp<GrVkTexture>(new GrVkTexture(gpu, kWrapped, desc, *info, imageView, wrapped));
}
}
GrVkTexture::~GrVkTexture() {
// either release or abandon should have been called by the owner of this object.
SkASSERT(!fTextureView);
SkASSERT(!fLinearTextureView);
}
void GrVkTexture::onRelease() {
// we create this and don't hand it off, so we should always destroy it
if (fTextureView) {
fTextureView->unref(this->getVkGpu());
fTextureView = nullptr;
}
if (fLinearTextureView) {
fLinearTextureView->unref(this->getVkGpu());
fLinearTextureView = nullptr;
}
this->releaseImage(this->getVkGpu());
INHERITED::onRelease();
}
void GrVkTexture::onAbandon() {
if (fTextureView) {
fTextureView->unrefAndAbandon();
fTextureView = nullptr;
}
if (fLinearTextureView) {
fLinearTextureView->unrefAndAbandon();
fLinearTextureView = nullptr;
}
this->abandonImage();
INHERITED::onAbandon();
}
GrBackendObject GrVkTexture::getTextureHandle() const {
return (GrBackendObject)&fInfo;
}
std::unique_ptr<GrExternalTextureData> GrVkTexture::detachBackendTexture() {
// Not supported on Vulkan yet
// TODO: Add thread-safe memory pools, and implement this.
return nullptr;
}
GrVkGpu* GrVkTexture::getVkGpu() const {
SkASSERT(!this->wasDestroyed());
return static_cast<GrVkGpu*>(this->getGpu());
}
const GrVkImageView* GrVkTexture::textureView(bool allowSRGB) {
VkFormat linearFormat;
if (allowSRGB || !GrVkFormatIsSRGB(fInfo.fFormat, &linearFormat)) {
return fTextureView;
}
if (!fLinearTextureView) {
fLinearTextureView = GrVkImageView::Create(this->getVkGpu(), fInfo.fImage,
linearFormat, GrVkImageView::kColor_Type,
fInfo.fLevelCount);
SkASSERT(fLinearTextureView);
}
return fLinearTextureView;
}
bool GrVkTexture::reallocForMipmap(GrVkGpu* gpu, uint32_t mipLevels) {
if (mipLevels == 1) {
// don't need to do anything for a 1x1 texture
return false;
}
const GrVkResource* oldResource = this->resource();
// We shouldn't realloc something that doesn't belong to us
if (fIsBorrowed) {
return false;
}
bool renderTarget = SkToBool(fDesc.fFlags & kRenderTarget_GrSurfaceFlag);
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT;
if (renderTarget) {
usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
usageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
GrVkImage::ImageDesc imageDesc;
imageDesc.fImageType = VK_IMAGE_TYPE_2D;
imageDesc.fFormat = fInfo.fFormat;
imageDesc.fWidth = fDesc.fWidth;
imageDesc.fHeight = fDesc.fHeight;
imageDesc.fLevels = mipLevels;
imageDesc.fSamples = 1;
imageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
imageDesc.fUsageFlags = usageFlags;
imageDesc.fMemProps = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
GrVkImageInfo info;
if (!GrVkImage::InitImageInfo(gpu, imageDesc, &info)) {
return false;
}
// have to create a new image view for new resource
const GrVkImageView* oldView = fTextureView;
VkImage image = info.fImage;
const GrVkImageView* textureView = GrVkImageView::Create(gpu, image, info.fFormat,
GrVkImageView::kColor_Type, mipLevels);
if (!textureView) {
GrVkImage::DestroyImageInfo(gpu, &info);
return false;
}
if (renderTarget) {
GrVkTextureRenderTarget* texRT = static_cast<GrVkTextureRenderTarget*>(this);
if (!texRT->updateForMipmap(gpu, info)) {
GrVkImage::DestroyImageInfo(gpu, &info);
return false;
}
}
oldResource->unref(gpu);
oldView->unref(gpu);
if (fLinearTextureView) {
fLinearTextureView->unref(gpu);
fLinearTextureView = nullptr;
}
this->setNewResource(info.fImage, info.fAlloc, info.fImageTiling);
fTextureView = textureView;
fInfo = info;
this->texturePriv().setMaxMipMapLevel(mipLevels);
return true;
}