summaryrefslogtreecommitdiffstats
path: root/third_party/rust/metal/src/sync.rs
blob: e4b3d8aa671a4f99fa228841fd4cfa93cb7f71aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright 2016 GFX developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use super::*;
use block::{Block, RcBlock};
use std::mem;

#[cfg(feature = "dispatch_queue")]
use dispatch;

type MTLSharedEventNotificationBlock<'a> = RcBlock<(&'a SharedEventRef, u64), ()>;

pub enum MTLEvent {}

foreign_obj_type! {
    type CType = MTLEvent;
    pub struct Event;
    pub struct EventRef;
}

impl EventRef {
    pub fn device(&self) -> &DeviceRef {
        unsafe { msg_send![self, device] }
    }
}

pub enum MTLSharedEvent {}

foreign_obj_type! {
    type CType = MTLSharedEvent;
    pub struct SharedEvent;
    pub struct SharedEventRef;
    type ParentType = EventRef;
}

impl SharedEventRef {
    pub fn signaled_value(&self) -> u64 {
        unsafe { msg_send![self, signaledValue] }
    }

    pub fn set_signaled_value(&self, new_value: u64) {
        unsafe { msg_send![self, setSignaledValue: new_value] }
    }

    /// Schedules a notification handler to be called after the shareable event’s signal value
    /// equals or exceeds a given value.
    pub fn notify(
        &self,
        listener: &SharedEventListenerRef,
        value: u64,
        block: MTLSharedEventNotificationBlock,
    ) {
        unsafe {
            // If the block doesn't have a signature, this segfaults.
            // Taken from https://github.com/servo/pathfinder/blob/e858c8dc1d8ff02a5b603e21e09a64d6b3e11327/metal/src/lib.rs#L2327
            let block = mem::transmute::<
                MTLSharedEventNotificationBlock,
                *mut BlockBase<(&SharedEventRef, u64), ()>,
            >(block);
            (*block).flags |= BLOCK_HAS_SIGNATURE | BLOCK_HAS_COPY_DISPOSE;
            (*block).extra = &BLOCK_EXTRA;
            let () = msg_send![self, notifyListener:listener atValue:value block:block];
            mem::forget(block);
        }

        extern "C" fn dtor(_: *mut BlockBase<(&SharedEventRef, u64), ()>) {}

        const SIGNATURE: &[u8] = b"v16@?0Q8\0";
        const SIGNATURE_PTR: *const i8 = &SIGNATURE[0] as *const u8 as *const i8;
        static mut BLOCK_EXTRA: BlockExtra<(&SharedEventRef, u64), ()> = BlockExtra {
            unknown0: 0 as *mut i32,
            unknown1: 0 as *mut i32,
            unknown2: 0 as *mut i32,
            dtor,
            signature: &SIGNATURE_PTR,
        };
    }
}

pub enum MTLSharedEventListener {}

foreign_obj_type! {
    type CType = MTLSharedEventListener;
    pub struct SharedEventListener;
    pub struct SharedEventListenerRef;
}

impl SharedEventListener {
    pub unsafe fn from_queue_handle(queue: dispatch_queue_t) -> Self {
        let listener: SharedEventListener = msg_send![class!(MTLSharedEventListener), alloc];
        let ptr: *mut Object = msg_send![listener.as_ref(), initWithDispatchQueue: queue];
        if ptr.is_null() {
            panic!("[MTLSharedEventListener alloc] initWithDispatchQueue failed");
        }
        listener
    }

    #[cfg(feature = "dispatch")]
    pub fn from_queue(queue: &dispatch::Queue) -> Self {
        unsafe {
            let raw_queue = std::mem::transmute::<&dispatch::Queue, *const dispatch_queue_t>(queue);
            Self::from_queue_handle(*raw_queue)
        }
    }
}

pub enum MTLFence {}

foreign_obj_type! {
    type CType = MTLFence;
    pub struct Fence;
    pub struct FenceRef;
}

impl FenceRef {
    pub fn device(&self) -> &DeviceRef {
        unsafe { msg_send![self, device] }
    }

    pub fn label(&self) -> &str {
        unsafe {
            let label = msg_send![self, label];
            crate::nsstring_as_str(label)
        }
    }

    pub fn set_label(&self, label: &str) {
        unsafe {
            let nslabel = crate::nsstring_from_str(label);
            let () = msg_send![self, setLabel: nslabel];
        }
    }
}

bitflags! {
    /// The render stages at which a synchronization command is triggered.
    ///
    /// Render stages provide finer control for specifying when synchronization must occur,
    /// allowing for vertex and fragment processing to overlap in execution.
    ///
    /// See <https://developer.apple.com/documentation/metal/mtlrenderstages>
    pub struct MTLRenderStages: NSUInteger {
        /// The vertex rendering stage.
        const Vertex = 1 << 0;
        /// The fragment rendering stage.
        const Fragment = 1 << 1;
        /// The tile rendering stage.
        const Tile = 1 << 2;
    }
}

const BLOCK_HAS_COPY_DISPOSE: i32 = 0x02000000;
const BLOCK_HAS_SIGNATURE: i32 = 0x40000000;

#[repr(C)]
struct BlockBase<A, R> {
    isa: *const std::ffi::c_void,                             // 0x00
    flags: i32,                                               // 0x08
    _reserved: i32,                                           // 0x0c
    invoke: unsafe extern "C" fn(*mut Block<A, R>, ...) -> R, // 0x10
    extra: *const BlockExtra<A, R>,                           // 0x18
}

type BlockExtraDtor<A, R> = extern "C" fn(*mut BlockBase<A, R>);

#[repr(C)]
struct BlockExtra<A, R> {
    unknown0: *mut i32,          // 0x00
    unknown1: *mut i32,          // 0x08
    unknown2: *mut i32,          // 0x10
    dtor: BlockExtraDtor<A, R>,  // 0x18
    signature: *const *const i8, // 0x20
}