/*
 * Copyright (C) 2020-2026 Intel Corporation
 *
 * SPDX-License-Identifier: MIT
 *
 */

#include "shared/source/command_container/cmdcontainer.h"
#include "shared/source/command_container/encode_surface_state.h"
#include "shared/source/command_stream/command_stream_receiver.h"
#include "shared/source/command_stream/linear_stream.h"
#include "shared/source/command_stream/transfer_direction.h"
#include "shared/source/debugger/debugger_l0.h"
#include "shared/source/device/device.h"
#include "shared/source/direct_submission/relaxed_ordering_helper.h"
#include "shared/source/execution_environment/execution_environment.h"
#include "shared/source/execution_environment/root_device_environment.h"
#include "shared/source/gmm_helper/gmm_helper.h"
#include "shared/source/helpers/addressing_mode_helper.h"
#include "shared/source/helpers/api_specific_config.h"
#include "shared/source/helpers/blit_commands_helper.h"
#include "shared/source/helpers/blit_properties.h"
#include "shared/source/helpers/common_types.h"
#include "shared/source/helpers/compiler_product_helper.h"
#include "shared/source/helpers/constants.h"
#include "shared/source/helpers/definitions/command_encoder_args.h"
#include "shared/source/helpers/gfx_core_helper.h"
#include "shared/source/helpers/image_helper.h"
#include "shared/source/helpers/in_order_cmd_helpers.h"
#include "shared/source/helpers/kernel_helpers.h"
#include "shared/source/helpers/pipe_control_args.h"
#include "shared/source/helpers/pipeline_select_args.h"
#include "shared/source/helpers/preamble.h"
#include "shared/source/helpers/register_offsets.h"
#include "shared/source/helpers/state_base_address_helper.h"
#include "shared/source/helpers/surface_format_info.h"
#include "shared/source/helpers/validators.h"
#include "shared/source/host_function/host_function.h"
#include "shared/source/indirect_heap/indirect_heap.h"
#include "shared/source/memory_manager/allocation_properties.h"
#include "shared/source/memory_manager/graphics_allocation.h"
#include "shared/source/memory_manager/memadvise_flags.h"
#include "shared/source/memory_manager/memory_manager.h"
#include "shared/source/memory_manager/unified_memory_manager.h"
#include "shared/source/os_interface/os_context.h"
#include "shared/source/page_fault_manager/cpu_page_fault_manager.h"
#include "shared/source/release_helpers/release_helper/release_helper.h"
#include "shared/source/utilities/stackvec.h"
#include "shared/source/utilities/tag_allocator.h"

#include "level_zero/core/source/builtin/builtin_functions_lib.h"
#include "level_zero/core/source/cmdlist/cmdlist_host_function_parameters.h"
#include "level_zero/core/source/cmdlist/cmdlist_hw.h"
#include "level_zero/core/source/cmdlist/cmdlist_launch_params.h"
#include "level_zero/core/source/cmdlist/cmdlist_memory_copy_params.h"
#include "level_zero/core/source/device/bcs_split.h"
#include "level_zero/core/source/device/device.h"
#include "level_zero/core/source/driver/driver_handle.h"
#include "level_zero/core/source/event/event.h"
#include "level_zero/core/source/gfx_core_helpers/l0_gfx_core_helper.h"
#include "level_zero/core/source/helpers/sw_tag_scope.h"
#include "level_zero/core/source/image/image.h"
#include "level_zero/core/source/image/internal_core_image_ext.h"
#include "level_zero/core/source/kernel/kernel.h"
#include "level_zero/core/source/kernel/kernel_imp.h"
#include "level_zero/core/source/module/module.h"
#include "level_zero/core/source/module/module_imp.h"
#include "level_zero/driver_experimental/zex_api.h"

#include "CL/cl_platform.h"

#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>

