├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── GX.md ├── LICENSE ├── README.md ├── cmake ├── aurora_core.cmake ├── aurora_gx.cmake ├── aurora_main.cmake ├── aurora_mtx.cmake ├── aurora_pad.cmake ├── aurora_si.cmake └── aurora_vi.cmake ├── examples ├── CMakeLists.txt └── simple.c ├── extern └── CMakeLists.txt ├── include ├── aurora │ ├── aurora.h │ ├── event.h │ ├── imgui.h │ ├── main.h │ └── math.hpp ├── dolphin │ ├── gx.h │ ├── gx │ │ ├── GXBump.h │ │ ├── GXCommandList.h │ │ ├── GXCull.h │ │ ├── GXDispList.h │ │ ├── GXDraw.h │ │ ├── GXEnum.h │ │ ├── GXExtra.h │ │ ├── GXFifo.h │ │ ├── GXFrameBuffer.h │ │ ├── GXGeometry.h │ │ ├── GXGet.h │ │ ├── GXLighting.h │ │ ├── GXManage.h │ │ ├── GXPerf.h │ │ ├── GXPixel.h │ │ ├── GXStruct.h │ │ ├── GXTev.h │ │ ├── GXTexture.h │ │ ├── GXTransform.h │ │ └── GXVert.h │ ├── mtx.h │ ├── mtx │ │ ├── GeoTypes.h │ │ └── mtx44ext.h │ ├── pad.h │ ├── si.h │ ├── types.h │ ├── vi.h │ └── vifuncs.h └── magic_enum.hpp └── lib ├── aurora.cpp ├── dawn ├── BackendBinding.cpp ├── BackendBinding.hpp └── MetalBinding.mm ├── dolphin ├── gx │ ├── GXBump.cpp │ ├── GXCull.cpp │ ├── GXDispList.cpp │ ├── GXDraw.cpp │ ├── GXExtra.cpp │ ├── GXFifo.cpp │ ├── GXFrameBuffer.cpp │ ├── GXGeometry.cpp │ ├── GXGet.cpp │ ├── GXLighting.cpp │ ├── GXManage.cpp │ ├── GXPerf.cpp │ ├── GXPixel.cpp │ ├── GXTev.cpp │ ├── GXTexture.cpp │ ├── GXTransform.cpp │ ├── GXVert.cpp │ └── gx.hpp ├── mtx.c ├── pad │ └── pad.cpp ├── si │ └── si.cpp └── vi │ └── vi.cpp ├── gfx ├── common.cpp ├── common.hpp ├── display_list.cpp ├── display_list.hpp ├── gx.cpp ├── gx.hpp ├── gx_fmt.hpp ├── gx_shader.cpp ├── model │ ├── shader.cpp │ └── shader.hpp ├── shader_info.cpp ├── shader_info.hpp ├── texture.cpp ├── texture.hpp ├── texture_convert.cpp └── texture_convert.hpp ├── imgui.cpp ├── imgui.hpp ├── input.cpp ├── input.hpp ├── internal.hpp ├── logging.cpp ├── logging.hpp ├── main.cpp ├── webgpu ├── gpu.cpp ├── gpu.hpp └── wgpu.hpp ├── window.cpp └── window.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | ColumnLimit: 120 4 | UseTab: Never 5 | TabWidth: 2 6 | --- 7 | Language: Cpp 8 | DerivePointerAlignment: false 9 | PointerAlignment: Left 10 | AlignAfterOpenBracket: Align 11 | AlignConsecutiveAssignments: false 12 | IndentCaseLabels: false 13 | AllowShortBlocksOnASingleLine: Always 14 | AlignOperands: true 15 | AlignTrailingComments: true 16 | AlwaysBreakBeforeMultilineStrings: true 17 | AlwaysBreakTemplateDeclarations: Yes 18 | BreakConstructorInitializersBeforeComma: true 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakAfterDefinitionReturnType: None 21 | AllowShortFunctionsOnASingleLine: All 22 | Cpp11BracedListStyle: true 23 | NamespaceIndentation: None 24 | BinPackArguments: true 25 | BinPackParameters: true 26 | SortIncludes: false 27 | AccessModifierOffset: -2 28 | ConstructorInitializerIndentWidth: 0 29 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .buildcache/ 2 | .DS_Store 3 | .idea/ 4 | .vs/ 5 | build/ 6 | cmake-build-*/ 7 | CMakeUserPresets.json 8 | out/ 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(aurora LANGUAGES C CXX) 3 | set(CMAKE_C_STANDARD 11) 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | add_subdirectory(extern) 7 | 8 | include(cmake/aurora_core.cmake) 9 | include(cmake/aurora_gx.cmake) 10 | include(cmake/aurora_pad.cmake) 11 | include(cmake/aurora_si.cmake) 12 | include(cmake/aurora_main.cmake) 13 | include(cmake/aurora_mtx.cmake) 14 | include(cmake/aurora_vi.cmake) 15 | 16 | if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) 17 | add_subdirectory(examples) 18 | endif () 19 | -------------------------------------------------------------------------------- /GX.md: -------------------------------------------------------------------------------- 1 | # GX API Support 2 | 3 | - GXBump 4 | - [x] GXSetNumIndStages 5 | - [x] GXSetIndTexOrder 6 | - [x] GXSetIndTexCoordScale 7 | - [x] GXSetIndTexMtx 8 | - [x] GXSetTevIndirect 9 | - [x] GXSetTevDirect 10 | - [x] GXSetTevIndWarp 11 | - [ ] GXSetTevIndTile 12 | - [ ] GXSetTevIndBumpST 13 | - [ ] GXSetTevIndBumpXYZ 14 | - [ ] GXSetTevIndRepeat 15 | - GXCull 16 | - [x] GXSetScissor 17 | - [x] GXSetCullMode 18 | - [ ] GXSetCoPlanar 19 | - GXDispList 20 | - [x] GXBeginDisplayList (stub) 21 | - [x] GXEndDisplayList (stub) 22 | - [x] GXCallDisplayList 23 | - GXDraw 24 | - [ ] GXDrawCylinder 25 | - [ ] GXDrawTorus 26 | - [ ] GXDrawSphere 27 | - [ ] GXDrawCube 28 | - [ ] GXDrawDodeca 29 | - [ ] GXDrawOctahedron 30 | - [ ] GXDrawIcosahedron 31 | - [ ] GXDrawSphere1 32 | - [ ] GXGenNormalTable 33 | - GXFifo 34 | - [x] GXGetGPStatus (stub) 35 | - [ ] GXGetFifoStatus 36 | - [x] GXGetFifoPtrs (stub) 37 | - [x] GXGetCPUFifo (stub) 38 | - [x] GXGetGPFifo (stub) 39 | - [ ] GXGetFifoBase 40 | - [ ] GXGetFifoSize 41 | - [ ] GXGetFifoLimits 42 | - [ ] GXSetBreakPtCallback 43 | - [ ] GXEnableBreakPt 44 | - [ ] GXDisableBreakPt 45 | - [x] GXInitFifoBase (stub) 46 | - [x] GXInitFifoPtrs (stub) 47 | - [ ] GXInitFifoLimits 48 | - [x] GXSetCPUFifo (stub) 49 | - [x] GXSetGPFifo (stub) 50 | - [x] GXSaveCPUFifo (stub) 51 | - [ ] GXSaveGPFifo 52 | - [ ] GXRedirectWriteGatherPipe 53 | - [ ] GXRestoreWriteGatherPipe 54 | - [ ] GXSetCurrentGXThread 55 | - [ ] GXGetCurrentGXThread 56 | - [ ] GXGetOverflowCount 57 | - [ ] GXResetOverflowCount 58 | - GXFrameBuffer 59 | - [x] GXAdjustForOverscan 60 | - [x] GXSetDispCopySrc (stub) 61 | - [x] GXSetTexCopySrc 62 | - [x] GXSetDispCopyDst (stub) 63 | - [x] GXSetTexCopyDst 64 | - [ ] GXSetDispCopyFrame2Field 65 | - [ ] GXSetCopyClamp 66 | - [x] GXSetDispCopyYScale (stub) 67 | - [x] GXSetCopyClear 68 | - [x] GXSetCopyFilter (stub) 69 | - [x] GXSetDispCopyGamma (stub) 70 | - [x] GXCopyDisp (stub) 71 | - [x] GXCopyTex 72 | - [ ] GXGetYScaleFactor 73 | - [ ] GXGetNumXfbLines 74 | - [ ] GXClearBoundingBox 75 | - [ ] GXReadBoundingBox 76 | - GXGeometry 77 | - [x] GXSetVtxDesc 78 | - [x] GXSetVtxDescv 79 | - [x] GXClearVtxDesc 80 | - [x] GXSetVtxAttrFmt 81 | - [ ] GXSetVtxAttrFmtv 82 | - [x] GXSetArray 83 | - [x] GXBegin 84 | - [x] GXEnd 85 | - [x] GXSetTexCoordGen2 86 | - [x] GXSetNumTexGens 87 | - [ ] GXInvalidateVtxCache 88 | - [ ] GXSetLineWidth 89 | - [ ] GXSetPointSize 90 | - [ ] GXEnableTexOffsets 91 | - GXGet 92 | - [ ] GXGetVtxDesc 93 | - [ ] GXGetVtxDescv 94 | - [ ] GXGetVtxAttrFmtv 95 | - [ ] GXGetLineWidth 96 | - [ ] GXGetPointSize 97 | - [x] GXGetVtxAttrFmt 98 | - [ ] GXGetViewportv 99 | - [x] GXGetProjectionv 100 | - [ ] GXGetScissor 101 | - [ ] GXGetCullMode 102 | - [x] GXGetLightAttnA 103 | - [x] GXGetLightAttnK 104 | - [x] GXGetLightPos 105 | - [x] GXGetLightDir 106 | - [x] GXGetLightColor 107 | - [x] GXGetTexObjData 108 | - [x] GXGetTexObjWidth 109 | - [x] GXGetTexObjHeight 110 | - [x] GXGetTexObjFmt 111 | - [x] GXGetTexObjWrapS 112 | - [x] GXGetTexObjWrapT 113 | - [x] GXGetTexObjMipMap 114 | - [ ] GXGetTexObjAll 115 | - [ ] GXGetTexObjMinFilt 116 | - [ ] GXGetTexObjMagFilt 117 | - [ ] GXGetTexObjMinLOD 118 | - [ ] GXGetTexObjMaxLOD 119 | - [ ] GXGetTexObjLODBias 120 | - [ ] GXGetTexObjBiasClamp 121 | - [ ] GXGetTexObjEdgeLOD 122 | - [ ] GXGetTexObjMaxAniso 123 | - [ ] GXGetTexObjLODAll 124 | - [ ] GXGetTexObjTlut 125 | - [ ] GXGetTlutObjData 126 | - [ ] GXGetTlutObjFmt 127 | - [ ] GXGetTlutObjNumEntries 128 | - [ ] GXGetTlutObjAll 129 | - [ ] GXGetTexRegionAll 130 | - [ ] GXGetTlutRegionAll 131 | - GXLighting 132 | - [x] GXInitLightAttn 133 | - [x] GXInitLightAttnA 134 | - [x] GXInitLightAttnK 135 | - [x] GXInitLightSpot 136 | - [x] GXInitLightDistAttn 137 | - [x] GXInitLightPos 138 | - [x] GXInitLightColor 139 | - [x] GXLoadLightObjImm 140 | - [ ] GXLoadLightObjIndx 141 | - [x] GXSetChanAmbColor 142 | - [x] GXSetChanMatColor 143 | - [x] GXSetNumChans 144 | - [x] GXInitLightDir 145 | - [x] GXInitSpecularDir 146 | - [x] GXInitSpecularDirHA 147 | - [x] GXSetChanCtrl 148 | - GXManage 149 | - [x] GXInit (stub) 150 | - [ ] GXAbortFrame 151 | - [ ] GXSetDrawSync 152 | - [ ] GXReadDrawSync 153 | - [ ] GXSetDrawSyncCallback 154 | - [x] GXDrawDone (stub) 155 | - [x] GXSetDrawDone (stub) 156 | - [ ] GXWaitDrawDone 157 | - [x] GXSetDrawDoneCallback (stub) 158 | - [ ] GXSetResetWritePipe 159 | - [x] GXFlush (stub) 160 | - [ ] GXResetWriteGatherPipe 161 | - [x] GXPixModeSync (stub) 162 | - [x] GXTexModeSync (stub) 163 | - [ ] IsWriteGatherBufferEmpty 164 | - [ ] GXSetMisc 165 | - GXPerf 166 | - [ ] GXSetGPMetric 167 | - [ ] GXClearGPMetric 168 | - [ ] GXReadGPMetric 169 | - [ ] GXReadGP0Metric 170 | - [ ] GXReadGP1Metric 171 | - [ ] GXReadMemMetric 172 | - [ ] GXClearMemMetric 173 | - [ ] GXReadPixMetric 174 | - [ ] GXClearPixMetric 175 | - [ ] GXSetVCacheMetric 176 | - [ ] GXReadVCacheMetric 177 | - [ ] GXClearVCacheMetric 178 | - [ ] GXReadXfRasMetric 179 | - [ ] GXInitXfRasMetric 180 | - [ ] GXReadClksPerVtx 181 | - GXPixel 182 | - [x] GXSetFog 183 | - [x] GXSetFogColor 184 | - [ ] GXInitFogAdjTable 185 | - [ ] GXSetFogRangeAdj 186 | - [x] GXSetBlendMode 187 | - [x] GXSetColorUpdate 188 | - [x] GXSetAlphaUpdate 189 | - [x] GXSetZMode 190 | - [ ] GXSetZCompLoc 191 | - [x] GXSetPixelFmt (stub) 192 | - [x] GXSetDither (stub) 193 | - [x] GXSetDstAlpha 194 | - [ ] GXSetFieldMask 195 | - [ ] GXSetFieldMode 196 | - GXTev 197 | - [x] GXSetTevOp 198 | - [x] GXSetTevColorIn 199 | - [x] GXSetTevAlphaIn 200 | - [x] GXSetTevColorOp 201 | - [x] GXSetTevAlphaOp 202 | - [x] GXSetTevColor 203 | - [x] GXSetTevColorS10 204 | - [x] GXSetAlphaCompare 205 | - [x] GXSetTevOrder 206 | - [ ] GXSetZTexture 207 | - [x] GXSetNumTevStages 208 | - [x] GXSetTevKColor 209 | - [x] GXSetTevKColorSel 210 | - [x] GXSetTevKAlphaSel 211 | - [x] GXSetTevSwapMode 212 | - [x] GXSetTevSwapModeTable 213 | - GXTexture 214 | - [x] GXInitTexObj 215 | - [x] GXInitTexObjCI 216 | - [x] GXInitTexObjLOD 217 | - [x] GXInitTexObjData 218 | - [x] GXInitTexObjWrapMode 219 | - [x] GXInitTexObjTlut 220 | - [ ] GXInitTexObjFilter 221 | - [ ] GXInitTexObjMaxLOD 222 | - [ ] GXInitTexObjMinLOD 223 | - [ ] GXInitTexObjLODBias 224 | - [ ] GXInitTexObjBiasClamp 225 | - [ ] GXInitTexObjEdgeLOD 226 | - [ ] GXInitTexObjMaxAniso 227 | - [ ] GXInitTexObjUserData 228 | - [ ] GXGetTexObjUserData 229 | - [x] GXLoadTexObj 230 | - [x] GXGetTexBufferSize 231 | - [x] GXInitTlutObj 232 | - [x] GXLoadTlut 233 | - [ ] GXInitTexCacheRegion 234 | - [ ] GXInitTexPreLoadRegion 235 | - [ ] GXInitTlutRegion 236 | - [ ] GXInvalidateTexRegion 237 | - [x] GXInvalidateTexAll (stub) 238 | - [ ] GXPreLoadEntireTexture 239 | - [ ] GXSetTexRegionCallback 240 | - [ ] GXSetTlutRegionCallback 241 | - [ ] GXLoadTexObjPreLoaded 242 | - [ ] GXSetTexCoordScaleManually 243 | - [ ] GXSetTexCoordCylWrap 244 | - [ ] GXSetTexCoordBias 245 | - GXTransform 246 | - [x] GXSetProjection 247 | - [ ] GXSetProjectionv 248 | - [x] GXLoadPosMtxImm 249 | - [ ] GXLoadPosMtxIndx 250 | - [x] GXLoadNrmMtxImm 251 | - [ ] GXLoadNrmMtxImm3x3 252 | - [ ] GXLoadNrmMtxIndx3x3 253 | - [x] GXSetCurrentMtx 254 | - [x] GXLoadTexMtxImm 255 | - [ ] GXLoadTexMtxIndx 256 | - [ ] GXProject 257 | - [x] GXSetViewport 258 | - [x] GXSetViewportJitter 259 | - [ ] GXSetZScaleOffset 260 | - [ ] GXSetScissorBoxOffset 261 | - [ ] GXSetClipMode 262 | - GXVert 263 | - [x] GXPosition\[n]\[t] 264 | - [x] GXNormal\[n]\[t] 265 | - [x] GXColor\[n]\[t] 266 | - [x] GXTexCoord\[n]\[t] 267 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2022 Luke Street 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aurora 2 | 3 | Aurora is a source-level GameCube & Wii compatibility layer intended for use with game reverse engineering projects. 4 | 5 | Originally developed for use in [Metaforce](https://github.com/AxioDL/metaforce), a Metroid Prime reverse engineering 6 | project. 7 | 8 | ### Features 9 | 10 | - GX compatibility layer 11 | - Graphics API support: D3D12, Vulkan, Metal, OpenGL 4.4+ and OpenGL ES 3.1+ 12 | - *Planned: deko3d backend for Switch* 13 | - Application layer using SDL 14 | - Runs on Windows, Linux, macOS, iOS, tvOS (Android coming soon) 15 | - Audio support with SDL_audio 16 | - PAD compatibility layer 17 | - Utilizes SDL_GameController for wide controller support, including GameCube controllers. 18 | - *Planned: Wii remote support* 19 | - [Dear ImGui](https://github.com/ocornut/imgui) built-in for UI 20 | 21 | ### GX 22 | 23 | The GX compatibility layer is built on top of [WebGPU](https://www.w3.org/TR/webgpu/), a cross-platform graphics API 24 | abstraction layer. WebGPU allows targeting all major platforms simultaneously with minimal overhead. 25 | 26 | Currently, the WebGPU implementation used is Chromium's [Dawn](https://dawn.googlesource.com/dawn/). 27 | 28 | See [GX API support](GX.md) for more information. 29 | 30 | ### PAD 31 | 32 | The PAD compatibility layer utilizes SDL_GameController to automatically support & provide mappings for hundreds of 33 | controllers across all platforms. 34 | -------------------------------------------------------------------------------- /cmake/aurora_core.cmake: -------------------------------------------------------------------------------- 1 | add_library(aurora_core STATIC 2 | lib/aurora.cpp 3 | lib/webgpu/gpu.cpp 4 | lib/imgui.cpp 5 | lib/input.cpp 6 | lib/window.cpp 7 | lib/logging.cpp 8 | ) 9 | add_library(aurora::core ALIAS aurora_core) 10 | 11 | target_compile_definitions(aurora_core PUBLIC AURORA TARGET_PC) 12 | target_include_directories(aurora_core PUBLIC include) 13 | target_link_libraries(aurora_core PUBLIC SDL3::SDL3-static fmt::fmt imgui xxhash) 14 | if (EMSCRIPTEN) 15 | target_link_options(aurora_core PUBLIC -sUSE_WEBGPU=1 -sASYNCIFY -sEXIT_RUNTIME) 16 | target_compile_definitions(aurora_core PRIVATE ENABLE_BACKEND_WEBGPU) 17 | else () 18 | target_link_libraries(aurora_core PRIVATE dawn::dawn_native dawn::dawn_proc) 19 | target_sources(aurora_core PRIVATE lib/dawn/BackendBinding.cpp) 20 | target_compile_definitions(aurora_core PRIVATE WEBGPU_DAWN) 21 | endif () 22 | target_link_libraries(aurora_core PRIVATE absl::btree absl::flat_hash_map) 23 | if (DAWN_ENABLE_VULKAN) 24 | target_compile_definitions(aurora_core PRIVATE DAWN_ENABLE_BACKEND_VULKAN) 25 | target_link_libraries(aurora_core PRIVATE Vulkan::Headers) 26 | endif () 27 | if (DAWN_ENABLE_METAL) 28 | target_compile_definitions(aurora_core PRIVATE DAWN_ENABLE_BACKEND_METAL) 29 | target_sources(aurora_core PRIVATE lib/dawn/MetalBinding.mm) 30 | set_source_files_properties(lib/dawn/MetalBinding.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) 31 | endif () 32 | if (DAWN_ENABLE_D3D11) 33 | target_compile_definitions(aurora_core PRIVATE DAWN_ENABLE_BACKEND_D3D11) 34 | endif () 35 | if (DAWN_ENABLE_D3D12) 36 | target_compile_definitions(aurora_core PRIVATE DAWN_ENABLE_BACKEND_D3D12) 37 | endif () 38 | if (DAWN_ENABLE_DESKTOP_GL OR DAWN_ENABLE_OPENGLES) 39 | target_compile_definitions(aurora_core PRIVATE DAWN_ENABLE_BACKEND_OPENGL) 40 | if (DAWN_ENABLE_DESKTOP_GL) 41 | target_compile_definitions(aurora_core PRIVATE DAWN_ENABLE_BACKEND_DESKTOP_GL) 42 | endif () 43 | if (DAWN_ENABLE_OPENGLES) 44 | target_compile_definitions(aurora_core PRIVATE DAWN_ENABLE_BACKEND_OPENGLES) 45 | endif () 46 | endif () 47 | if (DAWN_ENABLE_NULL) 48 | target_compile_definitions(aurora_core PRIVATE DAWN_ENABLE_BACKEND_NULL) 49 | endif () -------------------------------------------------------------------------------- /cmake/aurora_gx.cmake: -------------------------------------------------------------------------------- 1 | add_library(aurora_gx STATIC 2 | lib/gfx/common.cpp 3 | lib/gfx/texture.cpp 4 | lib/gfx/gx.cpp 5 | lib/gfx/gx_shader.cpp 6 | lib/gfx/texture_convert.cpp 7 | lib/gfx/display_list.cpp 8 | lib/gfx/shader_info.cpp 9 | lib/gfx/model/shader.cpp 10 | lib/dolphin/gx/GXBump.cpp 11 | lib/dolphin/gx/GXCull.cpp 12 | lib/dolphin/gx/GXDispList.cpp 13 | lib/dolphin/gx/GXDraw.cpp 14 | lib/dolphin/gx/GXExtra.cpp 15 | lib/dolphin/gx/GXFifo.cpp 16 | lib/dolphin/gx/GXFrameBuffer.cpp 17 | lib/dolphin/gx/GXGeometry.cpp 18 | lib/dolphin/gx/GXGet.cpp 19 | lib/dolphin/gx/GXLighting.cpp 20 | lib/dolphin/gx/GXManage.cpp 21 | lib/dolphin/gx/GXPerf.cpp 22 | lib/dolphin/gx/GXPixel.cpp 23 | lib/dolphin/gx/GXTev.cpp 24 | lib/dolphin/gx/GXTexture.cpp 25 | lib/dolphin/gx/GXTransform.cpp 26 | lib/dolphin/gx/GXVert.cpp 27 | ) 28 | add_library(aurora::gx ALIAS aurora_gx) 29 | 30 | target_link_libraries(aurora_gx PUBLIC aurora::core xxhash) 31 | target_link_libraries(aurora_gx PRIVATE absl::btree absl::flat_hash_map) 32 | if (EMSCRIPTEN) 33 | target_link_options(aurora_gx PUBLIC -sUSE_WEBGPU=1 -sASYNCIFY -sEXIT_RUNTIME) 34 | target_compile_definitions(aurora_gx PRIVATE ENABLE_BACKEND_WEBGPU) 35 | else () 36 | target_link_libraries(aurora_gx PRIVATE dawn::dawn_native dawn::dawn_proc) 37 | target_compile_definitions(aurora_gx PRIVATE WEBGPU_DAWN) 38 | endif () -------------------------------------------------------------------------------- /cmake/aurora_main.cmake: -------------------------------------------------------------------------------- 1 | add_library(aurora_main STATIC lib/main.cpp) 2 | add_library(aurora::main ALIAS aurora_main) 3 | 4 | target_include_directories(aurora_main PUBLIC include) 5 | target_link_libraries(aurora_main PUBLIC SDL3::SDL3-static) 6 | -------------------------------------------------------------------------------- /cmake/aurora_mtx.cmake: -------------------------------------------------------------------------------- 1 | add_library(aurora_mtx STATIC 2 | lib/dolphin/mtx.c 3 | ) 4 | add_library(aurora::mtx ALIAS aurora_mtx) 5 | 6 | target_include_directories(aurora_mtx PUBLIC include) 7 | -------------------------------------------------------------------------------- /cmake/aurora_pad.cmake: -------------------------------------------------------------------------------- 1 | add_library(aurora_pad STATIC lib/dolphin/pad/pad.cpp) 2 | 3 | add_library(aurora::pad ALIAS aurora_pad) 4 | 5 | target_include_directories(aurora_pad PUBLIC include) 6 | target_link_libraries(aurora_pad PUBLIC aurora::core aurora::si) 7 | target_link_libraries(aurora_pad PRIVATE absl::flat_hash_map) 8 | -------------------------------------------------------------------------------- /cmake/aurora_si.cmake: -------------------------------------------------------------------------------- 1 | add_library(aurora_si STATIC lib/dolphin/si/si.cpp) 2 | 3 | add_library(aurora::si ALIAS aurora_si) 4 | 5 | target_include_directories(aurora_si PUBLIC include) 6 | target_link_libraries(aurora_si PUBLIC aurora::core) 7 | target_link_libraries(aurora_si PRIVATE absl::flat_hash_map) 8 | -------------------------------------------------------------------------------- /cmake/aurora_vi.cmake: -------------------------------------------------------------------------------- 1 | add_library(aurora_vi STATIC 2 | lib/dolphin/vi/vi.cpp 3 | ) 4 | add_library(aurora::vi ALIAS aurora_vi) 5 | 6 | target_link_libraries(aurora_vi PUBLIC aurora::core) 7 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(simple simple.c) 2 | target_link_libraries(simple PRIVATE aurora::aurora aurora::main) 3 | -------------------------------------------------------------------------------- /examples/simple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | static void log_callback(AuroraLogLevel level, const char* message, unsigned int len) { 11 | const char* levelStr; 12 | FILE* out = stdout; 13 | switch (level) { 14 | case LOG_DEBUG: 15 | levelStr = "DEBUG"; 16 | break; 17 | case LOG_INFO: 18 | levelStr = "INFO"; 19 | break; 20 | case LOG_WARNING: 21 | levelStr = "WARNING"; 22 | break; 23 | case LOG_ERROR: 24 | levelStr = "ERROR"; 25 | out = stderr; 26 | break; 27 | case LOG_FATAL: 28 | levelStr = "FATAL"; 29 | out = stderr; 30 | break; 31 | } 32 | fprintf(out, "[%s: %s]\n", levelStr, message); 33 | if (level == LOG_FATAL) { 34 | fflush(out); 35 | abort(); 36 | } 37 | } 38 | 39 | static void draw() { 40 | GXSetCopyClear( 41 | (GXColor){ 42 | .r = 0, 43 | .g = 0, 44 | .b = 100, 45 | .a = 255, 46 | }, 47 | GX_MAX_Z24); 48 | } 49 | 50 | int main(int argc, char* argv[]) { 51 | const AuroraConfig config = { 52 | .appName = "Demo", 53 | .logCallback = &log_callback, 54 | }; 55 | AuroraInfo initInfo = aurora_initialize(argc, argv, &config); 56 | 57 | bool exiting = false; 58 | bool paused = false; 59 | while (!exiting) { 60 | const AuroraEvent* event = aurora_update(); 61 | while (event != NULL && event->type != AURORA_NONE) { 62 | switch (event->type) { 63 | case AURORA_EXIT: 64 | exiting = true; 65 | break; 66 | case AURORA_PAUSED: 67 | paused = true; 68 | break; 69 | case AURORA_UNPAUSED: 70 | paused = false; 71 | break; 72 | case AURORA_WINDOW_RESIZED: 73 | initInfo.windowSize = event->windowSize; 74 | break; 75 | default: 76 | break; 77 | } 78 | ++event; 79 | } 80 | if (exiting || paused || !aurora_begin_frame()) { 81 | continue; 82 | } 83 | draw(); 84 | aurora_end_frame(); 85 | } 86 | 87 | aurora_shutdown(); 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /extern/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | if (NOT EMSCRIPTEN) 4 | if (NOT TARGET dawn_native) 5 | message(STATUS "aurora: Building Dawn") 6 | 7 | set(DAWN_ENABLE_DESKTOP_GL OFF CACHE INTERNAL "Enable compilation of the OpenGL backend") 8 | set(DAWN_ENABLE_OPENGLES OFF CACHE INTERNAL "Enable compilation of the OpenGL ES backend") 9 | set(DAWN_FETCH_DEPENDENCIES ON CACHE INTERNAL "Use fetch_dawn_dependencies.py as an alternative to using depot_tools") 10 | 11 | FetchContent_Declare(dawn 12 | URL https://github.com/google/dawn/archive/65ce958ed0019a860238703efa0087660c4247d4.tar.gz 13 | URL_HASH SHA256=bd7df417fffd9c3efa390ce09b7e943887d76807e0b7307e192c835c38882ec1 14 | DOWNLOAD_EXTRACT_TIMESTAMP TRUE 15 | EXCLUDE_FROM_ALL 16 | ) 17 | FetchContent_MakeAvailable(dawn) 18 | if (NOT TARGET dawn_native) 19 | message(FATAL_ERROR "Failed to make dawn available") 20 | endif () 21 | else () 22 | message(STATUS "aurora: Using existing Dawn") 23 | endif () 24 | 25 | if (NOT TARGET SDL3-static) 26 | message(STATUS "aurora: Building SDL3") 27 | 28 | if (WIN32) 29 | set(SDL_LIBC ON CACHE BOOL "Use the system C library" FORCE) 30 | endif () 31 | 32 | FetchContent_Declare(SDL 33 | URL https://github.com/libsdl-org/SDL/archive/refs/tags/release-3.2.10.tar.gz 34 | URL_HASH SHA256=8a6ddc3e70de897f7921da275ac71e375de89dad802aa2932649d1fea9068478 35 | DOWNLOAD_EXTRACT_TIMESTAMP TRUE 36 | EXCLUDE_FROM_ALL 37 | ) 38 | FetchContent_MakeAvailable(SDL) 39 | if (NOT TARGET SDL3-static) 40 | message(FATAL_ERROR "Failed to make SDL3 available") 41 | endif () 42 | else () 43 | message(STATUS "aurora: Using existing SDL3") 44 | endif () 45 | else () 46 | set(ABSL_PROPAGATE_CXX_STD ON) 47 | add_subdirectory(dawn/third_party/abseil-cpp EXCLUDE_FROM_ALL) 48 | endif () 49 | 50 | if (NOT TARGET xxhash) 51 | message(STATUS "aurora: Building xxhash") 52 | FetchContent_Declare(xxhash 53 | URL https://github.com/Cyan4973/xxHash/archive/refs/tags/v0.8.3.tar.gz 54 | URL_HASH SHA256=aae608dfe8213dfd05d909a57718ef82f30722c392344583d3f39050c7f29a80 55 | SOURCE_SUBDIR cmake_unofficial 56 | DOWNLOAD_EXTRACT_TIMESTAMP TRUE 57 | EXCLUDE_FROM_ALL 58 | ) 59 | set(XXHASH_BUILD_XXHSUM OFF CACHE INTERNAL "Build the xxhsum binary") 60 | FetchContent_MakeAvailable(xxhash) 61 | if (NOT TARGET xxhash) 62 | message(FATAL_ERROR "Failed to make xxhash available") 63 | endif () 64 | else () 65 | message(STATUS "aurora: Using existing xxhash") 66 | endif () 67 | 68 | if (NOT TARGET fmt) 69 | message(STATUS "aurora: Building fmt") 70 | FetchContent_Declare(fmt 71 | URL https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.tar.gz 72 | URL_HASH SHA256=ac366b7b4c2e9f0dde63a59b3feb5ee59b67974b14ee5dc9ea8ad78aa2c1ee1e 73 | DOWNLOAD_EXTRACT_TIMESTAMP TRUE 74 | EXCLUDE_FROM_ALL 75 | ) 76 | FetchContent_MakeAvailable(fmt) 77 | if (NOT TARGET fmt) 78 | message(FATAL_ERROR "Failed to make fmt available") 79 | endif () 80 | else () 81 | message(STATUS "aurora: Using existing fmt") 82 | endif () 83 | 84 | if (NOT TARGET imgui) 85 | message(STATUS "aurora: Building imgui") 86 | FetchContent_Declare(imgui 87 | URL https://github.com/ocornut/imgui/archive/refs/tags/v1.91.9b-docking.tar.gz 88 | URL_HASH SHA256=466fdef9b18de15f0bb6e288e3d00ffa3d82200ec458ce5e4f724a161d9528a5 89 | DOWNLOAD_EXTRACT_TIMESTAMP TRUE 90 | EXCLUDE_FROM_ALL 91 | ) 92 | FetchContent_MakeAvailable(imgui) 93 | 94 | add_library(imgui STATIC 95 | ${imgui_SOURCE_DIR}/imgui.cpp 96 | ${imgui_SOURCE_DIR}/imgui_demo.cpp 97 | ${imgui_SOURCE_DIR}/imgui_draw.cpp 98 | ${imgui_SOURCE_DIR}/imgui_tables.cpp 99 | ${imgui_SOURCE_DIR}/imgui_widgets.cpp 100 | ${imgui_SOURCE_DIR}/misc/cpp/imgui_stdlib.cpp 101 | ) 102 | target_include_directories(imgui PUBLIC ${imgui_SOURCE_DIR}) 103 | 104 | add_library(imgui_backends STATIC 105 | ${imgui_SOURCE_DIR}/backends/imgui_impl_sdl3.cpp 106 | ${imgui_SOURCE_DIR}/backends/imgui_impl_sdlrenderer3.cpp 107 | ${imgui_SOURCE_DIR}/backends/imgui_impl_wgpu.cpp 108 | ) 109 | target_compile_definitions(imgui_backends PRIVATE IMGUI_IMPL_WEBGPU_BACKEND_DAWN) 110 | target_link_libraries(imgui_backends PRIVATE imgui SDL3::SDL3-static dawn::dawn_native) 111 | target_link_libraries(imgui PUBLIC imgui_backends) 112 | 113 | # Optional, replaces stb_freetype if available 114 | find_package(Freetype) 115 | # Permit disabling for macOS universal builds 116 | option(IMGUI_USE_FREETYPE "Enable freetype with imgui" ON) 117 | if (FREETYPE_FOUND AND IMGUI_USE_FREETYPE) 118 | target_sources(imgui PRIVATE ${imgui_SOURCE_DIR}/misc/freetype/imgui_freetype.cpp) 119 | target_compile_definitions(imgui PRIVATE IMGUI_ENABLE_FREETYPE) 120 | target_link_libraries(imgui PRIVATE Freetype::Freetype) 121 | endif () 122 | else () 123 | message(STATUS "aurora: Using existing imgui") 124 | endif () 125 | -------------------------------------------------------------------------------- /include/aurora/aurora.h: -------------------------------------------------------------------------------- 1 | #ifndef AURORA_AURORA_H 2 | #define AURORA_AURORA_H 3 | 4 | #ifdef __cplusplus 5 | #include 6 | #include 7 | 8 | extern "C" { 9 | #else 10 | #include "stdbool.h" 11 | #include "stddef.h" 12 | #include "stdint.h" 13 | #endif 14 | 15 | typedef enum { 16 | BACKEND_AUTO, 17 | BACKEND_D3D11, 18 | BACKEND_D3D12, 19 | BACKEND_METAL, 20 | BACKEND_VULKAN, 21 | BACKEND_OPENGL, 22 | BACKEND_OPENGLES, 23 | BACKEND_WEBGPU, 24 | BACKEND_NULL, 25 | } AuroraBackend; 26 | 27 | typedef enum { 28 | LOG_DEBUG, 29 | LOG_INFO, 30 | LOG_WARNING, 31 | LOG_ERROR, 32 | LOG_FATAL, 33 | } AuroraLogLevel; 34 | 35 | typedef struct { 36 | int32_t x; 37 | int32_t y; 38 | } AuroraWindowPos; 39 | 40 | typedef struct { 41 | uint32_t width; 42 | uint32_t height; 43 | uint32_t fb_width; 44 | uint32_t fb_height; 45 | float scale; 46 | } AuroraWindowSize; 47 | 48 | typedef struct SDL_Window SDL_Window; 49 | typedef struct AuroraEvent AuroraEvent; 50 | 51 | typedef void (*AuroraLogCallback)(AuroraLogLevel level, const char* module, const char* message, unsigned int len); 52 | typedef void (*AuroraImGuiInitCallback)(const AuroraWindowSize* size); 53 | 54 | typedef struct { 55 | const char* appName; 56 | const char* configPath; 57 | AuroraBackend desiredBackend; 58 | uint32_t msaa; 59 | uint16_t maxTextureAnisotropy; 60 | bool startFullscreen; 61 | bool allowJoystickBackgroundEvents; 62 | int32_t windowPosX; 63 | int32_t windowPosY; 64 | uint32_t windowWidth; 65 | uint32_t windowHeight; 66 | void* iconRGBA8; 67 | uint32_t iconWidth; 68 | uint32_t iconHeight; 69 | AuroraLogCallback logCallback; 70 | AuroraImGuiInitCallback imGuiInitCallback; 71 | } AuroraConfig; 72 | 73 | typedef struct { 74 | AuroraBackend backend; 75 | const char* configPath; 76 | SDL_Window* window; 77 | AuroraWindowSize windowSize; 78 | } AuroraInfo; 79 | 80 | AuroraInfo aurora_initialize(int argc, char* argv[], const AuroraConfig* config); 81 | void aurora_shutdown(); 82 | const AuroraEvent* aurora_update(); 83 | bool aurora_begin_frame(); 84 | void aurora_end_frame(); 85 | 86 | AuroraBackend aurora_get_backend(); 87 | const AuroraBackend* aurora_get_available_backends(size_t* count); 88 | 89 | #ifndef NDEBUG 90 | #define AURORA_GFX_DEBUG_GROUPS 91 | #endif 92 | 93 | void push_debug_group(const char* label); 94 | void pop_debug_group(); 95 | 96 | #ifdef __cplusplus 97 | } 98 | #endif 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /include/aurora/event.h: -------------------------------------------------------------------------------- 1 | #ifndef AURORA_EVENT_H 2 | #define AURORA_EVENT_H 3 | 4 | #include "aurora.h" 5 | 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | #include 11 | 12 | extern "C" { 13 | #else 14 | #include "stdint.h" 15 | #endif 16 | 17 | typedef enum { 18 | AURORA_NONE, 19 | AURORA_EXIT, 20 | AURORA_SDL_EVENT, 21 | AURORA_WINDOW_MOVED, 22 | AURORA_WINDOW_RESIZED, 23 | AURORA_CONTROLLER_ADDED, 24 | AURORA_CONTROLLER_REMOVED, 25 | AURORA_PAUSED, 26 | AURORA_UNPAUSED, 27 | AURORA_DISPLAY_SCALE_CHANGED, 28 | } AuroraEventType; 29 | 30 | struct AuroraEvent { 31 | AuroraEventType type; 32 | union { 33 | SDL_Event sdl; 34 | AuroraWindowPos windowPos; 35 | AuroraWindowSize windowSize; 36 | SDL_JoystickID controller; 37 | }; 38 | }; 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /include/aurora/imgui.h: -------------------------------------------------------------------------------- 1 | #ifndef AURORA_IMGUI_H 2 | #define AURORA_IMGUI_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | #include 8 | 9 | extern "C" { 10 | #else 11 | #include "stdint.h" 12 | #endif 13 | 14 | ImTextureID aurora_imgui_add_texture(uint32_t width, uint32_t height, const void* rgba8); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/aurora/main.h: -------------------------------------------------------------------------------- 1 | #ifndef AURORA_MAIN_H 2 | #define AURORA_MAIN_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | int aurora_main(int argc, char* argv[]); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | #define main aurora_main 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/aurora/math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifndef AURORA_VEC2_EXTRA 7 | #define AURORA_VEC2_EXTRA 8 | #endif 9 | #ifndef AURORA_VEC3_EXTRA 10 | #define AURORA_VEC3_EXTRA 11 | #endif 12 | #ifndef AURORA_VEC4_EXTRA 13 | #define AURORA_VEC4_EXTRA 14 | #endif 15 | #ifndef AURORA_MAT4X4_EXTRA 16 | #define AURORA_MAT4X4_EXTRA 17 | #endif 18 | 19 | #ifndef __has_attribute 20 | #define __has_attribute(x) 0 21 | #endif 22 | #ifndef __has_builtin 23 | #define __has_builtin(x) 0 24 | #endif 25 | #if __has_attribute(vector_size) 26 | #define USE_GCC_VECTOR_EXTENSIONS 27 | #endif 28 | 29 | namespace aurora { 30 | template 31 | struct Vec2 { 32 | T x{}; 33 | T y{}; 34 | 35 | constexpr Vec2() = default; 36 | constexpr Vec2(T x, T y) : x(x), y(y) {} 37 | AURORA_VEC2_EXTRA 38 | 39 | bool operator==(const Vec2& rhs) const { return x == rhs.x && y == rhs.y; } 40 | bool operator!=(const Vec2& rhs) const { return !(*this == rhs); } 41 | }; 42 | template 43 | struct Vec3 { 44 | T x{}; 45 | T y{}; 46 | T z{}; 47 | 48 | constexpr Vec3() = default; 49 | constexpr Vec3(T x, T y, T z) : x(x), y(y), z(z) {} 50 | AURORA_VEC3_EXTRA 51 | 52 | bool operator==(const Vec3& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; } 53 | bool operator!=(const Vec3& rhs) const { return !(*this == rhs); } 54 | }; 55 | template 56 | struct Vec4 { 57 | #ifdef USE_GCC_VECTOR_EXTENSIONS 58 | typedef T Vt __attribute__((vector_size(sizeof(T) * 4))); 59 | Vt m; 60 | #else 61 | using Vt = T[4]; 62 | Vt m; 63 | #endif 64 | 65 | constexpr Vec4() = default; 66 | constexpr Vec4(Vt m) : m(m) {} 67 | constexpr Vec4(T x, T y, T z, T w) : m{x, y, z, w} {} 68 | // For Vec3 with padding 69 | constexpr Vec4(T x, T y, T z) : m{x, y, z, {}} {} 70 | // For Vec3 -> Vec4 71 | constexpr Vec4(Vec3 v, T w) : m{v.x, v.y, v.z, w} {} 72 | AURORA_VEC4_EXTRA 73 | 74 | inline Vec4& operator=(const Vec4& other) { 75 | memcpy(&m, &other.m, sizeof(Vt)); 76 | return *this; 77 | } 78 | 79 | [[nodiscard]] inline T& x() { return m[0]; } 80 | [[nodiscard]] inline T x() const { return m[0]; } 81 | [[nodiscard]] inline T& y() { return m[1]; } 82 | [[nodiscard]] inline T y() const { return m[1]; } 83 | [[nodiscard]] inline T& z() { return m[2]; } 84 | [[nodiscard]] inline T z() const { return m[2]; } 85 | [[nodiscard]] inline T& w() { return m[3]; } 86 | [[nodiscard]] inline T w() const { return m[3]; } 87 | [[nodiscard]] inline T& operator[](size_t i) { return m[i]; } 88 | [[nodiscard]] inline T operator[](size_t i) const { return m[i]; } 89 | 90 | template 91 | [[nodiscard]] constexpr Vec4 shuffle() const { 92 | static_assert(x < 4 && y < 4 && z < 4 && w < 4); 93 | #if defined(USE_GCC_VECTOR_EXTENSIONS) && __has_builtin(__builtin_shuffle) 94 | typedef int Vi __attribute__((vector_size(16))); 95 | return __builtin_shuffle(m, Vi{x, y, z, w}); 96 | #else 97 | return {m[x], m[y], m[z], m[w]}; 98 | #endif 99 | } 100 | 101 | bool operator==(const Vec4& rhs) const { 102 | #if defined(USE_GCC_VECTOR_EXTENSIONS) && __has_builtin(__builtin_reduce_and) 103 | return __builtin_reduce_and(m == rhs.m) != 0; 104 | #else 105 | return m[0] == rhs.m[0] && m[1] == rhs.m[1] && m[2] == rhs.m[2] && m[3] == rhs.m[3]; 106 | #endif 107 | } 108 | bool operator!=(const Vec4& rhs) const { return !(*this == rhs); } 109 | }; 110 | template 111 | [[nodiscard]] Vec4 operator+(const Vec4& a, const Vec4& b) { 112 | #ifdef USE_GCC_VECTOR_EXTENSIONS 113 | return a.m + b.m; 114 | #else 115 | 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]}; 116 | #endif 117 | } 118 | template 119 | [[nodiscard]] Vec4 operator*(const Vec4& a, const Vec4& b) { 120 | #ifdef USE_GCC_VECTOR_EXTENSIONS 121 | return a.m * b.m; 122 | #else 123 | 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]}; 124 | #endif 125 | } 126 | template 127 | struct Mat3x2 { 128 | Vec2 m0{}; 129 | Vec2 m1{}; 130 | Vec2 m2{}; 131 | 132 | constexpr Mat3x2() = default; 133 | constexpr Mat3x2(const Vec2& m0, const Vec2& m1, const Vec2& m2) : m0(m0), m1(m1), m2(m2) {} 134 | 135 | bool operator==(const Mat3x2& rhs) const { return m0 == rhs.m0 && m1 == rhs.m1 && m2 == rhs.m2; } 136 | bool operator!=(const Mat3x2& rhs) const { return !(*this == rhs); } 137 | }; 138 | template 139 | struct Mat4x2 { 140 | Vec2 m0{}; 141 | Vec2 m1{}; 142 | Vec2 m2{}; 143 | Vec2 m3{}; 144 | 145 | constexpr Mat4x2() = default; 146 | constexpr Mat4x2(const Vec2& m0, const Vec2& m1, const Vec2& m2, const Vec2& m3) 147 | : m0(m0), m1(m1), m2(m2), m3(m3) {} 148 | 149 | inline Mat4x2 transpose() const { 150 | return { 151 | {m0.x, m2.x}, 152 | {m0.y, m2.y}, 153 | {m1.x, m3.x}, 154 | {m1.y, m3.y}, 155 | }; 156 | } 157 | 158 | bool operator==(const Mat4x2& rhs) const { return m0 == rhs.m0 && m1 == rhs.m1 && m2 == rhs.m2 && m3 == rhs.m3; } 159 | bool operator!=(const Mat4x2& rhs) const { return !(*this == rhs); } 160 | }; 161 | template 162 | struct Mat2x4 { 163 | Vec4 m0{}; 164 | Vec4 m1{}; 165 | 166 | constexpr Mat2x4() = default; 167 | constexpr Mat2x4(const Vec4& m0, const Vec4& m1, const Vec4& m2) : m0(m0), m1(m1) {} 168 | 169 | bool operator==(const Mat2x4& rhs) const { return m0 == rhs.m0 && m1 == rhs.m1; } 170 | bool operator!=(const Mat2x4& rhs) const { return !(*this == rhs); } 171 | }; 172 | static_assert(sizeof(Mat2x4) == 32); 173 | template 174 | struct Mat4x4; 175 | template 176 | struct Mat3x4 { 177 | Vec4 m0{}; 178 | Vec4 m1{}; 179 | Vec4 m2{}; 180 | 181 | constexpr Mat3x4() = default; 182 | constexpr Mat3x4(const Vec4& m0, const Vec4& m1, const Vec4& m2) : m0(m0), m1(m1), m2(m2) {} 183 | 184 | [[nodiscard]] Mat4x4 to4x4() const; 185 | [[nodiscard]] Mat4x4 toTransposed4x4() const; 186 | 187 | bool operator==(const Mat3x4& rhs) const { return m0 == rhs.m0 && m1 == rhs.m1 && m2 == rhs.m2; } 188 | bool operator!=(const Mat3x4& rhs) const { return !(*this == rhs); } 189 | }; 190 | static_assert(sizeof(Mat3x4) == 48); 191 | template 192 | struct Mat4x4 { 193 | Vec4 m0{}; 194 | Vec4 m1{}; 195 | Vec4 m2{}; 196 | Vec4 m3{}; 197 | 198 | constexpr Mat4x4() = default; 199 | constexpr Mat4x4(const Vec4& m0, const Vec4& m1, const Vec4& m2, const Vec4& m3) 200 | : m0(m0), m1(m1), m2(m2), m3(m3) {} 201 | AURORA_MAT4X4_EXTRA 202 | 203 | [[nodiscard]] Mat4x4 transpose() const { 204 | return { 205 | {m0[0], m1[0], m2[0], m3[0]}, 206 | {m0[1], m1[1], m2[1], m3[1]}, 207 | {m0[2], m1[2], m2[2], m3[2]}, 208 | {m0[3], m1[3], m2[3], m3[3]}, 209 | }; 210 | } 211 | Mat4x4& operator=(const Mat4x4& other) = default; 212 | 213 | Vec4& operator[](size_t i) { return *(&m0 + i); } 214 | const Vec4& operator[](size_t i) const { return *(&m0 + i); } 215 | 216 | bool operator==(const Mat4x4& rhs) const { return m0 == rhs.m0 && m1 == rhs.m1 && m2 == rhs.m2 && m3 == rhs.m3; } 217 | bool operator!=(const Mat4x4& rhs) const { return !(*this == rhs); } 218 | }; 219 | static_assert(sizeof(Mat4x4) == 64); 220 | template 221 | [[nodiscard]] Mat4x4 operator*(const Mat4x4& a, const Mat4x4& b) { 222 | Mat4x4 out; 223 | for (size_t i = 0; i < 4; ++i) { 224 | *(&out.m0 + i) = a.m0 * b[i].template shuffle<0, 0, 0, 0>() + a.m1 * b[i].template shuffle<1, 1, 1, 1>() + 225 | a.m2 * b[i].template shuffle<2, 2, 2, 2>() + a.m3 * b[i].template shuffle<3, 3, 3, 3>(); 226 | } 227 | return out; 228 | } 229 | template 230 | [[nodiscard]] Mat4x4 Mat3x4::to4x4() const { 231 | return { 232 | {m0[0], m0[1], m0[2], 0.f}, 233 | {m1[0], m1[1], m1[2], 0.f}, 234 | {m2[0], m2[1], m2[2], 0.f}, 235 | {m0[3], m1[3], m2[3], 1.f}, 236 | }; 237 | } 238 | template 239 | [[nodiscard]] Mat4x4 Mat3x4::toTransposed4x4() const { 240 | return Mat4x4{ 241 | {m0[0], m1[0], m2[0], 0.f}, 242 | {m0[1], m1[1], m2[1], 0.f}, 243 | {m0[2], m1[2], m2[2], 0.f}, 244 | {m0[3], m1[3], m2[3], 1.f}, 245 | }; 246 | } 247 | constexpr Mat4x4 Mat4x4_Identity{ 248 | Vec4{1.f, 0.f, 0.f, 0.f}, 249 | Vec4{0.f, 1.f, 0.f, 0.f}, 250 | Vec4{0.f, 0.f, 1.f, 0.f}, 251 | Vec4{0.f, 0.f, 0.f, 1.f}, 252 | }; 253 | } // namespace aurora 254 | -------------------------------------------------------------------------------- /include/dolphin/gx.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GX_H 2 | #define DOLPHIN_GX_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXBump.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXBUMP_H 2 | #define DOLPHIN_GXBUMP_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void GXSetTevDirect(GXTevStageID tev_stage); 11 | void GXSetNumIndStages(u8 nIndStages); 12 | #ifdef TARGET_PC 13 | void GXSetIndTexMtx(GXIndTexMtxID mtx_sel, const void* offset, s8 scale_exp); 14 | #else 15 | void GXSetIndTexMtx(GXIndTexMtxID mtx_sel, f32 offset[2][3], s8 scale_exp); 16 | #endif 17 | void GXSetIndTexOrder(GXIndTexStageID ind_stage, GXTexCoordID tex_coord, GXTexMapID tex_map); 18 | void GXSetIndTexCoordScale(GXIndTexStageID indStage, GXIndTexScale scaleS, GXIndTexScale scaleT); 19 | void GXSetTevIndirect(GXTevStageID tev_stage, GXIndTexStageID ind_stage, GXIndTexFormat format, 20 | GXIndTexBiasSel bias_sel, GXIndTexMtxID matrix_sel, GXIndTexWrap wrap_s, GXIndTexWrap wrap_t, 21 | GXBool add_prev, GXBool ind_lod, GXIndTexAlphaSel alpha_sel); 22 | void GXSetTevIndWarp(GXTevStageID tev_stage, GXIndTexStageID ind_stage, GXBool signed_offsets, GXBool replace_mode, 23 | GXIndTexMtxID matrix_sel); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXCommandList.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXCOMMANDLIST_H 2 | #define DOLPHIN_GXCOMMANDLIST_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #define GX_NOP 0x00 9 | #define GX_DRAW_QUADS 0x80 10 | #define GX_DRAW_TRIANGLES 0x90 11 | #define GX_DRAW_TRIANGLE_STRIP 0x98 12 | #define GX_DRAW_TRIANGLE_FAN 0xA0 13 | #define GX_DRAW_LINES 0xA8 14 | #define GX_DRAW_LINE_STRIP 0xB0 15 | #define GX_DRAW_POINTS 0xB8 16 | 17 | #define GX_LOAD_BP_REG 0x61 18 | #define GX_LOAD_CP_REG 0x08 19 | #define GX_LOAD_XF_REG 0x10 20 | #define GX_LOAD_INDX_A 0x20 21 | #define GX_LOAD_INDX_B 0x28 22 | #define GX_LOAD_INDX_C 0x30 23 | #define GX_LOAD_INDX_D 0x38 24 | 25 | #define GX_CMD_CALL_DL 0x40 26 | #define GX_CMD_INVL_VC 0x48 27 | 28 | #define GX_OPCODE_MASK 0xF8 29 | #define GX_VAT_MASK 0x07 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXCull.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXCULL_H 2 | #define DOLPHIN_GXCULL_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void GXSetScissor(u32 left, u32 top, u32 wd, u32 ht); 11 | void GXSetCullMode(GXCullMode mode); 12 | void GXSetCoPlanar(GXBool enable); 13 | 14 | #ifdef __cplusplus 15 | } 16 | #endif 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXDispList.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXDISPLIST_H 2 | #define DOLPHIN_GXDISPLIST_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #ifdef AURORA 11 | #define GXCallDisplayListNative GXCallDisplayListLE 12 | #else 13 | #define GXCallDisplayListNative GXCallDisplayList 14 | #endif 15 | 16 | void GXBeginDisplayList(void* list, u32 size); 17 | u32 GXEndDisplayList(void); 18 | void GXCallDisplayListLE(const void* list, u32 nbytes); 19 | void GXCallDisplayList(const void* list, u32 nbytes); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXDraw.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXDRAW_H 2 | #define DOLPHIN_GXDRAW_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void GXDrawSphere(u8 numMajor, u8 numMinor); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXExtra.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXEXTRA_H 2 | #define DOLPHIN_GXEXTRA_H 3 | // Extra types for PC 4 | #ifdef TARGET_PC 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | typedef struct { 13 | float r; 14 | float g; 15 | float b; 16 | float a; 17 | } GXColorF32; 18 | 19 | typedef enum { 20 | GX_TF_R8_PC = 0x60, 21 | GX_TF_RGBA8_PC = 0x61, 22 | } GXPCTexFmt; 23 | 24 | void GXDestroyTexObj(GXTexObj* obj); 25 | void GXDestroyTlutObj(GXTlutObj* obj); 26 | 27 | void GXColor4f32(float r, float g, float b, float a); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | #endif 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXFifo.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXFIFO_H 2 | #define DOLPHIN_GXFIFO_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef struct { 11 | u8 pad[128]; 12 | } GXFifoObj; 13 | 14 | void GXInitFifoBase(GXFifoObj* fifo, void* base, u32 size); 15 | void GXInitFifoPtrs(GXFifoObj* fifo, void* readPtr, void* writePtr); 16 | void GXGetFifoPtrs(GXFifoObj* fifo, void** readPtr, void** writePtr); 17 | GXFifoObj* GXGetCPUFifo(void); 18 | GXFifoObj* GXGetGPFifo(void); 19 | void GXSetCPUFifo(GXFifoObj* fifo); 20 | void GXSetGPFifo(GXFifoObj* fifo); 21 | void GXSaveCPUFifo(GXFifoObj* fifo); 22 | void GXGetFifoStatus(GXFifoObj* fifo, GXBool* overhi, GXBool* underlow, u32* fifoCount, GXBool* cpu_write, 23 | GXBool* gp_read, GXBool* fifowrap); 24 | void GXGetGPStatus(GXBool* overhi, GXBool* underlow, GXBool* readIdle, GXBool* cmdIdle, GXBool* brkpt); 25 | void GXInitFifoLimits(GXFifoObj* fifo, u32 hiWaterMark, u32 loWaterMark); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXFrameBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXFRAMEBUFFER_H 2 | #define DOLPHIN_GXFRAMEBUFFER_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #define GX_MAX_Z24 0x00FFFFFF 12 | 13 | void GXSetCopyClear(GXColor clear_clr, u32 clear_z); 14 | void GXAdjustForOverscan(GXRenderModeObj* rmin, GXRenderModeObj* rmout, u16 hor, u16 ver); 15 | void GXCopyDisp(void* dest, GXBool clear); 16 | void GXSetDispCopyGamma(GXGamma gamma); 17 | void GXSetDispCopySrc(u16 left, u16 top, u16 wd, u16 ht); 18 | void GXSetDispCopyDst(u16 wd, u16 ht); 19 | u32 GXSetDispCopyYScale(f32 vscale); 20 | void GXSetCopyFilter(GXBool aa, u8 sample_pattern[12][2], GXBool vf, u8 vfilter[7]); 21 | void GXSetPixelFmt(GXPixelFmt pix_fmt, GXZFmt16 z_fmt); 22 | void GXSetTexCopySrc(u16 left, u16 top, u16 wd, u16 ht); 23 | void GXSetTexCopyDst(u16 wd, u16 ht, GXTexFmt fmt, GXBool mipmap); 24 | void GXCopyTex(void* dest, GXBool clear); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXGeometry.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXGEOMETRY_H 2 | #define DOLPHIN_GXGEOMETRY_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void GXSetVtxDesc(GXAttr attr, GXAttrType type); 11 | void GXSetVtxDescv(GXVtxDescList* list); 12 | void GXClearVtxDesc(void); 13 | void GXSetVtxAttrFmt(GXVtxFmt vtxfmt, GXAttr attr, GXCompCnt cnt, GXCompType type, u8 frac); 14 | void GXSetNumTexGens(u8 nTexGens); 15 | void GXBegin(GXPrimitive type, GXVtxFmt vtxfmt, u16 nverts); 16 | void GXSetTexCoordGen2(GXTexCoordID dst_coord, GXTexGenType func, GXTexGenSrc src_param, u32 mtx, GXBool normalize, 17 | u32 postmtx); 18 | void GXSetLineWidth(u8 width, GXTexOffset texOffsets); 19 | void GXSetPointSize(u8 pointSize, GXTexOffset texOffsets); 20 | void GXEnableTexOffsets(GXTexCoordID coord, GXBool line_enable, GXBool point_enable); 21 | #ifdef TARGET_PC 22 | void GXSetArray(GXAttr attr, const void* data, u32 size, u8 stride); 23 | #define GXSETARRAY(attr, data, size, stride) GXSetArray((attr), (data), (size), (stride)) 24 | #else 25 | void GXSetArray(GXAttr attr, const void* data, u8 stride); 26 | #define GXSETARRAY(attr, data, size, stride) GXSetArray((attr), (data), (stride)) 27 | #endif 28 | void GXInvalidateVtxCache(void); 29 | 30 | static inline void GXSetTexCoordGen(GXTexCoordID dst_coord, GXTexGenType func, GXTexGenSrc src_param, u32 mtx) { 31 | GXSetTexCoordGen2(dst_coord, func, src_param, mtx, GX_FALSE, GX_PTIDENTITY); 32 | } 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXGet.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXGET_H 2 | #define DOLPHIN_GXGET_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | GXBool GXGetTexObjMipMap(GXTexObj* tex_obj); 12 | GXTexFmt GXGetTexObjFmt(GXTexObj* tex_obj); 13 | u16 GXGetTexObjHeight(GXTexObj* tex_obj); 14 | u16 GXGetTexObjWidth(GXTexObj* tex_obj); 15 | GXTexWrapMode GXGetTexObjWrapS(GXTexObj* tex_obj); 16 | GXTexWrapMode GXGetTexObjWrapT(GXTexObj* tex_obj); 17 | void* GXGetTexObjData(GXTexObj* tex_obj); 18 | void GXGetProjectionv(f32* p); 19 | void GXGetLightPos(GXLightObj* lt_obj, f32* x, f32* y, f32* z); 20 | void GXGetLightColor(GXLightObj* lt_obj, GXColor* color); 21 | void GXGetVtxAttrFmt(GXVtxFmt idx, GXAttr attr, GXCompCnt* compCnt, GXCompType* compType, u8* shift); 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXLighting.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXLIGHTING_H 2 | #define DOLPHIN_GXLIGHTING_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | void GXSetNumChans(u8 nChans); 12 | void GXSetChanCtrl(GXChannelID chan, GXBool enable, GXColorSrc amb_src, GXColorSrc mat_src, u32 light_mask, 13 | GXDiffuseFn diff_fn, GXAttnFn attn_fn); 14 | void GXSetChanAmbColor(GXChannelID chan, GXColor amb_color); 15 | void GXSetChanMatColor(GXChannelID chan, GXColor mat_color); 16 | 17 | void GXInitLightSpot(GXLightObj* lt_obj, f32 cutoff, GXSpotFn spot_func); 18 | void GXInitLightDistAttn(GXLightObj* lt_obj, f32 ref_distance, f32 ref_brightness, GXDistAttnFn dist_func); 19 | void GXInitLightPos(GXLightObj* lt_obj, f32 x, f32 y, f32 z); 20 | void GXInitLightDir(GXLightObj* lt_obj, f32 nx, f32 ny, f32 nz); 21 | void GXInitSpecularDir(GXLightObj* lt_obj, f32 nx, f32 ny, f32 nz); 22 | void GXInitLightColor(GXLightObj* lt_obj, GXColor color); 23 | void GXInitLightAttn(GXLightObj* lt_obj, f32 a0, f32 a1, f32 a2, f32 k0, f32 k1, f32 k2); 24 | void GXInitLightAttnA(GXLightObj* lt_obj, f32 a0, f32 a1, f32 a2); 25 | void GXInitLightAttnK(GXLightObj* lt_obj, f32 k0, f32 k1, f32 k2); 26 | void GXLoadLightObjImm(GXLightObj* lt_obj, GXLightID light); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXManage.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXMANAGE_H 2 | #define DOLPHIN_GXMANAGE_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef void (*GXDrawDoneCallback)(void); 11 | 12 | GXFifoObj* GXInit(void* base, u32 size); 13 | GXDrawDoneCallback GXSetDrawDoneCallback(GXDrawDoneCallback cb); 14 | void GXDrawDone(void); 15 | void GXSetDrawDone(void); 16 | void GXFlush(void); 17 | void GXPixModeSync(void); 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXPerf.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXPERF_H 2 | #define DOLPHIN_GXPERF_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void GXReadXfRasMetric(u32* xf_wait_in, u32* xf_wait_out, u32* ras_busy, u32* clocks); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXPixel.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXPIXEL_H 2 | #define DOLPHIN_GXPIXEL_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void GXSetFog(GXFogType type, f32 startz, f32 endz, f32 nearz, f32 farz, GXColor color); 11 | void GXSetFogColor(GXColor color); 12 | // ? GXSetFogRangeAdj(); 13 | void GXSetBlendMode(GXBlendMode type, GXBlendFactor src_factor, GXBlendFactor dst_factor, GXLogicOp op); 14 | void GXSetColorUpdate(GXBool update_enable); 15 | void GXSetAlphaUpdate(GXBool update_enable); 16 | void GXSetZMode(GXBool compare_enable, GXCompare func, GXBool update_enable); 17 | void GXSetZCompLoc(GXBool before_tex); 18 | void GXSetPixelFmt(GXPixelFmt pix_fmt, GXZFmt16 z_fmt); 19 | void GXSetDither(GXBool dither); 20 | void GXSetDstAlpha(GXBool enable, u8 alpha); 21 | // ? GXSetFieldMask(); 22 | // ? GXSetFieldMode(); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXStruct.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXSTRUCT_H 2 | #define DOLPHIN_GXSTRUCT_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #define VI_TVMODE(format, interlace) (((format) << 2) + (interlace)) 12 | 13 | #define VI_INTERLACE 0 14 | #define VI_NON_INTERLACE 1 15 | #define VI_PROGRESSIVE 2 16 | 17 | #define VI_NTSC 0 18 | #define VI_PAL 1 19 | #define VI_MPAL 2 20 | #define VI_DEBUG 3 21 | #define VI_DEBUG_PAL 4 22 | #define VI_EURGB60 5 23 | 24 | typedef enum { 25 | VI_TVMODE_NTSC_INT = VI_TVMODE(VI_NTSC, VI_INTERLACE), 26 | VI_TVMODE_NTSC_DS = VI_TVMODE(VI_NTSC, VI_NON_INTERLACE), 27 | VI_TVMODE_NTSC_PROG = VI_TVMODE(VI_NTSC, VI_PROGRESSIVE), 28 | VI_TVMODE_PAL_INT = VI_TVMODE(VI_PAL, VI_INTERLACE), 29 | VI_TVMODE_PAL_DS = VI_TVMODE(VI_PAL, VI_NON_INTERLACE), 30 | VI_TVMODE_EURGB60_INT = VI_TVMODE(VI_EURGB60, VI_INTERLACE), 31 | VI_TVMODE_EURGB60_DS = VI_TVMODE(VI_EURGB60, VI_NON_INTERLACE), 32 | VI_TVMODE_MPAL_INT = VI_TVMODE(VI_MPAL, VI_INTERLACE), 33 | VI_TVMODE_MPAL_DS = VI_TVMODE(VI_MPAL, VI_NON_INTERLACE), 34 | VI_TVMODE_DEBUG_INT = VI_TVMODE(VI_DEBUG, VI_INTERLACE), 35 | VI_TVMODE_DEBUG_PAL_INT = VI_TVMODE(VI_DEBUG_PAL, VI_INTERLACE), 36 | VI_TVMODE_DEBUG_PAL_DS = VI_TVMODE(VI_DEBUG_PAL, VI_NON_INTERLACE) 37 | } VITVMode; 38 | 39 | typedef enum { VI_XFBMODE_SF = 0, VI_XFBMODE_DF } VIXFBMode; 40 | 41 | typedef struct { 42 | /*0x00*/ VITVMode viTVmode; 43 | /*0x04*/ u16 fbWidth; 44 | /*0x06*/ u16 efbHeight; 45 | /*0x08*/ u16 xfbHeight; 46 | /*0x0A*/ u16 viXOrigin; 47 | /*0x0C*/ u16 viYOrigin; 48 | /*0x0E*/ u16 viWidth; 49 | /*0x10*/ u16 viHeight; 50 | /*0x14*/ VIXFBMode xFBmode; 51 | /*0x18*/ u8 field_rendering; 52 | u8 aa; 53 | u8 sample_pattern[12][2]; 54 | u8 vfilter[7]; 55 | } GXRenderModeObj; 56 | 57 | typedef struct { 58 | u8 r; 59 | u8 g; 60 | u8 b; 61 | u8 a; 62 | } GXColor; 63 | 64 | typedef struct { 65 | #ifdef TARGET_PC 66 | u32 dummy[22]; 67 | #else 68 | u32 dummy[8]; 69 | #endif 70 | } GXTexObj; 71 | 72 | typedef struct { 73 | #ifdef TARGET_PC 74 | u32 dummy[4]; 75 | #else 76 | u32 dummy[3]; 77 | #endif 78 | } GXTlutObj; 79 | 80 | typedef struct { 81 | u32 dummy[16]; 82 | } GXLightObj; 83 | 84 | typedef struct { 85 | GXAttr attr; 86 | GXAttrType type; 87 | } GXVtxDescList; 88 | 89 | typedef struct { 90 | s16 r; 91 | s16 g; 92 | s16 b; 93 | s16 a; 94 | } GXColorS10; 95 | 96 | typedef struct _GXTexRegion { 97 | u32 dummy[4]; 98 | } GXTexRegion; 99 | 100 | typedef struct _GXTlutRegion { 101 | u32 dummy[4]; 102 | } GXTlutRegion; 103 | 104 | #ifdef __cplusplus 105 | } 106 | #endif 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXTev.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXTEV_H 2 | #define DOLPHIN_GXTEV_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | void GXSetTevOp(GXTevStageID id, GXTevMode mode); 12 | void GXSetTevColorIn(GXTevStageID stage, GXTevColorArg a, GXTevColorArg b, GXTevColorArg c, GXTevColorArg d); 13 | void GXSetTevAlphaIn(GXTevStageID stage, GXTevAlphaArg a, GXTevAlphaArg b, GXTevAlphaArg c, GXTevAlphaArg d); 14 | void GXSetTevColorOp(GXTevStageID stage, GXTevOp op, GXTevBias bias, GXTevScale scale, GXBool clamp, 15 | GXTevRegID out_reg); 16 | void GXSetTevAlphaOp(GXTevStageID stage, GXTevOp op, GXTevBias bias, GXTevScale scale, GXBool clamp, 17 | GXTevRegID out_reg); 18 | void GXSetTevColor(GXTevRegID id, GXColor color); 19 | void GXSetTevColorS10(GXTevRegID id, GXColorS10 color); 20 | void GXSetTevKColor(GXTevKColorID id, GXColor color); 21 | void GXSetTevKColorSel(GXTevStageID stage, GXTevKColorSel sel); 22 | void GXSetTevKAlphaSel(GXTevStageID stage, GXTevKAlphaSel sel); 23 | void GXSetTevSwapMode(GXTevStageID stage, GXTevSwapSel ras_sel, GXTevSwapSel tex_sel); 24 | void GXSetTevSwapModeTable(GXTevSwapSel table, GXTevColorChan red, GXTevColorChan green, GXTevColorChan blue, 25 | GXTevColorChan alpha); 26 | void GXSetAlphaCompare(GXCompare comp0, u8 ref0, GXAlphaOp op, GXCompare comp1, u8 ref1); 27 | void GXSetZTexture(GXZTexOp op, GXTexFmt fmt, u32 bias); 28 | void GXSetTevOrder(GXTevStageID stage, GXTexCoordID coord, GXTexMapID map, GXChannelID color); 29 | void GXSetNumTevStages(u8 nStages); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXTexture.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXTEXTURE_H 2 | #define DOLPHIN_GXTEXTURE_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef GXTexRegion* (*GXTexRegionCallback)(const GXTexObj* obj, GXTexMapID id); 12 | 13 | void GXInitTexObj(GXTexObj* obj, const void* data, u16 width, u16 height, u32 format, GXTexWrapMode wrapS, 14 | GXTexWrapMode wrapT, GXBool mipmap); 15 | void GXInitTexObjCI(GXTexObj* obj, const void* data, u16 width, u16 height, GXCITexFmt format, GXTexWrapMode wrapS, 16 | GXTexWrapMode wrapT, GXBool mipmap, u32 tlut); 17 | void GXInitTexObjData(GXTexObj* obj, const void* data); 18 | void GXInitTexObjLOD(GXTexObj* obj, GXTexFilter min_filt, GXTexFilter mag_filt, f32 min_lod, f32 max_lod, f32 lod_bias, 19 | GXBool bias_clamp, GXBool do_edge_lod, GXAnisotropy max_aniso); 20 | void GXLoadTexObj(GXTexObj* obj, GXTexMapID id); 21 | u32 GXGetTexBufferSize(u16 width, u16 height, u32 format, GXBool mipmap, u8 max_lod); 22 | void GXInvalidateTexAll(); 23 | void GXInitTexObjWrapMode(GXTexObj* obj, GXTexWrapMode s, GXTexWrapMode t); 24 | void GXInitTlutObj(GXTlutObj* obj, const void* data, GXTlutFmt format, u16 entries); 25 | void GXLoadTlut(const GXTlutObj* obj, GXTlut idx); 26 | void GXSetTexCoordScaleManually(GXTexCoordID coord, GXBool enable, u16 ss, u16 ts); 27 | void GXInitTexCacheRegion(GXTexRegion* region, GXBool is_32b_mipmap, u32 tmem_even, GXTexCacheSize size_even, 28 | u32 tmem_odd, GXTexCacheSize size_odd); 29 | GXTexRegionCallback GXSetTexRegionCallback(GXTexRegionCallback callback); 30 | void GXInvalidateTexRegion(const GXTexRegion* region); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXTransform.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXTRANSFORM_H 2 | #define DOLPHIN_GXTRANSFORM_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #define GX_PROJECTION_SZ 7 11 | 12 | #ifdef TARGET_PC 13 | void GXSetProjection(const void* mtx, GXProjectionType type); 14 | void GXLoadPosMtxImm(const void* mtx, u32 id); 15 | void GXLoadNrmMtxImm(const void* mtx, u32 id); 16 | void GXLoadTexMtxImm(const void* mtx, u32 id, GXTexMtxType type); 17 | #else 18 | void GXSetProjection(f32 mtx[4][4], GXProjectionType type); 19 | void GXLoadPosMtxImm(f32 mtx[3][4], u32 id); 20 | void GXLoadNrmMtxImm(f32 mtx[3][4], u32 id); 21 | void GXLoadTexMtxImm(f32 mtx[][4], u32 id, GXTexMtxType type); 22 | #endif 23 | void GXSetViewport(f32 left, f32 top, f32 wd, f32 ht, f32 nearz, f32 farz); 24 | void GXSetCurrentMtx(u32 id); 25 | void GXSetViewportJitter(f32 left, f32 top, f32 wd, f32 ht, f32 nearz, f32 farz, u32 field); 26 | void GXSetScissorBoxOffset(s32 x_off, s32 y_off); 27 | void GXSetClipMode(GXClipMode mode); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/dolphin/gx/GXVert.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_GXVERT_H 2 | #define DOLPHIN_GXVERT_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #define GXFIFO_ADDR 0xCC008000 11 | 12 | typedef union { 13 | u8 u8; 14 | u16 u16; 15 | u32 u32; 16 | u64 u64; 17 | s8 s8; 18 | s16 s16; 19 | s32 s32; 20 | s64 s64; 21 | f32 f32; 22 | f64 f64; 23 | } PPCWGPipe; 24 | 25 | #ifdef __MWERKS__ 26 | volatile PPCWGPipe GXWGFifo : GXFIFO_ADDR; 27 | #else 28 | #define GXWGFifo (*(volatile PPCWGPipe*)GXFIFO_ADDR) 29 | #endif 30 | 31 | #ifdef TARGET_PC 32 | 33 | void GXPosition3f32(f32 x, f32 y, f32 z); 34 | void GXPosition3u16(u16 x, u16 y, u16 z); 35 | void GXPosition3s16(s16 x, s16 y, s16 z); 36 | void GXPosition3u8(u8 x, u8 y, u8 z); 37 | void GXPosition3s8(s8 x, s8 y, s8 z); 38 | 39 | void GXPosition2f32(f32 x, f32 y); 40 | void GXPosition2u16(u16 x, u16 y); 41 | void GXPosition2s16(s16 x, s16 y); 42 | void GXPosition2u8(u8 x, u8 y); 43 | void GXPosition2s8(s8 x, s8 y); 44 | 45 | void GXPosition1x16(u16 index); 46 | void GXPosition1x8(u8 index); 47 | 48 | void GXNormal3f32(f32 x, f32 y, f32 z); 49 | void GXNormal3s16(s16 x, s16 y, s16 z); 50 | void GXNormal3s8(s8 x, s8 y, s8 z); 51 | 52 | void GXNormal1x16(u16 index); 53 | void GXNormal1x8(u8 index); 54 | 55 | void GXColor4u8(u8 r, u8 g, u8 b, u8 a); 56 | 57 | void GXColor3u8(u8 r, u8 g, u8 b); 58 | 59 | void GXColor1u32(u32 clr); 60 | void GXColor1u16(u16 clr); 61 | 62 | void GXColor1x16(u16 index); 63 | void GXColor1x8(u8 index); 64 | 65 | void GXTexCoord2f32(f32 s, f32 t); 66 | void GXTexCoord2u16(u16 s, u16 t); 67 | void GXTexCoord2s16(s16 s, s16 t); 68 | void GXTexCoord2u8(u8 s, u8 t); 69 | void GXTexCoord2s8(s8 s, s8 t); 70 | 71 | void GXTexCoord1f32(f32 s); 72 | void GXTexCoord1u16(u16 s); 73 | void GXTexCoord1s16(s16 s); 74 | void GXTexCoord1u8(u8 s); 75 | void GXTexCoord1s8(s8 s); 76 | 77 | void GXTexCoord1x16(u16 index); 78 | void GXTexCoord1x8(u8 index); 79 | 80 | extern void GXEnd(void); 81 | 82 | #else 83 | 84 | static inline void GXPosition2f32(const f32 x, const f32 y) { 85 | GXWGFifo.f32 = x; 86 | GXWGFifo.f32 = y; 87 | } 88 | 89 | static inline void GXPosition3s16(const s16 x, const s16 y, const s16 z) { 90 | GXWGFifo.s16 = x; 91 | GXWGFifo.s16 = y; 92 | GXWGFifo.s16 = z; 93 | } 94 | 95 | static inline void GXPosition3f32(const f32 x, const f32 y, const f32 z) { 96 | GXWGFifo.f32 = x; 97 | GXWGFifo.f32 = y; 98 | GXWGFifo.f32 = z; 99 | } 100 | 101 | static inline void GXNormal3f32(const f32 x, const f32 y, const f32 z) { 102 | GXWGFifo.f32 = x; 103 | GXWGFifo.f32 = y; 104 | GXWGFifo.f32 = z; 105 | } 106 | 107 | static inline void GXColor4u8(const u8 r, const u8 g, const u8 b, const u8 a) { 108 | GXWGFifo.u8 = r; 109 | GXWGFifo.u8 = g; 110 | GXWGFifo.u8 = b; 111 | GXWGFifo.u8 = a; 112 | } 113 | 114 | static inline void GXTexCoord2s16(const s16 u, const s16 v) { 115 | GXWGFifo.s16 = u; 116 | GXWGFifo.s16 = v; 117 | } 118 | 119 | static inline void GXTexCoord2f32(const f32 u, const f32 v) { 120 | GXWGFifo.f32 = u; 121 | GXWGFifo.f32 = v; 122 | } 123 | 124 | static inline void GXEnd(void) {} 125 | 126 | #endif 127 | 128 | #ifdef __cplusplus 129 | } 130 | #endif 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /include/dolphin/mtx/GeoTypes.h: -------------------------------------------------------------------------------- 1 | #ifndef _DOLPHIN_GEOTYPES 2 | #define _DOLPHIN_GEOTYPES 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef struct { 11 | f32 x, y, z; 12 | } Vec, *VecPtr, Point3d, *Point3dPtr; 13 | 14 | typedef struct { 15 | s16 x; 16 | s16 y; 17 | s16 z; 18 | } S16Vec, *S16VecPtr; 19 | 20 | typedef struct { 21 | f32 x, y, z, w; 22 | } Quaternion, *QuaternionPtr, Qtrn, *QtrnPtr; 23 | 24 | typedef f32 Mtx[3][4]; 25 | 26 | typedef f32 (*MtxPtr)[4]; 27 | 28 | typedef f32 ROMtx[4][3]; 29 | 30 | typedef f32 (*ROMtxPtr)[3]; 31 | 32 | typedef f32 Mtx44[4][4]; 33 | 34 | typedef f32 (*Mtx44Ptr)[4]; 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif // _DOLPHIN_GEOTYPES 41 | -------------------------------------------------------------------------------- /include/dolphin/mtx/mtx44ext.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef _DOLPHIN_MTX44EXT 4 | #define _DOLPHIN_MTX44EXT 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void C_MTX44Identity(Mtx44 m); 11 | void C_MTX44Copy(const Mtx44 src, Mtx44 dst); 12 | void C_MTX44Concat(const Mtx44 a, const Mtx44 b, Mtx44 ab); 13 | void C_MTX44Transpose(const Mtx44 src, Mtx44 xPose); 14 | u32 C_MTX44Inverse(const Mtx44 src, Mtx44 inv); 15 | 16 | #ifdef GEKKO 17 | void PSMTX44Identity(Mtx44 m); 18 | void PSMTX44Copy(const Mtx44 src, Mtx44 dst); 19 | void PSMTX44Concat(const Mtx44 a, const Mtx44 b, Mtx44 ab); 20 | void PSMTX44Transpose(const Mtx44 src, Mtx44 xPose); 21 | #endif 22 | 23 | #ifdef MTX_USE_PS 24 | #define MTX44Identity PSMTX44Identity 25 | #define MTX44Copy PSMTX44Copy 26 | #define MTX44Concat PSMTX44Concat 27 | #define MTX44Transpose PSMTX44Transpose 28 | #else 29 | #define MTX44Identity C_MTX44Identity 30 | #define MTX44Copy C_MTX44Copy 31 | #define MTX44Concat C_MTX44Concat 32 | #define MTX44Transpose C_MTX44Transpose 33 | #endif 34 | 35 | #define MTX44Inverse C_MTX44Inverse 36 | 37 | void C_MTX44Trans(Mtx44 m, f32 xT, f32 yT, f32 zT); 38 | void C_MTX44TransApply(const Mtx44 src, Mtx44 dst, f32 xT, f32 yT, f32 zT); 39 | void C_MTX44Scale(Mtx44 m, f32 xS, f32 yS, f32 zS); 40 | void C_MTX44ScaleApply(const Mtx44 src, Mtx44 dst, f32 xS, f32 yS, f32 zS); 41 | 42 | void C_MTX44RotRad(Mtx44 m, char axis, f32 rad); 43 | void C_MTX44RotTrig(Mtx44 m, char axis, f32 sinA, f32 cosA); 44 | void C_MTX44RotAxisRad(Mtx44 m, const Vec* axis, f32 rad); 45 | 46 | #ifdef GEKKO 47 | void PSMTX44Trans(Mtx44 m, f32 xT, f32 yT, f32 zT); 48 | void PSMTX44TransApply(const Mtx44 src, Mtx44 dst, f32 xT, f32 yT, f32 zT); 49 | void PSMTX44Scale(Mtx44 m, f32 xS, f32 yS, f32 zS); 50 | void PSMTX44ScaleApply(const Mtx44 src, Mtx44 dst, f32 xS, f32 yS, f32 zS); 51 | 52 | void PSMTX44RotRad(Mtx44 m, char axis, f32 rad); 53 | void PSMTX44RotTrig(Mtx44 m, char axis, f32 sinA, f32 cosA); 54 | void PSMTX44RotAxisRad(Mtx44 m, const Vec* axis, f32 rad); 55 | #endif 56 | 57 | #ifdef MTX_USE_PS 58 | #define MTX44Trans PSMTX44Trans 59 | #define MTX44TransApply PSMTX44TransApply 60 | #define MTX44Scale PSMTX44Scale 61 | #define MTX44ScaleApply PSMTX44ScaleApply 62 | 63 | #define MTX44RotRad PSMTX44RotRad 64 | #define MTX44RotTrig PSMTX44RotTrig 65 | #define MTX44RotAxisRad PSMTX44RotAxisRad 66 | 67 | #else 68 | #define MTX44Trans C_MTX44Trans 69 | #define MTX44TransApply C_MTX44TransApply 70 | #define MTX44Scale C_MTX44Scale 71 | #define MTX44ScaleApply C_MTX44ScaleApply 72 | 73 | #define MTX44RotRad C_MTX44RotRad 74 | #define MTX44RotTrig C_MTX44RotTrig 75 | #define MTX44RotAxisRad C_MTX44RotAxisRad 76 | #endif 77 | 78 | void C_MTX44MultVec(const Mtx44 m, const Vec* src, Vec* dst); 79 | void C_MTX44MultVecArray(const Mtx44 m, const Vec* srcBase, Vec* dstBase, u32 count); 80 | void C_MTX44MultVecSR(const Mtx44 m, const Vec* src, Vec* dst); 81 | void C_MTX44MultVecArraySR(const Mtx44 m, const Vec* srcBase, Vec* dstBase, u32 count); 82 | 83 | #ifdef GEKKO 84 | void PSMTX44MultVec(const Mtx44 m, const Vec* src, Vec* dst); 85 | void PSMTX44MultVecArray(const Mtx44 m, const Vec* srcBase, Vec* dstBase, u32 count); 86 | void PSMTX44MultVecSR(const Mtx44 m, const Vec* src, Vec* dst); 87 | void PSMTX44MultVecArraySR(const Mtx44 m, const Vec* srcBase, Vec* dstBase, u32 count); 88 | #endif 89 | 90 | #ifdef MTX_USE_PS 91 | #define MTX44MultVec PSMTX44MultVec 92 | #define MTX44MultVecArray PSMTX44MultVecArray 93 | #define MTX44MultVecSR PSMTX44MultVecSR 94 | #define MTX44MultVecArraySR PSMTX44MultVecArraySR 95 | #else 96 | #define MTX44MultVec C_MTX44MultVec 97 | #define MTX44MultVecArray C_MTX44MultVecArray 98 | #define MTX44MultVecSR C_MTX44MultVecSR 99 | #define MTX44MultVecArraySR C_MTX44MultVecArraySR 100 | #endif 101 | 102 | #ifdef __cplusplus 103 | } 104 | #endif 105 | 106 | #endif // _DOLPHIN_MTX44EXT 107 | -------------------------------------------------------------------------------- /include/dolphin/pad.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_PAD_H 2 | #define DOLPHIN_PAD_H 3 | 4 | #include 5 | 6 | #define PAD_CHAN0 0 7 | #define PAD_CHAN1 1 8 | #define PAD_CHAN2 2 9 | #define PAD_CHAN3 3 10 | #define PAD_CHANMAX 4 11 | 12 | #define PAD_MOTOR_STOP 0 13 | #define PAD_MOTOR_RUMBLE 1 14 | #define PAD_MOTOR_STOP_HARD 2 15 | 16 | #define PAD_ERR_NONE 0 17 | #define PAD_ERR_NO_CONTROLLER -1 18 | #define PAD_ERR_NOT_READY -2 19 | #define PAD_ERR_TRANSFER -3 20 | 21 | #define PAD_BUTTON_LEFT 0x0001 22 | #define PAD_BUTTON_RIGHT 0x0002 23 | #define PAD_BUTTON_DOWN 0x0004 24 | #define PAD_BUTTON_UP 0x0008 25 | #define PAD_TRIGGER_Z 0x0010 26 | #define PAD_TRIGGER_R 0x0020 27 | #define PAD_TRIGGER_L 0x0040 28 | #define PAD_BUTTON_A 0x0100 29 | #define PAD_BUTTON_B 0x0200 30 | #define PAD_BUTTON_X 0x0400 31 | #define PAD_BUTTON_Y 0x0800 32 | #define PAD_BUTTON_MENU 0x1000 33 | #define PAD_BUTTON_START 0x1000 34 | 35 | #define PAD_CHAN0_BIT 0x80000000 36 | #define PAD_CHAN1_BIT 0x40000000 37 | #define PAD_CHAN2_BIT 0x20000000 38 | #define PAD_CHAN3_BIT 0x10000000 39 | 40 | #define PADButtonDown(buttonLast, button) (((buttonLast) ^ (button)) & (button)) 41 | #define PADButtonUp(buttonLast, button) (((buttonLast) ^ (button)) & (buttonLast)) 42 | 43 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | 47 | typedef struct PADStatus { 48 | u16 button; 49 | s8 stickX; 50 | s8 stickY; 51 | s8 substickX; 52 | s8 substickY; 53 | u8 triggerL; 54 | u8 triggerR; 55 | u8 analogA; 56 | u8 analogB; 57 | s8 err; 58 | } PADStatus; 59 | 60 | BOOL PADInit(); 61 | u32 PADRead(PADStatus* status); 62 | BOOL PADReset(u32 mask); 63 | BOOL PADRecalibrate(u32 mask); 64 | void PADClamp(PADStatus* status); 65 | void PADClampCircle(PADStatus* status); 66 | void PADControlMotor(s32 chan, u32 cmd); 67 | void PADSetSpec(u32 spec); 68 | void PADControlAllMotors(const u32* cmdArr); 69 | void PADSetAnalogMode(u32 mode); 70 | 71 | #ifdef TARGET_PC 72 | /* New API to facilitate controller interactions */ 73 | typedef struct PADDeadZones { 74 | bool emulateTriggers; 75 | bool useDeadzones; 76 | u16 stickDeadZone; 77 | u16 substickDeadZone; 78 | u16 leftTriggerActivationZone; 79 | u16 rightTriggerActivationZone; 80 | } PADDeadZones; 81 | 82 | typedef u16 PADButton; 83 | 84 | typedef struct PADButtonMapping { 85 | u32 nativeButton; 86 | PADButton padButton; 87 | } PADButtonMapping; 88 | 89 | /* Returns the total number of controllers */ 90 | u32 PADCount(); 91 | /* Returns the controller name for the given index into the controller map */ 92 | const char* PADGetNameForControllerIndex(u32 idx); 93 | void PADSetPortForIndex(u32 index, s32 port); 94 | s32 PADGetIndexForPort(u32 port); 95 | void PADGetVidPid(u32 port, u32* vid, u32* pid); 96 | void PADClearPort(u32 port); 97 | const char* PADGetName(u32 port); 98 | void PADSetButtonMapping(u32 port, PADButtonMapping mapping); 99 | void PADSetAllButtonMappings(u32 port, PADButtonMapping buttons[12]); 100 | PADButtonMapping* PADGetButtonMappings(u32 port, u32* buttonCount); 101 | void PADSerializeMappings(); 102 | PADDeadZones* PADGetDeadZones(u32 port); 103 | const char* PADGetButtonName(PADButton); 104 | const char* PADGetNativeButtonName(u32 button); 105 | /* Returns any pressed native button */ 106 | s32 PADGetNativeButtonPressed(u32 port); 107 | void PADRestoreDefaultMapping(u32 port); 108 | void PADBlockInput(bool block); 109 | #endif 110 | 111 | #ifdef __cplusplus 112 | } 113 | #endif 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /include/dolphin/si.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_SI_H 2 | #define DOLPHIN_SI_H 3 | 4 | #include 5 | 6 | #define SI_CHAN0 0 7 | #define SI_CHAN1 1 8 | #define SI_CHAN2 2 9 | #define SI_CHAN3 3 10 | #define SI_MAX_CHAN 4 11 | 12 | #define SI_CHAN0_BIT 0x80000000 13 | #define SI_CHAN1_BIT 0x40000000 14 | #define SI_CHAN2_BIT 0x20000000 15 | #define SI_CHAN3_BIT 0x10000000 16 | #define SI_CHAN_BIT(chn) (SI_CHAN0_BIT >> (chn)) 17 | 18 | #define SI_ERROR_UNDER_RUN 0x0001 19 | #define SI_ERROR_OVER_RUN 0x0002 20 | #define SI_ERROR_COLLISION 0x0004 21 | #define SI_ERROR_NO_RESPONSE 0x0008 22 | #define SI_ERROR_WRST 0x0010 23 | #define SI_ERROR_RDST 0x0020 24 | #define SI_ERROR_UNKNOWN 0x0040 25 | #define SI_ERROR_BUSY 0x0080 26 | 27 | #define SI_TYPE_MASK 0x18000000u 28 | #define SI_TYPE_N64 0x00000000u 29 | #define SI_TYPE_DOLPHIN 0x08000000u 30 | #define SI_TYPE_GC SI_TYPE_DOLPHIN 31 | 32 | // GameCube specific 33 | #define SI_GC_WIRELESS 0x80000000u 34 | #define SI_GC_NOMOTOR 0x20000000u // no rumble motor 35 | #define SI_GC_STANDARD 0x01000000u // dolphin standard controller 36 | 37 | // WaveBird specific 38 | #define SI_WIRELESS_RECEIVED 0x40000000u // 0: no wireless unit 39 | #define SI_WIRELESS_IR 0x04000000u // 0: IR 1: RF 40 | #define SI_WIRELESS_STATE 0x02000000u // 0: variable 1: fixed 41 | #define SI_WIRELESS_ORIGIN 0x00200000u // 0: invalid 1: valid 42 | #define SI_WIRELESS_FIX_ID 0x00100000u // 0: not fixed 1: fixed 43 | #define SI_WIRELESS_TYPE 0x000f0000u 44 | #define SI_WIRELESS_LITE_MASK 0x000c0000u // 0: normal 1: lite controller 45 | #define SI_WIRELESS_LITE 0x00040000u // 0: normal 1: lite controller 46 | #define SI_WIRELESS_CONT_MASK 0x00080000u // 0: non-controller 1: non-controller 47 | #define SI_WIRELESS_CONT 0x00000000u 48 | #define SI_WIRELESS_ID 0x00c0ff00u 49 | #define SI_WIRELESS_TYPE_ID (SI_WIRELESS_TYPE | SI_WIRELESS_ID) 50 | 51 | #define SI_N64_CONTROLLER (SI_TYPE_N64 | 0x05000000) 52 | #define SI_N64_MIC (SI_TYPE_N64 | 0x00010000) 53 | #define SI_N64_KEYBOARD (SI_TYPE_N64 | 0x00020000) 54 | #define SI_N64_MOUSE (SI_TYPE_N64 | 0x02000000) 55 | #define SI_GBA (SI_TYPE_N64 | 0x00040000) 56 | #define SI_GC_CONTROLLER (SI_TYPE_GC | SI_GC_STANDARD) 57 | #define SI_GC_RECEIVER (SI_TYPE_GC | SI_GC_WIRELESS) 58 | #define SI_GC_WAVEBIRD (SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_STANDARD | SI_WIRELESS_STATE | SI_WIRELESS_FIX_ID) 59 | #define SI_GC_KEYBOARD (SI_TYPE_GC | 0x00200000) 60 | #define SI_GC_STEERING (SI_TYPE_GC | 0x00000000) 61 | 62 | #ifdef __cplusplus 63 | extern "C" { 64 | #endif 65 | 66 | u32 SIProbe(s32 chan); 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /include/dolphin/types.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_TYPES_H 2 | #define DOLPHIN_TYPES_H 3 | 4 | #ifdef TARGET_PC 5 | #include 6 | typedef int8_t s8; 7 | typedef int16_t s16; 8 | typedef int32_t s32; 9 | typedef int64_t s64; 10 | typedef uint8_t u8; 11 | typedef uint16_t u16; 12 | typedef uint32_t u32; 13 | typedef uint64_t u64; 14 | #else 15 | typedef signed char s8; 16 | typedef signed short int s16; 17 | typedef signed long s32; 18 | typedef signed long long int s64; 19 | typedef unsigned char u8; 20 | typedef unsigned short int u16; 21 | typedef unsigned long u32; 22 | typedef unsigned long long int u64; 23 | #endif 24 | 25 | typedef volatile u8 vu8; 26 | typedef volatile u16 vu16; 27 | typedef volatile u32 vu32; 28 | typedef volatile u64 vu64; 29 | 30 | typedef volatile s8 vs8; 31 | typedef volatile s16 vs16; 32 | typedef volatile s32 vs32; 33 | typedef volatile s64 vs64; 34 | 35 | typedef float f32; 36 | typedef double f64; 37 | 38 | typedef volatile f32 vf32; 39 | typedef volatile f64 vf64; 40 | 41 | #if defined(TARGET_PC) && !defined(_WIN32) 42 | #include 43 | typedef bool BOOL; 44 | #ifndef FALSE 45 | #define FALSE false 46 | #endif 47 | #ifndef TRUE 48 | #define TRUE true 49 | #endif 50 | #else 51 | typedef int BOOL; 52 | #ifndef FALSE 53 | #define FALSE 0 54 | #endif 55 | #ifndef TRUE 56 | #define TRUE 1 57 | #endif 58 | #endif 59 | 60 | #ifdef TARGET_PC 61 | #include 62 | #else 63 | #ifndef NULL 64 | #define NULL 0 65 | #endif 66 | #endif 67 | #ifndef __cplusplus 68 | #ifndef nullptr 69 | #define nullptr NULL 70 | #endif 71 | #endif 72 | 73 | #if defined(__MWERKS__) 74 | #define AT_ADDRESS(addr) : (addr) 75 | #define ATTRIBUTE_ALIGN(num) __attribute__((aligned(num))) 76 | #elif defined(__GNUC__) 77 | #define AT_ADDRESS(addr) 78 | #define ATTRIBUTE_ALIGN(num) __attribute__((aligned(num))) 79 | #elif defined(_MSC_VER) 80 | #define AT_ADDRESS(addr) 81 | #define ATTRIBUTE_ALIGN(num) 82 | #else 83 | #error unknown compiler 84 | #endif 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /include/dolphin/vi.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_VI_H 2 | #define DOLPHIN_VI_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | void VIInit(void); 12 | void VIConfigure(GXRenderModeObj *rm); 13 | void VIFlush(void); 14 | u32 VIGetTvFormat(void); 15 | void VISetNextFrameBuffer(void *fb); 16 | void VIWaitForRetrace(void); 17 | void VISetBlack(BOOL black); 18 | 19 | #ifdef TARGET_PC 20 | void VISetWindowTitle(const char* title); 21 | void VISetWindowFullscreen(bool fullscreen); 22 | bool VIGetWindowFullscreen(); 23 | #endif 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/dolphin/vifuncs.h: -------------------------------------------------------------------------------- 1 | #ifndef DOLPHIN_VIFUNCS_H 2 | #define DOLPHIN_VIFUNCS_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | u32 VIGetNextField(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /lib/aurora.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gfx/common.hpp" 4 | #include "imgui.hpp" 5 | #include "input.hpp" 6 | #include "internal.hpp" 7 | #include "webgpu/gpu.hpp" 8 | #include "window.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace aurora { 15 | AuroraConfig g_config; 16 | 17 | namespace { 18 | Module Log("aurora"); 19 | 20 | // GPU 21 | using webgpu::g_device; 22 | using webgpu::g_queue; 23 | using webgpu::g_surface; 24 | 25 | constexpr std::array PreferredBackendOrder{ 26 | #ifdef ENABLE_BACKEND_WEBGPU 27 | BACKEND_WEBGPU, 28 | #endif 29 | #ifdef DAWN_ENABLE_BACKEND_D3D12 30 | BACKEND_D3D12, 31 | #endif 32 | #ifdef DAWN_ENABLE_BACKEND_METAL 33 | BACKEND_METAL, 34 | #endif 35 | #ifdef DAWN_ENABLE_BACKEND_VULKAN 36 | BACKEND_VULKAN, 37 | #endif 38 | #ifdef DAWN_ENABLE_BACKEND_D3D11 39 | BACKEND_D3D11, 40 | #endif 41 | // #ifdef DAWN_ENABLE_BACKEND_DESKTOP_GL 42 | // BACKEND_OPENGL, 43 | // #endif 44 | // #ifdef DAWN_ENABLE_BACKEND_OPENGLES 45 | // BACKEND_OPENGLES, 46 | // #endif 47 | #ifdef DAWN_ENABLE_BACKEND_NULL 48 | BACKEND_NULL, 49 | #endif 50 | }; 51 | 52 | bool g_initialFrame = false; 53 | 54 | AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexcept { 55 | g_config = config; 56 | Log.info("Aurora initializing"); 57 | if (g_config.appName == nullptr) { 58 | g_config.appName = "Aurora"; 59 | } 60 | if (g_config.configPath == nullptr) { 61 | g_config.configPath = SDL_GetPrefPath(nullptr, g_config.appName); 62 | } 63 | if (g_config.msaa == 0) { 64 | g_config.msaa = 1; 65 | } 66 | if (g_config.maxTextureAnisotropy == 0) { 67 | g_config.maxTextureAnisotropy = 16; 68 | } 69 | ASSERT(window::initialize(), "Error initializing window"); 70 | 71 | /* Attempt to create a window using the calling application's desired backend */ 72 | AuroraBackend selectedBackend = config.desiredBackend; 73 | bool windowCreated = false; 74 | if (selectedBackend != BACKEND_AUTO && window::create_window(selectedBackend)) { 75 | if (webgpu::initialize(selectedBackend)) { 76 | windowCreated = true; 77 | } else { 78 | window::destroy_window(); 79 | } 80 | } 81 | 82 | if (!windowCreated) { 83 | for (const auto backendType : PreferredBackendOrder) { 84 | selectedBackend = backendType; 85 | if (!window::create_window(selectedBackend)) { 86 | continue; 87 | } 88 | if (webgpu::initialize(selectedBackend)) { 89 | windowCreated = true; 90 | break; 91 | } else { 92 | window::destroy_window(); 93 | } 94 | } 95 | } 96 | 97 | ASSERT(windowCreated, "Error creating window: {}", SDL_GetError()); 98 | 99 | // Initialize SDL_Renderer for ImGui when we can't use a Dawn backend 100 | if (webgpu::g_backendType == wgpu::BackendType::Null) { 101 | ASSERT(window::create_renderer(), "Failed to initialize SDL renderer: {}", SDL_GetError()); 102 | } 103 | 104 | window::show_window(); 105 | 106 | gfx::initialize(); 107 | 108 | imgui::create_context(); 109 | const auto size = window::get_window_size(); 110 | Log.info("Using framebuffer size {}x{} scale {}", size.fb_width, size.fb_height, size.scale); 111 | if (g_config.imGuiInitCallback != nullptr) { 112 | g_config.imGuiInitCallback(&size); 113 | } 114 | imgui::initialize(); 115 | 116 | g_initialFrame = true; 117 | g_config.desiredBackend = selectedBackend; 118 | return { 119 | .backend = selectedBackend, 120 | .configPath = g_config.configPath, 121 | .window = window::get_sdl_window(), 122 | .windowSize = size, 123 | }; 124 | } 125 | 126 | wgpu::TextureView g_currentView; 127 | 128 | void shutdown() noexcept { 129 | g_currentView = {}; 130 | imgui::shutdown(); 131 | gfx::shutdown(); 132 | webgpu::shutdown(); 133 | window::shutdown(); 134 | } 135 | 136 | const AuroraEvent* update() noexcept { 137 | if (g_initialFrame) { 138 | g_initialFrame = false; 139 | input::initialize(); 140 | } 141 | return window::poll_events(); 142 | } 143 | 144 | bool begin_frame() noexcept { 145 | wgpu::SurfaceTexture surfaceTexture; 146 | g_surface.GetCurrentTexture(&surfaceTexture); 147 | switch (surfaceTexture.status) { 148 | case wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal: 149 | g_currentView = surfaceTexture.texture.CreateView(); 150 | break; 151 | case wgpu::SurfaceGetCurrentTextureStatus::SuccessSuboptimal: { 152 | Log.warn("Surface texture is suboptimal"); 153 | // Force swapchain recreation 154 | const auto size = window::get_window_size(); 155 | webgpu::resize_swapchain(size.fb_width, size.fb_height, true); 156 | return false; 157 | } 158 | default: 159 | Log.error("Failed to get surface texture: {}", magic_enum::enum_name(surfaceTexture.status)); 160 | return false; 161 | } 162 | imgui::new_frame(window::get_window_size()); 163 | gfx::begin_frame(); 164 | return true; 165 | } 166 | 167 | void end_frame() noexcept { 168 | const auto encoderDescriptor = wgpu::CommandEncoderDescriptor{ 169 | .label = "Redraw encoder", 170 | }; 171 | auto encoder = g_device.CreateCommandEncoder(&encoderDescriptor); 172 | gfx::end_frame(encoder); 173 | gfx::render(encoder); 174 | { 175 | const std::array attachments{ 176 | wgpu::RenderPassColorAttachment{ 177 | .view = g_currentView, 178 | .loadOp = wgpu::LoadOp::Clear, 179 | .storeOp = wgpu::StoreOp::Store, 180 | }, 181 | }; 182 | const wgpu::RenderPassDescriptor renderPassDescriptor{ 183 | .label = "Post render pass", 184 | .colorAttachmentCount = attachments.size(), 185 | .colorAttachments = attachments.data(), 186 | }; 187 | auto pass = encoder.BeginRenderPass(&renderPassDescriptor); 188 | // Copy EFB -> XFB (swapchain) 189 | pass.SetPipeline(webgpu::g_CopyPipeline); 190 | pass.SetBindGroup(0, webgpu::g_CopyBindGroup, 0, nullptr); 191 | pass.Draw(3); 192 | imgui::render(pass); 193 | pass.End(); 194 | } 195 | const wgpu::CommandBufferDescriptor cmdBufDescriptor{.label = "Redraw command buffer"}; 196 | const auto buffer = encoder.Finish(&cmdBufDescriptor); 197 | g_queue.Submit(1, &buffer); 198 | g_surface.Present(); 199 | g_currentView = {}; 200 | } 201 | } // namespace 202 | } // namespace aurora 203 | 204 | // C API bindings 205 | AuroraInfo aurora_initialize(int argc, char* argv[], const AuroraConfig* config) { 206 | return aurora::initialize(argc, argv, *config); 207 | } 208 | void aurora_shutdown() { aurora::shutdown(); } 209 | const AuroraEvent* aurora_update() { return aurora::update(); } 210 | bool aurora_begin_frame() { return aurora::begin_frame(); } 211 | void aurora_end_frame() { aurora::end_frame(); } 212 | AuroraBackend aurora_get_backend() { return aurora::g_config.desiredBackend; } 213 | const AuroraBackend* aurora_get_available_backends(size_t* count) { 214 | *count = aurora::PreferredBackendOrder.size(); 215 | return aurora::PreferredBackendOrder.data(); 216 | } 217 | -------------------------------------------------------------------------------- /lib/dawn/BackendBinding.cpp: -------------------------------------------------------------------------------- 1 | #include "BackendBinding.hpp" 2 | 3 | #include 4 | 5 | #if defined(DAWN_ENABLE_BACKEND_D3D11) 6 | #include 7 | #endif 8 | #if defined(DAWN_ENABLE_BACKEND_D3D12) 9 | #include 10 | #endif 11 | #if defined(DAWN_ENABLE_BACKEND_METAL) 12 | #include 13 | #endif 14 | #if defined(DAWN_ENABLE_BACKEND_VULKAN) 15 | #include 16 | #endif 17 | #if defined(DAWN_ENABLE_BACKEND_OPENGL) 18 | #include 19 | #endif 20 | #if defined(DAWN_ENABLE_BACKEND_NULL) 21 | #include 22 | #endif 23 | 24 | #if !defined(SDL_PLATFORM_MACOS) 25 | #include 26 | #endif 27 | 28 | namespace aurora::webgpu::utils { 29 | std::unique_ptr SetupWindowAndGetSurfaceDescriptorCocoa(SDL_Window* window); 30 | 31 | std::unique_ptr SetupWindowAndGetSurfaceDescriptor(SDL_Window* window) { 32 | #if defined(SDL_PLATFORM_MACOS) 33 | return SetupWindowAndGetSurfaceDescriptorCocoa(window); 34 | #else 35 | const auto props = SDL_GetWindowProperties(window); 36 | #if defined(SDL_PLATFORM_WIN32) 37 | std::unique_ptr desc = std::make_unique(); 38 | desc->hwnd = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); 39 | desc->hinstance = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER, nullptr); 40 | return std::move(desc); 41 | #elif defined(SDL_PLATFORM_LINUX) 42 | const char* driver = SDL_GetCurrentVideoDriver(); 43 | if (SDL_strcmp(driver, "wayland") == 0) { 44 | std::unique_ptr desc = std::make_unique(); 45 | desc->display = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, nullptr); 46 | desc->surface = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr); 47 | return std::move(desc); 48 | } 49 | if (SDL_strcmp(driver, "x11") == 0) { 50 | std::unique_ptr desc = std::make_unique(); 51 | desc->display = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr); 52 | desc->window = SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); 53 | return std::move(desc); 54 | } 55 | #endif 56 | return nullptr; 57 | #endif 58 | } 59 | 60 | } // namespace aurora::webgpu::utils 61 | -------------------------------------------------------------------------------- /lib/dawn/BackendBinding.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct SDL_Window; 7 | 8 | namespace aurora::webgpu::utils { 9 | 10 | std::unique_ptr SetupWindowAndGetSurfaceDescriptor(SDL_Window* window); 11 | 12 | } // namespace aurora::webgpu::utils 13 | -------------------------------------------------------------------------------- /lib/dawn/MetalBinding.mm: -------------------------------------------------------------------------------- 1 | #include "BackendBinding.hpp" 2 | 3 | #include 4 | 5 | namespace aurora::webgpu::utils { 6 | std::unique_ptr SetupWindowAndGetSurfaceDescriptorCocoa(SDL_Window* window) { 7 | SDL_MetalView view = SDL_Metal_CreateView(window); 8 | std::unique_ptr desc = std::make_unique(); 9 | desc->layer = SDL_Metal_GetLayer(view); 10 | return std::move(desc); 11 | } 12 | } // namespace aurora::webgpu::utils 13 | -------------------------------------------------------------------------------- /lib/dolphin/gx/GXBump.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | void GXSetNumIndStages(u8 num) { update_gx_state(g_gxState.numIndStages, num); } 5 | 6 | void GXSetIndTexOrder(GXIndTexStageID indStage, GXTexCoordID texCoord, GXTexMapID texMap) { 7 | auto& stage = g_gxState.indStages[indStage]; 8 | update_gx_state(stage.texCoordId, texCoord); 9 | update_gx_state(stage.texMapId, texMap); 10 | } 11 | 12 | void GXSetIndTexCoordScale(GXIndTexStageID indStage, GXIndTexScale scaleS, GXIndTexScale scaleT) { 13 | auto& stage = g_gxState.indStages[indStage]; 14 | update_gx_state(stage.scaleS, scaleS); 15 | update_gx_state(stage.scaleT, scaleT); 16 | } 17 | 18 | void GXSetIndTexMtx(GXIndTexMtxID id, const void* offset, s8 scaleExp) { 19 | CHECK(id >= GX_ITM_0 && id <= GX_ITM_2, "invalid ind tex mtx ID {}", static_cast(id)); 20 | update_gx_state(g_gxState.indTexMtxs[id - 1], {*reinterpret_cast*>(offset), scaleExp}); 21 | } 22 | 23 | void GXSetTevIndirect(GXTevStageID tevStage, GXIndTexStageID indStage, GXIndTexFormat fmt, GXIndTexBiasSel biasSel, 24 | GXIndTexMtxID matrixSel, GXIndTexWrap wrapS, GXIndTexWrap wrapT, GXBool addPrev, GXBool indLod, 25 | GXIndTexAlphaSel alphaSel) { 26 | auto& stage = g_gxState.tevStages[tevStage]; 27 | update_gx_state(stage.indTexStage, indStage); 28 | update_gx_state(stage.indTexFormat, fmt); 29 | update_gx_state(stage.indTexBiasSel, biasSel); 30 | update_gx_state(stage.indTexAlphaSel, alphaSel); 31 | update_gx_state(stage.indTexMtxId, matrixSel); 32 | update_gx_state(stage.indTexWrapS, wrapS); 33 | update_gx_state(stage.indTexWrapT, wrapT); 34 | update_gx_state(stage.indTexAddPrev, addPrev); 35 | update_gx_state(stage.indTexUseOrigLOD, indLod); 36 | } 37 | 38 | void GXSetTevDirect(GXTevStageID stageId) { 39 | auto& stage = g_gxState.tevStages[stageId]; 40 | // TODO is this right? 41 | update_gx_state(stage.indTexStage, GX_INDTEXSTAGE0); 42 | update_gx_state(stage.indTexFormat, GX_ITF_8); 43 | update_gx_state(stage.indTexBiasSel, GX_ITB_NONE); 44 | update_gx_state(stage.indTexAlphaSel, GX_ITBA_OFF); 45 | update_gx_state(stage.indTexMtxId, GX_ITM_OFF); 46 | update_gx_state(stage.indTexWrapS, GX_ITW_OFF); 47 | update_gx_state(stage.indTexWrapT, GX_ITW_OFF); 48 | update_gx_state(stage.indTexUseOrigLOD, false); 49 | update_gx_state(stage.indTexAddPrev, false); 50 | } 51 | 52 | void GXSetTevIndWarp(GXTevStageID tevStage, GXIndTexStageID indStage, GXBool signedOffsets, GXBool replaceMode, 53 | GXIndTexMtxID matrixSel) { 54 | const auto wrap = replaceMode ? GX_ITW_0 : GX_ITW_OFF; 55 | const auto biasSel = signedOffsets ? GX_ITB_STU : GX_ITB_NONE; 56 | GXSetTevIndirect(tevStage, indStage, GX_ITF_8, biasSel, matrixSel, wrap, wrap, false, false, GX_ITBA_OFF); 57 | } 58 | 59 | // TODO GXSetTevIndTile 60 | // TODO GXSetTevIndBumpST 61 | // TODO GXSetTevIndBumpXYZ 62 | // TODO GXSetTevIndRepeat 63 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXCull.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | 5 | void GXSetScissor(u32 left, u32 top, u32 width, u32 height) { aurora::gfx::set_scissor(left, top, width, height); } 6 | 7 | void GXSetCullMode(GXCullMode mode) { update_gx_state(g_gxState.cullMode, mode); } 8 | 9 | // TODO GXSetCoPlanar 10 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXDispList.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | #include "../../gfx/model/shader.hpp" 4 | 5 | #define ROUNDUP32(x) (((x) + 31) & ~31) 6 | 7 | extern "C" { 8 | void GXBeginDisplayList(void* list, u32 size) { 9 | CHECK(!g_gxState.dynamicDlBuf, "Display list began twice!"); 10 | g_gxState.dynamicDlBuf.emplace(static_cast(list), size); 11 | } 12 | 13 | u32 GXEndDisplayList() { 14 | auto &dlBuf = g_gxState.dynamicDlBuf; 15 | size_t size = dlBuf->size(); 16 | size_t paddedSize = ROUNDUP32(size); 17 | dlBuf->append_zeroes(paddedSize - size); 18 | dlBuf.reset(); 19 | return paddedSize; 20 | } 21 | 22 | void GXCallDisplayListLE(const void* data, u32 nbytes) { 23 | aurora::gfx::model::queue_surface(static_cast(data), nbytes, false); 24 | } 25 | 26 | void GXCallDisplayList(const void* data, u32 nbytes) { 27 | aurora::gfx::model::queue_surface(static_cast(data), nbytes, true); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/dolphin/gx/GXDraw.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | 5 | // TODO GXDrawCylinder 6 | // TODO GXDrawTorus 7 | 8 | void GXDrawSphere(u8 numMajor, u8 numMinor) { puts("GXDrawSphere is a stub"); } 9 | 10 | // TODO GXDrawCube 11 | // TODO GXDrawDodeca 12 | // TODO GXDrawOctahedron 13 | // TODO GXDrawIcosahedron 14 | // TODO GXDrawSphere1 15 | // TODO GXGenNormalTable 16 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXExtra.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | 5 | void GXDestroyTexObj(GXTexObj* obj_) { 6 | auto* obj = reinterpret_cast(obj_); 7 | obj->ref.reset(); 8 | } 9 | 10 | void GXDestroyTlutObj(GXTlutObj* obj_) { 11 | auto* obj = reinterpret_cast(obj_); 12 | obj->ref.reset(); 13 | } 14 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXFifo.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | 5 | static GXFifoObj* GPFifo; 6 | static GXFifoObj* CPUFifo; 7 | 8 | void GXGetGPStatus(GXBool* overhi, GXBool* underlow, GXBool* readIdle, GXBool* cmdIdle, GXBool* brkpt) { 9 | *overhi = *underlow = *readIdle = *cmdIdle = *brkpt = false; 10 | *readIdle = true; 11 | } 12 | 13 | // TODO GXGetFifoStatus 14 | 15 | void GXGetFifoPtrs(GXFifoObj* fifo, void** readPtr, void** writePtr) { 16 | *readPtr = NULL; 17 | *writePtr = NULL; 18 | } 19 | 20 | GXFifoObj* GXGetCPUFifo() { return CPUFifo; } 21 | 22 | GXFifoObj* GXGetGPFifo() { return GPFifo; } 23 | 24 | // TODO GXGetFifoBase 25 | // TODO GXGetFifoSize 26 | // TODO GXGetFifoLimits 27 | // TODO GXSetBreakPtCallback 28 | // TODO GXEnableBreakPt 29 | // TODO GXDisableBreakPt 30 | 31 | void GXInitFifoBase(GXFifoObj* fifo, void* base, u32 size) {} 32 | 33 | void GXInitFifoPtrs(GXFifoObj* fifo, void* readPtr, void* writePtr) {} 34 | 35 | // TODO GXInitFifoLimits 36 | 37 | void GXSetCPUFifo(GXFifoObj* fifo) { CPUFifo = fifo; } 38 | 39 | void GXSetGPFifo(GXFifoObj* fifo) { GPFifo = fifo; } 40 | 41 | void GXSaveCPUFifo(GXFifoObj* fifo) {} 42 | 43 | // TODO GXSaveGPFifo 44 | // TODO GXRedirectWriteGatherPipe 45 | // TODO GXRestoreWriteGatherPipe 46 | // TODO GXSetCurrentGXThread 47 | // TODO GXGetCurrentGXThread 48 | // TODO GXGetOverflowCount 49 | // TODO GXResetOverflowCount 50 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXFrameBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | #include "../../window.hpp" 4 | #include "../../webgpu/wgpu.hpp" 5 | 6 | extern "C" { 7 | GXRenderModeObj GXNtsc480IntDf = { 8 | VI_TVMODE_NTSC_INT, 640, 480, 480, 40, 0, 640, 480, VI_XFBMODE_DF, 0, 0, 9 | }; 10 | GXRenderModeObj GXPal528IntDf = { 11 | VI_TVMODE_PAL_INT, 704, 528, 480, 40, 0, 640, 480, VI_XFBMODE_DF, 0, 0, 12 | }; 13 | GXRenderModeObj GXMpal480IntDf = { 14 | VI_TVMODE_PAL_INT, 640, 480, 480, 40, 0, 640, 480, VI_XFBMODE_DF, 0, 0, 15 | }; 16 | 17 | 18 | void GXAdjustForOverscan(GXRenderModeObj* rmin, GXRenderModeObj* rmout, u16 hor, u16 ver) { 19 | *rmout = *rmin; 20 | const auto size = aurora::window::get_window_size(); 21 | rmout->fbWidth = size.fb_width; 22 | rmout->efbHeight = size.fb_height; 23 | rmout->xfbHeight = size.fb_height; 24 | } 25 | 26 | void GXSetDispCopySrc(u16 left, u16 top, u16 wd, u16 ht) {} 27 | 28 | void GXSetTexCopySrc(u16 left, u16 top, u16 wd, u16 ht) { g_gxState.texCopySrc = {left, top, wd, ht}; } 29 | 30 | void GXSetDispCopyDst(u16 wd, u16 ht) {} 31 | 32 | void GXSetTexCopyDst(u16 wd, u16 ht, GXTexFmt fmt, GXBool mipmap) { 33 | // TODO texture copy scaling (mipmap) 34 | g_gxState.texCopyFmt = fmt; 35 | } 36 | 37 | // TODO GXSetDispCopyFrame2Field 38 | // TODO GXSetCopyClamp 39 | 40 | u32 GXSetDispCopyYScale(f32 vscale) { return 0; } 41 | 42 | void GXSetCopyClear(GXColor color, u32 depth) { update_gx_state(g_gxState.clearColor, from_gx_color(color)); } 43 | 44 | void GXSetCopyFilter(GXBool aa, u8 sample_pattern[12][2], GXBool vf, u8 vfilter[7]) {} 45 | 46 | void GXSetDispCopyGamma(GXGamma gamma) {} 47 | 48 | void GXCopyDisp(void* dest, GXBool clear) {} 49 | 50 | void GXCopyTex(void* dest, GXBool clear) { 51 | const auto& rect = g_gxState.texCopySrc; 52 | const wgpu::Extent3D size{ 53 | .width = static_cast(rect.width), 54 | .height = static_cast(rect.height), 55 | .depthOrArrayLayers = 1, 56 | }; 57 | aurora::gfx::TextureHandle handle; 58 | const auto it = g_gxState.copyTextures.find(dest); 59 | if (it == g_gxState.copyTextures.end() || it->second->size != size) { 60 | handle = aurora::gfx::new_render_texture(rect.width, rect.height, g_gxState.texCopyFmt, "Resolved Texture"); 61 | g_gxState.copyTextures[dest] = handle; 62 | } else { 63 | handle = it->second; 64 | } 65 | aurora::gfx::resolve_pass(handle, rect, clear, g_gxState.clearColor); 66 | } 67 | 68 | // TODO GXGetYScaleFactor 69 | // TODO GXGetNumXfbLines 70 | // TODO GXClearBoundingBox 71 | // TODO GXReadBoundingBox 72 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXGeometry.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | #include 4 | 5 | extern "C" { 6 | 7 | void GXSetVtxDesc(GXAttr attr, GXAttrType type) { update_gx_state(g_gxState.vtxDesc[attr], type); } 8 | 9 | void GXSetVtxDescv(GXVtxDescList* list) { 10 | while (list->attr != GX_VA_NULL) { 11 | update_gx_state(g_gxState.vtxDesc[list->attr], list->type); 12 | ++list; 13 | } 14 | } 15 | 16 | void GXClearVtxDesc() { g_gxState.vtxDesc.fill({}); } 17 | 18 | void GXSetVtxAttrFmt(GXVtxFmt vtxfmt, GXAttr attr, GXCompCnt cnt, GXCompType type, u8 frac) { 19 | CHECK(vtxfmt >= GX_VTXFMT0 && vtxfmt < GX_MAX_VTXFMT, "invalid vtxfmt {}", underlying(vtxfmt)); 20 | CHECK(attr >= GX_VA_PNMTXIDX && attr < GX_VA_MAX_ATTR, "invalid attr {}", underlying(attr)); 21 | auto& fmt = g_gxState.vtxFmts[vtxfmt].attrs[attr]; 22 | update_gx_state(fmt.cnt, cnt); 23 | update_gx_state(fmt.type, type); 24 | update_gx_state(fmt.frac, frac); 25 | } 26 | 27 | // TODO GXSetVtxAttrFmtv 28 | 29 | void GXSetArray(GXAttr attr, const void* data, u32 size, u8 stride) { 30 | auto& array = g_gxState.arrays[attr]; 31 | update_gx_state(array.data, data); 32 | update_gx_state(array.size, size); 33 | update_gx_state(array.stride, stride); 34 | array.cachedRange = {}; 35 | } 36 | 37 | // TODO move GXBegin, GXEnd here 38 | 39 | void GXSetTexCoordGen2(GXTexCoordID dst, GXTexGenType type, GXTexGenSrc src, u32 mtx, GXBool normalize, u32 postMtx) { 40 | CHECK(dst >= GX_TEXCOORD0 && dst <= GX_TEXCOORD7, "invalid tex coord {}", underlying(dst)); 41 | update_gx_state(g_gxState.tcgs[dst], 42 | {type, src, static_cast(mtx), static_cast(postMtx), normalize}); 43 | } 44 | 45 | void GXSetNumTexGens(u8 num) { update_gx_state(g_gxState.numTexGens, num); } 46 | 47 | void GXInvalidateVtxCache() { 48 | // TODO 49 | } 50 | 51 | void GXSetLineWidth(u8 width, GXTexOffset offs) { 52 | // TODO 53 | } 54 | 55 | // TODO GXSetPointSize 56 | // TODO GXEnableTexOffsets 57 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXGet.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | #include "../../gfx/texture.hpp" 4 | 5 | extern "C" { 6 | 7 | // TODO GXGetVtxDesc 8 | // TODO GXGetVtxDescv 9 | // TODO GXGetVtxAttrFmtv 10 | // TODO GXGetLineWidth 11 | // TODO GXGetPointSize 12 | 13 | void GXGetVtxAttrFmt(GXVtxFmt idx, GXAttr attr, GXCompCnt* compCnt, GXCompType* compType, u8* shift) { 14 | const auto& fmt = g_gxState.vtxFmts[idx].attrs[attr]; 15 | *compCnt = fmt.cnt; 16 | *compType = fmt.type; 17 | *shift = fmt.frac; 18 | } 19 | 20 | // TODO GXGetViewportv 21 | 22 | void GXGetProjectionv(f32* p) { 23 | const auto& mtx = g_gxState.proj; 24 | p[0] = static_cast(g_gxState.projType); 25 | p[1] = mtx.m0[0]; 26 | p[3] = mtx.m1[1]; 27 | p[5] = mtx.m2[2]; 28 | p[6] = mtx.m2[3]; 29 | if (g_gxState.projType == GX_ORTHOGRAPHIC) { 30 | p[2] = mtx.m0[3]; 31 | p[4] = mtx.m1[3]; 32 | } else { 33 | p[2] = mtx.m0[2]; 34 | p[4] = mtx.m1[2]; 35 | } 36 | } 37 | 38 | // TODO GXGetScissor 39 | // TODO GXGetCullMode 40 | 41 | void GXGetLightAttnA(GXLightObj* light_, float* a0, float* a1, float* a2) { 42 | auto* light = reinterpret_cast(light_); 43 | *a0 = light->a0; 44 | *a1 = light->a1; 45 | *a2 = light->a2; 46 | } 47 | 48 | void GXGetLightAttnK(GXLightObj* light_, float* k0, float* k1, float* k2) { 49 | auto* light = reinterpret_cast(light_); 50 | *k0 = light->k0; 51 | *k1 = light->k1; 52 | *k2 = light->k2; 53 | } 54 | 55 | void GXGetLightPos(GXLightObj* light_, float* x, float* y, float* z) { 56 | auto* light = reinterpret_cast(light_); 57 | *x = light->px; 58 | *z = light->py; 59 | *z = light->pz; 60 | } 61 | 62 | void GXGetLightDir(GXLightObj* light_, float* nx, float* ny, float* nz) { 63 | auto* light = reinterpret_cast(light_); 64 | *nx = -light->nx; 65 | *ny = -light->ny; 66 | *nz = -light->nz; 67 | } 68 | 69 | void GXGetLightColor(GXLightObj* light_, GXColor* col) { 70 | auto* light = reinterpret_cast(light_); 71 | *col = light->color; 72 | } 73 | 74 | void* GXGetTexObjData(GXTexObj* tex_obj) { 75 | return const_cast(reinterpret_cast(tex_obj)->data); 76 | } 77 | 78 | u16 GXGetTexObjWidth(GXTexObj* tex_obj) { return reinterpret_cast(tex_obj)->width; } 79 | 80 | u16 GXGetTexObjHeight(GXTexObj* tex_obj) { return reinterpret_cast(tex_obj)->height; } 81 | 82 | GXTexFmt GXGetTexObjFmt(GXTexObj* tex_obj) { 83 | return static_cast(reinterpret_cast(tex_obj)->fmt); 84 | } 85 | 86 | GXTexWrapMode GXGetTexObjWrapS(GXTexObj* tex_obj) { return reinterpret_cast(tex_obj)->wrapS; } 87 | 88 | GXTexWrapMode GXGetTexObjWrapT(GXTexObj* tex_obj) { return reinterpret_cast(tex_obj)->wrapT; } 89 | 90 | GXBool GXGetTexObjMipMap(GXTexObj* tex_obj) { return reinterpret_cast(tex_obj)->hasMips; } 91 | 92 | // TODO GXGetTexObjAll 93 | // TODO GXGetTexObjMinFilt 94 | // TODO GXGetTexObjMagFilt 95 | // TODO GXGetTexObjMinLOD 96 | // TODO GXGetTexObjMaxLOD 97 | // TODO GXGetTexObjLODBias 98 | // TODO GXGetTexObjBiasClamp 99 | // TODO GXGetTexObjEdgeLOD 100 | // TODO GXGetTexObjMaxAniso 101 | // TODO GXGetTexObjLODAll 102 | // TODO GXGetTexObjTlut 103 | // TODO GXGetTlutObjData 104 | // TODO GXGetTlutObjFmt 105 | // TODO GXGetTlutObjNumEntries 106 | // TODO GXGetTlutObjAll 107 | // TODO GXGetTexRegionAll 108 | // TODO GXGetTlutRegionAll 109 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXLighting.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | void GXInitLightAttn(GXLightObj* light_, float a0, float a1, float a2, float k0, float k1, float k2) { 5 | auto* light = reinterpret_cast(light_); 6 | light->a0 = a0; 7 | light->a1 = a1; 8 | light->a2 = a2; 9 | light->k0 = k0; 10 | light->k1 = k1; 11 | light->k2 = k2; 12 | } 13 | 14 | void GXInitLightAttnA(GXLightObj* light_, float a0, float a1, float a2) { 15 | auto* light = reinterpret_cast(light_); 16 | light->a0 = a0; 17 | light->a1 = a1; 18 | light->a2 = a2; 19 | } 20 | 21 | void GXInitLightAttnK(GXLightObj* light_, float k0, float k1, float k2) { 22 | auto* light = reinterpret_cast(light_); 23 | light->k0 = k0; 24 | light->k1 = k1; 25 | light->k2 = k2; 26 | } 27 | 28 | void GXInitLightSpot(GXLightObj* light_, float cutoff, GXSpotFn spotFn) { 29 | if (cutoff <= 0.f || cutoff > 90.f) { 30 | spotFn = GX_SP_OFF; 31 | } 32 | 33 | float cr = std::cos((cutoff * M_PIF) / 180.f); 34 | float a0 = 1.f; 35 | float a1 = 0.f; 36 | float a2 = 0.f; 37 | switch (spotFn) { 38 | default: 39 | break; 40 | case GX_SP_FLAT: 41 | a0 = -1000.f * cr; 42 | a1 = 1000.f; 43 | a2 = 0.f; 44 | break; 45 | case GX_SP_COS: 46 | a0 = -cr / (1.f - cr); 47 | a1 = 1.f / (1.f - cr); 48 | a2 = 0.f; 49 | break; 50 | case GX_SP_COS2: 51 | a0 = 0.f; 52 | a1 = -cr / (1.f - cr); 53 | a2 = 1.f / (1.f - cr); 54 | break; 55 | case GX_SP_SHARP: { 56 | const float d = (1.f - cr) * (1.f - cr); 57 | a0 = cr * (cr - 2.f); 58 | a1 = 2.f / d; 59 | a2 = -1.f / d; 60 | break; 61 | } 62 | case GX_SP_RING1: { 63 | const float d = (1.f - cr) * (1.f - cr); 64 | a0 = 4.f * cr / d; 65 | a1 = 4.f * (1.f + cr) / d; 66 | a2 = -4.f / d; 67 | break; 68 | } 69 | case GX_SP_RING2: { 70 | const float d = (1.f - cr) * (1.f - cr); 71 | a0 = 1.f - 2.f * cr * cr / d; 72 | a1 = 4.f * cr / d; 73 | a2 = -2.f / d; 74 | break; 75 | } 76 | } 77 | 78 | auto* light = reinterpret_cast(light_); 79 | light->a0 = a0; 80 | light->a1 = a1; 81 | light->a2 = a2; 82 | } 83 | 84 | void GXInitLightDistAttn(GXLightObj* light_, float refDistance, float refBrightness, GXDistAttnFn distFunc) { 85 | if (refDistance < 0.f || refBrightness < 0.f || refBrightness >= 1.f) { 86 | distFunc = GX_DA_OFF; 87 | } 88 | float k0 = 1.f; 89 | float k1 = 0.f; 90 | float k2 = 0.f; 91 | switch (distFunc) { 92 | case GX_DA_GENTLE: 93 | k0 = 1.0f; 94 | k1 = (1.0f - refBrightness) / (refBrightness * refDistance); 95 | k2 = 0.0f; 96 | break; 97 | case GX_DA_MEDIUM: 98 | k0 = 1.0f; 99 | k1 = 0.5f * (1.0f - refBrightness) / (refBrightness * refDistance); 100 | k2 = 0.5f * (1.0f - refBrightness) / (refBrightness * refDistance * refDistance); 101 | break; 102 | case GX_DA_STEEP: 103 | k0 = 1.0f; 104 | k1 = 0.0f; 105 | k2 = (1.0f - refBrightness) / (refBrightness * refDistance * refDistance); 106 | break; 107 | case GX_DA_OFF: 108 | k0 = 1.0f; 109 | k1 = 0.0f; 110 | k2 = 0.0f; 111 | break; 112 | } 113 | 114 | auto* light = reinterpret_cast(light_); 115 | light->k0 = k0; 116 | light->k1 = k1; 117 | light->k2 = k2; 118 | } 119 | 120 | void GXInitLightPos(GXLightObj* light_, float x, float y, float z) { 121 | auto* light = reinterpret_cast(light_); 122 | light->px = x; 123 | light->py = y; 124 | light->pz = z; 125 | } 126 | 127 | void GXInitLightColor(GXLightObj* light_, GXColor col) { 128 | auto* light = reinterpret_cast(light_); 129 | light->color = col; 130 | } 131 | 132 | void GXLoadLightObjImm(GXLightObj* light_, GXLightID id) { 133 | u32 idx = std::log2(id); 134 | aurora::gfx::gx::Light realLight; 135 | auto* light = reinterpret_cast(light_); 136 | realLight.pos = {light->px, light->py, light->pz}; 137 | realLight.dir = {light->nx, light->ny, light->nz}; 138 | realLight.color = from_gx_color(light->color); 139 | realLight.cosAtt = {light->a0, light->a1, light->a2}; 140 | realLight.distAtt = {light->k0, light->k1, light->k2}; 141 | update_gx_state(g_gxState.lights[idx], realLight); 142 | } 143 | 144 | // TODO GXLoadLightObjIndx 145 | 146 | void GXSetChanAmbColor(GXChannelID id, GXColor color) { 147 | if (id == GX_COLOR0A0) { 148 | GXSetChanAmbColor(GX_COLOR0, color); 149 | GXSetChanAmbColor(GX_ALPHA0, color); 150 | return; 151 | } else if (id == GX_COLOR1A1) { 152 | GXSetChanAmbColor(GX_COLOR1, color); 153 | GXSetChanAmbColor(GX_ALPHA1, color); 154 | return; 155 | } 156 | CHECK(id >= GX_COLOR0 && id <= GX_ALPHA1, "bad channel {}", static_cast(id)); 157 | update_gx_state(g_gxState.colorChannelState[id].ambColor, from_gx_color(color)); 158 | } 159 | 160 | void GXSetChanMatColor(GXChannelID id, GXColor color) { 161 | if (id == GX_COLOR0A0) { 162 | GXSetChanMatColor(GX_COLOR0, color); 163 | GXSetChanMatColor(GX_ALPHA0, color); 164 | return; 165 | } else if (id == GX_COLOR1A1) { 166 | GXSetChanMatColor(GX_COLOR1, color); 167 | GXSetChanMatColor(GX_ALPHA1, color); 168 | return; 169 | } 170 | CHECK(id >= GX_COLOR0 && id <= GX_ALPHA1, "bad channel {}", static_cast(id)); 171 | update_gx_state(g_gxState.colorChannelState[id].matColor, from_gx_color(color)); 172 | } 173 | 174 | void GXSetNumChans(u8 num) { update_gx_state(g_gxState.numChans, num); } 175 | 176 | void GXInitLightDir(GXLightObj* light_, float nx, float ny, float nz) { 177 | auto* light = reinterpret_cast(light_); 178 | light->nx = -nx; 179 | light->ny = -ny; 180 | light->nz = -nz; 181 | } 182 | 183 | void GXInitSpecularDir(GXLightObj* light_, float nx, float ny, float nz) { 184 | float hx = -nx; 185 | float hy = -ny; 186 | float hz = (-nz + 1.0f); 187 | float mag = ((hx * hx) + (hy * hy) + (hz * hz)); 188 | if (mag != 0.0f) { 189 | mag = 1.0f / sqrtf(mag); 190 | } 191 | 192 | auto* light = reinterpret_cast(light_); 193 | light->px = (nx * GX_LARGE_NUMBER); 194 | light->py = (ny * GX_LARGE_NUMBER); 195 | light->pz = (nz * GX_LARGE_NUMBER); 196 | light->nx = hx * mag; 197 | light->ny = hy * mag; 198 | light->nz = hz * mag; 199 | } 200 | 201 | void GXInitSpecularDirHA(GXLightObj* light_, float nx, float ny, float nz, float hx, float hy, float hz) { 202 | auto* light = reinterpret_cast(light_); 203 | light->px = (nx * GX_LARGE_NUMBER); 204 | light->py = (ny * GX_LARGE_NUMBER); 205 | light->pz = (nz * GX_LARGE_NUMBER); 206 | light->nx = hx; 207 | light->ny = hy; 208 | light->nz = hz; 209 | } 210 | 211 | void GXSetChanCtrl(GXChannelID id, bool lightingEnabled, GXColorSrc ambSrc, GXColorSrc matSrc, u32 lightState, 212 | GXDiffuseFn diffFn, GXAttnFn attnFn) { 213 | if (id == GX_COLOR0A0) { 214 | GXSetChanCtrl(GX_COLOR0, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn); 215 | GXSetChanCtrl(GX_ALPHA0, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn); 216 | return; 217 | } else if (id == GX_COLOR1A1) { 218 | GXSetChanCtrl(GX_COLOR1, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn); 219 | GXSetChanCtrl(GX_ALPHA1, lightingEnabled, ambSrc, matSrc, lightState, diffFn, attnFn); 220 | return; 221 | } 222 | CHECK(id >= GX_COLOR0 && id <= GX_ALPHA1, "bad channel {}", static_cast(id)); 223 | auto& chan = g_gxState.colorChannelConfig[id]; 224 | update_gx_state(chan.lightingEnabled, lightingEnabled); 225 | update_gx_state(chan.ambSrc, ambSrc); 226 | update_gx_state(chan.matSrc, matSrc); 227 | update_gx_state(chan.diffFn, diffFn); 228 | update_gx_state(chan.attnFn, attnFn); 229 | update_gx_state(g_gxState.colorChannelState[id].lightMask, GX::LightMask{lightState}); 230 | } 231 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXManage.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | static GXDrawDoneCallback DrawDoneCB = nullptr; 5 | 6 | GXFifoObj* GXInit(void* base, u32 size) { return NULL; } 7 | 8 | // TODO GXAbortFrame 9 | // TODO GXSetDrawSync 10 | // TODO GXReadDrawSync 11 | // TODO GXSetDrawSyncCallback 12 | 13 | void GXDrawDone() { if (DrawDoneCB != nullptr) DrawDoneCB(); } 14 | 15 | void GXSetDrawDone() { if (DrawDoneCB != nullptr) DrawDoneCB(); } 16 | 17 | // TODO GXWaitDrawDone 18 | 19 | GXDrawDoneCallback GXSetDrawDoneCallback(GXDrawDoneCallback cb) { 20 | GXDrawDoneCallback old = DrawDoneCB; 21 | DrawDoneCB = cb; 22 | return old; 23 | } 24 | 25 | // TODO GXSetResetWritePipe 26 | 27 | void GXFlush() {} 28 | 29 | // TODO GXResetWriteGatherPipe 30 | 31 | void GXPixModeSync() {} 32 | 33 | void GXTexModeSync() {} 34 | 35 | // TODO IsWriteGatherBufferEmpty 36 | // TODO GXSetMisc 37 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXPerf.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | 5 | // TODO GXSetGPMetric 6 | // TODO GXClearGPMetric 7 | // TODO GXReadGPMetric 8 | // TODO GXReadGP0Metric 9 | // TODO GXReadGP1Metric 10 | // TODO GXReadMemMetric 11 | // TODO GXClearMemMetric 12 | // TODO GXReadPixMetric 13 | // TODO GXClearPixMetric 14 | // TODO GXSetVCacheMetric 15 | // TODO GXReadVCacheMetric 16 | // TODO GXClearVCacheMetric 17 | // TODO GXReadXfRasMetric 18 | // TODO GXInitXfRasMetric 19 | // TODO GXReadClksPerVtx 20 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXPixel.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | void GXSetFog(GXFogType type, float startZ, float endZ, float nearZ, float farZ, GXColor color) { 5 | update_gx_state(g_gxState.fog, {type, startZ, endZ, nearZ, farZ, from_gx_color(color)}); 6 | } 7 | 8 | void GXSetFogColor(GXColor color) { update_gx_state(g_gxState.fog.color, from_gx_color(color)); } 9 | 10 | // TODO GXInitFogAdjTable 11 | // TODO GXSetFogRangeAdj 12 | 13 | void GXSetBlendMode(GXBlendMode mode, GXBlendFactor src, GXBlendFactor dst, GXLogicOp op) { 14 | update_gx_state(g_gxState.blendMode, mode); 15 | update_gx_state(g_gxState.blendFacSrc, src); 16 | update_gx_state(g_gxState.blendFacDst, dst); 17 | update_gx_state(g_gxState.blendOp, op); 18 | } 19 | 20 | void GXSetColorUpdate(GXBool enabled) { update_gx_state(g_gxState.colorUpdate, enabled); } 21 | 22 | void GXSetAlphaUpdate(bool enabled) { update_gx_state(g_gxState.alphaUpdate, enabled); } 23 | 24 | void GXSetZMode(bool compare_enable, GXCompare func, bool update_enable) { 25 | update_gx_state(g_gxState.depthCompare, compare_enable); 26 | update_gx_state(g_gxState.depthFunc, func); 27 | update_gx_state(g_gxState.depthUpdate, update_enable); 28 | } 29 | 30 | void GXSetZCompLoc(GXBool before_tex) { 31 | // TODO 32 | } 33 | 34 | void GXSetPixelFmt(GXPixelFmt pix_fmt, GXZFmt16 z_fmt) {} 35 | 36 | void GXSetDither(GXBool dither) {} 37 | 38 | void GXSetDstAlpha(bool enabled, u8 value) { 39 | if (enabled) { 40 | update_gx_state(g_gxState.dstAlpha, value); 41 | } else { 42 | update_gx_state(g_gxState.dstAlpha, UINT32_MAX); 43 | } 44 | } 45 | 46 | // TODO GXSetFieldMask 47 | // TODO GXSetFieldMode 48 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXTev.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | void GXSetTevOp(GXTevStageID id, GXTevMode mode) { 5 | GXTevColorArg inputColor = GX_CC_RASC; 6 | GXTevAlphaArg inputAlpha = GX_CA_RASA; 7 | if (id != GX_TEVSTAGE0) { 8 | inputColor = GX_CC_CPREV; 9 | inputAlpha = GX_CA_APREV; 10 | } 11 | switch (mode) { 12 | case GX_MODULATE: 13 | GXSetTevColorIn(id, GX_CC_ZERO, GX_CC_TEXC, inputColor, GX_CC_ZERO); 14 | GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_TEXA, inputAlpha, GX_CA_ZERO); 15 | break; 16 | case GX_DECAL: 17 | GXSetTevColorIn(id, inputColor, GX_CC_TEXC, GX_CC_TEXA, GX_CC_ZERO); 18 | GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, inputAlpha); 19 | break; 20 | case GX_BLEND: 21 | GXSetTevColorIn(id, inputColor, GX_CC_ONE, GX_CC_TEXC, GX_CC_ZERO); 22 | GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_TEXA, inputAlpha, GX_CA_ZERO); 23 | break; 24 | case GX_REPLACE: 25 | GXSetTevColorIn(id, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC); 26 | GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA); 27 | break; 28 | case GX_PASSCLR: 29 | GXSetTevColorIn(id, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, inputColor); 30 | GXSetTevAlphaIn(id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, inputAlpha); 31 | break; 32 | } 33 | GXSetTevColorOp(id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 34 | GXSetTevAlphaOp(id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 35 | } 36 | 37 | void GXSetTevColorIn(GXTevStageID stageId, GXTevColorArg a, GXTevColorArg b, GXTevColorArg c, GXTevColorArg d) { 38 | update_gx_state(g_gxState.tevStages[stageId].colorPass, {a, b, c, d}); 39 | } 40 | 41 | void GXSetTevAlphaIn(GXTevStageID stageId, GXTevAlphaArg a, GXTevAlphaArg b, GXTevAlphaArg c, GXTevAlphaArg d) { 42 | update_gx_state(g_gxState.tevStages[stageId].alphaPass, {a, b, c, d}); 43 | } 44 | 45 | void GXSetTevColorOp(GXTevStageID stageId, GXTevOp op, GXTevBias bias, GXTevScale scale, bool clamp, 46 | GXTevRegID outReg) { 47 | update_gx_state(g_gxState.tevStages[stageId].colorOp, {op, bias, scale, outReg, clamp}); 48 | } 49 | 50 | void GXSetTevAlphaOp(GXTevStageID stageId, GXTevOp op, GXTevBias bias, GXTevScale scale, bool clamp, 51 | GXTevRegID outReg) { 52 | update_gx_state(g_gxState.tevStages[stageId].alphaOp, {op, bias, scale, outReg, clamp}); 53 | } 54 | 55 | void GXSetTevColor(GXTevRegID id, GXColor color) { 56 | CHECK(id >= GX_TEVPREV && id < GX_MAX_TEVREG, "bad tevreg {}", static_cast(id)); 57 | update_gx_state(g_gxState.colorRegs[id], from_gx_color(color)); 58 | } 59 | 60 | void GXSetTevColorS10(GXTevRegID id, GXColorS10 color) { 61 | update_gx_state(g_gxState.colorRegs[id], aurora::Vec4{ 62 | static_cast(color.r) / 255.f, 63 | static_cast(color.g) / 255.f, 64 | static_cast(color.b) / 255.f, 65 | static_cast(color.a) / 255.f, 66 | }); 67 | } 68 | 69 | void GXSetAlphaCompare(GXCompare comp0, u8 ref0, GXAlphaOp op, GXCompare comp1, u8 ref1) { 70 | update_gx_state(g_gxState.alphaCompare, {comp0, ref0, op, comp1, ref1}); 71 | } 72 | 73 | void GXSetTevOrder(GXTevStageID id, GXTexCoordID tcid, GXTexMapID tmid, GXChannelID cid) { 74 | auto& stage = g_gxState.tevStages[id]; 75 | update_gx_state(stage.texCoordId, tcid); 76 | update_gx_state(stage.texMapId, tmid); 77 | update_gx_state(stage.channelId, cid); 78 | } 79 | 80 | void GXSetZTexture(GXZTexOp op, GXTexFmt fmt, u32 bias) { 81 | // TODO 82 | } 83 | 84 | void GXSetNumTevStages(u8 num) { update_gx_state(g_gxState.numTevStages, num); } 85 | 86 | void GXSetTevKColor(GXTevKColorID id, GXColor color) { 87 | CHECK(id >= GX_KCOLOR0 && id < GX_MAX_KCOLOR, "bad kcolor {}", static_cast(id)); 88 | update_gx_state(g_gxState.kcolors[id], from_gx_color(color)); 89 | } 90 | 91 | void GXSetTevKColorSel(GXTevStageID id, GXTevKColorSel sel) { update_gx_state(g_gxState.tevStages[id].kcSel, sel); } 92 | 93 | void GXSetTevKAlphaSel(GXTevStageID id, GXTevKAlphaSel sel) { update_gx_state(g_gxState.tevStages[id].kaSel, sel); } 94 | 95 | void GXSetTevSwapMode(GXTevStageID stageId, GXTevSwapSel rasSel, GXTevSwapSel texSel) { 96 | auto& stage = g_gxState.tevStages[stageId]; 97 | update_gx_state(stage.tevSwapRas, rasSel); 98 | update_gx_state(stage.tevSwapTex, texSel); 99 | } 100 | 101 | void GXSetTevSwapModeTable(GXTevSwapSel id, GXTevColorChan red, GXTevColorChan green, GXTevColorChan blue, 102 | GXTevColorChan alpha) { 103 | CHECK(id >= GX_TEV_SWAP0 && id < GX_MAX_TEVSWAP, "bad tev swap sel {}", static_cast(id)); 104 | update_gx_state(g_gxState.tevSwapTable[id], {red, green, blue, alpha}); 105 | } 106 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXTexture.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | #include "../../gfx/texture.hpp" 4 | 5 | #include 6 | 7 | extern "C" { 8 | void GXInitTexObj(GXTexObj* obj_, const void* data, u16 width, u16 height, u32 format, GXTexWrapMode wrapS, 9 | GXTexWrapMode wrapT, GXBool mipmap) { 10 | memset(obj_, 0, sizeof(GXTexObj)); 11 | auto* obj = reinterpret_cast(obj_); 12 | obj->data = data; 13 | obj->width = width; 14 | obj->height = height; 15 | obj->fmt = format; 16 | obj->wrapS = wrapS; 17 | obj->wrapT = wrapT; 18 | obj->hasMips = mipmap; 19 | // TODO default values? 20 | obj->minFilter = GX_LINEAR; 21 | obj->magFilter = GX_LINEAR; 22 | obj->minLod = 0.f; 23 | obj->maxLod = 0.f; 24 | obj->lodBias = 0.f; 25 | obj->biasClamp = false; 26 | obj->doEdgeLod = false; 27 | obj->maxAniso = GX_ANISO_4; 28 | obj->tlut = GX_TLUT0; 29 | const auto it = g_gxState.copyTextures.find(data); 30 | if (it != g_gxState.copyTextures.end()) { 31 | obj->ref = it->second; 32 | obj->dataInvalidated = false; 33 | } else { 34 | obj->dataInvalidated = true; 35 | } 36 | } 37 | 38 | void GXInitTexObjCI(GXTexObj* obj_, const void* data, u16 width, u16 height, GXCITexFmt format, GXTexWrapMode wrapS, 39 | GXTexWrapMode wrapT, GXBool mipmap, u32 tlut) { 40 | memset(obj_, 0, sizeof(GXTexObj)); 41 | auto* obj = reinterpret_cast(obj_); 42 | obj->data = data; 43 | obj->width = width; 44 | obj->height = height; 45 | obj->fmt = static_cast(format); 46 | obj->wrapS = wrapS; 47 | obj->wrapT = wrapT; 48 | obj->hasMips = mipmap; 49 | obj->tlut = static_cast(tlut); 50 | // TODO default values? 51 | obj->minFilter = GX_LINEAR; 52 | obj->magFilter = GX_LINEAR; 53 | obj->minLod = 0.f; 54 | obj->maxLod = 0.f; 55 | obj->lodBias = 0.f; 56 | obj->biasClamp = false; 57 | obj->doEdgeLod = false; 58 | obj->maxAniso = GX_ANISO_4; 59 | const auto it = g_gxState.copyTextures.find(data); 60 | if (it != g_gxState.copyTextures.end()) { 61 | obj->ref = it->second; 62 | obj->dataInvalidated = false; 63 | } else { 64 | obj->dataInvalidated = true; 65 | } 66 | } 67 | 68 | void GXInitTexObjLOD(GXTexObj* obj_, GXTexFilter minFilt, GXTexFilter magFilt, float minLod, float maxLod, 69 | float lodBias, GXBool biasClamp, GXBool doEdgeLod, GXAnisotropy maxAniso) { 70 | auto* obj = reinterpret_cast(obj_); 71 | obj->minFilter = minFilt; 72 | obj->magFilter = magFilt; 73 | obj->minLod = minLod; 74 | obj->maxLod = maxLod; 75 | obj->lodBias = lodBias; 76 | obj->biasClamp = biasClamp; 77 | obj->doEdgeLod = doEdgeLod; 78 | obj->maxAniso = maxAniso; 79 | } 80 | 81 | void GXInitTexObjData(GXTexObj* obj_, const void* data) { 82 | auto* obj = reinterpret_cast(obj_); 83 | const auto it = g_gxState.copyTextures.find(data); 84 | if (it != g_gxState.copyTextures.end()) { 85 | obj->ref = it->second; 86 | obj->dataInvalidated = false; 87 | } else { 88 | obj->data = data; 89 | obj->dataInvalidated = true; 90 | } 91 | } 92 | 93 | void GXInitTexObjWrapMode(GXTexObj* obj_, GXTexWrapMode wrapS, GXTexWrapMode wrapT) { 94 | auto* obj = reinterpret_cast(obj_); 95 | obj->wrapS = wrapS; 96 | obj->wrapT = wrapT; 97 | } 98 | 99 | void GXInitTexObjTlut(GXTexObj* obj_, u32 tlut) { 100 | auto* obj = reinterpret_cast(obj_); 101 | obj->tlut = static_cast(tlut); 102 | } 103 | 104 | // TODO GXInitTexObjFilter 105 | // TODO GXInitTexObjMaxLOD 106 | // TODO GXInitTexObjMinLOD 107 | // TODO GXInitTexObjLODBias 108 | // TODO GXInitTexObjBiasClamp 109 | // TODO GXInitTexObjEdgeLOD 110 | // TODO GXInitTexObjMaxAniso 111 | // TODO GXInitTexObjUserData 112 | // TODO GXGetTexObjUserData 113 | 114 | void GXLoadTexObj(GXTexObj* obj_, GXTexMapID id) { 115 | auto* obj = reinterpret_cast(obj_); 116 | if (!obj->ref) { 117 | const auto name = fmt::format("GXLoadTexObj_{}", obj->fmt); 118 | obj->ref = 119 | aurora::gfx::new_dynamic_texture_2d(obj->width, obj->height, u32(obj->maxLod) + 1, obj->fmt, name.c_str()); 120 | } 121 | if (obj->dataInvalidated) { 122 | aurora::gfx::write_texture(*obj->ref, {static_cast(obj->data), UINT32_MAX /* TODO */}); 123 | obj->dataInvalidated = false; 124 | } 125 | g_gxState.textures[id] = {*obj}; 126 | g_gxState.stateDirty = true; // TODO only if changed? 127 | } 128 | 129 | u32 GXGetTexBufferSize(u16 width, u16 height, u32 fmt, GXBool mips, u8 maxLod) { 130 | s32 shiftX = 0; 131 | s32 shiftY = 0; 132 | switch (fmt) { 133 | case GX_TF_I4: 134 | case GX_TF_C4: 135 | case GX_TF_CMPR: 136 | case GX_CTF_R4: 137 | case GX_CTF_Z4: 138 | shiftX = 3; 139 | shiftY = 3; 140 | break; 141 | case GX_TF_I8: 142 | case GX_TF_IA4: 143 | case GX_TF_C8: 144 | case GX_TF_Z8: 145 | case GX_CTF_RA4: 146 | case GX_CTF_A8: 147 | case GX_CTF_R8: 148 | case GX_CTF_G8: 149 | case GX_CTF_B8: 150 | case GX_CTF_Z8M: 151 | case GX_CTF_Z8L: 152 | shiftX = 3; 153 | shiftY = 2; 154 | break; 155 | case GX_TF_IA8: 156 | case GX_TF_RGB565: 157 | case GX_TF_RGB5A3: 158 | case GX_TF_RGBA8: 159 | case GX_TF_C14X2: 160 | case GX_TF_Z16: 161 | case GX_TF_Z24X8: 162 | case GX_CTF_RA8: 163 | case GX_CTF_RG8: 164 | case GX_CTF_GB8: 165 | case GX_CTF_Z16L: 166 | shiftX = 2; 167 | shiftY = 2; 168 | break; 169 | default: 170 | break; 171 | } 172 | u32 bitSize = fmt == GX_TF_RGBA8 || fmt == GX_TF_Z24X8 ? 64 : 32; 173 | u32 bufLen = 0; 174 | if (mips) { 175 | while (maxLod != 0) { 176 | const u32 tileX = ((width + (1 << shiftX) - 1) >> shiftX); 177 | const u32 tileY = ((height + (1 << shiftY) - 1) >> shiftY); 178 | bufLen += bitSize * tileX * tileY; 179 | 180 | if (width == 1 && height == 1) { 181 | return bufLen; 182 | } 183 | 184 | width = (width < 2) ? 1 : width / 2; 185 | height = (height < 2) ? 1 : height / 2; 186 | --maxLod; 187 | }; 188 | } else { 189 | const u32 tileX = ((width + (1 << shiftX) - 1) >> shiftX); 190 | const u32 tileY = ((height + (1 << shiftY) - 1) >> shiftY); 191 | bufLen = bitSize * tileX * tileY; 192 | } 193 | 194 | return bufLen; 195 | } 196 | 197 | void GXInitTlutObj(GXTlutObj* obj_, const void* data, GXTlutFmt format, u16 entries) { 198 | memset(obj_, 0, sizeof(GXTlutObj)); 199 | GXTexFmt texFmt; 200 | switch (format) { 201 | DEFAULT_FATAL("invalid tlut format {}", static_cast(format)); 202 | case GX_TL_IA8: 203 | texFmt = GX_TF_IA8; 204 | break; 205 | case GX_TL_RGB565: 206 | texFmt = GX_TF_RGB565; 207 | break; 208 | case GX_TL_RGB5A3: 209 | texFmt = GX_TF_RGB5A3; 210 | break; 211 | } 212 | auto* obj = reinterpret_cast(obj_); 213 | obj->ref = aurora::gfx::new_static_texture_2d( 214 | entries, 1, 1, texFmt, aurora::ArrayRef{static_cast(data), static_cast(entries) * 2}, 215 | true, "GXInitTlutObj"); 216 | } 217 | 218 | void GXLoadTlut(const GXTlutObj* obj_, GXTlut idx) { 219 | g_gxState.tluts[idx] = *reinterpret_cast(obj_); 220 | // TODO stateDirty? 221 | } 222 | 223 | // TODO GXInitTexCacheRegion 224 | // TODO GXInitTexPreLoadRegion 225 | // TODO GXInitTlutRegion 226 | // TODO GXInvalidateTexRegion 227 | 228 | void GXInvalidateTexAll() { 229 | // no-op? 230 | } 231 | 232 | // TODO GXPreLoadEntireTexture 233 | // TODO GXSetTexRegionCallback 234 | // TODO GXSetTlutRegionCallback 235 | // TODO GXLoadTexObjPreLoaded 236 | void GXSetTexCoordScaleManually(GXTexCoordID coord, GXBool enable, u16 ss, u16 ts) { 237 | // TODO 238 | } 239 | // TODO GXSetTexCoordCylWrap 240 | // TODO GXSetTexCoordBias 241 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/GXTransform.cpp: -------------------------------------------------------------------------------- 1 | #include "gx.hpp" 2 | 3 | extern "C" { 4 | 5 | void GXSetProjection(const void* mtx_, GXProjectionType type) { 6 | const auto& mtx = *reinterpret_cast*>(mtx_); 7 | g_gxState.projType = type; 8 | update_gx_state(g_gxState.proj, mtx); 9 | } 10 | 11 | // TODO GXSetProjectionv 12 | 13 | void GXLoadPosMtxImm(const void* mtx_, u32 id) { 14 | CHECK(id >= GX_PNMTX0 && id <= GX_PNMTX9, "invalid pn mtx {}", static_cast(id)); 15 | auto& state = g_gxState.pnMtx[id / 3]; 16 | const auto& mtx = *reinterpret_cast*>(mtx_); 17 | update_gx_state(state.pos, mtx); 18 | } 19 | 20 | // TODO GXLoadPosMtxIndx 21 | 22 | void GXLoadNrmMtxImm(const void* mtx_, u32 id) { 23 | CHECK(id >= GX_PNMTX0 && id <= GX_PNMTX9, "invalid pn mtx {}", static_cast(id)); 24 | auto& state = g_gxState.pnMtx[id / 3]; 25 | const auto& mtx = *reinterpret_cast*>(mtx_); 26 | update_gx_state(state.nrm, mtx); 27 | } 28 | 29 | // TODO GXLoadNrmMtxImm3x3 30 | // TODO GXLoadNrmMtxIndx3x3 31 | 32 | void GXSetCurrentMtx(u32 id) { 33 | CHECK(id >= GX_PNMTX0 && id <= GX_PNMTX9, "invalid pn mtx {}", id); 34 | update_gx_state(g_gxState.currentPnMtx, id / 3); 35 | } 36 | 37 | void GXLoadTexMtxImm(const void* mtx_, u32 id, GXTexMtxType type) { 38 | CHECK((id >= GX_TEXMTX0 && id <= GX_IDENTITY) || (id >= GX_PTTEXMTX0 && id <= GX_PTIDENTITY), "invalid tex mtx {}", 39 | id); 40 | if (id >= GX_PTTEXMTX0) { 41 | CHECK(type == GX_MTX3x4, "invalid pt mtx type {}", underlying(type)); 42 | const auto idx = (id - GX_PTTEXMTX0) / 3; 43 | const auto& mtx = *reinterpret_cast*>(mtx_); 44 | update_gx_state(g_gxState.ptTexMtxs[idx], mtx); 45 | } else { 46 | const auto idx = (id - GX_TEXMTX0) / 3; 47 | switch (type) { 48 | case GX_MTX3x4: { 49 | const auto& mtx = *reinterpret_cast*>(mtx_); 50 | update_gx_state(g_gxState.texMtxs[idx], mtx); 51 | break; 52 | } 53 | case GX_MTX2x4: { 54 | const auto& mtx = *reinterpret_cast*>(mtx_); 55 | update_gx_state(g_gxState.texMtxs[idx], mtx); 56 | break; 57 | } 58 | } 59 | } 60 | } 61 | 62 | // TODO GXLoadTexMtxIndx 63 | // TODO GXProject 64 | 65 | void GXSetViewport(float left, float top, float width, float height, float nearZ, float farZ) { 66 | aurora::gfx::set_viewport(left, top, width, height, nearZ, farZ); 67 | } 68 | 69 | void GXSetViewportJitter(float left, float top, float width, float height, float nearZ, float farZ, u32 field) { 70 | aurora::gfx::set_viewport(left, top, width, height, nearZ, farZ); 71 | } 72 | 73 | // TODO GXSetZScaleOffset 74 | // TODO GXSetScissorBoxOffset 75 | // TODO GXSetClipMode 76 | } -------------------------------------------------------------------------------- /lib/dolphin/gx/gx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../internal.hpp" 4 | #include "../../gfx/gx.hpp" 5 | 6 | static aurora::Module Log("aurora::gx"); 7 | 8 | using aurora::gfx::gx::g_gxState; 9 | 10 | template 11 | static inline void update_gx_state(T& val, T newVal) { 12 | if (val != newVal) { 13 | val = std::move(newVal); 14 | g_gxState.stateDirty = true; 15 | } 16 | } 17 | 18 | static inline aurora::Vec4 from_gx_color(GXColor color) { 19 | return { 20 | static_cast(color.r) / 255.f, 21 | static_cast(color.g) / 255.f, 22 | static_cast(color.b) / 255.f, 23 | static_cast(color.a) / 255.f, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /lib/dolphin/mtx.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define ASSERTLINE(line, cond) (void)0 4 | #define ASSERTMSGLINE(line, cond, msg) (void)0 5 | #define ASSERTMSG1LINE(line, cond, msg, arg1) (void)0 6 | #define ASSERTMSG2LINE(line, cond, msg, arg1, arg2) (void)0 7 | #define ASSERTMSGLINEV(line, cond, ...) (void)0 8 | 9 | void C_MTXIdentity(Mtx m) { 10 | ASSERTMSGLINE(189, m, "MtxIdentity(): NULL Mtx 'm' "); 11 | m[0][0] = 1; 12 | m[0][1] = 0; 13 | m[0][2] = 0; 14 | m[0][3] = 0; 15 | m[1][0] = 0; 16 | m[1][1] = 1; 17 | m[1][2] = 0; 18 | m[1][3] = 0; 19 | m[2][0] = 0; 20 | m[2][1] = 0; 21 | m[2][2] = 1; 22 | m[2][3] = 0; 23 | } 24 | 25 | void C_MTXCopy(const Mtx src, Mtx dst) { 26 | ASSERTMSGLINE(250, src, "MTXCopy(): NULL MtxPtr 'src' "); 27 | ASSERTMSGLINE(251, dst, "MTXCopy(): NULL MtxPtr 'dst' "); 28 | if (src != dst) { 29 | dst[0][0] = src[0][0]; 30 | dst[0][1] = src[0][1]; 31 | dst[0][2] = src[0][2]; 32 | dst[0][3] = src[0][3]; 33 | dst[1][0] = src[1][0]; 34 | dst[1][1] = src[1][1]; 35 | dst[1][2] = src[1][2]; 36 | dst[1][3] = src[1][3]; 37 | dst[2][0] = src[2][0]; 38 | dst[2][1] = src[2][1]; 39 | dst[2][2] = src[2][2]; 40 | dst[2][3] = src[2][3]; 41 | } 42 | } 43 | 44 | void C_MTXConcat(const Mtx a, const Mtx b, Mtx ab) { 45 | Mtx mTmp; 46 | MtxPtr m; 47 | 48 | ASSERTMSGLINE(324, a, "MTXConcat(): NULL MtxPtr 'a' "); 49 | ASSERTMSGLINE(325, b, "MTXConcat(): NULL MtxPtr 'b' "); 50 | ASSERTMSGLINE(326, ab, "MTXConcat(): NULL MtxPtr 'ab' "); 51 | 52 | if (ab == a || ab == b) { 53 | m = mTmp; 54 | } else { 55 | m = ab; 56 | } 57 | 58 | m[0][0] = 0 + a[0][2] * b[2][0] + ((a[0][0] * b[0][0]) + (a[0][1] * b[1][0])); 59 | m[0][1] = 0 + a[0][2] * b[2][1] + ((a[0][0] * b[0][1]) + (a[0][1] * b[1][1])); 60 | m[0][2] = 0 + a[0][2] * b[2][2] + ((a[0][0] * b[0][2]) + (a[0][1] * b[1][2])); 61 | m[0][3] = a[0][3] + (a[0][2] * b[2][3] + (a[0][0] * b[0][3] + (a[0][1] * b[1][3]))); 62 | 63 | m[1][0] = 0 + a[1][2] * b[2][0] + ((a[1][0] * b[0][0]) + (a[1][1] * b[1][0])); 64 | m[1][1] = 0 + a[1][2] * b[2][1] + ((a[1][0] * b[0][1]) + (a[1][1] * b[1][1])); 65 | m[1][2] = 0 + a[1][2] * b[2][2] + ((a[1][0] * b[0][2]) + (a[1][1] * b[1][2])); 66 | m[1][3] = a[1][3] + (a[1][2] * b[2][3] + (a[1][0] * b[0][3] + (a[1][1] * b[1][3]))); 67 | 68 | m[2][0] = 0 + a[2][2] * b[2][0] + ((a[2][0] * b[0][0]) + (a[2][1] * b[1][0])); 69 | m[2][1] = 0 + a[2][2] * b[2][1] + ((a[2][0] * b[0][1]) + (a[2][1] * b[1][1])); 70 | m[2][2] = 0 + a[2][2] * b[2][2] + ((a[2][0] * b[0][2]) + (a[2][1] * b[1][2])); 71 | m[2][3] = a[2][3] + (a[2][2] * b[2][3] + (a[2][0] * b[0][3] + (a[2][1] * b[1][3]))); 72 | 73 | if (m == mTmp) { 74 | C_MTXCopy(mTmp, ab); 75 | } 76 | } 77 | 78 | u32 C_MTXInvXpose(const Mtx src, Mtx invX) { 79 | Mtx mTmp; 80 | MtxPtr m; 81 | f32 det; 82 | 83 | ASSERTMSGLINE(1185, src, "MTXInvXpose(): NULL MtxPtr 'src' "); 84 | ASSERTMSGLINE(1186, invX, "MTXInvXpose(): NULL MtxPtr 'invX' "); 85 | 86 | if (src == invX) { 87 | m = mTmp; 88 | } else { 89 | m = invX; 90 | } 91 | det = ((((src[2][1] * (src[0][2] * src[1][0])) 92 | + ((src[2][2] * (src[0][0] * src[1][1])) 93 | + (src[2][0] * (src[0][1] * src[1][2])))) 94 | - (src[0][2] * (src[2][0] * src[1][1]))) 95 | - (src[2][2] * (src[1][0] * src[0][1]))) 96 | - (src[1][2] * (src[0][0] * src[2][1])); 97 | if (0 == det) { 98 | return 0; 99 | } 100 | det = 1 / det; 101 | m[0][0] = (det * +((src[1][1] * src[2][2]) - (src[2][1] * src[1][2]))); 102 | m[0][1] = (det * -((src[1][0] * src[2][2]) - (src[2][0] * src[1][2]))); 103 | m[0][2] = (det * +((src[1][0] * src[2][1]) - (src[2][0] * src[1][1]))); 104 | 105 | m[1][0] = (det * -((src[0][1] * src[2][2]) - (src[2][1] * src[0][2]))); 106 | m[1][1] = (det * +((src[0][0] * src[2][2]) - (src[2][0] * src[0][2]))); 107 | m[1][2] = (det * -((src[0][0] * src[2][1]) - (src[2][0] * src[0][1]))); 108 | 109 | m[2][0] = (det * +((src[0][1] * src[1][2]) - (src[1][1] * src[0][2]))); 110 | m[2][1] = (det * -((src[0][0] * src[1][2]) - (src[1][0] * src[0][2]))); 111 | m[2][2] = (det * +((src[0][0] * src[1][1]) - (src[1][0] * src[0][1]))); 112 | 113 | m[0][3] = 0; 114 | m[1][3] = 0; 115 | m[2][3] = 0; 116 | 117 | if (m == mTmp) { 118 | C_MTXCopy(mTmp, invX); 119 | } 120 | return 1; 121 | } 122 | 123 | void C_MTXMultVec(const Mtx m, const Vec* src, Vec* dst) { 124 | Vec vTmp; 125 | 126 | ASSERTMSGLINE(66, m, "MTXMultVec(): NULL MtxPtr 'm' "); 127 | ASSERTMSGLINE(67, src, "MTXMultVec(): NULL VecPtr 'src' "); 128 | ASSERTMSGLINE(68, dst, "MTXMultVec(): NULL VecPtr 'dst' "); 129 | 130 | vTmp.x = m[0][3] + ((m[0][2] * src->z) + ((m[0][0] * src->x) + (m[0][1] * src->y))); 131 | vTmp.y = m[1][3] + ((m[1][2] * src->z) + ((m[1][0] * src->x) + (m[1][1] * src->y))); 132 | vTmp.z = m[2][3] + ((m[2][2] * src->z) + ((m[2][0] * src->x) + (m[2][1] * src->y))); 133 | dst->x = vTmp.x; 134 | dst->y = vTmp.y; 135 | dst->z = vTmp.z; 136 | } 137 | 138 | void C_MTXMultVecSR(const Mtx m, const Vec* src, Vec* dst) { 139 | Vec vTmp; 140 | 141 | ASSERTMSGLINE(313, m, "MTXMultVecSR(): NULL MtxPtr 'm' "); 142 | ASSERTMSGLINE(314, src, "MTXMultVecSR(): NULL VecPtr 'src' "); 143 | ASSERTMSGLINE(315, dst, "MTXMultVecSR(): NULL VecPtr 'dst' "); 144 | 145 | vTmp.x = (m[0][2] * src->z) + ((m[0][0] * src->x) + (m[0][1] * src->y)); 146 | vTmp.y = (m[1][2] * src->z) + ((m[1][0] * src->x) + (m[1][1] * src->y)); 147 | vTmp.z = (m[2][2] * src->z) + ((m[2][0] * src->x) + (m[2][1] * src->y)); 148 | dst->x = vTmp.x; 149 | dst->y = vTmp.y; 150 | dst->z = vTmp.z; 151 | } 152 | 153 | void C_MTXTrans(Mtx m, f32 xT, f32 yT, f32 zT) { 154 | ASSERTMSGLINE(1866, m, "MTXTrans(): NULL MtxPtr 'm' "); 155 | m[0][0] = 1; 156 | m[0][1] = 0; 157 | m[0][2] = 0; 158 | m[0][3] = xT; 159 | m[1][0] = 0; 160 | m[1][1] = 1; 161 | m[1][2] = 0; 162 | m[1][3] = yT; 163 | m[2][0] = 0; 164 | m[2][1] = 0; 165 | m[2][2] = 1; 166 | m[2][3] = zT; 167 | } 168 | 169 | void C_MTXFrustum(Mtx44 m, f32 t, f32 b, f32 l, f32 r, f32 n, f32 f) { 170 | f32 tmp; 171 | 172 | ASSERTMSGLINE(105, m, "MTXFrustum(): NULL Mtx44Ptr 'm' "); 173 | ASSERTMSGLINE(106, t != b, "MTXFrustum(): 't' and 'b' clipping planes are equal "); 174 | ASSERTMSGLINE(107, l != r, "MTXFrustum(): 'l' and 'r' clipping planes are equal "); 175 | ASSERTMSGLINE(108, n != f, "MTXFrustum(): 'n' and 'f' clipping planes are equal "); 176 | tmp = 1 / (r - l); 177 | m[0][0] = (2 * n * tmp); 178 | m[0][1] = 0; 179 | m[0][2] = (tmp * (r + l)); 180 | m[0][3] = 0; 181 | tmp = 1 / (t - b); 182 | m[1][0] = 0; 183 | m[1][1] = (2 * n * tmp); 184 | m[1][2] = (tmp * (t + b)); 185 | m[1][3] = 0; 186 | m[2][0] = 0; 187 | m[2][1] = 0; 188 | tmp = 1 / (f - n); 189 | m[2][2] = (-n * tmp); 190 | m[2][3] = (tmp * -(f * n)); 191 | m[3][0] = 0; 192 | m[3][1] = 0; 193 | m[3][2] = -1; 194 | m[3][3] = 0; 195 | } 196 | 197 | void C_MTXOrtho(Mtx44 m, f32 t, f32 b, f32 l, f32 r, f32 n, f32 f) { 198 | f32 tmp; 199 | 200 | ASSERTMSGLINE(254, m, "MTXOrtho(): NULL Mtx44Ptr 'm' "); 201 | ASSERTMSGLINE(255, t != b, "MTXOrtho(): 't' and 'b' clipping planes are equal "); 202 | ASSERTMSGLINE(256, l != r, "MTXOrtho(): 'l' and 'r' clipping planes are equal "); 203 | ASSERTMSGLINE(257, n != f, "MTXOrtho(): 'n' and 'f' clipping planes are equal "); 204 | tmp = 1 / (r - l); 205 | m[0][0] = 2 * tmp; 206 | m[0][1] = 0; 207 | m[0][2] = 0; 208 | m[0][3] = (tmp * -(r + l)); 209 | tmp = 1 / (t - b); 210 | m[1][0] = 0; 211 | m[1][1] = 2 * tmp; 212 | m[1][2] = 0; 213 | m[1][3] = (tmp * -(t + b)); 214 | m[2][0] = 0; 215 | m[2][1] = 0; 216 | tmp = 1 / (f - n); 217 | m[2][2] = (-1 * tmp); 218 | m[2][3] = (-f * tmp); 219 | m[3][0] = 0; 220 | m[3][1] = 0; 221 | m[3][2] = 0; 222 | m[3][3] = 1; 223 | } -------------------------------------------------------------------------------- /lib/dolphin/si/si.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../input.hpp" 3 | 4 | #include 5 | 6 | uint32_t SIProbe(int32_t chan) { 7 | auto* const controller = aurora::input::get_controller_for_player(chan); 8 | if (controller == nullptr) { 9 | return SI_ERROR_NO_RESPONSE; 10 | } 11 | 12 | if (controller->m_isGameCube) { 13 | auto level = SDL_GetJoystickPowerInfo(SDL_GetGamepadJoystick(controller->m_controller), nullptr); 14 | if (level == SDL_POWERSTATE_UNKNOWN) { 15 | return SI_GC_WAVEBIRD; 16 | } 17 | } 18 | 19 | return SI_GC_CONTROLLER; 20 | } 21 | -------------------------------------------------------------------------------- /lib/dolphin/vi/vi.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../../window.hpp" 4 | 5 | extern "C" { 6 | void VIInit() {} 7 | u32 VIGetTvFormat() { return 0; } 8 | void VIFlush() {} 9 | 10 | void VISetWindowTitle(const char* title) { aurora::window::set_title(title); } 11 | void VISetWindowFullscreen(bool fullscreen) { aurora::window::set_fullscreen(fullscreen); } 12 | bool VIGetWindowFullscreen() { return aurora::window::get_fullscreen(); } 13 | } 14 | -------------------------------------------------------------------------------- /lib/gfx/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../internal.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #define XXH_STATIC_LINKING_ONLY 14 | #include 15 | 16 | namespace aurora { 17 | #if INTPTR_MAX == INT32_MAX 18 | using HashType = XXH32_hash_t; 19 | #else 20 | using HashType = XXH64_hash_t; 21 | #endif 22 | static inline HashType xxh3_hash_s(const void* input, size_t len, HashType seed = 0) { 23 | return static_cast(XXH3_64bits_withSeed(input, len, seed)); 24 | } 25 | template 26 | static inline HashType xxh3_hash(const T& input, HashType seed = 0) { 27 | // Validate that the type has no padding bytes, which can easily cause 28 | // hash mismatches. This also disallows floats, but that's okay for us. 29 | static_assert(std::has_unique_object_representations_v); 30 | return xxh3_hash_s(&input, sizeof(T), seed); 31 | } 32 | 33 | class Hasher { 34 | public: 35 | explicit Hasher(const XXH64_hash_t seed = 0) { 36 | XXH3_INITSTATE(&state); 37 | XXH3_64bits_reset_withSeed(&state, seed); 38 | } 39 | 40 | void update(const void* data, const size_t size) { XXH3_64bits_update(&state, data, size); } 41 | 42 | template 43 | void update(const T& data) { 44 | static_assert(std::has_unique_object_representations_v); 45 | update(&data, sizeof(T)); 46 | } 47 | 48 | [[nodiscard]] XXH64_hash_t digest() const { return XXH3_64bits_digest(&state); } 49 | 50 | private: 51 | XXH3_state_t state; 52 | }; 53 | 54 | class ByteBuffer { 55 | public: 56 | ByteBuffer() noexcept = default; 57 | explicit ByteBuffer(size_t size) noexcept 58 | : m_data(static_cast(calloc(1, size))), m_length(size), m_capacity(size) {} 59 | explicit ByteBuffer(uint8_t* data, size_t size) noexcept : m_data(data), m_capacity(size), m_owned(false) {} 60 | ~ByteBuffer() noexcept { 61 | if (m_data != nullptr && m_owned) { 62 | free(m_data); 63 | } 64 | } 65 | ByteBuffer(ByteBuffer&& rhs) noexcept 66 | : m_data(rhs.m_data), m_length(rhs.m_length), m_capacity(rhs.m_capacity), m_owned(rhs.m_owned) { 67 | rhs.m_data = nullptr; 68 | rhs.m_length = 0; 69 | rhs.m_capacity = 0; 70 | rhs.m_owned = true; 71 | } 72 | ByteBuffer& operator=(ByteBuffer&& rhs) noexcept { 73 | if (m_data != nullptr && m_owned) { 74 | free(m_data); 75 | } 76 | m_data = rhs.m_data; 77 | m_length = rhs.m_length; 78 | m_capacity = rhs.m_capacity; 79 | m_owned = rhs.m_owned; 80 | rhs.m_data = nullptr; 81 | rhs.m_length = 0; 82 | rhs.m_capacity = 0; 83 | rhs.m_owned = true; 84 | return *this; 85 | } 86 | ByteBuffer(ByteBuffer const&) = delete; 87 | ByteBuffer& operator=(ByteBuffer const&) = delete; 88 | 89 | [[nodiscard]] uint8_t* data() noexcept { return m_data; } 90 | [[nodiscard]] const uint8_t* data() const noexcept { return m_data; } 91 | [[nodiscard]] size_t size() const noexcept { return m_length; } 92 | [[nodiscard]] bool empty() const noexcept { return m_length == 0; } 93 | 94 | void append(const void* data, size_t size) { 95 | resize(m_length + size, false); 96 | memcpy(m_data + m_length, data, size); 97 | m_length += size; 98 | } 99 | 100 | template 101 | void append(const T& obj) { 102 | append(&obj, sizeof(T)); 103 | } 104 | 105 | void append_zeroes(size_t size) { 106 | resize(m_length + size, true); 107 | m_length += size; 108 | } 109 | 110 | void clear() { 111 | if (m_data != nullptr && m_owned) { 112 | free(m_data); 113 | } 114 | m_data = nullptr; 115 | m_length = 0; 116 | m_capacity = 0; 117 | m_owned = true; 118 | } 119 | 120 | void reserve_extra(size_t size) { resize(m_length + size, true); } 121 | 122 | private: 123 | uint8_t* m_data = nullptr; 124 | size_t m_length = 0; 125 | size_t m_capacity = 0; 126 | bool m_owned = true; 127 | 128 | void resize(size_t size, bool zeroed) { 129 | if (size == 0) { 130 | clear(); 131 | } else if (m_data == nullptr) { 132 | if (zeroed) { 133 | m_data = static_cast(calloc(1, size)); 134 | } else { 135 | m_data = static_cast(malloc(size)); 136 | } 137 | m_owned = true; 138 | } else if (size > m_capacity) { 139 | if (!m_owned) { 140 | abort(); 141 | } 142 | m_data = static_cast(realloc(m_data, size)); 143 | if (zeroed) { 144 | memset(m_data + m_capacity, 0, size - m_capacity); 145 | } 146 | } else { 147 | return; 148 | } 149 | m_capacity = size; 150 | } 151 | }; 152 | } // namespace aurora 153 | 154 | namespace aurora::gfx { 155 | extern wgpu::Buffer g_vertexBuffer; 156 | extern wgpu::Buffer g_uniformBuffer; 157 | extern wgpu::Buffer g_indexBuffer; 158 | extern wgpu::Buffer g_storageBuffer; 159 | 160 | using BindGroupRef = HashType; 161 | using PipelineRef = HashType; 162 | using SamplerRef = HashType; 163 | using ShaderRef = HashType; 164 | struct Range { 165 | uint32_t offset = 0; 166 | uint32_t size = 0; 167 | 168 | bool operator==(const Range& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } 169 | bool operator!=(const Range& rhs) const { return !(*this == rhs); } 170 | }; 171 | 172 | struct ClipRect { 173 | int32_t x; 174 | int32_t y; 175 | int32_t width; 176 | int32_t height; 177 | 178 | bool operator==(const ClipRect& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } 179 | bool operator!=(const ClipRect& rhs) const { return !(*this == rhs); } 180 | }; 181 | 182 | struct TextureRef; 183 | using TextureHandle = std::shared_ptr; 184 | 185 | enum class ShaderType : uint8_t { 186 | Model = 1, 187 | }; 188 | 189 | void initialize(); 190 | void shutdown(); 191 | 192 | void begin_frame(); 193 | void end_frame(const wgpu::CommandEncoder& cmd); 194 | void render(wgpu::CommandEncoder& cmd); 195 | void render_pass(const wgpu::RenderPassEncoder& pass, uint32_t idx); 196 | void map_staging_buffer(); 197 | void resolve_pass(TextureHandle texture, ClipRect rect, bool clear, Vec4 clearColor); 198 | 199 | Range push_verts(const uint8_t* data, size_t length); 200 | template 201 | static inline Range push_verts(ArrayRef data) { 202 | return push_verts(reinterpret_cast(data.data()), data.size() * sizeof(T)); 203 | } 204 | Range push_indices(const uint8_t* data, size_t length); 205 | template 206 | static inline Range push_indices(ArrayRef data) { 207 | return push_indices(reinterpret_cast(data.data()), data.size() * sizeof(T)); 208 | } 209 | Range push_uniform(const uint8_t* data, size_t length); 210 | template 211 | static inline Range push_uniform(const T& data) { 212 | return push_uniform(reinterpret_cast(&data), sizeof(T)); 213 | } 214 | Range push_storage(const uint8_t* data, size_t length); 215 | template 216 | static inline Range push_storage(ArrayRef data) { 217 | return push_storage(reinterpret_cast(data.data()), data.size() * sizeof(T)); 218 | } 219 | template 220 | static inline Range push_storage(const T& data) { 221 | return push_storage(reinterpret_cast(&data), sizeof(T)); 222 | } 223 | Range push_texture_data(const uint8_t* data, size_t length, uint32_t bytesPerRow, uint32_t rowsPerImage); 224 | std::pair map_verts(size_t length); 225 | std::pair map_indices(size_t length); 226 | std::pair map_uniform(size_t length); 227 | std::pair map_storage(size_t length); 228 | 229 | template 230 | const State& get_state(); 231 | template 232 | void push_draw_command(DrawData data); 233 | template 234 | void merge_draw_command(DrawData data); 235 | 236 | template 237 | PipelineRef pipeline_ref(PipelineConfig config); 238 | bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass); 239 | 240 | BindGroupRef bind_group_ref(const wgpu::BindGroupDescriptor& descriptor); 241 | wgpu::BindGroup find_bind_group(BindGroupRef id); 242 | 243 | wgpu::Sampler sampler_ref(const wgpu::SamplerDescriptor& descriptor); 244 | 245 | uint32_t align_uniform(uint32_t value); 246 | 247 | void set_viewport(float left, float top, float width, float height, float znear, float zfar) noexcept; 248 | void set_scissor(uint32_t x, uint32_t y, uint32_t w, uint32_t h) noexcept; 249 | } // namespace aurora::gfx 250 | -------------------------------------------------------------------------------- /lib/gfx/display_list.cpp: -------------------------------------------------------------------------------- 1 | #include "display_list.hpp" 2 | 3 | #include "gx.hpp" 4 | #include "gx_fmt.hpp" 5 | 6 | namespace aurora::gfx::gx { 7 | static Module Log("aurora::gfx::model"); 8 | 9 | struct DisplayListCache { 10 | ByteBuffer vtxBuf; 11 | ByteBuffer idxBuf; 12 | GXVtxFmt fmt; 13 | 14 | DisplayListCache(ByteBuffer&& vtxBuf, ByteBuffer&& idxBuf, GXVtxFmt fmt) 15 | : vtxBuf(std::move(vtxBuf)), idxBuf(std::move(idxBuf)), fmt(fmt) {} 16 | }; 17 | 18 | static absl::flat_hash_map sCachedDisplayLists; 19 | 20 | static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u16 vtxCount, bool bigEndian) { 21 | using gx::g_gxState; 22 | struct { 23 | u8 count; 24 | GXCompType type; 25 | } attrArrays[GX_VA_MAX_ATTR] = {}; 26 | u32 vtxSize = 0; 27 | u32 outVtxSize = 0; 28 | 29 | // Calculate attribute offsets and vertex size 30 | for (int attr = 0; attr < GX_VA_MAX_ATTR; attr++) { 31 | const auto& attrFmt = g_gxState.vtxFmts[vtxfmt].attrs[attr]; 32 | switch (g_gxState.vtxDesc[attr]) { 33 | DEFAULT_FATAL("unhandled attribute type {}", g_gxState.vtxDesc[attr]); 34 | case GX_NONE: 35 | break; 36 | case GX_DIRECT: 37 | #define COMBINE(val1, val2, val3) (((val1) << 16) | ((val2) << 8) | (val3)) 38 | switch (COMBINE(attr, attrFmt.cnt, attrFmt.type)) { 39 | DEFAULT_FATAL("not handled: attr {}, cnt {}, type {}", attr, attrFmt.cnt, attrFmt.type); 40 | case COMBINE(GX_VA_POS, GX_POS_XYZ, GX_F32): 41 | case COMBINE(GX_VA_NRM, GX_NRM_XYZ, GX_F32): 42 | attrArrays[attr].count = 3; 43 | attrArrays[attr].type = GX_F32; 44 | vtxSize += 12; 45 | outVtxSize += 12; 46 | break; 47 | case COMBINE(GX_VA_POS, GX_POS_XYZ, GX_S16): 48 | case COMBINE(GX_VA_NRM, GX_NRM_XYZ, GX_S16): 49 | attrArrays[attr].count = 3; 50 | attrArrays[attr].type = GX_S16; 51 | vtxSize += 6; 52 | outVtxSize += 12; 53 | break; 54 | case COMBINE(GX_VA_TEX0, GX_TEX_ST, GX_F32): 55 | case COMBINE(GX_VA_TEX1, GX_TEX_ST, GX_F32): 56 | case COMBINE(GX_VA_TEX2, GX_TEX_ST, GX_F32): 57 | case COMBINE(GX_VA_TEX3, GX_TEX_ST, GX_F32): 58 | case COMBINE(GX_VA_TEX4, GX_TEX_ST, GX_F32): 59 | case COMBINE(GX_VA_TEX5, GX_TEX_ST, GX_F32): 60 | case COMBINE(GX_VA_TEX6, GX_TEX_ST, GX_F32): 61 | case COMBINE(GX_VA_TEX7, GX_TEX_ST, GX_F32): 62 | attrArrays[attr].count = 2; 63 | attrArrays[attr].type = GX_F32; 64 | vtxSize += 8; 65 | outVtxSize += 8; 66 | break; 67 | case COMBINE(GX_VA_TEX0, GX_TEX_ST, GX_S16): 68 | case COMBINE(GX_VA_TEX1, GX_TEX_ST, GX_S16): 69 | case COMBINE(GX_VA_TEX2, GX_TEX_ST, GX_S16): 70 | case COMBINE(GX_VA_TEX3, GX_TEX_ST, GX_S16): 71 | case COMBINE(GX_VA_TEX4, GX_TEX_ST, GX_S16): 72 | case COMBINE(GX_VA_TEX5, GX_TEX_ST, GX_S16): 73 | case COMBINE(GX_VA_TEX6, GX_TEX_ST, GX_S16): 74 | case COMBINE(GX_VA_TEX7, GX_TEX_ST, GX_S16): 75 | attrArrays[attr].count = 2; 76 | attrArrays[attr].type = GX_S16; 77 | vtxSize += 4; 78 | outVtxSize += 8; 79 | break; 80 | case COMBINE(GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8): 81 | case COMBINE(GX_VA_CLR1, GX_CLR_RGBA, GX_RGBA8): 82 | attrArrays[attr].count = 4; 83 | attrArrays[attr].type = GX_RGBA8; 84 | vtxSize += 4; 85 | outVtxSize += 16; 86 | break; 87 | } 88 | #undef COMBINE 89 | break; 90 | case GX_INDEX8: 91 | ++vtxSize; 92 | outVtxSize += 2; 93 | break; 94 | case GX_INDEX16: 95 | vtxSize += 2; 96 | outVtxSize += 2; 97 | break; 98 | } 99 | } 100 | // Align to 4 101 | int rem = outVtxSize % 4; 102 | int padding = 0; 103 | if (rem != 0) { 104 | padding = 4 - rem; 105 | outVtxSize += padding; 106 | } 107 | 108 | // Build vertex buffer 109 | buf.reserve_extra(vtxCount * outVtxSize); 110 | std::array out{}; 111 | for (u32 v = 0; v < vtxCount; ++v) { 112 | for (int attr = 0; attr < GX_VA_MAX_ATTR; attr++) { 113 | if (g_gxState.vtxDesc[attr] == GX_INDEX8) { 114 | buf.append(static_cast(*ptr)); 115 | ++ptr; 116 | } else if (g_gxState.vtxDesc[attr] == GX_INDEX16) { 117 | const auto value = *reinterpret_cast(ptr); 118 | buf.append(bigEndian ? bswap(value) : value); 119 | ptr += 2; 120 | } 121 | if (g_gxState.vtxDesc[attr] != GX_DIRECT) { 122 | continue; 123 | } 124 | const auto& attrFmt = g_gxState.vtxFmts[vtxfmt].attrs[attr]; 125 | u8 count = attrArrays[attr].count; 126 | switch (attrArrays[attr].type) { 127 | case GX_U8: 128 | for (int i = 0; i < count; ++i) { 129 | const auto value = reinterpret_cast(ptr)[i]; 130 | out[i] = static_cast(value) / static_cast(1 << attrFmt.frac); 131 | } 132 | buf.append(out.data(), sizeof(f32) * count); 133 | ptr += count; 134 | break; 135 | case GX_S8: 136 | for (int i = 0; i < count; ++i) { 137 | const auto value = reinterpret_cast(ptr)[i]; 138 | out[i] = static_cast(value) / static_cast(1 << attrFmt.frac); 139 | } 140 | buf.append(out.data(), sizeof(f32) * count); 141 | ptr += count; 142 | break; 143 | case GX_U16: 144 | for (int i = 0; i < count; ++i) { 145 | auto value = reinterpret_cast(ptr)[i]; 146 | out[i] = static_cast(bigEndian ? bswap(value) : value) / static_cast(1 << attrFmt.frac); 147 | } 148 | buf.append(out.data(), sizeof(f32) * count); 149 | ptr += count * sizeof(u16); 150 | break; 151 | case GX_S16: 152 | for (int i = 0; i < count; ++i) { 153 | const auto value = reinterpret_cast(ptr)[i]; 154 | out[i] = static_cast(bigEndian ? bswap(value) : value) / static_cast(1 << attrFmt.frac); 155 | } 156 | buf.append(out.data(), sizeof(f32) * count); 157 | ptr += count * sizeof(s16); 158 | break; 159 | case GX_F32: 160 | for (int i = 0; i < count; ++i) { 161 | const auto value = reinterpret_cast(ptr)[i]; 162 | out[i] = bigEndian ? bswap(value) : value; 163 | } 164 | buf.append(out.data(), sizeof(f32) * count); 165 | ptr += count * sizeof(f32); 166 | break; 167 | case GX_RGBA8: 168 | out[0] = static_cast(ptr[0]) / 255.f; 169 | out[1] = static_cast(ptr[1]) / 255.f; 170 | out[2] = static_cast(ptr[2]) / 255.f; 171 | out[3] = static_cast(ptr[3]) / 255.f; 172 | buf.append(out.data(), sizeof(f32) * 4); 173 | ptr += sizeof(u32); 174 | break; 175 | } 176 | } 177 | if (padding > 0) { 178 | buf.append_zeroes(padding); 179 | } 180 | } 181 | 182 | return vtxSize; 183 | } 184 | 185 | static u16 prepare_idx_buffer(ByteBuffer& buf, GXPrimitive prim, u16 vtxStart, u16 vtxCount) { 186 | u16 numIndices = 0; 187 | if (prim == GX_QUADS) { 188 | buf.reserve_extra((vtxCount / 4) * 6 * sizeof(u16)); 189 | 190 | for (u16 v = 0; v < vtxCount; v += 4) { 191 | u16 idx0 = vtxStart + v; 192 | u16 idx1 = vtxStart + v + 1; 193 | u16 idx2 = vtxStart + v + 2; 194 | u16 idx3 = vtxStart + v + 3; 195 | 196 | buf.append(idx0); 197 | buf.append(idx1); 198 | buf.append(idx2); 199 | numIndices += 3; 200 | 201 | buf.append(idx2); 202 | buf.append(idx3); 203 | buf.append(idx0); 204 | numIndices += 3; 205 | } 206 | } else if (prim == GX_TRIANGLES) { 207 | buf.reserve_extra(vtxCount * sizeof(u16)); 208 | for (u16 v = 0; v < vtxCount; ++v) { 209 | const u16 idx = vtxStart + v; 210 | buf.append(idx); 211 | ++numIndices; 212 | } 213 | } else if (prim == GX_TRIANGLEFAN) { 214 | buf.reserve_extra(((u32(vtxCount) - 3) * 3 + 3) * sizeof(u16)); 215 | for (u16 v = 0; v < vtxCount; ++v) { 216 | const u16 idx = vtxStart + v; 217 | if (v < 3) { 218 | buf.append(idx); 219 | ++numIndices; 220 | continue; 221 | } 222 | buf.append(std::array{vtxStart, static_cast(idx - 1), idx}); 223 | numIndices += 3; 224 | } 225 | } else if (prim == GX_TRIANGLESTRIP) { 226 | buf.reserve_extra(((static_cast(vtxCount) - 3) * 3 + 3) * sizeof(u16)); 227 | for (u16 v = 0; v < vtxCount; ++v) { 228 | const u16 idx = vtxStart + v; 229 | if (v < 3) { 230 | buf.append(idx); 231 | ++numIndices; 232 | continue; 233 | } 234 | if ((v & 1) == 0) { 235 | buf.append(std::array{static_cast(idx - 2), static_cast(idx - 1), idx}); 236 | } else { 237 | buf.append(std::array{static_cast(idx - 1), static_cast(idx - 2), idx}); 238 | } 239 | numIndices += 3; 240 | } 241 | } else 242 | UNLIKELY FATAL("unsupported primitive type {}", static_cast(prim)); 243 | return numIndices; 244 | } 245 | 246 | auto process_display_list(const u8* dlStart, u32 dlSize, bool bigEndian) -> DisplayListResult { 247 | const auto hash = xxh3_hash_s(dlStart, dlSize, 0); 248 | Range vertRange, idxRange; 249 | u32 numIndices = 0; 250 | GXVtxFmt fmt = GX_MAX_VTXFMT; 251 | auto it = sCachedDisplayLists.find(hash); 252 | if (it != sCachedDisplayLists.end()) { 253 | const auto& cache = it->second; 254 | numIndices = cache.idxBuf.size() / 2; 255 | vertRange = push_verts(cache.vtxBuf.data(), cache.vtxBuf.size()); 256 | idxRange = push_indices(cache.idxBuf.data(), cache.idxBuf.size()); 257 | fmt = cache.fmt; 258 | } else { 259 | const u8* data = dlStart; 260 | u32 pos = 0; 261 | ByteBuffer vtxBuf; 262 | ByteBuffer idxBuf; 263 | u16 vtxStart = 0; 264 | 265 | while (pos < dlSize) { 266 | u8 cmd = data[pos++]; 267 | 268 | u8 opcode = cmd & GX_OPCODE_MASK; 269 | switch (opcode) { 270 | DEFAULT_FATAL("unimplemented opcode: {}", opcode); 271 | case GX_NOP: 272 | continue; 273 | case GX_DRAW_QUADS: 274 | case GX_DRAW_TRIANGLES: 275 | case GX_DRAW_TRIANGLE_STRIP: 276 | case GX_DRAW_TRIANGLE_FAN: { 277 | const auto prim = static_cast(opcode); 278 | const auto newFmt = static_cast(cmd & GX_VAT_MASK); 279 | if (fmt != GX_MAX_VTXFMT && fmt != newFmt) { 280 | FATAL("Vertex format changed mid-display list: {} -> {}", fmt, newFmt); 281 | } 282 | fmt = newFmt; 283 | u16 vtxCount = *reinterpret_cast(data + pos); 284 | if (bigEndian) 285 | vtxCount = bswap(vtxCount); 286 | pos += 2; 287 | pos += vtxCount * prepare_vtx_buffer(vtxBuf, fmt, data + pos, vtxCount, bigEndian); 288 | numIndices += prepare_idx_buffer(idxBuf, prim, vtxStart, vtxCount); 289 | vtxStart += vtxCount; 290 | break; 291 | } 292 | case GX_DRAW_LINES: 293 | case GX_DRAW_LINE_STRIP: 294 | case GX_DRAW_POINTS: 295 | FATAL("unimplemented prim type: {}", opcode); 296 | break; 297 | } 298 | } 299 | vertRange = push_verts(vtxBuf.data(), vtxBuf.size()); 300 | idxRange = push_indices(idxBuf.data(), idxBuf.size()); 301 | sCachedDisplayLists.try_emplace(hash, std::move(vtxBuf), std::move(idxBuf), fmt); 302 | } 303 | 304 | return { 305 | .vertRange = vertRange, 306 | .idxRange = idxRange, 307 | .numIndices = numIndices, 308 | .fmt = fmt, 309 | }; 310 | } 311 | } // namespace aurora::gfx::gx -------------------------------------------------------------------------------- /lib/gfx/display_list.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gx.hpp" 4 | 5 | namespace aurora::gfx::gx { 6 | struct DisplayListResult { 7 | Range vertRange; 8 | Range idxRange; 9 | u32 numIndices; 10 | GXVtxFmt fmt; 11 | }; 12 | 13 | auto process_display_list(const u8* dlStart, u32 dlSize, bool bigEndian) -> DisplayListResult; 14 | }; // namespace aurora::gfx::gx 15 | -------------------------------------------------------------------------------- /lib/gfx/model/shader.cpp: -------------------------------------------------------------------------------- 1 | #include "shader.hpp" 2 | 3 | #include "../../webgpu/gpu.hpp" 4 | #include "../gx_fmt.hpp" 5 | #include "../display_list.hpp" 6 | #include "../shader_info.hpp" 7 | 8 | #include 9 | 10 | namespace aurora::gfx::model { 11 | static Module Log("aurora::gfx::model"); 12 | 13 | void queue_surface(const u8* dlStart, u32 dlSize, bool bigEndian) noexcept { 14 | const auto result = aurora::gfx::gx::process_display_list(dlStart, dlSize, bigEndian); 15 | 16 | gx::BindGroupRanges ranges{}; 17 | for (int i = 0; i < GX_VA_MAX_ATTR; ++i) { 18 | if (gx::g_gxState.vtxDesc[i] != GX_INDEX8 && gx::g_gxState.vtxDesc[i] != GX_INDEX16) { 19 | continue; 20 | } 21 | auto& array = gx::g_gxState.arrays[i]; 22 | if (array.cachedRange.size > 0) { 23 | // Use the currently cached range 24 | ranges.vaRanges[i] = array.cachedRange; 25 | } else { 26 | // Push array data to storage and cache range 27 | const auto range = push_storage(static_cast(array.data), array.size); 28 | ranges.vaRanges[i] = range; 29 | array.cachedRange = range; 30 | } 31 | } 32 | 33 | model::PipelineConfig config{}; 34 | populate_pipeline_config(config, GX_TRIANGLES, result.fmt); 35 | const auto info = gx::build_shader_info(config.shaderConfig); 36 | const auto bindGroups = gx::build_bind_groups(info, config.shaderConfig, ranges); 37 | const auto pipeline = pipeline_ref(config); 38 | 39 | push_draw_command(model::DrawData{ 40 | .pipeline = pipeline, 41 | .vertRange = result.vertRange, 42 | .idxRange = result.idxRange, 43 | .dataRanges = ranges, 44 | .uniformRange = build_uniform(info), 45 | .indexCount = result.numIndices, 46 | .bindGroups = bindGroups, 47 | .dstAlpha = gx::g_gxState.dstAlpha, 48 | }); 49 | } 50 | 51 | State construct_state() { return {}; } 52 | 53 | wgpu::RenderPipeline create_pipeline(const State& state, const PipelineConfig& config) { 54 | const auto info = build_shader_info(config.shaderConfig); // TODO remove 55 | const auto shader = build_shader(config.shaderConfig, info); 56 | 57 | std::array vtxAttrs{}; 58 | auto [num4xAttr, rem] = std::div(config.shaderConfig.indexedAttributeCount, 4); 59 | u32 num2xAttr = 0; 60 | if (rem > 2) { 61 | ++num4xAttr; 62 | } else if (rem > 0) { 63 | ++num2xAttr; 64 | } 65 | 66 | u32 offset = 0; 67 | u32 shaderLocation = 0; 68 | 69 | // Indexed attributes 70 | for (u32 i = 0; i < num4xAttr; ++i) { 71 | vtxAttrs[shaderLocation] = { 72 | .format = wgpu::VertexFormat::Uint16x4, 73 | .offset = offset, 74 | .shaderLocation = shaderLocation, 75 | }; 76 | offset += 8; 77 | ++shaderLocation; 78 | } 79 | for (u32 i = 0; i < num2xAttr; ++i) { 80 | vtxAttrs[shaderLocation] = { 81 | .format = wgpu::VertexFormat::Uint16x2, 82 | .offset = offset, 83 | .shaderLocation = shaderLocation, 84 | }; 85 | offset += 4; 86 | ++shaderLocation; 87 | } 88 | 89 | // Direct attributes 90 | for (int i = 0; i < gx::MaxVtxAttr; ++i) { 91 | const auto attrType = config.shaderConfig.vtxAttrs[i]; 92 | if (attrType != GX_DIRECT) { 93 | continue; 94 | } 95 | const auto attr = static_cast(i); 96 | switch (attr) { 97 | DEFAULT_FATAL("unhandled direct attr {}", i); 98 | case GX_VA_POS: 99 | case GX_VA_NRM: 100 | vtxAttrs[shaderLocation] = wgpu::VertexAttribute{ 101 | .format = wgpu::VertexFormat::Float32x3, 102 | .offset = offset, 103 | .shaderLocation = shaderLocation, 104 | }; 105 | offset += 12; 106 | break; 107 | case GX_VA_CLR0: 108 | case GX_VA_CLR1: 109 | vtxAttrs[shaderLocation] = wgpu::VertexAttribute{ 110 | .format = wgpu::VertexFormat::Float32x4, 111 | .offset = offset, 112 | .shaderLocation = shaderLocation, 113 | }; 114 | offset += 16; 115 | break; 116 | case GX_VA_TEX0: 117 | case GX_VA_TEX1: 118 | case GX_VA_TEX2: 119 | case GX_VA_TEX3: 120 | case GX_VA_TEX4: 121 | case GX_VA_TEX5: 122 | case GX_VA_TEX6: 123 | case GX_VA_TEX7: 124 | vtxAttrs[shaderLocation] = wgpu::VertexAttribute{ 125 | .format = wgpu::VertexFormat::Float32x2, 126 | .offset = offset, 127 | .shaderLocation = shaderLocation, 128 | }; 129 | offset += 8; 130 | break; 131 | } 132 | ++shaderLocation; 133 | } 134 | 135 | const std::array vtxBuffers{wgpu::VertexBufferLayout{ 136 | .stepMode = wgpu::VertexStepMode::Vertex, 137 | .arrayStride = offset, 138 | .attributeCount = shaderLocation, 139 | .attributes = vtxAttrs.data(), 140 | }}; 141 | 142 | return build_pipeline(config, info, vtxBuffers, shader, "GX Pipeline"); 143 | } 144 | 145 | void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass) { 146 | if (!bind_pipeline(data.pipeline, pass)) { 147 | return; 148 | } 149 | 150 | std::array offsets{data.uniformRange.offset}; 151 | uint32_t bindIdx = 1; 152 | for (uint32_t i = 0; i < GX_VA_MAX_ATTR; ++i) { 153 | const auto& range = data.dataRanges.vaRanges[i]; 154 | if (range.size <= 0) { 155 | continue; 156 | } 157 | offsets[bindIdx] = range.offset; 158 | ++bindIdx; 159 | } 160 | pass.SetBindGroup(0, find_bind_group(data.bindGroups.uniformBindGroup), bindIdx, offsets.data()); 161 | if (data.bindGroups.samplerBindGroup && data.bindGroups.textureBindGroup) { 162 | pass.SetBindGroup(1, find_bind_group(data.bindGroups.samplerBindGroup)); 163 | pass.SetBindGroup(2, find_bind_group(data.bindGroups.textureBindGroup)); 164 | } 165 | pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.offset, data.vertRange.size); 166 | pass.SetIndexBuffer(g_indexBuffer, wgpu::IndexFormat::Uint16, data.idxRange.offset, data.idxRange.size); 167 | if (data.dstAlpha != UINT32_MAX) { 168 | const wgpu::Color color{0.f, 0.f, 0.f, data.dstAlpha / 255.f}; 169 | pass.SetBlendConstant(&color); 170 | } 171 | pass.DrawIndexed(data.indexCount); 172 | } 173 | } // namespace aurora::gfx::model 174 | 175 | static absl::flat_hash_map sCachedRanges; 176 | -------------------------------------------------------------------------------- /lib/gfx/model/shader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../common.hpp" 4 | #include "../gx.hpp" 5 | 6 | namespace aurora::gfx::model { 7 | struct DrawData { 8 | PipelineRef pipeline; 9 | Range vertRange; 10 | Range idxRange; 11 | gx::BindGroupRanges dataRanges; 12 | Range uniformRange; 13 | uint32_t indexCount; 14 | gx::GXBindGroups bindGroups; 15 | u32 dstAlpha; 16 | }; 17 | 18 | struct PipelineConfig : gx::PipelineConfig {}; 19 | 20 | struct State {}; 21 | 22 | State construct_state(); 23 | wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config); 24 | void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass); 25 | 26 | void queue_surface(const u8* dlStart, u32 dlSize, bool bigEndian) noexcept; 27 | } // namespace aurora::gfx::model 28 | -------------------------------------------------------------------------------- /lib/gfx/shader_info.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gx.hpp" 4 | 5 | namespace aurora::gfx::gx { 6 | ShaderInfo build_shader_info(const ShaderConfig& config) noexcept; 7 | Range build_uniform(const ShaderInfo& info) noexcept; 8 | u8 color_channel(GXChannelID id) noexcept; 9 | }; // namespace aurora::gfx::gx 10 | -------------------------------------------------------------------------------- /lib/gfx/texture.cpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | 3 | #include "../internal.hpp" 4 | #include "../webgpu/gpu.hpp" 5 | #include "aurora/aurora.h" 6 | #include "texture.hpp" 7 | #include "texture_convert.hpp" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace aurora::gfx { 19 | using webgpu::g_device; 20 | using webgpu::g_queue; 21 | 22 | namespace { 23 | Module Log("aurora::gfx"); 24 | 25 | struct TextureFormatInfo { 26 | uint8_t blockWidth; 27 | uint8_t blockHeight; 28 | uint8_t blockSize; 29 | bool compressed; 30 | }; 31 | 32 | TextureFormatInfo format_info(wgpu::TextureFormat format) { 33 | switch (format) { 34 | DEFAULT_FATAL("unimplemented texture format {}", magic_enum::enum_name(format)); 35 | case wgpu::TextureFormat::R8Unorm: 36 | return {1, 1, 1, false}; 37 | case wgpu::TextureFormat::R16Sint: 38 | return {1, 1, 2, false}; 39 | case wgpu::TextureFormat::RGBA8Unorm: 40 | case wgpu::TextureFormat::R32Float: 41 | return {1, 1, 4, false}; 42 | case wgpu::TextureFormat::BC1RGBAUnorm: 43 | return {4, 4, 8, true}; 44 | } 45 | } 46 | 47 | wgpu::Extent3D physical_size(wgpu::Extent3D size, TextureFormatInfo info) { 48 | const uint32_t width = ((size.width + info.blockWidth - 1) / info.blockWidth) * info.blockWidth; 49 | const uint32_t height = ((size.height + info.blockHeight - 1) / info.blockHeight) * info.blockHeight; 50 | return {.width = width, .height = height, .depthOrArrayLayers = size.depthOrArrayLayers}; 51 | } 52 | } // namespace 53 | 54 | TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format, ArrayRef data, 55 | bool tlut, const char* label) noexcept { 56 | auto handle = new_dynamic_texture_2d(width, height, mips, format, label); 57 | const auto& ref = *handle; 58 | 59 | ByteBuffer buffer; 60 | if (ref.gxFormat != InvalidTextureFormat) { 61 | if (tlut) { 62 | CHECK(ref.size.height == 1, "new_static_texture_2d[{}]: expected tlut height 1, got {}", label, ref.size.height); 63 | CHECK(ref.mipCount == 1, "new_static_texture_2d[{}]: expected tlut mipCount 1, got {}", label, ref.mipCount); 64 | buffer = convert_tlut(ref.gxFormat, ref.size.width, data); 65 | } else { 66 | buffer = convert_texture(ref.gxFormat, ref.size.width, ref.size.height, ref.mipCount, data); 67 | } 68 | if (!buffer.empty()) { 69 | data = {buffer.data(), buffer.size()}; 70 | } 71 | } 72 | 73 | uint32_t offset = 0; 74 | for (uint32_t mip = 0; mip < mips; ++mip) { 75 | const wgpu::Extent3D mipSize{ 76 | .width = std::max(ref.size.width >> mip, 1u), 77 | .height = std::max(ref.size.height >> mip, 1u), 78 | .depthOrArrayLayers = ref.size.depthOrArrayLayers, 79 | }; 80 | const auto info = format_info(ref.format); 81 | const auto physicalSize = physical_size(mipSize, info); 82 | const uint32_t widthBlocks = physicalSize.width / info.blockWidth; 83 | const uint32_t heightBlocks = physicalSize.height / info.blockHeight; 84 | const uint32_t bytesPerRow = widthBlocks * info.blockSize; 85 | const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers; 86 | CHECK(offset + dataSize <= data.size(), "new_static_texture_2d[{}]: expected at least {} bytes, got {}", label, 87 | offset + dataSize, data.size()); 88 | const wgpu::TexelCopyTextureInfo dstView{ 89 | .texture = ref.texture, 90 | .mipLevel = mip, 91 | }; 92 | // const auto range = push_texture_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks); 93 | const wgpu::TexelCopyBufferLayout dataLayout{ 94 | // .offset = range.offset, 95 | .bytesPerRow = bytesPerRow, 96 | .rowsPerImage = heightBlocks, 97 | }; 98 | // TODO 99 | // g_textureUploads.emplace_back(dataLayout, std::move(dstView), physicalSize); 100 | g_queue.WriteTexture(&dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize); 101 | offset += dataSize; 102 | } 103 | if (data.size() != UINT32_MAX && offset < data.size()) { 104 | Log.warn("new_static_texture_2d[{}]: texture used {} bytes, but given {} bytes", label, offset, data.size()); 105 | } 106 | return handle; 107 | } 108 | 109 | TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format, 110 | const char* label) noexcept { 111 | const auto wgpuFormat = to_wgpu(format); 112 | const wgpu::Extent3D size{ 113 | .width = width, 114 | .height = height, 115 | .depthOrArrayLayers = 1, 116 | }; 117 | const wgpu::TextureDescriptor textureDescriptor{ 118 | .label = label, 119 | .usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst, 120 | .dimension = wgpu::TextureDimension::e2D, 121 | .size = size, 122 | .format = wgpuFormat, 123 | .mipLevelCount = mips, 124 | .sampleCount = 1, 125 | }; 126 | const auto viewLabel = fmt::format("{} view", label); 127 | const wgpu::TextureViewDescriptor textureViewDescriptor{ 128 | .label = viewLabel.c_str(), 129 | .format = wgpuFormat, 130 | .dimension = wgpu::TextureViewDimension::e2D, 131 | .mipLevelCount = mips, 132 | }; 133 | auto texture = g_device.CreateTexture(&textureDescriptor); 134 | auto textureView = texture.CreateView(&textureViewDescriptor); 135 | return std::make_shared(std::move(texture), std::move(textureView), size, wgpuFormat, mips, format, 136 | false); 137 | } 138 | 139 | TextureHandle new_render_texture(uint32_t width, uint32_t height, u32 fmt, const char* label) noexcept { 140 | const auto wgpuFormat = webgpu::g_graphicsConfig.surfaceConfiguration.format; 141 | const wgpu::Extent3D size{ 142 | .width = width, 143 | .height = height, 144 | .depthOrArrayLayers = 1, 145 | }; 146 | const wgpu::TextureDescriptor textureDescriptor{ 147 | .label = label, 148 | .usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst, 149 | .dimension = wgpu::TextureDimension::e2D, 150 | .size = size, 151 | .format = wgpuFormat, 152 | .mipLevelCount = 1, 153 | .sampleCount = 1, 154 | }; 155 | const auto viewLabel = fmt::format("{} view", label); 156 | const wgpu::TextureViewDescriptor textureViewDescriptor{ 157 | .label = viewLabel.c_str(), 158 | .format = wgpuFormat, 159 | .dimension = wgpu::TextureViewDimension::e2D, 160 | }; 161 | auto texture = g_device.CreateTexture(&textureDescriptor); 162 | auto textureView = texture.CreateView(&textureViewDescriptor); 163 | return std::make_shared(std::move(texture), std::move(textureView), size, wgpuFormat, 1, fmt, true); 164 | } 165 | 166 | void write_texture(const TextureRef& ref, ArrayRef data) noexcept { 167 | ByteBuffer buffer; 168 | if (ref.gxFormat != InvalidTextureFormat) { 169 | buffer = convert_texture(ref.gxFormat, ref.size.width, ref.size.height, ref.mipCount, data); 170 | if (!buffer.empty()) { 171 | data = {buffer.data(), buffer.size()}; 172 | } 173 | } 174 | 175 | uint32_t offset = 0; 176 | for (uint32_t mip = 0; mip < ref.mipCount; ++mip) { 177 | const wgpu::Extent3D mipSize{ 178 | .width = std::max(ref.size.width >> mip, 1u), 179 | .height = std::max(ref.size.height >> mip, 1u), 180 | .depthOrArrayLayers = ref.size.depthOrArrayLayers, 181 | }; 182 | const auto info = format_info(ref.format); 183 | const auto physicalSize = physical_size(mipSize, info); 184 | const uint32_t widthBlocks = physicalSize.width / info.blockWidth; 185 | const uint32_t heightBlocks = physicalSize.height / info.blockHeight; 186 | const uint32_t bytesPerRow = widthBlocks * info.blockSize; 187 | const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers; 188 | CHECK(offset + dataSize <= data.size(), "write_texture: expected at least {} bytes, got {}", offset + dataSize, 189 | data.size()); 190 | // auto dstView = wgpu::ImageCopyTexture{ 191 | // .texture = ref.texture, 192 | // .mipLevel = mip, 193 | // }; 194 | // const auto range = push_texture_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks); 195 | // const auto dataLayout = wgpu::TextureDataLayout{ 196 | // .offset = range.offset, 197 | // .bytesPerRow = bytesPerRow, 198 | // .rowsPerImage = heightBlocks, 199 | // }; 200 | // g_textureUploads.emplace_back(dataLayout, std::move(dstView), physicalSize); 201 | const wgpu::TexelCopyTextureInfo dstView{ 202 | .texture = ref.texture, 203 | .mipLevel = mip, 204 | }; 205 | const wgpu::TexelCopyBufferLayout dataLayout{ 206 | .bytesPerRow = bytesPerRow, 207 | .rowsPerImage = heightBlocks, 208 | }; 209 | g_queue.WriteTexture(&dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize); 210 | offset += dataSize; 211 | } 212 | if (data.size() != UINT32_MAX && offset < data.size()) { 213 | Log.warn("write_texture: texture used {} bytes, but given {} bytes", offset, data.size()); 214 | } 215 | } 216 | } // namespace aurora::gfx 217 | -------------------------------------------------------------------------------- /lib/gfx/texture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "common.hpp" 5 | 6 | namespace aurora::gfx { 7 | struct TextureUpload { 8 | wgpu::TexelCopyBufferLayout layout; 9 | wgpu::TexelCopyTextureInfo tex; 10 | wgpu::Extent3D size; 11 | 12 | TextureUpload(wgpu::TexelCopyBufferLayout layout, wgpu::TexelCopyTextureInfo tex, wgpu::Extent3D size) noexcept 13 | : layout(layout), tex(tex), size(size) {} 14 | }; 15 | extern std::vector g_textureUploads; 16 | 17 | constexpr u32 InvalidTextureFormat = -1; 18 | struct TextureRef { 19 | wgpu::Texture texture; 20 | wgpu::TextureView view; 21 | wgpu::Extent3D size; 22 | wgpu::TextureFormat format; 23 | uint32_t mipCount; 24 | u32 gxFormat; 25 | bool isRenderTexture; // :shrug: for now 26 | 27 | TextureRef(wgpu::Texture texture, wgpu::TextureView view, wgpu::Extent3D size, wgpu::TextureFormat format, 28 | uint32_t mipCount, u32 gxFormat, bool isRenderTexture) 29 | : texture(std::move(texture)) 30 | , view(std::move(view)) 31 | , size(size) 32 | , format(format) 33 | , mipCount(mipCount) 34 | , gxFormat(gxFormat) 35 | , isRenderTexture(isRenderTexture) {} 36 | }; 37 | 38 | TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format, ArrayRef data, 39 | bool tlut, const char* label) noexcept; 40 | TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format, 41 | const char* label) noexcept; 42 | TextureHandle new_render_texture(uint32_t width, uint32_t height, u32 fmt, const char* label) noexcept; 43 | void write_texture(const TextureRef& ref, ArrayRef data) noexcept; 44 | }; // namespace aurora::gfx 45 | 46 | struct GXTexObj_ { 47 | aurora::gfx::TextureHandle ref; 48 | const void* data; 49 | u32 dataSize; 50 | u16 width; 51 | u16 height; 52 | u32 fmt; 53 | GXTexWrapMode wrapS; 54 | GXTexWrapMode wrapT; 55 | GXBool hasMips; 56 | GXTexFilter minFilter; 57 | GXTexFilter magFilter; 58 | float minLod; 59 | float maxLod; 60 | float lodBias; 61 | GXBool biasClamp; 62 | GXBool doEdgeLod; 63 | GXAnisotropy maxAniso; 64 | GXTlut tlut; 65 | bool dataInvalidated; 66 | }; 67 | static_assert(sizeof(GXTexObj_) <= sizeof(GXTexObj), "GXTexObj too small!"); 68 | struct GXTlutObj_ { 69 | aurora::gfx::TextureHandle ref; 70 | }; 71 | static_assert(sizeof(GXTlutObj_) <= sizeof(GXTlutObj), "GXTlutObj too small!"); 72 | 73 | namespace aurora::gfx { 74 | struct TextureBind { 75 | GXTexObj_ texObj; 76 | 77 | TextureBind() noexcept = default; 78 | TextureBind(GXTexObj_ obj) noexcept : texObj(std::move(obj)) {} 79 | void reset() noexcept { texObj.ref.reset(); }; 80 | [[nodiscard]] wgpu::SamplerDescriptor get_descriptor() const noexcept; 81 | operator bool() const noexcept { return texObj.ref.operator bool(); } 82 | }; 83 | } // namespace aurora::gfx 84 | -------------------------------------------------------------------------------- /lib/gfx/texture_convert.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include "texture.hpp" 5 | #include "../webgpu/gpu.hpp" 6 | 7 | namespace aurora::gfx { 8 | static wgpu::TextureFormat to_wgpu(u32 format) { 9 | switch (format) { 10 | case GX_TF_I4: 11 | case GX_TF_I8: 12 | case GX_TF_R8_PC: 13 | return wgpu::TextureFormat::R8Unorm; 14 | case GX_TF_C4: 15 | case GX_TF_C8: 16 | case GX_TF_C14X2: 17 | return wgpu::TextureFormat::R16Sint; 18 | case GX_TF_CMPR: 19 | if (webgpu::g_device.HasFeature(wgpu::FeatureName::TextureCompressionBC)) { 20 | return wgpu::TextureFormat::BC1RGBAUnorm; 21 | } 22 | [[fallthrough]]; 23 | default: 24 | return wgpu::TextureFormat::RGBA8Unorm; 25 | } 26 | } 27 | 28 | ByteBuffer convert_texture(u32 format, uint32_t width, uint32_t height, uint32_t mips, ArrayRef data); 29 | ByteBuffer convert_tlut(u32 format, uint32_t width, ArrayRef data); 30 | } // namespace aurora::gfx 31 | -------------------------------------------------------------------------------- /lib/imgui.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "internal.hpp" 11 | #include "webgpu/gpu.hpp" 12 | #include "window.hpp" 13 | 14 | #define IMGUI_IMPL_WEBGPU_BACKEND_DAWN 15 | #include "backends/imgui_impl_sdl3.h" 16 | #include "backends/imgui_impl_sdlrenderer3.h" 17 | #include "backends/imgui_impl_wgpu.h" 18 | 19 | namespace aurora::imgui { 20 | static float g_scale; 21 | static std::string g_imguiSettings{}; 22 | static std::string g_imguiLog{}; 23 | static bool g_useSdlRenderer = false; 24 | 25 | static std::vector g_sdlTextures; 26 | static std::vector g_wgpuTextures; 27 | 28 | void create_context() noexcept { 29 | IMGUI_CHECKVERSION(); 30 | ImGui::CreateContext(); 31 | ImGuiIO& io = ImGui::GetIO(); 32 | g_imguiSettings = std::string{g_config.configPath} + "/imgui.ini"; 33 | g_imguiLog = std::string{g_config.configPath} + "/imgui.log"; 34 | io.IniFilename = g_imguiSettings.c_str(); 35 | io.LogFilename = g_imguiLog.c_str(); 36 | } 37 | 38 | void initialize() noexcept { 39 | SDL_Renderer* renderer = window::get_sdl_renderer(); 40 | ImGui_ImplSDL3_InitForSDLRenderer(window::get_sdl_window(), renderer); 41 | #ifdef __APPLE__ 42 | // Disable MouseCanUseGlobalState for scaling purposes 43 | ImGui_ImplSDL3_GetBackendData()->MouseCanUseGlobalState = false; 44 | #endif 45 | g_useSdlRenderer = renderer != nullptr; 46 | if (g_useSdlRenderer) { 47 | ImGui_ImplSDLRenderer3_Init(renderer); 48 | } else { 49 | ImGui_ImplWGPU_InitInfo info; 50 | info.Device = webgpu::g_device.Get(); 51 | info.RenderTargetFormat = static_cast(webgpu::g_graphicsConfig.surfaceConfiguration.format); 52 | ImGui_ImplWGPU_Init(&info); 53 | } 54 | } 55 | 56 | void shutdown() noexcept { 57 | if (g_useSdlRenderer) { 58 | ImGui_ImplSDLRenderer3_Shutdown(); 59 | } else { 60 | ImGui_ImplWGPU_Shutdown(); 61 | } 62 | ImGui_ImplSDL3_Shutdown(); 63 | ImGui::DestroyContext(); 64 | for (const auto& texture : g_sdlTextures) { 65 | SDL_DestroyTexture(texture); 66 | } 67 | g_sdlTextures.clear(); 68 | g_wgpuTextures.clear(); 69 | } 70 | 71 | void process_event(const SDL_Event& event) noexcept { 72 | if (event.type == SDL_EVENT_MOUSE_MOTION) { 73 | SDL_Event scaledEvent = event; 74 | const auto density = SDL_GetWindowPixelDensity(window::get_sdl_window()); 75 | scaledEvent.motion.x *= density; 76 | scaledEvent.motion.y *= density; 77 | scaledEvent.motion.xrel *= density; 78 | scaledEvent.motion.yrel *= density; 79 | ImGui_ImplSDL3_ProcessEvent(&scaledEvent); 80 | return; 81 | } 82 | ImGui_ImplSDL3_ProcessEvent(&event); 83 | } 84 | 85 | void new_frame(const AuroraWindowSize& size) noexcept { 86 | if (g_useSdlRenderer) { 87 | ImGui_ImplSDLRenderer3_NewFrame(); 88 | g_scale = size.scale; 89 | } else { 90 | if (g_scale != size.scale) { 91 | if (g_scale > 0.f) { 92 | ImGui_ImplWGPU_CreateDeviceObjects(); 93 | } 94 | g_scale = size.scale; 95 | } 96 | ImGui_ImplWGPU_NewFrame(); 97 | } 98 | ImGui_ImplSDL3_NewFrame(); 99 | 100 | // Render at full DPI 101 | ImGui::GetIO().DisplaySize = { 102 | static_cast(size.fb_width), 103 | static_cast(size.fb_height), 104 | }; 105 | ImGui::NewFrame(); 106 | } 107 | 108 | void render(const wgpu::RenderPassEncoder& pass) noexcept { 109 | ImGui::Render(); 110 | 111 | auto* data = ImGui::GetDrawData(); 112 | // io.DisplayFramebufferScale is informational; we're rendering at full DPI 113 | data->FramebufferScale = {1.f, 1.f}; 114 | if (g_useSdlRenderer) { 115 | SDL_Renderer* renderer = window::get_sdl_renderer(); 116 | SDL_RenderClear(renderer); 117 | ImGui_ImplSDLRenderer3_RenderDrawData(data, renderer); 118 | SDL_RenderPresent(renderer); 119 | } else { 120 | ImGui_ImplWGPU_RenderDrawData(data, pass.Get()); 121 | } 122 | } 123 | 124 | ImTextureID add_texture(uint32_t width, uint32_t height, const uint8_t* data) noexcept { 125 | if (g_useSdlRenderer) { 126 | SDL_Renderer* renderer = window::get_sdl_renderer(); 127 | SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height); 128 | SDL_UpdateTexture(texture, nullptr, data, width * 4); 129 | SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_LINEAR); 130 | g_sdlTextures.push_back(texture); 131 | return reinterpret_cast(texture); 132 | } 133 | const wgpu::Extent3D size{ 134 | .width = width, 135 | .height = height, 136 | .depthOrArrayLayers = 1, 137 | }; 138 | const wgpu::TextureDescriptor textureDescriptor{ 139 | .label = "imgui texture", 140 | .usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst, 141 | .dimension = wgpu::TextureDimension::e2D, 142 | .size = size, 143 | .format = wgpu::TextureFormat::RGBA8Unorm, 144 | .mipLevelCount = 1, 145 | .sampleCount = 1, 146 | }; 147 | const wgpu::TextureViewDescriptor textureViewDescriptor{ 148 | .label = "imgui texture view", 149 | .format = wgpu::TextureFormat::RGBA8Unorm, 150 | .dimension = wgpu::TextureViewDimension::e2D, 151 | .mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED, 152 | .arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED, 153 | }; 154 | auto texture = webgpu::g_device.CreateTexture(&textureDescriptor); 155 | auto textureView = texture.CreateView(&textureViewDescriptor); 156 | { 157 | const wgpu::TexelCopyTextureInfo dstView{ 158 | .texture = texture, 159 | }; 160 | const wgpu::TexelCopyBufferLayout dataLayout{ 161 | .bytesPerRow = 4 * width, 162 | .rowsPerImage = height, 163 | }; 164 | webgpu::g_queue.WriteTexture(&dstView, data, width * height * 4, &dataLayout, &size); 165 | } 166 | g_wgpuTextures.push_back(texture); 167 | return reinterpret_cast(textureView.MoveToCHandle()); 168 | } 169 | } // namespace aurora::imgui 170 | 171 | // C bindings 172 | extern "C" { 173 | ImTextureID aurora_imgui_add_texture(uint32_t width, uint32_t height, const void* rgba8) { 174 | return aurora::imgui::add_texture(width, height, static_cast(rgba8)); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /lib/imgui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | union SDL_Event; 8 | 9 | namespace wgpu { 10 | class RenderPassEncoder; 11 | } // namespace wgpu 12 | 13 | namespace aurora::imgui { 14 | void create_context() noexcept; 15 | void initialize() noexcept; 16 | void shutdown() noexcept; 17 | 18 | void process_event(const SDL_Event& event) noexcept; 19 | void new_frame(const AuroraWindowSize& size) noexcept; 20 | void render(const wgpu::RenderPassEncoder& pass) noexcept; 21 | } // namespace aurora::imgui 22 | -------------------------------------------------------------------------------- /lib/input.cpp: -------------------------------------------------------------------------------- 1 | #include "input.hpp" 2 | #include "internal.hpp" 3 | 4 | #include "magic_enum.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std::string_view_literals; 16 | 17 | namespace aurora::input { 18 | Module Log("aurora::input"); 19 | absl::flat_hash_map g_GameControllers; 20 | 21 | GameController* get_controller_for_player(uint32_t player) noexcept { 22 | for (auto& [which, controller] : g_GameControllers) { 23 | if (player_index(which) == player) { 24 | return &controller; 25 | } 26 | } 27 | 28 | #if 0 29 | /* If we don't have a controller assigned to this port use the first unassigned controller */ 30 | if (!g_GameControllers.empty()) { 31 | int32_t availIndex = -1; 32 | GameController* ct = nullptr; 33 | for (auto& controller : g_GameControllers) { 34 | if (player_index(controller.first) == -1) { 35 | availIndex = controller.first; 36 | ct = &controller.second; 37 | break; 38 | } 39 | } 40 | if (availIndex != -1) { 41 | set_player_index(availIndex, player); 42 | return ct; 43 | } 44 | } 45 | #endif 46 | return nullptr; 47 | } 48 | 49 | Sint32 get_instance_for_player(uint32_t player) noexcept { 50 | for (const auto& [which, controller] : g_GameControllers) { 51 | if (player_index(which) == player) { 52 | return which; 53 | } 54 | } 55 | 56 | return {}; 57 | } 58 | 59 | static std::optional remap_controller_layout(std::string mapping) { 60 | std::string newMapping; 61 | newMapping.reserve(mapping.size()); 62 | absl::btree_map entries; 63 | for (size_t idx = 0; const auto value : absl::StrSplit(mapping, ',')) { 64 | if (idx < 2) { 65 | if (idx > 0) { 66 | newMapping.push_back(','); 67 | } 68 | newMapping.append(value); 69 | } else { 70 | const auto split = absl::StrSplit(value, absl::MaxSplits(':', 2)); 71 | auto iter = split.begin(); 72 | auto first = *iter++; 73 | auto second = *iter; 74 | entries.emplace(std::move(first), std::move(second)); 75 | } 76 | idx++; 77 | } 78 | if (entries.contains("rightshoulder") && !entries.contains("leftshoulder")) { 79 | Log.info("Remapping GameCube controller layout"); 80 | entries.insert_or_assign("back", entries["rightshoulder"]); 81 | // TODO trigger buttons may differ per platform 82 | entries.insert_or_assign("leftshoulder", "b11"); 83 | entries.insert_or_assign("rightshoulder", "b10"); 84 | } else if (entries.contains("leftshoulder") && entries.contains("rightshoulder") && entries.contains("back")) { 85 | Log.info("Controller has standard layout"); 86 | #if 0 87 | auto a = entries["a"sv]; 88 | entries.insert_or_assign("a"sv, entries["b"sv]); 89 | entries.insert_or_assign("b"sv, a); 90 | #endif 91 | auto x = entries["x"]; 92 | entries.insert_or_assign("x", entries["y"]); 93 | entries.insert_or_assign("y", x); 94 | } else { 95 | Log.error("Controller has unsupported layout: {}", mapping); 96 | return {}; 97 | } 98 | for (auto [k, v] : entries) { 99 | newMapping.push_back(','); 100 | newMapping.append(k); 101 | newMapping.push_back(':'); 102 | newMapping.append(v); 103 | } 104 | return newMapping; 105 | } 106 | 107 | SDL_JoystickID add_controller(SDL_JoystickID which) noexcept { 108 | auto* ctrl = SDL_OpenGamepad(which); 109 | if (ctrl != nullptr) { 110 | { 111 | char* mapping = SDL_GetGamepadMapping(ctrl); 112 | if (mapping != nullptr) { 113 | auto newMapping = remap_controller_layout(mapping); 114 | SDL_free(mapping); 115 | if (newMapping) { 116 | if (SDL_AddGamepadMapping(newMapping->c_str()) == -1) { 117 | Log.error("Failed to update controller mapping: {}", SDL_GetError()); 118 | } 119 | } 120 | } else { 121 | Log.error("Failed to retrieve mapping for controller"); 122 | } 123 | } 124 | GameController controller; 125 | controller.m_controller = ctrl; 126 | controller.m_index = which; 127 | controller.m_vid = SDL_GetGamepadVendor(ctrl); 128 | controller.m_pid = SDL_GetGamepadProduct(ctrl); 129 | if (controller.m_vid == 0x05ac /* USB_VENDOR_APPLE */ && controller.m_pid == 3) { 130 | // Ignore Apple TV remote 131 | SDL_CloseGamepad(ctrl); 132 | return -1; 133 | } 134 | controller.m_isGameCube = controller.m_vid == 0x057E && controller.m_pid == 0x0337; 135 | const auto props = SDL_GetGamepadProperties(ctrl); 136 | controller.m_hasRumble = SDL_GetBooleanProperty(props, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, true); 137 | SDL_JoystickID instance = SDL_GetJoystickID(SDL_GetGamepadJoystick(ctrl)); 138 | g_GameControllers[instance] = controller; 139 | return instance; 140 | } 141 | 142 | return -1; 143 | } 144 | 145 | void remove_controller(Uint32 instance) noexcept { 146 | if (g_GameControllers.find(instance) != g_GameControllers.end()) { 147 | SDL_CloseGamepad(g_GameControllers[instance].m_controller); 148 | g_GameControllers.erase(instance); 149 | } 150 | } 151 | 152 | bool is_gamecube(Uint32 instance) noexcept { 153 | if (g_GameControllers.find(instance) != g_GameControllers.end()) { 154 | return g_GameControllers[instance].m_isGameCube; 155 | } 156 | return false; 157 | } 158 | 159 | int32_t player_index(Uint32 instance) noexcept { 160 | if (g_GameControllers.find(instance) != g_GameControllers.end()) { 161 | return SDL_GetGamepadPlayerIndex(g_GameControllers[instance].m_controller); 162 | } 163 | return -1; 164 | } 165 | 166 | void set_player_index(Uint32 instance, Sint32 index) noexcept { 167 | if (g_GameControllers.find(instance) != g_GameControllers.end()) { 168 | SDL_SetGamepadPlayerIndex(g_GameControllers[instance].m_controller, index); 169 | } 170 | } 171 | 172 | std::string controller_name(Uint32 instance) noexcept { 173 | if (g_GameControllers.find(instance) != g_GameControllers.end()) { 174 | const auto* name = SDL_GetGamepadName(g_GameControllers[instance].m_controller); 175 | if (name != nullptr) { 176 | return {name}; 177 | } 178 | } 179 | return {}; 180 | } 181 | 182 | bool controller_has_rumble(Uint32 instance) noexcept { 183 | if (g_GameControllers.find(instance) != g_GameControllers.end()) { 184 | return g_GameControllers[instance].m_hasRumble; 185 | } 186 | 187 | return false; 188 | } 189 | 190 | void controller_rumble(uint32_t instance, uint16_t low_freq_intensity, uint16_t high_freq_intensity, 191 | uint16_t duration_ms) noexcept { 192 | 193 | if (g_GameControllers.find(instance) != g_GameControllers.end()) { 194 | SDL_RumbleGamepad(g_GameControllers[instance].m_controller, low_freq_intensity, high_freq_intensity, duration_ms); 195 | } 196 | } 197 | 198 | uint32_t controller_count() noexcept { return g_GameControllers.size(); } 199 | 200 | void initialize() noexcept { 201 | /* Make sure we initialize everything input related now, this will automatically add all of the connected controllers 202 | * as expected */ 203 | ASSERT(SDL_Init(SDL_INIT_HAPTIC | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD), "Failed to initialize SDL subsystems: {}", 204 | SDL_GetError()); 205 | } 206 | } // namespace aurora::input 207 | -------------------------------------------------------------------------------- /lib/input.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "dolphin/pad.h" // For PADDeaZones and PADButtonMapping 5 | #include "SDL3/SDL_gamepad.h" 6 | #include "SDL3/SDL_keyboard.h" 7 | #include "SDL3/SDL_keycode.h" 8 | #include "SDL3/SDL_mouse.h" 9 | #include "logging.hpp" 10 | 11 | #include 12 | 13 | namespace aurora::input { 14 | extern Module Log; 15 | 16 | struct GameController { 17 | SDL_Gamepad* m_controller = nullptr; 18 | bool m_isGameCube = false; 19 | Sint32 m_index = -1; 20 | bool m_hasRumble = false; 21 | PADDeadZones m_deadZones{ 22 | .emulateTriggers = true, 23 | .useDeadzones = true, 24 | .stickDeadZone = 8000, 25 | .substickDeadZone = 8000, 26 | .leftTriggerActivationZone = 31150, 27 | .rightTriggerActivationZone = 31150, 28 | }; 29 | uint16_t m_vid = 0; 30 | uint16_t m_pid = 0; 31 | std::array m_mapping{}; 32 | bool m_mappingLoaded = false; 33 | constexpr bool operator==(const GameController& other) const { 34 | return m_controller == other.m_controller && m_index == other.m_index; 35 | } 36 | }; 37 | 38 | GameController* get_controller_for_player(uint32_t player) noexcept; 39 | Sint32 get_instance_for_player(uint32_t player) noexcept; 40 | SDL_JoystickID add_controller(SDL_JoystickID which) noexcept; 41 | void remove_controller(Uint32 instance) noexcept; 42 | Sint32 player_index(Uint32 instance) noexcept; 43 | void set_player_index(Uint32 instance, Sint32 index) noexcept; 44 | std::string controller_name(Uint32 instance) noexcept; 45 | bool is_gamecube(Uint32 instance) noexcept; 46 | bool controller_has_rumble(Uint32 instance) noexcept; 47 | void controller_rumble(uint32_t instance, uint16_t low_freq_intensity, uint16_t high_freq_intensity, 48 | uint16_t duration_ms) noexcept; 49 | uint32_t controller_count() noexcept; 50 | void initialize() noexcept; 51 | extern absl::flat_hash_map g_GameControllers; 52 | } // namespace aurora::input 53 | -------------------------------------------------------------------------------- /lib/internal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "logging.hpp" // IWYU pragma: keep 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std::string_view_literals; 14 | 15 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 16 | #ifndef SBIG 17 | #define SBIG(q) \ 18 | (((q) & 0x000000FF) << 24 | ((q) & 0x0000FF00) << 8 | ((q) & 0x00FF0000) >> 8 | ((q) & 0xFF000000) >> 24) 19 | #endif 20 | #else 21 | #ifndef SBIG 22 | #define SBIG(q) (q) 23 | #endif 24 | #endif 25 | 26 | template 27 | requires(sizeof(T) == sizeof(uint16_t) && std::is_arithmetic_v) 28 | constexpr T bswap(T val) noexcept { 29 | union { 30 | uint16_t u; 31 | T t; 32 | } v{.t = val}; 33 | #if __GNUC__ 34 | v.u = __builtin_bswap16(v.u); 35 | #elif _WIN32 36 | v.u = _byteswap_ushort(v.u); 37 | #else 38 | v.u = (v.u << 8) | ((v.u >> 8) & 0xFF); 39 | #endif 40 | return v.t; 41 | } 42 | 43 | template 44 | requires(sizeof(T) == sizeof(uint32_t) && std::is_arithmetic_v) 45 | constexpr T bswap(T val) noexcept { 46 | union { 47 | uint32_t u; 48 | T t; 49 | } v{.t = val}; 50 | #if __GNUC__ 51 | v.u = __builtin_bswap32(v.u); 52 | #elif _WIN32 53 | v.u = _byteswap_ulong(v.u); 54 | #else 55 | v.u = ((v.u & 0x0000FFFF) << 16) | ((v.u & 0xFFFF0000) >> 16) | ((v.u & 0x00FF00FF) << 8) | ((v.u & 0xFF00FF00) >> 8); 56 | #endif 57 | return v.t; 58 | } 59 | 60 | template 61 | requires(std::is_enum_v) 62 | auto underlying(T value) -> std::underlying_type_t { 63 | return static_cast>(value); 64 | } 65 | 66 | #ifndef ALIGN 67 | #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) 68 | #endif 69 | 70 | #if !defined(__has_cpp_attribute) 71 | #define __has_cpp_attribute(name) 0 72 | #endif 73 | #if __has_cpp_attribute(unlikely) 74 | #define UNLIKELY [[unlikely]] 75 | #else 76 | #define UNLIKELY 77 | #endif 78 | #define FATAL(msg, ...) Log.fatal(msg, ##__VA_ARGS__); 79 | #define ASSERT(cond, msg, ...) \ 80 | if (!(cond)) \ 81 | UNLIKELY FATAL(msg, ##__VA_ARGS__) 82 | #ifdef NDEBUG 83 | #define CHECK(cond, msg, ...) 84 | #else 85 | #define CHECK(cond, msg, ...) ASSERT(cond, msg, ##__VA_ARGS__) 86 | #endif 87 | #define DEFAULT_FATAL(msg, ...) UNLIKELY default : FATAL(msg, ##__VA_ARGS__) 88 | #define TRY(cond, msg, ...) \ 89 | if (!(cond)) \ 90 | UNLIKELY { \ 91 | Log.error(msg, ##__VA_ARGS__); \ 92 | return false; \ 93 | } 94 | #define TRY_WARN(cond, msg, ...) \ 95 | if (!(cond)) \ 96 | UNLIKELY { Log.warn(msg, ##__VA_ARGS__); } 97 | 98 | namespace aurora { 99 | extern AuroraConfig g_config; 100 | 101 | template 102 | class ArrayRef { 103 | public: 104 | using value_type = std::remove_cvref_t; 105 | using pointer = value_type*; 106 | using const_pointer = const value_type*; 107 | using reference = value_type&; 108 | using const_reference = const value_type&; 109 | using iterator = const_pointer; 110 | using const_iterator = const_pointer; 111 | using reverse_iterator = std::reverse_iterator; 112 | using const_reverse_iterator = std::reverse_iterator; 113 | using size_type = std::size_t; 114 | using difference_type = std::ptrdiff_t; 115 | 116 | ArrayRef() = default; 117 | explicit ArrayRef(const T& one) : ptr(&one), length(1) {} 118 | ArrayRef(const T* data, size_t length) : ptr(data), length(length) {} 119 | ArrayRef(const T* begin, const T* end) : ptr(begin), length(end - begin) {} 120 | template 121 | constexpr ArrayRef(const T (&arr)[N]) : ptr(arr), length(N) {} 122 | template 123 | constexpr ArrayRef(const std::array& arr) : ptr(arr.data()), length(arr.size()) {} 124 | ArrayRef(const std::vector& vec) : ptr(vec.data()), length(vec.size()) {} 125 | 126 | const T* data() const { return ptr; } 127 | size_t size() const { return length; } 128 | bool empty() const { return length == 0; } 129 | 130 | const T& front() const { 131 | assert(!empty()); 132 | return ptr[0]; 133 | } 134 | const T& back() const { 135 | assert(!empty()); 136 | return ptr[length - 1]; 137 | } 138 | const T& operator[](size_t i) const { 139 | assert(i < length && "Invalid index!"); 140 | return ptr[i]; 141 | } 142 | 143 | iterator begin() const { return ptr; } 144 | iterator end() const { return ptr + length; } 145 | 146 | reverse_iterator rbegin() const { return reverse_iterator(end()); } 147 | reverse_iterator rend() const { return reverse_iterator(begin()); } 148 | 149 | /// Disallow accidental assignment from a temporary. 150 | template 151 | std::enable_if_t::value, ArrayRef>& operator=(U&& Temporary) = delete; 152 | 153 | /// Disallow accidental assignment from a temporary. 154 | template 155 | std::enable_if_t::value, ArrayRef>& operator=(std::initializer_list) = delete; 156 | 157 | private: 158 | const T* ptr = nullptr; 159 | size_t length = 0; 160 | }; 161 | } // namespace aurora 162 | -------------------------------------------------------------------------------- /lib/logging.cpp: -------------------------------------------------------------------------------- 1 | #include "logging.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace aurora { 11 | extern AuroraConfig g_config; 12 | 13 | void log_internal(const AuroraLogLevel level, const char* module, const char* message, 14 | const unsigned int len) noexcept { 15 | if (g_config.logCallback == nullptr) { 16 | fmt::println(stderr, "[{}] [{}] {}", level, module, std::string_view(message, len)); 17 | } else { 18 | g_config.logCallback(level, module, message, len); 19 | } 20 | } 21 | } // namespace aurora 22 | 23 | auto fmt::formatter::format(const AuroraLogLevel level, format_context& ctx) const -> format_context::iterator { 24 | std::string_view name = "unknown"; 25 | switch (level) { 26 | case LOG_DEBUG: 27 | name = "debug"; 28 | break; 29 | case LOG_INFO: 30 | name = "info"; 31 | break; 32 | case LOG_WARNING: 33 | name = "warning"; 34 | break; 35 | case LOG_ERROR: 36 | name = "error"; 37 | break; 38 | case LOG_FATAL: 39 | name = "fatal"; 40 | break; 41 | default: 42 | break; 43 | } 44 | return formatter::format(name, ctx); 45 | } 46 | -------------------------------------------------------------------------------- /lib/logging.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace aurora { 12 | void log_internal(AuroraLogLevel level, const char* module, const char* message, unsigned int len) noexcept; 13 | 14 | struct Module { 15 | const char* name; 16 | explicit Module(const char* name) noexcept : name(name) {} 17 | 18 | template 19 | void report(const AuroraLogLevel level, fmt::format_string fmt, T&&... args) noexcept { 20 | auto message = fmt::format(fmt, std::forward(args)...); 21 | log_internal(level, name, message.c_str(), message.size()); 22 | } 23 | 24 | template 25 | void debug(fmt::format_string fmt, T&&... args) noexcept { 26 | report(LOG_DEBUG, fmt, std::forward(args)...); 27 | } 28 | 29 | template 30 | void info(fmt::format_string fmt, T&&... args) noexcept { 31 | report(LOG_INFO, fmt, std::forward(args)...); 32 | } 33 | 34 | template 35 | void warn(fmt::format_string fmt, T&&... args) noexcept { 36 | report(LOG_WARNING, fmt, std::forward(args)...); 37 | } 38 | 39 | template 40 | void error(fmt::format_string fmt, T&&... args) noexcept { 41 | report(LOG_ERROR, fmt, std::forward(args)...); 42 | } 43 | 44 | template 45 | [[noreturn]] void fatal(fmt::format_string fmt, T&&... args) noexcept { 46 | report(LOG_FATAL, fmt, std::forward(args)...); 47 | std::abort(); 48 | } 49 | }; 50 | } // namespace aurora 51 | 52 | template <> 53 | struct fmt::formatter : formatter { 54 | auto format(AuroraLogLevel level, format_context& ctx) const -> format_context::iterator; 55 | }; 56 | -------------------------------------------------------------------------------- /lib/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #undef main 3 | 4 | #include 5 | 6 | int main(int argc, char** argv) { return aurora_main(argc, argv); } 7 | -------------------------------------------------------------------------------- /lib/webgpu/gpu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "wgpu.hpp" 6 | 7 | #include 8 | #include 9 | 10 | struct SDL_Window; 11 | 12 | namespace aurora::webgpu { 13 | struct GraphicsConfig { 14 | wgpu::SurfaceConfiguration surfaceConfiguration; 15 | wgpu::TextureFormat depthFormat; 16 | uint32_t msaaSamples; 17 | uint16_t textureAnisotropy; 18 | }; 19 | struct TextureWithSampler { 20 | wgpu::Texture texture; 21 | wgpu::TextureView view; 22 | wgpu::Extent3D size; 23 | wgpu::TextureFormat format; 24 | wgpu::Sampler sampler; 25 | }; 26 | 27 | extern wgpu::Device g_device; 28 | extern wgpu::Queue g_queue; 29 | extern wgpu::Surface g_surface; 30 | extern wgpu::BackendType g_backendType; 31 | extern GraphicsConfig g_graphicsConfig; 32 | extern TextureWithSampler g_frameBuffer; 33 | extern TextureWithSampler g_frameBufferResolved; 34 | extern TextureWithSampler g_depthBuffer; 35 | extern wgpu::RenderPipeline g_CopyPipeline; 36 | extern wgpu::BindGroup g_CopyBindGroup; 37 | extern wgpu::Instance g_instance; 38 | 39 | bool initialize(AuroraBackend backend); 40 | void shutdown(); 41 | void resize_swapchain(uint32_t width, uint32_t height, bool force = false); 42 | TextureWithSampler create_render_texture(bool multisampled); 43 | } // namespace aurora::webgpu 44 | -------------------------------------------------------------------------------- /lib/webgpu/wgpu.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef EMSCRIPTEN 3 | #include 4 | #endif 5 | 6 | #include 7 | 8 | static inline bool operator==(const wgpu::Extent3D& lhs, const wgpu::Extent3D& rhs) { 9 | return lhs.width == rhs.width && lhs.height == rhs.height && lhs.depthOrArrayLayers == rhs.depthOrArrayLayers; 10 | } 11 | static inline bool operator!=(const wgpu::Extent3D& lhs, const wgpu::Extent3D& rhs) { return !(lhs == rhs); } 12 | 13 | namespace wgpu { 14 | inline std::string_view format_as(wgpu::StringView str) { return str; } 15 | } // namespace wgpu 16 | -------------------------------------------------------------------------------- /lib/window.cpp: -------------------------------------------------------------------------------- 1 | #include "window.hpp" 2 | 3 | #include "imgui.hpp" 4 | #include "webgpu/gpu.hpp" 5 | #include "input.hpp" 6 | #include "internal.hpp" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | namespace aurora::window { 25 | namespace { 26 | Module Log("aurora::window"); 27 | 28 | SDL_Window* g_window; 29 | SDL_Renderer* g_renderer; 30 | AuroraWindowSize g_windowSize; 31 | std::vector g_events; 32 | 33 | inline bool operator==(const AuroraWindowSize& lhs, const AuroraWindowSize& rhs) { 34 | return lhs.width == rhs.width && lhs.height == rhs.height && lhs.fb_width == rhs.fb_width && 35 | lhs.fb_height == rhs.fb_height && lhs.scale == rhs.scale; 36 | } 37 | 38 | void resize_swapchain() noexcept { 39 | const auto size = get_window_size(); 40 | if (size == g_windowSize) { 41 | return; 42 | } 43 | if (size.scale != g_windowSize.scale) { 44 | if (g_windowSize.scale > 0.f) { 45 | Log.info("Display scale changed to {}", size.scale); 46 | } 47 | } 48 | g_windowSize = size; 49 | webgpu::resize_swapchain(size.fb_width, size.fb_height); 50 | } 51 | 52 | void set_window_icon() noexcept { 53 | if (g_config.iconRGBA8 == nullptr) { 54 | return; 55 | } 56 | auto* iconSurface = 57 | SDL_CreateSurfaceFrom(static_cast(g_config.iconWidth), static_cast(g_config.iconHeight), 58 | SDL_GetPixelFormatForMasks(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000), 59 | g_config.iconRGBA8, static_cast(4 * g_config.iconWidth)); 60 | ASSERT(iconSurface != nullptr, "Failed to create icon surface: {}", SDL_GetError()); 61 | TRY_WARN(SDL_SetWindowIcon(g_window, iconSurface), "Failed to set window icon: {}", SDL_GetError()); 62 | SDL_DestroySurface(iconSurface); 63 | } 64 | } // namespace 65 | 66 | const AuroraEvent* poll_events() { 67 | g_events.clear(); 68 | 69 | SDL_Event event; 70 | while (SDL_PollEvent(&event)) { 71 | imgui::process_event(event); 72 | 73 | switch (event.type) { 74 | case SDL_EVENT_WINDOW_MINIMIZED: { 75 | // Android/iOS: Application backgrounded 76 | g_events.push_back(AuroraEvent{ 77 | .type = AURORA_PAUSED, 78 | }); 79 | break; 80 | } 81 | case SDL_EVENT_WINDOW_RESTORED: { 82 | // Android/iOS: Application focused 83 | g_events.push_back(AuroraEvent{ 84 | .type = AURORA_UNPAUSED, 85 | }); 86 | break; 87 | } 88 | case SDL_EVENT_WINDOW_MOVED: { 89 | g_events.push_back(AuroraEvent{ 90 | .type = AURORA_WINDOW_MOVED, 91 | .windowPos = {.x = event.window.data1, .y = event.window.data2}, 92 | }); 93 | break; 94 | } 95 | case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: { 96 | resize_swapchain(); 97 | g_events.push_back(AuroraEvent{ 98 | .type = AURORA_DISPLAY_SCALE_CHANGED, 99 | .windowSize = get_window_size(), 100 | }); 101 | break; 102 | } 103 | case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: { 104 | resize_swapchain(); 105 | g_events.push_back(AuroraEvent{ 106 | .type = AURORA_WINDOW_RESIZED, 107 | .windowSize = get_window_size(), 108 | }); 109 | break; 110 | } 111 | case SDL_EVENT_GAMEPAD_ADDED: { 112 | auto instance = input::add_controller(event.gdevice.which); 113 | g_events.push_back(AuroraEvent{ 114 | .type = AURORA_CONTROLLER_ADDED, 115 | .controller = instance, 116 | }); 117 | break; 118 | } 119 | case SDL_EVENT_GAMEPAD_REMOVED: { 120 | input::remove_controller(event.gdevice.which); 121 | g_events.push_back(AuroraEvent{ 122 | .type = AURORA_CONTROLLER_REMOVED, 123 | .controller = event.gdevice.which, 124 | }); 125 | break; 126 | } 127 | case SDL_EVENT_QUIT: 128 | g_events.push_back(AuroraEvent{ 129 | .type = AURORA_EXIT, 130 | }); 131 | default: 132 | break; 133 | } 134 | 135 | g_events.push_back(AuroraEvent{ 136 | .type = AURORA_SDL_EVENT, 137 | .sdl = event, 138 | }); 139 | } 140 | g_events.push_back(AuroraEvent{ 141 | .type = AURORA_NONE, 142 | }); 143 | return g_events.data(); 144 | } 145 | 146 | bool create_window(AuroraBackend backend) { 147 | SDL_WindowFlags flags = SDL_WINDOW_HIGH_PIXEL_DENSITY; 148 | #if TARGET_OS_IOS || TARGET_OS_TV 149 | flags |= SDL_WINDOW_FULLSCREEN; 150 | #else 151 | flags |= SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; 152 | if (g_config.startFullscreen) { 153 | flags |= SDL_WINDOW_FULLSCREEN; 154 | } 155 | #endif 156 | switch (backend) { 157 | #ifdef DAWN_ENABLE_BACKEND_VULKAN 158 | case BACKEND_VULKAN: 159 | flags |= SDL_WINDOW_VULKAN; 160 | break; 161 | #endif 162 | #ifdef DAWN_ENABLE_BACKEND_METAL 163 | case BACKEND_METAL: 164 | flags |= SDL_WINDOW_METAL; 165 | break; 166 | #endif 167 | #ifdef DAWN_ENABLE_BACKEND_OPENGL 168 | case BACKEND_OPENGL: 169 | case BACKEND_OPENGLES: 170 | flags |= SDL_WINDOW_OPENGL; 171 | break; 172 | #endif 173 | default: 174 | break; 175 | } 176 | #ifdef __SWITCH__ 177 | Sint32 width = 1280; 178 | Sint32 height = 720; 179 | #else 180 | auto width = static_cast(g_config.windowWidth); 181 | auto height = static_cast(g_config.windowHeight); 182 | if (width == 0 || height == 0) { 183 | width = 1280; 184 | height = 960; 185 | } 186 | 187 | Sint32 posX = g_config.windowPosX; 188 | Sint32 posY = g_config.windowPosY; 189 | if (posX < 0 || posY < 0) { 190 | posX = SDL_WINDOWPOS_UNDEFINED; 191 | posY = SDL_WINDOWPOS_UNDEFINED; 192 | } 193 | 194 | #endif 195 | const auto props = SDL_CreateProperties(); 196 | TRY(SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, g_config.appName), "Failed to set {}: {}", 197 | SDL_PROP_WINDOW_CREATE_TITLE_STRING, SDL_GetError()); 198 | TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, posX), "Failed to set {}: {}", 199 | SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_GetError()); 200 | TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, posY), "Failed to set {}: {}", 201 | SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_GetError()); 202 | TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width), "Failed to set {}: {}", 203 | SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, SDL_GetError()); 204 | TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height), "Failed to set {}: {}", 205 | SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, SDL_GetError()); 206 | TRY(SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags), "Failed to set {}: {}", 207 | SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, SDL_GetError()); 208 | g_window = SDL_CreateWindowWithProperties(props); 209 | if (g_window == nullptr) { 210 | Log.error("Failed to create window: {}", SDL_GetError()); 211 | return false; 212 | } 213 | set_window_icon(); 214 | return true; 215 | } 216 | 217 | bool create_renderer() { 218 | if (g_window == nullptr) { 219 | return false; 220 | } 221 | const auto props = SDL_CreateProperties(); 222 | TRY(SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, g_window), "Failed to set {}: {}", 223 | SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, SDL_GetError()); 224 | TRY(SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, SDL_RENDERER_VSYNC_ADAPTIVE), 225 | "Failed to set {}: {}", SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, SDL_GetError()); 226 | g_renderer = SDL_CreateRendererWithProperties(props); 227 | if (g_renderer == nullptr) { 228 | Log.error("Failed to create renderer: {}", SDL_GetError()); 229 | return false; 230 | } 231 | return true; 232 | } 233 | 234 | void destroy_window() { 235 | if (g_renderer != nullptr) { 236 | SDL_DestroyRenderer(g_renderer); 237 | g_renderer = nullptr; 238 | } 239 | if (g_window != nullptr) { 240 | SDL_DestroyWindow(g_window); 241 | g_window = nullptr; 242 | } 243 | } 244 | 245 | void show_window() { 246 | if (g_window != nullptr) { 247 | TRY_WARN(SDL_ShowWindow(g_window), "Failed to show window: {}", SDL_GetError()); 248 | } 249 | } 250 | 251 | bool initialize() { 252 | /* We don't want to initialize anything input related here, otherwise the add events will get lost to the void */ 253 | TRY(SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_VIDEO), "Error initializing SDL: {}", SDL_GetError()); 254 | 255 | #if !defined(_WIN32) && !defined(__APPLE__) 256 | TRY(SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"), "Error setting {}: {}", 257 | SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, SDL_GetError()); 258 | #endif 259 | TRY(SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, g_config.appName), "Error setting {}: {}", 260 | SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, SDL_GetError()); 261 | TRY(SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE, "1"), "Error setting {}: {}", 262 | SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE, SDL_GetError()); 263 | 264 | TRY(SDL_DisableScreenSaver(), "Error disabling screensaver: {}", SDL_GetError()); 265 | if (g_config.allowJoystickBackgroundEvents) { 266 | TRY(SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"), "Error setting {}: {}", 267 | SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, SDL_GetError()); 268 | } 269 | 270 | return true; 271 | } 272 | 273 | void shutdown() { 274 | destroy_window(); 275 | TRY_WARN(SDL_EnableScreenSaver(), "Error enabling screensaver: {}", SDL_GetError()); 276 | SDL_Quit(); 277 | } 278 | 279 | AuroraWindowSize get_window_size() { 280 | int width = 0; 281 | int height = 0; 282 | int fb_w = 0; 283 | int fb_h = 0; 284 | ASSERT(SDL_GetWindowSize(g_window, &width, &height), "Failed to get window size: {}", SDL_GetError()); 285 | ASSERT(SDL_GetWindowSizeInPixels(g_window, &fb_w, &fb_h), "Failed to get window size in pixels: {}", SDL_GetError()); 286 | const float scale = SDL_GetWindowDisplayScale(g_window); 287 | return { 288 | .width = static_cast(width), 289 | .height = static_cast(height), 290 | .fb_width = static_cast(fb_w), 291 | .fb_height = static_cast(fb_h), 292 | .scale = scale, 293 | }; 294 | } 295 | 296 | SDL_Window* get_sdl_window() { return g_window; } 297 | 298 | SDL_Renderer* get_sdl_renderer() { return g_renderer; } 299 | 300 | void set_title(const char* title) { 301 | TRY_WARN(SDL_SetWindowTitle(g_window, title), "Failed to set window title: {}", SDL_GetError()); 302 | } 303 | 304 | void set_fullscreen(bool fullscreen) { 305 | TRY_WARN(SDL_SetWindowFullscreen(g_window, fullscreen), "Failed to set window fullscreen: {}", SDL_GetError()); 306 | } 307 | 308 | bool get_fullscreen() { return (SDL_GetWindowFlags(g_window) & SDL_WINDOW_FULLSCREEN) != 0u; } 309 | 310 | } // namespace aurora::window 311 | -------------------------------------------------------------------------------- /lib/window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct SDL_Window; 7 | struct SDL_Renderer; 8 | 9 | namespace aurora::window { 10 | bool initialize(); 11 | void shutdown(); 12 | bool create_window(AuroraBackend backend); 13 | bool create_renderer(); 14 | void destroy_window(); 15 | void show_window(); 16 | AuroraWindowSize get_window_size(); 17 | const AuroraEvent* poll_events(); 18 | SDL_Window* get_sdl_window(); 19 | SDL_Renderer* get_sdl_renderer(); 20 | void set_title(const char* title); 21 | void set_fullscreen(bool fullscreen); 22 | bool get_fullscreen(); 23 | }; // namespace aurora::window 24 | --------------------------------------------------------------------------------