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:
ila
2026-07-23 16:35:01 +08:00
co-authored by Claude Opus 4.8
parent 97c1c4a974
commit f58728cddd
972 changed files with 597802 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: Unlicense OR MIT
// This file exists so this package builds on non-Windows platforms.
package d3d11
+871
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: Unlicense OR MIT
// This file exists so this package builds on non-Darwin platforms.
package metal
File diff suppressed because it is too large Load Diff
+1374
View File
File diff suppressed because it is too large Load Diff
+176
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: Unlicense OR MIT
package vulkan
// Empty file to avoid the build error for platforms without Vulkan support.