1
0

metal-shader.swift 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /******************************************************************************
  2. Copyright (C) 2024 by Patrick Heyer <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. import Foundation
  15. import Metal
  16. private typealias ParserError = MetalError.OBSShaderParserError
  17. private typealias ShaderError = MetalError.OBSShaderError
  18. private typealias MetalShaderError = MetalError.MetalShaderError
  19. /// Creates a ``MetalShader`` instance from the given shader string for use as a vertex shader.
  20. /// - Parameters:
  21. /// - device: Opaque pointer to ``MetalDevice`` instance shared with `libobs`
  22. /// - shader: C character pointer with the contents of the `libobs` effect file
  23. /// - file: C character pointer with the contents of the `libobs` effect file location
  24. /// - error_string: Pointer for another C character pointer with the contents of an error description
  25. /// - Returns: Opaque pointer to a new ``MetalShader`` instance on success or `nil` on error
  26. ///
  27. /// The string pointed to by the `data` argument is a re-compiled shader string created from the associated "effect"
  28. /// file (which will contain multiple effects). Each effect is made up of several passes (though usually only a single
  29. /// pass is defined), each of which contains a vertex and fragment shader. This function is then called with just the
  30. /// vertex shader string.
  31. ///
  32. /// This vertex shader string needs to be parsed again and transpiled into a Metal shader string, which is handled by
  33. /// the ``OBSShader`` class. The transpiled string is then used to create the actual ``MetalShader`` instance.
  34. @_cdecl("device_vertexshader_create")
  35. public func device_vertexshader_create(
  36. device: UnsafeRawPointer, shader: UnsafePointer<CChar>, file: UnsafePointer<CChar>,
  37. error_string: UnsafeMutablePointer<UnsafeMutablePointer<CChar>>
  38. ) -> OpaquePointer? {
  39. let device: MetalDevice = unretained(device)
  40. let content = String(cString: shader)
  41. let fileLocation = String(cString: file)
  42. do {
  43. let obsShader = try OBSShader(type: .vertex, content: content, fileLocation: fileLocation)
  44. let transpiled = try obsShader.transpiled()
  45. guard let metaData = obsShader.metaData else {
  46. OBSLog(.error, "device_vertexshader_create: No required metadata found for transpiled shader")
  47. return nil
  48. }
  49. let metalShader = try MetalShader(device: device, source: transpiled, type: .vertex, data: metaData)
  50. return metalShader.getRetained()
  51. } catch let error as ParserError {
  52. switch error {
  53. case .parseFail(let description):
  54. OBSLog(.error, "device_vertexshader_create: Error parsing shader.\n\(description)")
  55. default:
  56. OBSLog(.error, "device_vertexshader_create: Error parsing shader.\n\(error.description)")
  57. }
  58. } catch let error as ShaderError {
  59. switch error {
  60. case .transpileError(let description):
  61. OBSLog(.error, "device_vertexshader_create: Error transpiling shader.\n\(description)")
  62. case .parseError(let description):
  63. OBSLog(.error, "device_vertexshader_create: OBS parser error.\n\(description)")
  64. case .parseFail(let description):
  65. OBSLog(.error, "device_vertexshader_create: OBS parser failure.\n\(description)")
  66. default:
  67. OBSLog(.error, "device_vertexshader_create: OBS shader error.\n\(error.description)")
  68. }
  69. } catch {
  70. switch error {
  71. case let error as MetalShaderError:
  72. OBSLog(.error, "device_vertexshader_create: Error compiling shader.\n\(error.description)")
  73. case let error as MetalError.MTLDeviceError:
  74. OBSLog(.error, "device_vertexshader_create: Device error compiling shader.\n\(error.description)")
  75. default:
  76. OBSLog(.error, "device_vertexshader_create: Unknown error occurred")
  77. }
  78. }
  79. return nil
  80. }
  81. /// Creates a ``MetalShader`` instance from the given shader string for use as a fragment shader.
  82. /// - Parameters:
  83. /// - device: Opaque pointer to ``MetalDevice`` instance shared with `libobs`
  84. /// - shader: C character pointer with the contents of the `libobs` effect file
  85. /// - file: C character pointer with the contents of the `libobs` effect file location
  86. /// - error_string: Pointer for another C character pointer with the contents of an error description
  87. /// - Returns: Opaque pointer to a new ``MetalShader`` instance on success or `nil` on error
  88. ///
  89. /// The string pointed to by the `data` argument is a re-compiled shader string created from the associated "effect"
  90. /// file (which will contain multiple effects). Each effect is made up of several passes (though usually only a single
  91. /// pass is defined), each of which contains a vertex and fragment shader. This function is then called with just the
  92. /// vertex shader string.
  93. ///
  94. /// This fragment shader string needs to be parsed again and transpiled into a Metal shader string, which is handled by
  95. /// the ``OBSShader`` class. The transpiled string is then used to create the actual ``MetalShader`` instance.
  96. @_cdecl("device_pixelshader_create")
  97. public func device_pixelshader_create(
  98. device: UnsafeRawPointer, shader: UnsafePointer<CChar>, file: UnsafePointer<CChar>,
  99. error_string: UnsafeMutablePointer<UnsafeMutablePointer<CChar>>
  100. ) -> OpaquePointer? {
  101. let device: MetalDevice = unretained(device)
  102. let content = String(cString: shader)
  103. let fileLocation = String(cString: file)
  104. do {
  105. let obsShader = try OBSShader(type: .fragment, content: content, fileLocation: fileLocation)
  106. let transpiled = try obsShader.transpiled()
  107. guard let metaData = obsShader.metaData else {
  108. OBSLog(.error, "device_pixelshader_create: No required metadata found for transpiled shader")
  109. return nil
  110. }
  111. let metalShader = try MetalShader(device: device, source: transpiled, type: .fragment, data: metaData)
  112. return metalShader.getRetained()
  113. } catch let error as ParserError {
  114. switch error {
  115. case .parseFail(let description):
  116. OBSLog(.error, "device_vertexshader_create: Error parsing shader.\n\(description)")
  117. default:
  118. OBSLog(.error, "device_vertexshader_create: Error parsing shader.\n\(error.description)")
  119. }
  120. } catch let error as ShaderError {
  121. switch error {
  122. case .transpileError(let description):
  123. OBSLog(.error, "device_vertexshader_create: Error transpiling shader.\n\(description)")
  124. case .parseError(let description):
  125. OBSLog(.error, "device_vertexshader_create: OBS parser error.\n\(description)")
  126. case .parseFail(let description):
  127. OBSLog(.error, "device_vertexshader_create: OBS parser failure.\n\(description)")
  128. default:
  129. OBSLog(.error, "device_vertexshader_create: OBS shader error.\n\(error.description)")
  130. }
  131. } catch {
  132. switch error {
  133. case let error as MetalShaderError:
  134. OBSLog(.error, "device_vertexshader_create: Error compiling shader.\n\(error.description)")
  135. case let error as MetalError.MTLDeviceError:
  136. OBSLog(.error, "device_vertexshader_create: Device error compiling shader.\n\(error.description)")
  137. default:
  138. OBSLog(.error, "device_vertexshader_create: Unknown error occurred")
  139. }
  140. }
  141. return nil
  142. }
  143. /// Loads the ``MetalShader`` instance for use as the vertex shader for the current render pipeline descriptor.
  144. /// - Parameters:
  145. /// - device: Opaque pointer to ``MetalDevice`` instance shared with `libobs`
  146. /// - vertShader: Opaque pointer to ``MetalShader`` instance shared with `libobs`
  147. ///
  148. /// This function will simply set up the ``MTLFunction`` wrapped by the ``MetalShader`` instance as the current
  149. /// pipeline descriptor's `vertexFunction`. The Metal renderer will lazily create new render pipeline states for each
  150. /// permutation of pipeline descriptors, which is a comparatively costly operation but will only occur once for any
  151. /// such permutation.
  152. ///
  153. /// > Note: If a `NULL` pointer is passed for the `vertShader` argument, the vertex function on the current render
  154. /// pipeline descriptor will be _unset_.
  155. ///
  156. @_cdecl("device_load_vertexshader")
  157. public func device_load_vertexshader(device: UnsafeRawPointer, vertShader: UnsafeRawPointer?) {
  158. let device: MetalDevice = unretained(device)
  159. if let vertShader {
  160. let shader: MetalShader = unretained(vertShader)
  161. guard shader.type == .vertex else {
  162. assertionFailure("device_load_vertexshader: Invalid shader type \(shader.type)")
  163. return
  164. }
  165. device.renderState.vertexShader = shader
  166. device.renderState.pipelineDescriptor.vertexFunction = shader.function
  167. device.renderState.pipelineDescriptor.vertexDescriptor = shader.vertexDescriptor
  168. } else {
  169. device.renderState.vertexShader = nil
  170. device.renderState.pipelineDescriptor.vertexFunction = nil
  171. device.renderState.pipelineDescriptor.vertexDescriptor = nil
  172. }
  173. }
  174. /// Loads the ``MetalShader`` instance for use as the fragment shader for the current render pipeline descriptor.
  175. /// - Parameters:
  176. /// - device: Opaque pointer to ``MetalDevice`` instance shared with `libobs`
  177. /// - vertShader: Opaque pointer to ``MetalShader`` instance shared with `libobs`
  178. ///
  179. /// This function will simply set up the ``MTLFunction`` wrapped by the ``MetalShader`` instance as the current
  180. /// pipeline descriptor's `fragmentFunction`. The Metal renderer will lazily create new render pipeline states for
  181. /// each permutation of pipeline descriptors, which is a comparatively costly operation but will only occur once for
  182. /// any such permutation.
  183. ///
  184. /// As any fragment function is potentially associated with a number of textures and associated sampler states, the
  185. /// associated arrays are reset whenever a new fragment function is set up.
  186. ///
  187. /// > Note: If a `NULL` pointer is passed for the `pixelShader` argument, the fragment function on the current render
  188. /// pipeline descriptor will be _unset_.
  189. ///
  190. @_cdecl("device_load_pixelshader")
  191. public func device_load_pixelshader(device: UnsafeRawPointer, pixelShader: UnsafeRawPointer?) {
  192. let device: MetalDevice = unretained(device)
  193. for index in 0..<Int(GS_MAX_TEXTURES) {
  194. device.renderState.textures[index] = nil
  195. device.renderState.samplers[index] = nil
  196. }
  197. if let pixelShader {
  198. let shader: MetalShader = unretained(pixelShader)
  199. guard shader.type == .fragment else {
  200. assertionFailure("device_load_pixelshader: Invalid shader type \(shader.type)")
  201. return
  202. }
  203. device.renderState.fragmentShader = shader
  204. device.renderState.pipelineDescriptor.fragmentFunction = shader.function
  205. if let samplers = shader.samplers {
  206. device.renderState.samplers.replaceSubrange(0..<samplers.count, with: samplers)
  207. }
  208. } else {
  209. device.renderState.pipelineDescriptor.fragmentFunction = nil
  210. }
  211. }
  212. /// Gets the ``MetalShader`` set up as the current vertex shader for the pipeline
  213. /// - Parameter device: Opaque pointer to ``MetalDevice`` instance shared with `libobs`
  214. /// - Returns: Opaque pointer to ``MetalShader`` instance if a vertex shader is currently set up or `nil` otherwise
  215. @_cdecl("device_get_vertex_shader")
  216. public func device_get_vertex_shader(device: UnsafeRawPointer) -> OpaquePointer? {
  217. let device: MetalDevice = unretained(device)
  218. if let shader = device.renderState.vertexShader {
  219. return shader.getUnretained()
  220. } else {
  221. return nil
  222. }
  223. }
  224. /// Gets the ``MetalShader`` set up as the current fragment shader for the pipeline
  225. /// - Parameter device: Opaque pointer to ``MetalDevice`` instance shared with `libobs`
  226. /// - Returns: Opaque pointer to ``MetalShader`` instance if a fragment shader is currently set up or `nil` otherwise
  227. @_cdecl("device_get_pixel_shader")
  228. public func device_get_pixel_shader(device: UnsafeRawPointer) -> OpaquePointer? {
  229. let device: MetalDevice = unretained(device)
  230. if let shader = device.renderState.fragmentShader {
  231. return shader.getUnretained()
  232. } else {
  233. return nil
  234. }
  235. }
  236. /// Requests the deinitialization of the ``MetalShader`` instance shared with `libobs`
  237. /// - Parameter shader: Opaque pointer to ``MetalShader`` instance shared with `libobs`
  238. ///
  239. /// Ownership of the ``MetalShader`` instance will be transferred into the function and if this was the last strong
  240. /// reference to it, the object will be automatically deinitialized and deallocated by Swift.
  241. @_cdecl("gs_shader_destroy")
  242. public func gs_shader_destroy(shader: UnsafeRawPointer) {
  243. let _ = retained(shader) as MetalShader
  244. }
  245. /// Gets the number of uniform parameters used on the ``MetalShader``
  246. /// - Parameter shader: Opaque pointer to ``MetalShader`` instance shared with `libobs`
  247. /// - Returns: Number of uniforms
  248. @_cdecl("gs_shader_get_num_params")
  249. public func gs_shader_get_num_params(shader: UnsafeRawPointer) -> UInt32 {
  250. let shader: MetalShader = unretained(shader)
  251. return UInt32(shader.uniforms.count)
  252. }
  253. /// Gets a uniform parameter from the ``MetalShader`` by its array index
  254. /// - Parameters:
  255. /// - shader: Opaque pointer to ``MetalShader`` instance shared with `libobs`
  256. /// - param: Array index of uniform parameter to get
  257. /// - Returns: Opaque pointer to a ``ShaderUniform`` instance if index within uniform array bounds or `nil` otherwise
  258. ///
  259. /// This function requires that the array indices of the uniforms array do not change for a ``MetalShader`` and also
  260. /// that the exact order of uniforms is identical between `libobs`'s interpretation of the effects file and the
  261. /// transpiled shader's analysis of the uniforms.
  262. ///
  263. /// > Important: The opaque pointer for the ``ShaderUniform`` instance is passe unretained and as such can become
  264. /// invalid when its owning ``MetalShader`` instance either is deinitialized itself or is replaced in the uniforms
  265. /// array.
  266. @_cdecl("gs_shader_get_param_by_idx")
  267. public func gs_shader_get_param_by_idx(shader: UnsafeRawPointer, param: UInt32) -> OpaquePointer? {
  268. let shader: MetalShader = unretained(shader)
  269. guard param < shader.uniforms.count else {
  270. return nil
  271. }
  272. let uniform = shader.uniforms[Int(param)]
  273. let unretained = Unmanaged.passUnretained(uniform).toOpaque()
  274. return OpaquePointer(unretained)
  275. }
  276. /// Gets a uniform parameter from the ``MetalShader`` by its name
  277. /// - Parameters:
  278. /// - shader: Opaque pointer to ``MetalShader`` instance shared with `libobs`
  279. /// - param: C character array pointer with the name of the requested uniform parameter
  280. /// - Returns: Opaque pointer to a ``ShaderUniform`` instance if any uniform with the provided name was found or `nil`
  281. /// otherwise
  282. ///
  283. /// > Important: The opaque pointer for the ``ShaderUniform`` instance is passe unretained and as such can become
  284. /// invalid when its owning ``MetalShader`` instance either is deinitialized itself or is replaced in the uniforms
  285. /// array.
  286. ///
  287. @_cdecl("gs_shader_get_param_by_name")
  288. public func gs_shader_get_param_by_name(shader: UnsafeRawPointer, param: UnsafeMutablePointer<CChar>) -> OpaquePointer?
  289. {
  290. let shader: MetalShader = unretained(shader)
  291. let paramName = String(cString: param)
  292. for uniform in shader.uniforms {
  293. if uniform.name == paramName {
  294. let unretained = Unmanaged.passUnretained(uniform).toOpaque()
  295. return OpaquePointer(unretained)
  296. }
  297. }
  298. return nil
  299. }
  300. /// Gets the uniform parameter associated with the view projection matrix used by the ``MetalShader``
  301. /// - Parameter shader: Opaque pointer to ``MetalShader`` instance shared with `libobs`
  302. /// - Returns: Opaque pointer to a ``ShaderUniform`` instance if a uniform for the view projection matrix was found
  303. /// or `nil` otherwise
  304. ///
  305. /// The uniform for the view projection matrix has the associated name `viewProj` in the Metal renderer, thus a
  306. /// name-based lookup is used to find the associated ``ShaderUniform`` instance.
  307. ///
  308. /// > Important: The opaque pointer for the ``ShaderUniform`` instance is passe unretained and as such can become
  309. /// invalid when its owning ``MetalShader`` instance either is deinitialized itself or is replaced in the uniforms
  310. /// array.
  311. ///
  312. @_cdecl("gs_shader_get_viewproj_matrix")
  313. public func gs_shader_get_viewproj_matrix(shader: UnsafeRawPointer) -> OpaquePointer? {
  314. let shader: MetalShader = unretained(shader)
  315. let paramName = "viewProj"
  316. for uniform in shader.uniforms {
  317. if uniform.name == paramName {
  318. let unretained = Unmanaged.passUnretained(uniform).toOpaque()
  319. return OpaquePointer(unretained)
  320. }
  321. }
  322. return nil
  323. }
  324. /// Gets the uniform parameter associated with the world projection matrix used by the ``MetalShader``
  325. /// - Parameter shader: Opaque pointer to ``MetalShader`` instance shared with `libobs`
  326. /// - Returns: Opaque pointer to a ``ShaderUniform`` instance if a uniform for the world projection matrix was found
  327. /// or `nil` otherwise
  328. ///
  329. /// The uniform for the view projection matrix has the associated name `worldProj` in the Metal renderer, thus a
  330. /// name-based lookup is used to find the associated ``ShaderUniform`` instance.
  331. ///
  332. /// > Important: The opaque pointer for the ``ShaderUniform`` instance is passe unretained and as such can become
  333. /// invalid when its owning ``MetalShader`` instance either is deinitialized itself or is replaced in the uniforms
  334. /// array.
  335. @_cdecl("gs_shader_get_world_matrix")
  336. public func gs_shader_get_world_matrix(shader: UnsafeRawPointer) -> OpaquePointer? {
  337. let shader: MetalShader = unretained(shader)
  338. let paramName = "worldProj"
  339. for uniform in shader.uniforms {
  340. if uniform.name == paramName {
  341. let unretained = Unmanaged.passUnretained(uniform).toOpaque()
  342. return OpaquePointer(unretained)
  343. }
  344. }
  345. return nil
  346. }
  347. /// Gets the name and uniform type from the ``ShaderUniform`` instance
  348. /// - Parameters:
  349. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  350. /// - info: Pointer to a `gs_shader_param_info` struct pre-allocated by `libobs`
  351. ///
  352. /// > Warning: The C character array pointer holding the name of the uniform is managed by Swift and might become
  353. /// invalid at any point in time.
  354. @_cdecl("gs_shader_get_param_info")
  355. public func gs_shader_get_param_info(shaderParam: UnsafeRawPointer, info: UnsafeMutablePointer<gs_shader_param_info>) {
  356. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  357. shaderUniform.name.withCString {
  358. info.pointee.name = $0
  359. }
  360. info.pointee.type = shaderUniform.gsType
  361. }
  362. /// Sets a boolean value on the ``ShaderUniform`` instance
  363. /// - Parameters:
  364. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  365. /// - val: Boolean value to set for the uniform
  366. @_cdecl("gs_shader_set_bool")
  367. public func gs_shader_set_bool(shaderParam: UnsafeRawPointer, val: Bool) {
  368. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  369. withUnsafePointer(to: val) {
  370. shaderUniform.setParameter(data: $0, size: MemoryLayout<Int32>.size)
  371. }
  372. }
  373. /// Sets a 32-bit floating point value on the ``ShaderUniform`` instance
  374. /// - Parameters:
  375. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  376. /// - val: 32-bit floating point value to set for the uniform
  377. @_cdecl("gs_shader_set_float")
  378. public func gs_shader_set_float(shaderParam: UnsafeRawPointer, val: Float32) {
  379. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  380. withUnsafePointer(to: val) {
  381. shaderUniform.setParameter(data: $0, size: MemoryLayout<Float32>.size)
  382. }
  383. }
  384. /// Sets a 32-bit signed integer value on the ``ShaderUniform`` instance
  385. /// - Parameters:
  386. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  387. /// - val: 32-bit signed integer value to set for the uniform
  388. @_cdecl("gs_shader_set_int")
  389. public func gs_shader_set_int(shaderParam: UnsafeRawPointer, val: Int32) {
  390. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  391. withUnsafePointer(to: val) {
  392. shaderUniform.setParameter(data: $0, size: MemoryLayout<Int32>.size)
  393. }
  394. }
  395. /// Sets a 3x3 matrix of 32-bit floating point values on the ``ShaderUniform``instance
  396. /// - Parameters:
  397. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  398. /// - val: A 3x3 matrix of 32-bit floating point values
  399. ///
  400. /// The 3x3 matrix is converted into a 4x4 matrix (padded with zeros) before actually being set as the uniform data
  401. @_cdecl("gs_shader_set_matrix3")
  402. public func gs_shader_set_matrix3(shaderParam: UnsafeRawPointer, val: UnsafePointer<matrix3>) {
  403. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  404. var newMatrix = matrix4()
  405. matrix4_from_matrix3(&newMatrix, val)
  406. shaderUniform.setParameter(data: &newMatrix, size: MemoryLayout<matrix4>.size)
  407. }
  408. /// Sets a 4x4 matrix of 32-bit floating point values on the ``ShaderUniform`` instance
  409. /// - Parameters:
  410. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  411. /// - val: A 4x4 matrix of 32-bit floating point values
  412. @_cdecl("gs_shader_set_matrix4")
  413. public func gs_shader_set_matrix4(shaderParam: UnsafeRawPointer, val: UnsafePointer<matrix4>) {
  414. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  415. shaderUniform.setParameter(data: val, size: MemoryLayout<matrix4>.size)
  416. }
  417. /// Sets a vector of 2 32-bit floating point values on the ``ShaderUniform`` instance
  418. /// - Parameters:
  419. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  420. /// - val: A vector of 2 32-bit floating point values
  421. @_cdecl("gs_shader_set_vec2")
  422. public func gs_shader_set_vec2(shaderParam: UnsafeRawPointer, val: UnsafePointer<vec2>) {
  423. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  424. shaderUniform.setParameter(data: val, size: MemoryLayout<vec2>.size)
  425. }
  426. /// Sets a vector of 3 32-bit floating point values on the ``ShaderUniform`` instance
  427. /// - Parameters:
  428. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  429. /// - val: A vector of 3 32-bit floating point values
  430. @_cdecl("gs_shader_set_vec3")
  431. public func gs_shader_set_vec3(shaderParam: UnsafeRawPointer, val: UnsafePointer<vec3>) {
  432. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  433. shaderUniform.setParameter(data: val, size: MemoryLayout<vec3>.size)
  434. }
  435. /// Sets a vector of 4 32-bit floating point values on the ``ShaderUniform`` instance
  436. /// - Parameters:
  437. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  438. /// - val: A vector of 4 32-bit floating point values
  439. @_cdecl("gs_shader_set_vec4")
  440. public func gs_shader_set_vec4(shaderParam: UnsafeRawPointer, val: UnsafePointer<vec4>) {
  441. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  442. shaderUniform.setParameter(data: val, size: MemoryLayout<vec4>.size)
  443. }
  444. /// Sets up the data of a `gs_shader_texture` struct as a uniform on the ``ShaderUniform`` instance
  445. /// - Parameters:
  446. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  447. /// - val: A pointer to a `gs_shader_struct` containing an opaque pointer to the actual ``MetalTexture`` instance
  448. /// and an sRGB gamma state flag
  449. ///
  450. /// The struct's data is copied verbatim into the uniform, which allows reconstruction of the pointer at a later point
  451. /// as long as the actual ``MetalTexture`` instance still exists.
  452. @_cdecl("gs_shader_set_texture")
  453. public func gs_shader_set_texture(shaderParam: UnsafeRawPointer, val: UnsafePointer<gs_shader_texture>?) {
  454. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  455. if let val {
  456. shaderUniform.setParameter(data: val, size: MemoryLayout<gs_shader_texture>.size)
  457. }
  458. }
  459. /// Sets an arbitrary value on the ``ShaderUniform`` instance
  460. /// - Parameters:
  461. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  462. /// - val: Opaque pointer to some unknown data for use as the uniform
  463. /// - size: The size of the data available at the memory pointed to by the `val` argument
  464. ///
  465. /// The ``ShaderUniform`` itself is set up to hold a specific uniform type, each of which is associated with a size of
  466. /// bytes required for it. If the size of the data pointed to by `val` does not fit into this size, the uniform will
  467. /// not be updated.
  468. ///
  469. /// If the ``ShaderUniform`` expects a texture parameter, the pointer will be bound as memory of a `gs_shader_texture`
  470. /// instance before setting it up.
  471. @_cdecl("gs_shader_set_val")
  472. public func gs_shader_set_val(shaderParam: UnsafeRawPointer, val: UnsafeRawPointer, size: UInt32) {
  473. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  474. let size = Int(size)
  475. let valueSize = shaderUniform.gsType.size
  476. guard valueSize == size else {
  477. assertionFailure("gs_shader_set_val: Required size of uniform does not match size of input")
  478. return
  479. }
  480. if shaderUniform.gsType == GS_SHADER_PARAM_TEXTURE {
  481. let shaderTexture = val.bindMemory(to: gs_shader_texture.self, capacity: 1)
  482. shaderUniform.setParameter(data: shaderTexture, size: valueSize)
  483. } else {
  484. let bytes = val.bindMemory(to: UInt8.self, capacity: valueSize)
  485. shaderUniform.setParameter(data: bytes, size: valueSize)
  486. }
  487. }
  488. /// Resets the ``ShaderUniform``'s current data with its default data
  489. /// - Parameter shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  490. ///
  491. /// Each ``ShaderUniform`` is optionally set up with a set of default data (stored as an array of bytes) which is
  492. /// simply copied into the current values.
  493. @_cdecl("gs_shader_set_default")
  494. public func gs_shader_set_default(shaderParam: UnsafeRawPointer) {
  495. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  496. if let defaultValues = shaderUniform.defaultValues {
  497. shaderUniform.currentValues = Array(defaultValues)
  498. }
  499. }
  500. /// Sets up the ``MTLSamplerState`` as the sampler state for the ``ShaderUniform``
  501. /// - Parameters:
  502. /// - shaderParam: Opaque pointer to ``ShaderUniform`` instance shared with `libobs`
  503. /// - sampler: Opaque pointer to ``MTLSamplerState`` instance shared with `libobs`
  504. ///
  505. /// If the uniform represents a texture for use in the associated shader, this function will also set up the provided
  506. /// ``MTLSamplerState`` for the associated texture's texture slot.
  507. @_cdecl("gs_shader_set_next_sampler")
  508. public func gs_shader_set_next_sampler(shaderParam: UnsafeRawPointer, sampler: UnsafeRawPointer) {
  509. let shaderUniform: MetalShader.ShaderUniform = unretained(shaderParam)
  510. let samplerState = Unmanaged<MTLSamplerState>.fromOpaque(sampler).takeUnretainedValue()
  511. shaderUniform.samplerState = samplerState
  512. }