github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/gpu/internal/d3d11/d3d11_windows.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package d3d11
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"image"
     9  	"math"
    10  	"reflect"
    11  	"unsafe"
    12  
    13  	"golang.org/x/sys/windows"
    14  
    15  	"github.com/cybriq/giocore/gpu/internal/driver"
    16  	"github.com/cybriq/giocore/internal/d3d11"
    17  )
    18  
    19  type Backend struct {
    20  	dev *d3d11.Device
    21  	ctx *d3d11.DeviceContext
    22  
    23  	// Temporary storage to avoid garbage.
    24  	clearColor [4]float32
    25  	viewport   d3d11.VIEWPORT
    26  	depthState depthState
    27  	blendState blendState
    28  
    29  	// Current program.
    30  	prog *Program
    31  
    32  	caps driver.Caps
    33  
    34  	// fbo is the currently bound fbo.
    35  	fbo *Framebuffer
    36  
    37  	floatFormat uint32
    38  
    39  	// cached state objects.
    40  	depthStates map[depthState]*d3d11.DepthStencilState
    41  	blendStates map[blendState]*d3d11.BlendState
    42  }
    43  
    44  type blendState struct {
    45  	enable  bool
    46  	sfactor driver.BlendFactor
    47  	dfactor driver.BlendFactor
    48  }
    49  
    50  type depthState struct {
    51  	enable bool
    52  	mask   bool
    53  	fn     driver.DepthFunc
    54  }
    55  
    56  type Texture struct {
    57  	backend  *Backend
    58  	format   uint32
    59  	bindings driver.BufferBinding
    60  	tex      *d3d11.Texture2D
    61  	sampler  *d3d11.SamplerState
    62  	resView  *d3d11.ShaderResourceView
    63  	width    int
    64  	height   int
    65  }
    66  
    67  type Program struct {
    68  	backend *Backend
    69  
    70  	vert struct {
    71  		shader   *d3d11.VertexShader
    72  		uniforms *Buffer
    73  	}
    74  	frag struct {
    75  		shader   *d3d11.PixelShader
    76  		uniforms *Buffer
    77  	}
    78  }
    79  
    80  type Framebuffer struct {
    81  	dev          *d3d11.Device
    82  	ctx          *d3d11.DeviceContext
    83  	format       uint32
    84  	resource     *d3d11.Resource
    85  	renderTarget *d3d11.RenderTargetView
    86  	depthView    *d3d11.DepthStencilView
    87  	foreign      bool
    88  }
    89  
    90  type Buffer struct {
    91  	backend   *Backend
    92  	bind      uint32
    93  	buf       *d3d11.Buffer
    94  	immutable bool
    95  }
    96  
    97  type InputLayout struct {
    98  	layout *d3d11.InputLayout
    99  }
   100  
   101  func init() {
   102  	driver.NewDirect3D11Device = newDirect3D11Device
   103  }
   104  
   105  func detectFloatFormat(dev *d3d11.Device) (uint32, bool) {
   106  	formats := []uint32{
   107  		d3d11.DXGI_FORMAT_R16_FLOAT,
   108  		d3d11.DXGI_FORMAT_R32_FLOAT,
   109  		d3d11.DXGI_FORMAT_R16G16_FLOAT,
   110  		d3d11.DXGI_FORMAT_R32G32_FLOAT,
   111  		// These last two are really wasteful, but c'est la vie.
   112  		d3d11.DXGI_FORMAT_R16G16B16A16_FLOAT,
   113  		d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT,
   114  	}
   115  	for _, format := range formats {
   116  		need := uint32(d3d11.FORMAT_SUPPORT_TEXTURE2D | d3d11.FORMAT_SUPPORT_RENDER_TARGET)
   117  		if support, _ := dev.CheckFormatSupport(format); support&need == need {
   118  			return format, true
   119  		}
   120  	}
   121  	return 0, false
   122  }
   123  
   124  func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
   125  	dev := (*d3d11.Device)(api.Device)
   126  	b := &Backend{
   127  		dev: dev,
   128  		ctx: dev.GetImmediateContext(),
   129  		caps: driver.Caps{
   130  			MaxTextureSize: 2048, // 9.1 maximum
   131  		},
   132  		depthStates: make(map[depthState]*d3d11.DepthStencilState),
   133  		blendStates: make(map[blendState]*d3d11.BlendState),
   134  	}
   135  	featLvl := dev.GetFeatureLevel()
   136  	if featLvl < d3d11.FEATURE_LEVEL_9_1 {
   137  		d3d11.IUnknownRelease(unsafe.Pointer(dev), dev.Vtbl.Release)
   138  		d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
   139  		return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl)
   140  	}
   141  	switch {
   142  	case featLvl >= d3d11.FEATURE_LEVEL_11_0:
   143  		b.caps.MaxTextureSize = 16384
   144  	case featLvl >= d3d11.FEATURE_LEVEL_9_3:
   145  		b.caps.MaxTextureSize = 4096
   146  	}
   147  	if fmt, ok := detectFloatFormat(dev); ok {
   148  		b.floatFormat = fmt
   149  		b.caps.Features |= driver.FeatureFloatRenderTargets
   150  	}
   151  	// Enable depth mask to match OpenGL.
   152  	b.depthState.mask = true
   153  	// Disable backface culling to match OpenGL.
   154  	state, err := dev.CreateRasterizerState(&d3d11.RASTERIZER_DESC{
   155  		CullMode:        d3d11.CULL_NONE,
   156  		FillMode:        d3d11.FILL_SOLID,
   157  		DepthClipEnable: 1,
   158  	})
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	defer d3d11.IUnknownRelease(unsafe.Pointer(state), state.Vtbl.Release)
   163  	b.ctx.RSSetState(state)
   164  	return b, nil
   165  }
   166  
   167  func (b *Backend) BeginFrame(clear bool, viewport image.Point) driver.Framebuffer {
   168  	renderTarget, depthView := b.ctx.OMGetRenderTargets()
   169  	// Assume someone else is holding on to the render targets.
   170  	if renderTarget != nil {
   171  		d3d11.IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.Vtbl.Release)
   172  	}
   173  	if depthView != nil {
   174  		d3d11.IUnknownRelease(unsafe.Pointer(depthView), depthView.Vtbl.Release)
   175  	}
   176  	return &Framebuffer{ctx: b.ctx, dev: b.dev, renderTarget: renderTarget, depthView: depthView, foreign: true}
   177  }
   178  
   179  func (b *Backend) EndFrame() {
   180  }
   181  
   182  func (b *Backend) Caps() driver.Caps {
   183  	return b.caps
   184  }
   185  
   186  func (b *Backend) NewTimer() driver.Timer {
   187  	panic("timers not supported")
   188  }
   189  
   190  func (b *Backend) IsTimeContinuous() bool {
   191  	panic("timers not supported")
   192  }
   193  
   194  func (b *Backend) Release() {
   195  	for _, state := range b.depthStates {
   196  		d3d11.IUnknownRelease(unsafe.Pointer(state), state.Vtbl.Release)
   197  	}
   198  	for _, state := range b.blendStates {
   199  		d3d11.IUnknownRelease(unsafe.Pointer(state), state.Vtbl.Release)
   200  	}
   201  	d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
   202  	*b = Backend{}
   203  }
   204  
   205  func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
   206  	var d3dfmt uint32
   207  	switch format {
   208  	case driver.TextureFormatFloat:
   209  		d3dfmt = b.floatFormat
   210  	case driver.TextureFormatSRGB:
   211  		d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
   212  	default:
   213  		return nil, fmt.Errorf("unsupported texture format %d", format)
   214  	}
   215  	tex, err := b.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
   216  		Width:     uint32(width),
   217  		Height:    uint32(height),
   218  		MipLevels: 1,
   219  		ArraySize: 1,
   220  		Format:    d3dfmt,
   221  		SampleDesc: d3d11.DXGI_SAMPLE_DESC{
   222  			Count:   1,
   223  			Quality: 0,
   224  		},
   225  		BindFlags: convBufferBinding(bindings),
   226  	})
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  	var (
   231  		sampler *d3d11.SamplerState
   232  		resView *d3d11.ShaderResourceView
   233  	)
   234  	if bindings&driver.BufferBindingTexture != 0 {
   235  		var filter uint32
   236  		switch {
   237  		case minFilter == driver.FilterNearest && magFilter == driver.FilterNearest:
   238  			filter = d3d11.FILTER_MIN_MAG_MIP_POINT
   239  		case minFilter == driver.FilterLinear && magFilter == driver.FilterLinear:
   240  			filter = d3d11.FILTER_MIN_MAG_LINEAR_MIP_POINT
   241  		default:
   242  			d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   243  			return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter)
   244  		}
   245  		var err error
   246  		sampler, err = b.dev.CreateSamplerState(&d3d11.SAMPLER_DESC{
   247  			Filter:        filter,
   248  			AddressU:      d3d11.TEXTURE_ADDRESS_CLAMP,
   249  			AddressV:      d3d11.TEXTURE_ADDRESS_CLAMP,
   250  			AddressW:      d3d11.TEXTURE_ADDRESS_CLAMP,
   251  			MaxAnisotropy: 1,
   252  			MinLOD:        -math.MaxFloat32,
   253  			MaxLOD:        math.MaxFloat32,
   254  		})
   255  		if err != nil {
   256  			d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   257  			return nil, err
   258  		}
   259  		resView, err = b.dev.CreateShaderResourceViewTEX2D(
   260  			(*d3d11.Resource)(unsafe.Pointer(tex)),
   261  			&d3d11.SHADER_RESOURCE_VIEW_DESC_TEX2D{
   262  				SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{
   263  					Format:        d3dfmt,
   264  					ViewDimension: d3d11.SRV_DIMENSION_TEXTURE2D,
   265  				},
   266  				Texture2D: d3d11.TEX2D_SRV{
   267  					MostDetailedMip: 0,
   268  					MipLevels:       ^uint32(0),
   269  				},
   270  			},
   271  		)
   272  		if err != nil {
   273  			d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   274  			d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
   275  			return nil, err
   276  		}
   277  	}
   278  	return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, bindings: bindings, width: width, height: height}, nil
   279  }
   280  
   281  func (b *Backend) NewFramebuffer(tex driver.Texture, depthBits int) (driver.Framebuffer, error) {
   282  	d3dtex := tex.(*Texture)
   283  	if d3dtex.bindings&driver.BufferBindingFramebuffer == 0 {
   284  		return nil, errors.New("the texture was created without BufferBindingFramebuffer binding")
   285  	}
   286  	resource := (*d3d11.Resource)(unsafe.Pointer(d3dtex.tex))
   287  	renderTarget, err := b.dev.CreateRenderTargetView(resource)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  	fbo := &Framebuffer{ctx: b.ctx, dev: b.dev, format: d3dtex.format, resource: resource, renderTarget: renderTarget}
   292  	if depthBits > 0 {
   293  		depthView, err := d3d11.CreateDepthView(b.dev, d3dtex.width, d3dtex.height, depthBits)
   294  		if err != nil {
   295  			d3d11.IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.Vtbl.Release)
   296  			return nil, err
   297  		}
   298  		fbo.depthView = depthView
   299  	}
   300  	return fbo, nil
   301  }
   302  
   303  func (b *Backend) NewInputLayout(vertexShader driver.ShaderSources, layout []driver.InputDesc) (driver.InputLayout, error) {
   304  	if len(vertexShader.Inputs) != len(layout) {
   305  		return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs))
   306  	}
   307  	descs := make([]d3d11.INPUT_ELEMENT_DESC, len(layout))
   308  	for i, l := range layout {
   309  		inp := vertexShader.Inputs[i]
   310  		cname, err := windows.BytePtrFromString(inp.Semantic)
   311  		if err != nil {
   312  			return nil, err
   313  		}
   314  		var format uint32
   315  		switch l.Type {
   316  		case driver.DataTypeFloat:
   317  			switch l.Size {
   318  			case 1:
   319  				format = d3d11.DXGI_FORMAT_R32_FLOAT
   320  			case 2:
   321  				format = d3d11.DXGI_FORMAT_R32G32_FLOAT
   322  			case 3:
   323  				format = d3d11.DXGI_FORMAT_R32G32B32_FLOAT
   324  			case 4:
   325  				format = d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT
   326  			default:
   327  				panic("unsupported data size")
   328  			}
   329  		case driver.DataTypeShort:
   330  			switch l.Size {
   331  			case 1:
   332  				format = d3d11.DXGI_FORMAT_R16_SINT
   333  			case 2:
   334  				format = d3d11.DXGI_FORMAT_R16G16_SINT
   335  			default:
   336  				panic("unsupported data size")
   337  			}
   338  		default:
   339  			panic("unsupported data type")
   340  		}
   341  		descs[i] = d3d11.INPUT_ELEMENT_DESC{
   342  			SemanticName:      cname,
   343  			SemanticIndex:     uint32(inp.SemanticIndex),
   344  			Format:            format,
   345  			AlignedByteOffset: uint32(l.Offset),
   346  		}
   347  	}
   348  	l, err := b.dev.CreateInputLayout(descs, []byte(vertexShader.HLSL))
   349  	if err != nil {
   350  		return nil, err
   351  	}
   352  	return &InputLayout{layout: l}, nil
   353  }
   354  
   355  func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
   356  	if typ&driver.BufferBindingUniforms != 0 {
   357  		if typ != driver.BufferBindingUniforms {
   358  			return nil, errors.New("uniform buffers cannot have other bindings")
   359  		}
   360  		if size%16 != 0 {
   361  			return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", size)
   362  		}
   363  	}
   364  	bind := convBufferBinding(typ)
   365  	buf, err := b.dev.CreateBuffer(&d3d11.BUFFER_DESC{
   366  		ByteWidth: uint32(size),
   367  		BindFlags: bind,
   368  	}, nil)
   369  	if err != nil {
   370  		return nil, err
   371  	}
   372  	return &Buffer{backend: b, buf: buf, bind: bind}, nil
   373  }
   374  
   375  func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
   376  	if typ&driver.BufferBindingUniforms != 0 {
   377  		if typ != driver.BufferBindingUniforms {
   378  			return nil, errors.New("uniform buffers cannot have other bindings")
   379  		}
   380  		if len(data)%16 != 0 {
   381  			return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", len(data))
   382  		}
   383  	}
   384  	bind := convBufferBinding(typ)
   385  	buf, err := b.dev.CreateBuffer(&d3d11.BUFFER_DESC{
   386  		ByteWidth: uint32(len(data)),
   387  		Usage:     d3d11.USAGE_IMMUTABLE,
   388  		BindFlags: bind,
   389  	}, data)
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  	return &Buffer{backend: b, buf: buf, bind: bind, immutable: true}, nil
   394  }
   395  
   396  func (b *Backend) NewComputeProgram(shader driver.ShaderSources) (driver.Program, error) {
   397  	panic("not implemented")
   398  }
   399  
   400  func (b *Backend) NewProgram(vertexShader, fragmentShader driver.ShaderSources) (driver.Program, error) {
   401  	vs, err := b.dev.CreateVertexShader([]byte(vertexShader.HLSL))
   402  	if err != nil {
   403  		return nil, err
   404  	}
   405  	ps, err := b.dev.CreatePixelShader([]byte(fragmentShader.HLSL))
   406  	if err != nil {
   407  		return nil, err
   408  	}
   409  	p := &Program{backend: b}
   410  	p.vert.shader = vs
   411  	p.frag.shader = ps
   412  	return p, nil
   413  }
   414  
   415  func (b *Backend) Clear(colr, colg, colb, cola float32) {
   416  	b.clearColor = [4]float32{colr, colg, colb, cola}
   417  	b.ctx.ClearRenderTargetView(b.fbo.renderTarget, &b.clearColor)
   418  }
   419  
   420  func (b *Backend) ClearDepth(depth float32) {
   421  	if b.fbo.depthView != nil {
   422  		b.ctx.ClearDepthStencilView(b.fbo.depthView, d3d11.CLEAR_DEPTH|d3d11.CLEAR_STENCIL, depth, 0)
   423  	}
   424  }
   425  
   426  func (b *Backend) Viewport(x, y, width, height int) {
   427  	b.viewport = d3d11.VIEWPORT{
   428  		TopLeftX: float32(x),
   429  		TopLeftY: float32(y),
   430  		Width:    float32(width),
   431  		Height:   float32(height),
   432  		MinDepth: 0.0,
   433  		MaxDepth: 1.0,
   434  	}
   435  	b.ctx.RSSetViewports(&b.viewport)
   436  }
   437  
   438  func (b *Backend) DrawArrays(mode driver.DrawMode, off, count int) {
   439  	b.prepareDraw(mode)
   440  	b.ctx.Draw(uint32(count), uint32(off))
   441  }
   442  
   443  func (b *Backend) DrawElements(mode driver.DrawMode, off, count int) {
   444  	b.prepareDraw(mode)
   445  	b.ctx.DrawIndexed(uint32(count), uint32(off), 0)
   446  }
   447  
   448  func (b *Backend) prepareDraw(mode driver.DrawMode) {
   449  	if p := b.prog; p != nil {
   450  		b.ctx.VSSetShader(p.vert.shader)
   451  		b.ctx.PSSetShader(p.frag.shader)
   452  		if buf := p.vert.uniforms; buf != nil {
   453  			b.ctx.VSSetConstantBuffers(buf.buf)
   454  		}
   455  		if buf := p.frag.uniforms; buf != nil {
   456  			b.ctx.PSSetConstantBuffers(buf.buf)
   457  		}
   458  	}
   459  	var topology uint32
   460  	switch mode {
   461  	case driver.DrawModeTriangles:
   462  		topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLELIST
   463  	case driver.DrawModeTriangleStrip:
   464  		topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
   465  	default:
   466  		panic("unsupported draw mode")
   467  	}
   468  	b.ctx.IASetPrimitiveTopology(topology)
   469  
   470  	depthState, ok := b.depthStates[b.depthState]
   471  	if !ok {
   472  		var desc d3d11.DEPTH_STENCIL_DESC
   473  		if b.depthState.enable {
   474  			desc.DepthEnable = 1
   475  		}
   476  		if b.depthState.mask {
   477  			desc.DepthWriteMask = d3d11.DEPTH_WRITE_MASK_ALL
   478  		}
   479  		switch b.depthState.fn {
   480  		case driver.DepthFuncGreater:
   481  			desc.DepthFunc = d3d11.COMPARISON_GREATER
   482  		case driver.DepthFuncGreaterEqual:
   483  			desc.DepthFunc = d3d11.COMPARISON_GREATER_EQUAL
   484  		default:
   485  			panic("unsupported depth func")
   486  		}
   487  		var err error
   488  		depthState, err = b.dev.CreateDepthStencilState(&desc)
   489  		if err != nil {
   490  			panic(err)
   491  		}
   492  		b.depthStates[b.depthState] = depthState
   493  	}
   494  	b.ctx.OMSetDepthStencilState(depthState, 0)
   495  
   496  	blendState, ok := b.blendStates[b.blendState]
   497  	if !ok {
   498  		var desc d3d11.BLEND_DESC
   499  		t0 := &desc.RenderTarget[0]
   500  		t0.RenderTargetWriteMask = d3d11.COLOR_WRITE_ENABLE_ALL
   501  		t0.BlendOp = d3d11.BLEND_OP_ADD
   502  		t0.BlendOpAlpha = d3d11.BLEND_OP_ADD
   503  		if b.blendState.enable {
   504  			t0.BlendEnable = 1
   505  		}
   506  		scol, salpha := toBlendFactor(b.blendState.sfactor)
   507  		dcol, dalpha := toBlendFactor(b.blendState.dfactor)
   508  		t0.SrcBlend = scol
   509  		t0.SrcBlendAlpha = salpha
   510  		t0.DestBlend = dcol
   511  		t0.DestBlendAlpha = dalpha
   512  		var err error
   513  		blendState, err = b.dev.CreateBlendState(&desc)
   514  		if err != nil {
   515  			panic(err)
   516  		}
   517  		b.blendStates[b.blendState] = blendState
   518  	}
   519  	b.ctx.OMSetBlendState(blendState, nil, 0xffffffff)
   520  }
   521  
   522  func (b *Backend) DepthFunc(f driver.DepthFunc) {
   523  	b.depthState.fn = f
   524  }
   525  
   526  func (b *Backend) SetBlend(enable bool) {
   527  	b.blendState.enable = enable
   528  }
   529  
   530  func (b *Backend) SetDepthTest(enable bool) {
   531  	b.depthState.enable = enable
   532  }
   533  
   534  func (b *Backend) DepthMask(mask bool) {
   535  	b.depthState.mask = mask
   536  }
   537  
   538  func (b *Backend) BlendFunc(sfactor, dfactor driver.BlendFactor) {
   539  	b.blendState.sfactor = sfactor
   540  	b.blendState.dfactor = dfactor
   541  }
   542  
   543  func (b *Backend) BindImageTexture(unit int, tex driver.Texture, access driver.AccessBits, f driver.TextureFormat) {
   544  	panic("not implemented")
   545  }
   546  
   547  func (b *Backend) MemoryBarrier() {
   548  	panic("not implemented")
   549  }
   550  
   551  func (b *Backend) DispatchCompute(x, y, z int) {
   552  	panic("not implemented")
   553  }
   554  
   555  func (t *Texture) Upload(offset, size image.Point, pixels []byte) {
   556  	stride := size.X * 4
   557  	dst := &d3d11.BOX{
   558  		Left:   uint32(offset.X),
   559  		Top:    uint32(offset.Y),
   560  		Right:  uint32(offset.X + size.X),
   561  		Bottom: uint32(offset.Y + size.Y),
   562  		Front:  0,
   563  		Back:   1,
   564  	}
   565  	res := (*d3d11.Resource)(unsafe.Pointer(t.tex))
   566  	t.backend.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels)
   567  }
   568  
   569  func (t *Texture) Release() {
   570  	d3d11.IUnknownRelease(unsafe.Pointer(t.tex), t.tex.Vtbl.Release)
   571  	t.tex = nil
   572  	if t.sampler != nil {
   573  		d3d11.IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.Vtbl.Release)
   574  		t.sampler = nil
   575  	}
   576  	if t.resView != nil {
   577  		d3d11.IUnknownRelease(unsafe.Pointer(t.resView), t.resView.Vtbl.Release)
   578  		t.resView = nil
   579  	}
   580  }
   581  
   582  func (b *Backend) BindTexture(unit int, tex driver.Texture) {
   583  	t := tex.(*Texture)
   584  	b.ctx.PSSetSamplers(uint32(unit), t.sampler)
   585  	b.ctx.PSSetShaderResources(uint32(unit), t.resView)
   586  }
   587  
   588  func (b *Backend) BindProgram(prog driver.Program) {
   589  	b.prog = prog.(*Program)
   590  }
   591  
   592  func (p *Program) Release() {
   593  	d3d11.IUnknownRelease(unsafe.Pointer(p.vert.shader), p.vert.shader.Vtbl.Release)
   594  	d3d11.IUnknownRelease(unsafe.Pointer(p.frag.shader), p.frag.shader.Vtbl.Release)
   595  	p.vert.shader = nil
   596  	p.frag.shader = nil
   597  }
   598  
   599  func (p *Program) SetStorageBuffer(binding int, buffer driver.Buffer) {
   600  	panic("not implemented")
   601  }
   602  
   603  func (p *Program) SetVertexUniforms(buf driver.Buffer) {
   604  	p.vert.uniforms = buf.(*Buffer)
   605  }
   606  
   607  func (p *Program) SetFragmentUniforms(buf driver.Buffer) {
   608  	p.frag.uniforms = buf.(*Buffer)
   609  }
   610  
   611  func (b *Backend) BindVertexBuffer(buf driver.Buffer, stride, offset int) {
   612  	b.ctx.IASetVertexBuffers(buf.(*Buffer).buf, uint32(stride), uint32(offset))
   613  }
   614  
   615  func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
   616  	b.ctx.IASetIndexBuffer(buf.(*Buffer).buf, d3d11.DXGI_FORMAT_R16_UINT, 0)
   617  }
   618  
   619  func (b *Buffer) Download(data []byte) error {
   620  	panic("not implemented")
   621  }
   622  
   623  func (b *Buffer) Upload(data []byte) {
   624  	b.backend.ctx.UpdateSubresource((*d3d11.Resource)(unsafe.Pointer(b.buf)), nil, 0, 0, data)
   625  }
   626  
   627  func (b *Buffer) Release() {
   628  	d3d11.IUnknownRelease(unsafe.Pointer(b.buf), b.buf.Vtbl.Release)
   629  	b.buf = nil
   630  }
   631  
   632  func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
   633  	if f.resource == nil {
   634  		return errors.New("framebuffer does not support ReadPixels")
   635  	}
   636  	w, h := src.Dx(), src.Dy()
   637  	tex, err := f.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
   638  		Width:     uint32(w),
   639  		Height:    uint32(h),
   640  		MipLevels: 1,
   641  		ArraySize: 1,
   642  		Format:    f.format,
   643  		SampleDesc: d3d11.DXGI_SAMPLE_DESC{
   644  			Count:   1,
   645  			Quality: 0,
   646  		},
   647  		Usage:          d3d11.USAGE_STAGING,
   648  		CPUAccessFlags: d3d11.CPU_ACCESS_READ,
   649  	})
   650  	if err != nil {
   651  		return fmt.Errorf("ReadPixels: %v", err)
   652  	}
   653  	defer d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   654  	res := (*d3d11.Resource)(unsafe.Pointer(tex))
   655  	f.ctx.CopySubresourceRegion(
   656  		res,
   657  		0,       // Destination subresource.
   658  		0, 0, 0, // Destination coordinates (x, y, z).
   659  		f.resource,
   660  		0, // Source subresource.
   661  		&d3d11.BOX{
   662  			Left:   uint32(src.Min.X),
   663  			Top:    uint32(src.Min.Y),
   664  			Right:  uint32(src.Max.X),
   665  			Bottom: uint32(src.Max.Y),
   666  			Front:  0,
   667  			Back:   1,
   668  		},
   669  	)
   670  	resMap, err := f.ctx.Map(res, 0, d3d11.MAP_READ, 0)
   671  	if err != nil {
   672  		return fmt.Errorf("ReadPixels: %v", err)
   673  	}
   674  	defer f.ctx.Unmap(res, 0)
   675  	srcPitch := w * 4
   676  	dstPitch := int(resMap.RowPitch)
   677  	mapSize := dstPitch * h
   678  	data := sliceOf(resMap.PData, mapSize)
   679  	width := w * 4
   680  	for r := 0; r < h; r++ {
   681  		pixels := pixels[r*srcPitch:]
   682  		copy(pixels[:width], data[r*dstPitch:])
   683  	}
   684  	return nil
   685  }
   686  
   687  func (b *Backend) BindFramebuffer(fbo driver.Framebuffer) {
   688  	b.fbo = fbo.(*Framebuffer)
   689  	b.ctx.OMSetRenderTargets(b.fbo.renderTarget, b.fbo.depthView)
   690  }
   691  
   692  func (f *Framebuffer) Invalidate() {
   693  }
   694  
   695  func (f *Framebuffer) Release() {
   696  	if f.foreign {
   697  		panic("framebuffer not created by NewFramebuffer")
   698  	}
   699  	if f.renderTarget != nil {
   700  		d3d11.IUnknownRelease(unsafe.Pointer(f.renderTarget), f.renderTarget.Vtbl.Release)
   701  		f.renderTarget = nil
   702  	}
   703  	if f.depthView != nil {
   704  		d3d11.IUnknownRelease(unsafe.Pointer(f.depthView), f.depthView.Vtbl.Release)
   705  		f.depthView = nil
   706  	}
   707  }
   708  
   709  func (b *Backend) BindInputLayout(layout driver.InputLayout) {
   710  	b.ctx.IASetInputLayout(layout.(*InputLayout).layout)
   711  }
   712  
   713  func (l *InputLayout) Release() {
   714  	d3d11.IUnknownRelease(unsafe.Pointer(l.layout), l.layout.Vtbl.Release)
   715  	l.layout = nil
   716  }
   717  
   718  func convBufferBinding(typ driver.BufferBinding) uint32 {
   719  	var bindings uint32
   720  	if typ&driver.BufferBindingVertices != 0 {
   721  		bindings |= d3d11.BIND_VERTEX_BUFFER
   722  	}
   723  	if typ&driver.BufferBindingIndices != 0 {
   724  		bindings |= d3d11.BIND_INDEX_BUFFER
   725  	}
   726  	if typ&driver.BufferBindingUniforms != 0 {
   727  		bindings |= d3d11.BIND_CONSTANT_BUFFER
   728  	}
   729  	if typ&driver.BufferBindingTexture != 0 {
   730  		bindings |= d3d11.BIND_SHADER_RESOURCE
   731  	}
   732  	if typ&driver.BufferBindingFramebuffer != 0 {
   733  		bindings |= d3d11.BIND_RENDER_TARGET
   734  	}
   735  	return bindings
   736  }
   737  
   738  func toBlendFactor(f driver.BlendFactor) (uint32, uint32) {
   739  	switch f {
   740  	case driver.BlendFactorOne:
   741  		return d3d11.BLEND_ONE, d3d11.BLEND_ONE
   742  	case driver.BlendFactorOneMinusSrcAlpha:
   743  		return d3d11.BLEND_INV_SRC_ALPHA, d3d11.BLEND_INV_SRC_ALPHA
   744  	case driver.BlendFactorZero:
   745  		return d3d11.BLEND_ZERO, d3d11.BLEND_ZERO
   746  	case driver.BlendFactorDstColor:
   747  		return d3d11.BLEND_DEST_COLOR, d3d11.BLEND_DEST_ALPHA
   748  	default:
   749  		panic("unsupported blend source factor")
   750  	}
   751  }
   752  
   753  // sliceOf returns a slice from a (native) pointer.
   754  func sliceOf(ptr uintptr, cap int) []byte {
   755  	var data []byte
   756  	h := (*reflect.SliceHeader)(unsafe.Pointer(&data))
   757  	h.Data = ptr
   758  	h.Cap = cap
   759  	h.Len = cap
   760  	return data
   761  }