VulkanTexture: support format conversion via vkCmdBlitImage.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include "VulkanStagePool.h"
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
@@ -58,6 +59,51 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
|
||||
return stage;
|
||||
}
|
||||
|
||||
VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, PixelDataType type,
|
||||
uint32_t width, uint32_t height) {
|
||||
const VkFormat vkformat = getVkFormat(format, type);
|
||||
for (auto image : mFreeImages) {
|
||||
if (image->format == vkformat && image->width == width && image->height == height) {
|
||||
mFreeImages.erase(image);
|
||||
mUsedImages.insert(image);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
VulkanStageImage* image = new VulkanStageImage({
|
||||
.format = vkformat,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.lastAccessed = mCurrentFrame,
|
||||
});
|
||||
|
||||
mUsedImages.insert(image);
|
||||
|
||||
const VkImageCreateInfo imageInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = vkformat,
|
||||
.extent = { width, height, 1 },
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_LINEAR,
|
||||
.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT
|
||||
};
|
||||
|
||||
const VmaAllocationCreateInfo allocInfo {
|
||||
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
||||
.usage = VMA_MEMORY_USAGE_CPU_TO_GPU
|
||||
};
|
||||
|
||||
const UTILS_UNUSED VkResult result = vmaCreateImage(mContext.allocator, &imageInfo, &allocInfo,
|
||||
&image->image, &image->memory, nullptr);
|
||||
|
||||
assert_invariant(result == VK_SUCCESS);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void VulkanStagePool::gc() noexcept {
|
||||
// If this is one of the first few frames, return early to avoid wrapping unsigned integers.
|
||||
if (++mCurrentFrame <= TIME_BEFORE_EVICTION) {
|
||||
@@ -65,6 +111,7 @@ void VulkanStagePool::gc() noexcept {
|
||||
}
|
||||
const uint64_t evictionTime = mCurrentFrame - TIME_BEFORE_EVICTION;
|
||||
|
||||
// Destroy buffers that have not been used for several frames.
|
||||
decltype(mFreeStages) freeStages;
|
||||
freeStages.swap(mFreeStages);
|
||||
for (auto pair : freeStages) {
|
||||
@@ -76,6 +123,7 @@ void VulkanStagePool::gc() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
// Reclaim buffers that are no longer being used by any command buffer.
|
||||
decltype(mUsedStages) usedStages;
|
||||
usedStages.swap(mUsedStages);
|
||||
for (auto stage : usedStages) {
|
||||
@@ -86,6 +134,30 @@ void VulkanStagePool::gc() noexcept {
|
||||
mUsedStages.insert(stage);
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy images that have not been used for several frames.
|
||||
decltype(mFreeImages) freeImages;
|
||||
freeImages.swap(mFreeImages);
|
||||
for (auto image : freeImages) {
|
||||
if (image->lastAccessed < evictionTime) {
|
||||
vmaDestroyImage(mContext.allocator, image->image, image->memory);
|
||||
delete image;
|
||||
} else {
|
||||
mFreeImages.insert(image);
|
||||
}
|
||||
}
|
||||
|
||||
// Reclaim images that are no longer being used by any command buffer.
|
||||
decltype(mUsedImages) usedImages;
|
||||
usedImages.swap(mUsedImages);
|
||||
for (auto image : usedImages) {
|
||||
if (image->lastAccessed < evictionTime) {
|
||||
image->lastAccessed = mCurrentFrame;
|
||||
mFreeImages.insert(image);
|
||||
} else {
|
||||
mUsedImages.insert(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanStagePool::reset() noexcept {
|
||||
@@ -94,11 +166,24 @@ void VulkanStagePool::reset() noexcept {
|
||||
delete stage;
|
||||
}
|
||||
mUsedStages.clear();
|
||||
|
||||
for (auto pair : mFreeStages) {
|
||||
vmaDestroyBuffer(mContext.allocator, pair.second->buffer, pair.second->memory);
|
||||
delete pair.second;
|
||||
}
|
||||
mFreeStages.clear();
|
||||
|
||||
for (auto image : mUsedImages) {
|
||||
vmaDestroyImage(mContext.allocator, image->image, image->memory);
|
||||
delete image;
|
||||
}
|
||||
mUsedStages.clear();
|
||||
|
||||
for (auto image : mFreeImages) {
|
||||
vmaDestroyImage(mContext.allocator, image->image, image->memory);
|
||||
delete image;
|
||||
}
|
||||
mFreeStages.clear();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -33,7 +33,17 @@ struct VulkanStage {
|
||||
mutable uint64_t lastAccessed;
|
||||
};
|
||||
|
||||
struct VulkanStageImage {
|
||||
VkFormat format;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
mutable uint64_t lastAccessed;
|
||||
VmaAllocation memory;
|
||||
VkImage image;
|
||||
};
|
||||
|
||||
// Manages a pool of stages, periodically releasing stages that have been unused for a while.
|
||||
// This class manages two types of host-mappable staging areas: buffer stages and image stages.
|
||||
class VulkanStagePool {
|
||||
public:
|
||||
explicit VulkanStagePool(VulkanContext& context) noexcept : mContext(context) {}
|
||||
@@ -42,6 +52,9 @@ public:
|
||||
// The stage is automatically released back to the pool after TIME_BEFORE_EVICTION frames.
|
||||
VulkanStage const* acquireStage(uint32_t numBytes);
|
||||
|
||||
VulkanStageImage const* acquireImage(PixelDataFormat format, PixelDataType type,
|
||||
uint32_t width, uint32_t height);
|
||||
|
||||
// Evicts old unused stages and bumps the current frame number.
|
||||
void gc() noexcept;
|
||||
|
||||
@@ -58,6 +71,9 @@ private:
|
||||
// Simple unordered set for stashing a list of in-use stages that can be reclaimed later.
|
||||
std::unordered_set<VulkanStage const*> mUsedStages;
|
||||
|
||||
std::unordered_set<VulkanStageImage const*> mFreeImages;
|
||||
std::unordered_set<VulkanStageImage const*> mUsedImages;
|
||||
|
||||
// Store the current "time" (really just a frame count) and LRU eviction parameters.
|
||||
uint64_t mCurrentFrame = 0;
|
||||
};
|
||||
|
||||
@@ -236,48 +236,74 @@ void VulkanTexture::update2DImage(const PixelBufferDescriptor& data, uint32_t wi
|
||||
void VulkanTexture::update3DImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
|
||||
uint32_t depth, int miplevel) {
|
||||
assert_invariant(width <= this->width && height <= this->height && depth <= this->depth);
|
||||
const uint32_t numSrcBytes = data.size;
|
||||
const PixelBufferDescriptor* hostData = &data;
|
||||
PixelBufferDescriptor reshapedData;
|
||||
|
||||
uint32_t numDstBytes = numSrcBytes;
|
||||
PixelDataFormat dstFormat = data.format;
|
||||
void* mapped = nullptr;
|
||||
VulkanStage const* stage = nullptr;
|
||||
const void* clientData = data.buffer;
|
||||
|
||||
// Create and populate the staging buffer.
|
||||
// First, reshape 3-component data into 4-component data. The fourth component is usually
|
||||
// set to 1 (one exception is when type = HALF). In practice, alpha is just a dummy channel.
|
||||
// Note that the reshaped data is freed at the end of this method due to the callback.
|
||||
|
||||
if (data.format == PixelDataFormat::RGB) {
|
||||
const auto freeFunc = [](void* buffer, size_t size, void* user) { free(buffer); };
|
||||
const size_t reshapedSize = 4 * data.size / 3;
|
||||
const PixelDataFormat reshapedFormat = PixelDataFormat::RGBA;
|
||||
switch (data.type) {
|
||||
case PixelDataType::BYTE:
|
||||
case PixelDataType::UBYTE:
|
||||
numDstBytes = 4 * numSrcBytes / 3;
|
||||
dstFormat = PixelDataFormat::RGBA;
|
||||
stage = mStagePool.acquireStage(numDstBytes);
|
||||
vmaMapMemory(mContext.allocator, stage->memory, &mapped);
|
||||
DataReshaper::reshape<uint8_t, 3, 4>(mapped, clientData, numSrcBytes);
|
||||
case PixelDataType::UBYTE: {
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<uint8_t, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type, freeFunc);
|
||||
reshapedData = std::move(pbd);
|
||||
hostData = &reshapedData;
|
||||
break;
|
||||
}
|
||||
case PixelDataType::SHORT:
|
||||
case PixelDataType::USHORT:
|
||||
case PixelDataType::HALF:
|
||||
numDstBytes = 4 * numSrcBytes / 3;
|
||||
dstFormat = PixelDataFormat::RGBA;
|
||||
stage = mStagePool.acquireStage(numDstBytes);
|
||||
vmaMapMemory(mContext.allocator, stage->memory, &mapped);
|
||||
DataReshaper::reshape<uint16_t, 3, 4>(mapped, clientData, numSrcBytes);
|
||||
case PixelDataType::HALF: {
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<uint16_t, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type, freeFunc);
|
||||
reshapedData = std::move(pbd);
|
||||
hostData = &reshapedData;
|
||||
break;
|
||||
}
|
||||
case PixelDataType::INT:
|
||||
case PixelDataType::UINT:
|
||||
case PixelDataType::FLOAT: {
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<uint32_t, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type, freeFunc);
|
||||
reshapedData = std::move(pbd);
|
||||
hostData = &reshapedData;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numDstBytes == numSrcBytes) {
|
||||
stage = mStagePool.acquireStage(numDstBytes);
|
||||
vmaMapMemory(mContext.allocator, stage->memory, &mapped);
|
||||
memcpy(mapped, clientData, numSrcBytes);
|
||||
}
|
||||
// If format conversion is both required and supported, use vkCmdBlitImage. Otherwise, use
|
||||
// vkCmdCopyBufferToImage.
|
||||
|
||||
const VkFormat hostFormat = backend::getVkFormat(hostData->format, hostData->type);
|
||||
const VkFormat deviceFormat = getVkFormatLinear(mVkFormat);
|
||||
|
||||
if (hostFormat != deviceFormat && hostFormat != VK_FORMAT_UNDEFINED) {
|
||||
updateWithBlitImage(*hostData, width, height, depth, miplevel);
|
||||
} else {
|
||||
updateWithCopyBuffer(*hostData, width, height, depth, miplevel);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTexture::updateWithCopyBuffer(const PixelBufferDescriptor& hostData, uint32_t width,
|
||||
uint32_t height, uint32_t depth, int miplevel) {
|
||||
void* mapped = nullptr;
|
||||
VulkanStage const* stage = mStagePool.acquireStage(hostData.size);
|
||||
vmaMapMemory(mContext.allocator, stage->memory, &mapped);
|
||||
memcpy(mapped, hostData.buffer, hostData.size);
|
||||
vmaUnmapMemory(mContext.allocator, stage->memory);
|
||||
vmaFlushAllocation(mContext.allocator, stage->memory, 0, numDstBytes);
|
||||
vmaFlushAllocation(mContext.allocator, stage->memory, 0, hostData.size);
|
||||
|
||||
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
|
||||
transitionImageLayout(cmdbuffer, mTextureImage, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
@@ -286,7 +312,44 @@ void VulkanTexture::update3DImage(const PixelBufferDescriptor& data, uint32_t wi
|
||||
copyBufferToImage(cmdbuffer, stage->buffer, mTextureImage, width, height, depth,
|
||||
nullptr, miplevel);
|
||||
|
||||
transitionImageLayout(cmdbuffer, mTextureImage,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
transitionImageLayout(cmdbuffer, mTextureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
getTextureLayout(usage), miplevel, 1, 1, mAspect);
|
||||
}
|
||||
|
||||
void VulkanTexture::updateWithBlitImage(const PixelBufferDescriptor& hostData, uint32_t width,
|
||||
uint32_t height, uint32_t depth, int miplevel) {
|
||||
void* mapped = nullptr;
|
||||
VulkanStageImage const* stage = mStagePool.acquireImage(hostData.format, hostData.type,
|
||||
width, height);
|
||||
vmaMapMemory(mContext.allocator, stage->memory, &mapped);
|
||||
memcpy(mapped, hostData.buffer, hostData.size);
|
||||
vmaUnmapMemory(mContext.allocator, stage->memory);
|
||||
vmaFlushAllocation(mContext.allocator, stage->memory, 0, hostData.size);
|
||||
|
||||
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
|
||||
|
||||
// TODO: support blit-based format conversion for 3D images and cubemaps.
|
||||
const int layer = 0;
|
||||
|
||||
const VkOffset3D rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
|
||||
|
||||
const VkImageBlit blitRegions[1] = {{
|
||||
.srcSubresource = { mAspect, 0, 0, 1 },
|
||||
.srcOffsets = { rect[0], rect[1] },
|
||||
.dstSubresource = { mAspect, uint32_t(miplevel), layer, 1 },
|
||||
.dstOffsets = { rect[0], rect[1] }
|
||||
}};
|
||||
|
||||
transitionImageLayout(cmdbuffer, stage->image, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, miplevel, 1, 1, mAspect);
|
||||
|
||||
transitionImageLayout(cmdbuffer, mTextureImage, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, miplevel, 1, 1, mAspect);
|
||||
|
||||
vkCmdBlitImage(cmdbuffer, stage->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mTextureImage,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blitRegions, VK_FILTER_NEAREST);
|
||||
|
||||
transitionImageLayout(cmdbuffer, mTextureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
getTextureLayout(usage), miplevel, 1, 1, mAspect);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,12 @@ private:
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
FaceOffsets const* faceOffsets, uint32_t miplevel);
|
||||
|
||||
void updateWithCopyBuffer(const PixelBufferDescriptor& hostData, uint32_t width,
|
||||
uint32_t height, uint32_t depth, int miplevel);
|
||||
|
||||
void updateWithBlitImage(const PixelBufferDescriptor& hostData, uint32_t width,
|
||||
uint32_t height, uint32_t depth, int miplevel);
|
||||
|
||||
const VkFormat mVkFormat;
|
||||
const VkComponentMapping mSwizzle;
|
||||
VkImageViewType mViewType;
|
||||
|
||||
@@ -232,6 +232,99 @@ VkFormat getVkFormat(TextureFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
// Converts PixelBufferDescriptor format + type pair into a VkFormat.
|
||||
//
|
||||
// NOTE: This function only returns formats that support VK_FORMAT_FEATURE_BLIT_SRC_BIT as per
|
||||
// "Required Format Support" in the Vulkan specification. These are the only formats that can be
|
||||
// used for format conversion. If the requested format does not support this feature, then
|
||||
// VK_FORMAT_UNDEFINED is returned.
|
||||
VkFormat getVkFormat(PixelDataFormat format, PixelDataType type) {
|
||||
if (type == PixelDataType::USHORT_565) return VK_FORMAT_R5G6B5_UNORM_PACK16;
|
||||
if (type == PixelDataType::UINT_2_10_10_10_REV) return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
||||
if (type == PixelDataType::UINT_10F_11F_11F_REV) return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
|
||||
|
||||
#define CONVERT(FORMAT, TYPE, VK) \
|
||||
if (PixelDataFormat::FORMAT == format && PixelDataType::TYPE == type) return VK_FORMAT_ ## VK;
|
||||
|
||||
CONVERT(R, UBYTE, R8_UNORM);
|
||||
CONVERT(R, BYTE, R8_SNORM);
|
||||
CONVERT(R_INTEGER, UBYTE, R8_UINT);
|
||||
CONVERT(R_INTEGER, BYTE, R8_SINT);
|
||||
CONVERT(RG, UBYTE, R8G8_UNORM);
|
||||
CONVERT(RG, BYTE, R8G8_SNORM);
|
||||
CONVERT(RG_INTEGER, UBYTE, R8G8_UINT);
|
||||
CONVERT(RG_INTEGER, BYTE, R8G8_SINT);
|
||||
CONVERT(RGBA, UBYTE, R8G8B8A8_UNORM);
|
||||
CONVERT(RGBA, BYTE, R8G8B8A8_SNORM);
|
||||
CONVERT(RGBA_INTEGER, UBYTE, R8G8B8A8_UINT);
|
||||
CONVERT(RGBA_INTEGER, BYTE, R8G8B8A8_SINT);
|
||||
CONVERT(R_INTEGER, USHORT, R16_UINT);
|
||||
CONVERT(R_INTEGER, SHORT, R16_SINT);
|
||||
CONVERT(R, HALF, R16_SFLOAT);
|
||||
CONVERT(RG_INTEGER, USHORT, R16G16_UINT);
|
||||
CONVERT(RG_INTEGER, SHORT, R16G16_SINT);
|
||||
CONVERT(RG, HALF, R16G16_SFLOAT);
|
||||
CONVERT(RGBA_INTEGER, USHORT, R16G16B16A16_SINT);
|
||||
CONVERT(RGBA_INTEGER, SHORT, R16G16B16A16_SINT);
|
||||
CONVERT(RGBA, HALF, R16G16B16A16_SFLOAT);
|
||||
CONVERT(R_INTEGER, UINT, R32_UINT);
|
||||
CONVERT(R_INTEGER, INT, R32_SINT);
|
||||
CONVERT(R, FLOAT, R32_SFLOAT);
|
||||
CONVERT(RG_INTEGER, UINT, R32G32_UINT);
|
||||
CONVERT(RG_INTEGER, INT, R32G32_SINT);
|
||||
CONVERT(RG, FLOAT, R32G32_SFLOAT);
|
||||
CONVERT(RGBA_INTEGER, UINT, R32G32B32A32_UINT);
|
||||
CONVERT(RGBA_INTEGER, INT, R32G32B32A32_SINT);
|
||||
CONVERT(RGBA, FLOAT, R32G32B32A32_SFLOAT);
|
||||
#undef CONVERT
|
||||
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
// Converts an SRGB Vulkan format identifier into its corresponding canonical UNORM format. If the
|
||||
// given format is not an SRGB format, it is returned unmodified. This function is useful when
|
||||
// determining if blit-based conversion is needed, since SRGB is orthogonal to the actual bit layout
|
||||
// of color components.
|
||||
VkFormat getVkFormatLinear(VkFormat format) {
|
||||
switch (format) {
|
||||
case VK_FORMAT_R8_SRGB: return VK_FORMAT_R8_UNORM;
|
||||
case VK_FORMAT_R8G8_SRGB: return VK_FORMAT_R8G8_UNORM;
|
||||
case VK_FORMAT_R8G8B8_SRGB: return VK_FORMAT_R8G8B8_UNORM;
|
||||
case VK_FORMAT_B8G8R8_SRGB: return VK_FORMAT_B8G8R8_UNORM;
|
||||
case VK_FORMAT_R8G8B8A8_SRGB: return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case VK_FORMAT_B8G8R8A8_SRGB: return VK_FORMAT_B8G8R8A8_UNORM;
|
||||
case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
case VK_FORMAT_BC1_RGB_SRGB_BLOCK: return VK_FORMAT_BC1_RGB_UNORM_BLOCK;
|
||||
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
|
||||
case VK_FORMAT_BC2_SRGB_BLOCK: return VK_FORMAT_BC2_UNORM_BLOCK;
|
||||
case VK_FORMAT_BC3_SRGB_BLOCK: return VK_FORMAT_BC3_UNORM_BLOCK;
|
||||
case VK_FORMAT_BC7_SRGB_BLOCK: return VK_FORMAT_BC7_UNORM_BLOCK;
|
||||
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
|
||||
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
|
||||
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: return VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: return VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: return VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: return VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: return VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: return VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: return VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: return VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: return VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: return VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: return VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: return VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: return VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
|
||||
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: return VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
|
||||
case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG;
|
||||
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
|
||||
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: return VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG;
|
||||
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: return VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;
|
||||
default: return format;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
// See also FTexture::computeTextureDataSize, which takes a public-facing Texture format rather
|
||||
// than a driver-level Texture format, and can account for a specified byte alignment.
|
||||
uint32_t getBytesPerPixel(TextureFormat format) {
|
||||
|
||||
@@ -38,6 +38,8 @@ struct VulkanLayoutTransition {
|
||||
void createSemaphore(VkDevice device, VkSemaphore* semaphore);
|
||||
VkFormat getVkFormat(ElementType type, bool normalized, bool integer);
|
||||
VkFormat getVkFormat(TextureFormat format);
|
||||
VkFormat getVkFormat(PixelDataFormat format, PixelDataType type);
|
||||
VkFormat getVkFormatLinear(VkFormat format);
|
||||
uint32_t getBytesPerPixel(TextureFormat format);
|
||||
VkCompareOp getCompareOp(SamplerCompareFunc func);
|
||||
VkBlendFactor getBlendFactor(BlendFunction mode);
|
||||
|
||||
Reference in New Issue
Block a user