/* Copyright (c) 2015-2021 The Khronos Group Inc.
 * Copyright (c) 2015-2021 Valve Corporation
 * Copyright (c) 2015-2021 LunarG, Inc.
 * Copyright (C) 2015-2021 Google Inc.
 * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author: Courtney Goeltzenleuchter <courtneygo@google.com>
 * Author: Tobin Ehlis <tobine@google.com>
 * Author: Chris Forbes <chrisf@ijw.co.nz>
 * Author: Mark Lobodzinski <mark@lunarg.com>
 * Author: Dave Houlton <daveh@lunarg.com>
 * Author: John Zulauf <jzulauf@lunarg.com>
 * Author: Tobias Hector <tobias.hector@amd.com>
 */
#include "cmd_buffer_state.h"
#include "render_pass_state.h"

const char *CommandTypeString(CMD_TYPE type) {
    // Autogenerated as part of the command_validation.h codegen
    return kGeneratedCommandNameList[type];
}

VkDynamicState ConvertToDynamicState(CBStatusFlagBits flag) {
    switch (flag) {
        case CBSTATUS_LINE_WIDTH_SET:
            return VK_DYNAMIC_STATE_LINE_WIDTH;
        case CBSTATUS_DEPTH_BIAS_SET:
            return VK_DYNAMIC_STATE_DEPTH_BIAS;
        case CBSTATUS_BLEND_CONSTANTS_SET:
            return VK_DYNAMIC_STATE_BLEND_CONSTANTS;
        case CBSTATUS_DEPTH_BOUNDS_SET:
            return VK_DYNAMIC_STATE_DEPTH_BOUNDS;
        case CBSTATUS_STENCIL_READ_MASK_SET:
            return VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
        case CBSTATUS_STENCIL_WRITE_MASK_SET:
            return VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
        case CBSTATUS_STENCIL_REFERENCE_SET:
            return VK_DYNAMIC_STATE_STENCIL_REFERENCE;
        case CBSTATUS_VIEWPORT_SET:
            return VK_DYNAMIC_STATE_VIEWPORT;
        case CBSTATUS_SCISSOR_SET:
            return VK_DYNAMIC_STATE_SCISSOR;
        case CBSTATUS_EXCLUSIVE_SCISSOR_SET:
            return VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV;
        case CBSTATUS_SHADING_RATE_PALETTE_SET:
            return VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV;
        case CBSTATUS_LINE_STIPPLE_SET:
            return VK_DYNAMIC_STATE_LINE_STIPPLE_EXT;
        case CBSTATUS_VIEWPORT_W_SCALING_SET:
            return VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV;
        case CBSTATUS_CULL_MODE_SET:
            return VK_DYNAMIC_STATE_CULL_MODE_EXT;
        case CBSTATUS_FRONT_FACE_SET:
            return VK_DYNAMIC_STATE_FRONT_FACE_EXT;
        case CBSTATUS_PRIMITIVE_TOPOLOGY_SET:
            return VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT;
        case CBSTATUS_VIEWPORT_WITH_COUNT_SET:
            return VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT;
        case CBSTATUS_SCISSOR_WITH_COUNT_SET:
            return VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT;
        case CBSTATUS_VERTEX_INPUT_BINDING_STRIDE_SET:
            return VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT;
        case CBSTATUS_DEPTH_TEST_ENABLE_SET:
            return VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT;
        case CBSTATUS_DEPTH_WRITE_ENABLE_SET:
            return VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT;
        case CBSTATUS_DEPTH_COMPARE_OP_SET:
            return VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT;
        case CBSTATUS_DEPTH_BOUNDS_TEST_ENABLE_SET:
            return VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT;
        case CBSTATUS_STENCIL_TEST_ENABLE_SET:
            return VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT;
        case CBSTATUS_STENCIL_OP_SET:
            return VK_DYNAMIC_STATE_STENCIL_OP_EXT;
        case CBSTATUS_DISCARD_RECTANGLE_SET:
            return VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT;
        case CBSTATUS_SAMPLE_LOCATIONS_SET:
            return VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT;
        case CBSTATUS_COARSE_SAMPLE_ORDER_SET:
            return VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV;
        case CBSTATUS_PATCH_CONTROL_POINTS_SET:
            return VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT;
        case CBSTATUS_RASTERIZER_DISCARD_ENABLE_SET:
            return VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT;
        case CBSTATUS_DEPTH_BIAS_ENABLE_SET:
            return VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT;
        case CBSTATUS_LOGIC_OP_SET:
            return VK_DYNAMIC_STATE_LOGIC_OP_EXT;
        case CBSTATUS_PRIMITIVE_RESTART_ENABLE_SET:
            return VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT;
        case CBSTATUS_VERTEX_INPUT_SET:
            return VK_DYNAMIC_STATE_VERTEX_INPUT_EXT;
        default:
            // CBSTATUS_INDEX_BUFFER_BOUND is not in VkDynamicState
            return VK_DYNAMIC_STATE_MAX_ENUM;
    }
    return VK_DYNAMIC_STATE_MAX_ENUM;
}

