#
tokens: 49582/50000 7/78 files (page 3/7)
lines: off (toggle) GitHub
raw markdown copy
This is page 3 of 7. Use http://codebase.md/mehmetoguzderin/shaderc-vkrunner-mcp?page={x} to view the full context.

# Directory Structure

```
├── .devcontainer
│   ├── devcontainer.json
│   ├── docker-compose.yml
│   └── Dockerfile
├── .gitattributes
├── .github
│   └── workflows
│       └── build-push-image.yml
├── .gitignore
├── .vscode
│   └── mcp.json
├── Cargo.lock
├── Cargo.toml
├── Dockerfile
├── LICENSE
├── README.adoc
├── shaderc-vkrunner-mcp.jpg
├── src
│   └── main.rs
└── vkrunner
    ├── .editorconfig
    ├── .gitignore
    ├── .gitlab-ci.yml
    ├── build.rs
    ├── Cargo.toml
    ├── COPYING
    ├── examples
    │   ├── compute-shader.shader_test
    │   ├── cooperative-matrix.shader_test
    │   ├── depth-buffer.shader_test
    │   ├── desc_set_and_binding.shader_test
    │   ├── entrypoint.shader_test
    │   ├── float-framebuffer.shader_test
    │   ├── frexp.shader_test
    │   ├── geometry.shader_test
    │   ├── indices.shader_test
    │   ├── layouts.shader_test
    │   ├── properties.shader_test
    │   ├── push-constants.shader_test
    │   ├── require-subgroup-size.shader_test
    │   ├── row-major.shader_test
    │   ├── spirv.shader_test
    │   ├── ssbo.shader_test
    │   ├── tolerance.shader_test
    │   ├── tricolore.shader_test
    │   ├── ubo.shader_test
    │   ├── vertex-data-piglit.shader_test
    │   └── vertex-data.shader_test
    ├── include
    │   ├── vk_video
    │   │   ├── vulkan_video_codec_av1std_decode.h
    │   │   ├── vulkan_video_codec_av1std_encode.h
    │   │   ├── vulkan_video_codec_av1std.h
    │   │   ├── vulkan_video_codec_h264std_decode.h
    │   │   ├── vulkan_video_codec_h264std_encode.h
    │   │   ├── vulkan_video_codec_h264std.h
    │   │   ├── vulkan_video_codec_h265std_decode.h
    │   │   ├── vulkan_video_codec_h265std_encode.h
    │   │   ├── vulkan_video_codec_h265std.h
    │   │   └── vulkan_video_codecs_common.h
    │   └── vulkan
    │       ├── vk_platform.h
    │       ├── vulkan_core.h
    │       └── vulkan.h
    ├── precompile-script.py
    ├── README.md
    ├── scripts
    │   └── update-vulkan.sh
    ├── src
    │   └── main.rs
    ├── test-build.sh
    └── vkrunner
        ├── allocate_store.rs
        ├── buffer.rs
        ├── compiler
        │   └── fake_process.rs
        ├── compiler.rs
        ├── config.rs
        ├── context.rs
        ├── enum_table.rs
        ├── env_var_test.rs
        ├── executor.rs
        ├── fake_vulkan.rs
        ├── features.rs
        ├── flush_memory.rs
        ├── format_table.rs
        ├── format.rs
        ├── half_float.rs
        ├── hex.rs
        ├── inspect.rs
        ├── lib.rs
        ├── logger.rs
        ├── make-enums.py
        ├── make-features.py
        ├── make-formats.py
        ├── make-pipeline-key-data.py
        ├── make-vulkan-funcs-data.py
        ├── parse_num.rs
        ├── pipeline_key_data.rs
        ├── pipeline_key.rs
        ├── pipeline_set.rs
        ├── requirements.rs
        ├── result.rs
        ├── script.rs
        ├── shader_stage.rs
        ├── slot.rs
        ├── small_float.rs
        ├── source.rs
        ├── stream.rs
        ├── temp_file.rs
        ├── tester.rs
        ├── tolerance.rs
        ├── util.rs
        ├── vbo.rs
        ├── vk.rs
        ├── vulkan_funcs_data.rs
        ├── vulkan_funcs.rs
        ├── window_format.rs
        └── window.rs
```

# Files

--------------------------------------------------------------------------------
/vkrunner/include/vk_video/vulkan_video_codec_h265std.h:
--------------------------------------------------------------------------------

```
#ifndef VULKAN_VIDEO_CODEC_H265STD_H_
#define VULKAN_VIDEO_CODEC_H265STD_H_ 1

/*
** Copyright 2015-2025 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
*/

/*
** This header is generated from the Khronos Vulkan XML API Registry.
**
*/


#ifdef __cplusplus
extern "C" {
#endif



// vulkan_video_codec_h265std is a preprocessor guard. Do not pass it to API calls.
#define vulkan_video_codec_h265std 1
#include "vulkan_video_codecs_common.h"
#define STD_VIDEO_H265_CPB_CNT_LIST_SIZE  32
#define STD_VIDEO_H265_SUBLAYERS_LIST_SIZE 7
#define STD_VIDEO_H265_SCALING_LIST_4X4_NUM_LISTS 6
#define STD_VIDEO_H265_SCALING_LIST_4X4_NUM_ELEMENTS 16
#define STD_VIDEO_H265_SCALING_LIST_8X8_NUM_LISTS 6
#define STD_VIDEO_H265_SCALING_LIST_8X8_NUM_ELEMENTS 64
#define STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS 6
#define STD_VIDEO_H265_SCALING_LIST_16X16_NUM_ELEMENTS 64
#define STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS 2
#define STD_VIDEO_H265_SCALING_LIST_32X32_NUM_ELEMENTS 64
#define STD_VIDEO_H265_CHROMA_QP_OFFSET_LIST_SIZE 6
#define STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_COLS_LIST_SIZE 19
#define STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_ROWS_LIST_SIZE 21
#define STD_VIDEO_H265_PREDICTOR_PALETTE_COMPONENTS_LIST_SIZE 3
#define STD_VIDEO_H265_PREDICTOR_PALETTE_COMP_ENTRIES_LIST_SIZE 128
#define STD_VIDEO_H265_MAX_NUM_LIST_REF   15
#define STD_VIDEO_H265_MAX_CHROMA_PLANES  2
#define STD_VIDEO_H265_MAX_SHORT_TERM_REF_PIC_SETS 64
#define STD_VIDEO_H265_MAX_DPB_SIZE       16
#define STD_VIDEO_H265_MAX_LONG_TERM_REF_PICS_SPS 32
#define STD_VIDEO_H265_MAX_LONG_TERM_PICS 16
#define STD_VIDEO_H265_MAX_DELTA_POC      48
#define STD_VIDEO_H265_NO_REFERENCE_PICTURE 0xFF

typedef enum StdVideoH265ChromaFormatIdc {
    STD_VIDEO_H265_CHROMA_FORMAT_IDC_MONOCHROME = 0,
    STD_VIDEO_H265_CHROMA_FORMAT_IDC_420 = 1,
    STD_VIDEO_H265_CHROMA_FORMAT_IDC_422 = 2,
    STD_VIDEO_H265_CHROMA_FORMAT_IDC_444 = 3,
    STD_VIDEO_H265_CHROMA_FORMAT_IDC_INVALID = 0x7FFFFFFF,
    STD_VIDEO_H265_CHROMA_FORMAT_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265ChromaFormatIdc;

typedef enum StdVideoH265ProfileIdc {
    STD_VIDEO_H265_PROFILE_IDC_MAIN = 1,
    STD_VIDEO_H265_PROFILE_IDC_MAIN_10 = 2,
    STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE = 3,
    STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS = 4,
    STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS = 9,
    STD_VIDEO_H265_PROFILE_IDC_INVALID = 0x7FFFFFFF,
    STD_VIDEO_H265_PROFILE_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265ProfileIdc;

typedef enum StdVideoH265LevelIdc {
    STD_VIDEO_H265_LEVEL_IDC_1_0 = 0,
    STD_VIDEO_H265_LEVEL_IDC_2_0 = 1,
    STD_VIDEO_H265_LEVEL_IDC_2_1 = 2,
    STD_VIDEO_H265_LEVEL_IDC_3_0 = 3,
    STD_VIDEO_H265_LEVEL_IDC_3_1 = 4,
    STD_VIDEO_H265_LEVEL_IDC_4_0 = 5,
    STD_VIDEO_H265_LEVEL_IDC_4_1 = 6,
    STD_VIDEO_H265_LEVEL_IDC_5_0 = 7,
    STD_VIDEO_H265_LEVEL_IDC_5_1 = 8,
    STD_VIDEO_H265_LEVEL_IDC_5_2 = 9,
    STD_VIDEO_H265_LEVEL_IDC_6_0 = 10,
    STD_VIDEO_H265_LEVEL_IDC_6_1 = 11,
    STD_VIDEO_H265_LEVEL_IDC_6_2 = 12,
    STD_VIDEO_H265_LEVEL_IDC_INVALID = 0x7FFFFFFF,
    STD_VIDEO_H265_LEVEL_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265LevelIdc;

typedef enum StdVideoH265SliceType {
    STD_VIDEO_H265_SLICE_TYPE_B = 0,
    STD_VIDEO_H265_SLICE_TYPE_P = 1,
    STD_VIDEO_H265_SLICE_TYPE_I = 2,
    STD_VIDEO_H265_SLICE_TYPE_INVALID = 0x7FFFFFFF,
    STD_VIDEO_H265_SLICE_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265SliceType;

typedef enum StdVideoH265PictureType {
    STD_VIDEO_H265_PICTURE_TYPE_P = 0,
    STD_VIDEO_H265_PICTURE_TYPE_B = 1,
    STD_VIDEO_H265_PICTURE_TYPE_I = 2,
    STD_VIDEO_H265_PICTURE_TYPE_IDR = 3,
    STD_VIDEO_H265_PICTURE_TYPE_INVALID = 0x7FFFFFFF,
    STD_VIDEO_H265_PICTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265PictureType;

typedef enum StdVideoH265AspectRatioIdc {
    STD_VIDEO_H265_ASPECT_RATIO_IDC_UNSPECIFIED = 0,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_SQUARE = 1,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_12_11 = 2,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_10_11 = 3,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_16_11 = 4,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_40_33 = 5,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_24_11 = 6,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_20_11 = 7,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_32_11 = 8,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_80_33 = 9,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_18_11 = 10,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_15_11 = 11,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_64_33 = 12,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_160_99 = 13,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_4_3 = 14,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_3_2 = 15,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_2_1 = 16,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_EXTENDED_SAR = 255,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_INVALID = 0x7FFFFFFF,
    STD_VIDEO_H265_ASPECT_RATIO_IDC_MAX_ENUM = 0x7FFFFFFF
} StdVideoH265AspectRatioIdc;
typedef struct StdVideoH265DecPicBufMgr {
    uint32_t    max_latency_increase_plus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
    uint8_t     max_dec_pic_buffering_minus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
    uint8_t     max_num_reorder_pics[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
} StdVideoH265DecPicBufMgr;

typedef struct StdVideoH265SubLayerHrdParameters {
    uint32_t    bit_rate_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
    uint32_t    cpb_size_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
    uint32_t    cpb_size_du_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
    uint32_t    bit_rate_du_value_minus1[STD_VIDEO_H265_CPB_CNT_LIST_SIZE];
    uint32_t    cbr_flag;
} StdVideoH265SubLayerHrdParameters;

typedef struct StdVideoH265HrdFlags {
    uint32_t    nal_hrd_parameters_present_flag : 1;
    uint32_t    vcl_hrd_parameters_present_flag : 1;
    uint32_t    sub_pic_hrd_params_present_flag : 1;
    uint32_t    sub_pic_cpb_params_in_pic_timing_sei_flag : 1;
    uint32_t    fixed_pic_rate_general_flag : 8;
    uint32_t    fixed_pic_rate_within_cvs_flag : 8;
    uint32_t    low_delay_hrd_flag : 8;
} StdVideoH265HrdFlags;

typedef struct StdVideoH265HrdParameters {
    StdVideoH265HrdFlags                        flags;
    uint8_t                                     tick_divisor_minus2;
    uint8_t                                     du_cpb_removal_delay_increment_length_minus1;
    uint8_t                                     dpb_output_delay_du_length_minus1;
    uint8_t                                     bit_rate_scale;
    uint8_t                                     cpb_size_scale;
    uint8_t                                     cpb_size_du_scale;
    uint8_t                                     initial_cpb_removal_delay_length_minus1;
    uint8_t                                     au_cpb_removal_delay_length_minus1;
    uint8_t                                     dpb_output_delay_length_minus1;
    uint8_t                                     cpb_cnt_minus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
    uint16_t                                    elemental_duration_in_tc_minus1[STD_VIDEO_H265_SUBLAYERS_LIST_SIZE];
    uint16_t                                    reserved[3];
    const StdVideoH265SubLayerHrdParameters*    pSubLayerHrdParametersNal;
    const StdVideoH265SubLayerHrdParameters*    pSubLayerHrdParametersVcl;
} StdVideoH265HrdParameters;

typedef struct StdVideoH265VpsFlags {
    uint32_t    vps_temporal_id_nesting_flag : 1;
    uint32_t    vps_sub_layer_ordering_info_present_flag : 1;
    uint32_t    vps_timing_info_present_flag : 1;
    uint32_t    vps_poc_proportional_to_timing_flag : 1;
} StdVideoH265VpsFlags;

typedef struct StdVideoH265ProfileTierLevelFlags {
    uint32_t    general_tier_flag : 1;
    uint32_t    general_progressive_source_flag : 1;
    uint32_t    general_interlaced_source_flag : 1;
    uint32_t    general_non_packed_constraint_flag : 1;
    uint32_t    general_frame_only_constraint_flag : 1;
} StdVideoH265ProfileTierLevelFlags;

typedef struct StdVideoH265ProfileTierLevel {
    StdVideoH265ProfileTierLevelFlags    flags;
    StdVideoH265ProfileIdc               general_profile_idc;
    StdVideoH265LevelIdc                 general_level_idc;
} StdVideoH265ProfileTierLevel;

typedef struct StdVideoH265VideoParameterSet {
    StdVideoH265VpsFlags                   flags;
    uint8_t                                vps_video_parameter_set_id;
    uint8_t                                vps_max_sub_layers_minus1;
    uint8_t                                reserved1;
    uint8_t                                reserved2;
    uint32_t                               vps_num_units_in_tick;
    uint32_t                               vps_time_scale;
    uint32_t                               vps_num_ticks_poc_diff_one_minus1;
    uint32_t                               reserved3;
    const StdVideoH265DecPicBufMgr*        pDecPicBufMgr;
    const StdVideoH265HrdParameters*       pHrdParameters;
    const StdVideoH265ProfileTierLevel*    pProfileTierLevel;
} StdVideoH265VideoParameterSet;

typedef struct StdVideoH265ScalingLists {
    uint8_t    ScalingList4x4[STD_VIDEO_H265_SCALING_LIST_4X4_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_4X4_NUM_ELEMENTS];
    uint8_t    ScalingList8x8[STD_VIDEO_H265_SCALING_LIST_8X8_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_8X8_NUM_ELEMENTS];
    uint8_t    ScalingList16x16[STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_16X16_NUM_ELEMENTS];
    uint8_t    ScalingList32x32[STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS][STD_VIDEO_H265_SCALING_LIST_32X32_NUM_ELEMENTS];
    uint8_t    ScalingListDCCoef16x16[STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS];
    uint8_t    ScalingListDCCoef32x32[STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS];
} StdVideoH265ScalingLists;

typedef struct StdVideoH265SpsVuiFlags {
    uint32_t    aspect_ratio_info_present_flag : 1;
    uint32_t    overscan_info_present_flag : 1;
    uint32_t    overscan_appropriate_flag : 1;
    uint32_t    video_signal_type_present_flag : 1;
    uint32_t    video_full_range_flag : 1;
    uint32_t    colour_description_present_flag : 1;
    uint32_t    chroma_loc_info_present_flag : 1;
    uint32_t    neutral_chroma_indication_flag : 1;
    uint32_t    field_seq_flag : 1;
    uint32_t    frame_field_info_present_flag : 1;
    uint32_t    default_display_window_flag : 1;
    uint32_t    vui_timing_info_present_flag : 1;
    uint32_t    vui_poc_proportional_to_timing_flag : 1;
    uint32_t    vui_hrd_parameters_present_flag : 1;
    uint32_t    bitstream_restriction_flag : 1;
    uint32_t    tiles_fixed_structure_flag : 1;
    uint32_t    motion_vectors_over_pic_boundaries_flag : 1;
    uint32_t    restricted_ref_pic_lists_flag : 1;
} StdVideoH265SpsVuiFlags;

typedef struct StdVideoH265SequenceParameterSetVui {
    StdVideoH265SpsVuiFlags             flags;
    StdVideoH265AspectRatioIdc          aspect_ratio_idc;
    uint16_t                            sar_width;
    uint16_t                            sar_height;
    uint8_t                             video_format;
    uint8_t                             colour_primaries;
    uint8_t                             transfer_characteristics;
    uint8_t                             matrix_coeffs;
    uint8_t                             chroma_sample_loc_type_top_field;
    uint8_t                             chroma_sample_loc_type_bottom_field;
    uint8_t                             reserved1;
    uint8_t                             reserved2;
    uint16_t                            def_disp_win_left_offset;
    uint16_t                            def_disp_win_right_offset;
    uint16_t                            def_disp_win_top_offset;
    uint16_t                            def_disp_win_bottom_offset;
    uint32_t                            vui_num_units_in_tick;
    uint32_t                            vui_time_scale;
    uint32_t                            vui_num_ticks_poc_diff_one_minus1;
    uint16_t                            min_spatial_segmentation_idc;
    uint16_t                            reserved3;
    uint8_t                             max_bytes_per_pic_denom;
    uint8_t                             max_bits_per_min_cu_denom;
    uint8_t                             log2_max_mv_length_horizontal;
    uint8_t                             log2_max_mv_length_vertical;
    const StdVideoH265HrdParameters*    pHrdParameters;
} StdVideoH265SequenceParameterSetVui;

typedef struct StdVideoH265PredictorPaletteEntries {
    uint16_t    PredictorPaletteEntries[STD_VIDEO_H265_PREDICTOR_PALETTE_COMPONENTS_LIST_SIZE][STD_VIDEO_H265_PREDICTOR_PALETTE_COMP_ENTRIES_LIST_SIZE];
} StdVideoH265PredictorPaletteEntries;

typedef struct StdVideoH265SpsFlags {
    uint32_t    sps_temporal_id_nesting_flag : 1;
    uint32_t    separate_colour_plane_flag : 1;
    uint32_t    conformance_window_flag : 1;
    uint32_t    sps_sub_layer_ordering_info_present_flag : 1;
    uint32_t    scaling_list_enabled_flag : 1;
    uint32_t    sps_scaling_list_data_present_flag : 1;
    uint32_t    amp_enabled_flag : 1;
    uint32_t    sample_adaptive_offset_enabled_flag : 1;
    uint32_t    pcm_enabled_flag : 1;
    uint32_t    pcm_loop_filter_disabled_flag : 1;
    uint32_t    long_term_ref_pics_present_flag : 1;
    uint32_t    sps_temporal_mvp_enabled_flag : 1;
    uint32_t    strong_intra_smoothing_enabled_flag : 1;
    uint32_t    vui_parameters_present_flag : 1;
    uint32_t    sps_extension_present_flag : 1;
    uint32_t    sps_range_extension_flag : 1;
    uint32_t    transform_skip_rotation_enabled_flag : 1;
    uint32_t    transform_skip_context_enabled_flag : 1;
    uint32_t    implicit_rdpcm_enabled_flag : 1;
    uint32_t    explicit_rdpcm_enabled_flag : 1;
    uint32_t    extended_precision_processing_flag : 1;
    uint32_t    intra_smoothing_disabled_flag : 1;
    uint32_t    high_precision_offsets_enabled_flag : 1;
    uint32_t    persistent_rice_adaptation_enabled_flag : 1;
    uint32_t    cabac_bypass_alignment_enabled_flag : 1;
    uint32_t    sps_scc_extension_flag : 1;
    uint32_t    sps_curr_pic_ref_enabled_flag : 1;
    uint32_t    palette_mode_enabled_flag : 1;
    uint32_t    sps_palette_predictor_initializers_present_flag : 1;
    uint32_t    intra_boundary_filtering_disabled_flag : 1;
} StdVideoH265SpsFlags;

typedef struct StdVideoH265ShortTermRefPicSetFlags {
    uint32_t    inter_ref_pic_set_prediction_flag : 1;
    uint32_t    delta_rps_sign : 1;
} StdVideoH265ShortTermRefPicSetFlags;

typedef struct StdVideoH265ShortTermRefPicSet {
    StdVideoH265ShortTermRefPicSetFlags    flags;
    uint32_t                               delta_idx_minus1;
    uint16_t                               use_delta_flag;
    uint16_t                               abs_delta_rps_minus1;
    uint16_t                               used_by_curr_pic_flag;
    uint16_t                               used_by_curr_pic_s0_flag;
    uint16_t                               used_by_curr_pic_s1_flag;
    uint16_t                               reserved1;
    uint8_t                                reserved2;
    uint8_t                                reserved3;
    uint8_t                                num_negative_pics;
    uint8_t                                num_positive_pics;
    uint16_t                               delta_poc_s0_minus1[STD_VIDEO_H265_MAX_DPB_SIZE];
    uint16_t                               delta_poc_s1_minus1[STD_VIDEO_H265_MAX_DPB_SIZE];
} StdVideoH265ShortTermRefPicSet;

typedef struct StdVideoH265LongTermRefPicsSps {
    uint32_t    used_by_curr_pic_lt_sps_flag;
    uint32_t    lt_ref_pic_poc_lsb_sps[STD_VIDEO_H265_MAX_LONG_TERM_REF_PICS_SPS];
} StdVideoH265LongTermRefPicsSps;

typedef struct StdVideoH265SequenceParameterSet {
    StdVideoH265SpsFlags                          flags;
    StdVideoH265ChromaFormatIdc                   chroma_format_idc;
    uint32_t                                      pic_width_in_luma_samples;
    uint32_t                                      pic_height_in_luma_samples;
    uint8_t                                       sps_video_parameter_set_id;
    uint8_t                                       sps_max_sub_layers_minus1;
    uint8_t                                       sps_seq_parameter_set_id;
    uint8_t                                       bit_depth_luma_minus8;
    uint8_t                                       bit_depth_chroma_minus8;
    uint8_t                                       log2_max_pic_order_cnt_lsb_minus4;
    uint8_t                                       log2_min_luma_coding_block_size_minus3;
    uint8_t                                       log2_diff_max_min_luma_coding_block_size;
    uint8_t                                       log2_min_luma_transform_block_size_minus2;
    uint8_t                                       log2_diff_max_min_luma_transform_block_size;
    uint8_t                                       max_transform_hierarchy_depth_inter;
    uint8_t                                       max_transform_hierarchy_depth_intra;
    uint8_t                                       num_short_term_ref_pic_sets;
    uint8_t                                       num_long_term_ref_pics_sps;
    uint8_t                                       pcm_sample_bit_depth_luma_minus1;
    uint8_t                                       pcm_sample_bit_depth_chroma_minus1;
    uint8_t                                       log2_min_pcm_luma_coding_block_size_minus3;
    uint8_t                                       log2_diff_max_min_pcm_luma_coding_block_size;
    uint8_t                                       reserved1;
    uint8_t                                       reserved2;
    uint8_t                                       palette_max_size;
    uint8_t                                       delta_palette_max_predictor_size;
    uint8_t                                       motion_vector_resolution_control_idc;
    uint8_t                                       sps_num_palette_predictor_initializers_minus1;
    uint32_t                                      conf_win_left_offset;
    uint32_t                                      conf_win_right_offset;
    uint32_t                                      conf_win_top_offset;
    uint32_t                                      conf_win_bottom_offset;
    const StdVideoH265ProfileTierLevel*           pProfileTierLevel;
    const StdVideoH265DecPicBufMgr*               pDecPicBufMgr;
    const StdVideoH265ScalingLists*               pScalingLists;
    const StdVideoH265ShortTermRefPicSet*         pShortTermRefPicSet;
    const StdVideoH265LongTermRefPicsSps*         pLongTermRefPicsSps;
    const StdVideoH265SequenceParameterSetVui*    pSequenceParameterSetVui;
    const StdVideoH265PredictorPaletteEntries*    pPredictorPaletteEntries;
} StdVideoH265SequenceParameterSet;

typedef struct StdVideoH265PpsFlags {
    uint32_t    dependent_slice_segments_enabled_flag : 1;
    uint32_t    output_flag_present_flag : 1;
    uint32_t    sign_data_hiding_enabled_flag : 1;
    uint32_t    cabac_init_present_flag : 1;
    uint32_t    constrained_intra_pred_flag : 1;
    uint32_t    transform_skip_enabled_flag : 1;
    uint32_t    cu_qp_delta_enabled_flag : 1;
    uint32_t    pps_slice_chroma_qp_offsets_present_flag : 1;
    uint32_t    weighted_pred_flag : 1;
    uint32_t    weighted_bipred_flag : 1;
    uint32_t    transquant_bypass_enabled_flag : 1;
    uint32_t    tiles_enabled_flag : 1;
    uint32_t    entropy_coding_sync_enabled_flag : 1;
    uint32_t    uniform_spacing_flag : 1;
    uint32_t    loop_filter_across_tiles_enabled_flag : 1;
    uint32_t    pps_loop_filter_across_slices_enabled_flag : 1;
    uint32_t    deblocking_filter_control_present_flag : 1;
    uint32_t    deblocking_filter_override_enabled_flag : 1;
    uint32_t    pps_deblocking_filter_disabled_flag : 1;
    uint32_t    pps_scaling_list_data_present_flag : 1;
    uint32_t    lists_modification_present_flag : 1;
    uint32_t    slice_segment_header_extension_present_flag : 1;
    uint32_t    pps_extension_present_flag : 1;
    uint32_t    cross_component_prediction_enabled_flag : 1;
    uint32_t    chroma_qp_offset_list_enabled_flag : 1;
    uint32_t    pps_curr_pic_ref_enabled_flag : 1;
    uint32_t    residual_adaptive_colour_transform_enabled_flag : 1;
    uint32_t    pps_slice_act_qp_offsets_present_flag : 1;
    uint32_t    pps_palette_predictor_initializers_present_flag : 1;
    uint32_t    monochrome_palette_flag : 1;
    uint32_t    pps_range_extension_flag : 1;
} StdVideoH265PpsFlags;

typedef struct StdVideoH265PictureParameterSet {
    StdVideoH265PpsFlags                          flags;
    uint8_t                                       pps_pic_parameter_set_id;
    uint8_t                                       pps_seq_parameter_set_id;
    uint8_t                                       sps_video_parameter_set_id;
    uint8_t                                       num_extra_slice_header_bits;
    uint8_t                                       num_ref_idx_l0_default_active_minus1;
    uint8_t                                       num_ref_idx_l1_default_active_minus1;
    int8_t                                        init_qp_minus26;
    uint8_t                                       diff_cu_qp_delta_depth;
    int8_t                                        pps_cb_qp_offset;
    int8_t                                        pps_cr_qp_offset;
    int8_t                                        pps_beta_offset_div2;
    int8_t                                        pps_tc_offset_div2;
    uint8_t                                       log2_parallel_merge_level_minus2;
    uint8_t                                       log2_max_transform_skip_block_size_minus2;
    uint8_t                                       diff_cu_chroma_qp_offset_depth;
    uint8_t                                       chroma_qp_offset_list_len_minus1;
    int8_t                                        cb_qp_offset_list[STD_VIDEO_H265_CHROMA_QP_OFFSET_LIST_SIZE];
    int8_t                                        cr_qp_offset_list[STD_VIDEO_H265_CHROMA_QP_OFFSET_LIST_SIZE];
    uint8_t                                       log2_sao_offset_scale_luma;
    uint8_t                                       log2_sao_offset_scale_chroma;
    int8_t                                        pps_act_y_qp_offset_plus5;
    int8_t                                        pps_act_cb_qp_offset_plus5;
    int8_t                                        pps_act_cr_qp_offset_plus3;
    uint8_t                                       pps_num_palette_predictor_initializers;
    uint8_t                                       luma_bit_depth_entry_minus8;
    uint8_t                                       chroma_bit_depth_entry_minus8;
    uint8_t                                       num_tile_columns_minus1;
    uint8_t                                       num_tile_rows_minus1;
    uint8_t                                       reserved1;
    uint8_t                                       reserved2;
    uint16_t                                      column_width_minus1[STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_COLS_LIST_SIZE];
    uint16_t                                      row_height_minus1[STD_VIDEO_H265_CHROMA_QP_OFFSET_TILE_ROWS_LIST_SIZE];
    uint32_t                                      reserved3;
    const StdVideoH265ScalingLists*               pScalingLists;
    const StdVideoH265PredictorPaletteEntries*    pPredictorPaletteEntries;
} StdVideoH265PictureParameterSet;


#ifdef __cplusplus
}
#endif

#endif

```

