Util: Centralize dynlib loading

pull/3794/head
Stenzek 2 weeks ago
parent f1f6b0ee79
commit 3ccc0c45f4
No known key found for this signature in database

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
#include <jpeglib.h>
class Error;
#define DYN_LIBJPEG_FUNCTIONS(X) \
X(jpeg_CreateDecompress) \
X(jpeg_CreateCompress) \
X(jpeg_destroy_decompress) \
X(jpeg_destroy_compress) \
X(jpeg_std_error) \
X(jpeg_read_header) \
X(jpeg_start_decompress) \
X(jpeg_read_scanlines) \
X(jpeg_finish_decompress) \
X(jpeg_mem_src) \
X(jpeg_resync_to_restart) \
X(jpeg_set_defaults) \
X(jpeg_set_quality) \
X(jpeg_start_compress) \
X(jpeg_write_scanlines) \
X(jpeg_finish_compress)
struct DynLibJPEG
{
#define ADD_FUNC(F) decltype(&::F) F;
DYN_LIBJPEG_FUNCTIONS(ADD_FUNC)
#undef ADD_FUNC
bool Open(Error* error);
};
extern DynLibJPEG g_dyn_libjpeg;

@ -2,45 +2,110 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "dyn_freetype.h"
#include "dyn_libjpeg.h"
#include "dyn_libpng.h"
#include "dyn_libwebp.h"
#include "dyn_plutosvg.h"
#include "dyn_sdl.h"
#include "dyn_shaderc.h"
#include "dyn_spirv_cross.h"
#include "dyn_sqlite.h"
#include "common/assert.h"
#include "common/dynamic_library.h"
#include "common/error.h"
#include "common/log.h"
#include <utility>
LOG_CHANNEL(DynamicLibrary);
// Ensure it's in .bss
static_assert(std::is_trivially_copyable_v<DynFreeType> && std::is_standard_layout_v<DynFreeType>);
static_assert(std::is_trivially_copyable_v<DynPlutoSvg> && std::is_standard_layout_v<DynPlutoSvg>);
static_assert(std::is_trivially_copyable_v<DynLibPNG> && std::is_standard_layout_v<DynLibPNG>);
static_assert(std::is_trivially_copyable_v<DynLibJPEG> && std::is_standard_layout_v<DynLibJPEG>);
static_assert(std::is_trivially_copyable_v<DynLibWebP> && std::is_standard_layout_v<DynLibWebP>);
static_assert(std::is_trivially_copyable_v<DynSDL> && std::is_standard_layout_v<DynSDL>);
static_assert(std::is_trivially_copyable_v<DynShaderc> && std::is_standard_layout_v<DynShaderc>);
static_assert(std::is_trivially_copyable_v<DynSpirvCross> && std::is_standard_layout_v<DynSpirvCross>);
static_assert(std::is_trivially_copyable_v<DynSqlite> && std::is_standard_layout_v<DynSqlite>);
// Because of course friggin linux is different...
#ifdef _WIN32
static constexpr int FREETYPE_MAJOR_VERSION = -1;
static constexpr int PLUTOSVG_MAJOR_VERSION = -1;
static constexpr int LIBPNG_MAJOR_VERSION = -1;
static constexpr int LIBJPEG_MAJOR_VERSION = -1;
static constexpr int LIBWEBP_MAJOR_VERSION = -1;
static constexpr int LIBSDL_MAJOR_VERSION = -1;
static constexpr int SQLITE_MAJOR_VERSION = -1;
static constexpr int SPIRV_CROSS_MAJOR_VERSION = -1;
#else
static constexpr int FREETYPE_MAJOR_VERSION = 6;
static constexpr int PLUTOSVG_MAJOR_VERSION = 0;
static constexpr int LIBPNG_MAJOR_VERSION = 16;
static constexpr int LIBJPEG_MAJOR_VERSION = 62;
static constexpr int LIBWEBP_MAJOR_VERSION = 7;
static constexpr int LIBSDL_MAJOR_VERSION = 0;
static constexpr int SQLITE_MAJOR_VERSION = 3;
static constexpr int SPIRV_CROSS_MAJOR_VERSION = SPVC_C_API_VERSION_MAJOR;
#endif
namespace {
struct Locals
{
// Dynamic libraries
DynamicLibrary freetype_library;
std::once_flag freetype_init_flag;
DynamicLibrary plutosvg_library;
std::once_flag plutosvg_init_flag;
DynamicLibrary libjpeg_library;
std::once_flag libjpeg_init_flag;
DynamicLibrary libpng_library;
std::once_flag libpng_init_flag;
DynamicLibrary libwebp_library;
std::once_flag libwebp_init_flag;
DynamicLibrary sdl_library;
std::once_flag sdl_init_flag;
DynamicLibrary sqlite_library;
std::once_flag sqlite_init_flag;
DynamicLibrary shaderc_library;
std::once_flag shaderc_init_flag;
shaderc_compiler_t shaderc_compiler;
DynamicLibrary spirv_cross_library;
std::once_flag spirv_cross_init_flag;
#if defined(_DEBUG) || defined(_DEVEL)
~Locals();
#endif
};
} // namespace
DynFreeType g_dyn_freetype;
DynPlutoSvg g_dyn_plutosvg;
DynLibJPEG g_dyn_libjpeg;
DynLibWebP g_dyn_libwebp;
DynLibPNG g_dyn_libpng;
DynSDL g_dyn_sdl;
DynSqlite g_dyn_sqlite;
DynShaderc g_dyn_shaderc;
DynSpirvCross g_dyn_spirv_cross;
static Locals s_locals;
#if defined(_DEBUG) || defined(_DEVEL)
Locals::~Locals()
{
DebugAssert(!s_locals.shaderc_compiler);
}
#endif
static bool LoadDynLib(const char* libname, int major_version, DynamicLibrary& dynlib, std::once_flag& once_flag,
std::span<const DynamicLibrary::SymbolTable> symbols, Error* const error)
std::span<const DynamicLibrary::SymbolTable> symbols, Error* const error,
bool (*extra_loader)(Error* const) = nullptr)
{
std::call_once(once_flag, [&libname, &major_version, &dynlib, &symbols, error]() {
std::call_once(once_flag, [&libname, &major_version, &dynlib, &symbols, extra_loader, error]() {
Error lerror;
DynamicLibrary lib;
if (!lib.Open(DynamicLibrary::GetVersionedFilename(libname, major_version).c_str(), &lerror) ||
@ -51,6 +116,12 @@ static bool LoadDynLib(const char* libname, int major_version, DynamicLibrary& d
return;
}
if (extra_loader && !extra_loader(error))
{
DynamicLibrary::ClearSymbols(symbols.data(), symbols.size());
return;
}
dynlib = std::move(lib);
});
@ -86,3 +157,155 @@ bool DynPlutoSvg::Open(Error* const error)
return LoadDynLib("plutosvg", PLUTOSVG_MAJOR_VERSION, s_locals.plutosvg_library, s_locals.plutosvg_init_flag,
s_plutosvg_symbols, error);
}
static const DynamicLibrary::SymbolTable s_libpng_symbols[] = {
#define RESOLVE_SYMBOL(F) {#F, (void**)&g_dyn_libpng.F},
DYN_LIBPNG_FUNCTIONS(RESOLVE_SYMBOL)
#undef RESOLVE_SYMBOL
};
bool DynLibPNG::Open(Error* const error)
{
if (s_locals.libpng_library.IsOpen()) [[likely]]
return true;
return LoadDynLib("libpng16", LIBPNG_MAJOR_VERSION, s_locals.libpng_library, s_locals.libpng_init_flag,
s_libpng_symbols, error);
}
static const DynamicLibrary::SymbolTable s_libjpeg_symbols[] = {
#define RESOLVE_SYMBOL(F) {#F, (void**)&g_dyn_libjpeg.F},
DYN_LIBJPEG_FUNCTIONS(RESOLVE_SYMBOL)
#undef RESOLVE_SYMBOL
};
bool DynLibJPEG::Open(Error* const error)
{
if (s_locals.libjpeg_library.IsOpen()) [[likely]]
return true;
return LoadDynLib("jpeg", LIBJPEG_MAJOR_VERSION, s_locals.libjpeg_library, s_locals.libjpeg_init_flag,
s_libjpeg_symbols, error);
}
static const DynamicLibrary::SymbolTable s_libwebp_symbols[] = {
#define RESOLVE_SYMBOL(F) {#F, (void**)&g_dyn_libwebp.F},
DYN_LIBWEBP_FUNCTIONS(RESOLVE_SYMBOL)
#undef RESOLVE_SYMBOL
};
bool DynLibWebP::Open(Error* const error)
{
if (s_locals.libwebp_library.IsOpen()) [[likely]]
return true;
return LoadDynLib("webp", LIBWEBP_MAJOR_VERSION, s_locals.libwebp_library, s_locals.libwebp_init_flag,
s_libwebp_symbols, error);
}
static const DynamicLibrary::SymbolTable s_sdl_symbols[] = {
#define RESOLVE_SYMBOL(F) {#F, (void**)&g_dyn_sdl.F},
DYN_SDL_FUNCTIONS(RESOLVE_SYMBOL)
#undef RESOLVE_SYMBOL
};
static const DynamicLibrary::SymbolTable s_sqlite_symbols[] = {
#define RESOLVE_SYMBOL(F) {#F, (void**)&g_dyn_sqlite.F},
DYN_SQLITE_FUNCTIONS(RESOLVE_SYMBOL)
#undef RESOLVE_SYMBOL
};
bool DynSqlite::Open(Error* error)
{
if (s_locals.sqlite_library.IsOpen()) [[likely]]
return true;
return LoadDynLib("sqlite3", SQLITE_MAJOR_VERSION, s_locals.sqlite_library, s_locals.sqlite_init_flag,
s_sqlite_symbols, error);
}
static void SDLLogCallback(void* userdata, int category, SDL_LogPriority priority, const char* message)
{
static constexpr Log::Level priority_map[SDL_LOG_PRIORITY_COUNT] = {
Log::Level::Debug, // SDL_LOG_PRIORITY_INVALID
Log::Level::Trace, // SDL_LOG_PRIORITY_TRACE
Log::Level::Verbose, // SDL_LOG_PRIORITY_VERBOSE
Log::Level::Debug, // SDL_LOG_PRIORITY_DEBUG
Log::Level::Info, // SDL_LOG_PRIORITY_INFO
Log::Level::Warning, // SDL_LOG_PRIORITY_WARN
Log::Level::Error, // SDL_LOG_PRIORITY_ERROR
Log::Level::Error, // SDL_LOG_PRIORITY_CRITICAL
};
GENERIC_LOG(Log::Channel::SDL, priority_map[priority], Log::Color::Default, message);
}
static bool SDLLoadCallback(Error* const error)
{
g_dyn_sdl.SDL_SetLogOutputFunction(SDLLogCallback, nullptr);
#if defined(_DEBUG) || defined(_DEVEL)
g_dyn_sdl.SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
#else
g_dyn_sdl.SDL_SetLogPriorities(SDL_LOG_PRIORITY_INFO);
#endif
return true;
}
bool DynSDL::Open(Error* error)
{
if (s_locals.sdl_library.IsOpen()) [[likely]]
return true;
return LoadDynLib("SDL3", LIBSDL_MAJOR_VERSION, s_locals.sdl_library, s_locals.sdl_init_flag, s_sdl_symbols, error,
SDLLoadCallback);
}
static const DynamicLibrary::SymbolTable s_shaderc_symbols[] = {
#define RESOLVE_SYMBOL(F) {#F, (void**)&g_dyn_shaderc.F},
DYN_SHADERC_FUNCTIONS(RESOLVE_SYMBOL)
#undef RESOLVE_SYMBOL
};
bool DynShaderc::Open(Error* const error)
{
if (s_locals.shaderc_library.IsOpen()) [[likely]]
return true;
return LoadDynLib("shaderc_shared", -1, s_locals.shaderc_library, s_locals.shaderc_init_flag, s_shaderc_symbols,
error, [](Error* const error) {
g_dyn_shaderc.compiler = g_dyn_shaderc.shaderc_compiler_initialize();
if (!g_dyn_shaderc.compiler)
{
ERROR_LOG("shaderc_compiler_initialize() failed");
Error::SetStringView(error, "shaderc_compiler_initialize() failed");
return false;
}
return true;
});
}
void DynShaderc::Close()
{
if (compiler)
shaderc_compiler_release(std::exchange(compiler, nullptr));
}
// clang-format off
static const DynamicLibrary::SymbolTable s_spirv_cross_symbols[] = {
#define RESOLVE_SYMBOL(F) {#F, (void**)&g_dyn_spirv_cross.F},
DYN_SPIRV_CROSS_FUNCTIONS(RESOLVE_SYMBOL)
DYN_SPIRV_CROSS_HLSL_FUNCTIONS(RESOLVE_SYMBOL)
DYN_SPIRV_CROSS_MSL_FUNCTIONS(RESOLVE_SYMBOL)
#undef RESOLVE_SYMBOL
};
// clang-format on
bool DynSpirvCross::Open(Error* const error)
{
if (s_locals.spirv_cross_library.IsOpen()) [[likely]]
return true;
return LoadDynLib("spirv-cross-c-shared", SPIRV_CROSS_MAJOR_VERSION, s_locals.spirv_cross_library,
s_locals.spirv_cross_init_flag, s_spirv_cross_symbols, error);
}

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
#include <webp/decode.h>
#include <webp/encode.h>
class Error;
#define DYN_LIBWEBP_FUNCTIONS(X) \
X(WebPGetInfo) \
X(WebPDecodeRGBAInto) \
X(WebPEncodeRGBA) \
X(WebPFree)
struct DynLibWebP
{
#define ADD_FUNC(F) decltype(&::F) F;
DYN_LIBWEBP_FUNCTIONS(ADD_FUNC)
#undef ADD_FUNC
bool Open(Error* error);
};
extern DynLibWebP g_dyn_libwebp;

@ -33,6 +33,7 @@ struct DynShaderc
shaderc_compiler_t compiler;
bool Open(Error* error);
void Close();
};
extern DynShaderc g_dyn_shaderc;

@ -7,7 +7,7 @@
class Error;
#define SPIRV_CROSS_FUNCTIONS(X) \
#define DYN_SPIRV_CROSS_FUNCTIONS(X) \
X(spvc_context_create) \
X(spvc_context_destroy) \
X(spvc_context_set_error_callback) \
@ -34,24 +34,24 @@ class Error;
X(spvc_resources_get_resource_list_for_type)
#ifdef _WIN32
#define SPIRV_CROSS_HLSL_FUNCTIONS(X) \
#define DYN_SPIRV_CROSS_HLSL_FUNCTIONS(X) \
X(spvc_compiler_hlsl_add_resource_binding) \
X(spvc_compiler_hlsl_add_vertex_attribute_remap)
#else
#define SPIRV_CROSS_HLSL_FUNCTIONS(X)
#define DYN_SPIRV_CROSS_HLSL_FUNCTIONS(X)
#endif
#ifdef __APPLE__
#define SPIRV_CROSS_MSL_FUNCTIONS(X) X(spvc_compiler_msl_add_resource_binding)
#define DYN_SPIRV_CROSS_MSL_FUNCTIONS(X) X(spvc_compiler_msl_add_resource_binding)
#else
#define SPIRV_CROSS_MSL_FUNCTIONS(X)
#define DYN_SPIRV_CROSS_MSL_FUNCTIONS(X)
#endif
struct DynSpirvCross
{
#define ADD_FUNC(F) decltype(&::F) F;
SPIRV_CROSS_FUNCTIONS(ADD_FUNC)
SPIRV_CROSS_HLSL_FUNCTIONS(ADD_FUNC)
SPIRV_CROSS_MSL_FUNCTIONS(ADD_FUNC)
DYN_SPIRV_CROSS_FUNCTIONS(ADD_FUNC)
DYN_SPIRV_CROSS_HLSL_FUNCTIONS(ADD_FUNC)
DYN_SPIRV_CROSS_MSL_FUNCTIONS(ADD_FUNC)
#undef ADD_FUNC
bool Open(Error* error);

@ -11,7 +11,6 @@
#include "shadergen.h"
#include "common/assert.h"
#include "common/dynamic_library.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/hash_combine.h"
@ -57,20 +56,12 @@ struct Locals
std::array<u8, SHA1Digest::DIGEST_SIZE> pipeline_cache_hash;
// Dynamic libraries
DynamicLibrary shaderc_library;
DynamicLibrary spirv_cross_library;
std::once_flag shaderc_init_flag;
std::once_flag spirv_cross_init_flag;
};
} // namespace
ALIGN_TO_CACHE_LINE static Locals s_locals;
static_assert(std::is_trivially_copyable_v<DynShaderc> && std::is_standard_layout_v<DynShaderc>);
static_assert(std::is_trivially_copyable_v<DynSpirvCross> && std::is_standard_layout_v<DynSpirvCross>);
DynShaderc g_dyn_shaderc;
DynSpirvCross g_dyn_spirv_cross;
size_t GPUDevice::s_total_vram_usage = 0;
GPUDevice::Statistics GPUDevice::s_stats = {};
@ -1395,85 +1386,6 @@ std::unique_ptr<GPUDevice> GPUDevice::CreateDeviceForAPI(RenderAPI api)
}
}
bool DynShaderc::Open(Error* error)
{
if (compiler)
return true;
std::call_once(s_locals.shaderc_init_flag, [&error]() {
Error lerror;
DynamicLibrary lib;
if (!lib.Open(DynamicLibrary::GetVersionedFilename("shaderc_shared").c_str(), error))
{
ERROR_LOG("Failed to load shaderc: {}", (error ? error : &lerror)->GetDescription());
Error::AddPrefix(error, "Failed to load shaderc: ");
return;
}
static const DynamicLibrary::SymbolTable shaderc_symbols[] = {
#define SHADERC_SYMBOL(F) {#F, (void**)&g_dyn_shaderc.F},
DYN_SHADERC_FUNCTIONS(SHADERC_SYMBOL)
#undef SHADERC_SYMBOL
};
if (!lib.ResolveSymbols(shaderc_symbols, error))
return;
g_dyn_shaderc.compiler = g_dyn_shaderc.shaderc_compiler_initialize();
if (!g_dyn_shaderc.compiler)
{
ERROR_LOG("shaderc_compiler_initialize() failed");
Error::SetStringView(error, "shaderc_compiler_initialize() failed");
DynamicLibrary::ClearSymbols(shaderc_symbols);
return;
}
s_locals.shaderc_library = std::move(lib);
});
return (compiler != nullptr);
}
bool DynSpirvCross::Open(Error* error)
{
if (s_locals.spirv_cross_library.IsOpen())
return true;
std::call_once(s_locals.spirv_cross_init_flag, [&error]() {
Error lerror;
DynamicLibrary lib;
#if defined(_WIN32) || defined(__ANDROID__)
// SPVC's build on Windows doesn't spit out a versioned DLL.
const std::string libpath = DynamicLibrary::GetVersionedFilename("spirv-cross-c-shared");
#else
const std::string libpath = DynamicLibrary::GetVersionedFilename("spirv-cross-c-shared", SPVC_C_API_VERSION_MAJOR);
#endif
if (!lib.Open(libpath.c_str(), error ? error : &lerror))
{
ERROR_LOG("Failed to load spirv-cross: {}", (error ? error : &lerror)->GetDescription());
Error::AddPrefix(error, "Failed to load spirv-cross: ");
return;
}
// clang-format off
static const DynamicLibrary::SymbolTable spirv_cross_symbols[] = {
#define SPIRV_CROSS_SYMBOL(F) {#F, (void**)&g_dyn_spirv_cross.F},
SPIRV_CROSS_FUNCTIONS(SPIRV_CROSS_SYMBOL)
SPIRV_CROSS_HLSL_FUNCTIONS(SPIRV_CROSS_SYMBOL)
SPIRV_CROSS_MSL_FUNCTIONS(SPIRV_CROSS_SYMBOL)
#undef SPIRV_CROSS_SYMBOL
};
// clang-format on
if (!lib.ResolveSymbols(spirv_cross_symbols, std::size(spirv_cross_symbols), error))
return;
s_locals.spirv_cross_library = std::move(lib);
});
return s_locals.spirv_cross_library.IsOpen();
}
std::optional<DynamicHeapArray<u8>> GPUDevice::OptimizeVulkanSpv(const std::span<const u8> spirv, Error* error)
{
std::optional<DynamicHeapArray<u8>> ret;
@ -2060,13 +1972,7 @@ void GPUDevice::UnloadDynamicLibraries()
{
Assert(!g_gpu_device);
g_dyn_spirv_cross = {};
s_locals.spirv_cross_library.Close();
if (g_dyn_shaderc.compiler)
g_dyn_shaderc.shaderc_compiler_release(g_dyn_shaderc.compiler);
g_dyn_shaderc = {};
s_locals.shaderc_library.Close();
g_dyn_shaderc.Close();
#ifdef ENABLE_VULKAN
VulkanLoader::DestroyVulkanInstance();

@ -2,7 +2,9 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "image.h"
#include "dyn_libjpeg.h"
#include "dyn_libpng.h"
#include "dyn_libwebp.h"
#include "texture_decompress.h"
#include "common/assert.h"
@ -21,11 +23,7 @@
#include <cmath>
#include <dyn_plutosvg.h>
#include <jpeglib.h>
#include <limits>
#include <plutosvg.h>
#include <webp/decode.h>
#include <webp/encode.h>
#include <mutex>
@ -37,178 +35,6 @@
LOG_CHANNEL(Image);
#define DYN_LIBJPEG_FUNCTIONS(X) \
X(jpeg_CreateDecompress) \
X(jpeg_CreateCompress) \
X(jpeg_destroy_decompress) \
X(jpeg_destroy_compress) \
X(jpeg_std_error) \
X(jpeg_read_header) \
X(jpeg_start_decompress) \
X(jpeg_read_scanlines) \
X(jpeg_finish_decompress) \
X(jpeg_mem_src) \
X(jpeg_resync_to_restart) \
X(jpeg_set_defaults) \
X(jpeg_set_quality) \
X(jpeg_start_compress) \
X(jpeg_write_scanlines) \
X(jpeg_finish_compress)
#define DYN_LIBWEBP_FUNCTIONS(X) \
X(WebPGetInfo) \
X(WebPDecodeRGBAInto) \
X(WebPEncodeRGBA) \
X(WebPFree)
namespace {
struct DynLibJPEG
{
DynamicLibrary library;
std::once_flag init_flag;
#define ADD_FUNC(F) decltype(&::F) F;
DYN_LIBJPEG_FUNCTIONS(ADD_FUNC)
#undef ADD_FUNC
bool Open(Error* error);
};
struct DynLibWebP
{
DynamicLibrary library;
std::once_flag init_flag;
#define ADD_FUNC(F) decltype(&::F) F;
DYN_LIBWEBP_FUNCTIONS(ADD_FUNC)
#undef ADD_FUNC
bool Open(Error* error);
};
struct Locals
{
DynamicLibrary png_library;
std::once_flag png_init_flag;
};
} // namespace
static Locals s_locals;
static DynLibJPEG s_dyn_libjpeg;
static DynLibWebP s_dyn_libwebp;
DynLibPNG g_dyn_libpng;
bool DynLibPNG::Open(Error* error)
{
if (s_locals.png_library.IsOpen())
return true;
std::call_once(s_locals.png_init_flag, [&error]() {
Error lerror;
DynamicLibrary lib;
#ifdef _WIN32
static constexpr int lib_major_version = -1;
#else
static constexpr int lib_major_version = 16;
#endif
if (!lib.Open(DynamicLibrary::GetVersionedFilename("libpng16", lib_major_version).c_str(), &lerror))
{
ERROR_LOG("Failed to load libpng: {}", lerror.GetDescription());
Error::SetStringFmt(error, "Failed to load libpng: {}", lerror.GetDescription());
return;
}
// clang-format off
static const DynamicLibrary::SymbolTable png_symbols[] = {
#define PNG_SYMBOL(F) {#F, (void**)&g_dyn_libpng.F},
DYN_LIBPNG_FUNCTIONS(PNG_SYMBOL)
#undef PNG_SYMBOL
};
// clang-format on
if (!lib.ResolveSymbols(png_symbols, std::size(png_symbols), error))
return;
s_locals.png_library = std::move(lib);
});
return s_locals.png_library.IsOpen();
}
bool DynLibJPEG::Open(Error* error)
{
if (library.IsOpen())
return true;
std::call_once(init_flag, [&error]() {
Error lerror;
DynamicLibrary lib;
#ifdef _WIN32
static constexpr int lib_major_version = -1;
#else
static constexpr int lib_major_version = 62;
#endif
if (!lib.Open(DynamicLibrary::GetVersionedFilename("jpeg", lib_major_version).c_str(), &lerror))
{
ERROR_LOG("Failed to load libjpeg: {}", lerror.GetDescription());
Error::SetStringFmt(error, "Failed to load libjpeg: {}", lerror.GetDescription());
return;
}
// clang-format off
static const DynamicLibrary::SymbolTable jpeg_symbols[] = {
#define JPEG_SYMBOL(F) {#F, (void**)&s_dyn_libjpeg.F},
DYN_LIBJPEG_FUNCTIONS(JPEG_SYMBOL)
#undef JPEG_SYMBOL
};
// clang-format on
if (!lib.ResolveSymbols(jpeg_symbols, std::size(jpeg_symbols), error))
return;
s_dyn_libjpeg.library = std::move(lib);
});
return library.IsOpen();
}
bool DynLibWebP::Open(Error* error)
{
if (library.IsOpen())
return true;
std::call_once(init_flag, [&error]() {
Error lerror;
DynamicLibrary lib;
#ifdef _WIN32
static constexpr int lib_major_version = -1;
#else
static constexpr int lib_major_version = 7;
#endif
if (!lib.Open(DynamicLibrary::GetVersionedFilename("webp", lib_major_version).c_str(), &lerror))
{
ERROR_LOG("Failed to load libwebp: {}", lerror.GetDescription());
Error::SetStringFmt(error, "Failed to load libwebp: {}", lerror.GetDescription());
return;
}
// clang-format off
static const DynamicLibrary::SymbolTable webp_symbols[] = {
#define WEBP_SYMBOL(F) {#F, (void**)&s_dyn_libwebp.F},
DYN_LIBWEBP_FUNCTIONS(WEBP_SYMBOL)
#undef WEBP_SYMBOL
};
// clang-format on
if (!lib.ResolveSymbols(webp_symbols, std::size(webp_symbols), error))
return;
s_dyn_libwebp.library = std::move(lib);
});
return library.IsOpen();
}
static bool PNGBufferLoader(Image* image, std::span<const u8> data, Error* error);
static bool PNGBufferSaver(const Image& image, DynamicHeapArray<u8>* data, u8 quality, Error* error);
static bool PNGFileLoader(Image* image, std::string_view filename, std::FILE* fp, Error* error);
@ -1394,7 +1220,7 @@ struct JPEGErrorHandler
JPEGErrorHandler(Error* errptr_)
{
s_dyn_libjpeg.jpeg_std_error(&err);
g_dyn_libjpeg.jpeg_std_error(&err);
err.error_exit = &ErrorExit;
errptr = errptr_;
}
@ -1413,7 +1239,7 @@ struct JPEGErrorHandler
template<typename T>
static bool WrapJPEGDecompress(Image* image, Error* error, T setup_func)
{
if (!s_dyn_libjpeg.Open(error))
if (!g_dyn_libjpeg.Open(error))
return false;
std::vector<u8> scanline;
@ -1424,15 +1250,15 @@ static bool WrapJPEGDecompress(Image* image, Error* error, T setup_func)
JPEGErrorHandler errhandler(error);
if (fastjmp_set(&errhandler.jbuf) != 0)
{
s_dyn_libjpeg.jpeg_destroy_decompress(&info);
g_dyn_libjpeg.jpeg_destroy_decompress(&info);
return false;
}
info.err = &errhandler.err;
s_dyn_libjpeg.jpeg_CreateDecompress(&info, JPEG_LIB_VERSION, sizeof(info));
g_dyn_libjpeg.jpeg_CreateDecompress(&info, JPEG_LIB_VERSION, sizeof(info));
setup_func(info);
const int herr = s_dyn_libjpeg.jpeg_read_header(&info, TRUE);
const int herr = g_dyn_libjpeg.jpeg_read_header(&info, TRUE);
if (herr != JPEG_HEADER_OK)
{
Error::SetStringFmt(error, "jpeg_read_header() returned {}", herr);
@ -1449,7 +1275,7 @@ static bool WrapJPEGDecompress(Image* image, Error* error, T setup_func)
info.out_color_space = JCS_RGB;
info.out_color_components = 3;
if (!s_dyn_libjpeg.jpeg_start_decompress(&info))
if (!g_dyn_libjpeg.jpeg_start_decompress(&info))
{
Error::SetStringFmt(error, "jpeg_start_decompress() returned failure");
return false;
@ -1459,7 +1285,7 @@ static bool WrapJPEGDecompress(Image* image, Error* error, T setup_func)
if (!image->IsValid())
{
Error::SetStringFmt(error, "Image dimensions are too large: {}x{}", info.image_width, info.image_height);
s_dyn_libjpeg.jpeg_destroy_decompress(&info);
g_dyn_libjpeg.jpeg_destroy_decompress(&info);
return false;
}
scanline.resize(static_cast<size_t>(info.image_width) * 3);
@ -1468,7 +1294,7 @@ static bool WrapJPEGDecompress(Image* image, Error* error, T setup_func)
bool result = true;
for (u32 y = 0; y < info.image_height; y++)
{
if (s_dyn_libjpeg.jpeg_read_scanlines(&info, scanline_buffer, 1) != 1)
if (g_dyn_libjpeg.jpeg_read_scanlines(&info, scanline_buffer, 1) != 1)
{
Error::SetStringFmt(error, "jpeg_read_scanlines() failed at row {}", y);
result = false;
@ -1488,15 +1314,15 @@ static bool WrapJPEGDecompress(Image* image, Error* error, T setup_func)
}
}
s_dyn_libjpeg.jpeg_finish_decompress(&info);
s_dyn_libjpeg.jpeg_destroy_decompress(&info);
g_dyn_libjpeg.jpeg_finish_decompress(&info);
g_dyn_libjpeg.jpeg_destroy_decompress(&info);
return result;
}
bool JPEGBufferLoader(Image* image, std::span<const u8> data, Error* error)
{
return WrapJPEGDecompress(image, error, [data](jpeg_decompress_struct& info) {
s_dyn_libjpeg.jpeg_mem_src(&info, static_cast<const unsigned char*>(data.data()),
g_dyn_libjpeg.jpeg_mem_src(&info, static_cast<const unsigned char*>(data.data()),
static_cast<unsigned long>(data.size()));
});
}
@ -1553,7 +1379,7 @@ bool JPEGFileLoader(Image* image, std::string_view filename, std::FILE* fp, Erro
}
}
},
.resync_to_restart = s_dyn_libjpeg.jpeg_resync_to_restart,
.resync_to_restart = g_dyn_libjpeg.jpeg_resync_to_restart,
.term_source = [](j_decompress_ptr cinfo) {},
},
.fp = fp,
@ -1568,7 +1394,7 @@ bool JPEGFileLoader(Image* image, std::string_view filename, std::FILE* fp, Erro
template<typename T>
static bool WrapJPEGCompress(const Image& image, u8 quality, Error* error, T setup_func)
{
if (!s_dyn_libjpeg.Open(error))
if (!g_dyn_libjpeg.Open(error))
return false;
std::vector<u8> scanline;
@ -1579,12 +1405,12 @@ static bool WrapJPEGCompress(const Image& image, u8 quality, Error* error, T set
JPEGErrorHandler errhandler(error);
if (fastjmp_set(&errhandler.jbuf) != 0)
{
s_dyn_libjpeg.jpeg_destroy_compress(&info);
g_dyn_libjpeg.jpeg_destroy_compress(&info);
return false;
}
info.err = &errhandler.err;
s_dyn_libjpeg.jpeg_CreateCompress(&info, JPEG_LIB_VERSION, sizeof(info));
g_dyn_libjpeg.jpeg_CreateCompress(&info, JPEG_LIB_VERSION, sizeof(info));
setup_func(info);
info.image_width = image.GetWidth();
@ -1592,9 +1418,9 @@ static bool WrapJPEGCompress(const Image& image, u8 quality, Error* error, T set
info.in_color_space = JCS_RGB;
info.input_components = 3;
s_dyn_libjpeg.jpeg_set_defaults(&info);
s_dyn_libjpeg.jpeg_set_quality(&info, quality, TRUE);
s_dyn_libjpeg.jpeg_start_compress(&info, TRUE);
g_dyn_libjpeg.jpeg_set_defaults(&info);
g_dyn_libjpeg.jpeg_set_quality(&info, quality, TRUE);
g_dyn_libjpeg.jpeg_start_compress(&info, TRUE);
scanline.resize(static_cast<size_t>(image.GetWidth()) * 3);
u8* scanline_buffer[1] = {scanline.data()};
@ -1614,7 +1440,7 @@ static bool WrapJPEGCompress(const Image& image, u8 quality, Error* error, T set
*(dst_ptr++) = Truncate8(rgba >> 16);
}
if (s_dyn_libjpeg.jpeg_write_scanlines(&info, scanline_buffer, 1) != 1)
if (g_dyn_libjpeg.jpeg_write_scanlines(&info, scanline_buffer, 1) != 1)
{
Error::SetStringFmt(error, "jpeg_write_scanlines() failed at row {}", y);
result = false;
@ -1622,8 +1448,8 @@ static bool WrapJPEGCompress(const Image& image, u8 quality, Error* error, T set
}
}
s_dyn_libjpeg.jpeg_finish_compress(&info);
s_dyn_libjpeg.jpeg_destroy_compress(&info);
g_dyn_libjpeg.jpeg_finish_compress(&info);
g_dyn_libjpeg.jpeg_destroy_compress(&info);
return result;
}
@ -1724,11 +1550,11 @@ bool JPEGFileSaver(const Image& image, std::string_view filename, std::FILE* fp,
bool WebPBufferLoader(Image* image, std::span<const u8> data, Error* error)
{
if (!s_dyn_libwebp.Open(error))
if (!g_dyn_libwebp.Open(error))
return false;
int width, height;
if (!s_dyn_libwebp.WebPGetInfo(data.data(), data.size(), &width, &height) || width <= 0 || height <= 0)
if (!g_dyn_libwebp.WebPGetInfo(data.data(), data.size(), &width, &height) || width <= 0 || height <= 0)
{
Error::SetStringView(error, "WebPGetInfo() failed");
return false;
@ -1741,7 +1567,7 @@ bool WebPBufferLoader(Image* image, std::span<const u8> data, Error* error)
return false;
}
if (!s_dyn_libwebp.WebPDecodeRGBAInto(data.data(), data.size(), image->GetPixels(), image->GetStorageSize(),
if (!g_dyn_libwebp.WebPDecodeRGBAInto(data.data(), data.size(), image->GetPixels(), image->GetStorageSize(),
image->GetPitch()))
{
Error::SetStringView(error, "WebPDecodeRGBAInto() failed");
@ -1754,12 +1580,12 @@ bool WebPBufferLoader(Image* image, std::span<const u8> data, Error* error)
bool WebPBufferSaver(const Image& image, DynamicHeapArray<u8>* data, u8 quality, Error* error)
{
if (!s_dyn_libwebp.Open(error))
if (!g_dyn_libwebp.Open(error))
return false;
u8* encoded_data;
const size_t encoded_size =
s_dyn_libwebp.WebPEncodeRGBA(reinterpret_cast<const u8*>(image.GetPixels()), image.GetWidth(), image.GetHeight(),
g_dyn_libwebp.WebPEncodeRGBA(reinterpret_cast<const u8*>(image.GetPixels()), image.GetWidth(), image.GetHeight(),
image.GetPitch(), static_cast<float>(quality), &encoded_data);
if (encoded_size == 0)
{
@ -1769,7 +1595,7 @@ bool WebPBufferSaver(const Image& image, DynamicHeapArray<u8>* data, u8 quality,
data->resize(encoded_size);
std::memcpy(data->data(), encoded_data, encoded_size);
s_dyn_libwebp.WebPFree(encoded_data);
g_dyn_libwebp.WebPFree(encoded_data);
return true;
}

@ -13,7 +13,6 @@
#include "common/assert.h"
#include "common/bitutils.h"
#include "common/dynamic_library.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/gsvector.h"
@ -134,13 +133,6 @@ static constexpr const std::array<ButtonInfo, SDL_GAMEPAD_BUTTON_COUNT> s_ps_but
}};
// clang-format on
struct Locals
{
// Dynamic libraries
DynamicLibrary sdl_library;
std::once_flag sdl_init_flag;
};
} // namespace
static constexpr std::array<const char*, 4> s_sdl_hat_direction_names = {{
@ -223,9 +215,6 @@ static constexpr const SettingInfo s_sdl_advanced_settings_info[] = {
#endif
};
DynSDL g_dyn_sdl;
static Locals s_sdl_locals;
static bool IsPSControllerType(u8 type)
{
return (type >= SDL_GAMEPAD_TYPE_PS3 && type <= SDL_GAMEPAD_TYPE_PS5);
@ -303,68 +292,6 @@ static const char* GetButtonIcon(u8 type, u32 button)
return str;
}
static void SDLLogCallback(void* userdata, int category, SDL_LogPriority priority, const char* message)
{
static constexpr Log::Level priority_map[SDL_LOG_PRIORITY_COUNT] = {
Log::Level::Debug, // SDL_LOG_PRIORITY_INVALID
Log::Level::Trace, // SDL_LOG_PRIORITY_TRACE
Log::Level::Verbose, // SDL_LOG_PRIORITY_VERBOSE
Log::Level::Debug, // SDL_LOG_PRIORITY_DEBUG
Log::Level::Info, // SDL_LOG_PRIORITY_INFO
Log::Level::Warning, // SDL_LOG_PRIORITY_WARN
Log::Level::Error, // SDL_LOG_PRIORITY_ERROR
Log::Level::Error, // SDL_LOG_PRIORITY_CRITICAL
};
GENERIC_LOG(Log::Channel::SDL, priority_map[priority], Log::Color::Default, message);
}
bool DynSDL::Open(Error* error)
{
// Because of course friggin linux is different...
#ifdef _WIN32
static constexpr int lib_major_version = -1;
#else
static constexpr int lib_major_version = 0;
#endif
if (s_sdl_locals.sdl_library.IsOpen())
return true;
std::call_once(s_sdl_locals.sdl_init_flag, [&error]() {
Error lerror;
DynamicLibrary lib;
if (!lib.Open(DynamicLibrary::GetVersionedFilename("SDL3", lib_major_version).c_str(), &lerror))
{
ERROR_LOG("Failed to load sqlite: {}", lerror.GetDescription());
Error::SetStringFmt(error, "Failed to load sqlite: {}", lerror.GetDescription());
return;
}
// clang-format off
static const DynamicLibrary::SymbolTable sdl_symbols[] = {
#define SDL_SYMBOL(F) {#F, (void**)&g_dyn_sdl.F},
DYN_SDL_FUNCTIONS(SDL_SYMBOL)
#undef SDL_SYMBOL
};
// clang-format on
if (!lib.ResolveSymbols(sdl_symbols, std::size(sdl_symbols), error))
return;
s_sdl_locals.sdl_library = std::move(lib);
g_dyn_sdl.SDL_SetLogOutputFunction(SDLLogCallback, nullptr);
#if defined(_DEBUG) || defined(_DEVEL)
g_dyn_sdl.SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
#else
g_dyn_sdl.SDL_SetLogPriorities(SDL_LOG_PRIORITY_INFO);
#endif
});
return s_sdl_locals.sdl_library.IsOpen();
}
SDLInputSource::SDLInputSource() = default;
SDLInputSource::~SDLInputSource()

@ -3,7 +3,6 @@
#include "sqlite_helpers.h"
#include "common/dynamic_library.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/log.h"
@ -14,59 +13,6 @@
LOG_CHANNEL(Host);
namespace {
struct Locals
{
// Dynamic libraries
DynamicLibrary sqlite_library;
std::once_flag sqlite_init_flag;
};
} // namespace
static Locals s_locals;
static_assert(std::is_trivially_copyable_v<DynSqlite> && std::is_standard_layout_v<DynSqlite>);
DynSqlite g_dyn_sqlite;
bool DynSqlite::Open(Error* error)
{
// Because of course friggin linux is different...
#ifdef _WIN32
static constexpr int lib_major_version = -1;
#else
static constexpr int lib_major_version = 3;
#endif
if (s_locals.sqlite_library.IsOpen())
return true;
std::call_once(s_locals.sqlite_init_flag, [&error]() {
Error lerror;
DynamicLibrary lib;
if (!lib.Open(DynamicLibrary::GetVersionedFilename("sqlite3", lib_major_version).c_str(), &lerror))
{
ERROR_LOG("Failed to load sqlite: {}", lerror.GetDescription());
Error::SetStringFmt(error, "Failed to load sqlite: {}", lerror.GetDescription());
return;
}
// clang-format off
static const DynamicLibrary::SymbolTable sqlite_symbols[] = {
#define SQLITE_SYMBOL(F) {#F, (void**)&g_dyn_sqlite.F},
DYN_SQLITE_FUNCTIONS(SQLITE_SYMBOL)
#undef SQLITE_SYMBOL
};
// clang-format on
if (!lib.ResolveSymbols(sqlite_symbols, std::size(sqlite_symbols), error))
return;
s_locals.sqlite_library = std::move(lib);
});
return s_locals.sqlite_library.IsOpen();
}
sqlite3* SQLiteHelpers::OpenAndCheckDatabase(const char* const path, Error* const error)
{
if (!g_dyn_sqlite.Open(error)) [[unlikely]]

@ -3,6 +3,8 @@
<Import Project="..\..\dep\vsprops\Configurations.props" />
<ItemGroup>
<ClInclude Include="animated_image.h" />
<ClInclude Include="dyn_libjpeg.h" />
<ClInclude Include="dyn_libwebp.h" />
<ClInclude Include="dyn_sdl.h" />
<ClInclude Include="dyn_sqlite.h" />
<ClInclude Include="http_cache.h" />

@ -87,6 +87,8 @@
<ClInclude Include="sqlite_helpers.h" />
<ClInclude Include="zip_helpers.h" />
<ClInclude Include="dyn_sdl.h" />
<ClInclude Include="dyn_libjpeg.h" />
<ClInclude Include="dyn_libwebp.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="state_wrapper.cpp" />

Loading…
Cancel
Save