2023-03-27 07:14:22 +02:00
|
|
|
// 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 (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
|
|
|
|
"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/hlsl"
|
|
|
|
)
|
|
|
|
|
|
|
|
var inputElementDescsForDX11 = []_D3D11_INPUT_ELEMENT_DESC{
|
|
|
|
{
|
|
|
|
SemanticName: &([]byte("POSITION\000"))[0],
|
|
|
|
SemanticIndex: 0,
|
|
|
|
Format: _DXGI_FORMAT_R32G32_FLOAT,
|
|
|
|
InputSlot: 0,
|
|
|
|
AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT,
|
|
|
|
InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA,
|
|
|
|
InstanceDataStepRate: 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
SemanticName: &([]byte("TEXCOORD\000"))[0],
|
|
|
|
SemanticIndex: 0,
|
|
|
|
Format: _DXGI_FORMAT_R32G32_FLOAT,
|
|
|
|
InputSlot: 0,
|
|
|
|
AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT,
|
|
|
|
InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA,
|
|
|
|
InstanceDataStepRate: 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
SemanticName: &([]byte("COLOR\000"))[0],
|
|
|
|
SemanticIndex: 0,
|
|
|
|
Format: _DXGI_FORMAT_R32G32B32A32_FLOAT,
|
|
|
|
InputSlot: 0,
|
|
|
|
AlignedByteOffset: _D3D11_APPEND_ALIGNED_ELEMENT,
|
|
|
|
InputSlotClass: _D3D11_INPUT_PER_VERTEX_DATA,
|
|
|
|
InstanceDataStepRate: 0,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func blendFactorToBlend11(f graphicsdriver.BlendFactor, alpha bool) _D3D11_BLEND {
|
|
|
|
switch f {
|
|
|
|
case graphicsdriver.BlendFactorZero:
|
|
|
|
return _D3D11_BLEND_ZERO
|
|
|
|
case graphicsdriver.BlendFactorOne:
|
|
|
|
return _D3D11_BLEND_ONE
|
|
|
|
case graphicsdriver.BlendFactorSourceColor:
|
|
|
|
if alpha {
|
|
|
|
return _D3D11_BLEND_SRC_ALPHA
|
|
|
|
}
|
|
|
|
return _D3D11_BLEND_SRC_COLOR
|
|
|
|
case graphicsdriver.BlendFactorOneMinusSourceColor:
|
|
|
|
if alpha {
|
|
|
|
return _D3D11_BLEND_INV_SRC_ALPHA
|
|
|
|
}
|
|
|
|
return _D3D11_BLEND_INV_SRC_COLOR
|
|
|
|
case graphicsdriver.BlendFactorSourceAlpha:
|
|
|
|
return _D3D11_BLEND_SRC_ALPHA
|
|
|
|
case graphicsdriver.BlendFactorOneMinusSourceAlpha:
|
|
|
|
return _D3D11_BLEND_INV_SRC_ALPHA
|
|
|
|
case graphicsdriver.BlendFactorDestinationColor:
|
|
|
|
if alpha {
|
|
|
|
return _D3D11_BLEND_DEST_ALPHA
|
|
|
|
}
|
|
|
|
return _D3D11_BLEND_DEST_COLOR
|
|
|
|
case graphicsdriver.BlendFactorOneMinusDestinationColor:
|
|
|
|
if alpha {
|
|
|
|
return _D3D11_BLEND_INV_DEST_ALPHA
|
|
|
|
}
|
|
|
|
return _D3D11_BLEND_INV_DEST_COLOR
|
|
|
|
case graphicsdriver.BlendFactorDestinationAlpha:
|
|
|
|
return _D3D11_BLEND_DEST_ALPHA
|
|
|
|
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
|
|
|
|
return _D3D11_BLEND_INV_DEST_ALPHA
|
|
|
|
case graphicsdriver.BlendFactorSourceAlphaSaturated:
|
|
|
|
return _D3D11_BLEND_SRC_ALPHA_SAT
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("directx: invalid blend factor: %d", f))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func blendOperationToBlendOp11(o graphicsdriver.BlendOperation) _D3D11_BLEND_OP {
|
|
|
|
switch o {
|
|
|
|
case graphicsdriver.BlendOperationAdd:
|
|
|
|
return _D3D11_BLEND_OP_ADD
|
|
|
|
case graphicsdriver.BlendOperationSubtract:
|
|
|
|
return _D3D11_BLEND_OP_SUBTRACT
|
|
|
|
case graphicsdriver.BlendOperationReverseSubtract:
|
|
|
|
return _D3D11_BLEND_OP_REV_SUBTRACT
|
2023-10-12 16:55:31 +02:00
|
|
|
case graphicsdriver.BlendOperationMin:
|
|
|
|
return _D3D11_BLEND_OP_MIN
|
|
|
|
case graphicsdriver.BlendOperationMax:
|
|
|
|
return _D3D11_BLEND_OP_MAX
|
2023-03-27 07:14:22 +02:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("directx: invalid blend operation: %d", o))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type blendStateKey struct {
|
|
|
|
blend graphicsdriver.Blend
|
|
|
|
writeMask uint8
|
|
|
|
}
|
|
|
|
|
|
|
|
type graphics11 struct {
|
|
|
|
graphicsInfra *graphicsInfra
|
|
|
|
|
|
|
|
featureLevel _D3D_FEATURE_LEVEL
|
|
|
|
|
|
|
|
device *_ID3D11Device
|
|
|
|
deviceContext *_ID3D11DeviceContext
|
|
|
|
|
|
|
|
images map[graphicsdriver.ImageID]*image11
|
|
|
|
screenImage *image11
|
|
|
|
nextImageID graphicsdriver.ImageID
|
|
|
|
|
|
|
|
shaders map[graphicsdriver.ShaderID]*shader11
|
|
|
|
nextShaderID graphicsdriver.ShaderID
|
|
|
|
|
|
|
|
vertexBuffer *_ID3D11Buffer
|
|
|
|
vertexBufferSizeInBytes uint32
|
|
|
|
|
|
|
|
indexBuffer *_ID3D11Buffer
|
|
|
|
indexBufferSizeInBytes uint32
|
|
|
|
|
|
|
|
rasterizerState *_ID3D11RasterizerState
|
|
|
|
samplerState *_ID3D11SamplerState
|
|
|
|
blendStates map[blendStateKey]*_ID3D11BlendState
|
|
|
|
depthStencilStates map[stencilMode]*_ID3D11DepthStencilState
|
|
|
|
|
|
|
|
vsyncEnabled bool
|
|
|
|
window windows.HWND
|
2023-04-09 09:21:47 +02:00
|
|
|
|
|
|
|
newScreenWidth int
|
|
|
|
newScreenHeight int
|
2023-03-27 07:14:22 +02:00
|
|
|
}
|
|
|
|
|
2024-03-30 12:08:06 +01:00
|
|
|
func newGraphics11(useWARP bool, useDebugLayer bool) (gr11 *graphics11, ferr error) {
|
2023-03-27 07:14:22 +02:00
|
|
|
g := &graphics11{
|
|
|
|
vsyncEnabled: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
driverType := _D3D_DRIVER_TYPE_HARDWARE
|
|
|
|
if useWARP {
|
|
|
|
driverType = _D3D_DRIVER_TYPE_WARP
|
|
|
|
}
|
|
|
|
|
|
|
|
var flags _D3D11_CREATE_DEVICE_FLAG
|
|
|
|
if useDebugLayer {
|
|
|
|
flags |= _D3D11_CREATE_DEVICE_DEBUG
|
|
|
|
}
|
|
|
|
|
2023-12-17 14:42:34 +01:00
|
|
|
// Avoid _D3D_FEATURE_LEVEL_11_1 as DirectX 11.0 doesn't recognize this.
|
2023-03-27 07:14:22 +02:00
|
|
|
// Avoid _D3D_FEATURE_LEVEL_9_* for some shaders features (#1431).
|
|
|
|
featureLevels := []_D3D_FEATURE_LEVEL{
|
|
|
|
_D3D_FEATURE_LEVEL_11_0,
|
|
|
|
_D3D_FEATURE_LEVEL_10_1,
|
|
|
|
_D3D_FEATURE_LEVEL_10_0,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apparently, adapter must be nil if the driver type is not unknown. This is not documented explicitly.
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-d3d11createdevice
|
2023-05-01 12:17:04 +02:00
|
|
|
d, fl, ctx, err := _D3D11CreateDevice(nil, driverType, 0, uint32(flags), featureLevels, true, true)
|
2023-03-27 07:14:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
g.device = (*_ID3D11Device)(d)
|
2023-05-01 12:17:04 +02:00
|
|
|
g.featureLevel = fl
|
2023-03-27 07:14:22 +02:00
|
|
|
g.deviceContext = (*_ID3D11DeviceContext)(ctx)
|
|
|
|
|
2023-05-01 12:17:04 +02:00
|
|
|
// Get IDXGIFactory from the current device and use it, instead of CreateDXGIFactory.
|
|
|
|
// Or, MakeWindowAssociation doesn't work well (#2661).
|
|
|
|
dd, err := g.device.QueryInterface(&_IID_IDXGIDevice)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dxgiDevice := (*_IDXGIDevice)(dd)
|
|
|
|
defer dxgiDevice.Release()
|
|
|
|
|
|
|
|
dxgiAdapter, err := dxgiDevice.GetAdapter()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer dxgiAdapter.Release()
|
|
|
|
|
|
|
|
df, err := dxgiAdapter.GetParent(&_IID_IDXGIFactory)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dxgiFactory := (*_IDXGIFactory)(df)
|
|
|
|
|
2024-03-30 12:08:06 +01:00
|
|
|
gi, err := newGraphicsInfra(dxgiFactory)
|
2023-05-01 12:17:04 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
g.graphicsInfra = gi
|
|
|
|
defer func() {
|
|
|
|
if ferr != nil {
|
|
|
|
g.graphicsInfra.release()
|
|
|
|
g.graphicsInfra = nil
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2023-03-27 07:14:22 +02:00
|
|
|
g.deviceContext.IASetPrimitiveTopology(_D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
|
|
|
|
|
|
|
|
// Set the rasterizer state.
|
|
|
|
if g.rasterizerState == nil {
|
|
|
|
rs, err := g.device.CreateRasterizerState(&_D3D11_RASTERIZER_DESC{
|
|
|
|
FillMode: _D3D11_FILL_SOLID,
|
|
|
|
CullMode: _D3D11_CULL_NONE,
|
|
|
|
FrontCounterClockwise: 0,
|
|
|
|
DepthBias: 0,
|
|
|
|
DepthBiasClamp: 0,
|
|
|
|
SlopeScaledDepthBias: 0,
|
|
|
|
DepthClipEnable: 0,
|
|
|
|
ScissorEnable: 1,
|
|
|
|
MultisampleEnable: 0,
|
|
|
|
AntialiasedLineEnable: 0,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
g.rasterizerState = rs
|
|
|
|
}
|
|
|
|
g.deviceContext.RSSetState(g.rasterizerState)
|
|
|
|
|
|
|
|
// Set the sampler state.
|
|
|
|
if g.samplerState == nil {
|
|
|
|
s, err := g.device.CreateSamplerState(&_D3D11_SAMPLER_DESC{
|
|
|
|
Filter: _D3D11_FILTER_MIN_MAG_MIP_POINT,
|
|
|
|
AddressU: _D3D11_TEXTURE_ADDRESS_WRAP,
|
|
|
|
AddressV: _D3D11_TEXTURE_ADDRESS_WRAP,
|
|
|
|
AddressW: _D3D11_TEXTURE_ADDRESS_WRAP,
|
|
|
|
ComparisonFunc: _D3D11_COMPARISON_NEVER,
|
|
|
|
MinLOD: -math.MaxFloat32,
|
|
|
|
MaxLOD: math.MaxFloat32,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
g.samplerState = s
|
|
|
|
}
|
|
|
|
g.deviceContext.PSSetSamplers(0, []*_ID3D11SamplerState{g.samplerState})
|
|
|
|
|
|
|
|
return g, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) Initialize() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) Begin() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) End(present bool) error {
|
|
|
|
if !present {
|
|
|
|
return nil
|
|
|
|
}
|
2023-04-09 09:21:47 +02:00
|
|
|
|
2023-03-27 07:14:22 +02:00
|
|
|
if err := g.graphicsInfra.present(g.vsyncEnabled); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-04-09 09:21:47 +02:00
|
|
|
|
|
|
|
if g.newScreenWidth != 0 && g.newScreenHeight != 0 {
|
|
|
|
if g.screenImage != nil {
|
|
|
|
// ResizeBuffer requires all the related resources released,
|
|
|
|
// so release the swapchain's buffer.
|
|
|
|
// Do not dispose the screen image itself since the image's ID is still used.
|
|
|
|
g.screenImage.disposeBuffers()
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := g.graphicsInfra.resizeSwapChain(g.newScreenWidth, g.newScreenHeight); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
t, err := g.graphicsInfra.getBuffer(0, &_IID_ID3D11Texture2D)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.screenImage.width = g.newScreenWidth
|
|
|
|
g.screenImage.height = g.newScreenHeight
|
|
|
|
g.screenImage.texture = (*_ID3D11Texture2D)(t)
|
|
|
|
|
|
|
|
g.newScreenWidth = 0
|
|
|
|
g.newScreenHeight = 0
|
|
|
|
}
|
|
|
|
|
2023-03-27 07:14:22 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) SetWindow(window uintptr) {
|
|
|
|
g.window = windows.HWND(window)
|
|
|
|
// TODO: need to update the swap chain?
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) SetTransparent(transparent bool) {
|
|
|
|
// TODO: Implement this?
|
|
|
|
}
|
|
|
|
|
2023-11-03 18:45:16 +01:00
|
|
|
func (g *graphics11) SetVertices(vertices []float32, indices []uint32) error {
|
2023-03-27 07:14:22 +02:00
|
|
|
if size := pow2(uint32(len(vertices)) * uint32(unsafe.Sizeof(vertices[0]))); g.vertexBufferSizeInBytes < size {
|
|
|
|
if g.vertexBuffer != nil {
|
|
|
|
g.vertexBuffer.Release()
|
|
|
|
g.vertexBuffer = nil
|
|
|
|
}
|
|
|
|
b, err := g.device.CreateBuffer(&_D3D11_BUFFER_DESC{
|
|
|
|
ByteWidth: size,
|
|
|
|
Usage: _D3D11_USAGE_DYNAMIC,
|
|
|
|
BindFlags: uint32(_D3D11_BIND_VERTEX_BUFFER),
|
|
|
|
CPUAccessFlags: uint32(_D3D11_CPU_ACCESS_WRITE),
|
|
|
|
}, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.vertexBuffer = b
|
|
|
|
g.vertexBufferSizeInBytes = size
|
|
|
|
g.deviceContext.IASetVertexBuffers(0, []*_ID3D11Buffer{g.vertexBuffer},
|
|
|
|
[]uint32{graphics.VertexFloatCount * uint32(unsafe.Sizeof(vertices[0]))}, []uint32{0})
|
|
|
|
}
|
|
|
|
if size := pow2(uint32(len(indices)) * uint32(unsafe.Sizeof(indices[0]))); g.indexBufferSizeInBytes < size {
|
|
|
|
if g.indexBuffer != nil {
|
|
|
|
g.indexBuffer.Release()
|
|
|
|
g.indexBuffer = nil
|
|
|
|
}
|
|
|
|
b, err := g.device.CreateBuffer(&_D3D11_BUFFER_DESC{
|
|
|
|
ByteWidth: size,
|
|
|
|
Usage: _D3D11_USAGE_DYNAMIC,
|
|
|
|
BindFlags: uint32(_D3D11_BIND_INDEX_BUFFER),
|
|
|
|
CPUAccessFlags: uint32(_D3D11_CPU_ACCESS_WRITE),
|
|
|
|
}, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.indexBuffer = b
|
|
|
|
g.indexBufferSizeInBytes = size
|
2023-11-03 18:45:16 +01:00
|
|
|
g.deviceContext.IASetIndexBuffer(g.indexBuffer, _DXGI_FORMAT_R32_UINT, 0)
|
2023-03-27 07:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the vertices data.
|
|
|
|
{
|
|
|
|
var mapped _D3D11_MAPPED_SUBRESOURCE
|
|
|
|
if err := g.deviceContext.Map(unsafe.Pointer(g.vertexBuffer), 0, _D3D11_MAP_WRITE_DISCARD, 0, &mapped); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
copy(unsafe.Slice((*float32)(mapped.pData), len(vertices)), vertices)
|
|
|
|
g.deviceContext.Unmap(unsafe.Pointer(g.vertexBuffer), 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the indices data.
|
|
|
|
{
|
|
|
|
var mapped _D3D11_MAPPED_SUBRESOURCE
|
|
|
|
if err := g.deviceContext.Map(unsafe.Pointer(g.indexBuffer), 0, _D3D11_MAP_WRITE_DISCARD, 0, &mapped); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-11-03 18:45:16 +01:00
|
|
|
copy(unsafe.Slice((*uint32)(mapped.pData), len(indices)), indices)
|
2023-03-27 07:14:22 +02:00
|
|
|
g.deviceContext.Unmap(unsafe.Pointer(g.indexBuffer), 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) NewImage(width, height int) (graphicsdriver.Image, error) {
|
|
|
|
t, err := g.device.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
|
|
|
|
Width: uint32(graphics.InternalImageSize(width)),
|
|
|
|
Height: uint32(graphics.InternalImageSize(height)),
|
|
|
|
MipLevels: 1, // 0 doesn't work when shrinking the image.
|
|
|
|
ArraySize: 1,
|
|
|
|
Format: _DXGI_FORMAT_R8G8B8A8_UNORM,
|
|
|
|
SampleDesc: _DXGI_SAMPLE_DESC{
|
|
|
|
Count: 1,
|
|
|
|
Quality: 0,
|
|
|
|
},
|
|
|
|
Usage: _D3D11_USAGE_DEFAULT,
|
|
|
|
BindFlags: uint32(_D3D11_BIND_SHADER_RESOURCE | _D3D11_BIND_RENDER_TARGET),
|
|
|
|
CPUAccessFlags: 0,
|
|
|
|
MiscFlags: 0,
|
|
|
|
}, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
i := &image11{
|
|
|
|
graphics: g,
|
|
|
|
id: g.genNextImageID(),
|
|
|
|
width: width,
|
|
|
|
height: height,
|
|
|
|
texture: t,
|
|
|
|
}
|
|
|
|
g.addImage(i)
|
|
|
|
return i, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) NewScreenFramebufferImage(width, height int) (graphicsdriver.Image, error) {
|
2023-04-09 09:49:52 +02:00
|
|
|
imageWidth := width
|
|
|
|
imageHeight := height
|
2023-04-09 08:30:46 +02:00
|
|
|
if g.screenImage != nil {
|
2023-04-09 09:49:52 +02:00
|
|
|
imageWidth = g.screenImage.width
|
|
|
|
imageHeight = g.screenImage.height
|
2023-04-09 08:30:46 +02:00
|
|
|
g.screenImage.Dispose()
|
|
|
|
g.screenImage = nil
|
|
|
|
}
|
|
|
|
|
2023-03-27 07:14:22 +02:00
|
|
|
if g.graphicsInfra.isSwapChainInited() {
|
2023-04-09 09:49:52 +02:00
|
|
|
g.newScreenWidth, g.newScreenHeight = width, height
|
2023-03-27 07:14:22 +02:00
|
|
|
} else {
|
|
|
|
if err := g.graphicsInfra.initSwapChain(width, height, unsafe.Pointer(g.device), g.window); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
t, err := g.graphicsInfra.getBuffer(0, &_IID_ID3D11Texture2D)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
i := &image11{
|
|
|
|
graphics: g,
|
|
|
|
id: g.genNextImageID(),
|
2023-04-09 09:49:52 +02:00
|
|
|
width: imageWidth,
|
|
|
|
height: imageHeight,
|
2023-03-27 07:14:22 +02:00
|
|
|
screen: true,
|
|
|
|
texture: (*_ID3D11Texture2D)(t),
|
|
|
|
}
|
|
|
|
g.addImage(i)
|
|
|
|
g.screenImage = i
|
|
|
|
return i, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) addImage(img *image11) {
|
|
|
|
if g.images == nil {
|
|
|
|
g.images = map[graphicsdriver.ImageID]*image11{}
|
|
|
|
}
|
|
|
|
if _, ok := g.images[img.id]; ok {
|
|
|
|
panic(fmt.Sprintf("directx: image ID %d was already registered", img.id))
|
|
|
|
}
|
|
|
|
g.images[img.id] = img
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) removeImage(image *image11) {
|
|
|
|
delete(g.images, image.id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) SetVsyncEnabled(enabled bool) {
|
|
|
|
g.vsyncEnabled = enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) NeedsClearingScreen() bool {
|
|
|
|
// TODO: Confirm this is really true.
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) MaxImageSize() int {
|
|
|
|
switch g.featureLevel {
|
|
|
|
case _D3D_FEATURE_LEVEL_10_0:
|
|
|
|
return 8192
|
|
|
|
case _D3D_FEATURE_LEVEL_10_1:
|
|
|
|
return 8192
|
|
|
|
case _D3D_FEATURE_LEVEL_11_0:
|
|
|
|
return 16384
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("directx: invalid feature level: 0x%x", g.featureLevel))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) {
|
|
|
|
vs, ps, offsets := hlsl.Compile(program)
|
|
|
|
vsh, psh, err := compileShader(vs, ps)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
s := &shader11{
|
|
|
|
graphics: g,
|
|
|
|
id: g.genNextShaderID(),
|
|
|
|
uniformTypes: program.Uniforms,
|
|
|
|
uniformOffsets: offsets,
|
|
|
|
vertexShaderBlob: vsh,
|
|
|
|
pixelShaderBlob: psh,
|
|
|
|
}
|
|
|
|
g.addShader(s)
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) addShader(s *shader11) {
|
|
|
|
if g.shaders == nil {
|
|
|
|
g.shaders = map[graphicsdriver.ShaderID]*shader11{}
|
|
|
|
}
|
|
|
|
if _, ok := g.shaders[s.id]; ok {
|
|
|
|
panic(fmt.Sprintf("directx: shader ID %d was already registered", s.id))
|
|
|
|
}
|
|
|
|
g.shaders[s.id] = s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) removeShader(s *shader11) {
|
|
|
|
s.disposeImpl()
|
|
|
|
delete(g.shaders, s.id)
|
|
|
|
}
|
|
|
|
|
2023-11-04 10:09:47 +01:00
|
|
|
func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
2023-03-27 07:14:22 +02:00
|
|
|
// Remove bound textures first. This is needed to avoid warnings on the debugger.
|
|
|
|
g.deviceContext.OMSetRenderTargets([]*_ID3D11RenderTargetView{nil}, nil)
|
|
|
|
srvs := [graphics.ShaderImageCount]*_ID3D11ShaderResourceView{}
|
|
|
|
g.deviceContext.PSSetShaderResources(0, srvs[:])
|
|
|
|
|
|
|
|
dst := g.images[dstID]
|
|
|
|
var srcs [graphics.ShaderImageCount]*image11
|
|
|
|
for i, id := range srcIDs {
|
|
|
|
img := g.images[id]
|
|
|
|
if img == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
srcs[i] = img
|
|
|
|
}
|
|
|
|
|
|
|
|
w, h := dst.internalSize()
|
|
|
|
g.deviceContext.RSSetViewports([]_D3D11_VIEWPORT{
|
|
|
|
{
|
|
|
|
TopLeftX: 0,
|
|
|
|
TopLeftY: 0,
|
|
|
|
Width: float32(w),
|
|
|
|
Height: float32(h),
|
|
|
|
MinDepth: 0,
|
|
|
|
MaxDepth: 1,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2023-11-04 10:09:47 +01:00
|
|
|
if err := dst.setAsRenderTarget(fillRule != graphicsdriver.FillAll); err != nil {
|
2023-03-27 07:14:22 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the shader parameters.
|
|
|
|
shader := g.shaders[shaderID]
|
|
|
|
if err := shader.use(uniforms, srcs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-11-04 10:09:47 +01:00
|
|
|
if fillRule == graphicsdriver.FillAll {
|
2023-03-27 07:14:22 +02:00
|
|
|
bs, err := g.blendState(blend, noStencil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.deviceContext.OMSetBlendState(bs, nil, 0xffffffff)
|
|
|
|
|
|
|
|
dss, err := g.depthStencilState(noStencil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.deviceContext.OMSetDepthStencilState(dss, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, dstRegion := range dstRegions {
|
|
|
|
g.deviceContext.RSSetScissorRects([]_D3D11_RECT{
|
|
|
|
{
|
2023-09-28 07:29:55 +02:00
|
|
|
left: int32(dstRegion.Region.Min.X),
|
|
|
|
top: int32(dstRegion.Region.Min.Y),
|
|
|
|
right: int32(dstRegion.Region.Max.X),
|
|
|
|
bottom: int32(dstRegion.Region.Max.Y),
|
2023-03-27 07:14:22 +02:00
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2023-11-04 10:09:47 +01:00
|
|
|
switch fillRule {
|
|
|
|
case graphicsdriver.FillAll:
|
|
|
|
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
2023-11-06 01:18:08 +01:00
|
|
|
case graphicsdriver.NonZero:
|
|
|
|
bs, err := g.blendState(blend, incrementStencil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.deviceContext.OMSetBlendState(bs, nil, 0xffffffff)
|
|
|
|
dss, err := g.depthStencilState(incrementStencil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.deviceContext.OMSetDepthStencilState(dss, 0)
|
|
|
|
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
2023-11-04 10:09:47 +01:00
|
|
|
case graphicsdriver.EvenOdd:
|
2023-11-06 01:18:08 +01:00
|
|
|
bs, err := g.blendState(blend, invertStencil)
|
2023-03-27 07:14:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.deviceContext.OMSetBlendState(bs, nil, 0xffffffff)
|
2023-11-06 01:18:08 +01:00
|
|
|
dss, err := g.depthStencilState(invertStencil)
|
2023-03-27 07:14:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.deviceContext.OMSetDepthStencilState(dss, 0)
|
|
|
|
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
2023-11-06 01:18:08 +01:00
|
|
|
}
|
2023-03-27 07:14:22 +02:00
|
|
|
|
2023-11-06 01:18:08 +01:00
|
|
|
if fillRule != graphicsdriver.FillAll {
|
|
|
|
bs, err := g.blendState(blend, drawWithStencil)
|
2023-03-27 07:14:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.deviceContext.OMSetBlendState(bs, nil, 0xffffffff)
|
2023-11-06 01:18:08 +01:00
|
|
|
dss, err := g.depthStencilState(drawWithStencil)
|
2023-03-27 07:14:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.deviceContext.OMSetDepthStencilState(dss, 0)
|
|
|
|
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
indexOffset += dstRegion.IndexCount
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) genNextImageID() graphicsdriver.ImageID {
|
|
|
|
g.nextImageID++
|
|
|
|
return g.nextImageID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) genNextShaderID() graphicsdriver.ShaderID {
|
|
|
|
g.nextShaderID++
|
|
|
|
return g.nextShaderID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) blendState(blend graphicsdriver.Blend, stencilMode stencilMode) (*_ID3D11BlendState, error) {
|
2023-11-06 01:18:08 +01:00
|
|
|
var writeMask uint8
|
|
|
|
if stencilMode == noStencil || stencilMode == drawWithStencil {
|
|
|
|
writeMask = uint8(_D3D11_COLOR_WRITE_ENABLE_ALL)
|
2023-03-27 07:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
key := blendStateKey{
|
|
|
|
blend: blend,
|
|
|
|
writeMask: writeMask,
|
|
|
|
}
|
|
|
|
if bs, ok := g.blendStates[key]; ok {
|
|
|
|
return bs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
bs, err := g.device.CreateBlendState(&_D3D11_BLEND_DESC{
|
|
|
|
AlphaToCoverageEnable: 0,
|
|
|
|
IndependentBlendEnable: 0,
|
|
|
|
RenderTarget: [8]_D3D11_RENDER_TARGET_BLEND_DESC{
|
|
|
|
{
|
|
|
|
BlendEnable: 1,
|
|
|
|
SrcBlend: blendFactorToBlend11(blend.BlendFactorSourceRGB, false),
|
|
|
|
DestBlend: blendFactorToBlend11(blend.BlendFactorDestinationRGB, false),
|
|
|
|
BlendOp: blendOperationToBlendOp11(blend.BlendOperationRGB),
|
|
|
|
SrcBlendAlpha: blendFactorToBlend11(blend.BlendFactorSourceAlpha, true),
|
|
|
|
DestBlendAlpha: blendFactorToBlend11(blend.BlendFactorDestinationAlpha, true),
|
|
|
|
BlendOpAlpha: blendOperationToBlendOp11(blend.BlendOperationAlpha),
|
|
|
|
RenderTargetWriteMask: writeMask,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if g.blendStates == nil {
|
|
|
|
g.blendStates = map[blendStateKey]*_ID3D11BlendState{}
|
|
|
|
}
|
|
|
|
g.blendStates[key] = bs
|
|
|
|
return bs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *graphics11) depthStencilState(mode stencilMode) (*_ID3D11DepthStencilState, error) {
|
|
|
|
if s, ok := g.depthStencilStates[mode]; ok {
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
desc := &_D3D11_DEPTH_STENCIL_DESC{
|
|
|
|
DepthEnable: 0,
|
|
|
|
DepthWriteMask: _D3D11_DEPTH_WRITE_MASK_ALL,
|
|
|
|
DepthFunc: _D3D11_COMPARISON_LESS,
|
|
|
|
StencilEnable: 0,
|
|
|
|
StencilReadMask: _D3D11_DEFAULT_STENCIL_READ_MASK,
|
|
|
|
StencilWriteMask: _D3D11_DEFAULT_STENCIL_WRITE_MASK,
|
|
|
|
FrontFace: _D3D11_DEPTH_STENCILOP_DESC{
|
|
|
|
StencilFailOp: _D3D11_STENCIL_OP_KEEP,
|
|
|
|
StencilDepthFailOp: _D3D11_STENCIL_OP_KEEP,
|
|
|
|
StencilPassOp: _D3D11_STENCIL_OP_KEEP,
|
|
|
|
StencilFunc: _D3D11_COMPARISON_ALWAYS,
|
|
|
|
},
|
|
|
|
BackFace: _D3D11_DEPTH_STENCILOP_DESC{
|
|
|
|
StencilFailOp: _D3D11_STENCIL_OP_KEEP,
|
|
|
|
StencilDepthFailOp: _D3D11_STENCIL_OP_KEEP,
|
|
|
|
StencilPassOp: _D3D11_STENCIL_OP_KEEP,
|
|
|
|
StencilFunc: _D3D11_COMPARISON_ALWAYS,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
switch mode {
|
2023-11-06 01:18:08 +01:00
|
|
|
case incrementStencil:
|
|
|
|
desc.StencilEnable = 1
|
|
|
|
desc.FrontFace.StencilPassOp = _D3D11_STENCIL_OP_INCR
|
|
|
|
desc.BackFace.StencilPassOp = _D3D11_STENCIL_OP_DECR
|
|
|
|
case invertStencil:
|
2023-03-27 07:14:22 +02:00
|
|
|
desc.StencilEnable = 1
|
|
|
|
desc.FrontFace.StencilPassOp = _D3D11_STENCIL_OP_INVERT
|
|
|
|
desc.BackFace.StencilPassOp = _D3D11_STENCIL_OP_INVERT
|
|
|
|
case drawWithStencil:
|
|
|
|
desc.StencilEnable = 1
|
|
|
|
desc.FrontFace.StencilFunc = _D3D11_COMPARISON_NOT_EQUAL
|
|
|
|
desc.BackFace.StencilFunc = _D3D11_COMPARISON_NOT_EQUAL
|
|
|
|
}
|
|
|
|
|
|
|
|
s, err := g.device.CreateDepthStencilState(desc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if g.depthStencilStates == nil {
|
|
|
|
g.depthStencilStates = map[stencilMode]*_ID3D11DepthStencilState{}
|
|
|
|
}
|
|
|
|
g.depthStencilStates[mode] = s
|
|
|
|
return s, nil
|
|
|
|
}
|