tests/shader_runner: Add support for cube resources.

This commit is contained in:
Elizabeth Figura
2025-04-17 22:07:34 -05:00
committed by Henri Verbeet
parent b58ff893a5
commit e312207124
Notes: Henri Verbeet 2025-06-26 17:50:53 +02:00
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1606
9 changed files with 177 additions and 58 deletions

View File

@@ -621,6 +621,12 @@ static void parse_resource_directive(struct resource_params *resource, const cha
{
resource->desc.dimension = RESOURCE_DIMENSION_3D;
}
else if (sscanf(line, "( cube , %u ) ", &resource->desc.width) == 1)
{
resource->desc.dimension = RESOURCE_DIMENSION_CUBE;
resource->desc.height = resource->desc.width;
resource->desc.layer_count = 6;
}
else
{
fatal_error("Malformed resource size '%s'.\n", line);
@@ -652,9 +658,6 @@ static void parse_resource_directive(struct resource_params *resource, const cha
if (rest == line)
break;
if (resource->desc.layer_count > 1)
fatal_error("Upload not implemented for 2d arrays.\n");
vkd3d_array_reserve((void **)&resource->data, &resource->data_capacity, resource->data_size + sizeof(u), 1);
memcpy(resource->data + resource->data_size, &u, sizeof(u));
resource->data_size += sizeof(u);

View File

@@ -86,6 +86,7 @@ enum resource_dimension
RESOURCE_DIMENSION_BUFFER,
RESOURCE_DIMENSION_2D,
RESOURCE_DIMENSION_3D,
RESOURCE_DIMENSION_CUBE,
};
struct resource_desc

View File

@@ -417,10 +417,14 @@ static void init_subresource_data(D3D11_SUBRESOURCE_DATA *resource_data, const s
unsigned int level_height = get_level_dimension(params->desc.height, level);
unsigned int level_depth = get_level_dimension(params->desc.depth, level);
resource_data[level].pSysMem = &params->data[buffer_offset];
resource_data[level].SysMemPitch = level_width * params->desc.texel_size;
resource_data[level].SysMemSlicePitch = level_height * resource_data[level].SysMemPitch;
buffer_offset += level_depth * resource_data[level].SysMemSlicePitch;
for (unsigned int layer = 0; layer < params->desc.layer_count; ++layer)
{
D3D11_SUBRESOURCE_DATA *subresource = &resource_data[level * params->desc.layer_count + layer];
subresource->pSysMem = &params->data[buffer_offset];
subresource->SysMemPitch = level_width * params->desc.texel_size;
subresource->SysMemSlicePitch = level_height * subresource->SysMemPitch;
buffer_offset += level_depth * subresource->SysMemSlicePitch;
}
}
}
@@ -443,7 +447,7 @@ static void create_identity_view(ID3D11Device *device,
static bool init_resource_2d(struct d3d11_shader_runner *runner, struct d3d11_resource *resource,
const struct resource_params *params)
{
D3D11_SUBRESOURCE_DATA resource_data[3];
D3D11_SUBRESOURCE_DATA resource_data[6];
ID3D11Device *device = runner->device;
D3D11_TEXTURE2D_DESC desc = {0};
UINT quality_levels;
@@ -475,6 +479,9 @@ static bool init_resource_2d(struct d3d11_shader_runner *runner, struct d3d11_re
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = get_bind_flags(params);
if (params->desc.dimension == RESOURCE_DIMENSION_CUBE)
desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
if (params->data)
{
if (params->desc.sample_count > 1)
@@ -497,7 +504,7 @@ static bool init_resource_2d(struct d3d11_shader_runner *runner, struct d3d11_re
static bool init_resource_3d(struct d3d11_shader_runner *runner, struct d3d11_resource *resource,
const struct resource_params *params)
{
D3D11_SUBRESOURCE_DATA resource_data[3];
D3D11_SUBRESOURCE_DATA resource_data[6];
ID3D11Device *device = runner->device;
D3D11_TEXTURE3D_DESC desc = {0};
HRESULT hr;
@@ -600,7 +607,9 @@ static struct resource *d3d11_runner_create_resource(struct shader_runner *r, co
case RESOURCE_TYPE_TEXTURE:
if (params->desc.dimension == RESOURCE_DIMENSION_BUFFER)
init_resource_srv_buffer(runner, resource, params);
else if (params->desc.dimension == RESOURCE_DIMENSION_2D && !init_resource_2d(runner, resource, params))
else if ((params->desc.dimension == RESOURCE_DIMENSION_2D
|| params->desc.dimension == RESOURCE_DIMENSION_CUBE)
&& !init_resource_2d(runner, resource, params))
return NULL;
else if (params->desc.dimension == RESOURCE_DIMENSION_3D && !init_resource_3d(runner, resource, params))
return NULL;

View File

@@ -83,7 +83,7 @@ static struct resource *d3d12_runner_create_resource(struct shader_runner *r, co
struct d3d12_shader_runner *runner = d3d12_shader_runner(r);
struct test_context *test_context = &runner->test_context;
ID3D12Device *device = test_context->device;
D3D12_SUBRESOURCE_DATA resource_data[3] = {0};
D3D12_SUBRESOURCE_DATA resource_data[6] = {0};
D3D12_RESOURCE_STATES initial_state, state;
struct d3d12_resource *resource;
unsigned int buffer_offset = 0;
@@ -100,10 +100,14 @@ static struct resource *d3d12_runner_create_resource(struct shader_runner *r, co
unsigned int level_height = get_level_dimension(params->desc.height, level);
unsigned int level_depth = get_level_dimension(params->desc.depth, level);
resource_data[level].pData = &params->data[buffer_offset];
resource_data[level].RowPitch = level_width * params->desc.texel_size;
resource_data[level].SlicePitch = level_height * resource_data[level].RowPitch;
buffer_offset += level_depth * resource_data[level].SlicePitch;
for (unsigned int layer = 0; layer < params->desc.layer_count; ++layer)
{
D3D12_SUBRESOURCE_DATA *subresource = &resource_data[level * params->desc.layer_count + layer];
subresource->pData = &params->data[buffer_offset];
subresource->RowPitch = level_width * params->desc.texel_size;
subresource->SlicePitch = level_height * subresource->RowPitch;
buffer_offset += level_depth * subresource->SlicePitch;
}
}
state = resource_get_state(&resource->r);
@@ -175,16 +179,16 @@ static struct resource *d3d12_runner_create_resource(struct shader_runner *r, co
if (params->desc.sample_count > 1 && params->desc.level_count > 1)
fatal_error("Multisampled texture has multiple levels.\n");
if (params->desc.dimension == RESOURCE_DIMENSION_2D)
{
dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
depth = params->desc.layer_count;
}
else
if (params->desc.dimension == RESOURCE_DIMENSION_3D)
{
dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
depth = params->desc.depth;
}
else
{
dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
depth = params->desc.layer_count;
}
resource->resource = create_default_texture_(__FILE__, __LINE__, device,
dimension, params->desc.width, params->desc.height, depth,
@@ -195,13 +199,32 @@ static struct resource *d3d12_runner_create_resource(struct shader_runner *r, co
{
if (params->desc.sample_count > 1)
fatal_error("Cannot upload data to a multisampled texture.\n");
upload_texture_data_with_states(resource->resource, resource_data, params->desc.level_count,
upload_texture_data_with_states(resource->resource, resource_data,
params->desc.level_count * params->desc.layer_count,
test_context->queue, test_context->list, RESOURCE_STATE_DO_NOT_CHANGE, state);
reset_command_list(test_context->list, test_context->allocator);
}
ID3D12Device_CreateShaderResourceView(device, resource->resource,
NULL, get_cpu_descriptor_handle(test_context, runner->heap, resource->r.desc.slot));
if (params->desc.dimension == RESOURCE_DIMENSION_CUBE)
{
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc =
{
.Format = params->desc.format,
.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE,
.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
.TextureCube.MostDetailedMip = 0,
.TextureCube.MipLevels = params->desc.level_count,
.TextureCube.ResourceMinLODClamp = 0.0f,
};
ID3D12Device_CreateShaderResourceView(device, resource->resource,
&srv_desc, get_cpu_descriptor_handle(test_context, runner->heap, resource->r.desc.slot));
}
else
{
ID3D12Device_CreateShaderResourceView(device, resource->resource,
NULL, get_cpu_descriptor_handle(test_context, runner->heap, resource->r.desc.slot));
}
}
break;
@@ -256,7 +279,8 @@ static struct resource *d3d12_runner_create_resource(struct shader_runner *r, co
if (params->data)
{
upload_texture_data_with_states(resource->resource, resource_data, params->desc.level_count,
upload_texture_data_with_states(resource->resource, resource_data,
params->desc.level_count * params->desc.layer_count,
test_context->queue, test_context->list, RESOURCE_STATE_DO_NOT_CHANGE, state);
reset_command_list(test_context->list, test_context->allocator);
}

View File

@@ -31,6 +31,7 @@ struct d3d9_resource
{
struct resource r;
IDirect3DCubeTexture9 *cube;
IDirect3DSurface9 *surface;
IDirect3DTexture9 *texture;
IDirect3DVertexBuffer9 *vb;
@@ -291,6 +292,34 @@ static struct resource *d3d9_runner_create_resource(struct shader_runner *r, con
}
break;
}
else if (params->desc.dimension == RESOURCE_DIMENSION_CUBE)
{
hr = IDirect3DDevice9_CreateCubeTexture(device, params->desc.width,
params->desc.level_count, 0, format, D3DPOOL_MANAGED, &resource->cube, NULL);
ok(hr == D3D_OK, "Failed to create texture, hr %#lx.\n", hr);
for (unsigned int level = 0; level < params->desc.level_count; ++level)
{
unsigned int level_width = get_level_dimension(params->desc.width, level);
unsigned int src_row_pitch = level_width * params->desc.texel_size;
unsigned int src_slice_pitch = level_width * src_row_pitch;
D3DLOCKED_RECT map_desc;
for (unsigned int face = 0; face < 6; ++face)
{
hr = IDirect3DCubeTexture9_LockRect(resource->cube, face, level, &map_desc, NULL, 0);
ok(hr == D3D_OK, "Failed to map texture, hr %#lx.\n", hr);
for (unsigned int y = 0; y < level_width; ++y)
memcpy(&((uint8_t *)map_desc.pBits)[y * map_desc.Pitch],
&params->data[src_buffer_offset + y * src_row_pitch], src_row_pitch);
hr = IDirect3DCubeTexture9_UnlockRect(resource->cube, face, level);
ok(hr == D3D_OK, "Failed to unmap texture, hr %#lx.\n", hr);
src_buffer_offset += src_slice_pitch;
}
}
break;
}
}
case RESOURCE_TYPE_UAV:
@@ -317,6 +346,8 @@ static void d3d9_runner_destroy_resource(struct shader_runner *r, struct resourc
{
struct d3d9_resource *resource = d3d9_resource(res);
if (resource->cube)
IDirect3DCubeTexture9_Release(resource->cube);
if (resource->surface)
IDirect3DSurface9_Release(resource->surface);
if (resource->texture)
@@ -461,9 +492,12 @@ static bool d3d9_runner_draw(struct shader_runner *r,
if (resource->r.desc.dimension == RESOURCE_DIMENSION_2D)
hr = IDirect3DDevice9_SetTexture(device, resource->r.desc.slot,
(IDirect3DBaseTexture9 *)resource->texture);
else
else if (resource->r.desc.dimension == RESOURCE_DIMENSION_3D)
hr = IDirect3DDevice9_SetTexture(device, resource->r.desc.slot,
(IDirect3DBaseTexture9 *)resource->volume);
else
hr = IDirect3DDevice9_SetTexture(device, resource->r.desc.slot,
(IDirect3DBaseTexture9 *)resource->cube);
ok(hr == D3D_OK, "Failed to set texture, hr %#lx.\n", hr);
break;

View File

@@ -455,10 +455,14 @@ static bool init_resource_texture(struct gl_resource *resource, const struct res
else
target = GL_TEXTURE_2D;
}
else
else if (params->desc.dimension == RESOURCE_DIMENSION_3D)
{
target = GL_TEXTURE_3D;
}
else
{
target = GL_TEXTURE_CUBE_MAP;
}
resource->target = target;
resource->format = get_format_info(params->desc.format, params->is_shadow);
@@ -487,7 +491,7 @@ static bool init_resource_texture(struct gl_resource *resource, const struct res
if (params->desc.dimension == RESOURCE_DIMENSION_3D)
glTexStorage3D(target, params->desc.level_count, resource->format->internal_format,
params->desc.width, params->desc.height, params->desc.depth);
else if (params->desc.layer_count > 1)
else if (params->desc.layer_count > 1 && params->desc.dimension != RESOURCE_DIMENSION_CUBE)
glTexStorage3D(target, params->desc.level_count, resource->format->internal_format,
params->desc.width, params->desc.height, params->desc.layer_count);
else
@@ -511,6 +515,25 @@ static bool init_resource_texture(struct gl_resource *resource, const struct res
resource->format->type, params->data + offset);
offset += w * h * d * params->desc.texel_size;
}
else if (params->desc.dimension == RESOURCE_DIMENSION_CUBE)
{
static const GLenum faces[] =
{
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
};
for (unsigned int face = 0; face < 6; ++face)
{
glTexSubImage2D(faces[face], i, 0, 0, w, h, resource->format->format,
resource->format->type, params->data + offset);
offset += w * h * params->desc.texel_size;
}
}
else if (params->desc.layer_count > 1)
{
glTexSubImage3D(target, i, 0, 0, 0, w, h, params->desc.layer_count, resource->format->format,

View File

@@ -270,6 +270,9 @@ static void init_resource_texture(struct metal_runner *runner,
case RESOURCE_DIMENSION_3D:
desc.textureType = MTLTextureType3D;
break;
case RESOURCE_DIMENSION_CUBE:
desc.textureType = MTLTextureTypeCube;
break;
default:
fatal_error("Unhandled resource dimension %#x.\n", params->desc.dimension);
}
@@ -278,7 +281,10 @@ static void init_resource_texture(struct metal_runner *runner,
desc.width = params->desc.width;
desc.height = params->desc.height;
desc.depth = params->desc.depth;
desc.arrayLength = params->desc.layer_count;
if (params->desc.dimension == RESOURCE_DIMENSION_CUBE)
desc.arrayLength = params->desc.layer_count / 6;
else
desc.arrayLength = params->desc.layer_count;
desc.mipmapLevelCount = params->desc.level_count;
desc.sampleCount = max(params->desc.sample_count, 1);
desc.storageMode = MTLStorageModePrivate;
@@ -304,7 +310,7 @@ static void init_resource_texture(struct metal_runner *runner,
if (params->data)
{
unsigned int buffer_offset = 0, level, level_width, level_height, level_depth;
unsigned int buffer_offset = 0, layer, level, level_width, level_height, level_depth;
id<MTLCommandBuffer> command_buffer;
id<MTLBlitCommandEncoder> blit;
id<MTLTexture> upload_texture;
@@ -320,13 +326,17 @@ static void init_resource_texture(struct metal_runner *runner,
level_width = get_level_dimension(params->desc.width, level);
level_height = get_level_dimension(params->desc.height, level);
level_depth = get_level_dimension(params->desc.depth, level);
[upload_texture replaceRegion:MTLRegionMake3D(0, 0, 0, level_width, level_height, level_depth)
mipmapLevel:level
slice:0
withBytes:&params->data[buffer_offset]
bytesPerRow:level_width * params->desc.texel_size
bytesPerImage:level_height * level_width * params->desc.texel_size];
buffer_offset += level_depth * level_height * level_width * params->desc.texel_size;
for (layer = 0; layer < params->desc.layer_count; ++layer)
{
[upload_texture replaceRegion:MTLRegionMake3D(0, 0, 0, level_width, level_height, level_depth)
mipmapLevel:level
slice:layer
withBytes:&params->data[buffer_offset]
bytesPerRow:level_width * params->desc.texel_size
bytesPerImage:level_height * level_width * params->desc.texel_size];
buffer_offset += level_depth * level_height * level_width * params->desc.texel_size;
}
}
command_buffer = [runner->queue commandBuffer];

View File

@@ -97,6 +97,7 @@ static void resource_init_texture(struct vulkan_shader_runner *runner, struct vu
VkDevice device = context->device;
unsigned int buffer_offset = 0;
VkDeviceMemory staging_memory;
VkImageCreateFlags flags = 0;
VkBuffer staging_buffer;
VkImageType image_type;
void *data;
@@ -112,10 +113,14 @@ static void resource_init_texture(struct vulkan_shader_runner *runner, struct vu
else
image_type = VK_IMAGE_TYPE_2D;
if (params->desc.dimension == RESOURCE_DIMENSION_CUBE)
flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
resource->image = create_vulkan_image(context, image_type, desc->width, desc->height, desc->depth,
desc->level_count, desc->layer_count, desc->sample_count, usage, format, &resource->memory);
desc->level_count, desc->layer_count, desc->sample_count, usage, format, flags, &resource->memory);
resource->image_view = create_vulkan_image_view(context,
resource->image, format, VK_IMAGE_ASPECT_COLOR_BIT, image_type, layer_count);
resource->image, format, VK_IMAGE_ASPECT_COLOR_BIT, image_type,
(params->desc.dimension == RESOURCE_DIMENSION_CUBE), layer_count);
if (!params->data)
{
@@ -142,19 +147,24 @@ static void resource_init_texture(struct vulkan_shader_runner *runner, struct vu
unsigned int level_width = get_level_dimension(params->desc.width, level);
unsigned int level_height = get_level_dimension(params->desc.height, level);
unsigned int level_depth = get_level_dimension(params->desc.depth, level);
VkBufferImageCopy region = {0};
region.bufferOffset = buffer_offset;
region.imageSubresource.mipLevel = level;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = 1;
region.imageExtent.width = level_width;
region.imageExtent.height = level_height;
region.imageExtent.depth = level_depth;
VK_CALL(vkCmdCopyBufferToImage(context->cmd_buffer, staging_buffer, resource->image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region));
for (unsigned int layer = 0; layer < params->desc.layer_count; ++layer)
{
VkBufferImageCopy region = {0};
buffer_offset += level_depth * level_width * level_height * params->desc.texel_size;
region.bufferOffset = buffer_offset;
region.imageSubresource.mipLevel = level;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.baseArrayLayer = layer;
region.imageSubresource.layerCount = 1;
region.imageExtent.width = level_width;
region.imageExtent.height = level_height;
region.imageExtent.depth = level_depth;
VK_CALL(vkCmdCopyBufferToImage(context->cmd_buffer, staging_buffer, resource->image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region));
buffer_offset += level_depth * level_width * level_height * params->desc.texel_size;
}
}
transition_image_layout(context, resource->image, VK_IMAGE_ASPECT_COLOR_BIT,
@@ -214,9 +224,10 @@ static struct resource *vulkan_runner_create_resource(struct shader_runner *r, c
resource->image = create_vulkan_image(context, VK_IMAGE_TYPE_2D,
desc->width, desc->height, 1, desc->level_count, desc->layer_count, desc->sample_count,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, format, &resource->memory);
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
format, 0, &resource->memory);
resource->image_view = create_vulkan_image_view(context,
resource->image, format, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_TYPE_2D, layer_count);
resource->image, format, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_TYPE_2D, false, layer_count);
begin_command_buffer(context);
transition_image_layout(context, resource->image, VK_IMAGE_ASPECT_COLOR_BIT, 0,
@@ -230,9 +241,9 @@ static struct resource *vulkan_runner_create_resource(struct shader_runner *r, c
resource->image = create_vulkan_image(context, VK_IMAGE_TYPE_2D,
desc->width, desc->height, 1, desc->level_count, desc->layer_count, desc->sample_count,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
format, &resource->memory);
format, 0, &resource->memory);
resource->image_view = create_vulkan_image_view(context,
resource->image, format, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_TYPE_2D, layer_count);
resource->image, format, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_TYPE_2D, false, layer_count);
begin_command_buffer(context);
transition_image_layout(context, resource->image, VK_IMAGE_ASPECT_DEPTH_BIT, 0, layer_count,
@@ -1503,7 +1514,7 @@ static struct resource_readback *vulkan_runner_get_resource_readback(struct shad
resolved_desc.width, resolved_desc.height, resolved_desc.depth,
resolved_desc.level_count, resolved_desc.layer_count, resolved_desc.sample_count,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
vkd3d_get_vk_format(resource->r.desc.format), &resolved_memory);
vkd3d_get_vk_format(resource->r.desc.format), 0, &resolved_memory);
transition_image_layout(context, resolved_image, VK_IMAGE_ASPECT_COLOR_BIT, layer,
1, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);

View File

@@ -169,12 +169,14 @@ static inline VkBufferView create_vulkan_buffer_view(const struct vulkan_test_co
static inline VkImage create_vulkan_image(const struct vulkan_test_context *context, VkImageType image_type,
unsigned int width, unsigned int height, unsigned int depth, unsigned int level_count, unsigned int layer_count,
unsigned int sample_count, VkImageUsageFlags usage, VkFormat format, VkDeviceMemory *memory)
unsigned int sample_count, VkImageUsageFlags usage, VkFormat format,
VkImageCreateFlags flags, VkDeviceMemory *memory)
{
VkImageCreateInfo image_info = {.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
VkMemoryRequirements memory_reqs;
VkImage image;
image_info.flags = flags;
image_info.imageType = image_type;
image_info.format = format;
image_info.extent.width = width;
@@ -198,13 +200,15 @@ static inline VkImage create_vulkan_image(const struct vulkan_test_context *cont
}
static inline VkImageView create_vulkan_image_view(const struct vulkan_test_context *context, VkImage image,
VkFormat format, VkImageAspectFlags aspect_mask, VkImageType image_type, unsigned int layer_count)
VkFormat format, VkImageAspectFlags aspect_mask, VkImageType image_type, bool cube, unsigned int layer_count)
{
VkImageViewCreateInfo view_info = {.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
VkImageView view;
view_info.image = image;
if (image_type == VK_IMAGE_TYPE_2D)
if (cube)
view_info.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
else if (image_type == VK_IMAGE_TYPE_2D)
view_info.viewType = (layer_count > 1) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
else if (image_type == VK_IMAGE_TYPE_3D)
view_info.viewType = VK_IMAGE_VIEW_TYPE_3D;