diff --git a/Runtime/CGameAllocator.cpp b/Runtime/CGameAllocator.cpp index 87f062408..71369a62d 100644 --- a/Runtime/CGameAllocator.cpp +++ b/Runtime/CGameAllocator.cpp @@ -1,7 +1,9 @@ #include "Runtime/CGameAllocator.hpp" +#include + namespace metaforce { -logvisor::Module AllocLog("metaforce::CGameAllocator"); +static logvisor::Module Log("metaforce::CGameAllocator"); #pragma GCC diagnostic ignored "-Wclass-memaccess" @@ -44,7 +46,7 @@ u8* CGameAllocator::Alloc(size_t len) { void CGameAllocator::Free(u8* ptr) { SChunkDescription* info = reinterpret_cast(ptr - sizeof(SChunkDescription)); if (info->magic != 0xE8E8E8E8 || info->sentinal != 0xEFEFEFEF) { - AllocLog.report(logvisor::Fatal, FMT_STRING("Invalid chunk description, memory corruption!")); + Log.report(logvisor::Fatal, FMT_STRING("Invalid chunk description, memory corruption!")); return; } diff --git a/Runtime/IFactory.hpp b/Runtime/IFactory.hpp index d82e1247d..757fb0ae1 100644 --- a/Runtime/IFactory.hpp +++ b/Runtime/IFactory.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include diff --git a/Runtime/ImGuiConsole.cpp b/Runtime/ImGuiConsole.cpp index 3d5068478..4d4434480 100644 --- a/Runtime/ImGuiConsole.cpp +++ b/Runtime/ImGuiConsole.cpp @@ -1166,18 +1166,18 @@ void ImGuiConsole::PreUpdate() { g_StateManager->SetActiveRandomToDefault(); } - if (ImGui::IsKeyReleased(int(KeyCode::Grave))) { + if (ImGui::IsKeyReleased(ImGuiKey_GraveAccent)) { m_isVisible ^= 1; } if (m_stepFrame) { g_Main->SetPaused(true); m_stepFrame = false; } - if (m_paused && !m_stepFrame && ImGui::IsKeyPressed(int(KeyCode::F6))) { + if (m_paused && !m_stepFrame && ImGui::IsKeyPressed(ImGuiKey_F6)) { g_Main->SetPaused(false); m_stepFrame = true; } - if (ImGui::IsKeyReleased(int(KeyCode::F5))) { + if (ImGui::IsKeyReleased(ImGuiKey_F5)) { m_paused ^= 1; g_Main->SetPaused(m_paused); } diff --git a/Runtime/World/CPathFindArea.cpp b/Runtime/World/CPathFindArea.cpp index 7f6391476..d42cf8082 100644 --- a/Runtime/World/CPathFindArea.cpp +++ b/Runtime/World/CPathFindArea.cpp @@ -1,5 +1,7 @@ #include "Runtime/World/CPathFindArea.hpp" +#include + #include "Runtime/CToken.hpp" #include "Runtime/IVParamObj.hpp" diff --git a/aurora/CMakeLists.txt b/aurora/CMakeLists.txt index 88ff640ec..22a5d2cca 100644 --- a/aurora/CMakeLists.txt +++ b/aurora/CMakeLists.txt @@ -4,6 +4,9 @@ if (NOT MSVC) endif () add_subdirectory(../extern/dawn dawn EXCLUDE_FROM_ALL) +target_compile_definitions(dawn_native PRIVATE + DAWN_ENABLE_VULKAN_VALIDATION_LAYERS + DAWN_VK_DATA_DIR="vulkandata") if (NOT MSVC) target_compile_options(SPIRV-Tools-static PRIVATE -Wno-implicit-fallthrough) target_compile_options(SPIRV-Tools-opt PRIVATE -Wno-implicit-fallthrough) @@ -11,6 +14,7 @@ endif () add_library(aurora STATIC lib/aurora.cpp + lib/gpu.cpp lib/imgui.cpp lib/dawn/BackendBinding.cpp lib/gfx/common.cpp diff --git a/aurora/lib/aurora.cpp b/aurora/lib/aurora.cpp index 2e50297d2..2f95aab25 100644 --- a/aurora/lib/aurora.cpp +++ b/aurora/lib/aurora.cpp @@ -1,65 +1,31 @@ #include +#include "gfx/common.hpp" +#include "gpu.hpp" +#include "imgui.hpp" + #include -#include -// TODO HACK: dawn doesn't expose device toggles -#include "../extern/dawn/src/dawn/native/Toggles.h" -#include +#include #include #include -#ifdef DAWN_ENABLE_BACKEND_VULKAN -#include -#include -#endif -#ifdef DAWN_ENABLE_BACKEND_OPENGL -#include -#include -#endif -#ifdef DAWN_ENABLE_BACKEND_METAL -#include -#include -#endif - -#include "dawn/BackendBinding.hpp" - -// imgui -#include -#include - -// TODO HACK: dawn doesn't expose device toggles -namespace dawn::native { -class DeviceBase { -public: - void SetToggle(Toggle toggle, bool isEnabled); -}; -} // namespace dawn::native - namespace aurora { -// TODO: Move global state to a class/struct? static logvisor::Module Log("aurora"); +// TODO: Move global state to a class/struct? static std::unique_ptr g_AppDelegate; static std::vector g_Args; // SDL static SDL_Window* g_Window; -// Dawn / WebGPU -#ifdef DAWN_ENABLE_BACKEND_VULKAN -static wgpu::BackendType preferredBackendType = wgpu::BackendType::Vulkan; -#elif DAWN_ENABLE_BACKEND_METAL -static wgpu::BackendType preferredBackendType = wgpu::BackendType::Metal; -#else -static wgpu::BackendType preferredBackendType = wgpu::BackendType::OpenGL; -#endif -static std::unique_ptr g_Instance; -static dawn::native::Adapter g_Adapter; -static wgpu::AdapterProperties g_AdapterProperties; -wgpu::Device g_Device; -wgpu::Queue g_Queue; -static wgpu::SwapChain g_SwapChain; -static std::unique_ptr g_BackendBinding; +// GPU +using gpu::g_depthBuffer; +using gpu::g_device; +using gpu::g_frameBuffer; +using gpu::g_frameBufferResolved; +using gpu::g_queue; +using gpu::g_swapChain; static void set_window_icon(Icon icon) noexcept { SDL_Surface* iconSurface = SDL_CreateRGBSurfaceFrom(icon.data.get(), icon.width, icon.height, 32, 4 * icon.width, @@ -79,7 +45,7 @@ static void set_window_icon(Icon icon) noexcept { static bool poll_events() noexcept { SDL_Event event; while (SDL_PollEvent(&event) != 0) { - ImGui_ImplSDL2_ProcessEvent(&event); + imgui::process_event(event); switch (event.type) { case SDL_WINDOWEVENT: { @@ -98,8 +64,7 @@ static bool poll_events() noexcept { break; } case SDL_WINDOWEVENT_RESIZED: { - auto format = static_cast(g_BackendBinding->GetPreferredSwapChainTextureFormat()); - g_SwapChain.Configure(format, wgpu::TextureUsage::RenderAttachment, event.window.data1, event.window.data2); + gpu::resize_swapchain(event.window.data1, event.window.data2); g_AppDelegate->onAppWindowResized( {static_cast(event.window.data1), static_cast(event.window.data2)}); break; @@ -211,7 +176,7 @@ void app_run(std::unique_ptr app, Icon icon, int argc, char** argv) } Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE; - switch (preferredBackendType) { + switch (gpu::preferredBackendType) { #ifdef DAWN_ENABLE_BACKEND_VULKAN case wgpu::BackendType::Vulkan: flags |= SDL_WINDOW_VULKAN; @@ -236,126 +201,104 @@ void app_run(std::unique_ptr app, Icon icon, int argc, char** argv) } set_window_icon(std::move(icon)); - Log.report(logvisor::Info, FMT_STRING("Creating Dawn instance")); - g_Instance = std::make_unique(); - utils::DiscoverAdapter(g_Instance.get(), g_Window, preferredBackendType); + gpu::initialize(g_Window); + gfx::construct_state(); - { - std::vector adapters = g_Instance->GetAdapters(); - auto adapterIt = std::find_if(adapters.begin(), adapters.end(), [](const dawn::native::Adapter adapter) -> bool { - wgpu::AdapterProperties properties; - adapter.GetProperties(&properties); - return properties.backendType == preferredBackendType; - }); - if (adapterIt == adapters.end()) { - Log.report(logvisor::Fatal, FMT_STRING("Failed to find usable graphics backend")); - } - g_Adapter = *adapterIt; - } - g_Adapter.GetProperties(&g_AdapterProperties); - const auto backendName = magic_enum::enum_name(g_AdapterProperties.backendType); - Log.report(logvisor::Info, FMT_STRING("Using {} graphics backend"), backendName); - - { - const std::array requiredFeatures{ - wgpu::FeatureName::TextureCompressionBC, - }; - const auto deviceDescriptor = wgpu::DeviceDescriptor{ - .requiredFeaturesCount = requiredFeatures.size(), - .requiredFeatures = requiredFeatures.data(), - }; - g_Device = wgpu::Device::Acquire(g_Adapter.CreateDevice(&deviceDescriptor)); - // TODO HACK: dawn doesn't expose device toggles - static_cast(static_cast(g_Device.Get())) - ->SetToggle(dawn::native::Toggle::UseUserDefinedLabelsInBackend, true); - } - - g_BackendBinding = std::unique_ptr( - utils::CreateBinding(g_AdapterProperties.backendType, g_Window, g_Device.Get())); - if (!g_BackendBinding) { - Log.report(logvisor::Fatal, FMT_STRING("Unsupported backend {}"), backendName); - } - - g_Queue = g_Device.GetQueue(); - { - wgpu::SwapChainDescriptor descriptor{}; - descriptor.implementation = g_BackendBinding->GetSwapChainImplementation(); - g_SwapChain = g_Device.CreateSwapChain(nullptr, &descriptor); - } - { - auto size = get_window_size(); - auto format = static_cast(g_BackendBinding->GetPreferredSwapChainTextureFormat()); - Log.report(logvisor::Info, FMT_STRING("Using swapchain format {}"), magic_enum::enum_name(format)); - g_SwapChain.Configure(format, wgpu::TextureUsage::RenderAttachment, size.width, size.height); - } - - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - io.IniFilename = nullptr; + imgui::create_context(); g_AppDelegate->onImGuiInit(1.f); // TODO scale - ImGui_ImplSDL2_InitForMetal(g_Window); - ImGui_ImplWGPU_Init(g_Device.Get(), 1, g_BackendBinding->GetPreferredSwapChainTextureFormat()); + imgui::initialize(g_Window); g_AppDelegate->onImGuiAddTextures(); g_AppDelegate->onAppLaunched(); g_AppDelegate->onAppWindowResized(get_window_size()); while (poll_events()) { - ImGui_ImplWGPU_NewFrame(); - ImGui_ImplSDL2_NewFrame(); - ImGui::NewFrame(); + imgui::new_frame(); + if (!g_AppDelegate->onAppIdle(ImGui::GetIO().DeltaTime)) { + break; + } - g_AppDelegate->onAppIdle(ImGui::GetIO().DeltaTime); - - const wgpu::TextureView view = g_SwapChain.GetCurrentTextureView(); + const wgpu::TextureView view = g_swapChain.GetCurrentTextureView(); g_AppDelegate->onAppDraw(); - ImGui::Render(); - auto encoder = g_Device.CreateCommandEncoder(); + const auto encoderDescriptor = wgpu::CommandEncoderDescriptor{ + .label = "Redraw encoder", + }; + auto encoder = g_device.CreateCommandEncoder(&encoderDescriptor); { - std::array attachments{wgpu::RenderPassColorAttachment{ - .view = view, - .loadOp = wgpu::LoadOp::Clear, - .storeOp = wgpu::StoreOp::Store, - .clearColor = {0.f, 0.f, 0.f, 0.f}, - }}; + const std::array attachments{ + wgpu::RenderPassColorAttachment{ + .view = view, + // .resolveTarget = g_frameBufferResolved.view, + .loadOp = wgpu::LoadOp::Clear, + .storeOp = wgpu::StoreOp::Store, + .clearColor = {0.f, 0.f, 0.f, 0.f}, + }, + }; + const auto depthStencilAttachment = wgpu::RenderPassDepthStencilAttachment{ + .view = g_depthBuffer.view, + .depthLoadOp = wgpu::LoadOp::Clear, + .depthStoreOp = wgpu::StoreOp::Discard, + .clearDepth = 1.f, + .stencilLoadOp = wgpu::LoadOp::Clear, + .stencilStoreOp = wgpu::StoreOp::Discard, + }; auto renderPassDescriptor = wgpu::RenderPassDescriptor{ - .label = "Render Pass", + .label = "Main render pass", + .colorAttachmentCount = attachments.size(), + .colorAttachments = attachments.data(), + .depthStencilAttachment = &depthStencilAttachment, + }; + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + gfx::render(pass); + pass.End(); + } + { + const std::array attachments{ + wgpu::RenderPassColorAttachment{ + .view = view, + .loadOp = wgpu::LoadOp::Load, + .storeOp = wgpu::StoreOp::Store, + }, + }; + auto renderPassDescriptor = wgpu::RenderPassDescriptor{ + .label = "ImGui render pass", .colorAttachmentCount = attachments.size(), .colorAttachments = attachments.data(), }; auto pass = encoder.BeginRenderPass(&renderPassDescriptor); - ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass.Get()); + imgui::render(pass); pass.End(); } const auto buffer = encoder.Finish(); - g_Queue.Submit(1, &buffer); - g_SwapChain.Present(); + g_queue.Submit(1, &buffer); + g_swapChain.Present(); g_AppDelegate->onAppPostDraw(); + + ImGui::EndFrame(); } g_AppDelegate->onAppExiting(); - wgpuSwapChainRelease(g_SwapChain.Release()); - wgpuQueueRelease(g_Queue.Release()); - g_BackendBinding.reset(); - wgpuDeviceRelease(g_Device.Release()); - g_Instance.reset(); + imgui::shutdown(); + gpu::shutdown(); SDL_DestroyWindow(g_Window); SDL_Quit(); } std::vector get_args() noexcept { return g_Args; } + WindowSize get_window_size() noexcept { int width, height; SDL_GetWindowSize(g_Window, &width, &height); return {static_cast(width), static_cast(height)}; } + void set_window_title(zstring_view title) noexcept { SDL_SetWindowTitle(g_Window, title.c_str()); } + Backend get_backend() noexcept { - switch (g_AdapterProperties.backendType) { + switch (gpu::g_backendType) { case wgpu::BackendType::WebGPU: return Backend::WebGPU; case wgpu::BackendType::D3D11: @@ -374,7 +317,9 @@ Backend get_backend() noexcept { return Backend::Invalid; } } -std::string_view get_backend_string() noexcept { return magic_enum::enum_name(g_AdapterProperties.backendType); } + +std::string_view get_backend_string() noexcept { return magic_enum::enum_name(gpu::g_backendType); } + void set_fullscreen(bool fullscreen) noexcept { auto flags = SDL_GetWindowFlags(g_Window); if (fullscreen) { @@ -388,12 +333,15 @@ void set_fullscreen(bool fullscreen) noexcept { int32_t get_controller_player_index(uint32_t which) noexcept { return -1; // TODO } + void set_controller_player_index(uint32_t which, int32_t index) noexcept { // TODO } + bool is_controller_gamecube(uint32_t which) noexcept { return true; // TODO } + std::string get_controller_name(uint32_t which) noexcept { return ""; // TODO } diff --git a/aurora/lib/dawn/BackendBinding.cpp b/aurora/lib/dawn/BackendBinding.cpp index 77fc9bf1a..6face6d99 100644 --- a/aurora/lib/dawn/BackendBinding.cpp +++ b/aurora/lib/dawn/BackendBinding.cpp @@ -5,7 +5,7 @@ #include #endif -namespace aurora::utils { +namespace aurora::gpu::utils { #if defined(DAWN_ENABLE_BACKEND_D3D12) BackendBinding* CreateD3D12Binding(SDL_Window* window, WGPUDevice device); @@ -76,4 +76,4 @@ BackendBinding* CreateBinding(wgpu::BackendType type, SDL_Window* window, WGPUDe } } -} // namespace aurora::utils +} // namespace aurora::gpu::utils diff --git a/aurora/lib/dawn/BackendBinding.hpp b/aurora/lib/dawn/BackendBinding.hpp index 84180d1d8..bfcae76b3 100644 --- a/aurora/lib/dawn/BackendBinding.hpp +++ b/aurora/lib/dawn/BackendBinding.hpp @@ -5,7 +5,7 @@ struct SDL_Window; -namespace aurora::utils { +namespace aurora::gpu::utils { class BackendBinding { public: @@ -24,4 +24,4 @@ protected: void DiscoverAdapter(dawn::native::Instance* instance, SDL_Window* window, wgpu::BackendType type); BackendBinding* CreateBinding(wgpu::BackendType type, SDL_Window* window, WGPUDevice device); -} // namespace aurora::utils +} // namespace aurora::gpu::utils diff --git a/aurora/lib/dawn/MetalBinding.mm b/aurora/lib/dawn/MetalBinding.mm index 23b9de62a..e48f3be14 100644 --- a/aurora/lib/dawn/MetalBinding.mm +++ b/aurora/lib/dawn/MetalBinding.mm @@ -24,7 +24,7 @@ template DawnSwapChainImplementation CreateSwapChainImplementation( return impl; } -namespace aurora::utils { +namespace aurora::gpu::utils { class SwapChainImplMTL { public: using WSIContext = DawnWSIContextMetal; @@ -105,4 +105,4 @@ private: }; BackendBinding *CreateMetalBinding(SDL_Window *window, WGPUDevice device) { return new MetalBinding(window, device); } -} // namespace aurora::utils +} // namespace aurora::gpu::utils diff --git a/aurora/lib/dawn/OpenGLBinding.cpp b/aurora/lib/dawn/OpenGLBinding.cpp index e112b6193..41c379b15 100644 --- a/aurora/lib/dawn/OpenGLBinding.cpp +++ b/aurora/lib/dawn/OpenGLBinding.cpp @@ -3,26 +3,33 @@ #include #include -namespace aurora::utils { +namespace aurora::gpu::utils { class OpenGLBinding : public BackendBinding { public: OpenGLBinding(SDL_Window* window, WGPUDevice device) : BackendBinding(window, device) {} uint64_t GetSwapChainImplementation() override { if (m_swapChainImpl.userData == nullptr) { - m_swapChainImpl = dawn::native::opengl::CreateNativeSwapChainImpl( - m_device, [](void* userdata) { SDL_GL_SwapWindow(static_cast(userdata)); }, m_window); + CreateSwapChainImpl(); } return reinterpret_cast(&m_swapChainImpl); } WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { + if (m_swapChainImpl.userData == nullptr) { + CreateSwapChainImpl(); + } return dawn::native::opengl::GetNativeSwapChainPreferredFormat(&m_swapChainImpl); } private: DawnSwapChainImplementation m_swapChainImpl{}; + + void CreateSwapChainImpl() { + m_swapChainImpl = dawn::native::opengl::CreateNativeSwapChainImpl( + m_device, [](void* userdata) { SDL_GL_SwapWindow(static_cast(userdata)); }, m_window); + } }; BackendBinding* CreateOpenGLBinding(SDL_Window* window, WGPUDevice device) { return new OpenGLBinding(window, device); } -} // namespace aurora::utils +} // namespace aurora::gpu::utils diff --git a/aurora/lib/dawn/VulkanBinding.cpp b/aurora/lib/dawn/VulkanBinding.cpp index c6a56c949..065ba7734 100644 --- a/aurora/lib/dawn/VulkanBinding.cpp +++ b/aurora/lib/dawn/VulkanBinding.cpp @@ -4,30 +4,36 @@ #include #include -namespace aurora::utils { +namespace aurora::gpu::utils { class VulkanBinding : public BackendBinding { public: VulkanBinding(SDL_Window* window, WGPUDevice device) : BackendBinding(window, device) {} uint64_t GetSwapChainImplementation() override { if (m_swapChainImpl.userData == nullptr) { - VkSurfaceKHR surface = VK_NULL_HANDLE; - if (SDL_Vulkan_CreateSurface(m_window, dawn::native::vulkan::GetInstance(m_device), &surface) != SDL_TRUE) { - assert(false); - } - m_swapChainImpl = dawn::native::vulkan::CreateNativeSwapChainImpl(m_device, surface); + CreateSwapChainImpl(); } return reinterpret_cast(&m_swapChainImpl); } WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { - assert(m_swapChainImpl.userData != nullptr); + if (m_swapChainImpl.userData == nullptr) { + CreateSwapChainImpl(); + } return dawn::native::vulkan::GetNativeSwapChainPreferredFormat(&m_swapChainImpl); } private: DawnSwapChainImplementation m_swapChainImpl{}; + + void CreateSwapChainImpl() { + VkSurfaceKHR surface = VK_NULL_HANDLE; + if (SDL_Vulkan_CreateSurface(m_window, dawn::native::vulkan::GetInstance(m_device), &surface) != SDL_TRUE) { + assert(false); + } + m_swapChainImpl = dawn::native::vulkan::CreateNativeSwapChainImpl(m_device, surface); + } }; BackendBinding* CreateVulkanBinding(SDL_Window* window, WGPUDevice device) { return new VulkanBinding(window, device); } -} // namespace aurora::utils +} // namespace aurora::gpu::utils diff --git a/aurora/lib/gfx/common.cpp b/aurora/lib/gfx/common.cpp index 03e1cee1e..276813091 100644 --- a/aurora/lib/gfx/common.cpp +++ b/aurora/lib/gfx/common.cpp @@ -1,10 +1,20 @@ #include "common.hpp" -#include - +#include "../gpu.hpp" #include "movie_player/shader.hpp" +#include +#include + namespace aurora::gfx { +static logvisor::Module Log("aurora::gfx"); + +using gpu::g_device; +using gpu::g_queue; + +struct ShaderState { + movie_player::State moviePlayer; +}; struct ShaderDrawCommand { ShaderType type; union { @@ -45,9 +55,46 @@ zeus::CMatrix4f g_mvInv; zeus::CMatrix4f g_proj; metaforce::CFogState g_fogState; -std::vector g_commands; +static std::mutex g_pipelineMutex; +static std::unordered_map g_pipelines; +static std::vector g_queuedPipelines; +static std::unordered_map g_cachedBindGroups; -bool get_dxt_compression_supported() noexcept { return g_Device.HasFeature(wgpu::FeatureName::TextureCompressionBC); } +static ByteBuffer g_verts; +static ByteBuffer g_uniforms; +static ByteBuffer g_indices; +wgpu::Buffer g_vertexBuffer; +wgpu::Buffer g_uniformBuffer; +wgpu::Buffer g_indexBuffer; + +static ShaderState g_state; +static PipelineRef g_currentPipeline; + +static std::vector g_commands; + +using NewPipelineCallback = std::function; +static PipelineRef find_pipeline(PipelineCreateCommand command, NewPipelineCallback cb) { + const auto hash = xxh3_hash(command); + bool found; + { + std::lock_guard guard{g_pipelineMutex}; + const auto ref = g_pipelines.find(hash); + found = ref != g_pipelines.end(); + } + if (!found) { + // TODO another thread + wgpu::RenderPipeline pipeline = cb(); + { + std::lock_guard guard{g_pipelineMutex}; + g_pipelines[hash] = std::move(pipeline); + } + } + return hash; +} + +static void push_draw_command(ShaderDrawCommand data) { g_commands.push_back({CommandType::Draw, {.draw = data}}); } + +bool get_dxt_compression_supported() noexcept { return g_device.HasFeature(wgpu::FeatureName::TextureCompressionBC); } void update_model_view(const zeus::CMatrix4f& mv, const zeus::CMatrix4f& mv_inv) noexcept { g_mv = mv; @@ -98,8 +145,134 @@ void queue_colored_quad(CameraFilterType filter_type, ZTest z_comparison, bool z const zeus::CRectangle& rect, float z) noexcept { // TODO } + void queue_movie_player(const TextureHandle& tex_y, const TextureHandle& tex_u, const TextureHandle& tex_v, const zeus::CColor& color, float h_pad, float v_pad) noexcept { - // TODO + auto data = movie_player::make_draw_data(g_state.moviePlayer, tex_y, tex_u, tex_v, color, h_pad, v_pad); + push_draw_command({.type = ShaderType::MoviePlayer, .moviePlayer = data}); +} +template <> +PipelineRef pipeline_ref(movie_player::PipelineConfig config) { + return find_pipeline({.type = ShaderType::MoviePlayer, .moviePlayer = config}, + [=]() { return create_pipeline(g_state.moviePlayer, config); }); +} + +void construct_state() { + { + const auto uniformDescriptor = wgpu::BufferDescriptor{ + .label = "Shared Uniform Buffer", + .usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst, + .size = 134217728, // 128mb + }; + g_uniformBuffer = g_device.CreateBuffer(&uniformDescriptor); + } + { + const auto vertexDescriptor = wgpu::BufferDescriptor{ + .label = "Shared Vertex Buffer", + .usage = wgpu::BufferUsage::Vertex | wgpu::BufferUsage::CopyDst, + .size = 16777216, // 16mb + }; + g_vertexBuffer = g_device.CreateBuffer(&vertexDescriptor); + } + { + const auto vertexDescriptor = wgpu::BufferDescriptor{ + .label = "Shared Index Buffer", + .usage = wgpu::BufferUsage::Vertex | wgpu::BufferUsage::CopyDst, + .size = 4194304, // 4mb + }; + g_indexBuffer = g_device.CreateBuffer(&vertexDescriptor); + } + + g_state.moviePlayer = movie_player::construct_state(); +} + +void render(const wgpu::RenderPassEncoder& pass) { + { + g_queue.WriteBuffer(g_vertexBuffer, 0, g_verts.data(), g_verts.size()); + g_queue.WriteBuffer(g_uniformBuffer, 0, g_uniforms.data(), g_uniforms.size()); + g_queue.WriteBuffer(g_indexBuffer, 0, g_indices.data(), g_indices.size()); + g_verts.clear(); + g_uniforms.clear(); + g_indices.clear(); + } + + g_currentPipeline = UINT64_MAX; + + for (const auto& cmd : g_commands) { + switch (cmd.type) { + case CommandType::SetViewport: { + const auto& vp = cmd.data.setViewport; + pass.SetViewport(vp.rect.position.x(), vp.rect.position.y(), vp.rect.size.x(), vp.rect.size.y(), vp.znear, + vp.zfar); + } break; + case CommandType::SetScissor: { + const auto& sc = cmd.data.setScissor; + pass.SetScissorRect(sc.x, sc.y, sc.w, sc.h); + } break; + case CommandType::Draw: { + const auto& draw = cmd.data.draw; + switch (draw.type) { + case ShaderType::Aabb: + // TODO + break; + case ShaderType::TexturedQuad: + // TODO + break; + case ShaderType::MoviePlayer: + movie_player::render(g_state.moviePlayer, draw.moviePlayer, pass); + break; + } + } break; + } + } + + g_commands.clear(); +} + +bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass) { + if (ref == g_currentPipeline) { + return true; + } + std::lock_guard guard{g_pipelineMutex}; + if (!g_pipelines.contains(ref)) { + return false; + } + pass.SetPipeline(g_pipelines[ref]); + return true; +} + +static inline Range push(ByteBuffer& target, const uint8_t* data, size_t length, size_t alignment) { + size_t padding = 0; + if (alignment != 0) { + padding = alignment - length % alignment; + } + auto begin = target.size(); + target.append(data, length); + if (padding > 0) { + target.append_zeroes(padding); + } + return {begin, begin + length}; +} +Range push_verts(const uint8_t* data, size_t length) { return push(g_verts, data, length, 0 /* TODO? */); } +Range push_indices(const uint8_t* data, size_t length) { return push(g_indices, data, length, 0 /* TODO? */); } +Range push_uniform(const uint8_t* data, size_t length) { + wgpu::SupportedLimits limits; + g_device.GetLimits(&limits); + return push(g_uniforms, data, length, limits.limits.minUniformBufferOffsetAlignment); +} + +BindGroupRef bind_group_ref(const wgpu::BindGroupDescriptor& descriptor) { + const auto id = + xxh3_hash(descriptor.entries, descriptor.entryCount * sizeof(wgpu::BindGroupEntry), xxh3_hash(descriptor)); + if (!g_cachedBindGroups.contains(id)) { + g_cachedBindGroups[id] = g_device.CreateBindGroup(&descriptor); + } + return id; +} +const wgpu::BindGroup& find_bind_group(BindGroupRef id) { + if (!g_cachedBindGroups.contains(id)) { + Log.report(logvisor::Fatal, FMT_STRING("get_bind_group: failed to locate {}"), id); + } + return g_cachedBindGroups[id]; } } // namespace aurora::gfx diff --git a/aurora/lib/gfx/common.hpp b/aurora/lib/gfx/common.hpp index 71df4d9fe..569dad084 100644 --- a/aurora/lib/gfx/common.hpp +++ b/aurora/lib/gfx/common.hpp @@ -9,24 +9,103 @@ #include #endif -template -XXH64_hash_t xxh3_hash(const T& input, XXH64_hash_t seed = 0) { - return XXH3_64bits_withSeed(&input, sizeof(T), seed); -} +#ifndef ALIGN +#define ALIGN(x, a) (((x) + ((a)-1)) & ~((a)-1)) +#endif namespace aurora { -extern wgpu::Device g_Device; -extern wgpu::Queue g_Queue; +static inline XXH64_hash_t xxh3_hash(const void* input, size_t len, XXH64_hash_t seed = 0) { + return XXH3_64bits_withSeed(input, len, seed); +} +template +static inline XXH64_hash_t xxh3_hash(const T& input, XXH64_hash_t seed = 0) { + return xxh3_hash(&input, sizeof(T), seed); +} + +class ByteBuffer { +public: + ByteBuffer() = default; + explicit ByteBuffer(size_t capacity) : m_data(static_cast(calloc(1, capacity))), m_capacity(capacity) {} + + ~ByteBuffer() { + if (m_data != nullptr) { + free(m_data); + } + } + + uint8_t* data() { return m_data; } + const uint8_t* data() const { return m_data; } + size_t size() const { return m_length; } + + void append(const void* data, size_t size) { + resize(m_length + size); + memcpy(m_data + m_length, data, size); + m_length += size; + } + void append_zeroes(size_t size) { + resize(m_length + size); + memset(m_data + m_length, 0, size); + m_length += size; + } + + void resize(size_t size) { + if (size == 0) { + clear(); + } else if (m_data == nullptr) { + m_data = static_cast(malloc(size)); + } else if (size > m_capacity) { + m_data = static_cast(realloc(m_data, size)); + } else { + return; + } + m_capacity = size; + } + + void clear() { + if (m_data != nullptr) { + free(m_data); + } + m_data = nullptr; + m_length = 0; + m_capacity = 0; + } + +private: + uint8_t* m_data = nullptr; + size_t m_length = 0; + size_t m_capacity = 0; +}; + +template +struct Vec2 { + T x{}; + T y{}; +}; +template +struct Vec3 { + T x{}; + T y{}; + T z{}; +}; +template +struct Vec4 { + T x{}; + T y{}; + T z{}; + T w{}; +}; } // namespace aurora namespace aurora::gfx { -static logvisor::Module Log("aurora::gfx"); - extern zeus::CMatrix4f g_mv; extern zeus::CMatrix4f g_mvInv; extern zeus::CMatrix4f g_proj; extern metaforce::CFogState g_fogState; +extern wgpu::Buffer g_vertexBuffer; +extern wgpu::Buffer g_uniformBuffer; +extern wgpu::Buffer g_indexBuffer; + struct TextureRef { wgpu::Texture texture; wgpu::TextureView view; @@ -38,11 +117,41 @@ struct TextureRef { }; using PipelineRef = uint64_t; -using Range = std::pair; +using BindGroupRef = uint64_t; +using Range = std::pair; enum class ShaderType { Aabb, TexturedQuad, MoviePlayer, }; + +void construct_state(); + +void render(const wgpu::RenderPassEncoder& pass); + +Range push_verts(const uint8_t* data, size_t length); +template +static inline Range push_verts(ArrayRef data) { + return push_verts(reinterpret_cast(data.data()), data.size() * sizeof(T)); +} +Range push_indices(const uint8_t* data, size_t length); +template +static inline Range push_indices(ArrayRef data) { + return push_indices(reinterpret_cast(data.data()), data.size() * sizeof(T)); +} +Range push_uniform(const uint8_t* data, size_t length); +template +static inline Range push_uniform(const T& data) { + return push_uniform(reinterpret_cast(&data), sizeof(T)); +} + +template +PipelineRef pipeline_ref(PipelineConfig config); +bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass); + +BindGroupRef bind_group_ref(const wgpu::BindGroupDescriptor& descriptor); +const wgpu::BindGroup& find_bind_group(BindGroupRef id); + +static inline zeus::CMatrix4f get_combined_matrix() { return g_proj * g_mv; } } // namespace aurora::gfx diff --git a/aurora/lib/gfx/movie_player/shader.cpp b/aurora/lib/gfx/movie_player/shader.cpp index e69de29bb..26676f7f2 100644 --- a/aurora/lib/gfx/movie_player/shader.cpp +++ b/aurora/lib/gfx/movie_player/shader.cpp @@ -0,0 +1,280 @@ +#include "shader.hpp" + +#include "../../gpu.hpp" + +namespace aurora::gfx::movie_player { +using gpu::g_device; +using gpu::g_graphicsConfig; +using gpu::utils::make_vertex_attributes; +using gpu::utils::make_vertex_buffer_layout; +using gpu::utils::make_vertex_state; + +State construct_state() { + wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{}; + wgslDescriptor.source = R"""( +struct Uniform { + xf: mat4x4; + color: vec4; +}; +@group(0) @binding(0) +var ubuf: Uniform; +@group(0) @binding(1) +var tex_sampler: sampler; +@group(1) @binding(0) +var tex_y: texture_2d; +@group(1) @binding(1) +var tex_u: texture_2d; +@group(1) @binding(2) +var tex_v: texture_2d; + +struct VertexOutput { + @builtin(position) pos: vec4; + @location(0) uv: vec2; +}; + +@stage(vertex) +fn vs_main(@location(0) in_pos: vec3, @location(1) in_uv: vec2) -> VertexOutput { + var out: VertexOutput; + out.pos = ubuf.xf * vec4(in_pos, 1.0); + out.uv = in_uv; + return out; +} + +@stage(fragment) +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + var yuv = vec3( + 1.1643 * (textureSample(tex_y, tex_sampler, in.uv).x - 0.0625), + textureSample(tex_u, tex_sampler, in.uv).x - 0.5, + textureSample(tex_v, tex_sampler, in.uv).x - 0.5 + ); + return ubuf.color * vec4( + yuv.x + 1.5958 * yuv.z, + yuv.x - 0.39173 * yuv.y - 0.8129 * yuv.z, + yuv.x + 2.017 * yuv.y, + 1.0 + ); +} +)"""; + const auto shaderDescriptor = wgpu::ShaderModuleDescriptor{ + .nextInChain = &wgslDescriptor, + .label = "Movie Player Shader", + }; + auto shader = g_device.CreateShaderModule(&shaderDescriptor); + + wgpu::SupportedLimits limits; + g_device.GetLimits(&limits); + const auto uniform_alignment = limits.limits.minUniformBufferOffsetAlignment; + const auto uniform_size = ALIGN(sizeof(Uniform), uniform_alignment); + + const std::array uniformLayoutEntries{ + wgpu::BindGroupLayoutEntry{ + .binding = 0, + .visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment, + .buffer = + wgpu::BufferBindingLayout{ + .type = wgpu::BufferBindingType::Uniform, + .hasDynamicOffset = true, + .minBindingSize = uniform_size, + }, + }, + wgpu::BindGroupLayoutEntry{ + .binding = 1, + .visibility = wgpu::ShaderStage::Fragment, + .sampler = + wgpu::SamplerBindingLayout{ + .type = wgpu::SamplerBindingType::Filtering, + }, + }, + }; + const auto uniformLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{ + .label = "Movie Player Uniform Bind Group Layout", + .entryCount = uniformLayoutEntries.size(), + .entries = uniformLayoutEntries.data(), + }; + auto uniformLayout = g_device.CreateBindGroupLayout(&uniformLayoutDescriptor); + + const auto samplerDescriptor = wgpu::SamplerDescriptor{ + .addressModeU = wgpu::AddressMode::Repeat, + .addressModeV = wgpu::AddressMode::Repeat, + .addressModeW = wgpu::AddressMode::Repeat, + .magFilter = wgpu::FilterMode::Linear, + .minFilter = wgpu::FilterMode::Linear, + .mipmapFilter = wgpu::FilterMode::Linear, + .maxAnisotropy = g_graphicsConfig.textureAnistropy, + }; + auto sampler = g_device.CreateSampler(&samplerDescriptor); + + const std::array uniformBindGroupEntries{ + wgpu::BindGroupEntry{ + .binding = 0, + .buffer = g_uniformBuffer, + .offset = 0, + .size = uniform_size, + }, + wgpu::BindGroupEntry{ + .binding = 1, + .sampler = sampler, + }, + }; + const auto uniformBindGroupDescriptor = wgpu::BindGroupDescriptor{ + .label = "Movie Player Uniform Bind Group", + .layout = uniformLayout, + .entryCount = uniformBindGroupEntries.size(), + .entries = uniformBindGroupEntries.data(), + }; + auto uniformBindGroup = g_device.CreateBindGroup(&uniformBindGroupDescriptor); + + const auto textureBinding = wgpu::TextureBindingLayout{ + .sampleType = wgpu::TextureSampleType::Float, + .viewDimension = wgpu::TextureViewDimension::e2D, + }; + const std::array textureLayoutEntries{ + wgpu::BindGroupLayoutEntry{ + .binding = 0, + .visibility = wgpu::ShaderStage::Fragment, + .texture = textureBinding, + }, + wgpu::BindGroupLayoutEntry{ + .binding = 1, + .visibility = wgpu::ShaderStage::Fragment, + .texture = textureBinding, + }, + wgpu::BindGroupLayoutEntry{ + .binding = 2, + .visibility = wgpu::ShaderStage::Fragment, + .texture = textureBinding, + }, + }; + const auto textureLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{ + .label = "Movie Player Texture Bind Group Layout", + .entryCount = textureLayoutEntries.size(), + .entries = textureLayoutEntries.data(), + }; + auto textureLayout = g_device.CreateBindGroupLayout(&textureLayoutDescriptor); + + const std::array bindGroupLayouts{ + uniformLayout, + textureLayout, + }; + const auto pipelineLayoutDescriptor = wgpu::PipelineLayoutDescriptor{ + .label = "Movie Player Pipeline Layout", + .bindGroupLayoutCount = bindGroupLayouts.size(), + .bindGroupLayouts = bindGroupLayouts.data(), + }; + auto pipelineLayout = g_device.CreatePipelineLayout(&pipelineLayoutDescriptor); + + return { + .shader = shader, + .uniformLayout = uniformLayout, + .uniformBindGroup = uniformBindGroup, + .textureLayout = textureLayout, + .sampler = sampler, + .pipelineLayout = pipelineLayout, + }; +} + +wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) { + const auto attributes = + make_vertex_attributes(std::array{wgpu::VertexFormat::Float32x3, wgpu::VertexFormat::Float32x2}); + const std::array vertexBuffers{make_vertex_buffer_layout(sizeof(Vert), attributes)}; + const auto depthStencil = wgpu::DepthStencilState{ + .format = g_graphicsConfig.depthFormat, + }; + const auto blendComponent = wgpu::BlendComponent{ + .srcFactor = wgpu::BlendFactor::SrcAlpha, + .dstFactor = wgpu::BlendFactor::OneMinusSrcAlpha, + }; + const auto blendState = wgpu::BlendState{ + .color = blendComponent, + .alpha = blendComponent, + }; + const std::array colorTargets{ + wgpu::ColorTargetState{ + .format = g_graphicsConfig.colorFormat, + .blend = &blendState, + .writeMask = wgpu::ColorWriteMask::Red | wgpu::ColorWriteMask::Green | wgpu::ColorWriteMask::Blue, + }, + }; + const auto fragmentState = wgpu::FragmentState{ + .module = state.shader, + .entryPoint = "fs_main", + .targetCount = colorTargets.size(), + .targets = colorTargets.data(), + }; + const auto pipelineDescriptor = wgpu::RenderPipelineDescriptor{ + .label = "Movie Player Pipeline", + .layout = state.pipelineLayout, + .vertex = make_vertex_state(state.shader, vertexBuffers), + .primitive = + wgpu::PrimitiveState{ + .topology = wgpu::PrimitiveTopology::TriangleStrip, + }, + .depthStencil = &depthStencil, + .multisample = + wgpu::MultisampleState{ + .count = g_graphicsConfig.msaaSamples, + }, + .fragment = &fragmentState, + }; + return g_device.CreateRenderPipeline(&pipelineDescriptor); +} + +DrawData make_draw_data(const State& state, const TextureHandle& tex_y, const TextureHandle& tex_u, + const TextureHandle& tex_v, const zeus::CColor& color, float h_pad, float v_pad) { + auto pipeline = pipeline_ref(PipelineConfig{}); + + const std::array verts{ + Vert{{-h_pad, v_pad, 0.f}, {0.0, 0.0}}, + Vert{{-h_pad, -v_pad, 0.f}, {0.0, 1.0}}, + Vert{{h_pad, v_pad, 0.f}, {1.0, 0.0}}, + Vert{{h_pad, -v_pad, 0.f}, {1.0, 1.0}}, + }; + const auto vertRange = push_verts(ArrayRef{verts}); + + const auto uniform = Uniform{ + .xf = zeus::CMatrix4f{}, + .color = color, + }; + const auto uniformRange = push_uniform(uniform); + + std::array entries{ + wgpu::BindGroupEntry{ + .binding = 0, + .textureView = tex_y.ref->view, + }, + wgpu::BindGroupEntry{ + .binding = 1, + .textureView = tex_u.ref->view, + }, + wgpu::BindGroupEntry{ + .binding = 2, + .textureView = tex_v.ref->view, + }, + }; + const auto textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ + .label = "Movie Player Texture Bind Group", + .layout = state.textureLayout, + .entryCount = entries.size(), + .entries = entries.data(), + }); + + return { + .pipeline = pipeline, + .vertRange = vertRange, + .uniformRange = uniformRange, + .textureBindGroup = textureBindGroup, + }; +} + +void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass) { + if (!bind_pipeline(data.pipeline, pass)) { + return; + } + + const std::array offsets{data.uniformRange.first}; + pass.SetBindGroup(0, state.uniformBindGroup, offsets.size(), offsets.data()); + pass.SetBindGroup(1, find_bind_group(data.textureBindGroup)); + pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.first, data.vertRange.second); + pass.Draw(4); +} +} // namespace aurora::gfx::movie_player diff --git a/aurora/lib/gfx/movie_player/shader.hpp b/aurora/lib/gfx/movie_player/shader.hpp index 7577bdf05..2f29203e6 100644 --- a/aurora/lib/gfx/movie_player/shader.hpp +++ b/aurora/lib/gfx/movie_player/shader.hpp @@ -5,13 +5,13 @@ struct DrawData { PipelineRef pipeline; Range vertRange; Range uniformRange; - uint64_t bindGroupId; + BindGroupRef textureBindGroup; }; struct PipelineConfig { // nothing }; -const std::array INITIAL_PIPELINES{ +static const std::array INITIAL_PIPELINES{ PipelineConfig{}, }; @@ -22,8 +22,20 @@ struct State { wgpu::BindGroupLayout textureLayout; wgpu::Sampler sampler; wgpu::PipelineLayout pipelineLayout; - // Transient state - std::unordered_map textureBindGroups; - std::vector frameUsedTextures; }; + +struct Vert { + Vec3 pos; + Vec2 uv; +}; +struct Uniform { + zeus::CMatrix4f xf; + zeus::CColor color; +}; + +State construct_state(); +wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config); +DrawData make_draw_data(const State& state, const TextureHandle& tex_y, const TextureHandle& tex_u, + const TextureHandle& tex_v, const zeus::CColor& color, float h_pad, float v_pad); +void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass); } // namespace aurora::gfx::movie_player diff --git a/aurora/lib/gfx/texture.cpp b/aurora/lib/gfx/texture.cpp index b3f1db5d3..d749e8377 100644 --- a/aurora/lib/gfx/texture.cpp +++ b/aurora/lib/gfx/texture.cpp @@ -1,8 +1,15 @@ #include "common.hpp" +#include "../gpu.hpp" + #include namespace aurora::gfx { +static logvisor::Module Log("aurora::gfx"); + +using gpu::g_device; +using gpu::g_queue; + static wgpu::TextureFormat to_wgpu(TextureFormat format) { switch (format) { case TextureFormat::RGBA8: @@ -77,7 +84,7 @@ TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mi .bytesPerRow = bytesPerRow, .rowsPerImage = heightBlocks, }; - g_Queue.WriteTexture(&dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize); + g_queue.WriteTexture(&dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize); offset += dataSize; } if (offset < data.size()) { @@ -108,7 +115,7 @@ TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t m .dimension = wgpu::TextureViewDimension::e2D, .mipLevelCount = mips, }; - auto texture = g_Device.CreateTexture(&textureDescriptor); + auto texture = g_device.CreateTexture(&textureDescriptor); auto textureView = texture.CreateView(&textureViewDescriptor); return {std::make_shared(std::move(texture), std::move(textureView), size, wgpuFormat)}; } @@ -142,6 +149,6 @@ void write_texture(const TextureHandle& handle, ArrayRef data) noexcept .bytesPerRow = bytesPerRow, .rowsPerImage = heightBlocks, }; - g_Queue.WriteTexture(&dstView, data.data(), dataSize, &dataLayout, &ref.size); + g_queue.WriteTexture(&dstView, data.data(), dataSize, &dataLayout, &ref.size); } } // namespace aurora::gfx diff --git a/aurora/lib/gpu.cpp b/aurora/lib/gpu.cpp new file mode 100644 index 000000000..4abb1ed66 --- /dev/null +++ b/aurora/lib/gpu.cpp @@ -0,0 +1,208 @@ +#include "gpu.hpp" + +#include +#include +#include +#include +#include +#include + +#include "dawn/BackendBinding.hpp" + +// TODO HACK: dawn doesn't expose device toggles +#include "../extern/dawn/src/dawn/native/Toggles.h" +namespace dawn::native { +class DeviceBase { +public: + void SetToggle(Toggle toggle, bool isEnabled); +}; +} // namespace dawn::native + +namespace aurora::gpu { +static logvisor::Module Log("aurora::gpu"); + +wgpu::Device g_device; +wgpu::Queue g_queue; +wgpu::SwapChain g_swapChain; +wgpu::BackendType g_backendType; +GraphicsConfig g_graphicsConfig; +TextureWithSampler g_frameBuffer; +TextureWithSampler g_frameBufferResolved; +TextureWithSampler g_depthBuffer; + +static std::unique_ptr g_Instance; +static dawn::native::Adapter g_Adapter; +static wgpu::AdapterProperties g_AdapterProperties; +static std::unique_ptr g_BackendBinding; + +static TextureWithSampler create_render_texture(bool multisampled) { + const auto size = wgpu::Extent3D{ + .width = g_graphicsConfig.width, + .height = g_graphicsConfig.height, + }; + const auto format = g_graphicsConfig.colorFormat; + uint32_t sampleCount = 1; + if (multisampled) { + sampleCount = g_graphicsConfig.msaaSamples; + } + const auto textureDescriptor = wgpu::TextureDescriptor{ + .label = "Render texture", + .usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, + .size = size, + .format = format, + .sampleCount = sampleCount, + }; + auto texture = g_device.CreateTexture(&textureDescriptor); + + const auto viewDescriptor = wgpu::TextureViewDescriptor{}; + auto view = texture.CreateView(&viewDescriptor); + + const auto samplerDescriptor = wgpu::SamplerDescriptor{ + .label = "Render sampler", + .magFilter = wgpu::FilterMode::Linear, + .minFilter = wgpu::FilterMode::Linear, + .mipmapFilter = wgpu::FilterMode::Linear, + }; + auto sampler = g_device.CreateSampler(&samplerDescriptor); + + return { + .texture = std::move(texture), + .view = std::move(view), + .size = size, + .format = format, + .sampler = std::move(sampler), + }; +} + +static TextureWithSampler create_depth_texture() { + const auto size = wgpu::Extent3D{ + .width = g_graphicsConfig.width, + .height = g_graphicsConfig.height, + }; + const auto format = g_graphicsConfig.depthFormat; + const auto textureDescriptor = wgpu::TextureDescriptor{ + .label = "Depth texture", + .usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding, + .size = size, + .format = format, + .sampleCount = g_graphicsConfig.msaaSamples, + }; + auto texture = g_device.CreateTexture(&textureDescriptor); + + const auto viewDescriptor = wgpu::TextureViewDescriptor{}; + auto view = texture.CreateView(&viewDescriptor); + + const auto samplerDescriptor = wgpu::SamplerDescriptor{ + .label = "Depth sampler", + .magFilter = wgpu::FilterMode::Linear, + .minFilter = wgpu::FilterMode::Linear, + .mipmapFilter = wgpu::FilterMode::Linear, + }; + auto sampler = g_device.CreateSampler(&samplerDescriptor); + + return { + .texture = std::move(texture), + .view = std::move(view), + .size = size, + .format = format, + .sampler = std::move(sampler), + }; +} + +static void error_callback(WGPUErrorType type, char const* message, void* userdata) { + Log.report(logvisor::Error, FMT_STRING("Dawn error {}: {}"), + magic_enum::enum_name(static_cast(type)), message); +} + +void initialize(SDL_Window* window) { + Log.report(logvisor::Info, FMT_STRING("Creating Dawn instance")); + g_Instance = std::make_unique(); + g_Instance->EnableBackendValidation(true); + utils::DiscoverAdapter(g_Instance.get(), window, preferredBackendType); + + { + std::vector adapters = g_Instance->GetAdapters(); + auto adapterIt = std::find_if(adapters.begin(), adapters.end(), [](const dawn::native::Adapter adapter) -> bool { + wgpu::AdapterProperties properties; + adapter.GetProperties(&properties); + return properties.backendType == preferredBackendType; + }); + if (adapterIt == adapters.end()) { + Log.report(logvisor::Fatal, FMT_STRING("Failed to find usable graphics backend")); + } + g_Adapter = *adapterIt; + } + g_Adapter.GetProperties(&g_AdapterProperties); + g_backendType = g_AdapterProperties.backendType; + const auto backendName = magic_enum::enum_name(g_backendType); + Log.report(logvisor::Info, FMT_STRING("Using {} graphics backend"), backendName); + + { + const std::array requiredFeatures{ + wgpu::FeatureName::TextureCompressionBC, + }; + const auto deviceDescriptor = wgpu::DeviceDescriptor{ + .requiredFeaturesCount = requiredFeatures.size(), + .requiredFeatures = requiredFeatures.data(), + }; + g_device = wgpu::Device::Acquire(g_Adapter.CreateDevice(&deviceDescriptor)); + g_device.SetUncapturedErrorCallback(&error_callback, nullptr); + // TODO HACK: dawn doesn't expose device toggles + static_cast(static_cast(g_device.Get())) + ->SetToggle(dawn::native::Toggle::UseUserDefinedLabelsInBackend, true); + } + g_queue = g_device.GetQueue(); + + g_BackendBinding = + std::unique_ptr(utils::CreateBinding(g_backendType, window, g_device.Get())); + if (!g_BackendBinding) { + Log.report(logvisor::Fatal, FMT_STRING("Unsupported backend {}"), backendName); + } + + auto swapChainFormat = static_cast(g_BackendBinding->GetPreferredSwapChainTextureFormat()); + if (swapChainFormat == wgpu::TextureFormat::RGBA8UnormSrgb) { + swapChainFormat = wgpu::TextureFormat::RGBA8Unorm; + } else if (swapChainFormat == wgpu::TextureFormat::BGRA8UnormSrgb) { + swapChainFormat = wgpu::TextureFormat::BGRA8Unorm; + } + Log.report(logvisor::Info, FMT_STRING("Using swapchain swapChainFormat {}"), magic_enum::enum_name(swapChainFormat)); + { + const auto descriptor = wgpu::SwapChainDescriptor{ + .format = swapChainFormat, + .implementation = g_BackendBinding->GetSwapChainImplementation(), + }; + g_swapChain = g_device.CreateSwapChain(nullptr, &descriptor); + } + { + int width = 0; + int height = 0; + SDL_GetWindowSize(window, &width, &height); + g_graphicsConfig = GraphicsConfig{ + .width = static_cast(width), + .height = static_cast(height), + .colorFormat = swapChainFormat, + .depthFormat = wgpu::TextureFormat::Depth32Float, + .msaaSamples = 1, // TODO 4 + .textureAnistropy = 16, + }; + resize_swapchain(width, height); + } +} + +void shutdown() { + wgpuSwapChainRelease(g_swapChain.Release()); + wgpuQueueRelease(g_queue.Release()); + g_BackendBinding.reset(); + wgpuDeviceRelease(g_device.Release()); + g_Instance.reset(); +} + +void resize_swapchain(uint32_t width, uint32_t height) { + g_graphicsConfig.width = width; + g_graphicsConfig.height = height; + g_swapChain.Configure(g_graphicsConfig.colorFormat, wgpu::TextureUsage::RenderAttachment, width, height); + g_frameBuffer = create_render_texture(true); + g_frameBufferResolved = create_render_texture(false); + g_depthBuffer = create_depth_texture(); +} +} // namespace aurora::gpu diff --git a/aurora/lib/gpu.hpp b/aurora/lib/gpu.hpp new file mode 100644 index 000000000..5648ecd84 --- /dev/null +++ b/aurora/lib/gpu.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include + +struct SDL_Window; + +namespace aurora::gpu { +struct GraphicsConfig { + uint32_t width; + uint32_t height; + wgpu::TextureFormat colorFormat; + wgpu::TextureFormat depthFormat; + uint32_t msaaSamples; + uint8_t textureAnistropy; +}; +struct TextureWithSampler { + wgpu::Texture texture; + wgpu::TextureView view; + wgpu::Extent3D size; + wgpu::TextureFormat format; + wgpu::Sampler sampler; +}; + +#ifdef DAWN_ENABLE_BACKEND_VULKAN +static const wgpu::BackendType preferredBackendType = wgpu::BackendType::Vulkan; +#elif DAWN_ENABLE_BACKEND_METAL +static const wgpu::BackendType preferredBackendType = wgpu::BackendType::Metal; +#else +static const wgpu::BackendType preferredBackendType = wgpu::BackendType::OpenGL; +#endif +extern wgpu::Device g_device; +extern wgpu::Queue g_queue; +extern wgpu::SwapChain g_swapChain; +extern wgpu::BackendType g_backendType; +extern GraphicsConfig g_graphicsConfig; +extern TextureWithSampler g_frameBuffer; +extern TextureWithSampler g_frameBufferResolved; +extern TextureWithSampler g_depthBuffer; + +void initialize(SDL_Window* window); +void shutdown(); +void resize_swapchain(uint32_t width, uint32_t height); +} // namespace aurora::gpu + +namespace aurora::gpu::utils { +template +static consteval std::array +make_vertex_attributes(std::array formats) { + std::array attributes{}; + uint64_t offset = 0; + for (uint32_t i = 0; i < N; ++i) { + auto format = formats[i]; + attributes[i] = wgpu::VertexAttribute{ + .format = format, + .offset = offset, + .shaderLocation = i, + }; + switch (format) { + case wgpu::VertexFormat::Uint8x2: + case wgpu::VertexFormat::Sint8x2: + case wgpu::VertexFormat::Unorm8x2: + case wgpu::VertexFormat::Snorm8x2: + offset += 2; + break; + case wgpu::VertexFormat::Uint8x4: + case wgpu::VertexFormat::Sint8x4: + case wgpu::VertexFormat::Unorm8x4: + case wgpu::VertexFormat::Snorm8x4: + case wgpu::VertexFormat::Uint16x2: + case wgpu::VertexFormat::Sint16x2: + case wgpu::VertexFormat::Unorm16x2: + case wgpu::VertexFormat::Snorm16x2: + case wgpu::VertexFormat::Float16x2: + case wgpu::VertexFormat::Float32: + case wgpu::VertexFormat::Uint32: + case wgpu::VertexFormat::Sint32: + offset += 4; + break; + case wgpu::VertexFormat::Uint16x4: + case wgpu::VertexFormat::Sint16x4: + case wgpu::VertexFormat::Unorm16x4: + case wgpu::VertexFormat::Snorm16x4: + case wgpu::VertexFormat::Float16x4: + case wgpu::VertexFormat::Float32x2: + case wgpu::VertexFormat::Uint32x2: + case wgpu::VertexFormat::Sint32x2: + offset += 8; + break; + case wgpu::VertexFormat::Float32x3: + case wgpu::VertexFormat::Uint32x3: + case wgpu::VertexFormat::Sint32x3: + offset += 12; + break; + case wgpu::VertexFormat::Float32x4: + case wgpu::VertexFormat::Uint32x4: + case wgpu::VertexFormat::Sint32x4: + offset += 16; + break; + case wgpu::VertexFormat::Undefined: + break; + } + } + return attributes; +} + +template +static inline wgpu::VertexBufferLayout +make_vertex_buffer_layout(uint64_t arrayStride, const std::array& attributes, + wgpu::VertexStepMode stepMode = wgpu::VertexStepMode::Vertex) { + return { + .arrayStride = arrayStride, + .stepMode = stepMode, + .attributeCount = static_cast(attributes.size()), + .attributes = attributes.data(), + }; +} + +template +static inline wgpu::VertexState make_vertex_state(const wgpu::ShaderModule& module, + const std::array& buffers, + const char* entryPoint = "vs_main") { + return { + .module = module, + .entryPoint = entryPoint, + .bufferCount = static_cast(buffers.size()), + .buffers = buffers.data(), + }; +} +} // namespace aurora::gpu::utils diff --git a/aurora/lib/imgui.cpp b/aurora/lib/imgui.cpp index 2d017f6be..1711028a5 100644 --- a/aurora/lib/imgui.cpp +++ b/aurora/lib/imgui.cpp @@ -1,13 +1,49 @@ -#include +#include "imgui.hpp" +#include "gpu.hpp" + +#include +#include +#include #include -namespace aurora { -extern wgpu::Device g_Device; -extern wgpu::Queue g_Queue; -} // namespace aurora - namespace aurora::imgui { +using gpu::g_device; +using gpu::g_queue; + +void create_context() noexcept { + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + io.IniFilename = nullptr; +} + +void initialize(SDL_Window* window) noexcept { + // this just passes through to ImGui_ImplSDL2_Init (private) + // may need to change in the future + ImGui_ImplSDL2_InitForMetal(window); + ImGui_ImplWGPU_Init(g_device.Get(), 1, static_cast(gpu::g_graphicsConfig.colorFormat)); +} + +void shutdown() noexcept { + ImGui_ImplWGPU_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); +} + +void process_event(const SDL_Event& event) noexcept { ImGui_ImplSDL2_ProcessEvent(&event); } + +void new_frame() noexcept { + ImGui_ImplWGPU_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); +} + +void render(const wgpu::RenderPassEncoder& pass) noexcept { + ImGui::Render(); + ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass.Get()); +} + ImTextureID add_texture(uint32_t width, uint32_t height, ArrayRef data) noexcept { const auto size = wgpu::Extent3D{ .width = width, @@ -26,7 +62,7 @@ ImTextureID add_texture(uint32_t width, uint32_t height, ArrayRef data) .mipLevelCount = 1, .arrayLayerCount = 1, }; - auto texture = g_Device.CreateTexture(&textureDescriptor); + auto texture = g_device.CreateTexture(&textureDescriptor); auto textureView = texture.CreateView(&textureViewDescriptor); { const auto dstView = wgpu::ImageCopyTexture{ @@ -36,7 +72,7 @@ ImTextureID add_texture(uint32_t width, uint32_t height, ArrayRef data) .bytesPerRow = 4 * width, .rowsPerImage = height, }; - g_Queue.WriteTexture(&dstView, data.data(), data.size(), &dataLayout, &size); + g_queue.WriteTexture(&dstView, data.data(), data.size(), &dataLayout, &size); } texture.Release(); // leak some memory! return textureView.Release(); diff --git a/aurora/lib/imgui.hpp b/aurora/lib/imgui.hpp new file mode 100644 index 000000000..d516f408c --- /dev/null +++ b/aurora/lib/imgui.hpp @@ -0,0 +1,18 @@ +#pragma once + +struct SDL_Window; +union SDL_Event; + +namespace wgpu { +class RenderPassEncoder; +} // namespace wgpu + +namespace aurora::imgui { +void create_context() noexcept; +void initialize(SDL_Window* window) noexcept; +void shutdown() noexcept; + +void process_event(const SDL_Event& event) noexcept; +void new_frame() noexcept; +void render(const wgpu::RenderPassEncoder& pass) noexcept; +} // namespace aurora::imgui diff --git a/extern/imgui b/extern/imgui index 5c8f8d031..dca527be1 160000 --- a/extern/imgui +++ b/extern/imgui @@ -1 +1 @@ -Subproject commit 5c8f8d031166765d2f1e2ac2de27df6d3691c05a +Subproject commit dca527be1b77e38047054196fd6a96e8abdae131 diff --git a/imgui/ImGuiEngine.hpp b/imgui/ImGuiEngine.hpp index ac8c99832..c1f714480 100644 --- a/imgui/ImGuiEngine.hpp +++ b/imgui/ImGuiEngine.hpp @@ -23,203 +23,4 @@ struct Icon { uint32_t height; }; Icon GetIcon(); - -enum class KeyCode { - /// The '1' key over the letters. - Key1, - /// The '2' key over the letters. - Key2, - /// The '3' key over the letters. - Key3, - /// The '4' key over the letters. - Key4, - /// The '5' key over the letters. - Key5, - /// The '6' key over the letters. - Key6, - /// The '7' key over the letters. - Key7, - /// The '8' key over the letters. - Key8, - /// The '9' key over the letters. - Key9, - /// The '0' key over the 'O' and 'P' keys. - Key0, - - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - - /// The Escape key, next to F1. - Escape, - - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - F13, - F14, - F15, - F16, - F17, - F18, - F19, - F20, - F21, - F22, - F23, - F24, - - /// Print Screen/SysRq. - Snapshot, - /// Scroll Lock. - Scroll, - /// Pause/Break key, next to Scroll lock. - Pause, - - /// `Insert`, next to Backspace. - Insert, - Home, - Delete, - End, - PageDown, - PageUp, - - Left, - Up, - Right, - Down, - - /// The Backspace key, right over Enter. - // TODO: rename - Back, - /// The Enter key. - Return, - /// The space bar. - Space, - - /// The "Compose" key on Linux. - Compose, - - Caret, - - Numlock, - Numpad0, - Numpad1, - Numpad2, - Numpad3, - Numpad4, - Numpad5, - Numpad6, - Numpad7, - Numpad8, - Numpad9, - NumpadAdd, - NumpadDivide, - NumpadDecimal, - NumpadComma, - NumpadEnter, - NumpadEquals, - NumpadMultiply, - NumpadSubtract, - - AbntC1, - AbntC2, - Apostrophe, - Apps, - Asterisk, - At, - Ax, - Backslash, - Calculator, - Capital, - Colon, - Comma, - Convert, - Equals, - Grave, - Kana, - Kanji, - LAlt, - LBracket, - LControl, - LShift, - LWin, - Mail, - MediaSelect, - MediaStop, - Minus, - Mute, - MyComputer, - // also called "Next" - NavigateForward, - // also called "Prior" - NavigateBackward, - NextTrack, - NoConvert, - OEM102, - Period, - PlayPause, - Plus, - Power, - PrevTrack, - RAlt, - RBracket, - RControl, - RShift, - RWin, - Semicolon, - Slash, - Sleep, - Stop, - Sysrq, - Tab, - Underline, - Unlabeled, - VolumeDown, - VolumeUp, - Wake, - WebBack, - WebFavorites, - WebForward, - WebHome, - WebRefresh, - WebSearch, - WebStop, - Yen, - Copy, - Paste, - Cut, -}; } // namespace metaforce