blob: fcaaa7ec9bf3324ab8587d91ee703370db9c9cae [file] [log] [blame]
//TODO: copyrights
#include "memobject.h"
#include "context.h"
#include "device_interface.h"
#include "propertylist.h"
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace Devices;
/*
* MemObject
*/
MemObject::MemObject(Context *ctx, cl_mem_flags flags, void *host_ptr,
cl_int *errcode_ret)
: Object(Object::T_MemObject, ctx), p_num_devices(0), p_flags(flags),
p_host_ptr(host_ptr), p_devicebuffers(0), p_dtor_callback(0)
{
// Check the flags value
const cl_mem_flags all_flags = CL_MEM_READ_WRITE | CL_MEM_WRITE_ONLY |
CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR |
CL_MEM_ALLOC_HOST_PTR | CL_MEM_COPY_HOST_PTR;
if((flags & ~all_flags) != 0)
{
*errcode_ret = CL_INVALID_VALUE;
return;
}
if((flags & CL_MEM_ALLOC_HOST_PTR) && (flags & CL_MEM_USE_HOST_PTR))
{
*errcode_ret = CL_INVALID_VALUE;
return;
}
if((flags & CL_MEM_COPY_HOST_PTR) && (flags & CL_MEM_USE_HOST_PTR))
{
*errcode_ret = CL_INVALID_VALUE;
return;
}
// Check other values
if((flags & (CL_MEM_USE_HOST_PTR | CL_MEM_COPY_HOST_PTR)) != 0 && !host_ptr)
{
*errcode_ret = CL_INVALID_HOST_PTR;
return;
}
if((flags & (CL_MEM_USE_HOST_PTR | CL_MEM_COPY_HOST_PTR)) == 0 && host_ptr)
{
*errcode_ret = CL_INVALID_HOST_PTR;
return;
}
}
MemObject::~MemObject()
{
if(p_dtor_callback)
p_dtor_callback((cl_mem)this, p_dtor_userdata);
if(p_devicebuffers)
{
// Also delete our children in the device
for(unsigned int i = 0; i<p_num_devices; ++i)
delete p_devicebuffers[i];
std::free((void *)p_devicebuffers);
}
}
cl_int MemObject::init()
{
// Get the device list of the context
DeviceInterface **devices = 0;
cl_int rs;
rs = ((Context *)parent())->info(CL_CONTEXT_NUM_DEVICES,
sizeof(unsigned int),
&p_num_devices, 0);
if(rs != CL_SUCCESS)
return rs;
p_devices_to_allocate = p_num_devices;
devices = (DeviceInterface **)std::malloc(p_num_devices *
sizeof(DeviceInterface *));
if(!devices)
return CL_OUT_OF_HOST_MEMORY;
rs = ((Context *)parent())->info(CL_CONTEXT_DEVICES,
p_num_devices * sizeof(DeviceInterface *),
devices, 0);
if(rs != CL_SUCCESS)
{
std::free((void *)devices);
return rs;
}
// Allocate a table of DeviceBuffers
p_devicebuffers = (DeviceBuffer **)std::malloc(p_num_devices *
sizeof(DeviceBuffer *));
if(!p_devicebuffers)
{
std::free((void *)devices);
return CL_OUT_OF_HOST_MEMORY;
}
// If we have more than one device, the allocation on the devices is
// defered to first use, so host_ptr can become invalid. So, copy it in
// a RAM location and keep it. Also, set a flag telling CPU devices that
// they don't need to reallocate and re-copy host_ptr
if(p_num_devices > 1 && (p_flags & CL_MEM_COPY_HOST_PTR))
{
void *tmp_hostptr = std::malloc(size());
if(!tmp_hostptr)
{
std::free((void *)devices);
return CL_OUT_OF_HOST_MEMORY;
}
std::memcpy(tmp_hostptr, p_host_ptr, size());
p_host_ptr = tmp_hostptr;
// Now, the client application can safely std::free() its host_ptr
}
// Create a DeviceBuffer for each device
unsigned int failed_devices = 0;
for(unsigned int i = 0; i<p_num_devices; ++i)
{
DeviceInterface *device = devices[i];
rs = CL_SUCCESS;
p_devicebuffers[i] = device->createDeviceBuffer(this, &rs);
if(rs != CL_SUCCESS)
{
p_devicebuffers[i] = 0;
failed_devices++;
}
}
if(failed_devices == p_num_devices)
{
// Each device found a reason to reject the buffer, so it's invalid
std::free((void *)devices);
return rs;
}
std::free((void *)devices);
devices = 0;
// If we have only one device, already allocate the buffer
if(p_num_devices == 1)
{
if(!p_devicebuffers[0]->allocate())
return CL_MEM_OBJECT_ALLOCATION_FAILURE;
}
return CL_SUCCESS;
}
bool MemObject::allocate(DeviceInterface *device)
{
DeviceBuffer *buffer = deviceBuffer(device);
if(!buffer->allocated())
{
return buffer->allocate();
}
return true;
}
cl_mem_flags MemObject::flags() const
{
return p_flags;
}
void *MemObject::host_ptr() const
{
if(type() != SubBuffer)
return p_host_ptr;
else
{
const class SubBuffer *subbuf = (const class SubBuffer *)this;
char *tmp = (char *)subbuf->parent()->host_ptr();
if(!tmp) return 0;
tmp += subbuf->offset();
return (void *)tmp;
}
}
DeviceBuffer *MemObject::deviceBuffer(DeviceInterface *device) const
{
for(unsigned int i = 0; i<p_num_devices; ++i)
{
if(p_devicebuffers[i]->device() == device)
return p_devicebuffers[i];
}
return 0;
}
void MemObject::deviceAllocated(DeviceBuffer *buffer)
{
(void)buffer;
// Decrement the count of devices that must be allocated. If it becomes
// 0, it means we don't need to keep a copied host_ptr and that we can
// std::free() it.
p_devices_to_allocate--;
if(p_devices_to_allocate == 0 &&
p_num_devices > 1 &&
(p_flags & CL_MEM_COPY_HOST_PTR))
{
std::free(p_host_ptr);
p_host_ptr = 0;
}
}
void MemObject::setDestructorCallback(void (CL_CALLBACK *pfn_notify)
(cl_mem memobj, void *user_data),
void *user_data)
{
p_dtor_callback = pfn_notify;
p_dtor_userdata = user_data;
}
// HACK for the union
typedef void * void_p;
cl_int MemObject::info(cl_mem_info param_name,
size_t param_value_size,
void *param_value,
size_t *param_value_size_ret) const
{
void *value = 0;
size_t value_length = 0;
class SubBuffer *subbuf = (class SubBuffer *)this;
union {
cl_mem_object_type cl_mem_object_type_var;
cl_mem_flags cl_mem_flags_var;
size_t size_t_var;
void_p void_p_var;
cl_uint cl_uint_var;
cl_context cl_context_var;
cl_mem cl_mem_var;
};
switch(param_name)
{
case CL_MEM_TYPE:
switch(type())
{
case Buffer:
case SubBuffer:
cl_mem_object_type_var = CL_MEM_OBJECT_BUFFER;
break;
case Image2D:
cl_mem_object_type_var = CL_MEM_OBJECT_IMAGE2D;
break;
case Image3D:
cl_mem_object_type_var = CL_MEM_OBJECT_IMAGE3D;
break;
}
value = (void *)&cl_mem_object_type_var;
value_length = sizeof(cl_mem_object_type);
break;
case CL_MEM_FLAGS:
SIMPLE_ASSIGN(cl_mem_flags, p_flags);
break;
case CL_MEM_SIZE:
SIMPLE_ASSIGN(size_t, size());
break;
case CL_MEM_HOST_PTR:
SIMPLE_ASSIGN(void_p, host_ptr());
break;
case CL_MEM_MAP_COUNT:
SIMPLE_ASSIGN(cl_uint, 0); // TODO
break;
case CL_MEM_REFERENCE_COUNT:
SIMPLE_ASSIGN(cl_uint, references());
break;
case CL_MEM_CONTEXT:
SIMPLE_ASSIGN(cl_context, parent());
break;
case CL_MEM_ASSOCIATED_MEMOBJECT:
if(type() != SubBuffer)
SIMPLE_ASSIGN(cl_mem, 0)
else
SIMPLE_ASSIGN(cl_mem, subbuf->parent());
break;
case CL_MEM_OFFSET:
if(type() != SubBuffer)
SIMPLE_ASSIGN(cl_mem, 0)
else
SIMPLE_ASSIGN(cl_mem, subbuf->offset());
break;
default:
return CL_INVALID_VALUE;
}
if(param_value && param_value_size < value_length)
return CL_INVALID_VALUE;
if(param_value_size_ret)
*param_value_size_ret = value_length;
if(param_value)
std::memcpy(param_value, value, value_length);
return CL_SUCCESS;
}
/*
* Buffer
*/
Buffer::Buffer(Context *ctx, size_t size, void *host_ptr, cl_mem_flags flags,
cl_int *errcode_ret)
: MemObject(ctx, flags, host_ptr, errcode_ret), p_size(size)
{
if(size == 0)
{
*errcode_ret = CL_INVALID_BUFFER_SIZE;
return;
}
}
size_t Buffer::size() const
{
return p_size;
}
MemObject::Type Buffer::type() const
{
return MemObject::Buffer;
}
/*
* SubBuffer
*/
SubBuffer::SubBuffer(class Buffer *parent, size_t offset, size_t size,
cl_mem_flags flags, cl_int *errcode_ret)
: MemObject((Context *)parent->parent(), flags, 0, errcode_ret), p_offset(offset),
p_size(size), p_parent(parent)
{
if(size == 0)
{
*errcode_ret = CL_INVALID_BUFFER_SIZE;
return;
}
if(offset + size > parent->size())
{
*errcode_ret = CL_INVALID_BUFFER_SIZE;
return;
}
// Check the compatibility of flags and parent->flags()
const cl_mem_flags wrong_flags =
CL_MEM_ALLOC_HOST_PTR |
CL_MEM_USE_HOST_PTR |
CL_MEM_COPY_HOST_PTR;
if(flags & wrong_flags)
{
*errcode_ret = CL_INVALID_VALUE;
return;
}
if((parent->flags() & CL_MEM_WRITE_ONLY) &&
(flags & (CL_MEM_READ_WRITE | CL_MEM_READ_ONLY)))
{
*errcode_ret = CL_INVALID_VALUE;
return;
}
if((parent->flags() & CL_MEM_READ_ONLY) &&
(flags & (CL_MEM_READ_WRITE | CL_MEM_WRITE_ONLY)))
{
*errcode_ret = CL_INVALID_VALUE;
return;
}
}
size_t SubBuffer::size() const
{
return p_size;
}
MemObject::Type SubBuffer::type() const
{
return MemObject::SubBuffer;
}
bool SubBuffer::allocate(DeviceInterface *device)
{
return p_parent->allocate(device);
}
size_t SubBuffer::offset() const
{
return p_offset;
}
Buffer *SubBuffer::parent() const
{
return p_parent;
}
/*
* Image2D
*/
Image2D::Image2D(Context *ctx, size_t width, size_t height, size_t row_pitch,
const cl_image_format *format, void *host_ptr,
cl_mem_flags flags, cl_int *errcode_ret)
: MemObject(ctx, flags, host_ptr, errcode_ret),
p_width(width), p_height(height), p_row_pitch(row_pitch)
{
if(!width || !height)
{
*errcode_ret = CL_INVALID_IMAGE_SIZE;
return;
}
if(!format)
{
*errcode_ret = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR;
return;
}
p_format = *format;
// Check format descriptor
switch(p_format.image_channel_data_type)
{
case CL_UNORM_INT_101010:
case CL_UNORM_SHORT_555:
case CL_UNORM_SHORT_565:
if(p_format.image_channel_order != CL_RGB ||
p_format.image_channel_order != CL_RGBx)
{
*errcode_ret = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR;
return;
}
}
switch(p_format.image_channel_order)
{
case CL_LUMINANCE:
case CL_INTENSITY:
switch(p_format.image_channel_data_type)
{
case CL_UNORM_INT8:
case CL_UNORM_INT16:
case CL_SNORM_INT8:
case CL_SNORM_INT16:
case CL_HALF_FLOAT:
case CL_FLOAT:
break;
default:
*errcode_ret = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR;
return;
}
break;
case CL_RGB:
case CL_RGBx:
switch(p_format.image_channel_data_type)
{
case CL_UNORM_SHORT_555:
case CL_UNORM_SHORT_565:
case CL_UNORM_INT_101010:
break;
default:
*errcode_ret = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR;
return;
}
break;
case CL_ARGB:
case CL_BGRA:
switch(p_format.image_channel_data_type)
{
case CL_UNORM_INT8:
case CL_SNORM_INT8:
case CL_SIGNED_INT8:
case CL_UNSIGNED_INT8:
break;
default:
*errcode_ret = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR;
return;
}
break;
}
// Row pitch
p_row_pitch = width * pixel_size(p_format);
if(row_pitch)
{
if(!host_ptr)
{
// row_pitch must be 0 if host_ptr is null
*errcode_ret = CL_INVALID_IMAGE_SIZE;
return;
}
if(row_pitch < p_row_pitch)
{
*errcode_ret = CL_INVALID_IMAGE_SIZE;
return;
}
if(row_pitch % pixel_size(p_format) != 0)
{
*errcode_ret = CL_INVALID_IMAGE_SIZE;
return;
}
p_row_pitch = row_pitch;
}
}
size_t Image2D::size() const
{
return height() * row_pitch();
}
MemObject::Type Image2D::type() const
{
return MemObject::Image2D;
}
size_t Image2D::width() const
{
return p_width;
}
size_t Image2D::height() const
{
return p_height;
}
size_t Image2D::row_pitch() const
{
return p_row_pitch;
}
size_t Image2D::slice_pitch() const
{
// An Image2D is made of only one slice
return size();
}
const cl_image_format &Image2D::format() const
{
return p_format;
}
cl_int Image2D::imageInfo(cl_image_info param_name,
size_t param_value_size,
void *param_value,
size_t *param_value_size_ret) const
{
void *value = 0;
size_t value_length = 0;
class Image3D *image3D = (class Image3D *)this;
union {
cl_image_format cl_image_format_var;
size_t size_t_var;
};
switch(param_name)
{
case CL_IMAGE_FORMAT:
SIMPLE_ASSIGN(cl_image_format, format());
break;
case CL_IMAGE_ELEMENT_SIZE:
SIMPLE_ASSIGN(size_t, element_size(p_format));
break;
case CL_IMAGE_ROW_PITCH:
// TODO: What was given when the image was created or width*size ?
SIMPLE_ASSIGN(size_t, row_pitch());
break;
case CL_IMAGE_SLICE_PITCH:
if(type() == Image3D)
SIMPLE_ASSIGN(size_t, image3D->slice_pitch())
else
SIMPLE_ASSIGN(size_t, 0);
break;
case CL_IMAGE_WIDTH:
SIMPLE_ASSIGN(size_t, width());
break;
case CL_IMAGE_HEIGHT:
SIMPLE_ASSIGN(size_t, height());
break;
case CL_IMAGE_DEPTH:
if(type() == Image3D)
SIMPLE_ASSIGN(size_t, image3D->depth())
else
SIMPLE_ASSIGN(size_t, 0);
break;
default:
return CL_INVALID_VALUE;
}
if(param_value && param_value_size < value_length)
return CL_INVALID_VALUE;
if(param_value_size_ret)
*param_value_size_ret = value_length;
if(param_value)
std::memcpy(param_value, value, value_length);
return CL_SUCCESS;
}
size_t Image2D::element_size(const cl_image_format &format)
{
switch(format.image_channel_data_type)
{
case CL_SNORM_INT8:
case CL_UNORM_INT8:
case CL_SIGNED_INT8:
case CL_UNSIGNED_INT8:
return 1;
case CL_SNORM_INT16:
case CL_UNORM_INT16:
case CL_SIGNED_INT16:
case CL_UNSIGNED_INT16:
return 2;
case CL_SIGNED_INT32:
case CL_UNSIGNED_INT32:
return 4;
case CL_FLOAT:
return sizeof(float);
case CL_HALF_FLOAT:
return 2;
case CL_UNORM_SHORT_565:
case CL_UNORM_SHORT_555:
return 2;
case CL_UNORM_INT_101010:
return 4;
default:
return 0;
}
}
unsigned int Image2D::channels(const cl_image_format &format)
{
switch(format.image_channel_order)
{
case CL_R:
case CL_Rx:
case CL_A:
case CL_INTENSITY:
case CL_LUMINANCE:
return 1;
break;
case CL_RG:
case CL_RGx:
case CL_RA:
return 2;
break;
case CL_RGBA:
case CL_ARGB:
case CL_BGRA:
return 4;
break;
case CL_RGBx:
case CL_RGB:
return 1; // Only special data types allowed (565, 555, etc)
break;
default:
return 0;
}
}
size_t Image2D::pixel_size(const cl_image_format &format)
{
switch(format.image_channel_data_type)
{
case CL_UNORM_SHORT_565:
case CL_UNORM_SHORT_555:
return 2;
case CL_UNORM_INT_101010:
return 4;
default:
return channels(format) * element_size(format);
}
}
size_t Image2D::element_size() const
{
return element_size(p_format);
}
size_t Image2D::pixel_size() const
{
return pixel_size(p_format);
}
unsigned int Image2D::channels() const
{
return channels(p_format);
}
/*
* Image3D
*/
Image3D::Image3D(Context *ctx, size_t width, size_t height, size_t depth,
size_t row_pitch, size_t slice_pitch,
const cl_image_format *format, void *host_ptr,
cl_mem_flags flags, cl_int *errcode_ret)
: Image2D(ctx, width, height, row_pitch, format, host_ptr, flags, errcode_ret),
p_depth(depth)
{
if(depth <= 1)
{
*errcode_ret = CL_INVALID_IMAGE_SIZE;
return;
}
// Slice pitch
p_slice_pitch = height * this->row_pitch();
if(slice_pitch)
{
if(!host_ptr)
{
// slice_pitch must be 0 if host_ptr is null
*errcode_ret = CL_INVALID_IMAGE_SIZE;
return;
}
if(slice_pitch < p_slice_pitch)
{
*errcode_ret = CL_INVALID_IMAGE_SIZE;
return;
}
if(slice_pitch % this->row_pitch() != 0)
{
*errcode_ret = CL_INVALID_IMAGE_SIZE;
return;
}
p_slice_pitch = slice_pitch;
}
}
size_t Image3D::size() const
{
return depth() * slice_pitch();
}
MemObject::Type Image3D::type() const
{
return MemObject::Image3D;
}
size_t Image3D::depth() const
{
return p_depth;
}
size_t Image3D::slice_pitch() const
{
return p_slice_pitch;
}