blob: c432d79be752350121558c528f26bb284b1b9b68 [file] [log] [blame]
//
// 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.
//
#include "testBase.h"
#include "harness/testHarness.h"
const char *context_test_kernels[] = {
"__kernel void sample_test_1(__global uint *src, __global uint *dst)\n"
"{\n"
" int tid = get_global_id(0);\n"
" dst[tid] = src[tid];\n"
"\n"
"}\n"
"__kernel void sample_test_2(__global uint *src, __global uint *dst)\n"
"{\n"
" int tid = get_global_id(0);\n"
" dst[tid] = src[tid] * 2;\n"
"\n"
"}\n"
"__kernel void sample_test_3(__global uint *src, __global uint *dst)\n"
"{\n"
" int tid = get_global_id(0);\n"
" dst[tid] = src[tid] / 2;\n"
"\n"
"}\n"
"__kernel void sample_test_4(__global uint *src, __global uint *dst)\n"
"{\n"
" int tid = get_global_id(0);\n"
" dst[tid] = src[tid] /3;\n"
"\n"
"}\n"
};
cl_uint sampleAction1(cl_uint source) { return source; }
cl_uint sampleAction2(cl_uint source) { return source * 2; }
cl_uint sampleAction3(cl_uint source) { return source / 2; }
cl_uint sampleAction4(cl_uint source) { return source / 3; }
typedef cl_uint (*sampleActionFn)(cl_uint source);
sampleActionFn sampleActions[4] = { sampleAction1, sampleAction2, sampleAction3, sampleAction4 };
#define BUFFER_COUNT 2
#define TEST_SIZE 512
typedef struct TestItem
{
struct TestItem *next;
cl_context c;
cl_command_queue q;
cl_program p;
cl_kernel k[4];
cl_mem m[BUFFER_COUNT];
MTdata d;
}TestItem;
static void DestroyTestItem( TestItem *item );
// Attempt to create a context and associated objects
TestItem *CreateTestItem( cl_device_id deviceID, cl_int *err )
{
cl_int error = 0;
size_t i;
// Allocate the TestItem struct
TestItem *item = (TestItem *) malloc( sizeof(TestItem ) );
if( NULL == item )
{
if( err )
{
log_error( "FAILURE: Failed to allocate TestItem -- out of host memory!\n" );
*err = CL_OUT_OF_HOST_MEMORY;
}
return NULL;
}
//zero so we know which fields we have initialized
memset( item, 0, sizeof( *item ) );
item->d = init_genrand( gRandomSeed );
if( NULL == item->d )
{
if( err )
{
log_error( "FAILURE: Failed to allocate mtdata om CreateTestItem -- out of host memory!\n" );
*err = CL_OUT_OF_HOST_MEMORY;
}
DestroyTestItem( item );
return NULL;
}
// Create a context
item->c = clCreateContext(NULL, 1, &deviceID, notify_callback, NULL, &error );
if( item->c == NULL || error != CL_SUCCESS)
{
if (err) {
log_error( "FAILURE: clCreateContext failed in CreateTestItem: %d\n", error);
*err = error;
}
DestroyTestItem( item );
return NULL;
}
// Create a queue
item->q = clCreateCommandQueue( item->c, deviceID, 0, &error);
if( item->q == NULL || error != CL_SUCCESS)
{
if (err) {
log_error( "FAILURE: clCreateCommandQueue failed in CreateTestItem: %d\n", error );
*err = error;
}
DestroyTestItem( item );
return NULL;
}
// Create a program
error = create_single_kernel_helper_create_program(item->c, &item->p, 1, context_test_kernels);
if( NULL == item->p || CL_SUCCESS != error )
{
if( err )
{
log_error( "FAILURE: clCreateProgram failed in CreateTestItem: %d\n", error );
*err = error;
}
DestroyTestItem( item );
return NULL;
}
error = clBuildProgram( item->p, 1, &deviceID, "", NULL, NULL );
if( error )
{
if( err )
{
log_error( "FAILURE: clBuildProgram failed in CreateTestItem: %d\n", error );
*err = error;
}
DestroyTestItem( item );
return NULL;
}
// create some kernels
for( i = 0; i < sizeof( item->k ) / sizeof( item->k[0] ); i++ )
{
static const char *kernelNames[] = { "sample_test_1", "sample_test_2", "sample_test_3", "sample_test_4" };
item->k[i] = clCreateKernel( item->p, kernelNames[i], &error );
if( NULL == item->k[i] || CL_SUCCESS != error )
{
if( err )
{
log_error( "FAILURE: clCreateKernel( \"%s\" ) failed in CreateTestItem: %d\n", kernelNames[i], error );
*err = error;
}
DestroyTestItem( item );
return NULL;
}
}
// create some mem objects
for( i = 0; i < BUFFER_COUNT; i++ )
{
item->m[i] = clCreateBuffer(item->c, CL_MEM_READ_WRITE,
TEST_SIZE * sizeof(cl_uint), NULL, &error);
if( NULL == item->m[i] || CL_SUCCESS != error )
{
if( err )
{
log_error("FAILURE: clCreateBuffer( %ld bytes ) failed in "
"CreateTestItem: %d\n",
TEST_SIZE * sizeof(cl_uint), error);
*err = error;
}
DestroyTestItem( item );
return NULL;
}
}
return item;
}
// Destroy a context and associate objects
static void DestroyTestItem( TestItem *item )
{
size_t i;
if( NULL == item )
return;
if( item->d )
free_mtdata( item->d );
if( item->c)
clReleaseContext( item->c );
if( item->q)
clReleaseCommandQueue( item->q );
if( item->p)
clReleaseProgram( item->p );
for( i = 0; i < sizeof( item->k ) / sizeof( item->k[0] ); i++ )
{
if( item->k[i])
clReleaseKernel( item->k[i] );
}
for( i = 0; i < BUFFER_COUNT; i++ )
{
if( item->m[i])
clReleaseMemObject( item->m[i] );
}
free(item );
}
cl_int UseTestItem( const TestItem *item, cl_int *err )
{
size_t i, j;
cl_int error = CL_SUCCESS;
// Fill buffer 0 with random numbers
cl_uint *mapped = (cl_uint *)clEnqueueMapBuffer(
item->q, item->m[0], CL_TRUE, CL_MAP_WRITE, 0,
TEST_SIZE * sizeof(cl_uint), 0, NULL, NULL, &error);
if( NULL == mapped || CL_SUCCESS != error )
{
if( err )
{
log_error( "FAILURE: Failed to map buffer 0 for writing: %d\n", error );
*err = error;
}
return error;
}
for( j = 0; j < TEST_SIZE; j++ )
mapped[j] = genrand_int32(item->d);
error = clEnqueueUnmapMemObject( item->q, item->m[0], mapped, 0, NULL, NULL );
if( CL_SUCCESS != error )
{
if( err )
{
log_error( "FAILURE: failure to unmap buffer 0 for writing: %d\n", error );
*err = error;
}
return error;
}
// try each kernel in turn.
for( j = 0; j < sizeof(item->k) / sizeof( item->k[0] ); j++ )
{
// Fill buffer 1 with 0xdeaddead
mapped = (cl_uint *)clEnqueueMapBuffer(
item->q, item->m[1], CL_TRUE, CL_MAP_WRITE, 0,
TEST_SIZE * sizeof(cl_uint), 0, NULL, NULL, &error);
if( NULL == mapped || CL_SUCCESS != error )
{
if( err )
{
log_error( "Failed to map buffer 1 for writing: %d\n", error );
*err = error;
}
return error;
}
for( i = 0; i < TEST_SIZE; i++ )
mapped[i] = 0xdeaddead;
error = clEnqueueUnmapMemObject( item->q, item->m[1], mapped, 0, NULL, NULL );
if( CL_SUCCESS != error )
{
if( err )
{
log_error( "Failed to unmap buffer 1 for writing: %d\n", error );
*err = error;
}
return error;
}
// Run the kernel
error = clSetKernelArg( item->k[j], 0, sizeof( cl_mem), &item->m[0] );
if( error )
{
if( err )
{
log_error( "FAILURE to set arg 0 for kernel # %ld : %d\n", j, error );
*err = error;
}
return error;
}
error = clSetKernelArg( item->k[j], 1, sizeof( cl_mem), &item->m[1] );
if( error )
{
if( err )
{
log_error( "FAILURE: Unable to set arg 1 for kernel # %ld : %d\n", j, error );
*err = error;
}
return error;
}
size_t work_size = TEST_SIZE;
size_t global_offset = 0;
error = clEnqueueNDRangeKernel( item->q, item->k[j], 1, &global_offset, &work_size, NULL, 0, NULL, NULL );
if( CL_SUCCESS != error )
{
if( err )
{
log_error( "FAILURE: Unable to enqueue kernel %ld: %d\n", j, error );
*err = error;
}
return error;
}
// Get the results back
mapped = (cl_uint *)clEnqueueMapBuffer(
item->q, item->m[1], CL_TRUE, CL_MAP_READ, 0,
TEST_SIZE * sizeof(cl_uint), 0, NULL, NULL, &error);
if( NULL == mapped || CL_SUCCESS != error )
{
if( err )
{
log_error( "Failed to map buffer 1 for reading: %d\n", error );
*err = error;
}
return error;
}
// Get our input data so we can check against it
cl_uint *inputData = (cl_uint *)clEnqueueMapBuffer(
item->q, item->m[0], CL_TRUE, CL_MAP_READ, 0,
TEST_SIZE * sizeof(cl_uint), 0, NULL, NULL, &error);
if( NULL == mapped || CL_SUCCESS != error )
{
if( err )
{
log_error( "Failed to map buffer 0 for reading: %d\n", error );
*err = error;
}
return error;
}
//Verify the results
for( i = 0; i < TEST_SIZE; i++ )
{
cl_uint expected = sampleActions[j](inputData[i]);
cl_uint result = mapped[i];
if( expected != result )
{
log_error( "FAILURE: Sample data at position %ld does not match expected result: *0x%8.8x vs. 0x%8.8x\n", i, expected, result );
if( err )
*err = -1;
return -1;
}
}
//Clean up
error = clEnqueueUnmapMemObject( item->q, item->m[0], inputData, 0, NULL, NULL );
if( CL_SUCCESS != error )
{
if( err )
{
log_error( "Failed to unmap buffer 0 for reading: %d\n", error );
*err = error;
}
return error;
}
error = clEnqueueUnmapMemObject( item->q, item->m[1], mapped, 0, NULL, NULL );
if( CL_SUCCESS != error )
{
if( err )
{
log_error( "Failed to unmap buffer 1 for reading: %d\n", error );
*err = error;
}
return error;
}
}
// Make sure that the last set of unmap calls get run
error = clFinish( item->q );
if( CL_SUCCESS != error )
{
if( err )
{
log_error( "Failed to clFinish: %d\n", error );
*err = error;
}
return error;
}
return CL_SUCCESS;
}
int test_context_multiple_contexts_same_device(cl_device_id deviceID, size_t maxCount, size_t minCount )
{
size_t i, j;
cl_int err = CL_SUCCESS;
//Figure out how many of these we can make before the first failure
TestItem *list = NULL;
for( i = 0; i < maxCount; i++ )
{
// create a context and accompanying objects
TestItem *current = CreateTestItem( deviceID, NULL /*no error reporting*/ );
if( NULL == current )
break;
// Attempt to use it
cl_int failed = UseTestItem( current, NULL );
if( failed )
{
DestroyTestItem( current );
break;
}
// Add the successful test item to the list
current->next = list;
list = current;
}
// Check to make sure we made the minimum amount
if( i < minCount )
{
log_error( "FAILURE: only could make %ld of %ld contexts!\n", i, minCount );
err = -1;
goto exit;
}
// Report how many contexts we made
if( i == maxCount )
log_info( "Successfully created all %lu contexts.\n", i );
else
log_info( "Successfully created %lu contexts out of %lu\n", i, maxCount );
// Set the count to be the number we succesfully made
maxCount = i;
// Make sure we can do it again a few times
log_info( "Tring to do it 5 more times" );
fflush( stdout);
for( j = 0; j < 5; j++ )
{
//free all the contexts we already made
while( list )
{
TestItem *current = list;
list = list->next;
current->next = NULL;
DestroyTestItem( current );
}
// Attempt to make them again
for( i = 0; i < maxCount; i++ )
{
// create a context and accompanying objects
TestItem *current = CreateTestItem( deviceID, &err );
if( err )
{
log_error( "\nTest Failed with error at CreateTestItem: %d\n", err );
goto exit;
}
// Attempt to use it
cl_int failed = UseTestItem( current, &err );
if( failed || err )
{
DestroyTestItem( current );
log_error( "\nTest Failed with error at UseTestItem: %d\n", err );
goto exit;
}
// Add the successful test item to the list
current->next = list;
list = current;
}
log_info( "." );
fflush( stdout );
}
log_info( "Done.\n" );
exit:
//free all the contexts we already made
while( list )
{
TestItem *current = list;
list = list->next;
current->next = NULL;
DestroyTestItem( current );
}
return err;
}
// This test tests to make sure that your implementation isn't super leaky. We make a bunch of contexts (up to some
// sane limit, currently 200), attempting to use each along the way. We keep track of how many we could make before
// a failure occurred. We then free everything and attempt to go do it again a few times. If you are able to make
// that many contexts 5 times over, then you pass.
int test_context_multiple_contexts_same_device(cl_device_id deviceID, cl_context context, cl_command_queue queue, int num_elements)
{
return test_context_multiple_contexts_same_device(deviceID, 200, 1);
}
int test_context_two_contexts_same_device(cl_device_id deviceID, cl_context context, cl_command_queue queue, int num_elements)
{
return test_context_multiple_contexts_same_device( deviceID, 2, 2 );
}
int test_context_three_contexts_same_device(cl_device_id deviceID, cl_context context, cl_command_queue queue, int num_elements)
{
return test_context_multiple_contexts_same_device( deviceID, 3, 3 );
}
int test_context_four_contexts_same_device(cl_device_id deviceID, cl_context context, cl_command_queue queue, int num_elements)
{
return test_context_multiple_contexts_same_device( deviceID, 4, 4 );
}