| /* Copyright (c) 2019-2021 The Khronos Group Inc. |
| * Copyright (c) 2019-2021 Valve Corporation |
| * Copyright (c) 2019-2021 LunarG, Inc. |
| * Copyright (C) 2019-2021 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * John Zulauf <jzulauf@lunarg.com> |
| * |
| */ |
| #include <cassert> |
| #include "subresource_adapter.h" |
| #include "vk_format_utils.h" |
| #include <cmath> |
| #include "image_state.h" |
| #include "layer_chassis_dispatch.h" |
| |
| namespace subresource_adapter { |
| Subresource::Subresource(const RangeEncoder& encoder, const VkImageSubresource& subres) |
| : VkImageSubresource({0, subres.mipLevel, subres.arrayLayer}), aspect_index() { |
| aspect_index = encoder.LowerBoundFromMask(subres.aspectMask); |
| aspectMask = encoder.AspectBit(aspect_index); |
| } |
| |
| IndexType RangeEncoder::Encode1AspectArrayOnly(const Subresource& pos) const { return pos.arrayLayer; } |
| IndexType RangeEncoder::Encode1AspectMipArray(const Subresource& pos) const { return pos.arrayLayer + pos.mipLevel * mip_size_; } |
| IndexType RangeEncoder::Encode1AspectMipOnly(const Subresource& pos) const { return pos.mipLevel; } |
| |
| IndexType RangeEncoder::EncodeAspectArrayOnly(const Subresource& pos) const { |
| return pos.arrayLayer + aspect_base_[pos.aspect_index]; |
| } |
| IndexType RangeEncoder::EncodeAspectMipArray(const Subresource& pos) const { |
| return pos.arrayLayer + pos.mipLevel * mip_size_ + aspect_base_[pos.aspect_index]; |
| } |
| IndexType RangeEncoder::EncodeAspectMipOnly(const Subresource& pos) const { return pos.mipLevel + aspect_base_[pos.aspect_index]; } |
| |
| uint32_t RangeEncoder::LowerBoundImpl1(VkImageAspectFlags aspect_mask) const { |
| assert(aspect_mask & aspect_bits_[0]); |
| return 0; |
| } |
| uint32_t RangeEncoder::LowerBoundWithStartImpl1(VkImageAspectFlags aspect_mask, uint32_t start) const { |
| assert(start == 0); |
| if (aspect_mask & aspect_bits_[0]) { |
| return 0; |
| } |
| return limits_.aspect_index; |
| } |
| |
| uint32_t RangeEncoder::LowerBoundImpl2(VkImageAspectFlags aspect_mask) const { |
| if (aspect_mask & aspect_bits_[0]) { |
| return 0; |
| } |
| assert(aspect_mask & aspect_bits_[1]); |
| return 1; |
| } |
| uint32_t RangeEncoder::LowerBoundWithStartImpl2(VkImageAspectFlags aspect_mask, uint32_t start) const { |
| switch (start) { |
| case 0: |
| if (aspect_mask & aspect_bits_[0]) { |
| return 0; |
| } |
| // no break |
| case 1: |
| if (aspect_mask & aspect_bits_[1]) { |
| return 1; |
| } |
| break; |
| default: |
| break; |
| } |
| return limits_.aspect_index; |
| } |
| |
| uint32_t RangeEncoder::LowerBoundImpl3(VkImageAspectFlags aspect_mask) const { |
| if (aspect_mask & aspect_bits_[0]) { |
| return 0; |
| } else if (aspect_mask & aspect_bits_[1]) { |
| return 1; |
| } else { |
| assert(aspect_mask & aspect_bits_[2]); |
| return 2; |
| } |
| } |
| |
| uint32_t RangeEncoder::LowerBoundWithStartImpl3(VkImageAspectFlags aspect_mask, uint32_t start) const { |
| switch (start) { |
| case 0: |
| if (aspect_mask & aspect_bits_[0]) { |
| return 0; |
| } |
| // no break |
| case 1: |
| if ((aspect_mask & aspect_bits_[1])) { |
| return 1; |
| } |
| // no break |
| case 2: |
| if ((aspect_mask & aspect_bits_[2])) { |
| return 2; |
| } |
| break; |
| default: |
| break; |
| } |
| return limits_.aspect_index; |
| } |
| |
| void RangeEncoder::PopulateFunctionPointers() { |
| // Select the encode/decode specialists |
| if (limits_.aspect_index == 1) { |
| // One aspect use simplified encode/decode math |
| if (limits_.arrayLayer == 1) { // Same as mip_size_ == 1 |
| encode_function_ = &RangeEncoder::Encode1AspectMipOnly; |
| decode_function_ = &RangeEncoder::DecodeAspectMipOnly<1>; |
| } else if (limits_.mipLevel == 1) { |
| encode_function_ = &RangeEncoder::Encode1AspectArrayOnly; |
| decode_function_ = &RangeEncoder::DecodeAspectArrayOnly<1>; |
| } else { |
| encode_function_ = &RangeEncoder::Encode1AspectMipArray; |
| decode_function_ = &RangeEncoder::DecodeAspectMipArray<1>; |
| } |
| lower_bound_function_ = &RangeEncoder::LowerBoundImpl1; |
| lower_bound_with_start_function_ = &RangeEncoder::LowerBoundWithStartImpl1; |
| } else if (limits_.aspect_index == 2) { |
| // Two aspect use simplified encode/decode math |
| if (limits_.arrayLayer == 1) { // Same as mip_size_ == 1 |
| encode_function_ = &RangeEncoder::EncodeAspectMipOnly; |
| decode_function_ = &RangeEncoder::DecodeAspectMipOnly<2>; |
| } else if (limits_.mipLevel == 1) { |
| encode_function_ = &RangeEncoder::EncodeAspectArrayOnly; |
| decode_function_ = &RangeEncoder::DecodeAspectArrayOnly<2>; |
| } else { |
| encode_function_ = &RangeEncoder::EncodeAspectMipArray; |
| decode_function_ = &RangeEncoder::DecodeAspectMipArray<2>; |
| } |
| lower_bound_function_ = &RangeEncoder::LowerBoundImpl2; |
| lower_bound_with_start_function_ = &RangeEncoder::LowerBoundWithStartImpl2; |
| } else { |
| encode_function_ = &RangeEncoder::EncodeAspectMipArray; |
| decode_function_ = &RangeEncoder::DecodeAspectMipArray<3>; |
| lower_bound_function_ = &RangeEncoder::LowerBoundImpl3; |
| lower_bound_with_start_function_ = &RangeEncoder::LowerBoundWithStartImpl3; |
| } |
| |
| // Initialize the offset array |
| aspect_base_[0] = 0; |
| for (uint32_t i = 1; i < limits_.aspect_index; ++i) { |
| aspect_base_[i] = aspect_base_[i - 1] + aspect_size_; |
| } |
| } |
| |
| RangeEncoder::RangeEncoder(const VkImageSubresourceRange& full_range, const AspectParameters* param) |
| : limits_(param->AspectMask(), full_range.levelCount, full_range.layerCount, param->AspectCount()), |
| full_range_(full_range), |
| mip_size_(full_range.layerCount), |
| aspect_size_(mip_size_ * full_range.levelCount), |
| aspect_bits_(param->AspectBits()), |
| mask_index_function_(param->MaskToIndexFunction()), |
| encode_function_(nullptr), |
| decode_function_(nullptr) { |
| // Only valid to create an encoder for a *whole* image (i.e. base must be zero, and the specified limits_.selected_aspects |
| // *must* be equal to the traits aspect mask. (Encoder range assumes zero bases) |
| assert(full_range.aspectMask == limits_.aspectMask); |
| assert(full_range.baseArrayLayer == 0); |
| assert(full_range.baseMipLevel == 0); |
| // TODO: should be some static assert |
| assert(param->AspectCount() <= kMaxSupportedAspect); |
| PopulateFunctionPointers(); |
| } |
| |
| #ifndef NDEBUG |
| static bool IsValid(const RangeEncoder& encoder, const VkImageSubresourceRange& bounds) { |
| const auto& limits = encoder.Limits(); |
| return (((bounds.aspectMask & limits.aspectMask) == bounds.aspectMask) && |
| (bounds.baseMipLevel + bounds.levelCount <= limits.mipLevel) && |
| (bounds.baseArrayLayer + bounds.layerCount <= limits.arrayLayer)); |
| } |
| #endif |
| |
| // Create an iterator like "generator" that for each increment produces the next index range matching the |
| // next contiguous (in index space) section of the VkImageSubresourceRange |
| // Ranges will always span the layerCount layers, and if the layerCount is the full range of the image (as known by |
| // the encoder) will span the levelCount mip levels as weill. |
| RangeGenerator::RangeGenerator(const RangeEncoder& encoder, const VkImageSubresourceRange& subres_range) |
| : encoder_(&encoder), isr_pos_(encoder, subres_range), pos_(), aspect_base_() { |
| assert((((isr_pos_.Limits()).aspectMask & (encoder.Limits()).aspectMask) == (isr_pos_.Limits()).aspectMask)); |
| assert((isr_pos_.Limits()).baseMipLevel + (isr_pos_.Limits()).levelCount <= (encoder.Limits()).mipLevel); |
| assert((isr_pos_.Limits()).baseArrayLayer + (isr_pos_.Limits()).layerCount <= (encoder.Limits()).arrayLayer); |
| |
| // To see if we have a full range special case, need to compare the subres_range against the *encoders* limits |
| const auto& limits = encoder.Limits(); |
| if ((subres_range.baseArrayLayer == 0 && subres_range.layerCount == limits.arrayLayer)) { |
| if ((subres_range.baseMipLevel == 0) && (subres_range.levelCount == limits.mipLevel)) { |
| if (subres_range.aspectMask == limits.aspectMask) { |
| // Full range |
| pos_.begin = 0; |
| pos_.end = encoder.AspectSize() * limits.aspect_index; |
| aspect_count_ = 1; // Flag this to never advance aspects. |
| } else { |
| // All mips all layers but not all aspect |
| pos_.begin = encoder.AspectBase(isr_pos_.aspect_index); |
| pos_.end = pos_.begin + encoder.AspectSize(); |
| aspect_count_ = limits.aspect_index; |
| } |
| } else { |
| // All array layers, but not all levels |
| pos_.begin = encoder.AspectBase(isr_pos_.aspect_index) + subres_range.baseMipLevel * encoder.MipSize(); |
| pos_.end = pos_.begin + subres_range.levelCount * encoder.MipSize(); |
| aspect_count_ = limits.aspect_index; |
| } |
| |
| // Full set of array layers at a time, thus we can span across all selected mip levels |
| mip_count_ = 1; // we don't ever advance across mips, as we do all of then in one range |
| } else { |
| // Each range covers all included array_layers for each selected mip_level for each given selected aspect |
| // so we'll use the general purpose encode and smallest range size |
| pos_.begin = encoder.Encode(isr_pos_); |
| pos_.end = pos_.begin + subres_range.layerCount; |
| |
| // we do have to traverse across mips, though (other than Encode abover), we don't have to know which one we are on. |
| mip_count_ = subres_range.levelCount; |
| aspect_count_ = limits.aspect_index; |
| } |
| |
| // To get to the next aspect range we offset from the last base |
| aspect_base_ = pos_; |
| mip_index_ = 0; |
| aspect_index_ = isr_pos_.aspect_index; |
| } |
| |
| RangeGenerator& RangeGenerator::operator++() { |
| mip_index_++; |
| // NOTE: If all selected mip levels are done at once, mip_count_ is set to one, not the number of selected mip_levels |
| if (mip_index_ >= mip_count_) { |
| const auto last_aspect_index = aspect_index_; |
| // Seek the next value aspect (if any) |
| aspect_index_ = encoder_->LowerBoundFromMask(isr_pos_.Limits().aspectMask, aspect_index_ + 1); |
| if (aspect_index_ < aspect_count_) { |
| // Force isr_pos to the beginning of this found aspect |
| isr_pos_.SeekAspect(aspect_index_); |
| // SubresourceGenerator should never be at tombstones we we aren't |
| assert(isr_pos_.aspectMask != 0); |
| |
| // Offset by the distance between the last start of aspect and *this* start of aspect |
| aspect_base_ += (encoder_->AspectBase(isr_pos_.aspect_index) - encoder_->AspectBase(last_aspect_index)); |
| pos_ = aspect_base_; |
| mip_index_ = 0; |
| } else { |
| // Tombstone both index range and subresource positions to "At end" convention |
| pos_ = {0, 0}; |
| isr_pos_.aspectMask = 0; |
| } |
| } else { |
| // Note: for the layerCount < full_range.layerCount case, because the generated ranges per mip_level are discontinuous |
| // we have to do each individual array of ranges |
| pos_ += encoder_->MipSize(); |
| isr_pos_.SeekMip(isr_pos_.Limits().baseMipLevel + mip_index_); |
| } |
| return *this; |
| } |
| |
| ImageRangeEncoder::ImageRangeEncoder(const IMAGE_STATE& image) |
| : ImageRangeEncoder(image, AspectParameters::Get(image.full_range.aspectMask)) {} |
| |
| ImageRangeEncoder::ImageRangeEncoder(const IMAGE_STATE& image, const AspectParameters* param) |
| : RangeEncoder(image.full_range, param), image_(&image), total_size_(0U) { |
| if (image_->createInfo.extent.depth > 1) { |
| limits_.arrayLayer = image_->createInfo.extent.depth; |
| } |
| VkSubresourceLayout layout = {}; |
| VkImageSubresource subres = {}; |
| VkImageSubresourceLayers subres_layers = {limits_.aspectMask, 0, 0, limits_.arrayLayer}; |
| linear_image_ = false; |
| |
| // WORKAROUND for dev_sim and mock_icd not containing valid VkSubresourceLayout yet. Treat it as optimal image. |
| if (image_->createInfo.tiling == VK_IMAGE_TILING_LINEAR) { |
| subres = {static_cast<VkImageAspectFlags>(AspectBit(0)), 0, 0}; |
| DispatchGetImageSubresourceLayout(image_->store_device_as_workaround, image_->image(), &subres, &layout); |
| if (layout.size > 0) { |
| linear_image_ = true; |
| } |
| } |
| |
| is_compressed_ = FormatIsCompressed(image.createInfo.format); |
| texel_extent_ = FormatTexelBlockExtent(image.createInfo.format); |
| |
| is_3_d_ = image_->createInfo.imageType == VK_IMAGE_TYPE_3D; |
| y_interleave_ = false; |
| for (uint32_t aspect_index = 0; aspect_index < limits_.aspect_index; ++aspect_index) { |
| subres.aspectMask = static_cast<VkImageAspectFlags>(AspectBit(aspect_index)); |
| subres_layers.aspectMask = subres.aspectMask; |
| texel_sizes_.push_back(FormatTexelSize(image.createInfo.format, subres.aspectMask)); |
| IndexType aspect_size = 0; |
| for (uint32_t mip_index = 0; mip_index < limits_.mipLevel; ++mip_index) { |
| subres_layers.mipLevel = mip_index; |
| subres.mipLevel = mip_index; |
| auto subres_extent = image_->GetSubresourceExtent(subres_layers); |
| |
| if (linear_image_) { |
| DispatchGetImageSubresourceLayout(image_->store_device_as_workaround, image_->image(), &subres, &layout); |
| if (is_3_d_) { |
| if ((layout.depthPitch == 0) && (subres_extent.depth == 1)) { |
| layout.depthPitch = layout.size; // Certain implmentations don't supply pitches when size is 1 |
| } |
| y_interleave_ = y_interleave_ || (layout.rowPitch > layout.depthPitch); |
| } else { |
| if ((layout.arrayPitch == 0) && (limits_.arrayLayer == 1)) { |
| layout.arrayPitch = layout.size; // Certain implmentations don't supply pitches when size is 1 |
| } |
| y_interleave_ = y_interleave_ || (layout.rowPitch > layout.arrayPitch); |
| } |
| } else { |
| layout.offset += layout.size; |
| layout.rowPitch = static_cast<VkDeviceSize>(floor(subres_extent.width * texel_sizes_[aspect_index])); |
| layout.arrayPitch = layout.rowPitch * subres_extent.height; |
| layout.depthPitch = layout.arrayPitch; |
| if (is_3_d_) { |
| layout.size = layout.depthPitch * subres_extent.depth; |
| } else { |
| // 2D arrays are not affected by MIP level extent reductions. |
| layout.size = layout.arrayPitch * limits_.arrayLayer; |
| } |
| } |
| subres_info_.emplace_back(layout, subres_extent, texel_extent_, texel_sizes_[aspect_index]); |
| aspect_size += layout.size; |
| total_size_ += layout.size; |
| } |
| aspect_sizes_.emplace_back(aspect_size); |
| } |
| } |
| |
| IndexType ImageRangeEncoder::Encode2D(const VkSubresourceLayout& layout, uint32_t layer, uint32_t aspect_index, |
| const VkOffset3D& offset) const { |
| assert(offset.z == 0U); |
| return layout.offset + layer * layout.arrayPitch + offset.y * layout.rowPitch + |
| (offset.x ? static_cast<IndexType>(floor(offset.x * texel_sizes_[aspect_index])) : 0U); |
| } |
| |
| IndexType ImageRangeEncoder::Encode3D(const VkSubresourceLayout& layout, uint32_t aspect_index, const VkOffset3D& offset) const { |
| return layout.offset + offset.z * layout.depthPitch + offset.y * layout.rowPitch + |
| (offset.x ? static_cast<IndexType>(floor(offset.x * texel_sizes_[aspect_index])) : 0U); |
| } |
| |
| void ImageRangeEncoder::Decode(const VkImageSubresource& subres, const IndexType& encode, uint32_t& out_layer, |
| VkOffset3D& out_offset) const { |
| uint32_t subres_index = GetSubresourceIndex(LowerBoundFromMask(subres.aspectMask), subres.mipLevel); |
| const auto& subres_layout = GetSubresourceInfo(subres_index).layout; |
| IndexType decode = encode - subres_layout.offset; |
| out_layer = static_cast<uint32_t>(decode / subres_layout.arrayPitch); |
| decode -= (out_layer * subres_layout.arrayPitch); |
| out_offset.z = static_cast<int32_t>(decode / subres_layout.depthPitch); |
| decode -= (out_offset.z * subres_layout.depthPitch); |
| out_offset.y = static_cast<int32_t>(decode / subres_layout.rowPitch); |
| decode -= (out_offset.y * subres_layout.rowPitch); |
| out_offset.x = static_cast<int32_t>(static_cast<double>(decode) / texel_sizes_[LowerBoundFromMask(subres.aspectMask)]); |
| } |
| |
| |
| inline VkImageSubresourceRange GetRemaining(const VkImageSubresourceRange& full_range, VkImageSubresourceRange subres_range) { |
| if (subres_range.levelCount == VK_REMAINING_MIP_LEVELS) { |
| subres_range.levelCount = full_range.levelCount - subres_range.baseMipLevel; |
| } |
| if (subres_range.layerCount == VK_REMAINING_ARRAY_LAYERS) { |
| subres_range.layerCount = full_range.layerCount - subres_range.baseArrayLayer; |
| } |
| return subres_range; |
| } |
| inline bool CoversAllLayers(const VkImageSubresourceRange& full_range, VkImageSubresourceRange subres_range) { |
| return (subres_range.baseArrayLayer == 0) && (subres_range.layerCount == full_range.layerCount); |
| } |
| inline bool CoversAllLevels(const VkImageSubresourceRange& full_range, VkImageSubresourceRange subres_range) { |
| return (subres_range.baseMipLevel == 0) && (subres_range.layerCount == full_range.levelCount); |
| } |
| inline bool CoversAllAspects(const VkImageSubresourceRange& full_range, VkImageSubresourceRange subres_range) { |
| return full_range.aspectMask == subres_range.aspectMask; |
| } |
| |
| static bool SubresourceRangeIsEmpty(const VkImageSubresourceRange& range) { |
| return (0 == range.aspectMask) || (0 == range.levelCount) || (0 == range.layerCount); |
| } |
| static bool ExtentIsEmpty(const VkExtent3D& extent) { return (0 == extent.width) || (0 == extent.height) || (0 == extent.width); } |
| |
| void ImageRangeGenerator::SetInitialPosFullOffset(uint32_t layer, uint32_t aspect_index) { |
| const bool is_3D = encoder_->Is3D(); |
| const auto& subres_layout = subres_info_->layout; |
| const IndexType encode_base = is_3D ? encoder_->Encode3D(subres_layout, aspect_index, offset_) |
| : encoder_->Encode2D(subres_layout, layer, aspect_index, offset_); |
| const IndexType base = base_address_ + encode_base; |
| // To deal with compressed formats the span must cover the y-extent of lines (something we resmember in the y_step) |
| const IndexType span = static_cast<IndexType>(floor(encoder_->TexelSize(aspect_index) * (extent_.width * incr_state_.y_step))); |
| |
| const uint32_t z_count = is_3D ? extent_.depth : subres_range_.layerCount; |
| const IndexType z_pitch = is_3D ? subres_info_->z_step_pitch : subres_layout.arrayPitch; |
| incr_state_.Set(extent_.height, z_count, base, span, subres_info_->y_step_pitch, z_pitch); |
| } |
| |
| void ImageRangeGenerator::SetInitialPosFullWidth(uint32_t layer, uint32_t aspect_index) { |
| assert(!encoder_->IsInterleaveY() && (offset_.x == 0)); |
| const bool is_3D = encoder_->Is3D(); |
| const auto& subres_layout = subres_info_->layout; |
| const IndexType encode_base = is_3D ? encoder_->Encode3D(subres_layout, aspect_index, offset_) |
| : encoder_->Encode2D(subres_layout, layer, aspect_index, offset_); |
| const IndexType base = base_address_ + encode_base; |
| // Height must be in multiples of y_step (the texel dimension)... validated elsewhere |
| const IndexType span = subres_layout.rowPitch * extent_.height; |
| |
| const uint32_t z_count = is_3D ? extent_.depth : subres_range_.layerCount; |
| const IndexType z_pitch = is_3D ? subres_info_->z_step_pitch : subres_layout.arrayPitch; |
| incr_state_.Set(1U, z_count, base, span, subres_info_->y_step_pitch, z_pitch); |
| } |
| |
| void ImageRangeGenerator::SetInitialPosFullHeight(uint32_t layer, uint32_t aspect_index) { |
| assert(!encoder_->Is3D() && (offset_.x == 0) && (offset_.y == 0)); |
| const auto& subres_layout = subres_info_->layout; |
| const IndexType base = base_address_ + subres_layout.offset + subres_range_.baseArrayLayer * subres_layout.arrayPitch; |
| const IndexType span = subres_info_->layer_span; |
| const IndexType z_step = subres_layout.arrayPitch; |
| |
| incr_state_.Set(1, subres_range_.layerCount, base, span, span, z_step); |
| } |
| |
| void ImageRangeGenerator::SetInitialPosSomeDepth(uint32_t layer, uint32_t aspect_index) { |
| assert(encoder_->Is3D() && (offset_.x == 0) && (offset_.y == 0) && (layer == 0)); |
| const auto& subres_layout = subres_info_->layout; |
| const IndexType encode_base = encoder_->Encode3D(subres_layout, aspect_index, offset_); |
| const IndexType base = base_address_ + encode_base; |
| // Height must be in multiples of z_step (the texel dimension)... validated elsewhere |
| const IndexType span = subres_layout.depthPitch * extent_.depth; |
| |
| incr_state_.Set(1, 1, base, span, span, subres_layout.size); |
| } |
| |
| void ImageRangeGenerator::SetInitialPosFullDepth(uint32_t layer, uint32_t aspect_index) { |
| assert(encoder_->Is3D() && (offset_.x == 0) && (offset_.y == 0) && (offset_.z == 0) && (layer == 0)); |
| const auto& subres_layout = subres_info_->layout; |
| const IndexType base = base_address_ + subres_layout.offset; |
| const IndexType span = subres_layout.depthPitch * extent_.depth; |
| |
| incr_state_.Set(1, 1, base, span, span, span); |
| } |
| |
| void ImageRangeGenerator::SetInitialPosSomeLayers(uint32_t layer, uint32_t aspect_index) { |
| assert(!encoder_->Is3D() && (offset_.x == 0) && (offset_.y == 0) && (offset_.z == 0)); |
| const auto& subres_layout = subres_info_->layout; |
| const IndexType base = base_address_ + subres_layout.offset + layer * subres_layout.arrayPitch; |
| const IndexType span = subres_layout.arrayPitch * subres_range_.layerCount; |
| const IndexType z_step = subres_layout.arrayPitch * encoder_->Limits().arrayLayer; |
| incr_state_.Set(1, 1, base, span, span, z_step); |
| } |
| |
| void ImageRangeGenerator::SetInitialPosAllLayers(uint32_t layer, uint32_t aspect_index) { |
| assert(!encoder_->Is3D() && (offset_.x == 0) && (offset_.y == 0) && (offset_.z == 0) && |
| (layer == 0)); |
| const auto& subres_layout = subres_info_->layout; |
| const IndexType base = base_address_ + subres_layout.offset; |
| const IndexType span = subres_layout.arrayPitch * subres_range_.layerCount; |
| incr_state_.Set(1, 1, base, span, span, span); |
| } |
| |
| void ImageRangeGenerator::SetInitialPosOneAspect(uint32_t layer, uint32_t aspect_index) { |
| assert(!encoder_->IsLinearImage()); // Requires the major minor of "idealized/non-linear" images |
| const auto& subres_layout = subres_info_->layout; |
| const IndexType base = base_address_ + subres_layout.offset; |
| IndexType span = 0; |
| if (subres_range_.levelCount == encoder_->Limits().mipLevel) { |
| span = encoder_->GetAspectSize(aspect_index); |
| } else { |
| // Add up the mip sizes... |
| // Assumes subres_info is pointing to index(baseMipLevel, aspect_index) |
| // Assumes mip major order... |
| for (uint32_t level = 0; level < subres_range_.levelCount; level++) { |
| span += subres_info_[level].layout.size; |
| } |
| } |
| incr_mip_ = subres_range_.levelCount; |
| incr_state_.Set(1, 1, base, span, span, span); |
| } |
| |
| void ImageRangeGenerator::SetInitialPosAllSubres(uint32_t layer, uint32_t aspect_index) { |
| assert(!encoder_->IsLinearImage()); |
| const IndexType base = base_address_; |
| const IndexType span = encoder_->TotalSize(); |
| |
| // Just one range... everything, ++ will short circuit to "end" |
| single_full_size_range_ = true; |
| // We don't need to set up the rest of the incrementer, just the starting position |
| incr_state_.y_base = {base, base + span}; |
| } |
| |
| bool ImageRangeGenerator::Convert2DCompatibleTo3D() { |
| if (encoder_->Is3D()) { |
| if ((subres_range_.levelCount == 1) && ((subres_range_.baseArrayLayer > 0) || (subres_range_.layerCount > 1))) { |
| // This only valid for 2D compatible 3D images |
| // Touch up the extent and the subres to make this look like a depth extent |
| offset_.z = subres_range_.baseArrayLayer; |
| subres_range_.baseArrayLayer = 0; |
| extent_.depth = subres_range_.layerCount; |
| subres_range_.layerCount = 1; |
| return true; |
| } |
| } |
| return false; |
| } |
| ImageRangeGenerator::ImageRangeGenerator(const ImageRangeEncoder& encoder, const VkImageSubresourceRange& subres_range, |
| VkDeviceSize base_address) |
| : encoder_(&encoder), |
| subres_range_(GetRemaining(encoder.FullRange(), subres_range)), |
| offset_(), |
| extent_(), |
| base_address_(base_address) { |
| #ifndef NDEBUG |
| assert(IsValid(*encoder_, subres_range_)); |
| #endif |
| if (SubresourceRangeIsEmpty(subres_range)) { |
| // Not robust to empty ranges, so for to "at end" condition. |
| pos_ = {0, 0}; |
| return; |
| } |
| |
| SetUpSubresInfo(); |
| extent_ = subres_info_->extent; |
| const bool converted = Convert2DCompatibleTo3D(); |
| SetUpIncrementerDefaults(); |
| if (converted && (extent_.depth != subres_info_->extent.depth)) { |
| SetUpIncrementer(true, true, false); |
| } else { |
| SetUpSubresIncrementer(); |
| } |
| SetInitialPos(subres_range_.baseArrayLayer, aspect_index_); |
| pos_ = incr_state_.y_base; |
| } |
| |
| ImageRangeGenerator::ImageRangeGenerator(const ImageRangeEncoder& encoder, const VkImageSubresourceRange& subres_range, |
| const VkOffset3D& offset, const VkExtent3D& extent, VkDeviceSize base_address) |
| : encoder_(&encoder), |
| subres_range_(GetRemaining(encoder.FullRange(), subres_range)), |
| offset_(offset), |
| extent_(extent), |
| base_address_(base_address) { |
| #ifndef NDEBUG |
| assert(IsValid(*encoder_, subres_range_)); |
| #endif |
| |
| assert(subres_range_.levelCount == 1); |
| if (SubresourceRangeIsEmpty(subres_range)) { |
| // Empty range forces empty position -- no operations other than deref for empty check are valid |
| pos_ = {0, 0}; |
| return; |
| } |
| |
| // When passing in an offset and extent, *must* only specify *one* mip level |
| SetUpSubresInfo(); |
| Convert2DCompatibleTo3D(); |
| |
| const VkExtent3D& subres_extent = subres_info_->extent; |
| if (ExtentIsEmpty(extent_) || ((extent_.width + offset_.x) > subres_extent.width) || |
| ((extent_.height + offset_.y) > subres_extent.height) || ((extent_.depth + offset_.z) > subres_extent.depth)) { |
| // Empty range forces empty position -- no operations other than deref for empty check are valid |
| pos_ = {0, 0}; |
| return; |
| } |
| |
| const bool all_width = (offset.x == 0) && (extent_.width == subres_extent.width); |
| const bool all_height = (offset.y == 0) && (extent_.height == subres_extent.height); |
| const bool all_depth = !encoder_->Is3D() || ((offset.z == 0) && (extent_.depth == subres_extent.depth)); |
| |
| SetUpIncrementerDefaults(); |
| SetUpIncrementer(all_width, all_height, all_depth); |
| SetInitialPos(subres_range_.baseArrayLayer, aspect_index_); |
| pos_ = incr_state_.y_base; |
| } |
| |
| void ImageRangeGenerator::SetUpSubresInfo() { |
| mip_index_ = 0; |
| aspect_index_ = encoder_->LowerBoundFromMask(subres_range_.aspectMask); |
| subres_index_ = encoder_->GetSubresourceIndex(aspect_index_, subres_range_.baseMipLevel); |
| subres_info_ = &encoder_->GetSubresourceInfo(subres_index_); |
| } |
| |
| void ImageRangeGenerator::SetUpIncrementerDefaults() { |
| // These are safe defaults that most SetInitialPos* will use. Those that need to change them, do. |
| incr_state_.y_step = encoder_->TexelExtent().height; |
| incr_state_.layer_z_step = encoder_->Is3D() ? encoder_->TexelExtent().depth : 1U; |
| incr_mip_ = 1; |
| single_full_size_range_ = false; |
| } |
| |
| // Assumes full extent in width/height/depth(if present) |
| void ImageRangeGenerator::SetUpSubresIncrementer() { |
| const auto& full_range = encoder_->FullRange(); |
| const bool linear_image = encoder_->IsLinearImage(); |
| const bool is_3d = encoder_->Is3D(); |
| const bool layers_interleave = linear_image && (subres_info_->layout.arrayPitch > subres_info_->layout.size); |
| if (layers_interleave) { |
| // The implementation can interleave arrays, aspects, and mips arbitrarily |
| if (encoder_->Is3D()) { |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosFullDepth; |
| } else { |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosFullHeight; |
| } |
| } else if (is_3d || CoversAllLayers(full_range, subres_range_)) { |
| if (!linear_image) { |
| // Linear images are defined by the implementation and so we can't assume the ordering we use here |
| bool all_mips = (subres_range_.baseMipLevel == 0) && (subres_range_.levelCount == full_range.levelCount); |
| bool all_aspects = subres_range_.aspectMask == full_range.aspectMask; |
| if (all_aspects && all_mips) { |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosAllSubres; |
| } else { |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosOneAspect; |
| } |
| } else if (is_3d) { |
| // 3D implies CoversAllLayers |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosFullDepth; |
| } else { |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosAllLayers; |
| } |
| } else { |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosSomeLayers; |
| } |
| } |
| |
| void ImageRangeGenerator::SetUpIncrementer(bool all_width, bool all_height, bool all_depth) { |
| if (!all_width || encoder_->IsInterleaveY()) { |
| // Dimensional majority is not guaranteed for Linear images except in X |
| // For tiled images we can use "idealized" addresses |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosFullOffset; |
| } else if (!all_height) { |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosFullWidth; |
| } else if (encoder_->Is3D() && !all_depth) { |
| set_initial_pos_fn_ = &ImageRangeGenerator::SetInitialPosSomeDepth; |
| } else { |
| SetUpSubresIncrementer(); |
| } |
| } |
| |
| ImageRangeGenerator& ImageRangeGenerator::operator++() { |
| // Short circuit |
| if (single_full_size_range_) { |
| // Advance directly to end |
| pos_ = {0, 0}; |
| return *this; |
| } |
| |
| incr_state_.y_index += incr_state_.y_step; |
| if (incr_state_.y_index < incr_state_.y_count) { |
| incr_state_.y_base += incr_state_.incr_y; |
| pos_ = incr_state_.y_base; |
| } else { |
| incr_state_.layer_z_index += incr_state_.layer_z_step; |
| if (incr_state_.layer_z_index < incr_state_.layer_z_count) { |
| incr_state_.layer_z_base += incr_state_.incr_layer_z; |
| incr_state_.y_base = incr_state_.layer_z_base; |
| pos_ = incr_state_.y_base; |
| } else { |
| // For aspects and mips we need to move to a new subresource layer info |
| mip_index_ += incr_mip_; |
| if (mip_index_ < subres_range_.levelCount) { |
| // NOTE: This means that ImageRangeGenerator is relying on the major/minor ordering of mip and aspect in the |
| subres_index_ += incr_mip_; |
| extent_ = subres_info_->extent; // Overwrites input extent, but > 1 MIP isn't valid with input extent |
| } else { |
| const auto next_aspect_index = encoder_->LowerBoundFromMask(subres_range_.aspectMask, aspect_index_ + 1); |
| if (next_aspect_index < encoder_->Limits().aspect_index) { |
| // SubresourceLayout info in ImageRangeEncoder... it's a cheat, but it was a hotspot. |
| aspect_index_ = next_aspect_index; |
| mip_index_ = 0; |
| subres_index_ = encoder_->GetSubresourceIndex(aspect_index_, subres_range_.baseMipLevel); |
| } else { |
| // At End |
| pos_ = {0, 0}; |
| return *this; |
| } |
| } |
| |
| subres_info_ = &encoder_->GetSubresourceInfo(subres_index_); |
| SetInitialPos(subres_range_.baseArrayLayer, aspect_index_); |
| pos_ = incr_state_.y_base; |
| } |
| } |
| |
| return *this; |
| } |
| |
| template <typename AspectTraits> |
| class AspectParametersImpl : public AspectParameters { |
| public: |
| VkImageAspectFlags AspectMask() const override { return AspectTraits::kAspectMask; } |
| MaskIndexFunc MaskToIndexFunction() const override { return &AspectTraits::MaskIndex; } |
| uint32_t AspectCount() const override { return AspectTraits::kAspectCount; }; |
| const VkImageAspectFlagBits* AspectBits() const override { return AspectTraits::AspectBits().data(); } |
| }; |
| |
| struct NullAspectTraits { |
| static constexpr uint32_t kAspectCount = 0; |
| static constexpr VkImageAspectFlags kAspectMask = 0; |
| static uint32_t MaskIndex(VkImageAspectFlags mask) { return 0; }; |
| static const std::array<VkImageAspectFlagBits, kAspectCount>& AspectBits() { |
| static std::array<VkImageAspectFlagBits, kAspectCount> k_aspect_bits{}; |
| return k_aspect_bits; |
| } |
| }; |
| |
| struct ColorAspectTraits { |
| static constexpr uint32_t kAspectCount = 1; |
| static constexpr VkImageAspectFlags kAspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| static uint32_t MaskIndex(VkImageAspectFlags mask) { return 0; }; |
| static const std::array<VkImageAspectFlagBits, kAspectCount>& AspectBits() { |
| static std::array<VkImageAspectFlagBits, kAspectCount> k_aspect_bits{{VK_IMAGE_ASPECT_COLOR_BIT}}; |
| return k_aspect_bits; |
| } |
| }; |
| |
| struct DepthAspectTraits { |
| static constexpr uint32_t kAspectCount = 1; |
| static constexpr VkImageAspectFlags kAspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; |
| static uint32_t MaskIndex(VkImageAspectFlags mask) { return 0; }; |
| static const std::array<VkImageAspectFlagBits, kAspectCount>& AspectBits() { |
| static std::array<VkImageAspectFlagBits, kAspectCount> k_aspect_bits{{VK_IMAGE_ASPECT_DEPTH_BIT}}; |
| return k_aspect_bits; |
| } |
| }; |
| |
| struct StencilAspectTraits { |
| static constexpr uint32_t kAspectCount = 1; |
| static constexpr VkImageAspectFlags kAspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; |
| static uint32_t MaskIndex(VkImageAspectFlags mask) { return 0; }; |
| static const std::array<VkImageAspectFlagBits, kAspectCount>& AspectBits() { |
| static std::array<VkImageAspectFlagBits, kAspectCount> k_aspect_bits{{VK_IMAGE_ASPECT_STENCIL_BIT}}; |
| return k_aspect_bits; |
| } |
| }; |
| |
| struct DepthStencilAspectTraits { |
| // VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, >> 1 -> 1 -1 -> 0 |
| // VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, >> 1 -> 2 -1 = 1 |
| static constexpr uint32_t kAspectCount = 2; |
| static constexpr VkImageAspectFlags kAspectMask = (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); |
| static uint32_t MaskIndex(VkImageAspectFlags mask) { |
| uint32_t index = (mask >> 1) - 1; |
| assert((index == 0) || (index == 1)); |
| return index; |
| }; |
| static const std::array<VkImageAspectFlagBits, kAspectCount>& AspectBits() { |
| static std::array<VkImageAspectFlagBits, kAspectCount> k_aspect_bits{ |
| {VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_ASPECT_STENCIL_BIT}}; |
| return k_aspect_bits; |
| } |
| }; |
| |
| struct Multiplane2AspectTraits { |
| // VK_IMAGE_ASPECT_PLANE_0_BIT = 0x00000010, >> 4 - 1 -> 0 |
| // VK_IMAGE_ASPECT_PLANE_1_BIT = 0x00000020, >> 4 - 1 -> 1 |
| static constexpr uint32_t kAspectCount = 2; |
| static constexpr VkImageAspectFlags kAspectMask = (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT); |
| static uint32_t MaskIndex(VkImageAspectFlags mask) { |
| uint32_t index = (mask >> 4) - 1; |
| assert((index == 0) || (index == 1)); |
| return index; |
| }; |
| static const std::array<VkImageAspectFlagBits, kAspectCount>& AspectBits() { |
| static std::array<VkImageAspectFlagBits, kAspectCount> k_aspect_bits{ |
| {VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_ASPECT_PLANE_1_BIT}}; |
| return k_aspect_bits; |
| } |
| }; |
| |
| struct Multiplane3AspectTraits { |
| // VK_IMAGE_ASPECT_PLANE_0_BIT = 0x00000010, >> 4 - 1 -> 0 |
| // VK_IMAGE_ASPECT_PLANE_1_BIT = 0x00000020, >> 4 - 1 -> 1 |
| // VK_IMAGE_ASPECT_PLANE_2_BIT = 0x00000040, >> 4 - 1 -> 3 |
| static constexpr uint32_t kAspectCount = 3; |
| static constexpr VkImageAspectFlags kAspectMask = |
| (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT); |
| static uint32_t MaskIndex(VkImageAspectFlags mask) { |
| uint32_t index = (mask >> 4) - 1; |
| index = index > 2 ? 2 : index; |
| assert((index == 0) || (index == 1) || (index == 2)); |
| return index; |
| }; |
| static const std::array<VkImageAspectFlagBits, kAspectCount>& AspectBits() { |
| static std::array<VkImageAspectFlagBits, kAspectCount> k_aspect_bits{ |
| {VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_ASPECT_PLANE_1_BIT, VK_IMAGE_ASPECT_PLANE_2_BIT}}; |
| return k_aspect_bits; |
| } |
| }; |
| |
| // Create the encoder parameter suitable to the full range aspect mask (*must* be canonical) |
| const AspectParameters* AspectParameters::Get(VkImageAspectFlags aspect_mask) { |
| // We need a persitent instance of each specialist containing only a VTABLE each |
| static const AspectParametersImpl<ColorAspectTraits> k_color_param; |
| static const AspectParametersImpl<DepthAspectTraits> k_depth_param; |
| static const AspectParametersImpl<StencilAspectTraits> k_stencil_param; |
| static const AspectParametersImpl<DepthStencilAspectTraits> k_depth_stencil_param; |
| static const AspectParametersImpl<Multiplane2AspectTraits> k_mutliplane2_param; |
| static const AspectParametersImpl<Multiplane3AspectTraits> k_mutliplane3_param; |
| static const AspectParametersImpl<NullAspectTraits> k_null_aspect; |
| |
| const AspectParameters* param; |
| switch (aspect_mask) { |
| case ColorAspectTraits::kAspectMask: |
| param = &k_color_param; |
| break; |
| case DepthAspectTraits::kAspectMask: |
| param = &k_depth_param; |
| break; |
| case StencilAspectTraits::kAspectMask: |
| param = &k_stencil_param; |
| break; |
| case DepthStencilAspectTraits::kAspectMask: |
| param = &k_depth_stencil_param; |
| break; |
| case Multiplane2AspectTraits::kAspectMask: |
| param = &k_mutliplane2_param; |
| break; |
| case Multiplane3AspectTraits::kAspectMask: |
| param = &k_mutliplane3_param; |
| break; |
| default: |
| assert(false); |
| param = &k_null_aspect; |
| } |
| return param; |
| } |
| |
| inline ImageRangeEncoder::SubresInfo::SubresInfo(const VkSubresourceLayout& layout_, const VkExtent3D& extent_, |
| const VkExtent3D& texel_extent, double texel_size) |
| : layout(layout_), |
| extent(extent_), |
| y_step_pitch(layout.rowPitch * texel_extent.height), |
| z_step_pitch(layout.depthPitch * texel_extent.depth), |
| layer_span(layout.rowPitch * extent_.height) {} |
| |
| void ImageRangeGenerator::IncrementerState::Set(uint32_t y_count_, uint32_t layer_z_count_, IndexType base, IndexType span, |
| IndexType y_step, IndexType z_step) { |
| y_count = y_count_; |
| layer_z_count = layer_z_count_; |
| y_index = 0; |
| layer_z_index = 0; |
| y_base.begin = base; |
| y_base.end = base + span; |
| layer_z_base = y_base; |
| incr_y = y_step; |
| incr_layer_z = z_step; |
| } |
| |
| }; // namespace subresource_adapter |