RendererOpenGL3.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. package imgui
  2. import (
  3. "errors"
  4. "fmt"
  5. "image"
  6. "github.com/go-gl/gl/v3.2-core/gl"
  7. )
  8. // OpenGL3 implements a renderer based on github.com/go-gl/gl (v3.2-core).
  9. type OpenGL3 struct {
  10. imguiIO IO
  11. glslVersion string
  12. fontTexture uint32
  13. shaderHandle uint32
  14. vertHandle uint32
  15. fragHandle uint32
  16. attribLocationTex int32
  17. attribLocationProjMtx int32
  18. attribLocationPosition int32
  19. attribLocationUV int32
  20. attribLocationColor int32
  21. vboHandle uint32
  22. elementsHandle uint32
  23. contentScale float32
  24. textureMinFilter int32
  25. textureMagFilter int32
  26. }
  27. // Texture filtering types.
  28. const (
  29. textureFilterNearest = iota
  30. textureFilterLinear
  31. textureFilterNearestMipmapNearest
  32. textureFilterLinearMipmapNearest
  33. textureFilterNearestMipmapLinear
  34. textureFilterLinearMipmapLinear
  35. )
  36. // NewOpenGL3 attempts to initialize a renderer.
  37. // An OpenGL context has to be established before calling this function.
  38. func NewOpenGL3(io IO, contentScale float32) (*OpenGL3, error) {
  39. err := gl.Init()
  40. if err != nil {
  41. return nil, fmt.Errorf("failed to initialize OpenGL: %v", err)
  42. }
  43. renderer := &OpenGL3{
  44. imguiIO: io,
  45. glslVersion: "#version 150",
  46. contentScale: contentScale,
  47. textureMinFilter: gl.LINEAR,
  48. textureMagFilter: gl.LINEAR,
  49. }
  50. renderer.createDeviceObjects()
  51. return renderer, nil
  52. }
  53. // Dispose cleans up the resources.
  54. func (renderer *OpenGL3) Dispose() {
  55. renderer.invalidateDeviceObjects()
  56. }
  57. // PreRender clears the framebuffer.
  58. func (renderer *OpenGL3) PreRender(clearColor [4]float32) {
  59. gl.ClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3])
  60. gl.Clear(gl.COLOR_BUFFER_BIT)
  61. }
  62. // Render translates the ImGui draw data to OpenGL3 commands.
  63. func (renderer *OpenGL3) Render(displaySize [2]float32, framebufferSize [2]float32, drawData DrawData) {
  64. // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
  65. displayWidth, displayHeight := displaySize[0], displaySize[1]
  66. fbWidth, fbHeight := framebufferSize[0], framebufferSize[1]
  67. if (fbWidth <= 0) || (fbHeight <= 0) {
  68. return
  69. }
  70. drawData.ScaleClipRects(Vec2{
  71. X: fbWidth / displayWidth,
  72. Y: fbHeight / displayHeight,
  73. })
  74. // Backup GL state
  75. var lastActiveTexture int32
  76. gl.GetIntegerv(gl.ACTIVE_TEXTURE, &lastActiveTexture)
  77. gl.ActiveTexture(gl.TEXTURE0)
  78. var lastProgram int32
  79. gl.GetIntegerv(gl.CURRENT_PROGRAM, &lastProgram)
  80. var lastTexture int32
  81. gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
  82. var lastSampler int32
  83. gl.GetIntegerv(gl.SAMPLER_BINDING, &lastSampler)
  84. var lastArrayBuffer int32
  85. gl.GetIntegerv(gl.ARRAY_BUFFER_BINDING, &lastArrayBuffer)
  86. var lastElementArrayBuffer int32
  87. gl.GetIntegerv(gl.ELEMENT_ARRAY_BUFFER_BINDING, &lastElementArrayBuffer)
  88. var lastVertexArray int32
  89. gl.GetIntegerv(gl.VERTEX_ARRAY_BINDING, &lastVertexArray)
  90. var lastPolygonMode [2]int32
  91. gl.GetIntegerv(gl.POLYGON_MODE, &lastPolygonMode[0])
  92. var lastViewport [4]int32
  93. gl.GetIntegerv(gl.VIEWPORT, &lastViewport[0])
  94. var lastScissorBox [4]int32
  95. gl.GetIntegerv(gl.SCISSOR_BOX, &lastScissorBox[0])
  96. var lastBlendSrcRgb int32
  97. gl.GetIntegerv(gl.BLEND_SRC_RGB, &lastBlendSrcRgb)
  98. var lastBlendDstRgb int32
  99. gl.GetIntegerv(gl.BLEND_DST_RGB, &lastBlendDstRgb)
  100. var lastBlendSrcAlpha int32
  101. gl.GetIntegerv(gl.BLEND_SRC_ALPHA, &lastBlendSrcAlpha)
  102. var lastBlendDstAlpha int32
  103. gl.GetIntegerv(gl.BLEND_DST_ALPHA, &lastBlendDstAlpha)
  104. var lastBlendEquationRgb int32
  105. gl.GetIntegerv(gl.BLEND_EQUATION_RGB, &lastBlendEquationRgb)
  106. var lastBlendEquationAlpha int32
  107. gl.GetIntegerv(gl.BLEND_EQUATION_ALPHA, &lastBlendEquationAlpha)
  108. lastEnableBlend := gl.IsEnabled(gl.BLEND)
  109. lastEnableCullFace := gl.IsEnabled(gl.CULL_FACE)
  110. lastEnableDepthTest := gl.IsEnabled(gl.DEPTH_TEST)
  111. lastEnableScissorTest := gl.IsEnabled(gl.SCISSOR_TEST)
  112. // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
  113. gl.Enable(gl.BLEND)
  114. gl.BlendEquation(gl.FUNC_ADD)
  115. gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  116. gl.Disable(gl.CULL_FACE)
  117. gl.Disable(gl.DEPTH_TEST)
  118. gl.Enable(gl.SCISSOR_TEST)
  119. gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)
  120. // Setup viewport, orthographic projection matrix
  121. // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
  122. // DisplayMin is typically (0,0) for single viewport apps.
  123. gl.Viewport(0, 0, int32(fbWidth), int32(fbHeight))
  124. orthoProjection := [4][4]float32{
  125. {2.0 / displayWidth, 0.0, 0.0, 0.0},
  126. {0.0, 2.0 / -displayHeight, 0.0, 0.0},
  127. {0.0, 0.0, -1.0, 0.0},
  128. {-1.0, 1.0, 0.0, 1.0},
  129. }
  130. gl.UseProgram(renderer.shaderHandle)
  131. gl.Uniform1i(renderer.attribLocationTex, 0)
  132. gl.UniformMatrix4fv(renderer.attribLocationProjMtx, 1, false, &orthoProjection[0][0])
  133. gl.BindSampler(0, 0) // Rely on combined texture/sampler state.
  134. // Recreate the VAO every time
  135. // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and
  136. // we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.)
  137. var vaoHandle uint32
  138. gl.GenVertexArrays(1, &vaoHandle)
  139. gl.BindVertexArray(vaoHandle)
  140. gl.BindBuffer(gl.ARRAY_BUFFER, renderer.vboHandle)
  141. gl.EnableVertexAttribArray(uint32(renderer.attribLocationPosition))
  142. gl.EnableVertexAttribArray(uint32(renderer.attribLocationUV))
  143. gl.EnableVertexAttribArray(uint32(renderer.attribLocationColor))
  144. vertexSize, vertexOffsetPos, vertexOffsetUv, vertexOffsetCol := VertexBufferLayout()
  145. gl.VertexAttribPointerWithOffset(uint32(renderer.attribLocationPosition), 2, gl.FLOAT, false, int32(vertexSize), uintptr(vertexOffsetPos))
  146. gl.VertexAttribPointerWithOffset(uint32(renderer.attribLocationUV), 2, gl.FLOAT, false, int32(vertexSize), uintptr(vertexOffsetUv))
  147. gl.VertexAttribPointerWithOffset(uint32(renderer.attribLocationColor), 4, gl.UNSIGNED_BYTE, true, int32(vertexSize), uintptr(vertexOffsetCol))
  148. indexSize := IndexBufferLayout()
  149. drawType := gl.UNSIGNED_SHORT
  150. if indexSize == 4 {
  151. drawType = gl.UNSIGNED_INT
  152. }
  153. // Draw
  154. for _, list := range drawData.CommandLists() {
  155. // var indexBufferOffset uintptr
  156. vertexBuffer, vertexBufferSize := list.VertexBuffer()
  157. gl.BindBuffer(gl.ARRAY_BUFFER, renderer.vboHandle)
  158. gl.BufferData(gl.ARRAY_BUFFER, vertexBufferSize, vertexBuffer, gl.STREAM_DRAW)
  159. indexBuffer, indexBufferSize := list.IndexBuffer()
  160. gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, renderer.elementsHandle)
  161. gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, indexBufferSize, indexBuffer, gl.STREAM_DRAW)
  162. for _, cmd := range list.Commands() {
  163. if cmd.HasUserCallback() {
  164. cmd.CallUserCallback(list)
  165. } else {
  166. gl.BindTexture(gl.TEXTURE_2D, uint32(cmd.TextureID()))
  167. gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, renderer.textureMinFilter) // minification filter
  168. gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, renderer.textureMagFilter) // magnification filter
  169. clipRect := cmd.ClipRect()
  170. gl.Scissor(int32(clipRect.X), int32(fbHeight)-int32(clipRect.W), int32(clipRect.Z-clipRect.X), int32(clipRect.W-clipRect.Y))
  171. gl.DrawElementsBaseVertexWithOffset(gl.TRIANGLES, int32(cmd.ElementCount()), uint32(drawType),
  172. uintptr(cmd.IdxOffset()*uint(indexSize)), int32(cmd.VertexOffset()))
  173. }
  174. // indexBufferOffset += uintptr(cmd.ElementCount() * indexSize)
  175. }
  176. }
  177. gl.DeleteVertexArrays(1, &vaoHandle)
  178. // Restore modified GL state
  179. gl.UseProgram(uint32(lastProgram))
  180. gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
  181. gl.BindSampler(0, uint32(lastSampler))
  182. gl.ActiveTexture(uint32(lastActiveTexture))
  183. gl.BindVertexArray(uint32(lastVertexArray))
  184. gl.BindBuffer(gl.ARRAY_BUFFER, uint32(lastArrayBuffer))
  185. gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, uint32(lastElementArrayBuffer))
  186. gl.BlendEquationSeparate(uint32(lastBlendEquationRgb), uint32(lastBlendEquationAlpha))
  187. gl.BlendFuncSeparate(uint32(lastBlendSrcRgb), uint32(lastBlendDstRgb), uint32(lastBlendSrcAlpha), uint32(lastBlendDstAlpha))
  188. if lastEnableBlend {
  189. gl.Enable(gl.BLEND)
  190. } else {
  191. gl.Disable(gl.BLEND)
  192. }
  193. if lastEnableCullFace {
  194. gl.Enable(gl.CULL_FACE)
  195. } else {
  196. gl.Disable(gl.CULL_FACE)
  197. }
  198. if lastEnableDepthTest {
  199. gl.Enable(gl.DEPTH_TEST)
  200. } else {
  201. gl.Disable(gl.DEPTH_TEST)
  202. }
  203. if lastEnableScissorTest {
  204. gl.Enable(gl.SCISSOR_TEST)
  205. } else {
  206. gl.Disable(gl.SCISSOR_TEST)
  207. }
  208. gl.PolygonMode(gl.FRONT_AND_BACK, uint32(lastPolygonMode[0]))
  209. gl.Viewport(lastViewport[0], lastViewport[1], lastViewport[2], lastViewport[3])
  210. gl.Scissor(lastScissorBox[0], lastScissorBox[1], lastScissorBox[2], lastScissorBox[3])
  211. }
  212. func (renderer *OpenGL3) createDeviceObjects() {
  213. // Backup GL state
  214. var lastTexture int32
  215. var lastArrayBuffer int32
  216. var lastVertexArray int32
  217. gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
  218. gl.GetIntegerv(gl.ARRAY_BUFFER_BINDING, &lastArrayBuffer)
  219. gl.GetIntegerv(gl.VERTEX_ARRAY_BINDING, &lastVertexArray)
  220. vertexShader := renderer.glslVersion + `
  221. uniform mat4 ProjMtx;
  222. in vec2 Position;
  223. in vec2 UV;
  224. in vec4 Color;
  225. out vec2 Frag_UV;
  226. out vec4 Frag_Color;
  227. void main()
  228. {
  229. Frag_UV = UV;
  230. Frag_Color = Color;
  231. gl_Position = ProjMtx * vec4(Position.xy,0,1);
  232. }
  233. `
  234. fragmentShader := renderer.glslVersion + `
  235. uniform sampler2D Texture;
  236. in vec2 Frag_UV;
  237. in vec4 Frag_Color;
  238. out vec4 Out_Color;
  239. void main()
  240. {
  241. Out_Color = Frag_Color * texture(Texture, Frag_UV);
  242. }
  243. `
  244. renderer.shaderHandle = gl.CreateProgram()
  245. renderer.vertHandle = gl.CreateShader(gl.VERTEX_SHADER)
  246. renderer.fragHandle = gl.CreateShader(gl.FRAGMENT_SHADER)
  247. glShaderSource := func(handle uint32, source string) {
  248. csource, free := gl.Strs(source + "\x00")
  249. defer free()
  250. gl.ShaderSource(handle, 1, csource, nil)
  251. }
  252. glShaderSource(renderer.vertHandle, vertexShader)
  253. glShaderSource(renderer.fragHandle, fragmentShader)
  254. gl.CompileShader(renderer.vertHandle)
  255. gl.CompileShader(renderer.fragHandle)
  256. gl.AttachShader(renderer.shaderHandle, renderer.vertHandle)
  257. gl.AttachShader(renderer.shaderHandle, renderer.fragHandle)
  258. gl.LinkProgram(renderer.shaderHandle)
  259. renderer.attribLocationTex = gl.GetUniformLocation(renderer.shaderHandle, gl.Str("Texture"+"\x00"))
  260. renderer.attribLocationProjMtx = gl.GetUniformLocation(renderer.shaderHandle, gl.Str("ProjMtx"+"\x00"))
  261. renderer.attribLocationPosition = gl.GetAttribLocation(renderer.shaderHandle, gl.Str("Position"+"\x00"))
  262. renderer.attribLocationUV = gl.GetAttribLocation(renderer.shaderHandle, gl.Str("UV"+"\x00"))
  263. renderer.attribLocationColor = gl.GetAttribLocation(renderer.shaderHandle, gl.Str("Color"+"\x00"))
  264. gl.GenBuffers(1, &renderer.vboHandle)
  265. gl.GenBuffers(1, &renderer.elementsHandle)
  266. // Restore modified GL state
  267. gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
  268. gl.BindBuffer(gl.ARRAY_BUFFER, uint32(lastArrayBuffer))
  269. gl.BindVertexArray(uint32(lastVertexArray))
  270. }
  271. func (renderer *OpenGL3) SetFontTexture(image *RGBA32Image) {
  272. // Delete old font texture
  273. if renderer.fontTexture != 0 {
  274. gl.DeleteTextures(1, &renderer.fontTexture)
  275. CurrentIO().Fonts().SetTextureID(0)
  276. renderer.fontTexture = 0
  277. }
  278. // Upload texture to graphics system
  279. var lastTexture int32
  280. gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
  281. gl.GenTextures(1, &renderer.fontTexture)
  282. gl.BindTexture(gl.TEXTURE_2D, renderer.fontTexture)
  283. gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
  284. gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
  285. gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0)
  286. gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(image.Width), int32(image.Height), 0, gl.RGBA, gl.UNSIGNED_BYTE, image.Pixels)
  287. // Store our identifier
  288. CurrentIO().Fonts().SetTextureID(TextureID(renderer.fontTexture))
  289. // Restore state
  290. gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
  291. }
  292. func (renderer *OpenGL3) invalidateDeviceObjects() {
  293. if renderer.vboHandle != 0 {
  294. gl.DeleteBuffers(1, &renderer.vboHandle)
  295. }
  296. renderer.vboHandle = 0
  297. if renderer.elementsHandle != 0 {
  298. gl.DeleteBuffers(1, &renderer.elementsHandle)
  299. }
  300. renderer.elementsHandle = 0
  301. if (renderer.shaderHandle != 0) && (renderer.vertHandle != 0) {
  302. gl.DetachShader(renderer.shaderHandle, renderer.vertHandle)
  303. }
  304. if renderer.vertHandle != 0 {
  305. gl.DeleteShader(renderer.vertHandle)
  306. }
  307. renderer.vertHandle = 0
  308. if (renderer.shaderHandle != 0) && (renderer.fragHandle != 0) {
  309. gl.DetachShader(renderer.shaderHandle, renderer.fragHandle)
  310. }
  311. if renderer.fragHandle != 0 {
  312. gl.DeleteShader(renderer.fragHandle)
  313. }
  314. renderer.fragHandle = 0
  315. if renderer.shaderHandle != 0 {
  316. gl.DeleteProgram(renderer.shaderHandle)
  317. }
  318. renderer.shaderHandle = 0
  319. if renderer.fontTexture != 0 {
  320. gl.DeleteTextures(1, &renderer.fontTexture)
  321. CurrentIO().Fonts().SetTextureID(0)
  322. renderer.fontTexture = 0
  323. }
  324. }
  325. // SetTextureMinFilter sets the minifying function for texture filtering.
  326. func (renderer *OpenGL3) SetTextureMinFilter(min uint) error {
  327. switch min {
  328. case textureFilterNearest:
  329. renderer.textureMinFilter = gl.NEAREST
  330. case textureFilterLinear:
  331. renderer.textureMinFilter = gl.LINEAR
  332. case textureFilterNearestMipmapNearest:
  333. renderer.textureMinFilter = gl.NEAREST_MIPMAP_NEAREST
  334. case textureFilterLinearMipmapNearest:
  335. renderer.textureMinFilter = gl.LINEAR_MIPMAP_NEAREST
  336. case textureFilterNearestMipmapLinear:
  337. renderer.textureMinFilter = gl.NEAREST_MIPMAP_LINEAR
  338. case textureFilterLinearMipmapLinear:
  339. renderer.textureMinFilter = gl.LINEAR_MIPMAP_LINEAR
  340. default:
  341. return errors.New("invalid minifying filter")
  342. }
  343. return nil
  344. }
  345. // SetTextureMagFilter sets the magnifying function for texture filtering.
  346. func (renderer *OpenGL3) SetTextureMagFilter(mag uint) error {
  347. switch mag {
  348. case textureFilterNearest:
  349. renderer.textureMagFilter = gl.NEAREST
  350. case textureFilterLinear:
  351. renderer.textureMagFilter = gl.LINEAR
  352. default:
  353. return fmt.Errorf("invalid magnifying filter")
  354. }
  355. return nil
  356. }
  357. // Load image and return the TextureID
  358. func (renderer *OpenGL3) LoadImage(image *image.RGBA) (TextureID, error) {
  359. texture, err := renderer.createImageTexture(image)
  360. if err != nil {
  361. return 0, err
  362. }
  363. return texture, nil
  364. }
  365. func (renderer *OpenGL3) ReleaseImage(textureId TextureID) {
  366. gl.BindTexture(gl.TEXTURE_2D, 0)
  367. handle := uint32(textureId)
  368. gl.DeleteTextures(1, &handle)
  369. }
  370. func (renderer *OpenGL3) createImageTexture(img *image.RGBA) (TextureID, error) {
  371. // Upload texture to graphics system
  372. var lastTexture int32
  373. var handle uint32
  374. gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
  375. gl.GenTextures(1, &handle)
  376. gl.BindTexture(gl.TEXTURE_2D, handle)
  377. gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) // minification filter
  378. gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) // magnification filter
  379. gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(img.Bounds().Dx()), int32(img.Bounds().Dy()), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(img.Pix))
  380. gl.GenerateMipmap(gl.TEXTURE_2D)
  381. // Restore state
  382. gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
  383. return TextureID(handle), nil
  384. }