aurora: Null graphics backend with SDL_Renderer fallback

This commit is contained in:
Luke Street 2022-05-31 20:40:46 -04:00
parent 4048492279
commit 40a444d800
10 changed files with 130 additions and 44 deletions

View File

@ -17,7 +17,10 @@ add_library(aurora STATIC
target_compile_definitions(aurora PRIVATE IMGUI_USER_CONFIG="imconfig_user.h") # IMGUI_USE_WCHAR32
target_include_directories(aurora PUBLIC include ../)
target_include_directories(aurora PRIVATE ../imgui ../extern/imgui)
target_link_libraries(aurora PRIVATE dawn_native dawncpp webgpu_dawn zeus logvisor SDL2-static xxhash
if(NOT TARGET SDL2::SDL2-static)
find_package(SDL2 REQUIRED)
endif()
target_link_libraries(aurora PRIVATE dawn_native dawncpp webgpu_dawn zeus logvisor SDL2::SDL2-static xxhash
absl::btree absl::flat_hash_map)
if (DAWN_ENABLE_VULKAN)
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_VULKAN)
@ -36,3 +39,7 @@ if (DAWN_ENABLE_DESKTOP_GL)
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_OPENGL DAWN_ENABLE_BACKEND_DESKTOP_GL)
target_sources(aurora PRIVATE lib/dawn/OpenGLBinding.cpp)
endif ()
if (DAWN_ENABLE_NULL)
target_compile_definitions(aurora PRIVATE DAWN_ENABLE_BACKEND_NULL)
target_sources(aurora PRIVATE lib/dawn/NullBinding.cpp)
endif ()

View File

@ -65,7 +65,9 @@ static bool poll_events() noexcept {
break;
}
case SDL_WINDOWEVENT_EXPOSED:
#if SDL_VERSION_ATLEAST(2, 0, 18)
case SDL_WINDOWEVENT_DISPLAY_CHANGED:
#endif
case SDL_WINDOWEVENT_RESIZED:
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_MINIMIZED:
@ -224,7 +226,7 @@ static bool poll_events() noexcept {
static SDL_Window* create_window(wgpu::BackendType type) {
Uint32 flags = SDL_WINDOW_ALLOW_HIGHDPI;
#if TARGET_OS_IOS || TARGET_OS_TV
#if TARGET_OS_IOS || TARGET_OS_TV || defined(__SWITCH__)
flags |= SDL_WINDOW_FULLSCREEN;
#else
flags |= SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
@ -267,9 +269,12 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv,
#if !defined(_WIN32) && !defined(__APPLE__)
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
#endif
SDL_SetHint(SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE, "1");
#if SDL_VERSION_ATLEAST(2, 0, 18)
SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, "Metaforce");
#endif
#ifdef SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE
SDL_SetHint(SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE, "1");
#endif
SDL_DisableScreenSaver();
/* TODO: Make this an option rather than hard coding it */
@ -292,8 +297,23 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv,
unreachable();
}
set_window_icon(std::move(icon));
SDL_ShowWindow(g_window);
// Initialize SDL_Renderer for ImGui when we can't use a Dawn backend
SDL_Renderer* renderer = nullptr;
if (gpu::g_backendType == wgpu::BackendType::Null) {
const auto flags = SDL_RENDERER_PRESENTVSYNC;
renderer = SDL_CreateRenderer(g_window, -1, flags | SDL_RENDERER_ACCELERATED);
if (renderer == nullptr) {
// Attempt fallback to SW renderer
renderer = SDL_CreateRenderer(g_window, -1, flags);
}
if (renderer == nullptr) {
Log.report(logvisor::Fatal, FMT_STRING("Failed to initialize SDL renderer: {}"), SDL_GetError());
unreachable();
}
}
SDL_ShowWindow(g_window);
gfx::initialize();
imgui::create_context();
@ -301,7 +321,7 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv,
Log.report(logvisor::Info, FMT_STRING("Using framebuffer size {}x{} scale {}"), size.fb_width, size.fb_height,
size.scale);
g_AppDelegate->onImGuiInit(size.scale);
imgui::initialize(g_window);
imgui::initialize(g_window, renderer);
g_AppDelegate->onImGuiAddTextures();
g_AppDelegate->onAppLaunched();
@ -360,6 +380,9 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv,
imgui::shutdown();
gfx::shutdown();
gpu::shutdown();
if (renderer != nullptr) {
SDL_DestroyRenderer(renderer);
}
SDL_DestroyWindow(g_window);
SDL_EnableScreenSaver();
SDL_Quit();

View File

@ -13,6 +13,9 @@
#include <SDL_video.h>
#include <dawn/native/OpenGLBackend.h>
#endif
#if defined(DAWN_ENABLE_BACKEND_NULL)
#include <dawn/native/NullBackend.h>
#endif
namespace aurora::gpu::utils {
@ -69,6 +72,11 @@ bool DiscoverAdapter(dawn::native::Instance* instance, SDL_Window* window, wgpu:
return instance->DiscoverAdapters(&adapterOptions);
}
}
#endif
#if defined(DAWN_ENABLE_BACKEND_NULL)
case wgpu::BackendType::Null:
instance->DiscoverDefaultAdapters();
return true;
#endif
default:
return false;

