/* * Copyright (c) 2018, Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "hw_cmds.h" #include "runtime/command_queue/command_queue_hw.h" #include "runtime/command_stream/command_stream_receiver.h" #include "runtime/memory_manager/memory_manager.h" #include "runtime/helpers/basic_math.h" #include "runtime/helpers/kernel_commands.h" #include "runtime/helpers/options.h" #include "runtime/os_interface/device_factory.h" #include "unit_tests/command_queue/command_queue_fixture.h" #include "unit_tests/command_stream/command_stream_fixture.h" #include "unit_tests/fixtures/context_fixture.h" #include "unit_tests/fixtures/device_fixture.h" #include "unit_tests/fixtures/memory_management_fixture.h" #include "unit_tests/fixtures/buffer_fixture.h" #include "unit_tests/helpers/debug_manager_state_restore.h" #include "unit_tests/libult/ult_command_stream_receiver.h" #include "unit_tests/mocks/mock_memory_manager.h" #include "unit_tests/mocks/mock_command_queue.h" #include "unit_tests/mocks/mock_context.h" #include "unit_tests/mocks/mock_csr.h" #include "gtest/gtest.h" #include "gmock/gmock.h" #include "test.h" using namespace OCLRT; struct CommandQueueMemoryDevice : public MemoryManagementFixture, public DeviceFixture { void SetUp() override { MemoryManagementFixture::SetUp(); DeviceFixture::SetUp(); } void TearDown() override { DeviceFixture::TearDown(); MemoryManagementFixture::TearDown(); } }; struct CommandQueueTest : public CommandQueueMemoryDevice, public ContextFixture, public CommandQueueFixture, ::testing::TestWithParam { using CommandQueueFixture::SetUp; using ContextFixture::SetUp; CommandQueueTest() { } void SetUp() override { CommandQueueMemoryDevice::SetUp(); properties = GetParam(); cl_device_id device = pDevice; ContextFixture::SetUp(1, &device); CommandQueueFixture::SetUp(pContext, pDevice, properties); } void TearDown() override { CommandQueueFixture::TearDown(); ContextFixture::TearDown(); CommandQueueMemoryDevice::TearDown(); } cl_command_queue_properties properties; const HardwareInfo *pHwInfo = nullptr; }; TEST_P(CommandQueueTest, createDeleteCommandQueue_Properties) { InjectedFunction method = [this](size_t failureIndex) { auto retVal = CL_INVALID_VALUE; auto pCmdQ = CommandQueue::create( pContext, pDevice, nullptr, retVal); if (nonfailingAllocation == failureIndex) { EXPECT_EQ(CL_SUCCESS, retVal); EXPECT_NE(nullptr, pCmdQ); } else { EXPECT_EQ(CL_OUT_OF_HOST_MEMORY, retVal) << "for allocation " << failureIndex; EXPECT_EQ(nullptr, pCmdQ); } delete pCmdQ; }; injectFailures(method); } INSTANTIATE_TEST_CASE_P(CommandQueue, CommandQueueTest, ::testing::ValuesIn(AllCommandQueueProperties)); TEST(CommandQueue, taskLevelInitializesTo0) { CommandQueue cmdQ(nullptr, nullptr, 0); EXPECT_EQ(0u, cmdQ.taskLevel); EXPECT_EQ(0u, cmdQ.taskCount); } struct GetTagTest : public DeviceFixture, public CommandQueueFixture, public CommandStreamFixture, public ::testing::Test { using CommandQueueFixture::SetUp; void SetUp() override { DeviceFixture::SetUp(); CommandQueueFixture::SetUp(nullptr, pDevice, 0); CommandStreamFixture::SetUp(pCmdQ); } void TearDown() override { CommandStreamFixture::TearDown(); CommandQueueFixture::TearDown(); DeviceFixture::TearDown(); } }; TEST_F(GetTagTest, shouldReturnValue) { uint32_t tagValue = 0xdeadbeef; *pTagMemory = tagValue; EXPECT_EQ(tagValue, pCmdQ->getHwTag()); } TEST_F(GetTagTest, getHwTagInitialValue) { MockContext context; CommandQueue commandQueue(&context, pDevice, 0); EXPECT_EQ(initialHardwareTag, commandQueue.getHwTag()); } TEST(CommandQueue, IOQ_taskLevelFromCompletionStamp) { MockContext context; CommandQueue cmdQ(&context, nullptr, 0); CompletionStamp cs = { cmdQ.taskCount + 100, cmdQ.taskLevel + 50, 5, 0, EngineType::ENGINE_RCS}; cmdQ.updateFromCompletionStamp(cs); EXPECT_EQ(cs.taskLevel, cmdQ.taskLevel); EXPECT_EQ(cs.taskCount, cmdQ.taskCount); EXPECT_EQ(cs.flushStamp, cmdQ.flushStamp->peekStamp()); } TEST(CommandQueue, givenTimeStampWithTaskCountNotReadyStatusWhenupdateFromCompletionStampIsBeingCalledThenQueueTaskCountIsNotUpdated) { MockContext context; CommandQueue cmdQ(&context, nullptr, 0); cmdQ.taskCount = 1u; CompletionStamp cs = { Event::eventNotReady, 0, 0, 0, EngineType::ENGINE_RCS}; cmdQ.updateFromCompletionStamp(cs); EXPECT_EQ(1u, cmdQ.taskCount); } TEST(CommandQueue, GivenOOQwhenUpdateFromCompletionStampWithTrueIsCalledThenTaskLevelIsUpdated) { MockContext context; const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, 0}; CommandQueue cmdQ(&context, nullptr, props); auto oldTL = cmdQ.taskLevel; CompletionStamp cs = { cmdQ.taskCount + 100, cmdQ.taskLevel + 50, 5, 0, EngineType::ENGINE_RCS}; cmdQ.updateFromCompletionStamp(cs); EXPECT_NE(oldTL, cmdQ.taskLevel); EXPECT_EQ(oldTL + 50, cmdQ.taskLevel); EXPECT_EQ(cs.taskCount, cmdQ.taskCount); EXPECT_EQ(cs.flushStamp, cmdQ.flushStamp->peekStamp()); } TEST(CommandQueue, givenCmdQueueBlockedByReadyVirtualEventWhenUnblockingThenUpdateFlushTaskFromEvent) { auto context = new MockContext; auto cmdQ = new CommandQueue(context, nullptr, 0); auto userEvent = new Event(cmdQ, CL_COMMAND_NDRANGE_KERNEL, 0, 0); userEvent->setStatus(CL_COMPLETE); userEvent->flushStamp->setStamp(5); userEvent->incRefInternal(); FlushStamp expectedFlushStamp = 0; EXPECT_EQ(expectedFlushStamp, cmdQ->flushStamp->peekStamp()); cmdQ->virtualEvent = userEvent; EXPECT_FALSE(cmdQ->isQueueBlocked()); EXPECT_EQ(userEvent->flushStamp->peekStamp(), cmdQ->flushStamp->peekStamp()); userEvent->decRefInternal(); cmdQ->decRefInternal(); context->decRefInternal(); } TEST(CommandQueue, givenCmdQueueBlockedByAbortedVirtualEventWhenUnblockingThenUpdateFlushTaskFromEvent) { auto context = new MockContext; std::unique_ptr mockDevice(Device::create(nullptr)); auto cmdQ = new CommandQueue(context, mockDevice.get(), 0); auto userEvent = new Event(cmdQ, CL_COMMAND_NDRANGE_KERNEL, 0, 0); userEvent->setStatus(-1); userEvent->flushStamp->setStamp(5); FlushStamp expectedFlushStamp = 0; EXPECT_EQ(expectedFlushStamp, cmdQ->flushStamp->peekStamp()); userEvent->incRefInternal(); cmdQ->virtualEvent = userEvent; EXPECT_FALSE(cmdQ->isQueueBlocked()); EXPECT_EQ(expectedFlushStamp, cmdQ->flushStamp->peekStamp()); userEvent->decRefInternal(); cmdQ->decRefInternal(); context->decRefInternal(); } struct CommandQueueCommandStreamTest : public CommandQueueMemoryDevice, public ::testing::Test { void SetUp() override { CommandQueueMemoryDevice::SetUp(); } void TearDown() override { CommandQueueMemoryDevice::TearDown(); } MockContext context; }; HWTEST_F(CommandQueueCommandStreamTest, givenCommandQueueThatWaitsOnAbortedUserEventWhenIsQueueBlockedIsCalledThenTaskLevelAlignsToCsr) { MockContext context; std::unique_ptr mockDevice(Device::create(nullptr)); CommandQueue cmdQ(&context, mockDevice.get(), 0); auto &commandStreamReceiver = mockDevice->getUltCommandStreamReceiver(); commandStreamReceiver.taskLevel = 100u; Event userEvent(&cmdQ, CL_COMMAND_NDRANGE_KERNEL, 0, 0); userEvent.setStatus(-1); userEvent.incRefInternal(); cmdQ.virtualEvent = &userEvent; EXPECT_FALSE(cmdQ.isQueueBlocked()); EXPECT_EQ(100u, cmdQ.taskLevel); } TEST_F(CommandQueueCommandStreamTest, GetCommandStreamReturnsValidObject) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue commandQueue(&context, pDevice, props); auto &cs = commandQueue.getCS(); EXPECT_NE(nullptr, &cs); } TEST_F(CommandQueueCommandStreamTest, GetCommandStreamReturnsCsWithCsOverfetchSizeIncludedInGraphicsAllocation) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue commandQueue(&context, pDevice, props); size_t minSizeRequested = 20; auto &cs = commandQueue.getCS(minSizeRequested); ASSERT_NE(nullptr, &cs); auto *allocation = cs.getGraphicsAllocation(); ASSERT_NE(nullptr, &allocation); size_t expectedCsSize = alignUp(minSizeRequested + CSRequirements::minCommandQueueCommandStreamSize, MemoryConstants::pageSize) - CSRequirements::minCommandQueueCommandStreamSize; EXPECT_EQ(expectedCsSize, cs.getMaxAvailableSpace()); size_t expectedTotalSize = alignUp(minSizeRequested + CSRequirements::minCommandQueueCommandStreamSize, MemoryConstants::pageSize) + CSRequirements::csOverfetchSize; EXPECT_EQ(expectedTotalSize, allocation->getUnderlyingBufferSize()); } TEST_F(CommandQueueCommandStreamTest, getCommandStreamContainsMemoryForRequest) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue commandQueue(&context, pDevice, props); size_t requiredSize = 16384; const auto &commandStream = commandQueue.getCS(requiredSize); ASSERT_NE(nullptr, &commandStream); EXPECT_GE(commandStream.getMaxAvailableSpace(), requiredSize); } TEST_F(CommandQueueCommandStreamTest, getCommandStreamCanRecycle) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue commandQueue(&context, pDevice, props); auto &commandStreamInitial = commandQueue.getCS(); size_t requiredSize = commandStreamInitial.getMaxAvailableSpace() + 42; const auto &commandStream = commandQueue.getCS(requiredSize); ASSERT_NE(nullptr, &commandStream); EXPECT_GE(commandStream.getMaxAvailableSpace(), requiredSize); } TEST_F(CommandQueueCommandStreamTest, MemoryManagerWithReusableAllocationsWhenAskedForCommandStreamReturnsAllocationFromReusablePool) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue cmdQ(&context, pDevice, props); auto memoryManager = pDevice->getMemoryManager(); size_t requiredSize = alignUp(100, MemoryConstants::pageSize) + CSRequirements::csOverfetchSize; auto allocation = memoryManager->allocateGraphicsMemory(requiredSize, 4096); memoryManager->storeAllocation(std::unique_ptr(allocation), REUSABLE_ALLOCATION); EXPECT_FALSE(memoryManager->allocationsForReuse.peekIsEmpty()); EXPECT_TRUE(memoryManager->allocationsForReuse.peekContains(*allocation)); const auto &indirectHeap = cmdQ.getCS(100); EXPECT_EQ(indirectHeap.getGraphicsAllocation(), allocation); EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); } TEST_F(CommandQueueCommandStreamTest, givenCommandQueueWhenItIsDestroyedThenCommandStreamIsPutOnTheReusabeList) { auto cmdQ = new CommandQueue(&context, pDevice, 0); auto memoryManager = pDevice->getMemoryManager(); const auto &commandStream = cmdQ->getCS(100); auto graphicsAllocation = commandStream.getGraphicsAllocation(); EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); //now destroy command queue, heap should go to reusable list delete cmdQ; EXPECT_FALSE(memoryManager->allocationsForReuse.peekIsEmpty()); EXPECT_TRUE(memoryManager->allocationsForReuse.peekContains(*graphicsAllocation)); } TEST_F(CommandQueueCommandStreamTest, CommandQueueWhenAskedForNewCommandStreamStoresOldHeapForReuse) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue cmdQ(&context, pDevice, props); auto memoryManager = pDevice->getMemoryManager(); EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); const auto &indirectHeap = cmdQ.getCS(100); EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); auto graphicsAllocation = indirectHeap.getGraphicsAllocation(); cmdQ.getCS(10000); EXPECT_FALSE(memoryManager->allocationsForReuse.peekIsEmpty()); EXPECT_TRUE(memoryManager->allocationsForReuse.peekContains(*graphicsAllocation)); } struct CommandQueueIndirectHeapTest : public CommandQueueMemoryDevice, public ::testing::TestWithParam { void SetUp() override { CommandQueueMemoryDevice::SetUp(); } void TearDown() override { CommandQueueMemoryDevice::TearDown(); } MockContext context; }; TEST_P(CommandQueueIndirectHeapTest, IndirectHeapIsProvidedByDevice) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue cmdQ(&context, pDevice, props); auto &indirectHeap = cmdQ.getIndirectHeap(this->GetParam(), 8192); EXPECT_NE(nullptr, &indirectHeap); } TEST_P(CommandQueueIndirectHeapTest, IndirectHeapContainsAtLeast64KB) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue cmdQ(&context, pDevice, props); auto &indirectHeap = cmdQ.getIndirectHeap(this->GetParam(), sizeof(uint32_t)); if (this->GetParam() == IndirectHeap::SURFACE_STATE) { EXPECT_EQ(64 * KB - MemoryConstants::pageSize, indirectHeap.getAvailableSpace()); } else if (this->GetParam() == IndirectHeap::INSTRUCTION) { EXPECT_EQ(optimalInstructionHeapSize, indirectHeap.getAvailableSpace()); } else { EXPECT_EQ(64 * KB, indirectHeap.getAvailableSpace()); } } TEST_P(CommandQueueIndirectHeapTest, getIndirectHeapContainsMemoryForRequest) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue cmdQ(&context, pDevice, props); size_t requiredSize = 16384; const auto &indirectHeap = cmdQ.getIndirectHeap(this->GetParam(), requiredSize); ASSERT_NE(nullptr, &indirectHeap); EXPECT_GE(indirectHeap.getMaxAvailableSpace(), requiredSize); } TEST_P(CommandQueueIndirectHeapTest, getIndirectHeapCanRecycle) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue cmdQ(&context, pDevice, props); auto &indirectHeapInitial = cmdQ.getIndirectHeap(this->GetParam(), 10); size_t requiredSize = indirectHeapInitial.getMaxAvailableSpace() + 42; const auto &indirectHeap = cmdQ.getIndirectHeap(this->GetParam(), requiredSize); ASSERT_NE(nullptr, &indirectHeap); if (this->GetParam() == IndirectHeap::SURFACE_STATE) { //no matter what SSH is always capped EXPECT_EQ(indirectHeap.getMaxAvailableSpace(), maxSshSize); } else { EXPECT_GE(indirectHeap.getMaxAvailableSpace(), requiredSize); } } TEST_P(CommandQueueIndirectHeapTest, alignSizeToCacheLine) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue cmdQ(&context, pDevice, props); size_t minHeapSize = 64 * KB; auto &indirectHeapInitial = cmdQ.getIndirectHeap(this->GetParam(), 2 * minHeapSize + 1); EXPECT_TRUE(isAligned(indirectHeapInitial.getAvailableSpace())); indirectHeapInitial.getSpace(indirectHeapInitial.getAvailableSpace()); // use whole space to force obtain reusable const auto &indirectHeap = cmdQ.getIndirectHeap(this->GetParam(), minHeapSize + 1); ASSERT_NE(nullptr, &indirectHeap); EXPECT_TRUE(isAligned(indirectHeap.getAvailableSpace())); } TEST_P(CommandQueueIndirectHeapTest, MemoryManagerWithReusableAllocationsWhenAskedForHeapAllocationReturnsAllocationFromReusablePool) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue cmdQ(&context, pDevice, props); auto memoryManager = pDevice->getMemoryManager(); auto allocationSize = defaultHeapSize * 2; if (this->GetParam() == IndirectHeap::INSTRUCTION) { allocationSize = optimalInstructionHeapSize * 2; } auto allocation = memoryManager->allocateGraphicsMemory(allocationSize); memoryManager->storeAllocation(std::unique_ptr(allocation), REUSABLE_ALLOCATION); EXPECT_FALSE(memoryManager->allocationsForReuse.peekIsEmpty()); EXPECT_TRUE(memoryManager->allocationsForReuse.peekContains(*allocation)); const auto &indirectHeap = cmdQ.getIndirectHeap(this->GetParam(), 100); EXPECT_EQ(indirectHeap.getGraphicsAllocation(), allocation); //if we obtain heap from reusable pool, we need to keep the size of allocation //surface state heap is an exception, it is capped at ~60KB if (this->GetParam() == IndirectHeap::SURFACE_STATE) { EXPECT_EQ(indirectHeap.getMaxAvailableSpace(), 64 * KB - MemoryConstants::pageSize); } else { EXPECT_EQ(indirectHeap.getMaxAvailableSpace(), allocationSize); } EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); } TEST_P(CommandQueueIndirectHeapTest, CommandQueueWhenAskedForNewHeapStoresOldHeapForReuse) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; CommandQueue cmdQ(&context, pDevice, props); auto memoryManager = pDevice->getMemoryManager(); EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); const auto &indirectHeap = cmdQ.getIndirectHeap(this->GetParam(), 100); auto heapSize = indirectHeap.getAvailableSpace(); auto graphicsAllocation = indirectHeap.getGraphicsAllocation(); // Request a larger heap than the first. cmdQ.getIndirectHeap(this->GetParam(), heapSize + 6000); EXPECT_FALSE(memoryManager->allocationsForReuse.peekIsEmpty()); EXPECT_TRUE(memoryManager->allocationsForReuse.peekContains(*graphicsAllocation)); } TEST_P(CommandQueueIndirectHeapTest, GivenCommandQueueWithoutHeapAllocationWhenAskedForNewHeapReturnsAcquiresNewAllocationWithoutStoring) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; MockCommandQueue cmdQ(&context, pDevice, props); auto memoryManager = pDevice->getMemoryManager(); EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); const auto &indirectHeap = cmdQ.getIndirectHeap(this->GetParam(), 100); auto heapSize = indirectHeap.getAvailableSpace(); auto graphicsAllocation = indirectHeap.getGraphicsAllocation(); cmdQ.indirectHeap[this->GetParam()]->replaceGraphicsAllocation(nullptr); cmdQ.indirectHeap[this->GetParam()]->replaceBuffer(nullptr, 0); // Request a larger heap than the first. cmdQ.getIndirectHeap(this->GetParam(), heapSize + 6000); EXPECT_NE(graphicsAllocation, indirectHeap.getGraphicsAllocation()); memoryManager->freeGraphicsMemory(graphicsAllocation); } TEST_P(CommandQueueIndirectHeapTest, givenCommandQueueWithResourceCachingActiveWhenQueueISDestroyedThenIndirectHeapIsPutOnReuseList) { auto cmdQ = new CommandQueue(&context, pDevice, 0); auto memoryManager = pDevice->getMemoryManager(); const auto &indirectHeap = cmdQ->getIndirectHeap(this->GetParam(), 100); auto graphicsAllocation = indirectHeap.getGraphicsAllocation(); EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); //now destroy command queue, heap should go to reusable list delete cmdQ; EXPECT_FALSE(memoryManager->allocationsForReuse.peekIsEmpty()); EXPECT_TRUE(memoryManager->allocationsForReuse.peekContains(*graphicsAllocation)); } TEST_P(CommandQueueIndirectHeapTest, GivenCommandQueueWithHeapAllocatedWhenIndirectHeapIsReleasedThenHeapAllocationAndHeapBufferIsSetToNullptr) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; MockCommandQueue cmdQ(&context, pDevice, props); auto memoryManager = pDevice->getMemoryManager(); EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); const auto &indirectHeap = cmdQ.getIndirectHeap(this->GetParam(), 100); auto heapSize = indirectHeap.getMaxAvailableSpace(); EXPECT_NE(0u, heapSize); auto graphicsAllocation = indirectHeap.getGraphicsAllocation(); EXPECT_NE(nullptr, graphicsAllocation); cmdQ.releaseIndirectHeap(this->GetParam()); EXPECT_EQ(nullptr, cmdQ.indirectHeap[this->GetParam()]->getGraphicsAllocation()); EXPECT_EQ(nullptr, indirectHeap.getBase()); EXPECT_EQ(0u, indirectHeap.getMaxAvailableSpace()); } TEST_P(CommandQueueIndirectHeapTest, GivenCommandQueueWithoutHeapAllocatedWhenIndirectHeapIsReleasedThenIndirectHeapAllocationStaysNull) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; MockCommandQueue cmdQ(&context, pDevice, props); cmdQ.releaseIndirectHeap(this->GetParam()); EXPECT_EQ(nullptr, cmdQ.indirectHeap[this->GetParam()]); } TEST_P(CommandQueueIndirectHeapTest, GivenCommandQueueWithHeapWhenGraphicAllocationIsNullThenNothingOnReuseList) { const cl_queue_properties props[3] = {CL_QUEUE_PROPERTIES, 0, 0}; MockCommandQueue cmdQ(&context, pDevice, props); auto &ih = cmdQ.getIndirectHeap(this->GetParam()); auto allocation = ih.getGraphicsAllocation(); EXPECT_NE(nullptr, allocation); cmdQ.indirectHeap[this->GetParam()]->replaceGraphicsAllocation(nullptr); cmdQ.indirectHeap[this->GetParam()]->replaceBuffer(nullptr, 0); cmdQ.releaseIndirectHeap(this->GetParam()); auto memoryManager = pDevice->getMemoryManager(); EXPECT_TRUE(memoryManager->allocationsForReuse.peekIsEmpty()); memoryManager->freeGraphicsMemory(allocation); } INSTANTIATE_TEST_CASE_P( Device, CommandQueueIndirectHeapTest, testing::Values( IndirectHeap::DYNAMIC_STATE, IndirectHeap::GENERAL_STATE, IndirectHeap::INDIRECT_OBJECT, IndirectHeap::INSTRUCTION, IndirectHeap::SURFACE_STATE)); typedef Test CommandQueueCSTest; HWTEST_F(CommandQueueCSTest, getCSShouldReturnACSWithEnoughSizeCSRTraffic) { CommandQueueHw commandQueue(nullptr, pDevice, 0); auto WaNeeded = KernelCommandsHelper::isPipeControlWArequired(); size_t sizeCSRNeeds = sizeof(typename FamilyType::PIPE_CONTROL) * (WaNeeded ? 2 : 1) + sizeof(typename FamilyType::MI_BATCH_BUFFER_END) + MemoryConstants::cacheLineSize - 1; sizeCSRNeeds = alignUp(sizeCSRNeeds, MemoryConstants::cacheLineSize); // NOTE: This test attempts to reserve the maximum amount // of memory such that if a client gets everything he wants // we don't overflow/corrupt memory when CSR appends its // work. size_t sizeCQReserves = CSRequirements::minCommandQueueCommandStreamSize; EXPECT_EQ(sizeCSRNeeds, sizeCQReserves); size_t sizeRequested = 0x1000 - sizeCQReserves; auto &cs = commandQueue.getCS(sizeRequested); ASSERT_GE(0x1000u, cs.getMaxAvailableSpace()); EXPECT_GE(cs.getAvailableSpace(), sizeRequested); cs.getSpace(sizeRequested); // This should trigger an assert. //cs.getSpace(sizeCSRNeeds); // This won't trigger an assert. CSR should use // this interface for CS's it doesn't own. cs.getSpaceUnsecure(sizeCSRNeeds); EXPECT_EQ(0x1000u, cs.getUsed()); } struct KmdNotifyTests : public ::testing::Test { void SetUp() override { resetObjects(1, 1); *device->getTagAddress() = taskCountToWait; } void TearDown() override { delete cmdQ; delete device; DeviceFactory::releaseDevices(); } void resetObjects(int32_t overrideEnable, int32_t overrideTimeout) { if (cmdQ) { delete cmdQ; } if (device) { delete device; DeviceFactory::releaseDevices(); } DebugManagerStateRestore stateRestore; DebugManager.flags.OverrideEnableKmdNotify.set(overrideEnable); DebugManager.flags.OverrideKmdNotifyDelayMs.set(overrideTimeout); size_t numDevices; HardwareInfo *hwInfo = nullptr; DeviceFactory::getDevices(&hwInfo, numDevices); device = Device::create(hwInfo); cmdQ = new ::testing::NiceMock(&context, device); device->getCommandStreamReceiver().waitForFlushStamp(flushStampToWait); } class MyCommandQueue : public CommandQueue { public: MyCommandQueue(Context *ctx, Device *device) : CommandQueue(ctx, device, 0) {} }; template class MyCsr : public UltCommandStreamReceiver { public: MyCsr(const HardwareInfo &hwInfo) : UltCommandStreamReceiver(hwInfo) {} MOCK_METHOD1(waitForFlushStamp, bool(FlushStamp &flushStampToWait)); MOCK_METHOD3(waitForCompletionWithTimeout, bool(bool enableTimeout, int64_t timeoutMs, uint32_t taskCountToWait)); }; MockContext context; MockDevice *device = nullptr; ::testing::NiceMock *cmdQ = nullptr; FlushStamp flushStampToWait = 1000; uint32_t taskCountToWait = 5; }; HWTEST_F(KmdNotifyTests, givenTaskCountWhenWaitUntilCompletionCalledThenAlwaysTryCpuPolling) { auto csr = new ::testing::NiceMock>(device->getHardwareInfo()); device->resetCommandStreamReceiver(csr); EXPECT_CALL(*csr, waitForCompletionWithTimeout(true, 1, taskCountToWait)).Times(1).WillOnce(::testing::Return(true)); EXPECT_CALL(*csr, waitForCompletionWithTimeout(false, 1, taskCountToWait)).Times(0); cmdQ->waitUntilComplete(taskCountToWait, flushStampToWait); } HWTEST_F(KmdNotifyTests, givenTaskCountAndKmdNotifyDisabledWhenWaitUntilCompletionCalledThenTryCpuPollingWithoutTimeout) { resetObjects(0, 0); auto csr = new ::testing::NiceMock>(device->getHardwareInfo()); device->resetCommandStreamReceiver(csr); EXPECT_CALL(*csr, waitForCompletionWithTimeout(false, 0, taskCountToWait)).Times(1).WillOnce(::testing::Return(true)); EXPECT_CALL(*csr, waitForFlushStamp(::testing::_)).Times(0); cmdQ->waitUntilComplete(taskCountToWait, flushStampToWait); } HWTEST_F(KmdNotifyTests, givenNotReadyTaskCountWhenWaitUntilCompletionCalledThenTryCpuPollingAndKmdWait) { auto csr = new ::testing::NiceMock>(device->getHardwareInfo()); device->resetCommandStreamReceiver(csr); *device->getTagAddress() = taskCountToWait - 1; ::testing::InSequence is; EXPECT_CALL(*csr, waitForCompletionWithTimeout(true, 1, taskCountToWait)).Times(1).WillOnce(::testing::Return(false)); EXPECT_CALL(*csr, waitForFlushStamp(flushStampToWait)).Times(1).WillOnce(::testing::Return(true)); EXPECT_CALL(*csr, waitForCompletionWithTimeout(false, 1, taskCountToWait)).Times(1).WillOnce(::testing::Return(false)); //we have unrecoverable for this case, this will throw. EXPECT_THROW(cmdQ->waitUntilComplete(taskCountToWait, flushStampToWait), std::exception); } HWTEST_F(KmdNotifyTests, givenReadyTaskCountWhenWaitUntilCompletionCalledThenTryCpuPollingAndDontCallKmdWait) { auto csr = new ::testing::NiceMock>(device->getHardwareInfo()); device->resetCommandStreamReceiver(csr); ::testing::InSequence is; EXPECT_CALL(*csr, waitForCompletionWithTimeout(true, 1, taskCountToWait)).Times(1).WillOnce(::testing::Return(true)); EXPECT_CALL(*csr, waitForFlushStamp(::testing::_)).Times(0); cmdQ->waitUntilComplete(taskCountToWait, flushStampToWait); } HWTEST_F(KmdNotifyTests, givenNotReadyTaskCountWhenPollForCompletionCalledThenTimeout) { CommandQueue commandQ(&context, device, 0); *device->getTagAddress() = taskCountToWait - 1; auto success = device->getCommandStreamReceiver().waitForCompletionWithTimeout(true, 1, taskCountToWait); EXPECT_FALSE(success); } HWTEST_F(KmdNotifyTests, givenMultipleCommandQueuesWhenMarkerIsEmittedThenGraphicsAllocationIsReused) { std::unique_ptr commandQ(new CommandQueue(&context, device, 0)); *device->getTagAddress() = 0; commandQ->enqueueMarkerWithWaitList(0, nullptr, nullptr); commandQ->enqueueMarkerWithWaitList(0, nullptr, nullptr); auto commandStreamGraphicsAllocation = commandQ->getCS(0).getGraphicsAllocation(); commandQ.reset(new CommandQueue(&context, device, 0)); commandQ->enqueueMarkerWithWaitList(0, nullptr, nullptr); commandQ->enqueueMarkerWithWaitList(0, nullptr, nullptr); auto commandStreamGraphicsAllocation2 = commandQ->getCS(0).getGraphicsAllocation(); EXPECT_EQ(commandStreamGraphicsAllocation, commandStreamGraphicsAllocation2); } constexpr char sipPattern[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 39, 41}; static_assert(false == isAligned(sizeof(sipPattern)), "Will be checking for automatic cacheline alignment, so pattern length must not be a multiple of cacheline"); constexpr size_t alignedPatternSize = alignUp(sizeof(sipPattern), MemoryConstants::cacheLineSize); TEST(CommandQueueGetIndirectHeap, whenNewInstructionHeapIsBeingCreatedThenCommandStreamReceiverCanReserveAMemoryBlockAtItsBegining) { auto mockDevice = std::unique_ptr(MockDevice::create(nullptr)); MockCommandStreamReceiver *csr = new MockCommandStreamReceiver; mockDevice->resetCommandStreamReceiver(csr); csr->instructionHeapReserveredData.assign(sipPattern, sipPattern + sizeof(sipPattern)); MockCommandQueue cmdQ{nullptr, mockDevice.get(), nullptr}; IndirectHeap &heap = cmdQ.getIndirectHeap(OCLRT::IndirectHeap::INSTRUCTION, 8192); EXPECT_LE(8192U, heap.getAvailableSpace()); EXPECT_EQ(alignedPatternSize, heap.getUsed()); ASSERT_LE(sizeof(sipPattern), heap.getMaxAvailableSpace()); char *reservedBlock = reinterpret_cast(heap.getBase()); auto dataFoundInReservedBlock = ArrayRef(reservedBlock, sizeof(sipPattern)); auto expectedData = ArrayRef(csr->instructionHeapReserveredData); EXPECT_THAT(dataFoundInReservedBlock, testing::ContainerEq(expectedData)); } TEST(CommandQueueGetIndirectHeap, whenCheckingForCsrInstructionHeapReservedBlockSizeThenCachelineAlignmentIsExpected) { auto mockDevice = std::unique_ptr(MockDevice::create(nullptr)); MockCommandStreamReceiver *csr = new MockCommandStreamReceiver; mockDevice->resetCommandStreamReceiver(csr); csr->instructionHeapReserveredData.assign(sipPattern, sipPattern + sizeof(sipPattern)); MockCommandQueue cmdQ{nullptr, mockDevice.get(), nullptr}; EXPECT_GE(alignedPatternSize, csr->getInstructionHeapCmdStreamReceiverReservedSize()); EXPECT_EQ(alignedPatternSize, cmdQ.getInstructionHeapReservedBlockSize()); } TEST(CommandQueue, givenEnqueueAcquireSharedObjectsWhenNoObjectsThenReturnSuccess) { MockContext context; CommandQueue cmdQ(&context, nullptr, 0); cl_uint numObjects = 0; cl_mem *memObjects = nullptr; cl_int result = cmdQ.enqueueAcquireSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_SUCCESS); } TEST(CommandQueue, givenEnqueueAcquireSharedObjectsWhenIncorrectArgumentsThenReturnProperError) { MockContext context; CommandQueue cmdQ(&context, nullptr, 0); cl_uint numObjects = 1; cl_mem *memObjects = nullptr; cl_int result = cmdQ.enqueueAcquireSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_VALUE); numObjects = 0; memObjects = (cl_mem *)1; result = cmdQ.enqueueAcquireSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_VALUE); numObjects = 0; memObjects = (cl_mem *)1; result = cmdQ.enqueueAcquireSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_VALUE); cl_mem memObject = nullptr; numObjects = 1; memObjects = &memObject; result = cmdQ.enqueueAcquireSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_MEM_OBJECT); auto buffer = std::unique_ptr(BufferHelper<>::create(&context)); memObject = buffer.get(); numObjects = 1; memObjects = &memObject; result = cmdQ.enqueueAcquireSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_MEM_OBJECT); } TEST(CommandQueue, givenEnqueueReleaseSharedObjectsWhenNoObjectsThenReturnSuccess) { MockContext context; CommandQueue cmdQ(&context, nullptr, 0); cl_uint numObjects = 0; cl_mem *memObjects = nullptr; cl_int result = cmdQ.enqueueReleaseSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_SUCCESS); } TEST(CommandQueue, givenEnqueueReleaseSharedObjectsWhenIncorrectArgumentsThenReturnProperError) { MockContext context; CommandQueue cmdQ(&context, nullptr, 0); cl_uint numObjects = 1; cl_mem *memObjects = nullptr; cl_int result = cmdQ.enqueueReleaseSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_VALUE); numObjects = 0; memObjects = (cl_mem *)1; result = cmdQ.enqueueReleaseSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_VALUE); numObjects = 0; memObjects = (cl_mem *)1; result = cmdQ.enqueueReleaseSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_VALUE); cl_mem memObject = nullptr; numObjects = 1; memObjects = &memObject; result = cmdQ.enqueueReleaseSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_MEM_OBJECT); auto buffer = std::unique_ptr(BufferHelper<>::create(&context)); memObject = buffer.get(); numObjects = 1; memObjects = &memObject; result = cmdQ.enqueueReleaseSharedObjects(numObjects, memObjects, 0, nullptr, nullptr, 0); EXPECT_EQ(result, CL_INVALID_MEM_OBJECT); }