--------------------------------------------------------------------------------
/vkrunner/src/main.rs:
--------------------------------------------------------------------------------

```rust
// vkrunner
//
// Copyright (C) 2018 Intel Corporation
// Copyright 2023 Neil Roberts
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice (including the next
// paragraph) shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::ffi::{OsString, OsStr};
use std::collections::HashMap;
use std::fmt;
use std::process::ExitCode;
use std::ptr;
use std::rc::Rc;
use std::cell::RefCell;
use std::ffi::c_void;
use std::io::{self, BufWriter};
use std::fs::File;
extern crate vkrunner;
use vkrunner::{Config, Executor, Source, inspect, result};

#[derive(Debug)]
struct Opt {
    short: Option<char>,
    long: &'static str,
    help: &'static str,
    argument_name: Option<&'static str>,
    argument_type: ArgumentType,
}

#[derive(Debug)]
enum OptError {
    UnknownOption(String),
    MissingArgument(&'static Opt),
    InvalidUtf8(&'static Opt),
    InvalidInteger(String),
    ArgumentForFlag(&'static Opt),
}

#[derive(Debug)]
enum Error {
    OptError(OptError),
    InvalidTokenReplacement(String),
    ShowHelp,
    TestFailed,
    IoError(io::Error),
    BufferNotFound(u32),
    NoBuffers,
    ZeroDeviceId,
}

#[derive(Debug)]
enum ArgumentType {
    Flag,
    Filename,
    StringArray,
    Integer,
}

#[derive(Debug)]
enum ArgumentValue {
    Flag,
    Filename(OsString),
    StringArray(Vec<String>),
    Integer(u32),
}

type OptValues = HashMap<&'static str, ArgumentValue>;

#[derive(Debug)]
struct Options {
    values: OptValues,
    scripts: Vec<OsString>,
}

#[derive(Debug)]
struct TokenReplacement<'a> {
    token: &'a str,
    replacement: &'a str,
}

impl fmt::Display for OptError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            OptError::UnknownOption(s) => write!(f, "Unknown option: {}", s),
            OptError::MissingArgument(o) => {
                write!(f, "Option --{} requires an argument", o.long)
            },
            OptError::InvalidUtf8(o) => {
                write!(f, "Invalid UTF-8 in argument to --{}", o.long)
            },
            OptError::InvalidInteger(s) => {
                write!(f, "Invalid integer: {}", s)
            },
            OptError::ArgumentForFlag(o) => {
                write!(
                    f,
                    "Option --{} is a flag and can not take an argument",
                    o.long,
                )
            },
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::OptError(e) => e.fmt(f),
            Error::InvalidTokenReplacement(s) => {
                write!(f, "Invalid token replacement: {}", s)
            },
            Error::ShowHelp => format_help(f),
            Error::TestFailed => {
                write!(f, "{}", format_result(result::Result::Fail))
            },
            Error::IoError(e) => e.fmt(f),
            Error::BufferNotFound(binding) => {
                write!(f, "No buffer with binding {} was found", binding)
            },
            Error::NoBuffers => {
                write!(
                    f,
                    "Buffer dump requested but the script has no buffers"
                )
            },
            Error::ZeroDeviceId => {
                write!(
                    f,
                    "Device IDs start from 1 but 0 was specified",
                )
            },
        }
    }
}

impl From<OptError> for Error {
    fn from(e: OptError) -> Error {
        Error::OptError(e)
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Error {
        Error::IoError(e)
    }
}

static HELP_OPTION: &'static str = "help";
static IMAGE_OPTION: &'static str = "image";
static BUFFER_OPTION: &'static str = "buffer";
static BINDING_OPTION: &'static str = "binding";
static DISASM_OPTION: &'static str = "disasm";
static REPLACE_OPTION: &'static str = "replace";
static QUIET_OPTION: &'static str = "quiet";
static DEVICE_ID_OPTION: &'static str = "device-id";

static OPTIONS: [Opt; 8] = [
    Opt {
        short: Some('h'),
        long: HELP_OPTION,
        help: "Show this help message",
        argument_name: None,
        argument_type: ArgumentType::Flag,
    },
    Opt {
        short: Some('i'),
        long: IMAGE_OPTION,
        help: "Write the final rendering to IMG as a PPM image",
        argument_name: Some("IMG"),
        argument_type: ArgumentType::Filename,
    },
    Opt {
        short: Some('b'),
        long: BUFFER_OPTION,
        help: "Dump contents of a UBO or SSBO to BUF",
        argument_name: Some("BUF"),
        argument_type: ArgumentType::Filename,
    },
    Opt {
        short: Some('B'),
        long: BINDING_OPTION,
        help: "Select which buffer to dump using the -b option. Defaults to \
               first buffer",
        argument_name: Some("BINDING"),
        argument_type: ArgumentType::Integer,
    },
    Opt {
        short: Some('d'),
        long: DISASM_OPTION,
        help: "Show the SPIR-V disassembly",
        argument_name: None,
        argument_type: ArgumentType::Flag,
    },
    Opt {
        short: Some('D'),
        long: REPLACE_OPTION,
        help: "Replace occurences of TOK with REPL in the scripts",
        argument_name: Some("TOK=REPL"),
        argument_type: ArgumentType::StringArray,
    },
    Opt {
        short: Some('q'),
        long: QUIET_OPTION,
        help: "Don’t print any non-error information to stdout",
        argument_name: None,
        argument_type: ArgumentType::Flag,
    },
    Opt {
        short: None,
        long: DEVICE_ID_OPTION,
        help: "Select the Vulkan device",
        argument_name: Some("DEVID"),
        argument_type: ArgumentType::Integer,
    },
];

fn format_help(f: &mut fmt::Formatter) -> fmt::Result {
    writeln!(
        f,
        "usage: vkrunner [OPTION]… SCRIPT…\n\
         Runs the shader test script SCRIPT\n\
         \n\
         Options:"
    )?;

    let longest_long = OPTIONS
        .iter()
        .map(|o| o.long.chars().count())
        .max()
        .unwrap();
    let longest_arg = OPTIONS
        .iter()
        .map(|o| o.argument_name.map(|n| n.chars().count()).unwrap_or(0))
        .max()
        .unwrap();

    for (i, option) in OPTIONS.iter().enumerate() {
        if i > 0 {
            writeln!(f)?;
        }

        write!(f, " ")?;

        match option.short {
            Some(c) => write!(f, "-{},", c)?,
            None => write!(f, "   ")?,
        }

        write!(
            f,
            "--{:long_width$} {:arg_width$} {}",
            option.long,
            option.argument_name.unwrap_or(""),
            option.help,
            long_width = longest_long,
            arg_width = longest_arg,
        )?;
    }

    Ok(())
}

fn next_arg<I>(
    opt: &'static Opt,
    args: &mut I,
    opt_arg: Option<&str>
) -> Result<OsString, OptError>
    where I: Iterator<Item = OsString>
{
    // Use opt_arg if it’s available or else get the next arg from the iterator
    opt_arg
        .map(|arg| arg.into())
        .or_else(|| args.next())
        .ok_or_else(|| OptError::MissingArgument(opt))
}

fn process_argument<I>(
    values: &mut OptValues,
    args: &mut I,
    opt: &'static Opt,
    opt_arg: Option<&str>,
) -> Result<(), OptError>
    where I: Iterator<Item = OsString>
{
    match opt.argument_type {
        ArgumentType::Flag => {
            if opt_arg.is_some() {
                return Err(OptError::ArgumentForFlag(opt));
            }

            values.insert(opt.long, ArgumentValue::Flag);
        },
        ArgumentType::Filename => {
            values.insert(
                opt.long,
                ArgumentValue::Filename(next_arg(opt, args, opt_arg)?),
            );
        },
        ArgumentType::StringArray => {
            let arg = next_arg(opt, args, opt_arg)?;

            match arg.to_str() {
                Some(s) => {
                    values.entry(opt.long)
                        .and_modify(|value| match value {
                            ArgumentValue::StringArray(values) =>
                                values.push(s.to_string()),
                            _ => unreachable!(),
                        })
                        .or_insert_with(|| ArgumentValue::StringArray(
                            vec![s.to_string()]
                        ));
                },
                None => return Err(OptError::InvalidUtf8(opt)),
            }
        },
        ArgumentType::Integer => {
            let arg = next_arg(opt, args, opt_arg)?;

            match arg.to_str() {
                Some(s) => match s.parse::<u32>() {
                    Ok(value) => {
                        values.insert(
                            opt.long,
                            ArgumentValue::Integer(value),
                        );
                    },
                    Err(_) => {
                        return Err(OptError::InvalidInteger(
                            s.to_owned()
                        ));
                    },
                },
                None => return Err(OptError::InvalidUtf8(opt)),
            }
        },
    }

    Ok(())
}

fn process_long_arg<I>(
    values: &mut OptValues,
    args: &mut I,
    arg: &str
) -> Result<(), OptError>
    where I: Iterator<Item = OsString>
{
    let (arg, opt_arg) = match arg.split_once('=') {
        Some((arg, opt_arg)) => (arg, Some(opt_arg)),
        None => (arg, None),
    };

    for opt in OPTIONS.iter() {
        if opt.long.eq(arg) {
            return process_argument(values, args, opt, opt_arg);
        }
    }

    Err(OptError::UnknownOption(format!("--{}", arg)))
}

fn process_short_args<I>(
    values: &mut OptValues,
    args: &mut I,
    arg: &str
) -> Result<(), OptError>
    where I: Iterator<Item = OsString>
{
    if arg.len() == 0 {
        return Err(OptError::UnknownOption("-".to_string()));
    }

    'arg_loop: for ch in arg.chars() {
        for opt in OPTIONS.iter() {
            if let Some(opt_ch) = opt.short {
                if opt_ch == ch {
                    process_argument(values, args, opt, None)?;
                    continue 'arg_loop;
                }
            }
        }

        return Err(OptError::UnknownOption(format!("{}", ch)));
    }

    Ok(())
}

fn parse_options<I>(
    mut args: I
) -> Result<Options, OptError>
    where I: Iterator<Item = OsString>
{
    let mut values = HashMap::new();
    let mut scripts = Vec::new();

    // Skip the first arg
    if args.next().is_none() {
        return Ok(Options { values, scripts });
    }

    while let Some(arg) = args.next() {
        match arg.to_str() {
            Some(arg_str) => {
                if arg_str == "--" {
                    scripts.extend(args);
                    break;
                } else if arg_str.starts_with("--") {
                    process_long_arg(&mut values, &mut args, &arg_str[2..])?;
                } else if arg_str.starts_with("-") {
                    process_short_args(&mut values, &mut args, &arg_str[1..])?;
                } else {
                    scripts.push(arg);
                }
            },
            None => scripts.push(arg),
        }
    }

    Ok(Options { values, scripts })
}

impl<'a> InspectData<'a> {
    fn new(options: &'a Options) -> InspectData<'a> {
        InspectData {
            image_filename: match options.values.get(IMAGE_OPTION) {
                Some(ArgumentValue::Filename(filename)) => {
                    Some(filename)
                },
                _ => None,
            },
            buffer_filename: match options.values.get(BUFFER_OPTION) {
                Some(ArgumentValue::Filename(filename)) => {
                    Some(filename)
                },
                _ => None,
            },
            buffer_binding: match options.values.get(BINDING_OPTION) {
                Some(&ArgumentValue::Integer(binding)) => {
                    Some(binding)
                },
                _ => None,
            },
            failed: false,
        }
    }
}

fn get_token_replacements<'a>(
    values: &'a OptValues
) -> Result<Vec<TokenReplacement<'a>>, Error> {
    let mut res = Vec::new();

    if let Some(ArgumentValue::StringArray(replacements)) =
        values.get(REPLACE_OPTION)
    {
        for replacement in replacements {
            match replacement.split_once('=') {
                None => {
                    return Err(Error::InvalidTokenReplacement(
                        replacement.to_owned()
                    ));
                },
                Some((token, replacement)) => res.push(TokenReplacement {
                    token,
                    replacement,
                }),
            }
        }
    }

    Ok(res)
}

struct InspectData<'a> {
    image_filename: Option<&'a OsStr>,
    buffer_filename: Option<&'a OsStr>,
    buffer_binding: Option<u32>,
    failed: bool,
}

fn write_ppm(
    image: &inspect::Image,
    filename: &OsStr,
) -> Result<(), Error> {
    let mut file = BufWriter::new(File::create(filename)?);

    use std::io::Write;

    write!(
        &mut file,
        "P6\n\
         {} {}\n\
         255\n",
        image.width,
        image.height,
    )?;

    let format_size = image.format.size();

    for y in 0..image.height {
        let mut line = unsafe {
            std::slice::from_raw_parts(
                (image.data as *const u8).add(y as usize * image.stride),
                image.width as usize * format_size,
            )
        };

        for _ in 0..image.width {
            let pixel = image.format.load_pixel(&line[0..format_size]);
            let mut bytes = [0u8; 3];

            for (i, component) in pixel[0..3].iter().enumerate() {
                bytes[i] = (component.clamp(0.0, 1.0) * 255.0).round() as u8;
            }

            file.write_all(&bytes)?;

            line = &line[format_size..];
        }
    }

    Ok(())
}

fn write_buffer(
    data: &inspect::Data,
    filename: &OsStr,
    binding: Option<u32>,
) -> Result<(), Error> {
    let buffers = unsafe {
        std::slice::from_raw_parts(data.buffers, data.n_buffers)
    };

    let buffer = match binding {
        None => match buffers.get(0) {
            Some(buffer) => buffer,
            None => return Err(Error::NoBuffers),
        },
        Some(binding) => match buffers.iter().find(
            |b| b.binding as u32 == binding
        ) {
            Some(buffer) => buffer,
            None => return Err(Error::BufferNotFound(binding)),
        },
    };

    let mut file = File::create(filename)?;

    let data = unsafe {
        std::slice::from_raw_parts(
            buffer.data as *const u8,
            buffer.size,
        )
    };

    use std::io::Write;

    file.write_all(data)?;

    Ok(())
}

extern "C" fn inspect_cb(data: &inspect::Data, user_data: *mut c_void) {
    let inspect_data = unsafe { &mut *(user_data as *mut InspectData) };

    if let Some(filename) = inspect_data.image_filename {
        match write_ppm(&data.color_buffer, filename) {
            Err(e) => {
                eprintln!("{}", e);
                inspect_data.failed = true;
            },
            Ok(()) => (),
        }
    }

    if let Some(filename) = inspect_data.buffer_filename {
        match write_buffer(&data, filename, inspect_data.buffer_binding) {
            Err(e) => {
                eprintln!("{}", e);
                inspect_data.failed = true;
            },
            Ok(()) => (),
        }
    }
}

fn set_up_config(
    config: &Rc<RefCell<Config>>,
    options: &Options,
    inspect_data: &mut InspectData,
) -> Result<(), Error> {
    let mut config = config.borrow_mut();

    config.set_inspect_cb(Some(inspect_cb));
    config.set_user_data(ptr::addr_of_mut!(*inspect_data).cast());

    if let Some(&ArgumentValue::Integer(device_id))
        = options.values.get(DEVICE_ID_OPTION)
    {
        match device_id.checked_sub(1) {
            None => return Err(Error::ZeroDeviceId),
            Some(device_id) => config.set_device_id(Some(device_id as usize)),
        }
    }

    if let Some(ArgumentValue::Flag) = options.values.get(DISASM_OPTION) {
        config.set_show_disassembly(true);
    }

    Ok(())
}

fn format_result(result: result::Result) -> String {
    format!("PIGLIT: {{\"result\": \"{}\" }}", result.name())
}

fn add_token_replacements(
    token_replacements: &[TokenReplacement<'_>],
    source: &mut Source,
) {
    for token_replacement in token_replacements.iter() {
        source.add_token_replacement(
            token_replacement.token.to_owned(),
            token_replacement.replacement.to_owned(),
        );
    }
}

fn run() -> Result<(), Error> {
    let options = parse_options(std::env::args_os())?;

    if options.values.contains_key(HELP_OPTION) || options.scripts.is_empty() {
        return Err(Error::ShowHelp);
    }

    let config = Rc::new(RefCell::new(Config::new()));

    let mut inspect_data = InspectData::new(&options);

    set_up_config(&config, &options, &mut inspect_data)?;

    let token_replacements = get_token_replacements(&options.values)?;

    let mut executor = Executor::new(Rc::clone(&config));

    let mut overall_result = result::Result::Skip;

    for script_filename in options.scripts.iter() {
        if options.scripts.len() > 1
            && !options.values.contains_key(QUIET_OPTION)
        {
            println!("{}", script_filename.to_string_lossy());
        }

        let mut source = Source::from_file(script_filename.into());

        add_token_replacements(&token_replacements, &mut source);

        let result = executor.execute(&source);

        overall_result = overall_result.merge(result);
    }

    if inspect_data.failed {
        overall_result = overall_result.merge(result::Result::Fail);
    }

    match overall_result {
        result::Result::Fail => Err(Error::TestFailed),
        result::Result::Pass if options.values.contains_key(QUIET_OPTION) => {
            Ok(())
        },
        _ => {
            println!("{}", format_result(overall_result));
            Ok(())
        },
    }
}

fn main() -> ExitCode {
    match run() {
        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("{}", e);
            ExitCode::FAILURE
        },
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn all_arg_types() {
        let args: Vec<OsString> = vec![
            "vkrunner".into(),
            "-dB".into(), "12".into(),
            "--image".into(), "screenshot.png".into(),
            "--replace".into(), "bad=aĉa".into(),
            "--replace=good=bona".into(),
            "script.shader_test".into(),
        ];

        let options = parse_options(args.into_iter()).unwrap();

        assert!(matches!(&options.values[DISASM_OPTION], ArgumentValue::Flag));

        let ArgumentValue::Integer(binding) = &options.values[BINDING_OPTION]
        else { unreachable!(); };
        assert_eq!(*binding, 12);

        let ArgumentValue::Filename(image) = &options.values[IMAGE_OPTION]
        else { unreachable!(); };
        assert_eq!(image.to_str().unwrap(), "screenshot.png");

        let ArgumentValue::StringArray(values) = &options.values[REPLACE_OPTION]
        else { unreachable!(); };
        assert_eq!(
            values,
            &vec!["bad=aĉa".to_string(), "good=bona".to_string()]
        );

        assert_eq!(options.scripts.len(), 1);
        assert_eq!(options.scripts[0].to_str().unwrap(), "script.shader_test");
    }

    #[test]
    fn multi_arguments() {
        let args = vec![
            "vkrunner".into(),
            "-ib".into(), "image.png".into(), "buffer.raw".into(),
        ];

        let options = parse_options(args.into_iter()).unwrap();

        let ArgumentValue::Filename(image) = &options.values[IMAGE_OPTION]
        else { unreachable!(); };
        assert_eq!(image.to_str().unwrap(), "image.png");

        let ArgumentValue::Filename(image) = &options.values[BUFFER_OPTION]
        else { unreachable!(); };
        assert_eq!(image.to_str().unwrap(), "buffer.raw");
    }

    #[test]
    fn unknown_option() {
        let args = vec!["vkrunner".into(), "--bad-option".into()].into_iter();
        let error = parse_options(args).unwrap_err();
        assert_eq!(&error.to_string(), "Unknown option: --bad-option");

        let args = vec!["vkrunner".into(), "-d?".into()].into_iter();
        let error = parse_options(args).unwrap_err();
        assert_eq!(&error.to_string(), "Unknown option: ?");

        let args = vec!["vkrunner".into(), "-".into()].into_iter();
        let error = parse_options(args).unwrap_err();
        assert_eq!(&error.to_string(), "Unknown option: -");
    }

    #[test]
    fn trailing_arguments() {
        let args = vec![
            "vkrunner".into(),
            "-i".into(), "image.png".into(),
            "--".into(),
            "-i.shader_test".into(),
        ];

        let options = parse_options(args.into_iter()).unwrap();

        assert_eq!(options.scripts.len(), 1);
        assert_eq!(options.scripts[0].to_str().unwrap(), "-i.shader_test");
    }

    #[test]
    fn missing_argument() {
        let args = vec!["vkrunner".into(), "--buffer".into()].into_iter();
        let error = parse_options(args).unwrap_err();
        assert_eq!(&error.to_string(), "Option --buffer requires an argument");

        let args = vec!["vkrunner".into(), "-D".into()].into_iter();
        let error = parse_options(args).unwrap_err();
        assert_eq!(&error.to_string(), "Option --replace requires an argument");

        let args = vec!["vkrunner".into(), "-B".into()].into_iter();
        let error = parse_options(args).unwrap_err();
        assert_eq!(&error.to_string(), "Option --binding requires an argument");
    }

    #[test]
    #[cfg(unix)]
    fn invalid_utf8() {
        use std::os::unix::ffi::OsStrExt;
        use std::ffi::OsStr;

        let args = vec![
            "vkrunner".into(),
            "-D".into(),
            OsStr::from_bytes(b"buffer-\x80.raw").into(),
        ].into_iter();

        let error = parse_options(args).unwrap_err();
        assert_eq!(
            &error.to_string(),
            "Invalid UTF-8 in argument to --replace",
        );

        let args = vec![
            "vkrunner".into(),
            "-B".into(),
            OsStr::from_bytes(b"TWELVE-\x80").into(),
        ].into_iter();

        let error = parse_options(args).unwrap_err();
        assert_eq!(
            &error.to_string(),
            "Invalid UTF-8 in argument to --binding",
        );
    }

    #[test]
    fn invalid_integer() {
        let args = vec![
            "vkrunner".into(),
            "-B".into(), "twelve".into(),
        ].into_iter();

        let error = parse_options(args).unwrap_err();
        assert_eq!(
            &error.to_string(),
            "Invalid integer: twelve",
        );
    }

    #[test]
    fn argument_for_flag() {
        let args = vec!["vkrunner".into(), "--quiet=yes".into()].into_iter();
        let error = parse_options(args).unwrap_err();
        assert_eq!(
            &error.to_string(),
            "Option --quiet is a flag and can not take an argument",
        );
    }
}

```

