Vulken: Mix array aspect of image views

The image views type can change depending based on how they are bound
to shaders. When a shader accesses a view without array operations,
the image view should not be an array. This was previously ignored.

Pull Request: https://projects.blender.org/blender/blender/pulls/123726
This commit is contained in:
Jeroen Bakker 2024-06-25 15:15:18 +02:00
parent 4fd39f4103
commit ee0b7b9a95
12 changed files with 165 additions and 36 deletions

@ -149,12 +149,12 @@ class VKRenderGraph : public NonCopyable {
std::scoped_lock lock(resources_.mutex);
static VKRenderGraphNode node_template = {};
NodeHandle node_handle = nodes_.append_and_get_index(node_template);
#if 1
#if 0
/* Useful during debugging. When a validation error occurs during submission we know the node
* type and node handle, but we don't know when and by who that specific node was added to the
* render graph. By enabling this part of the code and set the correct node_handle and node
* type a debugger can break at the moment the node has been added to the render graph. */
if (node_handle == 851 && NodeInfo::node_type == VKNodeType::DRAW) {
if (node_handle == 267 && NodeInfo::node_type == VKNodeType::DRAW) {
std::cout << "break\n";
}
#endif

@ -698,34 +698,67 @@ VkImageType to_vk_image_type(const eGPUTextureType type)
return VK_IMAGE_TYPE_1D;
}
VkImageViewType to_vk_image_view_type(const eGPUTextureType type, const eImageViewUsage view_type)
VkImageViewType to_vk_image_view_type(const eGPUTextureType type,
const eImageViewUsage view_type,
VKImageViewArrayed arrayed)
{
VkImageViewType result = VK_IMAGE_VIEW_TYPE_1D;
switch (type) {
case GPU_TEXTURE_1D:
case GPU_TEXTURE_BUFFER:
return VK_IMAGE_VIEW_TYPE_1D;
result = VK_IMAGE_VIEW_TYPE_1D;
break;
case GPU_TEXTURE_2D:
return VK_IMAGE_VIEW_TYPE_2D;
result = VK_IMAGE_VIEW_TYPE_2D;
break;
case GPU_TEXTURE_3D:
return VK_IMAGE_VIEW_TYPE_3D;
result = VK_IMAGE_VIEW_TYPE_3D;
break;
case GPU_TEXTURE_CUBE:
return view_type == eImageViewUsage::Attachment ? VK_IMAGE_VIEW_TYPE_2D_ARRAY :
VK_IMAGE_VIEW_TYPE_CUBE;
result = view_type == eImageViewUsage::Attachment ? VK_IMAGE_VIEW_TYPE_2D_ARRAY :
VK_IMAGE_VIEW_TYPE_CUBE;
break;
case GPU_TEXTURE_1D_ARRAY:
return VK_IMAGE_VIEW_TYPE_1D_ARRAY;
result = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
break;
case GPU_TEXTURE_2D_ARRAY:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
result = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
break;
case GPU_TEXTURE_CUBE_ARRAY:
return view_type == eImageViewUsage::Attachment ? VK_IMAGE_VIEW_TYPE_2D_ARRAY :
VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
result = view_type == eImageViewUsage::Attachment ? VK_IMAGE_VIEW_TYPE_2D_ARRAY :
VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
break;
case GPU_TEXTURE_ARRAY:
/* GPU_TEXTURE_ARRAY should always be used together with 1D, 2D, or CUBE. */
break;
}
BLI_assert_unreachable();
return VK_IMAGE_VIEW_TYPE_1D;
if (arrayed == VKImageViewArrayed::NOT_ARRAYED) {
if (result == VK_IMAGE_VIEW_TYPE_1D_ARRAY) {
result = VK_IMAGE_VIEW_TYPE_1D;
}
else if (result == VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
result = VK_IMAGE_VIEW_TYPE_2D;
}
else if (result == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) {
result = VK_IMAGE_VIEW_TYPE_CUBE;
}
}
else if (arrayed == VKImageViewArrayed::ARRAYED) {
if (result == VK_IMAGE_VIEW_TYPE_1D) {
result = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
}
else if (result == VK_IMAGE_VIEW_TYPE_2D) {
result = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
}
else if (result == VK_IMAGE_VIEW_TYPE_CUBE) {
result = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
}
}
return result;
}
VkComponentSwizzle to_vk_component_swizzle(const char swizzle)

@ -38,6 +38,12 @@ enum class eImageViewUsage {
Attachment,
};
enum class VKImageViewArrayed {
DONT_CARE,
NOT_ARRAYED,
ARRAYED,
};
VkImageAspectFlags to_vk_image_aspect_flag_bits(const eGPUTextureFormat format);
VkImageAspectFlags to_vk_image_aspect_flag_bits(const eGPUFrameBufferBits buffers);
VkFormat to_vk_format(const eGPUTextureFormat format);
@ -48,7 +54,9 @@ VkFormat to_vk_format(const GPUVertCompType type,
VkFormat to_vk_format(const shader::Type type);
VkComponentSwizzle to_vk_component_swizzle(const char swizzle);
VkImageViewType to_vk_image_view_type(const eGPUTextureType type, eImageViewUsage view_type);
VkImageViewType to_vk_image_view_type(const eGPUTextureType type,
eImageViewUsage view_type,
VKImageViewArrayed arrayed);
VkImageType to_vk_image_type(const eGPUTextureType type);
VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const void *data);
VkIndexType to_vk_index_type(const GPUIndexBufType index_type);

@ -84,21 +84,25 @@ void VKDescriptorSetTracker::bind_as_ssbo(VKUniformBuffer &buffer,
}
void VKDescriptorSetTracker::image_bind(VKTexture &texture,
const VKDescriptorSet::Location location)
const VKDescriptorSet::Location location,
VKImageViewArrayed arrayed)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
binding.texture = &texture;
binding.arrayed = arrayed;
}
void VKDescriptorSetTracker::bind(VKTexture &texture,
const VKDescriptorSet::Location location,
const VKSampler &sampler)
const VKSampler &sampler,
VKImageViewArrayed arrayed)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
binding.texture = &texture;
binding.vk_sampler = sampler.vk_handle();
binding.arrayed = arrayed;
}
void VKDescriptorSetTracker::bind(VKVertexBuffer &vertex_buffer,
@ -188,7 +192,7 @@ void VKDescriptorSetTracker::update(VKContext &context)
* VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL or VK_IMAGE_LAYOUT_GENERAL. */
VkDescriptorImageInfo image_info = {};
image_info.sampler = binding.vk_sampler;
image_info.imageView = binding.texture->image_view_get().vk_handle();
image_info.imageView = binding.texture->image_view_get(binding.arrayed).vk_handle();
image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
image_infos.append(image_info);

@ -125,6 +125,7 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
VKTexture *texture = nullptr;
VkSampler vk_sampler = VK_NULL_HANDLE;
VKImageViewArrayed arrayed = VKImageViewArrayed::DONT_CARE;
Binding()
{
@ -167,8 +168,13 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
void bind(VKStorageBuffer &buffer, VKDescriptorSet::Location location);
void bind(VKUniformBuffer &buffer, VKDescriptorSet::Location location);
/* TODO: bind as image */
void image_bind(VKTexture &texture, VKDescriptorSet::Location location);
void bind(VKTexture &texture, VKDescriptorSet::Location location, const VKSampler &sampler);
void image_bind(VKTexture &texture,
VKDescriptorSet::Location location,
VKImageViewArrayed arrayed);
void bind(VKTexture &texture,
VKDescriptorSet::Location location,
const VKSampler &sampler,
VKImageViewArrayed);
/* Bind as uniform texel buffer. */
void bind(VKVertexBuffer &vertex_buffer, VKDescriptorSet::Location location);

@ -559,7 +559,8 @@ void VKFrameBuffer::rendering_ensure(VKContext &context)
IndexRange(attachment.mip, 1),
{{'r', 'g', 'b', 'a'}},
false,
srgb_ && enabled_srgb_};
srgb_ && enabled_srgb_,
VKImageViewArrayed::DONT_CARE};
vk_image_view = color_texture.image_view_get(image_view_info).vk_handle();
}
attachment_info.imageView = vk_image_view;
@ -599,7 +600,8 @@ void VKFrameBuffer::rendering_ensure(VKContext &context)
IndexRange(attachment.mip, 1),
{{'r', 'g', 'b', 'a'}},
is_stencil_attachment,
false};
false,
VKImageViewArrayed::DONT_CARE};
depth_image_view = depth_texture.image_view_get(image_view_info).vk_handle();
}

