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,77 @@
// 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.
package graphicsdriver
type Blend struct {
BlendFactorSourceRGB BlendFactor
BlendFactorSourceAlpha BlendFactor
BlendFactorDestinationRGB BlendFactor
BlendFactorDestinationAlpha BlendFactor
BlendOperationRGB BlendOperation
BlendOperationAlpha BlendOperation
}
type BlendFactor byte
const (
BlendFactorZero BlendFactor = iota
BlendFactorOne
BlendFactorSourceColor
BlendFactorOneMinusSourceColor
BlendFactorSourceAlpha
BlendFactorOneMinusSourceAlpha
BlendFactorDestinationColor
BlendFactorOneMinusDestinationColor
BlendFactorDestinationAlpha
BlendFactorOneMinusDestinationAlpha
BlendFactorSourceAlphaSaturated
)
type BlendOperation byte
const (
BlendOperationAdd BlendOperation = iota
BlendOperationSubtract
BlendOperationReverseSubtract
BlendOperationMin
BlendOperationMax
)
var BlendSourceOver = Blend{
BlendFactorSourceRGB: BlendFactorOne,
BlendFactorSourceAlpha: BlendFactorOne,
BlendFactorDestinationRGB: BlendFactorOneMinusSourceAlpha,
BlendFactorDestinationAlpha: BlendFactorOneMinusSourceAlpha,
BlendOperationRGB: BlendOperationAdd,
BlendOperationAlpha: BlendOperationAdd,
}
var BlendClear = Blend{
BlendFactorSourceRGB: BlendFactorZero,
BlendFactorSourceAlpha: BlendFactorZero,
BlendFactorDestinationRGB: BlendFactorZero,
BlendFactorDestinationAlpha: BlendFactorZero,
BlendOperationRGB: BlendOperationAdd,
BlendOperationAlpha: BlendOperationAdd,
}
var BlendCopy = Blend{
BlendFactorSourceRGB: BlendFactorOne,
BlendFactorSourceAlpha: BlendFactorOne,
BlendFactorDestinationRGB: BlendFactorZero,
BlendFactorDestinationAlpha: BlendFactorZero,
BlendOperationRGB: BlendOperationAdd,
BlendOperationAlpha: BlendOperationAdd,
}
@@ -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
}
@@ -0,0 +1,97 @@
// Copyright 2018 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 graphicsdriver
import (
"fmt"
"image"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
type DstRegion struct {
Region image.Rectangle
IndexCount int
}
type FillRule int
const (
FillAll FillRule = iota
NonZero
EvenOdd
)
func (f FillRule) String() string {
switch f {
case FillAll:
return "FillAll"
case NonZero:
return "NonZero"
case EvenOdd:
return "EvenOdd"
default:
return fmt.Sprintf("FillRule(%d)", f)
}
}
const (
InvalidImageID = 0
InvalidShaderID = 0
)
type Graphics interface {
Initialize() error
Begin() error
End(present bool) error
SetTransparent(transparent bool)
SetVertices(vertices []float32, indices []uint32) error
NewImage(width, height int) (Image, error)
NewScreenFramebufferImage(width, height int) (Image, error)
SetVsyncEnabled(enabled bool)
NeedsClearingScreen() bool
MaxImageSize() int
NewShader(program *shaderir.Program) (Shader, error)
// DrawTriangles draws an image onto another image with the given parameters.
DrawTriangles(dst ImageID, srcs [graphics.ShaderImageCount]ImageID, shader ShaderID, dstRegions []DstRegion, indexOffset int, blend Blend, uniforms []uint32, fillRule FillRule) error
}
type Resetter interface {
Reset() error
}
type Image interface {
ID() ImageID
Dispose()
ReadPixels(args []PixelsArgs) error
WritePixels(args []PixelsArgs) error
}
type ImageID int
type PixelsArgs struct {
Pixels []byte
Region image.Rectangle
}
type Shader interface {
ID() ShaderID
Dispose()
}
type ShaderID int
@@ -0,0 +1,5 @@
These packages are copied from Dmitri Shuralyov's mtl packages and edited with Dmitri's permission:
* `github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ca` (copied from `dmitri.shuralyov.com/gpu/mtl/example/movingtriangle/internal/ca`)
* `github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl` (copied from `dmitri.shuralyov.com/gpu/mtl`)
* `github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ns` (copied from `dmitri.shuralyov.com/gpu/mtl/example/movingtriangle/internal/ns`)
@@ -0,0 +1,218 @@
// Copyright 2018 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 ca provides access to Apple's Core Animation API (https://developer.apple.com/documentation/quartzcore).
//
// This package is in very early stages of development.
// It's a minimal implementation with scope limited to
// supporting the movingtriangle example.
package ca
import (
"errors"
"fmt"
"runtime"
"unsafe"
"github.com/ebitengine/purego"
"github.com/ebitengine/purego/objc"
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
)
// Layer is an object that manages image-based content and
// allows you to perform animations on that content.
//
// Reference: https://developer.apple.com/documentation/quartzcore/calayer.
type Layer interface {
// Layer returns the underlying CALayer * pointer.
Layer() unsafe.Pointer
}
// MetalLayer is a Core Animation Metal layer, a layer that manages a pool of Metal drawables.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
type MetalLayer struct {
metalLayer objc.ID
}
// MakeMetalLayer creates a new Core Animation Metal layer.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
func MakeMetalLayer() (MetalLayer, error) {
coreGraphics, err := purego.Dlopen("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
if err != nil {
return MetalLayer{}, err
}
cgColorSpaceCreateWithName, err := purego.Dlsym(coreGraphics, "CGColorSpaceCreateWithName")
if err != nil {
return MetalLayer{}, err
}
cgColorSpaceRelease, err := purego.Dlsym(coreGraphics, "CGColorSpaceRelease")
if err != nil {
return MetalLayer{}, err
}
kCGColorSpaceDisplayP3, err := purego.Dlsym(coreGraphics, "kCGColorSpaceDisplayP3")
if err != nil {
return MetalLayer{}, err
}
layer := objc.ID(objc.GetClass("CAMetalLayer")).Send(objc.RegisterName("new"))
if runtime.GOOS != "ios" {
colorspace, _, _ := purego.SyscallN(cgColorSpaceCreateWithName, **(**uintptr)(unsafe.Pointer(&kCGColorSpaceDisplayP3))) // Dlsym returns pointer to symbol so dereference it
layer.Send(objc.RegisterName("setColorspace:"), colorspace)
purego.SyscallN(cgColorSpaceRelease, colorspace)
}
return MetalLayer{layer}, nil
}
// Layer implements the Layer interface.
func (ml MetalLayer) Layer() unsafe.Pointer {
return *(*unsafe.Pointer)(unsafe.Pointer(&ml.metalLayer))
}
// PixelFormat returns the pixel format of textures for rendering layer content.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
func (ml MetalLayer) PixelFormat() mtl.PixelFormat {
return mtl.PixelFormat(ml.metalLayer.Send(objc.RegisterName("pixelFormat")))
}
// SetDevice sets the Metal device responsible for the layer's drawable resources.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device.
func (ml MetalLayer) SetDevice(device mtl.Device) {
ml.metalLayer.Send(objc.RegisterName("setDevice:"), uintptr(device.Device()))
}
// SetOpaque a Boolean value indicating whether the layer contains completely opaque content.
func (ml MetalLayer) SetOpaque(opaque bool) {
ml.metalLayer.Send(objc.RegisterName("setOpaque:"), opaque)
}
// SetPixelFormat controls the pixel format of textures for rendering layer content.
//
// The pixel format for a Metal layer must be PixelFormatBGRA8UNorm, PixelFormatBGRA8UNormSRGB,
// PixelFormatRGBA16Float, PixelFormatBGRA10XR, or PixelFormatBGRA10XRSRGB.
// SetPixelFormat panics for other values.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) {
switch pf {
case mtl.PixelFormatRGBA8UNorm, mtl.PixelFormatRGBA8UNormSRGB, mtl.PixelFormatBGRA8UNorm, mtl.PixelFormatBGRA8UNormSRGB, mtl.PixelFormatStencil8:
default:
panic(errors.New(fmt.Sprintf("invalid pixel format %d", pf)))
}
ml.metalLayer.Send(objc.RegisterName("setPixelFormat:"), uint(pf))
}
// SetMaximumDrawableCount controls the number of Metal drawables in the resource pool
// managed by Core Animation.
//
// It can set to 2 or 3 only. SetMaximumDrawableCount panics for other values.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount.
func (ml MetalLayer) SetMaximumDrawableCount(count int) {
if count < 2 || count > 3 {
panic(errors.New(fmt.Sprintf("failed trying to set maximumDrawableCount to %d outside of the valid range of [2, 3]", count)))
}
ml.metalLayer.Send(objc.RegisterName("setMaximumDrawableCount:"), count)
}
// SetDisplaySyncEnabled controls whether the Metal layer and its drawables
// are synchronized with the display's refresh rate.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled.
func (ml MetalLayer) SetDisplaySyncEnabled(enabled bool) {
if runtime.GOOS == "ios" {
return
}
ml.metalLayer.Send(objc.RegisterName("setDisplaySyncEnabled:"), enabled)
}
// SetDrawableSize sets the size, in pixels, of textures for rendering layer content.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize.
func (ml MetalLayer) SetDrawableSize(width, height int) {
// TODO: once objc supports calling functions with struct arguments replace this with just a ID.Send call
var sel_setDrawableSize = objc.RegisterName("setDrawableSize:")
sig := cocoa.NSMethodSignature_instanceMethodSignatureForSelector(objc.ID(objc.GetClass("CAMetalLayer")), sel_setDrawableSize)
inv := cocoa.NSInvocation_invocationWithMethodSignature(sig)
inv.SetTarget(ml.metalLayer)
inv.SetSelector(sel_setDrawableSize)
inv.SetArgumentAtIndex(unsafe.Pointer(&cocoa.CGSize{Width: cocoa.CGFloat(width), Height: cocoa.CGFloat(height)}), 2)
inv.Invoke()
}
// NextDrawable returns a Metal drawable.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable.
func (ml MetalLayer) NextDrawable() (MetalDrawable, error) {
md := ml.metalLayer.Send(objc.RegisterName("nextDrawable"))
if md == 0 {
return MetalDrawable{}, errors.New("nextDrawable returned nil")
}
return MetalDrawable{md}, nil
}
// PresentsWithTransaction returns a Boolean value that determines whether the layer presents its content using a Core Animation transaction.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction
func (ml MetalLayer) PresentsWithTransaction() bool {
return ml.metalLayer.Send(objc.RegisterName("presentsWithTransaction")) != 0
}
// SetPresentsWithTransaction sets a Boolean value that determines whether the layer presents its content using a Core Animation transaction.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction
func (ml MetalLayer) SetPresentsWithTransaction(presentsWithTransaction bool) {
ml.metalLayer.Send(objc.RegisterName("setPresentsWithTransaction:"), presentsWithTransaction)
}
// SetFramebufferOnly sets a Boolean value that determines whether the layers textures are used only for rendering.
//
// https://developer.apple.com/documentation/quartzcore/cametallayer/1478168-framebufferonly
func (ml MetalLayer) SetFramebufferOnly(framebufferOnly bool) {
ml.metalLayer.Send(objc.RegisterName("setFramebufferOnly:"), framebufferOnly)
}
// MetalDrawable is a displayable resource that can be rendered or written to by Metal.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable.
type MetalDrawable struct {
metalDrawable objc.ID
}
// Drawable implements the mtl.Drawable interface.
func (md MetalDrawable) Drawable() unsafe.Pointer {
return *(*unsafe.Pointer)(unsafe.Pointer(&md.metalDrawable))
}
// Texture returns a Metal texture object representing the drawable object's content.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture.
func (md MetalDrawable) Texture() mtl.Texture {
return mtl.NewTexture(md.metalDrawable.Send(objc.RegisterName("texture")))
}
// Present presents the drawable onscreen as soon as possible.
//
// Reference: https://developer.apple.com/documentation/metal/mtldrawable/1470284-present.
func (md MetalDrawable) Present() {
md.metalDrawable.Send(objc.RegisterName("present"))
}
@@ -0,0 +1,918 @@
// Copyright 2018 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 metal
import (
"fmt"
"image"
"math"
"runtime"
"sort"
"unsafe"
"github.com/ebitengine/purego/objc"
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ca"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
type Graphics struct {
view view
cq mtl.CommandQueue
cb mtl.CommandBuffer
rce mtl.RenderCommandEncoder
dsss map[stencilMode]mtl.DepthStencilState
screenDrawable ca.MetalDrawable
buffers map[mtl.CommandBuffer][]mtl.Buffer
unusedBuffers map[mtl.Buffer]struct{}
lastDst *Image
lastFillRule graphicsdriver.FillRule
vb mtl.Buffer
ib mtl.Buffer
images map[graphicsdriver.ImageID]*Image
nextImageID graphicsdriver.ImageID
shaders map[graphicsdriver.ShaderID]*Shader
nextShaderID graphicsdriver.ShaderID
transparent bool
maxImageSize int
tmpTextures []mtl.Texture
pool cocoa.NSAutoreleasePool
}
type stencilMode int
const (
noStencil stencilMode = iota
incrementStencil
invertStencil
drawWithStencil
)
var (
systemDefaultDevice mtl.Device
systemDefaultDeviceErr error
)
func init() {
// mtl.CreateSystemDefaultDevice must be called on the main thread (#2147).
d, err := mtl.CreateSystemDefaultDevice()
if err != nil {
systemDefaultDeviceErr = err
return
}
systemDefaultDevice = d
}
// NewGraphics creates an implementation of graphicsdriver.Graphics for Metal.
// The returned graphics value is nil iff the error is not nil.
func NewGraphics() (graphicsdriver.Graphics, error) {
// On old mac devices like iMac 2011, Metal is not supported (#779).
// TODO: Is there a better way to check whether Metal is available or not?
// It seems OK to call MTLCreateSystemDefaultDevice multiple times, so this should be fine.
if systemDefaultDeviceErr != nil {
return nil, fmt.Errorf("metal: mtl.CreateSystemDefaultDevice failed: %w", systemDefaultDeviceErr)
}
g := &Graphics{}
if runtime.GOOS != "ios" {
// Initializing a Metal device and a layer must be done in the main thread on macOS.
// Note that this assumes NewGraphics is called on the main thread on desktops.
if err := g.view.initialize(systemDefaultDevice); err != nil {
return nil, err
}
}
return g, nil
}
func (g *Graphics) Begin() error {
// NSAutoreleasePool is required to release drawable correctly (#847).
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/Drawables.html
g.pool = cocoa.NSAutoreleasePool_new()
return nil
}
func (g *Graphics) End(present bool) error {
g.flushIfNeeded(present)
g.screenDrawable = ca.MetalDrawable{}
g.pool.Release()
g.pool.ID = 0
return nil
}
func (g *Graphics) SetWindow(window uintptr) {
// Note that [NSApp mainWindow] returns nil when the window is borderless.
// Then the window is needed to be given explicitly.
g.view.setWindow(window)
}
func (g *Graphics) SetUIView(uiview uintptr) {
// TODO: Should this be called on the main thread?
g.view.setUIView(uiview)
}
func pow2(x uintptr) uintptr {
if x > (math.MaxUint+1)/2 {
return math.MaxUint
}
var p2 uintptr = 1
for p2 < x {
p2 *= 2
}
return p2
}
func (g *Graphics) gcBuffers() {
for cb, bs := range g.buffers {
// If the command buffer still lives, the buffer must not be updated.
// TODO: Handle an error?
if cb.Status() != mtl.CommandBufferStatusCompleted {
continue
}
for _, b := range bs {
if g.unusedBuffers == nil {
g.unusedBuffers = map[mtl.Buffer]struct{}{}
}
g.unusedBuffers[b] = struct{}{}
}
delete(g.buffers, cb)
cb.Release()
}
const maxUnusedBuffers = 10
if len(g.unusedBuffers) > maxUnusedBuffers {
bufs := make([]mtl.Buffer, 0, len(g.unusedBuffers))
for b := range g.unusedBuffers {
bufs = append(bufs, b)
}
sort.Slice(bufs, func(a, b int) bool {
return bufs[a].Length() > bufs[b].Length()
})
for _, b := range bufs[maxUnusedBuffers:] {
delete(g.unusedBuffers, b)
b.Release()
}
}
}
func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
if g.cb == (mtl.CommandBuffer{}) {
g.cb = g.cq.MakeCommandBuffer()
}
var newBuf mtl.Buffer
for b := range g.unusedBuffers {
if b.Length() >= length {
newBuf = b
delete(g.unusedBuffers, b)
break
}
}
if newBuf == (mtl.Buffer{}) {
newBuf = g.view.getMTLDevice().MakeBufferWithLength(pow2(length), resourceStorageMode)
}
if g.buffers == nil {
g.buffers = map[mtl.CommandBuffer][]mtl.Buffer{}
}
if _, ok := g.buffers[g.cb]; !ok {
g.cb.Retain()
}
g.buffers[g.cb] = append(g.buffers[g.cb], newBuf)
return newBuf
}
func (g *Graphics) SetVertices(vertices []float32, indices []uint32) error {
vbSize := unsafe.Sizeof(vertices[0]) * uintptr(len(vertices))
ibSize := unsafe.Sizeof(indices[0]) * uintptr(len(indices))
g.vb = g.availableBuffer(vbSize)
g.vb.CopyToContents(unsafe.Pointer(&vertices[0]), vbSize)
g.ib = g.availableBuffer(ibSize)
g.ib.CopyToContents(unsafe.Pointer(&indices[0]), ibSize)
return nil
}
func (g *Graphics) flushIfNeeded(present bool) {
if g.cb == (mtl.CommandBuffer{}) && !present {
return
}
g.flushRenderCommandEncoderIfNeeded()
if present {
// This check is necessary when skipping to render the screen (SetScreenClearedEveryFrame(false)).
if g.screenDrawable == (ca.MetalDrawable{}) && g.cb != (mtl.CommandBuffer{}) {
g.screenDrawable = g.view.nextDrawable()
}
if g.screenDrawable != (ca.MetalDrawable{}) {
g.cb.PresentDrawable(g.screenDrawable)
}
}
g.cb.Commit()
for _, t := range g.tmpTextures {
t.Release()
}
g.tmpTextures = g.tmpTextures[:0]
g.cb = mtl.CommandBuffer{}
}
func (g *Graphics) checkSize(width, height int) {
if width < 1 {
panic(fmt.Sprintf("metal: width (%d) must be equal or more than %d", width, 1))
}
if height < 1 {
panic(fmt.Sprintf("metal: height (%d) must be equal or more than %d", height, 1))
}
m := g.MaxImageSize()
if width > m {
panic(fmt.Sprintf("metal: width (%d) must be less than or equal to %d", width, m))
}
if height > m {
panic(fmt.Sprintf("metal: height (%d) must be less than or equal to %d", height, m))
}
}
func (g *Graphics) genNextImageID() graphicsdriver.ImageID {
g.nextImageID++
return g.nextImageID
}
func (g *Graphics) genNextShaderID() graphicsdriver.ShaderID {
g.nextShaderID++
return g.nextShaderID
}
func (g *Graphics) NewImage(width, height int) (graphicsdriver.Image, error) {
g.checkSize(width, height)
td := mtl.TextureDescriptor{
TextureType: mtl.TextureType2D,
PixelFormat: mtl.PixelFormatRGBA8UNorm,
Width: graphics.InternalImageSize(width),
Height: graphics.InternalImageSize(height),
StorageMode: storageMode,
Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget,
}
t := g.view.getMTLDevice().MakeTexture(td)
i := &Image{
id: g.genNextImageID(),
graphics: g,
width: width,
height: height,
texture: t,
}
g.addImage(i)
return i, nil
}
func (g *Graphics) NewScreenFramebufferImage(width, height int) (graphicsdriver.Image, error) {
g.view.setDrawableSize(width, height)
i := &Image{
id: g.genNextImageID(),
graphics: g,
width: width,
height: height,
screen: true,
}
g.addImage(i)
return i, nil
}
func (g *Graphics) addImage(img *Image) {
if g.images == nil {
g.images = map[graphicsdriver.ImageID]*Image{}
}
if _, ok := g.images[img.id]; ok {
panic(fmt.Sprintf("metal: image ID %d was already registered", img.id))
}
g.images[img.id] = img
}
func (g *Graphics) removeImage(img *Image) {
delete(g.images, img.id)
}
func (g *Graphics) SetTransparent(transparent bool) {
g.transparent = transparent
}
func blendFactorToMetalBlendFactor(c graphicsdriver.BlendFactor) mtl.BlendFactor {
switch c {
case graphicsdriver.BlendFactorZero:
return mtl.BlendFactorZero
case graphicsdriver.BlendFactorOne:
return mtl.BlendFactorOne
case graphicsdriver.BlendFactorSourceColor:
return mtl.BlendFactorSourceColor
case graphicsdriver.BlendFactorOneMinusSourceColor:
return mtl.BlendFactorOneMinusSourceColor
case graphicsdriver.BlendFactorSourceAlpha:
return mtl.BlendFactorSourceAlpha
case graphicsdriver.BlendFactorOneMinusSourceAlpha:
return mtl.BlendFactorOneMinusSourceAlpha
case graphicsdriver.BlendFactorDestinationColor:
return mtl.BlendFactorDestinationColor
case graphicsdriver.BlendFactorOneMinusDestinationColor:
return mtl.BlendFactorOneMinusDestinationColor
case graphicsdriver.BlendFactorDestinationAlpha:
return mtl.BlendFactorDestinationAlpha
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return mtl.BlendFactorOneMinusDestinationAlpha
case graphicsdriver.BlendFactorSourceAlphaSaturated:
return mtl.BlendFactorSourceAlphaSaturated
default:
panic(fmt.Sprintf("metal: invalid blend factor: %d", c))
}
}
func blendOperationToMetalBlendOperation(o graphicsdriver.BlendOperation) mtl.BlendOperation {
switch o {
case graphicsdriver.BlendOperationAdd:
return mtl.BlendOperationAdd
case graphicsdriver.BlendOperationSubtract:
return mtl.BlendOperationSubtract
case graphicsdriver.BlendOperationReverseSubtract:
return mtl.BlendOperationReverseSubtract
case graphicsdriver.BlendOperationMin:
return mtl.BlendOperationMin
case graphicsdriver.BlendOperationMax:
return mtl.BlendOperationMax
default:
panic(fmt.Sprintf("metal: invalid blend operation: %d", o))
}
}
func (g *Graphics) Initialize() error {
// Creating *State objects are expensive and reuse them whenever possible.
// See https://developer.apple.com/library/archive/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/Cmd-Submiss/Cmd-Submiss.html
for _, dss := range g.dsss {
dss.Release()
}
if g.dsss == nil {
g.dsss = map[stencilMode]mtl.DepthStencilState{}
}
if runtime.GOOS == "ios" {
// Initializing a Metal device and a layer must be done in the render thread on iOS.
if err := g.view.initialize(systemDefaultDevice); err != nil {
return err
}
}
if g.transparent {
g.view.ml.SetOpaque(false)
}
// The stencil reference value is always 0 (default).
g.dsss[noStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
BackFaceStencil: mtl.StencilDescriptor{
StencilFailureOperation: mtl.StencilOperationKeep,
DepthFailureOperation: mtl.StencilOperationKeep,
DepthStencilPassOperation: mtl.StencilOperationKeep,
StencilCompareFunction: mtl.CompareFunctionAlways,
},
FrontFaceStencil: mtl.StencilDescriptor{
StencilFailureOperation: mtl.StencilOperationKeep,
DepthFailureOperation: mtl.StencilOperationKeep,
DepthStencilPassOperation: mtl.StencilOperationKeep,
StencilCompareFunction: mtl.CompareFunctionAlways,
},
})
g.dsss[incrementStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
BackFaceStencil: mtl.StencilDescriptor{
StencilFailureOperation: mtl.StencilOperationKeep,
DepthFailureOperation: mtl.StencilOperationKeep,
DepthStencilPassOperation: mtl.StencilOperationDecrementWrap,
StencilCompareFunction: mtl.CompareFunctionAlways,
},
FrontFaceStencil: mtl.StencilDescriptor{
StencilFailureOperation: mtl.StencilOperationKeep,
DepthFailureOperation: mtl.StencilOperationKeep,
DepthStencilPassOperation: mtl.StencilOperationIncrementWrap,
StencilCompareFunction: mtl.CompareFunctionAlways,
},
})
g.dsss[invertStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
BackFaceStencil: mtl.StencilDescriptor{
StencilFailureOperation: mtl.StencilOperationKeep,
DepthFailureOperation: mtl.StencilOperationKeep,
DepthStencilPassOperation: mtl.StencilOperationInvert,
StencilCompareFunction: mtl.CompareFunctionAlways,
},
FrontFaceStencil: mtl.StencilDescriptor{
StencilFailureOperation: mtl.StencilOperationKeep,
DepthFailureOperation: mtl.StencilOperationKeep,
DepthStencilPassOperation: mtl.StencilOperationInvert,
StencilCompareFunction: mtl.CompareFunctionAlways,
},
})
g.dsss[drawWithStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
BackFaceStencil: mtl.StencilDescriptor{
StencilFailureOperation: mtl.StencilOperationKeep,
DepthFailureOperation: mtl.StencilOperationKeep,
DepthStencilPassOperation: mtl.StencilOperationKeep,
StencilCompareFunction: mtl.CompareFunctionNotEqual,
},
FrontFaceStencil: mtl.StencilDescriptor{
StencilFailureOperation: mtl.StencilOperationKeep,
DepthFailureOperation: mtl.StencilOperationKeep,
DepthStencilPassOperation: mtl.StencilOperationKeep,
StencilCompareFunction: mtl.CompareFunctionNotEqual,
},
})
g.cq = g.view.getMTLDevice().MakeCommandQueue()
return nil
}
func (g *Graphics) flushRenderCommandEncoderIfNeeded() {
if g.rce == (mtl.RenderCommandEncoder{}) {
return
}
g.rce.EndEncoding()
g.rce = mtl.RenderCommandEncoder{}
g.lastDst = nil
}
func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs [graphics.ShaderImageCount]*Image, indexOffset int, shader *Shader, uniforms [][]uint32, blend graphicsdriver.Blend, fillRule graphicsdriver.FillRule) error {
// When preparing a stencil buffer, flush the current render command encoder
// to make sure the stencil buffer is cleared when loading.
// TODO: What about clearing the stencil buffer by vertices?
if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillAll {
g.flushRenderCommandEncoderIfNeeded()
}
g.lastDst = dst
g.lastFillRule = fillRule
if g.rce == (mtl.RenderCommandEncoder{}) {
rpd := mtl.RenderPassDescriptor{}
// Even though the destination pixels are not used, mtl.LoadActionDontCare might cause glitches
// (#1019). Always using mtl.LoadActionLoad is safe.
if dst.screen {
rpd.ColorAttachments[0].LoadAction = mtl.LoadActionClear
} else {
rpd.ColorAttachments[0].LoadAction = mtl.LoadActionLoad
}
// The store action should always be 'store' even for the screen (#1700).
rpd.ColorAttachments[0].StoreAction = mtl.StoreActionStore
t := dst.mtlTexture()
if t == (mtl.Texture{}) {
return nil
}
rpd.ColorAttachments[0].Texture = t
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{}
if fillRule != graphicsdriver.FillAll {
dst.ensureStencil()
rpd.StencilAttachment.LoadAction = mtl.LoadActionClear
rpd.StencilAttachment.StoreAction = mtl.StoreActionDontCare
rpd.StencilAttachment.Texture = dst.stencil
}
if g.cb == (mtl.CommandBuffer{}) {
g.cb = g.cq.MakeCommandBuffer()
}
g.rce = g.cb.MakeRenderCommandEncoder(rpd)
}
w, h := dst.internalSize()
g.rce.SetViewport(mtl.Viewport{
OriginX: 0,
OriginY: 0,
Width: float64(w),
Height: float64(h),
ZNear: -1,
ZFar: 1,
})
g.rce.SetVertexBuffer(g.vb, 0, 0)
for i, u := range uniforms {
if u == nil {
continue
}
g.rce.SetVertexBytes(unsafe.Pointer(&u[0]), unsafe.Sizeof(u[0])*uintptr(len(u)), i+1)
g.rce.SetFragmentBytes(unsafe.Pointer(&u[0]), unsafe.Sizeof(u[0])*uintptr(len(u)), i+1)
}
for i, src := range srcs {
if src != nil {
g.rce.SetFragmentTexture(src.texture, i)
} else {
g.rce.SetFragmentTexture(mtl.Texture{}, i)
}
}
var (
noStencilRpss mtl.RenderPipelineState
incrementStencilRpss mtl.RenderPipelineState
invertStencilRpss mtl.RenderPipelineState
drawWithStencilRpss mtl.RenderPipelineState
)
switch fillRule {
case graphicsdriver.FillAll:
s, err := shader.RenderPipelineState(&g.view, blend, noStencil, dst.screen)
if err != nil {
return err
}
noStencilRpss = s
case graphicsdriver.NonZero:
s, err := shader.RenderPipelineState(&g.view, blend, incrementStencil, dst.screen)
if err != nil {
return err
}
incrementStencilRpss = s
case graphicsdriver.EvenOdd:
s, err := shader.RenderPipelineState(&g.view, blend, invertStencil, dst.screen)
if err != nil {
return err
}
invertStencilRpss = s
}
if fillRule != graphicsdriver.FillAll {
s, err := shader.RenderPipelineState(&g.view, blend, drawWithStencil, dst.screen)
if err != nil {
return err
}
drawWithStencilRpss = s
}
for _, dstRegion := range dstRegions {
g.rce.SetScissorRect(mtl.ScissorRect{
X: dstRegion.Region.Min.X,
Y: dstRegion.Region.Min.Y,
Width: dstRegion.Region.Dx(),
Height: dstRegion.Region.Dy(),
})
switch fillRule {
case graphicsdriver.FillAll:
g.rce.SetDepthStencilState(g.dsss[noStencil])
g.rce.SetRenderPipelineState(noStencilRpss)
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
case graphicsdriver.NonZero:
g.rce.SetDepthStencilState(g.dsss[incrementStencil])
g.rce.SetRenderPipelineState(incrementStencilRpss)
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
case graphicsdriver.EvenOdd:
g.rce.SetDepthStencilState(g.dsss[invertStencil])
g.rce.SetRenderPipelineState(invertStencilRpss)
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
}
if fillRule != graphicsdriver.FillAll {
g.rce.SetDepthStencilState(g.dsss[drawWithStencil])
g.rce.SetRenderPipelineState(drawWithStencilRpss)
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
}
indexOffset += dstRegion.IndexCount
}
return nil
}
func (g *Graphics) 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 {
if shaderID == graphicsdriver.InvalidShaderID {
return fmt.Errorf("metal: shader ID is invalid")
}
dst := g.images[dstID]
if dst.screen {
g.view.update()
}
var srcs [graphics.ShaderImageCount]*Image
for i, srcID := range srcIDs {
srcs[i] = g.images[srcID]
}
uniformVars := make([][]uint32, len(g.shaders[shaderID].ir.Uniforms))
// Set the additional uniform variables.
var idx int
for i, t := range g.shaders[shaderID].ir.Uniforms {
if i == graphics.ProjectionMatrixUniformVariableIndex {
// In Metal, 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.
uniforms[idx+1] ^= 1 << 31
uniforms[idx+5] ^= 1 << 31
uniforms[idx+9] ^= 1 << 31
uniforms[idx+13] ^= 1 << 31
}
n := t.Uint32Count()
switch t.Main {
case shaderir.Vec3, shaderir.IVec3:
// float3 requires 16-byte alignment (#2463).
v1 := make([]uint32, 4)
copy(v1[0:3], uniforms[idx:idx+3])
uniformVars[i] = v1
case shaderir.Mat3:
// float3x3 requires 16-byte alignment (#2036).
v1 := make([]uint32, 12)
copy(v1[0:3], uniforms[idx:idx+3])
copy(v1[4:7], uniforms[idx+3:idx+6])
copy(v1[8:11], uniforms[idx+6:idx+9])
uniformVars[i] = v1
case shaderir.Array:
switch t.Sub[0].Main {
case shaderir.Vec3, shaderir.IVec3:
v1 := make([]uint32, t.Length*4)
for j := 0; j < t.Length; j++ {
offset0 := j * 3
offset1 := j * 4
copy(v1[offset1:offset1+3], uniforms[idx+offset0:idx+offset0+3])
}
uniformVars[i] = v1
case shaderir.Mat3:
v1 := make([]uint32, t.Length*12)
for j := 0; j < t.Length; j++ {
offset0 := j * 9
offset1 := j * 12
copy(v1[offset1:offset1+3], uniforms[idx+offset0:idx+offset0+3])
copy(v1[offset1+4:offset1+7], uniforms[idx+offset0+3:idx+offset0+6])
copy(v1[offset1+8:offset1+11], uniforms[idx+offset0+6:idx+offset0+9])
}
uniformVars[i] = v1
default:
uniformVars[i] = uniforms[idx : idx+n]
}
default:
uniformVars[i] = uniforms[idx : idx+n]
}
idx += n
}
if err := g.draw(dst, dstRegions, srcs, indexOffset, g.shaders[shaderID], uniformVars, blend, fillRule); err != nil {
return err
}
return nil
}
func (g *Graphics) SetVsyncEnabled(enabled bool) {
g.view.setDisplaySyncEnabled(enabled)
}
func (g *Graphics) NeedsClearingScreen() bool {
return false
}
func (g *Graphics) MaxImageSize() int {
if g.maxImageSize != 0 {
return g.maxImageSize
}
d := g.view.getMTLDevice()
// supportsFamily is available as of macOS 10.15+ and iOS 13.0+.
// https://developer.apple.com/documentation/metal/mtldevice/3143473-supportsfamily
if d.RespondsToSelector(objc.RegisterName("supportsFamily:")) {
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
g.maxImageSize = 8192
switch {
case d.SupportsFamily(mtl.GPUFamilyApple3):
g.maxImageSize = 16384
case d.SupportsFamily(mtl.GPUFamilyMac2):
g.maxImageSize = 16384
}
return g.maxImageSize
}
// supportsFeatureSet is deprecated but some old macOS/iOS versions support only this (#2553).
switch {
case d.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily5_v1):
g.maxImageSize = 16384
case d.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily4_v1):
g.maxImageSize = 16384
case d.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily3_v1):
g.maxImageSize = 16384
case d.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v2):
g.maxImageSize = 8192
case d.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v1):
g.maxImageSize = 4096
case d.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v2):
g.maxImageSize = 8192
case d.SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v1):
g.maxImageSize = 4096
case d.SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily2_v1):
g.maxImageSize = 16384
case d.SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily1_v1):
g.maxImageSize = 8192
case d.SupportsFeatureSet(mtl.FeatureSet_macOS_GPUFamily1_v1):
g.maxImageSize = 16384
default:
panic("metal: there is no supported feature set")
}
return g.maxImageSize
}
func (g *Graphics) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) {
s, err := newShader(g.view.getMTLDevice(), g.genNextShaderID(), program)
if err != nil {
return nil, err
}
g.addShader(s)
return s, nil
}
func (g *Graphics) addShader(shader *Shader) {
if g.shaders == nil {
g.shaders = map[graphicsdriver.ShaderID]*Shader{}
}
if _, ok := g.shaders[shader.id]; ok {
panic(fmt.Sprintf("metal: shader ID %d was already registered", shader.id))
}
g.shaders[shader.id] = shader
}
func (g *Graphics) removeShader(shader *Shader) {
delete(g.shaders, shader.id)
}
type Image struct {
id graphicsdriver.ImageID
graphics *Graphics
width int
height int
screen bool
texture mtl.Texture
stencil mtl.Texture
}
func (i *Image) ID() graphicsdriver.ImageID {
return i.id
}
func (i *Image) internalSize() (int, int) {
if i.screen {
return i.width, i.height
}
return graphics.InternalImageSize(i.width), graphics.InternalImageSize(i.height)
}
func (i *Image) Dispose() {
if i.stencil != (mtl.Texture{}) {
i.stencil.Release()
i.stencil = mtl.Texture{}
}
if i.texture != (mtl.Texture{}) {
i.texture.Release()
i.texture = mtl.Texture{}
}
i.graphics.removeImage(i)
}
func (i *Image) syncTexture() {
i.graphics.flushRenderCommandEncoderIfNeeded()
// Calling SynchronizeTexture is ignored on iOS (see mtl.m), but it looks like committing BlitCommandEncoder
// is necessary (#1337).
if i.graphics.cb != (mtl.CommandBuffer{}) {
panic("metal: command buffer must be empty at syncTexture: flushIfNeeded is not called yet?")
}
cb := i.graphics.cq.MakeCommandBuffer()
bce := cb.MakeBlitCommandEncoder()
bce.SynchronizeTexture(i.texture, 0, 0)
bce.EndEncoding()
cb.Commit()
// TODO: Are fences available here?
cb.WaitUntilCompleted()
}
func (i *Image) ReadPixels(args []graphicsdriver.PixelsArgs) error {
i.graphics.flushIfNeeded(false)
i.syncTexture()
for _, arg := range args {
if got, want := len(arg.Pixels), 4*arg.Region.Dx()*arg.Region.Dy(); got != want {
return fmt.Errorf("metal: len(buf) must be %d but %d at ReadPixels", want, got)
}
i.texture.GetBytes(&arg.Pixels[0], uintptr(4*arg.Region.Dx()), mtl.Region{
Origin: mtl.Origin{X: arg.Region.Min.X, Y: arg.Region.Min.Y},
Size: mtl.Size{Width: arg.Region.Dx(), Height: arg.Region.Dy(), Depth: 1},
}, 0)
}
return nil
}
func (i *Image) WritePixels(args []graphicsdriver.PixelsArgs) error {
g := i.graphics
g.flushRenderCommandEncoderIfNeeded()
// Calculate the smallest texture size to include all the values in args.
var region image.Rectangle
for _, a := range args {
region = region.Union(a.Region)
}
// Use a temporary texture to send pixels asynchronously, whichever the memory is shared (e.g., iOS) or
// managed (e.g., macOS). A temporary texture is needed since ReplaceRegion tries to sync the pixel
// data between CPU and GPU, and doing it on the existing texture is inefficient (#1418).
// The texture cannot be reused until sending the pixels finishes, then create new ones for each call.
td := mtl.TextureDescriptor{
TextureType: mtl.TextureType2D,
PixelFormat: mtl.PixelFormatRGBA8UNorm,
Width: region.Dx(),
Height: region.Dy(),
StorageMode: storageMode,
Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget,
}
t := g.view.getMTLDevice().MakeTexture(td)
g.tmpTextures = append(g.tmpTextures, t)
for _, a := range args {
t.ReplaceRegion(mtl.Region{
Origin: mtl.Origin{X: a.Region.Min.X - region.Min.X, Y: a.Region.Min.Y - region.Min.Y, Z: 0},
Size: mtl.Size{Width: a.Region.Dx(), Height: a.Region.Dy(), Depth: 1},
}, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Region.Dx())
}
if g.cb == (mtl.CommandBuffer{}) {
g.cb = i.graphics.cq.MakeCommandBuffer()
}
bce := g.cb.MakeBlitCommandEncoder()
for _, a := range args {
so := mtl.Origin{X: a.Region.Min.X - region.Min.X, Y: a.Region.Min.Y - region.Min.Y, Z: 0}
ss := mtl.Size{Width: a.Region.Dx(), Height: a.Region.Dy(), Depth: 1}
do := mtl.Origin{X: a.Region.Min.X, Y: a.Region.Min.Y, Z: 0}
bce.CopyFromTexture(t, 0, 0, so, ss, i.texture, 0, 0, do)
}
bce.EndEncoding()
return nil
}
func (i *Image) mtlTexture() mtl.Texture {
if i.screen {
g := i.graphics
if g.screenDrawable == (ca.MetalDrawable{}) {
drawable := g.view.nextDrawable()
if drawable == (ca.MetalDrawable{}) {
return mtl.Texture{}
}
g.screenDrawable = drawable
// After nextDrawable, it is expected some command buffers are completed.
g.gcBuffers()
}
return g.screenDrawable.Texture()
}
return i.texture
}
func (i *Image) ensureStencil() {
if i.stencil != (mtl.Texture{}) {
return
}
td := mtl.TextureDescriptor{
TextureType: mtl.TextureType2D,
PixelFormat: mtl.PixelFormatStencil8,
Width: graphics.InternalImageSize(i.width),
Height: graphics.InternalImageSize(i.height),
StorageMode: mtl.StorageModePrivate,
Usage: mtl.TextureUsageRenderTarget,
}
i.stencil = i.graphics.view.getMTLDevice().MakeTexture(td)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,130 @@
// Copyright 2020 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 metal
import (
"fmt"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl"
)
type shaderRpsKey struct {
blend graphicsdriver.Blend
stencilMode stencilMode
screen bool
}
type Shader struct {
id graphicsdriver.ShaderID
ir *shaderir.Program
fs mtl.Function
vs mtl.Function
rpss map[shaderRpsKey]mtl.RenderPipelineState
}
func newShader(device mtl.Device, id graphicsdriver.ShaderID, program *shaderir.Program) (*Shader, error) {
s := &Shader{
id: id,
ir: program,
rpss: map[shaderRpsKey]mtl.RenderPipelineState{},
}
if err := s.init(device); err != nil {
return nil, err
}
return s, nil
}
func (s *Shader) ID() graphicsdriver.ShaderID {
return s.id
}
func (s *Shader) Dispose() {
for _, rps := range s.rpss {
rps.Release()
}
s.vs.Release()
s.fs.Release()
}
func (s *Shader) init(device mtl.Device) error {
src := msl.Compile(s.ir)
lib, err := device.MakeLibrary(src, mtl.CompileOptions{})
if err != nil {
return fmt.Errorf("metal: device.MakeLibrary failed: %w, source: %s", err, src)
}
vs, err := lib.MakeFunction(msl.VertexName)
if err != nil {
return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w, source: %s", err, src)
}
fs, err := lib.MakeFunction(msl.FragmentName)
if err != nil {
return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w, source: %s", err, src)
}
s.fs = fs
s.vs = vs
return nil
}
func (s *Shader) RenderPipelineState(view *view, blend graphicsdriver.Blend, stencilMode stencilMode, screen bool) (mtl.RenderPipelineState, error) {
key := shaderRpsKey{
blend: blend,
stencilMode: stencilMode,
screen: screen,
}
if rps, ok := s.rpss[key]; ok {
return rps, nil
}
rpld := mtl.RenderPipelineDescriptor{
VertexFunction: s.vs,
FragmentFunction: s.fs,
}
if stencilMode != noStencil {
rpld.StencilAttachmentPixelFormat = mtl.PixelFormatStencil8
}
// TODO: For the precise pixel format, whether the render target is the screen or not must be considered.
pix := mtl.PixelFormatRGBA8UNorm
if screen {
pix = view.colorPixelFormat()
}
rpld.ColorAttachments[0].PixelFormat = pix
rpld.ColorAttachments[0].BlendingEnabled = true
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = blendFactorToMetalBlendFactor(blend.BlendFactorDestinationAlpha)
rpld.ColorAttachments[0].DestinationRGBBlendFactor = blendFactorToMetalBlendFactor(blend.BlendFactorDestinationRGB)
rpld.ColorAttachments[0].SourceAlphaBlendFactor = blendFactorToMetalBlendFactor(blend.BlendFactorSourceAlpha)
rpld.ColorAttachments[0].SourceRGBBlendFactor = blendFactorToMetalBlendFactor(blend.BlendFactorSourceRGB)
rpld.ColorAttachments[0].AlphaBlendOperation = blendOperationToMetalBlendOperation(blend.BlendOperationAlpha)
rpld.ColorAttachments[0].RGBBlendOperation = blendOperationToMetalBlendOperation(blend.BlendOperationRGB)
if stencilMode == noStencil || stencilMode == drawWithStencil {
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll
} else {
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone
}
rps, err := view.getMTLDevice().MakeRenderPipelineState(rpld)
if err != nil {
return mtl.RenderPipelineState{}, err
}
s.rpss[key] = rps
return rps, nil
}
@@ -0,0 +1,98 @@
// Copyright 2019 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 metal
import (
"sync"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ca"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
)
type view struct {
window uintptr
uiview uintptr
windowChanged bool
vsyncDisabled bool
device mtl.Device
ml ca.MetalLayer
once sync.Once
}
func (v *view) setDrawableSize(width, height int) {
v.ml.SetDrawableSize(width, height)
}
func (v *view) getMTLDevice() mtl.Device {
return v.device
}
func (v *view) setDisplaySyncEnabled(enabled bool) {
if !v.vsyncDisabled == enabled {
return
}
v.forceSetDisplaySyncEnabled(enabled)
}
func (v *view) forceSetDisplaySyncEnabled(enabled bool) {
v.ml.SetDisplaySyncEnabled(enabled)
v.vsyncDisabled = !enabled
}
func (v *view) colorPixelFormat() mtl.PixelFormat {
return v.ml.PixelFormat()
}
func (v *view) initialize(device mtl.Device) error {
v.device = device
ml, err := ca.MakeMetalLayer()
if err != nil {
return err
}
v.ml = ml
v.ml.SetDevice(v.device)
// https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat
//
// The pixel format for a Metal layer must be MTLPixelFormatBGRA8Unorm,
// MTLPixelFormatBGRA8Unorm_sRGB, MTLPixelFormatRGBA16Float, MTLPixelFormatBGRA10_XR, or
// MTLPixelFormatBGRA10_XR_sRGB.
v.ml.SetPixelFormat(mtl.PixelFormatBGRA8UNorm)
// The vsync state might be reset. Set the state again (#1364).
v.forceSetDisplaySyncEnabled(!v.vsyncDisabled)
v.ml.SetFramebufferOnly(true)
// presentsWithTransaction doesn't work in the fullscreen mode (#1745, #1974).
// presentsWithTransaction doesn't work with vsync off (#1196).
// nextDrawable took more than one second if the window has other controls like NSTextView (#1029).
v.ml.SetPresentsWithTransaction(false)
v.ml.SetMaximumDrawableCount(v.maximumDrawableCount())
return nil
}
func (v *view) nextDrawable() ca.MetalDrawable {
d, err := v.ml.NextDrawable()
if err != nil {
// Drawable is nil. This can happen at the initial state. Let's wait and see.
return ca.MetalDrawable{}
}
return d
}
@@ -0,0 +1,71 @@
// Copyright 2019 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 metal
// Suppress the warnings about availability guard with -Wno-unguarded-availability-new.
// It is because old Xcode (8 or older?) does not accept @available syntax.
// #cgo CFLAGS: -Wno-unguarded-availability-new -x objective-c
// #cgo LDFLAGS: -framework UIKit -framework QuartzCore -framework Foundation -framework CoreGraphics
//
// #import <UIKit/UIKit.h>
//
// static void addSublayer(void* view, void* sublayer) {
// CALayer* layer = ((UIView*)view).layer;
// [layer addSublayer:(CALayer*)sublayer];
// }
//
// static void setFrame(void* cametal, void* uiview) {
// __block CGSize size;
// dispatch_sync(dispatch_get_main_queue(), ^{
// size = ((UIView*)uiview).frame.size;
// });
// ((CALayer*)cametal).frame = CGRectMake(0, 0, size.width, size.height);
// }
import "C"
import (
"unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
)
func (v *view) setWindow(window uintptr) {
panic("metal: setWindow is not available on iOS")
}
func (v *view) setUIView(uiview uintptr) {
v.uiview = uiview
}
func (v *view) update() {
v.once.Do(func() {
if v.ml.Layer() == nil {
panic("metal: CAMetalLayer is not initialized yet")
}
C.addSublayer(unsafe.Pointer(v.uiview), v.ml.Layer())
})
C.setFrame(v.ml.Layer(), unsafe.Pointer(v.uiview))
}
const (
storageMode = mtl.StorageModeShared
resourceStorageMode = mtl.ResourceStorageModeShared
)
func (v *view) maximumDrawableCount() int {
// TODO: Is 2 available for iOS?
return 3
}
@@ -0,0 +1,79 @@
// Copyright 2019 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 darwin && !ios
package metal
import (
"runtime"
"github.com/ebitengine/purego/objc"
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
)
func (v *view) setWindow(window uintptr) {
// NSView can be updated e.g., fullscreen-state is switched.
v.window = window
v.windowChanged = true
}
func (v *view) setUIView(uiview uintptr) {
panic("metal: setUIView is not available on macOS")
}
func (v *view) update() {
v.ml.SetMaximumDrawableCount(v.maximumDrawableCount())
if !v.windowChanged {
return
}
// TODO: Should this be called on the main thread?
cocoaWindow := cocoa.NSWindow{ID: objc.ID(v.window)}
cocoaWindow.ContentView().SetLayer(uintptr(v.ml.Layer()))
cocoaWindow.ContentView().SetWantsLayer(true)
v.windowChanged = false
}
const (
storageMode = mtl.StorageModeManaged
resourceStorageMode = mtl.ResourceStorageModeManaged
)
func (v *view) maximumDrawableCount() int {
// Note that the architecture might not be the true reason of the issues (#2880, #2883).
// Hajime tested only MacBook Pro 2020 (Intel) and MacBook Pro 2023 (M3).
// Use 3 for Intel Mac and iOS. With 2, There are some situations that the FPS becomes half, or the FPS becomes too low (#2880).
if runtime.GOARCH == "amd64" {
return 3
}
// Use 3 in fullscren.
// Though this might degrade FPS, this is necessary to avoid mysterious rendering delays.
if v.isFullscreen() {
return 3
}
// Use 2 for a Wnidow to avoid mysterious blinking (#2883).
return 2
}
func (v *view) isFullscreen() bool {
return cocoa.NSWindow{ID: objc.ID(v.window)}.StyleMask()&cocoa.NSWindowStyleMaskFullScreen != 0
}
@@ -0,0 +1,498 @@
// Copyright 2016 Hajime Hoshi
//
// 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 !playstation5
package opengl
import (
"errors"
"fmt"
"image"
"sync"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/glsl"
)
type blendFactor int
type blendOperation int
func convertBlendFactor(f graphicsdriver.BlendFactor) blendFactor {
switch f {
case graphicsdriver.BlendFactorZero:
return gl.ZERO
case graphicsdriver.BlendFactorOne:
return gl.ONE
case graphicsdriver.BlendFactorSourceColor:
return gl.SRC_COLOR
case graphicsdriver.BlendFactorOneMinusSourceColor:
return gl.ONE_MINUS_SRC_COLOR
case graphicsdriver.BlendFactorSourceAlpha:
return gl.SRC_ALPHA
case graphicsdriver.BlendFactorOneMinusSourceAlpha:
return gl.ONE_MINUS_SRC_ALPHA
case graphicsdriver.BlendFactorDestinationColor:
return gl.DST_COLOR
case graphicsdriver.BlendFactorOneMinusDestinationColor:
return gl.ONE_MINUS_DST_COLOR
case graphicsdriver.BlendFactorDestinationAlpha:
return gl.DST_ALPHA
case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return gl.ONE_MINUS_DST_ALPHA
case graphicsdriver.BlendFactorSourceAlphaSaturated:
return gl.SRC_ALPHA_SATURATE
default:
panic(fmt.Sprintf("opengl: invalid blend factor %d", f))
}
}
func convertBlendOperation(o graphicsdriver.BlendOperation) blendOperation {
switch o {
case graphicsdriver.BlendOperationAdd:
return gl.FUNC_ADD
case graphicsdriver.BlendOperationSubtract:
return gl.FUNC_SUBTRACT
case graphicsdriver.BlendOperationReverseSubtract:
return gl.FUNC_REVERSE_SUBTRACT
case graphicsdriver.BlendOperationMin:
return gl.MIN
case graphicsdriver.BlendOperationMax:
return gl.MAX
default:
panic(fmt.Sprintf("opengl: invalid blend operation %d", o))
}
}
type (
textureNative uint32
renderbufferNative uint32
framebufferNative uint32
shader uint32
program uint32
buffer uint32
)
type (
uniformLocation int32
attribLocation int32
)
const (
invalidFramebuffer = (1 << 32) - 1
invalidUniform = -1
)
type context struct {
ctx gl.Context
locationCache *locationCache
screenFramebuffer framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
lastFramebuffer framebufferNative
lastTexture textureNative
lastRenderbuffer renderbufferNative
lastViewportWidth int
lastViewportHeight int
lastBlend graphicsdriver.Blend
maxTextureSize int
maxTextureSizeOnce sync.Once
highp bool
highpOnce sync.Once
initOnce sync.Once
}
func (c *context) bindTexture(t textureNative) {
if c.lastTexture == t {
return
}
c.ctx.BindTexture(gl.TEXTURE_2D, uint32(t))
c.lastTexture = t
}
func (c *context) bindRenderbuffer(r renderbufferNative) {
if c.lastRenderbuffer == r {
return
}
c.ctx.BindRenderbuffer(gl.RENDERBUFFER, uint32(r))
c.lastRenderbuffer = r
}
func (c *context) bindFramebuffer(f framebufferNative) {
if c.lastFramebuffer == f {
return
}
c.ctx.BindFramebuffer(gl.FRAMEBUFFER, uint32(f))
c.lastFramebuffer = f
}
func (c *context) setViewport(f *framebuffer) {
c.bindFramebuffer(f.native)
if c.lastViewportWidth == f.width && c.lastViewportHeight == f.height {
return
}
// On some environments, viewport size must be within the framebuffer size.
// e.g. Edge (#71), Chrome on GPD Pocket (#420), macOS Mojave (#691).
// Use the same size of the framebuffer here.
c.ctx.Viewport(0, 0, int32(f.width), int32(f.height))
// glViewport must be called at least at every frame on iOS.
// As the screen framebuffer is the last render target, next SetViewport should be
// the first call at a frame.
if f.native == c.screenFramebuffer {
c.lastViewportWidth = 0
c.lastViewportHeight = 0
} else {
c.lastViewportWidth = f.width
c.lastViewportHeight = f.height
}
}
func (c *context) newScreenFramebuffer(width, height int) *framebuffer {
return &framebuffer{
native: c.screenFramebuffer,
width: width,
height: height,
}
}
func (c *context) getMaxTextureSize() int {
c.maxTextureSizeOnce.Do(func() {
c.maxTextureSize = c.ctx.GetInteger(gl.MAX_TEXTURE_SIZE)
})
return c.maxTextureSize
}
func (c *context) reset() error {
var err1 error
c.initOnce.Do(func() {
// Load OpenGL functions after WGL is initialized especially for Windows (#2452).
if err := c.ctx.LoadFunctions(); err != nil {
err1 = err
return
}
})
if err1 != nil {
return err1
}
c.locationCache = newLocationCache()
c.lastTexture = 0
c.lastFramebuffer = invalidFramebuffer
c.lastViewportWidth = 0
c.lastViewportHeight = 0
c.lastBlend = graphicsdriver.Blend{}
c.ctx.Enable(gl.BLEND)
c.ctx.Enable(gl.SCISSOR_TEST)
c.blend(graphicsdriver.BlendSourceOver)
c.screenFramebuffer = framebufferNative(c.ctx.GetInteger(gl.FRAMEBUFFER_BINDING))
// TODO: Need to update screenFramebufferWidth/Height?
return nil
}
func (c *context) blend(blend graphicsdriver.Blend) {
if c.lastBlend == blend {
return
}
c.lastBlend = blend
c.ctx.BlendFuncSeparate(
uint32(convertBlendFactor(blend.BlendFactorSourceRGB)),
uint32(convertBlendFactor(blend.BlendFactorDestinationRGB)),
uint32(convertBlendFactor(blend.BlendFactorSourceAlpha)),
uint32(convertBlendFactor(blend.BlendFactorDestinationAlpha)),
)
c.ctx.BlendEquationSeparate(
uint32(convertBlendOperation(blend.BlendOperationRGB)),
uint32(convertBlendOperation(blend.BlendOperationAlpha)),
)
}
func (c *context) newTexture(width, height int) (textureNative, error) {
t := c.ctx.CreateTexture()
if t <= 0 {
return 0, errors.New("opengl: creating texture failed")
}
c.bindTexture(textureNative(t))
c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
c.ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
c.ctx.PixelStorei(gl.UNPACK_ALIGNMENT, 4)
// Firefox warns the usage of textures without specifying pixels (#629, #2077)
//
// Error: WebGL warning: drawElements: This operation requires zeroing texture data. This is slow.
//
// In Ebitengine, textures are filled with pixels later by the filter that ignores destination, so it is fine
// to leave textures as uninitialized here. Rather, extra memory allocating for initialization should be
// avoided.
//
// See also https://stackoverflow.com/questions/57734645.
c.ctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, nil)
return textureNative(t), nil
}
func (c *context) framebufferPixels(buf []byte, f *framebuffer, region image.Rectangle) error {
if got, want := len(buf), 4*region.Dx()*region.Dy(); got != want {
return fmt.Errorf("opengl: len(buf) must be %d but was %d at framebufferPixels", got, want)
}
c.ctx.Flush()
c.bindFramebuffer(f.native)
x := int32(region.Min.X)
y := int32(region.Min.Y)
width := int32(region.Dx())
height := int32(region.Dy())
c.ctx.ReadPixels(buf, x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE)
return nil
}
func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) {
c.ctx.Flush()
c.bindFramebuffer(f.native)
c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, uint32(buffer))
c.ctx.ReadPixels(nil, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE)
c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, 0)
}
func (c *context) deleteTexture(t textureNative) {
if c.lastTexture == t {
c.lastTexture = 0
}
c.ctx.DeleteTexture(uint32(t))
}
func (c *context) newRenderbuffer(width, height int) (renderbufferNative, error) {
r := c.ctx.CreateRenderbuffer()
if r <= 0 {
return 0, errors.New("opengl: creating renderbuffer failed")
}
renderbuffer := renderbufferNative(r)
c.bindRenderbuffer(renderbuffer)
var stencilFormat uint32
if c.ctx.IsES() {
// https://docs.gl/es2/glRenderbufferStorage
// > Must be one of the following symbolic constants: GL_RGBA4, GL_RGB565, GL_RGB5_A1,
// > GL_DEPTH_COMPONENT16, or GL_STENCIL_INDEX8.
//
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/renderbufferStorage
// > A GLenum specifying the internal format of the renderbuffer. Possible values:
// > * gl.RGBA4: 4 red bits, 4 green bits, 4 blue bits 4 alpha bits.
// > * gl.RGB565: 5 red bits, 6 green bits, 5 blue bits.
// > * gl.RGB5_A1: 5 red bits, 5 green bits, 5 blue bits, 1 alpha bit.
// > * gl.DEPTH_COMPONENT16: 16 depth bits.
// > * gl.STENCIL_INDEX8: 8 stencil bits.
// > * gl.DEPTH_STENCIL
stencilFormat = gl.STENCIL_INDEX8
} else {
// GL_STENCIL_INDEX8 might not be available with OpenGL 2.1.
// https://www.khronos.org/opengl/wiki/Image_Format
// > There are only 2 depth/stencil formats, each providing 8 stencil bits: GL_DEPTH24_STENCIL8 and GL_DEPTH32F_STENCIL8.
// > [...]
// > Stencil formats can only be used for Textures if OpenGL 4.4 or ARB_texture_stencil8 is available.
stencilFormat = gl.DEPTH24_STENCIL8
}
c.ctx.RenderbufferStorage(gl.RENDERBUFFER, stencilFormat, int32(width), int32(height))
return renderbuffer, nil
}
func (c *context) deleteRenderbuffer(r renderbufferNative) {
if !c.ctx.IsRenderbuffer(uint32(r)) {
return
}
if c.lastRenderbuffer == r {
c.lastRenderbuffer = 0
}
c.ctx.DeleteRenderbuffer(uint32(r))
}
func (c *context) newFramebuffer(texture textureNative, width, height int) (*framebuffer, error) {
f := c.ctx.CreateFramebuffer()
if f <= 0 {
return nil, fmt.Errorf("opengl: creating framebuffer failed: the returned value is not positive but %d", f)
}
c.bindFramebuffer(framebufferNative(f))
c.ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(texture), 0)
if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
if s != 0 {
return nil, fmt.Errorf("opengl: creating framebuffer failed: %v", s)
}
if e := c.ctx.GetError(); e != gl.NO_ERROR {
return nil, fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e)
}
return nil, fmt.Errorf("opengl: creating framebuffer failed: unknown error")
}
return &framebuffer{
native: framebufferNative(f),
width: width,
height: height,
}, nil
}
func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) error {
c.bindFramebuffer(f)
c.ctx.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, uint32(r))
if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
return errors.New(fmt.Sprintf("opengl: glFramebufferRenderbuffer failed: %d", s))
}
return nil
}
func (c *context) deleteFramebuffer(f framebufferNative) {
if f == c.screenFramebuffer {
return
}
if !c.ctx.IsFramebuffer(uint32(f)) {
return
}
// If a framebuffer to be deleted is bound, a newly bound framebuffer
// will be a default framebuffer.
// https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffers.xml
if c.lastFramebuffer == f {
c.lastFramebuffer = invalidFramebuffer
c.lastViewportWidth = 0
c.lastViewportHeight = 0
}
c.ctx.DeleteFramebuffer(uint32(f))
}
func (c *context) newShader(shaderType uint32, source string) (shader, error) {
s := c.ctx.CreateShader(shaderType)
if s == 0 {
return 0, fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType)
}
c.ctx.ShaderSource(s, source)
c.ctx.CompileShader(s)
if c.ctx.GetShaderi(s, gl.COMPILE_STATUS) == gl.FALSE {
log := c.ctx.GetShaderInfoLog(s)
return 0, fmt.Errorf("opengl: shader compile failed: %s", log)
}
return shader(s), nil
}
func (c *context) newProgram(shaders []shader, attributes []string) (program, error) {
p := c.ctx.CreateProgram()
if p == 0 {
return 0, errors.New("opengl: glCreateProgram failed")
}
for _, shader := range shaders {
c.ctx.AttachShader(p, uint32(shader))
}
for i, name := range attributes {
c.ctx.BindAttribLocation(p, uint32(i), name)
}
c.ctx.LinkProgram(p)
if c.ctx.GetProgrami(p, gl.LINK_STATUS) == gl.FALSE {
info := c.ctx.GetProgramInfoLog(p)
return 0, fmt.Errorf("opengl: program error: %s", info)
}
return program(p), nil
}
func (c *context) deleteProgram(p program) {
c.locationCache.deleteProgram(p)
if !c.ctx.IsProgram(uint32(p)) {
return
}
c.ctx.DeleteProgram(uint32(p))
}
func (c *context) uniformInt(p program, location string, v int) bool {
l := c.locationCache.GetUniformLocation(c, p, location)
if l == invalidUniform {
return false
}
c.ctx.Uniform1i(int32(l), int32(v))
return true
}
func (c *context) uniforms(p program, location string, v []uint32, typ shaderir.Type) bool {
l := c.locationCache.GetUniformLocation(c, p, location)
if l == invalidUniform {
return false
}
base := typ.Main
if base == shaderir.Array {
base = typ.Sub[0].Main
}
switch base {
case shaderir.Float:
c.ctx.Uniform1fv(int32(l), uint32sToFloat32s(v))
case shaderir.Int:
c.ctx.Uniform1iv(int32(l), uint32sToInt32s(v))
case shaderir.Vec2:
c.ctx.Uniform2fv(int32(l), uint32sToFloat32s(v))
case shaderir.Vec3:
c.ctx.Uniform3fv(int32(l), uint32sToFloat32s(v))
case shaderir.Vec4:
c.ctx.Uniform4fv(int32(l), uint32sToFloat32s(v))
case shaderir.IVec2:
c.ctx.Uniform2iv(int32(l), uint32sToInt32s(v))
case shaderir.IVec3:
c.ctx.Uniform3iv(int32(l), uint32sToInt32s(v))
case shaderir.IVec4:
c.ctx.Uniform4iv(int32(l), uint32sToInt32s(v))
case shaderir.Mat2:
c.ctx.UniformMatrix2fv(int32(l), uint32sToFloat32s(v))
case shaderir.Mat3:
c.ctx.UniformMatrix3fv(int32(l), uint32sToFloat32s(v))
case shaderir.Mat4:
c.ctx.UniformMatrix4fv(int32(l), uint32sToFloat32s(v))
default:
panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String()))
}
return true
}
func (c *context) newArrayBuffer(size int) buffer {
b := c.ctx.CreateBuffer()
c.ctx.BindBuffer(gl.ARRAY_BUFFER, b)
c.ctx.BufferInit(gl.ARRAY_BUFFER, size, gl.DYNAMIC_DRAW)
return buffer(b)
}
func (c *context) newElementArrayBuffer(size int) buffer {
b := c.ctx.CreateBuffer()
c.ctx.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, b)
c.ctx.BufferInit(gl.ELEMENT_ARRAY_BUFFER, size, gl.DYNAMIC_DRAW)
return buffer(b)
}
func (c *context) glslVersion() glsl.GLSLVersion {
if c.ctx.IsES() {
return glsl.GLSLVersionES300
}
return glsl.GLSLVersionDefault
}
@@ -0,0 +1,100 @@
// 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.
//go:build nintendosdk
package opengl
// #cgo !darwin LDFLAGS: -Wl,-unresolved-symbols=ignore-all
// #cgo darwin LDFLAGS: -Wl,-undefined,dynamic_lookup
//
// #include <EGL/egl.h>
// #include <EGL/eglext.h>
import "C"
import (
"fmt"
)
type egl struct {
display C.EGLDisplay
surface C.EGLSurface
context C.EGLContext
}
func newEGL(nativeWindowHandle uintptr) (*egl, error) {
e := &egl{}
e.display = C.eglGetDisplay(C.NativeDisplayType(C.EGL_DEFAULT_DISPLAY))
if e.display == 0 {
return nil, fmt.Errorf("opengl: eglGetDisplay failed")
}
if r := C.eglInitialize(e.display, nil, nil); r == 0 {
return nil, fmt.Errorf("opengl: eglInitialize failed")
}
configAttribs := []C.EGLint{
C.EGL_RENDERABLE_TYPE, C.EGL_OPENGL_BIT,
C.EGL_SURFACE_TYPE, C.EGL_WINDOW_BIT,
C.EGL_RED_SIZE, 8,
C.EGL_GREEN_SIZE, 8,
C.EGL_BLUE_SIZE, 8,
C.EGL_ALPHA_SIZE, 8,
C.EGL_NONE}
var numConfigs C.EGLint
var config C.EGLConfig
if r := C.eglChooseConfig(e.display, &configAttribs[0], &config, 1, &numConfigs); r == 0 {
return nil, fmt.Errorf("opengl: eglChooseConfig failed")
}
if numConfigs != 1 {
return nil, fmt.Errorf("opengl: eglChooseConfig failed: numConfigs must be 1 but %d", numConfigs)
}
e.surface = C.eglCreateWindowSurface(e.display, config, C.NativeWindowType(nativeWindowHandle), nil)
if e.surface == C.EGLSurface(C.EGL_NO_SURFACE) {
return nil, fmt.Errorf("opengl: eglCreateWindowSurface failed")
}
// Set the current rendering API.
if r := C.eglBindAPI(C.EGL_OPENGL_API); r == 0 {
return nil, fmt.Errorf("opengl: eglBindAPI failed")
}
// Create new context and set it as current.
contextAttribs := []C.EGLint{
// Set target graphics api version.
C.EGL_CONTEXT_MAJOR_VERSION, 3,
C.EGL_CONTEXT_MINOR_VERSION, 2,
// For debug callback
C.EGL_CONTEXT_FLAGS_KHR, C.EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
C.EGL_NONE}
e.context = C.eglCreateContext(e.display, config, C.EGLContext(C.EGL_NO_CONTEXT), &contextAttribs[0])
if e.context == C.EGLContext(C.EGL_NO_CONTEXT) {
return nil, fmt.Errorf("opengl: eglCreateContext failed: error: %d", C.eglGetError())
}
return e, nil
}
func (e *egl) makeContextCurrent() error {
if r := C.eglMakeCurrent(e.display, e.surface, e.surface, e.context); r == 0 {
return fmt.Errorf("opengl: eglMakeCurrent failed")
}
return nil
}
func (e *egl) swapBuffers() {
C.eglSwapBuffers(e.display, e.surface)
}
@@ -0,0 +1,5 @@
This is a fork of `github.com/go-gl/gl/v2.1/gl` with the below patch. This is now modified manually.
The original version is generated from `github.com/hajimehoshi/glow`'s `nocgo` branch. This enables to remove dependencies on Cgo on Windows.
Now we are working on committing this 'no-cgo' change to the official gl package. See https://github.com/go-gl/glow/pull/102.
@@ -0,0 +1,90 @@
// 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 !playstation5
package gl
const (
ALWAYS = 0x0207
ARRAY_BUFFER = 0x8892
BACK = 0x0405
BLEND = 0x0BE2
CLAMP_TO_EDGE = 0x812F
COLOR_ATTACHMENT0 = 0x8CE0
COMPILE_STATUS = 0x8B81
DECR_WRAP = 0x8508
DEPTH24_STENCIL8 = 0x88F0
DST_ALPHA = 0x0304
DST_COLOR = 0x0306
DYNAMIC_DRAW = 0x88E8
ELEMENT_ARRAY_BUFFER = 0x8893
FALSE = 0
FLOAT = 0x1406
FRAGMENT_SHADER = 0x8B30
FRAMEBUFFER = 0x8D40
FRAMEBUFFER_BINDING = 0x8CA6
FRAMEBUFFER_COMPLETE = 0x8CD5
FRONT = 0x0404
FRONT_AND_BACK = 0x0408
FUNC_ADD = 0x8006
FUNC_REVERSE_SUBTRACT = 0x800b
FUNC_SUBTRACT = 0x800a
HIGH_FLOAT = 0x8DF2
INCR_WRAP = 0x8507
INFO_LOG_LENGTH = 0x8B84
INVERT = 0x150A
KEEP = 0x1E00
LINK_STATUS = 0x8B82
MAX = 0x8008
MAX_TEXTURE_SIZE = 0x0D33
MIN = 0x8007
NEAREST = 0x2600
NO_ERROR = 0
NOTEQUAL = 0x0205
ONE = 1
ONE_MINUS_DST_ALPHA = 0x0305
ONE_MINUS_DST_COLOR = 0x0307
ONE_MINUS_SRC_ALPHA = 0x0303
ONE_MINUS_SRC_COLOR = 0x0301
PIXEL_PACK_BUFFER = 0x88EB
PIXEL_UNPACK_BUFFER = 0x88EC
READ_WRITE = 0x88BA
RENDERBUFFER = 0x8D41
RGBA = 0x1908
SCISSOR_TEST = 0x0C11
SHORT = 0x1402
SRC_ALPHA = 0x0302
SRC_ALPHA_SATURATE = 0x0308
SRC_COLOR = 0x0300
STENCIL_ATTACHMENT = 0x8D20
STENCIL_BUFFER_BIT = 0x0400
STENCIL_INDEX8 = 0x8D48
STENCIL_TEST = 0x0B90
STREAM_DRAW = 0x88E0
TEXTURE0 = 0x84C0
TEXTURE_2D = 0x0DE1
TEXTURE_MAG_FILTER = 0x2800
TEXTURE_MIN_FILTER = 0x2801
TEXTURE_WRAP_S = 0x2802
TEXTURE_WRAP_T = 0x2803
TRIANGLES = 0x0004
TRUE = 1
UNPACK_ALIGNMENT = 0x0CF5
UNSIGNED_BYTE = 0x1401
UNSIGNED_INT = 0x1405
VERTEX_SHADER = 0x8B31
WRITE_ONLY = 0x88B9
ZERO = 0
)
@@ -0,0 +1,647 @@
// 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.
// Code generated by genkeys.go using 'go generate'. DO NOT EDIT.
//go:build !playstation5
package gl
import (
"fmt"
"os"
)
type DebugContext struct {
Context Context
}
var _ Context = (*DebugContext)(nil)
func (d *DebugContext) ActiveTexture(arg0 uint32) {
d.Context.ActiveTexture(arg0)
fmt.Fprintln(os.Stderr, "ActiveTexture")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at ActiveTexture", e))
}
}
func (d *DebugContext) AttachShader(arg0 uint32, arg1 uint32) {
d.Context.AttachShader(arg0, arg1)
fmt.Fprintln(os.Stderr, "AttachShader")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at AttachShader", e))
}
}
func (d *DebugContext) BindAttribLocation(arg0 uint32, arg1 uint32, arg2 string) {
d.Context.BindAttribLocation(arg0, arg1, arg2)
fmt.Fprintln(os.Stderr, "BindAttribLocation")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BindAttribLocation", e))
}
}
func (d *DebugContext) BindBuffer(arg0 uint32, arg1 uint32) {
d.Context.BindBuffer(arg0, arg1)
fmt.Fprintln(os.Stderr, "BindBuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BindBuffer", e))
}
}
func (d *DebugContext) BindFramebuffer(arg0 uint32, arg1 uint32) {
d.Context.BindFramebuffer(arg0, arg1)
fmt.Fprintln(os.Stderr, "BindFramebuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BindFramebuffer", e))
}
}
func (d *DebugContext) BindRenderbuffer(arg0 uint32, arg1 uint32) {
d.Context.BindRenderbuffer(arg0, arg1)
fmt.Fprintln(os.Stderr, "BindRenderbuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BindRenderbuffer", e))
}
}
func (d *DebugContext) BindTexture(arg0 uint32, arg1 uint32) {
d.Context.BindTexture(arg0, arg1)
fmt.Fprintln(os.Stderr, "BindTexture")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BindTexture", e))
}
}
func (d *DebugContext) BindVertexArray(arg0 uint32) {
d.Context.BindVertexArray(arg0)
fmt.Fprintln(os.Stderr, "BindVertexArray")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BindVertexArray", e))
}
}
func (d *DebugContext) BlendEquationSeparate(arg0 uint32, arg1 uint32) {
d.Context.BlendEquationSeparate(arg0, arg1)
fmt.Fprintln(os.Stderr, "BlendEquationSeparate")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BlendEquationSeparate", e))
}
}
func (d *DebugContext) BlendFuncSeparate(arg0 uint32, arg1 uint32, arg2 uint32, arg3 uint32) {
d.Context.BlendFuncSeparate(arg0, arg1, arg2, arg3)
fmt.Fprintln(os.Stderr, "BlendFuncSeparate")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BlendFuncSeparate", e))
}
}
func (d *DebugContext) BufferInit(arg0 uint32, arg1 int, arg2 uint32) {
d.Context.BufferInit(arg0, arg1, arg2)
fmt.Fprintln(os.Stderr, "BufferInit")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BufferInit", e))
}
}
func (d *DebugContext) BufferSubData(arg0 uint32, arg1 int, arg2 []uint8) {
d.Context.BufferSubData(arg0, arg1, arg2)
fmt.Fprintln(os.Stderr, "BufferSubData")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at BufferSubData", e))
}
}
func (d *DebugContext) CheckFramebufferStatus(arg0 uint32) uint32 {
out0 := d.Context.CheckFramebufferStatus(arg0)
fmt.Fprintln(os.Stderr, "CheckFramebufferStatus")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at CheckFramebufferStatus", e))
}
return out0
}
func (d *DebugContext) Clear(arg0 uint32) {
d.Context.Clear(arg0)
fmt.Fprintln(os.Stderr, "Clear")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Clear", e))
}
}
func (d *DebugContext) ColorMask(arg0 bool, arg1 bool, arg2 bool, arg3 bool) {
d.Context.ColorMask(arg0, arg1, arg2, arg3)
fmt.Fprintln(os.Stderr, "ColorMask")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at ColorMask", e))
}
}
func (d *DebugContext) CompileShader(arg0 uint32) {
d.Context.CompileShader(arg0)
fmt.Fprintln(os.Stderr, "CompileShader")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at CompileShader", e))
}
}
func (d *DebugContext) CreateBuffer() uint32 {
out0 := d.Context.CreateBuffer()
fmt.Fprintln(os.Stderr, "CreateBuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at CreateBuffer", e))
}
return out0
}
func (d *DebugContext) CreateFramebuffer() uint32 {
out0 := d.Context.CreateFramebuffer()
fmt.Fprintln(os.Stderr, "CreateFramebuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at CreateFramebuffer", e))
}
return out0
}
func (d *DebugContext) CreateProgram() uint32 {
out0 := d.Context.CreateProgram()
fmt.Fprintln(os.Stderr, "CreateProgram")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at CreateProgram", e))
}
return out0
}
func (d *DebugContext) CreateRenderbuffer() uint32 {
out0 := d.Context.CreateRenderbuffer()
fmt.Fprintln(os.Stderr, "CreateRenderbuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at CreateRenderbuffer", e))
}
return out0
}
func (d *DebugContext) CreateShader(arg0 uint32) uint32 {
out0 := d.Context.CreateShader(arg0)
fmt.Fprintln(os.Stderr, "CreateShader")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at CreateShader", e))
}
return out0
}
func (d *DebugContext) CreateTexture() uint32 {
out0 := d.Context.CreateTexture()
fmt.Fprintln(os.Stderr, "CreateTexture")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at CreateTexture", e))
}
return out0
}
func (d *DebugContext) CreateVertexArray() uint32 {
out0 := d.Context.CreateVertexArray()
fmt.Fprintln(os.Stderr, "CreateVertexArray")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at CreateVertexArray", e))
}
return out0
}
func (d *DebugContext) DeleteBuffer(arg0 uint32) {
d.Context.DeleteBuffer(arg0)
fmt.Fprintln(os.Stderr, "DeleteBuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at DeleteBuffer", e))
}
}
func (d *DebugContext) DeleteFramebuffer(arg0 uint32) {
d.Context.DeleteFramebuffer(arg0)
fmt.Fprintln(os.Stderr, "DeleteFramebuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at DeleteFramebuffer", e))
}
}
func (d *DebugContext) DeleteProgram(arg0 uint32) {
d.Context.DeleteProgram(arg0)
fmt.Fprintln(os.Stderr, "DeleteProgram")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at DeleteProgram", e))
}
}
func (d *DebugContext) DeleteRenderbuffer(arg0 uint32) {
d.Context.DeleteRenderbuffer(arg0)
fmt.Fprintln(os.Stderr, "DeleteRenderbuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at DeleteRenderbuffer", e))
}
}
func (d *DebugContext) DeleteShader(arg0 uint32) {
d.Context.DeleteShader(arg0)
fmt.Fprintln(os.Stderr, "DeleteShader")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at DeleteShader", e))
}
}
func (d *DebugContext) DeleteTexture(arg0 uint32) {
d.Context.DeleteTexture(arg0)
fmt.Fprintln(os.Stderr, "DeleteTexture")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at DeleteTexture", e))
}
}
func (d *DebugContext) DeleteVertexArray(arg0 uint32) {
d.Context.DeleteVertexArray(arg0)
fmt.Fprintln(os.Stderr, "DeleteVertexArray")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at DeleteVertexArray", e))
}
}
func (d *DebugContext) Disable(arg0 uint32) {
d.Context.Disable(arg0)
fmt.Fprintln(os.Stderr, "Disable")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Disable", e))
}
}
func (d *DebugContext) DisableVertexAttribArray(arg0 uint32) {
d.Context.DisableVertexAttribArray(arg0)
fmt.Fprintln(os.Stderr, "DisableVertexAttribArray")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at DisableVertexAttribArray", e))
}
}
func (d *DebugContext) DrawElements(arg0 uint32, arg1 int32, arg2 uint32, arg3 int) {
d.Context.DrawElements(arg0, arg1, arg2, arg3)
fmt.Fprintln(os.Stderr, "DrawElements")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at DrawElements", e))
}
}
func (d *DebugContext) Enable(arg0 uint32) {
d.Context.Enable(arg0)
fmt.Fprintln(os.Stderr, "Enable")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Enable", e))
}
}
func (d *DebugContext) EnableVertexAttribArray(arg0 uint32) {
d.Context.EnableVertexAttribArray(arg0)
fmt.Fprintln(os.Stderr, "EnableVertexAttribArray")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at EnableVertexAttribArray", e))
}
}
func (d *DebugContext) Flush() {
d.Context.Flush()
fmt.Fprintln(os.Stderr, "Flush")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Flush", e))
}
}
func (d *DebugContext) FramebufferRenderbuffer(arg0 uint32, arg1 uint32, arg2 uint32, arg3 uint32) {
d.Context.FramebufferRenderbuffer(arg0, arg1, arg2, arg3)
fmt.Fprintln(os.Stderr, "FramebufferRenderbuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at FramebufferRenderbuffer", e))
}
}
func (d *DebugContext) FramebufferTexture2D(arg0 uint32, arg1 uint32, arg2 uint32, arg3 uint32, arg4 int32) {
d.Context.FramebufferTexture2D(arg0, arg1, arg2, arg3, arg4)
fmt.Fprintln(os.Stderr, "FramebufferTexture2D")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at FramebufferTexture2D", e))
}
}
func (d *DebugContext) GetError() uint32 {
out0 := d.Context.GetError()
fmt.Fprintln(os.Stderr, "GetError")
return out0
}
func (d *DebugContext) GetInteger(arg0 uint32) int {
out0 := d.Context.GetInteger(arg0)
fmt.Fprintln(os.Stderr, "GetInteger")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at GetInteger", e))
}
return out0
}
func (d *DebugContext) GetProgramInfoLog(arg0 uint32) string {
out0 := d.Context.GetProgramInfoLog(arg0)
fmt.Fprintln(os.Stderr, "GetProgramInfoLog")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at GetProgramInfoLog", e))
}
return out0
}
func (d *DebugContext) GetProgrami(arg0 uint32, arg1 uint32) int {
out0 := d.Context.GetProgrami(arg0, arg1)
fmt.Fprintln(os.Stderr, "GetProgrami")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at GetProgrami", e))
}
return out0
}
func (d *DebugContext) GetShaderInfoLog(arg0 uint32) string {
out0 := d.Context.GetShaderInfoLog(arg0)
fmt.Fprintln(os.Stderr, "GetShaderInfoLog")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at GetShaderInfoLog", e))
}
return out0
}
func (d *DebugContext) GetShaderi(arg0 uint32, arg1 uint32) int {
out0 := d.Context.GetShaderi(arg0, arg1)
fmt.Fprintln(os.Stderr, "GetShaderi")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at GetShaderi", e))
}
return out0
}
func (d *DebugContext) GetUniformLocation(arg0 uint32, arg1 string) int32 {
out0 := d.Context.GetUniformLocation(arg0, arg1)
fmt.Fprintln(os.Stderr, "GetUniformLocation")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at GetUniformLocation", e))
}
return out0
}
func (d *DebugContext) IsES() bool {
out0 := d.Context.IsES()
return out0
}
func (d *DebugContext) IsFramebuffer(arg0 uint32) bool {
out0 := d.Context.IsFramebuffer(arg0)
fmt.Fprintln(os.Stderr, "IsFramebuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at IsFramebuffer", e))
}
return out0
}
func (d *DebugContext) IsProgram(arg0 uint32) bool {
out0 := d.Context.IsProgram(arg0)
fmt.Fprintln(os.Stderr, "IsProgram")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at IsProgram", e))
}
return out0
}
func (d *DebugContext) IsRenderbuffer(arg0 uint32) bool {
out0 := d.Context.IsRenderbuffer(arg0)
fmt.Fprintln(os.Stderr, "IsRenderbuffer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at IsRenderbuffer", e))
}
return out0
}
func (d *DebugContext) LinkProgram(arg0 uint32) {
d.Context.LinkProgram(arg0)
fmt.Fprintln(os.Stderr, "LinkProgram")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at LinkProgram", e))
}
}
func (d *DebugContext) LoadFunctions() error {
out0 := d.Context.LoadFunctions()
return out0
}
func (d *DebugContext) PixelStorei(arg0 uint32, arg1 int32) {
d.Context.PixelStorei(arg0, arg1)
fmt.Fprintln(os.Stderr, "PixelStorei")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at PixelStorei", e))
}
}
func (d *DebugContext) ReadPixels(arg0 []uint8, arg1 int32, arg2 int32, arg3 int32, arg4 int32, arg5 uint32, arg6 uint32) {
d.Context.ReadPixels(arg0, arg1, arg2, arg3, arg4, arg5, arg6)
fmt.Fprintln(os.Stderr, "ReadPixels")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at ReadPixels", e))
}
}
func (d *DebugContext) RenderbufferStorage(arg0 uint32, arg1 uint32, arg2 int32, arg3 int32) {
d.Context.RenderbufferStorage(arg0, arg1, arg2, arg3)
fmt.Fprintln(os.Stderr, "RenderbufferStorage")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at RenderbufferStorage", e))
}
}
func (d *DebugContext) Scissor(arg0 int32, arg1 int32, arg2 int32, arg3 int32) {
d.Context.Scissor(arg0, arg1, arg2, arg3)
fmt.Fprintln(os.Stderr, "Scissor")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Scissor", e))
}
}
func (d *DebugContext) ShaderSource(arg0 uint32, arg1 string) {
d.Context.ShaderSource(arg0, arg1)
fmt.Fprintln(os.Stderr, "ShaderSource")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at ShaderSource", e))
}
}
func (d *DebugContext) StencilFunc(arg0 uint32, arg1 int32, arg2 uint32) {
d.Context.StencilFunc(arg0, arg1, arg2)
fmt.Fprintln(os.Stderr, "StencilFunc")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at StencilFunc", e))
}
}
func (d *DebugContext) StencilOpSeparate(arg0 uint32, arg1 uint32, arg2 uint32, arg3 uint32) {
d.Context.StencilOpSeparate(arg0, arg1, arg2, arg3)
fmt.Fprintln(os.Stderr, "StencilOpSeparate")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at StencilOpSeparate", e))
}
}
func (d *DebugContext) TexImage2D(arg0 uint32, arg1 int32, arg2 int32, arg3 int32, arg4 int32, arg5 uint32, arg6 uint32, arg7 []uint8) {
d.Context.TexImage2D(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
fmt.Fprintln(os.Stderr, "TexImage2D")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at TexImage2D", e))
}
}
func (d *DebugContext) TexParameteri(arg0 uint32, arg1 uint32, arg2 int32) {
d.Context.TexParameteri(arg0, arg1, arg2)
fmt.Fprintln(os.Stderr, "TexParameteri")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at TexParameteri", e))
}
}
func (d *DebugContext) TexSubImage2D(arg0 uint32, arg1 int32, arg2 int32, arg3 int32, arg4 int32, arg5 int32, arg6 uint32, arg7 uint32, arg8 []uint8) {
d.Context.TexSubImage2D(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
fmt.Fprintln(os.Stderr, "TexSubImage2D")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at TexSubImage2D", e))
}
}
func (d *DebugContext) Uniform1fv(arg0 int32, arg1 []float32) {
d.Context.Uniform1fv(arg0, arg1)
fmt.Fprintln(os.Stderr, "Uniform1fv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Uniform1fv", e))
}
}
func (d *DebugContext) Uniform1i(arg0 int32, arg1 int32) {
d.Context.Uniform1i(arg0, arg1)
fmt.Fprintln(os.Stderr, "Uniform1i")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Uniform1i", e))
}
}
func (d *DebugContext) Uniform1iv(arg0 int32, arg1 []int32) {
d.Context.Uniform1iv(arg0, arg1)
fmt.Fprintln(os.Stderr, "Uniform1iv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Uniform1iv", e))
}
}
func (d *DebugContext) Uniform2fv(arg0 int32, arg1 []float32) {
d.Context.Uniform2fv(arg0, arg1)
fmt.Fprintln(os.Stderr, "Uniform2fv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Uniform2fv", e))
}
}
func (d *DebugContext) Uniform2iv(arg0 int32, arg1 []int32) {
d.Context.Uniform2iv(arg0, arg1)
fmt.Fprintln(os.Stderr, "Uniform2iv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Uniform2iv", e))
}
}
func (d *DebugContext) Uniform3fv(arg0 int32, arg1 []float32) {
d.Context.Uniform3fv(arg0, arg1)
fmt.Fprintln(os.Stderr, "Uniform3fv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Uniform3fv", e))
}
}
func (d *DebugContext) Uniform3iv(arg0 int32, arg1 []int32) {
d.Context.Uniform3iv(arg0, arg1)
fmt.Fprintln(os.Stderr, "Uniform3iv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Uniform3iv", e))
}
}
func (d *DebugContext) Uniform4fv(arg0 int32, arg1 []float32) {
d.Context.Uniform4fv(arg0, arg1)
fmt.Fprintln(os.Stderr, "Uniform4fv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Uniform4fv", e))
}
}
func (d *DebugContext) Uniform4iv(arg0 int32, arg1 []int32) {
d.Context.Uniform4iv(arg0, arg1)
fmt.Fprintln(os.Stderr, "Uniform4iv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Uniform4iv", e))
}
}
func (d *DebugContext) UniformMatrix2fv(arg0 int32, arg1 []float32) {
d.Context.UniformMatrix2fv(arg0, arg1)
fmt.Fprintln(os.Stderr, "UniformMatrix2fv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at UniformMatrix2fv", e))
}
}
func (d *DebugContext) UniformMatrix3fv(arg0 int32, arg1 []float32) {
d.Context.UniformMatrix3fv(arg0, arg1)
fmt.Fprintln(os.Stderr, "UniformMatrix3fv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at UniformMatrix3fv", e))
}
}
func (d *DebugContext) UniformMatrix4fv(arg0 int32, arg1 []float32) {
d.Context.UniformMatrix4fv(arg0, arg1)
fmt.Fprintln(os.Stderr, "UniformMatrix4fv")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at UniformMatrix4fv", e))
}
}
func (d *DebugContext) UseProgram(arg0 uint32) {
d.Context.UseProgram(arg0)
fmt.Fprintln(os.Stderr, "UseProgram")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at UseProgram", e))
}
}
func (d *DebugContext) VertexAttribPointer(arg0 uint32, arg1 int32, arg2 uint32, arg3 bool, arg4 int32, arg5 int) {
d.Context.VertexAttribPointer(arg0, arg1, arg2, arg3, arg4, arg5)
fmt.Fprintln(os.Stderr, "VertexAttribPointer")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at VertexAttribPointer", e))
}
}
func (d *DebugContext) Viewport(arg0 int32, arg1 int32, arg2 int32, arg3 int32) {
d.Context.Viewport(arg0, arg1, arg2, arg3)
fmt.Fprintln(os.Stderr, "Viewport")
if e := d.Context.GetError(); e != NO_ERROR {
panic(fmt.Sprintf("gl: GetError() returned %d at Viewport", e))
}
}
@@ -0,0 +1,853 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2014 Eric Woroshow
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build !darwin && !js && !windows && !playstation5
package gl
// #include <stdint.h>
// #include <stdlib.h>
//
// typedef unsigned int GLenum;
// typedef unsigned char GLboolean;
// typedef unsigned int GLbitfield;
// typedef int GLint;
// typedef unsigned int GLuint;
// typedef int GLsizei;
// typedef float GLfloat;
// typedef char GLchar;
// typedef ptrdiff_t GLintptr;
// typedef ptrdiff_t GLsizeiptr;
//
// static void glowActiveTexture(uintptr_t fnptr, GLenum texture) {
// typedef void (*fn)(GLenum texture);
// ((fn)(fnptr))(texture);
// }
// static void glowAttachShader(uintptr_t fnptr, GLuint program, GLuint shader) {
// typedef void (*fn)(GLuint program, GLuint shader);
// ((fn)(fnptr))(program, shader);
// }
// static void glowBindAttribLocation(uintptr_t fnptr, GLuint program, GLuint index, const GLchar* name) {
// typedef void (*fn)(GLuint program, GLuint index, const GLchar* name);
// ((fn)(fnptr))(program, index, name);
// }
// static void glowBindBuffer(uintptr_t fnptr, GLenum target, GLuint buffer) {
// typedef void (*fn)(GLenum target, GLuint buffer);
// ((fn)(fnptr))(target, buffer);
// }
// static void glowBindFramebuffer(uintptr_t fnptr, GLenum target, GLuint framebuffer) {
// typedef void (*fn)(GLenum target, GLuint framebuffer);
// ((fn)(fnptr))(target, framebuffer);
// }
// static void glowBindRenderbuffer(uintptr_t fnptr, GLenum target, GLuint renderbuffer) {
// typedef void (*fn)(GLenum target, GLuint renderbuffer);
// ((fn)(fnptr))(target, renderbuffer);
// }
// static void glowBindTexture(uintptr_t fnptr, GLenum target, GLuint texture) {
// typedef void (*fn)(GLenum target, GLuint texture);
// ((fn)(fnptr))(target, texture);
// }
// static void glowBindVertexArray(uintptr_t fnptr, GLuint array) {
// typedef void (*fn)(GLuint array);
// ((fn)(fnptr))(array);
// }
// static void glowBlendEquationSeparate(uintptr_t fnptr, GLenum modeRGB, GLenum modeAlpha) {
// typedef void (*fn)(GLenum modeRGB, GLenum modeAlpha);
// ((fn)(fnptr))(modeRGB, modeAlpha);
// }
// static void glowBlendFuncSeparate(uintptr_t fnptr, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
// typedef void (*fn)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
// ((fn)(fnptr))(srcRGB, dstRGB, srcAlpha, dstAlpha);
// }
// static void glowBufferData(uintptr_t fnptr, GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
// typedef void (*fn)(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
// ((fn)(fnptr))(target, size, data, usage);
// }
// static void glowBufferSubData(uintptr_t fnptr, GLenum target, GLintptr offset, GLsizeiptr size, const void* data) {
// typedef void (*fn)(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
// ((fn)(fnptr))(target, offset, size, data);
// }
// static GLenum glowCheckFramebufferStatus(uintptr_t fnptr, GLenum target) {
// typedef GLenum (*fn)(GLenum target);
// return ((fn)(fnptr))(target);
// }
// static void glowClear(uintptr_t fnptr, GLbitfield mask) {
// typedef void (*fn)(GLbitfield mask);
// ((fn)(fnptr))(mask);
// }
// static void glowColorMask(uintptr_t fnptr, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
// typedef void (*fn)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
// ((fn)(fnptr))(red, green, blue, alpha);
// }
// static void glowCompileShader(uintptr_t fnptr, GLuint shader) {
// typedef void (*fn)(GLuint shader);
// ((fn)(fnptr))(shader);
// }
// static GLuint glowCreateProgram(uintptr_t fnptr) {
// typedef GLuint (*fn)();
// return ((fn)(fnptr))();
// }
// static GLuint glowCreateShader(uintptr_t fnptr, GLenum type) {
// typedef GLuint (*fn)(GLenum type);
// return ((fn)(fnptr))(type);
// }
// static void glowDeleteBuffers(uintptr_t fnptr, GLsizei n, const GLuint* buffers) {
// typedef void (*fn)(GLsizei n, const GLuint* buffers);
// ((fn)(fnptr))(n, buffers);
// }
// static void glowDeleteFramebuffers(uintptr_t fnptr, GLsizei n, const GLuint* framebuffers) {
// typedef void (*fn)(GLsizei n, const GLuint* framebuffers);
// ((fn)(fnptr))(n, framebuffers);
// }
// static void glowDeleteProgram(uintptr_t fnptr, GLuint program) {
// typedef void (*fn)(GLuint program);
// ((fn)(fnptr))(program);
// }
// static void glowDeleteRenderbuffers(uintptr_t fnptr, GLsizei n, const GLuint* renderbuffers) {
// typedef void (*fn)(GLsizei n, const GLuint* renderbuffers);
// ((fn)(fnptr))(n, renderbuffers);
// }
// static void glowDeleteShader(uintptr_t fnptr, GLuint shader) {
// typedef void (*fn)(GLuint shader);
// ((fn)(fnptr))(shader);
// }
// static void glowDeleteTextures(uintptr_t fnptr, GLsizei n, const GLuint* textures) {
// typedef void (*fn)(GLsizei n, const GLuint* textures);
// ((fn)(fnptr))(n, textures);
// }
// static void glowDeleteVertexArrays(uintptr_t fnptr, GLsizei n, const GLuint* arrays) {
// typedef void (*fn)(GLsizei n, const GLuint* arrays);
// ((fn)(fnptr))(n, arrays);
// }
// static void glowDisable(uintptr_t fnptr, GLenum cap) {
// typedef void (*fn)(GLenum cap);
// ((fn)(fnptr))(cap);
// }
// static void glowDisableVertexAttribArray(uintptr_t fnptr, GLuint index) {
// typedef void (*fn)(GLuint index);
// ((fn)(fnptr))(index);
// }
// static void glowDrawElements(uintptr_t fnptr, GLenum mode, GLsizei count, GLenum type, const uintptr_t indices) {
// typedef void (*fn)(GLenum mode, GLsizei count, GLenum type, const uintptr_t indices);
// ((fn)(fnptr))(mode, count, type, indices);
// }
// static void glowEnable(uintptr_t fnptr, GLenum cap) {
// typedef void (*fn)(GLenum cap);
// ((fn)(fnptr))(cap);
// }
// static void glowEnableVertexAttribArray(uintptr_t fnptr, GLuint index) {
// typedef void (*fn)(GLuint index);
// ((fn)(fnptr))(index);
// }
// static void glowFlush(uintptr_t fnptr) {
// typedef void (*fn)();
// ((fn)(fnptr))();
// }
// static void glowFramebufferRenderbuffer(uintptr_t fnptr, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {
// typedef void (*fn)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
// ((fn)(fnptr))(target, attachment, renderbuffertarget, renderbuffer);
// }
// static void glowFramebufferTexture2D(uintptr_t fnptr, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
// typedef void (*fn)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
// ((fn)(fnptr))(target, attachment, textarget, texture, level);
// }
// static void glowGenBuffers(uintptr_t fnptr, GLsizei n, GLuint* buffers) {
// typedef void (*fn)(GLsizei n, GLuint* buffers);
// ((fn)(fnptr))(n, buffers);
// }
// static void glowGenFramebuffers(uintptr_t fnptr, GLsizei n, GLuint* framebuffers) {
// typedef void (*fn)(GLsizei n, GLuint* framebuffers);
// ((fn)(fnptr))(n, framebuffers);
// }
// static void glowGenRenderbuffers(uintptr_t fnptr, GLsizei n, GLuint* renderbuffers) {
// typedef void (*fn)(GLsizei n, GLuint* renderbuffers);
// ((fn)(fnptr))(n, renderbuffers);
// }
// static void glowGenTextures(uintptr_t fnptr, GLsizei n, GLuint* textures) {
// typedef void (*fn)(GLsizei n, GLuint* textures);
// ((fn)(fnptr))(n, textures);
// }
// static void glowGenVertexArrays(uintptr_t fnptr, GLsizei n, GLuint* arrays) {
// typedef void (*fn)(GLsizei n, GLuint* arrays);
// ((fn)(fnptr))(n, arrays);
// }
// static GLenum glowGetError(uintptr_t fnptr) {
// typedef GLenum (*fn)();
// return ((fn)(fnptr))();
// }
// static void glowGetIntegerv(uintptr_t fnptr, GLenum pname, GLint* data) {
// typedef void (*fn)(GLenum pname, GLint* data);
// ((fn)(fnptr))(pname, data);
// }
// static void glowGetProgramInfoLog(uintptr_t fnptr, GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog) {
// typedef void (*fn)(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
// ((fn)(fnptr))(program, bufSize, length, infoLog);
// }
// static void glowGetProgramiv(uintptr_t fnptr, GLuint program, GLenum pname, GLint* params) {
// typedef void (*fn)(GLuint program, GLenum pname, GLint* params);
// ((fn)(fnptr))(program, pname, params);
// }
// static void glowGetShaderInfoLog(uintptr_t fnptr, GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog) {
// typedef void (*fn)(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
// ((fn)(fnptr))(shader, bufSize, length, infoLog);
// }
// static void glowGetShaderiv(uintptr_t fnptr, GLuint shader, GLenum pname, GLint* params) {
// typedef void (*fn)(GLuint shader, GLenum pname, GLint* params);
// ((fn)(fnptr))(shader, pname, params);
// }
// static GLint glowGetUniformLocation(uintptr_t fnptr, GLuint program, const GLchar* name) {
// typedef GLint (*fn)(GLuint program, const GLchar* name);
// return ((fn)(fnptr))(program, name);
// }
// static GLboolean glowIsFramebuffer(uintptr_t fnptr, GLuint framebuffer) {
// typedef GLboolean (*fn)(GLuint framebuffer);
// return ((fn)(fnptr))(framebuffer);
// }
// static GLboolean glowIsProgram(uintptr_t fnptr, GLuint program) {
// typedef GLboolean (*fn)(GLuint program);
// return ((fn)(fnptr))(program);
// }
// static GLboolean glowIsRenderbuffer(uintptr_t fnptr, GLuint renderbuffer) {
// typedef GLboolean (*fn)(GLuint renderbuffer);
// return ((fn)(fnptr))(renderbuffer);
// }
// static void glowLinkProgram(uintptr_t fnptr, GLuint program) {
// typedef void (*fn)(GLuint program);
// ((fn)(fnptr))(program);
// }
// static void glowPixelStorei(uintptr_t fnptr, GLenum pname, GLint param) {
// typedef void (*fn)(GLenum pname, GLint param);
// ((fn)(fnptr))(pname, param);
// }
// static void glowReadPixels(uintptr_t fnptr, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {
// typedef void (*fn)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
// ((fn)(fnptr))(x, y, width, height, format, type, pixels);
// }
// static void glowRenderbufferStorage(uintptr_t fnptr, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
// typedef void (*fn)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
// ((fn)(fnptr))(target, internalformat, width, height);
// }
// static void glowScissor(uintptr_t fnptr, GLint x, GLint y, GLsizei width, GLsizei height) {
// typedef void (*fn)(GLint x, GLint y, GLsizei width, GLsizei height);
// ((fn)(fnptr))(x, y, width, height);
// }
// static void glowShaderSource(uintptr_t fnptr, GLuint shader, GLsizei count, const GLchar*const* string, const GLint* length) {
// typedef void (*fn)(GLuint shader, GLsizei count, const GLchar*const* string, const GLint* length);
// ((fn)(fnptr))(shader, count, string, length);
// }
// static void glowStencilFunc(uintptr_t fnptr, GLenum func, GLint ref, GLuint mask) {
// typedef void (*fn)(GLenum func, GLint ref, GLuint mask);
// ((fn)(fnptr))(func, ref, mask);
// }
// static void glowStencilOpSeparate(uintptr_t fnptr, GLenum face, GLenum fail, GLenum zfail, GLenum zpass) {
// typedef void (*fn)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
// ((fn)(fnptr))(face, fail, zfail, zpass);
// }
// static void glowTexImage2D(uintptr_t fnptr, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels) {
// typedef void (*fn)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels);
// ((fn)(fnptr))(target, level, internalformat, width, height, border, format, type, pixels);
// }
// static void glowTexParameteri(uintptr_t fnptr, GLenum target, GLenum pname, GLint param) {
// typedef void (*fn)(GLenum target, GLenum pname, GLint param);
// ((fn)(fnptr))(target, pname, param);
// }
// static void glowTexSubImage2D(uintptr_t fnptr, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) {
// typedef void (*fn)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels);
// ((fn)(fnptr))(target, level, xoffset, yoffset, width, height, format, type, pixels);
// }
// static void glowUniform1fv(uintptr_t fnptr, GLint location, GLsizei count, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLfloat* value);
// ((fn)(fnptr))(location, count, value);
// }
// static void glowUniform1i(uintptr_t fnptr, GLint location, GLint v0) {
// typedef void (*fn)(GLint location, GLint v0);
// ((fn)(fnptr))(location, v0);
// }
// static void glowUniform1iv(uintptr_t fnptr, GLint location, GLsizei count, const GLint* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLint* value);
// ((fn)(fnptr))(location, count, value);
// }
// static void glowUniform2fv(uintptr_t fnptr, GLint location, GLsizei count, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLfloat* value);
// ((fn)(fnptr))(location, count, value);
// }
// static void glowUniform2iv(uintptr_t fnptr, GLint location, GLsizei count, const GLint* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLint* value);
// ((fn)(fnptr))(location, count, value);
// }
// static void glowUniform3fv(uintptr_t fnptr, GLint location, GLsizei count, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLfloat* value);
// ((fn)(fnptr))(location, count, value);
// }
// static void glowUniform3iv(uintptr_t fnptr, GLint location, GLsizei count, const GLint* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLint* value);
// ((fn)(fnptr))(location, count, value);
// }
// static void glowUniform4fv(uintptr_t fnptr, GLint location, GLsizei count, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLfloat* value);
// ((fn)(fnptr))(location, count, value);
// }
// static void glowUniform4iv(uintptr_t fnptr, GLint location, GLsizei count, const GLint* value) {
// typedef void (*fn)(GLint location, GLsizei count, const GLint* value);
// ((fn)(fnptr))(location, count, value);
// }
// static void glowUniformMatrix2fv(uintptr_t fnptr, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
// ((fn)(fnptr))(location, count, transpose, value);
// }
// static void glowUniformMatrix3fv(uintptr_t fnptr, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
// ((fn)(fnptr))(location, count, transpose, value);
// }
// static void glowUniformMatrix4fv(uintptr_t fnptr, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
// typedef void (*fn)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
// ((fn)(fnptr))(location, count, transpose, value);
// }
// static void glowUseProgram(uintptr_t fnptr, GLuint program) {
// typedef void (*fn)(GLuint program);
// ((fn)(fnptr))(program);
// }
// static void glowVertexAttribPointer(uintptr_t fnptr, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const uintptr_t pointer) {
// typedef void (*fn)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const uintptr_t pointer);
// ((fn)(fnptr))(index, size, type, normalized, stride, pointer);
// }
// static void glowViewport(uintptr_t fnptr, GLint x, GLint y, GLsizei width, GLsizei height) {
// typedef void (*fn)(GLint x, GLint y, GLsizei width, GLsizei height);
// ((fn)(fnptr))(x, y, width, height);
// }
import "C"
import (
"runtime"
"unsafe"
)
type defaultContext struct {
gpActiveTexture C.uintptr_t
gpAttachShader C.uintptr_t
gpBindAttribLocation C.uintptr_t
gpBindBuffer C.uintptr_t
gpBindFramebuffer C.uintptr_t
gpBindRenderbuffer C.uintptr_t
gpBindTexture C.uintptr_t
gpBindVertexArray C.uintptr_t
gpBlendEquationSeparate C.uintptr_t
gpBlendFuncSeparate C.uintptr_t
gpBufferData C.uintptr_t
gpBufferSubData C.uintptr_t
gpCheckFramebufferStatus C.uintptr_t
gpClear C.uintptr_t
gpColorMask C.uintptr_t
gpCompileShader C.uintptr_t
gpCreateProgram C.uintptr_t
gpCreateShader C.uintptr_t
gpDeleteBuffers C.uintptr_t
gpDeleteFramebuffers C.uintptr_t
gpDeleteProgram C.uintptr_t
gpDeleteRenderbuffers C.uintptr_t
gpDeleteShader C.uintptr_t
gpDeleteTextures C.uintptr_t
gpDeleteVertexArrays C.uintptr_t
gpDisable C.uintptr_t
gpDisableVertexAttribArray C.uintptr_t
gpDrawElements C.uintptr_t
gpEnable C.uintptr_t
gpEnableVertexAttribArray C.uintptr_t
gpFlush C.uintptr_t
gpFramebufferRenderbuffer C.uintptr_t
gpFramebufferTexture2D C.uintptr_t
gpGenBuffers C.uintptr_t
gpGenFramebuffers C.uintptr_t
gpGenRenderbuffers C.uintptr_t
gpGenTextures C.uintptr_t
gpGenVertexArrays C.uintptr_t
gpGetError C.uintptr_t
gpGetIntegerv C.uintptr_t
gpGetProgramInfoLog C.uintptr_t
gpGetProgramiv C.uintptr_t
gpGetShaderInfoLog C.uintptr_t
gpGetShaderiv C.uintptr_t
gpGetUniformLocation C.uintptr_t
gpIsFramebuffer C.uintptr_t
gpIsProgram C.uintptr_t
gpIsRenderbuffer C.uintptr_t
gpLinkProgram C.uintptr_t
gpPixelStorei C.uintptr_t
gpReadPixels C.uintptr_t
gpRenderbufferStorage C.uintptr_t
gpScissor C.uintptr_t
gpShaderSource C.uintptr_t
gpStencilFunc C.uintptr_t
gpStencilOpSeparate C.uintptr_t
gpTexImage2D C.uintptr_t
gpTexParameteri C.uintptr_t
gpTexSubImage2D C.uintptr_t
gpUniform1fv C.uintptr_t
gpUniform1i C.uintptr_t
gpUniform1iv C.uintptr_t
gpUniform2fv C.uintptr_t
gpUniform2iv C.uintptr_t
gpUniform3fv C.uintptr_t
gpUniform3iv C.uintptr_t
gpUniform4fv C.uintptr_t
gpUniform4iv C.uintptr_t
gpUniformMatrix2fv C.uintptr_t
gpUniformMatrix3fv C.uintptr_t
gpUniformMatrix4fv C.uintptr_t
gpUseProgram C.uintptr_t
gpVertexAttribPointer C.uintptr_t
gpViewport C.uintptr_t
isES bool
}
func NewDefaultContext() (Context, error) {
ctx := &defaultContext{}
if err := ctx.init(); err != nil {
return nil, err
}
return ctx, nil
}
func boolToInt(b bool) int {
if b {
return 1
}
return 0
}
func (c *defaultContext) IsES() bool {
return c.isES
}
func (c *defaultContext) ActiveTexture(texture uint32) {
C.glowActiveTexture(c.gpActiveTexture, C.GLenum(texture))
}
func (c *defaultContext) AttachShader(program uint32, shader uint32) {
C.glowAttachShader(c.gpAttachShader, C.GLuint(program), C.GLuint(shader))
}
func (c *defaultContext) BindAttribLocation(program uint32, index uint32, name string) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
C.glowBindAttribLocation(c.gpBindAttribLocation, C.GLuint(program), C.GLuint(index), (*C.GLchar)(unsafe.Pointer(cname)))
}
func (c *defaultContext) BindBuffer(target uint32, buffer uint32) {
C.glowBindBuffer(c.gpBindBuffer, C.GLenum(target), C.GLuint(buffer))
}
func (c *defaultContext) BindFramebuffer(target uint32, framebuffer uint32) {
C.glowBindFramebuffer(c.gpBindFramebuffer, C.GLenum(target), C.GLuint(framebuffer))
}
func (c *defaultContext) BindRenderbuffer(target uint32, renderbuffer uint32) {
C.glowBindRenderbuffer(c.gpBindRenderbuffer, C.GLenum(target), C.GLuint(renderbuffer))
}
func (c *defaultContext) BindTexture(target uint32, texture uint32) {
C.glowBindTexture(c.gpBindTexture, C.GLenum(target), C.GLuint(texture))
}
func (c *defaultContext) BindVertexArray(array uint32) {
C.glowBindVertexArray(c.gpBindVertexArray, C.GLuint(array))
}
func (c *defaultContext) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
C.glowBlendEquationSeparate(c.gpBlendEquationSeparate, C.GLenum(modeRGB), C.GLenum(modeAlpha))
}
func (c *defaultContext) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
C.glowBlendFuncSeparate(c.gpBlendFuncSeparate, C.GLenum(srcRGB), C.GLenum(dstRGB), C.GLenum(srcAlpha), C.GLenum(dstAlpha))
}
func (c *defaultContext) BufferInit(target uint32, size int, usage uint32) {
C.glowBufferData(c.gpBufferData, C.GLenum(target), C.GLsizeiptr(size), nil, C.GLenum(usage))
}
func (c *defaultContext) BufferSubData(target uint32, offset int, data []byte) {
C.glowBufferSubData(c.gpBufferSubData, C.GLenum(target), C.GLintptr(offset), C.GLsizeiptr(len(data)), unsafe.Pointer(&data[0]))
runtime.KeepAlive(data)
}
func (c *defaultContext) CheckFramebufferStatus(target uint32) uint32 {
ret := C.glowCheckFramebufferStatus(c.gpCheckFramebufferStatus, C.GLenum(target))
return uint32(ret)
}
func (c *defaultContext) Clear(mask uint32) {
C.glowClear(c.gpClear, C.GLbitfield(mask))
}
func (c *defaultContext) ColorMask(red bool, green bool, blue bool, alpha bool) {
C.glowColorMask(c.gpColorMask, C.GLboolean(boolToInt(red)), C.GLboolean(boolToInt(green)), C.GLboolean(boolToInt(blue)), C.GLboolean(boolToInt(alpha)))
}
func (c *defaultContext) CompileShader(shader uint32) {
C.glowCompileShader(c.gpCompileShader, C.GLuint(shader))
}
func (c *defaultContext) CreateBuffer() uint32 {
var buffer uint32
C.glowGenBuffers(c.gpGenBuffers, 1, (*C.GLuint)(unsafe.Pointer(&buffer)))
return buffer
}
func (c *defaultContext) CreateFramebuffer() uint32 {
var framebuffer uint32
C.glowGenFramebuffers(c.gpGenFramebuffers, 1, (*C.GLuint)(unsafe.Pointer(&framebuffer)))
return framebuffer
}
func (c *defaultContext) CreateProgram() uint32 {
ret := C.glowCreateProgram(c.gpCreateProgram)
return uint32(ret)
}
func (c *defaultContext) CreateRenderbuffer() uint32 {
var renderbuffer uint32
C.glowGenRenderbuffers(c.gpGenRenderbuffers, 1, (*C.GLuint)(unsafe.Pointer(&renderbuffer)))
return renderbuffer
}
func (c *defaultContext) CreateShader(xtype uint32) uint32 {
ret := C.glowCreateShader(c.gpCreateShader, C.GLenum(xtype))
return uint32(ret)
}
func (c *defaultContext) CreateTexture() uint32 {
var texture uint32
C.glowGenTextures(c.gpGenTextures, 1, (*C.GLuint)(unsafe.Pointer(&texture)))
return texture
}
func (c *defaultContext) CreateVertexArray() uint32 {
var array uint32
C.glowGenVertexArrays(c.gpGenVertexArrays, 1, (*C.GLuint)(unsafe.Pointer(&array)))
return array
}
func (c *defaultContext) DeleteBuffer(buffer uint32) {
C.glowDeleteBuffers(c.gpDeleteBuffers, 1, (*C.GLuint)(unsafe.Pointer(&buffer)))
}
func (c *defaultContext) DeleteFramebuffer(framebuffer uint32) {
C.glowDeleteFramebuffers(c.gpDeleteFramebuffers, 1, (*C.GLuint)(unsafe.Pointer(&framebuffer)))
}
func (c *defaultContext) DeleteProgram(program uint32) {
C.glowDeleteProgram(c.gpDeleteProgram, C.GLuint(program))
}
func (c *defaultContext) DeleteRenderbuffer(renderbuffer uint32) {
C.glowDeleteRenderbuffers(c.gpDeleteRenderbuffers, 1, (*C.GLuint)(unsafe.Pointer(&renderbuffer)))
}
func (c *defaultContext) DeleteShader(shader uint32) {
C.glowDeleteShader(c.gpDeleteShader, C.GLuint(shader))
}
func (c *defaultContext) DeleteTexture(texture uint32) {
C.glowDeleteTextures(c.gpDeleteTextures, 1, (*C.GLuint)(unsafe.Pointer(&texture)))
}
func (c *defaultContext) DeleteVertexArray(array uint32) {
C.glowDeleteVertexArrays(c.gpDeleteVertexArrays, 1, (*C.GLuint)(unsafe.Pointer(&array)))
}
func (c *defaultContext) Disable(cap uint32) {
C.glowDisable(c.gpDisable, C.GLenum(cap))
}
func (c *defaultContext) DisableVertexAttribArray(index uint32) {
C.glowDisableVertexAttribArray(c.gpDisableVertexAttribArray, C.GLuint(index))
}
func (c *defaultContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) {
C.glowDrawElements(c.gpDrawElements, C.GLenum(mode), C.GLsizei(count), C.GLenum(xtype), C.uintptr_t(offset))
}
func (c *defaultContext) Enable(cap uint32) {
C.glowEnable(c.gpEnable, C.GLenum(cap))
}
func (c *defaultContext) EnableVertexAttribArray(index uint32) {
C.glowEnableVertexAttribArray(c.gpEnableVertexAttribArray, C.GLuint(index))
}
func (c *defaultContext) Flush() {
C.glowFlush(c.gpFlush)
}
func (c *defaultContext) FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32) {
C.glowFramebufferRenderbuffer(c.gpFramebufferRenderbuffer, C.GLenum(target), C.GLenum(attachment), C.GLenum(renderbuffertarget), C.GLuint(renderbuffer))
}
func (c *defaultContext) FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32) {
C.glowFramebufferTexture2D(c.gpFramebufferTexture2D, C.GLenum(target), C.GLenum(attachment), C.GLenum(textarget), C.GLuint(texture), C.GLint(level))
}
func (c *defaultContext) GetError() uint32 {
ret := C.glowGetError(c.gpGetError)
return uint32(ret)
}
func (c *defaultContext) GetInteger(pname uint32) int {
var dst int32
C.glowGetIntegerv(c.gpGetIntegerv, C.GLenum(pname), (*C.GLint)(unsafe.Pointer(&dst)))
return int(dst)
}
func (c *defaultContext) GetProgramInfoLog(program uint32) string {
bufSize := c.GetProgrami(program, INFO_LOG_LENGTH)
infoLog := make([]byte, bufSize)
C.glowGetProgramInfoLog(c.gpGetProgramInfoLog, C.GLuint(program), C.GLsizei(bufSize), nil, (*C.GLchar)(unsafe.Pointer(&infoLog[0])))
return string(infoLog)
}
func (c *defaultContext) GetProgrami(program uint32, pname uint32) int {
var dst int32
C.glowGetProgramiv(c.gpGetProgramiv, C.GLuint(program), C.GLenum(pname), (*C.GLint)(unsafe.Pointer(&dst)))
return int(dst)
}
func (c *defaultContext) GetShaderInfoLog(shader uint32) string {
bufSize := c.GetShaderi(shader, INFO_LOG_LENGTH)
infoLog := make([]byte, bufSize)
C.glowGetShaderInfoLog(c.gpGetShaderInfoLog, C.GLuint(shader), C.GLsizei(bufSize), nil, (*C.GLchar)(unsafe.Pointer(&infoLog[0])))
return string(infoLog)
}
func (c *defaultContext) GetShaderi(shader uint32, pname uint32) int {
var dst int32
C.glowGetShaderiv(c.gpGetShaderiv, C.GLuint(shader), C.GLenum(pname), (*C.GLint)(unsafe.Pointer(&dst)))
return int(dst)
}
func (c *defaultContext) GetUniformLocation(program uint32, name string) int32 {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
ret := C.glowGetUniformLocation(c.gpGetUniformLocation, C.GLuint(program), (*C.GLchar)(unsafe.Pointer(cname)))
return int32(ret)
}
func (c *defaultContext) IsFramebuffer(framebuffer uint32) bool {
ret := C.glowIsFramebuffer(c.gpIsFramebuffer, C.GLuint(framebuffer))
return ret == TRUE
}
func (c *defaultContext) IsProgram(program uint32) bool {
ret := C.glowIsProgram(c.gpIsProgram, C.GLuint(program))
return ret == TRUE
}
func (c *defaultContext) IsRenderbuffer(renderbuffer uint32) bool {
ret := C.glowIsRenderbuffer(c.gpIsRenderbuffer, C.GLuint(renderbuffer))
return ret == TRUE
}
func (c *defaultContext) LinkProgram(program uint32) {
C.glowLinkProgram(c.gpLinkProgram, C.GLuint(program))
}
func (c *defaultContext) PixelStorei(pname uint32, param int32) {
C.glowPixelStorei(c.gpPixelStorei, C.GLenum(pname), C.GLint(param))
}
func (c *defaultContext) ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) {
C.glowReadPixels(c.gpReadPixels, C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(xtype), unsafe.Pointer(&dst[0]))
}
func (c *defaultContext) RenderbufferStorage(target uint32, internalformat uint32, width int32, height int32) {
C.glowRenderbufferStorage(c.gpRenderbufferStorage, C.GLenum(target), C.GLenum(internalformat), C.GLsizei(width), C.GLsizei(height))
}
func (c *defaultContext) Scissor(x int32, y int32, width int32, height int32) {
C.glowScissor(c.gpScissor, C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
}
func (c *defaultContext) ShaderSource(shader uint32, xstring string) {
cstring := C.CString(xstring)
defer C.free(unsafe.Pointer(cstring))
C.glowShaderSource(c.gpShaderSource, C.GLuint(shader), 1, (**C.GLchar)(unsafe.Pointer(&cstring)), nil)
}
func (c *defaultContext) StencilFunc(xfunc uint32, ref int32, mask uint32) {
C.glowStencilFunc(c.gpStencilFunc, C.GLenum(xfunc), C.GLint(ref), C.GLuint(mask))
}
func (c *defaultContext) StencilOpSeparate(face uint32, fail uint32, zfail uint32, zpass uint32) {
C.glowStencilOpSeparate(c.gpStencilOpSeparate, C.GLenum(face), C.GLenum(fail), C.GLenum(zfail), C.GLenum(zpass))
}
func (c *defaultContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
var ptr *byte
if len(pixels) > 0 {
ptr = &pixels[0]
}
C.glowTexImage2D(c.gpTexImage2D, C.GLenum(target), C.GLint(level), C.GLint(internalformat), C.GLsizei(width), C.GLsizei(height), 0, C.GLenum(format), C.GLenum(xtype), unsafe.Pointer(ptr))
runtime.KeepAlive(pixels)
}
func (c *defaultContext) TexParameteri(target uint32, pname uint32, param int32) {
C.glowTexParameteri(c.gpTexParameteri, C.GLenum(target), C.GLenum(pname), C.GLint(param))
}
func (c *defaultContext) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
C.glowTexSubImage2D(c.gpTexSubImage2D, C.GLenum(target), C.GLint(level), C.GLint(xoffset), C.GLint(yoffset), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(xtype), unsafe.Pointer(&pixels[0]))
runtime.KeepAlive(pixels)
}
func (c *defaultContext) Uniform1fv(location int32, value []float32) {
C.glowUniform1fv(c.gpUniform1fv, C.GLint(location), C.GLsizei(len(value)), (*C.GLfloat)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform1i(location int32, v0 int32) {
C.glowUniform1i(c.gpUniform1i, C.GLint(location), C.GLint(v0))
}
func (c *defaultContext) Uniform1iv(location int32, value []int32) {
C.glowUniform1iv(c.gpUniform1iv, C.GLint(location), C.GLsizei(len(value)), (*C.GLint)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform2fv(location int32, value []float32) {
C.glowUniform2fv(c.gpUniform2fv, C.GLint(location), C.GLsizei(len(value)/2), (*C.GLfloat)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform2iv(location int32, value []int32) {
C.glowUniform2iv(c.gpUniform2iv, C.GLint(location), C.GLsizei(len(value)/2), (*C.GLint)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform3fv(location int32, value []float32) {
C.glowUniform3fv(c.gpUniform3fv, C.GLint(location), C.GLsizei(len(value)/3), (*C.GLfloat)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform3iv(location int32, value []int32) {
C.glowUniform3iv(c.gpUniform3iv, C.GLint(location), C.GLsizei(len(value)/3), (*C.GLint)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform4fv(location int32, value []float32) {
C.glowUniform4fv(c.gpUniform4fv, C.GLint(location), C.GLsizei(len(value)/4), (*C.GLfloat)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform4iv(location int32, value []int32) {
C.glowUniform4iv(c.gpUniform4iv, C.GLint(location), C.GLsizei(len(value)/4), (*C.GLint)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) UniformMatrix2fv(location int32, value []float32) {
C.glowUniformMatrix2fv(c.gpUniformMatrix2fv, C.GLint(location), C.GLsizei(len(value)/4), 0, (*C.GLfloat)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) UniformMatrix3fv(location int32, value []float32) {
C.glowUniformMatrix3fv(c.gpUniformMatrix3fv, C.GLint(location), C.GLsizei(len(value)/9), 0, (*C.GLfloat)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) UniformMatrix4fv(location int32, value []float32) {
C.glowUniformMatrix4fv(c.gpUniformMatrix4fv, C.GLint(location), C.GLsizei(len(value)/16), 0, (*C.GLfloat)(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) UseProgram(program uint32) {
C.glowUseProgram(c.gpUseProgram, C.GLuint(program))
}
func (c *defaultContext) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset int) {
C.glowVertexAttribPointer(c.gpVertexAttribPointer, C.GLuint(index), C.GLint(size), C.GLenum(xtype), C.GLboolean(boolToInt(normalized)), C.GLsizei(stride), C.uintptr_t(offset))
}
func (c *defaultContext) Viewport(x int32, y int32, width int32, height int32) {
C.glowViewport(c.gpViewport, C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
}
func (c *defaultContext) LoadFunctions() error {
g := procAddressGetter{ctx: c}
c.gpActiveTexture = C.uintptr_t(g.get("glActiveTexture"))
c.gpAttachShader = C.uintptr_t(g.get("glAttachShader"))
c.gpBindAttribLocation = C.uintptr_t(g.get("glBindAttribLocation"))
c.gpBindBuffer = C.uintptr_t(g.get("glBindBuffer"))
c.gpBindFramebuffer = C.uintptr_t(g.get("glBindFramebuffer"))
c.gpBindRenderbuffer = C.uintptr_t(g.get("glBindRenderbuffer"))
c.gpBindTexture = C.uintptr_t(g.get("glBindTexture"))
c.gpBindVertexArray = C.uintptr_t(g.get("glBindVertexArray"))
c.gpBlendEquationSeparate = C.uintptr_t(g.get("glBlendEquationSeparate"))
c.gpBlendFuncSeparate = C.uintptr_t(g.get("glBlendFuncSeparate"))
c.gpBufferData = C.uintptr_t(g.get("glBufferData"))
c.gpBufferSubData = C.uintptr_t(g.get("glBufferSubData"))
c.gpCheckFramebufferStatus = C.uintptr_t(g.get("glCheckFramebufferStatus"))
c.gpClear = C.uintptr_t(g.get("glClear"))
c.gpColorMask = C.uintptr_t(g.get("glColorMask"))
c.gpCompileShader = C.uintptr_t(g.get("glCompileShader"))
c.gpCreateProgram = C.uintptr_t(g.get("glCreateProgram"))
c.gpCreateShader = C.uintptr_t(g.get("glCreateShader"))
c.gpDeleteBuffers = C.uintptr_t(g.get("glDeleteBuffers"))
c.gpDeleteFramebuffers = C.uintptr_t(g.get("glDeleteFramebuffers"))
c.gpDeleteProgram = C.uintptr_t(g.get("glDeleteProgram"))
c.gpDeleteRenderbuffers = C.uintptr_t(g.get("glDeleteRenderbuffers"))
c.gpDeleteShader = C.uintptr_t(g.get("glDeleteShader"))
c.gpDeleteTextures = C.uintptr_t(g.get("glDeleteTextures"))
c.gpDeleteVertexArrays = C.uintptr_t(g.get("glDeleteVertexArrays"))
c.gpDisable = C.uintptr_t(g.get("glDisable"))
c.gpDisableVertexAttribArray = C.uintptr_t(g.get("glDisableVertexAttribArray"))
c.gpDrawElements = C.uintptr_t(g.get("glDrawElements"))
c.gpEnable = C.uintptr_t(g.get("glEnable"))
c.gpEnableVertexAttribArray = C.uintptr_t(g.get("glEnableVertexAttribArray"))
c.gpFlush = C.uintptr_t(g.get("glFlush"))
c.gpFramebufferRenderbuffer = C.uintptr_t(g.get("glFramebufferRenderbuffer"))
c.gpFramebufferTexture2D = C.uintptr_t(g.get("glFramebufferTexture2D"))
c.gpGenBuffers = C.uintptr_t(g.get("glGenBuffers"))
c.gpGenFramebuffers = C.uintptr_t(g.get("glGenFramebuffers"))
c.gpGenRenderbuffers = C.uintptr_t(g.get("glGenRenderbuffers"))
c.gpGenTextures = C.uintptr_t(g.get("glGenTextures"))
c.gpGenVertexArrays = C.uintptr_t(g.get("glGenVertexArrays"))
c.gpGetError = C.uintptr_t(g.get("glGetError"))
c.gpGetIntegerv = C.uintptr_t(g.get("glGetIntegerv"))
c.gpGetProgramInfoLog = C.uintptr_t(g.get("glGetProgramInfoLog"))
c.gpGetProgramiv = C.uintptr_t(g.get("glGetProgramiv"))
c.gpGetShaderInfoLog = C.uintptr_t(g.get("glGetShaderInfoLog"))
c.gpGetShaderiv = C.uintptr_t(g.get("glGetShaderiv"))
c.gpGetUniformLocation = C.uintptr_t(g.get("glGetUniformLocation"))
c.gpIsFramebuffer = C.uintptr_t(g.get("glIsFramebuffer"))
c.gpIsProgram = C.uintptr_t(g.get("glIsProgram"))
c.gpIsRenderbuffer = C.uintptr_t(g.get("glIsRenderbuffer"))
c.gpLinkProgram = C.uintptr_t(g.get("glLinkProgram"))
c.gpPixelStorei = C.uintptr_t(g.get("glPixelStorei"))
c.gpReadPixels = C.uintptr_t(g.get("glReadPixels"))
c.gpRenderbufferStorage = C.uintptr_t(g.get("glRenderbufferStorage"))
c.gpScissor = C.uintptr_t(g.get("glScissor"))
c.gpShaderSource = C.uintptr_t(g.get("glShaderSource"))
c.gpStencilFunc = C.uintptr_t(g.get("glStencilFunc"))
c.gpStencilOpSeparate = C.uintptr_t(g.get("glStencilOpSeparate"))
c.gpTexImage2D = C.uintptr_t(g.get("glTexImage2D"))
c.gpTexParameteri = C.uintptr_t(g.get("glTexParameteri"))
c.gpTexSubImage2D = C.uintptr_t(g.get("glTexSubImage2D"))
c.gpUniform1fv = C.uintptr_t(g.get("glUniform1fv"))
c.gpUniform1i = C.uintptr_t(g.get("glUniform1i"))
c.gpUniform1iv = C.uintptr_t(g.get("glUniform1iv"))
c.gpUniform2fv = C.uintptr_t(g.get("glUniform2fv"))
c.gpUniform2iv = C.uintptr_t(g.get("glUniform2iv"))
c.gpUniform3fv = C.uintptr_t(g.get("glUniform3fv"))
c.gpUniform3iv = C.uintptr_t(g.get("glUniform3iv"))
c.gpUniform4fv = C.uintptr_t(g.get("glUniform4fv"))
c.gpUniform4iv = C.uintptr_t(g.get("glUniform4iv"))
c.gpUniformMatrix2fv = C.uintptr_t(g.get("glUniformMatrix2fv"))
c.gpUniformMatrix3fv = C.uintptr_t(g.get("glUniformMatrix3fv"))
c.gpUniformMatrix4fv = C.uintptr_t(g.get("glUniformMatrix4fv"))
c.gpUseProgram = C.uintptr_t(g.get("glUseProgram"))
c.gpVertexAttribPointer = C.uintptr_t(g.get("glVertexAttribPointer"))
c.gpViewport = C.uintptr_t(g.get("glViewport"))
return g.error()
}
@@ -0,0 +1,634 @@
// 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.
package gl
import (
"fmt"
"syscall/js"
"github.com/hajimehoshi/ebiten/v2/internal/jsutil"
)
type defaultContext struct {
fnActiveTexture js.Value
fnAttachShader js.Value
fnBindAttribLocation js.Value
fnBindBuffer js.Value
fnBindFramebuffer js.Value
fnBindRenderbuffer js.Value
fnBindTexture js.Value
fnBindVertexArray js.Value
fnBlendEquationSeparate js.Value
fnBlendFuncSeparate js.Value
fnBufferData js.Value
fnBufferSubData js.Value
fnCheckFramebufferStatus js.Value
fnClear js.Value
fnColorMask js.Value
fnCompileShader js.Value
fnCreateBuffer js.Value
fnCreateFramebuffer js.Value
fnCreateProgram js.Value
fnCreateRenderbuffer js.Value
fnCreateShader js.Value
fnCreateTexture js.Value
fnCreateVertexArray js.Value
fnDeleteBuffer js.Value
fnDeleteFramebuffer js.Value
fnDeleteProgram js.Value
fnDeleteRenderbuffer js.Value
fnDeleteShader js.Value
fnDeleteTexture js.Value
fnDeleteVertexArray js.Value
fnDisable js.Value
fnDisableVertexAttribArray js.Value
fnDrawElements js.Value
fnEnable js.Value
fnEnableVertexAttribArray js.Value
fnFramebufferRenderbuffer js.Value
fnFramebufferTexture2D js.Value
fnFlush js.Value
fnGetError js.Value
fnGetParameter js.Value
fnGetProgramInfoLog js.Value
fnGetProgramParameter js.Value
fnGetShaderInfoLog js.Value
fnGetShaderParameter js.Value
fnGetUniformLocation js.Value
fnIsFramebuffer js.Value
fnIsProgram js.Value
fnIsRenderbuffer js.Value
fnLinkProgram js.Value
fnPixelStorei js.Value
fnReadPixels js.Value
fnRenderbufferStorage js.Value
fnScissor js.Value
fnShaderSource js.Value
fnStencilFunc js.Value
fnStencilMask js.Value
fnStencilOpSeparate js.Value
fnTexImage2D js.Value
fnTexSubImage2D js.Value
fnTexParameteri js.Value
fnUniform1fv js.Value
fnUniform1i js.Value
fnUniform1iv js.Value
fnUniform2fv js.Value
fnUniform2iv js.Value
fnUniform3fv js.Value
fnUniform3iv js.Value
fnUniform4fv js.Value
fnUniform4iv js.Value
fnUniformMatrix2fv js.Value
fnUniformMatrix3fv js.Value
fnUniformMatrix4fv js.Value
fnUseProgram js.Value
fnVertexAttribPointer js.Value
fnViewport js.Value
buffers values
framebuffers values
programs values
renderbuffers values
shaders values
textures values
vertexArrays values
uniformLocations map[uint32]*values
}
type values struct {
idToValue map[uint32]js.Value
lastID uint32
}
func (v *values) create(value js.Value) uint32 {
v.lastID++
id := v.lastID
if v.idToValue == nil {
v.idToValue = map[uint32]js.Value{}
}
v.idToValue[id] = value
return id
}
func (v *values) get(id uint32) js.Value {
return v.idToValue[id]
}
func (v *values) getID(value js.Value) (uint32, bool) {
for id, v := range v.idToValue {
if v.Equal(value) {
return id, true
}
}
return 0, false
}
func (v *values) getOrCreate(value js.Value) uint32 {
id, ok := v.getID(value)
if ok {
return id
}
return v.create(value)
}
func (v *values) delete(id uint32) {
delete(v.idToValue, id)
}
func NewDefaultContext(v js.Value) (Context, error) {
// Passing a Go string to the JS world is expensive. This causes conversion to UTF-16 (#1438).
// In order to reduce the cost when calling functions, create the function objects by bind and use them.
g := &defaultContext{
fnActiveTexture: v.Get("activeTexture").Call("bind", v),
fnAttachShader: v.Get("attachShader").Call("bind", v),
fnBindAttribLocation: v.Get("bindAttribLocation").Call("bind", v),
fnBindBuffer: v.Get("bindBuffer").Call("bind", v),
fnBindFramebuffer: v.Get("bindFramebuffer").Call("bind", v),
fnBindRenderbuffer: v.Get("bindRenderbuffer").Call("bind", v),
fnBindTexture: v.Get("bindTexture").Call("bind", v),
fnBindVertexArray: v.Get("bindVertexArray").Call("bind", v),
fnBlendEquationSeparate: v.Get("blendEquationSeparate").Call("bind", v),
fnBlendFuncSeparate: v.Get("blendFuncSeparate").Call("bind", v),
fnBufferData: v.Get("bufferData").Call("bind", v),
fnBufferSubData: v.Get("bufferSubData").Call("bind", v),
fnCheckFramebufferStatus: v.Get("checkFramebufferStatus").Call("bind", v),
fnClear: v.Get("clear").Call("bind", v),
fnColorMask: v.Get("colorMask").Call("bind", v),
fnCompileShader: v.Get("compileShader").Call("bind", v),
fnCreateBuffer: v.Get("createBuffer").Call("bind", v),
fnCreateFramebuffer: v.Get("createFramebuffer").Call("bind", v),
fnCreateProgram: v.Get("createProgram").Call("bind", v),
fnCreateRenderbuffer: v.Get("createRenderbuffer").Call("bind", v),
fnCreateShader: v.Get("createShader").Call("bind", v),
fnCreateTexture: v.Get("createTexture").Call("bind", v),
fnCreateVertexArray: v.Get("createVertexArray").Call("bind", v),
fnDeleteBuffer: v.Get("deleteBuffer").Call("bind", v),
fnDeleteFramebuffer: v.Get("deleteFramebuffer").Call("bind", v),
fnDeleteProgram: v.Get("deleteProgram").Call("bind", v),
fnDeleteRenderbuffer: v.Get("deleteRenderbuffer").Call("bind", v),
fnDeleteShader: v.Get("deleteShader").Call("bind", v),
fnDeleteTexture: v.Get("deleteTexture").Call("bind", v),
fnDeleteVertexArray: v.Get("deleteVertexArray").Call("bind", v),
fnDisable: v.Get("disable").Call("bind", v),
fnDisableVertexAttribArray: v.Get("disableVertexAttribArray").Call("bind", v),
fnDrawElements: v.Get("drawElements").Call("bind", v),
fnEnable: v.Get("enable").Call("bind", v),
fnEnableVertexAttribArray: v.Get("enableVertexAttribArray").Call("bind", v),
fnFramebufferRenderbuffer: v.Get("framebufferRenderbuffer").Call("bind", v),
fnFramebufferTexture2D: v.Get("framebufferTexture2D").Call("bind", v),
fnFlush: v.Get("flush").Call("bind", v),
fnGetError: v.Get("getError").Call("bind", v),
fnGetParameter: v.Get("getParameter").Call("bind", v),
fnGetProgramInfoLog: v.Get("getProgramInfoLog").Call("bind", v),
fnGetProgramParameter: v.Get("getProgramParameter").Call("bind", v),
fnGetShaderInfoLog: v.Get("getShaderInfoLog").Call("bind", v),
fnGetShaderParameter: v.Get("getShaderParameter").Call("bind", v),
fnGetUniformLocation: v.Get("getUniformLocation").Call("bind", v),
fnIsFramebuffer: v.Get("isFramebuffer").Call("bind", v),
fnIsProgram: v.Get("isProgram").Call("bind", v),
fnIsRenderbuffer: v.Get("isRenderbuffer").Call("bind", v),
fnLinkProgram: v.Get("linkProgram").Call("bind", v),
fnPixelStorei: v.Get("pixelStorei").Call("bind", v),
fnReadPixels: v.Get("readPixels").Call("bind", v),
fnRenderbufferStorage: v.Get("renderbufferStorage").Call("bind", v),
fnScissor: v.Get("scissor").Call("bind", v),
fnShaderSource: v.Get("shaderSource").Call("bind", v),
fnStencilFunc: v.Get("stencilFunc").Call("bind", v),
fnStencilMask: v.Get("stencilMask").Call("bind", v),
fnStencilOpSeparate: v.Get("stencilOpSeparate").Call("bind", v),
fnTexImage2D: v.Get("texImage2D").Call("bind", v),
fnTexSubImage2D: v.Get("texSubImage2D").Call("bind", v),
fnTexParameteri: v.Get("texParameteri").Call("bind", v),
fnUniform1fv: v.Get("uniform1fv").Call("bind", v),
fnUniform1i: v.Get("uniform1i").Call("bind", v),
fnUniform1iv: v.Get("uniform1iv").Call("bind", v),
fnUniform2fv: v.Get("uniform2fv").Call("bind", v),
fnUniform2iv: v.Get("uniform2iv").Call("bind", v),
fnUniform3fv: v.Get("uniform3fv").Call("bind", v),
fnUniform3iv: v.Get("uniform3iv").Call("bind", v),
fnUniform4fv: v.Get("uniform4fv").Call("bind", v),
fnUniform4iv: v.Get("uniform4iv").Call("bind", v),
fnUniformMatrix2fv: v.Get("uniformMatrix2fv").Call("bind", v),
fnUniformMatrix3fv: v.Get("uniformMatrix3fv").Call("bind", v),
fnUniformMatrix4fv: v.Get("uniformMatrix4fv").Call("bind", v),
fnUseProgram: v.Get("useProgram").Call("bind", v),
fnVertexAttribPointer: v.Get("vertexAttribPointer").Call("bind", v),
fnViewport: v.Get("viewport").Call("bind", v),
}
return g, nil
}
func (c *defaultContext) getUniformLocation(location int32) js.Value {
program := uint32(location) >> 5
return c.uniformLocations[program].get(uint32(location) & ((1 << 5) - 1))
}
func (c *defaultContext) LoadFunctions() error {
return nil
}
func (c *defaultContext) IsES() bool {
// WebGL is compatible with GLES.
return true
}
func (c *defaultContext) ActiveTexture(texture uint32) {
c.fnActiveTexture.Invoke(texture)
}
func (c *defaultContext) AttachShader(program uint32, shader uint32) {
c.fnAttachShader.Invoke(c.programs.get(program), c.shaders.get(shader))
}
func (c *defaultContext) BindAttribLocation(program uint32, index uint32, name string) {
c.fnBindAttribLocation.Invoke(c.programs.get(program), index, name)
}
func (c *defaultContext) BindBuffer(target uint32, buffer uint32) {
c.fnBindBuffer.Invoke(target, c.buffers.get(buffer))
}
func (c *defaultContext) BindFramebuffer(target uint32, framebuffer uint32) {
c.fnBindFramebuffer.Invoke(target, c.framebuffers.get(framebuffer))
}
func (c *defaultContext) BindRenderbuffer(target uint32, renderbuffer uint32) {
c.fnBindRenderbuffer.Invoke(target, c.renderbuffers.get(renderbuffer))
}
func (c *defaultContext) BindTexture(target uint32, texture uint32) {
c.fnBindTexture.Invoke(target, c.textures.get(texture))
}
func (c *defaultContext) BindVertexArray(array uint32) {
c.fnBindVertexArray.Invoke(c.vertexArrays.get(array))
}
func (c *defaultContext) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
c.fnBlendEquationSeparate.Invoke(modeRGB, modeAlpha)
}
func (c *defaultContext) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
c.fnBlendFuncSeparate.Invoke(srcRGB, dstRGB, srcAlpha, dstAlpha)
}
func (c *defaultContext) BufferInit(target uint32, size int, usage uint32) {
c.fnBufferData.Invoke(target, size, usage)
}
func (c *defaultContext) BufferSubData(target uint32, offset int, data []byte) {
l := len(data)
arr := jsutil.TemporaryUint8ArrayFromUint8Slice(l, data)
c.fnBufferSubData.Invoke(target, offset, arr, 0, l)
}
func (c *defaultContext) CheckFramebufferStatus(target uint32) uint32 {
return uint32(c.fnCheckFramebufferStatus.Invoke(target).Int())
}
func (c *defaultContext) Clear(mask uint32) {
c.fnClear.Invoke(mask)
}
func (c *defaultContext) ColorMask(red, green, blue, alpha bool) {
c.fnColorMask.Invoke(red, green, blue, alpha)
}
func (c *defaultContext) CompileShader(shader uint32) {
c.fnCompileShader.Invoke(c.shaders.get(shader))
}
func (c *defaultContext) CreateBuffer() uint32 {
return c.buffers.create(c.fnCreateBuffer.Invoke())
}
func (c *defaultContext) CreateFramebuffer() uint32 {
return c.framebuffers.create(c.fnCreateFramebuffer.Invoke())
}
func (c *defaultContext) CreateProgram() uint32 {
return c.programs.create(c.fnCreateProgram.Invoke())
}
func (c *defaultContext) CreateRenderbuffer() uint32 {
return c.renderbuffers.create(c.fnCreateRenderbuffer.Invoke())
}
func (c *defaultContext) CreateShader(xtype uint32) uint32 {
return c.shaders.create(c.fnCreateShader.Invoke(xtype))
}
func (c *defaultContext) CreateTexture() uint32 {
return c.textures.create(c.fnCreateTexture.Invoke())
}
func (c *defaultContext) CreateVertexArray() uint32 {
return c.vertexArrays.create(c.fnCreateVertexArray.Invoke())
}
func (c *defaultContext) DeleteBuffer(buffer uint32) {
c.fnDeleteBuffer.Invoke(c.buffers.get(buffer))
c.buffers.delete(buffer)
}
func (c *defaultContext) DeleteFramebuffer(framebuffer uint32) {
c.fnDeleteFramebuffer.Invoke(c.framebuffers.get(framebuffer))
c.framebuffers.delete(framebuffer)
}
func (c *defaultContext) DeleteProgram(program uint32) {
c.fnDeleteProgram.Invoke(c.programs.get(program))
c.programs.delete(program)
delete(c.uniformLocations, program)
}
func (c *defaultContext) DeleteRenderbuffer(renderbuffer uint32) {
c.fnDeleteRenderbuffer.Invoke(c.renderbuffers.get(renderbuffer))
c.renderbuffers.delete(renderbuffer)
}
func (c *defaultContext) DeleteShader(shader uint32) {
c.fnDeleteShader.Invoke(c.shaders.get(shader))
c.shaders.delete(shader)
}
func (c *defaultContext) DeleteTexture(texture uint32) {
c.fnDeleteTexture.Invoke(c.textures.get(texture))
c.textures.delete(texture)
}
func (c *defaultContext) DeleteVertexArray(array uint32) {
c.fnDeleteVertexArray.Invoke(c.vertexArrays.get(array))
c.textures.delete(array)
}
func (c *defaultContext) Disable(cap uint32) {
c.fnDisable.Invoke(cap)
}
func (c *defaultContext) DisableVertexAttribArray(index uint32) {
c.fnDisableVertexAttribArray.Invoke(index)
}
func (c *defaultContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) {
c.fnDrawElements.Invoke(mode, count, xtype, offset)
}
func (c *defaultContext) Enable(cap uint32) {
c.fnEnable.Invoke(cap)
}
func (c *defaultContext) EnableVertexAttribArray(index uint32) {
c.fnEnableVertexAttribArray.Invoke(index)
}
func (c *defaultContext) Flush() {
c.fnFlush.Invoke()
}
func (c *defaultContext) FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32) {
c.fnFramebufferRenderbuffer.Invoke(target, attachment, renderbuffertarget, c.renderbuffers.get(renderbuffer))
}
func (c *defaultContext) FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32) {
c.fnFramebufferTexture2D.Invoke(target, attachment, textarget, c.textures.get(texture), level)
}
func (c *defaultContext) GetError() uint32 {
return uint32(c.fnGetError.Invoke().Int())
}
func (c *defaultContext) GetInteger(pname uint32) int {
ret := c.fnGetParameter.Invoke(pname)
switch pname {
case FRAMEBUFFER_BINDING:
id, ok := c.framebuffers.getID(ret)
if !ok {
return 0
}
return int(id)
case MAX_TEXTURE_SIZE:
return ret.Int()
default:
panic(fmt.Sprintf("gl: unexpected pname at GetInteger: %d", pname))
}
}
func (c *defaultContext) GetProgramInfoLog(program uint32) string {
return c.fnGetProgramInfoLog.Invoke(c.programs.get(program)).String()
}
func (c *defaultContext) GetProgrami(program uint32, pname uint32) int {
v := c.fnGetProgramParameter.Invoke(c.programs.get(program), pname)
switch v.Type() {
case js.TypeNumber:
return v.Int()
case js.TypeBoolean:
if v.Bool() {
return TRUE
}
return FALSE
default:
panic(fmt.Sprintf("gl: unexpected return type at GetProgrami: %v", v))
}
}
func (c *defaultContext) GetShaderInfoLog(shader uint32) string {
return c.fnGetShaderInfoLog.Invoke(c.shaders.get(shader)).String()
}
func (c *defaultContext) GetShaderi(shader uint32, pname uint32) int {
v := c.fnGetShaderParameter.Invoke(c.shaders.get(shader), pname)
switch v.Type() {
case js.TypeNumber:
return v.Int()
case js.TypeBoolean:
if v.Bool() {
return TRUE
}
return FALSE
default:
panic(fmt.Sprintf("gl: unexpected return type at GetShaderi: %v", v))
}
}
func (c *defaultContext) GetUniformLocation(program uint32, name string) int32 {
location := c.fnGetUniformLocation.Invoke(c.programs.get(program), name)
if c.uniformLocations == nil {
c.uniformLocations = map[uint32]*values{}
}
vs, ok := c.uniformLocations[program]
if !ok {
vs = &values{}
c.uniformLocations[program] = vs
}
idx := vs.getOrCreate(location)
return int32((program << 5) | idx)
}
func (c *defaultContext) IsFramebuffer(framebuffer uint32) bool {
return c.fnIsFramebuffer.Invoke(c.framebuffers.get(framebuffer)).Bool()
}
func (c *defaultContext) IsProgram(program uint32) bool {
return c.fnIsProgram.Invoke(c.programs.get(program)).Bool()
}
func (c *defaultContext) IsRenderbuffer(renderbuffer uint32) bool {
return c.fnIsRenderbuffer.Invoke(c.renderbuffers.get(renderbuffer)).Bool()
}
func (c *defaultContext) LinkProgram(program uint32) {
c.fnLinkProgram.Invoke(c.programs.get(program))
}
func (c *defaultContext) PixelStorei(pname uint32, param int32) {
c.fnPixelStorei.Invoke(pname, param)
}
func (c *defaultContext) ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) {
if dst == nil {
c.fnReadPixels.Invoke(x, y, width, height, format, xtype, 0)
return
}
p := jsutil.TemporaryUint8ArrayFromUint8Slice(len(dst), nil)
c.fnReadPixels.Invoke(x, y, width, height, format, xtype, p)
js.CopyBytesToGo(dst, p)
}
func (c *defaultContext) RenderbufferStorage(target uint32, internalFormat uint32, width int32, height int32) {
c.fnRenderbufferStorage.Invoke(target, internalFormat, width, height)
}
func (c *defaultContext) Scissor(x, y, width, height int32) {
c.fnScissor.Invoke(x, y, width, height)
}
func (c *defaultContext) ShaderSource(shader uint32, xstring string) {
c.fnShaderSource.Invoke(c.shaders.get(shader), xstring)
}
func (c *defaultContext) StencilFunc(func_ uint32, ref int32, mask uint32) {
c.fnStencilFunc.Invoke(func_, ref, mask)
}
func (c *defaultContext) StencilOpSeparate(face, sfail, dpfail, dppass uint32) {
c.fnStencilOpSeparate.Invoke(face, sfail, dpfail, dppass)
}
func (c *defaultContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
if pixels != nil {
panic("gl: TexImage2D with non-nil pixels is not implemented")
}
c.fnTexImage2D.Invoke(target, level, internalformat, width, height, 0, format, xtype, nil)
}
func (c *defaultContext) TexParameteri(target uint32, pname uint32, param int32) {
c.fnTexParameteri.Invoke(target, pname, param)
}
func (c *defaultContext) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
arr := jsutil.TemporaryUint8ArrayFromUint8Slice(len(pixels), pixels)
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
// GLsizei width, GLsizei height,
// GLenum format, GLenum type, ArrayBufferView pixels, srcOffset);
c.fnTexSubImage2D.Invoke(target, level, xoffset, yoffset, width, height, format, xtype, arr, 0)
}
func (c *defaultContext) Uniform1fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
c.fnUniform1fv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform1i(location int32, v0 int32) {
l := c.getUniformLocation(location)
c.fnUniform1i.Invoke(l, v0)
}
func (c *defaultContext) Uniform1iv(location int32, value []int32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryInt32Array(len(value), value)
c.fnUniform1iv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform2fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
c.fnUniform2fv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform2iv(location int32, value []int32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryInt32Array(len(value), value)
c.fnUniform2iv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform3fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
c.fnUniform3fv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform3iv(location int32, value []int32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryInt32Array(len(value), value)
c.fnUniform3iv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform4fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
c.fnUniform4fv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) Uniform4iv(location int32, value []int32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryInt32Array(len(value), value)
c.fnUniform4iv.Invoke(l, arr, 0, len(value))
}
func (c *defaultContext) UniformMatrix2fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
c.fnUniformMatrix2fv.Invoke(l, false, arr, 0, len(value))
}
func (c *defaultContext) UniformMatrix3fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
c.fnUniformMatrix3fv.Invoke(l, false, arr, 0, len(value))
}
func (c *defaultContext) UniformMatrix4fv(location int32, value []float32) {
l := c.getUniformLocation(location)
arr := jsutil.TemporaryFloat32Array(len(value), value)
c.fnUniformMatrix4fv.Invoke(l, false, arr, 0, len(value))
}
func (c *defaultContext) UseProgram(program uint32) {
c.fnUseProgram.Invoke(c.programs.get(program))
}
func (c *defaultContext) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset int) {
c.fnVertexAttribPointer.Invoke(index, size, xtype, normalized, stride, offset)
}
func (c *defaultContext) Viewport(x int32, y int32, width int32, height int32) {
c.fnViewport.Invoke(x, y, width, height)
}
@@ -0,0 +1,569 @@
// 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 darwin || windows
package gl
import (
"runtime"
"unsafe"
"github.com/ebitengine/purego"
)
type defaultContext struct {
gpActiveTexture uintptr
gpAttachShader uintptr
gpBindAttribLocation uintptr
gpBindBuffer uintptr
gpBindFramebuffer uintptr
gpBindRenderbuffer uintptr
gpBindTexture uintptr
gpBindVertexArray uintptr
gpBlendEquationSeparate uintptr
gpBlendFuncSeparate uintptr
gpBufferData uintptr
gpBufferSubData uintptr
gpCheckFramebufferStatus uintptr
gpClear uintptr
gpColorMask uintptr
gpCompileShader uintptr
gpCreateProgram uintptr
gpCreateShader uintptr
gpDeleteBuffers uintptr
gpDeleteFramebuffers uintptr
gpDeleteProgram uintptr
gpDeleteRenderbuffers uintptr
gpDeleteShader uintptr
gpDeleteTextures uintptr
gpDeleteVertexArrays uintptr
gpDisable uintptr
gpDisableVertexAttribArray uintptr
gpDrawElements uintptr
gpEnable uintptr
gpEnableVertexAttribArray uintptr
gpFlush uintptr
gpFramebufferRenderbuffer uintptr
gpFramebufferTexture2D uintptr
gpGenBuffers uintptr
gpGenFramebuffers uintptr
gpGenRenderbuffers uintptr
gpGenTextures uintptr
gpGenVertexArrays uintptr
gpGetError uintptr
gpGetIntegerv uintptr
gpGetProgramInfoLog uintptr
gpGetProgramiv uintptr
gpGetShaderInfoLog uintptr
gpGetShaderiv uintptr
gpGetUniformLocation uintptr
gpIsFramebuffer uintptr
gpIsProgram uintptr
gpIsRenderbuffer uintptr
gpLinkProgram uintptr
gpPixelStorei uintptr
gpReadPixels uintptr
gpRenderbufferStorage uintptr
gpScissor uintptr
gpShaderSource uintptr
gpStencilFunc uintptr
gpStencilOpSeparate uintptr
gpTexImage2D uintptr
gpTexParameteri uintptr
gpTexSubImage2D uintptr
gpUniform1fv uintptr
gpUniform1i uintptr
gpUniform1iv uintptr
gpUniform2fv uintptr
gpUniform2iv uintptr
gpUniform3fv uintptr
gpUniform3iv uintptr
gpUniform4fv uintptr
gpUniform4iv uintptr
gpUniformMatrix2fv uintptr
gpUniformMatrix3fv uintptr
gpUniformMatrix4fv uintptr
gpUseProgram uintptr
gpVertexAttribPointer uintptr
gpViewport uintptr
isES bool
}
func NewDefaultContext() (Context, error) {
ctx := &defaultContext{}
if err := ctx.init(); err != nil {
return nil, err
}
return ctx, nil
}
func boolToInt(b bool) int {
if b {
return 1
}
return 0
}
func (c *defaultContext) IsES() bool {
return c.isES
}
func (c *defaultContext) ActiveTexture(texture uint32) {
purego.SyscallN(c.gpActiveTexture, uintptr(texture))
}
func (c *defaultContext) AttachShader(program uint32, shader uint32) {
purego.SyscallN(c.gpAttachShader, uintptr(program), uintptr(shader))
}
func (c *defaultContext) BindAttribLocation(program uint32, index uint32, name string) {
cname, free := cStr(name)
defer free()
purego.SyscallN(c.gpBindAttribLocation, uintptr(program), uintptr(index), uintptr(unsafe.Pointer(cname)))
}
func (c *defaultContext) BindBuffer(target uint32, buffer uint32) {
purego.SyscallN(c.gpBindBuffer, uintptr(target), uintptr(buffer))
}
func (c *defaultContext) BindFramebuffer(target uint32, framebuffer uint32) {
purego.SyscallN(c.gpBindFramebuffer, uintptr(target), uintptr(framebuffer))
}
func (c *defaultContext) BindRenderbuffer(target uint32, renderbuffer uint32) {
purego.SyscallN(c.gpBindRenderbuffer, uintptr(target), uintptr(renderbuffer))
}
func (c *defaultContext) BindTexture(target uint32, texture uint32) {
purego.SyscallN(c.gpBindTexture, uintptr(target), uintptr(texture))
}
func (c *defaultContext) BindVertexArray(array uint32) {
purego.SyscallN(c.gpBindVertexArray, uintptr(array))
}
func (c *defaultContext) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
purego.SyscallN(c.gpBlendEquationSeparate, uintptr(modeRGB), uintptr(modeAlpha))
}
func (c *defaultContext) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
purego.SyscallN(c.gpBlendFuncSeparate, uintptr(srcRGB), uintptr(dstRGB), uintptr(srcAlpha), uintptr(dstAlpha))
}
func (c *defaultContext) BufferInit(target uint32, size int, usage uint32) {
purego.SyscallN(c.gpBufferData, uintptr(target), uintptr(size), 0, uintptr(usage))
}
func (c *defaultContext) BufferSubData(target uint32, offset int, data []byte) {
purego.SyscallN(c.gpBufferSubData, uintptr(target), uintptr(offset), uintptr(len(data)), uintptr(unsafe.Pointer(&data[0])))
runtime.KeepAlive(data)
}
func (c *defaultContext) CheckFramebufferStatus(target uint32) uint32 {
ret, _, _ := purego.SyscallN(c.gpCheckFramebufferStatus, uintptr(target))
return uint32(ret)
}
func (c *defaultContext) Clear(mask uint32) {
purego.SyscallN(c.gpClear, uintptr(mask))
}
func (c *defaultContext) ColorMask(red bool, green bool, blue bool, alpha bool) {
purego.SyscallN(c.gpColorMask, uintptr(boolToInt(red)), uintptr(boolToInt(green)), uintptr(boolToInt(blue)), uintptr(boolToInt(alpha)))
}
func (c *defaultContext) CompileShader(shader uint32) {
purego.SyscallN(c.gpCompileShader, uintptr(shader))
}
func (c *defaultContext) CreateBuffer() uint32 {
var buffer uint32
purego.SyscallN(c.gpGenBuffers, 1, uintptr(unsafe.Pointer(&buffer)))
return buffer
}
func (c *defaultContext) CreateFramebuffer() uint32 {
var framebuffer uint32
purego.SyscallN(c.gpGenFramebuffers, 1, uintptr(unsafe.Pointer(&framebuffer)))
return framebuffer
}
func (c *defaultContext) CreateProgram() uint32 {
ret, _, _ := purego.SyscallN(c.gpCreateProgram)
return uint32(ret)
}
func (c *defaultContext) CreateRenderbuffer() uint32 {
var renderbuffer uint32
purego.SyscallN(c.gpGenRenderbuffers, 1, uintptr(unsafe.Pointer(&renderbuffer)))
return renderbuffer
}
func (c *defaultContext) CreateShader(xtype uint32) uint32 {
ret, _, _ := purego.SyscallN(c.gpCreateShader, uintptr(xtype))
return uint32(ret)
}
func (c *defaultContext) CreateTexture() uint32 {
var texture uint32
purego.SyscallN(c.gpGenTextures, 1, uintptr(unsafe.Pointer(&texture)))
return texture
}
func (c *defaultContext) CreateVertexArray() uint32 {
var array uint32
purego.SyscallN(c.gpGenVertexArrays, 1, uintptr(unsafe.Pointer(&array)))
return array
}
func (c *defaultContext) DeleteBuffer(buffer uint32) {
purego.SyscallN(c.gpDeleteBuffers, 1, uintptr(unsafe.Pointer(&buffer)))
}
func (c *defaultContext) DeleteFramebuffer(framebuffer uint32) {
purego.SyscallN(c.gpDeleteFramebuffers, 1, uintptr(unsafe.Pointer(&framebuffer)))
}
func (c *defaultContext) DeleteProgram(program uint32) {
purego.SyscallN(c.gpDeleteProgram, uintptr(program))
}
func (c *defaultContext) DeleteRenderbuffer(renderbuffer uint32) {
purego.SyscallN(c.gpDeleteRenderbuffers, 1, uintptr(unsafe.Pointer(&renderbuffer)))
}
func (c *defaultContext) DeleteShader(shader uint32) {
purego.SyscallN(c.gpDeleteShader, uintptr(shader))
}
func (c *defaultContext) DeleteTexture(texture uint32) {
purego.SyscallN(c.gpDeleteTextures, 1, uintptr(unsafe.Pointer(&texture)))
}
func (c *defaultContext) DeleteVertexArray(array uint32) {
purego.SyscallN(c.gpDeleteVertexArrays, 1, uintptr(unsafe.Pointer(&array)))
}
func (c *defaultContext) Disable(cap uint32) {
purego.SyscallN(c.gpDisable, uintptr(cap))
}
func (c *defaultContext) DisableVertexAttribArray(index uint32) {
purego.SyscallN(c.gpDisableVertexAttribArray, uintptr(index))
}
func (c *defaultContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) {
purego.SyscallN(c.gpDrawElements, uintptr(mode), uintptr(count), uintptr(xtype), uintptr(offset))
}
func (c *defaultContext) Enable(cap uint32) {
purego.SyscallN(c.gpEnable, uintptr(cap))
}
func (c *defaultContext) EnableVertexAttribArray(index uint32) {
purego.SyscallN(c.gpEnableVertexAttribArray, uintptr(index))
}
func (c *defaultContext) Flush() {
purego.SyscallN(c.gpFlush)
}
func (c *defaultContext) FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32) {
purego.SyscallN(c.gpFramebufferRenderbuffer, uintptr(target), uintptr(attachment), uintptr(renderbuffertarget), uintptr(renderbuffer))
}
func (c *defaultContext) FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32) {
purego.SyscallN(c.gpFramebufferTexture2D, uintptr(target), uintptr(attachment), uintptr(textarget), uintptr(texture), uintptr(level))
}
func (c *defaultContext) GetError() uint32 {
ret, _, _ := purego.SyscallN(c.gpGetError)
return uint32(ret)
}
func (c *defaultContext) GetInteger(pname uint32) int {
var dst int32
purego.SyscallN(c.gpGetIntegerv, uintptr(pname), uintptr(unsafe.Pointer(&dst)))
return int(dst)
}
func (c *defaultContext) GetProgramInfoLog(program uint32) string {
bufSize := c.GetProgrami(program, INFO_LOG_LENGTH)
infoLog := make([]byte, bufSize)
purego.SyscallN(c.gpGetProgramInfoLog, uintptr(program), uintptr(bufSize), 0, uintptr(unsafe.Pointer(&infoLog[0])))
return string(infoLog)
}
func (c *defaultContext) GetProgrami(program uint32, pname uint32) int {
var dst int32
purego.SyscallN(c.gpGetProgramiv, uintptr(program), uintptr(pname), uintptr(unsafe.Pointer(&dst)))
return int(dst)
}
func (c *defaultContext) GetShaderInfoLog(shader uint32) string {
bufSize := c.GetShaderi(shader, INFO_LOG_LENGTH)
infoLog := make([]byte, bufSize)
purego.SyscallN(c.gpGetShaderInfoLog, uintptr(shader), uintptr(bufSize), 0, uintptr(unsafe.Pointer(&infoLog[0])))
return string(infoLog)
}
func (c *defaultContext) GetShaderi(shader uint32, pname uint32) int {
var dst int32
purego.SyscallN(c.gpGetShaderiv, uintptr(shader), uintptr(pname), uintptr(unsafe.Pointer(&dst)))
return int(dst)
}
func (c *defaultContext) GetUniformLocation(program uint32, name string) int32 {
cname, free := cStr(name)
defer free()
ret, _, _ := purego.SyscallN(c.gpGetUniformLocation, uintptr(program), uintptr(unsafe.Pointer(cname)))
return int32(ret)
}
func (c *defaultContext) IsFramebuffer(framebuffer uint32) bool {
ret, _, _ := purego.SyscallN(c.gpIsFramebuffer, uintptr(framebuffer))
return byte(ret) != 0
}
func (c *defaultContext) IsProgram(program uint32) bool {
ret, _, _ := purego.SyscallN(c.gpIsProgram, uintptr(program))
return byte(ret) != 0
}
func (c *defaultContext) IsRenderbuffer(renderbuffer uint32) bool {
ret, _, _ := purego.SyscallN(c.gpIsRenderbuffer, uintptr(renderbuffer))
return byte(ret) != 0
}
func (c *defaultContext) LinkProgram(program uint32) {
purego.SyscallN(c.gpLinkProgram, uintptr(program))
}
func (c *defaultContext) PixelStorei(pname uint32, param int32) {
purego.SyscallN(c.gpPixelStorei, uintptr(pname), uintptr(param))
}
func (c *defaultContext) ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) {
purego.SyscallN(c.gpReadPixels, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(xtype), uintptr(unsafe.Pointer(&dst[0])))
}
func (c *defaultContext) RenderbufferStorage(target uint32, internalformat uint32, width int32, height int32) {
purego.SyscallN(c.gpRenderbufferStorage, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height))
}
func (c *defaultContext) Scissor(x int32, y int32, width int32, height int32) {
purego.SyscallN(c.gpScissor, uintptr(x), uintptr(y), uintptr(width), uintptr(height))
}
func (c *defaultContext) ShaderSource(shader uint32, xstring string) {
cstring, free := cStr(xstring)
defer free()
purego.SyscallN(c.gpShaderSource, uintptr(shader), 1, uintptr(unsafe.Pointer(&cstring)), 0)
}
func (c *defaultContext) StencilFunc(xfunc uint32, ref int32, mask uint32) {
purego.SyscallN(c.gpStencilFunc, uintptr(xfunc), uintptr(ref), uintptr(mask))
}
func (c *defaultContext) StencilOpSeparate(face uint32, fail uint32, zfail uint32, zpass uint32) {
purego.SyscallN(c.gpStencilOpSeparate, uintptr(face), uintptr(fail), uintptr(zfail), uintptr(zpass))
}
func (c *defaultContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
var ptr *byte
if len(pixels) > 0 {
ptr = &pixels[0]
}
purego.SyscallN(c.gpTexImage2D, uintptr(target), uintptr(level), uintptr(internalformat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(xtype), uintptr(unsafe.Pointer(ptr)))
runtime.KeepAlive(pixels)
}
func (c *defaultContext) TexParameteri(target uint32, pname uint32, param int32) {
purego.SyscallN(c.gpTexParameteri, uintptr(target), uintptr(pname), uintptr(param))
}
func (c *defaultContext) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
purego.SyscallN(c.gpTexSubImage2D, uintptr(target), uintptr(level), uintptr(xoffset), uintptr(yoffset), uintptr(width), uintptr(height), uintptr(format), uintptr(xtype), uintptr(unsafe.Pointer(&pixels[0])))
runtime.KeepAlive(pixels)
}
func (c *defaultContext) Uniform1fv(location int32, value []float32) {
purego.SyscallN(c.gpUniform1fv, uintptr(location), uintptr(len(value)), uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform1i(location int32, v0 int32) {
purego.SyscallN(c.gpUniform1i, uintptr(location), uintptr(v0))
}
func (c *defaultContext) Uniform1iv(location int32, value []int32) {
purego.SyscallN(c.gpUniform1iv, uintptr(location), uintptr(len(value)), uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform2fv(location int32, value []float32) {
purego.SyscallN(c.gpUniform2fv, uintptr(location), uintptr(len(value)/2), uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform2iv(location int32, value []int32) {
purego.SyscallN(c.gpUniform2iv, uintptr(location), uintptr(len(value)/2), uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform3fv(location int32, value []float32) {
purego.SyscallN(c.gpUniform3fv, uintptr(location), uintptr(len(value)/3), uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform3iv(location int32, value []int32) {
purego.SyscallN(c.gpUniform3iv, uintptr(location), uintptr(len(value)/3), uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform4fv(location int32, value []float32) {
purego.SyscallN(c.gpUniform4fv, uintptr(location), uintptr(len(value)/4), uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) Uniform4iv(location int32, value []int32) {
purego.SyscallN(c.gpUniform4iv, uintptr(location), uintptr(len(value)/4), uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) UniformMatrix2fv(location int32, value []float32) {
purego.SyscallN(c.gpUniformMatrix2fv, uintptr(location), uintptr(len(value)/4), 0, uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) UniformMatrix3fv(location int32, value []float32) {
purego.SyscallN(c.gpUniformMatrix3fv, uintptr(location), uintptr(len(value)/9), 0, uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) UniformMatrix4fv(location int32, value []float32) {
purego.SyscallN(c.gpUniformMatrix4fv, uintptr(location), uintptr(len(value)/16), 0, uintptr(unsafe.Pointer(&value[0])))
runtime.KeepAlive(value)
}
func (c *defaultContext) UseProgram(program uint32) {
purego.SyscallN(c.gpUseProgram, uintptr(program))
}
func (c *defaultContext) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset int) {
purego.SyscallN(c.gpVertexAttribPointer, uintptr(index), uintptr(size), uintptr(xtype), uintptr(boolToInt(normalized)), uintptr(stride), uintptr(offset))
}
func (c *defaultContext) Viewport(x int32, y int32, width int32, height int32) {
purego.SyscallN(c.gpViewport, uintptr(x), uintptr(y), uintptr(width), uintptr(height))
}
func (c *defaultContext) LoadFunctions() error {
g := procAddressGetter{ctx: c}
c.gpActiveTexture = g.get("glActiveTexture")
c.gpAttachShader = g.get("glAttachShader")
c.gpBindAttribLocation = g.get("glBindAttribLocation")
c.gpBindBuffer = g.get("glBindBuffer")
c.gpBindFramebuffer = g.get("glBindFramebuffer")
c.gpBindRenderbuffer = g.get("glBindRenderbuffer")
c.gpBindTexture = g.get("glBindTexture")
c.gpBindVertexArray = g.get("glBindVertexArray")
c.gpBlendEquationSeparate = g.get("glBlendEquationSeparate")
c.gpBlendFuncSeparate = g.get("glBlendFuncSeparate")
c.gpBufferData = g.get("glBufferData")
c.gpBufferSubData = g.get("glBufferSubData")
c.gpCheckFramebufferStatus = g.get("glCheckFramebufferStatus")
c.gpClear = g.get("glClear")
c.gpColorMask = g.get("glColorMask")
c.gpCompileShader = g.get("glCompileShader")
c.gpCreateProgram = g.get("glCreateProgram")
c.gpCreateShader = g.get("glCreateShader")
c.gpDeleteBuffers = g.get("glDeleteBuffers")
c.gpDeleteFramebuffers = g.get("glDeleteFramebuffers")
c.gpDeleteProgram = g.get("glDeleteProgram")
c.gpDeleteRenderbuffers = g.get("glDeleteRenderbuffers")
c.gpDeleteShader = g.get("glDeleteShader")
c.gpDeleteTextures = g.get("glDeleteTextures")
c.gpDeleteVertexArrays = g.get("glDeleteVertexArrays")
c.gpDisable = g.get("glDisable")
c.gpDisableVertexAttribArray = g.get("glDisableVertexAttribArray")
c.gpDrawElements = g.get("glDrawElements")
c.gpEnable = g.get("glEnable")
c.gpEnableVertexAttribArray = g.get("glEnableVertexAttribArray")
c.gpFlush = g.get("glFlush")
c.gpFramebufferRenderbuffer = g.get("glFramebufferRenderbuffer")
c.gpFramebufferTexture2D = g.get("glFramebufferTexture2D")
c.gpGenBuffers = g.get("glGenBuffers")
c.gpGenFramebuffers = g.get("glGenFramebuffers")
c.gpGenRenderbuffers = g.get("glGenRenderbuffers")
c.gpGenTextures = g.get("glGenTextures")
c.gpGenVertexArrays = g.get("glGenVertexArrays")
c.gpGetError = g.get("glGetError")
c.gpGetIntegerv = g.get("glGetIntegerv")
c.gpGetProgramInfoLog = g.get("glGetProgramInfoLog")
c.gpGetProgramiv = g.get("glGetProgramiv")
c.gpGetShaderInfoLog = g.get("glGetShaderInfoLog")
c.gpGetShaderiv = g.get("glGetShaderiv")
c.gpGetUniformLocation = g.get("glGetUniformLocation")
c.gpIsFramebuffer = g.get("glIsFramebuffer")
c.gpIsProgram = g.get("glIsProgram")
c.gpIsRenderbuffer = g.get("glIsRenderbuffer")
c.gpLinkProgram = g.get("glLinkProgram")
c.gpPixelStorei = g.get("glPixelStorei")
c.gpReadPixels = g.get("glReadPixels")
c.gpRenderbufferStorage = g.get("glRenderbufferStorage")
c.gpScissor = g.get("glScissor")
c.gpShaderSource = g.get("glShaderSource")
c.gpStencilFunc = g.get("glStencilFunc")
c.gpStencilOpSeparate = g.get("glStencilOpSeparate")
c.gpTexImage2D = g.get("glTexImage2D")
c.gpTexParameteri = g.get("glTexParameteri")
c.gpTexSubImage2D = g.get("glTexSubImage2D")
c.gpUniform1fv = g.get("glUniform1fv")
c.gpUniform1i = g.get("glUniform1i")
c.gpUniform1iv = g.get("glUniform1iv")
c.gpUniform2fv = g.get("glUniform2fv")
c.gpUniform2iv = g.get("glUniform2iv")
c.gpUniform3fv = g.get("glUniform3fv")
c.gpUniform3iv = g.get("glUniform3iv")
c.gpUniform4fv = g.get("glUniform4fv")
c.gpUniform4iv = g.get("glUniform4iv")
c.gpUniformMatrix2fv = g.get("glUniformMatrix2fv")
c.gpUniformMatrix3fv = g.get("glUniformMatrix3fv")
c.gpUniformMatrix4fv = g.get("glUniformMatrix4fv")
c.gpUseProgram = g.get("glUseProgram")
c.gpVertexAttribPointer = g.get("glVertexAttribPointer")
c.gpViewport = g.get("glViewport")
return g.error()
}
// cStr takes a Go string (with or without null-termination)
// and returns the C counterpart.
//
// The returned free function must be called once you are done using the string
// in order to free the memory.
func cStr(str string) (cstr *byte, free func()) {
bs := []byte(str)
if len(bs) == 0 || bs[len(bs)-1] != 0 {
bs = append(bs, 0)
}
return &bs[0], func() {
runtime.KeepAlive(bs)
bs = nil
}
}
@@ -0,0 +1,104 @@
// Copyright 2020 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 !playstation5
package gl
//go:generate go run gen.go
//go:generate gofmt -s -w .
// Context is a context for OpenGL (ES) functions.
//
// Context is basically the same as gomobile's gl.Context.
// See https://pkg.go.dev/github.com/ebitengine/gomobile/gl#Context
type Context interface {
LoadFunctions() error
IsES() bool
ActiveTexture(texture uint32)
AttachShader(program uint32, shader uint32)
BindAttribLocation(program uint32, index uint32, name string)
BindBuffer(target uint32, buffer uint32)
BindFramebuffer(target uint32, framebuffer uint32)
BindRenderbuffer(target uint32, renderbuffer uint32)
BindTexture(target uint32, texture uint32)
BindVertexArray(array uint32)
BlendEquationSeparate(modeRGB uint32, modeAlpha uint32)
BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32)
BufferInit(target uint32, size int, usage uint32)
BufferSubData(target uint32, offset int, data []byte)
CheckFramebufferStatus(target uint32) uint32
Clear(mask uint32)
ColorMask(red, green, blue, alpha bool)
CompileShader(shader uint32)
CreateBuffer() uint32
CreateFramebuffer() uint32
CreateProgram() uint32
CreateRenderbuffer() uint32
CreateShader(xtype uint32) uint32
CreateTexture() uint32
CreateVertexArray() uint32
DeleteBuffer(buffer uint32)
DeleteFramebuffer(framebuffer uint32)
DeleteProgram(program uint32)
DeleteRenderbuffer(renderbuffer uint32)
DeleteShader(shader uint32)
DeleteTexture(texture uint32)
DeleteVertexArray(array uint32)
Disable(cap uint32)
DisableVertexAttribArray(index uint32)
DrawElements(mode uint32, count int32, xtype uint32, offset int)
Enable(cap uint32)
EnableVertexAttribArray(index uint32)
Flush()
FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32)
FramebufferTexture2D(target uint32, attachment uint32, textarget uint32, texture uint32, level int32)
GetError() uint32
GetInteger(pname uint32) int
GetProgramInfoLog(program uint32) string
GetProgrami(program uint32, pname uint32) int
GetShaderInfoLog(shader uint32) string
GetShaderi(shader uint32, pname uint32) int
GetUniformLocation(program uint32, name string) int32
IsFramebuffer(framebuffer uint32) bool
IsProgram(program uint32) bool
IsRenderbuffer(renderbuffer uint32) bool
LinkProgram(program uint32)
PixelStorei(pname uint32, param int32)
ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32)
RenderbufferStorage(target uint32, internalFormat uint32, width int32, height int32)
Scissor(x, y, width, height int32)
ShaderSource(shader uint32, xstring string)
StencilFunc(func_ uint32, ref int32, mask uint32)
StencilOpSeparate(face, sfail, dpfail, dppass uint32)
TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte)
TexParameteri(target uint32, pname uint32, param int32)
TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte)
Uniform1fv(location int32, value []float32)
Uniform1i(location int32, v0 int32)
Uniform1iv(location int32, value []int32)
Uniform2fv(location int32, value []float32)
Uniform2iv(location int32, value []int32)
Uniform3fv(location int32, value []float32)
Uniform3iv(location int32, value []int32)
Uniform4fv(location int32, value []float32)
Uniform4iv(location int32, value []int32)
UniformMatrix2fv(location int32, value []float32)
UniformMatrix3fv(location int32, value []float32)
UniformMatrix4fv(location int32, value []float32)
UseProgram(program uint32)
VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset int)
Viewport(x int32, y int32, width int32, height int32)
}
@@ -0,0 +1,43 @@
// 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.
//go:build !js && !playstation5
package gl
import (
"fmt"
)
type procAddressGetter struct {
ctx *defaultContext
err error
}
func (p *procAddressGetter) get(name string) uintptr {
proc, err := p.ctx.getProcAddress(name)
if err != nil {
p.err = fmt.Errorf("gl: %s is missing: %w", name, err)
return 0
}
if proc == 0 {
p.err = fmt.Errorf("gl: %s is missing", name)
return 0
}
return proc
}
func (p *procAddressGetter) error() error {
return p.err
}
@@ -0,0 +1,51 @@
// 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.
package gl
import (
"fmt"
"github.com/ebitengine/purego"
)
var (
opengl uintptr
)
func (c *defaultContext) init() error {
lib, errGLES := purego.Dlopen("/System/Library/Frameworks/OpenGLES.framework/OpenGLES", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
if errGLES == nil {
c.isES = true
opengl = lib
return nil
}
lib, errGL := purego.Dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
if errGL == nil {
opengl = lib
return nil
}
// TODO: Use multiple %w-s as of Go 1.20
return fmt.Errorf("gl: failed to load: OpenGL.framework: %v, OpenGLES.framework: %v", errGL, errGLES)
}
func (c *defaultContext) getProcAddress(name string) (uintptr, error) {
proc, err := purego.Dlsym(opengl, name)
if err != nil {
return 0, err
}
return proc, nil
}
@@ -0,0 +1,114 @@
// 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 (freebsd || linux || netbsd || openbsd) && !nintendosdk && !playstation5
package gl
// #cgo LDFLAGS: -ldl
//
// #include <dlfcn.h>
// #include <stdlib.h>
//
// static void* getProcAddressGL(void* libGL, const char* name) {
// static void*(*glXGetProcAddress)(const char*);
// if (!glXGetProcAddress) {
// glXGetProcAddress = dlsym(libGL, "glXGetProcAddress");
// if (!glXGetProcAddress) {
// glXGetProcAddress = dlsym(libGL, "glXGetProcAddressARB");
// }
// }
// return glXGetProcAddress(name);
// }
//
// static void* getProcAddressGLES(void* libGLES, const char* name) {
// return dlsym(libGLES, name);
// }
import "C"
import (
"fmt"
"os"
"runtime"
"strings"
"unsafe"
)
var (
libGL unsafe.Pointer
libGLES unsafe.Pointer
)
func (c *defaultContext) init() error {
var preferES bool
if runtime.GOOS == "android" {
preferES = true
}
if !preferES {
for _, t := range strings.Split(os.Getenv("EBITENGINE_OPENGL"), ",") {
switch strings.TrimSpace(t) {
case "es":
preferES = true
break
}
}
}
// Try OpenGL first. OpenGL is preferable as this doesn't cause context losses.
if !preferES {
// Usually libGL.so or libGL.so.1 is used. libGL.so.2 might exist only on NetBSD.
for _, name := range []string{"libGL.so", "libGL.so.2", "libGL.so.1", "libGL.so.0"} {
cname := C.CString(name)
lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL)
C.free(unsafe.Pointer(cname))
if lib != nil {
libGL = lib
return nil
}
}
}
// Try OpenGL ES.
for _, name := range []string{"libGLESv2.so", "libGLESv2.so.2", "libGLESv2.so.1", "libGLESv2.so.0"} {
cname := C.CString(name)
lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL)
C.free(unsafe.Pointer(cname))
if lib != nil {
libGLES = lib
c.isES = true
return nil
}
}
return fmt.Errorf("gl: failed to load libGL.so and libGLESv2.so")
}
func (c *defaultContext) getProcAddress(name string) (uintptr, error) {
if c.isES {
return getProcAddressGLES(name), nil
}
return getProcAddressGL(name), nil
}
func getProcAddressGL(name string) uintptr {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return uintptr(C.getProcAddressGL(libGL, cname))
}
func getProcAddressGLES(name string) uintptr {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return uintptr(C.getProcAddressGLES(libGLES, cname))
}
@@ -0,0 +1,39 @@
// 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 nintendosdk
package gl
// #cgo LDFLAGS: -Wl,-unresolved-symbols=ignore-all
//
// #include <stdlib.h>
// #include <EGL/egl.h>
//
// static void* getProcAddress(const char* name) {
// return eglGetProcAddress(name);
// }
import "C"
import "unsafe"
func (c *defaultContext) init() error {
return nil
}
func (c *defaultContext) getProcAddress(name string) (uintptr, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
return uintptr(C.getProcAddress(cname)), nil
}
@@ -0,0 +1,52 @@
// 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.
package gl
import (
"fmt"
"unsafe"
"golang.org/x/sys/windows"
)
var (
opengl32 = windows.NewLazySystemDLL("opengl32")
procWglGetProcAddress = opengl32.NewProc("wglGetProcAddress")
)
func (c *defaultContext) init() error {
return nil
}
func (c *defaultContext) getProcAddress(namea string) (uintptr, error) {
cname, err := windows.BytePtrFromString(namea)
if err != nil {
return 0, err
}
r, _, err := procWglGetProcAddress.Call(uintptr(unsafe.Pointer(cname)))
if r != 0 {
return r, nil
}
if err != nil && err != windows.ERROR_SUCCESS && err != windows.ERROR_PROC_NOT_FOUND {
return 0, fmt.Errorf("gl: wglGetProcAddress failed for %s: %w", namea, err)
}
p := opengl32.NewProc(namea)
if err := p.Find(); err != nil {
return 0, err
}
return p.Addr(), nil
}
@@ -0,0 +1,341 @@
// Copyright 2018 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 !playstation5
package opengl
import (
"fmt"
"unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
type activatedTexture struct {
textureNative textureNative
index int
}
type Graphics struct {
state openGLState
context context
vsync bool
nextImageID graphicsdriver.ImageID
images map[graphicsdriver.ImageID]*Image
nextShaderID graphicsdriver.ShaderID
shaders map[graphicsdriver.ShaderID]*Shader
// drawCalled is true just after Draw is called. This holds true until WritePixels is called.
drawCalled bool
uniformVariableNameCache map[int]string
textureVariableNameCache map[int]string
uniformVars []uniformVariable
// activatedTextures is a set of activated textures.
// textureNative cannot be a map key unfortunately.
activatedTextures []activatedTexture
graphicsPlatform
}
func newGraphics(ctx gl.Context) *Graphics {
g := &Graphics{
vsync: true,
}
if isDebug {
g.context.ctx = &gl.DebugContext{Context: ctx}
} else {
g.context.ctx = ctx
}
return g
}
func (g *Graphics) Begin() error {
// Do nothing.
return nil
}
func (g *Graphics) End(present bool) error {
// Call glFlush to prevent black flicking (especially on Android (#226) and iOS).
// TODO: examples/sprites worked without this. Is this really needed?
g.context.ctx.Flush()
// The last uniforms must be reset before swapping the buffer (#2517).
if present {
g.state.resetLastUniforms()
if err := g.swapBuffers(); err != nil {
return err
}
}
return nil
}
func (g *Graphics) SetTransparent(transparent bool) {
// Do nothing.
}
func (g *Graphics) checkSize(width, height int) {
if width < 1 {
panic(fmt.Sprintf("opengl: width (%d) must be equal or more than %d", width, 1))
}
if height < 1 {
panic(fmt.Sprintf("opengl: height (%d) must be equal or more than %d", height, 1))
}
m := g.context.getMaxTextureSize()
if width > m {
panic(fmt.Sprintf("opengl: width (%d) must be less than or equal to %d", width, m))
}
if height > m {
panic(fmt.Sprintf("opengl: height (%d) must be less than or equal to %d", height, m))
}
}
func (g *Graphics) genNextImageID() graphicsdriver.ImageID {
g.nextImageID++
return g.nextImageID
}
func (g *Graphics) genNextShaderID() graphicsdriver.ShaderID {
g.nextShaderID++
return g.nextShaderID
}
func (g *Graphics) NewImage(width, height int) (graphicsdriver.Image, error) {
i := &Image{
id: g.genNextImageID(),
graphics: g,
width: width,
height: height,
}
w := graphics.InternalImageSize(width)
h := graphics.InternalImageSize(height)
g.checkSize(w, h)
t, err := g.context.newTexture(w, h)
if err != nil {
return nil, err
}
i.texture = t
g.addImage(i)
return i, nil
}
func (g *Graphics) NewScreenFramebufferImage(width, height int) (graphicsdriver.Image, error) {
g.checkSize(width, height)
i := &Image{
id: g.genNextImageID(),
graphics: g,
width: width,
height: height,
screen: true,
}
g.addImage(i)
return i, nil
}
func (g *Graphics) addImage(img *Image) {
if g.images == nil {
g.images = map[graphicsdriver.ImageID]*Image{}
}
if _, ok := g.images[img.id]; ok {
panic(fmt.Sprintf("opengl: image ID %d was already registered", img.id))
}
g.images[img.id] = img
}
func (g *Graphics) removeImage(img *Image) {
delete(g.images, img.id)
}
func (g *Graphics) Initialize() error {
if err := g.makeContextCurrent(); err != nil {
return err
}
if err := g.state.reset(&g.context); err != nil {
return err
}
return nil
}
// Reset resets or initializes the current OpenGL state.
func (g *Graphics) Reset() error {
return g.state.reset(&g.context)
}
func (g *Graphics) SetVertices(vertices []float32, indices []uint32) error {
g.state.setVertices(&g.context, vertices, indices)
return nil
}
func (g *Graphics) uniformVariableName(idx int) string {
if v, ok := g.uniformVariableNameCache[idx]; ok {
return v
}
if g.uniformVariableNameCache == nil {
g.uniformVariableNameCache = map[int]string{}
}
name := fmt.Sprintf("U%d", idx)
g.uniformVariableNameCache[idx] = name
return name
}
func (g *Graphics) 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 {
if shaderID == graphicsdriver.InvalidShaderID {
return fmt.Errorf("opengl: shader ID is invalid")
}
destination := g.images[dstID]
g.drawCalled = true
if err := destination.setViewport(); err != nil {
return err
}
g.context.blend(blend)
shader := g.shaders[shaderID]
program := shader.p
ulen := len(shader.ir.Uniforms)
if cap(g.uniformVars) < ulen {
g.uniformVars = make([]uniformVariable, ulen)
} else {
g.uniformVars = g.uniformVars[:ulen]
}
var idx int
for i, typ := range shader.ir.Uniforms {
n := typ.Uint32Count()
g.uniformVars[i].name = g.uniformVariableName(i)
g.uniformVars[i].value = uniforms[idx : idx+n]
g.uniformVars[i].typ = typ
idx += n
}
// In OpenGL, the NDC's Y direction is upward, so flip the Y direction for the final framebuffer.
if destination.screen {
const idx = graphics.ProjectionMatrixUniformVariableIndex
// Invert the sign bits as float32 values.
g.uniformVars[idx].value[1] ^= 1 << 31
g.uniformVars[idx].value[5] ^= 1 << 31
g.uniformVars[idx].value[9] ^= 1 << 31
g.uniformVars[idx].value[13] ^= 1 << 31
}
var imgs [graphics.ShaderImageCount]textureVariable
for i, srcID := range srcIDs {
if srcID == graphicsdriver.InvalidImageID {
continue
}
imgs[i].valid = true
imgs[i].native = g.images[srcID].texture
}
if err := g.useProgram(program, g.uniformVars, imgs); err != nil {
return err
}
for i := range g.uniformVars {
g.uniformVars[i] = uniformVariable{}
}
g.uniformVars = g.uniformVars[:0]
if fillRule != graphicsdriver.FillAll {
if err := destination.ensureStencilBuffer(); err != nil {
return err
}
g.context.ctx.Enable(gl.STENCIL_TEST)
}
for _, dstRegion := range dstRegions {
g.context.ctx.Scissor(
int32(dstRegion.Region.Min.X),
int32(dstRegion.Region.Min.Y),
int32(dstRegion.Region.Dx()),
int32(dstRegion.Region.Dy()),
)
switch fillRule {
case graphicsdriver.NonZero:
g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT)
g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff)
g.context.ctx.StencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP)
g.context.ctx.StencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP)
g.context.ctx.ColorMask(false, false, false, false)
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
case graphicsdriver.EvenOdd:
g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT)
g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff)
g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.INVERT)
g.context.ctx.ColorMask(false, false, false, false)
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
}
if fillRule != graphicsdriver.FillAll {
g.context.ctx.StencilFunc(gl.NOTEQUAL, 0x00, 0xff)
g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.KEEP)
g.context.ctx.ColorMask(true, true, true, true)
}
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
indexOffset += dstRegion.IndexCount
}
if fillRule != graphicsdriver.FillAll {
g.context.ctx.Disable(gl.STENCIL_TEST)
}
return nil
}
func (g *Graphics) SetVsyncEnabled(enabled bool) {
g.vsync = enabled
}
func (g *Graphics) NeedsClearingScreen() bool {
return true
}
func (g *Graphics) MaxImageSize() int {
return g.context.getMaxTextureSize()
}
func (g *Graphics) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) {
s, err := newShader(g.genNextShaderID(), g, program)
if err != nil {
return nil, err
}
g.addShader(s)
return s, nil
}
func (g *Graphics) addShader(shader *Shader) {
if g.shaders == nil {
g.shaders = map[graphicsdriver.ShaderID]*Shader{}
}
if _, ok := g.shaders[shader.id]; ok {
panic(fmt.Sprintf("opengl: shader ID %d was already registered", shader.id))
}
g.shaders[shader.id] = shader
}
func (g *Graphics) removeShader(shader *Shader) {
delete(g.shaders, shader.id)
}
@@ -0,0 +1,19 @@
// 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.
//go:build ebitenginegldebug
package opengl
const isDebug = true
@@ -0,0 +1,119 @@
// 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 !android && !ios && !js && !nintendosdk && !playstation5
package opengl
import (
"fmt"
"runtime"
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
"github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk"
)
type graphicsPlatform struct {
window *glfw.Window
}
// NewGraphics creates an implementation of graphicsdriver.Graphics for OpenGL.
// The returned graphics value is nil iff the error is not nil.
func NewGraphics() (graphicsdriver.Graphics, error) {
if microsoftgdk.IsXbox() {
return nil, fmt.Errorf("opengl: OpenGL is not supported on Xbox")
}
ctx, err := gl.NewDefaultContext()
if err != nil {
return nil, err
}
if err := setGLFWClientAPI(ctx.IsES()); err != nil {
return nil, err
}
return newGraphics(ctx), nil
}
func setGLFWClientAPI(isES bool) error {
if isES {
if err := glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLESAPI); err != nil {
return err
}
if err := glfw.WindowHint(glfw.ContextVersionMajor, 3); err != nil {
return err
}
if err := glfw.WindowHint(glfw.ContextVersionMinor, 0); err != nil {
return err
}
if err := glfw.WindowHint(glfw.ContextCreationAPI, glfw.EGLContextAPI); err != nil {
return err
}
return nil
}
if err := glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI); err != nil {
return err
}
if err := glfw.WindowHint(glfw.ContextVersionMajor, 3); err != nil {
return err
}
if err := glfw.WindowHint(glfw.ContextVersionMinor, 2); err != nil {
return err
}
// macOS requires forward-compatible and a core profile.
if runtime.GOOS == "darwin" {
if err := glfw.WindowHint(glfw.OpenGLForwardCompat, glfw.True); err != nil {
return err
}
if err := glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile); err != nil {
return err
}
}
return nil
}
func (g *Graphics) SetGLFWWindow(window *glfw.Window) {
g.window = window
}
func (g *Graphics) makeContextCurrent() error {
return g.window.MakeContextCurrent()
}
func (g *Graphics) swapBuffers() error {
// Call SwapIntervals even though vsync is not changed.
// When toggling to fullscreen, vsync state might be reset unexpectedly (#1787).
// SwapInterval is affected by the current monitor of the window.
// This needs to be called at least after SetMonitor.
// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
if g.vsync {
if err := glfw.SwapInterval(1); err != nil {
return err
}
} else {
if err := glfw.SwapInterval(0); err != nil {
return err
}
}
if err := g.window.SwapBuffers(); err != nil {
return err
}
return nil
}
@@ -0,0 +1,58 @@
// 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.
package opengl
import (
"fmt"
"syscall/js"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
)
type graphicsPlatform struct {
}
// NewGraphics creates an implementation of graphicsdriver.Graphics for OpenGL.
// The returned graphics value is nil iff the error is not nil.
func NewGraphics(canvas js.Value) (graphicsdriver.Graphics, error) {
var glContext js.Value
attr := js.Global().Get("Object").New()
attr.Set("alpha", true)
attr.Set("premultipliedAlpha", true)
attr.Set("stencil", true)
glContext = canvas.Call("getContext", "webgl2", attr)
if !glContext.Truthy() {
return nil, fmt.Errorf("opengl: getContext for webgl2 failed")
}
ctx, err := gl.NewDefaultContext(glContext)
if err != nil {
return nil, err
}
return newGraphics(ctx), nil
}
func (g *Graphics) makeContextCurrent() error {
return nil
}
func (g *Graphics) swapBuffers() error {
return nil
}
@@ -0,0 +1,43 @@
// Copyright 2018 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 android || ios
package opengl
import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
)
type graphicsPlatform struct {
}
// NewGraphics creates an implementation of graphicsdriver.Graphics for OpenGL.
// The returned graphics value is nil iff the error is not nil.
func NewGraphics() (graphicsdriver.Graphics, error) {
ctx, err := gl.NewDefaultContext()
if err != nil {
return nil, err
}
return newGraphics(ctx), nil
}
func (g *Graphics) makeContextCurrent() error {
return nil
}
func (g *Graphics) swapBuffers() error {
return nil
}
@@ -0,0 +1,49 @@
// 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.
//go:build nintendosdk
package opengl
import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
)
type graphicsPlatform struct {
egl *egl
}
func NewGraphics(nativeWindowType uintptr) (graphicsdriver.Graphics, error) {
ctx, err := gl.NewDefaultContext()
if err != nil {
return nil, err
}
g := newGraphics(ctx)
e, err := newEGL(nativeWindowType)
if err != nil {
return nil, err
}
g.egl = e
return g, nil
}
func (g *Graphics) makeContextCurrent() error {
return g.egl.makeContextCurrent()
}
func (g *Graphics) swapBuffers() error {
g.egl.swapBuffers()
return nil
}
@@ -0,0 +1,19 @@
// 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.
//go:build !ebitenginegldebug
package opengl
const isDebug = false
@@ -0,0 +1,158 @@
// Copyright 2018 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 !playstation5
package opengl
import (
"errors"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
)
type Image struct {
id graphicsdriver.ImageID
graphics *Graphics
texture textureNative
stencil renderbufferNative
framebuffer *framebuffer
width int
height int
screen bool
}
// framebuffer is a wrapper of OpenGL's framebuffer.
type framebuffer struct {
graphics *Graphics
native framebufferNative
width int
height int
}
func (i *Image) ID() graphicsdriver.ImageID {
return i.id
}
func (i *Image) Dispose() {
if i.framebuffer != nil {
i.graphics.context.deleteFramebuffer(i.framebuffer.native)
}
if i.texture != 0 {
i.graphics.context.deleteTexture(i.texture)
}
if i.stencil != 0 {
i.graphics.context.deleteRenderbuffer(i.stencil)
}
i.graphics.removeImage(i)
}
func (i *Image) setViewport() error {
if err := i.ensureFramebuffer(); err != nil {
return err
}
i.graphics.context.setViewport(i.framebuffer)
return nil
}
func (i *Image) ReadPixels(args []graphicsdriver.PixelsArgs) error {
if err := i.ensureFramebuffer(); err != nil {
return err
}
for _, arg := range args {
if err := i.graphics.context.framebufferPixels(arg.Pixels, i.framebuffer, arg.Region); err != nil {
return err
}
}
return nil
}
func (i *Image) framebufferSize() (int, int) {
if i.screen {
// The (default) framebuffer size can't be converted to a power of 2.
// On browsers, i.width and i.height are used as viewport size and
// Edge can't treat a bigger viewport than the drawing area (#71).
return i.width, i.height
}
return graphics.InternalImageSize(i.width), graphics.InternalImageSize(i.height)
}
func (i *Image) ensureFramebuffer() error {
if i.framebuffer != nil {
return nil
}
w, h := i.framebufferSize()
if i.screen {
i.framebuffer = i.graphics.context.newScreenFramebuffer(w, h)
return nil
}
f, err := i.graphics.context.newFramebuffer(i.texture, w, h)
if err != nil {
return err
}
i.framebuffer = f
return nil
}
func (i *Image) ensureStencilBuffer() error {
if i.stencil != 0 {
return nil
}
if err := i.ensureFramebuffer(); err != nil {
return err
}
r, err := i.graphics.context.newRenderbuffer(i.framebufferSize())
if err != nil {
return err
}
i.stencil = r
if err := i.graphics.context.bindStencilBuffer(i.framebuffer.native, i.stencil); err != nil {
return err
}
return nil
}
func (i *Image) WritePixels(args []graphicsdriver.PixelsArgs) error {
if i.screen {
return errors.New("opengl: WritePixels cannot be called on the screen")
}
if len(args) == 0 {
return nil
}
// glFlush is necessary on Android.
// glTexSubImage2D didn't work without this hack at least on Nexus 5x and NuAns NEO [Reloaded] (#211).
if i.graphics.drawCalled {
i.graphics.context.ctx.Flush()
}
i.graphics.drawCalled = false
i.graphics.context.bindTexture(i.texture)
for _, a := range args {
x := int32(a.Region.Min.X)
y := int32(a.Region.Min.Y)
width := int32(a.Region.Dx())
height := int32(a.Region.Dy())
i.graphics.context.ctx.TexSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, a.Pixels)
}
return nil
}
@@ -0,0 +1,43 @@
// Copyright 2014 Hajime Hoshi
//
// 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 !playstation5
package opengl
type locationCache struct {
uniformLocationCache map[program]map[string]uniformLocation
}
func newLocationCache() *locationCache {
return &locationCache{
uniformLocationCache: map[program]map[string]uniformLocation{},
}
}
func (c *locationCache) GetUniformLocation(context *context, p program, location string) uniformLocation {
if _, ok := c.uniformLocationCache[p]; !ok {
c.uniformLocationCache[p] = map[string]uniformLocation{}
}
l, ok := c.uniformLocationCache[p][location]
if !ok {
l = uniformLocation(context.ctx.GetUniformLocation(uint32(p), location))
c.uniformLocationCache[p][location] = l
}
return l
}
func (c *locationCache) deleteProgram(p program) {
delete(c.uniformLocationCache, p)
}
@@ -0,0 +1,344 @@
// Copyright 2014 Hajime Hoshi
//
// 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 !playstation5
package opengl
import (
"fmt"
"math"
"runtime"
"unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
const floatSizeInBytes = 4
// arrayBufferLayoutPart is a part of an array buffer layout.
type arrayBufferLayoutPart struct {
// TODO: This struct should belong to a program and know it.
name string
num int
}
// arrayBufferLayout is an array buffer layout.
//
// An array buffer in OpenGL is a buffer representing vertices and
// is passed to a vertex shader.
type arrayBufferLayout struct {
parts []arrayBufferLayoutPart
total int
}
func (a *arrayBufferLayout) names() []string {
ns := make([]string, len(a.parts))
for i, p := range a.parts {
ns[i] = p.name
}
return ns
}
// totalBytes returns the size in bytes for one element of the array buffer.
func (a *arrayBufferLayout) totalBytes() int {
if a.total != 0 {
return a.total
}
t := 0
for _, p := range a.parts {
t += floatSizeInBytes * p.num
}
a.total = t
return a.total
}
// enable starts using the array buffer.
func (a *arrayBufferLayout) enable(context *context) {
for i := range a.parts {
context.ctx.EnableVertexAttribArray(uint32(i))
}
total := a.totalBytes()
offset := 0
for i, p := range a.parts {
context.ctx.VertexAttribPointer(uint32(i), int32(p.num), gl.FLOAT, false, int32(total), offset)
offset += floatSizeInBytes * p.num
}
}
// disable stops using the array buffer.
func (a *arrayBufferLayout) disable(context *context) {
// TODO: Disabling should be done in reversed order?
for i := range a.parts {
context.ctx.DisableVertexAttribArray(uint32(i))
}
}
// theArrayBufferLayout is the array buffer layout for Ebitengine.
var theArrayBufferLayout = arrayBufferLayout{
// Note that GL_MAX_VERTEX_ATTRIBS is at least 16.
parts: []arrayBufferLayoutPart{
{
name: "A0",
num: 2,
},
{
name: "A1",
num: 2,
},
{
name: "A2",
num: 4,
},
},
}
func init() {
vertexFloatCount := theArrayBufferLayout.totalBytes() / floatSizeInBytes
if graphics.VertexFloatCount != vertexFloatCount {
panic(fmt.Sprintf("vertex float num must be %d but %d", graphics.VertexFloatCount, vertexFloatCount))
}
}
type openGLState struct {
vertexArray uint32
// arrayBuffer is OpenGL's array buffer (vertices data).
arrayBuffer buffer
arrayBufferSizeInBytes int
// elementArrayBuffer is OpenGL's element array buffer (indices data).
elementArrayBuffer buffer
elementArrayBufferSizeInBytes int
lastProgram program
lastUniforms map[string][]uint32
lastActiveTexture int
}
// reset resets or initializes the OpenGL state.
func (s *openGLState) reset(context *context) error {
if err := context.reset(); err != nil {
return err
}
s.lastProgram = 0
context.ctx.UseProgram(0)
for key := range s.lastUniforms {
delete(s.lastUniforms, key)
}
// On browsers (at least Chrome), buffers are already detached from the context
// and must not be deleted by DeleteBuffer.
if runtime.GOOS != "js" {
if s.arrayBuffer != 0 {
context.ctx.DeleteBuffer(uint32(s.arrayBuffer))
}
if s.elementArrayBuffer != 0 {
context.ctx.DeleteBuffer(uint32(s.elementArrayBuffer))
}
if s.vertexArray != 0 {
context.ctx.DeleteVertexArray(s.vertexArray)
}
}
s.arrayBuffer = 0
s.arrayBufferSizeInBytes = 0
s.elementArrayBuffer = 0
s.elementArrayBufferSizeInBytes = 0
s.vertexArray = 0
return nil
}
func pow2(x int) int {
if x > (math.MaxInt+1)/2 {
return math.MaxInt
}
p2 := 1
for p2 < x {
p2 *= 2
}
return p2
}
func (s *openGLState) setVertices(context *context, vertices []float32, indices []uint32) {
if s.vertexArray == 0 {
s.vertexArray = context.ctx.CreateVertexArray()
}
context.ctx.BindVertexArray(s.vertexArray)
if size := len(vertices) * int(unsafe.Sizeof(vertices[0])); s.arrayBufferSizeInBytes < size {
if s.arrayBuffer != 0 {
context.ctx.DeleteBuffer(uint32(s.arrayBuffer))
}
newSize := pow2(size)
// newArrayBuffer calls BindBuffer.
s.arrayBuffer = context.newArrayBuffer(newSize)
s.arrayBufferSizeInBytes = newSize
// Reenable the array buffer layout explicitly after resetting the array buffer.
theArrayBufferLayout.enable(context)
}
if size := len(indices) * int(unsafe.Sizeof(indices[0])); s.elementArrayBufferSizeInBytes < size {
if s.elementArrayBuffer != 0 {
context.ctx.DeleteBuffer(uint32(s.elementArrayBuffer))
}
newSize := pow2(size)
// newElementArrayBuffer calls BindBuffer.
s.elementArrayBuffer = context.newElementArrayBuffer(newSize)
s.elementArrayBufferSizeInBytes = newSize
}
// Note that the vertices and the indices passed to BufferSubData is not under GC management in the gl package.
vs := unsafe.Slice((*byte)(unsafe.Pointer(&vertices[0])), len(vertices)*int(unsafe.Sizeof(vertices[0])))
context.ctx.BufferSubData(gl.ARRAY_BUFFER, 0, vs)
is := unsafe.Slice((*byte)(unsafe.Pointer(&indices[0])), len(indices)*int(unsafe.Sizeof(indices[0])))
context.ctx.BufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, is)
}
func (s *openGLState) resetLastUniforms() {
for k := range s.lastUniforms {
delete(s.lastUniforms, k)
}
}
// areSameUint32Array returns a boolean indicating if a and b are deeply equal.
func areSameUint32Array(a, b []uint32) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
type uniformVariable struct {
name string
value []uint32
typ shaderir.Type
}
type textureVariable struct {
valid bool
native textureNative
}
func (g *Graphics) textureVariableName(idx int) string {
if v, ok := g.textureVariableNameCache[idx]; ok {
return v
}
if g.textureVariableNameCache == nil {
g.textureVariableNameCache = map[int]string{}
}
name := fmt.Sprintf("T%d", idx)
g.textureVariableNameCache[idx] = name
return name
}
// useProgram uses the program (programTexture).
func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textures [graphics.ShaderImageCount]textureVariable) error {
if g.state.lastProgram != program {
g.context.ctx.UseProgram(uint32(program))
g.state.lastProgram = program
for k := range g.state.lastUniforms {
delete(g.state.lastUniforms, k)
}
g.state.lastActiveTexture = 0
g.context.ctx.ActiveTexture(gl.TEXTURE0)
g.context.lastTexture = 0 // Make sure next bindTexture call actually does something.
}
for _, u := range uniforms {
if u.value == nil {
continue
}
if got, expected := len(u.value), u.typ.Uint32Count(); got != expected {
// Copy a shaderir.Type value once. Do not pass u.typ directly to fmt.Errorf arguments, or
// the value u would be allocated on heap.
typ := u.typ
return fmt.Errorf("opengl: length of a uniform variables %s (%s) doesn't match: expected %d but %d", u.name, typ.String(), expected, got)
}
cached, ok := g.state.lastUniforms[u.name]
if ok && areSameUint32Array(cached, u.value) {
continue
}
g.context.uniforms(program, u.name, u.value, u.typ)
if g.state.lastUniforms == nil {
g.state.lastUniforms = map[string][]uint32{}
}
g.state.lastUniforms[u.name] = u.value
}
var idx int
loop:
for i, t := range textures {
if !t.valid {
continue
}
// If the texture is already bound, set the texture variable to point to the texture.
// Rebinding the same texture seems problematic (#1193).
for _, at := range g.activatedTextures {
if t.native == at.textureNative {
g.context.uniformInt(program, g.textureVariableName(i), at.index)
continue loop
}
}
g.activatedTextures = append(g.activatedTextures, activatedTexture{
textureNative: t.native,
index: idx,
})
g.context.uniformInt(program, g.textureVariableName(i), idx)
if g.state.lastActiveTexture != idx {
g.context.ctx.ActiveTexture(uint32(gl.TEXTURE0 + idx))
g.state.lastActiveTexture = idx
g.context.lastTexture = 0 // Make sure next bindTexture call actually does something.
}
// Apparently, a texture must be bound every time. The cache is not used here.
g.context.bindTexture(t.native)
idx++
}
for i := range g.activatedTextures {
g.activatedTextures[i] = activatedTexture{}
}
g.activatedTextures = g.activatedTextures[:0]
return nil
}
func uint32sToFloat32s(s []uint32) []float32 {
return unsafe.Slice((*float32)(unsafe.Pointer(&s[0])), len(s))
}
func uint32sToInt32s(s []uint32) []int32 {
return unsafe.Slice((*int32)(unsafe.Pointer(&s[0])), len(s))
}
@@ -0,0 +1,79 @@
// Copyright 2020 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 !playstation5
package opengl
import (
"fmt"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl/gl"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/glsl"
)
type Shader struct {
id graphicsdriver.ShaderID
graphics *Graphics
ir *shaderir.Program
p program
}
func newShader(id graphicsdriver.ShaderID, graphics *Graphics, program *shaderir.Program) (*Shader, error) {
s := &Shader{
id: id,
graphics: graphics,
ir: program,
}
if err := s.compile(); err != nil {
return nil, err
}
return s, nil
}
func (s *Shader) ID() graphicsdriver.ShaderID {
return s.id
}
func (s *Shader) Dispose() {
s.graphics.context.deleteProgram(s.p)
s.graphics.removeShader(s)
}
func (s *Shader) compile() error {
vssrc, fssrc := glsl.Compile(s.ir, s.graphics.context.glslVersion())
vs, err := s.graphics.context.newShader(gl.VERTEX_SHADER, vssrc)
if err != nil {
return fmt.Errorf("opengl: vertex shader compile error: %v, source:\n%s", err, vssrc)
}
defer s.graphics.context.ctx.DeleteShader(uint32(vs))
fs, err := s.graphics.context.newShader(gl.FRAGMENT_SHADER, fssrc)
if err != nil {
return fmt.Errorf("opengl: fragment shader compile error: %v, source:\n%s", err, fssrc)
}
defer s.graphics.context.ctx.DeleteShader(uint32(fs))
p, err := s.graphics.context.newProgram([]shader{vs, fs}, theArrayBufferLayout.names())
if err != nil {
return err
}
s.p = p
return nil
}
@@ -0,0 +1,41 @@
// 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.
//go:build playstation5
// The actual implementation will be provided by -overlay.
#include "graphics_playstation5.h"
extern "C" ebitengine_Error ebitengine_InitializeGraphics(void) {
return {};
}
extern "C" ebitengine_Error ebitengine_NewImage(int* image, int width, int height) {
return {};
}
extern "C" ebitengine_Error ebitengine_NewScreenFramebufferImage(int* image, int width, int height) {
return {};
}
extern "C" void ebitengine_DisposeImage(int id) {
}
extern "C" ebitengine_Error ebitengine_NewShader(int* shader, const char* source) {
return {};
}
extern "C" void ebitengine_DisposeShader(int id) {
}
@@ -0,0 +1,155 @@
// 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.
//go:build playstation5
package playstation5
// #include "graphics_playstation5.h"
import "C"
import (
"fmt"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
)
type playstation5Error struct {
name string
code int
message string
}
func newPlaystation5Error(name string, err C.ebitengine_Error) *playstation5Error {
return &playstation5Error{
name: name,
code: int(err.code),
message: C.GoString(err.message),
}
}
func (e *playstation5Error) Error() string {
return fmt.Sprintf("playstation5: error at %s, code: %d, message: %s", e.name, e.code, e.message)
}
type Graphics struct {
}
func NewGraphics() (*Graphics, error) {
return &Graphics{}, nil
}
func (g *Graphics) Initialize() error {
if err := C.ebitengine_InitializeGraphics(); !C.ebitengine_IsErrorNil(&err) {
return newPlaystation5Error("(*playstation5.Graphics).Initialize", err)
}
return nil
}
func (g *Graphics) Begin() error {
return nil
}
func (g *Graphics) End(present bool) error {
return nil
}
func (g *Graphics) SetTransparent(transparent bool) {
}
func (g *Graphics) SetVertices(vertices []float32, indices []uint32) error {
return nil
}
func (g *Graphics) NewImage(width, height int) (graphicsdriver.Image, error) {
var id C.int
if err := C.ebitengine_NewImage(&id, C.int(width), C.int(height)); !C.ebitengine_IsErrorNil(&err) {
return nil, newPlaystation5Error("(*playstation5.Graphics).NewImage", err)
}
return &Image{
id: graphicsdriver.ImageID(id),
}, nil
}
func (g *Graphics) NewScreenFramebufferImage(width, height int) (graphicsdriver.Image, error) {
var id C.int
if err := C.ebitengine_NewScreenFramebufferImage(&id, C.int(width), C.int(height)); !C.ebitengine_IsErrorNil(&err) {
return nil, newPlaystation5Error("(*playstation5.Graphics).NewScreenFramebufferImage", err)
}
return &Image{
id: graphicsdriver.ImageID(id),
}, nil
}
func (g *Graphics) SetVsyncEnabled(enabled bool) {
}
func (g *Graphics) NeedsClearingScreen() bool {
return false
}
func (g *Graphics) MaxImageSize() int {
return 4096 // TODO: Get the value from the SDK.
}
func (g *Graphics) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) {
var id C.int
// TODO: Give a source code.
if err := C.ebitengine_NewShader(&id, nil); !C.ebitengine_IsErrorNil(&err) {
return nil, newPlaystation5Error("(*playstation5.Graphics).NewShader", err)
}
return &Shader{
id: graphicsdriver.ShaderID(id),
}, nil
}
func (g *Graphics) DrawTriangles(dst graphicsdriver.ImageID, srcs [graphics.ShaderImageCount]graphicsdriver.ImageID, shader graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
return nil
}
type Image struct {
id graphicsdriver.ImageID
}
func (i *Image) ID() graphicsdriver.ImageID {
return i.id
}
func (i *Image) Dispose() {
C.ebitengine_DisposeImage(C.int(i.id))
}
func (i *Image) ReadPixels(args []graphicsdriver.PixelsArgs) error {
// TODO: Implement this
return nil
}
func (i *Image) WritePixels(args []graphicsdriver.PixelsArgs) error {
// TODO: Implement this
return nil
}
type Shader struct {
id graphicsdriver.ShaderID
}
func (s *Shader) ID() graphicsdriver.ShaderID {
return s.id
}
func (s *Shader) Dispose() {
C.ebitengine_DisposeShader(C.int(s.id))
}
@@ -0,0 +1,48 @@
// 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.
//go:build playstation5
#ifndef EBITENGINE_INTERNAL_GRAPHICSDRIVER_PLAYSTATION5_GRAPHICS_PLAYSTATION5_H
#define EBITENGINE_INTERNAL_GRAPHICSDRIVER_PLAYSTATION5_GRAPHICS_PLAYSTATION5_H
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ebitengine_Error {
const char* message;
int code;
} ebitengine_Error;
static bool ebitengine_IsErrorNil(ebitengine_Error* err) {
return err->message == NULL && err->code == 0;
}
ebitengine_Error ebitengine_InitializeGraphics(void);
ebitengine_Error ebitengine_NewImage(int* image, int width, int height);
ebitengine_Error ebitengine_NewScreenFramebufferImage(int* image, int width, int height);
void ebitengine_DisposeImage(int id);
ebitengine_Error ebitengine_NewShader(int* shader, const char* source);
void ebitengine_DisposeShader(int id);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // EBITENGINE_INTERNAL_GRAPHICSDRIVER_PLAYSTATION5_GRAPHICS_PLAYSTATION5_H