vendor dependencies, make some changes to how input is done
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
// Copyright 2023 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package graphics
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ManagedBytes is a managed byte slice.
|
||||
// The internal byte alice are managed in a pool.
|
||||
// ManagedBytes is useful when its lifetime is explicit, as the underlying byte slice can be reused for another ManagedBytes later.
|
||||
// This can reduce allocations and GCs.
|
||||
type ManagedBytes struct {
|
||||
bytes []byte
|
||||
pool *bytesPool
|
||||
}
|
||||
|
||||
// Len returns the length of the slice.
|
||||
func (m *ManagedBytes) Len() int {
|
||||
return len(m.bytes)
|
||||
}
|
||||
|
||||
// Read reads the byte slice's content to dst.
|
||||
func (m *ManagedBytes) Read(dst []byte, from, to int) {
|
||||
copy(dst, m.bytes[from:to])
|
||||
}
|
||||
|
||||
// Clone creates a new ManagedBytes with the same content.
|
||||
func (m *ManagedBytes) Clone() *ManagedBytes {
|
||||
return NewManagedBytes(len(m.bytes), func(bs []byte) {
|
||||
copy(bs, m.bytes)
|
||||
})
|
||||
}
|
||||
|
||||
// GetAndRelease returns the raw byte slice and a finalizer.
|
||||
// A finalizer should be called when you can ensure that the slice is no longer used,
|
||||
// e.g. when a graphics command using this slice is sent and executed.
|
||||
//
|
||||
// After GetAndRelease is called, the underlying byte slice is no longer available.
|
||||
func (m *ManagedBytes) GetAndRelease() ([]byte, func()) {
|
||||
bs := m.bytes
|
||||
m.bytes = nil
|
||||
return bs, func() {
|
||||
m.pool.put(bs)
|
||||
runtime.SetFinalizer(m, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Release releases the underlying byte slice.
|
||||
//
|
||||
// After Release is called, the underlying byte slice is no longer available.
|
||||
func (m *ManagedBytes) Release() {
|
||||
m.pool.put(m.bytes)
|
||||
m.bytes = nil
|
||||
runtime.SetFinalizer(m, nil)
|
||||
}
|
||||
|
||||
// NewManagedBytes returns a managed byte slice initialized by the given constructor f.
|
||||
//
|
||||
// The byte slice is not zero-cleared at the constructor.
|
||||
func NewManagedBytes(size int, f func([]byte)) *ManagedBytes {
|
||||
bs := theBytesPool.get(size)
|
||||
f(bs.bytes)
|
||||
return bs
|
||||
}
|
||||
|
||||
type bytesPool struct {
|
||||
pool [][]byte
|
||||
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
var theBytesPool bytesPool
|
||||
|
||||
func (b *bytesPool) get(size int) *ManagedBytes {
|
||||
bs := b.getFromCache(size)
|
||||
if bs == nil {
|
||||
bs = make([]byte, size)
|
||||
}
|
||||
m := &ManagedBytes{
|
||||
bytes: bs,
|
||||
pool: b,
|
||||
}
|
||||
runtime.SetFinalizer(m, func(m *ManagedBytes) {
|
||||
b.put(m.bytes)
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
func (b *bytesPool) getFromCache(size int) []byte {
|
||||
b.m.Lock()
|
||||
defer b.m.Unlock()
|
||||
|
||||
for i, bs := range b.pool {
|
||||
if cap(bs) < size {
|
||||
continue
|
||||
}
|
||||
|
||||
copy(b.pool[i:], b.pool[i+1:])
|
||||
b.pool[len(b.pool)-1] = nil
|
||||
b.pool = b.pool[:len(b.pool)-1]
|
||||
return bs[:size]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bytesPool) put(bs []byte) {
|
||||
if len(bs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
b.m.Lock()
|
||||
defer b.m.Unlock()
|
||||
|
||||
b.pool = append(b.pool, bs)
|
||||
|
||||
// GC the pool. The size limitation is arbitrary.
|
||||
for len(b.pool) >= 32 || b.totalSize() >= 1024*1024*1024 {
|
||||
b.pool = b.pool[1:]
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bytesPool) totalSize() int {
|
||||
var s int
|
||||
for _, bs := range b.pool {
|
||||
s += len(bs)
|
||||
}
|
||||
return s
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright 2014 Hajime Hoshi
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package graphics
|
||||
|
||||
// InternalImageSize returns a nearest appropriate size as an internal image.
|
||||
func InternalImageSize(x int) int {
|
||||
// minInternalImageSize is the minimum size of internal images (texture/framebuffer).
|
||||
//
|
||||
// For example, the image size less than 15 is not supported on some iOS devices.
|
||||
// See also: https://stackoverflow.com/questions/15935651
|
||||
const minInternalImageSize = 16
|
||||
|
||||
if x <= 0 {
|
||||
panic("graphics: x must be positive")
|
||||
}
|
||||
if x < minInternalImageSize {
|
||||
return minInternalImageSize
|
||||
}
|
||||
r := 1
|
||||
for r < x {
|
||||
r <<= 1
|
||||
}
|
||||
return r
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
// Copyright 2022 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package graphics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shader"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
)
|
||||
|
||||
func shaderSuffix(unit shaderir.Unit) (string, error) {
|
||||
shaderSuffix := fmt.Sprintf(`
|
||||
var __imageDstTextureSize vec2
|
||||
|
||||
// imageDstTextureSize returns the destination image's texture size in pixels.
|
||||
//
|
||||
// Deprecated: as of v2.6. Use the pixel-unit mode.
|
||||
func imageDstTextureSize() vec2 {
|
||||
return __imageDstTextureSize
|
||||
}
|
||||
|
||||
var __imageSrcTextureSizes [%[1]d]vec2
|
||||
|
||||
// imageSrcTextureSize returns the 0th source image's texture size in pixels.
|
||||
// As an image is a part of internal texture, the texture is usually bigger than the image.
|
||||
// The texture's size is useful when you want to calculate pixels from texels in the texel mode.
|
||||
//
|
||||
// Deprecated: as of v2.6. Use the pixel-unit mode.
|
||||
func imageSrcTextureSize() vec2 {
|
||||
return __imageSrcTextureSizes[0]
|
||||
}
|
||||
|
||||
// The unit is the destination texture's pixel or texel.
|
||||
var __imageDstRegionOrigin vec2
|
||||
|
||||
// The unit is the source texture's pixel or texel.
|
||||
var __imageDstRegionSize vec2
|
||||
|
||||
// imageDstRegionOnTexture returns the destination image's region (the origin and the size) on its texture.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
//
|
||||
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
|
||||
//
|
||||
// Deprecated: as of v2.6. Use imageDstOrigin or imageDstSize.
|
||||
func imageDstRegionOnTexture() (vec2, vec2) {
|
||||
return __imageDstRegionOrigin, __imageDstRegionSize
|
||||
}
|
||||
|
||||
// imageDstOrigin returns the destination image's origin on its texture.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
//
|
||||
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
|
||||
func imageDstOrigin() vec2 {
|
||||
return __imageDstRegionOrigin
|
||||
}
|
||||
|
||||
// imageDstSize returns the destination image's size.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
func imageDstSize() vec2 {
|
||||
return __imageDstRegionSize
|
||||
}
|
||||
|
||||
// The unit is the source texture's pixel or texel.
|
||||
var __imageSrcRegionOrigins [%[1]d]vec2
|
||||
|
||||
// The unit is the source texture's pixel or texel.
|
||||
var __imageSrcRegionSizes [%[1]d]vec2
|
||||
|
||||
// imageSrcRegionOnTexture returns the 0th source image's region (the origin and the size) on its texture.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
//
|
||||
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
|
||||
//
|
||||
// Deprecated: as of v2.6. Use imageSrc0Origin or imageSrc0Size instead.
|
||||
func imageSrcRegionOnTexture() (vec2, vec2) {
|
||||
return __imageSrcRegionOrigins[0], __imageSrcRegionSizes[0]
|
||||
}
|
||||
`, ShaderImageCount)
|
||||
|
||||
for i := 0; i < ShaderImageCount; i++ {
|
||||
shaderSuffix += fmt.Sprintf(`
|
||||
// imageSrc%[1]dOrigin returns the source image's region origin on its texture.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
//
|
||||
// As an image is a part of internal texture, the image can be located at an arbitrary position on the texture.
|
||||
func imageSrc%[1]dOrigin() vec2 {
|
||||
return __imageSrcRegionOrigins[%[1]d]
|
||||
}
|
||||
|
||||
// imageSrc%[1]dSize returns the source image's size.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
func imageSrc%[1]dSize() vec2 {
|
||||
return __imageSrcRegionSizes[%[1]d]
|
||||
}
|
||||
`, i)
|
||||
|
||||
pos := "pos"
|
||||
if i >= 1 {
|
||||
// Convert the position in texture0's positions to the target texture positions.
|
||||
switch unit {
|
||||
case shaderir.Pixels:
|
||||
pos = fmt.Sprintf("pos - __imageSrcRegionOrigins[0] + __imageSrcRegionOrigins[%d]", i)
|
||||
case shaderir.Texels:
|
||||
pos = fmt.Sprintf("((pos - __imageSrcRegionOrigins[0]) * __imageSrcTextureSizes[0]) / __imageSrcTextureSizes[%[1]d] + __imageSrcRegionOrigins[%[1]d]", i)
|
||||
default:
|
||||
return "", fmt.Errorf("graphics: unexpected unit: %d", unit)
|
||||
}
|
||||
}
|
||||
// __t%d is a special variable for a texture variable.
|
||||
shaderSuffix += fmt.Sprintf(`
|
||||
func imageSrc%[1]dUnsafeAt(pos vec2) vec4 {
|
||||
// pos is the position in positions of the source texture (= 0th image's texture).
|
||||
return __texelAt(__t%[1]d, %[2]s)
|
||||
}
|
||||
`, i, pos)
|
||||
switch unit {
|
||||
case shaderir.Pixels:
|
||||
shaderSuffix += fmt.Sprintf(`
|
||||
func imageSrc%[1]dAt(pos vec2) vec4 {
|
||||
// pos is the position of the source texture (= 0th image's texture).
|
||||
// If pos is in the region, the result is (1, 1). Otherwise, either element is 0.
|
||||
in := step(__imageSrcRegionOrigins[0], pos) - step(__imageSrcRegionOrigins[0] + __imageSrcRegionSizes[%[1]d], pos)
|
||||
return __texelAt(__t%[1]d, %[2]s) * in.x * in.y
|
||||
}
|
||||
`, i, pos)
|
||||
case shaderir.Texels:
|
||||
shaderSuffix += fmt.Sprintf(`
|
||||
func imageSrc%[1]dAt(pos vec2) vec4 {
|
||||
// pos is the position of the source texture (= 0th image's texture).
|
||||
// If pos is in the region, the result is (1, 1). Otherwise, either element is 0.
|
||||
// With the texel mode, all the source region sizes are the same (#1870).
|
||||
// As pos is in texels of the 0th texture, always use the 0th image region size.
|
||||
in := step(__imageSrcRegionOrigins[0], pos) - step(__imageSrcRegionOrigins[0] + __imageSrcRegionSizes[0], pos)
|
||||
return __texelAt(__t%[1]d, %[2]s) * in.x * in.y
|
||||
}
|
||||
`, i, pos)
|
||||
}
|
||||
}
|
||||
|
||||
shaderSuffix += `
|
||||
var __projectionMatrix mat4
|
||||
|
||||
func __vertex(dstPos vec2, srcPos vec2, color vec4) (vec4, vec2, vec4) {
|
||||
return __projectionMatrix * vec4(dstPos, 0, 1), srcPos, color
|
||||
}
|
||||
`
|
||||
return shaderSuffix, nil
|
||||
}
|
||||
|
||||
func CompileShader(src []byte) (*shaderir.Program, error) {
|
||||
unit, err := shader.ParseCompilerDirectives(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
suffix, err := shaderSuffix(unit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write(src)
|
||||
buf.WriteString(suffix)
|
||||
|
||||
const (
|
||||
vert = "__vertex"
|
||||
frag = "Fragment"
|
||||
)
|
||||
ir, err := shader.Compile(buf.Bytes(), vert, frag, ShaderImageCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ir.VertexFunc.Block == nil {
|
||||
return nil, fmt.Errorf("graphics: vertex shader entry point '%s' is missing", vert)
|
||||
}
|
||||
if ir.FragmentFunc.Block == nil {
|
||||
return nil, fmt.Errorf("graphics: fragment shader entry point '%s' is missing", frag)
|
||||
}
|
||||
|
||||
return ir, nil
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// Copyright 2019 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package graphics
|
||||
|
||||
const (
|
||||
ShaderImageCount = 4
|
||||
|
||||
// PreservedUniformVariablesCount represents the number of preserved uniform variables.
|
||||
// Any shaders in Ebitengine must have these uniform variables.
|
||||
PreservedUniformVariablesCount = 1 + // the destination texture size
|
||||
1 + // the source texture sizes array
|
||||
1 + // the destination image region origin
|
||||
1 + // the destination image region size
|
||||
1 + // the source image region origins
|
||||
1 + // the source image region sizes array
|
||||
1 // the projection matrix
|
||||
|
||||
ProjectionMatrixUniformVariableIndex = 6
|
||||
|
||||
PreservedUniformUint32Count = 2 + // the destination texture size
|
||||
2*ShaderImageCount + // the source texture sizes array
|
||||
2 + // the destination image region origin
|
||||
2 + // the destination image region size
|
||||
2*ShaderImageCount + // the source image region origins array
|
||||
2*ShaderImageCount + // the source image region sizes array
|
||||
16 // the projection matrix
|
||||
)
|
||||
|
||||
const (
|
||||
VertexFloatCount = 8
|
||||
)
|
||||
|
||||
var (
|
||||
quadIndices = []uint32{0, 1, 2, 1, 2, 3}
|
||||
)
|
||||
|
||||
func QuadIndices() []uint32 {
|
||||
return quadIndices
|
||||
}
|
||||
|
||||
// QuadVertices sets a float32 slice for a quadrangle.
|
||||
// QuadVertices sets a slice that never overlaps with other slices returned this function,
|
||||
// and users can do optimization based on this fact.
|
||||
func QuadVertices(dst []float32, sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) {
|
||||
x := sx1 - sx0
|
||||
y := sy1 - sy0
|
||||
ax, by, cx, dy := a*x, b*y, c*x, d*y
|
||||
u0, v0, u1, v1 := sx0, sy0, sx1, sy1
|
||||
|
||||
// This function is very performance-sensitive and implement in a very dumb way.
|
||||
dst = dst[:4*VertexFloatCount]
|
||||
|
||||
dst[0] = adjustDestinationPixel(tx)
|
||||
dst[1] = adjustDestinationPixel(ty)
|
||||
dst[2] = u0
|
||||
dst[3] = v0
|
||||
dst[4] = cr
|
||||
dst[5] = cg
|
||||
dst[6] = cb
|
||||
dst[7] = ca
|
||||
|
||||
dst[8] = adjustDestinationPixel(ax + tx)
|
||||
dst[9] = adjustDestinationPixel(cx + ty)
|
||||
dst[10] = u1
|
||||
dst[11] = v0
|
||||
dst[12] = cr
|
||||
dst[13] = cg
|
||||
dst[14] = cb
|
||||
dst[15] = ca
|
||||
|
||||
dst[16] = adjustDestinationPixel(by + tx)
|
||||
dst[17] = adjustDestinationPixel(dy + ty)
|
||||
dst[18] = u0
|
||||
dst[19] = v1
|
||||
dst[20] = cr
|
||||
dst[21] = cg
|
||||
dst[22] = cb
|
||||
dst[23] = ca
|
||||
|
||||
dst[24] = adjustDestinationPixel(ax + by + tx)
|
||||
dst[25] = adjustDestinationPixel(cx + dy + ty)
|
||||
dst[26] = u1
|
||||
dst[27] = v1
|
||||
dst[28] = cr
|
||||
dst[29] = cg
|
||||
dst[30] = cb
|
||||
dst[31] = ca
|
||||
}
|
||||
|
||||
func adjustDestinationPixel(x float32) float32 {
|
||||
// Avoid the center of the pixel, which is problematic (#929, #1171).
|
||||
// Instead, align the vertices with about 1/3 pixels.
|
||||
//
|
||||
// The intention here is roughly this code:
|
||||
//
|
||||
// float32(math.Floor((float64(x)+1.0/6.0)*3) / 3)
|
||||
//
|
||||
// The actual implementation is more optimized than the above implementation.
|
||||
ix := float32(int(x))
|
||||
if x < 0 && x != ix {
|
||||
ix -= 1
|
||||
}
|
||||
frac := x - ix
|
||||
switch {
|
||||
case frac < 3.0/16.0:
|
||||
return ix
|
||||
case frac < 8.0/16.0:
|
||||
return ix + 5.0/16.0
|
||||
case frac < 13.0/16.0:
|
||||
return ix + 11.0/16.0
|
||||
default:
|
||||
return ix + 16.0/16.0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user