--------------------------------------------------------------------------------
/vkrunner/vkrunner/vulkan_funcs_data.rs:
--------------------------------------------------------------------------------

```rust
// Automatically generated by make-vulkan-funcs-data.py

#[derive(Debug, Clone)]
#[allow(non_snake_case)]
pub struct Library {
    lib_vulkan: *const c_void,
    lib_vulkan_is_fake: bool,

    pub vkGetInstanceProcAddr: vk::PFN_vkGetInstanceProcAddr,
    pub vkCreateInstance: vk::PFN_vkCreateInstance,
    pub vkEnumerateInstanceExtensionProperties: vk::PFN_vkEnumerateInstanceExtensionProperties,
}

#[derive(Debug, Clone)]
#[allow(non_snake_case)]
pub struct Instance {
    pub vkCreateDevice: vk::PFN_vkCreateDevice,
    pub vkDestroyInstance: vk::PFN_vkDestroyInstance,
    pub vkEnumerateDeviceExtensionProperties: vk::PFN_vkEnumerateDeviceExtensionProperties,
    pub vkEnumeratePhysicalDevices: vk::PFN_vkEnumeratePhysicalDevices,
    pub vkGetDeviceProcAddr: vk::PFN_vkGetDeviceProcAddr,
    pub vkGetPhysicalDeviceFeatures: vk::PFN_vkGetPhysicalDeviceFeatures,
    pub vkGetPhysicalDeviceFeatures2KHR: vk::PFN_vkGetPhysicalDeviceFeatures2KHR,
    pub vkGetPhysicalDeviceFormatProperties: vk::PFN_vkGetPhysicalDeviceFormatProperties,
    pub vkGetPhysicalDeviceMemoryProperties: vk::PFN_vkGetPhysicalDeviceMemoryProperties,
    pub vkGetPhysicalDeviceProperties: vk::PFN_vkGetPhysicalDeviceProperties,
    pub vkGetPhysicalDeviceProperties2: vk::PFN_vkGetPhysicalDeviceProperties2,
    pub vkGetPhysicalDeviceQueueFamilyProperties: vk::PFN_vkGetPhysicalDeviceQueueFamilyProperties,
    pub vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR: vk::PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR,
}

#[derive(Debug, Clone)]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub struct Device {
    pub vkAllocateCommandBuffers: vk::PFN_vkAllocateCommandBuffers,
    pub vkAllocateDescriptorSets: vk::PFN_vkAllocateDescriptorSets,
    pub vkAllocateMemory: vk::PFN_vkAllocateMemory,
    pub vkBeginCommandBuffer: vk::PFN_vkBeginCommandBuffer,
    pub vkBindBufferMemory: vk::PFN_vkBindBufferMemory,
    pub vkBindImageMemory: vk::PFN_vkBindImageMemory,
    pub vkCmdBeginRenderPass: vk::PFN_vkCmdBeginRenderPass,
    pub vkCmdBindDescriptorSets: vk::PFN_vkCmdBindDescriptorSets,
    pub vkCmdBindIndexBuffer: vk::PFN_vkCmdBindIndexBuffer,
    pub vkCmdBindPipeline: vk::PFN_vkCmdBindPipeline,
    pub vkCmdBindVertexBuffers: vk::PFN_vkCmdBindVertexBuffers,
    pub vkCmdClearAttachments: vk::PFN_vkCmdClearAttachments,
    pub vkCmdCopyBufferToImage: vk::PFN_vkCmdCopyBufferToImage,
    pub vkCmdCopyImageToBuffer: vk::PFN_vkCmdCopyImageToBuffer,
    pub vkCmdDispatch: vk::PFN_vkCmdDispatch,
    pub vkCmdDraw: vk::PFN_vkCmdDraw,
    pub vkCmdDrawIndexed: vk::PFN_vkCmdDrawIndexed,
    pub vkCmdDrawIndexedIndirect: vk::PFN_vkCmdDrawIndexedIndirect,
    pub vkCmdEndRenderPass: vk::PFN_vkCmdEndRenderPass,
    pub vkCmdPipelineBarrier: vk::PFN_vkCmdPipelineBarrier,
    pub vkCmdPushConstants: vk::PFN_vkCmdPushConstants,
    pub vkCmdSetScissor: vk::PFN_vkCmdSetScissor,
    pub vkCmdSetViewport: vk::PFN_vkCmdSetViewport,
    pub vkCreateBuffer: vk::PFN_vkCreateBuffer,
    pub vkCreateCommandPool: vk::PFN_vkCreateCommandPool,
    pub vkCreateComputePipelines: vk::PFN_vkCreateComputePipelines,
    pub vkCreateDescriptorPool: vk::PFN_vkCreateDescriptorPool,
    pub vkCreateDescriptorSetLayout: vk::PFN_vkCreateDescriptorSetLayout,
    pub vkCreateFence: vk::PFN_vkCreateFence,
    pub vkCreateFramebuffer: vk::PFN_vkCreateFramebuffer,
    pub vkCreateGraphicsPipelines: vk::PFN_vkCreateGraphicsPipelines,
    pub vkCreateImage: vk::PFN_vkCreateImage,
    pub vkCreateImageView: vk::PFN_vkCreateImageView,
    pub vkCreatePipelineCache: vk::PFN_vkCreatePipelineCache,
    pub vkCreatePipelineLayout: vk::PFN_vkCreatePipelineLayout,
    pub vkCreateRenderPass: vk::PFN_vkCreateRenderPass,
    pub vkCreateSampler: vk::PFN_vkCreateSampler,
    pub vkCreateSemaphore: vk::PFN_vkCreateSemaphore,
    pub vkCreateShaderModule: vk::PFN_vkCreateShaderModule,
    pub vkDestroyBuffer: vk::PFN_vkDestroyBuffer,
    pub vkDestroyCommandPool: vk::PFN_vkDestroyCommandPool,
    pub vkDestroyDescriptorPool: vk::PFN_vkDestroyDescriptorPool,
    pub vkDestroyDescriptorSetLayout: vk::PFN_vkDestroyDescriptorSetLayout,
    pub vkDestroyDevice: vk::PFN_vkDestroyDevice,
    pub vkDestroyFence: vk::PFN_vkDestroyFence,
    pub vkDestroyFramebuffer: vk::PFN_vkDestroyFramebuffer,
    pub vkDestroyImage: vk::PFN_vkDestroyImage,
    pub vkDestroyImageView: vk::PFN_vkDestroyImageView,
    pub vkDestroyPipeline: vk::PFN_vkDestroyPipeline,
    pub vkDestroyPipelineCache: vk::PFN_vkDestroyPipelineCache,
    pub vkDestroyPipelineLayout: vk::PFN_vkDestroyPipelineLayout,
    pub vkDestroyRenderPass: vk::PFN_vkDestroyRenderPass,
    pub vkDestroySampler: vk::PFN_vkDestroySampler,
    pub vkDestroySemaphore: vk::PFN_vkDestroySemaphore,
    pub vkDestroyShaderModule: vk::PFN_vkDestroyShaderModule,
    pub vkEndCommandBuffer: vk::PFN_vkEndCommandBuffer,
    pub vkFlushMappedMemoryRanges: vk::PFN_vkFlushMappedMemoryRanges,
    pub vkFreeCommandBuffers: vk::PFN_vkFreeCommandBuffers,
    pub vkFreeDescriptorSets: vk::PFN_vkFreeDescriptorSets,
    pub vkFreeMemory: vk::PFN_vkFreeMemory,
    pub vkGetBufferMemoryRequirements: vk::PFN_vkGetBufferMemoryRequirements,
    pub vkGetDeviceQueue: vk::PFN_vkGetDeviceQueue,
    pub vkGetImageMemoryRequirements: vk::PFN_vkGetImageMemoryRequirements,
    pub vkGetImageSubresourceLayout: vk::PFN_vkGetImageSubresourceLayout,
    pub vkInvalidateMappedMemoryRanges: vk::PFN_vkInvalidateMappedMemoryRanges,
    pub vkMapMemory: vk::PFN_vkMapMemory,
    pub vkQueueSubmit: vk::PFN_vkQueueSubmit,
    pub vkQueueWaitIdle: vk::PFN_vkQueueWaitIdle,
    pub vkResetFences: vk::PFN_vkResetFences,
    pub vkUnmapMemory: vk::PFN_vkUnmapMemory,
    pub vkUpdateDescriptorSets: vk::PFN_vkUpdateDescriptorSets,
    pub vkWaitForFences: vk::PFN_vkWaitForFences,
}

impl Instance {
    pub unsafe fn new(
        get_instance_proc_cb: GetInstanceProcFunc,
        user_data: *const c_void,
    ) -> Instance {
        Instance {
            vkCreateDevice: std::mem::transmute(get_instance_proc_cb(
                "vkCreateDevice\0".as_ptr().cast(),
                user_data,
            )),
            vkDestroyInstance: std::mem::transmute(get_instance_proc_cb(
                "vkDestroyInstance\0".as_ptr().cast(),
                user_data,
            )),
            vkEnumerateDeviceExtensionProperties: std::mem::transmute(get_instance_proc_cb(
                "vkEnumerateDeviceExtensionProperties\0".as_ptr().cast(),
                user_data,
            )),
            vkEnumeratePhysicalDevices: std::mem::transmute(get_instance_proc_cb(
                "vkEnumeratePhysicalDevices\0".as_ptr().cast(),
                user_data,
            )),
            vkGetDeviceProcAddr: std::mem::transmute(get_instance_proc_cb(
                "vkGetDeviceProcAddr\0".as_ptr().cast(),
                user_data,
            )),
            vkGetPhysicalDeviceFeatures: std::mem::transmute(get_instance_proc_cb(
                "vkGetPhysicalDeviceFeatures\0".as_ptr().cast(),
                user_data,
            )),
            vkGetPhysicalDeviceFeatures2KHR: std::mem::transmute(get_instance_proc_cb(
                "vkGetPhysicalDeviceFeatures2KHR\0".as_ptr().cast(),
                user_data,
            )),
            vkGetPhysicalDeviceFormatProperties: std::mem::transmute(get_instance_proc_cb(
                "vkGetPhysicalDeviceFormatProperties\0".as_ptr().cast(),
                user_data,
            )),
            vkGetPhysicalDeviceMemoryProperties: std::mem::transmute(get_instance_proc_cb(
                "vkGetPhysicalDeviceMemoryProperties\0".as_ptr().cast(),
                user_data,
            )),
            vkGetPhysicalDeviceProperties: std::mem::transmute(get_instance_proc_cb(
                "vkGetPhysicalDeviceProperties\0".as_ptr().cast(),
                user_data,
            )),
            vkGetPhysicalDeviceProperties2: std::mem::transmute(get_instance_proc_cb(
                "vkGetPhysicalDeviceProperties2\0".as_ptr().cast(),
                user_data,
            )),
            vkGetPhysicalDeviceQueueFamilyProperties: std::mem::transmute(get_instance_proc_cb(
                "vkGetPhysicalDeviceQueueFamilyProperties\0".as_ptr().cast(),
                user_data,
            )),
            vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR: std::mem::transmute(get_instance_proc_cb(
                "vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR\0".as_ptr().cast(),
                user_data,
            )),
        }
    }
}

#[allow(dead_code)]
impl Device {
    pub fn new(instance: &Instance, device: vk::VkDevice) -> Device {
        Device {
            vkAllocateCommandBuffers: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkAllocateCommandBuffers\0".as_ptr().cast(),
                ))
            },
            vkAllocateDescriptorSets: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkAllocateDescriptorSets\0".as_ptr().cast(),
                ))
            },
            vkAllocateMemory: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkAllocateMemory\0".as_ptr().cast(),
                ))
            },
            vkBeginCommandBuffer: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkBeginCommandBuffer\0".as_ptr().cast(),
                ))
            },
            vkBindBufferMemory: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkBindBufferMemory\0".as_ptr().cast(),
                ))
            },
            vkBindImageMemory: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkBindImageMemory\0".as_ptr().cast(),
                ))
            },
            vkCmdBeginRenderPass: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdBeginRenderPass\0".as_ptr().cast(),
                ))
            },
            vkCmdBindDescriptorSets: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdBindDescriptorSets\0".as_ptr().cast(),
                ))
            },
            vkCmdBindIndexBuffer: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdBindIndexBuffer\0".as_ptr().cast(),
                ))
            },
            vkCmdBindPipeline: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdBindPipeline\0".as_ptr().cast(),
                ))
            },
            vkCmdBindVertexBuffers: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdBindVertexBuffers\0".as_ptr().cast(),
                ))
            },
            vkCmdClearAttachments: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdClearAttachments\0".as_ptr().cast(),
                ))
            },
            vkCmdCopyBufferToImage: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdCopyBufferToImage\0".as_ptr().cast(),
                ))
            },
            vkCmdCopyImageToBuffer: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdCopyImageToBuffer\0".as_ptr().cast(),
                ))
            },
            vkCmdDispatch: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdDispatch\0".as_ptr().cast(),
                ))
            },
            vkCmdDraw: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdDraw\0".as_ptr().cast(),
                ))
            },
            vkCmdDrawIndexed: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdDrawIndexed\0".as_ptr().cast(),
                ))
            },
            vkCmdDrawIndexedIndirect: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdDrawIndexedIndirect\0".as_ptr().cast(),
                ))
            },
            vkCmdEndRenderPass: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdEndRenderPass\0".as_ptr().cast(),
                ))
            },
            vkCmdPipelineBarrier: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdPipelineBarrier\0".as_ptr().cast(),
                ))
            },
            vkCmdPushConstants: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdPushConstants\0".as_ptr().cast(),
                ))
            },
            vkCmdSetScissor: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdSetScissor\0".as_ptr().cast(),
                ))
            },
            vkCmdSetViewport: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCmdSetViewport\0".as_ptr().cast(),
                ))
            },
            vkCreateBuffer: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateBuffer\0".as_ptr().cast(),
                ))
            },
            vkCreateCommandPool: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateCommandPool\0".as_ptr().cast(),
                ))
            },
            vkCreateComputePipelines: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateComputePipelines\0".as_ptr().cast(),
                ))
            },
            vkCreateDescriptorPool: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateDescriptorPool\0".as_ptr().cast(),
                ))
            },
            vkCreateDescriptorSetLayout: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateDescriptorSetLayout\0".as_ptr().cast(),
                ))
            },
            vkCreateFence: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateFence\0".as_ptr().cast(),
                ))
            },
            vkCreateFramebuffer: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateFramebuffer\0".as_ptr().cast(),
                ))
            },
            vkCreateGraphicsPipelines: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateGraphicsPipelines\0".as_ptr().cast(),
                ))
            },
            vkCreateImage: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateImage\0".as_ptr().cast(),
                ))
            },
            vkCreateImageView: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateImageView\0".as_ptr().cast(),
                ))
            },
            vkCreatePipelineCache: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreatePipelineCache\0".as_ptr().cast(),
                ))
            },
            vkCreatePipelineLayout: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreatePipelineLayout\0".as_ptr().cast(),
                ))
            },
            vkCreateRenderPass: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateRenderPass\0".as_ptr().cast(),
                ))
            },
            vkCreateSampler: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateSampler\0".as_ptr().cast(),
                ))
            },
            vkCreateSemaphore: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateSemaphore\0".as_ptr().cast(),
                ))
            },
            vkCreateShaderModule: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkCreateShaderModule\0".as_ptr().cast(),
                ))
            },
            vkDestroyBuffer: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyBuffer\0".as_ptr().cast(),
                ))
            },
            vkDestroyCommandPool: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyCommandPool\0".as_ptr().cast(),
                ))
            },
            vkDestroyDescriptorPool: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyDescriptorPool\0".as_ptr().cast(),
                ))
            },
            vkDestroyDescriptorSetLayout: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyDescriptorSetLayout\0".as_ptr().cast(),
                ))
            },
            vkDestroyDevice: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyDevice\0".as_ptr().cast(),
                ))
            },
            vkDestroyFence: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyFence\0".as_ptr().cast(),
                ))
            },
            vkDestroyFramebuffer: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyFramebuffer\0".as_ptr().cast(),
                ))
            },
            vkDestroyImage: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyImage\0".as_ptr().cast(),
                ))
            },
            vkDestroyImageView: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyImageView\0".as_ptr().cast(),
                ))
            },
            vkDestroyPipeline: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyPipeline\0".as_ptr().cast(),
                ))
            },
            vkDestroyPipelineCache: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyPipelineCache\0".as_ptr().cast(),
                ))
            },
            vkDestroyPipelineLayout: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyPipelineLayout\0".as_ptr().cast(),
                ))
            },
            vkDestroyRenderPass: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyRenderPass\0".as_ptr().cast(),
                ))
            },
            vkDestroySampler: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroySampler\0".as_ptr().cast(),
                ))
            },
            vkDestroySemaphore: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroySemaphore\0".as_ptr().cast(),
                ))
            },
            vkDestroyShaderModule: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkDestroyShaderModule\0".as_ptr().cast(),
                ))
            },
            vkEndCommandBuffer: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkEndCommandBuffer\0".as_ptr().cast(),
                ))
            },
            vkFlushMappedMemoryRanges: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkFlushMappedMemoryRanges\0".as_ptr().cast(),
                ))
            },
            vkFreeCommandBuffers: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkFreeCommandBuffers\0".as_ptr().cast(),
                ))
            },
            vkFreeDescriptorSets: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkFreeDescriptorSets\0".as_ptr().cast(),
                ))
            },
            vkFreeMemory: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkFreeMemory\0".as_ptr().cast(),
                ))
            },
            vkGetBufferMemoryRequirements: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkGetBufferMemoryRequirements\0".as_ptr().cast(),
                ))
            },
            vkGetDeviceQueue: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkGetDeviceQueue\0".as_ptr().cast(),
                ))
            },
            vkGetImageMemoryRequirements: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkGetImageMemoryRequirements\0".as_ptr().cast(),
                ))
            },
            vkGetImageSubresourceLayout: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkGetImageSubresourceLayout\0".as_ptr().cast(),
                ))
            },
            vkInvalidateMappedMemoryRanges: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkInvalidateMappedMemoryRanges\0".as_ptr().cast(),
                ))
            },
            vkMapMemory: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkMapMemory\0".as_ptr().cast(),
                ))
            },
            vkQueueSubmit: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkQueueSubmit\0".as_ptr().cast(),
                ))
            },
            vkQueueWaitIdle: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkQueueWaitIdle\0".as_ptr().cast(),
                ))
            },
            vkResetFences: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkResetFences\0".as_ptr().cast(),
                ))
            },
            vkUnmapMemory: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkUnmapMemory\0".as_ptr().cast(),
                ))
            },
            vkUpdateDescriptorSets: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkUpdateDescriptorSets\0".as_ptr().cast(),
                ))
            },
            vkWaitForFences: unsafe {
                std::mem::transmute(instance.vkGetDeviceProcAddr.unwrap()(
                    device,
                    "vkWaitForFences\0".as_ptr().cast(),
                ))
            },
        }
    }
}

```

--------------------------------------------------------------------------------
/vkrunner/vkrunner/pipeline_key.rs:
--------------------------------------------------------------------------------

