Files
gio-patched/gpu/internal/d3d11/d3d11_windows.go
T
Elias Naur b5e3756ee8 gpu/internal/driver: get rid of Device.MemoryBarrier
MemoryBarrier is meant to stand in for OpenGL ES 3.1's glMemoryBarrier.
However, it badly fits with the other backends: Metal and D3D11 have
automatic memory barriers, and Vulkan needs barriers for graphics as
well.

This change removes MemoryBarrier, and puts the burden on the backends.
The OpenGL backend simply adds a barrier between every compute dispatch.

This change only adds a single memory barrier compared to the manual
barriers before this change, which is unlikely to affect performance
much.. We can revisit the automatic barriers if they ever become a
performance problem.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-09-21 17:53:58 +02:00

860 lines
23 KiB
Go

// 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
}