mirror of
https://github.com/love2d/love.git
synced 2026-08-15 15:51:12 +02:00
Add texture views. Fixes #2022.
Add love.graphics.newTextureView(basetexture, viewsettings). Requires GLSL4 support. Add 'viewformats' boolean setting to texture setting tables. Add Texture:hasViewFormats. The viewsettings table has the following fields: format = nil, -- set this to a pixel format to override the base format. It must have the same number of bytes per pixel as the original, and the 'viewformats' setting on the original texture must be true for this to work. Cannot be used for compressed or depth/stencil textures. type = nil, -- set this to a texture type to override the base texture type. 2d base textures can make [2d, array] views. [array, cube] base textures can make [2d, array, cube] views. mipmapstart = nil, -- set to a number to use a less detailed mipmap level as a base. mipmapcount = nil, -- set to a number to override the number of mipmaps in the texture view. layerstart = nil, -- set to a number to use a specific layer or cube face as a base. layers = nil, -- set to a number to override the number of layers in an array texture view.
This commit is contained in:
@@ -47,11 +47,22 @@ Texture::Texture(love::graphics::Graphics *gfx, const Settings &settings, const
|
||||
slices.clear();
|
||||
}
|
||||
|
||||
Texture::Texture(love::graphics::Graphics *gfx, love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
|
||||
: love::graphics::Texture(gfx, base, viewsettings)
|
||||
, vgfx(dynamic_cast<Graphics*>(gfx))
|
||||
, slices(viewsettings.type.get(base->getTextureType()))
|
||||
, imageAspect(0)
|
||||
{
|
||||
loadVolatile();
|
||||
}
|
||||
|
||||
bool Texture::loadVolatile()
|
||||
{
|
||||
allocator = vgfx->getVmaAllocator();
|
||||
device = vgfx->getDevice();
|
||||
|
||||
bool root = rootView.texture == this;
|
||||
|
||||
if (isPixelFormatDepth(format))
|
||||
imageAspect |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
if (isPixelFormatStencil(format))
|
||||
@@ -79,82 +90,96 @@ bool Texture::loadVolatile()
|
||||
usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
}
|
||||
|
||||
VkImageCreateFlags createFlags = 0;
|
||||
|
||||
layerCount = 1;
|
||||
|
||||
if (texType == TEXTURE_2D_ARRAY)
|
||||
layerCount = getLayerCount();
|
||||
else if (texType == TEXTURE_CUBE)
|
||||
{
|
||||
layerCount = 6;
|
||||
createFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
}
|
||||
|
||||
msaaSamples = vgfx->getMsaaCount(requestedMSAA);
|
||||
|
||||
VkImageCreateInfo imageInfo{};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.flags = createFlags;
|
||||
imageInfo.imageType = Vulkan::getImageType(getTextureType());
|
||||
imageInfo.extent.width = static_cast<uint32_t>(pixelWidth);
|
||||
imageInfo.extent.height = static_cast<uint32_t>(pixelHeight);
|
||||
imageInfo.extent.depth = static_cast<uint32_t>(depth);
|
||||
imageInfo.arrayLayers = static_cast<uint32_t>(layerCount);
|
||||
imageInfo.mipLevels = static_cast<uint32_t>(mipmapCount);
|
||||
imageInfo.format = vulkanFormat.internalFormat;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = usageFlags;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.samples = msaaSamples;
|
||||
|
||||
VmaAllocationCreateInfo imageAllocationCreateInfo{};
|
||||
|
||||
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS)
|
||||
throw love::Exception("failed to create image");
|
||||
|
||||
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
|
||||
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
else if (computeWrite)
|
||||
imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
else
|
||||
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, imageLayout,
|
||||
0, VK_REMAINING_MIP_LEVELS,
|
||||
0, VK_REMAINING_ARRAY_LAYERS);
|
||||
|
||||
bool hasdata = slices.get(0, 0) != nullptr;
|
||||
|
||||
if (hasdata)
|
||||
if (root)
|
||||
{
|
||||
for (int mip = 0; mip < getMipmapCount(); mip++)
|
||||
{
|
||||
int sliceCount;
|
||||
if (texType == TEXTURE_CUBE)
|
||||
sliceCount = 6;
|
||||
else
|
||||
sliceCount = slices.getSliceCount();
|
||||
VkImageCreateFlags createFlags = 0;
|
||||
|
||||
for (int slice = 0; slice < sliceCount; slice++)
|
||||
if (viewFormats)
|
||||
createFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
|
||||
if (texType == TEXTURE_CUBE || texType == TEXTURE_2D_ARRAY)
|
||||
createFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
|
||||
|
||||
msaaSamples = vgfx->getMsaaCount(requestedMSAA);
|
||||
|
||||
VkImageCreateInfo imageInfo{};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.flags = createFlags;
|
||||
imageInfo.imageType = Vulkan::getImageType(getTextureType());
|
||||
imageInfo.extent.width = static_cast<uint32_t>(pixelWidth);
|
||||
imageInfo.extent.height = static_cast<uint32_t>(pixelHeight);
|
||||
imageInfo.extent.depth = static_cast<uint32_t>(depth);
|
||||
imageInfo.arrayLayers = static_cast<uint32_t>(layerCount);
|
||||
imageInfo.mipLevels = static_cast<uint32_t>(mipmapCount);
|
||||
imageInfo.format = vulkanFormat.internalFormat;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = usageFlags;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.samples = msaaSamples;
|
||||
|
||||
VmaAllocationCreateInfo imageAllocationCreateInfo{};
|
||||
|
||||
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS)
|
||||
throw love::Exception("failed to create image");
|
||||
|
||||
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
|
||||
|
||||
if (isPixelFormatDepthStencil(format))
|
||||
imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
else if (computeWrite)
|
||||
imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
else
|
||||
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, imageLayout,
|
||||
0, VK_REMAINING_MIP_LEVELS,
|
||||
0, VK_REMAINING_ARRAY_LAYERS);
|
||||
|
||||
bool hasdata = slices.get(0, 0) != nullptr;
|
||||
|
||||
if (hasdata)
|
||||
{
|
||||
for (int mip = 0; mip < getMipmapCount(); mip++)
|
||||
{
|
||||
auto id = slices.get(slice, mip);
|
||||
if (id != nullptr)
|
||||
uploadImageData(id, mip, slice, 0, 0);
|
||||
int sliceCount;
|
||||
if (texType == TEXTURE_CUBE)
|
||||
sliceCount = 6;
|
||||
else
|
||||
sliceCount = slices.getSliceCount();
|
||||
|
||||
for (int slice = 0; slice < sliceCount; slice++)
|
||||
{
|
||||
auto id = slices.get(slice, mip);
|
||||
if (id != nullptr)
|
||||
uploadImageData(id, mip, slice, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
clear();
|
||||
}
|
||||
else
|
||||
clear();
|
||||
{
|
||||
Texture *roottex = (Texture *) rootView.texture;
|
||||
textureImage = roottex->textureImage;
|
||||
textureImageAllocation = VK_NULL_HANDLE;
|
||||
imageLayout = roottex->imageLayout;
|
||||
msaaSamples = roottex->msaaSamples;
|
||||
}
|
||||
|
||||
createTextureImageView();
|
||||
textureSampler = vgfx->getCachedSampler(samplerState);
|
||||
setSamplerState(samplerState);
|
||||
|
||||
if (!isPixelFormatDepthStencil(format) && slices.getMipmapCount() <= 1 && getMipmapsMode() != MIPMAPS_NONE)
|
||||
if (root && !isPixelFormatDepthStencil(format) && slices.getMipmapCount() <= 1 && getMipmapsMode() != MIPMAPS_NONE)
|
||||
generateMipmaps();
|
||||
|
||||
if (renderTarget)
|
||||
@@ -172,9 +197,9 @@ bool Texture::loadVolatile()
|
||||
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
|
||||
viewInfo.format = vulkanFormat.internalFormat;
|
||||
viewInfo.subresourceRange.aspectMask = imageAspect;
|
||||
viewInfo.subresourceRange.baseMipLevel = mip;
|
||||
viewInfo.subresourceRange.baseMipLevel = mip + rootView.startMipmap;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = slice;
|
||||
viewInfo.subresourceRange.baseArrayLayer = slice + rootView.startLayer;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
viewInfo.components.r = vulkanFormat.swizzleR;
|
||||
viewInfo.components.g = vulkanFormat.swizzleG;
|
||||
@@ -189,15 +214,18 @@ bool Texture::loadVolatile()
|
||||
|
||||
int64 memsize = 0;
|
||||
|
||||
for (int mip = 0; mip < getMipmapCount(); mip++)
|
||||
if (root)
|
||||
{
|
||||
int w = getPixelWidth(mip);
|
||||
int h = getPixelHeight(mip);
|
||||
int slices = getDepth(mip) * layerCount;
|
||||
memsize += getPixelFormatSliceSize(format, w, h) * slices;
|
||||
}
|
||||
for (int mip = 0; mip < getMipmapCount(); mip++)
|
||||
{
|
||||
int w = getPixelWidth(mip);
|
||||
int h = getPixelHeight(mip);
|
||||
int slices = getDepth(mip) * layerCount;
|
||||
memsize += getPixelFormatSliceSize(format, w, h) * slices;
|
||||
}
|
||||
|
||||
memsize *= static_cast<int>(msaaSamples);
|
||||
memsize *= static_cast<int>(msaaSamples);
|
||||
}
|
||||
|
||||
setGraphicsMemorySize(memsize);
|
||||
|
||||
@@ -207,8 +235,16 @@ bool Texture::loadVolatile()
|
||||
{
|
||||
VkDebugUtilsObjectNameInfoEXT nameInfo{};
|
||||
nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;
|
||||
nameInfo.objectHandle = (uint64_t)textureImage;
|
||||
if (root)
|
||||
{
|
||||
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;
|
||||
nameInfo.objectHandle = (uint64_t)textureImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE_VIEW;
|
||||
nameInfo.objectHandle = (uint64_t)textureImageView;
|
||||
}
|
||||
nameInfo.pObjectName = debugName.c_str();
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
@@ -230,13 +266,15 @@ void Texture::unloadVolatile()
|
||||
textureImageAllocation = textureImageAllocation,
|
||||
textureImageViews = std::move(renderTargetImageViews)] () {
|
||||
vkDestroyImageView(device, textureImageView, nullptr);
|
||||
vmaDestroyImage(allocator, textureImage, textureImageAllocation);
|
||||
if (textureImageAllocation)
|
||||
vmaDestroyImage(allocator, textureImage, textureImageAllocation);
|
||||
for (const auto &views : textureImageViews)
|
||||
for (const auto &view : views)
|
||||
vkDestroyImageView(device, view, nullptr);
|
||||
});
|
||||
|
||||
textureImage = VK_NULL_HANDLE;
|
||||
textureImageAllocation = VK_NULL_HANDLE;
|
||||
|
||||
setGraphicsMemorySize(0);
|
||||
}
|
||||
@@ -278,8 +316,7 @@ ptrdiff_t Texture::getHandle() const
|
||||
|
||||
void Texture::setSamplerState(const SamplerState &s)
|
||||
{
|
||||
love::graphics::Texture::setSamplerState(s);
|
||||
|
||||
samplerState = validateSamplerState(s);
|
||||
textureSampler = vgfx->getCachedSampler(samplerState);
|
||||
}
|
||||
|
||||
@@ -291,16 +328,15 @@ VkImageLayout Texture::getImageLayout() const
|
||||
void Texture::createTextureImageView()
|
||||
{
|
||||
auto vulkanFormat = Vulkan::getTextureFormat(format);
|
||||
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = textureImage;
|
||||
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
|
||||
viewInfo.format = vulkanFormat.internalFormat;
|
||||
viewInfo.subresourceRange.aspectMask = imageAspect;
|
||||
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||
viewInfo.subresourceRange.baseMipLevel = rootView.startMipmap;
|
||||
viewInfo.subresourceRange.levelCount = getMipmapCount();
|
||||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||
viewInfo.subresourceRange.baseArrayLayer = rootView.startLayer;
|
||||
viewInfo.subresourceRange.layerCount = layerCount;
|
||||
viewInfo.components.r = vulkanFormat.swizzleR;
|
||||
viewInfo.components.g = vulkanFormat.swizzleG;
|
||||
@@ -404,16 +440,16 @@ void Texture::generateMipmapsInternal()
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
barrier.subresourceRange.baseArrayLayer = 0;
|
||||
barrier.subresourceRange.baseArrayLayer = rootView.startLayer;
|
||||
barrier.subresourceRange.layerCount = static_cast<uint32_t>(layerCount);
|
||||
barrier.subresourceRange.baseMipLevel = 0;
|
||||
barrier.subresourceRange.baseMipLevel = rootView.startMipmap;
|
||||
barrier.subresourceRange.levelCount = 1u;
|
||||
|
||||
uint32_t mipLevels = static_cast<uint32_t>(getMipmapCount());
|
||||
|
||||
for (uint32_t i = 1; i < mipLevels; i++)
|
||||
{
|
||||
barrier.subresourceRange.baseMipLevel = i - 1;
|
||||
barrier.subresourceRange.baseMipLevel = rootView.startMipmap + i - 1;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
@@ -429,15 +465,15 @@ void Texture::generateMipmapsInternal()
|
||||
blit.srcOffsets[0] = { 0, 0, 0 };
|
||||
blit.srcOffsets[1] = { getPixelWidth(i - 1), getPixelHeight(i - 1), 1 };
|
||||
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
blit.srcSubresource.mipLevel = i - 1;
|
||||
blit.srcSubresource.baseArrayLayer = 0;
|
||||
blit.srcSubresource.mipLevel = rootView.startMipmap + i - 1;
|
||||
blit.srcSubresource.baseArrayLayer = rootView.startLayer;
|
||||
blit.srcSubresource.layerCount = static_cast<uint32_t>(layerCount);
|
||||
|
||||
blit.dstOffsets[0] = { 0, 0, 0 };
|
||||
blit.dstOffsets[1] = { getPixelWidth(i), getPixelHeight(i), 1 };
|
||||
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
blit.dstSubresource.mipLevel = i;
|
||||
blit.dstSubresource.baseArrayLayer = 0;
|
||||
blit.dstSubresource.mipLevel = rootView.startMipmap + i;
|
||||
blit.dstSubresource.baseArrayLayer = rootView.startLayer;
|
||||
blit.dstSubresource.layerCount = static_cast<uint32_t>(layerCount);
|
||||
|
||||
vkCmdBlitImage(commandBuffer,
|
||||
@@ -458,7 +494,7 @@ void Texture::generateMipmapsInternal()
|
||||
1, &barrier);
|
||||
}
|
||||
|
||||
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
|
||||
barrier.subresourceRange.baseMipLevel = rootView.startMipmap + mipLevels - 1;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
@@ -496,11 +532,11 @@ void Texture::uploadByteData(const void *data, size_t size, int level, int slice
|
||||
region.bufferRowLength = 0;
|
||||
region.bufferImageHeight = 0;
|
||||
|
||||
uint32_t baseLayer;
|
||||
if (getTextureType() == TEXTURE_VOLUME)
|
||||
baseLayer = 0;
|
||||
else
|
||||
baseLayer = slice;
|
||||
uint32_t baseLayer = rootView.startLayer;
|
||||
if (getTextureType() != TEXTURE_VOLUME)
|
||||
baseLayer += slice;
|
||||
|
||||
level += rootView.startMipmap;
|
||||
|
||||
region.imageSubresource.aspectMask = imageAspect;
|
||||
region.imageSubresource.mipLevel = level;
|
||||
@@ -558,8 +594,8 @@ void Texture::copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int
|
||||
|
||||
VkImageSubresourceLayers layers{};
|
||||
layers.aspectMask = imageAspect;
|
||||
layers.mipLevel = mipmap;
|
||||
layers.baseArrayLayer = slice;
|
||||
layers.mipLevel = mipmap + rootView.startMipmap;
|
||||
layers.baseArrayLayer = slice + rootView.startLayer;
|
||||
layers.layerCount = 1;
|
||||
|
||||
VkBufferImageCopy region{};
|
||||
@@ -588,8 +624,8 @@ void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const
|
||||
|
||||
VkImageSubresourceLayers layers{};
|
||||
layers.aspectMask = imageAspect;
|
||||
layers.mipLevel = mipmap;
|
||||
layers.baseArrayLayer = slice;
|
||||
layers.mipLevel = mipmap + rootView.startMipmap;
|
||||
layers.baseArrayLayer = slice + rootView.startLayer;
|
||||
layers.layerCount = 1;
|
||||
|
||||
VkBufferImageCopy region{};
|
||||
|
||||
Reference in New Issue
Block a user