```rust
// vkrunner
//
// Copyright (C) 2018 Intel Corporation
// Copyright 2023 Neil Roberts
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice (including the next
// paragraph) shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use crate::shader_stage;
use crate::vk;
use crate::parse_num;
use crate::hex;
use crate::util;
use std::fmt;
use std::mem;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Type {
    Graphics,
    Compute,
}

/// Notes whether the pipeline will be used to draw a rectangle or
/// whether it will use the data in the `[vertex data]` section of the
/// script.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Source {
    Rectangle,
    VertexData,
}

/// The failure code returned by [set](Key::set)
#[derive(Copy, Clone, Debug)]
pub enum SetPropertyError<'a> {
    /// The property was not recognised by VkRunner
    NotFound { property: &'a str },
    /// The property was recognised but the value string was not in a
    /// valid format
    InvalidValue { value: &'a str },
}

impl<'a> fmt::Display for SetPropertyError<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SetPropertyError::NotFound { property } => {
                write!(f, "Unknown property: {}", property)
            },
            SetPropertyError::InvalidValue { value } => {
                write!(f, "Invalid value: {}", value)
            },
        }
    }
}

enum PropertyType {
    Bool,
    Int,
    Float,
}

struct Property {
    prop_type: PropertyType,
    num: usize,
    name: &'static str,
}

include!{"pipeline_key_data.rs"}

struct EnumValue {
    name: &'static str,
    value: i32,
}

include!{"enum_table.rs"}

/// A set of properties that can be used to create a VkPipeline. The
/// intention is that this will work as a key so that the program can
/// detect if the same state is used multiple times so it can reuse
/// the same key.
#[derive(Clone)]
pub struct Key {
    pipeline_type: Type,
    source: Source,

    entrypoints: [Option<String>; shader_stage::N_STAGES],

    bool_properties: [bool; N_BOOL_PROPERTIES],
    int_properties: [i32; N_INT_PROPERTIES],
    float_properties: [f32; N_FLOAT_PROPERTIES],
}

impl Key {
    pub fn set_pipeline_type(&mut self, pipeline_type: Type) {
        self.pipeline_type = pipeline_type;
    }

    pub fn pipeline_type(&self) -> Type {
        self.pipeline_type
    }

    pub fn set_source(&mut self, source: Source) {
        self.source = source;
    }

    pub fn source(&self) -> Source {
        self.source
    }

    pub fn set_topology(&mut self, topology: vk::VkPrimitiveTopology) {
        self.int_properties[TOPOLOGY_PROP_NUM] = topology as i32;
    }

    pub fn set_patch_control_points(&mut self, patch_control_points: u32) {
        self.int_properties[PATCH_CONTROL_POINTS_PROP_NUM] =
            patch_control_points as i32;
    }

    pub fn set_entrypoint(
        &mut self,
        stage: shader_stage::Stage,
        entrypoint: String,
    ) {
        self.entrypoints[stage as usize] = Some(entrypoint);
    }

    pub fn entrypoint(&self, stage: shader_stage::Stage) -> &str {
        match &self.entrypoints[stage as usize] {
            Some(s) => &s[..],
            None => "main",
        }
    }

    fn find_prop(
        property: &str
    ) -> Result<&'static Property, SetPropertyError> {
        PROPERTIES.binary_search_by(|p| p.name.cmp(property))
            .and_then(|pos| Ok(&PROPERTIES[pos]))
            .or_else(|_| Err(SetPropertyError::NotFound { property }))
    }

    fn set_bool<'a>(
        &mut self,
        prop: &Property,
        value: &'a str,
    ) -> Result<(), SetPropertyError<'a>> {
        let value = if value == "true" {
            true
        } else if value == "false" {
            false
        } else {
            match parse_num::parse_i32(value) {
                Ok((v, tail)) if tail.is_empty() => v != 0,
                _ => return Err(SetPropertyError::InvalidValue { value }),
            }
        };

        self.bool_properties[prop.num] = value;

        Ok(())
    }

    // Looks up the given enum name in ENUM_VALUES using a binary
    // search. Any trailing characters that can’t be part of an enum
    // name are cut. If successful it returns the enum value and a
    // slice containing the part of the name that was cut. Otherwise
    // returns None.
    fn lookup_enum(name: &str) -> Option<(i32, &str)> {
        let length = name
            .chars()
            .take_while(|&c| c.is_alphanumeric() || c == '_')
            .count();

        let part = &name[0..length];

        ENUM_VALUES.binary_search_by(|enum_value| enum_value.name.cmp(part))
            .ok()
            .and_then(|pos| Some((ENUM_VALUES[pos].value, &name[length..])))
            .or_else(|| None)
    }

    fn set_int<'a>(
        &mut self,
        prop: &Property,
        value: &'a str,
    ) -> Result<(), SetPropertyError<'a>> {
        let mut value_part = value;
        let mut num_value = 0i32;

        loop {
            value_part = value_part.trim_start();

            if let Ok((v, tail)) = parse_num::parse_i32(value_part) {
                num_value |= v;
                value_part = tail;
            } else if let Some((v, tail)) = Key::lookup_enum(value_part) {
                num_value |= v;
                value_part = tail;
            } else {
                break Err(SetPropertyError::InvalidValue { value });
            }

            value_part = value_part.trim_start();

            if value_part.is_empty() {
                self.int_properties[prop.num] = num_value;
                break Ok(());
            }

            // If there’s anything left after the number it must be
            // the ‘|’ operator
            if !value_part.starts_with('|') {
                break Err(SetPropertyError::InvalidValue { value });
            }

            // Skip the ‘|’ character
            value_part = &value_part[1..];
        }
    }

    fn set_float<'a>(
        &mut self,
        prop: &Property,
        value: &'a str,
    ) -> Result<(), SetPropertyError<'a>> {
        match hex::parse_f32(value) {
            Ok((v, tail)) if tail.is_empty() => {
                self.float_properties[prop.num] = v;
                Ok(())
            },
            _ => Err(SetPropertyError::InvalidValue { value }),
        }
    }

    /// Set a property on the pipeline key. The property name is one
    /// of the members of any of the structs pointed to by
    /// `VkGraphicsPipelineCreateInfo`. For example, if `prop_name` is
    /// `"polygonMode"` then it will set the `polygonMode` field of
    /// the `VkPipelineRasterizationStateCreateInfo` struct pointed to
    /// by the `VkGraphicsPipelineCreateInfo` struct.
    ///
    /// The `value` will be interpreted depending on the type of the
    /// property. It will be one of the following three basic types:
    ///
    /// bool: The value can either be `true`, `false` or an integer
    /// value. If it’s an integer the bool will be true if the value
    /// is non-zero.
    ///
    /// int: The value can either be an integer value or one of the
    /// enum names used by the properties. You can also use the `|`
    /// operator to bitwise or values. This is useful for setting
    /// flags. For example `VK_COLOR_COMPONENT_R_BIT |
    /// VK_COLOR_COMPONENT_G_BIT` can be used as a value to set the
    /// `colorWriteMask` property.
    ///
    /// float: The value will be interperted as a floating-point value
    /// using [hex::parse_f32].
    pub fn set<'a>(
        &mut self,
        prop_name: &'a str,
        value: &'a str
    ) -> Result<(), SetPropertyError<'a>> {
        let prop = Key::find_prop(prop_name)?;

        let value = value.trim();

        match prop.prop_type {
            PropertyType::Bool => self.set_bool(prop, value),
            PropertyType::Int => self.set_int(prop, value),
            PropertyType::Float => self.set_float(prop, value),
        }
    }

    fn alloc_struct_size(
        buf: &mut Vec<u8>,
        struct_type: vk::VkStructureType,
        size: usize,
        align: usize
    ) -> usize {
        let offset = util::align(buf.len(), align);
        buf.resize(offset + size, 0);
        buf[offset..offset + mem::size_of::<vk::VkStructureType>()]
            .copy_from_slice(&struct_type.to_ne_bytes());
        offset
    }

    fn alloc_struct<T>(
        buf: &mut Vec<u8>,
        struct_type: vk::VkStructureType,
    ) -> usize {
        Key::alloc_struct_size(
            buf,
            struct_type,
            mem::size_of::<T>(),
            mem::align_of::<T>(),
        )
    }

    fn alloc_create_info() -> Box<[u8]> {
        let mut buf = Vec::new();

        // Allocate all of the structures before setting the pointers
        // because the addresses will change when the vec grows
        let base_offset =
            Key::alloc_struct::<vk::VkGraphicsPipelineCreateInfo>(
                &mut buf,
                vk::VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
            );

        assert_eq!(base_offset, 0);

        let input_assembly =
            Key::alloc_struct::<vk::VkPipelineInputAssemblyStateCreateInfo>(
                &mut buf,
                vk::VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
            );
        let tessellation =
            Key::alloc_struct::<vk::VkPipelineTessellationStateCreateInfo>(
                &mut buf,
                vk::VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
            );
        let rasterization =
            Key::alloc_struct::<vk::VkPipelineRasterizationStateCreateInfo>(
                &mut buf,
                vk::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
            );
        let color_blend =
            Key::alloc_struct::<vk::VkPipelineColorBlendStateCreateInfo>(
                &mut buf,
                vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
            );
        let color_blend_attachment =
            Key::alloc_struct::<vk::VkPipelineColorBlendAttachmentState>(
                &mut buf,
                0, // no struture type
            );
        let depth_stencil =
            Key::alloc_struct::<vk::VkPipelineDepthStencilStateCreateInfo>(
                &mut buf,
                vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
            );

        let mut buf = buf.into_boxed_slice();

        let create_info = unsafe {
            &mut *(buf.as_mut_ptr() as *mut vk::VkGraphicsPipelineCreateInfo)
        };

        let base_ptr = buf.as_ptr() as *const u8;

        // SAFETY: The pointer adds should all be within the single
        // allocated buf
        unsafe {
            create_info.pInputAssemblyState =
                base_ptr.add(input_assembly).cast();
            create_info.pTessellationState =
                base_ptr.add(tessellation).cast();
            create_info.pRasterizationState =
                base_ptr.add(rasterization).cast();
            create_info.pColorBlendState =
                base_ptr.add(color_blend).cast();
            create_info.pDepthStencilState =
                base_ptr.add(depth_stencil).cast();

            // We need to transmute to get rid of the const
            let color_blend: &mut vk::VkPipelineColorBlendStateCreateInfo =
                mem::transmute(create_info.pColorBlendState);
            color_blend.pAttachments =
                base_ptr.add(color_blend_attachment).cast();
        }

        buf
    }

    /// Allocates a `VkGraphicsPipelineCreateInfo` struct inside a
    /// boxed u8 slice along with the
    /// `VkPipelineInputAssemblyStateCreateInfo`,
    /// `VkPipelineTessellationStateCreateInfo`,
    /// `VkPipelineRasterizationStateCreateInfo`,
    /// `VkPipelineColorBlendStateCreateInfo`,
    /// `VkPipelineColorBlendAttachmentState` and
    /// `VkPipelineDepthStencilStateCreateInfo` structs that it points
    /// to. The properties from the pipeline key are filled in and the
    /// `sType` fields are given the appropriate values. All other
    /// fields are initialised to zero. The structs need to be in a
    /// box because they contain pointers to each other which means
    /// the structs won’t work correctly if the array is moved to a
    /// different address. The `VkGraphicsPipelineCreateInfo` is at
    /// the start of the array and the other structs can be found by
    /// following the internal pointers.
    pub fn to_create_info(&self) -> Box<[u8]> {
        let mut buf = Key::alloc_create_info();
        let create_info = unsafe {
            &mut *(buf.as_mut_ptr() as *mut vk::VkGraphicsPipelineCreateInfo)
        };
        unsafe {
            mem::transmute::<_, &mut vk::VkPipelineColorBlendStateCreateInfo>(
                create_info.pColorBlendState
            ).attachmentCount = 1;
        }
        copy_properties_to_create_info(self, create_info);
        buf
    }
}

impl PartialEq for Key {
    fn eq(&self, other: &Key) -> bool {
        if self.pipeline_type != other.pipeline_type {
            return false;
        }

        match self.pipeline_type {
            Type::Graphics => {
                if self.source != other.source
                    || self.bool_properties != other.bool_properties
                    || self.int_properties != other.int_properties
                    || self.float_properties != other.float_properties
                {
                    return false;
                }

                // Check the entrypoints for all of the stages except
                // the compute stage because that doesn’t affect
                // pipelines used for graphics.
                for i in 0..shader_stage::N_STAGES {
                    if i == shader_stage::Stage::Compute as usize {
                        continue;
                    }
                    if self.entrypoints[i].ne(&other.entrypoints[i]) {
                        return false;
                    }
                }

                true
            },
            Type::Compute => {
                // All of the properties only have an effect when the
                // pipeline is used for graphics so the only thing we
                // care about is the compute entrypoint.
                self.entrypoints[shader_stage::Stage::Compute as usize].eq(
                    &other.entrypoints[shader_stage::Stage::Compute as usize]
                )
            },
        }
    }
}

// Custom debug implementation for Key that reports the properties
// using the property names instead of confusing arrays.
impl fmt::Debug for Key {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Key {{ pipeline_type: {:?}, \
             source: {:?}, \
             entrypoints: {:?}",
            self.pipeline_type,
            self.source,
            &self.entrypoints,
        )?;

        for prop in PROPERTIES.iter() {
            write!(f, ", {}: ", prop.name)?;

            match prop.prop_type {
                PropertyType::Bool => {
                    write!(f, "{}", self.bool_properties[prop.num])?;
                },
                PropertyType::Int => {
                    write!(f, "{}", self.int_properties[prop.num])?;
                },
                PropertyType::Float => {
                    write!(f, "{}", self.float_properties[prop.num])?;
                },
            }
        }

        write!(f, " }}")
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use shader_stage::Stage;

    fn get_create_info_topology(key: &Key) -> vk::VkPrimitiveTopology {
        let s = key.to_create_info();
        let create_info = unsafe {
            mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>(
                s.as_ptr()
            )
        };
        unsafe {
            (*create_info.pInputAssemblyState).topology
        }
    }

    fn get_create_info_pcp(key: &Key) -> u32 {
        let s = key.to_create_info();
        let create_info = unsafe {
            mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>(
                s.as_ptr()
            )
        };
        unsafe {
            (*create_info.pTessellationState).patchControlPoints
        }
    }

    #[test]
    fn test_key_base() {
        let mut key = Key::default();

        key.set_pipeline_type(Type::Graphics);
        assert_eq!(key.pipeline_type(), Type::Graphics);
        key.set_pipeline_type(Type::Compute);
        assert_eq!(key.pipeline_type(), Type::Compute);
        key.set_source(Source::Rectangle);
        assert_eq!(key.source(), Source::Rectangle);
        key.set_source(Source::VertexData);
        assert_eq!(key.source(), Source::VertexData);

        key.set_topology(vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
        assert_eq!(
            get_create_info_topology(&key),
            vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
        );
        key.set_topology(vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
        assert_eq!(
            get_create_info_topology(&key),
            vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
        );

        key.set_patch_control_points(5);
        assert_eq!(get_create_info_pcp(&key), 5);
        key.set_patch_control_points(u32::MAX);
        assert_eq!(get_create_info_pcp(&key), u32::MAX);

        key.set_entrypoint(Stage::Vertex, "mystery_vortex".to_owned());
        assert_eq!(key.entrypoint(Stage::Vertex), "mystery_vortex");
        key.set_entrypoint(Stage::Fragment, "fraggle_rock".to_owned());
        assert_eq!(key.entrypoint(Stage::Fragment), "fraggle_rock");
        assert_eq!(key.entrypoint(Stage::Geometry), "main");
    }

    #[test]
    fn test_all_props() {
        let mut key = Key::default();

        // Check that setting all of the props works without returning
        // an error
        for prop in PROPERTIES.iter() {
            assert!(key.set(prop.name, "1").is_ok());
        }
    }

    fn check_bool_prop(value: &str) -> vk::VkBool32 {
        let mut key = Key::default();

        assert!(key.set("depthTestEnable", value).is_ok());

        let s = key.to_create_info();
        let create_info = unsafe {
            mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>(
                s.as_ptr()
            )
        };
        unsafe {
            (*create_info.pDepthStencilState).depthTestEnable
        }
    }

    #[test]
    fn test_bool_props() {
        assert_eq!(check_bool_prop("true"), 1);
        assert_eq!(check_bool_prop("false"), 0);
        assert_eq!(check_bool_prop(" true "), 1);
        assert_eq!(check_bool_prop("   false  "), 0);
        assert_eq!(check_bool_prop("1"), 1);
        assert_eq!(check_bool_prop("42"), 1);
        assert_eq!(check_bool_prop("  0x42  "), 1);
        assert_eq!(check_bool_prop("0"), 0);
        assert_eq!(check_bool_prop("  -0  "), 0);

        let e = Key::default().set("depthTestEnable", "foo").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: foo");
        let e = Key::default().set("stencilTestEnable", "9 foo").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: 9 foo");
        let e = Key::default().set("stencilTestEnable", "true fo").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: true fo");
    }

    fn check_int_prop(value: &str) -> u32 {
        let mut key = Key::default();

        assert!(key.set("patchControlPoints", value).is_ok());

        get_create_info_pcp(&key)
    }

    #[test]
    fn test_int_props() {
        assert_eq!(check_int_prop("0"), 0);
        assert_eq!(check_int_prop("1"), 1);
        assert_eq!(check_int_prop("-1"), u32::MAX);
        assert_eq!(check_int_prop("  42  "), 42);
        assert_eq!(check_int_prop(" 8 | 1 "), 9);
        assert_eq!(check_int_prop("6|16|1"), 23);
        assert_eq!(check_int_prop("010"), 8);
        assert_eq!(check_int_prop("0x80|010"), 0x88);
        assert_eq!(
            check_int_prop("VK_COLOR_COMPONENT_R_BIT"),
            vk::VK_COLOR_COMPONENT_R_BIT,
        );
        assert_eq!(
            check_int_prop(
                "VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT"
            ),
            vk::VK_COLOR_COMPONENT_R_BIT | vk::VK_COLOR_COMPONENT_G_BIT,
        );

        let e = Key::default().set("patchControlPoints", "").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: ");
        let e = Key::default().set("patchControlPoints", "9 |").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: 9 |");
        let e = Key::default().set("patchControlPoints", "|9").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: |9");
        let e = Key::default().set("patchControlPoints", "9|foo").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: 9|foo");
        let e = Key::default().set("patchControlPoints", "9foo").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: 9foo");

        let mut key = Key::default();

        // Check that all enum values can be set without error
        for e in ENUM_VALUES.iter() {
            assert!(key.set("srcColorBlendFactor", e.name).is_ok());
        }
    }

    fn check_float_prop(value: &str) -> f32 {
        let mut key = Key::default();

        assert!(key.set("depthBiasClamp", value).is_ok());

        let s = key.to_create_info();
        let create_info = unsafe {
            mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>(
                s.as_ptr()
            )
        };
        unsafe {
            (*create_info.pRasterizationState).depthBiasClamp
        }
    }

    #[test]
    fn test_float_props() {
        assert_eq!(check_float_prop("1"), 1.0);
        assert_eq!(check_float_prop("-1"), -1.0);
        assert_eq!(check_float_prop("1.0e1"), 10.0);
        assert_eq!(check_float_prop("  0x3F800000  "), 1.0);

        let e = Key::default().set("lineWidth", "0.3 foo").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: 0.3 foo");
        let e = Key::default().set("lineWidth", "foo").unwrap_err();
        assert_eq!(e.to_string(), "Invalid value: foo");
    }

    #[test]
    fn test_unknown_property() {
        let e = Key::default().set("unicornCount", "2").unwrap_err();
        assert_eq!(e.to_string(), "Unknown property: unicornCount");
    }

    #[test]
    fn test_struct_types() {
        let s = Key::default().to_create_info();

        let create_info = unsafe {
            mem::transmute::<_, &vk::VkGraphicsPipelineCreateInfo>(
                s.as_ptr()
            )
        };
        unsafe {
            assert_eq!(
                (*create_info.pDepthStencilState).sType,
                vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
            );
            assert_eq!(
                (*create_info.pColorBlendState).sType,
                vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
            );
        }
    }

    #[test]
    fn test_eq() {
        let mut key_a = Key::default();
        let mut key_b = key_a.clone();

        assert!(key_a.eq(&key_b));

        key_a.set_source(Source::VertexData);
        assert!(!key_a.eq(&key_b));
        key_b.set_source(Source::VertexData);
        assert!(key_a.eq(&key_b));

        assert!(key_a.set("depthClampEnable", "true").is_ok());
        assert!(!key_a.eq(&key_b));
        assert!(key_b.set("depthClampEnable", "true").is_ok());
        assert!(key_a.eq(&key_b));

        assert!(key_a.set("colorWriteMask", "1").is_ok());
        assert!(!key_a.eq(&key_b));
        assert!(key_b.set("colorWriteMask", "1").is_ok());
        assert!(key_a.eq(&key_b));

        assert!(key_a.set("lineWidth", "3.0").is_ok());
        assert!(!key_a.eq(&key_b));
        assert!(key_b.set("lineWidth", "3.0").is_ok());
        assert!(key_a.eq(&key_b));

        key_a.set_entrypoint(Stage::TessEval, "durberville".to_owned());
        assert!(!key_a.eq(&key_b));
        key_b.set_entrypoint(Stage::TessEval, "durberville".to_owned());
        assert!(key_a.eq(&key_b));

        // Setting the compute entry point shouldn’t affect the
        // equality for graphics pipelines
        key_a.set_entrypoint(Stage::Compute, "saysno".to_owned());
        assert!(key_a.eq(&key_b));
        key_b.set_entrypoint(Stage::Compute, "saysno".to_owned());
        assert!(key_a.eq(&key_b));

        key_a.set_pipeline_type(Type::Compute);
        assert!(!key_a.eq(&key_b));
        key_b.set_pipeline_type(Type::Compute);
        assert!(key_a.eq(&key_b));

        // Setting properties shouldn’t affect the equality for compute shaders
        assert!(key_a.set("lineWidth", "5.0").is_ok());
        assert!(key_a.eq(&key_b));
        key_a.set_source(Source::Rectangle);
        assert!(key_a.eq(&key_b));
        key_a.set_entrypoint(Stage::TessCtrl, "yes".to_owned());
        assert!(key_a.eq(&key_b));

        // Setting the compute entrypoint however should affect the equality
        key_a.set_entrypoint(Stage::Compute, "rclub".to_owned());
        assert!(!key_a.eq(&key_b));
        key_b.set_entrypoint(Stage::Compute, "rclub".to_owned());
        assert!(key_a.eq(&key_b));
    }

    #[test]
    fn test_debug() {
        let mut key = Key::default();

        assert!(key.set("depthWriteEnable", "true").is_ok());
        assert!(key.set("colorWriteMask", "1").is_ok());
        assert!(key.set("lineWidth", "42.0").is_ok());

        assert_eq!(
            format!("{:?}", key),
            "Key { \
             pipeline_type: Graphics, \
             source: Rectangle, \
             entrypoints: [None, None, None, None, None, None], \
             alphaBlendOp: 0, \
             back.compareMask: -1, \
             back.compareOp: 7, \
             back.depthFailOp: 0, \
             back.failOp: 0, \
             back.passOp: 0, \
             back.reference: 0, \
             back.writeMask: -1, \
             blendEnable: false, \
             colorBlendOp: 0, \
             colorWriteMask: 1, \
             cullMode: 0, \
             depthBiasClamp: 0, \
             depthBiasConstantFactor: 0, \
             depthBiasEnable: false, \
             depthBiasSlopeFactor: 0, \
             depthBoundsTestEnable: false, \
             depthClampEnable: false, \
             depthCompareOp: 1, \
             depthTestEnable: false, \
             depthWriteEnable: true, \
             dstAlphaBlendFactor: 7, \
             dstColorBlendFactor: 7, \
             front.compareMask: -1, \
             front.compareOp: 7, \
             front.depthFailOp: 0, \
             front.failOp: 0, \
             front.passOp: 0, \
             front.reference: 0, \
             front.writeMask: -1, \
             frontFace: 0, \
             lineWidth: 42, \
             logicOp: 15, \
             logicOpEnable: false, \
             maxDepthBounds: 0, \
             minDepthBounds: 0, \
             patchControlPoints: 0, \
             polygonMode: 0, \
             primitiveRestartEnable: false, \
             rasterizerDiscardEnable: false, \
             srcAlphaBlendFactor: 6, \
             srcColorBlendFactor: 6, \
             stencilTestEnable: false, \
             topology: 4 \
             }",
        );
    }
}

```

--------------------------------------------------------------------------------
/vkrunner/vkrunner/vbo.rs:
--------------------------------------------------------------------------------

```rust
// Copyright © 2011, 2016, 2018 Intel Corporation
// Copyright 2023 Neil Roberts
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice (including the next
// paragraph) shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// Based on piglit-vbo.cpp

//! This module adds the facility for specifying vertex data to
//! VkRunner tests using a columnar text format, for example:
//!
//! ```text
//!   0/r32g32b32_sfloat 1/r32_uint      3/int/int       4/int/int
//!   0.0 0.0 0.0        10              0               0       # comment
//!   0.0 1.0 0.0         5              1               1
//!   1.0 1.0 0.0         0              0               1
//! ```
//!
//! The format consists of a row of column headers followed by any
//! number of rows of data. Each column header has the form
//! `ATTRLOC/FORMAT` where `ATTRLOC` is the location of the vertex
//! attribute to be bound to this column and FORMAT is the name of a
//! VkFormat minus the `VK_FORMAT` prefix.
//!
//! Alternatively the column header can use something closer to the
//! Piglit format like `ATTRLOC/GL_TYPE/GLSL_TYPE`. `GL_TYPE` is the
//! GL type of data that follows (“`half`”, “`float`”, “`double`”,
//! “`byte`”, “`ubyte`”, “`short`”, “`ushort`”, “`int`” or “`uint`”),
//! `GLSL_TYPE` is the GLSL type of the data (“`int`”, “`uint`”,
//! “`float`”, “`double`”, “`ivec*`”, “`uvec*`”, “`vec*`”, “`dvec*`”).
//!
//! The data follows the column headers in space-separated form. `#`
//! can be used for comments, as in shell scripts.
//!
//! The text can be parsed either by using the [`str::parse::<Vbo>`]
//! method to parse an entire string, or by constructing a [Parser]
//! object to parse the data line-by-line.

use crate::format::{Format, Mode};
use crate::{util, parse_num, hex};
use std::fmt;
use std::cell::RefCell;

#[derive(Debug, Clone)]
pub enum Error {
    InvalidHeader(String),
    InvalidData(String),
}

/// Struct representing a blob of structured data that can be used as
/// vertex inputs to Vulkan. The Vbo can be constructed either by
/// parsing an entire string slice with the [str::parse] method or by
/// parsing the source line-by-line by constructing a [Parser] object.
#[derive(Debug)]
pub struct Vbo {
    // Description of each attribute
    attribs: Box<[Attrib]>,
    // Raw data buffer containing parsed numbers
    raw_data: Box<[u8]>,
    // Number of bytes in each row of raw_data
    stride: usize,
}

#[derive(Debug)]
pub struct Attrib {
    format: &'static Format,

    // Vertex location
    location: u32,
    // Byte offset into the vertex data of this attribute
    offset: usize,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match &self {
            Error::InvalidHeader(s) => write!(f, "{}", s),
            Error::InvalidData(s) => write!(f, "{}", s),
        }
    }
}

impl Vbo {
    #[inline]
    pub fn attribs(&self) -> &[Attrib] {
        self.attribs.as_ref()
    }

    #[inline]
    pub fn raw_data(&self) -> &[u8] {
        self.raw_data.as_ref()
    }

    #[inline]
    pub fn stride(&self) -> usize {
        self.stride
    }
}

impl Attrib {
    #[inline]
    pub fn format(&self) -> &'static Format {
        self.format
    }

    #[inline]
    pub fn location(&self) -> u32 {
        self.location
    }

    #[inline]
    pub fn offset(&self) -> usize {
        self.offset
    }
}

/// Helper struct to construct a [Vbo] by parsing data line-by-line.
/// Construct the parser with [new](Parser::new) and then add each
/// line with [parse_line](Parser::parse_line). When the vbo data is
/// complete call [into_vbo](Parser::into_vbo) to finish the parsing
/// and convert the parser into the final Vbo.
#[derive(Debug)]
pub struct Parser {
    // None if we haven’t parsed the header line yet, otherwise an
    // array of attribs
    attribs: Option<Box<[Attrib]>>,

