mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-05 17:35:36 +00:00
840b9ffa9b
We'd like to allow Gio to share a Direct3D context with an embedding program like the GLFW example does for OpenGL. To do that, d3d11.Device needs to carry only the minimal information needed (ID3D11Device). This change moves the caches of ID3D11DepthStencilState and ID3D11BlendState from from d3d11.Device to d3d11.Backend. It also adds a Release method for freeing them. Signed-off-by: Elias Naur <mail@eliasnaur.com>
890 lines
24 KiB
Go
890 lines
24 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package d3d11
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"image"
|
|
"math"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
"gioui.org/gpu/backend"
|
|
gunsafe "gioui.org/internal/unsafe"
|
|
)
|
|
|
|
const debug = false
|
|
|
|
type Device struct {
|
|
dev *_ID3D11Device
|
|
ctx *_ID3D11DeviceContext
|
|
featLvl uint32
|
|
floatFormat uint32
|
|
}
|
|
|
|
type Backend struct {
|
|
// Temporary storage to avoid garbage.
|
|
clearColor [4]float32
|
|
viewport _D3D11_VIEWPORT
|
|
depthState depthState
|
|
blendState blendState
|
|
|
|
// Current program.
|
|
prog *Program
|
|
|
|
dev *Device
|
|
caps backend.Caps
|
|
|
|
// fbo is the currently bound fbo.
|
|
fbo *Framebuffer
|
|
|
|
// cached state objects.
|
|
depthStates map[depthState]*_ID3D11DepthStencilState
|
|
blendStates map[blendState]*_ID3D11BlendState
|
|
}
|
|
|
|
type blendState struct {
|
|
enable bool
|
|
sfactor backend.BlendFactor
|
|
dfactor backend.BlendFactor
|
|
}
|
|
|
|
type depthState struct {
|
|
enable bool
|
|
mask bool
|
|
fn backend.DepthFunc
|
|
}
|
|
|
|
type Texture struct {
|
|
backend *Backend
|
|
format uint32
|
|
bindings backend.BufferBinding
|
|
tex *_ID3D11Texture2D
|
|
sampler *_ID3D11SamplerState
|
|
resView *_ID3D11ShaderResourceView
|
|
width int
|
|
height int
|
|
}
|
|
|
|
type Program struct {
|
|
backend *Backend
|
|
|
|
vert struct {
|
|
shader *_ID3D11VertexShader
|
|
uniforms *Buffer
|
|
}
|
|
frag struct {
|
|
shader *_ID3D11PixelShader
|
|
uniforms *Buffer
|
|
}
|
|
}
|
|
|
|
type Framebuffer struct {
|
|
dev *Device
|
|
format uint32
|
|
resource *_ID3D11Resource
|
|
renderTarget *_ID3D11RenderTargetView
|
|
depthView *_ID3D11DepthStencilView
|
|
foreign bool
|
|
}
|
|
|
|
type Buffer struct {
|
|
backend *Backend
|
|
bind uint32
|
|
buf *_ID3D11Buffer
|
|
immutable bool
|
|
}
|
|
|
|
type InputLayout struct {
|
|
dev *Device
|
|
layout *_ID3D11InputLayout
|
|
}
|
|
|
|
type SwapChain struct {
|
|
swchain *_IDXGISwapChain
|
|
fbo *Framebuffer
|
|
}
|
|
|
|
func NewDevice() (*Device, error) {
|
|
var flags uint32
|
|
if debug {
|
|
flags |= _D3D11_CREATE_DEVICE_DEBUG
|
|
}
|
|
d3ddev, d3dctx, featLvl, err := _D3D11CreateDevice(
|
|
_D3D_DRIVER_TYPE_HARDWARE,
|
|
flags,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("NewContext: %v", err)
|
|
}
|
|
dev := &Device{dev: d3ddev, ctx: d3dctx, featLvl: featLvl}
|
|
if featLvl < _D3D_FEATURE_LEVEL_9_1 {
|
|
_IUnknownRelease(unsafe.Pointer(d3ddev), d3ddev.vtbl.Release)
|
|
_IUnknownRelease(unsafe.Pointer(d3dctx), d3dctx.vtbl.Release)
|
|
return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl)
|
|
}
|
|
floatFormat, _ := detectFloatFormat(d3ddev)
|
|
dev.floatFormat = floatFormat
|
|
return dev, nil
|
|
}
|
|
|
|
func detectFloatFormat(dev *_ID3D11Device) (uint32, bool) {
|
|
formats := []uint32{
|
|
_DXGI_FORMAT_R16_FLOAT,
|
|
_DXGI_FORMAT_R32_FLOAT,
|
|
_DXGI_FORMAT_R16G16_FLOAT,
|
|
_DXGI_FORMAT_R32G32_FLOAT,
|
|
// These last two are really wasteful, but c'est la vie.
|
|
_DXGI_FORMAT_R16G16B16A16_FLOAT,
|
|
_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 (d *Device) CreateSwapChain(hwnd windows.Handle) (*SwapChain, error) {
|
|
dxgiDev, err := _IUnknownQueryInterface(unsafe.Pointer(d.dev), d.dev.vtbl.QueryInterface, &_IID_IDXGIDevice)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("NewContext: %v", err)
|
|
}
|
|
adapter, err := (*_IDXGIDevice)(unsafe.Pointer(dxgiDev)).GetAdapter()
|
|
_IUnknownRelease(unsafe.Pointer(dxgiDev), dxgiDev.vtbl.Release)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("NewContext: %v", err)
|
|
}
|
|
dxgiFactory, err := (*_IDXGIObject)(unsafe.Pointer(adapter)).GetParent(&_IID_IDXGIFactory)
|
|
_IUnknownRelease(unsafe.Pointer(adapter), adapter.vtbl.Release)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("NewContext: %v", err)
|
|
}
|
|
d3dswchain, err := (*_IDXGIFactory)(unsafe.Pointer(dxgiFactory)).CreateSwapChain(
|
|
(*_IUnknown)(unsafe.Pointer(d.dev)),
|
|
&_DXGI_SWAP_CHAIN_DESC{
|
|
BufferDesc: _DXGI_MODE_DESC{
|
|
Format: _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
|
|
},
|
|
SampleDesc: _DXGI_SAMPLE_DESC{
|
|
Count: 1,
|
|
},
|
|
BufferUsage: _DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
|
BufferCount: 1,
|
|
OutputWindow: hwnd,
|
|
Windowed: 1,
|
|
SwapEffect: _DXGI_SWAP_EFFECT_DISCARD,
|
|
},
|
|
)
|
|
_IUnknownRelease(unsafe.Pointer(dxgiFactory), dxgiFactory.vtbl.Release)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("NewContext: %v", err)
|
|
}
|
|
return &SwapChain{swchain: d3dswchain, fbo: &Framebuffer{}}, nil
|
|
}
|
|
|
|
func (d *Device) BindFramebuffer(fbo *Framebuffer) {
|
|
d.ctx.OMSetRenderTargets(fbo.renderTarget, fbo.depthView)
|
|
}
|
|
|
|
func (s *SwapChain) Framebuffer(d *Device) (*Framebuffer, error) {
|
|
if s.fbo.renderTarget != nil {
|
|
return s.fbo, nil
|
|
}
|
|
desc, err := s.swchain.GetDesc()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
backBuffer, err := s.swchain.GetBuffer(0, &_IID_ID3D11Texture2D)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
texture := (*_ID3D11Resource)(unsafe.Pointer(backBuffer))
|
|
renderTarget, err := d.dev.CreateRenderTargetView(texture)
|
|
_IUnknownRelease(unsafe.Pointer(backBuffer), backBuffer.vtbl.Release)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
depthView, err := createDepthView(d.dev, int(desc.BufferDesc.Width), int(desc.BufferDesc.Height), 24)
|
|
if err != nil {
|
|
_IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release)
|
|
return nil, err
|
|
}
|
|
s.fbo.renderTarget = renderTarget
|
|
s.fbo.depthView = depthView
|
|
s.fbo.dev = d
|
|
return s.fbo, nil
|
|
}
|
|
|
|
func (d *Device) Release() {
|
|
_IUnknownRelease(unsafe.Pointer(d.ctx), d.ctx.vtbl.Release)
|
|
_IUnknownRelease(unsafe.Pointer(d.dev), d.dev.vtbl.Release)
|
|
d.ctx = nil
|
|
d.dev = nil
|
|
}
|
|
|
|
func (s *SwapChain) Resize() error {
|
|
if s.fbo.renderTarget != nil {
|
|
s.fbo.Release()
|
|
}
|
|
return s.swchain.ResizeBuffers(0, 0, 0, _DXGI_FORMAT_UNKNOWN, 0)
|
|
}
|
|
|
|
func (s *SwapChain) Release() {
|
|
_IUnknownRelease(unsafe.Pointer(s.swchain), s.swchain.vtbl.Release)
|
|
}
|
|
|
|
func (s *SwapChain) Present() error {
|
|
return s.swchain.Present(1, 0)
|
|
}
|
|
|
|
func NewBackend(d *Device) (*Backend, error) {
|
|
caps := backend.Caps{
|
|
MaxTextureSize: 2048, // 9.1 maximum
|
|
}
|
|
if d.floatFormat != 0 {
|
|
caps.Features |= backend.FeatureFloatRenderTargets
|
|
}
|
|
switch {
|
|
case d.featLvl >= _D3D_FEATURE_LEVEL_11_0:
|
|
caps.MaxTextureSize = 16384
|
|
case d.featLvl >= _D3D_FEATURE_LEVEL_9_3:
|
|
caps.MaxTextureSize = 4096
|
|
}
|
|
b := &Backend{
|
|
dev: d, caps: caps,
|
|
depthStates: make(map[depthState]*_ID3D11DepthStencilState),
|
|
blendStates: make(map[blendState]*_ID3D11BlendState),
|
|
}
|
|
// Enable depth mask to match OpenGL.
|
|
b.depthState.mask = true
|
|
// Disable backface culling to match OpenGL.
|
|
state, err := b.dev.dev.CreateRasterizerState(&_D3D11_RASTERIZER_DESC{
|
|
CullMode: _D3D11_CULL_NONE,
|
|
FillMode: _D3D11_FILL_SOLID,
|
|
DepthClipEnable: 1,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release)
|
|
b.dev.ctx.RSSetState(state)
|
|
return b, nil
|
|
}
|
|
|
|
func (b *Backend) BeginFrame() backend.Framebuffer {
|
|
renderTarget, depthView := b.dev.ctx.OMGetRenderTargets()
|
|
// Assume someone else is holding on to the render targets.
|
|
if renderTarget != nil {
|
|
_IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release)
|
|
}
|
|
if depthView != nil {
|
|
_IUnknownRelease(unsafe.Pointer(depthView), depthView.vtbl.Release)
|
|
}
|
|
return &Framebuffer{dev: b.dev, renderTarget: renderTarget, depthView: depthView, foreign: true}
|
|
}
|
|
|
|
func (b *Backend) EndFrame() {
|
|
}
|
|
|
|
func (b *Backend) Caps() backend.Caps {
|
|
return b.caps
|
|
}
|
|
|
|
func (b *Backend) NewTimer() backend.Timer {
|
|
panic("timers not supported")
|
|
}
|
|
|
|
func (b *Backend) IsTimeContinuous() bool {
|
|
panic("timers not supported")
|
|
}
|
|
|
|
func (b *Backend) Release() {
|
|
for _, state := range b.depthStates {
|
|
_IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release)
|
|
}
|
|
for _, state := range b.blendStates {
|
|
_IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release)
|
|
}
|
|
*b = Backend{}
|
|
}
|
|
|
|
func (b *Backend) NewTexture(format backend.TextureFormat, width, height int, minFilter, magFilter backend.TextureFilter, bindings backend.BufferBinding) (backend.Texture, error) {
|
|
var d3dfmt uint32
|
|
switch format {
|
|
case backend.TextureFormatFloat:
|
|
d3dfmt = b.dev.floatFormat
|
|
case backend.TextureFormatSRGB:
|
|
d3dfmt = _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
|
|
default:
|
|
return nil, fmt.Errorf("unsupported texture format %d", format)
|
|
}
|
|
tex, err := b.dev.dev.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
|
|
Width: uint32(width),
|
|
Height: uint32(height),
|
|
MipLevels: 1,
|
|
ArraySize: 1,
|
|
Format: d3dfmt,
|
|
SampleDesc: _DXGI_SAMPLE_DESC{
|
|
Count: 1,
|
|
Quality: 0,
|
|
},
|
|
BindFlags: convBufferBinding(bindings),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var (
|
|
sampler *_ID3D11SamplerState
|
|
resView *_ID3D11ShaderResourceView
|
|
)
|
|
if bindings&backend.BufferBindingTexture != 0 {
|
|
var filter uint32
|
|
switch {
|
|
case minFilter == backend.FilterNearest && magFilter == backend.FilterNearest:
|
|
filter = _D3D11_FILTER_MIN_MAG_MIP_POINT
|
|
case minFilter == backend.FilterLinear && magFilter == backend.FilterLinear:
|
|
filter = _D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT
|
|
default:
|
|
_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.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 {
|
|
_IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release)
|
|
return nil, err
|
|
}
|
|
resView, err = b.dev.dev.CreateShaderResourceViewTEX2D(
|
|
(*_ID3D11Resource)(unsafe.Pointer(tex)),
|
|
&_D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D{
|
|
_D3D11_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 {
|
|
_IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release)
|
|
_IUnknownRelease(unsafe.Pointer(sampler), sampler.vtbl.Release)
|
|
return nil, err
|
|
}
|
|
}
|
|
return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, bindings: bindings, width: width, height: height}, nil
|
|
}
|
|
|
|
func (b *Backend) NewFramebuffer(tex backend.Texture, depthBits int) (backend.Framebuffer, error) {
|
|
d3dtex := tex.(*Texture)
|
|
if d3dtex.bindings&backend.BufferBindingFramebuffer == 0 {
|
|
return nil, errors.New("the texture was created without BufferBindingFramebuffer binding")
|
|
}
|
|
resource := (*_ID3D11Resource)(unsafe.Pointer(d3dtex.tex))
|
|
renderTarget, err := b.dev.dev.CreateRenderTargetView(resource)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fbo := &Framebuffer{dev: b.dev, format: d3dtex.format, resource: resource, renderTarget: renderTarget}
|
|
if depthBits > 0 {
|
|
depthView, err := createDepthView(b.dev.dev, d3dtex.width, d3dtex.height, depthBits)
|
|
if err != nil {
|
|
_IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release)
|
|
return nil, err
|
|
}
|
|
fbo.depthView = depthView
|
|
}
|
|
return fbo, nil
|
|
}
|
|
|
|
func createDepthView(d *_ID3D11Device, width, height, depthBits int) (*_ID3D11DepthStencilView, error) {
|
|
depthTex, err := d.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
|
|
Width: uint32(width),
|
|
Height: uint32(height),
|
|
MipLevels: 1,
|
|
ArraySize: 1,
|
|
Format: _DXGI_FORMAT_D24_UNORM_S8_UINT,
|
|
SampleDesc: _DXGI_SAMPLE_DESC{
|
|
Count: 1,
|
|
Quality: 0,
|
|
},
|
|
BindFlags: _D3D11_BIND_DEPTH_STENCIL,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
depthView, err := d.CreateDepthStencilViewTEX2D(
|
|
(*_ID3D11Resource)(unsafe.Pointer(depthTex)),
|
|
&_D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D{
|
|
Format: _DXGI_FORMAT_D24_UNORM_S8_UINT,
|
|
ViewDimension: _D3D11_DSV_DIMENSION_TEXTURE2D,
|
|
},
|
|
)
|
|
_IUnknownRelease(unsafe.Pointer(depthTex), depthTex.vtbl.Release)
|
|
return depthView, err
|
|
}
|
|
|
|
func (b *Backend) NewInputLayout(vertexShader backend.ShaderSources, layout []backend.InputDesc) (backend.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 backend.DataTypeFloat:
|
|
switch l.Size {
|
|
case 1:
|
|
format = _DXGI_FORMAT_R32_FLOAT
|
|
case 2:
|
|
format = _DXGI_FORMAT_R32G32_FLOAT
|
|
case 3:
|
|
format = _DXGI_FORMAT_R32G32B32_FLOAT
|
|
case 4:
|
|
format = _DXGI_FORMAT_R32G32B32A32_FLOAT
|
|
default:
|
|
panic("unsupported float data size")
|
|
}
|
|
case backend.DataTypeShort:
|
|
switch l.Size {
|
|
case 1:
|
|
format = _DXGI_FORMAT_R16_SINT
|
|
case 2:
|
|
format = _DXGI_FORMAT_R16G16_SINT
|
|
default:
|
|
panic("unsupported float 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),
|
|
}
|
|
}
|
|
l, err := b.dev.dev.CreateInputLayout(descs, vertexShader.HLSL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &InputLayout{dev: b.dev, layout: l}, nil
|
|
}
|
|
|
|
func (b *Backend) NewBuffer(typ backend.BufferBinding, size int) (backend.Buffer, error) {
|
|
if typ&backend.BufferBindingUniforms != 0 {
|
|
if typ != backend.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)
|
|
buf, err := b.dev.dev.CreateBuffer(&_D3D11_BUFFER_DESC{
|
|
ByteWidth: uint32(size),
|
|
BindFlags: bind,
|
|
}, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Buffer{backend: b, buf: buf, bind: bind}, nil
|
|
}
|
|
|
|
func (b *Backend) NewImmutableBuffer(typ backend.BufferBinding, data []byte) (backend.Buffer, error) {
|
|
if typ&backend.BufferBindingUniforms != 0 {
|
|
if typ != backend.BufferBindingUniforms {
|
|
return nil, errors.New("uniform buffers cannot have other bindings")
|
|
}
|
|
if len(data)%16 != 0 {
|
|
return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", len(data))
|
|
}
|
|
}
|
|
bind := convBufferBinding(typ)
|
|
buf, err := b.dev.dev.CreateBuffer(&_D3D11_BUFFER_DESC{
|
|
ByteWidth: uint32(len(data)),
|
|
Usage: _D3D11_USAGE_IMMUTABLE,
|
|
BindFlags: bind,
|
|
}, data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Buffer{backend: b, buf: buf, bind: bind, immutable: true}, nil
|
|
}
|
|
|
|
func (b *Backend) NewComputeProgram(shader backend.ShaderSources) (backend.Program, error) {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (b *Backend) NewProgram(vertexShader, fragmentShader backend.ShaderSources) (backend.Program, error) {
|
|
vs, err := b.dev.dev.CreateVertexShader(vertexShader.HLSL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ps, err := b.dev.dev.CreatePixelShader(fragmentShader.HLSL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p := &Program{backend: b}
|
|
p.vert.shader = vs
|
|
p.frag.shader = ps
|
|
return p, nil
|
|
}
|
|
|
|
func (b *Backend) Clear(colr, colg, colb, cola float32) {
|
|
b.clearColor = [4]float32{colr, colg, colb, cola}
|
|
b.dev.ctx.ClearRenderTargetView(b.fbo.renderTarget, &b.clearColor)
|
|
}
|
|
|
|
func (b *Backend) ClearDepth(depth float32) {
|
|
if b.fbo.depthView != nil {
|
|
b.dev.ctx.ClearDepthStencilView(b.fbo.depthView, _D3D11_CLEAR_DEPTH|_D3D11_CLEAR_STENCIL, depth, 0)
|
|
}
|
|
}
|
|
|
|
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.dev.ctx.RSSetViewports(&b.viewport)
|
|
}
|
|
|
|
func (b *Backend) DrawArrays(mode backend.DrawMode, off, count int) {
|
|
b.prepareDraw(mode)
|
|
b.dev.ctx.Draw(uint32(count), uint32(off))
|
|
}
|
|
|
|
func (b *Backend) DrawElements(mode backend.DrawMode, off, count int) {
|
|
b.prepareDraw(mode)
|
|
b.dev.ctx.DrawIndexed(uint32(count), uint32(off), 0)
|
|
}
|
|
|
|
func (b *Backend) prepareDraw(mode backend.DrawMode) {
|
|
if p := b.prog; p != nil {
|
|
b.dev.ctx.VSSetShader(p.vert.shader)
|
|
b.dev.ctx.PSSetShader(p.frag.shader)
|
|
if buf := p.vert.uniforms; buf != nil {
|
|
b.dev.ctx.VSSetConstantBuffers(buf.buf)
|
|
}
|
|
if buf := p.frag.uniforms; buf != nil {
|
|
b.dev.ctx.PSSetConstantBuffers(buf.buf)
|
|
}
|
|
}
|
|
var topology uint32
|
|
switch mode {
|
|
case backend.DrawModeTriangles:
|
|
topology = _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
|
|
case backend.DrawModeTriangleStrip:
|
|
topology = _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
|
|
default:
|
|
panic("unsupported draw mode")
|
|
}
|
|
b.dev.ctx.IASetPrimitiveTopology(topology)
|
|
|
|
depthState, ok := b.depthStates[b.depthState]
|
|
if !ok {
|
|
var desc _D3D11_DEPTH_STENCIL_DESC
|
|
if b.depthState.enable {
|
|
desc.DepthEnable = 1
|
|
}
|
|
if b.depthState.mask {
|
|
desc.DepthWriteMask = _D3D11_DEPTH_WRITE_MASK_ALL
|
|
}
|
|
switch b.depthState.fn {
|
|
case backend.DepthFuncGreater:
|
|
desc.DepthFunc = _D3D11_COMPARISON_GREATER
|
|
case backend.DepthFuncGreaterEqual:
|
|
desc.DepthFunc = _D3D11_COMPARISON_GREATER_EQUAL
|
|
default:
|
|
panic("unsupported depth func")
|
|
}
|
|
var err error
|
|
depthState, err = b.dev.dev.CreateDepthStencilState(&desc)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
b.depthStates[b.depthState] = depthState
|
|
}
|
|
b.dev.ctx.OMSetDepthStencilState(depthState, 0)
|
|
|
|
blendState, ok := b.blendStates[b.blendState]
|
|
if !ok {
|
|
var desc _D3D11_BLEND_DESC
|
|
t0 := &desc.RenderTarget[0]
|
|
t0.RenderTargetWriteMask = _D3D11_COLOR_WRITE_ENABLE_ALL
|
|
t0.BlendOp = _D3D11_BLEND_OP_ADD
|
|
t0.BlendOpAlpha = _D3D11_BLEND_OP_ADD
|
|
if b.blendState.enable {
|
|
t0.BlendEnable = 1
|
|
}
|
|
scol, salpha := toBlendFactor(b.blendState.sfactor)
|
|
dcol, dalpha := toBlendFactor(b.blendState.dfactor)
|
|
t0.SrcBlend = scol
|
|
t0.SrcBlendAlpha = salpha
|
|
t0.DestBlend = dcol
|
|
t0.DestBlendAlpha = dalpha
|
|
var err error
|
|
blendState, err = b.dev.dev.CreateBlendState(&desc)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
b.blendStates[b.blendState] = blendState
|
|
}
|
|
b.dev.ctx.OMSetBlendState(blendState, nil, 0xffffffff)
|
|
}
|
|
|
|
func (b *Backend) DepthFunc(f backend.DepthFunc) {
|
|
b.depthState.fn = f
|
|
}
|
|
|
|
func (b *Backend) SetBlend(enable bool) {
|
|
b.blendState.enable = enable
|
|
}
|
|
|
|
func (b *Backend) SetDepthTest(enable bool) {
|
|
b.depthState.enable = enable
|
|
}
|
|
|
|
func (b *Backend) DepthMask(mask bool) {
|
|
b.depthState.mask = mask
|
|
}
|
|
|
|
func (b *Backend) BlendFunc(sfactor, dfactor backend.BlendFactor) {
|
|
b.blendState.sfactor = sfactor
|
|
b.blendState.dfactor = dfactor
|
|
}
|
|
|
|
func (b *Backend) BindImageTexture(unit int, tex backend.Texture, access backend.AccessBits, f backend.TextureFormat) {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (b *Backend) MemoryBarrier() {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (b *Backend) DispatchCompute(x, y, z int) {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (t *Texture) Upload(offset, size image.Point, pixels []byte) {
|
|
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 := (*_ID3D11Resource)(unsafe.Pointer(t.tex))
|
|
t.backend.dev.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels)
|
|
}
|
|
|
|
func (t *Texture) Release() {
|
|
_IUnknownRelease(unsafe.Pointer(t.tex), t.tex.vtbl.Release)
|
|
t.tex = nil
|
|
if t.sampler != nil {
|
|
_IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.vtbl.Release)
|
|
t.sampler = nil
|
|
}
|
|
if t.resView != nil {
|
|
_IUnknownRelease(unsafe.Pointer(t.resView), t.resView.vtbl.Release)
|
|
t.resView = nil
|
|
}
|
|
}
|
|
|
|
func (b *Backend) BindTexture(unit int, tex backend.Texture) {
|
|
t := tex.(*Texture)
|
|
b.dev.ctx.PSSetSamplers(uint32(unit), t.sampler)
|
|
b.dev.ctx.PSSetShaderResources(uint32(unit), t.resView)
|
|
}
|
|
|
|
func (b *Backend) BindProgram(prog backend.Program) {
|
|
b.prog = prog.(*Program)
|
|
}
|
|
|
|
func (p *Program) Release() {
|
|
_IUnknownRelease(unsafe.Pointer(p.vert.shader), p.vert.shader.vtbl.Release)
|
|
_IUnknownRelease(unsafe.Pointer(p.frag.shader), p.frag.shader.vtbl.Release)
|
|
p.vert.shader = nil
|
|
p.frag.shader = nil
|
|
}
|
|
|
|
func (p *Program) SetStorageBuffer(binding int, buffer backend.Buffer) {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (p *Program) SetVertexUniforms(buf backend.Buffer) {
|
|
p.vert.uniforms = buf.(*Buffer)
|
|
}
|
|
|
|
func (p *Program) SetFragmentUniforms(buf backend.Buffer) {
|
|
p.frag.uniforms = buf.(*Buffer)
|
|
}
|
|
|
|
func (b *Backend) BindVertexBuffer(buf backend.Buffer, stride, offset int) {
|
|
b.dev.ctx.IASetVertexBuffers(buf.(*Buffer).buf, uint32(stride), uint32(offset))
|
|
}
|
|
|
|
func (b *Backend) BindIndexBuffer(buf backend.Buffer) {
|
|
b.dev.ctx.IASetIndexBuffer(buf.(*Buffer).buf, _DXGI_FORMAT_R16_UINT, 0)
|
|
}
|
|
|
|
func (b *Buffer) Download(data []byte) error {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (b *Buffer) Upload(data []byte) {
|
|
b.backend.dev.ctx.UpdateSubresource((*_ID3D11Resource)(unsafe.Pointer(b.buf)), nil, 0, 0, data)
|
|
}
|
|
|
|
func (b *Buffer) Release() {
|
|
_IUnknownRelease(unsafe.Pointer(b.buf), b.buf.vtbl.Release)
|
|
b.buf = nil
|
|
}
|
|
|
|
func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
|
|
if f.resource == nil {
|
|
return errors.New("framebuffer does not support ReadPixels")
|
|
}
|
|
w, h := src.Dx(), src.Dy()
|
|
tex, err := f.dev.dev.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
|
|
Width: uint32(w),
|
|
Height: uint32(h),
|
|
MipLevels: 1,
|
|
ArraySize: 1,
|
|
Format: f.format,
|
|
SampleDesc: _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 _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release)
|
|
res := (*_ID3D11Resource)(unsafe.Pointer(tex))
|
|
f.dev.ctx.CopySubresourceRegion(
|
|
res,
|
|
0, // Destination subresource.
|
|
0, 0, 0, // Destination coordinates (x, y, z).
|
|
f.resource,
|
|
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 := f.dev.ctx.Map(res, 0, _D3D11_MAP_READ, 0)
|
|
if err != nil {
|
|
return fmt.Errorf("ReadPixels: %v", err)
|
|
}
|
|
defer f.dev.ctx.Unmap(res, 0)
|
|
srcPitch := w * 4
|
|
dstPitch := int(resMap.RowPitch)
|
|
mapSize := dstPitch * h
|
|
data := gunsafe.SliceOf(resMap.pData)[:mapSize: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) BindFramebuffer(fbo backend.Framebuffer) {
|
|
b.fbo = fbo.(*Framebuffer)
|
|
b.dev.BindFramebuffer(b.fbo)
|
|
}
|
|
|
|
func (f *Framebuffer) Invalidate() {
|
|
}
|
|
|
|
func (f *Framebuffer) Release() {
|
|
if f.foreign {
|
|
panic("framebuffer not created by NewFramebuffer")
|
|
}
|
|
if f.renderTarget != nil {
|
|
_IUnknownRelease(unsafe.Pointer(f.renderTarget), f.renderTarget.vtbl.Release)
|
|
f.renderTarget = nil
|
|
}
|
|
if f.depthView != nil {
|
|
_IUnknownRelease(unsafe.Pointer(f.depthView), f.depthView.vtbl.Release)
|
|
f.depthView = nil
|
|
}
|
|
}
|
|
|
|
func (b *Backend) BindInputLayout(layout backend.InputLayout) {
|
|
b.dev.ctx.IASetInputLayout(layout.(*InputLayout).layout)
|
|
}
|
|
|
|
func (l *InputLayout) Release() {
|
|
_IUnknownRelease(unsafe.Pointer(l.layout), l.layout.vtbl.Release)
|
|
l.layout = nil
|
|
}
|
|
|
|
func convBufferBinding(typ backend.BufferBinding) uint32 {
|
|
var bindings uint32
|
|
if typ&backend.BufferBindingVertices != 0 {
|
|
bindings |= _D3D11_BIND_VERTEX_BUFFER
|
|
}
|
|
if typ&backend.BufferBindingIndices != 0 {
|
|
bindings |= _D3D11_BIND_INDEX_BUFFER
|
|
}
|
|
if typ&backend.BufferBindingUniforms != 0 {
|
|
bindings |= _D3D11_BIND_CONSTANT_BUFFER
|
|
}
|
|
if typ&backend.BufferBindingTexture != 0 {
|
|
bindings |= _D3D11_BIND_SHADER_RESOURCE
|
|
}
|
|
if typ&backend.BufferBindingFramebuffer != 0 {
|
|
bindings |= _D3D11_BIND_RENDER_TARGET
|
|
}
|
|
return bindings
|
|
}
|
|
|
|
func toBlendFactor(f backend.BlendFactor) (uint32, uint32) {
|
|
switch f {
|
|
case backend.BlendFactorOne:
|
|
return _D3D11_BLEND_ONE, _D3D11_BLEND_ONE
|
|
case backend.BlendFactorOneMinusSrcAlpha:
|
|
return _D3D11_BLEND_INV_SRC_ALPHA, _D3D11_BLEND_INV_SRC_ALPHA
|
|
case backend.BlendFactorZero:
|
|
return _D3D11_BLEND_ZERO, _D3D11_BLEND_ZERO
|
|
case backend.BlendFactorDstColor:
|
|
return _D3D11_BLEND_DEST_COLOR, _D3D11_BLEND_DEST_ALPHA
|
|
default:
|
|
panic("unsupported blend source factor")
|
|
}
|
|
}
|