OpenGLRendering.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. #if os(Linux)
  2. #if GLES
  3. import COpenGLES.gles2
  4. let GL_DEPTH24_STENCIL8 = GL_DEPTH24_STENCIL8_OES
  5. let GL_TRUE = GLboolean(1)
  6. let GL_FALSE = GLboolean(0)
  7. #else
  8. import COpenGL
  9. #endif
  10. #else
  11. #if GLES
  12. import OpenGLES
  13. #else
  14. import OpenGL.GL3
  15. #endif
  16. #endif
  17. import Foundation
  18. public enum InputTextureStorageFormat {
  19. case textureCoordinates([GLfloat])
  20. case textureVBO(GLuint)
  21. }
  22. public struct InputTextureProperties {
  23. public let textureStorage: InputTextureStorageFormat
  24. public let texture: GLuint
  25. public init(textureCoordinates: [GLfloat]? = nil, textureVBO: GLuint? = nil, texture: GLuint) {
  26. self.texture = texture
  27. switch (textureCoordinates, textureVBO) {
  28. case let (.some(coordinates), .none): self.textureStorage = .textureCoordinates(coordinates)
  29. case let (.none, .some(vbo)): textureStorage = .textureVBO(vbo)
  30. case (.none, .none): fatalError("Need to specify either texture coordinates or a VBO to InputTextureProperties")
  31. case (.some, .some): fatalError("Can't specify both texture coordinates and a VBO to InputTextureProperties")
  32. }
  33. }
  34. }
  35. public struct GLSize {
  36. public let width: GLint
  37. public let height: GLint
  38. public init(width: GLint, height: GLint) {
  39. self.width = width
  40. self.height = height
  41. }
  42. public init(_ size: Size) {
  43. width = size.glWidth()
  44. height = size.glHeight()
  45. }
  46. }
  47. extension Size {
  48. init(_ size: GLSize) {
  49. width = Float(size.width)
  50. height = Float(size.height)
  51. }
  52. }
  53. public let standardImageVertices: [GLfloat] = [-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0]
  54. public let verticallyInvertedImageVertices: [GLfloat] = [-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0]
  55. // "position" and "inputTextureCoordinate", "inputTextureCoordinate2" attribute naming follows the convention of the old GPUImage
  56. public func renderQuadWithShader(_ shader: ShaderProgram, uniformSettings: ShaderUniformSettings? = nil, vertices: [GLfloat]? = nil, vertexBufferObject: GLuint? = nil, inputTextures: [InputTextureProperties], context: OpenGLContext = sharedImageProcessingContext) {
  57. switch (vertices, vertexBufferObject) {
  58. case (.none, .some): break
  59. case (.some, .none): break
  60. case (.some, .some): fatalError("Can't specify both vertices and a VBO in renderQuadWithShader()")
  61. case (.none, .none): fatalError("Can't specify both vertices and a VBO in renderQuadWithShader()")
  62. }
  63. context.makeCurrentContext()
  64. shader.use()
  65. uniformSettings?.restoreShaderSettings(shader)
  66. guard let positionAttribute = shader.attributeIndex("position") else { fatalError("A position attribute was missing from the shader program during rendering.") }
  67. if let boundVBO = vertexBufferObject {
  68. glBindBuffer(GLenum(GL_ARRAY_BUFFER), boundVBO)
  69. glVertexAttribPointer(positionAttribute, 2, GLenum(GL_FLOAT), 0, 0, nil)
  70. glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
  71. } else {
  72. glVertexAttribPointer(positionAttribute, 2, GLenum(GL_FLOAT), 0, 0, vertices!)
  73. }
  74. for (index, inputTexture) in inputTextures.enumerated() {
  75. if let textureCoordinateAttribute = shader.attributeIndex("inputTextureCoordinate".withNonZeroSuffix(index)) {
  76. switch inputTexture.textureStorage {
  77. case let .textureCoordinates(textureCoordinates):
  78. glVertexAttribPointer(textureCoordinateAttribute, 2, GLenum(GL_FLOAT), 0, 0, textureCoordinates)
  79. case let .textureVBO(textureVBO):
  80. glBindBuffer(GLenum(GL_ARRAY_BUFFER), textureVBO)
  81. glVertexAttribPointer(textureCoordinateAttribute, 2, GLenum(GL_FLOAT), 0, 0, nil)
  82. glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
  83. }
  84. } else if index == 0 {
  85. fatalError("The required attribute named inputTextureCoordinate was missing from the shader program during rendering.")
  86. }
  87. glActiveTexture(textureUnitForIndex(index))
  88. glBindTexture(GLenum(GL_TEXTURE_2D), inputTexture.texture)
  89. shader.setValue(GLint(index), forUniform: "inputImageTexture".withNonZeroSuffix(index))
  90. }
  91. glDrawArrays(GLenum(GL_TRIANGLE_STRIP), 0, 4)
  92. if vertexBufferObject != nil {
  93. glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
  94. }
  95. for (index, _) in inputTextures.enumerated() {
  96. glActiveTexture(textureUnitForIndex(index))
  97. glBindTexture(GLenum(GL_TEXTURE_2D), 0)
  98. }
  99. }
  100. public func clearFramebufferWithColor(_ color: Color) {
  101. glClearColor(GLfloat(color.redComponent), GLfloat(color.greenComponent), GLfloat(color.blueComponent), GLfloat(color.alphaComponent))
  102. glClear(GLenum(GL_COLOR_BUFFER_BIT))
  103. }
  104. func renderStencilMaskFromFramebuffer(_ framebuffer: Framebuffer) {
  105. let inputTextureProperties = framebuffer.texturePropertiesForOutputRotation(.noRotation)
  106. glEnable(GLenum(GL_STENCIL_TEST))
  107. glClearStencil(0)
  108. glClear(GLenum(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))
  109. glColorMask(GLboolean(GL_FALSE), GLboolean(GL_FALSE), GLboolean(GL_FALSE), GLboolean(GL_FALSE))
  110. glStencilFunc(GLenum(GL_ALWAYS), 1, 1)
  111. glStencilOp(GLenum(GL_KEEP), GLenum(GL_KEEP), GLenum(GL_REPLACE))
  112. #if GL
  113. glEnable(GLenum(GL_ALPHA_TEST))
  114. glAlphaFunc(GLenum(GL_NOTEQUAL), 0.0)
  115. renderQuadWithShader(sharedImageProcessingContext.passthroughShader, vertices: standardImageVertices, inputTextures: [inputTextureProperties])
  116. #else
  117. let alphaTestShader = crashOnShaderCompileFailure("Stencil") { try sharedImageProcessingContext.programForVertexShader(OneInputVertexShader, fragmentShader: AlphaTestFragmentShader) }
  118. renderQuadWithShader(alphaTestShader, vertices: standardImageVertices, inputTextures: [inputTextureProperties])
  119. #endif
  120. glColorMask(GLboolean(GL_TRUE), GLboolean(GL_TRUE), GLboolean(GL_TRUE), GLboolean(GL_TRUE))
  121. glStencilFunc(GLenum(GL_EQUAL), 1, 1)
  122. glStencilOp(GLenum(GL_KEEP), GLenum(GL_KEEP), GLenum(GL_KEEP))
  123. #if GL
  124. glDisable(GLenum(GL_ALPHA_TEST))
  125. #endif
  126. }
  127. func disableStencil() {
  128. glDisable(GLenum(GL_STENCIL_TEST))
  129. }
  130. func textureUnitForIndex(_ index: Int) -> GLenum {
  131. switch index {
  132. case 0: return GLenum(GL_TEXTURE0)
  133. case 1: return GLenum(GL_TEXTURE1)
  134. case 2: return GLenum(GL_TEXTURE2)
  135. case 3: return GLenum(GL_TEXTURE3)
  136. case 4: return GLenum(GL_TEXTURE4)
  137. case 5: return GLenum(GL_TEXTURE5)
  138. case 6: return GLenum(GL_TEXTURE6)
  139. case 7: return GLenum(GL_TEXTURE7)
  140. case 8: return GLenum(GL_TEXTURE8)
  141. default: fatalError("Attempted to address too high a texture unit")
  142. }
  143. }
  144. public func generateTexture(minFilter: Int32, magFilter: Int32, wrapS: Int32, wrapT: Int32) -> GLuint {
  145. var texture: GLuint = 0
  146. glActiveTexture(GLenum(GL_TEXTURE1))
  147. glGenTextures(1, &texture)
  148. glBindTexture(GLenum(GL_TEXTURE_2D), texture)
  149. glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), minFilter)
  150. glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), magFilter)
  151. glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), wrapS)
  152. glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), wrapT)
  153. glBindTexture(GLenum(GL_TEXTURE_2D), 0)
  154. return texture
  155. }
  156. public func uploadLocalArray(data: [GLfloat], into texture: GLuint, size: GLSize) {
  157. glActiveTexture(GLenum(GL_TEXTURE1))
  158. glBindTexture(GLenum(GL_TEXTURE_2D), texture)
  159. glTexImage2D(GLenum(GL_TEXTURE_2D), 0, GL_RGBA, size.width, size.height, 0, GLenum(GL_RGBA), GLenum(GL_FLOAT), data)
  160. glBindTexture(GLenum(GL_TEXTURE_2D), 0)
  161. }
  162. func generateFramebufferForTexture(_ texture: GLuint, width: GLint, height: GLint, internalFormat: Int32, format: Int32, type: Int32, stencil: Bool) throws -> (GLuint, GLuint?) {
  163. var framebuffer: GLuint = 0
  164. glActiveTexture(GLenum(GL_TEXTURE1))
  165. glGenFramebuffers(1, &framebuffer)
  166. glBindFramebuffer(GLenum(GL_FRAMEBUFFER), framebuffer)
  167. glBindTexture(GLenum(GL_TEXTURE_2D), texture)
  168. glTexImage2D(GLenum(GL_TEXTURE_2D), 0, internalFormat, width, height, 0, GLenum(format), GLenum(type), nil)
  169. glFramebufferTexture2D(GLenum(GL_FRAMEBUFFER), GLenum(GL_COLOR_ATTACHMENT0), GLenum(GL_TEXTURE_2D), texture, 0)
  170. let status = glCheckFramebufferStatus(GLenum(GL_FRAMEBUFFER))
  171. if status != GLenum(GL_FRAMEBUFFER_COMPLETE) {
  172. throw FramebufferCreationError(errorCode: status)
  173. }
  174. let stencilBuffer: GLuint?
  175. if stencil {
  176. stencilBuffer = try attachStencilBuffer(width: width, height: height)
  177. } else {
  178. stencilBuffer = nil
  179. }
  180. glBindTexture(GLenum(GL_TEXTURE_2D), 0)
  181. glBindFramebuffer(GLenum(GL_FRAMEBUFFER), 0)
  182. return (framebuffer, stencilBuffer)
  183. }
  184. func attachStencilBuffer(width: GLint, height: GLint) throws -> GLuint {
  185. var stencilBuffer: GLuint = 0
  186. glGenRenderbuffers(1, &stencilBuffer)
  187. glBindRenderbuffer(GLenum(GL_RENDERBUFFER), stencilBuffer)
  188. glRenderbufferStorage(GLenum(GL_RENDERBUFFER), GLenum(GL_DEPTH24_STENCIL8), width, height) // iOS seems to only support combination depth + stencil, from references
  189. #if os(iOS)
  190. glFramebufferRenderbuffer(GLenum(GL_FRAMEBUFFER), GLenum(GL_DEPTH_ATTACHMENT), GLenum(GL_RENDERBUFFER), stencilBuffer)
  191. #endif
  192. glFramebufferRenderbuffer(GLenum(GL_FRAMEBUFFER), GLenum(GL_STENCIL_ATTACHMENT), GLenum(GL_RENDERBUFFER), stencilBuffer)
  193. glBindRenderbuffer(GLenum(GL_RENDERBUFFER), 0)
  194. let status = glCheckFramebufferStatus(GLenum(GL_FRAMEBUFFER))
  195. if status != GLenum(GL_FRAMEBUFFER_COMPLETE) {
  196. throw FramebufferCreationError(errorCode: status)
  197. }
  198. return stencilBuffer
  199. }
  200. public func enableAdditiveBlending() {
  201. glBlendEquation(GLenum(GL_FUNC_ADD))
  202. glBlendFunc(GLenum(GL_ONE), GLenum(GL_ONE))
  203. glEnable(GLenum(GL_BLEND))
  204. }
  205. public func disableBlending() {
  206. glDisable(GLenum(GL_BLEND))
  207. }
  208. public func generateVBO(for vertices: [GLfloat]) -> GLuint {
  209. var newBuffer: GLuint = 0
  210. glGenBuffers(1, &newBuffer)
  211. glBindBuffer(GLenum(GL_ARRAY_BUFFER), newBuffer)
  212. glBufferData(GLenum(GL_ARRAY_BUFFER), MemoryLayout<GLfloat>.size * vertices.count, vertices, GLenum(GL_STATIC_DRAW))
  213. glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
  214. return newBuffer
  215. }
  216. public func deleteVBO(_ vbo: GLuint) {
  217. var deletedVBO = vbo
  218. glDeleteBuffers(1, &deletedVBO)
  219. }
  220. extension String {
  221. func withNonZeroSuffix(_ suffix: Int) -> String {
  222. if suffix == 0 {
  223. return self
  224. } else {
  225. return "\(self)\(suffix + 1)"
  226. }
  227. }
  228. func withGLChar(_ operation: (UnsafePointer<GLchar>) -> Void) {
  229. withCString { pointer in
  230. operation(UnsafePointer<GLchar>(pointer))
  231. }
  232. }
  233. }