Make Vulkan testing-only backend textures have optimal layout
Change-Id: I984a6a657417565c36687d32e41b5f7d9bbc39f8 Reviewed-on: https://skia-review.googlesource.com/112800 Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
7578f3ecdd
commit
de9f546a04
@ -1191,7 +1191,6 @@ GrBackendTexture GrVkGpu::createTestingOnlyBackendTexture(void* srcData, int w,
|
|||||||
return GrBackendTexture(); // invalid
|
return GrBackendTexture(); // invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
bool linearTiling = false;
|
|
||||||
if (!fVkCaps->isConfigTexturable(config)) {
|
if (!fVkCaps->isConfigTexturable(config)) {
|
||||||
return GrBackendTexture(); // invalid
|
return GrBackendTexture(); // invalid
|
||||||
}
|
}
|
||||||
@ -1205,12 +1204,6 @@ GrBackendTexture GrVkGpu::createTestingOnlyBackendTexture(void* srcData, int w,
|
|||||||
return GrBackendTexture(); // invalid
|
return GrBackendTexture(); // invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fVkCaps->isConfigTexturableLinearly(config) &&
|
|
||||||
(!isRenderTarget || fVkCaps->isConfigRenderableLinearly(config, false)) &&
|
|
||||||
GrMipMapped::kNo == mipMapped) {
|
|
||||||
linearTiling = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT;
|
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
usageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
usageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||||
usageFlags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
usageFlags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
@ -1220,11 +1213,7 @@ GrBackendTexture GrVkGpu::createTestingOnlyBackendTexture(void* srcData, int w,
|
|||||||
|
|
||||||
VkImage image = VK_NULL_HANDLE;
|
VkImage image = VK_NULL_HANDLE;
|
||||||
GrVkAlloc alloc;
|
GrVkAlloc alloc;
|
||||||
|
VkImageLayout initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
VkImageTiling imageTiling = linearTiling ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL;
|
|
||||||
VkImageLayout initialLayout = (VK_IMAGE_TILING_LINEAR == imageTiling)
|
|
||||||
? VK_IMAGE_LAYOUT_PREINITIALIZED
|
|
||||||
: VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
|
|
||||||
// Create Image
|
// Create Image
|
||||||
VkSampleCountFlagBits vkSamples;
|
VkSampleCountFlagBits vkSamples;
|
||||||
@ -1239,26 +1228,26 @@ GrBackendTexture GrVkGpu::createTestingOnlyBackendTexture(void* srcData, int w,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const VkImageCreateInfo imageCreateInfo = {
|
const VkImageCreateInfo imageCreateInfo = {
|
||||||
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
|
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
|
||||||
nullptr, // pNext
|
nullptr, // pNext
|
||||||
0, // VkImageCreateFlags
|
0, // VkImageCreateFlags
|
||||||
VK_IMAGE_TYPE_2D, // VkImageType
|
VK_IMAGE_TYPE_2D, // VkImageType
|
||||||
pixelFormat, // VkFormat
|
pixelFormat, // VkFormat
|
||||||
{ (uint32_t) w, (uint32_t) h, 1 }, // VkExtent3D
|
{(uint32_t)w, (uint32_t)h, 1}, // VkExtent3D
|
||||||
mipLevels, // mipLevels
|
mipLevels, // mipLevels
|
||||||
1, // arrayLayers
|
1, // arrayLayers
|
||||||
vkSamples, // samples
|
vkSamples, // samples
|
||||||
imageTiling, // VkImageTiling
|
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling
|
||||||
usageFlags, // VkImageUsageFlags
|
usageFlags, // VkImageUsageFlags
|
||||||
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode
|
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode
|
||||||
0, // queueFamilyCount
|
0, // queueFamilyCount
|
||||||
0, // pQueueFamilyIndices
|
0, // pQueueFamilyIndices
|
||||||
initialLayout // initialLayout
|
initialLayout // initialLayout
|
||||||
};
|
};
|
||||||
|
|
||||||
GR_VK_CALL_ERRCHECK(this->vkInterface(), CreateImage(this->device(), &imageCreateInfo, nullptr, &image));
|
GR_VK_CALL_ERRCHECK(this->vkInterface(), CreateImage(this->device(), &imageCreateInfo, nullptr, &image));
|
||||||
|
|
||||||
if (!GrVkMemory::AllocAndBindImageMemory(this, image, linearTiling, &alloc)) {
|
if (!GrVkMemory::AllocAndBindImageMemory(this, image, false, &alloc)) {
|
||||||
VK_CALL(DestroyImage(this->device(), image, nullptr));
|
VK_CALL(DestroyImage(this->device(), image, nullptr));
|
||||||
return GrBackendTexture(); // invalid
|
return GrBackendTexture(); // invalid
|
||||||
}
|
}
|
||||||
@ -1295,147 +1284,122 @@ GrBackendTexture GrVkGpu::createTestingOnlyBackendTexture(void* srcData, int w,
|
|||||||
SkASSERT(!err);
|
SkASSERT(!err);
|
||||||
|
|
||||||
size_t bpp = GrBytesPerPixel(config);
|
size_t bpp = GrBytesPerPixel(config);
|
||||||
size_t rowCopyBytes = bpp * w;
|
SkASSERT(w && h);
|
||||||
if (linearTiling) {
|
|
||||||
const VkImageSubresource subres = {
|
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
|
||||||
0, // mipLevel
|
|
||||||
0, // arraySlice
|
|
||||||
};
|
|
||||||
VkSubresourceLayout layout;
|
|
||||||
|
|
||||||
VK_CALL(GetImageSubresourceLayout(fDevice, image, &subres, &layout));
|
SkTArray<size_t> individualMipOffsets(mipLevels);
|
||||||
|
individualMipOffsets.push_back(0);
|
||||||
|
size_t combinedBufferSize = w * bpp * h;
|
||||||
|
int currentWidth = w;
|
||||||
|
int currentHeight = h;
|
||||||
|
// The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image
|
||||||
|
// config. This works with the assumption that the bytes in pixel config is always a power
|
||||||
|
// of 2.
|
||||||
|
SkASSERT((bpp & (bpp - 1)) == 0);
|
||||||
|
const size_t alignmentMask = 0x3 | (bpp - 1);
|
||||||
|
for (uint32_t currentMipLevel = 1; currentMipLevel < mipLevels; currentMipLevel++) {
|
||||||
|
currentWidth = SkTMax(1, currentWidth / 2);
|
||||||
|
currentHeight = SkTMax(1, currentHeight / 2);
|
||||||
|
|
||||||
if (!copy_testing_data(this, srcData, alloc, 0, rowCopyBytes,
|
const size_t trimmedSize = currentWidth * bpp * currentHeight;
|
||||||
static_cast<size_t>(layout.rowPitch), h)) {
|
const size_t alignmentDiff = combinedBufferSize & alignmentMask;
|
||||||
GrVkMemory::FreeImageMemory(this, true, alloc);
|
if (alignmentDiff != 0) {
|
||||||
VK_CALL(DestroyImage(fDevice, image, nullptr));
|
combinedBufferSize += alignmentMask - alignmentDiff + 1;
|
||||||
VK_CALL(EndCommandBuffer(cmdBuffer));
|
|
||||||
VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
|
|
||||||
return GrBackendTexture(); // invalid
|
|
||||||
}
|
}
|
||||||
} else {
|
individualMipOffsets.push_back(combinedBufferSize);
|
||||||
SkASSERT(w && h);
|
combinedBufferSize += trimmedSize;
|
||||||
|
}
|
||||||
|
|
||||||
SkTArray<size_t> individualMipOffsets(mipLevels);
|
VkBufferCreateInfo bufInfo;
|
||||||
individualMipOffsets.push_back(0);
|
memset(&bufInfo, 0, sizeof(VkBufferCreateInfo));
|
||||||
size_t combinedBufferSize = w * bpp * h;
|
bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
int currentWidth = w;
|
bufInfo.flags = 0;
|
||||||
int currentHeight = h;
|
bufInfo.size = combinedBufferSize;
|
||||||
// The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image
|
bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||||
// config. This works with the assumption that the bytes in pixel config is always a power
|
bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
// of 2.
|
bufInfo.queueFamilyIndexCount = 0;
|
||||||
SkASSERT((bpp & (bpp - 1)) == 0);
|
bufInfo.pQueueFamilyIndices = nullptr;
|
||||||
const size_t alignmentMask = 0x3 | (bpp - 1);
|
err = VK_CALL(CreateBuffer(fDevice, &bufInfo, nullptr, &buffer));
|
||||||
for (uint32_t currentMipLevel = 1; currentMipLevel < mipLevels; currentMipLevel++) {
|
|
||||||
currentWidth = SkTMax(1, currentWidth/2);
|
|
||||||
currentHeight = SkTMax(1, currentHeight/2);
|
|
||||||
|
|
||||||
const size_t trimmedSize = currentWidth * bpp * currentHeight;
|
if (err) {
|
||||||
const size_t alignmentDiff = combinedBufferSize & alignmentMask;
|
GrVkMemory::FreeImageMemory(this, false, alloc);
|
||||||
if (alignmentDiff != 0) {
|
VK_CALL(DestroyImage(fDevice, image, nullptr));
|
||||||
combinedBufferSize += alignmentMask - alignmentDiff + 1;
|
VK_CALL(EndCommandBuffer(cmdBuffer));
|
||||||
}
|
VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
|
||||||
individualMipOffsets.push_back(combinedBufferSize);
|
return GrBackendTexture(); // invalid
|
||||||
combinedBufferSize += trimmedSize;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
VkBufferCreateInfo bufInfo;
|
if (!GrVkMemory::AllocAndBindBufferMemory(this, buffer, GrVkBuffer::kCopyRead_Type, true,
|
||||||
memset(&bufInfo, 0, sizeof(VkBufferCreateInfo));
|
&bufferAlloc)) {
|
||||||
bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
GrVkMemory::FreeImageMemory(this, false, alloc);
|
||||||
bufInfo.flags = 0;
|
VK_CALL(DestroyImage(fDevice, image, nullptr));
|
||||||
bufInfo.size = combinedBufferSize;
|
VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
|
||||||
bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
VK_CALL(EndCommandBuffer(cmdBuffer));
|
||||||
bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
|
||||||
bufInfo.queueFamilyIndexCount = 0;
|
return GrBackendTexture(); // invalid
|
||||||
bufInfo.pQueueFamilyIndices = nullptr;
|
}
|
||||||
err = VK_CALL(CreateBuffer(fDevice, &bufInfo, nullptr, &buffer));
|
|
||||||
|
|
||||||
if (err) {
|
currentWidth = w;
|
||||||
GrVkMemory::FreeImageMemory(this, false, alloc);
|
currentHeight = h;
|
||||||
VK_CALL(DestroyImage(fDevice, image, nullptr));
|
for (uint32_t currentMipLevel = 0; currentMipLevel < mipLevels; currentMipLevel++) {
|
||||||
VK_CALL(EndCommandBuffer(cmdBuffer));
|
SkASSERT(0 == currentMipLevel || !srcData);
|
||||||
VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
|
size_t currentRowBytes = bpp * currentWidth;
|
||||||
return GrBackendTexture(); // invalid
|
size_t bufferOffset = individualMipOffsets[currentMipLevel];
|
||||||
}
|
if (!copy_testing_data(this, srcData, bufferAlloc, bufferOffset, currentRowBytes,
|
||||||
|
currentRowBytes, currentHeight)) {
|
||||||
if (!GrVkMemory::AllocAndBindBufferMemory(this, buffer, GrVkBuffer::kCopyRead_Type,
|
|
||||||
true, &bufferAlloc)) {
|
|
||||||
GrVkMemory::FreeImageMemory(this, false, alloc);
|
GrVkMemory::FreeImageMemory(this, false, alloc);
|
||||||
VK_CALL(DestroyImage(fDevice, image, nullptr));
|
VK_CALL(DestroyImage(fDevice, image, nullptr));
|
||||||
|
GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
|
||||||
VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
|
VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
|
||||||
VK_CALL(EndCommandBuffer(cmdBuffer));
|
VK_CALL(EndCommandBuffer(cmdBuffer));
|
||||||
VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
|
VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
|
||||||
return GrBackendTexture(); // invalid
|
return GrBackendTexture(); // invalid
|
||||||
}
|
}
|
||||||
|
currentWidth = SkTMax(1, currentWidth / 2);
|
||||||
currentWidth = w;
|
currentHeight = SkTMax(1, currentHeight / 2);
|
||||||
currentHeight = h;
|
|
||||||
for (uint32_t currentMipLevel = 0; currentMipLevel < mipLevels; currentMipLevel++) {
|
|
||||||
SkASSERT(0 == currentMipLevel || !srcData);
|
|
||||||
size_t currentRowBytes = bpp * currentWidth;
|
|
||||||
size_t bufferOffset = individualMipOffsets[currentMipLevel];
|
|
||||||
if (!copy_testing_data(this, srcData, bufferAlloc, bufferOffset,
|
|
||||||
currentRowBytes, currentRowBytes, currentHeight)) {
|
|
||||||
GrVkMemory::FreeImageMemory(this, false, alloc);
|
|
||||||
VK_CALL(DestroyImage(fDevice, image, nullptr));
|
|
||||||
GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
|
|
||||||
VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
|
|
||||||
VK_CALL(EndCommandBuffer(cmdBuffer));
|
|
||||||
VK_CALL(FreeCommandBuffers(fDevice, fCmdPool, 1, &cmdBuffer));
|
|
||||||
return GrBackendTexture(); // invalid
|
|
||||||
}
|
|
||||||
currentWidth = SkTMax(1, currentWidth/2);
|
|
||||||
currentHeight = SkTMax(1, currentHeight/2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set image layout and add barrier
|
|
||||||
VkImageMemoryBarrier barrier;
|
|
||||||
memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
|
|
||||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
||||||
barrier.pNext = nullptr;
|
|
||||||
barrier.srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(initialLayout);
|
|
||||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
||||||
barrier.oldLayout = initialLayout;
|
|
||||||
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
||||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
barrier.image = image;
|
|
||||||
barrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, mipLevels, 0 , 1};
|
|
||||||
|
|
||||||
VK_CALL(CmdPipelineBarrier(cmdBuffer,
|
|
||||||
GrVkMemory::LayoutToPipelineStageFlags(initialLayout),
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
0,
|
|
||||||
0, nullptr,
|
|
||||||
0, nullptr,
|
|
||||||
1, &barrier));
|
|
||||||
initialLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
||||||
|
|
||||||
SkTArray<VkBufferImageCopy> regions(mipLevels);
|
|
||||||
|
|
||||||
currentWidth = w;
|
|
||||||
currentHeight = h;
|
|
||||||
for (uint32_t currentMipLevel = 0; currentMipLevel < mipLevels; currentMipLevel++) {
|
|
||||||
// Submit copy command
|
|
||||||
VkBufferImageCopy& region = regions.push_back();
|
|
||||||
memset(®ion, 0, sizeof(VkBufferImageCopy));
|
|
||||||
region.bufferOffset = individualMipOffsets[currentMipLevel];
|
|
||||||
region.bufferRowLength = currentWidth;
|
|
||||||
region.bufferImageHeight = currentHeight;
|
|
||||||
region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
|
|
||||||
region.imageOffset = { 0, 0, 0 };
|
|
||||||
region.imageExtent = { (uint32_t)currentWidth, (uint32_t)currentHeight, 1 };
|
|
||||||
currentWidth = SkTMax(1, currentWidth/2);
|
|
||||||
currentHeight = SkTMax(1, currentHeight/2);
|
|
||||||
}
|
|
||||||
|
|
||||||
VK_CALL(CmdCopyBufferToImage(cmdBuffer, buffer, image, initialLayout, regions.count(),
|
|
||||||
regions.begin()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set image layout and add barrier
|
||||||
|
VkImageMemoryBarrier barrier;
|
||||||
|
memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
|
||||||
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
barrier.pNext = nullptr;
|
||||||
|
barrier.srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(initialLayout);
|
||||||
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
barrier.oldLayout = initialLayout;
|
||||||
|
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.image = image;
|
||||||
|
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, mipLevels, 0, 1};
|
||||||
|
|
||||||
|
VK_CALL(CmdPipelineBarrier(cmdBuffer, GrVkMemory::LayoutToPipelineStageFlags(initialLayout),
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
||||||
|
&barrier));
|
||||||
|
initialLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
|
||||||
|
SkTArray<VkBufferImageCopy> regions(mipLevels);
|
||||||
|
|
||||||
|
currentWidth = w;
|
||||||
|
currentHeight = h;
|
||||||
|
for (uint32_t currentMipLevel = 0; currentMipLevel < mipLevels; currentMipLevel++) {
|
||||||
|
// Submit copy command
|
||||||
|
VkBufferImageCopy& region = regions.push_back();
|
||||||
|
memset(®ion, 0, sizeof(VkBufferImageCopy));
|
||||||
|
region.bufferOffset = individualMipOffsets[currentMipLevel];
|
||||||
|
region.bufferRowLength = currentWidth;
|
||||||
|
region.bufferImageHeight = currentHeight;
|
||||||
|
region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||||
|
region.imageOffset = {0, 0, 0};
|
||||||
|
region.imageExtent = {(uint32_t)currentWidth, (uint32_t)currentHeight, 1};
|
||||||
|
currentWidth = SkTMax(1, currentWidth / 2);
|
||||||
|
currentHeight = SkTMax(1, currentHeight / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
VK_CALL(CmdCopyBufferToImage(cmdBuffer, buffer, image, initialLayout, regions.count(),
|
||||||
|
regions.begin()));
|
||||||
|
|
||||||
// Change Image layout to shader read since if we use this texture as a borrowed textures within
|
// Change Image layout to shader read since if we use this texture as a borrowed textures within
|
||||||
// Ganesh we require that its layout be set to that
|
// Ganesh we require that its layout be set to that
|
||||||
VkImageMemoryBarrier barrier;
|
|
||||||
memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
|
memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
|
||||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
barrier.pNext = nullptr;
|
barrier.pNext = nullptr;
|
||||||
@ -1508,7 +1472,7 @@ GrBackendTexture GrVkGpu::createTestingOnlyBackendTexture(void* srcData, int w,
|
|||||||
GrVkImageInfo info;
|
GrVkImageInfo info;
|
||||||
info.fImage = image;
|
info.fImage = image;
|
||||||
info.fAlloc = alloc;
|
info.fAlloc = alloc;
|
||||||
info.fImageTiling = imageTiling;
|
info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
info.fImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
info.fImageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
info.fFormat = pixelFormat;
|
info.fFormat = pixelFormat;
|
||||||
info.fLevelCount = mipLevels;
|
info.fLevelCount = mipLevels;
|
||||||
|
Loading…
Reference in New Issue
Block a user