diff --git a/include/vsg/commands/CopyAndReleaseImage.h b/include/vsg/commands/CopyAndReleaseImage.h index b7a28a037..b000ee3e8 100644 --- a/include/vsg/commands/CopyAndReleaseImage.h +++ b/include/vsg/commands/CopyAndReleaseImage.h @@ -40,7 +40,6 @@ namespace vsg uint32_t width = 0; uint32_t height = 0; uint32_t depth = 0; - Data::MipmapOffsets mipmapOffsets; void record(CommandBuffer& commandBuffer) const; }; diff --git a/include/vsg/core/Data.h b/include/vsg/core/Data.h index 033ad278d..946dcb5e7 100644 --- a/include/vsg/core/Data.h +++ b/include/vsg/core/Data.h @@ -189,8 +189,11 @@ namespace vsg uint32_t stride() const { return properties.stride ? properties.stride : static_cast(valueSize()); } using MipmapOffsets = std::vector; - MipmapOffsets computeMipmapOffsets() const; - static size_t computeValueCountIncludingMipmaps(size_t w, size_t h, size_t d, uint32_t maxNumMipmaps); + // Note, computeMipmapOffsets will naively interpret the data dimensions as the image dimensions + // and return incorrect values for layered images. + MipmapOffsets computeMipmapOffsets() const; // deprecated + static size_t computeValueCountIncludingMipmaps(size_t w, size_t h, size_t d, uint32_t maxNumMipmaps); // deprecated + static size_t computeValueCountIncludingMipmaps(size_t w, size_t h, size_t d, uint32_t maxNumMipmaps, uint32_t arrayLayers); /// increment the ModifiedCount to signify the data has been modified void dirty() { ++_modifiedCount; } diff --git a/include/vsg/state/ImageInfo.h b/include/vsg/state/ImageInfo.h index afd8f857e..5cc5c0a1c 100644 --- a/include/vsg/state/ImageInfo.h +++ b/include/vsg/state/ImageInfo.h @@ -99,6 +99,18 @@ namespace vsg extern VSG_DECLSPEC FormatTraits getFormatTraits(VkFormat format, bool default_one = true); /// return the number of mip map levels specified by Data/Sampler. - extern VSG_DECLSPEC uint32_t computeNumMipMapLevels(const Data* data, const Sampler* sampler); + extern VSG_DECLSPEC uint32_t computeNumMipMapLevels(const Data* data, const Sampler* sampler); // deprecated + extern VSG_DECLSPEC uint32_t computeNumMipMapLevels(uint32_t w, uint32_t h, uint32_t d, const Sampler* sampler); + /// return the size of a full mipmap chain for dimensions. + /// equivalent to: log2(max(w,h,d))+1 + /// See Vulkan spec 12.3.2 "Image Miplevel Sizing" + extern VSG_DECLSPEC uint32_t computeNumMipMapLevels(uint32_t w, uint32_t h, uint32_t d); + + /// return the number of mipLevels that can be read from vsg::Data for this vsg::Image. + extern VSG_DECLSPEC uint32_t computeNumMipMapLevels(const vsg::Data::Properties& properties, const Image* image); + + /// return the number of Data values required for Image including mipmaps + /// Note, Data::valueCount() doesn't support layers correctly + extern VSG_DECLSPEC size_t computeValueCount(const vsg::Data::Properties& properties, const Image* image); } // namespace vsg diff --git a/include/vsg/state/ImageView.h b/include/vsg/state/ImageView.h index 904bbf75a..28c933426 100644 --- a/include/vsg/state/ImageView.h +++ b/include/vsg/state/ImageView.h @@ -68,6 +68,6 @@ namespace vsg extern VSG_DECLSPEC ref_ptr createImageView(Device* device, ref_ptr image, VkImageAspectFlags aspectFlags); /// convenience function that uploads staging buffer data to device including mipmaps. - extern VSG_DECLSPEC void transferImageData(ref_ptr imageView, VkImageLayout targetImageLayout, Data::Properties properties, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, const Data::MipmapOffsets& mipmapOffsets, ref_ptr stagingBuffer, VkDeviceSize stagingBufferOffset, VkCommandBuffer vk_commandBuffer, vsg::Device* device); + extern VSG_DECLSPEC void transferImageData(ref_ptr imageView, VkImageLayout targetImageLayout, const Data::Properties& properties, uint32_t width, uint32_t height, uint32_t depth, ref_ptr stagingBuffer, VkDeviceSize stagingBufferOffset, VkCommandBuffer vk_commandBuffer, vsg::Device* device); } // namespace vsg diff --git a/src/vsg/app/TransferTask.cpp b/src/vsg/app/TransferTask.cpp index bdd6e2375..7440c65b0 100644 --- a/src/vsg/app/TransferTask.cpp +++ b/src/vsg/app/TransferTask.cpp @@ -202,7 +202,8 @@ void TransferTask::assign(const ImageInfoList& imageInfoList) VkFormat targetFormat = imageInfo->imageView->format; auto targetTraits = getFormatTraits(targetFormat); - VkDeviceSize imageTotalSize = targetTraits.size * data->valueCount(); + auto valueCount = vsg::computeValueCount(data->properties, imageInfo->imageView->image); + VkDeviceSize imageTotalSize = targetTraits.size * valueCount; VkDeviceSize endOfEntry = offset + imageTotalSize; offset = (/*alignment == 1 ||*/ (endOfEntry % alignment) == 0) ? endOfEntry : ((endOfEntry / alignment) + 1) * alignment; @@ -254,21 +255,22 @@ void TransferTask::_transferImageInfo(VkCommandBuffer vk_commandBuffer, Frame& f auto width = data->width(); auto height = data->height(); auto depth = data->depth(); - auto mipmapOffsets = data->computeMipmapOffsets(); - uint32_t mipLevels = vsg::computeNumMipMapLevels(data, imageInfo.sampler); auto source_offset = offset; - log(level, "ImageInfo needs copying ", data, ", mipLevels = ", mipLevels); + log(level, "ImageInfo needs copying ", data, ", image->mipLevels = ", imageInfo.imageView->image->mipLevels); // copy data. VkFormat sourceFormat = data->properties.format; VkFormat targetFormat = imageInfo.imageView->format; + //auto dataSize = data->dataSize(); + auto valueCount = vsg::computeValueCount(data->properties, imageInfo.imageView->image); + auto dataSize = valueCount * data->properties.stride; if (sourceFormat == targetFormat) { log(level, " sourceFormat and targetFormat compatible."); - std::memcpy(ptr, data->dataPointer(), data->dataSize()); - offset += data->dataSize(); + std::memcpy(ptr, data->dataPointer(), dataSize); + offset += dataSize; } else { @@ -277,17 +279,17 @@ void TransferTask::_transferImageInfo(VkCommandBuffer vk_commandBuffer, Frame& f if (sourceTraits.size == targetTraits.size) { log(level, " sourceTraits.size and targetTraits.size compatible."); - std::memcpy(ptr, data->dataPointer(), data->dataSize()); - offset += data->dataSize(); + std::memcpy(ptr, data->dataPointer(), dataSize); + offset += dataSize; } else { - VkDeviceSize imageTotalSize = targetTraits.size * data->valueCount(); + VkDeviceSize imageTotalSize = targetTraits.size * valueCount; properties.format = targetFormat; properties.stride = targetTraits.size; - log(level, " sourceTraits.size and targetTraits.size not compatible. dataSize() = ", data->dataSize(), ", imageTotalSize = ", imageTotalSize); + log(level, " sourceTraits.size and targetTraits.size not compatible. dataSize = ", dataSize, ", imageTotalSize = ", imageTotalSize); // set up a vec4 worth of default values for the type const uint8_t* default_ptr = targetTraits.defaultValue; @@ -302,7 +304,6 @@ void TransferTask::_transferImageInfo(VkCommandBuffer vk_commandBuffer, Frame& f value_type* dest_ptr = reinterpret_cast(ptr); - size_t valueCount = data->valueCount(); for (size_t i = 0; i < valueCount; ++i) { uint32_t s = 0; @@ -321,7 +322,7 @@ void TransferTask::_transferImageInfo(VkCommandBuffer vk_commandBuffer, Frame& f } // transfer data. - transferImageData(imageInfo.imageView, imageInfo.imageLayout, properties, width, height, depth, mipLevels, mipmapOffsets, imageStagingBuffer, source_offset, vk_commandBuffer, device); + transferImageData(imageInfo.imageView, imageInfo.imageLayout, properties, width, height, depth, imageStagingBuffer, source_offset, vk_commandBuffer, device); } VkResult TransferTask::transferDynamicData() diff --git a/src/vsg/commands/CopyAndReleaseImage.cpp b/src/vsg/commands/CopyAndReleaseImage.cpp index 68588ad9a..8550ea28c 100644 --- a/src/vsg/commands/CopyAndReleaseImage.cpp +++ b/src/vsg/commands/CopyAndReleaseImage.cpp @@ -38,7 +38,6 @@ CopyAndReleaseImage::CopyData::CopyData(ref_ptr src, ref_ptrdata->width(); height = source->data->height(); depth = source->data->depth(); - mipmapOffsets = source->data->computeMipmapOffsets(); } } @@ -50,7 +49,8 @@ void CopyAndReleaseImage::add(const CopyData& cd) void CopyAndReleaseImage::add(ref_ptr src, ref_ptr dest) { - add(CopyData(src, dest, vsg::computeNumMipMapLevels(src->data, dest->sampler))); + uint32_t dataMipLevels = computeNumMipMapLevels(src->data->properties, dest->imageView->image); + add(CopyData(src, dest, dataMipLevels)); } void CopyAndReleaseImage::add(ref_ptr src, ref_ptr dest, uint32_t numMipMapLevels) @@ -60,7 +60,8 @@ void CopyAndReleaseImage::add(ref_ptr src, ref_ptr dest, void CopyAndReleaseImage::copy(ref_ptr data, ref_ptr dest) { - copy(data, dest, vsg::computeNumMipMapLevels(data, dest->sampler)); + uint32_t dataMipLevels = computeNumMipMapLevels(data->properties, dest->imageView->image); + copy(data, dest, dataMipLevels); } void CopyAndReleaseImage::copy(ref_ptr data, ref_ptr dest, uint32_t numMipMapLevels) @@ -90,7 +91,9 @@ void CopyAndReleaseImage::copy(ref_ptr data, ref_ptr dest, uint { VkMemoryPropertyFlags memoryPropertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - VkDeviceSize imageTotalSize = targetTraits.size * data->valueCount(); + //VkDeviceSize imageTotalSize = targetTraits.size * data->valueCount(); + size_t valueCount = vsg::computeValueCount(data->properties, dest->imageView->image); + VkDeviceSize imageTotalSize = targetTraits.size * valueCount; VkDeviceSize alignment = std::max(VkDeviceSize(4), VkDeviceSize(targetTraits.size)); auto stagingBufferInfo = stagingMemoryBufferPools->reserveBuffer(imageTotalSize, alignment, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, memoryPropertyFlags); @@ -121,7 +124,6 @@ void CopyAndReleaseImage::copy(ref_ptr data, ref_ptr dest, uint imageStagingMemory->map(imageStagingBuffer->getMemoryOffset(deviceID) + stagingBufferInfo->offset, imageTotalSize, 0, &buffer_data); value_type* dest_ptr = reinterpret_cast(buffer_data); - size_t valueCount = data->valueCount(); for (size_t i = 0; i < valueCount; ++i) { uint32_t s = 0; @@ -145,7 +147,8 @@ void CopyAndReleaseImage::copy(ref_ptr data, ref_ptr dest, uint void CopyAndReleaseImage::_copyDirectly(ref_ptr data, ref_ptr dest, uint32_t numMipMapLevels) { - VkDeviceSize imageTotalSize = data->dataSize(); + size_t valueCount = vsg::computeValueCount(data->properties, dest->imageView->image); + VkDeviceSize imageTotalSize = data->properties.stride * valueCount; VkDeviceSize alignment = std::max(VkDeviceSize(4), VkDeviceSize(data->valueSize())); VkMemoryPropertyFlags memoryPropertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; @@ -166,7 +169,7 @@ void CopyAndReleaseImage::_copyDirectly(ref_ptr data, ref_ptr d void CopyAndReleaseImage::CopyData::record(CommandBuffer& commandBuffer) const { - transferImageData(destination->imageView, destination->imageLayout, layout, width, height, depth, mipLevels, mipmapOffsets, source->buffer, source->offset, commandBuffer.vk(), commandBuffer.getDevice()); + transferImageData(destination->imageView, destination->imageLayout, layout, width, height, depth, source->buffer, source->offset, commandBuffer.vk(), commandBuffer.getDevice()); } void CopyAndReleaseImage::record(CommandBuffer& commandBuffer) const diff --git a/src/vsg/core/Data.cpp b/src/vsg/core/Data.cpp index 1929ef801..62fdcd93e 100644 --- a/src/vsg/core/Data.cpp +++ b/src/vsg/core/Data.cpp @@ -143,9 +143,14 @@ Data::MipmapOffsets Data::computeMipmapOffsets() const std::size_t Data::computeValueCountIncludingMipmaps(std::size_t w, std::size_t h, std::size_t d, uint32_t numMipmaps) { - if (numMipmaps <= 1) return w * h * d; + return computeValueCountIncludingMipmaps(w, h, d, numMipmaps, 1); +} + +std::size_t Data::computeValueCountIncludingMipmaps(std::size_t w, std::size_t h, std::size_t d, uint32_t numMipmaps, uint32_t arrayLayers) +{ + if (numMipmaps <= 1) return w * h * d * arrayLayers; - std::size_t lastPosition = (w * h * d); + std::size_t lastPosition = (w * h * d * arrayLayers); while (numMipmaps > 1 && (w > 1 || h > 1 || d > 1)) { --numMipmaps; @@ -154,7 +159,7 @@ std::size_t Data::computeValueCountIncludingMipmaps(std::size_t w, std::size_t h if (h > 1) h /= 2; if (d > 1) d /= 2; - lastPosition += (w * h * d); + lastPosition += (w * h * d * arrayLayers); } return lastPosition; diff --git a/src/vsg/state/DescriptorImage.cpp b/src/vsg/state/DescriptorImage.cpp index a5f0021d4..9ca669e86 100644 --- a/src/vsg/state/DescriptorImage.cpp +++ b/src/vsg/state/DescriptorImage.cpp @@ -118,7 +118,7 @@ void DescriptorImage::compile(Context& context) if (imageView.image && imageView.image->syncModifiedCount(context.deviceID)) { auto& image = *imageView.image; - context.copy(image.data, imageInfo, image.mipLevels); + context.copy(image.data, imageInfo); } } } diff --git a/src/vsg/state/ImageInfo.cpp b/src/vsg/state/ImageInfo.cpp index cb5bcaa2c..8f855dfc9 100644 --- a/src/vsg/state/ImageInfo.cpp +++ b/src/vsg/state/ImageInfo.cpp @@ -93,26 +93,19 @@ FormatTraits vsg::getFormatTraits(VkFormat format, bool default_one) } uint32_t vsg::computeNumMipMapLevels(const Data* data, const Sampler* sampler) +{ + return computeNumMipMapLevels(data->width(), data->height(), data->depth(), sampler); +} + +uint32_t vsg::computeNumMipMapLevels(uint32_t w, uint32_t h, uint32_t d, const Sampler* sampler) { uint32_t mipLevels = 1; if (sampler) { - // clamp the mipLevels so that it's no larger than what the data dimensions support - uint32_t maxDimension = std::max({data->width(), data->height(), data->depth()}); - if (sampler->maxLod == VK_LOD_CLAMP_NONE) - { - while ((1u << mipLevels) <= maxDimension) - { - ++mipLevels; - } - } - else if (static_cast(sampler->maxLod) > 1) + mipLevels = vsg::computeNumMipMapLevels(w, h, d); + if (sampler->maxLod != VK_LOD_CLAMP_NONE) { - mipLevels = static_cast(sampler->maxLod); - while ((1u << (mipLevels - 1)) > maxDimension) - { - --mipLevels; - } + mipLevels = std::max(1u, std::min(static_cast(sampler->maxLod), mipLevels)); } } @@ -150,10 +143,17 @@ void ImageInfo::computeNumMipMapLevels() { auto image = imageView->image; auto data = image->data; - auto mipLevels = vsg::computeNumMipMapLevels(data, sampler); + uint32_t mipLevels = vsg::computeNumMipMapLevels(image->extent.width / data->properties.blockWidth, image->extent.height / data->properties.blockHeight, image->extent.depth / data->properties.blockDepth, sampler); - const auto& mipmapOffsets = image->data->computeMipmapOffsets(); - bool generateMipmaps = (mipLevels > 1) && (mipmapOffsets.size() <= 1); + bool generateMipmaps = false; + if (data->properties.maxNumMipmaps <= 1 && mipLevels > 1) + { + generateMipmaps = true; + } + else + { + mipLevels = vsg::computeNumMipMapLevels(data->properties, image); + } if (generateMipmaps) { @@ -167,6 +167,7 @@ void ImageInfo::computeNumMipMapLevels() } mipLevels = 1; + generateMipmaps = false; } } @@ -175,3 +176,42 @@ void ImageInfo::computeNumMipMapLevels() if (generateMipmaps) image->usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } } + +uint32_t vsg::computeNumMipMapLevels(uint32_t w, uint32_t h, uint32_t d) +{ + // clamp the mipLevels so that it's no larger than what the data dimensions support + uint32_t maxDimension = std::max({w, h, d}); + uint32_t mipLevels = 1; + while ((1u << mipLevels) <= maxDimension) + { + ++mipLevels; + } + return mipLevels; +} + +uint32_t vsg::computeNumMipMapLevels(const vsg::Data::Properties& properties, const Image* image) +{ + // get the dimensions, usually equivalent to Data::width/height/depth() except for layered images + uint32_t w = image->extent.width / properties.blockWidth; + uint32_t h = image->extent.height / properties.blockHeight; + uint32_t d = image->extent.depth / properties.blockDepth; + // clamp the mipLevels so that it's no larger than what the data dimensions support + uint32_t mipLevels = vsg::computeNumMipMapLevels(w, h, d); + + // clamp the mipLevels so that it's no larger than specified by vsg::Data + mipLevels = std::min(std::max(1u, static_cast(properties.maxNumMipmaps)), mipLevels); + + // clamp the mipLevels so that it's no larger than the mipLevels vsg::Image was compiled with + if (image->mipLevels > 0) + mipLevels = std::min(image->mipLevels, mipLevels); + return mipLevels; +} + +size_t vsg::computeValueCount(const vsg::Data::Properties& properties, const vsg::Image* image) +{ + uint32_t w = image->extent.width / properties.blockWidth; + uint32_t h = image->extent.height / properties.blockHeight; + uint32_t d = image->extent.depth / properties.blockDepth; + uint32_t mipLevels = vsg::computeNumMipMapLevels(properties, image); + return Data::computeValueCountIncludingMipmaps(w, h, d, mipLevels, image->arrayLayers); +} diff --git a/src/vsg/state/ImageView.cpp b/src/vsg/state/ImageView.cpp index 9c904b091..4620e2f4e 100644 --- a/src/vsg/state/ImageView.cpp +++ b/src/vsg/state/ImageView.cpp @@ -200,7 +200,7 @@ ref_ptr vsg::createImageView(Device* device, ref_ptr image, Vk return imageView; } -void vsg::transferImageData(ref_ptr imageView, VkImageLayout targetImageLayout, Data::Properties properties, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, const Data::MipmapOffsets& mipmapOffsets, ref_ptr stagingBuffer, VkDeviceSize stagingBufferOffset, VkCommandBuffer commandBuffer, vsg::Device* device) +void vsg::transferImageData(ref_ptr imageView, VkImageLayout targetImageLayout, const Data::Properties& properties, uint32_t width, uint32_t height, uint32_t depth, ref_ptr stagingBuffer, VkDeviceSize stagingBufferOffset, VkCommandBuffer commandBuffer, vsg::Device* device) { ref_ptr textureImage(imageView->image); auto aspectMask = imageView->subresourceRange.aspectMask; @@ -240,8 +240,9 @@ void vsg::transferImageData(ref_ptr imageView, VkImageLayout targetIm const auto valueSize = properties.stride; // data->valueSize(); - bool useDataMipmaps = (mipLevels > 1) && (mipmapOffsets.size() > 1); - bool generateMipmaps = (mipLevels > 1) && (mipmapOffsets.size() <= 1); + uint32_t mipLevels = imageView->image->mipLevels; + bool useDataMipmaps = (mipLevels > 1) && (properties.maxNumMipmaps > 1); + bool generateMipmaps = (mipLevels > 1) && (properties.maxNumMipmaps <= 1); auto vk_textureImage = textureImage->vk(device->deviceID);