path.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package gpu
  3. // GPU accelerated path drawing using the algorithms from
  4. // Pathfinder (https://github.com/servo/pathfinder).
  5. import (
  6. "encoding/binary"
  7. "image"
  8. "math"
  9. "unsafe"
  10. "gioui.org/gpu/internal/driver"
  11. "gioui.org/internal/byteslice"
  12. "gioui.org/internal/f32"
  13. "gioui.org/internal/f32color"
  14. "gioui.org/shader"
  15. "gioui.org/shader/gio"
  16. )
  17. type pather struct {
  18. ctx driver.Device
  19. viewport image.Point
  20. stenciler *stenciler
  21. coverer *coverer
  22. }
  23. type coverer struct {
  24. ctx driver.Device
  25. pipelines [2][3]*pipeline
  26. texUniforms *coverTexUniforms
  27. colUniforms *coverColUniforms
  28. linearGradientUniforms *coverLinearGradientUniforms
  29. }
  30. type coverTexUniforms struct {
  31. coverUniforms
  32. _ [12]byte // Padding to multiple of 16.
  33. }
  34. type coverColUniforms struct {
  35. coverUniforms
  36. _ [128 - unsafe.Sizeof(coverUniforms{}) - unsafe.Sizeof(colorUniforms{})]byte // Padding to 128 bytes.
  37. colorUniforms
  38. }
  39. type coverLinearGradientUniforms struct {
  40. coverUniforms
  41. _ [128 - unsafe.Sizeof(coverUniforms{}) - unsafe.Sizeof(gradientUniforms{})]byte // Padding to 128.
  42. gradientUniforms
  43. }
  44. type coverUniforms struct {
  45. transform [4]float32
  46. uvCoverTransform [4]float32
  47. uvTransformR1 [4]float32
  48. uvTransformR2 [4]float32
  49. fbo float32
  50. }
  51. type stenciler struct {
  52. ctx driver.Device
  53. pipeline struct {
  54. pipeline *pipeline
  55. uniforms *stencilUniforms
  56. }
  57. ipipeline struct {
  58. pipeline *pipeline
  59. uniforms *intersectUniforms
  60. }
  61. fbos fboSet
  62. intersections fboSet
  63. indexBuf driver.Buffer
  64. }
  65. type stencilUniforms struct {
  66. transform [4]float32
  67. pathOffset [2]float32
  68. _ [8]byte // Padding to multiple of 16.
  69. }
  70. type intersectUniforms struct {
  71. vert struct {
  72. uvTransform [4]float32
  73. subUVTransform [4]float32
  74. }
  75. }
  76. type fboSet struct {
  77. fbos []FBO
  78. }
  79. type FBO struct {
  80. size image.Point
  81. tex driver.Texture
  82. }
  83. type pathData struct {
  84. ncurves int
  85. data driver.Buffer
  86. }
  87. // vertex data suitable for passing to vertex programs.
  88. type vertex struct {
  89. // Corner encodes the corner: +0.5 for south, +.25 for east.
  90. Corner float32
  91. MaxY float32
  92. FromX, FromY float32
  93. CtrlX, CtrlY float32
  94. ToX, ToY float32
  95. }
  96. // encode needs to stay in-sync with the code in clip.go encodeQuadTo.
  97. func (v vertex) encode(d []byte, maxy uint32) {
  98. d = d[0:32]
  99. bo := binary.LittleEndian
  100. bo.PutUint32(d[0:4], math.Float32bits(v.Corner))
  101. bo.PutUint32(d[4:8], maxy)
  102. bo.PutUint32(d[8:12], math.Float32bits(v.FromX))
  103. bo.PutUint32(d[12:16], math.Float32bits(v.FromY))
  104. bo.PutUint32(d[16:20], math.Float32bits(v.CtrlX))
  105. bo.PutUint32(d[20:24], math.Float32bits(v.CtrlY))
  106. bo.PutUint32(d[24:28], math.Float32bits(v.ToX))
  107. bo.PutUint32(d[28:32], math.Float32bits(v.ToY))
  108. }
  109. const (
  110. // Number of path quads per draw batch.
  111. pathBatchSize = 10000
  112. // Size of a vertex as sent to gpu
  113. vertStride = 8 * 4
  114. )
  115. func newPather(ctx driver.Device) *pather {
  116. return &pather{
  117. ctx: ctx,
  118. stenciler: newStenciler(ctx),
  119. coverer: newCoverer(ctx),
  120. }
  121. }
  122. func newCoverer(ctx driver.Device) *coverer {
  123. c := &coverer{
  124. ctx: ctx,
  125. }
  126. c.colUniforms = new(coverColUniforms)
  127. c.texUniforms = new(coverTexUniforms)
  128. c.linearGradientUniforms = new(coverLinearGradientUniforms)
  129. pipelines, err := createColorPrograms(ctx, gio.Shader_cover_vert, gio.Shader_cover_frag,
  130. [3]interface{}{c.colUniforms, c.linearGradientUniforms, c.texUniforms},
  131. )
  132. if err != nil {
  133. panic(err)
  134. }
  135. c.pipelines = pipelines
  136. return c
  137. }
  138. func newStenciler(ctx driver.Device) *stenciler {
  139. // Allocate a suitably large index buffer for drawing paths.
  140. indices := make([]uint16, pathBatchSize*6)
  141. for i := 0; i < pathBatchSize; i++ {
  142. i := uint16(i)
  143. indices[i*6+0] = i*4 + 0
  144. indices[i*6+1] = i*4 + 1
  145. indices[i*6+2] = i*4 + 2
  146. indices[i*6+3] = i*4 + 2
  147. indices[i*6+4] = i*4 + 1
  148. indices[i*6+5] = i*4 + 3
  149. }
  150. indexBuf, err := ctx.NewImmutableBuffer(driver.BufferBindingIndices, byteslice.Slice(indices))
  151. if err != nil {
  152. panic(err)
  153. }
  154. progLayout := driver.VertexLayout{
  155. Inputs: []driver.InputDesc{
  156. {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))},
  157. {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))},
  158. {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))},
  159. {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))},
  160. {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))},
  161. },
  162. Stride: vertStride,
  163. }
  164. iprogLayout := driver.VertexLayout{
  165. Inputs: []driver.InputDesc{
  166. {Type: shader.DataTypeFloat, Size: 2, Offset: 0},
  167. {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
  168. },
  169. Stride: 4 * 4,
  170. }
  171. st := &stenciler{
  172. ctx: ctx,
  173. indexBuf: indexBuf,
  174. }
  175. vsh, fsh, err := newShaders(ctx, gio.Shader_stencil_vert, gio.Shader_stencil_frag)
  176. if err != nil {
  177. panic(err)
  178. }
  179. defer vsh.Release()
  180. defer fsh.Release()
  181. st.pipeline.uniforms = new(stencilUniforms)
  182. vertUniforms := newUniformBuffer(ctx, st.pipeline.uniforms)
  183. pipe, err := st.ctx.NewPipeline(driver.PipelineDesc{
  184. VertexShader: vsh,
  185. FragmentShader: fsh,
  186. VertexLayout: progLayout,
  187. BlendDesc: driver.BlendDesc{
  188. Enable: true,
  189. SrcFactor: driver.BlendFactorOne,
  190. DstFactor: driver.BlendFactorOne,
  191. },
  192. PixelFormat: driver.TextureFormatFloat,
  193. Topology: driver.TopologyTriangles,
  194. })
  195. st.pipeline.pipeline = &pipeline{pipe, vertUniforms}
  196. if err != nil {
  197. panic(err)
  198. }
  199. vsh, fsh, err = newShaders(ctx, gio.Shader_intersect_vert, gio.Shader_intersect_frag)
  200. if err != nil {
  201. panic(err)
  202. }
  203. defer vsh.Release()
  204. defer fsh.Release()
  205. st.ipipeline.uniforms = new(intersectUniforms)
  206. vertUniforms = newUniformBuffer(ctx, &st.ipipeline.uniforms.vert)
  207. ipipe, err := st.ctx.NewPipeline(driver.PipelineDesc{
  208. VertexShader: vsh,
  209. FragmentShader: fsh,
  210. VertexLayout: iprogLayout,
  211. BlendDesc: driver.BlendDesc{
  212. Enable: true,
  213. SrcFactor: driver.BlendFactorDstColor,
  214. DstFactor: driver.BlendFactorZero,
  215. },
  216. PixelFormat: driver.TextureFormatFloat,
  217. Topology: driver.TopologyTriangleStrip,
  218. })
  219. st.ipipeline.pipeline = &pipeline{ipipe, vertUniforms}
  220. if err != nil {
  221. panic(err)
  222. }
  223. return st
  224. }
  225. func (s *fboSet) resize(ctx driver.Device, format driver.TextureFormat, sizes []image.Point) {
  226. // Add fbos.
  227. for i := len(s.fbos); i < len(sizes); i++ {
  228. s.fbos = append(s.fbos, FBO{})
  229. }
  230. // Resize fbos.
  231. for i, sz := range sizes {
  232. f := &s.fbos[i]
  233. // Resizing or recreating FBOs can introduce rendering stalls.
  234. // Avoid if the space waste is not too high.
  235. resize := sz.X > f.size.X || sz.Y > f.size.Y
  236. waste := float32(sz.X*sz.Y) / float32(f.size.X*f.size.Y)
  237. resize = resize || waste > 1.2
  238. if resize {
  239. if f.tex != nil {
  240. f.tex.Release()
  241. }
  242. // Add 5% extra space in each dimension to minimize resizing.
  243. sz = sz.Mul(105).Div(100)
  244. max := ctx.Caps().MaxTextureSize
  245. if sz.Y > max {
  246. sz.Y = max
  247. }
  248. if sz.X > max {
  249. sz.X = max
  250. }
  251. tex, err := ctx.NewTexture(format, sz.X, sz.Y, driver.FilterNearest, driver.FilterNearest,
  252. driver.BufferBindingTexture|driver.BufferBindingFramebuffer)
  253. if err != nil {
  254. panic(err)
  255. }
  256. f.size = sz
  257. f.tex = tex
  258. }
  259. }
  260. // Delete extra fbos.
  261. s.delete(ctx, len(sizes))
  262. }
  263. func (s *fboSet) delete(ctx driver.Device, idx int) {
  264. for i := idx; i < len(s.fbos); i++ {
  265. f := s.fbos[i]
  266. f.tex.Release()
  267. }
  268. s.fbos = s.fbos[:idx]
  269. }
  270. func (s *stenciler) release() {
  271. s.fbos.delete(s.ctx, 0)
  272. s.intersections.delete(s.ctx, 0)
  273. s.pipeline.pipeline.Release()
  274. s.ipipeline.pipeline.Release()
  275. s.indexBuf.Release()
  276. }
  277. func (p *pather) release() {
  278. p.stenciler.release()
  279. p.coverer.release()
  280. }
  281. func (c *coverer) release() {
  282. for _, p := range c.pipelines {
  283. for _, p := range p {
  284. p.Release()
  285. }
  286. }
  287. }
  288. func buildPath(ctx driver.Device, p []byte) pathData {
  289. buf, err := ctx.NewImmutableBuffer(driver.BufferBindingVertices, p)
  290. if err != nil {
  291. panic(err)
  292. }
  293. return pathData{
  294. ncurves: len(p) / vertStride,
  295. data: buf,
  296. }
  297. }
  298. func (p pathData) release() {
  299. p.data.Release()
  300. }
  301. func (p *pather) begin(sizes []image.Point) {
  302. p.stenciler.begin(sizes)
  303. }
  304. func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) {
  305. p.stenciler.stencilPath(bounds, offset, uv, data)
  306. }
  307. func (s *stenciler) beginIntersect(sizes []image.Point) {
  308. // 8 bit coverage is enough, but OpenGL ES only supports single channel
  309. // floating point formats. Replace with GL_RGB+GL_UNSIGNED_BYTE if
  310. // no floating point support is available.
  311. s.intersections.resize(s.ctx, driver.TextureFormatFloat, sizes)
  312. }
  313. func (s *stenciler) cover(idx int) FBO {
  314. return s.fbos.fbos[idx]
  315. }
  316. func (s *stenciler) begin(sizes []image.Point) {
  317. s.fbos.resize(s.ctx, driver.TextureFormatFloat, sizes)
  318. }
  319. func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) {
  320. s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
  321. // Transform UI coordinates to OpenGL coordinates.
  322. texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
  323. scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
  324. orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
  325. s.pipeline.uniforms.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y}
  326. s.pipeline.uniforms.pathOffset = [2]float32{offset.X, offset.Y}
  327. s.pipeline.pipeline.UploadUniforms(s.ctx)
  328. // Draw in batches that fit in uint16 indices.
  329. start := 0
  330. nquads := data.ncurves / 4
  331. for start < nquads {
  332. batch := nquads - start
  333. if max := pathBatchSize; batch > max {
  334. batch = max
  335. }
  336. off := vertStride * start * 4
  337. s.ctx.BindVertexBuffer(data.data, off)
  338. s.ctx.DrawElements(0, batch*6)
  339. start += batch
  340. }
  341. }
  342. func (p *pather) cover(mat materialType, isFBO bool, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
  343. p.coverer.cover(mat, isFBO, col, col1, col2, scale, off, uvTrans, coverScale, coverOff)
  344. }
  345. func (c *coverer) cover(mat materialType, isFBO bool, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
  346. var uniforms *coverUniforms
  347. switch mat {
  348. case materialColor:
  349. c.colUniforms.color = col
  350. uniforms = &c.colUniforms.coverUniforms
  351. case materialLinearGradient:
  352. c.linearGradientUniforms.color1 = col1
  353. c.linearGradientUniforms.color2 = col2
  354. t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
  355. c.linearGradientUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
  356. c.linearGradientUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
  357. uniforms = &c.linearGradientUniforms.coverUniforms
  358. case materialTexture:
  359. t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
  360. c.texUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
  361. c.texUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
  362. uniforms = &c.texUniforms.coverUniforms
  363. }
  364. uniforms.fbo = 0
  365. if isFBO {
  366. uniforms.fbo = 1
  367. }
  368. uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
  369. uniforms.uvCoverTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
  370. fboIdx := 0
  371. if isFBO {
  372. fboIdx = 1
  373. }
  374. c.pipelines[fboIdx][mat].UploadUniforms(c.ctx)
  375. c.ctx.DrawArrays(0, 4)
  376. }
  377. func init() {
  378. // Check that struct vertex has the expected size and
  379. // that it contains no padding.
  380. if unsafe.Sizeof(*(*vertex)(nil)) != vertStride {
  381. panic("unexpected struct size")
  382. }
  383. }