blob: 3ea94df2888d4f8a7d4de3cc2c32d6f5b641c735 [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/vk/VulkanTexture.h"
#include "src/core/SkMipmap.h"
#include "src/gpu/MutableTextureStateRef.h"
#include "src/gpu/graphite/Log.h"
#include "src/gpu/graphite/vk/VulkanCaps.h"
#include "src/gpu/graphite/vk/VulkanGraphiteUtilsPriv.h"
#include "src/gpu/graphite/vk/VulkanSharedContext.h"
#include "src/gpu/vk/VulkanMemory.h"
namespace skgpu::graphite {
bool VulkanTexture::MakeVkImage(const VulkanSharedContext* sharedContext,
SkISize dimensions,
const TextureInfo& info,
CreatedImageInfo* outInfo) {
SkASSERT(outInfo);
if (dimensions.isEmpty()) {
SKGPU_LOG_E("Tried to create VkImage with empty dimensions.");
return false;
}
const VulkanCaps& caps = sharedContext->vulkanCaps();
if (info.isProtected() == Protected::kYes && !caps.protectedSupport()) {
SKGPU_LOG_E("Tried to create protected VkImage when protected not supported.");
return false;
}
const VulkanTextureSpec& spec = info.vulkanTextureSpec();
bool isLinear = spec.fImageTiling == VK_IMAGE_TILING_LINEAR;
VkImageLayout initialLayout = isLinear ? VK_IMAGE_LAYOUT_PREINITIALIZED
: VK_IMAGE_LAYOUT_UNDEFINED;
// Create Image
VkSampleCountFlagBits vkSamples;
if (!SampleCountToVkSampleCount(info.numSamples(), &vkSamples)) {
SKGPU_LOG_E("Failed creating VkImage because we could not covert the number of samples: "
"%d to a VkSampleCountFlagBits.", info.numSamples());
return false;
}
SkASSERT(!isLinear || vkSamples == VK_SAMPLE_COUNT_1_BIT);
VkImageCreateFlags createflags = 0;
if (info.isProtected() == Protected::kYes) {
SkASSERT(caps.protectedSupport());
createflags |= VK_IMAGE_CREATE_PROTECTED_BIT;
}
uint32_t numMipLevels = 1;
if (info.mipmapped() == Mipmapped::kYes) {
numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
}
uint32_t width = static_cast<uint32_t>(dimensions.fWidth);
uint32_t height = static_cast<uint32_t>(dimensions.fHeight);
const VkImageCreateInfo imageCreateInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
nullptr, // pNext
createflags, // VkImageCreateFlags
VK_IMAGE_TYPE_2D, // VkImageType
spec.fFormat, // VkFormat
{ width, height, 1 }, // VkExtent3D
numMipLevels, // mipLevels
1, // arrayLayers
vkSamples, // samples
spec.fImageTiling, // VkImageTiling
spec.fImageUsageFlags, // VkImageUsageFlags
spec.fSharingMode, // VkSharingMode
0, // queueFamilyCount
nullptr, // pQueueFamilyIndices
initialLayout // initialLayout
};
auto interface = sharedContext->interface();
auto device = sharedContext->device();
VkImage image = VK_NULL_HANDLE;
VkResult result;
VULKAN_CALL_RESULT(interface, result,
CreateImage(device, &imageCreateInfo, nullptr, &image));
if (result != VK_SUCCESS) {
SKGPU_LOG_E("Failed call to vkCreateImage with error: %d", result);
return false;
}
auto allocator = sharedContext->memoryAllocator();
bool forceDedicatedMemory = caps.shouldAlwaysUseDedicatedImageMemory();
bool useLazyAllocation =
SkToBool(spec.fImageUsageFlags & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT);
auto checkResult = [](VkResult result) {
return result == VK_SUCCESS;
};
if (!skgpu::VulkanMemory::AllocImageMemory(allocator,
image,
info.isProtected(),
forceDedicatedMemory,
useLazyAllocation,
checkResult,
&outInfo->fMemoryAlloc) ||
(useLazyAllocation &&
!SkToBool(outInfo->fMemoryAlloc.fFlags & skgpu::VulkanAlloc::kLazilyAllocated_Flag))) {
VULKAN_CALL(interface, DestroyImage(device, image, nullptr));
return false;
}
VULKAN_CALL_RESULT(interface, result, BindImageMemory(device,
image,
outInfo->fMemoryAlloc.fMemory,
outInfo->fMemoryAlloc.fOffset));
if (result != VK_SUCCESS) {
skgpu::VulkanMemory::FreeImageMemory(allocator, outInfo->fMemoryAlloc);
VULKAN_CALL(interface, DestroyImage(device, image, nullptr));
return false;
}
outInfo->fImage = image;
outInfo->fMutableState = sk_make_sp<MutableTextureStateRef>(initialLayout,
VK_QUEUE_FAMILY_IGNORED);
return true;
}
sk_sp<Texture> VulkanTexture::Make(const VulkanSharedContext* sharedContext,
SkISize dimensions,
const TextureInfo& info,
skgpu::Budgeted budgeted) {
CreatedImageInfo imageInfo;
if (!MakeVkImage(sharedContext, dimensions, info, &imageInfo)) {
return nullptr;
}
return sk_sp<Texture>(new VulkanTexture(sharedContext,
dimensions,
info,
std::move(imageInfo.fMutableState),
imageInfo.fImage,
imageInfo.fMemoryAlloc,
Ownership::kOwned,
budgeted));
}
sk_sp<Texture> VulkanTexture::MakeWrapped(const VulkanSharedContext* sharedContext,
SkISize dimensions,
const TextureInfo& info,
sk_sp<MutableTextureStateRef> mutableState,
VkImage image,
const VulkanAlloc& alloc) {
return sk_sp<Texture>(new VulkanTexture(sharedContext,
dimensions,
info,
std::move(mutableState),
image,
alloc,
Ownership::kWrapped,
skgpu::Budgeted::kNo));
}
VulkanTexture::VulkanTexture(const VulkanSharedContext* sharedContext,
SkISize dimensions,
const TextureInfo& info,
sk_sp<MutableTextureStateRef> mutableState,
VkImage image,
const VulkanAlloc& alloc,
Ownership ownership,
skgpu::Budgeted budgeted)
: Texture(sharedContext, dimensions, info, std::move(mutableState), ownership, budgeted)
, fImage(image)
, fMemoryAlloc(alloc) {}
void VulkanTexture::freeGpuData() {
auto sharedContext = static_cast<const VulkanSharedContext*>(this->sharedContext());
skgpu::VulkanMemory::FreeImageMemory(sharedContext->memoryAllocator(), fMemoryAlloc);
}
} // namespace skgpu::graphite