/* * Copyright (C) 2017-2019 Intel Corporation * * SPDX-License-Identifier: MIT * */ #include "runtime/built_ins/built_ins.h" #include "runtime/compiler_interface/compiler_interface.h" #include "runtime/helpers/aligned_memory.h" #include "runtime/helpers/mipmap.h" #include "runtime/mem_obj/image.h" #include "runtime/mem_obj/mem_obj_helper.h" #include "runtime/os_interface/os_context.h" #include "unit_tests/command_queue/command_queue_fixture.h" #include "unit_tests/fixtures/device_fixture.h" #include "unit_tests/fixtures/image_fixture.h" #include "unit_tests/fixtures/memory_management_fixture.h" #include "unit_tests/helpers/debug_manager_state_restore.h" #include "unit_tests/helpers/kernel_binary_helper.h" #include "unit_tests/helpers/memory_management.h" #include "unit_tests/mocks/mock_context.h" #include "unit_tests/mocks/mock_gmm.h" #include "unit_tests/mocks/mock_memory_manager.h" #include "test.h" using namespace OCLRT; static const unsigned int testImageDimensions = 45; auto channelType = CL_UNORM_INT8; auto channelOrder = CL_RGBA; auto const elementSize = 4; //sizeof CL_RGBA * CL_UNORM_INT8 class CreateImageTest : public DeviceFixture, public testing::TestWithParam, public CommandQueueHwFixture { typedef CommandQueueHwFixture CommandQueueFixture; public: CreateImageTest() { } Image *createImageWithFlags(cl_mem_flags flags) { auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); return Image::create(context, flags, surfaceFormat, &imageDesc, nullptr, retVal); } protected: void SetUp() override { DeviceFixture::SetUp(); CommandQueueFixture::SetUp(pDevice, 0); flags = GetParam(); // clang-format off imageFormat.image_channel_data_type = channelType; imageFormat.image_channel_order = channelOrder; imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D; imageDesc.image_width = testImageDimensions; imageDesc.image_height = testImageDimensions; imageDesc.image_depth = 0; imageDesc.image_array_size = 1; imageDesc.image_row_pitch = 0; imageDesc.image_slice_pitch = 0; imageDesc.num_mip_levels = 0; imageDesc.num_samples = 0; imageDesc.mem_object = NULL; // clang-format on } void TearDown() override { CommandQueueFixture::TearDown(); DeviceFixture::TearDown(); } cl_image_format imageFormat; cl_image_desc imageDesc; cl_int retVal = CL_SUCCESS; cl_mem_flags flags = 0; unsigned char pHostPtr[testImageDimensions * testImageDimensions * elementSize * 4]; }; typedef CreateImageTest CreateImageNoHostPtr; TEST(TestSliceAndRowPitch, ForDifferentDescriptorsGetHostPtrSlicePitchAndRowPitchReturnsProperValues) { DebugManagerStateRestore dbgRestorer; DebugManager.flags.ForceLinearImages.set(true); cl_image_format imageFormat; cl_image_desc imageDesc; cl_int retVal; MockContext context; const size_t width = 5; const size_t height = 3; const size_t depth = 2; char *hostPtr = (char *)alignedMalloc(width * height * depth * elementSize * 2, 64); imageFormat.image_channel_data_type = channelType; imageFormat.image_channel_order = channelOrder; imageDesc.num_mip_levels = 0; imageDesc.num_samples = 0; imageDesc.mem_object = NULL; // 1D image with 0 row_pitch and 0 slice_pitch imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; imageDesc.image_width = width; imageDesc.image_height = 0; imageDesc.image_depth = 0; imageDesc.image_array_size = 0; imageDesc.image_row_pitch = 0; imageDesc.image_slice_pitch = 0; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); auto image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal); ASSERT_NE(nullptr, image); EXPECT_EQ(width * elementSize, image->getHostPtrRowPitch()); EXPECT_EQ(0u, image->getHostPtrSlicePitch()); delete image; // 1D image with non-zero row_pitch and 0 slice_pitch imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; imageDesc.image_width = width; imageDesc.image_height = 0; imageDesc.image_depth = 0; imageDesc.image_array_size = 0; imageDesc.image_row_pitch = (width + 1) * elementSize; imageDesc.image_slice_pitch = 0; image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal); ASSERT_NE(nullptr, image); EXPECT_EQ((width + 1) * elementSize, image->getHostPtrRowPitch()); EXPECT_EQ(0u, image->getHostPtrSlicePitch()); delete image; // 2D image with non-zero row_pitch and 0 slice_pitch imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D; imageDesc.image_width = width; imageDesc.image_height = height; imageDesc.image_depth = 0; imageDesc.image_array_size = 0; imageDesc.image_row_pitch = (width + 1) * elementSize; imageDesc.image_slice_pitch = 0; image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal); ASSERT_NE(nullptr, image); EXPECT_EQ((width + 1) * elementSize, image->getHostPtrRowPitch()); EXPECT_EQ(0u, image->getHostPtrSlicePitch()); delete image; // 1D ARRAY image with non-zero row_pitch and 0 slice_pitch imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D_ARRAY; imageDesc.image_width = width; imageDesc.image_height = 0; imageDesc.image_depth = 0; imageDesc.image_array_size = 2; imageDesc.image_row_pitch = (width + 1) * elementSize; imageDesc.image_slice_pitch = 0; image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal); ASSERT_NE(nullptr, image); EXPECT_EQ((width + 1) * elementSize, image->getHostPtrRowPitch()); EXPECT_EQ((width + 1) * elementSize, image->getHostPtrSlicePitch()); delete image; // 2D ARRAY image with non-zero row_pitch and 0 slice_pitch imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D_ARRAY; imageDesc.image_width = width; imageDesc.image_height = height; imageDesc.image_depth = 0; imageDesc.image_array_size = 2; imageDesc.image_row_pitch = (width + 1) * elementSize; imageDesc.image_slice_pitch = 0; image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal); ASSERT_NE(nullptr, image); EXPECT_EQ((width + 1) * elementSize, image->getHostPtrRowPitch()); EXPECT_EQ((width + 1) * elementSize * height, image->getHostPtrSlicePitch()); delete image; // 2D ARRAY image with zero row_pitch and non-zero slice_pitch imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D_ARRAY; imageDesc.image_width = width; imageDesc.image_height = height; imageDesc.image_depth = 0; imageDesc.image_array_size = 2; imageDesc.image_row_pitch = 0; imageDesc.image_slice_pitch = (width + 1) * elementSize * height; image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal); ASSERT_NE(nullptr, image); EXPECT_EQ(width * elementSize, image->getHostPtrRowPitch()); EXPECT_EQ((width + 1) * elementSize * height, image->getHostPtrSlicePitch()); delete image; // 2D ARRAY image with non-zero row_pitch and non-zero slice_pitch imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D_ARRAY; imageDesc.image_width = width; imageDesc.image_height = height; imageDesc.image_depth = 0; imageDesc.image_array_size = 2; imageDesc.image_row_pitch = (width + 1) * elementSize; imageDesc.image_slice_pitch = (width + 1) * elementSize * height; image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal); ASSERT_NE(nullptr, image); EXPECT_EQ((width + 1) * elementSize, image->getHostPtrRowPitch()); EXPECT_EQ((width + 1) * elementSize * height, image->getHostPtrSlicePitch()); delete image; // 2D ARRAY image with non-zero row_pitch and non-zero slice_pitch > row_pitch * height imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D_ARRAY; imageDesc.image_width = width; imageDesc.image_height = height; imageDesc.image_depth = 0; imageDesc.image_array_size = 2; imageDesc.image_row_pitch = (width + 1) * elementSize; imageDesc.image_slice_pitch = (width + 1) * elementSize * (height + 1); image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal); ASSERT_NE(nullptr, image); EXPECT_EQ((width + 1) * elementSize, image->getHostPtrRowPitch()); EXPECT_EQ((width + 1) * elementSize * (height + 1), image->getHostPtrSlicePitch()); delete image; alignedFree(hostPtr); } TEST(TestCreateImage, UseSharedContextToCreateImage) { cl_image_format imageFormat; cl_image_desc imageDesc; cl_int retVal; MockContext context; context.isSharedContext = true; const size_t width = 5; const size_t height = 3; const size_t depth = 2; char *hostPtr = (char *)alignedMalloc(width * height * depth * elementSize * 2, 64); imageFormat.image_channel_data_type = channelType; imageFormat.image_channel_order = channelOrder; imageDesc.num_mip_levels = 0; imageDesc.num_samples = 0; imageDesc.mem_object = NULL; // 2D image with non-zero row_pitch and 0 slice_pitch imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D; imageDesc.image_width = width; imageDesc.image_height = height; imageDesc.image_depth = 0; imageDesc.image_array_size = 0; imageDesc.image_row_pitch = (width + 1) * elementSize; imageDesc.image_slice_pitch = 0; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); auto image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal); ASSERT_NE(nullptr, image); EXPECT_EQ((width + 1) * elementSize, image->getHostPtrRowPitch()); EXPECT_EQ(0u, image->getHostPtrSlicePitch()); EXPECT_TRUE(image->isMemObjZeroCopy()); delete image; alignedFree(hostPtr); } TEST(TestCreateImageUseHostPtr, CheckMemoryAllocationForDifferenHostPtrAlignments) { KernelBinaryHelper kbHelper(KernelBinaryHelper::BUILT_INS); cl_image_format imageFormat; cl_image_desc imageDesc; cl_int retVal; MockContext context; const size_t width = 4; const size_t height = 32; imageFormat.image_channel_data_type = channelType; imageFormat.image_channel_order = channelOrder; imageDesc.num_mip_levels = 0; imageDesc.num_samples = 0; imageDesc.mem_object = NULL; // 2D image with 0 row_pitch and 0 slice_pitch imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D; imageDesc.image_width = width; imageDesc.image_height = height; imageDesc.image_depth = 0; imageDesc.image_array_size = 0; imageDesc.image_row_pitch = alignUp(alignUp(width, 4) * 4, 0x80); //row pitch for tiled img imageDesc.image_slice_pitch = 0; void *pageAlignedPointer = alignedMalloc(imageDesc.image_row_pitch * height * 1 * 4 + 256, 4096); void *hostPtr[] = {reinterpret_cast(reinterpret_cast(pageAlignedPointer) + 16), // 16 - byte alignment reinterpret_cast(reinterpret_cast(pageAlignedPointer) + 32), // 32 - byte alignment reinterpret_cast(reinterpret_cast(pageAlignedPointer) + 64), // 64 - byte alignment reinterpret_cast(reinterpret_cast(pageAlignedPointer) + 128)}; // 128 - byte alignment bool result[] = {false, false, true, true}; cl_mem_flags flags = CL_MEM_HOST_NO_ACCESS | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); for (int i = 0; i < 4; i++) { auto image = Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr[i], retVal); ASSERT_NE(nullptr, image); auto address = image->getCpuAddress(); if (result[i] && !image->allowTiling()) { EXPECT_EQ(hostPtr[i], address); } else { EXPECT_NE(hostPtr[i], address); } delete image; } alignedFree(pageAlignedPointer); } TEST(TestCreateImageUseHostPtr, givenZeroCopyImageValuesWhenUsingHostPtrThenZeroCopyImageIsCreated) { cl_int retVal = CL_SUCCESS; MockContext context; cl_image_desc imageDesc = {}; imageDesc.image_width = 4096; imageDesc.image_height = 1; imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; cl_image_format imageFormat = {}; imageFormat.image_channel_data_type = CL_UNSIGNED_INT8; imageFormat.image_channel_order = CL_R; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); auto hostPtr = alignedMalloc(imageDesc.image_width * surfaceFormat->ImageElementSizeInBytes, MemoryConstants::cacheLineSize); auto image = std::unique_ptr(Image::create( &context, flags, surfaceFormat, &imageDesc, hostPtr, retVal)); EXPECT_NE(nullptr, image); EXPECT_EQ(CL_SUCCESS, retVal); EXPECT_TRUE(image->isMemObjZeroCopy()); EXPECT_EQ(hostPtr, image->getGraphicsAllocation()->getUnderlyingBuffer()); alignedFree(hostPtr); } TEST_P(CreateImageNoHostPtr, validFlags) { auto image = createImageWithFlags(flags); ASSERT_EQ(CL_SUCCESS, retVal); ASSERT_NE(nullptr, image); delete image; } TEST_P(CreateImageNoHostPtr, getImageDesc) { auto image = createImageWithFlags(flags); ASSERT_EQ(CL_SUCCESS, retVal); ASSERT_NE(nullptr, image); const auto &imageDesc = image->getImageDesc(); // Sometimes the user doesn't pass image_row/slice_pitch during a create. // Ensure the driver fills in the missing data. EXPECT_NE(0u, imageDesc.image_row_pitch); EXPECT_GE(imageDesc.image_slice_pitch, imageDesc.image_row_pitch); delete image; } TEST_P(CreateImageNoHostPtr, withImageGraphicsAllocationReportsImageType) { auto image = createImageWithFlags(flags); ASSERT_EQ(CL_SUCCESS, retVal); ASSERT_NE(nullptr, image); auto allocation = image->getGraphicsAllocation(); EXPECT_TRUE(allocation->getAllocationType() == GraphicsAllocation::AllocationType::IMAGE); auto isImageWritable = !(flags & (CL_MEM_READ_ONLY | CL_MEM_HOST_READ_ONLY | CL_MEM_HOST_NO_ACCESS)); EXPECT_EQ(isImageWritable, allocation->isMemObjectsAllocationWithWritableFlags()); delete image; } // Parameterized test that tests image creation with all flags that should be // valid with a nullptr host ptr static cl_mem_flags NoHostPtrFlags[] = { CL_MEM_READ_WRITE, CL_MEM_WRITE_ONLY, CL_MEM_READ_ONLY, CL_MEM_HOST_READ_ONLY, CL_MEM_HOST_WRITE_ONLY, CL_MEM_HOST_NO_ACCESS}; INSTANTIATE_TEST_CASE_P( CreateImageTest_Create, CreateImageNoHostPtr, testing::ValuesIn(NoHostPtrFlags)); struct CreateImageHostPtr : public CreateImageTest, public MemoryManagementFixture { typedef CreateImageTest BaseClass; CreateImageHostPtr() { } void SetUp() override { MemoryManagementFixture::SetUp(); BaseClass::SetUp(); } void TearDown() override { delete image; BaseClass::TearDown(); MemoryManagementFixture::TearDown(); } Image *createImage(cl_int &retVal) { auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); return Image::create( context, flags, surfaceFormat, &imageDesc, pHostPtr, retVal); } cl_int retVal = CL_INVALID_VALUE; Image *image = nullptr; }; TEST_P(CreateImageHostPtr, isResidentDefaultsToFalseAfterCreate) { KernelBinaryHelper kbHelper(KernelBinaryHelper::BUILT_INS); image = createImage(retVal); ASSERT_NE(nullptr, image); EXPECT_FALSE(image->getGraphicsAllocation()->isResident(pDevice->getDefaultEngine().osContext->getContextId())); } TEST_P(CreateImageHostPtr, getAddress) { image = createImage(retVal); ASSERT_NE(nullptr, image); auto address = image->getBasePtrForMap(); EXPECT_NE(nullptr, address); if (!(flags & CL_MEM_USE_HOST_PTR)) { EXPECT_EQ(nullptr, image->getHostPtr()); } if (flags & CL_MEM_USE_HOST_PTR) { //if size fits within a page then zero copy can be applied, if not RT needs to do a copy of image auto computedSize = imageDesc.image_width * elementSize * alignUp(imageDesc.image_height, 4) * imageDesc.image_array_size; auto ptrSize = imageDesc.image_width * elementSize * imageDesc.image_height * imageDesc.image_array_size; auto alignedRequiredSize = alignSizeWholePage(static_cast(pHostPtr), computedSize); auto alignedPtrSize = alignSizeWholePage(static_cast(pHostPtr), ptrSize); size_t HalignReq = imageDesc.image_type == CL_MEM_OBJECT_IMAGE1D_ARRAY ? 64 : 1; auto rowPitch = imageDesc.image_width * elementSize; auto slicePitch = rowPitch * imageDesc.image_height; auto requiredRowPitch = alignUp(imageDesc.image_width, HalignReq) * elementSize; auto requiredSlicePitch = requiredRowPitch * alignUp(imageDesc.image_height, 4); bool copyRequired = (alignedRequiredSize > alignedPtrSize) | (requiredRowPitch != rowPitch) | (slicePitch != requiredSlicePitch); EXPECT_EQ(pHostPtr, address); EXPECT_EQ(pHostPtr, image->getHostPtr()); if (copyRequired) { EXPECT_FALSE(image->isMemObjZeroCopy()); } } else { EXPECT_NE(pHostPtr, address); } if (flags & CL_MEM_COPY_HOST_PTR && !image->allowTiling()) { // Buffer should contain a copy of host memory EXPECT_EQ(0, memcmp(pHostPtr, address, sizeof(testImageDimensions))); } } TEST_P(CreateImageHostPtr, givenImageWhenItIsCreateItHasProperSizeAndGraphicsAllocation) { image = createImage(retVal); ASSERT_NE(nullptr, image); EXPECT_NE(0u, image->getSize()); EXPECT_NE(nullptr, image->getGraphicsAllocation()); } TEST_P(CreateImageHostPtr, getImageDesc) { image = createImage(retVal); ASSERT_NE(nullptr, image); const auto &imageDesc = image->getImageDesc(); // clang-format off EXPECT_EQ(this->imageDesc.image_type, imageDesc.image_type); EXPECT_EQ(this->imageDesc.image_width, imageDesc.image_width); EXPECT_EQ(this->imageDesc.image_height, imageDesc.image_height); EXPECT_EQ(this->imageDesc.image_depth, imageDesc.image_depth); EXPECT_EQ(0u, imageDesc.image_array_size); EXPECT_NE(0u, imageDesc.image_row_pitch); EXPECT_GE(imageDesc.image_slice_pitch, imageDesc.image_row_pitch); EXPECT_EQ(this->imageDesc.num_mip_levels, imageDesc.num_mip_levels); EXPECT_EQ(this->imageDesc.num_samples, imageDesc.num_samples); EXPECT_EQ(this->imageDesc.buffer, imageDesc.buffer); EXPECT_EQ(this->imageDesc.mem_object, imageDesc.mem_object); // clang-format on EXPECT_EQ(image->getHostPtrRowPitch(), static_cast(imageDesc.image_width * elementSize)); // Only 3D, and array images can have slice pitch int isArrayOr3DType = 0; if (this->imageDesc.image_type == CL_MEM_OBJECT_IMAGE3D || this->imageDesc.image_type == CL_MEM_OBJECT_IMAGE2D_ARRAY || this->imageDesc.image_type == CL_MEM_OBJECT_IMAGE2D_ARRAY) { isArrayOr3DType = 1; } EXPECT_EQ(image->getHostPtrSlicePitch(), static_cast(imageDesc.image_width * elementSize * imageDesc.image_height) * isArrayOr3DType); EXPECT_EQ(image->getImageCount(), 1u); } TEST_P(CreateImageHostPtr, failedAllocationInjection) { InjectedFunction method = [this](size_t failureIndex) { // System under test image = createImage(retVal); if (nonfailingAllocation == failureIndex) { EXPECT_EQ(CL_SUCCESS, retVal); EXPECT_NE(nullptr, image); } else { EXPECT_EQ(CL_OUT_OF_HOST_MEMORY, retVal) << "for allocation " << failureIndex; EXPECT_EQ(nullptr, image); } delete image; image = nullptr; }; injectFailures(method, 4); // check only first 5 allocations - avoid checks on writeImg call allocations for tiled imgs } TEST_P(CreateImageHostPtr, checkWritingOutsideAllocatedMemoryWhileCreatingImage) { auto mockMemoryManager = new MockMemoryManager(executionEnvironment); pDevice->injectMemoryManager(mockMemoryManager); context->setMemoryManager(mockMemoryManager); mockMemoryManager->redundancyRatio = 2; memset(pHostPtr, 1, testImageDimensions * testImageDimensions * elementSize * 4); imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D_ARRAY; imageDesc.image_height = 1; imageDesc.image_row_pitch = elementSize * imageDesc.image_width + 1; image = createImage(retVal); char *memory = (char *)image->getGraphicsAllocation()->getUnderlyingBuffer(); auto memorySize = image->getGraphicsAllocation()->getUnderlyingBufferSize() / 2; for (size_t i = 0; i < image->getHostPtrSlicePitch(); ++i) { if (i < imageDesc.image_width * elementSize) { EXPECT_EQ(1, memory[i]); } else { EXPECT_EQ(0, memory[i]); } } for (size_t i = 0; i < memorySize; ++i) { EXPECT_EQ(0, memory[memorySize + i]); } mockMemoryManager->redundancyRatio = 1; } struct ModifyableImage { enum { flags = 0 }; static cl_image_format imageFormat; static cl_image_desc imageDesc; static void *hostPtr; static OCLRT::Context *context; }; void *ModifyableImage::hostPtr = nullptr; OCLRT::Context *ModifyableImage::context = nullptr; cl_image_format ModifyableImage::imageFormat; cl_image_desc ModifyableImage::imageDesc; class ImageTransfer : public ::testing::Test { public: void SetUp() override { context = new MockContext(); ASSERT_NE(context, nullptr); ModifyableImage::context = context; ModifyableImage::hostPtr = nullptr; ModifyableImage::imageFormat = {CL_R, CL_FLOAT}; ModifyableImage::imageDesc = {CL_MEM_OBJECT_IMAGE1D, 512, 0, 0, 0, 0, 0, 0, 0, {nullptr}}; hostPtr = nullptr; unalignedHostPtr = nullptr; } void TearDown() override { if (context) delete context; if (hostPtr) alignedFree(hostPtr); } void createHostPtrs(size_t imageSize) { hostPtr = alignedMalloc(imageSize + 100, 4096); unalignedHostPtr = (char *)hostPtr + 4; memset(hostPtr, 0, imageSize + 100); memset(unalignedHostPtr, 1, imageSize); } MockContext *context; void *hostPtr; void *unalignedHostPtr; }; TEST_F(ImageTransfer, GivenNonZeroCopyImageWhenDataTransferedFromHostPtrToMemStorageThenNoOverflowOfHostPtr) { size_t imageSize = 512 * 4; createHostPtrs(imageSize); ModifyableImage::imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; ModifyableImage::imageDesc.image_width = 512; ModifyableImage::imageDesc.image_height = 0; ModifyableImage::imageDesc.image_row_pitch = 0; ModifyableImage::imageDesc.image_array_size = 0; ModifyableImage::imageFormat.image_channel_order = CL_R; ModifyableImage::imageFormat.image_channel_data_type = CL_FLOAT; ModifyableImage::hostPtr = unalignedHostPtr; Image *imageNonZeroCopy = ImageHelper>::create(); ASSERT_NE(nullptr, imageNonZeroCopy); void *memoryStorage = imageNonZeroCopy->getCpuAddress(); size_t memoryStorageSize = imageNonZeroCopy->getSize(); ASSERT_NE(memoryStorage, unalignedHostPtr); int result = memcmp(memoryStorage, unalignedHostPtr, imageSize); EXPECT_EQ(0, result); memset(memoryStorage, 0, memoryStorageSize); memset((char *)unalignedHostPtr + imageSize, 2, 100 - 4); auto &imgDesc = imageNonZeroCopy->getImageDesc(); MemObjOffsetArray copyOffset = {{0, 0, 0}}; MemObjSizeArray copySize = {{imgDesc.image_width, imgDesc.image_height, imgDesc.image_depth}}; imageNonZeroCopy->transferDataFromHostPtr(copySize, copyOffset); void *foundData = memchr(memoryStorage, 2, memoryStorageSize); EXPECT_EQ(0, foundData); delete imageNonZeroCopy; } TEST_F(ImageTransfer, GivenNonZeroCopyNonZeroRowPitchImageWhenDataIsTransferedFromHostPtrToMemStorageThenDestinationIsNotOverflowed) { ModifyableImage::imageDesc.image_width = 16; ModifyableImage::imageDesc.image_row_pitch = 65; ModifyableImage::imageFormat.image_channel_data_type = CL_UNORM_INT8; size_t imageSize = ModifyableImage::imageDesc.image_row_pitch; size_t imageWidth = ModifyableImage::imageDesc.image_width; createHostPtrs(imageSize); ModifyableImage::hostPtr = unalignedHostPtr; Image *imageNonZeroCopy = ImageHelper>::create(); ASSERT_NE(nullptr, imageNonZeroCopy); void *memoryStorage = imageNonZeroCopy->getCpuAddress(); size_t memoryStorageSize = imageNonZeroCopy->getSize(); ASSERT_NE(memoryStorage, unalignedHostPtr); int result = memcmp(memoryStorage, unalignedHostPtr, imageWidth); EXPECT_EQ(0, result); memset(memoryStorage, 0, memoryStorageSize); memset((char *)unalignedHostPtr + imageSize, 2, 100 - 4); auto &imgDesc = imageNonZeroCopy->getImageDesc(); MemObjOffsetArray copyOffset = {{0, 0, 0}}; MemObjSizeArray copySize = {{imgDesc.image_width, imgDesc.image_height, imgDesc.image_depth}}; imageNonZeroCopy->transferDataFromHostPtr(copySize, copyOffset); void *foundData = memchr(memoryStorage, 2, memoryStorageSize); EXPECT_EQ(0, foundData); delete imageNonZeroCopy; } TEST_F(ImageTransfer, GivenNonZeroCopyNonZeroRowPitchWithExtraBytes1DArrayImageWhenDataIsTransferedForthAndBackThenDataValidates) { ModifyableImage::imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D_ARRAY; ModifyableImage::imageDesc.image_width = 5; ModifyableImage::imageDesc.image_row_pitch = 28; // == (4 * 5) row bytes + (4 * 2) extra bytes ModifyableImage::imageDesc.image_array_size = 3; ModifyableImage::imageFormat.image_channel_order = CL_RGBA; ModifyableImage::imageFormat.image_channel_data_type = CL_UNORM_INT8; const size_t imageWidth = ModifyableImage::imageDesc.image_width; const size_t imageRowPitchInPixels = ModifyableImage::imageDesc.image_row_pitch / 4; const size_t imageHeight = 1; const size_t imageCount = ModifyableImage::imageDesc.image_array_size; size_t imageSize = ModifyableImage::imageDesc.image_row_pitch * imageHeight * imageCount; createHostPtrs(imageSize); uint32_t *row = static_cast(unalignedHostPtr); for (uint32_t arrayIndex = 0; arrayIndex < imageCount; ++arrayIndex) { for (uint32_t pixelInRow = 0; pixelInRow < imageRowPitchInPixels; ++pixelInRow) { if (pixelInRow < imageWidth) { row[pixelInRow] = pixelInRow; } else { row[pixelInRow] = 66; } } row = row + imageRowPitchInPixels; } ModifyableImage::hostPtr = unalignedHostPtr; Image *imageNonZeroCopy = ImageHelper>::create(); ASSERT_NE(nullptr, imageNonZeroCopy); void *memoryStorage = imageNonZeroCopy->getCpuAddress(); ASSERT_NE(memoryStorage, unalignedHostPtr); size_t internalSlicePitch = imageNonZeroCopy->getImageDesc().image_slice_pitch; // Check twice, once after image create, and second time after transfer from HostPtrToMemoryStorage // when these paths are unified, only one check will be enough for (size_t run = 0; run < 2; ++run) { row = static_cast(unalignedHostPtr); unsigned char *internalRow = static_cast(memoryStorage); if (run == 1) { auto &imgDesc = imageNonZeroCopy->getImageDesc(); MemObjOffsetArray copyOffset = {{0, 0, 0}}; MemObjSizeArray copySize = {{imgDesc.image_width, imgDesc.image_height, imgDesc.image_depth}}; imageNonZeroCopy->transferDataFromHostPtr(copySize, copyOffset); } for (size_t arrayIndex = 0; arrayIndex < imageCount; ++arrayIndex) { for (size_t pixelInRow = 0; pixelInRow < imageRowPitchInPixels; ++pixelInRow) { if (pixelInRow < imageWidth) { if (memcmp(&row[pixelInRow], &internalRow[pixelInRow * 4], 4)) { EXPECT_FALSE(1) << "Data in memory storage did not validate, row: " << pixelInRow << " array: " << arrayIndex << "\n"; } } else { // Change extra bytes pattern row[pixelInRow] = 55; } } row = row + imageRowPitchInPixels; internalRow = internalRow + internalSlicePitch; } } auto &imgDesc = imageNonZeroCopy->getImageDesc(); MemObjOffsetArray copyOffset = {{0, 0, 0}}; MemObjSizeArray copySize = {{imgDesc.image_width, imgDesc.image_height, imgDesc.image_depth}}; imageNonZeroCopy->transferDataToHostPtr(copySize, copyOffset); row = static_cast(unalignedHostPtr); for (size_t arrayIndex = 0; arrayIndex < imageCount; ++arrayIndex) { for (size_t pixelInRow = 0; pixelInRow < imageRowPitchInPixels; ++pixelInRow) { if (pixelInRow < imageWidth) { if (row[pixelInRow] != pixelInRow) { EXPECT_FALSE(1) << "Data under host_ptr did not validate, row: " << pixelInRow << " array: " << arrayIndex << "\n"; } } else { if (row[pixelInRow] != 55) { EXPECT_FALSE(1) << "Data under host_ptr corrupted in extra bytes, row: " << pixelInRow << " array: " << arrayIndex << "\n"; } } } row = row + imageRowPitchInPixels; } delete imageNonZeroCopy; } // Parameterized test that tests image creation with all flags that should be // valid with a valid host ptr static cl_mem_flags ValidHostPtrFlags[] = { 0 | CL_MEM_USE_HOST_PTR, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, CL_MEM_HOST_READ_ONLY | CL_MEM_USE_HOST_PTR, CL_MEM_HOST_WRITE_ONLY | CL_MEM_USE_HOST_PTR, CL_MEM_HOST_NO_ACCESS | CL_MEM_USE_HOST_PTR, 0 | CL_MEM_COPY_HOST_PTR, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, CL_MEM_WRITE_ONLY | CL_MEM_COPY_HOST_PTR, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, CL_MEM_HOST_READ_ONLY | CL_MEM_COPY_HOST_PTR, CL_MEM_HOST_WRITE_ONLY | CL_MEM_COPY_HOST_PTR, CL_MEM_HOST_NO_ACCESS | CL_MEM_COPY_HOST_PTR}; INSTANTIATE_TEST_CASE_P( CreateImageTest_Create, CreateImageHostPtr, testing::ValuesIn(ValidHostPtrFlags)); TEST(ImageGetSurfaceFormatInfoTest, givenNullptrFormatWhenGetSurfaceFormatInfoIsCalledThenReturnsNullptr) { auto surfaceFormat = Image::getSurfaceFormatFromTable(0, nullptr); EXPECT_EQ(nullptr, surfaceFormat); } class ImageCompressionTests : public ::testing::Test { public: class MyMemoryManager : public MockMemoryManager { public: using MockMemoryManager::MockMemoryManager; GraphicsAllocation *allocateGraphicsMemoryForImage(const AllocationData &allocationData) override { mockMethodCalled = true; capturedImgInfo = *allocationData.imgInfo; return OsAgnosticMemoryManager::allocateGraphicsMemoryForImage(allocationData); } ImageInfo capturedImgInfo = {}; bool mockMethodCalled = false; }; void SetUp() override { mockDevice.reset(MockDevice::createWithNewExecutionEnvironment(*platformDevices)); myMemoryManager = new MyMemoryManager(*mockDevice->getExecutionEnvironment()); mockDevice->injectMemoryManager(myMemoryManager); mockContext.reset(new MockContext(mockDevice.get())); } std::unique_ptr mockDevice; std::unique_ptr mockContext; MyMemoryManager *myMemoryManager = nullptr; cl_image_desc imageDesc = {}; cl_image_format imageFormat{CL_RGBA, CL_UNORM_INT8}; cl_mem_flags flags = CL_MEM_READ_WRITE; cl_int retVal = CL_SUCCESS; }; TEST_F(ImageCompressionTests, givenTiledImageWhenCreatingAllocationThenPreferRenderCompression) { imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D; imageDesc.image_width = 5; imageDesc.image_height = 5; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); auto image = std::unique_ptr(Image::create(mockContext.get(), flags, surfaceFormat, &imageDesc, nullptr, retVal)); ASSERT_NE(nullptr, image); EXPECT_TRUE(image->isTiledImage); EXPECT_TRUE(myMemoryManager->mockMethodCalled); EXPECT_TRUE(myMemoryManager->capturedImgInfo.preferRenderCompression); } TEST_F(ImageCompressionTests, givenNonTiledImageWhenCreatingAllocationThenDontPreferRenderCompression) { imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; imageDesc.image_width = 5; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); auto image = std::unique_ptr(Image::create(mockContext.get(), flags, surfaceFormat, &imageDesc, nullptr, retVal)); ASSERT_NE(nullptr, image); EXPECT_FALSE(image->isTiledImage); EXPECT_TRUE(myMemoryManager->mockMethodCalled); EXPECT_FALSE(myMemoryManager->capturedImgInfo.preferRenderCompression); } TEST(ImageTest, givenImageWhenAskedForPtrOffsetForGpuMappingThenReturnCorrectValue) { MockContext ctx; std::unique_ptr image(ImageHelper::create(&ctx)); EXPECT_FALSE(image->mappingOnCpuAllowed()); MemObjOffsetArray origin = {{4, 5, 6}}; auto retOffset = image->calculateOffsetForMapping(origin); size_t expectedOffset = image->getSurfaceFormatInfo().ImageElementSizeInBytes * origin[0] + image->getHostPtrRowPitch() * origin[1] + image->getHostPtrSlicePitch() * origin[2]; EXPECT_EQ(expectedOffset, retOffset); } TEST(ImageTest, givenImageWhenAskedForMcsInfoThenDefaultValuesAreReturned) { MockContext ctx; std::unique_ptr image(ImageHelper::create(&ctx)); auto mcsInfo = image->getMcsSurfaceInfo(); EXPECT_EQ(0u, mcsInfo.multisampleCount); EXPECT_EQ(0u, mcsInfo.qPitch); EXPECT_EQ(0u, mcsInfo.pitch); } TEST(ImageTest, givenImageWhenAskedForPtrOffsetForCpuMappingThenReturnCorrectValue) { DebugManagerStateRestore restore; DebugManager.flags.ForceLinearImages.set(true); MockContext ctx; std::unique_ptr image(ImageHelper::create(&ctx)); EXPECT_TRUE(image->mappingOnCpuAllowed()); MemObjOffsetArray origin = {{4, 5, 6}}; auto retOffset = image->calculateOffsetForMapping(origin); size_t expectedOffset = image->getSurfaceFormatInfo().ImageElementSizeInBytes * origin[0] + image->getImageDesc().image_row_pitch * origin[1] + image->getImageDesc().image_slice_pitch * origin[2]; EXPECT_EQ(expectedOffset, retOffset); } TEST(ImageTest, given1DArrayImageWhenAskedForPtrOffsetForMappingThenReturnCorrectValue) { MockContext ctx; std::unique_ptr image(ImageHelper::create(&ctx)); MemObjOffsetArray origin = {{4, 5, 0}}; auto retOffset = image->calculateOffsetForMapping(origin); size_t expectedOffset = image->getSurfaceFormatInfo().ImageElementSizeInBytes * origin[0] + image->getImageDesc().image_slice_pitch * origin[1]; EXPECT_EQ(expectedOffset, retOffset); } TEST(ImageTest, givenImageWhenAskedForPtrLengthForGpuMappingThenReturnCorrectValue) { MockContext ctx; std::unique_ptr image(ImageHelper::create(&ctx)); EXPECT_FALSE(image->mappingOnCpuAllowed()); MemObjSizeArray region = {{4, 5, 6}}; auto retLength = image->calculateMappedPtrLength(region); size_t expectedLength = image->getSurfaceFormatInfo().ImageElementSizeInBytes * region[0] + image->getHostPtrRowPitch() * region[1] + image->getHostPtrSlicePitch() * region[2]; EXPECT_EQ(expectedLength, retLength); } TEST(ImageTest, givenImageWhenAskedForPtrLengthForCpuMappingThenReturnCorrectValue) { DebugManagerStateRestore restore; DebugManager.flags.ForceLinearImages.set(true); MockContext ctx; std::unique_ptr image(ImageHelper::create(&ctx)); EXPECT_TRUE(image->mappingOnCpuAllowed()); MemObjSizeArray region = {{4, 5, 6}}; auto retLength = image->calculateMappedPtrLength(region); size_t expectedLength = image->getSurfaceFormatInfo().ImageElementSizeInBytes * region[0] + image->getImageDesc().image_row_pitch * region[1] + image->getImageDesc().image_slice_pitch * region[2]; EXPECT_EQ(expectedLength, retLength); } TEST(ImageTest, givenMipMapImage3DWhenAskedForPtrOffsetForGpuMappingThenReturnOffsetWithSlicePitch) { MockContext ctx; cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE3D; imageDesc.image_width = 5; imageDesc.image_height = 5; imageDesc.image_depth = 5; imageDesc.num_mip_levels = 2; std::unique_ptr image(ImageHelper::create(&ctx, &imageDesc)); EXPECT_FALSE(image->mappingOnCpuAllowed()); MemObjOffsetArray origin{{1, 1, 1}}; auto retOffset = image->calculateOffsetForMapping(origin); size_t expectedOffset = image->getSurfaceFormatInfo().ImageElementSizeInBytes * origin[0] + image->getHostPtrRowPitch() * origin[1] + image->getHostPtrSlicePitch() * origin[2]; EXPECT_EQ(expectedOffset, retOffset); } TEST(ImageTest, givenMipMapImage2DArrayWhenAskedForPtrOffsetForGpuMappingThenReturnOffsetWithSlicePitch) { MockContext ctx; cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D_ARRAY; imageDesc.image_width = 5; imageDesc.image_height = 5; imageDesc.image_array_size = 5; imageDesc.num_mip_levels = 2; std::unique_ptr image(ImageHelper::create(&ctx, &imageDesc)); EXPECT_FALSE(image->mappingOnCpuAllowed()); MemObjOffsetArray origin{{1, 1, 1}}; auto retOffset = image->calculateOffsetForMapping(origin); size_t expectedOffset = image->getSurfaceFormatInfo().ImageElementSizeInBytes * origin[0] + image->getHostPtrRowPitch() * origin[1] + image->getHostPtrSlicePitch() * origin[2]; EXPECT_EQ(expectedOffset, retOffset); } TEST(ImageTest, givenNonMipMapImage2DArrayWhenAskedForPtrOffsetForGpuMappingThenReturnOffsetWithSlicePitch) { MockContext ctx; cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D_ARRAY; imageDesc.image_width = 5; imageDesc.image_height = 5; imageDesc.image_array_size = 5; imageDesc.num_mip_levels = 1; std::unique_ptr image(ImageHelper::create(&ctx, &imageDesc)); EXPECT_FALSE(image->mappingOnCpuAllowed()); MemObjOffsetArray origin{{1, 1, 1}}; auto retOffset = image->calculateOffsetForMapping(origin); size_t expectedOffset = image->getSurfaceFormatInfo().ImageElementSizeInBytes * origin[0] + image->getHostPtrRowPitch() * origin[1] + image->getHostPtrSlicePitch() * origin[2]; EXPECT_EQ(expectedOffset, retOffset); } TEST(ImageTest, givenMipMapImage1DArrayWhenAskedForPtrOffsetForGpuMappingThenReturnOffsetWithSlicePitch) { MockContext ctx; cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D_ARRAY; imageDesc.image_width = 5; imageDesc.image_array_size = 5; imageDesc.num_mip_levels = 2; std::unique_ptr image(ImageHelper::create(&ctx, &imageDesc)); EXPECT_FALSE(image->mappingOnCpuAllowed()); MemObjOffsetArray origin{{1, 1, 0}}; auto retOffset = image->calculateOffsetForMapping(origin); size_t expectedOffset = image->getSurfaceFormatInfo().ImageElementSizeInBytes * origin[0] + image->getHostPtrSlicePitch() * origin[1]; EXPECT_EQ(expectedOffset, retOffset); } TEST(ImageTest, givenNullHostPtrWhenIsCopyRequiredIsCalledThenFalseIsReturned) { ImageInfo imgInfo{}; EXPECT_FALSE(Image::isCopyRequired(imgInfo, nullptr)); } TEST(ImageTest, givenAllowedTilingWhenIsCopyRequiredIsCalledThenTrueIsReturned) { ImageInfo imgInfo{}; cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE2D; imageDesc.image_width = 1; imageDesc.image_height = 1; cl_image_format imageFormat = {}; imageFormat.image_channel_data_type = CL_UNSIGNED_INT8; imageFormat.image_channel_order = CL_R; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); imgInfo.imgDesc = &imageDesc; imgInfo.surfaceFormat = surfaceFormat; imgInfo.rowPitch = imageDesc.image_width * surfaceFormat->ImageElementSizeInBytes; imgInfo.slicePitch = imgInfo.rowPitch * imageDesc.image_height; imgInfo.size = imgInfo.slicePitch; char memory; EXPECT_TRUE(Image::isCopyRequired(imgInfo, &memory)); } TEST(ImageTest, givenDifferentRowPitchWhenIsCopyRequiredIsCalledThenTrueIsReturned) { ImageInfo imgInfo{}; cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; imageDesc.image_width = 1; imageDesc.image_height = 1; imageDesc.image_row_pitch = 10; cl_image_format imageFormat = {}; imageFormat.image_channel_data_type = CL_UNSIGNED_INT8; imageFormat.image_channel_order = CL_R; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); imgInfo.imgDesc = &imageDesc; imgInfo.surfaceFormat = surfaceFormat; imgInfo.rowPitch = imageDesc.image_width * surfaceFormat->ImageElementSizeInBytes; imgInfo.slicePitch = imgInfo.rowPitch * imageDesc.image_height; imgInfo.size = imgInfo.slicePitch; char memory[10]; EXPECT_TRUE(Image::isCopyRequired(imgInfo, memory)); } TEST(ImageTest, givenDifferentSlicePitchAndTilingNotAllowedWhenIsCopyRequiredIsCalledThenTrueIsReturned) { ImageInfo imgInfo{}; cl_image_format imageFormat = {}; imageFormat.image_channel_data_type = CL_UNSIGNED_INT8; imageFormat.image_channel_order = CL_R; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; imageDesc.image_width = 4; imageDesc.image_height = 2; imageDesc.image_slice_pitch = imageDesc.image_width * (imageDesc.image_height + 3) * surfaceFormat->ImageElementSizeInBytes; imgInfo.imgDesc = &imageDesc; imgInfo.surfaceFormat = surfaceFormat; imgInfo.rowPitch = imageDesc.image_width * surfaceFormat->ImageElementSizeInBytes; imgInfo.slicePitch = imgInfo.rowPitch * imageDesc.image_height; imgInfo.size = imgInfo.slicePitch; char memory[8]; EXPECT_TRUE(Image::isCopyRequired(imgInfo, memory)); } TEST(ImageTest, givenNotCachelinAlignedPointerWhenIsCopyRequiredIsCalledThenTrueIsReturned) { ImageInfo imgInfo{}; cl_image_format imageFormat = {}; imageFormat.image_channel_data_type = CL_UNSIGNED_INT8; imageFormat.image_channel_order = CL_R; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; imageDesc.image_width = 4096; imageDesc.image_height = 1; imgInfo.imgDesc = &imageDesc; imgInfo.surfaceFormat = surfaceFormat; imgInfo.rowPitch = imageDesc.image_width * surfaceFormat->ImageElementSizeInBytes; imgInfo.slicePitch = imgInfo.rowPitch * imageDesc.image_height; imgInfo.size = imgInfo.slicePitch; char memory[8]; EXPECT_TRUE(Image::isCopyRequired(imgInfo, &memory[1])); } TEST(ImageTest, givenCachelineAlignedPointerAndProperDescriptorValuesWhenIsCopyRequiredIsCalledThenFalseIsReturned) { ImageInfo imgInfo{}; cl_image_format imageFormat = {}; imageFormat.image_channel_data_type = CL_UNSIGNED_INT8; imageFormat.image_channel_order = CL_R; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; imageDesc.image_width = 2; imageDesc.image_height = 1; imgInfo.imgDesc = &imageDesc; imgInfo.surfaceFormat = surfaceFormat; imgInfo.rowPitch = imageDesc.image_width * surfaceFormat->ImageElementSizeInBytes; imgInfo.slicePitch = imgInfo.rowPitch * imageDesc.image_height; imgInfo.size = imgInfo.slicePitch; auto hostPtr = alignedMalloc(imgInfo.size, MemoryConstants::cacheLineSize); EXPECT_FALSE(Image::isCopyRequired(imgInfo, hostPtr)); alignedFree(hostPtr); } TEST(ImageTest, givenForcedLinearImages3DImageAndProperDescriptorValuesWhenIsCopyRequiredIsCalledThenFalseIsReturned) { DebugManagerStateRestore dbgRestorer; DebugManager.flags.ForceLinearImages.set(true); ImageInfo imgInfo{}; cl_image_format imageFormat = {}; imageFormat.image_channel_data_type = CL_UNSIGNED_INT8; imageFormat.image_channel_order = CL_R; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE3D; imageDesc.image_width = 2; imageDesc.image_height = 2; imageDesc.image_depth = 2; imgInfo.imgDesc = &imageDesc; imgInfo.surfaceFormat = surfaceFormat; imgInfo.rowPitch = imageDesc.image_width * surfaceFormat->ImageElementSizeInBytes; imgInfo.slicePitch = imgInfo.rowPitch * imageDesc.image_height; imgInfo.size = imgInfo.slicePitch; auto hostPtr = alignedMalloc(imgInfo.size, MemoryConstants::cacheLineSize); EXPECT_FALSE(Image::isCopyRequired(imgInfo, hostPtr)); alignedFree(hostPtr); } TEST(ImageTest, givenClMemCopyHostPointerPassedToImageCreateWhenAllocationIsNotInSystemMemoryPoolThenAllocationIsWrittenByEnqueueWriteImage) { ExecutionEnvironment executionEnvironment; executionEnvironment.incRefInternal(); auto *memoryManager = new ::testing::NiceMock(executionEnvironment); executionEnvironment.memoryManager.reset(memoryManager); EXPECT_CALL(*memoryManager, allocateGraphicsMemoryInDevicePool(::testing::_, ::testing::_)) .WillRepeatedly(::testing::Invoke(memoryManager, &GMockMemoryManagerFailFirstAllocation::baseAllocateGraphicsMemoryInDevicePool)); std::unique_ptr device(MockDevice::create(*platformDevices, &executionEnvironment, 0)); MockContext ctx(device.get()); EXPECT_CALL(*memoryManager, allocateGraphicsMemoryInDevicePool(::testing::_, ::testing::_)) .WillOnce(::testing::Invoke(memoryManager, &GMockMemoryManagerFailFirstAllocation::allocateNonSystemGraphicsMemoryInDevicePool)) .WillRepeatedly(::testing::Invoke(memoryManager, &GMockMemoryManagerFailFirstAllocation::baseAllocateGraphicsMemoryInDevicePool)); char memory[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; auto taskCount = device->getCommandStreamReceiver().peekLatestFlushedTaskCount(); cl_int retVal = 0; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR; cl_image_desc imageDesc{}; imageDesc.image_type = CL_MEM_OBJECT_IMAGE1D; imageDesc.image_width = 1; imageDesc.image_height = 1; imageDesc.image_row_pitch = sizeof(memory); cl_image_format imageFormat = {}; imageFormat.image_channel_data_type = CL_UNSIGNED_INT8; imageFormat.image_channel_order = CL_R; auto surfaceFormat = Image::getSurfaceFormatFromTable(flags, &imageFormat); std::unique_ptr image(Image::create(&ctx, flags, surfaceFormat, &imageDesc, memory, retVal)); EXPECT_NE(nullptr, image); auto taskCountSent = device->getCommandStreamReceiver().peekLatestFlushedTaskCount(); EXPECT_LT(taskCount, taskCountSent); } typedef ::testing::TestWithParam MipLevelCoordinateTest; TEST_P(MipLevelCoordinateTest, givenMipmappedImageWhenValidateRegionAndOriginIsCalledThenAdditionalOriginCoordinateIsAnalyzed) { size_t origin[4]{}; size_t region[3]{1, 1, 1}; cl_image_desc desc = {}; desc.image_type = GetParam(); desc.num_mip_levels = 2; origin[getMipLevelOriginIdx(desc.image_type)] = 1; EXPECT_EQ(CL_SUCCESS, Image::validateRegionAndOrigin(origin, region, desc)); origin[getMipLevelOriginIdx(desc.image_type)] = 2; EXPECT_EQ(CL_INVALID_MIP_LEVEL, Image::validateRegionAndOrigin(origin, region, desc)); } INSTANTIATE_TEST_CASE_P(MipLevelCoordinate, MipLevelCoordinateTest, ::testing::Values(CL_MEM_OBJECT_IMAGE1D, CL_MEM_OBJECT_IMAGE1D_ARRAY, CL_MEM_OBJECT_IMAGE2D, CL_MEM_OBJECT_IMAGE2D_ARRAY, CL_MEM_OBJECT_IMAGE3D)); typedef ::testing::TestWithParam> HasSlicesTest; TEST_P(HasSlicesTest, givenMemObjectTypeWhenHasSlicesIsCalledThenReturnsTrueIfTypeDefinesObjectWithSlicePitch) { auto pair = GetParam(); EXPECT_EQ(pair.second, Image::hasSlices(pair.first)); } INSTANTIATE_TEST_CASE_P(HasSlices, HasSlicesTest, ::testing::Values(std::make_pair(CL_MEM_OBJECT_IMAGE1D, false), std::make_pair(CL_MEM_OBJECT_IMAGE1D_ARRAY, true), std::make_pair(CL_MEM_OBJECT_IMAGE2D, false), std::make_pair(CL_MEM_OBJECT_IMAGE2D_ARRAY, true), std::make_pair(CL_MEM_OBJECT_IMAGE3D, true), std::make_pair(CL_MEM_OBJECT_BUFFER, false), std::make_pair(CL_MEM_OBJECT_PIPE, false))); typedef ::testing::Test ImageTransformTest; HWTEST_F(ImageTransformTest, givenSurfaceStateWhenTransformImage3dTo2dArrayIsCalledThenSurface2dArrayIsSet) { using RENDER_SURFACE_STATE = typename FamilyType::RENDER_SURFACE_STATE; using SURFACE_TYPE = typename RENDER_SURFACE_STATE::SURFACE_TYPE; MockContext context; auto image = std::unique_ptr(ImageHelper::create(&context)); auto surfaceState = FamilyType::cmdInitRenderSurfaceState; auto imageHw = static_cast *>(image.get()); surfaceState.setSurfaceType(SURFACE_TYPE::SURFACE_TYPE_SURFTYPE_3D); surfaceState.setSurfaceArray(false); imageHw->transformImage3dTo2dArray(reinterpret_cast(&surfaceState)); EXPECT_EQ(SURFACE_TYPE::SURFACE_TYPE_SURFTYPE_2D, surfaceState.getSurfaceType()); EXPECT_TRUE(surfaceState.getSurfaceArray()); } HWTEST_F(ImageTransformTest, givenSurfaceStateWhenTransformImage2dArrayTo3dIsCalledThenSurface3dIsSet) { using RENDER_SURFACE_STATE = typename FamilyType::RENDER_SURFACE_STATE; using SURFACE_TYPE = typename RENDER_SURFACE_STATE::SURFACE_TYPE; MockContext context; auto image = std::unique_ptr(ImageHelper::create(&context)); auto surfaceState = FamilyType::cmdInitRenderSurfaceState; auto imageHw = static_cast *>(image.get()); surfaceState.setSurfaceType(SURFACE_TYPE::SURFACE_TYPE_SURFTYPE_2D); surfaceState.setSurfaceArray(true); imageHw->transformImage2dArrayTo3d(reinterpret_cast(&surfaceState)); EXPECT_EQ(SURFACE_TYPE::SURFACE_TYPE_SURFTYPE_3D, surfaceState.getSurfaceType()); EXPECT_FALSE(surfaceState.getSurfaceArray()); } HWTEST_F(ImageTransformTest, givenSurfaceBaseAddressAndUnifiedSurfaceWhenSetUnifiedAuxAddressCalledThenAddressIsSet) { using RENDER_SURFACE_STATE = typename FamilyType::RENDER_SURFACE_STATE; using SURFACE_TYPE = typename RENDER_SURFACE_STATE::SURFACE_TYPE; MockContext context; auto image = std::unique_ptr(ImageHelper::create(&context)); auto surfaceState = FamilyType::cmdInitRenderSurfaceState; auto imageHw = static_cast *>(image.get()); auto gmm = std::unique_ptr(new Gmm(nullptr, 1, false)); uint64_t surfBsaseAddress = 0xABCDEF1000; surfaceState.setSurfaceBaseAddress(surfBsaseAddress); auto mockResource = reinterpret_cast(gmm->gmmResourceInfo.get()); mockResource->setUnifiedAuxTranslationCapable(); EXPECT_EQ(0u, surfaceState.getAuxiliarySurfaceBaseAddress()); imageHw->setUnifiedAuxBaseAddress(&surfaceState, gmm.get()); uint64_t offset = gmm->gmmResourceInfo->getUnifiedAuxSurfaceOffset(GMM_UNIFIED_AUX_TYPE::GMM_AUX_SURF); EXPECT_EQ(surfBsaseAddress + offset, surfaceState.getAuxiliarySurfaceBaseAddress()); } template class MockImageHw : public ImageHw { public: MockImageHw(Context *context, const cl_image_format &format, const cl_image_desc &desc, SurfaceFormatInfo &surfaceFormatInfo, GraphicsAllocation *graphicsAllocation) : ImageHw(context, 0, 0, nullptr, format, desc, false, graphicsAllocation, false, false, 0, 0, surfaceFormatInfo) { } void setClearColorParams(typename FamilyName::RENDER_SURFACE_STATE *surfaceState, const Gmm *gmm) override; void setAuxParamsForMCSCCS(typename FamilyName::RENDER_SURFACE_STATE *surfaceState, Gmm *gmm) override; bool setClearColorParamsCalled = false; bool setAuxParamsForMCSCCSCalled = false; }; template void MockImageHw::setClearColorParams(typename FamilyName::RENDER_SURFACE_STATE *surfaceState, const Gmm *gmm) { this->setClearColorParamsCalled = true; } template void MockImageHw::setAuxParamsForMCSCCS(typename FamilyName::RENDER_SURFACE_STATE *surfaceState, Gmm *gmm) { this->setAuxParamsForMCSCCSCalled = true; } using HwImageTest = ::testing::Test; HWTEST_F(HwImageTest, givenImageHwWhenSettingCCSParamsThenSetClearColorParamsIsCalled) { MockContext context; OsAgnosticMemoryManager memoryManager(false, false, *context.getDevice(0)->getExecutionEnvironment()); context.setMemoryManager(&memoryManager); cl_image_desc imgDesc = {}; imgDesc.image_height = 4; imgDesc.image_width = 4; imgDesc.image_depth = 1; imgDesc.image_type = CL_MEM_OBJECT_IMAGE2D; cl_image_format format = {}; format.image_channel_data_type = CL_UNORM_INT8; format.image_channel_order = CL_RGBA; auto imgInfo = MockGmm::initImgInfo(imgDesc, 0, nullptr); AllocationProperties allocProperties = MemObjHelper::getAllocationProperties(&imgInfo, true); DevicesBitfield devices = 0; auto graphicsAllocation = memoryManager.allocateGraphicsMemoryInPreferredPool(allocProperties, devices, nullptr); SurfaceFormatInfo formatInfo = {}; std::unique_ptr> mockImage(new MockImageHw(&context, format, imgDesc, formatInfo, graphicsAllocation)); typedef typename FamilyType::RENDER_SURFACE_STATE RENDER_SURFACE_STATE; auto surfaceState = FamilyType::cmdInitRenderSurfaceState; EXPECT_FALSE(mockImage->setClearColorParamsCalled); mockImage->setAuxParamsForCCS(&surfaceState, graphicsAllocation->gmm); EXPECT_TRUE(mockImage->setClearColorParamsCalled); } using HwImageTest = ::testing::Test; HWTEST_F(HwImageTest, givenImageHwWithUnifiedSurfaceAndMcsWhenSettingParamsForMultisampleImageThenSetParamsForCcsMcsIsCalled) { MockContext context; OsAgnosticMemoryManager memoryManager(false, false, *context.getDevice(0)->getExecutionEnvironment()); context.setMemoryManager(&memoryManager); cl_image_desc imgDesc = {}; imgDesc.num_samples = 8; cl_image_format format = {}; auto imgInfo = MockGmm::initImgInfo(imgDesc, 0, nullptr); AllocationProperties allocProperties = MemObjHelper::getAllocationProperties(&imgInfo, true); DevicesBitfield devices = 0; auto graphicsAllocation = memoryManager.allocateGraphicsMemoryInPreferredPool(allocProperties, devices, nullptr); SurfaceFormatInfo formatInfo = {}; std::unique_ptr> mockImage(new MockImageHw(&context, format, imgDesc, formatInfo, graphicsAllocation)); McsSurfaceInfo msi = {10, 20, 3}; auto mcsAlloc = context.getMemoryManager()->allocateGraphicsMemoryWithProperties(MockAllocationProperties{MemoryConstants::pageSize}); mcsAlloc->gmm = new Gmm(nullptr, 1, false); auto mockMcsGmmResInfo = reinterpret_cast<::testing::NiceMock *>(mcsAlloc->gmm->gmmResourceInfo.get()); mockMcsGmmResInfo->setUnifiedAuxTranslationCapable(); mockMcsGmmResInfo->setMultisampleControlSurface(); EXPECT_TRUE(mcsAlloc->gmm->unifiedAuxTranslationCapable()); EXPECT_TRUE(mcsAlloc->gmm->hasMultisampleControlSurface()); mockImage->setMcsSurfaceInfo(msi); mockImage->setMcsAllocation(mcsAlloc); typedef typename FamilyType::RENDER_SURFACE_STATE RENDER_SURFACE_STATE; auto surfaceState = FamilyType::cmdInitRenderSurfaceState; EXPECT_FALSE(mockImage->setAuxParamsForMCSCCSCalled); mockImage->setAuxParamsForMultisamples(&surfaceState); EXPECT_TRUE(mockImage->setAuxParamsForMCSCCSCalled); }