aboutsummaryrefslogtreecommitdiff
path: root/vendor/gioui.org/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gioui.org/gpu')
-rw-r--r--vendor/gioui.org/gpu/api.go40
-rw-r--r--vendor/gioui.org/gpu/backend/backend.go204
-rw-r--r--vendor/gioui.org/gpu/caches.go17
-rw-r--r--vendor/gioui.org/gpu/clip.go24
-rw-r--r--vendor/gioui.org/gpu/compute.go2219
-rw-r--r--vendor/gioui.org/gpu/cpu.go129
-rw-r--r--vendor/gioui.org/gpu/gen.go5
-rw-r--r--vendor/gioui.org/gpu/gl/backend.go835
-rw-r--r--vendor/gioui.org/gpu/gl/gl.go161
-rw-r--r--vendor/gioui.org/gpu/gl/types.go27
-rw-r--r--vendor/gioui.org/gpu/gl/types_js.go29
-rw-r--r--vendor/gioui.org/gpu/gl/util.go75
-rw-r--r--vendor/gioui.org/gpu/gpu.go1131
-rw-r--r--vendor/gioui.org/gpu/internal/d3d11/d3d11.go5
-rw-r--r--vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go859
-rw-r--r--vendor/gioui.org/gpu/internal/driver/api.go127
-rw-r--r--vendor/gioui.org/gpu/internal/driver/driver.go237
-rw-r--r--vendor/gioui.org/gpu/internal/metal/metal.go5
-rw-r--r--vendor/gioui.org/gpu/internal/metal/metal_darwin.go1141
-rw-r--r--vendor/gioui.org/gpu/internal/opengl/opengl.go1357
-rw-r--r--vendor/gioui.org/gpu/internal/opengl/srgb.go176
-rw-r--r--vendor/gioui.org/gpu/internal/vulkan/vulkan.go1121
-rw-r--r--vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go5
-rw-r--r--vendor/gioui.org/gpu/pack.go86
-rw-r--r--vendor/gioui.org/gpu/path.go254
-rw-r--r--vendor/gioui.org/gpu/shaders.go657
-rw-r--r--vendor/gioui.org/gpu/timer.go12
27 files changed, 8327 insertions, 2611 deletions
diff --git a/vendor/gioui.org/gpu/api.go b/vendor/gioui.org/gpu/api.go
new file mode 100644
index 0000000..d347e5a
--- /dev/null
+++ b/vendor/gioui.org/gpu/api.go
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gpu
+
+import "gioui.org/gpu/internal/driver"
+
+// An API carries the necessary GPU API specific resources to create a Device.
+// There is an API type for each supported GPU API such as OpenGL and Direct3D.
+type API = driver.API
+
+// A RenderTarget denotes the destination framebuffer for a frame.
+type RenderTarget = driver.RenderTarget
+
+// OpenGLRenderTarget is a render target suitable for the OpenGL backend.
+type OpenGLRenderTarget = driver.OpenGLRenderTarget
+
+// Direct3D11RenderTarget is a render target suitable for the Direct3D 11 backend.
+type Direct3D11RenderTarget = driver.Direct3D11RenderTarget
+
+// MetalRenderTarget is a render target suitable for the Metal backend.
+type MetalRenderTarget = driver.MetalRenderTarget
+
+// VulkanRenderTarget is a render target suitable for the Vulkan backend.
+type VulkanRenderTarget = driver.VulkanRenderTarget
+
+// OpenGL denotes the OpenGL or OpenGL ES API.
+type OpenGL = driver.OpenGL
+
+// Direct3D11 denotes the Direct3D API.
+type Direct3D11 = driver.Direct3D11
+
+// Metal denotes the Apple Metal API.
+type Metal = driver.Metal
+
+// Vulkan denotes the Vulkan API.
+type Vulkan = driver.Vulkan
+
+// ErrDeviceLost is returned from GPU operations when the underlying GPU device
+// is lost and should be recreated.
+var ErrDeviceLost = driver.ErrDeviceLost
diff --git a/vendor/gioui.org/gpu/backend/backend.go b/vendor/gioui.org/gpu/backend/backend.go
deleted file mode 100644
index 871eea4..0000000
--- a/vendor/gioui.org/gpu/backend/backend.go
+++ /dev/null
@@ -1,204 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package backend
-
-import (
- "image"
- "time"
-)
-
-// Device represents the abstraction of underlying GPU
-// APIs such as OpenGL, Direct3D useful for rendering Gio
-// operations.
-type Device interface {
- BeginFrame()
- EndFrame()
- Caps() Caps
- NewTimer() Timer
- // IsContinuousTime reports whether all timer measurements
- // are valid at the point of call.
- IsTimeContinuous() bool
- NewTexture(format TextureFormat, width, height int, minFilter, magFilter TextureFilter, bindings BufferBinding) (Texture, error)
- CurrentFramebuffer() Framebuffer
- NewFramebuffer(tex Texture, depthBits int) (Framebuffer, error)
- NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error)
- NewBuffer(typ BufferBinding, size int) (Buffer, error)
- NewProgram(vertexShader, fragmentShader ShaderSources) (Program, error)
- NewInputLayout(vertexShader ShaderSources, layout []InputDesc) (InputLayout, error)
-
- DepthFunc(f DepthFunc)
- ClearDepth(d float32)
- Clear(r, g, b, a float32)
- Viewport(x, y, width, height int)
- DrawArrays(mode DrawMode, off, count int)
- DrawElements(mode DrawMode, off, count int)
- SetBlend(enable bool)
- SetDepthTest(enable bool)
- DepthMask(mask bool)
- BlendFunc(sfactor, dfactor BlendFactor)
-
- BindInputLayout(i InputLayout)
- BindProgram(p Program)
- BindFramebuffer(f Framebuffer)
- BindTexture(unit int, t Texture)
- BindVertexBuffer(b Buffer, stride, offset int)
- BindIndexBuffer(b Buffer)
-}
-
-type ShaderSources struct {
- GLSL100ES string
- GLSL300ES string
- GLSL130 string
- GLSL150 string
- HLSL []byte
- Uniforms UniformsReflection
- Inputs []InputLocation
- Textures []TextureBinding
-}
-
-type UniformsReflection struct {
- Blocks []UniformBlock
- Locations []UniformLocation
- Size int
-}
-
-type TextureBinding struct {
- Name string
- Binding int
-}
-
-type UniformBlock struct {
- Name string
- Binding int
-}
-
-type UniformLocation struct {
- Name string
- Type DataType
- Size int
- Offset int
-}
-
-type InputLocation struct {
- // For GLSL.
- Name string
- Location int
- // For HLSL.
- Semantic string
- SemanticIndex int
-
- Type DataType
- Size int
-}
-
-// InputDesc describes a vertex attribute as laid out in a Buffer.
-type InputDesc struct {
- Type DataType
- Size int
-
- Offset int
-}
-
-// InputLayout is the backend specific representation of the mapping
-// between Buffers and shader attributes.
-type InputLayout interface {
- Release()
-}
-
-type BlendFactor uint8
-
-type DrawMode uint8
-
-type TextureFilter uint8
-type TextureFormat uint8
-
-type BufferBinding uint8
-
-type DataType uint8
-
-type DepthFunc uint8
-
-type Features uint
-
-type Caps struct {
- Features Features
- MaxTextureSize int
-}
-
-type Program interface {
- Release()
- SetVertexUniforms(buf Buffer)
- SetFragmentUniforms(buf Buffer)
-}
-
-type Buffer interface {
- Release()
- Upload(data []byte)
-}
-
-type Framebuffer interface {
- Invalidate()
- Release()
- ReadPixels(src image.Rectangle, pixels []byte) error
-}
-
-type Timer interface {
- Begin()
- End()
- Duration() (time.Duration, bool)
- Release()
-}
-
-type Texture interface {
- Upload(img *image.RGBA)
- Release()
-}
-
-const (
- DepthFuncGreater DepthFunc = iota
- DepthFuncGreaterEqual
-)
-
-const (
- DataTypeFloat DataType = iota
- DataTypeInt
- DataTypeShort
-)
-
-const (
- BufferBindingIndices BufferBinding = 1 << iota
- BufferBindingVertices
- BufferBindingUniforms
- BufferBindingTexture
- BufferBindingFramebuffer
-)
-
-const (
- TextureFormatSRGB TextureFormat = iota
- TextureFormatFloat
-)
-
-const (
- FilterNearest TextureFilter = iota
- FilterLinear
-)
-
-const (
- FeatureTimers Features = iota
-)
-
-const (
- DrawModeTriangleStrip DrawMode = iota
- DrawModeTriangles
-)
-
-const (
- BlendFactorOne BlendFactor = iota
- BlendFactorOneMinusSrcAlpha
- BlendFactorZero
- BlendFactorDstColor
-)
-
-func (f Features) Has(feats Features) bool {
- return f&feats == feats
-}
diff --git a/vendor/gioui.org/gpu/caches.go b/vendor/gioui.org/gpu/caches.go
index cefba83..c6a71bc 100644
--- a/vendor/gioui.org/gpu/caches.go
+++ b/vendor/gioui.org/gpu/caches.go
@@ -6,7 +6,6 @@ import (
"fmt"
"gioui.org/f32"
- "gioui.org/internal/ops"
)
type resourceCache struct {
@@ -19,17 +18,18 @@ type resourceCache struct {
// since benchmarking showed them as a bottleneck.
type opCache struct {
// store the index + 1 in cache this key is stored in
- index map[ops.Key]int
+ index map[opKey]int
// list of indexes in cache that are free and can be used
freelist []int
cache []opCacheValue
}
type opCacheValue struct {
- data pathData
+ data pathData
+
bounds f32.Rectangle
// the fields below are handled by opCache
- key ops.Key
+ key opKey
keep bool
}
@@ -70,7 +70,8 @@ func (r *resourceCache) frame() {
}
func (r *resourceCache) release() {
- for _, v := range r.newRes {
+ r.frame()
+ for _, v := range r.res {
v.release()
}
r.newRes = nil
@@ -79,13 +80,13 @@ func (r *resourceCache) release() {
func newOpCache() *opCache {
return &opCache{
- index: make(map[ops.Key]int),
+ index: make(map[opKey]int),
freelist: make([]int, 0),
cache: make([]opCacheValue, 0),
}
}
-func (r *opCache) get(key ops.Key) (o opCacheValue, exist bool) {
+func (r *opCache) get(key opKey) (o opCacheValue, exist bool) {
v := r.index[key]
if v == 0 {
return
@@ -94,7 +95,7 @@ func (r *opCache) get(key ops.Key) (o opCacheValue, exist bool) {
return r.cache[v-1], true
}
-func (r *opCache) put(key ops.Key, val opCacheValue) {
+func (r *opCache) put(key opKey, val opCacheValue) {
v := r.index[key]
val.keep = true
val.key = key
diff --git a/vendor/gioui.org/gpu/clip.go b/vendor/gioui.org/gpu/clip.go
index 51022a0..8ae25b5 100644
--- a/vendor/gioui.org/gpu/clip.go
+++ b/vendor/gioui.org/gpu/clip.go
@@ -2,11 +2,10 @@ package gpu
import (
"gioui.org/f32"
- "gioui.org/internal/ops"
+ "gioui.org/internal/stroke"
)
type quadSplitter struct {
- verts []byte
bounds f32.Rectangle
contour uint32
d *drawOps
@@ -48,7 +47,7 @@ func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) {
encodeQuadTo(data, qs.contour, from, ctrl, to)
}
-func (qs *quadSplitter) splitAndEncode(quad ops.Quad) {
+func (qs *quadSplitter) splitAndEncode(quad stroke.QuadSegment) {
cbnd := f32.Rectangle{
Min: quad.From,
Max: quad.To,
@@ -94,5 +93,22 @@ func (qs *quadSplitter) splitAndEncode(quad ops.Quad) {
}
}
- qs.bounds = qs.bounds.Union(cbnd)
+ qs.bounds = unionRect(qs.bounds, cbnd)
+}
+
+// Union is like f32.Rectangle.Union but ignores empty rectangles.
+func unionRect(r, s f32.Rectangle) f32.Rectangle {
+ if r.Min.X > s.Min.X {
+ r.Min.X = s.Min.X
+ }
+ if r.Min.Y > s.Min.Y {
+ r.Min.Y = s.Min.Y
+ }
+ if r.Max.X < s.Max.X {
+ r.Max.X = s.Max.X
+ }
+ if r.Max.Y < s.Max.Y {
+ r.Max.Y = s.Max.Y
+ }
+ return r
}
diff --git a/vendor/gioui.org/gpu/compute.go b/vendor/gioui.org/gpu/compute.go
new file mode 100644
index 0000000..625658c
--- /dev/null
+++ b/vendor/gioui.org/gpu/compute.go
@@ -0,0 +1,2219 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gpu
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "hash/maphash"
+ "image"
+ "image/color"
+ "image/draw"
+ "image/png"
+ "io/ioutil"
+ "math"
+ "math/bits"
+ "runtime"
+ "sort"
+ "time"
+ "unsafe"
+
+ "gioui.org/cpu"
+ "gioui.org/f32"
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/byteslice"
+ "gioui.org/internal/f32color"
+ "gioui.org/internal/ops"
+ "gioui.org/internal/scene"
+ "gioui.org/layout"
+ "gioui.org/op"
+ "gioui.org/shader"
+ "gioui.org/shader/gio"
+ "gioui.org/shader/piet"
+)
+
+type compute struct {
+ ctx driver.Device
+
+ collector collector
+ enc encoder
+ texOps []textureOp
+ viewport image.Point
+ maxTextureDim int
+ srgb bool
+ atlases []*textureAtlas
+ frameCount uint
+ moves []atlasMove
+
+ programs struct {
+ elements computeProgram
+ tileAlloc computeProgram
+ pathCoarse computeProgram
+ backdrop computeProgram
+ binning computeProgram
+ coarse computeProgram
+ kernel4 computeProgram
+ }
+ buffers struct {
+ config sizedBuffer
+ scene sizedBuffer
+ state sizedBuffer
+ memory sizedBuffer
+ }
+ output struct {
+ blitPipeline driver.Pipeline
+
+ buffer sizedBuffer
+
+ uniforms *copyUniforms
+ uniBuf driver.Buffer
+
+ layerVertices []layerVertex
+ descriptors *piet.Kernel4DescriptorSetLayout
+
+ nullMaterials driver.Texture
+ }
+ // imgAllocs maps imageOpData.handles to allocs.
+ imgAllocs map[interface{}]*atlasAlloc
+ // materials contains the pre-processed materials (transformed images for
+ // now, gradients etc. later) packed in a texture atlas. The atlas is used
+ // as source in kernel4.
+ materials struct {
+ // allocs maps texture ops the their atlases and FillImage offsets.
+ allocs map[textureKey]materialAlloc
+
+ pipeline driver.Pipeline
+ buffer sizedBuffer
+ quads []materialVertex
+ uniforms struct {
+ u *materialUniforms
+ buf driver.Buffer
+ }
+ }
+ timers struct {
+ profile string
+ t *timers
+ compact *timer
+ render *timer
+ blit *timer
+ }
+
+ // CPU fallback fields.
+ useCPU bool
+ dispatcher *dispatcher
+
+ // The following fields hold scratch space to avoid garbage.
+ zeroSlice []byte
+ memHeader *memoryHeader
+ conf *config
+}
+
+type materialAlloc struct {
+ alloc *atlasAlloc
+ offset image.Point
+}
+
+type layer struct {
+ rect image.Rectangle
+ alloc *atlasAlloc
+ ops []paintOp
+ materials *textureAtlas
+}
+
+type allocQuery struct {
+ atlas *textureAtlas
+ size image.Point
+ empty bool
+ format driver.TextureFormat
+ bindings driver.BufferBinding
+ nocompact bool
+}
+
+type atlasAlloc struct {
+ atlas *textureAtlas
+ rect image.Rectangle
+ cpu bool
+ dead bool
+ frameCount uint
+}
+
+type atlasMove struct {
+ src *textureAtlas
+ dstPos image.Point
+ srcRect image.Rectangle
+ cpu bool
+}
+
+type textureAtlas struct {
+ image driver.Texture
+ format driver.TextureFormat
+ bindings driver.BufferBinding
+ hasCPU bool
+ cpuImage cpu.ImageDescriptor
+ size image.Point
+ allocs []*atlasAlloc
+ packer packer
+ realized bool
+ lastFrame uint
+ compact bool
+}
+
+type copyUniforms struct {
+ scale [2]float32
+ pos [2]float32
+ uvScale [2]float32
+ _ [8]byte // Pad to 16 bytes.
+}
+
+type materialUniforms struct {
+ scale [2]float32
+ pos [2]float32
+ emulatesRGB float32
+ _ [12]byte // Pad to 16 bytes
+}
+
+type collector struct {
+ hasher maphash.Hash
+ profile bool
+ reader ops.Reader
+ states []f32.Affine2D
+ clear bool
+ clearColor f32color.RGBA
+ clipStates []clipState
+ order []hashIndex
+ transStack []transEntry
+ prevFrame opsCollector
+ frame opsCollector
+}
+
+type transEntry struct {
+ t f32.Affine2D
+ relTrans f32.Affine2D
+}
+
+type hashIndex struct {
+ index int
+ hash uint64
+}
+
+type opsCollector struct {
+ paths []byte
+ clipCmds []clipCmd
+ ops []paintOp
+ layers []layer
+}
+
+type paintOp struct {
+ clipStack []clipCmd
+ offset image.Point
+ state paintKey
+ intersect f32.Rectangle
+ hash uint64
+ layer int
+ texOpIdx int
+}
+
+// clipCmd describes a clipping command ready to be used for the compute
+// pipeline.
+type clipCmd struct {
+ // union of the bounds of the operations that are clipped.
+ union f32.Rectangle
+ state clipKey
+ path []byte
+ pathKey ops.Key
+ absBounds f32.Rectangle
+}
+
+type encoderState struct {
+ relTrans f32.Affine2D
+ clip *clipState
+
+ paintKey
+}
+
+// clipKey completely describes a clip operation (along with its path) and is appropriate
+// for hashing and equality checks.
+type clipKey struct {
+ bounds f32.Rectangle
+ strokeWidth float32
+ relTrans f32.Affine2D
+ pathHash uint64
+}
+
+// paintKey completely defines a paint operation. It is suitable for hashing and
+// equality checks.
+type paintKey struct {
+ t f32.Affine2D
+ matType materialType
+ // Current paint.ImageOp
+ image imageOpData
+ // Current paint.ColorOp, if any.
+ color color.NRGBA
+
+ // Current paint.LinearGradientOp.
+ stop1 f32.Point
+ stop2 f32.Point
+ color1 color.NRGBA
+ color2 color.NRGBA
+}
+
+type clipState struct {
+ absBounds f32.Rectangle
+ parent *clipState
+ path []byte
+ pathKey ops.Key
+ intersect f32.Rectangle
+ push bool
+
+ clipKey
+}
+
+type layerVertex struct {
+ posX, posY float32
+ u, v float32
+}
+
+// materialVertex describes a vertex of a quad used to render a transformed
+// material.
+type materialVertex struct {
+ posX, posY float32
+ u, v float32
+}
+
+// textureKey identifies textureOp.
+type textureKey struct {
+ handle interface{}
+ transform f32.Affine2D
+ bounds image.Rectangle
+}
+
+// textureOp represents an paintOp that requires texture space.
+type textureOp struct {
+ img imageOpData
+ key textureKey
+ // offset is the integer offset separated from key.transform to increase cache hit rate.
+ off image.Point
+ // matAlloc is the atlas placement for material.
+ matAlloc materialAlloc
+ // imgAlloc is the atlas placement for the source image
+ imgAlloc *atlasAlloc
+}
+
+type encoder struct {
+ scene []scene.Command
+ npath int
+ npathseg int
+ ntrans int
+}
+
+type encodeState struct {
+ trans f32.Affine2D
+ clip f32.Rectangle
+}
+
+// sizedBuffer holds a GPU buffer, or its equivalent CPU memory.
+type sizedBuffer struct {
+ size int
+ buffer driver.Buffer
+ // cpuBuf is initialized when useCPU is true.
+ cpuBuf cpu.BufferDescriptor
+}
+
+// computeProgram holds a compute program, or its equivalent CPU implementation.
+type computeProgram struct {
+ prog driver.Program
+
+ // CPU fields.
+ progInfo *cpu.ProgramInfo
+ descriptors unsafe.Pointer
+ buffers []*cpu.BufferDescriptor
+}
+
+// config matches Config in setup.h
+type config struct {
+ n_elements uint32 // paths
+ n_pathseg uint32
+ width_in_tiles uint32
+ height_in_tiles uint32
+ tile_alloc memAlloc
+ bin_alloc memAlloc
+ ptcl_alloc memAlloc
+ pathseg_alloc memAlloc
+ anno_alloc memAlloc
+ trans_alloc memAlloc
+}
+
+// memAlloc matches Alloc in mem.h
+type memAlloc struct {
+ offset uint32
+ //size uint32
+}
+
+// memoryHeader matches the header of Memory in mem.h.
+type memoryHeader struct {
+ mem_offset uint32
+ mem_error uint32
+}
+
+// rect is a oriented rectangle.
+type rectangle [4]f32.Point
+
+const (
+ layersBindings = driver.BufferBindingShaderStorageWrite | driver.BufferBindingTexture
+ materialsBindings = driver.BufferBindingFramebuffer | driver.BufferBindingShaderStorageRead
+ // Materials and layers can share texture storage if their bindings match.
+ combinedBindings = layersBindings | materialsBindings
+)
+
+// GPU structure sizes and constants.
+const (
+ tileWidthPx = 32
+ tileHeightPx = 32
+ ptclInitialAlloc = 1024
+ kernel4OutputUnit = 2
+ kernel4AtlasUnit = 3
+
+ pathSize = 12
+ binSize = 8
+ pathsegSize = 52
+ annoSize = 32
+ transSize = 24
+ stateSize = 60
+ stateStride = 4 + 2*stateSize
+)
+
+// mem.h constants.
+const (
+ memNoError = 0 // NO_ERROR
+ memMallocFailed = 1 // ERR_MALLOC_FAILED
+)
+
+func newCompute(ctx driver.Device) (*compute, error) {
+ caps := ctx.Caps()
+ maxDim := caps.MaxTextureSize
+ // Large atlas textures cause artifacts due to precision loss in
+ // shaders.
+ if cap := 8192; maxDim > cap {
+ maxDim = cap
+ }
+ // The compute programs can only span 128x64 tiles. Limit to 64 for now, and leave the
+ // complexity of a rectangular limit for later.
+ if computeCap := 4096; maxDim > computeCap {
+ maxDim = computeCap
+ }
+ g := &compute{
+ ctx: ctx,
+ maxTextureDim: maxDim,
+ srgb: caps.Features.Has(driver.FeatureSRGB),
+ conf: new(config),
+ memHeader: new(memoryHeader),
+ }
+ null, err := ctx.NewTexture(driver.TextureFormatRGBA8, 1, 1, driver.FilterNearest, driver.FilterNearest, driver.BufferBindingShaderStorageRead)
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.output.nullMaterials = null
+ shaders := []struct {
+ prog *computeProgram
+ src shader.Sources
+ info *cpu.ProgramInfo
+ }{
+ {&g.programs.elements, piet.Shader_elements_comp, piet.ElementsProgramInfo},
+ {&g.programs.tileAlloc, piet.Shader_tile_alloc_comp, piet.Tile_allocProgramInfo},
+ {&g.programs.pathCoarse, piet.Shader_path_coarse_comp, piet.Path_coarseProgramInfo},
+ {&g.programs.backdrop, piet.Shader_backdrop_comp, piet.BackdropProgramInfo},
+ {&g.programs.binning, piet.Shader_binning_comp, piet.BinningProgramInfo},
+ {&g.programs.coarse, piet.Shader_coarse_comp, piet.CoarseProgramInfo},
+ {&g.programs.kernel4, piet.Shader_kernel4_comp, piet.Kernel4ProgramInfo},
+ }
+ if !caps.Features.Has(driver.FeatureCompute) {
+ if !cpu.Supported {
+ return nil, errors.New("gpu: missing support for compute programs")
+ }
+ g.useCPU = true
+ }
+ if g.useCPU {
+ g.dispatcher = newDispatcher(runtime.NumCPU())
+ }
+
+ copyVert, copyFrag, err := newShaders(ctx, gio.Shader_copy_vert, gio.Shader_copy_frag)
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ defer copyVert.Release()
+ defer copyFrag.Release()
+ pipe, err := ctx.NewPipeline(driver.PipelineDesc{
+ VertexShader: copyVert,
+ FragmentShader: copyFrag,
+ VertexLayout: driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 0},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
+ },
+ Stride: int(unsafe.Sizeof(g.output.layerVertices[0])),
+ },
+ PixelFormat: driver.TextureFormatOutput,
+ BlendDesc: driver.BlendDesc{
+ Enable: true,
+ SrcFactor: driver.BlendFactorOne,
+ DstFactor: driver.BlendFactorOneMinusSrcAlpha,
+ },
+ Topology: driver.TopologyTriangles,
+ })
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.output.blitPipeline = pipe
+ g.output.uniforms = new(copyUniforms)
+
+ buf, err := ctx.NewBuffer(driver.BufferBindingUniforms, int(unsafe.Sizeof(*g.output.uniforms)))
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.output.uniBuf = buf
+
+ materialVert, materialFrag, err := newShaders(ctx, gio.Shader_material_vert, gio.Shader_material_frag)
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ defer materialVert.Release()
+ defer materialFrag.Release()
+ pipe, err = ctx.NewPipeline(driver.PipelineDesc{
+ VertexShader: materialVert,
+ FragmentShader: materialFrag,
+ VertexLayout: driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 0},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
+ },
+ Stride: int(unsafe.Sizeof(g.materials.quads[0])),
+ },
+ PixelFormat: driver.TextureFormatRGBA8,
+ Topology: driver.TopologyTriangles,
+ })
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.materials.pipeline = pipe
+ g.materials.uniforms.u = new(materialUniforms)
+
+ buf, err = ctx.NewBuffer(driver.BufferBindingUniforms, int(unsafe.Sizeof(*g.materials.uniforms.u)))
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ g.materials.uniforms.buf = buf
+
+ for _, shader := range shaders {
+ if !g.useCPU {
+ p, err := ctx.NewComputeProgram(shader.src)
+ if err != nil {
+ g.Release()
+ return nil, err
+ }
+ shader.prog.prog = p
+ } else {
+ shader.prog.progInfo = shader.info
+ }
+ }
+ if g.useCPU {
+ {
+ desc := new(piet.ElementsDescriptorSetLayout)
+ g.programs.elements.descriptors = unsafe.Pointer(desc)
+ g.programs.elements.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1(), desc.Binding2(), desc.Binding3()}
+ }
+ {
+ desc := new(piet.Tile_allocDescriptorSetLayout)
+ g.programs.tileAlloc.descriptors = unsafe.Pointer(desc)
+ g.programs.tileAlloc.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.Path_coarseDescriptorSetLayout)
+ g.programs.pathCoarse.descriptors = unsafe.Pointer(desc)
+ g.programs.pathCoarse.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.BackdropDescriptorSetLayout)
+ g.programs.backdrop.descriptors = unsafe.Pointer(desc)
+ g.programs.backdrop.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.BinningDescriptorSetLayout)
+ g.programs.binning.descriptors = unsafe.Pointer(desc)
+ g.programs.binning.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.CoarseDescriptorSetLayout)
+ g.programs.coarse.descriptors = unsafe.Pointer(desc)
+ g.programs.coarse.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ }
+ {
+ desc := new(piet.Kernel4DescriptorSetLayout)
+ g.programs.kernel4.descriptors = unsafe.Pointer(desc)
+ g.programs.kernel4.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()}
+ g.output.descriptors = desc
+ }
+ }
+ return g, nil
+}
+
+func newShaders(ctx driver.Device, vsrc, fsrc shader.Sources) (vert driver.VertexShader, frag driver.FragmentShader, err error) {
+ vert, err = ctx.NewVertexShader(vsrc)
+ if err != nil {
+ return
+ }
+ frag, err = ctx.NewFragmentShader(fsrc)
+ if err != nil {
+ vert.Release()
+ }
+ return
+}
+
+func (g *compute) Frame(frameOps *op.Ops, target RenderTarget, viewport image.Point) error {
+ g.frameCount++
+ g.collect(viewport, frameOps)
+ return g.frame(target)
+}
+
+func (g *compute) collect(viewport image.Point, ops *op.Ops) {
+ g.viewport = viewport
+ g.collector.reset()
+
+ g.texOps = g.texOps[:0]
+ g.collector.collect(ops, viewport, &g.texOps)
+}
+
+func (g *compute) Clear(col color.NRGBA) {
+ g.collector.clear = true
+ g.collector.clearColor = f32color.LinearFromSRGB(col)
+}
+
+func (g *compute) frame(target RenderTarget) error {
+ viewport := g.viewport
+ defFBO := g.ctx.BeginFrame(target, g.collector.clear, viewport)
+ defer g.ctx.EndFrame()
+
+ t := &g.timers
+ if g.collector.profile && t.t == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) {
+ t.t = newTimers(g.ctx)
+ t.compact = t.t.newTimer()
+ t.render = t.t.newTimer()
+ t.blit = t.t.newTimer()
+ }
+
+ if err := g.uploadImages(); err != nil {
+ return err
+ }
+ if err := g.renderMaterials(); err != nil {
+ return err
+ }
+ g.layer(viewport, g.texOps)
+ t.render.begin()
+ if err := g.renderLayers(viewport); err != nil {
+ return err
+ }
+ t.render.end()
+ d := driver.LoadDesc{
+ ClearColor: g.collector.clearColor,
+ }
+ if g.collector.clear {
+ g.collector.clear = false
+ d.Action = driver.LoadActionClear
+ }
+ t.blit.begin()
+ g.blitLayers(d, defFBO, viewport)
+ t.blit.end()
+ t.compact.begin()
+ if err := g.compactAllocs(); err != nil {
+ return err
+ }
+ t.compact.end()
+ if g.collector.profile && t.t.ready() {
+ com, ren, blit := t.compact.Elapsed, t.render.Elapsed, t.blit.Elapsed
+ ft := com + ren + blit
+ q := 100 * time.Microsecond
+ ft = ft.Round(q)
+ com, ren, blit = com.Round(q), ren.Round(q), blit.Round(q)
+ t.profile = fmt.Sprintf("ft:%7s com: %7s ren:%7s blit:%7s", ft, com, ren, blit)
+ }
+ return nil
+}
+
+func (g *compute) dumpAtlases() {
+ for i, a := range g.atlases {
+ dump := image.NewRGBA(image.Rectangle{Max: a.size})
+ err := driver.DownloadImage(g.ctx, a.image, dump)
+ if err != nil {
+ panic(err)
+ }
+ nrgba := image.NewNRGBA(dump.Bounds())
+ draw.Draw(nrgba, image.Rectangle{}, dump, image.Point{}, draw.Src)
+ var buf bytes.Buffer
+ if err := png.Encode(&buf, nrgba); err != nil {
+ panic(err)
+ }
+ if err := ioutil.WriteFile(fmt.Sprintf("dump-%d.png", i), buf.Bytes(), 0600); err != nil {
+ panic(err)
+ }
+ }
+}
+
+func (g *compute) Profile() string {
+ return g.timers.profile
+}
+
+func (g *compute) compactAllocs() error {
+ const (
+ maxAllocAge = 3
+ maxAtlasAge = 10
+ )
+ atlases := g.atlases
+ for _, a := range atlases {
+ if len(a.allocs) > 0 && g.frameCount-a.lastFrame > maxAtlasAge {
+ a.compact = true
+ }
+ }
+ for len(atlases) > 0 {
+ var (
+ dstAtlas *textureAtlas
+ format driver.TextureFormat
+ bindings driver.BufferBinding
+ )
+ g.moves = g.moves[:0]
+ addedLayers := false
+ useCPU := false
+ fill:
+ for len(atlases) > 0 {
+ srcAtlas := atlases[0]
+ allocs := srcAtlas.allocs
+ if !srcAtlas.compact {
+ atlases = atlases[1:]
+ continue
+ }
+ if addedLayers && (format != srcAtlas.format || srcAtlas.bindings&bindings != srcAtlas.bindings) {
+ break
+ }
+ format = srcAtlas.format
+ bindings = srcAtlas.bindings
+ for len(srcAtlas.allocs) > 0 {
+ a := srcAtlas.allocs[0]
+ n := len(srcAtlas.allocs)
+ if g.frameCount-a.frameCount > maxAllocAge {
+ a.dead = true
+ srcAtlas.allocs[0] = srcAtlas.allocs[n-1]
+ srcAtlas.allocs = srcAtlas.allocs[:n-1]
+ continue
+ }
+ size := a.rect.Size()
+ alloc, fits := g.atlasAlloc(allocQuery{
+ atlas: dstAtlas,
+ size: size,
+ format: format,
+ bindings: bindings,
+ nocompact: true,
+ })
+ if !fits {
+ break fill
+ }
+ dstAtlas = alloc.atlas
+ allocs = append(allocs, a)
+ addedLayers = true
+ useCPU = useCPU || a.cpu
+ dstAtlas.allocs = append(dstAtlas.allocs, a)
+ pos := alloc.rect.Min
+ g.moves = append(g.moves, atlasMove{
+ src: srcAtlas, dstPos: pos, srcRect: a.rect, cpu: a.cpu,
+ })
+ a.atlas = dstAtlas
+ a.rect = image.Rectangle{Min: pos, Max: pos.Add(a.rect.Size())}
+ srcAtlas.allocs[0] = srcAtlas.allocs[n-1]
+ srcAtlas.allocs = srcAtlas.allocs[:n-1]
+ }
+ srcAtlas.compact = false
+ srcAtlas.realized = false
+ srcAtlas.packer.clear()
+ srcAtlas.packer.newPage()
+ srcAtlas.packer.maxDims = image.Pt(g.maxTextureDim, g.maxTextureDim)
+ atlases = atlases[1:]
+ }
+ if !addedLayers {
+ break
+ }
+ outputSize := dstAtlas.packer.sizes[0]
+ if err := g.realizeAtlas(dstAtlas, useCPU, outputSize); err != nil {
+ return err
+ }
+ for _, move := range g.moves {
+ if !move.cpu {
+ g.ctx.CopyTexture(dstAtlas.image, move.dstPos, move.src.image, move.srcRect)
+ } else {
+ src := move.src.cpuImage.Data()
+ dst := dstAtlas.cpuImage.Data()
+ sstride := move.src.size.X * 4
+ dstride := dstAtlas.size.X * 4
+ copyImage(dst, dstride, move.dstPos, src, sstride, move.srcRect)
+ }
+ }
+ }
+ for i := len(g.atlases) - 1; i >= 0; i-- {
+ a := g.atlases[i]
+ if len(a.allocs) == 0 && g.frameCount-a.lastFrame > maxAtlasAge {
+ a.Release()
+ n := len(g.atlases)
+ g.atlases[i] = g.atlases[n-1]
+ g.atlases = g.atlases[:n-1]
+ }
+ }
+ return nil
+}
+
+func copyImage(dst []byte, dstStride int, dstPos image.Point, src []byte, srcStride int, srcRect image.Rectangle) {
+ sz := srcRect.Size()
+ soff := srcRect.Min.Y*srcStride + srcRect.Min.X*4
+ doff := dstPos.Y*dstStride + dstPos.X*4
+ rowLen := sz.X * 4
+ for y := 0; y < sz.Y; y++ {
+ srow := src[soff : soff+rowLen]
+ drow := dst[doff : doff+rowLen]
+ copy(drow, srow)
+ soff += srcStride
+ doff += dstStride
+ }
+}
+
+func (g *compute) renderLayers(viewport image.Point) error {
+ layers := g.collector.frame.layers
+ for len(layers) > 0 {
+ var materials, dst *textureAtlas
+ addedLayers := false
+ g.enc.reset()
+ for len(layers) > 0 {
+ l := &layers[0]
+ if l.alloc != nil {
+ layers = layers[1:]
+ continue
+ }
+ if materials != nil {
+ if l.materials != nil && materials != l.materials {
+ // Only one materials texture per compute pass.
+ break
+ }
+ } else {
+ materials = l.materials
+ }
+ size := l.rect.Size()
+ alloc, fits := g.atlasAlloc(allocQuery{
+ atlas: dst,
+ empty: true,
+ format: driver.TextureFormatRGBA8,
+ bindings: combinedBindings,
+ // Pad to avoid overlap.
+ size: size.Add(image.Pt(1, 1)),
+ })
+ if !fits {
+ // Only one output atlas per compute pass.
+ break
+ }
+ dst = alloc.atlas
+ dst.compact = true
+ addedLayers = true
+ l.alloc = &alloc
+ dst.allocs = append(dst.allocs, l.alloc)
+ encodeLayer(*l, alloc.rect.Min, viewport, &g.enc, g.texOps)
+ layers = layers[1:]
+ }
+ if !addedLayers {
+ break
+ }
+ outputSize := dst.packer.sizes[0]
+ tileDims := image.Point{
+ X: (outputSize.X + tileWidthPx - 1) / tileWidthPx,
+ Y: (outputSize.Y + tileHeightPx - 1) / tileHeightPx,
+ }
+ w, h := tileDims.X*tileWidthPx, tileDims.Y*tileHeightPx
+ if err := g.realizeAtlas(dst, g.useCPU, image.Pt(w, h)); err != nil {
+ return err
+ }
+ if err := g.render(materials, dst.image, dst.cpuImage, tileDims, dst.size.X*4); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (g *compute) blitLayers(d driver.LoadDesc, fbo driver.Texture, viewport image.Point) {
+ layers := g.collector.frame.layers
+ g.output.layerVertices = g.output.layerVertices[:0]
+ for _, l := range layers {
+ placef := layout.FPt(l.alloc.rect.Min)
+ sizef := layout.FPt(l.rect.Size())
+ r := layout.FRect(l.rect)
+ quad := [4]layerVertex{
+ {posX: float32(r.Min.X), posY: float32(r.Min.Y), u: placef.X, v: placef.Y},
+ {posX: float32(r.Max.X), posY: float32(r.Min.Y), u: placef.X + sizef.X, v: placef.Y},
+ {posX: float32(r.Max.X), posY: float32(r.Max.Y), u: placef.X + sizef.X, v: placef.Y + sizef.Y},
+ {posX: float32(r.Min.X), posY: float32(r.Max.Y), u: placef.X, v: placef.Y + sizef.Y},
+ }
+ g.output.layerVertices = append(g.output.layerVertices, quad[0], quad[1], quad[3], quad[3], quad[2], quad[1])
+ g.ctx.PrepareTexture(l.alloc.atlas.image)
+ }
+ if len(g.output.layerVertices) > 0 {
+ vertexData := byteslice.Slice(g.output.layerVertices)
+ g.output.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, len(vertexData))
+ g.output.buffer.buffer.Upload(vertexData)
+ }
+ g.ctx.BeginRenderPass(fbo, d)
+ defer g.ctx.EndRenderPass()
+ if len(layers) == 0 {
+ return
+ }
+ g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
+ g.ctx.BindPipeline(g.output.blitPipeline)
+ g.ctx.BindVertexBuffer(g.output.buffer.buffer, 0)
+ start := 0
+ for len(layers) > 0 {
+ count := 0
+ atlas := layers[0].alloc.atlas
+ for len(layers) > 0 {
+ l := layers[0]
+ if l.alloc.atlas != atlas {
+ break
+ }
+ layers = layers[1:]
+ const verticesPerQuad = 6
+ count += verticesPerQuad
+ }
+
+ // Transform positions to clip space: [-1, -1] - [1, 1], and texture
+ // coordinates to texture space: [0, 0] - [1, 1].
+ clip := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(2/float32(viewport.X), 2/float32(viewport.Y))).Offset(f32.Pt(-1, -1))
+ sx, _, ox, _, sy, oy := clip.Elems()
+ g.output.uniforms.scale = [2]float32{sx, sy}
+ g.output.uniforms.pos = [2]float32{ox, oy}
+ g.output.uniforms.uvScale = [2]float32{1 / float32(atlas.size.X), 1 / float32(atlas.size.Y)}
+ g.output.uniBuf.Upload(byteslice.Struct(g.output.uniforms))
+ g.ctx.BindUniforms(g.output.uniBuf)
+ g.ctx.BindTexture(0, atlas.image)
+ g.ctx.DrawArrays(start, count)
+ start += count
+ }
+}
+
+func (g *compute) renderMaterials() error {
+ m := &g.materials
+ for k, place := range m.allocs {
+ if place.alloc.dead {
+ delete(m.allocs, k)
+ }
+ }
+ texOps := g.texOps
+ for len(texOps) > 0 {
+ m.quads = m.quads[:0]
+ var (
+ atlas *textureAtlas
+ imgAtlas *textureAtlas
+ )
+ // A material is clipped to avoid drawing outside its atlas bounds.
+ // However, imprecision in the clipping may cause a single pixel
+ // overflow.
+ var padding = image.Pt(1, 1)
+ var allocStart int
+ for len(texOps) > 0 {
+ op := &texOps[0]
+ if a, exists := m.allocs[op.key]; exists {
+ g.touchAlloc(a.alloc)
+ op.matAlloc = a
+ texOps = texOps[1:]
+ continue
+ }
+
+ if imgAtlas != nil && op.imgAlloc.atlas != imgAtlas {
+ // Only one image atlas per render pass.
+ break
+ }
+ imgAtlas = op.imgAlloc.atlas
+ quad := g.materialQuad(imgAtlas.size, op.key.transform, op.img, op.imgAlloc.rect.Min)
+ boundsf := quadBounds(quad)
+ bounds := boundRectF(boundsf)
+ bounds = bounds.Intersect(op.key.bounds)
+
+ size := bounds.Size()
+ alloc, fits := g.atlasAlloc(allocQuery{
+ atlas: atlas,
+ size: size.Add(padding),
+ format: driver.TextureFormatRGBA8,
+ bindings: combinedBindings,
+ })
+ if !fits {
+ break
+ }
+ if atlas == nil {
+ allocStart = len(alloc.atlas.allocs)
+ }
+ atlas = alloc.atlas
+ alloc.cpu = g.useCPU
+ offsetf := layout.FPt(bounds.Min.Mul(-1))
+ scale := f32.Pt(float32(size.X), float32(size.Y))
+ for i := range quad {
+ // Position quad to match place.
+ quad[i].posX += offsetf.X
+ quad[i].posY += offsetf.Y
+ // Scale to match viewport [0, 1].
+ quad[i].posX /= scale.X
+ quad[i].posY /= scale.Y
+ }
+ // Draw quad as two triangles.
+ m.quads = append(m.quads, quad[0], quad[1], quad[3], quad[3], quad[1], quad[2])
+ if m.allocs == nil {
+ m.allocs = make(map[textureKey]materialAlloc)
+ }
+ atlasAlloc := materialAlloc{
+ alloc: &alloc,
+ offset: bounds.Min.Mul(-1),
+ }
+ atlas.allocs = append(atlas.allocs, atlasAlloc.alloc)
+ m.allocs[op.key] = atlasAlloc
+ op.matAlloc = atlasAlloc
+ texOps = texOps[1:]
+ }
+ if len(m.quads) == 0 {
+ break
+ }
+ realized := atlas.realized
+ if err := g.realizeAtlas(atlas, g.useCPU, atlas.packer.sizes[0]); err != nil {
+ return err
+ }
+ // Transform to clip space: [-1, -1] - [1, 1].
+ *m.uniforms.u = materialUniforms{
+ scale: [2]float32{2, 2},
+ pos: [2]float32{-1, -1},
+ }
+ if !g.srgb {
+ m.uniforms.u.emulatesRGB = 1.0
+ }
+ m.uniforms.buf.Upload(byteslice.Struct(m.uniforms.u))
+ vertexData := byteslice.Slice(m.quads)
+ n := pow2Ceil(len(vertexData))
+ m.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, n)
+ m.buffer.buffer.Upload(vertexData)
+ var d driver.LoadDesc
+ if !realized {
+ d.Action = driver.LoadActionClear
+ }
+ g.ctx.PrepareTexture(imgAtlas.image)
+ g.ctx.BeginRenderPass(atlas.image, d)
+ g.ctx.BindTexture(0, imgAtlas.image)
+ g.ctx.BindPipeline(m.pipeline)
+ g.ctx.BindUniforms(m.uniforms.buf)
+ g.ctx.BindVertexBuffer(m.buffer.buffer, 0)
+ newAllocs := atlas.allocs[allocStart:]
+ for i, a := range newAllocs {
+ sz := a.rect.Size().Sub(padding)
+ g.ctx.Viewport(a.rect.Min.X, a.rect.Min.Y, sz.X, sz.Y)
+ g.ctx.DrawArrays(i*6, 6)
+ }
+ g.ctx.EndRenderPass()
+ if !g.useCPU {
+ continue
+ }
+ src := atlas.image
+ data := atlas.cpuImage.Data()
+ for _, a := range newAllocs {
+ stride := atlas.size.X * 4
+ col := a.rect.Min.X * 4
+ row := stride * a.rect.Min.Y
+ off := col + row
+ src.ReadPixels(a.rect, data[off:], stride)
+ }
+ }
+ return nil
+}
+
+func (g *compute) uploadImages() error {
+ for k, a := range g.imgAllocs {
+ if a.dead {
+ delete(g.imgAllocs, k)
+ }
+ }
+ type upload struct {
+ pos image.Point
+ img *image.RGBA
+ }
+ var uploads []upload
+ format := driver.TextureFormatSRGBA
+ if !g.srgb {
+ format = driver.TextureFormatRGBA8
+ }
+ // padding is the number of pixels added to the right and below
+ // images, to avoid atlas filtering artifacts.
+ const padding = 1
+ texOps := g.texOps
+ for len(texOps) > 0 {
+ uploads = uploads[:0]
+ var atlas *textureAtlas
+ for len(texOps) > 0 {
+ op := &texOps[0]
+ if a, exists := g.imgAllocs[op.img.handle]; exists {
+ g.touchAlloc(a)
+ op.imgAlloc = a
+ texOps = texOps[1:]
+ continue
+ }
+ size := op.img.src.Bounds().Size().Add(image.Pt(padding, padding))
+ alloc, fits := g.atlasAlloc(allocQuery{
+ atlas: atlas,
+ size: size,
+ format: format,
+ bindings: driver.BufferBindingTexture | driver.BufferBindingFramebuffer,
+ })
+ if !fits {
+ break
+ }
+ atlas = alloc.atlas
+ if g.imgAllocs == nil {
+ g.imgAllocs = make(map[interface{}]*atlasAlloc)
+ }
+ op.imgAlloc = &alloc
+ atlas.allocs = append(atlas.allocs, op.imgAlloc)
+ g.imgAllocs[op.img.handle] = op.imgAlloc
+ uploads = append(uploads, upload{pos: alloc.rect.Min, img: op.img.src})
+ texOps = texOps[1:]
+ }
+ if len(uploads) == 0 {
+ break
+ }
+ if err := g.realizeAtlas(atlas, false, atlas.packer.sizes[0]); err != nil {
+ return err
+ }
+ for _, u := range uploads {
+ size := u.img.Bounds().Size()
+ driver.UploadImage(atlas.image, u.pos, u.img)
+ rightPadding := image.Pt(padding, size.Y)
+ atlas.image.Upload(image.Pt(u.pos.X+size.X, u.pos.Y), rightPadding, g.zeros(rightPadding.X*rightPadding.Y*4), 0)
+ bottomPadding := image.Pt(size.X, padding)
+ atlas.image.Upload(image.Pt(u.pos.X, u.pos.Y+size.Y), bottomPadding, g.zeros(bottomPadding.X*bottomPadding.Y*4), 0)
+ }
+ }
+ return nil
+}
+
+func pow2Ceil(v int) int {
+ exp := bits.Len(uint(v))
+ if bits.OnesCount(uint(v)) == 1 {
+ exp--
+ }
+ return 1 << exp
+}
+
+// materialQuad constructs a quad that represents the transformed image. It returns the quad
+// and its bounds.
+func (g *compute) materialQuad(imgAtlasSize image.Point, M f32.Affine2D, img imageOpData, uvPos image.Point) [4]materialVertex {
+ imgSize := layout.FPt(img.src.Bounds().Size())
+ sx, hx, ox, hy, sy, oy := M.Elems()
+ transOff := f32.Pt(ox, oy)
+ // The 4 corners of the image rectangle transformed by M, excluding its offset, are:
+ //
+ // q0: M * (0, 0) q3: M * (w, 0)
+ // q1: M * (0, h) q2: M * (w, h)
+ //
+ // Note that q0 = M*0 = 0, q2 = q1 + q3.
+ q0 := f32.Pt(0, 0)
+ q1 := f32.Pt(hx*imgSize.Y, sy*imgSize.Y)
+ q3 := f32.Pt(sx*imgSize.X, hy*imgSize.X)
+ q2 := q1.Add(q3)
+ q0 = q0.Add(transOff)
+ q1 = q1.Add(transOff)
+ q2 = q2.Add(transOff)
+ q3 = q3.Add(transOff)
+
+ uvPosf := layout.FPt(uvPos)
+ atlasScale := f32.Pt(1/float32(imgAtlasSize.X), 1/float32(imgAtlasSize.Y))
+ uvBounds := f32.Rectangle{
+ Min: uvPosf,
+ Max: uvPosf.Add(imgSize),
+ }
+ uvBounds.Min.X *= atlasScale.X
+ uvBounds.Min.Y *= atlasScale.Y
+ uvBounds.Max.X *= atlasScale.X
+ uvBounds.Max.Y *= atlasScale.Y
+ quad := [4]materialVertex{
+ {posX: q0.X, posY: q0.Y, u: uvBounds.Min.X, v: uvBounds.Min.Y},
+ {posX: q1.X, posY: q1.Y, u: uvBounds.Min.X, v: uvBounds.Max.Y},
+ {posX: q2.X, posY: q2.Y, u: uvBounds.Max.X, v: uvBounds.Max.Y},
+ {posX: q3.X, posY: q3.Y, u: uvBounds.Max.X, v: uvBounds.Min.Y},
+ }
+ return quad
+}
+
+func quadBounds(q [4]materialVertex) f32.Rectangle {
+ q0 := f32.Pt(q[0].posX, q[0].posY)
+ q1 := f32.Pt(q[1].posX, q[1].posY)
+ q2 := f32.Pt(q[2].posX, q[2].posY)
+ q3 := f32.Pt(q[3].posX, q[3].posY)
+ return f32.Rectangle{
+ Min: min(min(q0, q1), min(q2, q3)),
+ Max: max(max(q0, q1), max(q2, q3)),
+ }
+}
+
+func max(p1, p2 f32.Point) f32.Point {
+ p := p1
+ if p2.X > p.X {
+ p.X = p2.X
+ }
+ if p2.Y > p.Y {
+ p.Y = p2.Y
+ }
+ return p
+}
+
+func min(p1, p2 f32.Point) f32.Point {
+ p := p1
+ if p2.X < p.X {
+ p.X = p2.X
+ }
+ if p2.Y < p.Y {
+ p.Y = p2.Y
+ }
+ return p
+}
+
+func (enc *encoder) encodePath(verts []byte, fillMode int) {
+ for ; len(verts) >= scene.CommandSize+4; verts = verts[scene.CommandSize+4:] {
+ cmd := ops.DecodeCommand(verts[4:])
+ if cmd.Op() == scene.OpGap {
+ if fillMode != scene.FillModeNonzero {
+ // Skip gaps in strokes.
+ continue
+ }
+ // Replace them by a straight line in outlines.
+ cmd = scene.Line(scene.DecodeGap(cmd))
+ }
+ enc.scene = append(enc.scene, cmd)
+ enc.npathseg++
+ }
+}
+
+func (g *compute) render(images *textureAtlas, dst driver.Texture, cpuDst cpu.ImageDescriptor, tileDims image.Point, stride int) error {
+ const (
+ // wgSize is the largest and most common workgroup size.
+ wgSize = 128
+ // PARTITION_SIZE from elements.comp
+ partitionSize = 32 * 4
+ )
+ widthInBins := (tileDims.X + 15) / 16
+ heightInBins := (tileDims.Y + 7) / 8
+ if widthInBins*heightInBins > wgSize {
+ return fmt.Errorf("gpu: output too large (%dx%d)", tileDims.X*tileWidthPx, tileDims.Y*tileHeightPx)
+ }
+
+ enc := &g.enc
+ // Pad scene with zeroes to avoid reading garbage in elements.comp.
+ scenePadding := partitionSize - len(enc.scene)%partitionSize
+ enc.scene = append(enc.scene, make([]scene.Command, scenePadding)...)
+
+ scene := byteslice.Slice(enc.scene)
+ if s := len(scene); s > g.buffers.scene.size {
+ paddedCap := s * 11 / 10
+ if err := g.buffers.scene.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead, paddedCap); err != nil {
+ return err
+ }
+ }
+ g.buffers.scene.upload(scene)
+
+ // alloc is the number of allocated bytes for static buffers.
+ var alloc uint32
+ round := func(v, quantum int) int {
+ return (v + quantum - 1) &^ (quantum - 1)
+ }
+ malloc := func(size int) memAlloc {
+ size = round(size, 4)
+ offset := alloc
+ alloc += uint32(size)
+ return memAlloc{offset /*, uint32(size)*/}
+ }
+
+ *g.conf = config{
+ n_elements: uint32(enc.npath),
+ n_pathseg: uint32(enc.npathseg),
+ width_in_tiles: uint32(tileDims.X),
+ height_in_tiles: uint32(tileDims.Y),
+ tile_alloc: malloc(enc.npath * pathSize),
+ bin_alloc: malloc(round(enc.npath, wgSize) * binSize),
+ ptcl_alloc: malloc(tileDims.X * tileDims.Y * ptclInitialAlloc),
+ pathseg_alloc: malloc(enc.npathseg * pathsegSize),
+ anno_alloc: malloc(enc.npath * annoSize),
+ trans_alloc: malloc(enc.ntrans * transSize),
+ }
+
+ numPartitions := (enc.numElements() + 127) / 128
+ // clearSize is the atomic partition counter plus flag and 2 states per partition.
+ clearSize := 4 + numPartitions*stateStride
+ if clearSize > g.buffers.state.size {
+ paddedCap := clearSize * 11 / 10
+ if err := g.buffers.state.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite, paddedCap); err != nil {
+ return err
+ }
+ }
+
+ confData := byteslice.Struct(g.conf)
+ g.buffers.config.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead, len(confData))
+ g.buffers.config.upload(confData)
+
+ minSize := int(unsafe.Sizeof(memoryHeader{})) + int(alloc)
+ if minSize > g.buffers.memory.size {
+ // Add space for dynamic GPU allocations.
+ const sizeBump = 4 * 1024 * 1024
+ minSize += sizeBump
+ if err := g.buffers.memory.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite, minSize); err != nil {
+ return err
+ }
+ }
+
+ for {
+ *g.memHeader = memoryHeader{
+ mem_offset: alloc,
+ }
+ g.buffers.memory.upload(byteslice.Struct(g.memHeader))
+ g.buffers.state.upload(g.zeros(clearSize))
+
+ if !g.useCPU {
+ g.ctx.BeginCompute()
+ g.ctx.BindImageTexture(kernel4OutputUnit, dst)
+ img := g.output.nullMaterials
+ if images != nil {
+ img = images.image
+ }
+ g.ctx.BindImageTexture(kernel4AtlasUnit, img)
+ } else {
+ *g.output.descriptors.Binding2() = cpuDst
+ if images != nil {
+ *g.output.descriptors.Binding3() = images.cpuImage
+ }
+ }
+
+ g.bindBuffers()
+ g.memoryBarrier()
+ g.dispatch(g.programs.elements, numPartitions, 1, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.tileAlloc, (enc.npath+wgSize-1)/wgSize, 1, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.pathCoarse, (enc.npathseg+31)/32, 1, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.backdrop, (enc.npath+wgSize-1)/wgSize, 1, 1)
+ // No barrier needed between backdrop and binning.
+ g.dispatch(g.programs.binning, (enc.npath+wgSize-1)/wgSize, 1, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.coarse, widthInBins, heightInBins, 1)
+ g.memoryBarrier()
+ g.dispatch(g.programs.kernel4, tileDims.X, tileDims.Y, 1)
+ g.memoryBarrier()
+ if !g.useCPU {
+ g.ctx.EndCompute()
+ } else {
+ g.dispatcher.Sync()
+ }
+
+ if err := g.buffers.memory.download(byteslice.Struct(g.memHeader)); err != nil {
+ if err == driver.ErrContentLost {
+ continue
+ }
+ return err
+ }
+ switch errCode := g.memHeader.mem_error; errCode {
+ case memNoError:
+ if g.useCPU {
+ w, h := tileDims.X*tileWidthPx, tileDims.Y*tileHeightPx
+ dst.Upload(image.Pt(0, 0), image.Pt(w, h), cpuDst.Data(), stride)
+ }
+ return nil
+ case memMallocFailed:
+ // Resize memory and try again.
+ sz := g.buffers.memory.size * 15 / 10
+ if err := g.buffers.memory.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite, sz); err != nil {
+ return err
+ }
+ continue
+ default:
+ return fmt.Errorf("compute: shader program failed with error %d", errCode)
+ }
+ }
+}
+
+func (g *compute) memoryBarrier() {
+ if g.useCPU {
+ g.dispatcher.Barrier()
+ }
+}
+
+func (g *compute) dispatch(p computeProgram, x, y, z int) {
+ if !g.useCPU {
+ g.ctx.BindProgram(p.prog)
+ g.ctx.DispatchCompute(x, y, z)
+ } else {
+ g.dispatcher.Dispatch(p.progInfo, p.descriptors, x, y, z)
+ }
+}
+
+// zeros returns a byte slice with size bytes of zeros.
+func (g *compute) zeros(size int) []byte {
+ if cap(g.zeroSlice) < size {
+ g.zeroSlice = append(g.zeroSlice, make([]byte, size)...)
+ }
+ return g.zeroSlice[:size]
+}
+
+func (g *compute) touchAlloc(a *atlasAlloc) {
+ if a.dead {
+ panic("re-use of dead allocation")
+ }
+ a.frameCount = g.frameCount
+ a.atlas.lastFrame = a.frameCount
+}
+
+func (g *compute) atlasAlloc(q allocQuery) (atlasAlloc, bool) {
+ var (
+ place placement
+ fits bool
+ atlas = q.atlas
+ )
+ if atlas != nil {
+ place, fits = atlas.packer.tryAdd(q.size)
+ if !fits {
+ atlas.compact = true
+ }
+ }
+ if atlas == nil {
+ // Look for matching atlas to re-use.
+ for _, a := range g.atlases {
+ if q.empty && len(a.allocs) > 0 {
+ continue
+ }
+ if q.nocompact && a.compact {
+ continue
+ }
+ if a.format != q.format || a.bindings&q.bindings != q.bindings {
+ continue
+ }
+ place, fits = a.packer.tryAdd(q.size)
+ if !fits {
+ a.compact = true
+ continue
+ }
+ atlas = a
+ break
+ }
+ }
+ if atlas == nil {
+ atlas = &textureAtlas{
+ format: q.format,
+ bindings: q.bindings,
+ }
+ atlas.packer.maxDims = image.Pt(g.maxTextureDim, g.maxTextureDim)
+ atlas.packer.newPage()
+ g.atlases = append(g.atlases, atlas)
+ place, fits = atlas.packer.tryAdd(q.size)
+ if !fits {
+ panic(fmt.Errorf("compute: atlas allocation too large (%v)", q.size))
+ }
+ }
+ if !fits {
+ return atlasAlloc{}, false
+ }
+ atlas.lastFrame = g.frameCount
+ return atlasAlloc{
+ frameCount: g.frameCount,
+ atlas: atlas,
+ rect: image.Rectangle{Min: place.Pos, Max: place.Pos.Add(q.size)},
+ }, true
+}
+
+func (g *compute) realizeAtlas(atlas *textureAtlas, useCPU bool, size image.Point) error {
+ defer func() {
+ atlas.packer.maxDims = atlas.size
+ atlas.realized = true
+ atlas.ensureCPUImage(useCPU)
+ }()
+ if atlas.size.X >= size.X && atlas.size.Y >= size.Y {
+ return nil
+ }
+ if atlas.realized {
+ panic("resizing a realized atlas")
+ }
+ if err := atlas.resize(g.ctx, size); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (a *textureAtlas) resize(ctx driver.Device, size image.Point) error {
+ a.Release()
+
+ img, err := ctx.NewTexture(a.format, size.X, size.Y,
+ driver.FilterNearest,
+ driver.FilterNearest,
+ a.bindings)
+ if err != nil {
+ return err
+ }
+ a.image = img
+ a.size = size
+ return nil
+}
+
+func (a *textureAtlas) ensureCPUImage(useCPU bool) {
+ if !useCPU || a.hasCPU {
+ return
+ }
+ a.hasCPU = true
+ a.cpuImage = cpu.NewImageRGBA(a.size.X, a.size.Y)
+}
+
+func (g *compute) Release() {
+ if g.useCPU {
+ g.dispatcher.Stop()
+ }
+ type resource interface {
+ Release()
+ }
+ res := []resource{
+ g.output.nullMaterials,
+ &g.programs.elements,
+ &g.programs.tileAlloc,
+ &g.programs.pathCoarse,
+ &g.programs.backdrop,
+ &g.programs.binning,
+ &g.programs.coarse,
+ &g.programs.kernel4,
+ g.output.blitPipeline,
+ &g.output.buffer,
+ g.output.uniBuf,
+ &g.buffers.scene,
+ &g.buffers.state,
+ &g.buffers.memory,
+ &g.buffers.config,
+ g.materials.pipeline,
+ &g.materials.buffer,
+ g.materials.uniforms.buf,
+ g.timers.t,
+ }
+ for _, r := range res {
+ if r != nil {
+ r.Release()
+ }
+ }
+ for _, a := range g.atlases {
+ a.Release()
+ }
+ g.ctx.Release()
+ *g = compute{}
+}
+
+func (a *textureAtlas) Release() {
+ if a.image != nil {
+ a.image.Release()
+ a.image = nil
+ }
+ a.cpuImage.Free()
+ a.hasCPU = false
+}
+
+func (g *compute) bindBuffers() {
+ g.bindStorageBuffers(g.programs.elements, g.buffers.memory, g.buffers.config, g.buffers.scene, g.buffers.state)
+ g.bindStorageBuffers(g.programs.tileAlloc, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.pathCoarse, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.backdrop, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.binning, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.coarse, g.buffers.memory, g.buffers.config)
+ g.bindStorageBuffers(g.programs.kernel4, g.buffers.memory, g.buffers.config)
+}
+
+func (p *computeProgram) Release() {
+ if p.prog != nil {
+ p.prog.Release()
+ }
+ *p = computeProgram{}
+}
+
+func (b *sizedBuffer) Release() {
+ if b.buffer != nil {
+ b.buffer.Release()
+ }
+ b.cpuBuf.Free()
+ *b = sizedBuffer{}
+}
+
+func (b *sizedBuffer) ensureCapacity(useCPU bool, ctx driver.Device, binding driver.BufferBinding, size int) error {
+ if b.size >= size {
+ return nil
+ }
+ if b.buffer != nil {
+ b.Release()
+ }
+ b.cpuBuf.Free()
+ if !useCPU {
+ buf, err := ctx.NewBuffer(binding, size)
+ if err != nil {
+ return err
+ }
+ b.buffer = buf
+ } else {
+ b.cpuBuf = cpu.NewBuffer(size)
+ }
+ b.size = size
+ return nil
+}
+
+func (b *sizedBuffer) download(data []byte) error {
+ if b.buffer != nil {
+ return b.buffer.Download(data)
+ } else {
+ copy(data, b.cpuBuf.Data())
+ return nil
+ }
+}
+
+func (b *sizedBuffer) upload(data []byte) {
+ if b.buffer != nil {
+ b.buffer.Upload(data)
+ } else {
+ copy(b.cpuBuf.Data(), data)
+ }
+}
+
+func (g *compute) bindStorageBuffers(prog computeProgram, buffers ...sizedBuffer) {
+ for i, buf := range buffers {
+ if !g.useCPU {
+ g.ctx.BindStorageBuffer(i, buf.buffer)
+ } else {
+ *prog.buffers[i] = buf.cpuBuf
+ }
+ }
+}
+
+var bo = binary.LittleEndian
+
+func (e *encoder) reset() {
+ e.scene = e.scene[:0]
+ e.npath = 0
+ e.npathseg = 0
+ e.ntrans = 0
+}
+
+func (e *encoder) numElements() int {
+ return len(e.scene)
+}
+
+func (e *encoder) append(e2 encoder) {
+ e.scene = append(e.scene, e2.scene...)
+ e.npath += e2.npath
+ e.npathseg += e2.npathseg
+ e.ntrans += e2.ntrans
+}
+
+func (e *encoder) transform(m f32.Affine2D) {
+ e.scene = append(e.scene, scene.Transform(m))
+ e.ntrans++
+}
+
+func (e *encoder) lineWidth(width float32) {
+ e.scene = append(e.scene, scene.SetLineWidth(width))
+}
+
+func (e *encoder) fillMode(mode scene.FillMode) {
+ e.scene = append(e.scene, scene.SetFillMode(mode))
+}
+
+func (e *encoder) beginClip(bbox f32.Rectangle) {
+ e.scene = append(e.scene, scene.BeginClip(bbox))
+ e.npath++
+}
+
+func (e *encoder) endClip(bbox f32.Rectangle) {
+ e.scene = append(e.scene, scene.EndClip(bbox))
+ e.npath++
+}
+
+func (e *encoder) rect(r f32.Rectangle) {
+ // Rectangle corners, clock-wise.
+ c0, c1, c2, c3 := r.Min, f32.Pt(r.Min.X, r.Max.Y), r.Max, f32.Pt(r.Max.X, r.Min.Y)
+ e.line(c0, c1)
+ e.line(c1, c2)
+ e.line(c2, c3)
+ e.line(c3, c0)
+}
+
+func (e *encoder) fillColor(col color.RGBA) {
+ e.scene = append(e.scene, scene.FillColor(col))
+ e.npath++
+}
+
+func (e *encoder) fillImage(index int, offset image.Point) {
+ e.scene = append(e.scene, scene.FillImage(index, offset))
+ e.npath++
+}
+
+func (e *encoder) line(start, end f32.Point) {
+ e.scene = append(e.scene, scene.Line(start, end))
+ e.npathseg++
+}
+
+func (e *encoder) quad(start, ctrl, end f32.Point) {
+ e.scene = append(e.scene, scene.Quad(start, ctrl, end))
+ e.npathseg++
+}
+
+func (c *collector) reset() {
+ c.prevFrame, c.frame = c.frame, c.prevFrame
+ c.profile = false
+ c.clipStates = c.clipStates[:0]
+ c.transStack = c.transStack[:0]
+ c.frame.reset()
+}
+
+func (c *opsCollector) reset() {
+ c.paths = c.paths[:0]
+ c.clipCmds = c.clipCmds[:0]
+ c.ops = c.ops[:0]
+ c.layers = c.layers[:0]
+}
+
+func (c *collector) addClip(state *encoderState, viewport, bounds f32.Rectangle, path []byte, key ops.Key, hash uint64, strokeWidth float32, push bool) {
+ // Rectangle clip regions.
+ if len(path) == 0 && !push {
+ // If the rectangular clip region contains a previous path it can be discarded.
+ p := state.clip
+ t := state.relTrans.Invert()
+ for p != nil {
+ // rect is the parent bounds transformed relative to the rectangle.
+ rect := transformBounds(t, p.bounds)
+ if rect.In(bounds) {
+ return
+ }
+ t = p.relTrans.Invert().Mul(t)
+ p = p.parent
+ }
+ }
+
+ absBounds := transformBounds(state.t, bounds).Bounds()
+ intersect := absBounds
+ if state.clip != nil {
+ intersect = state.clip.intersect.Intersect(intersect)
+ }
+ c.clipStates = append(c.clipStates, clipState{
+ parent: state.clip,
+ absBounds: absBounds,
+ path: path,
+ pathKey: key,
+ intersect: intersect,
+ clipKey: clipKey{
+ bounds: bounds,
+ relTrans: state.relTrans,
+ strokeWidth: strokeWidth,
+ pathHash: hash,
+ },
+ })
+ state.clip = &c.clipStates[len(c.clipStates)-1]
+ state.relTrans = f32.Affine2D{}
+}
+
+func (c *collector) collect(root *op.Ops, viewport image.Point, texOps *[]textureOp) {
+ fview := f32.Rectangle{Max: layout.FPt(viewport)}
+ var intOps *ops.Ops
+ if root != nil {
+ intOps = &root.Internal
+ }
+ c.reader.Reset(intOps)
+ var state encoderState
+ reset := func() {
+ state = encoderState{
+ paintKey: paintKey{
+ color: color.NRGBA{A: 0xff},
+ },
+ }
+ }
+ reset()
+ r := &c.reader
+ var (
+ pathData struct {
+ data []byte
+ key ops.Key
+ hash uint64
+ }
+ strWidth float32
+ )
+ c.addClip(&state, fview, fview, nil, ops.Key{}, 0, 0, false)
+ for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
+ switch ops.OpType(encOp.Data[0]) {
+ case ops.TypeProfile:
+ c.profile = true
+ case ops.TypeTransform:
+ dop, push := ops.DecodeTransform(encOp.Data)
+ if push {
+ c.transStack = append(c.transStack, transEntry{t: state.t, relTrans: state.relTrans})
+ }
+ state.t = state.t.Mul(dop)
+ state.relTrans = state.relTrans.Mul(dop)
+ case ops.TypePopTransform:
+ n := len(c.transStack)
+ st := c.transStack[n-1]
+ c.transStack = c.transStack[:n-1]
+ state.t = st.t
+ state.relTrans = st.relTrans
+ case ops.TypeStroke:
+ strWidth = decodeStrokeOp(encOp.Data)
+ case ops.TypePath:
+ hash := bo.Uint64(encOp.Data[1:])
+ encOp, ok = r.Decode()
+ if !ok {
+ panic("unexpected end of path operation")
+ }
+ pathData.data = encOp.Data[ops.TypeAuxLen:]
+ pathData.key = encOp.Key
+ pathData.hash = hash
+ case ops.TypeClip:
+ var op ops.ClipOp
+ op.Decode(encOp.Data)
+ bounds := layout.FRect(op.Bounds)
+ c.addClip(&state, fview, bounds, pathData.data, pathData.key, pathData.hash, strWidth, true)
+ pathData.data = nil
+ strWidth = 0
+ case ops.TypePopClip:
+ state.relTrans = state.clip.relTrans.Mul(state.relTrans)
+ state.clip = state.clip.parent
+ case ops.TypeColor:
+ state.matType = materialColor
+ state.color = decodeColorOp(encOp.Data)
+ case ops.TypeLinearGradient:
+ state.matType = materialLinearGradient
+ op := decodeLinearGradientOp(encOp.Data)
+ state.stop1 = op.stop1
+ state.stop2 = op.stop2
+ state.color1 = op.color1
+ state.color2 = op.color2
+ case ops.TypeImage:
+ state.matType = materialTexture
+ state.image = decodeImageOp(encOp.Data, encOp.Refs)
+ case ops.TypePaint:
+ paintState := state
+ if paintState.matType == materialTexture {
+ // Clip to the bounds of the image, to hide other images in the atlas.
+ sz := state.image.src.Rect.Size()
+ bounds := f32.Rectangle{Max: layout.FPt(sz)}
+ c.addClip(&paintState, fview, bounds, nil, ops.Key{}, 0, 0, false)
+ }
+ intersect := paintState.clip.intersect
+ if intersect.Empty() {
+ break
+ }
+
+ // If the paint is a uniform opaque color that takes up the whole
+ // screen, it covers all previous paints and we can discard all
+ // rendering commands recorded so far.
+ if paintState.clip == nil && paintState.matType == materialColor && paintState.color.A == 255 {
+ c.clearColor = f32color.LinearFromSRGB(paintState.color).Opaque()
+ c.clear = true
+ c.frame.reset()
+ break
+ }
+
+ // Flatten clip stack.
+ p := paintState.clip
+ startIdx := len(c.frame.clipCmds)
+ for p != nil {
+ idx := len(c.frame.paths)
+ c.frame.paths = append(c.frame.paths, make([]byte, len(p.path))...)
+ path := c.frame.paths[idx:]
+ copy(path, p.path)
+ c.frame.clipCmds = append(c.frame.clipCmds, clipCmd{
+ state: p.clipKey,
+ path: path,
+ pathKey: p.pathKey,
+ absBounds: p.absBounds,
+ })
+ p = p.parent
+ }
+ clipStack := c.frame.clipCmds[startIdx:]
+ c.frame.ops = append(c.frame.ops, paintOp{
+ clipStack: clipStack,
+ state: paintState.paintKey,
+ intersect: intersect,
+ })
+ case ops.TypeSave:
+ id := ops.DecodeSave(encOp.Data)
+ c.save(id, state.t)
+ case ops.TypeLoad:
+ reset()
+ id := ops.DecodeLoad(encOp.Data)
+ state.t = c.states[id]
+ state.relTrans = state.t
+ }
+ }
+ for i := range c.frame.ops {
+ op := &c.frame.ops[i]
+ // For each clip, cull rectangular clip regions that contain its
+ // (transformed) bounds. addClip already handled the converse case.
+ // TODO: do better than O(n²) to efficiently deal with deep stacks.
+ for j := 0; j < len(op.clipStack)-1; j++ {
+ cl := op.clipStack[j]
+ p := cl.state
+ r := transformBounds(p.relTrans, p.bounds)
+ for k := j + 1; k < len(op.clipStack); k++ {
+ cl2 := op.clipStack[k]
+ p2 := cl2.state
+ if len(cl2.path) == 0 && r.In(cl2.state.bounds) {
+ op.clipStack = append(op.clipStack[:k], op.clipStack[k+1:]...)
+ k--
+ op.clipStack[k].state.relTrans = p2.relTrans.Mul(op.clipStack[k].state.relTrans)
+ }
+ r = transformRect(p2.relTrans, r)
+ }
+ }
+ // Separate the integer offset from the first transform. Two ops that differ
+ // only in integer offsets may share backing storage.
+ if len(op.clipStack) > 0 {
+ c := &op.clipStack[len(op.clipStack)-1]
+ t := c.state.relTrans
+ t, off := separateTransform(t)
+ c.state.relTrans = t
+ op.offset = off
+ op.state.t = op.state.t.Offset(layout.FPt(off.Mul(-1)))
+ }
+ op.hash = c.hashOp(*op)
+ op.texOpIdx = -1
+ switch op.state.matType {
+ case materialTexture:
+ op.texOpIdx = len(*texOps)
+ // Separate integer offset from transformation. TextureOps that have identical transforms
+ // except for their integer offsets can share a transformed image.
+ t := op.state.t.Offset(layout.FPt(op.offset))
+ t, off := separateTransform(t)
+ bounds := boundRectF(op.intersect).Sub(off)
+ *texOps = append(*texOps, textureOp{
+ img: op.state.image,
+ off: off,
+ key: textureKey{
+ bounds: bounds,
+ transform: t,
+ handle: op.state.image.handle,
+ },
+ })
+ }
+ }
+}
+
+func (c *collector) hashOp(op paintOp) uint64 {
+ c.hasher.Reset()
+ for _, cl := range op.clipStack {
+ k := cl.state
+ keyBytes := (*[unsafe.Sizeof(k)]byte)(unsafe.Pointer(unsafe.Pointer(&k)))
+ c.hasher.Write(keyBytes[:])
+ }
+ k := op.state
+ keyBytes := (*[unsafe.Sizeof(k)]byte)(unsafe.Pointer(unsafe.Pointer(&k)))
+ c.hasher.Write(keyBytes[:])
+ return c.hasher.Sum64()
+}
+
+func (g *compute) layer(viewport image.Point, texOps []textureOp) {
+ // Sort ops from previous frames by hash.
+ c := &g.collector
+ prevOps := c.prevFrame.ops
+ c.order = c.order[:0]
+ for i, op := range prevOps {
+ c.order = append(c.order, hashIndex{
+ index: i,
+ hash: op.hash,
+ })
+ }
+ sort.Slice(c.order, func(i, j int) bool {
+ return c.order[i].hash < c.order[j].hash
+ })
+ // Split layers with different materials atlas; the compute stage has only
+ // one materials slot.
+ splitLayer := func(ops []paintOp, prevLayerIdx int) {
+ for len(ops) > 0 {
+ var materials *textureAtlas
+ idx := 0
+ for idx < len(ops) {
+ if i := ops[idx].texOpIdx; i != -1 {
+ omats := texOps[i].matAlloc.alloc.atlas
+ if materials != nil && omats != nil && omats != materials {
+ break
+ }
+ materials = omats
+ }
+ idx++
+ }
+ l := layer{ops: ops[:idx], materials: materials}
+ if prevLayerIdx != -1 {
+ prev := c.prevFrame.layers[prevLayerIdx]
+ if !prev.alloc.dead && len(prev.ops) == len(l.ops) {
+ l.alloc = prev.alloc
+ l.materials = prev.materials
+ g.touchAlloc(l.alloc)
+ }
+ }
+ for i, op := range l.ops {
+ l.rect = l.rect.Union(boundRectF(op.intersect))
+ l.ops[i].layer = len(c.frame.layers)
+ }
+ c.frame.layers = append(c.frame.layers, l)
+ ops = ops[idx:]
+ }
+ }
+ ops := c.frame.ops
+ idx := 0
+ for idx < len(ops) {
+ op := ops[idx]
+ // Search for longest matching op sequence.
+ // start is the earliest index of a match.
+ start := searchOp(c.order, op.hash)
+ layerOps, prevLayerIdx := longestLayer(prevOps, c.order[start:], ops[idx:])
+ if len(layerOps) == 0 {
+ idx++
+ continue
+ }
+ if unmatched := ops[:idx]; len(unmatched) > 0 {
+ // Flush layer of unmatched ops.
+ splitLayer(unmatched, -1)
+ ops = ops[idx:]
+ idx = 0
+ }
+ splitLayer(layerOps, prevLayerIdx)
+ ops = ops[len(layerOps):]
+ }
+ if len(ops) > 0 {
+ splitLayer(ops, -1)
+ }
+}
+
+func longestLayer(prev []paintOp, order []hashIndex, ops []paintOp) ([]paintOp, int) {
+ longest := 0
+ longestIdx := -1
+outer:
+ for len(order) > 0 {
+ first := order[0]
+ order = order[1:]
+ match := prev[first.index:]
+ // Potential match found. Now find longest matching sequence.
+ end := 0
+ layer := match[0].layer
+ off := match[0].offset.Sub(ops[0].offset)
+ for end < len(match) && end < len(ops) {
+ m := match[end]
+ o := ops[end]
+ // End layers on previous match.
+ if m.layer != layer {
+ break
+ }
+ // End layer when the next op doesn't match.
+ if m.hash != o.hash {
+ if end == 0 {
+ // Hashes are sorted so if the first op doesn't match, no
+ // more matches are possible.
+ break outer
+ }
+ break
+ }
+ if !opEqual(off, m, o) {
+ break
+ }
+ end++
+ }
+ if end > longest {
+ longest = end
+ longestIdx = layer
+
+ }
+ }
+ return ops[:longest], longestIdx
+}
+
+func searchOp(order []hashIndex, hash uint64) int {
+ lo, hi := 0, len(order)
+ for lo < hi {
+ mid := (lo + hi) / 2
+ if order[mid].hash < hash {
+ lo = mid + 1
+ } else {
+ hi = mid
+ }
+ }
+ return lo
+}
+
+func opEqual(off image.Point, o1 paintOp, o2 paintOp) bool {
+ if len(o1.clipStack) != len(o2.clipStack) {
+ return false
+ }
+ if o1.state != o2.state {
+ return false
+ }
+ if o1.offset.Sub(o2.offset) != off {
+ return false
+ }
+ for i, cl1 := range o1.clipStack {
+ cl2 := o2.clipStack[i]
+ if len(cl1.path) != len(cl2.path) {
+ return false
+ }
+ if cl1.state != cl2.state {
+ return false
+ }
+ if cl1.pathKey != cl2.pathKey && !bytes.Equal(cl1.path, cl2.path) {
+ return false
+ }
+ }
+ return true
+}
+
+func encodeLayer(l layer, pos image.Point, viewport image.Point, enc *encoder, texOps []textureOp) {
+ off := pos.Sub(l.rect.Min)
+ offf := layout.FPt(off)
+
+ enc.transform(f32.Affine2D{}.Offset(offf))
+ for _, op := range l.ops {
+ encodeOp(viewport, off, enc, texOps, op)
+ }
+ enc.transform(f32.Affine2D{}.Offset(offf.Mul(-1)))
+}
+
+func encodeOp(viewport image.Point, absOff image.Point, enc *encoder, texOps []textureOp, op paintOp) {
+ // Fill in clip bounds, which the shaders expect to be the union
+ // of all affected bounds.
+ var union f32.Rectangle
+ for i, cl := range op.clipStack {
+ union = union.Union(cl.absBounds)
+ op.clipStack[i].union = union
+ }
+
+ absOfff := layout.FPt(absOff)
+ fillMode := scene.FillModeNonzero
+ opOff := layout.FPt(op.offset)
+ inv := f32.Affine2D{}.Offset(opOff)
+ enc.transform(inv)
+ for i := len(op.clipStack) - 1; i >= 0; i-- {
+ cl := op.clipStack[i]
+ if w := cl.state.strokeWidth; w > 0 {
+ enc.fillMode(scene.FillModeStroke)
+ enc.lineWidth(w)
+ fillMode = scene.FillModeStroke
+ } else if fillMode != scene.FillModeNonzero {
+ enc.fillMode(scene.FillModeNonzero)
+ fillMode = scene.FillModeNonzero
+ }
+ enc.transform(cl.state.relTrans)
+ inv = inv.Mul(cl.state.relTrans)
+ if len(cl.path) == 0 {
+ enc.rect(cl.state.bounds)
+ } else {
+ enc.encodePath(cl.path, fillMode)
+ }
+ if i != 0 {
+ enc.beginClip(cl.union.Add(absOfff))
+ }
+ }
+ if len(op.clipStack) == 0 {
+ // No clipping; fill the entire view.
+ enc.rect(f32.Rectangle{Max: layout.FPt(viewport)})
+ }
+
+ switch op.state.matType {
+ case materialTexture:
+ texOp := texOps[op.texOpIdx]
+ off := texOp.matAlloc.alloc.rect.Min.Add(texOp.matAlloc.offset).Sub(texOp.off).Sub(absOff)
+ enc.fillImage(0, off)
+ case materialColor:
+ enc.fillColor(f32color.NRGBAToRGBA(op.state.color))
+ case materialLinearGradient:
+ // TODO: implement.
+ enc.fillColor(f32color.NRGBAToRGBA(op.state.color1))
+ default:
+ panic("not implemented")
+ }
+ enc.transform(inv.Invert())
+ // Pop the clip stack, except the first entry used for fill.
+ for i := 1; i < len(op.clipStack); i++ {
+ cl := op.clipStack[i]
+ enc.endClip(cl.union.Add(absOfff))
+ }
+ if fillMode != scene.FillModeNonzero {
+ enc.fillMode(scene.FillModeNonzero)
+ }
+}
+
+func (c *collector) save(id int, state f32.Affine2D) {
+ if extra := id - len(c.states) + 1; extra > 0 {
+ c.states = append(c.states, make([]f32.Affine2D, extra)...)
+ }
+ c.states[id] = state
+}
+
+func transformBounds(t f32.Affine2D, bounds f32.Rectangle) rectangle {
+ return rectangle{
+ t.Transform(bounds.Min), t.Transform(f32.Pt(bounds.Max.X, bounds.Min.Y)),
+ t.Transform(bounds.Max), t.Transform(f32.Pt(bounds.Min.X, bounds.Max.Y)),
+ }
+}
+
+func separateTransform(t f32.Affine2D) (f32.Affine2D, image.Point) {
+ sx, hx, ox, hy, sy, oy := t.Elems()
+ intx, fracx := math.Modf(float64(ox))
+ inty, fracy := math.Modf(float64(oy))
+ t = f32.NewAffine2D(sx, hx, float32(fracx), hy, sy, float32(fracy))
+ return t, image.Pt(int(intx), int(inty))
+}
+
+func transformRect(t f32.Affine2D, r rectangle) rectangle {
+ var tr rectangle
+ for i, c := range r {
+ tr[i] = t.Transform(c)
+ }
+ return tr
+}
+
+func (r rectangle) In(b f32.Rectangle) bool {
+ for _, c := range r {
+ inside := b.Min.X <= c.X && c.X <= b.Max.X &&
+ b.Min.Y <= c.Y && c.Y <= b.Max.Y
+ if !inside {
+ return false
+ }
+ }
+ return true
+}
+
+func (r rectangle) Contains(b f32.Rectangle) bool {
+ return true
+}
+
+func (r rectangle) Bounds() f32.Rectangle {
+ bounds := f32.Rectangle{
+ Min: f32.Pt(math.MaxFloat32, math.MaxFloat32),
+ Max: f32.Pt(-math.MaxFloat32, -math.MaxFloat32),
+ }
+ for _, c := range r {
+ if c.X < bounds.Min.X {
+ bounds.Min.X = c.X
+ }
+ if c.Y < bounds.Min.Y {
+ bounds.Min.Y = c.Y
+ }
+ if c.X > bounds.Max.X {
+ bounds.Max.X = c.X
+ }
+ if c.Y > bounds.Max.Y {
+ bounds.Max.Y = c.Y
+ }
+ }
+ return bounds
+}
diff --git a/vendor/gioui.org/gpu/cpu.go b/vendor/gioui.org/gpu/cpu.go
new file mode 100644
index 0000000..f2f84ad
--- /dev/null
+++ b/vendor/gioui.org/gpu/cpu.go
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package gpu
+
+import (
+ "unsafe"
+
+ "gioui.org/cpu"
+)
+
+// This file contains code specific to running compute shaders on the CPU.
+
+// dispatcher dispatches CPU compute programs across multiple goroutines.
+type dispatcher struct {
+ // done is notified when a worker completes its work slice.
+ done chan struct{}
+ // work receives work slice indices. It is closed when the dispatcher is released.
+ work chan work
+ // dispatch receives compute jobs, which is then split among workers.
+ dispatch chan dispatch
+ // sync receives notification when a Sync completes.
+ sync chan struct{}
+}
+
+type work struct {
+ ctx *cpu.DispatchContext
+ index int
+}
+
+type dispatch struct {
+ _type jobType
+ program *cpu.ProgramInfo
+ descSet unsafe.Pointer
+ x, y, z int
+}
+
+type jobType uint8
+
+const (
+ jobDispatch jobType = iota
+ jobBarrier
+ jobSync
+)
+
+func newDispatcher(workers int) *dispatcher {
+ d := &dispatcher{
+ work: make(chan work, workers),
+ done: make(chan struct{}, workers),
+ // Leave some room to avoid blocking calls to Dispatch.
+ dispatch: make(chan dispatch, 20),
+ sync: make(chan struct{}),
+ }
+ for i := 0; i < workers; i++ {
+ go d.worker()
+ }
+ go d.dispatcher()
+ return d
+}
+
+func (d *dispatcher) dispatcher() {
+ defer close(d.work)
+ var free []*cpu.DispatchContext
+ defer func() {
+ for _, ctx := range free {
+ ctx.Free()
+ }
+ }()
+ var used []*cpu.DispatchContext
+ for job := range d.dispatch {
+ switch job._type {
+ case jobDispatch:
+ if len(free) == 0 {
+ free = append(free, cpu.NewDispatchContext())
+ }
+ ctx := free[len(free)-1]
+ free = free[:len(free)-1]
+ used = append(used, ctx)
+ ctx.Prepare(cap(d.work), job.program, job.descSet, job.x, job.y, job.z)
+ for i := 0; i < cap(d.work); i++ {
+ d.work <- work{
+ ctx: ctx,
+ index: i,
+ }
+ }
+ case jobBarrier:
+ // Wait for all outstanding dispatches to complete.
+ for i := 0; i < len(used)*cap(d.work); i++ {
+ <-d.done
+ }
+ free = append(free, used...)
+ used = used[:0]
+ case jobSync:
+ d.sync <- struct{}{}
+ }
+ }
+}
+
+func (d *dispatcher) worker() {
+ thread := cpu.NewThreadContext()
+ defer thread.Free()
+ for w := range d.work {
+ w.ctx.Dispatch(w.index, thread)
+ d.done <- struct{}{}
+ }
+}
+
+func (d *dispatcher) Barrier() {
+ d.dispatch <- dispatch{_type: jobBarrier}
+}
+
+func (d *dispatcher) Sync() {
+ d.dispatch <- dispatch{_type: jobSync}
+ <-d.sync
+}
+
+func (d *dispatcher) Dispatch(program *cpu.ProgramInfo, descSet unsafe.Pointer, x, y, z int) {
+ d.dispatch <- dispatch{
+ _type: jobDispatch,
+ program: program,
+ descSet: descSet,
+ x: x,
+ y: y,
+ z: z,
+ }
+}
+
+func (d *dispatcher) Stop() {
+ close(d.dispatch)
+}
diff --git a/vendor/gioui.org/gpu/gen.go b/vendor/gioui.org/gpu/gen.go
deleted file mode 100644
index 709d0a7..0000000
--- a/vendor/gioui.org/gpu/gen.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package gpu
-
-//go:generate go run ../internal/cmd/convertshaders -package gpu
diff --git a/vendor/gioui.org/gpu/gl/backend.go b/vendor/gioui.org/gpu/gl/backend.go
deleted file mode 100644
index 02037c3..0000000
--- a/vendor/gioui.org/gpu/gl/backend.go
+++ /dev/null
@@ -1,835 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package gl
-
-import (
- "errors"
- "fmt"
- "image"
- "strings"
- "time"
- "unsafe"
-
- "gioui.org/gpu/backend"
-)
-
-// Backend implements backend.Device.
-type Backend struct {
- funcs Functions
-
- state glstate
-
- glver [2]int
- gles bool
- ubo bool
- feats backend.Caps
- // floatTriple holds the settings for floating point
- // textures.
- floatTriple textureTriple
- // Single channel alpha textures.
- alphaTriple textureTriple
- srgbaTriple textureTriple
-}
-
-// State tracking.
-type glstate struct {
- // nattr is the current number of enabled vertex arrays.
- nattr int
- prog *gpuProgram
- texUnits [2]*gpuTexture
- layout *gpuInputLayout
- buffer bufferBinding
-}
-
-type bufferBinding struct {
- buf *gpuBuffer
- offset int
- stride int
-}
-
-type gpuTimer struct {
- funcs Functions
- obj Query
-}
-
-type gpuTexture struct {
- backend *Backend
- obj Texture
- triple textureTriple
- width int
- height int
-}
-
-type gpuFramebuffer struct {
- backend *Backend
- obj Framebuffer
- hasDepth bool
- depthBuf Renderbuffer
- foreign bool
-}
-
-type gpuBuffer struct {
- backend *Backend
- hasBuffer bool
- obj Buffer
- typ backend.BufferBinding
- size int
- immutable bool
- version int
- // For emulation of uniform buffers.
- data []byte
-}
-
-type gpuProgram struct {
- backend *Backend
- obj Program
- nattr int
- vertUniforms uniformsTracker
- fragUniforms uniformsTracker
-}
-
-type uniformsTracker struct {
- locs []uniformLocation
- size int
- buf *gpuBuffer
- version int
-}
-
-type uniformLocation struct {
- uniform Uniform
- offset int
- typ backend.DataType
- size int
-}
-
-type gpuInputLayout struct {
- inputs []backend.InputLocation
- layout []backend.InputDesc
-}
-
-// textureTriple holds the type settings for
-// a TexImage2D call.
-type textureTriple struct {
- internalFormat int
- format Enum
- typ Enum
-}
-
-func NewBackend(f Functions) (*Backend, error) {
- exts := strings.Split(f.GetString(EXTENSIONS), " ")
- glVer := f.GetString(VERSION)
- ver, gles, err := ParseGLVersion(glVer)
- if err != nil {
- return nil, err
- }
- floatTriple, err := floatTripleFor(f, ver, exts)
- if err != nil {
- return nil, err
- }
- srgbaTriple, err := srgbaTripleFor(ver, exts)
- if err != nil {
- return nil, err
- }
- ubo := ver[0] >= 3 && gles
- b := &Backend{
- glver: ver,
- gles: gles,
- ubo: ubo,
- funcs: f,
- floatTriple: floatTriple,
- alphaTriple: alphaTripleFor(ver),
- srgbaTriple: srgbaTriple,
- }
- if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") {
- b.feats.Features |= backend.FeatureTimers
- }
- b.feats.MaxTextureSize = f.GetInteger(MAX_TEXTURE_SIZE)
- return b, nil
-}
-
-func (b *Backend) BeginFrame() {
- // Assume GL state is reset between frames.
- b.state = glstate{}
-}
-
-func (b *Backend) EndFrame() {
- b.funcs.ActiveTexture(TEXTURE0)
-}
-
-func (b *Backend) Caps() backend.Caps {
- return b.feats
-}
-
-func (b *Backend) NewTimer() backend.Timer {
- return &gpuTimer{
- funcs: b.funcs,
- obj: b.funcs.CreateQuery(),
- }
-}
-
-func (b *Backend) IsTimeContinuous() bool {
- return b.funcs.GetInteger(GPU_DISJOINT_EXT) == FALSE
-}
-
-func (b *Backend) NewFramebuffer(tex backend.Texture, depthBits int) (backend.Framebuffer, error) {
- glErr(b.funcs)
- gltex := tex.(*gpuTexture)
- fb := b.funcs.CreateFramebuffer()
- fbo := &gpuFramebuffer{backend: b, obj: fb}
- b.BindFramebuffer(fbo)
- if err := glErr(b.funcs); err != nil {
- fbo.Release()
- return nil, err
- }
- b.funcs.FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, gltex.obj, 0)
- if depthBits > 0 {
- size := Enum(DEPTH_COMPONENT16)
- switch {
- case depthBits > 24:
- size = DEPTH_COMPONENT32F
- case depthBits > 16:
- size = DEPTH_COMPONENT24
- }
- depthBuf := b.funcs.CreateRenderbuffer()
- b.funcs.BindRenderbuffer(RENDERBUFFER, depthBuf)
- b.funcs.RenderbufferStorage(RENDERBUFFER, size, gltex.width, gltex.height)
- b.funcs.FramebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, depthBuf)
- fbo.depthBuf = depthBuf
- fbo.hasDepth = true
- if err := glErr(b.funcs); err != nil {
- fbo.Release()
- return nil, err
- }
- }
- if st := b.funcs.CheckFramebufferStatus(FRAMEBUFFER); st != FRAMEBUFFER_COMPLETE {
- fbo.Release()
- return nil, fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError())
- }
- return fbo, nil
-}
-
-func (b *Backend) CurrentFramebuffer() backend.Framebuffer {
- fboID := Framebuffer(b.funcs.GetBinding(FRAMEBUFFER_BINDING))
- return &gpuFramebuffer{backend: b, obj: fboID, foreign: true}
-}
-
-func (b *Backend) NewTexture(format backend.TextureFormat, width, height int, minFilter, magFilter backend.TextureFilter, binding backend.BufferBinding) (backend.Texture, error) {
- glErr(b.funcs)
- tex := &gpuTexture{backend: b, obj: b.funcs.CreateTexture(), width: width, height: height}
- switch format {
- case backend.TextureFormatFloat:
- tex.triple = b.floatTriple
- case backend.TextureFormatSRGB:
- tex.triple = b.srgbaTriple
- default:
- return nil, errors.New("unsupported texture format")
- }
- b.BindTexture(0, tex)
- b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, toTexFilter(magFilter))
- b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, toTexFilter(minFilter))
- b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE)
- b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE)
- b.funcs.TexImage2D(TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ, nil)
- if err := glErr(b.funcs); err != nil {
- tex.Release()
- return nil, err
- }
- return tex, nil
-}
-
-func (b *Backend) NewBuffer(typ backend.BufferBinding, size int) (backend.Buffer, error) {
- glErr(b.funcs)
- buf := &gpuBuffer{backend: b, typ: typ, size: size}
- if typ&backend.BufferBindingUniforms != 0 {
- if typ != backend.BufferBindingUniforms {
- return nil, errors.New("uniforms buffers cannot be bound as anything else")
- }
- if !b.ubo {
- // GLES 2 doesn't support uniform buffers.
- buf.data = make([]byte, size)
- }
- }
- if typ&^backend.BufferBindingUniforms != 0 || b.ubo {
- buf.hasBuffer = true
- buf.obj = b.funcs.CreateBuffer()
- if err := glErr(b.funcs); err != nil {
- buf.Release()
- return nil, err
- }
- }
- return buf, nil
-}
-
-func (b *Backend) NewImmutableBuffer(typ backend.BufferBinding, data []byte) (backend.Buffer, error) {
- glErr(b.funcs)
- obj := b.funcs.CreateBuffer()
- buf := &gpuBuffer{backend: b, obj: obj, typ: typ, size: len(data), hasBuffer: true}
- buf.Upload(data)
- buf.immutable = true
- if err := glErr(b.funcs); err != nil {
- buf.Release()
- return nil, err
- }
- return buf, nil
-}
-
-func glErr(f Functions) error {
- if st := f.GetError(); st != NO_ERROR {
- return fmt.Errorf("glGetError: %#x", st)
- }
- return nil
-}
-
-func (b *Backend) bindTexture(unit int, t *gpuTexture) {
- if b.state.texUnits[unit] != t {
- b.funcs.ActiveTexture(TEXTURE0 + Enum(unit))
- b.funcs.BindTexture(TEXTURE_2D, t.obj)
- b.state.texUnits[unit] = t
- }
-}
-
-func (b *Backend) useProgram(p *gpuProgram) {
- if b.state.prog != p {
- p.backend.funcs.UseProgram(p.obj)
- b.state.prog = p
- }
-}
-
-func (b *Backend) enableVertexArrays(n int) {
- // Enable needed arrays.
- for i := b.state.nattr; i < n; i++ {
- b.funcs.EnableVertexAttribArray(Attrib(i))
- }
- // Disable extra arrays.
- for i := n; i < b.state.nattr; i++ {
- b.funcs.DisableVertexAttribArray(Attrib(i))
- }
- b.state.nattr = n
-}
-
-func (b *Backend) SetDepthTest(enable bool) {
- if enable {
- b.funcs.Enable(DEPTH_TEST)
- } else {
- b.funcs.Disable(DEPTH_TEST)
- }
-}
-
-func (b *Backend) BlendFunc(sfactor, dfactor backend.BlendFactor) {
- b.funcs.BlendFunc(toGLBlendFactor(sfactor), toGLBlendFactor(dfactor))
-}
-
-func toGLBlendFactor(f backend.BlendFactor) Enum {
- switch f {
- case backend.BlendFactorOne:
- return ONE
- case backend.BlendFactorOneMinusSrcAlpha:
- return ONE_MINUS_SRC_ALPHA
- case backend.BlendFactorZero:
- return ZERO
- case backend.BlendFactorDstColor:
- return DST_COLOR
- default:
- panic("unsupported blend factor")
- }
-}
-
-func (b *Backend) DepthMask(mask bool) {
- b.funcs.DepthMask(mask)
-}
-
-func (b *Backend) SetBlend(enable bool) {
- if enable {
- b.funcs.Enable(BLEND)
- } else {
- b.funcs.Disable(BLEND)
- }
-}
-
-func (b *Backend) DrawElements(mode backend.DrawMode, off, count int) {
- b.prepareDraw()
- // off is in 16-bit indices, but DrawElements take a byte offset.
- byteOff := off * 2
- b.funcs.DrawElements(toGLDrawMode(mode), count, UNSIGNED_SHORT, byteOff)
-}
-
-func (b *Backend) DrawArrays(mode backend.DrawMode, off, count int) {
- b.prepareDraw()
- b.funcs.DrawArrays(toGLDrawMode(mode), off, count)
-}
-
-func (b *Backend) prepareDraw() {
- nattr := b.state.prog.nattr
- b.enableVertexArrays(nattr)
- if nattr > 0 {
- b.setupVertexArrays()
- }
- if p := b.state.prog; p != nil {
- p.updateUniforms()
- }
-}
-
-func toGLDrawMode(mode backend.DrawMode) Enum {
- switch mode {
- case backend.DrawModeTriangleStrip:
- return TRIANGLE_STRIP
- case backend.DrawModeTriangles:
- return TRIANGLES
- default:
- panic("unsupported draw mode")
- }
-}
-
-func (b *Backend) Viewport(x, y, width, height int) {
- b.funcs.Viewport(x, y, width, height)
-}
-
-func (b *Backend) Clear(colR, colG, colB, colA float32) {
- b.funcs.ClearColor(colR, colG, colB, colA)
- b.funcs.Clear(COLOR_BUFFER_BIT)
-}
-
-func (b *Backend) ClearDepth(d float32) {
- b.funcs.ClearDepthf(d)
- b.funcs.Clear(DEPTH_BUFFER_BIT)
-}
-
-func (b *Backend) DepthFunc(f backend.DepthFunc) {
- var glfunc Enum
- switch f {
- case backend.DepthFuncGreater:
- glfunc = GREATER
- case backend.DepthFuncGreaterEqual:
- glfunc = GEQUAL
- default:
- panic("unsupported depth func")
- }
- b.funcs.DepthFunc(glfunc)
-}
-
-func (b *Backend) NewInputLayout(vs backend.ShaderSources, layout []backend.InputDesc) (backend.InputLayout, error) {
- if len(vs.Inputs) != len(layout) {
- return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vs.Inputs))
- }
- for i, inp := range vs.Inputs {
- if exp, got := inp.Size, layout[i].Size; exp != got {
- return nil, fmt.Errorf("NewInputLayout: data size mismatch for %q: got %d expected %d", inp.Name, got, exp)
- }
- }
- return &gpuInputLayout{
- inputs: vs.Inputs,
- layout: layout,
- }, nil
-}
-
-func (b *Backend) NewProgram(vertShader, fragShader backend.ShaderSources) (backend.Program, error) {
- attr := make([]string, len(vertShader.Inputs))
- for _, inp := range vertShader.Inputs {
- attr[inp.Location] = inp.Name
- }
- vsrc, fsrc := vertShader.GLSL100ES, fragShader.GLSL100ES
- if b.glver[0] >= 3 {
- // OpenGL (ES) 3.0.
- switch {
- case b.gles:
- vsrc, fsrc = vertShader.GLSL300ES, fragShader.GLSL300ES
- case b.glver[0] >= 4 || b.glver[1] >= 2:
- // OpenGL 3.2 Core only accepts glsl 1.50 or newer.
- vsrc, fsrc = vertShader.GLSL150, fragShader.GLSL150
- default:
- vsrc, fsrc = vertShader.GLSL130, fragShader.GLSL130
- }
- }
- p, err := CreateProgram(b.funcs, vsrc, fsrc, attr)
- if err != nil {
- return nil, err
- }
- gpuProg := &gpuProgram{
- backend: b,
- obj: p,
- nattr: len(attr),
- }
- b.BindProgram(gpuProg)
- // Bind texture uniforms.
- for _, tex := range vertShader.Textures {
- u := b.funcs.GetUniformLocation(p, tex.Name)
- if u.valid() {
- b.funcs.Uniform1i(u, tex.Binding)
- }
- }
- for _, tex := range fragShader.Textures {
- u := b.funcs.GetUniformLocation(p, tex.Name)
- if u.valid() {
- b.funcs.Uniform1i(u, tex.Binding)
- }
- }
- if b.ubo {
- for _, block := range vertShader.Uniforms.Blocks {
- blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name)
- if blockIdx != INVALID_INDEX {
- b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding))
- }
- }
- // To match Direct3D 11 with separate vertex and fragment
- // shader uniform buffers, offset all fragment blocks to be
- // located after the vertex blocks.
- off := len(vertShader.Uniforms.Blocks)
- for _, block := range fragShader.Uniforms.Blocks {
- blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name)
- if blockIdx != INVALID_INDEX {
- b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding+off))
- }
- }
- } else {
- gpuProg.vertUniforms.setup(b.funcs, p, vertShader.Uniforms.Size, vertShader.Uniforms.Locations)
- gpuProg.fragUniforms.setup(b.funcs, p, fragShader.Uniforms.Size, fragShader.Uniforms.Locations)
- }
- return gpuProg, nil
-}
-
-func lookupUniform(funcs Functions, p Program, loc backend.UniformLocation) uniformLocation {
- u := GetUniformLocation(funcs, p, loc.Name)
- return uniformLocation{uniform: u, offset: loc.Offset, typ: loc.Type, size: loc.Size}
-}
-
-func (p *gpuProgram) SetVertexUniforms(buffer backend.Buffer) {
- p.vertUniforms.setBuffer(buffer)
-}
-
-func (p *gpuProgram) SetFragmentUniforms(buffer backend.Buffer) {
- p.fragUniforms.setBuffer(buffer)
-}
-
-func (p *gpuProgram) updateUniforms() {
- f := p.backend.funcs
- if p.backend.ubo {
- if b := p.vertUniforms.buf; b != nil {
- f.BindBufferBase(UNIFORM_BUFFER, 0, b.obj)
- }
- if b := p.fragUniforms.buf; b != nil {
- f.BindBufferBase(UNIFORM_BUFFER, 1, b.obj)
- }
- } else {
- p.vertUniforms.update(f)
- p.fragUniforms.update(f)
- }
-}
-
-func (b *Backend) BindProgram(prog backend.Program) {
- p := prog.(*gpuProgram)
- b.useProgram(p)
-}
-
-func (p *gpuProgram) Release() {
- p.backend.funcs.DeleteProgram(p.obj)
-}
-
-func (u *uniformsTracker) setup(funcs Functions, p Program, uniformSize int, uniforms []backend.UniformLocation) {
- u.locs = make([]uniformLocation, len(uniforms))
- for i, uniform := range uniforms {
- u.locs[i] = lookupUniform(funcs, p, uniform)
- }
- u.size = uniformSize
-}
-
-func (u *uniformsTracker) setBuffer(buffer backend.Buffer) {
- buf := buffer.(*gpuBuffer)
- if buf.typ&backend.BufferBindingUniforms == 0 {
- panic("not a uniform buffer")
- }
- if buf.size < u.size {
- panic(fmt.Errorf("uniform buffer too small, got %d need %d", buf.size, u.size))
- }
- u.buf = buf
- // Force update.
- u.version = buf.version - 1
-}
-
-func (p *uniformsTracker) update(funcs Functions) {
- b := p.buf
- if b == nil || b.version == p.version {
- return
- }
- p.version = b.version
- data := b.data
- for _, u := range p.locs {
- data := data[u.offset:]
- switch {
- case u.typ == backend.DataTypeFloat && u.size == 1:
- data := data[:4]
- v := *(*[1]float32)(unsafe.Pointer(&data[0]))
- funcs.Uniform1f(u.uniform, v[0])
- case u.typ == backend.DataTypeFloat && u.size == 2:
- data := data[:8]
- v := *(*[2]float32)(unsafe.Pointer(&data[0]))
- funcs.Uniform2f(u.uniform, v[0], v[1])
- case u.typ == backend.DataTypeFloat && u.size == 3:
- data := data[:12]
- v := *(*[3]float32)(unsafe.Pointer(&data[0]))
- funcs.Uniform3f(u.uniform, v[0], v[1], v[2])
- case u.typ == backend.DataTypeFloat && u.size == 4:
- data := data[:16]
- v := *(*[4]float32)(unsafe.Pointer(&data[0]))
- funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3])
- default:
- panic("unsupported uniform data type or size")
- }
- }
-}
-
-func (b *gpuBuffer) Upload(data []byte) {
- if b.immutable {
- panic("immutable buffer")
- }
- if len(data) > b.size {
- panic("buffer size overflow")
- }
- b.version++
- copy(b.data, data)
- if b.hasBuffer {
- firstBinding := firstBufferType(b.typ)
- b.backend.funcs.BindBuffer(firstBinding, b.obj)
- b.backend.funcs.BufferData(firstBinding, data, STATIC_DRAW)
- }
-}
-
-func (b *gpuBuffer) Release() {
- if b.hasBuffer {
- b.backend.funcs.DeleteBuffer(b.obj)
- b.hasBuffer = false
- }
-}
-
-func (b *Backend) BindVertexBuffer(buf backend.Buffer, stride, offset int) {
- gbuf := buf.(*gpuBuffer)
- if gbuf.typ&backend.BufferBindingVertices == 0 {
- panic("not a vertex buffer")
- }
- b.state.buffer = bufferBinding{buf: gbuf, stride: stride, offset: offset}
-}
-
-func (b *Backend) setupVertexArrays() {
- layout := b.state.layout
- if layout == nil {
- return
- }
- buf := b.state.buffer
- b.funcs.BindBuffer(ARRAY_BUFFER, buf.buf.obj)
- for i, inp := range layout.inputs {
- l := layout.layout[i]
- var gltyp Enum
- switch l.Type {
- case backend.DataTypeFloat:
- gltyp = FLOAT
- case backend.DataTypeShort:
- gltyp = SHORT
- default:
- panic("unsupported data type")
- }
- b.funcs.VertexAttribPointer(Attrib(inp.Location), l.Size, gltyp, false, buf.stride, buf.offset+l.Offset)
- }
-}
-
-func (b *Backend) BindIndexBuffer(buf backend.Buffer) {
- gbuf := buf.(*gpuBuffer)
- if gbuf.typ&backend.BufferBindingIndices == 0 {
- panic("not an index buffer")
- }
- b.funcs.BindBuffer(ELEMENT_ARRAY_BUFFER, gbuf.obj)
-}
-
-func (f *gpuFramebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
- glErr(f.backend.funcs)
- f.backend.BindFramebuffer(f)
- if len(pixels) < src.Dx()*src.Dy() {
- return errors.New("unexpected RGBA size")
- }
- f.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, src.Dx(), src.Dy(), RGBA, UNSIGNED_BYTE, pixels)
- // OpenGL origin is in the lower-left corner. Flip the image to
- // match.
- flipImageY(src.Dx()*4, src.Dy(), pixels)
- return glErr(f.backend.funcs)
-}
-
-func flipImageY(stride int, height int, pixels []byte) {
- // Flip image in y-direction. OpenGL's origin is in the lower
- // left corner.
- row := make([]uint8, stride)
- for y := 0; y < height/2; y++ {
- y1 := height - y - 1
- dest := y1 * stride
- src := y * stride
- copy(row, pixels[dest:])
- copy(pixels[dest:], pixels[src:src+len(row)])
- copy(pixels[src:], row)
- }
-}
-
-func (b *Backend) BindFramebuffer(fbo backend.Framebuffer) {
- b.funcs.BindFramebuffer(FRAMEBUFFER, fbo.(*gpuFramebuffer).obj)
-}
-
-func (f *gpuFramebuffer) Invalidate() {
- f.backend.BindFramebuffer(f)
- f.backend.funcs.InvalidateFramebuffer(FRAMEBUFFER, COLOR_ATTACHMENT0)
-}
-
-func (f *gpuFramebuffer) Release() {
- if f.foreign {
- panic("cannot release framebuffer created by CurrentFramebuffer")
- }
- f.backend.funcs.DeleteFramebuffer(f.obj)
- if f.hasDepth {
- f.backend.funcs.DeleteRenderbuffer(f.depthBuf)
- }
-}
-
-func toTexFilter(f backend.TextureFilter) int {
- switch f {
- case backend.FilterNearest:
- return NEAREST
- case backend.FilterLinear:
- return LINEAR
- default:
- panic("unsupported texture filter")
- }
-}
-
-func (b *Backend) BindTexture(unit int, t backend.Texture) {
- b.bindTexture(unit, t.(*gpuTexture))
-}
-
-func (t *gpuTexture) Release() {
- t.backend.funcs.DeleteTexture(t.obj)
-}
-
-func (t *gpuTexture) Upload(img *image.RGBA) {
- t.backend.BindTexture(0, t)
- var pixels []byte
- b := img.Bounds()
- w, h := b.Dx(), b.Dy()
- if img.Stride != w*4 {
- panic("unsupported stride")
- }
- start := (b.Min.X + b.Min.Y*w) * 4
- end := (b.Max.X + (b.Max.Y-1)*w) * 4
- pixels = img.Pix[start:end]
- t.backend.funcs.TexImage2D(TEXTURE_2D, 0, t.triple.internalFormat, w, h, t.triple.format, t.triple.typ, pixels)
-}
-
-func (t *gpuTimer) Begin() {
- t.funcs.BeginQuery(TIME_ELAPSED_EXT, t.obj)
-}
-
-func (t *gpuTimer) End() {
- t.funcs.EndQuery(TIME_ELAPSED_EXT)
-}
-
-func (t *gpuTimer) ready() bool {
- return t.funcs.GetQueryObjectuiv(t.obj, QUERY_RESULT_AVAILABLE) == TRUE
-}
-
-func (t *gpuTimer) Release() {
- t.funcs.DeleteQuery(t.obj)
-}
-
-func (t *gpuTimer) Duration() (time.Duration, bool) {
- if !t.ready() {
- return 0, false
- }
- nanos := t.funcs.GetQueryObjectuiv(t.obj, QUERY_RESULT)
- return time.Duration(nanos), true
-}
-
-func (b *Backend) BindInputLayout(l backend.InputLayout) {
- b.state.layout = l.(*gpuInputLayout)
-}
-
-func (l *gpuInputLayout) Release() {}
-
-// floatTripleFor determines the best texture triple for floating point FBOs.
-func floatTripleFor(f Functions, ver [2]int, exts []string) (textureTriple, error) {
- var triples []textureTriple
- if ver[0] >= 3 {
- triples = append(triples, textureTriple{R16F, Enum(RED), Enum(HALF_FLOAT)})
- }
- // According to the OES_texture_half_float specification, EXT_color_buffer_half_float is needed to
- // render to FBOs. However, the Safari WebGL1 implementation does support half-float FBOs but does not
- // report EXT_color_buffer_half_float support. The triples are verified below, so it doesn't matter if we're
- // wrong.
- if hasExtension(exts, "GL_OES_texture_half_float") || hasExtension(exts, "GL_EXT_color_buffer_half_float") {
- // Try single channel.
- triples = append(triples, textureTriple{LUMINANCE, Enum(LUMINANCE), Enum(HALF_FLOAT_OES)})
- // Fallback to 4 channels.
- triples = append(triples, textureTriple{RGBA, Enum(RGBA), Enum(HALF_FLOAT_OES)})
- }
- if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") {
- triples = append(triples, textureTriple{RGBA, Enum(RGBA), Enum(FLOAT)})
- }
- tex := f.CreateTexture()
- defer f.DeleteTexture(tex)
- f.BindTexture(TEXTURE_2D, tex)
- f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE)
- f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE)
- f.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST)
- f.TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST)
- fbo := f.CreateFramebuffer()
- defer f.DeleteFramebuffer(fbo)
- defFBO := Framebuffer(f.GetBinding(FRAMEBUFFER_BINDING))
- f.BindFramebuffer(FRAMEBUFFER, fbo)
- defer f.BindFramebuffer(FRAMEBUFFER, defFBO)
- var attempts []string
- for _, tt := range triples {
- const size = 256
- f.TexImage2D(TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ, nil)
- f.FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, tex, 0)
- st := f.CheckFramebufferStatus(FRAMEBUFFER)
- if st == FRAMEBUFFER_COMPLETE {
- return tt, nil
- }
- attempts = append(attempts, fmt.Sprintf("(0x%x, 0x%x, 0x%x): 0x%x", tt.internalFormat, tt.format, tt.typ, st))
- }
- return textureTriple{}, fmt.Errorf("floating point fbos not supported (attempted %s)", attempts)
-}
-
-func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) {
- switch {
- case ver[0] >= 3:
- return textureTriple{SRGB8_ALPHA8, Enum(RGBA), Enum(UNSIGNED_BYTE)}, nil
- case hasExtension(exts, "GL_EXT_sRGB"):
- return textureTriple{SRGB_ALPHA_EXT, Enum(SRGB_ALPHA_EXT), Enum(UNSIGNED_BYTE)}, nil
- default:
- return textureTriple{}, errors.New("no sRGB texture formats found")
- }
-}
-
-func alphaTripleFor(ver [2]int) textureTriple {
- intf, f := R8, Enum(RED)
- if ver[0] < 3 {
- // R8, RED not supported on OpenGL ES 2.0.
- intf, f = LUMINANCE, Enum(LUMINANCE)
- }
- return textureTriple{intf, f, UNSIGNED_BYTE}
-}
-
-func hasExtension(exts []string, ext string) bool {
- for _, e := range exts {
- if ext == e {
- return true
- }
- }
- return false
-}
-
-func firstBufferType(typ backend.BufferBinding) Enum {
- switch {
- case typ&backend.BufferBindingIndices != 0:
- return ELEMENT_ARRAY_BUFFER
- case typ&backend.BufferBindingVertices != 0:
- return ARRAY_BUFFER
- case typ&backend.BufferBindingUniforms != 0:
- return UNIFORM_BUFFER
- default:
- panic("unsupported buffer type")
- }
-}
diff --git a/vendor/gioui.org/gpu/gl/gl.go b/vendor/gioui.org/gpu/gl/gl.go
deleted file mode 100644
index 350f60f..0000000
--- a/vendor/gioui.org/gpu/gl/gl.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package gl
-
-type (
- Attrib uint
- Enum uint
-)
-
-const (
- ARRAY_BUFFER = 0x8892
- BLEND = 0xbe2
- CLAMP_TO_EDGE = 0x812f
- COLOR_ATTACHMENT0 = 0x8ce0
- COLOR_BUFFER_BIT = 0x4000
- COMPILE_STATUS = 0x8b81
- DEPTH_BUFFER_BIT = 0x100
- DEPTH_ATTACHMENT = 0x8d00
- DEPTH_COMPONENT16 = 0x81a5
- DEPTH_COMPONENT24 = 0x81A6
- DEPTH_COMPONENT32F = 0x8CAC
- DEPTH_TEST = 0xb71
- DST_COLOR = 0x306
- ELEMENT_ARRAY_BUFFER = 0x8893
- EXTENSIONS = 0x1f03
- FALSE = 0
- FLOAT = 0x1406
- FRAGMENT_SHADER = 0x8b30
- FRAMEBUFFER = 0x8d40
- FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 0x8210
- FRAMEBUFFER_BINDING = 0x8ca6
- FRAMEBUFFER_COMPLETE = 0x8cd5
- HALF_FLOAT = 0x140b
- HALF_FLOAT_OES = 0x8d61
- INFO_LOG_LENGTH = 0x8B84
- INVALID_INDEX = ^uint(0)
- GREATER = 0x204
- GEQUAL = 0x206
- LINEAR = 0x2601
- LINK_STATUS = 0x8b82
- LUMINANCE = 0x1909
- MAX_TEXTURE_SIZE = 0xd33
- NEAREST = 0x2600
- NO_ERROR = 0x0
- NUM_EXTENSIONS = 0x821D
- ONE = 0x1
- ONE_MINUS_SRC_ALPHA = 0x303
- QUERY_RESULT = 0x8866
- QUERY_RESULT_AVAILABLE = 0x8867
- R16F = 0x822d
- R8 = 0x8229
- READ_FRAMEBUFFER = 0x8ca8
- RED = 0x1903
- RENDERER = 0x1F01
- RENDERBUFFER = 0x8d41
- RENDERBUFFER_BINDING = 0x8ca7
- RENDERBUFFER_HEIGHT = 0x8d43
- RENDERBUFFER_WIDTH = 0x8d42
- RGB = 0x1907
- RGBA = 0x1908
- RGBA8 = 0x8058
- SHORT = 0x1402
- SRGB = 0x8c40
- SRGB_ALPHA_EXT = 0x8c42
- SRGB8 = 0x8c41
- SRGB8_ALPHA8 = 0x8c43
- STATIC_DRAW = 0x88e4
- TEXTURE_2D = 0xde1
- TEXTURE_MAG_FILTER = 0x2800
- TEXTURE_MIN_FILTER = 0x2801
- TEXTURE_WRAP_S = 0x2802
- TEXTURE_WRAP_T = 0x2803
- TEXTURE0 = 0x84c0
- TEXTURE1 = 0x84c1
- TRIANGLE_STRIP = 0x5
- TRIANGLES = 0x4
- TRUE = 1
- UNIFORM_BUFFER = 0x8A11
- UNPACK_ALIGNMENT = 0xcf5
- UNSIGNED_BYTE = 0x1401
- UNSIGNED_SHORT = 0x1403
- VERSION = 0x1f02
- VERTEX_SHADER = 0x8b31
- ZERO = 0x0
-
- // EXT_disjoint_timer_query
- TIME_ELAPSED_EXT = 0x88BF
- GPU_DISJOINT_EXT = 0x8FBB
-)
-
-type Functions interface {
- ActiveTexture(texture Enum)
- AttachShader(p Program, s Shader)
- BeginQuery(target Enum, query Query)
- BindAttribLocation(p Program, a Attrib, name string)
- BindBuffer(target Enum, b Buffer)
- BindBufferBase(target Enum, index int, buffer Buffer)
- BindFramebuffer(target Enum, fb Framebuffer)
- BindRenderbuffer(target Enum, fb Renderbuffer)
- BindTexture(target Enum, t Texture)
- BlendEquation(mode Enum)
- BlendFunc(sfactor, dfactor Enum)
- BufferData(target Enum, src []byte, usage Enum)
- CheckFramebufferStatus(target Enum) Enum
- Clear(mask Enum)
- ClearColor(red, green, blue, alpha float32)
- ClearDepthf(d float32)
- CompileShader(s Shader)
- CreateBuffer() Buffer
- CreateFramebuffer() Framebuffer
- CreateProgram() Program
- CreateQuery() Query
- CreateRenderbuffer() Renderbuffer
- CreateShader(ty Enum) Shader
- CreateTexture() Texture
- DeleteBuffer(v Buffer)
- DeleteFramebuffer(v Framebuffer)
- DeleteProgram(p Program)
- DeleteQuery(query Query)
- DeleteRenderbuffer(r Renderbuffer)
- DeleteShader(s Shader)
- DeleteTexture(v Texture)
- DepthFunc(f Enum)
- DepthMask(mask bool)
- DisableVertexAttribArray(a Attrib)
- Disable(cap Enum)
- DrawArrays(mode Enum, first, count int)
- DrawElements(mode Enum, count int, ty Enum, offset int)
- Enable(cap Enum)
- EnableVertexAttribArray(a Attrib)
- EndQuery(target Enum)
- FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int)
- FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer)
- GetBinding(pname Enum) Object
- GetError() Enum
- GetInteger(pname Enum) int
- GetProgrami(p Program, pname Enum) int
- GetProgramInfoLog(p Program) string
- GetQueryObjectuiv(query Query, pname Enum) uint
- GetShaderi(s Shader, pname Enum) int
- GetShaderInfoLog(s Shader) string
- GetString(pname Enum) string
- GetUniformBlockIndex(p Program, name string) uint
- GetUniformLocation(p Program, name string) Uniform
- InvalidateFramebuffer(target, attachment Enum)
- LinkProgram(p Program)
- ReadPixels(x, y, width, height int, format, ty Enum, data []byte)
- RenderbufferStorage(target, internalformat Enum, width, height int)
- ShaderSource(s Shader, src string)
- TexImage2D(target Enum, level int, internalFormat int, width, height int, format, ty Enum, data []byte)
- TexParameteri(target, pname Enum, param int)
- UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint)
- Uniform1f(dst Uniform, v float32)
- Uniform1i(dst Uniform, v int)
- Uniform2f(dst Uniform, v0, v1 float32)
- Uniform3f(dst Uniform, v0, v1, v2 float32)
- Uniform4f(dst Uniform, v0, v1, v2, v3 float32)
- UseProgram(p Program)
- VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int)
- Viewport(x, y, width, height int)
-}
diff --git a/vendor/gioui.org/gpu/gl/types.go b/vendor/gioui.org/gpu/gl/types.go
deleted file mode 100644
index f986bba..0000000
--- a/vendor/gioui.org/gpu/gl/types.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// +build !js
-
-package gl
-
-type (
- Buffer struct{ V uint }
- Framebuffer struct{ V uint }
- Program struct{ V uint }
- Renderbuffer struct{ V uint }
- Shader struct{ V uint }
- Texture struct{ V uint }
- Query struct{ V uint }
- Uniform struct{ V int }
- Object struct{ V uint }
-)
-
-func (u Uniform) valid() bool {
- return u.V != -1
-}
-
-func (p Program) valid() bool {
- return p.V != 0
-}
-
-func (s Shader) valid() bool {
- return s.V != 0
-}
diff --git a/vendor/gioui.org/gpu/gl/types_js.go b/vendor/gioui.org/gpu/gl/types_js.go
deleted file mode 100644
index 16e3a71..0000000
--- a/vendor/gioui.org/gpu/gl/types_js.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package gl
-
-import "syscall/js"
-
-type (
- Buffer js.Value
- Framebuffer js.Value
- Program js.Value
- Renderbuffer js.Value
- Shader js.Value
- Texture js.Value
- Query js.Value
- Uniform js.Value
- Object js.Value
-)
-
-func (p Program) valid() bool {
- return !js.Value(p).IsUndefined() && !js.Value(p).IsNull()
-}
-
-func (s Shader) valid() bool {
- return !js.Value(s).IsUndefined() && !js.Value(s).IsNull()
-}
-
-func (u Uniform) valid() bool {
- return !js.Value(u).IsUndefined() && !js.Value(u).IsNull()
-}
diff --git a/vendor/gioui.org/gpu/gl/util.go b/vendor/gioui.org/gpu/gl/util.go
deleted file mode 100644
index 65b2155..0000000
--- a/vendor/gioui.org/gpu/gl/util.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: Unlicense OR MIT
-
-package gl
-
-import (
- "errors"
- "fmt"
- "strings"
-)
-
-func CreateProgram(ctx Functions, vsSrc, fsSrc string, attribs []string) (Program, error) {
- vs, err := createShader(ctx, VERTEX_SHADER, vsSrc)
- if err != nil {
- return Program{}, err
- }
- defer ctx.DeleteShader(vs)
- fs, err := createShader(ctx, FRAGMENT_SHADER, fsSrc)
- if err != nil {
- return Program{}, err
- }
- defer ctx.DeleteShader(fs)
- prog := ctx.CreateProgram()
- if !prog.valid() {
- return Program{}, errors.New("glCreateProgram failed")
- }
- ctx.AttachShader(prog, vs)
- ctx.AttachShader(prog, fs)
- for i, a := range attribs {
- ctx.BindAttribLocation(prog, Attrib(i), a)
- }
- ctx.LinkProgram(prog)
- if ctx.GetProgrami(prog, LINK_STATUS) == 0 {
- log := ctx.GetProgramInfoLog(prog)
- ctx.DeleteProgram(prog)
- return Program{}, fmt.Errorf("program link failed: %s", strings.TrimSpace(log))
- }
- return prog, nil
-}
-
-func GetUniformLocation(ctx Functions, prog Program, name string) Uniform {
- loc := ctx.GetUniformLocation(prog, name)
- if !loc.valid() {
- panic(fmt.Errorf("uniform %s not found", name))
- }
- return loc
-}
-
-func createShader(ctx Functions, typ Enum, src string) (Shader, error) {
- sh := ctx.CreateShader(typ)
- if !sh.valid() {
- return Shader{}, errors.New("glCreateShader failed")
- }
- ctx.ShaderSource(sh, src)
- ctx.CompileShader(sh)
- if ctx.GetShaderi(sh, COMPILE_STATUS) == 0 {
- log := ctx.GetShaderInfoLog(sh)
- ctx.DeleteShader(sh)
- return Shader{}, fmt.Errorf("shader compilation failed: %s", strings.TrimSpace(log))
- }
- return sh, nil
-}
-
-func ParseGLVersion(glVer string) (version [2]int, gles bool, err error) {
- var ver [2]int
- if _, err := fmt.Sscanf(glVer, "OpenGL ES %d.%d", &ver[0], &ver[1]); err == nil {
- return ver, true, nil
- } else if _, err := fmt.Sscanf(glVer, "WebGL %d.%d", &ver[0], &ver[1]); err == nil {
- // WebGL major version v corresponds to OpenGL ES version v + 1
- ver[0]++
- return ver, true, nil
- } else if _, err := fmt.Sscanf(glVer, "%d.%d", &ver[0], &ver[1]); err == nil {
- return ver, false, nil
- }
- return ver, false, fmt.Errorf("failed to parse OpenGL ES version (%s)", glVer)
-}
diff --git a/vendor/gioui.org/gpu/gpu.go b/vendor/gioui.org/gpu/gpu.go
index 321b2a8..0bd15c6 100644
--- a/vendor/gioui.org/gpu/gpu.go
+++ b/vendor/gioui.org/gpu/gpu.go
@@ -13,36 +13,57 @@ import (
"image"
"image/color"
"math"
+ "os"
"reflect"
"time"
"unsafe"
"gioui.org/f32"
- "gioui.org/gpu/backend"
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/byteslice"
"gioui.org/internal/f32color"
- "gioui.org/internal/opconst"
"gioui.org/internal/ops"
- gunsafe "gioui.org/internal/unsafe"
+ "gioui.org/internal/scene"
+ "gioui.org/internal/stroke"
"gioui.org/layout"
"gioui.org/op"
- "gioui.org/op/paint"
+ "gioui.org/shader"
+ "gioui.org/shader/gio"
+
+ // Register backends.
+ _ "gioui.org/gpu/internal/d3d11"
+ _ "gioui.org/gpu/internal/metal"
+ _ "gioui.org/gpu/internal/opengl"
+ _ "gioui.org/gpu/internal/vulkan"
)
-type GPU struct {
+type GPU interface {
+ // Release non-Go resources. The GPU is no longer valid after Release.
+ Release()
+ // Clear sets the clear color for the next Frame.
+ Clear(color color.NRGBA)
+ // Frame draws the graphics operations from op into a viewport of target.
+ Frame(frame *op.Ops, target RenderTarget, viewport image.Point) error
+ // Profile returns the last available profiling information. Profiling
+ // information is requested when Frame sees an io/profile.Op, and the result
+ // is available through Profile at some later time.
+ Profile() string
+}
+
+type gpu struct {
cache *resourceCache
- defFBO backend.Framebuffer
- profile string
- timers *timers
- frameStart time.Time
- zopsTimer, stencilTimer, coverTimer, cleanupTimer *timer
- drawOps drawOps
- ctx backend.Device
- renderer *renderer
+ profile string
+ timers *timers
+ frameStart time.Time
+ stencilTimer, coverTimer, cleanupTimer *timer
+ drawOps drawOps
+ ctx driver.Device
+ renderer *renderer
}
type renderer struct {
- ctx backend.Device
+ ctx driver.Device
blitter *blitter
pather *pather
packer packer
@@ -50,17 +71,15 @@ type renderer struct {
}
type drawOps struct {
- profile bool
- reader ops.Reader
- cache *resourceCache
- vertCache []byte
- viewport image.Point
- clearColor f32color.RGBA
- imageOps []imageOp
- // zimageOps are the rectangle clipped opaque images
- // that can use fast front-to-back rendering with z-test
- // and no blending.
- zimageOps []imageOp
+ profile bool
+ reader ops.Reader
+ states []f32.Affine2D
+ transStack []f32.Affine2D
+ vertCache []byte
+ viewport image.Point
+ clear bool
+ clearColor f32color.RGBA
+ imageOps []imageOp
pathOps []*pathOp
pathOpCache []pathOp
qs quadSplitter
@@ -68,26 +87,35 @@ type drawOps struct {
}
type drawState struct {
- clip f32.Rectangle
t f32.Affine2D
cpath *pathOp
- rect bool
- z int
matType materialType
// Current paint.ImageOp
image imageOpData
// Current paint.ColorOp, if any.
- color color.RGBA
+ color color.NRGBA
+
+ // Current paint.LinearGradientOp.
+ stop1 f32.Point
+ stop2 f32.Point
+ color1 color.NRGBA
+ color2 color.NRGBA
}
type pathOp struct {
off f32.Point
+ // rect tracks whether the clip stack can be represented by a
+ // pixel-aligned rectangle.
+ rect bool
// clip is the union of all
// later clip rectangles.
- clip image.Rectangle
- bounds f32.Rectangle
- pathKey ops.Key
+ clip image.Rectangle
+ bounds f32.Rectangle
+ // intersect is the intersection of bounds and all
+ // previous clip bounds.
+ intersect f32.Rectangle
+ pathKey opKey
path bool
pathVerts []byte
parent *pathOp
@@ -95,87 +123,70 @@ type pathOp struct {
}
type imageOp struct {
- z float32
path *pathOp
- off f32.Point
clip image.Rectangle
material material
clipType clipType
place placement
}
+func decodeStrokeOp(data []byte) float32 {
+ _ = data[4]
+ bo := binary.LittleEndian
+ return math.Float32frombits(bo.Uint32(data[1:]))
+}
+
+type quadsOp struct {
+ key opKey
+ aux []byte
+}
+
+type opKey struct {
+ outline bool
+ strokeWidth float32
+ sx, hx, sy, hy float32
+ ops.Key
+}
+
type material struct {
material materialType
opaque bool
// For materialTypeColor.
color f32color.RGBA
+ // For materialTypeLinearGradient.
+ color1 f32color.RGBA
+ color2 f32color.RGBA
// For materialTypeTexture.
- texture *texture
+ data imageOpData
uvTrans f32.Affine2D
}
-// clipOp is the shadow of clip.Op.
-type clipOp struct {
- bounds f32.Rectangle
-}
-
// imageOpData is the shadow of paint.ImageOp.
type imageOpData struct {
- rect image.Rectangle
src *image.RGBA
handle interface{}
}
-func (op *clipOp) decode(data []byte) {
- if opconst.OpType(data[0]) != opconst.TypeClip {
- panic("invalid op")
- }
- bo := binary.LittleEndian
- r := f32.Rectangle{
- Min: f32.Point{
- X: math.Float32frombits(bo.Uint32(data[1:])),
- Y: math.Float32frombits(bo.Uint32(data[5:])),
- },
- Max: f32.Point{
- X: math.Float32frombits(bo.Uint32(data[9:])),
- Y: math.Float32frombits(bo.Uint32(data[13:])),
- },
- }
- *op = clipOp{
- bounds: r,
- }
+type linearGradientOpData struct {
+ stop1 f32.Point
+ color1 color.NRGBA
+ stop2 f32.Point
+ color2 color.NRGBA
}
func decodeImageOp(data []byte, refs []interface{}) imageOpData {
- if opconst.OpType(data[0]) != opconst.TypeImage {
- panic("invalid op")
- }
handle := refs[1]
if handle == nil {
return imageOpData{}
}
- bo := binary.LittleEndian
return imageOpData{
- rect: image.Rectangle{
- Min: image.Point{
- X: int(bo.Uint32(data[1:])),
- Y: int(bo.Uint32(data[5:])),
- },
- Max: image.Point{
- X: int(bo.Uint32(data[9:])),
- Y: int(bo.Uint32(data[13:])),
- },
- },
src: refs[0].(*image.RGBA),
handle: handle,
}
}
-func decodeColorOp(data []byte) color.RGBA {
- if opconst.OpType(data[0]) != opconst.TypeColor {
- panic("invalid op")
- }
- return color.RGBA{
+func decodeColorOp(data []byte) color.NRGBA {
+ return color.NRGBA{
R: data[1],
G: data[2],
B: data[3],
@@ -183,23 +194,29 @@ func decodeColorOp(data []byte) color.RGBA {
}
}
-func decodePaintOp(data []byte) paint.PaintOp {
+func decodeLinearGradientOp(data []byte) linearGradientOpData {
bo := binary.LittleEndian
- if opconst.OpType(data[0]) != opconst.TypePaint {
- panic("invalid op")
- }
- r := f32.Rectangle{
- Min: f32.Point{
+ return linearGradientOpData{
+ stop1: f32.Point{
X: math.Float32frombits(bo.Uint32(data[1:])),
Y: math.Float32frombits(bo.Uint32(data[5:])),
},
- Max: f32.Point{
+ stop2: f32.Point{
X: math.Float32frombits(bo.Uint32(data[9:])),
Y: math.Float32frombits(bo.Uint32(data[13:])),
},
- }
- return paint.PaintOp{
- Rect: r,
+ color1: color.NRGBA{
+ R: data[17+0],
+ G: data[17+1],
+ B: data[17+2],
+ A: data[17+3],
+ },
+ color2: color.NRGBA{
+ R: data[21+0],
+ G: data[21+1],
+ B: data[21+2],
+ A: data[21+3],
+ },
}
}
@@ -211,58 +228,60 @@ type resource interface {
type texture struct {
src *image.RGBA
- tex backend.Texture
+ tex driver.Texture
}
type blitter struct {
- ctx backend.Device
- viewport image.Point
- prog [2]*program
- layout backend.InputLayout
- colUniforms *blitColUniforms
- texUniforms *blitTexUniforms
- quadVerts backend.Buffer
+ ctx driver.Device
+ viewport image.Point
+ pipelines [3]*pipeline
+ colUniforms *blitColUniforms
+ texUniforms *blitTexUniforms
+ linearGradientUniforms *blitLinearGradientUniforms
+ quadVerts driver.Buffer
}
type blitColUniforms struct {
- vert struct {
- blitUniforms
- _ [12]byte // Padding to a multiple of 16.
- }
- frag struct {
- colorUniforms
- }
+ blitUniforms
+ _ [128 - unsafe.Sizeof(blitUniforms{}) - unsafe.Sizeof(colorUniforms{})]byte // Padding to 128 bytes.
+ colorUniforms
}
type blitTexUniforms struct {
- vert struct {
- blitUniforms
- _ [12]byte // Padding to a multiple of 16.
- }
+ blitUniforms
+}
+
+type blitLinearGradientUniforms struct {
+ blitUniforms
+ _ [128 - unsafe.Sizeof(blitUniforms{}) - unsafe.Sizeof(gradientUniforms{})]byte // Padding to 128 bytes.
+ gradientUniforms
}
type uniformBuffer struct {
- buf backend.Buffer
+ buf driver.Buffer
ptr []byte
}
-type program struct {
- prog backend.Program
- vertUniforms *uniformBuffer
- fragUniforms *uniformBuffer
+type pipeline struct {
+ pipeline driver.Pipeline
+ uniforms *uniformBuffer
}
type blitUniforms struct {
transform [4]float32
uvTransformR1 [4]float32
uvTransformR2 [4]float32
- z float32
}
type colorUniforms struct {
color f32color.RGBA
}
+type gradientUniforms struct {
+ color1 f32color.RGBA
+ color2 f32color.RGBA
+}
+
type materialType uint8
const (
@@ -273,14 +292,37 @@ const (
const (
materialColor materialType = iota
+ materialLinearGradient
materialTexture
)
-func New(ctx backend.Device) (*GPU, error) {
- defFBO := ctx.CurrentFramebuffer()
- g := &GPU{
- defFBO: defFBO,
- cache: newResourceCache(),
+// New creates a GPU for the given API.
+func New(api API) (GPU, error) {
+ d, err := driver.NewDevice(api)
+ if err != nil {
+ return nil, err
+ }
+ return NewWithDevice(d)
+}
+
+// NewWithDevice creates a GPU with a pre-existing device.
+//
+// Note: for internal use only.
+func NewWithDevice(d driver.Device) (GPU, error) {
+ d.BeginFrame(nil, false, image.Point{})
+ defer d.EndFrame()
+ forceCompute := os.Getenv("GIORENDERER") == "forcecompute"
+ feats := d.Caps().Features
+ switch {
+ case !forceCompute && feats.Has(driver.FeatureFloatRenderTargets) && feats.Has(driver.FeatureSRGB):
+ return newGPU(d)
+ }
+ return newCompute(d)
+}
+
+func newGPU(ctx driver.Device) (*gpu, error) {
+ g := &gpu{
+ cache: newResourceCache(),
}
g.drawOps.pathCache = newOpCache()
if err := g.init(ctx); err != nil {
@@ -289,111 +331,116 @@ func New(ctx backend.Device) (*GPU, error) {
return g, nil
}
-func (g *GPU) init(ctx backend.Device) error {
+func (g *gpu) init(ctx driver.Device) error {
g.ctx = ctx
g.renderer = newRenderer(ctx)
return nil
}
-func (g *GPU) Release() {
+func (g *gpu) Clear(col color.NRGBA) {
+ g.drawOps.clear = true
+ g.drawOps.clearColor = f32color.LinearFromSRGB(col)
+}
+
+func (g *gpu) Release() {
g.renderer.release()
g.drawOps.pathCache.release()
g.cache.release()
if g.timers != nil {
- g.timers.release()
+ g.timers.Release()
}
+ g.ctx.Release()
+}
+
+func (g *gpu) Frame(frameOps *op.Ops, target RenderTarget, viewport image.Point) error {
+ g.collect(viewport, frameOps)
+ return g.frame(target)
}
-func (g *GPU) Collect(viewport image.Point, frameOps *op.Ops) {
+func (g *gpu) collect(viewport image.Point, frameOps *op.Ops) {
g.renderer.blitter.viewport = viewport
g.renderer.pather.viewport = viewport
- g.drawOps.reset(g.cache, viewport)
- g.drawOps.collect(g.cache, frameOps, viewport)
+ g.drawOps.reset(viewport)
+ g.drawOps.collect(frameOps, viewport)
g.frameStart = time.Now()
- if g.drawOps.profile && g.timers == nil && g.ctx.Caps().Features.Has(backend.FeatureTimers) {
+ if g.drawOps.profile && g.timers == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) {
g.timers = newTimers(g.ctx)
- g.zopsTimer = g.timers.newTimer()
g.stencilTimer = g.timers.newTimer()
g.coverTimer = g.timers.newTimer()
g.cleanupTimer = g.timers.newTimer()
}
- for _, p := range g.drawOps.pathOps {
- if v, exists := g.drawOps.pathCache.get(p.pathKey); !exists || v.data.data == nil {
- data := buildPath(g.ctx, p.pathVerts)
- g.drawOps.pathCache.put(p.pathKey, opCacheValue{
- data: data,
- bounds: p.bounds,
- })
- }
- p.pathVerts = nil
- }
}
-func (g *GPU) BeginFrame() {
- g.ctx.BeginFrame()
- defer g.ctx.EndFrame()
+func (g *gpu) frame(target RenderTarget) error {
viewport := g.renderer.blitter.viewport
+ defFBO := g.ctx.BeginFrame(target, g.drawOps.clear, viewport)
+ defer g.ctx.EndFrame()
+ g.drawOps.buildPaths(g.ctx)
for _, img := range g.drawOps.imageOps {
expandPathOp(img.path, img.clip)
}
- if g.drawOps.profile {
- g.zopsTimer.begin()
- }
- g.ctx.BindFramebuffer(g.defFBO)
- g.ctx.DepthFunc(backend.DepthFuncGreater)
- g.ctx.ClearDepth(0.0)
- g.ctx.Clear(g.drawOps.clearColor.Float32())
- g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
- g.renderer.drawZOps(g.drawOps.zimageOps)
- g.zopsTimer.end()
g.stencilTimer.begin()
- g.ctx.SetBlend(true)
g.renderer.packStencils(&g.drawOps.pathOps)
g.renderer.stencilClips(g.drawOps.pathCache, g.drawOps.pathOps)
g.renderer.packIntersections(g.drawOps.imageOps)
+ g.renderer.prepareIntersections(g.drawOps.imageOps)
g.renderer.intersect(g.drawOps.imageOps)
g.stencilTimer.end()
g.coverTimer.begin()
- g.ctx.BindFramebuffer(g.defFBO)
+ g.renderer.uploadImages(g.cache, g.drawOps.imageOps)
+ g.renderer.prepareDrawOps(g.cache, g.drawOps.imageOps)
+ d := driver.LoadDesc{
+ ClearColor: g.drawOps.clearColor,
+ }
+ if g.drawOps.clear {
+ g.drawOps.clear = false
+ d.Action = driver.LoadActionClear
+ }
+ g.ctx.BeginRenderPass(defFBO, d)
g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
- g.renderer.drawOps(g.drawOps.imageOps)
- g.ctx.SetBlend(false)
- g.renderer.pather.stenciler.invalidateFBO()
+ g.renderer.drawOps(g.cache, g.drawOps.imageOps)
g.coverTimer.end()
- g.ctx.BindFramebuffer(g.defFBO)
-}
-
-func (g *GPU) EndFrame() {
+ g.ctx.EndRenderPass()
g.cleanupTimer.begin()
g.cache.frame()
g.drawOps.pathCache.frame()
g.cleanupTimer.end()
if g.drawOps.profile && g.timers.ready() {
- zt, st, covt, cleant := g.zopsTimer.Elapsed, g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed
- ft := zt + st + covt + cleant
+ st, covt, cleant := g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed
+ ft := st + covt + cleant
q := 100 * time.Microsecond
- zt, st, covt = zt.Round(q), st.Round(q), covt.Round(q)
+ st, covt = st.Round(q), covt.Round(q)
frameDur := time.Since(g.frameStart).Round(q)
ft = ft.Round(q)
- g.profile = fmt.Sprintf("draw:%7s gpu:%7s zt:%7s st:%7s cov:%7s", frameDur, ft, zt, st, covt)
+ g.profile = fmt.Sprintf("draw:%7s gpu:%7s st:%7s cov:%7s", frameDur, ft, st, covt)
}
+ return nil
}
-func (g *GPU) Profile() string {
+func (g *gpu) Profile() string {
return g.profile
}
-func (r *renderer) texHandle(t *texture) backend.Texture {
- if t.tex != nil {
- return t.tex
+func (r *renderer) texHandle(cache *resourceCache, data imageOpData) driver.Texture {
+ var tex *texture
+ t, exists := cache.get(data.handle)
+ if !exists {
+ t = &texture{
+ src: data.src,
+ }
+ cache.put(data.handle, t)
}
- tex, err := r.ctx.NewTexture(backend.TextureFormatSRGB, t.src.Bounds().Dx(), t.src.Bounds().Dy(), backend.FilterLinear, backend.FilterLinear, backend.BufferBindingTexture)
+ tex = t.(*texture)
+ if tex.tex != nil {
+ return tex.tex
+ }
+ handle, err := r.ctx.NewTexture(driver.TextureFormatSRGBA, data.src.Bounds().Dx(), data.src.Bounds().Dy(), driver.FilterLinear, driver.FilterLinear, driver.BufferBindingTexture)
if err != nil {
panic(err)
}
- tex.Upload(t.src)
- t.tex = tex
- return t.tex
+ driver.UploadImage(handle, image.Pt(0, 0), data.src)
+ tex.tex = handle
+ return tex.tex
}
func (t *texture) release() {
@@ -402,14 +449,22 @@ func (t *texture) release() {
}
}
-func newRenderer(ctx backend.Device) *renderer {
+func newRenderer(ctx driver.Device) *renderer {
r := &renderer{
ctx: ctx,
blitter: newBlitter(ctx),
pather: newPather(ctx),
}
- r.packer.maxDim = ctx.Caps().MaxTextureSize
- r.intersections.maxDim = r.packer.maxDim
+
+ maxDim := ctx.Caps().MaxTextureSize
+ // Large atlas textures cause artifacts due to precision loss in
+ // shaders.
+ if cap := 8192; maxDim > cap {
+ maxDim = cap
+ }
+
+ r.packer.maxDims = image.Pt(maxDim, maxDim)
+ r.intersections.maxDims = image.Pt(maxDim, maxDim)
return r
}
@@ -418,13 +473,13 @@ func (r *renderer) release() {
r.blitter.release()
}
-func newBlitter(ctx backend.Device) *blitter {
- quadVerts, err := ctx.NewImmutableBuffer(backend.BufferBindingVertices,
- gunsafe.BytesView([]float32{
- -1, +1, 0, 0,
- +1, +1, 1, 0,
- -1, -1, 0, 1,
- +1, -1, 1, 1,
+func newBlitter(ctx driver.Device) *blitter {
+ quadVerts, err := ctx.NewImmutableBuffer(driver.BufferBindingVertices,
+ byteslice.Slice([]float32{
+ -1, -1, 0, 0,
+ +1, -1, 1, 0,
+ -1, +1, 0, 1,
+ +1, +1, 1, 1,
}),
)
if err != nil {
@@ -436,65 +491,125 @@ func newBlitter(ctx backend.Device) *blitter {
}
b.colUniforms = new(blitColUniforms)
b.texUniforms = new(blitTexUniforms)
- prog, layout, err := createColorPrograms(ctx, shader_blit_vert, shader_blit_frag,
- [2]interface{}{&b.colUniforms.vert, &b.texUniforms.vert}, [2]interface{}{&b.colUniforms.frag, nil})
+ b.linearGradientUniforms = new(blitLinearGradientUniforms)
+ pipelines, err := createColorPrograms(ctx, gio.Shader_blit_vert, gio.Shader_blit_frag,
+ [3]interface{}{b.colUniforms, b.linearGradientUniforms, b.texUniforms},
+ )
if err != nil {
panic(err)
}
- b.prog = prog
- b.layout = layout
+ b.pipelines = pipelines
return b
}
func (b *blitter) release() {
b.quadVerts.Release()
- for _, p := range b.prog {
+ for _, p := range b.pipelines {
p.Release()
}
- b.layout.Release()
}
-func createColorPrograms(b backend.Device, vsSrc backend.ShaderSources, fsSrc [2]backend.ShaderSources, vertUniforms, fragUniforms [2]interface{}) ([2]*program, backend.InputLayout, error) {
- var progs [2]*program
- prog, err := b.NewProgram(vsSrc, fsSrc[materialTexture])
- if err != nil {
- return progs, nil, err
+func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.Sources, uniforms [3]interface{}) ([3]*pipeline, error) {
+ var pipelines [3]*pipeline
+ blend := driver.BlendDesc{
+ Enable: true,
+ SrcFactor: driver.BlendFactorOne,
+ DstFactor: driver.BlendFactorOneMinusSrcAlpha,
}
- var vertBuffer *uniformBuffer
- if u := vertUniforms[materialTexture]; u != nil {
- vertBuffer = newUniformBuffer(b, u)
- prog.SetVertexUniforms(vertBuffer.buf)
- }
- var fragBuffer *uniformBuffer
- if u := fragUniforms[materialTexture]; u != nil {
- fragBuffer = newUniformBuffer(b, u)
- prog.SetFragmentUniforms(fragBuffer.buf)
+ layout := driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 0},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
+ },
+ Stride: 4 * 4,
}
- progs[materialTexture] = newProgram(prog, vertBuffer, fragBuffer)
- prog, err = b.NewProgram(vsSrc, fsSrc[materialColor])
+ vsh, err := b.NewVertexShader(vsSrc)
if err != nil {
- progs[materialTexture].Release()
- return progs, nil, err
- }
- if u := vertUniforms[materialColor]; u != nil {
- vertBuffer = newUniformBuffer(b, u)
- prog.SetVertexUniforms(vertBuffer.buf)
- }
- if u := fragUniforms[materialColor]; u != nil {
- fragBuffer = newUniformBuffer(b, u)
- prog.SetFragmentUniforms(fragBuffer.buf)
- }
- progs[materialColor] = newProgram(prog, vertBuffer, fragBuffer)
- layout, err := b.NewInputLayout(vsSrc, []backend.InputDesc{
- {Type: backend.DataTypeFloat, Size: 2, Offset: 0},
- {Type: backend.DataTypeFloat, Size: 2, Offset: 4 * 2},
- })
+ return pipelines, err
+ }
+ defer vsh.Release()
+ {
+ fsh, err := b.NewFragmentShader(fsSrc[materialTexture])
+ if err != nil {
+ return pipelines, err
+ }
+ defer fsh.Release()
+ pipe, err := b.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ BlendDesc: blend,
+ VertexLayout: layout,
+ PixelFormat: driver.TextureFormatOutput,
+ Topology: driver.TopologyTriangleStrip,
+ })
+ if err != nil {
+ return pipelines, err
+ }
+ var vertBuffer *uniformBuffer
+ if u := uniforms[materialTexture]; u != nil {
+ vertBuffer = newUniformBuffer(b, u)
+ }
+ pipelines[materialTexture] = &pipeline{pipe, vertBuffer}
+ }
+ {
+ var vertBuffer *uniformBuffer
+ fsh, err := b.NewFragmentShader(fsSrc[materialColor])
+ if err != nil {
+ pipelines[materialTexture].Release()
+ return pipelines, err
+ }
+ defer fsh.Release()
+ pipe, err := b.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ BlendDesc: blend,
+ VertexLayout: layout,
+ PixelFormat: driver.TextureFormatOutput,
+ Topology: driver.TopologyTriangleStrip,
+ })
+ if err != nil {
+ pipelines[materialTexture].Release()
+ return pipelines, err
+ }
+ if u := uniforms[materialColor]; u != nil {
+ vertBuffer = newUniformBuffer(b, u)
+ }
+ pipelines[materialColor] = &pipeline{pipe, vertBuffer}
+ }
+ {
+ var vertBuffer *uniformBuffer
+ fsh, err := b.NewFragmentShader(fsSrc[materialLinearGradient])
+ if err != nil {
+ pipelines[materialTexture].Release()
+ pipelines[materialColor].Release()
+ return pipelines, err
+ }
+ defer fsh.Release()
+ pipe, err := b.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ BlendDesc: blend,
+ VertexLayout: layout,
+ PixelFormat: driver.TextureFormatOutput,
+ Topology: driver.TopologyTriangleStrip,
+ })
+ if err != nil {
+ pipelines[materialTexture].Release()
+ pipelines[materialColor].Release()
+ return pipelines, err
+ }
+ if u := uniforms[materialLinearGradient]; u != nil {
+ vertBuffer = newUniformBuffer(b, u)
+ }
+ pipelines[materialLinearGradient] = &pipeline{pipe, vertBuffer}
+ }
if err != nil {
- progs[materialTexture].Release()
- progs[materialColor].Release()
- return progs, nil, err
+ for _, p := range pipelines {
+ p.Release()
+ }
+ return pipelines, err
}
- return progs, layout, nil
+ return pipelines, nil
}
func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
@@ -505,14 +620,31 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
r.pather.begin(r.packer.sizes)
for _, p := range ops {
if fbo != p.place.Idx {
+ if fbo != -1 {
+ r.ctx.EndRenderPass()
+ }
fbo = p.place.Idx
f := r.pather.stenciler.cover(fbo)
- r.ctx.BindFramebuffer(f.fbo)
- r.ctx.Clear(0.0, 0.0, 0.0, 0.0)
+ r.ctx.BeginRenderPass(f.tex, driver.LoadDesc{Action: driver.LoadActionClear})
+ r.ctx.BindPipeline(r.pather.stenciler.pipeline.pipeline.pipeline)
+ r.ctx.BindIndexBuffer(r.pather.stenciler.indexBuf)
}
v, _ := pathCache.get(p.pathKey)
r.pather.stencilPath(p.clip, p.off, p.place.Pos, v.data)
}
+ if fbo != -1 {
+ r.ctx.EndRenderPass()
+ }
+}
+
+func (r *renderer) prepareIntersections(ops []imageOp) {
+ for _, img := range ops {
+ if img.clipType != clipTypeIntersection {
+ continue
+ }
+ fbo := r.pather.stenciler.cover(img.path.place.Idx)
+ r.ctx.PrepareTexture(fbo.tex)
+ }
}
func (r *renderer) intersect(ops []imageOp) {
@@ -521,21 +653,28 @@ func (r *renderer) intersect(ops []imageOp) {
}
fbo := -1
r.pather.stenciler.beginIntersect(r.intersections.sizes)
- r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0)
- r.ctx.BindInputLayout(r.pather.stenciler.iprog.layout)
for _, img := range ops {
if img.clipType != clipTypeIntersection {
continue
}
if fbo != img.place.Idx {
+ if fbo != -1 {
+ r.ctx.EndRenderPass()
+ }
fbo = img.place.Idx
f := r.pather.stenciler.intersections.fbos[fbo]
- r.ctx.BindFramebuffer(f.fbo)
- r.ctx.Clear(1.0, 0.0, 0.0, 0.0)
+ d := driver.LoadDesc{Action: driver.LoadActionClear}
+ d.ClearColor.R = 1.0
+ r.ctx.BeginRenderPass(f.tex, d)
+ r.ctx.BindPipeline(r.pather.stenciler.ipipeline.pipeline.pipeline)
+ r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
}
r.ctx.Viewport(img.place.Pos.X, img.place.Pos.Y, img.clip.Dx(), img.clip.Dy())
r.intersectPath(img.path, img.clip)
}
+ if fbo != -1 {
+ r.ctx.EndRenderPass()
+ }
}
func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) {
@@ -556,12 +695,12 @@ func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) {
}
fbo := r.pather.stenciler.cover(p.place.Idx)
r.ctx.BindTexture(0, fbo.tex)
- coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size)
- subScale, subOff := texSpaceTransform(toRectF(sub), p.clip.Size())
- r.pather.stenciler.iprog.uniforms.vert.uvTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
- r.pather.stenciler.iprog.uniforms.vert.subUVTransform = [4]float32{subScale.X, subScale.Y, subOff.X, subOff.Y}
- r.pather.stenciler.iprog.prog.UploadUniforms()
- r.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4)
+ coverScale, coverOff := texSpaceTransform(layout.FRect(uv), fbo.size)
+ subScale, subOff := texSpaceTransform(layout.FRect(sub), p.clip.Size())
+ r.pather.stenciler.ipipeline.uniforms.vert.uvTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
+ r.pather.stenciler.ipipeline.uniforms.vert.subUVTransform = [4]float32{subScale.X, subScale.Y, subOff.X, subOff.Y}
+ r.pather.stenciler.ipipeline.pipeline.UploadUniforms(r.ctx)
+ r.ctx.DrawArrays(0, 4)
}
func (r *renderer) packIntersections(ops []imageOp) {
@@ -611,7 +750,7 @@ func (r *renderer) packStencils(pops *[]*pathOp) {
if !ok {
// The clip area is at most the entire screen. Hopefully no
// screen is larger than GL_MAX_TEXTURE_SIZE.
- panic(fmt.Errorf("clip area %v is larger than maximum texture size %dx%d", p.clip, r.packer.maxDim, r.packer.maxDim))
+ panic(fmt.Errorf("clip area %v is larger than maximum texture size %v", p.clip, r.packer.maxDims))
}
p.place = place
i++
@@ -619,8 +758,7 @@ func (r *renderer) packStencils(pops *[]*pathOp) {
*pops = ops
}
-// intersects intersects clip and b where b is offset by off.
-// ceilRect returns a bounding image.Rectangle for a f32.Rectangle.
+// boundRectF returns a bounding image.Rectangle for a f32.Rectangle.
func boundRectF(r f32.Rectangle) image.Rectangle {
return image.Rectangle{
Min: image.Point{
@@ -634,19 +772,6 @@ func boundRectF(r f32.Rectangle) image.Rectangle {
}
}
-func toRectF(r image.Rectangle) f32.Rectangle {
- return f32.Rectangle{
- Min: f32.Point{
- X: float32(r.Min.X),
- Y: float32(r.Min.Y),
- },
- Max: f32.Point{
- X: float32(r.Max.X),
- Y: float32(r.Max.Y),
- },
- }
-}
-
func ceil(v float32) int {
return int(math.Ceil(float64(v)))
}
@@ -655,30 +780,39 @@ func floor(v float32) int {
return int(math.Floor(float64(v)))
}
-func (d *drawOps) reset(cache *resourceCache, viewport image.Point) {
+func (d *drawOps) reset(viewport image.Point) {
d.profile = false
- d.clearColor = f32color.RGBA{R: 1.0, G: 1.0, B: 1.0, A: 1.0}
- d.cache = cache
d.viewport = viewport
d.imageOps = d.imageOps[:0]
- d.zimageOps = d.zimageOps[:0]
d.pathOps = d.pathOps[:0]
d.pathOpCache = d.pathOpCache[:0]
d.vertCache = d.vertCache[:0]
+ d.transStack = d.transStack[:0]
}
-func (d *drawOps) collect(cache *resourceCache, root *op.Ops, viewport image.Point) {
- d.reset(cache, viewport)
- clip := f32.Rectangle{
+func (d *drawOps) collect(root *op.Ops, viewport image.Point) {
+ viewf := f32.Rectangle{
Max: f32.Point{X: float32(viewport.X), Y: float32(viewport.Y)},
}
- d.reader.Reset(root)
- state := drawState{
- clip: clip,
- rect: true,
- color: color.RGBA{A: 0xff},
+ var ops *ops.Ops
+ if root != nil {
+ ops = &root.Internal
+ }
+ d.reader.Reset(ops)
+ d.collectOps(&d.reader, viewf)
+}
+
+func (d *drawOps) buildPaths(ctx driver.Device) {
+ for _, p := range d.pathOps {
+ if v, exists := d.pathCache.get(p.pathKey); !exists || v.data.data == nil {
+ data := buildPath(ctx, p.pathVerts)
+ d.pathCache.put(p.pathKey, opCacheValue{
+ data: data,
+ bounds: p.bounds,
+ })
+ }
+ p.pathVerts = nil
}
- d.collectOps(&d.reader, state)
}
func (d *drawOps) newPathOp() *pathOp {
@@ -686,24 +820,30 @@ func (d *drawOps) newPathOp() *pathOp {
return &d.pathOpCache[len(d.pathOpCache)-1]
}
-func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey ops.Key, bounds f32.Rectangle, off f32.Point) {
+func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds f32.Rectangle, off f32.Point, push bool) {
npath := d.newPathOp()
*npath = pathOp{
- parent: state.cpath,
- bounds: bounds,
- off: off,
+ parent: state.cpath,
+ bounds: bounds,
+ off: off,
+ intersect: bounds.Add(off),
+ rect: true,
+ }
+ if npath.parent != nil {
+ npath.rect = npath.parent.rect
+ npath.intersect = npath.parent.intersect.Intersect(npath.intersect)
}
- state.cpath = npath
if len(aux) > 0 {
- state.rect = false
- state.cpath.pathKey = auxKey
- state.cpath.path = true
- state.cpath.pathVerts = aux
- d.pathOps = append(d.pathOps, state.cpath)
+ npath.rect = false
+ npath.pathKey = auxKey
+ npath.path = true
+ npath.pathVerts = aux
+ d.pathOps = append(d.pathOps, npath)
}
+ state.cpath = npath
}
-// split a transform into two parts, one which is pur offset and the
+// split a transform into two parts, one which is pure offset and the
// other representing the scaling, shearing and rotation part
func splitTransform(t f32.Affine2D) (srs f32.Affine2D, offset f32.Point) {
sx, hx, ox, hy, sy, oy := t.Elems()
@@ -712,121 +852,169 @@ func splitTransform(t f32.Affine2D) (srs f32.Affine2D, offset f32.Point) {
return
}
-func (d *drawOps) collectOps(r *ops.Reader, state drawState) int {
- var aux []byte
- var auxKey ops.Key
+func (d *drawOps) save(id int, state f32.Affine2D) {
+ if extra := id - len(d.states) + 1; extra > 0 {
+ d.states = append(d.states, make([]f32.Affine2D, extra)...)
+ }
+ d.states[id] = state
+}
+
+func (k opKey) SetTransform(t f32.Affine2D) opKey {
+ sx, hx, _, hy, sy, _ := t.Elems()
+ k.sx = sx
+ k.hx = hx
+ k.hy = hy
+ k.sy = sy
+ return k
+}
+
+func (d *drawOps) collectOps(r *ops.Reader, viewport f32.Rectangle) {
+ var (
+ quads quadsOp
+ state drawState
+ )
+ reset := func() {
+ state = drawState{
+ color: color.NRGBA{A: 0xff},
+ }
+ }
+ reset()
loop:
for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
- switch opconst.OpType(encOp.Data[0]) {
- case opconst.TypeProfile:
+ switch ops.OpType(encOp.Data[0]) {
+ case ops.TypeProfile:
d.profile = true
- case opconst.TypeTransform:
- dop := ops.DecodeTransform(encOp.Data)
+ case ops.TypeTransform:
+ dop, push := ops.DecodeTransform(encOp.Data)
+ if push {
+ d.transStack = append(d.transStack, state.t)
+ }
state.t = state.t.Mul(dop)
- case opconst.TypeAux:
- aux = encOp.Data[opconst.TypeAuxLen:]
- auxKey = encOp.Key
- case opconst.TypeClip:
- var op clipOp
- op.decode(encOp.Data)
- bounds := op.bounds
+ case ops.TypePopTransform:
+ n := len(d.transStack)
+ state.t = d.transStack[n-1]
+ d.transStack = d.transStack[:n-1]
+
+ case ops.TypeStroke:
+ quads.key.strokeWidth = decodeStrokeOp(encOp.Data)
+
+ case ops.TypePath:
+ encOp, ok = r.Decode()
+ if !ok {
+ break loop
+ }
+ quads.aux = encOp.Data[ops.TypeAuxLen:]
+ quads.key.Key = encOp.Key
+
+ case ops.TypeClip:
+ var op ops.ClipOp
+ op.Decode(encOp.Data)
+ quads.key.outline = op.Outline
+ bounds := layout.FRect(op.Bounds)
trans, off := splitTransform(state.t)
- if len(aux) > 0 {
+ if len(quads.aux) > 0 {
// There is a clipping path, build the gpu data and update the
// cache key such that it will be equal only if the transform is the
// same also. Use cached data if we have it.
- auxKey = auxKey.SetTransform(trans)
- if v, ok := d.pathCache.get(auxKey); ok {
+ quads.key = quads.key.SetTransform(trans)
+ if v, ok := d.pathCache.get(quads.key); ok {
// Since the GPU data exists in the cache aux will not be used.
// Why is this not used for the offset shapes?
- op.bounds = v.bounds
+ bounds = v.bounds
} else {
- aux, op.bounds = d.buildVerts(aux, trans)
+ var pathData []byte
+ pathData, bounds = d.buildVerts(
+ quads.aux, trans, quads.key.outline, quads.key.strokeWidth,
+ )
+ quads.aux = pathData
// add it to the cache, without GPU data, so the transform can be
// reused.
- d.pathCache.put(auxKey, opCacheValue{bounds: op.bounds})
+ d.pathCache.put(quads.key, opCacheValue{bounds: bounds})
}
} else {
- aux, op.bounds, _ = d.boundsForTransformedRect(bounds, trans)
- auxKey = encOp.Key
- auxKey.SetTransform(trans)
+ quads.aux, bounds, _ = d.boundsForTransformedRect(bounds, trans)
+ quads.key = opKey{Key: encOp.Key}
}
- state.clip = state.clip.Intersect(op.bounds.Add(off))
- d.addClipPath(&state, aux, auxKey, op.bounds, off)
- aux = nil
- auxKey = ops.Key{}
- case opconst.TypeColor:
+ d.addClipPath(&state, quads.aux, quads.key, bounds, off, true)
+ quads = quadsOp{}
+ case ops.TypePopClip:
+ state.cpath = state.cpath.parent
+
+ case ops.TypeColor:
state.matType = materialColor
state.color = decodeColorOp(encOp.Data)
- case opconst.TypeImage:
+ case ops.TypeLinearGradient:
+ state.matType = materialLinearGradient
+ op := decodeLinearGradientOp(encOp.Data)
+ state.stop1 = op.stop1
+ state.stop2 = op.stop2
+ state.color1 = op.color1
+ state.color2 = op.color2
+ case ops.TypeImage:
state.matType = materialTexture
state.image = decodeImageOp(encOp.Data, encOp.Refs)
- case opconst.TypePaint:
- op := decodePaintOp(encOp.Data)
+ case ops.TypePaint:
// Transform (if needed) the painting rectangle and if so generate a clip path,
// for those cases also compute a partialTrans that maps texture coordinates between
// the new bounding rectangle and the transformed original paint rectangle.
- trans, off := splitTransform(state.t)
- clipData, bnd, partialTrans := d.boundsForTransformedRect(op.Rect, trans)
- clip := state.clip.Intersect(bnd.Add(off))
- if clip.Empty() {
+ t, off := splitTransform(state.t)
+ // Fill the clip area, unless the material is a (bounded) image.
+ // TODO: Find a tighter bound.
+ inf := float32(1e6)
+ dst := f32.Rect(-inf, -inf, inf, inf)
+ if state.matType == materialTexture {
+ sz := state.image.src.Rect.Size()
+ dst = f32.Rectangle{Max: layout.FPt(sz)}
+ }
+ clipData, bnd, partialTrans := d.boundsForTransformedRect(dst, t)
+ cl := viewport.Intersect(bnd.Add(off))
+ if state.cpath != nil {
+ cl = state.cpath.intersect.Intersect(cl)
+ }
+ if cl.Empty() {
continue
}
- wasrect := state.rect
if clipData != nil {
// The paint operation is sheared or rotated, add a clip path representing
// this transformed rectangle.
- encOp.Key.SetTransform(trans)
- d.addClipPath(&state, clipData, encOp.Key, bnd, off)
+ k := opKey{Key: encOp.Key}
+ k.SetTransform(t) // TODO: This call has no effect.
+ d.addClipPath(&state, clipData, k, bnd, off, false)
}
- bounds := boundRectF(clip)
- mat := state.materialFor(d.cache, bnd, off, partialTrans, bounds)
+ bounds := boundRectF(cl)
+ mat := state.materialFor(bnd, off, partialTrans, bounds)
- if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && state.rect && mat.opaque && mat.material == materialColor {
+ rect := state.cpath == nil || state.cpath.rect
+ if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && rect && mat.opaque && (mat.material == materialColor) {
// The image is a uniform opaque color and takes up the whole screen.
// Scrap images up to and including this image and set clear color.
- d.zimageOps = d.zimageOps[:0]
d.imageOps = d.imageOps[:0]
- state.z = 0
d.clearColor = mat.color.Opaque()
+ d.clear = true
continue
}
- state.z++
- if state.z != int(uint16(state.z)) {
- // TODO(eliasnaur) gioui.org/issue/127.
- panic("more than 65k paint objects not supported")
- }
- // Assume 16-bit depth buffer.
- const zdepth = 1 << 16
- // Convert z to window-space, assuming depth range [0;1].
- zf := float32(state.z)*2/zdepth - 1.0
img := imageOp{
- z: zf,
path: state.cpath,
- off: off,
clip: bounds,
material: mat,
}
- if state.rect && img.material.opaque {
- d.zimageOps = append(d.zimageOps, img)
- } else {
- d.imageOps = append(d.imageOps, img)
- }
+ d.imageOps = append(d.imageOps, img)
if clipData != nil {
// we added a clip path that should not remain
state.cpath = state.cpath.parent
- state.rect = wasrect
}
- case opconst.TypePush:
- state.z = d.collectOps(r, state)
- case opconst.TypePop:
- break loop
+ case ops.TypeSave:
+ id := ops.DecodeSave(encOp.Data)
+ d.save(id, state.t)
+ case ops.TypeLoad:
+ reset()
+ id := ops.DecodeLoad(encOp.Data)
+ state.t = d.states[id]
}
}
- return state.z
}
func expandPathOp(p *pathOp, clip image.Rectangle) {
@@ -840,78 +1028,83 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
}
}
-func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f32.Point, trans f32.Affine2D, clip image.Rectangle) material {
+func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32.Affine2D, clip image.Rectangle) material {
var m material
switch d.matType {
case materialColor:
m.material = materialColor
- m.color = f32color.RGBAFromSRGB(d.color)
+ m.color = f32color.LinearFromSRGB(d.color)
m.opaque = m.color.A == 1.0
+ case materialLinearGradient:
+ m.material = materialLinearGradient
+
+ m.color1 = f32color.LinearFromSRGB(d.color1)
+ m.color2 = f32color.LinearFromSRGB(d.color2)
+ m.opaque = m.color1.A == 1.0 && m.color2.A == 1.0
+
+ m.uvTrans = partTrans.Mul(gradientSpaceTransform(clip, off, d.stop1, d.stop2))
case materialTexture:
m.material = materialTexture
dr := boundRectF(rect.Add(off))
sz := d.image.src.Bounds().Size()
- sr := layout.FRect(d.image.rect)
- if dx := float32(dr.Dx()); dx != 0 {
- // Don't clip 1 px width sources.
- if sdx := sr.Dx(); sdx > 1 {
- sr.Min.X += (float32(clip.Min.X-dr.Min.X)*sdx + dx/2) / dx
- sr.Max.X -= (float32(dr.Max.X-clip.Max.X)*sdx + dx/2) / dx
- }
- }
- if dy := float32(dr.Dy()); dy != 0 {
- // Don't clip 1 px height sources.
- if sdy := sr.Dy(); sdy > 1 {
- sr.Min.Y += (float32(clip.Min.Y-dr.Min.Y)*sdy + dy/2) / dy
- sr.Max.Y -= (float32(dr.Max.Y-clip.Max.Y)*sdy + dy/2) / dy
- }
- }
- tex, exists := cache.get(d.image.handle)
- if !exists {
- t := &texture{
- src: d.image.src,
- }
- cache.put(d.image.handle, t)
- tex = t
+ sr := f32.Rectangle{
+ Max: f32.Point{
+ X: float32(sz.X),
+ Y: float32(sz.Y),
+ },
}
- m.texture = tex.(*texture)
+ dx := float32(dr.Dx())
+ sdx := sr.Dx()
+ sr.Min.X += float32(clip.Min.X-dr.Min.X) * sdx / dx
+ sr.Max.X -= float32(dr.Max.X-clip.Max.X) * sdx / dx
+ dy := float32(dr.Dy())
+ sdy := sr.Dy()
+ sr.Min.Y += float32(clip.Min.Y-dr.Min.Y) * sdy / dy
+ sr.Max.Y -= float32(dr.Max.Y-clip.Max.Y) * sdy / dy
uvScale, uvOffset := texSpaceTransform(sr, sz)
- m.uvTrans = trans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset))
+ m.uvTrans = partTrans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset))
+ m.data = d.image
}
return m
}
-func (r *renderer) drawZOps(ops []imageOp) {
- r.ctx.SetDepthTest(true)
- r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0)
- r.ctx.BindInputLayout(r.blitter.layout)
- // Render front to back.
- for i := len(ops) - 1; i >= 0; i-- {
- img := ops[i]
+func (r *renderer) uploadImages(cache *resourceCache, ops []imageOp) {
+ for _, img := range ops {
+ m := img.material
+ if m.material == materialTexture {
+ r.texHandle(cache, m.data)
+ }
+ }
+}
+
+func (r *renderer) prepareDrawOps(cache *resourceCache, ops []imageOp) {
+ for _, img := range ops {
m := img.material
switch m.material {
case materialTexture:
- r.ctx.BindTexture(0, r.texHandle(m.texture))
+ r.ctx.PrepareTexture(r.texHandle(cache, m.data))
}
- drc := img.clip
- scale, off := clipSpaceTransform(drc, r.blitter.viewport)
- r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans)
+
+ var fbo stencilFBO
+ switch img.clipType {
+ case clipTypeNone:
+ continue
+ case clipTypePath:
+ fbo = r.pather.stenciler.cover(img.place.Idx)
+ case clipTypeIntersection:
+ fbo = r.pather.stenciler.intersections.fbos[img.place.Idx]
+ }
+ r.ctx.PrepareTexture(fbo.tex)
}
- r.ctx.SetDepthTest(false)
}
-func (r *renderer) drawOps(ops []imageOp) {
- r.ctx.SetDepthTest(true)
- r.ctx.DepthMask(false)
- r.ctx.BlendFunc(backend.BlendFactorOne, backend.BlendFactorOneMinusSrcAlpha)
- r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0)
- r.ctx.BindInputLayout(r.pather.coverer.layout)
- var coverTex backend.Texture
+func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) {
+ var coverTex driver.Texture
for _, img := range ops {
m := img.material
switch m.material {
case materialTexture:
- r.ctx.BindTexture(0, r.texHandle(m.texture))
+ r.ctx.BindTexture(0, r.texHandle(cache, m.data))
}
drc := img.clip
@@ -919,7 +1112,10 @@ func (r *renderer) drawOps(ops []imageOp) {
var fbo stencilFBO
switch img.clipType {
case clipTypeNone:
- r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans)
+ p := r.blitter.pipelines[m.material]
+ r.ctx.BindPipeline(p.pipeline)
+ r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
+ r.blitter.blit(m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans)
continue
case clipTypePath:
fbo = r.pather.stenciler.cover(img.place.Idx)
@@ -934,42 +1130,50 @@ func (r *renderer) drawOps(ops []imageOp) {
Min: img.place.Pos,
Max: img.place.Pos.Add(drc.Size()),
}
- coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size)
- r.pather.cover(img.z, m.material, m.color, scale, off, m.uvTrans, coverScale, coverOff)
+ coverScale, coverOff := texSpaceTransform(layout.FRect(uv), fbo.size)
+ p := r.pather.coverer.pipelines[m.material]
+ r.ctx.BindPipeline(p.pipeline)
+ r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
+ r.pather.cover(m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans, coverScale, coverOff)
}
- r.ctx.DepthMask(true)
- r.ctx.SetDepthTest(false)
}
-func (b *blitter) blit(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) {
- p := b.prog[mat]
- b.ctx.BindProgram(p.prog)
+func (b *blitter) blit(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) {
+ p := b.pipelines[mat]
+ b.ctx.BindPipeline(p.pipeline)
var uniforms *blitUniforms
switch mat {
case materialColor:
- b.colUniforms.frag.color = col
- uniforms = &b.colUniforms.vert.blitUniforms
+ b.colUniforms.color = col
+ uniforms = &b.colUniforms.blitUniforms
case materialTexture:
t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
- b.texUniforms.vert.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
- b.texUniforms.vert.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
- uniforms = &b.texUniforms.vert.blitUniforms
+ b.texUniforms.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
+ b.texUniforms.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
+ uniforms = &b.texUniforms.blitUniforms
+ case materialLinearGradient:
+ b.linearGradientUniforms.color1 = col1
+ b.linearGradientUniforms.color2 = col2
+
+ t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
+ b.linearGradientUniforms.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
+ b.linearGradientUniforms.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
+ uniforms = &b.linearGradientUniforms.blitUniforms
}
- uniforms.z = z
uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
- p.UploadUniforms()
- b.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4)
+ p.UploadUniforms(b.ctx)
+ b.ctx.DrawArrays(0, 4)
}
// newUniformBuffer creates a new GPU uniform buffer backed by the
// structure uniformBlock points to.
-func newUniformBuffer(b backend.Device, uniformBlock interface{}) *uniformBuffer {
+func newUniformBuffer(b driver.Device, uniformBlock interface{}) *uniformBuffer {
ref := reflect.ValueOf(uniformBlock)
// Determine the size of the uniforms structure, *uniforms.
size := ref.Elem().Type().Size()
// Map the uniforms structure as a byte slice.
ptr := (*[1 << 30]byte)(unsafe.Pointer(ref.Pointer()))[:size:size]
- ubuf, err := b.NewBuffer(backend.BufferBindingUniforms, len(ptr))
+ ubuf, err := b.NewBuffer(driver.BufferBindingUniforms, len(ptr))
if err != nil {
panic(err)
}
@@ -985,36 +1189,19 @@ func (u *uniformBuffer) Release() {
u.buf = nil
}
-func newProgram(prog backend.Program, vertUniforms, fragUniforms *uniformBuffer) *program {
- if vertUniforms != nil {
- prog.SetVertexUniforms(vertUniforms.buf)
- }
- if fragUniforms != nil {
- prog.SetFragmentUniforms(fragUniforms.buf)
- }
- return &program{prog: prog, vertUniforms: vertUniforms, fragUniforms: fragUniforms}
-}
-
-func (p *program) UploadUniforms() {
- if p.vertUniforms != nil {
- p.vertUniforms.Upload()
- }
- if p.fragUniforms != nil {
- p.fragUniforms.Upload()
+func (p *pipeline) UploadUniforms(ctx driver.Device) {
+ if p.uniforms != nil {
+ p.uniforms.Upload()
+ ctx.BindUniforms(p.uniforms.buf)
}
}
-func (p *program) Release() {
- p.prog.Release()
- p.prog = nil
- if p.vertUniforms != nil {
- p.vertUniforms.Release()
- p.vertUniforms = nil
- }
- if p.fragUniforms != nil {
- p.fragUniforms.Release()
- p.fragUniforms = nil
+func (p *pipeline) Release() {
+ p.pipeline.Release()
+ if p.uniforms != nil {
+ p.uniforms.Release()
}
+ *p = pipeline{}
}
// texSpaceTransform return the scale and offset that transforms the given subimage
@@ -1026,26 +1213,42 @@ func texSpaceTransform(r f32.Rectangle, bounds image.Point) (f32.Point, f32.Poin
return scale, offset
}
+// gradientSpaceTransform transforms stop1 and stop2 to [(0,0), (1,1)].
+func gradientSpaceTransform(clip image.Rectangle, off f32.Point, stop1, stop2 f32.Point) f32.Affine2D {
+ d := stop2.Sub(stop1)
+ l := float32(math.Sqrt(float64(d.X*d.X + d.Y*d.Y)))
+ a := float32(math.Atan2(float64(-d.Y), float64(d.X)))
+
+ // TODO: optimize
+ zp := f32.Point{}
+ return f32.Affine2D{}.
+ Scale(zp, layout.FPt(clip.Size())). // scale to pixel space
+ Offset(zp.Sub(off).Add(layout.FPt(clip.Min))). // offset to clip space
+ Offset(zp.Sub(stop1)). // offset to first stop point
+ Rotate(zp, a). // rotate to align gradient
+ Scale(zp, f32.Pt(1/l, 1/l)) // scale gradient to right size
+}
+
// clipSpaceTransform returns the scale and offset that transforms the given
-// rectangle from a viewport into OpenGL clip space.
+// rectangle from a viewport into GPU driver device coordinates.
func clipSpaceTransform(r image.Rectangle, viewport image.Point) (f32.Point, f32.Point) {
- // First, transform UI coordinates to OpenGL coordinates:
+ // First, transform UI coordinates to device coordinates:
//
- // [(-1, +1) (+1, +1)]
// [(-1, -1) (+1, -1)]
+ // [(-1, +1) (+1, +1)]
//
x, y := float32(r.Min.X), float32(r.Min.Y)
w, h := float32(r.Dx()), float32(r.Dy())
vx, vy := 2/float32(viewport.X), 2/float32(viewport.Y)
x = x*vx - 1
- y = 1 - y*vy
+ y = y*vy - 1
w *= vx
h *= vy
// Then, compute the transformation from the fullscreen quad to
// the rectangle at (x, y) and dimensions (w, h).
scale := f32.Point{X: w * .5, Y: h * .5}
- offset := f32.Point{X: x + w*.5, Y: y - h*.5}
+ offset := f32.Point{X: x + w*.5, Y: y + h*.5}
return scale, offset
}
@@ -1097,29 +1300,73 @@ func (d *drawOps) writeVertCache(n int) []byte {
}
// transform, split paths as needed, calculate maxY, bounds and create GPU vertices.
-func (d *drawOps) buildVerts(aux []byte, tr f32.Affine2D) (verts []byte, bounds f32.Rectangle) {
+func (d *drawOps) buildVerts(pathData []byte, tr f32.Affine2D, outline bool, strWidth float32) (verts []byte, bounds f32.Rectangle) {
inf := float32(math.Inf(+1))
d.qs.bounds = f32.Rectangle{
Min: f32.Point{X: inf, Y: inf},
Max: f32.Point{X: -inf, Y: -inf},
}
d.qs.d = d
- bo := binary.LittleEndian
startLength := len(d.vertCache)
- for qi := 0; len(aux) >= (ops.QuadSize + 4); qi++ {
- d.qs.contour = bo.Uint32(aux)
- quad := ops.DecodeQuad(aux[4:])
- quad = quad.Transform(tr)
- d.qs.splitAndEncode(quad)
+ switch {
+ case strWidth > 0:
+ // Stroke path.
+ ss := stroke.StrokeStyle{
+ Width: strWidth,
+ }
+ quads := stroke.StrokePathCommands(ss, pathData)
+ for _, quad := range quads {
+ d.qs.contour = quad.Contour
+ quad.Quad = quad.Quad.Transform(tr)
+
+ d.qs.splitAndEncode(quad.Quad)
+ }
- aux = aux[ops.QuadSize+4:]
+ case outline:
+ decodeToOutlineQuads(&d.qs, tr, pathData)
}
fillMaxY(d.vertCache[startLength:])
return d.vertCache[startLength:], d.qs.bounds
}
+// decodeOutlineQuads decodes scene commands, splits them into quadratic béziers
+// as needed and feeds them to the supplied splitter.
+func decodeToOutlineQuads(qs *quadSplitter, tr f32.Affine2D, pathData []byte) {
+ for len(pathData) >= scene.CommandSize+4 {
+ qs.contour = bo.Uint32(pathData)
+ cmd := ops.DecodeCommand(pathData[4:])
+ switch cmd.Op() {
+ case scene.OpLine:
+ var q stroke.QuadSegment
+ q.From, q.To = scene.DecodeLine(cmd)
+ q.Ctrl = q.From.Add(q.To).Mul(.5)
+ q = q.Transform(tr)
+ qs.splitAndEncode(q)
+ case scene.OpGap:
+ var q stroke.QuadSegment
+ q.From, q.To = scene.DecodeGap(cmd)
+ q.Ctrl = q.From.Add(q.To).Mul(.5)
+ q = q.Transform(tr)
+ qs.splitAndEncode(q)
+ case scene.OpQuad:
+ var q stroke.QuadSegment
+ q.From, q.Ctrl, q.To = scene.DecodeQuad(cmd)
+ q = q.Transform(tr)
+ qs.splitAndEncode(q)
+ case scene.OpCubic:
+ for _, q := range stroke.SplitCubic(scene.DecodeCubic(cmd)) {
+ q = q.Transform(tr)
+ qs.splitAndEncode(q)
+ }
+ default:
+ panic("unsupported scene command")
+ }
+ pathData = pathData[scene.CommandSize+4:]
+ }
+}
+
// create GPU vertices for transformed r, find the bounds and establish texture transform.
func (d *drawOps) boundsForTransformedRect(r f32.Rectangle, tr f32.Affine2D) (aux []byte, bnd f32.Rectangle, ptr f32.Affine2D) {
if isPureOffset(tr) {
diff --git a/vendor/gioui.org/gpu/internal/d3d11/d3d11.go b/vendor/gioui.org/gpu/internal/d3d11/d3d11.go
new file mode 100644
index 0000000..3ddf7c3
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/d3d11/d3d11.go
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// This file exists so this package builds on non-Windows platforms.
+
+package d3d11
diff --git a/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go b/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go
new file mode 100644
index 0000000..08698c3
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package d3d11
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "math"
+ "reflect"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/d3d11"
+ "gioui.org/shader"
+)
+
+type Backend struct {
+ dev *d3d11.Device
+ ctx *d3d11.DeviceContext
+
+ // Temporary storage to avoid garbage.
+ clearColor [4]float32
+ viewport d3d11.VIEWPORT
+
+ pipeline *Pipeline
+ vert struct {
+ buffer *Buffer
+ offset int
+ }
+
+ program *Program
+
+ caps driver.Caps
+
+ floatFormat uint32
+}
+
+type Pipeline struct {
+ vert *d3d11.VertexShader
+ frag *d3d11.PixelShader
+ layout *d3d11.InputLayout
+ blend *d3d11.BlendState
+ stride int
+ topology driver.Topology
+}
+
+type Texture struct {
+ backend *Backend
+ format uint32
+ bindings driver.BufferBinding
+ tex *d3d11.Texture2D
+ sampler *d3d11.SamplerState
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ renderTarget *d3d11.RenderTargetView
+
+ width int
+ height int
+ foreign bool
+}
+
+type VertexShader struct {
+ backend *Backend
+ shader *d3d11.VertexShader
+ src shader.Sources
+}
+
+type FragmentShader struct {
+ backend *Backend
+ shader *d3d11.PixelShader
+}
+
+type Program struct {
+ backend *Backend
+ shader *d3d11.ComputeShader
+}
+
+type Buffer struct {
+ backend *Backend
+ bind uint32
+ buf *d3d11.Buffer
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ size int
+ immutable bool
+}
+
+func init() {
+ driver.NewDirect3D11Device = newDirect3D11Device
+}
+
+func detectFloatFormat(dev *d3d11.Device) (uint32, bool) {
+ formats := []uint32{
+ d3d11.DXGI_FORMAT_R16_FLOAT,
+ d3d11.DXGI_FORMAT_R32_FLOAT,
+ d3d11.DXGI_FORMAT_R16G16_FLOAT,
+ d3d11.DXGI_FORMAT_R32G32_FLOAT,
+ // These last two are really wasteful, but c'est la vie.
+ d3d11.DXGI_FORMAT_R16G16B16A16_FLOAT,
+ d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT,
+ }
+ for _, format := range formats {
+ need := uint32(d3d11.FORMAT_SUPPORT_TEXTURE2D | d3d11.FORMAT_SUPPORT_RENDER_TARGET)
+ if support, _ := dev.CheckFormatSupport(format); support&need == need {
+ return format, true
+ }
+ }
+ return 0, false
+}
+
+func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
+ dev := (*d3d11.Device)(api.Device)
+ b := &Backend{
+ dev: dev,
+ ctx: dev.GetImmediateContext(),
+ caps: driver.Caps{
+ MaxTextureSize: 2048, // 9.1 maximum
+ Features: driver.FeatureSRGB,
+ },
+ }
+ featLvl := dev.GetFeatureLevel()
+ switch {
+ case featLvl < d3d11.FEATURE_LEVEL_9_1:
+ d3d11.IUnknownRelease(unsafe.Pointer(dev), dev.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
+ return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl)
+ case featLvl >= d3d11.FEATURE_LEVEL_11_0:
+ b.caps.MaxTextureSize = 16384
+ b.caps.Features |= driver.FeatureCompute
+ case featLvl >= d3d11.FEATURE_LEVEL_9_3:
+ b.caps.MaxTextureSize = 4096
+ }
+ if fmt, ok := detectFloatFormat(dev); ok {
+ b.floatFormat = fmt
+ b.caps.Features |= driver.FeatureFloatRenderTargets
+ }
+ // Disable backface culling to match OpenGL.
+ state, err := dev.CreateRasterizerState(&d3d11.RASTERIZER_DESC{
+ CullMode: d3d11.CULL_NONE,
+ FillMode: d3d11.FILL_SOLID,
+ })
+ if err != nil {
+ return nil, err
+ }
+ defer d3d11.IUnknownRelease(unsafe.Pointer(state), state.Vtbl.Release)
+ b.ctx.RSSetState(state)
+ return b, nil
+}
+
+func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
+ var (
+ renderTarget *d3d11.RenderTargetView
+ )
+ if target != nil {
+ switch t := target.(type) {
+ case driver.Direct3D11RenderTarget:
+ renderTarget = (*d3d11.RenderTargetView)(t.RenderTarget)
+ case *Texture:
+ renderTarget = t.renderTarget
+ default:
+ panic(fmt.Errorf("d3d11: invalid render target type: %T", target))
+ }
+ }
+ b.ctx.OMSetRenderTargets(renderTarget, nil)
+ return &Texture{backend: b, renderTarget: renderTarget, foreign: true}
+}
+
+func (b *Backend) CopyTexture(dstTex driver.Texture, dstOrigin image.Point, srcTex driver.Texture, srcRect image.Rectangle) {
+ dst := (*d3d11.Resource)(unsafe.Pointer(dstTex.(*Texture).tex))
+ src := (*d3d11.Resource)(srcTex.(*Texture).tex)
+ b.ctx.CopySubresourceRegion(
+ dst,
+ 0, // Destination subresource.
+ uint32(dstOrigin.X), uint32(dstOrigin.Y), 0, // Destination coordinates (x, y, z).
+ src,
+ 0, // Source subresource.
+ &d3d11.BOX{
+ Left: uint32(srcRect.Min.X),
+ Top: uint32(srcRect.Min.Y),
+ Right: uint32(srcRect.Max.X),
+ Bottom: uint32(srcRect.Max.Y),
+ Front: 0,
+ Back: 1,
+ },
+ )
+}
+
+func (b *Backend) EndFrame() {
+}
+
+func (b *Backend) Caps() driver.Caps {
+ return b.caps
+}
+
+func (b *Backend) NewTimer() driver.Timer {
+ panic("timers not supported")
+}
+
+func (b *Backend) IsTimeContinuous() bool {
+ panic("timers not supported")
+}
+
+func (b *Backend) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
+ *b = Backend{}
+}
+
+func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
+ var d3dfmt uint32
+ switch format {
+ case driver.TextureFormatFloat:
+ d3dfmt = b.floatFormat
+ case driver.TextureFormatSRGBA:
+ d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
+ case driver.TextureFormatRGBA8:
+ d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM
+ default:
+ return nil, fmt.Errorf("unsupported texture format %d", format)
+ }
+ tex, err := b.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
+ Width: uint32(width),
+ Height: uint32(height),
+ MipLevels: 1,
+ ArraySize: 1,
+ Format: d3dfmt,
+ SampleDesc: d3d11.DXGI_SAMPLE_DESC{
+ Count: 1,
+ Quality: 0,
+ },
+ BindFlags: convBufferBinding(bindings),
+ })
+ if err != nil {
+ return nil, err
+ }
+ var (
+ sampler *d3d11.SamplerState
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ fbo *d3d11.RenderTargetView
+ )
+ if bindings&driver.BufferBindingTexture != 0 {
+ var filter uint32
+ switch {
+ case minFilter == driver.FilterNearest && magFilter == driver.FilterNearest:
+ filter = d3d11.FILTER_MIN_MAG_MIP_POINT
+ case minFilter == driver.FilterLinear && magFilter == driver.FilterLinear:
+ filter = d3d11.FILTER_MIN_MAG_LINEAR_MIP_POINT
+ default:
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter)
+ }
+ var err error
+ sampler, err = b.dev.CreateSamplerState(&d3d11.SAMPLER_DESC{
+ Filter: filter,
+ AddressU: d3d11.TEXTURE_ADDRESS_CLAMP,
+ AddressV: d3d11.TEXTURE_ADDRESS_CLAMP,
+ AddressW: d3d11.TEXTURE_ADDRESS_CLAMP,
+ MaxAnisotropy: 1,
+ MinLOD: -math.MaxFloat32,
+ MaxLOD: math.MaxFloat32,
+ })
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, err
+ }
+ resView, err = b.dev.CreateShaderResourceView(
+ (*d3d11.Resource)(unsafe.Pointer(tex)),
+ unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_TEX2D{
+ SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{
+ Format: d3dfmt,
+ ViewDimension: d3d11.SRV_DIMENSION_TEXTURE2D,
+ },
+ Texture2D: d3d11.TEX2D_SRV{
+ MostDetailedMip: 0,
+ MipLevels: ^uint32(0),
+ },
+ }),
+ )
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
+ return nil, err
+ }
+ }
+ if bindings&driver.BufferBindingShaderStorageWrite != 0 {
+ uaView, err = b.dev.CreateUnorderedAccessView(
+ (*d3d11.Resource)(unsafe.Pointer(tex)),
+ unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_TEX2D{
+ UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{
+ Format: d3dfmt,
+ ViewDimension: d3d11.UAV_DIMENSION_TEXTURE2D,
+ },
+ Texture2D: d3d11.TEX2D_UAV{
+ MipSlice: 0,
+ },
+ }),
+ )
+ if err != nil {
+ if sampler != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
+ }
+ if resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, err
+ }
+ }
+ if bindings&driver.BufferBindingFramebuffer != 0 {
+ resource := (*d3d11.Resource)(unsafe.Pointer(tex))
+ fbo, err = b.dev.CreateRenderTargetView(resource)
+ if err != nil {
+ if uaView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(uaView), uaView.Vtbl.Release)
+ }
+ if sampler != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
+ }
+ if resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, err
+ }
+ }
+ return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, uaView: uaView, renderTarget: fbo, bindings: bindings, width: width, height: height}, nil
+}
+
+func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []driver.InputDesc) (*d3d11.InputLayout, error) {
+ if len(vertexShader.Inputs) != len(layout) {
+ return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs))
+ }
+ descs := make([]d3d11.INPUT_ELEMENT_DESC, len(layout))
+ for i, l := range layout {
+ inp := vertexShader.Inputs[i]
+ cname, err := windows.BytePtrFromString(inp.Semantic)
+ if err != nil {
+ return nil, err
+ }
+ var format uint32
+ switch l.Type {
+ case shader.DataTypeFloat:
+ switch l.Size {
+ case 1:
+ format = d3d11.DXGI_FORMAT_R32_FLOAT
+ case 2:
+ format = d3d11.DXGI_FORMAT_R32G32_FLOAT
+ case 3:
+ format = d3d11.DXGI_FORMAT_R32G32B32_FLOAT
+ case 4:
+ format = d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT
+ default:
+ panic("unsupported data size")
+ }
+ case shader.DataTypeShort:
+ switch l.Size {
+ case 1:
+ format = d3d11.DXGI_FORMAT_R16_SINT
+ case 2:
+ format = d3d11.DXGI_FORMAT_R16G16_SINT
+ default:
+ panic("unsupported data size")
+ }
+ default:
+ panic("unsupported data type")
+ }
+ descs[i] = d3d11.INPUT_ELEMENT_DESC{
+ SemanticName: cname,
+ SemanticIndex: uint32(inp.SemanticIndex),
+ Format: format,
+ AlignedByteOffset: uint32(l.Offset),
+ }
+ }
+ return b.dev.CreateInputLayout(descs, []byte(vertexShader.DXBC))
+}
+
+func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
+ return b.newBuffer(typ, size, nil, false)
+}
+
+func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
+ return b.newBuffer(typ, len(data), data, true)
+}
+
+func (b *Backend) newBuffer(typ driver.BufferBinding, size int, data []byte, immutable bool) (*Buffer, error) {
+ if typ&driver.BufferBindingUniforms != 0 {
+ if typ != driver.BufferBindingUniforms {
+ return nil, errors.New("uniform buffers cannot have other bindings")
+ }
+ if size%16 != 0 {
+ return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", size)
+ }
+ }
+ bind := convBufferBinding(typ)
+ var usage, miscFlags, cpuFlags uint32
+ if immutable {
+ usage = d3d11.USAGE_IMMUTABLE
+ }
+ if typ&driver.BufferBindingShaderStorageWrite != 0 {
+ cpuFlags = d3d11.CPU_ACCESS_READ
+ }
+ if typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 {
+ miscFlags |= d3d11.RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS
+ }
+ buf, err := b.dev.CreateBuffer(&d3d11.BUFFER_DESC{
+ ByteWidth: uint32(size),
+ Usage: usage,
+ BindFlags: bind,
+ CPUAccessFlags: cpuFlags,
+ MiscFlags: miscFlags,
+ }, data)
+ if err != nil {
+ return nil, err
+ }
+ var (
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ )
+ if typ&driver.BufferBindingShaderStorageWrite != 0 {
+ uaView, err = b.dev.CreateUnorderedAccessView(
+ (*d3d11.Resource)(unsafe.Pointer(buf)),
+ unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_BUFFER{
+ UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{
+ Format: d3d11.DXGI_FORMAT_R32_TYPELESS,
+ ViewDimension: d3d11.UAV_DIMENSION_BUFFER,
+ },
+ Buffer: d3d11.BUFFER_UAV{
+ FirstElement: 0,
+ NumElements: uint32(size / 4),
+ Flags: d3d11.BUFFER_UAV_FLAG_RAW,
+ },
+ }),
+ )
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release)
+ return nil, err
+ }
+ } else if typ&driver.BufferBindingShaderStorageRead != 0 {
+ resView, err = b.dev.CreateShaderResourceView(
+ (*d3d11.Resource)(unsafe.Pointer(buf)),
+ unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_BUFFEREX{
+ SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{
+ Format: d3d11.DXGI_FORMAT_R32_TYPELESS,
+ ViewDimension: d3d11.SRV_DIMENSION_BUFFEREX,
+ },
+ Buffer: d3d11.BUFFEREX_SRV{
+ FirstElement: 0,
+ NumElements: uint32(size / 4),
+ Flags: d3d11.BUFFEREX_SRV_FLAG_RAW,
+ },
+ }),
+ )
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release)
+ return nil, err
+ }
+ }
+ return &Buffer{backend: b, buf: buf, bind: bind, size: size, resView: resView, uaView: uaView, immutable: immutable}, nil
+}
+
+func (b *Backend) NewComputeProgram(shader shader.Sources) (driver.Program, error) {
+ cs, err := b.dev.CreateComputeShader([]byte(shader.DXBC))
+ if err != nil {
+ return nil, err
+ }
+ return &Program{backend: b, shader: cs}, nil
+}
+
+func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
+ vsh := desc.VertexShader.(*VertexShader)
+ fsh := desc.FragmentShader.(*FragmentShader)
+ blend, err := b.newBlendState(desc.BlendDesc)
+ if err != nil {
+ return nil, err
+ }
+ var layout *d3d11.InputLayout
+ if l := desc.VertexLayout; l.Stride > 0 {
+ var err error
+ layout, err = b.newInputLayout(vsh.src, l.Inputs)
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(blend), blend.Vtbl.AddRef)
+ return nil, err
+ }
+ }
+
+ // Retain shaders.
+ vshRef := vsh.shader
+ fshRef := fsh.shader
+ d3d11.IUnknownAddRef(unsafe.Pointer(vshRef), vshRef.Vtbl.AddRef)
+ d3d11.IUnknownAddRef(unsafe.Pointer(fshRef), fshRef.Vtbl.AddRef)
+
+ return &Pipeline{
+ vert: vshRef,
+ frag: fshRef,
+ layout: layout,
+ stride: desc.VertexLayout.Stride,
+ blend: blend,
+ topology: desc.Topology,
+ }, nil
+}
+
+func (b *Backend) newBlendState(desc driver.BlendDesc) (*d3d11.BlendState, error) {
+ var d3ddesc d3d11.BLEND_DESC
+ t0 := &d3ddesc.RenderTarget[0]
+ t0.RenderTargetWriteMask = d3d11.COLOR_WRITE_ENABLE_ALL
+ t0.BlendOp = d3d11.BLEND_OP_ADD
+ t0.BlendOpAlpha = d3d11.BLEND_OP_ADD
+ if desc.Enable {
+ t0.BlendEnable = 1
+ }
+ scol, salpha := toBlendFactor(desc.SrcFactor)
+ dcol, dalpha := toBlendFactor(desc.DstFactor)
+ t0.SrcBlend = scol
+ t0.SrcBlendAlpha = salpha
+ t0.DestBlend = dcol
+ t0.DestBlendAlpha = dalpha
+ return b.dev.CreateBlendState(&d3ddesc)
+}
+
+func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
+ vs, err := b.dev.CreateVertexShader([]byte(src.DXBC))
+ if err != nil {
+ return nil, err
+ }
+ return &VertexShader{b, vs, src}, nil
+}
+
+func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
+ fs, err := b.dev.CreatePixelShader([]byte(src.DXBC))
+ if err != nil {
+ return nil, err
+ }
+ return &FragmentShader{b, fs}, nil
+}
+
+func (b *Backend) Viewport(x, y, width, height int) {
+ b.viewport = d3d11.VIEWPORT{
+ TopLeftX: float32(x),
+ TopLeftY: float32(y),
+ Width: float32(width),
+ Height: float32(height),
+ MinDepth: 0.0,
+ MaxDepth: 1.0,
+ }
+ b.ctx.RSSetViewports(&b.viewport)
+}
+
+func (b *Backend) DrawArrays(off, count int) {
+ b.prepareDraw()
+ b.ctx.Draw(uint32(count), uint32(off))
+}
+
+func (b *Backend) DrawElements(off, count int) {
+ b.prepareDraw()
+ b.ctx.DrawIndexed(uint32(count), uint32(off), 0)
+}
+
+func (b *Backend) prepareDraw() {
+ p := b.pipeline
+ if p == nil {
+ return
+ }
+ b.ctx.VSSetShader(p.vert)
+ b.ctx.PSSetShader(p.frag)
+ b.ctx.IASetInputLayout(p.layout)
+ b.ctx.OMSetBlendState(p.blend, nil, 0xffffffff)
+ if b.vert.buffer != nil {
+ b.ctx.IASetVertexBuffers(b.vert.buffer.buf, uint32(p.stride), uint32(b.vert.offset))
+ }
+ var topology uint32
+ switch p.topology {
+ case driver.TopologyTriangles:
+ topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLELIST
+ case driver.TopologyTriangleStrip:
+ topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
+ default:
+ panic("unsupported draw mode")
+ }
+ b.ctx.IASetPrimitiveTopology(topology)
+}
+
+func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ if t.uaView != nil {
+ b.ctx.CSSetUnorderedAccessViews(uint32(unit), t.uaView)
+ } else {
+ b.ctx.CSSetShaderResources(uint32(unit), t.resView)
+ }
+}
+
+func (b *Backend) DispatchCompute(x, y, z int) {
+ b.ctx.CSSetShader(b.program.shader)
+ b.ctx.Dispatch(uint32(x), uint32(y), uint32(z))
+}
+
+func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
+ if stride == 0 {
+ stride = size.X * 4
+ }
+ dst := &d3d11.BOX{
+ Left: uint32(offset.X),
+ Top: uint32(offset.Y),
+ Right: uint32(offset.X + size.X),
+ Bottom: uint32(offset.Y + size.Y),
+ Front: 0,
+ Back: 1,
+ }
+ res := (*d3d11.Resource)(unsafe.Pointer(t.tex))
+ t.backend.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels)
+}
+
+func (t *Texture) Release() {
+ if t.foreign {
+ panic("texture not created by NewTexture")
+ }
+ if t.renderTarget != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.renderTarget), t.renderTarget.Vtbl.Release)
+ }
+ if t.sampler != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.Vtbl.Release)
+ }
+ if t.resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.resView), t.resView.Vtbl.Release)
+ }
+ if t.uaView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.uaView), t.uaView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(t.tex), t.tex.Vtbl.Release)
+ *t = Texture{}
+}
+
+func (b *Backend) PrepareTexture(tex driver.Texture) {}
+
+func (b *Backend) BindTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ b.ctx.PSSetSamplers(uint32(unit), t.sampler)
+ b.ctx.PSSetShaderResources(uint32(unit), t.resView)
+}
+
+func (b *Backend) BindPipeline(pipe driver.Pipeline) {
+ b.pipeline = pipe.(*Pipeline)
+}
+
+func (b *Backend) BindProgram(prog driver.Program) {
+ b.program = prog.(*Program)
+}
+
+func (s *VertexShader) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
+ *s = VertexShader{}
+}
+
+func (s *FragmentShader) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
+ *s = FragmentShader{}
+}
+
+func (s *Program) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
+ *s = Program{}
+}
+
+func (p *Pipeline) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(p.vert), p.vert.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(p.frag), p.frag.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(p.blend), p.blend.Vtbl.Release)
+ if l := p.layout; l != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(l), l.Vtbl.Release)
+ }
+ *p = Pipeline{}
+}
+
+func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ if buf.resView != nil {
+ b.ctx.CSSetShaderResources(uint32(binding), buf.resView)
+ } else {
+ b.ctx.CSSetUnorderedAccessViews(uint32(binding), buf.uaView)
+ }
+}
+
+func (b *Backend) BindUniforms(buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ b.ctx.VSSetConstantBuffers(buf.buf)
+ b.ctx.PSSetConstantBuffers(buf.buf)
+}
+
+func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
+ b.vert.buffer = buf.(*Buffer)
+ b.vert.offset = offset
+}
+
+func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
+ b.ctx.IASetIndexBuffer(buf.(*Buffer).buf, d3d11.DXGI_FORMAT_R16_UINT, 0)
+}
+
+func (b *Buffer) Download(dst []byte) error {
+ res := (*d3d11.Resource)(unsafe.Pointer(b.buf))
+ resMap, err := b.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0)
+ if err != nil {
+ return fmt.Errorf("d3d11: %v", err)
+ }
+ defer b.backend.ctx.Unmap(res, 0)
+ data := sliceOf(resMap.PData, len(dst))
+ copy(dst, data)
+ return nil
+}
+
+func (b *Buffer) Upload(data []byte) {
+ var dst *d3d11.BOX
+ if len(data) < b.size {
+ dst = &d3d11.BOX{
+ Left: 0,
+ Right: uint32(len(data)),
+ Top: 0,
+ Bottom: 1,
+ Front: 0,
+ Back: 1,
+ }
+ }
+ b.backend.ctx.UpdateSubresource((*d3d11.Resource)(unsafe.Pointer(b.buf)), dst, 0, 0, data)
+}
+
+func (b *Buffer) Release() {
+ if b.resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(b.resView), b.resView.Vtbl.Release)
+ }
+ if b.uaView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(b.uaView), b.uaView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(b.buf), b.buf.Vtbl.Release)
+ *b = Buffer{}
+}
+
+func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
+ w, h := src.Dx(), src.Dy()
+ tex, err := t.backend.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
+ Width: uint32(w),
+ Height: uint32(h),
+ MipLevels: 1,
+ ArraySize: 1,
+ Format: t.format,
+ SampleDesc: d3d11.DXGI_SAMPLE_DESC{
+ Count: 1,
+ Quality: 0,
+ },
+ Usage: d3d11.USAGE_STAGING,
+ CPUAccessFlags: d3d11.CPU_ACCESS_READ,
+ })
+ if err != nil {
+ return fmt.Errorf("ReadPixels: %v", err)
+ }
+ defer d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ res := (*d3d11.Resource)(unsafe.Pointer(tex))
+ t.backend.ctx.CopySubresourceRegion(
+ res,
+ 0, // Destination subresource.
+ 0, 0, 0, // Destination coordinates (x, y, z).
+ (*d3d11.Resource)(t.tex),
+ 0, // Source subresource.
+ &d3d11.BOX{
+ Left: uint32(src.Min.X),
+ Top: uint32(src.Min.Y),
+ Right: uint32(src.Max.X),
+ Bottom: uint32(src.Max.Y),
+ Front: 0,
+ Back: 1,
+ },
+ )
+ resMap, err := t.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0)
+ if err != nil {
+ return fmt.Errorf("ReadPixels: %v", err)
+ }
+ defer t.backend.ctx.Unmap(res, 0)
+ srcPitch := stride
+ dstPitch := int(resMap.RowPitch)
+ mapSize := dstPitch * h
+ data := sliceOf(resMap.PData, mapSize)
+ width := w * 4
+ for r := 0; r < h; r++ {
+ pixels := pixels[r*srcPitch:]
+ copy(pixels[:width], data[r*dstPitch:])
+ }
+ return nil
+}
+
+func (b *Backend) BeginCompute() {
+}
+
+func (b *Backend) EndCompute() {
+}
+
+func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
+ t := tex.(*Texture)
+ b.ctx.OMSetRenderTargets(t.renderTarget, nil)
+ if d.Action == driver.LoadActionClear {
+ c := d.ClearColor
+ b.clearColor = [4]float32{c.R, c.G, c.B, c.A}
+ b.ctx.ClearRenderTargetView(t.renderTarget, &b.clearColor)
+ }
+}
+
+func (b *Backend) EndRenderPass() {
+}
+
+func (f *Texture) ImplementsRenderTarget() {}
+
+func convBufferBinding(typ driver.BufferBinding) uint32 {
+ var bindings uint32
+ if typ&driver.BufferBindingVertices != 0 {
+ bindings |= d3d11.BIND_VERTEX_BUFFER
+ }
+ if typ&driver.BufferBindingIndices != 0 {
+ bindings |= d3d11.BIND_INDEX_BUFFER
+ }
+ if typ&driver.BufferBindingUniforms != 0 {
+ bindings |= d3d11.BIND_CONSTANT_BUFFER
+ }
+ if typ&driver.BufferBindingTexture != 0 {
+ bindings |= d3d11.BIND_SHADER_RESOURCE
+ }
+ if typ&driver.BufferBindingFramebuffer != 0 {
+ bindings |= d3d11.BIND_RENDER_TARGET
+ }
+ if typ&driver.BufferBindingShaderStorageWrite != 0 {
+ bindings |= d3d11.BIND_UNORDERED_ACCESS
+ } else if typ&driver.BufferBindingShaderStorageRead != 0 {
+ bindings |= d3d11.BIND_SHADER_RESOURCE
+ }
+ return bindings
+}
+
+func toBlendFactor(f driver.BlendFactor) (uint32, uint32) {
+ switch f {
+ case driver.BlendFactorOne:
+ return d3d11.BLEND_ONE, d3d11.BLEND_ONE
+ case driver.BlendFactorOneMinusSrcAlpha:
+ return d3d11.BLEND_INV_SRC_ALPHA, d3d11.BLEND_INV_SRC_ALPHA
+ case driver.BlendFactorZero:
+ return d3d11.BLEND_ZERO, d3d11.BLEND_ZERO
+ case driver.BlendFactorDstColor:
+ return d3d11.BLEND_DEST_COLOR, d3d11.BLEND_DEST_ALPHA
+ default:
+ panic("unsupported blend source factor")
+ }
+}
+
+// sliceOf returns a slice from a (native) pointer.
+func sliceOf(ptr uintptr, cap int) []byte {
+ var data []byte
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ h.Data = ptr
+ h.Cap = cap
+ h.Len = cap
+ return data
+}
diff --git a/vendor/gioui.org/gpu/internal/driver/api.go b/vendor/gioui.org/gpu/internal/driver/api.go
new file mode 100644
index 0000000..9a762a6
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/driver/api.go
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package driver
+
+import (
+ "fmt"
+ "unsafe"
+
+ "gioui.org/internal/gl"
+)
+
+// See gpu/api.go for documentation for the API types.
+
+type API interface {
+ implementsAPI()
+}
+
+type RenderTarget interface {
+ ImplementsRenderTarget()
+}
+
+type OpenGLRenderTarget gl.Framebuffer
+
+type Direct3D11RenderTarget struct {
+ // RenderTarget is a *ID3D11RenderTargetView.
+ RenderTarget unsafe.Pointer
+}
+
+type MetalRenderTarget struct {
+ // Texture is a MTLTexture.
+ Texture uintptr
+}
+
+type VulkanRenderTarget struct {
+ // WaitSem is a VkSemaphore that must signaled before accessing Framebuffer.
+ WaitSem uint64
+ // SignalSem is a VkSemaphore that signal access to Framebuffer is complete.
+ SignalSem uint64
+ // Image is the VkImage to render into.
+ Image uint64
+ // Framebuffer is a VkFramebuffer for Image.
+ Framebuffer uint64
+}
+
+type OpenGL struct {
+ // ES forces the use of ANGLE OpenGL ES libraries on macOS. It is
+ // ignored on all other platforms.
+ ES bool
+ // Context contains the WebGL context for WebAssembly platforms. It is
+ // empty for all other platforms; an OpenGL context is assumed current when
+ // calling NewDevice.
+ Context gl.Context
+ // Shared instructs users of the context to restore the GL state after
+ // use.
+ Shared bool
+}
+
+type Direct3D11 struct {
+ // Device contains a *ID3D11Device.
+ Device unsafe.Pointer
+}
+
+type Metal struct {
+ // Device is an MTLDevice.
+ Device uintptr
+ // Queue is a MTLCommandQueue.
+ Queue uintptr
+ // PixelFormat is the MTLPixelFormat of the default framebuffer.
+ PixelFormat int
+}
+
+type Vulkan struct {
+ // PhysDevice is a VkPhysicalDevice.
+ PhysDevice unsafe.Pointer
+ // Device is a VkDevice.
+ Device unsafe.Pointer
+ // QueueFamily is the queue familily index of the queue.
+ QueueFamily int
+ // QueueIndex is the logical queue index of the queue.
+ QueueIndex int
+ // Format is a VkFormat that matches render targets.
+ Format int
+}
+
+// API specific device constructors.
+var (
+ NewOpenGLDevice func(api OpenGL) (Device, error)
+ NewDirect3D11Device func(api Direct3D11) (Device, error)
+ NewMetalDevice func(api Metal) (Device, error)
+ NewVulkanDevice func(api Vulkan) (Device, error)
+)
+
+// NewDevice creates a new Device given the api.
+//
+// Note that the device does not assume ownership of the resources contained in
+// api; the caller must ensure the resources are valid until the device is
+// released.
+func NewDevice(api API) (Device, error) {
+ switch api := api.(type) {
+ case OpenGL:
+ if NewOpenGLDevice != nil {
+ return NewOpenGLDevice(api)
+ }
+ case Direct3D11:
+ if NewDirect3D11Device != nil {
+ return NewDirect3D11Device(api)
+ }
+ case Metal:
+ if NewMetalDevice != nil {
+ return NewMetalDevice(api)
+ }
+ case Vulkan:
+ if NewVulkanDevice != nil {
+ return NewVulkanDevice(api)
+ }
+ }
+ return nil, fmt.Errorf("driver: no driver available for the API %T", api)
+}
+
+func (OpenGL) implementsAPI() {}
+func (Direct3D11) implementsAPI() {}
+func (Metal) implementsAPI() {}
+func (Vulkan) implementsAPI() {}
+func (OpenGLRenderTarget) ImplementsRenderTarget() {}
+func (Direct3D11RenderTarget) ImplementsRenderTarget() {}
+func (MetalRenderTarget) ImplementsRenderTarget() {}
+func (VulkanRenderTarget) ImplementsRenderTarget() {}
diff --git a/vendor/gioui.org/gpu/internal/driver/driver.go b/vendor/gioui.org/gpu/internal/driver/driver.go
new file mode 100644
index 0000000..58cb89b
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/driver/driver.go
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package driver
+
+import (
+ "errors"
+ "image"
+ "time"
+
+ "gioui.org/internal/f32color"
+ "gioui.org/shader"
+)
+
+// Device represents the abstraction of underlying GPU
+// APIs such as OpenGL, Direct3D useful for rendering Gio
+// operations.
+type Device interface {
+ BeginFrame(target RenderTarget, clear bool, viewport image.Point) Texture
+ EndFrame()
+ Caps() Caps
+ NewTimer() Timer
+ // IsContinuousTime reports whether all timer measurements
+ // are valid at the point of call.
+ IsTimeContinuous() bool
+ NewTexture(format TextureFormat, width, height int, minFilter, magFilter TextureFilter, bindings BufferBinding) (Texture, error)
+ NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error)
+ NewBuffer(typ BufferBinding, size int) (Buffer, error)
+ NewComputeProgram(shader shader.Sources) (Program, error)
+ NewVertexShader(src shader.Sources) (VertexShader, error)
+ NewFragmentShader(src shader.Sources) (FragmentShader, error)
+ NewPipeline(desc PipelineDesc) (Pipeline, error)
+
+ Viewport(x, y, width, height int)
+ DrawArrays(off, count int)
+ DrawElements(off, count int)
+
+ BeginRenderPass(t Texture, desc LoadDesc)
+ EndRenderPass()
+ PrepareTexture(t Texture)
+ BindProgram(p Program)
+ BindPipeline(p Pipeline)
+ BindTexture(unit int, t Texture)
+ BindVertexBuffer(b Buffer, offset int)
+ BindIndexBuffer(b Buffer)
+ BindImageTexture(unit int, texture Texture)
+ BindUniforms(buf Buffer)
+ BindStorageBuffer(binding int, buf Buffer)
+
+ BeginCompute()
+ EndCompute()
+ CopyTexture(dst Texture, dstOrigin image.Point, src Texture, srcRect image.Rectangle)
+ DispatchCompute(x, y, z int)
+
+ Release()
+}
+
+var ErrDeviceLost = errors.New("GPU device lost")
+
+type LoadDesc struct {
+ Action LoadAction
+ ClearColor f32color.RGBA
+}
+
+type Pipeline interface {
+ Release()
+}
+
+type PipelineDesc struct {
+ VertexShader VertexShader
+ FragmentShader FragmentShader
+ VertexLayout VertexLayout
+ BlendDesc BlendDesc
+ PixelFormat TextureFormat
+ Topology Topology
+}
+
+type VertexLayout struct {
+ Inputs []InputDesc
+ Stride int
+}
+
+// InputDesc describes a vertex attribute as laid out in a Buffer.
+type InputDesc struct {
+ Type shader.DataType
+ Size int
+
+ Offset int
+}
+
+type BlendDesc struct {
+ Enable bool
+ SrcFactor, DstFactor BlendFactor
+}
+
+type BlendFactor uint8
+
+type Topology uint8
+
+type TextureFilter uint8
+type TextureFormat uint8
+
+type BufferBinding uint8
+
+type LoadAction uint8
+
+type Features uint
+
+type Caps struct {
+ // BottomLeftOrigin is true if the driver has the origin in the lower left
+ // corner. The OpenGL driver returns true.
+ BottomLeftOrigin bool
+ Features Features
+ MaxTextureSize int
+}
+
+type VertexShader interface {
+ Release()
+}
+
+type FragmentShader interface {
+ Release()
+}
+
+type Program interface {
+ Release()
+}
+
+type Buffer interface {
+ Release()
+ Upload(data []byte)
+ Download(data []byte) error
+}
+
+type Timer interface {
+ Begin()
+ End()
+ Duration() (time.Duration, bool)
+ Release()
+}
+
+type Texture interface {
+ RenderTarget
+ Upload(offset, size image.Point, pixels []byte, stride int)
+ ReadPixels(src image.Rectangle, pixels []byte, stride int) error
+ Release()
+}
+
+const (
+ BufferBindingIndices BufferBinding = 1 << iota
+ BufferBindingVertices
+ BufferBindingUniforms
+ BufferBindingTexture
+ BufferBindingFramebuffer
+ BufferBindingShaderStorageRead
+ BufferBindingShaderStorageWrite
+)
+
+const (
+ TextureFormatSRGBA TextureFormat = iota
+ TextureFormatFloat
+ TextureFormatRGBA8
+ // TextureFormatOutput denotes the format used by the output framebuffer.
+ TextureFormatOutput
+)
+
+const (
+ FilterNearest TextureFilter = iota
+ FilterLinear
+)
+
+const (
+ FeatureTimers Features = 1 << iota
+ FeatureFloatRenderTargets
+ FeatureCompute
+ FeatureSRGB
+)
+
+const (
+ TopologyTriangleStrip Topology = iota
+ TopologyTriangles
+)
+
+const (
+ BlendFactorOne BlendFactor = iota
+ BlendFactorOneMinusSrcAlpha
+ BlendFactorZero
+ BlendFactorDstColor
+)
+
+const (
+ LoadActionKeep LoadAction = iota
+ LoadActionClear
+ LoadActionInvalidate
+)
+
+var ErrContentLost = errors.New("buffer content lost")
+
+func (f Features) Has(feats Features) bool {
+ return f&feats == feats
+}
+
+func DownloadImage(d Device, t Texture, img *image.RGBA) error {
+ r := img.Bounds()
+ if err := t.ReadPixels(r, img.Pix, img.Stride); err != nil {
+ return err
+ }
+ if d.Caps().BottomLeftOrigin {
+ // OpenGL origin is in the lower-left corner. Flip the image to
+ // match.
+ flipImageY(r.Dx()*4, r.Dy(), img.Pix)
+ }
+ return nil
+}
+
+func flipImageY(stride, height int, pixels []byte) {
+ // Flip image in y-direction. OpenGL's origin is in the lower
+ // left corner.
+ row := make([]uint8, stride)
+ for y := 0; y < height/2; y++ {
+ y1 := height - y - 1
+ dest := y1 * stride
+ src := y * stride
+ copy(row, pixels[dest:])
+ copy(pixels[dest:], pixels[src:src+len(row)])
+ copy(pixels[src:], row)
+ }
+}
+
+func UploadImage(t Texture, offset image.Point, img *image.RGBA) {
+ var pixels []byte
+ size := img.Bounds().Size()
+ min := img.Rect.Min
+ start := img.PixOffset(min.X, min.Y)
+ end := img.PixOffset(min.X+size.X, min.Y+size.Y-1)
+ pixels = img.Pix[start:end]
+ t.Upload(offset, size, pixels, img.Stride)
+}
diff --git a/vendor/gioui.org/gpu/internal/metal/metal.go b/vendor/gioui.org/gpu/internal/metal/metal.go
new file mode 100644
index 0000000..b9739af
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/metal/metal.go
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+// This file exists so this package builds on non-Darwin platforms.
+
+package metal
diff --git a/vendor/gioui.org/gpu/internal/metal/metal_darwin.go b/vendor/gioui.org/gpu/internal/metal/metal_darwin.go
new file mode 100644
index 0000000..c180731
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/metal/metal_darwin.go
@@ -0,0 +1,1141 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package metal
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "unsafe"
+
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/shader"
+)
+
+/*
+#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc
+#cgo LDFLAGS: -framework CoreGraphics
+
+@import Metal;
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Metal/Metal.h>
+
+typedef struct {
+ void *addr;
+ NSUInteger size;
+} slice;
+
+static CFTypeRef queueNewBuffer(CFTypeRef queueRef) {
+ @autoreleasepool {
+ id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef;
+ return CFBridgingRetain([queue commandBuffer]);
+ }
+}
+
+static void cmdBufferCommit(CFTypeRef cmdBufRef) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ [cmdBuf commit];
+ }
+}
+
+static void cmdBufferWaitUntilCompleted(CFTypeRef cmdBufRef) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ [cmdBuf waitUntilCompleted];
+ }
+}
+
+static CFTypeRef cmdBufferRenderEncoder(CFTypeRef cmdBufRef, CFTypeRef textureRef, MTLLoadAction act, float r, float g, float b, float a) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ MTLRenderPassDescriptor *desc = [MTLRenderPassDescriptor new];
+ desc.colorAttachments[0].texture = (__bridge id<MTLTexture>)textureRef;
+ desc.colorAttachments[0].loadAction = act;
+ desc.colorAttachments[0].clearColor = MTLClearColorMake(r, g, b, a);
+ return CFBridgingRetain([cmdBuf renderCommandEncoderWithDescriptor:desc]);
+ }
+}
+
+static CFTypeRef cmdBufferComputeEncoder(CFTypeRef cmdBufRef) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ return CFBridgingRetain([cmdBuf computeCommandEncoder]);
+ }
+}
+
+static CFTypeRef cmdBufferBlitEncoder(CFTypeRef cmdBufRef) {
+ @autoreleasepool {
+ id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
+ return CFBridgingRetain([cmdBuf blitCommandEncoder]);
+ }
+}
+
+static void renderEncEnd(CFTypeRef renderEncRef) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc endEncoding];
+ }
+}
+
+static void renderEncViewport(CFTypeRef renderEncRef, MTLViewport viewport) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc setViewport:viewport];
+ }
+}
+
+static void renderEncSetFragmentTexture(CFTypeRef renderEncRef, NSUInteger index, CFTypeRef texRef) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef;
+ [enc setFragmentTexture:tex atIndex:index];
+ }
+}
+
+static void renderEncSetFragmentSamplerState(CFTypeRef renderEncRef, NSUInteger index, CFTypeRef samplerRef) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLSamplerState> sampler = (__bridge id<MTLSamplerState>)samplerRef;
+ [enc setFragmentSamplerState:sampler atIndex:index];
+ }
+}
+
+static void renderEncSetVertexBuffer(CFTypeRef renderEncRef, CFTypeRef bufRef, NSUInteger idx, NSUInteger offset) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ [enc setVertexBuffer:buf offset:offset atIndex:idx];
+ }
+}
+
+static void renderEncSetFragmentBuffer(CFTypeRef renderEncRef, CFTypeRef bufRef, NSUInteger idx, NSUInteger offset) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ [enc setFragmentBuffer:buf offset:offset atIndex:idx];
+ }
+}
+
+static void renderEncSetFragmentBytes(CFTypeRef renderEncRef, const void *bytes, NSUInteger length, NSUInteger idx) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc setFragmentBytes:bytes length:length atIndex:idx];
+ }
+}
+
+static void renderEncSetVertexBytes(CFTypeRef renderEncRef, const void *bytes, NSUInteger length, NSUInteger idx) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc setVertexBytes:bytes length:length atIndex:idx];
+ }
+}
+
+static void renderEncSetRenderPipelineState(CFTypeRef renderEncRef, CFTypeRef pipeRef) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLRenderPipelineState> pipe = (__bridge id<MTLRenderPipelineState>)pipeRef;
+ [enc setRenderPipelineState:pipe];
+ }
+}
+
+static void renderEncDrawPrimitives(CFTypeRef renderEncRef, MTLPrimitiveType type, NSUInteger start, NSUInteger count) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ [enc drawPrimitives:type vertexStart:start vertexCount:count];
+ }
+}
+
+static void renderEncDrawIndexedPrimitives(CFTypeRef renderEncRef, MTLPrimitiveType type, CFTypeRef bufRef, NSUInteger offset, NSUInteger count) {
+ @autoreleasepool {
+ id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ [enc drawIndexedPrimitives:type indexCount:count indexType:MTLIndexTypeUInt16 indexBuffer:buf indexBufferOffset:offset];
+ }
+}
+
+static void computeEncSetPipeline(CFTypeRef computeEncRef, CFTypeRef pipeRef) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ id<MTLComputePipelineState> pipe = (__bridge id<MTLComputePipelineState>)pipeRef;
+ [enc setComputePipelineState:pipe];
+ }
+}
+
+static void computeEncSetTexture(CFTypeRef computeEncRef, NSUInteger index, CFTypeRef texRef) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef;
+ [enc setTexture:tex atIndex:index];
+ }
+}
+
+static void computeEncEnd(CFTypeRef computeEncRef) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ [enc endEncoding];
+ }
+}
+
+static void computeEncSetBuffer(CFTypeRef computeEncRef, NSUInteger index, CFTypeRef bufRef) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ [enc setBuffer:buf offset:0 atIndex:index];
+ }
+}
+
+static void computeEncDispatch(CFTypeRef computeEncRef, MTLSize threadgroupsPerGrid, MTLSize threadsPerThreadgroup) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ [enc dispatchThreadgroups:threadgroupsPerGrid threadsPerThreadgroup:threadsPerThreadgroup];
+ }
+}
+
+static void computeEncSetBytes(CFTypeRef computeEncRef, const void *bytes, NSUInteger length, NSUInteger index) {
+ @autoreleasepool {
+ id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
+ [enc setBytes:bytes length:length atIndex:index];
+ }
+}
+
+static void blitEncEnd(CFTypeRef blitEncRef) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ [enc endEncoding];
+ }
+}
+
+static void blitEncCopyFromTexture(CFTypeRef blitEncRef, CFTypeRef srcRef, MTLOrigin srcOrig, MTLSize srcSize, CFTypeRef dstRef, MTLOrigin dstOrig) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ id<MTLTexture> src = (__bridge id<MTLTexture>)srcRef;
+ id<MTLTexture> dst = (__bridge id<MTLTexture>)dstRef;
+ [enc copyFromTexture:src
+ sourceSlice:0
+ sourceLevel:0
+ sourceOrigin:srcOrig
+ sourceSize:srcSize
+ toTexture:dst
+ destinationSlice:0
+ destinationLevel:0
+ destinationOrigin:dstOrig];
+ }
+}
+
+static void blitEncCopyBufferToTexture(CFTypeRef blitEncRef, CFTypeRef bufRef, CFTypeRef texRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ id<MTLBuffer> src = (__bridge id<MTLBuffer>)bufRef;
+ id<MTLTexture> dst = (__bridge id<MTLTexture>)texRef;
+ [enc copyFromBuffer:src
+ sourceOffset:offset
+ sourceBytesPerRow:stride
+ sourceBytesPerImage:length
+ sourceSize:dims
+ toTexture:dst
+ destinationSlice:0
+ destinationLevel:0
+ destinationOrigin:orig];
+ }
+}
+
+static void blitEncCopyTextureToBuffer(CFTypeRef blitEncRef, CFTypeRef texRef, CFTypeRef bufRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ id<MTLTexture> src = (__bridge id<MTLTexture>)texRef;
+ id<MTLBuffer> dst = (__bridge id<MTLBuffer>)bufRef;
+ [enc copyFromTexture:src
+ sourceSlice:0
+ sourceLevel:0
+ sourceOrigin:orig
+ sourceSize:dims
+ toBuffer:dst
+ destinationOffset:offset
+ destinationBytesPerRow:stride
+ destinationBytesPerImage:length];
+ }
+}
+
+static void blitEncCopyBufferToBuffer(CFTypeRef blitEncRef, CFTypeRef srcRef, CFTypeRef dstRef, NSUInteger srcOff, NSUInteger dstOff, NSUInteger size) {
+ @autoreleasepool {
+ id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
+ id<MTLBuffer> src = (__bridge id<MTLBuffer>)srcRef;
+ id<MTLBuffer> dst = (__bridge id<MTLBuffer>)dstRef;
+ [enc copyFromBuffer:src
+ sourceOffset:srcOff
+ toBuffer:dst
+ destinationOffset:dstOff
+ size:size];
+ }
+}
+
+static CFTypeRef newTexture(CFTypeRef devRef, NSUInteger width, NSUInteger height, MTLPixelFormat format, MTLTextureUsage usage) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ MTLTextureDescriptor *mtlDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
+ width: width
+ height: height
+ mipmapped: NO];
+ mtlDesc.usage = usage;
+ mtlDesc.storageMode = MTLStorageModePrivate;
+ return CFBridgingRetain([dev newTextureWithDescriptor:mtlDesc]);
+ }
+}
+
+static CFTypeRef newSampler(CFTypeRef devRef, MTLSamplerMinMagFilter minFilter, MTLSamplerMinMagFilter magFilter) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ MTLSamplerDescriptor *desc = [MTLSamplerDescriptor new];
+ desc.minFilter = minFilter;
+ desc.magFilter = magFilter;
+ return CFBridgingRetain([dev newSamplerStateWithDescriptor:desc]);
+ }
+}
+
+static CFTypeRef newBuffer(CFTypeRef devRef, NSUInteger size, MTLResourceOptions opts) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ id<MTLBuffer> buf = [dev newBufferWithLength:size
+ options:opts];
+ return CFBridgingRetain(buf);
+ }
+}
+
+static slice bufferContents(CFTypeRef bufRef) {
+ @autoreleasepool {
+ id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
+ slice s = {.addr = [buf contents], .size = [buf length]};
+ return s;
+ }
+}
+
+static CFTypeRef newLibrary(CFTypeRef devRef, char *name, void *mtllib, size_t size) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ dispatch_data_t data = dispatch_data_create(mtllib, size, DISPATCH_TARGET_QUEUE_DEFAULT, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
+ id<MTLLibrary> lib = [dev newLibraryWithData:data error:nil];
+ lib.label = [NSString stringWithUTF8String:name];
+ return CFBridgingRetain(lib);
+ }
+}
+
+static CFTypeRef libraryNewFunction(CFTypeRef libRef, char *funcName) {
+ @autoreleasepool {
+ id<MTLLibrary> lib = (__bridge id<MTLLibrary>)libRef;
+ NSString *name = [NSString stringWithUTF8String:funcName];
+ return CFBridgingRetain([lib newFunctionWithName:name]);
+ }
+}
+
+static CFTypeRef newComputePipeline(CFTypeRef devRef, CFTypeRef funcRef) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ id<MTLFunction> func = (__bridge id<MTLFunction>)funcRef;
+ return CFBridgingRetain([dev newComputePipelineStateWithFunction:func error:nil]);
+ }
+}
+
+static CFTypeRef newRenderPipeline(CFTypeRef devRef, CFTypeRef vertFunc, CFTypeRef fragFunc, MTLPixelFormat pixelFormat, NSUInteger bufIdx, NSUInteger nverts, MTLVertexFormat *fmts, NSUInteger *offsets, NSUInteger stride, int blend, MTLBlendFactor srcFactor, MTLBlendFactor dstFactor, NSUInteger nvertBufs, NSUInteger nfragBufs) {
+ @autoreleasepool {
+ id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
+ id<MTLFunction> vfunc = (__bridge id<MTLFunction>)vertFunc;
+ id<MTLFunction> ffunc = (__bridge id<MTLFunction>)fragFunc;
+ MTLVertexDescriptor *vdesc = [MTLVertexDescriptor vertexDescriptor];
+ vdesc.layouts[bufIdx].stride = stride;
+ for (NSUInteger i = 0; i < nverts; i++) {
+ vdesc.attributes[i].format = fmts[i];
+ vdesc.attributes[i].offset = offsets[i];
+ vdesc.attributes[i].bufferIndex = bufIdx;
+ }
+ MTLRenderPipelineDescriptor *desc = [MTLRenderPipelineDescriptor new];
+ desc.vertexFunction = vfunc;
+ desc.fragmentFunction = ffunc;
+ desc.vertexDescriptor = vdesc;
+ for (NSUInteger i = 0; i < nvertBufs; i++) {
+ if (@available(iOS 11.0, *)) {
+ desc.vertexBuffers[i].mutability = MTLMutabilityImmutable;
+ }
+ }
+ for (NSUInteger i = 0; i < nfragBufs; i++) {
+ if (@available(iOS 11.0, *)) {
+ desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable;
+ }
+ }
+ desc.colorAttachments[0].pixelFormat = pixelFormat;
+ desc.colorAttachments[0].blendingEnabled = blend ? YES : NO;
+ desc.colorAttachments[0].sourceAlphaBlendFactor = srcFactor;
+ desc.colorAttachments[0].sourceRGBBlendFactor = srcFactor;
+ desc.colorAttachments[0].destinationAlphaBlendFactor = dstFactor;
+ desc.colorAttachments[0].destinationRGBBlendFactor = dstFactor;
+ return CFBridgingRetain([dev newRenderPipelineStateWithDescriptor:desc
+ error:nil]);
+ }
+}
+*/
+import "C"
+
+type Backend struct {
+ dev C.CFTypeRef
+ queue C.CFTypeRef
+ pixelFmt C.MTLPixelFormat
+
+ cmdBuffer C.CFTypeRef
+ lastCmdBuffer C.CFTypeRef
+ renderEnc C.CFTypeRef
+ computeEnc C.CFTypeRef
+ blitEnc C.CFTypeRef
+
+ prog *Program
+ topology C.MTLPrimitiveType
+
+ stagingBuf C.CFTypeRef
+ stagingOff int
+
+ indexBuf *Buffer
+
+ // bufSizes is scratch space for filling out the spvBufferSizeConstants
+ // that spirv-cross generates for emulating buffer.length expressions in
+ // shaders.
+ bufSizes []uint32
+}
+
+type Texture struct {
+ backend *Backend
+ texture C.CFTypeRef
+ sampler C.CFTypeRef
+ width int
+ height int
+ foreign bool
+}
+
+type Shader struct {
+ function C.CFTypeRef
+ inputs []shader.InputLocation
+}
+
+type Program struct {
+ pipeline C.CFTypeRef
+ groupSize [3]int
+}
+
+type Pipeline struct {
+ pipeline C.CFTypeRef
+ topology C.MTLPrimitiveType
+}
+
+type Buffer struct {
+ backend *Backend
+ size int
+ buffer C.CFTypeRef
+
+ // store is the buffer contents For buffers not allocated on the GPU.
+ store []byte
+}
+
+const (
+ uniformBufferIndex = 0
+ attributeBufferIndex = 1
+
+ spvBufferSizeConstantsBinding = 25
+)
+
+const (
+ texUnits = 4
+ bufferUnits = 4
+)
+
+func init() {
+ driver.NewMetalDevice = newMetalDevice
+}
+
+func newMetalDevice(api driver.Metal) (driver.Device, error) {
+ dev := C.CFTypeRef(api.Device)
+ C.CFRetain(dev)
+ queue := C.CFTypeRef(api.Queue)
+ C.CFRetain(queue)
+ b := &Backend{
+ dev: dev,
+ queue: queue,
+ pixelFmt: C.MTLPixelFormat(api.PixelFormat),
+ bufSizes: make([]uint32, bufferUnits),
+ }
+ return b, nil
+}
+
+func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
+ if b.lastCmdBuffer != 0 {
+ C.cmdBufferWaitUntilCompleted(b.lastCmdBuffer)
+ b.stagingOff = 0
+ }
+ if target == nil {
+ return nil
+ }
+ switch t := target.(type) {
+ case driver.MetalRenderTarget:
+ texture := C.CFTypeRef(t.Texture)
+ return &Texture{texture: texture, foreign: true}
+ case *Texture:
+ return t
+ default:
+ panic(fmt.Sprintf("metal: unsupported render target type: %T", t))
+ }
+}
+
+func (b *Backend) startBlit() C.CFTypeRef {
+ if b.blitEnc != 0 {
+ return b.blitEnc
+ }
+ b.endEncoder()
+ b.ensureCmdBuffer()
+ b.blitEnc = C.cmdBufferBlitEncoder(b.cmdBuffer)
+ if b.blitEnc == 0 {
+ panic("metal: [MTLCommandBuffer blitCommandEncoder:] failed")
+ }
+ return b.blitEnc
+}
+
+func (b *Backend) CopyTexture(dst driver.Texture, dorig image.Point, src driver.Texture, srect image.Rectangle) {
+ enc := b.startBlit()
+ dstTex := dst.(*Texture).texture
+ srcTex := src.(*Texture).texture
+ ssz := srect.Size()
+ C.blitEncCopyFromTexture(
+ enc,
+ srcTex,
+ C.MTLOrigin{
+ x: C.NSUInteger(srect.Min.X),
+ y: C.NSUInteger(srect.Min.Y),
+ },
+ C.MTLSize{
+ width: C.NSUInteger(ssz.X),
+ height: C.NSUInteger(ssz.Y),
+ depth: 1,
+ },
+ dstTex,
+ C.MTLOrigin{
+ x: C.NSUInteger(dorig.X),
+ y: C.NSUInteger(dorig.Y),
+ },
+ )
+}
+
+func (b *Backend) EndFrame() {
+ b.endCmdBuffer(false)
+}
+
+func (b *Backend) endCmdBuffer(wait bool) {
+ b.endEncoder()
+ if b.cmdBuffer == 0 {
+ return
+ }
+ C.cmdBufferCommit(b.cmdBuffer)
+ if wait {
+ C.cmdBufferWaitUntilCompleted(b.cmdBuffer)
+ }
+ if b.lastCmdBuffer != 0 {
+ C.CFRelease(b.lastCmdBuffer)
+ }
+ b.lastCmdBuffer = b.cmdBuffer
+ b.cmdBuffer = 0
+}
+
+func (b *Backend) Caps() driver.Caps {
+ return driver.Caps{
+ MaxTextureSize: 8192,
+ Features: driver.FeatureSRGB | driver.FeatureCompute | driver.FeatureFloatRenderTargets,
+ }
+}
+
+func (b *Backend) NewTimer() driver.Timer {
+ panic("timers not supported")
+}
+
+func (b *Backend) IsTimeContinuous() bool {
+ panic("timers not supported")
+}
+
+func (b *Backend) Release() {
+ if b.cmdBuffer != 0 {
+ C.CFRelease(b.cmdBuffer)
+ }
+ if b.lastCmdBuffer != 0 {
+ C.CFRelease(b.lastCmdBuffer)
+ }
+ if b.stagingBuf != 0 {
+ C.CFRelease(b.stagingBuf)
+ }
+ C.CFRelease(b.queue)
+ C.CFRelease(b.dev)
+ *b = Backend{}
+}
+
+func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
+ mformat := pixelFormatFor(format)
+ var usage C.MTLTextureUsage
+ if bindings&(driver.BufferBindingTexture|driver.BufferBindingShaderStorageRead) != 0 {
+ usage |= C.MTLTextureUsageShaderRead
+ }
+ if bindings&driver.BufferBindingFramebuffer != 0 {
+ usage |= C.MTLTextureUsageRenderTarget
+ }
+ if bindings&driver.BufferBindingShaderStorageWrite != 0 {
+ usage |= C.MTLTextureUsageShaderWrite
+ }
+ tex := C.newTexture(b.dev, C.NSUInteger(width), C.NSUInteger(height), mformat, usage)
+ if tex == 0 {
+ return nil, errors.New("metal: [MTLDevice newTextureWithDescriptor:] failed")
+ }
+ min := samplerFilterFor(minFilter)
+ max := samplerFilterFor(magFilter)
+ s := C.newSampler(b.dev, min, max)
+ if s == 0 {
+ C.CFRelease(tex)
+ return nil, errors.New("metal: [MTLDevice newSamplerStateWithDescriptor:] failed")
+ }
+ return &Texture{backend: b, texture: tex, sampler: s, width: width, height: height}, nil
+}
+
+func samplerFilterFor(f driver.TextureFilter) C.MTLSamplerMinMagFilter {
+ switch f {
+ case driver.FilterNearest:
+ return C.MTLSamplerMinMagFilterNearest
+ case driver.FilterLinear:
+ return C.MTLSamplerMinMagFilterLinear
+ default:
+ panic("invalid texture filter")
+ }
+}
+
+func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
+ vsh, fsh := desc.VertexShader.(*Shader), desc.FragmentShader.(*Shader)
+ layout := desc.VertexLayout.Inputs
+ if got, exp := len(layout), len(vsh.inputs); got != exp {
+ return nil, fmt.Errorf("metal: number of input descriptors (%d) doesn't match number of inputs (%d)", got, exp)
+ }
+ formats := make([]C.MTLVertexFormat, len(layout))
+ offsets := make([]C.NSUInteger, len(layout))
+ for i, inp := range layout {
+ index := vsh.inputs[i].Location
+ formats[index] = vertFormatFor(vsh.inputs[i])
+ offsets[index] = C.NSUInteger(inp.Offset)
+ }
+ var (
+ fmtPtr *C.MTLVertexFormat
+ offPtr *C.NSUInteger
+ )
+ if len(layout) > 0 {
+ fmtPtr = &formats[0]
+ offPtr = &offsets[0]
+ }
+ srcFactor := blendFactorFor(desc.BlendDesc.SrcFactor)
+ dstFactor := blendFactorFor(desc.BlendDesc.DstFactor)
+ blend := C.int(0)
+ if desc.BlendDesc.Enable {
+ blend = 1
+ }
+ pf := b.pixelFmt
+ if f := desc.PixelFormat; f != driver.TextureFormatOutput {
+ pf = pixelFormatFor(f)
+ }
+ pipe := C.newRenderPipeline(
+ b.dev,
+ vsh.function,
+ fsh.function,
+ pf,
+ attributeBufferIndex,
+ C.NSUInteger(len(layout)), fmtPtr, offPtr,
+ C.NSUInteger(desc.VertexLayout.Stride),
+ blend, srcFactor, dstFactor,
+ 2, // Number of vertex buffers.
+ 1, // Number of fragment buffers.
+ )
+ if pipe == 0 {
+ return nil, errors.New("metal: pipeline construction failed")
+ }
+ return &Pipeline{pipeline: pipe, topology: primitiveFor(desc.Topology)}, nil
+}
+
+func dataTypeSize(d shader.DataType) int {
+ switch d {
+ case shader.DataTypeFloat:
+ return 4
+ default:
+ panic("unsupported data type")
+ }
+}
+
+func blendFactorFor(f driver.BlendFactor) C.MTLBlendFactor {
+ switch f {
+ case driver.BlendFactorZero:
+ return C.MTLBlendFactorZero
+ case driver.BlendFactorOne:
+ return C.MTLBlendFactorOne
+ case driver.BlendFactorOneMinusSrcAlpha:
+ return C.MTLBlendFactorOneMinusSourceAlpha
+ case driver.BlendFactorDstColor:
+ return C.MTLBlendFactorDestinationColor
+ default:
+ panic("unsupported blend factor")
+ }
+}
+
+func vertFormatFor(f shader.InputLocation) C.MTLVertexFormat {
+ t := f.Type
+ s := f.Size
+ switch {
+ case t == shader.DataTypeFloat && s == 1:
+ return C.MTLVertexFormatFloat
+ case t == shader.DataTypeFloat && s == 2:
+ return C.MTLVertexFormatFloat2
+ case t == shader.DataTypeFloat && s == 3:
+ return C.MTLVertexFormatFloat3
+ case t == shader.DataTypeFloat && s == 4:
+ return C.MTLVertexFormatFloat4
+ default:
+ panic("unsupported data type")
+ }
+}
+
+func pixelFormatFor(f driver.TextureFormat) C.MTLPixelFormat {
+ switch f {
+ case driver.TextureFormatFloat:
+ return C.MTLPixelFormatR16Float
+ case driver.TextureFormatRGBA8:
+ return C.MTLPixelFormatRGBA8Unorm
+ case driver.TextureFormatSRGBA:
+ return C.MTLPixelFormatRGBA8Unorm_sRGB
+ default:
+ panic("unsupported pixel format")
+ }
+}
+
+func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
+ // Transfer buffer contents in command encoders on every use for
+ // smaller buffers. The advantage is that buffer re-use during a frame
+ // won't occur a GPU wait.
+ // We can't do this for buffers written to by the GPU and read by the client,
+ // and Metal doesn't require a buffer for indexed draws.
+ if size <= 4096 && typ&(driver.BufferBindingShaderStorageWrite|driver.BufferBindingIndices) == 0 {
+ return &Buffer{size: size, store: make([]byte, size)}, nil
+ }
+ buf := C.newBuffer(b.dev, C.NSUInteger(size), C.MTLResourceStorageModePrivate)
+ return &Buffer{backend: b, size: size, buffer: buf}, nil
+}
+
+func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
+ buf, err := b.NewBuffer(typ, len(data))
+ if err != nil {
+ return nil, err
+ }
+ buf.Upload(data)
+ return buf, nil
+}
+
+func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) {
+ sh, err := b.newShader(src)
+ if err != nil {
+ return nil, err
+ }
+ defer sh.Release()
+ pipe := C.newComputePipeline(b.dev, sh.function)
+ if pipe == 0 {
+ return nil, fmt.Errorf("metal: compute program %q load failed", src.Name)
+ }
+ return &Program{pipeline: pipe, groupSize: src.WorkgroupSize}, nil
+}
+
+func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
+ return b.newShader(src)
+}
+
+func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
+ return b.newShader(src)
+}
+
+func (b *Backend) newShader(src shader.Sources) (*Shader, error) {
+ vsrc := []byte(src.MetalLib)
+ cname := C.CString(src.Name)
+ defer C.free(unsafe.Pointer(cname))
+ vlib := C.newLibrary(b.dev, cname, unsafe.Pointer(&vsrc[0]), C.size_t(len(vsrc)))
+ if vlib == 0 {
+ return nil, fmt.Errorf("metal: vertex shader %q load failed", src.Name)
+ }
+ defer C.CFRelease(vlib)
+ funcName := C.CString("main0")
+ defer C.free(unsafe.Pointer(funcName))
+ f := C.libraryNewFunction(vlib, funcName)
+ if f == 0 {
+ return nil, fmt.Errorf("metal: main function not found in %q", src.Name)
+ }
+ return &Shader{function: f, inputs: src.Inputs}, nil
+}
+
+func (b *Backend) Viewport(x, y, width, height int) {
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncViewport(enc, C.MTLViewport{
+ originX: C.double(x),
+ originY: C.double(y),
+ width: C.double(width),
+ height: C.double(height),
+ znear: 0.0,
+ zfar: 1.0,
+ })
+}
+
+func (b *Backend) DrawArrays(off, count int) {
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncDrawPrimitives(enc, b.topology, C.NSUInteger(off), C.NSUInteger(count))
+}
+
+func (b *Backend) DrawElements(off, count int) {
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncDrawIndexedPrimitives(enc, b.topology, b.indexBuf.buffer, C.NSUInteger(off), C.NSUInteger(count))
+}
+
+func primitiveFor(mode driver.Topology) C.MTLPrimitiveType {
+ switch mode {
+ case driver.TopologyTriangles:
+ return C.MTLPrimitiveTypeTriangle
+ case driver.TopologyTriangleStrip:
+ return C.MTLPrimitiveTypeTriangleStrip
+ default:
+ panic("metal: unknown draw mode")
+ }
+}
+
+func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
+ b.BindTexture(unit, tex)
+}
+
+func (b *Backend) BeginCompute() {
+ b.endEncoder()
+ b.ensureCmdBuffer()
+ for i := range b.bufSizes {
+ b.bufSizes[i] = 0
+ }
+ b.computeEnc = C.cmdBufferComputeEncoder(b.cmdBuffer)
+ if b.computeEnc == 0 {
+ panic("metal: [MTLCommandBuffer computeCommandEncoder:] failed")
+ }
+}
+
+func (b *Backend) EndCompute() {
+ if b.computeEnc == 0 {
+ panic("no active compute pass")
+ }
+ C.computeEncEnd(b.computeEnc)
+ C.CFRelease(b.computeEnc)
+ b.computeEnc = 0
+}
+
+func (b *Backend) DispatchCompute(x, y, z int) {
+ enc := b.computeEnc
+ if enc == 0 {
+ panic("no active compute pass")
+ }
+ C.computeEncSetBytes(enc, unsafe.Pointer(&b.bufSizes[0]), C.NSUInteger(len(b.bufSizes)*4), spvBufferSizeConstantsBinding)
+ threadgroupsPerGrid := C.MTLSize{
+ width: C.NSUInteger(x), height: C.NSUInteger(y), depth: C.NSUInteger(z),
+ }
+ sz := b.prog.groupSize
+ threadsPerThreadgroup := C.MTLSize{
+ width: C.NSUInteger(sz[0]), height: C.NSUInteger(sz[1]), depth: C.NSUInteger(sz[2]),
+ }
+ C.computeEncDispatch(enc, threadgroupsPerGrid, threadsPerThreadgroup)
+}
+
+func (b *Backend) stagingBuffer(size int) (C.CFTypeRef, int) {
+ if b.stagingBuf == 0 || b.stagingOff+size > len(bufferStore(b.stagingBuf)) {
+ if b.stagingBuf != 0 {
+ C.CFRelease(b.stagingBuf)
+ }
+ cap := 2 * (b.stagingOff + size)
+ b.stagingBuf = C.newBuffer(b.dev, C.NSUInteger(cap), C.MTLResourceStorageModeShared|C.MTLResourceCPUCacheModeWriteCombined)
+ if b.stagingBuf == 0 {
+ panic(fmt.Errorf("metal: failed to allocate %d bytes of staging buffer", cap))
+ }
+ b.stagingOff = 0
+ }
+ off := b.stagingOff
+ b.stagingOff += size
+ return b.stagingBuf, off
+}
+
+func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
+ if len(pixels) == 0 {
+ return
+ }
+ if stride == 0 {
+ stride = size.X * 4
+ }
+ dstStride := size.X * 4
+ n := size.Y * dstStride
+ buf, off := t.backend.stagingBuffer(n)
+ store := bufferSlice(buf, off, n)
+ var srcOff, dstOff int
+ for y := 0; y < size.Y; y++ {
+ srcRow := pixels[srcOff : srcOff+dstStride]
+ dstRow := store[dstOff : dstOff+dstStride]
+ copy(dstRow, srcRow)
+ dstOff += dstStride
+ srcOff += stride
+ }
+ enc := t.backend.startBlit()
+ orig := C.MTLOrigin{
+ x: C.NSUInteger(offset.X),
+ y: C.NSUInteger(offset.Y),
+ }
+ msize := C.MTLSize{
+ width: C.NSUInteger(size.X),
+ height: C.NSUInteger(size.Y),
+ depth: 1,
+ }
+ C.blitEncCopyBufferToTexture(enc, buf, t.texture, C.NSUInteger(off), C.NSUInteger(dstStride), C.NSUInteger(len(store)), msize, orig)
+}
+
+func (t *Texture) Release() {
+ if t.foreign {
+ panic("metal: release of external texture")
+ }
+ C.CFRelease(t.texture)
+ C.CFRelease(t.sampler)
+ *t = Texture{}
+}
+
+func (p *Pipeline) Release() {
+ C.CFRelease(p.pipeline)
+ *p = Pipeline{}
+}
+
+func (b *Backend) PrepareTexture(tex driver.Texture) {}
+
+func (b *Backend) BindTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ if enc := b.renderEnc; enc != 0 {
+ C.renderEncSetFragmentTexture(enc, C.NSUInteger(unit), t.texture)
+ C.renderEncSetFragmentSamplerState(enc, C.NSUInteger(unit), t.sampler)
+ } else if enc := b.computeEnc; enc != 0 {
+ C.computeEncSetTexture(enc, C.NSUInteger(unit), t.texture)
+ } else {
+ panic("no active render nor compute pass")
+ }
+}
+
+func (b *Backend) ensureCmdBuffer() {
+ if b.cmdBuffer != 0 {
+ return
+ }
+ b.cmdBuffer = C.queueNewBuffer(b.queue)
+ if b.cmdBuffer == 0 {
+ panic("metal: [MTLCommandQueue cmdBuffer] failed")
+ }
+}
+
+func (b *Backend) BindPipeline(pipe driver.Pipeline) {
+ p := pipe.(*Pipeline)
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncSetRenderPipelineState(enc, p.pipeline)
+ b.topology = p.topology
+}
+
+func (b *Backend) BindProgram(prog driver.Program) {
+ enc := b.computeEnc
+ if enc == 0 {
+ panic("no active compute pass")
+ }
+ p := prog.(*Program)
+ C.computeEncSetPipeline(enc, p.pipeline)
+ b.prog = p
+}
+
+func (s *Shader) Release() {
+ C.CFRelease(s.function)
+ *s = Shader{}
+}
+
+func (p *Program) Release() {
+ C.CFRelease(p.pipeline)
+ *p = Program{}
+}
+
+func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ b.bufSizes[binding] = uint32(buf.size)
+ enc := b.computeEnc
+ if enc == 0 {
+ panic("no active compute pass")
+ }
+ if buf.buffer != 0 {
+ C.computeEncSetBuffer(enc, C.NSUInteger(binding), buf.buffer)
+ } else if buf.size > 0 {
+ C.computeEncSetBytes(enc, unsafe.Pointer(&buf.store[0]), C.NSUInteger(buf.size), C.NSUInteger(binding))
+ }
+}
+
+func (b *Backend) BindUniforms(buf driver.Buffer) {
+ bf := buf.(*Buffer)
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ if bf.buffer != 0 {
+ C.renderEncSetVertexBuffer(enc, bf.buffer, uniformBufferIndex, 0)
+ C.renderEncSetFragmentBuffer(enc, bf.buffer, uniformBufferIndex, 0)
+ } else if bf.size > 0 {
+ C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
+ C.renderEncSetFragmentBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
+ }
+}
+
+func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
+ bf := buf.(*Buffer)
+ enc := b.renderEnc
+ if enc == 0 {
+ panic("no active render pass")
+ }
+ if bf.buffer != 0 {
+ C.renderEncSetVertexBuffer(enc, bf.buffer, attributeBufferIndex, C.NSUInteger(offset))
+ } else if n := bf.size - offset; n > 0 {
+ C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[offset]), C.NSUInteger(n), attributeBufferIndex)
+ }
+}
+
+func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
+ b.indexBuf = buf.(*Buffer)
+}
+
+func (b *Buffer) Download(data []byte) error {
+ if len(data) > b.size {
+ panic(fmt.Errorf("len(data) (%d) larger than len(content) (%d)", len(data), b.size))
+ }
+ buf, off := b.backend.stagingBuffer(len(data))
+ enc := b.backend.startBlit()
+ C.blitEncCopyBufferToBuffer(enc, b.buffer, buf, 0, C.NSUInteger(off), C.NSUInteger(len(data)))
+ b.backend.endCmdBuffer(true)
+ store := bufferSlice(buf, off, len(data))
+ copy(data, store)
+ return nil
+}
+
+func (b *Buffer) Upload(data []byte) {
+ if len(data) > b.size {
+ panic(fmt.Errorf("len(data) (%d) larger than len(content) (%d)", len(data), b.size))
+ }
+ if b.buffer == 0 {
+ copy(b.store, data)
+ return
+ }
+ buf, off := b.backend.stagingBuffer(len(data))
+ store := bufferSlice(buf, off, len(data))
+ copy(store, data)
+ enc := b.backend.startBlit()
+ C.blitEncCopyBufferToBuffer(enc, buf, b.buffer, C.NSUInteger(off), 0, C.NSUInteger(len(store)))
+}
+
+func bufferStore(buf C.CFTypeRef) []byte {
+ contents := C.bufferContents(buf)
+ return (*(*[1 << 30]byte)(contents.addr))[:contents.size:contents.size]
+}
+
+func bufferSlice(buf C.CFTypeRef, off, len int) []byte {
+ store := bufferStore(buf)
+ return store[off : off+len]
+}
+
+func (b *Buffer) Release() {
+ if b.buffer != 0 {
+ C.CFRelease(b.buffer)
+ }
+ *b = Buffer{}
+}
+
+func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
+ if len(pixels) == 0 {
+ return nil
+ }
+ sz := src.Size()
+ orig := C.MTLOrigin{
+ x: C.NSUInteger(src.Min.X),
+ y: C.NSUInteger(src.Min.Y),
+ }
+ msize := C.MTLSize{
+ width: C.NSUInteger(sz.X),
+ height: C.NSUInteger(sz.Y),
+ depth: 1,
+ }
+ stageStride := sz.X * 4
+ n := sz.Y * stageStride
+ buf, off := t.backend.stagingBuffer(n)
+ enc := t.backend.startBlit()
+ C.blitEncCopyTextureToBuffer(enc, t.texture, buf, C.NSUInteger(off), C.NSUInteger(stageStride), C.NSUInteger(n), msize, orig)
+ t.backend.endCmdBuffer(true)
+ store := bufferSlice(buf, off, n)
+ var srcOff, dstOff int
+ for y := 0; y < sz.Y; y++ {
+ dstRow := pixels[srcOff : srcOff+stageStride]
+ srcRow := store[dstOff : dstOff+stageStride]
+ copy(dstRow, srcRow)
+ dstOff += stageStride
+ srcOff += stride
+ }
+ return nil
+}
+
+func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
+ b.endEncoder()
+ b.ensureCmdBuffer()
+ f := tex.(*Texture)
+ col := d.ClearColor
+ var act C.MTLLoadAction
+ switch d.Action {
+ case driver.LoadActionKeep:
+ act = C.MTLLoadActionLoad
+ case driver.LoadActionClear:
+ act = C.MTLLoadActionClear
+ case driver.LoadActionInvalidate:
+ act = C.MTLLoadActionDontCare
+ }
+ b.renderEnc = C.cmdBufferRenderEncoder(b.cmdBuffer, f.texture, act, C.float(col.R), C.float(col.G), C.float(col.B), C.float(col.A))
+ if b.renderEnc == 0 {
+ panic("metal: [MTLCommandBuffer renderCommandEncoderWithDescriptor:] failed")
+ }
+}
+
+func (b *Backend) EndRenderPass() {
+ if b.renderEnc == 0 {
+ panic("no active render pass")
+ }
+ C.renderEncEnd(b.renderEnc)
+ C.CFRelease(b.renderEnc)
+ b.renderEnc = 0
+}
+
+func (b *Backend) endEncoder() {
+ if b.renderEnc != 0 {
+ panic("active render pass")
+ }
+ if b.computeEnc != 0 {
+ panic("active compute pass")
+ }
+ if b.blitEnc != 0 {
+ C.blitEncEnd(b.blitEnc)
+ C.CFRelease(b.blitEnc)
+ b.blitEnc = 0
+ }
+}
+
+func (f *Texture) ImplementsRenderTarget() {}
diff --git a/vendor/gioui.org/gpu/internal/opengl/opengl.go b/vendor/gioui.org/gpu/internal/opengl/opengl.go
new file mode 100644
index 0000000..ef89197
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/opengl/opengl.go
@@ -0,0 +1,1357 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package opengl
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "strings"
+ "time"
+ "unsafe"
+
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/gl"
+ "gioui.org/shader"
+)
+
+// Backend implements driver.Device.
+type Backend struct {
+ funcs *gl.Functions
+
+ clear bool
+ glstate glState
+ state state
+ savedState glState
+ sharedCtx bool
+
+ glver [2]int
+ gles bool
+ feats driver.Caps
+ // floatTriple holds the settings for floating point
+ // textures.
+ floatTriple textureTriple
+ // Single channel alpha textures.
+ alphaTriple textureTriple
+ srgbaTriple textureTriple
+ storage [storageBindings]*buffer
+
+ outputFBO gl.Framebuffer
+ sRGBFBO *SRGBFBO
+
+ // vertArray is bound during a frame. We don't need it, but
+ // core desktop OpenGL profile 3.3 requires some array bound.
+ vertArray gl.VertexArray
+}
+
+// State tracking.
+type glState struct {
+ drawFBO gl.Framebuffer
+ readFBO gl.Framebuffer
+ renderBuf gl.Renderbuffer
+ vertAttribs [5]struct {
+ obj gl.Buffer
+ enabled bool
+ size int
+ typ gl.Enum
+ normalized bool
+ stride int
+ offset uintptr
+ }
+ prog gl.Program
+ texUnits struct {
+ active gl.Enum
+ binds [2]gl.Texture
+ }
+ arrayBuf gl.Buffer
+ elemBuf gl.Buffer
+ uniBuf gl.Buffer
+ uniBufs [2]gl.Buffer
+ storeBuf gl.Buffer
+ storeBufs [4]gl.Buffer
+ vertArray gl.VertexArray
+ srgb bool
+ blend struct {
+ enable bool
+ srcRGB, dstRGB gl.Enum
+ srcA, dstA gl.Enum
+ }
+ clearColor [4]float32
+ viewport [4]int
+ unpack_row_length int
+ pack_row_length int
+}
+
+type state struct {
+ pipeline *pipeline
+ buffer bufferBinding
+}
+
+type bufferBinding struct {
+ obj gl.Buffer
+ offset int
+}
+
+type timer struct {
+ funcs *gl.Functions
+ obj gl.Query
+}
+
+type texture struct {
+ backend *Backend
+ obj gl.Texture
+ fbo gl.Framebuffer
+ hasFBO bool
+ triple textureTriple
+ width int
+ height int
+ bindings driver.BufferBinding
+ foreign bool
+}
+
+type pipeline struct {
+ prog *program
+ inputs []shader.InputLocation
+ layout driver.VertexLayout
+ blend driver.BlendDesc
+ topology driver.Topology
+}
+
+type buffer struct {
+ backend *Backend
+ hasBuffer bool
+ obj gl.Buffer
+ typ driver.BufferBinding
+ size int
+ immutable bool
+ // For emulation of uniform buffers.
+ data []byte
+}
+
+type glshader struct {
+ backend *Backend
+ obj gl.Shader
+ src shader.Sources
+}
+
+type program struct {
+ backend *Backend
+ obj gl.Program
+ vertUniforms uniforms
+ fragUniforms uniforms
+}
+
+type uniforms struct {
+ locs []uniformLocation
+ size int
+}
+
+type uniformLocation struct {
+ uniform gl.Uniform
+ offset int
+ typ shader.DataType
+ size int
+}
+
+type inputLayout struct {
+ inputs []shader.InputLocation
+ layout []driver.InputDesc
+}
+
+// textureTriple holds the type settings for
+// a TexImage2D call.
+type textureTriple struct {
+ internalFormat gl.Enum
+ format gl.Enum
+ typ gl.Enum
+}
+
+const (
+ storageBindings = 32
+)
+
+func init() {
+ driver.NewOpenGLDevice = newOpenGLDevice
+}
+
+// Supporting compute programs is theoretically possible with OpenGL ES 3.1. In
+// practice, there are too many driver issues, especially on Android (e.g.
+// Google Pixel, Samsung J2 are both broken i different ways). Disable support
+// and rely on Vulkan for devices that support it, and the CPU fallback for
+// devices that don't.
+const brokenGLES31 = true
+
+func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) {
+ f, err := gl.NewFunctions(api.Context, api.ES)
+ if err != nil {
+ return nil, err
+ }
+ exts := strings.Split(f.GetString(gl.EXTENSIONS), " ")
+ glVer := f.GetString(gl.VERSION)
+ ver, gles, err := gl.ParseGLVersion(glVer)
+ if err != nil {
+ return nil, err
+ }
+ floatTriple, ffboErr := floatTripleFor(f, ver, exts)
+ srgbaTriple, srgbErr := srgbaTripleFor(ver, exts)
+ gles31 := gles && (ver[0] > 3 || (ver[0] == 3 && ver[1] >= 1))
+ b := &Backend{
+ glver: ver,
+ gles: gles,
+ funcs: f,
+ floatTriple: floatTriple,
+ alphaTriple: alphaTripleFor(ver),
+ srgbaTriple: srgbaTriple,
+ sharedCtx: api.Shared,
+ }
+ b.feats.BottomLeftOrigin = true
+ if srgbErr == nil {
+ b.feats.Features |= driver.FeatureSRGB
+ }
+ if ffboErr == nil {
+ b.feats.Features |= driver.FeatureFloatRenderTargets
+ }
+ if gles31 && !brokenGLES31 {
+ b.feats.Features |= driver.FeatureCompute
+ }
+ if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") {
+ b.feats.Features |= driver.FeatureTimers
+ }
+ b.feats.MaxTextureSize = f.GetInteger(gl.MAX_TEXTURE_SIZE)
+ if !b.sharedCtx {
+ // We have exclusive access to the context, so query the GL state once
+ // instead of at each frame.
+ b.glstate = b.queryState()
+ }
+ return b, nil
+}
+
+func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
+ b.clear = clear
+ if b.sharedCtx {
+ b.glstate = b.queryState()
+ b.savedState = b.glstate
+ }
+ b.state = state{}
+ var renderFBO gl.Framebuffer
+ if target != nil {
+ switch t := target.(type) {
+ case driver.OpenGLRenderTarget:
+ renderFBO = gl.Framebuffer(t)
+ case *texture:
+ renderFBO = t.ensureFBO()
+ default:
+ panic(fmt.Errorf("opengl: invalid render target type: %T", target))
+ }
+ }
+ b.outputFBO = renderFBO
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO)
+ if b.gles {
+ // If the output framebuffer is not in the sRGB colorspace already, emulate it.
+ var fbEncoding int
+ if !renderFBO.Valid() {
+ fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)
+ } else {
+ fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)
+ }
+ if fbEncoding == gl.LINEAR && viewport != (image.Point{}) {
+ if b.sRGBFBO == nil {
+ sfbo, err := NewSRGBFBO(b.funcs, &b.glstate)
+ if err != nil {
+ panic(err)
+ }
+ b.sRGBFBO = sfbo
+ }
+ if err := b.sRGBFBO.Refresh(viewport); err != nil {
+ panic(err)
+ }
+ renderFBO = b.sRGBFBO.Framebuffer()
+ } else if b.sRGBFBO != nil {
+ b.sRGBFBO.Release()
+ b.sRGBFBO = nil
+ }
+ } else {
+ b.glstate.set(b.funcs, gl.FRAMEBUFFER_SRGB, true)
+ if !b.vertArray.Valid() {
+ b.vertArray = b.funcs.CreateVertexArray()
+ }
+ b.glstate.bindVertexArray(b.funcs, b.vertArray)
+ }
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO)
+ if b.sRGBFBO != nil && !clear {
+ b.clearOutput(0, 0, 0, 0)
+ }
+ return &texture{backend: b, fbo: renderFBO, hasFBO: true, foreign: true}
+}
+
+func (b *Backend) EndFrame() {
+ if b.sRGBFBO != nil {
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, b.outputFBO)
+ if b.clear {
+ b.SetBlend(false)
+ } else {
+ b.BlendFunc(driver.BlendFactorOne, driver.BlendFactorOneMinusSrcAlpha)
+ b.SetBlend(true)
+ }
+ b.sRGBFBO.Blit()
+ }
+ if b.sharedCtx {
+ b.restoreState(b.savedState)
+ }
+}
+
+func (b *Backend) queryState() glState {
+ s := glState{
+ prog: gl.Program(b.funcs.GetBinding(gl.CURRENT_PROGRAM)),
+ arrayBuf: gl.Buffer(b.funcs.GetBinding(gl.ARRAY_BUFFER_BINDING)),
+ elemBuf: gl.Buffer(b.funcs.GetBinding(gl.ELEMENT_ARRAY_BUFFER_BINDING)),
+ drawFBO: gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING)),
+ clearColor: b.funcs.GetFloat4(gl.COLOR_CLEAR_VALUE),
+ viewport: b.funcs.GetInteger4(gl.VIEWPORT),
+ unpack_row_length: b.funcs.GetInteger(gl.UNPACK_ROW_LENGTH),
+ pack_row_length: b.funcs.GetInteger(gl.PACK_ROW_LENGTH),
+ }
+ s.blend.enable = b.funcs.IsEnabled(gl.BLEND)
+ s.blend.srcRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_RGB))
+ s.blend.dstRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_RGB))
+ s.blend.srcA = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_ALPHA))
+ s.blend.dstA = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_ALPHA))
+ s.texUnits.active = gl.Enum(b.funcs.GetInteger(gl.ACTIVE_TEXTURE))
+ if !b.gles {
+ s.srgb = b.funcs.IsEnabled(gl.FRAMEBUFFER_SRGB)
+ }
+ if !b.gles || b.glver[0] >= 3 {
+ s.vertArray = gl.VertexArray(b.funcs.GetBinding(gl.VERTEX_ARRAY_BINDING))
+ s.readFBO = gl.Framebuffer(b.funcs.GetBinding(gl.READ_FRAMEBUFFER_BINDING))
+ s.uniBuf = gl.Buffer(b.funcs.GetBinding(gl.UNIFORM_BUFFER_BINDING))
+ for i := range s.uniBufs {
+ s.uniBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.UNIFORM_BUFFER_BINDING, i))
+ }
+ }
+ if b.gles && (b.glver[0] > 3 || (b.glver[0] == 3 && b.glver[1] >= 1)) {
+ s.storeBuf = gl.Buffer(b.funcs.GetBinding(gl.SHADER_STORAGE_BUFFER_BINDING))
+ for i := range s.storeBufs {
+ s.storeBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.SHADER_STORAGE_BUFFER_BINDING, i))
+ }
+ }
+ for i := range s.texUnits.binds {
+ s.activeTexture(b.funcs, gl.TEXTURE0+gl.Enum(i))
+ s.texUnits.binds[i] = gl.Texture(b.funcs.GetBinding(gl.TEXTURE_BINDING_2D))
+ }
+ for i := range s.vertAttribs {
+ a := &s.vertAttribs[i]
+ a.enabled = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED) != gl.FALSE
+ a.obj = gl.Buffer(b.funcs.GetVertexAttribBinding(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED))
+ a.size = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_SIZE)
+ a.typ = gl.Enum(b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_TYPE))
+ a.normalized = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED) != gl.FALSE
+ a.stride = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_STRIDE)
+ a.offset = b.funcs.GetVertexAttribPointer(i, gl.VERTEX_ATTRIB_ARRAY_POINTER)
+ }
+ return s
+}
+
+func (b *Backend) restoreState(dst glState) {
+ src := b.glstate
+ f := b.funcs
+ for i, unit := range dst.texUnits.binds {
+ src.bindTexture(f, i, unit)
+ }
+ src.activeTexture(f, dst.texUnits.active)
+ src.bindFramebuffer(f, gl.FRAMEBUFFER, dst.drawFBO)
+ src.bindFramebuffer(f, gl.READ_FRAMEBUFFER, dst.readFBO)
+ src.set(f, gl.BLEND, dst.blend.enable)
+ bf := dst.blend
+ src.setBlendFuncSeparate(f, bf.srcRGB, bf.dstRGB, bf.srcA, bf.dstA)
+ src.set(f, gl.FRAMEBUFFER_SRGB, dst.srgb)
+ src.bindVertexArray(f, dst.vertArray)
+ src.useProgram(f, dst.prog)
+ src.bindBuffer(f, gl.ELEMENT_ARRAY_BUFFER, dst.elemBuf)
+ for i, b := range dst.uniBufs {
+ src.bindBufferBase(f, gl.UNIFORM_BUFFER, i, b)
+ }
+ src.bindBuffer(f, gl.UNIFORM_BUFFER, dst.uniBuf)
+ for i, b := range dst.storeBufs {
+ src.bindBufferBase(f, gl.SHADER_STORAGE_BUFFER, i, b)
+ }
+ src.bindBuffer(f, gl.SHADER_STORAGE_BUFFER, dst.storeBuf)
+ col := dst.clearColor
+ src.setClearColor(f, col[0], col[1], col[2], col[3])
+ for i, attr := range dst.vertAttribs {
+ src.setVertexAttribArray(f, i, attr.enabled)
+ src.vertexAttribPointer(f, attr.obj, i, attr.size, attr.typ, attr.normalized, attr.stride, int(attr.offset))
+ }
+ src.bindBuffer(f, gl.ARRAY_BUFFER, dst.arrayBuf)
+ v := dst.viewport
+ src.setViewport(f, v[0], v[1], v[2], v[3])
+ src.pixelStorei(f, gl.UNPACK_ROW_LENGTH, dst.unpack_row_length)
+ src.pixelStorei(f, gl.PACK_ROW_LENGTH, dst.pack_row_length)
+}
+
+func (s *glState) setVertexAttribArray(f *gl.Functions, idx int, enabled bool) {
+ a := &s.vertAttribs[idx]
+ if enabled != a.enabled {
+ if enabled {
+ f.EnableVertexAttribArray(gl.Attrib(idx))
+ } else {
+ f.DisableVertexAttribArray(gl.Attrib(idx))
+ }
+ a.enabled = enabled
+ }
+}
+
+func (s *glState) vertexAttribPointer(f *gl.Functions, buf gl.Buffer, idx, size int, typ gl.Enum, normalized bool, stride, offset int) {
+ s.bindBuffer(f, gl.ARRAY_BUFFER, buf)
+ a := &s.vertAttribs[idx]
+ a.obj = buf
+ a.size = size
+ a.typ = typ
+ a.normalized = normalized
+ a.stride = stride
+ a.offset = uintptr(offset)
+ f.VertexAttribPointer(gl.Attrib(idx), a.size, a.typ, a.normalized, a.stride, int(a.offset))
+}
+
+func (s *glState) activeTexture(f *gl.Functions, unit gl.Enum) {
+ if unit != s.texUnits.active {
+ f.ActiveTexture(unit)
+ s.texUnits.active = unit
+ }
+}
+
+func (s *glState) bindRenderbuffer(f *gl.Functions, target gl.Enum, r gl.Renderbuffer) {
+ if !r.Equal(s.renderBuf) {
+ f.BindRenderbuffer(gl.RENDERBUFFER, r)
+ s.renderBuf = r
+ }
+}
+
+func (s *glState) bindTexture(f *gl.Functions, unit int, t gl.Texture) {
+ s.activeTexture(f, gl.TEXTURE0+gl.Enum(unit))
+ if !t.Equal(s.texUnits.binds[unit]) {
+ f.BindTexture(gl.TEXTURE_2D, t)
+ s.texUnits.binds[unit] = t
+ }
+}
+
+func (s *glState) bindVertexArray(f *gl.Functions, a gl.VertexArray) {
+ if !a.Equal(s.vertArray) {
+ f.BindVertexArray(a)
+ s.vertArray = a
+ }
+}
+
+func (s *glState) deleteRenderbuffer(f *gl.Functions, r gl.Renderbuffer) {
+ f.DeleteRenderbuffer(r)
+ if r.Equal(s.renderBuf) {
+ s.renderBuf = gl.Renderbuffer{}
+ }
+}
+
+func (s *glState) deleteFramebuffer(f *gl.Functions, fbo gl.Framebuffer) {
+ f.DeleteFramebuffer(fbo)
+ if fbo.Equal(s.drawFBO) {
+ s.drawFBO = gl.Framebuffer{}
+ }
+ if fbo.Equal(s.readFBO) {
+ s.readFBO = gl.Framebuffer{}
+ }
+}
+
+func (s *glState) deleteBuffer(f *gl.Functions, b gl.Buffer) {
+ f.DeleteBuffer(b)
+ if b.Equal(s.arrayBuf) {
+ s.arrayBuf = gl.Buffer{}
+ }
+ if b.Equal(s.elemBuf) {
+ s.elemBuf = gl.Buffer{}
+ }
+ if b.Equal(s.uniBuf) {
+ s.uniBuf = gl.Buffer{}
+ }
+ if b.Equal(s.storeBuf) {
+ s.uniBuf = gl.Buffer{}
+ }
+ for i, b2 := range s.storeBufs {
+ if b.Equal(b2) {
+ s.storeBufs[i] = gl.Buffer{}
+ }
+ }
+ for i, b2 := range s.uniBufs {
+ if b.Equal(b2) {
+ s.uniBufs[i] = gl.Buffer{}
+ }
+ }
+}
+
+func (s *glState) deleteProgram(f *gl.Functions, p gl.Program) {
+ f.DeleteProgram(p)
+ if p.Equal(s.prog) {
+ s.prog = gl.Program{}
+ }
+}
+
+func (s *glState) deleteVertexArray(f *gl.Functions, a gl.VertexArray) {
+ f.DeleteVertexArray(a)
+ if a.Equal(s.vertArray) {
+ s.vertArray = gl.VertexArray{}
+ }
+}
+
+func (s *glState) deleteTexture(f *gl.Functions, t gl.Texture) {
+ f.DeleteTexture(t)
+ binds := &s.texUnits.binds
+ for i, obj := range binds {
+ if t.Equal(obj) {
+ binds[i] = gl.Texture{}
+ }
+ }
+}
+
+func (s *glState) useProgram(f *gl.Functions, p gl.Program) {
+ if !p.Equal(s.prog) {
+ f.UseProgram(p)
+ s.prog = p
+ }
+}
+
+func (s *glState) bindFramebuffer(f *gl.Functions, target gl.Enum, fbo gl.Framebuffer) {
+ switch target {
+ case gl.FRAMEBUFFER:
+ if fbo.Equal(s.drawFBO) && fbo.Equal(s.readFBO) {
+ return
+ }
+ s.drawFBO = fbo
+ s.readFBO = fbo
+ case gl.READ_FRAMEBUFFER:
+ if fbo.Equal(s.readFBO) {
+ return
+ }
+ s.readFBO = fbo
+ case gl.DRAW_FRAMEBUFFER:
+ if fbo.Equal(s.drawFBO) {
+ return
+ }
+ s.drawFBO = fbo
+ default:
+ panic("unknown target")
+ }
+ f.BindFramebuffer(target, fbo)
+}
+
+func (s *glState) bindBufferBase(f *gl.Functions, target gl.Enum, idx int, buf gl.Buffer) {
+ switch target {
+ case gl.UNIFORM_BUFFER:
+ if buf.Equal(s.uniBuf) && buf.Equal(s.uniBufs[idx]) {
+ return
+ }
+ s.uniBuf = buf
+ s.uniBufs[idx] = buf
+ case gl.SHADER_STORAGE_BUFFER:
+ if buf.Equal(s.storeBuf) && buf.Equal(s.storeBufs[idx]) {
+ return
+ }
+ s.storeBuf = buf
+ s.storeBufs[idx] = buf
+ default:
+ panic("unknown buffer target")
+ }
+ f.BindBufferBase(target, idx, buf)
+}
+
+func (s *glState) bindBuffer(f *gl.Functions, target gl.Enum, buf gl.Buffer) {
+ switch target {
+ case gl.ARRAY_BUFFER:
+ if buf.Equal(s.arrayBuf) {
+ return
+ }
+ s.arrayBuf = buf
+ case gl.ELEMENT_ARRAY_BUFFER:
+ if buf.Equal(s.elemBuf) {
+ return
+ }
+ s.elemBuf = buf
+ case gl.UNIFORM_BUFFER:
+ if buf.Equal(s.uniBuf) {
+ return
+ }
+ s.uniBuf = buf
+ case gl.SHADER_STORAGE_BUFFER:
+ if buf.Equal(s.storeBuf) {
+ return
+ }
+ s.storeBuf = buf
+ default:
+ panic("unknown buffer target")
+ }
+ f.BindBuffer(target, buf)
+}
+
+func (s *glState) pixelStorei(f *gl.Functions, pname gl.Enum, val int) {
+ switch pname {
+ case gl.UNPACK_ROW_LENGTH:
+ if val == s.unpack_row_length {
+ return
+ }
+ s.unpack_row_length = val
+ case gl.PACK_ROW_LENGTH:
+ if val == s.pack_row_length {
+ return
+ }
+ s.pack_row_length = val
+ default:
+ panic("unsupported PixelStorei pname")
+ }
+ f.PixelStorei(pname, val)
+}
+
+func (s *glState) setClearColor(f *gl.Functions, r, g, b, a float32) {
+ col := [4]float32{r, g, b, a}
+ if col != s.clearColor {
+ f.ClearColor(r, g, b, a)
+ s.clearColor = col
+ }
+}
+
+func (s *glState) setViewport(f *gl.Functions, x, y, width, height int) {
+ view := [4]int{x, y, width, height}
+ if view != s.viewport {
+ f.Viewport(x, y, width, height)
+ s.viewport = view
+ }
+}
+
+func (s *glState) setBlendFuncSeparate(f *gl.Functions, srcRGB, dstRGB, srcA, dstA gl.Enum) {
+ if srcRGB != s.blend.srcRGB || dstRGB != s.blend.dstRGB || srcA != s.blend.srcA || dstA != s.blend.dstA {
+ s.blend.srcRGB = srcRGB
+ s.blend.dstRGB = dstRGB
+ s.blend.srcA = srcA
+ s.blend.dstA = dstA
+ f.BlendFuncSeparate(srcA, dstA, srcA, dstA)
+ }
+}
+
+func (s *glState) set(f *gl.Functions, target gl.Enum, enable bool) {
+ switch target {
+ case gl.FRAMEBUFFER_SRGB:
+ if s.srgb == enable {
+ return
+ }
+ s.srgb = enable
+ case gl.BLEND:
+ if enable == s.blend.enable {
+ return
+ }
+ s.blend.enable = enable
+ default:
+ panic("unknown enable")
+ }
+ if enable {
+ f.Enable(target)
+ } else {
+ f.Disable(target)
+ }
+}
+
+func (b *Backend) Caps() driver.Caps {
+ return b.feats
+}
+
+func (b *Backend) NewTimer() driver.Timer {
+ return &timer{
+ funcs: b.funcs,
+ obj: b.funcs.CreateQuery(),
+ }
+}
+
+func (b *Backend) IsTimeContinuous() bool {
+ return b.funcs.GetInteger(gl.GPU_DISJOINT_EXT) == gl.FALSE
+}
+
+func (t *texture) ensureFBO() gl.Framebuffer {
+ if t.hasFBO {
+ return t.fbo
+ }
+ b := t.backend
+ oldFBO := b.glstate.drawFBO
+ defer func() {
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, oldFBO)
+ }()
+ glErr(b.funcs)
+ fb := b.funcs.CreateFramebuffer()
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fb)
+ if err := glErr(b.funcs); err != nil {
+ b.funcs.DeleteFramebuffer(fb)
+ panic(err)
+ }
+ b.funcs.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t.obj, 0)
+ if st := b.funcs.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
+ b.funcs.DeleteFramebuffer(fb)
+ panic(fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError()))
+ }
+ t.fbo = fb
+ t.hasFBO = true
+ return fb
+}
+
+func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, binding driver.BufferBinding) (driver.Texture, error) {
+ glErr(b.funcs)
+ tex := &texture{backend: b, obj: b.funcs.CreateTexture(), width: width, height: height, bindings: binding}
+ switch format {
+ case driver.TextureFormatFloat:
+ tex.triple = b.floatTriple
+ case driver.TextureFormatSRGBA:
+ tex.triple = b.srgbaTriple
+ case driver.TextureFormatRGBA8:
+ tex.triple = textureTriple{gl.RGBA8, gl.RGBA, gl.UNSIGNED_BYTE}
+ default:
+ return nil, errors.New("unsupported texture format")
+ }
+ b.BindTexture(0, tex)
+ b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, toTexFilter(magFilter))
+ b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, toTexFilter(minFilter))
+ b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
+ b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
+ if b.gles && b.glver[0] >= 3 {
+ // Immutable textures are required for BindImageTexture, and can't hurt otherwise.
+ b.funcs.TexStorage2D(gl.TEXTURE_2D, 1, tex.triple.internalFormat, width, height)
+ } else {
+ b.funcs.TexImage2D(gl.TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ)
+ }
+ if err := glErr(b.funcs); err != nil {
+ tex.Release()
+ return nil, err
+ }
+ return tex, nil
+}
+
+func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
+ glErr(b.funcs)
+ buf := &buffer{backend: b, typ: typ, size: size}
+ if typ&driver.BufferBindingUniforms != 0 {
+ if typ != driver.BufferBindingUniforms {
+ return nil, errors.New("uniforms buffers cannot be bound as anything else")
+ }
+ buf.data = make([]byte, size)
+ }
+ if typ&^driver.BufferBindingUniforms != 0 {
+ buf.hasBuffer = true
+ buf.obj = b.funcs.CreateBuffer()
+ if err := glErr(b.funcs); err != nil {
+ buf.Release()
+ return nil, err
+ }
+ firstBinding := firstBufferType(typ)
+ b.glstate.bindBuffer(b.funcs, firstBinding, buf.obj)
+ b.funcs.BufferData(firstBinding, size, gl.DYNAMIC_DRAW, nil)
+ }
+ return buf, nil
+}
+
+func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
+ glErr(b.funcs)
+ obj := b.funcs.CreateBuffer()
+ buf := &buffer{backend: b, obj: obj, typ: typ, size: len(data), hasBuffer: true}
+ firstBinding := firstBufferType(typ)
+ b.glstate.bindBuffer(b.funcs, firstBinding, buf.obj)
+ b.funcs.BufferData(firstBinding, len(data), gl.STATIC_DRAW, data)
+ buf.immutable = true
+ if err := glErr(b.funcs); err != nil {
+ buf.Release()
+ return nil, err
+ }
+ return buf, nil
+}
+
+func glErr(f *gl.Functions) error {
+ if st := f.GetError(); st != gl.NO_ERROR {
+ return fmt.Errorf("glGetError: %#x", st)
+ }
+ return nil
+}
+
+func (b *Backend) Release() {
+ if b.sRGBFBO != nil {
+ b.sRGBFBO.Release()
+ }
+ if b.vertArray.Valid() {
+ b.glstate.deleteVertexArray(b.funcs, b.vertArray)
+ }
+ *b = Backend{}
+}
+
+func (b *Backend) DispatchCompute(x, y, z int) {
+ for binding, buf := range b.storage {
+ if buf != nil {
+ b.glstate.bindBufferBase(b.funcs, gl.SHADER_STORAGE_BUFFER, binding, buf.obj)
+ }
+ }
+ b.funcs.DispatchCompute(x, y, z)
+ b.funcs.MemoryBarrier(gl.ALL_BARRIER_BITS)
+}
+
+func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
+ t := tex.(*texture)
+ var acc gl.Enum
+ switch t.bindings & (driver.BufferBindingShaderStorageRead | driver.BufferBindingShaderStorageWrite) {
+ case driver.BufferBindingShaderStorageRead:
+ acc = gl.READ_ONLY
+ case driver.BufferBindingShaderStorageWrite:
+ acc = gl.WRITE_ONLY
+ case driver.BufferBindingShaderStorageRead | driver.BufferBindingShaderStorageWrite:
+ acc = gl.READ_WRITE
+ default:
+ panic("unsupported access bits")
+ }
+ b.funcs.BindImageTexture(unit, t.obj, 0, false, 0, acc, t.triple.internalFormat)
+}
+
+func (b *Backend) BlendFunc(sfactor, dfactor driver.BlendFactor) {
+ src, dst := toGLBlendFactor(sfactor), toGLBlendFactor(dfactor)
+ b.glstate.setBlendFuncSeparate(b.funcs, src, dst, src, dst)
+}
+
+func toGLBlendFactor(f driver.BlendFactor) gl.Enum {
+ switch f {
+ case driver.BlendFactorOne:
+ return gl.ONE
+ case driver.BlendFactorOneMinusSrcAlpha:
+ return gl.ONE_MINUS_SRC_ALPHA
+ case driver.BlendFactorZero:
+ return gl.ZERO
+ case driver.BlendFactorDstColor:
+ return gl.DST_COLOR
+ default:
+ panic("unsupported blend factor")
+ }
+}
+
+func (b *Backend) SetBlend(enable bool) {
+ b.glstate.set(b.funcs, gl.BLEND, enable)
+}
+
+func (b *Backend) DrawElements(off, count int) {
+ b.prepareDraw()
+ // off is in 16-bit indices, but DrawElements take a byte offset.
+ byteOff := off * 2
+ b.funcs.DrawElements(toGLDrawMode(b.state.pipeline.topology), count, gl.UNSIGNED_SHORT, byteOff)
+}
+
+func (b *Backend) DrawArrays(off, count int) {
+ b.prepareDraw()
+ b.funcs.DrawArrays(toGLDrawMode(b.state.pipeline.topology), off, count)
+}
+
+func (b *Backend) prepareDraw() {
+ p := b.state.pipeline
+ if p == nil {
+ return
+ }
+ b.setupVertexArrays()
+}
+
+func toGLDrawMode(mode driver.Topology) gl.Enum {
+ switch mode {
+ case driver.TopologyTriangleStrip:
+ return gl.TRIANGLE_STRIP
+ case driver.TopologyTriangles:
+ return gl.TRIANGLES
+ default:
+ panic("unsupported draw mode")
+ }
+}
+
+func (b *Backend) Viewport(x, y, width, height int) {
+ b.glstate.setViewport(b.funcs, x, y, width, height)
+}
+
+func (b *Backend) clearOutput(colR, colG, colB, colA float32) {
+ b.glstate.setClearColor(b.funcs, colR, colG, colB, colA)
+ b.funcs.Clear(gl.COLOR_BUFFER_BIT)
+}
+
+func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) {
+ // We don't support ES 3.1 compute, see brokenGLES31 above.
+ const GLES31Source = ""
+ p, err := gl.CreateComputeProgram(b.funcs, GLES31Source)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %v", src.Name, err)
+ }
+ return &program{
+ backend: b,
+ obj: p,
+ }, nil
+}
+
+func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
+ glslSrc := b.glslFor(src)
+ sh, err := gl.CreateShader(b.funcs, gl.VERTEX_SHADER, glslSrc)
+ return &glshader{backend: b, obj: sh, src: src}, err
+}
+
+func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
+ glslSrc := b.glslFor(src)
+ sh, err := gl.CreateShader(b.funcs, gl.FRAGMENT_SHADER, glslSrc)
+ return &glshader{backend: b, obj: sh, src: src}, err
+}
+
+func (b *Backend) glslFor(src shader.Sources) string {
+ if b.gles {
+ return src.GLSL100ES
+ } else {
+ return src.GLSL150
+ }
+}
+
+func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
+ p, err := b.newProgram(desc)
+ if err != nil {
+ return nil, err
+ }
+ layout := desc.VertexLayout
+ vsrc := desc.VertexShader.(*glshader).src
+ if len(vsrc.Inputs) != len(layout.Inputs) {
+ return nil, fmt.Errorf("opengl: got %d inputs, expected %d", len(layout.Inputs), len(vsrc.Inputs))
+ }
+ for i, inp := range vsrc.Inputs {
+ if exp, got := inp.Size, layout.Inputs[i].Size; exp != got {
+ return nil, fmt.Errorf("opengl: data size mismatch for %q: got %d expected %d", inp.Name, got, exp)
+ }
+ }
+ return &pipeline{
+ prog: p,
+ inputs: vsrc.Inputs,
+ layout: layout,
+ blend: desc.BlendDesc,
+ topology: desc.Topology,
+ }, nil
+}
+
+func (b *Backend) newProgram(desc driver.PipelineDesc) (*program, error) {
+ p := b.funcs.CreateProgram()
+ if !p.Valid() {
+ return nil, errors.New("opengl: glCreateProgram failed")
+ }
+ vsh, fsh := desc.VertexShader.(*glshader), desc.FragmentShader.(*glshader)
+ b.funcs.AttachShader(p, vsh.obj)
+ b.funcs.AttachShader(p, fsh.obj)
+ for _, inp := range vsh.src.Inputs {
+ b.funcs.BindAttribLocation(p, gl.Attrib(inp.Location), inp.Name)
+ }
+ b.funcs.LinkProgram(p)
+ if b.funcs.GetProgrami(p, gl.LINK_STATUS) == 0 {
+ log := b.funcs.GetProgramInfoLog(p)
+ b.funcs.DeleteProgram(p)
+ return nil, fmt.Errorf("opengl: program link failed: %s", strings.TrimSpace(log))
+ }
+ prog := &program{
+ backend: b,
+ obj: p,
+ }
+ b.glstate.useProgram(b.funcs, p)
+ // Bind texture uniforms.
+ for _, tex := range vsh.src.Textures {
+ u := b.funcs.GetUniformLocation(p, tex.Name)
+ if u.Valid() {
+ b.funcs.Uniform1i(u, tex.Binding)
+ }
+ }
+ for _, tex := range fsh.src.Textures {
+ u := b.funcs.GetUniformLocation(p, tex.Name)
+ if u.Valid() {
+ b.funcs.Uniform1i(u, tex.Binding)
+ }
+ }
+ prog.vertUniforms.setup(b.funcs, p, vsh.src.Uniforms.Size, vsh.src.Uniforms.Locations)
+ prog.fragUniforms.setup(b.funcs, p, fsh.src.Uniforms.Size, fsh.src.Uniforms.Locations)
+ return prog, nil
+}
+
+func (b *Backend) BindStorageBuffer(binding int, buf driver.Buffer) {
+ bf := buf.(*buffer)
+ if bf.typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) == 0 {
+ panic("not a shader storage buffer")
+ }
+ b.storage[binding] = bf
+}
+
+func (b *Backend) BindUniforms(buf driver.Buffer) {
+ bf := buf.(*buffer)
+ if bf.typ&driver.BufferBindingUniforms == 0 {
+ panic("not a uniform buffer")
+ }
+ b.state.pipeline.prog.vertUniforms.update(b.funcs, bf)
+ b.state.pipeline.prog.fragUniforms.update(b.funcs, bf)
+}
+
+func (b *Backend) BindProgram(prog driver.Program) {
+ p := prog.(*program)
+ b.glstate.useProgram(b.funcs, p.obj)
+}
+
+func (s *glshader) Release() {
+ s.backend.funcs.DeleteShader(s.obj)
+}
+
+func (p *program) Release() {
+ p.backend.glstate.deleteProgram(p.backend.funcs, p.obj)
+}
+
+func (u *uniforms) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []shader.UniformLocation) {
+ u.locs = make([]uniformLocation, len(uniforms))
+ for i, uniform := range uniforms {
+ loc := funcs.GetUniformLocation(p, uniform.Name)
+ u.locs[i] = uniformLocation{uniform: loc, offset: uniform.Offset, typ: uniform.Type, size: uniform.Size}
+ }
+ u.size = uniformSize
+}
+
+func (p *uniforms) update(funcs *gl.Functions, buf *buffer) {
+ if buf.size < p.size {
+ panic(fmt.Errorf("uniform buffer too small, got %d need %d", buf.size, p.size))
+ }
+ data := buf.data
+ for _, u := range p.locs {
+ if !u.uniform.Valid() {
+ continue
+ }
+ data := data[u.offset:]
+ switch {
+ case u.typ == shader.DataTypeFloat && u.size == 1:
+ data := data[:4]
+ v := *(*[1]float32)(unsafe.Pointer(&data[0]))
+ funcs.Uniform1f(u.uniform, v[0])
+ case u.typ == shader.DataTypeFloat && u.size == 2:
+ data := data[:8]
+ v := *(*[2]float32)(unsafe.Pointer(&data[0]))
+ funcs.Uniform2f(u.uniform, v[0], v[1])
+ case u.typ == shader.DataTypeFloat && u.size == 3:
+ data := data[:12]
+ v := *(*[3]float32)(unsafe.Pointer(&data[0]))
+ funcs.Uniform3f(u.uniform, v[0], v[1], v[2])
+ case u.typ == shader.DataTypeFloat && u.size == 4:
+ data := data[:16]
+ v := *(*[4]float32)(unsafe.Pointer(&data[0]))
+ funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3])
+ default:
+ panic("unsupported uniform data type or size")
+ }
+ }
+}
+
+func (b *buffer) Upload(data []byte) {
+ if b.immutable {
+ panic("immutable buffer")
+ }
+ if len(data) > b.size {
+ panic("buffer size overflow")
+ }
+ copy(b.data, data)
+ if b.hasBuffer {
+ firstBinding := firstBufferType(b.typ)
+ b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj)
+ if len(data) == b.size {
+ // the iOS GL implementation doesn't recognize when BufferSubData
+ // clears the entire buffer. Tell it and avoid GPU stalls.
+ // See also https://github.com/godotengine/godot/issues/23956.
+ b.backend.funcs.BufferData(firstBinding, b.size, gl.DYNAMIC_DRAW, data)
+ } else {
+ b.backend.funcs.BufferSubData(firstBinding, 0, data)
+ }
+ }
+}
+
+func (b *buffer) Download(data []byte) error {
+ if len(data) > b.size {
+ panic("buffer size overflow")
+ }
+ if !b.hasBuffer {
+ copy(data, b.data)
+ return nil
+ }
+ firstBinding := firstBufferType(b.typ)
+ b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj)
+ bufferMap := b.backend.funcs.MapBufferRange(firstBinding, 0, len(data), gl.MAP_READ_BIT)
+ if bufferMap == nil {
+ return fmt.Errorf("MapBufferRange: error %#x", b.backend.funcs.GetError())
+ }
+ copy(data, bufferMap)
+ if !b.backend.funcs.UnmapBuffer(firstBinding) {
+ return driver.ErrContentLost
+ }
+ return nil
+}
+
+func (b *buffer) Release() {
+ if b.hasBuffer {
+ b.backend.glstate.deleteBuffer(b.backend.funcs, b.obj)
+ b.hasBuffer = false
+ }
+}
+
+func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
+ gbuf := buf.(*buffer)
+ if gbuf.typ&driver.BufferBindingVertices == 0 {
+ panic("not a vertex buffer")
+ }
+ b.state.buffer = bufferBinding{obj: gbuf.obj, offset: offset}
+}
+
+func (b *Backend) setupVertexArrays() {
+ p := b.state.pipeline
+ inputs := p.inputs
+ if len(inputs) == 0 {
+ return
+ }
+ layout := p.layout
+ const max = len(b.glstate.vertAttribs)
+ var enabled [max]bool
+ buf := b.state.buffer
+ for i, inp := range inputs {
+ l := layout.Inputs[i]
+ var gltyp gl.Enum
+ switch l.Type {
+ case shader.DataTypeFloat:
+ gltyp = gl.FLOAT
+ case shader.DataTypeShort:
+ gltyp = gl.SHORT
+ default:
+ panic("unsupported data type")
+ }
+ enabled[inp.Location] = true
+ b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, p.layout.Stride, buf.offset+l.Offset)
+ }
+ for i := 0; i < max; i++ {
+ b.glstate.setVertexAttribArray(b.funcs, i, enabled[i])
+ }
+}
+
+func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
+ gbuf := buf.(*buffer)
+ if gbuf.typ&driver.BufferBindingIndices == 0 {
+ panic("not an index buffer")
+ }
+ b.glstate.bindBuffer(b.funcs, gl.ELEMENT_ARRAY_BUFFER, gbuf.obj)
+}
+
+func (b *Backend) CopyTexture(dst driver.Texture, dstOrigin image.Point, src driver.Texture, srcRect image.Rectangle) {
+ const unit = 0
+ oldTex := b.glstate.texUnits.binds[unit]
+ defer func() {
+ b.glstate.bindTexture(b.funcs, unit, oldTex)
+ }()
+ b.glstate.bindTexture(b.funcs, unit, dst.(*texture).obj)
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, src.(*texture).ensureFBO())
+ sz := srcRect.Size()
+ b.funcs.CopyTexSubImage2D(gl.TEXTURE_2D, 0, dstOrigin.X, dstOrigin.Y, srcRect.Min.X, srcRect.Min.Y, sz.X, sz.Y)
+}
+
+func (t *texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
+ glErr(t.backend.funcs)
+ t.backend.glstate.bindFramebuffer(t.backend.funcs, gl.FRAMEBUFFER, t.ensureFBO())
+ if len(pixels) < src.Dx()*src.Dy()*4 {
+ return errors.New("unexpected RGBA size")
+ }
+ w, h := src.Dx(), src.Dy()
+ // WebGL 1 doesn't support PACK_ROW_LENGTH != 0. Avoid it if possible.
+ rowLen := 0
+ if n := stride / 4; n != w {
+ rowLen = n
+ }
+ t.backend.glstate.pixelStorei(t.backend.funcs, gl.PACK_ROW_LENGTH, rowLen)
+ t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
+ return glErr(t.backend.funcs)
+}
+
+func (b *Backend) BindPipeline(pl driver.Pipeline) {
+ p := pl.(*pipeline)
+ b.state.pipeline = p
+ b.glstate.useProgram(b.funcs, p.prog.obj)
+ b.SetBlend(p.blend.Enable)
+ b.BlendFunc(p.blend.SrcFactor, p.blend.DstFactor)
+}
+
+func (b *Backend) BeginCompute() {
+ b.funcs.MemoryBarrier(gl.ALL_BARRIER_BITS)
+}
+
+func (b *Backend) EndCompute() {
+}
+
+func (b *Backend) BeginRenderPass(tex driver.Texture, desc driver.LoadDesc) {
+ fbo := tex.(*texture).ensureFBO()
+ b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo)
+ switch desc.Action {
+ case driver.LoadActionClear:
+ c := desc.ClearColor
+ b.clearOutput(c.R, c.G, c.B, c.A)
+ case driver.LoadActionInvalidate:
+ b.funcs.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
+ }
+}
+
+func (b *Backend) EndRenderPass() {
+}
+
+func (f *texture) ImplementsRenderTarget() {}
+
+func (p *pipeline) Release() {
+ p.prog.Release()
+ *p = pipeline{}
+}
+
+func toTexFilter(f driver.TextureFilter) int {
+ switch f {
+ case driver.FilterNearest:
+ return gl.NEAREST
+ case driver.FilterLinear:
+ return gl.LINEAR
+ default:
+ panic("unsupported texture filter")
+ }
+}
+
+func (b *Backend) PrepareTexture(tex driver.Texture) {}
+
+func (b *Backend) BindTexture(unit int, t driver.Texture) {
+ b.glstate.bindTexture(b.funcs, unit, t.(*texture).obj)
+}
+
+func (t *texture) Release() {
+ if t.foreign {
+ panic("texture not created by NewTexture")
+ }
+ if t.hasFBO {
+ t.backend.glstate.deleteFramebuffer(t.backend.funcs, t.fbo)
+ }
+ t.backend.glstate.deleteTexture(t.backend.funcs, t.obj)
+}
+
+func (t *texture) Upload(offset, size image.Point, pixels []byte, stride int) {
+ if min := size.X * size.Y * 4; min > len(pixels) {
+ panic(fmt.Errorf("size %d larger than data %d", min, len(pixels)))
+ }
+ t.backend.BindTexture(0, t)
+ // WebGL 1 doesn't support UNPACK_ROW_LENGTH != 0. Avoid it if possible.
+ rowLen := 0
+ if n := stride / 4; n != size.X {
+ rowLen = n
+ }
+ t.backend.glstate.pixelStorei(t.backend.funcs, gl.UNPACK_ROW_LENGTH, rowLen)
+ t.backend.funcs.TexSubImage2D(gl.TEXTURE_2D, 0, offset.X, offset.Y, size.X, size.Y, t.triple.format, t.triple.typ, pixels)
+}
+
+func (t *timer) Begin() {
+ t.funcs.BeginQuery(gl.TIME_ELAPSED_EXT, t.obj)
+}
+
+func (t *timer) End() {
+ t.funcs.EndQuery(gl.TIME_ELAPSED_EXT)
+}
+
+func (t *timer) ready() bool {
+ return t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT_AVAILABLE) == gl.TRUE
+}
+
+func (t *timer) Release() {
+ t.funcs.DeleteQuery(t.obj)
+}
+
+func (t *timer) Duration() (time.Duration, bool) {
+ if !t.ready() {
+ return 0, false
+ }
+ nanos := t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT)
+ return time.Duration(nanos), true
+}
+
+// floatTripleFor determines the best texture triple for floating point FBOs.
+func floatTripleFor(f *gl.Functions, ver [2]int, exts []string) (textureTriple, error) {
+ var triples []textureTriple
+ if ver[0] >= 3 {
+ triples = append(triples, textureTriple{gl.R16F, gl.Enum(gl.RED), gl.Enum(gl.HALF_FLOAT)})
+ }
+ // According to the OES_texture_half_float specification, EXT_color_buffer_half_float is needed to
+ // render to FBOs. However, the Safari WebGL1 implementation does support half-float FBOs but does not
+ // report EXT_color_buffer_half_float support. The triples are verified below, so it doesn't matter if we're
+ // wrong.
+ if hasExtension(exts, "GL_OES_texture_half_float") || hasExtension(exts, "GL_EXT_color_buffer_half_float") {
+ // Try single channel.
+ triples = append(triples, textureTriple{gl.LUMINANCE, gl.Enum(gl.LUMINANCE), gl.Enum(gl.HALF_FLOAT_OES)})
+ // Fallback to 4 channels.
+ triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.HALF_FLOAT_OES)})
+ }
+ if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") {
+ triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.FLOAT)})
+ }
+ tex := f.CreateTexture()
+ defer f.DeleteTexture(tex)
+ defTex := gl.Texture(f.GetBinding(gl.TEXTURE_BINDING_2D))
+ defer f.BindTexture(gl.TEXTURE_2D, defTex)
+ f.BindTexture(gl.TEXTURE_2D, tex)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
+ fbo := f.CreateFramebuffer()
+ defer f.DeleteFramebuffer(fbo)
+ defFBO := gl.Framebuffer(f.GetBinding(gl.FRAMEBUFFER_BINDING))
+ f.BindFramebuffer(gl.FRAMEBUFFER, fbo)
+ defer f.BindFramebuffer(gl.FRAMEBUFFER, defFBO)
+ var attempts []string
+ for _, tt := range triples {
+ const size = 256
+ f.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ)
+ f.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0)
+ st := f.CheckFramebufferStatus(gl.FRAMEBUFFER)
+ if st == gl.FRAMEBUFFER_COMPLETE {
+ return tt, nil
+ }
+ attempts = append(attempts, fmt.Sprintf("(0x%x, 0x%x, 0x%x): 0x%x", tt.internalFormat, tt.format, tt.typ, st))
+ }
+ return textureTriple{}, fmt.Errorf("floating point fbos not supported (attempted %s)", attempts)
+}
+
+func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) {
+ switch {
+ case ver[0] >= 3:
+ return textureTriple{gl.SRGB8_ALPHA8, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}, nil
+ case hasExtension(exts, "GL_EXT_sRGB"):
+ return textureTriple{gl.SRGB_ALPHA_EXT, gl.Enum(gl.SRGB_ALPHA_EXT), gl.Enum(gl.UNSIGNED_BYTE)}, nil
+ default:
+ return textureTriple{}, errors.New("no sRGB texture formats found")
+ }
+}
+
+func alphaTripleFor(ver [2]int) textureTriple {
+ intf, f := gl.Enum(gl.R8), gl.Enum(gl.RED)
+ if ver[0] < 3 {
+ // R8, RED not supported on OpenGL ES 2.0.
+ intf, f = gl.LUMINANCE, gl.Enum(gl.LUMINANCE)
+ }
+ return textureTriple{intf, f, gl.UNSIGNED_BYTE}
+}
+
+func hasExtension(exts []string, ext string) bool {
+ for _, e := range exts {
+ if ext == e {
+ return true
+ }
+ }
+ return false
+}
+
+func firstBufferType(typ driver.BufferBinding) gl.Enum {
+ switch {
+ case typ&driver.BufferBindingIndices != 0:
+ return gl.ELEMENT_ARRAY_BUFFER
+ case typ&driver.BufferBindingVertices != 0:
+ return gl.ARRAY_BUFFER
+ case typ&driver.BufferBindingUniforms != 0:
+ return gl.UNIFORM_BUFFER
+ case typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0:
+ return gl.SHADER_STORAGE_BUFFER
+ default:
+ panic("unsupported buffer type")
+ }
+}
diff --git a/vendor/gioui.org/gpu/internal/opengl/srgb.go b/vendor/gioui.org/gpu/internal/opengl/srgb.go
new file mode 100644
index 0000000..4871d94
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/opengl/srgb.go
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package opengl
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "runtime"
+ "strings"
+
+ "gioui.org/internal/byteslice"
+ "gioui.org/internal/gl"
+)
+
+// SRGBFBO implements an intermediate sRGB FBO
+// for gamma-correct rendering on platforms without
+// sRGB enabled native framebuffers.
+type SRGBFBO struct {
+ c *gl.Functions
+ state *glState
+ viewport image.Point
+ fbo gl.Framebuffer
+ tex gl.Texture
+ blitted bool
+ quad gl.Buffer
+ prog gl.Program
+ format textureTriple
+}
+
+func NewSRGBFBO(f *gl.Functions, state *glState) (*SRGBFBO, error) {
+ glVer := f.GetString(gl.VERSION)
+ ver, _, err := gl.ParseGLVersion(glVer)
+ if err != nil {
+ return nil, err
+ }
+ exts := strings.Split(f.GetString(gl.EXTENSIONS), " ")
+ srgbTriple, err := srgbaTripleFor(ver, exts)
+ if err != nil {
+ // Fall back to the linear RGB colorspace, at the cost of color precision loss.
+ srgbTriple = textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}
+ }
+ s := &SRGBFBO{
+ c: f,
+ state: state,
+ format: srgbTriple,
+ fbo: f.CreateFramebuffer(),
+ tex: f.CreateTexture(),
+ }
+ state.bindTexture(f, 0, s.tex)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
+ f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
+ return s, nil
+}
+
+func (s *SRGBFBO) Blit() {
+ if !s.blitted {
+ prog, err := gl.CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"})
+ if err != nil {
+ panic(err)
+ }
+ s.prog = prog
+ s.state.useProgram(s.c, prog)
+ s.c.Uniform1i(s.c.GetUniformLocation(prog, "tex"), 0)
+ s.quad = s.c.CreateBuffer()
+ s.state.bindBuffer(s.c, gl.ARRAY_BUFFER, s.quad)
+ coords := byteslice.Slice([]float32{
+ -1, +1, 0, 1,
+ +1, +1, 1, 1,
+ -1, -1, 0, 0,
+ +1, -1, 1, 0,
+ })
+ s.c.BufferData(gl.ARRAY_BUFFER, len(coords), gl.STATIC_DRAW, coords)
+ s.blitted = true
+ }
+ s.state.useProgram(s.c, s.prog)
+ s.state.bindTexture(s.c, 0, s.tex)
+ s.state.vertexAttribPointer(s.c, s.quad, 0 /* pos */, 2, gl.FLOAT, false, 4*4, 0)
+ s.state.vertexAttribPointer(s.c, s.quad, 1 /* uv */, 2, gl.FLOAT, false, 4*4, 4*2)
+ s.state.setVertexAttribArray(s.c, 0, true)
+ s.state.setVertexAttribArray(s.c, 1, true)
+ s.c.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
+ s.state.bindFramebuffer(s.c, gl.FRAMEBUFFER, s.fbo)
+ s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
+}
+
+func (s *SRGBFBO) Framebuffer() gl.Framebuffer {
+ return s.fbo
+}
+
+func (s *SRGBFBO) Refresh(viewport image.Point) error {
+ if viewport.X == 0 || viewport.Y == 0 {
+ return errors.New("srgb: zero-sized framebuffer")
+ }
+ if s.viewport == viewport {
+ return nil
+ }
+ s.viewport = viewport
+ s.state.bindTexture(s.c, 0, s.tex)
+ s.c.TexImage2D(gl.TEXTURE_2D, 0, s.format.internalFormat, viewport.X, viewport.Y, s.format.format, s.format.typ)
+ s.state.bindFramebuffer(s.c, gl.FRAMEBUFFER, s.fbo)
+ s.c.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, s.tex, 0)
+ if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
+ return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", viewport.X, viewport.Y, st, s.c.GetError())
+ }
+
+ if runtime.GOOS == "js" {
+ // With macOS Safari, rendering to and then reading from a SRGB8_ALPHA8
+ // texture result in twice gamma corrected colors. Using a plain RGBA
+ // texture seems to work.
+ s.state.setClearColor(s.c, .5, .5, .5, 1.0)
+ s.c.Clear(gl.COLOR_BUFFER_BIT)
+ var pixel [4]byte
+ s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:])
+ if pixel[0] == 128 { // Correct sRGB color value is ~188
+ s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, viewport.X, viewport.Y, gl.RGBA, gl.UNSIGNED_BYTE)
+ if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
+ return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", viewport.X, viewport.Y, st, s.c.GetError())
+ }
+ }
+ }
+
+ return nil
+}
+
+func (s *SRGBFBO) Release() {
+ s.state.deleteFramebuffer(s.c, s.fbo)
+ s.state.deleteTexture(s.c, s.tex)
+ if s.blitted {
+ s.state.deleteBuffer(s.c, s.quad)
+ s.state.deleteProgram(s.c, s.prog)
+ }
+ s.c = nil
+}
+
+const (
+ blitVSrc = `
+#version 100
+
+precision highp float;
+
+attribute vec2 pos;
+attribute vec2 uv;
+
+varying vec2 vUV;
+
+void main() {
+ gl_Position = vec4(pos, 0, 1);
+ vUV = uv;
+}
+`
+ blitFSrc = `
+#version 100
+
+precision mediump float;
+
+uniform sampler2D tex;
+varying vec2 vUV;
+
+vec3 gamma(vec3 rgb) {
+ vec3 exp = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055);
+ vec3 lin = rgb * vec3(12.92);
+ bvec3 cut = lessThan(rgb, vec3(0.0031308));
+ return vec3(cut.r ? lin.r : exp.r, cut.g ? lin.g : exp.g, cut.b ? lin.b : exp.b);
+}
+
+void main() {
+ vec4 col = texture2D(tex, vUV);
+ vec3 rgb = col.rgb;
+ rgb = gamma(rgb);
+ gl_FragColor = vec4(rgb, col.a);
+}
+`
+)
diff --git a/vendor/gioui.org/gpu/internal/vulkan/vulkan.go b/vendor/gioui.org/gpu/internal/vulkan/vulkan.go
new file mode 100644
index 0000000..7d3791e
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/vulkan/vulkan.go
@@ -0,0 +1,1121 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+//go:build (linux || freebsd) && !novulkan
+// +build linux freebsd
+// +build !novulkan
+
+package vulkan
+
+import (
+ "errors"
+ "fmt"
+ "image"
+
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/vk"
+ "gioui.org/shader"
+)
+
+type Backend struct {
+ physDev vk.PhysicalDevice
+ dev vk.Device
+ queue vk.Queue
+ cmdPool struct {
+ current vk.CommandBuffer
+ pool vk.CommandPool
+ used int
+ buffers []vk.CommandBuffer
+ }
+ outFormat vk.Format
+ staging struct {
+ buf *Buffer
+ mem []byte
+ size int
+ cap int
+ }
+ defers []func(d vk.Device)
+ frameSig vk.Semaphore
+ waitSems []vk.Semaphore
+ waitStages []vk.PipelineStageFlags
+ sigSems []vk.Semaphore
+ fence vk.Fence
+
+ allPipes []*Pipeline
+
+ pipe *Pipeline
+
+ passes map[passKey]vk.RenderPass
+
+ // bindings and offset are temporary storage for BindVertexBuffer.
+ bindings []vk.Buffer
+ offsets []vk.DeviceSize
+
+ desc struct {
+ dirty bool
+ texBinds [texUnits]*Texture
+ bufBinds [storageUnits]*Buffer
+ }
+
+ caps driver.Features
+}
+
+type passKey struct {
+ fmt vk.Format
+ loadAct vk.AttachmentLoadOp
+ initLayout vk.ImageLayout
+ finalLayout vk.ImageLayout
+}
+
+type Texture struct {
+ backend *Backend
+ img vk.Image
+ mem vk.DeviceMemory
+ view vk.ImageView
+ sampler vk.Sampler
+ fbo vk.Framebuffer
+ format vk.Format
+ layout vk.ImageLayout
+ passLayout vk.ImageLayout
+ width int
+ height int
+ acquire vk.Semaphore
+ foreign bool
+
+ scope struct {
+ stage vk.PipelineStageFlags
+ access vk.AccessFlags
+ }
+}
+
+type Shader struct {
+ dev vk.Device
+ module vk.ShaderModule
+ pushRange vk.PushConstantRange
+ src shader.Sources
+}
+
+type Pipeline struct {
+ backend *Backend
+ pipe vk.Pipeline
+ pushRanges []vk.PushConstantRange
+ ninputs int
+ desc *descPool
+}
+
+type descPool struct {
+ layout vk.PipelineLayout
+ descLayout vk.DescriptorSetLayout
+ pool vk.DescriptorPool
+ size int
+ cap int
+ texBinds []int
+ imgBinds []int
+ bufBinds []int
+}
+
+type Buffer struct {
+ backend *Backend
+ buf vk.Buffer
+ store []byte
+ mem vk.DeviceMemory
+ usage vk.BufferUsageFlags
+
+ scope struct {
+ stage vk.PipelineStageFlags
+ access vk.AccessFlags
+ }
+}
+
+const (
+ texUnits = 4
+ storageUnits = 4
+)
+
+func init() {
+ driver.NewVulkanDevice = newVulkanDevice
+}
+
+func newVulkanDevice(api driver.Vulkan) (driver.Device, error) {
+ b := &Backend{
+ physDev: vk.PhysicalDevice(api.PhysDevice),
+ dev: vk.Device(api.Device),
+ outFormat: vk.Format(api.Format),
+ caps: driver.FeatureCompute,
+ passes: make(map[passKey]vk.RenderPass),
+ }
+ b.queue = vk.GetDeviceQueue(b.dev, api.QueueFamily, api.QueueIndex)
+ cmdPool, err := vk.CreateCommandPool(b.dev, api.QueueFamily)
+ if err != nil {
+ return nil, err
+ }
+ b.cmdPool.pool = cmdPool
+ props := vk.GetPhysicalDeviceFormatProperties(b.physDev, vk.FORMAT_R16_SFLOAT)
+ reqs := vk.FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | vk.FORMAT_FEATURE_SAMPLED_IMAGE_BIT
+ if props&reqs == reqs {
+ b.caps |= driver.FeatureFloatRenderTargets
+ }
+ reqs = vk.FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | vk.FORMAT_FEATURE_SAMPLED_IMAGE_BIT
+ props = vk.GetPhysicalDeviceFormatProperties(b.physDev, vk.FORMAT_R8G8B8A8_SRGB)
+ if props&reqs == reqs {
+ b.caps |= driver.FeatureSRGB
+ }
+ fence, err := vk.CreateFence(b.dev)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ b.fence = fence
+ return b, nil
+}
+
+func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
+ vk.QueueWaitIdle(b.queue)
+ b.staging.size = 0
+ b.cmdPool.used = 0
+ b.runDefers()
+ b.resetPipes()
+
+ if target == nil {
+ return nil
+ }
+ switch t := target.(type) {
+ case driver.VulkanRenderTarget:
+ layout := vk.IMAGE_LAYOUT_UNDEFINED
+ if !clear {
+ layout = vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ }
+ b.frameSig = vk.Semaphore(t.SignalSem)
+ tex := &Texture{
+ img: vk.Image(t.Image),
+ fbo: vk.Framebuffer(t.Framebuffer),
+ width: viewport.X,
+ height: viewport.Y,
+ layout: layout,
+ passLayout: vk.IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ format: b.outFormat,
+ acquire: vk.Semaphore(t.WaitSem),
+ foreign: true,
+ }
+ return tex
+ case *Texture:
+ return t
+ default:
+ panic(fmt.Sprintf("vulkan: unsupported render target type: %T", t))
+ }
+}
+
+func (b *Backend) deferFunc(f func(d vk.Device)) {
+ b.defers = append(b.defers, f)
+}
+
+func (b *Backend) runDefers() {
+ for _, f := range b.defers {
+ f(b.dev)
+ }
+ b.defers = b.defers[:0]
+}
+
+func (b *Backend) resetPipes() {
+ for i := len(b.allPipes) - 1; i >= 0; i-- {
+ p := b.allPipes[i]
+ if p.pipe == 0 {
+ // Released pipeline.
+ b.allPipes = append(b.allPipes[:i], b.allPipes[:i+1]...)
+ continue
+ }
+ if p.desc.size > 0 {
+ vk.ResetDescriptorPool(b.dev, p.desc.pool)
+ p.desc.size = 0
+ }
+ }
+}
+
+func (b *Backend) EndFrame() {
+ if b.frameSig != 0 {
+ b.sigSems = append(b.sigSems, b.frameSig)
+ b.frameSig = 0
+ }
+ b.submitCmdBuf(false)
+}
+
+func (b *Backend) Caps() driver.Caps {
+ return driver.Caps{
+ MaxTextureSize: 4096,
+ Features: b.caps,
+ }
+}
+
+func (b *Backend) NewTimer() driver.Timer {
+ panic("timers not supported")
+}
+
+func (b *Backend) IsTimeContinuous() bool {
+ panic("timers not supported")
+}
+
+func (b *Backend) Release() {
+ vk.DeviceWaitIdle(b.dev)
+ if buf := b.staging.buf; buf != nil {
+ vk.UnmapMemory(b.dev, b.staging.buf.mem)
+ buf.Release()
+ }
+ b.runDefers()
+ for _, rp := range b.passes {
+ vk.DestroyRenderPass(b.dev, rp)
+ }
+ vk.DestroyFence(b.dev, b.fence)
+ vk.FreeCommandBuffers(b.dev, b.cmdPool.pool, b.cmdPool.buffers...)
+ vk.DestroyCommandPool(b.dev, b.cmdPool.pool)
+ *b = Backend{}
+}
+
+func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
+ vkfmt := formatFor(format)
+ usage := vk.IMAGE_USAGE_TRANSFER_DST_BIT | vk.IMAGE_USAGE_TRANSFER_SRC_BIT
+ passLayout := vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+ if bindings&driver.BufferBindingTexture != 0 {
+ usage |= vk.IMAGE_USAGE_SAMPLED_BIT
+ passLayout = vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+ }
+ if bindings&driver.BufferBindingFramebuffer != 0 {
+ usage |= vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT
+ }
+ if bindings&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 {
+ usage |= vk.IMAGE_USAGE_STORAGE_BIT
+ }
+ filterFor := func(f driver.TextureFilter) vk.Filter {
+ switch minFilter {
+ case driver.FilterLinear:
+ return vk.FILTER_LINEAR
+ case driver.FilterNearest:
+ return vk.FILTER_NEAREST
+ }
+ panic("unknown filter")
+ }
+ sampler, err := vk.CreateSampler(b.dev, filterFor(minFilter), filterFor(magFilter))
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ img, mem, err := vk.CreateImage(b.physDev, b.dev, vkfmt, width, height, usage)
+ if err != nil {
+ vk.DestroySampler(b.dev, sampler)
+ return nil, mapErr(err)
+ }
+ view, err := vk.CreateImageView(b.dev, img, vkfmt)
+ if err != nil {
+ vk.DestroySampler(b.dev, sampler)
+ vk.DestroyImage(b.dev, img)
+ vk.FreeMemory(b.dev, mem)
+ return nil, mapErr(err)
+ }
+ t := &Texture{backend: b, img: img, mem: mem, view: view, sampler: sampler, layout: vk.IMAGE_LAYOUT_UNDEFINED, passLayout: passLayout, width: width, height: height, format: vkfmt}
+ if bindings&driver.BufferBindingFramebuffer != 0 {
+ pass, err := vk.CreateRenderPass(b.dev, vkfmt, vk.ATTACHMENT_LOAD_OP_DONT_CARE,
+ vk.IMAGE_LAYOUT_UNDEFINED, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, nil)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ defer vk.DestroyRenderPass(b.dev, pass)
+ fbo, err := vk.CreateFramebuffer(b.dev, pass, view, width, height)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ t.fbo = fbo
+ }
+ return t, nil
+}
+
+func (b *Backend) NewBuffer(bindings driver.BufferBinding, size int) (driver.Buffer, error) {
+ if bindings&driver.BufferBindingUniforms != 0 {
+ // Implement uniform buffers as inline push constants.
+ return &Buffer{store: make([]byte, size)}, nil
+ }
+ usage := vk.BUFFER_USAGE_TRANSFER_DST_BIT | vk.BUFFER_USAGE_TRANSFER_SRC_BIT
+ if bindings&driver.BufferBindingIndices != 0 {
+ usage |= vk.BUFFER_USAGE_INDEX_BUFFER_BIT
+ }
+ if bindings&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 {
+ usage |= vk.BUFFER_USAGE_STORAGE_BUFFER_BIT
+ }
+ if bindings&driver.BufferBindingVertices != 0 {
+ usage |= vk.BUFFER_USAGE_VERTEX_BUFFER_BIT
+ }
+ buf, err := b.newBuffer(size, usage, vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+ return buf, mapErr(err)
+}
+
+func (b *Backend) newBuffer(size int, usage vk.BufferUsageFlags, props vk.MemoryPropertyFlags) (*Buffer, error) {
+ buf, mem, err := vk.CreateBuffer(b.physDev, b.dev, size, usage, props)
+ return &Buffer{backend: b, buf: buf, mem: mem, usage: usage}, err
+}
+
+func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
+ buf, err := b.NewBuffer(typ, len(data))
+ if err != nil {
+ return nil, err
+ }
+ buf.Upload(data)
+ return buf, nil
+}
+
+func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
+ sh, err := b.newShader(src, vk.SHADER_STAGE_VERTEX_BIT)
+ return sh, mapErr(err)
+}
+
+func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
+ sh, err := b.newShader(src, vk.SHADER_STAGE_FRAGMENT_BIT)
+ return sh, mapErr(err)
+}
+
+func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
+ vs := desc.VertexShader.(*Shader)
+ fs := desc.FragmentShader.(*Shader)
+ var ranges []vk.PushConstantRange
+ if r := vs.pushRange; r != (vk.PushConstantRange{}) {
+ ranges = append(ranges, r)
+ }
+ if r := fs.pushRange; r != (vk.PushConstantRange{}) {
+ ranges = append(ranges, r)
+ }
+ descPool, err := createPipelineLayout(b.dev, fs.src, ranges)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ blend := desc.BlendDesc
+ factorFor := func(f driver.BlendFactor) vk.BlendFactor {
+ switch f {
+ case driver.BlendFactorZero:
+ return vk.BLEND_FACTOR_ZERO
+ case driver.BlendFactorOne:
+ return vk.BLEND_FACTOR_ONE
+ case driver.BlendFactorOneMinusSrcAlpha:
+ return vk.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
+ case driver.BlendFactorDstColor:
+ return vk.BLEND_FACTOR_DST_COLOR
+ default:
+ panic("unknown blend factor")
+ }
+ }
+ var top vk.PrimitiveTopology
+ switch desc.Topology {
+ case driver.TopologyTriangles:
+ top = vk.PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
+ case driver.TopologyTriangleStrip:
+ top = vk.PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP
+ default:
+ panic("unknown topology")
+ }
+ var binds []vk.VertexInputBindingDescription
+ var attrs []vk.VertexInputAttributeDescription
+ inputs := desc.VertexLayout.Inputs
+ for i, inp := range inputs {
+ binds = append(binds, vk.VertexInputBindingDescription{
+ Binding: i,
+ Stride: desc.VertexLayout.Stride,
+ })
+ attrs = append(attrs, vk.VertexInputAttributeDescription{
+ Binding: i,
+ Location: vs.src.Inputs[i].Location,
+ Format: vertFormatFor(vs.src.Inputs[i]),
+ Offset: inp.Offset,
+ })
+ }
+ fmt := b.outFormat
+ if f := desc.PixelFormat; f != driver.TextureFormatOutput {
+ fmt = formatFor(f)
+ }
+ pass, err := vk.CreateRenderPass(b.dev, fmt, vk.ATTACHMENT_LOAD_OP_DONT_CARE,
+ vk.IMAGE_LAYOUT_UNDEFINED, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, nil)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ defer vk.DestroyRenderPass(b.dev, pass)
+ pipe, err := vk.CreateGraphicsPipeline(b.dev, pass, vs.module, fs.module, blend.Enable, factorFor(blend.SrcFactor), factorFor(blend.DstFactor), top, binds, attrs, descPool.layout)
+ if err != nil {
+ descPool.release(b.dev)
+ return nil, mapErr(err)
+ }
+ p := &Pipeline{backend: b, pipe: pipe, desc: descPool, pushRanges: ranges, ninputs: len(inputs)}
+ b.allPipes = append(b.allPipes, p)
+ return p, nil
+}
+
+func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) {
+ sh, err := b.newShader(src, vk.SHADER_STAGE_COMPUTE_BIT)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ defer sh.Release()
+ descPool, err := createPipelineLayout(b.dev, src, nil)
+ if err != nil {
+ return nil, mapErr(err)
+ }
+ pipe, err := vk.CreateComputePipeline(b.dev, sh.module, descPool.layout)
+ if err != nil {
+ descPool.release(b.dev)
+ return nil, mapErr(err)
+ }
+ return &Pipeline{backend: b, pipe: pipe, desc: descPool}, nil
+}
+
+func vertFormatFor(f shader.InputLocation) vk.Format {
+ t := f.Type
+ s := f.Size
+ switch {
+ case t == shader.DataTypeFloat && s == 1:
+ return vk.FORMAT_R32_SFLOAT
+ case t == shader.DataTypeFloat && s == 2:
+ return vk.FORMAT_R32G32_SFLOAT
+ case t == shader.DataTypeFloat && s == 3:
+ return vk.FORMAT_R32G32B32_SFLOAT
+ case t == shader.DataTypeFloat && s == 4:
+ return vk.FORMAT_R32G32B32A32_SFLOAT
+ default:
+ panic("unsupported data type")
+ }
+}
+
+func createPipelineLayout(d vk.Device, src shader.Sources, ranges []vk.PushConstantRange) (*descPool, error) {
+ var (
+ descLayouts []vk.DescriptorSetLayout
+ descLayout vk.DescriptorSetLayout
+ )
+ texBinds := make([]int, len(src.Textures))
+ imgBinds := make([]int, len(src.Images))
+ bufBinds := make([]int, len(src.StorageBuffers))
+ var descBinds []vk.DescriptorSetLayoutBinding
+ for i, t := range src.Textures {
+ descBinds = append(descBinds, vk.DescriptorSetLayoutBinding{
+ Binding: t.Binding,
+ StageFlags: vk.SHADER_STAGE_FRAGMENT_BIT,
+ DescriptorType: vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ })
+ texBinds[i] = t.Binding
+ }
+ for i, img := range src.Images {
+ descBinds = append(descBinds, vk.DescriptorSetLayoutBinding{
+ Binding: img.Binding,
+ StageFlags: vk.SHADER_STAGE_COMPUTE_BIT,
+ DescriptorType: vk.DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ })
+ imgBinds[i] = img.Binding
+ }
+ for i, buf := range src.StorageBuffers {
+ descBinds = append(descBinds, vk.DescriptorSetLayoutBinding{
+ Binding: buf.Binding,
+ StageFlags: vk.SHADER_STAGE_COMPUTE_BIT,
+ DescriptorType: vk.DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ })
+ bufBinds[i] = buf.Binding
+ }
+ if len(descBinds) > 0 {
+ var err error
+ descLayout, err = vk.CreateDescriptorSetLayout(d, descBinds)
+ if err != nil {
+ return nil, err
+ }
+ descLayouts = append(descLayouts, descLayout)
+ }
+ layout, err := vk.CreatePipelineLayout(d, ranges, descLayouts)
+ if err != nil {
+ if descLayout != 0 {
+ vk.DestroyDescriptorSetLayout(d, descLayout)
+ }
+ return nil, err
+ }
+ descPool := &descPool{
+ texBinds: texBinds,
+ bufBinds: bufBinds,
+ imgBinds: imgBinds,
+ layout: layout,
+ descLayout: descLayout,
+ }
+ return descPool, nil
+}
+
+func (b *Backend) newShader(src shader.Sources, stage vk.ShaderStageFlags) (*Shader, error) {
+ mod, err := vk.CreateShaderModule(b.dev, src.SPIRV)
+ if err != nil {
+ return nil, err
+ }
+
+ sh := &Shader{dev: b.dev, module: mod, src: src}
+ if locs := src.Uniforms.Locations; len(locs) > 0 {
+ pushOffset := 0x7fffffff
+ for _, l := range locs {
+ if l.Offset < pushOffset {
+ pushOffset = l.Offset
+ }
+ }
+ sh.pushRange = vk.BuildPushConstantRange(stage, pushOffset, src.Uniforms.Size)
+ }
+ return sh, nil
+}
+
+func (b *Backend) CopyTexture(dstTex driver.Texture, dorig image.Point, srcFBO driver.Texture, srect image.Rectangle) {
+ dst := dstTex.(*Texture)
+ src := srcFBO.(*Texture)
+ cmdBuf := b.ensureCmdBuf()
+ op := vk.BuildImageCopy(srect.Min.X, srect.Min.Y, dorig.X, dorig.Y, srect.Dx(), srect.Dy())
+ src.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_READ_BIT,
+ )
+ dst.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_WRITE_BIT,
+ )
+ vk.CmdCopyImage(cmdBuf, src.img, src.layout, dst.img, dst.layout, []vk.ImageCopy{op})
+}
+
+func (b *Backend) Viewport(x, y, width, height int) {
+ cmdBuf := b.currentCmdBuf()
+ vp := vk.BuildViewport(float32(x), float32(y), float32(width), float32(height))
+ vk.CmdSetViewport(cmdBuf, 0, vp)
+}
+
+func (b *Backend) DrawArrays(off, count int) {
+ cmdBuf := b.currentCmdBuf()
+ if b.desc.dirty {
+ b.pipe.desc.bindDescriptorSet(b, cmdBuf, vk.PIPELINE_BIND_POINT_GRAPHICS, b.desc.texBinds, b.desc.bufBinds)
+ b.desc.dirty = false
+ }
+ vk.CmdDraw(cmdBuf, count, 1, off, 0)
+}
+
+func (b *Backend) DrawElements(off, count int) {
+ cmdBuf := b.currentCmdBuf()
+ if b.desc.dirty {
+ b.pipe.desc.bindDescriptorSet(b, cmdBuf, vk.PIPELINE_BIND_POINT_GRAPHICS, b.desc.texBinds, b.desc.bufBinds)
+ b.desc.dirty = false
+ }
+ vk.CmdDrawIndexed(cmdBuf, count, 1, off, 0, 0)
+}
+
+func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ b.desc.texBinds[unit] = t
+ b.desc.dirty = true
+ t.imageBarrier(b.currentCmdBuf(),
+ vk.IMAGE_LAYOUT_GENERAL,
+ vk.PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ vk.ACCESS_SHADER_READ_BIT|vk.ACCESS_SHADER_WRITE_BIT,
+ )
+}
+
+func (b *Backend) DispatchCompute(x, y, z int) {
+ cmdBuf := b.currentCmdBuf()
+ if b.desc.dirty {
+ b.pipe.desc.bindDescriptorSet(b, cmdBuf, vk.PIPELINE_BIND_POINT_COMPUTE, b.desc.texBinds, b.desc.bufBinds)
+ b.desc.dirty = false
+ }
+ vk.CmdDispatch(cmdBuf, x, y, z)
+}
+
+func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
+ if stride == 0 {
+ stride = size.X * 4
+ }
+ cmdBuf := t.backend.ensureCmdBuf()
+ dstStride := size.X * 4
+ n := size.Y * dstStride
+ stage, mem, off := t.backend.stagingBuffer(n)
+ var srcOff, dstOff int
+ for y := 0; y < size.Y; y++ {
+ srcRow := pixels[srcOff : srcOff+dstStride]
+ dstRow := mem[dstOff : dstOff+dstStride]
+ copy(dstRow, srcRow)
+ dstOff += dstStride
+ srcOff += stride
+ }
+ op := vk.BuildBufferImageCopy(off, dstStride/4, offset.X, offset.Y, size.X, size.Y)
+ t.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_WRITE_BIT,
+ )
+ vk.CmdCopyBufferToImage(cmdBuf, stage.buf, t.img, t.layout, op)
+}
+
+func (t *Texture) Release() {
+ if t.foreign {
+ panic("external textures cannot be released")
+ }
+ freet := *t
+ t.backend.deferFunc(func(d vk.Device) {
+ if freet.fbo != 0 {
+ vk.DestroyFramebuffer(d, freet.fbo)
+ }
+ vk.DestroySampler(d, freet.sampler)
+ vk.DestroyImageView(d, freet.view)
+ vk.DestroyImage(d, freet.img)
+ vk.FreeMemory(d, freet.mem)
+ })
+ *t = Texture{}
+}
+
+func (p *Pipeline) Release() {
+ freep := *p
+ p.backend.deferFunc(func(d vk.Device) {
+ freep.desc.release(d)
+ vk.DestroyPipeline(d, freep.pipe)
+ })
+ *p = Pipeline{}
+}
+
+func (p *descPool) release(d vk.Device) {
+ if p := p.pool; p != 0 {
+ vk.DestroyDescriptorPool(d, p)
+ }
+ if l := p.descLayout; l != 0 {
+ vk.DestroyDescriptorSetLayout(d, l)
+ }
+ vk.DestroyPipelineLayout(d, p.layout)
+}
+
+func (p *descPool) bindDescriptorSet(b *Backend, cmdBuf vk.CommandBuffer, bindPoint vk.PipelineBindPoint, texBinds [texUnits]*Texture, bufBinds [storageUnits]*Buffer) {
+ realloced := false
+ destroyPool := func() {
+ if pool := p.pool; pool != 0 {
+ b.deferFunc(func(d vk.Device) {
+ vk.DestroyDescriptorPool(d, pool)
+ })
+ }
+ p.pool = 0
+ p.cap = 0
+ }
+ for {
+ if p.size == p.cap {
+ if realloced {
+ panic("vulkan: vkAllocateDescriptorSet failed on a newly allocated descriptor pool")
+ }
+ destroyPool()
+ realloced = true
+ newCap := p.cap * 2
+ const initialPoolSize = 100
+ if newCap < initialPoolSize {
+ newCap = initialPoolSize
+ }
+ var poolSizes []vk.DescriptorPoolSize
+ if n := len(p.texBinds); n > 0 {
+ poolSizes = append(poolSizes, vk.BuildDescriptorPoolSize(vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, newCap*n))
+ }
+ if n := len(p.imgBinds); n > 0 {
+ poolSizes = append(poolSizes, vk.BuildDescriptorPoolSize(vk.DESCRIPTOR_TYPE_STORAGE_IMAGE, newCap*n))
+ }
+ if n := len(p.bufBinds); n > 0 {
+ poolSizes = append(poolSizes, vk.BuildDescriptorPoolSize(vk.DESCRIPTOR_TYPE_STORAGE_BUFFER, newCap*n))
+ }
+ pool, err := vk.CreateDescriptorPool(b.dev, newCap, poolSizes)
+ if err != nil {
+ panic(fmt.Errorf("vulkan: failed to allocate descriptor pool with %d descriptors", newCap))
+ }
+ p.pool = pool
+ p.cap = newCap
+ p.size = 0
+ }
+ l := p.descLayout
+ if l == 0 {
+ panic("vulkan: descriptor set is dirty, but pipeline has empty layout")
+ }
+ descSet, err := vk.AllocateDescriptorSet(b.dev, p.pool, l)
+ if err != nil {
+ destroyPool()
+ continue
+ }
+ p.size++
+ for _, bind := range p.texBinds {
+ tex := texBinds[bind]
+ write := vk.BuildWriteDescriptorSetImage(descSet, bind, vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, tex.sampler, tex.view, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+ vk.UpdateDescriptorSet(b.dev, write)
+ }
+ for _, bind := range p.imgBinds {
+ tex := texBinds[bind]
+ write := vk.BuildWriteDescriptorSetImage(descSet, bind, vk.DESCRIPTOR_TYPE_STORAGE_IMAGE, 0, tex.view, vk.IMAGE_LAYOUT_GENERAL)
+ vk.UpdateDescriptorSet(b.dev, write)
+ }
+ for _, bind := range p.bufBinds {
+ buf := bufBinds[bind]
+ write := vk.BuildWriteDescriptorSetBuffer(descSet, bind, vk.DESCRIPTOR_TYPE_STORAGE_BUFFER, buf.buf)
+ vk.UpdateDescriptorSet(b.dev, write)
+ }
+ vk.CmdBindDescriptorSets(cmdBuf, bindPoint, p.layout, 0, []vk.DescriptorSet{descSet})
+ break
+ }
+}
+
+func (t *Texture) imageBarrier(cmdBuf vk.CommandBuffer, layout vk.ImageLayout, stage vk.PipelineStageFlags, access vk.AccessFlags) {
+ srcStage := t.scope.stage
+ if srcStage == 0 && t.layout == layout {
+ t.scope.stage = stage
+ t.scope.access = access
+ return
+ }
+ if srcStage == 0 {
+ srcStage = vk.PIPELINE_STAGE_TOP_OF_PIPE_BIT
+ }
+ b := vk.BuildImageMemoryBarrier(
+ t.img,
+ t.scope.access, access,
+ t.layout, layout,
+ )
+ vk.CmdPipelineBarrier(cmdBuf, srcStage, stage, vk.DEPENDENCY_BY_REGION_BIT, nil, nil, []vk.ImageMemoryBarrier{b})
+ t.layout = layout
+ t.scope.stage = stage
+ t.scope.access = access
+}
+
+func (b *Backend) PrepareTexture(tex driver.Texture) {
+ t := tex.(*Texture)
+ cmdBuf := b.ensureCmdBuf()
+ t.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ vk.PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
+ vk.ACCESS_SHADER_READ_BIT,
+ )
+}
+
+func (b *Backend) BindTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ b.desc.texBinds[unit] = t
+ b.desc.dirty = true
+}
+
+func (b *Backend) BindPipeline(pipe driver.Pipeline) {
+ b.bindPipeline(pipe.(*Pipeline), vk.PIPELINE_BIND_POINT_GRAPHICS)
+}
+
+func (b *Backend) BindProgram(prog driver.Program) {
+ b.bindPipeline(prog.(*Pipeline), vk.PIPELINE_BIND_POINT_COMPUTE)
+}
+
+func (b *Backend) bindPipeline(p *Pipeline, point vk.PipelineBindPoint) {
+ b.pipe = p
+ b.desc.dirty = p.desc.descLayout != 0
+ cmdBuf := b.currentCmdBuf()
+ vk.CmdBindPipeline(cmdBuf, point, p.pipe)
+}
+
+func (s *Shader) Release() {
+ vk.DestroyShaderModule(s.dev, s.module)
+ *s = Shader{}
+}
+
+func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ b.desc.bufBinds[binding] = buf
+ b.desc.dirty = true
+ buf.barrier(b.currentCmdBuf(),
+ vk.PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ vk.ACCESS_SHADER_READ_BIT|vk.ACCESS_SHADER_WRITE_BIT,
+ )
+}
+
+func (b *Backend) BindUniforms(buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ cmdBuf := b.currentCmdBuf()
+ for _, s := range b.pipe.pushRanges {
+ off := s.Offset()
+ vk.CmdPushConstants(cmdBuf, b.pipe.desc.layout, s.StageFlags(), off, buf.store[off:off+s.Size()])
+ }
+}
+
+func (b *Backend) BindVertexBuffer(buffer driver.Buffer, offset int) {
+ buf := buffer.(*Buffer)
+ cmdBuf := b.currentCmdBuf()
+ b.bindings = b.bindings[:0]
+ b.offsets = b.offsets[:0]
+ for i := 0; i < b.pipe.ninputs; i++ {
+ b.bindings = append(b.bindings, buf.buf)
+ b.offsets = append(b.offsets, vk.DeviceSize(offset))
+ }
+ vk.CmdBindVertexBuffers(cmdBuf, 0, b.bindings, b.offsets)
+}
+
+func (b *Backend) BindIndexBuffer(buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ cmdBuf := b.currentCmdBuf()
+ vk.CmdBindIndexBuffer(cmdBuf, buf.buf, 0, vk.INDEX_TYPE_UINT16)
+}
+
+func (b *Buffer) Download(data []byte) error {
+ if b.buf == 0 {
+ copy(data, b.store)
+ return nil
+ }
+ stage, mem, off := b.backend.stagingBuffer(len(data))
+ cmdBuf := b.backend.ensureCmdBuf()
+ b.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_READ_BIT,
+ )
+ vk.CmdCopyBuffer(cmdBuf, b.buf, stage.buf, 0, off, len(data))
+ stage.scope.stage = vk.PIPELINE_STAGE_TRANSFER_BIT
+ stage.scope.access = vk.ACCESS_TRANSFER_WRITE_BIT
+ stage.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_HOST_BIT,
+ vk.ACCESS_HOST_READ_BIT,
+ )
+ b.backend.submitCmdBuf(true)
+ copy(data, mem)
+ return nil
+}
+
+func (b *Buffer) Upload(data []byte) {
+ if b.buf == 0 {
+ copy(b.store, data)
+ return
+ }
+ stage, mem, off := b.backend.stagingBuffer(len(data))
+ copy(mem, data)
+ cmdBuf := b.backend.ensureCmdBuf()
+ b.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_WRITE_BIT,
+ )
+ vk.CmdCopyBuffer(cmdBuf, stage.buf, b.buf, off, 0, len(data))
+ var access vk.AccessFlags
+ if b.usage&vk.BUFFER_USAGE_INDEX_BUFFER_BIT != 0 {
+ access |= vk.ACCESS_INDEX_READ_BIT
+ }
+ if b.usage&vk.BUFFER_USAGE_VERTEX_BUFFER_BIT != 0 {
+ access |= vk.ACCESS_VERTEX_ATTRIBUTE_READ_BIT
+ }
+ if access != 0 {
+ b.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_VERTEX_INPUT_BIT,
+ access,
+ )
+ }
+}
+
+func (b *Buffer) barrier(cmdBuf vk.CommandBuffer, stage vk.PipelineStageFlags, access vk.AccessFlags) {
+ srcStage := b.scope.stage
+ if srcStage == 0 {
+ b.scope.stage = stage
+ b.scope.access = access
+ return
+ }
+ barrier := vk.BuildBufferMemoryBarrier(
+ b.buf,
+ b.scope.access, access,
+ )
+ vk.CmdPipelineBarrier(cmdBuf, srcStage, stage, vk.DEPENDENCY_BY_REGION_BIT, nil, []vk.BufferMemoryBarrier{barrier}, nil)
+ b.scope.stage = stage
+ b.scope.access = access
+}
+
+func (b *Buffer) Release() {
+ freeb := *b
+ if freeb.buf != 0 {
+ b.backend.deferFunc(func(d vk.Device) {
+ vk.DestroyBuffer(d, freeb.buf)
+ vk.FreeMemory(d, freeb.mem)
+ })
+ }
+ *b = Buffer{}
+}
+
+func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
+ if len(pixels) == 0 {
+ return nil
+ }
+ sz := src.Size()
+ stageStride := sz.X * 4
+ n := sz.Y * stageStride
+ stage, mem, off := t.backend.stagingBuffer(n)
+ cmdBuf := t.backend.ensureCmdBuf()
+ region := vk.BuildBufferImageCopy(off, stageStride/4, src.Min.X, src.Min.Y, sz.X, sz.Y)
+ t.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ vk.PIPELINE_STAGE_TRANSFER_BIT,
+ vk.ACCESS_TRANSFER_READ_BIT,
+ )
+ vk.CmdCopyImageToBuffer(cmdBuf, t.img, t.layout, stage.buf, []vk.BufferImageCopy{region})
+ stage.scope.stage = vk.PIPELINE_STAGE_TRANSFER_BIT
+ stage.scope.access = vk.ACCESS_TRANSFER_WRITE_BIT
+ stage.barrier(cmdBuf,
+ vk.PIPELINE_STAGE_HOST_BIT,
+ vk.ACCESS_HOST_READ_BIT,
+ )
+ t.backend.submitCmdBuf(true)
+ var srcOff, dstOff int
+ for y := 0; y < sz.Y; y++ {
+ dstRow := pixels[srcOff : srcOff+stageStride]
+ srcRow := mem[dstOff : dstOff+stageStride]
+ copy(dstRow, srcRow)
+ dstOff += stageStride
+ srcOff += stride
+ }
+ return nil
+}
+
+func (b *Backend) currentCmdBuf() vk.CommandBuffer {
+ cur := b.cmdPool.current
+ if cur == nil {
+ panic("vulkan: invalid operation outside a render or compute pass")
+ }
+ return cur
+}
+
+func (b *Backend) ensureCmdBuf() vk.CommandBuffer {
+ if b.cmdPool.current != nil {
+ return b.cmdPool.current
+ }
+ if b.cmdPool.used < len(b.cmdPool.buffers) {
+ buf := b.cmdPool.buffers[b.cmdPool.used]
+ b.cmdPool.current = buf
+ } else {
+ buf, err := vk.AllocateCommandBuffer(b.dev, b.cmdPool.pool)
+ if err != nil {
+ panic(err)
+ }
+ b.cmdPool.buffers = append(b.cmdPool.buffers, buf)
+ b.cmdPool.current = buf
+ }
+ b.cmdPool.used++
+ buf := b.cmdPool.current
+ if err := vk.BeginCommandBuffer(buf); err != nil {
+ panic(err)
+ }
+ return buf
+}
+
+func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
+ t := tex.(*Texture)
+ var vkop vk.AttachmentLoadOp
+ switch d.Action {
+ case driver.LoadActionClear:
+ vkop = vk.ATTACHMENT_LOAD_OP_CLEAR
+ case driver.LoadActionInvalidate:
+ vkop = vk.ATTACHMENT_LOAD_OP_DONT_CARE
+ case driver.LoadActionKeep:
+ vkop = vk.ATTACHMENT_LOAD_OP_LOAD
+ }
+ cmdBuf := b.ensureCmdBuf()
+ if sem := t.acquire; sem != 0 {
+ // The render pass targets a framebuffer that has an associated acquire semaphore.
+ // Wait for it by forming an execution barrier.
+ b.waitSems = append(b.waitSems, sem)
+ b.waitStages = append(b.waitStages, vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)
+ // But only for the first pass in a frame.
+ t.acquire = 0
+ }
+ t.imageBarrier(cmdBuf,
+ vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ vk.ACCESS_COLOR_ATTACHMENT_READ_BIT|vk.ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ )
+ pass := b.lookupPass(t.format, vkop, t.layout, t.passLayout)
+ col := d.ClearColor
+ vk.CmdBeginRenderPass(cmdBuf, pass, t.fbo, t.width, t.height, [4]float32{col.R, col.G, col.B, col.A})
+ t.layout = t.passLayout
+ // If the render pass describes an automatic image layout transition to its final layout, there
+ // is an implicit image barrier with destination PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT. Make
+ // sure any subsequent barrier includes the transition.
+ // See also https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VkSubpassDependency.
+ t.scope.stage |= vk.PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
+}
+
+func (b *Backend) EndRenderPass() {
+ vk.CmdEndRenderPass(b.cmdPool.current)
+}
+
+func (b *Backend) BeginCompute() {
+ b.ensureCmdBuf()
+}
+
+func (b *Backend) EndCompute() {
+}
+
+func (b *Backend) lookupPass(fmt vk.Format, loadAct vk.AttachmentLoadOp, initLayout, finalLayout vk.ImageLayout) vk.RenderPass {
+ key := passKey{fmt: fmt, loadAct: loadAct, initLayout: initLayout, finalLayout: finalLayout}
+ if pass, ok := b.passes[key]; ok {
+ return pass
+ }
+ pass, err := vk.CreateRenderPass(b.dev, fmt, loadAct, initLayout, finalLayout, nil)
+ if err != nil {
+ panic(err)
+ }
+ b.passes[key] = pass
+ return pass
+}
+
+func (b *Backend) submitCmdBuf(sync bool) {
+ buf := b.cmdPool.current
+ if buf == nil {
+ return
+ }
+ b.cmdPool.current = nil
+ if err := vk.EndCommandBuffer(buf); err != nil {
+ panic(err)
+ }
+ var fence vk.Fence
+ if sync {
+ fence = b.fence
+ }
+ if err := vk.QueueSubmit(b.queue, buf, b.waitSems, b.waitStages, b.sigSems, fence); err != nil {
+ panic(err)
+ }
+ b.waitSems = b.waitSems[:0]
+ b.sigSems = b.sigSems[:0]
+ b.waitStages = b.waitStages[:0]
+ if sync {
+ vk.WaitForFences(b.dev, b.fence)
+ vk.ResetFences(b.dev, b.fence)
+ }
+}
+
+func (b *Backend) stagingBuffer(size int) (*Buffer, []byte, int) {
+ if b.staging.size+size > b.staging.cap {
+ if b.staging.buf != nil {
+ vk.UnmapMemory(b.dev, b.staging.buf.mem)
+ b.staging.buf.Release()
+ b.staging.cap = 0
+ }
+ cap := 2 * (b.staging.size + size)
+ buf, err := b.newBuffer(cap, vk.BUFFER_USAGE_TRANSFER_SRC_BIT|vk.BUFFER_USAGE_TRANSFER_DST_BIT,
+ vk.MEMORY_PROPERTY_HOST_VISIBLE_BIT|vk.MEMORY_PROPERTY_HOST_COHERENT_BIT)
+ if err != nil {
+ panic(err)
+ }
+ mem, err := vk.MapMemory(b.dev, buf.mem, 0, cap)
+ if err != nil {
+ buf.Release()
+ panic(err)
+ }
+ b.staging.buf = buf
+ b.staging.mem = mem
+ b.staging.size = 0
+ b.staging.cap = cap
+ }
+ off := b.staging.size
+ b.staging.size += size
+ mem := b.staging.mem[off : off+size]
+ return b.staging.buf, mem, off
+}
+
+func formatFor(format driver.TextureFormat) vk.Format {
+ switch format {
+ case driver.TextureFormatRGBA8:
+ return vk.FORMAT_R8G8B8A8_UNORM
+ case driver.TextureFormatSRGBA:
+ return vk.FORMAT_R8G8B8A8_SRGB
+ case driver.TextureFormatFloat:
+ return vk.FORMAT_R16_SFLOAT
+ default:
+ panic("unsupported texture format")
+ }
+}
+
+func mapErr(err error) error {
+ var vkErr vk.Error
+ if errors.As(err, &vkErr) && vkErr == vk.ERROR_DEVICE_LOST {
+ return driver.ErrDeviceLost
+ }
+ return err
+}
+
+func (f *Texture) ImplementsRenderTarget() {}
diff --git a/vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go b/vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go
new file mode 100644
index 0000000..4364a43
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package vulkan
+
+// Empty file to avoid the build error for platforms without Vulkan support.
diff --git a/vendor/gioui.org/gpu/pack.go b/vendor/gioui.org/gpu/pack.go
index c4dbaad..3f8c925 100644
--- a/vendor/gioui.org/gpu/pack.go
+++ b/vendor/gioui.org/gpu/pack.go
@@ -9,8 +9,8 @@ import (
// packer packs a set of many smaller rectangles into
// much fewer larger atlases.
type packer struct {
- maxDim int
- spaces []image.Rectangle
+ maxDims image.Point
+ spaces []image.Rectangle
sizes []image.Point
pos image.Point
@@ -41,45 +41,65 @@ func (p *packer) newPage() {
p.sizes = append(p.sizes, image.Point{})
p.spaces = p.spaces[:0]
p.spaces = append(p.spaces, image.Rectangle{
- Max: image.Point{X: p.maxDim, Y: p.maxDim},
+ Max: image.Point{X: 1e6, Y: 1e6},
})
}
func (p *packer) tryAdd(s image.Point) (placement, bool) {
- // Go backwards to prioritize smaller spaces first.
- for i := len(p.spaces) - 1; i >= 0; i-- {
- space := p.spaces[i]
+ var (
+ bestIdx = -1
+ bestSpace image.Rectangle
+ bestSize = p.maxDims
+ )
+ // Go backwards to prioritize smaller spaces.
+ for i, space := range p.spaces {
rightSpace := space.Dx() - s.X
bottomSpace := space.Dy() - s.Y
- if rightSpace >= 0 && bottomSpace >= 0 {
- // Remove space.
- p.spaces[i] = p.spaces[len(p.spaces)-1]
- p.spaces = p.spaces[:len(p.spaces)-1]
- // Put s in the top left corner and add the (at most)
- // two smaller spaces.
- pos := space.Min
- if bottomSpace > 0 {
- p.spaces = append(p.spaces, image.Rectangle{
- Min: image.Point{X: pos.X, Y: pos.Y + s.Y},
- Max: image.Point{X: space.Max.X, Y: space.Max.Y},
- })
- }
- if rightSpace > 0 {
- p.spaces = append(p.spaces, image.Rectangle{
- Min: image.Point{X: pos.X + s.X, Y: pos.Y},
- Max: image.Point{X: space.Max.X, Y: pos.Y + s.Y},
- })
- }
- idx := len(p.sizes) - 1
- size := &p.sizes[idx]
- if x := pos.X + s.X; x > size.X {
- size.X = x
+ if rightSpace < 0 || bottomSpace < 0 {
+ continue
+ }
+ idx := len(p.sizes) - 1
+ size := p.sizes[idx]
+ if x := space.Min.X + s.X; x > size.X {
+ if x > p.maxDims.X {
+ continue
}
- if y := pos.Y + s.Y; y > size.Y {
- size.Y = y
+ size.X = x
+ }
+ if y := space.Min.Y + s.Y; y > size.Y {
+ if y > p.maxDims.Y {
+ continue
}
- return placement{Idx: idx, Pos: pos}, true
+ size.Y = y
}
+ if size.X*size.Y < bestSize.X*bestSize.Y {
+ bestIdx = i
+ bestSpace = space
+ bestSize = size
+ }
+ }
+ if bestIdx == -1 {
+ return placement{}, false
+ }
+ // Remove space.
+ p.spaces[bestIdx] = p.spaces[len(p.spaces)-1]
+ p.spaces = p.spaces[:len(p.spaces)-1]
+ // Put s in the top left corner and add the (at most)
+ // two smaller spaces.
+ pos := bestSpace.Min
+ if rem := bestSpace.Dy() - s.Y; rem > 0 {
+ p.spaces = append(p.spaces, image.Rectangle{
+ Min: image.Point{X: pos.X, Y: pos.Y + s.Y},
+ Max: image.Point{X: bestSpace.Max.X, Y: bestSpace.Max.Y},
+ })
+ }
+ if rem := bestSpace.Dx() - s.X; rem > 0 {
+ p.spaces = append(p.spaces, image.Rectangle{
+ Min: image.Point{X: pos.X + s.X, Y: pos.Y},
+ Max: image.Point{X: bestSpace.Max.X, Y: pos.Y + s.Y},
+ })
}
- return placement{}, false
+ idx := len(p.sizes) - 1
+ p.sizes[idx] = bestSize
+ return placement{Idx: idx, Pos: pos}, true
}
diff --git a/vendor/gioui.org/gpu/path.go b/vendor/gioui.org/gpu/path.go
index 2dd5875..5fd73cf 100644
--- a/vendor/gioui.org/gpu/path.go
+++ b/vendor/gioui.org/gpu/path.go
@@ -12,13 +12,15 @@ import (
"unsafe"
"gioui.org/f32"
- "gioui.org/gpu/backend"
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/byteslice"
"gioui.org/internal/f32color"
- gunsafe "gioui.org/internal/unsafe"
+ "gioui.org/shader"
+ "gioui.org/shader/gio"
)
type pather struct {
- ctx backend.Device
+ ctx driver.Device
viewport image.Point
@@ -27,28 +29,28 @@ type pather struct {
}
type coverer struct {
- ctx backend.Device
- prog [2]*program
- texUniforms *coverTexUniforms
- colUniforms *coverColUniforms
- layout backend.InputLayout
+ ctx driver.Device
+ pipelines [3]*pipeline
+ texUniforms *coverTexUniforms
+ colUniforms *coverColUniforms
+ linearGradientUniforms *coverLinearGradientUniforms
}
type coverTexUniforms struct {
- vert struct {
- coverUniforms
- _ [12]byte // Padding to multiple of 16.
- }
+ coverUniforms
+ _ [12]byte // Padding to multiple of 16.
}
type coverColUniforms struct {
- vert struct {
- coverUniforms
- _ [12]byte // Padding to multiple of 16.
- }
- frag struct {
- colorUniforms
- }
+ coverUniforms
+ _ [128 - unsafe.Sizeof(coverUniforms{}) - unsafe.Sizeof(colorUniforms{})]byte // Padding to 128 bytes.
+ colorUniforms
+}
+
+type coverLinearGradientUniforms struct {
+ coverUniforms
+ _ [128 - unsafe.Sizeof(coverUniforms{}) - unsafe.Sizeof(gradientUniforms{})]byte // Padding to 128.
+ gradientUniforms
}
type coverUniforms struct {
@@ -60,28 +62,24 @@ type coverUniforms struct {
}
type stenciler struct {
- ctx backend.Device
- prog struct {
- prog *program
+ ctx driver.Device
+ pipeline struct {
+ pipeline *pipeline
uniforms *stencilUniforms
- layout backend.InputLayout
}
- iprog struct {
- prog *program
+ ipipeline struct {
+ pipeline *pipeline
uniforms *intersectUniforms
- layout backend.InputLayout
}
fbos fboSet
intersections fboSet
- indexBuf backend.Buffer
+ indexBuf driver.Buffer
}
type stencilUniforms struct {
- vert struct {
- transform [4]float32
- pathOffset [2]float32
- _ [8]byte // Padding to multiple of 16.
- }
+ transform [4]float32
+ pathOffset [2]float32
+ _ [8]byte // Padding to multiple of 16.
}
type intersectUniforms struct {
@@ -97,13 +95,12 @@ type fboSet struct {
type stencilFBO struct {
size image.Point
- fbo backend.Framebuffer
- tex backend.Texture
+ tex driver.Texture
}
type pathData struct {
ncurves int
- data backend.Buffer
+ data driver.Buffer
}
// vertex data suitable for passing to vertex programs.
@@ -132,10 +129,10 @@ const (
// Number of path quads per draw batch.
pathBatchSize = 10000
// Size of a vertex as sent to gpu
- vertStride = 7*4 + 2*2
+ vertStride = 8 * 4
)
-func newPather(ctx backend.Device) *pather {
+func newPather(ctx driver.Device) *pather {
return &pather{
ctx: ctx,
stenciler: newStenciler(ctx),
@@ -143,25 +140,24 @@ func newPather(ctx backend.Device) *pather {
}
}
-func newCoverer(ctx backend.Device) *coverer {
+func newCoverer(ctx driver.Device) *coverer {
c := &coverer{
ctx: ctx,
}
c.colUniforms = new(coverColUniforms)
c.texUniforms = new(coverTexUniforms)
- prog, layout, err := createColorPrograms(ctx, shader_cover_vert, shader_cover_frag,
- [2]interface{}{&c.colUniforms.vert, &c.texUniforms.vert},
- [2]interface{}{&c.colUniforms.frag, nil},
+ c.linearGradientUniforms = new(coverLinearGradientUniforms)
+ pipelines, err := createColorPrograms(ctx, gio.Shader_cover_vert, gio.Shader_cover_frag,
+ [3]interface{}{c.colUniforms, c.linearGradientUniforms, c.texUniforms},
)
if err != nil {
panic(err)
}
- c.prog = prog
- c.layout = layout
+ c.pipelines = pipelines
return c
}
-func newStenciler(ctx backend.Device) *stenciler {
+func newStenciler(ctx driver.Device) *stenciler {
// Allocate a suitably large index buffer for drawing paths.
indices := make([]uint16, pathBatchSize*6)
for i := 0; i < pathBatchSize; i++ {
@@ -173,51 +169,80 @@ func newStenciler(ctx backend.Device) *stenciler {
indices[i*6+4] = i*4 + 1
indices[i*6+5] = i*4 + 3
}
- indexBuf, err := ctx.NewImmutableBuffer(backend.BufferBindingIndices, gunsafe.BytesView(indices))
+ indexBuf, err := ctx.NewImmutableBuffer(driver.BufferBindingIndices, byteslice.Slice(indices))
if err != nil {
panic(err)
}
- progLayout, err := ctx.NewInputLayout(shader_stencil_vert, []backend.InputDesc{
- {Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))},
- {Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))},
- {Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))},
- {Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))},
- {Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))},
- })
- if err != nil {
- panic(err)
+ progLayout := driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))},
+ {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))},
+ },
+ Stride: vertStride,
}
- iprogLayout, err := ctx.NewInputLayout(shader_intersect_vert, []backend.InputDesc{
- {Type: backend.DataTypeFloat, Size: 2, Offset: 0},
- {Type: backend.DataTypeFloat, Size: 2, Offset: 4 * 2},
- })
- if err != nil {
- panic(err)
+ iprogLayout := driver.VertexLayout{
+ Inputs: []driver.InputDesc{
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 0},
+ {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
+ },
+ Stride: 4 * 4,
}
st := &stenciler{
ctx: ctx,
indexBuf: indexBuf,
}
- prog, err := ctx.NewProgram(shader_stencil_vert, shader_stencil_frag)
+ vsh, fsh, err := newShaders(ctx, gio.Shader_stencil_vert, gio.Shader_stencil_frag)
if err != nil {
panic(err)
}
- st.prog.uniforms = new(stencilUniforms)
- vertUniforms := newUniformBuffer(ctx, &st.prog.uniforms.vert)
- st.prog.prog = newProgram(prog, vertUniforms, nil)
- st.prog.layout = progLayout
- iprog, err := ctx.NewProgram(shader_intersect_vert, shader_intersect_frag)
+ defer vsh.Release()
+ defer fsh.Release()
+ st.pipeline.uniforms = new(stencilUniforms)
+ vertUniforms := newUniformBuffer(ctx, st.pipeline.uniforms)
+ pipe, err := st.ctx.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ VertexLayout: progLayout,
+ BlendDesc: driver.BlendDesc{
+ Enable: true,
+ SrcFactor: driver.BlendFactorOne,
+ DstFactor: driver.BlendFactorOne,
+ },
+ PixelFormat: driver.TextureFormatFloat,
+ Topology: driver.TopologyTriangles,
+ })
+ st.pipeline.pipeline = &pipeline{pipe, vertUniforms}
+ if err != nil {
+ panic(err)
+ }
+ vsh, fsh, err = newShaders(ctx, gio.Shader_intersect_vert, gio.Shader_intersect_frag)
if err != nil {
panic(err)
}
- st.iprog.uniforms = new(intersectUniforms)
- vertUniforms = newUniformBuffer(ctx, &st.iprog.uniforms.vert)
- st.iprog.prog = newProgram(iprog, vertUniforms, nil)
- st.iprog.layout = iprogLayout
+ defer vsh.Release()
+ defer fsh.Release()
+ st.ipipeline.uniforms = new(intersectUniforms)
+ vertUniforms = newUniformBuffer(ctx, &st.ipipeline.uniforms.vert)
+ ipipe, err := st.ctx.NewPipeline(driver.PipelineDesc{
+ VertexShader: vsh,
+ FragmentShader: fsh,
+ VertexLayout: iprogLayout,
+ BlendDesc: driver.BlendDesc{
+ Enable: true,
+ SrcFactor: driver.BlendFactorDstColor,
+ DstFactor: driver.BlendFactorZero,
+ },
+ PixelFormat: driver.TextureFormatFloat,
+ Topology: driver.TopologyTriangleStrip,
+ })
+ st.ipipeline.pipeline = &pipeline{ipipe, vertUniforms}
return st
}
-func (s *fboSet) resize(ctx backend.Device, sizes []image.Point) {
+func (s *fboSet) resize(ctx driver.Device, sizes []image.Point) {
// Add fbos.
for i := len(s.fbos); i < len(sizes); i++ {
s.fbos = append(s.fbos, stencilFBO{})
@@ -231,38 +256,25 @@ func (s *fboSet) resize(ctx backend.Device, sizes []image.Point) {
waste := float32(sz.X*sz.Y) / float32(f.size.X*f.size.Y)
resize = resize || waste > 1.2
if resize {
- if f.fbo != nil {
- f.fbo.Release()
+ if f.tex != nil {
f.tex.Release()
}
- tex, err := ctx.NewTexture(backend.TextureFormatFloat, sz.X, sz.Y, backend.FilterNearest, backend.FilterNearest,
- backend.BufferBindingTexture|backend.BufferBindingFramebuffer)
- if err != nil {
- panic(err)
- }
- fbo, err := ctx.NewFramebuffer(tex, 0)
+ tex, err := ctx.NewTexture(driver.TextureFormatFloat, sz.X, sz.Y, driver.FilterNearest, driver.FilterNearest,
+ driver.BufferBindingTexture|driver.BufferBindingFramebuffer)
if err != nil {
panic(err)
}
f.size = sz
f.tex = tex
- f.fbo = fbo
}
}
// Delete extra fbos.
s.delete(ctx, len(sizes))
}
-func (s *fboSet) invalidate(ctx backend.Device) {
- for _, f := range s.fbos {
- f.fbo.Invalidate()
- }
-}
-
-func (s *fboSet) delete(ctx backend.Device, idx int) {
+func (s *fboSet) delete(ctx driver.Device, idx int) {
for i := idx; i < len(s.fbos); i++ {
f := s.fbos[i]
- f.fbo.Release()
f.tex.Release()
}
s.fbos = s.fbos[:idx]
@@ -270,10 +282,9 @@ func (s *fboSet) delete(ctx backend.Device, idx int) {
func (s *stenciler) release() {
s.fbos.delete(s.ctx, 0)
- s.prog.layout.Release()
- s.prog.prog.Release()
- s.iprog.layout.Release()
- s.iprog.prog.Release()
+ s.intersections.delete(s.ctx, 0)
+ s.pipeline.pipeline.Release()
+ s.ipipeline.pipeline.Release()
s.indexBuf.Release()
}
@@ -283,14 +294,13 @@ func (p *pather) release() {
}
func (c *coverer) release() {
- for _, p := range c.prog {
+ for _, p := range c.pipelines {
p.Release()
}
- c.layout.Release()
}
-func buildPath(ctx backend.Device, p []byte) pathData {
- buf, err := ctx.NewImmutableBuffer(backend.BufferBindingVertices, p)
+func buildPath(ctx driver.Device, p []byte) pathData {
+ buf, err := ctx.NewImmutableBuffer(driver.BufferBindingVertices, p)
if err != nil {
panic(err)
}
@@ -313,17 +323,10 @@ func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.
}
func (s *stenciler) beginIntersect(sizes []image.Point) {
- s.ctx.BlendFunc(backend.BlendFactorDstColor, backend.BlendFactorZero)
// 8 bit coverage is enough, but OpenGL ES only supports single channel
// floating point formats. Replace with GL_RGB+GL_UNSIGNED_BYTE if
// no floating point support is available.
s.intersections.resize(s.ctx, sizes)
- s.ctx.BindProgram(s.iprog.prog.prog)
-}
-
-func (s *stenciler) invalidateFBO() {
- s.intersections.invalidate(s.ctx)
- s.fbos.invalidate(s.ctx)
}
func (s *stenciler) cover(idx int) stencilFBO {
@@ -331,11 +334,7 @@ func (s *stenciler) cover(idx int) stencilFBO {
}
func (s *stenciler) begin(sizes []image.Point) {
- s.ctx.BlendFunc(backend.BlendFactorOne, backend.BlendFactorOne)
s.fbos.resize(s.ctx, sizes)
- s.ctx.BindProgram(s.prog.prog.prog)
- s.ctx.BindInputLayout(s.prog.layout)
- s.ctx.BindIndexBuffer(s.indexBuf)
}
func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) {
@@ -344,9 +343,9 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
- s.prog.uniforms.vert.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y}
- s.prog.uniforms.vert.pathOffset = [2]float32{offset.X, offset.Y}
- s.prog.prog.UploadUniforms()
+ s.pipeline.uniforms.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y}
+ s.pipeline.uniforms.pathOffset = [2]float32{offset.X, offset.Y}
+ s.pipeline.pipeline.UploadUniforms(s.ctx)
// Draw in batches that fit in uint16 indices.
start := 0
nquads := data.ncurves / 4
@@ -356,35 +355,40 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima
batch = max
}
off := vertStride * start * 4
- s.ctx.BindVertexBuffer(data.data, vertStride, off)
- s.ctx.DrawElements(backend.DrawModeTriangles, 0, batch*6)
+ s.ctx.BindVertexBuffer(data.data, off)
+ s.ctx.DrawElements(0, batch*6)
start += batch
}
}
-func (p *pather) cover(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
- p.coverer.cover(z, mat, col, scale, off, uvTrans, coverScale, coverOff)
+func (p *pather) cover(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
+ p.coverer.cover(mat, col, col1, col2, scale, off, uvTrans, coverScale, coverOff)
}
-func (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
- p := c.prog[mat]
- c.ctx.BindProgram(p.prog)
+func (c *coverer) cover(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
var uniforms *coverUniforms
switch mat {
case materialColor:
- c.colUniforms.frag.color = col
- uniforms = &c.colUniforms.vert.coverUniforms
+ c.colUniforms.color = col
+ uniforms = &c.colUniforms.coverUniforms
+ case materialLinearGradient:
+ c.linearGradientUniforms.color1 = col1
+ c.linearGradientUniforms.color2 = col2
+
+ t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
+ c.linearGradientUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
+ c.linearGradientUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
+ uniforms = &c.linearGradientUniforms.coverUniforms
case materialTexture:
t1, t2, t3, t4, t5, t6 := uvTrans.Elems()
- c.texUniforms.vert.uvTransformR1 = [4]float32{t1, t2, t3, 0}
- c.texUniforms.vert.uvTransformR2 = [4]float32{t4, t5, t6, 0}
- uniforms = &c.texUniforms.vert.coverUniforms
+ c.texUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0}
+ c.texUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
+ uniforms = &c.texUniforms.coverUniforms
}
- uniforms.z = z
uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
uniforms.uvCoverTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
- p.UploadUniforms()
- c.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4)
+ c.pipelines[mat].UploadUniforms(c.ctx)
+ c.ctx.DrawArrays(0, 4)
}
func init() {
diff --git a/vendor/gioui.org/gpu/shaders.go b/vendor/gioui.org/gpu/shaders.go
deleted file mode 100644
index d8e8435..0000000
--- a/vendor/gioui.org/gpu/shaders.go
+++ /dev/null
@@ -1,657 +0,0 @@
-// Code generated by build.go. DO NOT EDIT.
-
-package gpu
-
-import "gioui.org/gpu/backend"
-
-var (
- shader_blit_frag = [...]backend.ShaderSources{
- {
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Color", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_12._color", Type: 0x0, Size: 4, Offset: 0}},
- Size: 16,
- },
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = _12._color;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nlayout(std140) uniform Color\n{\n vec4 _color;\n} _12;\n\nlayout(location = 0) out vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n}\n\n",
- GLSL130: "#version 130\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n}\n\n",
- GLSL150: "#version 150\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n}\n\n",
- /*
- cbuffer Color : register(b0)
- {
- float4 _12_color : packoffset(c0);
- };
-
-
- static float4 fragColor;
- static float2 vUV;
-
- struct SPIRV_Cross_Input
- {
- float2 vUV : TEXCOORD0;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- fragColor = _12_color;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vUV = stage_input.vUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xc6, 0x4a, 0x23, 0x81, 0x87, 0xab, 0xe3, 0xca, 0x12, 0x9, 0x7e, 0x2f, 0x5e, 0x2, 0x62, 0x14, 0x1, 0x0, 0x0, 0x0, 0x70, 0x2, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x84, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x48, 0x1, 0x0, 0x0, 0x8, 0x2, 0x0, 0x0, 0x3c, 0x2, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x44, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x14, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x40, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x6, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xb8, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x32, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- },
- {
- Textures: []backend.TextureBinding{{Name: "tex", Binding: 0}},
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\n\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = texture2D(tex, vUV);\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\n\nlayout(location = 0) out vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n}\n\n",
- GLSL130: "#version 130\n\nuniform sampler2D tex;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n}\n\n",
- GLSL150: "#version 150\n\nuniform sampler2D tex;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n}\n\n",
- /*
- Texture2D<float4> tex : register(t0);
- SamplerState _tex_sampler : register(s0);
-
- static float4 fragColor;
- static float2 vUV;
-
- struct SPIRV_Cross_Input
- {
- float2 vUV : TEXCOORD0;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- fragColor = tex.Sample(_tex_sampler, vUV);
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vUV = stage_input.vUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xb7, 0x3f, 0x1d, 0xb1, 0x80, 0xcd, 0x80, 0xa3, 0x57, 0x9, 0xfb, 0x5a, 0x9f, 0x56, 0xd6, 0xda, 0x1, 0x0, 0x0, 0x0, 0x94, 0x2, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xa4, 0x0, 0x0, 0x0, 0x10, 0x1, 0x0, 0x0, 0x8c, 0x1, 0x0, 0x0, 0x2c, 0x2, 0x0, 0x0, 0x60, 0x2, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x64, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x3c, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x3, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x0, 0x8, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x64, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x6d, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x69, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x5f, 0x74, 0x65, 0x78, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x74, 0x65, 0x78, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- },
- }
- shader_blit_vert = backend.ShaderSources{
- Inputs: []backend.InputLocation{{Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 2}},
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_52.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_52.uvTransformR1", Type: 0x0, Size: 4, Offset: 16}, {Name: "_52.uvTransformR2", Type: 0x0, Size: 4, Offset: 32}, {Name: "_52.z", Type: 0x0, Size: 1, Offset: 48}},
- Size: 52,
- },
- GLSL100ES: "\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _52;\n\nattribute vec2 pos;\nvarying vec2 vUV;\nattribute vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n",
- GLSL300ES: "#version 300 es\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nlayout(std140) uniform Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n} _52;\n\nlayout(location = 0) in vec2 pos;\nout vec2 vUV;\nlayout(location = 1) in vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n",
- GLSL130: "#version 130\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _52;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n",
- GLSL150: "#version 150\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _52;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n",
- /*
- struct m3x2
- {
- float3 r0;
- float3 r1;
- };
-
- static const m3x2 _116 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, -1.0f, 1.0f) };
- static const m3x2 _118 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, 1.0f, 0.0f) };
-
- cbuffer Block : register(b0)
- {
- float4 _69_transform : packoffset(c0);
- float4 _69_uvTransformR1 : packoffset(c1);
- float4 _69_uvTransformR2 : packoffset(c2);
- float _69_z : packoffset(c3);
- };
-
-
- static float4 gl_Position;
- static float2 pos;
- static float2 vUV;
- static float2 uv;
-
- struct SPIRV_Cross_Input
- {
- float2 pos : POSITION;
- float2 uv : NORMAL;
- };
-
- struct SPIRV_Cross_Output
- {
- float2 vUV : TEXCOORD0;
- float4 gl_Position : SV_Position;
- };
-
- float4 toClipSpace(float4 pos_1)
- {
- return float4(pos_1.xy, (pos_1.z + pos_1.w) * 0.5f, pos_1.w);
- }
-
- float3 transform3x2(m3x2 t, float3 v)
- {
- return float3(dot(t.r0, v), dot(t.r1, v), dot(float3(0.0f, 0.0f, 1.0f), v));
- }
-
- void vert_main()
- {
- float2 p = (pos * _69_transform.xy) + _69_transform.zw;
- float4 param = float4(p, _69_z, 1.0f);
- gl_Position = toClipSpace(param);
- m3x2 _103 = { _69_uvTransformR1.xyz, _69_uvTransformR2.xyz };
- m3x2 param_1 = _103;
- float3 param_2 = float3(uv, 1.0f);
- vUV = transform3x2(param_1, param_2).xy;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- pos = stage_input.pos;
- uv = stage_input.uv;
- vert_main();
- SPIRV_Cross_Output stage_output;
- stage_output.gl_Position = gl_Position;
- stage_output.vUV = vUV;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x57, 0x0, 0x34, 0xa, 0xf6, 0x3b, 0x1b, 0xc4, 0x28, 0xa1, 0x26, 0x75, 0x95, 0x95, 0xc4, 0x27, 0x1, 0x0, 0x0, 0x0, 0xc0, 0x4, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x24, 0x1, 0x0, 0x0, 0x54, 0x2, 0x0, 0x0, 0xd0, 0x2, 0x0, 0x0, 0x18, 0x4, 0x0, 0x0, 0x68, 0x4, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xe4, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x5, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x7, 0x80, 0x1, 0x0, 0xc4, 0x90, 0x5, 0x0, 0xd0, 0xa0, 0x5, 0x0, 0xc5, 0xa0, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0xe0, 0x2, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0xe0, 0x3, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x90, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7, 0x80, 0x5, 0x0, 0xe4, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0xc, 0xc0, 0x4, 0x0, 0x0, 0xa0, 0x0, 0x0, 0x64, 0x80, 0x0, 0x0, 0x24, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x28, 0x1, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x4a, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x10, 0x0, 0x0, 0x8, 0x12, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x8, 0x22, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xa, 0x42, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x36, 0x0, 0x0, 0x5, 0x82, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x40, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0x18, 0x1, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xee, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x31, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x32, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x7a, 0x0, 0xab, 0xab, 0x0, 0x0, 0x3, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x48, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x50, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab},
- }
- shader_cover_frag = [...]backend.ShaderSources{
- {
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Color", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_12._color", Type: 0x0, Size: 4, Offset: 0}},
- Size: 16,
- },
- Textures: []backend.TextureBinding{{Name: "cover", Binding: 1}},
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nuniform mediump sampler2D cover;\n\nvarying highp vec2 vCoverUV;\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = _12._color;\n float cover_1 = abs(texture2D(cover, vCoverUV).x);\n gl_FragData[0] *= cover_1;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nlayout(std140) uniform Color\n{\n vec4 _color;\n} _12;\n\nuniform mediump sampler2D cover;\n\nlayout(location = 0) out vec4 fragColor;\nin highp vec2 vCoverUV;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- GLSL130: "#version 130\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vCoverUV;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- GLSL150: "#version 150\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vCoverUV;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- /*
- cbuffer Color : register(b0)
- {
- float4 _12_color : packoffset(c0);
- };
-
- Texture2D<float4> cover : register(t1);
- SamplerState _cover_sampler : register(s1);
-
- static float4 fragColor;
- static float2 vCoverUV;
- static float2 vUV;
-
- struct SPIRV_Cross_Input
- {
- float2 vCoverUV : TEXCOORD0;
- float2 vUV : TEXCOORD1;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- fragColor = _12_color;
- float cover_1 = abs(cover.Sample(_cover_sampler, vCoverUV).x);
- fragColor *= cover_1;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vCoverUV = stage_input.vCoverUV;
- vUV = stage_input.vUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x94, 0xe9, 0xc3, 0x12, 0xd8, 0xfb, 0xb, 0x3b, 0xe, 0xda, 0x43, 0x25, 0xcb, 0x53, 0x1c, 0x9d, 0x1, 0x0, 0x0, 0x0, 0x84, 0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x74, 0x1, 0x0, 0x0, 0xf0, 0x1, 0x0, 0x0, 0x4, 0x3, 0x0, 0x0, 0x50, 0x3, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x8c, 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x58, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x28, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x34, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x34, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x0, 0x8, 0xe4, 0xa0, 0x23, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0xa0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x9, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xc, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8b, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x91, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x0, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0x91, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x32, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x44, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- },
- {
- Textures: []backend.TextureBinding{{Name: "tex", Binding: 0}, {Name: "cover", Binding: 1}},
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\nuniform mediump sampler2D cover;\n\nvarying vec2 vUV;\nvarying highp vec2 vCoverUV;\n\nvoid main()\n{\n gl_FragData[0] = texture2D(tex, vUV);\n float cover_1 = abs(texture2D(cover, vCoverUV).x);\n gl_FragData[0] *= cover_1;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\nuniform mediump sampler2D cover;\n\nlayout(location = 0) out vec4 fragColor;\nin vec2 vUV;\nin highp vec2 vCoverUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- GLSL130: "#version 130\n\nuniform sampler2D tex;\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vUV;\nin vec2 vCoverUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- GLSL150: "#version 150\n\nuniform sampler2D tex;\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vUV;\nin vec2 vCoverUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n",
- /*
- Texture2D<float4> tex : register(t0);
- SamplerState _tex_sampler : register(s0);
- Texture2D<float4> cover : register(t1);
- SamplerState _cover_sampler : register(s1);
-
- static float4 fragColor;
- static float2 vUV;
- static float2 vCoverUV;
-
- struct SPIRV_Cross_Input
- {
- float2 vCoverUV : TEXCOORD0;
- float2 vUV : TEXCOORD1;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- fragColor = tex.Sample(_tex_sampler, vUV);
- float cover_1 = abs(cover.Sample(_cover_sampler, vCoverUV).x);
- fragColor *= cover_1;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vUV = stage_input.vUV;
- vCoverUV = stage_input.vCoverUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xcb, 0xa0, 0xb0, 0x5b, 0x6e, 0x48, 0xa6, 0x5a, 0x1c, 0x34, 0x50, 0xfe, 0xd4, 0x6f, 0x72, 0xb5, 0x1, 0x0, 0x0, 0x0, 0xbc, 0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xec, 0x0, 0x0, 0x0, 0xcc, 0x1, 0x0, 0x0, 0x48, 0x2, 0x0, 0x0, 0x3c, 0x3, 0x0, 0x0, 0x88, 0x3, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xac, 0x0, 0x0, 0x0, 0xac, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x80, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x2, 0x0, 0x24, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x1, 0x8, 0xf, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x1b, 0xb0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x8, 0xe4, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x1, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x1, 0x8, 0xe4, 0xa0, 0x23, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0xd8, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0xc2, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x1a, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x8, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0xe, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xec, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0xc2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa9, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xb8, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x5f, 0x74, 0x65, 0x78, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x74, 0x65, 0x78, 0x0, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x44, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xc, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- },
- }
- shader_cover_vert = backend.ShaderSources{
- Inputs: []backend.InputLocation{{Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 2}},
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_53.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_53.uvCoverTransform", Type: 0x0, Size: 4, Offset: 16}, {Name: "_53.uvTransformR1", Type: 0x0, Size: 4, Offset: 32}, {Name: "_53.uvTransformR2", Type: 0x0, Size: 4, Offset: 48}, {Name: "_53.z", Type: 0x0, Size: 1, Offset: 64}},
- Size: 68,
- },
- GLSL100ES: "\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _53;\n\nattribute vec2 pos;\nvarying vec2 vUV;\nattribute vec2 uv;\nvarying vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n",
- GLSL300ES: "#version 300 es\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nlayout(std140) uniform Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n} _53;\n\nlayout(location = 0) in vec2 pos;\nout vec2 vUV;\nlayout(location = 1) in vec2 uv;\nout vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n",
- GLSL130: "#version 130\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _53;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\nout vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n",
- GLSL150: "#version 150\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _53;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\nout vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n",
- /*
- struct m3x2
- {
- float3 r0;
- float3 r1;
- };
-
- static const m3x2 _115 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, -1.0f, 1.0f) };
- static const m3x2 _141 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, 1.0f, 0.0f) };
-
- cbuffer Block : register(b0)
- {
- float4 _70_transform : packoffset(c0);
- float4 _70_uvCoverTransform : packoffset(c1);
- float4 _70_uvTransformR1 : packoffset(c2);
- float4 _70_uvTransformR2 : packoffset(c3);
- float _70_z : packoffset(c4);
- };
-
-
- static float4 gl_Position;
- static float2 pos;
- static float2 vUV;
- static float2 uv;
- static float2 vCoverUV;
-
- struct SPIRV_Cross_Input
- {
- float2 pos : POSITION;
- float2 uv : NORMAL;
- };
-
- struct SPIRV_Cross_Output
- {
- float2 vCoverUV : TEXCOORD0;
- float2 vUV : TEXCOORD1;
- float4 gl_Position : SV_Position;
- };
-
- float4 toClipSpace(float4 pos_1)
- {
- return float4(pos_1.xy, (pos_1.z + pos_1.w) * 0.5f, pos_1.w);
- }
-
- float3 transform3x2(m3x2 t, float3 v)
- {
- return float3(dot(t.r0, v), dot(t.r1, v), dot(float3(0.0f, 0.0f, 1.0f), v));
- }
-
- void vert_main()
- {
- float4 param = float4((pos * _70_transform.xy) + _70_transform.zw, _70_z, 1.0f);
- gl_Position = toClipSpace(param);
- m3x2 _101 = { _70_uvTransformR1.xyz, _70_uvTransformR2.xyz };
- m3x2 param_1 = _101;
- float3 param_2 = float3(uv, 1.0f);
- vUV = transform3x2(param_1, param_2).xy;
- m3x2 param_3 = _115;
- float3 param_4 = float3(uv, 1.0f);
- float3 uv3 = transform3x2(param_3, param_4);
- vCoverUV = ((uv3 * float3(_70_uvCoverTransform.xy, 1.0f)) + float3(_70_uvCoverTransform.zw, 0.0f)).xy;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- pos = stage_input.pos;
- uv = stage_input.uv;
- vert_main();
- SPIRV_Cross_Output stage_output;
- stage_output.gl_Position = gl_Position;
- stage_output.vUV = vUV;
- stage_output.vCoverUV = vCoverUV;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xb3, 0x71, 0x8c, 0x9f, 0x3e, 0x4f, 0x8b, 0x50, 0xb7, 0xa2, 0x4b, 0xfb, 0xad, 0x7d, 0xc8, 0x71, 0x1, 0x0, 0x0, 0x0, 0xcc, 0x5, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x78, 0x1, 0x0, 0x0, 0x1c, 0x3, 0x0, 0x0, 0x98, 0x3, 0x0, 0x0, 0xc, 0x5, 0x0, 0x0, 0x5c, 0x5, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x38, 0x1, 0x0, 0x0, 0x38, 0x1, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x4, 0x1, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x6, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0x3f, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x7, 0x80, 0x1, 0x0, 0xc4, 0x90, 0x6, 0x0, 0xd0, 0xa0, 0x6, 0x0, 0xc5, 0xa0, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x8, 0xe0, 0x3, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x4, 0xe0, 0x4, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x1, 0x0, 0xe1, 0x90, 0x6, 0x0, 0xe4, 0xa0, 0x6, 0x0, 0xe1, 0xa0, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x6, 0x0, 0xe2, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0xe0, 0x0, 0x0, 0xe4, 0x80, 0x2, 0x0, 0xe4, 0xa0, 0x2, 0x0, 0xee, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x90, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xb, 0x80, 0x6, 0x0, 0xe4, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0xc, 0xc0, 0x5, 0x0, 0x0, 0xa0, 0x0, 0x0, 0x74, 0x80, 0x0, 0x0, 0x34, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x9c, 0x1, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x67, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xc2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0xf, 0x0, 0x0, 0xa, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x5, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x8, 0x42, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x8, 0x82, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xa, 0x42, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x36, 0x0, 0x0, 0x5, 0x82, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x6c, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0x44, 0x1, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf4, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x1, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x1, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x34, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x75, 0x76, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x31, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x32, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x7a, 0x0, 0xab, 0x0, 0x0, 0x3, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x48, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x68, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x3, 0x0, 0x0, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab},
- }
- shader_intersect_frag = backend.ShaderSources{
- Textures: []backend.TextureBinding{{Name: "cover", Binding: 0}},
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D cover;\n\nvarying highp vec2 vUV;\n\nvoid main()\n{\n float cover_1 = abs(texture2D(cover, vUV).x);\n gl_FragData[0].x = cover_1;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D cover;\n\nin highp vec2 vUV;\nlayout(location = 0) out vec4 fragColor;\n\nvoid main()\n{\n float cover_1 = abs(texture(cover, vUV).x);\n fragColor.x = cover_1;\n}\n\n",
- GLSL130: "#version 130\n\nuniform sampler2D cover;\n\nin vec2 vUV;\nout vec4 fragColor;\n\nvoid main()\n{\n float cover_1 = abs(texture(cover, vUV).x);\n fragColor.x = cover_1;\n}\n\n",
- GLSL150: "#version 150\n\nuniform sampler2D cover;\n\nin vec2 vUV;\nout vec4 fragColor;\n\nvoid main()\n{\n float cover_1 = abs(texture(cover, vUV).x);\n fragColor.x = cover_1;\n}\n\n",
- /*
- Texture2D<float4> cover : register(t0);
- SamplerState _cover_sampler : register(s0);
-
- static float2 vUV;
- static float4 fragColor;
-
- struct SPIRV_Cross_Input
- {
- float2 vUV : TEXCOORD0;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragColor : SV_Target0;
- };
-
- void frag_main()
- {
- float cover_1 = abs(cover.Sample(_cover_sampler, vUV).x);
- fragColor.x = cover_1;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vUV = stage_input.vUV;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragColor = fragColor;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xe0, 0xe4, 0x3, 0x8c, 0xac, 0x56, 0x46, 0x82, 0x6c, 0xe7, 0x7c, 0xc3, 0x54, 0xa6, 0x27, 0xef, 0x1, 0x0, 0x0, 0x0, 0x8, 0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0xfc, 0x1, 0x0, 0x0, 0xa0, 0x2, 0x0, 0x0, 0xd4, 0x2, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x94, 0x0, 0x0, 0x0, 0x94, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x6c, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x51, 0x0, 0x0, 0x5, 0x0, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x3, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x0, 0x8, 0xe4, 0xa0, 0x23, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xe, 0x80, 0x0, 0x0, 0x0, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0xa4, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x6, 0x12, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xe2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x71, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6b, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- }
- shader_intersect_vert = backend.ShaderSources{
- Inputs: []backend.InputLocation{{Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 2}},
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_78.uvTransform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_78.subUVTransform", Type: 0x0, Size: 4, Offset: 16}},
- Size: 32,
- },
- GLSL100ES: "\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n};\n\nuniform Block _78;\n\nattribute vec2 pos;\nattribute vec2 uv;\nvarying vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n",
- GLSL300ES: "#version 300 es\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nlayout(std140) uniform Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n} _78;\n\nlayout(location = 0) in vec2 pos;\nlayout(location = 1) in vec2 uv;\nout vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n",
- GLSL130: "#version 130\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n};\n\nuniform Block _78;\n\nin vec2 pos;\nin vec2 uv;\nout vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n",
- GLSL150: "#version 150\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n};\n\nuniform Block _78;\n\nin vec2 pos;\nin vec2 uv;\nout vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n",
- /*
- struct m3x2
- {
- float3 r0;
- float3 r1;
- };
-
- static const m3x2 _38 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, 1.0f, 0.0f) };
- static const m3x2 _63 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, -1.0f, 1.0f) };
-
- cbuffer Block : register(b0)
- {
- float4 _78_uvTransform : packoffset(c0);
- float4 _78_subUVTransform : packoffset(c1);
- };
-
-
- static float4 gl_Position;
- static float2 pos;
- static float2 uv;
- static float2 vUV;
-
- struct SPIRV_Cross_Input
- {
- float2 pos : POSITION;
- float2 uv : NORMAL;
- };
-
- struct SPIRV_Cross_Output
- {
- float2 vUV : TEXCOORD0;
- float4 gl_Position : SV_Position;
- };
-
- float3 transform3x2(m3x2 t, float3 v)
- {
- return float3(dot(t.r0, v), dot(t.r1, v), dot(float3(0.0f, 0.0f, 1.0f), v));
- }
-
- void vert_main()
- {
- m3x2 param = _38;
- float3 param_1 = float3(pos, 1.0f);
- float3 p = transform3x2(param, param_1);
- gl_Position = float4(p, 1.0f);
- m3x2 param_2 = _63;
- float3 param_3 = float3(uv, 1.0f);
- float3 uv3 = transform3x2(param_2, param_3);
- vUV = (uv3.xy * _78_subUVTransform.xy) + _78_subUVTransform.zw;
- m3x2 param_4 = _63;
- float3 param_5 = float3(vUV, 1.0f);
- vUV = transform3x2(param_4, param_5).xy;
- vUV = (vUV * _78_uvTransform.xy) + _78_uvTransform.zw;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- pos = stage_input.pos;
- uv = stage_input.uv;
- vert_main();
- SPIRV_Cross_Output stage_output;
- stage_output.gl_Position = gl_Position;
- stage_output.vUV = vUV;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x27, 0x8, 0x9f, 0xbf, 0x30, 0xa, 0x5b, 0x38, 0xe, 0x78, 0x0, 0x22, 0xdb, 0x3b, 0x30, 0x54, 0x1, 0x0, 0x0, 0x0, 0xd8, 0x4, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x4c, 0x1, 0x0, 0x0, 0xc4, 0x2, 0x0, 0x0, 0x40, 0x3, 0x0, 0x0, 0x30, 0x4, 0x0, 0x0, 0x80, 0x4, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xc, 0x1, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0xd8, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x3, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x1, 0x0, 0x55, 0x90, 0x3, 0x0, 0xe4, 0xa0, 0x3, 0x0, 0xe1, 0xa0, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x3, 0x0, 0xe2, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x2, 0x0, 0xe4, 0xa0, 0x2, 0x0, 0xee, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x4, 0x80, 0x3, 0x0, 0x0, 0xa0, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x8, 0x80, 0x3, 0x0, 0xc9, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0xe0, 0x0, 0x0, 0xec, 0x80, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x90, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xc, 0xc0, 0x3, 0x0, 0x0, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x70, 0x1, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x36, 0x0, 0x0, 0x5, 0x52, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x14, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0xa, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0xa, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0xf, 0x0, 0x0, 0xa, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x5, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xc2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xe8, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x38, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x38, 0x5f, 0x73, 0x75, 0x62, 0x55, 0x56, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x48, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x50, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab},
- }
- shader_stencil_frag = backend.ShaderSources{
- GLSL100ES: "precision mediump float;\nprecision highp int;\n\nvarying vec2 vTo;\nvarying vec2 vFrom;\nvarying vec2 vCtrl;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n gl_FragData[0].x = area;\n}\n\n",
- GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nin vec2 vTo;\nin vec2 vFrom;\nin vec2 vCtrl;\nlayout(location = 0) out vec4 fragCover;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n fragCover.x = area;\n}\n\n",
- GLSL130: "#version 130\n\nin vec2 vTo;\nin vec2 vFrom;\nin vec2 vCtrl;\nout vec4 fragCover;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n fragCover.x = area;\n}\n\n",
- GLSL150: "#version 150\n\nin vec2 vTo;\nin vec2 vFrom;\nin vec2 vCtrl;\nout vec4 fragCover;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n fragCover.x = area;\n}\n\n",
- /*
- static float2 vTo;
- static float2 vFrom;
- static float2 vCtrl;
- static float4 fragCover;
-
- struct SPIRV_Cross_Input
- {
- float2 vFrom : TEXCOORD0;
- float2 vCtrl : TEXCOORD1;
- float2 vTo : TEXCOORD2;
- };
-
- struct SPIRV_Cross_Output
- {
- float4 fragCover : SV_Target0;
- };
-
- void frag_main()
- {
- float dx = vTo.x - vFrom.x;
- bool increasing = vTo.x >= vFrom.x;
- bool2 _35 = increasing.xx;
- float2 left = float2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);
- bool2 _41 = increasing.xx;
- float2 right = float2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);
- float2 extent = clamp(float2(vFrom.x, vTo.x), (-0.5f).xx, 0.5f.xx);
- float midx = lerp(extent.x, extent.y, 0.5f);
- float x0 = midx - left.x;
- float2 p1 = vCtrl - left;
- float2 v = right - vCtrl;
- float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));
- float y = lerp(lerp(left.y, vCtrl.y, t), lerp(vCtrl.y, right.y, t), t);
- float2 d_half = lerp(p1, v, t.xx);
- float dy = d_half.y / d_half.x;
- float width = extent.y - extent.x;
- dy = abs(dy * width);
- float4 sides = float4((dy * 0.5f) + y, (dy * (-0.5f)) + y, (0.5f - y) / dy, ((-0.5f) - y) / dy);
- sides = clamp(sides + 0.5f.xxxx, 0.0f.xxxx, 1.0f.xxxx);
- float area = 0.5f * ((((sides.z - (sides.z * sides.y)) + 1.0f) - sides.x) + (sides.x * sides.w));
- area *= width;
- if (width == 0.0f)
- {
- area = 0.0f;
- }
- fragCover.x = area;
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- vTo = stage_input.vTo;
- vFrom = stage_input.vFrom;
- vCtrl = stage_input.vCtrl;
- frag_main();
- SPIRV_Cross_Output stage_output;
- stage_output.fragCover = fragCover;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x94, 0x21, 0xb9, 0x13, 0x4c, 0xba, 0xd, 0x11, 0x8f, 0xc7, 0xce, 0xe, 0x41, 0x73, 0xec, 0xe1, 0x1, 0x0, 0x0, 0x0, 0x5c, 0xa, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x9c, 0x3, 0x0, 0x0, 0xfc, 0x8, 0x0, 0x0, 0x78, 0x9, 0x0, 0x0, 0xc4, 0x9, 0x0, 0x0, 0x28, 0xa, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x5c, 0x3, 0x0, 0x0, 0x5c, 0x3, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x38, 0x3, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x2, 0xff, 0xff, 0x51, 0x0, 0x0, 0x5, 0x0, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x3, 0xb0, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0xa0, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0xa0, 0xa, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x81, 0x1, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x1, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0xa, 0x0, 0x0, 0x3, 0x2, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0xb0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x81, 0xb, 0x0, 0x0, 0x3, 0x3, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0xb0, 0x1, 0x0, 0x0, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x4, 0x80, 0x1, 0x0, 0x0, 0x81, 0x1, 0x0, 0x0, 0xb0, 0x58, 0x0, 0x0, 0x4, 0x3, 0x0, 0x2, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x55, 0xb0, 0x1, 0x0, 0x55, 0x80, 0x58, 0x0, 0x0, 0x4, 0x2, 0x0, 0x2, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x55, 0x80, 0x1, 0x0, 0x55, 0xb0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0xc, 0x80, 0x3, 0x0, 0x1b, 0x80, 0x0, 0x0, 0xe4, 0xb1, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0x80, 0x2, 0x0, 0xe4, 0x81, 0x0, 0x0, 0x1b, 0xb0, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0xff, 0x80, 0x1, 0x0, 0x0, 0x81, 0x5, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x7, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x6, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x0, 0x80, 0x6, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0x55, 0x80, 0x2, 0x0, 0x55, 0x80, 0x12, 0x0, 0x0, 0x4, 0x2, 0x0, 0x3, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x1b, 0x80, 0x1, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x0, 0x0, 0xaa, 0xb0, 0x12, 0x0, 0x0, 0x4, 0x2, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x6, 0x0, 0x0, 0x2, 0x0, 0x0, 0x2, 0x80, 0x2, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x55, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x55, 0x80, 0x23, 0x0, 0x0, 0x2, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x1, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x2, 0x0, 0xaa, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0xa0, 0x2, 0x0, 0xaa, 0x80, 0x6, 0x0, 0x0, 0x2, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0xc, 0x80, 0x2, 0x0, 0xaa, 0x81, 0x0, 0x0, 0x1b, 0xa0, 0x5, 0x0, 0x0, 0x3, 0x1, 0x0, 0x8, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xff, 0x80, 0x5, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x1f, 0x80, 0x1, 0x0, 0xe4, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x55, 0x81, 0x1, 0x0, 0xaa, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0x0, 0x81, 0x0, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0xff, 0x80, 0x0, 0x0, 0x55, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x58, 0x0, 0x0, 0x4, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x81, 0x0, 0x0, 0xff, 0xa0, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xe, 0x80, 0x0, 0x0, 0xff, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x58, 0x5, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x56, 0x1, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0xc2, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0xa, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0xa, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1d, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x9, 0x42, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x9, 0x42, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x72, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0xa6, 0x1b, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x9, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0x1e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xb2, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0xe, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x8, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4b, 0x0, 0x0, 0x5, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0xc2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0xd, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0xe, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3a, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xd, 0x32, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0xa6, 0xa, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x4, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0xa, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0xa, 0xf2, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0xe, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x32, 0x0, 0x0, 0xa, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x8, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x7, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x37, 0x0, 0x0, 0x9, 0x12, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xe2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x5c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xc, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
- }
- shader_stencil_vert = backend.ShaderSources{
- Inputs: []backend.InputLocation{{Name: "corner", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 1}, {Name: "maxy", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 1}, {Name: "from", Location: 2, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "ctrl", Location: 3, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}, {Name: "to", Location: 4, Semantic: "TEXCOORD", SemanticIndex: 2, Type: 0x0, Size: 2}},
- Uniforms: backend.UniformsReflection{
- Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}},
- Locations: []backend.UniformLocation{{Name: "_16.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_16.pathOffset", Type: 0x0, Size: 2, Offset: 16}},
- Size: 24,
- },
- GLSL100ES: "\nstruct Block\n{\n vec4 transform;\n vec2 pathOffset;\n};\n\nuniform Block _16;\n\nattribute vec2 from;\nattribute vec2 ctrl;\nattribute vec2 to;\nattribute float maxy;\nattribute float corner;\nvarying vec2 vFrom;\nvarying vec2 vCtrl;\nvarying vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n",
- GLSL300ES: "#version 300 es\n\nlayout(std140) uniform Block\n{\n vec4 transform;\n vec2 pathOffset;\n} _16;\n\nlayout(location = 2) in vec2 from;\nlayout(location = 3) in vec2 ctrl;\nlayout(location = 4) in vec2 to;\nlayout(location = 1) in float maxy;\nlayout(location = 0) in float corner;\nout vec2 vFrom;\nout vec2 vCtrl;\nout vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n",
- GLSL130: "#version 130\n\nstruct Block\n{\n vec4 transform;\n vec2 pathOffset;\n};\n\nuniform Block _16;\n\nin vec2 from;\nin vec2 ctrl;\nin vec2 to;\nin float maxy;\nin float corner;\nout vec2 vFrom;\nout vec2 vCtrl;\nout vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n",
- GLSL150: "#version 150\n\nstruct Block\n{\n vec4 transform;\n vec2 pathOffset;\n};\n\nuniform Block _16;\n\nin vec2 from;\nin vec2 ctrl;\nin vec2 to;\nin float maxy;\nin float corner;\nout vec2 vFrom;\nout vec2 vCtrl;\nout vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n",
- /*
- cbuffer Block : register(b0)
- {
- float4 _16_transform : packoffset(c0);
- float2 _16_pathOffset : packoffset(c1);
- };
-
-
- static float4 gl_Position;
- static float2 from;
- static float2 ctrl;
- static float2 to;
- static float maxy;
- static float corner;
- static float2 vFrom;
- static float2 vCtrl;
- static float2 vTo;
-
- struct SPIRV_Cross_Input
- {
- float corner : POSITION;
- float maxy : NORMAL;
- float2 from : TEXCOORD0;
- float2 ctrl : TEXCOORD1;
- float2 to : TEXCOORD2;
- };
-
- struct SPIRV_Cross_Output
- {
- float2 vFrom : TEXCOORD0;
- float2 vCtrl : TEXCOORD1;
- float2 vTo : TEXCOORD2;
- float4 gl_Position : SV_Position;
- };
-
- void vert_main()
- {
- float2 from_1 = from + _16_pathOffset;
- float2 ctrl_1 = ctrl + _16_pathOffset;
- float2 to_1 = to + _16_pathOffset;
- float maxy_1 = maxy + _16_pathOffset.y;
- float c = corner;
- float2 pos;
- if (c >= 0.375f)
- {
- c -= 0.5f;
- pos.y = maxy_1 + 1.0f;
- }
- else
- {
- pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0f;
- }
- if (c >= 0.125f)
- {
- pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0f;
- }
- else
- {
- pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0f;
- }
- vFrom = from_1 - pos;
- vCtrl = ctrl_1 - pos;
- vTo = to_1 - pos;
- pos = (pos * _16_transform.xy) + _16_transform.zw;
- gl_Position = float4(pos, 1.0f, 1.0f);
- }
-
- SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
- {
- from = stage_input.from;
- ctrl = stage_input.ctrl;
- to = stage_input.to;
- maxy = stage_input.maxy;
- corner = stage_input.corner;
- vert_main();
- SPIRV_Cross_Output stage_output;
- stage_output.gl_Position = gl_Position;
- stage_output.vFrom = vFrom;
- stage_output.vCtrl = vCtrl;
- stage_output.vTo = vTo;
- return stage_output;
- }
-
- */
- HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x99, 0xea, 0x97, 0xb5, 0xa8, 0xd5, 0x84, 0x5e, 0x4b, 0x12, 0x14, 0x56, 0xdd, 0xee, 0xfc, 0x44, 0x1, 0x0, 0x0, 0x0, 0x18, 0x8, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x4c, 0x2, 0x0, 0x0, 0x74, 0x5, 0x0, 0x0, 0xf0, 0x5, 0x0, 0x0, 0xec, 0x6, 0x0, 0x0, 0x90, 0x7, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xc, 0x2, 0x0, 0x0, 0xc, 0x2, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0xd8, 0x1, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x3, 0x0, 0xf, 0xa0, 0x0, 0x0, 0xc0, 0x3e, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0xbf, 0x51, 0x0, 0x0, 0x5, 0x4, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x2, 0x80, 0x2, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x3, 0x80, 0x3, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x4, 0x80, 0x4, 0x0, 0xf, 0x90, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x90, 0x2, 0x0, 0x55, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x4, 0x80, 0x0, 0x0, 0x0, 0x80, 0x3, 0x0, 0x55, 0xa0, 0xd, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x90, 0x3, 0x0, 0x0, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x0, 0x90, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x0, 0x90, 0x3, 0x0, 0xff, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x2, 0x0, 0x3, 0x80, 0x2, 0x0, 0xe4, 0x90, 0x2, 0x0, 0xe4, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x2, 0x0, 0xc, 0x80, 0x3, 0x0, 0x14, 0x90, 0x2, 0x0, 0x14, 0xa0, 0xa, 0x0, 0x0, 0x3, 0x3, 0x0, 0x3, 0x80, 0x2, 0x0, 0xee, 0x80, 0x2, 0x0, 0xe1, 0x80, 0x2, 0x0, 0x0, 0x3, 0x3, 0x0, 0xc, 0x80, 0x4, 0x0, 0x44, 0x90, 0x2, 0x0, 0x44, 0xa0, 0xa, 0x0, 0x0, 0x3, 0x3, 0x0, 0x3, 0x80, 0x3, 0x0, 0xeb, 0x80, 0x3, 0x0, 0xe4, 0x80, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0x80, 0x3, 0x0, 0xe4, 0x80, 0x3, 0x0, 0xaa, 0xa0, 0x12, 0x0, 0x0, 0x4, 0x4, 0x0, 0x6, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x1, 0x0, 0xc8, 0x80, 0xd, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x4, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0xa0, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x2, 0x0, 0xff, 0x80, 0x2, 0x0, 0x0, 0x80, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x3, 0x0, 0xaa, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x3, 0x0, 0x55, 0xa0, 0x12, 0x0, 0x0, 0x4, 0x4, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0xe0, 0x2, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x28, 0x81, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0xe0, 0x3, 0x0, 0xee, 0x80, 0x4, 0x0, 0xe8, 0x81, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x4, 0x0, 0xe8, 0x80, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xc, 0xc0, 0x3, 0x0, 0x55, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x20, 0x3, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0xc8, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x12, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x12, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xc2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x1d, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0xc0, 0x3e, 0x0, 0x0, 0x0, 0x7, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x32, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x14, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6, 0x84, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0xb6, 0xf, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x16, 0x5, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6, 0x14, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x6, 0x84, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0xb6, 0xf, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x9, 0x62, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x6, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa6, 0x8, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1d, 0x0, 0x0, 0x7, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x34, 0x0, 0x0, 0x7, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x7, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x37, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x86, 0x8, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0xe, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x86, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0xa, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x86, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xc2, 0x20, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xf4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x36, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x36, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x0, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x9c, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x89, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x80, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x3, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x71, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab},
- }
-)
diff --git a/vendor/gioui.org/gpu/timer.go b/vendor/gioui.org/gpu/timer.go
index a1da948..c1e3227 100644
--- a/vendor/gioui.org/gpu/timer.go
+++ b/vendor/gioui.org/gpu/timer.go
@@ -5,18 +5,18 @@ package gpu
import (
"time"
- "gioui.org/gpu/backend"
+ "gioui.org/gpu/internal/driver"
)
type timers struct {
- backend backend.Device
+ backend driver.Device
timers []*timer
}
type timer struct {
Elapsed time.Duration
- backend backend.Device
- timer backend.Timer
+ backend driver.Device
+ timer driver.Timer
state timerState
}
@@ -28,7 +28,7 @@ const (
timerWaiting
)
-func newTimers(b backend.Device) *timers {
+func newTimers(b driver.Device) *timers {
return &timers{
backend: b,
}
@@ -83,7 +83,7 @@ func (t *timers) ready() bool {
return t.backend.IsTimeContinuous()
}
-func (t *timers) release() {
+func (t *timers) Release() {
if t == nil {
return
}