@ -47,7 +47,7 @@ VKImageView::VKImageView(VKTexture &texture, const VKImageViewInfo &info, String
VkImageViewCreateInfo image_view_info = {};
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
image_view_info.image = texture.vk_image_handle();
image_view_info.viewType = to_vk_image_view_type(texture.type_get(), info.usage);
image_view_info.viewType = to_vk_image_view_type(texture.type_get(), info.usage, info.arrayed);
image_view_info.format = vk_format_;
image_view_info.components.r = to_vk_component_swizzle(info.swizzle[0]);
image_view_info.components.g = to_vk_component_swizzle(info.swizzle[1]);

@ -26,12 +26,20 @@ struct VKImageViewInfo {
};
bool use_stencil;
bool use_srgb;
/**
* When binding an image to a shader it needs to match the operations used inside the shader.
*
* If an shader accesses an image via an image view using the operation should match the view.
* arrayed will ensure the right image view is created.
*/
VKImageViewArrayed arrayed;
bool operator==(const VKImageViewInfo &other) const
{
return usage == other.usage && layer_range == other.layer_range &&
mip_range == other.mip_range && swizzle_data == other.swizzle_data &&
use_stencil == other.use_stencil && use_srgb == other.use_srgb;
use_stencil == other.use_stencil && use_srgb == other.use_srgb &&
arrayed == other.arrayed;
}
};

