shader.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // Copyright 2020 The Ebiten Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package ebiten
  15. import (
  16. "fmt"
  17. "sync"
  18. "sync/atomic"
  19. "github.com/hajimehoshi/ebiten/v2/internal/builtinshader"
  20. "github.com/hajimehoshi/ebiten/v2/internal/graphics"
  21. "github.com/hajimehoshi/ebiten/v2/internal/shaderir"
  22. "github.com/hajimehoshi/ebiten/v2/internal/ui"
  23. )
  24. // Shader represents a compiled shader program.
  25. //
  26. // For the details about the shader, see https://ebitengine.org/en/documents/shader.html.
  27. type Shader struct {
  28. shader *ui.Shader
  29. unit shaderir.Unit
  30. }
  31. // NewShader compiles a shader program in the shading language Kage, and returns the result.
  32. //
  33. // If the compilation fails, NewShader returns an error.
  34. //
  35. // For the details about the shader, see https://ebitengine.org/en/documents/shader.html.
  36. func NewShader(src []byte) (*Shader, error) {
  37. return newShader(src, "")
  38. }
  39. func newShader(src []byte, name string) (*Shader, error) {
  40. ir, err := graphics.CompileShader(src)
  41. if err != nil {
  42. return nil, err
  43. }
  44. return &Shader{
  45. shader: ui.NewShader(ir, name),
  46. unit: ir.Unit,
  47. }, nil
  48. }
  49. // Dispose disposes the shader program.
  50. // After disposing, the shader is no longer available.
  51. //
  52. // Deprecated: as of v2.7. Use Deallocate instead.
  53. func (s *Shader) Dispose() {
  54. s.shader.Deallocate()
  55. s.shader = nil
  56. }
  57. func (s *Shader) isDisposed() bool {
  58. return s.shader == nil
  59. }
  60. // Deallocate deallocates the internal state of the shader.
  61. // Even after Deallocate is called, the shader is still available.
  62. // In this case, the shader's internal state is allocated again.
  63. //
  64. // Usually, you don't have to call Deallocate since the internal state is automatically released by GC.
  65. // However, if you are sure that the shader is no longer used but not sure how this shader object is referred,
  66. // you can call Deallocate to make sure that the internal state is deallocated.
  67. //
  68. // If the shader is disposed, Deallocate does nothing.
  69. func (s *Shader) Deallocate() {
  70. if s.shader == nil {
  71. return
  72. }
  73. s.shader.Deallocate()
  74. }
  75. func (s *Shader) appendUniforms(dst []uint32, uniforms map[string]any) []uint32 {
  76. return s.shader.AppendUniforms(dst, uniforms)
  77. }
  78. var (
  79. builtinShadersForRead atomic.Pointer[[builtinshader.FilterCount][builtinshader.AddressCount][2]*Shader]
  80. builtinShadersM sync.Mutex
  81. )
  82. func builtinShader(filter builtinshader.Filter, address builtinshader.Address, useColorM bool) *Shader {
  83. var c int
  84. if useColorM {
  85. c = 1
  86. }
  87. if read := builtinShadersForRead.Load(); read != nil {
  88. if s := (*read)[filter][address][c]; s != nil {
  89. return s
  90. }
  91. }
  92. builtinShadersM.Lock()
  93. defer builtinShadersM.Unlock()
  94. // Double check in case another goroutine already created a shader.
  95. if read := builtinShadersForRead.Load(); read != nil {
  96. if s := (*read)[filter][address][c]; s != nil {
  97. return s
  98. }
  99. }
  100. var shader *Shader
  101. if address == builtinshader.AddressUnsafe && !useColorM {
  102. switch filter {
  103. case builtinshader.FilterNearest:
  104. shader = &Shader{shader: ui.NearestFilterShader}
  105. case builtinshader.FilterLinear:
  106. shader = &Shader{shader: ui.LinearFilterShader}
  107. }
  108. } else {
  109. src := builtinshader.ShaderSource(filter, address, useColorM)
  110. var name string
  111. switch filter {
  112. case builtinshader.FilterNearest:
  113. name = "nearest"
  114. case builtinshader.FilterLinear:
  115. name = "linear"
  116. }
  117. switch address {
  118. case builtinshader.AddressClampToZero:
  119. name += "-clamptozero"
  120. case builtinshader.AddressRepeat:
  121. name += "-repeat"
  122. }
  123. if useColorM {
  124. name += "-colorm"
  125. }
  126. s, err := newShader(src, name)
  127. if err != nil {
  128. panic(fmt.Sprintf("ebiten: NewShader for a built-in shader failed: %v", err))
  129. }
  130. shader = s
  131. }
  132. var shaders [builtinshader.FilterCount][builtinshader.AddressCount][2]*Shader
  133. if ptr := builtinShadersForRead.Load(); ptr != nil {
  134. shaders = *ptr
  135. }
  136. shaders[filter][address][c] = shader
  137. builtinShadersForRead.Store(&shaders)
  138. return shader
  139. }