internal/graphicsdriver/directx: refactoring

This is a preparation for a DirectX 11 driver.

Updates #2613
This commit is contained in:
Hajime Hoshi 2023-03-27 14:14:22 +09:00
parent 4779bbc04d
commit c8a2e5dc71
7 changed files with 140 additions and 120 deletions

View File

@ -726,7 +726,7 @@ type _D3D12_ROOT_SIGNATURE_DESC struct {
} }
type _D3D12_SHADER_BYTECODE struct { type _D3D12_SHADER_BYTECODE struct {
pShaderBytecode uintptr pShaderBytecode unsafe.Pointer
BytecodeLength uintptr BytecodeLength uintptr
} }
@ -1507,10 +1507,10 @@ func (i *_ID3D12Device) CreateRenderTargetView(pResource *_ID3D12Resource, pDesc
runtime.KeepAlive(pDesc) runtime.KeepAlive(pDesc)
} }
func (i *_ID3D12Device) CreateRootSignature(nodeMask uint32, pBlobWithRootSignature uintptr, blobLengthInBytes uintptr) (*_ID3D12RootSignature, error) { func (i *_ID3D12Device) CreateRootSignature(nodeMask uint32, pBlobWithRootSignature unsafe.Pointer, blobLengthInBytes uintptr) (*_ID3D12RootSignature, error) {
var signature *_ID3D12RootSignature var signature *_ID3D12RootSignature
r, _, _ := syscall.Syscall6(i.vtbl.CreateRootSignature, 6, uintptr(unsafe.Pointer(i)), r, _, _ := syscall.Syscall6(i.vtbl.CreateRootSignature, 6, uintptr(unsafe.Pointer(i)),
uintptr(nodeMask), pBlobWithRootSignature, blobLengthInBytes, uintptr(nodeMask), uintptr(pBlobWithRootSignature), blobLengthInBytes,
uintptr(unsafe.Pointer(&_IID_ID3D12RootSignature)), uintptr(unsafe.Pointer(&signature))) uintptr(unsafe.Pointer(&_IID_ID3D12RootSignature)), uintptr(unsafe.Pointer(&signature)))
if uint32(r) != uint32(windows.S_OK) { if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: ID3D12Device::CreateRootSignature failed: %w", handleError(windows.Handle(uint32(r)))) return nil, fmt.Errorf("directx: ID3D12Device::CreateRootSignature failed: %w", handleError(windows.Handle(uint32(r))))

View File

@ -112,10 +112,10 @@ func (i *_ID3DBlob) AddRef() uint32 {
return uint32(r) return uint32(r)
} }
func (i *_ID3DBlob) GetBufferPointer() uintptr { func (i *_ID3DBlob) GetBufferPointer() unsafe.Pointer {
r, _, _ := syscall.Syscall(i.vtbl.GetBufferPointer, 1, uintptr(unsafe.Pointer(i)), r, _, _ := syscall.Syscall(i.vtbl.GetBufferPointer, 1, uintptr(unsafe.Pointer(i)),
0, 0) 0, 0)
return r return unsafe.Pointer(r)
} }
func (i *_ID3DBlob) GetBufferSize() uintptr { func (i *_ID3DBlob) GetBufferSize() uintptr {

View File

@ -17,7 +17,6 @@ package directx
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"unsafe" "unsafe"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
@ -91,9 +90,9 @@ type graphics12 struct {
nextImageID graphicsdriver.ImageID nextImageID graphicsdriver.ImageID
disposedImages [frameCount][]*image12 disposedImages [frameCount][]*image12
shaders map[graphicsdriver.ShaderID]*Shader shaders map[graphicsdriver.ShaderID]*shader12
nextShaderID graphicsdriver.ShaderID nextShaderID graphicsdriver.ShaderID
disposedShaders [frameCount][]*Shader disposedShaders [frameCount][]*shader12
vsyncEnabled bool vsyncEnabled bool
transparent bool transparent bool
@ -864,18 +863,6 @@ func (g *graphics12) SetTransparent(transparent bool) {
g.transparent = transparent g.transparent = transparent
} }
func pow2(x uint32) uint32 {
if x > (math.MaxUint32+1)/2 {
return math.MaxUint32
}
var p2 uint32 = 1
for p2 < x {
p2 *= 2
}
return p2
}
func (g *graphics12) SetVertices(vertices []float32, indices []uint16) (ferr error) { func (g *graphics12) SetVertices(vertices []float32, indices []uint16) (ferr error) {
// Create buffers if necessary. // Create buffers if necessary.
vidx := len(g.vertices[g.frameIndex]) vidx := len(g.vertices[g.frameIndex])
@ -884,7 +871,7 @@ func (g *graphics12) SetVertices(vertices []float32, indices []uint16) (ferr err
} else { } else {
g.vertices[g.frameIndex] = append(g.vertices[g.frameIndex], nil) g.vertices[g.frameIndex] = append(g.vertices[g.frameIndex], nil)
} }
vsize := pow2(uint32(len(vertices)) * uint32(unsafe.Sizeof(float32(0)))) vsize := pow2(uint32(len(vertices)) * uint32(unsafe.Sizeof(vertices[0])))
if g.vertices[g.frameIndex][vidx] != nil && g.vertices[g.frameIndex][vidx].sizeInBytes < vsize { if g.vertices[g.frameIndex][vidx] != nil && g.vertices[g.frameIndex][vidx].sizeInBytes < vsize {
g.vertices[g.frameIndex][vidx].release() g.vertices[g.frameIndex][vidx].release()
g.vertices[g.frameIndex][vidx] = nil g.vertices[g.frameIndex][vidx] = nil
@ -913,7 +900,7 @@ func (g *graphics12) SetVertices(vertices []float32, indices []uint16) (ferr err
} else { } else {
g.indices[g.frameIndex] = append(g.indices[g.frameIndex], nil) g.indices[g.frameIndex] = append(g.indices[g.frameIndex], nil)
} }
isize := pow2(uint32(len(indices)) * uint32(unsafe.Sizeof(uint16(0)))) isize := pow2(uint32(len(indices)) * uint32(unsafe.Sizeof(indices[0])))
if g.indices[g.frameIndex][iidx] != nil && g.indices[g.frameIndex][iidx].sizeInBytes < isize { if g.indices[g.frameIndex][iidx] != nil && g.indices[g.frameIndex][iidx].sizeInBytes < isize {
g.indices[g.frameIndex][iidx].release() g.indices[g.frameIndex][iidx].release()
g.indices[g.frameIndex][iidx] = nil g.indices[g.frameIndex][iidx] = nil
@ -1031,9 +1018,9 @@ func (g *graphics12) removeImage(img *image12) {
g.disposedImages[g.frameIndex] = append(g.disposedImages[g.frameIndex], img) g.disposedImages[g.frameIndex] = append(g.disposedImages[g.frameIndex], img)
} }
func (g *graphics12) addShader(s *Shader) { func (g *graphics12) addShader(s *shader12) {
if g.shaders == nil { if g.shaders == nil {
g.shaders = map[graphicsdriver.ShaderID]*Shader{} g.shaders = map[graphicsdriver.ShaderID]*shader12{}
} }
if _, ok := g.shaders[s.id]; ok { if _, ok := g.shaders[s.id]; ok {
panic(fmt.Sprintf("directx: shader ID %d was already registered", s.id)) panic(fmt.Sprintf("directx: shader ID %d was already registered", s.id))
@ -1041,7 +1028,7 @@ func (g *graphics12) addShader(s *Shader) {
g.shaders[s.id] = s g.shaders[s.id] = s
} }
func (g *graphics12) removeShader(s *Shader) { func (g *graphics12) removeShader(s *shader12) {
delete(g.shaders, s.id) delete(g.shaders, s.id)
g.disposedShaders[g.frameIndex] = append(g.disposedShaders[g.frameIndex], s) g.disposedShaders[g.frameIndex] = append(g.disposedShaders[g.frameIndex], s)
} }
@ -1078,7 +1065,7 @@ func (g *graphics12) NewShader(program *shaderir.Program) (graphicsdriver.Shader
return nil, err return nil, err
} }
s := &Shader{ s := &shader12{
graphics: g, graphics: g,
id: g.genNextShaderID(), id: g.genNextShaderID(),
uniformTypes: program.Uniforms, uniformTypes: program.Uniforms,
@ -1138,7 +1125,7 @@ func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.
} }
shader := g.shaders[shaderID] shader := g.shaders[shaderID]
adjustedUniforms := shader.adjustUniforms(uniforms) adjustedUniforms := adjustUniforms(shader.uniformTypes, shader.uniformOffsets, uniforms)
w, h := dst.internalSize() w, h := dst.internalSize()
g.needFlushDrawCommandList = true g.needFlushDrawCommandList = true

View File

@ -17,6 +17,7 @@ package directx
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"os" "os"
"runtime" "runtime"
"strings" "strings"
@ -28,8 +29,28 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
) )
type stencilMode int
const (
prepareStencil stencilMode = iota
drawWithStencil
noStencil
)
const frameCount = 2 const frameCount = 2
func pow2(x uint32) uint32 {
if x > (math.MaxUint32+1)/2 {
return math.MaxUint32
}
var p2 uint32 = 1
for p2 < x {
p2 *= 2
}
return p2
}
// NewGraphics creates an implementation of graphicsdriver.Graphics for DirectX. // NewGraphics creates an implementation of graphicsdriver.Graphics for DirectX.
// The returned graphics value is nil iff the error is not nil. // The returned graphics value is nil iff the error is not nil.
func NewGraphics() (graphicsdriver.Graphics, error) { func NewGraphics() (graphicsdriver.Graphics, error) {

View File

@ -23,7 +23,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
) )
var inputElementDescs = []_D3D12_INPUT_ELEMENT_DESC{ var inputElementDescsForDX12 = []_D3D12_INPUT_ELEMENT_DESC{
{ {
SemanticName: &([]byte("POSITION\000"))[0], SemanticName: &([]byte("POSITION\000"))[0],
SemanticIndex: 0, SemanticIndex: 0,
@ -55,7 +55,7 @@ var inputElementDescs = []_D3D12_INPUT_ELEMENT_DESC{
const numDescriptorsPerFrame = 32 const numDescriptorsPerFrame = 32
func blendFactorToBlend(f graphicsdriver.BlendFactor, alpha bool) _D3D12_BLEND { func blendFactorToBlend12(f graphicsdriver.BlendFactor, alpha bool) _D3D12_BLEND {
// D3D12_RENDER_TARGET_BLEND_DESC's *BlendAlpha members don't allow *_COLOR values. // D3D12_RENDER_TARGET_BLEND_DESC's *BlendAlpha members don't allow *_COLOR values.
// See https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_render_target_blend_desc. // See https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_render_target_blend_desc.
@ -99,7 +99,7 @@ func blendFactorToBlend(f graphicsdriver.BlendFactor, alpha bool) _D3D12_BLEND {
} }
} }
func blendOperationToBlendOp(o graphicsdriver.BlendOperation) _D3D12_BLEND_OP { func blendOperationToBlendOp12(o graphicsdriver.BlendOperation) _D3D12_BLEND_OP {
switch o { switch o {
case graphicsdriver.BlendOperationAdd: case graphicsdriver.BlendOperationAdd:
return _D3D12_BLEND_OP_ADD return _D3D12_BLEND_OP_ADD
@ -176,7 +176,7 @@ func (p *pipelineStates) initialize(device *_ID3D12Device) (ferr error) {
return nil return nil
} }
func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D12GraphicsCommandList, frameIndex int, screen bool, srcs [graphics.ShaderImageCount]*image12, shader *Shader, dstRegions []graphicsdriver.DstRegion, uniforms []uint32, blend graphicsdriver.Blend, indexOffset int, evenOdd bool) error { func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D12GraphicsCommandList, frameIndex int, screen bool, srcs [graphics.ShaderImageCount]*image12, shader *shader12, dstRegions []graphicsdriver.DstRegion, uniforms []uint32, blend graphicsdriver.Blend, indexOffset int, evenOdd bool) error {
idx := len(p.constantBuffers[frameIndex]) idx := len(p.constantBuffers[frameIndex])
if idx >= numDescriptorsPerFrame { if idx >= numDescriptorsPerFrame {
return fmt.Errorf("directx: too many constant buffers") return fmt.Errorf("directx: too many constant buffers")
@ -479,12 +479,12 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
{ {
BlendEnable: 1, BlendEnable: 1,
LogicOpEnable: 0, LogicOpEnable: 0,
SrcBlend: blendFactorToBlend(blend.BlendFactorSourceRGB, false), SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
DestBlend: blendFactorToBlend(blend.BlendFactorDestinationRGB, false), DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
BlendOp: blendOperationToBlendOp(blend.BlendOperationRGB), BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
SrcBlendAlpha: blendFactorToBlend(blend.BlendFactorSourceAlpha, true), SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend(blend.BlendFactorDestinationAlpha, true), DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: blendOperationToBlendOp(blend.BlendOperationAlpha), BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP, LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask, RenderTargetWriteMask: writeMask,
}, },
@ -506,8 +506,8 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
}, },
DepthStencilState: depthStencilDesc, DepthStencilState: depthStencilDesc,
InputLayout: _D3D12_INPUT_LAYOUT_DESC{ InputLayout: _D3D12_INPUT_LAYOUT_DESC{
pInputElementDescs: &inputElementDescs[0], pInputElementDescs: &inputElementDescsForDX12[0],
NumElements: uint32(len(inputElementDescs)), NumElements: uint32(len(inputElementDescsForDX12)),
}, },
PrimitiveTopologyType: _D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, PrimitiveTopologyType: _D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
NumRenderTargets: 1, NumRenderTargets: 1,

View File

@ -0,0 +1,89 @@
// Copyright 2023 The Ebitengine Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package directx
import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
type pipelineStateKey struct {
blend graphicsdriver.Blend
stencilMode stencilMode
screen bool
}
type shader12 struct {
graphics *graphics12
id graphicsdriver.ShaderID
uniformTypes []shaderir.Type
uniformOffsets []int
vertexShader *_ID3DBlob
pixelShader *_ID3DBlob
pipelineStates map[pipelineStateKey]*_ID3D12PipelineState
}
func (s *shader12) ID() graphicsdriver.ShaderID {
return s.id
}
func (s *shader12) Dispose() {
s.graphics.removeShader(s)
}
func (s *shader12) disposeImpl() {
for c, p := range s.pipelineStates {
p.Release()
delete(s.pipelineStates, c)
}
if s.pixelShader != nil {
s.pixelShader.Release()
s.pixelShader = nil
}
if s.vertexShader != nil {
count := s.vertexShader.Release()
if count == 0 {
for k, v := range vertexShaderCache {
if v == s.vertexShader {
delete(vertexShaderCache, k)
}
}
}
s.vertexShader = nil
}
}
func (s *shader12) pipelineState(blend graphicsdriver.Blend, stencilMode stencilMode, screen bool) (*_ID3D12PipelineState, error) {
key := pipelineStateKey{
blend: blend,
stencilMode: stencilMode,
screen: screen,
}
if state, ok := s.pipelineStates[key]; ok {
return state, nil
}
state, err := s.graphics.pipelineStates.newPipelineState(s.graphics.device, s.vertexShader, s.pixelShader, blend, stencilMode, screen)
if err != nil {
return nil, err
}
if s.pipelineStates == nil {
s.pipelineStates = map[pipelineStateKey]*_ID3D12PipelineState{}
}
s.pipelineStates[key] = state
return state, nil
}

View File

@ -20,34 +20,9 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir"
) )
type stencilMode int
const (
prepareStencil stencilMode = iota
drawWithStencil
noStencil
)
type pipelineStateKey struct {
blend graphicsdriver.Blend
stencilMode stencilMode
screen bool
}
type Shader struct {
graphics *graphics12
id graphicsdriver.ShaderID
uniformTypes []shaderir.Type
uniformOffsets []int
vertexShader *_ID3DBlob
pixelShader *_ID3DBlob
pipelineStates map[pipelineStateKey]*_ID3D12PipelineState
}
var vertexShaderCache = map[string]*_ID3DBlob{} var vertexShaderCache = map[string]*_ID3DBlob{}
func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) { func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) {
@ -105,64 +80,12 @@ func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) {
return return
} }
func (s *Shader) ID() graphicsdriver.ShaderID { func adjustUniforms(uniformTypes []shaderir.Type, uniformOffsets []int, uniforms []uint32) []uint32 {
return s.id
}
func (s *Shader) Dispose() {
s.graphics.removeShader(s)
}
func (s *Shader) disposeImpl() {
for c, p := range s.pipelineStates {
p.Release()
delete(s.pipelineStates, c)
}
if s.pixelShader != nil {
s.pixelShader.Release()
s.pixelShader = nil
}
if s.vertexShader != nil {
count := s.vertexShader.Release()
if count == 0 {
for k, v := range vertexShaderCache {
if v == s.vertexShader {
delete(vertexShaderCache, k)
}
}
}
s.vertexShader = nil
}
}
func (s *Shader) pipelineState(blend graphicsdriver.Blend, stencilMode stencilMode, screen bool) (*_ID3D12PipelineState, error) {
key := pipelineStateKey{
blend: blend,
stencilMode: stencilMode,
screen: screen,
}
if state, ok := s.pipelineStates[key]; ok {
return state, nil
}
state, err := s.graphics.pipelineStates.newPipelineState(s.graphics.device, s.vertexShader, s.pixelShader, blend, stencilMode, screen)
if err != nil {
return nil, err
}
if s.pipelineStates == nil {
s.pipelineStates = map[pipelineStateKey]*_ID3D12PipelineState{}
}
s.pipelineStates[key] = state
return state, nil
}
func (s *Shader) adjustUniforms(uniforms []uint32) []uint32 {
var fs []uint32 var fs []uint32
var idx int var idx int
for i, typ := range s.uniformTypes { for i, typ := range uniformTypes {
if len(fs) < s.uniformOffsets[i]/4 { if len(fs) < uniformOffsets[i]/4 {
fs = append(fs, make([]uint32, s.uniformOffsets[i]/4-len(fs))...) fs = append(fs, make([]uint32, uniformOffsets[i]/4-len(fs))...)
} }
n := typ.Uint32Count() n := typ.Uint32Count()