View File

@ -0,0 +1,27 @@
#include "BackendBinding.hpp"
#include <SDL_video.h>
#include <dawn/native/NullBackend.h>
namespace aurora::gpu::utils {
class NullBinding : public BackendBinding {
public:
NullBinding(SDL_Window* window, WGPUDevice device) : BackendBinding(window, device) {}
uint64_t GetSwapChainImplementation() override {
if (m_swapChainImpl.userData == nullptr) {
m_swapChainImpl = dawn::native::null::CreateNativeSwapChainImpl();
}
return reinterpret_cast<uint64_t>(&m_swapChainImpl);
}
WGPUTextureFormat GetPreferredSwapChainTextureFormat() override {
return WGPUTextureFormat_RGBA8Unorm;
}
private:
DawnSwapChainImplementation m_swapChainImpl{};
};
BackendBinding* CreateNullBinding(SDL_Window* window, WGPUDevice device) { return new NullBinding(window, device); }
} // namespace aurora::gpu::utils

View File

@ -265,7 +265,7 @@ const stream::State& get_state() {
}
template <>
void push_draw_command(stream::DrawData data) {
push_draw_command({.type = ShaderType::Stream, .stream = data});
push_draw_command(ShaderDrawCommand{.type = ShaderType::Stream, .stream = data});
}
template <>
PipelineRef pipeline_ref(stream::PipelineConfig config) {
@ -274,7 +274,7 @@ PipelineRef pipeline_ref(stream::PipelineConfig config) {
template <>
void push_draw_command(model::DrawData data) {
push_draw_command({.type = ShaderType::Model, .model = data});
push_draw_command(ShaderDrawCommand{.type = ShaderType::Model, .model = data});
}
template <>
PipelineRef pipeline_ref(model::PipelineConfig config) {

View File

@ -29,6 +29,8 @@ static inline std::string_view chan_comp(GX::TevColorChan chan) noexcept {
return "b";
case GX::CH_ALPHA:
return "a";
default:
return "?";
}
}
@ -116,26 +118,6 @@ static void color_arg_reg_info(GX::TevColorArg arg, const TevStage& stage, Shade
static bool formatHasAlpha(GX::TextureFormat format) {
switch (format) {
case GX::TF_I4:
case GX::TF_I8:
case GX::TF_RGB565:
case GX::TF_C4:
case GX::TF_C8:
case GX::TF_C14X2:
case GX::TF_Z8:
case GX::TF_Z16:
case GX::TF_Z24X8:
case GX::CTF_R4:
case GX::CTF_R8:
case GX::CTF_G8:
case GX::CTF_B8:
case GX::CTF_RG8:
case GX::CTF_GB8:
case GX::CTF_Z4:
case GX::CTF_Z8M:
case GX::CTF_Z8L:
case GX::CTF_Z16L:
return false;
case GX::TF_IA4:
case GX::TF_IA8:
case GX::TF_RGB5A3:
@ -146,6 +128,8 @@ static bool formatHasAlpha(GX::TextureFormat format) {
case GX::CTF_YUVA8:
case GX::CTF_A8:
return true;
default:
return false;
}
}

View File

@ -47,6 +47,9 @@ constexpr std::array PreferredBackendOrder{
#ifdef DAWN_ENABLE_BACKEND_OPENGLES
wgpu::BackendType::OpenGLES,
#endif
#ifdef DAWN_ENABLE_BACKEND_NULL
wgpu::BackendType::Null,
#endif
};
extern wgpu::Device g_device;

View File

@ -9,6 +9,7 @@
#include <dawn/webgpu_cpp.h>
#include "../extern/imgui/backends/imgui_impl_sdl.cpp"
#include "../extern/imgui/backends/imgui_impl_sdlrenderer.cpp"
#include "../extern/imgui/backends/imgui_impl_wgpu.cpp"
namespace aurora::imgui {
@ -29,17 +30,27 @@ void create_context() noexcept {
io.LogFilename = g_imguiLog.c_str();
}
void initialize(SDL_Window* window) noexcept {
ImGui_ImplSDL2_Init(window, nullptr);
static bool g_useSdlRenderer = false;
void initialize(SDL_Window* window, SDL_Renderer* renderer) noexcept {
ImGui_ImplSDL2_Init(window, renderer);
#ifdef __APPLE__
// Disable MouseCanUseGlobalState for scaling purposes
ImGui_ImplSDL2_GetBackendData()->MouseCanUseGlobalState = false;
#endif
ImGui_ImplWGPU_Init(g_device.Get(), 1, static_cast<WGPUTextureFormat>(gpu::g_graphicsConfig.colorFormat));
g_useSdlRenderer = renderer != nullptr;
if (g_useSdlRenderer) {
ImGui_ImplSDLRenderer_Init(renderer);
} else {
ImGui_ImplWGPU_Init(g_device.Get(), 1, static_cast<WGPUTextureFormat>(gpu::g_graphicsConfig.colorFormat));
}
}
void shutdown() noexcept {
ImGui_ImplWGPU_Shutdown();
if (g_useSdlRenderer) {
ImGui_ImplSDLRenderer_Shutdown();
} else {
ImGui_ImplWGPU_Shutdown();
}
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
}
@ -49,8 +60,7 @@ void process_event(const SDL_Event& event) noexcept {
if (event.type == SDL_MOUSEMOTION) {
auto& io = ImGui::GetIO();
// Scale up mouse coordinates
io.AddMousePosEvent(static_cast<float>(event.motion.x) * g_scale,
static_cast<float>(event.motion.y) * g_scale);
io.AddMousePosEvent(static_cast<float>(event.motion.x) * g_scale, static_cast<float>(event.motion.y) * g_scale);
return;
}
#endif
@ -58,15 +68,19 @@ void process_event(const SDL_Event& event) noexcept {
}
void new_frame(const WindowSize& size) noexcept {
if (g_scale != size.scale) {
if (g_scale > 0.f) {
// TODO wgpu backend bug: doesn't clear bind groups on invalidate
g_resources.ImageBindGroups.Clear();
ImGui_ImplWGPU_CreateDeviceObjects();
if (g_useSdlRenderer) {
ImGui_ImplSDLRenderer_NewFrame();
} else {
if (g_scale != size.scale) {
if (g_scale > 0.f) {
// TODO wgpu backend bug: doesn't clear bind groups on invalidate
g_resources.ImageBindGroups.Clear();
ImGui_ImplWGPU_CreateDeviceObjects();
}
g_scale = size.scale;
}
g_scale = size.scale;
ImGui_ImplWGPU_NewFrame();
}
ImGui_ImplWGPU_NewFrame();
ImGui_ImplSDL2_NewFrame();
// Render at full DPI
@ -80,10 +94,24 @@ void render(const wgpu::RenderPassEncoder& pass) noexcept {
auto* data = ImGui::GetDrawData();
// io.DisplayFramebufferScale is informational; we're rendering at full DPI
data->FramebufferScale = {1.f, 1.f};
ImGui_ImplWGPU_RenderDrawData(data, pass.Get());
if (g_useSdlRenderer) {
SDL_Renderer* renderer = ImGui_ImplSDLRenderer_GetBackendData()->SDLRenderer;
SDL_RenderClear(renderer);
ImGui_ImplSDLRenderer_RenderDrawData(data);
SDL_RenderPresent(renderer);
} else {
ImGui_ImplWGPU_RenderDrawData(data, pass.Get());
}
}
ImTextureID add_texture(uint32_t width, uint32_t height, ArrayRef<uint8_t> data) noexcept {
if (g_useSdlRenderer) {
SDL_Renderer* renderer = ImGui_ImplSDLRenderer_GetBackendData()->SDLRenderer;
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height);
SDL_UpdateTexture(texture, nullptr, data.data(), width * 4);
SDL_SetTextureScaleMode(texture, SDL_ScaleModeLinear);
return texture;
}
const auto size = wgpu::Extent3D{
.width = width,
.height = height,

View File

@ -1,5 +1,6 @@
#pragma once
struct SDL_Renderer;
struct SDL_Window;
union SDL_Event;
@ -13,7 +14,7 @@ struct WindowSize;
namespace aurora::imgui {
void create_context() noexcept;
void initialize(SDL_Window* window) noexcept;
void initialize(SDL_Window* window, SDL_Renderer* renderer) noexcept;
void shutdown() noexcept;
void process_event(const SDL_Event& event) noexcept;

View File

@ -5,6 +5,7 @@
#include "magic_enum.hpp"
#include <SDL_haptic.h>
#include <SDL_version.h>
#include <absl/container/btree_map.h>
#include <absl/container/flat_hash_map.h>
@ -103,7 +104,7 @@ static std::optional<std::string> remap_controller_layout(std::string_view mappi
Log.report(logvisor::Error, FMT_STRING("Controller has unsupported layout: {}"), mapping);
return {};
}
for (const auto [k, v] : entries) {
for (auto [k, v] : entries) {
newMapping.push_back(',');
newMapping.append(k);
newMapping.push_back(':');
@ -140,7 +141,11 @@ Sint32 add_controller(Sint32 which) noexcept {
return -1;
}
controller.m_isGameCube = controller.m_vid == 0x057E && controller.m_pid == 0x0337;
#if SDL_VERSION_ATLEAST(2, 0, 18)
controller.m_hasRumble = (SDL_GameControllerHasRumble(ctrl) != 0u);
#else
controller.m_hasRumble = true;
#endif
Sint32 instance = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ctrl));
g_GameControllers[instance] = controller;
return instance;