CBStatusFlagBits ConvertToCBStatusFlagBits(VkDynamicState state) {
    switch (state) {
        case VK_DYNAMIC_STATE_VIEWPORT:
            return CBSTATUS_VIEWPORT_SET;
        case VK_DYNAMIC_STATE_SCISSOR:
            return CBSTATUS_SCISSOR_SET;
        case VK_DYNAMIC_STATE_LINE_WIDTH:
            return CBSTATUS_LINE_WIDTH_SET;
        case VK_DYNAMIC_STATE_DEPTH_BIAS:
            return CBSTATUS_DEPTH_BIAS_SET;
        case VK_DYNAMIC_STATE_BLEND_CONSTANTS:
            return CBSTATUS_BLEND_CONSTANTS_SET;
        case VK_DYNAMIC_STATE_DEPTH_BOUNDS:
            return CBSTATUS_DEPTH_BOUNDS_SET;
        case VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK:
            return CBSTATUS_STENCIL_READ_MASK_SET;
        case VK_DYNAMIC_STATE_STENCIL_WRITE_MASK:
            return CBSTATUS_STENCIL_WRITE_MASK_SET;
        case VK_DYNAMIC_STATE_STENCIL_REFERENCE:
            return CBSTATUS_STENCIL_REFERENCE_SET;
        case VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV:
            return CBSTATUS_VIEWPORT_W_SCALING_SET;
        case VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT:
            return CBSTATUS_DISCARD_RECTANGLE_SET;
        case VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT:
            return CBSTATUS_SAMPLE_LOCATIONS_SET;
        case VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV:
            return CBSTATUS_SHADING_RATE_PALETTE_SET;
        case VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV:
            return CBSTATUS_COARSE_SAMPLE_ORDER_SET;
        case VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV:
            return CBSTATUS_EXCLUSIVE_SCISSOR_SET;
        case VK_DYNAMIC_STATE_LINE_STIPPLE_EXT:
            return CBSTATUS_LINE_STIPPLE_SET;
        case VK_DYNAMIC_STATE_CULL_MODE_EXT:
            return CBSTATUS_CULL_MODE_SET;
        case VK_DYNAMIC_STATE_FRONT_FACE_EXT:
            return CBSTATUS_FRONT_FACE_SET;
        case VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT:
            return CBSTATUS_PRIMITIVE_TOPOLOGY_SET;
        case VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT:
            return CBSTATUS_VIEWPORT_WITH_COUNT_SET;
        case VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT:
            return CBSTATUS_SCISSOR_WITH_COUNT_SET;
        case VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT:
            return CBSTATUS_VERTEX_INPUT_BINDING_STRIDE_SET;
        case VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT:
            return CBSTATUS_DEPTH_TEST_ENABLE_SET;
        case VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT:
            return CBSTATUS_DEPTH_WRITE_ENABLE_SET;
        case VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT:
            return CBSTATUS_DEPTH_COMPARE_OP_SET;
        case VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT:
            return CBSTATUS_DEPTH_BOUNDS_TEST_ENABLE_SET;
        case VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT:
            return CBSTATUS_STENCIL_TEST_ENABLE_SET;
        case VK_DYNAMIC_STATE_STENCIL_OP_EXT:
            return CBSTATUS_STENCIL_OP_SET;
        case VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT:
            return CBSTATUS_PATCH_CONTROL_POINTS_SET;
        case VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT:
            return CBSTATUS_RASTERIZER_DISCARD_ENABLE_SET;
        case VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT:
            return CBSTATUS_DEPTH_BIAS_ENABLE_SET;
        case VK_DYNAMIC_STATE_LOGIC_OP_EXT:
            return CBSTATUS_LOGIC_OP_SET;
        case VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT:
            return CBSTATUS_PRIMITIVE_RESTART_ENABLE_SET;
        case VK_DYNAMIC_STATE_VERTEX_INPUT_EXT:
            return CBSTATUS_VERTEX_INPUT_SET;
        default:
            return CBSTATUS_NONE;
    }
    return CBSTATUS_NONE;
}

// Get the image viewstate for a given framebuffer attachment
IMAGE_VIEW_STATE *CMD_BUFFER_STATE::GetActiveAttachmentImageViewState(uint32_t index) {
    assert(active_attachments && index != VK_ATTACHMENT_UNUSED && (index < active_attachments->size()));
    return active_attachments->at(index);
}

// Get the image viewstate for a given framebuffer attachment
const IMAGE_VIEW_STATE *CMD_BUFFER_STATE::GetActiveAttachmentImageViewState(uint32_t index) const {
    assert(active_attachments && index != VK_ATTACHMENT_UNUSED && (index < active_attachments->size()));
    return active_attachments->at(index);
}

void CMD_BUFFER_STATE::AddChild(BASE_NODE *child_node) {
    if (child_node->AddParent(this)) {
        object_bindings.insert(child_node->Handle());
    }
}

void CMD_BUFFER_STATE::RemoveChild(BASE_NODE *child_node) {
    child_node->RemoveParent(this);
    object_bindings.erase(child_node->Handle());
}

