diff options
Diffstat (limited to 'vendor/gioui.org/gpu')
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 } |