From e866245f604e5d503bf7d46f5e79fb5b19aa79fc Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 16 Feb 2022 18:13:40 -0500 Subject: [PATCH] aurora: Dawn + SDL united at last --- aurora/CMakeLists.txt | 19 +++- aurora/include/aurora/aurora.hpp | 1 + aurora/lib/aurora.cpp | 173 +++++++++++++++-------------- aurora/lib/dawn/BackendBinding.cpp | 79 +++++++++++++ aurora/lib/dawn/BackendBinding.hpp | 27 +++++ aurora/lib/dawn/MetalBinding.mm | 108 ++++++++++++++++++ aurora/lib/dawn/OpenGLBinding.cpp | 28 +++++ aurora/lib/dawn/VulkanBinding.cpp | 33 ++++++ 8 files changed, 382 insertions(+), 86 deletions(-) create mode 100644 aurora/lib/dawn/BackendBinding.cpp create mode 100644 aurora/lib/dawn/BackendBinding.hpp create mode 100644 aurora/lib/dawn/MetalBinding.mm create mode 100644 aurora/lib/dawn/OpenGLBinding.cpp create mode 100644 aurora/lib/dawn/VulkanBinding.cpp diff --git a/aurora/CMakeLists.txt b/aurora/CMakeLists.txt index a0bf0ab40..4f0c99b72 100644 --- a/aurora/CMakeLists.txt +++ b/aurora/CMakeLists.txt @@ -9,8 +9,23 @@ if (NOT MSVC) target_compile_options(SPIRV-Tools-opt PRIVATE -Wno-implicit-fallthrough) endif () -add_library(aurora STATIC lib/aurora.cpp lib/imgui.cpp lib/gfx/common.cpp) +add_library(aurora STATIC + lib/aurora.cpp + lib/imgui.cpp + lib/dawn/BackendBinding.cpp + lib/gfx/common.cpp + ) target_include_directories(aurora PUBLIC include ../Runtime) target_include_directories(aurora PRIVATE ../imgui ../extern/imgui) target_link_libraries(aurora PRIVATE dawn_native dawncpp webgpu_dawn zeus logvisor SDL2-static) -target_compile_definitions(aurora PRIVATE AURORA_ENABLE_VULKAN AURORA_ENABLE_OPENGL) +if (APPLE) + target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_METAL) + target_sources(aurora PRIVATE lib/dawn/MetalBinding.mm) + set_source_files_properties(lib/dawn/MetalBinding.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) +else () + target_compile_definitions(aurora PRIVATE + DAWN_ENABLE_BACKEND_VULKAN + DAWN_ENABLE_BACKEND_OPENGL + DAWN_ENABLE_BACKEND_DESKTOP_GL) + target_sources(aurora PRIVATE lib/dawn/OpenGLBinding.cpp lib/dawn/VulkanBinding.cpp) +endif () diff --git a/aurora/include/aurora/aurora.hpp b/aurora/include/aurora/aurora.hpp index 73dc01f82..b1d07b678 100644 --- a/aurora/include/aurora/aurora.hpp +++ b/aurora/include/aurora/aurora.hpp @@ -182,6 +182,7 @@ enum class Backend : uint8_t { D3D12, D3D11, OpenGL, + OpenGLES, WebGPU, }; diff --git a/aurora/lib/aurora.cpp b/aurora/lib/aurora.cpp index c4f8bc682..345c04cf5 100644 --- a/aurora/lib/aurora.cpp +++ b/aurora/lib/aurora.cpp @@ -1,40 +1,51 @@ -#include #include -#include -#include + +#include #include -#ifdef AURORA_ENABLE_VULKAN +#include +#include +#include + +#ifdef DAWN_ENABLE_BACKEND_VULKAN #include #include #endif -#ifdef AURORA_ENABLE_OPENGL +#ifdef DAWN_ENABLE_BACKEND_OPENGL #include #include #endif -#include -#include +#ifdef DAWN_ENABLE_BACKEND_METAL +#include +#include +#endif + +#include "dawn/BackendBinding.hpp" namespace aurora { // TODO: Move global state to a class/struct? static logvisor::Module Log("aurora"); + static std::unique_ptr g_AppDelegate; +static std::vector g_Args; + // SDL static SDL_Window* g_Window; // Dawn / WebGPU -#ifdef AURORA_ENABLE_VULKAN -static wgpu::BackendType backendType = wgpu::BackendType::Vulkan; -#elif AURORA_ENABLE_METAL -static wgpu::BackendType backendType = wgpu::BackendType::Metal; +#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 backendType = wgpu::BackendType::OpenGL; +static wgpu::BackendType preferredBackendType = wgpu::BackendType::OpenGL; #endif -static std::vector g_Args; -static wgpu::SwapChain g_SwapChain; -static DawnSwapChainImplementation g_SwapChainImpl; -static wgpu::Queue g_Queue; +static std::unique_ptr g_Instance; +static dawn::native::Adapter g_Adapter; +static wgpu::AdapterProperties g_AdapterProperties; static wgpu::Device g_Device; -static wgpu::TextureFormat g_SwapChainFormat; +static wgpu::Queue g_Queue; +static wgpu::SwapChain g_SwapChain; +static std::unique_ptr g_BackendBinding; static void set_window_icon(Icon icon) noexcept { SDL_Surface* iconSurface = SDL_CreateRGBSurfaceFrom(icon.data.get(), icon.width, icon.height, 32, 4 * icon.width, @@ -182,45 +193,23 @@ void app_run(std::unique_ptr app, Icon icon, int argc, char** argv) g_Args.emplace_back(argv[i]); } - Log.report(logvisor::Info, FMT_STRING("Creating Dawn instance")); - auto instance = std::make_unique(); - instance->DiscoverDefaultAdapters(); - - dawn::native::Adapter backendAdapter; - { - std::vector adapters = 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 == backendType; - }); - if (adapterIt == adapters.end()) { - Log.report(logvisor::Fatal, FMT_STRING("Failed to find usable graphics backend")); - } - backendAdapter = *adapterIt; - } - wgpu::AdapterProperties adapterProperties; - backendAdapter.GetProperties(&adapterProperties); - const auto backendName = magic_enum::enum_name(adapterProperties.backendType); - Log.report(logvisor::Info, FMT_STRING("Using {} graphics backend"), backendName); - if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { Log.report(logvisor::Fatal, FMT_STRING("Error initializing SDL: {}"), SDL_GetError()); } - Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI; - switch (adapterProperties.backendType) { -#ifdef AURORA_ENABLE_VULKAN + Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE; + switch (preferredBackendType) { +#ifdef DAWN_ENABLE_BACKEND_VULKAN case wgpu::BackendType::Vulkan: flags |= SDL_WINDOW_VULKAN; break; #endif -#ifdef AURORA_ENABLE_METAL +#ifdef DAWN_ENABLE_BACKEND_METAL case wgpu::BackendType::Metal: flags |= SDL_WINDOW_METAL; break; #endif -#ifdef AURORA_ENABLE_OPENGL +#ifdef DAWN_ENABLE_BACKEND_OPENGL case wgpu::BackendType::OpenGL: flags |= SDL_WINDOW_OPENGL; break; @@ -234,59 +223,74 @@ void app_run(std::unique_ptr app, Icon icon, int argc, char** argv) } set_window_icon(std::move(icon)); - g_Device = wgpu::Device::Acquire(backendAdapter.CreateDevice()); - switch (adapterProperties.backendType) { -#ifdef AURORA_ENABLE_VULKAN - case wgpu::BackendType::Vulkan: { - VkSurfaceKHR surface = VK_NULL_HANDLE; - if (SDL_Vulkan_CreateSurface(g_Window, dawn::native::vulkan::GetInstance(g_Device.Get()), &surface) != SDL_TRUE) { - Log.report(logvisor::Fatal, FMT_STRING("Failed to create Vulkan surface: {}"), SDL_GetError()); + Log.report(logvisor::Info, FMT_STRING("Creating Dawn instance")); + g_Instance = std::make_unique(); + utils::DiscoverAdapter(g_Instance.get(), g_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_SwapChainImpl = dawn::native::vulkan::CreateNativeSwapChainImpl(g_Device.Get(), surface); - g_SwapChainFormat = - static_cast(dawn::native::vulkan::GetNativeSwapChainPreferredFormat(&g_SwapChainImpl)); - break; + g_Adapter = *adapterIt; } -#endif -#ifdef AURORA_ENABLE_METAL - case wgpu::BackendType::Metal: { - // TODO - g_SwapChainFormat = WGPUTextureFormat_BGRA8Unorm; - break; - } -#endif -#ifdef AURORA_ENABLE_OPENGL - case wgpu::BackendType::OpenGL: { - g_SwapChainImpl = dawn::native::opengl::CreateNativeSwapChainImpl( - g_Device.Get(), [](void* userdata) { SDL_GL_SwapWindow(static_cast(userdata)); }, g_Window); - g_SwapChainFormat = - static_cast(dawn::native::opengl::GetNativeSwapChainPreferredFormat(&g_SwapChainImpl)); - break; - } -#endif - default: + 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); + + g_Device = wgpu::Device::Acquire(g_Adapter.CreateDevice()); + 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 = reinterpret_cast(&g_SwapChainImpl); + descriptor.implementation = g_BackendBinding->GetSwapChainImplementation(); g_SwapChain = g_Device.CreateSwapChain(nullptr, &descriptor); } { - int width, height; - SDL_GetWindowSize(g_Window, &width, &height); - g_SwapChain.Configure(g_SwapChainFormat, wgpu::TextureUsage::RenderAttachment, width, height); + auto size = get_window_size(); + auto format = static_cast(g_BackendBinding->GetPreferredSwapChainTextureFormat()); + g_SwapChain.Configure(format, wgpu::TextureUsage::RenderAttachment, size.width, size.height); } g_AppDelegate->onAppLaunched(); g_AppDelegate->onAppWindowResized(get_window_size()); - while (poll_events()) {} + while (poll_events()) { + auto encoder = g_Device.CreateCommandEncoder(); + { + std::array attachments{wgpu::RenderPassColorAttachment{ + .view = g_SwapChain.GetCurrentTextureView(), + .loadOp = wgpu::LoadOp::Clear, + .storeOp = wgpu::StoreOp::Store, + .clearColor = {0.5f, 0.5f, 0.5f, 1.f}, + }}; + auto renderPassDescriptor = wgpu::RenderPassDescriptor{ + .label = "Render Pass", + .colorAttachmentCount = attachments.size(), + .colorAttachments = attachments.data(), + }; + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.End(); + } + const auto buffer = encoder.Finish(); + g_Queue.Submit(1, &buffer); + g_SwapChain.Present(); + } - g_SwapChain.Release(); - g_Queue.Release(); - g_Device.Release(); + wgpuSwapChainRelease(g_SwapChain.Release()); + wgpuQueueRelease(g_Queue.Release()); + g_BackendBinding.reset(); + wgpuDeviceRelease(g_Device.Release()); + g_Instance.reset(); SDL_DestroyWindow(g_Window); SDL_Quit(); } @@ -299,7 +303,7 @@ WindowSize get_window_size() noexcept { } void set_window_title(zstring_view title) noexcept { SDL_SetWindowTitle(g_Window, title.c_str()); } Backend get_backend() noexcept { - switch (backendType) { + switch (g_AdapterProperties.backendType) { case wgpu::BackendType::WebGPU: return Backend::WebGPU; case wgpu::BackendType::D3D11: @@ -311,13 +315,14 @@ Backend get_backend() noexcept { case wgpu::BackendType::Vulkan: return Backend::Vulkan; case wgpu::BackendType::OpenGL: - case wgpu::BackendType::OpenGLES: return Backend::OpenGL; + case wgpu::BackendType::OpenGLES: + return Backend::OpenGLES; default: return Backend::Invalid; } } -std::string_view get_backend_string() noexcept { return magic_enum::enum_name(get_backend()); } +std::string_view get_backend_string() noexcept { return magic_enum::enum_name(g_AdapterProperties.backendType); } void set_fullscreen(bool fullscreen) noexcept { auto flags = SDL_GetWindowFlags(g_Window); if (fullscreen) { diff --git a/aurora/lib/dawn/BackendBinding.cpp b/aurora/lib/dawn/BackendBinding.cpp new file mode 100644 index 000000000..77fc9bf1a --- /dev/null +++ b/aurora/lib/dawn/BackendBinding.cpp @@ -0,0 +1,79 @@ +#include "BackendBinding.hpp" + +#if defined(DAWN_ENABLE_BACKEND_OPENGL) +#include +#include +#endif + +namespace aurora::utils { + +#if defined(DAWN_ENABLE_BACKEND_D3D12) +BackendBinding* CreateD3D12Binding(SDL_Window* window, WGPUDevice device); +#endif +#if defined(DAWN_ENABLE_BACKEND_METAL) +BackendBinding* CreateMetalBinding(SDL_Window* window, WGPUDevice device); +#endif +#if defined(DAWN_ENABLE_BACKEND_NULL) +BackendBinding* CreateNullBinding(SDL_Window* window, WGPUDevice device); +#endif +#if defined(DAWN_ENABLE_BACKEND_OPENGL) +BackendBinding* CreateOpenGLBinding(SDL_Window* window, WGPUDevice device); +#endif +#if defined(DAWN_ENABLE_BACKEND_VULKAN) +BackendBinding* CreateVulkanBinding(SDL_Window* window, WGPUDevice device); +#endif + +BackendBinding::BackendBinding(SDL_Window* window, WGPUDevice device) : m_window(window), m_device(device) {} + +void DiscoverAdapter(dawn::native::Instance* instance, SDL_Window* window, wgpu::BackendType type) { + if (type == wgpu::BackendType::OpenGL || type == wgpu::BackendType::OpenGLES) { +#if defined(DAWN_ENABLE_BACKEND_OPENGL) + SDL_GL_CreateContext(window); + auto getProc = reinterpret_cast(SDL_GL_GetProcAddress); + if (type == wgpu::BackendType::OpenGL) { + dawn::native::opengl::AdapterDiscoveryOptions adapterOptions; + adapterOptions.getProc = getProc; + instance->DiscoverAdapters(&adapterOptions); + } else { + dawn::native::opengl::AdapterDiscoveryOptionsES adapterOptions; + adapterOptions.getProc = getProc; + instance->DiscoverAdapters(&adapterOptions); + } +#endif + } else { + instance->DiscoverDefaultAdapters(); + } +} + +BackendBinding* CreateBinding(wgpu::BackendType type, SDL_Window* window, WGPUDevice device) { + switch (type) { +#if defined(DAWN_ENABLE_BACKEND_D3D12) + case wgpu::BackendType::D3D12: + return CreateD3D12Binding(window, device); +#endif +#if defined(DAWN_ENABLE_BACKEND_METAL) + case wgpu::BackendType::Metal: + return CreateMetalBinding(window, device); +#endif +#if defined(DAWN_ENABLE_BACKEND_NULL) + case wgpu::BackendType::Null: + return CreateNullBinding(window, device); +#endif +#if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL) + case wgpu::BackendType::OpenGL: + return CreateOpenGLBinding(window, device); +#endif +#if defined(DAWN_ENABLE_BACKEND_OPENGLES) + case wgpu::BackendType::OpenGLES: + return CreateOpenGLBinding(window, device); +#endif +#if defined(DAWN_ENABLE_BACKEND_VULKAN) + case wgpu::BackendType::Vulkan: + return CreateVulkanBinding(window, device); +#endif + default: + return nullptr; + } +} + +} // namespace aurora::utils diff --git a/aurora/lib/dawn/BackendBinding.hpp b/aurora/lib/dawn/BackendBinding.hpp new file mode 100644 index 000000000..84180d1d8 --- /dev/null +++ b/aurora/lib/dawn/BackendBinding.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +struct SDL_Window; + +namespace aurora::utils { + +class BackendBinding { +public: + virtual ~BackendBinding() = default; + + virtual uint64_t GetSwapChainImplementation() = 0; + virtual WGPUTextureFormat GetPreferredSwapChainTextureFormat() = 0; + +protected: + BackendBinding(SDL_Window* window, WGPUDevice device); + + SDL_Window* m_window = nullptr; + WGPUDevice m_device = nullptr; +}; + +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 diff --git a/aurora/lib/dawn/MetalBinding.mm b/aurora/lib/dawn/MetalBinding.mm new file mode 100644 index 000000000..23b9de62a --- /dev/null +++ b/aurora/lib/dawn/MetalBinding.mm @@ -0,0 +1,108 @@ +#include "BackendBinding.hpp" + +#include +#include + +#import + +template DawnSwapChainImplementation CreateSwapChainImplementation(T *swapChain) { + DawnSwapChainImplementation impl = {}; + impl.userData = swapChain; + impl.Init = [](void *userData, void *wsiContext) { + auto *ctx = static_cast(wsiContext); + reinterpret_cast(userData)->Init(ctx); + }; + impl.Destroy = [](void *userData) { delete reinterpret_cast(userData); }; + impl.Configure = [](void *userData, WGPUTextureFormat format, WGPUTextureUsage allowedUsage, uint32_t width, + uint32_t height) { + return static_cast(userData)->Configure(format, allowedUsage, width, height); + }; + impl.GetNextTexture = [](void *userData, DawnSwapChainNextTexture *nextTexture) { + return static_cast(userData)->GetNextTexture(nextTexture); + }; + impl.Present = [](void *userData) { return static_cast(userData)->Present(); }; + return impl; +} + +namespace aurora::utils { +class SwapChainImplMTL { +public: + using WSIContext = DawnWSIContextMetal; + + explicit SwapChainImplMTL(SDL_Window *window) : m_view(SDL_Metal_CreateView(window)) {} + + ~SwapChainImplMTL() { SDL_Metal_DestroyView(m_view); } + + void Init(DawnWSIContextMetal *ctx) { + mMtlDevice = ctx->device; + mCommandQueue = ctx->queue; + } + + DawnSwapChainError Configure(WGPUTextureFormat format, WGPUTextureUsage usage, uint32_t width, uint32_t height) { + if (format != WGPUTextureFormat_BGRA8Unorm) { + return "unsupported format"; + } + assert(width > 0); + assert(height > 0); + + CGSize size = {}; + size.width = width; + size.height = height; + + mLayer = (__bridge CAMetalLayer *)(SDL_Metal_GetLayer(m_view)); + [mLayer setDevice:mMtlDevice]; + [mLayer setPixelFormat:MTLPixelFormatBGRA8Unorm]; + [mLayer setDrawableSize:size]; + + constexpr uint32_t kFramebufferOnlyTextureUsages = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_Present; + bool hasOnlyFramebufferUsages = (usage & (~kFramebufferOnlyTextureUsages)) == 0u; + if (hasOnlyFramebufferUsages) { + [mLayer setFramebufferOnly:YES]; + } + + return DAWN_SWAP_CHAIN_NO_ERROR; + } + + DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture *nextTexture) { + mCurrentDrawable = [mLayer nextDrawable]; + mCurrentTexture = mCurrentDrawable.texture; + nextTexture->texture.ptr = (__bridge void *)(mCurrentTexture); + return DAWN_SWAP_CHAIN_NO_ERROR; + } + + DawnSwapChainError Present() { + id commandBuffer = [mCommandQueue commandBuffer]; + [commandBuffer presentDrawable:mCurrentDrawable]; + [commandBuffer commit]; + return DAWN_SWAP_CHAIN_NO_ERROR; + } + +private: + SDL_MetalView m_view = nil; + id mMtlDevice = nil; + id mCommandQueue = nil; + + CAMetalLayer *mLayer = nullptr; + id mCurrentDrawable = nil; + id mCurrentTexture = nil; +}; + +class MetalBinding : public BackendBinding { +public: + MetalBinding(SDL_Window *window, WGPUDevice device) : BackendBinding(window, device) {} + + uint64_t GetSwapChainImplementation() override { + if (m_swapChainImpl.userData == nullptr) { + m_swapChainImpl = CreateSwapChainImplementation(new SwapChainImplMTL(m_window)); + } + return reinterpret_cast(&m_swapChainImpl); + } + + WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { return WGPUTextureFormat_BGRA8Unorm; } + +private: + DawnSwapChainImplementation m_swapChainImpl{}; +}; + +BackendBinding *CreateMetalBinding(SDL_Window *window, WGPUDevice device) { return new MetalBinding(window, device); } +} // namespace aurora::utils diff --git a/aurora/lib/dawn/OpenGLBinding.cpp b/aurora/lib/dawn/OpenGLBinding.cpp new file mode 100644 index 000000000..e112b6193 --- /dev/null +++ b/aurora/lib/dawn/OpenGLBinding.cpp @@ -0,0 +1,28 @@ +#include "BackendBinding.hpp" + +#include +#include + +namespace aurora::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); + } + return reinterpret_cast(&m_swapChainImpl); + } + + WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { + return dawn::native::opengl::GetNativeSwapChainPreferredFormat(&m_swapChainImpl); + } + +private: + DawnSwapChainImplementation m_swapChainImpl{}; +}; + +BackendBinding* CreateOpenGLBinding(SDL_Window* window, WGPUDevice device) { return new OpenGLBinding(window, device); } +} // namespace aurora::utils diff --git a/aurora/lib/dawn/VulkanBinding.cpp b/aurora/lib/dawn/VulkanBinding.cpp new file mode 100644 index 000000000..c6a56c949 --- /dev/null +++ b/aurora/lib/dawn/VulkanBinding.cpp @@ -0,0 +1,33 @@ +#include "BackendBinding.hpp" + +#include +#include +#include + +namespace aurora::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); + } + return reinterpret_cast(&m_swapChainImpl); + } + + WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { + assert(m_swapChainImpl.userData != nullptr); + return dawn::native::vulkan::GetNativeSwapChainPreferredFormat(&m_swapChainImpl); + } + +private: + DawnSwapChainImplementation m_swapChainImpl{}; +}; + +BackendBinding* CreateVulkanBinding(SDL_Window* window, WGPUDevice device) { return new VulkanBinding(window, device); } +} // namespace aurora::utils