From c8a2e5dc713ad32555607ed7fa7fb896c0f58e64 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 27 Mar 2023 14:14:22 +0900 Subject: [PATCH] internal/graphicsdriver/directx: refactoring This is a preparation for a DirectX 11 driver. Updates #2613 --- .../graphicsdriver/directx/d3d12_windows.go | 6 +- .../graphicsdriver/directx/d3d_windows.go | 4 +- .../directx/graphics12_windows.go | 31 ++----- .../directx/graphics_windows.go | 21 +++++ .../directx/pipeline12_windows.go | 24 ++--- .../directx/shader12_windows.go | 89 +++++++++++++++++++ .../graphicsdriver/directx/shader_windows.go | 85 +----------------- 7 files changed, 140 insertions(+), 120 deletions(-) create mode 100644 internal/graphicsdriver/directx/shader12_windows.go diff --git a/internal/graphicsdriver/directx/d3d12_windows.go b/internal/graphicsdriver/directx/d3d12_windows.go index ff9ab637f..58960223b 100644 --- a/internal/graphicsdriver/directx/d3d12_windows.go +++ b/internal/graphicsdriver/directx/d3d12_windows.go @@ -726,7 +726,7 @@ type _D3D12_ROOT_SIGNATURE_DESC struct { } type _D3D12_SHADER_BYTECODE struct { - pShaderBytecode uintptr + pShaderBytecode unsafe.Pointer BytecodeLength uintptr } @@ -1507,10 +1507,10 @@ func (i *_ID3D12Device) CreateRenderTargetView(pResource *_ID3D12Resource, 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 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))) if uint32(r) != uint32(windows.S_OK) { return nil, fmt.Errorf("directx: ID3D12Device::CreateRootSignature failed: %w", handleError(windows.Handle(uint32(r)))) diff --git a/internal/graphicsdriver/directx/d3d_windows.go b/internal/graphicsdriver/directx/d3d_windows.go index a7929439d..b89289084 100644 --- a/internal/graphicsdriver/directx/d3d_windows.go +++ b/internal/graphicsdriver/directx/d3d_windows.go @@ -112,10 +112,10 @@ func (i *_ID3DBlob) AddRef() uint32 { 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)), 0, 0) - return r + return unsafe.Pointer(r) } func (i *_ID3DBlob) GetBufferSize() uintptr { diff --git a/internal/graphicsdriver/directx/graphics12_windows.go b/internal/graphicsdriver/directx/graphics12_windows.go index 9d2410471..110b95c06 100644 --- a/internal/graphicsdriver/directx/graphics12_windows.go +++ b/internal/graphicsdriver/directx/graphics12_windows.go @@ -17,7 +17,6 @@ package directx import ( "errors" "fmt" - "math" "unsafe" "golang.org/x/sys/windows" @@ -91,9 +90,9 @@ type graphics12 struct { nextImageID graphicsdriver.ImageID disposedImages [frameCount][]*image12 - shaders map[graphicsdriver.ShaderID]*Shader + shaders map[graphicsdriver.ShaderID]*shader12 nextShaderID graphicsdriver.ShaderID - disposedShaders [frameCount][]*Shader + disposedShaders [frameCount][]*shader12 vsyncEnabled bool transparent bool @@ -864,18 +863,6 @@ func (g *graphics12) SetTransparent(transparent bool) { 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) { // Create buffers if necessary. vidx := len(g.vertices[g.frameIndex]) @@ -884,7 +871,7 @@ func (g *graphics12) SetVertices(vertices []float32, indices []uint16) (ferr err } else { 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 { g.vertices[g.frameIndex][vidx].release() g.vertices[g.frameIndex][vidx] = nil @@ -913,7 +900,7 @@ func (g *graphics12) SetVertices(vertices []float32, indices []uint16) (ferr err } else { 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 { g.indices[g.frameIndex][iidx].release() 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) } -func (g *graphics12) addShader(s *Shader) { +func (g *graphics12) addShader(s *shader12) { if g.shaders == nil { - g.shaders = map[graphicsdriver.ShaderID]*Shader{} + g.shaders = map[graphicsdriver.ShaderID]*shader12{} } if _, ok := g.shaders[s.id]; ok { 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 } -func (g *graphics12) removeShader(s *Shader) { +func (g *graphics12) removeShader(s *shader12) { delete(g.shaders, s.id) 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 } - s := &Shader{ + s := &shader12{ graphics: g, id: g.genNextShaderID(), uniformTypes: program.Uniforms, @@ -1138,7 +1125,7 @@ func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics. } shader := g.shaders[shaderID] - adjustedUniforms := shader.adjustUniforms(uniforms) + adjustedUniforms := adjustUniforms(shader.uniformTypes, shader.uniformOffsets, uniforms) w, h := dst.internalSize() g.needFlushDrawCommandList = true diff --git a/internal/graphicsdriver/directx/graphics_windows.go b/internal/graphicsdriver/directx/graphics_windows.go index 9766da5cf..37af2f1c5 100644 --- a/internal/graphicsdriver/directx/graphics_windows.go +++ b/internal/graphicsdriver/directx/graphics_windows.go @@ -17,6 +17,7 @@ package directx import ( "errors" "fmt" + "math" "os" "runtime" "strings" @@ -28,8 +29,28 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) +type stencilMode int + +const ( + prepareStencil stencilMode = iota + drawWithStencil + noStencil +) + 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. // The returned graphics value is nil iff the error is not nil. func NewGraphics() (graphicsdriver.Graphics, error) { diff --git a/internal/graphicsdriver/directx/pipeline12_windows.go b/internal/graphicsdriver/directx/pipeline12_windows.go index 301f2c51e..f54189b37 100644 --- a/internal/graphicsdriver/directx/pipeline12_windows.go +++ b/internal/graphicsdriver/directx/pipeline12_windows.go @@ -23,7 +23,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) -var inputElementDescs = []_D3D12_INPUT_ELEMENT_DESC{ +var inputElementDescsForDX12 = []_D3D12_INPUT_ELEMENT_DESC{ { SemanticName: &([]byte("POSITION\000"))[0], SemanticIndex: 0, @@ -55,7 +55,7 @@ var inputElementDescs = []_D3D12_INPUT_ELEMENT_DESC{ 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. // 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 { case graphicsdriver.BlendOperationAdd: return _D3D12_BLEND_OP_ADD @@ -176,7 +176,7 @@ func (p *pipelineStates) initialize(device *_ID3D12Device) (ferr error) { 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]) if idx >= numDescriptorsPerFrame { return fmt.Errorf("directx: too many constant buffers") @@ -479,12 +479,12 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D { BlendEnable: 1, LogicOpEnable: 0, - SrcBlend: blendFactorToBlend(blend.BlendFactorSourceRGB, false), - DestBlend: blendFactorToBlend(blend.BlendFactorDestinationRGB, false), - BlendOp: blendOperationToBlendOp(blend.BlendOperationRGB), - SrcBlendAlpha: blendFactorToBlend(blend.BlendFactorSourceAlpha, true), - DestBlendAlpha: blendFactorToBlend(blend.BlendFactorDestinationAlpha, true), - BlendOpAlpha: blendOperationToBlendOp(blend.BlendOperationAlpha), + SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false), + DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false), + BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB), + SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true), + DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true), + BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha), LogicOp: _D3D12_LOGIC_OP_NOOP, RenderTargetWriteMask: writeMask, }, @@ -506,8 +506,8 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D }, DepthStencilState: depthStencilDesc, InputLayout: _D3D12_INPUT_LAYOUT_DESC{ - pInputElementDescs: &inputElementDescs[0], - NumElements: uint32(len(inputElementDescs)), + pInputElementDescs: &inputElementDescsForDX12[0], + NumElements: uint32(len(inputElementDescsForDX12)), }, PrimitiveTopologyType: _D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, NumRenderTargets: 1, diff --git a/internal/graphicsdriver/directx/shader12_windows.go b/internal/graphicsdriver/directx/shader12_windows.go new file mode 100644 index 000000000..505fbead2 --- /dev/null +++ b/internal/graphicsdriver/directx/shader12_windows.go @@ -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 +} diff --git a/internal/graphicsdriver/directx/shader_windows.go b/internal/graphicsdriver/directx/shader_windows.go index 2a18c8427..0cf9c2417 100644 --- a/internal/graphicsdriver/directx/shader_windows.go +++ b/internal/graphicsdriver/directx/shader_windows.go @@ -20,34 +20,9 @@ import ( "golang.org/x/sync/errgroup" "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "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{} 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 } -func (s *Shader) ID() graphicsdriver.ShaderID { - 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 { +func adjustUniforms(uniformTypes []shaderir.Type, uniformOffsets []int, uniforms []uint32) []uint32 { var fs []uint32 var idx int - for i, typ := range s.uniformTypes { - if len(fs) < s.uniformOffsets[i]/4 { - fs = append(fs, make([]uint32, s.uniformOffsets[i]/4-len(fs))...) + for i, typ := range uniformTypes { + if len(fs) < uniformOffsets[i]/4 { + fs = append(fs, make([]uint32, uniformOffsets[i]/4-len(fs))...) } n := typ.Uint32Count()