/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ #pragma clang diagnostic ignored "-Wunused-parameter" #include #include #include #include #include #pragma clang diagnostic ignored "-Wunused-variable" #pragma clang diagnostic ignored "-Wunused-parameter" // #define TEST_TEXTURE // Only for float2 static constexpr bool printLog = false; // Print log for debugging /** * @addtogroup hipCreateTextureObject hipCreateTextureObject * @{ * @ingroup TextureTest */ // Populate mipmap next level array template static __global__ void populateMipmapNextLevelArray(hipSurfaceObject_t surfOut, hipTextureObject_t texIn, unsigned int width, unsigned int height, T* data = nullptr) { #if !__HIP_NO_IMAGE_SUPPORT int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; float px = 1.0 / float(width); float py = 1.0 / float(height); if (x < width && y < height) { if constexpr (readMode == hipReadModeElementType) { T t = tex2D(texIn, x * px, y * py); #ifdef TEST_TEXTURE printf("(%d/%u, %d/%u):(%f, %f)\n", x, width, y, height, t.x, t.y); #endif surf2Dwrite(t, surfOut, x * sizeof(T), y); if (data) data[y * width + x] = t; // record it for later verification } if constexpr (readMode == hipReadModeNormalizedFloat) { float4 t = tex2D(texIn, x * px, y * py); T tc = getTypeFromNormalizedFloat(t); surf2Dwrite(tc, surfOut, x * sizeof(T), y); if (data) data[y * width + x] = tc; } // Users have freedom to use other methods to init level array // for example, use averge of surrounding pixes } #endif } template static __global__ void getMipmap(hipTextureObject_t texMipmap, unsigned int width, unsigned int height, float offsetX, float offsetY, float lod, T* data = nullptr) { #if !__HIP_NO_IMAGE_SUPPORT int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; float px = 1.0 / float(width); float py = 1.0 / float(height); if (x < width && y < height) { T t = tex2DLod(texMipmap, (x + offsetX) * px, (y + offsetY) * py, lod); if (data) data[y * width + x] = t; } #endif } template static void populateMipmaps(hipMipmappedArray_t mipmapArray, hipExtent size, std::vector>& mipmapData) { size_t width = size.width; size_t height = size.height; unsigned int level = 0; while (width != 1 || height != 1) { hipArray_t levelArray = nullptr, nextLevelArray = nullptr; HIP_CHECK(hipGetMipmappedArrayLevel(&levelArray, mipmapArray, level)); HIP_CHECK(hipGetMipmappedArrayLevel(&nextLevelArray, mipmapArray, level + 1)); hipExtent levelArraySize{0, 0, 0}; HIP_CHECK(hipArrayGetInfo(nullptr, &levelArraySize, nullptr, levelArray)); if (levelArraySize.width != width || levelArraySize.height != height) { fprintf(stderr, "Level %u: size (%zu, %zu, %zu) != Expected size (%zu, %zu, 0)\n", level, levelArraySize.width, levelArraySize.height, levelArraySize.depth, width, height); REQUIRE(false); } width = width >> 1 ? width >> 1 : 1; height = height >> 1 ? height >> 1 : 1; hipExtent nextLevelArraySize{0, 0, 0}; HIP_CHECK(hipArrayGetInfo(nullptr, &nextLevelArraySize, nullptr, nextLevelArray)); if (nextLevelArraySize.width != width || nextLevelArraySize.height != height) { fprintf(stderr, "Next level %u: size (%zu, %zu, %zu) != Expected size (%zu, %zu, 0)\n", level + 1, nextLevelArraySize.width, nextLevelArraySize.height, nextLevelArraySize.depth, width, height); REQUIRE(false); } hipTextureObject_t texIn; hipResourceDesc texRes; memset(&texRes, 0, sizeof(hipResourceDesc)); texRes.resType = hipResourceTypeArray; texRes.res.array.array = levelArray; hipTextureDesc texDescr; memset(&texDescr, 0, sizeof(hipTextureDesc)); texDescr.normalizedCoords = 1; // To populate next level array smoothly texDescr.filterMode = filterMode; texDescr.addressMode[0] = addressMode; texDescr.addressMode[1] = addressMode; texDescr.addressMode[2] = addressMode; texDescr.readMode = readMode; HIP_CHECK(hipCreateTextureObject(&texIn, &texRes, &texDescr, NULL)); hipSurfaceObject_t surfOut; hipResourceDesc surfRes; memset(&surfRes, 0, sizeof(hipResourceDesc)); surfRes.resType = hipResourceTypeArray; surfRes.res.array.array = nextLevelArray; HIP_CHECK(hipCreateSurfaceObject(&surfOut, &surfRes)); size_t size = width * height * sizeof(T); mipmapLevelArray data{nullptr, {width, height, 0}}; HIP_CHECK(hipHostMalloc((void**)&data.data, size)); memset(data.data, 0, size); dim3 blockSize(16, 16, 1); dim3 gridSize((width + blockSize.x - 1) / blockSize.x, (height + blockSize.y - 1) / blockSize.y, 1); populateMipmapNextLevelArray <<>>(surfOut, texIn, width, height, data.data); HIP_CHECK(hipDeviceSynchronize()); HIP_CHECK(hipGetLastError()); HIP_CHECK(hipDestroySurfaceObject(surfOut)); HIP_CHECK(hipDestroyTextureObject(texIn)); HIP_CHECK(hipFreeArray(levelArray)); HIP_CHECK(hipFreeArray(nextLevelArray)); mipmapData.push_back(data); // For later verification level++; } } template static void verifyMipmapLevel(hipTextureObject_t texMipmap, T* data, size_t width, size_t height, float level, float offsetX, float offsetY) { T* hOutput = nullptr; size_t size = width * height * sizeof(T); HIP_CHECK(hipHostMalloc((void**)&hOutput, size)); memset(hOutput, 0, size); dim3 blockSize(16, 16, 1); dim3 gridSize((width + blockSize.x - 1) / blockSize.x, (height + blockSize.y - 1) / blockSize.y, 1); getMipmap<<>>(texMipmap, width, height, offsetX, offsetY, level, hOutput); HIP_CHECK(hipDeviceSynchronize()); HIP_CHECK(hipGetLastError()); for (unsigned int j = 0; j < height; j++) { for (unsigned int i = 0; i < width; i++) { unsigned int index = j * width + i; T cpuExpected = getExpectedValue( width, height, i + offsetX, j + offsetY, data); T gpuOutput = hOutput[index]; bool match = hipTextureSamplingVerify(gpuOutput, cpuExpected); if (!match) { WARN("Mismatch at (level " << level << ": " << i << ", " << j << " -> " << (i + offsetX) << ", " << (j + offsetY) << ") GPU output : " << getString(gpuOutput) << " CPU expected: " << getString(cpuExpected) << ", data[" << index << "]:" << getString(data[index]) << "\n"); REQUIRE(false); } else if (printLog) { WARN("Matching at (level " << level << ": " << i << ", " << j << " -> " << (i + offsetX) << ", " << (j + offsetY) << ") GPU output : " << getString(gpuOutput) << " CPU expected: " << getString(cpuExpected) << ", data[" << index << "]:" << getString(data[index]) << "\n"); } } } HIP_CHECK(hipHostFree(hOutput)); } template static void testMipmapTextureObj(size_t width, size_t height, float offsetX = 0., float offsetY = 0.) { std::vector> mipmapData; size_t size = width * height * sizeof(T); mipmapLevelArray data{nullptr, {width, height, 0}}; HIP_CHECK(hipHostMalloc((void**)&data.data, size)); memset(data.data, 0, size); for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { if (isFloat() && filterMode == hipFilterModeLinear) { /* * For linear sampling of images, the GPU does not use IEEE floating point types, it uses * lower-precision sampling optimized formats. Also, those formats often change between GPU * generations. So counting on IEEE precision and accuracy when doing linear sampling * is mistaken. To workaround this issue, we can initialize float pixels on a retively * smoothy surface. */ data.data[j * width + i] = T(float(i) * (float(i) - width + 1) * float(j) * (float(j) - height + 1)); } else { initVal(data.data[j * width + i]); // Randomize initial values } } } mipmapData.push_back(data); // record level 0 data for later verification // Get the max mipmap levels in terms of image size const unsigned int maxLevels = 1 + std::log2(std::max(width, height)); // create mipmap array hipChannelFormatDesc desc = hipCreateChannelDesc(); hipMipmappedArray_t mipmapArray = nullptr; hipExtent extent{width, height, 0}; HIP_CHECK(hipMallocMipmappedArray(&mipmapArray, &desc, extent, maxLevels)); // Initialize level 0 hipArray_t levelArray; HIP_CHECK(hipGetMipmappedArrayLevel(&levelArray, mipmapArray, 0)); hipMemcpy3DParms copyParams{}; copyParams.srcPtr = make_hipPitchedPtr(data.data, width * sizeof(T), width, height); copyParams.dstArray = levelArray; copyParams.extent.width = width; copyParams.extent.height = height; copyParams.extent.depth = 1; copyParams.kind = hipMemcpyHostToDevice; HIP_CHECK(hipMemcpy3D(©Params)); // Populate other mipmap levels based on level 0 populateMipmaps(mipmapArray, extent, mipmapData); if (maxLevels != mipmapData.size()) { fprintf(stderr, "maxLevels %u != mipmapData.size() %zu\n", maxLevels, mipmapData.size()); REQUIRE(false); } hipResourceDesc resDescr; memset(&resDescr, 0, sizeof(hipResourceDesc)); resDescr.resType = hipResourceTypeMipmappedArray; // For mipmap texture resDescr.res.mipmap.mipmap = mipmapArray; hipTextureDesc texDescr; memset(&texDescr, 0, sizeof(hipTextureDesc)); texDescr.normalizedCoords = 1; // normalizedCoords must be 1 for mipmap array texDescr.filterMode = filterMode; texDescr.mipmapFilterMode = filterMode; texDescr.addressMode[0] = addressMode; texDescr.addressMode[1] = addressMode; texDescr.addressMode[2] = addressMode; // Ignored in AMD Hw sampler SRD, but cuda need it texDescr.maxMipmapLevelClamp = float(maxLevels - 1); texDescr.readMode = readMode; hipTextureObject_t texMipmap = nullptr; HIP_CHECK(hipCreateTextureObject(&texMipmap, &resDescr, &texDescr, NULL)); for (unsigned int level = 0; level < mipmapData.size(); level++) { mipmapLevelArray& data = mipmapData.at(level); if constexpr (hipReadModeNormalizedFloat == readMode) { typedef decltype(getNormalizedFloatType(*data.data)) TargetType; std::vector fData; fData.resize(data.e.width * data.e.height); for (unsigned int j = 0; j < data.e.height; j++) { for (unsigned int i = 0; i < data.e.width; i++) { unsigned int index = j * data.e.width + i; fData[index] = getNormalizedFloatType(data.data[index]); } } verifyMipmapLevel( texMipmap, fData.data(), data.e.width, data.e.height, level, offsetX, offsetY); } else { // hipReadModeElementType == readMode verifyMipmapLevel(texMipmap, data.data, data.e.width, data.e.height, level, offsetX, offsetY); } HIP_CHECK(hipHostFree(data.data)); memset(&data, 0, sizeof(data)); } HIP_CHECK(hipDestroyTextureObject(texMipmap)); HIP_CHECK(hipFreeMipmappedArray(mipmapArray)); } /** * Test Description * ------------------------ * - The suite will test following functions with hipReadModeElementType and hipFilterModePoint in 2D creating MipMap array, getting level array, creating/initilizing texture and surface objects on level array, creating texture object on the mipmap array, verifing the texture object * Test source * ------------------------ * - catch\unit\texture\hipTextureMipmapObj2D.cc * Test requirements * ------------------------ * - Host specific (WINDOWS) * - Textures supported on device * - HIP_VERSION >= 5.7 */ HIP_TEMPLATE_TEST_CASE(Unit_hipTextureMipmapObj2D_Check_hipReadModeElementType, char, uchar, short, ushort, int, uint, float, char1, uchar1, short1, ushort1, int1, uint1, float1, char2, uchar2, short2, ushort2, int2, uint2, float2, char4, uchar4, short4, ushort4, int4, uint4, float4) { CHECK_IMAGE_SUPPORT SECTION( "hipTextureMipmapObj2D_Check - hipReadModeElementType, hipFilterModePoint, " "hipAddressModeClamp 23, 21") { testMipmapTextureObj( 23, 21, 0.4, -0.9); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeElementType, hipFilterModePoint, " "hipAddressModeClamp 67, 131") { testMipmapTextureObj( 67, 131, -0.3, -0.67); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeElementType, hipFilterModePoint, " "hipAddressModeBorder 131, 263") { testMipmapTextureObj(131, 263, 0.15, -0.34); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeElementType, hipFilterModePoint, " "hipAddressModeBorder 263, 67") { testMipmapTextureObj(263, 67, 0.13, 0.96); } } /** * Test Description * ------------------------ * - The suite will test following functions with hipReadModeNormalizedFloat on integer types in 2D creating MipMap array, getting level array, creating/initilizing texture and surface objects on level array, creating texture object on the mipmap array, verifing the texture object * Test source * ------------------------ * - catch\unit\texture\hipTextureMipmapObj2D.cc * Test requirements * ------------------------ * - Host specific (WINDOWS) * - Textures supported on device * - HIP_VERSION >= 5.7 */ HIP_TEMPLATE_TEST_CASE(Unit_hipTextureMipmapObj2D_Check_hipReadModeNormalizedFloat, char, uchar, short, ushort, char1, uchar1, short1, ushort1, char2, uchar2, short2, ushort2, char4, uchar4, short4, ushort4) { CHECK_IMAGE_SUPPORT SECTION( "hipTextureMipmapObj2D_Check - hipReadModeNormalizedFloat, hipFilterModePoint, " "hipAddressModeClamp 23, 21") { testMipmapTextureObj(23, 21, 0.4, -0.9); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeNormalizedFloat, hipFilterModePoint, " "hipAddressModeClamp 131, 263") { testMipmapTextureObj(131, 263, 0.15, -0.34); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeNormalizedFloat, hipFilterModeLinear, " "hipAddressModeClamp 67, 131") { testMipmapTextureObj(23, 17, -0.3, -0.67); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeNormalizedFloat, hipFilterModeLinear, " "hipAddressModeClamp 263, 67") { testMipmapTextureObj(263, 67, 0.13, 0.96); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeNormalizedFloat, hipFilterModePoint, " "hipAddressModeBorder 131, 263") { testMipmapTextureObj(131, 263, 0.15, -0.34); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeNormalizedFloat, hipFilterModePoint, " "hipAddressModeBorder 23, 21") { testMipmapTextureObj(23, 21, 0.4, -0.9); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeNormalizedFloat, hipFilterModeLinear, " "hipAddressModeBorder 263, 67") { testMipmapTextureObj(263, 67, 0.13, 0.96); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeNormalizedFloat, hipFilterModeLinear, " "hipAddressModeBorder 67, 131") { testMipmapTextureObj(67, 131, -0.3, -0.67); } } /** * Test Description * ------------------------ * - The suite will test following functions with hipReadModeElementType and hipFilterModeLinear on float types in 2D, creating MipMap array, getting level array, creating/initilizing texture and surface objects on level array, creating texture object on the mipmap array, verifing the texture object * Test source * ------------------------ * - catch\unit\texture\hipTextureMipmapObj2D.cc * Test requirements * ------------------------ * - Host specific (WINDOWS) * - Textures supported on device * - HIP_VERSION >= 5.7 */ HIP_TEMPLATE_TEST_CASE(Unit_hipTextureMipmapObj2D_Check_hipReadModeElementType_float_only, float, float1, float2, float4) { CHECK_IMAGE_SUPPORT SECTION( "hipTextureMipmapObj2D_Check - hipReadModeElementType, hipFilterModeLinear, " "hipAddressModeClamp 23, 17, 0., 0.") { testMipmapTextureObj(23, 17, 0.79, 0.37); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeElementType, hipFilterModeLinear, " "hipAddressModeClamp 23, 17") { testMipmapTextureObj(23, 17, -0.3, -0.67); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeElementType, hipFilterModeLinear, " "hipAddressModeClamp 263, 67") { testMipmapTextureObj(263, 67, 0.13, 0.96); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeElementType, hipFilterModeLinear, " "hipAddressModeBorder 263, 67") { testMipmapTextureObj(263, 67, 0.13, 0.96); } SECTION( "hipTextureMipmapObj2D_Check - hipReadModeElementType, hipFilterModeLinear, " "hipAddressModeBorder 67, 131") { testMipmapTextureObj(67, 131, -0.3, -0.67); } } /** * End doxygen group TextureTest. * @} */