shader.go (3666B)
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 15 //go:build darwin 16 // +build darwin 17 18 package metal 19 20 import ( 21 "fmt" 22 23 "github.com/hajimehoshi/ebiten/v2/internal/driver" 24 "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl" 25 "github.com/hajimehoshi/ebiten/v2/internal/shaderir" 26 "github.com/hajimehoshi/ebiten/v2/internal/shaderir/metal" 27 ) 28 29 type shaderRpsKey struct { 30 compositeMode driver.CompositeMode 31 stencilMode stencilMode 32 } 33 34 type Shader struct { 35 id driver.ShaderID 36 37 ir *shaderir.Program 38 fs mtl.Function 39 vs mtl.Function 40 rpss map[shaderRpsKey]mtl.RenderPipelineState 41 } 42 43 func newShader(device mtl.Device, id driver.ShaderID, program *shaderir.Program) (*Shader, error) { 44 s := &Shader{ 45 id: id, 46 ir: program, 47 rpss: map[shaderRpsKey]mtl.RenderPipelineState{}, 48 } 49 if err := s.init(device); err != nil { 50 return nil, err 51 } 52 return s, nil 53 } 54 55 func (s *Shader) ID() driver.ShaderID { 56 return s.id 57 } 58 59 func (s *Shader) Dispose() { 60 for _, rps := range s.rpss { 61 rps.Release() 62 } 63 s.vs.Release() 64 s.fs.Release() 65 } 66 67 func (s *Shader) init(device mtl.Device) error { 68 const ( 69 v = "Vertex" 70 f = "Fragment" 71 ) 72 73 src := metal.Compile(s.ir, v, f) 74 lib, err := device.MakeLibrary(src, mtl.CompileOptions{}) 75 if err != nil { 76 return fmt.Errorf("metal: device.MakeLibrary failed: %v, source: %s", err, src) 77 } 78 vs, err := lib.MakeFunction(v) 79 if err != nil { 80 return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %v, source: %s", err, src) 81 } 82 fs, err := lib.MakeFunction(f) 83 if err != nil { 84 return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %v, source: %s", err, src) 85 } 86 s.fs = fs 87 s.vs = vs 88 return nil 89 } 90 91 func (s *Shader) RenderPipelineState(device mtl.Device, compositeMode driver.CompositeMode, stencilMode stencilMode) (mtl.RenderPipelineState, error) { 92 if rps, ok := s.rpss[shaderRpsKey{ 93 compositeMode: compositeMode, 94 stencilMode: stencilMode, 95 }]; ok { 96 return rps, nil 97 } 98 99 rpld := mtl.RenderPipelineDescriptor{ 100 VertexFunction: s.vs, 101 FragmentFunction: s.fs, 102 } 103 if stencilMode != noStencil { 104 rpld.StencilAttachmentPixelFormat = mtl.PixelFormatStencil8 105 } 106 107 // TODO: For the precise pixel format, whether the render target is the screen or not must be considered. 108 rpld.ColorAttachments[0].PixelFormat = mtl.PixelFormatRGBA8UNorm 109 rpld.ColorAttachments[0].BlendingEnabled = true 110 111 src, dst := compositeMode.Operations() 112 rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst) 113 rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst) 114 rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src) 115 rpld.ColorAttachments[0].SourceRGBBlendFactor = operationToBlendFactor(src) 116 if stencilMode == prepareStencil { 117 rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone 118 } else { 119 rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll 120 } 121 122 rps, err := device.MakeRenderPipelineState(rpld) 123 if err != nil { 124 return mtl.RenderPipelineState{}, err 125 } 126 127 s.rpss[shaderRpsKey{ 128 compositeMode: compositeMode, 129 stencilMode: stencilMode, 130 }] = rps 131 return rps, nil 132 }