    raw_data: RefCell<Vec<u8>>,

    stride: usize,
}

macro_rules! invalid_header {
    ($data:expr, $($message:expr),+) => {
        return Err(Error::InvalidHeader(format!($($message),+)))
    };
}

macro_rules! invalid_data {
    ($data:expr, $($message:expr),+) => {
        return Err(Error::InvalidData(format!($($message),+)))
    };
}

impl std::str::FromStr for Vbo {
    type Err = Error;

    fn from_str(s: &str) -> Result<Vbo, Error> {
        let mut data = Parser::new();

        for line in s.lines() {
            data.parse_line(line)?;
        }

        data.into_vbo()
    }
}

impl Parser {
    pub fn new() -> Parser {
        Parser {
            raw_data: RefCell::new(Vec::new()),
            stride: 0,
            attribs: None,
        }
    }

    fn trim_line(line: &str) -> &str {
        let line = line.trim_start();

        // Remove comments at the end of the line
        let line = match line.trim_start().find('#') {
            Some(end) => &line[0..end],
            None => line,
        };

        line.trim_end()
    }

    fn lookup_gl_type(&self, gl_type: &str) -> Result<(Mode, usize), Error> {
        struct GlType {
            name: &'static str,
            mode: Mode,
            bit_size: usize,
        }
        static GL_TYPES: [GlType; 9] = [
            GlType { name: "byte", mode: Mode::SINT, bit_size: 8 },
            GlType { name: "ubyte", mode: Mode::UINT, bit_size: 8 },
            GlType { name: "short", mode: Mode::SINT, bit_size: 16 },
            GlType { name: "ushort", mode: Mode::UINT, bit_size: 16 },
            GlType { name: "int", mode: Mode::SINT, bit_size: 32 },
            GlType { name: "uint", mode: Mode::UINT, bit_size: 32 },
            GlType { name: "half", mode: Mode::SFLOAT, bit_size: 16 },
            GlType { name: "float", mode: Mode::SFLOAT, bit_size: 32 },
            GlType { name: "double", mode: Mode::SFLOAT, bit_size: 64 },
        ];

        match GL_TYPES.iter().find(|t| t.name == gl_type) {
            Some(t) => Ok((t.mode, t.bit_size)),
            None => invalid_header!(self, "Unknown GL type: {}", gl_type),
        }
    }

    fn components_for_glsl_type(
        &self,
        glsl_type: &str
    ) -> Result<usize, Error> {
        if ["int", "uint", "float", "double"].contains(&glsl_type) {
            return Ok(1);
        }

        let vec_part = match glsl_type.chars().next() {
            Some('i' | 'u' | 'd') => &glsl_type[1..],
            _ => glsl_type,
        };

        if !vec_part.starts_with("vec") {
            invalid_header!(self, "Unknown GLSL type: {}", glsl_type);
        }

        match vec_part[3..].parse::<usize>() {
            Ok(n) if n >= 2 && n <= 4 => Ok(n),
            _ => invalid_header!(self, "Invalid vec size: {}", glsl_type),
        }
    }

    fn decode_type(
        &self,
        gl_type: &str,
        glsl_type: &str
    ) -> Result<&'static Format, Error> {
        let (mode, bit_size) = self.lookup_gl_type(gl_type)?;
        let n_components = self.components_for_glsl_type(glsl_type)?;

        match Format::lookup_by_details(bit_size, mode, n_components) {
            Some(f) => Ok(f),
            None => {
                invalid_header!(
                    self,
                    "Invalid type combo: {}/{}",
                    gl_type,
                    glsl_type
                );
            },
        }
    }

    fn parse_attrib(
        &mut self,
        s: &str,
        offset: usize
    ) -> Result<Attrib, Error> {
        let mut parts = s.split('/');

        let location = match parts.next().unwrap().parse::<u32>() {
            Ok(n) => n,
            Err(_) => invalid_header!(self, "Invalid attrib location in {}", s),
        };

        let format_name = match parts.next() {
            Some(n) => n,
            None => {
                invalid_header!(
                    self,
                    "Column headers must be in the form \
                     location/format. Got: {}",
                    s
                );
            },
        };

        let format = match parts.next() {
            None => match Format::lookup_by_name(format_name) {
                None => {
                    invalid_header!(
                        self,
                        "Unknown format: {}",
                        format_name
                    );
                },
                Some(f) => f,
            },
            Some(glsl_type) => {
                if let Some(_) = parts.next() {
                    invalid_header!(
                        self,
                        "Extra data at end of column header: {}",
                        s
                    );
                }

                self.decode_type(format_name, glsl_type)?
            },
        };

        Ok(Attrib {
            format,
            location,
            offset: util::align(offset, format.alignment()),
        })
    }

    fn parse_header_line(&mut self, line: &str) -> Result<(), Error> {
        let mut attribs = Vec::new();
        let mut stride = 0;
        let mut max_alignment = 1;

        for attrib in line.split_whitespace() {
            let attrib = self.parse_attrib(attrib, stride)?;

            stride = attrib.offset + attrib.format.size();

            let alignment = attrib.format.alignment();

            if alignment > max_alignment {
                max_alignment = alignment;
            }

            attribs.push(attrib);
        }

        self.attribs = Some(attribs.into_boxed_slice());
        self.stride = util::align(stride, max_alignment);

        Ok(())
    }

    #[inline]
    fn write_bytes(data: &mut [u8], bytes: &[u8]) {
        data[0..bytes.len()].copy_from_slice(bytes);
    }

    fn parse_unsigned_datum<'a>(
        &self,
        bit_size: usize,
        text: &'a str,
        data: &mut [u8],
    ) -> Result<&'a str, Error> {
        match bit_size {
            8 => match parse_num::parse_u8(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as unsigned byte")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            16 => match parse_num::parse_u16(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as unsigned short")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            32 => match parse_num::parse_u32(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as unsigned int")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            64 => match parse_num::parse_u64(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as unsigned long")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            _ => unreachable!("unexpected bit size {}", bit_size),
        }
    }

    fn parse_signed_datum<'a>(
        &self,
        bit_size: usize,
        text: &'a str,
        data: &mut [u8],
    ) -> Result<&'a str, Error> {
        match bit_size {
            8 => match parse_num::parse_i8(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as signed byte")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            16 => match parse_num::parse_i16(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as signed short")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            32 => match parse_num::parse_i32(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as signed int")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            64 => match parse_num::parse_i64(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as signed long")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            _ => unreachable!("unexpected bit size {}", bit_size),
        }
    }

    fn parse_float_datum<'a>(
        &self,
        bit_size: usize,
        text: &'a str,
        data: &mut [u8],
    ) -> Result<&'a str, Error> {
        match bit_size {
            16 => match hex::parse_half_float(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as half float")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            32 => match hex::parse_f32(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as float")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            64 => match hex::parse_f64(text) {
                Err(_) => {
                    invalid_data!(self, "Couldn’t parse as double")
                },
                Ok((v, tail)) => {
                    Parser::write_bytes(data, &v.to_ne_bytes());
                    Ok(tail)
                },
            },
            _ => unreachable!("unexpected bit size {}", bit_size),
        }
    }

    // Parse a single number (floating point or integral) from one of
    // the data rows and store it at the start of the `data` slice. If
    // successful it returns the slice in `text` after the number.
    // Otherwise it returns an error.
    fn parse_datum<'a>(
        &self,
        mode: Mode,
        bit_size: usize,
        text: &'a str,
        data: &mut [u8],
    ) -> Result<&'a str, Error> {
        match mode {
            Mode::SFLOAT => self.parse_float_datum(bit_size, text, data),
            Mode::UNORM | Mode::USCALED | Mode::UINT | Mode::SRGB => {
                self.parse_unsigned_datum(bit_size, text, data)
            },
            Mode::SNORM | Mode::SSCALED | Mode::SINT => {
                self.parse_signed_datum(bit_size, text, data)
            },
            Mode::UFLOAT => {
                // This shouldn’t happen because all of the UFLOAT
                // formats are packed so the data will be integers.
                unreachable!("unexpected UFLOAT component");
            },
        }
    }

    // Parse each component of an unpacked data format (floating point
    // or integral) from one of the data rows and store it at the
    // start of the `data` slice. If successful it returns the slice
    // in `text` after the values. Otherwise it returns an error.
    fn parse_unpacked_data<'a>(
        &self,
        format: &'static Format,
        mut text: &'a str,
        mut data: &mut [u8]
    ) -> Result<&'a str, Error> {
        for part in format.parts() {
            text = self.parse_datum(part.mode, part.bits, text, data)?;
            data = &mut data[part.bits / 8..];
        }

        Ok(text)
    }

    fn parse_data_line(&mut self, mut line: &str) -> Result<(), Error> {
        // Allocate space in raw_data for this line
        let old_length = self.raw_data.borrow().len();
        self.raw_data.borrow_mut().resize(old_length + self.stride, 0);

        for attrib in self.attribs.as_ref().unwrap().iter() {
            let data_ptr =
                &mut self.raw_data.borrow_mut()[old_length + attrib.offset..];

            match attrib.format.packed_size() {
                Some(packed_size) => {
                    line = self.parse_unsigned_datum(
                        packed_size,
                        line,
                        data_ptr
                    )?;
                },
                None => {
                    line = self.parse_unpacked_data(
                        attrib.format,
                        line,
                        data_ptr
                    )?;
                }
            }
        }

        if !line.trim_end().is_empty() {
            invalid_data!(self, "Extra data at end of line");
        }

        Ok(())
    }

    /// Add one line of data to the vbo. Returns an error if the line
    /// is invalid.
    pub fn parse_line(&mut self, line: &str) -> Result<(), Error> {
        let line = Parser::trim_line(line);

        // Ignore blank or comment-only lines */
        if line.len() <= 0 {
            return Ok(());
        }

        if self.attribs.is_none() {
            self.parse_header_line(line)?;
        } else {
            self.parse_data_line(line)?;
        }

        Ok(())
    }

    /// Call this at the end of parsing to convert the parser into the
    /// final Vbo. This can fail if the parser didn’t have enough data
    /// to complete the vbo.
    pub fn into_vbo(mut self) -> Result<Vbo, Error> {
        let attribs = match self.attribs.take() {
            None => invalid_header!(data, "Missing header line"),
            Some(a) => a,
        };

        Ok(Vbo {
            attribs,
            raw_data: self.raw_data.into_inner().into_boxed_slice(),
            stride: self.stride,
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::vk;

    #[test]
    fn test_general() {
        let source = "# position      color \n\
                      0/R32G32_SFLOAT 1/A8B8G8R8_UNORM_PACK32 \n\
                      \n\
                      # Top-left red \n\
                      -1 -1           0xff0000ff \n\
                      0  -1           0xff1200ff";

        let vbo = source.parse::<Vbo>().unwrap();

        assert_eq!(vbo.attribs().len(), 2);
        assert_eq!(vbo.stride(), 4 * 3);

        assert_eq!(
            vbo.attribs()[0].format(),
            Format::lookup_by_vk_format(vk::VK_FORMAT_R32G32_SFLOAT),
        );
        assert_eq!(vbo.attribs()[0].location(), 0);
        assert_eq!(vbo.attribs()[0].offset(), 0);
        assert_eq!(
            vbo.attribs()[1].format(),
            Format::lookup_by_vk_format(vk::VK_FORMAT_A8B8G8R8_UNORM_PACK32),
        );
        assert_eq!(vbo.attribs()[1].location(), 1);
        assert_eq!(vbo.attribs()[1].offset(), 8);

        assert_eq!(vbo.raw_data().len() % vbo.stride(), 0);

        let mut expected_data = Vec::<u8>::new();
        expected_data.extend(&(-1.0f32).to_ne_bytes());
        expected_data.extend(&(-1.0f32).to_ne_bytes());
        expected_data.extend(0xff0000ffu32.to_ne_bytes());
        expected_data.extend(0.0f32.to_ne_bytes());
        expected_data.extend(&(-1.0f32).to_ne_bytes());
        expected_data.extend(0xff1200ffu32.to_ne_bytes());
        assert_eq!(vbo.raw_data(), &expected_data);
    }

    #[test]
    fn test_no_header() {
        let err = "".parse::<Vbo>().unwrap_err();
        assert_eq!(err.to_string(), "Missing header line");
        assert!(matches!(err, Error::InvalidHeader(_)));
    }

    #[test]
    fn test_line_comment() {
        let source = "0/R32_SFLOAT\n\
                      42.0 # the next number is ignored 32.0";
        let vbo = source.parse::<Vbo>().unwrap();
        assert_eq!(vbo.raw_data(), &42.0f32.to_ne_bytes());
    }

    fn test_gl_type(name: &str, values: &str, expected_bytes: &[u8]) {
        let source = format!("0/{}/int\n{}", name, values);
        let vbo = source.parse::<Vbo>().unwrap();
        assert_eq!(vbo.raw_data(), expected_bytes);
        assert_eq!(vbo.attribs().len(), 1);
        assert_eq!(vbo.attribs()[0].location, 0);
        assert_eq!(vbo.attribs()[0].format.parts().len(), 1);
        assert_eq!(
            vbo.attribs()[0].format.parts()[0].bits,
            expected_bytes.len() * 8
        );
    }

    fn test_glsl_type(name: &str, values: &str, expected_floats: &[f32]) {
        let source = format!("1/float/{}\n{}", name, values);
        let vbo = source.parse::<Vbo>().unwrap();
        let expected_bytes = expected_floats
            .iter()
            .map(|v| v.to_ne_bytes())
            .flatten()
            .collect::<Vec<u8>>();
        assert_eq!(vbo.raw_data(), &expected_bytes);
        assert_eq!(vbo.attribs().len(), 1);
        assert_eq!(vbo.attribs()[0].location, 1);
        assert_eq!(
            vbo.attribs()[0].format.parts().len(),
            expected_floats.len()
        );
        for part in vbo.attribs()[0].format.parts() {
            assert_eq!(part.bits, 32);
        }
    }

    #[test]
    fn test_piglit_style_format() {
        test_gl_type("byte", "-42", &(-42i8).to_ne_bytes());
        test_gl_type("ubyte", "42", &[42u8]);
        test_gl_type("short", "-30000", &(-30000i16).to_ne_bytes());
        test_gl_type("ushort", "65534", &65534u16.to_ne_bytes());
        test_gl_type("int", "-70000", &(-70000i32).to_ne_bytes());
        test_gl_type("uint", "0xffffffff", &u32::MAX.to_ne_bytes());
        test_gl_type("half", "-2", &0xc000u16.to_ne_bytes());
        test_gl_type("float", "1.0000", &1.0f32.to_ne_bytes());
        test_gl_type("double", "32.0000", &32.0f64.to_ne_bytes());

        let err = "1/uverylong/int".parse::<Vbo>().unwrap_err();
        assert_eq!(&err.to_string(), "Unknown GL type: uverylong");
        assert!(matches!(err, Error::InvalidHeader(_)));

        test_glsl_type("int", "1.0", &[1.0]);
        test_glsl_type("uint", "2.0", &[2.0]);
        test_glsl_type("float", "3.0", &[3.0]);
        test_glsl_type("double", "4.0", &[4.0]);
        test_glsl_type("vec2", "1.0 2.0", &[1.0, 2.0]);
        test_glsl_type("vec3", "1.0 2.0 3.0", &[1.0, 2.0, 3.0]);
        test_glsl_type("vec4", "1.0 2.0 3.0 4.0", &[1.0, 2.0, 3.0, 4.0]);
        test_glsl_type("ivec2", "1.0 2.0", &[1.0, 2.0]);
        test_glsl_type("uvec2", "1.0 2.0", &[1.0, 2.0]);
        test_glsl_type("dvec2", "1.0 2.0", &[1.0, 2.0]);

        let err = "1/int/ituple2".parse::<Vbo>().unwrap_err();
        assert_eq!(&err.to_string(), "Unknown GLSL type: ituple2");
        assert!(matches!(err, Error::InvalidHeader(_)));

        let err = "1/int/ivecfoo".parse::<Vbo>().unwrap_err();
        assert_eq!(&err.to_string(), "Invalid vec size: ivecfoo");
        assert!(matches!(err, Error::InvalidHeader(_)));

        let err = "1/int/vec1".parse::<Vbo>().unwrap_err();
        assert_eq!(&err.to_string(), "Invalid vec size: vec1");
        assert!(matches!(err, Error::InvalidHeader(_)));

        let err = "1/int/dvec5".parse::<Vbo>().unwrap_err();
        assert_eq!(&err.to_string(), "Invalid vec size: dvec5");
        assert!(matches!(err, Error::InvalidHeader(_)));
    }

    #[test]
    fn test_bad_attrib() {
        let err = "foo/int/int".parse::<Vbo>().unwrap_err();
        assert_eq!(&err.to_string(), "Invalid attrib location in foo/int/int");
        assert!(matches!(err, Error::InvalidHeader(_)));

        assert_eq!(
            "12".parse::<Vbo>().unwrap_err().to_string(),
            "Column headers must be in the form location/format. \
             Got: 12",
        );

        assert_eq!(
            "1/R76_SFLOAT".parse::<Vbo>().unwrap_err().to_string(),
            "Unknown format: R76_SFLOAT",
        );

        assert_eq!(
            "1/int/int/more_int".parse::<Vbo>().unwrap_err().to_string(),
            "Extra data at end of column header: 1/int/int/more_int",
        );
    }

    #[test]
    fn test_alignment() {
        let source = "1/R8_UNORM 2/R64_SFLOAT 3/R8_UNORM\n \
                      1 12.0 24";
        let vbo = source.parse::<Vbo>().unwrap();
        assert_eq!(vbo.attribs().len(), 3);
        assert_eq!(vbo.attribs()[0].offset, 0);
        assert_eq!(vbo.attribs()[0].format.parts()[0].bits, 8);
        assert_eq!(vbo.attribs()[1].offset, 8);
        assert_eq!(vbo.attribs()[1].format.parts()[0].bits, 64);
        assert_eq!(vbo.attribs()[2].offset, 16);
        assert_eq!(vbo.attribs()[2].format.parts()[0].bits, 8);
        assert_eq!(vbo.stride, 24);
    }

    fn test_type(format: &str, values: &str, expected_bytes: &[u8]) {
        // Add an extra attribute so we can test it got the right offset
        let source = format!("8/{} 9/R8_UNORM\n{} 42", format, values);
        let vbo = source.parse::<Vbo>().unwrap();
        let mut full_expected_bytes = expected_bytes.to_owned();
        full_expected_bytes.push(42);
        assert!(vbo.stride() >= expected_bytes.len() + 1);
        full_expected_bytes.resize(vbo.stride(), 0);
        assert_eq!(vbo.raw_data(), full_expected_bytes);
        assert_eq!(vbo.attribs().len(), 2);
        assert_eq!(
            vbo.attribs()[0].format.parts()[0].bits,
            expected_bytes.len() * 8
        );
    }

    fn test_value_error(format: &str, error_text: &str) {
        let source = format!("0/{}\nfoo", format);
        let err = source.parse::<Vbo>().unwrap_err();
        assert_eq!(&err.to_string(), error_text);
    }

    #[test]
    fn test_parse_datum() {
        test_type("R8_UNORM", "12", &[12u8]);
        test_value_error("R8_UNORM", "Couldn’t parse as unsigned byte");
        test_type("R16_UNORM", "65000", &65000u16.to_ne_bytes());
        test_value_error("R16_USCALED", "Couldn’t parse as unsigned short");
        test_type("R32_UINT", "66000", &66000u32.to_ne_bytes());
        test_value_error("R32_UINT", "Couldn’t parse as unsigned int");
        test_type("R64_UINT", "0xffffffffffffffff", &u64::MAX.to_ne_bytes());
        test_value_error("R64_UINT", "Couldn’t parse as unsigned long");

        test_type("R8_SNORM", "-12", &(-12i8).to_ne_bytes());
        test_value_error("R8_SNORM", "Couldn’t parse as signed byte");
        test_type("R16_SNORM", "-32768", &(-32768i16).to_ne_bytes());
        test_value_error("R16_SNORM", "Couldn’t parse as signed short");
        test_type("R32_SINT", "-66000", &(-66000i32).to_ne_bytes());
        test_value_error("R32_SINT", "Couldn’t parse as signed int");
        test_type("R64_SINT", "0x7fffffffffffffff", &i64::MAX.to_ne_bytes());
        test_value_error("R64_SINT", "Couldn’t parse as signed long");

        test_type("R16_SFLOAT", "0xc000", &0xc000u16.to_ne_bytes());
        test_type("R16_SFLOAT", "-2", &0xc000u16.to_ne_bytes());
        test_value_error("R16_SFLOAT", "Couldn’t parse as half float");
        test_type("R32_SFLOAT", "-2", &(-2.0f32).to_ne_bytes());
        test_value_error("R32_SFLOAT", "Couldn’t parse as float");
        test_type("R64_SFLOAT", "-4", &(-4.0f64).to_ne_bytes());
        test_value_error("R64_SFLOAT", "Couldn’t parse as double");
    }

    #[test]
    fn test_packed_data() {
        let source = "1/B10G11R11_UFLOAT_PACK32\n\
                      0xfedcba98";
        let vbo = source.parse::<Vbo>().unwrap();
        assert_eq!(vbo.raw_data(), &0xfedcba98u32.to_ne_bytes());
    }

    #[test]
    fn test_trailing_data() {
        let source = "1/R8_UNORM\n\
                      23 25 ";
        let err = source.parse::<Vbo>().unwrap_err();
        assert_eq!(err.to_string(), "Extra data at end of line");
    }
}

```

--------------------------------------------------------------------------------
/vkrunner/vkrunner/window.rs:
--------------------------------------------------------------------------------

```rust
// vkrunner
//
// Copyright (C) 2013, 2014, 2015, 2017, 2023 Neil Roberts
// Copyright (C) 2019 Google LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice (including the next
// paragraph) shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use crate::context::Context;
use crate::window_format::WindowFormat;
use crate::format::{Format, Component};
use crate::vk;
use crate::result;
use crate::vulkan_funcs;
use crate::buffer::{self, MappedMemory, DeviceMemory, Buffer};
use std::rc::Rc;
use std::fmt;
use std::ffi::c_void;
use std::ptr;

/// Struct containing the framebuffer and all of the objects on top of
/// the [Context] needed to construct it. It also keeps a reference to
/// the Context which can be retrieved publically so this works as a
/// central object to share the essential Vulkan resources used for
/// running tests.
#[derive(Debug)]
pub struct Window {
    format: WindowFormat,

    // These are listed in the reverse order that they are created so
    // that they will be destroyed in the right order too

    need_linear_memory_invalidate: bool,
    linear_memory_stride: usize,
    linear_memory_map: MappedMemory,
    linear_memory: DeviceMemory,
    linear_buffer: Buffer,

    framebuffer: Framebuffer,

    _depth_stencil_resources: Option<DepthStencilResources>,

    _color_image_view: ImageView,
    _memory: DeviceMemory,
    color_image: Image,

    // The first render pass is used for the first render and has a
    // loadOp of DONT_CARE. The second is used for subsequent renders
    // and loads the framebuffer contents.
    render_pass: [RenderPass; 2],

    context: Rc<Context>,
}

#[derive(Debug)]
struct DepthStencilResources {
    // These are listed in the reverse order that they are created so
    // that they will be destroyed in the right order too
    image_view: ImageView,
    _memory: DeviceMemory,
    _image: Image,
}

#[derive(Debug)]
pub enum WindowError {
    IncompatibleFormat(String),
    RenderPassError,
    ImageError,
    ImageViewError,
    BufferError(buffer::Error),
    FramebufferError,
}

impl fmt::Display for WindowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            WindowError::IncompatibleFormat(s) => write!(f, "{}", s),
            WindowError::BufferError(e) => e.fmt(f),
            WindowError::RenderPassError => write!(
                f,
                "Error creating render pass",
            ),
            WindowError::ImageError => write!(
                f,
                "Error creating vkImage",
            ),
            WindowError::ImageViewError => write!(
                f,
                "Error creating vkImageView",
            ),
            WindowError::FramebufferError => write!(
                f,
                "Error creating vkFramebuffer",
            ),
        }
    }
}

