chore(v2): vendor dependencies for offline/China builds
go mod vendor pins onnxruntime_go v1.12.1, Gio and the rest into v2/vendor so go run/build work without hitting proxy.golang.org (blocked/slow in China). Verified: CGO_ENABLED=1 go build -mod=vendor ./internal/spike and GOOS=windows go build -mod=vendor ./internal/ui both pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+40
@@ -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
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gpu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gioui.org/internal/f32"
|
||||
)
|
||||
|
||||
type textureCacheKey struct {
|
||||
filter byte
|
||||
handle any
|
||||
}
|
||||
|
||||
type textureCache struct {
|
||||
res map[textureCacheKey]resourceCacheValue
|
||||
}
|
||||
|
||||
type resourceCacheValue struct {
|
||||
used bool
|
||||
resource resource
|
||||
}
|
||||
|
||||
// opCache is like a resourceCache but using concrete types and a
|
||||
// freelist instead of two maps to avoid runtime.mapaccess2 calls
|
||||
// since benchmarking showed them as a bottleneck.
|
||||
type opCache struct {
|
||||
// store the index + 1 in cache this key is stored in
|
||||
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
|
||||
|
||||
bounds f32.Rectangle
|
||||
// the fields below are handled by opCache
|
||||
key opKey
|
||||
keep bool
|
||||
}
|
||||
|
||||
func newTextureCache() *textureCache {
|
||||
return &textureCache{
|
||||
res: make(map[textureCacheKey]resourceCacheValue),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *textureCache) get(key textureCacheKey) (resource, bool) {
|
||||
v, exists := r.res[key]
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
if !v.used {
|
||||
v.used = true
|
||||
r.res[key] = v
|
||||
}
|
||||
return v.resource, exists
|
||||
}
|
||||
|
||||
func (r *textureCache) put(key textureCacheKey, val resource) {
|
||||
v, exists := r.res[key]
|
||||
if exists && v.used {
|
||||
panic(fmt.Errorf("key exists, %v", key))
|
||||
}
|
||||
v.used = true
|
||||
v.resource = val
|
||||
r.res[key] = v
|
||||
}
|
||||
|
||||
func (r *textureCache) frame() {
|
||||
for k, v := range r.res {
|
||||
if v.used {
|
||||
v.used = false
|
||||
r.res[k] = v
|
||||
} else {
|
||||
delete(r.res, k)
|
||||
v.resource.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *textureCache) release() {
|
||||
for _, v := range r.res {
|
||||
v.resource.release()
|
||||
}
|
||||
r.res = nil
|
||||
}
|
||||
|
||||
func newOpCache() *opCache {
|
||||
return &opCache{
|
||||
index: make(map[opKey]int),
|
||||
freelist: make([]int, 0),
|
||||
cache: make([]opCacheValue, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *opCache) get(key opKey) (o opCacheValue, exist bool) {
|
||||
v := r.index[key]
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
r.cache[v-1].keep = true
|
||||
return r.cache[v-1], true
|
||||
}
|
||||
|
||||
func (r *opCache) put(key opKey, val opCacheValue) {
|
||||
v := r.index[key]
|
||||
val.keep = true
|
||||
val.key = key
|
||||
if v == 0 {
|
||||
// not in cache
|
||||
i := len(r.cache)
|
||||
if len(r.freelist) > 0 {
|
||||
i = r.freelist[len(r.freelist)-1]
|
||||
r.freelist = r.freelist[:len(r.freelist)-1]
|
||||
r.cache[i] = val
|
||||
} else {
|
||||
r.cache = append(r.cache, val)
|
||||
}
|
||||
r.index[key] = i + 1
|
||||
} else {
|
||||
r.cache[v-1] = val
|
||||
}
|
||||
}
|
||||
|
||||
func (r *opCache) frame() {
|
||||
r.freelist = r.freelist[:0]
|
||||
for i, v := range r.cache {
|
||||
r.cache[i].keep = false
|
||||
if v.keep {
|
||||
continue
|
||||
}
|
||||
if v.data.data != nil {
|
||||
v.data.release()
|
||||
r.cache[i].data.data = nil
|
||||
}
|
||||
delete(r.index, v.key)
|
||||
r.freelist = append(r.freelist, i)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *opCache) release() {
|
||||
for i := range r.cache {
|
||||
r.cache[i].keep = false
|
||||
}
|
||||
r.frame()
|
||||
r.index = nil
|
||||
r.freelist = nil
|
||||
r.cache = nil
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package gpu
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
|
||||
"gioui.org/internal/f32"
|
||||
"gioui.org/internal/stroke"
|
||||
)
|
||||
|
||||
type quadSplitter struct {
|
||||
bounds f32.Rectangle
|
||||
contour uint32
|
||||
d *drawOps
|
||||
|
||||
// scratch space used by calls to stroke.SplitCubic
|
||||
scratch []stroke.QuadSegment
|
||||
}
|
||||
|
||||
func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) {
|
||||
// inlined code:
|
||||
// encodeVertex(data, meta, 1, -1, from, ctrl, to)
|
||||
// encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to)
|
||||
// encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to)
|
||||
// encodeVertex(data[vertStride*3:], meta, -1, 1, from, ctrl, to)
|
||||
// this code needs to stay in sync with `vertex.encode`.
|
||||
|
||||
bo := binary.LittleEndian
|
||||
data = data[:vertStride*4]
|
||||
|
||||
// encode the main template
|
||||
bo.PutUint32(data[4:8], meta)
|
||||
bo.PutUint32(data[8:12], math.Float32bits(from.X))
|
||||
bo.PutUint32(data[12:16], math.Float32bits(from.Y))
|
||||
bo.PutUint32(data[16:20], math.Float32bits(ctrl.X))
|
||||
bo.PutUint32(data[20:24], math.Float32bits(ctrl.Y))
|
||||
bo.PutUint32(data[24:28], math.Float32bits(to.X))
|
||||
bo.PutUint32(data[28:32], math.Float32bits(to.Y))
|
||||
|
||||
copy(data[vertStride*1:vertStride*2], data[vertStride*0:vertStride*1])
|
||||
copy(data[vertStride*2:vertStride*3], data[vertStride*0:vertStride*1])
|
||||
copy(data[vertStride*3:vertStride*4], data[vertStride*0:vertStride*1])
|
||||
|
||||
bo.PutUint32(data[vertStride*0:vertStride*0+4], math.Float32bits(nwCorner))
|
||||
bo.PutUint32(data[vertStride*1:vertStride*1+4], math.Float32bits(neCorner))
|
||||
bo.PutUint32(data[vertStride*2:vertStride*2+4], math.Float32bits(swCorner))
|
||||
bo.PutUint32(data[vertStride*3:vertStride*3+4], math.Float32bits(seCorner))
|
||||
}
|
||||
|
||||
const (
|
||||
nwCorner = 1*0.5 + 0*0.25
|
||||
neCorner = 1*0.5 + 1*0.25
|
||||
swCorner = 0*0.5 + 0*0.25
|
||||
seCorner = 0*0.5 + 1*0.25
|
||||
)
|
||||
|
||||
func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) {
|
||||
var corner float32
|
||||
if cornerx == 1 {
|
||||
corner += .5
|
||||
}
|
||||
if cornery == 1 {
|
||||
corner += .25
|
||||
}
|
||||
v := vertex{
|
||||
Corner: corner,
|
||||
FromX: from.X,
|
||||
FromY: from.Y,
|
||||
CtrlX: ctrl.X,
|
||||
CtrlY: ctrl.Y,
|
||||
ToX: to.X,
|
||||
ToY: to.Y,
|
||||
}
|
||||
v.encode(data, meta)
|
||||
}
|
||||
|
||||
func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) {
|
||||
data := qs.d.writeVertCache(vertStride * 4)
|
||||
encodeQuadTo(data, qs.contour, from, ctrl, to)
|
||||
}
|
||||
|
||||
func (qs *quadSplitter) splitAndEncode(quad stroke.QuadSegment) {
|
||||
cbnd := f32.Rectangle{
|
||||
Min: quad.From,
|
||||
Max: quad.To,
|
||||
}.Canon()
|
||||
from, ctrl, to := quad.From, quad.Ctrl, quad.To
|
||||
|
||||
// If the curve contain areas where a vertical line
|
||||
// intersects it twice, split the curve in two x monotone
|
||||
// lower and upper curves. The stencil fragment program
|
||||
// expects only one intersection per curve.
|
||||
|
||||
// Find the t where the derivative in x is 0.
|
||||
v0 := ctrl.Sub(from)
|
||||
v1 := to.Sub(ctrl)
|
||||
d := v0.X - v1.X
|
||||
// t = v0 / d. Split if t is in ]0;1[.
|
||||
if v0.X > 0 && d > v0.X || v0.X < 0 && d < v0.X {
|
||||
t := v0.X / d
|
||||
ctrl0 := from.Mul(1 - t).Add(ctrl.Mul(t))
|
||||
ctrl1 := ctrl.Mul(1 - t).Add(to.Mul(t))
|
||||
mid := ctrl0.Mul(1 - t).Add(ctrl1.Mul(t))
|
||||
qs.encodeQuadTo(from, ctrl0, mid)
|
||||
qs.encodeQuadTo(mid, ctrl1, to)
|
||||
if mid.X > cbnd.Max.X {
|
||||
cbnd.Max.X = mid.X
|
||||
}
|
||||
if mid.X < cbnd.Min.X {
|
||||
cbnd.Min.X = mid.X
|
||||
}
|
||||
} else {
|
||||
qs.encodeQuadTo(from, ctrl, to)
|
||||
}
|
||||
// Find the y extremum, if any.
|
||||
d = v0.Y - v1.Y
|
||||
if v0.Y > 0 && d > v0.Y || v0.Y < 0 && d < v0.Y {
|
||||
t := v0.Y / d
|
||||
y := (1-t)*(1-t)*from.Y + 2*(1-t)*t*ctrl.Y + t*t*to.Y
|
||||
if y > cbnd.Max.Y {
|
||||
cbnd.Max.Y = y
|
||||
}
|
||||
if y < cbnd.Min.Y {
|
||||
cbnd.Min.Y = y
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+1603
File diff suppressed because it is too large
Load Diff
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// This file exists so this package builds on non-Windows platforms.
|
||||
|
||||
package d3d11
|
||||
+871
@@ -0,0 +1,871 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package d3d11
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"math"
|
||||
"math/bits"
|
||||
"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
|
||||
mipmap bool
|
||||
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)
|
||||
}
|
||||
bindFlags := convBufferBinding(bindings)
|
||||
miscFlags := uint32(0)
|
||||
mipmap := minFilter == driver.FilterLinearMipmapLinear
|
||||
nmipmaps := 1
|
||||
if mipmap {
|
||||
// Flags required by ID3D11DeviceContext::GenerateMips.
|
||||
bindFlags |= d3d11.BIND_SHADER_RESOURCE | d3d11.BIND_RENDER_TARGET
|
||||
miscFlags |= d3d11.RESOURCE_MISC_GENERATE_MIPS
|
||||
dim := max(height, width)
|
||||
log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1
|
||||
nmipmaps = log2 + 1
|
||||
}
|
||||
tex, err := b.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
|
||||
Width: uint32(width),
|
||||
Height: uint32(height),
|
||||
MipLevels: uint32(nmipmaps),
|
||||
ArraySize: 1,
|
||||
Format: d3dfmt,
|
||||
SampleDesc: d3d11.DXGI_SAMPLE_DESC{
|
||||
Count: 1,
|
||||
Quality: 0,
|
||||
},
|
||||
BindFlags: bindFlags,
|
||||
MiscFlags: miscFlags,
|
||||
})
|
||||
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
|
||||
case minFilter == driver.FilterLinearMipmapLinear && magFilter == driver.FilterLinear:
|
||||
filter = d3d11.FILTER_MIN_MAG_MIP_LINEAR
|
||||
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, mipmap: mipmap}, 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)
|
||||
if t.mipmap {
|
||||
t.backend.ctx.GenerateMips(t.resView)
|
||||
}
|
||||
}
|
||||
|
||||
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 := range h {
|
||||
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 {
|
||||
return unsafe.Slice((*byte)(unsafe.Pointer(ptr)), cap)
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// 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
|
||||
// Fence is a VkFence that is set when all commands to Framebuffer has completed.
|
||||
Fence 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() {}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
// 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
|
||||
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
|
||||
FilterLinearMipmapLinear
|
||||
)
|
||||
|
||||
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 := range height / 2 {
|
||||
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)
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// This file exists so this package builds on non-Darwin platforms.
|
||||
|
||||
package metal
|
||||
+1159
File diff suppressed because it is too large
Load Diff
+1374
File diff suppressed because it is too large
Load Diff
+176
@@ -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);
|
||||
}
|
||||
`
|
||||
)
|
||||
+1163
File diff suppressed because it is too large
Load Diff
+5
@@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package vulkan
|
||||
|
||||
// Empty file to avoid the build error for platforms without Vulkan support.
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gpu
|
||||
|
||||
import (
|
||||
"image"
|
||||
)
|
||||
|
||||
// packer packs a set of many smaller rectangles into
|
||||
// much fewer larger atlases.
|
||||
type packer struct {
|
||||
maxDims image.Point
|
||||
spaces []image.Rectangle
|
||||
|
||||
sizes []image.Point
|
||||
pos image.Point
|
||||
}
|
||||
|
||||
type placement struct {
|
||||
Idx int
|
||||
Pos image.Point
|
||||
}
|
||||
|
||||
// add adds the given rectangle to the atlases and
|
||||
// return the allocated position.
|
||||
func (p *packer) add(s image.Point) (placement, bool) {
|
||||
if place, ok := p.tryAdd(s); ok {
|
||||
return place, true
|
||||
}
|
||||
p.newPage()
|
||||
return p.tryAdd(s)
|
||||
}
|
||||
|
||||
func (p *packer) clear() {
|
||||
p.sizes = p.sizes[:0]
|
||||
p.spaces = p.spaces[:0]
|
||||
}
|
||||
|
||||
func (p *packer) newPage() {
|
||||
p.pos = image.Point{}
|
||||
p.sizes = append(p.sizes, image.Point{})
|
||||
p.spaces = p.spaces[:0]
|
||||
p.spaces = append(p.spaces, image.Rectangle{
|
||||
Max: image.Point{X: 1e6, Y: 1e6},
|
||||
})
|
||||
}
|
||||
|
||||
func (p *packer) tryAdd(s image.Point) (placement, bool) {
|
||||
if len(p.spaces) == 0 || len(p.sizes) == 0 {
|
||||
return placement{}, false
|
||||
}
|
||||
|
||||
var (
|
||||
bestIdx *image.Rectangle
|
||||
bestSize = p.maxDims
|
||||
lastSize = p.sizes[len(p.sizes)-1]
|
||||
)
|
||||
// Go backwards to prioritize smaller spaces.
|
||||
for i := range p.spaces {
|
||||
space := &p.spaces[i]
|
||||
rightSpace := space.Dx() - s.X
|
||||
bottomSpace := space.Dy() - s.Y
|
||||
if rightSpace < 0 || bottomSpace < 0 {
|
||||
continue
|
||||
}
|
||||
size := lastSize
|
||||
if x := space.Min.X + s.X; x > size.X {
|
||||
if x > p.maxDims.X {
|
||||
continue
|
||||
}
|
||||
size.X = x
|
||||
}
|
||||
if y := space.Min.Y + s.Y; y > size.Y {
|
||||
if y > p.maxDims.Y {
|
||||
continue
|
||||
}
|
||||
size.Y = y
|
||||
}
|
||||
if size.X*size.Y < bestSize.X*bestSize.Y {
|
||||
bestIdx = space
|
||||
bestSize = size
|
||||
}
|
||||
}
|
||||
if bestIdx == nil {
|
||||
return placement{}, false
|
||||
}
|
||||
// Remove space.
|
||||
bestSpace := *bestIdx
|
||||
*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},
|
||||
})
|
||||
}
|
||||
idx := len(p.sizes) - 1
|
||||
p.sizes[idx] = bestSize
|
||||
return placement{Idx: idx, Pos: pos}, true
|
||||
}
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gpu
|
||||
|
||||
// GPU accelerated path drawing using the algorithms from
|
||||
// Pathfinder (https://github.com/servo/pathfinder).
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"image"
|
||||
"math"
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/gpu/internal/driver"
|
||||
"gioui.org/internal/byteslice"
|
||||
"gioui.org/internal/f32"
|
||||
"gioui.org/internal/f32color"
|
||||
"gioui.org/shader"
|
||||
"gioui.org/shader/gio"
|
||||
)
|
||||
|
||||
type pather struct {
|
||||
ctx driver.Device
|
||||
|
||||
viewport image.Point
|
||||
|
||||
stenciler *stenciler
|
||||
coverer *coverer
|
||||
}
|
||||
|
||||
type coverer struct {
|
||||
ctx driver.Device
|
||||
pipelines [2][3]*pipeline
|
||||
texUniforms *coverTexUniforms
|
||||
colUniforms *coverColUniforms
|
||||
linearGradientUniforms *coverLinearGradientUniforms
|
||||
}
|
||||
|
||||
type coverTexUniforms struct {
|
||||
coverUniforms
|
||||
_ [12]byte // Padding to multiple of 16.
|
||||
}
|
||||
|
||||
type coverColUniforms struct {
|
||||
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 {
|
||||
transform [4]float32
|
||||
uvCoverTransform [4]float32
|
||||
uvTransformR1 [4]float32
|
||||
uvTransformR2 [4]float32
|
||||
fbo float32
|
||||
}
|
||||
|
||||
type stenciler struct {
|
||||
ctx driver.Device
|
||||
pipeline struct {
|
||||
pipeline *pipeline
|
||||
uniforms *stencilUniforms
|
||||
}
|
||||
ipipeline struct {
|
||||
pipeline *pipeline
|
||||
uniforms *intersectUniforms
|
||||
}
|
||||
fbos fboSet
|
||||
intersections fboSet
|
||||
indexBuf driver.Buffer
|
||||
}
|
||||
|
||||
type stencilUniforms struct {
|
||||
transform [4]float32
|
||||
pathOffset [2]float32
|
||||
_ [8]byte // Padding to multiple of 16.
|
||||
}
|
||||
|
||||
type intersectUniforms struct {
|
||||
vert struct {
|
||||
uvTransform [4]float32
|
||||
subUVTransform [4]float32
|
||||
}
|
||||
}
|
||||
|
||||
type fboSet struct {
|
||||
fbos []FBO
|
||||
}
|
||||
|
||||
type FBO struct {
|
||||
size image.Point
|
||||
tex driver.Texture
|
||||
}
|
||||
|
||||
type pathData struct {
|
||||
ncurves int
|
||||
data driver.Buffer
|
||||
}
|
||||
|
||||
// vertex data suitable for passing to vertex programs.
|
||||
type vertex struct {
|
||||
// Corner encodes the corner: +0.5 for south, +.25 for east.
|
||||
Corner float32
|
||||
MaxY float32
|
||||
FromX, FromY float32
|
||||
CtrlX, CtrlY float32
|
||||
ToX, ToY float32
|
||||
}
|
||||
|
||||
// encode needs to stay in-sync with the code in clip.go encodeQuadTo.
|
||||
func (v vertex) encode(d []byte, maxy uint32) {
|
||||
d = d[0:32]
|
||||
bo := binary.LittleEndian
|
||||
bo.PutUint32(d[0:4], math.Float32bits(v.Corner))
|
||||
bo.PutUint32(d[4:8], maxy)
|
||||
bo.PutUint32(d[8:12], math.Float32bits(v.FromX))
|
||||
bo.PutUint32(d[12:16], math.Float32bits(v.FromY))
|
||||
bo.PutUint32(d[16:20], math.Float32bits(v.CtrlX))
|
||||
bo.PutUint32(d[20:24], math.Float32bits(v.CtrlY))
|
||||
bo.PutUint32(d[24:28], math.Float32bits(v.ToX))
|
||||
bo.PutUint32(d[28:32], math.Float32bits(v.ToY))
|
||||
}
|
||||
|
||||
const (
|
||||
// Number of path quads per draw batch.
|
||||
pathBatchSize = 10000
|
||||
// Size of a vertex as sent to gpu
|
||||
vertStride = 8 * 4
|
||||
)
|
||||
|
||||
func newPather(ctx driver.Device) *pather {
|
||||
return &pather{
|
||||
ctx: ctx,
|
||||
stenciler: newStenciler(ctx),
|
||||
coverer: newCoverer(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func newCoverer(ctx driver.Device) *coverer {
|
||||
c := &coverer{
|
||||
ctx: ctx,
|
||||
}
|
||||
c.colUniforms = new(coverColUniforms)
|
||||
c.texUniforms = new(coverTexUniforms)
|
||||
c.linearGradientUniforms = new(coverLinearGradientUniforms)
|
||||
pipelines, err := createColorPrograms(ctx, gio.Shader_cover_vert, gio.Shader_cover_frag,
|
||||
[3]any{c.colUniforms, c.linearGradientUniforms, c.texUniforms},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.pipelines = pipelines
|
||||
return c
|
||||
}
|
||||
|
||||
func newStenciler(ctx driver.Device) *stenciler {
|
||||
// Allocate a suitably large index buffer for drawing paths.
|
||||
indices := make([]uint16, pathBatchSize*6)
|
||||
for i := range pathBatchSize {
|
||||
i := uint16(i)
|
||||
indices[i*6+0] = i*4 + 0
|
||||
indices[i*6+1] = i*4 + 1
|
||||
indices[i*6+2] = i*4 + 2
|
||||
indices[i*6+3] = i*4 + 2
|
||||
indices[i*6+4] = i*4 + 1
|
||||
indices[i*6+5] = i*4 + 3
|
||||
}
|
||||
indexBuf, err := ctx.NewImmutableBuffer(driver.BufferBindingIndices, byteslice.Slice(indices))
|
||||
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 := 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,
|
||||
}
|
||||
vsh, fsh, err := newShaders(ctx, gio.Shader_stencil_vert, gio.Shader_stencil_frag)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
func (s *fboSet) resize(ctx driver.Device, format driver.TextureFormat, sizes []image.Point) {
|
||||
// Add fbos.
|
||||
for i := len(s.fbos); i < len(sizes); i++ {
|
||||
s.fbos = append(s.fbos, FBO{})
|
||||
}
|
||||
// Resize fbos.
|
||||
for i, sz := range sizes {
|
||||
f := &s.fbos[i]
|
||||
// Resizing or recreating FBOs can introduce rendering stalls.
|
||||
// Avoid if the space waste is not too high.
|
||||
resize := sz.X > f.size.X || sz.Y > f.size.Y
|
||||
waste := float32(sz.X*sz.Y) / float32(f.size.X*f.size.Y)
|
||||
resize = resize || waste > 1.2
|
||||
if resize {
|
||||
if f.tex != nil {
|
||||
f.tex.Release()
|
||||
}
|
||||
// Add 5% extra space in each dimension to minimize resizing.
|
||||
sz = sz.Mul(105).Div(100)
|
||||
max := ctx.Caps().MaxTextureSize
|
||||
if sz.Y > max {
|
||||
sz.Y = max
|
||||
}
|
||||
if sz.X > max {
|
||||
sz.X = max
|
||||
}
|
||||
tex, err := ctx.NewTexture(format, sz.X, sz.Y, driver.FilterNearest, driver.FilterNearest,
|
||||
driver.BufferBindingTexture|driver.BufferBindingFramebuffer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
f.size = sz
|
||||
f.tex = tex
|
||||
}
|
||||
}
|
||||
// Delete extra fbos.
|
||||
s.delete(ctx, len(sizes))
|
||||
}
|
||||
|
||||
func (s *fboSet) delete(ctx driver.Device, idx int) {
|
||||
for i := idx; i < len(s.fbos); i++ {
|
||||
f := s.fbos[i]
|
||||
f.tex.Release()
|
||||
}
|
||||
s.fbos = s.fbos[:idx]
|
||||
}
|
||||
|
||||
func (s *stenciler) release() {
|
||||
s.fbos.delete(s.ctx, 0)
|
||||
s.intersections.delete(s.ctx, 0)
|
||||
s.pipeline.pipeline.Release()
|
||||
s.ipipeline.pipeline.Release()
|
||||
s.indexBuf.Release()
|
||||
}
|
||||
|
||||
func (p *pather) release() {
|
||||
p.stenciler.release()
|
||||
p.coverer.release()
|
||||
}
|
||||
|
||||
func (c *coverer) release() {
|
||||
for _, p := range c.pipelines {
|
||||
for _, p := range p {
|
||||
p.Release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildPath(ctx driver.Device, p []byte) pathData {
|
||||
buf, err := ctx.NewImmutableBuffer(driver.BufferBindingVertices, p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return pathData{
|
||||
ncurves: len(p) / vertStride,
|
||||
data: buf,
|
||||
}
|
||||
}
|
||||
|
||||
func (p pathData) release() {
|
||||
p.data.Release()
|
||||
}
|
||||
|
||||
func (p *pather) begin(sizes []image.Point) {
|
||||
p.stenciler.begin(sizes)
|
||||
}
|
||||
|
||||
func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) {
|
||||
p.stenciler.stencilPath(bounds, offset, uv, data)
|
||||
}
|
||||
|
||||
func (s *stenciler) beginIntersect(sizes []image.Point) {
|
||||
// 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, driver.TextureFormatFloat, sizes)
|
||||
}
|
||||
|
||||
func (s *stenciler) cover(idx int) FBO {
|
||||
return s.fbos.fbos[idx]
|
||||
}
|
||||
|
||||
func (s *stenciler) begin(sizes []image.Point) {
|
||||
s.fbos.resize(s.ctx, driver.TextureFormatFloat, sizes)
|
||||
}
|
||||
|
||||
func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) {
|
||||
s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
|
||||
// Transform UI coordinates to OpenGL coordinates.
|
||||
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.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
|
||||
for start < nquads {
|
||||
batch := nquads - start
|
||||
if max := pathBatchSize; batch > max {
|
||||
batch = max
|
||||
}
|
||||
off := vertStride * start * 4
|
||||
s.ctx.BindVertexBuffer(data.data, off)
|
||||
s.ctx.DrawElements(0, batch*6)
|
||||
start += batch
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pather) cover(mat materialType, isFBO bool, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
|
||||
p.coverer.cover(mat, isFBO, col, col1, col2, scale, off, uvTrans, coverScale, coverOff)
|
||||
}
|
||||
|
||||
func (c *coverer) cover(mat materialType, isFBO bool, 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.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.uvTransformR1 = [4]float32{t1, t2, t3, 0}
|
||||
c.texUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0}
|
||||
uniforms = &c.texUniforms.coverUniforms
|
||||
}
|
||||
uniforms.fbo = 0
|
||||
if isFBO {
|
||||
uniforms.fbo = 1
|
||||
}
|
||||
uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
|
||||
uniforms.uvCoverTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
|
||||
fboIdx := 0
|
||||
if isFBO {
|
||||
fboIdx = 1
|
||||
}
|
||||
c.pipelines[fboIdx][mat].UploadUniforms(c.ctx)
|
||||
c.ctx.DrawArrays(0, 4)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Check that struct vertex has the expected size and
|
||||
// that it contains no padding.
|
||||
if unsafe.Sizeof(*(*vertex)(nil)) != vertStride {
|
||||
panic("unexpected struct size")
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gpu
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gioui.org/gpu/internal/driver"
|
||||
)
|
||||
|
||||
type timers struct {
|
||||
backend driver.Device
|
||||
timers []*timer
|
||||
}
|
||||
|
||||
type timer struct {
|
||||
Elapsed time.Duration
|
||||
backend driver.Device
|
||||
timer driver.Timer
|
||||
state timerState
|
||||
}
|
||||
|
||||
type timerState uint8
|
||||
|
||||
const (
|
||||
timerIdle timerState = iota
|
||||
timerRunning
|
||||
timerWaiting
|
||||
)
|
||||
|
||||
func newTimers(b driver.Device) *timers {
|
||||
return &timers{
|
||||
backend: b,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *timers) newTimer() *timer {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
tt := &timer{
|
||||
backend: t.backend,
|
||||
timer: t.backend.NewTimer(),
|
||||
}
|
||||
t.timers = append(t.timers, tt)
|
||||
return tt
|
||||
}
|
||||
|
||||
func (t *timer) begin() {
|
||||
if t == nil || t.state != timerIdle {
|
||||
return
|
||||
}
|
||||
t.timer.Begin()
|
||||
t.state = timerRunning
|
||||
}
|
||||
|
||||
func (t *timer) end() {
|
||||
if t == nil || t.state != timerRunning {
|
||||
return
|
||||
}
|
||||
t.timer.End()
|
||||
t.state = timerWaiting
|
||||
}
|
||||
|
||||
func (t *timers) ready() bool {
|
||||
if t == nil {
|
||||
return false
|
||||
}
|
||||
for _, tt := range t.timers {
|
||||
switch tt.state {
|
||||
case timerIdle:
|
||||
continue
|
||||
case timerRunning:
|
||||
return false
|
||||
}
|
||||
d, ok := tt.timer.Duration()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
tt.state = timerIdle
|
||||
tt.Elapsed = d
|
||||
}
|
||||
return t.backend.IsTimeContinuous()
|
||||
}
|
||||
|
||||
func (t *timers) Release() {
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
for _, tt := range t.timers {
|
||||
tt.timer.Release()
|
||||
}
|
||||
t.timers = nil
|
||||
}
|
||||
Reference in New Issue
Block a user