ocl/standard/image.rs
1//! An OpenCL Image.
2//!
3//
4// * NOTE: `Image` does not have the latest command builders nor does it have
5// support for mapping yet. (TODO: implement)
6//
7
8use crate::core::{
9 self, AsMem, ImageChannelDataType, ImageChannelOrder, ImageDescriptor, ImageFormat,
10 ImageFormatParseResult, ImageInfo, ImageInfoResult, MapFlags, Mem as MemCore, MemCmdAll,
11 MemCmdRw, MemFlags, MemInfo, MemInfoResult, MemObjectType, OclPrm,
12};
13use crate::error::{Error as OclError, Result as OclResult};
14use crate::standard::{
15 ClNullEventPtrEnum, ClWaitListPtrEnum, Context, HostSlice, QueCtx, Queue, SpatialDims,
16};
17use crate::MemMap;
18use std;
19use std::marker::PhantomData;
20use std::mem;
21use std::ops::{Deref, DerefMut};
22
23#[cfg(not(feature = "opencl_vendor_mesa"))]
24use crate::core::GlTextureTarget;
25#[cfg(not(feature = "opencl_vendor_mesa"))]
26use crate::ffi::{cl_GLint, cl_GLuint};
27
28use crate::ffi::{cl_id3d11_texture2d, cl_id3d11_texture3d, cl_uint};
29
30/// The type of operation to be performed by a command.
31#[derive(Debug)]
32pub enum ImageCmdKind<'c, T: 'c> {
33 Unspecified,
34 Read {
35 data: &'c mut [T],
36 },
37 Write {
38 data: &'c [T],
39 },
40 Map,
41 Fill {
42 color: T,
43 },
44 Copy {
45 dst_image: &'c MemCore,
46 dst_origin: [usize; 3],
47 },
48 CopyToBuffer {
49 buffer: &'c MemCore,
50 dst_offset: usize,
51 },
52 GLAcquire,
53 GLRelease,
54 D3D11Acquire,
55 D3D11Release,
56}
57
58impl<'c, T: 'c> ImageCmdKind<'c, T> {
59 fn is_unspec(&'c self) -> bool {
60 if let ImageCmdKind::Unspecified = *self {
61 true
62 } else {
63 false
64 }
65 }
66}
67
68/// An image command builder for enqueuing reads, writes, fills, and copies.
69///
70/// ## Examples
71///
72/// ```rust,ignore
73///
74/// // Copies one image to another:
75/// src_image.cmd().copy(&dst_image, [0, 0, 0]).enq().unwrap();
76///
77/// // Writes from a vector to an image, waiting on an event:
78/// image.write(&src_vec).ewait(&event).enq().unwrap();
79///
80/// // Reads from a image into a vector, waiting on an event list and
81/// // filling a new empty event:
82/// image.read(&dst_vec).ewait(&event_list).enew(&empty_event).enq().unwrap();
83///
84/// // Reads without blocking:
85/// image.cmd().read_async(&dst_vec).enew(&empty_event).enq().unwrap();
86///
87/// ```
88///
89/// [FIXME]: Fills not yet implemented.
90#[must_use = "commands do nothing unless enqueued"]
91#[allow(dead_code)]
92pub struct ImageCmd<'c, T: 'c> {
93 queue: Option<&'c Queue>,
94 obj_core: &'c MemCore,
95 block: bool,
96 origin: [usize; 3],
97 region: [usize; 3],
98 row_pitch_bytes: usize,
99 slc_pitch_bytes: usize,
100 kind: ImageCmdKind<'c, T>,
101 ewait: Option<ClWaitListPtrEnum<'c>>,
102 enew: Option<ClNullEventPtrEnum<'c>>,
103 mem_dims: [usize; 3],
104 ext_fns: Option<&'c core::ExtensionFunctions>,
105}
106
107/// [UNSTABLE]: All methods still in a state of adjustifulsomeness.
108impl<'c, T: 'c + OclPrm> ImageCmd<'c, T> {
109 /// Returns a new image command builder associated with with the
110 /// memory object `obj_core` along with a default `queue` and `to_len`
111 /// (the length of the device side image).
112 fn new(
113 queue: Option<&'c Queue>,
114 obj_core: &'c MemCore,
115 dims: [usize; 3],
116 ext_fns: Option<&'c core::ExtensionFunctions>,
117 ) -> ImageCmd<'c, T> {
118 ImageCmd {
119 queue,
120 obj_core,
121 block: true,
122 origin: [0, 0, 0],
123 region: dims,
124 row_pitch_bytes: 0,
125 slc_pitch_bytes: 0,
126 kind: ImageCmdKind::Unspecified,
127 ewait: None,
128 enew: None,
129 mem_dims: dims,
130 ext_fns,
131 }
132 }
133
134 /// Specifies that this command will be a blocking read operation.
135 ///
136 /// After calling this method, the blocking state of this command will
137 /// be locked to true and a call to `::block` will cause a panic.
138 ///
139 /// ## Panics
140 ///
141 /// The command operation kind must not have already been specified.
142 ///
143 /// ### More Information
144 ///
145 /// See [SDK][read_image] docs for more details.
146 ///
147 /// [read_image]: https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/clEnqueueReadImage.html
148 pub fn read<'d>(mut self, dst_data: &'d mut [T]) -> ImageCmd<'c, T>
149 where
150 'd: 'c,
151 {
152 assert!(
153 self.kind.is_unspec(),
154 "ocl::ImageCmd::read(): Operation kind \
155 already set for this command."
156 );
157 self.kind = ImageCmdKind::Read { data: dst_data };
158 self.block = true;
159 self
160 }
161
162 /// Specifies that this command will be a write operation.
163 ///
164 /// ## Panics
165 ///
166 /// The command operation kind must not have already been specified
167 ///
168 /// ### More Information
169 ///
170 /// See [SDK][read_buffer] docs for more details.
171 ///
172 /// [read_buffer]: https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/clEnqueueReadBuffer.html
173 pub fn write<'d>(mut self, src_data: &'d [T]) -> ImageCmd<'c, T>
174 where
175 'd: 'c,
176 {
177 assert!(
178 self.kind.is_unspec(),
179 "ocl::ImageCmd::write(): Operation kind \
180 already set for this command."
181 );
182 self.kind = ImageCmdKind::Write { data: src_data };
183 self
184 }
185
186 /// Specifies that this command will be a map operation.
187 ///
188 /// If `.block(..)` has been set it will be ignored. Non-blocking map
189 /// commands are enqueued using `::enq_async`.
190 ///
191 /// ## Safety
192 ///
193 /// The caller must ensure that only one mapping of a particular memory
194 /// region exists at a time.
195 ///
196 /// ## Panics
197 ///
198 /// The command operation kind must not have already been specified
199 ///
200 /// ### More Information
201 ///
202 /// See [SDK][map_image] docs for more details.
203 ///
204 /// [map_image]: https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/clEnqueueMapImage.html
205 pub unsafe fn map(mut self) -> ImageMapCmd<'c, T> {
206 assert!(
207 self.kind.is_unspec(),
208 "ocl::BufferCmd::write(): Operation kind \
209 already set for this command."
210 );
211 self.kind = ImageCmdKind::Map;
212
213 unimplemented!();
214 // ImageMapCmd { cmd: self }
215 }
216
217 /// Specifies that this command will be a copy operation.
218 ///
219 /// If `.block(..)` has been set it will be ignored.
220 ///
221 /// ## Errors
222 ///
223 /// If this is a rectangular copy, `dst_origin` and `len` must be zero.
224 ///
225 /// ## Panics
226 ///
227 /// The command operation kind must not have already been specified
228 ///
229 pub fn copy<'d>(mut self, dst_image: &'d Image<T>, dst_origin: [usize; 3]) -> ImageCmd<'c, T>
230 where
231 'd: 'c,
232 {
233 assert!(
234 self.kind.is_unspec(),
235 "ocl::ImageCmd::copy(): Operation kind \
236 already set for this command."
237 );
238 self.kind = ImageCmdKind::Copy {
239 dst_image: dst_image.as_core(),
240 dst_origin,
241 };
242 self
243 }
244
245 /// Specifies that this command will be a copy to image.
246 ///
247 /// If `.block(..)` has been set it will be ignored.
248 ///
249 /// ## Panics
250 ///
251 /// The command operation kind must not have already been specified
252 ///
253 pub fn copy_to_buffer<'d>(mut self, buffer: &'d MemCore, dst_offset: usize) -> ImageCmd<'c, T>
254 where
255 'd: 'c,
256 {
257 assert!(
258 self.kind.is_unspec(),
259 "ocl::ImageCmd::copy_to_buffer(): Operation kind \
260 already set for this command."
261 );
262 self.kind = ImageCmdKind::CopyToBuffer { buffer, dst_offset };
263 self
264 }
265
266 /// Specifies that this command will acquire a GL buffer.
267 ///
268 /// If `.block(..)` has been set it will be ignored.
269 ///
270 /// ## Panics
271 ///
272 /// The command operation kind must not have already been specified
273 ///
274 pub fn gl_acquire(mut self) -> ImageCmd<'c, T> {
275 assert!(
276 self.kind.is_unspec(),
277 "ocl::ImageCmd::gl_acquire(): Operation kind \
278 already set for this command."
279 );
280 self.kind = ImageCmdKind::GLAcquire;
281 self
282 }
283
284 /// Specifies that this command will release a GL buffer.
285 ///
286 /// If `.block(..)` has been set it will be ignored.
287 ///
288 /// ## Panics
289 ///
290 /// The command operation kind must not have already been specified
291 ///
292 pub fn gl_release(mut self) -> ImageCmd<'c, T> {
293 assert!(
294 self.kind.is_unspec(),
295 "ocl::ImageCmd::gl_release(): Operation kind \
296 already set for this command."
297 );
298 self.kind = ImageCmdKind::GLRelease;
299 self
300 }
301
302 /// Specifies that this command will acquire a D3D11 buffer.
303 ///
304 /// If `.block(..)` has been set it will be ignored.
305 ///
306 /// ## Panics
307 ///
308 /// The command operation kind must not have already been specified
309 ///
310 pub fn d3d11_acquire(mut self) -> ImageCmd<'c, T> {
311 assert!(
312 self.kind.is_unspec(),
313 "ocl::ImageCmd::d3d11_acquire(): Operation kind \
314 already set for this command."
315 );
316 self.kind = ImageCmdKind::D3D11Acquire;
317 self
318 }
319
320 /// Specifies that this command will release a D3D11 buffer.
321 ///
322 /// If `.block(..)` has been set it will be ignored.
323 ///
324 /// ## Panics
325 ///
326 /// The command operation kind must not have already been specified
327 ///
328 pub fn d3d11_release(mut self) -> ImageCmd<'c, T> {
329 assert!(
330 self.kind.is_unspec(),
331 "ocl::ImageCmd::d3d11_release(): Operation kind \
332 already set for this command."
333 );
334 self.kind = ImageCmdKind::D3D11Release;
335 self
336 }
337
338 /// Specifies that this command will be a fill.
339 ///
340 /// If `.block(..)` has been set it will be ignored.
341 ///
342 /// ## Panics
343 ///
344 /// The command operation kind must not have already been specified
345 ///
346 pub fn fill(mut self, color: T) -> ImageCmd<'c, T> {
347 assert!(
348 self.kind.is_unspec(),
349 "ocl::ImageCmd::fill(): Operation kind \
350 already set for this command."
351 );
352 self.kind = ImageCmdKind::Fill { color };
353 self
354 }
355
356 /// Specifies a queue to use for this call only.
357 ///
358 /// Overrides the image's default queue if one is set. If no default queue
359 /// is set, this method **must** be called before enqueuing the command.
360 pub fn queue(mut self, queue: &'c Queue) -> ImageCmd<'c, T> {
361 self.queue = Some(queue);
362 self
363 }
364
365 /// Specifies whether or not to block the current thread until completion.
366 ///
367 /// Ignored if this is not a read or write operation.
368 ///
369 /// Default is `block = true`.
370 ///
371 /// ## Safety
372 ///
373 /// When performing non-blocking reads or writes, the caller must ensure
374 /// that the data being read from or written to is not accessed improperly
375 /// until the command completes. Use events (`Event::wait_for`) or the
376 /// command queue (`Queue::finish`) to synchronize.
377 ///
378 /// If possible, prefer instead to use [`::map`] with [`::enq_async`] for
379 /// optimal performance and data integrity.
380 ///
381 /// [`::map`]: struct.ImageMapCmd.html
382 /// [`::enq_async`]: struct.ImageMapCmd.html#method.enq_async
383 ///
384 pub unsafe fn block(mut self, block: bool) -> ImageCmd<'c, T> {
385 self.block = block;
386 self
387 }
388
389 /// Sets the three dimensional offset, the origin point, for an operation.
390 ///
391 /// Defaults to [0, 0, 0] if not set.
392 ///
393 /// ## Panics
394 ///
395 /// The 'shape' may not have already been set to rectangular by the
396 /// `::rect` function.
397 pub fn origin<D>(mut self, origin: D) -> ImageCmd<'c, T>
398 where
399 D: Into<SpatialDims>,
400 {
401 self.origin = origin.into().to_offset().unwrap();
402 self
403 }
404
405 /// Sets the region size for an operation.
406 ///
407 /// Defaults to the full region size of the image(s) as defined when first
408 /// created if not set.
409 ///
410 /// ## Panics [TENATIVE]
411 ///
412 /// Panics if the region is out of range on any of the three dimensions.
413 ///
414 /// [FIXME]: Probably delay checking this until enq().
415 ///
416 pub fn region<D>(mut self, region: D) -> ImageCmd<'c, T>
417 where
418 D: Into<SpatialDims>,
419 {
420 self.region = region.into().to_lens().unwrap();
421 self
422 }
423
424 /// Sets the row and slice pitch for a read or write operation in bytes.
425 ///
426 /// `row_pitch_bytes`: Must be greater than or equal to the region width
427 /// in bytes (region[0] * sizeof(T)).
428 ///
429 /// `slice_pitch: Must be greater than or equal to `row_pitch` * region
430 /// height in bytes (region[1] * sizeof(T)).
431 ///
432 /// Only needs to be set if region has been set to something other than
433 /// the (default) image buffer size.
434 ///
435 pub unsafe fn pitch_bytes(
436 mut self,
437 row_pitch_bytes: usize,
438 slc_pitch_bytes: usize,
439 ) -> ImageCmd<'c, T> {
440 self.row_pitch_bytes = row_pitch_bytes;
441 self.slc_pitch_bytes = slc_pitch_bytes;
442 self
443 }
444
445 /// Specifies an event or list of events to wait on before the command
446 /// will run.
447 ///
448 /// When events generated using the `::enew` method of **other**,
449 /// previously enqueued commands are passed here (either individually or
450 /// as part of an [`EventList`]), this command will not execute until
451 /// those commands have completed.
452 ///
453 /// Using events can compliment the use of queues to order commands by
454 /// creating temporal dependencies between them (where commands in one
455 /// queue must wait for the completion of commands in another). Events can
456 /// also supplant queues altogether when, for example, using out-of-order
457 /// queues.
458 ///
459 /// # Example
460 ///
461 /// ```rust,ignore
462 /// // Create an event list:
463 /// let mut event_list = EventList::new();
464 /// // Enqueue a kernel on `queue_1`, creating an event representing the kernel
465 /// // command in our list:
466 /// kernel.cmd().queue(&queue_1).enew(&mut event_list).enq()?;
467 /// // Read from a buffer using `queue_2`, ensuring the read does not begin until
468 /// // after the kernel command has completed:
469 /// buffer.read(rwvec.clone()).queue(&queue_2).ewait(&event_list).enq_async()?;
470 /// ```
471 ///
472 /// [`EventList`]: struct.EventList.html
473 pub fn ewait<'e, Ewl>(mut self, ewait: Ewl) -> ImageCmd<'c, T>
474 where
475 'e: 'c,
476 Ewl: Into<ClWaitListPtrEnum<'e>>,
477 {
478 self.ewait = Some(ewait.into());
479 self
480 }
481
482 /// Specifies the destination to store a new, optionally created event
483 /// associated with this command.
484 ///
485 /// The destination can be a mutable reference to an empty event (created
486 /// using [`Event::empty`]) or a mutable reference to an event list.
487 ///
488 /// After this command is enqueued, the event in the destination can be
489 /// passed to the `::ewait` method of another command. Doing so will cause
490 /// the other command to wait until this command has completed before
491 /// executing.
492 ///
493 /// Using events can compliment the use of queues to order commands by
494 /// creating temporal dependencies between them (where commands in one
495 /// queue must wait for the completion of commands in another). Events can
496 /// also supplant queues altogether when, for example, using out-of-order
497 /// queues.
498 ///
499 /// # Example
500 ///
501 /// ```rust,ignore
502 /// // Create an event list:
503 /// let mut event = Event::empty();
504 /// // Enqueue a kernel on `queue_1`, creating an event representing the kernel
505 /// // command in our list:
506 /// kernel.cmd().queue(&queue_1).enew(&mut event).enq()?;
507 /// // Read from a buffer using `queue_2`, ensuring the read does not begin until
508 /// // after the kernel command has completed:
509 /// buffer.read(rwvec.clone()).queue(&queue_2).ewait(&event).enq_async()?;
510 /// ```
511 ///
512 /// [`Event::empty`]: struct.Event.html#method.empty
513 pub fn enew<'e, En>(mut self, enew: En) -> ImageCmd<'c, T>
514 where
515 'e: 'c,
516 En: Into<ClNullEventPtrEnum<'e>>,
517 {
518 self.enew = Some(enew.into());
519 self
520 }
521
522 /// Enqueues this command.
523 ///
524 /// * TODO: FOR COPY, FILL, AND COPYTOBUFFER -- ENSURE PITCHES ARE BOTH
525 /// UNSET.
526 pub fn enq(self) -> OclResult<()> {
527 let queue = match self.queue {
528 Some(q) => q,
529 None => return Err("ImageCmd::enq: No queue set.".into()),
530 };
531
532 match self.kind {
533 ImageCmdKind::Read { data } => {
534 unsafe { core::enqueue_read_image(queue, self.obj_core, self.block,
535 self.origin, self.region, self.row_pitch_bytes, self.slc_pitch_bytes, data, self.ewait,
536 self.enew) }
537 },
538 ImageCmdKind::Write { data } => {
539 unsafe {
540 core::enqueue_write_image(queue, self.obj_core, self.block,
541 self.origin, self.region, self.row_pitch_bytes, self.slc_pitch_bytes, data, self.ewait,
542 self.enew)
543 }
544 },
545 ImageCmdKind::Copy { dst_image, dst_origin } => {
546 core::enqueue_copy_image(queue, self.obj_core, dst_image, self.origin,
547 dst_origin, self.region, self.ewait, self.enew)
548 },
549 ImageCmdKind::CopyToBuffer { buffer, dst_offset } => {
550 core::enqueue_copy_image_to_buffer::<T, _, _, _>(queue, self.obj_core, buffer, self.origin,
551 self.region, dst_offset, self.ewait, self.enew)
552 },
553
554 #[cfg(not(feature="opencl_vendor_mesa"))]
555 ImageCmdKind::GLAcquire => {
556 // core::enqueue_acquire_gl_buffer(queue, self.obj_core, self.ewait, self.enew)
557 let buf_slc = unsafe { std::slice::from_raw_parts(self.obj_core, 1) };
558 core::enqueue_acquire_gl_objects(queue, buf_slc, self.ewait, self.enew)
559 },
560
561 #[cfg(not(feature="opencl_vendor_mesa"))]
562 ImageCmdKind::GLRelease => {
563 // core::enqueue_release_gl_buffer(queue, self.obj_core, self.ewait, self.enew)
564 let buf_slc = unsafe { std::slice::from_raw_parts(self.obj_core, 1) };
565 core::enqueue_release_gl_objects(queue, buf_slc, self.ewait, self.enew)
566 },
567
568 ImageCmdKind::D3D11Acquire => {
569 match self.ext_fns {
570 Some(fns) => {
571 let buf_slc = unsafe { std::slice::from_raw_parts(self.obj_core, 1) };
572 core::enqueue_acquire_d3d11_objects(queue, buf_slc, self.ewait, self.enew, fns)
573 }
574 None => Err("ocl::ImageCmd::enq(): The function pointer to clEnqueueAcquireD3D11Objects was not resolved.".into())
575 }
576 },
577 ImageCmdKind::D3D11Release => {
578 match self.ext_fns {
579 Some(fns) => {
580 let buf_slc = unsafe { std::slice::from_raw_parts(self.obj_core, 1) };
581 core::enqueue_release_d3d11_objects(queue, buf_slc, self.ewait, self.enew, fns)
582 }
583 None => Err("ocl::ImageCmd::enq(): The function pointer to clEnqueueReleaseD3D11Objects was not resolved.".into())
584 }
585 },
586
587 ImageCmdKind::Unspecified => Err("ocl::ImageCmd::enq(): No operation \
588 specified. Use '.read(...)', 'write(...)', etc. before calling '.enq()'.".into()),
589 _ => unimplemented!(),
590 }.map_err(OclError::from)
591 }
592}
593
594/// A buffer command builder used to enqueue maps.
595///
596/// See [SDK][map_buffer] docs for more details.
597///
598/// [map_buffer]: https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/clEnqueueMapBuffer.html
599
600// const size_t * origin ,
601// const size_t * region ,
602// size_t *image_row_pitch ,
603// size_t *image_slice_pitch ,
604
605pub struct ImageMapCmd<'c, T>
606where
607 T: 'c,
608{
609 cmd: ImageCmd<'c, T>,
610 flags: Option<MapFlags>,
611}
612
613impl<'c, T> ImageMapCmd<'c, T>
614where
615 T: OclPrm,
616{
617 /// Specifies the flags to be used with this map command.
618 ///
619 /// See [SDK] docs for more details.
620 ///
621 /// [SDK]: https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/clEnqueueMapBuffer.html
622 pub fn flags(mut self, flags: MapFlags) -> ImageMapCmd<'c, T> {
623 self.flags = Some(flags);
624 self
625 }
626
627 /// Sets the three dimensional offset, the origin point, for an operation.
628 ///
629 /// Defaults to [0, 0, 0] if not set.
630 ///
631 /// ## Panics
632 ///
633 /// The 'shape' may not have already been set to rectangular by the
634 /// `::rect` function.
635 pub fn origin(mut self, origin: [usize; 3]) -> ImageMapCmd<'c, T> {
636 self.cmd.origin = origin;
637 self
638 }
639
640 /// Sets the region size for an operation.
641 ///
642 /// Defaults to the full region size of the image(s) as defined when first
643 /// created if not set.
644 ///
645 /// ## Panics [TENATIVE]
646 ///
647 /// Panics if the region is out of range on any of the three dimensions.
648 ///
649 /// [FIXME]: Probably delay checking this until enq().
650 ///
651 pub fn region(mut self, region: [usize; 3]) -> ImageMapCmd<'c, T> {
652 self.cmd.region = region;
653 self
654 }
655
656 /// Specifies an event or list of events to wait on before the command
657 /// will run.
658 ///
659 /// When events generated using the `::enew` method of **other**,
660 /// previously enqueued commands are passed here (either individually or
661 /// as part of an [`EventList`]), this command will not execute until
662 /// those commands have completed.
663 ///
664 /// Using events can compliment the use of queues to order commands by
665 /// creating temporal dependencies between them (where commands in one
666 /// queue must wait for the completion of commands in another). Events can
667 /// also supplant queues altogether when, for example, using out-of-order
668 /// queues.
669 ///
670 /// # Example
671 ///
672 /// ```rust,ignore
673 /// // Create an event list:
674 /// let mut event_list = EventList::new();
675 /// // Enqueue a kernel on `queue_1`, creating an event representing the kernel
676 /// // command in our list:
677 /// kernel.cmd().queue(&queue_1).enew(&mut event_list).enq()?;
678 /// // Read from a buffer using `queue_2`, ensuring the read does not begin until
679 /// // after the kernel command has completed:
680 /// buffer.read(rwvec.clone()).queue(&queue_2).ewait(&event_list).enq_async()?;
681 /// ```
682 ///
683 /// [`EventList`]: struct.EventList.html
684 pub fn ewait<'e, Ewl>(mut self, ewait: Ewl) -> ImageMapCmd<'c, T>
685 where
686 'e: 'c,
687 Ewl: Into<ClWaitListPtrEnum<'e>>,
688 {
689 self.cmd.ewait = Some(ewait.into());
690 self
691 }
692
693 /// Specifies the destination to store a new, optionally created event
694 /// associated with this command.
695 ///
696 /// The destination can be a mutable reference to an empty event (created
697 /// using [`Event::empty`]) or a mutable reference to an event list.
698 ///
699 /// After this command is enqueued, the event in the destination can be
700 /// passed to the `::ewait` method of another command. Doing so will cause
701 /// the other command to wait until this command has completed before
702 /// executing.
703 ///
704 /// Using events can compliment the use of queues to order commands by
705 /// creating temporal dependencies between them (where commands in one
706 /// queue must wait for the completion of commands in another). Events can
707 /// also supplant queues altogether when, for example, using out-of-order
708 /// queues.
709 ///
710 /// # Example
711 ///
712 /// ```rust,ignore
713 /// // Create an event list:
714 /// let mut event = Event::empty();
715 /// // Enqueue a kernel on `queue_1`, creating an event representing the kernel
716 /// // command in our list:
717 /// kernel.cmd().queue(&queue_1).enew(&mut event).enq()?;
718 /// // Read from a buffer using `queue_2`, ensuring the read does not begin until
719 /// // after the kernel command has completed:
720 /// buffer.read(rwvec.clone()).queue(&queue_2).ewait(&event).enq_async()?;
721 /// ```
722 ///
723 /// [`Event::empty`]: struct.Event.html#method.empty
724 pub fn enew<'e, En>(mut self, enew: En) -> ImageMapCmd<'c, T>
725 where
726 'e: 'c,
727 En: Into<ClNullEventPtrEnum<'e>>,
728 {
729 self.cmd.enew = Some(enew.into());
730 self
731 }
732
733 /// Enqueues this command.
734 ///
735 /// * TODO: FOR COPY, FILL, AND COPYTOBUFFER -- ENSURE PITCHES ARE BOTH UNSET.
736 #[allow(unused_variables, unreachable_code)]
737 pub fn enq(self) -> OclResult<MemMap<T>> {
738 let queue = match self.cmd.queue {
739 Some(q) => q,
740 None => return Err("ImageCmd::enq: No queue set.".into()),
741 };
742
743 let flags = self.flags.unwrap_or(MapFlags::empty());
744
745 match self.cmd.kind {
746 ImageCmdKind::Map => {
747 // try!(check_len(self.cmd.to_len, data.len(), offset));
748
749 let mut row_pitch_bytes = 0usize;
750 let mut slc_pitch_bytes = 0usize;
751
752 unsafe {
753 let mm_core = core::enqueue_map_image::<T, _, _, _>(
754 queue,
755 self.cmd.obj_core,
756 self.cmd.block,
757 flags,
758 self.cmd.origin,
759 self.cmd.region,
760 &mut row_pitch_bytes,
761 &mut slc_pitch_bytes,
762 self.cmd.ewait,
763 self.cmd.enew,
764 )?;
765
766 let len_bytes = if slc_pitch_bytes == 0 {
767 // 1D or 2D image.
768 unimplemented!();
769 } else {
770 // 1D image array, 2D image array, or 3D image.
771 unimplemented!();
772 };
773
774 // let unmap_event = None;
775
776 // * TODO: Create a special container for mapped images
777 // that can take into account row and slice pitch. It
778 // cannot deref into a &[T] as the size of rows (and
779 // slices) can vary with byte-sized precision.
780
781 // Ok(MemMap::new(mm_core, 0, unmap_event, self.cmd.obj_core.clone(),
782 // queue.core().clone()))
783 }
784 }
785 _ => unreachable!(),
786 }
787 }
788}
789
790/// A section of device memory which represents one or many images.
791///
792/// Use `::builder` for an easy way to create. [UNIMPLEMENTED]
793///
794#[derive(Clone, Debug)]
795pub struct Image<T: OclPrm> {
796 obj_core: MemCore,
797 queue: Option<Queue>,
798 dims: SpatialDims,
799 pixel_element_len: usize,
800 extension_functions: Option<core::ExtensionFunctions>,
801 _pixel: PhantomData<T>,
802}
803
804impl<T: OclPrm> Image<T> {
805 /// Returns a list of supported image formats.
806 pub fn supported_formats(
807 context: &Context,
808 flags: MemFlags,
809 mem_obj_type: MemObjectType,
810 ) -> OclResult<Vec<ImageFormatParseResult>> {
811 core::get_supported_image_formats(context, flags, mem_obj_type).map_err(OclError::from)
812 }
813
814 /// Returns an `ImageBuilder`. This is the recommended method to create
815 /// a new `Image`.
816 pub fn builder<'a>() -> ImageBuilder<'a, T> {
817 ImageBuilder::new()
818 }
819
820 /// Returns a new `Image`.
821 ///
822 /// Prefer `::builder` to create a new image.
823 pub unsafe fn new<'o, Q>(
824 que_ctx: Q,
825 flags: MemFlags,
826 image_format: ImageFormat,
827 image_desc: ImageDescriptor,
828 host_data: Option<&[T]>,
829 ) -> OclResult<Image<T>>
830 where
831 Q: Into<QueCtx<'o>>,
832 {
833 let que_ctx = que_ctx.into();
834 let context = que_ctx.context_cloned();
835 let device_versions = context.device_versions()?;
836
837 let obj_core = core::create_image(
838 &context,
839 flags,
840 &image_format,
841 &image_desc,
842 host_data,
843 Some(&device_versions),
844 )?;
845
846 let pixel_element_len = match core::get_image_info(&obj_core, ImageInfo::ElementSize)? {
847 ImageInfoResult::ElementSize(s) => s / mem::size_of::<T>(),
848 _ => {
849 return Err("ocl::Image::element_len(): \
850 Unexpected 'ImageInfoResult' variant."
851 .into())
852 }
853 };
854
855 let dims = [
856 image_desc.image_width,
857 image_desc.image_height,
858 image_desc.image_depth,
859 ]
860 .into();
861
862 let new_img = Image {
863 obj_core,
864 queue: que_ctx.into(),
865 dims,
866 pixel_element_len,
867 extension_functions: None,
868 _pixel: PhantomData,
869 };
870
871 Ok(new_img)
872 }
873
874 /// Returns a new `Image` from an existant GL texture2D/3D.
875 ///
876 /// Remember to specify the GL context when creating the CL context,
877 /// using `.properties(ocl_interop::get_properties_list())`
878 ///
879 /// Don't forget to `.cmd().gl_acquire().enq()` before using it and
880 /// `.cmd().gl_release().enq()` after.
881 ///
882 // [WORK IN PROGRESS]
883 #[cfg(not(feature = "opencl_vendor_mesa"))]
884 pub fn from_gl_texture<'o, Q>(
885 que_ctx: Q,
886 flags: MemFlags,
887 image_desc: ImageDescriptor,
888 texture_target: GlTextureTarget,
889 miplevel: cl_GLint,
890 texture: cl_GLuint,
891 ) -> OclResult<Image<T>>
892 where
893 Q: Into<QueCtx<'o>>,
894 {
895 let que_ctx = que_ctx.into();
896 let context = que_ctx.context_cloned();
897 let device_versions = context.device_versions()?;
898
899 if texture_target == GlTextureTarget::GlTextureBuffer && miplevel != 0 {
900 return Err(
901 "If texture_target is GL_TEXTURE_BUFFER, miplevel must be 0.\
902 Implementations may return CL_INVALID_OPERATION for miplevel values > 0"
903 .into(),
904 );
905 }
906
907 let obj_core = unsafe {
908 core::create_from_gl_texture(
909 &context,
910 texture_target as u32,
911 miplevel,
912 texture,
913 flags,
914 Some(&device_versions),
915 )?
916 };
917
918 // FIXME can I do this from a GLTexture ?
919 let pixel_element_len = match core::get_image_info(&obj_core, ImageInfo::ElementSize)? {
920 ImageInfoResult::ElementSize(s) => s / mem::size_of::<T>(),
921 _ => {
922 return Err("ocl::Image::element_len(): Unexpected \
923 'ImageInfoResult' variant."
924 .into())
925 }
926 };
927
928 let dims = [
929 image_desc.image_width,
930 image_desc.image_height,
931 image_desc.image_depth,
932 ]
933 .into();
934
935 let new_img = Image {
936 obj_core,
937 queue: que_ctx.into(),
938 dims,
939 pixel_element_len,
940 extension_functions: None,
941 _pixel: PhantomData,
942 };
943
944 Ok(new_img)
945 }
946
947 /// Returns a new `Image` from an existant renderbuffer.
948 ///
949 /// Remember to specify the GL context when creating the CL context,
950 /// using `.properties(ocl_interop::get_properties_list())`
951 ///
952 /// Don't forget to `.cmd().gl_acquire().enq()` before using it and
953 /// `.cmd().gl_release().enq()` after.
954 ///
955 // [WORK IN PROGRESS]
956 #[cfg(not(feature = "opencl_vendor_mesa"))]
957 pub fn from_gl_renderbuffer<'o, Q>(
958 que_ctx: Q,
959 flags: MemFlags,
960 image_desc: ImageDescriptor,
961 renderbuffer: cl_GLuint,
962 ) -> OclResult<Image<T>>
963 where
964 Q: Into<QueCtx<'o>>,
965 {
966 let que_ctx = que_ctx.into();
967 let context = que_ctx.context_cloned();
968
969 let obj_core = unsafe { core::create_from_gl_renderbuffer(&context, renderbuffer, flags)? };
970
971 // FIXME can I do this from a renderbuffer ?
972 let pixel_element_len = match core::get_image_info(&obj_core, ImageInfo::ElementSize)? {
973 ImageInfoResult::ElementSize(s) => s / mem::size_of::<T>(),
974 _ => {
975 return Err("ocl::Image::element_len(): \
976 Unexpected 'ImageInfoResult' variant."
977 .into())
978 }
979 };
980
981 let dims = [image_desc.image_width, image_desc.image_height].into();
982
983 let new_img = Image {
984 obj_core,
985 queue: que_ctx.into(),
986 dims,
987 pixel_element_len,
988 extension_functions: None,
989 _pixel: PhantomData,
990 };
991
992 Ok(new_img)
993 }
994
995 /// Returns a new `Image` from an existant ID3D11Texture2D.
996 ///
997 /// Remember to specify the D3D11 device when creating the CL context,
998 /// using `.properties()` and `.set_property_value(ContextPropertyValue::D3d11DeviceKhr(<pointer to ID3D11Device>))`
999 ///
1000 /// Don't forget to `.cmd().d3d11_acquire().enq()` before using it and
1001 /// `.cmd().d3d11_release().enq()` after.
1002 ///
1003 pub fn from_d3d11_texture2d<'o, Q>(
1004 que_ctx: Q,
1005 flags: MemFlags,
1006 image_desc: ImageDescriptor,
1007 texture: cl_id3d11_texture2d,
1008 subresource: u32,
1009 ) -> OclResult<Image<T>>
1010 where
1011 Q: Into<QueCtx<'o>>,
1012 {
1013 let que_ctx = que_ctx.into();
1014 let context = que_ctx.context_cloned();
1015 let device_versions = context.device_versions()?;
1016
1017 let extension_fns = match context.platform()? {
1018 Some(platform) => core::ExtensionFunctions::resolve_all(*platform)?,
1019 _ => {
1020 return Err(
1021 "ocl::Image::from_d3d11_texture2d(): Platform must be set in context.".into(),
1022 )
1023 }
1024 };
1025
1026 let obj_core = unsafe {
1027 core::create_from_d3d11_texture2d(
1028 &context,
1029 texture,
1030 subresource as cl_uint,
1031 flags,
1032 Some(&device_versions),
1033 &extension_fns,
1034 )?
1035 };
1036
1037 let pixel_element_len = match core::get_image_info(&obj_core, ImageInfo::ElementSize)? {
1038 ImageInfoResult::ElementSize(s) => s / mem::size_of::<T>(),
1039 _ => {
1040 return Err("ocl::Image::element_len(): Unexpected \
1041 'ImageInfoResult' variant."
1042 .into())
1043 }
1044 };
1045
1046 let dims = [
1047 image_desc.image_width,
1048 image_desc.image_height,
1049 image_desc.image_depth,
1050 ]
1051 .into();
1052
1053 let new_img = Image {
1054 obj_core,
1055 queue: que_ctx.into(),
1056 dims,
1057 pixel_element_len,
1058 extension_functions: Some(extension_fns),
1059 _pixel: PhantomData,
1060 };
1061
1062 Ok(new_img)
1063 }
1064
1065 /// Returns a new `Image` from an existant ID3D11Texture2D.
1066 ///
1067 /// Remember to specify the D3D11 device when creating the CL context,
1068 /// using `.properties()` and `.set_property_value(ContextPropertyValue::D3d11DeviceKhr(<pointer to ID3D11Device>))`
1069 ///
1070 /// Don't forget to `.cmd().d3d11_acquire().enq()` before using it and
1071 /// `.cmd().d3d11_release().enq()` after.
1072 ///
1073 pub fn from_d3d11_texture3d<'o, Q>(
1074 que_ctx: Q,
1075 flags: MemFlags,
1076 image_desc: ImageDescriptor,
1077 texture: cl_id3d11_texture3d,
1078 subresource: u32,
1079 ) -> OclResult<Image<T>>
1080 where
1081 Q: Into<QueCtx<'o>>,
1082 {
1083 let que_ctx = que_ctx.into();
1084 let context = que_ctx.context_cloned();
1085 let device_versions = context.device_versions()?;
1086
1087 let extension_fns = match context.platform()? {
1088 Some(platform) => core::ExtensionFunctions::resolve_all(*platform)?,
1089 _ => {
1090 return Err(
1091 "ocl::Image::from_d3d11_texture3d(): Platform must be set in context.".into(),
1092 )
1093 }
1094 };
1095
1096 let obj_core = unsafe {
1097 core::create_from_d3d11_texture3d(
1098 &context,
1099 texture,
1100 subresource as cl_uint,
1101 flags,
1102 Some(&device_versions),
1103 &extension_fns,
1104 )?
1105 };
1106
1107 let pixel_element_len = match core::get_image_info(&obj_core, ImageInfo::ElementSize)? {
1108 ImageInfoResult::ElementSize(s) => s / mem::size_of::<T>(),
1109 _ => {
1110 return Err("ocl::Image::element_len(): Unexpected \
1111 'ImageInfoResult' variant."
1112 .into())
1113 }
1114 };
1115
1116 let dims = [
1117 image_desc.image_width,
1118 image_desc.image_height,
1119 image_desc.image_depth,
1120 ]
1121 .into();
1122
1123 let new_img = Image {
1124 obj_core,
1125 queue: que_ctx.into(),
1126 dims,
1127 pixel_element_len,
1128 extension_functions: Some(extension_fns),
1129 _pixel: PhantomData,
1130 };
1131
1132 Ok(new_img)
1133 }
1134
1135 /// Returns an image command builder used to read, write, copy, etc.
1136 ///
1137 /// Call `.enq()` to enqueue the command.
1138 ///
1139 /// See the [command builder documentation](struct.ImageCmd)
1140 /// for more details.
1141 pub fn cmd(&self) -> ImageCmd<T> {
1142 ImageCmd::new(
1143 self.queue.as_ref(),
1144 &self.obj_core,
1145 self.dims.to_lens().expect("ocl::Image::cmd"),
1146 self.extension_functions.as_ref(),
1147 )
1148 }
1149
1150 /// Returns an image command builder set to read.
1151 ///
1152 /// Call `.enq()` to enqueue the command.
1153 ///
1154 /// See the [command builder documentation](struct.ImageCmd#method.read)
1155 /// for more details.
1156 pub fn read<'c, 'd>(&'c self, data: &'d mut [T]) -> ImageCmd<'c, T>
1157 where
1158 'd: 'c,
1159 {
1160 self.cmd().read(data)
1161 }
1162
1163 /// Returns an image command builder set to write.
1164 ///
1165 /// Call `.enq()` to enqueue the command.
1166 ///
1167 /// See the [command builder documentation](struct.ImageCmd#method.write)
1168 /// for more details.
1169 pub fn write<'c, 'd>(&'c self, data: &'d [T]) -> ImageCmd<'c, T>
1170 where
1171 'd: 'c,
1172 {
1173 self.cmd().write(data)
1174 }
1175
1176 /// Returns a command builder used to map data for reading or writing.
1177 ///
1178 /// Call `.enq()` to enqueue the command.
1179 ///
1180 /// ## Safety
1181 ///
1182 /// The caller must ensure that only one mapping of a particular memory
1183 /// region exists at a time.
1184 ///
1185 /// See the [command builder documentation](struct.ImageCmd#method.map)
1186 /// for more details.
1187 ///
1188 #[inline]
1189 pub unsafe fn map<'c>(&'c self) -> ImageMapCmd<'c, T> {
1190 unimplemented!();
1191 // self.cmd().map()
1192 }
1193
1194 // /// Specifies that this command will be a copy operation.
1195 // ///
1196 // /// Call `.enq()` to enqueue the command.
1197 // ///
1198 // /// See the [command builder documentation](struct.ImageCmd#method.copy)
1199 // /// for more details.
1200 // ///
1201 // #[inline]
1202 // pub fn copy<'c, M>(&'c self, dst_buffer: &'c M, dst_offset: Option<usize>, len: Option<usize>)
1203 // -> BufferCmd<'c, T>
1204 // where M: AsMem
1205 // {
1206 // self.cmd().copy(dst_buffer, dst_offset, len)
1207 // }
1208
1209 /// Changes the default queue.
1210 ///
1211 /// Returns a ref for chaining i.e.:
1212 ///
1213 /// `image.set_default_queue(queue).write(....);`
1214 ///
1215 /// [NOTE]: Even when used as above, the queue is changed permanently,
1216 /// not just for the one call. Changing the queue is cheap so feel free
1217 /// to change as often as needed.
1218 ///
1219 /// The new queue must be associated with a valid device.
1220 ///
1221 pub fn set_default_queue<'a>(&'a mut self, queue: Queue) -> &'a mut Image<T> {
1222 // self.command_queue_obj_core = queue.core().clone();
1223 self.queue = Some(queue);
1224 self
1225 }
1226
1227 /// Returns a reference to the default queue.
1228 pub fn default_queue(&self) -> Option<&Queue> {
1229 self.queue.as_ref()
1230 }
1231
1232 /// Returns this image's dimensions.
1233 pub fn dims(&self) -> &SpatialDims {
1234 &self.dims
1235 }
1236
1237 /// Returns the total number of pixels in this image.
1238 pub fn pixel_count(&self) -> usize {
1239 self.dims.to_len()
1240 }
1241
1242 /// Returns the length of each pixel element.
1243 pub fn pixel_element_len(&self) -> usize {
1244 self.pixel_element_len
1245 }
1246
1247 /// Returns the total number of pixel elements in this image. Equivalent to its length.
1248 pub fn element_count(&self) -> usize {
1249 self.pixel_count() * self.pixel_element_len()
1250 }
1251
1252 /// Get information about this image.
1253 pub fn info(&self, info_kind: ImageInfo) -> OclResult<ImageInfoResult> {
1254 // match core::get_image_info(&self.obj_core, info_kind) {
1255 // Ok(res) => res,
1256 // Err(err) => ImageInfoResult::Error(Box::new(err)),
1257 // }
1258 core::get_image_info(&self.obj_core, info_kind).map_err(OclError::from)
1259 }
1260
1261 /// Returns info about this image's memory.
1262 pub fn mem_info(&self, info_kind: MemInfo) -> OclResult<MemInfoResult> {
1263 // match core::get_mem_object_info(&self.obj_core, info_kind) {
1264 // Ok(res) => res,
1265 // Err(err) => MemInfoResult::Error(Box::new(err)),
1266 // }
1267 core::get_mem_object_info(&self.obj_core, info_kind).map_err(OclError::from)
1268 }
1269
1270 /// Returns a reference to the core pointer wrapper, usable by functions in
1271 /// the `core` module.
1272 #[inline]
1273 pub fn as_core(&self) -> &MemCore {
1274 &self.obj_core
1275 }
1276
1277 /// Format image info.
1278 fn fmt_info(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1279 f.debug_struct("Image")
1280 .field("ElementSize", &self.info(ImageInfo::ElementSize))
1281 .field("RowPitch", &self.info(ImageInfo::RowPitch))
1282 .field("SlicePitch", &self.info(ImageInfo::SlicePitch))
1283 .field("Width", &self.info(ImageInfo::Width))
1284 .field("Height", &self.info(ImageInfo::Height))
1285 .field("Depth", &self.info(ImageInfo::Depth))
1286 .field("ArraySize", &self.info(ImageInfo::ArraySize))
1287 .field("Buffer", &self.info(ImageInfo::Buffer))
1288 .field("NumMipLevels", &self.info(ImageInfo::NumMipLevels))
1289 .field("NumSamples", &self.info(ImageInfo::NumSamples))
1290 .finish()
1291 }
1292
1293 /// Format image mem info.
1294 fn fmt_mem_info(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1295 f.debug_struct("Mem")
1296 .field("Type", &self.mem_info(MemInfo::Type))
1297 .field("Flags", &self.mem_info(MemInfo::Flags))
1298 .field("Size", &self.mem_info(MemInfo::Size))
1299 .field("HostPtr", &self.mem_info(MemInfo::HostPtr))
1300 .field("MapCount", &self.mem_info(MemInfo::MapCount))
1301 .field("ReferenceCount", &self.mem_info(MemInfo::ReferenceCount))
1302 .field("Context", &self.mem_info(MemInfo::Context))
1303 .field(
1304 "AssociatedMemobject",
1305 &self.mem_info(MemInfo::AssociatedMemobject),
1306 )
1307 .field("Offset", &self.mem_info(MemInfo::Offset))
1308 .finish()
1309 }
1310}
1311
1312impl<T: OclPrm> std::fmt::Display for Image<T> {
1313 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1314 self.fmt_info(f)?;
1315 write!(f, " ")?;
1316 self.fmt_mem_info(f)
1317 }
1318}
1319
1320impl<T: OclPrm> Deref for Image<T> {
1321 type Target = MemCore;
1322
1323 fn deref(&self) -> &MemCore {
1324 &self.obj_core
1325 }
1326}
1327
1328impl<T: OclPrm> DerefMut for Image<T> {
1329 fn deref_mut(&mut self) -> &mut MemCore {
1330 &mut self.obj_core
1331 }
1332}
1333
1334impl<T: OclPrm> AsMem<T> for Image<T> {
1335 fn as_mem(&self) -> &MemCore {
1336 &self.obj_core
1337 }
1338}
1339
1340unsafe impl<'a, T> MemCmdRw for Image<T> where T: OclPrm {}
1341unsafe impl<'a, T> MemCmdRw for &'a Image<T> where T: OclPrm {}
1342unsafe impl<'a, T> MemCmdRw for &'a mut Image<T> where T: OclPrm {}
1343unsafe impl<'a, T> MemCmdAll for Image<T> where T: OclPrm {}
1344unsafe impl<'a, T> MemCmdAll for &'a Image<T> where T: OclPrm {}
1345unsafe impl<'a, T> MemCmdAll for &'a mut Image<T> where T: OclPrm {}
1346
1347/// A builder for `Image`.
1348#[must_use = "builders do nothing unless '::build' is called"]
1349pub struct ImageBuilder<'a, T>
1350where
1351 T: 'a,
1352{
1353 queue_option: Option<QueCtx<'a>>,
1354 flags: MemFlags,
1355 host_slice: HostSlice<'a, T>,
1356 image_format: ImageFormat,
1357 image_desc: ImageDescriptor,
1358 _pixel: PhantomData<T>,
1359}
1360
1361impl<'a, T> ImageBuilder<'a, T>
1362where
1363 T: 'a + OclPrm,
1364{
1365 /// Returns a new `ImageBuilder` with very basic defaults.
1366 ///
1367 /// ## Defaults
1368 ///
1369 /// * Flags:
1370 ///
1371 /// ```rust,ignore
1372 /// ocl::MEM_READ_WRITE
1373 /// ```
1374 ///
1375 /// * Image Format:
1376 ///
1377 /// ```rust,ignore
1378 /// ocl::ImageFormat {
1379 /// channel_order: ocl::ImageChannelOrder::Rgba,
1380 /// channel_data_type: ocl::ImageChannelDataType::SnormInt8,
1381 /// }
1382 /// ```
1383 ///
1384 /// * Descriptor (stores everything else - width, height, pitch, etc.):
1385 ///
1386 /// ```rust,ignore
1387 /// ImageDescriptor::new(MemObjectType::Image1d, 0, 0, 0, 0, 0, 0, None)
1388 /// ```
1389 ///
1390 /// ## Reference
1391 ///
1392 /// See the [official SDK documentation] for more information.
1393 ///
1394 /// Some descriptions here are adapted from various SDK docs.
1395 ///
1396 /// [official SDK docs]: https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clCreateImage.html
1397 pub fn new() -> ImageBuilder<'a, T> {
1398 ImageBuilder {
1399 queue_option: None,
1400 flags: core::MEM_READ_WRITE,
1401 host_slice: HostSlice::None,
1402 image_format: ImageFormat::new_rgba(),
1403 image_desc: ImageDescriptor::new(MemObjectType::Image1d, 0, 0, 0, 0, 0, 0, None),
1404 _pixel: PhantomData,
1405 }
1406 }
1407
1408 /// Sets the context with which to associate the buffer.
1409 ///
1410 /// May not be used in combination with `::queue` (use one or the other).
1411 pub fn context<'o>(mut self, context: &'o Context) -> ImageBuilder<'a, T>
1412 where
1413 'o: 'a,
1414 {
1415 assert!(self.queue_option.is_none());
1416 self.queue_option = Some(QueCtx::Context(context));
1417 self
1418 }
1419
1420 /// Sets the default queue.
1421 ///
1422 /// If this is set, the context associated with the `default_queue` will
1423 /// be used when creating the buffer (use one or the other).
1424 pub fn queue<'b>(mut self, default_queue: Queue) -> ImageBuilder<'a, T> {
1425 assert!(self.queue_option.is_none());
1426 self.queue_option = Some(QueCtx::Queue(default_queue));
1427 self
1428 }
1429
1430 /// Sets the flags used when creating the image.
1431 ///
1432 /// Defaults to `flags::MEM_READ_WRITE` aka.
1433 /// `MemFlags::new().read_write()` if this is not set. See the [SDK Docs]
1434 /// for more information about flags. Note that the names of all flags in
1435 /// this library have the `CL_` prefix removed for brevity.
1436 ///
1437 /// ### Panics
1438 ///
1439 /// Due to its unsafety, setting the
1440 /// `MEM_USE_HOST_PTR`/`MemFlags::new()::use_host_ptr()` flag will cause a
1441 /// panic. Use the `::use_host_slice` method instead.
1442 ///
1443 /// [SDK Docs]: https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clCreateBuffer.html
1444 pub fn flags(mut self, flags: MemFlags) -> ImageBuilder<'a, T> {
1445 assert!(
1446 !flags.contains(MemFlags::new().use_host_ptr()),
1447 "The `ImageBuilder::flags` method may not be used to set the \
1448 `MEM_USE_HOST_PTR` flag. Use the `::use_host_ptr` method instead."
1449 );
1450 self.flags = flags;
1451 self
1452 }
1453
1454 /// Specifies a region of host memory to use as storage for the image.
1455 ///
1456 /// OpenCL implementations are allowed to cache the image contents
1457 /// pointed to by `host_slice` in device memory. This cached copy can be
1458 /// used when kernels are executed on a device.
1459 ///
1460 /// The result of OpenCL commands that operate on multiple image objects
1461 /// created with the same `host_slice` or overlapping host regions is
1462 /// considered to be undefined
1463 ///
1464 /// Refer to the [description of the alignment][align_rules] rules for
1465 /// `host_slice` for memory objects (buffer and images) created using
1466 /// this method.
1467 ///
1468 /// Automatically sets the `flags::MEM_USE_HOST_PTR` aka.
1469 /// `MemFlags::new().use_host_ptr()` flag.
1470 ///
1471 /// ### Panics
1472 ///
1473 /// `::copy_host_slice` or `::use_host_slice` must not have already been
1474 /// called.
1475 ///
1476 /// ### Safety
1477 ///
1478 /// The caller must ensure that `host_slice` lives until the image is
1479 /// destroyed. The caller must also ensure that only one image uses
1480 /// `host_slice` and that it is not tampered with inappropriately.
1481 ///
1482 /// [align_rules]: https://www.khronos.org/registry/OpenCL/sdk/1.2/docs/man/xhtml/dataTypes.html
1483 pub unsafe fn use_host_slice<'d>(mut self, host_slice: &'d [T]) -> ImageBuilder<'a, T>
1484 where
1485 'd: 'a,
1486 {
1487 assert!(
1488 self.host_slice.is_none(),
1489 "ImageBuilder::use_host_slice: \
1490 A host slice has already been specified."
1491 );
1492 self.host_slice = HostSlice::Use(host_slice);
1493 self
1494 }
1495
1496 /// Specifies a region of memory to copy into the image upon creation.
1497 ///
1498 /// Automatically sets the `flags::MEM_COPY_HOST_PTR` aka.
1499 /// `MemFlags::new().copy_host_ptr()` flag.
1500 ///
1501 /// ### Panics
1502 ///
1503 /// `::copy_host_slice` or `::use_host_slice` must not have already been
1504 /// called.
1505 ///
1506 pub fn copy_host_slice<'d>(mut self, host_slice: &'d [T]) -> ImageBuilder<'a, T>
1507 where
1508 'd: 'a,
1509 {
1510 assert!(
1511 self.host_slice.is_none(),
1512 "ImageBuilder::copy_host_slice: \
1513 A host slice has already been specified."
1514 );
1515 self.host_slice = HostSlice::Copy(host_slice);
1516 self
1517 }
1518
1519 pub fn channel_order(mut self, order: ImageChannelOrder) -> ImageBuilder<'a, T> {
1520 self.image_format.channel_order = order;
1521 self
1522 }
1523
1524 pub fn channel_data_type(mut self, data_type: ImageChannelDataType) -> ImageBuilder<'a, T> {
1525 self.image_format.channel_data_type = data_type;
1526 self
1527 }
1528
1529 /// Sets the type of image (technically the type of memory buffer).
1530 ///
1531 /// Describes the image type and must be either `Image1d`, `Image1dBuffer`,
1532 /// `Image1dArray`, `Image2d`, `Image2dArray`, or `Image3d`.
1533 ///
1534 pub fn image_type(mut self, image_type: MemObjectType) -> ImageBuilder<'a, T> {
1535 self.image_desc.image_type = image_type;
1536 self
1537 }
1538
1539 /// The width, height, and depth of an image or image array:
1540 ///
1541 /// Some notes adapted from SDK docs:
1542 ///
1543 /// ## Width
1544 ///
1545 /// The width of the image in pixels. For a 2D image and image array, the
1546 /// image width must be ≤ `DeviceInfo::Image2dMaxWidth`. For a 3D image, the
1547 /// image width must be ≤ `DeviceInfo::Image3dMaxWidth`. For a 1D image buffer,
1548 /// the image width must be ≤ `DeviceInfo::ImageMaxBufferSize`. For a 1D image
1549 /// and 1D image array, the image width must be ≤ `DeviceInfo::Image2dMaxWidth`.
1550 ///
1551 /// ## Height
1552 ///
1553 /// The height of the image in pixels. This is only used if the
1554 /// image is a 2D, 3D or 2D image array. For a 2D image or image array, the
1555 /// image height must be ≤ `DeviceInfo::Image2dMaxHeight`. For a 3D image, the
1556 /// image height must be ≤ `DeviceInfo::Image3dMaxHeight`.
1557 ///
1558 /// ## Depth
1559 ///
1560 /// image_depth The depth of the image in pixels. This is only used if the
1561 /// image is a 3D image and must be a value ≥ 1 and ≤
1562 /// `DeviceInfo::Image3dMaxDepth`.
1563 ///
1564 /// ## Examples
1565 ///
1566 /// * To set the dimensions of a 2d image use:
1567 /// `SpatialDims::Two(width, height)`.
1568 /// * To set the dimensions of a 2d image array use:
1569 /// `SpatialDims::Three(width, height, array_length)`.
1570 /// * To set the dimensions of a 3d image use:
1571 /// `SpatialDims::Three(width, height, depth)`.
1572 ///
1573 pub fn dims<D>(mut self, dims: D) -> ImageBuilder<'a, T>
1574 where
1575 D: Into<SpatialDims>,
1576 {
1577 let dims = dims.into().to_lens().unwrap();
1578 self.image_desc.image_width = dims[0];
1579 self.image_desc.image_height = dims[1];
1580 self.image_desc.image_depth = dims[2];
1581 self
1582 }
1583
1584 /// Image array size.
1585 ///
1586 /// The number of images in the image array. This is only used if the image is
1587 /// a 1D or 2D image array. The values for image_array_size, if specified,
1588 /// must be a value ≥ 1 and ≤ `DeviceInfo::ImageMaxArraySize`.
1589 ///
1590 /// Note that reading and writing 2D image arrays from a kernel with
1591 /// image_array_size = 1 may be lower performance than 2D images.
1592 ///
1593 pub fn array_size(mut self, array_size: usize) -> ImageBuilder<'a, T> {
1594 self.image_desc.image_array_size = array_size;
1595 self
1596 }
1597
1598 /// Image row pitch.
1599 ///
1600 /// The scan-line pitch in bytes. This must be 0 if host data is `None` and
1601 /// can be either 0 or ≥ image_width * size of element in bytes if host data
1602 /// is not `None`. If host data is not `None` and image_row_pitch = 0,
1603 /// image_row_pitch is calculated as image_width * size of element in bytes.
1604 /// If image_row_pitch is not 0, it must be a multiple of the image element
1605 /// size in bytes.
1606 ///
1607 pub fn row_pitch_bytes(mut self, row_pitch: usize) -> ImageBuilder<'a, T> {
1608 self.image_desc.image_row_pitch = row_pitch;
1609 self
1610 }
1611
1612 /// Image slice pitch.
1613 ///
1614 /// The size in bytes of each 2D slice in the 3D image or the size in bytes of
1615 /// each image in a 1D or 2D image array. This must be 0 if host data is
1616 /// `None`. If host data is not `None`, image_slice_pitch can be either 0 or ≥
1617 /// image_row_pitch * image_height for a 2D image array or 3D image and can be
1618 /// either 0 or ≥ image_row_pitch for a 1D image array. If host data is not
1619 /// `None` and image_slice_pitch = 0, image_slice_pitch is calculated as
1620 /// image_row_pitch * image_height for a 2D image array or 3D image and
1621 /// image_row_pitch for a 1D image array. If image_slice_pitch is not 0, it
1622 /// must be a multiple of the image_row_pitch.
1623 ///
1624 pub fn slc_pitch_bytes(mut self, slc_pitch: usize) -> ImageBuilder<'a, T> {
1625 self.image_desc.image_slice_pitch = slc_pitch;
1626 self
1627 }
1628
1629 /// Buffer synchronization.
1630 ///
1631 /// Refers to a valid buffer memory object if image_type is
1632 /// `MemObjectType::Image1dBuffer`. Otherwise it must be `None` (default).
1633 /// For a 1D image buffer object, the image pixels are taken from the buffer
1634 /// object's data store. When the contents of a buffer object's data store are
1635 /// modified, those changes are reflected in the contents of the 1D image
1636 /// buffer object and vice-versa at corresponding sychronization points. The
1637 /// image_width * size of element in bytes must be ≤ size of buffer object
1638 /// data store.
1639 ///
1640 pub fn buffer_sync(mut self, buffer: MemCore) -> ImageBuilder<'a, T> {
1641 self.image_desc.buffer = Some(buffer);
1642 self
1643 }
1644
1645 /// Specifies the image pixel format.
1646 ///
1647 /// If unspecified, defaults to:
1648 ///
1649 /// ```rust,ignore
1650 /// ImageFormat {
1651 /// channel_order: ImageChannelOrder::Rgba,
1652 /// channel_data_type: ImageChannelDataType::SnormInt8,
1653 /// }
1654 /// ```
1655 pub fn image_format(mut self, image_format: ImageFormat) -> ImageBuilder<'a, T> {
1656 self.image_format = image_format;
1657 self
1658 }
1659
1660 /// Specifies the image descriptor containing a number of important settings.
1661 ///
1662 /// If unspecified (not recommended), defaults to:
1663 ///
1664 /// ```rust,ignore
1665 /// ImageDescriptor {
1666 /// image_type: MemObjectType::Image1d,
1667 /// image_width: 0,
1668 /// image_height: 0,
1669 /// image_depth: 0,
1670 /// image_array_size: 0,
1671 /// image_row_pitch: 0,
1672 /// image_slice_pitch: 0,
1673 /// num_mip_levels: 0,
1674 /// num_samples: 0,
1675 /// buffer: None,
1676 /// }
1677 /// ```
1678 ///
1679 /// If you are unsure, just set the first four by using
1680 /// `ImageDescriptor::new`. Ex.:
1681 ///
1682 /// ```rust,ignore
1683 /// ocl::Image::builder()
1684 /// .image_desc(ocl::ImageDescriptor::new(
1685 /// ocl::MemObjectType::Image2d, 1280, 800, 1))
1686 /// ...
1687 /// ...
1688 /// .build()
1689 /// ```
1690 ///
1691 /// Setting this overwrites any previously set type, dimensions, array size, pitch, etc.
1692 ///
1693 pub unsafe fn image_desc(mut self, image_desc: ImageDescriptor) -> ImageBuilder<'a, T> {
1694 self.image_desc = image_desc;
1695 self
1696 }
1697
1698 /// Builds with no host side image data memory specified and returns a
1699 /// new `Image`.
1700 pub fn build(mut self) -> OclResult<Image<T>> {
1701 let host_slice = match self.host_slice {
1702 HostSlice::Use(hs) => {
1703 self.flags.insert(MemFlags::new().use_host_ptr());
1704 Some(hs)
1705 }
1706 HostSlice::Copy(hs) => {
1707 self.flags.insert(MemFlags::new().copy_host_ptr());
1708 Some(hs)
1709 }
1710 HostSlice::None => None,
1711 };
1712
1713 match self.queue_option {
1714 Some(qo) => unsafe {
1715 Image::new(
1716 qo,
1717 self.flags,
1718 self.image_format.clone(),
1719 self.image_desc.clone(),
1720 host_slice,
1721 )
1722 },
1723 None => panic!(
1724 "ocl::ImageBuilder::build: A context or default queue must be set \
1725 with '.context(...)' or '.queue(...)'."
1726 ),
1727 }
1728 }
1729}