rusticl/memory: use get_mut instead of lock in drop
[platform/upstream/mesa.git] / src / gallium / frontends / rusticl / core / memory.rs
1 use crate::api::icd::*;
2 use crate::api::types::*;
3 use crate::api::util::*;
4 use crate::core::context::*;
5 use crate::core::device::*;
6 use crate::core::format::*;
7 use crate::core::queue::*;
8 use crate::core::util::*;
9 use crate::impl_cl_type_trait;
10
11 use mesa_rust::pipe::context::*;
12 use mesa_rust::pipe::resource::*;
13 use mesa_rust::pipe::screen::ResourceType;
14 use mesa_rust::pipe::transfer::*;
15 use mesa_rust_gen::*;
16 use mesa_rust_util::math::*;
17 use mesa_rust_util::properties::Properties;
18 use rusticl_opencl_gen::*;
19
20 use std::cmp;
21 use std::collections::hash_map::Entry;
22 use std::collections::HashMap;
23 use std::convert::TryInto;
24 use std::mem::size_of;
25 use std::ops::AddAssign;
26 use std::os::raw::c_void;
27 use std::ptr;
28 use std::sync::Arc;
29 use std::sync::Mutex;
30 use std::sync::MutexGuard;
31
32 struct MappingTransfer {
33     tx: PipeTransfer,
34     shadow: Option<PipeResource>,
35     pending: u32,
36 }
37
38 impl MappingTransfer {
39     fn new(tx: PipeTransfer, shadow: Option<PipeResource>) -> Self {
40         MappingTransfer {
41             tx: tx,
42             shadow: shadow,
43             pending: 1,
44         }
45     }
46 }
47
48 struct Mappings {
49     tx: HashMap<&'static Device, MappingTransfer>,
50     maps: HashMap<*mut c_void, u32>,
51 }
52
53 impl Mappings {
54     fn new() -> Mutex<Self> {
55         Mutex::new(Mappings {
56             tx: HashMap::new(),
57             maps: HashMap::new(),
58         })
59     }
60
61     fn mark_pending(&mut self, dev: &Device) {
62         self.tx.get_mut(dev).unwrap().pending += 1;
63     }
64
65     fn unmark_pending(&mut self, dev: &Device) {
66         if let Some(tx) = self.tx.get_mut(dev) {
67             tx.pending -= 1;
68         }
69     }
70
71     fn increase_ref(&mut self, dev: &Device, ptr: *mut c_void) -> bool {
72         let res = self.maps.is_empty();
73         *self.maps.entry(ptr).or_default() += 1;
74         self.unmark_pending(dev);
75         res
76     }
77
78     fn decrease_ref(&mut self, ptr: *mut c_void, dev: &Device) -> (bool, Option<&PipeResource>) {
79         if let Some(r) = self.maps.get_mut(&ptr) {
80             *r -= 1;
81
82             if *r == 0 {
83                 self.maps.remove(&ptr);
84             }
85
86             if self.maps.is_empty() {
87                 let shadow = self.tx.get(dev).and_then(|tx| tx.shadow.as_ref());
88                 return (true, shadow);
89             }
90         }
91         (false, None)
92     }
93
94     fn clean_up_tx(&mut self, dev: &Device, ctx: &PipeContext) {
95         if self.maps.is_empty() {
96             if let Some(tx) = self.tx.get(&dev) {
97                 if tx.pending == 0 {
98                     self.tx.remove(dev).unwrap().tx.with_ctx(ctx);
99                 }
100             }
101         }
102     }
103 }
104
105 pub struct Mem {
106     pub base: CLObjectBase<CL_INVALID_MEM_OBJECT>,
107     pub context: Arc<Context>,
108     pub parent: Option<Arc<Mem>>,
109     pub mem_type: cl_mem_object_type,
110     pub flags: cl_mem_flags,
111     pub size: usize,
112     pub offset: usize,
113     pub host_ptr: *mut c_void,
114     pub image_format: cl_image_format,
115     pub pipe_format: pipe_format,
116     pub image_desc: cl_image_desc,
117     pub image_elem_size: u8,
118     pub props: Vec<cl_mem_properties>,
119     pub cbs: Mutex<Vec<Box<dyn Fn(cl_mem)>>>,
120     res: Option<HashMap<&'static Device, Arc<PipeResource>>>,
121     maps: Mutex<Mappings>,
122 }
123
124 impl_cl_type_trait!(cl_mem, Mem, CL_INVALID_MEM_OBJECT);
125
126 pub trait CLImageDescInfo {
127     fn type_info(&self) -> (u8, bool);
128     fn pixels(&self) -> usize;
129     fn bx(&self) -> CLResult<pipe_box>;
130     fn row_pitch(&self) -> CLResult<u32>;
131     fn slice_pitch(&self) -> usize;
132     fn width(&self) -> CLResult<u32>;
133     fn height(&self) -> CLResult<u32>;
134     fn size(&self) -> CLVec<usize>;
135
136     fn dims(&self) -> u8 {
137         self.type_info().0
138     }
139
140     fn dims_with_array(&self) -> u8 {
141         let array: u8 = self.is_array().into();
142         self.dims() + array
143     }
144
145     fn has_slice(&self) -> bool {
146         self.dims() == 3 || self.is_array()
147     }
148
149     fn is_array(&self) -> bool {
150         self.type_info().1
151     }
152 }
153
154 impl CLImageDescInfo for cl_image_desc {
155     fn type_info(&self) -> (u8, bool) {
156         match self.image_type {
157             CL_MEM_OBJECT_IMAGE1D | CL_MEM_OBJECT_IMAGE1D_BUFFER => (1, false),
158             CL_MEM_OBJECT_IMAGE1D_ARRAY => (1, true),
159             CL_MEM_OBJECT_IMAGE2D => (2, false),
160             CL_MEM_OBJECT_IMAGE2D_ARRAY => (2, true),
161             CL_MEM_OBJECT_IMAGE3D => (3, false),
162             _ => panic!("unknown image_type {:x}", self.image_type),
163         }
164     }
165
166     fn pixels(&self) -> usize {
167         let mut res = self.image_width;
168         let dims = self.dims();
169
170         if dims > 1 {
171             res *= self.image_height;
172         }
173
174         if dims > 2 {
175             res *= self.image_depth;
176         }
177
178         if self.is_array() {
179             res *= self.image_array_size;
180         }
181
182         res
183     }
184
185     fn size(&self) -> CLVec<usize> {
186         let mut height = cmp::max(self.image_height, 1);
187         let mut depth = cmp::max(self.image_depth, 1);
188
189         match self.image_type {
190             CL_MEM_OBJECT_IMAGE1D_ARRAY => height = self.image_array_size,
191             CL_MEM_OBJECT_IMAGE2D_ARRAY => depth = self.image_array_size,
192             _ => {}
193         }
194
195         CLVec::new([self.image_width, height, depth])
196     }
197
198     fn bx(&self) -> CLResult<pipe_box> {
199         create_pipe_box(CLVec::default(), self.size(), self.image_type)
200     }
201
202     fn row_pitch(&self) -> CLResult<u32> {
203         self.image_row_pitch
204             .try_into()
205             .map_err(|_| CL_OUT_OF_HOST_MEMORY)
206     }
207
208     fn slice_pitch(&self) -> usize {
209         self.image_slice_pitch
210     }
211
212     fn width(&self) -> CLResult<u32> {
213         self.image_width
214             .try_into()
215             .map_err(|_| CL_OUT_OF_HOST_MEMORY)
216     }
217
218     fn height(&self) -> CLResult<u32> {
219         self.image_height
220             .try_into()
221             .map_err(|_| CL_OUT_OF_HOST_MEMORY)
222     }
223 }
224
225 fn sw_copy(
226     src: *const c_void,
227     dst: *mut c_void,
228     region: &CLVec<usize>,
229     src_origin: &CLVec<usize>,
230     src_row_pitch: usize,
231     src_slice_pitch: usize,
232     dst_origin: &CLVec<usize>,
233     dst_row_pitch: usize,
234     dst_slice_pitch: usize,
235     pixel_size: u8,
236 ) {
237     for z in 0..region[2] {
238         for y in 0..region[1] {
239             unsafe {
240                 ptr::copy_nonoverlapping(
241                     src.add(
242                         (*src_origin + [0, y, z])
243                             * [pixel_size as usize, src_row_pitch, src_slice_pitch],
244                     ),
245                     dst.add(
246                         (*dst_origin + [0, y, z])
247                             * [pixel_size as usize, dst_row_pitch, dst_slice_pitch],
248                     ),
249                     region[0] * pixel_size as usize,
250                 )
251             };
252         }
253     }
254 }
255
256 /// helper function to determine if we can just map the resource in question or if we have to go
257 /// through a shdow buffer to let the CPU access the resources memory
258 fn can_map_directly(dev: &Device, res: &PipeResource) -> bool {
259     // there are two aprts to this check:
260     //   1. is the resource located in system RAM
261     //   2. has the resource a linear memory layout
262     // we do not want to map memory over the PCIe bus as this generally leads to bad performance.
263     (dev.unified_memory() || res.is_staging() || res.is_user)
264         && (res.is_buffer() || res.is_linear())
265 }
266
267 impl Mem {
268     pub fn new_buffer(
269         context: Arc<Context>,
270         flags: cl_mem_flags,
271         size: usize,
272         host_ptr: *mut c_void,
273         props: Vec<cl_mem_properties>,
274     ) -> CLResult<Arc<Mem>> {
275         let res_type = if bit_check(flags, CL_MEM_ALLOC_HOST_PTR) {
276             ResourceType::Staging
277         } else {
278             ResourceType::Normal
279         };
280
281         let buffer = context.create_buffer(
282             size,
283             host_ptr,
284             bit_check(flags, CL_MEM_COPY_HOST_PTR),
285             res_type,
286         )?;
287
288         let host_ptr = if bit_check(flags, CL_MEM_USE_HOST_PTR) {
289             host_ptr
290         } else {
291             ptr::null_mut()
292         };
293
294         Ok(Arc::new(Self {
295             base: CLObjectBase::new(),
296             context: context,
297             parent: None,
298             mem_type: CL_MEM_OBJECT_BUFFER,
299             flags: flags,
300             size: size,
301             offset: 0,
302             host_ptr: host_ptr,
303             image_format: cl_image_format::default(),
304             pipe_format: pipe_format::PIPE_FORMAT_NONE,
305             image_desc: cl_image_desc::default(),
306             image_elem_size: 0,
307             props: props,
308             cbs: Mutex::new(Vec::new()),
309             res: Some(buffer),
310             maps: Mappings::new(),
311         }))
312     }
313
314     pub fn new_sub_buffer(
315         parent: Arc<Mem>,
316         flags: cl_mem_flags,
317         offset: usize,
318         size: usize,
319     ) -> Arc<Mem> {
320         let host_ptr = if parent.host_ptr.is_null() {
321             ptr::null_mut()
322         } else {
323             unsafe { parent.host_ptr.add(offset) }
324         };
325
326         Arc::new(Self {
327             base: CLObjectBase::new(),
328             context: parent.context.clone(),
329             parent: Some(parent),
330             mem_type: CL_MEM_OBJECT_BUFFER,
331             flags: flags,
332             size: size,
333             offset: offset,
334             host_ptr: host_ptr,
335             image_format: cl_image_format::default(),
336             pipe_format: pipe_format::PIPE_FORMAT_NONE,
337             image_desc: cl_image_desc::default(),
338             image_elem_size: 0,
339             props: Vec::new(),
340             cbs: Mutex::new(Vec::new()),
341             res: None,
342             maps: Mappings::new(),
343         })
344     }
345
346     pub fn new_image(
347         context: Arc<Context>,
348         parent: Option<Arc<Mem>>,
349         mem_type: cl_mem_object_type,
350         flags: cl_mem_flags,
351         image_format: &cl_image_format,
352         mut image_desc: cl_image_desc,
353         image_elem_size: u8,
354         host_ptr: *mut c_void,
355         props: Vec<cl_mem_properties>,
356     ) -> CLResult<Arc<Mem>> {
357         // we have to sanitize the image_desc a little for internal use
358         let api_image_desc = image_desc;
359         let dims = image_desc.dims();
360         let is_array = image_desc.is_array();
361         if dims < 3 {
362             image_desc.image_depth = 1;
363         }
364         if dims < 2 {
365             image_desc.image_height = 1;
366         }
367         if !is_array {
368             image_desc.image_array_size = 1;
369         }
370
371         let res_type = if bit_check(flags, CL_MEM_ALLOC_HOST_PTR) {
372             ResourceType::Staging
373         } else {
374             ResourceType::Normal
375         };
376
377         let texture = if parent.is_none() {
378             let mut texture = context.create_texture(
379                 &image_desc,
380                 image_format,
381                 host_ptr,
382                 bit_check(flags, CL_MEM_COPY_HOST_PTR),
383                 res_type,
384             );
385
386             // if we error allocating a Staging resource, just try with normal as
387             // `CL_MEM_ALLOC_HOST_PTR` is just a performance hint.
388             if res_type == ResourceType::Staging && texture.is_err() {
389                 texture = context.create_texture(
390                     &image_desc,
391                     image_format,
392                     host_ptr,
393                     bit_check(flags, CL_MEM_COPY_HOST_PTR),
394                     ResourceType::Normal,
395                 )
396             }
397
398             Some(texture?)
399         } else {
400             None
401         };
402
403         let host_ptr = if bit_check(flags, CL_MEM_USE_HOST_PTR) {
404             host_ptr
405         } else {
406             ptr::null_mut()
407         };
408
409         let pipe_format = image_format.to_pipe_format().unwrap();
410         Ok(Arc::new(Self {
411             base: CLObjectBase::new(),
412             context: context,
413             parent: parent,
414             mem_type: mem_type,
415             flags: flags,
416             size: image_desc.pixels() * image_format.pixel_size().unwrap() as usize,
417             offset: 0,
418             host_ptr: host_ptr,
419             image_format: *image_format,
420             pipe_format: pipe_format,
421             image_desc: api_image_desc,
422             image_elem_size: image_elem_size,
423             props: props,
424             cbs: Mutex::new(Vec::new()),
425             res: texture,
426             maps: Mappings::new(),
427         }))
428     }
429
430     pub fn pixel_size(&self) -> Option<u8> {
431         if self.is_buffer() {
432             Some(1)
433         } else {
434             self.image_format.pixel_size()
435         }
436     }
437
438     pub fn is_buffer(&self) -> bool {
439         self.mem_type == CL_MEM_OBJECT_BUFFER
440     }
441
442     fn tx_raw(
443         &self,
444         q: &Arc<Queue>,
445         ctx: &PipeContext,
446         mut offset: usize,
447         size: usize,
448         rw: RWFlags,
449     ) -> CLResult<PipeTransfer> {
450         let b = self.to_parent(&mut offset);
451         let r = b.get_res()?.get(&q.device).unwrap();
452
453         Ok(ctx.buffer_map(
454             r,
455             offset.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
456             size.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
457             rw,
458             ResourceMapType::Normal,
459         ))
460     }
461
462     fn tx_raw_async(
463         &self,
464         q: &Arc<Queue>,
465         rw: RWFlags,
466     ) -> CLResult<(PipeTransfer, Option<PipeResource>)> {
467         let mut offset = 0;
468         let b = self.to_parent(&mut offset);
469         let r = b.get_res()?.get(&q.device).unwrap();
470         let size = self.size.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?;
471         let ctx = q.device.helper_ctx();
472
473         assert!(self.is_buffer());
474
475         let tx = if can_map_directly(q.device, r) {
476             ctx.buffer_map_directly(
477                 r,
478                 offset.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
479                 size,
480                 rw,
481             )
482         } else {
483             None
484         };
485
486         if let Some(tx) = tx {
487             Ok((tx, None))
488         } else {
489             let shadow = q
490                 .device
491                 .screen()
492                 .resource_create_buffer(size as u32, ResourceType::Staging)
493                 .ok_or(CL_OUT_OF_RESOURCES)?;
494             let tx = ctx.buffer_map_coherent(&shadow, 0, size, rw);
495             Ok((tx, Some(shadow)))
496         }
497     }
498
499     fn tx<'a>(
500         &self,
501         q: &Arc<Queue>,
502         ctx: &'a PipeContext,
503         offset: usize,
504         size: usize,
505         rw: RWFlags,
506     ) -> CLResult<GuardedPipeTransfer<'a>> {
507         Ok(self.tx_raw(q, ctx, offset, size, rw)?.with_ctx(ctx))
508     }
509
510     fn tx_image_raw(
511         &self,
512         q: &Arc<Queue>,
513         ctx: &PipeContext,
514         bx: &pipe_box,
515         rw: RWFlags,
516     ) -> CLResult<PipeTransfer> {
517         assert!(!self.is_buffer());
518
519         let r = self.get_res()?.get(&q.device).unwrap();
520         Ok(ctx.texture_map(r, bx, rw, ResourceMapType::Normal))
521     }
522
523     fn tx_image_raw_async(
524         &self,
525         q: &Arc<Queue>,
526         bx: &pipe_box,
527         rw: RWFlags,
528     ) -> CLResult<(PipeTransfer, Option<PipeResource>)> {
529         assert!(!self.is_buffer());
530
531         let r = self.get_res()?.get(q.device).unwrap();
532         let ctx = q.device.helper_ctx();
533
534         let tx = if can_map_directly(q.device, r) {
535             ctx.texture_map_directly(r, bx, rw)
536         } else {
537             None
538         };
539
540         if let Some(tx) = tx {
541             Ok((tx, None))
542         } else {
543             let shadow = q
544                 .device
545                 .screen()
546                 .resource_create_texture(
547                     r.width(),
548                     r.height(),
549                     r.depth(),
550                     r.array_size(),
551                     cl_mem_type_to_texture_target(self.image_desc.image_type),
552                     self.pipe_format,
553                     ResourceType::Staging,
554                     false,
555                 )
556                 .ok_or(CL_OUT_OF_RESOURCES)?;
557             let tx = ctx.texture_map_coherent(&shadow, bx, rw);
558             Ok((tx, Some(shadow)))
559         }
560     }
561
562     fn tx_image<'a>(
563         &self,
564         q: &Arc<Queue>,
565         ctx: &'a PipeContext,
566         bx: &pipe_box,
567         rw: RWFlags,
568     ) -> CLResult<GuardedPipeTransfer<'a>> {
569         Ok(self.tx_image_raw(q, ctx, bx, rw)?.with_ctx(ctx))
570     }
571
572     pub fn has_same_parent(&self, other: &Self) -> bool {
573         ptr::eq(self.get_parent(), other.get_parent())
574     }
575
576     pub fn is_parent_buffer(&self) -> bool {
577         self.parent.as_ref().map_or(false, |p| p.is_buffer())
578     }
579
580     pub fn is_image_from_buffer(&self) -> bool {
581         self.is_parent_buffer() && self.mem_type == CL_MEM_OBJECT_IMAGE2D
582     }
583
584     // this is kinda bogus, because that won't work with system SVM, but the spec wants us to
585     // implement this.
586     pub fn is_svm(&self) -> bool {
587         let mem = self.get_parent();
588         self.context.find_svm_alloc(mem.host_ptr.cast()).is_some()
589             && bit_check(mem.flags, CL_MEM_USE_HOST_PTR)
590     }
591
592     fn get_res(&self) -> CLResult<&HashMap<&'static Device, Arc<PipeResource>>> {
593         self.get_parent().res.as_ref().ok_or(CL_OUT_OF_HOST_MEMORY)
594     }
595
596     pub fn get_res_of_dev(&self, dev: &Device) -> CLResult<&Arc<PipeResource>> {
597         Ok(self.get_res()?.get(dev).unwrap())
598     }
599
600     fn get_parent(&self) -> &Self {
601         if let Some(parent) = &self.parent {
602             parent
603         } else {
604             self
605         }
606     }
607
608     fn to_parent<'a>(&'a self, offset: &mut usize) -> &'a Self {
609         if let Some(parent) = &self.parent {
610             offset.add_assign(self.offset);
611             parent
612         } else {
613             self
614         }
615     }
616
617     fn has_user_shadow_buffer(&self, d: &Device) -> CLResult<bool> {
618         let r = self.get_res()?.get(d).unwrap();
619         Ok(!r.is_user && bit_check(self.flags, CL_MEM_USE_HOST_PTR))
620     }
621
622     pub fn read_to_user(
623         &self,
624         q: &Arc<Queue>,
625         ctx: &PipeContext,
626         offset: usize,
627         ptr: *mut c_void,
628         size: usize,
629     ) -> CLResult<()> {
630         assert!(self.is_buffer());
631
632         let tx = self.tx(q, ctx, offset, size, RWFlags::RD)?;
633
634         unsafe {
635             ptr::copy_nonoverlapping(tx.ptr(), ptr, size);
636         }
637
638         Ok(())
639     }
640
641     pub fn write_from_user(
642         &self,
643         q: &Arc<Queue>,
644         ctx: &PipeContext,
645         mut offset: usize,
646         ptr: *const c_void,
647         size: usize,
648     ) -> CLResult<()> {
649         assert!(self.is_buffer());
650
651         let b = self.to_parent(&mut offset);
652         let r = b.get_res()?.get(&q.device).unwrap();
653         ctx.buffer_subdata(
654             r,
655             offset.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
656             ptr,
657             size.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
658         );
659         Ok(())
660     }
661
662     pub fn copy_to(
663         &self,
664         q: &Arc<Queue>,
665         ctx: &PipeContext,
666         dst: &Arc<Mem>,
667         mut src_origin: CLVec<usize>,
668         mut dst_origin: CLVec<usize>,
669         region: &CLVec<usize>,
670     ) -> CLResult<()> {
671         let dst_base = dst;
672         let src = self.to_parent(&mut src_origin[0]);
673         let dst = dst.to_parent(&mut dst_origin[0]);
674
675         let src_res = src.get_res()?.get(&q.device).unwrap();
676         let dst_res = dst.get_res()?.get(&q.device).unwrap();
677
678         // We just want to use sw_copy if mem objects have different types
679         // or if copy can have custom strides (image2d from buff/images)
680         if self.is_buffer() != dst_base.is_buffer()
681             || !self.is_buffer() && self.parent.is_some()
682             || !dst_base.is_buffer() && dst_base.parent.is_some()
683         {
684             let tx_src;
685             let tx_dst;
686             let mut src_pitch = [0, 0, 0];
687             let mut dst_pitch = [0, 0, 0];
688
689             let bpp = if !self.is_buffer() {
690                 self.pixel_size().unwrap() as usize
691             } else {
692                 dst_base.pixel_size().unwrap() as usize
693             };
694
695             if src.is_buffer() {
696                 // If image is created from a buffer, use image's slice and row pitch instead
697                 src_pitch[0] = bpp;
698                 if self.is_image_from_buffer() {
699                     src_pitch[1] = self.image_desc.row_pitch()? as usize;
700                     src_pitch[2] = self.image_desc.slice_pitch();
701                 } else {
702                     src_pitch[1] = region[0] * bpp;
703                     src_pitch[2] = region[0] * region[1] * bpp;
704                 }
705
706                 let (offset, size) = CLVec::calc_offset_size(src_origin, region, src_pitch);
707                 tx_src = src.tx(q, ctx, offset, size, RWFlags::RD)?;
708             } else {
709                 tx_src = src.tx_image(
710                     q,
711                     ctx,
712                     &create_pipe_box(src_origin, *region, src.mem_type)?,
713                     RWFlags::RD,
714                 )?;
715
716                 src_pitch = [1, tx_src.row_pitch() as usize, tx_src.slice_pitch()];
717             }
718
719             if dst.is_buffer() {
720                 // If image is created from a buffer, use image's slice and row pitch instead
721                 dst_pitch[0] = bpp;
722                 if dst_base.is_image_from_buffer() {
723                     dst_pitch[1] = dst_base.image_desc.row_pitch()? as usize;
724                     dst_pitch[2] = dst_base.image_desc.slice_pitch();
725                 } else {
726                     dst_pitch[1] = region[0] * bpp;
727                     dst_pitch[2] = region[0] * region[1] * bpp;
728                 }
729
730                 let (offset, size) = CLVec::calc_offset_size(dst_origin, region, dst_pitch);
731                 tx_dst = dst.tx(q, ctx, offset, size, RWFlags::WR)?;
732             } else {
733                 tx_dst = dst.tx_image(
734                     q,
735                     ctx,
736                     &create_pipe_box(dst_origin, *region, dst.mem_type)?,
737                     RWFlags::WR,
738                 )?;
739
740                 dst_pitch = [1, tx_dst.row_pitch() as usize, tx_dst.slice_pitch()];
741             }
742
743             // Those pitch values cannot have 0 value in its coordinates
744             assert!(src_pitch[0] != 0 && src_pitch[1] != 0 && src_pitch[2] != 0);
745             assert!(dst_pitch[0] != 0 && dst_pitch[1] != 0 && dst_pitch[2] != 0);
746
747             sw_copy(
748                 tx_src.ptr(),
749                 tx_dst.ptr(),
750                 region,
751                 &CLVec::default(),
752                 src_pitch[1],
753                 src_pitch[2],
754                 &CLVec::default(),
755                 dst_pitch[1],
756                 dst_pitch[2],
757                 bpp as u8,
758             )
759         } else {
760             let bx = create_pipe_box(src_origin, *region, src.mem_type)?;
761             let mut dst_origin: [u32; 3] = dst_origin.try_into()?;
762
763             if src.mem_type == CL_MEM_OBJECT_IMAGE1D_ARRAY {
764                 (dst_origin[1], dst_origin[2]) = (dst_origin[2], dst_origin[1]);
765             }
766
767             ctx.resource_copy_region(src_res, dst_res, &dst_origin, &bx);
768         }
769         Ok(())
770     }
771
772     pub fn fill(
773         &self,
774         q: &Arc<Queue>,
775         ctx: &PipeContext,
776         pattern: &[u8],
777         mut offset: usize,
778         size: usize,
779     ) -> CLResult<()> {
780         assert!(self.is_buffer());
781
782         let b = self.to_parent(&mut offset);
783         let res = b.get_res()?.get(&q.device).unwrap();
784         ctx.clear_buffer(
785             res,
786             pattern,
787             offset.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
788             size.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
789         );
790         Ok(())
791     }
792
793     pub fn fill_image(
794         &self,
795         q: &Arc<Queue>,
796         ctx: &PipeContext,
797         pattern: &[u32],
798         origin: &CLVec<usize>,
799         region: &CLVec<usize>,
800     ) -> CLResult<()> {
801         assert!(!self.is_buffer());
802
803         let res = self.get_res()?.get(&q.device).unwrap();
804         // make sure we allocate multiples of 4 bytes so drivers don't read out of bounds or
805         // unaligned.
806         // TODO: use div_ceil once it's available
807         let pixel_size = align(self.pixel_size().unwrap() as usize, size_of::<u32>());
808         let mut new_pattern: Vec<u32> = vec![0; pixel_size / size_of::<u32>()];
809
810         // we don't support CL_DEPTH for now
811         assert!(pattern.len() == 4);
812
813         // SAFETY: pointers have to be valid for read/writes of exactly one pixel of their
814         // respective format.
815         // `new_pattern` has the correct size due to the `size` above.
816         // `pattern` is validated through the CL API and allows undefined behavior if not followed
817         // by CL API rules. It's expected to be a 4 component array of 32 bit values, except for
818         // CL_DEPTH where it's just one value.
819         unsafe {
820             util_format_pack_rgba(
821                 self.pipe_format,
822                 new_pattern.as_mut_ptr().cast(),
823                 pattern.as_ptr().cast(),
824                 1,
825             );
826         }
827
828         // If image is created from a buffer, use clear_image_buffer instead
829         // for each row
830         if self.is_parent_buffer() {
831             let strides = (
832                 self.image_desc.row_pitch()? as usize,
833                 self.image_desc.slice_pitch(),
834             );
835             ctx.clear_image_buffer(res, &new_pattern, origin, region, strides, pixel_size);
836         } else {
837             let bx = create_pipe_box(*origin, *region, self.mem_type)?;
838             ctx.clear_texture(res, &new_pattern, &bx);
839         }
840
841         Ok(())
842     }
843
844     pub fn write_from_user_rect(
845         &self,
846         src: *const c_void,
847         q: &Arc<Queue>,
848         ctx: &PipeContext,
849         region: &CLVec<usize>,
850         src_origin: &CLVec<usize>,
851         src_row_pitch: usize,
852         mut src_slice_pitch: usize,
853         dst_origin: &CLVec<usize>,
854         dst_row_pitch: usize,
855         dst_slice_pitch: usize,
856     ) -> CLResult<()> {
857         if self.is_buffer() || self.is_image_from_buffer() {
858             let pixel_size = self.pixel_size().unwrap();
859             let (offset, size) = CLVec::calc_offset_size(
860                 dst_origin,
861                 region,
862                 [
863                     pixel_size.try_into().unwrap(),
864                     dst_row_pitch,
865                     dst_slice_pitch,
866                 ],
867             );
868             let tx = self.tx(q, ctx, offset, size, RWFlags::WR)?;
869
870             sw_copy(
871                 src,
872                 tx.ptr(),
873                 region,
874                 src_origin,
875                 src_row_pitch,
876                 src_slice_pitch,
877                 &CLVec::default(),
878                 dst_row_pitch,
879                 dst_slice_pitch,
880                 pixel_size,
881             );
882         } else {
883             assert!(dst_row_pitch == self.image_desc.image_row_pitch);
884             assert!(dst_slice_pitch == self.image_desc.image_slice_pitch);
885             assert!(src_origin == &CLVec::default());
886
887             let res = self.get_res()?.get(&q.device).unwrap();
888             let bx = create_pipe_box(*dst_origin, *region, self.mem_type)?;
889
890             if self.mem_type == CL_MEM_OBJECT_IMAGE1D_ARRAY {
891                 src_slice_pitch = src_row_pitch;
892             }
893
894             ctx.texture_subdata(
895                 res,
896                 &bx,
897                 src,
898                 src_row_pitch
899                     .try_into()
900                     .map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
901                 src_slice_pitch,
902             );
903         }
904         Ok(())
905     }
906
907     pub fn read_to_user_rect(
908         &self,
909         dst: *mut c_void,
910         q: &Arc<Queue>,
911         ctx: &PipeContext,
912         region: &CLVec<usize>,
913         src_origin: &CLVec<usize>,
914         mut src_row_pitch: usize,
915         mut src_slice_pitch: usize,
916         dst_origin: &CLVec<usize>,
917         dst_row_pitch: usize,
918         dst_slice_pitch: usize,
919     ) -> CLResult<()> {
920         let tx;
921         let pixel_size;
922
923         if self.is_buffer() || self.is_image_from_buffer() {
924             pixel_size = self.pixel_size().unwrap();
925             let (offset, size) = CLVec::calc_offset_size(
926                 src_origin,
927                 region,
928                 [
929                     pixel_size.try_into().unwrap(),
930                     src_row_pitch,
931                     src_slice_pitch,
932                 ],
933             );
934             tx = self.tx(q, ctx, offset, size, RWFlags::RD)?;
935         } else {
936             assert!(dst_origin == &CLVec::default());
937
938             let bx = create_pipe_box(*src_origin, *region, self.mem_type)?;
939             tx = self.tx_image(q, ctx, &bx, RWFlags::RD)?;
940             src_row_pitch = tx.row_pitch() as usize;
941             src_slice_pitch = tx.slice_pitch();
942
943             pixel_size = self.pixel_size().unwrap();
944         };
945
946         sw_copy(
947             tx.ptr(),
948             dst,
949             region,
950             &CLVec::default(),
951             src_row_pitch,
952             src_slice_pitch,
953             dst_origin,
954             dst_row_pitch,
955             dst_slice_pitch,
956             pixel_size,
957         );
958
959         Ok(())
960     }
961
962     pub fn copy_to_rect(
963         &self,
964         dst: &Self,
965         q: &Arc<Queue>,
966         ctx: &PipeContext,
967         region: &CLVec<usize>,
968         src_origin: &CLVec<usize>,
969         src_row_pitch: usize,
970         src_slice_pitch: usize,
971         dst_origin: &CLVec<usize>,
972         dst_row_pitch: usize,
973         dst_slice_pitch: usize,
974     ) -> CLResult<()> {
975         assert!(self.is_buffer());
976         assert!(dst.is_buffer());
977
978         let (offset, size) =
979             CLVec::calc_offset_size(src_origin, region, [1, src_row_pitch, src_slice_pitch]);
980         let tx_src = self.tx(q, ctx, offset, size, RWFlags::RD)?;
981
982         let (offset, size) =
983             CLVec::calc_offset_size(dst_origin, region, [1, dst_row_pitch, dst_slice_pitch]);
984         let tx_dst = dst.tx(q, ctx, offset, size, RWFlags::WR)?;
985
986         // TODO check to use hw accelerated paths (e.g. resource_copy_region or blits)
987         sw_copy(
988             tx_src.ptr(),
989             tx_dst.ptr(),
990             region,
991             &CLVec::default(),
992             src_row_pitch,
993             src_slice_pitch,
994             &CLVec::default(),
995             dst_row_pitch,
996             dst_slice_pitch,
997             1,
998         );
999
1000         Ok(())
1001     }
1002
1003     // TODO: only sync on map when the memory is not mapped with discard
1004     pub fn sync_shadow_buffer(
1005         &self,
1006         q: &Arc<Queue>,
1007         ctx: &PipeContext,
1008         ptr: *mut c_void,
1009     ) -> CLResult<()> {
1010         let mut lock = self.maps.lock().unwrap();
1011         if !lock.increase_ref(q.device, ptr) {
1012             return Ok(());
1013         }
1014
1015         if self.has_user_shadow_buffer(q.device)? {
1016             self.read_to_user(q, ctx, 0, self.host_ptr, self.size)
1017         } else {
1018             if let Some(shadow) = lock.tx.get(&q.device).and_then(|tx| tx.shadow.as_ref()) {
1019                 let mut offset = 0;
1020                 let b = self.to_parent(&mut offset);
1021                 let res = b.get_res_of_dev(q.device)?;
1022                 let bx = create_pipe_box(
1023                     [offset, 0, 0].into(),
1024                     [self.size, 1, 1].into(),
1025                     CL_MEM_OBJECT_BUFFER,
1026                 )?;
1027                 ctx.resource_copy_region(res, shadow, &[0; 3], &bx);
1028             }
1029             Ok(())
1030         }
1031     }
1032
1033     // TODO: only sync on map when the memory is not mapped with discard
1034     pub fn sync_shadow_image(
1035         &self,
1036         q: &Arc<Queue>,
1037         ctx: &PipeContext,
1038         ptr: *mut c_void,
1039     ) -> CLResult<()> {
1040         let mut lock = self.maps.lock().unwrap();
1041         if !lock.increase_ref(q.device, ptr) {
1042             return Ok(());
1043         }
1044
1045         if self.has_user_shadow_buffer(q.device)? {
1046             self.read_to_user_rect(
1047                 self.host_ptr,
1048                 q,
1049                 ctx,
1050                 &self.image_desc.size(),
1051                 &CLVec::default(),
1052                 0,
1053                 0,
1054                 &CLVec::default(),
1055                 self.image_desc.image_row_pitch,
1056                 self.image_desc.image_slice_pitch,
1057             )
1058         } else {
1059             if let Some(shadow) = lock.tx.get(q.device).and_then(|tx| tx.shadow.as_ref()) {
1060                 let res = self.get_res_of_dev(q.device)?;
1061                 let bx = self.image_desc.bx()?;
1062                 ctx.resource_copy_region(res, shadow, &[0, 0, 0], &bx);
1063             }
1064             Ok(())
1065         }
1066     }
1067
1068     /// Maps the queue associated device's resource.
1069     ///
1070     /// Mapping resources could have been quite straightforward if OpenCL wouldn't allow for so
1071     /// called non blocking maps. Non blocking maps shall return a valid pointer to the mapped
1072     /// region immediately, but should not synchronize data (in case of shadow buffers) until after
1073     /// the map event is reached in the queue.
1074     /// This makes it not possible to simply use pipe_transfers as those can't be explicitly synced
1075     /// by the frontend.
1076     ///
1077     /// In order to have a compliant implementation of the mapping API we have to consider the
1078     /// following cases:
1079     ///   1. Mapping a cl_mem object with CL_MEM_USE_HOST_PTR: We simply return the host_ptr.
1080     ///      Synchronization of shadowed host ptrs are done in `sync_shadow_buffer` and
1081     ///      `sync_shadow_image` on demand.
1082     ///   2. Mapping linear resources on UMA systems: We simply create the pipe_transfer with
1083     ///      `PIPE_MAP_DIRECTLY` and `PIPE_MAP_UNSYNCHRONIZED` and return the attached pointer.
1084     ///   3. On non UMA systems or when 2. fails (e.g. due to the resource being tiled) we
1085     ///      - create a shadow pipe_resource with `PIPE_USAGE_STAGING`,
1086     ///        `PIPE_RESOURCE_FLAG_MAP_PERSISTENT` and `PIPE_RESOURCE_FLAG_MAP_COHERENT`
1087     ///      - create a pipe_transfer with `PIPE_MAP_COHERENT`, `PIPE_MAP_PERSISTENT` and
1088     ///        `PIPE_MAP_UNSYNCHRONIZED`
1089     ///      - sync the shadow buffer like a host_ptr shadow buffer in 1.
1090     ///
1091     /// Taking this approach we guarentee that we only copy when actually needed while making sure
1092     /// the content behind the returned pointer is valid until unmapped.
1093     fn map<'a>(
1094         &self,
1095         q: &Arc<Queue>,
1096         lock: &'a mut MutexGuard<Mappings>,
1097         rw: RWFlags,
1098     ) -> CLResult<&'a PipeTransfer> {
1099         if let Entry::Vacant(e) = lock.tx.entry(q.device) {
1100             let (tx, res) = if self.is_buffer() {
1101                 self.tx_raw_async(q, rw)?
1102             } else {
1103                 let bx = self.image_desc.bx()?;
1104                 self.tx_image_raw_async(q, &bx, rw)?
1105             };
1106
1107             e.insert(MappingTransfer::new(tx, res));
1108         } else {
1109             lock.mark_pending(q.device);
1110         }
1111
1112         Ok(&lock.tx.get_mut(&q.device).unwrap().tx)
1113     }
1114
1115     pub fn map_buffer(&self, q: &Arc<Queue>, offset: usize, _size: usize) -> CLResult<*mut c_void> {
1116         assert!(self.is_buffer());
1117
1118         let mut lock = self.maps.lock().unwrap();
1119         let ptr = if self.has_user_shadow_buffer(q.device)? {
1120             self.host_ptr
1121         } else {
1122             let tx = self.map(q, &mut lock, RWFlags::RW)?;
1123             tx.ptr()
1124         };
1125
1126         let ptr = unsafe { ptr.add(offset) };
1127         Ok(ptr)
1128     }
1129
1130     pub fn map_image(
1131         &self,
1132         q: &Arc<Queue>,
1133         origin: &CLVec<usize>,
1134         _region: &CLVec<usize>,
1135         row_pitch: &mut usize,
1136         slice_pitch: &mut usize,
1137     ) -> CLResult<*mut c_void> {
1138         assert!(!self.is_buffer());
1139
1140         let mut lock = self.maps.lock().unwrap();
1141
1142         // we might have a host_ptr shadow buffer or image created from buffer
1143         let ptr = if self.has_user_shadow_buffer(q.device)? || self.is_parent_buffer() {
1144             *row_pitch = self.image_desc.image_row_pitch;
1145             *slice_pitch = self.image_desc.image_slice_pitch;
1146
1147             if let Some(src) = &self.parent {
1148                 let tx = src.map(q, &mut lock, RWFlags::RW)?;
1149                 tx.ptr()
1150             } else {
1151                 self.host_ptr
1152             }
1153         } else {
1154             let tx = self.map(q, &mut lock, RWFlags::RW)?;
1155
1156             if self.image_desc.dims() > 1 {
1157                 *row_pitch = tx.row_pitch() as usize;
1158             }
1159             if self.image_desc.dims() > 2 || self.image_desc.is_array() {
1160                 *slice_pitch = tx.slice_pitch();
1161             }
1162
1163             tx.ptr()
1164         };
1165
1166         let ptr = unsafe {
1167             ptr.add(
1168                 *origin
1169                     * [
1170                         self.pixel_size().unwrap() as usize,
1171                         *row_pitch,
1172                         *slice_pitch,
1173                     ],
1174             )
1175         };
1176
1177         Ok(ptr)
1178     }
1179
1180     pub fn is_mapped_ptr(&self, ptr: *mut c_void) -> bool {
1181         self.maps.lock().unwrap().maps.contains_key(&ptr)
1182     }
1183
1184     // TODO: only sync on unmap when the memory is not mapped for writing
1185     pub fn unmap(&self, q: &Arc<Queue>, ctx: &PipeContext, ptr: *mut c_void) -> CLResult<()> {
1186         let mut lock = self.maps.lock().unwrap();
1187         if !lock.maps.contains_key(&ptr) {
1188             return Ok(());
1189         }
1190
1191         let (needs_sync, shadow) = lock.decrease_ref(ptr, q.device);
1192         if needs_sync {
1193             if let Some(shadow) = shadow {
1194                 let mut offset = 0;
1195                 let b = self.to_parent(&mut offset);
1196                 let res = b.get_res_of_dev(q.device)?;
1197
1198                 let bx = if b.is_buffer() {
1199                     create_pipe_box(
1200                         CLVec::default(),
1201                         [self.size, 1, 1].into(),
1202                         CL_MEM_OBJECT_BUFFER,
1203                     )?
1204                 } else {
1205                     self.image_desc.bx()?
1206                 };
1207
1208                 ctx.resource_copy_region(shadow, res, &[offset as u32, 0, 0], &bx);
1209             } else if self.has_user_shadow_buffer(q.device)? {
1210                 if self.is_buffer() {
1211                     self.write_from_user(q, ctx, 0, self.host_ptr, self.size)?;
1212                 } else {
1213                     self.write_from_user_rect(
1214                         self.host_ptr,
1215                         q,
1216                         ctx,
1217                         &self.image_desc.size(),
1218                         &CLVec::default(),
1219                         self.image_desc.image_row_pitch,
1220                         self.image_desc.image_slice_pitch,
1221                         &CLVec::default(),
1222                         self.image_desc.image_row_pitch,
1223                         self.image_desc.image_slice_pitch,
1224                     )?;
1225                 }
1226             }
1227         }
1228
1229         lock.clean_up_tx(q.device, ctx);
1230
1231         Ok(())
1232     }
1233 }
1234
1235 impl Drop for Mem {
1236     fn drop(&mut self) {
1237         let cl = cl_mem::from_ptr(self);
1238         self.cbs
1239             .get_mut()
1240             .unwrap()
1241             .iter()
1242             .rev()
1243             .for_each(|cb| cb(cl));
1244
1245         for (d, tx) in self.maps.get_mut().unwrap().tx.drain() {
1246             d.helper_ctx().unmap(tx.tx);
1247         }
1248     }
1249 }
1250
1251 pub struct Sampler {
1252     pub base: CLObjectBase<CL_INVALID_SAMPLER>,
1253     pub context: Arc<Context>,
1254     pub normalized_coords: bool,
1255     pub addressing_mode: cl_addressing_mode,
1256     pub filter_mode: cl_filter_mode,
1257     pub props: Option<Properties<cl_sampler_properties>>,
1258 }
1259
1260 impl_cl_type_trait!(cl_sampler, Sampler, CL_INVALID_SAMPLER);
1261
1262 impl Sampler {
1263     pub fn new(
1264         context: Arc<Context>,
1265         normalized_coords: bool,
1266         addressing_mode: cl_addressing_mode,
1267         filter_mode: cl_filter_mode,
1268         props: Option<Properties<cl_sampler_properties>>,
1269     ) -> Arc<Sampler> {
1270         Arc::new(Self {
1271             base: CLObjectBase::new(),
1272             context: context,
1273             normalized_coords: normalized_coords,
1274             addressing_mode: addressing_mode,
1275             filter_mode: filter_mode,
1276             props: props,
1277         })
1278     }
1279
1280     pub fn nir_to_cl(
1281         addressing_mode: u32,
1282         filter_mode: u32,
1283         normalized_coords: u32,
1284     ) -> (cl_addressing_mode, cl_filter_mode, bool) {
1285         let addr_mode = match addressing_mode {
1286             cl_sampler_addressing_mode::SAMPLER_ADDRESSING_MODE_NONE => CL_ADDRESS_NONE,
1287             cl_sampler_addressing_mode::SAMPLER_ADDRESSING_MODE_CLAMP_TO_EDGE => {
1288                 CL_ADDRESS_CLAMP_TO_EDGE
1289             }
1290             cl_sampler_addressing_mode::SAMPLER_ADDRESSING_MODE_CLAMP => CL_ADDRESS_CLAMP,
1291             cl_sampler_addressing_mode::SAMPLER_ADDRESSING_MODE_REPEAT => CL_ADDRESS_REPEAT,
1292             cl_sampler_addressing_mode::SAMPLER_ADDRESSING_MODE_REPEAT_MIRRORED => {
1293                 CL_ADDRESS_MIRRORED_REPEAT
1294             }
1295             _ => panic!("unknown addressing_mode"),
1296         };
1297
1298         let filter = match filter_mode {
1299             cl_sampler_filter_mode::SAMPLER_FILTER_MODE_NEAREST => CL_FILTER_NEAREST,
1300             cl_sampler_filter_mode::SAMPLER_FILTER_MODE_LINEAR => CL_FILTER_LINEAR,
1301             _ => panic!("unknown filter_mode"),
1302         };
1303
1304         (addr_mode, filter, normalized_coords != 0)
1305     }
1306
1307     pub fn cl_to_pipe(
1308         (addressing_mode, filter_mode, normalized_coords): (
1309             cl_addressing_mode,
1310             cl_filter_mode,
1311             bool,
1312         ),
1313     ) -> pipe_sampler_state {
1314         let mut res = pipe_sampler_state::default();
1315
1316         let wrap = match addressing_mode {
1317             CL_ADDRESS_CLAMP_TO_EDGE => pipe_tex_wrap::PIPE_TEX_WRAP_CLAMP_TO_EDGE,
1318             CL_ADDRESS_CLAMP => pipe_tex_wrap::PIPE_TEX_WRAP_CLAMP_TO_BORDER,
1319             CL_ADDRESS_REPEAT => pipe_tex_wrap::PIPE_TEX_WRAP_REPEAT,
1320             CL_ADDRESS_MIRRORED_REPEAT => pipe_tex_wrap::PIPE_TEX_WRAP_MIRROR_REPEAT,
1321             // TODO: what's a reasonable default?
1322             _ => pipe_tex_wrap::PIPE_TEX_WRAP_CLAMP_TO_EDGE,
1323         };
1324
1325         let img_filter = match filter_mode {
1326             CL_FILTER_NEAREST => pipe_tex_filter::PIPE_TEX_FILTER_NEAREST,
1327             CL_FILTER_LINEAR => pipe_tex_filter::PIPE_TEX_FILTER_LINEAR,
1328             _ => panic!("unknown filter_mode"),
1329         };
1330
1331         res.set_min_img_filter(img_filter);
1332         res.set_mag_img_filter(img_filter);
1333         res.set_unnormalized_coords((!normalized_coords).into());
1334         res.set_wrap_r(wrap);
1335         res.set_wrap_s(wrap);
1336         res.set_wrap_t(wrap);
1337
1338         res
1339     }
1340
1341     pub fn pipe(&self) -> pipe_sampler_state {
1342         Self::cl_to_pipe((
1343             self.addressing_mode,
1344             self.filter_mode,
1345             self.normalized_coords,
1346         ))
1347     }
1348 }