impl WindowError {
    pub fn result(&self) -> result::Result {
        match self {
            WindowError::IncompatibleFormat(_) => result::Result::Skip,
            WindowError::RenderPassError => result::Result::Fail,
            WindowError::ImageError => result::Result::Fail,
            WindowError::ImageViewError => result::Result::Fail,
            WindowError::FramebufferError => result::Result::Fail,
            WindowError::BufferError(_) => result::Result::Fail,
        }
    }
}

impl From<buffer::Error> for WindowError {
    fn from(e: buffer::Error) -> WindowError {
        WindowError::BufferError(e)
    }
}

fn check_format(
    context: &Context,
    format: &Format,
    flags: vk::VkFormatFeatureFlags,
) -> bool {
    let mut format_properties: vk::VkFormatProperties = Default::default();

    unsafe {
        context.instance().vkGetPhysicalDeviceFormatProperties.unwrap()(
            context.physical_device(),
            format.vk_format,
            &mut format_properties as *mut vk::VkFormatProperties,
        );
    }

    format_properties.optimalTilingFeatures & flags == flags
}

fn check_window_format(
    context: &Context,
    window_format: &WindowFormat,
) -> Result<(), WindowError> {
    if !check_format(
        context,
        window_format.color_format,
        vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
            | vk::VK_FORMAT_FEATURE_BLIT_SRC_BIT,
    ) {
        return Err(WindowError::IncompatibleFormat(format!(
            "Format {} is not supported as a color attachment and blit source",
            window_format.color_format.name,
        )));
    }

    if let Some(depth_stencil_format) = window_format.depth_stencil_format {
        if !check_format(
            context,
            depth_stencil_format,
            vk::VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT,
        ) {
            return Err(WindowError::IncompatibleFormat(format!(
                "Format {} is not supported as a depth/stencil attachment",
                depth_stencil_format.name,
            )));
        }
    }

    Ok(())
}

#[derive(Debug)]
struct RenderPass {
    render_pass: vk::VkRenderPass,
    // Needed for the destructor
    context: Rc<Context>,
}

impl RenderPass {
    fn new(
        context: Rc<Context>,
        window_format: &WindowFormat,
        first_render: bool,
    ) -> Result<RenderPass, WindowError> {
        let has_stencil = match window_format.depth_stencil_format {
            None => false,
            Some(format) => {
                format
                    .parts()
                    .into_iter()
                    .find(|p| p.component == Component::S)
                    .is_some()
            },
        };

        let attachment_descriptions = [
            vk::VkAttachmentDescription {
                flags: 0,
                format: window_format.color_format.vk_format,
                samples: vk::VK_SAMPLE_COUNT_1_BIT,
                loadOp: if first_render {
                    vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE
                } else {
                    vk::VK_ATTACHMENT_LOAD_OP_LOAD
                },
                storeOp: vk::VK_ATTACHMENT_STORE_OP_STORE,
                stencilLoadOp: vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,
                stencilStoreOp: vk::VK_ATTACHMENT_STORE_OP_DONT_CARE,
                initialLayout: if first_render {
                    vk::VK_IMAGE_LAYOUT_UNDEFINED
                } else {
                    vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
                },
                finalLayout: vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
            },
            vk::VkAttachmentDescription {
                flags: 0,
                format: match window_format.depth_stencil_format {
                    Some(f) => f.vk_format,
                    None => 0,
                },
                samples: vk::VK_SAMPLE_COUNT_1_BIT,
                loadOp: if first_render {
                    vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE
                } else {
                    vk::VK_ATTACHMENT_LOAD_OP_LOAD
                },
                storeOp: vk::VK_ATTACHMENT_STORE_OP_STORE,
                stencilLoadOp: if first_render || !has_stencil {
                    vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE
                } else {
                    vk::VK_ATTACHMENT_LOAD_OP_LOAD
                },
                stencilStoreOp: if has_stencil {
                    vk::VK_ATTACHMENT_STORE_OP_STORE
                } else {
                    vk::VK_ATTACHMENT_STORE_OP_DONT_CARE
                },
                initialLayout: if first_render {
                    vk::VK_IMAGE_LAYOUT_UNDEFINED
                } else {
                    vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
                },
                finalLayout:
                vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
            },
        ];

        let color_attachment_reference = vk::VkAttachmentReference {
            attachment: 0,
            layout: vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
        };
        let depth_stencil_attachment_reference = vk::VkAttachmentReference {
            attachment: 1,
            layout: vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
        };

        let subpass_descriptions = [
            vk::VkSubpassDescription {
                flags: 0,
                pipelineBindPoint: vk::VK_PIPELINE_BIND_POINT_GRAPHICS,
                inputAttachmentCount: 0,
                pInputAttachments: ptr::null(),
                colorAttachmentCount: 1,
                pColorAttachments: ptr::addr_of!(color_attachment_reference),
                pResolveAttachments: ptr::null(),
                pDepthStencilAttachment:
                if window_format.depth_stencil_format.is_some() {
                    ptr::addr_of!(depth_stencil_attachment_reference)
                } else {
                    ptr::null()
                },
                preserveAttachmentCount: 0,
                pPreserveAttachments: ptr::null(),
            },
        ];

        let render_pass_create_info = vk::VkRenderPassCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
            pNext: ptr::null(),
            flags: 0,
            attachmentCount: attachment_descriptions.len() as u32
                - window_format.depth_stencil_format.is_none() as u32,
            pAttachments: ptr::addr_of!(attachment_descriptions[0]),
            subpassCount: subpass_descriptions.len() as u32,
            pSubpasses: ptr::addr_of!(subpass_descriptions[0]),
            dependencyCount: 0,
            pDependencies: ptr::null(),
        };

        let mut render_pass: vk::VkRenderPass = vk::null_handle();

        let res = unsafe {
            context.device().vkCreateRenderPass.unwrap()(
                context.vk_device(),
                ptr::addr_of!(render_pass_create_info),
                ptr::null(), // allocator
                ptr::addr_of_mut!(render_pass)
            )
        };

        if res == vk::VK_SUCCESS {
            Ok(RenderPass { render_pass, context })
        } else {
            Err(WindowError::RenderPassError)
        }
    }
}

impl Drop for RenderPass {
    fn drop(&mut self) {
        unsafe {
            self.context.device().vkDestroyRenderPass.unwrap()(
                self.context.vk_device(),
                self.render_pass,
                ptr::null(), // allocator
            );
        }
    }
}

#[derive(Debug)]
struct Image {
    image: vk::VkImage,
    // Needed for the destructor
    context: Rc<Context>,
}

impl Drop for Image {
    fn drop(&mut self) {
        unsafe {
            self.context.device().vkDestroyImage.unwrap()(
                self.context.vk_device(),
                self.image,
                ptr::null(), // allocator
            );
        }
    }
}

impl Image {
    fn new_from_create_info(
        context: Rc<Context>,
        image_create_info: &vk::VkImageCreateInfo,
    ) -> Result<Image, WindowError> {
        let mut image: vk::VkImage = vk::null_handle();

        let res = unsafe {
            context.device().vkCreateImage.unwrap()(
                context.vk_device(),
                image_create_info as *const vk::VkImageCreateInfo,
                ptr::null(), // allocator
                ptr::addr_of_mut!(image),
            )
        };

        if res == vk::VK_SUCCESS {
            Ok(Image { image, context })
        } else {
            Err(WindowError::ImageError)
        }
    }

    fn new_color(
        context: Rc<Context>,
        window_format: &WindowFormat,
    ) -> Result<Image, WindowError> {
        let image_create_info = vk::VkImageCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
            pNext: ptr::null(),
            flags: 0,
            imageType: vk::VK_IMAGE_TYPE_2D,
            format: window_format.color_format.vk_format,
            extent: vk::VkExtent3D {
                width: window_format.width as u32,
                height: window_format.height as u32,
                depth: 1,
            },
            mipLevels: 1,
            arrayLayers: 1,
            samples: vk::VK_SAMPLE_COUNT_1_BIT,
            tiling: vk::VK_IMAGE_TILING_OPTIMAL,
            usage: vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT
                | vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
            sharingMode: vk::VK_SHARING_MODE_EXCLUSIVE,
            queueFamilyIndexCount: 0,
            pQueueFamilyIndices: ptr::null(),
            initialLayout: vk::VK_IMAGE_LAYOUT_UNDEFINED,
        };

        Image::new_from_create_info(context, &image_create_info)
    }

    fn new_depth_stencil(
        context: Rc<Context>,
        format: &Format,
        width: usize,
        height: usize,
    ) -> Result<Image, WindowError> {
        let image_create_info = vk::VkImageCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
            pNext: ptr::null(),
            flags: 0,
            imageType: vk::VK_IMAGE_TYPE_2D,
            format: format.vk_format,
            extent: vk::VkExtent3D {
                width: width as u32,
                height: height as u32,
                depth: 1,
            },
            mipLevels: 1,
            arrayLayers: 1,
            samples: vk::VK_SAMPLE_COUNT_1_BIT,
            tiling: vk::VK_IMAGE_TILING_OPTIMAL,
            usage: vk::VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
            sharingMode: vk::VK_SHARING_MODE_EXCLUSIVE,
            queueFamilyIndexCount: 0,
            pQueueFamilyIndices: ptr::null(),
            initialLayout: vk::VK_IMAGE_LAYOUT_UNDEFINED,
        };

        Image::new_from_create_info(context, &image_create_info)
    }
}

#[derive(Debug)]
struct ImageView {
    image_view: vk::VkImageView,
    // Needed for the destructor
    context: Rc<Context>,
}

impl Drop for ImageView {
    fn drop(&mut self) {
        unsafe {
            self.context.device().vkDestroyImageView.unwrap()(
                self.context.vk_device(),
                self.image_view,
                ptr::null(), // allocator
            );
        }
    }
}

impl ImageView {
    fn new(
        context: Rc<Context>,
        format: &Format,
        image: vk::VkImage,
        aspect_mask: vk::VkImageAspectFlags,
    ) -> Result<ImageView, WindowError> {
        let image_view_create_info = vk::VkImageViewCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
            pNext: ptr::null(),
            flags: 0,
            image,
            viewType: vk::VK_IMAGE_VIEW_TYPE_2D,
            format: format.vk_format,
            components: vk::VkComponentMapping {
                r: vk::VK_COMPONENT_SWIZZLE_R,
                g: vk::VK_COMPONENT_SWIZZLE_G,
                b: vk::VK_COMPONENT_SWIZZLE_B,
                a: vk::VK_COMPONENT_SWIZZLE_A,
            },
            subresourceRange: vk::VkImageSubresourceRange {
                aspectMask: aspect_mask,
                baseMipLevel: 0,
                levelCount: 1,
                baseArrayLayer: 0,
                layerCount: 1
            },
        };

        let mut image_view: vk::VkImageView = vk::null_handle();

        let res = unsafe {
            context.device().vkCreateImageView.unwrap()(
                context.vk_device(),
                ptr::addr_of!(image_view_create_info),
                ptr::null(), // allocator
                ptr::addr_of_mut!(image_view),
            )
        };

        if res == vk::VK_SUCCESS {
            Ok(ImageView { image_view, context })
        } else {
            Err(WindowError::ImageViewError)
        }
    }
}

impl DepthStencilResources {
    fn new(
        context: Rc<Context>,
        format: &Format,
        width: usize,
        height: usize,
    ) -> Result<DepthStencilResources, WindowError> {
        let image = Image::new_depth_stencil(
            Rc::clone(&context),
            format,
            width,
            height,
        )?;
        let memory = DeviceMemory::new_image(
            Rc::clone(&context),
            0, // memory_type_flags
            image.image,
        )?;
        let image_view = ImageView::new(
            context,
            format,
            image.image,
            format.depth_stencil_aspect_flags(),
        )?;

        Ok(DepthStencilResources { _image: image, _memory: memory, image_view })
    }
}

#[derive(Debug)]
struct Framebuffer {
    framebuffer: vk::VkFramebuffer,
    // Needed for the destructor
    context: Rc<Context>,
}

impl Drop for Framebuffer {
    fn drop(&mut self) {
        unsafe {
            self.context.device().vkDestroyFramebuffer.unwrap()(
                self.context.vk_device(),
                self.framebuffer,
                ptr::null(), // allocator
            );
        }
    }
}

impl Framebuffer {
    fn new(
        context: Rc<Context>,
        window_format: &WindowFormat,
        render_pass: vk::VkRenderPass,
        color_image_view: vk::VkImageView,
        depth_stencil_image_view: Option<vk::VkImageView>,
    ) -> Result<Framebuffer, WindowError> {
        let mut attachments = vec![color_image_view];

        if let Some(image_view) = depth_stencil_image_view {
            attachments.push(image_view);
        }

        let framebuffer_create_info = vk::VkFramebufferCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
            pNext: ptr::null(),
            flags: 0,
            renderPass: render_pass,
            attachmentCount: attachments.len() as u32,
            pAttachments: attachments.as_ptr(),
            width: window_format.width as u32,
            height: window_format.height as u32,
            layers: 1,
        };

        let mut framebuffer: vk::VkFramebuffer = vk::null_handle();

        let res = unsafe {
            context.device().vkCreateFramebuffer.unwrap()(
                context.vk_device(),
                ptr::addr_of!(framebuffer_create_info),
                ptr::null(), // allocator
                ptr::addr_of_mut!(framebuffer),
            )
        };

        if res == vk::VK_SUCCESS {
            Ok(Framebuffer { framebuffer, context })
        } else {
            Err(WindowError::FramebufferError)
        }
    }
}

fn need_linear_memory_invalidate(
    context: &Context,
    linear_memory_type: u32,
) -> bool {
    context
        .memory_properties()
        .memoryTypes[linear_memory_type as usize]
        .propertyFlags
        & vk::VK_MEMORY_PROPERTY_HOST_COHERENT_BIT == 0
}

impl Window {
    pub fn new(
        context: Rc<Context>,
        format: &WindowFormat,
    ) -> Result<Window, WindowError> {
        check_window_format(&context, format)?;

        let render_pass = [
            RenderPass::new(Rc::clone(&context), format, true)?,
            RenderPass::new(Rc::clone(&context), format, false)?,
        ];

        let color_image = Image::new_color(Rc::clone(&context), format)?;
        let memory = DeviceMemory::new_image(
            Rc::clone(&context),
            0, // memory_type_flags
            color_image.image,
        )?;

        let color_image_view = ImageView::new(
            Rc::clone(&context),
            format.color_format,
            color_image.image,
            vk::VK_IMAGE_ASPECT_COLOR_BIT,
        )?;

        let depth_stencil_resources = match &format.depth_stencil_format {
            Some(depth_stencil_format) => Some(DepthStencilResources::new(
                Rc::clone(&context),
                depth_stencil_format,
                format.width,
                format.height,
            )?),
            None => None,
        };

        let framebuffer = Framebuffer::new(
            Rc::clone(&context),
            format,
            render_pass[0].render_pass,
            color_image_view.image_view,
            depth_stencil_resources.as_ref().map(|r| r.image_view.image_view),
        )?;

        let linear_memory_stride = format.color_format.size() * format.width;

        let linear_buffer = Buffer::new(
            Rc::clone(&context),
            linear_memory_stride * format.height,
            vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT,
        )?;
        let linear_memory = DeviceMemory::new_buffer(
            Rc::clone(&context),
            vk::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
            linear_buffer.buffer,
        )?;

        let linear_memory_map = MappedMemory::new(
            Rc::clone(&context),
            linear_memory.memory,
        )?;

        Ok(Window {
            format: format.clone(),

            need_linear_memory_invalidate: need_linear_memory_invalidate(
                &context,
                linear_memory.memory_type_index,
            ),
            linear_memory_stride,
            linear_memory_map,
            linear_memory,
            linear_buffer,

            framebuffer,

            _depth_stencil_resources: depth_stencil_resources,

            _color_image_view: color_image_view,
            _memory: memory,
            color_image,

            render_pass,

            context,
        })
    }

    /// Retrieve the [Context] that the Window was created with.
    pub fn context(&self) -> &Rc<Context> {
        &self.context
    }

    /// Retrieve the [WindowFormat] that the Window was created for.
    pub fn format(&self) -> &WindowFormat {
        &self.format
    }

    /// Retrieve the [Device](vulkan_funcs::Device) that the
    /// Window was created from. This is just a convenience function
    /// for getting the device from the [Context].
    pub fn device(&self) -> &vulkan_funcs::Device {
        self.context.device()
    }

    /// Retrieve the [VkDevice](vk::VkDevice) that the window was
    /// created from. This is just a convenience function for getting
    /// the device from the [Context].
    pub fn vk_device(&self) -> vk::VkDevice {
        self.context.vk_device()
    }

    /// Get the two [VkRenderPasses](vk::VkRenderPass) that were
    /// created for the window. The first render pass should be used
    /// for the first render and the second one should be used for all
    /// subsequent renders.
    pub fn render_passes(&self) -> [vk::VkRenderPass; 2] {
        [
            self.render_pass[0].render_pass,
            self.render_pass[1].render_pass,
        ]
    }

    /// Get the vulkan handle to the linear memory that can be used to
    /// copy framebuffer results into in order to inspect it.
    pub fn linear_memory(&self) -> vk::VkDeviceMemory {
        self.linear_memory.memory
    }

    /// Get the `VkBuffer` that represents the linear memory buffer
    pub fn linear_buffer(&self) -> vk::VkBuffer {
        self.linear_buffer.buffer
    }

    /// Get the pointer to the mapping that the Window holds to
    /// examine the linear memory buffer.
    pub fn linear_memory_map(&self) -> *const c_void {
        self.linear_memory_map.pointer
    }

    /// Get the stride of the linear memory buffer
    pub fn linear_memory_stride(&self) -> usize {
        self.linear_memory_stride
    }

    /// Return whether the mapping for the linear memory buffer owned
    /// by the Window needs to be invalidated with
    /// `vkInvalidateMappedMemoryRanges` before it can be read after
    /// it has been modified. This is will be true if the memory type
    /// used for the linear memory buffer doesn’t have the
    /// `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set.
    pub fn need_linear_memory_invalidate(&self) -> bool {
        self.need_linear_memory_invalidate
    }

    /// Return the `VkFramebuffer` that was created for the window.
    pub fn framebuffer(&self) -> vk::VkFramebuffer {
        self.framebuffer.framebuffer
    }

    /// Return the `VkImage` that was created for the color buffer of
    /// the window.
    pub fn color_image(&self) -> vk::VkImage {
        self.color_image.image
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::fake_vulkan::{FakeVulkan, HandleType};
    use crate::requirements::Requirements;

    fn base_fake_vulkan() -> Box<FakeVulkan> {
        let mut fake_vulkan = FakeVulkan::new();

        fake_vulkan.physical_devices.push(Default::default());
        fake_vulkan.physical_devices[0].format_properties.insert(
            vk::VK_FORMAT_B8G8R8A8_UNORM,
            vk::VkFormatProperties {
                linearTilingFeatures: 0,
                optimalTilingFeatures:
                vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
                    | vk::VK_FORMAT_FEATURE_BLIT_SRC_BIT,
                bufferFeatures: 0,
            },
        );

        fake_vulkan.physical_devices[0].format_properties.insert(
            vk::VK_FORMAT_D24_UNORM_S8_UINT,
            vk::VkFormatProperties {
                linearTilingFeatures: 0,
                optimalTilingFeatures:
                vk::VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT,
                bufferFeatures: 0,
            },
        );

        let memory_properties =
            &mut fake_vulkan.physical_devices[0].memory_properties;
        memory_properties.memoryTypes[0].propertyFlags =
            vk::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
        memory_properties.memoryTypeCount = 1;
        fake_vulkan.memory_requirements.memoryTypeBits = 1;

        fake_vulkan
    }

    fn get_render_pass_attachments(
        fake_vulkan: &mut FakeVulkan,
        render_pass: vk::VkRenderPass,
    ) -> &[vk::VkAttachmentDescription] {
        match &fake_vulkan.get_handle(render_pass).data {
            HandleType::RenderPass { attachments } => attachments.as_slice(),
            _ => unreachable!("mismatched handle type"),
        }
    }

    struct TestResources {
        // These two need to be dropped in this order
        context: Rc<Context>,
        fake_vulkan: Box<FakeVulkan>,
    }

    fn test_simple_function_error(
        function_name: &str,
        expected_error_string: &str,
    ) -> TestResources {
        let mut fake_vulkan = base_fake_vulkan();
        fake_vulkan.queue_result(
            function_name.to_string(),
            vk::VK_ERROR_UNKNOWN
        );

        fake_vulkan.set_override();
        let context = Rc::new(Context::new(
            &Requirements::new(),
            None
        ).unwrap());

        let err = Window::new(
            Rc::clone(&context),
            &Default::default(), // format
        ).unwrap_err();

        assert_eq!(&err.to_string(), expected_error_string);
        assert_eq!(err.result(), result::Result::Fail);

        TestResources { context, fake_vulkan }
    }

    #[test]
    fn basic() {
        let mut fake_vulkan = base_fake_vulkan();

        fake_vulkan.set_override();
        let context = Rc::new(Context::new(
            &Requirements::new(),
            None
        ).unwrap());

        let window = Window::new(
            Rc::clone(&context),
            &Default::default() // format
        ).unwrap();

        assert!(Rc::ptr_eq(window.context(), &context));
        assert_eq!(
            window.format().color_format.vk_format,
            vk::VK_FORMAT_B8G8R8A8_UNORM
        );
        assert_eq!(
            window.device() as *const vulkan_funcs::Device,
            context.device() as *const vulkan_funcs::Device,
        );
        assert_eq!(window.vk_device(), context.vk_device());
        assert_ne!(window.render_passes()[0], window.render_passes()[1]);
        assert!(window.linear_memory() != vk::null_handle());
        assert!(window.linear_buffer() != vk::null_handle());
        assert!(!window.linear_memory_map().is_null());
        assert_eq!(
            window.linear_memory_stride(),
            window.format().color_format.size()
                * window.format().width
        );
        assert!(window.need_linear_memory_invalidate());
        assert!(window.framebuffer() != vk::null_handle());
        assert!(window.color_image() != vk::null_handle());

        let rp = get_render_pass_attachments(
            fake_vulkan.as_mut(),
            window.render_passes()[0]
        );
        assert_eq!(rp.len(), 1);
        assert_eq!(rp[0].loadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE);
        assert_eq!(rp[0].initialLayout, vk::VK_IMAGE_LAYOUT_UNDEFINED);
        assert_eq!(rp[0].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE);

        let rp = get_render_pass_attachments(
            fake_vulkan.as_mut(),
            window.render_passes()[1]
        );
        assert_eq!(rp.len(), 1);
        assert_eq!(rp[0].loadOp, vk::VK_ATTACHMENT_LOAD_OP_LOAD);
        assert_eq!(
            rp[0].initialLayout,
            vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
        );
        assert_eq!(rp[0].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE);
    }

    #[test]
    fn depth_stencil() {
        let mut fake_vulkan = base_fake_vulkan();

        let mut depth_format = WindowFormat::default();

        depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format(
            vk::VK_FORMAT_D24_UNORM_S8_UINT,
        ));

        fake_vulkan.set_override();
        let context = Rc::new(Context::new(
            &Requirements::new(),
            None
        ).unwrap());

        let window = Window::new(
            Rc::clone(&context),
            &depth_format,
        ).unwrap();

        let rp = get_render_pass_attachments(
            fake_vulkan.as_mut(),
            window.render_passes()[0]
        );
        assert_eq!(rp.len(), 2);
        assert_eq!(rp[0].loadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE);
        assert_eq!(rp[0].initialLayout, vk::VK_IMAGE_LAYOUT_UNDEFINED);
        assert_eq!(rp[0].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE);
        assert_eq!(rp[1].loadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE);
        assert_eq!(rp[1].initialLayout, vk::VK_IMAGE_LAYOUT_UNDEFINED);
        assert_eq!(rp[1].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE);
        assert_eq!(rp[1].stencilStoreOp, vk::VK_ATTACHMENT_STORE_OP_STORE);

        let rp = get_render_pass_attachments(
            fake_vulkan.as_mut(),
            window.render_passes()[1]
        );
        assert_eq!(rp.len(), 2);
        assert_eq!(rp[0].loadOp, vk::VK_ATTACHMENT_LOAD_OP_LOAD);
        assert_eq!(
            rp[0].initialLayout,
            vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
        );
        assert_eq!(rp[0].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE);
        assert_eq!(rp[1].loadOp, vk::VK_ATTACHMENT_LOAD_OP_LOAD);
        assert_eq!(
            rp[1].initialLayout,
            vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
        );
        assert_eq!(rp[1].stencilLoadOp, vk::VK_ATTACHMENT_LOAD_OP_LOAD);
        assert_eq!(rp[1].stencilStoreOp, vk::VK_ATTACHMENT_STORE_OP_STORE);
    }

