metal-stagesurf.swift 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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. /// Creates a ``MetalStageBuffer`` instance for use as a stage surface by `libobs`
  17. /// - Parameters:
  18. /// - device: Opaque pointer to ``MetalStageBuffer`` instance shared with `libobs`
  19. /// - width: Number of data rows
  20. /// - height: Number of data columns
  21. /// - format: Color format of the stage surface texture as defined by `libobs`'s `gs_color_format` struct
  22. /// - Returns: A ``MetalStageBuffer`` instance that wraps a `MTLBuffer` or a `nil` pointer otherwise
  23. ///
  24. /// Stage surfaces are used by `libobs` for transfer of image data from the GPU to the CPU. The most common use case is
  25. /// to block transfer (blit) the video output texture into a staging texture and then downloading the texture data from
  26. /// the staging texture into CPU memory.
  27. @_cdecl("device_stagesurface_create")
  28. public func device_stagesurface_create(device: UnsafeRawPointer, width: UInt32, height: UInt32, format: gs_color_format)
  29. -> OpaquePointer?
  30. {
  31. let device: MetalDevice = unretained(device)
  32. guard
  33. let buffer = MetalStageBuffer(
  34. device: device,
  35. width: Int(width),
  36. height: Int(height),
  37. format: format.mtlFormat
  38. )
  39. else {
  40. OBSLog(.error, "device_stagesurface_create: Unable to create MetalStageBuffer with provided format \(format)")
  41. return nil
  42. }
  43. return buffer.getRetained()
  44. }
  45. /// Requests the deinitialization of the ``MetalStageBuffer`` instance that was shared with `libobs`
  46. /// - Parameter stagesurf: Opaque pointer to ``MetalStageBuffer`` instance shared with `libobs`
  47. ///
  48. /// The ownership of the shared pointer is transferred into this function and the instance is placed under Swift's
  49. /// memory management again.
  50. @_cdecl("gs_stagesurface_destroy")
  51. public func gs_stagesurface_destroy(stagesurf: UnsafeRawPointer) {
  52. let _ = retained(stagesurf) as MetalStageBuffer
  53. }
  54. /// Gets the "width" of the staging texture
  55. /// - Parameter stagesurf: Opaque pointer to ``MetalStageBuffer`` instance shared with `libobs`
  56. /// - Returns: Amount of data rows in the buffer representing the width of an image
  57. @_cdecl("gs_stagesurface_get_width")
  58. public func gs_stagesurface_get_width(stagesurf: UnsafeRawPointer) -> UInt32 {
  59. let stageSurface: MetalStageBuffer = unretained(stagesurf)
  60. return UInt32(stageSurface.width)
  61. }
  62. /// Gets the "height" of the staging texture
  63. /// - Parameter stagesurf: Opaque pointer to ``MetalStageBuffer`` instance shared with `libobs`
  64. /// - Returns: Amount of data columns in the buffer representing the height of an image
  65. @_cdecl("gs_stagesurface_get_height")
  66. public func gs_stagesurface_get_height(stagesurf: UnsafeRawPointer) -> UInt32 {
  67. let stageSurface: MetalStageBuffer = unretained(stagesurf)
  68. return UInt32(stageSurface.height)
  69. }
  70. /// Gets the color format of the staged image data
  71. /// - Parameter stagesurf: Opaque pointer to ``MetalStageBuffer`` instance shared with `libobs`
  72. /// - Returns: Color format in `libobs`'s own color format struct
  73. ///
  74. /// The Metal color format is automatically converted into its corresponding `gs_color_format` variant.
  75. @_cdecl("gs_stagesurface_get_color_format")
  76. public func gs_stagesurface_get_height(stagesurf: UnsafeRawPointer) -> gs_color_format {
  77. let stageSurface: MetalStageBuffer = unretained(stagesurf)
  78. return stageSurface.format.gsColorFormat
  79. }
  80. /// Provides a pointer to memory that contains the buffer's raw data.
  81. /// - Parameters:
  82. /// - stagesurf: Opaque pointer to ``MetalStageBuffer`` instance shared with `libobs`
  83. /// - ptr: Opaque pointer to memory which itself can hold a pointer to the actual image data
  84. /// - linesize: Opaque pointer to memory which itself can hold the row size of the image data
  85. /// - Returns: `true` if the data can be provided, `false` otherwise
  86. ///
  87. /// Metal does not provide "map" and "unmap" operations as they exist in Direct3D11, as resource management and
  88. /// synchronization needs to be handled explicitly by the application. To reduce unnecessary copy operations, the
  89. /// original texture's data was copied into a `MTLBuffer` (instead of another texture) using a block transfer on the
  90. /// GPU.
  91. ///
  92. /// As the Metal renderer is only available on Apple Silicon machines, this means that the buffer itself is available
  93. /// for direct access by the CPU and thus a pointer to the raw bytes of the buffer can be shared with `libobs`.
  94. @_cdecl("gs_stagesurface_map")
  95. public func gs_stagesurface_map(
  96. stagesurf: UnsafeRawPointer, ptr: UnsafeMutablePointer<UnsafeMutableRawPointer>,
  97. linesize: UnsafeMutablePointer<UInt32>
  98. ) -> Bool {
  99. let stageSurface: MetalStageBuffer = unretained(stagesurf)
  100. ptr.pointee = stageSurface.buffer.contents()
  101. linesize.pointee = UInt32(stageSurface.width * stageSurface.format.bytesPerPixel!)
  102. return true
  103. }
  104. /// Signals that the downloaded image data of the stage texture is not needed anymore.
  105. ///
  106. /// - Parameter stagesurf: Opaque pointer to ``MetalStageBuffer`` instance shared with `libobs`
  107. ///
  108. /// This function has no effect as the `MTLBuffer` used by the ``MetalStageBuffer`` does not need to be "unmapped".
  109. @_cdecl("gs_stagesurface_unmap")
  110. public func gs_stagesurface_unmap(stagesurf: UnsafeRawPointer) {
  111. return
  112. }