diff options
author | Denis Steckelmacher <steckdenis@yahoo.fr> | 2011-08-01 14:57:40 +0200 |
---|---|---|
committer | Denis Steckelmacher <steckdenis@yahoo.fr> | 2011-08-01 14:57:40 +0200 |
commit | ce8975073e81ae54c8542646d146b43c741048e4 (patch) | |
tree | 3c656cebc8b247858daa1a0782e864146be5b066 | |
parent | aac90234d34bc2d6cee8461b692a740c56a140da (diff) |
Implement clEnqueueCopyImageToBuffer and clEnqueueCopyBufferToImage
Plus bug fixes regarding Image2D slice_pitch.
-rw-r--r-- | src/api/api_enqueue.cpp | 40 | ||||
-rw-r--r-- | src/core/cpu/worker.cpp | 45 | ||||
-rw-r--r-- | src/core/events.cpp | 151 | ||||
-rw-r--r-- | src/core/events.h | 42 | ||||
-rw-r--r-- | src/core/memobject.cpp | 6 | ||||
-rw-r--r-- | src/core/memobject.h | 1 | ||||
-rw-r--r-- | tests/test_commandqueue.cpp | 116 |
7 files changed, 366 insertions, 35 deletions
diff --git a/src/api/api_enqueue.cpp b/src/api/api_enqueue.cpp index 1c28ee0..6ea5ef5 100644 --- a/src/api/api_enqueue.cpp +++ b/src/api/api_enqueue.cpp @@ -365,7 +365,25 @@ clEnqueueCopyImageToBuffer(cl_command_queue command_queue, const cl_event * event_wait_list, cl_event * event) { - return 0; + cl_int rs = CL_SUCCESS; + + if (!command_queue) + return CL_INVALID_COMMAND_QUEUE; + + Coal::CopyImageToBufferEvent *command = new Coal::CopyImageToBufferEvent( + (Coal::CommandQueue *)command_queue, + (Coal::Image2D *)src_image, (Coal::MemObject *)dst_buffer, + src_origin, region, dst_offset, + num_events_in_wait_list, (const Coal::Event **)event_wait_list, &rs + ); + + if (rs != CL_SUCCESS) + { + delete command; + return rs; + } + + return queueEvent(command_queue, command, event, false); } cl_int @@ -379,7 +397,25 @@ clEnqueueCopyBufferToImage(cl_command_queue command_queue, const cl_event * event_wait_list, cl_event * event) { - return 0; + cl_int rs = CL_SUCCESS; + + if (!command_queue) + return CL_INVALID_COMMAND_QUEUE; + + Coal::CopyBufferToImageEvent *command = new Coal::CopyBufferToImageEvent( + (Coal::CommandQueue *)command_queue, + (Coal::MemObject *)src_buffer, (Coal::Image2D *)dst_image, + src_offset, dst_origin, region, + num_events_in_wait_list, (const Coal::Event **)event_wait_list, &rs + ); + + if (rs != CL_SUCCESS) + { + delete command; + return rs; + } + + return queueEvent(command_queue, command, event, false); } void * diff --git a/src/core/cpu/worker.cpp b/src/core/cpu/worker.cpp index 2c055e6..550dadf 100644 --- a/src/core/cpu/worker.cpp +++ b/src/core/cpu/worker.cpp @@ -92,6 +92,8 @@ void *worker(void *data) case Event::ReadImage: case Event::WriteImage: case Event::CopyImage: + case Event::CopyBufferToImage: + case Event::CopyImageToBuffer: { // src = buffer and dst = mem if note copy ReadWriteCopyBufferRectEvent *e = (ReadWriteCopyBufferRectEvent *)event; @@ -100,20 +102,27 @@ void *worker(void *data) unsigned char *src = (unsigned char *)src_buf->data(); unsigned char *dst; - if (t == Event::CopyBufferRect || t == Event::CopyImage) + switch (t) { - CopyBufferRectEvent *cbre = (CopyBufferRectEvent *)e; - CPUBuffer *dst_buf = - (CPUBuffer *)cbre->destination()->deviceBuffer(device); + case Event::CopyBufferRect: + case Event::CopyImage: + case Event::CopyImageToBuffer: + case Event::CopyBufferToImage: + { + CopyBufferRectEvent *cbre = (CopyBufferRectEvent *)e; + CPUBuffer *dst_buf = + (CPUBuffer *)cbre->destination()->deviceBuffer(device); - dst = (unsigned char *)dst_buf->data(); - } - else - { - // dst = host memory location - ReadWriteBufferRectEvent *rwbre = (ReadWriteBufferRectEvent *)e; + dst = (unsigned char *)dst_buf->data(); + break; + } + default: + { + // dst = host memory location + ReadWriteBufferRectEvent *rwbre = (ReadWriteBufferRectEvent *)e; - dst = (unsigned char *)rwbre->ptr(); + dst = (unsigned char *)rwbre->ptr(); + } } // Iterate over the lines to copy and use memcpy @@ -140,6 +149,20 @@ void *worker(void *data) e->src_slice_pitch(), 1); + // Copying and image to a buffer may need to add an offset + // to the buffer address (its rectangular origin is + // always (0, 0, 0)). + if (t == Event::CopyBufferToImage) + { + CopyBufferToImageEvent *cptie = (CopyBufferToImageEvent *)e; + s += cptie->offset(); + } + else if (t == Event::CopyImageToBuffer) + { + CopyImageToBufferEvent *citbe = (CopyImageToBufferEvent *)e; + d += citbe->offset(); + } + if (t == Event::WriteBufferRect || t == Event::WriteImage) std::memcpy(s, d, e->region(0)); // Write dest (memory) in src else diff --git a/src/core/events.cpp b/src/core/events.cpp index c1ac530..7cf6415 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -22,6 +22,8 @@ BufferEvent::BufferEvent(CommandQueue *parent, : Event(parent, Queued, num_events_in_wait_list, event_wait_list, errcode_ret), p_buffer(buffer) { + if (*errcode_ret != CL_SUCCESS) return; + // Correct buffer if (!buffer) { @@ -105,6 +107,8 @@ ReadWriteBufferEvent::ReadWriteBufferEvent(CommandQueue *parent, : BufferEvent(parent, buffer, num_events_in_wait_list, event_wait_list, errcode_ret), p_offset(offset), p_cb(cb), p_ptr(ptr) { + if (*errcode_ret != CL_SUCCESS) return; + // Check for out-of-bounds reads if (!ptr) { @@ -179,6 +183,8 @@ MapBufferEvent::MapBufferEvent(CommandQueue *parent, : BufferEvent(parent, buffer, num_events_in_wait_list, event_wait_list, errcode_ret), p_offset(offset), p_cb(cb), p_map_flags(map_flags) { + if (*errcode_ret != CL_SUCCESS) return; + // Check flags if (map_flags & ~(CL_MAP_READ | CL_MAP_WRITE)) { @@ -228,6 +234,8 @@ UnmapBufferEvent::UnmapBufferEvent(CommandQueue *parent, : BufferEvent(parent, buffer, num_events_in_wait_list, event_wait_list, errcode_ret), p_mapping(mapped_addr) { + if (*errcode_ret != CL_SUCCESS) return; + // TODO: Check that p_mapping is ok (will be done in the drivers) if (!mapped_addr) { @@ -259,6 +267,8 @@ CopyBufferEvent::CopyBufferEvent(CommandQueue *parent, errcode_ret), p_destination(destination), p_src_offset(src_offset), p_dst_offset(dst_offset), p_cb(cb) { + if (*errcode_ret != CL_SUCCESS) return; + if (!destination) { *errcode_ret = CL_INVALID_MEM_OBJECT; @@ -352,6 +362,8 @@ NativeKernelEvent::NativeKernelEvent(CommandQueue *parent, : Event (parent, Queued, num_events_in_wait_list, event_wait_list, errcode_ret), p_user_func((void *)user_func), p_args(0) { + if (*errcode_ret != CL_SUCCESS) return; + // Parameters sanity if (!user_func) { @@ -475,6 +487,8 @@ KernelEvent::KernelEvent(CommandQueue *parent, : Event(parent, Queued, num_events_in_wait_list, event_wait_list, errcode_ret), p_kernel(kernel), p_work_dim(work_dim) { + if (*errcode_ret != CL_SUCCESS) return; + *errcode_ret = CL_SUCCESS; // Sanity checks @@ -770,6 +784,8 @@ ReadWriteCopyBufferRectEvent::ReadWriteCopyBufferRectEvent(CommandQueue *parent, : BufferEvent (parent, source, num_events_in_wait_list, event_wait_list, errcode_ret) { + if (*errcode_ret != CL_SUCCESS) return; + // Copy the vectors if (src_origin) std::memcpy(&p_src_origin, src_origin, 3 * sizeof(size_t)); @@ -911,6 +927,8 @@ CopyBufferRectEvent::CopyBufferRectEvent(CommandQueue *parent, num_events_in_wait_list, event_wait_list, errcode_ret), p_destination(destination) { + if (*errcode_ret != CL_SUCCESS) return; + if (!destination) { *errcode_ret = CL_INVALID_MEM_OBJECT; @@ -918,17 +936,17 @@ CopyBufferRectEvent::CopyBufferRectEvent(CommandQueue *parent, } // Check for out-of-bounds - if ((src_origin[0] + region[0]) * bytes_per_element > this->src_row_pitch() || - (src_origin[1] + region[1]) * this->src_row_pitch() > this->src_slice_pitch() || - (src_origin[2] + region[2]) * this->src_slice_pitch() > source->size()) + if ((p_src_origin[0] + p_region[0]) > p_src_row_pitch || + (p_src_origin[1] + p_region[1]) * p_src_row_pitch > p_src_slice_pitch || + (p_src_origin[2] + p_region[2]) * p_src_slice_pitch > source->size()) { *errcode_ret = CL_INVALID_VALUE; return; } - if ((dst_origin[0] + region[0]) * bytes_per_element > this->dst_row_pitch() || - (dst_origin[1] + region[1]) * this->dst_row_pitch() > this->dst_slice_pitch() || - (dst_origin[2] + region[2]) * this->dst_slice_pitch() > destination->size()) + if ((p_dst_origin[0] + p_region[0]) > p_dst_row_pitch || + (p_dst_origin[1] + p_region[1]) * p_dst_row_pitch > p_dst_slice_pitch || + (p_dst_origin[2] + p_region[2]) * p_dst_slice_pitch > destination->size()) { *errcode_ret = CL_INVALID_VALUE; return; @@ -941,8 +959,8 @@ CopyBufferRectEvent::CopyBufferRectEvent(CommandQueue *parent, for (unsigned int i=0; i<3; ++i) { - if (dst_origin[i] < src_origin[i] && dst_origin[i] + region[i] > src_origin[i] || - src_origin[i] < dst_origin[i] && src_origin[i] + region[i] > dst_origin[i]) + if ((p_dst_origin[i] < p_src_origin[i] && p_dst_origin[i] + p_region[i] > p_src_origin[i]) || + (p_src_origin[i] < p_dst_origin[i] && p_src_origin[i] + p_region[i] > p_dst_origin[i])) overlapping_dimensions++; } @@ -1006,6 +1024,8 @@ ReadWriteBufferRectEvent::ReadWriteBufferRectEvent(CommandQueue *parent, num_events_in_wait_list, event_wait_list, errcode_ret), p_ptr(ptr) { + if (*errcode_ret != CL_SUCCESS) return; + if (!ptr) { *errcode_ret = CL_INVALID_VALUE; @@ -1013,9 +1033,9 @@ ReadWriteBufferRectEvent::ReadWriteBufferRectEvent(CommandQueue *parent, } // Check for out-of-bounds - if ((buffer_origin[0] + region[0]) * bytes_per_element > src_row_pitch() || - (buffer_origin[1] + region[1]) * src_row_pitch() > src_slice_pitch() || - (buffer_origin[2] + region[2]) * src_slice_pitch() > buffer->size()) + if ((p_src_origin[0] + p_region[0]) > p_src_row_pitch || + (p_src_origin[1] + p_region[1]) * p_src_row_pitch > p_src_slice_pitch || + (p_src_origin[2] + p_region[2]) * p_src_slice_pitch > buffer->size()) { *errcode_ret = CL_INVALID_VALUE; return; @@ -1088,11 +1108,12 @@ ReadWriteImageEvent::ReadWriteImageEvent (CommandQueue *parent, const Event **event_wait_list, cl_int *errcode_ret) : ReadWriteBufferRectEvent(parent, image, origin, 0, region, image->row_pitch(), - (image->type() == MemObject::Image3D ? - ((Image3D *)image)->slice_pitch() : - 0), row_pitch, slice_pitch, ptr, image->pixel_size(), - num_events_in_wait_list, event_wait_list, errcode_ret) + image->slice_pitch(), row_pitch, slice_pitch, ptr, + image->pixel_size(), num_events_in_wait_list, + event_wait_list, errcode_ret) { + if (*errcode_ret != CL_SUCCESS) return; + if (image->type() == MemObject::Image2D && (origin[2] != 0 || region[2] != 1)) { @@ -1155,15 +1176,13 @@ CopyImageEvent::CopyImageEvent(CommandQueue *parent, const Event **event_wait_list, cl_int *errcode_ret) : CopyBufferRectEvent (parent, source, destination, src_origin, dst_origin, - region, source->row_pitch(), - (source->type() == MemObject::Image3D ? - ((Image3D *)source)->slice_pitch() : 0), - destination->row_pitch(), - (destination->type() == MemObject::Image3D ? - ((Image3D *)destination)->slice_pitch() : 0), + region, source->row_pitch(), source->slice_pitch(), + destination->row_pitch(), destination->slice_pitch(), source->pixel_size(), num_events_in_wait_list, event_wait_list, errcode_ret) { + if (*errcode_ret != CL_SUCCESS) return; + // Check bounds if (source->type() == MemObject::Image2D && (src_origin[2] != 0 || region[2] != 1)) @@ -1191,3 +1210,93 @@ Event::Type CopyImageEvent::type() const { return Event::CopyImage; } + +CopyImageToBufferEvent::CopyImageToBufferEvent(CommandQueue *parent, + Image2D *source, + MemObject *destination, + const size_t src_origin[3], + const size_t region[3], + size_t dst_offset, + cl_uint num_events_in_wait_list, + const Event **event_wait_list, + cl_int *errcode_ret) +: CopyBufferRectEvent(parent, source, destination, src_origin, 0, region, + source->row_pitch(), source->slice_pitch(), 0, 0, + source->pixel_size(), num_events_in_wait_list, + event_wait_list, errcode_ret), + p_offset(dst_offset) +{ + if (*errcode_ret != CL_SUCCESS) return; + + // Check for buffer overflow + size_t dst_cb = region[2] * region[1] * region[0] * source->pixel_size(); + + if (dst_offset + dst_cb > destination->size()) + { + *errcode_ret = CL_INVALID_VALUE; + return; + } + + // Check validity + if (source->type() == MemObject::Image2D && + (src_origin[2] != 0 || region[2] != 1)) + { + *errcode_ret = CL_INVALID_VALUE; + return; + } +} + +size_t CopyImageToBufferEvent::offset() const +{ + return p_offset; +} + +Event::Type CopyImageToBufferEvent::type() const +{ + return Event::CopyImageToBuffer; +} + +CopyBufferToImageEvent::CopyBufferToImageEvent(CommandQueue *parent, + MemObject *source, + Image2D *destination, + size_t src_offset, + const size_t dst_origin[3], + const size_t region[3], + cl_uint num_events_in_wait_list, + const Event **event_wait_list, + cl_int *errcode_ret) +: CopyBufferRectEvent(parent, source, destination, 0, dst_origin, region, 0, 0, + destination->row_pitch(), destination->slice_pitch(), + destination->pixel_size(), num_events_in_wait_list, + event_wait_list, errcode_ret), + p_offset(src_offset) +{ + if (*errcode_ret != CL_SUCCESS) return; + + // Check for buffer overflow + size_t src_cb = region[2] * region[1] * region[0] * destination->pixel_size(); + + if (src_offset + src_cb > source->size()) + { + *errcode_ret = CL_INVALID_VALUE; + return; + } + + // Check validity + if (destination->type() == MemObject::Image2D && + (dst_origin[2] != 0 || region[2] != 1)) + { + *errcode_ret = CL_INVALID_VALUE; + return; + } +} + +size_t CopyBufferToImageEvent::offset() const +{ + return p_offset; +} + +Event::Type CopyBufferToImageEvent::type() const +{ + return Event::CopyBufferToImage; +} diff --git a/src/core/events.h b/src/core/events.h index 4d8cd86..7ed08dd 100644 --- a/src/core/events.h +++ b/src/core/events.h @@ -179,7 +179,7 @@ class ReadWriteCopyBufferRectEvent : public BufferEvent size_t dst_slice_pitch() const; MemObject *source() const; - private: + protected: size_t p_src_origin[3], p_dst_origin[3], p_region[3]; size_t p_src_row_pitch, p_src_slice_pitch; size_t p_dst_row_pitch, p_dst_slice_pitch; @@ -339,6 +339,46 @@ class CopyImageEvent : public CopyBufferRectEvent Type type() const; }; +class CopyImageToBufferEvent : public CopyBufferRectEvent +{ + public: + CopyImageToBufferEvent(CommandQueue *parent, + Image2D *source, + MemObject *destination, + const size_t src_origin[3], + const size_t region[3], + size_t dst_offset, + cl_uint num_events_in_wait_list, + const Event **event_wait_list, + cl_int *errcode_ret); + + size_t offset() const; + Type type() const; + + private: + size_t p_offset; +}; + +class CopyBufferToImageEvent : public CopyBufferRectEvent +{ + public: + CopyBufferToImageEvent(CommandQueue *parent, + MemObject *source, + Image2D *destination, + size_t src_offset, + const size_t dst_origin[3], + const size_t region[3], + cl_uint num_events_in_wait_list, + const Event **event_wait_list, + cl_int *errcode_ret); + + size_t offset() const; + Type type() const; + + private: + size_t p_offset; +}; + class NativeKernelEvent : public Event { public: diff --git a/src/core/memobject.cpp b/src/core/memobject.cpp index 32fa49d..aabb974 100644 --- a/src/core/memobject.cpp +++ b/src/core/memobject.cpp @@ -578,6 +578,12 @@ 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; diff --git a/src/core/memobject.h b/src/core/memobject.h index dce70b5..a0ac36b 100644 --- a/src/core/memobject.h +++ b/src/core/memobject.h @@ -107,6 +107,7 @@ class Image2D : public MemObject size_t width() const; size_t height() const; size_t row_pitch() const; + virtual size_t slice_pitch() const; const cl_image_format &format() const; cl_int imageInfo(cl_image_info param_name, diff --git a/tests/test_commandqueue.cpp b/tests/test_commandqueue.cpp index 70dc161..2bb57f6 100644 --- a/tests/test_commandqueue.cpp +++ b/tests/test_commandqueue.cpp @@ -476,6 +476,7 @@ START_TEST (test_read_write_rect) "the part of the buffer was not correctly read using a buffer" ); + clReleaseEvent(event); clReleaseMemObject(buf_part); clReleaseMemObject(buf); clReleaseCommandQueue(queue); @@ -665,6 +666,7 @@ START_TEST (test_read_write_image) "copying images doesn't produce the correct result" ); + clReleaseEvent(event); clReleaseMemObject(part2d); clReleaseMemObject(image2d); clReleaseCommandQueue(queue); @@ -672,6 +674,119 @@ START_TEST (test_read_write_image) } END_TEST +START_TEST (test_copy_image_buffer) +{ + cl_platform_id platform = 0; + cl_device_id device; + cl_context ctx; + cl_command_queue queue; + cl_mem image, buffer; + cl_int result; + cl_event event; + + unsigned char image_buffer[3*3*4] = { + 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, + 128, 0, 0, 0, 0, 128, 0, 0, 0, 0, 128, 0, + 64, 0, 0, 0, 0, 64, 0, 0, 0, 0, 64, 0 + }; + + // Square that will be put in image_buffer at (1, 0) + unsigned char buffer_buffer[2*2*4+1] = { + 33, // Oh, a padding ! + 255, 255, 255, 0, 255, 0, 255, 0, + 0, 255, 255, 0, 255, 255, 0, 0 + }; + + // What we must get once re-reading 2x2 rect at (1, 1) + unsigned char correct_data[2*2*4] = { + 0, 255, 255, 0, 255, 255, 0, 0, + 0, 64, 0, 0, 0, 0, 64, 0 + }; + + cl_image_format fmt; + + fmt.image_channel_data_type = CL_UNORM_INT8; + fmt.image_channel_order = CL_RGBA; + + size_t origin[3] = {1, 0, 0}; + size_t region[3] = {2, 2, 1}; + + result = clGetDeviceIDs(platform, CL_DEVICE_TYPE_DEFAULT, 1, &device, 0); + fail_if( + result != CL_SUCCESS, + "unable to get the default device" + ); + + ctx = clCreateContext(0, 1, &device, 0, 0, &result); + fail_if( + result != CL_SUCCESS || ctx == 0, + "unable to create a valid context" + ); + + queue = clCreateCommandQueue(ctx, device, 0, &result); + fail_if( + result != CL_SUCCESS || queue == 0, + "cannot create a command queue" + ); + + image = clCreateImage2D(ctx, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, &fmt, + 3, 3, 0, image_buffer, &result); + fail_if( + result != CL_SUCCESS, + "unable to create a 3x3 bgra image" + ); + + buffer = clCreateBuffer(ctx, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, + sizeof(buffer_buffer), buffer_buffer, &result); + fail_if( + result != CL_SUCCESS, + "unable to create a buffer object" + ); + + // Write buffer in image + result = clEnqueueCopyBufferToImage(queue, buffer, image, 1, origin, region, + 0, 0, &event); + fail_if( + result != CL_SUCCESS, + "unable to queue a copy buffer to image event, buffer offset 1, image 2x2 @ (1, 0)" + ); + + result = clWaitForEvents(1, &event); + fail_if( + result != CL_SUCCESS, + "cannot wait for event" + ); + + clReleaseEvent(event); + + // Read it back into buffer, again with an offset + origin[1] = 1; + result = clEnqueueCopyImageToBuffer(queue, image, buffer, origin, region, 1, + 0, 0, &event); + fail_if( + result != CL_SUCCESS, + "unable to queue a copy image to buffer event, buffer offset 1, image 2x2 @ (1, 1)" + ); + + result = clWaitForEvents(1, &event); + fail_if( + result != CL_SUCCESS, + "cannot wait for event" + ); + + fail_if( + std::memcmp(buffer_buffer + 1, correct_data, sizeof(correct_data)) != 0, + "copying data around isn't working the expected way" + ); + + clReleaseEvent(event); + clReleaseMemObject(image); + clReleaseMemObject(buffer); + clReleaseCommandQueue(queue); + clReleaseContext(ctx); +} +END_TEST + TCase *cl_commandqueue_tcase_create(void) { TCase *tc = NULL; @@ -683,5 +798,6 @@ TCase *cl_commandqueue_tcase_create(void) tcase_add_test(tc, test_read_write_rect); tcase_add_test(tc, test_copy_buffer); tcase_add_test(tc, test_read_write_image); + tcase_add_test(tc, test_copy_image_buffer); return tc; } |