aboutsummaryrefslogtreecommitdiff
path: root/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go')
-rw-r--r--vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go859
1 files changed, 859 insertions, 0 deletions
diff --git a/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go b/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go
new file mode 100644
index 0000000..08698c3
--- /dev/null
+++ b/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: Unlicense OR MIT
+
+package d3d11
+
+import (
+ "errors"
+ "fmt"
+ "image"
+ "math"
+ "reflect"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+
+ "gioui.org/gpu/internal/driver"
+ "gioui.org/internal/d3d11"
+ "gioui.org/shader"
+)
+
+type Backend struct {
+ dev *d3d11.Device
+ ctx *d3d11.DeviceContext
+
+ // Temporary storage to avoid garbage.
+ clearColor [4]float32
+ viewport d3d11.VIEWPORT
+
+ pipeline *Pipeline
+ vert struct {
+ buffer *Buffer
+ offset int
+ }
+
+ program *Program
+
+ caps driver.Caps
+
+ floatFormat uint32
+}
+
+type Pipeline struct {
+ vert *d3d11.VertexShader
+ frag *d3d11.PixelShader
+ layout *d3d11.InputLayout
+ blend *d3d11.BlendState
+ stride int
+ topology driver.Topology
+}
+
+type Texture struct {
+ backend *Backend
+ format uint32
+ bindings driver.BufferBinding
+ tex *d3d11.Texture2D
+ sampler *d3d11.SamplerState
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ renderTarget *d3d11.RenderTargetView
+
+ width int
+ height int
+ foreign bool
+}
+
+type VertexShader struct {
+ backend *Backend
+ shader *d3d11.VertexShader
+ src shader.Sources
+}
+
+type FragmentShader struct {
+ backend *Backend
+ shader *d3d11.PixelShader
+}
+
+type Program struct {
+ backend *Backend
+ shader *d3d11.ComputeShader
+}
+
+type Buffer struct {
+ backend *Backend
+ bind uint32
+ buf *d3d11.Buffer
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ size int
+ immutable bool
+}
+
+func init() {
+ driver.NewDirect3D11Device = newDirect3D11Device
+}
+
+func detectFloatFormat(dev *d3d11.Device) (uint32, bool) {
+ formats := []uint32{
+ d3d11.DXGI_FORMAT_R16_FLOAT,
+ d3d11.DXGI_FORMAT_R32_FLOAT,
+ d3d11.DXGI_FORMAT_R16G16_FLOAT,
+ d3d11.DXGI_FORMAT_R32G32_FLOAT,
+ // These last two are really wasteful, but c'est la vie.
+ d3d11.DXGI_FORMAT_R16G16B16A16_FLOAT,
+ d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT,
+ }
+ for _, format := range formats {
+ need := uint32(d3d11.FORMAT_SUPPORT_TEXTURE2D | d3d11.FORMAT_SUPPORT_RENDER_TARGET)
+ if support, _ := dev.CheckFormatSupport(format); support&need == need {
+ return format, true
+ }
+ }
+ return 0, false
+}
+
+func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
+ dev := (*d3d11.Device)(api.Device)
+ b := &Backend{
+ dev: dev,
+ ctx: dev.GetImmediateContext(),
+ caps: driver.Caps{
+ MaxTextureSize: 2048, // 9.1 maximum
+ Features: driver.FeatureSRGB,
+ },
+ }
+ featLvl := dev.GetFeatureLevel()
+ switch {
+ case featLvl < d3d11.FEATURE_LEVEL_9_1:
+ d3d11.IUnknownRelease(unsafe.Pointer(dev), dev.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
+ return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl)
+ case featLvl >= d3d11.FEATURE_LEVEL_11_0:
+ b.caps.MaxTextureSize = 16384
+ b.caps.Features |= driver.FeatureCompute
+ case featLvl >= d3d11.FEATURE_LEVEL_9_3:
+ b.caps.MaxTextureSize = 4096
+ }
+ if fmt, ok := detectFloatFormat(dev); ok {
+ b.floatFormat = fmt
+ b.caps.Features |= driver.FeatureFloatRenderTargets
+ }
+ // Disable backface culling to match OpenGL.
+ state, err := dev.CreateRasterizerState(&d3d11.RASTERIZER_DESC{
+ CullMode: d3d11.CULL_NONE,
+ FillMode: d3d11.FILL_SOLID,
+ })
+ if err != nil {
+ return nil, err
+ }
+ defer d3d11.IUnknownRelease(unsafe.Pointer(state), state.Vtbl.Release)
+ b.ctx.RSSetState(state)
+ return b, nil
+}
+
+func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
+ var (
+ renderTarget *d3d11.RenderTargetView
+ )
+ if target != nil {
+ switch t := target.(type) {
+ case driver.Direct3D11RenderTarget:
+ renderTarget = (*d3d11.RenderTargetView)(t.RenderTarget)
+ case *Texture:
+ renderTarget = t.renderTarget
+ default:
+ panic(fmt.Errorf("d3d11: invalid render target type: %T", target))
+ }
+ }
+ b.ctx.OMSetRenderTargets(renderTarget, nil)
+ return &Texture{backend: b, renderTarget: renderTarget, foreign: true}
+}
+
+func (b *Backend) CopyTexture(dstTex driver.Texture, dstOrigin image.Point, srcTex driver.Texture, srcRect image.Rectangle) {
+ dst := (*d3d11.Resource)(unsafe.Pointer(dstTex.(*Texture).tex))
+ src := (*d3d11.Resource)(srcTex.(*Texture).tex)
+ b.ctx.CopySubresourceRegion(
+ dst,
+ 0, // Destination subresource.
+ uint32(dstOrigin.X), uint32(dstOrigin.Y), 0, // Destination coordinates (x, y, z).
+ src,
+ 0, // Source subresource.
+ &d3d11.BOX{
+ Left: uint32(srcRect.Min.X),
+ Top: uint32(srcRect.Min.Y),
+ Right: uint32(srcRect.Max.X),
+ Bottom: uint32(srcRect.Max.Y),
+ Front: 0,
+ Back: 1,
+ },
+ )
+}
+
+func (b *Backend) EndFrame() {
+}
+
+func (b *Backend) Caps() driver.Caps {
+ return b.caps
+}
+
+func (b *Backend) NewTimer() driver.Timer {
+ panic("timers not supported")
+}
+
+func (b *Backend) IsTimeContinuous() bool {
+ panic("timers not supported")
+}
+
+func (b *Backend) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
+ *b = Backend{}
+}
+
+func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
+ var d3dfmt uint32
+ switch format {
+ case driver.TextureFormatFloat:
+ d3dfmt = b.floatFormat
+ case driver.TextureFormatSRGBA:
+ d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
+ case driver.TextureFormatRGBA8:
+ d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM
+ default:
+ return nil, fmt.Errorf("unsupported texture format %d", format)
+ }
+ tex, err := b.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
+ Width: uint32(width),
+ Height: uint32(height),
+ MipLevels: 1,
+ ArraySize: 1,
+ Format: d3dfmt,
+ SampleDesc: d3d11.DXGI_SAMPLE_DESC{
+ Count: 1,
+ Quality: 0,
+ },
+ BindFlags: convBufferBinding(bindings),
+ })
+ if err != nil {
+ return nil, err
+ }
+ var (
+ sampler *d3d11.SamplerState
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ fbo *d3d11.RenderTargetView
+ )
+ if bindings&driver.BufferBindingTexture != 0 {
+ var filter uint32
+ switch {
+ case minFilter == driver.FilterNearest && magFilter == driver.FilterNearest:
+ filter = d3d11.FILTER_MIN_MAG_MIP_POINT
+ case minFilter == driver.FilterLinear && magFilter == driver.FilterLinear:
+ filter = d3d11.FILTER_MIN_MAG_LINEAR_MIP_POINT
+ default:
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter)
+ }
+ var err error
+ sampler, err = b.dev.CreateSamplerState(&d3d11.SAMPLER_DESC{
+ Filter: filter,
+ AddressU: d3d11.TEXTURE_ADDRESS_CLAMP,
+ AddressV: d3d11.TEXTURE_ADDRESS_CLAMP,
+ AddressW: d3d11.TEXTURE_ADDRESS_CLAMP,
+ MaxAnisotropy: 1,
+ MinLOD: -math.MaxFloat32,
+ MaxLOD: math.MaxFloat32,
+ })
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, err
+ }
+ resView, err = b.dev.CreateShaderResourceView(
+ (*d3d11.Resource)(unsafe.Pointer(tex)),
+ unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_TEX2D{
+ SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{
+ Format: d3dfmt,
+ ViewDimension: d3d11.SRV_DIMENSION_TEXTURE2D,
+ },
+ Texture2D: d3d11.TEX2D_SRV{
+ MostDetailedMip: 0,
+ MipLevels: ^uint32(0),
+ },
+ }),
+ )
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
+ return nil, err
+ }
+ }
+ if bindings&driver.BufferBindingShaderStorageWrite != 0 {
+ uaView, err = b.dev.CreateUnorderedAccessView(
+ (*d3d11.Resource)(unsafe.Pointer(tex)),
+ unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_TEX2D{
+ UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{
+ Format: d3dfmt,
+ ViewDimension: d3d11.UAV_DIMENSION_TEXTURE2D,
+ },
+ Texture2D: d3d11.TEX2D_UAV{
+ MipSlice: 0,
+ },
+ }),
+ )
+ if err != nil {
+ if sampler != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
+ }
+ if resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, err
+ }
+ }
+ if bindings&driver.BufferBindingFramebuffer != 0 {
+ resource := (*d3d11.Resource)(unsafe.Pointer(tex))
+ fbo, err = b.dev.CreateRenderTargetView(resource)
+ if err != nil {
+ if uaView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(uaView), uaView.Vtbl.Release)
+ }
+ if sampler != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
+ }
+ if resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ return nil, err
+ }
+ }
+ return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, uaView: uaView, renderTarget: fbo, bindings: bindings, width: width, height: height}, nil
+}
+
+func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []driver.InputDesc) (*d3d11.InputLayout, error) {
+ if len(vertexShader.Inputs) != len(layout) {
+ return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs))
+ }
+ descs := make([]d3d11.INPUT_ELEMENT_DESC, len(layout))
+ for i, l := range layout {
+ inp := vertexShader.Inputs[i]
+ cname, err := windows.BytePtrFromString(inp.Semantic)
+ if err != nil {
+ return nil, err
+ }
+ var format uint32
+ switch l.Type {
+ case shader.DataTypeFloat:
+ switch l.Size {
+ case 1:
+ format = d3d11.DXGI_FORMAT_R32_FLOAT
+ case 2:
+ format = d3d11.DXGI_FORMAT_R32G32_FLOAT
+ case 3:
+ format = d3d11.DXGI_FORMAT_R32G32B32_FLOAT
+ case 4:
+ format = d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT
+ default:
+ panic("unsupported data size")
+ }
+ case shader.DataTypeShort:
+ switch l.Size {
+ case 1:
+ format = d3d11.DXGI_FORMAT_R16_SINT
+ case 2:
+ format = d3d11.DXGI_FORMAT_R16G16_SINT
+ default:
+ panic("unsupported data size")
+ }
+ default:
+ panic("unsupported data type")
+ }
+ descs[i] = d3d11.INPUT_ELEMENT_DESC{
+ SemanticName: cname,
+ SemanticIndex: uint32(inp.SemanticIndex),
+ Format: format,
+ AlignedByteOffset: uint32(l.Offset),
+ }
+ }
+ return b.dev.CreateInputLayout(descs, []byte(vertexShader.DXBC))
+}
+
+func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
+ return b.newBuffer(typ, size, nil, false)
+}
+
+func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
+ return b.newBuffer(typ, len(data), data, true)
+}
+
+func (b *Backend) newBuffer(typ driver.BufferBinding, size int, data []byte, immutable bool) (*Buffer, error) {
+ if typ&driver.BufferBindingUniforms != 0 {
+ if typ != driver.BufferBindingUniforms {
+ return nil, errors.New("uniform buffers cannot have other bindings")
+ }
+ if size%16 != 0 {
+ return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", size)
+ }
+ }
+ bind := convBufferBinding(typ)
+ var usage, miscFlags, cpuFlags uint32
+ if immutable {
+ usage = d3d11.USAGE_IMMUTABLE
+ }
+ if typ&driver.BufferBindingShaderStorageWrite != 0 {
+ cpuFlags = d3d11.CPU_ACCESS_READ
+ }
+ if typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 {
+ miscFlags |= d3d11.RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS
+ }
+ buf, err := b.dev.CreateBuffer(&d3d11.BUFFER_DESC{
+ ByteWidth: uint32(size),
+ Usage: usage,
+ BindFlags: bind,
+ CPUAccessFlags: cpuFlags,
+ MiscFlags: miscFlags,
+ }, data)
+ if err != nil {
+ return nil, err
+ }
+ var (
+ resView *d3d11.ShaderResourceView
+ uaView *d3d11.UnorderedAccessView
+ )
+ if typ&driver.BufferBindingShaderStorageWrite != 0 {
+ uaView, err = b.dev.CreateUnorderedAccessView(
+ (*d3d11.Resource)(unsafe.Pointer(buf)),
+ unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_BUFFER{
+ UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{
+ Format: d3d11.DXGI_FORMAT_R32_TYPELESS,
+ ViewDimension: d3d11.UAV_DIMENSION_BUFFER,
+ },
+ Buffer: d3d11.BUFFER_UAV{
+ FirstElement: 0,
+ NumElements: uint32(size / 4),
+ Flags: d3d11.BUFFER_UAV_FLAG_RAW,
+ },
+ }),
+ )
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release)
+ return nil, err
+ }
+ } else if typ&driver.BufferBindingShaderStorageRead != 0 {
+ resView, err = b.dev.CreateShaderResourceView(
+ (*d3d11.Resource)(unsafe.Pointer(buf)),
+ unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_BUFFEREX{
+ SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{
+ Format: d3d11.DXGI_FORMAT_R32_TYPELESS,
+ ViewDimension: d3d11.SRV_DIMENSION_BUFFEREX,
+ },
+ Buffer: d3d11.BUFFEREX_SRV{
+ FirstElement: 0,
+ NumElements: uint32(size / 4),
+ Flags: d3d11.BUFFEREX_SRV_FLAG_RAW,
+ },
+ }),
+ )
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release)
+ return nil, err
+ }
+ }
+ return &Buffer{backend: b, buf: buf, bind: bind, size: size, resView: resView, uaView: uaView, immutable: immutable}, nil
+}
+
+func (b *Backend) NewComputeProgram(shader shader.Sources) (driver.Program, error) {
+ cs, err := b.dev.CreateComputeShader([]byte(shader.DXBC))
+ if err != nil {
+ return nil, err
+ }
+ return &Program{backend: b, shader: cs}, nil
+}
+
+func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
+ vsh := desc.VertexShader.(*VertexShader)
+ fsh := desc.FragmentShader.(*FragmentShader)
+ blend, err := b.newBlendState(desc.BlendDesc)
+ if err != nil {
+ return nil, err
+ }
+ var layout *d3d11.InputLayout
+ if l := desc.VertexLayout; l.Stride > 0 {
+ var err error
+ layout, err = b.newInputLayout(vsh.src, l.Inputs)
+ if err != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(blend), blend.Vtbl.AddRef)
+ return nil, err
+ }
+ }
+
+ // Retain shaders.
+ vshRef := vsh.shader
+ fshRef := fsh.shader
+ d3d11.IUnknownAddRef(unsafe.Pointer(vshRef), vshRef.Vtbl.AddRef)
+ d3d11.IUnknownAddRef(unsafe.Pointer(fshRef), fshRef.Vtbl.AddRef)
+
+ return &Pipeline{
+ vert: vshRef,
+ frag: fshRef,
+ layout: layout,
+ stride: desc.VertexLayout.Stride,
+ blend: blend,
+ topology: desc.Topology,
+ }, nil
+}
+
+func (b *Backend) newBlendState(desc driver.BlendDesc) (*d3d11.BlendState, error) {
+ var d3ddesc d3d11.BLEND_DESC
+ t0 := &d3ddesc.RenderTarget[0]
+ t0.RenderTargetWriteMask = d3d11.COLOR_WRITE_ENABLE_ALL
+ t0.BlendOp = d3d11.BLEND_OP_ADD
+ t0.BlendOpAlpha = d3d11.BLEND_OP_ADD
+ if desc.Enable {
+ t0.BlendEnable = 1
+ }
+ scol, salpha := toBlendFactor(desc.SrcFactor)
+ dcol, dalpha := toBlendFactor(desc.DstFactor)
+ t0.SrcBlend = scol
+ t0.SrcBlendAlpha = salpha
+ t0.DestBlend = dcol
+ t0.DestBlendAlpha = dalpha
+ return b.dev.CreateBlendState(&d3ddesc)
+}
+
+func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
+ vs, err := b.dev.CreateVertexShader([]byte(src.DXBC))
+ if err != nil {
+ return nil, err
+ }
+ return &VertexShader{b, vs, src}, nil
+}
+
+func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
+ fs, err := b.dev.CreatePixelShader([]byte(src.DXBC))
+ if err != nil {
+ return nil, err
+ }
+ return &FragmentShader{b, fs}, nil
+}
+
+func (b *Backend) Viewport(x, y, width, height int) {
+ b.viewport = d3d11.VIEWPORT{
+ TopLeftX: float32(x),
+ TopLeftY: float32(y),
+ Width: float32(width),
+ Height: float32(height),
+ MinDepth: 0.0,
+ MaxDepth: 1.0,
+ }
+ b.ctx.RSSetViewports(&b.viewport)
+}
+
+func (b *Backend) DrawArrays(off, count int) {
+ b.prepareDraw()
+ b.ctx.Draw(uint32(count), uint32(off))
+}
+
+func (b *Backend) DrawElements(off, count int) {
+ b.prepareDraw()
+ b.ctx.DrawIndexed(uint32(count), uint32(off), 0)
+}
+
+func (b *Backend) prepareDraw() {
+ p := b.pipeline
+ if p == nil {
+ return
+ }
+ b.ctx.VSSetShader(p.vert)
+ b.ctx.PSSetShader(p.frag)
+ b.ctx.IASetInputLayout(p.layout)
+ b.ctx.OMSetBlendState(p.blend, nil, 0xffffffff)
+ if b.vert.buffer != nil {
+ b.ctx.IASetVertexBuffers(b.vert.buffer.buf, uint32(p.stride), uint32(b.vert.offset))
+ }
+ var topology uint32
+ switch p.topology {
+ case driver.TopologyTriangles:
+ topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLELIST
+ case driver.TopologyTriangleStrip:
+ topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
+ default:
+ panic("unsupported draw mode")
+ }
+ b.ctx.IASetPrimitiveTopology(topology)
+}
+
+func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ if t.uaView != nil {
+ b.ctx.CSSetUnorderedAccessViews(uint32(unit), t.uaView)
+ } else {
+ b.ctx.CSSetShaderResources(uint32(unit), t.resView)
+ }
+}
+
+func (b *Backend) DispatchCompute(x, y, z int) {
+ b.ctx.CSSetShader(b.program.shader)
+ b.ctx.Dispatch(uint32(x), uint32(y), uint32(z))
+}
+
+func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
+ if stride == 0 {
+ stride = size.X * 4
+ }
+ dst := &d3d11.BOX{
+ Left: uint32(offset.X),
+ Top: uint32(offset.Y),
+ Right: uint32(offset.X + size.X),
+ Bottom: uint32(offset.Y + size.Y),
+ Front: 0,
+ Back: 1,
+ }
+ res := (*d3d11.Resource)(unsafe.Pointer(t.tex))
+ t.backend.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels)
+}
+
+func (t *Texture) Release() {
+ if t.foreign {
+ panic("texture not created by NewTexture")
+ }
+ if t.renderTarget != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.renderTarget), t.renderTarget.Vtbl.Release)
+ }
+ if t.sampler != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.Vtbl.Release)
+ }
+ if t.resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.resView), t.resView.Vtbl.Release)
+ }
+ if t.uaView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(t.uaView), t.uaView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(t.tex), t.tex.Vtbl.Release)
+ *t = Texture{}
+}
+
+func (b *Backend) PrepareTexture(tex driver.Texture) {}
+
+func (b *Backend) BindTexture(unit int, tex driver.Texture) {
+ t := tex.(*Texture)
+ b.ctx.PSSetSamplers(uint32(unit), t.sampler)
+ b.ctx.PSSetShaderResources(uint32(unit), t.resView)
+}
+
+func (b *Backend) BindPipeline(pipe driver.Pipeline) {
+ b.pipeline = pipe.(*Pipeline)
+}
+
+func (b *Backend) BindProgram(prog driver.Program) {
+ b.program = prog.(*Program)
+}
+
+func (s *VertexShader) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
+ *s = VertexShader{}
+}
+
+func (s *FragmentShader) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
+ *s = FragmentShader{}
+}
+
+func (s *Program) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
+ *s = Program{}
+}
+
+func (p *Pipeline) Release() {
+ d3d11.IUnknownRelease(unsafe.Pointer(p.vert), p.vert.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(p.frag), p.frag.Vtbl.Release)
+ d3d11.IUnknownRelease(unsafe.Pointer(p.blend), p.blend.Vtbl.Release)
+ if l := p.layout; l != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(l), l.Vtbl.Release)
+ }
+ *p = Pipeline{}
+}
+
+func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ if buf.resView != nil {
+ b.ctx.CSSetShaderResources(uint32(binding), buf.resView)
+ } else {
+ b.ctx.CSSetUnorderedAccessViews(uint32(binding), buf.uaView)
+ }
+}
+
+func (b *Backend) BindUniforms(buffer driver.Buffer) {
+ buf := buffer.(*Buffer)
+ b.ctx.VSSetConstantBuffers(buf.buf)
+ b.ctx.PSSetConstantBuffers(buf.buf)
+}
+
+func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
+ b.vert.buffer = buf.(*Buffer)
+ b.vert.offset = offset
+}
+
+func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
+ b.ctx.IASetIndexBuffer(buf.(*Buffer).buf, d3d11.DXGI_FORMAT_R16_UINT, 0)
+}
+
+func (b *Buffer) Download(dst []byte) error {
+ res := (*d3d11.Resource)(unsafe.Pointer(b.buf))
+ resMap, err := b.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0)
+ if err != nil {
+ return fmt.Errorf("d3d11: %v", err)
+ }
+ defer b.backend.ctx.Unmap(res, 0)
+ data := sliceOf(resMap.PData, len(dst))
+ copy(dst, data)
+ return nil
+}
+
+func (b *Buffer) Upload(data []byte) {
+ var dst *d3d11.BOX
+ if len(data) < b.size {
+ dst = &d3d11.BOX{
+ Left: 0,
+ Right: uint32(len(data)),
+ Top: 0,
+ Bottom: 1,
+ Front: 0,
+ Back: 1,
+ }
+ }
+ b.backend.ctx.UpdateSubresource((*d3d11.Resource)(unsafe.Pointer(b.buf)), dst, 0, 0, data)
+}
+
+func (b *Buffer) Release() {
+ if b.resView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(b.resView), b.resView.Vtbl.Release)
+ }
+ if b.uaView != nil {
+ d3d11.IUnknownRelease(unsafe.Pointer(b.uaView), b.uaView.Vtbl.Release)
+ }
+ d3d11.IUnknownRelease(unsafe.Pointer(b.buf), b.buf.Vtbl.Release)
+ *b = Buffer{}
+}
+
+func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
+ w, h := src.Dx(), src.Dy()
+ tex, err := t.backend.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
+ Width: uint32(w),
+ Height: uint32(h),
+ MipLevels: 1,
+ ArraySize: 1,
+ Format: t.format,
+ SampleDesc: d3d11.DXGI_SAMPLE_DESC{
+ Count: 1,
+ Quality: 0,
+ },
+ Usage: d3d11.USAGE_STAGING,
+ CPUAccessFlags: d3d11.CPU_ACCESS_READ,
+ })
+ if err != nil {
+ return fmt.Errorf("ReadPixels: %v", err)
+ }
+ defer d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
+ res := (*d3d11.Resource)(unsafe.Pointer(tex))
+ t.backend.ctx.CopySubresourceRegion(
+ res,
+ 0, // Destination subresource.
+ 0, 0, 0, // Destination coordinates (x, y, z).
+ (*d3d11.Resource)(t.tex),
+ 0, // Source subresource.
+ &d3d11.BOX{
+ Left: uint32(src.Min.X),
+ Top: uint32(src.Min.Y),
+ Right: uint32(src.Max.X),
+ Bottom: uint32(src.Max.Y),
+ Front: 0,
+ Back: 1,
+ },
+ )
+ resMap, err := t.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0)
+ if err != nil {
+ return fmt.Errorf("ReadPixels: %v", err)
+ }
+ defer t.backend.ctx.Unmap(res, 0)
+ srcPitch := stride
+ dstPitch := int(resMap.RowPitch)
+ mapSize := dstPitch * h
+ data := sliceOf(resMap.PData, mapSize)
+ width := w * 4
+ for r := 0; r < h; r++ {
+ pixels := pixels[r*srcPitch:]
+ copy(pixels[:width], data[r*dstPitch:])
+ }
+ return nil
+}
+
+func (b *Backend) BeginCompute() {
+}
+
+func (b *Backend) EndCompute() {
+}
+
+func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
+ t := tex.(*Texture)
+ b.ctx.OMSetRenderTargets(t.renderTarget, nil)
+ if d.Action == driver.LoadActionClear {
+ c := d.ClearColor
+ b.clearColor = [4]float32{c.R, c.G, c.B, c.A}
+ b.ctx.ClearRenderTargetView(t.renderTarget, &b.clearColor)
+ }
+}
+
+func (b *Backend) EndRenderPass() {
+}
+
+func (f *Texture) ImplementsRenderTarget() {}
+
+func convBufferBinding(typ driver.BufferBinding) uint32 {
+ var bindings uint32
+ if typ&driver.BufferBindingVertices != 0 {
+ bindings |= d3d11.BIND_VERTEX_BUFFER
+ }
+ if typ&driver.BufferBindingIndices != 0 {
+ bindings |= d3d11.BIND_INDEX_BUFFER
+ }
+ if typ&driver.BufferBindingUniforms != 0 {
+ bindings |= d3d11.BIND_CONSTANT_BUFFER
+ }
+ if typ&driver.BufferBindingTexture != 0 {
+ bindings |= d3d11.BIND_SHADER_RESOURCE
+ }
+ if typ&driver.BufferBindingFramebuffer != 0 {
+ bindings |= d3d11.BIND_RENDER_TARGET
+ }
+ if typ&driver.BufferBindingShaderStorageWrite != 0 {
+ bindings |= d3d11.BIND_UNORDERED_ACCESS
+ } else if typ&driver.BufferBindingShaderStorageRead != 0 {
+ bindings |= d3d11.BIND_SHADER_RESOURCE
+ }
+ return bindings
+}
+
+func toBlendFactor(f driver.BlendFactor) (uint32, uint32) {
+ switch f {
+ case driver.BlendFactorOne:
+ return d3d11.BLEND_ONE, d3d11.BLEND_ONE
+ case driver.BlendFactorOneMinusSrcAlpha:
+ return d3d11.BLEND_INV_SRC_ALPHA, d3d11.BLEND_INV_SRC_ALPHA
+ case driver.BlendFactorZero:
+ return d3d11.BLEND_ZERO, d3d11.BLEND_ZERO
+ case driver.BlendFactorDstColor:
+ return d3d11.BLEND_DEST_COLOR, d3d11.BLEND_DEST_ALPHA
+ default:
+ panic("unsupported blend source factor")
+ }
+}
+
+// sliceOf returns a slice from a (native) pointer.
+func sliceOf(ptr uintptr, cap int) []byte {
+ var data []byte
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ h.Data = ptr
+ h.Cap = cap
+ h.Len = cap
+ return data
+}