namespace L0 {

template <GFXCORE_FAMILY gfxCoreFamily>
CommandListCoreFamily<gfxCoreFamily>::~CommandListCoreFamily() {
    clearCommandsToPatch();
    for (auto &alloc : this->ownedPrivateAllocations) {
        device->getNEODevice()->getMemoryManager()->freeGraphicsMemory(alloc.second);
    }
    this->ownedPrivateAllocations.clear();
    this->storeFillPatternResourcesForReuse();
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::postInitComputeSetup() {

    if (!this->stateBaseAddressTracking && !this->heaplessModeEnabled) {
        if (!isImmediateType()) {
            programStateBaseAddress(commandContainer, false);
        }
    }

    commandContainer.setDirtyStateForAllHeaps(false);

    setStreamPropertiesDefaultSettings(requiredStreamState);
    setStreamPropertiesDefaultSettings(finalStreamState);

    currentSurfaceStateBaseAddress = NEO::StreamProperty64::initValue;
    currentDynamicStateBaseAddress = NEO::StreamProperty64::initValue;
    currentIndirectObjectBaseAddress = NEO::StreamProperty64::initValue;
    currentBindingTablePoolBaseAddress = NEO::StreamProperty64::initValue;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::reset() {
    if (flatCapture) {
        flatCapture->reset();
    }
    this->storeFillPatternResourcesForReuse();
    removeDeallocationContainerData();
    removeHostPtrAllocations();
    this->memAdviseOperations.clear();
    removeMemoryPrefetchAllocations();
    commandContainer.reset();
    clearCommandsToPatch();
    externalSemaphoreHostFunctionData.clear();

    if (!isCopyOnly(false)) {
        printfKernelContainer.clear();
        containsStatelessUncachedResource = false;
        indirectAllocationsAllowed = false;
        unifiedMemoryControls.indirectHostAllocationsAllowed = false;
        unifiedMemoryControls.indirectSharedAllocationsAllowed = false;
        unifiedMemoryControls.indirectDeviceAllocationsAllowed = false;
        commandListPreemptionMode = device->getDevicePreemptionMode();
        commandListPerThreadScratchSize[0] = 0u;
        commandListPerThreadScratchSize[1] = 0u;
        usedScratchController = nullptr;
        requiredStreamState.resetState();
        finalStreamState.resetState();
        containsAnyKernel = false;
        containsCooperativeKernelsFlag = false;
        commandListSLMEnabled = false;
        kernelWithAssertAppended = false;

        postInitComputeSetup();

        this->returnPoints.clear();
    }

    for (auto &alloc : this->ownedPrivateAllocations) {
        device->getNEODevice()->getMemoryManager()->freeGraphicsMemory(alloc.second);
    }
    this->ownedPrivateAllocations.clear();
    cmdListCurrentStartOffset = 0;

    mappedTsEventList.clear();
    interruptEvents.clear();

    if (inOrderExecInfo) {
        inOrderExecInfo.reset();
        enableInOrderExecution();
    }

    latestOperationRequiredNonWalkerInOrderCmdsChaining = false;
    taskCountUpdateFenceRequired = false;
    textureCacheFlushPending = false;
    closedCmdList = false;
    isWalkerWithProfilingEnqueued = false;

    this->totalNoopSpace = 0;
    this->latesTagGpuAllocation = nullptr;
    this->latestTaskCount = 0;

    destroyRecordedBcsSplitResources();

    this->executeCleanupCallbacks();

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::assignInOrderExecInfoToEvent(Event *event) {
    if (!event->isCounterBasedExplicitlyEnabled() && !event->getInOrderExecEventHelper().getEventData()) {
        event->getInOrderExecEventHelper().initializeLocalTempStorage();
    }
    event->updateInOrderExecState(inOrderExecInfo, inOrderExecInfo->getCounterValue(), inOrderExecInfo->getAllocationOffset());
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::handleInOrderDependencyCounter(Event *signalEvent, bool nonWalkerInOrderCmdsChaining, bool copyOffloadOperation) {
    if (!isInOrderExecutionEnabled()) {
        if (signalEvent && signalEvent->getInOrderExecEventHelper().isDataAssigned()) {
            UNRECOVERABLE_IF(signalEvent->isCounterBased());
            signalEvent->unsetInOrderExecInfo(); // unset temporary assignment from previous append calls
        }

        return;
    }

    this->handleInOrderCounterOverflow(copyOffloadOperation);

    if (!this->isPostSyncSkippedOnLatestInOrderOperation) {
        inOrderExecInfo->addCounterValue(getInOrderIncrementValue());
    }

    this->addResidency(inOrderExecInfo->getDeviceCounterAllocation(), inOrderExecInfo->getHostCounterAllocation());

    if (signalEvent) {
        if (signalEvent->getInOrderIncrementValue(this->partitionCount) == 0) {
            if (signalEvent->isCounterBased() || nonWalkerInOrderCmdsChaining || (isImmediateType() && this->duplicatedInOrderCounterStorageEnabled)) {
                assignInOrderExecInfoToEvent(signalEvent);
            } else {
                signalEvent->unsetInOrderExecInfo();
            }
        } else {
            auto incrementValue = signalEvent->getInOrderIncrementValue(1);
            auto currentUsage = signalEvent->getInOrderExecEventHelper().getAggregatedEventUsageCounter();

            if ((currentUsage + incrementValue) > signalEvent->getInOrderExecBaseSignalValue()) {
                signalEvent->getInOrderExecEventHelper().resetAggregatedEventUsageCounter();
            }
            signalEvent->getInOrderExecEventHelper().addAggregatedEventUsageCounter(incrementValue);
        }
        signalEvent->setDualCopyOffload(getCopyOffloadModeForOperation(copyOffloadOperation) == CopyOffloadModes::dualStream);
    }

    this->latestOperationRequiredNonWalkerInOrderCmdsChaining = nonWalkerInOrderCmdsChaining;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::handleInOrderCounterOverflow(bool copyOffloadOperation) {
    if (!isQwordInOrderCounter() && ((inOrderExecInfo->getCounterValue() + 1) == std::numeric_limits<uint32_t>::max())) {
        CommandListCoreFamily<gfxCoreFamily>::appendWaitOnInOrderDependency(inOrderExecInfo->getDeviceCounterAllocation(), inOrderExecInfo->getBaseDeviceAddress(), inOrderExecInfo->getNumDevicePartitionsToWait(),
                                                                            nullptr, inOrderExecInfo->getCounterValue() + 1, inOrderExecInfo->getAllocationOffset(), false, true, false, false,
                                                                            isDualStreamCopyOffloadOperation(copyOffloadOperation));

        uint32_t newOffset = 0;
        if (inOrderExecInfo->getAllocationOffset() == 0) {
            // multitile immediate writes are uint64_t aligned
            newOffset = alignUp(this->partitionCount * device->getL0GfxCoreHelper().getImmediateWritePostSyncOffset(), MemoryConstants::cacheLineSize * 4);
            UNRECOVERABLE_IF(newOffset == 0);
        }

        inOrderExecInfo->setAllocationOffset(newOffset);
        inOrderExecInfo->resetCounterValue();
        inOrderExecInfo->initializeAllocationsFromHost();

        CommandListCoreFamily<gfxCoreFamily>::appendSignalInOrderDependencyCounter(nullptr, copyOffloadOperation, false, false, false); // signal counter on new offset
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::handlePostSubmissionState() {
    this->commandContainer.clearResidencyContainer();
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::containsSystemAllocation(const NEO::ResidencyContainer &residencyContainer) {
    for (const auto &allocation : residencyContainer) {
        if (allocation != nullptr && allocation->getAllocationType() == NEO::AllocationType::bufferHostMemory) {
            return true;
        }
    }
    return false;
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::containsExternalAllocation(const NEO::ResidencyContainer &residencyContainer) {
    for (const auto &allocation : residencyContainer) {
        if (allocation != nullptr && allocation->getIsImported()) {
            return true;
        }
    }
    return false;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::initialize(Device *device, NEO::EngineGroupType engineGroupType,
                                                             ze_command_list_flags_t flags) {
    this->device = device;
    this->commandListPreemptionMode = device->getDevicePreemptionMode();
    this->engineGroupType = engineGroupType;
    this->flags = flags;

    auto &hwInfo = device->getHwInfo();
    auto neoDevice = device->getNEODevice();
    auto &rootDeviceEnvironment = neoDevice->getRootDeviceEnvironment();
    auto &productHelper = rootDeviceEnvironment.getHelper<NEO::ProductHelper>();
    auto &gfxCoreHelper = neoDevice->getGfxCoreHelper();
    const auto &releaseHelper = neoDevice->getReleaseHelper();
    auto &l0GfxCoreHelper = device->getL0GfxCoreHelper();
    auto &compilerProductHelper = neoDevice->getCompilerProductHelper();
    auto gmmHelper = rootDeviceEnvironment.getGmmHelper();

    this->dcFlushSupport = NEO::MemorySynchronizationCommands<GfxFamily>::getDcFlushEnable(true, rootDeviceEnvironment);
    this->systolicModeSupport = NEO::PreambleHelper<GfxFamily>::isSystolicModeConfigurable(rootDeviceEnvironment);
    this->stateComputeModeTracking = L0GfxCoreHelper::enableStateComputeModeTracking(rootDeviceEnvironment);
    this->frontEndStateTracking = L0GfxCoreHelper::enableFrontEndStateTracking(rootDeviceEnvironment);
    this->pipelineSelectStateTracking = L0GfxCoreHelper::enablePipelineSelectStateTracking(rootDeviceEnvironment);
    this->stateBaseAddressTracking = L0GfxCoreHelper::enableStateBaseAddressTracking(rootDeviceEnvironment);
    this->pipeControlMultiKernelEventSync = L0GfxCoreHelper::usePipeControlMultiKernelEventSync(hwInfo);
    this->signalAllEventPackets = L0GfxCoreHelper::useSignalAllEventPackets(hwInfo);
    this->dynamicHeapRequired = NEO::EncodeDispatchKernel<GfxFamily>::isDshNeeded(device->getDeviceInfo());
    this->doubleSbaWa = productHelper.isAdditionalStateBaseAddressWARequired(hwInfo);
    this->defaultMocsIndex = (gmmHelper->getL3EnabledMOCS() >> 1);
    this->l1CachePolicyData.init(productHelper);
    this->cmdListHeapAddressModel = L0GfxCoreHelper::getHeapAddressModel(rootDeviceEnvironment);
    this->dummyBlitWa.rootDeviceEnvironment = &(neoDevice->getRootDeviceEnvironmentRef());
    this->dispatchCmdListBatchBufferAsPrimary = L0GfxCoreHelper::dispatchCmdListBatchBufferAsPrimary(true);
    this->useOnlyGlobalTimestamps = gfxCoreHelper.useOnlyGlobalTimestamps();
    this->maxFillPatternSizeForCopyEngine = l0GfxCoreHelper.getMaxFillPatternSizeForCopyEngine();
    this->heaplessModeEnabled = compilerProductHelper.isHeaplessModeEnabled(hwInfo);
    this->requiredStreamState.initSupport(rootDeviceEnvironment);
    this->finalStreamState.initSupport(rootDeviceEnvironment);
    this->duplicatedInOrderCounterStorageEnabled = gfxCoreHelper.duplicatedInOrderCounterStorageEnabled();
    this->inOrderAtomicSignalingEnabled = gfxCoreHelper.inOrderAtomicSignallingEnabled();
    this->scratchAddressPatchingEnabled = (this->heaplessModeEnabled && !isImmediateType());
    this->copyOperationFenceSupported = (isCopyOnly(false) || isCopyOffloadEnabled()) && productHelper.isDeviceToHostCopySignalingFenceRequired();
    this->defaultPipelinedThreadArbitrationPolicy = gfxCoreHelper.getDefaultThreadArbitrationPolicy();
    this->implicitSynchronizedDispatchForCooperativeKernelsAllowed = l0GfxCoreHelper.implicitSynchronizedDispatchForCooperativeKernelsAllowed();
    this->maxLocalSubRegionSize = productHelper.getMaxLocalSubRegionSize(hwInfo);
    this->l3FlushAfterPostSyncEnabled = productHelper.isL3FlushAfterPostSyncSupported();
    this->systemMemoryFenceInPostSyncRequired = productHelper.isGlobalFenceInPostSyncRequired(hwInfo);
    this->compactL3FlushEventPacket = L0GfxCoreHelper::useCompactL3FlushEventPacket(hwInfo, this->l3FlushAfterPostSyncEnabled);
    this->useAdditionalBlitProperties = productHelper.useAdditionalBlitProperties();
    this->isPostImageWriteFlushRequired = releaseHelper.isPostImageWriteFlushRequired();
    this->isPreImageReadFlushRequired = releaseHelper.isPreImageReadFlushRequired();
    this->shouldRegisterEnqueuedWalkerWithProfiling = this->device->getNEODevice()->getProductHelper().shouldRegisterEnqueuedWalkerWithProfiling();
    this->isWalkerPostSyncSkipEnabled = gfxCoreHelper.isWalkerPostSyncSkipEnabled(this->dcFlushSupport);
    this->statelessBuiltinsEnabled = compilerProductHelper.isForceToStatelessRequired();
    this->defaultBuiltInMode = compilerProductHelper.getDefaultBuiltInAddressingMode(
        NEO::ApiSpecificConfig::getBindlessMode(*neoDevice));
    this->swTagsEnabled = NEO::debugManager.flags.EnableSWTags.get();
    this->setupPatchPreambleEnabled(false);
    this->commandContainer.doubleSbaWaRef() = this->doubleSbaWa;
    this->commandContainer.l1CachePolicyDataRef() = &this->l1CachePolicyData;
    this->commandContainer.setHeapAddressModel(this->cmdListHeapAddressModel);
    if (isImmediateType()) {
        this->commandContainer.setImmediateCmdListCsr(getCsr(false));
    }
    this->commandContainer.setStateBaseAddressTracking(this->stateBaseAddressTracking);
    this->commandContainer.setUsingPrimaryBuffer(this->dispatchCmdListBatchBufferAsPrimary);

    if (device->isImplicitScalingCapable() && !this->internalUsage && !isCopyOnly(false)) {
        this->partitionCount = static_cast<uint32_t>(neoDevice->getDeviceBitfield().count());
    }

    if (isImmediateType()) {
        commandContainer.setFlushTaskUsedForImmediate(true);
        commandContainer.setNumIddPerBlock(1);
        this->setupFlushMethod(rootDeviceEnvironment);
    }

    if (this->immediateCmdListHeapSharing) {
        commandContainer.enableHeapSharing();
    }

    commandContainer.setReservedSshSize(getReserveSshSize());
    commandContainer.setEstimatedNumberOfCommands(this->estimatedNumberOfCommands);

    auto createSecondaryCmdBufferInHostMem = isImmediateType() &&
                                             !device->isImplicitScalingCapable() &&
                                             getCsr(false) &&
                                             getCsr(false)->isAnyDirectSubmissionEnabled() &&
                                             !neoDevice->getExecutionEnvironment()->areMetricsEnabled() &&
                                             neoDevice->getMemoryManager()->isLocalMemorySupported(neoDevice->getRootDeviceIndex());

    if (NEO::debugManager.flags.DirectSubmissionFlatRingBuffer.get() != -1) {
        createSecondaryCmdBufferInHostMem = isImmediateType() && static_cast<bool>(NEO::debugManager.flags.DirectSubmissionFlatRingBuffer.get());
    }

    auto returnValue = commandContainer.initialize(device->getActiveDevice(),
                                                   device->allocationsForReuse.get(),
                                                   NEO::EncodeStates<GfxFamily>::getSshHeapSize(),
                                                   !isCopyOnly(false),
                                                   createSecondaryCmdBufferInHostMem);
    if (!this->pipelineSelectStateTracking) {
        // allow systolic support set in container when tracking disabled
        // setting systolic support allows dispatching untracked command in legacy mode
        commandContainer.systolicModeSupportRef() = this->systolicModeSupport;
    }

    ze_result_t returnType = (returnValue == NEO::CommandContainer::ErrorCode::outOfDeviceMemory)
                                 ? ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY
                                 : ZE_RESULT_SUCCESS;
    if (returnType == ZE_RESULT_SUCCESS) {
        if (!isCopyOnly(false)) {
            postInitComputeSetup();
        }
    }

    if (this->flags & ZE_COMMAND_LIST_FLAG_IN_ORDER) {
        enableInOrderExecution();
    }

    if (NEO::debugManager.flags.ForceSynchronizedDispatchMode.get() != -1) {
        enableSynchronizedDispatch((NEO::debugManager.flags.ForceSynchronizedDispatchMode.get() == 1) ? NEO::SynchronizedDispatchMode::full : NEO::SynchronizedDispatchMode::disabled);
    }

    const bool copyOffloadSupported = l0GfxCoreHelper.isDefaultCmdListWithCopyOffloadSupported(this->useAdditionalBlitProperties);

    if (copyOffloadSupported && !this->internalUsage && !isCopyOffloadEnabled()) {
        enableCopyOperationOffload();
    }

    enableBcsSplit();

    this->minimalSizeForBcsSplit = getDefaultMinBcsSplitSize();

    if (NEO::debugManager.flags.SplitBcsSize.get() != -1) {
        this->minimalSizeForBcsSplit = NEO::debugManager.flags.SplitBcsSize.get() * MemoryConstants::kiloByte;
    }

    if (isImmediateType() == false) {
        if ((this->flags & ZE_COMMAND_LIST_FLAG_ENABLE_CMD_VISITING) || NEO::debugManager.flags.ExperimentalFlatCommandListApiRecording.get()) {
            this->flags |= ZE_COMMAND_LIST_FLAG_ENABLE_CMD_VISITING;
            this->flatCapture = std::make_unique<RecordedApiCommands>();
        }
    }

    return returnType;
}
template <GFXCORE_FAMILY gfxCoreFamily>
size_t CommandListCoreFamily<gfxCoreFamily>::getDefaultMinBcsSplitSize() const {
    return 4 * MemoryConstants::megaByte;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::close() {
    commandContainer.removeDuplicatesFromResidencyContainer();
    if (this->dispatchCmdListBatchBufferAsPrimary) {
        commandContainer.endAlignedPrimaryBuffer();
    } else {
        NEO::EncodeBatchBufferStartOrEnd<GfxFamily>::programBatchBufferEnd(commandContainer);
    }

    for (auto &cmdList : this->subCmdListsForRecordedBcsSplit) {
        cmdList->close();
    }

    closedCmdList = true;

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::programL3(bool isSLMused) {}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::prefetchKernelMemory(NEO::LinearStream &cmdStream, const Kernel &kernel, const NEO::GraphicsAllocation *iohAllocation, size_t iohOffset, CommandToPatchContainer *outListCommands,
                                                                uint64_t cmdId, size_t estimateSizeForPrefetch) {
    if (!kernelMemoryPrefetchEnabled()) {
        return;
    }

    auto &rootExecEnv = device->getNEODevice()->getRootDeviceEnvironment();

    auto currentCmdStreamPtr = cmdStream.getSpace(0);
    auto cmdStreamOffset = cmdStream.getUsed();

    NEO::EncodeMemoryPrefetch<GfxFamily>::programMemoryPrefetch(cmdStream, *iohAllocation, kernel.getIndirectSize(), iohOffset, rootExecEnv);

    auto isaPrefetchSizeLimit = CommandList::getLimitIsaPrefetchSize();
    auto isaSizeToPrefetch = std::min(kernel.getImmutableData()->getIsaSize(), isaPrefetchSizeLimit);
    NEO::EncodeMemoryPrefetch<GfxFamily>::programMemoryPrefetch(cmdStream, *kernel.getIsaAllocation(), isaSizeToPrefetch, kernel.getIsaOffsetInParentAllocation(), rootExecEnv);

    auto cmdStreamSizeConsumed = cmdStream.getUsed() - cmdStreamOffset;
    if (estimateSizeForPrefetch > cmdStreamSizeConsumed) {
        auto deltaSize = estimateSizeForPrefetch - cmdStreamSizeConsumed;
        NEO::EncodeNoop<GfxFamily>::emitNoop(cmdStream, deltaSize);
    }

    // estimateSizeForPrefetch > 0 - only when mutable command list is in isa mutation mode - not importnant, when for example, only wait events are recorded
    // outListCommands != nullptr - only during appendLaunchKernel - so when recording commands and not mutating them.
    if (estimateSizeForPrefetch > 0 && outListCommands != nullptr) {
        outListCommands->push_back(PatchPrefetchKernelMemory{
            .pDestination = currentCmdStreamPtr,
            .offset = iohOffset,
            .patchSize = cmdStream.getUsed() - cmdStreamOffset,
        });
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
std::optional<SWTagScope<typename NEO::GfxFamilyMapper<gfxCoreFamily>::GfxFamily>>
CommandListCoreFamily<gfxCoreFamily>::emplaceSWTagScope(const char *callName) {
    if (this->swTagsEnabled) {
        return std::make_optional<SWTagScope<GfxFamily>>(
            *device->getNEODevice(),
            *commandContainer.getCommandStream(),
            callName);
    }
    return std::nullopt;
}

template <GFXCORE_FAMILY gfxCoreFamily>
uint32_t CommandListCoreFamily<gfxCoreFamily>::getIohSizeForPrefetch(const Kernel &kernel, uint32_t reserveExtraSpace) const {
    return kernel.getIndirectSize() + reserveExtraSpace;
}

template <GFXCORE_FAMILY gfxCoreFamily>
std::pair<NEO::GraphicsAllocation *, size_t> CommandListCoreFamily<gfxCoreFamily>::getIohAllocationAndOffsetForPrefetch(const Kernel &kernel, uint32_t reserveExtraSpace, bool isThreadDataMapAllowed) {
    auto ioh = commandContainer.getHeapWithRequiredSizeAndAlignment(NEO::IndirectHeapType::indirectObject, getIohSizeForPrefetch(kernel, reserveExtraSpace), GfxFamily::indirectDataAlignment);
    if (isThreadDataMapAllowed) {
        uint32_t inlineDataProgrammingOffset = 0u;
        if (NEO::EncodeDispatchKernel<GfxFamily>::inlineDataProgrammingRequired(kernel.getKernelDescriptor())) {
            constexpr uint32_t inlineDataSize = GfxFamily::DefaultWalkerType::getInlineDataSize();
            inlineDataProgrammingOffset = std::min(inlineDataSize, kernel.getCrossThreadDataSize());
        }
        const std::span<const uint8_t> crossThreadSpan(kernel.getCrossThreadData() + inlineDataProgrammingOffset, kernel.getCrossThreadDataSize() - inlineDataProgrammingOffset);
        const std::span<const uint8_t> perThreadSpan(kernel.getPerThreadData(), kernel.getPerThreadDataSizeForWholeThreadGroup());
        auto cachedIohOffset = commandContainer.getCachedIohOffset(crossThreadSpan, perThreadSpan);
        if (cachedIohOffset) {
            auto cacheStorage = commandContainer.getThreadDataMapStorage();
            return {cacheStorage->getGraphicsAllocation(), static_cast<size_t>(*cachedIohOffset - cacheStorage->getHeapGpuStartOffset())};
        }
    }
    return {ioh->getGraphicsAllocation(), ioh->getUsed()};
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernel(ze_kernel_handle_t kernelHandle,
                                                                     const ze_group_count_t &threadGroupDimensions,
                                                                     ze_event_handle_t hEvent,
                                                                     uint32_t numWaitEvents,
                                                                     ze_event_handle_t *phWaitEvents,
                                                                     CmdListKernelLaunchParams &launchParams) {

    auto swTagScope = emplaceSWTagScope(launchParams.isCooperative ? "zeCommandListAppendLaunchCooperativeKernel" : "zeCommandListAppendLaunchKernel");

    auto kernel = Kernel::fromHandle(kernelHandle);

    auto result = validateLaunchParams(*kernel, launchParams, threadGroupDimensions);
    if (result != ZE_RESULT_SUCCESS) {
        return result;
    }
    {
        const bool isThreadDataMapAllowed = commandContainer.getIOHCacheEnabled() && (kernel->getIndirectSize() != 0u) && (kernel->getImplicitArgs() == nullptr);
        if (isThreadDataMapAllowed) {
            this->patchKernelProperties(launchParams, *kernel, threadGroupDimensions);
        }
        auto [iohAllocation, iohOffset] = getIohAllocationAndOffsetForPrefetch(*kernel, launchParams.reserveExtraPayloadSpace, isThreadDataMapAllowed);
        auto estimateSizeForPrefetch = ensureCmdBufferSpaceForPrefetch();
        prefetchKernelMemory(*commandContainer.getCommandStream(), *kernel, iohAllocation, iohOffset, launchParams.outListCommands, getPrefetchCmdId(), estimateSizeForPrefetch);
    }

    CmdListWaitEventParameters waitEventsParameters = {
        .outWaitCmds = launchParams.outListCommands,
        .relaxedOrderingAllowed = launchParams.relaxedOrderingDispatch,
        .trackDependencies = true,
        .waitForImplicitInOrderDependency = true,
        .skipAddingWaitEventsToResidency = launchParams.omitAddingWaitEventsResidency,
        .dualStreamCopyOffloadOperation = false,
    };

    ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, waitEventsParameters);
    if (ret) {
        return ret;
    }

    if (launchParams.isCooperative && this->implicitSynchronizedDispatchForCooperativeKernelsAllowed) {
        enableSynchronizedDispatch(NEO::SynchronizedDispatchMode::full);
    }

    appendSynchronizedDispatchInitializationSection();

    Event *event = nullptr;
    if (hEvent) {
        event = Event::fromHandle(hEvent);
        if (!launchParams.isKernelSplitOperation) {
            event->resetKernelCountAndPacketUsedCount();
        }

        registerWalkerWithProfilingEnqueued(event);
    }

    if (!handleCounterBasedEventOperations(event, launchParams.omitAddingEventResidency)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    auto res = appendLaunchKernelWithParams(kernel, threadGroupDimensions, event, launchParams);

    handleInOrderDependencyCounter(event, launchParams.inOrderNonWalkerSignalingRequired && event && !event->isCounterBased(), false);

    if (this->synchronizedDispatchMode != NEO::SynchronizedDispatchMode::disabled) {
        appendSynchronizedDispatchCleanupSection();
    }

    addToMappedEventList(event);

    return res;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernelIndirect(ze_kernel_handle_t kernelHandle,
                                                                             const ze_group_count_t &pDispatchArgumentsBuffer,
                                                                             ze_event_handle_t hEvent,
                                                                             uint32_t numWaitEvents,
                                                                             ze_event_handle_t *phWaitEvents, bool relaxedOrderingDispatch) {

    CmdListWaitEventParameters waitEventsParameters = {
        .outWaitCmds = nullptr,
        .relaxedOrderingAllowed = relaxedOrderingDispatch,
        .trackDependencies = true,
        .waitForImplicitInOrderDependency = true,
        .skipAddingWaitEventsToResidency = false,
        .dualStreamCopyOffloadOperation = false,
    };
    ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, waitEventsParameters);
    if (ret) {
        return ret;
    }

    appendSynchronizedDispatchInitializationSection();

    CmdListKernelLaunchParams launchParams = {};
    Event *event = nullptr;
    if (hEvent) {
        event = Event::fromHandle(hEvent);
        if (Kernel::fromHandle(kernelHandle)->getPrintfBufferAllocation() != nullptr) {
            Kernel *kernel = Kernel::fromHandle(kernelHandle);
            auto module = static_cast<const ModuleImp *>(&static_cast<KernelImp *>(kernel)->getParentModule());
            event->setKernelForPrintf(module->getPrintfKernelWeakPtr(kernelHandle));
            event->setKernelWithPrintfDeviceMutex(kernel->getDevicePrintfKernelMutex());
        }
        launchParams.isHostSignalScopeEvent = event->isSignalScope(ZE_EVENT_SCOPE_FLAG_HOST);
        registerWalkerWithProfilingEnqueued(event);
    }

    if (!handleCounterBasedEventOperations(event, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    appendEventForProfiling(event, nullptr, true, false, false, false);
    launchParams.isIndirect = true;
    launchParams.relaxedOrderingDispatch = relaxedOrderingDispatch;
    ret = appendLaunchKernelWithParams(Kernel::fromHandle(kernelHandle), pDispatchArgumentsBuffer,
                                       nullptr, launchParams);
    addToMappedEventList(event);
    appendSignalEventPostWalker(event, nullptr, nullptr, false, false, false);

    handleInOrderDependencyCounter(event, isInOrderNonWalkerSignalingRequired(event), false);

    appendSynchronizedDispatchCleanupSection();

    return ret;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendLaunchMultipleKernelsIndirect(uint32_t numKernels,
                                                                                      const ze_kernel_handle_t *kernelHandles,
                                                                                      const uint32_t *pNumLaunchArguments,
                                                                                      const ze_group_count_t *pLaunchArgumentsBuffer,
                                                                                      ze_event_handle_t hEvent,
                                                                                      uint32_t numWaitEvents,
                                                                                      ze_event_handle_t *phWaitEvents, bool relaxedOrderingDispatch) {

    CmdListWaitEventParameters waitEventsParameters = {
        .outWaitCmds = nullptr,
        .relaxedOrderingAllowed = relaxedOrderingDispatch,
        .trackDependencies = true,
        .waitForImplicitInOrderDependency = true,
        .skipAddingWaitEventsToResidency = false,
        .dualStreamCopyOffloadOperation = false,
    };
    ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, waitEventsParameters);
    if (ret) {
        return ret;
    }

    appendSynchronizedDispatchInitializationSection();

    CmdListKernelLaunchParams launchParams = {};
    launchParams.isIndirect = true;
    launchParams.isPredicate = true;
    launchParams.relaxedOrderingDispatch = relaxedOrderingDispatch;

    Event *event = nullptr;
    if (hEvent) {
        event = Event::fromHandle(hEvent);
        launchParams.isHostSignalScopeEvent = event->isSignalScope(ZE_EVENT_SCOPE_FLAG_HOST);
        registerWalkerWithProfilingEnqueued(event);
    }

    if (!handleCounterBasedEventOperations(event, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    appendEventForProfiling(event, nullptr, true, false, false, false);

    auto countBufferAllocData = resolveAlignedAllocation(this->device, pNumLaunchArguments, sizeof(uint32_t), nullptr, {});
    commandContainer.addToResidencyContainer(countBufferAllocData.alloc);
    auto countBufferGpuAddress = ptrOffset(countBufferAllocData.alignedAllocationPtr, countBufferAllocData.offset);

    for (uint32_t i = 0; i < numKernels; i++) {
        NEO::EncodeMathMMIO<GfxFamily>::encodeGreaterThanPredicate(commandContainer, countBufferGpuAddress, i, isCopyOnly(false));
        launchParams.isKernelPatched = false;
        ret = appendLaunchKernelWithParams(Kernel::fromHandle(kernelHandles[i]),
                                           pLaunchArgumentsBuffer[i],
                                           nullptr, launchParams);
        if (ret) {
            return ret;
        }
    }
    addToMappedEventList(event);
    appendSignalEventPostWalker(event, nullptr, nullptr, false, false, false);

    appendSynchronizedDispatchCleanupSection();

    return ret;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernelWithArguments(ze_kernel_handle_t hKernel,
                                                                                  const ze_group_count_t groupCounts,
                                                                                  const ze_group_size_t groupSizes,
                                                                                  void **pArguments,
                                                                                  const void *pNext,
                                                                                  ze_event_handle_t hSignalEvent,
                                                                                  uint32_t numWaitEvents,
                                                                                  ze_event_handle_t *phWaitEvents) {

    auto kernel = L0::Kernel::fromHandle(hKernel);
    auto result = CommandList::setKernelState(kernel, groupSizes, pArguments);
    if (result != ZE_RESULT_SUCCESS) {
        return result;
    }
    return this->appendLaunchKernelWithParameters(hKernel, &groupCounts, pNext, hSignalEvent, numWaitEvents, phWaitEvents);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernelWithParameters(
    ze_kernel_handle_t hKernel,
    const ze_group_count_t *pGroupCounts,
    const void *pNext,
    ze_event_handle_t hSignalEvent,
    uint32_t numWaitEvents,
    ze_event_handle_t *phWaitEvents) {

    if (hKernel == nullptr) {
        return ZE_RESULT_ERROR_INVALID_NULL_HANDLE;
    }

    auto kernel = L0::Kernel::fromHandle(hKernel);
    if (kernel == nullptr) {
        return ZE_RESULT_ERROR_INVALID_NULL_HANDLE;
    }

    if (pGroupCounts == nullptr) {
        return ZE_RESULT_ERROR_INVALID_NULL_POINTER;
    }
    if ((phWaitEvents == nullptr) && (numWaitEvents > 0)) {
        return ZE_RESULT_ERROR_INVALID_SIZE;
    }

    L0::CmdListKernelLaunchParams launchParams = {};

    auto result = this->obtainLaunchParamsFromExtensions(reinterpret_cast<const ze_base_desc_t *>(pNext), launchParams, hKernel);
    if (result != ZE_RESULT_SUCCESS) {
        return result;
    }

    return this->appendLaunchKernel(hKernel, *pGroupCounts, hSignalEvent, numWaitEvents, phWaitEvents, launchParams);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendEventReset(ze_event_handle_t hEvent) {
    auto event = Event::fromHandle(hEvent);

    event->disableImplicitCounterBasedMode();

    if (event->isCounterBased()) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    NEO::Device *neoDevice = device->getNEODevice();
    auto swTagScope = emplaceSWTagScope("zeCommandListAppendEventReset");

    if (this->isInOrderExecutionEnabled()) {
        handleInOrderImplicitDependencies(isRelaxedOrderingDispatchAllowed(0, false), false);
    }

    appendSynchronizedDispatchInitializationSection();

    event->resetPackets(false);
    event->disableHostCaching(!isImmediateType());
    commandContainer.addToResidencyContainer(event->getAllocation(this->device));

    // default state of event is single packet, handle case when reset is used 1st, launchkernel 2nd - just reset all packets then, use max
    bool useMaxPackets = event->isEventTimestampFlagSet() || (event->getPacketsInUse() < this->partitionCount);

    bool appendPipeControlWithPostSync = (!isCopyOnly(false)) && (event->isSignalScope() || event->isEventTimestampFlagSet());
    dispatchEventPostSyncOperation(event, nullptr, nullptr, Event::STATE_CLEARED, false, useMaxPackets, appendPipeControlWithPostSync, false, isCopyOnly(false));

    if (!isCopyOnly(false)) {
        if (this->partitionCount > 1) {
            appendMultiTileBarrier(*neoDevice);
        }
    }

    if (this->isInOrderExecutionEnabled()) {
        appendSignalInOrderDependencyCounter(event, false, false, false, false);
    }
    handleInOrderDependencyCounter(event, false, false);
    event->unsetInOrderExecInfo();

    appendSynchronizedDispatchCleanupSection();

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryRangesBarrier(uint32_t numRanges,
                                                                            const size_t *pRangeSizes,
                                                                            const void **pRanges,
                                                                            ze_event_handle_t hSignalEvent,
                                                                            uint32_t numWaitEvents,
                                                                            ze_event_handle_t *phWaitEvents,
                                                                            CmdListWaitEventParameters &waitEventParams) {
    ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, waitEventParams);
    if (ret) {
        return ret;
    }

    appendSynchronizedDispatchInitializationSection();

    Event *signalEvent = nullptr;
    if (hSignalEvent) {
        signalEvent = Event::fromHandle(hSignalEvent);
    }

    if (!handleCounterBasedEventOperations(signalEvent, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    appendEventForProfiling(signalEvent, nullptr, true, false, false, isCopyOnly(false));
    applyMemoryRangesBarrier(numRanges, pRangeSizes, pRanges);
    appendSignalEventPostWalker(signalEvent, nullptr, nullptr, false, false, isCopyOnly(false));
    addToMappedEventList(signalEvent);

    if (this->isInOrderExecutionEnabled()) {
        appendSignalInOrderDependencyCounter(signalEvent, false, false, false, false);
    }
    handleInOrderDependencyCounter(signalEvent, false, false);

    appendSynchronizedDispatchCleanupSection();

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendImageCopyFromMemory(ze_image_handle_t hDstImage, const void *srcPtr, const ze_image_region_t *pDstRegion, ze_event_handle_t hEvent,
                                                                            uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {
    return appendImageCopyFromMemoryExt(hDstImage, srcPtr, pDstRegion, 0, 0,
                                        hEvent, numWaitEvents, phWaitEvents, memoryCopyParams);
}

static ze_image_region_t getRegionFromImageDesc(ze_image_desc_t imgDesc) {
    ze_image_region_t region{0,
                             0,
                             0,
                             static_cast<uint32_t>(imgDesc.width),
                             static_cast<uint32_t>(imgDesc.height),
                             static_cast<uint32_t>(imgDesc.depth)};
    // If this is a 1D or 2D image, then the height or depth is ignored and must be set to 1.
    // Internally, all dimensions must be >= 1.
    if (imgDesc.type == ZE_IMAGE_TYPE_1D) {
        region.height = 1;
    } else if (imgDesc.type == ZE_IMAGE_TYPE_1DARRAY) {
        region.height = imgDesc.arraylevels;
    }
    if (imgDesc.type == ZE_IMAGE_TYPE_2DARRAY) {
        region.depth = imgDesc.arraylevels;
    } else if (imgDesc.type != ZE_IMAGE_TYPE_3D) {
        region.depth = 1;
    }
    return region;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendImageCopyFromMemoryExt(ze_image_handle_t hDstImage, const void *srcPtr, const ze_image_region_t *pDstRegion, uint32_t srcRowPitch, uint32_t srcSlicePitch,
                                                                               ze_event_handle_t hEvent, uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {
    if (!hDstImage) {
        return ZE_RESULT_ERROR_INVALID_NULL_HANDLE;
    }
    if (!srcPtr) {
        return ZE_RESULT_ERROR_INVALID_NULL_POINTER;
    }

    bool sharedSystemEnabled = isSharedSystemEnabled();

    auto image = Image::fromHandle(hDstImage);
    const auto &imgInfo = image->getImageInfo();
    auto bytesPerPixel = static_cast<uint32_t>(imgInfo.surfaceFormat->imageElementSizeInBytes);
    Vec3<size_t> imgSize = {image->getImageDesc().width,
                            image->getImageDesc().height,
                            image->getImageDesc().depth};
    if (image->getImageDesc().type == ZE_IMAGE_TYPE_1DARRAY) {
        imgSize.y = image->getImageDesc().arraylevels;
    }
    if (image->getImageDesc().type == ZE_IMAGE_TYPE_2DARRAY) {
        imgSize.z = image->getImageDesc().arraylevels;
    }

    Event *event = nullptr;
    if (hEvent) {
        event = Event::fromHandle(hEvent);
    }

    ze_image_region_t tmpRegion;
    if (pDstRegion == nullptr) {
        tmpRegion = getRegionFromImageDesc(image->getImageDesc());
        pDstRegion = &tmpRegion;
    }

    if (srcRowPitch == 0) {
        if (image->getCustomRowPitch() > 0) {
            srcRowPitch = static_cast<uint32_t>(image->getCustomRowPitch());
        } else if (image->isMimickedImage()) {
            uint32_t srcBytesPerPixel = bytesPerPixel;
            if (bytesPerPixel == 8) {
                srcBytesPerPixel = 6;
            }
            if (bytesPerPixel == 4) {
                srcBytesPerPixel = 3;
            }
            srcRowPitch = pDstRegion->width * srcBytesPerPixel;
        } else {
            srcRowPitch = pDstRegion->width * bytesPerPixel;
        }
    }
    uint64_t srcSlicePitchCalculated = srcSlicePitch;
    if (srcSlicePitch == 0) {
        if (image->getCustomSlicePitch() > 0) {
            srcSlicePitchCalculated = image->getCustomSlicePitch();
        } else {
            uint64_t height = (imgInfo.imgDesc.imageType == NEO::ImageType::image1DArray ? 1 : pDstRegion->height);
            srcSlicePitchCalculated = height * srcRowPitch;
        }
    }

    uint64_t bufferSize = getInputBufferSize(imgInfo.imgDesc.imageType, srcRowPitch, srcSlicePitchCalculated, pDstRegion, bytesPerPixel);

    auto allocationStruct = resolveAlignedAllocation(this->device, srcPtr, bufferSize, nullptr, {.sharedSystemEnabled = sharedSystemEnabled, .hostCopyAllowed = true});
    if (allocationStruct.alloc == nullptr && sharedSystemEnabled == false) {
        return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
    }

    bool remoteCopy = isRemoteAlloc(allocationStruct.svmAllocData);

    memoryCopyParams.taskCountUpdateRequired |= CommandList::isExternalHostPtrAlloc(allocationStruct.alloc);

    emitMemAdviseForSystemCopy(allocationStruct, bufferSize);

    DriverHandle *driverHandle = device->getDriverHandle();
    if (driverHandle->isRemoteImageNeeded(image, device)) {
        L0::Image *peerImage = nullptr;

        ze_result_t ret = driverHandle->getPeerImage(device, image, &peerImage);
        if (ret != ZE_RESULT_SUCCESS) {
            return ret;
        }
        image = peerImage;
        remoteCopy = true;
    }

    bool imageToBuffer = (imgInfo.imgDesc.imageType == NEO::ImageType::image1DBuffer);
    bool shouldUseCopyOffload = this->isCopyOffloadForFillOrStagingPreferred(imageToBuffer);
    memoryCopyParams.copyOffloadAllowed = isCopyOffloadAllowed(allocationStruct.alloc, image->getAllocation(), remoteCopy, imageToBuffer, imgInfo.imgDesc.numMipLevels) && shouldUseCopyOffload;

    if (isCopyOnly(memoryCopyParams.copyOffloadAllowed)) {
        if ((bytesPerPixel == 3) || (bytesPerPixel == 6) || image->isMimickedImage()) {
            return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
        }
        size_t imgRowPitch = imgInfo.rowPitch;
        size_t imgSlicePitch = imgInfo.slicePitch;
        this->commandContainer.addToResidencyContainer(allocationStruct.alloc);
        auto ptr = ptrOffset(allocationStruct.alignedAllocationPtr, allocationStruct.offset);
        Vec3<size_t> region = {pDstRegion->width, pDstRegion->height, pDstRegion->depth};
        Vec3<size_t> imgSizeBlit = imgSize;
        Vec3<size_t> dstOffsets = {pDstRegion->originX, pDstRegion->originY, pDstRegion->originZ};
        auto bpp = bytesPerPixel;
        if (imageToBuffer) {
            dstOffsets.x *= bpp;
            region.x *= bpp;
            imgSizeBlit.x *= bpp;
            bpp = 1;
        }
        auto status = appendCopyImageBlit(ptr, nullptr, ptrOffset(image->getAllocation()->getGpuAddress(), imgInfo.offset), image->getAllocation(),
                                          {0, 0, 0}, dstOffsets, srcRowPitch, srcSlicePitchCalculated,
                                          imgRowPitch, imgSlicePitch, bpp, region, region, imgSizeBlit,
                                          event, numWaitEvents, phWaitEvents, memoryCopyParams, imageToBuffer);
        addToMappedEventList(Event::fromHandle(hEvent));
        return status;
    }

    auto builtInMode = this->defaultBuiltInMode;
    device->getL0GfxCoreHelper().adjustBuiltInImageModeToBindful(builtInMode);
    builtInMode.adjustToWideStatelessIfRequired(bufferSize);
    ImageBuiltIn imageBuiltIn = ImageBuiltIn::copyBufferToImage3dBytes;

    switch (bytesPerPixel) {
    case 1u:
        imageBuiltIn = ImageBuiltIn::copyBufferToImage3dBytes;
        break;
    case 2u:
        imageBuiltIn = ImageBuiltIn::copyBufferToImage3d2Bytes;
        break;
    case 4u:
        if (image->isMimickedImage()) {
            imageBuiltIn = ImageBuiltIn::copyBufferToImage3d3To4Bytes;
        } else {
            imageBuiltIn = ImageBuiltIn::copyBufferToImage3d4Bytes;
        }
        break;
    case 8u:
        if (image->isMimickedImage()) {
            imageBuiltIn = ImageBuiltIn::copyBufferToImage3d6To8Bytes;
        } else {
            imageBuiltIn = ImageBuiltIn::copyBufferToImage3d8Bytes;
        }
        break;
    case 16u: {
        bool isSrc16BytesAligned = isAligned<16>(allocationStruct.alignedAllocationPtr, allocationStruct.offset,
                                                 srcRowPitch, srcSlicePitchCalculated);
        if (isSrc16BytesAligned) {
            imageBuiltIn = ImageBuiltIn::copyBufferToImage3d16BytesAligned;
        } else {
            imageBuiltIn = ImageBuiltIn::copyBufferToImage3d16Bytes;
        }
        break;
    }
    default:
        UNRECOVERABLE_IF(true);
        break;
    }

    auto lock = device->getBuiltinFunctionsLib()->obtainUniqueOwnership();
    Kernel *builtinKernel = device->getBuiltinFunctionsLib()->getImageFunction(imageBuiltIn, builtInMode);

    builtinSetArg(builtinKernel, 0, allocationStruct.alignedAllocationPtr, allocationStruct.alloc);
    builtinKernel->setArgRedescribedImage(1u, image->toHandle(), false, getRegionMipLevel(pDstRegion, imgInfo.imgDesc.numMipLevels));
    builtinKernel->setArgumentValue(2u, sizeof(size_t), &allocationStruct.offset);

    uint32_t origin[] = {pDstRegion->originX,
                         pDstRegion->originY,
                         pDstRegion->originZ,
                         0u};
    builtinKernel->setArgumentValue(3u, sizeof(origin), &origin);

    if (!builtInMode.wideMode) {
        uint32_t pitch[] = {srcRowPitch, static_cast<uint32_t>(srcSlicePitchCalculated)};
        builtinKernel->setArgumentValue(4u, sizeof(pitch), &pitch);
    } else {
        size_t pitch[] = {srcRowPitch, srcSlicePitchCalculated};
        builtinKernel->setArgumentValue(4u, sizeof(pitch), &pitch);
    }

    uint32_t groupSizeX = pDstRegion->width;
    uint32_t groupSizeY = pDstRegion->height;
    uint32_t groupSizeZ = pDstRegion->depth;

    ze_result_t ret = builtinKernel->suggestGroupSize(groupSizeX, groupSizeY, groupSizeZ,
                                                      &groupSizeX, &groupSizeY, &groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    ret = builtinKernel->setGroupSize(groupSizeX, groupSizeY, groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    if (pDstRegion->width % groupSizeX || pDstRegion->height % groupSizeY || pDstRegion->depth % groupSizeZ) {
        CREATE_DEBUG_STRING(str, "Invalid group size {%d, %d, %d} specified\n", groupSizeX, groupSizeY, groupSizeZ);
        driverHandle->setErrorDescription(std::string(str.get()));
        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "Invalid group size {%d, %d, %d} specified\n",
                     groupSizeX, groupSizeY, groupSizeZ);
        DEBUG_BREAK_IF(true);
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    ze_group_count_t kernelArgs{pDstRegion->width / groupSizeX, pDstRegion->height / groupSizeY,
                                pDstRegion->depth / groupSizeZ};

    CmdListKernelLaunchParams launchParams = {};
    launchParams.isBuiltInKernel = true;
    launchParams.relaxedOrderingDispatch = memoryCopyParams.relaxedOrderingDispatch;
    launchParams.outListCommands = memoryCopyParams.waitEventsParameters.outWaitCmds;
    launchParams.omitAddingWaitEventsResidency = memoryCopyParams.waitEventsParameters.skipAddingWaitEventsToResidency;

    auto status = CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernel(builtinKernel->toHandle(), kernelArgs,
                                                                           event, numWaitEvents, phWaitEvents,
                                                                           launchParams);
    addToMappedEventList(Event::fromHandle(hEvent));

    return status;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendImageCopyToMemory(void *dstPtr,
                                                                          ze_image_handle_t hSrcImage,
                                                                          const ze_image_region_t *pSrcRegion,
                                                                          ze_event_handle_t hEvent,
                                                                          uint32_t numWaitEvents,
                                                                          ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {
    return appendImageCopyToMemoryExt(dstPtr, hSrcImage, pSrcRegion, 0, 0,
                                      hEvent, numWaitEvents, phWaitEvents, memoryCopyParams);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendImageCopyToMemoryExt(void *dstPtr,
                                                                             ze_image_handle_t hSrcImage,
                                                                             const ze_image_region_t *pSrcRegion,
                                                                             uint32_t destRowPitch,
                                                                             uint32_t destSlicePitch,
                                                                             ze_event_handle_t hEvent,
                                                                             uint32_t numWaitEvents,
                                                                             ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {
    if (!dstPtr) {
        return ZE_RESULT_ERROR_INVALID_NULL_POINTER;
    }
    if (!hSrcImage) {
        return ZE_RESULT_ERROR_INVALID_NULL_HANDLE;
    }

    bool sharedSystemEnabled = isSharedSystemEnabled();

    auto image = Image::fromHandle(hSrcImage);
    const auto &imgInfo = image->getImageInfo();
    auto bytesPerPixel = static_cast<uint32_t>(imgInfo.surfaceFormat->imageElementSizeInBytes);
    Vec3<size_t> imgSize = {image->getImageDesc().width,
                            image->getImageDesc().height,
                            image->getImageDesc().depth};
    if (image->getImageDesc().type == ZE_IMAGE_TYPE_1DARRAY) {
        imgSize.y = image->getImageDesc().arraylevels;
    }
    if (image->getImageDesc().type == ZE_IMAGE_TYPE_2DARRAY) {
        imgSize.z = image->getImageDesc().arraylevels;
    }

    Event *event = nullptr;
    if (hEvent) {
        event = Event::fromHandle(hEvent);
    }

    ze_image_region_t tmpRegion;
    if (pSrcRegion == nullptr) {
        tmpRegion = getRegionFromImageDesc(image->getImageDesc());
        pSrcRegion = &tmpRegion;
    }

    if (destRowPitch == 0) {
        if (image->getCustomRowPitch() > 0) {
            destRowPitch = static_cast<uint32_t>(image->getCustomRowPitch());
        } else if (image->isMimickedImage()) {
            uint32_t destBytesPerPixel = bytesPerPixel;
            if (bytesPerPixel == 8) {
                destBytesPerPixel = 6;
            }
            if (bytesPerPixel == 4) {
                destBytesPerPixel = 3;
            }
            destRowPitch = pSrcRegion->width * destBytesPerPixel;
        } else {
            destRowPitch = pSrcRegion->width * bytesPerPixel;
        }
    }
    uint64_t destSlicePitchCalculated = destSlicePitch;
    if (destSlicePitch == 0) {
        if (image->getCustomSlicePitch() > 0) {
            destSlicePitchCalculated = image->getCustomSlicePitch();
        } else {
            uint64_t height = (imgInfo.imgDesc.imageType == NEO::ImageType::image1DArray ? 1 : pSrcRegion->height);
            destSlicePitchCalculated = height * destRowPitch;
        }
    }

    uint64_t bufferSize = getInputBufferSize(imgInfo.imgDesc.imageType, destRowPitch, destSlicePitchCalculated, pSrcRegion, bytesPerPixel);

    auto allocationStruct = resolveAlignedAllocation(this->device, dstPtr, bufferSize, nullptr, {.sharedSystemEnabled = sharedSystemEnabled});
    if (allocationStruct.alloc == nullptr && sharedSystemEnabled == false) {
        return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
    }

    memoryCopyParams.taskCountUpdateRequired |= CommandList::isExternalHostPtrAlloc(allocationStruct.alloc);

    emitMemAdviseForSystemCopy(allocationStruct, bufferSize);

    bool remoteCopy = isRemoteAlloc(allocationStruct.svmAllocData);

    DriverHandle *driverHandle = device->getDriverHandle();
    if (driverHandle->isRemoteImageNeeded(image, device)) {
        L0::Image *peerImage = nullptr;

        ze_result_t ret = driverHandle->getPeerImage(device, image, &peerImage);
        if (ret != ZE_RESULT_SUCCESS) {
            return ret;
        }
        image = peerImage;
        remoteCopy = true;
    }

    bool imageToBuffer = true;
    bool shouldUseCopyOffload = this->isCopyOffloadForFillOrStagingPreferred(imageToBuffer);
    memoryCopyParams.copyOffloadAllowed = isCopyOffloadAllowed(image->getAllocation(), allocationStruct.alloc, remoteCopy, false, imgInfo.imgDesc.numMipLevels) && shouldUseCopyOffload;

    if (isCopyOnly(memoryCopyParams.copyOffloadAllowed)) {
        if ((bytesPerPixel == 3) || (bytesPerPixel == 6) || image->isMimickedImage()) {
            return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
        }
        size_t imgRowPitch = imgInfo.rowPitch;
        size_t imgSlicePitch = imgInfo.slicePitch;
        this->commandContainer.addToResidencyContainer(allocationStruct.alloc);
        auto ptr = ptrOffset(allocationStruct.alignedAllocationPtr, allocationStruct.offset);
        Vec3<size_t> srcOffsets = {pSrcRegion->originX, pSrcRegion->originY, pSrcRegion->originZ};
        Vec3<size_t> region = {pSrcRegion->width, pSrcRegion->height, pSrcRegion->depth};
        Vec3<size_t> imgSizeBlit = imgSize;
        auto bpp = bytesPerPixel;
        bool isImageFromBuffer = (imgInfo.imgDesc.imageType == NEO::ImageType::image1DBuffer);
        if (isImageFromBuffer) {
            srcOffsets.x *= bpp;
            region.x *= bpp;
            imgSizeBlit.x *= bpp;
            bpp = 1;
        }
        auto status = appendCopyImageBlit(ptrOffset(image->getAllocation()->getGpuAddress(), imgInfo.offset), image->getAllocation(), ptr, nullptr,
                                          srcOffsets, {0, 0, 0}, imgRowPitch, imgSlicePitch,
                                          destRowPitch, destSlicePitchCalculated, bpp, region,
                                          imgSizeBlit, region, event, numWaitEvents, phWaitEvents, memoryCopyParams, isImageFromBuffer);
        addToMappedEventList(event);
        return status;
    }

    auto builtInMode = this->defaultBuiltInMode;
    device->getL0GfxCoreHelper().adjustBuiltInImageModeToBindful(builtInMode);
    builtInMode.adjustToWideStatelessIfRequired(bufferSize);
    ImageBuiltIn imageBuiltIn = ImageBuiltIn::copyImage3dToBufferBytes;

    switch (bytesPerPixel) {
    case 1u:
        imageBuiltIn = ImageBuiltIn::copyImage3dToBufferBytes;
        break;
    case 2u:
        imageBuiltIn = ImageBuiltIn::copyImage3dToBuffer2Bytes;
        break;
    case 3u:
        imageBuiltIn = ImageBuiltIn::copyImage3dToBuffer3Bytes;
        break;
    case 4u:
        if (image->isMimickedImage()) {
            imageBuiltIn = ImageBuiltIn::copyImage3dToBuffer4To3Bytes;
        } else {
            imageBuiltIn = ImageBuiltIn::copyImage3dToBuffer4Bytes;
        }
        break;
    case 6u:
        imageBuiltIn = ImageBuiltIn::copyImage3dToBuffer6Bytes;
        break;
    case 8u:
        if (image->isMimickedImage()) {
            imageBuiltIn = ImageBuiltIn::copyImage3dToBuffer8To6Bytes;
        } else {
            imageBuiltIn = ImageBuiltIn::copyImage3dToBuffer8Bytes;
        }
        break;
    case 16u: {
        bool isDst16BytesAligned = isAligned<16>(allocationStruct.alignedAllocationPtr, allocationStruct.offset,
                                                 destRowPitch, destSlicePitchCalculated);
        if (isDst16BytesAligned) {
            imageBuiltIn = ImageBuiltIn::copyImage3dToBuffer16BytesAligned;
        } else {
            imageBuiltIn = ImageBuiltIn::copyImage3dToBuffer16Bytes;
        }

        break;
    }
    default: {
        CREATE_DEBUG_STRING(str, "Invalid bytesPerPixel of size: %u\n", bytesPerPixel);
        driverHandle->setErrorDescription(std::string(str.get()));
        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "invalid bytesPerPixel of size: %u\n", bytesPerPixel);
        UNRECOVERABLE_IF(true);
        break;
    }
    }

    auto lock = device->getBuiltinFunctionsLib()->obtainUniqueOwnership();
    Kernel *builtinKernel = device->getBuiltinFunctionsLib()->getImageFunction(imageBuiltIn, builtInMode);

    builtinKernel->setArgRedescribedImage(0u, image->toHandle(), false, getRegionMipLevel(pSrcRegion, imgInfo.imgDesc.numMipLevels));
    builtinSetArg(builtinKernel, 1, allocationStruct.alignedAllocationPtr, allocationStruct.alloc);
    uint32_t origin[] = {pSrcRegion->originX,
                         pSrcRegion->originY,
                         pSrcRegion->originZ,
                         0u};
    builtinKernel->setArgumentValue(2u, sizeof(origin), &origin);
    builtinKernel->setArgumentValue(3u, sizeof(size_t), &allocationStruct.offset);

    if (!builtInMode.wideMode) {
        uint32_t pitch[] = {destRowPitch, static_cast<uint32_t>(destSlicePitchCalculated)};
        builtinKernel->setArgumentValue(4u, sizeof(pitch), &pitch);
    } else {
        size_t pitch[] = {destRowPitch, destSlicePitchCalculated};
        builtinKernel->setArgumentValue(4u, sizeof(pitch), &pitch);
    }

    uint32_t groupSizeX = pSrcRegion->width;
    uint32_t groupSizeY = pSrcRegion->height;
    uint32_t groupSizeZ = pSrcRegion->depth;

    ze_result_t ret = builtinKernel->suggestGroupSize(groupSizeX, groupSizeY, groupSizeZ,
                                                      &groupSizeX, &groupSizeY, &groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    ret = builtinKernel->setGroupSize(groupSizeX, groupSizeY, groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    if (pSrcRegion->width % groupSizeX || pSrcRegion->height % groupSizeY || pSrcRegion->depth % groupSizeZ) {
        CREATE_DEBUG_STRING(str, "Invalid group size {%d, %d, %d} specified\n", groupSizeX, groupSizeY, groupSizeZ);
        driverHandle->setErrorDescription(std::string(str.get()));
        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "Invalid group size {%d, %d, %d} specified\n",
                     groupSizeX, groupSizeY, groupSizeZ);
        DEBUG_BREAK_IF(true);
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    ze_group_count_t kernelArgs{pSrcRegion->width / groupSizeX, pSrcRegion->height / groupSizeY,
                                pSrcRegion->depth / groupSizeZ};

    CmdListKernelLaunchParams launchParams = {};
    launchParams.isBuiltInKernel = true;
    if (allocationStruct.alloc == nullptr) {
        launchParams.isDestinationAllocationInSystemMemory = true;
        launchParams.isDestinationAllocationImported = false;
    } else {
        auto dstAllocationType = allocationStruct.alloc->getAllocationType();
        launchParams.isDestinationAllocationInSystemMemory =
            (dstAllocationType == NEO::AllocationType::bufferHostMemory) ||
            (dstAllocationType == NEO::AllocationType::externalHostPtr);

        if constexpr (checkIfAllocationImportedRequired()) {
            launchParams.isDestinationAllocationImported = allocationStruct.alloc->getIsImported();
        }
    }
    launchParams.relaxedOrderingDispatch = memoryCopyParams.relaxedOrderingDispatch;
    launchParams.outListCommands = memoryCopyParams.waitEventsParameters.outWaitCmds;
    launchParams.omitAddingWaitEventsResidency = memoryCopyParams.waitEventsParameters.skipAddingWaitEventsToResidency;
    ret = CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernel(builtinKernel->toHandle(), kernelArgs,
                                                                   event, numWaitEvents, phWaitEvents, launchParams);
    addToMappedEventList(event);
    return ret;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendImageCopyRegion(ze_image_handle_t hDstImage,
                                                                        ze_image_handle_t hSrcImage,
                                                                        const ze_image_region_t *pDstRegion,
                                                                        const ze_image_region_t *pSrcRegion,
                                                                        ze_event_handle_t hEvent,
                                                                        uint32_t numWaitEvents,
                                                                        ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {
    auto dstImage = L0::Image::fromHandle(hDstImage);
    auto srcImage = L0::Image::fromHandle(hSrcImage);
    cl_int4 srcOffset, dstOffset;

    ze_image_region_t srcRegion, dstRegion;

    if (pSrcRegion != nullptr) {
        srcRegion = *pSrcRegion;
    } else {
        ze_image_desc_t srcDesc = srcImage->getImageDesc();
        srcRegion = {0, 0, 0, static_cast<uint32_t>(srcDesc.width), srcDesc.height, srcDesc.depth};
    }

    srcOffset.x = static_cast<cl_int>(srcRegion.originX);
    srcOffset.y = static_cast<cl_int>(srcRegion.originY);
    srcOffset.z = static_cast<cl_int>(srcRegion.originZ);
    srcOffset.w = 0;

    if (pDstRegion != nullptr) {
        dstRegion = *pDstRegion;
    } else {
        ze_image_desc_t dstDesc = dstImage->getImageDesc();
        dstRegion = {0, 0, 0, static_cast<uint32_t>(dstDesc.width), dstDesc.height, dstDesc.depth};
    }

    dstOffset.x = static_cast<cl_int>(dstRegion.originX);
    dstOffset.y = static_cast<cl_int>(dstRegion.originY);
    dstOffset.z = static_cast<cl_int>(dstRegion.originZ);
    dstOffset.w = 0;

    if (srcRegion.width != dstRegion.width ||
        srcRegion.height != dstRegion.height ||
        srcRegion.depth != dstRegion.depth) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    uint32_t groupSizeX = srcRegion.width;
    uint32_t groupSizeY = srcRegion.height;
    uint32_t groupSizeZ = srcRegion.depth;

    Event *event = nullptr;
    if (hEvent) {
        event = Event::fromHandle(hEvent);
    }

    DriverHandle *driverHandle = device->getDriverHandle();

    bool remoteCopy = false;

    if (driverHandle->isRemoteImageNeeded(dstImage, device)) {
        L0::Image *peerImage = nullptr;

        ze_result_t ret = driverHandle->getPeerImage(device, dstImage, &peerImage);
        if (ret != ZE_RESULT_SUCCESS) {
            return ret;
        }
        dstImage = peerImage;
        remoteCopy = true;
    }

    if (driverHandle->isRemoteImageNeeded(srcImage, device)) {
        L0::Image *peerImage = nullptr;

        ze_result_t ret = driverHandle->getPeerImage(device, srcImage, &peerImage);
        if (ret != ZE_RESULT_SUCCESS) {
            return ret;
        }
        srcImage = peerImage;
        remoteCopy = true;
    }

    const auto &srcImgInfo = srcImage->getImageInfo();
    const auto &dstImgInfo = dstImage->getImageInfo();

    bool imageToBuffer = (srcImgInfo.imgDesc.imageType == NEO::ImageType::image1DBuffer) ||
                         (dstImgInfo.imgDesc.imageType == NEO::ImageType::image1DBuffer);
    bool shouldUseCopyOffload = this->isCopyOffloadForFillOrStagingPreferred(imageToBuffer);
    memoryCopyParams.copyOffloadAllowed = isCopyOffloadAllowed(srcImage->getAllocation(), dstImage->getAllocation(), remoteCopy, imageToBuffer, srcImgInfo.imgDesc.numMipLevels) && shouldUseCopyOffload;

    if (isCopyOnly(memoryCopyParams.copyOffloadAllowed)) {
        auto bytesPerPixel = static_cast<uint32_t>(srcImgInfo.surfaceFormat->imageElementSizeInBytes);

        ze_image_region_t region = getRegionFromImageDesc(srcImage->getImageDesc());
        Vec3<size_t> srcImgSize = {region.width, region.height, region.depth};

        region = getRegionFromImageDesc(dstImage->getImageDesc());
        Vec3<size_t> dstImgSize = {region.width, region.height, region.depth};

        auto srcRowPitch = srcImgInfo.rowPitch;
        auto srcSlicePitch =
            (srcImgInfo.imgDesc.imageType == NEO::ImageType::image1DArray ? 1 : srcRegion.height) * srcRowPitch;

        auto dstRowPitch = dstImgInfo.rowPitch;
        auto dstSlicePitch =
            (dstImgInfo.imgDesc.imageType == NEO::ImageType::image1DArray ? 1 : dstRegion.height) * dstRowPitch;

        bool src1DBuffer = (srcImgInfo.imgDesc.imageType == NEO::ImageType::image1DBuffer);
        bool dst1DBuffer = (dstImgInfo.imgDesc.imageType == NEO::ImageType::image1DBuffer);
        bool isImageFromBuffer = src1DBuffer || dst1DBuffer;
        Vec3<size_t> srcOff = {srcRegion.originX, srcRegion.originY, srcRegion.originZ};
        Vec3<size_t> dstOff = {dstRegion.originX, dstRegion.originY, dstRegion.originZ};
        Vec3<size_t> copySz = {srcRegion.width, srcRegion.height, srcRegion.depth};
        auto bpp = bytesPerPixel;
        if (isImageFromBuffer) {
            if (src1DBuffer) {
                srcOff.x *= bpp;
                srcImgSize.x *= bpp;
            }
            if (dst1DBuffer) {
                dstOff.x *= bpp;
                dstImgSize.x *= bpp;
            }
            copySz.x *= bpp;
            bpp = 1;
        }
        auto status = appendCopyImageBlit(ptrOffset(srcImage->getAllocation()->getGpuAddress(), srcImgInfo.offset), srcImage->getAllocation(), ptrOffset(dstImage->getAllocation()->getGpuAddress(), dstImgInfo.offset), dstImage->getAllocation(),
                                          srcOff, dstOff, srcRowPitch, srcSlicePitch,
                                          dstRowPitch, dstSlicePitch, bpp, copySz, srcImgSize, dstImgSize,
                                          event, numWaitEvents, phWaitEvents, memoryCopyParams, isImageFromBuffer);
        addToMappedEventList(event);
        return status;
    }

    ImageBuiltIn imageBuiltIn = ImageBuiltIn::copyImageRegion;
    auto builtInMode = this->defaultBuiltInMode;

    auto lock = device->getBuiltinFunctionsLib()->obtainUniqueOwnership();
    auto kernel = device->getBuiltinFunctionsLib()->getImageFunction(imageBuiltIn, builtInMode);

    ze_result_t ret = kernel->suggestGroupSize(groupSizeX, groupSizeY, groupSizeZ, &groupSizeX,
                                               &groupSizeY, &groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    ret = kernel->setGroupSize(groupSizeX, groupSizeY, groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    if (srcRegion.width % groupSizeX || srcRegion.height % groupSizeY || srcRegion.depth % groupSizeZ) {
        CREATE_DEBUG_STRING(str, "Invalid group size {%d, %d, %d} specified\n", groupSizeX, groupSizeY, groupSizeZ);
        driverHandle->setErrorDescription(std::string(str.get()));
        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "Invalid group size {%d, %d, %d} specified\n",
                     groupSizeX, groupSizeY, groupSizeZ);
        DEBUG_BREAK_IF(true);
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    ze_group_count_t kernelArgs{srcRegion.width / groupSizeX, srcRegion.height / groupSizeY,
                                srcRegion.depth / groupSizeZ};

    const bool isPackedFormat = NEO::ImageHelper::areImagesCompatibleWithPackedFormat(device->getProductHelper(), srcImgInfo, dstImgInfo, srcImage->getAllocation(), dstImage->getAllocation(), srcRegion.width);

    kernel->setArgRedescribedImage(0, srcImage->toHandle(), isPackedFormat, getRegionMipLevel(pSrcRegion, srcImgInfo.imgDesc.numMipLevels));
    kernel->setArgRedescribedImage(1, dstImage->toHandle(), isPackedFormat, getRegionMipLevel(pDstRegion, dstImgInfo.imgDesc.numMipLevels));

    kernel->setArgumentValue(2, sizeof(srcOffset), &srcOffset);
    kernel->setArgumentValue(3, sizeof(dstOffset), &dstOffset);

    CmdListKernelLaunchParams launchParams = {};
    launchParams.isBuiltInKernel = true;
    launchParams.relaxedOrderingDispatch = memoryCopyParams.relaxedOrderingDispatch;
    launchParams.outListCommands = memoryCopyParams.waitEventsParameters.outWaitCmds;
    launchParams.omitAddingWaitEventsResidency = memoryCopyParams.waitEventsParameters.skipAddingWaitEventsToResidency;
    auto status = CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernel(kernel->toHandle(), kernelArgs,
                                                                           event, numWaitEvents, phWaitEvents,
                                                                           launchParams);
    addToMappedEventList(event);

    return status;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendImageCopy(ze_image_handle_t hDstImage,
                                                                  ze_image_handle_t hSrcImage,
                                                                  ze_event_handle_t hEvent,
                                                                  uint32_t numWaitEvents,
                                                                  ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {

    return this->appendImageCopyRegion(hDstImage, hSrcImage, nullptr, nullptr, hEvent,
                                       numWaitEvents, phWaitEvents, memoryCopyParams);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemAdvise(ze_device_handle_t hDevice,
                                                                  const void *ptr, size_t size,
                                                                  ze_memory_advice_t advice) {

    this->memAdviseOperations.emplace_back(hDevice, ptr, size, advice);

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::executeMemAdvise(ze_device_handle_t hDevice,
                                                                   const void *ptr, size_t size,
                                                                   ze_memory_advice_t advice) {

    auto driverHandle = device->getDriverHandle();
    auto callingNEODevice = device->getNEODevice();
    auto allocData = driverHandle->getSvmAllocsManager()->getSVMAlloc(ptr);

    if (!allocData) {
        if (callingNEODevice->areSharedSystemAllocationsAllowed()) {

            Device *targetDevice = L0::Device::fromHandle(hDevice);
            auto targetNEODevice = targetDevice->getNEODevice();

            if (!targetNEODevice->areSharedSystemAllocationsAllowed()) {
                return ZE_RESULT_ERROR_INVALID_ARGUMENT;
            }

            NEO::MemAdvise memAdviseOp = NEO::MemAdvise::invalidAdvise;

            switch (advice) {
            case ZE_MEMORY_ADVICE_SET_PREFERRED_LOCATION:
                memAdviseOp = NEO::MemAdvise::setPreferredLocation;
                break;
            case ZE_MEMORY_ADVICE_CLEAR_PREFERRED_LOCATION:
                memAdviseOp = NEO::MemAdvise::clearPreferredLocation;
                break;
            case ZE_MEMORY_ADVICE_SET_SYSTEM_MEMORY_PREFERRED_LOCATION:
                memAdviseOp = NEO::MemAdvise::setSystemMemoryPreferredLocation;
                break;
            case ZE_MEMORY_ADVICE_CLEAR_SYSTEM_MEMORY_PREFERRED_LOCATION:
                memAdviseOp = NEO::MemAdvise::clearSystemMemoryPreferredLocation;
                break;
            case ZE_MEMORY_ADVICE_SET_READ_MOSTLY:
            case ZE_MEMORY_ADVICE_CLEAR_READ_MOSTLY:
            case ZE_MEMORY_ADVICE_BIAS_CACHED:
            case ZE_MEMORY_ADVICE_BIAS_UNCACHED:
            case ZE_MEMORY_ADVICE_SET_NON_ATOMIC_MOSTLY:
            case ZE_MEMORY_ADVICE_CLEAR_NON_ATOMIC_MOSTLY:
            default:
                return ZE_RESULT_SUCCESS;
            }

            auto unifiedMemoryManager = driverHandle->getSvmAllocsManager();

            if (!unifiedMemoryManager->sharedSystemMemAdvise(*callingNEODevice, *targetNEODevice, memAdviseOp, ptr, size)) {
                return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
            }

            return ZE_RESULT_SUCCESS;
        } else {
            return ZE_RESULT_ERROR_INVALID_ARGUMENT;
        }
    }

    NEO::MemAdviseFlags flags{};
    Device *adviceDevice = L0::Device::fromHandle(hDevice);

    if (adviceDevice->memAdviseSharedAllocations.find(allocData) != adviceDevice->memAdviseSharedAllocations.end()) {
        flags = adviceDevice->memAdviseSharedAllocations[allocData];
    }

    const auto currentFlags = flags;

    switch (advice) {
    case ZE_MEMORY_ADVICE_SET_READ_MOSTLY:
        flags.readOnly = 1;
        break;
    case ZE_MEMORY_ADVICE_CLEAR_READ_MOSTLY:
        flags.readOnly = 0;
        break;
    case ZE_MEMORY_ADVICE_SET_PREFERRED_LOCATION:
        flags.devicePreferredLocation = 1;
        break;
    case ZE_MEMORY_ADVICE_CLEAR_PREFERRED_LOCATION:
        flags.devicePreferredLocation = 0;
        break;
    case ZE_MEMORY_ADVICE_SET_SYSTEM_MEMORY_PREFERRED_LOCATION:
        flags.systemPreferredLocation = 1;
        break;
    case ZE_MEMORY_ADVICE_CLEAR_SYSTEM_MEMORY_PREFERRED_LOCATION:
        flags.systemPreferredLocation = 0;
        break;
    case ZE_MEMORY_ADVICE_BIAS_CACHED:
        flags.cachedMemory = 1;
        break;
    case ZE_MEMORY_ADVICE_BIAS_UNCACHED:
        flags.cachedMemory = 0;
        break;
    case ZE_MEMORY_ADVICE_SET_NON_ATOMIC_MOSTLY:
    case ZE_MEMORY_ADVICE_CLEAR_NON_ATOMIC_MOSTLY:
    default:
        break;
    }

    auto memoryManager = device->getDriverHandle()->getMemoryManager();
    auto pageFaultManager = memoryManager->getPageFaultManager();

    if (pageFaultManager) {
        /* If Read Only and Device Preferred Hints have been cleared, then cpu_migration of Shared memory can be re-enabled*/
        if (flags.cpuMigrationBlocked) {
            if (flags.readOnly == 0 && flags.devicePreferredLocation == 0) {
                pageFaultManager->protectCPUMemoryAccess(const_cast<void *>(ptr), size);
                flags.cpuMigrationBlocked = 0;
            }
        }
        /* Given MemAdvise hints, use different gpu Domain Handler for the Page Fault Handling */
        pageFaultManager->setGpuDomainHandler(L0::transferAndUnprotectMemoryWithHints);
    }

    if (currentFlags.allFlags == flags.allFlags) {
        return ZE_RESULT_SUCCESS;
    }

    auto alloc = allocData->gpuAllocations.getGraphicsAllocation(adviceDevice->getRootDeviceIndex());
    memoryManager->setMemAdvise(alloc, flags, adviceDevice->getRootDeviceIndex());

    adviceDevice->memAdviseSharedAllocations[allocData] = flags;

    return ZE_RESULT_SUCCESS;
}

static inline void builtinSetArg(Kernel *builtinKernel, uint32_t argIndex, uintptr_t argPtr, NEO::GraphicsAllocation *allocation) {
    if (allocation) {
        builtinKernel->setArgBufferWithAlloc(argIndex, argPtr, allocation, nullptr);
    } else {
        builtinKernel->setArgumentValue(argIndex, sizeof(argPtr), &argPtr);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopyKernelWithGA(uintptr_t dstPtr,
                                                                               NEO::GraphicsAllocation *dstPtrAlloc,
                                                                               uint64_t dstOffset,
                                                                               uintptr_t srcPtr,
                                                                               NEO::GraphicsAllocation *srcPtrAlloc,
                                                                               uint64_t srcOffset,
                                                                               uint64_t size,
                                                                               uint64_t elementSize,
                                                                               BufferBuiltIn builtin,
                                                                               const NEO::BuiltIn::AddressingMode &builtInMode,
                                                                               Event *signalEvent,
                                                                               CmdListKernelLaunchParams &launchParams) {

    auto lock = device->getBuiltinFunctionsLib()->obtainUniqueOwnership();

    if (signalEvent) {
        registerWalkerWithProfilingEnqueued(signalEvent);
    }

    Kernel *builtinKernel = nullptr;

    builtinKernel = device->getBuiltinFunctionsLib()->getFunction(builtin, builtInMode);

    uint32_t groupSizeX = builtinKernel->getImmutableData()
                              ->getDescriptor()
                              .kernelAttributes.simdSize;
    uint32_t groupSizeY = 1u;
    uint32_t groupSizeZ = 1u;

    ze_result_t ret = builtinKernel->setGroupSize(groupSizeX, groupSizeY, groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    builtinSetArg(builtinKernel, 0, dstPtr, dstPtrAlloc);
    builtinSetArg(builtinKernel, 1, srcPtr, srcPtrAlloc);

    uint64_t elems = size / elementSize;
    builtinKernel->setArgumentValue(2, sizeof(elems), &elems);
    builtinKernel->setArgumentValue(3, sizeof(dstOffset), &dstOffset);
    builtinKernel->setArgumentValue(4, sizeof(srcOffset), &srcOffset);

    uint32_t groups = static_cast<uint32_t>((size + ((static_cast<uint64_t>(groupSizeX) * elementSize) - 1)) / (static_cast<uint64_t>(groupSizeX) * elementSize));
    ze_group_count_t dispatchKernelArgs{groups, 1u, 1u};

    launchParams.isBuiltInKernel = true;
    if (dstPtrAlloc) {
        auto dstAllocationType = dstPtrAlloc->getAllocationType();
        launchParams.isDestinationAllocationInSystemMemory = this->isUsingSystemAllocation(dstAllocationType);
        if constexpr (checkIfAllocationImportedRequired()) {
            launchParams.isDestinationAllocationImported = dstPtrAlloc->getIsImported();
        }
    } else {
        launchParams.isDestinationAllocationInSystemMemory = true;
    }
    return CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernelSplit(builtinKernel, dispatchKernelArgs, signalEvent, launchParams);
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isHighPriorityImmediateCmdList() const {
    return (this->isImmediateType() && getCsr(false)->getOsContext().isHighPriority());
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopyBlit(uintptr_t dstPtr,
                                                                       NEO::GraphicsAllocation *dstPtrAlloc,
                                                                       uint64_t dstOffset, uintptr_t srcPtr,
                                                                       NEO::GraphicsAllocation *srcPtrAlloc,
                                                                       uint64_t srcOffset,
                                                                       uint64_t size,
                                                                       Event *signalEvent,
                                                                       CmdListMemoryCopyParams &memoryCopyParams) {
    if (dstPtrAlloc) {
        dstOffset += ptrDiff<uintptr_t>(dstPtr, dstPtrAlloc->getGpuAddress());
        dstPtr = dstPtrAlloc->getGpuAddress();
    }
    if (srcPtrAlloc) {
        srcOffset += ptrDiff<uintptr_t>(srcPtr, srcPtrAlloc->getGpuAddress());
        srcPtr = srcPtrAlloc->getGpuAddress();
    }

    auto clearColorAllocation = device->getNEODevice()->getDefaultEngine().commandStreamReceiver->getClearColorAllocation();
    auto blitProperties = NEO::BlitProperties::constructPropertiesForCopy(
        dstPtrAlloc, dstPtr,
        srcPtrAlloc, srcPtr,
        {dstOffset, 0, 0}, {srcOffset, 0, 0}, {size, 0, 0},
        0, 0, 0, 0, clearColorAllocation);
    blitProperties.computeStreamPartitionCount = this->partitionCount;
    blitProperties.highPriority = isHighPriorityImmediateCmdList();

    setAdditionalBlitPropertiesFromMemoryCopyParams(blitProperties, memoryCopyParams);

    addResidency(dstPtrAlloc, srcPtrAlloc, clearColorAllocation);

    if (useAdditionalBlitProperties) {
        size_t nBlitsPerRow = NEO::BlitCommandsHelper<GfxFamily>::getNumberOfBlitsForCopyPerRow(blitProperties.copySize, device->getNEODevice()->getRootDeviceEnvironmentRef(), blitProperties.isSystemMemoryPoolUsed);
        bool useAdditionalTimestamp = nBlitsPerRow > 1;
        setAdditionalBlitProperties(blitProperties, signalEvent, memoryCopyParams.forceAggregatedEventIncValue, useAdditionalTimestamp);
    }

    NEO::BlitCommandsHelper<GfxFamily>::dispatchBlitCommandsForBufferPerRow(blitProperties, *commandContainer.getCommandStream(), *this->dummyBlitWa.rootDeviceEnvironment);

    if (arePostBlitWACmdsRequired()) {
        NEO::BlitCommandsHelper<GfxFamily>::dispatchPostBlitWaCommands(*commandContainer.getCommandStream(), *this->dummyBlitWa.rootDeviceEnvironment);
    }
    dummyBlitWa.isWaRequired = true;
    if (useAdditionalBlitProperties && signalEvent && signalEvent->isSignalWithUserInterrupt()) {
        NEO::EncodeUserInterrupt<GfxFamily>::encode(*commandContainer.getCommandStream());
    }
    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopyBlitRegion(AlignedAllocationData *srcAllocationData,
                                                                             AlignedAllocationData *dstAllocationData,
                                                                             ze_copy_region_t srcRegion,
                                                                             ze_copy_region_t dstRegion, const Vec3<size_t> &copySize,
                                                                             size_t srcRowPitch, size_t srcSlicePitch,
                                                                             size_t dstRowPitch, size_t dstSlicePitch,
                                                                             const Vec3<size_t> &srcSize, const Vec3<size_t> &dstSize,
                                                                             Event *signalEvent,
                                                                             uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents,
                                                                             CmdListMemoryCopyParams &memoryCopyParams) {
    uint64_t srcPtr = srcAllocationData->alignedAllocationPtr;
    uint64_t dstPtr = dstAllocationData->alignedAllocationPtr;
    if (srcAllocationData->alloc) {
        srcRegion.originX += getRegionOffsetForAppendMemoryCopyBlitRegion(srcAllocationData);
        srcPtr = srcAllocationData->alloc->getGpuAddress();
    }
    if (dstAllocationData->alloc) {
        dstRegion.originX += getRegionOffsetForAppendMemoryCopyBlitRegion(dstAllocationData);
        dstPtr = dstAllocationData->alloc->getGpuAddress();
    }
    uint32_t bytesPerPixel = NEO::BlitCommandsHelper<GfxFamily>::getAvailableBytesPerPixel(copySize.x, srcRegion.originX, dstRegion.originX, srcSize.x, dstSize.x);
    Vec3<size_t> srcPtrOffset = {srcRegion.originX / bytesPerPixel, srcRegion.originY, srcRegion.originZ};
    Vec3<size_t> dstPtrOffset = {dstRegion.originX / bytesPerPixel, dstRegion.originY, dstRegion.originZ};
    auto clearColorAllocation = device->getNEODevice()->getDefaultEngine().commandStreamReceiver->getClearColorAllocation();

    Vec3<size_t> copySizeModified = {copySize.x / bytesPerPixel, copySize.y, copySize.z};
    srcRowPitch = srcRowPitch ? srcRowPitch : copySizeModified.x;
    dstRowPitch = dstRowPitch ? dstRowPitch : copySizeModified.x;
    auto blitProperties = NEO::BlitProperties::constructPropertiesForCopy(
        dstAllocationData->alloc, dstPtr,
        srcAllocationData->alloc, srcPtr,
        dstPtrOffset, srcPtrOffset, copySizeModified,
        srcRowPitch, srcSlicePitch, dstRowPitch, dstSlicePitch, clearColorAllocation);

    this->addResidency(dstAllocationData->alloc, srcAllocationData->alloc, clearColorAllocation);

    blitProperties.computeStreamPartitionCount = this->partitionCount;
    blitProperties.highPriority = isHighPriorityImmediateCmdList();
    blitProperties.bytesPerPixel = bytesPerPixel;
    blitProperties.srcSize = srcSize;
    blitProperties.dstSize = dstSize;
    memoryCopyParams.waitEventsParameters.waitForImplicitInOrderDependency = true;
    ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, memoryCopyParams.waitEventsParameters);
    if (ret) {
        return ret;
    }

    if (!handleCounterBasedEventOperations(signalEvent, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    auto &rootDeviceEnvironment = device->getNEODevice()->getRootDeviceEnvironmentRef();
    bool copyRegionPreferred = NEO::BlitCommandsHelper<GfxFamily>::validatePitchesForCopyRegion(srcRowPitch, dstRowPitch) &&
                               NEO::BlitCommandsHelper<GfxFamily>::isCopyRegionPreferred(blitProperties.copySize, rootDeviceEnvironment, blitProperties.isSystemMemoryPoolUsed);
    size_t nBlits = copyRegionPreferred ? NEO::BlitCommandsHelper<GfxFamily>::getNumberOfBlitsForCopyRegion(blitProperties.copySize, rootDeviceEnvironment, blitProperties.isSystemMemoryPoolUsed) : NEO::BlitCommandsHelper<GfxFamily>::getNumberOfBlitsForCopyPerRow(blitProperties.copySize, rootDeviceEnvironment, blitProperties.isSystemMemoryPoolUsed);
    bool useAdditionalTimestamp = nBlits > 1;
    if (useAdditionalBlitProperties) {
        setAdditionalBlitProperties(blitProperties, signalEvent, memoryCopyParams.forceAggregatedEventIncValue, useAdditionalTimestamp);
    } else {
        appendEventForProfiling(signalEvent, nullptr, true, false, false, true);
    }

    if (copyRegionPreferred) {
        NEO::BlitCommandsHelper<GfxFamily>::dispatchBlitCommandsForBufferRegion(blitProperties, *commandContainer.getCommandStream(), rootDeviceEnvironment);
    } else {
        NEO::BlitCommandsHelper<GfxFamily>::dispatchBlitCommandsForBufferPerRow(blitProperties, *commandContainer.getCommandStream(), rootDeviceEnvironment);
    }
    if (arePostBlitWACmdsRequired()) {
        NEO::BlitCommandsHelper<GfxFamily>::dispatchPostBlitWaCommands(*commandContainer.getCommandStream(), rootDeviceEnvironment);
    }
    dummyBlitWa.isWaRequired = true;

    if (!useAdditionalBlitProperties || (copySize.x == 0)) {
        appendSignalEventPostWalker(signalEvent, nullptr, nullptr, false, false, true);
    } else if (signalEvent && signalEvent->isSignalWithUserInterrupt()) {
        NEO::EncodeUserInterrupt<GfxFamily>::encode(*commandContainer.getCommandStream());
    }
    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendCopyImageBlit(uintptr_t srcPtr,
                                                                      NEO::GraphicsAllocation *src,
                                                                      uintptr_t dstPtr,
                                                                      NEO::GraphicsAllocation *dst,
                                                                      const Vec3<size_t> &srcOffsets, const Vec3<size_t> &dstOffsets,
                                                                      size_t srcRowPitch, size_t srcSlicePitch,
                                                                      size_t dstRowPitch, size_t dstSlicePitch,
                                                                      size_t bytesPerPixel, const Vec3<size_t> &copySize,
                                                                      const Vec3<size_t> &srcSize, const Vec3<size_t> &dstSize,
                                                                      Event *signalEvent, uint32_t numWaitEvents,
                                                                      ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams,
                                                                      bool isImageFromBuffer) {
    const bool dualStreamCopyOffloadOperation = isDualStreamCopyOffloadOperation(memoryCopyParams.copyOffloadAllowed);
    memoryCopyParams.waitEventsParameters.relaxedOrderingAllowed = memoryCopyParams.relaxedOrderingDispatch;
    memoryCopyParams.waitEventsParameters.waitForImplicitInOrderDependency = true;
    memoryCopyParams.waitEventsParameters.dualStreamCopyOffloadOperation = dualStreamCopyOffloadOperation;
    auto ret = addEventsToCmdList(numWaitEvents, phWaitEvents, memoryCopyParams.waitEventsParameters);
    if (ret != ZE_RESULT_SUCCESS) {
        return ret;
    }

    if (!handleCounterBasedEventOperations(signalEvent, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    auto clearColorAllocation = device->getNEODevice()->getDefaultEngine().commandStreamReceiver->getClearColorAllocation();

    auto blitProperties = NEO::BlitProperties::constructPropertiesForCopy(
        dst, dstPtr,
        src, srcPtr,
        dstOffsets, srcOffsets, copySize,
        srcRowPitch, srcSlicePitch, dstRowPitch, dstSlicePitch, clearColorAllocation);
    blitProperties.computeStreamPartitionCount = this->partitionCount;
    blitProperties.highPriority = isHighPriorityImmediateCmdList();
    blitProperties.bytesPerPixel = bytesPerPixel;
    blitProperties.srcSize = srcSize;
    blitProperties.dstSize = dstSize;

    this->addResidency(dst, src, clearColorAllocation);

    bool useAdditionalTimestamp = blitProperties.copySize.z > 1;
    if (useAdditionalBlitProperties) {
        setAdditionalBlitProperties(blitProperties, signalEvent, 0, useAdditionalTimestamp);
    } else {
        appendEventForProfiling(signalEvent, nullptr, true, false, false, true);
    }
    blitProperties.transform1DArrayTo2DArrayIfNeeded();

    if (isImageFromBuffer) {
        NEO::BlitCommandsHelper<GfxFamily>::dispatchBlitCommandsForBufferPerRow(blitProperties, *commandContainer.getCommandStream(), *dummyBlitWa.rootDeviceEnvironment);
    } else {
        NEO::BlitCommandsHelper<GfxFamily>::dispatchBlitCommandsForImageRegion(blitProperties, *commandContainer.getCommandStream(), *dummyBlitWa.rootDeviceEnvironment);
    }
    if (arePostBlitWACmdsRequired()) {
        NEO::BlitCommandsHelper<GfxFamily>::dispatchPostBlitWaCommands(*commandContainer.getCommandStream(), *dummyBlitWa.rootDeviceEnvironment);
    }
    dummyBlitWa.isWaRequired = true;

    if (!useAdditionalBlitProperties) {
        appendSignalEventPostWalker(signalEvent, nullptr, nullptr, false, false, true);
        if (this->isInOrderExecutionEnabled()) {
            appendSignalInOrderDependencyCounter(signalEvent, false, false, false, false);
        }
    } else if (signalEvent && signalEvent->isSignalWithUserInterrupt()) {
        NEO::EncodeUserInterrupt<GfxFamily>::encode(*commandContainer.getCommandStream());
    }
    handleInOrderDependencyCounter(signalEvent, false, false);

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendPageFaultCopy(NEO::GraphicsAllocation *dstAllocation,
                                                                      NEO::GraphicsAllocation *srcAllocation,
                                                                      size_t size, bool flushHost, size_t offset) {

    size_t middleElSize = sizeof(uint32_t) * 4;
    uintptr_t rightSize = size % middleElSize;
    auto builtInMode = this->defaultBuiltInMode;
    builtInMode.adjustToWideStatelessIfRequired(size);

    uintptr_t dstAddress = static_cast<uintptr_t>(dstAllocation->getGpuAddress());
    uintptr_t srcAddress = static_cast<uintptr_t>(srcAllocation->getGpuAddress());
    ze_result_t ret = ZE_RESULT_ERROR_UNKNOWN;
    if (isCopyOnly(false)) {
        CmdListMemoryCopyParams memoryCopyParams{};
        return appendMemoryCopyBlit(dstAddress, dstAllocation, offset,
                                    srcAddress, srcAllocation, offset,
                                    size, nullptr, memoryCopyParams);
    } else {
        CmdListKernelLaunchParams launchParams = {};
        launchParams.isKernelSplitOperation = rightSize > 0;
        launchParams.numKernelsInSplitLaunch = 2;
        ret = appendMemoryCopyKernelWithGA(dstAddress,
                                           dstAllocation, offset,
                                           srcAddress,
                                           srcAllocation, offset,
                                           size - rightSize,
                                           middleElSize,
                                           BufferBuiltIn::copyBufferToBufferMiddle,
                                           builtInMode,
                                           nullptr,
                                           launchParams);
        launchParams.numKernelsExecutedInSplitLaunch++;
        if (ret == ZE_RESULT_SUCCESS && rightSize) {
            ret = appendMemoryCopyKernelWithGA(dstAddress,
                                               dstAllocation, offset + size - rightSize,
                                               srcAddress,
                                               srcAllocation, offset + size - rightSize,
                                               rightSize, 1UL,
                                               BufferBuiltIn::copyBufferToBufferSide,
                                               builtInMode,
                                               nullptr,
                                               launchParams);
            launchParams.numKernelsExecutedInSplitLaunch++;
        }

        if (this->dcFlushSupport) {
            if (flushHost) {
                NEO::PipeControlArgs args;
                args.dcFlushEnable = true;
                NEO::MemorySynchronizationCommands<GfxFamily>::addSingleBarrier(*commandContainer.getCommandStream(), args);
            }
        }

        if (this->isInOrderExecutionEnabled()) {
            if (launchParams.isKernelSplitOperation) {
                dispatchInOrderPostOperationBarrier(nullptr, false, false);
                appendSignalInOrderDependencyCounter(nullptr, false, false, false, false);
            }
            handleInOrderDependencyCounter(nullptr, false, false);
        }
    }
    return ret;
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isCopyOffloadAllowed(const NEO::GraphicsAllocation *srcAllocation, const NEO::GraphicsAllocation *dstAllocation, bool remoteCopy, bool localToLocalAllowed, uint32_t mipLevel) const {
    if (mipLevel > 1) {
        return false;
    }

    if (remoteCopy || srcAllocation == nullptr || dstAllocation == nullptr) {
        return isCopyOffloadEnabled();
    }

    if (srcAllocation->isAllocatedInLocalMemoryPool() && dstAllocation->isAllocatedInLocalMemoryPool()) {
        return localToLocalAllowed && isCopyOffloadEnabled();
    }

    return isCopyOffloadEnabled();
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isSharedSystemEnabled() const {
    NEO::Device *neoDevice = device->getNEODevice();
    return neoDevice->areSharedSystemAllocationsAllowed() && (NEO::debugManager.flags.TreatNonUsmForTransfersAsSharedSystem.get() == 1);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::emitMemAdviseForSystemCopy(const AlignedAllocationData &allocationStruct, size_t size) {
    if ((allocationStruct.alloc == nullptr) && (NEO::debugManager.flags.EmitMemAdvisePriorToCopyForNonUsm.get() == 1)) {
        appendMemAdvise(device, reinterpret_cast<void *>(allocationStruct.alignedAllocationPtr), size,
                        static_cast<ze_memory_advice_t>(ZE_MEMORY_ADVICE_SET_SYSTEM_MEMORY_PREFERRED_LOCATION));
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendHostFunction(
    ze_host_function_callback_t pHostFunction,
    void *pUserData,
    const void *pNext,
    ze_event_handle_t hSignalEvent,
    uint32_t numWaitEvents,
    ze_event_handle_t *phWaitEvents,
    CmdListHostFunctionParameters &parameters) {

    const bool trackDependencies = true;
    const bool waitForImplicitInOrderDependency = true;
    const bool dualStreamCopyOffloadOperation = false;
    const bool skipAddingSignalEventsToResidency = false;

    parameters.waitEventParams.trackDependencies = trackDependencies;
    parameters.waitEventParams.waitForImplicitInOrderDependency = waitForImplicitInOrderDependency;
    parameters.waitEventParams.dualStreamCopyOffloadOperation = dualStreamCopyOffloadOperation;

    ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, parameters.waitEventParams);
    if (ret) {
        return ret;
    }

    appendSynchronizedDispatchInitializationSection();

    auto signalEvent = Event::fromHandle(hSignalEvent);
    const bool beforeWalker = true;
    const bool skipBarrierForEndProfiling = true;
    const bool copyOffload = false;
    const bool copyQueue = isCopyOnly(copyOffload);

    appendEventForProfiling(signalEvent, nullptr, beforeWalker, skipBarrierForEndProfiling, skipAddingSignalEventsToResidency, copyQueue);

    dispatchHostFunction(pHostFunction, pUserData, parameters.memorySynchronizationRequired);

    appendSignalEventPostWalker(signalEvent, nullptr, nullptr, skipBarrierForEndProfiling, skipAddingSignalEventsToResidency, copyQueue);

    if (!handleCounterBasedEventOperations(signalEvent, skipAddingSignalEventsToResidency)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    addToMappedEventList(signalEvent);

    if (isInOrderExecutionEnabled()) {
        const bool stallRequired = false;
        const bool textureFlushRequired = false;
        const bool skipAggregatedEventSignaling = false;
        appendSignalInOrderDependencyCounter(signalEvent, copyOffload, stallRequired, textureFlushRequired, skipAggregatedEventSignaling);
    }

    const bool nonWalkerInOrderCmdsChaining = false;
    handleInOrderDependencyCounter(signalEvent, nonWalkerInOrderCmdsChaining, copyOffload);

    appendSynchronizedDispatchCleanupSection();

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::dispatchHostFunction(
    ze_host_function_callback_t pHostFunction,
    void *pUserData,
    bool memorySynchronizationRequired) {

    uint64_t userHostFunctionAddress = reinterpret_cast<uint64_t>(pHostFunction);
    uint64_t userDataAddress = reinterpret_cast<uint64_t>(pUserData);

    NEO::HostFunction hostFunction{
        .hostFunctionAddress = userHostFunctionAddress,
        .userDataAddress = userDataAddress};

    if (isImmediateType()) {
        auto csr = getCsr(false);
        auto allocator = this->getDevice()->getHostFunctionAllocator(csr);
        csr->ensureHostFunctionWorkerStarted(allocator);
        auto &streamer = csr->getHostFunctionStreamer();
        auto *allocation = streamer.getHostFunctionIdAllocation();
        this->commandContainer.addToResidencyContainer(allocation);
        NEO::HostFunctionHelper<GfxFamily>::programHostFunction(*this->commandContainer.getCommandStream(),
                                                                streamer,
                                                                std::move(hostFunction),
                                                                NEO::HostFunctionHelper<GfxFamily>::isMemorySynchronizationRequired(memorySynchronizationRequired));
        csr->signalHostFunctionWorker(1u);
    } else {
        addHostFunctionToPatchCommands(hostFunction, memorySynchronizationRequired);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::addHostFunctionToPatchCommands(const NEO::HostFunction &hostFunction, bool memorySynchronizationRequired) {

    auto additionalSize = 1 + this->partitionCount;
    commandsToPatch.reserve(commandsToPatch.size() + additionalSize);

    bool useMemorySynchronization = NEO::HostFunctionHelper<GfxFamily>::isMemorySynchronizationRequired(memorySynchronizationRequired);
    if (useMemorySynchronization) {
        this->hostFunctionWithMemorySynchronizationCount++;
    } else {
        this->hostFunctionWithoutMemorySynchronizationCount++;
    }

    auto size = NEO::HostFunctionHelper<GfxFamily>::getSizeForHostFunctionIdProgramming(useMemorySynchronization, dcFlushSupport);

    auto gpuAddressInCmdStream = commandContainer.getCommandStream()->getCurrentGpuAddressPosition();
    auto cpuBufferCmd = commandContainer.getCommandStream()->getSpace(size);

    commandsToPatch.push_back(PatchHostFunctionId{
        .cmdBufferSpace = cpuBufferCmd,
        .gpuAddress = gpuAddressInCmdStream,
        .callbackAddress = hostFunction.hostFunctionAddress,
        .userDataAddress = hostFunction.userDataAddress,
        .memorySynchronizationRequired = useMemorySynchronization,
    });

    for (auto partitionId = 0u; partitionId < this->partitionCount; partitionId++) {

        gpuAddressInCmdStream = commandContainer.getCommandStream()->getCurrentGpuAddressPosition();
        cpuBufferCmd = commandContainer.getCommandStream()->getSpace(NEO::EncodeSemaphore<GfxFamily>::getSizeMiSemaphoreWait());

        commandsToPatch.push_back(PatchHostFunctionWait{
            .cmdBufferSpace = cpuBufferCmd,
            .gpuAddress = gpuAddressInCmdStream,
            .partitionId = partitionId,
        });
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendRecordedBcsSplit(void *dstptr, const void *srcptr, size_t size, ze_event_handle_t hSignalEvent, uint32_t numWaitEvents,
                                                                         ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {
    bool hasStallingCmds = true;
    bool copyOffloadFlush = false;
    setupFlagsForBcsSplit(memoryCopyParams, hasStallingCmds, copyOffloadFlush, srcptr, dstptr, size, size);

    auto splitCall = [&](CommandListCoreFamily<gfxCoreFamily> *subCmdList, const BcsSplitParams::CopyParams &copyParams, size_t sizeParam, ze_event_handle_t hSignalEventParam, uint64_t aggregatedEventIncValue) {
        memoryCopyParams.forceAggregatedEventIncValue = aggregatedEventIncValue;
        auto &params = std::get<BcsSplitParams::MemCopy>(copyParams);
        return subCmdList->CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopy(params.dst, params.src, sizeParam, hSignalEventParam, 0u, nullptr, memoryCopyParams);
    };

    BcsSplitParams::CopyParams copyParams = BcsSplitParams::MemCopy{dstptr, srcptr};
    return this->device->bcsSplit->template appendRecordedInOrderSplitCall<gfxCoreFamily>(this, copyParams, size, hSignalEvent, numWaitEvents, phWaitEvents, splitCall);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopy(void *dstptr,
                                                                   const void *srcptr,
                                                                   size_t size,
                                                                   ze_event_handle_t hSignalEvent,
                                                                   uint32_t numWaitEvents,
                                                                   ze_event_handle_t *phWaitEvents,
                                                                   CmdListMemoryCopyParams &memoryCopyParams) {
    return appendMemoryCopy(dstptr, srcptr, size, hSignalEvent, numWaitEvents, phWaitEvents, memoryCopyParams, nullptr, nullptr);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopy(void *dstptr,
                                                                   const void *srcptr,
                                                                   size_t size,
                                                                   ze_event_handle_t hSignalEvent,
                                                                   uint32_t numWaitEvents,
                                                                   ze_event_handle_t *phWaitEvents,
                                                                   CmdListMemoryCopyParams &memoryCopyParams,
                                                                   const MemAllocInfo *dstAllocInfo,
                                                                   const MemAllocInfo *srcAllocInfo) {

    bool sharedSystemEnabled = isSharedSystemEnabled();

    auto swTagScope = emplaceSWTagScope("zeCommandListAppendMemoryCopy");
    NEO::TransferDirection direction;

    if (this->bcsSplitMode == BcsSplitParams::BcsSplitMode::recorded && this->isAppendSplitNeeded(dstptr, srcptr, size, direction)) {
        return appendRecordedBcsSplit(dstptr, srcptr, size, hSignalEvent, numWaitEvents, phWaitEvents, memoryCopyParams);
    }

    auto allocSize = NEO::getIfValid(memoryCopyParams.bcsSplitTotalDstSize, size);
    auto dstAllocationStruct = resolveAlignedAllocation(this->device, NEO::getIfValid(memoryCopyParams.bcsSplitBaseDstPtr, dstptr), allocSize, dstAllocInfo, {.sharedSystemEnabled = sharedSystemEnabled, .copyOffload = isCopyOffloadEnabled()});
    auto srcAllocationStruct = resolveAlignedAllocation(this->device, NEO::getIfValid(memoryCopyParams.bcsSplitBaseSrcPtr, srcptr), allocSize, srcAllocInfo, {.sharedSystemEnabled = sharedSystemEnabled, .hostCopyAllowed = true, .copyOffload = isCopyOffloadEnabled()});

    bool remoteCopy = isRemoteAlloc(srcAllocationStruct.svmAllocData) || isRemoteAlloc(dstAllocationStruct.svmAllocData);

    if (memoryCopyParams.bscSplitEnabled) {
        dstAllocationStruct.offset += ptrDiff(dstptr, memoryCopyParams.bcsSplitBaseDstPtr);
        srcAllocationStruct.offset += ptrDiff(srcptr, memoryCopyParams.bcsSplitBaseSrcPtr);
    }

    if ((dstAllocationStruct.alloc == nullptr || srcAllocationStruct.alloc == nullptr) && (size != 0u) && (sharedSystemEnabled == false)) {
        return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
    }

    if (this->isImmediateType()) {
        memoryCopyParams.taskCountUpdateRequired |= CommandList::isExternalHostPtrAlloc(dstAllocationStruct.alloc) ||
                                                    CommandList::isExternalHostPtrAlloc(srcAllocationStruct.alloc);
    }

    emitMemAdviseForSystemCopy(dstAllocationStruct, size);
    emitMemAdviseForSystemCopy(srcAllocationStruct, size);

    if (!memoryCopyParams.copyOffloadAllowed) {
        bool imageToBuffer = false;
        bool shouldUseCopyOffload = this->isCopyOffloadForFillOrStagingPreferred(imageToBuffer) || doParamsRequireCopyOnly(memoryCopyParams);
        memoryCopyParams.copyOffloadAllowed = isCopyOffloadAllowed(srcAllocationStruct.alloc, dstAllocationStruct.alloc, remoteCopy, size <= BlitterConstants::maxD2DBcsCopySize, 0) && shouldUseCopyOffload;
    }

    const bool isCopyOnlyEnabled = isCopyOnly(memoryCopyParams.copyOffloadAllowed);
    const bool inOrderCopyOnlySignalingAllowed = this->isInOrderExecutionEnabled() && !memoryCopyParams.forceDisableCopyOnlyInOrderSignaling && isCopyOnlyEnabled;

    uint32_t kernelCounter = 0;
    uintptr_t leftSize = 0;
    uintptr_t rightSize = 0;
    uintptr_t middleSizeBytes = 0;
    size_t middleElSize = 0;
    NEO::BuiltIn::AddressingMode builtInMode = this->defaultBuiltInMode;

    if (!isCopyOnlyEnabled) {
        if (doParamsRequireCopyOnly(memoryCopyParams)) {
            return ZE_RESULT_ERROR_INVALID_ARGUMENT;
        }

        middleElSize = sizeof(uint32_t) * 4;
        builtInMode = this->defaultBuiltInMode;
        builtInMode.adjustToWideStatelessIfRequired(size);

        uintptr_t start = reinterpret_cast<uintptr_t>(dstptr);

        const size_t middleAlignment = MemoryConstants::cacheLineSize;

        leftSize = start % middleAlignment;

        leftSize = (leftSize > 0) ? (middleAlignment - leftSize) : 0;
        leftSize = std::min(leftSize, size);

        rightSize = (start + size) % middleAlignment;
        rightSize = std::min(rightSize, size - leftSize);

        middleSizeBytes = size - leftSize - rightSize;

        if (!isAligned<4>(reinterpret_cast<uintptr_t>(srcptr) + leftSize)) {
            leftSize += middleSizeBytes;
            middleSizeBytes = 0;
        }

        DEBUG_BREAK_IF(size != leftSize + middleSizeBytes + rightSize);

        kernelCounter = leftSize > 0 ? 1 : 0;
        kernelCounter += middleSizeBytes > 0 ? 1 : 0;
        kernelCounter += rightSize > 0 ? 1 : 0;

        if (NEO::debugManager.flags.ForceNonWalkerSplitMemoryCopy.get() == 1) {
            leftSize = size;
            middleSizeBytes = 0;
            rightSize = 0;
            kernelCounter = 1;
        }
    }

    bool waitForImplicitInOrderDependency = !isCopyOnlyEnabled || inOrderCopyOnlySignalingAllowed;

    memoryCopyParams.waitEventsParameters.relaxedOrderingAllowed = memoryCopyParams.relaxedOrderingDispatch;
    memoryCopyParams.waitEventsParameters.waitForImplicitInOrderDependency = waitForImplicitInOrderDependency;
    memoryCopyParams.waitEventsParameters.dualStreamCopyOffloadOperation = isDualStreamCopyOffloadOperation(memoryCopyParams.copyOffloadAllowed);
    ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, memoryCopyParams.waitEventsParameters);

    if (ret) {
        return ret;
    }

    appendSynchronizedDispatchInitializationSection();

    bool dcFlush = false;
    Event *signalEvent = nullptr;
    CmdListKernelLaunchParams launchParams = {};

    if (hSignalEvent) {
        signalEvent = Event::fromHandle(hSignalEvent);
        launchParams.isHostSignalScopeEvent = signalEvent->isSignalScope(ZE_EVENT_SCOPE_FLAG_HOST);
        dcFlush = getDcFlushRequired(signalEvent->isFlushRequiredForSignal());
    }

    if (!handleCounterBasedEventOperations(signalEvent, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    bool isSplitOperation = kernelCounter > 1;
    launchParams.numKernelsInSplitLaunch = kernelCounter;
    launchParams.isKernelSplitOperation = isSplitOperation;
    bool singlePipeControlPacket = eventSignalPipeControl(launchParams.isKernelSplitOperation, dcFlush);

    launchParams.pipeControlSignalling = (signalEvent && singlePipeControlPacket);

    if (!useAdditionalBlitProperties || !isCopyOnlyEnabled || size == 0) {
        appendEventForProfilingAllWalkers(signalEvent, nullptr, nullptr, true, singlePipeControlPacket, false, isCopyOnlyEnabled);
    }

    if (isCopyOnlyEnabled) {
        if (NEO::debugManager.flags.FlushTlbBeforeCopy.get() == 1) {
            NEO::MiFlushArgs args{this->dummyBlitWa};
            args.tlbFlush = true;
            encodeMiFlush(0, 0, args);
        }
        ret = appendMemoryCopyBlit(dstAllocationStruct.alignedAllocationPtr,
                                   dstAllocationStruct.alloc, dstAllocationStruct.offset,
                                   srcAllocationStruct.alignedAllocationPtr,
                                   srcAllocationStruct.alloc, srcAllocationStruct.offset, size, signalEvent, memoryCopyParams);
    } else {
        if (NEO::debugManager.flags.FlushTlbBeforeCopy.get() == 1) {
            NEO::PipeControlArgs args;
            args.tlbInvalidation = true;

            NEO::MemorySynchronizationCommands<GfxFamily>::addSingleBarrier(*commandContainer.getCommandStream(), args);
        }

        if (ret == ZE_RESULT_SUCCESS && leftSize) {

            BufferBuiltIn copyKernel = BufferBuiltIn::copyBufferToBufferSide;

            ret = appendMemoryCopyKernelWithGA(dstAllocationStruct.alignedAllocationPtr,
                                               dstAllocationStruct.alloc, dstAllocationStruct.offset,
                                               srcAllocationStruct.alignedAllocationPtr,
                                               srcAllocationStruct.alloc, srcAllocationStruct.offset,
                                               leftSize, 1UL,
                                               copyKernel,
                                               builtInMode,
                                               signalEvent,
                                               launchParams);
            launchParams.numKernelsExecutedInSplitLaunch++;
        }

        if (ret == ZE_RESULT_SUCCESS && middleSizeBytes) {

            BufferBuiltIn copyKernel = BufferBuiltIn::copyBufferToBufferMiddle;

            ret = appendMemoryCopyKernelWithGA(dstAllocationStruct.alignedAllocationPtr,
                                               dstAllocationStruct.alloc, leftSize + dstAllocationStruct.offset,
                                               srcAllocationStruct.alignedAllocationPtr,
                                               srcAllocationStruct.alloc, leftSize + srcAllocationStruct.offset,
                                               middleSizeBytes,
                                               middleElSize,
                                               copyKernel,
                                               builtInMode,
                                               signalEvent,
                                               launchParams);
            launchParams.numKernelsExecutedInSplitLaunch++;
        }

        if (ret == ZE_RESULT_SUCCESS && rightSize) {

            BufferBuiltIn copyKernel = BufferBuiltIn::copyBufferToBufferSide;

            ret = appendMemoryCopyKernelWithGA(dstAllocationStruct.alignedAllocationPtr,
                                               dstAllocationStruct.alloc, leftSize + middleSizeBytes + dstAllocationStruct.offset,
                                               srcAllocationStruct.alignedAllocationPtr,
                                               srcAllocationStruct.alloc, leftSize + middleSizeBytes + srcAllocationStruct.offset,
                                               rightSize, 1UL,
                                               copyKernel,
                                               builtInMode,
                                               signalEvent,
                                               launchParams);
            launchParams.numKernelsExecutedInSplitLaunch++;
        }
    }

    appendCopyOperationFence(signalEvent, srcAllocationStruct.alloc, dstAllocationStruct.alloc, isCopyOnlyEnabled);

    if (!useAdditionalBlitProperties || !isCopyOnlyEnabled || size == 0) {
        appendEventForProfilingAllWalkers(signalEvent, nullptr, nullptr, false, singlePipeControlPacket, false, isCopyOnlyEnabled);
    }

    addToMappedEventList(signalEvent);

    if (this->isInOrderExecutionEnabled()) {
        bool emitPipeControl = !isCopyOnlyEnabled && launchParams.pipeControlSignalling;

        if ((!useAdditionalBlitProperties || !isCopyOnlyEnabled || size == 0) &&
            (launchParams.isKernelSplitOperation || inOrderCopyOnlySignalingAllowed || emitPipeControl)) {
            dispatchInOrderPostOperationBarrier(signalEvent, dcFlush, isCopyOnlyEnabled);
            appendSignalInOrderDependencyCounter(signalEvent, memoryCopyParams.copyOffloadAllowed, false, false, false);
        } else if ((!useAdditionalBlitProperties || size == 0) && isCopyOnlyEnabled && Event::isAggregatedEvent(signalEvent)) {
            appendSignalAggregatedEventAtomic(*signalEvent, isCopyOnlyEnabled);
        }

        if (!isCopyOnlyEnabled || inOrderCopyOnlySignalingAllowed) {
            bool nonWalkerInOrderCmdChaining = !isCopyOnlyEnabled && isInOrderNonWalkerSignalingRequired(signalEvent) && !emitPipeControl && !this->latestOperationHasHeapfullCbEventWithProfiling;
            handleInOrderDependencyCounter(signalEvent, nonWalkerInOrderCmdChaining, isCopyOnlyEnabled);
        }
    } else {
        handleInOrderDependencyCounter(signalEvent, false, isCopyOnlyEnabled);
    }
    appendSynchronizedDispatchCleanupSection();

    return ret;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopyWithParameters(void *dstptr,
                                                                                 const void *srcptr,
                                                                                 size_t size,
                                                                                 const void *pNext,
                                                                                 ze_event_handle_t hSignalEvent,
                                                                                 uint32_t numWaitEvents,
                                                                                 ze_event_handle_t *phWaitEvents,
                                                                                 CmdListMemoryCopyParams &memoryCopyParams) {
    ze_result_t ret = obtainMemoryCopyParamsFromExtensions(static_cast<const ze_base_desc_t *>(pNext), memoryCopyParams, false);
    if (ret) {
        return ret;
    }

    return appendMemoryCopy(dstptr, srcptr, size, hSignalEvent, numWaitEvents, phWaitEvents, memoryCopyParams);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopyRegion(void *dstPtr,
                                                                         const ze_copy_region_t *dstRegion,
                                                                         uint32_t dstPitch,
                                                                         uint32_t dstSlicePitch,
                                                                         const void *srcPtr,
                                                                         const ze_copy_region_t *srcRegion,
                                                                         uint32_t srcPitch,
                                                                         uint32_t srcSlicePitch,
                                                                         ze_event_handle_t hSignalEvent,
                                                                         uint32_t numWaitEvents,
                                                                         ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {

    bool sharedSystemEnabled = isSharedSystemEnabled();

    auto swTagScope = emplaceSWTagScope("zeCommandListAppendMemoryCopyRegion");

    auto dstSize = this->getTotalSizeForCopyRegion(dstRegion, dstPitch, dstSlicePitch);
    auto srcSize = this->getTotalSizeForCopyRegion(srcRegion, srcPitch, srcSlicePitch);

    size_t dstAllocSize = NEO::getIfValid(memoryCopyParams.bcsSplitTotalDstSize, dstSize);
    size_t srcAllocSize = NEO::getIfValid(memoryCopyParams.bcsSplitTotalSrcSize, srcSize);

    auto dstAllocationStruct = resolveAlignedAllocation(this->device, NEO::getIfValid(memoryCopyParams.bcsSplitBaseDstPtr, dstPtr), dstAllocSize, nullptr, {.sharedSystemEnabled = sharedSystemEnabled, .copyOffload = isCopyOffloadEnabled()});
    auto srcAllocationStruct = resolveAlignedAllocation(this->device, NEO::getIfValid(memoryCopyParams.bcsSplitBaseSrcPtr, srcPtr), srcAllocSize, nullptr, {.sharedSystemEnabled = sharedSystemEnabled, .hostCopyAllowed = true, .copyOffload = isCopyOffloadEnabled()});

    bool remoteCopy = isRemoteAlloc(srcAllocationStruct.svmAllocData) || isRemoteAlloc(dstAllocationStruct.svmAllocData);

    UNRECOVERABLE_IF(srcSlicePitch && srcPitch == 0);
    Vec3<size_t> srcSize3 = {srcPitch ? srcPitch : srcRegion->width + srcRegion->originX,
                             srcSlicePitch ? srcSlicePitch / srcPitch : srcRegion->height + srcRegion->originY,
                             srcRegion->depth + srcRegion->originZ};
    UNRECOVERABLE_IF(dstSlicePitch && dstPitch == 0);
    Vec3<size_t> dstSize3 = {dstPitch ? dstPitch : dstRegion->width + dstRegion->originX,
                             dstSlicePitch ? dstSlicePitch / dstPitch : dstRegion->height + dstRegion->originY,
                             dstRegion->depth + dstRegion->originZ};

    Event *signalEvent = nullptr;
    if (hSignalEvent) {
        signalEvent = Event::fromHandle(hSignalEvent);
    }

    if (((dstAllocationStruct.alloc == nullptr && dstAllocSize != 0u) || (srcAllocationStruct.alloc == nullptr && srcAllocSize != 0u)) && (sharedSystemEnabled == false)) {
        return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
    }

    if (this->isImmediateType()) {
        memoryCopyParams.taskCountUpdateRequired |= CommandList::isExternalHostPtrAlloc(dstAllocationStruct.alloc) ||
                                                    CommandList::isExternalHostPtrAlloc(srcAllocationStruct.alloc);
    }

    emitMemAdviseForSystemCopy(dstAllocationStruct, dstSize);
    emitMemAdviseForSystemCopy(srcAllocationStruct, srcSize);

    bool imageToBuffer = false;
    bool shouldUseCopyOffload = this->isCopyOffloadForFillOrStagingPreferred(imageToBuffer);
    memoryCopyParams.copyOffloadAllowed = isCopyOffloadAllowed(srcAllocationStruct.alloc, dstAllocationStruct.alloc, remoteCopy, std::max(srcSize, dstSize) <= BlitterConstants::maxD2DBcsCopySize, 0) && shouldUseCopyOffload;
    const bool isCopyOnlyEnabled = isCopyOnly(memoryCopyParams.copyOffloadAllowed);
    const bool inOrderCopyOnlySignalingAllowed = this->isInOrderExecutionEnabled() && !memoryCopyParams.forceDisableCopyOnlyInOrderSignaling && isCopyOnlyEnabled;

    auto builtInMode = this->defaultBuiltInMode;
    builtInMode.adjustToWideStatelessIfRequired(std::max(srcSize, dstSize));
    ze_result_t result = ZE_RESULT_SUCCESS;
    memoryCopyParams.waitEventsParameters.relaxedOrderingAllowed = memoryCopyParams.relaxedOrderingDispatch;
    if (isCopyOnlyEnabled) {
        memoryCopyParams.waitEventsParameters.dualStreamCopyOffloadOperation = isDualStreamCopyOffloadOperation(memoryCopyParams.copyOffloadAllowed);
        result = appendMemoryCopyBlitRegion(&srcAllocationStruct, &dstAllocationStruct, *srcRegion, *dstRegion,
                                            {srcRegion->width, srcRegion->height, srcRegion->depth},
                                            srcPitch, srcSlicePitch, dstPitch, dstSlicePitch, srcSize3, dstSize3,
                                            signalEvent, numWaitEvents, phWaitEvents, memoryCopyParams);
    } else if ((srcRegion->depth > 1) || (srcRegion->originZ != 0) || (dstRegion->originZ != 0)) {
        BufferBuiltIn bufferBuiltIn = BufferBuiltIn::copyBufferRectBytes3d;
        result = this->appendMemoryCopyKernel3d(&dstAllocationStruct, &srcAllocationStruct, bufferBuiltIn, builtInMode,
                                                dstRegion, dstPitch, dstSlicePitch, dstAllocationStruct.offset,
                                                srcRegion, srcPitch, srcSlicePitch, srcAllocationStruct.offset,
                                                signalEvent, numWaitEvents, phWaitEvents,
                                                memoryCopyParams.waitEventsParameters);
    } else {
        BufferBuiltIn bufferBuiltIn = BufferBuiltIn::copyBufferRectBytes2d;
        result = this->appendMemoryCopyKernel2d(&dstAllocationStruct, &srcAllocationStruct, bufferBuiltIn, builtInMode,
                                                dstRegion, dstPitch, dstAllocationStruct.offset,
                                                srcRegion, srcPitch, srcAllocationStruct.offset,
                                                signalEvent, numWaitEvents, phWaitEvents,
                                                memoryCopyParams.waitEventsParameters);
    }

    if (result) {
        return result;
    }

    appendCopyOperationFence(signalEvent, srcAllocationStruct.alloc, dstAllocationStruct.alloc, isCopyOnlyEnabled);

    addToMappedEventList(signalEvent);

    if (this->isInOrderExecutionEnabled()) {
        if (inOrderCopyOnlySignalingAllowed) {
            if (!useAdditionalBlitProperties || srcRegion->width == 0) {
                appendSignalInOrderDependencyCounter(signalEvent, memoryCopyParams.copyOffloadAllowed, false, false, false);
            }
            handleInOrderDependencyCounter(signalEvent, false, isCopyOnlyEnabled);
        } else if ((!useAdditionalBlitProperties || srcRegion->width == 0) && isCopyOnlyEnabled && Event::isAggregatedEvent(signalEvent)) {
            appendSignalAggregatedEventAtomic(*signalEvent, isCopyOnlyEnabled);
        }
    } else {
        handleInOrderDependencyCounter(signalEvent, false, isCopyOnlyEnabled);
    }

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopyKernel3d(AlignedAllocationData *dstAlignedAllocation,
                                                                           AlignedAllocationData *srcAlignedAllocation,
                                                                           BufferBuiltIn builtin,
                                                                           const NEO::BuiltIn::AddressingMode &builtInMode,
                                                                           const ze_copy_region_t *dstRegion,
                                                                           uint32_t dstPitch,
                                                                           uint32_t dstSlicePitch,
                                                                           size_t dstOffset,
                                                                           const ze_copy_region_t *srcRegion,
                                                                           uint32_t srcPitch,
                                                                           uint32_t srcSlicePitch,
                                                                           size_t srcOffset,
                                                                           Event *signalEvent,
                                                                           uint32_t numWaitEvents,
                                                                           ze_event_handle_t *phWaitEvents,
                                                                           CmdListWaitEventParameters &waitEventParamters) {

    auto lock = device->getBuiltinFunctionsLib()->obtainUniqueOwnership();
    const auto driverHandle = device->getDriverHandle();

    auto builtinKernel = device->getBuiltinFunctionsLib()->getFunction(builtin, builtInMode);

    uint32_t groupSizeX = srcRegion->width;
    uint32_t groupSizeY = srcRegion->height;
    uint32_t groupSizeZ = srcRegion->depth;

    ze_result_t ret = builtinKernel->suggestGroupSize(groupSizeX, groupSizeY, groupSizeZ,
                                                      &groupSizeX, &groupSizeY, &groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    ret = builtinKernel->setGroupSize(groupSizeX, groupSizeY, groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    if (srcRegion->width % groupSizeX || srcRegion->height % groupSizeY || srcRegion->depth % groupSizeZ) {
        CREATE_DEBUG_STRING(str, "Invalid group size {%d, %d, %d} specified\n", groupSizeX, groupSizeY, groupSizeZ);
        driverHandle->setErrorDescription(std::string(str.get()));
        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "Invalid group size {%d, %d, %d} specified\n",
                     groupSizeX, groupSizeY, groupSizeZ);
        DEBUG_BREAK_IF(true);
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    ze_group_count_t dispatchKernelArgs{srcRegion->width / groupSizeX, srcRegion->height / groupSizeY,
                                        srcRegion->depth / groupSizeZ};

    builtinSetArg(builtinKernel, 0u, srcAlignedAllocation->alignedAllocationPtr, srcAlignedAllocation->alloc);
    builtinSetArg(builtinKernel, 1u, dstAlignedAllocation->alignedAllocationPtr, dstAlignedAllocation->alloc);

    uint32_t srcOrigin32[3] = {(srcRegion->originX + static_cast<uint32_t>(srcOffset)), (srcRegion->originY), (srcRegion->originZ)};
    uint32_t dstOrigin32[3] = {(dstRegion->originX + static_cast<uint32_t>(dstOffset)), (dstRegion->originY), (dstRegion->originZ)};
    uint32_t srcPitches32[2] = {srcPitch, srcSlicePitch};
    uint32_t dstPitches32[2] = {dstPitch, dstSlicePitch};
    builtinKernel->setArgumentValue(2, sizeof(srcOrigin32), &srcOrigin32);
    builtinKernel->setArgumentValue(3, sizeof(dstOrigin32), &dstOrigin32);
    builtinKernel->setArgumentValue(4, sizeof(srcPitches32), &srcPitches32);
    builtinKernel->setArgumentValue(5, sizeof(dstPitches32), &dstPitches32);

    CmdListKernelLaunchParams launchParams = {};
    launchParams.isBuiltInKernel = true;
    if (dstAlignedAllocation->alloc) {
        auto dstAllocationType = dstAlignedAllocation->alloc->getAllocationType();
        launchParams.isDestinationAllocationInSystemMemory =
            (dstAllocationType == NEO::AllocationType::bufferHostMemory) ||
            (dstAllocationType == NEO::AllocationType::externalHostPtr);

        if constexpr (checkIfAllocationImportedRequired()) {
            launchParams.isDestinationAllocationImported = dstAlignedAllocation->alloc->getIsImported();
        }
    } else {
        launchParams.isDestinationAllocationInSystemMemory = true;
    }
    launchParams.relaxedOrderingDispatch = waitEventParamters.relaxedOrderingAllowed;
    launchParams.omitAddingWaitEventsResidency = waitEventParamters.skipAddingWaitEventsToResidency;
    launchParams.outListCommands = waitEventParamters.outWaitCmds;
    return CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernel(builtinKernel->toHandle(), dispatchKernelArgs, signalEvent, numWaitEvents,
                                                                    phWaitEvents, launchParams);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopyKernel2d(AlignedAllocationData *dstAlignedAllocation,
                                                                           AlignedAllocationData *srcAlignedAllocation,
                                                                           BufferBuiltIn builtin,
                                                                           const NEO::BuiltIn::AddressingMode &builtInMode,
                                                                           const ze_copy_region_t *dstRegion,
                                                                           uint32_t dstPitch,
                                                                           size_t dstOffset,
                                                                           const ze_copy_region_t *srcRegion,
                                                                           uint32_t srcPitch,
                                                                           size_t srcOffset,
                                                                           Event *signalEvent,
                                                                           uint32_t numWaitEvents,
                                                                           ze_event_handle_t *phWaitEvents,
                                                                           CmdListWaitEventParameters &waitEventParamters) {

    auto lock = device->getBuiltinFunctionsLib()->obtainUniqueOwnership();
    const auto driverHandle = device->getDriverHandle();

    auto builtinKernel = device->getBuiltinFunctionsLib()->getFunction(builtin, builtInMode);

    uint32_t groupSizeX = srcRegion->width;
    uint32_t groupSizeY = srcRegion->height;
    uint32_t groupSizeZ = 1u;

    ze_result_t ret = builtinKernel->suggestGroupSize(groupSizeX, groupSizeY, groupSizeZ, &groupSizeX,
                                                      &groupSizeY, &groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    ret = builtinKernel->setGroupSize(groupSizeX, groupSizeY, groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    if (srcRegion->width % groupSizeX || srcRegion->height % groupSizeY) {
        CREATE_DEBUG_STRING(str, "Invalid group size {%d, %d} specified\n", groupSizeX, groupSizeY);
        driverHandle->setErrorDescription(std::string(str.get()));
        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "Invalid group size {%d, %d}\n",
                     groupSizeX, groupSizeY);
        DEBUG_BREAK_IF(true);
        return ZE_RESULT_ERROR_UNKNOWN;
    }

    ze_group_count_t dispatchKernelArgs{srcRegion->width / groupSizeX, srcRegion->height / groupSizeY, 1u};

    builtinSetArg(builtinKernel, 0u, srcAlignedAllocation->alignedAllocationPtr, srcAlignedAllocation->alloc);
    builtinSetArg(builtinKernel, 1u, dstAlignedAllocation->alignedAllocationPtr, dstAlignedAllocation->alloc);

    uint32_t srcOrigin32[2] = {(srcRegion->originX + static_cast<uint32_t>(srcOffset)), (srcRegion->originY)};
    uint32_t dstOrigin32[2] = {(dstRegion->originX + static_cast<uint32_t>(dstOffset)), (dstRegion->originY)};
    uint32_t srcPitch32 = static_cast<uint32_t>(srcPitch);
    uint32_t dstPitch32 = static_cast<uint32_t>(dstPitch);
    builtinKernel->setArgumentValue(2, sizeof(srcOrigin32), &srcOrigin32);
    builtinKernel->setArgumentValue(3, sizeof(dstOrigin32), &dstOrigin32);
    builtinKernel->setArgumentValue(4, sizeof(srcPitch32), &srcPitch32);
    builtinKernel->setArgumentValue(5, sizeof(dstPitch32), &dstPitch32);

    CmdListKernelLaunchParams launchParams = {};
    launchParams.isBuiltInKernel = true;
    if (dstAlignedAllocation->alloc) {
        auto dstAllocationType = dstAlignedAllocation->alloc->getAllocationType();
        launchParams.isDestinationAllocationInSystemMemory =
            (dstAllocationType == NEO::AllocationType::bufferHostMemory) ||
            (dstAllocationType == NEO::AllocationType::externalHostPtr);

        if constexpr (CommandListCoreFamily<gfxCoreFamily>::checkIfAllocationImportedRequired()) {
            launchParams.isDestinationAllocationImported = dstAlignedAllocation->alloc->getIsImported();
        }
    } else {
        launchParams.isDestinationAllocationInSystemMemory = true;
    }
    launchParams.relaxedOrderingDispatch = waitEventParamters.relaxedOrderingAllowed;
    launchParams.omitAddingWaitEventsResidency = waitEventParamters.skipAddingWaitEventsToResidency;
    launchParams.outListCommands = waitEventParamters.outWaitCmds;
    return CommandListCoreFamily<gfxCoreFamily>::appendLaunchKernel(builtinKernel->toHandle(),
                                                                    dispatchKernelArgs, signalEvent,
                                                                    numWaitEvents,
                                                                    phWaitEvents,
                                                                    launchParams);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryPrefetch(const void *ptr, size_t size) {
    auto svmAllocMgr = device->getDriverHandle()->getSvmAllocsManager();
    auto allocData = svmAllocMgr->getSVMAlloc(ptr);

    if (!allocData) {
        if (device->getNEODevice()->areSharedSystemAllocationsAllowed()) {
            this->performMemoryPrefetch = true;
            auto prefetchManager = device->getDriverHandle()->getMemoryManager()->getPrefetchManager();
            if (prefetchManager) {
                prefetchManager->insertAllocation(this->prefetchContext, *device->getDriverHandle()->getSvmAllocsManager(), *device->getNEODevice(), ptr, size);
            }
            return ZE_RESULT_SUCCESS;
        } else {
            return ZE_RESULT_ERROR_INVALID_ARGUMENT;
        }
    }

    if (NEO::debugManager.flags.AppendMemoryPrefetchForKmdMigratedSharedAllocations.get() == true) {
        this->performMemoryPrefetch = true;
        auto prefetchManager = device->getDriverHandle()->getMemoryManager()->getPrefetchManager();
        if (prefetchManager) {
            prefetchManager->insertAllocation(this->prefetchContext, *device->getDriverHandle()->getSvmAllocsManager(), *device->getNEODevice(), ptr, size);
        }
    }

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendUnalignedFillKernel(bool isStateless, uint32_t unalignedSize, const AlignedAllocationData &dstAllocation, const void *pattern, Event *signalEvent, CmdListKernelLaunchParams &launchParams) {

    auto builtInMode = this->defaultBuiltInMode;
    builtInMode.adjustToWideStatelessIfRequired(0);
    auto builtin = BufferBuiltIn::fillBufferImmediateLeftOver;

    Kernel *builtinKernel = device->getBuiltinFunctionsLib()->getFunction(builtin, builtInMode);
    uint32_t groupSizeY = 1, groupSizeZ = 1;
    uint32_t groupSizeX = static_cast<uint32_t>(unalignedSize);
    builtinKernel->suggestGroupSize(groupSizeX, groupSizeY, groupSizeZ, &groupSizeX, &groupSizeY, &groupSizeZ);
    builtinKernel->setGroupSize(groupSizeX, groupSizeY, groupSizeZ);
    ze_group_count_t dispatchKernelRemainderArgs{static_cast<uint32_t>(unalignedSize / groupSizeX), 1u, 1u};
    uint32_t value = *(reinterpret_cast<const unsigned char *>(pattern));
    builtinSetArg(builtinKernel, 0, dstAllocation.alignedAllocationPtr, dstAllocation.alloc);
    builtinKernel->setArgumentValue(1, sizeof(dstAllocation.offset), &dstAllocation.offset);
    builtinKernel->setArgumentValue(2, sizeof(value), &value);

    auto res = appendLaunchKernelSplit(builtinKernel, dispatchKernelRemainderArgs, signalEvent, launchParams);
    if (res) {
        return res;
    }
    return ZE_RESULT_SUCCESS;
}

inline bool canUseImmediateFill(size_t size, size_t patternSize, size_t offset, size_t maxWgSize) {
    return patternSize == 1 || (patternSize <= 4 &&
                                isAligned<sizeof(uint32_t)>(offset) &&
                                isAligned<sizeof(uint32_t) * 4>(size) &&
                                (size <= maxWgSize || isAligned(size / (sizeof(uint32_t) * 4), maxWgSize)));
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isCopyOffloadForFillOrStagingPreferred(bool isWriteToImageFromBuffer) const {
    return NEO::debugManager.flags.EnableBlitterForEnqueueOperations.getIfNotDefault(device->getProductHelper().blitEnqueuePreferred(isWriteToImageFromBuffer));
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isCopyOffloadForFillPreferred(size_t size) const {
    constexpr size_t defaultThresholdKb = 8;

    return (size <= (NEO::debugManager.flags.OverrideFillCopyOffloadThresholdKb.getIfNotDefault(defaultThresholdKb)) * MemoryConstants::kiloByte);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryFill(void *ptr,
                                                                   const void *pattern,
                                                                   size_t patternSize,
                                                                   size_t size,
                                                                   ze_event_handle_t hSignalEvent,
                                                                   uint32_t numWaitEvents,
                                                                   ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {
    if (patternSize == 0) {
        return ZE_RESULT_ERROR_INVALID_SIZE;
    }
    const auto isStateless = this->forceStateless(size);
    auto builtInMode = this->defaultBuiltInMode;
    builtInMode.adjustToWideStatelessIfRequired(size);
    bool isWriteToImageFromBuffer = false;
    const bool shouldUseCopyOffload = (isCopyOffloadForFillOrStagingPreferred(isWriteToImageFromBuffer) && isCopyOffloadForFillPreferred(size)) || doParamsRequireCopyOnly(memoryCopyParams);
    memoryCopyParams.waitEventsParameters.relaxedOrderingAllowed = memoryCopyParams.relaxedOrderingDispatch;
    memoryCopyParams.copyOffloadAllowed = isCopyOffloadEnabled() && (patternSize <= this->maxFillPatternSizeForCopyEngine) && (0 == size % patternSize) && shouldUseCopyOffload;

    NEO::Device *neoDevice = device->getNEODevice();
    bool sharedSystemEnabled = isSharedSystemEnabled();
    auto swTagScope = emplaceSWTagScope("zeCommandListAppendMemoryFill");

    CmdListKernelLaunchParams launchParams = {};

    Event *signalEvent = nullptr;
    bool dcFlush = false;
    if (hSignalEvent) {
        signalEvent = Event::fromHandle(hSignalEvent);
        launchParams.isHostSignalScopeEvent = signalEvent->isSignalScope(ZE_EVENT_SCOPE_FLAG_HOST);
        dcFlush = getDcFlushRequired(signalEvent->isFlushRequiredForSignal());
        registerWalkerWithProfilingEnqueued(signalEvent);
    }

    if (isCopyOnly(memoryCopyParams.copyOffloadAllowed)) {
        auto status = appendBlitFill(ptr, pattern, patternSize, size, signalEvent, numWaitEvents, phWaitEvents, memoryCopyParams);
        addToMappedEventList(signalEvent);
        return status;
    } else if (doParamsRequireCopyOnly(memoryCopyParams)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    memoryCopyParams.waitEventsParameters.waitForImplicitInOrderDependency = true;
    ze_result_t res = addEventsToCmdList(numWaitEvents, phWaitEvents, memoryCopyParams.waitEventsParameters);
    if (res) {
        return res;
    }

    appendSynchronizedDispatchInitializationSection();

    if (!handleCounterBasedEventOperations(signalEvent, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    bool hostPointerNeedsFlush = false;

    NEO::SvmAllocationData *allocData = nullptr;
    bool dstAllocFound = device->getDriverHandle()->findAllocationDataForRange(ptr, size, allocData);
    if (dstAllocFound) {
        if (allocData->memoryType == InternalMemoryType::hostUnifiedMemory ||
            allocData->memoryType == InternalMemoryType::sharedUnifiedMemory) {
            hostPointerNeedsFlush = true;
        }

    } else {
        if ((sharedSystemEnabled == false) && (neoDevice->areSharedSystemAllocationsAllowed() == false) && (device->getDriverHandle()->getHostPointerBaseAddress(ptr, nullptr) != ZE_RESULT_SUCCESS)) {
            // first two conditions, above are default, and each may be turned true only with debug variables
            return ZE_RESULT_ERROR_INVALID_ARGUMENT;
        }
        hostPointerNeedsFlush = true;
    }

    MemAllocInfo dstMemAllocInfo{allocData, nullptr, nullptr};
    auto dstAllocation = this->resolveAlignedAllocation(this->device, ptr, size, &dstMemAllocInfo, {.sharedSystemEnabled = sharedSystemEnabled});
    if ((dstAllocation.alloc == nullptr) && (sharedSystemEnabled == false)) {
        return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
    }

    emitMemAdviseForSystemCopy(dstAllocation, size);

    auto lock = device->getBuiltinFunctionsLib()->obtainUniqueOwnership();

    bool useImmediateFill = canUseImmediateFill(size, patternSize, dstAllocation.offset, this->device->getDeviceInfo().maxWorkGroupSize);
    auto builtin = useImmediateFill
                       ? BufferBuiltIn::fillBufferImmediate
                       : BufferBuiltIn::fillBufferMiddle;

    Kernel *builtinKernel = device->getBuiltinFunctionsLib()->getFunction(builtin, builtInMode);

    launchParams.isBuiltInKernel = true;
    launchParams.isDestinationAllocationInSystemMemory = hostPointerNeedsFlush;
    if (dstAllocation.alloc) {
        if constexpr (checkIfAllocationImportedRequired()) {
            launchParams.isDestinationAllocationImported = dstAllocation.alloc->getIsImported();
        }
    }
    CmdListFillKernelArguments fillArguments = {};
    setupFillKernelArguments(dstAllocation.offset, patternSize, size, fillArguments, builtinKernel);

    launchParams.isKernelSplitOperation = (fillArguments.leftRemainingBytes > 0 || fillArguments.rightRemainingBytes > 0);
    bool singlePipeControlPacket = eventSignalPipeControl(launchParams.isKernelSplitOperation, dcFlush);
    launchParams.pipeControlSignalling = (signalEvent && singlePipeControlPacket);

    appendEventForProfilingAllWalkers(signalEvent, nullptr, nullptr, true, singlePipeControlPacket, false, isCopyOnly(false));

    if (fillArguments.leftRemainingBytes > 0) {
        launchParams.numKernelsInSplitLaunch++;
    }
    if (fillArguments.rightRemainingBytes > 0) {
        launchParams.numKernelsInSplitLaunch++;
    }

    if (useImmediateFill) {
        launchParams.numKernelsInSplitLaunch++;
        if (fillArguments.leftRemainingBytes > 0) {
            DEBUG_BREAK_IF(useImmediateFill && patternSize > 1u);
            res = appendUnalignedFillKernel(isStateless, fillArguments.leftRemainingBytes, dstAllocation, pattern, signalEvent, launchParams);
            if (res) {
                return res;
            }
            launchParams.numKernelsExecutedInSplitLaunch++;
        }

        ze_result_t ret = builtinKernel->setGroupSize(static_cast<uint32_t>(fillArguments.mainGroupSize), 1u, 1u);
        if (ret != ZE_RESULT_SUCCESS) {
            DEBUG_BREAK_IF(true);
            return ret;
        }

        ze_group_count_t dispatchKernelArgs{static_cast<uint32_t>(fillArguments.groups), 1u, 1u};

        uint32_t value = 0u;

        switch (patternSize) {
        case 1:
            memset(&value, *reinterpret_cast<const unsigned char *>(pattern), 4);
            break;
        case 2:
            memcpy(&value, pattern, 2);
            value <<= 16;
            memcpy(&value, pattern, 2);
            break;
        case 4:
            memcpy(&value, pattern, 4);
            break;
        default:
            UNRECOVERABLE_IF(true);
        }

        builtinSetArg(builtinKernel, 0, dstAllocation.alignedAllocationPtr, dstAllocation.alloc);
        builtinKernel->setArgumentValue(1, sizeof(fillArguments.mainOffset), &fillArguments.mainOffset);
        builtinKernel->setArgumentValue(2, sizeof(value), &value);

        res = appendLaunchKernelSplit(builtinKernel, dispatchKernelArgs, signalEvent, launchParams);
        if (res) {
            return res;
        }
        launchParams.numKernelsExecutedInSplitLaunch++;

        if (fillArguments.rightRemainingBytes > 0) {
            DEBUG_BREAK_IF(useImmediateFill && patternSize > 1u);
            dstAllocation.offset = fillArguments.rightOffset;
            res = appendUnalignedFillKernel(isStateless, fillArguments.rightRemainingBytes, dstAllocation, pattern, signalEvent, launchParams);
            if (res) {
                return res;
            }
            launchParams.numKernelsExecutedInSplitLaunch++;
        }
    } else {
        builtinKernel->setGroupSize(static_cast<uint32_t>(fillArguments.mainGroupSize), 1, 1);

        NEO::GraphicsAllocation *patternGfxAlloc = nullptr;
        NEO::TagNodeBase *patternTagNode = nullptr;
        void *patternGfxAllocPtr = nullptr;
        size_t patternAllocationSize = alignUp(patternSize, MemoryConstants::cacheLineSize);

        if (patternAllocationSize > MemoryConstants::cacheLineSize) {
            patternGfxAlloc = device->obtainReusableAllocation(patternAllocationSize, NEO::AllocationType::fillPattern);
            if (patternGfxAlloc == nullptr) {
                NEO::AllocationProperties allocationProperties{neoDevice->getRootDeviceIndex(),
                                                               patternAllocationSize,
                                                               NEO::AllocationType::fillPattern,
                                                               neoDevice->getDeviceBitfield()};
                allocationProperties.alignment = MemoryConstants::pageSize;
                patternGfxAlloc = device->getDriverHandle()->getMemoryManager()->allocateGraphicsMemoryWithProperties(allocationProperties);
            }
            patternGfxAllocPtr = patternGfxAlloc->getUnderlyingBuffer();
            patternAllocations.push_back(patternGfxAlloc);
        } else {
            patternTagNode = device->getFillPatternAllocator()->getTag();
            patternGfxAllocPtr = patternTagNode->getCpuBase();
            patternGfxAlloc = patternTagNode->getBaseGraphicsAllocation()->getGraphicsAllocation(device->getRootDeviceIndex());
            this->patternTags.push_back(patternTagNode);
            commandContainer.addToResidencyContainer(patternGfxAlloc);
        }

        uint64_t patternAllocPtr = reinterpret_cast<uintptr_t>(patternGfxAllocPtr);
        uint64_t patternAllocOffset = 0;
        uint64_t patternSizeToCopy = patternSize;
        do {
            memcpy_s(reinterpret_cast<void *>(patternAllocPtr + patternAllocOffset),
                     patternSizeToCopy, pattern, patternSizeToCopy);

            if ((patternAllocOffset + patternSizeToCopy) > patternAllocationSize) {
                patternSizeToCopy = patternAllocationSize - patternAllocOffset;
            }

            patternAllocOffset += patternSizeToCopy;
        } while (patternAllocOffset < patternAllocationSize);

        if (patternTagNode != nullptr) {
            auto *uploadCsr = isImmediateType() ? getCsr(false) : device->getNEODevice()->getDefaultEngine().commandStreamReceiver;
            uploadCsr->writeTagAllocationChunkToSimulation(*patternTagNode, 0, patternAllocationSize);
            uploadCsr->setWritableForSimulation(false, *patternGfxAlloc);
        }

        if (fillArguments.leftRemainingBytes == 0) {
            builtinSetArg(builtinKernel, 0, dstAllocation.alignedAllocationPtr, dstAllocation.alloc);
            builtinKernel->setArgumentValue(1, sizeof(dstAllocation.offset), &dstAllocation.offset);
            builtinKernel->setArgBufferWithAlloc(2, reinterpret_cast<uintptr_t>(patternGfxAllocPtr), patternGfxAlloc, nullptr);
            builtinKernel->setArgumentValue(3, sizeof(fillArguments.patternSizeInEls), &fillArguments.patternSizeInEls);

            ze_group_count_t dispatchKernelArgs{static_cast<uint32_t>(fillArguments.groups), 1u, 1u};
            launchParams.numKernelsInSplitLaunch++;
            res = appendLaunchKernelSplit(builtinKernel, dispatchKernelArgs, signalEvent, launchParams);
            if (res) {
                return res;
            }
            launchParams.numKernelsExecutedInSplitLaunch++;
        } else {
            uint32_t dstOffsetRemainder = static_cast<uint32_t>(dstAllocation.offset);

            auto builtin = BufferBuiltIn::fillBufferRightLeftover;
            Kernel *builtinKernelRemainder = device->getBuiltinFunctionsLib()->getFunction(builtin, builtInMode);

            builtinKernelRemainder->setGroupSize(static_cast<uint32_t>(fillArguments.mainGroupSize), 1, 1);
            ze_group_count_t dispatchKernelArgs{static_cast<uint32_t>(fillArguments.groups), 1u, 1u};

            builtinSetArg(builtinKernelRemainder, 0, dstAllocation.alignedAllocationPtr, dstAllocation.alloc);
            builtinKernelRemainder->setArgumentValue(1,
                                                     sizeof(dstOffsetRemainder),
                                                     &dstOffsetRemainder);
            builtinKernelRemainder->setArgBufferWithAlloc(2,
                                                          reinterpret_cast<uintptr_t>(patternGfxAllocPtr),
                                                          patternGfxAlloc, nullptr);
            builtinKernelRemainder->setArgumentValue(3, sizeof(patternAllocationSize), &patternAllocationSize);

            res = appendLaunchKernelSplit(builtinKernelRemainder, dispatchKernelArgs, signalEvent, launchParams);
            if (res) {
                return res;
            }
            launchParams.numKernelsExecutedInSplitLaunch++;
        }

        if (fillArguments.rightRemainingBytes > 0) {
            uint32_t dstOffsetRemainder = static_cast<uint32_t>(fillArguments.rightOffset);
            uint64_t patternOffsetRemainder = fillArguments.patternOffsetRemainder;

            auto builtin = BufferBuiltIn::fillBufferRightLeftover;
            Kernel *builtinKernelRemainder = device->getBuiltinFunctionsLib()->getFunction(builtin, builtInMode);

            builtinKernelRemainder->setGroupSize(fillArguments.rightRemainingBytes, 1u, 1u);
            ze_group_count_t dispatchKernelArgs{1u, 1u, 1u};

            builtinSetArg(builtinKernelRemainder, 0, dstAllocation.alignedAllocationPtr, dstAllocation.alloc);
            builtinKernelRemainder->setArgumentValue(1,
                                                     sizeof(dstOffsetRemainder),
                                                     &dstOffsetRemainder);
            builtinKernelRemainder->setArgBufferWithAlloc(2,
                                                          reinterpret_cast<uintptr_t>(patternGfxAllocPtr) + patternOffsetRemainder,
                                                          patternGfxAlloc, nullptr);
            builtinKernelRemainder->setArgumentValue(3, sizeof(patternAllocationSize), &patternAllocationSize);

            res = appendLaunchKernelSplit(builtinKernelRemainder, dispatchKernelArgs, signalEvent, launchParams);
            if (res) {
                return res;
            }
            launchParams.numKernelsExecutedInSplitLaunch++;
        }
    }

    addToMappedEventList(signalEvent);
    appendEventForProfilingAllWalkers(signalEvent, nullptr, nullptr, false, singlePipeControlPacket, false, isCopyOnly(false));

    bool nonWalkerInOrderCmdChaining = false;
    if (this->isInOrderExecutionEnabled()) {
        if (launchParams.isKernelSplitOperation || launchParams.pipeControlSignalling) {
            dispatchInOrderPostOperationBarrier(signalEvent, dcFlush, isCopyOnly(false));
            appendSignalInOrderDependencyCounter(signalEvent, false, false, false, false);
        } else {
            nonWalkerInOrderCmdChaining = isInOrderNonWalkerSignalingRequired(signalEvent) && !this->latestOperationHasHeapfullCbEventWithProfiling;
        }
    }
    handleInOrderDependencyCounter(signalEvent, nonWalkerInOrderCmdChaining, false);

    appendSynchronizedDispatchCleanupSection();

    return res;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryFillWithParameters(void *ptr,
                                                                                 const void *pattern,
                                                                                 size_t patternSize,
                                                                                 size_t size,
                                                                                 const void *pNext,
                                                                                 ze_event_handle_t hSignalEvent,
                                                                                 uint32_t numWaitEvents,
                                                                                 ze_event_handle_t *phWaitEvents,
                                                                                 CmdListMemoryCopyParams &memoryCopyParams) {

    ze_result_t ret = obtainMemoryCopyParamsFromExtensions(static_cast<const ze_base_desc_t *>(pNext), memoryCopyParams, true);
    if (ret) {
        return ret;
    }

    return appendMemoryFill(ptr, pattern, patternSize, size, hSignalEvent, numWaitEvents, phWaitEvents, memoryCopyParams);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendBlitFill(void *ptr, const void *pattern, size_t patternSize, size_t size, Event *signalEvent, uint32_t numWaitEvents,
                                                                 ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {

    NEO::Device *neoDevice = device->getNEODevice();
    bool sharedSystemEnabled = isSharedSystemEnabled();

    if (this->maxFillPatternSizeForCopyEngine < patternSize) {
        return ZE_RESULT_ERROR_INVALID_SIZE;
    } else {
        const bool dualStreamCopyOffloadOperation = isDualStreamCopyOffloadOperation(memoryCopyParams.copyOffloadAllowed);
        const bool isCopyOnlySignaling = isCopyOnly(dualStreamCopyOffloadOperation) && (!useAdditionalBlitProperties || size == 0);
        memoryCopyParams.waitEventsParameters.waitForImplicitInOrderDependency = true;
        memoryCopyParams.waitEventsParameters.dualStreamCopyOffloadOperation = dualStreamCopyOffloadOperation;
        ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, memoryCopyParams.waitEventsParameters);
        if (ret) {
            return ret;
        }

        if (!handleCounterBasedEventOperations(signalEvent, false)) {
            return ZE_RESULT_ERROR_INVALID_ARGUMENT;
        }

        if (isCopyOnlySignaling) {
            appendEventForProfiling(signalEvent, nullptr, true, false, false, true);
        }
        NEO::GraphicsAllocation *gpuAllocation = device->getDriverHandle()->getDriverSystemMemoryAllocation(ptr,
                                                                                                            size,
                                                                                                            neoDevice->getRootDeviceIndex(),
                                                                                                            nullptr);

        DriverHandle *driverHandle = device->getDriverHandle();
        auto allocData = driverHandle->getSvmAllocsManager()->getSVMAlloc(ptr);
        if (driverHandle->isRemoteResourceNeeded(gpuAllocation, allocData, device)) {
            if (allocData) {
                uint64_t pbase = allocData->gpuAllocations.getDefaultGraphicsAllocation()->getGpuAddress();
                gpuAllocation = driverHandle->getPeerAllocation(device, allocData, reinterpret_cast<void *>(pbase), nullptr, nullptr, false);
            }
        }

        uint32_t patternToCommand[4] = {};
        if (!useZeroedPatternInMemoryFill(memoryCopyParams)) {
            memcpy_s(&patternToCommand, sizeof(patternToCommand), pattern, patternSize);
        }
        NEO::BlitProperties blitProperties;
        bool useAdditionalTimestamp = false;
        if (gpuAllocation) {
            auto offset = getAllocationOffsetForAppendBlitFill(ptr, *gpuAllocation);

            commandContainer.addToResidencyContainer(gpuAllocation);

            blitProperties = NEO::BlitProperties::constructPropertiesForMemoryFill(gpuAllocation, 0, size, patternToCommand, patternSize, offset);
            size_t nBlits = NEO::BlitCommandsHelper<GfxFamily>::getNumberOfBlitsForColorFill(blitProperties.copySize, patternSize, neoDevice->getRootDeviceEnvironmentRef(), blitProperties.isSystemMemoryPoolUsed);
            useAdditionalTimestamp = nBlits > 1;
        } else if (sharedSystemEnabled) {
            if (NEO::debugManager.flags.EmitMemAdvisePriorToCopyForNonUsm.get() == 1) {
                appendMemAdvise(device, ptr, size, static_cast<ze_memory_advice_t>(ZE_MEMORY_ADVICE_SET_SYSTEM_MEMORY_PREFERRED_LOCATION));
            }
            blitProperties = NEO::BlitProperties::constructPropertiesForMemoryFill(nullptr, reinterpret_cast<uint64_t>(ptr), size, patternToCommand, patternSize, 0ul);
        } else if (neoDevice->areSharedSystemAllocationsAllowed()) {
            gpuAllocation = getHostPtrAlloc(ptr, size, false, false);

            commandContainer.addToResidencyContainer(gpuAllocation);

            blitProperties = NEO::BlitProperties::constructPropertiesForMemoryFill(gpuAllocation, 0, size, patternToCommand, patternSize, 0ul);
            size_t nBlits = NEO::BlitCommandsHelper<GfxFamily>::getNumberOfBlitsForColorFill(blitProperties.copySize, patternSize, neoDevice->getRootDeviceEnvironmentRef(), blitProperties.isSystemMemoryPoolUsed);
            useAdditionalTimestamp = nBlits > 1;
        } else {
            return ZE_RESULT_ERROR_INVALID_ARGUMENT;
        }

        setAdditionalBlitPropertiesFromMemoryCopyParams(blitProperties, memoryCopyParams);

        if (useAdditionalBlitProperties) {
            setAdditionalBlitProperties(blitProperties, signalEvent, 0, useAdditionalTimestamp);
        }
        blitProperties.computeStreamPartitionCount = this->partitionCount;
        blitProperties.highPriority = isHighPriorityImmediateCmdList();

        NEO::BlitCommandsHelper<GfxFamily>::dispatchBlitMemoryFill(blitProperties, *commandContainer.getCommandStream(), neoDevice->getRootDeviceEnvironmentRef());

        dummyBlitWa.isWaRequired = true;
        if (isCopyOnlySignaling) {
            appendSignalEventPostWalker(signalEvent, nullptr, nullptr, false, false, true);
        } else if (useAdditionalBlitProperties && signalEvent && signalEvent->isSignalWithUserInterrupt()) {
            NEO::EncodeUserInterrupt<GfxFamily>::encode(*commandContainer.getCommandStream());
        }

        if (isInOrderExecutionEnabled() && isCopyOnlySignaling) {
            appendSignalInOrderDependencyCounter(signalEvent, memoryCopyParams.copyOffloadAllowed, false, false, false);
        }
        handleInOrderDependencyCounter(signalEvent, false, memoryCopyParams.copyOffloadAllowed);
    }
    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendSignalEventPostWalker(Event *event, void **syncCmdBuffer, CommandToPatchContainer *outTimeStampSyncCmds, bool skipBarrierForEndProfiling, bool skipAddingEventToResidency, bool copyOperation) {
    if (event == nullptr || !event->getAllocation(this->device)) {
        return;
    }
    if (event->isEventTimestampFlagSet()) {
        appendEventForProfiling(event, outTimeStampSyncCmds, false, skipBarrierForEndProfiling, skipAddingEventToResidency, copyOperation);
    } else {
        event->resetKernelCountAndPacketUsedCount();
        if (!skipAddingEventToResidency) {
            commandContainer.addToResidencyContainer(event->getAllocation(this->device));
        }

        event->setPacketsInUse(copyOperation ? 1 : this->partitionCount);
        dispatchEventPostSyncOperation(event, syncCmdBuffer, nullptr, Event::STATE_SIGNALED, false, false, !copyOperation, false, copyOperation);
    }
    if (event->isSignalWithUserInterrupt()) {
        NEO::EncodeUserInterrupt<GfxFamily>::encode(*commandContainer.getCommandStream());
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendEventForProfilingCopyCommand(Event *event, bool beforeWalker) {
    if (!event->isEventTimestampFlagSet()) {
        return;
    }
    commandContainer.addToResidencyContainer(event->getAllocation(this->device));
    if (beforeWalker) {
        event->resetKernelCountAndPacketUsedCount();
    } else {
        NEO::MiFlushArgs args{this->dummyBlitWa};
        encodeMiFlush(0, 0, args);
        dispatchEventPostSyncOperation(event, nullptr, nullptr, Event::STATE_SIGNALED, true, false, false, false, true);
    }
    appendWriteKernelTimestamp(event, nullptr, beforeWalker, false, false, true);
}

template <GFXCORE_FAMILY gfxCoreFamily>
inline uint64_t CommandListCoreFamily<gfxCoreFamily>::getInputBufferSize(NEO::ImageType imageType,
                                                                         uint32_t bufferRowPitch,
                                                                         uint64_t bufferSlicePitch,
                                                                         const ze_image_region_t *region, size_t pixelSize) {
    switch (imageType) {
    case NEO::ImageType::image1D:
    case NEO::ImageType::image1DBuffer:
        return region->width * pixelSize;
    case NEO::ImageType::image2D:
        return static_cast<uint64_t>(region->height - 1) * bufferRowPitch + static_cast<uint64_t>(region->width) * pixelSize;
    case NEO::ImageType::image1DArray:
        return static_cast<uint64_t>(region->height - 1) * bufferSlicePitch + static_cast<uint64_t>(region->width) * pixelSize;
    case NEO::ImageType::image3D:
    case NEO::ImageType::image2DArray:
        return static_cast<uint64_t>(region->depth - 1) * bufferSlicePitch + static_cast<uint64_t>(region->height - 1) * bufferRowPitch + static_cast<uint64_t>(region->width) * pixelSize;
    default:
        CREATE_DEBUG_STRING(str, "invalid imageType: %d\n", static_cast<int>(imageType));
        device->getDriverHandle()->setErrorDescription(std::string(str.get()));
        PRINT_STRING(NEO::debugManager.flags.PrintDebugMessages.get(), stderr, "invalid imageType: %d\n", imageType);
        UNRECOVERABLE_IF(true);
        return 0;
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
inline AlignedAllocationData CommandListCoreFamily<gfxCoreFamily>::resolveAlignedAllocation(Device *device, const void *buffer, uint64_t bufferSize, const MemAllocInfo *bufferAllocInfo, const ResolveAlignedAllocationFlags &flags) {
    NEO::SvmAllocationData *svmAlloc = bufferAllocInfo ? bufferAllocInfo->svmAlloc : nullptr;
    NEO::GraphicsAllocation *importedHostAlloc = bufferAllocInfo ? bufferAllocInfo->importedHostAlloc : nullptr;
    NEO::GraphicsAllocation *cachedHostAlloc = bufferAllocInfo ? bufferAllocInfo->cachedHostAlloc : nullptr;

    void *ptr = const_cast<void *>(buffer);
    uintptr_t sourcePtr = reinterpret_cast<uintptr_t>(ptr);
    size_t sshAlignmentOffset = 0;
    NEO::EncodeSurfaceState<GfxFamily>::getSshAlignedPointer(sourcePtr, sshAlignmentOffset);

    bool svmAllocFound = (svmAlloc != nullptr);
    if (!svmAlloc && !importedHostAlloc && !cachedHostAlloc) {
        svmAllocFound = device->getDriverHandle()->findAllocationDataForRange(ptr, bufferSize, svmAlloc);
    }

    if (svmAllocFound) {
        return alignSvmAllocationData(device, svmAlloc, buffer, sourcePtr, sshAlignmentOffset);
    }

    if (!importedHostAlloc && !cachedHostAlloc) {
        importedHostAlloc = device->getDriverHandle()->findHostPointerAllocation(ptr, static_cast<size_t>(bufferSize), device->getRootDeviceIndex());
    }

    if (importedHostAlloc) {
        return alignImportedHostAllocationData(importedHostAlloc, ptr);
    }

    if (flags.sharedSystemEnabled || bufferSize == 0u) {
        return AlignedAllocationData::forSystemPointer(ptr);
    }

    if (!cachedHostAlloc) {
        cachedHostAlloc = getHostPtrAlloc(buffer, bufferSize, flags.hostCopyAllowed, flags.copyOffload);
    }

    if (cachedHostAlloc) {
        return alignCachedHostAllocationData(cachedHostAlloc, sourcePtr, sshAlignmentOffset);
    }
    return AlignedAllocationData::invalid();
}

template <GFXCORE_FAMILY gfxCoreFamily>
AlignedAllocationData CommandListCoreFamily<gfxCoreFamily>::alignSvmAllocationData(Device *device, NEO::SvmAllocationData *svmAlloc, const void *buffer, uintptr_t sourcePtr, size_t sshAlignmentOffset) {
    auto alloc = svmAlloc->gpuAllocations.getGraphicsAllocation(device->getRootDeviceIndex());
    DriverHandle *driverHandle = device->getDriverHandle();
    uintptr_t alignedPtr = 0u;
    if (driverHandle->isRemoteResourceNeeded(alloc, svmAlloc, device)) {
        uint64_t pbase = svmAlloc->gpuAllocations.getDefaultGraphicsAllocation()->getGpuAddress();
        uint64_t peerBaseOffset = sourcePtr - pbase;

        alloc = driverHandle->getPeerAllocation(device, svmAlloc, reinterpret_cast<void *>(pbase), &alignedPtr, nullptr, true);
        alignedPtr += peerBaseOffset;

        if (svmAlloc->memoryType == InternalMemoryType::sharedUnifiedMemory) {
            commandContainer.addToResidencyContainer(svmAlloc->gpuAllocations.getDefaultGraphicsAllocation());
        }
    } else {
        alignedPtr = sourcePtr;
    }

    bool hostPointerNeedsFlush = false;
    if (svmAlloc->memoryType == InternalMemoryType::hostUnifiedMemory ||
        svmAlloc->memoryType == InternalMemoryType::sharedUnifiedMemory) {
        hostPointerNeedsFlush = true;
    }
    addVirtualReservationToResidency(svmAlloc, buffer);
    return AlignedAllocationData::fromAllocation(svmAlloc, alignedPtr, sshAlignmentOffset, alloc, hostPointerNeedsFlush);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::addVirtualReservationToResidency(NEO::SvmAllocationData *svmAlloc, const void *buffer) {
    if (!svmAlloc->virtualReservationData) {
        return;
    }
    for (const auto &mappedAllocationData : svmAlloc->virtualReservationData->mappedAllocations) {
        if (buffer != mappedAllocationData.second->ptr) {
            commandContainer.addToResidencyContainer(mappedAllocationData.second->mappedAllocation.allocation);
        }
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
AlignedAllocationData CommandListCoreFamily<gfxCoreFamily>::alignImportedHostAllocationData(NEO::GraphicsAllocation *importedHostAlloc, void *ptr) {
    uintptr_t alignedPtr = static_cast<size_t>(alignDown(importedHostAlloc->getGpuAddress(), NEO::EncodeSurfaceState<GfxFamily>::getSurfaceBaseAddressAlignment()));
    size_t gpuAddressOffset = static_cast<size_t>(importedHostAlloc->getGpuAddress()) - alignedPtr;
    gpuAddressOffset += reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(importedHostAlloc->getUnderlyingBuffer());
    return AlignedAllocationData::fromAllocation(nullptr, alignedPtr, gpuAddressOffset, importedHostAlloc, true);
}

template <GFXCORE_FAMILY gfxCoreFamily>
AlignedAllocationData CommandListCoreFamily<gfxCoreFamily>::alignCachedHostAllocationData(NEO::GraphicsAllocation *cachedHostAlloc, uintptr_t sourcePtr, size_t sshAlignmentOffset) {
    uintptr_t alignedPtr = static_cast<uintptr_t>(alignDown(cachedHostAlloc->getGpuAddress(), NEO::EncodeSurfaceState<GfxFamily>::getSurfaceBaseAddressAlignment()));
    if (cachedHostAlloc->getAllocationType() == NEO::AllocationType::externalHostPtr) {
        auto hostAllocCpuPtr = reinterpret_cast<uintptr_t>(cachedHostAlloc->getUnderlyingBuffer());
        hostAllocCpuPtr = alignDown(hostAllocCpuPtr, NEO::EncodeSurfaceState<GfxFamily>::getSurfaceBaseAddressAlignment());
        auto allignedPtrOffset = sourcePtr - hostAllocCpuPtr;
        alignedPtr = ptrOffset(alignedPtr, allignedPtrOffset);
    }
    return AlignedAllocationData::fromAllocation(nullptr, alignedPtr, sshAlignmentOffset, cachedHostAlloc, true);
}

template <GFXCORE_FAMILY gfxCoreFamily>
inline size_t CommandListCoreFamily<gfxCoreFamily>::getAllocationOffsetForAppendBlitFill(void *ptr, NEO::GraphicsAllocation &gpuAllocation) {
    uint64_t offset;
    if (gpuAllocation.getAllocationType() == NEO::AllocationType::externalHostPtr) {
        offset = castToUint64(ptr) - castToUint64(gpuAllocation.getUnderlyingBuffer()) + gpuAllocation.getAllocationOffset();
    } else {
        offset = castToUint64(ptr) - gpuAllocation.getGpuAddress();
    }
    return offset;
}

template <GFXCORE_FAMILY gfxCoreFamily>
inline uint32_t CommandListCoreFamily<gfxCoreFamily>::getRegionOffsetForAppendMemoryCopyBlitRegion(AlignedAllocationData *allocationData) {
    uint64_t ptr = allocationData->alignedAllocationPtr + allocationData->offset;
    uint64_t allocPtr = allocationData->alloc->getGpuAddress();
    return static_cast<uint32_t>(ptr - allocPtr);
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::handleInOrderImplicitDependencies(bool relaxedOrderingAllowed, bool dualStreamCopyOffloadOperation) {
    if (dualStreamCopyOffloadOperation || relaxedOrderingAllowed) {
        auto ret = this->flushInOrderCounterSignal();
        if (ret != ZE_RESULT_SUCCESS) {
            return ret;
        }
    }

    if (hasInOrderDependencies()) {
        if (!this->isPostSyncSkippedOnLatestInOrderOperation && inOrderExecInfo->isCounterAlreadyDone(inOrderExecInfo->getCounterValue(), inOrderExecInfo->getAllocationOffset())) {
            this->latestOperationHasHeapfullCbEventWithProfiling = false;
            return false;
        }

        if (relaxedOrderingAllowed) {
            NEO::RelaxedOrderingHelper::encodeRegistersBeforeDependencyCheckers<GfxFamily>(*commandContainer.getCommandStream(), isCopyOnly(dualStreamCopyOffloadOperation));
        }

        CommandListCoreFamily<gfxCoreFamily>::appendWaitOnInOrderDependency(inOrderExecInfo->getDeviceCounterAllocation(), inOrderExecInfo->getBaseDeviceAddress(), inOrderExecInfo->getNumDevicePartitionsToWait(),
                                                                            nullptr, inOrderExecInfo->getCounterValue(), inOrderExecInfo->getAllocationOffset(), relaxedOrderingAllowed, true, false, false, dualStreamCopyOffloadOperation);

        this->latestOperationHasHeapfullCbEventWithProfiling = false;
        this->isPostSyncSkippedOnLatestInOrderOperation = false;
        return true;
    }

    this->latestOperationHasHeapfullCbEventWithProfiling = false;
    this->isPostSyncSkippedOnLatestInOrderOperation = false;
    return false;
}

template <GFXCORE_FAMILY gfxCoreFamily>
inline ze_result_t CommandListCoreFamily<gfxCoreFamily>::addEventsToCmdList(uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents, CmdListWaitEventParameters &waitEventsParameters) {
    bool copyOnly = isCopyOnly(waitEventsParameters.dualStreamCopyOffloadOperation);
    bool inOrderDependenciesSent = false;

    if (this->latestOperationRequiredNonWalkerInOrderCmdsChaining && !waitEventsParameters.relaxedOrderingAllowed && !copyOnly) {
        waitEventsParameters.waitForImplicitInOrderDependency = false;
    }

    if (waitEventsParameters.waitForImplicitInOrderDependency) {
        inOrderDependenciesSent = handleInOrderImplicitDependencies(waitEventsParameters.relaxedOrderingAllowed, waitEventsParameters.dualStreamCopyOffloadOperation);
    }

    if (waitEventsParameters.relaxedOrderingAllowed && numWaitEvents > 0 && !inOrderDependenciesSent) {
        NEO::RelaxedOrderingHelper::encodeRegistersBeforeDependencyCheckers<GfxFamily>(*commandContainer.getCommandStream(), copyOnly);
    }

    if (numWaitEvents > 0) {
        if (phWaitEvents) {
            waitEventsParameters.apiRequest = false;
            waitEventsParameters.skipFlush = false;
            return CommandListCoreFamily<gfxCoreFamily>::appendWaitOnEvents(numWaitEvents, phWaitEvents, waitEventsParameters);
        } else {
            return ZE_RESULT_ERROR_INVALID_ARGUMENT;
        }
    }

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendSignalEvent(ze_event_handle_t hEvent, bool relaxedOrderingDispatch) {
    auto event = Event::fromHandle(hEvent);

    if (event->isCounterBased()) {
        CmdListWaitEventParameters waitEventsParameters = {
            .outWaitCmds = nullptr,
            .relaxedOrderingAllowed = relaxedOrderingDispatch,
            .trackDependencies = true,
            .waitForImplicitInOrderDependency = true,
            .skipAddingWaitEventsToResidency = false,
            .dualStreamCopyOffloadOperation = false,
        };
        return appendBarrier(hEvent, 0, nullptr, waitEventsParameters);
    }

    if (this->isInOrderExecutionEnabled()) {
        handleInOrderImplicitDependencies(relaxedOrderingDispatch, false);
    }

    event->resetKernelCountAndPacketUsedCount();

    if (!handleCounterBasedEventOperations(event, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    commandContainer.addToResidencyContainer(event->getAllocation(this->device));

    auto swTagScope = emplaceSWTagScope("zeCommandListAppendSignalEvent");

    event->setPacketsInUse(this->partitionCount);
    bool appendPipeControlWithPostSync = (!isCopyOnly(false)) && (event->isSignalScope() || event->isEventTimestampFlagSet());
    dispatchEventPostSyncOperation(event, nullptr, nullptr, Event::STATE_SIGNALED, false, false, appendPipeControlWithPostSync, false, isCopyOnly(false));

    if (event->isSignalWithUserInterrupt()) {
        NEO::EncodeUserInterrupt<GfxFamily>::encode(*commandContainer.getCommandStream());
    }

    if (!event->getAllocation(this->device) && appendPipeControlWithPostSync && getDcFlushRequired(true)) {
        NEO::PipeControlArgs pipeControlArgs;
        pipeControlArgs.dcFlushEnable = true;

        NEO::MemorySynchronizationCommands<GfxFamily>::addSingleBarrier(*commandContainer.getCommandStream(), pipeControlArgs);
    }

    if (this->isInOrderExecutionEnabled()) {
        appendSignalInOrderDependencyCounter(event, false, false, false, false);
    }
    handleInOrderDependencyCounter(event, false, false);

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
NEO::GraphicsAllocation *CommandListCoreFamily<gfxCoreFamily>::getDeviceCounterAllocForResidency(NEO::GraphicsAllocation *counterDeviceAlloc) {
    NEO::GraphicsAllocation *counterDeviceAllocForResidency = counterDeviceAlloc;

    if (counterDeviceAllocForResidency && (counterDeviceAllocForResidency->getRootDeviceIndex() != device->getRootDeviceIndex())) {
        DriverHandle *driverHandle = device->getDriverHandle();

        counterDeviceAllocForResidency = driverHandle->getCounterPeerAllocation(device, *counterDeviceAllocForResidency);
        UNRECOVERABLE_IF(!counterDeviceAllocForResidency);
        UNRECOVERABLE_IF(counterDeviceAllocForResidency->getGpuAddress() != counterDeviceAlloc->getGpuAddress());
    }
    return counterDeviceAllocForResidency;
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isResolveIoqDependencyWithBarrier(bool implicitDependency, bool copyOnlyWait, bool dualStreamCopyOffloadOperation) const {
    const bool crossEngineDependency = (this->latestFlushIsDualCopyOffload != dualStreamCopyOffloadOperation);
    const bool sameEngineImplicitDependency = !crossEngineDependency && !copyOnlyWait && implicitDependency;
    if (!sameEngineImplicitDependency) {
        return false;
    }

    const bool heapfulProfilingEvent = !this->heaplessModeEnabled && this->latestOperationHasHeapfullCbEventWithProfiling;
    auto resolveIoqDependencyWithBarrier = (this->dcFlushSupport || heapfulProfilingEvent);
    const auto isBarrierRequired = (this->isPostSyncSkippedOnLatestInOrderOperation || heapfulProfilingEvent);
    if (this->isImmediateType() && resolveIoqDependencyWithBarrier && !isBarrierRequired) {
        // Use semaphore to avoid serialization if different cmd list submitted workload between previous and current submission.
        // Semaphore is not allowed if previous submission used HP event or post sync was skipped.
        const bool noInterleavingSinceLastSubmit = (this->cmdQImmediate->getTaskCount() == this->getCsr(false)->peekTaskCount());
        resolveIoqDependencyWithBarrier = noInterleavingSinceLastSubmit;
    }
    DEBUG_BREAK_IF(!resolveIoqDependencyWithBarrier && isBarrierRequired);

    if (NEO::debugManager.flags.ResolveDependenciesViaPipeControls.get() != -1) {
        resolveIoqDependencyWithBarrier = NEO::debugManager.flags.ResolveDependenciesViaPipeControls.get();
    }
    return resolveIoqDependencyWithBarrier;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendWaitOnInOrderDependency(NEO::GraphicsAllocation *deviceCounterAlloc, uint64_t deviceBaseCounterGpuVa, uint32_t deviceCounterPartitionCount, CommandToPatchContainer *outListCommands,
                                                                         uint64_t waitValue, uint32_t offset, bool relaxedOrderingAllowed, bool implicitDependency, bool skipAddingWaitEventsToResidency,
                                                                         bool noopDispatch, bool dualStreamCopyOffloadOperation) {
    using COMPARE_OPERATION = typename GfxFamily::MI_SEMAPHORE_WAIT::COMPARE_OPERATION;

    UNRECOVERABLE_IF(waitValue > static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) && !isQwordInOrderCounter());

    auto deviceAllocForResidency = this->getDeviceCounterAllocForResidency(deviceCounterAlloc);
    if (!skipAddingWaitEventsToResidency) {
        commandContainer.addToResidencyContainer(deviceAllocForResidency);
    }

    uint64_t gpuAddress = deviceBaseCounterGpuVa + offset;

    const uint32_t immWriteOffset = device->getL0GfxCoreHelper().getImmediateWritePostSyncOffset();
    const bool copyOnlyWait = isCopyOnly(dualStreamCopyOffloadOperation);

    for (uint32_t i = 0; i < deviceCounterPartitionCount; i++) {
        if (relaxedOrderingAllowed) {
            NEO::EncodeBatchBufferStartOrEnd<GfxFamily>::programConditionalDataMemBatchBufferStart(*commandContainer.getCommandStream(), 0, gpuAddress, waitValue, NEO::CompareOperation::less, true,
                                                                                                   isQwordInOrderCounter(), copyOnlyWait);

        } else {
            const bool resolveIoqDependencyWithBarrier = isResolveIoqDependencyWithBarrier(implicitDependency, copyOnlyWait, dualStreamCopyOffloadOperation);
            if (resolveIoqDependencyWithBarrier) {
                NEO::PipeControlArgs args;
                if (this->consumeTextureCacheFlushPending()) {
                    args.textureCacheInvalidationEnable = true;
                } else {
                    args.csStallOnly = true;
                    if (this->isPostSyncSkippedOnLatestInOrderOperation) {
                        args.isL1FlushRequired = true;
                    }
                }
                NEO::MemorySynchronizationCommands<GfxFamily>::addSingleBarrier(*commandContainer.getCommandStream(), args);
                break;
            } else {
                bool indirectMode = false;
                const bool useSemaphore64bCmd = this->device->getDeviceInfo().semaphore64bCmdSupport;
                const bool qwordIndirect = NEO::InOrderProgrammingHelpers::isLriFor64bDataProgrammingRequired(isQwordInOrderCounter(), useSemaphore64bCmd);

                NEO::EncodeCaptureCommandData cmdCaptureData = {};
                if (outListCommands != nullptr) {
                    cmdCaptureData.makeCommandView = outListCommands->makeCommandView;
                }

                if (qwordIndirect) {
                    indirectMode = true;

                    constexpr uint32_t firstRegister = RegisterOffsets::csGprR0;
                    constexpr uint32_t secondRegister = RegisterOffsets::csGprR0 + 4;

                    NEO::EncodeSetMMIO<GfxFamily>::encodeIMM(*commandContainer.getCommandStream(), firstRegister, getLowPart(waitValue), true, copyOnlyWait, &cmdCaptureData);
                    if (noopDispatch) {
                        memset(cmdCaptureData.cpuBuffer, 0, cmdCaptureData.cmdSize);
                    }
                    if (outListCommands != nullptr) {
                        outListCommands->emplace_back(PatchCbWaitEventLoadRegisterImm{
                            .gpuDestination = cmdCaptureData.gpuAddress,
                            .pDestination = cmdCaptureData.cpuBuffer,
                            .commandView = cmdCaptureData.commandView,
                            .offset = firstRegister,
                            .patchSize = cmdCaptureData.cmdSize});
                    }

                    NEO::EncodeSetMMIO<GfxFamily>::encodeIMM(*commandContainer.getCommandStream(), secondRegister, getHighPart(waitValue), true, copyOnlyWait, &cmdCaptureData);
                    if (noopDispatch) {
                        memset(cmdCaptureData.cpuBuffer, 0, cmdCaptureData.cmdSize);
                    }
                    if (outListCommands != nullptr) {
                        outListCommands->emplace_back(PatchCbWaitEventLoadRegisterImm{
                            .gpuDestination = cmdCaptureData.gpuAddress,
                            .pDestination = cmdCaptureData.cpuBuffer,
                            .commandView = cmdCaptureData.commandView,
                            .offset = secondRegister,
                            .patchSize = cmdCaptureData.cmdSize});
                    }
                }

                auto switchOnUnsuccessful = !implicitDependency;
                NEO::EncodeSemaphore<GfxFamily>::addMiSemaphoreWaitCommand(*commandContainer.getCommandStream(), gpuAddress, waitValue, COMPARE_OPERATION::COMPARE_OPERATION_SAD_GREATER_THAN_OR_EQUAL_SDD,
                                                                           false, isQwordInOrderCounter(), indirectMode, switchOnUnsuccessful, useSemaphore64bCmd, &cmdCaptureData);
                if (noopDispatch) {
                    memset(cmdCaptureData.cpuBuffer, 0, cmdCaptureData.cmdSize);
                }

                if (outListCommands != nullptr) {
                    outListCommands->emplace_back(PatchCbWaitEventSemaphoreWait{
                        .gpuDestination = cmdCaptureData.gpuAddress,
                        .pDestination = cmdCaptureData.cpuBuffer,
                        .commandView = cmdCaptureData.commandView,
                        .offset = i * immWriteOffset,
                        .patchSize = cmdCaptureData.cmdSize});
                }
            }
        }

        gpuAddress += immWriteOffset;
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::canSkipInOrderEventWait(Event &event, bool ignorCbEventBoundToCmdList) const {
    if (isInOrderExecutionEnabled() && !Event::isAggregatedEvent(&event)) {
        return ((isImmediateType() && event.getLatestUsedCmdQueue() == this->cmdQImmediate) || // 1. Immediate CmdList can skip "regular Events" from the same CmdList
                (isCbEventBoundToCmdList(&event) && !ignorCbEventBoundToCmdList));             // 2. Both Immediate and Regular CmdLists can skip "CounterBased Events" from the same CmdList
    }

    return false;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendWaitOnEvents(uint32_t numEvents, ze_event_handle_t *phEvent, CmdListWaitEventParameters &waitEventParams) {

    auto swTagScope = emplaceSWTagScope("zeCommandListAppendWaitOnEvents");

    const bool dualStreamCopyOffload = isDualStreamCopyOffloadOperation(waitEventParams.dualStreamCopyOffloadOperation);

    if (this->isInOrderExecutionEnabled() && waitEventParams.apiRequest) {
        handleInOrderImplicitDependencies(false, dualStreamCopyOffload);
    }

    for (uint32_t i = 0; i < numEvents; i++) {
        auto event = Event::fromHandle(phEvent[i]);

        if (!event) {
            return ZE_RESULT_ERROR_INVALID_ARGUMENT;
        }

        if (event->isCounterBased()) {
            if (event->getInOrderExecEventHelper().is2WayIpcSharingEnabled()) {
                event->refreshImported2WayIpcCbData();
            }
            if (!event->getInOrderExecEventHelper().isDataAssigned()) {
                return ZE_RESULT_ERROR_INVALID_ARGUMENT; // in-order event not signaled yet
            }
        }

        if ((isImmediateType() && event->isAlreadyCompleted()) ||
            canSkipInOrderEventWait(*event, this->allowCbWaitEventsNoopDispatch)) {
            continue;
        }

        if (event->isCounterBased() && (this->heaplessModeEnabled || !event->hasInOrderTimestampNode())) {
            auto &inOrderExecHelper = event->getInOrderExecEventHelper();
            CommandListCoreFamily<gfxCoreFamily>::appendWaitOnInOrderDependency(inOrderExecHelper.getDeviceCounterAllocation(), inOrderExecHelper.getBaseDeviceAddress(), inOrderExecHelper.getEventData()->devicePartitions,
                                                                                waitEventParams.outWaitCmds,
                                                                                event->getInOrderExecBaseSignalValue(), event->getInOrderAllocationOffset(),
                                                                                waitEventParams.relaxedOrderingAllowed, false, waitEventParams.skipAddingWaitEventsToResidency,
                                                                                isCbEventBoundToCmdList(event), dualStreamCopyOffload);

            continue;
        }

        if (!isImmediateType()) {
            event->disableImplicitCounterBasedMode();
        }

        if (!waitEventParams.skipAddingWaitEventsToResidency) {
            commandContainer.addToResidencyContainer(event->getAllocation(this->device));
        }

        appendWaitOnSingleEvent<PatchWaitEventSemaphoreWait>(event, waitEventParams.outWaitCmds, waitEventParams.relaxedOrderingAllowed, dualStreamCopyOffload);
    }

    if (isImmediateType() && isCopyOnly(dualStreamCopyOffload) && waitEventParams.trackDependencies) {
        NEO::MiFlushArgs args{this->dummyBlitWa};
        args.commandWithPostSync = true;
        auto csr = getCsr(false);
        encodeMiFlush(csr->getBarrierCountGpuAddress(), csr->getNextBarrierCount() + 1, args);
        commandContainer.addToResidencyContainer(csr->getTagAllocation());
    }

    if (waitEventParams.apiRequest) {
        if (this->isInOrderExecutionEnabled()) {
            appendSignalInOrderDependencyCounter(nullptr, false, false, false, false);
        }
        handleInOrderDependencyCounter(nullptr, false, false);
    }

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendSdiInOrderCounterSignalling(uint64_t baseGpuVa, uint64_t signalValue, bool copyOffloadOperation) {
    using MI_STORE_DATA_IMM = typename GfxFamily::MI_STORE_DATA_IMM;

    UNRECOVERABLE_IF(isNonDualStreamCopyOffloadOperation(copyOffloadOperation));

    uint64_t gpuVa = baseGpuVa + inOrderExecInfo->getAllocationOffset();

    uint32_t numWrites = 1;
    bool partitionOffsetEnabled = this->partitionCount > 1;

    if (copyOffloadOperation && partitionOffsetEnabled) {
        numWrites = this->partitionCount;
        partitionOffsetEnabled = false;
    }

    for (uint32_t i = 0; i < numWrites; i++) {
        auto miStoreCmd = reinterpret_cast<MI_STORE_DATA_IMM *>(commandContainer.getCommandStream()->getSpace(sizeof(MI_STORE_DATA_IMM)));

        NEO::EncodeStoreMemory<GfxFamily>::programStoreDataImm(miStoreCmd, gpuVa, getLowPart(signalValue), getHighPart(signalValue),
                                                               isQwordInOrderCounter(), partitionOffsetEnabled);

        gpuVa += device->getL0GfxCoreHelper().getImmediateWritePostSyncOffset();
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendSignalAggregatedEventAtomic(Event &event, bool dualStreamCopyOffload) {
    using ATOMIC_OPCODES = typename GfxFamily::MI_ATOMIC::ATOMIC_OPCODES;
    using DATA_SIZE = typename GfxFamily::MI_ATOMIC::DATA_SIZE;

    auto partitionCountForAtomic = dualStreamCopyOffload ? 1u : this->partitionCount;

    auto incValue = event.getInOrderIncrementValue(partitionCountForAtomic);

    NEO::EncodeAtomic<GfxFamily>::programMiAtomic(*commandContainer.getCommandStream(), event.getInOrderExecEventHelper().getBaseDeviceAddress(), ATOMIC_OPCODES::ATOMIC_8B_ADD,
                                                  DATA_SIZE::DATA_SIZE_QWORD, 0, 0, incValue, 0);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendSignalInOrderDependencyCounter(Event *signalEvent, bool copyOffloadOperation, bool stall, bool textureFlushRequired, bool skipAggregatedEventSignaling) {
    using ATOMIC_OPCODES = typename GfxFamily::MI_ATOMIC::ATOMIC_OPCODES;
    using DATA_SIZE = typename GfxFamily::MI_ATOMIC::DATA_SIZE;

    UNRECOVERABLE_IF(isNonDualStreamCopyOffloadOperation(copyOffloadOperation));

    uint64_t deviceAllocGpuVa = inOrderExecInfo->getBaseDeviceAddress();
    uint64_t signalValue = inOrderExecInfo->getCounterValue() + getInOrderIncrementValue();

    auto cmdStream = commandContainer.getCommandStream();

    if (isCopyOnly(copyOffloadOperation) && !this->useAdditionalBlitProperties && NEO::debugManager.flags.InOrderCopyMiFlushSync.get() != 0) {
        NEO::MiFlushArgs args{this->dummyBlitWa};
        encodeMiFlush(0, 0, args);
    }

    if (stall && !copyOffloadOperation) {
        NEO::PipeControlArgs args;
        args.dcFlushEnable = signalEvent ? getDcFlushRequired(signalEvent->isSignalScope()) : false;
        args.workloadPartitionOffset = partitionCount > 1;
        args.textureCacheInvalidationEnable = textureFlushRequired;
        args.isWalkerWithProfilingEnqueued = this->getAndClearIsWalkerWithProfilingEnqueued();
        NEO::MemorySynchronizationCommands<GfxFamily>::addBarrierWithPostSyncOperation(
            *cmdStream,
            NEO::PostSyncMode::immediateData,
            deviceAllocGpuVa + inOrderExecInfo->getAllocationOffset(),
            signalValue,
            device->getNEODevice()->getRootDeviceEnvironment(),
            args);

    } else if (this->inOrderAtomicSignalingEnabled) {
        ATOMIC_OPCODES opcode = ATOMIC_OPCODES::ATOMIC_8B_INCREMENT;
        uint64_t operand1Data = 0;

        if (copyOffloadOperation && this->partitionCount > 1) {
            opcode = ATOMIC_OPCODES::ATOMIC_8B_ADD;
            operand1Data = this->partitionCount;
        }

        NEO::EncodeAtomic<GfxFamily>::programMiAtomic(*cmdStream, deviceAllocGpuVa, opcode,
                                                      DATA_SIZE::DATA_SIZE_QWORD, 0, 0, operand1Data, 0);

    } else {
        appendSdiInOrderCounterSignalling(deviceAllocGpuVa, signalValue, copyOffloadOperation);
    }

    if (inOrderExecInfo->isHostStorageDuplicated()) {
        appendSdiInOrderCounterSignalling(inOrderExecInfo->getBaseHostGpuAddress(), signalValue, copyOffloadOperation);
    }

    if (!skipAggregatedEventSignaling && Event::isAggregatedEvent(signalEvent)) {
        appendSignalAggregatedEventAtomic(*signalEvent, copyOffloadOperation);
    }

    const bool interruptModeEmit = (NEO::debugManager.flags.ProgramUserInterruptOnResolvedDependency.get() == 1 || isCopyOnly(copyOffloadOperation)) && signalEvent && signalEvent->isInterruptModeEnabled();
    const bool counterBasedUserInterruptEmit = signalEvent && signalEvent->isCounterBased() && signalEvent->isSignalWithUserInterrupt() && !signalEvent->getAllocation(this->device);
    if (interruptModeEmit || counterBasedUserInterruptEmit) {
        NEO::EncodeUserInterrupt<GfxFamily>::encode(*cmdStream);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::programSyncBuffer(Kernel &kernel, NEO::Device &device,
                                                                    const ze_group_count_t &threadGroupDimensions, size_t &patchIndex) {
    uint32_t maximalNumberOfWorkgroupsAllowed = kernel.suggestMaxCooperativeGroupCount(this->engineGroupType, false);

    size_t requestedNumberOfWorkgroups = (threadGroupDimensions.groupCountX * threadGroupDimensions.groupCountY * threadGroupDimensions.groupCountZ);
    if (requestedNumberOfWorkgroups > maximalNumberOfWorkgroupsAllowed) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    auto [gfxAllocation, bufferOffset] = NEO::KernelHelper::getSyncBufferAllocationOffset(device, requestedNumberOfWorkgroups);
    kernel.patchSyncBuffer(gfxAllocation, bufferOffset);

    if (!isImmediateType()) {
        patchIndex = commandsToPatch.size();
        commandsToPatch.push_back(PatchNoopSpace{});

        auto &syncBufferSpace = std::get<PatchNoopSpace>(commandsToPatch[patchIndex]);
        syncBufferSpace.offset = bufferOffset;
        syncBufferSpace.pDestination = ptrOffset(gfxAllocation->getUnderlyingBuffer(), bufferOffset);
        syncBufferSpace.patchSize = NEO::KernelHelper::getSyncBufferSize(requestedNumberOfWorkgroups);
        syncBufferSpace.gpuAddress = gfxAllocation->getGpuAddressToPatch() + bufferOffset;

        this->totalNoopSpace += syncBufferSpace.patchSize;
    }

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendWriteKernelTimestamp(Event *event, CommandToPatchContainer *outTimeStampSyncCmds, bool beforeWalker, bool maskLsb, bool workloadPartition, bool copyOperation) {
    auto baseAddr = event->getPacketAddress(this->device);
    writeKernelTimestamp(baseAddr, event, outTimeStampSyncCmds, beforeWalker ? event->getGlobalStartOffset() : event->getGlobalEndOffset(), maskLsb, workloadPartition, copyOperation, true);
    writeKernelTimestamp(baseAddr, event, outTimeStampSyncCmds, beforeWalker ? event->getContextStartOffset() : event->getContextEndOffset(), maskLsb, workloadPartition, copyOperation, false);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::writeKernelTimestamp(uint64_t baseAddr, Event *event, CommandToPatchContainer *outTimeStampSyncCmds, size_t offset, bool maskLsb, bool workloadPartition, bool copyOperation, bool isGlobalTimestamp) {
    void **postSyncCmdBuffer = nullptr;
    void *postSyncCmd = nullptr;
    if (outTimeStampSyncCmds != nullptr) {
        postSyncCmdBuffer = &postSyncCmd;
    }
    uint64_t address = ptrOffset(baseAddr, offset);

    uint32_t registerOffset = isGlobalTimestamp ? RegisterOffsets::globalTimestampLdw : NEO::ContextTimestampRegister<GfxFamily>::getRegisterOffsetLow();
    writeTimestamp(commandContainer, registerOffset, address, maskLsb, workloadPartition, postSyncCmdBuffer, copyOperation);
    pushTimestampPatch(outTimeStampSyncCmds, offset, postSyncCmd);
    adjustWriteKernelTimestamp(address, baseAddr, outTimeStampSyncCmds, workloadPartition, copyOperation, isGlobalTimestamp);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::writeTimestamp(NEO::CommandContainer &container, uint32_t regOffset, uint64_t address, bool maskLsb, bool workloadPartition, void **postSyncCmdBuffer, bool copyOperation) {
    constexpr uint32_t mask = 0xfffffffe;
    if (maskLsb) {
        NEO::EncodeMathMMIO<GfxFamily>::encodeBitwiseAndVal(
            container, regOffset, mask, address, workloadPartition, postSyncCmdBuffer, copyOperation);
    } else {
        NEO::EncodeStoreMMIO<GfxFamily>::encode(
            *container.getCommandStream(), regOffset, address, workloadPartition, postSyncCmdBuffer, copyOperation);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::pushTimestampPatch(CommandToPatchContainer *container, uint64_t offset, void *pDestination) {
    if (container) {
        container->emplace_back(PatchTimestampEventPostSyncStoreRegMem{
            .pDestination = pDestination,
            .offset = offset,
        });
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendEventForProfiling(Event *event, CommandToPatchContainer *outTimeStampSyncCmds, bool beforeWalker, bool skipBarrierForEndProfiling, bool skipAddingEventToResidency, bool copyOperation) {
    if (!event) {
        return;
    }

    if (copyOperation) {
        appendEventForProfilingCopyCommand(event, beforeWalker);
    } else {
        if (!event->isEventTimestampFlagSet()) {
            return;
        }

        if (!skipAddingEventToResidency) {
            commandContainer.addToResidencyContainer(event->getAllocation(this->device));
        }
        bool workloadPartition = isTimestampEventForMultiTile(event);

        appendDispatchOffsetRegister(workloadPartition, true);

        if (beforeWalker) {
            event->resetKernelCountAndPacketUsedCount();
            bool workloadPartition = setupTimestampEventForMultiTile(event);
            appendWriteKernelTimestamp(event, outTimeStampSyncCmds, beforeWalker, true, workloadPartition, copyOperation);
        } else {
            dispatchEventPostSyncOperation(event, nullptr, nullptr, Event::STATE_SIGNALED, true, false, false, true, copyOperation);

            const auto &rootDeviceEnvironment = this->device->getNEODevice()->getRootDeviceEnvironment();

            if (!skipBarrierForEndProfiling) {
                NEO::PipeControlArgs args;
                args.dcFlushEnable = getDcFlushRequired(event->isSignalScope());
                args.textureCacheInvalidationEnable = this->consumeTextureCacheFlushPending();
                NEO::MemorySynchronizationCommands<GfxFamily>::setPostSyncExtraProperties(args);

                NEO::MemorySynchronizationCommands<GfxFamily>::addSingleBarrier(*commandContainer.getCommandStream(), args);
            }

            uint64_t baseAddr = event->getGpuAddress(this->device);
            NEO::MemorySynchronizationCommands<GfxFamily>::addAdditionalSynchronization(*commandContainer.getCommandStream(), baseAddr, NEO::FenceType::release, rootDeviceEnvironment);
            appendWriteKernelTimestamp(event, outTimeStampSyncCmds, beforeWalker, true, workloadPartition, copyOperation);
        }

        appendDispatchOffsetRegister(workloadPartition, false);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendWriteGlobalTimestamp(
    uint64_t *dstptr, ze_event_handle_t hSignalEvent,
    uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents,
    CmdListWaitEventParameters &waitEventParams) {
    ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, waitEventParams);
    if (ret != ZE_RESULT_SUCCESS) {
        return ret;
    }

    appendSynchronizedDispatchInitializationSection();

    Event *signalEvent = nullptr;
    if (hSignalEvent) {
        signalEvent = Event::fromHandle(hSignalEvent);
    }

    if (!handleCounterBasedEventOperations(signalEvent, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    appendEventForProfiling(signalEvent, nullptr, true, false, false, isCopyOnly(false));

    auto allocationStruct = resolveAlignedAllocation(this->device, dstptr, sizeof(uint64_t), nullptr, {});
    if (allocationStruct.alloc == nullptr) {
        return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
    }
    commandContainer.addToResidencyContainer(allocationStruct.alloc);

    if (isCopyOnly(false)) {
        NEO::MiFlushArgs args{this->dummyBlitWa};
        args.timeStampOperation = true;
        args.commandWithPostSync = true;
        encodeMiFlush(allocationStruct.alignedAllocationPtr, 0, args);
    } else {
        NEO::PipeControlArgs args;
        args.blockSettingPostSyncProperties = true;
        args.isWalkerWithProfilingEnqueued = this->getAndClearIsWalkerWithProfilingEnqueued();

        NEO::MemorySynchronizationCommands<GfxFamily>::addBarrierWithPostSyncOperation(
            *commandContainer.getCommandStream(),
            NEO::PostSyncMode::timestamp,
            allocationStruct.alignedAllocationPtr,
            0,
            this->device->getNEODevice()->getRootDeviceEnvironment(),
            args);
    }

    appendSignalEventPostWalker(signalEvent, nullptr, nullptr, false, false, isCopyOnly(false));

    if (this->isInOrderExecutionEnabled()) {
        appendSignalInOrderDependencyCounter(signalEvent, false, false, false, false);
    }
    handleInOrderDependencyCounter(signalEvent, false, false);

    appendSynchronizedDispatchCleanupSection();

    addToMappedEventList(signalEvent);

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopyFromContext(
    void *dstptr, ze_context_handle_t hContextSrc, const void *srcptr,
    size_t size, ze_event_handle_t hSignalEvent, uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents, CmdListMemoryCopyParams &memoryCopyParams) {
    return CommandListCoreFamily<gfxCoreFamily>::appendMemoryCopy(dstptr, srcptr, size, hSignalEvent, numWaitEvents, phWaitEvents, memoryCopyParams);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendQueryKernelTimestamps(
    uint32_t numEvents, ze_event_handle_t *phEvents, void *dstptr,
    const size_t *pOffsets, ze_event_handle_t hSignalEvent,
    uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents,
    CmdListWaitEventParameters &waitEventsParameters) {
    if (numEvents == 0) {
        ze_result_t ret = ZE_RESULT_SUCCESS;
        if (numWaitEvents > 0) {
            ret = this->appendWaitOnEvents(numWaitEvents, phWaitEvents, waitEventsParameters);
        }
        if (ret == ZE_RESULT_SUCCESS && hSignalEvent != nullptr) {
            ret = appendSignalEvent(hSignalEvent, false);
        }
        return ret;
    }
    auto dstPtrAllocationStruct = resolveAlignedAllocation(this->device, dstptr, sizeof(ze_kernel_timestamp_result_t) * numEvents, nullptr, {});
    if (dstPtrAllocationStruct.alloc == nullptr) {
        return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
    }
    commandContainer.addToResidencyContainer(dstPtrAllocationStruct.alloc);

    std::unique_ptr<EventData[]> timestampsData = std::make_unique_for_overwrite<EventData[]>(numEvents);

    for (uint32_t i = 0u; i < numEvents; ++i) {
        auto event = Event::fromHandle(phEvents[i]);
        commandContainer.addToResidencyContainer(event->getAllocation(this->device));
        timestampsData[i].address = event->getGpuAddress(this->device);
        timestampsData[i].packetsInUse = event->getPacketsInUse();
        timestampsData[i].timestampSizeInDw = event->getTimestampSizeInDw();
    }

    size_t alignedSize = alignUp<size_t>(sizeof(EventData) * numEvents, MemoryConstants::pageSize64k);
    NEO::AllocationType allocationType = NEO::AllocationType::gpuTimestampDeviceBuffer;
    auto devices = device->getNEODevice()->getDeviceBitfield();
    NEO::AllocationProperties allocationProperties{device->getRootDeviceIndex(),
                                                   true,
                                                   alignedSize,
                                                   allocationType,
                                                   devices.count() > 1,
                                                   false,
                                                   devices};

    NEO::GraphicsAllocation *timestampsGPUData = device->getDriverHandle()->getMemoryManager()->allocateGraphicsMemoryWithProperties(allocationProperties);

    UNRECOVERABLE_IF(timestampsGPUData == nullptr);

    commandContainer.addToResidencyContainer(timestampsGPUData);
    commandContainer.getDeallocationContainer().push_back(timestampsGPUData);

    bool result = device->getDriverHandle()->getMemoryManager()->copyMemoryToAllocation(timestampsGPUData, 0, timestampsData.get(), sizeof(EventData) * numEvents);

    UNRECOVERABLE_IF(!result);

    Kernel *builtinKernel = nullptr;
    auto useOnlyGlobalTimestampsValue = this->useOnlyGlobalTimestamps ? 1u : 0u;
    auto lock = device->getBuiltinFunctionsLib()->obtainUniqueOwnership();

    auto builtInMode = this->defaultBuiltInMode;
    if (pOffsets == nullptr) {
        builtinKernel = device->getBuiltinFunctionsLib()->getFunction(BufferBuiltIn::queryKernelTimestamps, builtInMode);
        builtinKernel->setArgumentValue(2u, sizeof(uint32_t), &useOnlyGlobalTimestampsValue);
    } else {
        auto pOffsetAllocationStruct = resolveAlignedAllocation(this->device, pOffsets, sizeof(size_t) * numEvents, nullptr, {});
        if (pOffsetAllocationStruct.alloc == nullptr) {
            return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
        }

        auto offsetValPtr = static_cast<uintptr_t>(pOffsetAllocationStruct.alloc->getGpuAddress());
        commandContainer.addToResidencyContainer(pOffsetAllocationStruct.alloc);
        builtinKernel = device->getBuiltinFunctionsLib()->getFunction(BufferBuiltIn::queryKernelTimestampsWithOffsets, builtInMode);
        builtinKernel->setArgBufferWithAlloc(2, offsetValPtr, pOffsetAllocationStruct.alloc, nullptr);
        builtinKernel->setArgumentValue(3u, sizeof(uint32_t), &useOnlyGlobalTimestampsValue);
        offsetValPtr += sizeof(size_t);
    }

    uint32_t groupSizeX = 1u;
    uint32_t groupSizeY = 1u;
    uint32_t groupSizeZ = 1u;

    ze_result_t ret = builtinKernel->suggestGroupSize(numEvents, 1u, 1u,
                                                      &groupSizeX, &groupSizeY, &groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    ret = builtinKernel->setGroupSize(groupSizeX, groupSizeY, groupSizeZ);
    if (ret != ZE_RESULT_SUCCESS) {
        DEBUG_BREAK_IF(true);
        return ret;
    }

    for (uint32_t i = 0; i < numEvents; i++) {
        auto event = Event::fromHandle(phEvents[i]);
        if (event->isCounterBased()) {
            appendWaitOnSingleEvent<PatchCbEventTimestampPostSyncSemaphoreWait>(event, nullptr, false, false);
        }
    }

    ze_group_count_t dispatchKernelArgs{numEvents / groupSizeX, 1u, 1u};

    auto dstValPtr = static_cast<uintptr_t>(dstPtrAllocationStruct.alignedAllocationPtr + dstPtrAllocationStruct.offset);

    builtinKernel->setArgBufferWithAlloc(0u, static_cast<uintptr_t>(timestampsGPUData->getGpuAddress()), timestampsGPUData, nullptr);
    builtinKernel->setArgBufferWithAlloc(1, dstValPtr, dstPtrAllocationStruct.alloc, nullptr);

    auto dstAllocationType = dstPtrAllocationStruct.alloc->getAllocationType();
    CmdListKernelLaunchParams launchParams = {};
    launchParams.isBuiltInKernel = true;
    launchParams.isDestinationAllocationInSystemMemory =
        (dstAllocationType == NEO::AllocationType::bufferHostMemory) ||
        (dstAllocationType == NEO::AllocationType::externalHostPtr);

    if constexpr (checkIfAllocationImportedRequired()) {
        launchParams.isDestinationAllocationImported = dstPtrAllocationStruct.alloc->getIsImported();
    }
    launchParams.outListCommands = waitEventsParameters.outWaitCmds;
    launchParams.omitAddingWaitEventsResidency = waitEventsParameters.skipAddingWaitEventsToResidency;
    launchParams.relaxedOrderingDispatch = waitEventsParameters.relaxedOrderingAllowed;
    auto appendResult = appendLaunchKernel(builtinKernel->toHandle(), dispatchKernelArgs, hSignalEvent, numWaitEvents,
                                           phWaitEvents, launchParams);
    waitEventsParameters.skipAddingWaitEventsToResidency = launchParams.omitAddingWaitEventsResidency;
    waitEventsParameters.outWaitCmds = launchParams.outListCommands;
    if (appendResult != ZE_RESULT_SUCCESS) {
        return appendResult;
    }

    addToMappedEventList(Event::fromHandle(hSignalEvent));

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::hostSynchronize(uint64_t timeout) {
    return ZE_RESULT_ERROR_INVALID_ARGUMENT;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::reserveSpace(size_t size, void **ptr) {
    auto availableSpace = commandContainer.getCommandStream()->getAvailableSpace();
    if (availableSpace < size) {
        *ptr = nullptr;
    } else {
        *ptr = commandContainer.getCommandStream()->getSpace(size);
    }
    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::prepareIndirectParams(const ze_group_count_t *threadGroupDimensions) {
    auto allocData = device->getDriverHandle()->getSvmAllocsManager()->getSVMAlloc(static_cast<const void *>(threadGroupDimensions));
    if (allocData) {
        auto groupCountAllocData = resolveAlignedAllocation(this->device, threadGroupDimensions, sizeof(ze_group_count_t), nullptr, {});
        commandContainer.addToResidencyContainer(groupCountAllocData.alloc);
        auto groupCount = ptrOffset(groupCountAllocData.alignedAllocationPtr, groupCountAllocData.offset);

        NEO::EncodeSetMMIO<GfxFamily>::encodeMEM(commandContainer, RegisterOffsets::gpgpuDispatchDimX,
                                                 ptrOffset(groupCount, offsetof(ze_group_count_t, groupCountX)), isCopyOnly(false));
        NEO::EncodeSetMMIO<GfxFamily>::encodeMEM(commandContainer, RegisterOffsets::gpgpuDispatchDimY,
                                                 ptrOffset(groupCount, offsetof(ze_group_count_t, groupCountY)), isCopyOnly(false));
        NEO::EncodeSetMMIO<GfxFamily>::encodeMEM(commandContainer, RegisterOffsets::gpgpuDispatchDimZ,
                                                 ptrOffset(groupCount, offsetof(ze_group_count_t, groupCountZ)), isCopyOnly(false));
    }

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::updateStreamProperties(Kernel &kernel, bool isCooperative, const ze_group_count_t &threadGroupDimensions, bool isIndirect) {
    if (isImmediateType()) {
        updateStreamPropertiesForFlushTaskDispatchFlags(kernel, isCooperative, threadGroupDimensions, isIndirect);
    } else {
        updateStreamPropertiesForRegularCommandLists(kernel, isCooperative, threadGroupDimensions, isIndirect);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
inline bool getFusedEuDisabled(Kernel &kernel, Device *device, const ze_group_count_t &threadGroupDimensions, bool isIndirect) {
    auto &kernelAttributes = kernel.getKernelDescriptor().kernelAttributes;

    bool fusedEuDisabled = kernelAttributes.flags.requiresDisabledEUFusion;
    if (device->calculationForDisablingEuFusionWithDpasNeeded) {
        auto &productHelper = device->getProductHelper();
        uint32_t *groupCountPtr = nullptr;
        uint32_t groupCount[3] = {};
        if (!isIndirect) {
            groupCount[0] = threadGroupDimensions.groupCountX;
            groupCount[1] = threadGroupDimensions.groupCountY;
            groupCount[2] = threadGroupDimensions.groupCountZ;
            groupCountPtr = groupCount;
        }
        fusedEuDisabled |= productHelper.isFusedEuDisabledForDpas(kernelAttributes.flags.usesSystolicPipelineSelectMode, kernel.getGroupSize(), groupCountPtr, device->getHwInfo());
    }
    return fusedEuDisabled;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::updateStreamPropertiesForFlushTaskDispatchFlags(Kernel &kernel, bool isCooperative, const ze_group_count_t &threadGroupDimensions, bool isIndirect) {
    auto &kernelAttributes = kernel.getKernelDescriptor().kernelAttributes;

    bool fusedEuDisabled = getFusedEuDisabled<gfxCoreFamily>(kernel, this->device, threadGroupDimensions, isIndirect);

    requiredStreamState.stateComputeMode.setPropertiesGrfNumberThreadArbitration(kernelAttributes.numGrfRequired, kernelAttributes.threadArbitrationPolicy);

    requiredStreamState.frontEndState.setPropertiesComputeDispatchAllWalkerEnableDisableEuFusion(isCooperative, fusedEuDisabled);

    requiredStreamState.pipelineSelect.setPropertySystolicMode(kernelAttributes.flags.usesSystolicPipelineSelectMode);

    KernelImp &kernelImp = static_cast<KernelImp &>(kernel);
    int32_t currentMocsState = static_cast<int32_t>(device->getMOCS(!kernelImp.getKernelRequiresUncachedMocs(), false) >> 1);
    requiredStreamState.stateBaseAddress.setPropertyStatelessMocs(currentMocsState);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendVfeStateCmdToPatch() {
    if constexpr (GfxFamily::isHeaplessRequired() == false) {
        auto &rootDeviceEnvironment = device->getNEODevice()->getRootDeviceEnvironment();
        using FrontEndStateCommand = typename GfxFamily::FrontEndStateCommand;
        uint64_t gpuAddress = 0;
        auto frontEndStateAddress = NEO::PreambleHelper<GfxFamily>::getSpaceForVfeState(commandContainer.getCommandStream(), device->getHwInfo(), engineGroupType, &gpuAddress);
        auto frontEndStateCmd = new FrontEndStateCommand;
        NEO::PreambleHelper<GfxFamily>::programVfeState(frontEndStateCmd, rootDeviceEnvironment, 0, 0, device->getMaxNumHwThreads(), finalStreamState);

        commandsToPatch.push_back(PatchFrontEndState{
            .pDestination = frontEndStateAddress,
            .pCommand = frontEndStateCmd,
            .gpuAddress = gpuAddress,
        });
        this->frontEndPatchListCount++;
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::updateStreamPropertiesForRegularCommandLists(Kernel &kernel, bool isCooperative, const ze_group_count_t &threadGroupDimensions, bool isIndirect) {
    size_t currentSurfaceStateSize = NEO::StreamPropertySizeT::initValue;
    size_t currentDynamicStateSize = NEO::StreamPropertySizeT::initValue;
    size_t currentIndirectObjectSize = NEO::StreamPropertySizeT::initValue;
    size_t currentBindingTablePoolSize = NEO::StreamPropertySizeT::initValue;

    auto &rootDeviceEnvironment = device->getNEODevice()->getRootDeviceEnvironment();
    auto &kernelAttributes = kernel.getKernelDescriptor().kernelAttributes;

    const auto bindlessHeapsHelper = device->getNEODevice()->getBindlessHeapsHelper();
    const bool useGlobalHeaps = bindlessHeapsHelper != nullptr;

    KernelImp &kernelImp = static_cast<KernelImp &>(kernel);

    int32_t currentMocsState = static_cast<int32_t>(device->getMOCS(!kernelImp.getKernelRequiresUncachedMocs(), false) >> 1);
    bool checkSsh = false;
    bool checkDsh = false;
    bool checkIoh = false;

    if (this->cmdListHeapAddressModel == NEO::HeapAddressModel::privateHeaps) {
        if (currentSurfaceStateBaseAddress == NEO::StreamProperty64::initValue || commandContainer.isHeapDirty(NEO::IndirectHeap::Type::surfaceState)) {
            auto ssh = commandContainer.getIndirectHeap(NEO::IndirectHeap::Type::surfaceState);
            if (ssh) {
                currentSurfaceStateBaseAddress = NEO::getStateBaseAddressForSsh(*ssh, useGlobalHeaps);
                currentSurfaceStateSize = NEO::getStateSizeForSsh(*ssh, useGlobalHeaps);

                currentBindingTablePoolBaseAddress = currentSurfaceStateBaseAddress;
                currentBindingTablePoolSize = currentSurfaceStateSize;

                checkSsh = true;
            }
            DEBUG_BREAK_IF(ssh == nullptr && commandContainer.isHeapDirty(NEO::IndirectHeap::Type::surfaceState));
        }

        if (this->dynamicHeapRequired && (currentDynamicStateBaseAddress == NEO::StreamProperty64::initValue || commandContainer.isHeapDirty(NEO::IndirectHeap::Type::dynamicState))) {
            auto dsh = commandContainer.getIndirectHeap(NEO::IndirectHeap::Type::dynamicState);

            currentDynamicStateBaseAddress = NEO::getStateBaseAddress(*dsh, useGlobalHeaps);
            currentDynamicStateSize = NEO::getStateSize(*dsh, useGlobalHeaps);

            checkDsh = true;
        }
    }

    if (currentIndirectObjectBaseAddress == NEO::StreamProperty64::initValue) {
        auto ioh = commandContainer.getIndirectHeap(NEO::IndirectHeap::Type::indirectObject);
        currentIndirectObjectBaseAddress = ioh->getHeapGpuBase();
        currentIndirectObjectSize = ioh->getHeapSizeInPages();

        checkIoh = true;
    }

    bool fusedEuDisabled = getFusedEuDisabled<gfxCoreFamily>(kernel, this->device, threadGroupDimensions, isIndirect);

    if (!containsAnyKernel) {
        requiredStreamState.frontEndState.setPropertiesComputeDispatchAllWalkerEnableDisableEuFusion(isCooperative, fusedEuDisabled);
        requiredStreamState.pipelineSelect.setPropertySystolicMode(kernelAttributes.flags.usesSystolicPipelineSelectMode);

        requiredStreamState.stateBaseAddress.setPropertyStatelessMocs(currentMocsState);

        if (checkSsh) {
            requiredStreamState.stateBaseAddress.setPropertiesBindingTableSurfaceState(currentBindingTablePoolBaseAddress, currentBindingTablePoolSize,
                                                                                       currentSurfaceStateBaseAddress, currentSurfaceStateSize);
        }
        if (checkDsh) {
            requiredStreamState.stateBaseAddress.setPropertiesDynamicState(currentDynamicStateBaseAddress, currentDynamicStateSize);
        }
        requiredStreamState.stateBaseAddress.setPropertiesIndirectState(currentIndirectObjectBaseAddress, currentIndirectObjectSize);

        if (this->stateComputeModeTracking) {
            requiredStreamState.stateComputeMode.setPropertiesGrfNumberThreadArbitration(kernelAttributes.numGrfRequired, kernelAttributes.threadArbitrationPolicy);
            finalStreamState = requiredStreamState;
        } else {
            finalStreamState = requiredStreamState;
            requiredStreamState.stateComputeMode.setPropertiesAll(cmdListDefaultCoherency, kernelAttributes.numGrfRequired, kernelAttributes.threadArbitrationPolicy, device->getDevicePreemptionMode(), device->getNEODevice()->hasAnyPeerAccess());
        }
        containsAnyKernel = true;
    }

    finalStreamState.pipelineSelect.setPropertySystolicMode(kernelAttributes.flags.usesSystolicPipelineSelectMode);
    if (this->pipelineSelectStateTracking && finalStreamState.pipelineSelect.isDirty()) {
        NEO::PipelineSelectArgs pipelineSelectArgs;
        pipelineSelectArgs.systolicPipelineSelectMode = kernelAttributes.flags.usesSystolicPipelineSelectMode;
        pipelineSelectArgs.systolicPipelineSelectSupport = this->systolicModeSupport;

        NEO::PreambleHelper<GfxFamily>::programPipelineSelect(commandContainer.getCommandStream(),
                                                              pipelineSelectArgs,
                                                              rootDeviceEnvironment);
    }

    finalStreamState.frontEndState.setPropertiesComputeDispatchAllWalkerEnableDisableEuFusion(isCooperative, fusedEuDisabled);
    bool isPatchingVfeStateAllowed = (NEO::debugManager.flags.AllowPatchingVfeStateInCommandLists.get() || (this->frontEndStateTracking && this->dispatchCmdListBatchBufferAsPrimary));
    if (finalStreamState.frontEndState.isDirty()) {
        if (isPatchingVfeStateAllowed) {
            appendVfeStateCmdToPatch();
        }

        if (this->frontEndStateTracking && !this->dispatchCmdListBatchBufferAsPrimary) {
            auto &stream = *commandContainer.getCommandStream();
            NEO::EncodeBatchBufferStartOrEnd<GfxFamily>::programBatchBufferEnd(stream);

            CmdListReturnPoint returnPoint = {
                {},
                stream.getGpuBase() + stream.getUsed(),
                stream.getGraphicsAllocation()};
            returnPoint.configSnapshot.frontEndState.copyPropertiesAll(finalStreamState.frontEndState);
            returnPoints.push_back(returnPoint);
        }
    }

    if (this->stateComputeModeTracking) {
        finalStreamState.stateComputeMode.setPropertiesGrfNumberThreadArbitration(kernelAttributes.numGrfRequired, kernelAttributes.threadArbitrationPolicy);
    } else {
        finalStreamState.stateComputeMode.setPropertiesAll(cmdListDefaultCoherency, kernelAttributes.numGrfRequired, kernelAttributes.threadArbitrationPolicy, device->getDevicePreemptionMode(), device->getNEODevice()->hasAnyPeerAccess());
    }
    if (finalStreamState.stateComputeMode.isDirty()) {
        bool isRcs = (this->engineGroupType == NEO::EngineGroupType::renderCompute);
        NEO::PipelineSelectArgs pipelineSelectArgs;
        pipelineSelectArgs.systolicPipelineSelectMode = kernelAttributes.flags.usesSystolicPipelineSelectMode;
        pipelineSelectArgs.systolicPipelineSelectSupport = this->systolicModeSupport;

        NEO::EncodeComputeMode<GfxFamily>::programComputeModeCommandWithSynchronization(
            *commandContainer.getCommandStream(), finalStreamState.stateComputeMode, pipelineSelectArgs, false, rootDeviceEnvironment, isRcs, this->dcFlushSupport);
    }

    finalStreamState.stateBaseAddress.setPropertyStatelessMocs(currentMocsState);
    if (checkSsh) {
        finalStreamState.stateBaseAddress.setPropertiesBindingTableSurfaceState(currentBindingTablePoolBaseAddress, currentBindingTablePoolSize,
                                                                                currentSurfaceStateBaseAddress, currentSurfaceStateSize);
    }
    if (checkDsh) {
        finalStreamState.stateBaseAddress.setPropertiesDynamicState(currentDynamicStateBaseAddress, currentDynamicStateSize);
    }
    if (checkIoh) {
        finalStreamState.stateBaseAddress.setPropertiesIndirectState(currentIndirectObjectBaseAddress, currentIndirectObjectSize);
    }

    const bool isDebuggerActive = device->getNEODevice()->getDebugger() != nullptr;
    finalStreamState.stateBaseAddress.setPropertyL1CachePolicy(static_cast<int32_t>(l1CachePolicyData.getL1CacheValue(isDebuggerActive)));

    if (this->stateBaseAddressTracking) {
        if (finalStreamState.stateBaseAddress.isDirty()) {
            commandContainer.setDirtyStateForAllHeaps(false);
            programStateBaseAddress(commandContainer, true);
            finalStreamState.stateBaseAddress.clearIsDirty();
        }
    } else if (!this->heaplessModeEnabled && finalStreamState.stateBaseAddress.l1CachePolicy.isDirty) {
        commandContainer.setDirtyStateForAllHeaps(true);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
inline size_t CommandListCoreFamily<gfxCoreFamily>::getTotalSizeForCopyRegion(const ze_copy_region_t *region, uint32_t pitch, uint32_t slicePitch) {
    const size_t originX = region->originX;
    const size_t originY = region->originY;
    const size_t originZ = region->originZ;
    const size_t width = region->width;
    const size_t height = region->height;
    const size_t depth = region->depth;

    size_t hostPtrOffsetInBytes = originZ * slicePitch + originY * pitch + originX;
    size_t hostPtrRegionSizeInbytes = width + pitch * (height - 1);
    if (depth > 1) {
        hostPtrRegionSizeInbytes += slicePitch * (depth - 1);
    }
    size_t hostPtrSize = hostPtrOffsetInBytes + hostPtrRegionSizeInbytes;
    return hostPtrSize;
}

inline NEO::MemoryPool getMemoryPoolFromAllocDataForSplit(bool allocFound, const NEO::SvmAllocationData *allocData) {
    if (allocFound) {
        return allocData->gpuAllocations.getDefaultGraphicsAllocation()->getMemoryPool();
    } else if (NEO::debugManager.flags.SplitBcsCopyHostptr.get() != 0) {
        return NEO::MemoryPool::system4KBPages;
    }
    return NEO::MemoryPool::memoryNull;
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isRemoteAlloc(NEO::SvmAllocationData *allocData) const {
    auto driver = this->device->getDriverHandle();

    if (allocData) {
        auto alloc = allocData->gpuAllocations.getGraphicsAllocation(device->getRootDeviceIndex());
        return (driver->isRemoteResourceNeeded(alloc, allocData, this->device) || allocData->isImportedAllocation);
    }

    return false;
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isAppendSplitNeeded(void *dstPtr, const void *srcPtr, size_t size, NEO::TransferDirection &directionOut) {
    if (size < minimalSizeForBcsSplit) {
        return false;
    }

    NEO::SvmAllocationData *srcAllocData = nullptr;
    NEO::SvmAllocationData *dstAllocData = nullptr;
    bool srcAllocFound = this->device->getDriverHandle()->findAllocationDataForRange(const_cast<void *>(srcPtr), size, srcAllocData);
    bool dstAllocFound = this->device->getDriverHandle()->findAllocationDataForRange(dstPtr, size, dstAllocData);

    auto srcMemoryPool = getMemoryPoolFromAllocDataForSplit(srcAllocFound, srcAllocData);
    auto dstMemoryPool = getMemoryPoolFromAllocDataForSplit(dstAllocFound, dstAllocData);
    for (const auto memoryPool : {srcMemoryPool, dstMemoryPool}) {
        if (memoryPool == NEO::MemoryPool::memoryNull) {
            return false;
        }
    }

    bool remoteCopy = isRemoteAlloc(srcAllocData) || isRemoteAlloc(dstAllocData);

    return this->isAppendSplitNeeded(dstMemoryPool, srcMemoryPool, size, directionOut, remoteCopy);
}

template <GFXCORE_FAMILY gfxCoreFamily>
inline bool CommandListCoreFamily<gfxCoreFamily>::isAppendSplitNeeded(NEO::MemoryPool dstPool, NEO::MemoryPool srcPool, size_t size, NEO::TransferDirection &directionOut, bool remoteCopy) {
    directionOut = NEO::createTransferDirection(!NEO::MemoryPoolHelper::isSystemMemoryPool(srcPool), !NEO::MemoryPoolHelper::isSystemMemoryPool(dstPool), remoteCopy);
    bool directionSupported = transferDirectionRequiresBcsSplit(directionOut);

    if (NEO::debugManager.flags.SplitBcsTransferDirectionMask.get() != -1) {
        directionSupported = (1 << static_cast<int32_t>(directionOut)) & NEO::debugManager.flags.SplitBcsTransferDirectionMask.get();
    }

    return (this->isBcsSplitEnabled() && (size >= minimalSizeForBcsSplit) && directionSupported);
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::transferDirectionRequiresBcsSplit(NEO::TransferDirection direction) const {
    return (direction != NEO::TransferDirection::localToLocal);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::programStateBaseAddress(NEO::CommandContainer &container, bool useSbaProperties) {
    using STATE_BASE_ADDRESS = typename GfxFamily::STATE_BASE_ADDRESS;

    bool isRcs = (this->engineGroupType == NEO::EngineGroupType::renderCompute);

    uint32_t statelessMocsIndex = this->defaultMocsIndex;
    NEO::StateBaseAddressProperties *sbaProperties = useSbaProperties ? &this->finalStreamState.stateBaseAddress : nullptr;

    STATE_BASE_ADDRESS sba;

    NEO::EncodeWA<GfxFamily>::addPipeControlBeforeStateBaseAddress(*commandContainer.getCommandStream(), this->device->getNEODevice()->getRootDeviceEnvironment(), isRcs, this->dcFlushSupport);

    NEO::EncodeStateBaseAddressArgs<GfxFamily> encodeStateBaseAddressArgs = {
        &commandContainer,                        // container
        sba,                                      // sbaCmd
        sbaProperties,                            // sbaProperties
        statelessMocsIndex,                       // statelessMocsIndex
        l1CachePolicyData.getL1CacheValue(false), // l1CachePolicy
        l1CachePolicyData.getL1CacheValue(true),  // l1CachePolicyDebuggerActive
        this->partitionCount > 1,                 // multiOsContextCapable
        isRcs,                                    // isRcs
        this->doubleSbaWa,                        // doubleSbaWa
        this->heaplessModeEnabled                 // heaplessModeEnabled
    };
    auto offsetSbaCmd = container.getCommandStream()->getUsed();
    NEO::EncodeStateBaseAddress<GfxFamily>::encode(encodeStateBaseAddressArgs);

    bool sbaTrackingEnabled = NEO::Debugger::isDebugEnabled(this->internalUsage) && this->device->getL0Debugger() && this->device->getL0Debugger()->isSbaTrackingEnabled();
    NEO::EncodeStateBaseAddress<GfxFamily>::setSbaTrackingForL0DebuggerIfEnabled(sbaTrackingEnabled,
                                                                                 *this->device->getNEODevice(),
                                                                                 *container.getCommandStream(),
                                                                                 sba, (isImmediateType() || this->dispatchCmdListBatchBufferAsPrimary));

    programStateBaseAddressHook(offsetSbaCmd, sba.getSurfaceStateBaseAddressModifyEnable());
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isSkippingInOrderBarrierAllowed(ze_event_handle_t hSignalEvent, uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents) const {
    uint32_t eventsToWait = numWaitEvents;

    for (uint32_t i = 0; i < numWaitEvents; i++) {
        if (CommandListCoreFamily<gfxCoreFamily>::canSkipInOrderEventWait(*Event::fromHandle(phWaitEvents[i]), false)) {
            eventsToWait--;
        }
    }

    if (eventsToWait > 0) {
        return false;
    }

    auto signalEvent = Event::fromHandle(hSignalEvent);

    if (signalEvent && signalEvent->isSignalWithUserInterrupt()) {
        return false;
    }

    if (signalEvent && this->latestOperationHasHeapfullCbEventWithProfiling) {
        return false;
    }

    if (signalEvent) {
        const bool dcFLushEvent = getDcFlushRequired(signalEvent->isSignalScope(ZE_EVENT_SCOPE_FLAG_HOST));
        return !(dcFLushEvent || signalEvent->isEventTimestampFlagSet() || !signalEvent->isCounterBased() || this->isPostSyncSkippedOnLatestInOrderOperation || Event::isAggregatedEvent(signalEvent));
    }

    return true;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::setupEventParamsForInOrderBarrierSkip(ze_event_handle_t hSignalEvent) {
    if (hSignalEvent) {
        auto event = Event::fromHandle(hSignalEvent);
        assignInOrderExecInfoToEvent(event);
        event->setHeapfullCbEventWithProfiling(this->latestOperationHasHeapfullCbEventWithProfiling);
        if (isImmediateType()) {
            event->setLatestUsedCmdQueue(this->cmdQImmediate);
        }
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendBarrier(ze_event_handle_t hSignalEvent, uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents, CmdListWaitEventParameters &waitEventsParameters) {
    if (isInOrderExecutionEnabled() && isSkippingInOrderBarrierAllowed(hSignalEvent, numWaitEvents, phWaitEvents)) {
        setupEventParamsForInOrderBarrierSkip(hSignalEvent);
        return ZE_RESULT_SUCCESS;
    }

    ze_result_t ret = addEventsToCmdList(numWaitEvents, phWaitEvents, waitEventsParameters);
    if (ret) {
        return ret;
    }

    appendSynchronizedDispatchInitializationSection();

    Event *signalEvent = nullptr;
    bool hostVisibleEvent = false;
    if (hSignalEvent) {
        signalEvent = Event::fromHandle(hSignalEvent);
        hostVisibleEvent = signalEvent->isSignalScope(ZE_EVENT_SCOPE_FLAG_HOST);
    }

    if (!handleCounterBasedEventOperations(signalEvent, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    appendEventForProfiling(signalEvent, nullptr, true, false, false, isCopyOnly(false));

    if (!this->isInOrderExecutionEnabled()) {
        if (isCopyOnly(false)) {
            NEO::MiFlushArgs args{this->dummyBlitWa};
            uint64_t gpuAddress = 0u;
            TaskCountType value = 0u;
            if (isImmediateType()) {
                args.commandWithPostSync = true;
                auto csr = getCsr(false);
                gpuAddress = csr->getBarrierCountGpuAddress();
                value = csr->getNextBarrierCount() + 1;
                commandContainer.addToResidencyContainer(csr->getTagAllocation());
            }

            encodeMiFlush(gpuAddress, value, args);
        } else {
            appendComputeBarrierCommand();
        }
    }

    addToMappedEventList(signalEvent);

    bool skipPipeControl = this->isInOrderExecutionEnabled() && !getDcFlushRequired(hostVisibleEvent);

    appendSignalEventPostWalker(signalEvent, nullptr, nullptr, skipPipeControl, false, isCopyOnly(false));

    if (isInOrderExecutionEnabled()) {
        appendSignalInOrderDependencyCounter(signalEvent, false, hostVisibleEvent, false, false);
    }
    handleInOrderDependencyCounter(signalEvent, false, false);

    appendSynchronizedDispatchCleanupSection();

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::setupFillKernelArguments(size_t baseOffset,
                                                                    size_t patternSize,
                                                                    size_t dstSize,
                                                                    CmdListFillKernelArguments &outArguments,
                                                                    Kernel *kernel) {
    const auto maxWgSize = this->device->getDeviceInfo().maxWorkGroupSize;
    if (canUseImmediateFill(dstSize, patternSize, baseOffset, maxWgSize)) {
        constexpr auto dataTypeSize = sizeof(uint32_t) * 4;

        size_t middleSize = dstSize;
        outArguments.mainOffset = baseOffset;
        outArguments.leftRemainingBytes = sizeof(uint32_t) - (baseOffset % sizeof(uint32_t));
        if (baseOffset % sizeof(uint32_t) != 0 && outArguments.leftRemainingBytes <= dstSize) {
            middleSize -= outArguments.leftRemainingBytes;
            outArguments.mainOffset += outArguments.leftRemainingBytes;
        } else {
            outArguments.leftRemainingBytes = 0;
        }

        size_t adjustedSize = middleSize / dataTypeSize;
        outArguments.mainGroupSize = maxWgSize;
        if (outArguments.mainGroupSize > adjustedSize && adjustedSize > 0) {
            outArguments.mainGroupSize = adjustedSize;
        }

        outArguments.groups = adjustedSize / outArguments.mainGroupSize;
        outArguments.rightRemainingBytes = static_cast<uint32_t>((adjustedSize % outArguments.mainGroupSize) * dataTypeSize +
                                                                 middleSize % dataTypeSize);

        if (outArguments.rightRemainingBytes > 0) {
            outArguments.rightOffset = outArguments.mainOffset + (middleSize - outArguments.rightRemainingBytes);
        }
    } else {
        size_t elSize = sizeof(uint32_t);
        if (baseOffset % elSize != 0) {
            outArguments.leftRemainingBytes = static_cast<uint32_t>(elSize - (baseOffset % elSize));
        }
        if (outArguments.leftRemainingBytes > 0) {
            elSize = sizeof(uint8_t);
        }
        size_t adjustedSize = dstSize / elSize;
        uint32_t groupSizeX = static_cast<uint32_t>(adjustedSize);
        uint32_t groupSizeY = 1, groupSizeZ = 1;
        kernel->suggestGroupSize(groupSizeX, groupSizeY, groupSizeZ, &groupSizeX, &groupSizeY, &groupSizeZ);
        outArguments.mainGroupSize = groupSizeX;

        outArguments.groups = static_cast<uint32_t>(adjustedSize) / outArguments.mainGroupSize;
        outArguments.rightRemainingBytes = static_cast<uint32_t>((adjustedSize % outArguments.mainGroupSize) * elSize +
                                                                 dstSize % elSize);

        size_t patternAllocationSize = alignUp(patternSize, MemoryConstants::cacheLineSize);
        outArguments.patternSizeInEls = static_cast<uint32_t>(patternAllocationSize / elSize);

        if (outArguments.rightRemainingBytes > 0) {
            outArguments.rightOffset = outArguments.groups * outArguments.mainGroupSize * elSize;
            outArguments.patternOffsetRemainder = (outArguments.mainGroupSize * outArguments.groups & (outArguments.patternSizeInEls - 1)) * elSize;
        }
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendWaitOnMemory(void *desc, void *ptr, uint64_t data, ze_event_handle_t signalEventHandle, bool useQwordData) {
    using COMPARE_OPERATION = typename GfxFamily::MI_SEMAPHORE_WAIT::COMPARE_OPERATION;

    auto descriptor = reinterpret_cast<zex_wait_on_mem_desc_t *>(desc);
    COMPARE_OPERATION comparator;
    switch (descriptor->actionFlag) {
    case ZEX_WAIT_ON_MEMORY_FLAG_EQUAL:
        comparator = COMPARE_OPERATION::COMPARE_OPERATION_SAD_EQUAL_SDD;
        break;
    case ZEX_WAIT_ON_MEMORY_FLAG_NOT_EQUAL:
        comparator = COMPARE_OPERATION::COMPARE_OPERATION_SAD_NOT_EQUAL_SDD;
        break;
    case ZEX_WAIT_ON_MEMORY_FLAG_GREATER_THAN:
        comparator = COMPARE_OPERATION::COMPARE_OPERATION_SAD_GREATER_THAN_SDD;
        break;
    case ZEX_WAIT_ON_MEMORY_FLAG_GREATER_THAN_EQUAL:
        comparator = COMPARE_OPERATION::COMPARE_OPERATION_SAD_GREATER_THAN_OR_EQUAL_SDD;
        break;
    case ZEX_WAIT_ON_MEMORY_FLAG_LESSER_THAN:
        comparator = COMPARE_OPERATION::COMPARE_OPERATION_SAD_LESS_THAN_SDD;
        break;
    case ZEX_WAIT_ON_MEMORY_FLAG_LESSER_THAN_EQUAL:
        comparator = COMPARE_OPERATION::COMPARE_OPERATION_SAD_LESS_THAN_OR_EQUAL_SDD;
        break;
    default:
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    Event *signalEvent = nullptr;
    if (signalEventHandle) {
        signalEvent = Event::fromHandle(signalEventHandle);
    }

    auto srcAllocationStruct = resolveAlignedAllocation(this->device, ptr, sizeof(uint32_t), nullptr, {.hostCopyAllowed = true});
    if (srcAllocationStruct.alloc == nullptr) {
        return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
    }
    UNRECOVERABLE_IF(srcAllocationStruct.alloc == nullptr);

    if (this->isInOrderExecutionEnabled()) {
        handleInOrderImplicitDependencies(false, false);
    }

    if (!handleCounterBasedEventOperations(signalEvent, false)) {
        return ZE_RESULT_ERROR_INVALID_ARGUMENT;
    }

    appendEventForProfiling(signalEvent, nullptr, true, false, false, isCopyOnly(false));

    commandContainer.addToResidencyContainer(srcAllocationStruct.alloc);
    uint64_t gpuAddress = static_cast<uint64_t>(srcAllocationStruct.alignedAllocationPtr);

    bool indirectMode = false;
    bool useSemaphore64bCmd = this->device->getDeviceInfo().semaphore64bCmdSupport;

    if (useQwordData) {
        if (isQwordInOrderCounter()) {
            if (!useSemaphore64bCmd) {
                indirectMode = true;

                NEO::LriHelper<GfxFamily>::program(commandContainer.getCommandStream(), RegisterOffsets::csGprR0, getLowPart(data), true, isCopyOnly(false));
                NEO::LriHelper<GfxFamily>::program(commandContainer.getCommandStream(), RegisterOffsets::csGprR0 + 4, getHighPart(data), true, isCopyOnly(false));
            }
        } else {
            return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
        }
    } else {
        UNRECOVERABLE_IF(getHighPart(data) != 0);
    }

    NEO::EncodeSemaphore<GfxFamily>::addMiSemaphoreWaitCommand(*commandContainer.getCommandStream(), gpuAddress, data, comparator, false, useQwordData, indirectMode, false, useSemaphore64bCmd, nullptr);

    const auto &rootDeviceEnvironment = this->device->getNEODevice()->getRootDeviceEnvironment();
    auto allocType = srcAllocationStruct.alloc->getAllocationType();
    bool isSystemMemoryUsed =
        (allocType == NEO::AllocationType::bufferHostMemory) ||
        (allocType == NEO::AllocationType::externalHostPtr);
    if (isSystemMemoryUsed) {
        NEO::MemorySynchronizationCommands<GfxFamily>::addAdditionalSynchronization(*commandContainer.getCommandStream(), gpuAddress, NEO::FenceType::acquire, rootDeviceEnvironment);
    }

    appendSignalEventPostWalker(signalEvent, nullptr, nullptr, false, false, isCopyOnly(false));

    if (this->isInOrderExecutionEnabled()) {
        appendSignalInOrderDependencyCounter(signalEvent, false, false, false, false);
    }
    handleInOrderDependencyCounter(signalEvent, false, false);

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendWriteToMemory(void *desc,
                                                                      void *ptr,
                                                                      uint64_t data) {
    return this->appendWriteToMemory(desc, ptr, data, nullptr);
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendWriteToMemory(void *desc,
                                                                      void *ptr,
                                                                      uint64_t data,
                                                                      bool *requireTaskCountUpdate) {
    auto descriptor = reinterpret_cast<zex_write_to_mem_desc_t *>(desc);

    size_t bufSize = sizeof(uint64_t);
    auto dstAllocationStruct = resolveAlignedAllocation(this->device, ptr, bufSize, nullptr, {});
    if (dstAllocationStruct.alloc == nullptr) {
        return ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY;
    }

    if (requireTaskCountUpdate) {
        *requireTaskCountUpdate = CommandList::isExternalHostPtrAlloc(dstAllocationStruct.alloc);
    }

    UNRECOVERABLE_IF(dstAllocationStruct.alloc == nullptr);
    commandContainer.addToResidencyContainer(dstAllocationStruct.alloc);

    if (this->isInOrderExecutionEnabled()) {
        handleInOrderImplicitDependencies(false, false);
    }

    const uint64_t gpuAddress = static_cast<uint64_t>(dstAllocationStruct.alignedAllocationPtr);

    if (isCopyOnly(false)) {
        NEO::MiFlushArgs args{this->dummyBlitWa};
        args.commandWithPostSync = true;
        encodeMiFlush(gpuAddress,
                      data, args);
    } else {
        NEO::PipeControlArgs args;
        args.dcFlushEnable = getDcFlushRequired(!!descriptor->writeScope);
        args.dcFlushEnable &= dstAllocationStruct.needsFlush;
        args.isWalkerWithProfilingEnqueued = this->getAndClearIsWalkerWithProfilingEnqueued();

        NEO::MemorySynchronizationCommands<GfxFamily>::addBarrierWithPostSyncOperation(
            *commandContainer.getCommandStream(),
            NEO::PostSyncMode::immediateData,
            gpuAddress,
            data,
            device->getNEODevice()->getRootDeviceEnvironment(),
            args);
    }

    if (this->isInOrderExecutionEnabled()) {
        appendSignalInOrderDependencyCounter(nullptr, false, false, false, false);
    }
    handleInOrderDependencyCounter(nullptr, false, false);

    return ZE_RESULT_SUCCESS;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendWaitExternalSemaphores(uint32_t numExternalSemaphores, const ze_external_semaphore_ext_handle_t *hSemaphores,
                                                                               const ze_external_semaphore_wait_params_ext_t *params, ze_event_handle_t hSignalEvent,
                                                                               uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents) {
    if (NEO::debugManager.flags.EnableHostFunctionBasedExternalSemaphores.get() == 1) {
        ExternalSemaphoreOperationData operationData{};
        operationData.semaphores.reserve(numExternalSemaphores);
        for (uint32_t i = 0; i < numExternalSemaphores; ++i) {
            auto semaphore = static_cast<ExternalSemaphoreImp *>(hSemaphores[i]);
            auto fenceValue = semaphore->neoExternalSemaphore->acquireWaitFenceValue(params[i].value);
            operationData.semaphores.push_back(std::pair(semaphore, fenceValue));
        }

        void *pHostFunctionData = nullptr;
        {
            std::lock_guard<std::mutex> lock(externalSemaphoreHostFunctionDataMutex);
            auto &hostFunctionData = externalSemaphoreHostFunctionData.emplace_back(*this, std::move(operationData));
            hostFunctionData.self = std::prev(externalSemaphoreHostFunctionData.end());
            pHostFunctionData = &hostFunctionData;
        }

        CmdListHostFunctionParameters parameters{};

        return this->appendHostFunction(semaphoreWaitHostFunction, pHostFunctionData, nullptr, hSignalEvent, numWaitEvents, phWaitEvents, parameters);
    }

    return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendSignalExternalSemaphores(uint32_t numExternalSemaphores, const ze_external_semaphore_ext_handle_t *hSemaphores,
                                                                                 const ze_external_semaphore_signal_params_ext_t *params, ze_event_handle_t hSignalEvent,
                                                                                 uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents) {
    if (NEO::debugManager.flags.EnableHostFunctionBasedExternalSemaphores.get() == 1) {
        ExternalSemaphoreOperationData operationData{};
        operationData.semaphores.reserve(numExternalSemaphores);
        for (uint32_t i = 0; i < numExternalSemaphores; ++i) {
            auto semaphore = static_cast<ExternalSemaphoreImp *>(hSemaphores[i]);
            auto fenceValue = semaphore->neoExternalSemaphore->acquireSignalFenceValue(params[i].value);
            operationData.semaphores.push_back(std::pair(semaphore, fenceValue));
        }

        void *pHostFunctionData = nullptr;
        {
            std::lock_guard<std::mutex> lock(externalSemaphoreHostFunctionDataMutex);
            auto &hostFunctionData = externalSemaphoreHostFunctionData.emplace_back(*this, std::move(operationData));
            hostFunctionData.self = std::prev(externalSemaphoreHostFunctionData.end());
            pHostFunctionData = &hostFunctionData;
        }

        CmdListHostFunctionParameters parameters{};

        return this->appendHostFunction(semaphoreSignalHostFunction, pHostFunctionData, nullptr, hSignalEvent, numWaitEvents, phWaitEvents, parameters);
    }

    return ZE_RESULT_ERROR_UNSUPPORTED_FEATURE;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::allocateOrReuseKernelPrivateMemoryIfNeeded(Kernel *kernel, uint32_t sizePerHwThread) {
    L0::KernelImp *kernelImp = static_cast<KernelImp *>(kernel);
    if (sizePerHwThread != 0U && kernelImp->getParentModule().shouldAllocatePrivateMemoryPerDispatch()) {
        allocateOrReuseKernelPrivateMemory(kernel, sizePerHwThread, ownedPrivateAllocations);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::allocateOrReuseKernelPrivateMemory(Kernel *kernel, uint32_t sizePerHwThread, NEO::PrivateAllocsToReuseContainer &privateAllocsToReuse) {
    L0::KernelImp *kernelImp = static_cast<KernelImp *>(kernel);
    NEO::GraphicsAllocation *privateAlloc = nullptr;

    bool allocToReuseFound = false;

    for (auto &alloc : privateAllocsToReuse) {
        if (sizePerHwThread == alloc.first) {
            privateAlloc = alloc.second;
            allocToReuseFound = true;
            break;
        }
    }
    if (!allocToReuseFound) {
        privateAlloc = kernelImp->allocatePrivateMemoryGraphicsAllocation();
        privateAllocsToReuse.push_back({sizePerHwThread, privateAlloc});
        this->commandContainer.addToResidencyContainer(privateAlloc);
    }
    kernel->patchCrossthreadDataWithPrivateAllocation(privateAlloc);
}

template <GFXCORE_FAMILY gfxCoreFamily>
CmdListEventOperation CommandListCoreFamily<gfxCoreFamily>::estimateEventPostSync(Event *event, uint32_t operations) {
    CmdListEventOperation ret;

    UNRECOVERABLE_IF(operations & (this->partitionCount - 1));

    ret.operationCount = operations / this->partitionCount;
    ret.operationOffset = event->getSinglePacketSize() * this->partitionCount;
    ret.workPartitionOperation = this->partitionCount > 1;
    ret.isTimestmapEvent = event->isEventTimestampFlagSet();
    ret.completionFieldOffset = event->getCompletionFieldOffset();

    return ret;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::dispatchPostSyncCopy(uint64_t gpuAddress, uint32_t value, bool workloadPartition, void **outCmdBuffer) {

    NEO::MiFlushArgs miFlushArgs{this->dummyBlitWa};
    miFlushArgs.commandWithPostSync = true;

    encodeMiFlush(gpuAddress, value, miFlushArgs);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::dispatchPostSyncCompute(uint64_t gpuAddress, uint32_t value, bool workloadPartition, void **outCmdBuffer) {
    NEO::EncodeStoreMemory<GfxFamily>::programStoreDataImm(
        *commandContainer.getCommandStream(),
        gpuAddress,
        value,
        0u,
        false,
        workloadPartition,
        outCmdBuffer);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::dispatchPostSyncCommands(const CmdListEventOperation &eventOperations, uint64_t gpuAddress, void **syncCmdBuffer, CommandToPatchContainer *outListCommands,
                                                                    uint32_t value, bool useLastPipeControl, bool signalScope, bool skipPartitionOffsetProgramming, bool copyOperation) {
    decltype(&CommandListCoreFamily<gfxCoreFamily>::dispatchPostSyncCompute) dispatchFunction = &CommandListCoreFamily<gfxCoreFamily>::dispatchPostSyncCompute;
    if (copyOperation) {
        dispatchFunction = &CommandListCoreFamily<gfxCoreFamily>::dispatchPostSyncCopy;
    }

    auto operationCount = eventOperations.operationCount;
    if (useLastPipeControl) {
        operationCount--;
    }

    if (eventOperations.isTimestmapEvent && !skipPartitionOffsetProgramming) {
        appendDispatchOffsetRegister(eventOperations.workPartitionOperation, true);
    }

    void **outCmdBuffer = nullptr;
    void *outCmd = nullptr;
    if (outListCommands != nullptr) {
        outCmdBuffer = &outCmd;
    }

    for (uint32_t i = 0; i < operationCount; i++) {
        (this->*dispatchFunction)(gpuAddress, value, eventOperations.workPartitionOperation, outCmdBuffer);
        if (outListCommands != nullptr) {
            outListCommands->emplace_back(PatchCbEventTimestampClearStoreDataImm{
                .pDestination = outCmd,
                .offset = i * eventOperations.operationOffset + eventOperations.completionFieldOffset,
            });
        }

        gpuAddress += eventOperations.operationOffset;
    }

    if (useLastPipeControl) {
        NEO::PipeControlArgs pipeControlArgs;
        pipeControlArgs.dcFlushEnable = getDcFlushRequired(signalScope);
        pipeControlArgs.workloadPartitionOffset = eventOperations.workPartitionOperation;
        pipeControlArgs.isWalkerWithProfilingEnqueued = this->getAndClearIsWalkerWithProfilingEnqueued();

        const auto &productHelper = this->device->getNEODevice()->getRootDeviceEnvironment().template getHelper<NEO::ProductHelper>();
        if (productHelper.isDirectSubmissionConstantCacheInvalidationNeeded(this->device->getHwInfo())) {
            if (isImmediateType()) {
                pipeControlArgs.constantCacheInvalidationEnable = getCsr(false)->isDirectSubmissionEnabled();
            } else {
                pipeControlArgs.constantCacheInvalidationEnable = this->device->getNEODevice()->isAnyDirectSubmissionEnabled();
            }
        }

        NEO::MemorySynchronizationCommands<GfxFamily>::addBarrierWithPostSyncOperation(
            *commandContainer.getCommandStream(),
            NEO::PostSyncMode::immediateData,
            gpuAddress,
            value,
            device->getNEODevice()->getRootDeviceEnvironment(),
            pipeControlArgs);

        if (syncCmdBuffer != nullptr) {
            *syncCmdBuffer = pipeControlArgs.postSyncCmd;
        }
    }

    if (eventOperations.isTimestmapEvent && !skipPartitionOffsetProgramming) {
        appendDispatchOffsetRegister(eventOperations.workPartitionOperation, false);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::dispatchEventPostSyncOperation(Event *event, void **syncCmdBuffer, CommandToPatchContainer *outListCommands, uint32_t value, bool omitFirstOperation, bool useMax,
                                                                          bool useLastPipeControl, bool skipPartitionOffsetProgramming, bool copyOeration) {
    if (!event->getAllocation(this->device)) {
        return;
    }

    uint32_t packets = event->getPacketsInUse();
    if (this->signalAllEventPackets || useMax) {
        packets = event->getMaxPacketsCount();
    }
    auto eventPostSync = estimateEventPostSync(event, packets);

    uint64_t gpuAddress = event->getCompletionFieldGpuAddress(this->device);
    if (omitFirstOperation) {
        gpuAddress += eventPostSync.operationOffset;
        eventPostSync.operationCount--;
    }

    dispatchPostSyncCommands(eventPostSync, gpuAddress, syncCmdBuffer, outListCommands, value, useLastPipeControl, event->isSignalScope(), skipPartitionOffsetProgramming, copyOeration);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::dispatchEventRemainingPacketsPostSyncOperation(Event *event, bool copyOperation) {
    if (!event->isCounterBasedExplicitlyEnabled() && event->getPacketsInUse() < event->getMaxPacketsCount()) {
        uint32_t packets = event->getMaxPacketsCount() - event->getPacketsInUse();
        CmdListEventOperation remainingPacketsOperation = estimateEventPostSync(event, packets);

        uint64_t eventAddress = event->getCompletionFieldGpuAddress(device);
        eventAddress += event->getSinglePacketSize() * event->getPacketsInUse();

        constexpr bool appendLastPipeControl = false;
        dispatchPostSyncCommands(remainingPacketsOperation, eventAddress, nullptr, nullptr, Event::STATE_SIGNALED, appendLastPipeControl, event->isSignalScope(), false, copyOperation);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
template <typename PatchSemaphoreType>
void CommandListCoreFamily<gfxCoreFamily>::appendWaitOnSingleEvent(Event *event, CommandToPatchContainer *outWaitCmds, bool relaxedOrderingAllowed, bool dualStreamCopyOffload) {
    using COMPARE_OPERATION = typename GfxFamily::MI_SEMAPHORE_WAIT::COMPARE_OPERATION;

    uint64_t gpuAddr = event->getCompletionFieldGpuAddress(this->device);
    uint32_t packetsToWait = event->getPacketsToWait();

    NEO::EncodeCaptureCommandData *outSemWaitCaptureDataPtr = nullptr;
    NEO::EncodeCaptureCommandData outSemWaitCaptureData = {};
    if (outWaitCmds != nullptr) {
        outSemWaitCaptureData.makeCommandView = outWaitCmds->makeCommandView;
        outSemWaitCaptureDataPtr = &outSemWaitCaptureData;
    }

    for (uint32_t i = 0u; i < packetsToWait; i++) {
        if (relaxedOrderingAllowed) {
            NEO::EncodeBatchBufferStartOrEnd<GfxFamily>::programConditionalDataMemBatchBufferStart(*commandContainer.getCommandStream(), 0, gpuAddr, Event::STATE_CLEARED,
                                                                                                   NEO::CompareOperation::equal, true, false, isCopyOnly(dualStreamCopyOffload));
        } else {
            bool useSemaphore64bCmd = this->device->getDeviceInfo().semaphore64bCmdSupport;
            NEO::EncodeSemaphore<GfxFamily>::addMiSemaphoreWaitCommand(*commandContainer.getCommandStream(),
                                                                       gpuAddr,
                                                                       Event::STATE_CLEARED,
                                                                       COMPARE_OPERATION::COMPARE_OPERATION_SAD_NOT_EQUAL_SDD, false, false, false, true, useSemaphore64bCmd, outSemWaitCaptureDataPtr);

            if (outWaitCmds != nullptr) {
                if constexpr (!std::is_same_v<PatchSemaphoreType, PatchInvalidPatchType>) {
                    auto offset = i * event->getSinglePacketSize() + event->getCompletionFieldOffset();
                    outWaitCmds->emplace_back(PatchSemaphoreType{
                        .gpuDestination = outSemWaitCaptureData.gpuAddress,
                        .pDestination = outSemWaitCaptureData.cpuBuffer,
                        .commandView = outSemWaitCaptureData.commandView,
                        .offset = offset,
                        .patchSize = outSemWaitCaptureData.cmdSize});
                } else {
                    UNRECOVERABLE_IF(true);
                }
            }
        }

        gpuAddr += event->getSinglePacketSize();
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::hasInOrderDependencies() const {
    const bool skip = (NEO::debugManager.flags.SkipImplicitInOrderDependencies.get() == 1);

    return (!skip && inOrderExecInfo.get() && (inOrderExecInfo->getCounterValue() > inOrderExecInfo->getInitialCounterValue() || this->isPostSyncSkippedOnLatestInOrderOperation));
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::handleCounterBasedEventOperations(Event *signalEvent, bool skipAddingEventToResidency) {
    if (!signalEvent) {
        return true;
    }

    bool implicitCounterBasedEventConversionEnable = !this->dcFlushSupport;
    if (NEO::debugManager.flags.EnableImplicitConvertionToCounterBasedEvents.get() != -1) {
        implicitCounterBasedEventConversionEnable = !!NEO::debugManager.flags.EnableImplicitConvertionToCounterBasedEvents.get();
    }

    if (implicitCounterBasedEventConversionEnable && !signalEvent->isCounterBasedExplicitlyEnabled()) {
        if (isInOrderExecutionEnabled() && isImmediateType()) {
            signalEvent->enableCounterBasedMode(false, ZE_EVENT_POOL_COUNTER_BASED_EXP_FLAG_IMMEDIATE);
        } else {
            signalEvent->disableImplicitCounterBasedMode();
        }
    }

    signalEvent->setHeapfullCbEventWithProfiling(false);

    if (signalEvent->isCounterBased()) {
        if (!isInOrderExecutionEnabled() || (signalEvent->isIpcImported() && !signalEvent->getInOrderExecEventHelper().is2WayIpcSharingEnabled())) {
            return false;
        }

        const auto counterBasedFlags = signalEvent->getCounterBasedFlags();
        const bool isImmediateCounterBasedEvent = (counterBasedFlags & ZE_EVENT_POOL_COUNTER_BASED_EXP_FLAG_IMMEDIATE);

        if (isImmediateType() && !isImmediateCounterBasedEvent) {
            return false;
        }

        if (!isImmediateType()) {
            const bool acceptsRecordedImmediateEvent = this->getIsGraphInstantiationTarget() && isImmediateCounterBasedEvent;
            const bool isNonImmediateCounterBasedEvent = (counterBasedFlags & ZE_EVENT_POOL_COUNTER_BASED_EXP_FLAG_NON_IMMEDIATE);

            if (!isNonImmediateCounterBasedEvent && !acceptsRecordedImmediateEvent) {
                return false;
            }
        }

        if (signalEvent->isEventTimestampFlagSet()) {
            auto *inOrderTimestampAllocator = device->getInOrderTimestampAllocator();
            auto tag = inOrderTimestampAllocator->getTag();
            auto *uploadCsr = isImmediateType() ? getCsr(false) : device->getNEODevice()->getDefaultEngine().commandStreamReceiver;
            uploadCsr->writeTagAllocationChunkToSimulation(*tag, 0, inOrderTimestampAllocator->getTagSize());

            if (skipAddingEventToResidency == false) {
                this->commandContainer.addToResidencyContainer(tag->getBaseGraphicsAllocation()->getGraphicsAllocation(device->getRootDeviceIndex()));
            }
            signalEvent->resetInOrderTimestampNode(tag, this->partitionCount);
            signalEvent->resetAdditionalTimestampNode(nullptr, this->partitionCount, false);
        }
    }

    const bool assignCsrOnSubmit = signalEvent->isSignalWithUserInterrupt() ||
                                   (signalEvent->isCounterBased() && signalEvent->isKmdWaitModeEnabled());
    if (!isImmediateType() && assignCsrOnSubmit) {
        this->interruptEvents.push_back(signalEvent);
    }

    return true;
}

template <GFXCORE_FAMILY gfxCoreFamily>
uint64_t CommandListCoreFamily<gfxCoreFamily>::getInOrderIncrementValue() const {
    return (this->inOrderAtomicSignalingEnabled ? this->getPartitionCount() : 1);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::encodeMiFlush(uint64_t immediateDataGpuAddress, uint64_t immediateData, NEO::MiFlushArgs &args) {
    args.waArgs.isWaRequired &= args.commandWithPostSync;
    auto isDummyBlitRequired = NEO::BlitCommandsHelper<GfxFamily>::isDummyBlitWaNeeded(args.waArgs);
    NEO::EncodeMiFlushDW<GfxFamily>::programWithWa(*commandContainer.getCommandStream(), immediateDataGpuAddress, immediateData, args);
    if (isDummyBlitRequired) {
        const auto &rootDeviceEnvironment = device->getNEODevice()->getRootDeviceEnvironment();
        auto dummyAllocation = rootDeviceEnvironment.getDummyAllocation();
        commandContainer.addToResidencyContainer(dummyAllocation);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
inline bool CommandListCoreFamily<gfxCoreFamily>::isCbEventBoundToCmdList(Event *event) const {
    return event->isCounterBased() && this->isInOrderExecutionEnabled() && (event->getInOrderExecEventHelper().getBaseDeviceAddress() == inOrderExecInfo->getBaseDeviceAddress());
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendSynchronizedDispatchInitializationSection() {
    auto syncAlloc = device->getSyncDispatchTokenAllocation();

    if (getSynchronizedDispatchMode() != NEO::SynchronizedDispatchMode::disabled) {
        commandContainer.addToResidencyContainer(syncAlloc);
    }

    if (getSynchronizedDispatchMode() == NEO::SynchronizedDispatchMode::limited) {
        bool useSemaphore64bCmd = this->device->getDeviceInfo().semaphore64bCmdSupport;
        NEO::EncodeSemaphore<GfxFamily>::addMiSemaphoreWaitCommand(*commandContainer.getCommandStream(), syncAlloc->getGpuAddress() + sizeof(uint32_t), 0u,
                                                                   GfxFamily::MI_SEMAPHORE_WAIT::COMPARE_OPERATION::COMPARE_OPERATION_SAD_EQUAL_SDD,
                                                                   false, false, false, true, useSemaphore64bCmd, nullptr);
    } else if (getSynchronizedDispatchMode() == NEO::SynchronizedDispatchMode::full) {
        appendFullSynchronizedDispatchInit();
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendFullSynchronizedDispatchInit() {
    using MI_ATOMIC = typename GfxFamily::MI_ATOMIC;
    using ATOMIC_OPCODES = typename MI_ATOMIC::ATOMIC_OPCODES;
    using DATA_SIZE = typename MI_ATOMIC::DATA_SIZE;

    constexpr size_t conditionalDataMemBbStartSize = NEO::EncodeBatchBufferStartOrEnd<GfxFamily>::getCmdSizeConditionalDataMemBatchBufferStart(false);

    const uint32_t queueId = this->syncDispatchQueueId + 1;
    const uint64_t queueIdToken = static_cast<uint64_t>(queueId) << 32;
    const uint64_t tokenInitialValue = queueIdToken + this->partitionCount;
    const bool useSemaphore64bCmd = this->device->getDeviceInfo().semaphore64bCmdSupport;

    auto syncAllocationGpuVa = device->getSyncDispatchTokenAllocation()->getGpuAddress();
    auto workPartitionAllocationGpuVa = device->getNEODevice()->getDefaultEngine().commandStreamReceiver->getWorkPartitionAllocation()->getGpuAddress();
    auto cmdStream = commandContainer.getCommandStream();

    // If Secondary Tile, then jump to Secondary Tile section
    // Reserve space for now. Will be patched later
    NEO::LinearStream skipPrimaryTileSectionCmdStream(cmdStream->getSpace(conditionalDataMemBbStartSize), conditionalDataMemBbStartSize);

    // If token acquired, jump to the end
    NEO::LinearStream jumpToEndSectionFromPrimaryTile;

    // Primary Tile section
    {
        // Try acquire token
        uint64_t acquireTokenCmdBufferVa = cmdStream->getCurrentGpuAddressPosition();
        NEO::EncodeMiPredicate<GfxFamily>::encode(*cmdStream, NEO::MiPredicateType::disable);
        NEO::EncodeAtomic<GfxFamily>::programMiAtomic(*cmdStream, syncAllocationGpuVa, ATOMIC_OPCODES::ATOMIC_8B_CMP_WR,
                                                      DATA_SIZE::DATA_SIZE_QWORD, 1, 1, 0, tokenInitialValue);

        // If token acquired, jump to the end
        // Reserve space for now. Will be patched later
        jumpToEndSectionFromPrimaryTile.replaceBuffer(cmdStream->getSpace(conditionalDataMemBbStartSize), conditionalDataMemBbStartSize);

        // Semaphore for potential switch
        NEO::EncodeSemaphore<GfxFamily>::addMiSemaphoreWaitCommand(*cmdStream, syncAllocationGpuVa + sizeof(uint32_t), 0u,
                                                                   GfxFamily::MI_SEMAPHORE_WAIT::COMPARE_OPERATION::COMPARE_OPERATION_SAD_EQUAL_SDD,
                                                                   false, false, false, true, useSemaphore64bCmd, nullptr);

        // Loop back to acquire again
        NEO::EncodeBatchBufferStartOrEnd<GfxFamily>::programBatchBufferStart(cmdStream, acquireTokenCmdBufferVa, false, false, false);
    }

    // Patch Primary Tile section skip (to Secondary Tile section)
    NEO::EncodeBatchBufferStartOrEnd<GfxFamily>::programConditionalDataMemBatchBufferStart(skipPrimaryTileSectionCmdStream, cmdStream->getCurrentGpuAddressPosition(), workPartitionAllocationGpuVa, 0,
                                                                                           NEO::CompareOperation::notEqual, false, false, isCopyOnly(false));

    // Secondary Tile section
    {
        NEO::EncodeMiPredicate<GfxFamily>::encode(*cmdStream, NEO::MiPredicateType::disable);

        // Wait for token acquisition by Primary Tile
        NEO::EncodeSemaphore<GfxFamily>::addMiSemaphoreWaitCommand(*cmdStream, syncAllocationGpuVa + sizeof(uint32_t), queueId,
                                                                   GfxFamily::MI_SEMAPHORE_WAIT::COMPARE_OPERATION::COMPARE_OPERATION_SAD_EQUAL_SDD,
                                                                   false, false, false, true, useSemaphore64bCmd, nullptr);
    }

    // Patch Primary Tile section jump to end
    NEO::EncodeBatchBufferStartOrEnd<GfxFamily>::programConditionalDataMemBatchBufferStart(jumpToEndSectionFromPrimaryTile, cmdStream->getCurrentGpuAddressPosition(), syncAllocationGpuVa + sizeof(uint32_t), queueId,
                                                                                           NEO::CompareOperation::equal, false, false, isCopyOnly(false));

    // End section
    NEO::EncodeMiPredicate<GfxFamily>::encode(*cmdStream, NEO::MiPredicateType::disable);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendSynchronizedDispatchCleanupSection() {
    if (getSynchronizedDispatchMode() != NEO::SynchronizedDispatchMode::full) {
        return;
    }

    using MI_ATOMIC = typename GfxFamily::MI_ATOMIC;
    using ATOMIC_OPCODES = typename MI_ATOMIC::ATOMIC_OPCODES;
    using DATA_SIZE = typename MI_ATOMIC::DATA_SIZE;

    auto cmdStream = commandContainer.getCommandStream();
    auto syncAllocationGpuVa = device->getSyncDispatchTokenAllocation()->getGpuAddress();

    const uint64_t queueIdToken = static_cast<uint64_t>(this->syncDispatchQueueId + 1) << 32;

    NEO::EncodeAtomic<GfxFamily>::programMiAtomic(*cmdStream, syncAllocationGpuVa, ATOMIC_OPCODES::ATOMIC_8B_DECREMENT, DATA_SIZE::DATA_SIZE_QWORD, 1, 1, 0, 0);

    if (!isInOrderExecutionEnabled()) {
        NEO::EncodeSemaphore<GfxFamily>::addMiSemaphoreWaitCommand(*cmdStream, syncAllocationGpuVa, 0u,
                                                                   GfxFamily::MI_SEMAPHORE_WAIT::COMPARE_OPERATION::COMPARE_OPERATION_SAD_EQUAL_SDD,
                                                                   false, false, false, true, this->device->getDeviceInfo().semaphore64bCmdSupport, nullptr);
    }

    NEO::EncodeAtomic<GfxFamily>::programMiAtomic(*cmdStream, syncAllocationGpuVa, ATOMIC_OPCODES::ATOMIC_8B_CMP_WR, DATA_SIZE::DATA_SIZE_QWORD, 1, 1, queueIdToken, 0);
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isDeviceToHostCopyEventFenceRequired(Event *signalEvent) const {
    return (signalEvent && signalEvent->isSignalScope(ZE_EVENT_SCOPE_FLAG_HOST)) || (isImmediateType() && isInOrderExecutionEnabled());
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isDeviceToHostBcsCopy(NEO::GraphicsAllocation *srcAllocation, NEO::GraphicsAllocation *dstAllocation, bool copyEngineOperation) const {
    bool srcInLocalPool = false;
    bool dstInLocalPool = false;
    if (srcAllocation) {
        srcInLocalPool = srcAllocation->isAllocatedInLocalMemoryPool();
    }
    if (dstAllocation) {
        dstInLocalPool = dstAllocation->isAllocatedInLocalMemoryPool();
    }
    return (copyEngineOperation && (srcInLocalPool && !dstInLocalPool));
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendCopyOperationFence(Event *signalEvent, NEO::GraphicsAllocation *srcAllocation, NEO::GraphicsAllocation *dstAllocation, bool copyEngineOperation) {
    if (this->copyOperationFenceSupported && isDeviceToHostBcsCopy(srcAllocation, dstAllocation, copyEngineOperation)) {
        if (isDeviceToHostCopyEventFenceRequired(signalEvent)) {
            NEO::MemorySynchronizationCommands<GfxFamily>::addAdditionalSynchronization(*commandContainer.getCommandStream(), 0, NEO::FenceType::release, device->getNEODevice()->getRootDeviceEnvironment());
            taskCountUpdateFenceRequired = false;
        } else {
            taskCountUpdateFenceRequired = true;
        }
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
ze_result_t CommandListCoreFamily<gfxCoreFamily>::appendCommandLists(uint32_t numCommandLists, ze_command_list_handle_t *phCommandLists,
                                                                     ze_event_handle_t hSignalEvent, uint32_t numWaitEvents, ze_event_handle_t *phWaitEvents,
                                                                     CommandListExecutionInternalOptions &internalOptions) {

    return ZE_RESULT_ERROR_INVALID_ARGUMENT;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::appendEventForProfilingAllWalkers(Event *event, void **syncCmdBuffer, CommandToPatchContainer *outTimeStampSyncCmds, bool beforeWalker, bool singlePacketEvent, bool skipAddingEventToResidency, bool copyOperation) {
    if (copyOperation || singleEventPacketRequired(singlePacketEvent)) {
        if (beforeWalker) {
            appendEventForProfiling(event, outTimeStampSyncCmds, true, false, skipAddingEventToResidency, copyOperation);
        } else {
            appendSignalEventPostWalker(event, syncCmdBuffer, outTimeStampSyncCmds, false, skipAddingEventToResidency, copyOperation);
        }
    } else {
        if (event) {
            if (beforeWalker) {
                event->resetKernelCountAndPacketUsedCount();
                event->zeroKernelCount();
            } else {
                if (event->getKernelCount() > 1) {
                    if (getDcFlushRequired(event->isSignalScope())) {
                        programEventL3Flush(event);
                    }
                    dispatchEventRemainingPacketsPostSyncOperation(event, copyOperation);
                    if (event->isSignalWithUserInterrupt()) {
                        NEO::EncodeUserInterrupt<GfxFamily>::encode(*commandContainer.getCommandStream());
                    }
                }
            }
        }
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::programEventL3Flush(Event *event) {
    auto eventPartitionOffset = (partitionCount > 1) ? (partitionCount * event->getSinglePacketSize())
                                                     : event->getSinglePacketSize();
    uint64_t eventAddress = event->getPacketAddress(device) + eventPartitionOffset;
    if (event->isEventTimestampFlagSet()) {
        eventAddress += event->getContextEndOffset();
    }

    if (partitionCount > 1) {
        event->setPacketsInUse(event->getPacketsUsedInLastKernel() + partitionCount);
    } else {
        event->setPacketsInUse(event->getPacketsUsedInLastKernel() + 1);
    }

    event->setL3FlushForCurrentKernel();

    auto &cmdListStream = *commandContainer.getCommandStream();
    NEO::PipeControlArgs args;
    args.dcFlushEnable = true;
    args.workloadPartitionOffset = partitionCount > 1;
    args.isWalkerWithProfilingEnqueued = this->getAndClearIsWalkerWithProfilingEnqueued();

    NEO::MemorySynchronizationCommands<GfxFamily>::addBarrierWithPostSyncOperation(
        cmdListStream,
        NEO::PostSyncMode::immediateData,
        eventAddress,
        Event::STATE_SIGNALED,
        device->getNEODevice()->getRootDeviceEnvironment(),
        args);
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::setAdditionalKernelLaunchParams(CmdListKernelLaunchParams &launchParams, Kernel &kernel) const {
    auto &kernelDescriptor = kernel.getImmutableData()->getDescriptor();

    if (launchParams.requiredDispatchWalkOrder == NEO::RequiredDispatchWalkOrder::none) {
        launchParams.requiredDispatchWalkOrder = kernelDescriptor.kernelAttributes.dispatchWalkOrder;
    }
    if (launchParams.requiredPartitionDim == NEO::RequiredPartitionDim::none) {
        launchParams.requiredPartitionDim = kernelDescriptor.kernelAttributes.partitionDim;
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::dispatchInOrderPostOperationBarrier(Event *signalEvent, bool dcFlushRequired, bool copyOperation) {
    if ((!signalEvent || !signalEvent->getAllocation(this->device)) && !copyOperation) {
        NEO::PipeControlArgs args;
        args.dcFlushEnable = dcFlushRequired;

        NEO::MemorySynchronizationCommands<GfxFamily>::setPostSyncExtraProperties(args);
        NEO::MemorySynchronizationCommands<GfxFamily>::addSingleBarrier(*commandContainer.getCommandStream(), args);
    }
}

template <GFXCORE_FAMILY gfxCoreFamily>
bool CommandListCoreFamily<gfxCoreFamily>::isKernelUncachedMocsRequired(bool kernelState) {
    this->containsStatelessUncachedResource |= kernelState;
    auto &productHelper = this->device->getNEODevice()->getProductHelper();
    auto &rootDeviceEnvironment = this->device->getNEODevice()->getRootDeviceEnvironment();

    if (this->stateBaseAddressTracking || productHelper.deferMOCSToPatIndex(rootDeviceEnvironment.isWddmOnLinux())) {
        return false;
    }
    return this->containsStatelessUncachedResource;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::setupFlagsForBcsSplit(CmdListMemoryCopyParams &memoryCopyParams, bool &hasStallingCmds, bool &copyOffloadFlush, const void *srcPtr, void *dstPtr, size_t srcSize, size_t dstSize) {
    memoryCopyParams.relaxedOrderingDispatch = isRelaxedOrderingDispatchAllowed(1, false); // split generates more than 1 event
    memoryCopyParams.forceDisableCopyOnlyInOrderSignaling = true;
    memoryCopyParams.taskCountUpdateRequired = true;
    memoryCopyParams.copyOffloadAllowed = this->isCopyOffloadEnabled();
    copyOffloadFlush = memoryCopyParams.copyOffloadAllowed;
    hasStallingCmds = !memoryCopyParams.relaxedOrderingDispatch;

    memoryCopyParams.bscSplitEnabled = true;
    memoryCopyParams.bcsSplitBaseDstPtr = dstPtr;
    memoryCopyParams.bcsSplitBaseSrcPtr = srcPtr;
    memoryCopyParams.bcsSplitTotalDstSize = dstSize;
    memoryCopyParams.bcsSplitTotalSrcSize = srcSize;
}

template <GFXCORE_FAMILY gfxCoreFamily>
void CommandListCoreFamily<gfxCoreFamily>::patchKernelProperties(CmdListKernelLaunchParams &launchParams, Kernel &kernel, const ze_group_count_t &threadGroupDimensions) {
    if (!launchParams.isKernelPatched) {
        kernel.patchGlobalOffset();
        kernel.patchRegionParams(launchParams, threadGroupDimensions);
        this->allocateOrReuseKernelPrivateMemoryIfNeeded(&kernel, kernel.getKernelDescriptor().kernelAttributes.perHwThreadPrivateMemorySize);

        if (launchParams.isIndirect) {
            prepareIndirectParams(&threadGroupDimensions);
        } else {
            kernel.setGroupCount(threadGroupDimensions.groupCountX,
                                 threadGroupDimensions.groupCountY,
                                 threadGroupDimensions.groupCountZ);
        }
        launchParams.isKernelPatched = true;
    }
}

} // namespace L0