    #[test]
    fn incompatible_format() {
        let mut fake_vulkan = base_fake_vulkan();

        fake_vulkan
            .physical_devices[0]
            .format_properties
            .insert(
                vk::VK_FORMAT_R8_UNORM,
                vk::VkFormatProperties {
                    linearTilingFeatures: 0,
                    optimalTilingFeatures: 0,
                    bufferFeatures: 0,
                },
            );

        let mut depth_format = WindowFormat::default();

        depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format(
            vk::VK_FORMAT_R8_UNORM,
        ));

        fake_vulkan.set_override();
        let context = Rc::new(Context::new(
            &Requirements::new(),
            None
        ).unwrap());

        let err = Window::new(
            Rc::clone(&context),
            &depth_format,
        ).unwrap_err();

        assert_eq!(
            &err.to_string(),
            "Format R8_UNORM is not supported as a depth/stencil attachment",
        );
        assert_eq!(err.result(), result::Result::Skip);

        fake_vulkan
            .physical_devices[0]
            .format_properties
            .get_mut(&vk::VK_FORMAT_B8G8R8A8_UNORM)
            .unwrap()
            .optimalTilingFeatures
            &= !vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;

        fake_vulkan.set_override();
        let context = Rc::new(Context::new(
            &Requirements::new(),
            None
        ).unwrap());

        let err = Window::new(
            Rc::clone(&context),
            &Default::default(), // format
        ).unwrap_err();

        assert_eq!(
            &err.to_string(),
            "Format B8G8R8A8_UNORM is not supported as a color attachment \
             and blit source",
        );
        assert_eq!(err.result(), result::Result::Skip);
    }

    #[test]
    fn render_pass_error() {
        let mut res = test_simple_function_error(
            "vkCreateRenderPass",
            "Error creating render pass"
        );

        // Try making the second render pass fail too
        res.fake_vulkan.queue_result(
            "vkCreateRenderPass".to_string(),
            vk::VK_SUCCESS
        );
        res.fake_vulkan.queue_result(
            "vkCreateRenderPass".to_string(),
            vk::VK_ERROR_UNKNOWN
        );

        let err = Window::new(
            Rc::clone(&res.context),
            &Default::default(), // format
        ).unwrap_err();

        assert_eq!(&err.to_string(), "Error creating render pass");
    }

    #[test]
    fn image_error() {
        let mut res = test_simple_function_error(
            "vkCreateImage",
            "Error creating vkImage"
        );

        // Also try the depth/stencil image
        res.fake_vulkan.queue_result(
            "vkCreateImage".to_string(),
            vk::VK_SUCCESS
        );
        res.fake_vulkan.queue_result(
            "vkCreateImage".to_string(),
            vk::VK_ERROR_UNKNOWN
        );

        let mut depth_format = WindowFormat::default();

        depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format(
            vk::VK_FORMAT_D24_UNORM_S8_UINT,
        ));

        let err = Window::new(
            Rc::clone(&res.context),
            &depth_format,
        ).unwrap_err();

        assert_eq!(&err.to_string(), "Error creating vkImage");
    }

    #[test]
    fn image_view_error() {
        let mut res = test_simple_function_error(
            "vkCreateImageView",
            "Error creating vkImageView"
        );

        // Also try the depth/stencil image
        res.fake_vulkan.queue_result(
            "vkCreateImageView".to_string(),
            vk::VK_SUCCESS
        );
        res.fake_vulkan.queue_result(
            "vkCreateImageView".to_string(),
            vk::VK_ERROR_UNKNOWN
        );

        let mut depth_format = WindowFormat::default();

        depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format(
            vk::VK_FORMAT_D24_UNORM_S8_UINT,
        ));

        let err = Window::new(
            Rc::clone(&res.context),
            &depth_format,
        ).unwrap_err();

        assert_eq!(&err.to_string(), "Error creating vkImageView");
    }

    #[test]
    fn allocate_store_error() {
        let mut res = test_simple_function_error(
            "vkAllocateMemory",
            "vkAllocateMemory failed"
        );

        // Make the second allocate fail to so that the depth/stencil
        // image will fail
        res.fake_vulkan.queue_result(
            "vkAllocateMemory".to_string(),
            vk::VK_SUCCESS,
        );
        res.fake_vulkan.queue_result(
            "vkAllocateMemory".to_string(),
            vk::VK_ERROR_UNKNOWN,
        );

        let mut depth_format = WindowFormat::default();

        depth_format.depth_stencil_format = Some(Format::lookup_by_vk_format(
            vk::VK_FORMAT_D24_UNORM_S8_UINT,
        ));

        let err = Window::new(
            Rc::clone(&res.context),
            &depth_format,
        ).unwrap_err();

        assert_eq!(&err.to_string(), "vkAllocateMemory failed");
        assert_eq!(err.result(), result::Result::Fail);

        // Remove the host visible bit so the linear memory won’t allocate
        let memory_properties =
            &mut res.fake_vulkan.physical_devices[0].memory_properties;
        memory_properties.memoryTypes[0].propertyFlags &=
            !vk::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;

        res.fake_vulkan.set_override();
        let context = Rc::new(Context::new(
            &Requirements::new(),
            None
        ).unwrap());

        let err = Window::new(
            Rc::clone(&context),
            &Default::default(), // format
        ).unwrap_err();

        assert_eq!(
            &err.to_string(),
            "Couldn’t find suitable memory type to allocate buffer",
        );
        assert_eq!(err.result(), result::Result::Fail);
    }

    #[test]
    fn map_memory_error() {
        test_simple_function_error("vkMapMemory", "vkMapMemory failed");
    }

    #[test]
    fn buffer_error() {
        test_simple_function_error("vkCreateBuffer", "Error creating vkBuffer");
    }

    #[test]
    fn framebuffer_error() {
        test_simple_function_error(
            "vkCreateFramebuffer",
            "Error creating vkFramebuffer",
        );
    }

    #[test]
    fn rectangular_framebuffer() {
        let fake_vulkan = base_fake_vulkan();

        fake_vulkan.set_override();
        let context = Rc::new(Context::new(
            &Requirements::new(),
            None
        ).unwrap());

        let mut format = WindowFormat::default();

        format.width = 200;
        format.height = 100;

        let window = Window::new(
            Rc::clone(&context),
            &format,
        ).unwrap();

        assert_eq!(
            window.linear_memory_stride(),
            200 * format.color_format.size()
        );

        let HandleType::Memory { ref contents, .. } =
            fake_vulkan.get_handle(window.linear_memory()).data
        else { unreachable!("Mismatched handle"); };

        assert_eq!(
            contents.len(),
            window.linear_memory_stride() * 100,
        );
    }
}

```

--------------------------------------------------------------------------------
/vkrunner/vkrunner/context.rs:
--------------------------------------------------------------------------------

```rust
// vkrunner
//
// Copyright (C) 2013, 2014, 2015, 2017, 2023 Neil Roberts
// Copyright (C) 2018 Intel Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice (including the next
// paragraph) shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use crate::vk;
use crate::requirements::{self, Requirements};
use crate::vulkan_funcs;
use crate::util::env_var_as_boolean;
use crate::result;
use std::ffi::{c_char, c_void, CStr};
use std::fmt;
use std::ptr;

/// Struct containing the VkDevice and accessories such as a
/// VkCommandPool, VkQueue and the function pointers from
/// [vulkan_funcs].
#[derive(Debug)]
pub struct Context {
    // These three need to be in this order so that they will be
    // dropped in the correct order.
    device_pair: DevicePair,
    instance_pair: InstancePair,
    // This isn’t read anywhere but we want to keep it alive for the
    // duration of the Context so that the library won’t be unloaded.
    _vklib: Option<Box<vulkan_funcs::Library>>,

    physical_device: vk::VkPhysicalDevice,

    memory_properties: vk::VkPhysicalDeviceMemoryProperties,

    command_pool: vk::VkCommandPool,
    command_buffer: vk::VkCommandBuffer,
    fence: vk::VkFence,

    queue: vk::VkQueue,

    always_flush_memory: bool,
}

/// Error returned by [Context::new]
#[derive(Debug)]
pub enum Error {
    FuncsError(vulkan_funcs::Error),
    RequirementsError(requirements::Error),
    EnumerateInstanceExtensionPropertiesFailed,
    MissingInstanceExtension(String),
    IncompatibleDriver,
    CreateInstanceFailed,
    CreateDeviceFailed,
    CreateCommandPoolFailed,
    CommandBufferAllocateFailed,
    CreateFenceFailed,
    EnumeratePhysicalDevicesFailed,
    NoDevices,
    /// None of the drivers succeeded and the vector is an error for
    /// each possible driver.
    DeviceErrors(Vec<Error>),
    NoGraphicsQueueFamily,
    InvalidDeviceId { device_id: usize, n_devices: u32 },
}

impl From<vulkan_funcs::Error> for Error {
    fn from(error: vulkan_funcs::Error) -> Error {
        Error::FuncsError(error)
    }
}

impl From<requirements::Error> for Error {
    fn from(error: requirements::Error) -> Error {
        Error::RequirementsError(error)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::FuncsError(e) => e.fmt(f),
            Error::RequirementsError(e) => e.fmt(f),
            Error::EnumerateInstanceExtensionPropertiesFailed => {
                write!(f, "vkEnumerateInstanceExtensionProperties failed")
            },
            Error::MissingInstanceExtension(s) => {
                write!(f, "Missing instance extension: {}", s)
            },
            Error::IncompatibleDriver => {
                write!(
                    f,
                    "vkCreateInstance reported VK_ERROR_INCOMPATIBLE_DRIVER",
                )
            },
            Error::CreateInstanceFailed => write!(f, "vkCreateInstance failed"),
            Error::CreateDeviceFailed => write!(f, "vkCreateDevice failed"),
            Error::CreateCommandPoolFailed => {
                write!(f, "vkCreateCommandPool failed")
            },
            Error::CommandBufferAllocateFailed => {
                write!(f, "vkCommandBufferAllocate failed")
            },
            Error::CreateFenceFailed => {
                write!(f, "vkCreateFence failed")
            },
            Error::EnumeratePhysicalDevicesFailed => {
                write!(f, "vkEnumeratePhysicalDevices failed")
            },
            Error::NoDevices => {
                write!(f, "The Vulkan instance reported zero drivers")
            },
            Error::DeviceErrors(errors) => {
                for (i, error) in errors.iter().enumerate() {
                    if i > 0 {
                        writeln!(f)?;
                    }

                    write!(f, "{}: {}", i, error)?;
                }
                Ok(())
            },
            Error::NoGraphicsQueueFamily => {
                write!(
                    f,
                    "Device has no graphics queue family"
                )
            },
            &Error::InvalidDeviceId { device_id, n_devices } => {
                write!(
                    f,
                    "Device {} was selected but the Vulkan instance only \
                     reported {} device{}.",
                    device_id.saturating_add(1),
                    n_devices,
                    if n_devices == 1 { "" } else { "s" },
                )
            },
        }
    }
}

impl Error {
    pub fn result(&self) -> result::Result {
        match self {
            Error::FuncsError(_) => result::Result::Fail,
            Error::RequirementsError(e) => e.result(),
            Error::EnumerateInstanceExtensionPropertiesFailed => {
                result::Result::Fail
            },
            Error::MissingInstanceExtension(_) => result::Result::Skip,
            Error::IncompatibleDriver => result::Result::Skip,
            Error::CreateInstanceFailed => result::Result::Fail,
            Error::CreateDeviceFailed => result::Result::Fail,
            Error::CreateCommandPoolFailed => result::Result::Fail,
            Error::CommandBufferAllocateFailed => result::Result::Fail,
            Error::CreateFenceFailed => result::Result::Fail,
            Error::EnumeratePhysicalDevicesFailed => result::Result::Fail,
            Error::NoDevices => result::Result::Skip,
            Error::DeviceErrors(errors) => {
                // If all of the errors were Fail then we’ll return
                // failure overall, otherwise we’ll return Skip.
                if errors.iter().all(|e| e.result() == result::Result::Fail) {
                    result::Result::Fail
                } else {
                    result::Result::Skip
                }
            },
            Error::NoGraphicsQueueFamily => result::Result::Skip,
            Error::InvalidDeviceId { .. } => result::Result::Fail,
        }
    }
}

struct GetInstanceProcClosure<'a> {
    vklib: &'a vulkan_funcs::Library,
    vk_instance: vk::VkInstance,
}

extern "C" fn get_instance_proc(
    func_name: *const c_char,
    user_data: *const c_void,
) -> *const c_void {
    unsafe {
        let data: &GetInstanceProcClosure = &*user_data.cast();
        let vklib = data.vklib;
        std::mem::transmute(
            vklib.vkGetInstanceProcAddr.unwrap()(
                data.vk_instance,
                func_name.cast()
            )
        )
    }
}

// ext is a zero-terminated byte array, as one of the constants in
// vulkan_bindings.
fn check_instance_extension(
    vklib: &vulkan_funcs::Library,
    ext: &[u8],
) -> Result<(), Error> {
    let mut count: u32 = 0;

    let res = unsafe {
        vklib.vkEnumerateInstanceExtensionProperties.unwrap()(
            ptr::null(), // layerName
            ptr::addr_of_mut!(count),
            ptr::null_mut(), // props
        )
    };

    if res != vk::VK_SUCCESS {
        return Err(Error::EnumerateInstanceExtensionPropertiesFailed);
    }

    let mut props = Vec::<vk::VkExtensionProperties>::new();
    props.resize_with(count as usize, Default::default);

    let res = unsafe {
        vklib.vkEnumerateInstanceExtensionProperties.unwrap()(
            ptr::null(), // layerName
            ptr::addr_of_mut!(count),
            props.as_mut_ptr(),
        )
    };

    if res != vk::VK_SUCCESS {
        return Err(Error::EnumerateInstanceExtensionPropertiesFailed);
    }

    'prop_loop: for prop in props.iter() {
        assert!(ext.len() <= prop.extensionName.len());

        // Can’t just compare the slices because one is i8 and the
        // other is u8. ext should include the null terminator so this
        // will check for null too.

        for (i, &c) in ext.iter().enumerate() {
            if c as c_char != prop.extensionName[i] {
                continue 'prop_loop;
            }
        }

        // If we made it here then it’s the same extension name
        return Ok(());
    }

    let ext_name = CStr::from_bytes_with_nul(ext).unwrap();

    Err(Error::MissingInstanceExtension(ext_name.to_string_lossy().to_string()))
}

// Struct that contains a VkInstance and its function pointers from
// vulkan_funcs. This is needed so that it can have a drop
// implementation that uses the function pointers to free the
// instance.
#[derive(Debug)]
struct InstancePair {
    is_external: bool,
    vk_instance: vk::VkInstance,
    vkinst: Box<vulkan_funcs::Instance>,
}

impl InstancePair {
    fn new(
        vklib: &vulkan_funcs::Library,
        requirements: &Requirements
    ) -> Result<InstancePair, Error> {
        let application_info = vk::VkApplicationInfo {
            sType: vk::VK_STRUCTURE_TYPE_APPLICATION_INFO,
            pNext: ptr::null(),
            pApplicationName: "vkrunner\0".as_ptr().cast(),
            applicationVersion: 0,
            pEngineName: ptr::null(),
            engineVersion: 0,
            apiVersion: requirements.version(),
        };

        let mut instance_create_info = vk::VkInstanceCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
            pNext: ptr::null(),
            flags: 0,
            pApplicationInfo: ptr::addr_of!(application_info),
            enabledLayerCount: 0,
            ppEnabledLayerNames: ptr::null(),
            enabledExtensionCount: 0,
            ppEnabledExtensionNames: ptr::null(),
        };

        let ext = vk::VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
        let structures = requirements.c_structures();

        let mut enabled_extensions = Vec::<*const c_char>::new();

        if structures.is_some() {
            check_instance_extension(vklib, ext)?;
            enabled_extensions.push(ext.as_ptr().cast());
        }

        instance_create_info.enabledExtensionCount =
            enabled_extensions.len() as u32;
        instance_create_info.ppEnabledExtensionNames =
            enabled_extensions.as_ptr();

        // VkInstance is a dispatchable handle so it is always a
        // pointer and we can’t use vk::null_handle.
        let mut vk_instance = ptr::null_mut();

        let res = unsafe {
            vklib.vkCreateInstance.unwrap()(
                ptr::addr_of!(instance_create_info),
                ptr::null(), // allocator
                ptr::addr_of_mut!(vk_instance),
            )
        };

        match res {
            vk::VK_ERROR_INCOMPATIBLE_DRIVER => Err(Error::IncompatibleDriver),
            vk::VK_SUCCESS => {
                let vkinst = unsafe {
                    let closure = GetInstanceProcClosure {
                        vklib,
                        vk_instance,
                    };

                    Box::new(vulkan_funcs::Instance::new(
                        get_instance_proc,
                        ptr::addr_of!(closure).cast(),
                    ))
                };

                Ok(InstancePair {
                    is_external: false,
                    vk_instance,
                    vkinst,
                })
            },
            _ => Err(Error::CreateInstanceFailed),
        }
    }

    fn new_external(
        get_instance_proc_cb: vulkan_funcs::GetInstanceProcFunc,
        user_data: *const c_void,
    ) -> InstancePair {
        InstancePair {
            is_external: true,
            vk_instance: ptr::null_mut(),
            vkinst: unsafe {
                Box::new(vulkan_funcs::Instance::new(
                    get_instance_proc_cb,
                    user_data,
                ))
            },
        }
    }
}

impl Drop for InstancePair {
    fn drop(&mut self) {
        if !self.is_external {
            unsafe {
                self.vkinst.vkDestroyInstance.unwrap()(
                    self.vk_instance,
                    ptr::null(), // allocator
                );
            }
        }
    }
}

// Struct that contains a VkDevice and its function pointers from
// vulkan_funcs. This is needed so that it can have a drop
// implementation that uses the function pointers to free the device.
#[derive(Debug)]
struct DevicePair {
    is_external: bool,
    device: vk::VkDevice,
    vkdev: Box<vulkan_funcs::Device>,
}

impl DevicePair {
    fn new(
        instance_pair: &InstancePair,
        requirements: &Requirements,
        physical_device: vk::VkPhysicalDevice,
        queue_family: u32,
    ) -> Result<DevicePair, Error> {
        let structures = requirements.c_structures();
        let base_features = requirements.c_base_features();
        let extensions = requirements.c_extensions();

        let queue_priorities = [1.0f32];

        let queue_create_info = vk::VkDeviceQueueCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
            pNext: ptr::null(),
            flags: 0,
            queueFamilyIndex: queue_family,
            queueCount: 1,
            pQueuePriorities: queue_priorities.as_ptr(),
        };

        let device_create_info = vk::VkDeviceCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
            pNext: structures
                .and_then(|s| Some(s.as_ptr().cast()))
                .unwrap_or(ptr::null()),
            flags: 0,
            queueCreateInfoCount: 1,
            pQueueCreateInfos: ptr::addr_of!(queue_create_info),
            enabledLayerCount: 0,
            ppEnabledLayerNames: ptr::null(),
            enabledExtensionCount: extensions.len() as u32,
            ppEnabledExtensionNames: if extensions.len() > 0 {
                extensions.as_ptr().cast()
            } else {
                ptr::null()
            },
            pEnabledFeatures: base_features,
        };

        // VkDevice is a dispatchable handle so it is always a pointer
        // and we can’t use vk::null_handle.
        let mut device = ptr::null_mut();

        let res = unsafe {
            instance_pair.vkinst.vkCreateDevice.unwrap()(
                physical_device,
                ptr::addr_of!(device_create_info),
                ptr::null(), // allocator
                ptr::addr_of_mut!(device),
            )
        };

        if res == vk::VK_SUCCESS {
            Ok(DevicePair {
                is_external: false,
                device,
                vkdev: Box::new(vulkan_funcs::Device::new(
                    &instance_pair.vkinst,
                    device,
                )),
            })
        } else {
            Err(Error::CreateDeviceFailed)
        }
    }

    fn new_external(
        instance_pair: &InstancePair,
        device: vk::VkDevice,
    ) -> DevicePair {
        DevicePair {
            is_external: true,
            device,
            vkdev: Box::new(vulkan_funcs::Device::new(
                &instance_pair.vkinst,
                device,
            )),
        }
    }
}

impl Drop for DevicePair {
    fn drop(&mut self) {
        if !self.is_external {
            unsafe {
                self.vkdev.vkDestroyDevice.unwrap()(
                    self.device,
                    ptr::null(), // allocator
                );
            }
        }
    }
}

fn free_resources(
    device_pair: &DevicePair,
    mut command_pool: Option<vk::VkCommandPool>,
    mut command_buffer: Option<vk::VkCommandBuffer>,
    mut fence: Option<vk::VkFence>,
) {
    unsafe {
        if let Some(fence) = fence.take() {
            device_pair.vkdev.vkDestroyFence.unwrap()(
                device_pair.device,
                fence,
                ptr::null() // allocator
            );
        }

        if let Some(mut command_buffer) = command_buffer.take() {
            device_pair.vkdev.vkFreeCommandBuffers.unwrap()(
                device_pair.device,
                command_pool.unwrap(),
                1, // commandBufferCount
                ptr::addr_of_mut!(command_buffer),
            );
        }

        if let Some(command_pool) = command_pool.take() {
            device_pair.vkdev.vkDestroyCommandPool.unwrap()(
                device_pair.device,
                command_pool,
                ptr::null() // allocator
            );
        }
    }
}

// This is a helper struct for creating the rest of the context
// resources. Its members are optional so that it can handle
// destruction if an error occurs between creating one of the
// resources.
struct DeviceResources<'a> {
    device_pair: &'a DevicePair,

    command_pool: Option<vk::VkCommandPool>,
    command_buffer: Option<vk::VkCommandBuffer>,
    fence: Option<vk::VkFence>,
}

impl<'a> DeviceResources<'a> {
    fn create_command_pool(
        &mut self,
        queue_family: u32,
    ) -> Result<(), Error> {
        let command_pool_create_info = vk::VkCommandPoolCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
            pNext: ptr::null(),
            flags: vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
            queueFamilyIndex: queue_family,
        };
        let mut command_pool = vk::null_handle();
        let res = unsafe {
            self.device_pair.vkdev.vkCreateCommandPool.unwrap()(
                self.device_pair.device,
                ptr::addr_of!(command_pool_create_info),
                ptr::null(), // allocator
                ptr::addr_of_mut!(command_pool),
            )
        };

        if res != vk::VK_SUCCESS {
            return Err(Error::CreateCommandPoolFailed);
        }

        self.command_pool = Some(command_pool);

        Ok(())
    }

    fn allocate_command_buffer(&mut self) -> Result<(), Error> {
        let command_buffer_allocate_info = vk::VkCommandBufferAllocateInfo {
            sType: vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
            pNext: ptr::null(),
            commandPool: self.command_pool.unwrap(),
            level: vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY,
            commandBufferCount: 1,
        };
        // VkCommandBuffer is a dispatchable handle so it is always a
        // pointer and we can’t use vk::null_handle.
        let mut command_buffer = ptr::null_mut();
        let res = unsafe {
            self.device_pair.vkdev.vkAllocateCommandBuffers.unwrap()(
                self.device_pair.device,
                ptr::addr_of!(command_buffer_allocate_info),
                ptr::addr_of_mut!(command_buffer),
            )
        };

        if res != vk::VK_SUCCESS {
            return Err(Error::CommandBufferAllocateFailed);
        }

        self.command_buffer = Some(command_buffer);

        Ok(())
    }

    fn create_fence(&mut self) -> Result<(), Error> {
        let fence_create_info = vk::VkFenceCreateInfo {
            sType: vk::VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
            pNext: ptr::null(),
            flags: 0,
        };
        let mut fence = vk::null_handle();
        let res = unsafe {
            self.device_pair.vkdev.vkCreateFence.unwrap()(
                self.device_pair.device,
                ptr::addr_of!(fence_create_info),
                ptr::null(), // allocator
                ptr::addr_of_mut!(fence),
            )
        };
        if res != vk::VK_SUCCESS {
            return Err(Error::CreateFenceFailed);
        }

        self.fence = Some(fence);

        Ok(())
    }

    fn new(
        device_pair: &'a DevicePair,
        queue_family: u32,
    ) -> Result<DeviceResources<'a>, Error> {
        let mut device_resources = DeviceResources {
            device_pair,
            command_pool: None,
            command_buffer: None,
            fence: None,
        };

        device_resources.create_command_pool(queue_family)?;
        device_resources.allocate_command_buffer()?;
        device_resources.create_fence()?;

        Ok(device_resources)
    }
}

impl<'a> Drop for DeviceResources<'a> {
    fn drop(&mut self) {
        free_resources(
            self.device_pair,
            self.command_pool.take(),
            self.command_buffer.take(),
            self.fence.take(),
        );
    }
}

fn find_queue_family(
    instance_pair: &InstancePair,
    physical_device: vk::VkPhysicalDevice,
) -> Result<u32, Error> {
    let vkinst = instance_pair.vkinst.as_ref();

    let mut count = 0u32;

    unsafe {
        vkinst.vkGetPhysicalDeviceQueueFamilyProperties.unwrap()(
            physical_device,
            ptr::addr_of_mut!(count),
            ptr::null_mut(), // queues
        );
    }

    let mut queues = Vec::<vk::VkQueueFamilyProperties>::new();
    queues.resize_with(count as usize, Default::default);

    unsafe {
        vkinst.vkGetPhysicalDeviceQueueFamilyProperties.unwrap()(
            physical_device,
            ptr::addr_of_mut!(count),
            queues.as_mut_ptr(),
        );
    }

    for (i, queue) in queues.into_iter().enumerate() {
        if queue.queueFlags & vk::VK_QUEUE_GRAPHICS_BIT != 0 &&
            queue.queueCount >= 1
        {
            return Ok(i as u32);
        }
    }

    Err(Error::NoGraphicsQueueFamily)
}

// Checks whether the chosen physical device can be used and has an
// appropriate queue family. If so, it returns the index of the queue
// family, otherwise an error.
fn check_physical_device(
    instance_pair: &InstancePair,
    requirements: &Requirements,
    physical_device: vk::VkPhysicalDevice,
) -> Result<u32, Error> {
    requirements.check(
        instance_pair.vkinst.as_ref(),
        physical_device
    )?;

    find_queue_family(instance_pair, physical_device)
}

// Checks all of the physical devices advertised by the instance. If
// it can found one matching the requirements then it returns it along
// with a queue family index of the first graphics queue. Otherwise
// returns an error.
fn find_physical_device(
    instance_pair: &InstancePair,
    requirements: &Requirements,
    device_id: Option<usize>,
) -> Result<(vk::VkPhysicalDevice, u32), Error> {
    let vkinst = instance_pair.vkinst.as_ref();

    let mut count = 0u32;

    let res = unsafe {
        vkinst.vkEnumeratePhysicalDevices.unwrap()(
            instance_pair.vk_instance,
            ptr::addr_of_mut!(count),
            ptr::null_mut(), // devices
        )
    };

    if res != vk::VK_SUCCESS {
        return Err(Error::EnumeratePhysicalDevicesFailed);
    }

    let mut devices = Vec::<vk::VkPhysicalDevice>::new();
    devices.resize(count as usize, ptr::null_mut());

    let res = unsafe {
        vkinst.vkEnumeratePhysicalDevices.unwrap()(
            instance_pair.vk_instance,
            ptr::addr_of_mut!(count),
            devices.as_mut_ptr(),
        )
    };

    if res != vk::VK_SUCCESS {
        return Err(Error::EnumeratePhysicalDevicesFailed);
    }

    if let Some(device_id) = device_id {
        if device_id >= count as usize {
            return Err(Error::InvalidDeviceId { device_id, n_devices: count });
        } else {
            return match check_physical_device(
                instance_pair,
                requirements,
                devices[device_id]
            ) {
                Ok(queue_family) => Ok((devices[device_id], queue_family)),
                Err(e) => Err(e),
            };
        }
    }

    // Collect all of the errors into an array so we can report all of
    // them in a combined error message if we can’t find a good
    // device.
    let mut errors = Vec::<Error>::new();

    for device in devices {
        match check_physical_device(
            instance_pair,
            requirements,
            device
        ) {
            Ok(queue_family) => return Ok((device, queue_family)),
            Err(e) => {
                errors.push(e);
            },
        }
    }

    match errors.len() {
        0 => Err(Error::NoDevices),
        1 => Err(errors.pop().unwrap()),
        _ => Err(Error::DeviceErrors(errors)),
    }
}

impl Context {
    fn new_internal(
        vklib: Option<Box<vulkan_funcs::Library>>,
        instance_pair: InstancePair,
        physical_device: vk::VkPhysicalDevice,
        queue_family: u32,
        device_pair: DevicePair,
    ) -> Result<Context, Error> {
        let mut memory_properties =
            vk::VkPhysicalDeviceMemoryProperties::default();

        unsafe {
            instance_pair.vkinst.vkGetPhysicalDeviceMemoryProperties.unwrap()(
                physical_device,
                ptr::addr_of_mut!(memory_properties),
            );
        }

        // VkQueue is a dispatchable handle so it is always a pointer
        // and we can’t use vk::null_handle.
        let mut queue = ptr::null_mut();

        unsafe {
            device_pair.vkdev.vkGetDeviceQueue.unwrap()(
                device_pair.device,
                queue_family,
                0, // queueIndex
                ptr::addr_of_mut!(queue)
            );
        }

        let mut resources = DeviceResources::new(
            &device_pair,
            queue_family,
        )?;

        let command_pool = resources.command_pool.take().unwrap();
        let command_buffer = resources.command_buffer.take().unwrap();
        let fence = resources.fence.take().unwrap();
        drop(resources);

        Ok(Context {
            _vklib: vklib,
            instance_pair,
            device_pair,
            physical_device,
            memory_properties,
            command_pool,
            command_buffer,
            fence,
            queue,
            always_flush_memory: env_var_as_boolean(
                "VKRUNNER_ALWAYS_FLUSH_MEMORY",
                false
            )
        })
    }

    /// Constructs a Context or returns [Error::Failure] if an
    /// error occurred while constructing it or
    /// [Error::Incompatible] if the requirements couldn’t be
    /// met. `device_id` can optionally be set to limit the device
    /// selection to an index in the list returned by
    /// `vkEnumeratePhysicalDevices`.
    pub fn new(
        requirements: &Requirements,
        device_id: Option<usize>,
    ) -> Result<Context, Error> {
        let vklib = Box::new(vulkan_funcs::Library::new()?);

        let instance_pair = InstancePair::new(vklib.as_ref(), requirements)?;

        let (physical_device, queue_family) = find_physical_device(
            &instance_pair,
            requirements,
            device_id,
        )?;

        let device_pair = DevicePair::new(
            &instance_pair,
            requirements,
            physical_device,
            queue_family,
        )?;

        Context::new_internal(
            Some(vklib),
            instance_pair,
            physical_device,
            queue_family,
            device_pair
        )
    }

    /// Constructs a Context from a VkDevice created externally. The
    /// VkDevice won’t be freed when the context is dropped. It is the
    /// caller’s responsibility to keep the device alive during the
    /// lifetime of the Context. It also needs to ensure that any
    /// features and extensions that might be used during script
    /// execution were enabled when the device was created.
    ///
    /// `get_instance_proc_cb` will be called to get the
    /// instance-level functions that the context needs. The rest of
    /// the functions will be retrieved using the
    /// `vkGetDeviceProcAddr` function that that returns. `user_data`
    /// will be passed to the function.
    pub fn new_with_device(
        get_instance_proc_cb: vulkan_funcs::GetInstanceProcFunc,
        user_data: *const c_void,
        physical_device: vk::VkPhysicalDevice,
        queue_family: u32,
        device: vk::VkDevice
    ) -> Result<Context, Error> {
        let instance_pair = InstancePair::new_external(
            get_instance_proc_cb,
            user_data,
        );

        let device_pair = DevicePair::new_external(
            &instance_pair,
            device,
        );

        Context::new_internal(
            None, // vklib
            instance_pair,
            physical_device,
            queue_family,
            device_pair,
        )
    }

    /// Get the instance function pointers
    #[inline]
    pub fn instance(&self) -> &vulkan_funcs::Instance {
        &self.instance_pair.vkinst
    }

    /// Get the VkDevice handle
    #[inline]
    pub fn vk_device(&self) -> vk::VkDevice {
        self.device_pair.device
    }

    /// Get the device function pointers
    #[inline]
    pub fn device(&self) -> &vulkan_funcs::Device {
        &self.device_pair.vkdev
    }

    /// Get the physical device that was used to create this context.
    #[inline]
    pub fn physical_device(&self) -> vk::VkPhysicalDevice {
        self.physical_device
    }

    /// Get the memory properties struct for the physical device. This
    /// is queried from the physical device once when the context is
    /// constructed and cached for later use so this method is very
    /// cheap.
    #[inline]
    pub fn memory_properties(&self) -> &vk::VkPhysicalDeviceMemoryProperties {
        &self.memory_properties
    }

    /// Get the single shared command buffer that is associated with
    /// the context.
    #[inline]
    pub fn command_buffer(&self) -> vk::VkCommandBuffer {
        self.command_buffer
    }

    /// Get the single shared fence that is associated with the
    /// context.
    #[inline]
    pub fn fence(&self) -> vk::VkFence {
        self.fence
    }

    /// Get the queue chosen for this context.
    #[inline]
    pub fn queue(&self) -> vk::VkQueue {
        self.queue
    }

    /// Returns whether the memory should always be flushed regardless
    /// of whether the `VK_MEMORY_PROPERTY_HOST_COHERENT` bit is set
    /// in the [memory_properties](Context::memory_properties). This
    /// is mainly for testing purposes and can be enabled by setting
    /// the `VKRUNNER_ALWAYS_FLUSH_MEMORY` environment variable to
    /// `true`.
    #[inline]
    pub fn always_flush_memory(&self) -> bool {
        self.always_flush_memory
    }
}

impl Drop for Context {
    fn drop(&mut self) {
        free_resources(
            &self.device_pair,
            Some(self.command_pool),
            Some(self.command_buffer),
            Some(self.fence),
        );
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::fake_vulkan::{FakeVulkan, HandleType};
    use crate::env_var_test::EnvVarLock;

    #[test]
    fn base() {
        let mut fake_vulkan = FakeVulkan::new();
        fake_vulkan.physical_devices.push(Default::default());
        fake_vulkan.physical_devices[0].memory_properties.memoryTypeCount = 3;

        fake_vulkan.set_override();
        let context = Context::new(&Requirements::new(), None).unwrap();

        assert!(!context.instance_pair.vk_instance.is_null());
        assert!(context.instance().vkCreateDevice.is_some());
        assert!(!context.vk_device().is_null());
        assert!(context.device().vkCreateCommandPool.is_some());
        assert_eq!(
            context.physical_device(),
            fake_vulkan.index_to_physical_device(0)
        );
        assert_eq!(context.memory_properties().memoryTypeCount, 3);
        assert!(!context.command_buffer().is_null());
        assert!(context.fence() != vk::null_handle());
        assert_eq!(FakeVulkan::unmake_queue(context.queue()), (0, 0));
    }

    #[test]
    fn no_devices() {
        let fake_vulkan = FakeVulkan::new();
        fake_vulkan.set_override();
        let err = Context::new(&Requirements::new(), None).unwrap_err();
        assert_eq!(
            &err.to_string(),
            "The Vulkan instance reported zero drivers"
        );
    }

    #[test]
    fn check_instance_extension() {
        let mut fake_vulkan = FakeVulkan::new();
        fake_vulkan.physical_devices.push(Default::default());

        let mut reqs = Requirements::new();
        // Add a requirement so that c_structures won’t be NULL
        reqs.add("shaderInt8");

        // Make vkEnumerateInstanceExtensionProperties fail
        fake_vulkan.queue_result(
            "vkEnumerateInstanceExtensionProperties".to_string(),
            vk::VK_ERROR_UNKNOWN,
        );
        fake_vulkan.set_override();
        let err = Context::new(&mut reqs, None).unwrap_err();
        assert_eq!(
            err.to_string(),
            "vkEnumerateInstanceExtensionProperties failed",
        );

        // Make it fail the second time too
        fake_vulkan.queue_result(
            "vkEnumerateInstanceExtensionProperties".to_string(),
            vk::VK_SUCCESS,
        );
        fake_vulkan.queue_result(
            "vkEnumerateInstanceExtensionProperties".to_string(),
            vk::VK_ERROR_UNKNOWN,
        );
        fake_vulkan.set_override();
        let err = Context::new(&mut reqs, None).unwrap_err();
        assert_eq!(
            err.to_string(),
            "vkEnumerateInstanceExtensionProperties failed",
        );

        // Add an extension name with the same length but different characters
        fake_vulkan.add_instance_extension(
            "VK_KHR_get_physical_device_properties3"
        );

        // Add a shorter extension name
        fake_vulkan.add_instance_extension("VK_KHR");
        // And a longer one
        fake_vulkan.add_instance_extension(
            "VK_KHR_get_physical_device_properties23"
        );

        fake_vulkan.set_override();
        let err = Context::new(&mut reqs, None).unwrap_err();
        assert_eq!(
            err.to_string(),
            "Missing instance extension: \
             VK_KHR_get_physical_device_properties2",
        );
    }

    #[test]
    fn extension_feature() {
        let mut fake_vulkan = FakeVulkan::new();
        fake_vulkan.physical_devices.push(Default::default());
        fake_vulkan.add_instance_extension(
            "VK_KHR_get_physical_device_properties2"
        );

        let mut reqs = Requirements::new();

        reqs.add("multiview");

        fake_vulkan.set_override();
        assert_eq!(
            Context::new(&mut reqs, None).unwrap_err().to_string(),
            "Missing required extension: VK_KHR_multiview",
        );

        fake_vulkan.physical_devices[0].add_extension("VK_KHR_multiview");
        fake_vulkan.physical_devices[0].multiview.multiview = vk::VK_TRUE;

        fake_vulkan.set_override();
        Context::new(&mut reqs, None).unwrap();
    }

    #[test]
    fn multiple_mismatches() {
        let mut fake_vulkan = FakeVulkan::new();

        let mut reqs = Requirements::new();
        reqs.add("madeup_extension");

        fake_vulkan.physical_devices.push(Default::default());
        fake_vulkan.physical_devices[0].properties.apiVersion =
            crate::requirements::make_version(0, 1, 0);
        fake_vulkan.physical_devices[0].add_extension("madeup_extension");

        fake_vulkan.physical_devices.push(Default::default());
        fake_vulkan.physical_devices[1].queue_families.clear();
        fake_vulkan.physical_devices[1].add_extension("madeup_extension");

        fake_vulkan.physical_devices.push(Default::default());

        fake_vulkan.set_override();
        let err = Context::new(&mut reqs, None).unwrap_err();
        assert_eq!(
            err.to_string(),
            "0: Vulkan API version 1.0.0 required but the driver \
             reported 0.1.0\n\
             1: Device has no graphics queue family\n\
             2: Missing required extension: madeup_extension",
        );
        assert_eq!(err.result(), result::Result::Skip);

        // Try making them one of them fail
        fake_vulkan.queue_result(
            "vkEnumerateDeviceExtensionProperties".to_string(),
            vk::VK_ERROR_UNKNOWN,
        );

        fake_vulkan.set_override();
        let err = Context::new(&mut reqs, None).unwrap_err();
        assert_eq!(
            err.to_string(),
            "0: vkEnumerateDeviceExtensionProperties failed\n\
             1: Device has no graphics queue family\n\
             2: Missing required extension: madeup_extension",
        );
        assert_eq!(err.result(), result::Result::Skip);

        // Try making all of them fail
        fake_vulkan.physical_devices[0] = Default::default();
        fake_vulkan.physical_devices[1] = Default::default();
        fake_vulkan.queue_result(
            "vkEnumerateDeviceExtensionProperties".to_string(),
            vk::VK_ERROR_UNKNOWN,
        );
        fake_vulkan.queue_result(
            "vkEnumerateDeviceExtensionProperties".to_string(),
            vk::VK_ERROR_UNKNOWN,
        );
        fake_vulkan.queue_result(
            "vkEnumerateDeviceExtensionProperties".to_string(),
            vk::VK_ERROR_UNKNOWN,
        );

        fake_vulkan.set_override();
        let err = Context::new(&mut reqs, None).unwrap_err();
        assert_eq!(
            err.to_string(),
            "0: vkEnumerateDeviceExtensionProperties failed\n\
             1: vkEnumerateDeviceExtensionProperties failed\n\
             2: vkEnumerateDeviceExtensionProperties failed",
        );
        assert_eq!(err.result(), result::Result::Fail);

        // Finally add a physical device that will succeed
        fake_vulkan.physical_devices.push(Default::default());
        fake_vulkan.physical_devices[3].add_extension("madeup_extension");
        fake_vulkan.set_override();
        let context = Context::new(&mut reqs, None).unwrap();
        assert_eq!(
            fake_vulkan.physical_device_to_index(context.physical_device),
            3,
        );
    }

    #[test]
    fn device_id() {
        let mut fake_vulkan = FakeVulkan::new();
        for _ in 0..3 {
            fake_vulkan.physical_devices.push(Default::default());
        }

        let mut reqs = Requirements::new();

        // Try selecting each device
        for i in 0..3 {
            fake_vulkan.set_override();
            let context = Context::new(&mut reqs, Some(i)).unwrap();
            assert_eq!(
                fake_vulkan.physical_device_to_index(context.physical_device),
                i,
            );
        }

        // Try selecting a non-existant device
        fake_vulkan.set_override();
        let err = Context::new(&mut reqs, Some(3)).unwrap_err();
        assert_eq!(
            err.to_string(),
            "Device 4 was selected but the Vulkan instance only reported 3 \
             devices."
        );

        fake_vulkan.physical_devices.truncate(1);
        fake_vulkan.set_override();
        let err = Context::new(&mut reqs, Some(3)).unwrap_err();
        assert_eq!(
            err.to_string(),
            "Device 4 was selected but the Vulkan instance only reported 1 \
             device."
        );

        // Try a failure
        reqs.add("madeup_extension");
        fake_vulkan.physical_devices.push(Default::default());
        fake_vulkan.set_override();
        let err = Context::new(&mut reqs, Some(0)).unwrap_err();
        assert_eq!(
            err.to_string(),
            "Missing required extension: madeup_extension",
        );
    }

    #[test]
    fn always_flush_memory_false() {
        let mut fake_vulkan = FakeVulkan::new();
        fake_vulkan.physical_devices.push(Default::default());
        let mut reqs = Requirements::new();

        let _env_var_lock = EnvVarLock::new(&[
            ("VKRUNNER_ALWAYS_FLUSH_MEMORY", "false"),
        ]);

        fake_vulkan.set_override();
        let context = Context::new(&mut reqs, None).unwrap();
        assert_eq!(context.always_flush_memory(), false);
    }

    #[test]
    fn always_flush_memory_true() {
        let mut fake_vulkan = FakeVulkan::new();
        fake_vulkan.physical_devices.push(Default::default());
        let mut reqs = Requirements::new();

        let _env_var_lock = EnvVarLock::new(&[
            ("VKRUNNER_ALWAYS_FLUSH_MEMORY", "true"),
        ]);

        fake_vulkan.set_override();
        let context = Context::new(&mut reqs, None).unwrap();
        assert_eq!(context.always_flush_memory(), true);
    }

    #[test]
    fn new_with_device() {
        let mut fake_vulkan = FakeVulkan::new();
        fake_vulkan.physical_devices.push(Default::default());

        let device = fake_vulkan.add_dispatchable_handle(HandleType::Device);

        extern "C" fn no_create_device(
            _physical_device: vk::VkPhysicalDevice,
            _create_info: *const vk::VkDeviceCreateInfo,
            _allocator: *const vk::VkAllocationCallbacks,
            _device_out: *mut vk::VkDevice,
        ) -> vk::VkResult {
            unreachable!(
                "vkCreateDevice shouldn’t be called for an external \
                 device"
            );
        }

        extern "C" fn no_destroy_device(
            _device: vk::VkDevice,
            _allocator: *const vk::VkAllocationCallbacks
        ) {
            unreachable!(
                "vkDestroyDevice shouldn’t be called for an external \
                 device"
            );
        }

        extern "C" fn no_destroy_instance(
            _instance: vk::VkInstance,
            _allocator: *const vk::VkAllocationCallbacks
        ) {
            unreachable!(
                "vkDestroyInstance shouldn’t be called for an external \
                 device"
            );
        }

        extern "C" fn get_device_proc_addr(
            _device: vk::VkDevice,
            name: *const c_char,
        ) -> vk::PFN_vkVoidFunction {
            unsafe {
                std::mem::transmute(get_instance_proc_cb(
                    name.cast(),
                    (FakeVulkan::current() as *mut FakeVulkan).cast(),
                ))
            }
        }

        extern "C" fn get_instance_proc_cb(
            func_name: *const c_char,
            user_data: *const c_void,
        ) -> *const c_void {
            let name = unsafe {
                CStr::from_ptr(func_name.cast()).to_str().unwrap()
            };

            match name {
                "vkGetDeviceProcAddr" => unsafe {
                    std::mem::transmute::<vk::PFN_vkGetDeviceProcAddr, _>(
                        Some(get_device_proc_addr)
                    )
                },
                "vkDestroyInstance" => unsafe {
                    std::mem::transmute::<vk::PFN_vkDestroyInstance, _>(
                        Some(no_destroy_instance)
                    )
                },
                "vkCreateDevice" => unsafe {
                    std::mem::transmute::<vk::PFN_vkCreateDevice, _>(
                        Some(no_create_device)
                    )
                },
                "vkDestroyDevice" => unsafe {
                    std::mem::transmute::<vk::PFN_vkDestroyDevice, _>(
                        Some(no_destroy_device)
                    )
                },
                _ => unsafe {
                    let fake_vulkan = &*(user_data.cast::<FakeVulkan>());
                    std::mem::transmute(
                        fake_vulkan.get_function(func_name.cast())
                    )
                },
            }
        }

        let context = Context::new_with_device(
            get_instance_proc_cb,
            (fake_vulkan.as_ref() as *const FakeVulkan).cast(),
            fake_vulkan.index_to_physical_device(0),
            3, // queue_family
            device,
        ).unwrap();

        assert_eq!(context.instance_pair.vk_instance, ptr::null_mut());
        assert_eq!(
            fake_vulkan.physical_device_to_index(context.physical_device()),
            0
        );
        assert_eq!(context.vk_device(), device);
        assert!(context.device_pair.is_external);
        assert!(context.instance_pair.is_external);

        drop(context);

        fake_vulkan.get_dispatchable_handle_mut(device).freed = true;
    }
}

```
Page 3/7FirstPrevNextLast