vendor dependencies, make some changes to how input is done

This commit is contained in:
2026-06-15 18:54:00 +02:00
parent 535130933c
commit 4800eb28d9
759 changed files with 360941 additions and 30 deletions
@@ -0,0 +1,54 @@
// 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.
//go:build 386 || arm
package directx
import (
"unsafe"
)
type _D3D12_DEPTH_STENCIL_VIEW_DESC struct {
Format _DXGI_FORMAT
ViewDimension _D3D12_DSV_DIMENSION
Flags _D3D12_DSV_FLAGS
Texture2D _D3D12_TEX2D_DSV // Union
_ [12 - unsafe.Sizeof(_D3D12_TEX2D_DSV{})]byte // Padding for union
}
type _D3D12_RESOURCE_DESC struct {
Dimension _D3D12_RESOURCE_DIMENSION
_ [4]byte // Padding
Alignment uint64
Width uint64
Height uint32
DepthOrArraySize uint16
MipLevels uint16
Format _DXGI_FORMAT
SampleDesc _DXGI_SAMPLE_DESC
Layout _D3D12_TEXTURE_LAYOUT
Flags _D3D12_RESOURCE_FLAGS
// This is a pseudo padding which D3D12_RESOURCE_DESC1 would use.
// Mysteriously, some functions don't work correctly without this hack (#2867).
_ [12]byte
}
type _D3D12_ROOT_PARAMETER struct {
ParameterType _D3D12_ROOT_PARAMETER_TYPE
DescriptorTable _D3D12_ROOT_DESCRIPTOR_TABLE // Union
_ [4]byte // Padding
ShaderVisibility _D3D12_SHADER_VISIBILITY
}
@@ -0,0 +1,49 @@
// 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.
//go:build amd64 || arm64
package directx
import (
"unsafe"
)
type _D3D12_DEPTH_STENCIL_VIEW_DESC struct {
Format _DXGI_FORMAT
ViewDimension _D3D12_DSV_DIMENSION
Flags _D3D12_DSV_FLAGS
_ [4]byte // Padding
Texture2D _D3D12_TEX2D_DSV // Union
_ [12 - unsafe.Sizeof(_D3D12_TEX2D_DSV{})]byte // Padding for union
}
type _D3D12_RESOURCE_DESC struct {
Dimension _D3D12_RESOURCE_DIMENSION
Alignment uint64
Width uint64
Height uint32
DepthOrArraySize uint16
MipLevels uint16
Format _DXGI_FORMAT
SampleDesc _DXGI_SAMPLE_DESC
Layout _D3D12_TEXTURE_LAYOUT
Flags _D3D12_RESOURCE_FLAGS
}
type _D3D12_ROOT_PARAMETER struct {
ParameterType _D3D12_ROOT_PARAMETER_TYPE
DescriptorTable _D3D12_ROOT_DESCRIPTOR_TABLE // Union
ShaderVisibility _D3D12_SHADER_VISIBILITY
}
@@ -0,0 +1,228 @@
// Copyright 2022 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.
//go:build microsoftgdk
package directx
// Some functions of ID3D12GraphicsCommandList has additional logics besides the original COM function call.
// Then, instead of calling them by LazyProc.Call, we have to defer this call to the C++ side.
// These functions are chosen based on the DirectX header file's implementation.
//
// These functions should be defined on the C++ side like this:
//
// extern "C" {
// void Ebitengine_ID3D12GraphicsCommandList_ClearDepthStencilView(void* i, uintptr_t depthStencilView, int32_t clearFlags, float depth, uint8_t stencil, uint32_t numRects, void* pRects) {
// static_cast<ID3D12GraphicsCommandList*>(i)->ClearDepthStencilView(D3D12_CPU_DESCRIPTOR_HANDLE{ depthStencilView }, static_cast<D3D12_CLEAR_FLAGS>(clearFlags), depth, stencil, numRects, static_cast<D3D12_RECT*>(pRects));
// }
// void Ebitengine_ID3D12GraphicsCommandList_ClearRenderTargetView(void* i, uintptr_t pRenderTargetView, void* colorRGBA, uint32_t numRects, void* pRects) {
// static_cast<ID3D12GraphicsCommandList*>(i)->ClearRenderTargetView(D3D12_CPU_DESCRIPTOR_HANDLE{ pRenderTargetView }, static_cast<FLOAT*>(colorRGBA), numRects, static_cast<D3D12_RECT*>(pRects));
// }
// uintptr_t Ebitengine_ID3D12GraphicsCommandList_Close(void* i) {
// auto r = static_cast<ID3D12GraphicsCommandList*>(i)->Close();
// return uintptr_t(r);
// }
// void Ebitengine_ID3D12GraphicsCommandList_CopyTextureRegion(void* i, void* pDst, uint32_t dstX, uint32_t dstY, uint32_t dstZ, void* pSrc, void* pSrcBox) {
// static_cast<ID3D12GraphicsCommandList*>(i)->CopyTextureRegion(static_cast<D3D12_TEXTURE_COPY_LOCATION*>(pDst), dstX, dstY, dstZ, static_cast<D3D12_TEXTURE_COPY_LOCATION*>(pSrc), static_cast<D3D12_BOX*>(pSrcBox));
// }
// void Ebitengine_ID3D12GraphicsCommandList_DrawIndexedInstanced(void* i, uint32_t indexCountPerInstance, uint32_t instanceCount, uint32_t startIndexLocation, int32_t baseVertexLocation, uint32_t startInstanceLocation) {
// static_cast<ID3D12GraphicsCommandList*>(i)->DrawIndexedInstanced(indexCountPerInstance, instanceCount, startIndexLocation, baseVertexLocation, startInstanceLocation);
// }
// void Ebitengine_ID3D12GraphicsCommandList_IASetIndexBuffer(void* i, void* pView) {
// static_cast<ID3D12GraphicsCommandList*>(i)->IASetIndexBuffer(static_cast<D3D12_INDEX_BUFFER_VIEW*>(pView));
// }
// void Ebitengine_ID3D12GraphicsCommandList_IASetPrimitiveTopology(void* i, int32_t primitiveTopology) {
// static_cast<ID3D12GraphicsCommandList*>(i)->IASetPrimitiveTopology(static_cast<D3D12_PRIMITIVE_TOPOLOGY>(primitiveTopology));
// }
// void Ebitengine_ID3D12GraphicsCommandList_IASetVertexBuffers(void* i, uint32_t startSlot, uint32_t numViews, void* pViews) {
// static_cast<ID3D12GraphicsCommandList*>(i)->IASetVertexBuffers(startSlot, numViews, static_cast<D3D12_VERTEX_BUFFER_VIEW*>(pViews));
// }
// void Ebitengine_ID3D12GraphicsCommandList_OMSetRenderTargets(void* i, uint32_t numRenderTargetDescriptors, void* pRenderTargetDescriptors, int rtsSingleHandleToDescriptorRange, void* pDepthStencilDescriptor) {
// static_cast<ID3D12GraphicsCommandList*>(i)->OMSetRenderTargets(numRenderTargetDescriptors, static_cast<D3D12_CPU_DESCRIPTOR_HANDLE*>(pRenderTargetDescriptors), static_cast<BOOL>(rtsSingleHandleToDescriptorRange), static_cast<D3D12_CPU_DESCRIPTOR_HANDLE*>(pDepthStencilDescriptor));
// }
// void Ebitengine_ID3D12GraphicsCommandList_OMSetStencilRef(void* i, uint32_t stencilRef) {
// static_cast<ID3D12GraphicsCommandList*>(i)->OMSetStencilRef(stencilRef);
// }
// uint32_t Ebitengine_ID3D12GraphicsCommandList_Release(void* i) {
// return static_cast<uint32_t>(static_cast<ID3D12GraphicsCommandList*>(i)->Release());
// }
// uintptr_t Ebitengine_ID3D12GraphicsCommandList_Reset(void* i, void* pAllocator, void* pInitialState) {
// auto r = static_cast<ID3D12GraphicsCommandList*>(i)->Reset(static_cast<ID3D12CommandAllocator*>(pAllocator), static_cast<ID3D12PipelineState*>(pInitialState));
// return static_cast<uintptr_t>(r);
// }
// void Ebitengine_ID3D12GraphicsCommandList_ResourceBarrier(void* i, uint32_t numBarriers, void* pBarriers) {
// static_cast<ID3D12GraphicsCommandList*>(i)->ResourceBarrier(numBarriers, static_cast<D3D12_RESOURCE_BARRIER*>(pBarriers));
// }
// void Ebitengine_ID3D12GraphicsCommandList_RSSetViewports(void* i, uint32_t numViewports, void* pViewports) {
// static_cast<ID3D12GraphicsCommandList*>(i)->RSSetViewports(numViewports, static_cast<D3D12_VIEWPORT*>(pViewports));
// }
// void Ebitengine_ID3D12GraphicsCommandList_RSSetScissorRects(void* i, uint32_t numRects, void* pRects) {
// static_cast<ID3D12GraphicsCommandList*>(i)->RSSetScissorRects(numRects, static_cast<D3D12_RECT*>(pRects));
// }
// void Ebitengine_ID3D12GraphicsCommandList_SetDescriptorHeaps(void* i, uint32_t numDescriptorHeaps, void* ppDescriptorHeaps) {
// static_cast<ID3D12GraphicsCommandList*>(i)->SetDescriptorHeaps(numDescriptorHeaps, static_cast<ID3D12DescriptorHeap**>(ppDescriptorHeaps));
// }
// void Ebitengine_ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(void* i, uint32_t rootParameterIndex, uint64_t baseDescriptorPtr) {
// static_cast<ID3D12GraphicsCommandList*>(i)->SetGraphicsRootDescriptorTable(rootParameterIndex, D3D12_GPU_DESCRIPTOR_HANDLE{ baseDescriptorPtr });
// }
// void Ebitengine_ID3D12GraphicsCommandList_SetGraphicsRootSignature(void* i, void* pRootSignature) {
// static_cast<ID3D12GraphicsCommandList*>(i)->SetGraphicsRootSignature(static_cast<ID3D12RootSignature*>(pRootSignature));
// }
// void Ebitengine_ID3D12GraphicsCommandList_SetPipelineState(void* i, void* pPipelineState) {
// static_cast<ID3D12GraphicsCommandList*>(i)->SetPipelineState(static_cast<ID3D12PipelineState*>(pPipelineState));
// }
// }
// #include <stdint.h>
//
// void Ebitengine_ID3D12GraphicsCommandList_ClearDepthStencilView(void* i, uintptr_t depthStencilView, int32_t clearFlags, float depth, uint8_t stencil, uint32_t numRects, void* pRects);
// void Ebitengine_ID3D12GraphicsCommandList_ClearRenderTargetView(void* i, uintptr_t pRenderTargetView, void* colorRGBA, uint32_t numRects, void* pRects);
// uintptr_t Ebitengine_ID3D12GraphicsCommandList_Close(void* i);
// void Ebitengine_ID3D12GraphicsCommandList_CopyTextureRegion(void* i, void* pDst, uint32_t dstX, uint32_t dstY, uint32_t dstZ, void* pSrc, void* pSrcBox);
// void Ebitengine_ID3D12GraphicsCommandList_DrawIndexedInstanced(void* i, uint32_t indexCountPerInstance, uint32_t instanceCount, uint32_t startIndexLocation, int32_t baseVertexLocation, uint32_t startInstanceLocation);
// void Ebitengine_ID3D12GraphicsCommandList_IASetIndexBuffer(void* i, void* pView);
// void Ebitengine_ID3D12GraphicsCommandList_IASetPrimitiveTopology(void* i, int32_t primitiveTopology);
// void Ebitengine_ID3D12GraphicsCommandList_IASetVertexBuffers(void* i, uint32_t startSlot, uint32_t numViews, void* pViews);
// void Ebitengine_ID3D12GraphicsCommandList_OMSetRenderTargets(void* i, uint32_t numRenderTargetDescriptors, void* pRenderTargetDescriptors, int rtsSingleHandleToDescriptorRange, void* pDepthStencilDescriptor);
// void Ebitengine_ID3D12GraphicsCommandList_OMSetStencilRef(void* i, uint32_t stencilRef);
// uint32_t Ebitengine_ID3D12GraphicsCommandList_Release(void* i);
// uintptr_t Ebitengine_ID3D12GraphicsCommandList_Reset(void* i, void* pAllocator, void* pInitialState);
// void Ebitengine_ID3D12GraphicsCommandList_ResourceBarrier(void* i, uint32_t numBarriers, void* pBarriers);
// void Ebitengine_ID3D12GraphicsCommandList_RSSetViewports(void* i, uint32_t numViewports, void* pViewports);
// void Ebitengine_ID3D12GraphicsCommandList_RSSetScissorRects(void* i, uint32_t numRects, void* pRects);
// void Ebitengine_ID3D12GraphicsCommandList_SetDescriptorHeaps(void* i, uint32_t numDescriptorHeaps, void* ppDescriptorHeaps);
// void Ebitengine_ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(void* i, uint32_t rootParameterIndex, uint64_t baseDescriptorPtr);
// void Ebitengine_ID3D12GraphicsCommandList_SetGraphicsRootSignature(void* i, void* pRootSignature);
// void Ebitengine_ID3D12GraphicsCommandList_SetPipelineState(void* i, void* pPipelineState);
import "C"
import (
"unsafe"
)
func _ID3D12GraphicsCommandList_ClearDepthStencilView(i *_ID3D12GraphicsCommandList, depthStencilView _D3D12_CPU_DESCRIPTOR_HANDLE, clearFlags _D3D12_CLEAR_FLAGS, depth float32, stencil uint8, rects []_D3D12_RECT) {
var pRects *_D3D12_RECT
if len(rects) > 0 {
pRects = &rects[0]
}
C.Ebitengine_ID3D12GraphicsCommandList_ClearDepthStencilView(unsafe.Pointer(i), C.uintptr_t(depthStencilView.ptr), C.int32_t(clearFlags), C.float(depth), C.uint8_t(stencil), C.uint32_t(len(rects)), unsafe.Pointer(pRects))
}
func _ID3D12GraphicsCommandList_ClearRenderTargetView(i *_ID3D12GraphicsCommandList, pRenderTargetView _D3D12_CPU_DESCRIPTOR_HANDLE, colorRGBA [4]float32, rects []_D3D12_RECT) {
var pRects *_D3D12_RECT
if len(rects) > 0 {
pRects = &rects[0]
}
C.Ebitengine_ID3D12GraphicsCommandList_ClearRenderTargetView(unsafe.Pointer(i), C.uintptr_t(pRenderTargetView.ptr), unsafe.Pointer(&colorRGBA[0]), C.uint32_t(len(rects)), unsafe.Pointer(pRects))
}
func _ID3D12GraphicsCommandList_Close(i *_ID3D12GraphicsCommandList) uintptr {
r := C.Ebitengine_ID3D12GraphicsCommandList_Close(unsafe.Pointer(i))
return uintptr(r)
}
func _ID3D12GraphicsCommandList_CopyTextureRegion(i *_ID3D12GraphicsCommandList, pDst unsafe.Pointer, dstX uint32, dstY uint32, dstZ uint32, pSrc unsafe.Pointer, pSrcBox *_D3D12_BOX) {
C.Ebitengine_ID3D12GraphicsCommandList_CopyTextureRegion(unsafe.Pointer(i), pDst, C.uint32_t(dstX), C.uint32_t(dstY), C.uint32_t(dstZ), pSrc, unsafe.Pointer(pSrcBox))
}
func _ID3D12GraphicsCommandList_DrawIndexedInstanced(i *_ID3D12GraphicsCommandList, indexCountPerInstance uint32, instanceCount uint32, startIndexLocation uint32, baseVertexLocation int32, startInstanceLocation uint32) {
C.Ebitengine_ID3D12GraphicsCommandList_DrawIndexedInstanced(unsafe.Pointer(i),
C.uint32_t(indexCountPerInstance), C.uint32_t(instanceCount), C.uint32_t(startIndexLocation),
C.int32_t(baseVertexLocation), C.uint32_t(startInstanceLocation))
}
func _ID3D12GraphicsCommandList_IASetIndexBuffer(i *_ID3D12GraphicsCommandList, pView *_D3D12_INDEX_BUFFER_VIEW) {
C.Ebitengine_ID3D12GraphicsCommandList_IASetIndexBuffer(unsafe.Pointer(i), unsafe.Pointer(pView))
}
func _ID3D12GraphicsCommandList_IASetPrimitiveTopology(i *_ID3D12GraphicsCommandList, primitiveTopology _D3D_PRIMITIVE_TOPOLOGY) {
C.Ebitengine_ID3D12GraphicsCommandList_IASetPrimitiveTopology(unsafe.Pointer(i), C.int32_t(primitiveTopology))
}
func _ID3D12GraphicsCommandList_IASetVertexBuffers(i *_ID3D12GraphicsCommandList, startSlot uint32, views []_D3D12_VERTEX_BUFFER_VIEW) {
var pViews *_D3D12_VERTEX_BUFFER_VIEW
if len(views) > 0 {
pViews = &views[0]
}
C.Ebitengine_ID3D12GraphicsCommandList_IASetVertexBuffers(unsafe.Pointer(i), C.uint32_t(startSlot), C.uint32_t(len(views)), unsafe.Pointer(pViews))
}
func _ID3D12GraphicsCommandList_OMSetRenderTargets(i *_ID3D12GraphicsCommandList, renderTargetDescriptors []_D3D12_CPU_DESCRIPTOR_HANDLE, rtsSingleHandleToDescriptorRange bool, pDepthStencilDescriptor *_D3D12_CPU_DESCRIPTOR_HANDLE) {
var pRenderTargetDescriptors *_D3D12_CPU_DESCRIPTOR_HANDLE
if len(renderTargetDescriptors) > 0 {
pRenderTargetDescriptors = &renderTargetDescriptors[0]
}
v := 0
if rtsSingleHandleToDescriptorRange {
v = 1
}
C.Ebitengine_ID3D12GraphicsCommandList_OMSetRenderTargets(unsafe.Pointer(i), C.uint32_t(len(renderTargetDescriptors)), unsafe.Pointer(pRenderTargetDescriptors), C.int(v), unsafe.Pointer(pDepthStencilDescriptor))
}
func _ID3D12GraphicsCommandList_OMSetStencilRef(i *_ID3D12GraphicsCommandList, stencilRef uint32) {
C.Ebitengine_ID3D12GraphicsCommandList_OMSetStencilRef(unsafe.Pointer(i), C.uint32_t(stencilRef))
}
func _ID3D12GraphicsCommandList_Release(i *_ID3D12GraphicsCommandList) uint32 {
return uint32(C.Ebitengine_ID3D12GraphicsCommandList_Release(unsafe.Pointer(i)))
}
func _ID3D12GraphicsCommandList_Reset(i *_ID3D12GraphicsCommandList, pAllocator *_ID3D12CommandAllocator, pInitialState *_ID3D12PipelineState) uintptr {
r := C.Ebitengine_ID3D12GraphicsCommandList_Reset(unsafe.Pointer(i), unsafe.Pointer(pAllocator), unsafe.Pointer(pInitialState))
return uintptr(r)
}
func _ID3D12GraphicsCommandList_ResourceBarrier(i *_ID3D12GraphicsCommandList, barriers []_D3D12_RESOURCE_BARRIER_Transition) {
var pBarriers *_D3D12_RESOURCE_BARRIER_Transition
if len(barriers) > 0 {
pBarriers = &barriers[0]
}
C.Ebitengine_ID3D12GraphicsCommandList_ResourceBarrier(unsafe.Pointer(i), C.uint32_t(len(barriers)), unsafe.Pointer(pBarriers))
}
func _ID3D12GraphicsCommandList_RSSetViewports(i *_ID3D12GraphicsCommandList, viewports []_D3D12_VIEWPORT) {
var pViewports *_D3D12_VIEWPORT
if len(viewports) > 0 {
pViewports = &viewports[0]
}
C.Ebitengine_ID3D12GraphicsCommandList_RSSetViewports(unsafe.Pointer(i), C.uint32_t(len(viewports)), unsafe.Pointer(pViewports))
}
func _ID3D12GraphicsCommandList_RSSetScissorRects(i *_ID3D12GraphicsCommandList, rects []_D3D12_RECT) {
var pRects *_D3D12_RECT
if len(rects) > 0 {
pRects = &rects[0]
}
C.Ebitengine_ID3D12GraphicsCommandList_RSSetScissorRects(unsafe.Pointer(i), C.uint32_t(len(rects)), unsafe.Pointer(pRects))
}
func _ID3D12GraphicsCommandList_SetDescriptorHeaps(i *_ID3D12GraphicsCommandList, descriptorHeaps []*_ID3D12DescriptorHeap) {
var ppDescriptorHeaps **_ID3D12DescriptorHeap
if len(descriptorHeaps) > 0 {
ppDescriptorHeaps = &descriptorHeaps[0]
}
C.Ebitengine_ID3D12GraphicsCommandList_SetDescriptorHeaps(unsafe.Pointer(i), C.uint32_t(len(descriptorHeaps)), unsafe.Pointer(ppDescriptorHeaps))
}
func _ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(i *_ID3D12GraphicsCommandList, rootParameterIndex uint32, baseDescriptor _D3D12_GPU_DESCRIPTOR_HANDLE) {
C.Ebitengine_ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(unsafe.Pointer(i), C.uint32_t(rootParameterIndex), C.uint64_t(baseDescriptor.ptr))
}
func _ID3D12GraphicsCommandList_SetGraphicsRootSignature(i *_ID3D12GraphicsCommandList, pRootSignature *_ID3D12RootSignature) {
C.Ebitengine_ID3D12GraphicsCommandList_SetGraphicsRootSignature(unsafe.Pointer(i), unsafe.Pointer(pRootSignature))
}
func _ID3D12GraphicsCommandList_SetPipelineState(i *_ID3D12GraphicsCommandList, pPipelineState *_ID3D12PipelineState) {
C.Ebitengine_ID3D12GraphicsCommandList_SetPipelineState(unsafe.Pointer(i), unsafe.Pointer(pPipelineState))
}
@@ -0,0 +1,97 @@
// Copyright 2022 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.
//go:build !microsoftgdk
package directx
import (
"unsafe"
)
func _ID3D12GraphicsCommandList_ClearDepthStencilView(i *_ID3D12GraphicsCommandList, depthStencilView _D3D12_CPU_DESCRIPTOR_HANDLE, clearFlags _D3D12_CLEAR_FLAGS, depth float32, stencil uint8, rects []_D3D12_RECT) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_ClearRenderTargetView(i *_ID3D12GraphicsCommandList, pRenderTargetView _D3D12_CPU_DESCRIPTOR_HANDLE, colorRGBA [4]float32, rects []_D3D12_RECT) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_Close(i *_ID3D12GraphicsCommandList) uintptr {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_CopyTextureRegion(i *_ID3D12GraphicsCommandList, pDst unsafe.Pointer, dstX uint32, dstY uint32, dstZ uint32, pSrc unsafe.Pointer, pSrcBox *_D3D12_BOX) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_DrawIndexedInstanced(i *_ID3D12GraphicsCommandList, indexCountPerInstance uint32, instanceCount uint32, startIndexLocation uint32, baseVertexLocation int32, startInstanceLocation uint32) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_IASetIndexBuffer(i *_ID3D12GraphicsCommandList, pView *_D3D12_INDEX_BUFFER_VIEW) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_IASetPrimitiveTopology(i *_ID3D12GraphicsCommandList, primitiveTopology _D3D_PRIMITIVE_TOPOLOGY) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_IASetVertexBuffers(i *_ID3D12GraphicsCommandList, startSlot uint32, pViews []_D3D12_VERTEX_BUFFER_VIEW) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_OMSetRenderTargets(i *_ID3D12GraphicsCommandList, renderTargetDescriptors []_D3D12_CPU_DESCRIPTOR_HANDLE, rtsSingleHandleToDescriptorRange bool, pDepthStencilDescriptor *_D3D12_CPU_DESCRIPTOR_HANDLE) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_OMSetStencilRef(i *_ID3D12GraphicsCommandList, stencilRef uint32) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_Release(i *_ID3D12GraphicsCommandList) uint32 {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_Reset(i *_ID3D12GraphicsCommandList, pAllocator *_ID3D12CommandAllocator, pInitialState *_ID3D12PipelineState) uintptr {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_ResourceBarrier(i *_ID3D12GraphicsCommandList, barriers []_D3D12_RESOURCE_BARRIER_Transition) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_RSSetViewports(i *_ID3D12GraphicsCommandList, viewports []_D3D12_VIEWPORT) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_RSSetScissorRects(i *_ID3D12GraphicsCommandList, rects []_D3D12_RECT) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_SetDescriptorHeaps(i *_ID3D12GraphicsCommandList, descriptorHeaps []*_ID3D12DescriptorHeap) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(i *_ID3D12GraphicsCommandList, rootParameterIndex uint32, baseDescriptor _D3D12_GPU_DESCRIPTOR_HANDLE) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_SetGraphicsRootSignature(i *_ID3D12GraphicsCommandList, pRootSignature *_ID3D12RootSignature) {
panic("not implemented")
}
func _ID3D12GraphicsCommandList_SetPipelineState(i *_ID3D12GraphicsCommandList, pPipelineState *_ID3D12PipelineState) {
panic("not implemented")
}
@@ -0,0 +1,60 @@
// 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"
"unsafe"
"golang.org/x/sys/windows"
)
const is64bit = unsafe.Sizeof(uintptr(0)) == 8
type handleError windows.Handle
func (h handleError) Error() string {
return fmt.Sprintf("HANDLE(%d)", h)
}
type (
_BOOL int32
)
func boolToUintptr(v bool) uintptr {
if v {
return 1
}
return 0
}
type _PAPPSTATE_CHANGE_ROUTINE func(quiesced bool, context unsafe.Pointer) uintptr
var (
// https://github.com/MicrosoftDocs/sdk-api/blob/docs/sdk-api-src/content/appnotify/nf-appnotify-registerappstatechangenotification.md
appnotify = windows.NewLazySystemDLL("API-MS-Win-Core-psm-appnotify-l1-1-0.dll")
procRegisterAppStateChangeNotification = appnotify.NewProc("RegisterAppStateChangeNotification")
)
func _RegisterAppStateChangeNotification(routine _PAPPSTATE_CHANGE_ROUTINE, context unsafe.Pointer) (unsafe.Pointer, error) {
cb := windows.NewCallback(routine)
var registration unsafe.Pointer
r, _, _ := procRegisterAppStateChangeNotification.Call(cb, uintptr(context), uintptr(unsafe.Pointer(&registration)))
if windows.Errno(r) != windows.ERROR_SUCCESS {
return nil, fmt.Errorf("directx: RegisterAppStateChangeNotification failed: %w", windows.Errno(r))
}
return registration, nil
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,180 @@
// 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"
"runtime"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// Reference:
// * https://github.com/wine-mirror/wine/blob/master/include/d3dcommon.idl
type _D3DCOMPILE uint32
const (
_D3DCOMPILE_OPTIMIZATION_LEVEL3 _D3DCOMPILE = (1 << 15)
)
type _D3D_DRIVER_TYPE int32
const (
_D3D_DRIVER_TYPE_UNKNOWN _D3D_DRIVER_TYPE = iota
_D3D_DRIVER_TYPE_HARDWARE
_D3D_DRIVER_TYPE_REFERENCE
_D3D_DRIVER_TYPE_NULL
_D3D_DRIVER_TYPE_SOFTWARE
_D3D_DRIVER_TYPE_WARP
)
type _D3D_FEATURE_LEVEL int32
const (
_D3D_FEATURE_LEVEL_9_1 _D3D_FEATURE_LEVEL = 0x9100
_D3D_FEATURE_LEVEL_9_2 _D3D_FEATURE_LEVEL = 0x9200
_D3D_FEATURE_LEVEL_9_3 _D3D_FEATURE_LEVEL = 0x9300
_D3D_FEATURE_LEVEL_10_0 _D3D_FEATURE_LEVEL = 0xa000
_D3D_FEATURE_LEVEL_10_1 _D3D_FEATURE_LEVEL = 0xa100
_D3D_FEATURE_LEVEL_11_0 _D3D_FEATURE_LEVEL = 0xb000
_D3D_FEATURE_LEVEL_11_1 _D3D_FEATURE_LEVEL = 0xb100
_D3D_FEATURE_LEVEL_12_0 _D3D_FEATURE_LEVEL = 0xc000
_D3D_FEATURE_LEVEL_12_1 _D3D_FEATURE_LEVEL = 0xc100
_D3D_FEATURE_LEVEL_12_2 _D3D_FEATURE_LEVEL = 0xc200
)
type _D3D_PRIMITIVE_TOPOLOGY int32
const (
_D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST _D3D_PRIMITIVE_TOPOLOGY = 4
)
type _D3D_ROOT_SIGNATURE_VERSION int32
const (
_D3D_ROOT_SIGNATURE_VERSION_1_0 _D3D_ROOT_SIGNATURE_VERSION = 0x1
)
var (
procD3DCompile *windows.LazyProc
)
func init() {
var d3dcompiler *windows.LazyDLL
// Enumerate possible DLL names for d3dcompiler_*.dll.
// https://walbourn.github.io/hlsl-fxc-and-d3dcompile/
for _, name := range []string{"d3dcompiler_47.dll", "d3dcompiler_46.dll", "d3dcompiler_43.dll"} {
dll := windows.NewLazySystemDLL(name)
if err := dll.Load(); err != nil {
continue
}
d3dcompiler = dll
break
}
if d3dcompiler == nil {
return
}
procD3DCompile = d3dcompiler.NewProc("D3DCompile")
}
func isD3DCompilerDLLAvailable() bool {
return procD3DCompile != nil
}
func _D3DCompile(srcData []byte, sourceName string, pDefines []_D3D_SHADER_MACRO, pInclude unsafe.Pointer, entryPoint string, target string, flags1 uint32, flags2 uint32) (*_ID3DBlob, error) {
if !isD3DCompilerDLLAvailable() {
return nil, fmt.Errorf("directx: d3dcompiler_*.dll is missing in this environment")
}
// TODO: Define _ID3DInclude for pInclude, but is it possible in Go?
var defs unsafe.Pointer
if len(pDefines) > 0 {
defs = unsafe.Pointer(&pDefines[0])
}
sourceNameBytes := append([]byte(sourceName), 0)
entryPointBytes := append([]byte(entryPoint), 0)
targetBytes := append([]byte(target), 0)
var code *_ID3DBlob
var errorMsgs *_ID3DBlob
r, _, _ := procD3DCompile.Call(
uintptr(unsafe.Pointer(&srcData[0])), uintptr(len(srcData)), uintptr(unsafe.Pointer(&sourceNameBytes[0])),
uintptr(defs), uintptr(unsafe.Pointer(pInclude)), uintptr(unsafe.Pointer(&entryPointBytes[0])),
uintptr(unsafe.Pointer(&targetBytes[0])), uintptr(flags1), uintptr(flags2),
uintptr(unsafe.Pointer(&code)), uintptr(unsafe.Pointer(&errorMsgs)))
runtime.KeepAlive(pDefines)
runtime.KeepAlive(pInclude)
runtime.KeepAlive(sourceNameBytes)
runtime.KeepAlive(entryPointBytes)
runtime.KeepAlive(targetBytes)
if uint32(r) != uint32(windows.S_OK) {
if errorMsgs != nil {
defer errorMsgs.Release()
return nil, fmt.Errorf("directx: D3DCompile failed: %s: %w", errorMsgs.String(), handleError(windows.Handle(uint32(r))))
}
return nil, fmt.Errorf("directx: D3DCompile failed: %w", handleError(windows.Handle(uint32(r))))
}
return code, nil
}
type _D3D_SHADER_MACRO struct {
Name *byte
Definition *byte
}
type _ID3DBlob struct {
vtbl *_ID3DBlob_Vtbl
}
type _ID3DBlob_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
GetBufferPointer uintptr
GetBufferSize uintptr
}
func (i *_ID3DBlob) AddRef() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.AddRef, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
func (i *_ID3DBlob) GetBufferPointer() unsafe.Pointer {
r, _, _ := syscall.Syscall(i.vtbl.GetBufferPointer, 1, uintptr(unsafe.Pointer(i)),
0, 0)
return unsafe.Pointer(r)
}
func (i *_ID3DBlob) GetBufferSize() uintptr {
r, _, _ := syscall.Syscall(i.vtbl.GetBufferSize, 1, uintptr(unsafe.Pointer(i)),
0, 0)
return r
}
func (i *_ID3DBlob) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
func (i *_ID3DBlob) String() string {
return string(unsafe.Slice((*byte)(unsafe.Pointer(i.GetBufferPointer())), i.GetBufferSize()))
}
@@ -0,0 +1,622 @@
// 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"
"runtime"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type _DXGI_ALPHA_MODE uint32
const (
_DXGI_ALPHA_MODE_UNSPECIFIED _DXGI_ALPHA_MODE = 0
_DXGI_ALPHA_MODE_PREMULTIPLIED _DXGI_ALPHA_MODE = 1
_DXGI_ALPHA_MODE_STRAIGHT _DXGI_ALPHA_MODE = 2
_DXGI_ALPHA_MODE_IGNORE _DXGI_ALPHA_MODE = 3
_DXGI_ALPHA_MODE_FORCE_DWORD _DXGI_ALPHA_MODE = 0xffffffff
)
type _DXGI_COLOR_SPACE_TYPE int32
type _DXGI_FEATURE int32
const (
_DXGI_FEATURE_PRESENT_ALLOW_TEARING _DXGI_FEATURE = 0
)
type _DXGI_FORMAT int32
const (
_DXGI_FORMAT_UNKNOWN _DXGI_FORMAT = 0
_DXGI_FORMAT_R32G32B32A32_FLOAT _DXGI_FORMAT = 2
_DXGI_FORMAT_R32G32_FLOAT _DXGI_FORMAT = 16
_DXGI_FORMAT_R8G8B8A8_UNORM _DXGI_FORMAT = 28
_DXGI_FORMAT_R32_UINT _DXGI_FORMAT = 42
_DXGI_FORMAT_D24_UNORM_S8_UINT _DXGI_FORMAT = 45
_DXGI_FORMAT_B8G8R8A8_UNORM _DXGI_FORMAT = 87
)
type _DXGI_MODE_SCANLINE_ORDER int32
type _DXGI_MODE_SCALING int32
type _DXGI_PRESENT uint32
const (
_DXGI_PRESENT_TEST _DXGI_PRESENT = 0x00000001
_DXGI_PRESENT_ALLOW_TEARING _DXGI_PRESENT = 0x00000200
)
type _DXGI_SCALING int32
type _DXGI_SWAP_CHAIN_FLAG int32
const (
_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING _DXGI_SWAP_CHAIN_FLAG = 2048
)
type _DXGI_SWAP_EFFECT int32
const (
_DXGI_SWAP_EFFECT_DISCARD _DXGI_SWAP_EFFECT = 0
_DXGI_SWAP_EFFECT_SEQUENTIAL _DXGI_SWAP_EFFECT = 1
_DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL _DXGI_SWAP_EFFECT = 3
_DXGI_SWAP_EFFECT_FLIP_DISCARD _DXGI_SWAP_EFFECT = 4
)
type _DXGI_USAGE uint32
const (
_DXGI_USAGE_RENDER_TARGET_OUTPUT _DXGI_USAGE = 1 << (1 + 4)
)
const (
_DXGI_ADAPTER_FLAG_SOFTWARE = 2
_DXGI_CREATE_FACTORY_DEBUG = 0x01
_DXGI_ERROR_NOT_FOUND = handleError(0x887A0002)
_DXGI_MWA_NO_ALT_ENTER = 0x2
_DXGI_MWA_NO_WINDOW_CHANGES = 0x1
)
var (
_IID_IDXGIAdapter1 = windows.GUID{Data1: 0x29038f61, Data2: 0x3839, Data3: 0x4626, Data4: [...]byte{0x91, 0xfd, 0x08, 0x68, 0x79, 0x01, 0x1a, 0x05}}
_IID_IDXGIDevice = windows.GUID{Data1: 0x54ec77fa, Data2: 0x1377, Data3: 0x44e6, Data4: [...]byte{0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c}}
_IID_IDXGIFactory = windows.GUID{Data1: 0x7b7166ec, Data2: 0x21c7, Data3: 0x44ae, Data4: [...]byte{0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69}}
_IID_IDXGIFactory4 = windows.GUID{Data1: 0x1bc6ea02, Data2: 0xef36, Data3: 0x464f, Data4: [...]byte{0xbf, 0x0c, 0x21, 0xca, 0x39, 0xe5, 0x16, 0x8a}}
_IID_IDXGIFactory5 = windows.GUID{Data1: 0x7632e1f5, Data2: 0xee65, Data3: 0x4dca, Data4: [...]byte{0x87, 0xfd, 0x84, 0xcd, 0x75, 0xf8, 0x83, 0x8d}}
_IID_IDXGISwapChain4 = windows.GUID{Data1: 0x3d585d5a, Data2: 0xbd4a, Data3: 0x489e, Data4: [...]byte{0xb1, 0xf4, 0x3d, 0xbc, 0xb6, 0x45, 0x2f, 0xfb}}
)
var (
dxgi = windows.NewLazySystemDLL("dxgi.dll")
procCreateDXGIFactory = dxgi.NewProc("CreateDXGIFactory")
)
func _CreateDXGIFactory() (*_IDXGIFactory, error) {
var factory *_IDXGIFactory
r, _, _ := procCreateDXGIFactory.Call(uintptr(unsafe.Pointer(&_IID_IDXGIFactory)), uintptr(unsafe.Pointer(&factory)))
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: CreateDXGIFactory failed: %w", handleError(windows.Handle(uint32(r))))
}
return factory, nil
}
type _DXGI_ADAPTER_DESC1 struct {
Description [128]uint16
VendorId uint32
DeviceId uint32
SubSysId uint32
Revision uint32
DedicatedVideoMemory uint
DedicatedSystemMemory uint
SharedSystemMemory uint
AdapterLuid _LUID
Flags uint32
}
type _DXGI_MODE_DESC struct {
Width uint32
Height uint32
RefreshRate _DXGI_RATIONAL
Format _DXGI_FORMAT
ScanlineOrdering _DXGI_MODE_SCANLINE_ORDER
Scaling _DXGI_MODE_SCALING
}
type _DXGI_RATIONAL struct {
Numerator uint32
Denominator uint32
}
type _DXGI_SWAP_CHAIN_FULLSCREEN_DESC struct {
RefreshRate _DXGI_RATIONAL
ScanlineOrdering _DXGI_MODE_SCANLINE_ORDER
Scaling _DXGI_MODE_SCALING
Windowed _BOOL
}
type _DXGI_SAMPLE_DESC struct {
Count uint32
Quality uint32
}
type _DXGI_SWAP_CHAIN_DESC struct {
BufferDesc _DXGI_MODE_DESC
SampleDesc _DXGI_SAMPLE_DESC
BufferUsage _DXGI_USAGE
BufferCount uint32
OutputWindow windows.HWND
Windowed _BOOL
SwapEffect _DXGI_SWAP_EFFECT
Flags uint32
}
type _LUID struct {
LowPart uint32
HighPart int32
}
type _IDXGIAdapter struct {
vtbl *_IDXGIAdapter1_Vtbl
}
type _IDXGIAdapter_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
SetPrivateData uintptr
SetPrivateDataInterface uintptr
GetPrivateData uintptr
GetParent uintptr
EnumOutputs uintptr
GetDesc uintptr
CheckInterfaceSupport uintptr
}
func (i *_IDXGIAdapter) EnumOutputs(output uint32) (*_IDXGIOutput, error) {
var pOutput *_IDXGIOutput
r, _, _ := syscall.Syscall(i.vtbl.EnumOutputs, 3, uintptr(unsafe.Pointer(i)), uintptr(output), uintptr(unsafe.Pointer(&pOutput)))
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGIAdapter::EnumOutputs failed: %w", handleError(windows.Handle(uint32(r))))
}
return pOutput, nil
}
func (i *_IDXGIAdapter) GetParent(riid *windows.GUID) (unsafe.Pointer, error) {
var v unsafe.Pointer
r, _, _ := syscall.Syscall(i.vtbl.GetParent, 3, uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(riid)), uintptr(unsafe.Pointer(&v)))
runtime.KeepAlive(riid)
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGIAdapter::GetParent failed: %w", handleError(windows.Handle(uint32(r))))
}
return v, nil
}
func (i *_IDXGIAdapter) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
type _IDXGIAdapter1 struct {
vtbl *_IDXGIAdapter1_Vtbl
}
type _IDXGIAdapter1_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
SetPrivateData uintptr
SetPrivateDataInterface uintptr
GetPrivateData uintptr
GetParent uintptr
EnumOutputs uintptr
GetDesc uintptr
CheckInterfaceSupport uintptr
GetDesc1 uintptr
}
func (i *_IDXGIAdapter1) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
func (i *_IDXGIAdapter1) GetDesc1() (*_DXGI_ADAPTER_DESC1, error) {
var desc _DXGI_ADAPTER_DESC1
r, _, _ := syscall.Syscall(i.vtbl.GetDesc1, 2, uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(&desc)), 0)
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGIAdapter1::GetDesc1 failed: %w", handleError(windows.Handle(uint32(r))))
}
return &desc, nil
}
type _IDXGIDevice struct {
vtbl *_IDXGIDevice_Vtbl
}
type _IDXGIDevice_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
SetPrivateData uintptr
SetPrivateDataInterface uintptr
GetPrivateData uintptr
GetParent uintptr
GetAdapter uintptr
CreateSurface uintptr
QueryResourceResidency uintptr
SetGPUThreadPriority uintptr
GetGPUThreadPriority uintptr
}
func (i *_IDXGIDevice) GetAdapter() (*_IDXGIAdapter, error) {
var adapter *_IDXGIAdapter
r, _, _ := syscall.Syscall(i.vtbl.GetAdapter, 2, uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(&adapter)), 0)
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGIDevice::GetAdapter failed: %w", handleError(windows.Handle(uint32(r))))
}
return adapter, nil
}
func (i *_IDXGIDevice) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
type _IDXGIFactory struct {
vtbl *_IDXGIFactory_Vtbl
}
type _IDXGIFactory_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
SetPrivateData uintptr
SetPrivateDataInterface uintptr
GetPrivateData uintptr
GetParent uintptr
EnumAdapters uintptr
MakeWindowAssociation uintptr
GetWindowAssociation uintptr
CreateSwapChain uintptr
CreateSoftwareAdapter uintptr
}
func (i *_IDXGIFactory) CreateSwapChain(pDevice unsafe.Pointer, pDesc *_DXGI_SWAP_CHAIN_DESC) (*_IDXGISwapChain, error) {
var swapChain *_IDXGISwapChain
r, _, _ := syscall.Syscall6(i.vtbl.CreateSwapChain, 4, uintptr(unsafe.Pointer(i)),
uintptr(pDevice), uintptr(unsafe.Pointer(pDesc)), uintptr(unsafe.Pointer(&swapChain)),
0, 0)
runtime.KeepAlive(pDevice)
runtime.KeepAlive(pDesc)
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGIFactory::CreateSwapChain failed: %w", handleError(windows.Handle(uint32(r))))
}
return swapChain, nil
}
func (i *_IDXGIFactory) MakeWindowAssociation(windowHandle windows.HWND, flags uint32) error {
r, _, _ := syscall.Syscall(i.vtbl.MakeWindowAssociation, 3, uintptr(unsafe.Pointer(i)), uintptr(windowHandle), uintptr(flags))
if uint32(r) != uint32(windows.S_OK) {
return fmt.Errorf("directx: IDXGIFactory::MakeWIndowAssociation failed: %w", handleError(windows.Handle(uint32(r))))
}
return nil
}
func (i *_IDXGIFactory) QueryInterface(riid *windows.GUID) (unsafe.Pointer, error) {
var v unsafe.Pointer
r, _, _ := syscall.Syscall(i.vtbl.QueryInterface, 3, uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(riid)), uintptr(unsafe.Pointer(&v)))
runtime.KeepAlive(riid)
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGIFactory::QueryInterface failed: %w", handleError(windows.Handle(uint32(r))))
}
return v, nil
}
func (i *_IDXGIFactory) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
type _IDXGIFactory4 struct {
vtbl *_IDXGIFactory4_Vtbl
}
type _IDXGIFactory4_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
SetPrivateData uintptr
SetPrivateDataInterface uintptr
GetPrivateData uintptr
GetParent uintptr
EnumAdapters uintptr
MakeWindowAssociation uintptr
GetWindowAssociation uintptr
CreateSwapChain uintptr
CreateSoftwareAdapter uintptr
EnumAdapters1 uintptr
IsCurrent uintptr
IsWindowedStereoEnabled uintptr
CreateSwapChainForHwnd uintptr
CreateSwapChainForCoreWindow uintptr
GetSharedResourceAdapterLuid uintptr
RegisterStereoStatusWindow uintptr
RegisterStereoStatusEvent uintptr
UnregisterStereoStatus uintptr
RegisterOcclusionStatusWindow uintptr
RegisterOcclusionStatusEvent uintptr
UnregisterOcclusionStatus uintptr
CreateSwapChainForComposition uintptr
GetCreationFlags uintptr
EnumAdapterByLuid uintptr
EnumWarpAdapter uintptr
}
func (i *_IDXGIFactory4) EnumAdapters1(adapter uint32) (*_IDXGIAdapter1, error) {
var ptr *_IDXGIAdapter1
r, _, _ := syscall.Syscall(i.vtbl.EnumAdapters1, 3, uintptr(unsafe.Pointer(i)), uintptr(adapter), uintptr(unsafe.Pointer(&ptr)))
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGIFactory4::EnumAdapters1 failed: %w", handleError(windows.Handle(uint32(r))))
}
return ptr, nil
}
func (i *_IDXGIFactory4) EnumWarpAdapter() (*_IDXGIAdapter1, error) {
var ptr *_IDXGIAdapter1
r, _, _ := syscall.Syscall(i.vtbl.EnumWarpAdapter, 3, uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(&_IID_IDXGIAdapter1)), uintptr(unsafe.Pointer(&ptr)))
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGIFactory4::EnumWarpAdapter failed: %w", handleError(windows.Handle(uint32(r))))
}
return ptr, nil
}
func (i *_IDXGIFactory4) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
type _IDXGIFactory5 struct {
vtbl *_IDXGIFactory5_Vtbl
}
type _IDXGIFactory5_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
SetPrivateData uintptr
SetPrivateDataInterface uintptr
GetPrivateData uintptr
GetParent uintptr
EnumAdapters uintptr
MakeWindowAssociation uintptr
GetWindowAssociation uintptr
CreateSwapChain uintptr
CreateSoftwareAdapter uintptr
EnumAdapters1 uintptr
IsCurrent uintptr
IsWindowedStereoEnabled uintptr
CreateSwapChainForHwnd uintptr
CreateSwapChainForCoreWindow uintptr
GetSharedResourceAdapterLuid uintptr
RegisterStereoStatusWindow uintptr
RegisterStereoStatusEvent uintptr
UnregisterStereoStatus uintptr
RegisterOcclusionStatusWindow uintptr
RegisterOcclusionStatusEvent uintptr
UnregisterOcclusionStatus uintptr
CreateSwapChainForComposition uintptr
GetCreationFlags uintptr
EnumAdapterByLuid uintptr
EnumWarpAdapter uintptr
CheckFeatureSupport uintptr
}
func (i *_IDXGIFactory5) CheckFeatureSupport(feature _DXGI_FEATURE, pFeatureSupportData unsafe.Pointer, featureSupportDataSize uint32) error {
r, _, _ := syscall.Syscall6(i.vtbl.CheckFeatureSupport, 4, uintptr(unsafe.Pointer(i)),
uintptr(feature), uintptr(pFeatureSupportData), uintptr(featureSupportDataSize),
0, 0)
runtime.KeepAlive(pFeatureSupportData)
if uint32(r) != uint32(windows.S_OK) {
return fmt.Errorf("directx: IDXGIFactory5::CheckFeatureSupport failed: %w", handleError(windows.Handle(uint32(r))))
}
return nil
}
func (i *_IDXGIFactory5) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
type _IDXGIOutput struct {
vtbl *_IDXGIOutput_Vtbl
}
type _IDXGIOutput_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
SetPrivateData uintptr
SetPrivateDataInterface uintptr
GetPrivateData uintptr
GetParent uintptr
GetDesc uintptr
GetDisplayModeList uintptr
FindClosestMatchingMode uintptr
WaitForVBlank uintptr
TakeOwnership uintptr
ReleaseOwnership uintptr
GetGammaControlCapabilities uintptr
SetGammaControl uintptr
GetGammaControl uintptr
SetDisplaySurface uintptr
GetDisplaySurfaceData uintptr
GetFrameStatistics uintptr
}
func (i *_IDXGIOutput) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
type _IDXGISwapChain struct {
vtbl *_IDXGISwapChain_Vtbl
}
type _IDXGISwapChain_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
SetPrivateData uintptr
SetPrivateDataInterface uintptr
GetPrivateData uintptr
GetParent uintptr
GetDevice uintptr
Present uintptr
GetBuffer uintptr
SetFullscreenState uintptr
GetFullscreenState uintptr
GetDesc uintptr
ResizeBuffers uintptr
ResizeTarget uintptr
GetContainingOutput uintptr
GetFrameStatistics uintptr
GetLastPresentCount uintptr
}
func (i *_IDXGISwapChain) GetBuffer(buffer uint32, riid *windows.GUID) (unsafe.Pointer, error) {
var resource unsafe.Pointer
r, _, _ := syscall.Syscall6(i.vtbl.GetBuffer, 4, uintptr(unsafe.Pointer(i)),
uintptr(buffer), uintptr(unsafe.Pointer(riid)), uintptr(unsafe.Pointer(&resource)),
0, 0)
runtime.KeepAlive(riid)
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGISwapChain::GetBuffer failed: %w", handleError(windows.Handle(uint32(r))))
}
return resource, nil
}
func (i *_IDXGISwapChain) ResizeBuffers(bufferCount uint32, width uint32, height uint32, newFormat _DXGI_FORMAT, swapChainFlags uint32) error {
r, _, _ := syscall.Syscall6(i.vtbl.ResizeBuffers, 6,
uintptr(unsafe.Pointer(i)), uintptr(bufferCount), uintptr(width),
uintptr(height), uintptr(newFormat), uintptr(swapChainFlags))
if uint32(r) != uint32(windows.S_OK) {
return fmt.Errorf("directx: IDXGISwapChain::ResizeBuffers failed: %w", handleError(windows.Handle(uint32(r))))
}
return nil
}
func (i *_IDXGISwapChain) Present(syncInterval uint32, flags uint32) (occluded bool, err error) {
r, _, _ := syscall.Syscall(i.vtbl.Present, 3, uintptr(unsafe.Pointer(i)), uintptr(syncInterval), uintptr(flags))
if uint32(r) != uint32(windows.S_OK) {
// During a screen lock, Present fails (#2179).
if uint32(r) == uint32(windows.DXGI_STATUS_OCCLUDED) {
return true, nil
}
return false, fmt.Errorf("directx: IDXGISwapChain::Present failed: %w", handleError(windows.Handle(uint32(r))))
}
return false, nil
}
func (i *_IDXGISwapChain) QueryInterface(riid *windows.GUID) (unsafe.Pointer, error) {
var v unsafe.Pointer
r, _, _ := syscall.Syscall(i.vtbl.QueryInterface, 3, uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(riid)), uintptr(unsafe.Pointer(&v)))
runtime.KeepAlive(riid)
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("directx: IDXGISwapChain::QueryInterface failed: %w", handleError(windows.Handle(uint32(r))))
}
return v, nil
}
func (i *_IDXGISwapChain) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
type _IDXGISwapChain4 struct {
vtbl *_IDXGISwapChain4_Vtbl
}
type _IDXGISwapChain4_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
SetPrivateData uintptr
SetPrivateDataInterface uintptr
GetPrivateData uintptr
GetParent uintptr
GetDevice uintptr
Present uintptr
GetBuffer uintptr
SetFullscreenState uintptr
GetFullscreenState uintptr
GetDesc uintptr
ResizeBuffers uintptr
ResizeTarget uintptr
GetContainingOutput uintptr
GetFrameStatistics uintptr
GetLastPresentCount uintptr
GetDesc1 uintptr
GetFullscreenDesc uintptr
GetHwnd uintptr
GetCoreWindow uintptr
Present1 uintptr
IsTemporaryMonoSupported uintptr
GetRestrictToOutput uintptr
SetBackgroundColor uintptr
GetBackgroundColor uintptr
SetRotation uintptr
GetRotation uintptr
SetSourceSize uintptr
GetSourceSize uintptr
SetMaximumFrameLatency uintptr
GetMaximumFrameLatency uintptr
GetFrameLatencyWaitableObject uintptr
SetMatrixTransform uintptr
GetMatrixTransform uintptr
GetCurrentBackBufferIndex uintptr
CheckColorSpaceSupport uintptr
SetColorSpace1 uintptr
ResizeBuffers1 uintptr
SetHDRMetaData uintptr
}
func (i *_IDXGISwapChain4) GetCurrentBackBufferIndex() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.GetCurrentBackBufferIndex, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
func (i *_IDXGISwapChain4) Release() uint32 {
r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0)
return uint32(r)
}
@@ -0,0 +1,730 @@
// 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
case graphicsdriver.BlendOperationMin:
return _D3D11_BLEND_OP_MIN
case graphicsdriver.BlendOperationMax:
return _D3D11_BLEND_OP_MAX
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
newScreenWidth int
newScreenHeight int
}
func newGraphics11(useWARP bool, useDebugLayer bool) (gr11 *graphics11, ferr error) {
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
}
// Avoid _D3D_FEATURE_LEVEL_11_1 as DirectX 11.0 doesn't recognize this.
// 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
d, fl, ctx, err := _D3D11CreateDevice(nil, driverType, 0, uint32(flags), featureLevels, true, true)
if err != nil {
return nil, err
}
g.device = (*_ID3D11Device)(d)
g.featureLevel = fl
g.deviceContext = (*_ID3D11DeviceContext)(ctx)
// 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)
gi, err := newGraphicsInfra(dxgiFactory)
if err != nil {
return nil, err
}
g.graphicsInfra = gi
defer func() {
if ferr != nil {
g.graphicsInfra.release()
g.graphicsInfra = nil
}
}()
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
}
if err := g.graphicsInfra.present(g.vsyncEnabled); err != nil {
return err
}
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
}
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?
}
func (g *graphics11) SetVertices(vertices []float32, indices []uint32) error {
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
g.deviceContext.IASetIndexBuffer(g.indexBuffer, _DXGI_FORMAT_R32_UINT, 0)
}
// 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
}
copy(unsafe.Slice((*uint32)(mapped.pData), len(indices)), indices)
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) {
imageWidth := width
imageHeight := height
if g.screenImage != nil {
imageWidth = g.screenImage.width
imageHeight = g.screenImage.height
g.screenImage.Dispose()
g.screenImage = nil
}
if g.graphicsInfra.isSwapChainInited() {
g.newScreenWidth, g.newScreenHeight = width, height
} 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(),
width: imageWidth,
height: imageHeight,
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)
}
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 {
// 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,
},
})
if err := dst.setAsRenderTarget(fillRule != graphicsdriver.FillAll); err != nil {
return err
}
// Set the shader parameters.
shader := g.shaders[shaderID]
if err := shader.use(uniforms, srcs); err != nil {
return err
}
if fillRule == graphicsdriver.FillAll {
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{
{
left: int32(dstRegion.Region.Min.X),
top: int32(dstRegion.Region.Min.Y),
right: int32(dstRegion.Region.Max.X),
bottom: int32(dstRegion.Region.Max.Y),
},
})
switch fillRule {
case graphicsdriver.FillAll:
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
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)
case graphicsdriver.EvenOdd:
bs, err := g.blendState(blend, invertStencil)
if err != nil {
return err
}
g.deviceContext.OMSetBlendState(bs, nil, 0xffffffff)
dss, err := g.depthStencilState(invertStencil)
if err != nil {
return err
}
g.deviceContext.OMSetDepthStencilState(dss, 0)
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
}
if fillRule != graphicsdriver.FillAll {
bs, err := g.blendState(blend, drawWithStencil)
if err != nil {
return err
}
g.deviceContext.OMSetBlendState(bs, nil, 0xffffffff)
dss, err := g.depthStencilState(drawWithStencil)
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) {
var writeMask uint8
if stencilMode == noStencil || stencilMode == drawWithStencil {
writeMask = uint8(_D3D11_COLOR_WRITE_ENABLE_ALL)
}
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 {
case incrementStencil:
desc.StencilEnable = 1
desc.FrontFace.StencilPassOp = _D3D11_STENCIL_OP_INCR
desc.BackFace.StencilPassOp = _D3D11_STENCIL_OP_DECR
case invertStencil:
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
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,374 @@
// 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 (
"errors"
"fmt"
"math"
"os"
"runtime"
"strconv"
"strings"
"time"
"unsafe"
"golang.org/x/sys/windows"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk"
"github.com/hajimehoshi/ebiten/v2/internal/winver"
)
type stencilMode int
const (
noStencil stencilMode = iota
incrementStencil
invertStencil
drawWithStencil
)
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
}
func parseFeatureLevel(str string) (_D3D_FEATURE_LEVEL, bool) {
switch str {
case "11_0":
return _D3D_FEATURE_LEVEL_11_0, true
case "11_1":
return _D3D_FEATURE_LEVEL_11_1, true
case "12_0":
return _D3D_FEATURE_LEVEL_12_0, true
case "12_1":
return _D3D_FEATURE_LEVEL_12_1, true
case "12_2":
return _D3D_FEATURE_LEVEL_12_2, true
default:
return 0, false
}
}
// 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) {
if !isD3DCompilerDLLAvailable() {
return nil, fmt.Errorf("directx: d3dcompiler_*.dll is missing in this environment")
}
var useWARP bool
var useDebugLayer bool
version := 11
// Specify the feature level 11 by default.
// Some old cards don't work well with the default feature level (#2447, #2486).
featureLevel := _D3D_FEATURE_LEVEL_11_0
// Parse a special environment variable for backward compatibility.
if env := os.Getenv("EBITENGINE_DIRECTX_FEATURE_LEVEL"); env != "" {
if fl, ok := parseFeatureLevel(env); ok {
featureLevel = fl
}
}
env := os.Getenv("EBITENGINE_DIRECTX")
if env == "" {
// For backward compatibility, read the EBITEN_ version.
env = os.Getenv("EBITEN_DIRECTX")
}
for _, t := range strings.Split(env, ",") {
t := strings.TrimSpace(t)
switch {
case t == "warp":
// TODO: Is WARP available on Xbox?
useWARP = true
case t == "debug":
useDebugLayer = true
case strings.HasPrefix(t, "version="):
v, err := strconv.Atoi(t[len("version="):])
if err != nil {
continue
}
version = v
case strings.HasPrefix(t, "featurelevel="):
fl, ok := parseFeatureLevel(t[len("featurelevel="):])
if !ok {
continue
}
featureLevel = fl
}
}
// On Xbox, only DirectX 12 is available.
if microsoftgdk.IsXbox() {
version = 12
}
switch version {
case 11:
g, err := newGraphics11(useWARP, useDebugLayer)
if err != nil {
return nil, err
}
return g, nil
case 12:
g, err := newGraphics12(useWARP, useDebugLayer, featureLevel)
if err != nil {
return nil, err
}
return g, nil
default:
panic(fmt.Sprintf("directx: unexpected DirectX version: %d", version))
}
}
type graphicsInfra struct {
factory *_IDXGIFactory
swapChain *_IDXGISwapChain
swapChain4 *_IDXGISwapChain4
allowTearing bool
// occluded reports whether the screen is invisible or not.
occluded bool
// lastTime is the last time for rendering.
lastTime time.Time
bufferCount int
}
// newGraphicsInfra takes the ownership of the given factory.
func newGraphicsInfra(factory *_IDXGIFactory) (*graphicsInfra, error) {
g := &graphicsInfra{
factory: factory,
}
runtime.SetFinalizer(g, (*graphicsInfra).release)
if f, err := g.factory.QueryInterface(&_IID_IDXGIFactory5); err == nil && f != nil {
factory := (*_IDXGIFactory5)(f)
defer factory.Release()
var allowTearing int32
if err := factory.CheckFeatureSupport(_DXGI_FEATURE_PRESENT_ALLOW_TEARING, unsafe.Pointer(&allowTearing), uint32(unsafe.Sizeof(allowTearing))); err == nil && allowTearing != 0 {
g.allowTearing = true
}
}
return g, nil
}
func (g *graphicsInfra) release() {
if g.factory != nil {
g.factory.Release()
g.factory = nil
}
if g.swapChain != nil {
g.swapChain.Release()
g.swapChain = nil
}
if g.swapChain4 != nil {
g.swapChain4.Release()
g.swapChain4 = nil
}
}
// appendAdapters appends found adapters to the given adapters.
// Releasing them is the caller's responsibility.
//
// warpForDX12 is valid only for DirectX 12.
func (g *graphicsInfra) appendAdapters(adapters []*_IDXGIAdapter1, warpForDX12 bool) ([]*_IDXGIAdapter1, error) {
f, err := g.factory.QueryInterface(&_IID_IDXGIFactory4)
if err != nil {
return nil, err
}
if f == nil {
return nil, fmt.Errorf("directx: IID_IDXGIFactory4 was not available")
}
factory4 := (*_IDXGIFactory4)(f)
defer factory4.Release()
if warpForDX12 {
a, err := factory4.EnumWarpAdapter()
if err != nil {
return nil, err
}
adapters = append(adapters, a)
return adapters, nil
}
for i := uint32(0); ; i++ {
a, err := factory4.EnumAdapters1(i)
if errors.Is(err, _DXGI_ERROR_NOT_FOUND) {
break
}
if err != nil {
return nil, err
}
adapters = append(adapters, a)
}
return adapters, nil
}
func (g *graphicsInfra) isSwapChainInited() bool {
return g.swapChain != nil
}
func (g *graphicsInfra) initSwapChain(width, height int, device unsafe.Pointer, window windows.HWND) (ferr error) {
if g.swapChain != nil {
return fmt.Errorf("directx: swap chain must not be initialized at initSwapChain, but is already done")
}
// Create a swap chain.
//
// DXGI_ALPHA_MODE_PREMULTIPLIED doesn't work with a HWND well.
//
// IDXGIFactory::CreateSwapChain: Alpha blended swapchains must be created with CreateSwapChainForComposition,
// or CreateSwapChainForCoreWindow with the DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER flag
//
// Use *_SEQUENTIAL swap effects to follow the Mozilla way:
// https://github.com/mozilla/gecko-dev/blob/0907529ff72c456ddb47839f5f7ba16291f28dce/gfx/layers/d3d11/CompositorD3D11.cpp#L167-L254
desc := &_DXGI_SWAP_CHAIN_DESC{
BufferDesc: _DXGI_MODE_DESC{
Width: uint32(width),
Height: uint32(height),
Format: _DXGI_FORMAT_B8G8R8A8_UNORM,
},
SampleDesc: _DXGI_SAMPLE_DESC{
Count: 1,
Quality: 0,
},
BufferUsage: _DXGI_USAGE_RENDER_TARGET_OUTPUT,
BufferCount: frameCount,
OutputWindow: window,
Windowed: 1,
SwapEffect: _DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
}
// DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL/DISCARD are not supported for older Windows than 10 or DirectX 12.
// https://learn.microsoft.com/en-us/windows/win32/api/dxgi/ne-dxgi-dxgi_swap_effect
if !winver.IsWindows10OrGreater() {
desc.SwapEffect = _DXGI_SWAP_EFFECT_SEQUENTIAL
// With the non-flip (bitblt) mode, the buffer count should be 1. See also:
// * https://bugzilla.mozilla.org/show_bug.cgi?id=1419293#c18
// * https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-flip-model
desc.BufferCount = 1
}
g.bufferCount = int(desc.BufferCount)
if g.allowTearing {
desc.Flags |= uint32(_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)
}
s, err := g.factory.CreateSwapChain(device, desc)
if err != nil {
return err
}
g.swapChain = s
defer func() {
if ferr != nil {
g.release()
}
}()
if s4, err := g.swapChain.QueryInterface(&_IID_IDXGISwapChain4); err == nil && s4 != nil {
g.swapChain4 = (*_IDXGISwapChain4)(s4)
}
// MakeWindowAssociation should be called after swap chain creation.
// https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgifactory-makewindowassociation
if err := g.factory.MakeWindowAssociation(window, _DXGI_MWA_NO_WINDOW_CHANGES|_DXGI_MWA_NO_ALT_ENTER); err != nil {
return err
}
return nil
}
func (g *graphicsInfra) resizeSwapChain(width, height int) error {
if g.swapChain == nil {
return fmt.Errorf("directx: swap chain must be initialized at resizeSwapChain, but is not")
}
var flag uint32
if g.allowTearing {
flag |= uint32(_DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)
}
if err := g.swapChain.ResizeBuffers(uint32(g.bufferCount), uint32(width), uint32(height), _DXGI_FORMAT_B8G8R8A8_UNORM, flag); err != nil {
return err
}
return nil
}
func (g *graphicsInfra) currentBackBufferIndex() (int, error) {
if g.swapChain4 == nil {
return 0, fmt.Errorf("directx: IDXGISwapChain4 is not available")
}
return int(g.swapChain4.GetCurrentBackBufferIndex()), nil
}
func (g *graphicsInfra) present(vsyncEnabled bool) error {
if g.swapChain == nil {
return fmt.Errorf("directx: swap chain must be initialized at present, but is not")
}
var syncInterval uint32
var flags _DXGI_PRESENT
if g.occluded {
// The screen is not visible. Test whether we can resume.
flags |= _DXGI_PRESENT_TEST
} else {
// Do actual rendering only when the screen is visible.
if vsyncEnabled {
syncInterval = 1
} else if g.allowTearing {
flags |= _DXGI_PRESENT_ALLOW_TEARING
}
}
occluded, err := g.swapChain.Present(syncInterval, uint32(flags))
if err != nil {
return err
}
g.occluded = occluded
// Reduce FPS when the screen is invisible.
now := time.Now()
if g.occluded {
if delta := 100*time.Millisecond - now.Sub(g.lastTime); delta > 0 {
time.Sleep(delta)
}
}
g.lastTime = now
return nil
}
func (g *graphicsInfra) getBuffer(buffer uint32, riid *windows.GUID) (unsafe.Pointer, error) {
return g.swapChain.GetBuffer(buffer, riid)
}
@@ -0,0 +1,216 @@
// 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"
"image"
"unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
)
type image11 struct {
graphics *graphics11
id graphicsdriver.ImageID
width int
height int
screen bool
texture *_ID3D11Texture2D
stencil *_ID3D11Texture2D
renderTargetView *_ID3D11RenderTargetView
stencilView *_ID3D11DepthStencilView
shaderResourceView *_ID3D11ShaderResourceView
}
func (i *image11) internalSize() (int, int) {
if i.screen {
return i.width, i.height
}
return graphics.InternalImageSize(i.width), graphics.InternalImageSize(i.height)
}
func (i *image11) ID() graphicsdriver.ImageID {
return i.id
}
func (i *image11) Dispose() {
i.disposeBuffers()
i.graphics.removeImage(i)
}
func (i *image11) disposeBuffers() {
if i.texture != nil {
i.texture.Release()
i.texture = nil
}
if i.stencil != nil {
i.stencil.Release()
i.stencil = nil
}
if i.renderTargetView != nil {
i.renderTargetView.Release()
i.renderTargetView = nil
}
if i.stencilView != nil {
i.stencilView.Release()
i.stencilView = nil
}
if i.shaderResourceView != nil {
i.shaderResourceView.Release()
i.shaderResourceView = nil
}
}
func (i *image11) ReadPixels(args []graphicsdriver.PixelsArgs) error {
var unionRegion image.Rectangle
for _, a := range args {
unionRegion = unionRegion.Union(a.Region)
}
staging, err := i.graphics.device.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
Width: uint32(unionRegion.Dx()),
Height: uint32(unionRegion.Dy()),
MipLevels: 0,
ArraySize: 1,
Format: _DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc: _DXGI_SAMPLE_DESC{
Count: 1,
Quality: 0,
},
Usage: _D3D11_USAGE_STAGING,
BindFlags: 0,
CPUAccessFlags: uint32(_D3D11_CPU_ACCESS_READ),
MiscFlags: 0,
}, nil)
if err != nil {
return err
}
defer staging.Release()
i.graphics.deviceContext.CopySubresourceRegion(unsafe.Pointer(staging), 0, 0, 0, 0, unsafe.Pointer(i.texture), 0, &_D3D11_BOX{
left: uint32(unionRegion.Min.X),
top: uint32(unionRegion.Min.Y),
front: 0,
right: uint32(unionRegion.Max.X),
bottom: uint32(unionRegion.Max.Y),
back: 1,
})
var mapped _D3D11_MAPPED_SUBRESOURCE
if err := i.graphics.deviceContext.Map(unsafe.Pointer(staging), 0, _D3D11_MAP_READ, 0, &mapped); err != nil {
return err
}
stride := int(mapped.RowPitch)
srcPix := unsafe.Slice((*byte)(mapped.pData), stride*unionRegion.Dy())
for _, a := range args {
w := a.Region.Dx()
if unionRegion == a.Region && stride == 4*w {
copy(a.Pixels, srcPix)
continue
}
offset := 4*(a.Region.Min.X-unionRegion.Min.X) + stride*(a.Region.Min.Y-unionRegion.Min.Y)
for j := 0; j < a.Region.Dy(); j++ {
copy(a.Pixels[j*4*w:(j+1)*4*w], srcPix[offset+j*stride:])
}
}
i.graphics.deviceContext.Unmap(unsafe.Pointer(staging), 0)
return nil
}
func (i *image11) WritePixels(args []graphicsdriver.PixelsArgs) error {
for _, a := range args {
i.graphics.deviceContext.UpdateSubresource(unsafe.Pointer(i.texture), 0, &_D3D11_BOX{
left: uint32(a.Region.Min.X),
top: uint32(a.Region.Min.Y),
front: 0,
right: uint32(a.Region.Max.X),
bottom: uint32(a.Region.Max.Y),
back: 1,
}, unsafe.Pointer(&a.Pixels[0]), uint32(4*a.Region.Dx()), 0)
}
return nil
}
func (i *image11) setAsRenderTarget(useStencil bool) error {
if i.renderTargetView == nil {
rtv, err := i.graphics.device.CreateRenderTargetView(unsafe.Pointer(i.texture), nil)
if err != nil {
return err
}
i.renderTargetView = rtv
}
if !useStencil {
i.graphics.deviceContext.OMSetRenderTargets([]*_ID3D11RenderTargetView{i.renderTargetView}, nil)
return nil
}
if i.screen {
return fmt.Errorf("directx: a stencil buffer is not available for a screen image")
}
if i.stencil == nil {
w, h := i.internalSize()
s, err := i.graphics.device.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
Width: uint32(w),
Height: uint32(h),
MipLevels: 0,
ArraySize: 1,
Format: _DXGI_FORMAT_D24_UNORM_S8_UINT,
SampleDesc: _DXGI_SAMPLE_DESC{
Count: 1,
Quality: 0,
},
Usage: _D3D11_USAGE_DEFAULT,
BindFlags: uint32(_D3D11_BIND_DEPTH_STENCIL),
CPUAccessFlags: 0,
MiscFlags: 0,
}, nil)
if err != nil {
return err
}
i.stencil = s
}
if i.stencilView == nil {
sv, err := i.graphics.device.CreateDepthStencilView(unsafe.Pointer(i.stencil), nil)
if err != nil {
return err
}
i.stencilView = sv
}
i.graphics.deviceContext.OMSetRenderTargets([]*_ID3D11RenderTargetView{i.renderTargetView}, i.stencilView)
i.graphics.deviceContext.ClearDepthStencilView(i.stencilView, uint8(_D3D11_CLEAR_STENCIL), 0, 0)
return nil
}
func (i *image11) getShaderResourceView() (*_ID3D11ShaderResourceView, error) {
if i.shaderResourceView == nil {
srv, err := i.graphics.device.CreateShaderResourceView(unsafe.Pointer(i.texture), nil)
if err != nil {
return nil, err
}
i.shaderResourceView = srv
}
return i.shaderResourceView, nil
}
@@ -0,0 +1,426 @@
// 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 (
"errors"
"fmt"
"image"
"unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
)
type image12 struct {
graphics *graphics12
id graphicsdriver.ImageID
width int
height int
screen bool
states [frameCount]_D3D12_RESOURCE_STATES
texture *_ID3D12Resource
stencil *_ID3D12Resource
rtvDescriptorHeap *_ID3D12DescriptorHeap
dsvDescriptorHeap *_ID3D12DescriptorHeap
uploadingStagingBuffers []*_ID3D12Resource
}
func (i *image12) ID() graphicsdriver.ImageID {
return i.id
}
func (i *image12) Dispose() {
// Dipose the images later as this image might still be used.
i.graphics.removeImage(i)
}
func (i *image12) disposeImpl() {
if i.dsvDescriptorHeap != nil {
i.dsvDescriptorHeap.Release()
i.dsvDescriptorHeap = nil
}
if i.rtvDescriptorHeap != nil {
i.rtvDescriptorHeap.Release()
i.rtvDescriptorHeap = nil
}
if i.stencil != nil {
i.stencil.Release()
i.stencil = nil
}
if i.texture != nil {
i.texture.Release()
i.texture = nil
}
}
func (i *image12) ReadPixels(args []graphicsdriver.PixelsArgs) error {
if i.screen {
return errors.New("directx: Pixels cannot be called on the screen")
}
if err := i.graphics.flushCommandList(i.graphics.drawCommandList); err != nil {
return err
}
var unionRegion image.Rectangle
for _, a := range args {
unionRegion = unionRegion.Union(a.Region)
}
desc := _D3D12_RESOURCE_DESC{
Dimension: _D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Alignment: 0,
Width: uint64(unionRegion.Dx()),
Height: uint32(unionRegion.Dy()),
DepthOrArraySize: 1,
MipLevels: 0,
Format: _DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc: _DXGI_SAMPLE_DESC{
Count: 1,
Quality: 0,
},
Layout: _D3D12_TEXTURE_LAYOUT_UNKNOWN,
Flags: _D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,
}
layouts, _, _, totalBytes := i.graphics.device.GetCopyableFootprints(&desc, 0, 1, 0)
readingStagingBuffer, err := createBuffer(i.graphics.device, totalBytes, _D3D12_HEAP_TYPE_READBACK)
if err != nil {
return err
}
defer func() {
readingStagingBuffer.Release()
}()
if rb, ok := i.transiteState(_D3D12_RESOURCE_STATE_COPY_SOURCE); ok {
i.graphics.copyCommandList.ResourceBarrier([]_D3D12_RESOURCE_BARRIER_Transition{rb})
}
m, err := readingStagingBuffer.Map(0, &_D3D12_RANGE{0, 0})
if err != nil {
return err
}
dst := _D3D12_TEXTURE_COPY_LOCATION_PlacedFootPrint{
pResource: readingStagingBuffer,
Type: _D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
PlacedFootprint: layouts,
}
src := _D3D12_TEXTURE_COPY_LOCATION_SubresourceIndex{
pResource: i.texture,
Type: _D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
SubresourceIndex: 0,
}
i.graphics.needFlushCopyCommandList = true
i.graphics.copyCommandList.CopyTextureRegion_PlacedFootPrint_SubresourceIndex(
&dst, 0, 0, 0, &src, &_D3D12_BOX{
left: uint32(unionRegion.Min.X),
top: uint32(unionRegion.Min.Y),
front: 0,
right: uint32(unionRegion.Max.X),
bottom: uint32(unionRegion.Max.Y),
back: 1,
})
if err := i.graphics.flushCommandList(i.graphics.copyCommandList); err != nil {
return err
}
stride := int(layouts.Footprint.RowPitch)
srcPix := unsafe.Slice((*byte)(unsafe.Pointer(m)), totalBytes)
for _, a := range args {
w := a.Region.Dx()
if unionRegion == a.Region && stride == 4*w {
copy(a.Pixels, srcPix)
continue
}
offset := 4*(a.Region.Min.X-unionRegion.Min.X) + stride*(a.Region.Min.Y-unionRegion.Min.Y)
for j := 0; j < a.Region.Dy(); j++ {
copy(a.Pixels[j*w*4:(j+1)*w*4], srcPix[offset+j*stride:])
}
}
readingStagingBuffer.Unmap(0, nil)
return nil
}
func (i *image12) WritePixels(args []graphicsdriver.PixelsArgs) error {
if i.screen {
return errors.New("directx: WritePixels cannot be called on the screen")
}
if err := i.graphics.flushCommandList(i.graphics.drawCommandList); err != nil {
return err
}
var region image.Rectangle
for _, a := range args {
region = region.Union(a.Region)
}
desc := _D3D12_RESOURCE_DESC{
Dimension: _D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Alignment: 0,
Width: uint64(region.Dx()),
Height: uint32(region.Dy()),
DepthOrArraySize: 1,
MipLevels: 0,
Format: _DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc: _DXGI_SAMPLE_DESC{
Count: 1,
Quality: 0,
},
Layout: _D3D12_TEXTURE_LAYOUT_UNKNOWN,
Flags: _D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,
}
layouts, _, _, totalBytes := i.graphics.device.GetCopyableFootprints(&desc, 0, 1, 0)
uploadingStagingBuffer, err := createBuffer(i.graphics.device, totalBytes, _D3D12_HEAP_TYPE_UPLOAD)
if err != nil {
return err
}
i.uploadingStagingBuffers = append(i.uploadingStagingBuffers, uploadingStagingBuffer)
if rb, ok := i.transiteState(_D3D12_RESOURCE_STATE_COPY_DEST); ok {
i.graphics.copyCommandList.ResourceBarrier([]_D3D12_RESOURCE_BARRIER_Transition{rb})
}
m, err := uploadingStagingBuffer.Map(0, &_D3D12_RANGE{0, 0})
if err != nil {
return err
}
i.graphics.needFlushCopyCommandList = true
srcBytes := unsafe.Slice((*byte)(unsafe.Pointer(m)), totalBytes)
for _, a := range args {
for j := 0; j < a.Region.Dy(); j++ {
copy(srcBytes[((a.Region.Min.Y-region.Min.Y)+j)*int(layouts.Footprint.RowPitch)+(a.Region.Min.X-region.Min.X)*4:], a.Pixels[j*a.Region.Dx()*4:(j+1)*a.Region.Dx()*4])
}
}
for _, a := range args {
dst := _D3D12_TEXTURE_COPY_LOCATION_SubresourceIndex{
pResource: i.texture,
Type: _D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
SubresourceIndex: 0,
}
src := _D3D12_TEXTURE_COPY_LOCATION_PlacedFootPrint{
pResource: uploadingStagingBuffer,
Type: _D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
PlacedFootprint: layouts,
}
i.graphics.copyCommandList.CopyTextureRegion_SubresourceIndex_PlacedFootPrint(
&dst, uint32(a.Region.Min.X), uint32(a.Region.Min.Y), 0, &src, &_D3D12_BOX{
left: uint32(a.Region.Min.X - region.Min.X),
top: uint32(a.Region.Min.Y - region.Min.Y),
front: 0,
right: uint32(a.Region.Max.X - region.Min.X),
bottom: uint32(a.Region.Max.Y - region.Min.Y),
back: 1,
})
}
uploadingStagingBuffer.Unmap(0, nil)
return nil
}
func (i *image12) resource() *_ID3D12Resource {
if i.screen {
return i.graphics.renderTargets[i.graphics.frameIndex]
}
return i.texture
}
func (i *image12) state() _D3D12_RESOURCE_STATES {
if i.screen {
return i.states[i.graphics.frameIndex]
}
return i.states[0]
}
func (i *image12) setState(newState _D3D12_RESOURCE_STATES) {
if i.screen {
i.states[i.graphics.frameIndex] = newState
return
}
i.states[0] = newState
}
func (i *image12) transiteState(newState _D3D12_RESOURCE_STATES) (_D3D12_RESOURCE_BARRIER_Transition, bool) {
if i.state() == newState {
return _D3D12_RESOURCE_BARRIER_Transition{}, false
}
oldState := i.state()
i.setState(newState)
return _D3D12_RESOURCE_BARRIER_Transition{
Type: _D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
Flags: _D3D12_RESOURCE_BARRIER_FLAG_NONE,
Transition: _D3D12_RESOURCE_TRANSITION_BARRIER{
pResource: i.resource(),
Subresource: _D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
StateBefore: oldState,
StateAfter: newState,
},
}, true
}
func (i *image12) internalSize() (int, int) {
if i.screen {
return i.width, i.height
}
return graphics.InternalImageSize(i.width), graphics.InternalImageSize(i.height)
}
func (i *image12) setAsRenderTarget(drawCommandList *_ID3D12GraphicsCommandList, device *_ID3D12Device, useStencil bool) error {
if err := i.ensureRenderTargetView(device); err != nil {
return err
}
if i.screen {
if useStencil {
return fmt.Errorf("directx: stencils are not available on the screen framebuffer")
}
rtv, err := i.graphics.rtvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
if err != nil {
return err
}
rtv.Offset(int32(i.graphics.frameIndex), i.graphics.rtvDescriptorSize)
drawCommandList.OMSetRenderTargets([]_D3D12_CPU_DESCRIPTOR_HANDLE{rtv}, false, nil)
return nil
}
rtv, err := i.rtvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
if err != nil {
return err
}
if !useStencil {
drawCommandList.OMSetRenderTargets([]_D3D12_CPU_DESCRIPTOR_HANDLE{rtv}, false, nil)
return nil
}
if err := i.ensureDepthStencilView(device); err != nil {
return err
}
dsv, err := i.dsvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
if err != nil {
return err
}
drawCommandList.OMSetStencilRef(0)
drawCommandList.OMSetRenderTargets([]_D3D12_CPU_DESCRIPTOR_HANDLE{rtv}, false, &dsv)
drawCommandList.ClearDepthStencilView(dsv, _D3D12_CLEAR_FLAG_STENCIL, 0, 0, nil)
return nil
}
func (i *image12) ensureRenderTargetView(device *_ID3D12Device) error {
if i.screen {
return nil
}
if i.rtvDescriptorHeap != nil {
return nil
}
h, err := device.CreateDescriptorHeap(&_D3D12_DESCRIPTOR_HEAP_DESC{
Type: _D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
NumDescriptors: 1,
Flags: _D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
})
if err != nil {
return err
}
i.rtvDescriptorHeap = h
rtv, err := i.rtvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
if err != nil {
return err
}
device.CreateRenderTargetView(i.texture, nil, rtv)
return nil
}
func (i *image12) ensureDepthStencilView(device *_ID3D12Device) error {
if i.screen {
return fmt.Errorf("directx: stencils are not available on the screen framebuffer")
}
if i.dsvDescriptorHeap != nil {
return nil
}
h, err := device.CreateDescriptorHeap(&_D3D12_DESCRIPTOR_HEAP_DESC{
Type: _D3D12_DESCRIPTOR_HEAP_TYPE_DSV,
NumDescriptors: 1,
Flags: _D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
NodeMask: 0,
})
if err != nil {
return err
}
i.dsvDescriptorHeap = h
dsv, err := i.dsvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
if err != nil {
return err
}
if i.stencil == nil {
s, err := device.CreateCommittedResource(&_D3D12_HEAP_PROPERTIES{
Type: _D3D12_HEAP_TYPE_DEFAULT,
CPUPageProperty: _D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
MemoryPoolPreference: _D3D12_MEMORY_POOL_UNKNOWN,
CreationNodeMask: 1,
VisibleNodeMask: 1,
}, _D3D12_HEAP_FLAG_NONE, &_D3D12_RESOURCE_DESC{
Dimension: _D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Alignment: 0,
Width: uint64(graphics.InternalImageSize(i.width)),
Height: uint32(graphics.InternalImageSize(i.height)),
DepthOrArraySize: 1,
MipLevels: 0,
Format: _DXGI_FORMAT_D24_UNORM_S8_UINT,
SampleDesc: _DXGI_SAMPLE_DESC{
Count: 1,
Quality: 0,
},
Layout: _D3D12_TEXTURE_LAYOUT_UNKNOWN,
Flags: _D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL,
}, _D3D12_RESOURCE_STATE_DEPTH_WRITE, &_D3D12_CLEAR_VALUE{
Format: _DXGI_FORMAT_D24_UNORM_S8_UINT,
})
if err != nil {
return err
}
i.stencil = s
}
device.CreateDepthStencilView(i.stencil, nil, dsv)
return nil
}
func (i *image12) releaseUploadingStagingBuffers() {
for idx, buf := range i.uploadingStagingBuffers {
buf.Release()
i.uploadingStagingBuffers[idx] = nil
}
i.uploadingStagingBuffers = i.uploadingStagingBuffers[:0]
}
@@ -0,0 +1,567 @@
// 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"
)
var inputElementDescsForDX12 = []_D3D12_INPUT_ELEMENT_DESC{
{
SemanticName: &([]byte("POSITION\000"))[0],
SemanticIndex: 0,
Format: _DXGI_FORMAT_R32G32_FLOAT,
InputSlot: 0,
AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT,
InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
{
SemanticName: &([]byte("TEXCOORD\000"))[0],
SemanticIndex: 0,
Format: _DXGI_FORMAT_R32G32_FLOAT,
InputSlot: 0,
AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT,
InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
{
SemanticName: &([]byte("COLOR\000"))[0],
SemanticIndex: 0,
Format: _DXGI_FORMAT_R32G32B32A32_FLOAT,
InputSlot: 0,
AlignedByteOffset: _D3D12_APPEND_ALIGNED_ELEMENT,
InputSlotClass: _D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
InstanceDataStepRate: 0,
},
}
const numDescriptorsPerFrame = 32
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.
switch f {
case graphicsdriver.BlendFactorZero:
return _D3D12_BLEND_ZERO
case graphicsdriver.BlendFactorOne:
return _D3D12_BLEND_ONE
case graphicsdriver.BlendFactorSourceColor:
if alpha {
return _D3D12_BLEND_SRC_ALPHA
}
return _D3D12_BLEND_SRC_COLOR
case graphicsdriver.BlendFactorOneMinusSourceColor:
if alpha {
return _D3D12_BLEND_INV_SRC_ALPHA
}
return _D3D12_BLEND_INV_SRC_COLOR
case graphicsdriver.BlendFactorSourceAlpha:
return _D3D12_BLEND_SRC_ALPHA
case graphicsdriver.BlendFactorOneMinusSourceAlpha:
return _D3D12_BLEND_INV_SRC_ALPHA
case graphicsdriver.BlendFactorDestinationColor:
if alpha {
return _D3D12_BLEND_DEST_ALPHA
}
return _D3D12_BLEND_DEST_COLOR
case graphicsdriver.BlendFactorOneMinusDestinationColor:
if alpha {
return _D3D12_BLEND_INV_DEST_ALPHA
}
return _D3D12_BLEND_INV_DEST_COLOR
case graphicsdriver.BlendFactorDestinationAlpha:
return _D3D12_BLEND_DEST_ALPHA
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return _D3D12_BLEND_INV_DEST_ALPHA
case graphicsdriver.BlendFactorSourceAlphaSaturated:
return _D3D12_BLEND_SRC_ALPHA_SAT
default:
panic(fmt.Sprintf("directx: invalid blend factor: %d", f))
}
}
func blendOperationToBlendOp12(o graphicsdriver.BlendOperation) _D3D12_BLEND_OP {
switch o {
case graphicsdriver.BlendOperationAdd:
return _D3D12_BLEND_OP_ADD
case graphicsdriver.BlendOperationSubtract:
return _D3D12_BLEND_OP_SUBTRACT
case graphicsdriver.BlendOperationReverseSubtract:
return _D3D12_BLEND_OP_REV_SUBTRACT
case graphicsdriver.BlendOperationMin:
return _D3D12_BLEND_OP_MIN
case graphicsdriver.BlendOperationMax:
return _D3D12_BLEND_OP_MAX
default:
panic(fmt.Sprintf("directx: invalid blend operation: %d", o))
}
}
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) 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, fillRule graphicsdriver.FillRule) 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 bufferSizeAlignment = 256
bufferSize := uint32(unsafe.Sizeof(uint32(0))) * uint32(len(uniforms))
if bufferSize > 0 {
bufferSize = ((bufferSize-1)/bufferSizeAlignment + 1) * bufferSizeAlignment
}
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.
copy(unsafe.Slice((*uint32)(unsafe.Pointer(m)), len(uniforms)), uniforms)
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)
if fillRule == graphicsdriver.FillAll {
s, err := shader.pipelineState(blend, noStencil, screen)
if err != nil {
return err
}
commandList.SetPipelineState(s)
}
for _, dstRegion := range dstRegions {
commandList.RSSetScissorRects([]_D3D12_RECT{
{
left: int32(dstRegion.Region.Min.X),
top: int32(dstRegion.Region.Min.Y),
right: int32(dstRegion.Region.Max.X),
bottom: int32(dstRegion.Region.Max.Y),
},
})
switch fillRule {
case graphicsdriver.FillAll:
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
case graphicsdriver.NonZero:
s, err := shader.pipelineState(blend, incrementStencil, screen)
if err != nil {
return err
}
commandList.SetPipelineState(s)
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
case graphicsdriver.EvenOdd:
s, err := shader.pipelineState(blend, invertStencil, screen)
if err != nil {
return err
}
commandList.SetPipelineState(s)
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
}
if fillRule != graphicsdriver.FillAll {
s, err := shader.pipelineState(blend, drawWithStencil, screen)
if err != nil {
return err
}
commandList.SetPipelineState(s)
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
}
indexOffset += dstRegion.IndexCount
}
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 (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3DBlob, blend graphicsdriver.Blend, 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,
},
}
var writeMask uint8
if stencilMode == noStencil || stencilMode == drawWithStencil {
writeMask = uint8(_D3D12_COLOR_WRITE_ENABLE_ALL)
}
switch stencilMode {
case incrementStencil:
depthStencilDesc.StencilEnable = 1
depthStencilDesc.FrontFace.StencilPassOp = _D3D12_STENCIL_OP_INCR
depthStencilDesc.BackFace.StencilPassOp = _D3D12_STENCIL_OP_DECR
case invertStencil:
depthStencilDesc.StencilEnable = 1
depthStencilDesc.FrontFace.StencilPassOp = _D3D12_STENCIL_OP_INVERT
depthStencilDesc.BackFace.StencilPassOp = _D3D12_STENCIL_OP_INVERT
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.
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: 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,
},
},
},
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: &inputElementDescsForDX12[0],
NumElements: uint32(len(inputElementDescsForDX12)),
},
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]
}
@@ -0,0 +1,195 @@
// 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 (
"unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
type shader11 struct {
graphics *graphics11
id graphicsdriver.ShaderID
uniformTypes []shaderir.Type
uniformOffsets []int
vertexShaderBlob *_ID3DBlob
pixelShaderBlob *_ID3DBlob
inputLayout *_ID3D11InputLayout
vertexShader *_ID3D11VertexShader
pixelShader *_ID3D11PixelShader
constantBuffer *_ID3D11Buffer
}
func (s *shader11) ID() graphicsdriver.ShaderID {
return s.id
}
func (s *shader11) Dispose() {
s.graphics.removeShader(s)
}
func (s *shader11) disposeImpl() {
if s.pixelShaderBlob != nil {
s.pixelShaderBlob.Release()
s.pixelShaderBlob = nil
}
if s.vertexShaderBlob != nil {
count := s.vertexShaderBlob.Release()
if count == 0 {
for k, v := range vertexShaderCache {
if v == s.vertexShaderBlob {
delete(vertexShaderCache, k)
}
}
}
s.vertexShader = nil
}
if s.inputLayout != nil {
s.inputLayout.Release()
s.inputLayout = nil
}
if s.vertexShader != nil {
s.vertexShader.Release()
s.vertexShader = nil
}
if s.pixelShader != nil {
s.pixelShader.Release()
s.pixelShader = nil
}
if s.constantBuffer != nil {
s.constantBuffer.Release()
s.constantBuffer = nil
}
}
func (s *shader11) use(uniforms []uint32, srcs [graphics.ShaderImageCount]*image11) error {
vs, err := s.ensureVertexShader()
if err != nil {
return err
}
s.graphics.deviceContext.VSSetShader(vs, nil)
ps, err := s.ensurePixelShader()
if err != nil {
return err
}
s.graphics.deviceContext.PSSetShader(ps, nil)
il, err := s.ensureInputLayout()
if err != nil {
return err
}
s.graphics.deviceContext.IASetInputLayout(il)
cb, err := s.ensureConstantBuffer()
if err != nil {
return err
}
s.graphics.deviceContext.VSSetConstantBuffers(0, []*_ID3D11Buffer{cb})
s.graphics.deviceContext.PSSetConstantBuffers(0, []*_ID3D11Buffer{cb})
// Send the constant buffer data.
uniforms = adjustUniforms(s.uniformTypes, s.uniformOffsets, uniforms)
var mapped _D3D11_MAPPED_SUBRESOURCE
if err := s.graphics.deviceContext.Map(unsafe.Pointer(cb), 0, _D3D11_MAP_WRITE_DISCARD, 0, &mapped); err != nil {
return err
}
copy(unsafe.Slice((*uint32)(mapped.pData), len(uniforms)), uniforms)
s.graphics.deviceContext.Unmap(unsafe.Pointer(cb), 0)
// Set the render sources.
var srvs [graphics.ShaderImageCount]*_ID3D11ShaderResourceView
for i, src := range srcs {
if src == nil {
continue
}
srv, err := src.getShaderResourceView()
if err != nil {
return err
}
srvs[i] = srv
}
s.graphics.deviceContext.PSSetShaderResources(0, srvs[:])
return nil
}
func (s *shader11) ensureInputLayout() (*_ID3D11InputLayout, error) {
if s.inputLayout != nil {
return s.inputLayout, nil
}
i, err := s.graphics.device.CreateInputLayout(inputElementDescsForDX11, s.vertexShaderBlob.GetBufferPointer(), s.vertexShaderBlob.GetBufferSize())
if err != nil {
return nil, err
}
s.inputLayout = i
return i, nil
}
func (s *shader11) ensureVertexShader() (*_ID3D11VertexShader, error) {
if s.vertexShader != nil {
return s.vertexShader, nil
}
vs, err := s.graphics.device.CreateVertexShader(s.vertexShaderBlob.GetBufferPointer(), s.vertexShaderBlob.GetBufferSize(), nil)
if err != nil {
return nil, err
}
s.vertexShader = vs
return vs, nil
}
func (s *shader11) ensurePixelShader() (*_ID3D11PixelShader, error) {
if s.pixelShader != nil {
return s.pixelShader, nil
}
ps, err := s.graphics.device.CreatePixelShader(s.pixelShaderBlob.GetBufferPointer(), s.pixelShaderBlob.GetBufferSize(), nil)
if err != nil {
return nil, err
}
s.pixelShader = ps
return ps, nil
}
func alignUp16(x uint32) uint32 {
if x%16 == 0 {
return x
}
return x + 16 - (x % 16)
}
func (s *shader11) ensureConstantBuffer() (*_ID3D11Buffer, error) {
if s.constantBuffer != nil {
return s.constantBuffer, nil
}
cb, err := s.graphics.device.CreateBuffer(&_D3D11_BUFFER_DESC{
ByteWidth: alignUp16(uint32(constantBufferSize(s.uniformTypes, s.uniformOffsets)) * 4),
Usage: _D3D11_USAGE_DYNAMIC,
BindFlags: uint32(_D3D11_BIND_CONSTANT_BUFFER),
CPUAccessFlags: uint32(_D3D11_CPU_ACCESS_WRITE),
}, nil)
if err != nil {
return nil, err
}
s.constantBuffer = cb
return cb, nil
}
@@ -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
}
@@ -0,0 +1,262 @@
// 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"
"golang.org/x/sync/errgroup"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
var vertexShaderCache = map[string]*_ID3DBlob{}
func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) {
var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3)
defer func() {
if ferr == nil {
return
}
if vsh != nil {
vsh.Release()
}
if psh != nil {
psh.Release()
}
}()
var wg errgroup.Group
// Vertex shaders are likely the same. If so, reuse the same _ID3DBlob.
if v, ok := vertexShaderCache[vs]; ok {
// Increment the reference count not to release this object unexpectedly.
// The value will be removed when the count reached 0.
// See (*Shader).disposeImpl.
v.AddRef()
vsh = v
} else {
defer func() {
if ferr == nil {
vertexShaderCache[vs] = vsh
}
}()
wg.Go(func() error {
v, err := _D3DCompile([]byte(vs), "shader", nil, nil, "VSMain", "vs_4_0", flag, 0)
if err != nil {
return fmt.Errorf("directx: D3DCompile for VSMain failed, original source: %s, %w", vs, err)
}
vsh = v
return nil
})
}
wg.Go(func() error {
p, err := _D3DCompile([]byte(ps), "shader", nil, nil, "PSMain", "ps_4_0", flag, 0)
if err != nil {
return fmt.Errorf("directx: D3DCompile for PSMain failed, original source: %s, %w", ps, err)
}
psh = p
return nil
})
if err := wg.Wait(); err != nil {
return nil, nil, err
}
return
}
func constantBufferSize(uniformTypes []shaderir.Type, uniformOffsets []int) int {
var size int
for i, typ := range uniformTypes {
if size < uniformOffsets[i]/4 {
size = uniformOffsets[i] / 4
}
switch typ.Main {
case shaderir.Float:
size += 1
case shaderir.Int:
size += 1
case shaderir.Vec2, shaderir.IVec2:
size += 2
case shaderir.Vec3, shaderir.IVec3:
size += 3
case shaderir.Vec4, shaderir.IVec4:
size += 4
case shaderir.Mat2:
size += 6
case shaderir.Mat3:
size += 11
case shaderir.Mat4:
size += 16
case shaderir.Array:
// Each element is aligned to the boundary.
switch typ.Sub[0].Main {
case shaderir.Float:
size += 4*(typ.Length-1) + 1
case shaderir.Int:
size += 4*(typ.Length-1) + 1
case shaderir.Vec2, shaderir.IVec2:
size += 4*(typ.Length-1) + 2
case shaderir.Vec3, shaderir.IVec3:
size += 4*(typ.Length-1) + 3
case shaderir.Vec4, shaderir.IVec4:
size += 4 * typ.Length
case shaderir.Mat2:
size += 8*(typ.Length-1) + 6
case shaderir.Mat3:
size += 12*(typ.Length-1) + 11
case shaderir.Mat4:
size += 16 * typ.Length
default:
panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", typ.String()))
}
default:
panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", typ.String()))
}
}
return size
}
func adjustUniforms(uniformTypes []shaderir.Type, uniformOffsets []int, uniforms []uint32) []uint32 {
var fs []uint32
var idx int
for i, typ := range uniformTypes {
if len(fs) < uniformOffsets[i]/4 {
fs = append(fs, make([]uint32, uniformOffsets[i]/4-len(fs))...)
}
n := typ.Uint32Count()
switch typ.Main {
case shaderir.Float:
fs = append(fs, uniforms[idx:idx+1]...)
case shaderir.Int:
fs = append(fs, uniforms[idx:idx+1]...)
case shaderir.Vec2, shaderir.IVec2:
fs = append(fs, uniforms[idx:idx+2]...)
case shaderir.Vec3, shaderir.IVec3:
fs = append(fs, uniforms[idx:idx+3]...)
case shaderir.Vec4, shaderir.IVec4:
fs = append(fs, uniforms[idx:idx+4]...)
case shaderir.Mat2:
fs = append(fs,
uniforms[idx+0], uniforms[idx+2], 0, 0,
uniforms[idx+1], uniforms[idx+3],
)
case shaderir.Mat3:
fs = append(fs,
uniforms[idx+0], uniforms[idx+3], uniforms[idx+6], 0,
uniforms[idx+1], uniforms[idx+4], uniforms[idx+7], 0,
uniforms[idx+2], uniforms[idx+5], uniforms[idx+8],
)
case shaderir.Mat4:
if i == graphics.ProjectionMatrixUniformVariableIndex {
// In DirectX, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't
// match. Then, the Y direction must be inverted.
// Invert the sign bits as float32 values.
fs = append(fs,
uniforms[idx+0], uniforms[idx+4], uniforms[idx+8], uniforms[idx+12],
uniforms[idx+1]^(1<<31), uniforms[idx+5]^(1<<31), uniforms[idx+9]^(1<<31), uniforms[idx+13]^(1<<31),
uniforms[idx+2], uniforms[idx+6], uniforms[idx+10], uniforms[idx+14],
uniforms[idx+3], uniforms[idx+7], uniforms[idx+11], uniforms[idx+15],
)
} else {
fs = append(fs,
uniforms[idx+0], uniforms[idx+4], uniforms[idx+8], uniforms[idx+12],
uniforms[idx+1], uniforms[idx+5], uniforms[idx+9], uniforms[idx+13],
uniforms[idx+2], uniforms[idx+6], uniforms[idx+10], uniforms[idx+14],
uniforms[idx+3], uniforms[idx+7], uniforms[idx+11], uniforms[idx+15],
)
}
case shaderir.Array:
// Each element is aligned to the boundary.
switch typ.Sub[0].Main {
case shaderir.Float:
for j := 0; j < typ.Length; j++ {
fs = append(fs, uniforms[idx+j])
if j < typ.Length-1 {
fs = append(fs, 0, 0, 0)
}
}
case shaderir.Int:
for j := 0; j < typ.Length; j++ {
fs = append(fs, uniforms[idx+j])
if j < typ.Length-1 {
fs = append(fs, 0, 0, 0)
}
}
case shaderir.Vec2, shaderir.IVec2:
for j := 0; j < typ.Length; j++ {
fs = append(fs, uniforms[idx+2*j:idx+2*(j+1)]...)
if j < typ.Length-1 {
fs = append(fs, 0, 0)
}
}
case shaderir.Vec3, shaderir.IVec3:
for j := 0; j < typ.Length; j++ {
fs = append(fs, uniforms[idx+3*j:idx+3*(j+1)]...)
if j < typ.Length-1 {
fs = append(fs, 0)
}
}
case shaderir.Vec4, shaderir.IVec4:
fs = append(fs, uniforms[idx:idx+4*typ.Length]...)
case shaderir.Mat2:
for j := 0; j < typ.Length; j++ {
u := uniforms[idx+4*j : idx+4*(j+1)]
fs = append(fs,
u[0], u[2], 0, 0,
u[1], u[3], 0, 0,
)
}
if typ.Length > 0 {
fs = fs[:len(fs)-2]
}
case shaderir.Mat3:
for j := 0; j < typ.Length; j++ {
u := uniforms[idx+9*j : idx+9*(j+1)]
fs = append(fs,
u[0], u[3], u[6], 0,
u[1], u[4], u[7], 0,
u[2], u[5], u[8], 0,
)
}
if typ.Length > 0 {
fs = fs[:len(fs)-1]
}
case shaderir.Mat4:
for j := 0; j < typ.Length; j++ {
u := uniforms[idx+16*j : idx+16*(j+1)]
fs = append(fs,
u[0], u[4], u[8], u[12],
u[1], u[5], u[9], u[13],
u[2], u[6], u[10], u[14],
u[3], u[7], u[11], u[15],
)
}
default:
panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", typ.String()))
}
default:
panic(fmt.Sprintf("directx: not implemented type for uniform variables: %s", typ.String()))
}
idx += n
}
return fs
}