mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-27 04:08:53 +01:00
a9574627e8
Updates #2369
476 lines
15 KiB
Go
476 lines
15 KiB
Go
// Copyright 2022 The Ebiten 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 (
|
|
"fmt"
|
|
"math"
|
|
"unsafe"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
|
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
|
)
|
|
|
|
const numDescriptorsPerFrame = 32
|
|
|
|
func operationToBlend(c graphicsdriver.Operation, alpha bool) _D3D12_BLEND {
|
|
switch c {
|
|
case graphicsdriver.Zero:
|
|
return _D3D12_BLEND_ZERO
|
|
case graphicsdriver.One:
|
|
return _D3D12_BLEND_ONE
|
|
case graphicsdriver.SrcAlpha:
|
|
return _D3D12_BLEND_SRC_ALPHA
|
|
case graphicsdriver.DstAlpha:
|
|
return _D3D12_BLEND_DEST_ALPHA
|
|
case graphicsdriver.OneMinusSrcAlpha:
|
|
return _D3D12_BLEND_INV_SRC_ALPHA
|
|
case graphicsdriver.OneMinusDstAlpha:
|
|
return _D3D12_BLEND_INV_DEST_ALPHA
|
|
case graphicsdriver.DstColor:
|
|
if alpha {
|
|
return _D3D12_BLEND_DEST_ALPHA
|
|
}
|
|
return _D3D12_BLEND_DEST_COLOR
|
|
default:
|
|
panic(fmt.Sprintf("directx: invalid operation: %d", c))
|
|
}
|
|
}
|
|
|
|
type pipelineStates struct {
|
|
rootSignature *_ID3D12RootSignature
|
|
|
|
shaderDescriptorHeap *_ID3D12DescriptorHeap
|
|
shaderDescriptorSize uint32
|
|
|
|
samplerDescriptorHeap *_ID3D12DescriptorHeap
|
|
|
|
constantBuffers [frameCount][]*_ID3D12Resource
|
|
constantBufferMaps [frameCount][]uintptr
|
|
}
|
|
|
|
const numConstantBufferAndSourceTextures = 1 + graphics.ShaderImageCount
|
|
|
|
func (p *pipelineStates) initialize(device *_ID3D12Device) (ferr error) {
|
|
// Create a CBV/SRV/UAV descriptor heap.
|
|
// 5n+0: constants
|
|
// 5n+m (1<=4): textures
|
|
shaderH, err := device.CreateDescriptorHeap(&_D3D12_DESCRIPTOR_HEAP_DESC{
|
|
Type: _D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
NumDescriptors: frameCount * numDescriptorsPerFrame * numConstantBufferAndSourceTextures,
|
|
Flags: _D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
|
|
NodeMask: 0,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.shaderDescriptorHeap = shaderH
|
|
defer func() {
|
|
if ferr != nil {
|
|
p.shaderDescriptorHeap.Release()
|
|
p.shaderDescriptorHeap = nil
|
|
}
|
|
}()
|
|
p.shaderDescriptorSize = device.GetDescriptorHandleIncrementSize(_D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)
|
|
|
|
samplerH, err := device.CreateDescriptorHeap(&_D3D12_DESCRIPTOR_HEAP_DESC{
|
|
Type: _D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
|
|
NumDescriptors: 1,
|
|
Flags: _D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
|
|
NodeMask: 0,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.samplerDescriptorHeap = samplerH
|
|
|
|
h, err := p.samplerDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
device.CreateSampler(&_D3D12_SAMPLER_DESC{
|
|
Filter: _D3D12_FILTER_MIN_MAG_MIP_POINT,
|
|
AddressU: _D3D12_TEXTURE_ADDRESS_MODE_WRAP,
|
|
AddressV: _D3D12_TEXTURE_ADDRESS_MODE_WRAP,
|
|
AddressW: _D3D12_TEXTURE_ADDRESS_MODE_WRAP,
|
|
ComparisonFunc: _D3D12_COMPARISON_FUNC_NEVER,
|
|
MinLOD: -math.MaxFloat32,
|
|
MaxLOD: math.MaxFloat32,
|
|
}, h)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *pipelineStates) useGraphicsPipelineState(device *_ID3D12Device, commandList *_ID3D12GraphicsCommandList, frameIndex int, pipelineState *_ID3D12PipelineState, srcs [graphics.ShaderImageCount]*Image, uniforms []float32) error {
|
|
idx := len(p.constantBuffers[frameIndex])
|
|
if idx >= numDescriptorsPerFrame {
|
|
return fmt.Errorf("directx: too many constant buffers")
|
|
}
|
|
|
|
if cap(p.constantBuffers[frameIndex]) > idx {
|
|
p.constantBuffers[frameIndex] = p.constantBuffers[frameIndex][:idx+1]
|
|
p.constantBufferMaps[frameIndex] = p.constantBufferMaps[frameIndex][:idx+1]
|
|
} else {
|
|
p.constantBuffers[frameIndex] = append(p.constantBuffers[frameIndex], nil)
|
|
p.constantBufferMaps[frameIndex] = append(p.constantBufferMaps[frameIndex], 0)
|
|
}
|
|
|
|
const bufferSizeAlignement = 256
|
|
bufferSize := uint32(unsafe.Sizeof(float32(0))) * uint32(len(uniforms))
|
|
if bufferSize > 0 {
|
|
bufferSize = ((bufferSize-1)/bufferSizeAlignement + 1) * bufferSizeAlignement
|
|
}
|
|
|
|
cb := p.constantBuffers[frameIndex][idx]
|
|
m := p.constantBufferMaps[frameIndex][idx]
|
|
if cb != nil {
|
|
if uint32(cb.GetDesc().Width) < bufferSize {
|
|
p.constantBuffers[frameIndex][idx].Unmap(0, nil)
|
|
p.constantBuffers[frameIndex][idx].Release()
|
|
p.constantBuffers[frameIndex][idx] = nil
|
|
p.constantBufferMaps[frameIndex][idx] = 0
|
|
cb = nil
|
|
}
|
|
}
|
|
if cb == nil {
|
|
var err error
|
|
cb, err = createBuffer(device, uint64(bufferSize), _D3D12_HEAP_TYPE_UPLOAD)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.constantBuffers[frameIndex][idx] = cb
|
|
|
|
h, err := p.shaderDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
offset := int32(numConstantBufferAndSourceTextures * (frameIndex*numDescriptorsPerFrame + idx))
|
|
h.Offset(offset, p.shaderDescriptorSize)
|
|
device.CreateConstantBufferView(&_D3D12_CONSTANT_BUFFER_VIEW_DESC{
|
|
BufferLocation: cb.GetGPUVirtualAddress(),
|
|
SizeInBytes: bufferSize,
|
|
}, h)
|
|
|
|
m, err = cb.Map(0, &_D3D12_RANGE{0, 0})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.constantBufferMaps[frameIndex][idx] = m
|
|
}
|
|
if m == 0 {
|
|
return fmt.Errorf("directx: ID3D12Resource::Map failed")
|
|
}
|
|
|
|
h, err := p.shaderDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
offset := int32(numConstantBufferAndSourceTextures * (frameIndex*numDescriptorsPerFrame + idx))
|
|
h.Offset(offset, p.shaderDescriptorSize)
|
|
for _, src := range srcs {
|
|
h.Offset(1, p.shaderDescriptorSize)
|
|
if src == nil {
|
|
continue
|
|
}
|
|
device.CreateShaderResourceView(src.resource(), &_D3D12_SHADER_RESOURCE_VIEW_DESC{
|
|
Format: _DXGI_FORMAT_R8G8B8A8_UNORM,
|
|
ViewDimension: _D3D12_SRV_DIMENSION_TEXTURE2D,
|
|
Shader4ComponentMapping: _D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
|
|
Texture2D: _D3D12_TEX2D_SRV{
|
|
MipLevels: 1, // TODO: Can this be 0?
|
|
},
|
|
}, h)
|
|
}
|
|
|
|
// Update the constant buffer.
|
|
copyFloat32s(m, uniforms)
|
|
|
|
commandList.SetPipelineState(pipelineState)
|
|
|
|
rs, err := p.ensureRootSignature(device)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
commandList.SetGraphicsRootSignature(rs)
|
|
|
|
commandList.SetDescriptorHeaps([]*_ID3D12DescriptorHeap{
|
|
p.shaderDescriptorHeap,
|
|
p.samplerDescriptorHeap,
|
|
})
|
|
|
|
// Match the indices with rootParams in graphicsPipelineState.
|
|
gh, err := p.shaderDescriptorHeap.GetGPUDescriptorHandleForHeapStart()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
gh.Offset(offset, p.shaderDescriptorSize)
|
|
commandList.SetGraphicsRootDescriptorTable(0, gh)
|
|
commandList.SetGraphicsRootDescriptorTable(1, gh)
|
|
sh, err := p.samplerDescriptorHeap.GetGPUDescriptorHandleForHeapStart()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
commandList.SetGraphicsRootDescriptorTable(2, sh)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *pipelineStates) ensureRootSignature(device *_ID3D12Device) (rootSignature *_ID3D12RootSignature, ferr error) {
|
|
if p.rootSignature != nil {
|
|
return p.rootSignature, nil
|
|
}
|
|
|
|
cbv := _D3D12_DESCRIPTOR_RANGE{
|
|
RangeType: _D3D12_DESCRIPTOR_RANGE_TYPE_CBV, // b0
|
|
NumDescriptors: 1,
|
|
BaseShaderRegister: 0,
|
|
RegisterSpace: 0,
|
|
OffsetInDescriptorsFromTableStart: 0,
|
|
}
|
|
srv := _D3D12_DESCRIPTOR_RANGE{
|
|
RangeType: _D3D12_DESCRIPTOR_RANGE_TYPE_SRV, // t0
|
|
NumDescriptors: graphics.ShaderImageCount,
|
|
BaseShaderRegister: 0,
|
|
RegisterSpace: 0,
|
|
OffsetInDescriptorsFromTableStart: 1,
|
|
}
|
|
sampler := _D3D12_DESCRIPTOR_RANGE{
|
|
RangeType: _D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, // s0
|
|
NumDescriptors: 1,
|
|
BaseShaderRegister: 0,
|
|
RegisterSpace: 0,
|
|
OffsetInDescriptorsFromTableStart: 0,
|
|
}
|
|
|
|
rootParams := [...]_D3D12_ROOT_PARAMETER{
|
|
{
|
|
ParameterType: _D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
|
|
DescriptorTable: _D3D12_ROOT_DESCRIPTOR_TABLE{
|
|
NumDescriptorRanges: 1,
|
|
pDescriptorRanges: &cbv,
|
|
},
|
|
ShaderVisibility: _D3D12_SHADER_VISIBILITY_ALL,
|
|
},
|
|
{
|
|
ParameterType: _D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
|
|
DescriptorTable: _D3D12_ROOT_DESCRIPTOR_TABLE{
|
|
NumDescriptorRanges: 1,
|
|
pDescriptorRanges: &srv,
|
|
},
|
|
ShaderVisibility: _D3D12_SHADER_VISIBILITY_PIXEL,
|
|
},
|
|
{
|
|
ParameterType: _D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
|
|
DescriptorTable: _D3D12_ROOT_DESCRIPTOR_TABLE{
|
|
NumDescriptorRanges: 1,
|
|
pDescriptorRanges: &sampler,
|
|
},
|
|
ShaderVisibility: _D3D12_SHADER_VISIBILITY_PIXEL,
|
|
},
|
|
}
|
|
|
|
// Create a root signature.
|
|
sig, err := _D3D12SerializeRootSignature(&_D3D12_ROOT_SIGNATURE_DESC{
|
|
NumParameters: uint32(len(rootParams)),
|
|
pParameters: &rootParams[0],
|
|
NumStaticSamplers: 0,
|
|
pStaticSamplers: nil,
|
|
Flags: _D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT,
|
|
}, _D3D_ROOT_SIGNATURE_VERSION_1_0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer sig.Release()
|
|
|
|
rs, err := device.CreateRootSignature(0, sig.GetBufferPointer(), sig.GetBufferSize())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if ferr != nil {
|
|
rootSignature.Release()
|
|
}
|
|
}()
|
|
|
|
p.rootSignature = rs
|
|
|
|
return p.rootSignature, nil
|
|
}
|
|
|
|
func newShader(source []byte, defs []_D3D_SHADER_MACRO) (vsh, psh *_ID3DBlob, ferr error) {
|
|
var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3)
|
|
|
|
// Create a shader
|
|
v, err := _D3DCompile(source, "shader", defs, nil, "VSMain", "vs_5_0", flag, 0)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("directx: D3DCompile for VSMain failed, original source: %s, %w", string(source), err)
|
|
}
|
|
defer func() {
|
|
if ferr != nil {
|
|
v.Release()
|
|
}
|
|
}()
|
|
|
|
p, err := _D3DCompile(source, "shader", defs, nil, "PSMain", "ps_5_0", flag, 0)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("directx: D3DCompile for PSMain failed, original source: %s, %w", string(source), err)
|
|
}
|
|
defer func() {
|
|
if ferr != nil {
|
|
p.Release()
|
|
}
|
|
}()
|
|
|
|
return v, p, nil
|
|
}
|
|
|
|
func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3DBlob, compositeMode graphicsdriver.CompositeMode, stencilMode stencilMode, screen bool) (state *_ID3D12PipelineState, ferr error) {
|
|
rootSignature, err := p.ensureRootSignature(device)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if ferr != nil {
|
|
rootSignature.Release()
|
|
}
|
|
}()
|
|
|
|
depthStencilDesc := _D3D12_DEPTH_STENCIL_DESC{
|
|
DepthEnable: 0,
|
|
DepthWriteMask: _D3D12_DEPTH_WRITE_MASK_ALL,
|
|
DepthFunc: _D3D12_COMPARISON_FUNC_LESS,
|
|
StencilEnable: 0,
|
|
StencilReadMask: _D3D12_DEFAULT_STENCIL_READ_MASK,
|
|
StencilWriteMask: _D3D12_DEFAULT_STENCIL_WRITE_MASK,
|
|
FrontFace: _D3D12_DEPTH_STENCILOP_DESC{
|
|
StencilFailOp: _D3D12_STENCIL_OP_KEEP,
|
|
StencilDepthFailOp: _D3D12_STENCIL_OP_KEEP,
|
|
StencilPassOp: _D3D12_STENCIL_OP_KEEP,
|
|
StencilFunc: _D3D12_COMPARISON_FUNC_ALWAYS,
|
|
},
|
|
BackFace: _D3D12_DEPTH_STENCILOP_DESC{
|
|
StencilFailOp: _D3D12_STENCIL_OP_KEEP,
|
|
StencilDepthFailOp: _D3D12_STENCIL_OP_KEEP,
|
|
StencilPassOp: _D3D12_STENCIL_OP_KEEP,
|
|
StencilFunc: _D3D12_COMPARISON_FUNC_ALWAYS,
|
|
},
|
|
}
|
|
writeMask := uint8(_D3D12_COLOR_WRITE_ENABLE_ALL)
|
|
|
|
switch stencilMode {
|
|
case prepareStencil:
|
|
depthStencilDesc.StencilEnable = 1
|
|
depthStencilDesc.FrontFace.StencilPassOp = _D3D12_STENCIL_OP_INVERT
|
|
depthStencilDesc.BackFace.StencilPassOp = _D3D12_STENCIL_OP_INVERT
|
|
writeMask = 0
|
|
case drawWithStencil:
|
|
depthStencilDesc.StencilEnable = 1
|
|
depthStencilDesc.FrontFace.StencilFunc = _D3D12_COMPARISON_FUNC_NOT_EQUAL
|
|
depthStencilDesc.BackFace.StencilFunc = _D3D12_COMPARISON_FUNC_NOT_EQUAL
|
|
}
|
|
|
|
rtvFormat := _DXGI_FORMAT_R8G8B8A8_UNORM
|
|
if screen {
|
|
rtvFormat = _DXGI_FORMAT_B8G8R8A8_UNORM
|
|
}
|
|
dsvFormat := _DXGI_FORMAT_UNKNOWN
|
|
if stencilMode != noStencil {
|
|
dsvFormat = _DXGI_FORMAT_D24_UNORM_S8_UINT
|
|
}
|
|
|
|
// Create a pipeline state.
|
|
srcOp, dstOp := compositeMode.Operations()
|
|
psoDesc := _D3D12_GRAPHICS_PIPELINE_STATE_DESC{
|
|
pRootSignature: rootSignature,
|
|
VS: _D3D12_SHADER_BYTECODE{
|
|
pShaderBytecode: vsh.GetBufferPointer(),
|
|
BytecodeLength: vsh.GetBufferSize(),
|
|
},
|
|
PS: _D3D12_SHADER_BYTECODE{
|
|
pShaderBytecode: psh.GetBufferPointer(),
|
|
BytecodeLength: psh.GetBufferSize(),
|
|
},
|
|
BlendState: _D3D12_BLEND_DESC{
|
|
AlphaToCoverageEnable: 0,
|
|
IndependentBlendEnable: 0,
|
|
RenderTarget: [8]_D3D12_RENDER_TARGET_BLEND_DESC{
|
|
{
|
|
BlendEnable: 1,
|
|
LogicOpEnable: 0,
|
|
SrcBlend: operationToBlend(srcOp, false),
|
|
DestBlend: operationToBlend(dstOp, false),
|
|
BlendOp: _D3D12_BLEND_OP_ADD,
|
|
SrcBlendAlpha: operationToBlend(srcOp, true),
|
|
DestBlendAlpha: operationToBlend(dstOp, true),
|
|
BlendOpAlpha: _D3D12_BLEND_OP_ADD,
|
|
LogicOp: _D3D12_LOGIC_OP_NOOP,
|
|
RenderTargetWriteMask: writeMask,
|
|
},
|
|
},
|
|
},
|
|
SampleMask: math.MaxUint32,
|
|
RasterizerState: _D3D12_RASTERIZER_DESC{
|
|
FillMode: _D3D12_FILL_MODE_SOLID,
|
|
CullMode: _D3D12_CULL_MODE_NONE,
|
|
FrontCounterClockwise: 0,
|
|
DepthBias: _D3D12_DEFAULT_DEPTH_BIAS,
|
|
DepthBiasClamp: _D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
|
|
SlopeScaledDepthBias: _D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
|
|
DepthClipEnable: 0,
|
|
MultisampleEnable: 0,
|
|
AntialiasedLineEnable: 0,
|
|
ForcedSampleCount: 0,
|
|
ConservativeRaster: _D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF,
|
|
},
|
|
DepthStencilState: depthStencilDesc,
|
|
InputLayout: _D3D12_INPUT_LAYOUT_DESC{
|
|
pInputElementDescs: &inputElementDescs[0],
|
|
NumElements: uint32(len(inputElementDescs)),
|
|
},
|
|
PrimitiveTopologyType: _D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
|
NumRenderTargets: 1,
|
|
RTVFormats: [8]_DXGI_FORMAT{
|
|
rtvFormat,
|
|
},
|
|
DSVFormat: dsvFormat,
|
|
SampleDesc: _DXGI_SAMPLE_DESC{
|
|
Count: 1,
|
|
Quality: 0,
|
|
},
|
|
}
|
|
|
|
s, err := device.CreateGraphicsPipelineState(&psoDesc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
func (p *pipelineStates) releaseConstantBuffers(frameIndex int) {
|
|
for i := range p.constantBuffers[frameIndex] {
|
|
p.constantBuffers[frameIndex][i].Unmap(0, nil)
|
|
p.constantBuffers[frameIndex][i].Release()
|
|
p.constantBuffers[frameIndex][i] = nil
|
|
p.constantBufferMaps[frameIndex][i] = 0
|
|
}
|
|
p.constantBuffers[frameIndex] = p.constantBuffers[frameIndex][:0]
|
|
p.constantBufferMaps[frameIndex] = p.constantBufferMaps[frameIndex][:0]
|
|
}
|
|
|
|
func (p *pipelineStates) resetConstantBuffers(frameIndex int) {
|
|
p.constantBuffers[frameIndex] = p.constantBuffers[frameIndex][:0]
|
|
p.constantBufferMaps[frameIndex] = p.constantBufferMaps[frameIndex][:0]
|
|
}
|