zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

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 }