void CMD_BUFFER_STATE::Reset() {
    ResetUse();
    // Reset CB state (note that createInfo is not cleared)
    memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo));
    memset(&inheritanceInfo, 0, sizeof(VkCommandBufferInheritanceInfo));
    hasDrawCmd = false;
    hasTraceRaysCmd = false;
    hasBuildAccelerationStructureCmd = false;
    hasDispatchCmd = false;
    state = CB_NEW;
    commandCount = 0;
    submitCount = 0;
    image_layout_change_count = 1;  // Start at 1. 0 is insert value for validation cache versions, s.t. new == dirty
    status = 0;
    static_status = 0;
    inheritedViewportDepths.clear();
    usedViewportScissorCount = 0;
    pipelineStaticViewportCount = 0;
    pipelineStaticScissorCount = 0;
    viewportMask = 0;
    viewportWithCountMask = 0;
    viewportWithCountCount = 0;
    scissorMask = 0;
    scissorWithCountMask = 0;
    scissorWithCountCount = 0;
    trashedViewportMask = 0;
    trashedScissorMask = 0;
    trashedViewportCount = false;
    trashedScissorCount = false;
    usedDynamicViewportCount = false;
    usedDynamicScissorCount = false;
    primitiveTopology = VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;

    activeRenderPassBeginInfo = safe_VkRenderPassBeginInfo();
    activeRenderPass = nullptr;
    active_attachments = nullptr;
    active_subpasses = nullptr;
    attachments_view_states.clear();
    activeSubpassContents = VK_SUBPASS_CONTENTS_INLINE;
    activeSubpass = 0;
    broken_bindings.clear();
    waitedEvents.clear();
    events.clear();
    writeEventsBeforeWait.clear();
    activeQueries.clear();
    startedQueries.clear();
    image_layout_map.clear();
    current_vertex_buffer_binding_info.vertex_buffer_bindings.clear();
    vertex_buffer_used = false;
    primaryCommandBuffer = VK_NULL_HANDLE;

    linkedCommandBuffers.clear();
    // Remove reverse command buffer links.
    Invalidate(true);

    queue_submit_functions.clear();
    cmd_execute_commands_functions.clear();
    eventUpdates.clear();
    queryUpdates.clear();

    // Remove object bindings
    for (const auto &obj : object_bindings) {
        if (obj.node) {
            obj.node->RemoveParent(this);
        }
    }
    object_bindings.clear();

    for (auto &item : lastBound) {
        item.Reset();
    }
    // Remove this cmdBuffer's reference from each FrameBuffer's CB ref list
    for (auto &framebuffer : framebuffers) {
        framebuffer->RemoveParent(this);
    }
    framebuffers.clear();
    activeFramebuffer = VK_NULL_HANDLE;
    index_buffer_binding.reset();

    qfo_transfer_image_barriers.Reset();
    qfo_transfer_buffer_barriers.Reset();

    // Clean up the label data
    debug_label.Reset();
    validate_descriptorsets_in_queuesubmit.clear();

    // Best practices info
    small_indexed_draw_call_count = 0;

    transform_feedback_active = false;

    // Remove object bindings
    for (const auto &obj : object_bindings) {
        BASE_NODE *base_obj = obj.node;
        if (base_obj) RemoveChild(base_obj);
    }
    object_bindings.clear();
}

void CMD_BUFFER_STATE::Destroy() {
    Reset();
    BASE_NODE::Destroy();
}

void CMD_BUFFER_STATE::NotifyInvalidate(const LogObjectList &invalid_handles, bool unlink) {
    if (state == CB_RECORDING) {
        state = CB_INVALID_INCOMPLETE;
    } else if (state == CB_RECORDED) {
        state = CB_INVALID_COMPLETE;
    }
    assert(!invalid_handles.object_list.empty());
    broken_bindings.emplace(invalid_handles.object_list[0], invalid_handles);

    if (unlink) {
        for (auto &obj : invalid_handles.object_list) {
            object_bindings.erase(obj);
            if (obj.type == kVulkanObjectTypeCommandBuffer) {
                linkedCommandBuffers.erase(static_cast<CMD_BUFFER_STATE *>(obj.node));
            }
        }
    }
    BASE_NODE::NotifyInvalidate(invalid_handles, unlink);
}

// The const variant only need the image as it is the key for the map
const ImageSubresourceLayoutMap *CMD_BUFFER_STATE::GetImageSubresourceLayoutMap(VkImage image) const {
    auto it = image_layout_map.find(image);
    if (it == image_layout_map.cend()) {
        return nullptr;
    }
    return &it->second;
}

// The non-const variant only needs the image state, as the factory requires it to construct a new entry
ImageSubresourceLayoutMap *CMD_BUFFER_STATE::GetImageSubresourceLayoutMap(const IMAGE_STATE &image_state) {
    auto &layout_map = image_layout_map[image_state.image()];
    if (!layout_map) {
        // Was an empty slot... fill it in.
        layout_map.emplace(image_state);
    }
    return &layout_map;
}

