gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/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  	"math/bits"
    11  	"unsafe"
    12  
    13  	"golang.org/x/sys/windows"
    14  
    15  	"gioui.org/gpu/internal/driver"
    16  	"gioui.org/internal/d3d11"
    17  	"gioui.org/shader"
    18  )
    19  
    20  type Backend struct {
    21  	dev *d3d11.Device
    22  	ctx *d3d11.DeviceContext
    23  
    24  	// Temporary storage to avoid garbage.
    25  	clearColor [4]float32
    26  	viewport   d3d11.VIEWPORT
    27  
    28  	pipeline *Pipeline
    29  	vert     struct {
    30  		buffer *Buffer
    31  		offset int
    32  	}
    33  
    34  	program *Program
    35  
    36  	caps driver.Caps
    37  
    38  	floatFormat uint32
    39  }
    40  
    41  type Pipeline struct {
    42  	vert     *d3d11.VertexShader
    43  	frag     *d3d11.PixelShader
    44  	layout   *d3d11.InputLayout
    45  	blend    *d3d11.BlendState
    46  	stride   int
    47  	topology driver.Topology
    48  }
    49  
    50  type Texture struct {
    51  	backend      *Backend
    52  	format       uint32
    53  	bindings     driver.BufferBinding
    54  	tex          *d3d11.Texture2D
    55  	sampler      *d3d11.SamplerState
    56  	resView      *d3d11.ShaderResourceView
    57  	uaView       *d3d11.UnorderedAccessView
    58  	renderTarget *d3d11.RenderTargetView
    59  
    60  	width   int
    61  	height  int
    62  	mipmap  bool
    63  	foreign bool
    64  }
    65  
    66  type VertexShader struct {
    67  	backend *Backend
    68  	shader  *d3d11.VertexShader
    69  	src     shader.Sources
    70  }
    71  
    72  type FragmentShader struct {
    73  	backend *Backend
    74  	shader  *d3d11.PixelShader
    75  }
    76  
    77  type Program struct {
    78  	backend *Backend
    79  	shader  *d3d11.ComputeShader
    80  }
    81  
    82  type Buffer struct {
    83  	backend   *Backend
    84  	bind      uint32
    85  	buf       *d3d11.Buffer
    86  	resView   *d3d11.ShaderResourceView
    87  	uaView    *d3d11.UnorderedAccessView
    88  	size      int
    89  	immutable bool
    90  }
    91  
    92  func init() {
    93  	driver.NewDirect3D11Device = newDirect3D11Device
    94  }
    95  
    96  func detectFloatFormat(dev *d3d11.Device) (uint32, bool) {
    97  	formats := []uint32{
    98  		d3d11.DXGI_FORMAT_R16_FLOAT,
    99  		d3d11.DXGI_FORMAT_R32_FLOAT,
   100  		d3d11.DXGI_FORMAT_R16G16_FLOAT,
   101  		d3d11.DXGI_FORMAT_R32G32_FLOAT,
   102  		// These last two are really wasteful, but c'est la vie.
   103  		d3d11.DXGI_FORMAT_R16G16B16A16_FLOAT,
   104  		d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT,
   105  	}
   106  	for _, format := range formats {
   107  		need := uint32(d3d11.FORMAT_SUPPORT_TEXTURE2D | d3d11.FORMAT_SUPPORT_RENDER_TARGET)
   108  		if support, _ := dev.CheckFormatSupport(format); support&need == need {
   109  			return format, true
   110  		}
   111  	}
   112  	return 0, false
   113  }
   114  
   115  func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
   116  	dev := (*d3d11.Device)(api.Device)
   117  	b := &Backend{
   118  		dev: dev,
   119  		ctx: dev.GetImmediateContext(),
   120  		caps: driver.Caps{
   121  			MaxTextureSize: 2048, // 9.1 maximum
   122  			Features:       driver.FeatureSRGB,
   123  		},
   124  	}
   125  	featLvl := dev.GetFeatureLevel()
   126  	switch {
   127  	case featLvl < d3d11.FEATURE_LEVEL_9_1:
   128  		d3d11.IUnknownRelease(unsafe.Pointer(dev), dev.Vtbl.Release)
   129  		d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
   130  		return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl)
   131  	case featLvl >= d3d11.FEATURE_LEVEL_11_0:
   132  		b.caps.MaxTextureSize = 16384
   133  		b.caps.Features |= driver.FeatureCompute
   134  	case featLvl >= d3d11.FEATURE_LEVEL_9_3:
   135  		b.caps.MaxTextureSize = 4096
   136  	}
   137  	if fmt, ok := detectFloatFormat(dev); ok {
   138  		b.floatFormat = fmt
   139  		b.caps.Features |= driver.FeatureFloatRenderTargets
   140  	}
   141  	// Disable backface culling to match OpenGL.
   142  	state, err := dev.CreateRasterizerState(&d3d11.RASTERIZER_DESC{
   143  		CullMode: d3d11.CULL_NONE,
   144  		FillMode: d3d11.FILL_SOLID,
   145  	})
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  	defer d3d11.IUnknownRelease(unsafe.Pointer(state), state.Vtbl.Release)
   150  	b.ctx.RSSetState(state)
   151  	return b, nil
   152  }
   153  
   154  func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
   155  	var (
   156  		renderTarget *d3d11.RenderTargetView
   157  	)
   158  	if target != nil {
   159  		switch t := target.(type) {
   160  		case driver.Direct3D11RenderTarget:
   161  			renderTarget = (*d3d11.RenderTargetView)(t.RenderTarget)
   162  		case *Texture:
   163  			renderTarget = t.renderTarget
   164  		default:
   165  			panic(fmt.Errorf("d3d11: invalid render target type: %T", target))
   166  		}
   167  	}
   168  	b.ctx.OMSetRenderTargets(renderTarget, nil)
   169  	return &Texture{backend: b, renderTarget: renderTarget, foreign: true}
   170  }
   171  
   172  func (b *Backend) CopyTexture(dstTex driver.Texture, dstOrigin image.Point, srcTex driver.Texture, srcRect image.Rectangle) {
   173  	dst := (*d3d11.Resource)(unsafe.Pointer(dstTex.(*Texture).tex))
   174  	src := (*d3d11.Resource)(srcTex.(*Texture).tex)
   175  	b.ctx.CopySubresourceRegion(
   176  		dst,
   177  		0,                                           // Destination subresource.
   178  		uint32(dstOrigin.X), uint32(dstOrigin.Y), 0, // Destination coordinates (x, y, z).
   179  		src,
   180  		0, // Source subresource.
   181  		&d3d11.BOX{
   182  			Left:   uint32(srcRect.Min.X),
   183  			Top:    uint32(srcRect.Min.Y),
   184  			Right:  uint32(srcRect.Max.X),
   185  			Bottom: uint32(srcRect.Max.Y),
   186  			Front:  0,
   187  			Back:   1,
   188  		},
   189  	)
   190  }
   191  
   192  func (b *Backend) EndFrame() {
   193  }
   194  
   195  func (b *Backend) Caps() driver.Caps {
   196  	return b.caps
   197  }
   198  
   199  func (b *Backend) NewTimer() driver.Timer {
   200  	panic("timers not supported")
   201  }
   202  
   203  func (b *Backend) IsTimeContinuous() bool {
   204  	panic("timers not supported")
   205  }
   206  
   207  func (b *Backend) Release() {
   208  	d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
   209  	*b = Backend{}
   210  }
   211  
   212  func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
   213  	var d3dfmt uint32
   214  	switch format {
   215  	case driver.TextureFormatFloat:
   216  		d3dfmt = b.floatFormat
   217  	case driver.TextureFormatSRGBA:
   218  		d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
   219  	case driver.TextureFormatRGBA8:
   220  		d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM
   221  	default:
   222  		return nil, fmt.Errorf("unsupported texture format %d", format)
   223  	}
   224  	bindFlags := convBufferBinding(bindings)
   225  	miscFlags := uint32(0)
   226  	mipmap := minFilter == driver.FilterLinearMipmapLinear
   227  	nmipmaps := 1
   228  	if mipmap {
   229  		// Flags required by ID3D11DeviceContext::GenerateMips.
   230  		bindFlags |= d3d11.BIND_SHADER_RESOURCE | d3d11.BIND_RENDER_TARGET
   231  		miscFlags |= d3d11.RESOURCE_MISC_GENERATE_MIPS
   232  		dim := width
   233  		if height > dim {
   234  			dim = height
   235  		}
   236  		log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1
   237  		nmipmaps = log2 + 1
   238  	}
   239  	tex, err := b.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
   240  		Width:     uint32(width),
   241  		Height:    uint32(height),
   242  		MipLevels: uint32(nmipmaps),
   243  		ArraySize: 1,
   244  		Format:    d3dfmt,
   245  		SampleDesc: d3d11.DXGI_SAMPLE_DESC{
   246  			Count:   1,
   247  			Quality: 0,
   248  		},
   249  		BindFlags: bindFlags,
   250  		MiscFlags: miscFlags,
   251  	})
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	var (
   256  		sampler *d3d11.SamplerState
   257  		resView *d3d11.ShaderResourceView
   258  		uaView  *d3d11.UnorderedAccessView
   259  		fbo     *d3d11.RenderTargetView
   260  	)
   261  	if bindings&driver.BufferBindingTexture != 0 {
   262  		var filter uint32
   263  		switch {
   264  		case minFilter == driver.FilterNearest && magFilter == driver.FilterNearest:
   265  			filter = d3d11.FILTER_MIN_MAG_MIP_POINT
   266  		case minFilter == driver.FilterLinear && magFilter == driver.FilterLinear:
   267  			filter = d3d11.FILTER_MIN_MAG_LINEAR_MIP_POINT
   268  		case minFilter == driver.FilterLinearMipmapLinear && magFilter == driver.FilterLinear:
   269  			filter = d3d11.FILTER_MIN_MAG_MIP_LINEAR
   270  		default:
   271  			d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   272  			return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter)
   273  		}
   274  		var err error
   275  		sampler, err = b.dev.CreateSamplerState(&d3d11.SAMPLER_DESC{
   276  			Filter:        filter,
   277  			AddressU:      d3d11.TEXTURE_ADDRESS_CLAMP,
   278  			AddressV:      d3d11.TEXTURE_ADDRESS_CLAMP,
   279  			AddressW:      d3d11.TEXTURE_ADDRESS_CLAMP,
   280  			MaxAnisotropy: 1,
   281  			MinLOD:        -math.MaxFloat32,
   282  			MaxLOD:        math.MaxFloat32,
   283  		})
   284  		if err != nil {
   285  			d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   286  			return nil, err
   287  		}
   288  		resView, err = b.dev.CreateShaderResourceView(
   289  			(*d3d11.Resource)(unsafe.Pointer(tex)),
   290  			unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_TEX2D{
   291  				SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{
   292  					Format:        d3dfmt,
   293  					ViewDimension: d3d11.SRV_DIMENSION_TEXTURE2D,
   294  				},
   295  				Texture2D: d3d11.TEX2D_SRV{
   296  					MostDetailedMip: 0,
   297  					MipLevels:       ^uint32(0),
   298  				},
   299  			}),
   300  		)
   301  		if err != nil {
   302  			d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   303  			d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
   304  			return nil, err
   305  		}
   306  	}
   307  	if bindings&driver.BufferBindingShaderStorageWrite != 0 {
   308  		uaView, err = b.dev.CreateUnorderedAccessView(
   309  			(*d3d11.Resource)(unsafe.Pointer(tex)),
   310  			unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_TEX2D{
   311  				UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{
   312  					Format:        d3dfmt,
   313  					ViewDimension: d3d11.UAV_DIMENSION_TEXTURE2D,
   314  				},
   315  				Texture2D: d3d11.TEX2D_UAV{
   316  					MipSlice: 0,
   317  				},
   318  			}),
   319  		)
   320  		if err != nil {
   321  			if sampler != nil {
   322  				d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
   323  			}
   324  			if resView != nil {
   325  				d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release)
   326  			}
   327  			d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   328  			return nil, err
   329  		}
   330  	}
   331  	if bindings&driver.BufferBindingFramebuffer != 0 {
   332  		resource := (*d3d11.Resource)(unsafe.Pointer(tex))
   333  		fbo, err = b.dev.CreateRenderTargetView(resource)
   334  		if err != nil {
   335  			if uaView != nil {
   336  				d3d11.IUnknownRelease(unsafe.Pointer(uaView), uaView.Vtbl.Release)
   337  			}
   338  			if sampler != nil {
   339  				d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
   340  			}
   341  			if resView != nil {
   342  				d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release)
   343  			}
   344  			d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   345  			return nil, err
   346  		}
   347  	}
   348  	return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, uaView: uaView, renderTarget: fbo, bindings: bindings, width: width, height: height, mipmap: mipmap}, nil
   349  }
   350  
   351  func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []driver.InputDesc) (*d3d11.InputLayout, error) {
   352  	if len(vertexShader.Inputs) != len(layout) {
   353  		return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs))
   354  	}
   355  	descs := make([]d3d11.INPUT_ELEMENT_DESC, len(layout))
   356  	for i, l := range layout {
   357  		inp := vertexShader.Inputs[i]
   358  		cname, err := windows.BytePtrFromString(inp.Semantic)
   359  		if err != nil {
   360  			return nil, err
   361  		}
   362  		var format uint32
   363  		switch l.Type {
   364  		case shader.DataTypeFloat:
   365  			switch l.Size {
   366  			case 1:
   367  				format = d3d11.DXGI_FORMAT_R32_FLOAT
   368  			case 2:
   369  				format = d3d11.DXGI_FORMAT_R32G32_FLOAT
   370  			case 3:
   371  				format = d3d11.DXGI_FORMAT_R32G32B32_FLOAT
   372  			case 4:
   373  				format = d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT
   374  			default:
   375  				panic("unsupported data size")
   376  			}
   377  		case shader.DataTypeShort:
   378  			switch l.Size {
   379  			case 1:
   380  				format = d3d11.DXGI_FORMAT_R16_SINT
   381  			case 2:
   382  				format = d3d11.DXGI_FORMAT_R16G16_SINT
   383  			default:
   384  				panic("unsupported data size")
   385  			}
   386  		default:
   387  			panic("unsupported data type")
   388  		}
   389  		descs[i] = d3d11.INPUT_ELEMENT_DESC{
   390  			SemanticName:      cname,
   391  			SemanticIndex:     uint32(inp.SemanticIndex),
   392  			Format:            format,
   393  			AlignedByteOffset: uint32(l.Offset),
   394  		}
   395  	}
   396  	return b.dev.CreateInputLayout(descs, []byte(vertexShader.DXBC))
   397  }
   398  
   399  func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
   400  	return b.newBuffer(typ, size, nil, false)
   401  }
   402  
   403  func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
   404  	return b.newBuffer(typ, len(data), data, true)
   405  }
   406  
   407  func (b *Backend) newBuffer(typ driver.BufferBinding, size int, data []byte, immutable bool) (*Buffer, error) {
   408  	if typ&driver.BufferBindingUniforms != 0 {
   409  		if typ != driver.BufferBindingUniforms {
   410  			return nil, errors.New("uniform buffers cannot have other bindings")
   411  		}
   412  		if size%16 != 0 {
   413  			return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", size)
   414  		}
   415  	}
   416  	bind := convBufferBinding(typ)
   417  	var usage, miscFlags, cpuFlags uint32
   418  	if immutable {
   419  		usage = d3d11.USAGE_IMMUTABLE
   420  	}
   421  	if typ&driver.BufferBindingShaderStorageWrite != 0 {
   422  		cpuFlags = d3d11.CPU_ACCESS_READ
   423  	}
   424  	if typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 {
   425  		miscFlags |= d3d11.RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS
   426  	}
   427  	buf, err := b.dev.CreateBuffer(&d3d11.BUFFER_DESC{
   428  		ByteWidth:      uint32(size),
   429  		Usage:          usage,
   430  		BindFlags:      bind,
   431  		CPUAccessFlags: cpuFlags,
   432  		MiscFlags:      miscFlags,
   433  	}, data)
   434  	if err != nil {
   435  		return nil, err
   436  	}
   437  	var (
   438  		resView *d3d11.ShaderResourceView
   439  		uaView  *d3d11.UnorderedAccessView
   440  	)
   441  	if typ&driver.BufferBindingShaderStorageWrite != 0 {
   442  		uaView, err = b.dev.CreateUnorderedAccessView(
   443  			(*d3d11.Resource)(unsafe.Pointer(buf)),
   444  			unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_BUFFER{
   445  				UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{
   446  					Format:        d3d11.DXGI_FORMAT_R32_TYPELESS,
   447  					ViewDimension: d3d11.UAV_DIMENSION_BUFFER,
   448  				},
   449  				Buffer: d3d11.BUFFER_UAV{
   450  					FirstElement: 0,
   451  					NumElements:  uint32(size / 4),
   452  					Flags:        d3d11.BUFFER_UAV_FLAG_RAW,
   453  				},
   454  			}),
   455  		)
   456  		if err != nil {
   457  			d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release)
   458  			return nil, err
   459  		}
   460  	} else if typ&driver.BufferBindingShaderStorageRead != 0 {
   461  		resView, err = b.dev.CreateShaderResourceView(
   462  			(*d3d11.Resource)(unsafe.Pointer(buf)),
   463  			unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_BUFFEREX{
   464  				SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{
   465  					Format:        d3d11.DXGI_FORMAT_R32_TYPELESS,
   466  					ViewDimension: d3d11.SRV_DIMENSION_BUFFEREX,
   467  				},
   468  				Buffer: d3d11.BUFFEREX_SRV{
   469  					FirstElement: 0,
   470  					NumElements:  uint32(size / 4),
   471  					Flags:        d3d11.BUFFEREX_SRV_FLAG_RAW,
   472  				},
   473  			}),
   474  		)
   475  		if err != nil {
   476  			d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release)
   477  			return nil, err
   478  		}
   479  	}
   480  	return &Buffer{backend: b, buf: buf, bind: bind, size: size, resView: resView, uaView: uaView, immutable: immutable}, nil
   481  }
   482  
   483  func (b *Backend) NewComputeProgram(shader shader.Sources) (driver.Program, error) {
   484  	cs, err := b.dev.CreateComputeShader([]byte(shader.DXBC))
   485  	if err != nil {
   486  		return nil, err
   487  	}
   488  	return &Program{backend: b, shader: cs}, nil
   489  }
   490  
   491  func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
   492  	vsh := desc.VertexShader.(*VertexShader)
   493  	fsh := desc.FragmentShader.(*FragmentShader)
   494  	blend, err := b.newBlendState(desc.BlendDesc)
   495  	if err != nil {
   496  		return nil, err
   497  	}
   498  	var layout *d3d11.InputLayout
   499  	if l := desc.VertexLayout; l.Stride > 0 {
   500  		var err error
   501  		layout, err = b.newInputLayout(vsh.src, l.Inputs)
   502  		if err != nil {
   503  			d3d11.IUnknownRelease(unsafe.Pointer(blend), blend.Vtbl.AddRef)
   504  			return nil, err
   505  		}
   506  	}
   507  
   508  	// Retain shaders.
   509  	vshRef := vsh.shader
   510  	fshRef := fsh.shader
   511  	d3d11.IUnknownAddRef(unsafe.Pointer(vshRef), vshRef.Vtbl.AddRef)
   512  	d3d11.IUnknownAddRef(unsafe.Pointer(fshRef), fshRef.Vtbl.AddRef)
   513  
   514  	return &Pipeline{
   515  		vert:     vshRef,
   516  		frag:     fshRef,
   517  		layout:   layout,
   518  		stride:   desc.VertexLayout.Stride,
   519  		blend:    blend,
   520  		topology: desc.Topology,
   521  	}, nil
   522  }
   523  
   524  func (b *Backend) newBlendState(desc driver.BlendDesc) (*d3d11.BlendState, error) {
   525  	var d3ddesc d3d11.BLEND_DESC
   526  	t0 := &d3ddesc.RenderTarget[0]
   527  	t0.RenderTargetWriteMask = d3d11.COLOR_WRITE_ENABLE_ALL
   528  	t0.BlendOp = d3d11.BLEND_OP_ADD
   529  	t0.BlendOpAlpha = d3d11.BLEND_OP_ADD
   530  	if desc.Enable {
   531  		t0.BlendEnable = 1
   532  	}
   533  	scol, salpha := toBlendFactor(desc.SrcFactor)
   534  	dcol, dalpha := toBlendFactor(desc.DstFactor)
   535  	t0.SrcBlend = scol
   536  	t0.SrcBlendAlpha = salpha
   537  	t0.DestBlend = dcol
   538  	t0.DestBlendAlpha = dalpha
   539  	return b.dev.CreateBlendState(&d3ddesc)
   540  }
   541  
   542  func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
   543  	vs, err := b.dev.CreateVertexShader([]byte(src.DXBC))
   544  	if err != nil {
   545  		return nil, err
   546  	}
   547  	return &VertexShader{b, vs, src}, nil
   548  }
   549  
   550  func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
   551  	fs, err := b.dev.CreatePixelShader([]byte(src.DXBC))
   552  	if err != nil {
   553  		return nil, err
   554  	}
   555  	return &FragmentShader{b, fs}, nil
   556  }
   557  
   558  func (b *Backend) Viewport(x, y, width, height int) {
   559  	b.viewport = d3d11.VIEWPORT{
   560  		TopLeftX: float32(x),
   561  		TopLeftY: float32(y),
   562  		Width:    float32(width),
   563  		Height:   float32(height),
   564  		MinDepth: 0.0,
   565  		MaxDepth: 1.0,
   566  	}
   567  	b.ctx.RSSetViewports(&b.viewport)
   568  }
   569  
   570  func (b *Backend) DrawArrays(off, count int) {
   571  	b.prepareDraw()
   572  	b.ctx.Draw(uint32(count), uint32(off))
   573  }
   574  
   575  func (b *Backend) DrawElements(off, count int) {
   576  	b.prepareDraw()
   577  	b.ctx.DrawIndexed(uint32(count), uint32(off), 0)
   578  }
   579  
   580  func (b *Backend) prepareDraw() {
   581  	p := b.pipeline
   582  	if p == nil {
   583  		return
   584  	}
   585  	b.ctx.VSSetShader(p.vert)
   586  	b.ctx.PSSetShader(p.frag)
   587  	b.ctx.IASetInputLayout(p.layout)
   588  	b.ctx.OMSetBlendState(p.blend, nil, 0xffffffff)
   589  	if b.vert.buffer != nil {
   590  		b.ctx.IASetVertexBuffers(b.vert.buffer.buf, uint32(p.stride), uint32(b.vert.offset))
   591  	}
   592  	var topology uint32
   593  	switch p.topology {
   594  	case driver.TopologyTriangles:
   595  		topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLELIST
   596  	case driver.TopologyTriangleStrip:
   597  		topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
   598  	default:
   599  		panic("unsupported draw mode")
   600  	}
   601  	b.ctx.IASetPrimitiveTopology(topology)
   602  }
   603  
   604  func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
   605  	t := tex.(*Texture)
   606  	if t.uaView != nil {
   607  		b.ctx.CSSetUnorderedAccessViews(uint32(unit), t.uaView)
   608  	} else {
   609  		b.ctx.CSSetShaderResources(uint32(unit), t.resView)
   610  	}
   611  }
   612  
   613  func (b *Backend) DispatchCompute(x, y, z int) {
   614  	b.ctx.CSSetShader(b.program.shader)
   615  	b.ctx.Dispatch(uint32(x), uint32(y), uint32(z))
   616  }
   617  
   618  func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
   619  	if stride == 0 {
   620  		stride = size.X * 4
   621  	}
   622  	dst := &d3d11.BOX{
   623  		Left:   uint32(offset.X),
   624  		Top:    uint32(offset.Y),
   625  		Right:  uint32(offset.X + size.X),
   626  		Bottom: uint32(offset.Y + size.Y),
   627  		Front:  0,
   628  		Back:   1,
   629  	}
   630  	res := (*d3d11.Resource)(unsafe.Pointer(t.tex))
   631  	t.backend.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels)
   632  	if t.mipmap {
   633  		t.backend.ctx.GenerateMips(t.resView)
   634  	}
   635  }
   636  
   637  func (t *Texture) Release() {
   638  	if t.foreign {
   639  		panic("texture not created by NewTexture")
   640  	}
   641  	if t.renderTarget != nil {
   642  		d3d11.IUnknownRelease(unsafe.Pointer(t.renderTarget), t.renderTarget.Vtbl.Release)
   643  	}
   644  	if t.sampler != nil {
   645  		d3d11.IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.Vtbl.Release)
   646  	}
   647  	if t.resView != nil {
   648  		d3d11.IUnknownRelease(unsafe.Pointer(t.resView), t.resView.Vtbl.Release)
   649  	}
   650  	if t.uaView != nil {
   651  		d3d11.IUnknownRelease(unsafe.Pointer(t.uaView), t.uaView.Vtbl.Release)
   652  	}
   653  	d3d11.IUnknownRelease(unsafe.Pointer(t.tex), t.tex.Vtbl.Release)
   654  	*t = Texture{}
   655  }
   656  
   657  func (b *Backend) PrepareTexture(tex driver.Texture) {}
   658  
   659  func (b *Backend) BindTexture(unit int, tex driver.Texture) {
   660  	t := tex.(*Texture)
   661  	b.ctx.PSSetSamplers(uint32(unit), t.sampler)
   662  	b.ctx.PSSetShaderResources(uint32(unit), t.resView)
   663  }
   664  
   665  func (b *Backend) BindPipeline(pipe driver.Pipeline) {
   666  	b.pipeline = pipe.(*Pipeline)
   667  }
   668  
   669  func (b *Backend) BindProgram(prog driver.Program) {
   670  	b.program = prog.(*Program)
   671  }
   672  
   673  func (s *VertexShader) Release() {
   674  	d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
   675  	*s = VertexShader{}
   676  }
   677  
   678  func (s *FragmentShader) Release() {
   679  	d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
   680  	*s = FragmentShader{}
   681  }
   682  
   683  func (s *Program) Release() {
   684  	d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
   685  	*s = Program{}
   686  }
   687  
   688  func (p *Pipeline) Release() {
   689  	d3d11.IUnknownRelease(unsafe.Pointer(p.vert), p.vert.Vtbl.Release)
   690  	d3d11.IUnknownRelease(unsafe.Pointer(p.frag), p.frag.Vtbl.Release)
   691  	d3d11.IUnknownRelease(unsafe.Pointer(p.blend), p.blend.Vtbl.Release)
   692  	if l := p.layout; l != nil {
   693  		d3d11.IUnknownRelease(unsafe.Pointer(l), l.Vtbl.Release)
   694  	}
   695  	*p = Pipeline{}
   696  }
   697  
   698  func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
   699  	buf := buffer.(*Buffer)
   700  	if buf.resView != nil {
   701  		b.ctx.CSSetShaderResources(uint32(binding), buf.resView)
   702  	} else {
   703  		b.ctx.CSSetUnorderedAccessViews(uint32(binding), buf.uaView)
   704  	}
   705  }
   706  
   707  func (b *Backend) BindUniforms(buffer driver.Buffer) {
   708  	buf := buffer.(*Buffer)
   709  	b.ctx.VSSetConstantBuffers(buf.buf)
   710  	b.ctx.PSSetConstantBuffers(buf.buf)
   711  }
   712  
   713  func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
   714  	b.vert.buffer = buf.(*Buffer)
   715  	b.vert.offset = offset
   716  }
   717  
   718  func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
   719  	b.ctx.IASetIndexBuffer(buf.(*Buffer).buf, d3d11.DXGI_FORMAT_R16_UINT, 0)
   720  }
   721  
   722  func (b *Buffer) Download(dst []byte) error {
   723  	res := (*d3d11.Resource)(unsafe.Pointer(b.buf))
   724  	resMap, err := b.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0)
   725  	if err != nil {
   726  		return fmt.Errorf("d3d11: %v", err)
   727  	}
   728  	defer b.backend.ctx.Unmap(res, 0)
   729  	data := sliceOf(resMap.PData, len(dst))
   730  	copy(dst, data)
   731  	return nil
   732  }
   733  
   734  func (b *Buffer) Upload(data []byte) {
   735  	var dst *d3d11.BOX
   736  	if len(data) < b.size {
   737  		dst = &d3d11.BOX{
   738  			Left:   0,
   739  			Right:  uint32(len(data)),
   740  			Top:    0,
   741  			Bottom: 1,
   742  			Front:  0,
   743  			Back:   1,
   744  		}
   745  	}
   746  	b.backend.ctx.UpdateSubresource((*d3d11.Resource)(unsafe.Pointer(b.buf)), dst, 0, 0, data)
   747  }
   748  
   749  func (b *Buffer) Release() {
   750  	if b.resView != nil {
   751  		d3d11.IUnknownRelease(unsafe.Pointer(b.resView), b.resView.Vtbl.Release)
   752  	}
   753  	if b.uaView != nil {
   754  		d3d11.IUnknownRelease(unsafe.Pointer(b.uaView), b.uaView.Vtbl.Release)
   755  	}
   756  	d3d11.IUnknownRelease(unsafe.Pointer(b.buf), b.buf.Vtbl.Release)
   757  	*b = Buffer{}
   758  }
   759  
   760  func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
   761  	w, h := src.Dx(), src.Dy()
   762  	tex, err := t.backend.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
   763  		Width:     uint32(w),
   764  		Height:    uint32(h),
   765  		MipLevels: 1,
   766  		ArraySize: 1,
   767  		Format:    t.format,
   768  		SampleDesc: d3d11.DXGI_SAMPLE_DESC{
   769  			Count:   1,
   770  			Quality: 0,
   771  		},
   772  		Usage:          d3d11.USAGE_STAGING,
   773  		CPUAccessFlags: d3d11.CPU_ACCESS_READ,
   774  	})
   775  	if err != nil {
   776  		return fmt.Errorf("ReadPixels: %v", err)
   777  	}
   778  	defer d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
   779  	res := (*d3d11.Resource)(unsafe.Pointer(tex))
   780  	t.backend.ctx.CopySubresourceRegion(
   781  		res,
   782  		0,       // Destination subresource.
   783  		0, 0, 0, // Destination coordinates (x, y, z).
   784  		(*d3d11.Resource)(t.tex),
   785  		0, // Source subresource.
   786  		&d3d11.BOX{
   787  			Left:   uint32(src.Min.X),
   788  			Top:    uint32(src.Min.Y),
   789  			Right:  uint32(src.Max.X),
   790  			Bottom: uint32(src.Max.Y),
   791  			Front:  0,
   792  			Back:   1,
   793  		},
   794  	)
   795  	resMap, err := t.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0)
   796  	if err != nil {
   797  		return fmt.Errorf("ReadPixels: %v", err)
   798  	}
   799  	defer t.backend.ctx.Unmap(res, 0)
   800  	srcPitch := stride
   801  	dstPitch := int(resMap.RowPitch)
   802  	mapSize := dstPitch * h
   803  	data := sliceOf(resMap.PData, mapSize)
   804  	width := w * 4
   805  	for r := 0; r < h; r++ {
   806  		pixels := pixels[r*srcPitch:]
   807  		copy(pixels[:width], data[r*dstPitch:])
   808  	}
   809  	return nil
   810  }
   811  
   812  func (b *Backend) BeginCompute() {
   813  }
   814  
   815  func (b *Backend) EndCompute() {
   816  }
   817  
   818  func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
   819  	t := tex.(*Texture)
   820  	b.ctx.OMSetRenderTargets(t.renderTarget, nil)
   821  	if d.Action == driver.LoadActionClear {
   822  		c := d.ClearColor
   823  		b.clearColor = [4]float32{c.R, c.G, c.B, c.A}
   824  		b.ctx.ClearRenderTargetView(t.renderTarget, &b.clearColor)
   825  	}
   826  }
   827  
   828  func (b *Backend) EndRenderPass() {
   829  }
   830  
   831  func (f *Texture) ImplementsRenderTarget() {}
   832  
   833  func convBufferBinding(typ driver.BufferBinding) uint32 {
   834  	var bindings uint32
   835  	if typ&driver.BufferBindingVertices != 0 {
   836  		bindings |= d3d11.BIND_VERTEX_BUFFER
   837  	}
   838  	if typ&driver.BufferBindingIndices != 0 {
   839  		bindings |= d3d11.BIND_INDEX_BUFFER
   840  	}
   841  	if typ&driver.BufferBindingUniforms != 0 {
   842  		bindings |= d3d11.BIND_CONSTANT_BUFFER
   843  	}
   844  	if typ&driver.BufferBindingTexture != 0 {
   845  		bindings |= d3d11.BIND_SHADER_RESOURCE
   846  	}
   847  	if typ&driver.BufferBindingFramebuffer != 0 {
   848  		bindings |= d3d11.BIND_RENDER_TARGET
   849  	}
   850  	if typ&driver.BufferBindingShaderStorageWrite != 0 {
   851  		bindings |= d3d11.BIND_UNORDERED_ACCESS
   852  	} else if typ&driver.BufferBindingShaderStorageRead != 0 {
   853  		bindings |= d3d11.BIND_SHADER_RESOURCE
   854  	}
   855  	return bindings
   856  }
   857  
   858  func toBlendFactor(f driver.BlendFactor) (uint32, uint32) {
   859  	switch f {
   860  	case driver.BlendFactorOne:
   861  		return d3d11.BLEND_ONE, d3d11.BLEND_ONE
   862  	case driver.BlendFactorOneMinusSrcAlpha:
   863  		return d3d11.BLEND_INV_SRC_ALPHA, d3d11.BLEND_INV_SRC_ALPHA
   864  	case driver.BlendFactorZero:
   865  		return d3d11.BLEND_ZERO, d3d11.BLEND_ZERO
   866  	case driver.BlendFactorDstColor:
   867  		return d3d11.BLEND_DEST_COLOR, d3d11.BLEND_DEST_ALPHA
   868  	default:
   869  		panic("unsupported blend source factor")
   870  	}
   871  }
   872  
   873  // sliceOf returns a slice from a (native) pointer.
   874  func sliceOf(ptr uintptr, cap int) []byte {
   875  	return unsafe.Slice((*byte)(unsafe.Pointer(ptr)), cap)
   876  }