@ -174,6 +174,9 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
descriptor_set_bind_types_.fill(shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER);
access_masks_ = Array<VkAccessFlags>(resources_len);
access_masks_.fill(VK_ACCESS_NONE);
arrayed_ = Array<VKImageViewArrayed>(resources_len);
arrayed_.fill(VKImageViewArrayed::DONT_CARE);
uint32_t descriptor_set_location = 0;
for (const ShaderCreateInfo::SubpassIn &subpass_in : info.subpass_inputs_) {
const ShaderInput *input = shader_input_get(
@ -183,12 +186,50 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
descriptor_set_location_update(input,
descriptor_set_location++,
shader::ShaderCreateInfo::Resource::BindType::SAMPLER,
std::nullopt);
std::nullopt,
VKImageViewArrayed::DONT_CARE);
}
for (ShaderCreateInfo::Resource &res : all_resources) {
const ShaderInput *input = shader_input_get(res);
BLI_assert(input);
descriptor_set_location_update(input, descriptor_set_location++, res.bind_type, res);
VKImageViewArrayed arrayed = VKImageViewArrayed::DONT_CARE;
if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
arrayed = ELEM(res.image.type,
shader::ImageType::FLOAT_1D_ARRAY,
shader::ImageType::FLOAT_2D_ARRAY,
shader::ImageType::FLOAT_CUBE_ARRAY,
shader::ImageType::INT_1D_ARRAY,
shader::ImageType::INT_2D_ARRAY,
shader::ImageType::INT_CUBE_ARRAY,
shader::ImageType::UINT_1D_ARRAY,
shader::ImageType::UINT_2D_ARRAY,
shader::ImageType::UINT_CUBE_ARRAY,
shader::ImageType::UINT_2D_ARRAY_ATOMIC,
shader::ImageType::INT_2D_ARRAY_ATOMIC) ?
VKImageViewArrayed::ARRAYED :
VKImageViewArrayed::NOT_ARRAYED;
}
else if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
arrayed = ELEM(res.sampler.type,
shader::ImageType::FLOAT_1D_ARRAY,
shader::ImageType::FLOAT_2D_ARRAY,
shader::ImageType::FLOAT_CUBE_ARRAY,
shader::ImageType::INT_1D_ARRAY,
shader::ImageType::INT_2D_ARRAY,
shader::ImageType::INT_CUBE_ARRAY,
shader::ImageType::UINT_1D_ARRAY,
shader::ImageType::UINT_2D_ARRAY,
shader::ImageType::UINT_CUBE_ARRAY,
shader::ImageType::SHADOW_2D_ARRAY,
shader::ImageType::SHADOW_CUBE_ARRAY,
shader::ImageType::DEPTH_2D_ARRAY,
shader::ImageType::DEPTH_CUBE_ARRAY,
shader::ImageType::UINT_2D_ARRAY_ATOMIC,
shader::ImageType::INT_2D_ARRAY_ATOMIC) ?
VKImageViewArrayed::ARRAYED :
VKImageViewArrayed::NOT_ARRAYED;
}
descriptor_set_location_update(input, descriptor_set_location++, res.bind_type, res, arrayed);
}
/* Post initializing push constants. */
@ -200,7 +241,8 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
descriptor_set_location_update(push_constant_input,
push_constants_fallback_location,
shader::ShaderCreateInfo::Resource::UNIFORM_BUFFER,
std::nullopt);
std::nullopt,
VKImageViewArrayed::DONT_CARE);
}
push_constants_layout_.init(
info, *this, push_constants_storage_type, push_constant_descriptor_set_location);
@ -217,7 +259,8 @@ void VKShaderInterface::descriptor_set_location_update(
const ShaderInput *shader_input,
const VKDescriptorSet::Location location,
const shader::ShaderCreateInfo::Resource::BindType bind_type,
std::optional<const shader::ShaderCreateInfo::Resource> resource)
std::optional<const shader::ShaderCreateInfo::Resource> resource,
VKImageViewArrayed arrayed)
{
BLI_assert_msg(resource.has_value() ||
ELEM(bind_type,
@ -232,6 +275,7 @@ void VKShaderInterface::descriptor_set_location_update(
BLI_assert(descriptor_set_locations_[index].binding == -1);
descriptor_set_locations_[index] = location;
descriptor_set_bind_types_[index] = bind_type;
arrayed_[index] = arrayed;
VkAccessFlags vk_access_flags = VK_ACCESS_NONE;
if (resource.has_value()) {
@ -326,6 +370,17 @@ const VkAccessFlags VKShaderInterface::access_mask(
return access_mask(shader_input);
}
const VKImageViewArrayed VKShaderInterface::arrayed(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
{
const ShaderInput *shader_input = shader_input_get(bind_type, binding);
if (shader_input == nullptr) {
return VKImageViewArrayed::DONT_CARE;
}
int32_t index = shader_input_index(inputs_, shader_input);
return arrayed_[index];
}
const ShaderInput *VKShaderInterface::shader_input_get(
const shader::ShaderCreateInfo::Resource &resource) const
{

@ -23,6 +23,9 @@ class VKShaderInterface : public ShaderInterface {
private:
Array<VKDescriptorSet::Location> descriptor_set_locations_;
Array<shader::ShaderCreateInfo::Resource::BindType> descriptor_set_bind_types_;
/** Image views should match the binding arrayed aspect. */
Array<VKImageViewArrayed> arrayed_;
Array<VkAccessFlags> access_masks_;
VKDescriptorSetLayoutInfo descriptor_set_layout_info_;
@ -59,6 +62,8 @@ class VKShaderInterface : public ShaderInterface {
*/
const VkAccessFlags access_mask(const shader::ShaderCreateInfo::Resource::BindType &bind_type,
int binding) const;
const VKImageViewArrayed arrayed(const shader::ShaderCreateInfo::Resource::BindType &bind_type,
int binding) const;
/** Get the Layout of the shader. */
const VKPushConstants::Layout &push_constants_layout_get() const
@ -103,7 +108,8 @@ class VKShaderInterface : public ShaderInterface {
const ShaderInput *shader_input,
const VKDescriptorSet::Location location,
const shader::ShaderCreateInfo::Resource::BindType bind_type,
std::optional<const shader::ShaderCreateInfo::Resource> resource);
std::optional<const shader::ShaderCreateInfo::Resource> resource,
VKImageViewArrayed arrayed);
};
} // namespace blender::gpu

@ -528,13 +528,14 @@ void VKTexture::add_to_descriptor_set(AddToDescriptorSetContext &data,
const std::optional<VKDescriptorSet::Location> location =
data.shader_interface.descriptor_set_location(bind_type, binding);
if (location) {
const VKImageViewArrayed arrayed = data.shader_interface.arrayed(bind_type, binding);
if (bind_type == shader::ShaderCreateInfo::Resource::BindType::IMAGE) {
data.descriptor_set.image_bind(*this, *location);
data.descriptor_set.image_bind(*this, *location, arrayed);
}
else {
VKDevice &device = VKBackend::get().device;
const VKSampler &sampler = device.samplers().get(sampler_state);
data.descriptor_set.bind(*this, *location, sampler);
data.descriptor_set.bind(*this, *location, sampler, arrayed);
}
data.resource_access_info.images.append({vk_image_handle(),
data.shader_interface.access_mask(bind_type, binding),
@ -593,7 +594,7 @@ const VKImageView &VKTexture::image_view_get(const VKImageViewInfo &info)
if (is_texture_view()) {
// TODO: API should be improved as we don't support image view specialization.
// In the current API this is still possible to setup when using attachments.
return image_view_get();
return image_view_get(info.arrayed);
}
for (const VKImageView &image_view : image_views_) {
if (image_view.info == info) {
@ -605,12 +606,17 @@ const VKImageView &VKTexture::image_view_get(const VKImageViewInfo &info)
return image_views_.last();
}
const VKImageView &VKTexture::image_view_get()
const VKImageView &VKTexture::image_view_get(VKImageViewArrayed arrayed)
{
image_view_info_.layer_range = layer_range();
image_view_info_.mip_range = mip_map_range();
image_view_info_.use_srgb = true;
image_view_info_.use_stencil = use_stencil_;
image_view_info_.arrayed = arrayed;
image_view_info_.layer_range = layer_range();
if (arrayed == VKImageViewArrayed::NOT_ARRAYED) {
image_view_info_.layer_range = image_view_info_.layer_range.slice(
0, ELEM(type_, GPU_TEXTURE_CUBE, GPU_TEXTURE_CUBE_ARRAY) ? 6 : 1);
}
if (is_texture_view()) {
return source_texture_->image_view_get(image_view_info_);

@ -52,7 +52,8 @@ class VKTexture : public Texture, public VKBindableResource {
IndexRange(0, VK_REMAINING_MIP_LEVELS),
{{'r', 'g', 'b', 'a'}},
false,
false};
false,
VKImageViewArrayed::DONT_CARE};
public:
VKTexture(const char *name) : Texture(name) {}
@ -114,7 +115,7 @@ class VKTexture : public Texture, public VKBindableResource {
/**
* Get the current image view for this texture.
*/
const VKImageView &image_view_get();
const VKImageView &image_view_get(VKImageViewArrayed arrayed);
protected:
bool init_internal() override;