mirror of https://github.com/encounter/aurora.git
Initial commit
This commit is contained in:
commit
9a725c89cf
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
ColumnLimit: 120
|
||||||
|
UseTab: Never
|
||||||
|
TabWidth: 2
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
PointerAlignment: Left
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
IndentCaseLabels: false
|
||||||
|
AllowShortBlocksOnASingleLine: Always
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BreakConstructorInitializersBeforeComma: true
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
NamespaceIndentation: None
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
SortIncludes: false
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
ConstructorInitializerIndentWidth: 0
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
|
@ -0,0 +1,8 @@
|
||||||
|
.buildcache/
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.vs/
|
||||||
|
build/
|
||||||
|
cmake-build-*/
|
||||||
|
CMakeUserPresets.json
|
||||||
|
out/
|
|
@ -0,0 +1,13 @@
|
||||||
|
[submodule "extern/dawn"]
|
||||||
|
path = extern/dawn
|
||||||
|
url = https://github.com/encounter/dawn-cmake.git
|
||||||
|
[submodule "extern/SDL"]
|
||||||
|
path = extern/SDL
|
||||||
|
url = https://github.com/encounter/SDL.git
|
||||||
|
branch = merged
|
||||||
|
[submodule "extern/imgui"]
|
||||||
|
path = extern/imgui
|
||||||
|
url = https://github.com/ocornut/imgui.git
|
||||||
|
[submodule "extern/fmt"]
|
||||||
|
path = extern/fmt
|
||||||
|
url = https://github.com/fmtlib/fmt.git
|
|
@ -0,0 +1,86 @@
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(aurora LANGUAGES C CXX)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
option(AURORA_NATIVE_MATRIX "Assume OpenGL-layout matrices, disables transposing" OFF)
|
||||||
|
|
||||||
|
add_subdirectory(extern)
|
||||||
|
add_library(aurora STATIC
|
||||||
|
lib/aurora.cpp
|
||||||
|
lib/webgpu/gpu.cpp
|
||||||
|
lib/imgui.cpp
|
||||||
|
lib/input.cpp
|
||||||
|
lib/window.cpp
|
||||||
|
lib/dawn/BackendBinding.cpp
|
||||||
|
lib/gfx/common.cpp
|
||||||
|
lib/gfx/texture.cpp
|
||||||
|
lib/gfx/gx.cpp
|
||||||
|
lib/gfx/gx_shader.cpp
|
||||||
|
lib/gfx/texture_convert.cpp
|
||||||
|
lib/gfx/stream/shader.cpp
|
||||||
|
lib/gfx/model/shader.cpp
|
||||||
|
lib/dolphin/GXBump.cpp
|
||||||
|
lib/dolphin/GXCull.cpp
|
||||||
|
lib/dolphin/GXDispList.cpp
|
||||||
|
lib/dolphin/GXDraw.cpp
|
||||||
|
lib/dolphin/GXExtra.cpp
|
||||||
|
lib/dolphin/GXFifo.cpp
|
||||||
|
lib/dolphin/GXFrameBuffer.cpp
|
||||||
|
lib/dolphin/GXGeometry.cpp
|
||||||
|
lib/dolphin/GXGet.cpp
|
||||||
|
lib/dolphin/GXLighting.cpp
|
||||||
|
lib/dolphin/GXManage.cpp
|
||||||
|
lib/dolphin/GXPerf.cpp
|
||||||
|
lib/dolphin/GXPixel.cpp
|
||||||
|
lib/dolphin/GXTev.cpp
|
||||||
|
lib/dolphin/GXTexture.cpp
|
||||||
|
lib/dolphin/GXTransform.cpp
|
||||||
|
lib/dolphin/GXVert.cpp
|
||||||
|
lib/dolphin/vi.cpp
|
||||||
|
)
|
||||||
|
add_library(aurora::aurora ALIAS aurora)
|
||||||
|
target_compile_definitions(aurora PUBLIC AURORA TARGET_PC)
|
||||||
|
if (AURORA_NATIVE_MATRIX)
|
||||||
|
target_compile_definitions(aurora PRIVATE AURORA_NATIVE_MATRIX)
|
||||||
|
endif ()
|
||||||
|
target_include_directories(aurora PUBLIC include)
|
||||||
|
target_include_directories(aurora PRIVATE ../imgui)
|
||||||
|
if (NOT TARGET SDL2::SDL2-static)
|
||||||
|
find_package(SDL2 REQUIRED)
|
||||||
|
endif ()
|
||||||
|
target_link_libraries(aurora PUBLIC SDL2::SDL2-static fmt::fmt imgui xxhash)
|
||||||
|
target_link_libraries(aurora PRIVATE dawn_native dawncpp webgpu_dawn absl::btree absl::flat_hash_map)
|
||||||
|
if (DAWN_ENABLE_VULKAN)
|
||||||
|
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_VULKAN)
|
||||||
|
target_sources(aurora PRIVATE lib/dawn/VulkanBinding.cpp)
|
||||||
|
endif ()
|
||||||
|
if (DAWN_ENABLE_METAL)
|
||||||
|
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_METAL)
|
||||||
|
target_sources(aurora PRIVATE lib/dawn/MetalBinding.mm)
|
||||||
|
set_source_files_properties(lib/dawn/MetalBinding.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
|
||||||
|
endif ()
|
||||||
|
if (DAWN_ENABLE_D3D12)
|
||||||
|
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_D3D12)
|
||||||
|
target_sources(aurora PRIVATE lib/dawn/D3D12Binding.cpp)
|
||||||
|
endif ()
|
||||||
|
if (DAWN_ENABLE_DESKTOP_GL OR DAWN_ENABLE_OPENGLES)
|
||||||
|
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_OPENGL)
|
||||||
|
if (DAWN_ENABLE_DESKTOP_GL)
|
||||||
|
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_DESKTOP_GL)
|
||||||
|
endif ()
|
||||||
|
if (DAWN_ENABLE_OPENGLES)
|
||||||
|
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_OPENGLES)
|
||||||
|
endif ()
|
||||||
|
target_sources(aurora PRIVATE lib/dawn/OpenGLBinding.cpp)
|
||||||
|
endif ()
|
||||||
|
if (DAWN_ENABLE_NULL)
|
||||||
|
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_NULL)
|
||||||
|
target_sources(aurora PRIVATE lib/dawn/NullBinding.cpp)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
add_library(aurora_main STATIC lib/main.cpp)
|
||||||
|
target_include_directories(aurora_main PUBLIC include)
|
||||||
|
target_link_libraries(aurora_main PUBLIC SDL2::SDL2main)
|
||||||
|
add_library(aurora::main ALIAS aurora_main)
|
|
@ -0,0 +1,266 @@
|
||||||
|
# GX API Support
|
||||||
|
|
||||||
|
- GXBump
|
||||||
|
- [x] GXSetNumIndStages
|
||||||
|
- [x] GXSetIndTexOrder
|
||||||
|
- [x] GXSetIndTexCoordScale
|
||||||
|
- [x] GXSetIndTexMtx
|
||||||
|
- [x] GXSetTevIndirect
|
||||||
|
- [x] GXSetTevDirect
|
||||||
|
- [x] GXSetTevIndWarp
|
||||||
|
- [ ] GXSetTevIndTile
|
||||||
|
- [ ] GXSetTevIndBumpST
|
||||||
|
- [ ] GXSetTevIndBumpXYZ
|
||||||
|
- [ ] GXSetTevIndRepeat
|
||||||
|
- GXCull
|
||||||
|
- [x] GXSetScissor
|
||||||
|
- [x] GXSetCullMode
|
||||||
|
- [ ] GXSetCoPlanar
|
||||||
|
- GXDispList
|
||||||
|
- [x] GXBeginDisplayList (stub)
|
||||||
|
- [x] GXEndDisplayList (stub)
|
||||||
|
- [x] GXCallDisplayList
|
||||||
|
- GXDraw
|
||||||
|
- [ ] GXDrawCylinder
|
||||||
|
- [ ] GXDrawTorus
|
||||||
|
- [ ] GXDrawSphere
|
||||||
|
- [ ] GXDrawCube
|
||||||
|
- [ ] GXDrawDodeca
|
||||||
|
- [ ] GXDrawOctahedron
|
||||||
|
- [ ] GXDrawIcosahedron
|
||||||
|
- [ ] GXDrawSphere1
|
||||||
|
- [ ] GXGenNormalTable
|
||||||
|
- GXFifo
|
||||||
|
- [x] GXGetGPStatus (stub)
|
||||||
|
- [ ] GXGetFifoStatus
|
||||||
|
- [x] GXGetFifoPtrs (stub)
|
||||||
|
- [x] GXGetCPUFifo (stub)
|
||||||
|
- [x] GXGetGPFifo (stub)
|
||||||
|
- [ ] GXGetFifoBase
|
||||||
|
- [ ] GXGetFifoSize
|
||||||
|
- [ ] GXGetFifoLimits
|
||||||
|
- [ ] GXSetBreakPtCallback
|
||||||
|
- [ ] GXEnableBreakPt
|
||||||
|
- [ ] GXDisableBreakPt
|
||||||
|
- [x] GXInitFifoBase (stub)
|
||||||
|
- [x] GXInitFifoPtrs (stub)
|
||||||
|
- [ ] GXInitFifoLimits
|
||||||
|
- [x] GXSetCPUFifo (stub)
|
||||||
|
- [x] GXSetGPFifo (stub)
|
||||||
|
- [x] GXSaveCPUFifo (stub)
|
||||||
|
- [ ] GXSaveGPFifo
|
||||||
|
- [ ] GXRedirectWriteGatherPipe
|
||||||
|
- [ ] GXRestoreWriteGatherPipe
|
||||||
|
- [ ] GXSetCurrentGXThread
|
||||||
|
- [ ] GXGetCurrentGXThread
|
||||||
|
- [ ] GXGetOverflowCount
|
||||||
|
- [ ] GXResetOverflowCount
|
||||||
|
- GXFrameBuffer
|
||||||
|
- [x] GXAdjustForOverscan
|
||||||
|
- [x] GXSetDispCopySrc (stub)
|
||||||
|
- [x] GXSetTexCopySrc
|
||||||
|
- [x] GXSetDispCopyDst (stub)
|
||||||
|
- [x] GXSetTexCopyDst
|
||||||
|
- [ ] GXSetDispCopyFrame2Field
|
||||||
|
- [ ] GXSetCopyClamp
|
||||||
|
- [x] GXSetDispCopyYScale (stub)
|
||||||
|
- [x] GXSetCopyClear
|
||||||
|
- [x] GXSetCopyFilter (stub)
|
||||||
|
- [x] GXSetDispCopyGamma (stub)
|
||||||
|
- [x] GXCopyDisp (stub)
|
||||||
|
- [x] GXCopyTex
|
||||||
|
- [ ] GXGetYScaleFactor
|
||||||
|
- [ ] GXGetNumXfbLines
|
||||||
|
- [ ] GXClearBoundingBox
|
||||||
|
- [ ] GXReadBoundingBox
|
||||||
|
- GXGeometry
|
||||||
|
- [x] GXSetVtxDesc
|
||||||
|
- [x] GXSetVtxDescv
|
||||||
|
- [x] GXClearVtxDesc
|
||||||
|
- [x] GXSetVtxAttrFmt
|
||||||
|
- [ ] GXSetVtxAttrFmtv
|
||||||
|
- [x] GXSetArray
|
||||||
|
- [x] GXBegin
|
||||||
|
- [x] GXEnd
|
||||||
|
- [x] GXSetTexCoordGen2
|
||||||
|
- [x] GXSetNumTexGens
|
||||||
|
- [ ] GXInvalidateVtxCache
|
||||||
|
- [ ] GXSetLineWidth
|
||||||
|
- [ ] GXSetPointSize
|
||||||
|
- [ ] GXEnableTexOffsets
|
||||||
|
- GXGet
|
||||||
|
- [ ] GXGetVtxDesc
|
||||||
|
- [ ] GXGetVtxDescv
|
||||||
|
- [ ] GXGetVtxAttrFmtv
|
||||||
|
- [ ] GXGetLineWidth
|
||||||
|
- [ ] GXGetPointSize
|
||||||
|
- [x] GXGetVtxAttrFmt
|
||||||
|
- [ ] GXGetViewportv
|
||||||
|
- [x] GXGetProjectionv
|
||||||
|
- [ ] GXGetScissor
|
||||||
|
- [ ] GXGetCullMode
|
||||||
|
- [x] GXGetLightAttnA
|
||||||
|
- [x] GXGetLightAttnK
|
||||||
|
- [x] GXGetLightPos
|
||||||
|
- [x] GXGetLightDir
|
||||||
|
- [x] GXGetLightColor
|
||||||
|
- [x] GXGetTexObjData
|
||||||
|
- [x] GXGetTexObjWidth
|
||||||
|
- [x] GXGetTexObjHeight
|
||||||
|
- [x] GXGetTexObjFmt
|
||||||
|
- [x] GXGetTexObjWrapS
|
||||||
|
- [x] GXGetTexObjWrapT
|
||||||
|
- [x] GXGetTexObjMipMap
|
||||||
|
- [ ] GXGetTexObjAll
|
||||||
|
- [ ] GXGetTexObjMinFilt
|
||||||
|
- [ ] GXGetTexObjMagFilt
|
||||||
|
- [ ] GXGetTexObjMinLOD
|
||||||
|
- [ ] GXGetTexObjMaxLOD
|
||||||
|
- [ ] GXGetTexObjLODBias
|
||||||
|
- [ ] GXGetTexObjBiasClamp
|
||||||
|
- [ ] GXGetTexObjEdgeLOD
|
||||||
|
- [ ] GXGetTexObjMaxAniso
|
||||||
|
- [ ] GXGetTexObjLODAll
|
||||||
|
- [ ] GXGetTexObjTlut
|
||||||
|
- [ ] GXGetTlutObjData
|
||||||
|
- [ ] GXGetTlutObjFmt
|
||||||
|
- [ ] GXGetTlutObjNumEntries
|
||||||
|
- [ ] GXGetTlutObjAll
|
||||||
|
- [ ] GXGetTexRegionAll
|
||||||
|
- [ ] GXGetTlutRegionAll
|
||||||
|
- GXLighting
|
||||||
|
- [x] GXInitLightAttn
|
||||||
|
- [x] GXInitLightAttnA
|
||||||
|
- [x] GXInitLightAttnK
|
||||||
|
- [x] GXInitLightSpot
|
||||||
|
- [x] GXInitLightDistAttn
|
||||||
|
- [x] GXInitLightPos
|
||||||
|
- [x] GXInitLightColor
|
||||||
|
- [x] GXLoadLightObjImm
|
||||||
|
- [ ] GXLoadLightObjIndx
|
||||||
|
- [x] GXSetChanAmbColor
|
||||||
|
- [x] GXSetChanMatColor
|
||||||
|
- [x] GXSetNumChans
|
||||||
|
- [x] GXInitLightDir
|
||||||
|
- [x] GXInitSpecularDir
|
||||||
|
- [x] GXInitSpecularDirHA
|
||||||
|
- [x] GXSetChanCtrl
|
||||||
|
- GXManage
|
||||||
|
- [x] GXInit (stub)
|
||||||
|
- [ ] GXAbortFrame
|
||||||
|
- [ ] GXSetDrawSync
|
||||||
|
- [ ] GXReadDrawSync
|
||||||
|
- [ ] GXSetDrawSyncCallback
|
||||||
|
- [x] GXDrawDone (stub)
|
||||||
|
- [x] GXSetDrawDone (stub)
|
||||||
|
- [ ] GXWaitDrawDone
|
||||||
|
- [x] GXSetDrawDoneCallback (stub)
|
||||||
|
- [ ] GXSetResetWritePipe
|
||||||
|
- [x] GXFlush (stub)
|
||||||
|
- [ ] GXResetWriteGatherPipe
|
||||||
|
- [x] GXPixModeSync (stub)
|
||||||
|
- [x] GXTexModeSync (stub)
|
||||||
|
- [ ] IsWriteGatherBufferEmpty
|
||||||
|
- [ ] GXSetMisc
|
||||||
|
- GXPerf
|
||||||
|
- [ ] GXSetGPMetric
|
||||||
|
- [ ] GXClearGPMetric
|
||||||
|
- [ ] GXReadGPMetric
|
||||||
|
- [ ] GXReadGP0Metric
|
||||||
|
- [ ] GXReadGP1Metric
|
||||||
|
- [ ] GXReadMemMetric
|
||||||
|
- [ ] GXClearMemMetric
|
||||||
|
- [ ] GXReadPixMetric
|
||||||
|
- [ ] GXClearPixMetric
|
||||||
|
- [ ] GXSetVCacheMetric
|
||||||
|
- [ ] GXReadVCacheMetric
|
||||||
|
- [ ] GXClearVCacheMetric
|
||||||
|
- [ ] GXReadXfRasMetric
|
||||||
|
- [ ] GXInitXfRasMetric
|
||||||
|
- [ ] GXReadClksPerVtx
|
||||||
|
- GXPixel
|
||||||
|
- [x] GXSetFog
|
||||||
|
- [x] GXSetFogColor
|
||||||
|
- [ ] GXInitFogAdjTable
|
||||||
|
- [ ] GXSetFogRangeAdj
|
||||||
|
- [x] GXSetBlendMode
|
||||||
|
- [x] GXSetColorUpdate
|
||||||
|
- [x] GXSetAlphaUpdate
|
||||||
|
- [x] GXSetZMode
|
||||||
|
- [ ] GXSetZCompLoc
|
||||||
|
- [x] GXSetPixelFmt (stub)
|
||||||
|
- [x] GXSetDither (stub)
|
||||||
|
- [x] GXSetDstAlpha
|
||||||
|
- [ ] GXSetFieldMask
|
||||||
|
- [ ] GXSetFieldMode
|
||||||
|
- GXTev
|
||||||
|
- [x] GXSetTevOp
|
||||||
|
- [x] GXSetTevColorIn
|
||||||
|
- [x] GXSetTevAlphaIn
|
||||||
|
- [x] GXSetTevColorOp
|
||||||
|
- [x] GXSetTevAlphaOp
|
||||||
|
- [x] GXSetTevColor
|
||||||
|
- [x] GXSetTevColorS10
|
||||||
|
- [x] GXSetAlphaCompare
|
||||||
|
- [x] GXSetTevOrder
|
||||||
|
- [ ] GXSetZTexture
|
||||||
|
- [x] GXSetNumTevStages
|
||||||
|
- [x] GXSetTevKColor
|
||||||
|
- [x] GXSetTevKColorSel
|
||||||
|
- [x] GXSetTevKAlphaSel
|
||||||
|
- [x] GXSetTevSwapMode
|
||||||
|
- [x] GXSetTevSwapModeTable
|
||||||
|
- GXTexture
|
||||||
|
- [x] GXInitTexObj
|
||||||
|
- [x] GXInitTexObjCI
|
||||||
|
- [x] GXInitTexObjLOD
|
||||||
|
- [x] GXInitTexObjData
|
||||||
|
- [x] GXInitTexObjWrapMode
|
||||||
|
- [x] GXInitTexObjTlut
|
||||||
|
- [ ] GXInitTexObjFilter
|
||||||
|
- [ ] GXInitTexObjMaxLOD
|
||||||
|
- [ ] GXInitTexObjMinLOD
|
||||||
|
- [ ] GXInitTexObjLODBias
|
||||||
|
- [ ] GXInitTexObjBiasClamp
|
||||||
|
- [ ] GXInitTexObjEdgeLOD
|
||||||
|
- [ ] GXInitTexObjMaxAniso
|
||||||
|
- [ ] GXInitTexObjUserData
|
||||||
|
- [ ] GXGetTexObjUserData
|
||||||
|
- [x] GXLoadTexObj
|
||||||
|
- [x] GXGetTexBufferSize
|
||||||
|
- [x] GXInitTlutObj
|
||||||
|
- [x] GXLoadTlut
|
||||||
|
- [ ] GXInitTexCacheRegion
|
||||||
|
- [ ] GXInitTexPreLoadRegion
|
||||||
|
- [ ] GXInitTlutRegion
|
||||||
|
- [ ] GXInvalidateTexRegion
|
||||||
|
- [x] GXInvalidateTexAll (stub)
|
||||||
|
- [ ] GXPreLoadEntireTexture
|
||||||
|
- [ ] GXSetTexRegionCallback
|
||||||
|
- [ ] GXSetTlutRegionCallback
|
||||||
|
- [ ] GXLoadTexObjPreLoaded
|
||||||
|
- [ ] GXSetTexCoordScaleManually
|
||||||
|
- [ ] GXSetTexCoordCylWrap
|
||||||
|
- [ ] GXSetTexCoordBias
|
||||||
|
- GXTransform
|
||||||
|
- [x] GXSetProjection
|
||||||
|
- [ ] GXSetProjectionv
|
||||||
|
- [x] GXLoadPosMtxImm
|
||||||
|
- [ ] GXLoadPosMtxIndx
|
||||||
|
- [x] GXLoadNrmMtxImm
|
||||||
|
- [ ] GXLoadNrmMtxImm3x3
|
||||||
|
- [ ] GXLoadNrmMtxIndx3x3
|
||||||
|
- [x] GXSetCurrentMtx
|
||||||
|
- [x] GXLoadTexMtxImm
|
||||||
|
- [ ] GXLoadTexMtxIndx
|
||||||
|
- [ ] GXProject
|
||||||
|
- [x] GXSetViewport
|
||||||
|
- [x] GXSetViewportJitter
|
||||||
|
- [ ] GXSetZScaleOffset
|
||||||
|
- [ ] GXSetScissorBoxOffset
|
||||||
|
- [ ] GXSetClipMode
|
||||||
|
- GXVert
|
||||||
|
- [x] GXPosition\[n]\[t]
|
||||||
|
- [x] GXNormal\[n]\[t]
|
||||||
|
- [x] GXColor\[n]\[t]
|
||||||
|
- [x] GXTexCoord\[n]\[t]
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Luke Street
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Aurora
|
||||||
|
|
||||||
|
Aurora is a source-level GameCube & Wii compatibility layer intended for use with game reverse engineering projects.
|
||||||
|
|
||||||
|
Originally developed for use in [Metaforce](https://github.com/AxioDL/metaforce), a Metroid Prime reverse engineering
|
||||||
|
project.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- GX compatibility layer
|
||||||
|
- Graphics API support: D3D12, Vulkan, Metal, OpenGL 4.4+ and OpenGL ES 3.1+
|
||||||
|
- *Planned: deko3d backend for Switch*
|
||||||
|
- Application layer using SDL
|
||||||
|
- Runs on Windows, Linux, macOS, iOS, tvOS (Android coming soon)
|
||||||
|
- Audio support with SDL_audio
|
||||||
|
- PAD compatibility layer
|
||||||
|
- Utilizes SDL_GameController for wide controller support, including GameCube controllers.
|
||||||
|
- *Planned: Wii remote support*
|
||||||
|
- [Dear ImGui](https://github.com/ocornut/imgui) built-in for UI
|
||||||
|
|
||||||
|
### GX
|
||||||
|
|
||||||
|
The GX compatibility layer is built on top of [WebGPU](https://www.w3.org/TR/webgpu/), a cross-platform graphics API
|
||||||
|
abstraction layer. WebGPU allows targeting all major platforms simultaneously with minimal overhead.
|
||||||
|
|
||||||
|
Currently, the WebGPU implementation used is Chromium's [Dawn](https://dawn.googlesource.com/dawn/).
|
||||||
|
|
||||||
|
See [GX API support](GX.md) for more information.
|
||||||
|
|
||||||
|
### PAD
|
||||||
|
|
||||||
|
The PAD compatibility layer utilizes SDL_GameController to automatically support & provide mappings for hundreds of
|
||||||
|
controllers across all platforms.
|
|
@ -0,0 +1,38 @@
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL Windows)
|
||||||
|
set(DAWN_ENABLE_DESKTOP_GL ON CACHE BOOL "Enable compilation of the OpenGL backend" FORCE)
|
||||||
|
endif ()
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||||
|
set(DAWN_ENABLE_OPENGLES ON CACHE BOOL "Enable compilation of the OpenGL ES backend" FORCE)
|
||||||
|
endif ()
|
||||||
|
add_subdirectory(dawn EXCLUDE_FROM_ALL)
|
||||||
|
if (DAWN_ENABLE_VULKAN)
|
||||||
|
target_compile_definitions(dawn_native PRIVATE
|
||||||
|
DAWN_ENABLE_VULKAN_VALIDATION_LAYERS
|
||||||
|
DAWN_VK_DATA_DIR="vulkandata")
|
||||||
|
endif ()
|
||||||
|
if (MSVC)
|
||||||
|
target_compile_options(dawn_native PRIVATE /bigobj)
|
||||||
|
else ()
|
||||||
|
target_compile_options(SPIRV-Tools-static PRIVATE -Wno-implicit-fallthrough)
|
||||||
|
target_compile_options(SPIRV-Tools-opt PRIVATE -Wno-implicit-fallthrough)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
set(SDL_LIBC ON CACHE BOOL "Use the system C library" FORCE)
|
||||||
|
endif ()
|
||||||
|
add_subdirectory(SDL EXCLUDE_FROM_ALL)
|
||||||
|
if (NOT MSVC)
|
||||||
|
target_compile_options(SDL2-static PRIVATE -Wno-implicit-fallthrough -Wno-shadow)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_subdirectory(xxhash EXCLUDE_FROM_ALL)
|
||||||
|
add_subdirectory(fmt EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
add_library(imgui
|
||||||
|
imgui/imgui.cpp
|
||||||
|
imgui/imgui_demo.cpp
|
||||||
|
imgui/imgui_draw.cpp
|
||||||
|
imgui/imgui_tables.cpp
|
||||||
|
imgui/imgui_widgets.cpp
|
||||||
|
imgui/misc/cpp/imgui_stdlib.cpp)
|
||||||
|
target_include_directories(imgui PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/imgui)
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 34458fe9f4a11a5b15ced6193e4c5d7ef97cca36
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 64a23ce0ede5f232cc209b69d64164ede6810b65
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 81f1cc74a776581cdef8659d176049d3aeb743c6
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit e99c4fc6688e218a0e5da50f56638aebab45da9b
|
|
@ -0,0 +1,3 @@
|
||||||
|
add_library(xxhash xxhash_impl.c)
|
||||||
|
target_include_directories(xxhash PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_compile_definitions(xxhash INTERFACE XXH_STATIC_LINKING_ONLY)
|
|
@ -0,0 +1,26 @@
|
||||||
|
xxHash Library
|
||||||
|
Copyright (c) 2012-2021 Yann Collet
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,770 @@
|
||||||
|
/*
|
||||||
|
* xxHash - Extremely Fast Hash algorithm
|
||||||
|
* Copyright (C) 2020-2021 Yann Collet
|
||||||
|
*
|
||||||
|
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* You can contact the author at:
|
||||||
|
* - xxHash homepage: https://www.xxhash.com
|
||||||
|
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @file xxh_x86dispatch.c
|
||||||
|
*
|
||||||
|
* Automatic dispatcher code for the @ref XXH3_family on x86-based targets.
|
||||||
|
*
|
||||||
|
* Optional add-on.
|
||||||
|
*
|
||||||
|
* **Compile this file with the default flags for your target.** Do not compile
|
||||||
|
* with flags like `-mavx*`, `-march=native`, or `/arch:AVX*`, there will be
|
||||||
|
* an error. See @ref XXH_X86DISPATCH_ALLOW_AVX for details.
|
||||||
|
*
|
||||||
|
* @defgroup dispatch x86 Dispatcher
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64))
|
||||||
|
# error "Dispatching is currently only supported on x86 and x86_64."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @def XXH_X86DISPATCH_ALLOW_AVX
|
||||||
|
* @brief Disables the AVX sanity check.
|
||||||
|
*
|
||||||
|
* Don't compile xxh_x86dispatch.c with options like `-mavx*`, `-march=native`,
|
||||||
|
* or `/arch:AVX*`. It is intended to be compiled for the minimum target, and
|
||||||
|
* it selectively enables SSE2, AVX2, and AVX512 when it is needed.
|
||||||
|
*
|
||||||
|
* Using this option _globally_ allows this feature, and therefore makes it
|
||||||
|
* undefined behavior to execute on any CPU without said feature.
|
||||||
|
*
|
||||||
|
* Even if the source code isn't directly using AVX intrinsics in a function,
|
||||||
|
* the compiler can still generate AVX code from autovectorization and by
|
||||||
|
* "upgrading" SSE2 intrinsics to use the VEX prefixes (a.k.a. AVX128).
|
||||||
|
*
|
||||||
|
* Use the same flags that you use to compile the rest of the program; this
|
||||||
|
* file will safely generate SSE2, AVX2, and AVX512 without these flags.
|
||||||
|
*
|
||||||
|
* Define XXH_X86DISPATCH_ALLOW_AVX to ignore this check, and feel free to open
|
||||||
|
* an issue if there is a target in the future where AVX is a default feature.
|
||||||
|
*/
|
||||||
|
#ifdef XXH_DOXYGEN
|
||||||
|
# define XXH_X86DISPATCH_ALLOW_AVX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__AVX__) && !defined(XXH_X86DISPATCH_ALLOW_AVX)
|
||||||
|
# error "Do not compile xxh_x86dispatch.c with AVX enabled! See the comment above."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
# define XXH_HAS_INCLUDE(header) __has_include(header)
|
||||||
|
#else
|
||||||
|
# define XXH_HAS_INCLUDE(header) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @def XXH_DISPATCH_SCALAR
|
||||||
|
* @brief Enables/dispatching the scalar code path.
|
||||||
|
*
|
||||||
|
* If this is defined to 0, SSE2 support is assumed. This reduces code size
|
||||||
|
* when the scalar path is not needed.
|
||||||
|
*
|
||||||
|
* This is automatically defined to 0 when...
|
||||||
|
* - SSE2 support is enabled in the compiler
|
||||||
|
* - Targeting x86_64
|
||||||
|
* - Targeting Android x86
|
||||||
|
* - Targeting macOS
|
||||||
|
*/
|
||||||
|
#ifndef XXH_DISPATCH_SCALAR
|
||||||
|
# if defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) /* SSE2 on by default */ \
|
||||||
|
|| defined(__x86_64__) || defined(_M_X64) /* x86_64 */ \
|
||||||
|
|| defined(__ANDROID__) || defined(__APPLEv__) /* Android or macOS */
|
||||||
|
# define XXH_DISPATCH_SCALAR 0 /* disable */
|
||||||
|
# else
|
||||||
|
# define XXH_DISPATCH_SCALAR 1
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
/*!
|
||||||
|
* @def XXH_DISPATCH_AVX2
|
||||||
|
* @brief Enables/disables dispatching for AVX2.
|
||||||
|
*
|
||||||
|
* This is automatically detected if it is not defined.
|
||||||
|
* - GCC 4.7 and later are known to support AVX2, but >4.9 is required for
|
||||||
|
* to get the AVX2 intrinsics and typedefs without -mavx -mavx2.
|
||||||
|
* - Visual Studio 2013 Update 2 and later are known to support AVX2.
|
||||||
|
* - The GCC/Clang internal header `<avx2intrin.h>` is detected. While this is
|
||||||
|
* not allowed to be included directly, it still appears in the builtin
|
||||||
|
* include path and is detectable with `__has_include`.
|
||||||
|
*
|
||||||
|
* @see XXH_AVX2
|
||||||
|
*/
|
||||||
|
#ifndef XXH_DISPATCH_AVX2
|
||||||
|
# if (defined(__GNUC__) && (__GNUC__ > 4)) /* GCC 5.0+ */ \
|
||||||
|
|| (defined(_MSC_VER) && _MSC_VER >= 1900) /* VS 2015+ */ \
|
||||||
|
|| (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 180030501) /* VS 2013 Update 2 */ \
|
||||||
|
|| XXH_HAS_INCLUDE(<avx2intrin.h>) /* GCC/Clang internal header */
|
||||||
|
# define XXH_DISPATCH_AVX2 1 /* enable dispatch towards AVX2 */
|
||||||
|
# else
|
||||||
|
# define XXH_DISPATCH_AVX2 0
|
||||||
|
# endif
|
||||||
|
#endif /* XXH_DISPATCH_AVX2 */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @def XXH_DISPATCH_AVX512
|
||||||
|
* @brief Enables/disables dispatching for AVX512.
|
||||||
|
*
|
||||||
|
* Automatically detected if one of the following conditions is met:
|
||||||
|
* - GCC 4.9 and later are known to support AVX512.
|
||||||
|
* - Visual Studio 2017 and later are known to support AVX2.
|
||||||
|
* - The GCC/Clang internal header `<avx512fintrin.h>` is detected. While this
|
||||||
|
* is not allowed to be included directly, it still appears in the builtin
|
||||||
|
* include path and is detectable with `__has_include`.
|
||||||
|
*
|
||||||
|
* @see XXH_AVX512
|
||||||
|
*/
|
||||||
|
#ifndef XXH_DISPATCH_AVX512
|
||||||
|
# if (defined(__GNUC__) \
|
||||||
|
&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))) /* GCC 4.9+ */ \
|
||||||
|
|| (defined(_MSC_VER) && _MSC_VER >= 1910) /* VS 2017+ */ \
|
||||||
|
|| XXH_HAS_INCLUDE(<avx512fintrin.h>) /* GCC/Clang internal header */
|
||||||
|
# define XXH_DISPATCH_AVX512 1 /* enable dispatch towards AVX512 */
|
||||||
|
# else
|
||||||
|
# define XXH_DISPATCH_AVX512 0
|
||||||
|
# endif
|
||||||
|
#endif /* XXH_DISPATCH_AVX512 */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @def XXH_TARGET_SSE2
|
||||||
|
* @brief Allows a function to be compiled with SSE2 intrinsics.
|
||||||
|
*
|
||||||
|
* Uses `__attribute__((__target__("sse2")))` on GCC to allow SSE2 to be used
|
||||||
|
* even with `-mno-sse2`.
|
||||||
|
*
|
||||||
|
* @def XXH_TARGET_AVX2
|
||||||
|
* @brief Like @ref XXH_TARGET_SSE2, but for AVX2.
|
||||||
|
*
|
||||||
|
* @def XXH_TARGET_AVX512
|
||||||
|
* @brief Like @ref XXH_TARGET_SSE2, but for AVX512.
|
||||||
|
*/
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
# include <emmintrin.h> /* SSE2 */
|
||||||
|
# if XXH_DISPATCH_AVX2 || XXH_DISPATCH_AVX512
|
||||||
|
# include <immintrin.h> /* AVX2, AVX512F */
|
||||||
|
# endif
|
||||||
|
# define XXH_TARGET_SSE2 __attribute__((__target__("sse2")))
|
||||||
|
# define XXH_TARGET_AVX2 __attribute__((__target__("avx2")))
|
||||||
|
# define XXH_TARGET_AVX512 __attribute__((__target__("avx512f")))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# include <intrin.h>
|
||||||
|
# define XXH_TARGET_SSE2
|
||||||
|
# define XXH_TARGET_AVX2
|
||||||
|
# define XXH_TARGET_AVX512
|
||||||
|
#else
|
||||||
|
# error "Dispatching is currently not supported for your compiler."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef XXH_DISPATCH_DEBUG
|
||||||
|
/* debug logging */
|
||||||
|
# include <stdio.h>
|
||||||
|
# define XXH_debugPrint(str) { fprintf(stderr, "DEBUG: xxHash dispatch: %s \n", str); fflush(NULL); }
|
||||||
|
#else
|
||||||
|
# define XXH_debugPrint(str) ((void)0)
|
||||||
|
# undef NDEBUG /* avoid redefinition */
|
||||||
|
# define NDEBUG
|
||||||
|
#endif
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define XXH_INLINE_ALL
|
||||||
|
#define XXH_X86DISPATCH
|
||||||
|
#include "xxhash.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Support both AT&T and Intel dialects
|
||||||
|
*
|
||||||
|
* GCC doesn't convert AT&T syntax to Intel syntax, and will error out if
|
||||||
|
* compiled with -masm=intel. Instead, it supports dialect switching with
|
||||||
|
* curly braces: { AT&T syntax | Intel syntax }
|
||||||
|
*
|
||||||
|
* Clang's integrated assembler automatically converts AT&T syntax to Intel if
|
||||||
|
* needed, making the dialect switching useless (it isn't even supported).
|
||||||
|
*
|
||||||
|
* Note: Comments are written in the inline assembly itself.
|
||||||
|
*/
|
||||||
|
#ifdef __clang__
|
||||||
|
# define XXH_I_ATT(intel, att) att "\n\t"
|
||||||
|
#else
|
||||||
|
# define XXH_I_ATT(intel, att) "{" att "|" intel "}\n\t"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @internal
|
||||||
|
* @brief Runs CPUID.
|
||||||
|
*
|
||||||
|
* @param eax , ecx The parameters to pass to CPUID, %eax and %ecx respectively.
|
||||||
|
* @param abcd The array to store the result in, `{ eax, ebx, ecx, edx }`
|
||||||
|
*/
|
||||||
|
static void XXH_cpuid(xxh_u32 eax, xxh_u32 ecx, xxh_u32* abcd)
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
__cpuidex(abcd, eax, ecx);
|
||||||
|
#else
|
||||||
|
xxh_u32 ebx, edx;
|
||||||
|
# if defined(__i386__) && defined(__PIC__)
|
||||||
|
__asm__(
|
||||||
|
"# Call CPUID\n\t"
|
||||||
|
"#\n\t"
|
||||||
|
"# On 32-bit x86 with PIC enabled, we are not allowed to overwrite\n\t"
|
||||||
|
"# EBX, so we use EDI instead.\n\t"
|
||||||
|
XXH_I_ATT("mov edi, ebx", "movl %%ebx, %%edi")
|
||||||
|
XXH_I_ATT("cpuid", "cpuid" )
|
||||||
|
XXH_I_ATT("xchg edi, ebx", "xchgl %%ebx, %%edi")
|
||||||
|
: "=D" (ebx),
|
||||||
|
# else
|
||||||
|
__asm__(
|
||||||
|
"# Call CPUID\n\t"
|
||||||
|
XXH_I_ATT("cpuid", "cpuid")
|
||||||
|
: "=b" (ebx),
|
||||||
|
# endif
|
||||||
|
"+a" (eax), "+c" (ecx), "=d" (edx));
|
||||||
|
abcd[0] = eax;
|
||||||
|
abcd[1] = ebx;
|
||||||
|
abcd[2] = ecx;
|
||||||
|
abcd[3] = edx;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Modified version of Intel's guide
|
||||||
|
* https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if XXH_DISPATCH_AVX2 || XXH_DISPATCH_AVX512
|
||||||
|
/*!
|
||||||
|
* @internal
|
||||||
|
* @brief Runs `XGETBV`.
|
||||||
|
*
|
||||||
|
* While the CPU may support AVX2, the operating system might not properly save
|
||||||
|
* the full YMM/ZMM registers.
|
||||||
|
*
|
||||||
|
* xgetbv is used for detecting this: Any compliant operating system will define
|
||||||
|
* a set of flags in the xcr0 register indicating how it saves the AVX registers.
|
||||||
|
*
|
||||||
|
* You can manually disable this flag on Windows by running, as admin:
|
||||||
|
*
|
||||||
|
* bcdedit.exe /set xsavedisable 1
|
||||||
|
*
|
||||||
|
* and rebooting. Run the same command with 0 to re-enable it.
|
||||||
|
*/
|
||||||
|
static xxh_u64 XXH_xgetbv(void)
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
return _xgetbv(0); /* min VS2010 SP1 compiler is required */
|
||||||
|
#else
|
||||||
|
xxh_u32 xcr0_lo, xcr0_hi;
|
||||||
|
__asm__(
|
||||||
|
"# Call XGETBV\n\t"
|
||||||
|
"#\n\t"
|
||||||
|
"# Older assemblers (e.g. macOS's ancient GAS version) don't support\n\t"
|
||||||
|
"# the XGETBV opcode, so we encode it by hand instead.\n\t"
|
||||||
|
"# See <https://github.com/asmjit/asmjit/issues/78> for details.\n\t"
|
||||||
|
".byte 0x0f, 0x01, 0xd0\n\t"
|
||||||
|
: "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0));
|
||||||
|
return xcr0_lo | ((xxh_u64)xcr0_hi << 32);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define XXH_SSE2_CPUID_MASK (1 << 26)
|
||||||
|
#define XXH_OSXSAVE_CPUID_MASK ((1 << 26) | (1 << 27))
|
||||||
|
#define XXH_AVX2_CPUID_MASK (1 << 5)
|
||||||
|
#define XXH_AVX2_XGETBV_MASK ((1 << 2) | (1 << 1))
|
||||||
|
#define XXH_AVX512F_CPUID_MASK (1 << 16)
|
||||||
|
#define XXH_AVX512F_XGETBV_MASK ((7 << 5) | (1 << 2) | (1 << 1))
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @internal
|
||||||
|
* @brief Returns the best XXH3 implementation.
|
||||||
|
*
|
||||||
|
* Runs various CPUID/XGETBV tests to try and determine the best implementation.
|
||||||
|
*
|
||||||
|
* @return The best @ref XXH_VECTOR implementation.
|
||||||
|
* @see XXH_VECTOR_TYPES
|
||||||
|
*/
|
||||||
|
static int XXH_featureTest(void)
|
||||||
|
{
|
||||||
|
xxh_u32 abcd[4];
|
||||||
|
xxh_u32 max_leaves;
|
||||||
|
int best = XXH_SCALAR;
|
||||||
|
#if XXH_DISPATCH_AVX2 || XXH_DISPATCH_AVX512
|
||||||
|
xxh_u64 xgetbv_val;
|
||||||
|
#endif
|
||||||
|
#if defined(__GNUC__) && defined(__i386__)
|
||||||
|
xxh_u32 cpuid_supported;
|
||||||
|
__asm__(
|
||||||
|
"# For the sake of ruthless backwards compatibility, check if CPUID\n\t"
|
||||||
|
"# is supported in the EFLAGS on i386.\n\t"
|
||||||
|
"# This is not necessary on x86_64 - CPUID is mandatory.\n\t"
|
||||||
|
"# The ID flag (bit 21) in the EFLAGS register indicates support\n\t"
|
||||||
|
"# for the CPUID instruction. If a software procedure can set and\n\t"
|
||||||
|
"# clear this flag, the processor executing the procedure supports\n\t"
|
||||||
|
"# the CPUID instruction.\n\t"
|
||||||
|
"# <https://c9x.me/x86/html/file_module_x86_id_45.html>\n\t"
|
||||||
|
"#\n\t"
|
||||||
|
"# Routine is from <https://wiki.osdev.org/CPUID>.\n\t"
|
||||||
|
|
||||||
|
"# Save EFLAGS\n\t"
|
||||||
|
XXH_I_ATT("pushfd", "pushfl" )
|
||||||
|
"# Store EFLAGS\n\t"
|
||||||
|
XXH_I_ATT("pushfd", "pushfl" )
|
||||||
|
"# Invert the ID bit in stored EFLAGS\n\t"
|
||||||
|
XXH_I_ATT("xor dword ptr[esp], 0x200000", "xorl $0x200000, (%%esp)")
|
||||||
|
"# Load stored EFLAGS (with ID bit inverted)\n\t"
|
||||||
|
XXH_I_ATT("popfd", "popfl" )
|
||||||
|
"# Store EFLAGS again (ID bit may or not be inverted)\n\t"
|
||||||
|
XXH_I_ATT("pushfd", "pushfl" )
|
||||||
|
"# eax = modified EFLAGS (ID bit may or may not be inverted)\n\t"
|
||||||
|
XXH_I_ATT("pop eax", "popl %%eax" )
|
||||||
|
"# eax = whichever bits were changed\n\t"
|
||||||
|
XXH_I_ATT("xor eax, dword ptr[esp]", "xorl (%%esp), %%eax" )
|
||||||
|
"# Restore original EFLAGS\n\t"
|
||||||
|
XXH_I_ATT("popfd", "popfl" )
|
||||||
|
"# eax = zero if ID bit can't be changed, else non-zero\n\t"
|
||||||
|
XXH_I_ATT("and eax, 0x200000", "andl $0x200000, %%eax" )
|
||||||
|
: "=a" (cpuid_supported) :: "cc");
|
||||||
|
|
||||||
|
if (XXH_unlikely(!cpuid_supported)) {
|
||||||
|
XXH_debugPrint("CPUID support is not detected!");
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
/* Check how many CPUID pages we have */
|
||||||
|
XXH_cpuid(0, 0, abcd);
|
||||||
|
max_leaves = abcd[0];
|
||||||
|
|
||||||
|
/* Shouldn't happen on hardware, but happens on some QEMU configs. */
|
||||||
|
if (XXH_unlikely(max_leaves == 0)) {
|
||||||
|
XXH_debugPrint("Max CPUID leaves == 0!");
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for SSE2, OSXSAVE and xgetbv */
|
||||||
|
XXH_cpuid(1, 0, abcd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test for SSE2. The check is redundant on x86_64, but it doesn't hurt.
|
||||||
|
*/
|
||||||
|
if (XXH_unlikely((abcd[3] & XXH_SSE2_CPUID_MASK) != XXH_SSE2_CPUID_MASK))
|
||||||
|
return best;
|
||||||
|
|
||||||
|
XXH_debugPrint("SSE2 support detected.");
|
||||||
|
|
||||||
|
best = XXH_SSE2;
|
||||||
|
#if XXH_DISPATCH_AVX2 || XXH_DISPATCH_AVX512
|
||||||
|
/* Make sure we have enough leaves */
|
||||||
|
if (XXH_unlikely(max_leaves < 7))
|
||||||
|
return best;
|
||||||
|
|
||||||
|
/* Test for OSXSAVE and XGETBV */
|
||||||
|
if ((abcd[2] & XXH_OSXSAVE_CPUID_MASK) != XXH_OSXSAVE_CPUID_MASK)
|
||||||
|
return best;
|
||||||
|
|
||||||
|
/* CPUID check for AVX features */
|
||||||
|
XXH_cpuid(7, 0, abcd);
|
||||||
|
|
||||||
|
xgetbv_val = XXH_xgetbv();
|
||||||
|
#if XXH_DISPATCH_AVX2
|
||||||
|
/* Validate that AVX2 is supported by the CPU */
|
||||||
|
if ((abcd[1] & XXH_AVX2_CPUID_MASK) != XXH_AVX2_CPUID_MASK)
|
||||||
|
return best;
|
||||||
|
|
||||||
|
/* Validate that the OS supports YMM registers */
|
||||||
|
if ((xgetbv_val & XXH_AVX2_XGETBV_MASK) != XXH_AVX2_XGETBV_MASK) {
|
||||||
|
XXH_debugPrint("AVX2 supported by the CPU, but not the OS.");
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AVX2 supported */
|
||||||
|
XXH_debugPrint("AVX2 support detected.");
|
||||||
|
best = XXH_AVX2;
|
||||||
|
#endif
|
||||||
|
#if XXH_DISPATCH_AVX512
|
||||||
|
/* Check if AVX512F is supported by the CPU */
|
||||||
|
if ((abcd[1] & XXH_AVX512F_CPUID_MASK) != XXH_AVX512F_CPUID_MASK) {
|
||||||
|
XXH_debugPrint("AVX512F not supported by CPU");
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate that the OS supports ZMM registers */
|
||||||
|
if ((xgetbv_val & XXH_AVX512F_XGETBV_MASK) != XXH_AVX512F_XGETBV_MASK) {
|
||||||
|
XXH_debugPrint("AVX512F supported by the CPU, but not the OS.");
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AVX512F supported */
|
||||||
|
XXH_debugPrint("AVX512F support detected.");
|
||||||
|
best = XXH_AVX512;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* === Vector implementations === */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @internal
|
||||||
|
* @brief Defines the various dispatch functions.
|
||||||
|
*
|
||||||
|
* TODO: Consolidate?
|
||||||
|
*
|
||||||
|
* @param suffix The suffix for the functions, e.g. sse2 or scalar
|
||||||
|
* @param target XXH_TARGET_* or empty.
|
||||||
|
*/
|
||||||
|
#define XXH_DEFINE_DISPATCH_FUNCS(suffix, target) \
|
||||||
|
\
|
||||||
|
/* === XXH3, default variants === */ \
|
||||||
|
\
|
||||||
|
XXH_NO_INLINE target XXH64_hash_t \
|
||||||
|
XXHL64_default_##suffix(const void* XXH_RESTRICT input, size_t len) \
|
||||||
|
{ \
|
||||||
|
return XXH3_hashLong_64b_internal( \
|
||||||
|
input, len, XXH3_kSecret, sizeof(XXH3_kSecret), \
|
||||||
|
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix \
|
||||||
|
); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* === XXH3, Seeded variants === */ \
|
||||||
|
\
|
||||||
|
XXH_NO_INLINE target XXH64_hash_t \
|
||||||
|
XXHL64_seed_##suffix(const void* XXH_RESTRICT input, size_t len, \
|
||||||
|
XXH64_hash_t seed) \
|
||||||
|
{ \
|
||||||
|
return XXH3_hashLong_64b_withSeed_internal( \
|
||||||
|
input, len, seed, XXH3_accumulate_512_##suffix, \
|
||||||
|
XXH3_scrambleAcc_##suffix, XXH3_initCustomSecret_##suffix \
|
||||||
|
); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* === XXH3, Secret variants === */ \
|
||||||
|
\
|
||||||
|
XXH_NO_INLINE target XXH64_hash_t \
|
||||||
|
XXHL64_secret_##suffix(const void* XXH_RESTRICT input, size_t len, \
|
||||||
|
const void* secret, size_t secretLen) \
|
||||||
|
{ \
|
||||||
|
return XXH3_hashLong_64b_internal( \
|
||||||
|
input, len, secret, secretLen, \
|
||||||
|
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix \
|
||||||
|
); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* === XXH3 update variants === */ \
|
||||||
|
\
|
||||||
|
XXH_NO_INLINE target XXH_errorcode \
|
||||||
|
XXH3_update_##suffix(XXH3_state_t* state, const void* input, size_t len) \
|
||||||
|
{ \
|
||||||
|
return XXH3_update(state, (const xxh_u8*)input, len, \
|
||||||
|
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* === XXH128 default variants === */ \
|
||||||
|
\
|
||||||
|
XXH_NO_INLINE target XXH128_hash_t \
|
||||||
|
XXHL128_default_##suffix(const void* XXH_RESTRICT input, size_t len) \
|
||||||
|
{ \
|
||||||
|
return XXH3_hashLong_128b_internal( \
|
||||||
|
input, len, XXH3_kSecret, sizeof(XXH3_kSecret), \
|
||||||
|
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix \
|
||||||
|
); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* === XXH128 Secret variants === */ \
|
||||||
|
\
|
||||||
|
XXH_NO_INLINE target XXH128_hash_t \
|
||||||
|
XXHL128_secret_##suffix(const void* XXH_RESTRICT input, size_t len, \
|
||||||
|
const void* XXH_RESTRICT secret, size_t secretLen) \
|
||||||
|
{ \
|
||||||
|
return XXH3_hashLong_128b_internal( \
|
||||||
|
input, len, (const xxh_u8*)secret, secretLen, \
|
||||||
|
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* === XXH128 Seeded variants === */ \
|
||||||
|
\
|
||||||
|
XXH_NO_INLINE target XXH128_hash_t \
|
||||||
|
XXHL128_seed_##suffix(const void* XXH_RESTRICT input, size_t len, \
|
||||||
|
XXH64_hash_t seed) \
|
||||||
|
{ \
|
||||||
|
return XXH3_hashLong_128b_withSeed_internal(input, len, seed, \
|
||||||
|
XXH3_accumulate_512_##suffix, XXH3_scrambleAcc_##suffix, \
|
||||||
|
XXH3_initCustomSecret_##suffix); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End XXH_DEFINE_DISPATCH_FUNCS */
|
||||||
|
|
||||||
|
#if XXH_DISPATCH_SCALAR
|
||||||
|
XXH_DEFINE_DISPATCH_FUNCS(scalar, /* nothing */)
|
||||||
|
#endif
|
||||||
|
XXH_DEFINE_DISPATCH_FUNCS(sse2, XXH_TARGET_SSE2)
|
||||||
|
#if XXH_DISPATCH_AVX2
|
||||||
|
XXH_DEFINE_DISPATCH_FUNCS(avx2, XXH_TARGET_AVX2)
|
||||||
|
#endif
|
||||||
|
#if XXH_DISPATCH_AVX512
|
||||||
|
XXH_DEFINE_DISPATCH_FUNCS(avx512, XXH_TARGET_AVX512)
|
||||||
|
#endif
|
||||||
|
#undef XXH_DEFINE_DISPATCH_FUNCS
|
||||||
|
|
||||||
|
/* ==== Dispatchers ==== */
|
||||||
|
|
||||||
|
typedef XXH64_hash_t (*XXH3_dispatchx86_hashLong64_default)(const void* XXH_RESTRICT, size_t);
|
||||||
|
|
||||||
|
typedef XXH64_hash_t (*XXH3_dispatchx86_hashLong64_withSeed)(const void* XXH_RESTRICT, size_t, XXH64_hash_t);
|
||||||
|
|
||||||
|
typedef XXH64_hash_t (*XXH3_dispatchx86_hashLong64_withSecret)(const void* XXH_RESTRICT, size_t, const void* XXH_RESTRICT, size_t);
|
||||||
|
|
||||||
|
typedef XXH_errorcode (*XXH3_dispatchx86_update)(XXH3_state_t*, const void*, size_t);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
XXH3_dispatchx86_hashLong64_default hashLong64_default;
|
||||||
|
XXH3_dispatchx86_hashLong64_withSeed hashLong64_seed;
|
||||||
|
XXH3_dispatchx86_hashLong64_withSecret hashLong64_secret;
|
||||||
|
XXH3_dispatchx86_update update;
|
||||||
|
} XXH_dispatchFunctions_s;
|
||||||
|
|
||||||
|
#define XXH_NB_DISPATCHES 4
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @internal
|
||||||
|
* @brief Table of dispatchers for @ref XXH3_64bits().
|
||||||
|
*
|
||||||
|
* @pre The indices must match @ref XXH_VECTOR_TYPE.
|
||||||
|
*/
|
||||||
|
static const XXH_dispatchFunctions_s XXH_kDispatch[XXH_NB_DISPATCHES] = {
|
||||||
|
#if XXH_DISPATCH_SCALAR
|
||||||
|
/* Scalar */ { XXHL64_default_scalar, XXHL64_seed_scalar, XXHL64_secret_scalar, XXH3_update_scalar },
|
||||||
|
#else
|
||||||
|
/* Scalar */ { NULL, NULL, NULL, NULL },
|
||||||
|
#endif
|
||||||
|
/* SSE2 */ { XXHL64_default_sse2, XXHL64_seed_sse2, XXHL64_secret_sse2, XXH3_update_sse2 },
|
||||||
|
#if XXH_DISPATCH_AVX2
|
||||||
|
/* AVX2 */ { XXHL64_default_avx2, XXHL64_seed_avx2, XXHL64_secret_avx2, XXH3_update_avx2 },
|
||||||
|
#else
|
||||||
|
/* AVX2 */ { NULL, NULL, NULL, NULL },
|
||||||
|
#endif
|
||||||
|
#if XXH_DISPATCH_AVX512
|
||||||
|
/* AVX512 */ { XXHL64_default_avx512, XXHL64_seed_avx512, XXHL64_secret_avx512, XXH3_update_avx512 }
|
||||||
|
#else
|
||||||
|
/* AVX512 */ { NULL, NULL, NULL, NULL }
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
/*!
|
||||||
|
* @internal
|
||||||
|
* @brief The selected dispatch table for @ref XXH3_64bits().
|
||||||
|
*/
|
||||||
|
static XXH_dispatchFunctions_s XXH_g_dispatch = { NULL, NULL, NULL, NULL };
|
||||||
|
|
||||||
|
|
||||||
|
typedef XXH128_hash_t (*XXH3_dispatchx86_hashLong128_default)(const void* XXH_RESTRICT, size_t);
|
||||||
|
|
||||||
|
typedef XXH128_hash_t (*XXH3_dispatchx86_hashLong128_withSeed)(const void* XXH_RESTRICT, size_t, XXH64_hash_t);
|
||||||
|
|
||||||
|
typedef XXH128_hash_t (*XXH3_dispatchx86_hashLong128_withSecret)(const void* XXH_RESTRICT, size_t, const void* XXH_RESTRICT, size_t);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
XXH3_dispatchx86_hashLong128_default hashLong128_default;
|
||||||
|
XXH3_dispatchx86_hashLong128_withSeed hashLong128_seed;
|
||||||
|
XXH3_dispatchx86_hashLong128_withSecret hashLong128_secret;
|
||||||
|
XXH3_dispatchx86_update update;
|
||||||
|
} XXH_dispatch128Functions_s;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @internal
|
||||||
|
* @brief Table of dispatchers for @ref XXH3_128bits().
|
||||||
|
*
|
||||||
|
* @pre The indices must match @ref XXH_VECTOR_TYPE.
|
||||||
|
*/
|
||||||
|
static const XXH_dispatch128Functions_s XXH_kDispatch128[XXH_NB_DISPATCHES] = {
|
||||||
|
#if XXH_DISPATCH_SCALAR
|
||||||
|
/* Scalar */ { XXHL128_default_scalar, XXHL128_seed_scalar, XXHL128_secret_scalar, XXH3_update_scalar },
|
||||||
|
#else
|
||||||
|
/* Scalar */ { NULL, NULL, NULL, NULL },
|
||||||
|
#endif
|
||||||
|
/* SSE2 */ { XXHL128_default_sse2, XXHL128_seed_sse2, XXHL128_secret_sse2, XXH3_update_sse2 },
|
||||||
|
#if XXH_DISPATCH_AVX2
|
||||||
|
/* AVX2 */ { XXHL128_default_avx2, XXHL128_seed_avx2, XXHL128_secret_avx2, XXH3_update_avx2 },
|
||||||
|
#else
|
||||||
|
/* AVX2 */ { NULL, NULL, NULL, NULL },
|
||||||
|
#endif
|
||||||
|
#if XXH_DISPATCH_AVX512
|
||||||
|
/* AVX512 */ { XXHL128_default_avx512, XXHL128_seed_avx512, XXHL128_secret_avx512, XXH3_update_avx512 }
|
||||||
|
#else
|
||||||
|
/* AVX512 */ { NULL, NULL, NULL, NULL }
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @internal
|
||||||
|
* @brief The selected dispatch table for @ref XXH3_64bits().
|
||||||
|
*/
|
||||||
|
static XXH_dispatch128Functions_s XXH_g_dispatch128 = { NULL, NULL, NULL, NULL };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @internal
|
||||||
|
* @brief Runs a CPUID check and sets the correct dispatch tables.
|
||||||
|
*/
|
||||||
|
static void XXH_setDispatch(void)
|
||||||
|
{
|
||||||
|
int vecID = XXH_featureTest();
|
||||||
|
XXH_STATIC_ASSERT(XXH_AVX512 == XXH_NB_DISPATCHES-1);
|
||||||
|
assert(XXH_SCALAR <= vecID && vecID <= XXH_AVX512);
|
||||||
|
#if !XXH_DISPATCH_SCALAR
|
||||||
|
assert(vecID != XXH_SCALAR);
|
||||||
|
#endif
|
||||||
|
#if !XXH_DISPATCH_AVX512
|
||||||
|
assert(vecID != XXH_AVX512);
|
||||||
|
#endif
|
||||||
|
#if !XXH_DISPATCH_AVX2
|
||||||
|
assert(vecID != XXH_AVX2);
|
||||||
|
#endif
|
||||||
|
XXH_g_dispatch = XXH_kDispatch[vecID];
|
||||||
|
XXH_g_dispatch128 = XXH_kDispatch128[vecID];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==== XXH3 public functions ==== */
|
||||||
|
|
||||||
|
static XXH64_hash_t
|
||||||
|
XXH3_hashLong_64b_defaultSecret_selection(const void* input, size_t len,
|
||||||
|
XXH64_hash_t seed64, const xxh_u8* secret, size_t secretLen)
|
||||||
|
{
|
||||||
|
(void)seed64; (void)secret; (void)secretLen;
|
||||||
|
if (XXH_g_dispatch.hashLong64_default == NULL) XXH_setDispatch();
|
||||||
|
return XXH_g_dispatch.hashLong64_default(input, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH64_hash_t XXH3_64bits_dispatch(const void* input, size_t len)
|
||||||
|
{
|
||||||
|
return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_defaultSecret_selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
static XXH64_hash_t
|
||||||
|
XXH3_hashLong_64b_withSeed_selection(const void* input, size_t len,
|
||||||
|
XXH64_hash_t seed64, const xxh_u8* secret, size_t secretLen)
|
||||||
|
{
|
||||||
|
(void)secret; (void)secretLen;
|
||||||
|
if (XXH_g_dispatch.hashLong64_seed == NULL) XXH_setDispatch();
|
||||||
|
return XXH_g_dispatch.hashLong64_seed(input, len, seed64);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH64_hash_t XXH3_64bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed)
|
||||||
|
{
|
||||||
|
return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed_selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
static XXH64_hash_t
|
||||||
|
XXH3_hashLong_64b_withSecret_selection(const void* input, size_t len,
|
||||||
|
XXH64_hash_t seed64, const xxh_u8* secret, size_t secretLen)
|
||||||
|
{
|
||||||
|
(void)seed64;
|
||||||
|
if (XXH_g_dispatch.hashLong64_secret == NULL) XXH_setDispatch();
|
||||||
|
return XXH_g_dispatch.hashLong64_secret(input, len, secret, secretLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH64_hash_t XXH3_64bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen)
|
||||||
|
{
|
||||||
|
return XXH3_64bits_internal(input, len, 0, secret, secretLen, XXH3_hashLong_64b_withSecret_selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH_errorcode
|
||||||
|
XXH3_64bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len)
|
||||||
|
{
|
||||||
|
if (XXH_g_dispatch.update == NULL) XXH_setDispatch();
|
||||||
|
return XXH_g_dispatch.update(state, (const xxh_u8*)input, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==== XXH128 public functions ==== */
|
||||||
|
|
||||||
|
static XXH128_hash_t
|
||||||
|
XXH3_hashLong_128b_defaultSecret_selection(const void* input, size_t len,
|
||||||
|
XXH64_hash_t seed64, const void* secret, size_t secretLen)
|
||||||
|
{
|
||||||
|
(void)seed64; (void)secret; (void)secretLen;
|
||||||
|
if (XXH_g_dispatch128.hashLong128_default == NULL) XXH_setDispatch();
|
||||||
|
return XXH_g_dispatch128.hashLong128_default(input, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH128_hash_t XXH3_128bits_dispatch(const void* input, size_t len)
|
||||||
|
{
|
||||||
|
return XXH3_128bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_defaultSecret_selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
static XXH128_hash_t
|
||||||
|
XXH3_hashLong_128b_withSeed_selection(const void* input, size_t len,
|
||||||
|
XXH64_hash_t seed64, const void* secret, size_t secretLen)
|
||||||
|
{
|
||||||
|
(void)secret; (void)secretLen;
|
||||||
|
if (XXH_g_dispatch128.hashLong128_seed == NULL) XXH_setDispatch();
|
||||||
|
return XXH_g_dispatch128.hashLong128_seed(input, len, seed64);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH128_hash_t XXH3_128bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed)
|
||||||
|
{
|
||||||
|
return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_withSeed_selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
static XXH128_hash_t
|
||||||
|
XXH3_hashLong_128b_withSecret_selection(const void* input, size_t len,
|
||||||
|
XXH64_hash_t seed64, const void* secret, size_t secretLen)
|
||||||
|
{
|
||||||
|
(void)seed64;
|
||||||
|
if (XXH_g_dispatch128.hashLong128_secret == NULL) XXH_setDispatch();
|
||||||
|
return XXH_g_dispatch128.hashLong128_secret(input, len, secret, secretLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH128_hash_t XXH3_128bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen)
|
||||||
|
{
|
||||||
|
return XXH3_128bits_internal(input, len, 0, secret, secretLen, XXH3_hashLong_128b_withSecret_selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
XXH_errorcode
|
||||||
|
XXH3_128bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len)
|
||||||
|
{
|
||||||
|
if (XXH_g_dispatch128.update == NULL) XXH_setDispatch();
|
||||||
|
return XXH_g_dispatch128.update(state, (const xxh_u8*)input, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/*! @} */
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* xxHash - XXH3 Dispatcher for x86-based targets
|
||||||
|
* Copyright (C) 2020-2021 Yann Collet
|
||||||
|
*
|
||||||
|
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* You can contact the author at:
|
||||||
|
* - xxHash homepage: https://www.xxhash.com
|
||||||
|
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XXH_X86DISPATCH_H_13563687684
|
||||||
|
#define XXH_X86DISPATCH_H_13563687684
|
||||||
|
|
||||||
|
#include "xxhash.h" /* XXH64_hash_t, XXH3_state_t */
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_dispatch(const void* input, size_t len);
|
||||||
|
XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed);
|
||||||
|
XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen);
|
||||||
|
XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len);
|
||||||
|
|
||||||
|
XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_dispatch(const void* input, size_t len);
|
||||||
|
XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed);
|
||||||
|
XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen);
|
||||||
|
XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len);
|
||||||
|
|
||||||
|
#if defined (__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* automatic replacement of XXH3 functions.
|
||||||
|
* can be disabled by setting XXH_DISPATCH_DISABLE_REPLACE */
|
||||||
|
#ifndef XXH_DISPATCH_DISABLE_REPLACE
|
||||||
|
|
||||||
|
# undef XXH3_64bits
|
||||||
|
# define XXH3_64bits XXH3_64bits_dispatch
|
||||||
|
# undef XXH3_64bits_withSeed
|
||||||
|
# define XXH3_64bits_withSeed XXH3_64bits_withSeed_dispatch
|
||||||
|
# undef XXH3_64bits_withSecret
|
||||||
|
# define XXH3_64bits_withSecret XXH3_64bits_withSecret_dispatch
|
||||||
|
# undef XXH3_64bits_update
|
||||||
|
# define XXH3_64bits_update XXH3_64bits_update_dispatch
|
||||||
|
|
||||||
|
# undef XXH128
|
||||||
|
# define XXH128 XXH3_128bits_withSeed_dispatch
|
||||||
|
# undef XXH3_128bits
|
||||||
|
# define XXH3_128bits XXH3_128bits_dispatch
|
||||||
|
# undef XXH3_128bits_withSeed
|
||||||
|
# define XXH3_128bits_withSeed XXH3_128bits_withSeed_dispatch
|
||||||
|
# undef XXH3_128bits_withSecret
|
||||||
|
# define XXH3_128bits_withSecret XXH3_128bits_withSecret_dispatch
|
||||||
|
# undef XXH3_128bits_update
|
||||||
|
# define XXH3_128bits_update XXH3_128bits_update_dispatch
|
||||||
|
|
||||||
|
#endif /* XXH_DISPATCH_DISABLE_REPLACE */
|
||||||
|
|
||||||
|
#endif /* XXH_X86DISPATCH_H_13563687684 */
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* xxHash - Extremely Fast Hash algorithm
|
||||||
|
* Copyright (C) 2012-2021 Yann Collet
|
||||||
|
*
|
||||||
|
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* You can contact the author at:
|
||||||
|
* - xxHash homepage: https://www.xxhash.com
|
||||||
|
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* xxhash.c instantiates functions defined in xxhash.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */
|
||||||
|
#define XXH_IMPLEMENTATION /* access definitions */
|
||||||
|
|
||||||
|
#include "xxhash.h"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
||||||
|
//#if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64)
|
||||||
|
//#include "xxh_x86dispatch.c"
|
||||||
|
//#endif
|
||||||
|
#include "xxhash.c"
|
|
@ -0,0 +1,5 @@
|
||||||
|
//#if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64)
|
||||||
|
//#include "xxh_x86dispatch.h"
|
||||||
|
//#else
|
||||||
|
#include "xxhash.h"
|
||||||
|
//#endif
|
|
@ -0,0 +1,84 @@
|
||||||
|
#ifndef AURORA_AURORA_H
|
||||||
|
#define AURORA_AURORA_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#else
|
||||||
|
#include "stdint.h"
|
||||||
|
#include "stdbool.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BACKEND_AUTO,
|
||||||
|
BACKEND_D3D12,
|
||||||
|
BACKEND_METAL,
|
||||||
|
BACKEND_VULKAN,
|
||||||
|
BACKEND_OPENGL,
|
||||||
|
BACKEND_OPENGLES,
|
||||||
|
BACKEND_WEBGPU,
|
||||||
|
BACKEND_NULL,
|
||||||
|
} AuroraBackend;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LOG_DEBUG,
|
||||||
|
LOG_INFO,
|
||||||
|
LOG_WARNING,
|
||||||
|
LOG_ERROR,
|
||||||
|
LOG_FATAL,
|
||||||
|
} AuroraLogLevel;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t fb_width;
|
||||||
|
uint32_t fb_height;
|
||||||
|
float scale;
|
||||||
|
} AuroraWindowSize;
|
||||||
|
|
||||||
|
typedef struct AuroraEvent AuroraEvent;
|
||||||
|
|
||||||
|
typedef void (*AuroraLogCallback)(AuroraLogLevel level, const char* message, unsigned int len);
|
||||||
|
typedef void (*AuroraImGuiInitCallback)(const AuroraWindowSize* size);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* appName;
|
||||||
|
const char* configPath;
|
||||||
|
AuroraBackend desiredBackend;
|
||||||
|
uint32_t msaa;
|
||||||
|
uint16_t maxTextureAnisotropy;
|
||||||
|
bool startFullscreen;
|
||||||
|
uint32_t windowWidth;
|
||||||
|
uint32_t windowHeight;
|
||||||
|
void* iconRGBA8;
|
||||||
|
uint32_t iconWidth;
|
||||||
|
uint32_t iconHeight;
|
||||||
|
AuroraLogCallback logCallback;
|
||||||
|
AuroraImGuiInitCallback imGuiInitCallback;
|
||||||
|
} AuroraConfig;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
AuroraBackend backend;
|
||||||
|
const char* configPath;
|
||||||
|
AuroraWindowSize windowSize;
|
||||||
|
} AuroraInfo;
|
||||||
|
|
||||||
|
AuroraInfo aurora_initialize(int argc, char* argv[], const AuroraConfig* config);
|
||||||
|
void aurora_shutdown();
|
||||||
|
const AuroraEvent* aurora_update();
|
||||||
|
bool aurora_begin_frame();
|
||||||
|
void aurora_end_frame();
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define AURORA_GFX_DEBUG_GROUPS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void push_debug_group(const char* label);
|
||||||
|
void pop_debug_group();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef AURORA_EVENT_H
|
||||||
|
#define AURORA_EVENT_H
|
||||||
|
|
||||||
|
#include "aurora.h"
|
||||||
|
|
||||||
|
#include <SDL_events.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#else
|
||||||
|
#include "stdint.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AURORA_NONE,
|
||||||
|
AURORA_EXIT,
|
||||||
|
AURORA_SDL_EVENT,
|
||||||
|
AURORA_WINDOW_RESIZED,
|
||||||
|
AURORA_CONTROLLER_ADDED,
|
||||||
|
AURORA_CONTROLLER_REMOVED,
|
||||||
|
AURORA_PAUSED,
|
||||||
|
AURORA_UNPAUSED,
|
||||||
|
} AuroraEventType;
|
||||||
|
|
||||||
|
struct AuroraEvent {
|
||||||
|
AuroraEventType type;
|
||||||
|
union {
|
||||||
|
SDL_Event sdl;
|
||||||
|
AuroraWindowSize windowSize;
|
||||||
|
int32_t controller;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef AURORA_IMGUI_H
|
||||||
|
#define AURORA_IMGUI_H
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#else
|
||||||
|
#include "stdint.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ImTextureID aurora_imgui_add_texture(uint32_t width, uint32_t height, const void* rgba8);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef AURORA_MAIN_H
|
||||||
|
#define AURORA_MAIN_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int aurora_main(int argc, char* argv[]);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define main aurora_main
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,254 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#ifndef AURORA_VEC2_EXTRA
|
||||||
|
#define AURORA_VEC2_EXTRA
|
||||||
|
#endif
|
||||||
|
#ifndef AURORA_VEC3_EXTRA
|
||||||
|
#define AURORA_VEC3_EXTRA
|
||||||
|
#endif
|
||||||
|
#ifndef AURORA_VEC4_EXTRA
|
||||||
|
#define AURORA_VEC4_EXTRA
|
||||||
|
#endif
|
||||||
|
#ifndef AURORA_MAT4X4_EXTRA
|
||||||
|
#define AURORA_MAT4X4_EXTRA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __has_attribute
|
||||||
|
#define __has_attribute(x) 0
|
||||||
|
#endif
|
||||||
|
#ifndef __has_builtin
|
||||||
|
#define __has_builtin(x) 0
|
||||||
|
#endif
|
||||||
|
#if __has_attribute(vector_size)
|
||||||
|
//#define USE_GCC_VECTOR_EXTENSIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace aurora {
|
||||||
|
template <typename T>
|
||||||
|
struct Vec2 {
|
||||||
|
T x{};
|
||||||
|
T y{};
|
||||||
|
|
||||||
|
constexpr Vec2() = default;
|
||||||
|
constexpr Vec2(T x, T y) : x(x), y(y) {}
|
||||||
|
AURORA_VEC2_EXTRA
|
||||||
|
#ifdef METAFORCE
|
||||||
|
constexpr Vec2(const zeus::CVector2f& vec) : x(vec.x()), y(vec.y()) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool operator==(const Vec2& rhs) const { return x == rhs.x && y == rhs.y; }
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
struct Vec3 {
|
||||||
|
T x{};
|
||||||
|
T y{};
|
||||||
|
T z{};
|
||||||
|
|
||||||
|
constexpr Vec3() = default;
|
||||||
|
constexpr Vec3(T x, T y, T z) : x(x), y(y), z(z) {}
|
||||||
|
AURORA_VEC3_EXTRA
|
||||||
|
#ifdef METAFORCE
|
||||||
|
constexpr Vec3(const zeus::CVector3f& vec) : x(vec.x()), y(vec.y()), z(vec.z()) {}
|
||||||
|
operator zeus::CVector3f() const { return {x, y, z}; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool operator==(const Vec3& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; }
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
struct Vec4 {
|
||||||
|
#ifdef USE_GCC_VECTOR_EXTENSIONS
|
||||||
|
typedef T Vt __attribute__((vector_size(sizeof(T) * 4)));
|
||||||
|
Vt m;
|
||||||
|
#else
|
||||||
|
using Vt = T[4];
|
||||||
|
Vt m;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr Vec4() = default;
|
||||||
|
constexpr Vec4(Vt m) : m(m) {}
|
||||||
|
constexpr Vec4(T x, T y, T z, T w) : m{x, y, z, w} {}
|
||||||
|
// For Vec3 with padding
|
||||||
|
constexpr Vec4(T x, T y, T z) : m{x, y, z, {}} {}
|
||||||
|
// For Vec3 -> Vec4
|
||||||
|
constexpr Vec4(Vec3<T> v, T w) : m{v.x, v.y, v.z, w} {}
|
||||||
|
AURORA_VEC4_EXTRA
|
||||||
|
#ifdef METAFORCE
|
||||||
|
constexpr Vec4(const zeus::CVector4f& vec) : x(vec.x()), y(vec.y()), z(vec.z()), w(vec.w()) {}
|
||||||
|
constexpr Vec4(const zeus::CColor& color) : x(color.r()), y(color.g()), z(color.b()), w(color.a()) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline Vec4& operator=(const Vec4& other) {
|
||||||
|
memcpy(&m, &other.m, sizeof(Vt));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline T& x() { return m[0]; }
|
||||||
|
[[nodiscard]] inline T x() const { return m[0]; }
|
||||||
|
[[nodiscard]] inline T& y() { return m[1]; }
|
||||||
|
[[nodiscard]] inline T y() const { return m[1]; }
|
||||||
|
[[nodiscard]] inline T& z() { return m[2]; }
|
||||||
|
[[nodiscard]] inline T z() const { return m[2]; }
|
||||||
|
[[nodiscard]] inline T& w() { return m[3]; }
|
||||||
|
[[nodiscard]] inline T w() const { return m[3]; }
|
||||||
|
[[nodiscard]] inline T& operator[](size_t i) { return m[i]; }
|
||||||
|
[[nodiscard]] inline T operator[](size_t i) const { return m[i]; }
|
||||||
|
|
||||||
|
template <size_t x, size_t y, size_t z, size_t w>
|
||||||
|
[[nodiscard]] constexpr Vec4 shuffle() const {
|
||||||
|
static_assert(x < 4 && y < 4 && z < 4 && w < 4);
|
||||||
|
#if defined(USE_GCC_VECTOR_EXTENSIONS) && __has_builtin(__builtin_shuffle)
|
||||||
|
typedef int Vi __attribute__((vector_size(16)));
|
||||||
|
return __builtin_shuffle(m, Vi{x, y, z, w});
|
||||||
|
#else
|
||||||
|
return {m[x], m[y], m[z], m[w]};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Vec4& rhs) const {
|
||||||
|
#if defined(USE_GCC_VECTOR_EXTENSIONS) && __has_builtin(__builtin_reduce_and)
|
||||||
|
return __builtin_reduce_and(m == rhs.m) != 0;
|
||||||
|
#else
|
||||||
|
return m[0] == rhs.m[0] && m[1] == rhs.m[1] && m[2] == rhs.m[2] && m[3] == rhs.m[3];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] inline Vec4<T> operator+(const Vec4<T>& a, const Vec4<T>& b) {
|
||||||
|
#ifdef USE_GCC_VECTOR_EXTENSIONS
|
||||||
|
return a.m + b.m;
|
||||||
|
#else
|
||||||
|
return {a.m[0] + b.m[0], a.m[1] + b.m[1], a.m[2] + b.m[2], a.m[3] + b.m[3]};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] inline Vec4<T> operator*(const Vec4<T>& a, const Vec4<T>& b) {
|
||||||
|
#ifdef USE_GCC_VECTOR_EXTENSIONS
|
||||||
|
return a.m * b.m;
|
||||||
|
#else
|
||||||
|
return {a.m[0] * b.m[0], a.m[1] * b.m[1], a.m[2] * b.m[2], a.m[3] * b.m[3]};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
struct Mat3x2 {
|
||||||
|
Vec2<T> m0{};
|
||||||
|
Vec2<T> m1{};
|
||||||
|
Vec2<T> m2{};
|
||||||
|
|
||||||
|
constexpr Mat3x2() = default;
|
||||||
|
constexpr Mat3x2(const Vec2<T>& m0, const Vec2<T>& m1, const Vec2<T>& m2) : m0(m0), m1(m1), m2(m2) {}
|
||||||
|
|
||||||
|
bool operator==(const Mat3x2& rhs) const { return m0 == rhs.m0 && m1 == rhs.m1 && m2 == rhs.m2; }
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
struct Mat4x2 {
|
||||||
|
Vec2<T> m0{};
|
||||||
|
Vec2<T> m1{};
|
||||||
|
Vec2<T> m2{};
|
||||||
|
Vec2<T> m3{};
|
||||||
|
|
||||||
|
constexpr Mat4x2() = default;
|
||||||
|
constexpr Mat4x2(const Vec2<T>& m0, const Vec2<T>& m1, const Vec2<T>& m2, const Vec2<T>& m3)
|
||||||
|
: m0(m0), m1(m1), m2(m2), m3(m3) {}
|
||||||
|
|
||||||
|
inline Mat4x2 transpose() const {
|
||||||
|
return {
|
||||||
|
{m0.x, m2.x},
|
||||||
|
{m0.y, m2.y},
|
||||||
|
{m1.x, m3.x},
|
||||||
|
{m1.y, m3.y},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Mat4x2& rhs) const { return m0 == rhs.m0 && m1 == rhs.m1 && m2 == rhs.m2 && m3 == rhs.m3; }
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
struct Mat4x4;
|
||||||
|
template <typename T>
|
||||||
|
struct Mat3x4 {
|
||||||
|
Vec4<T> m0{};
|
||||||
|
Vec4<T> m1{};
|
||||||
|
Vec4<T> m2{};
|
||||||
|
|
||||||
|
constexpr Mat3x4() = default;
|
||||||
|
constexpr Mat3x4(const Vec4<T>& m0, const Vec4<T>& m1, const Vec4<T>& m2) : m0(m0), m1(m1), m2(m2) {}
|
||||||
|
|
||||||
|
inline Mat4x4<T> to4x4() const;
|
||||||
|
inline Mat4x4<T> toTransposed4x4() const;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Mat3x4<float>) == sizeof(float[3][4]));
|
||||||
|
template <typename T>
|
||||||
|
struct Mat4x4 {
|
||||||
|
Vec4<T> m0{};
|
||||||
|
Vec4<T> m1{};
|
||||||
|
Vec4<T> m2{};
|
||||||
|
Vec4<T> m3{};
|
||||||
|
|
||||||
|
constexpr Mat4x4() = default;
|
||||||
|
constexpr Mat4x4(const Vec4<T>& m0, const Vec4<T>& m1, const Vec4<T>& m2, const Vec4<T>& m3)
|
||||||
|
: m0(m0), m1(m1), m2(m2), m3(m3) {}
|
||||||
|
AURORA_MAT4X4_EXTRA
|
||||||
|
#ifdef METAFORCE
|
||||||
|
constexpr Mat4x4(const zeus::CMatrix4f& m) : m0(m[0]), m1(m[1]), m2(m[2]), m3(m[3]) {}
|
||||||
|
constexpr Mat4x4(const zeus::CTransform& m) : Mat4x4(m.toMatrix4f()) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[[nodiscard]] Mat4x4 transpose() const {
|
||||||
|
return {
|
||||||
|
{m0[0], m1[0], m2[0], m3[0]},
|
||||||
|
{m0[1], m1[1], m2[1], m3[1]},
|
||||||
|
{m0[2], m1[2], m2[2], m3[2]},
|
||||||
|
{m0[3], m1[3], m2[3], m3[3]},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
inline Mat4x4& operator=(const Mat4x4& other) {
|
||||||
|
m0 = other.m0;
|
||||||
|
m1 = other.m1;
|
||||||
|
m2 = other.m2;
|
||||||
|
m3 = other.m3;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Vec4<T>& operator[](size_t i) { return *(&m0 + i); }
|
||||||
|
inline const Vec4<T>& operator[](size_t i) const { return *(&m0 + i); }
|
||||||
|
|
||||||
|
bool operator==(const Mat4x4& rhs) const { return m0 == rhs.m0 && m1 == rhs.m1 && m2 == rhs.m2 && m3 == rhs.m3; }
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Mat4x4<float>) == sizeof(float[4][4]));
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] inline Mat4x4<T> operator*(const Mat4x4<T>& a, const Mat4x4<T>& b) {
|
||||||
|
Mat4x4<T> out;
|
||||||
|
for (size_t i = 0; i < 4; ++i) {
|
||||||
|
*(&out.m0 + i) = a.m0 * b[i].template shuffle<0, 0, 0, 0>() + a.m1 * b[i].template shuffle<1, 1, 1, 1>() +
|
||||||
|
a.m2 * b[i].template shuffle<2, 2, 2, 2>() + a.m3 * b[i].template shuffle<3, 3, 3, 3>();
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] inline Mat4x4<T> Mat3x4<T>::to4x4() const {
|
||||||
|
return {
|
||||||
|
{m0.m[0], m0.m[1], m0.m[2], 0.f},
|
||||||
|
{m1.m[0], m1.m[1], m1.m[2], 0.f},
|
||||||
|
{m2.m[0], m2.m[1], m2.m[2], 0.f},
|
||||||
|
{m0.m[3], m1.m[3], m2.m[3], 1.f},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] inline Mat4x4<T> Mat3x4<T>::toTransposed4x4() const {
|
||||||
|
return Mat4x4<T>{
|
||||||
|
m0,
|
||||||
|
m1,
|
||||||
|
m2,
|
||||||
|
{0.f, 0.f, 0.f, 1.f},
|
||||||
|
}
|
||||||
|
.transpose();
|
||||||
|
}
|
||||||
|
constexpr Mat4x4<float> Mat4x4_Identity{
|
||||||
|
Vec4<float>{1.f, 0.f, 0.f, 0.f},
|
||||||
|
Vec4<float>{0.f, 1.f, 0.f, 0.f},
|
||||||
|
Vec4<float>{0.f, 0.f, 1.f, 0.f},
|
||||||
|
Vec4<float>{0.f, 0.f, 0.f, 1.f},
|
||||||
|
};
|
||||||
|
} // namespace aurora
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef DOLPHIN_GX_H
|
||||||
|
#define DOLPHIN_GX_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXBump.h>
|
||||||
|
#include <dolphin/gx/GXCommandList.h>
|
||||||
|
#include <dolphin/gx/GXCull.h>
|
||||||
|
#include <dolphin/gx/GXDispList.h>
|
||||||
|
#include <dolphin/gx/GXDraw.h>
|
||||||
|
#include <dolphin/gx/GXExtra.h>
|
||||||
|
#include <dolphin/gx/GXFifo.h>
|
||||||
|
#include <dolphin/gx/GXFrameBuffer.h>
|
||||||
|
#include <dolphin/gx/GXGeometry.h>
|
||||||
|
#include <dolphin/gx/GXGet.h>
|
||||||
|
#include <dolphin/gx/GXLighting.h>
|
||||||
|
#include <dolphin/gx/GXManage.h>
|
||||||
|
#include <dolphin/gx/GXPerf.h>
|
||||||
|
#include <dolphin/gx/GXPixel.h>
|
||||||
|
#include <dolphin/gx/GXStruct.h>
|
||||||
|
#include <dolphin/gx/GXTev.h>
|
||||||
|
#include <dolphin/gx/GXTexture.h>
|
||||||
|
#include <dolphin/gx/GXTransform.h>
|
||||||
|
#include <dolphin/gx/GXVert.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef DOLPHIN_GXBUMP_H
|
||||||
|
#define DOLPHIN_GXBUMP_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXSetTevDirect(GXTevStageID tev_stage);
|
||||||
|
void GXSetNumIndStages(u8 nIndStages);
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
void GXSetIndTexMtx(GXIndTexMtxID mtx_sel, const void* offset, s8 scale_exp);
|
||||||
|
#else
|
||||||
|
void GXSetIndTexMtx(GXIndTexMtxID mtx_sel, f32 offset[2][3], s8 scale_exp);
|
||||||
|
#endif
|
||||||
|
void GXSetIndTexOrder(GXIndTexStageID ind_stage, GXTexCoordID tex_coord, GXTexMapID tex_map);
|
||||||
|
void GXSetTevIndirect(GXTevStageID tev_stage, GXIndTexStageID ind_stage, GXIndTexFormat format,
|
||||||
|
GXIndTexBiasSel bias_sel, GXIndTexMtxID matrix_sel, GXIndTexWrap wrap_s, GXIndTexWrap wrap_t,
|
||||||
|
GXBool add_prev, GXBool ind_lod, GXIndTexAlphaSel alpha_sel);
|
||||||
|
void GXSetTevIndWarp(GXTevStageID tev_stage, GXIndTexStageID ind_stage, GXBool signed_offsets, GXBool replace_mode,
|
||||||
|
GXIndTexMtxID matrix_sel);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef DOLPHIN_GXCOMMANDLIST_H
|
||||||
|
#define DOLPHIN_GXCOMMANDLIST_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GX_NOP 0x00
|
||||||
|
#define GX_DRAW_QUADS 0x80
|
||||||
|
#define GX_DRAW_TRIANGLES 0x90
|
||||||
|
#define GX_DRAW_TRIANGLE_STRIP 0x98
|
||||||
|
#define GX_DRAW_TRIANGLE_FAN 0xA0
|
||||||
|
#define GX_DRAW_LINES 0xA8
|
||||||
|
#define GX_DRAW_LINE_STRIP 0xB0
|
||||||
|
#define GX_DRAW_POINTS 0xB8
|
||||||
|
|
||||||
|
#define GX_LOAD_BP_REG 0x61
|
||||||
|
#define GX_LOAD_CP_REG 0x08
|
||||||
|
#define GX_LOAD_XF_REG 0x10
|
||||||
|
#define GX_LOAD_INDX_A 0x20
|
||||||
|
#define GX_LOAD_INDX_B 0x28
|
||||||
|
#define GX_LOAD_INDX_C 0x30
|
||||||
|
#define GX_LOAD_INDX_D 0x38
|
||||||
|
|
||||||
|
#define GX_CMD_CALL_DL 0x40
|
||||||
|
#define GX_CMD_INVL_VC 0x48
|
||||||
|
|
||||||
|
#define GX_OPCODE_MASK 0xF8
|
||||||
|
#define GX_VAT_MASK 0x07
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef DOLPHIN_GXCULL_H
|
||||||
|
#define DOLPHIN_GXCULL_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXSetScissor(u32 left, u32 top, u32 wd, u32 ht);
|
||||||
|
void GXSetCullMode(GXCullMode mode);
|
||||||
|
void GXSetCoPlanar(GXBool enable);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef DOLPHIN_GXDISPLIST_H
|
||||||
|
#define DOLPHIN_GXDISPLIST_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXBeginDisplayList(void* list, u32 size);
|
||||||
|
u32 GXEndDisplayList(void);
|
||||||
|
void GXCallDisplayList(const void* list, u32 nbytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef DOLPHIN_GXDRAW_H
|
||||||
|
#define DOLPHIN_GXDRAW_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXDrawSphere(u8 numMajor, u8 numMinor);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,758 @@
|
||||||
|
#ifndef DOLPHIN_GXENUM_H
|
||||||
|
#define DOLPHIN_GXENUM_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef bool GXBool;
|
||||||
|
#else
|
||||||
|
typedef u8 GXBool;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GX_FALSE ((GXBool)0)
|
||||||
|
#define GX_TRUE ((GXBool)1)
|
||||||
|
|
||||||
|
#define GX_ENABLE ((GXBool)1)
|
||||||
|
#define GX_DISABLE ((GXBool)0)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_PERSPECTIVE,
|
||||||
|
GX_ORTHOGRAPHIC,
|
||||||
|
} GXProjectionType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_NEVER,
|
||||||
|
GX_LESS,
|
||||||
|
GX_EQUAL,
|
||||||
|
GX_LEQUAL,
|
||||||
|
GX_GREATER,
|
||||||
|
GX_NEQUAL,
|
||||||
|
GX_GEQUAL,
|
||||||
|
GX_ALWAYS,
|
||||||
|
} GXCompare;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_AOP_AND,
|
||||||
|
GX_AOP_OR,
|
||||||
|
GX_AOP_XOR,
|
||||||
|
GX_AOP_XNOR,
|
||||||
|
GX_MAX_ALPHAOP,
|
||||||
|
} GXAlphaOp;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_ZC_LINEAR,
|
||||||
|
GX_ZC_NEAR,
|
||||||
|
GX_ZC_MID,
|
||||||
|
GX_ZC_FAR,
|
||||||
|
} GXZFmt16;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_GM_1_0,
|
||||||
|
GX_GM_1_7,
|
||||||
|
GX_GM_2_2,
|
||||||
|
} GXGamma;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_PF_RGB8_Z24,
|
||||||
|
GX_PF_RGBA6_Z24,
|
||||||
|
GX_PF_RGB565_Z16,
|
||||||
|
GX_PF_Z24,
|
||||||
|
GX_PF_Y8,
|
||||||
|
GX_PF_U8,
|
||||||
|
GX_PF_V8,
|
||||||
|
GX_PF_YUV420,
|
||||||
|
} GXPixelFmt;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_QUADS = 0x80,
|
||||||
|
GX_TRIANGLES = 0x90,
|
||||||
|
GX_TRIANGLESTRIP = 0x98,
|
||||||
|
GX_TRIANGLEFAN = 0xA0,
|
||||||
|
GX_LINES = 0xA8,
|
||||||
|
GX_LINESTRIP = 0xB0,
|
||||||
|
GX_POINTS = 0xB8,
|
||||||
|
} GXPrimitive;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_VTXFMT0,
|
||||||
|
GX_VTXFMT1,
|
||||||
|
GX_VTXFMT2,
|
||||||
|
GX_VTXFMT3,
|
||||||
|
GX_VTXFMT4,
|
||||||
|
GX_VTXFMT5,
|
||||||
|
GX_VTXFMT6,
|
||||||
|
GX_VTXFMT7,
|
||||||
|
GX_MAX_VTXFMT,
|
||||||
|
} GXVtxFmt;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_VA_PNMTXIDX,
|
||||||
|
GX_VA_TEX0MTXIDX,
|
||||||
|
GX_VA_TEX1MTXIDX,
|
||||||
|
GX_VA_TEX2MTXIDX,
|
||||||
|
GX_VA_TEX3MTXIDX,
|
||||||
|
GX_VA_TEX4MTXIDX,
|
||||||
|
GX_VA_TEX5MTXIDX,
|
||||||
|
GX_VA_TEX6MTXIDX,
|
||||||
|
GX_VA_TEX7MTXIDX,
|
||||||
|
GX_VA_POS,
|
||||||
|
GX_VA_NRM,
|
||||||
|
GX_VA_CLR0,
|
||||||
|
GX_VA_CLR1,
|
||||||
|
GX_VA_TEX0,
|
||||||
|
GX_VA_TEX1,
|
||||||
|
GX_VA_TEX2,
|
||||||
|
GX_VA_TEX3,
|
||||||
|
GX_VA_TEX4,
|
||||||
|
GX_VA_TEX5,
|
||||||
|
GX_VA_TEX6,
|
||||||
|
GX_VA_TEX7,
|
||||||
|
GX_POS_MTX_ARRAY,
|
||||||
|
GX_NRM_MTX_ARRAY,
|
||||||
|
GX_TEX_MTX_ARRAY,
|
||||||
|
GX_LIGHT_ARRAY,
|
||||||
|
GX_VA_NBT,
|
||||||
|
GX_VA_MAX_ATTR,
|
||||||
|
GX_VA_NULL = 0xFF,
|
||||||
|
} GXAttr;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_NONE,
|
||||||
|
GX_DIRECT,
|
||||||
|
GX_INDEX8,
|
||||||
|
GX_INDEX16,
|
||||||
|
} GXAttrType;
|
||||||
|
|
||||||
|
#define _GX_TF_CTF 0x20
|
||||||
|
#define _GX_TF_ZTF 0x10
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TF_I4 = 0x0,
|
||||||
|
GX_TF_I8 = 0x1,
|
||||||
|
GX_TF_IA4 = 0x2,
|
||||||
|
GX_TF_IA8 = 0x3,
|
||||||
|
GX_TF_RGB565 = 0x4,
|
||||||
|
GX_TF_RGB5A3 = 0x5,
|
||||||
|
GX_TF_RGBA8 = 0x6,
|
||||||
|
GX_TF_CMPR = 0xE,
|
||||||
|
|
||||||
|
GX_CTF_R4 = 0x0 | _GX_TF_CTF,
|
||||||
|
GX_CTF_RA4 = 0x2 | _GX_TF_CTF,
|
||||||
|
GX_CTF_RA8 = 0x3 | _GX_TF_CTF,
|
||||||
|
GX_CTF_YUVA8 = 0x6 | _GX_TF_CTF,
|
||||||
|
GX_CTF_A8 = 0x7 | _GX_TF_CTF,
|
||||||
|
GX_CTF_R8 = 0x8 | _GX_TF_CTF,
|
||||||
|
GX_CTF_G8 = 0x9 | _GX_TF_CTF,
|
||||||
|
GX_CTF_B8 = 0xA | _GX_TF_CTF,
|
||||||
|
GX_CTF_RG8 = 0xB | _GX_TF_CTF,
|
||||||
|
GX_CTF_GB8 = 0xC | _GX_TF_CTF,
|
||||||
|
|
||||||
|
GX_TF_Z8 = 0x1 | _GX_TF_ZTF,
|
||||||
|
GX_TF_Z16 = 0x3 | _GX_TF_ZTF,
|
||||||
|
GX_TF_Z24X8 = 0x6 | _GX_TF_ZTF,
|
||||||
|
|
||||||
|
GX_CTF_Z4 = 0x0 | _GX_TF_ZTF | _GX_TF_CTF,
|
||||||
|
GX_CTF_Z8M = 0x9 | _GX_TF_ZTF | _GX_TF_CTF,
|
||||||
|
GX_CTF_Z8L = 0xA | _GX_TF_ZTF | _GX_TF_CTF,
|
||||||
|
GX_CTF_Z16L = 0xC | _GX_TF_ZTF | _GX_TF_CTF,
|
||||||
|
|
||||||
|
GX_TF_A8 = GX_CTF_A8,
|
||||||
|
} GXTexFmt;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TF_C4 = 0x8,
|
||||||
|
GX_TF_C8 = 0x9,
|
||||||
|
GX_TF_C14X2 = 0xa,
|
||||||
|
} GXCITexFmt;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_CLAMP,
|
||||||
|
GX_REPEAT,
|
||||||
|
GX_MIRROR,
|
||||||
|
GX_MAX_TEXWRAPMODE,
|
||||||
|
} GXTexWrapMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_NEAR,
|
||||||
|
GX_LINEAR,
|
||||||
|
GX_NEAR_MIP_NEAR,
|
||||||
|
GX_LIN_MIP_NEAR,
|
||||||
|
GX_NEAR_MIP_LIN,
|
||||||
|
GX_LIN_MIP_LIN,
|
||||||
|
} GXTexFilter;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_ANISO_1,
|
||||||
|
GX_ANISO_2,
|
||||||
|
GX_ANISO_4,
|
||||||
|
GX_MAX_ANISOTROPY,
|
||||||
|
} GXAnisotropy;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TEXMAP0,
|
||||||
|
GX_TEXMAP1,
|
||||||
|
GX_TEXMAP2,
|
||||||
|
GX_TEXMAP3,
|
||||||
|
GX_TEXMAP4,
|
||||||
|
GX_TEXMAP5,
|
||||||
|
GX_TEXMAP6,
|
||||||
|
GX_TEXMAP7,
|
||||||
|
GX_MAX_TEXMAP,
|
||||||
|
GX_TEXMAP_NULL = 0xFF,
|
||||||
|
GX_TEX_DISABLE = 0x100,
|
||||||
|
} GXTexMapID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TEXCOORD0,
|
||||||
|
GX_TEXCOORD1,
|
||||||
|
GX_TEXCOORD2,
|
||||||
|
GX_TEXCOORD3,
|
||||||
|
GX_TEXCOORD4,
|
||||||
|
GX_TEXCOORD5,
|
||||||
|
GX_TEXCOORD6,
|
||||||
|
GX_TEXCOORD7,
|
||||||
|
GX_MAX_TEXCOORD,
|
||||||
|
GX_TEXCOORD_NULL = 0xFF,
|
||||||
|
} GXTexCoordID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TEVSTAGE0,
|
||||||
|
GX_TEVSTAGE1,
|
||||||
|
GX_TEVSTAGE2,
|
||||||
|
GX_TEVSTAGE3,
|
||||||
|
GX_TEVSTAGE4,
|
||||||
|
GX_TEVSTAGE5,
|
||||||
|
GX_TEVSTAGE6,
|
||||||
|
GX_TEVSTAGE7,
|
||||||
|
GX_TEVSTAGE8,
|
||||||
|
GX_TEVSTAGE9,
|
||||||
|
GX_TEVSTAGE10,
|
||||||
|
GX_TEVSTAGE11,
|
||||||
|
GX_TEVSTAGE12,
|
||||||
|
GX_TEVSTAGE13,
|
||||||
|
GX_TEVSTAGE14,
|
||||||
|
GX_TEVSTAGE15,
|
||||||
|
GX_MAX_TEVSTAGE,
|
||||||
|
} GXTevStageID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_MODULATE,
|
||||||
|
GX_DECAL,
|
||||||
|
GX_BLEND,
|
||||||
|
GX_REPLACE,
|
||||||
|
GX_PASSCLR,
|
||||||
|
} GXTevMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_MTX3x4,
|
||||||
|
GX_MTX2x4,
|
||||||
|
} GXTexMtxType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TG_MTX3x4,
|
||||||
|
GX_TG_MTX2x4,
|
||||||
|
GX_TG_BUMP0,
|
||||||
|
GX_TG_BUMP1,
|
||||||
|
GX_TG_BUMP2,
|
||||||
|
GX_TG_BUMP3,
|
||||||
|
GX_TG_BUMP4,
|
||||||
|
GX_TG_BUMP5,
|
||||||
|
GX_TG_BUMP6,
|
||||||
|
GX_TG_BUMP7,
|
||||||
|
GX_TG_SRTG,
|
||||||
|
} GXTexGenType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_PNMTX0 = 0,
|
||||||
|
GX_PNMTX1 = 3,
|
||||||
|
GX_PNMTX2 = 6,
|
||||||
|
GX_PNMTX3 = 9,
|
||||||
|
GX_PNMTX4 = 12,
|
||||||
|
GX_PNMTX5 = 15,
|
||||||
|
GX_PNMTX6 = 18,
|
||||||
|
GX_PNMTX7 = 21,
|
||||||
|
GX_PNMTX8 = 24,
|
||||||
|
GX_PNMTX9 = 27,
|
||||||
|
} GXPosNrmMtx;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TEXMTX0 = 30,
|
||||||
|
GX_TEXMTX1 = 33,
|
||||||
|
GX_TEXMTX2 = 36,
|
||||||
|
GX_TEXMTX3 = 39,
|
||||||
|
GX_TEXMTX4 = 42,
|
||||||
|
GX_TEXMTX5 = 45,
|
||||||
|
GX_TEXMTX6 = 48,
|
||||||
|
GX_TEXMTX7 = 51,
|
||||||
|
GX_TEXMTX8 = 54,
|
||||||
|
GX_TEXMTX9 = 57,
|
||||||
|
GX_IDENTITY = 60,
|
||||||
|
} GXTexMtx;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_COLOR0,
|
||||||
|
GX_COLOR1,
|
||||||
|
GX_ALPHA0,
|
||||||
|
GX_ALPHA1,
|
||||||
|
GX_COLOR0A0,
|
||||||
|
GX_COLOR1A1,
|
||||||
|
GX_COLOR_ZERO,
|
||||||
|
GX_ALPHA_BUMP,
|
||||||
|
GX_ALPHA_BUMPN,
|
||||||
|
GX_COLOR_NULL = 0xFF,
|
||||||
|
} GXChannelID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TG_POS,
|
||||||
|
GX_TG_NRM,
|
||||||
|
GX_TG_BINRM,
|
||||||
|
GX_TG_TANGENT,
|
||||||
|
GX_TG_TEX0,
|
||||||
|
GX_TG_TEX1,
|
||||||
|
GX_TG_TEX2,
|
||||||
|
GX_TG_TEX3,
|
||||||
|
GX_TG_TEX4,
|
||||||
|
GX_TG_TEX5,
|
||||||
|
GX_TG_TEX6,
|
||||||
|
GX_TG_TEX7,
|
||||||
|
GX_TG_TEXCOORD0,
|
||||||
|
GX_TG_TEXCOORD1,
|
||||||
|
GX_TG_TEXCOORD2,
|
||||||
|
GX_TG_TEXCOORD3,
|
||||||
|
GX_TG_TEXCOORD4,
|
||||||
|
GX_TG_TEXCOORD5,
|
||||||
|
GX_TG_TEXCOORD6,
|
||||||
|
GX_TG_COLOR0,
|
||||||
|
GX_TG_COLOR1,
|
||||||
|
GX_MAX_TEXGENSRC,
|
||||||
|
} GXTexGenSrc;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_BM_NONE,
|
||||||
|
GX_BM_BLEND,
|
||||||
|
GX_BM_LOGIC,
|
||||||
|
GX_BM_SUBTRACT,
|
||||||
|
GX_MAX_BLENDMODE,
|
||||||
|
} GXBlendMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_BL_ZERO,
|
||||||
|
GX_BL_ONE,
|
||||||
|
GX_BL_SRCCLR,
|
||||||
|
GX_BL_INVSRCCLR,
|
||||||
|
GX_BL_SRCALPHA,
|
||||||
|
GX_BL_INVSRCALPHA,
|
||||||
|
GX_BL_DSTALPHA,
|
||||||
|
GX_BL_INVDSTALPHA,
|
||||||
|
GX_BL_DSTCLR = GX_BL_SRCCLR,
|
||||||
|
GX_BL_INVDSTCLR = GX_BL_INVSRCCLR,
|
||||||
|
} GXBlendFactor;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_LO_CLEAR,
|
||||||
|
GX_LO_AND,
|
||||||
|
GX_LO_REVAND,
|
||||||
|
GX_LO_COPY,
|
||||||
|
GX_LO_INVAND,
|
||||||
|
GX_LO_NOOP,
|
||||||
|
GX_LO_XOR,
|
||||||
|
GX_LO_OR,
|
||||||
|
GX_LO_NOR,
|
||||||
|
GX_LO_EQUIV,
|
||||||
|
GX_LO_INV,
|
||||||
|
GX_LO_REVOR,
|
||||||
|
GX_LO_INVCOPY,
|
||||||
|
GX_LO_INVOR,
|
||||||
|
GX_LO_NAND,
|
||||||
|
GX_LO_SET,
|
||||||
|
} GXLogicOp;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_POS_XY = 0,
|
||||||
|
GX_POS_XYZ = 1,
|
||||||
|
GX_NRM_XYZ = 0,
|
||||||
|
GX_NRM_NBT = 1,
|
||||||
|
GX_NRM_NBT3 = 2,
|
||||||
|
GX_CLR_RGB = 0,
|
||||||
|
GX_CLR_RGBA = 1,
|
||||||
|
GX_TEX_S = 0,
|
||||||
|
GX_TEX_ST = 1,
|
||||||
|
} GXCompCnt;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_U8 = 0,
|
||||||
|
GX_S8 = 1,
|
||||||
|
GX_U16 = 2,
|
||||||
|
GX_S16 = 3,
|
||||||
|
GX_F32 = 4,
|
||||||
|
GX_RGB565 = 0,
|
||||||
|
GX_RGB8 = 1,
|
||||||
|
GX_RGBX8 = 2,
|
||||||
|
GX_RGBA4 = 3,
|
||||||
|
GX_RGBA6 = 4,
|
||||||
|
GX_RGBA8 = 5,
|
||||||
|
} GXCompType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_PTTEXMTX0 = 64,
|
||||||
|
GX_PTTEXMTX1 = 67,
|
||||||
|
GX_PTTEXMTX2 = 70,
|
||||||
|
GX_PTTEXMTX3 = 73,
|
||||||
|
GX_PTTEXMTX4 = 76,
|
||||||
|
GX_PTTEXMTX5 = 79,
|
||||||
|
GX_PTTEXMTX6 = 82,
|
||||||
|
GX_PTTEXMTX7 = 85,
|
||||||
|
GX_PTTEXMTX8 = 88,
|
||||||
|
GX_PTTEXMTX9 = 91,
|
||||||
|
GX_PTTEXMTX10 = 94,
|
||||||
|
GX_PTTEXMTX11 = 97,
|
||||||
|
GX_PTTEXMTX12 = 100,
|
||||||
|
GX_PTTEXMTX13 = 103,
|
||||||
|
GX_PTTEXMTX14 = 106,
|
||||||
|
GX_PTTEXMTX15 = 109,
|
||||||
|
GX_PTTEXMTX16 = 112,
|
||||||
|
GX_PTTEXMTX17 = 115,
|
||||||
|
GX_PTTEXMTX18 = 118,
|
||||||
|
GX_PTTEXMTX19 = 121,
|
||||||
|
GX_PTIDENTITY = 125,
|
||||||
|
} GXPTTexMtx;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TEVPREV,
|
||||||
|
GX_TEVREG0,
|
||||||
|
GX_TEVREG1,
|
||||||
|
GX_TEVREG2,
|
||||||
|
GX_MAX_TEVREG,
|
||||||
|
} GXTevRegID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_DF_NONE,
|
||||||
|
GX_DF_SIGN,
|
||||||
|
GX_DF_CLAMP,
|
||||||
|
} GXDiffuseFn;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_SRC_REG,
|
||||||
|
GX_SRC_VTX,
|
||||||
|
} GXColorSrc;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_AF_SPEC,
|
||||||
|
GX_AF_SPOT,
|
||||||
|
GX_AF_NONE,
|
||||||
|
} GXAttnFn;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_LIGHT0 = 0x001,
|
||||||
|
GX_LIGHT1 = 0x002,
|
||||||
|
GX_LIGHT2 = 0x004,
|
||||||
|
GX_LIGHT3 = 0x008,
|
||||||
|
GX_LIGHT4 = 0x010,
|
||||||
|
GX_LIGHT5 = 0x020,
|
||||||
|
GX_LIGHT6 = 0x040,
|
||||||
|
GX_LIGHT7 = 0x080,
|
||||||
|
GX_MAX_LIGHT = 0x100,
|
||||||
|
GX_LIGHT_NULL = 0,
|
||||||
|
} GXLightID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TO_ZERO,
|
||||||
|
GX_TO_SIXTEENTH,
|
||||||
|
GX_TO_EIGHTH,
|
||||||
|
GX_TO_FOURTH,
|
||||||
|
GX_TO_HALF,
|
||||||
|
GX_TO_ONE,
|
||||||
|
GX_MAX_TEXOFFSET,
|
||||||
|
} GXTexOffset;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_SP_OFF,
|
||||||
|
GX_SP_FLAT,
|
||||||
|
GX_SP_COS,
|
||||||
|
GX_SP_COS2,
|
||||||
|
GX_SP_SHARP,
|
||||||
|
GX_SP_RING1,
|
||||||
|
GX_SP_RING2,
|
||||||
|
} GXSpotFn;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_DA_OFF,
|
||||||
|
GX_DA_GENTLE,
|
||||||
|
GX_DA_MEDIUM,
|
||||||
|
GX_DA_STEEP,
|
||||||
|
} GXDistAttnFn;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_CULL_NONE,
|
||||||
|
GX_CULL_FRONT,
|
||||||
|
GX_CULL_BACK,
|
||||||
|
GX_CULL_ALL
|
||||||
|
|
||||||
|
} GXCullMode;
|
||||||
|
|
||||||
|
typedef enum { GX_TEV_SWAP0 = 0, GX_TEV_SWAP1, GX_TEV_SWAP2, GX_TEV_SWAP3, GX_MAX_TEVSWAP } GXTevSwapSel;
|
||||||
|
|
||||||
|
typedef enum { GX_CH_RED = 0, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA } GXTevColorChan;
|
||||||
|
|
||||||
|
typedef enum _GXFogType {
|
||||||
|
GX_FOG_NONE = 0,
|
||||||
|
GX_FOG_PERSP_LIN = 2,
|
||||||
|
GX_FOG_PERSP_EXP = 4,
|
||||||
|
GX_FOG_PERSP_EXP2 = 5,
|
||||||
|
GX_FOG_PERSP_REVEXP = 6,
|
||||||
|
GX_FOG_PERSP_REVEXP2 = 7,
|
||||||
|
GX_FOG_ORTHO_LIN = 10,
|
||||||
|
GX_FOG_ORTHO_EXP = 12,
|
||||||
|
GX_FOG_ORTHO_EXP2 = 13,
|
||||||
|
GX_FOG_ORTHO_REVEXP = 14,
|
||||||
|
GX_FOG_ORTHO_REVEXP2 = 15,
|
||||||
|
GX_FOG_LIN = GX_FOG_PERSP_LIN,
|
||||||
|
GX_FOG_EXP = GX_FOG_PERSP_EXP,
|
||||||
|
GX_FOG_EXP2 = GX_FOG_PERSP_EXP2,
|
||||||
|
GX_FOG_REVEXP = GX_FOG_PERSP_REVEXP,
|
||||||
|
GX_FOG_REVEXP2 = GX_FOG_PERSP_REVEXP2,
|
||||||
|
} GXFogType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_CC_CPREV,
|
||||||
|
GX_CC_APREV,
|
||||||
|
GX_CC_C0,
|
||||||
|
GX_CC_A0,
|
||||||
|
GX_CC_C1,
|
||||||
|
GX_CC_A1,
|
||||||
|
GX_CC_C2,
|
||||||
|
GX_CC_A2,
|
||||||
|
GX_CC_TEXC,
|
||||||
|
GX_CC_TEXA,
|
||||||
|
GX_CC_RASC,
|
||||||
|
GX_CC_RASA,
|
||||||
|
GX_CC_ONE,
|
||||||
|
GX_CC_HALF,
|
||||||
|
GX_CC_KONST,
|
||||||
|
GX_CC_ZERO
|
||||||
|
} GXTevColorArg;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_CA_APREV,
|
||||||
|
GX_CA_A0,
|
||||||
|
GX_CA_A1,
|
||||||
|
GX_CA_A2,
|
||||||
|
GX_CA_TEXA,
|
||||||
|
GX_CA_RASA,
|
||||||
|
GX_CA_KONST,
|
||||||
|
GX_CA_ZERO
|
||||||
|
} GXTevAlphaArg;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TEV_ADD = 0,
|
||||||
|
GX_TEV_SUB = 1,
|
||||||
|
GX_TEV_COMP_R8_GT = 8,
|
||||||
|
GX_TEV_COMP_R8_EQ = 9,
|
||||||
|
GX_TEV_COMP_GR16_GT = 10,
|
||||||
|
GX_TEV_COMP_GR16_EQ = 11,
|
||||||
|
GX_TEV_COMP_BGR24_GT = 12,
|
||||||
|
GX_TEV_COMP_BGR24_EQ = 13,
|
||||||
|
GX_TEV_COMP_RGB8_GT = 14,
|
||||||
|
GX_TEV_COMP_RGB8_EQ = 15,
|
||||||
|
GX_TEV_COMP_A8_GT = GX_TEV_COMP_RGB8_GT,
|
||||||
|
GX_TEV_COMP_A8_EQ = GX_TEV_COMP_RGB8_EQ
|
||||||
|
} GXTevOp;
|
||||||
|
|
||||||
|
typedef enum { GX_TB_ZERO, GX_TB_ADDHALF, GX_TB_SUBHALF, GX_MAX_TEVBIAS } GXTevBias;
|
||||||
|
|
||||||
|
typedef enum { GX_CS_SCALE_1, GX_CS_SCALE_2, GX_CS_SCALE_4, GX_CS_DIVIDE_2, GX_MAX_TEVSCALE } GXTevScale;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TEV_KCSEL_8_8 = 0x00,
|
||||||
|
GX_TEV_KCSEL_7_8 = 0x01,
|
||||||
|
GX_TEV_KCSEL_6_8 = 0x02,
|
||||||
|
GX_TEV_KCSEL_5_8 = 0x03,
|
||||||
|
GX_TEV_KCSEL_4_8 = 0x04,
|
||||||
|
GX_TEV_KCSEL_3_8 = 0x05,
|
||||||
|
GX_TEV_KCSEL_2_8 = 0x06,
|
||||||
|
GX_TEV_KCSEL_1_8 = 0x07,
|
||||||
|
GX_TEV_KCSEL_1 = GX_TEV_KCSEL_8_8,
|
||||||
|
GX_TEV_KCSEL_3_4 = GX_TEV_KCSEL_6_8,
|
||||||
|
GX_TEV_KCSEL_1_2 = GX_TEV_KCSEL_4_8,
|
||||||
|
GX_TEV_KCSEL_1_4 = GX_TEV_KCSEL_2_8,
|
||||||
|
GX_TEV_KCSEL_K0 = 0x0C,
|
||||||
|
GX_TEV_KCSEL_K1 = 0x0D,
|
||||||
|
GX_TEV_KCSEL_K2 = 0x0E,
|
||||||
|
GX_TEV_KCSEL_K3 = 0x0F,
|
||||||
|
GX_TEV_KCSEL_K0_R = 0x10,
|
||||||
|
GX_TEV_KCSEL_K1_R = 0x11,
|
||||||
|
GX_TEV_KCSEL_K2_R = 0x12,
|
||||||
|
GX_TEV_KCSEL_K3_R = 0x13,
|
||||||
|
GX_TEV_KCSEL_K0_G = 0x14,
|
||||||
|
GX_TEV_KCSEL_K1_G = 0x15,
|
||||||
|
GX_TEV_KCSEL_K2_G = 0x16,
|
||||||
|
GX_TEV_KCSEL_K3_G = 0x17,
|
||||||
|
GX_TEV_KCSEL_K0_B = 0x18,
|
||||||
|
GX_TEV_KCSEL_K1_B = 0x19,
|
||||||
|
GX_TEV_KCSEL_K2_B = 0x1A,
|
||||||
|
GX_TEV_KCSEL_K3_B = 0x1B,
|
||||||
|
GX_TEV_KCSEL_K0_A = 0x1C,
|
||||||
|
GX_TEV_KCSEL_K1_A = 0x1D,
|
||||||
|
GX_TEV_KCSEL_K2_A = 0x1E,
|
||||||
|
GX_TEV_KCSEL_K3_A = 0x1F
|
||||||
|
} GXTevKColorSel;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TEV_KASEL_8_8 = 0x00,
|
||||||
|
GX_TEV_KASEL_7_8 = 0x01,
|
||||||
|
GX_TEV_KASEL_6_8 = 0x02,
|
||||||
|
GX_TEV_KASEL_5_8 = 0x03,
|
||||||
|
GX_TEV_KASEL_4_8 = 0x04,
|
||||||
|
GX_TEV_KASEL_3_8 = 0x05,
|
||||||
|
GX_TEV_KASEL_2_8 = 0x06,
|
||||||
|
GX_TEV_KASEL_1_8 = 0x07,
|
||||||
|
GX_TEV_KASEL_1 = GX_TEV_KASEL_8_8,
|
||||||
|
GX_TEV_KASEL_3_4 = GX_TEV_KASEL_6_8,
|
||||||
|
GX_TEV_KASEL_1_2 = GX_TEV_KASEL_4_8,
|
||||||
|
GX_TEV_KASEL_1_4 = GX_TEV_KASEL_2_8,
|
||||||
|
GX_TEV_KASEL_K0_R = 0x10,
|
||||||
|
GX_TEV_KASEL_K1_R = 0x11,
|
||||||
|
GX_TEV_KASEL_K2_R = 0x12,
|
||||||
|
GX_TEV_KASEL_K3_R = 0x13,
|
||||||
|
GX_TEV_KASEL_K0_G = 0x14,
|
||||||
|
GX_TEV_KASEL_K1_G = 0x15,
|
||||||
|
GX_TEV_KASEL_K2_G = 0x16,
|
||||||
|
GX_TEV_KASEL_K3_G = 0x17,
|
||||||
|
GX_TEV_KASEL_K0_B = 0x18,
|
||||||
|
GX_TEV_KASEL_K1_B = 0x19,
|
||||||
|
GX_TEV_KASEL_K2_B = 0x1A,
|
||||||
|
GX_TEV_KASEL_K3_B = 0x1B,
|
||||||
|
GX_TEV_KASEL_K0_A = 0x1C,
|
||||||
|
GX_TEV_KASEL_K1_A = 0x1D,
|
||||||
|
GX_TEV_KASEL_K2_A = 0x1E,
|
||||||
|
GX_TEV_KASEL_K3_A = 0x1F
|
||||||
|
} GXTevKAlphaSel;
|
||||||
|
|
||||||
|
typedef enum { GX_KCOLOR0 = 0, GX_KCOLOR1, GX_KCOLOR2, GX_KCOLOR3, GX_MAX_KCOLOR } GXTevKColorID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_ZT_DISABLE,
|
||||||
|
GX_ZT_ADD,
|
||||||
|
GX_ZT_REPLACE,
|
||||||
|
GX_MAX_ZTEXOP,
|
||||||
|
} GXZTexOp;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_ITF_8,
|
||||||
|
GX_ITF_5,
|
||||||
|
GX_ITF_4,
|
||||||
|
GX_ITF_3,
|
||||||
|
GX_MAX_ITFORMAT,
|
||||||
|
} GXIndTexFormat;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_ITB_NONE,
|
||||||
|
GX_ITB_S,
|
||||||
|
GX_ITB_T,
|
||||||
|
GX_ITB_ST,
|
||||||
|
GX_ITB_U,
|
||||||
|
GX_ITB_SU,
|
||||||
|
GX_ITB_TU,
|
||||||
|
GX_ITB_STU,
|
||||||
|
GX_MAX_ITBIAS,
|
||||||
|
} GXIndTexBiasSel;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_ITBA_OFF,
|
||||||
|
GX_ITBA_S,
|
||||||
|
GX_ITBA_T,
|
||||||
|
GX_ITBA_U,
|
||||||
|
GX_MAX_ITBALPHA,
|
||||||
|
} GXIndTexAlphaSel;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_ITM_OFF,
|
||||||
|
GX_ITM_0,
|
||||||
|
GX_ITM_1,
|
||||||
|
GX_ITM_2,
|
||||||
|
GX_ITM_S0 = 5,
|
||||||
|
GX_ITM_S1,
|
||||||
|
GX_ITM_S2,
|
||||||
|
GX_ITM_T0 = 9,
|
||||||
|
GX_ITM_T1,
|
||||||
|
GX_ITM_T2,
|
||||||
|
} GXIndTexMtxID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_ITW_OFF,
|
||||||
|
GX_ITW_256,
|
||||||
|
GX_ITW_128,
|
||||||
|
GX_ITW_64,
|
||||||
|
GX_ITW_32,
|
||||||
|
GX_ITW_16,
|
||||||
|
GX_ITW_0,
|
||||||
|
GX_MAX_ITWRAP,
|
||||||
|
} GXIndTexWrap;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_INDTEXSTAGE0,
|
||||||
|
GX_INDTEXSTAGE1,
|
||||||
|
GX_INDTEXSTAGE2,
|
||||||
|
GX_INDTEXSTAGE3,
|
||||||
|
GX_MAX_INDTEXSTAGE,
|
||||||
|
} GXIndTexStageID;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_ITS_1,
|
||||||
|
GX_ITS_2,
|
||||||
|
GX_ITS_4,
|
||||||
|
GX_ITS_8,
|
||||||
|
GX_ITS_16,
|
||||||
|
GX_ITS_32,
|
||||||
|
GX_ITS_64,
|
||||||
|
GX_ITS_128,
|
||||||
|
GX_ITS_256,
|
||||||
|
GX_MAX_ITSCALE,
|
||||||
|
} GXIndTexScale;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_CLIP_ENABLE = 0,
|
||||||
|
GX_CLIP_DISABLE = 1,
|
||||||
|
} GXClipMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TLUT0 = 0,
|
||||||
|
GX_TLUT1 = 1,
|
||||||
|
GX_TLUT2 = 2,
|
||||||
|
GX_TLUT3 = 3,
|
||||||
|
GX_TLUT4 = 4,
|
||||||
|
GX_TLUT5 = 5,
|
||||||
|
GX_TLUT6 = 6,
|
||||||
|
GX_TLUT7 = 7,
|
||||||
|
GX_TLUT8 = 8,
|
||||||
|
GX_TLUT9 = 9,
|
||||||
|
GX_TLUT10 = 10,
|
||||||
|
GX_TLUT11 = 11,
|
||||||
|
GX_TLUT12 = 12,
|
||||||
|
GX_TLUT13 = 13,
|
||||||
|
GX_TLUT14 = 14,
|
||||||
|
GX_TLUT15 = 15,
|
||||||
|
GX_BIGTLUT0 = 16,
|
||||||
|
GX_BIGTLUT1 = 17,
|
||||||
|
GX_BIGTLUT2 = 18,
|
||||||
|
GX_BIGTLUT3 = 19,
|
||||||
|
} GXTlut;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TL_IA8,
|
||||||
|
GX_TL_RGB565,
|
||||||
|
GX_TL_RGB5A3,
|
||||||
|
GX_MAX_TLUTFMT,
|
||||||
|
} GXTlutFmt;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef DOLPHIN_GXEXTRA_H
|
||||||
|
#define DOLPHIN_GXEXTRA_H
|
||||||
|
// Extra types for PC
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
#include <dolphin/gx/GXStruct.h>
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float r;
|
||||||
|
float g;
|
||||||
|
float b;
|
||||||
|
float a;
|
||||||
|
} GXColorF32;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GX_TF_R8_PC = 0x60,
|
||||||
|
GX_TF_RGBA8_PC = 0x61,
|
||||||
|
} GXPCTexFmt;
|
||||||
|
|
||||||
|
void GXDestroyTexObj(GXTexObj* obj);
|
||||||
|
|
||||||
|
void GXColor4f32(float r, float g, float b, float a);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef DOLPHIN_GXFIFO_H
|
||||||
|
#define DOLPHIN_GXFIFO_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 pad[128];
|
||||||
|
} GXFifoObj;
|
||||||
|
|
||||||
|
void GXInitFifoBase(GXFifoObj* fifo, void* base, u32 size);
|
||||||
|
void GXInitFifoPtrs(GXFifoObj* fifo, void* readPtr, void* writePtr);
|
||||||
|
void GXGetFifoPtrs(GXFifoObj* fifo, void** readPtr, void** writePtr);
|
||||||
|
GXFifoObj* GXGetCPUFifo(void);
|
||||||
|
GXFifoObj* GXGetGPFifo(void);
|
||||||
|
void GXSetCPUFifo(GXFifoObj* fifo);
|
||||||
|
void GXSetGPFifo(GXFifoObj* fifo);
|
||||||
|
void GXSaveCPUFifo(GXFifoObj* fifo);
|
||||||
|
void GXGetFifoStatus(GXFifoObj* fifo, GXBool* overhi, GXBool* underlow, u32* fifoCount, GXBool* cpu_write,
|
||||||
|
GXBool* gp_read, GXBool* fifowrap);
|
||||||
|
void GXGetGPStatus(GXBool* overhi, GXBool* underlow, GXBool* readIdle, GXBool* cmdIdle, GXBool* brkpt);
|
||||||
|
void GXInitFifoLimits(GXFifoObj* fifo, u32 hiWaterMark, u32 loWaterMark);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef DOLPHIN_GXFRAMEBUFFER_H
|
||||||
|
#define DOLPHIN_GXFRAMEBUFFER_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
#include <dolphin/gx/GXStruct.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GX_MAX_Z24 0x00FFFFFF
|
||||||
|
|
||||||
|
void GXSetCopyClear(GXColor clear_clr, u32 clear_z);
|
||||||
|
void GXAdjustForOverscan(GXRenderModeObj* rmin, GXRenderModeObj* rmout, u16 hor, u16 ver);
|
||||||
|
void GXCopyDisp(void* dest, GXBool clear);
|
||||||
|
void GXSetDispCopyGamma(GXGamma gamma);
|
||||||
|
void GXSetDispCopySrc(u16 left, u16 top, u16 wd, u16 ht);
|
||||||
|
void GXSetDispCopyDst(u16 wd, u16 ht);
|
||||||
|
u32 GXSetDispCopyYScale(f32 vscale);
|
||||||
|
void GXSetCopyFilter(GXBool aa, u8 sample_pattern[12][2], GXBool vf, u8 vfilter[7]);
|
||||||
|
void GXSetPixelFmt(GXPixelFmt pix_fmt, GXZFmt16 z_fmt);
|
||||||
|
void GXSetTexCopySrc(u16 left, u16 top, u16 wd, u16 ht);
|
||||||
|
void GXSetTexCopyDst(u16 wd, u16 ht, GXTexFmt fmt, GXBool mipmap);
|
||||||
|
void GXCopyTex(void* dest, GXBool clear);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef DOLPHIN_GXGEOMETRY_H
|
||||||
|
#define DOLPHIN_GXGEOMETRY_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXSetVtxDesc(GXAttr attr, GXAttrType type);
|
||||||
|
void GXSetVtxDescv(GXVtxDescList* list);
|
||||||
|
void GXClearVtxDesc(void);
|
||||||
|
void GXSetVtxAttrFmt(GXVtxFmt vtxfmt, GXAttr attr, GXCompCnt cnt, GXCompType type, u8 frac);
|
||||||
|
void GXSetNumTexGens(u8 nTexGens);
|
||||||
|
void GXBegin(GXPrimitive type, GXVtxFmt vtxfmt, u16 nverts);
|
||||||
|
void GXSetTexCoordGen2(GXTexCoordID dst_coord, GXTexGenType func, GXTexGenSrc src_param, u32 mtx, GXBool normalize,
|
||||||
|
u32 postmtx);
|
||||||
|
void GXSetLineWidth(u8 width, GXTexOffset texOffsets);
|
||||||
|
void GXSetPointSize(u8 pointSize, GXTexOffset texOffsets);
|
||||||
|
void GXEnableTexOffsets(GXTexCoordID coord, GXBool line_enable, GXBool point_enable);
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
void GXSetArray(GXAttr attr, const void* data, u32 size, u8 stride);
|
||||||
|
#else
|
||||||
|
void GXSetArray(GXAttr attr, const void* data, u8 stride);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void GXSetTexCoordGen(GXTexCoordID dst_coord, GXTexGenType func, GXTexGenSrc src_param, u32 mtx) {
|
||||||
|
GXSetTexCoordGen2(dst_coord, func, src_param, mtx, GX_FALSE, GX_PTIDENTITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef DOLPHIN_GXGET_H
|
||||||
|
#define DOLPHIN_GXGET_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
#include <dolphin/gx/GXStruct.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GXBool GXGetTexObjMipMap(GXTexObj* tex_obj);
|
||||||
|
GXTexFmt GXGetTexObjFmt(GXTexObj* tex_obj);
|
||||||
|
u16 GXGetTexObjHeight(GXTexObj* tex_obj);
|
||||||
|
u16 GXGetTexObjWidth(GXTexObj* tex_obj);
|
||||||
|
GXTexWrapMode GXGetTexObjWrapS(GXTexObj* tex_obj);
|
||||||
|
GXTexWrapMode GXGetTexObjWrapT(GXTexObj* tex_obj);
|
||||||
|
void* GXGetTexObjData(GXTexObj* tex_obj);
|
||||||
|
void GXGetProjectionv(f32* p);
|
||||||
|
void GXGetLightPos(GXLightObj* lt_obj, f32* x, f32* y, f32* z);
|
||||||
|
void GXGetLightColor(GXLightObj* lt_obj, GXColor* color);
|
||||||
|
void GXGetVtxAttrFmt(GXVtxFmt idx, GXAttr attr, GXCompCnt* compCnt, GXCompType* compType, u8* shift);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef DOLPHIN_GXLIGHTING_H
|
||||||
|
#define DOLPHIN_GXLIGHTING_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
#include <dolphin/gx/GXStruct.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXSetNumChans(u8 nChans);
|
||||||
|
void GXSetChanCtrl(GXChannelID chan, GXBool enable, GXColorSrc amb_src, GXColorSrc mat_src, u32 light_mask,
|
||||||
|
GXDiffuseFn diff_fn, GXAttnFn attn_fn);
|
||||||
|
void GXSetChanAmbColor(GXChannelID chan, GXColor amb_color);
|
||||||
|
void GXSetChanMatColor(GXChannelID chan, GXColor mat_color);
|
||||||
|
|
||||||
|
void GXInitLightSpot(GXLightObj* lt_obj, f32 cutoff, GXSpotFn spot_func);
|
||||||
|
void GXInitLightDistAttn(GXLightObj* lt_obj, f32 ref_distance, f32 ref_brightness, GXDistAttnFn dist_func);
|
||||||
|
void GXInitLightPos(GXLightObj* lt_obj, f32 x, f32 y, f32 z);
|
||||||
|
void GXInitLightDir(GXLightObj* lt_obj, f32 nx, f32 ny, f32 nz);
|
||||||
|
void GXInitLightColor(GXLightObj* lt_obj, GXColor color);
|
||||||
|
void GXInitLightAttn(GXLightObj* lt_obj, f32 a0, f32 a1, f32 a2, f32 k0, f32 k1, f32 k2);
|
||||||
|
void GXInitLightAttnA(GXLightObj* lt_obj, f32 a0, f32 a1, f32 a2);
|
||||||
|
void GXInitLightAttnK(GXLightObj* lt_obj, f32 k0, f32 k1, f32 k2);
|
||||||
|
void GXLoadLightObjImm(GXLightObj* lt_obj, GXLightID light);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef DOLPHIN_GXMANAGE_H
|
||||||
|
#define DOLPHIN_GXMANAGE_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXFifo.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef void (*GXDrawDoneCallback)(void);
|
||||||
|
|
||||||
|
GXFifoObj* GXInit(void* base, u32 size);
|
||||||
|
GXDrawDoneCallback GXSetDrawDoneCallback(GXDrawDoneCallback cb);
|
||||||
|
void GXDrawDone(void);
|
||||||
|
void GXSetDrawDone(void);
|
||||||
|
void GXFlush(void);
|
||||||
|
void GXPixModeSync(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef DOLPHIN_GXPERF_H
|
||||||
|
#define DOLPHIN_GXPERF_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXReadXfRasMetric(u32* xf_wait_in, u32* xf_wait_out, u32* ras_busy, u32* clocks);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef DOLPHIN_GXPIXEL_H
|
||||||
|
#define DOLPHIN_GXPIXEL_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXSetFog(GXFogType type, f32 startz, f32 endz, f32 nearz, f32 farz, GXColor color);
|
||||||
|
void GXSetFogColor(GXColor color);
|
||||||
|
// ? GXSetFogRangeAdj();
|
||||||
|
void GXSetBlendMode(GXBlendMode type, GXBlendFactor src_factor, GXBlendFactor dst_factor, GXLogicOp op);
|
||||||
|
void GXSetColorUpdate(GXBool update_enable);
|
||||||
|
void GXSetAlphaUpdate(GXBool update_enable);
|
||||||
|
void GXSetZMode(GXBool compare_enable, GXCompare func, GXBool update_enable);
|
||||||
|
void GXSetZCompLoc(GXBool before_tex);
|
||||||
|
void GXSetPixelFmt(GXPixelFmt pix_fmt, GXZFmt16 z_fmt);
|
||||||
|
void GXSetDither(GXBool dither);
|
||||||
|
void GXSetDstAlpha(GXBool enable, u8 alpha);
|
||||||
|
// ? GXSetFieldMask();
|
||||||
|
// ? GXSetFieldMode();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,100 @@
|
||||||
|
#ifndef DOLPHIN_GXSTRUCT_H
|
||||||
|
#define DOLPHIN_GXSTRUCT_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define VI_TVMODE(format, interlace) (((format) << 2) + (interlace))
|
||||||
|
|
||||||
|
#define VI_INTERLACE 0
|
||||||
|
#define VI_NON_INTERLACE 1
|
||||||
|
#define VI_PROGRESSIVE 2
|
||||||
|
|
||||||
|
#define VI_NTSC 0
|
||||||
|
#define VI_PAL 1
|
||||||
|
#define VI_MPAL 2
|
||||||
|
#define VI_DEBUG 3
|
||||||
|
#define VI_DEBUG_PAL 4
|
||||||
|
#define VI_EURGB60 5
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VI_TVMODE_NTSC_INT = VI_TVMODE(VI_NTSC, VI_INTERLACE),
|
||||||
|
VI_TVMODE_NTSC_DS = VI_TVMODE(VI_NTSC, VI_NON_INTERLACE),
|
||||||
|
VI_TVMODE_NTSC_PROG = VI_TVMODE(VI_NTSC, VI_PROGRESSIVE),
|
||||||
|
VI_TVMODE_PAL_INT = VI_TVMODE(VI_PAL, VI_INTERLACE),
|
||||||
|
VI_TVMODE_PAL_DS = VI_TVMODE(VI_PAL, VI_NON_INTERLACE),
|
||||||
|
VI_TVMODE_EURGB60_INT = VI_TVMODE(VI_EURGB60, VI_INTERLACE),
|
||||||
|
VI_TVMODE_EURGB60_DS = VI_TVMODE(VI_EURGB60, VI_NON_INTERLACE),
|
||||||
|
VI_TVMODE_MPAL_INT = VI_TVMODE(VI_MPAL, VI_INTERLACE),
|
||||||
|
VI_TVMODE_MPAL_DS = VI_TVMODE(VI_MPAL, VI_NON_INTERLACE),
|
||||||
|
VI_TVMODE_DEBUG_INT = VI_TVMODE(VI_DEBUG, VI_INTERLACE),
|
||||||
|
VI_TVMODE_DEBUG_PAL_INT = VI_TVMODE(VI_DEBUG_PAL, VI_INTERLACE),
|
||||||
|
VI_TVMODE_DEBUG_PAL_DS = VI_TVMODE(VI_DEBUG_PAL, VI_NON_INTERLACE)
|
||||||
|
} VITVMode;
|
||||||
|
|
||||||
|
typedef enum { VI_XFBMODE_SF = 0, VI_XFBMODE_DF } VIXFBMode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/*0x00*/ VITVMode viTVmode;
|
||||||
|
/*0x04*/ u16 fbWidth;
|
||||||
|
/*0x06*/ u16 efbHeight;
|
||||||
|
/*0x08*/ u16 xfbHeight;
|
||||||
|
/*0x0A*/ u16 viXOrigin;
|
||||||
|
/*0x0C*/ u16 viYOrigin;
|
||||||
|
/*0x0E*/ u16 viWidth;
|
||||||
|
/*0x10*/ u16 viHeight;
|
||||||
|
/*0x14*/ VIXFBMode xFBmode;
|
||||||
|
/*0x18*/ u8 field_rendering;
|
||||||
|
u8 aa;
|
||||||
|
u8 sample_pattern[12][2];
|
||||||
|
u8 vfilter[7];
|
||||||
|
} GXRenderModeObj;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 r;
|
||||||
|
u8 g;
|
||||||
|
u8 b;
|
||||||
|
u8 a;
|
||||||
|
} GXColor;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
u32 dummy[22];
|
||||||
|
#else
|
||||||
|
u32 dummy[8];
|
||||||
|
#endif
|
||||||
|
} GXTexObj;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
u32 dummy[4];
|
||||||
|
#else
|
||||||
|
u32 dummy[3];
|
||||||
|
#endif
|
||||||
|
} GXTlutObj;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 dummy[16];
|
||||||
|
} GXLightObj;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GXAttr attr;
|
||||||
|
GXAttrType type;
|
||||||
|
} GXVtxDescList;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
s16 r;
|
||||||
|
s16 g;
|
||||||
|
s16 b;
|
||||||
|
s16 a;
|
||||||
|
} GXColorS10;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef DOLPHIN_GXTEV_H
|
||||||
|
#define DOLPHIN_GXTEV_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
#include <dolphin/gx/GXStruct.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXSetTevOp(GXTevStageID id, GXTevMode mode);
|
||||||
|
void GXSetTevColorIn(GXTevStageID stage, GXTevColorArg a, GXTevColorArg b, GXTevColorArg c, GXTevColorArg d);
|
||||||
|
void GXSetTevAlphaIn(GXTevStageID stage, GXTevAlphaArg a, GXTevAlphaArg b, GXTevAlphaArg c, GXTevAlphaArg d);
|
||||||
|
void GXSetTevColorOp(GXTevStageID stage, GXTevOp op, GXTevBias bias, GXTevScale scale, GXBool clamp,
|
||||||
|
GXTevRegID out_reg);
|
||||||
|
void GXSetTevAlphaOp(GXTevStageID stage, GXTevOp op, GXTevBias bias, GXTevScale scale, GXBool clamp,
|
||||||
|
GXTevRegID out_reg);
|
||||||
|
void GXSetTevColor(GXTevRegID id, GXColor color);
|
||||||
|
void GXSetTevColorS10(GXTevRegID id, GXColorS10 color);
|
||||||
|
void GXSetTevKColor(GXTevKColorID id, GXColor color);
|
||||||
|
void GXSetTevKColorSel(GXTevStageID stage, GXTevKColorSel sel);
|
||||||
|
void GXSetTevKAlphaSel(GXTevStageID stage, GXTevKAlphaSel sel);
|
||||||
|
void GXSetTevSwapMode(GXTevStageID stage, GXTevSwapSel ras_sel, GXTevSwapSel tex_sel);
|
||||||
|
void GXSetTevSwapModeTable(GXTevSwapSel table, GXTevColorChan red, GXTevColorChan green, GXTevColorChan blue,
|
||||||
|
GXTevColorChan alpha);
|
||||||
|
void GXSetAlphaCompare(GXCompare comp0, u8 ref0, GXAlphaOp op, GXCompare comp1, u8 ref1);
|
||||||
|
void GXSetZTexture(GXZTexOp op, GXTexFmt fmt, u32 bias);
|
||||||
|
void GXSetTevOrder(GXTevStageID stage, GXTexCoordID coord, GXTexMapID map, GXChannelID color);
|
||||||
|
void GXSetNumTevStages(u8 nStages);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef DOLPHIN_GXTEXTURE_H
|
||||||
|
#define DOLPHIN_GXTEXTURE_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
#include <dolphin/gx/GXStruct.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void GXInitTexObj(GXTexObj* obj, const void* data, u16 width, u16 height, u32 format, GXTexWrapMode wrapS,
|
||||||
|
GXTexWrapMode wrapT, GXBool mipmap);
|
||||||
|
void GXInitTexObjCI(GXTexObj* obj, const void* data, u16 width, u16 height, GXCITexFmt format, GXTexWrapMode wrapS,
|
||||||
|
GXTexWrapMode wrapT, GXBool mipmap, u32 tlut);
|
||||||
|
void GXInitTexObjData(GXTexObj* obj, const void* data);
|
||||||
|
void GXInitTexObjLOD(GXTexObj* obj, GXTexFilter min_filt, GXTexFilter mag_filt, f32 min_lod, f32 max_lod, f32 lod_bias,
|
||||||
|
GXBool bias_clamp, GXBool do_edge_lod, GXAnisotropy max_aniso);
|
||||||
|
void GXLoadTexObj(GXTexObj* obj, GXTexMapID id);
|
||||||
|
u32 GXGetTexBufferSize(u16 width, u16 height, u32 format, GXBool mipmap, u8 max_lod);
|
||||||
|
void GXInvalidateTexAll();
|
||||||
|
void GXInitTexObjWrapMode(GXTexObj* obj, GXTexWrapMode s, GXTexWrapMode t);
|
||||||
|
void GXInitTlutObj(GXTlutObj* obj, const void* data, GXTlutFmt format, u16 entries);
|
||||||
|
void GXLoadTlut(const GXTlutObj* obj, GXTlut idx);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef DOLPHIN_GXTRANSFORM_H
|
||||||
|
#define DOLPHIN_GXTRANSFORM_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXEnum.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GX_PROJECTION_SZ 7
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
void GXSetProjection(const void* mtx, GXProjectionType type);
|
||||||
|
void GXLoadPosMtxImm(const void* mtx, u32 id);
|
||||||
|
void GXLoadNrmMtxImm(const void* mtx, u32 id);
|
||||||
|
void GXLoadTexMtxImm(const void* mtx, u32 id, GXTexMtxType type);
|
||||||
|
#else
|
||||||
|
void GXSetProjection(f32 mtx[4][4], GXProjectionType type);
|
||||||
|
void GXLoadPosMtxImm(f32 mtx[3][4], u32 id);
|
||||||
|
void GXLoadNrmMtxImm(f32 mtx[3][4], u32 id);
|
||||||
|
void GXLoadTexMtxImm(f32 mtx[][4], u32 id, GXTexMtxType type);
|
||||||
|
#endif
|
||||||
|
void GXSetViewport(f32 left, f32 top, f32 wd, f32 ht, f32 nearz, f32 farz);
|
||||||
|
void GXSetCurrentMtx(u32 id);
|
||||||
|
void GXSetViewportJitter(f32 left, f32 top, f32 wd, f32 ht, f32 nearz, f32 farz, u32 field);
|
||||||
|
void GXSetScissorBoxOffset(s32 x_off, s32 y_off);
|
||||||
|
void GXSetClipMode(GXClipMode mode);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,132 @@
|
||||||
|
#ifndef DOLPHIN_GXVERT_H
|
||||||
|
#define DOLPHIN_GXVERT_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GXFIFO_ADDR 0xCC008000
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
u8 u8;
|
||||||
|
u16 u16;
|
||||||
|
u32 u32;
|
||||||
|
u64 u64;
|
||||||
|
s8 s8;
|
||||||
|
s16 s16;
|
||||||
|
s32 s32;
|
||||||
|
s64 s64;
|
||||||
|
f32 f32;
|
||||||
|
f64 f64;
|
||||||
|
} PPCWGPipe;
|
||||||
|
|
||||||
|
#ifdef __MWERKS__
|
||||||
|
volatile PPCWGPipe GXWGFifo : GXFIFO_ADDR;
|
||||||
|
#else
|
||||||
|
#define GXWGFifo (*(volatile PPCWGPipe*)GXFIFO_ADDR)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
|
||||||
|
void GXPosition3f32(f32 x, f32 y, f32 z);
|
||||||
|
void GXPosition3u16(u16 x, u16 y, u16 z);
|
||||||
|
void GXPosition3s16(s16 x, s16 y, s16 z);
|
||||||
|
void GXPosition3u8(u8 x, u8 y, u8 z);
|
||||||
|
void GXPosition3s8(s8 x, s8 y, s8 z);
|
||||||
|
|
||||||
|
void GXPosition2f32(f32 x, f32 y);
|
||||||
|
void GXPosition2u16(u16 x, u16 y);
|
||||||
|
void GXPosition2s16(s16 x, s16 y);
|
||||||
|
void GXPosition2u8(u8 x, u8 y);
|
||||||
|
void GXPosition2s8(s8 x, s8 y);
|
||||||
|
|
||||||
|
void GXPosition1x16(u16 index);
|
||||||
|
void GXPosition1x8(u8 index);
|
||||||
|
|
||||||
|
void GXNormal3f32(f32 x, f32 y, f32 z);
|
||||||
|
void GXNormal3s16(s16 x, s16 y, s16 z);
|
||||||
|
void GXNormal3s8(s8 x, s8 y, s8 z);
|
||||||
|
|
||||||
|
void GXNormal1x16(u16 index);
|
||||||
|
void GXNormal1x8(u8 index);
|
||||||
|
|
||||||
|
void GXColor4u8(u8 r, u8 g, u8 b, u8 a);
|
||||||
|
|
||||||
|
void GXColor3u8(u8 r, u8 g, u8 b);
|
||||||
|
|
||||||
|
void GXColor1u32(u32 clr);
|
||||||
|
void GXColor1u16(u16 clr);
|
||||||
|
|
||||||
|
void GXColor1x16(u16 index);
|
||||||
|
void GXColor1x8(u8 index);
|
||||||
|
|
||||||
|
void GXTexCoord2f32(f32 s, f32 t);
|
||||||
|
void GXTexCoord2u16(u16 s, u16 t);
|
||||||
|
void GXTexCoord2s16(s16 s, s16 t);
|
||||||
|
void GXTexCoord2u8(u8 s, u8 t);
|
||||||
|
void GXTexCoord2s8(s8 s, s8 t);
|
||||||
|
|
||||||
|
void GXTexCoord1f32(f32 s, f32 t);
|
||||||
|
void GXTexCoord1u16(u16 s, u16 t);
|
||||||
|
void GXTexCoord1s16(s16 s, s16 t);
|
||||||
|
void GXTexCoord1u8(u8 s, u8 t);
|
||||||
|
void GXTexCoord1s8(s8 s, s8 t);
|
||||||
|
|
||||||
|
void GXTexCoord1x16(u16 index);
|
||||||
|
void GXTexCoord1x8(u8 index);
|
||||||
|
|
||||||
|
extern void GXEnd(void);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static inline void GXPosition2f32(const f32 x, const f32 y) {
|
||||||
|
GXWGFifo.f32 = x;
|
||||||
|
GXWGFifo.f32 = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void GXPosition3s16(const s16 x, const s16 y, const s16 z) {
|
||||||
|
GXWGFifo.s16 = x;
|
||||||
|
GXWGFifo.s16 = y;
|
||||||
|
GXWGFifo.s16 = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void GXPosition3f32(const f32 x, const f32 y, const f32 z) {
|
||||||
|
GXWGFifo.f32 = x;
|
||||||
|
GXWGFifo.f32 = y;
|
||||||
|
GXWGFifo.f32 = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void GXNormal3f32(const f32 x, const f32 y, const f32 z) {
|
||||||
|
GXWGFifo.f32 = x;
|
||||||
|
GXWGFifo.f32 = y;
|
||||||
|
GXWGFifo.f32 = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void GXColor4u8(const u8 r, const u8 g, const u8 b, const u8 a) {
|
||||||
|
GXWGFifo.u8 = r;
|
||||||
|
GXWGFifo.u8 = g;
|
||||||
|
GXWGFifo.u8 = b;
|
||||||
|
GXWGFifo.u8 = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void GXTexCoord2s16(const s16 u, const s16 v) {
|
||||||
|
GXWGFifo.s16 = u;
|
||||||
|
GXWGFifo.s16 = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void GXTexCoord2f32(const f32 u, const f32 v) {
|
||||||
|
GXWGFifo.f32 = u;
|
||||||
|
GXWGFifo.f32 = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void GXEnd(void) {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,114 @@
|
||||||
|
#ifndef DOLPHIN_PAD_H
|
||||||
|
#define DOLPHIN_PAD_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#define PAD_CHAN0 0
|
||||||
|
#define PAD_CHAN1 1
|
||||||
|
#define PAD_CHAN2 2
|
||||||
|
#define PAD_CHAN3 3
|
||||||
|
#define PAD_CHANMAX 4
|
||||||
|
|
||||||
|
#define PAD_MOTOR_STOP 0
|
||||||
|
#define PAD_MOTOR_RUMBLE 1
|
||||||
|
#define PAD_MOTOR_STOP_HARD 2
|
||||||
|
|
||||||
|
#define PAD_ERR_NONE 0
|
||||||
|
#define PAD_ERR_NO_CONTROLLER -1
|
||||||
|
#define PAD_ERR_NOT_READY -2
|
||||||
|
#define PAD_ERR_TRANSFER -3
|
||||||
|
|
||||||
|
#define PAD_BUTTON_LEFT 0x0001
|
||||||
|
#define PAD_BUTTON_RIGHT 0x0002
|
||||||
|
#define PAD_BUTTON_DOWN 0x0004
|
||||||
|
#define PAD_BUTTON_UP 0x0008
|
||||||
|
#define PAD_TRIGGER_Z 0x0010
|
||||||
|
#define PAD_TRIGGER_R 0x0020
|
||||||
|
#define PAD_TRIGGER_L 0x0040
|
||||||
|
#define PAD_BUTTON_A 0x0100
|
||||||
|
#define PAD_BUTTON_B 0x0200
|
||||||
|
#define PAD_BUTTON_X 0x0400
|
||||||
|
#define PAD_BUTTON_Y 0x0800
|
||||||
|
#define PAD_BUTTON_MENU 0x1000
|
||||||
|
#define PAD_BUTTON_START 0x1000
|
||||||
|
|
||||||
|
#define PAD_CHAN0_BIT 0x80000000
|
||||||
|
#define PAD_CHAN1_BIT 0x40000000
|
||||||
|
#define PAD_CHAN2_BIT 0x20000000
|
||||||
|
#define PAD_CHAN3_BIT 0x10000000
|
||||||
|
|
||||||
|
#define PADButtonDown(buttonLast, button) (((buttonLast) ^ (button)) & (button))
|
||||||
|
#define PADButtonUp(buttonLast, button) (((buttonLast) ^ (button)) & (buttonLast))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct PADStatus {
|
||||||
|
u16 button;
|
||||||
|
s8 stickX;
|
||||||
|
s8 stickY;
|
||||||
|
s8 substickX;
|
||||||
|
s8 substickY;
|
||||||
|
u8 triggerL;
|
||||||
|
u8 triggerR;
|
||||||
|
u8 analogA;
|
||||||
|
u8 analogB;
|
||||||
|
s8 err;
|
||||||
|
} PADStatus;
|
||||||
|
|
||||||
|
BOOL PADInit();
|
||||||
|
u32 PADRead(PADStatus* status);
|
||||||
|
BOOL PADReset(u32 mask);
|
||||||
|
BOOL PADRecalibrate(u32 mask);
|
||||||
|
void PADClamp(PADStatus* status);
|
||||||
|
void PADClampCircle(PADStatus* status);
|
||||||
|
void PADControlMotor(s32 chan, u32 cmd);
|
||||||
|
void PADSetSpec(u32 spec);
|
||||||
|
void PADControlAllMotors(const u32* cmdArr);
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
/* New API to facilitate controller interactions */
|
||||||
|
typedef struct PADDeadZones {
|
||||||
|
bool emulateTriggers;
|
||||||
|
bool useDeadzones;
|
||||||
|
u16 stickDeadZone;
|
||||||
|
u16 substickDeadZone;
|
||||||
|
u16 leftTriggerActivationZone;
|
||||||
|
u16 rightTriggerActivationZone;
|
||||||
|
} PADDeadZones;
|
||||||
|
|
||||||
|
typedef u16 PADButton;
|
||||||
|
|
||||||
|
typedef struct PADButtonMapping {
|
||||||
|
u32 nativeButton;
|
||||||
|
PADButton padButton;
|
||||||
|
} PADButtonMapping;
|
||||||
|
|
||||||
|
/* Returns the total number of controllers */
|
||||||
|
u32 PADCount();
|
||||||
|
/* Returns the controller name for the given index into the controller map */
|
||||||
|
const char* PADGetNameForControllerIndex(u32 idx);
|
||||||
|
void PADSetPortForIndex(u32 index, s32 port);
|
||||||
|
s32 PADGetIndexForPort(u32 port);
|
||||||
|
void PADGetVidPid(u32 port, u32* vid, u32* pid);
|
||||||
|
void PADClearPort(u32 port);
|
||||||
|
const char* PADGetName(u32 port);
|
||||||
|
void PADSetButtonMapping(u32 port, PADButtonMapping mapping);
|
||||||
|
void PADSetAllButtonMappings(u32 port, PADButtonMapping buttons[12]);
|
||||||
|
PADButtonMapping* PADGetButtonMappings(u32 port, u32* buttonCount);
|
||||||
|
void PADSerializeMappings();
|
||||||
|
PADDeadZones* PADGetDeadZones(u32 port);
|
||||||
|
const char* PADGetButtonName(PADButton);
|
||||||
|
const char* PADGetNativeButtonName(u32 button);
|
||||||
|
/* Returns any pressed native button */
|
||||||
|
s32 PADGetNativeButtonPressed(u32 port);
|
||||||
|
void PADRestoreDefaultMapping(u32 port);
|
||||||
|
void PADBlockInput(bool block);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,72 @@
|
||||||
|
#ifndef DOLPHIN_SI_H
|
||||||
|
#define DOLPHIN_SI_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#define SI_CHAN0 0
|
||||||
|
#define SI_CHAN1 1
|
||||||
|
#define SI_CHAN2 2
|
||||||
|
#define SI_CHAN3 3
|
||||||
|
#define SI_MAX_CHAN 4
|
||||||
|
|
||||||
|
#define SI_CHAN0_BIT 0x80000000
|
||||||
|
#define SI_CHAN1_BIT 0x40000000
|
||||||
|
#define SI_CHAN2_BIT 0x20000000
|
||||||
|
#define SI_CHAN3_BIT 0x10000000
|
||||||
|
#define SI_CHAN_BIT(chn) (SI_CHAN0_BIT >> (chn))
|
||||||
|
|
||||||
|
#define SI_ERROR_UNDER_RUN 0x0001
|
||||||
|
#define SI_ERROR_OVER_RUN 0x0002
|
||||||
|
#define SI_ERROR_COLLISION 0x0004
|
||||||
|
#define SI_ERROR_NO_RESPONSE 0x0008
|
||||||
|
#define SI_ERROR_WRST 0x0010
|
||||||
|
#define SI_ERROR_RDST 0x0020
|
||||||
|
#define SI_ERROR_UNKNOWN 0x0040
|
||||||
|
#define SI_ERROR_BUSY 0x0080
|
||||||
|
|
||||||
|
#define SI_TYPE_MASK 0x18000000u
|
||||||
|
#define SI_TYPE_N64 0x00000000u
|
||||||
|
#define SI_TYPE_DOLPHIN 0x08000000u
|
||||||
|
#define SI_TYPE_GC SI_TYPE_DOLPHIN
|
||||||
|
|
||||||
|
// GameCube specific
|
||||||
|
#define SI_GC_WIRELESS 0x80000000u
|
||||||
|
#define SI_GC_NOMOTOR 0x20000000u // no rumble motor
|
||||||
|
#define SI_GC_STANDARD 0x01000000u // dolphin standard controller
|
||||||
|
|
||||||
|
// WaveBird specific
|
||||||
|
#define SI_WIRELESS_RECEIVED 0x40000000u // 0: no wireless unit
|
||||||
|
#define SI_WIRELESS_IR 0x04000000u // 0: IR 1: RF
|
||||||
|
#define SI_WIRELESS_STATE 0x02000000u // 0: variable 1: fixed
|
||||||
|
#define SI_WIRELESS_ORIGIN 0x00200000u // 0: invalid 1: valid
|
||||||
|
#define SI_WIRELESS_FIX_ID 0x00100000u // 0: not fixed 1: fixed
|
||||||
|
#define SI_WIRELESS_TYPE 0x000f0000u
|
||||||
|
#define SI_WIRELESS_LITE_MASK 0x000c0000u // 0: normal 1: lite controller
|
||||||
|
#define SI_WIRELESS_LITE 0x00040000u // 0: normal 1: lite controller
|
||||||
|
#define SI_WIRELESS_CONT_MASK 0x00080000u // 0: non-controller 1: non-controller
|
||||||
|
#define SI_WIRELESS_CONT 0x00000000u
|
||||||
|
#define SI_WIRELESS_ID 0x00c0ff00u
|
||||||
|
#define SI_WIRELESS_TYPE_ID (SI_WIRELESS_TYPE | SI_WIRELESS_ID)
|
||||||
|
|
||||||
|
#define SI_N64_CONTROLLER (SI_TYPE_N64 | 0x05000000)
|
||||||
|
#define SI_N64_MIC (SI_TYPE_N64 | 0x00010000)
|
||||||
|
#define SI_N64_KEYBOARD (SI_TYPE_N64 | 0x00020000)
|
||||||
|
#define SI_N64_MOUSE (SI_TYPE_N64 | 0x02000000)
|
||||||
|
#define SI_GBA (SI_TYPE_N64 | 0x00040000)
|
||||||
|
#define SI_GC_CONTROLLER (SI_TYPE_GC | SI_GC_STANDARD)
|
||||||
|
#define SI_GC_RECEIVER (SI_TYPE_GC | SI_GC_WIRELESS)
|
||||||
|
#define SI_GC_WAVEBIRD (SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_STANDARD | SI_WIRELESS_STATE | SI_WIRELESS_FIX_ID)
|
||||||
|
#define SI_GC_KEYBOARD (SI_TYPE_GC | 0x00200000)
|
||||||
|
#define SI_GC_STEERING (SI_TYPE_GC | 0x00000000)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u32 SIProbe(s32 chan);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,75 @@
|
||||||
|
#ifndef DOLPHIN_TYPES_H
|
||||||
|
#define DOLPHIN_TYPES_H
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
#include <bits/wordsize.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef signed char s8;
|
||||||
|
typedef signed short int s16;
|
||||||
|
typedef signed int s32;
|
||||||
|
#if __WORDSIZE == 64
|
||||||
|
typedef signed long int s64;
|
||||||
|
#else
|
||||||
|
typedef signed long long int s64;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
typedef unsigned short int u16;
|
||||||
|
typedef unsigned int u32;
|
||||||
|
#if __WORDSIZE == 64
|
||||||
|
typedef unsigned long int u64;
|
||||||
|
#else
|
||||||
|
typedef unsigned long long int u64;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef volatile u8 vu8;
|
||||||
|
typedef volatile u16 vu16;
|
||||||
|
typedef volatile u32 vu32;
|
||||||
|
typedef volatile u64 vu64;
|
||||||
|
|
||||||
|
typedef volatile s8 vs8;
|
||||||
|
typedef volatile s16 vs16;
|
||||||
|
typedef volatile s32 vs32;
|
||||||
|
typedef volatile s64 vs64;
|
||||||
|
|
||||||
|
typedef float f32;
|
||||||
|
typedef double f64;
|
||||||
|
|
||||||
|
typedef volatile f32 vf32;
|
||||||
|
typedef volatile f64 vf64;
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
#include <stdbool.h>
|
||||||
|
typedef bool BOOL;
|
||||||
|
#define FALSE false
|
||||||
|
#define TRUE true
|
||||||
|
#else
|
||||||
|
typedef int BOOL;
|
||||||
|
#define FALSE 0
|
||||||
|
#define TRUE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TARGET_PC
|
||||||
|
#include <stddef.h>
|
||||||
|
#else
|
||||||
|
#define NULL 0
|
||||||
|
#endif
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#define nullptr NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__MWERKS__)
|
||||||
|
#define AT_ADDRESS(addr) : (addr)
|
||||||
|
#define ATTRIBUTE_ALIGN(num) __attribute__((aligned(num)))
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define AT_ADDRESS(addr) // was removed in GCC. define in linker script instead.
|
||||||
|
#define ATTRIBUTE_ALIGN(num) __attribute__((aligned(num)))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define AT_ADDRESS(addr)
|
||||||
|
#define ATTRIBUTE_ALIGN(num)
|
||||||
|
#else
|
||||||
|
#error unknown compiler
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef DOLPHIN_VI_H
|
||||||
|
#define DOLPHIN_VI_H
|
||||||
|
|
||||||
|
#include <dolphin/gx/GXStruct.h>
|
||||||
|
#include <dolphin/vifuncs.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void VIInit(void);
|
||||||
|
void VIConfigure(GXRenderModeObj *rm);
|
||||||
|
void VIFlush(void);
|
||||||
|
u32 VIGetTvFormat(void);
|
||||||
|
void VISetNextFrameBuffer(void *fb);
|
||||||
|
void VIWaitForRetrace(void);
|
||||||
|
void VISetBlack(BOOL black);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef DOLPHIN_VIFUNCS_H
|
||||||
|
#define DOLPHIN_VIFUNCS_H
|
||||||
|
|
||||||
|
#include <dolphin/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u32 VIGetNextField(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,210 @@
|
||||||
|
#include <aurora/aurora.h>
|
||||||
|
|
||||||
|
#include "gfx/common.hpp"
|
||||||
|
#include "imgui.hpp"
|
||||||
|
#include "internal.hpp"
|
||||||
|
#include "webgpu/gpu.hpp"
|
||||||
|
#include "window.hpp"
|
||||||
|
|
||||||
|
#include <SDL_filesystem.h>
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
namespace aurora {
|
||||||
|
static Module Log("aurora");
|
||||||
|
|
||||||
|
AuroraConfig g_config;
|
||||||
|
|
||||||
|
// GPU
|
||||||
|
using webgpu::g_device;
|
||||||
|
using webgpu::g_queue;
|
||||||
|
using webgpu::g_swapChain;
|
||||||
|
|
||||||
|
constexpr std::array PreferredBackendOrder{
|
||||||
|
#ifdef DAWN_ENABLE_BACKEND_D3D12
|
||||||
|
// BACKEND_D3D12,
|
||||||
|
#endif
|
||||||
|
#ifdef DAWN_ENABLE_BACKEND_METAL
|
||||||
|
BACKEND_METAL,
|
||||||
|
#endif
|
||||||
|
#ifdef DAWN_ENABLE_BACKEND_VULKAN
|
||||||
|
BACKEND_VULKAN,
|
||||||
|
#endif
|
||||||
|
#ifdef DAWN_ENABLE_BACKEND_DESKTOP_GL
|
||||||
|
BACKEND_OPENGL,
|
||||||
|
#endif
|
||||||
|
#ifdef DAWN_ENABLE_BACKEND_OPENGLES
|
||||||
|
BACKEND_OPENGLES,
|
||||||
|
#endif
|
||||||
|
#ifdef DAWN_ENABLE_BACKEND_NULL
|
||||||
|
BACKEND_NULL,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool g_initialFrame = false;
|
||||||
|
|
||||||
|
static AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexcept {
|
||||||
|
g_config = config;
|
||||||
|
if (g_config.appName == nullptr) {
|
||||||
|
g_config.appName = "Aurora";
|
||||||
|
}
|
||||||
|
if (g_config.configPath == nullptr) {
|
||||||
|
g_config.configPath = SDL_GetPrefPath(nullptr, g_config.appName);
|
||||||
|
}
|
||||||
|
if (g_config.msaa == 0) {
|
||||||
|
g_config.msaa = 1;
|
||||||
|
}
|
||||||
|
if (g_config.maxTextureAnisotropy == 0) {
|
||||||
|
g_config.maxTextureAnisotropy = 16;
|
||||||
|
}
|
||||||
|
window::initialize();
|
||||||
|
|
||||||
|
/* Attempt to create a window using the calling application's desired backend */
|
||||||
|
AuroraBackend selectedBackend = config.desiredBackend;
|
||||||
|
bool windowCreated = false;
|
||||||
|
if (selectedBackend != BACKEND_AUTO && window::create_window(selectedBackend)) {
|
||||||
|
if (webgpu::initialize(selectedBackend)) {
|
||||||
|
windowCreated = true;
|
||||||
|
} else {
|
||||||
|
window::destroy_window();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!windowCreated) {
|
||||||
|
for (const auto backendType : PreferredBackendOrder) {
|
||||||
|
selectedBackend = backendType;
|
||||||
|
if (!window::create_window(selectedBackend)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (webgpu::initialize(selectedBackend)) {
|
||||||
|
windowCreated = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
window::destroy_window();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!windowCreated) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Error creating window: {}"), SDL_GetError());
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize SDL_Renderer for ImGui when we can't use a Dawn backend
|
||||||
|
if (webgpu::g_backendType == WGPUBackendType_Null) {
|
||||||
|
if (!window::create_renderer()) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Failed to initialize SDL renderer: {}"), SDL_GetError());
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window::show_window();
|
||||||
|
gfx::initialize();
|
||||||
|
|
||||||
|
imgui::create_context();
|
||||||
|
const auto size = window::get_window_size();
|
||||||
|
Log.report(LOG_INFO, FMT_STRING("Using framebuffer size {}x{} scale {}"), size.fb_width, size.fb_height, size.scale);
|
||||||
|
if (g_config.imGuiInitCallback != nullptr) {
|
||||||
|
g_config.imGuiInitCallback(&size);
|
||||||
|
}
|
||||||
|
imgui::initialize();
|
||||||
|
|
||||||
|
if (aurora_begin_frame()) {
|
||||||
|
g_initialFrame = true;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.backend = selectedBackend,
|
||||||
|
.configPath = g_config.configPath,
|
||||||
|
.windowSize = size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static WGPUTextureView g_currentView = nullptr;
|
||||||
|
|
||||||
|
static void shutdown() noexcept {
|
||||||
|
if (g_currentView != nullptr) {
|
||||||
|
wgpuTextureViewRelease(g_currentView);
|
||||||
|
g_currentView = nullptr;
|
||||||
|
}
|
||||||
|
imgui::shutdown();
|
||||||
|
gfx::shutdown();
|
||||||
|
webgpu::shutdown();
|
||||||
|
window::shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const AuroraEvent* update() noexcept {
|
||||||
|
if (g_initialFrame) {
|
||||||
|
aurora_end_frame();
|
||||||
|
g_initialFrame = false;
|
||||||
|
}
|
||||||
|
const auto* events = window::poll_events();
|
||||||
|
imgui::new_frame(window::get_window_size());
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool begin_frame() noexcept {
|
||||||
|
g_currentView = wgpuSwapChainGetCurrentTextureView(g_swapChain);
|
||||||
|
if (!g_currentView) {
|
||||||
|
ImGui::EndFrame();
|
||||||
|
// Force swapchain recreation
|
||||||
|
const auto size = window::get_window_size();
|
||||||
|
webgpu::resize_swapchain(size.fb_width, size.fb_height, true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gfx::begin_frame();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void end_frame() noexcept {
|
||||||
|
const auto encoderDescriptor = WGPUCommandEncoderDescriptor{
|
||||||
|
.label = "Redraw encoder",
|
||||||
|
};
|
||||||
|
auto encoder = wgpuDeviceCreateCommandEncoder(g_device, &encoderDescriptor);
|
||||||
|
gfx::end_frame(encoder);
|
||||||
|
gfx::render(encoder);
|
||||||
|
{
|
||||||
|
const std::array attachments{
|
||||||
|
WGPURenderPassColorAttachment{
|
||||||
|
.view = g_currentView,
|
||||||
|
.loadOp = WGPULoadOp_Clear,
|
||||||
|
.storeOp = WGPUStoreOp_Store,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const WGPURenderPassDescriptor renderPassDescriptor{
|
||||||
|
.label = "Post render pass",
|
||||||
|
.colorAttachmentCount = attachments.size(),
|
||||||
|
.colorAttachments = attachments.data(),
|
||||||
|
};
|
||||||
|
auto pass = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDescriptor);
|
||||||
|
// Copy EFB -> XFB (swapchain)
|
||||||
|
wgpuRenderPassEncoderSetPipeline(pass, webgpu::g_CopyPipeline);
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(pass, 0, webgpu::g_CopyBindGroup, 0, nullptr);
|
||||||
|
wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
|
||||||
|
if (!g_initialFrame) {
|
||||||
|
// Render ImGui
|
||||||
|
imgui::render(pass);
|
||||||
|
}
|
||||||
|
wgpuRenderPassEncoderEnd(pass);
|
||||||
|
wgpuRenderPassEncoderRelease(pass);
|
||||||
|
}
|
||||||
|
const WGPUCommandBufferDescriptor cmdBufDescriptor{.label = "Redraw command buffer"};
|
||||||
|
const auto buffer = wgpuCommandEncoderFinish(encoder, &cmdBufDescriptor);
|
||||||
|
wgpuQueueSubmit(g_queue, 1, &buffer);
|
||||||
|
wgpuCommandBufferRelease(buffer);
|
||||||
|
wgpuCommandEncoderRelease(encoder);
|
||||||
|
wgpuSwapChainPresent(g_swapChain);
|
||||||
|
wgpuTextureViewRelease(g_currentView);
|
||||||
|
g_currentView = nullptr;
|
||||||
|
if (!g_initialFrame) {
|
||||||
|
ImGui::EndFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace aurora
|
||||||
|
|
||||||
|
// C API bindings
|
||||||
|
AuroraInfo aurora_initialize(int argc, char* argv[], const AuroraConfig* config) {
|
||||||
|
return aurora::initialize(argc, argv, *config);
|
||||||
|
}
|
||||||
|
void aurora_shutdown() { aurora::shutdown(); }
|
||||||
|
const AuroraEvent* aurora_update() { return aurora::update(); }
|
||||||
|
bool aurora_begin_frame() { return aurora::begin_frame(); }
|
||||||
|
void aurora_end_frame() { aurora::end_frame(); }
|
|
@ -0,0 +1,127 @@
|
||||||
|
#include "BackendBinding.hpp"
|
||||||
|
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_D3D12)
|
||||||
|
#include <dawn/native/D3D12Backend.h>
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_METAL)
|
||||||
|
#include <dawn/native/MetalBackend.h>
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_VULKAN)
|
||||||
|
#include <dawn/native/VulkanBackend.h>
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_OPENGL)
|
||||||
|
#include <SDL_video.h>
|
||||||
|
#include <dawn/native/OpenGLBackend.h>
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_NULL)
|
||||||
|
#include <dawn/native/NullBackend.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace aurora::webgpu::utils {
|
||||||
|
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_D3D12)
|
||||||
|
BackendBinding* CreateD3D12Binding(SDL_Window* window, WGPUDevice device);
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_METAL)
|
||||||
|
BackendBinding* CreateMetalBinding(SDL_Window* window, WGPUDevice device);
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_NULL)
|
||||||
|
BackendBinding* CreateNullBinding(SDL_Window* window, WGPUDevice device);
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_OPENGL)
|
||||||
|
BackendBinding* CreateOpenGLBinding(SDL_Window* window, WGPUDevice device);
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_VULKAN)
|
||||||
|
BackendBinding* CreateVulkanBinding(SDL_Window* window, WGPUDevice device);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BackendBinding::BackendBinding(SDL_Window* window, WGPUDevice device) : m_window(window), m_device(device) {}
|
||||||
|
|
||||||
|
bool DiscoverAdapter(dawn::native::Instance* instance, SDL_Window* window, WGPUBackendType type) {
|
||||||
|
switch (type) {
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_D3D12)
|
||||||
|
case WGPUBackendType_D3D12: {
|
||||||
|
dawn::native::d3d12::AdapterDiscoveryOptions options;
|
||||||
|
return instance->DiscoverAdapters(&options);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_METAL)
|
||||||
|
case WGPUBackendType_Metal: {
|
||||||
|
dawn::native::metal::AdapterDiscoveryOptions options;
|
||||||
|
return instance->DiscoverAdapters(&options);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_VULKAN)
|
||||||
|
case WGPUBackendType_Vulkan: {
|
||||||
|
dawn::native::vulkan::AdapterDiscoveryOptions options;
|
||||||
|
return instance->DiscoverAdapters(&options);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
|
||||||
|
case WGPUBackendType_OpenGL: {
|
||||||
|
SDL_GL_ResetAttributes();
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4);
|
||||||
|
SDL_GL_CreateContext(window);
|
||||||
|
auto getProc = reinterpret_cast<void* (*)(const char*)>(SDL_GL_GetProcAddress);
|
||||||
|
dawn::native::opengl::AdapterDiscoveryOptions adapterOptions;
|
||||||
|
adapterOptions.getProc = getProc;
|
||||||
|
return instance->DiscoverAdapters(&adapterOptions);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_OPENGLES)
|
||||||
|
case WGPUBackendType_OpenGLES: {
|
||||||
|
SDL_GL_ResetAttributes();
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||||
|
SDL_GL_CreateContext(window);
|
||||||
|
auto getProc = reinterpret_cast<void* (*)(const char*)>(SDL_GL_GetProcAddress);
|
||||||
|
dawn::native::opengl::AdapterDiscoveryOptionsES adapterOptions;
|
||||||
|
adapterOptions.getProc = getProc;
|
||||||
|
return instance->DiscoverAdapters(&adapterOptions);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_NULL)
|
||||||
|
case WGPUBackendType_Null:
|
||||||
|
instance->DiscoverDefaultAdapters();
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BackendBinding* CreateBinding(WGPUBackendType type, SDL_Window* window, WGPUDevice device) {
|
||||||
|
switch (type) {
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_D3D12)
|
||||||
|
case WGPUBackendType_D3D12:
|
||||||
|
return CreateD3D12Binding(window, device);
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_METAL)
|
||||||
|
case WGPUBackendType_Metal:
|
||||||
|
return CreateMetalBinding(window, device);
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_NULL)
|
||||||
|
case WGPUBackendType_Null:
|
||||||
|
return CreateNullBinding(window, device);
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
|
||||||
|
case WGPUBackendType_OpenGL:
|
||||||
|
return CreateOpenGLBinding(window, device);
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_OPENGLES)
|
||||||
|
case WGPUBackendType_OpenGLES:
|
||||||
|
return CreateOpenGLBinding(window, device);
|
||||||
|
#endif
|
||||||
|
#if defined(DAWN_ENABLE_BACKEND_VULKAN)
|
||||||
|
case WGPUBackendType_Vulkan:
|
||||||
|
return CreateVulkanBinding(window, device);
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace aurora::webgpu::utils
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <dawn/native/DawnNative.h>
|
||||||
|
#include <webgpu/webgpu.h>
|
||||||
|
|
||||||
|
struct SDL_Window;
|
||||||
|
|
||||||
|
namespace aurora::webgpu::utils {
|
||||||
|
|
||||||
|
class BackendBinding {
|
||||||
|
public:
|
||||||
|
virtual ~BackendBinding() = default;
|
||||||
|
|
||||||
|
virtual uint64_t GetSwapChainImplementation() = 0;
|
||||||
|
virtual WGPUTextureFormat GetPreferredSwapChainTextureFormat() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BackendBinding(SDL_Window* window, WGPUDevice device);
|
||||||
|
|
||||||
|
SDL_Window* m_window = nullptr;
|
||||||
|
WGPUDevice m_device = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool DiscoverAdapter(dawn::native::Instance* instance, SDL_Window* window, WGPUBackendType type);
|
||||||
|
BackendBinding* CreateBinding(WGPUBackendType type, SDL_Window* window, WGPUDevice device);
|
||||||
|
|
||||||
|
} // namespace aurora::webgpu::utils
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include "BackendBinding.hpp"
|
||||||
|
|
||||||
|
#include <SDL_syswm.h>
|
||||||
|
#include <dawn/native/D3D12Backend.h>
|
||||||
|
|
||||||
|
namespace aurora::webgpu::utils {
|
||||||
|
class D3D12Binding : public BackendBinding {
|
||||||
|
public:
|
||||||
|
D3D12Binding(SDL_Window* window, WGPUDevice device) : BackendBinding(window, device) {}
|
||||||
|
|
||||||
|
uint64_t GetSwapChainImplementation() override {
|
||||||
|
if (m_swapChainImpl.userData == nullptr) {
|
||||||
|
CreateSwapChainImpl();
|
||||||
|
}
|
||||||
|
return reinterpret_cast<uint64_t>(&m_swapChainImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
WGPUTextureFormat GetPreferredSwapChainTextureFormat() override {
|
||||||
|
if (m_swapChainImpl.userData == nullptr) {
|
||||||
|
CreateSwapChainImpl();
|
||||||
|
}
|
||||||
|
return dawn::native::d3d12::GetNativeSwapChainPreferredFormat(&m_swapChainImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DawnSwapChainImplementation m_swapChainImpl{};
|
||||||
|
|
||||||
|
void CreateSwapChainImpl() {
|
||||||
|
SDL_SysWMinfo wmInfo;
|
||||||
|
SDL_VERSION(&wmInfo.version);
|
||||||
|
SDL_GetWindowWMInfo(m_window, &wmInfo);
|
||||||
|
m_swapChainImpl = dawn::native::d3d12::CreateNativeSwapChainImpl(m_device, wmInfo.info.win.window);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BackendBinding* CreateD3D12Binding(SDL_Window* window, WGPUDevice device) { return new D3D12Binding(window, device); }
|
||||||
|
} // namespace aurora::webgpu::utils
|
|
@ -0,0 +1,108 @@
|
||||||
|
#include "BackendBinding.hpp"
|
||||||
|
|
||||||
|
#include <SDL_metal.h>
|
||||||
|
#include <dawn/native/MetalBackend.h>
|
||||||
|
|
||||||
|
#import <QuartzCore/CAMetalLayer.h>
|
||||||
|
|
||||||
|
template <typename T> DawnSwapChainImplementation CreateSwapChainImplementation(T *swapChain) {
|
||||||
|
DawnSwapChainImplementation impl = {};
|
||||||
|
impl.userData = swapChain;
|
||||||
|
impl.Init = [](void *userData, void *wsiContext) {
|
||||||
|
auto *ctx = static_cast<typename T::WSIContext *>(wsiContext);
|
||||||
|
reinterpret_cast<T *>(userData)->Init(ctx);
|
||||||
|
};
|
||||||
|
impl.Destroy = [](void *userData) { delete reinterpret_cast<T *>(userData); };
|
||||||
|
impl.Configure = [](void *userData, WGPUTextureFormat format, WGPUTextureUsage allowedUsage, uint32_t width,
|
||||||
|
uint32_t height) {
|
||||||
|
return static_cast<T *>(userData)->Configure(format, allowedUsage, width, height);
|
||||||
|
};
|
||||||
|
impl.GetNextTexture = [](void *userData, DawnSwapChainNextTexture *nextTexture) {
|
||||||
|
return static_cast<T *>(userData)->GetNextTexture(nextTexture);
|
||||||
|
};
|
||||||
|
impl.Present = [](void *userData) { return static_cast<T *>(userData)->Present(); };
|
||||||
|
return impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace aurora::webgpu::utils {
|
||||||
|
class SwapChainImplMTL {
|
||||||
|
public:
|
||||||
|
using WSIContext = DawnWSIContextMetal;
|
||||||
|
|
||||||
|
explicit SwapChainImplMTL(SDL_Window *window) : m_view(SDL_Metal_CreateView(window)) {}
|
||||||
|
|
||||||
|
~SwapChainImplMTL() { SDL_Metal_DestroyView(m_view); }
|
||||||
|
|
||||||
|
void Init(DawnWSIContextMetal *ctx) {
|
||||||
|
mMtlDevice = ctx->device;
|
||||||
|
mCommandQueue = ctx->queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DawnSwapChainError Configure(WGPUTextureFormat format, WGPUTextureUsage usage, uint32_t width, uint32_t height) {
|
||||||
|
if (format != WGPUTextureFormat_BGRA8Unorm) {
|
||||||
|
return "unsupported format";
|
||||||
|
}
|
||||||
|
assert(width > 0);
|
||||||
|
assert(height > 0);
|
||||||
|
|
||||||
|
CGSize size = {};
|
||||||
|
size.width = width;
|
||||||
|
size.height = height;
|
||||||
|
|
||||||
|
mLayer = (__bridge CAMetalLayer *)(SDL_Metal_GetLayer(m_view));
|
||||||
|
[mLayer setDevice:mMtlDevice];
|
||||||
|
[mLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
|
||||||
|
[mLayer setDrawableSize:size];
|
||||||
|
|
||||||
|
constexpr uint32_t kFramebufferOnlyTextureUsages = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_Present;
|
||||||
|
bool hasOnlyFramebufferUsages = (usage & (~kFramebufferOnlyTextureUsages)) == 0u;
|
||||||
|
if (hasOnlyFramebufferUsages) {
|
||||||
|
[mLayer setFramebufferOnly:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
return DAWN_SWAP_CHAIN_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture *nextTexture) {
|
||||||
|
mCurrentDrawable = [mLayer nextDrawable];
|
||||||
|
mCurrentTexture = mCurrentDrawable.texture;
|
||||||
|
nextTexture->texture.ptr = (__bridge void *)(mCurrentTexture);
|
||||||
|
return DAWN_SWAP_CHAIN_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
DawnSwapChainError Present() {
|
||||||
|
id<MTLCommandBuffer> commandBuffer = [mCommandQueue commandBuffer];
|
||||||
|
[commandBuffer presentDrawable:mCurrentDrawable];
|
||||||
|
[commandBuffer commit];
|
||||||
|
return DAWN_SWAP_CHAIN_NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL_MetalView m_view = nil;
|
||||||
|
id<MTLDevice> mMtlDevice = nil;
|
||||||
|
id<MTLCommandQueue> mCommandQueue = nil;
|
||||||
|
|
||||||
|
CAMetalLayer *mLayer = nullptr;
|
||||||
|
id<CAMetalDrawable> mCurrentDrawable = nil;
|
||||||
|
id<MTLTexture> mCurrentTexture = nil;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MetalBinding : public BackendBinding {
|
||||||
|
public:
|
||||||
|
MetalBinding(SDL_Window *window, WGPUDevice device) : BackendBinding(window, device) {}
|
||||||
|
|
||||||
|
uint64_t GetSwapChainImplementation() override {
|
||||||
|
if (m_swapChainImpl.userData == nullptr) {
|
||||||
|
m_swapChainImpl = CreateSwapChainImplementation(new SwapChainImplMTL(m_window));
|
||||||
|
}
|
||||||
|
return reinterpret_cast<uint64_t>(&m_swapChainImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { return WGPUTextureFormat_BGRA8Unorm; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
DawnSwapChainImplementation m_swapChainImpl{};
|
||||||
|
};
|
||||||
|
|
||||||
|
BackendBinding *CreateMetalBinding(SDL_Window *window, WGPUDevice device) { return new MetalBinding(window, device); }
|
||||||
|
} // namespace aurora::webgpu::utils
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "BackendBinding.hpp"
|
||||||
|
|
||||||
|
#include <dawn/native/NullBackend.h>
|
||||||
|
|
||||||
|
namespace aurora::webgpu::utils {
|
||||||
|
class NullBinding : public BackendBinding {
|
||||||
|
public:
|
||||||
|
NullBinding(SDL_Window* window, WGPUDevice device) : BackendBinding(window, device) {}
|
||||||
|
|
||||||
|
uint64_t GetSwapChainImplementation() override {
|
||||||
|
if (m_swapChainImpl.userData == nullptr) {
|
||||||
|
m_swapChainImpl = dawn::native::null::CreateNativeSwapChainImpl();
|
||||||
|
}
|
||||||
|
return reinterpret_cast<uint64_t>(&m_swapChainImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
WGPUTextureFormat GetPreferredSwapChainTextureFormat() override {
|
||||||
|
return WGPUTextureFormat_RGBA8Unorm;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DawnSwapChainImplementation m_swapChainImpl{};
|
||||||
|
};
|
||||||
|
|
||||||
|
BackendBinding* CreateNullBinding(SDL_Window* window, WGPUDevice device) { return new NullBinding(window, device); }
|
||||||
|
} // namespace aurora::webgpu::utils
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include "BackendBinding.hpp"
|
||||||
|
|
||||||
|
#include <SDL_video.h>
|
||||||
|
#include <dawn/native/OpenGLBackend.h>
|
||||||
|
|
||||||
|
namespace aurora::webgpu::utils {
|
||||||
|
class OpenGLBinding : public BackendBinding {
|
||||||
|
public:
|
||||||
|
OpenGLBinding(SDL_Window* window, WGPUDevice device) : BackendBinding(window, device) {}
|
||||||
|
|
||||||
|
uint64_t GetSwapChainImplementation() override {
|
||||||
|
if (m_swapChainImpl.userData == nullptr) {
|
||||||
|
CreateSwapChainImpl();
|
||||||
|
}
|
||||||
|
return reinterpret_cast<uint64_t>(&m_swapChainImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
WGPUTextureFormat GetPreferredSwapChainTextureFormat() override {
|
||||||
|
if (m_swapChainImpl.userData == nullptr) {
|
||||||
|
CreateSwapChainImpl();
|
||||||
|
}
|
||||||
|
return dawn::native::opengl::GetNativeSwapChainPreferredFormat(&m_swapChainImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DawnSwapChainImplementation m_swapChainImpl{};
|
||||||
|
|
||||||
|
void CreateSwapChainImpl() {
|
||||||
|
m_swapChainImpl = dawn::native::opengl::CreateNativeSwapChainImpl(
|
||||||
|
m_device, [](void* userdata) { SDL_GL_SwapWindow(static_cast<SDL_Window*>(userdata)); }, m_window);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BackendBinding* CreateOpenGLBinding(SDL_Window* window, WGPUDevice device) { return new OpenGLBinding(window, device); }
|
||||||
|
} // namespace aurora::webgpu::utils
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include "BackendBinding.hpp"
|
||||||
|
|
||||||
|
#include "../internal.hpp"
|
||||||
|
|
||||||
|
#include <SDL_vulkan.h>
|
||||||
|
#include <dawn/native/VulkanBackend.h>
|
||||||
|
|
||||||
|
namespace aurora::webgpu::utils {
|
||||||
|
static Module Log("aurora::webgpu::utils::VulkanBinding");
|
||||||
|
|
||||||
|
class VulkanBinding : public BackendBinding {
|
||||||
|
public:
|
||||||
|
VulkanBinding(SDL_Window* window, WGPUDevice device) : BackendBinding(window, device) {}
|
||||||
|
|
||||||
|
uint64_t GetSwapChainImplementation() override {
|
||||||
|
if (m_swapChainImpl.userData == nullptr) {
|
||||||
|
CreateSwapChainImpl();
|
||||||
|
}
|
||||||
|
return reinterpret_cast<uint64_t>(&m_swapChainImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
WGPUTextureFormat GetPreferredSwapChainTextureFormat() override {
|
||||||
|
if (m_swapChainImpl.userData == nullptr) {
|
||||||
|
CreateSwapChainImpl();
|
||||||
|
}
|
||||||
|
return dawn::native::vulkan::GetNativeSwapChainPreferredFormat(&m_swapChainImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DawnSwapChainImplementation m_swapChainImpl{};
|
||||||
|
|
||||||
|
void CreateSwapChainImpl() {
|
||||||
|
VkSurfaceKHR surface = VK_NULL_HANDLE;
|
||||||
|
if (SDL_Vulkan_CreateSurface(m_window, dawn::native::vulkan::GetInstance(m_device), &surface) != SDL_TRUE) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Failed to create Vulkan surface: {}"), SDL_GetError());
|
||||||
|
}
|
||||||
|
m_swapChainImpl = dawn::native::vulkan::CreateNativeSwapChainImpl(m_device, surface);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BackendBinding* CreateVulkanBinding(SDL_Window* window, WGPUDevice device) { return new VulkanBinding(window, device); }
|
||||||
|
} // namespace aurora::webgpu::utils
|
|
@ -0,0 +1,63 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
void GXSetNumIndStages(u8 num) { update_gx_state(g_gxState.numIndStages, num); }
|
||||||
|
|
||||||
|
void GXSetIndTexOrder(GXIndTexStageID indStage, GXTexCoordID texCoord, GXTexMapID texMap) {
|
||||||
|
auto& stage = g_gxState.indStages[indStage];
|
||||||
|
update_gx_state(stage.texCoordId, texCoord);
|
||||||
|
update_gx_state(stage.texMapId, texMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetIndTexCoordScale(GXIndTexStageID indStage, GXIndTexScale scaleS, GXIndTexScale scaleT) {
|
||||||
|
auto& stage = g_gxState.indStages[indStage];
|
||||||
|
update_gx_state(stage.scaleS, scaleS);
|
||||||
|
update_gx_state(stage.scaleT, scaleT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetIndTexMtx(GXIndTexMtxID id, const void* offset, s8 scaleExp) {
|
||||||
|
if (id < GX_ITM_0 || id > GX_ITM_2) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid ind tex mtx ID {}"), id);
|
||||||
|
}
|
||||||
|
update_gx_state(g_gxState.indTexMtxs[id - 1], {*reinterpret_cast<const aurora::Mat3x2<float>*>(offset), scaleExp});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevIndirect(GXTevStageID tevStage, GXIndTexStageID indStage, GXIndTexFormat fmt, GXIndTexBiasSel biasSel,
|
||||||
|
GXIndTexMtxID matrixSel, GXIndTexWrap wrapS, GXIndTexWrap wrapT, GXBool addPrev, GXBool indLod,
|
||||||
|
GXIndTexAlphaSel alphaSel) {
|
||||||
|
auto& stage = g_gxState.tevStages[tevStage];
|
||||||
|
update_gx_state(stage.indTexStage, indStage);
|
||||||
|
update_gx_state(stage.indTexFormat, fmt);
|
||||||
|
update_gx_state(stage.indTexBiasSel, biasSel);
|
||||||
|
update_gx_state(stage.indTexAlphaSel, alphaSel);
|
||||||
|
update_gx_state(stage.indTexMtxId, matrixSel);
|
||||||
|
update_gx_state(stage.indTexWrapS, wrapS);
|
||||||
|
update_gx_state(stage.indTexWrapT, wrapT);
|
||||||
|
update_gx_state(stage.indTexAddPrev, addPrev);
|
||||||
|
update_gx_state(stage.indTexUseOrigLOD, indLod);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevDirect(GXTevStageID stageId) {
|
||||||
|
auto& stage = g_gxState.tevStages[stageId];
|
||||||
|
// TODO is this right?
|
||||||
|
update_gx_state(stage.indTexStage, GX_INDTEXSTAGE0);
|
||||||
|
update_gx_state(stage.indTexFormat, GX_ITF_8);
|
||||||
|
update_gx_state(stage.indTexBiasSel, GX_ITB_NONE);
|
||||||
|
update_gx_state(stage.indTexAlphaSel, GX_ITBA_OFF);
|
||||||
|
update_gx_state(stage.indTexMtxId, GX_ITM_OFF);
|
||||||
|
update_gx_state(stage.indTexWrapS, GX_ITW_OFF);
|
||||||
|
update_gx_state(stage.indTexWrapT, GX_ITW_OFF);
|
||||||
|
update_gx_state(stage.indTexUseOrigLOD, false);
|
||||||
|
update_gx_state(stage.indTexAddPrev, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevIndWarp(GXTevStageID tevStage, GXIndTexStageID indStage, GXBool signedOffsets, GXBool replaceMode,
|
||||||
|
GXIndTexMtxID matrixSel) {
|
||||||
|
const auto wrap = replaceMode ? GX_ITW_0 : GX_ITW_OFF;
|
||||||
|
const auto biasSel = signedOffsets ? GX_ITB_STU : GX_ITB_NONE;
|
||||||
|
GXSetTevIndirect(tevStage, indStage, GX_ITF_8, biasSel, matrixSel, wrap, wrap, false, false, GX_ITBA_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXSetTevIndTile
|
||||||
|
// TODO GXSetTevIndBumpST
|
||||||
|
// TODO GXSetTevIndBumpXYZ
|
||||||
|
// TODO GXSetTevIndRepeat
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
void GXSetScissor(u32 left, u32 top, u32 width, u32 height) { aurora::gfx::set_scissor(left, top, width, height); }
|
||||||
|
|
||||||
|
void GXSetCullMode(GXCullMode mode) { update_gx_state(g_gxState.cullMode, mode); }
|
||||||
|
|
||||||
|
// TODO GXSetCoPlanar
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
#include "../gfx/model/shader.hpp"
|
||||||
|
|
||||||
|
void GXBeginDisplayList(void* list, u32 size) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GXEndDisplayList() {
|
||||||
|
// TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXCallDisplayList(const void* data, u32 nbytes) {
|
||||||
|
// TODO CElementGen needs fixing
|
||||||
|
for (const auto& type : aurora::gfx::gx::g_gxState.vtxDesc) {
|
||||||
|
if (type == GX_DIRECT) {
|
||||||
|
Log.report(LOG_WARNING, FMT_STRING("Direct attributes in surface config!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aurora::gfx::model::queue_surface(static_cast<const u8*>(data), nbytes);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
// TODO GXDrawCylinder
|
||||||
|
// TODO GXDrawTorus
|
||||||
|
|
||||||
|
void GXDrawSphere(u8 numMajor, u8 numMinor) { puts("GXDrawSphere is a stub"); }
|
||||||
|
|
||||||
|
// TODO GXDrawCube
|
||||||
|
// TODO GXDrawDodeca
|
||||||
|
// TODO GXDrawOctahedron
|
||||||
|
// TODO GXDrawIcosahedron
|
||||||
|
// TODO GXDrawSphere1
|
||||||
|
// TODO GXGenNormalTable
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
void GXDestroyTexObj(GXTexObj* obj_) {
|
||||||
|
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||||
|
obj->ref.reset();
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
static GXFifoObj* GPFifo;
|
||||||
|
static GXFifoObj* CPUFifo;
|
||||||
|
|
||||||
|
void GXGetGPStatus(GXBool* overhi, GXBool* underlow, GXBool* readIdle, GXBool* cmdIdle, GXBool* brkpt) {
|
||||||
|
*overhi = *underlow = *readIdle = *cmdIdle = *brkpt = false;
|
||||||
|
*readIdle = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXGetFifoStatus
|
||||||
|
|
||||||
|
void GXGetFifoPtrs(GXFifoObj* fifo, void** readPtr, void** writePtr) {
|
||||||
|
*readPtr = NULL;
|
||||||
|
*writePtr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GXFifoObj* GXGetCPUFifo() { return CPUFifo; }
|
||||||
|
|
||||||
|
GXFifoObj* GXGetGPFifo() { return GPFifo; }
|
||||||
|
|
||||||
|
// TODO GXGetFifoBase
|
||||||
|
// TODO GXGetFifoSize
|
||||||
|
// TODO GXGetFifoLimits
|
||||||
|
// TODO GXSetBreakPtCallback
|
||||||
|
// TODO GXEnableBreakPt
|
||||||
|
// TODO GXDisableBreakPt
|
||||||
|
|
||||||
|
void GXInitFifoBase(GXFifoObj* fifo, void* base, u32 size) {}
|
||||||
|
|
||||||
|
void GXInitFifoPtrs(GXFifoObj* fifo, void* readPtr, void* writePtr) {}
|
||||||
|
|
||||||
|
// TODO GXInitFifoLimits
|
||||||
|
|
||||||
|
void GXSetCPUFifo(GXFifoObj* fifo) { CPUFifo = fifo; }
|
||||||
|
|
||||||
|
void GXSetGPFifo(GXFifoObj* fifo) { GPFifo = fifo; }
|
||||||
|
|
||||||
|
void GXSaveCPUFifo(GXFifoObj* fifo) {}
|
||||||
|
|
||||||
|
// TODO GXSaveGPFifo
|
||||||
|
// TODO GXRedirectWriteGatherPipe
|
||||||
|
// TODO GXRestoreWriteGatherPipe
|
||||||
|
// TODO GXSetCurrentGXThread
|
||||||
|
// TODO GXGetCurrentGXThread
|
||||||
|
// TODO GXGetOverflowCount
|
||||||
|
// TODO GXResetOverflowCount
|
|
@ -0,0 +1,55 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
#include "../window.hpp"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
GXRenderModeObj GXNtsc480IntDf = {
|
||||||
|
VI_TVMODE_NTSC_INT, 640, 480, 480, 40, 0, 640, 480, VI_XFBMODE_DF, 0, 0,
|
||||||
|
};
|
||||||
|
GXRenderModeObj GXPal528IntDf = {
|
||||||
|
VI_TVMODE_PAL_INT, 704, 528, 480, 40, 0, 640, 480, VI_XFBMODE_DF, 0, 0,
|
||||||
|
};
|
||||||
|
GXRenderModeObj GXMpal480IntDf = {
|
||||||
|
VI_TVMODE_PAL_INT, 640, 480, 480, 40, 0, 640, 480, VI_XFBMODE_DF, 0, 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXAdjustForOverscan(GXRenderModeObj* rmin, GXRenderModeObj* rmout, u16 hor, u16 ver) {
|
||||||
|
*rmout = *rmin;
|
||||||
|
const auto size = aurora::window::get_window_size();
|
||||||
|
rmout->fbWidth = size.fb_width;
|
||||||
|
rmout->efbHeight = size.fb_height;
|
||||||
|
rmout->xfbHeight = size.fb_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetDispCopySrc(u16 left, u16 top, u16 wd, u16 ht) {}
|
||||||
|
|
||||||
|
void GXSetTexCopySrc(u16 left, u16 top, u16 wd, u16 ht) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetDispCopyDst(u16 wd, u16 ht) {}
|
||||||
|
|
||||||
|
void GXSetTexCopyDst(u16 wd, u16 ht, GXTexFmt fmt, GXBool mipmap) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXSetDispCopyFrame2Field
|
||||||
|
// TODO GXSetCopyClamp
|
||||||
|
|
||||||
|
u32 GXSetDispCopyYScale(f32 vscale) { return 0; }
|
||||||
|
|
||||||
|
void GXSetCopyClear(GXColor color, u32 depth) { update_gx_state(g_gxState.clearColor, from_gx_color(color)); }
|
||||||
|
|
||||||
|
void GXSetCopyFilter(GXBool aa, u8 sample_pattern[12][2], GXBool vf, u8 vfilter[7]) {}
|
||||||
|
|
||||||
|
void GXSetDispCopyGamma(GXGamma gamma) {}
|
||||||
|
|
||||||
|
void GXCopyDisp(void* dest, GXBool clear) {}
|
||||||
|
|
||||||
|
// TODO move GXCopyTex here
|
||||||
|
|
||||||
|
// TODO GXGetYScaleFactor
|
||||||
|
// TODO GXGetNumXfbLines
|
||||||
|
// TODO GXClearBoundingBox
|
||||||
|
// TODO GXReadBoundingBox
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
void GXSetVtxDesc(GXAttr attr, GXAttrType type) { update_gx_state(g_gxState.vtxDesc[attr], type); }
|
||||||
|
|
||||||
|
void GXSetVtxDescv(GXVtxDescList* list) {
|
||||||
|
g_gxState.vtxDesc.fill({});
|
||||||
|
while (list->attr != GX_VA_NULL) {
|
||||||
|
update_gx_state(g_gxState.vtxDesc[list->attr], list->type);
|
||||||
|
++list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXClearVtxDesc() { g_gxState.vtxDesc.fill({}); }
|
||||||
|
|
||||||
|
void GXSetVtxAttrFmt(GXVtxFmt vtxfmt, GXAttr attr, GXCompCnt cnt, GXCompType type, u8 frac) {
|
||||||
|
if (vtxfmt < GX_VTXFMT0 || vtxfmt >= GX_MAX_VTXFMT) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid vtxfmt {}"), vtxfmt);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
if (attr < GX_VA_PNMTXIDX || attr >= GX_VA_MAX_ATTR) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid attr {}"), attr);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
auto& fmt = g_gxState.vtxFmts[vtxfmt].attrs[attr];
|
||||||
|
update_gx_state(fmt.cnt, cnt);
|
||||||
|
update_gx_state(fmt.type, type);
|
||||||
|
update_gx_state(fmt.frac, frac);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXSetVtxAttrFmtv
|
||||||
|
|
||||||
|
void GXSetArray(GXAttr attr, const void* data, u32 size, u8 stride) {
|
||||||
|
auto& array = g_gxState.arrays[attr];
|
||||||
|
array.data = data;
|
||||||
|
array.size = size;
|
||||||
|
array.stride = stride;
|
||||||
|
array.cachedRange = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO move GXBegin, GXEnd here
|
||||||
|
|
||||||
|
void GXSetTexCoordGen2(GXTexCoordID dst, GXTexGenType type, GXTexGenSrc src, u32 mtx, GXBool normalize, u32 postMtx) {
|
||||||
|
if (dst < GX_TEXCOORD0 || dst > GX_TEXCOORD7) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid tex coord {}"), dst);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
update_gx_state(g_gxState.tcgs[dst],
|
||||||
|
{type, src, static_cast<GXTexMtx>(mtx), static_cast<GXPTTexMtx>(postMtx), normalize});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetNumTexGens(u8 num) { update_gx_state(g_gxState.numTexGens, num); }
|
||||||
|
|
||||||
|
// TODO GXInvalidateVtxCache
|
||||||
|
|
||||||
|
void GXSetLineWidth(u8 width, GXTexOffset offs) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXSetPointSize
|
||||||
|
// TODO GXEnableTexOffsets
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
#include "../gfx/texture.hpp"
|
||||||
|
|
||||||
|
// TODO GXGetVtxDesc
|
||||||
|
// TODO GXGetVtxDescv
|
||||||
|
// TODO GXGetVtxAttrFmtv
|
||||||
|
// TODO GXGetLineWidth
|
||||||
|
// TODO GXGetPointSize
|
||||||
|
|
||||||
|
void GXGetVtxAttrFmt(GXVtxFmt idx, GXAttr attr, GXCompCnt* compCnt, GXCompType* compType, u8* shift) {
|
||||||
|
const auto& fmt = g_gxState.vtxFmts[idx].attrs[attr];
|
||||||
|
*compCnt = fmt.cnt;
|
||||||
|
*compType = fmt.type;
|
||||||
|
*shift = fmt.frac;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXGetViewportv
|
||||||
|
|
||||||
|
void GXGetProjectionv(f32* p) {
|
||||||
|
const auto& mtx = g_gxState.origProj;
|
||||||
|
p[0] = static_cast<float>(g_gxState.projType);
|
||||||
|
p[1] = mtx.m0[0];
|
||||||
|
p[3] = mtx.m1[1];
|
||||||
|
p[5] = mtx.m2[2];
|
||||||
|
p[6] = mtx.m2[3];
|
||||||
|
if (g_gxState.projType == GX_ORTHOGRAPHIC) {
|
||||||
|
p[2] = mtx.m0[3];
|
||||||
|
p[4] = mtx.m1[3];
|
||||||
|
} else {
|
||||||
|
p[2] = mtx.m0[2];
|
||||||
|
p[4] = mtx.m1[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXGetScissor
|
||||||
|
// TODO GXGetCullMode
|
||||||
|
|
||||||
|
void GXGetLightAttnA(GXLightObj* light_, float* a0, float* a1, float* a2) {
|
||||||
|
auto* light = reinterpret_cast<const GXLightObj_*>(light_);
|
||||||
|
*a0 = light->a0;
|
||||||
|
*a1 = light->a1;
|
||||||
|
*a2 = light->a2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXGetLightAttnK(GXLightObj* light_, float* k0, float* k1, float* k2) {
|
||||||
|
auto* light = reinterpret_cast<const GXLightObj_*>(light_);
|
||||||
|
*k0 = light->k0;
|
||||||
|
*k1 = light->k1;
|
||||||
|
*k2 = light->k2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXGetLightPos(GXLightObj* light_, float* x, float* y, float* z) {
|
||||||
|
auto* light = reinterpret_cast<const GXLightObj_*>(light_);
|
||||||
|
*x = light->px;
|
||||||
|
*z = light->py;
|
||||||
|
*z = light->pz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXGetLightDir(GXLightObj* light_, float* nx, float* ny, float* nz) {
|
||||||
|
auto* light = reinterpret_cast<const GXLightObj_*>(light_);
|
||||||
|
*nx = -light->nx;
|
||||||
|
*ny = -light->ny;
|
||||||
|
*nz = -light->nz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXGetLightColor(GXLightObj* light_, GXColor* col) {
|
||||||
|
auto* light = reinterpret_cast<const GXLightObj_*>(light_);
|
||||||
|
*col = light->color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GXGetTexObjData(GXTexObj* tex_obj) {
|
||||||
|
return const_cast<void*>(reinterpret_cast<const GXTexObj_*>(tex_obj)->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 GXGetTexObjWidth(GXTexObj* tex_obj) { return reinterpret_cast<const GXTexObj_*>(tex_obj)->width; }
|
||||||
|
|
||||||
|
u16 GXGetTexObjHeight(GXTexObj* tex_obj) { return reinterpret_cast<const GXTexObj_*>(tex_obj)->height; }
|
||||||
|
|
||||||
|
GXTexFmt GXGetTexObjFmt(GXTexObj* tex_obj) {
|
||||||
|
return static_cast<GXTexFmt>(reinterpret_cast<const GXTexObj_*>(tex_obj)->fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
GXTexWrapMode GXGetTexObjWrapS(GXTexObj* tex_obj) { return reinterpret_cast<const GXTexObj_*>(tex_obj)->wrapS; }
|
||||||
|
|
||||||
|
GXTexWrapMode GXGetTexObjWrapT(GXTexObj* tex_obj) { return reinterpret_cast<const GXTexObj_*>(tex_obj)->wrapT; }
|
||||||
|
|
||||||
|
GXBool GXGetTexObjMipMap(GXTexObj* tex_obj) { return reinterpret_cast<const GXTexObj_*>(tex_obj)->hasMips; }
|
||||||
|
|
||||||
|
// TODO GXGetTexObjAll
|
||||||
|
// TODO GXGetTexObjMinFilt
|
||||||
|
// TODO GXGetTexObjMagFilt
|
||||||
|
// TODO GXGetTexObjMinLOD
|
||||||
|
// TODO GXGetTexObjMaxLOD
|
||||||
|
// TODO GXGetTexObjLODBias
|
||||||
|
// TODO GXGetTexObjBiasClamp
|
||||||
|
// TODO GXGetTexObjEdgeLOD
|
||||||
|
// TODO GXGetTexObjMaxAniso
|
||||||
|
// TODO GXGetTexObjLODAll
|
||||||
|
// TODO GXGetTexObjTlut
|
||||||
|
// TODO GXGetTlutObjData
|
||||||
|
// TODO GXGetTlutObjFmt
|
||||||
|
// TODO GXGetTlutObjNumEntries
|
||||||
|
// TODO GXGetTlutObjAll
|
||||||
|
// TODO GXGetTexRegionAll
|
||||||
|
// TODO GXGetTlutRegionAll
|
|
@ -0,0 +1,238 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
void GXInitLightAttn(GXLightObj* light_, float a0, float a1, float a2, float k0, float k1, float k2) {
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->a0 = a0;
|
||||||
|
light->a1 = a1;
|
||||||
|
light->a2 = a2;
|
||||||
|
light->k0 = k0;
|
||||||
|
light->k1 = k1;
|
||||||
|
light->k2 = k2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitLightAttnA(GXLightObj* light_, float a0, float a1, float a2) {
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->a0 = a0;
|
||||||
|
light->a1 = a1;
|
||||||
|
light->a2 = a2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitLightAttnK(GXLightObj* light_, float k0, float k1, float k2) {
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->k0 = k0;
|
||||||
|
light->k1 = k1;
|
||||||
|
light->k2 = k2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitLightSpot(GXLightObj* light_, float cutoff, GXSpotFn spotFn) {
|
||||||
|
if (cutoff <= 0.f || cutoff > 90.f) {
|
||||||
|
spotFn = GX_SP_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cr = std::cos((cutoff * M_PIF) / 180.f);
|
||||||
|
float a0 = 1.f;
|
||||||
|
float a1 = 0.f;
|
||||||
|
float a2 = 0.f;
|
||||||
|
switch (spotFn) {
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
case GX_SP_FLAT:
|
||||||
|
a0 = -1000.f * cr;
|
||||||
|
a1 = 1000.f;
|
||||||
|
a2 = 0.f;
|
||||||
|
break;
|
||||||
|
case GX_SP_COS:
|
||||||
|
a0 = -cr / (1.f - cr);
|
||||||
|
a1 = 1.f / (1.f - cr);
|
||||||
|
a2 = 0.f;
|
||||||
|
break;
|
||||||
|
case GX_SP_COS2:
|
||||||
|
a0 = 0.f;
|
||||||
|
a1 = -cr / (1.f - cr);
|
||||||
|
a2 = 1.f / (1.f - cr);
|
||||||
|
break;
|
||||||
|
case GX_SP_SHARP: {
|
||||||
|
const float d = (1.f - cr) * (1.f - cr);
|
||||||
|
a0 = cr * (cr - 2.f);
|
||||||
|
a1 = 2.f / d;
|
||||||
|
a2 = -1.f / d;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GX_SP_RING1: {
|
||||||
|
const float d = (1.f - cr) * (1.f - cr);
|
||||||
|
a0 = 4.f * cr / d;
|
||||||
|
a1 = 4.f * (1.f + cr) / d;
|
||||||
|
a2 = -4.f / d;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GX_SP_RING2: {
|
||||||
|
const float d = (1.f - cr) * (1.f - cr);
|
||||||
|
a0 = 1.f - 2.f * cr * cr / d;
|
||||||
|
a1 = 4.f * cr / d;
|
||||||
|
a2 = -2.f / d;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->a0 = a0;
|
||||||
|
light->a1 = a1;
|
||||||
|
light->a2 = a2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitLightDistAttn(GXLightObj* light_, float refDistance, float refBrightness, GXDistAttnFn distFunc) {
|
||||||
|
if (refDistance < 0.f || refBrightness < 0.f || refBrightness >= 1.f) {
|
||||||
|
distFunc = GX_DA_OFF;
|
||||||
|
}
|
||||||
|
float k0 = 1.f;
|
||||||
|
float k1 = 0.f;
|
||||||
|
float k2 = 0.f;
|
||||||
|
switch (distFunc) {
|
||||||
|
case GX_DA_GENTLE:
|
||||||
|
k0 = 1.0f;
|
||||||
|
k1 = (1.0f - refBrightness) / (refBrightness * refDistance);
|
||||||
|
k2 = 0.0f;
|
||||||
|
break;
|
||||||
|
case GX_DA_MEDIUM:
|
||||||
|
k0 = 1.0f;
|
||||||
|
k1 = 0.5f * (1.0f - refBrightness) / (refBrightness * refDistance);
|
||||||
|
k2 = 0.5f * (1.0f - refBrightness) / (refBrightness * refDistance * refDistance);
|
||||||
|
break;
|
||||||
|
case GX_DA_STEEP:
|
||||||
|
k0 = 1.0f;
|
||||||
|
k1 = 0.0f;
|
||||||
|
k2 = (1.0f - refBrightness) / (refBrightness * refDistance * refDistance);
|
||||||
|
break;
|
||||||
|
case GX_DA_OFF:
|
||||||
|
k0 = 1.0f;
|
||||||
|
k1 = 0.0f;
|
||||||
|
k2 = 0.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->k0 = k0;
|
||||||
|
light->k1 = k1;
|
||||||
|
light->k2 = k2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitLightPos(GXLightObj* light_, float x, float y, float z) {
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->px = x;
|
||||||
|
light->py = y;
|
||||||
|
light->pz = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitLightColor(GXLightObj* light_, GXColor col) {
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->color = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXLoadLightObjImm(GXLightObj* light_, GXLightID id) {
|
||||||
|
u32 idx = std::log2<u32>(id);
|
||||||
|
aurora::gfx::gx::Light realLight;
|
||||||
|
auto* light = reinterpret_cast<const GXLightObj_*>(light_);
|
||||||
|
realLight.pos = {light->px, light->py, light->pz};
|
||||||
|
realLight.dir = {light->nx, light->ny, light->nz};
|
||||||
|
realLight.cosAtt = {light->a0, light->a1, light->a2};
|
||||||
|
realLight.distAtt = {light->k0, light->k1, light->k2};
|
||||||
|
realLight.color = from_gx_color(light->color);
|
||||||
|
update_gx_state(g_gxState.lights[idx], realLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXLoadLightObjIndx
|
||||||
|
|
||||||
|
void GXSetChanAmbColor(GXChannelID id, GXColor color) {
|
||||||
|
if (id == GX_COLOR0A0) {
|
||||||
|
GXSetChanAmbColor(GX_COLOR0, color);
|
||||||
|
GXSetChanAmbColor(GX_ALPHA0, color);
|
||||||
|
return;
|
||||||
|
} else if (id == GX_COLOR1A1) {
|
||||||
|
GXSetChanAmbColor(GX_COLOR1, color);
|
||||||
|
GXSetChanAmbColor(GX_ALPHA1, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id < GX_COLOR0 || id > GX_ALPHA1) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("bad channel {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
update_gx_state(g_gxState.colorChannelState[id].ambColor, from_gx_color(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetChanMatColor(GXChannelID id, GXColor color) {
|
||||||
|
if (id == GX_COLOR0A0) {
|
||||||
|
GXSetChanMatColor(GX_COLOR0, color);
|
||||||
|
GXSetChanMatColor(GX_ALPHA0, color);
|
||||||
|
return;
|
||||||
|
} else if (id == GX_COLOR1A1) {
|
||||||
|
GXSetChanMatColor(GX_COLOR1, color);
|
||||||
|
GXSetChanMatColor(GX_ALPHA1, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id < GX_COLOR0 || id > GX_ALPHA1) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("bad channel {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
update_gx_state(g_gxState.colorChannelState[id].matColor, from_gx_color(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetNumChans(u8 num) { update_gx_state(g_gxState.numChans, num); }
|
||||||
|
|
||||||
|
void GXInitLightDir(GXLightObj* light_, float nx, float ny, float nz) {
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->nx = -nx;
|
||||||
|
light->ny = -ny;
|
||||||
|
light->nz = -nz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitSpecularDir(GXLightObj* light_, float nx, float ny, float nz) {
|
||||||
|
float hx = -nx;
|
||||||
|
float hy = -ny;
|
||||||
|
float hz = (-nz + 1.0f);
|
||||||
|
float mag = ((hx * hx) + (hy * hy) + (hz * hz));
|
||||||
|
if (mag != 0.0f) {
|
||||||
|
mag = 1.0f / sqrtf(mag);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->px = (nx * GX_LARGE_NUMBER);
|
||||||
|
light->py = (ny * GX_LARGE_NUMBER);
|
||||||
|
light->pz = (nz * GX_LARGE_NUMBER);
|
||||||
|
light->nx = hx * mag;
|
||||||
|
light->ny = hy * mag;
|
||||||
|
light->nz = hz * mag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitSpecularDirHA(GXLightObj* light_, float nx, float ny, float nz, float hx, float hy, float hz) {
|
||||||
|
auto* light = reinterpret_cast<GXLightObj_*>(light_);
|
||||||
|
light->px = (nx * GX_LARGE_NUMBER);
|
||||||
|
light->py = (ny * GX_LARGE_NUMBER);
|
||||||
|
light->pz = (nz * GX_LARGE_NUMBER);
|
||||||
|
light->nx = hx;
|
||||||
|
light->ny = hy;
|
||||||
|
light->nz = hz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetChanCtrl(GXChannelID id, bool lightingEnabled, GXColorSrc ambSrc, GXColorSrc matSrc, u32 lightState,
|
||||||
|
GXDiffuseFn diffFn, GXAttnFn attnFn) {
|
||||||
|
if (id == GX_COLOR0A0) {
|
||||||
|
GXSetChanCtrl(GX_COLOR0, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn);
|
||||||
|
GXSetChanCtrl(GX_ALPHA0, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn);
|
||||||
|
return;
|
||||||
|
} else if (id == GX_COLOR1A1) {
|
||||||
|
GXSetChanCtrl(GX_COLOR1, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn);
|
||||||
|
GXSetChanCtrl(GX_ALPHA1, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (id < GX_COLOR0 || id > GX_ALPHA1) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("bad channel {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
auto& chan = g_gxState.colorChannelConfig[id];
|
||||||
|
update_gx_state(chan.lightingEnabled, lightingEnabled);
|
||||||
|
update_gx_state(chan.ambSrc, ambSrc);
|
||||||
|
update_gx_state(chan.matSrc, matSrc);
|
||||||
|
update_gx_state(chan.diffFn, diffFn);
|
||||||
|
update_gx_state(chan.attnFn, attnFn);
|
||||||
|
update_gx_state(g_gxState.colorChannelState[id].lightMask, GX::LightMask{lightState});
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
static GXDrawDoneCallback DrawDoneCB = nullptr;
|
||||||
|
|
||||||
|
GXFifoObj* GXInit(void* base, u32 size) { return NULL; }
|
||||||
|
|
||||||
|
// TODO GXAbortFrame
|
||||||
|
// TODO GXSetDrawSync
|
||||||
|
// TODO GXReadDrawSync
|
||||||
|
// TODO GXSetDrawSyncCallback
|
||||||
|
|
||||||
|
void GXDrawDone() { DrawDoneCB(); }
|
||||||
|
|
||||||
|
void GXSetDrawDone() { DrawDoneCB(); }
|
||||||
|
|
||||||
|
// TODO GXWaitDrawDone
|
||||||
|
|
||||||
|
GXDrawDoneCallback GXSetDrawDoneCallback(GXDrawDoneCallback cb) {
|
||||||
|
GXDrawDoneCallback old = DrawDoneCB;
|
||||||
|
DrawDoneCB = cb;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXSetResetWritePipe
|
||||||
|
|
||||||
|
void GXFlush() {}
|
||||||
|
|
||||||
|
// TODO GXResetWriteGatherPipe
|
||||||
|
|
||||||
|
void GXPixModeSync() {}
|
||||||
|
|
||||||
|
void GXTexModeSync() {}
|
||||||
|
|
||||||
|
// TODO IsWriteGatherBufferEmpty
|
||||||
|
// TODO GXSetMisc
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
// TODO GXSetGPMetric
|
||||||
|
// TODO GXClearGPMetric
|
||||||
|
// TODO GXReadGPMetric
|
||||||
|
// TODO GXReadGP0Metric
|
||||||
|
// TODO GXReadGP1Metric
|
||||||
|
// TODO GXReadMemMetric
|
||||||
|
// TODO GXClearMemMetric
|
||||||
|
// TODO GXReadPixMetric
|
||||||
|
// TODO GXClearPixMetric
|
||||||
|
// TODO GXSetVCacheMetric
|
||||||
|
// TODO GXReadVCacheMetric
|
||||||
|
// TODO GXClearVCacheMetric
|
||||||
|
// TODO GXReadXfRasMetric
|
||||||
|
// TODO GXInitXfRasMetric
|
||||||
|
// TODO GXReadClksPerVtx
|
|
@ -0,0 +1,46 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
void GXSetFog(GXFogType type, float startZ, float endZ, float nearZ, float farZ, GXColor color) {
|
||||||
|
update_gx_state(g_gxState.fog, {type, startZ, endZ, nearZ, farZ, from_gx_color(color)});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetFogColor(GXColor color) { update_gx_state(g_gxState.fog.color, from_gx_color(color)); }
|
||||||
|
|
||||||
|
// TODO GXInitFogAdjTable
|
||||||
|
// TODO GXSetFogRangeAdj
|
||||||
|
|
||||||
|
void GXSetBlendMode(GXBlendMode mode, GXBlendFactor src, GXBlendFactor dst, GXLogicOp op) {
|
||||||
|
update_gx_state(g_gxState.blendMode, mode);
|
||||||
|
update_gx_state(g_gxState.blendFacSrc, src);
|
||||||
|
update_gx_state(g_gxState.blendFacDst, dst);
|
||||||
|
update_gx_state(g_gxState.blendOp, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetColorUpdate(GXBool enabled) { update_gx_state(g_gxState.colorUpdate, enabled); }
|
||||||
|
|
||||||
|
void GXSetAlphaUpdate(bool enabled) { update_gx_state(g_gxState.alphaUpdate, enabled); }
|
||||||
|
|
||||||
|
void GXSetZMode(bool compare_enable, GXCompare func, bool update_enable) {
|
||||||
|
update_gx_state(g_gxState.depthCompare, compare_enable);
|
||||||
|
update_gx_state(g_gxState.depthFunc, func);
|
||||||
|
update_gx_state(g_gxState.depthUpdate, update_enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetZCompLoc(GXBool before_tex) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetPixelFmt(GXPixelFmt pix_fmt, GXZFmt16 z_fmt) {}
|
||||||
|
|
||||||
|
void GXSetDither(GXBool dither) {}
|
||||||
|
|
||||||
|
void GXSetDstAlpha(bool enabled, u8 value) {
|
||||||
|
if (enabled) {
|
||||||
|
update_gx_state<u32>(g_gxState.dstAlpha, value);
|
||||||
|
} else {
|
||||||
|
update_gx_state(g_gxState.dstAlpha, UINT32_MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXSetFieldMask
|
||||||
|
// TODO GXSetFieldMode
|
|
@ -0,0 +1,111 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
void GXSetTevOp(GXTevStageID id, GXTevMode mode) {
|
||||||
|
GXTevColorArg inputColor = GX_CC_RASC;
|
||||||
|
GXTevAlphaArg inputAlpha = GX_CA_RASA;
|
||||||
|
if (id != GX_TEVSTAGE0) {
|
||||||
|
inputColor = GX_CC_CPREV;
|
||||||
|
inputAlpha = GX_CA_APREV;
|
||||||
|
}
|
||||||
|
switch (mode) {
|
||||||
|
case GX_MODULATE:
|
||||||
|
GXSetTevColorIn(id, GX_CC_ZERO, GX_CC_TEXC, inputColor, GX_CC_ZERO);
|
||||||
|
GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_TEXA, inputAlpha, GX_CA_ZERO);
|
||||||
|
break;
|
||||||
|
case GX_DECAL:
|
||||||
|
GXSetTevColorIn(id, inputColor, GX_CC_TEXC, GX_CC_TEXA, GX_CC_ZERO);
|
||||||
|
GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, inputAlpha);
|
||||||
|
break;
|
||||||
|
case GX_BLEND:
|
||||||
|
GXSetTevColorIn(id, inputColor, GX_CC_ONE, GX_CC_TEXC, GX_CC_ZERO);
|
||||||
|
GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_TEXA, inputAlpha, GX_CA_ZERO);
|
||||||
|
break;
|
||||||
|
case GX_REPLACE:
|
||||||
|
GXSetTevColorIn(id, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC);
|
||||||
|
GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA);
|
||||||
|
break;
|
||||||
|
case GX_PASSCLR:
|
||||||
|
GXSetTevColorIn(id, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, inputColor);
|
||||||
|
GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, inputAlpha);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
GXSetTevColorOp(id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
|
||||||
|
GXSetTevAlphaOp(id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevColorIn(GXTevStageID stageId, GXTevColorArg a, GXTevColorArg b, GXTevColorArg c, GXTevColorArg d) {
|
||||||
|
update_gx_state(g_gxState.tevStages[stageId].colorPass, {a, b, c, d});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevAlphaIn(GXTevStageID stageId, GXTevAlphaArg a, GXTevAlphaArg b, GXTevAlphaArg c, GXTevAlphaArg d) {
|
||||||
|
update_gx_state(g_gxState.tevStages[stageId].alphaPass, {a, b, c, d});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevColorOp(GXTevStageID stageId, GXTevOp op, GXTevBias bias, GXTevScale scale, bool clamp,
|
||||||
|
GXTevRegID outReg) {
|
||||||
|
update_gx_state(g_gxState.tevStages[stageId].colorOp, {op, bias, scale, outReg, clamp});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevAlphaOp(GXTevStageID stageId, GXTevOp op, GXTevBias bias, GXTevScale scale, bool clamp,
|
||||||
|
GXTevRegID outReg) {
|
||||||
|
update_gx_state(g_gxState.tevStages[stageId].alphaOp, {op, bias, scale, outReg, clamp});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevColor(GXTevRegID id, GXColor color) {
|
||||||
|
if (id < GX_TEVPREV || id > GX_TEVREG2) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("bad tevreg {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
update_gx_state(g_gxState.colorRegs[id], from_gx_color(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevColorS10(GXTevRegID id, GXColorS10 color) {
|
||||||
|
update_gx_state(g_gxState.colorRegs[id], aurora::Vec4<float>{
|
||||||
|
static_cast<float>(color.r) / 1023.f,
|
||||||
|
static_cast<float>(color.g) / 1023.f,
|
||||||
|
static_cast<float>(color.b) / 1023.f,
|
||||||
|
static_cast<float>(color.a) / 1023.f,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetAlphaCompare(GXCompare comp0, u8 ref0, GXAlphaOp op, GXCompare comp1, u8 ref1) {
|
||||||
|
update_gx_state(g_gxState.alphaCompare, {comp0, ref0, op, comp1, ref1});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevOrder(GXTevStageID id, GXTexCoordID tcid, GXTexMapID tmid, GXChannelID cid) {
|
||||||
|
auto& stage = g_gxState.tevStages[id];
|
||||||
|
update_gx_state(stage.texCoordId, tcid);
|
||||||
|
update_gx_state(stage.texMapId, tmid);
|
||||||
|
update_gx_state(stage.channelId, cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXSetZTexture
|
||||||
|
|
||||||
|
void GXSetNumTevStages(u8 num) { update_gx_state(g_gxState.numTevStages, num); }
|
||||||
|
|
||||||
|
void GXSetTevKColor(GXTevKColorID id, GXColor color) {
|
||||||
|
if (id >= GX_MAX_KCOLOR) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("bad kcolor {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
update_gx_state(g_gxState.kcolors[id], from_gx_color(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevKColorSel(GXTevStageID id, GXTevKColorSel sel) { update_gx_state(g_gxState.tevStages[id].kcSel, sel); }
|
||||||
|
|
||||||
|
void GXSetTevKAlphaSel(GXTevStageID id, GXTevKAlphaSel sel) { update_gx_state(g_gxState.tevStages[id].kaSel, sel); }
|
||||||
|
|
||||||
|
void GXSetTevSwapMode(GXTevStageID stageId, GXTevSwapSel rasSel, GXTevSwapSel texSel) {
|
||||||
|
auto& stage = g_gxState.tevStages[stageId];
|
||||||
|
update_gx_state(stage.tevSwapRas, rasSel);
|
||||||
|
update_gx_state(stage.tevSwapTex, texSel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetTevSwapModeTable(GXTevSwapSel id, GXTevColorChan red, GXTevColorChan green, GXTevColorChan blue,
|
||||||
|
GXTevColorChan alpha) {
|
||||||
|
if (id < GX_TEV_SWAP0 || id >= GX_MAX_TEVSWAP) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid tev swap sel {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
update_gx_state(g_gxState.tevSwapTable[id], {red, green, blue, alpha});
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
#include "../gfx/texture.hpp"
|
||||||
|
|
||||||
|
#include <absl/container/flat_hash_map.h>
|
||||||
|
|
||||||
|
static absl::flat_hash_map<void*, int> g_resolvedTexMap;
|
||||||
|
|
||||||
|
void GXInitTexObj(GXTexObj* obj_, const void* data, u16 width, u16 height, u32 format, GXTexWrapMode wrapS,
|
||||||
|
GXTexWrapMode wrapT, GXBool mipmap) {
|
||||||
|
memset(obj_, 0, sizeof(GXTexObj));
|
||||||
|
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||||
|
obj->data = data;
|
||||||
|
obj->width = width;
|
||||||
|
obj->height = height;
|
||||||
|
obj->fmt = format;
|
||||||
|
obj->wrapS = wrapS;
|
||||||
|
obj->wrapT = wrapT;
|
||||||
|
obj->hasMips = mipmap;
|
||||||
|
// TODO default values?
|
||||||
|
obj->minFilter = GX_LINEAR;
|
||||||
|
obj->magFilter = GX_LINEAR;
|
||||||
|
obj->minLod = 0.f;
|
||||||
|
obj->maxLod = 0.f;
|
||||||
|
obj->lodBias = 0.f;
|
||||||
|
obj->biasClamp = false;
|
||||||
|
obj->doEdgeLod = false;
|
||||||
|
obj->maxAniso = GX_ANISO_4;
|
||||||
|
obj->tlut = GX_TLUT0;
|
||||||
|
if (g_resolvedTexMap.contains(data)) {
|
||||||
|
obj->dataInvalidated = false; // TODO hack
|
||||||
|
} else {
|
||||||
|
obj->dataInvalidated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitTexObjCI(GXTexObj* obj_, const void* data, u16 width, u16 height, GXCITexFmt format, GXTexWrapMode wrapS,
|
||||||
|
GXTexWrapMode wrapT, GXBool mipmap, u32 tlut) {
|
||||||
|
memset(obj_, 0, sizeof(GXTexObj));
|
||||||
|
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||||
|
obj->data = data;
|
||||||
|
obj->width = width;
|
||||||
|
obj->height = height;
|
||||||
|
obj->fmt = static_cast<GXTexFmt>(format);
|
||||||
|
obj->wrapS = wrapS;
|
||||||
|
obj->wrapT = wrapT;
|
||||||
|
obj->hasMips = mipmap;
|
||||||
|
obj->tlut = static_cast<GXTlut>(tlut);
|
||||||
|
// TODO default values?
|
||||||
|
obj->minFilter = GX_LINEAR;
|
||||||
|
obj->magFilter = GX_LINEAR;
|
||||||
|
obj->minLod = 0.f;
|
||||||
|
obj->maxLod = 0.f;
|
||||||
|
obj->lodBias = 0.f;
|
||||||
|
obj->biasClamp = false;
|
||||||
|
obj->doEdgeLod = false;
|
||||||
|
obj->maxAniso = GX_ANISO_4;
|
||||||
|
obj->dataInvalidated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitTexObjLOD(GXTexObj* obj_, GXTexFilter minFilt, GXTexFilter magFilt, float minLod, float maxLod,
|
||||||
|
float lodBias, GXBool biasClamp, GXBool doEdgeLod, GXAnisotropy maxAniso) {
|
||||||
|
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||||
|
obj->minFilter = minFilt;
|
||||||
|
obj->magFilter = magFilt;
|
||||||
|
obj->minLod = minLod;
|
||||||
|
obj->maxLod = maxLod;
|
||||||
|
obj->lodBias = lodBias;
|
||||||
|
obj->biasClamp = biasClamp;
|
||||||
|
obj->doEdgeLod = doEdgeLod;
|
||||||
|
obj->maxAniso = maxAniso;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitTexObjData(GXTexObj* obj_, const void* data) {
|
||||||
|
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||||
|
obj->data = data;
|
||||||
|
obj->dataInvalidated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitTexObjWrapMode(GXTexObj* obj_, GXTexWrapMode wrapS, GXTexWrapMode wrapT) {
|
||||||
|
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||||
|
obj->wrapS = wrapS;
|
||||||
|
obj->wrapT = wrapT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitTexObjTlut(GXTexObj* obj_, u32 tlut) {
|
||||||
|
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||||
|
obj->tlut = static_cast<GXTlut>(tlut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXInitTexObjFilter
|
||||||
|
// TODO GXInitTexObjMaxLOD
|
||||||
|
// TODO GXInitTexObjMinLOD
|
||||||
|
// TODO GXInitTexObjLODBias
|
||||||
|
// TODO GXInitTexObjBiasClamp
|
||||||
|
// TODO GXInitTexObjEdgeLOD
|
||||||
|
// TODO GXInitTexObjMaxAniso
|
||||||
|
// TODO GXInitTexObjUserData
|
||||||
|
// TODO GXGetTexObjUserData
|
||||||
|
|
||||||
|
void GXLoadTexObj(GXTexObj* obj_, GXTexMapID id) {
|
||||||
|
auto* obj = reinterpret_cast<GXTexObj_*>(obj_);
|
||||||
|
if (!obj->ref) {
|
||||||
|
obj->ref = aurora::gfx::new_dynamic_texture_2d(obj->width, obj->height, u32(obj->maxLod) + 1, obj->fmt,
|
||||||
|
fmt::format(FMT_STRING("GXLoadTexObj_{}"), obj->fmt).c_str());
|
||||||
|
}
|
||||||
|
if (obj->dataInvalidated) {
|
||||||
|
aurora::gfx::write_texture(*obj->ref, {static_cast<const u8*>(obj->data), UINT32_MAX /* TODO */});
|
||||||
|
obj->dataInvalidated = false;
|
||||||
|
}
|
||||||
|
g_gxState.textures[id] = {*obj};
|
||||||
|
// TODO stateDirty?
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GXGetTexBufferSize(u16 width, u16 height, u32 fmt, GXBool mips, u8 maxLod) {
|
||||||
|
s32 shiftX = 0;
|
||||||
|
s32 shiftY = 0;
|
||||||
|
switch (fmt) {
|
||||||
|
case GX_TF_I4:
|
||||||
|
case GX_TF_C4:
|
||||||
|
case GX_TF_CMPR:
|
||||||
|
case GX_CTF_R4:
|
||||||
|
case GX_CTF_Z4:
|
||||||
|
shiftX = 3;
|
||||||
|
shiftY = 3;
|
||||||
|
break;
|
||||||
|
case GX_TF_I8:
|
||||||
|
case GX_TF_IA4:
|
||||||
|
case GX_TF_C8:
|
||||||
|
case GX_TF_Z8:
|
||||||
|
case GX_CTF_RA4:
|
||||||
|
case GX_CTF_A8:
|
||||||
|
case GX_CTF_R8:
|
||||||
|
case GX_CTF_G8:
|
||||||
|
case GX_CTF_B8:
|
||||||
|
case GX_CTF_Z8M:
|
||||||
|
case GX_CTF_Z8L:
|
||||||
|
shiftX = 3;
|
||||||
|
shiftY = 2;
|
||||||
|
break;
|
||||||
|
case GX_TF_IA8:
|
||||||
|
case GX_TF_RGB565:
|
||||||
|
case GX_TF_RGB5A3:
|
||||||
|
case GX_TF_RGBA8:
|
||||||
|
case GX_TF_C14X2:
|
||||||
|
case GX_TF_Z16:
|
||||||
|
case GX_TF_Z24X8:
|
||||||
|
case GX_CTF_RA8:
|
||||||
|
case GX_CTF_RG8:
|
||||||
|
case GX_CTF_GB8:
|
||||||
|
case GX_CTF_Z16L:
|
||||||
|
shiftX = 2;
|
||||||
|
shiftY = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
u32 bitSize = fmt == GX_TF_RGBA8 || fmt == GX_TF_Z24X8 ? 64 : 32;
|
||||||
|
u32 bufLen = 0;
|
||||||
|
if (mips) {
|
||||||
|
while (maxLod != 0) {
|
||||||
|
const u32 tileX = ((width + (1 << shiftX) - 1) >> shiftX);
|
||||||
|
const u32 tileY = ((height + (1 << shiftY) - 1) >> shiftY);
|
||||||
|
bufLen += bitSize * tileX * tileY;
|
||||||
|
|
||||||
|
if (width == 1 && height == 1) {
|
||||||
|
return bufLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
width = (width < 2) ? 1 : width / 2;
|
||||||
|
height = (height < 2) ? 1 : height / 2;
|
||||||
|
--maxLod;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const u32 tileX = ((width + (1 << shiftX) - 1) >> shiftX);
|
||||||
|
const u32 tileY = ((height + (1 << shiftY) - 1) >> shiftY);
|
||||||
|
bufLen = bitSize * tileX * tileY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXInitTlutObj(GXTlutObj* obj_, const void* data, GXTlutFmt format, u16 entries) {
|
||||||
|
memset(obj_, 0, sizeof(GXTlutObj));
|
||||||
|
GXTexFmt texFmt;
|
||||||
|
switch (format) {
|
||||||
|
case GX_TL_IA8:
|
||||||
|
texFmt = GX_TF_IA8;
|
||||||
|
break;
|
||||||
|
case GX_TL_RGB565:
|
||||||
|
texFmt = GX_TF_RGB565;
|
||||||
|
break;
|
||||||
|
case GX_TL_RGB5A3:
|
||||||
|
texFmt = GX_TF_RGB5A3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid tlut format {}"), format);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
auto* obj = reinterpret_cast<GXTlutObj_*>(obj_);
|
||||||
|
obj->ref = aurora::gfx::new_static_texture_2d(
|
||||||
|
entries, 1, 1, texFmt, aurora::ArrayRef{static_cast<const u8*>(data), static_cast<size_t>(entries) * 2},
|
||||||
|
"GXInitTlutObj");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXLoadTlut(const GXTlutObj* obj_, GXTlut idx) {
|
||||||
|
g_gxState.tluts[idx] = *reinterpret_cast<const GXTlutObj_*>(obj_);
|
||||||
|
// TODO stateDirty?
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXInitTexCacheRegion
|
||||||
|
// TODO GXInitTexPreLoadRegion
|
||||||
|
// TODO GXInitTlutRegion
|
||||||
|
// TODO GXInvalidateTexRegion
|
||||||
|
|
||||||
|
void GXInvalidateTexAll() {
|
||||||
|
// no-op?
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXPreLoadEntireTexture
|
||||||
|
// TODO GXSetTexRegionCallback
|
||||||
|
// TODO GXSetTlutRegionCallback
|
||||||
|
// TODO GXLoadTexObjPreLoaded
|
||||||
|
// TODO GXSetTexCoordScaleManually
|
||||||
|
// TODO GXSetTexCoordCylWrap
|
||||||
|
// TODO GXSetTexCoordBias
|
||||||
|
|
||||||
|
void GXCopyTex(void* dest, GXBool clear) {
|
||||||
|
// TODO
|
||||||
|
g_resolvedTexMap.emplace(dest, 0);
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
constexpr aurora::Mat4x4<float> DepthCorrect{
|
||||||
|
{1.f, 0.f, 0.f, 0.f},
|
||||||
|
{0.f, 1.f, 0.f, 0.f},
|
||||||
|
{0.f, 0.f, 1.f, 0.f},
|
||||||
|
{0.f, 0.f, 1.f, 1.f},
|
||||||
|
};
|
||||||
|
|
||||||
|
void GXSetProjection(const void* mtx_, GXProjectionType type) {
|
||||||
|
const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_);
|
||||||
|
g_gxState.origProj = mtx;
|
||||||
|
g_gxState.projType = type;
|
||||||
|
#ifdef AURORA_NATIVE_MATRIX
|
||||||
|
update_gx_state(g_gxState.proj, DepthCorrect * mtx);
|
||||||
|
#else
|
||||||
|
update_gx_state(g_gxState.proj, DepthCorrect * mtx.transpose());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXSetProjectionv
|
||||||
|
|
||||||
|
void GXLoadPosMtxImm(const void* mtx_, u32 id) {
|
||||||
|
if (id < GX_PNMTX0 || id > GX_PNMTX9) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid pn mtx {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
auto& state = g_gxState.pnMtx[id / 3];
|
||||||
|
#ifdef AURORA_NATIVE_MATRIX
|
||||||
|
const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_);
|
||||||
|
update_gx_state(state.pos, mtx);
|
||||||
|
#else
|
||||||
|
const auto* mtx = reinterpret_cast<const aurora::Mat3x4<float>*>(mtx_);
|
||||||
|
update_gx_state(state.pos, mtx->toTransposed4x4());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXLoadPosMtxIndx
|
||||||
|
|
||||||
|
void GXLoadNrmMtxImm(const void* mtx_, u32 id) {
|
||||||
|
if (id < GX_PNMTX0 || id > GX_PNMTX9) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid pn mtx {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
auto& state = g_gxState.pnMtx[id / 3];
|
||||||
|
#ifdef AURORA_NATIVE_MATRIX
|
||||||
|
const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_);
|
||||||
|
update_gx_state(state.nrm, mtx);
|
||||||
|
#else
|
||||||
|
const auto* mtx = reinterpret_cast<const aurora::Mat3x4<float>*>(mtx_);
|
||||||
|
update_gx_state(state.nrm, mtx->toTransposed4x4());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXLoadNrmMtxImm3x3
|
||||||
|
// TODO GXLoadNrmMtxIndx3x3
|
||||||
|
|
||||||
|
void GXSetCurrentMtx(u32 id) {
|
||||||
|
if (id < GX_PNMTX0 || id > GX_PNMTX9) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid pn mtx {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
update_gx_state(g_gxState.currentPnMtx, id / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXLoadTexMtxImm(const void* mtx_, u32 id, GXTexMtxType type) {
|
||||||
|
if ((id < GX_TEXMTX0 || id > GX_IDENTITY) && (id < GX_PTTEXMTX0 || id > GX_PTIDENTITY)) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid tex mtx {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
if (id >= GX_PTTEXMTX0) {
|
||||||
|
if (type != GX_MTX3x4) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid pt mtx type {}"), type);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
const auto idx = (id - GX_PTTEXMTX0) / 3;
|
||||||
|
#ifdef AURORA_NATIVE_MATRIX
|
||||||
|
const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_);
|
||||||
|
update_gx_state<aurora::Mat4x4<float>>(g_gxState.ptTexMtxs[idx], mtx);
|
||||||
|
#else
|
||||||
|
const auto& mtx = *reinterpret_cast<const aurora::Mat3x4<float>*>(mtx_);
|
||||||
|
update_gx_state<aurora::Mat4x4<float>>(g_gxState.ptTexMtxs[idx], mtx.toTransposed4x4());
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
const auto idx = (id - GX_TEXMTX0) / 3;
|
||||||
|
switch (type) {
|
||||||
|
case GX_MTX3x4: {
|
||||||
|
#ifdef AURORA_NATIVE_MATRIX
|
||||||
|
const auto& mtx = *reinterpret_cast<const aurora::Mat4x4<float>*>(mtx_);
|
||||||
|
update_gx_state<aurora::gfx::gx::TexMtxVariant>(g_gxState.texMtxs[idx], mtx);
|
||||||
|
#else
|
||||||
|
const auto& mtx = *reinterpret_cast<const aurora::Mat3x4<float>*>(mtx_);
|
||||||
|
update_gx_state<aurora::gfx::gx::TexMtxVariant>(g_gxState.texMtxs[idx], mtx.toTransposed4x4());
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GX_MTX2x4: {
|
||||||
|
const auto& mtx = *reinterpret_cast<const aurora::Mat4x2<float>*>(mtx_);
|
||||||
|
#ifdef AURORA_NATIVE_MATRIX
|
||||||
|
update_gx_state<aurora::gfx::gx::TexMtxVariant>(g_gxState.texMtxs[idx], mtx);
|
||||||
|
#else
|
||||||
|
update_gx_state<aurora::gfx::gx::TexMtxVariant>(g_gxState.texMtxs[idx], mtx.transpose());
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXLoadTexMtxIndx
|
||||||
|
// TODO GXProject
|
||||||
|
|
||||||
|
void GXSetViewport(float left, float top, float width, float height, float nearZ, float farZ) {
|
||||||
|
aurora::gfx::set_viewport(left, top, width, height, nearZ, farZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXSetViewportJitter(float left, float top, float width, float height, float nearZ, float farZ, u32 field) {
|
||||||
|
aurora::gfx::set_viewport(left, top, width, height, nearZ, farZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO GXSetZScaleOffset
|
||||||
|
// TODO GXSetScissorBoxOffset
|
||||||
|
// TODO GXSetClipMode
|
|
@ -0,0 +1,188 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
#include "../gfx/stream/shader.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
static inline GXAttr next_attr(size_t begin) {
|
||||||
|
auto iter = std::find_if(g_gxState.vtxDesc.begin() + begin, g_gxState.vtxDesc.end(),
|
||||||
|
[](const auto type) { return type != GX_NONE; });
|
||||||
|
if (begin > 0 && iter == g_gxState.vtxDesc.end()) {
|
||||||
|
// wrap around
|
||||||
|
iter = std::find_if(g_gxState.vtxDesc.begin(), g_gxState.vtxDesc.end(),
|
||||||
|
[](const auto type) { return type != GX_NONE; });
|
||||||
|
}
|
||||||
|
return GXAttr(iter - g_gxState.vtxDesc.begin());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct SStreamState {
|
||||||
|
GXPrimitive primitive;
|
||||||
|
u16 vertexCount = 0;
|
||||||
|
aurora::ByteBuffer vertexBuffer;
|
||||||
|
std::vector<u16> indices;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
GXAttr nextAttr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
explicit SStreamState(GXPrimitive primitive, u16 numVerts, u16 vertexSize) noexcept : primitive(primitive) {
|
||||||
|
vertexBuffer.reserve_extra(size_t(numVerts) * vertexSize);
|
||||||
|
if (numVerts > 3 && (primitive == GX_TRIANGLEFAN || primitive == GX_TRIANGLESTRIP)) {
|
||||||
|
indices.reserve((u32(numVerts) - 3) * 3 + 3);
|
||||||
|
} else if (numVerts > 4 && primitive == GX_QUADS) {
|
||||||
|
indices.reserve(u32(numVerts) / 4 * 6);
|
||||||
|
} else {
|
||||||
|
indices.reserve(numVerts);
|
||||||
|
}
|
||||||
|
#ifndef NDEBUG
|
||||||
|
nextAttr = next_attr(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::optional<SStreamState> sStreamState;
|
||||||
|
|
||||||
|
void GXBegin(GXPrimitive primitive, GXVtxFmt vtxFmt, u16 nVerts) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (sStreamState) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Stream began twice!"));
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
uint16_t vertexSize = 0;
|
||||||
|
for (GXAttr attr{}; const auto type : g_gxState.vtxDesc) {
|
||||||
|
if (type == GX_DIRECT) {
|
||||||
|
if (attr == GX_VA_POS || attr == GX_VA_NRM) {
|
||||||
|
vertexSize += 12;
|
||||||
|
} else if (attr == GX_VA_CLR0 || attr == GX_VA_CLR1) {
|
||||||
|
vertexSize += 16;
|
||||||
|
} else if (attr >= GX_VA_TEX0 && attr <= GX_VA_TEX7) {
|
||||||
|
vertexSize += 8;
|
||||||
|
} else {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("don't know how to handle attr {}"), attr);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
} else if (type == GX_INDEX8 || type == GX_INDEX16) {
|
||||||
|
vertexSize += 2;
|
||||||
|
}
|
||||||
|
attr = GXAttr(attr + 1);
|
||||||
|
}
|
||||||
|
if (vertexSize == 0) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("no vtx attributes enabled?"));
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
sStreamState.emplace(primitive, nVerts, vertexSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void check_attr_order(GXAttr attr) noexcept {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (!sStreamState) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Stream not started!"));
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
if (sStreamState->nextAttr != attr) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("bad attribute order: {}, expected {}"), attr, sStreamState->nextAttr);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
sStreamState->nextAttr = next_attr(attr + 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXPosition3f32(float x, float y, float z) {
|
||||||
|
check_attr_order(GX_VA_POS);
|
||||||
|
auto& state = *sStreamState;
|
||||||
|
state.vertexBuffer.append(&x, sizeof(float));
|
||||||
|
state.vertexBuffer.append(&y, sizeof(float));
|
||||||
|
state.vertexBuffer.append(&z, sizeof(float));
|
||||||
|
if (state.primitive == GX_TRIANGLES || state.vertexCount < 3) {
|
||||||
|
// pass
|
||||||
|
} else if (state.primitive == GX_TRIANGLEFAN) {
|
||||||
|
state.indices.push_back(0);
|
||||||
|
state.indices.push_back(state.vertexCount - 1);
|
||||||
|
} else if (state.primitive == GX_TRIANGLESTRIP) {
|
||||||
|
if ((state.vertexCount & 1) == 0) {
|
||||||
|
state.indices.push_back(state.vertexCount - 2);
|
||||||
|
state.indices.push_back(state.vertexCount - 1);
|
||||||
|
} else {
|
||||||
|
state.indices.push_back(state.vertexCount - 1);
|
||||||
|
state.indices.push_back(state.vertexCount - 2);
|
||||||
|
}
|
||||||
|
} else if (state.primitive == GX_QUADS) {
|
||||||
|
if ((state.vertexCount & 3) == 3) {
|
||||||
|
state.indices.push_back(state.vertexCount - 3);
|
||||||
|
state.indices.push_back(state.vertexCount - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.indices.push_back(state.vertexCount);
|
||||||
|
++state.vertexCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXPosition3s16(s16 x, s16 y, s16 z) {
|
||||||
|
// TODO frac
|
||||||
|
GXPosition3f32(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXNormal3f32(float x, float y, float z) {
|
||||||
|
check_attr_order(GX_VA_NRM);
|
||||||
|
sStreamState->vertexBuffer.append(&x, 4);
|
||||||
|
sStreamState->vertexBuffer.append(&y, 4);
|
||||||
|
sStreamState->vertexBuffer.append(&z, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXColor4f32(float r, float g, float b, float a) {
|
||||||
|
check_attr_order(GX_VA_CLR0);
|
||||||
|
sStreamState->vertexBuffer.append(&r, 4);
|
||||||
|
sStreamState->vertexBuffer.append(&g, 4);
|
||||||
|
sStreamState->vertexBuffer.append(&b, 4);
|
||||||
|
sStreamState->vertexBuffer.append(&a, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXColor4u8(u8 r, u8 g, u8 b, u8 a) {
|
||||||
|
GXColor4f32(static_cast<float>(r) / 255.f, static_cast<float>(g) / 255.f, static_cast<float>(b) / 255.f,
|
||||||
|
static_cast<float>(a) / 255.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXTexCoord2f32(float u, float v) {
|
||||||
|
check_attr_order(GX_VA_TEX0);
|
||||||
|
sStreamState->vertexBuffer.append(&u, 4);
|
||||||
|
sStreamState->vertexBuffer.append(&v, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXTexCoord2s16(s16 s, s16 t) {
|
||||||
|
// TODO frac
|
||||||
|
GXTexCoord2f32(s, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXPosition1x16(u16 idx) {
|
||||||
|
check_attr_order(GX_VA_POS);
|
||||||
|
// keep aligned
|
||||||
|
if (sStreamState->vertexBuffer.size() % 4 != 0) {
|
||||||
|
sStreamState->vertexBuffer.append_zeroes(4 - (sStreamState->vertexBuffer.size() % 4));
|
||||||
|
}
|
||||||
|
sStreamState->vertexBuffer.append(&idx, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GXEnd() {
|
||||||
|
if (sStreamState->vertexCount == 0) {
|
||||||
|
sStreamState.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto vertRange = aurora::gfx::push_verts(sStreamState->vertexBuffer.data(), sStreamState->vertexBuffer.size());
|
||||||
|
const auto indexRange = aurora::gfx::push_indices(aurora::ArrayRef{sStreamState->indices});
|
||||||
|
aurora::gfx::stream::PipelineConfig config{};
|
||||||
|
populate_pipeline_config(config, GX_TRIANGLES);
|
||||||
|
const auto info = aurora::gfx::gx::build_shader_info(config.shaderConfig);
|
||||||
|
const auto pipeline = aurora::gfx::pipeline_ref(config);
|
||||||
|
aurora::gfx::push_draw_command(aurora::gfx::stream::DrawData{
|
||||||
|
.pipeline = pipeline,
|
||||||
|
.vertRange = vertRange,
|
||||||
|
.uniformRange = build_uniform(info),
|
||||||
|
.indexRange = indexRange,
|
||||||
|
.indexCount = static_cast<uint32_t>(sStreamState->indices.size()),
|
||||||
|
.bindGroups = aurora::gfx::gx::build_bind_groups(info, config.shaderConfig, {}),
|
||||||
|
.dstAlpha = g_gxState.dstAlpha,
|
||||||
|
});
|
||||||
|
sStreamState.reset();
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../internal.hpp"
|
||||||
|
#include "../gfx/gx.hpp"
|
||||||
|
|
||||||
|
static aurora::Module Log("aurora::gx");
|
||||||
|
|
||||||
|
using aurora::gfx::gx::g_gxState;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline void update_gx_state(T& val, T newVal) {
|
||||||
|
if (val != newVal) {
|
||||||
|
val = std::move(newVal);
|
||||||
|
g_gxState.stateDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline aurora::Vec4<float> from_gx_color(GXColor color) {
|
||||||
|
return {
|
||||||
|
static_cast<float>(color.r) / 255.f,
|
||||||
|
static_cast<float>(color.g) / 255.f,
|
||||||
|
static_cast<float>(color.b) / 255.f,
|
||||||
|
static_cast<float>(color.a) / 255.f,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
extern "C" {
|
||||||
|
#include <dolphin/vi.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
void VIInit() {}
|
||||||
|
u32 VIGetTvFormat() { return 0; }
|
||||||
|
void VIFlush() {}
|
|
@ -0,0 +1,813 @@
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#include "../internal.hpp"
|
||||||
|
#include "../webgpu/gpu.hpp"
|
||||||
|
#include "model/shader.hpp"
|
||||||
|
#include "stream/shader.hpp"
|
||||||
|
#include "texture.hpp"
|
||||||
|
|
||||||
|
#include <absl/container/flat_hash_map.h>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <deque>
|
||||||
|
#include <fstream>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
|
namespace aurora::gfx {
|
||||||
|
static Module Log("aurora::gfx");
|
||||||
|
|
||||||
|
using webgpu::g_device;
|
||||||
|
using webgpu::g_queue;
|
||||||
|
|
||||||
|
#ifdef AURORA_GFX_DEBUG_GROUPS
|
||||||
|
std::vector<std::string> g_debugGroupStack;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr uint64_t UniformBufferSize = 3145728; // 3mb
|
||||||
|
constexpr uint64_t VertexBufferSize = 3145728; // 3mb
|
||||||
|
constexpr uint64_t IndexBufferSize = 1048576; // 1mb
|
||||||
|
constexpr uint64_t StorageBufferSize = 8388608; // 8mb
|
||||||
|
constexpr uint64_t TextureUploadSize = 25165824; // 24mb
|
||||||
|
|
||||||
|
constexpr uint64_t StagingBufferSize =
|
||||||
|
UniformBufferSize + VertexBufferSize + IndexBufferSize + StorageBufferSize + TextureUploadSize;
|
||||||
|
|
||||||
|
struct ShaderState {
|
||||||
|
stream::State stream;
|
||||||
|
model::State model;
|
||||||
|
};
|
||||||
|
struct ShaderDrawCommand {
|
||||||
|
ShaderType type;
|
||||||
|
union {
|
||||||
|
stream::DrawData stream;
|
||||||
|
model::DrawData model;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
enum class CommandType {
|
||||||
|
SetViewport,
|
||||||
|
SetScissor,
|
||||||
|
Draw,
|
||||||
|
};
|
||||||
|
struct Command {
|
||||||
|
CommandType type;
|
||||||
|
#ifdef AURORA_GFX_DEBUG_GROUPS
|
||||||
|
std::vector<std::string> debugGroupStack;
|
||||||
|
#endif
|
||||||
|
union Data {
|
||||||
|
struct SetViewportCommand {
|
||||||
|
float left;
|
||||||
|
float top;
|
||||||
|
float width;
|
||||||
|
float height;
|
||||||
|
float znear;
|
||||||
|
float zfar;
|
||||||
|
|
||||||
|
bool operator==(const SetViewportCommand& rhs) const {
|
||||||
|
return left == rhs.left && top == rhs.top && width == rhs.width && height == rhs.height && znear == rhs.znear &&
|
||||||
|
zfar == rhs.zfar;
|
||||||
|
}
|
||||||
|
bool operator!=(const SetViewportCommand& rhs) const { return !(*this == rhs); }
|
||||||
|
} setViewport;
|
||||||
|
struct SetScissorCommand {
|
||||||
|
uint32_t x;
|
||||||
|
uint32_t y;
|
||||||
|
uint32_t w;
|
||||||
|
uint32_t h;
|
||||||
|
|
||||||
|
bool operator==(const SetScissorCommand& rhs) const {
|
||||||
|
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
|
||||||
|
}
|
||||||
|
bool operator!=(const SetScissorCommand& rhs) const { return !(*this == rhs); }
|
||||||
|
} setScissor;
|
||||||
|
ShaderDrawCommand draw;
|
||||||
|
} data;
|
||||||
|
};
|
||||||
|
} // namespace aurora::gfx
|
||||||
|
|
||||||
|
namespace aurora {
|
||||||
|
// For types that we can't ensure are safe to hash with has_unique_object_representations,
|
||||||
|
// we create specialized methods to handle them. Note that these are highly dependent on
|
||||||
|
// the structure definition, which could easily change with Dawn updates.
|
||||||
|
template <>
|
||||||
|
inline HashType xxh3_hash(const WGPUBindGroupDescriptor& input, HashType seed) {
|
||||||
|
constexpr auto offset = sizeof(void*) * 2; // skip nextInChain, label
|
||||||
|
const auto hash = xxh3_hash_s(reinterpret_cast<const u8*>(&input) + offset,
|
||||||
|
sizeof(WGPUBindGroupDescriptor) - offset - sizeof(void*) /* skip entries */, seed);
|
||||||
|
return xxh3_hash_s(input.entries, sizeof(WGPUBindGroupEntry) * input.entryCount, hash);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline HashType xxh3_hash(const WGPUSamplerDescriptor& input, HashType seed) {
|
||||||
|
constexpr auto offset = sizeof(void*) * 2; // skip nextInChain, label
|
||||||
|
return xxh3_hash_s(reinterpret_cast<const u8*>(&input) + offset,
|
||||||
|
sizeof(WGPUSamplerDescriptor) - offset - 2 /* skip padding */, seed);
|
||||||
|
}
|
||||||
|
} // namespace aurora
|
||||||
|
|
||||||
|
namespace aurora::gfx {
|
||||||
|
using NewPipelineCallback = std::function<WGPURenderPipeline()>;
|
||||||
|
std::mutex g_pipelineMutex;
|
||||||
|
static bool g_hasPipelineThread = false;
|
||||||
|
static std::thread g_pipelineThread;
|
||||||
|
static std::atomic_bool g_pipelineThreadEnd;
|
||||||
|
static std::condition_variable g_pipelineCv;
|
||||||
|
static absl::flat_hash_map<PipelineRef, WGPURenderPipeline> g_pipelines;
|
||||||
|
static std::deque<std::pair<PipelineRef, NewPipelineCallback>> g_queuedPipelines;
|
||||||
|
static absl::flat_hash_map<BindGroupRef, WGPUBindGroup> g_cachedBindGroups;
|
||||||
|
static absl::flat_hash_map<SamplerRef, WGPUSampler> g_cachedSamplers;
|
||||||
|
std::atomic_uint32_t queuedPipelines;
|
||||||
|
std::atomic_uint32_t createdPipelines;
|
||||||
|
|
||||||
|
static ByteBuffer g_verts;
|
||||||
|
static ByteBuffer g_uniforms;
|
||||||
|
static ByteBuffer g_indices;
|
||||||
|
static ByteBuffer g_storage;
|
||||||
|
static ByteBuffer g_staticStorage;
|
||||||
|
static ByteBuffer g_textureUpload;
|
||||||
|
WGPUBuffer g_vertexBuffer;
|
||||||
|
WGPUBuffer g_uniformBuffer;
|
||||||
|
WGPUBuffer g_indexBuffer;
|
||||||
|
WGPUBuffer g_storageBuffer;
|
||||||
|
size_t g_staticStorageLastSize = 0;
|
||||||
|
static std::array<WGPUBuffer, 3> g_stagingBuffers;
|
||||||
|
static WGPUSupportedLimits g_cachedLimits;
|
||||||
|
|
||||||
|
static ShaderState g_state;
|
||||||
|
static PipelineRef g_currentPipeline;
|
||||||
|
|
||||||
|
using CommandList = std::vector<Command>;
|
||||||
|
struct ClipRect {
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
int32_t width;
|
||||||
|
int32_t height;
|
||||||
|
};
|
||||||
|
struct RenderPass {
|
||||||
|
u32 resolveTarget = UINT32_MAX;
|
||||||
|
ClipRect resolveRect;
|
||||||
|
Vec4<float> clearColor{0.f, 0.f, 0.f, 0.f};
|
||||||
|
CommandList commands;
|
||||||
|
bool clear = true;
|
||||||
|
};
|
||||||
|
static std::vector<RenderPass> g_renderPasses;
|
||||||
|
static u32 g_currentRenderPass = UINT32_MAX;
|
||||||
|
std::vector<TextureHandle> g_resolvedTextures;
|
||||||
|
std::vector<TextureUpload> g_textureUploads;
|
||||||
|
|
||||||
|
static ByteBuffer g_serializedPipelines{};
|
||||||
|
static u32 g_serializedPipelineCount = 0;
|
||||||
|
|
||||||
|
template <typename PipelineConfig>
|
||||||
|
static void serialize_pipeline_config(ShaderType type, const PipelineConfig& config) {
|
||||||
|
static_assert(std::has_unique_object_representations_v<PipelineConfig>);
|
||||||
|
g_serializedPipelines.append(&type, sizeof(type));
|
||||||
|
const u32 configSize = sizeof(config);
|
||||||
|
g_serializedPipelines.append(&configSize, sizeof(configSize));
|
||||||
|
g_serializedPipelines.append(&config, configSize);
|
||||||
|
++g_serializedPipelineCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PipelineConfig>
|
||||||
|
static PipelineRef find_pipeline(ShaderType type, const PipelineConfig& config, NewPipelineCallback&& cb,
|
||||||
|
bool serialize = true) {
|
||||||
|
PipelineRef hash = xxh3_hash(config, static_cast<HashType>(type));
|
||||||
|
bool found = false;
|
||||||
|
{
|
||||||
|
std::scoped_lock guard{g_pipelineMutex};
|
||||||
|
found = g_pipelines.contains(hash);
|
||||||
|
if (!found) {
|
||||||
|
if (g_hasPipelineThread) {
|
||||||
|
const auto ref =
|
||||||
|
std::find_if(g_queuedPipelines.begin(), g_queuedPipelines.end(), [=](auto v) { return v.first == hash; });
|
||||||
|
if (ref != g_queuedPipelines.end()) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g_pipelines.try_emplace(hash, cb());
|
||||||
|
if (serialize) {
|
||||||
|
serialize_pipeline_config(type, config);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
g_queuedPipelines.emplace_back(std::pair{hash, std::move(cb)});
|
||||||
|
if (serialize) {
|
||||||
|
serialize_pipeline_config(type, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
g_pipelineCv.notify_one();
|
||||||
|
queuedPipelines++;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void push_command(CommandType type, const Command::Data& data) {
|
||||||
|
if (g_currentRenderPass == UINT32_MAX) {
|
||||||
|
Log.report(LOG_WARNING, FMT_STRING("Dropping command {}"), magic_enum::enum_name(type));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_renderPasses[g_currentRenderPass].commands.push_back({
|
||||||
|
.type = type,
|
||||||
|
#ifdef AURORA_GFX_DEBUG_GROUPS
|
||||||
|
.debugGroupStack = g_debugGroupStack,
|
||||||
|
#endif
|
||||||
|
.data = data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void push_draw_command(ShaderDrawCommand data) { push_command(CommandType::Draw, Command::Data{.draw = data}); }
|
||||||
|
|
||||||
|
static Command::Data::SetViewportCommand g_cachedViewport;
|
||||||
|
void set_viewport(float left, float top, float width, float height, float znear, float zfar) noexcept {
|
||||||
|
Command::Data::SetViewportCommand cmd{left, top, width, height, znear, zfar};
|
||||||
|
if (cmd != g_cachedViewport) {
|
||||||
|
push_command(CommandType::SetViewport, Command::Data{.setViewport = cmd});
|
||||||
|
g_cachedViewport = cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static Command::Data::SetScissorCommand g_cachedScissor;
|
||||||
|
void set_scissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) noexcept {
|
||||||
|
Command::Data::SetScissorCommand cmd{x, y, w, h};
|
||||||
|
if (cmd != g_cachedScissor) {
|
||||||
|
push_command(CommandType::SetScissor, Command::Data{.setScissor = cmd});
|
||||||
|
g_cachedScissor = cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool operator==(const WGPUExtent3D& lhs, const WGPUExtent3D& rhs) {
|
||||||
|
return lhs.width == rhs.width && lhs.height == rhs.height && lhs.depthOrArrayLayers == rhs.depthOrArrayLayers;
|
||||||
|
}
|
||||||
|
static inline bool operator!=(const WGPUExtent3D& lhs, const WGPUExtent3D& rhs) { return !(lhs == rhs); }
|
||||||
|
|
||||||
|
void resolve_color(const ClipRect& rect, uint32_t bind, GXTexFmt fmt, bool clear_depth) noexcept {
|
||||||
|
if (g_resolvedTextures.size() < bind + 1) {
|
||||||
|
g_resolvedTextures.resize(bind + 1);
|
||||||
|
}
|
||||||
|
const WGPUExtent3D size{
|
||||||
|
.width = static_cast<uint32_t>(rect.width),
|
||||||
|
.height = static_cast<uint32_t>(rect.height),
|
||||||
|
.depthOrArrayLayers = 1,
|
||||||
|
};
|
||||||
|
if (!g_resolvedTextures[bind] || g_resolvedTextures[bind]->size != size) {
|
||||||
|
g_resolvedTextures[bind] = new_render_texture(rect.width, rect.height, fmt, "Resolved Texture");
|
||||||
|
}
|
||||||
|
auto& currentPass = g_renderPasses[g_currentRenderPass];
|
||||||
|
currentPass.resolveTarget = bind;
|
||||||
|
currentPass.resolveRect = rect;
|
||||||
|
auto& newPass = g_renderPasses.emplace_back();
|
||||||
|
newPass.clearColor = gx::g_gxState.clearColor;
|
||||||
|
newPass.clear = false; // TODO
|
||||||
|
++g_currentRenderPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const stream::State& get_state() {
|
||||||
|
return g_state.stream;
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
void push_draw_command(stream::DrawData data) {
|
||||||
|
push_draw_command(ShaderDrawCommand{.type = ShaderType::Stream, .stream = data});
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
PipelineRef pipeline_ref(stream::PipelineConfig config) {
|
||||||
|
return find_pipeline(ShaderType::Stream, config, [=]() { return create_pipeline(g_state.stream, config); });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void push_draw_command(model::DrawData data) {
|
||||||
|
push_draw_command(ShaderDrawCommand{.type = ShaderType::Model, .model = data});
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
PipelineRef pipeline_ref(model::PipelineConfig config) {
|
||||||
|
return find_pipeline(ShaderType::Model, config, [=]() { return create_pipeline(g_state.model, config); });
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pipeline_worker() {
|
||||||
|
bool hasMore = false;
|
||||||
|
while (true) {
|
||||||
|
std::pair<PipelineRef, NewPipelineCallback> cb;
|
||||||
|
{
|
||||||
|
std::unique_lock lock{g_pipelineMutex};
|
||||||
|
if (!hasMore) {
|
||||||
|
g_pipelineCv.wait(lock, [] { return !g_queuedPipelines.empty() || g_pipelineThreadEnd; });
|
||||||
|
}
|
||||||
|
if (g_pipelineThreadEnd) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cb = std::move(g_queuedPipelines.front());
|
||||||
|
}
|
||||||
|
auto result = cb.second();
|
||||||
|
// std::this_thread::sleep_for(std::chrono::milliseconds{1500});
|
||||||
|
{
|
||||||
|
std::scoped_lock lock{g_pipelineMutex};
|
||||||
|
if (!g_pipelines.try_emplace(cb.first, std::move(result)).second) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Duplicate pipeline {}"), cb.first);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
g_queuedPipelines.pop_front();
|
||||||
|
hasMore = !g_queuedPipelines.empty();
|
||||||
|
}
|
||||||
|
createdPipelines++;
|
||||||
|
queuedPipelines--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize() {
|
||||||
|
// No async pipelines for OpenGL (ES)
|
||||||
|
if (webgpu::g_backendType == WGPUBackendType_OpenGL || webgpu::g_backendType == WGPUBackendType_OpenGLES) {
|
||||||
|
g_hasPipelineThread = false;
|
||||||
|
} else {
|
||||||
|
g_pipelineThreadEnd = false;
|
||||||
|
g_pipelineThread = std::thread(pipeline_worker);
|
||||||
|
g_hasPipelineThread = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For uniform & storage buffer offset alignments
|
||||||
|
wgpuDeviceGetLimits(g_device, &g_cachedLimits);
|
||||||
|
|
||||||
|
const auto createBuffer = [](WGPUBuffer& out, WGPUBufferUsageFlags usage, uint64_t size, const char* label) {
|
||||||
|
if (size <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const WGPUBufferDescriptor descriptor{
|
||||||
|
.label = label,
|
||||||
|
.usage = usage,
|
||||||
|
.size = size,
|
||||||
|
};
|
||||||
|
out = wgpuDeviceCreateBuffer(g_device, &descriptor);
|
||||||
|
};
|
||||||
|
createBuffer(g_uniformBuffer, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, UniformBufferSize,
|
||||||
|
"Shared Uniform Buffer");
|
||||||
|
createBuffer(g_vertexBuffer, WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst, VertexBufferSize,
|
||||||
|
"Shared Vertex Buffer");
|
||||||
|
createBuffer(g_indexBuffer, WGPUBufferUsage_Index | WGPUBufferUsage_CopyDst, IndexBufferSize, "Shared Index Buffer");
|
||||||
|
createBuffer(g_storageBuffer, WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst, StorageBufferSize,
|
||||||
|
"Shared Storage Buffer");
|
||||||
|
for (int i = 0; i < g_stagingBuffers.size(); ++i) {
|
||||||
|
const auto label = fmt::format(FMT_STRING("Staging Buffer {}"), i);
|
||||||
|
createBuffer(g_stagingBuffers[i], WGPUBufferUsage_MapWrite | WGPUBufferUsage_CopySrc, StagingBufferSize,
|
||||||
|
label.c_str());
|
||||||
|
}
|
||||||
|
map_staging_buffer();
|
||||||
|
|
||||||
|
g_state.stream = stream::construct_state();
|
||||||
|
g_state.model = model::construct_state();
|
||||||
|
|
||||||
|
{
|
||||||
|
// Load serialized pipeline cache
|
||||||
|
std::string path = std::string{g_config.configPath} + "/pipeline_cache.bin";
|
||||||
|
std::ifstream file(path, std::ios::in | std::ios::binary | std::ios::ate);
|
||||||
|
if (file) {
|
||||||
|
const auto size = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
constexpr size_t headerSize = sizeof(g_serializedPipelineCount);
|
||||||
|
if (size != -1 && size > headerSize) {
|
||||||
|
g_serializedPipelines.append_zeroes(size_t(size) - headerSize);
|
||||||
|
file.read(reinterpret_cast<char*>(&g_serializedPipelineCount), headerSize);
|
||||||
|
file.read(reinterpret_cast<char*>(g_serializedPipelines.data()), size_t(size) - headerSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (g_serializedPipelineCount > 0) {
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset < g_serializedPipelines.size()) {
|
||||||
|
ShaderType type = *reinterpret_cast<const ShaderType*>(g_serializedPipelines.data() + offset);
|
||||||
|
offset += sizeof(ShaderType);
|
||||||
|
u32 size = *reinterpret_cast<const u32*>(g_serializedPipelines.data() + offset);
|
||||||
|
offset += sizeof(u32);
|
||||||
|
switch (type) {
|
||||||
|
case ShaderType::Stream: {
|
||||||
|
if (size != sizeof(stream::PipelineConfig)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto config = *reinterpret_cast<const stream::PipelineConfig*>(g_serializedPipelines.data() + offset);
|
||||||
|
if (config.version != gx::GXPipelineConfigVersion) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
find_pipeline(
|
||||||
|
type, config, [=]() { return stream::create_pipeline(g_state.stream, config); }, false);
|
||||||
|
} break;
|
||||||
|
case ShaderType::Model: {
|
||||||
|
if (size != sizeof(model::PipelineConfig)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto config = *reinterpret_cast<const model::PipelineConfig*>(g_serializedPipelines.data() + offset);
|
||||||
|
if (config.version != gx::GXPipelineConfigVersion) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
find_pipeline(
|
||||||
|
type, config, [=]() { return model::create_pipeline(g_state.model, config); }, false);
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_WARNING, FMT_STRING("Unknown pipeline type {}"), static_cast<int>(type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown() {
|
||||||
|
if (g_hasPipelineThread) {
|
||||||
|
g_pipelineThreadEnd = true;
|
||||||
|
g_pipelineCv.notify_all();
|
||||||
|
g_pipelineThread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Write serialized pipelines to file
|
||||||
|
const auto path = std::string{g_config.configPath} + "pipeline_cache.bin";
|
||||||
|
std::ofstream file(path, std::ios::out | std::ios::trunc | std::ios::binary);
|
||||||
|
if (file) {
|
||||||
|
file.write(reinterpret_cast<const char*>(&g_serializedPipelineCount), sizeof(g_serializedPipelineCount));
|
||||||
|
file.write(reinterpret_cast<const char*>(g_serializedPipelines.data()), g_serializedPipelines.size());
|
||||||
|
}
|
||||||
|
g_serializedPipelines.clear();
|
||||||
|
g_serializedPipelineCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gx::shutdown();
|
||||||
|
|
||||||
|
g_resolvedTextures.clear();
|
||||||
|
g_textureUploads.clear();
|
||||||
|
for (const auto& item : g_cachedBindGroups) {
|
||||||
|
wgpuBindGroupRelease(item.second);
|
||||||
|
}
|
||||||
|
g_cachedBindGroups.clear();
|
||||||
|
for (const auto& item : g_cachedSamplers) {
|
||||||
|
wgpuSamplerRelease(item.second);
|
||||||
|
}
|
||||||
|
g_cachedSamplers.clear();
|
||||||
|
for (const auto& item : g_pipelines) {
|
||||||
|
wgpuRenderPipelineRelease(item.second);
|
||||||
|
}
|
||||||
|
g_pipelines.clear();
|
||||||
|
g_queuedPipelines.clear();
|
||||||
|
if (g_vertexBuffer != nullptr) {
|
||||||
|
wgpuBufferDestroy(g_vertexBuffer);
|
||||||
|
g_vertexBuffer = nullptr;
|
||||||
|
}
|
||||||
|
if (g_uniformBuffer != nullptr) {
|
||||||
|
wgpuBufferDestroy(g_uniformBuffer);
|
||||||
|
g_uniformBuffer = nullptr;
|
||||||
|
}
|
||||||
|
if (g_indexBuffer != nullptr) {
|
||||||
|
wgpuBufferDestroy(g_indexBuffer);
|
||||||
|
g_indexBuffer = nullptr;
|
||||||
|
}
|
||||||
|
if (g_storageBuffer != nullptr) {
|
||||||
|
wgpuBufferDestroy(g_storageBuffer);
|
||||||
|
g_storageBuffer = nullptr;
|
||||||
|
}
|
||||||
|
for (auto& item : g_stagingBuffers) {
|
||||||
|
if (item != nullptr) {
|
||||||
|
wgpuBufferDestroy(item);
|
||||||
|
}
|
||||||
|
item = nullptr;
|
||||||
|
}
|
||||||
|
g_renderPasses.clear();
|
||||||
|
g_currentRenderPass = UINT32_MAX;
|
||||||
|
|
||||||
|
g_state = {};
|
||||||
|
|
||||||
|
queuedPipelines = 0;
|
||||||
|
createdPipelines = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t currentStagingBuffer = 0;
|
||||||
|
static bool bufferMapped = false;
|
||||||
|
void map_staging_buffer() {
|
||||||
|
bufferMapped = false;
|
||||||
|
wgpuBufferMapAsync(
|
||||||
|
g_stagingBuffers[currentStagingBuffer], WGPUMapMode_Write, 0, StagingBufferSize,
|
||||||
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
||||||
|
if (status == WGPUBufferMapAsyncStatus_DestroyedBeforeCallback) {
|
||||||
|
return;
|
||||||
|
} else if (status != WGPUBufferMapAsyncStatus_Success) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Buffer mapping failed: {}"), status);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
*static_cast<bool*>(userdata) = true;
|
||||||
|
},
|
||||||
|
&bufferMapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_frame() {
|
||||||
|
while (!bufferMapped) {
|
||||||
|
wgpuDeviceTick(g_device);
|
||||||
|
}
|
||||||
|
size_t bufferOffset = 0;
|
||||||
|
auto& stagingBuf = g_stagingBuffers[currentStagingBuffer];
|
||||||
|
const auto mapBuffer = [&](ByteBuffer& buf, uint64_t size) {
|
||||||
|
if (size <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buf = ByteBuffer{static_cast<u8*>(wgpuBufferGetMappedRange(stagingBuf, bufferOffset, size)),
|
||||||
|
static_cast<size_t>(size)};
|
||||||
|
bufferOffset += size;
|
||||||
|
};
|
||||||
|
mapBuffer(g_verts, VertexBufferSize);
|
||||||
|
mapBuffer(g_uniforms, UniformBufferSize);
|
||||||
|
mapBuffer(g_indices, IndexBufferSize);
|
||||||
|
mapBuffer(g_storage, StorageBufferSize);
|
||||||
|
mapBuffer(g_textureUpload, TextureUploadSize);
|
||||||
|
|
||||||
|
g_renderPasses.emplace_back();
|
||||||
|
g_renderPasses[0].clearColor = gx::g_gxState.clearColor;
|
||||||
|
g_currentRenderPass = 0;
|
||||||
|
// push_command(CommandType::SetViewport, Command::Data{.setViewport = g_cachedViewport});
|
||||||
|
// push_command(CommandType::SetScissor, Command::Data{.setScissor = g_cachedScissor});
|
||||||
|
}
|
||||||
|
|
||||||
|
// for imgui debug
|
||||||
|
size_t g_lastVertSize;
|
||||||
|
size_t g_lastUniformSize;
|
||||||
|
size_t g_lastIndexSize;
|
||||||
|
size_t g_lastStorageSize;
|
||||||
|
|
||||||
|
void end_frame(WGPUCommandEncoder cmd) {
|
||||||
|
uint64_t bufferOffset = 0;
|
||||||
|
const auto writeBuffer = [&](ByteBuffer& buf, WGPUBuffer& out, uint64_t size, std::string_view label) {
|
||||||
|
const auto writeSize = buf.size(); // Only need to copy this many bytes
|
||||||
|
if (writeSize > 0) {
|
||||||
|
wgpuCommandEncoderCopyBufferToBuffer(cmd, g_stagingBuffers[currentStagingBuffer], bufferOffset, out, 0,
|
||||||
|
writeSize);
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
bufferOffset += size;
|
||||||
|
return writeSize;
|
||||||
|
};
|
||||||
|
wgpuBufferUnmap(g_stagingBuffers[currentStagingBuffer]);
|
||||||
|
g_lastVertSize = writeBuffer(g_verts, g_vertexBuffer, VertexBufferSize, "Vertex");
|
||||||
|
g_lastUniformSize = writeBuffer(g_uniforms, g_uniformBuffer, UniformBufferSize, "Uniform");
|
||||||
|
g_lastIndexSize = writeBuffer(g_indices, g_indexBuffer, IndexBufferSize, "Index");
|
||||||
|
g_lastStorageSize = writeBuffer(g_storage, g_storageBuffer, StorageBufferSize, "Storage");
|
||||||
|
{
|
||||||
|
// Perform texture copies
|
||||||
|
for (const auto& item : g_textureUploads) {
|
||||||
|
const WGPUImageCopyBuffer buf{
|
||||||
|
.layout =
|
||||||
|
WGPUTextureDataLayout{
|
||||||
|
.offset = item.layout.offset + bufferOffset,
|
||||||
|
.bytesPerRow = ALIGN(item.layout.bytesPerRow, 256),
|
||||||
|
.rowsPerImage = item.layout.rowsPerImage,
|
||||||
|
},
|
||||||
|
.buffer = g_stagingBuffers[currentStagingBuffer],
|
||||||
|
};
|
||||||
|
wgpuCommandEncoderCopyBufferToTexture(cmd, &buf, &item.tex, &item.size);
|
||||||
|
}
|
||||||
|
g_textureUploads.clear();
|
||||||
|
g_textureUpload.clear();
|
||||||
|
}
|
||||||
|
currentStagingBuffer = (currentStagingBuffer + 1) % g_stagingBuffers.size();
|
||||||
|
map_staging_buffer();
|
||||||
|
g_currentRenderPass = UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(WGPUCommandEncoder cmd) {
|
||||||
|
for (u32 i = 0; i < g_renderPasses.size(); ++i) {
|
||||||
|
const auto& passInfo = g_renderPasses[i];
|
||||||
|
bool finalPass = i == g_renderPasses.size() - 1;
|
||||||
|
if (finalPass && passInfo.resolveTarget != UINT32_MAX) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Final render pass must not have resolve target"));
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
const std::array attachments{
|
||||||
|
WGPURenderPassColorAttachment{
|
||||||
|
.view = webgpu::g_frameBuffer.view,
|
||||||
|
.resolveTarget = webgpu::g_graphicsConfig.msaaSamples > 1 ? webgpu::g_frameBufferResolved.view : nullptr,
|
||||||
|
.loadOp = passInfo.clear ? WGPULoadOp_Clear : WGPULoadOp_Load,
|
||||||
|
.storeOp = WGPUStoreOp_Store,
|
||||||
|
.clearColor = {NAN, NAN, NAN, NAN},
|
||||||
|
.clearValue =
|
||||||
|
{
|
||||||
|
.r = passInfo.clearColor.x(),
|
||||||
|
.g = passInfo.clearColor.y(),
|
||||||
|
.b = passInfo.clearColor.z(),
|
||||||
|
.a = passInfo.clearColor.w(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const WGPURenderPassDepthStencilAttachment depthStencilAttachment{
|
||||||
|
.view = webgpu::g_depthBuffer.view,
|
||||||
|
.depthLoadOp = passInfo.clear ? WGPULoadOp_Clear : WGPULoadOp_Load,
|
||||||
|
.depthStoreOp = WGPUStoreOp_Store,
|
||||||
|
.clearDepth = NAN,
|
||||||
|
.depthClearValue = 1.f,
|
||||||
|
};
|
||||||
|
const auto label = fmt::format(FMT_STRING("Render pass {}"), i);
|
||||||
|
const WGPURenderPassDescriptor renderPassDescriptor{
|
||||||
|
.label = label.c_str(),
|
||||||
|
.colorAttachmentCount = attachments.size(),
|
||||||
|
.colorAttachments = attachments.data(),
|
||||||
|
.depthStencilAttachment = &depthStencilAttachment,
|
||||||
|
};
|
||||||
|
auto pass = wgpuCommandEncoderBeginRenderPass(cmd, &renderPassDescriptor);
|
||||||
|
render_pass(pass, i);
|
||||||
|
wgpuRenderPassEncoderEnd(pass);
|
||||||
|
wgpuRenderPassEncoderRelease(pass);
|
||||||
|
|
||||||
|
if (passInfo.resolveTarget != UINT32_MAX) {
|
||||||
|
WGPUImageCopyTexture src{
|
||||||
|
.origin =
|
||||||
|
WGPUOrigin3D{
|
||||||
|
.x = static_cast<uint32_t>(passInfo.resolveRect.x),
|
||||||
|
.y = static_cast<uint32_t>(passInfo.resolveRect.y),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (webgpu::g_graphicsConfig.msaaSamples > 1) {
|
||||||
|
src.texture = webgpu::g_frameBufferResolved.texture;
|
||||||
|
} else {
|
||||||
|
src.texture = webgpu::g_frameBuffer.texture;
|
||||||
|
}
|
||||||
|
auto& target = g_resolvedTextures[passInfo.resolveTarget];
|
||||||
|
const WGPUImageCopyTexture dst{
|
||||||
|
.texture = target->texture,
|
||||||
|
};
|
||||||
|
const WGPUExtent3D size{
|
||||||
|
.width = static_cast<uint32_t>(passInfo.resolveRect.width),
|
||||||
|
.height = static_cast<uint32_t>(passInfo.resolveRect.height),
|
||||||
|
.depthOrArrayLayers = 1,
|
||||||
|
};
|
||||||
|
wgpuCommandEncoderCopyTextureToTexture(cmd, &src, &dst, &size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_renderPasses.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_pass(WGPURenderPassEncoder pass, u32 idx) {
|
||||||
|
g_currentPipeline = UINTPTR_MAX;
|
||||||
|
#ifdef AURORA_GFX_DEBUG_GROUPS
|
||||||
|
std::vector<std::string> lastDebugGroupStack;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (const auto& cmd : g_renderPasses[idx].commands) {
|
||||||
|
#ifdef AURORA_GFX_DEBUG_GROUPS
|
||||||
|
{
|
||||||
|
size_t firstDiff = lastDebugGroupStack.size();
|
||||||
|
for (size_t i = 0; i < lastDebugGroupStack.size(); ++i) {
|
||||||
|
if (i >= cmd.debugGroupStack.size() || cmd.debugGroupStack[i] != lastDebugGroupStack[i]) {
|
||||||
|
firstDiff = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t i = firstDiff; i < lastDebugGroupStack.size(); ++i) {
|
||||||
|
wgpuRenderPassEncoderPopDebugGroup(pass);
|
||||||
|
}
|
||||||
|
for (size_t i = firstDiff; i < cmd.debugGroupStack.size(); ++i) {
|
||||||
|
wgpuRenderPassEncoderPushDebugGroup(pass, cmd.debugGroupStack[i].c_str());
|
||||||
|
}
|
||||||
|
lastDebugGroupStack = cmd.debugGroupStack;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
switch (cmd.type) {
|
||||||
|
case CommandType::SetViewport: {
|
||||||
|
const auto& vp = cmd.data.setViewport;
|
||||||
|
wgpuRenderPassEncoderSetViewport(pass, vp.left, vp.top, vp.width, vp.height, vp.znear, vp.zfar);
|
||||||
|
} break;
|
||||||
|
case CommandType::SetScissor: {
|
||||||
|
const auto& sc = cmd.data.setScissor;
|
||||||
|
wgpuRenderPassEncoderSetScissorRect(pass, sc.x, sc.y, sc.w, sc.h);
|
||||||
|
} break;
|
||||||
|
case CommandType::Draw: {
|
||||||
|
const auto& draw = cmd.data.draw;
|
||||||
|
switch (draw.type) {
|
||||||
|
case ShaderType::Stream:
|
||||||
|
stream::render(g_state.stream, draw.stream, pass);
|
||||||
|
break;
|
||||||
|
case ShaderType::Model:
|
||||||
|
model::render(g_state.model, draw.model, pass);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AURORA_GFX_DEBUG_GROUPS
|
||||||
|
for (size_t i = 0; i < lastDebugGroupStack.size(); ++i) {
|
||||||
|
wgpuRenderPassEncoderPopDebugGroup(pass);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bind_pipeline(PipelineRef ref, WGPURenderPassEncoder pass) {
|
||||||
|
if (ref == g_currentPipeline) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
std::lock_guard guard{g_pipelineMutex};
|
||||||
|
const auto it = g_pipelines.find(ref);
|
||||||
|
if (it == g_pipelines.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
wgpuRenderPassEncoderSetPipeline(pass, it->second);
|
||||||
|
g_currentPipeline = ref;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Range push(ByteBuffer& target, const uint8_t* data, size_t length, size_t alignment) {
|
||||||
|
size_t padding = 0;
|
||||||
|
if (alignment != 0) {
|
||||||
|
padding = alignment - length % alignment;
|
||||||
|
}
|
||||||
|
auto begin = target.size();
|
||||||
|
if (length == 0) {
|
||||||
|
length = alignment;
|
||||||
|
target.append_zeroes(alignment);
|
||||||
|
} else {
|
||||||
|
target.append(data, length);
|
||||||
|
if (padding > 0) {
|
||||||
|
target.append_zeroes(padding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {static_cast<uint32_t>(begin), static_cast<uint32_t>(length + padding)};
|
||||||
|
}
|
||||||
|
static inline Range map(ByteBuffer& target, size_t length, size_t alignment) {
|
||||||
|
size_t padding = 0;
|
||||||
|
if (alignment != 0) {
|
||||||
|
padding = alignment - length % alignment;
|
||||||
|
}
|
||||||
|
if (length == 0) {
|
||||||
|
length = alignment;
|
||||||
|
}
|
||||||
|
auto begin = target.size();
|
||||||
|
target.append_zeroes(length + padding);
|
||||||
|
return {static_cast<uint32_t>(begin), static_cast<uint32_t>(length + padding)};
|
||||||
|
}
|
||||||
|
Range push_verts(const uint8_t* data, size_t length) { return push(g_verts, data, length, 4); }
|
||||||
|
Range push_indices(const uint8_t* data, size_t length) { return push(g_indices, data, length, 4); }
|
||||||
|
Range push_uniform(const uint8_t* data, size_t length) {
|
||||||
|
return push(g_uniforms, data, length, g_cachedLimits.limits.minUniformBufferOffsetAlignment);
|
||||||
|
}
|
||||||
|
Range push_storage(const uint8_t* data, size_t length) {
|
||||||
|
return push(g_storage, data, length, g_cachedLimits.limits.minStorageBufferOffsetAlignment);
|
||||||
|
}
|
||||||
|
Range push_texture_data(const uint8_t* data, size_t length, u32 bytesPerRow, u32 rowsPerImage) {
|
||||||
|
// For CopyBufferToTexture, we need an alignment of 256 per row (see Dawn kTextureBytesPerRowAlignment)
|
||||||
|
const auto copyBytesPerRow = ALIGN(bytesPerRow, 256);
|
||||||
|
const auto range = map(g_textureUpload, copyBytesPerRow * rowsPerImage, 0);
|
||||||
|
u8* dst = g_textureUpload.data() + range.offset;
|
||||||
|
for (u32 i = 0; i < rowsPerImage; ++i) {
|
||||||
|
memcpy(dst, data, bytesPerRow);
|
||||||
|
data += bytesPerRow;
|
||||||
|
dst += copyBytesPerRow;
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
std::pair<ByteBuffer, Range> map_verts(size_t length) {
|
||||||
|
const auto range = map(g_verts, length, 4);
|
||||||
|
return {ByteBuffer{g_verts.data() + range.offset, range.size}, range};
|
||||||
|
}
|
||||||
|
std::pair<ByteBuffer, Range> map_indices(size_t length) {
|
||||||
|
const auto range = map(g_indices, length, 4);
|
||||||
|
return {ByteBuffer{g_indices.data() + range.offset, range.size}, range};
|
||||||
|
}
|
||||||
|
std::pair<ByteBuffer, Range> map_uniform(size_t length) {
|
||||||
|
const auto range = map(g_uniforms, length, g_cachedLimits.limits.minUniformBufferOffsetAlignment);
|
||||||
|
return {ByteBuffer{g_uniforms.data() + range.offset, range.size}, range};
|
||||||
|
}
|
||||||
|
std::pair<ByteBuffer, Range> map_storage(size_t length) {
|
||||||
|
const auto range = map(g_storage, length, g_cachedLimits.limits.minStorageBufferOffsetAlignment);
|
||||||
|
return {ByteBuffer{g_storage.data() + range.offset, range.size}, range};
|
||||||
|
}
|
||||||
|
|
||||||
|
BindGroupRef bind_group_ref(const WGPUBindGroupDescriptor& descriptor) {
|
||||||
|
const auto id = xxh3_hash(descriptor);
|
||||||
|
if (!g_cachedBindGroups.contains(id)) {
|
||||||
|
g_cachedBindGroups.try_emplace(id, wgpuDeviceCreateBindGroup(g_device, &descriptor));
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
WGPUBindGroup find_bind_group(BindGroupRef id) {
|
||||||
|
const auto it = g_cachedBindGroups.find(id);
|
||||||
|
if (it == g_cachedBindGroups.end()) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("get_bind_group: failed to locate {}"), id);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
WGPUSampler sampler_ref(const WGPUSamplerDescriptor& descriptor) {
|
||||||
|
const auto id = xxh3_hash(descriptor);
|
||||||
|
auto it = g_cachedSamplers.find(id);
|
||||||
|
if (it == g_cachedSamplers.end()) {
|
||||||
|
it = g_cachedSamplers.try_emplace(id, wgpuDeviceCreateSampler(g_device, &descriptor)).first;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t align_uniform(uint32_t value) { return ALIGN(value, g_cachedLimits.limits.minUniformBufferOffsetAlignment); }
|
||||||
|
} // namespace aurora::gfx
|
||||||
|
|
||||||
|
void push_debug_group(const char* label) {
|
||||||
|
#ifdef AURORA_GFX_DEBUG_GROUPS
|
||||||
|
aurora::gfx::g_debugGroupStack.emplace_back(label);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void pop_debug_group() {
|
||||||
|
#ifdef AURORA_GFX_DEBUG_GROUPS
|
||||||
|
aurora::gfx::g_debugGroupStack.pop_back();
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../internal.hpp"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <webgpu/webgpu.h>
|
||||||
|
#include <xxhash_impl.h>
|
||||||
|
|
||||||
|
namespace aurora {
|
||||||
|
#if INTPTR_MAX == INT32_MAX
|
||||||
|
using HashType = XXH32_hash_t;
|
||||||
|
#else
|
||||||
|
using HashType = XXH64_hash_t;
|
||||||
|
#endif
|
||||||
|
static inline HashType xxh3_hash_s(const void* input, size_t len, HashType seed = 0) {
|
||||||
|
return static_cast<HashType>(XXH3_64bits_withSeed(input, len, seed));
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
static inline HashType xxh3_hash(const T& input, HashType seed = 0) {
|
||||||
|
// Validate that the type has no padding bytes, which can easily cause
|
||||||
|
// hash mismatches. This also disallows floats, but that's okay for us.
|
||||||
|
static_assert(std::has_unique_object_representations_v<T>);
|
||||||
|
return xxh3_hash_s(&input, sizeof(T), seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByteBuffer {
|
||||||
|
public:
|
||||||
|
ByteBuffer() noexcept = default;
|
||||||
|
explicit ByteBuffer(size_t size) noexcept
|
||||||
|
: m_data(static_cast<uint8_t*>(calloc(1, size))), m_length(size), m_capacity(size) {}
|
||||||
|
explicit ByteBuffer(uint8_t* data, size_t size) noexcept
|
||||||
|
: m_data(data), m_length(0), m_capacity(size), m_owned(false) {}
|
||||||
|
~ByteBuffer() noexcept {
|
||||||
|
if (m_data != nullptr && m_owned) {
|
||||||
|
free(m_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ByteBuffer(ByteBuffer&& rhs) noexcept
|
||||||
|
: m_data(rhs.m_data), m_length(rhs.m_length), m_capacity(rhs.m_capacity), m_owned(rhs.m_owned) {
|
||||||
|
rhs.m_data = nullptr;
|
||||||
|
rhs.m_length = 0;
|
||||||
|
rhs.m_capacity = 0;
|
||||||
|
rhs.m_owned = true;
|
||||||
|
}
|
||||||
|
ByteBuffer& operator=(ByteBuffer&& rhs) noexcept {
|
||||||
|
if (m_data != nullptr && m_owned) {
|
||||||
|
free(m_data);
|
||||||
|
}
|
||||||
|
m_data = rhs.m_data;
|
||||||
|
m_length = rhs.m_length;
|
||||||
|
m_capacity = rhs.m_capacity;
|
||||||
|
m_owned = rhs.m_owned;
|
||||||
|
rhs.m_data = nullptr;
|
||||||
|
rhs.m_length = 0;
|
||||||
|
rhs.m_capacity = 0;
|
||||||
|
rhs.m_owned = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ByteBuffer(ByteBuffer const&) = delete;
|
||||||
|
ByteBuffer& operator=(ByteBuffer const&) = delete;
|
||||||
|
|
||||||
|
[[nodiscard]] uint8_t* data() noexcept { return m_data; }
|
||||||
|
[[nodiscard]] const uint8_t* data() const noexcept { return m_data; }
|
||||||
|
[[nodiscard]] size_t size() const noexcept { return m_length; }
|
||||||
|
[[nodiscard]] bool empty() const noexcept { return m_length == 0; }
|
||||||
|
|
||||||
|
void append(const void* data, size_t size) {
|
||||||
|
resize(m_length + size, false);
|
||||||
|
memcpy(m_data + m_length, data, size);
|
||||||
|
m_length += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append_zeroes(size_t size) {
|
||||||
|
resize(m_length + size, true);
|
||||||
|
m_length += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
if (m_data != nullptr && m_owned) {
|
||||||
|
free(m_data);
|
||||||
|
}
|
||||||
|
m_data = nullptr;
|
||||||
|
m_length = 0;
|
||||||
|
m_capacity = 0;
|
||||||
|
m_owned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve_extra(size_t size) { resize(m_length + size, true); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t* m_data = nullptr;
|
||||||
|
size_t m_length = 0;
|
||||||
|
size_t m_capacity = 0;
|
||||||
|
bool m_owned = true;
|
||||||
|
|
||||||
|
void resize(size_t size, bool zeroed) {
|
||||||
|
if (size == 0) {
|
||||||
|
clear();
|
||||||
|
} else if (m_data == nullptr) {
|
||||||
|
if (zeroed) {
|
||||||
|
m_data = static_cast<uint8_t*>(calloc(1, size));
|
||||||
|
} else {
|
||||||
|
m_data = static_cast<uint8_t*>(malloc(size));
|
||||||
|
}
|
||||||
|
m_owned = true;
|
||||||
|
} else if (size > m_capacity) {
|
||||||
|
if (!m_owned) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
m_data = static_cast<uint8_t*>(realloc(m_data, size));
|
||||||
|
if (zeroed) {
|
||||||
|
memset(m_data + m_capacity, 0, size - m_capacity);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_capacity = size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace aurora
|
||||||
|
|
||||||
|
namespace aurora::gfx {
|
||||||
|
extern WGPUBuffer g_vertexBuffer;
|
||||||
|
extern WGPUBuffer g_uniformBuffer;
|
||||||
|
extern WGPUBuffer g_indexBuffer;
|
||||||
|
extern WGPUBuffer g_storageBuffer;
|
||||||
|
extern size_t g_staticStorageLastSize;
|
||||||
|
|
||||||
|
using BindGroupRef = HashType;
|
||||||
|
using PipelineRef = HashType;
|
||||||
|
using SamplerRef = HashType;
|
||||||
|
using ShaderRef = HashType;
|
||||||
|
struct Range {
|
||||||
|
uint32_t offset = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
|
||||||
|
inline bool operator==(const Range& rhs) { return offset == rhs.offset && size == rhs.size; }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ShaderType {
|
||||||
|
Stream,
|
||||||
|
Model,
|
||||||
|
};
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
void begin_frame();
|
||||||
|
void end_frame(WGPUCommandEncoder cmd);
|
||||||
|
void render(WGPUCommandEncoder cmd);
|
||||||
|
void render_pass(WGPURenderPassEncoder pass, uint32_t idx);
|
||||||
|
void map_staging_buffer();
|
||||||
|
|
||||||
|
Range push_verts(const uint8_t* data, size_t length);
|
||||||
|
template <typename T>
|
||||||
|
static inline Range push_verts(ArrayRef<T> data) {
|
||||||
|
return push_verts(reinterpret_cast<const uint8_t*>(data.data()), data.size() * sizeof(T));
|
||||||
|
}
|
||||||
|
Range push_indices(const uint8_t* data, size_t length);
|
||||||
|
template <typename T>
|
||||||
|
static inline Range push_indices(ArrayRef<T> data) {
|
||||||
|
return push_indices(reinterpret_cast<const uint8_t*>(data.data()), data.size() * sizeof(T));
|
||||||
|
}
|
||||||
|
Range push_uniform(const uint8_t* data, size_t length);
|
||||||
|
template <typename T>
|
||||||
|
static inline Range push_uniform(const T& data) {
|
||||||
|
return push_uniform(reinterpret_cast<const uint8_t*>(&data), sizeof(T));
|
||||||
|
}
|
||||||
|
Range push_storage(const uint8_t* data, size_t length);
|
||||||
|
template <typename T>
|
||||||
|
static inline Range push_storage(ArrayRef<T> data) {
|
||||||
|
return push_storage(reinterpret_cast<const uint8_t*>(data.data()), data.size() * sizeof(T));
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
static inline Range push_storage(const T& data) {
|
||||||
|
return push_storage(reinterpret_cast<const uint8_t*>(&data), sizeof(T));
|
||||||
|
}
|
||||||
|
Range push_texture_data(const uint8_t* data, size_t length, uint32_t bytesPerRow, uint32_t rowsPerImage);
|
||||||
|
std::pair<ByteBuffer, Range> map_verts(size_t length);
|
||||||
|
std::pair<ByteBuffer, Range> map_indices(size_t length);
|
||||||
|
std::pair<ByteBuffer, Range> map_uniform(size_t length);
|
||||||
|
std::pair<ByteBuffer, Range> map_storage(size_t length);
|
||||||
|
|
||||||
|
template <typename State>
|
||||||
|
const State& get_state();
|
||||||
|
template <typename DrawData>
|
||||||
|
void push_draw_command(DrawData data);
|
||||||
|
|
||||||
|
template <typename PipelineConfig>
|
||||||
|
PipelineRef pipeline_ref(PipelineConfig config);
|
||||||
|
bool bind_pipeline(PipelineRef ref, WGPURenderPassEncoder pass);
|
||||||
|
|
||||||
|
BindGroupRef bind_group_ref(const WGPUBindGroupDescriptor& descriptor);
|
||||||
|
WGPUBindGroup find_bind_group(BindGroupRef id);
|
||||||
|
|
||||||
|
WGPUSampler sampler_ref(const WGPUSamplerDescriptor& descriptor);
|
||||||
|
|
||||||
|
uint32_t align_uniform(uint32_t value);
|
||||||
|
|
||||||
|
void set_viewport(float left, float top, float width, float height, float znear, float zfar) noexcept;
|
||||||
|
void set_scissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) noexcept;
|
||||||
|
} // namespace aurora::gfx
|
|
@ -0,0 +1,794 @@
|
||||||
|
#include "gx.hpp"
|
||||||
|
|
||||||
|
#include "../webgpu/gpu.hpp"
|
||||||
|
#include "../window.hpp"
|
||||||
|
#include "../internal.hpp"
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#include <absl/container/flat_hash_map.h>
|
||||||
|
#include <cfloat>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
using aurora::gfx::gx::g_gxState;
|
||||||
|
static aurora::Module Log("aurora::gx");
|
||||||
|
|
||||||
|
namespace aurora::gfx {
|
||||||
|
static Module Log("aurora::gfx::gx");
|
||||||
|
|
||||||
|
namespace gx {
|
||||||
|
using webgpu::g_device;
|
||||||
|
using webgpu::g_graphicsConfig;
|
||||||
|
|
||||||
|
GXState g_gxState{};
|
||||||
|
|
||||||
|
const TextureBind& get_texture(GXTexMapID id) noexcept { return g_gxState.textures[static_cast<size_t>(id)]; }
|
||||||
|
|
||||||
|
static inline WGPUBlendFactor to_blend_factor(GXBlendFactor fac, bool isDst) {
|
||||||
|
switch (fac) {
|
||||||
|
case GX_BL_ZERO:
|
||||||
|
return WGPUBlendFactor_Zero;
|
||||||
|
case GX_BL_ONE:
|
||||||
|
return WGPUBlendFactor_One;
|
||||||
|
case GX_BL_SRCCLR: // + GX_BL_DSTCLR
|
||||||
|
if (isDst) {
|
||||||
|
return WGPUBlendFactor_Src;
|
||||||
|
} else {
|
||||||
|
return WGPUBlendFactor_Dst;
|
||||||
|
}
|
||||||
|
case GX_BL_INVSRCCLR: // + GX_BL_INVDSTCLR
|
||||||
|
if (isDst) {
|
||||||
|
return WGPUBlendFactor_OneMinusSrc;
|
||||||
|
} else {
|
||||||
|
return WGPUBlendFactor_OneMinusDst;
|
||||||
|
}
|
||||||
|
case GX_BL_SRCALPHA:
|
||||||
|
return WGPUBlendFactor_SrcAlpha;
|
||||||
|
case GX_BL_INVSRCALPHA:
|
||||||
|
return WGPUBlendFactor_OneMinusSrcAlpha;
|
||||||
|
case GX_BL_DSTALPHA:
|
||||||
|
return WGPUBlendFactor_DstAlpha;
|
||||||
|
case GX_BL_INVDSTALPHA:
|
||||||
|
return WGPUBlendFactor_OneMinusDstAlpha;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid blend factor {}"), fac);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline WGPUCompareFunction to_compare_function(GXCompare func) {
|
||||||
|
switch (func) {
|
||||||
|
case GX_NEVER:
|
||||||
|
return WGPUCompareFunction_Never;
|
||||||
|
case GX_LESS:
|
||||||
|
return WGPUCompareFunction_Less;
|
||||||
|
case GX_EQUAL:
|
||||||
|
return WGPUCompareFunction_Equal;
|
||||||
|
case GX_LEQUAL:
|
||||||
|
return WGPUCompareFunction_LessEqual;
|
||||||
|
case GX_GREATER:
|
||||||
|
return WGPUCompareFunction_Greater;
|
||||||
|
case GX_NEQUAL:
|
||||||
|
return WGPUCompareFunction_NotEqual;
|
||||||
|
case GX_GEQUAL:
|
||||||
|
return WGPUCompareFunction_GreaterEqual;
|
||||||
|
case GX_ALWAYS:
|
||||||
|
return WGPUCompareFunction_Always;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid depth fn {}"), func);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline WGPUBlendState to_blend_state(GXBlendMode mode, GXBlendFactor srcFac, GXBlendFactor dstFac, GXLogicOp op,
|
||||||
|
u32 dstAlpha) {
|
||||||
|
WGPUBlendComponent colorBlendComponent;
|
||||||
|
switch (mode) {
|
||||||
|
case GX_BM_NONE:
|
||||||
|
colorBlendComponent = {
|
||||||
|
.operation = WGPUBlendOperation_Add,
|
||||||
|
.srcFactor = WGPUBlendFactor_One,
|
||||||
|
.dstFactor = WGPUBlendFactor_Zero,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case GX_BM_BLEND:
|
||||||
|
colorBlendComponent = {
|
||||||
|
.operation = WGPUBlendOperation_Add,
|
||||||
|
.srcFactor = to_blend_factor(srcFac, false),
|
||||||
|
.dstFactor = to_blend_factor(dstFac, true),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case GX_BM_SUBTRACT:
|
||||||
|
colorBlendComponent = {
|
||||||
|
.operation = WGPUBlendOperation_ReverseSubtract,
|
||||||
|
.srcFactor = WGPUBlendFactor_One,
|
||||||
|
.dstFactor = WGPUBlendFactor_One,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case GX_BM_LOGIC:
|
||||||
|
switch (op) {
|
||||||
|
case GX_LO_CLEAR:
|
||||||
|
colorBlendComponent = {
|
||||||
|
.operation = WGPUBlendOperation_Add,
|
||||||
|
.srcFactor = WGPUBlendFactor_Zero,
|
||||||
|
.dstFactor = WGPUBlendFactor_Zero,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case GX_LO_COPY:
|
||||||
|
colorBlendComponent = {
|
||||||
|
.operation = WGPUBlendOperation_Add,
|
||||||
|
.srcFactor = WGPUBlendFactor_One,
|
||||||
|
.dstFactor = WGPUBlendFactor_Zero,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case GX_LO_NOOP:
|
||||||
|
colorBlendComponent = {
|
||||||
|
.operation = WGPUBlendOperation_Add,
|
||||||
|
.srcFactor = WGPUBlendFactor_Zero,
|
||||||
|
.dstFactor = WGPUBlendFactor_One,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("unsupported logic op {}"), op);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("unsupported blend mode {}"), mode);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
WGPUBlendComponent alphaBlendComponent{
|
||||||
|
.operation = WGPUBlendOperation_Add,
|
||||||
|
.srcFactor = WGPUBlendFactor_One,
|
||||||
|
.dstFactor = WGPUBlendFactor_Zero,
|
||||||
|
};
|
||||||
|
if (dstAlpha != UINT32_MAX) {
|
||||||
|
alphaBlendComponent = WGPUBlendComponent{
|
||||||
|
.operation = WGPUBlendOperation_Add,
|
||||||
|
.srcFactor = WGPUBlendFactor_Constant,
|
||||||
|
.dstFactor = WGPUBlendFactor_Zero,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.color = colorBlendComponent,
|
||||||
|
.alpha = alphaBlendComponent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline WGPUColorWriteMaskFlags to_write_mask(bool colorUpdate, bool alphaUpdate) {
|
||||||
|
WGPUColorWriteMaskFlags writeMask = WGPUColorWriteMask_None;
|
||||||
|
if (colorUpdate) {
|
||||||
|
writeMask |= WGPUColorWriteMask_Red | WGPUColorWriteMask_Green | WGPUColorWriteMask_Blue;
|
||||||
|
}
|
||||||
|
if (alphaUpdate) {
|
||||||
|
writeMask |= WGPUColorWriteMask_Alpha;
|
||||||
|
}
|
||||||
|
return writeMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline WGPUPrimitiveState to_primitive_state(GXPrimitive gx_prim, GXCullMode gx_cullMode) {
|
||||||
|
WGPUPrimitiveTopology primitive = WGPUPrimitiveTopology_TriangleList;
|
||||||
|
switch (gx_prim) {
|
||||||
|
case GX_TRIANGLES:
|
||||||
|
break;
|
||||||
|
case GX_TRIANGLESTRIP:
|
||||||
|
primitive = WGPUPrimitiveTopology_TriangleStrip;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Unsupported primitive type {}"), gx_prim);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
WGPUCullMode cullMode = WGPUCullMode_None;
|
||||||
|
switch (gx_cullMode) {
|
||||||
|
case GX_CULL_FRONT:
|
||||||
|
cullMode = WGPUCullMode_Front;
|
||||||
|
break;
|
||||||
|
case GX_CULL_BACK:
|
||||||
|
cullMode = WGPUCullMode_Back;
|
||||||
|
break;
|
||||||
|
case GX_CULL_NONE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Unsupported cull mode {}"), gx_cullMode);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.topology = primitive,
|
||||||
|
.frontFace = WGPUFrontFace_CW,
|
||||||
|
.cullMode = cullMode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
WGPURenderPipeline build_pipeline(const PipelineConfig& config, const ShaderInfo& info,
|
||||||
|
ArrayRef<WGPUVertexBufferLayout> vtxBuffers, WGPUShaderModule shader,
|
||||||
|
const char* label) noexcept {
|
||||||
|
const WGPUDepthStencilState depthStencil{
|
||||||
|
.format = g_graphicsConfig.depthFormat,
|
||||||
|
.depthWriteEnabled = config.depthUpdate,
|
||||||
|
.depthCompare = to_compare_function(config.depthFunc),
|
||||||
|
.stencilFront =
|
||||||
|
WGPUStencilFaceState{
|
||||||
|
.compare = WGPUCompareFunction_Always,
|
||||||
|
},
|
||||||
|
.stencilBack =
|
||||||
|
WGPUStencilFaceState{
|
||||||
|
.compare = WGPUCompareFunction_Always,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const auto blendState =
|
||||||
|
to_blend_state(config.blendMode, config.blendFacSrc, config.blendFacDst, config.blendOp, config.dstAlpha);
|
||||||
|
const std::array colorTargets{WGPUColorTargetState{
|
||||||
|
.format = g_graphicsConfig.colorFormat,
|
||||||
|
.blend = &blendState,
|
||||||
|
.writeMask = to_write_mask(config.colorUpdate, config.alphaUpdate),
|
||||||
|
}};
|
||||||
|
const WGPUFragmentState fragmentState{
|
||||||
|
.module = shader,
|
||||||
|
.entryPoint = "fs_main",
|
||||||
|
.targetCount = colorTargets.size(),
|
||||||
|
.targets = colorTargets.data(),
|
||||||
|
};
|
||||||
|
auto layouts = build_bind_group_layouts(info, config.shaderConfig);
|
||||||
|
const std::array bindGroupLayouts{
|
||||||
|
layouts.uniformLayout,
|
||||||
|
layouts.samplerLayout,
|
||||||
|
layouts.textureLayout,
|
||||||
|
};
|
||||||
|
const WGPUPipelineLayoutDescriptor pipelineLayoutDescriptor{
|
||||||
|
.label = "GX Pipeline Layout",
|
||||||
|
.bindGroupLayoutCount = static_cast<uint32_t>(info.sampledTextures.any() ? bindGroupLayouts.size() : 1),
|
||||||
|
.bindGroupLayouts = bindGroupLayouts.data(),
|
||||||
|
};
|
||||||
|
auto pipelineLayout = wgpuDeviceCreatePipelineLayout(g_device, &pipelineLayoutDescriptor);
|
||||||
|
const WGPURenderPipelineDescriptor descriptor{
|
||||||
|
.label = label,
|
||||||
|
.layout = pipelineLayout,
|
||||||
|
.vertex =
|
||||||
|
{
|
||||||
|
.module = shader,
|
||||||
|
.entryPoint = "vs_main",
|
||||||
|
.bufferCount = static_cast<uint32_t>(vtxBuffers.size()),
|
||||||
|
.buffers = vtxBuffers.data(),
|
||||||
|
},
|
||||||
|
.primitive = to_primitive_state(config.primitive, config.cullMode),
|
||||||
|
.depthStencil = &depthStencil,
|
||||||
|
.multisample =
|
||||||
|
WGPUMultisampleState{
|
||||||
|
.count = g_graphicsConfig.msaaSamples,
|
||||||
|
.mask = UINT32_MAX,
|
||||||
|
},
|
||||||
|
.fragment = &fragmentState,
|
||||||
|
};
|
||||||
|
auto pipeline = wgpuDeviceCreateRenderPipeline(g_device, &descriptor);
|
||||||
|
wgpuPipelineLayoutRelease(pipelineLayout);
|
||||||
|
return pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
void populate_pipeline_config(PipelineConfig& config, GXPrimitive primitive) noexcept {
|
||||||
|
config.shaderConfig.fogType = g_gxState.fog.type;
|
||||||
|
config.shaderConfig.vtxAttrs = g_gxState.vtxDesc;
|
||||||
|
int lastIndexedAttr = -1;
|
||||||
|
for (int i = 0; i < GX_VA_MAX_ATTR; ++i) {
|
||||||
|
const auto type = g_gxState.vtxDesc[i];
|
||||||
|
if (type != GX_INDEX8 && type != GX_INDEX16) {
|
||||||
|
config.shaderConfig.attrMapping[i] = GX_VA_NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& array = g_gxState.arrays[i];
|
||||||
|
if (lastIndexedAttr >= 0 && array == g_gxState.arrays[lastIndexedAttr]) {
|
||||||
|
// Map attribute to previous attribute
|
||||||
|
config.shaderConfig.attrMapping[i] = config.shaderConfig.attrMapping[lastIndexedAttr];
|
||||||
|
} else {
|
||||||
|
// Map attribute to its own storage
|
||||||
|
config.shaderConfig.attrMapping[i] = static_cast<GXAttr>(i);
|
||||||
|
}
|
||||||
|
lastIndexedAttr = i;
|
||||||
|
}
|
||||||
|
config.shaderConfig.tevSwapTable = g_gxState.tevSwapTable;
|
||||||
|
for (u8 i = 0; i < g_gxState.numTevStages; ++i) {
|
||||||
|
config.shaderConfig.tevStages[i] = g_gxState.tevStages[i];
|
||||||
|
}
|
||||||
|
config.shaderConfig.tevStageCount = g_gxState.numTevStages;
|
||||||
|
for (u8 i = 0; i < g_gxState.numChans * 2; ++i) {
|
||||||
|
const auto& cc = g_gxState.colorChannelConfig[i];
|
||||||
|
if (cc.lightingEnabled) {
|
||||||
|
config.shaderConfig.colorChannels[i] = cc;
|
||||||
|
} else {
|
||||||
|
// Only matSrc matters when lighting disabled
|
||||||
|
config.shaderConfig.colorChannels[i] = {
|
||||||
|
.matSrc = cc.matSrc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (u8 i = 0; i < g_gxState.numTexGens; ++i) {
|
||||||
|
config.shaderConfig.tcgs[i] = g_gxState.tcgs[i];
|
||||||
|
}
|
||||||
|
if (g_gxState.alphaCompare) {
|
||||||
|
config.shaderConfig.alphaCompare = g_gxState.alphaCompare;
|
||||||
|
}
|
||||||
|
config.shaderConfig.indexedAttributeCount =
|
||||||
|
std::count_if(config.shaderConfig.vtxAttrs.begin(), config.shaderConfig.vtxAttrs.end(),
|
||||||
|
[](const auto type) { return type == GX_INDEX8 || type == GX_INDEX16; });
|
||||||
|
for (u8 i = 0; i < MaxTextures; ++i) {
|
||||||
|
const auto& bind = g_gxState.textures[i];
|
||||||
|
TextureConfig texConfig{};
|
||||||
|
if (bind.texObj.ref) {
|
||||||
|
if (requires_copy_conversion(bind.texObj)) {
|
||||||
|
texConfig.copyFmt = bind.texObj.ref->gxFormat;
|
||||||
|
}
|
||||||
|
if (requires_load_conversion(bind.texObj)) {
|
||||||
|
texConfig.loadFmt = bind.texObj.fmt;
|
||||||
|
}
|
||||||
|
texConfig.renderTex = bind.texObj.ref->isRenderTexture;
|
||||||
|
}
|
||||||
|
config.shaderConfig.textureConfig[i] = texConfig;
|
||||||
|
}
|
||||||
|
config = {
|
||||||
|
.shaderConfig = config.shaderConfig,
|
||||||
|
.primitive = primitive,
|
||||||
|
.depthFunc = g_gxState.depthFunc,
|
||||||
|
.cullMode = g_gxState.cullMode,
|
||||||
|
.blendMode = g_gxState.blendMode,
|
||||||
|
.blendFacSrc = g_gxState.blendFacSrc,
|
||||||
|
.blendFacDst = g_gxState.blendFacDst,
|
||||||
|
.blendOp = g_gxState.blendOp,
|
||||||
|
.dstAlpha = g_gxState.dstAlpha,
|
||||||
|
.depthCompare = g_gxState.depthCompare,
|
||||||
|
.depthUpdate = g_gxState.depthUpdate,
|
||||||
|
.alphaUpdate = g_gxState.alphaUpdate,
|
||||||
|
.colorUpdate = g_gxState.colorUpdate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Range build_uniform(const ShaderInfo& info) noexcept {
|
||||||
|
auto [buf, range] = map_uniform(info.uniformSize);
|
||||||
|
{
|
||||||
|
buf.append(&g_gxState.pnMtx[g_gxState.currentPnMtx], 128);
|
||||||
|
buf.append(&g_gxState.proj, 64);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < info.loadsTevReg.size(); ++i) {
|
||||||
|
if (!info.loadsTevReg.test(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buf.append(&g_gxState.colorRegs[i], 16);
|
||||||
|
}
|
||||||
|
bool lightingEnabled = false;
|
||||||
|
for (int i = 0; i < info.sampledColorChannels.size(); ++i) {
|
||||||
|
if (!info.sampledColorChannels.test(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& ccc = g_gxState.colorChannelConfig[i * 2];
|
||||||
|
const auto& ccca = g_gxState.colorChannelConfig[i * 2 + 1];
|
||||||
|
if (ccc.lightingEnabled || ccca.lightingEnabled) {
|
||||||
|
lightingEnabled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lightingEnabled) {
|
||||||
|
// Lights
|
||||||
|
static_assert(sizeof(g_gxState.lights) == 80 * GX::MaxLights);
|
||||||
|
buf.append(&g_gxState.lights, 80 * GX::MaxLights);
|
||||||
|
// Light state for all channels
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
u32 lightState = g_gxState.colorChannelState[i].lightMask.to_ulong();
|
||||||
|
buf.append(&lightState, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < info.sampledColorChannels.size(); ++i) {
|
||||||
|
if (!info.sampledColorChannels.test(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& ccc = g_gxState.colorChannelConfig[i * 2];
|
||||||
|
const auto& ccs = g_gxState.colorChannelState[i * 2];
|
||||||
|
if (ccc.lightingEnabled && ccc.ambSrc == GX_SRC_REG) {
|
||||||
|
buf.append(&ccs.ambColor, 16);
|
||||||
|
}
|
||||||
|
if (ccc.matSrc == GX_SRC_REG) {
|
||||||
|
buf.append(&ccs.matColor, 16);
|
||||||
|
}
|
||||||
|
const auto& ccca = g_gxState.colorChannelConfig[i * 2 + 1];
|
||||||
|
const auto& ccsa = g_gxState.colorChannelState[i * 2 + 1];
|
||||||
|
if (ccca.lightingEnabled && ccca.ambSrc == GX_SRC_REG) {
|
||||||
|
buf.append(&ccsa.ambColor, 16);
|
||||||
|
}
|
||||||
|
if (ccca.matSrc == GX_SRC_REG) {
|
||||||
|
buf.append(&ccsa.matColor, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < info.sampledKColors.size(); ++i) {
|
||||||
|
if (!info.sampledKColors.test(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buf.append(&g_gxState.kcolors[i], 16);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < info.usesTexMtx.size(); ++i) {
|
||||||
|
if (!info.usesTexMtx.test(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& state = g_gxState;
|
||||||
|
switch (info.texMtxTypes[i]) {
|
||||||
|
case GX_TG_MTX2x4:
|
||||||
|
if (std::holds_alternative<Mat4x2<float>>(state.texMtxs[i])) {
|
||||||
|
buf.append(&std::get<Mat4x2<float>>(state.texMtxs[i]), 32);
|
||||||
|
} else if (std::holds_alternative<Mat4x4<float>>(g_gxState.texMtxs[i])) {
|
||||||
|
// TODO: SMB hits this?
|
||||||
|
Mat4x2<float> mtx{
|
||||||
|
{1.f, 0.f},
|
||||||
|
{0.f, 1.f},
|
||||||
|
{0.f, 0.f},
|
||||||
|
{0.f, 0.f},
|
||||||
|
};
|
||||||
|
buf.append(&mtx, 32);
|
||||||
|
} else {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("expected 2x4 mtx in idx {}"), i);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GX_TG_MTX3x4:
|
||||||
|
if (std::holds_alternative<Mat4x4<float>>(g_gxState.texMtxs[i])) {
|
||||||
|
const auto& mat = std::get<Mat4x4<float>>(g_gxState.texMtxs[i]);
|
||||||
|
buf.append(&mat, 64);
|
||||||
|
} else {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("expected 3x4 mtx in idx {}"), i);
|
||||||
|
buf.append(&Mat4x4_Identity, 64);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("unhandled tex mtx type {}"), info.texMtxTypes[i]);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < info.usesPTTexMtx.size(); ++i) {
|
||||||
|
if (!info.usesPTTexMtx.test(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buf.append(&g_gxState.ptTexMtxs[i], 64);
|
||||||
|
}
|
||||||
|
if (info.usesFog) {
|
||||||
|
const auto& state = g_gxState.fog;
|
||||||
|
struct Fog {
|
||||||
|
Vec4<float> color = state.color;
|
||||||
|
float a = 0.f;
|
||||||
|
float b = 0.5f;
|
||||||
|
float c = 0.f;
|
||||||
|
float pad = FLT_MAX;
|
||||||
|
} fog{};
|
||||||
|
static_assert(sizeof(Fog) == 32);
|
||||||
|
if (state.nearZ != state.farZ && state.startZ != state.endZ) {
|
||||||
|
const float depthRange = state.farZ - state.nearZ;
|
||||||
|
const float fogRange = state.endZ - state.startZ;
|
||||||
|
fog.a = (state.farZ * state.nearZ) / (depthRange * fogRange);
|
||||||
|
fog.b = state.farZ / depthRange;
|
||||||
|
fog.c = state.startZ / fogRange;
|
||||||
|
}
|
||||||
|
buf.append(&fog, 32);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < info.sampledTextures.size(); ++i) {
|
||||||
|
if (!info.sampledTextures.test(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& tex = get_texture(static_cast<GXTexMapID>(i));
|
||||||
|
if (!tex) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("unbound texture {}"), i);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
buf.append(&tex.texObj.lodBias, 4);
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
static absl::flat_hash_map<u32, WGPUBindGroupLayout> sUniformBindGroupLayouts;
|
||||||
|
static absl::flat_hash_map<u32, std::pair<WGPUBindGroupLayout, WGPUBindGroupLayout>> sTextureBindGroupLayouts;
|
||||||
|
|
||||||
|
GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& config,
|
||||||
|
const BindGroupRanges& ranges) noexcept {
|
||||||
|
const auto layouts = build_bind_group_layouts(info, config);
|
||||||
|
|
||||||
|
std::array<WGPUBindGroupEntry, GX_VA_MAX_ATTR + 1> uniformEntries{
|
||||||
|
WGPUBindGroupEntry{
|
||||||
|
.binding = 0,
|
||||||
|
.buffer = g_uniformBuffer,
|
||||||
|
.size = info.uniformSize,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
u32 uniformBindIdx = 1;
|
||||||
|
for (u32 i = 0; i < GX_VA_MAX_ATTR; ++i) {
|
||||||
|
const Range& range = ranges.vaRanges[i];
|
||||||
|
if (range.size <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uniformEntries[uniformBindIdx] = WGPUBindGroupEntry{
|
||||||
|
.binding = uniformBindIdx,
|
||||||
|
.buffer = g_storageBuffer,
|
||||||
|
.size = range.size,
|
||||||
|
};
|
||||||
|
++uniformBindIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<WGPUBindGroupEntry, MaxTextures> samplerEntries;
|
||||||
|
std::array<WGPUBindGroupEntry, MaxTextures * 2> textureEntries;
|
||||||
|
u32 samplerCount = 0;
|
||||||
|
u32 textureCount = 0;
|
||||||
|
for (u32 i = 0; i < info.sampledTextures.size(); ++i) {
|
||||||
|
if (!info.sampledTextures.test(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& tex = g_gxState.textures[i];
|
||||||
|
if (!tex) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("unbound texture {}"), i);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
samplerEntries[samplerCount] = {
|
||||||
|
.binding = samplerCount,
|
||||||
|
.sampler = sampler_ref(tex.get_descriptor()),
|
||||||
|
};
|
||||||
|
++samplerCount;
|
||||||
|
textureEntries[textureCount] = {
|
||||||
|
.binding = textureCount,
|
||||||
|
.textureView = tex.texObj.ref->view,
|
||||||
|
};
|
||||||
|
++textureCount;
|
||||||
|
// Load palette
|
||||||
|
const auto& texConfig = config.textureConfig[i];
|
||||||
|
if (is_palette_format(texConfig.loadFmt)) {
|
||||||
|
u32 tlut = tex.texObj.tlut;
|
||||||
|
if (tlut < GX_TLUT0 || tlut > GX_TLUT7) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("tlut out of bounds {}"), tlut);
|
||||||
|
unreachable();
|
||||||
|
} else if (!g_gxState.tluts[tlut].ref) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("tlut unbound {}"), tlut);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
textureEntries[textureCount] = {
|
||||||
|
.binding = textureCount,
|
||||||
|
.textureView = g_gxState.tluts[tlut].ref->view,
|
||||||
|
};
|
||||||
|
++textureCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.uniformBindGroup = bind_group_ref(WGPUBindGroupDescriptor{
|
||||||
|
.label = "GX Uniform Bind Group",
|
||||||
|
.layout = layouts.uniformLayout,
|
||||||
|
.entryCount = uniformBindIdx,
|
||||||
|
.entries = uniformEntries.data(),
|
||||||
|
}),
|
||||||
|
.samplerBindGroup = bind_group_ref(WGPUBindGroupDescriptor{
|
||||||
|
.label = "GX Sampler Bind Group",
|
||||||
|
.layout = layouts.samplerLayout,
|
||||||
|
.entryCount = samplerCount,
|
||||||
|
.entries = samplerEntries.data(),
|
||||||
|
}),
|
||||||
|
.textureBindGroup = bind_group_ref(WGPUBindGroupDescriptor{
|
||||||
|
.label = "GX Texture Bind Group",
|
||||||
|
.layout = layouts.textureLayout,
|
||||||
|
.entryCount = textureCount,
|
||||||
|
.entries = textureEntries.data(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const ShaderConfig& config) noexcept {
|
||||||
|
GXBindGroupLayouts out;
|
||||||
|
u32 uniformSizeKey = info.uniformSize + (config.indexedAttributeCount > 0 ? 1 : 0);
|
||||||
|
const auto uniformIt = sUniformBindGroupLayouts.find(uniformSizeKey);
|
||||||
|
if (uniformIt != sUniformBindGroupLayouts.end()) {
|
||||||
|
out.uniformLayout = uniformIt->second;
|
||||||
|
} else {
|
||||||
|
std::array<WGPUBindGroupLayoutEntry, GX_VA_MAX_ATTR + 1> uniformLayoutEntries{
|
||||||
|
WGPUBindGroupLayoutEntry{
|
||||||
|
.binding = 0,
|
||||||
|
.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment,
|
||||||
|
.buffer =
|
||||||
|
WGPUBufferBindingLayout{
|
||||||
|
.type = WGPUBufferBindingType_Uniform,
|
||||||
|
.hasDynamicOffset = true,
|
||||||
|
.minBindingSize = info.uniformSize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
u32 bindIdx = 1;
|
||||||
|
for (int i = 0; i < GX_VA_MAX_ATTR; ++i) {
|
||||||
|
if (config.attrMapping[i] == static_cast<GXAttr>(i)) {
|
||||||
|
uniformLayoutEntries[bindIdx] = WGPUBindGroupLayoutEntry{
|
||||||
|
.binding = bindIdx,
|
||||||
|
.visibility = WGPUShaderStage_Vertex,
|
||||||
|
.buffer =
|
||||||
|
WGPUBufferBindingLayout{
|
||||||
|
.type = WGPUBufferBindingType_ReadOnlyStorage,
|
||||||
|
.hasDynamicOffset = true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
++bindIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto uniformLayoutDescriptor = WGPUBindGroupLayoutDescriptor{
|
||||||
|
.label = "GX Uniform Bind Group Layout",
|
||||||
|
.entryCount = bindIdx,
|
||||||
|
.entries = uniformLayoutEntries.data(),
|
||||||
|
};
|
||||||
|
out.uniformLayout = wgpuDeviceCreateBindGroupLayout(g_device, &uniformLayoutDescriptor);
|
||||||
|
// sUniformBindGroupLayouts.try_emplace(uniformSizeKey, out.uniformLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// u32 textureCount = info.sampledTextures.count();
|
||||||
|
// const auto textureIt = sTextureBindGroupLayouts.find(textureCount);
|
||||||
|
// if (textureIt != sTextureBindGroupLayouts.end()) {
|
||||||
|
// const auto& [sl, tl] = textureIt->second;
|
||||||
|
// out.samplerLayout = sl;
|
||||||
|
// out.textureLayout = tl;
|
||||||
|
// } else {
|
||||||
|
u32 numSamplers = 0;
|
||||||
|
u32 numTextures = 0;
|
||||||
|
std::array<WGPUBindGroupLayoutEntry, MaxTextures> samplerEntries;
|
||||||
|
std::array<WGPUBindGroupLayoutEntry, MaxTextures * 2> textureEntries;
|
||||||
|
for (u32 i = 0; i < info.sampledTextures.size(); ++i) {
|
||||||
|
if (!info.sampledTextures.test(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& texConfig = config.textureConfig[i];
|
||||||
|
bool copyAsPalette = is_palette_format(texConfig.copyFmt);
|
||||||
|
bool loadAsPalette = is_palette_format(texConfig.loadFmt);
|
||||||
|
samplerEntries[numSamplers] = {
|
||||||
|
.binding = numSamplers,
|
||||||
|
.visibility = WGPUShaderStage_Fragment,
|
||||||
|
.sampler = {.type = copyAsPalette && loadAsPalette ? WGPUSamplerBindingType_NonFiltering
|
||||||
|
: WGPUSamplerBindingType_Filtering},
|
||||||
|
};
|
||||||
|
++numSamplers;
|
||||||
|
if (loadAsPalette) {
|
||||||
|
textureEntries[numTextures] = {
|
||||||
|
.binding = numTextures,
|
||||||
|
.visibility = WGPUShaderStage_Fragment,
|
||||||
|
.texture =
|
||||||
|
{
|
||||||
|
.sampleType = copyAsPalette ? WGPUTextureSampleType_Sint : WGPUTextureSampleType_Float,
|
||||||
|
.viewDimension = WGPUTextureViewDimension_2D,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
++numTextures;
|
||||||
|
textureEntries[numTextures] = {
|
||||||
|
.binding = numTextures,
|
||||||
|
.visibility = WGPUShaderStage_Fragment,
|
||||||
|
.texture =
|
||||||
|
{
|
||||||
|
.sampleType = WGPUTextureSampleType_Float,
|
||||||
|
.viewDimension = WGPUTextureViewDimension_2D,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
++numTextures;
|
||||||
|
} else {
|
||||||
|
textureEntries[numTextures] = {
|
||||||
|
.binding = numTextures,
|
||||||
|
.visibility = WGPUShaderStage_Fragment,
|
||||||
|
.texture =
|
||||||
|
{
|
||||||
|
.sampleType = WGPUTextureSampleType_Float,
|
||||||
|
.viewDimension = WGPUTextureViewDimension_2D,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
++numTextures;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const WGPUBindGroupLayoutDescriptor descriptor{
|
||||||
|
.label = "GX Sampler Bind Group Layout",
|
||||||
|
.entryCount = numSamplers,
|
||||||
|
.entries = samplerEntries.data(),
|
||||||
|
};
|
||||||
|
out.samplerLayout = wgpuDeviceCreateBindGroupLayout(g_device, &descriptor);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const WGPUBindGroupLayoutDescriptor descriptor{
|
||||||
|
.label = "GX Texture Bind Group Layout",
|
||||||
|
.entryCount = numTextures,
|
||||||
|
.entries = textureEntries.data(),
|
||||||
|
};
|
||||||
|
out.textureLayout = wgpuDeviceCreateBindGroupLayout(g_device, &descriptor);
|
||||||
|
}
|
||||||
|
// sTextureBindGroupLayouts.try_emplace(textureCount, out.samplerLayout, out.textureLayout);
|
||||||
|
// }
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO this is awkward
|
||||||
|
extern absl::flat_hash_map<ShaderRef, std::pair<WGPUShaderModule, gx::ShaderInfo>> g_gxCachedShaders;
|
||||||
|
void shutdown() noexcept {
|
||||||
|
// TODO we should probably store this all in g_state.gx instead
|
||||||
|
for (const auto& item : sUniformBindGroupLayouts) {
|
||||||
|
wgpuBindGroupLayoutRelease(item.second);
|
||||||
|
}
|
||||||
|
sUniformBindGroupLayouts.clear();
|
||||||
|
for (const auto& item : sTextureBindGroupLayouts) {
|
||||||
|
wgpuBindGroupLayoutRelease(item.second.first);
|
||||||
|
wgpuBindGroupLayoutRelease(item.second.second);
|
||||||
|
}
|
||||||
|
sTextureBindGroupLayouts.clear();
|
||||||
|
for (auto& item : g_gxState.textures) {
|
||||||
|
item.texObj.ref.reset();
|
||||||
|
}
|
||||||
|
for (auto& item : g_gxState.tluts) {
|
||||||
|
item.ref.reset();
|
||||||
|
}
|
||||||
|
for (const auto& item : g_gxCachedShaders) {
|
||||||
|
wgpuShaderModuleRelease(item.second.first);
|
||||||
|
}
|
||||||
|
g_gxCachedShaders.clear();
|
||||||
|
}
|
||||||
|
} // namespace gx
|
||||||
|
|
||||||
|
static WGPUAddressMode wgpu_address_mode(GXTexWrapMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case GX_CLAMP:
|
||||||
|
return WGPUAddressMode_ClampToEdge;
|
||||||
|
case GX_REPEAT:
|
||||||
|
return WGPUAddressMode_Repeat;
|
||||||
|
case GX_MIRROR:
|
||||||
|
return WGPUAddressMode_MirrorRepeat;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid wrap mode {}"), mode);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static std::pair<WGPUFilterMode, WGPUFilterMode> wgpu_filter_mode(GXTexFilter filter) {
|
||||||
|
switch (filter) {
|
||||||
|
case GX_NEAR:
|
||||||
|
return {WGPUFilterMode_Nearest, WGPUFilterMode_Linear};
|
||||||
|
case GX_LINEAR:
|
||||||
|
return {WGPUFilterMode_Linear, WGPUFilterMode_Linear};
|
||||||
|
case GX_NEAR_MIP_NEAR:
|
||||||
|
return {WGPUFilterMode_Nearest, WGPUFilterMode_Nearest};
|
||||||
|
case GX_LIN_MIP_NEAR:
|
||||||
|
return {WGPUFilterMode_Linear, WGPUFilterMode_Nearest};
|
||||||
|
case GX_NEAR_MIP_LIN:
|
||||||
|
return {WGPUFilterMode_Nearest, WGPUFilterMode_Linear};
|
||||||
|
case GX_LIN_MIP_LIN:
|
||||||
|
return {WGPUFilterMode_Linear, WGPUFilterMode_Linear};
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid filter mode {}"), filter);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static u16 wgpu_aniso(GXAnisotropy aniso) {
|
||||||
|
switch (aniso) {
|
||||||
|
case GX_ANISO_1:
|
||||||
|
return 1;
|
||||||
|
case GX_ANISO_2:
|
||||||
|
return std::max<u16>(webgpu::g_graphicsConfig.textureAnisotropy / 2, 1);
|
||||||
|
case GX_ANISO_4:
|
||||||
|
return std::max<u16>(webgpu::g_graphicsConfig.textureAnisotropy, 1);
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("invalid aniso mode {}"), aniso);
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WGPUSamplerDescriptor TextureBind::get_descriptor() const noexcept {
|
||||||
|
if (gx::requires_copy_conversion(texObj) && gx::is_palette_format(texObj.ref->gxFormat)) {
|
||||||
|
return {
|
||||||
|
.label = "Generated Non-Filtering Sampler",
|
||||||
|
.addressModeU = wgpu_address_mode(texObj.wrapS),
|
||||||
|
.addressModeV = wgpu_address_mode(texObj.wrapT),
|
||||||
|
.addressModeW = WGPUAddressMode_Repeat,
|
||||||
|
.magFilter = WGPUFilterMode_Nearest,
|
||||||
|
.minFilter = WGPUFilterMode_Nearest,
|
||||||
|
.mipmapFilter = WGPUFilterMode_Nearest,
|
||||||
|
.lodMinClamp = 0.f,
|
||||||
|
.lodMaxClamp = 1000.f,
|
||||||
|
.maxAnisotropy = 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const auto [minFilter, mipFilter] = wgpu_filter_mode(texObj.minFilter);
|
||||||
|
const auto [magFilter, _] = wgpu_filter_mode(texObj.magFilter);
|
||||||
|
return {
|
||||||
|
.label = "Generated Filtering Sampler",
|
||||||
|
.addressModeU = wgpu_address_mode(texObj.wrapS),
|
||||||
|
.addressModeV = wgpu_address_mode(texObj.wrapT),
|
||||||
|
.addressModeW = WGPUAddressMode_Repeat,
|
||||||
|
.magFilter = magFilter,
|
||||||
|
.minFilter = minFilter,
|
||||||
|
.mipmapFilter = mipFilter,
|
||||||
|
.lodMinClamp = 0.f,
|
||||||
|
.lodMaxClamp = 1000.f,
|
||||||
|
.maxAnisotropy = wgpu_aniso(texObj.maxAniso),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} // namespace aurora::gfx
|
|
@ -0,0 +1,402 @@
|
||||||
|
#pragma once
|
||||||
|
#include <dolphin/gx.h>
|
||||||
|
#include <aurora/math.hpp>
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "../internal.hpp"
|
||||||
|
#include "texture.hpp"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
#include <cstring>
|
||||||
|
#include <bitset>
|
||||||
|
#include <memory>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#define M_PIF 3.14159265358979323846f
|
||||||
|
|
||||||
|
namespace GX {
|
||||||
|
constexpr u8 MaxLights = 8;
|
||||||
|
using LightMask = std::bitset<MaxLights>;
|
||||||
|
} // namespace GX
|
||||||
|
|
||||||
|
struct GXLightObj_ {
|
||||||
|
GXColor color;
|
||||||
|
float a0 = 1.f;
|
||||||
|
float a1 = 0.f;
|
||||||
|
float a2 = 0.f;
|
||||||
|
float k0 = 1.f;
|
||||||
|
float k1 = 0.f;
|
||||||
|
float k2 = 0.f;
|
||||||
|
float px = 0.f;
|
||||||
|
float py = 0.f;
|
||||||
|
float pz = 0.f;
|
||||||
|
float nx = 0.f;
|
||||||
|
float ny = 0.f;
|
||||||
|
float nz = 0.f;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(GXLightObj_) <= sizeof(GXLightObj), "GXLightObj too small!");
|
||||||
|
|
||||||
|
#if GX_IS_WII
|
||||||
|
constexpr float GX_LARGE_NUMBER = -1.0e+18f;
|
||||||
|
#else
|
||||||
|
constexpr float GX_LARGE_NUMBER = -1048576.0f;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace aurora::gfx::gx {
|
||||||
|
constexpr u32 MaxTextures = GX_MAX_TEXMAP;
|
||||||
|
constexpr u32 MaxTevStages = GX_MAX_TEVSTAGE;
|
||||||
|
constexpr u32 MaxColorChannels = 4;
|
||||||
|
constexpr u32 MaxTevRegs = 4; // TEVPREV, TEVREG0-2
|
||||||
|
constexpr u32 MaxKColors = GX_MAX_KCOLOR;
|
||||||
|
constexpr u32 MaxTexMtx = 10;
|
||||||
|
constexpr u32 MaxPTTexMtx = 20;
|
||||||
|
constexpr u32 MaxTexCoord = GX_MAX_TEXCOORD;
|
||||||
|
constexpr u32 MaxVtxAttr = GX_VA_MAX_ATTR;
|
||||||
|
constexpr u32 MaxTevSwap = GX_MAX_TEVSWAP;
|
||||||
|
constexpr u32 MaxIndStages = GX_MAX_INDTEXSTAGE;
|
||||||
|
constexpr u32 MaxIndTexMtxs = 3;
|
||||||
|
constexpr u32 MaxVtxFmt = GX_MAX_VTXFMT;
|
||||||
|
constexpr u32 MaxPnMtx = (GX_PNMTX9 / 3) + 1;
|
||||||
|
|
||||||
|
template <typename Arg, Arg Default>
|
||||||
|
struct TevPass {
|
||||||
|
Arg a = Default;
|
||||||
|
Arg b = Default;
|
||||||
|
Arg c = Default;
|
||||||
|
Arg d = Default;
|
||||||
|
|
||||||
|
bool operator==(const TevPass& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<TevPass<GXTevColorArg, GX_CC_ZERO>>);
|
||||||
|
static_assert(std::has_unique_object_representations_v<TevPass<GXTevAlphaArg, GX_CA_ZERO>>);
|
||||||
|
struct TevOp {
|
||||||
|
GXTevOp op = GX_TEV_ADD;
|
||||||
|
GXTevBias bias = GX_TB_ZERO;
|
||||||
|
GXTevScale scale = GX_CS_SCALE_1;
|
||||||
|
GXTevRegID outReg = GX_TEVPREV;
|
||||||
|
bool clamp = true;
|
||||||
|
u8 _p1 = 0;
|
||||||
|
u8 _p2 = 0;
|
||||||
|
u8 _p3 = 0;
|
||||||
|
|
||||||
|
bool operator==(const TevOp& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<TevOp>);
|
||||||
|
struct TevStage {
|
||||||
|
TevPass<GXTevColorArg, GX_CC_ZERO> colorPass;
|
||||||
|
TevPass<GXTevAlphaArg, GX_CA_ZERO> alphaPass;
|
||||||
|
TevOp colorOp;
|
||||||
|
TevOp alphaOp;
|
||||||
|
GXTevKColorSel kcSel = GX_TEV_KCSEL_1;
|
||||||
|
GXTevKAlphaSel kaSel = GX_TEV_KASEL_1;
|
||||||
|
GXTexCoordID texCoordId = GX_TEXCOORD_NULL;
|
||||||
|
GXTexMapID texMapId = GX_TEXMAP_NULL;
|
||||||
|
GXChannelID channelId = GX_COLOR_NULL;
|
||||||
|
GXTevSwapSel tevSwapRas = GX_TEV_SWAP0;
|
||||||
|
GXTevSwapSel tevSwapTex = GX_TEV_SWAP0;
|
||||||
|
GXIndTexStageID indTexStage = GX_INDTEXSTAGE0;
|
||||||
|
GXIndTexFormat indTexFormat = GX_ITF_8;
|
||||||
|
GXIndTexBiasSel indTexBiasSel = GX_ITB_NONE;
|
||||||
|
GXIndTexAlphaSel indTexAlphaSel = GX_ITBA_OFF;
|
||||||
|
GXIndTexMtxID indTexMtxId = GX_ITM_OFF;
|
||||||
|
GXIndTexWrap indTexWrapS = GX_ITW_OFF;
|
||||||
|
GXIndTexWrap indTexWrapT = GX_ITW_OFF;
|
||||||
|
bool indTexUseOrigLOD = false;
|
||||||
|
bool indTexAddPrev = false;
|
||||||
|
u8 _p1 = 0;
|
||||||
|
u8 _p2 = 0;
|
||||||
|
|
||||||
|
bool operator==(const TevStage& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<TevStage>);
|
||||||
|
struct IndStage {
|
||||||
|
GXTexCoordID texCoordId;
|
||||||
|
GXTexMapID texMapId;
|
||||||
|
GXIndTexScale scaleS;
|
||||||
|
GXIndTexScale scaleT;
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<IndStage>);
|
||||||
|
// For shader generation
|
||||||
|
struct ColorChannelConfig {
|
||||||
|
GXColorSrc matSrc = GX_SRC_REG;
|
||||||
|
GXColorSrc ambSrc = GX_SRC_REG;
|
||||||
|
GXDiffuseFn diffFn = GX_DF_NONE;
|
||||||
|
GXAttnFn attnFn = GX_AF_NONE;
|
||||||
|
bool lightingEnabled = false;
|
||||||
|
u8 _p1 = 0;
|
||||||
|
u8 _p2 = 0;
|
||||||
|
u8 _p3 = 0;
|
||||||
|
|
||||||
|
bool operator==(const ColorChannelConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<ColorChannelConfig>);
|
||||||
|
// For uniform generation
|
||||||
|
struct ColorChannelState {
|
||||||
|
Vec4<float> matColor;
|
||||||
|
Vec4<float> ambColor;
|
||||||
|
GX::LightMask lightMask;
|
||||||
|
};
|
||||||
|
// Mat4x4 used instead of Mat4x3 for padding purposes
|
||||||
|
using TexMtxVariant = std::variant<std::monostate, Mat4x2<float>, Mat4x4<float>>;
|
||||||
|
struct TcgConfig {
|
||||||
|
GXTexGenType type = GX_TG_MTX2x4;
|
||||||
|
GXTexGenSrc src = GX_MAX_TEXGENSRC;
|
||||||
|
GXTexMtx mtx = GX_IDENTITY;
|
||||||
|
GXPTTexMtx postMtx = GX_PTIDENTITY;
|
||||||
|
bool normalize = false;
|
||||||
|
u8 _p1 = 0;
|
||||||
|
u8 _p2 = 0;
|
||||||
|
u8 _p3 = 0;
|
||||||
|
|
||||||
|
bool operator==(const TcgConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<TcgConfig>);
|
||||||
|
struct FogState {
|
||||||
|
GXFogType type = GX_FOG_NONE;
|
||||||
|
float startZ = 0.f;
|
||||||
|
float endZ = 0.f;
|
||||||
|
float nearZ = 0.f;
|
||||||
|
float farZ = 0.f;
|
||||||
|
Vec4<float> color;
|
||||||
|
|
||||||
|
bool operator==(const FogState& rhs) const {
|
||||||
|
return type == rhs.type && startZ == rhs.startZ && endZ == rhs.endZ && nearZ == rhs.nearZ && farZ == rhs.farZ &&
|
||||||
|
color == rhs.color;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct TevSwap {
|
||||||
|
GXTevColorChan red = GX_CH_RED;
|
||||||
|
GXTevColorChan green = GX_CH_GREEN;
|
||||||
|
GXTevColorChan blue = GX_CH_BLUE;
|
||||||
|
GXTevColorChan alpha = GX_CH_ALPHA;
|
||||||
|
|
||||||
|
bool operator==(const TevSwap& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
|
||||||
|
explicit operator bool() const { return !(*this == TevSwap{}); }
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<TevSwap>);
|
||||||
|
struct AlphaCompare {
|
||||||
|
GXCompare comp0 = GX_ALWAYS;
|
||||||
|
u32 ref0; // would be u8 but extended to avoid padding bytes
|
||||||
|
GXAlphaOp op = GX_AOP_AND;
|
||||||
|
GXCompare comp1 = GX_ALWAYS;
|
||||||
|
u32 ref1;
|
||||||
|
|
||||||
|
bool operator==(const AlphaCompare& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
|
||||||
|
explicit operator bool() const { return comp0 != GX_ALWAYS || comp1 != GX_ALWAYS; }
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<AlphaCompare>);
|
||||||
|
struct IndTexMtxInfo {
|
||||||
|
aurora::Mat3x2<float> mtx;
|
||||||
|
s8 scaleExp;
|
||||||
|
|
||||||
|
bool operator==(const IndTexMtxInfo& rhs) const { return mtx == rhs.mtx && scaleExp == rhs.scaleExp; }
|
||||||
|
};
|
||||||
|
struct VtxAttrFmt {
|
||||||
|
GXCompCnt cnt;
|
||||||
|
GXCompType type;
|
||||||
|
u8 frac;
|
||||||
|
};
|
||||||
|
struct VtxFmt {
|
||||||
|
std::array<VtxAttrFmt, MaxVtxAttr> attrs;
|
||||||
|
};
|
||||||
|
struct PnMtx {
|
||||||
|
Mat4x4<float> pos;
|
||||||
|
Mat4x4<float> nrm;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PnMtx) == sizeof(Mat4x4<float>) * 2);
|
||||||
|
struct Light {
|
||||||
|
Vec4<float> pos{0.f, 0.f, 0.f};
|
||||||
|
Vec4<float> dir{0.f, 0.f, 0.f};
|
||||||
|
Vec4<float> color{0.f, 0.f, 0.f, 0.f};
|
||||||
|
Vec4<float> cosAtt{0.f, 0.f, 0.f};
|
||||||
|
Vec4<float> distAtt{0.f, 0.f, 0.f};
|
||||||
|
|
||||||
|
bool operator==(const Light& rhs) const {
|
||||||
|
return pos == rhs.pos && dir == rhs.dir && color == rhs.color && cosAtt == rhs.cosAtt && distAtt == rhs.distAtt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Light) == 80);
|
||||||
|
struct AttrArray {
|
||||||
|
const void* data;
|
||||||
|
u32 size;
|
||||||
|
u8 stride;
|
||||||
|
Range cachedRange;
|
||||||
|
};
|
||||||
|
inline bool operator==(const AttrArray& lhs, const AttrArray& rhs) {
|
||||||
|
return lhs.data == rhs.data && lhs.size == rhs.size && lhs.stride == rhs.stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GXState {
|
||||||
|
std::array<PnMtx, MaxPnMtx> pnMtx;
|
||||||
|
u32 currentPnMtx;
|
||||||
|
Mat4x4<float> proj;
|
||||||
|
Mat4x4<float> origProj; // for GXGetProjectionv
|
||||||
|
GXProjectionType projType; // for GXGetProjectionv
|
||||||
|
FogState fog;
|
||||||
|
GXCullMode cullMode = GX_CULL_BACK;
|
||||||
|
GXBlendMode blendMode = GX_BM_NONE;
|
||||||
|
GXBlendFactor blendFacSrc = GX_BL_SRCALPHA;
|
||||||
|
GXBlendFactor blendFacDst = GX_BL_INVSRCALPHA;
|
||||||
|
GXLogicOp blendOp = GX_LO_CLEAR;
|
||||||
|
GXCompare depthFunc = GX_LEQUAL;
|
||||||
|
Vec4<float> clearColor{0.f, 0.f, 0.f, 1.f};
|
||||||
|
u32 dstAlpha; // u8; UINT32_MAX = disabled
|
||||||
|
AlphaCompare alphaCompare;
|
||||||
|
std::array<Vec4<float>, MaxTevRegs> colorRegs;
|
||||||
|
std::array<Vec4<float>, GX_MAX_KCOLOR> kcolors;
|
||||||
|
std::array<ColorChannelConfig, MaxColorChannels> colorChannelConfig;
|
||||||
|
std::array<ColorChannelState, MaxColorChannels> colorChannelState;
|
||||||
|
std::array<Light, GX::MaxLights> lights;
|
||||||
|
std::array<TevStage, MaxTevStages> tevStages;
|
||||||
|
std::array<TextureBind, MaxTextures> textures;
|
||||||
|
std::array<GXTlutObj_, MaxTextures> tluts;
|
||||||
|
std::array<TexMtxVariant, MaxTexMtx> texMtxs;
|
||||||
|
std::array<Mat4x4<float>, MaxPTTexMtx> ptTexMtxs;
|
||||||
|
std::array<TcgConfig, MaxTexCoord> tcgs;
|
||||||
|
std::array<GXAttrType, MaxVtxAttr> vtxDesc;
|
||||||
|
std::array<VtxFmt, MaxVtxFmt> vtxFmts;
|
||||||
|
std::array<TevSwap, MaxTevSwap> tevSwapTable{
|
||||||
|
TevSwap{},
|
||||||
|
TevSwap{GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA},
|
||||||
|
TevSwap{GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_ALPHA},
|
||||||
|
TevSwap{GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA},
|
||||||
|
};
|
||||||
|
std::array<IndStage, MaxIndStages> indStages;
|
||||||
|
std::array<IndTexMtxInfo, MaxIndTexMtxs> indTexMtxs;
|
||||||
|
std::array<AttrArray, GX_VA_MAX_ATTR> arrays;
|
||||||
|
bool depthCompare = true;
|
||||||
|
bool depthUpdate = true;
|
||||||
|
bool colorUpdate = true;
|
||||||
|
bool alphaUpdate = true;
|
||||||
|
u8 numChans = 0;
|
||||||
|
u8 numIndStages = 0;
|
||||||
|
u8 numTevStages = 0;
|
||||||
|
u8 numTexGens = 0;
|
||||||
|
bool stateDirty = true;
|
||||||
|
};
|
||||||
|
extern GXState g_gxState;
|
||||||
|
|
||||||
|
void shutdown() noexcept;
|
||||||
|
const TextureBind& get_texture(GXTexMapID id) noexcept;
|
||||||
|
|
||||||
|
static inline bool requires_copy_conversion(const GXTexObj_& obj) {
|
||||||
|
if (!obj.ref) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (obj.ref->isRenderTexture) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch (obj.ref->gxFormat) {
|
||||||
|
// case GX_TF_RGB565:
|
||||||
|
// case GX_TF_I4:
|
||||||
|
// case GX_TF_I8:
|
||||||
|
case GX_TF_C4:
|
||||||
|
case GX_TF_C8:
|
||||||
|
case GX_TF_C14X2:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static inline bool requires_load_conversion(const GXTexObj_& obj) {
|
||||||
|
if (!obj.ref) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (obj.fmt) {
|
||||||
|
case GX_TF_I4:
|
||||||
|
case GX_TF_I8:
|
||||||
|
case GX_TF_C4:
|
||||||
|
case GX_TF_C8:
|
||||||
|
case GX_TF_C14X2:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static inline bool is_palette_format(u32 fmt) { return fmt == GX_TF_C4 || fmt == GX_TF_C8 || fmt == GX_TF_C14X2; }
|
||||||
|
|
||||||
|
struct TextureConfig {
|
||||||
|
u32 copyFmt = InvalidTextureFormat; // Underlying texture format
|
||||||
|
u32 loadFmt = InvalidTextureFormat; // Texture format being bound
|
||||||
|
bool renderTex = false; // Perform conversion
|
||||||
|
u8 _p1 = 0;
|
||||||
|
u8 _p2 = 0;
|
||||||
|
u8 _p3 = 0;
|
||||||
|
|
||||||
|
bool operator==(const TextureConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<TextureConfig>);
|
||||||
|
struct ShaderConfig {
|
||||||
|
GXFogType fogType;
|
||||||
|
std::array<GXAttrType, MaxVtxAttr> vtxAttrs;
|
||||||
|
// Mapping for indexed attributes -> storage buffer
|
||||||
|
std::array<GXAttr, MaxVtxAttr> attrMapping;
|
||||||
|
std::array<TevSwap, MaxTevSwap> tevSwapTable;
|
||||||
|
std::array<TevStage, MaxTevStages> tevStages;
|
||||||
|
u32 tevStageCount = 0;
|
||||||
|
std::array<ColorChannelConfig, MaxColorChannels> colorChannels;
|
||||||
|
std::array<TcgConfig, MaxTexCoord> tcgs;
|
||||||
|
AlphaCompare alphaCompare;
|
||||||
|
u32 indexedAttributeCount = 0;
|
||||||
|
std::array<TextureConfig, MaxTextures> textureConfig;
|
||||||
|
|
||||||
|
bool operator==(const ShaderConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; }
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<ShaderConfig>);
|
||||||
|
|
||||||
|
constexpr u32 GXPipelineConfigVersion = 4;
|
||||||
|
struct PipelineConfig {
|
||||||
|
u32 version = GXPipelineConfigVersion;
|
||||||
|
ShaderConfig shaderConfig;
|
||||||
|
GXPrimitive primitive;
|
||||||
|
GXCompare depthFunc;
|
||||||
|
GXCullMode cullMode;
|
||||||
|
GXBlendMode blendMode;
|
||||||
|
GXBlendFactor blendFacSrc, blendFacDst;
|
||||||
|
GXLogicOp blendOp;
|
||||||
|
u32 dstAlpha;
|
||||||
|
bool depthCompare, depthUpdate, alphaUpdate, colorUpdate;
|
||||||
|
};
|
||||||
|
static_assert(std::has_unique_object_representations_v<PipelineConfig>);
|
||||||
|
|
||||||
|
struct GXBindGroupLayouts {
|
||||||
|
WGPUBindGroupLayout uniformLayout;
|
||||||
|
WGPUBindGroupLayout samplerLayout;
|
||||||
|
WGPUBindGroupLayout textureLayout;
|
||||||
|
};
|
||||||
|
struct GXBindGroups {
|
||||||
|
BindGroupRef uniformBindGroup;
|
||||||
|
BindGroupRef samplerBindGroup;
|
||||||
|
BindGroupRef textureBindGroup;
|
||||||
|
};
|
||||||
|
// Output info from shader generation
|
||||||
|
struct ShaderInfo {
|
||||||
|
std::bitset<MaxTexCoord> sampledTexCoords;
|
||||||
|
std::bitset<MaxTextures> sampledTextures;
|
||||||
|
std::bitset<MaxKColors> sampledKColors;
|
||||||
|
std::bitset<MaxColorChannels / 2> sampledColorChannels;
|
||||||
|
std::bitset<MaxTevRegs> loadsTevReg;
|
||||||
|
std::bitset<MaxTevRegs> writesTevReg;
|
||||||
|
std::bitset<MaxTexMtx> usesTexMtx;
|
||||||
|
std::bitset<MaxPTTexMtx> usesPTTexMtx;
|
||||||
|
std::array<GXTexGenType, MaxTexMtx> texMtxTypes{};
|
||||||
|
u32 uniformSize = 0;
|
||||||
|
bool usesFog : 1 = false;
|
||||||
|
};
|
||||||
|
struct BindGroupRanges {
|
||||||
|
std::array<Range, GX_VA_MAX_ATTR> vaRanges{};
|
||||||
|
};
|
||||||
|
void populate_pipeline_config(PipelineConfig& config, GXPrimitive primitive) noexcept;
|
||||||
|
WGPURenderPipeline build_pipeline(const PipelineConfig& config, const ShaderInfo& info,
|
||||||
|
ArrayRef<WGPUVertexBufferLayout> vtxBuffers, WGPUShaderModule shader,
|
||||||
|
const char* label) noexcept;
|
||||||
|
ShaderInfo build_shader_info(const ShaderConfig& config) noexcept;
|
||||||
|
WGPUShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& info) noexcept;
|
||||||
|
// Range build_vertex_buffer(const GXShaderInfo& info) noexcept;
|
||||||
|
Range build_uniform(const ShaderInfo& info) noexcept;
|
||||||
|
GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const ShaderConfig& config) noexcept;
|
||||||
|
GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& config,
|
||||||
|
const BindGroupRanges& ranges) noexcept;
|
||||||
|
} // namespace aurora::gfx::gx
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,499 @@
|
||||||
|
#include "shader.hpp"
|
||||||
|
|
||||||
|
#include "../../webgpu/gpu.hpp"
|
||||||
|
|
||||||
|
#include <absl/container/flat_hash_map.h>
|
||||||
|
|
||||||
|
namespace aurora::gfx::model {
|
||||||
|
static Module Log("aurora::gfx::model");
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T bswap16(T val) noexcept {
|
||||||
|
static_assert(sizeof(T) == sizeof(u16));
|
||||||
|
union {
|
||||||
|
u16 u;
|
||||||
|
T t;
|
||||||
|
} v{.t = val};
|
||||||
|
#if __GNUC__
|
||||||
|
v.u = __builtin_bswap16(v.u);
|
||||||
|
#elif _WIN32
|
||||||
|
v.u = _byteswap_ushort(v.u);
|
||||||
|
#else
|
||||||
|
v.u = (v.u << 8) | ((v.u >> 8) & 0xFF);
|
||||||
|
#endif
|
||||||
|
return v.t;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
constexpr T bswap32(T val) noexcept {
|
||||||
|
static_assert(sizeof(T) == sizeof(u32));
|
||||||
|
union {
|
||||||
|
u32 u;
|
||||||
|
T t;
|
||||||
|
} v{.t = val};
|
||||||
|
#if __GNUC__
|
||||||
|
v.u = __builtin_bswap32(v.u);
|
||||||
|
#elif _WIN32
|
||||||
|
v.u = _byteswap_ulong(v.u);
|
||||||
|
#else
|
||||||
|
v.u = ((v.u & 0x0000FFFF) << 16) | ((v.u & 0xFFFF0000) >> 16) | ((v.u & 0x00FF00FF) << 8) | ((v.u & 0xFF00FF00) >> 8);
|
||||||
|
#endif
|
||||||
|
return v.t;
|
||||||
|
}
|
||||||
|
|
||||||
|
using IndexedAttrs = std::array<bool, GX_VA_MAX_ATTR>;
|
||||||
|
struct DisplayListCache {
|
||||||
|
ByteBuffer vtxBuf;
|
||||||
|
ByteBuffer idxBuf;
|
||||||
|
IndexedAttrs indexedAttrs;
|
||||||
|
|
||||||
|
DisplayListCache(ByteBuffer&& vtxBuf, ByteBuffer&& idxBuf, IndexedAttrs indexedAttrs)
|
||||||
|
: vtxBuf(std::move(vtxBuf)), idxBuf(std::move(idxBuf)), indexedAttrs(indexedAttrs) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static absl::flat_hash_map<HashType, DisplayListCache> sCachedDisplayLists;
|
||||||
|
|
||||||
|
static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u16 vtxCount,
|
||||||
|
IndexedAttrs& indexedAttrs) {
|
||||||
|
using aurora::gfx::gx::g_gxState;
|
||||||
|
struct {
|
||||||
|
u8 count;
|
||||||
|
GXCompType type;
|
||||||
|
} attrArrays[GX_VA_MAX_ATTR] = {};
|
||||||
|
u32 vtxSize = 0;
|
||||||
|
u32 outVtxSize = 0;
|
||||||
|
|
||||||
|
// Calculate attribute offsets and vertex size
|
||||||
|
for (int attr = 0; attr < GX_VA_MAX_ATTR; attr++) {
|
||||||
|
const auto& attrFmt = g_gxState.vtxFmts[vtxfmt].attrs[attr];
|
||||||
|
switch (g_gxState.vtxDesc[attr]) {
|
||||||
|
case GX_NONE:
|
||||||
|
break;
|
||||||
|
case GX_DIRECT:
|
||||||
|
#define COMBINE(val1, val2, val3) (((val1) << 16) | ((val2) << 8) | (val3))
|
||||||
|
switch (COMBINE(attr, attrFmt.cnt, attrFmt.type)) {
|
||||||
|
case COMBINE(GX_VA_POS, GX_POS_XYZ, GX_F32):
|
||||||
|
case COMBINE(GX_VA_NRM, GX_NRM_XYZ, GX_F32):
|
||||||
|
attrArrays[attr].count = 3;
|
||||||
|
attrArrays[attr].type = GX_F32;
|
||||||
|
vtxSize += 12;
|
||||||
|
outVtxSize += 12;
|
||||||
|
break;
|
||||||
|
case COMBINE(GX_VA_POS, GX_POS_XYZ, GX_S16):
|
||||||
|
case COMBINE(GX_VA_NRM, GX_NRM_XYZ, GX_S16):
|
||||||
|
attrArrays[attr].count = 3;
|
||||||
|
attrArrays[attr].type = GX_S16;
|
||||||
|
vtxSize += 6;
|
||||||
|
outVtxSize += 12;
|
||||||
|
break;
|
||||||
|
case COMBINE(GX_VA_TEX0, GX_TEX_ST, GX_F32):
|
||||||
|
case COMBINE(GX_VA_TEX1, GX_TEX_ST, GX_F32):
|
||||||
|
case COMBINE(GX_VA_TEX2, GX_TEX_ST, GX_F32):
|
||||||
|
case COMBINE(GX_VA_TEX3, GX_TEX_ST, GX_F32):
|
||||||
|
case COMBINE(GX_VA_TEX4, GX_TEX_ST, GX_F32):
|
||||||
|
case COMBINE(GX_VA_TEX5, GX_TEX_ST, GX_F32):
|
||||||
|
case COMBINE(GX_VA_TEX6, GX_TEX_ST, GX_F32):
|
||||||
|
case COMBINE(GX_VA_TEX7, GX_TEX_ST, GX_F32):
|
||||||
|
attrArrays[attr].count = 2;
|
||||||
|
attrArrays[attr].type = GX_F32;
|
||||||
|
vtxSize += 8;
|
||||||
|
outVtxSize += 8;
|
||||||
|
break;
|
||||||
|
case COMBINE(GX_VA_TEX0, GX_TEX_ST, GX_S16):
|
||||||
|
case COMBINE(GX_VA_TEX1, GX_TEX_ST, GX_S16):
|
||||||
|
case COMBINE(GX_VA_TEX2, GX_TEX_ST, GX_S16):
|
||||||
|
case COMBINE(GX_VA_TEX3, GX_TEX_ST, GX_S16):
|
||||||
|
case COMBINE(GX_VA_TEX4, GX_TEX_ST, GX_S16):
|
||||||
|
case COMBINE(GX_VA_TEX5, GX_TEX_ST, GX_S16):
|
||||||
|
case COMBINE(GX_VA_TEX6, GX_TEX_ST, GX_S16):
|
||||||
|
case COMBINE(GX_VA_TEX7, GX_TEX_ST, GX_S16):
|
||||||
|
attrArrays[attr].count = 2;
|
||||||
|
attrArrays[attr].type = GX_S16;
|
||||||
|
vtxSize += 4;
|
||||||
|
outVtxSize += 8;
|
||||||
|
break;
|
||||||
|
case COMBINE(GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8):
|
||||||
|
case COMBINE(GX_VA_CLR1, GX_CLR_RGBA, GX_RGBA8):
|
||||||
|
attrArrays[attr].count = 4;
|
||||||
|
attrArrays[attr].type = GX_RGBA8;
|
||||||
|
vtxSize += 4;
|
||||||
|
outVtxSize += 16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("not handled: attr {}, cnt {}, type {}"), attr, attrFmt.cnt, attrFmt.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#undef COMBINE
|
||||||
|
break;
|
||||||
|
case GX_INDEX8:
|
||||||
|
++vtxSize;
|
||||||
|
outVtxSize += 2;
|
||||||
|
indexedAttrs[attr] = true;
|
||||||
|
break;
|
||||||
|
case GX_INDEX16:
|
||||||
|
vtxSize += 2;
|
||||||
|
outVtxSize += 2;
|
||||||
|
indexedAttrs[attr] = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("unhandled attribute type {}"), g_gxState.vtxDesc[attr]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Align to 4
|
||||||
|
int rem = outVtxSize % 4;
|
||||||
|
int padding = 0;
|
||||||
|
if (rem != 0) {
|
||||||
|
padding = 4 - rem;
|
||||||
|
outVtxSize += padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build vertex buffer
|
||||||
|
buf.reserve_extra(vtxCount * outVtxSize);
|
||||||
|
std::array<f32, 4> out{};
|
||||||
|
for (u32 v = 0; v < vtxCount; ++v) {
|
||||||
|
for (int attr = 0; attr < GX_VA_MAX_ATTR; attr++) {
|
||||||
|
if (g_gxState.vtxDesc[attr] == GX_INDEX8) {
|
||||||
|
u16 index = *ptr;
|
||||||
|
buf.append(&index, 2);
|
||||||
|
++ptr;
|
||||||
|
} else if (g_gxState.vtxDesc[attr] == GX_INDEX16) {
|
||||||
|
u16 index = bswap16(*reinterpret_cast<const u16*>(ptr));
|
||||||
|
buf.append(&index, 2);
|
||||||
|
ptr += 2;
|
||||||
|
}
|
||||||
|
if (g_gxState.vtxDesc[attr] != GX_DIRECT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& attrFmt = g_gxState.vtxFmts[vtxfmt].attrs[attr];
|
||||||
|
u8 count = attrArrays[attr].count;
|
||||||
|
switch (attrArrays[attr].type) {
|
||||||
|
case GX_U8:
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
const auto value = reinterpret_cast<const u8*>(ptr)[i];
|
||||||
|
out[i] = static_cast<f32>(value) / static_cast<f32>(1 << attrFmt.frac);
|
||||||
|
}
|
||||||
|
buf.append(out.data(), sizeof(f32) * count);
|
||||||
|
ptr += count;
|
||||||
|
break;
|
||||||
|
case GX_S8:
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
const auto value = reinterpret_cast<const s8*>(ptr)[i];
|
||||||
|
out[i] = static_cast<f32>(value) / static_cast<f32>(1 << attrFmt.frac);
|
||||||
|
}
|
||||||
|
buf.append(out.data(), sizeof(f32) * count);
|
||||||
|
ptr += count;
|
||||||
|
break;
|
||||||
|
case GX_U16:
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
const auto value = bswap16(reinterpret_cast<const u16*>(ptr)[i]);
|
||||||
|
out[i] = static_cast<f32>(value) / static_cast<f32>(1 << attrFmt.frac);
|
||||||
|
}
|
||||||
|
buf.append(out.data(), sizeof(f32) * count);
|
||||||
|
ptr += count * sizeof(u16);
|
||||||
|
break;
|
||||||
|
case GX_S16:
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
const auto value = bswap16(reinterpret_cast<const s16*>(ptr)[i]);
|
||||||
|
out[i] = static_cast<f32>(value) / static_cast<f32>(1 << attrFmt.frac);
|
||||||
|
}
|
||||||
|
buf.append(out.data(), sizeof(f32) * count);
|
||||||
|
ptr += count * sizeof(s16);
|
||||||
|
break;
|
||||||
|
case GX_F32:
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
out[i] = bswap32(reinterpret_cast<const f32*>(ptr)[i]);
|
||||||
|
}
|
||||||
|
buf.append(out.data(), sizeof(f32) * count);
|
||||||
|
ptr += count * sizeof(f32);
|
||||||
|
break;
|
||||||
|
case GX_RGBA8:
|
||||||
|
out[0] = static_cast<f32>(ptr[0]) / 255.f;
|
||||||
|
out[1] = static_cast<f32>(ptr[1]) / 255.f;
|
||||||
|
out[2] = static_cast<f32>(ptr[2]) / 255.f;
|
||||||
|
out[3] = static_cast<f32>(ptr[3]) / 255.f;
|
||||||
|
buf.append(out.data(), sizeof(f32) * 4);
|
||||||
|
ptr += sizeof(u32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (padding > 0) {
|
||||||
|
buf.append_zeroes(padding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vtxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u16 prepare_idx_buffer(ByteBuffer& buf, GXPrimitive prim, u16 vtxStart, u16 vtxCount) {
|
||||||
|
u16 numIndices = 0;
|
||||||
|
if (prim == GX_TRIANGLES) {
|
||||||
|
buf.reserve_extra(vtxCount * sizeof(u16));
|
||||||
|
for (u16 v = 0; v < vtxCount; ++v) {
|
||||||
|
const u16 idx = vtxStart + v;
|
||||||
|
buf.append(&idx, sizeof(u16));
|
||||||
|
++numIndices;
|
||||||
|
}
|
||||||
|
} else if (prim == GX_TRIANGLEFAN) {
|
||||||
|
buf.reserve_extra(((u32(vtxCount) - 3) * 3 + 3) * sizeof(u16));
|
||||||
|
for (u16 v = 0; v < vtxCount; ++v) {
|
||||||
|
const u16 idx = vtxStart + v;
|
||||||
|
if (v < 3) {
|
||||||
|
buf.append(&idx, sizeof(u16));
|
||||||
|
++numIndices;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const std::array<u16, 3> idxs{vtxStart, u16(idx - 1), idx};
|
||||||
|
buf.append(idxs.data(), sizeof(u16) * 3);
|
||||||
|
numIndices += 3;
|
||||||
|
}
|
||||||
|
} else if (prim == GX_TRIANGLESTRIP) {
|
||||||
|
buf.reserve_extra(((u32(vtxCount) - 3) * 3 + 3) * sizeof(u16));
|
||||||
|
for (u16 v = 0; v < vtxCount; ++v) {
|
||||||
|
const u16 idx = vtxStart + v;
|
||||||
|
if (v < 3) {
|
||||||
|
buf.append(&idx, sizeof(u16));
|
||||||
|
++numIndices;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((v & 1) == 0) {
|
||||||
|
const std::array<u16, 3> idxs{u16(idx - 2), u16(idx - 1), idx};
|
||||||
|
buf.append(idxs.data(), sizeof(u16) * 3);
|
||||||
|
} else {
|
||||||
|
const std::array<u16, 3> idxs{u16(idx - 1), u16(idx - 2), idx};
|
||||||
|
buf.append(idxs.data(), sizeof(u16) * 3);
|
||||||
|
}
|
||||||
|
numIndices += 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Unsupported primitive type {}"), static_cast<u32>(prim));
|
||||||
|
}
|
||||||
|
return numIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue_surface(const u8* dlStart, u32 dlSize) noexcept {
|
||||||
|
const auto hash = xxh3_hash_s(dlStart, dlSize, 0);
|
||||||
|
Range vertRange, idxRange;
|
||||||
|
u32 numIndices = 0;
|
||||||
|
IndexedAttrs indexedAttrs{};
|
||||||
|
auto it = sCachedDisplayLists.find(hash);
|
||||||
|
if (it != sCachedDisplayLists.end()) {
|
||||||
|
const auto& cache = it->second;
|
||||||
|
numIndices = cache.idxBuf.size() / 2;
|
||||||
|
vertRange = push_verts(cache.vtxBuf.data(), cache.vtxBuf.size());
|
||||||
|
idxRange = push_indices(cache.idxBuf.data(), cache.idxBuf.size());
|
||||||
|
indexedAttrs = cache.indexedAttrs;
|
||||||
|
} else {
|
||||||
|
const u8* data = dlStart;
|
||||||
|
u32 pos = 0;
|
||||||
|
ByteBuffer vtxBuf;
|
||||||
|
ByteBuffer idxBuf;
|
||||||
|
u16 vtxStart = 0;
|
||||||
|
|
||||||
|
while (pos < dlSize) {
|
||||||
|
u8 cmd = data[pos++];
|
||||||
|
|
||||||
|
u8 opcode = cmd & GX_OPCODE_MASK;
|
||||||
|
switch (opcode) {
|
||||||
|
case GX_NOP:
|
||||||
|
continue;
|
||||||
|
case GX_LOAD_BP_REG:
|
||||||
|
// TODO?
|
||||||
|
pos += 4;
|
||||||
|
break;
|
||||||
|
case GX_DRAW_QUADS:
|
||||||
|
case GX_DRAW_TRIANGLES:
|
||||||
|
case GX_DRAW_TRIANGLE_STRIP:
|
||||||
|
case GX_DRAW_TRIANGLE_FAN: {
|
||||||
|
const auto prim = static_cast<GXPrimitive>(opcode);
|
||||||
|
const auto fmt = static_cast<GXVtxFmt>(cmd & GX_VAT_MASK);
|
||||||
|
u16 vtxCount = bswap16(*reinterpret_cast<const u16*>(data + pos));
|
||||||
|
pos += 2;
|
||||||
|
pos += vtxCount * prepare_vtx_buffer(vtxBuf, fmt, data + pos, vtxCount, indexedAttrs);
|
||||||
|
numIndices += prepare_idx_buffer(idxBuf, prim, vtxStart, vtxCount);
|
||||||
|
vtxStart += vtxCount;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GX_DRAW_LINES:
|
||||||
|
case GX_DRAW_LINE_STRIP:
|
||||||
|
case GX_DRAW_POINTS:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("unimplemented prim type: {}"), opcode);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("unimplemented opcode: {}"), opcode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vertRange = push_verts(vtxBuf.data(), vtxBuf.size());
|
||||||
|
idxRange = push_indices(idxBuf.data(), idxBuf.size());
|
||||||
|
sCachedDisplayLists.try_emplace(hash, std::move(vtxBuf), std::move(idxBuf), indexedAttrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
gx::BindGroupRanges ranges{};
|
||||||
|
int lastIndexedAttr = -1;
|
||||||
|
for (int i = 0; i < GX_VA_MAX_ATTR; ++i) {
|
||||||
|
if (!indexedAttrs[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& array = gx::g_gxState.arrays[i];
|
||||||
|
if (lastIndexedAttr >= 0 && array == gx::g_gxState.arrays[lastIndexedAttr]) {
|
||||||
|
// Reuse range from last attribute in shader
|
||||||
|
// Don't set the output range, so it remains unbound
|
||||||
|
const auto range = gx::g_gxState.arrays[lastIndexedAttr].cachedRange;
|
||||||
|
array.cachedRange = range;
|
||||||
|
} else if (array.cachedRange.size > 0) {
|
||||||
|
// Use the currently cached range
|
||||||
|
ranges.vaRanges[i] = array.cachedRange;
|
||||||
|
} else {
|
||||||
|
// Push array data to storage and cache range
|
||||||
|
const auto range = push_storage(static_cast<const uint8_t*>(array.data), array.size);
|
||||||
|
ranges.vaRanges[i] = range;
|
||||||
|
array.cachedRange = range;
|
||||||
|
}
|
||||||
|
lastIndexedAttr = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
model::PipelineConfig config{};
|
||||||
|
populate_pipeline_config(config, GX_TRIANGLES);
|
||||||
|
const auto info = gx::build_shader_info(config.shaderConfig);
|
||||||
|
const auto bindGroups = gx::build_bind_groups(info, config.shaderConfig, ranges);
|
||||||
|
const auto pipeline = pipeline_ref(config);
|
||||||
|
|
||||||
|
push_draw_command(model::DrawData{
|
||||||
|
.pipeline = pipeline,
|
||||||
|
.vertRange = vertRange,
|
||||||
|
.idxRange = idxRange,
|
||||||
|
.dataRanges = ranges,
|
||||||
|
.uniformRange = build_uniform(info),
|
||||||
|
.indexCount = numIndices,
|
||||||
|
.bindGroups = bindGroups,
|
||||||
|
.dstAlpha = gx::g_gxState.dstAlpha,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
State construct_state() { return {}; }
|
||||||
|
|
||||||
|
WGPURenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config) {
|
||||||
|
const auto info = build_shader_info(config.shaderConfig); // TODO remove
|
||||||
|
const auto shader = build_shader(config.shaderConfig, info);
|
||||||
|
|
||||||
|
std::array<WGPUVertexAttribute, gx::MaxVtxAttr> vtxAttrs{};
|
||||||
|
auto [num4xAttr, rem] = std::div(config.shaderConfig.indexedAttributeCount, 4);
|
||||||
|
u32 num2xAttr = 0;
|
||||||
|
if (rem > 2) {
|
||||||
|
++num4xAttr;
|
||||||
|
} else if (rem > 0) {
|
||||||
|
++num2xAttr;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 offset = 0;
|
||||||
|
u32 shaderLocation = 0;
|
||||||
|
|
||||||
|
// Indexed attributes
|
||||||
|
for (u32 i = 0; i < num4xAttr; ++i) {
|
||||||
|
vtxAttrs[shaderLocation] = {
|
||||||
|
.format = WGPUVertexFormat_Sint16x4,
|
||||||
|
.offset = offset,
|
||||||
|
.shaderLocation = shaderLocation,
|
||||||
|
};
|
||||||
|
offset += 8;
|
||||||
|
++shaderLocation;
|
||||||
|
}
|
||||||
|
for (u32 i = 0; i < num2xAttr; ++i) {
|
||||||
|
vtxAttrs[shaderLocation] = {
|
||||||
|
.format = WGPUVertexFormat_Sint16x2,
|
||||||
|
.offset = offset,
|
||||||
|
.shaderLocation = shaderLocation,
|
||||||
|
};
|
||||||
|
offset += 4;
|
||||||
|
++shaderLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct attributes
|
||||||
|
for (int i = 0; i < gx::MaxVtxAttr; ++i) {
|
||||||
|
const auto attrType = config.shaderConfig.vtxAttrs[i];
|
||||||
|
if (attrType != GX_DIRECT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto attr = static_cast<GXAttr>(i);
|
||||||
|
switch (attr) {
|
||||||
|
case GX_VA_POS:
|
||||||
|
case GX_VA_NRM:
|
||||||
|
vtxAttrs[shaderLocation] = WGPUVertexAttribute{
|
||||||
|
.format = WGPUVertexFormat_Float32x3,
|
||||||
|
.offset = offset,
|
||||||
|
.shaderLocation = shaderLocation,
|
||||||
|
};
|
||||||
|
offset += 12;
|
||||||
|
break;
|
||||||
|
case GX_VA_CLR0:
|
||||||
|
case GX_VA_CLR1:
|
||||||
|
vtxAttrs[shaderLocation] = WGPUVertexAttribute{
|
||||||
|
.format = WGPUVertexFormat_Float32x4,
|
||||||
|
.offset = offset,
|
||||||
|
.shaderLocation = shaderLocation,
|
||||||
|
};
|
||||||
|
offset += 16;
|
||||||
|
break;
|
||||||
|
case GX_VA_TEX0:
|
||||||
|
case GX_VA_TEX1:
|
||||||
|
case GX_VA_TEX2:
|
||||||
|
case GX_VA_TEX3:
|
||||||
|
case GX_VA_TEX4:
|
||||||
|
case GX_VA_TEX5:
|
||||||
|
case GX_VA_TEX6:
|
||||||
|
case GX_VA_TEX7:
|
||||||
|
vtxAttrs[shaderLocation] = WGPUVertexAttribute{
|
||||||
|
.format = WGPUVertexFormat_Float32x2,
|
||||||
|
.offset = offset,
|
||||||
|
.shaderLocation = shaderLocation,
|
||||||
|
};
|
||||||
|
offset += 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("unhandled direct attr {}"), i);
|
||||||
|
}
|
||||||
|
++shaderLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::array vtxBuffers{WGPUVertexBufferLayout{
|
||||||
|
.arrayStride = offset,
|
||||||
|
.stepMode = WGPUVertexStepMode_Vertex,
|
||||||
|
.attributeCount = shaderLocation,
|
||||||
|
.attributes = vtxAttrs.data(),
|
||||||
|
}};
|
||||||
|
|
||||||
|
return build_pipeline(config, info, vtxBuffers, shader, "GX Pipeline");
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(const State& state, const DrawData& data, const WGPURenderPassEncoder& pass) {
|
||||||
|
if (!bind_pipeline(data.pipeline, pass)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint32_t, GX_VA_MAX_ATTR + 1> offsets{data.uniformRange.offset};
|
||||||
|
uint32_t bindIdx = 1;
|
||||||
|
for (uint32_t i = 0; i < GX_VA_MAX_ATTR; ++i) {
|
||||||
|
const auto& range = data.dataRanges.vaRanges[i];
|
||||||
|
if (range.size <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
offsets[bindIdx] = range.offset;
|
||||||
|
++bindIdx;
|
||||||
|
}
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(pass, 0, find_bind_group(data.bindGroups.uniformBindGroup), bindIdx,
|
||||||
|
offsets.data());
|
||||||
|
if (data.bindGroups.samplerBindGroup && data.bindGroups.textureBindGroup) {
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(pass, 1, find_bind_group(data.bindGroups.samplerBindGroup), 0, nullptr);
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(pass, 2, find_bind_group(data.bindGroups.textureBindGroup), 0, nullptr);
|
||||||
|
}
|
||||||
|
wgpuRenderPassEncoderSetVertexBuffer(pass, 0, g_vertexBuffer, data.vertRange.offset, data.vertRange.size);
|
||||||
|
wgpuRenderPassEncoderSetIndexBuffer(pass, g_indexBuffer, WGPUIndexFormat_Uint16, data.idxRange.offset,
|
||||||
|
data.idxRange.size);
|
||||||
|
if (data.dstAlpha != UINT32_MAX) {
|
||||||
|
const WGPUColor color{0.f, 0.f, 0.f, data.dstAlpha / 255.f};
|
||||||
|
wgpuRenderPassEncoderSetBlendConstant(pass, &color);
|
||||||
|
}
|
||||||
|
wgpuRenderPassEncoderDrawIndexed(pass, data.indexCount, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
} // namespace aurora::gfx::model
|
||||||
|
|
||||||
|
static absl::flat_hash_map<aurora::HashType, aurora::gfx::Range> sCachedRanges;
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common.hpp"
|
||||||
|
#include "../gx.hpp"
|
||||||
|
|
||||||
|
namespace aurora::gfx::model {
|
||||||
|
struct DrawData {
|
||||||
|
PipelineRef pipeline;
|
||||||
|
Range vertRange;
|
||||||
|
Range idxRange;
|
||||||
|
gx::BindGroupRanges dataRanges;
|
||||||
|
Range uniformRange;
|
||||||
|
uint32_t indexCount;
|
||||||
|
gx::GXBindGroups bindGroups;
|
||||||
|
u32 dstAlpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipelineConfig : gx::PipelineConfig {};
|
||||||
|
|
||||||
|
struct State {};
|
||||||
|
|
||||||
|
State construct_state();
|
||||||
|
WGPURenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config);
|
||||||
|
void render(const State& state, const DrawData& data, const WGPURenderPassEncoder& pass);
|
||||||
|
|
||||||
|
void queue_surface(const u8* dlStart, u32 dlSize) noexcept;
|
||||||
|
} // namespace aurora::gfx::model
|
|
@ -0,0 +1,84 @@
|
||||||
|
#include "shader.hpp"
|
||||||
|
|
||||||
|
#include "../../webgpu/gpu.hpp"
|
||||||
|
|
||||||
|
namespace aurora::gfx::stream {
|
||||||
|
static Module Log("aurora::gfx::stream");
|
||||||
|
|
||||||
|
using webgpu::g_device;
|
||||||
|
|
||||||
|
WGPURenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config) {
|
||||||
|
const auto info = build_shader_info(config.shaderConfig); // TODO remove
|
||||||
|
const auto shader = build_shader(config.shaderConfig, info);
|
||||||
|
|
||||||
|
std::array<WGPUVertexAttribute, 4> attributes{};
|
||||||
|
attributes[0] = WGPUVertexAttribute{
|
||||||
|
.format = WGPUVertexFormat_Float32x3,
|
||||||
|
.offset = 0,
|
||||||
|
.shaderLocation = 0,
|
||||||
|
};
|
||||||
|
uint64_t offset = 12;
|
||||||
|
uint32_t shaderLocation = 1;
|
||||||
|
if (config.shaderConfig.vtxAttrs[GX_VA_NRM] == GX_DIRECT) {
|
||||||
|
attributes[shaderLocation] = WGPUVertexAttribute{
|
||||||
|
.format = WGPUVertexFormat_Float32x3,
|
||||||
|
.offset = offset,
|
||||||
|
.shaderLocation = shaderLocation,
|
||||||
|
};
|
||||||
|
offset += 12;
|
||||||
|
shaderLocation++;
|
||||||
|
}
|
||||||
|
if (config.shaderConfig.vtxAttrs[GX_VA_CLR0] == GX_DIRECT) {
|
||||||
|
attributes[shaderLocation] = WGPUVertexAttribute{
|
||||||
|
.format = WGPUVertexFormat_Float32x4,
|
||||||
|
.offset = offset,
|
||||||
|
.shaderLocation = shaderLocation,
|
||||||
|
};
|
||||||
|
offset += 16;
|
||||||
|
shaderLocation++;
|
||||||
|
}
|
||||||
|
for (int i = GX_VA_TEX0; i < GX_VA_TEX7; ++i) {
|
||||||
|
if (config.shaderConfig.vtxAttrs[i] != GX_DIRECT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
attributes[shaderLocation] = WGPUVertexAttribute{
|
||||||
|
.format = WGPUVertexFormat_Float32x2,
|
||||||
|
.offset = offset,
|
||||||
|
.shaderLocation = shaderLocation,
|
||||||
|
};
|
||||||
|
offset += 8;
|
||||||
|
shaderLocation++;
|
||||||
|
}
|
||||||
|
const std::array vertexBuffers{WGPUVertexBufferLayout{
|
||||||
|
.arrayStride = offset,
|
||||||
|
.attributeCount = shaderLocation,
|
||||||
|
.attributes = attributes.data(),
|
||||||
|
}};
|
||||||
|
|
||||||
|
return build_pipeline(config, info, vertexBuffers, shader, "Stream Pipeline");
|
||||||
|
}
|
||||||
|
|
||||||
|
State construct_state() { return {}; }
|
||||||
|
|
||||||
|
void render(const State& state, const DrawData& data, const WGPURenderPassEncoder& pass) {
|
||||||
|
if (!bind_pipeline(data.pipeline, pass)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::array offsets{data.uniformRange.offset};
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(pass, 0, find_bind_group(data.bindGroups.uniformBindGroup), offsets.size(),
|
||||||
|
offsets.data());
|
||||||
|
if (data.bindGroups.samplerBindGroup && data.bindGroups.textureBindGroup) {
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(pass, 1, find_bind_group(data.bindGroups.samplerBindGroup), 0, nullptr);
|
||||||
|
wgpuRenderPassEncoderSetBindGroup(pass, 2, find_bind_group(data.bindGroups.textureBindGroup), 0, nullptr);
|
||||||
|
}
|
||||||
|
wgpuRenderPassEncoderSetVertexBuffer(pass, 0, g_vertexBuffer, data.vertRange.offset, data.vertRange.size);
|
||||||
|
wgpuRenderPassEncoderSetIndexBuffer(pass, g_indexBuffer, WGPUIndexFormat_Uint16, data.indexRange.offset,
|
||||||
|
data.indexRange.size);
|
||||||
|
if (data.dstAlpha != UINT32_MAX) {
|
||||||
|
const WGPUColor color{0.f, 0.f, 0.f, data.dstAlpha / 255.f};
|
||||||
|
wgpuRenderPassEncoderSetBlendConstant(pass, &color);
|
||||||
|
}
|
||||||
|
wgpuRenderPassEncoderDrawIndexed(pass, data.indexCount, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
} // namespace aurora::gfx::stream
|
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common.hpp"
|
||||||
|
#include "../gx.hpp"
|
||||||
|
|
||||||
|
namespace aurora::gfx::stream {
|
||||||
|
struct DrawData {
|
||||||
|
PipelineRef pipeline;
|
||||||
|
Range vertRange;
|
||||||
|
Range uniformRange;
|
||||||
|
Range indexRange;
|
||||||
|
uint32_t indexCount;
|
||||||
|
gx::GXBindGroups bindGroups;
|
||||||
|
u32 dstAlpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipelineConfig : public gx::PipelineConfig {};
|
||||||
|
|
||||||
|
struct State {};
|
||||||
|
|
||||||
|
State construct_state();
|
||||||
|
WGPURenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config);
|
||||||
|
void render(const State& state, const DrawData& data, const WGPURenderPassEncoder& pass);
|
||||||
|
} // namespace aurora::gfx::stream
|
|
@ -0,0 +1,210 @@
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#include "../webgpu/gpu.hpp"
|
||||||
|
#include "../internal.hpp"
|
||||||
|
#include "texture.hpp"
|
||||||
|
#include "texture_convert.hpp"
|
||||||
|
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
|
namespace aurora::gfx {
|
||||||
|
static Module Log("aurora::gfx");
|
||||||
|
|
||||||
|
using webgpu::g_device;
|
||||||
|
using webgpu::g_queue;
|
||||||
|
|
||||||
|
struct TextureFormatInfo {
|
||||||
|
uint8_t blockWidth;
|
||||||
|
uint8_t blockHeight;
|
||||||
|
uint8_t blockSize;
|
||||||
|
bool compressed;
|
||||||
|
};
|
||||||
|
static TextureFormatInfo format_info(WGPUTextureFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case WGPUTextureFormat_R8Unorm:
|
||||||
|
return {1, 1, 1, false};
|
||||||
|
case WGPUTextureFormat_R16Sint:
|
||||||
|
return {1, 1, 2, false};
|
||||||
|
case WGPUTextureFormat_RGBA8Unorm:
|
||||||
|
case WGPUTextureFormat_R32Float:
|
||||||
|
return {1, 1, 4, false};
|
||||||
|
case WGPUTextureFormat_BC1RGBAUnorm:
|
||||||
|
return {4, 4, 8, true};
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("format_info: unimplemented format {}"), magic_enum::enum_name(format));
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static WGPUExtent3D physical_size(WGPUExtent3D size, TextureFormatInfo info) {
|
||||||
|
const uint32_t width = ((size.width + info.blockWidth - 1) / info.blockWidth) * info.blockWidth;
|
||||||
|
const uint32_t height = ((size.height + info.blockHeight - 1) / info.blockHeight) * info.blockHeight;
|
||||||
|
return {width, height, size.depthOrArrayLayers};
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format,
|
||||||
|
ArrayRef<uint8_t> data, const char* label) noexcept {
|
||||||
|
auto handle = new_dynamic_texture_2d(width, height, mips, format, label);
|
||||||
|
const auto& ref = *handle;
|
||||||
|
|
||||||
|
ByteBuffer buffer;
|
||||||
|
if (ref.gxFormat != InvalidTextureFormat) {
|
||||||
|
buffer = convert_texture(ref.gxFormat, ref.size.width, ref.size.height, ref.mipCount, data);
|
||||||
|
if (!buffer.empty()) {
|
||||||
|
data = {buffer.data(), buffer.size()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t offset = 0;
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const WGPUExtent3D mipSize{
|
||||||
|
.width = std::max(ref.size.width >> mip, 1u),
|
||||||
|
.height = std::max(ref.size.height >> mip, 1u),
|
||||||
|
.depthOrArrayLayers = ref.size.depthOrArrayLayers,
|
||||||
|
};
|
||||||
|
const auto info = format_info(ref.format);
|
||||||
|
const auto physicalSize = physical_size(mipSize, info);
|
||||||
|
const uint32_t widthBlocks = physicalSize.width / info.blockWidth;
|
||||||
|
const uint32_t heightBlocks = physicalSize.height / info.blockHeight;
|
||||||
|
const uint32_t bytesPerRow = widthBlocks * info.blockSize;
|
||||||
|
const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers;
|
||||||
|
if (offset + dataSize > data.size()) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("new_static_texture_2d[{}]: expected at least {} bytes, got {}"), label,
|
||||||
|
offset + dataSize, data.size());
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
const WGPUImageCopyTexture dstView{
|
||||||
|
.texture = ref.texture,
|
||||||
|
.mipLevel = mip,
|
||||||
|
};
|
||||||
|
// const auto range = push_texture_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks);
|
||||||
|
const WGPUTextureDataLayout dataLayout{
|
||||||
|
// .offset = range.offset,
|
||||||
|
.bytesPerRow = bytesPerRow,
|
||||||
|
.rowsPerImage = heightBlocks,
|
||||||
|
};
|
||||||
|
// TODO
|
||||||
|
// g_textureUploads.emplace_back(dataLayout, std::move(dstView), physicalSize);
|
||||||
|
wgpuQueueWriteTexture(g_queue, &dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize);
|
||||||
|
offset += dataSize;
|
||||||
|
}
|
||||||
|
if (data.size() != UINT32_MAX && offset < data.size()) {
|
||||||
|
Log.report(LOG_WARNING, FMT_STRING("new_static_texture_2d[{}]: texture used {} bytes, but given {} bytes"), label,
|
||||||
|
offset, data.size());
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format,
|
||||||
|
const char* label) noexcept {
|
||||||
|
const auto wgpuFormat = to_wgpu(format);
|
||||||
|
const WGPUExtent3D size{
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.depthOrArrayLayers = 1,
|
||||||
|
};
|
||||||
|
const WGPUTextureDescriptor textureDescriptor{
|
||||||
|
.label = label,
|
||||||
|
.usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst,
|
||||||
|
.dimension = WGPUTextureDimension_2D,
|
||||||
|
.size = size,
|
||||||
|
.format = wgpuFormat,
|
||||||
|
.mipLevelCount = mips,
|
||||||
|
.sampleCount = 1,
|
||||||
|
};
|
||||||
|
const auto viewLabel = fmt::format(FMT_STRING("{} view"), label);
|
||||||
|
const WGPUTextureViewDescriptor textureViewDescriptor{
|
||||||
|
.label = viewLabel.c_str(),
|
||||||
|
.format = wgpuFormat,
|
||||||
|
.dimension = WGPUTextureViewDimension_2D,
|
||||||
|
.mipLevelCount = mips,
|
||||||
|
.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED,
|
||||||
|
};
|
||||||
|
auto texture = wgpuDeviceCreateTexture(g_device, &textureDescriptor);
|
||||||
|
auto textureView = wgpuTextureCreateView(texture, &textureViewDescriptor);
|
||||||
|
return std::make_shared<TextureRef>(texture, textureView, size, wgpuFormat, mips, format, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureHandle new_render_texture(uint32_t width, uint32_t height, u32 fmt, const char* label) noexcept {
|
||||||
|
const auto wgpuFormat = webgpu::g_graphicsConfig.colorFormat;
|
||||||
|
const WGPUExtent3D size{
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.depthOrArrayLayers = 1,
|
||||||
|
};
|
||||||
|
const WGPUTextureDescriptor textureDescriptor{
|
||||||
|
.label = label,
|
||||||
|
.usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst,
|
||||||
|
.dimension = WGPUTextureDimension_2D,
|
||||||
|
.size = size,
|
||||||
|
.format = wgpuFormat,
|
||||||
|
.mipLevelCount = 1,
|
||||||
|
.sampleCount = 1,
|
||||||
|
};
|
||||||
|
const auto viewLabel = fmt::format(FMT_STRING("{} view"), label);
|
||||||
|
const WGPUTextureViewDescriptor textureViewDescriptor{
|
||||||
|
.label = viewLabel.c_str(),
|
||||||
|
.format = wgpuFormat,
|
||||||
|
.dimension = WGPUTextureViewDimension_2D,
|
||||||
|
.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED,
|
||||||
|
.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED,
|
||||||
|
};
|
||||||
|
auto texture = wgpuDeviceCreateTexture(g_device, &textureDescriptor);
|
||||||
|
auto textureView = wgpuTextureCreateView(texture, &textureViewDescriptor);
|
||||||
|
return std::make_shared<TextureRef>(texture, textureView, size, wgpuFormat, 1, fmt, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_texture(const TextureRef& ref, ArrayRef<uint8_t> data) noexcept {
|
||||||
|
ByteBuffer buffer;
|
||||||
|
if (ref.gxFormat != InvalidTextureFormat) {
|
||||||
|
buffer = convert_texture(ref.gxFormat, ref.size.width, ref.size.height, ref.mipCount, data);
|
||||||
|
if (!buffer.empty()) {
|
||||||
|
data = {buffer.data(), buffer.size()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t offset = 0;
|
||||||
|
for (uint32_t mip = 0; mip < ref.mipCount; ++mip) {
|
||||||
|
const WGPUExtent3D mipSize{
|
||||||
|
.width = std::max(ref.size.width >> mip, 1u),
|
||||||
|
.height = std::max(ref.size.height >> mip, 1u),
|
||||||
|
.depthOrArrayLayers = ref.size.depthOrArrayLayers,
|
||||||
|
};
|
||||||
|
const auto info = format_info(ref.format);
|
||||||
|
const auto physicalSize = physical_size(mipSize, info);
|
||||||
|
const uint32_t widthBlocks = physicalSize.width / info.blockWidth;
|
||||||
|
const uint32_t heightBlocks = physicalSize.height / info.blockHeight;
|
||||||
|
const uint32_t bytesPerRow = widthBlocks * info.blockSize;
|
||||||
|
const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers;
|
||||||
|
if (offset + dataSize > data.size()) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("write_texture: expected at least {} bytes, got {}"), offset + dataSize,
|
||||||
|
data.size());
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
// auto dstView = WGPUImageCopyTexture{
|
||||||
|
// .texture = ref.texture,
|
||||||
|
// .mipLevel = mip,
|
||||||
|
// };
|
||||||
|
// const auto range = push_texture_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks);
|
||||||
|
// const auto dataLayout = WGPUTextureDataLayout{
|
||||||
|
// .offset = range.offset,
|
||||||
|
// .bytesPerRow = bytesPerRow,
|
||||||
|
// .rowsPerImage = heightBlocks,
|
||||||
|
// };
|
||||||
|
// g_textureUploads.emplace_back(dataLayout, std::move(dstView), physicalSize);
|
||||||
|
const WGPUImageCopyTexture dstView{
|
||||||
|
.texture = ref.texture,
|
||||||
|
.mipLevel = mip,
|
||||||
|
};
|
||||||
|
const WGPUTextureDataLayout dataLayout{
|
||||||
|
.bytesPerRow = bytesPerRow,
|
||||||
|
.rowsPerImage = heightBlocks,
|
||||||
|
};
|
||||||
|
wgpuQueueWriteTexture(g_queue, &dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize);
|
||||||
|
offset += dataSize;
|
||||||
|
}
|
||||||
|
if (data.size() != UINT32_MAX && offset < data.size()) {
|
||||||
|
Log.report(LOG_WARNING, FMT_STRING("write_texture: texture used {} bytes, but given {} bytes"), offset,
|
||||||
|
data.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace aurora::gfx
|
|
@ -0,0 +1,90 @@
|
||||||
|
#pragma once
|
||||||
|
#include <dolphin/gx.h>
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
namespace aurora::gfx {
|
||||||
|
struct TextureUpload {
|
||||||
|
WGPUTextureDataLayout layout;
|
||||||
|
WGPUImageCopyTexture tex;
|
||||||
|
WGPUExtent3D size;
|
||||||
|
|
||||||
|
TextureUpload(WGPUTextureDataLayout layout, WGPUImageCopyTexture tex, WGPUExtent3D size) noexcept
|
||||||
|
: layout(layout), tex(tex), size(size) {}
|
||||||
|
};
|
||||||
|
extern std::vector<TextureUpload> g_textureUploads;
|
||||||
|
|
||||||
|
constexpr u32 InvalidTextureFormat = -1;
|
||||||
|
struct TextureRef {
|
||||||
|
WGPUTexture texture;
|
||||||
|
WGPUTextureView view;
|
||||||
|
WGPUExtent3D size;
|
||||||
|
WGPUTextureFormat format;
|
||||||
|
uint32_t mipCount;
|
||||||
|
u32 gxFormat;
|
||||||
|
bool isRenderTexture; // :shrug: for now
|
||||||
|
|
||||||
|
TextureRef(WGPUTexture texture, WGPUTextureView view, WGPUExtent3D size, WGPUTextureFormat format, uint32_t mipCount,
|
||||||
|
u32 gxFormat, bool isRenderTexture)
|
||||||
|
: texture(texture)
|
||||||
|
, view(view)
|
||||||
|
, size(size)
|
||||||
|
, format(format)
|
||||||
|
, mipCount(mipCount)
|
||||||
|
, gxFormat(gxFormat)
|
||||||
|
, isRenderTexture(isRenderTexture) {}
|
||||||
|
|
||||||
|
~TextureRef() {
|
||||||
|
wgpuTextureViewRelease(view);
|
||||||
|
wgpuTextureDestroy(texture);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using TextureHandle = std::shared_ptr<TextureRef>;
|
||||||
|
|
||||||
|
TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format,
|
||||||
|
ArrayRef<uint8_t> data, const char* label) noexcept;
|
||||||
|
TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format,
|
||||||
|
const char* label) noexcept;
|
||||||
|
TextureHandle new_render_texture(uint32_t width, uint32_t height, u32 fmt, const char* label) noexcept;
|
||||||
|
void write_texture(const TextureRef& ref, ArrayRef<uint8_t> data) noexcept;
|
||||||
|
}; // namespace aurora::gfx
|
||||||
|
|
||||||
|
struct GXTexObj_ {
|
||||||
|
aurora::gfx::TextureHandle ref;
|
||||||
|
const void* data;
|
||||||
|
u32 dataSize;
|
||||||
|
u16 width;
|
||||||
|
u16 height;
|
||||||
|
u32 fmt;
|
||||||
|
GXTexWrapMode wrapS;
|
||||||
|
GXTexWrapMode wrapT;
|
||||||
|
GXBool hasMips;
|
||||||
|
GXTexFilter minFilter;
|
||||||
|
GXTexFilter magFilter;
|
||||||
|
float minLod;
|
||||||
|
float maxLod;
|
||||||
|
float lodBias;
|
||||||
|
GXBool biasClamp;
|
||||||
|
GXBool doEdgeLod;
|
||||||
|
GXAnisotropy maxAniso;
|
||||||
|
GXTlut tlut;
|
||||||
|
bool dataInvalidated;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(GXTexObj_) <= sizeof(GXTexObj), "GXTexObj too small!");
|
||||||
|
struct GXTlutObj_ {
|
||||||
|
aurora::gfx::TextureHandle ref;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(GXTlutObj_) <= sizeof(GXTlutObj), "GXTlutObj too small!");
|
||||||
|
|
||||||
|
namespace aurora::gfx {
|
||||||
|
struct TextureBind {
|
||||||
|
GXTexObj_ texObj;
|
||||||
|
|
||||||
|
TextureBind() noexcept = default;
|
||||||
|
TextureBind(GXTexObj_ obj) noexcept : texObj(std::move(obj)) {}
|
||||||
|
void reset() noexcept { texObj.ref.reset(); };
|
||||||
|
[[nodiscard]] WGPUSamplerDescriptor get_descriptor() const noexcept;
|
||||||
|
operator bool() const noexcept { return texObj.ref.operator bool(); }
|
||||||
|
};
|
||||||
|
} // namespace aurora::gfx
|
|
@ -0,0 +1,607 @@
|
||||||
|
#include "texture_convert.hpp"
|
||||||
|
|
||||||
|
#include "../internal.hpp"
|
||||||
|
|
||||||
|
namespace aurora::gfx {
|
||||||
|
static Module Log("aurora::gfx");
|
||||||
|
|
||||||
|
struct RGBA8 {
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
uint8_t a;
|
||||||
|
};
|
||||||
|
struct DXT1Block {
|
||||||
|
uint16_t color1;
|
||||||
|
uint16_t color2;
|
||||||
|
std::array<uint8_t, 4> lines;
|
||||||
|
};
|
||||||
|
|
||||||
|
// http://www.mindcontrol.org/~hplus/graphics/expand-bits.html
|
||||||
|
template <uint8_t v>
|
||||||
|
constexpr uint8_t ExpandTo8(uint8_t n) {
|
||||||
|
if constexpr (v == 3) {
|
||||||
|
return (n << (8 - 3)) | (n << (8 - 6)) | (n >> (9 - 8));
|
||||||
|
} else {
|
||||||
|
return (n << (8 - v)) | (n >> ((v * 2) - 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint8_t S3TCBlend(uint32_t a, uint32_t b) {
|
||||||
|
return static_cast<uint8_t>((((a << 1) + a) + ((b << 2) + b)) >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint8_t HalfBlend(uint8_t a, uint8_t b) {
|
||||||
|
return static_cast<uint8_t>((static_cast<uint32_t>(a) + static_cast<uint32_t>(b)) >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ComputeMippedTexelCount(uint32_t w, uint32_t h, uint32_t mips) {
|
||||||
|
size_t ret = w * h;
|
||||||
|
for (uint32_t i = mips; i > 1; --i) {
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
ret += w * h;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ComputeMippedBlockCountDXT1(uint32_t w, uint32_t h, uint32_t mips) {
|
||||||
|
w /= 4;
|
||||||
|
h /= 4;
|
||||||
|
size_t ret = w * h;
|
||||||
|
for (uint32_t i = mips; i > 1; --i) {
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
ret += w * h;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T bswap16(T val) noexcept {
|
||||||
|
#if __GNUC__
|
||||||
|
return __builtin_bswap16(val);
|
||||||
|
#elif _WIN32
|
||||||
|
return _byteswap_ushort(val);
|
||||||
|
#else
|
||||||
|
return (val = (val << 8) | ((val >> 8) & 0xFF));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static ByteBuffer BuildI4FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
ByteBuffer buf{texelCount};
|
||||||
|
|
||||||
|
uint32_t w = width;
|
||||||
|
uint32_t h = height;
|
||||||
|
uint8_t* targetMip = buf.data();
|
||||||
|
const uint8_t* in = data.data();
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 7) / 8;
|
||||||
|
const uint32_t bheight = (h + 7) / 8;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 8;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 8;
|
||||||
|
for (uint32_t y = 0; y < std::min(h, 8u); ++y) {
|
||||||
|
uint8_t* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
for (uint32_t x = 0; x < std::min(w, 8u); ++x) {
|
||||||
|
target[x] = ExpandTo8<4>(in[x / 2] >> ((x & 1) ? 0 : 4) & 0xf);
|
||||||
|
}
|
||||||
|
in += std::min<size_t>(w / 4, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ByteBuffer BuildI8FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
ByteBuffer buf{texelCount};
|
||||||
|
|
||||||
|
uint32_t w = width;
|
||||||
|
uint32_t h = height;
|
||||||
|
auto* targetMip = buf.data();
|
||||||
|
const uint8_t* in = data.data();
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 7) / 8;
|
||||||
|
const uint32_t bheight = (h + 3) / 4;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 4;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 8;
|
||||||
|
for (uint32_t y = 0; y < 4; ++y) {
|
||||||
|
uint8_t* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
const auto n = std::min(w, 8u);
|
||||||
|
for (size_t x = 0; x < n; ++x) {
|
||||||
|
target[x] = in[x];
|
||||||
|
}
|
||||||
|
in += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer BuildIA4FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
ByteBuffer buf{sizeof(RGBA8) * texelCount};
|
||||||
|
|
||||||
|
uint32_t w = width;
|
||||||
|
uint32_t h = height;
|
||||||
|
RGBA8* targetMip = reinterpret_cast<RGBA8*>(buf.data());
|
||||||
|
const uint8_t* in = data.data();
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 7) / 8;
|
||||||
|
const uint32_t bheight = (h + 3) / 4;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 4;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 8;
|
||||||
|
for (uint32_t y = 0; y < 4; ++y) {
|
||||||
|
RGBA8* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
const auto n = std::min(w, 8u);
|
||||||
|
for (size_t x = 0; x < n; ++x) {
|
||||||
|
const uint8_t intensity = ExpandTo8<4>(in[x] & 0xf);
|
||||||
|
target[x].r = intensity;
|
||||||
|
target[x].g = intensity;
|
||||||
|
target[x].b = intensity;
|
||||||
|
target[x].a = ExpandTo8<4>(in[x] >> 4);
|
||||||
|
}
|
||||||
|
in += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer BuildIA8FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
ByteBuffer buf{sizeof(RGBA8) * texelCount};
|
||||||
|
|
||||||
|
uint32_t w = width;
|
||||||
|
uint32_t h = height;
|
||||||
|
auto* targetMip = reinterpret_cast<RGBA8*>(buf.data());
|
||||||
|
const auto* in = reinterpret_cast<const uint16_t*>(data.data());
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 3) / 4;
|
||||||
|
const uint32_t bheight = (h + 3) / 4;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 4;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 4;
|
||||||
|
for (uint32_t y = 0; y < 4; ++y) {
|
||||||
|
RGBA8* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
for (size_t x = 0; x < 4; ++x) {
|
||||||
|
const auto texel = bswap16(in[x]);
|
||||||
|
const uint8_t intensity = texel >> 8;
|
||||||
|
target[x].r = intensity;
|
||||||
|
target[x].g = intensity;
|
||||||
|
target[x].b = intensity;
|
||||||
|
target[x].a = texel & 0xff;
|
||||||
|
}
|
||||||
|
in += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer BuildC4FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
ByteBuffer buf{texelCount * 2};
|
||||||
|
|
||||||
|
uint32_t w = width;
|
||||||
|
uint32_t h = height;
|
||||||
|
uint16_t* targetMip = reinterpret_cast<uint16_t*>(buf.data());
|
||||||
|
const uint8_t* in = data.data();
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 7) / 8;
|
||||||
|
const uint32_t bheight = (h + 7) / 8;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 8;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 8;
|
||||||
|
for (uint32_t y = 0; y < std::min(8u, h); ++y) {
|
||||||
|
uint16_t* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
const auto n = std::min(w, 8u);
|
||||||
|
for (size_t x = 0; x < n; ++x) {
|
||||||
|
target[x] = in[x / 2] >> ((x & 1) ? 0 : 4) & 0xf;
|
||||||
|
}
|
||||||
|
in += n / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer BuildC8FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
ByteBuffer buf{texelCount * 2};
|
||||||
|
|
||||||
|
uint32_t w = width;
|
||||||
|
uint32_t h = height;
|
||||||
|
uint16_t* targetMip = reinterpret_cast<uint16_t*>(buf.data());
|
||||||
|
const uint8_t* in = data.data();
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 7) / 8;
|
||||||
|
const uint32_t bheight = (h + 3) / 4;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 4;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 8;
|
||||||
|
for (uint32_t y = 0; y < 4; ++y) {
|
||||||
|
uint16_t* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
const auto n = std::min(w, 8u);
|
||||||
|
for (size_t x = 0; x < n; ++x) {
|
||||||
|
target[x] = in[x];
|
||||||
|
}
|
||||||
|
in += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer BuildRGB565FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
ByteBuffer buf{sizeof(RGBA8) * texelCount};
|
||||||
|
|
||||||
|
uint32_t w = width;
|
||||||
|
uint32_t h = height;
|
||||||
|
auto* targetMip = reinterpret_cast<RGBA8*>(buf.data());
|
||||||
|
const auto* in = reinterpret_cast<const uint16_t*>(data.data());
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 3) / 4;
|
||||||
|
const uint32_t bheight = (h + 3) / 4;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 4;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 4;
|
||||||
|
for (uint32_t y = 0; y < std::min(4u, h); ++y) {
|
||||||
|
RGBA8* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
for (size_t x = 0; x < std::min(4u, w); ++x) {
|
||||||
|
const auto texel = bswap16(in[x]);
|
||||||
|
target[x].r = ExpandTo8<5>(texel >> 11 & 0x1f);
|
||||||
|
target[x].g = ExpandTo8<6>(texel >> 5 & 0x3f);
|
||||||
|
target[x].b = ExpandTo8<5>(texel & 0x1f);
|
||||||
|
target[x].a = 0xff;
|
||||||
|
}
|
||||||
|
in += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer BuildRGB5A3FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
ByteBuffer buf{sizeof(RGBA8) * texelCount};
|
||||||
|
|
||||||
|
uint32_t w = width;
|
||||||
|
uint32_t h = height;
|
||||||
|
auto* targetMip = reinterpret_cast<RGBA8*>(buf.data());
|
||||||
|
const auto* in = reinterpret_cast<const uint16_t*>(data.data());
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 3) / 4;
|
||||||
|
const uint32_t bheight = (h + 3) / 4;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 4;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 4;
|
||||||
|
for (uint32_t y = 0; y < std::min(4u, h); ++y) {
|
||||||
|
RGBA8* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
for (size_t x = 0; x < std::min(4u, w); ++x) {
|
||||||
|
const auto texel = bswap16(in[x]);
|
||||||
|
if ((texel & 0x8000) != 0) {
|
||||||
|
target[x].r = ExpandTo8<5>(texel >> 10 & 0x1f);
|
||||||
|
target[x].g = ExpandTo8<5>(texel >> 5 & 0x1f);
|
||||||
|
target[x].b = ExpandTo8<5>(texel & 0x1f);
|
||||||
|
target[x].a = 0xff;
|
||||||
|
} else {
|
||||||
|
target[x].r = ExpandTo8<4>(texel >> 8 & 0xf);
|
||||||
|
target[x].g = ExpandTo8<4>(texel >> 4 & 0xf);
|
||||||
|
target[x].b = ExpandTo8<4>(texel & 0xf);
|
||||||
|
target[x].a = ExpandTo8<3>(texel >> 12 & 0x7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer BuildRGBA8FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
ByteBuffer buf{sizeof(RGBA8) * texelCount};
|
||||||
|
|
||||||
|
uint32_t w = width;
|
||||||
|
uint32_t h = height;
|
||||||
|
auto* targetMip = reinterpret_cast<RGBA8*>(buf.data());
|
||||||
|
const uint8_t* in = data.data();
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 3) / 4;
|
||||||
|
const uint32_t bheight = (h + 3) / 4;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 4;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 4;
|
||||||
|
for (uint32_t c = 0; c < 2; ++c) {
|
||||||
|
for (uint32_t y = 0; y < 4; ++y) {
|
||||||
|
RGBA8* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
for (size_t x = 0; x < 4; ++x) {
|
||||||
|
if (c != 0) {
|
||||||
|
target[x].g = in[x * 2];
|
||||||
|
target[x].b = in[x * 2 + 1];
|
||||||
|
} else {
|
||||||
|
target[x].a = in[x * 2];
|
||||||
|
target[x].r = in[x * 2 + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer BuildDXT1FromGCN(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t blockCount = ComputeMippedBlockCountDXT1(width, height, mips);
|
||||||
|
ByteBuffer buf{sizeof(DXT1Block) * blockCount};
|
||||||
|
|
||||||
|
uint32_t w = width / 4;
|
||||||
|
uint32_t h = height / 4;
|
||||||
|
auto* targetMip = reinterpret_cast<DXT1Block*>(buf.data());
|
||||||
|
const auto* in = reinterpret_cast<const DXT1Block*>(data.data());
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
const uint32_t bwidth = (w + 1) / 2;
|
||||||
|
const uint32_t bheight = (h + 1) / 2;
|
||||||
|
for (uint32_t by = 0; by < bheight; ++by) {
|
||||||
|
const uint32_t baseY = by * 2;
|
||||||
|
for (uint32_t bx = 0; bx < bwidth; ++bx) {
|
||||||
|
const uint32_t baseX = bx * 2;
|
||||||
|
for (uint32_t y = 0; y < 2; ++y) {
|
||||||
|
DXT1Block* target = targetMip + (baseY + y) * w + baseX;
|
||||||
|
for (size_t x = 0; x < 2; ++x) {
|
||||||
|
target[x].color1 = bswap16(in[x].color1);
|
||||||
|
target[x].color2 = bswap16(in[x].color2);
|
||||||
|
for (size_t i = 0; i < 4; ++i) {
|
||||||
|
std::array<uint8_t, 4> ind;
|
||||||
|
const uint8_t packed = in[x].lines[i];
|
||||||
|
ind[3] = packed & 0x3;
|
||||||
|
ind[2] = (packed >> 2) & 0x3;
|
||||||
|
ind[1] = (packed >> 4) & 0x3;
|
||||||
|
ind[0] = (packed >> 6) & 0x3;
|
||||||
|
target[x].lines[i] = ind[0] | (ind[1] << 2) | (ind[2] << 4) | (ind[3] << 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetMip += w * h;
|
||||||
|
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer BuildRGBA8FromCMPR(uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
const size_t texelCount = ComputeMippedTexelCount(width, height, mips);
|
||||||
|
const size_t blockCount = ComputeMippedBlockCountDXT1(width, height, mips);
|
||||||
|
ByteBuffer buf{sizeof(RGBA8) * texelCount};
|
||||||
|
|
||||||
|
uint32_t h = height;
|
||||||
|
uint32_t w = width;
|
||||||
|
uint8_t* dst = buf.data();
|
||||||
|
const uint8_t* src = data.data();
|
||||||
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
||||||
|
for (uint32_t yy = 0; yy < h; yy += 8) {
|
||||||
|
for (uint32_t xx = 0; xx < w; xx += 8) {
|
||||||
|
for (uint32_t yb = 0; yb < 8; yb += 4) {
|
||||||
|
for (uint32_t xb = 0; xb < 8; xb += 4) {
|
||||||
|
// CMPR difference: Big-endian color1/2
|
||||||
|
const uint16_t color1 = bswap16(*reinterpret_cast<const uint16_t*>(src));
|
||||||
|
const uint16_t color2 = bswap16(*reinterpret_cast<const uint16_t*>(src + 2));
|
||||||
|
src += 4;
|
||||||
|
|
||||||
|
// Fill in first two colors in color table.
|
||||||
|
std::array<uint8_t, 16> color_table{};
|
||||||
|
|
||||||
|
color_table[0] = ExpandTo8<5>(static_cast<uint8_t>((color1 >> 11) & 0x1F));
|
||||||
|
color_table[1] = ExpandTo8<6>(static_cast<uint8_t>((color1 >> 5) & 0x3F));
|
||||||
|
color_table[2] = ExpandTo8<5>(static_cast<uint8_t>(color1 & 0x1F));
|
||||||
|
color_table[3] = 0xFF;
|
||||||
|
|
||||||
|
color_table[4] = ExpandTo8<5>(static_cast<uint8_t>((color2 >> 11) & 0x1F));
|
||||||
|
color_table[5] = ExpandTo8<6>(static_cast<uint8_t>((color2 >> 5) & 0x3F));
|
||||||
|
color_table[6] = ExpandTo8<5>(static_cast<uint8_t>(color2 & 0x1F));
|
||||||
|
color_table[7] = 0xFF;
|
||||||
|
if (color1 > color2) {
|
||||||
|
// Predict gradients.
|
||||||
|
color_table[8] = S3TCBlend(color_table[4], color_table[0]);
|
||||||
|
color_table[9] = S3TCBlend(color_table[5], color_table[1]);
|
||||||
|
color_table[10] = S3TCBlend(color_table[6], color_table[2]);
|
||||||
|
color_table[11] = 0xFF;
|
||||||
|
|
||||||
|
color_table[12] = S3TCBlend(color_table[0], color_table[4]);
|
||||||
|
color_table[13] = S3TCBlend(color_table[1], color_table[5]);
|
||||||
|
color_table[14] = S3TCBlend(color_table[2], color_table[6]);
|
||||||
|
color_table[15] = 0xFF;
|
||||||
|
} else {
|
||||||
|
color_table[8] = HalfBlend(color_table[0], color_table[4]);
|
||||||
|
color_table[9] = HalfBlend(color_table[1], color_table[5]);
|
||||||
|
color_table[10] = HalfBlend(color_table[2], color_table[6]);
|
||||||
|
color_table[11] = 0xFF;
|
||||||
|
|
||||||
|
// CMPR difference: GX fills with an alpha 0 midway point here.
|
||||||
|
color_table[12] = color_table[8];
|
||||||
|
color_table[13] = color_table[9];
|
||||||
|
color_table[14] = color_table[10];
|
||||||
|
color_table[15] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t y = 0; y < 4; ++y) {
|
||||||
|
uint8_t bits = src[y];
|
||||||
|
for (uint32_t x = 0; x < 4; ++x) {
|
||||||
|
if (xx + xb + x >= w || yy + yb + y >= h) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint8_t* dstOffs = dst + ((yy + yb + y) * w + (xx + xb + x)) * 4;
|
||||||
|
const uint8_t* colorTableOffs = &color_table[static_cast<size_t>((bits >> 6) & 3) * 4];
|
||||||
|
memcpy(dstOffs, colorTableOffs, 4);
|
||||||
|
bits <<= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst += w * h * 4;
|
||||||
|
if (w > 1) {
|
||||||
|
w /= 2;
|
||||||
|
}
|
||||||
|
if (h > 1) {
|
||||||
|
h /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer convert_texture(u32 format, uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||||
|
switch (format) {
|
||||||
|
default:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("convert_texture: unknown format supplied {}"), format);
|
||||||
|
unreachable();
|
||||||
|
case GX_TF_R8_PC:
|
||||||
|
case GX_TF_RGBA8_PC:
|
||||||
|
return {}; // No conversion
|
||||||
|
case GX_TF_I4:
|
||||||
|
return BuildI4FromGCN(width, height, mips, data);
|
||||||
|
case GX_TF_I8:
|
||||||
|
return BuildI8FromGCN(width, height, mips, data);
|
||||||
|
case GX_TF_IA4:
|
||||||
|
return BuildIA4FromGCN(width, height, mips, data);
|
||||||
|
case GX_TF_IA8:
|
||||||
|
return BuildIA8FromGCN(width, height, mips, data);
|
||||||
|
case GX_TF_C4:
|
||||||
|
return BuildC4FromGCN(width, height, mips, data);
|
||||||
|
case GX_TF_C8:
|
||||||
|
return BuildC8FromGCN(width, height, mips, data);
|
||||||
|
case GX_TF_C14X2:
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("convert_texture: C14X2 unimplemented"));
|
||||||
|
unreachable();
|
||||||
|
case GX_TF_RGB565:
|
||||||
|
return BuildRGB565FromGCN(width, height, mips, data);
|
||||||
|
case GX_TF_RGB5A3:
|
||||||
|
return BuildRGB5A3FromGCN(width, height, mips, data);
|
||||||
|
case GX_TF_RGBA8:
|
||||||
|
return BuildRGBA8FromGCN(width, height, mips, data);
|
||||||
|
case GX_TF_CMPR:
|
||||||
|
if (wgpuDeviceHasFeature(webgpu::g_device, WGPUFeatureName_TextureCompressionBC)) {
|
||||||
|
return BuildDXT1FromGCN(width, height, mips, data);
|
||||||
|
} else {
|
||||||
|
return BuildRGBA8FromCMPR(width, height, mips, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace aurora::gfx
|
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "texture.hpp"
|
||||||
|
#include "../webgpu/gpu.hpp"
|
||||||
|
|
||||||
|
namespace aurora::gfx {
|
||||||
|
static WGPUTextureFormat to_wgpu(u32 format) {
|
||||||
|
switch (format) {
|
||||||
|
case GX_TF_I4:
|
||||||
|
case GX_TF_I8:
|
||||||
|
case GX_TF_R8_PC:
|
||||||
|
return WGPUTextureFormat_R8Unorm;
|
||||||
|
case GX_TF_C4:
|
||||||
|
case GX_TF_C8:
|
||||||
|
case GX_TF_C14X2:
|
||||||
|
return WGPUTextureFormat_R16Sint;
|
||||||
|
case GX_TF_CMPR:
|
||||||
|
if (wgpuDeviceHasFeature(webgpu::g_device, WGPUFeatureName_TextureCompressionBC)) {
|
||||||
|
return WGPUTextureFormat_BC1RGBAUnorm;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
default:
|
||||||
|
return WGPUTextureFormat_RGBA8Unorm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer convert_texture(u32 format, uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data);
|
||||||
|
} // namespace aurora::gfx
|
|
@ -0,0 +1,170 @@
|
||||||
|
#include "imgui.hpp"
|
||||||
|
|
||||||
|
#include "webgpu/gpu.hpp"
|
||||||
|
#include "internal.hpp"
|
||||||
|
#include "window.hpp"
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <webgpu/webgpu.h>
|
||||||
|
|
||||||
|
#include "../imgui/backends/imgui_impl_sdl.cpp" // NOLINT(bugprone-suspicious-include)
|
||||||
|
#include "../imgui/backends/imgui_impl_sdlrenderer.cpp" // NOLINT(bugprone-suspicious-include)
|
||||||
|
#include "../imgui/backends/imgui_impl_wgpu.cpp" // NOLINT(bugprone-suspicious-include)
|
||||||
|
|
||||||
|
namespace aurora::imgui {
|
||||||
|
static float g_scale;
|
||||||
|
static std::string g_imguiSettings{};
|
||||||
|
static std::string g_imguiLog{};
|
||||||
|
static bool g_useSdlRenderer = false;
|
||||||
|
|
||||||
|
static std::vector<SDL_Texture*> g_sdlTextures;
|
||||||
|
static std::vector<WGPUTexture> g_wgpuTextures;
|
||||||
|
|
||||||
|
void create_context() noexcept {
|
||||||
|
IMGUI_CHECKVERSION();
|
||||||
|
ImGui::CreateContext();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
g_imguiSettings = std::string{g_config.configPath} + "/imgui.ini";
|
||||||
|
g_imguiLog = std::string{g_config.configPath} + "/imgui.log";
|
||||||
|
io.IniFilename = g_imguiSettings.c_str();
|
||||||
|
io.LogFilename = g_imguiLog.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize() noexcept {
|
||||||
|
SDL_Renderer* renderer = window::get_sdl_renderer();
|
||||||
|
ImGui_ImplSDL2_Init(window::get_sdl_window(), renderer);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Disable MouseCanUseGlobalState for scaling purposes
|
||||||
|
ImGui_ImplSDL2_GetBackendData()->MouseCanUseGlobalState = false;
|
||||||
|
#endif
|
||||||
|
g_useSdlRenderer = renderer != nullptr;
|
||||||
|
if (g_useSdlRenderer) {
|
||||||
|
ImGui_ImplSDLRenderer_Init(renderer);
|
||||||
|
} else {
|
||||||
|
ImGui_ImplWGPU_Init(webgpu::g_device, 1, webgpu::g_graphicsConfig.colorFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown() noexcept {
|
||||||
|
if (g_useSdlRenderer) {
|
||||||
|
ImGui_ImplSDLRenderer_Shutdown();
|
||||||
|
} else {
|
||||||
|
ImGui_ImplWGPU_Shutdown();
|
||||||
|
}
|
||||||
|
ImGui_ImplSDL2_Shutdown();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
for (const auto& texture : g_sdlTextures) {
|
||||||
|
SDL_DestroyTexture(texture);
|
||||||
|
}
|
||||||
|
g_sdlTextures.clear();
|
||||||
|
for (const auto& texture : g_wgpuTextures) {
|
||||||
|
wgpuTextureDestroy(texture);
|
||||||
|
}
|
||||||
|
g_wgpuTextures.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_event(const SDL_Event& event) noexcept {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (event.type == SDL_MOUSEMOTION) {
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
// Scale up mouse coordinates
|
||||||
|
io.AddMousePosEvent(static_cast<float>(event.motion.x) * g_scale, static_cast<float>(event.motion.y) * g_scale);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void new_frame(const AuroraWindowSize& size) noexcept {
|
||||||
|
if (g_useSdlRenderer) {
|
||||||
|
ImGui_ImplSDLRenderer_NewFrame();
|
||||||
|
} else {
|
||||||
|
if (g_scale != size.scale) {
|
||||||
|
if (g_scale > 0.f) {
|
||||||
|
// TODO wgpu backend bug: doesn't clear bind groups on invalidate
|
||||||
|
g_resources.ImageBindGroups.Clear();
|
||||||
|
ImGui_ImplWGPU_CreateDeviceObjects();
|
||||||
|
}
|
||||||
|
g_scale = size.scale;
|
||||||
|
}
|
||||||
|
ImGui_ImplWGPU_NewFrame();
|
||||||
|
}
|
||||||
|
ImGui_ImplSDL2_NewFrame();
|
||||||
|
|
||||||
|
// Render at full DPI
|
||||||
|
ImGui::GetIO().DisplaySize = {
|
||||||
|
static_cast<float>(size.fb_width),
|
||||||
|
static_cast<float>(size.fb_height),
|
||||||
|
};
|
||||||
|
ImGui::NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(WGPURenderPassEncoder pass) noexcept {
|
||||||
|
ImGui::Render();
|
||||||
|
|
||||||
|
auto* data = ImGui::GetDrawData();
|
||||||
|
// io.DisplayFramebufferScale is informational; we're rendering at full DPI
|
||||||
|
data->FramebufferScale = {1.f, 1.f};
|
||||||
|
if (g_useSdlRenderer) {
|
||||||
|
SDL_Renderer* renderer = ImGui_ImplSDLRenderer_GetBackendData()->SDLRenderer;
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
ImGui_ImplSDLRenderer_RenderDrawData(data);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
} else {
|
||||||
|
ImGui_ImplWGPU_RenderDrawData(data, pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImTextureID add_texture(uint32_t width, uint32_t height, const uint8_t* data) noexcept {
|
||||||
|
if (g_useSdlRenderer) {
|
||||||
|
SDL_Renderer* renderer = ImGui_ImplSDLRenderer_GetBackendData()->SDLRenderer;
|
||||||
|
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height);
|
||||||
|
SDL_UpdateTexture(texture, nullptr, data, width * 4);
|
||||||
|
SDL_SetTextureScaleMode(texture, SDL_ScaleModeLinear);
|
||||||
|
g_sdlTextures.push_back(texture);
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
const auto size = WGPUExtent3D{
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.depthOrArrayLayers = 1,
|
||||||
|
};
|
||||||
|
const auto textureDescriptor = WGPUTextureDescriptor{
|
||||||
|
.label = "imgui texture",
|
||||||
|
.usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst,
|
||||||
|
.dimension = WGPUTextureDimension_2D,
|
||||||
|
.size = size,
|
||||||
|
.format = WGPUTextureFormat_RGBA8Unorm,
|
||||||
|
.mipLevelCount = 1,
|
||||||
|
.sampleCount = 1,
|
||||||
|
};
|
||||||
|
const auto textureViewDescriptor = WGPUTextureViewDescriptor{
|
||||||
|
.label = "imgui texture view",
|
||||||
|
.format = WGPUTextureFormat_RGBA8Unorm,
|
||||||
|
.dimension = WGPUTextureViewDimension_2D,
|
||||||
|
.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED,
|
||||||
|
.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED,
|
||||||
|
};
|
||||||
|
auto texture = wgpuDeviceCreateTexture(webgpu::g_device, &textureDescriptor);
|
||||||
|
auto textureView = wgpuTextureCreateView(texture, &textureViewDescriptor);
|
||||||
|
{
|
||||||
|
const auto dstView = WGPUImageCopyTexture{
|
||||||
|
.texture = texture,
|
||||||
|
};
|
||||||
|
const auto dataLayout = WGPUTextureDataLayout{
|
||||||
|
.bytesPerRow = 4 * width,
|
||||||
|
.rowsPerImage = height,
|
||||||
|
};
|
||||||
|
wgpuQueueWriteTexture(webgpu::g_queue, &dstView, data, width * height * 4, &dataLayout, &size);
|
||||||
|
}
|
||||||
|
g_wgpuTextures.push_back(texture);
|
||||||
|
return textureView;
|
||||||
|
}
|
||||||
|
} // namespace aurora::imgui
|
||||||
|
|
||||||
|
// C bindings
|
||||||
|
extern "C" {
|
||||||
|
ImTextureID aurora_imgui_add_texture(uint32_t width, uint32_t height, const void* rgba8) {
|
||||||
|
return aurora::imgui::add_texture(width, height, static_cast<const uint8_t*>(rgba8));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <aurora/event.h>
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
union SDL_Event;
|
||||||
|
typedef struct WGPURenderPassEncoderImpl* WGPURenderPassEncoder;
|
||||||
|
|
||||||
|
namespace aurora::imgui {
|
||||||
|
void create_context() noexcept;
|
||||||
|
void initialize() noexcept;
|
||||||
|
void shutdown() noexcept;
|
||||||
|
|
||||||
|
void process_event(const SDL_Event& event) noexcept;
|
||||||
|
void new_frame(const AuroraWindowSize& size) noexcept;
|
||||||
|
void render(WGPURenderPassEncoder pass) noexcept;
|
||||||
|
} // namespace aurora::imgui
|
|
@ -0,0 +1,815 @@
|
||||||
|
#include "input.hpp"
|
||||||
|
#include "internal.hpp"
|
||||||
|
#include "pad.hpp"
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
|
#include <SDL_haptic.h>
|
||||||
|
#include <SDL_version.h>
|
||||||
|
|
||||||
|
#include <absl/container/btree_map.h>
|
||||||
|
#include <absl/container/flat_hash_map.h>
|
||||||
|
#include <absl/strings/str_split.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
namespace aurora::input {
|
||||||
|
static Module Log("aurora::input");
|
||||||
|
|
||||||
|
struct GameController {
|
||||||
|
SDL_GameController* m_controller = nullptr;
|
||||||
|
bool m_isGameCube = false;
|
||||||
|
Sint32 m_index = -1;
|
||||||
|
bool m_hasRumble = false;
|
||||||
|
PADDeadZones m_deadZones{
|
||||||
|
.emulateTriggers = true,
|
||||||
|
.useDeadzones = true,
|
||||||
|
.stickDeadZone = 8000,
|
||||||
|
.substickDeadZone = 8000,
|
||||||
|
.leftTriggerActivationZone = 31150,
|
||||||
|
.rightTriggerActivationZone = 31150,
|
||||||
|
};
|
||||||
|
uint16_t m_vid = 0;
|
||||||
|
uint16_t m_pid = 0;
|
||||||
|
std::array<PADButtonMapping, 12> m_mapping{};
|
||||||
|
bool m_mappingLoaded = false;
|
||||||
|
constexpr bool operator==(const GameController& other) const {
|
||||||
|
return m_controller == other.m_controller && m_index == other.m_index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
absl::flat_hash_map<Uint32, GameController> g_GameControllers;
|
||||||
|
|
||||||
|
GameController* get_controller_for_player(uint32_t player) noexcept {
|
||||||
|
for (auto& [which, controller] : g_GameControllers) {
|
||||||
|
if (player_index(which) == player) {
|
||||||
|
return &controller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* If we don't have a controller assigned to this port use the first unassigned controller */
|
||||||
|
if (!g_GameControllers.empty()) {
|
||||||
|
int32_t availIndex = -1;
|
||||||
|
GameController* ct = nullptr;
|
||||||
|
for (auto& controller : g_GameControllers) {
|
||||||
|
if (player_index(controller.first) == -1) {
|
||||||
|
availIndex = controller.first;
|
||||||
|
ct = &controller.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (availIndex != -1) {
|
||||||
|
set_player_index(availIndex, player);
|
||||||
|
return ct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sint32 get_instance_for_player(uint32_t player) noexcept {
|
||||||
|
for (const auto& [which, controller] : g_GameControllers) {
|
||||||
|
if (player_index(which) == player) {
|
||||||
|
return which;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<std::string> remap_controller_layout(std::string_view mapping) {
|
||||||
|
std::string newMapping;
|
||||||
|
newMapping.reserve(mapping.size());
|
||||||
|
absl::btree_map<std::string_view, std::string_view> entries;
|
||||||
|
for (size_t idx = 0; const auto value : absl::StrSplit(mapping, ',')) {
|
||||||
|
if (idx < 2) {
|
||||||
|
if (idx > 0) {
|
||||||
|
newMapping.push_back(',');
|
||||||
|
}
|
||||||
|
newMapping.append(value);
|
||||||
|
} else {
|
||||||
|
const auto split = absl::StrSplit(value, absl::MaxSplits(':', 2));
|
||||||
|
auto iter = split.begin();
|
||||||
|
entries.emplace(*iter++, *iter);
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
if (entries.contains("rightshoulder"sv) && !entries.contains("leftshoulder"sv)) {
|
||||||
|
Log.report(LOG_INFO, FMT_STRING("Remapping GameCube controller layout"));
|
||||||
|
entries.insert_or_assign("back"sv, entries["rightshoulder"sv]);
|
||||||
|
// TODO trigger buttons may differ per platform
|
||||||
|
entries.insert_or_assign("leftshoulder"sv, "b11"sv);
|
||||||
|
entries.insert_or_assign("rightshoulder"sv, "b10"sv);
|
||||||
|
} else if (entries.contains("leftshoulder"sv) && entries.contains("rightshoulder"sv) && entries.contains("back"sv)) {
|
||||||
|
Log.report(LOG_INFO, FMT_STRING("Controller has standard layout"));
|
||||||
|
auto a = entries["a"sv];
|
||||||
|
entries.insert_or_assign("a"sv, entries["b"sv]);
|
||||||
|
entries.insert_or_assign("b"sv, a);
|
||||||
|
auto x = entries["x"sv];
|
||||||
|
entries.insert_or_assign("x"sv, entries["y"sv]);
|
||||||
|
entries.insert_or_assign("y"sv, x);
|
||||||
|
} else {
|
||||||
|
Log.report(LOG_ERROR, FMT_STRING("Controller has unsupported layout: {}"), mapping);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
for (auto [k, v] : entries) {
|
||||||
|
newMapping.push_back(',');
|
||||||
|
newMapping.append(k);
|
||||||
|
newMapping.push_back(':');
|
||||||
|
newMapping.append(v);
|
||||||
|
}
|
||||||
|
return newMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sint32 add_controller(Sint32 which) noexcept {
|
||||||
|
auto* ctrl = SDL_GameControllerOpen(which);
|
||||||
|
if (ctrl != nullptr) {
|
||||||
|
{
|
||||||
|
char* mapping = SDL_GameControllerMapping(ctrl);
|
||||||
|
if (mapping != nullptr) {
|
||||||
|
auto newMapping = remap_controller_layout(mapping);
|
||||||
|
SDL_free(mapping);
|
||||||
|
if (newMapping) {
|
||||||
|
if (SDL_GameControllerAddMapping(newMapping->c_str()) == -1) {
|
||||||
|
Log.report(LOG_ERROR, FMT_STRING("Failed to update controller mapping: {}"), SDL_GetError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.report(LOG_ERROR, FMT_STRING("Failed to retrieve mapping for controller"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GameController controller;
|
||||||
|
controller.m_controller = ctrl;
|
||||||
|
controller.m_index = which;
|
||||||
|
controller.m_vid = SDL_GameControllerGetVendor(ctrl);
|
||||||
|
controller.m_pid = SDL_GameControllerGetProduct(ctrl);
|
||||||
|
if (controller.m_vid == 0x05ac /* USB_VENDOR_APPLE */ && controller.m_pid == 3) {
|
||||||
|
// Ignore Apple TV remote
|
||||||
|
SDL_GameControllerClose(ctrl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
controller.m_isGameCube = controller.m_vid == 0x057E && controller.m_pid == 0x0337;
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||||
|
controller.m_hasRumble = (SDL_GameControllerHasRumble(ctrl) != 0u);
|
||||||
|
#else
|
||||||
|
controller.m_hasRumble = true;
|
||||||
|
#endif
|
||||||
|
Sint32 instance = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ctrl));
|
||||||
|
g_GameControllers[instance] = controller;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_controller(Uint32 instance) noexcept {
|
||||||
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
||||||
|
SDL_GameControllerClose(g_GameControllers[instance].m_controller);
|
||||||
|
g_GameControllers.erase(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_gamecube(Uint32 instance) noexcept {
|
||||||
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
||||||
|
return g_GameControllers[instance].m_isGameCube;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t player_index(Uint32 instance) noexcept {
|
||||||
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
||||||
|
return SDL_GameControllerGetPlayerIndex(g_GameControllers[instance].m_controller);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_player_index(Uint32 instance, Sint32 index) noexcept {
|
||||||
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
||||||
|
SDL_GameControllerSetPlayerIndex(g_GameControllers[instance].m_controller, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string controller_name(Uint32 instance) noexcept {
|
||||||
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
||||||
|
const auto* name = SDL_GameControllerName(g_GameControllers[instance].m_controller);
|
||||||
|
if (name != nullptr) {
|
||||||
|
return {name};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool controller_has_rumble(Uint32 instance) noexcept {
|
||||||
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
||||||
|
return g_GameControllers[instance].m_hasRumble;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void controller_rumble(uint32_t instance, uint16_t low_freq_intensity, uint16_t high_freq_intensity,
|
||||||
|
uint16_t duration_ms) noexcept {
|
||||||
|
|
||||||
|
if (g_GameControllers.find(instance) != g_GameControllers.end()) {
|
||||||
|
SDL_GameControllerRumble(g_GameControllers[instance].m_controller, low_freq_intensity, high_freq_intensity,
|
||||||
|
duration_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t controller_count() noexcept { return g_GameControllers.size(); }
|
||||||
|
|
||||||
|
} // namespace aurora::input
|
||||||
|
|
||||||
|
static const std::array<PADButtonMapping, 12> mDefaultButtons{{
|
||||||
|
{SDL_CONTROLLER_BUTTON_A, PAD_BUTTON_A},
|
||||||
|
{SDL_CONTROLLER_BUTTON_B, PAD_BUTTON_B},
|
||||||
|
{SDL_CONTROLLER_BUTTON_X, PAD_BUTTON_X},
|
||||||
|
{SDL_CONTROLLER_BUTTON_Y, PAD_BUTTON_Y},
|
||||||
|
{SDL_CONTROLLER_BUTTON_START, PAD_BUTTON_START},
|
||||||
|
{SDL_CONTROLLER_BUTTON_BACK, PAD_TRIGGER_Z},
|
||||||
|
{SDL_CONTROLLER_BUTTON_LEFTSHOULDER, PAD_TRIGGER_L},
|
||||||
|
{SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, PAD_TRIGGER_R},
|
||||||
|
{SDL_CONTROLLER_BUTTON_DPAD_UP, PAD_BUTTON_UP},
|
||||||
|
{SDL_CONTROLLER_BUTTON_DPAD_DOWN, PAD_BUTTON_DOWN},
|
||||||
|
{SDL_CONTROLLER_BUTTON_DPAD_LEFT, PAD_BUTTON_LEFT},
|
||||||
|
{SDL_CONTROLLER_BUTTON_DPAD_RIGHT, PAD_BUTTON_RIGHT},
|
||||||
|
}};
|
||||||
|
|
||||||
|
void PADSetSpec(u32 spec) {}
|
||||||
|
BOOL PADInit() { return true; }
|
||||||
|
BOOL PADRecalibrate(u32 mask) { return true; }
|
||||||
|
BOOL PADReset(u32 mask) { return true; }
|
||||||
|
void PADSetAnalogMode(u32 mode) {}
|
||||||
|
|
||||||
|
aurora::input::GameController* __PADGetControllerForIndex(uint32_t idx) {
|
||||||
|
if (idx >= aurora::input::g_GameControllers.size()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tmp = 0;
|
||||||
|
auto iter = aurora::input::g_GameControllers.begin();
|
||||||
|
while (tmp < idx) {
|
||||||
|
++iter;
|
||||||
|
++tmp;
|
||||||
|
}
|
||||||
|
if (iter == aurora::input::g_GameControllers.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t PADCount() { return aurora::input::g_GameControllers.size(); }
|
||||||
|
|
||||||
|
const char* PADGetNameForControllerIndex(uint32_t idx) {
|
||||||
|
auto* ctrl = __PADGetControllerForIndex(idx);
|
||||||
|
if (ctrl == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_GameControllerName(ctrl->m_controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADSetPortForIndex(uint32_t idx, int32_t port) {
|
||||||
|
auto* ctrl = __PADGetControllerForIndex(idx);
|
||||||
|
auto* dest = aurora::input::get_controller_for_player(port);
|
||||||
|
if (ctrl == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dest != nullptr) {
|
||||||
|
SDL_GameControllerSetPlayerIndex(dest->m_controller, -1);
|
||||||
|
}
|
||||||
|
SDL_GameControllerSetPlayerIndex(ctrl->m_controller, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t PADGetIndexForPort(uint32_t port) {
|
||||||
|
auto* ctrl = aurora::input::get_controller_for_player(port);
|
||||||
|
if (ctrl == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int32_t index = 0;
|
||||||
|
for (auto iter = aurora::input::g_GameControllers.begin(); iter != aurora::input::g_GameControllers.end();
|
||||||
|
++iter, ++index) {
|
||||||
|
if (&iter->second == ctrl) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADClearPort(uint32_t port) {
|
||||||
|
auto* ctrl = aurora::input::get_controller_for_player(port);
|
||||||
|
if (ctrl == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SDL_GameControllerSetPlayerIndex(ctrl->m_controller, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __PADLoadMapping(aurora::input::GameController* controller) {
|
||||||
|
int32_t playerIndex = SDL_GameControllerGetPlayerIndex(controller->m_controller);
|
||||||
|
if (playerIndex == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string basePath{aurora::g_config.configPath};
|
||||||
|
if (!controller->m_mappingLoaded) {
|
||||||
|
controller->m_mapping = mDefaultButtons;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller->m_mappingLoaded = true;
|
||||||
|
|
||||||
|
auto path = fmt::format(FMT_STRING("{}/{}_{:04X}_{:04X}.controller"), basePath, PADGetName(playerIndex),
|
||||||
|
controller->m_vid, controller->m_pid);
|
||||||
|
FILE* file = fopen(path.c_str(), "rb");
|
||||||
|
if (file == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t magic = 0;
|
||||||
|
fread(&magic, 1, sizeof(uint32_t), file);
|
||||||
|
if (magic != SBIG('CTRL')) {
|
||||||
|
fmt::print(FMT_STRING("Invalid controller mapping magic!\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t version = 0;
|
||||||
|
fread(&version, 1, sizeof(uint32_t), file);
|
||||||
|
if (version != 1) {
|
||||||
|
fmt::print(FMT_STRING("Invalid controller mapping version!\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isGameCube = false;
|
||||||
|
fread(&isGameCube, 1, 1, file);
|
||||||
|
fseek(file, (ftell(file) + 31) & ~31, SEEK_SET);
|
||||||
|
uint32_t dataStart = ftell(file);
|
||||||
|
if (isGameCube) {
|
||||||
|
fseek(file, dataStart + ((sizeof(PADDeadZones) + sizeof(PADButtonMapping)) * playerIndex), SEEK_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(&controller->m_deadZones, 1, sizeof(PADDeadZones), file);
|
||||||
|
fread(&controller->m_mapping, 1, sizeof(PADButtonMapping) * controller->m_mapping.size(), file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gBlockPAD = false;
|
||||||
|
uint32_t PADRead(PADStatus* status) {
|
||||||
|
if (gBlockPAD) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t rumbleSupport = 0;
|
||||||
|
for (uint32_t i = 0; i < 4; ++i) {
|
||||||
|
memset(&status[i], 0, sizeof(PADStatus));
|
||||||
|
auto controller = aurora::input::get_controller_for_player(i);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
status[i].err = PAD_ERR_NO_CONTROLLER;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!controller->m_mappingLoaded) {
|
||||||
|
__PADLoadMapping(controller);
|
||||||
|
}
|
||||||
|
status[i].err = PAD_ERR_NONE;
|
||||||
|
std::for_each(controller->m_mapping.begin(), controller->m_mapping.end(),
|
||||||
|
[&controller, &i, &status](const auto& mapping) {
|
||||||
|
if (SDL_GameControllerGetButton(controller->m_controller,
|
||||||
|
static_cast<SDL_GameControllerButton>(mapping.nativeButton))) {
|
||||||
|
status[i].button |= mapping.padButton;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Sint16 x = SDL_GameControllerGetAxis(controller->m_controller, SDL_CONTROLLER_AXIS_LEFTX);
|
||||||
|
Sint16 y = SDL_GameControllerGetAxis(controller->m_controller, SDL_CONTROLLER_AXIS_LEFTY);
|
||||||
|
if (controller->m_deadZones.useDeadzones) {
|
||||||
|
if (std::abs(x) > controller->m_deadZones.stickDeadZone) {
|
||||||
|
x /= 256;
|
||||||
|
} else {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
if (std::abs(y) > controller->m_deadZones.stickDeadZone) {
|
||||||
|
y = (-(y + 1u)) / 256u;
|
||||||
|
} else {
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x /= 256;
|
||||||
|
y = (-(y + 1u)) / 256u;
|
||||||
|
}
|
||||||
|
|
||||||
|
status[i].stickX = static_cast<int8_t>(x);
|
||||||
|
status[i].stickY = static_cast<int8_t>(y);
|
||||||
|
|
||||||
|
x = SDL_GameControllerGetAxis(controller->m_controller, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||||
|
y = SDL_GameControllerGetAxis(controller->m_controller, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||||
|
if (controller->m_deadZones.useDeadzones) {
|
||||||
|
if (std::abs(x) > controller->m_deadZones.substickDeadZone) {
|
||||||
|
x /= 256;
|
||||||
|
} else {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::abs(y) > controller->m_deadZones.substickDeadZone) {
|
||||||
|
y = (-(y + 1u)) / 256u;
|
||||||
|
} else {
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x /= 256;
|
||||||
|
y = (-(y + 1u)) / 256u;
|
||||||
|
}
|
||||||
|
|
||||||
|
status[i].substickX = static_cast<int8_t>(x);
|
||||||
|
status[i].substickY = static_cast<int8_t>(y);
|
||||||
|
|
||||||
|
x = SDL_GameControllerGetAxis(controller->m_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||||
|
y = SDL_GameControllerGetAxis(controller->m_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||||
|
if (/*!controller->m_isGameCube && */ controller->m_deadZones.emulateTriggers) {
|
||||||
|
if (x > controller->m_deadZones.leftTriggerActivationZone) {
|
||||||
|
status[i].button |= PAD_TRIGGER_L;
|
||||||
|
}
|
||||||
|
if (y > controller->m_deadZones.rightTriggerActivationZone) {
|
||||||
|
status[i].button |= PAD_TRIGGER_R;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x /= 128;
|
||||||
|
y /= 128;
|
||||||
|
|
||||||
|
status[i].triggerL = static_cast<int8_t>(x);
|
||||||
|
status[i].triggerR = static_cast<int8_t>(y);
|
||||||
|
|
||||||
|
if (controller->m_hasRumble) {
|
||||||
|
rumbleSupport |= PAD_CHAN0_BIT >> i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rumbleSupport;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADControlAllMotors(const uint32_t* commands) {
|
||||||
|
for (uint32_t i = 0; i < 4; ++i) {
|
||||||
|
auto controller = aurora::input::get_controller_for_player(i);
|
||||||
|
auto instance = aurora::input::get_instance_for_player(i);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller->m_isGameCube) {
|
||||||
|
if (commands[i] == PAD_MOTOR_STOP) {
|
||||||
|
aurora::input::controller_rumble(instance, 0, 1, 0);
|
||||||
|
} else if (commands[i] == PAD_MOTOR_RUMBLE) {
|
||||||
|
aurora::input::controller_rumble(instance, 1, 1, 0);
|
||||||
|
} else if (commands[i] == PAD_MOTOR_STOP_HARD) {
|
||||||
|
aurora::input::controller_rumble(instance, 0, 0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (commands[i] == PAD_MOTOR_STOP) {
|
||||||
|
aurora::input::controller_rumble(instance, 0, 0, 1);
|
||||||
|
} else if (commands[i] == PAD_MOTOR_RUMBLE) {
|
||||||
|
aurora::input::controller_rumble(instance, 32767, 32767, 0);
|
||||||
|
} else if (commands[i] == PAD_MOTOR_STOP_HARD) {
|
||||||
|
aurora::input::controller_rumble(instance, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SIProbe(int32_t chan) {
|
||||||
|
auto* const controller = aurora::input::get_controller_for_player(chan);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
return SI_ERROR_NO_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller->m_isGameCube) {
|
||||||
|
auto level = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(controller->m_controller));
|
||||||
|
if (level == SDL_JOYSTICK_POWER_UNKNOWN) {
|
||||||
|
return SI_GC_WAVEBIRD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SI_GC_CONTROLLER;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PADCLampRegion {
|
||||||
|
uint8_t minTrigger;
|
||||||
|
uint8_t maxTrigger;
|
||||||
|
int8_t minStick;
|
||||||
|
int8_t maxStick;
|
||||||
|
int8_t xyStick;
|
||||||
|
int8_t minSubstick;
|
||||||
|
int8_t maxSubstick;
|
||||||
|
int8_t xySubstick;
|
||||||
|
int8_t radStick;
|
||||||
|
int8_t radSubstick;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr PADCLampRegion ClampRegion{
|
||||||
|
// Triggers
|
||||||
|
30,
|
||||||
|
180,
|
||||||
|
|
||||||
|
// Left stick
|
||||||
|
15,
|
||||||
|
72,
|
||||||
|
40,
|
||||||
|
|
||||||
|
// Right stick
|
||||||
|
15,
|
||||||
|
59,
|
||||||
|
31,
|
||||||
|
|
||||||
|
// Stick radii
|
||||||
|
56,
|
||||||
|
44,
|
||||||
|
};
|
||||||
|
|
||||||
|
void ClampTrigger(uint8_t* trigger, uint8_t min, uint8_t max) {
|
||||||
|
if (*trigger <= min) {
|
||||||
|
*trigger = 0;
|
||||||
|
} else {
|
||||||
|
if (*trigger > max) {
|
||||||
|
*trigger = max;
|
||||||
|
}
|
||||||
|
*trigger -= min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClampCircle(int8_t* px, int8_t* py, int8_t radius, int8_t min) {
|
||||||
|
int x = *px;
|
||||||
|
int y = *py;
|
||||||
|
|
||||||
|
if (-min < x && x < min) {
|
||||||
|
x = 0;
|
||||||
|
} else if (0 < x) {
|
||||||
|
x -= min;
|
||||||
|
} else {
|
||||||
|
x += min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-min < y && y < min) {
|
||||||
|
y = 0;
|
||||||
|
} else if (0 < y) {
|
||||||
|
y -= min;
|
||||||
|
} else {
|
||||||
|
y += min;
|
||||||
|
}
|
||||||
|
|
||||||
|
int squared = x * x + y * y;
|
||||||
|
if (radius * radius < squared) {
|
||||||
|
int32_t length = static_cast<int32_t>(std::sqrt(squared));
|
||||||
|
x = (x * radius) / length;
|
||||||
|
y = (y * radius) / length;
|
||||||
|
}
|
||||||
|
|
||||||
|
*px = static_cast<int8_t>(x);
|
||||||
|
*py = static_cast<int8_t>(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClampStick(int8_t* px, int8_t* py, int8_t max, int8_t xy, int8_t min) {
|
||||||
|
int32_t x = *px;
|
||||||
|
int32_t y = *py;
|
||||||
|
|
||||||
|
int32_t signX = 0;
|
||||||
|
if (0 <= x) {
|
||||||
|
signX = 1;
|
||||||
|
} else {
|
||||||
|
signX = -1;
|
||||||
|
x = -x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t signY = 0;
|
||||||
|
if (0 <= y) {
|
||||||
|
signY = 1;
|
||||||
|
} else {
|
||||||
|
signY = -1;
|
||||||
|
y = -y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x <= min) {
|
||||||
|
x = 0;
|
||||||
|
} else {
|
||||||
|
x -= min;
|
||||||
|
}
|
||||||
|
if (y <= min) {
|
||||||
|
y = 0;
|
||||||
|
} else {
|
||||||
|
y -= min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x == 0 && y == 0) {
|
||||||
|
*px = *py = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xy * y <= xy * x) {
|
||||||
|
int32_t d = xy * x + (max - xy) * y;
|
||||||
|
if (xy * max < d) {
|
||||||
|
x = (xy * max * x / d);
|
||||||
|
y = (xy * max * y / d);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int32_t d = xy * y + (max - xy) * x;
|
||||||
|
if (xy * max < d) {
|
||||||
|
x = (xy * max * x / d);
|
||||||
|
y = (xy * max * y / d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*px = (signX * x);
|
||||||
|
*py = (signY * y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADClamp(PADStatus* status) {
|
||||||
|
for (uint32_t i = 0; i < 4; ++i) {
|
||||||
|
if (status[i].err != PAD_ERR_NONE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClampStick(&status[i].stickX, &status[i].stickY, ClampRegion.maxStick, ClampRegion.xyStick, ClampRegion.minStick);
|
||||||
|
ClampStick(&status[i].substickX, &status[i].substickY, ClampRegion.maxSubstick, ClampRegion.xySubstick,
|
||||||
|
ClampRegion.minSubstick);
|
||||||
|
ClampTrigger(&status[i].triggerL, ClampRegion.minTrigger, ClampRegion.maxTrigger);
|
||||||
|
ClampTrigger(&status[i].triggerR, ClampRegion.minTrigger, ClampRegion.maxTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADClampCircle(PADStatus* status) {
|
||||||
|
for (uint32_t i = 0; i < 4; ++i) {
|
||||||
|
if (status[i].err != PAD_ERR_NONE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClampCircle(&status[i].stickX, &status[i].stickY, ClampRegion.radStick, ClampRegion.minStick);
|
||||||
|
ClampCircle(&status[i].substickX, &status[i].substickY, ClampRegion.radSubstick, ClampRegion.minSubstick);
|
||||||
|
ClampTrigger(&status[i].triggerL, ClampRegion.minTrigger, ClampRegion.maxTrigger);
|
||||||
|
ClampTrigger(&status[i].triggerR, ClampRegion.minTrigger, ClampRegion.maxTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADGetVidPid(uint32_t port, uint32_t* vid, uint32_t* pid) {
|
||||||
|
*vid = 0;
|
||||||
|
*pid = 0;
|
||||||
|
auto* controller = aurora::input::get_controller_for_player(port);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*vid = controller->m_vid;
|
||||||
|
*pid = controller->m_pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* PADGetName(uint32_t port) {
|
||||||
|
auto* controller = aurora::input::get_controller_for_player(port);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_GameControllerName(controller->m_controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADSetButtonMapping(uint32_t port, PADButtonMapping mapping) {
|
||||||
|
auto* controller = aurora::input::get_controller_for_player(port);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = std::find_if(controller->m_mapping.begin(), controller->m_mapping.end(),
|
||||||
|
[mapping](const auto& pair) { return mapping.padButton == pair.padButton; });
|
||||||
|
if (iter == controller->m_mapping.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*iter = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADSetAllButtonMappings(uint32_t port, PADButtonMapping buttons[12]) {
|
||||||
|
for (uint32_t i = 0; i < 12; ++i) {
|
||||||
|
PADSetButtonMapping(port, buttons[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PADButtonMapping* PADGetButtonMappings(uint32_t port, uint32_t* buttonCount) {
|
||||||
|
auto* controller = aurora::input::get_controller_for_player(port);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
*buttonCount = 0;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buttonCount = controller->m_mapping.size();
|
||||||
|
return controller->m_mapping.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __PADWriteDeadZones(FILE* file, aurora::input::GameController& controller) {
|
||||||
|
fwrite(&controller.m_deadZones, 1, sizeof(PADDeadZones), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADSerializeMappings() {
|
||||||
|
std::string basePath{aurora::g_config.configPath};
|
||||||
|
|
||||||
|
bool wroteGameCubeAlready = false;
|
||||||
|
for (auto& controller : aurora::input::g_GameControllers) {
|
||||||
|
if (!controller.second.m_mappingLoaded) {
|
||||||
|
__PADLoadMapping(&controller.second);
|
||||||
|
}
|
||||||
|
FILE* file = fopen(fmt::format(FMT_STRING("{}/{}_{:04X}_{:04X}.controller"), basePath,
|
||||||
|
aurora::input::controller_name(controller.second.m_index), controller.second.m_vid,
|
||||||
|
controller.second.m_pid)
|
||||||
|
.c_str(),
|
||||||
|
"wbe");
|
||||||
|
if (file == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t magic = SBIG('CTRL');
|
||||||
|
uint32_t version = 1;
|
||||||
|
fwrite(&magic, 1, sizeof(magic), file);
|
||||||
|
fwrite(&version, 1, sizeof(magic), file);
|
||||||
|
fwrite(&controller.second.m_isGameCube, 1, 1, file);
|
||||||
|
fseek(file, (ftell(file) + 31) & ~31, SEEK_SET);
|
||||||
|
int32_t dataStart = ftell(file);
|
||||||
|
if (!controller.second.m_isGameCube) {
|
||||||
|
__PADWriteDeadZones(file, controller.second);
|
||||||
|
fwrite(controller.second.m_mapping.data(), 1, sizeof(PADButtonMapping) * controller.second.m_mapping.size(),
|
||||||
|
file);
|
||||||
|
} else {
|
||||||
|
if (!wroteGameCubeAlready) {
|
||||||
|
for (uint32_t i = 0; i < 4; ++i) {
|
||||||
|
/* Just use the current controller's configs for this */
|
||||||
|
__PADWriteDeadZones(file, controller.second);
|
||||||
|
fwrite(mDefaultButtons.data(), 1, sizeof(PADButtonMapping) * mDefaultButtons.size(), file);
|
||||||
|
}
|
||||||
|
fflush(file);
|
||||||
|
wroteGameCubeAlready = true;
|
||||||
|
}
|
||||||
|
uint32_t port = aurora::input::player_index(controller.second.m_index);
|
||||||
|
fseek(file, dataStart + ((sizeof(PADDeadZones) + sizeof(PADButtonMapping)) * port), SEEK_SET);
|
||||||
|
__PADWriteDeadZones(file, controller.second);
|
||||||
|
fwrite(controller.second.m_mapping.data(), 1, sizeof(PADButtonMapping) * controller.second.m_mapping.size(),
|
||||||
|
file);
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PADDeadZones* PADGetDeadZones(uint32_t port) {
|
||||||
|
auto* controller = aurora::input::get_controller_for_player(port);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &controller->m_deadZones;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<std::pair<PADButton, std::string_view>, 12> skButtonNames = {{
|
||||||
|
{PAD_BUTTON_LEFT, "Left"sv},
|
||||||
|
{PAD_BUTTON_RIGHT, "Right"sv},
|
||||||
|
{PAD_BUTTON_DOWN, "Down"sv},
|
||||||
|
{PAD_BUTTON_UP, "Up"sv},
|
||||||
|
{PAD_TRIGGER_Z, "Z"sv},
|
||||||
|
{PAD_TRIGGER_R, "R"sv},
|
||||||
|
{PAD_TRIGGER_L, "L"sv},
|
||||||
|
{PAD_BUTTON_A, "A"sv},
|
||||||
|
{PAD_BUTTON_B, "B"sv},
|
||||||
|
{PAD_BUTTON_X, "X"sv},
|
||||||
|
{PAD_BUTTON_Y, "Y"sv},
|
||||||
|
{PAD_BUTTON_START, "Start"sv},
|
||||||
|
}};
|
||||||
|
|
||||||
|
const char* PADGetButtonName(PADButton button) {
|
||||||
|
auto it = std::find_if(skButtonNames.begin(), skButtonNames.end(),
|
||||||
|
[&button](const auto& pair) { return button == pair.first; });
|
||||||
|
|
||||||
|
if (it != skButtonNames.end()) {
|
||||||
|
return it->second.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* PADGetNativeButtonName(uint32_t button) {
|
||||||
|
return SDL_GameControllerGetStringForButton(static_cast<SDL_GameControllerButton>(button));
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t PADGetNativeButtonPressed(uint32_t port) {
|
||||||
|
auto* controller = aurora::input::get_controller_for_player(port);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
|
||||||
|
if (SDL_GameControllerGetButton(controller->m_controller, static_cast<SDL_GameControllerButton>(i)) != 0u) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADRestoreDefaultMapping(uint32_t port) {
|
||||||
|
auto* controller = aurora::input::get_controller_for_player(port);
|
||||||
|
if (controller == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
controller->m_mapping = mDefaultButtons;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PADBlockInput(bool block) { gBlockPAD = block; }
|
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "SDL_gamecontroller.h"
|
||||||
|
#include "SDL_keyboard.h"
|
||||||
|
#include "SDL_keycode.h"
|
||||||
|
#include "SDL_mouse.h"
|
||||||
|
|
||||||
|
namespace aurora::input {
|
||||||
|
Sint32 get_instance_for_player(uint32_t player) noexcept;
|
||||||
|
Sint32 add_controller(Sint32 which) noexcept;
|
||||||
|
void remove_controller(Uint32 instance) noexcept;
|
||||||
|
Sint32 player_index(Uint32 instance) noexcept;
|
||||||
|
void set_player_index(Uint32 instance, Sint32 index) noexcept;
|
||||||
|
std::string controller_name(Uint32 instance) noexcept;
|
||||||
|
bool is_gamecube(Uint32 instance) noexcept;
|
||||||
|
bool controller_has_rumble(Uint32 instance) noexcept;
|
||||||
|
void controller_rumble(uint32_t instance, uint16_t low_freq_intensity, uint16_t high_freq_intensity,
|
||||||
|
uint16_t duration_ms) noexcept;
|
||||||
|
uint32_t controller_count() noexcept;
|
||||||
|
} // namespace aurora::input
|
|
@ -0,0 +1,113 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <aurora/aurora.h>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
#ifndef SBIG
|
||||||
|
#define SBIG(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifndef SBIG
|
||||||
|
#define SBIG(q) (q)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
[[noreturn]] inline __attribute__((always_inline)) void unreachable() { __builtin_unreachable(); }
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
[[noreturn]] __forceinline void unreachable() { __assume(false); }
|
||||||
|
#else
|
||||||
|
#error Unknown compiler
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ALIGN
|
||||||
|
#define ALIGN(x, a) (((x) + ((a)-1)) & ~((a)-1))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace aurora {
|
||||||
|
extern AuroraConfig g_config;
|
||||||
|
|
||||||
|
struct Module {
|
||||||
|
const char* name;
|
||||||
|
explicit Module(const char* name) noexcept : name(name) {}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
inline void report(AuroraLogLevel level, fmt::format_string<T...> fmt, T&&... args) noexcept {
|
||||||
|
auto message = fmt::format(fmt, std::forward<T>(args)...);
|
||||||
|
if (g_config.logCallback != nullptr) {
|
||||||
|
g_config.logCallback(level, message.c_str(), message.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class ArrayRef {
|
||||||
|
public:
|
||||||
|
using value_type = std::remove_cvref_t<T>;
|
||||||
|
using pointer = value_type*;
|
||||||
|
using const_pointer = const value_type*;
|
||||||
|
using reference = value_type&;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
using iterator = const_pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
ArrayRef() = default;
|
||||||
|
explicit ArrayRef(const T& one) : ptr(&one), length(1) {}
|
||||||
|
ArrayRef(const T* data, size_t length) : ptr(data), length(length) {}
|
||||||
|
ArrayRef(const T* begin, const T* end) : ptr(begin), length(end - begin) {}
|
||||||
|
template <size_t N>
|
||||||
|
constexpr ArrayRef(const T (&arr)[N]) : ptr(arr), length(N) {}
|
||||||
|
template <size_t N>
|
||||||
|
constexpr ArrayRef(const std::array<T, N>& arr) : ptr(arr.data()), length(arr.size()) {}
|
||||||
|
ArrayRef(const std::vector<T>& vec) : ptr(vec.data()), length(vec.size()) {}
|
||||||
|
// template <size_t N>
|
||||||
|
// ArrayRef(const rstl::reserved_vector<T, N>& vec) : ptr(vec.data()), length(vec.size()) {}
|
||||||
|
|
||||||
|
const T* data() const { return ptr; }
|
||||||
|
size_t size() const { return length; }
|
||||||
|
bool empty() const { return length == 0; }
|
||||||
|
|
||||||
|
const T& front() const {
|
||||||
|
assert(!empty());
|
||||||
|
return ptr[0];
|
||||||
|
}
|
||||||
|
const T& back() const {
|
||||||
|
assert(!empty());
|
||||||
|
return ptr[length - 1];
|
||||||
|
}
|
||||||
|
const T& operator[](size_t i) const {
|
||||||
|
assert(i < length && "Invalid index!");
|
||||||
|
return ptr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin() const { return ptr; }
|
||||||
|
iterator end() const { return ptr + length; }
|
||||||
|
|
||||||
|
reverse_iterator rbegin() const { return reverse_iterator(end()); }
|
||||||
|
reverse_iterator rend() const { return reverse_iterator(begin()); }
|
||||||
|
|
||||||
|
/// Disallow accidental assignment from a temporary.
|
||||||
|
template <typename U>
|
||||||
|
std::enable_if_t<std::is_same<U, T>::value, ArrayRef<T>>& operator=(U&& Temporary) = delete;
|
||||||
|
|
||||||
|
/// Disallow accidental assignment from a temporary.
|
||||||
|
template <typename U>
|
||||||
|
std::enable_if_t<std::is_same<U, T>::value, ArrayRef<T>>& operator=(std::initializer_list<U>) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const T* ptr = nullptr;
|
||||||
|
size_t length = 0;
|
||||||
|
};
|
||||||
|
} // namespace aurora
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <aurora/main.h>
|
||||||
|
#undef main
|
||||||
|
|
||||||
|
#include <SDL_main.h>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { return aurora_main(argc, argv); }
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <dolphin/pad.h>
|
||||||
|
#include <dolphin/si.h>
|
||||||
|
|
|
@ -0,0 +1,466 @@
|
||||||
|
#include "gpu.hpp"
|
||||||
|
|
||||||
|
#include <aurora/aurora.h>
|
||||||
|
|
||||||
|
#include "../window.hpp"
|
||||||
|
#include "../internal.hpp"
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <dawn/native/DawnNative.h>
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "../dawn/BackendBinding.hpp"
|
||||||
|
|
||||||
|
namespace aurora::webgpu {
|
||||||
|
static Module Log("aurora::gpu");
|
||||||
|
|
||||||
|
WGPUDevice g_device;
|
||||||
|
WGPUQueue g_queue;
|
||||||
|
WGPUSwapChain g_swapChain;
|
||||||
|
WGPUBackendType g_backendType;
|
||||||
|
GraphicsConfig g_graphicsConfig;
|
||||||
|
TextureWithSampler g_frameBuffer;
|
||||||
|
TextureWithSampler g_frameBufferResolved;
|
||||||
|
TextureWithSampler g_depthBuffer;
|
||||||
|
|
||||||
|
// EFB -> XFB copy pipeline
|
||||||
|
static WGPUBindGroupLayout g_CopyBindGroupLayout;
|
||||||
|
WGPURenderPipeline g_CopyPipeline;
|
||||||
|
WGPUBindGroup g_CopyBindGroup;
|
||||||
|
|
||||||
|
static std::unique_ptr<dawn::native::Instance> g_Instance;
|
||||||
|
static dawn::native::Adapter g_Adapter;
|
||||||
|
static WGPUAdapterProperties g_AdapterProperties;
|
||||||
|
static std::unique_ptr<utils::BackendBinding> g_BackendBinding;
|
||||||
|
|
||||||
|
TextureWithSampler create_render_texture(bool multisampled) {
|
||||||
|
const WGPUExtent3D size{
|
||||||
|
.width = g_graphicsConfig.width,
|
||||||
|
.height = g_graphicsConfig.height,
|
||||||
|
.depthOrArrayLayers = 1,
|
||||||
|
};
|
||||||
|
const auto format = g_graphicsConfig.colorFormat;
|
||||||
|
uint32_t sampleCount = 1;
|
||||||
|
if (multisampled) {
|
||||||
|
sampleCount = g_graphicsConfig.msaaSamples;
|
||||||
|
}
|
||||||
|
const WGPUTextureDescriptor textureDescriptor{
|
||||||
|
.label = "Render texture",
|
||||||
|
.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopySrc |
|
||||||
|
WGPUTextureUsage_CopyDst,
|
||||||
|
.dimension = WGPUTextureDimension_2D,
|
||||||
|
.size = size,
|
||||||
|
.format = format,
|
||||||
|
.mipLevelCount = 1,
|
||||||
|
.sampleCount = sampleCount,
|
||||||
|
};
|
||||||
|
auto texture = wgpuDeviceCreateTexture(g_device, &textureDescriptor);
|
||||||
|
|
||||||
|
const WGPUTextureViewDescriptor viewDescriptor{
|
||||||
|
.dimension = WGPUTextureViewDimension_2D,
|
||||||
|
.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED,
|
||||||
|
.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED,
|
||||||
|
};
|
||||||
|
auto view = wgpuTextureCreateView(texture, &viewDescriptor);
|
||||||
|
|
||||||
|
const WGPUSamplerDescriptor samplerDescriptor{
|
||||||
|
.label = "Render sampler",
|
||||||
|
.addressModeU = WGPUAddressMode_ClampToEdge,
|
||||||
|
.addressModeV = WGPUAddressMode_ClampToEdge,
|
||||||
|
.addressModeW = WGPUAddressMode_ClampToEdge,
|
||||||
|
.magFilter = WGPUFilterMode_Linear,
|
||||||
|
.minFilter = WGPUFilterMode_Linear,
|
||||||
|
.mipmapFilter = WGPUFilterMode_Linear,
|
||||||
|
.lodMinClamp = 0.f,
|
||||||
|
.lodMaxClamp = 1000.f,
|
||||||
|
.maxAnisotropy = 1,
|
||||||
|
};
|
||||||
|
auto sampler = wgpuDeviceCreateSampler(g_device, &samplerDescriptor);
|
||||||
|
|
||||||
|
return {
|
||||||
|
.texture{texture},
|
||||||
|
.view{view},
|
||||||
|
.size = size,
|
||||||
|
.format = format,
|
||||||
|
.sampler{sampler},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static TextureWithSampler create_depth_texture() {
|
||||||
|
const WGPUExtent3D size{
|
||||||
|
.width = g_graphicsConfig.width,
|
||||||
|
.height = g_graphicsConfig.height,
|
||||||
|
.depthOrArrayLayers = 1,
|
||||||
|
};
|
||||||
|
const auto format = g_graphicsConfig.depthFormat;
|
||||||
|
const WGPUTextureDescriptor textureDescriptor{
|
||||||
|
.label = "Depth texture",
|
||||||
|
.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding,
|
||||||
|
.dimension = WGPUTextureDimension_2D,
|
||||||
|
.size = size,
|
||||||
|
.format = format,
|
||||||
|
.mipLevelCount = 1,
|
||||||
|
.sampleCount = g_graphicsConfig.msaaSamples,
|
||||||
|
};
|
||||||
|
auto texture = wgpuDeviceCreateTexture(g_device, &textureDescriptor);
|
||||||
|
|
||||||
|
const WGPUTextureViewDescriptor viewDescriptor{
|
||||||
|
.dimension = WGPUTextureViewDimension_2D,
|
||||||
|
.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED,
|
||||||
|
.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED,
|
||||||
|
};
|
||||||
|
auto view = wgpuTextureCreateView(texture, &viewDescriptor);
|
||||||
|
|
||||||
|
const WGPUSamplerDescriptor samplerDescriptor{
|
||||||
|
.label = "Depth sampler",
|
||||||
|
.addressModeU = WGPUAddressMode_ClampToEdge,
|
||||||
|
.addressModeV = WGPUAddressMode_ClampToEdge,
|
||||||
|
.addressModeW = WGPUAddressMode_ClampToEdge,
|
||||||
|
.magFilter = WGPUFilterMode_Linear,
|
||||||
|
.minFilter = WGPUFilterMode_Linear,
|
||||||
|
.mipmapFilter = WGPUFilterMode_Linear,
|
||||||
|
.lodMinClamp = 0.f,
|
||||||
|
.lodMaxClamp = 1000.f,
|
||||||
|
.maxAnisotropy = 1,
|
||||||
|
};
|
||||||
|
auto sampler = wgpuDeviceCreateSampler(g_device, &samplerDescriptor);
|
||||||
|
|
||||||
|
return {
|
||||||
|
.texture{texture},
|
||||||
|
.view{view},
|
||||||
|
.size = size,
|
||||||
|
.format = format,
|
||||||
|
.sampler{sampler},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_copy_pipeline() {
|
||||||
|
WGPUShaderModuleWGSLDescriptor sourceDescriptor{
|
||||||
|
.chain = {.sType = WGPUSType_ShaderModuleWGSLDescriptor},
|
||||||
|
.source = R"""(
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var efb_sampler: sampler;
|
||||||
|
@group(0) @binding(1)
|
||||||
|
var efb_texture: texture_2d<f32>;
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) pos: vec4<f32>,
|
||||||
|
@location(0) uv: vec2<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
var<private> pos: array<vec2<f32>, 3> = array<vec2<f32>, 3>(
|
||||||
|
vec2(-1.0, 1.0),
|
||||||
|
vec2(-1.0, -3.0),
|
||||||
|
vec2(3.0, 1.0),
|
||||||
|
);
|
||||||
|
var<private> uvs: array<vec2<f32>, 3> = array<vec2<f32>, 3>(
|
||||||
|
vec2(0.0, 0.0),
|
||||||
|
vec2(0.0, 2.0),
|
||||||
|
vec2(2.0, 0.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
@stage(vertex)
|
||||||
|
fn vs_main(@builtin(vertex_index) vtxIdx: u32) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
out.pos = vec4<f32>(pos[vtxIdx], 0.0, 1.0);
|
||||||
|
out.uv = uvs[vtxIdx];
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@stage(fragment)
|
||||||
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
return textureSample(efb_texture, efb_sampler, in.uv);
|
||||||
|
}
|
||||||
|
)""",
|
||||||
|
};
|
||||||
|
const WGPUShaderModuleDescriptor moduleDescriptor{
|
||||||
|
.nextInChain = &sourceDescriptor.chain,
|
||||||
|
.label = "XFB Copy Module",
|
||||||
|
};
|
||||||
|
auto module = wgpuDeviceCreateShaderModule(g_device, &moduleDescriptor);
|
||||||
|
const std::array colorTargets{WGPUColorTargetState{
|
||||||
|
.format = g_graphicsConfig.colorFormat,
|
||||||
|
.writeMask = WGPUColorWriteMask_All,
|
||||||
|
}};
|
||||||
|
const WGPUFragmentState fragmentState{
|
||||||
|
.module = module,
|
||||||
|
.entryPoint = "fs_main",
|
||||||
|
.targetCount = colorTargets.size(),
|
||||||
|
.targets = colorTargets.data(),
|
||||||
|
};
|
||||||
|
const std::array bindGroupLayoutEntries{
|
||||||
|
WGPUBindGroupLayoutEntry{
|
||||||
|
.binding = 0,
|
||||||
|
.visibility = WGPUShaderStage_Fragment,
|
||||||
|
.sampler =
|
||||||
|
WGPUSamplerBindingLayout{
|
||||||
|
.type = WGPUSamplerBindingType_Filtering,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
WGPUBindGroupLayoutEntry{
|
||||||
|
.binding = 1,
|
||||||
|
.visibility = WGPUShaderStage_Fragment,
|
||||||
|
.texture =
|
||||||
|
WGPUTextureBindingLayout{
|
||||||
|
.sampleType = WGPUTextureSampleType_Float,
|
||||||
|
.viewDimension = WGPUTextureViewDimension_2D,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const WGPUBindGroupLayoutDescriptor bindGroupLayoutDescriptor{
|
||||||
|
.entryCount = bindGroupLayoutEntries.size(),
|
||||||
|
.entries = bindGroupLayoutEntries.data(),
|
||||||
|
};
|
||||||
|
g_CopyBindGroupLayout = wgpuDeviceCreateBindGroupLayout(g_device, &bindGroupLayoutDescriptor);
|
||||||
|
const WGPUPipelineLayoutDescriptor layoutDescriptor{
|
||||||
|
.bindGroupLayoutCount = 1,
|
||||||
|
.bindGroupLayouts = &g_CopyBindGroupLayout,
|
||||||
|
};
|
||||||
|
auto pipelineLayout = wgpuDeviceCreatePipelineLayout(g_device, &layoutDescriptor);
|
||||||
|
const WGPURenderPipelineDescriptor pipelineDescriptor{
|
||||||
|
.layout = pipelineLayout,
|
||||||
|
.vertex =
|
||||||
|
WGPUVertexState{
|
||||||
|
.module = module,
|
||||||
|
.entryPoint = "vs_main",
|
||||||
|
},
|
||||||
|
.primitive =
|
||||||
|
WGPUPrimitiveState{
|
||||||
|
.topology = WGPUPrimitiveTopology_TriangleList,
|
||||||
|
},
|
||||||
|
.multisample =
|
||||||
|
WGPUMultisampleState{
|
||||||
|
.count = 1,
|
||||||
|
.mask = UINT32_MAX,
|
||||||
|
},
|
||||||
|
.fragment = &fragmentState,
|
||||||
|
};
|
||||||
|
g_CopyPipeline = wgpuDeviceCreateRenderPipeline(g_device, &pipelineDescriptor);
|
||||||
|
wgpuPipelineLayoutRelease(pipelineLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_copy_bind_group() {
|
||||||
|
const std::array bindGroupEntries{
|
||||||
|
WGPUBindGroupEntry{
|
||||||
|
.binding = 0,
|
||||||
|
.sampler = g_graphicsConfig.msaaSamples > 1 ? g_frameBufferResolved.sampler : g_frameBuffer.sampler,
|
||||||
|
},
|
||||||
|
WGPUBindGroupEntry{
|
||||||
|
.binding = 1,
|
||||||
|
.textureView = g_graphicsConfig.msaaSamples > 1 ? g_frameBufferResolved.view : g_frameBuffer.view,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const WGPUBindGroupDescriptor bindGroupDescriptor{
|
||||||
|
.layout = g_CopyBindGroupLayout,
|
||||||
|
.entryCount = bindGroupEntries.size(),
|
||||||
|
.entries = bindGroupEntries.data(),
|
||||||
|
};
|
||||||
|
g_CopyBindGroup = wgpuDeviceCreateBindGroup(g_device, &bindGroupDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void error_callback(WGPUErrorType type, char const* message, void* userdata) {
|
||||||
|
Log.report(LOG_FATAL, FMT_STRING("Dawn error {}: {}"), magic_enum::enum_name(static_cast<WGPUErrorType>(type)),
|
||||||
|
message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void device_callback(WGPURequestDeviceStatus status, WGPUDevice device, char const* message, void* userdata) {
|
||||||
|
if (status == WGPURequestDeviceStatus_Success) {
|
||||||
|
g_device = device;
|
||||||
|
} else {
|
||||||
|
Log.report(LOG_WARNING, FMT_STRING("Device request failed with message: {}"), message);
|
||||||
|
}
|
||||||
|
*static_cast<bool*>(userdata) = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WGPUBackendType to_wgpu_backend(AuroraBackend backend) {
|
||||||
|
switch (backend) {
|
||||||
|
case BACKEND_WEBGPU:
|
||||||
|
return WGPUBackendType_WebGPU;
|
||||||
|
case BACKEND_D3D12:
|
||||||
|
return WGPUBackendType_D3D12;
|
||||||
|
case BACKEND_METAL:
|
||||||
|
return WGPUBackendType_Metal;
|
||||||
|
case BACKEND_VULKAN:
|
||||||
|
return WGPUBackendType_Vulkan;
|
||||||
|
case BACKEND_OPENGL:
|
||||||
|
return WGPUBackendType_OpenGL;
|
||||||
|
case BACKEND_OPENGLES:
|
||||||
|
return WGPUBackendType_OpenGLES;
|
||||||
|
default:
|
||||||
|
return WGPUBackendType_Null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initialize(AuroraBackend auroraBackend) {
|
||||||
|
if (!g_Instance) {
|
||||||
|
Log.report(LOG_INFO, FMT_STRING("Creating Dawn instance"));
|
||||||
|
g_Instance = std::make_unique<dawn::native::Instance>();
|
||||||
|
}
|
||||||
|
WGPUBackendType backend = to_wgpu_backend(auroraBackend);
|
||||||
|
Log.report(LOG_INFO, FMT_STRING("Attempting to initialize {}"), magic_enum::enum_name(backend));
|
||||||
|
#if 0
|
||||||
|
// D3D12's debug layer is very slow
|
||||||
|
g_Instance->EnableBackendValidation(backend != WGPUBackendType::D3D12);
|
||||||
|
#endif
|
||||||
|
SDL_Window* window = window::get_sdl_window();
|
||||||
|
if (!utils::DiscoverAdapter(g_Instance.get(), window, backend)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::vector<dawn::native::Adapter> adapters = g_Instance->GetAdapters();
|
||||||
|
std::sort(adapters.begin(), adapters.end(), [&](const auto& a, const auto& b) {
|
||||||
|
WGPUAdapterProperties propertiesA;
|
||||||
|
WGPUAdapterProperties propertiesB;
|
||||||
|
a.GetProperties(&propertiesA);
|
||||||
|
b.GetProperties(&propertiesB);
|
||||||
|
constexpr std::array PreferredTypeOrder{
|
||||||
|
WGPUAdapterType_DiscreteGPU,
|
||||||
|
WGPUAdapterType_IntegratedGPU,
|
||||||
|
WGPUAdapterType_CPU,
|
||||||
|
};
|
||||||
|
const auto typeItA = std::find(PreferredTypeOrder.begin(), PreferredTypeOrder.end(), propertiesA.adapterType);
|
||||||
|
const auto typeItB = std::find(PreferredTypeOrder.begin(), PreferredTypeOrder.end(), propertiesB.adapterType);
|
||||||
|
return typeItA < typeItB;
|
||||||
|
});
|
||||||
|
const auto adapterIt = std::find_if(adapters.begin(), adapters.end(), [=](const auto& adapter) -> bool {
|
||||||
|
WGPUAdapterProperties properties;
|
||||||
|
adapter.GetProperties(&properties);
|
||||||
|
return properties.backendType == backend;
|
||||||
|
});
|
||||||
|
if (adapterIt == adapters.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
g_Adapter = *adapterIt;
|
||||||
|
}
|
||||||
|
g_Adapter.GetProperties(&g_AdapterProperties);
|
||||||
|
g_backendType = g_AdapterProperties.backendType;
|
||||||
|
const auto backendName = magic_enum::enum_name(g_backendType);
|
||||||
|
Log.report(LOG_INFO, FMT_STRING("Graphics adapter information\n API: {}\n Device: {} ({})\n Driver: {}"),
|
||||||
|
backendName, g_AdapterProperties.name, magic_enum::enum_name(g_AdapterProperties.adapterType),
|
||||||
|
g_AdapterProperties.driverDescription);
|
||||||
|
|
||||||
|
{
|
||||||
|
WGPUSupportedLimits supportedLimits{};
|
||||||
|
g_Adapter.GetLimits(&supportedLimits);
|
||||||
|
const WGPURequiredLimits requiredLimits{
|
||||||
|
.limits =
|
||||||
|
{
|
||||||
|
// Use "best" supported alignments
|
||||||
|
.minUniformBufferOffsetAlignment = supportedLimits.limits.minUniformBufferOffsetAlignment == 0
|
||||||
|
? static_cast<uint32_t>(WGPU_LIMIT_U32_UNDEFINED)
|
||||||
|
: supportedLimits.limits.minUniformBufferOffsetAlignment,
|
||||||
|
.minStorageBufferOffsetAlignment = supportedLimits.limits.minStorageBufferOffsetAlignment == 0
|
||||||
|
? static_cast<uint32_t>(WGPU_LIMIT_U32_UNDEFINED)
|
||||||
|
: supportedLimits.limits.minStorageBufferOffsetAlignment,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
std::vector<WGPUFeatureName> features;
|
||||||
|
const auto supportedFeatures = g_Adapter.GetSupportedFeatures();
|
||||||
|
for (const auto* const feature : supportedFeatures) {
|
||||||
|
if (strcmp(feature, "texture-compression-bc") == 0) {
|
||||||
|
features.push_back(WGPUFeatureName_TextureCompressionBC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const std::array enableToggles {
|
||||||
|
/* clang-format off */
|
||||||
|
#if _WIN32
|
||||||
|
"use_dxc",
|
||||||
|
#endif
|
||||||
|
#ifdef NDEBUG
|
||||||
|
"skip_validation",
|
||||||
|
"disable_robustness",
|
||||||
|
#endif
|
||||||
|
"use_user_defined_labels_in_backend",
|
||||||
|
"disable_symbol_renaming",
|
||||||
|
/* clang-format on */
|
||||||
|
};
|
||||||
|
const WGPUDawnTogglesDeviceDescriptor togglesDescriptor{
|
||||||
|
.chain = {.sType = WGPUSType_DawnTogglesDeviceDescriptor},
|
||||||
|
.forceEnabledTogglesCount = enableToggles.size(),
|
||||||
|
.forceEnabledToggles = enableToggles.data(),
|
||||||
|
};
|
||||||
|
const WGPUDeviceDescriptor deviceDescriptor{
|
||||||
|
.nextInChain = &togglesDescriptor.chain,
|
||||||
|
.requiredFeaturesCount = static_cast<uint32_t>(features.size()),
|
||||||
|
.requiredFeatures = features.data(),
|
||||||
|
.requiredLimits = &requiredLimits,
|
||||||
|
};
|
||||||
|
bool deviceCallbackReceived = false;
|
||||||
|
g_Adapter.RequestDevice(&deviceDescriptor, &device_callback, &deviceCallbackReceived);
|
||||||
|
// while (!deviceCallbackReceived) {
|
||||||
|
// TODO wgpuInstanceProcessEvents
|
||||||
|
// }
|
||||||
|
if (!g_device) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
wgpuDeviceSetUncapturedErrorCallback(g_device, &error_callback, nullptr);
|
||||||
|
}
|
||||||
|
wgpuDeviceSetDeviceLostCallback(g_device, nullptr, nullptr);
|
||||||
|
g_queue = wgpuDeviceGetQueue(g_device);
|
||||||
|
|
||||||
|
g_BackendBinding = std::unique_ptr<utils::BackendBinding>(utils::CreateBinding(g_backendType, window, g_device));
|
||||||
|
if (!g_BackendBinding) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto swapChainFormat = static_cast<WGPUTextureFormat>(g_BackendBinding->GetPreferredSwapChainTextureFormat());
|
||||||
|
if (swapChainFormat == WGPUTextureFormat_RGBA8UnormSrgb) {
|
||||||
|
swapChainFormat = WGPUTextureFormat_RGBA8Unorm;
|
||||||
|
} else if (swapChainFormat == WGPUTextureFormat_BGRA8UnormSrgb) {
|
||||||
|
swapChainFormat = WGPUTextureFormat_BGRA8Unorm;
|
||||||
|
}
|
||||||
|
Log.report(LOG_INFO, FMT_STRING("Using swapchain format {}"), magic_enum::enum_name(swapChainFormat));
|
||||||
|
{
|
||||||
|
const WGPUSwapChainDescriptor descriptor{
|
||||||
|
.format = swapChainFormat,
|
||||||
|
.implementation = g_BackendBinding->GetSwapChainImplementation(),
|
||||||
|
};
|
||||||
|
g_swapChain = wgpuDeviceCreateSwapChain(g_device, nullptr, &descriptor);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto size = window::get_window_size();
|
||||||
|
g_graphicsConfig = GraphicsConfig{
|
||||||
|
.width = size.fb_width,
|
||||||
|
.height = size.fb_height,
|
||||||
|
.colorFormat = swapChainFormat,
|
||||||
|
.depthFormat = WGPUTextureFormat_Depth32Float,
|
||||||
|
.msaaSamples = g_config.msaa,
|
||||||
|
.textureAnisotropy = g_config.maxTextureAnisotropy,
|
||||||
|
};
|
||||||
|
create_copy_pipeline();
|
||||||
|
resize_swapchain(size.fb_width, size.fb_height, true);
|
||||||
|
// g_windowSize = size;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown() {
|
||||||
|
wgpuBindGroupLayoutRelease(g_CopyBindGroupLayout);
|
||||||
|
wgpuRenderPipelineRelease(g_CopyPipeline);
|
||||||
|
wgpuBindGroupRelease(g_CopyBindGroup);
|
||||||
|
g_frameBuffer = {};
|
||||||
|
g_frameBufferResolved = {};
|
||||||
|
g_depthBuffer = {};
|
||||||
|
wgpuSwapChainRelease(g_swapChain);
|
||||||
|
wgpuQueueRelease(g_queue);
|
||||||
|
g_BackendBinding.reset();
|
||||||
|
wgpuDeviceDestroy(g_device);
|
||||||
|
g_Instance.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize_swapchain(uint32_t width, uint32_t height, bool force) {
|
||||||
|
if (!force && g_graphicsConfig.width == width && g_graphicsConfig.height == height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_graphicsConfig.width = width;
|
||||||
|
g_graphicsConfig.height = height;
|
||||||
|
wgpuSwapChainConfigure(g_swapChain, g_graphicsConfig.colorFormat, WGPUTextureUsage_RenderAttachment, width, height);
|
||||||
|
g_frameBuffer = create_render_texture(true);
|
||||||
|
g_frameBufferResolved = create_render_texture(false);
|
||||||
|
g_depthBuffer = create_depth_texture();
|
||||||
|
create_copy_bind_group();
|
||||||
|
}
|
||||||
|
} // namespace aurora::webgpu
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue