// // Copyright (c) 2017 The Khronos Group Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef _imageHelpers_h #define _imageHelpers_h #include "compat.h" #include #include #include #include #include #include #if !defined(_WIN32) #include #endif #include #include "errorHelpers.h" #include "conversions.h" #include "typeWrappers.h" #include "kernelHelpers.h" #include "errorHelpers.h" #include "mt19937.h" #include "rounding_mode.h" #include "clImageHelper.h" #include extern cl_device_type gDeviceType; extern bool gTestRounding; // Number of iterations per image format to test if not testing max images, // rounding, or small images #define NUM_IMAGE_ITERATIONS 3 // Definition for our own sampler type, to mirror the cl_sampler internals #define MAX_sRGB_TO_lRGB_CONVERSION_ERROR 0.5 #define MAX_lRGB_TO_sRGB_CONVERSION_ERROR 0.6 // Definition for our own sampler type, to mirror the cl_sampler internals typedef struct { cl_addressing_mode addressing_mode; cl_filter_mode filter_mode; bool normalized_coords; } image_sampler_data; int round_to_even(float v); #define NORMALIZE(v, max) (v < 0 ? 0 : (v > 1.f ? max : round_to_even(v * max))) #define NORMALIZE_UNROUNDED(v, max) (v < 0 ? 0 : (v > 1.f ? max : v * max)) #define NORMALIZE_SIGNED(v, min, max) \ (v < -1.0f ? min : (v > 1.f ? max : round_to_even(v * max))) #define NORMALIZE_SIGNED_UNROUNDED(v, min, max) \ (v < -1.0f ? min : (v > 1.f ? max : v * max)) #define CONVERT_INT(v, min, max, max_val) \ (v < min ? min : (v > max ? max_val : round_to_even(v))) #define CONVERT_UINT(v, max, max_val) \ (v < 0 ? 0 : (v > max ? max_val : round_to_even(v))) extern void print_read_header(const cl_image_format *format, image_sampler_data *sampler, bool err = false, int t = 0); extern void print_write_header(const cl_image_format *format, bool err); extern void print_header(const cl_image_format *format, bool err); extern bool find_format(cl_image_format *formatList, unsigned int numFormats, cl_image_format *formatToFind); extern bool is_image_format_required(cl_image_format format, cl_mem_flags flags, cl_mem_object_type image_type, cl_device_id device); extern void build_required_image_formats(cl_mem_flags flags, cl_mem_object_type image_type, cl_device_id device, std::vector &formatsToSupport); extern uint32_t get_format_type_size(const cl_image_format *format); extern uint32_t get_channel_data_type_size(cl_channel_type channelType); extern uint32_t get_format_channel_count(const cl_image_format *format); extern uint32_t get_channel_order_channel_count(cl_channel_order order); cl_channel_type get_channel_type_from_name(const char *name); cl_channel_order get_channel_order_from_name(const char *name); extern int is_format_signed(const cl_image_format *format); extern uint32_t get_pixel_size(const cl_image_format *format); /* Helper to get any ol image format as long as it is 8-bits-per-channel */ extern int get_8_bit_image_format(cl_context context, cl_mem_object_type objType, cl_mem_flags flags, size_t channelCount, cl_image_format *outFormat); /* Helper to get any ol image format as long as it is 32-bits-per-channel */ extern int get_32_bit_image_format(cl_context context, cl_mem_object_type objType, cl_mem_flags flags, size_t channelCount, cl_image_format *outFormat); int random_in_range(int minV, int maxV, MTdata d); int random_log_in_range(int minV, int maxV, MTdata d); typedef struct { size_t width; size_t height; size_t depth; size_t rowPitch; size_t slicePitch; size_t arraySize; const cl_image_format *format; cl_mem buffer; cl_mem_object_type type; cl_uint num_mip_levels; } image_descriptor; typedef struct { float p[4]; } FloatPixel; void print_first_pixel_difference_error(size_t where, const char *sourcePixel, const char *destPixel, image_descriptor *imageInfo, size_t y, size_t thirdDim); void get_max_sizes(size_t *numberOfSizes, const int maxNumberOfSizes, size_t sizes[][3], size_t maxWidth, size_t maxHeight, size_t maxDepth, size_t maxArraySize, const cl_ulong maxIndividualAllocSize, const cl_ulong maxTotalAllocSize, cl_mem_object_type image_type, const cl_image_format *format, int usingMaxPixelSize = 0); extern size_t get_format_max_int(const cl_image_format *format); extern cl_ulong get_image_size(image_descriptor const *imageInfo); extern cl_ulong get_image_size_mb(image_descriptor const *imageInfo); extern char *generate_random_image_data(image_descriptor *imageInfo, BufferOwningPtr &Owner, MTdata d); extern int debug_find_vector_in_image(void *imagePtr, image_descriptor *imageInfo, void *vectorToFind, size_t vectorSize, int *outX, int *outY, int *outZ, size_t lod = 0); extern int debug_find_pixel_in_image(void *imagePtr, image_descriptor *imageInfo, unsigned int *valuesToFind, int *outX, int *outY, int *outZ, int lod = 0); extern int debug_find_pixel_in_image(void *imagePtr, image_descriptor *imageInfo, int *valuesToFind, int *outX, int *outY, int *outZ, int lod = 0); extern int debug_find_pixel_in_image(void *imagePtr, image_descriptor *imageInfo, float *valuesToFind, int *outX, int *outY, int *outZ, int lod = 0); extern void copy_image_data(image_descriptor *srcImageInfo, image_descriptor *dstImageInfo, void *imageValues, void *destImageValues, const size_t sourcePos[], const size_t destPos[], const size_t regionSize[]); int has_alpha(const cl_image_format *format); extern bool is_sRGBA_order(cl_channel_order image_channel_order); inline float calculate_array_index(float coord, float extent); cl_uint compute_max_mip_levels(size_t width, size_t height, size_t depth); cl_ulong compute_mipmapped_image_size(image_descriptor imageInfo); size_t compute_mip_level_offset(image_descriptor *imageInfo, size_t lod); template void read_image_pixel(void *imageData, image_descriptor *imageInfo, int x, int y, int z, T *outData, int lod) { size_t width_lod = imageInfo->width, height_lod = imageInfo->height, depth_lod = imageInfo->depth, slice_pitch_lod = 0 /*imageInfo->slicePitch*/, row_pitch_lod = 0 /*imageInfo->rowPitch*/; width_lod = (imageInfo->width >> lod) ? (imageInfo->width >> lod) : 1; if (imageInfo->type != CL_MEM_OBJECT_IMAGE1D_ARRAY && imageInfo->type != CL_MEM_OBJECT_IMAGE1D) height_lod = (imageInfo->height >> lod) ? (imageInfo->height >> lod) : 1; if (imageInfo->type == CL_MEM_OBJECT_IMAGE3D) depth_lod = (imageInfo->depth >> lod) ? (imageInfo->depth >> lod) : 1; row_pitch_lod = (imageInfo->num_mip_levels > 0) ? (width_lod * get_pixel_size(imageInfo->format)) : imageInfo->rowPitch; slice_pitch_lod = (imageInfo->num_mip_levels > 0) ? (row_pitch_lod * height_lod) : imageInfo->slicePitch; // correct depth_lod and height_lod for array image types in order to avoid // return if (imageInfo->type == CL_MEM_OBJECT_IMAGE1D_ARRAY && height_lod == 1 && depth_lod == 1) { depth_lod = 0; height_lod = 0; } if (imageInfo->type == CL_MEM_OBJECT_IMAGE2D_ARRAY && depth_lod == 1) { depth_lod = 0; } if (x < 0 || x >= (int)width_lod || (height_lod != 0 && (y < 0 || y >= (int)height_lod)) || (depth_lod != 0 && (z < 0 || z >= (int)depth_lod)) || (imageInfo->arraySize != 0 && (z < 0 || z >= (int)imageInfo->arraySize))) { // Border color if (imageInfo->format->image_channel_order == CL_DEPTH) { outData[0] = 1; } else { outData[0] = outData[1] = outData[2] = outData[3] = 0; if (!has_alpha(imageInfo->format)) outData[3] = 1; } return; } const cl_image_format *format = imageInfo->format; unsigned int i; T tempData[4]; // Advance to the right spot char *ptr = (char *)imageData; size_t pixelSize = get_pixel_size(format); ptr += z * slice_pitch_lod + y * row_pitch_lod + x * pixelSize; // OpenCL only supports reading floats from certain formats switch (format->image_channel_data_type) { case CL_SNORM_INT8: { cl_char *dPtr = (cl_char *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_UNORM_INT8: { cl_uchar *dPtr = (cl_uchar *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_SIGNED_INT8: { cl_char *dPtr = (cl_char *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_UNSIGNED_INT8: { cl_uchar *dPtr = (cl_uchar *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_SNORM_INT16: { cl_short *dPtr = (cl_short *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_UNORM_INT16: { cl_ushort *dPtr = (cl_ushort *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_SIGNED_INT16: { cl_short *dPtr = (cl_short *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_UNSIGNED_INT16: { cl_ushort *dPtr = (cl_ushort *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_HALF_FLOAT: { cl_half *dPtr = (cl_half *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)cl_half_to_float(dPtr[i]); break; } case CL_SIGNED_INT32: { cl_int *dPtr = (cl_int *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_UNSIGNED_INT32: { cl_uint *dPtr = (cl_uint *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } case CL_UNORM_SHORT_565: { cl_ushort *dPtr = (cl_ushort *)ptr; tempData[0] = (T)(dPtr[0] >> 11); tempData[1] = (T)((dPtr[0] >> 5) & 63); tempData[2] = (T)(dPtr[0] & 31); break; } #ifdef OBSOLETE_FORMAT case CL_UNORM_SHORT_565_REV: { unsigned short *dPtr = (unsigned short *)ptr; tempData[2] = (T)(dPtr[0] >> 11); tempData[1] = (T)((dPtr[0] >> 5) & 63); tempData[0] = (T)(dPtr[0] & 31); break; } case CL_UNORM_SHORT_555_REV: { unsigned short *dPtr = (unsigned short *)ptr; tempData[2] = (T)((dPtr[0] >> 10) & 31); tempData[1] = (T)((dPtr[0] >> 5) & 31); tempData[0] = (T)(dPtr[0] & 31); break; } case CL_UNORM_INT_8888: { unsigned int *dPtr = (unsigned int *)ptr; tempData[3] = (T)(dPtr[0] >> 24); tempData[2] = (T)((dPtr[0] >> 16) & 0xff); tempData[1] = (T)((dPtr[0] >> 8) & 0xff); tempData[0] = (T)(dPtr[0] & 0xff); break; } case CL_UNORM_INT_8888_REV: { unsigned int *dPtr = (unsigned int *)ptr; tempData[0] = (T)(dPtr[0] >> 24); tempData[1] = (T)((dPtr[0] >> 16) & 0xff); tempData[2] = (T)((dPtr[0] >> 8) & 0xff); tempData[3] = (T)(dPtr[0] & 0xff); break; } case CL_UNORM_INT_101010_REV: { unsigned int *dPtr = (unsigned int *)ptr; tempData[2] = (T)((dPtr[0] >> 20) & 0x3ff); tempData[1] = (T)((dPtr[0] >> 10) & 0x3ff); tempData[0] = (T)(dPtr[0] & 0x3ff); break; } #endif case CL_UNORM_SHORT_555: { cl_ushort *dPtr = (cl_ushort *)ptr; tempData[0] = (T)((dPtr[0] >> 10) & 31); tempData[1] = (T)((dPtr[0] >> 5) & 31); tempData[2] = (T)(dPtr[0] & 31); break; } case CL_UNORM_INT_101010: { cl_uint *dPtr = (cl_uint *)ptr; tempData[0] = (T)((dPtr[0] >> 20) & 0x3ff); tempData[1] = (T)((dPtr[0] >> 10) & 0x3ff); tempData[2] = (T)(dPtr[0] & 0x3ff); break; } case CL_FLOAT: { cl_float *dPtr = (cl_float *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i]; break; } #ifdef CL_SFIXED14_APPLE case CL_SFIXED14_APPLE: { cl_float *dPtr = (cl_float *)ptr; for (i = 0; i < get_format_channel_count(format); i++) tempData[i] = (T)dPtr[i] + 0x4000; break; } #endif } outData[0] = outData[1] = outData[2] = 0; outData[3] = 1; if (format->image_channel_order == CL_A) { outData[3] = tempData[0]; } else if (format->image_channel_order == CL_R) { outData[0] = tempData[0]; } else if (format->image_channel_order == CL_Rx) { outData[0] = tempData[0]; } else if (format->image_channel_order == CL_RA) { outData[0] = tempData[0]; outData[3] = tempData[1]; } else if (format->image_channel_order == CL_RG) { outData[0] = tempData[0]; outData[1] = tempData[1]; } else if (format->image_channel_order == CL_RGx) { outData[0] = tempData[0]; outData[1] = tempData[1]; } else if ((format->image_channel_order == CL_RGB) || (format->image_channel_order == CL_sRGB)) { outData[0] = tempData[0]; outData[1] = tempData[1]; outData[2] = tempData[2]; } else if ((format->image_channel_order == CL_RGBx) || (format->image_channel_order == CL_sRGBx)) { outData[0] = tempData[0]; outData[1] = tempData[1]; outData[2] = tempData[2]; outData[3] = 0; } else if ((format->image_channel_order == CL_RGBA) || (format->image_channel_order == CL_sRGBA)) { outData[0] = tempData[0]; outData[1] = tempData[1]; outData[2] = tempData[2]; outData[3] = tempData[3]; } else if (format->image_channel_order == CL_ARGB) { outData[0] = tempData[1]; outData[1] = tempData[2]; outData[2] = tempData[3]; outData[3] = tempData[0]; } else if ((format->image_channel_order == CL_BGRA) || (format->image_channel_order == CL_sBGRA)) { outData[0] = tempData[2]; outData[1] = tempData[1]; outData[2] = tempData[0]; outData[3] = tempData[3]; } else if (format->image_channel_order == CL_INTENSITY) { outData[0] = tempData[0]; outData[1] = tempData[0]; outData[2] = tempData[0]; outData[3] = tempData[0]; } else if (format->image_channel_order == CL_LUMINANCE) { outData[0] = tempData[0]; outData[1] = tempData[0]; outData[2] = tempData[0]; } else if (format->image_channel_order == CL_DEPTH) { outData[0] = tempData[0]; } #ifdef CL_1RGB_APPLE else if (format->image_channel_order == CL_1RGB_APPLE) { outData[0] = tempData[1]; outData[1] = tempData[2]; outData[2] = tempData[3]; outData[3] = 0xff; } #endif #ifdef CL_BGR1_APPLE else if (format->image_channel_order == CL_BGR1_APPLE) { outData[0] = tempData[2]; outData[1] = tempData[1]; outData[2] = tempData[0]; outData[3] = 0xff; } #endif else { log_error("Invalid format:"); print_header(format, true); } } template void read_image_pixel(void *imageData, image_descriptor *imageInfo, int x, int y, int z, T *outData) { read_image_pixel(imageData, imageInfo, x, y, z, outData, 0); } // Stupid template rules bool get_integer_coords(float x, float y, float z, size_t width, size_t height, size_t depth, image_sampler_data *imageSampler, image_descriptor *imageInfo, int &outX, int &outY, int &outZ); bool get_integer_coords_offset(float x, float y, float z, float xAddressOffset, float yAddressOffset, float zAddressOffset, size_t width, size_t height, size_t depth, image_sampler_data *imageSampler, image_descriptor *imageInfo, int &outX, int &outY, int &outZ); template void sample_image_pixel_offset(void *imageData, image_descriptor *imageInfo, float x, float y, float z, float xAddressOffset, float yAddressOffset, float zAddressOffset, image_sampler_data *imageSampler, T *outData, int lod) { int iX = 0, iY = 0, iZ = 0; float max_w = imageInfo->width; float max_h; float max_d; switch (imageInfo->type) { case CL_MEM_OBJECT_IMAGE1D_ARRAY: max_h = imageInfo->arraySize; max_d = 0; break; case CL_MEM_OBJECT_IMAGE2D_ARRAY: max_h = imageInfo->height; max_d = imageInfo->arraySize; break; default: max_h = imageInfo->height; max_d = imageInfo->depth; break; } if (/*gTestMipmaps*/ imageInfo->num_mip_levels > 1) { switch (imageInfo->type) { case CL_MEM_OBJECT_IMAGE3D: max_d = (float)((imageInfo->depth >> lod) ? (imageInfo->depth >> lod) : 1); case CL_MEM_OBJECT_IMAGE2D: case CL_MEM_OBJECT_IMAGE2D_ARRAY: max_h = (float)((imageInfo->height >> lod) ? (imageInfo->height >> lod) : 1); break; default:; } max_w = (float)((imageInfo->width >> lod) ? (imageInfo->width >> lod) : 1); } get_integer_coords_offset(x, y, z, xAddressOffset, yAddressOffset, zAddressOffset, max_w, max_h, max_d, imageSampler, imageInfo, iX, iY, iZ); read_image_pixel(imageData, imageInfo, iX, iY, iZ, outData, lod); } template void sample_image_pixel_offset(void *imageData, image_descriptor *imageInfo, float x, float y, float z, float xAddressOffset, float yAddressOffset, float zAddressOffset, image_sampler_data *imageSampler, T *outData) { sample_image_pixel_offset(imageData, imageInfo, x, y, z, xAddressOffset, yAddressOffset, zAddressOffset, imageSampler, outData, 0); } template void sample_image_pixel(void *imageData, image_descriptor *imageInfo, float x, float y, float z, image_sampler_data *imageSampler, T *outData) { return sample_image_pixel_offset(imageData, imageInfo, x, y, z, 0.0f, 0.0f, 0.0f, imageSampler, outData); } FloatPixel sample_image_pixel_float(void *imageData, image_descriptor *imageInfo, float x, float y, float z, image_sampler_data *imageSampler, float *outData, int verbose, int *containsDenorms); FloatPixel sample_image_pixel_float(void *imageData, image_descriptor *imageInfo, float x, float y, float z, image_sampler_data *imageSampler, float *outData, int verbose, int *containsDenorms, int lod); FloatPixel sample_image_pixel_float_offset( void *imageData, image_descriptor *imageInfo, float x, float y, float z, float xAddressOffset, float yAddressOffset, float zAddressOffset, image_sampler_data *imageSampler, float *outData, int verbose, int *containsDenorms); FloatPixel sample_image_pixel_float_offset( void *imageData, image_descriptor *imageInfo, float x, float y, float z, float xAddressOffset, float yAddressOffset, float zAddressOffset, image_sampler_data *imageSampler, float *outData, int verbose, int *containsDenorms, int lod); extern void pack_image_pixel(unsigned int *srcVector, const cl_image_format *imageFormat, void *outData); extern void pack_image_pixel(int *srcVector, const cl_image_format *imageFormat, void *outData); extern void pack_image_pixel(float *srcVector, const cl_image_format *imageFormat, void *outData); extern void pack_image_pixel_error(const float *srcVector, const cl_image_format *imageFormat, const void *results, float *errors); extern char *create_random_image_data(ExplicitType dataType, image_descriptor *imageInfo, BufferOwningPtr &P, MTdata d, bool image2DFromBuffer = false); // deprecated // extern bool clamp_image_coord( image_sampler_data *imageSampler, float value, // size_t max, int &outValue ); extern void get_sampler_kernel_code(image_sampler_data *imageSampler, char *outLine); extern float get_max_absolute_error(const cl_image_format *format, image_sampler_data *sampler); extern float get_max_relative_error(const cl_image_format *format, image_sampler_data *sampler, int is3D, int isLinearFilter); #define errMax(_x, _y) ((_x) != (_x) ? (_x) : (_x) > (_y) ? (_x) : (_y)) static inline cl_uint abs_diff_uint(cl_uint x, cl_uint y) { return y > x ? y - x : x - y; } static inline cl_uint abs_diff_int(cl_int x, cl_int y) { return (cl_uint)(y > x ? y - x : x - y); } static inline cl_float relative_error(float test, float expected) { // 0-0/0 is 0 in this case, not NaN if (test == 0.0f && expected == 0.0f) return 0.0f; return (test - expected) / expected; } extern float random_float(float low, float high); class CoordWalker { public: CoordWalker(void *coords, bool useFloats, size_t vecSize); ~CoordWalker(); cl_float Get(size_t idx, size_t el); protected: cl_float *mFloatCoords; cl_int *mIntCoords; size_t mVecSize; }; extern cl_half convert_float_to_half(float f); extern int DetectFloatToHalfRoundingMode( cl_command_queue); // Returns CL_SUCCESS on success // sign bit: don't care, exponent: maximum value, significand: non-zero static int inline is_half_nan(cl_half half) { return (half & 0x7fff) > 0x7c00; } // sign bit: don't care, exponent: zero, significand: non-zero static int inline is_half_denorm(cl_half half) { return IsHalfSubnormal(half); } // sign bit: don't care, exponent: zero, significand: zero static int inline is_half_zero(cl_half half) { return (half & 0x7fff) == 0; } extern double sRGBmap(float fc); extern const char *convert_image_type_to_string(cl_mem_object_type imageType); #endif // _imageHelpers_h