github.com/Seikaijyu/gio@v0.0.1/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/shader" 16 "github.com/Seikaijyu/gio/gpu/internal/driver" 17 "github.com/Seikaijyu/gio/internal/d3d11" 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 }