mirror of
https://github.com/stenzek/duckstation.git
synced 2024-05-31 18:48:04 -04:00
Compare commits
4 commits
8aad99e3e0
...
2a21418cbe
Author | SHA1 | Date | |
---|---|---|---|
2a21418cbe | |||
be920acf38 | |||
5808e14b7e | |||
03f9708911 |
|
@ -36,6 +36,12 @@ endif()
|
|||
|
||||
if(ENABLE_VULKAN OR APPLE)
|
||||
find_package(Shaderc REQUIRED)
|
||||
|
||||
if(LINUX AND ENABLE_VULKAN)
|
||||
# We need to add the rpath for shaderc to the executable.
|
||||
get_filename_component(SHADERC_LIBRARY_DIRECTORY ${SHADERC_LIBRARY} DIRECTORY)
|
||||
list(APPEND CMAKE_BUILD_RPATH ${SHADERC_LIBRARY_DIRECTORY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
|
|
|
@ -55,6 +55,10 @@ BINARY=duckstation-qt
|
|||
APPDIRNAME=DuckStation.AppDir
|
||||
STRIP=strip
|
||||
|
||||
declare -a MANUAL_LIBS=(
|
||||
"libshaderc_shared.so"
|
||||
)
|
||||
|
||||
declare -a MANUAL_QT_LIBS=(
|
||||
"libQt6WaylandEglClientHwIntegration.so.6"
|
||||
)
|
||||
|
@ -90,6 +94,24 @@ fi
|
|||
OUTDIR=$(realpath "./$APPDIRNAME")
|
||||
rm -fr "$OUTDIR"
|
||||
|
||||
echo "Locating extra libraries..."
|
||||
EXTRA_LIBS_ARGS=""
|
||||
for lib in "${MANUAL_LIBS[@]}"; do
|
||||
srcpath=$(find "$DEPSDIR" -name "$lib")
|
||||
if [ ! -f "$srcpath" ]; then
|
||||
echo "Missinge extra library $lib. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found $lib at $srcpath."
|
||||
|
||||
if [ "$EXTRA_LIBS_ARGS" == "" ]; then
|
||||
EXTRA_LIBS_ARGS="--library=$srcpath"
|
||||
else
|
||||
EXTRA_LIBS_ARGS="$EXTRA_LIBS_ARGS,$srcpath"
|
||||
fi
|
||||
done
|
||||
|
||||
# Why the nastyness? linuxdeploy strips our main binary, and there's no option to turn it off.
|
||||
# It also doesn't strip the Qt libs. We can't strip them after running linuxdeploy, because
|
||||
# patchelf corrupts the libraries (but they still work), but patchelf+strip makes them crash
|
||||
|
@ -112,9 +134,9 @@ EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \
|
|||
DEPLOY_PLATFORM_THEMES="1" \
|
||||
QMAKE="$DEPSDIR/bin/qmake" \
|
||||
NO_STRIP="1" \
|
||||
$LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/duckstation-qt" \
|
||||
$LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/duckstation-qt" $EXTRA_LIBS_ARGS \
|
||||
--desktop-file="$ROOTDIR/scripts/org.duckstation.DuckStation.desktop" \
|
||||
--icon-file="$ROOTDIR/scripts/org.duckstation.DuckStation.png"
|
||||
--icon-file="$ROOTDIR/scripts/org.duckstation.DuckStation.png" \
|
||||
|
||||
echo "Copying resources into AppDir..."
|
||||
cp -a "$BUILDDIR/bin/resources" "$OUTDIR/usr/bin"
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "bios.h"
|
||||
#include "cpu_disasm.h"
|
||||
#include "host.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/md5_digest.h"
|
||||
#include "common/path.h"
|
||||
#include "cpu_disasm.h"
|
||||
#include "host.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
Log_SetChannel(BIOS);
|
||||
|
||||
static constexpr BIOS::Hash MakeHashFromString(const char str[])
|
||||
|
@ -164,13 +168,13 @@ BIOS::Hash BIOS::GetImageHash(const BIOS::Image& image)
|
|||
return hash;
|
||||
}
|
||||
|
||||
std::optional<BIOS::Image> BIOS::LoadImageFromFile(const char* filename)
|
||||
std::optional<BIOS::Image> BIOS::LoadImageFromFile(const char* filename, Error* error)
|
||||
{
|
||||
Image ret(BIOS_SIZE);
|
||||
auto fp = FileSystem::OpenManagedCFile(filename, "rb");
|
||||
auto fp = FileSystem::OpenManagedCFile(filename, "rb", error);
|
||||
if (!fp)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open BIOS image '%s', errno=%d", filename, errno);
|
||||
Error::AddPrefixFmt(error, "Failed to open BIOS '{}': ", Path::GetFileName(filename));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
@ -180,14 +184,15 @@ std::optional<BIOS::Image> BIOS::LoadImageFromFile(const char* filename)
|
|||
|
||||
if (size != BIOS_SIZE && size != BIOS_SIZE_PS2 && size != BIOS_SIZE_PS3)
|
||||
{
|
||||
Log_ErrorPrintf("BIOS image '%s' size mismatch, expecting either %u or %u or %u bytes but got %u bytes", filename,
|
||||
BIOS_SIZE, BIOS_SIZE_PS2, BIOS_SIZE_PS3, size);
|
||||
Error::SetStringFmt(error, "BIOS image '{}' size mismatch, expecting either {} or {} bytes but got {} bytes",
|
||||
Path::GetFileName(filename), static_cast<unsigned>(BIOS_SIZE),
|
||||
static_cast<unsigned>(BIOS_SIZE_PS2), size);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (std::fread(ret.data(), 1, ret.size(), fp.get()) != ret.size())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to read BIOS image '%s'", filename);
|
||||
Error::SetErrno(error, TinyString::from_format("Failed to read BIOS '{}': ", Path::GetFileName(filename)), errno);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
@ -326,7 +331,7 @@ DiscRegion BIOS::GetPSExeDiscRegion(const PSEXEHeader& header)
|
|||
return DiscRegion::Other;
|
||||
}
|
||||
|
||||
std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region)
|
||||
std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region, Error* error)
|
||||
{
|
||||
std::string bios_name;
|
||||
switch (region)
|
||||
|
@ -345,31 +350,36 @@ std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region)
|
|||
break;
|
||||
}
|
||||
|
||||
std::optional<Image> image;
|
||||
|
||||
if (bios_name.empty())
|
||||
{
|
||||
// auto-detect
|
||||
return FindBIOSImageInDirectory(region, EmuFolders::Bios.c_str());
|
||||
image = FindBIOSImageInDirectory(region, EmuFolders::Bios.c_str(), error);
|
||||
}
|
||||
|
||||
// try the configured path
|
||||
std::optional<Image> image = LoadImageFromFile(Path::Combine(EmuFolders::Bios, bios_name).c_str());
|
||||
if (!image.has_value())
|
||||
else
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Error", TRANSLATE("HostInterface", "Failed to load configured BIOS file '%s'"),
|
||||
bios_name.c_str());
|
||||
return std::nullopt;
|
||||
// try the configured path
|
||||
image = LoadImageFromFile(Path::Combine(EmuFolders::Bios, bios_name).c_str(), error);
|
||||
}
|
||||
|
||||
const ImageInfo* ii = GetInfoForImage(image.value());
|
||||
if (!ii || !IsValidBIOSForRegion(region, ii->region))
|
||||
Log_WarningPrintf("BIOS '%s' does not match region. This may cause issues.", bios_name.c_str());
|
||||
// verify region
|
||||
if (image.has_value())
|
||||
{
|
||||
const ImageInfo* ii = GetInfoForImage(image.value());
|
||||
if (!ii || !IsValidBIOSForRegion(region, ii->region))
|
||||
{
|
||||
Log_WarningFmt("BIOS region {} does not match requested region {}. This may cause issues.",
|
||||
Settings::GetConsoleRegionName(region), Settings::GetConsoleRegionName(ii->region));
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion region, const char* directory)
|
||||
std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion region, const char* directory, Error* error)
|
||||
{
|
||||
Log_InfoPrintf("Searching for a %s BIOS in '%s'...", Settings::GetConsoleRegionDisplayName(region), directory);
|
||||
Log_InfoFmt("Searching for a {} BIOS in '{}'...", Settings::GetConsoleRegionName(region), directory);
|
||||
|
||||
FileSystem::FindResultsArray results;
|
||||
FileSystem::FindFiles(
|
||||
|
@ -383,20 +393,21 @@ std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion regi
|
|||
{
|
||||
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
|
||||
{
|
||||
Log_WarningPrintf("Skipping '%s': incorrect size", fd.FileName.c_str());
|
||||
Log_WarningFmt("Skipping '{}': incorrect size", fd.FileName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string full_path(Path::Combine(directory, fd.FileName));
|
||||
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
|
||||
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str(), nullptr);
|
||||
if (!found_image)
|
||||
continue;
|
||||
|
||||
const ImageInfo* ii = GetInfoForImage(found_image.value());
|
||||
if (ii && IsValidBIOSForRegion(region, ii->region))
|
||||
{
|
||||
Log_InfoPrintf("Using BIOS '%s': %s", fd.FileName.c_str(), ii->description);
|
||||
return found_image;
|
||||
Log_InfoFmt("Using BIOS '{}': {}", fd.FileName.c_str(), ii->description);
|
||||
fallback_image = std::move(found_image);
|
||||
return fallback_image;
|
||||
}
|
||||
|
||||
// don't let an unknown bios take precedence over a known one
|
||||
|
@ -410,53 +421,24 @@ std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion regi
|
|||
|
||||
if (!fallback_image.has_value())
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Error", TRANSLATE("HostInterface", "No BIOS image found for %s region"),
|
||||
Settings::GetConsoleRegionDisplayName(region));
|
||||
return std::nullopt;
|
||||
Error::SetStringFmt(error, TRANSLATE_FS("System", "No BIOS image found for {} region."),
|
||||
Settings::GetConsoleRegionName(region));
|
||||
return fallback_image;
|
||||
}
|
||||
|
||||
if (!fallback_info)
|
||||
{
|
||||
Log_WarningPrintf("Using unknown BIOS '%s'. This may crash.", fallback_path.c_str());
|
||||
Log_WarningFmt("Using unknown BIOS '{}'. This may crash.", Path::GetFileName(fallback_path));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_WarningPrintf("Falling back to possibly-incompatible image '%s': %s", fallback_path.c_str(),
|
||||
fallback_info->description);
|
||||
Log_WarningFmt("Falling back to possibly-incompatible image '{}': {}", Path::GetFileName(fallback_path),
|
||||
fallback_info->description);
|
||||
}
|
||||
|
||||
return fallback_image;
|
||||
}
|
||||
|
||||
std::string BIOS::FindBIOSPathWithHash(const char* directory, const Hash& hash)
|
||||
{
|
||||
FileSystem::FindResultsArray files;
|
||||
FileSystem::FindFiles(directory, "*",
|
||||
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &files);
|
||||
|
||||
std::string ret;
|
||||
|
||||
for (FILESYSTEM_FIND_DATA& fd : files)
|
||||
{
|
||||
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
|
||||
continue;
|
||||
|
||||
std::string full_path(Path::Combine(directory, fd.FileName));
|
||||
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
|
||||
if (!found_image)
|
||||
continue;
|
||||
|
||||
const BIOS::Hash found_hash = GetImageHash(found_image.value());
|
||||
if (found_hash == hash)
|
||||
{
|
||||
ret = std::move(full_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImagesInDirectory(const char* directory)
|
||||
{
|
||||
std::vector<std::pair<std::string, const ImageInfo*>> results;
|
||||
|
@ -471,7 +453,7 @@ std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImages
|
|||
continue;
|
||||
|
||||
std::string full_path(Path::Combine(directory, fd.FileName));
|
||||
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
|
||||
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str(), nullptr);
|
||||
if (!found_image)
|
||||
continue;
|
||||
|
||||
|
@ -484,5 +466,5 @@ std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImages
|
|||
|
||||
bool BIOS::HasAnyBIOSImages()
|
||||
{
|
||||
return FindBIOSImageInDirectory(ConsoleRegion::Auto, EmuFolders::Bios.c_str()).has_value();
|
||||
return FindBIOSImageInDirectory(ConsoleRegion::Auto, EmuFolders::Bios.c_str(), nullptr).has_value();
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>.
|
||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>.
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class Error;
|
||||
|
||||
namespace BIOS {
|
||||
enum : u32
|
||||
{
|
||||
|
@ -58,7 +62,7 @@ struct PSEXEHeader
|
|||
static_assert(sizeof(PSEXEHeader) == 0x800);
|
||||
#pragma pack(pop)
|
||||
|
||||
std::optional<Image> LoadImageFromFile(const char* filename);
|
||||
std::optional<Image> LoadImageFromFile(const char* filename, Error* error);
|
||||
Hash GetImageHash(const Image& image);
|
||||
|
||||
const ImageInfo* GetInfoForImage(const Image& image);
|
||||
|
@ -74,14 +78,11 @@ bool IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size);
|
|||
DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header);
|
||||
|
||||
/// Loads the BIOS image for the specified region.
|
||||
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
|
||||
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region, Error* error);
|
||||
|
||||
/// Searches for a BIOS image for the specified region in the specified directory. If no match is found, the first
|
||||
/// BIOS image within 512KB and 4MB will be used.
|
||||
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory);
|
||||
|
||||
/// Returns a BIOS image which matches the specified hash.
|
||||
std::string FindBIOSPathWithHash(const char* directory, const BIOS::Hash& hash);
|
||||
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory, Error* error);
|
||||
|
||||
/// Returns a list of filenames and descriptions for BIOS images in a directory.
|
||||
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> FindBIOSImagesInDirectory(const char* directory);
|
||||
|
|
|
@ -356,7 +356,6 @@ const char* GameDatabase::GetTraitName(Trait trait)
|
|||
const char* GameDatabase::GetTraitDisplayName(Trait trait)
|
||||
{
|
||||
return Host::TranslateToCString("GameDatabase", s_trait_display_names[static_cast<size_t>(trait)]);
|
||||
"";
|
||||
}
|
||||
|
||||
const char* GameDatabase::GetCompatibilityRatingName(CompatibilityRating rating)
|
||||
|
|
|
@ -268,7 +268,7 @@ void Host::ReportFormattedDebuggerMessage(const char* format, ...)
|
|||
ReportDebuggerMessage(message);
|
||||
}
|
||||
|
||||
bool Host::CreateGPUDevice(RenderAPI api)
|
||||
bool Host::CreateGPUDevice(RenderAPI api, Error* error)
|
||||
{
|
||||
DebugAssert(!g_gpu_device);
|
||||
|
||||
|
@ -292,29 +292,32 @@ bool Host::CreateGPUDevice(RenderAPI api)
|
|||
if (g_settings.gpu_disable_texture_copy_to_self)
|
||||
disabled_features |= GPUDevice::FEATURE_MASK_TEXTURE_COPY_TO_SELF;
|
||||
|
||||
Error error;
|
||||
Error create_error;
|
||||
if (!g_gpu_device || !g_gpu_device->Create(
|
||||
g_settings.gpu_adapter,
|
||||
g_settings.gpu_disable_shader_cache ? std::string_view() : std::string_view(EmuFolders::Cache),
|
||||
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, System::IsVSyncEffectivelyEnabled(),
|
||||
g_settings.gpu_threaded_presentation, exclusive_fullscreen_control,
|
||||
static_cast<GPUDevice::FeatureMask>(disabled_features), &error))
|
||||
static_cast<GPUDevice::FeatureMask>(disabled_features), &create_error))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create GPU device.");
|
||||
Log_ErrorFmt("Failed to create GPU device: {}", create_error.GetDescription());
|
||||
if (g_gpu_device)
|
||||
g_gpu_device->Destroy();
|
||||
g_gpu_device.reset();
|
||||
|
||||
Host::ReportErrorAsync(
|
||||
"Error", fmt::format("Failed to create render device:\n\n{}\n\nThis may be due to your GPU not supporting the "
|
||||
"chosen renderer ({}), or because your graphics drivers need to be updated.",
|
||||
error.GetDescription(), GPUDevice::RenderAPIToString(api)));
|
||||
Error::SetStringFmt(
|
||||
error,
|
||||
TRANSLATE_FS("System", "Failed to create render device:\n\n{0}\n\nThis may be due to your GPU not supporting the "
|
||||
"chosen renderer ({1}), or because your graphics drivers need to be updated."),
|
||||
create_error.GetDescription(), GPUDevice::RenderAPIToString(api));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ImGuiManager::Initialize(g_settings.display_osd_scale / 100.0f, g_settings.display_show_osd_messages, &error))
|
||||
if (!ImGuiManager::Initialize(g_settings.display_osd_scale / 100.0f, g_settings.display_show_osd_messages,
|
||||
&create_error))
|
||||
{
|
||||
Host::ReportErrorAsync("Error", fmt::format("Failed to initialize ImGuiManager: {}", error.GetDescription()));
|
||||
Log_ErrorFmt("Failed to initialize ImGuiManager: {}", create_error.GetDescription());
|
||||
Error::SetStringFmt(error, "Failed to initialize ImGuiManager: {}", create_error.GetDescription());
|
||||
g_gpu_device->Destroy();
|
||||
g_gpu_device.reset();
|
||||
return false;
|
||||
|
@ -379,4 +382,3 @@ void Host::ReleaseGPUDevice()
|
|||
g_gpu_device->Destroy();
|
||||
g_gpu_device.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class Error;
|
||||
class SettingsInterface;
|
||||
struct WindowInfo;
|
||||
enum class AudioBackend : u8;
|
||||
|
@ -91,7 +92,7 @@ void DisplayLoadingScreen(const char* message, int progress_min = -1, int progre
|
|||
void RunOnCPUThread(std::function<void()> function, bool block = false);
|
||||
|
||||
/// Attempts to create the rendering device backend.
|
||||
bool CreateGPUDevice(RenderAPI api);
|
||||
bool CreateGPUDevice(RenderAPI api, Error* error);
|
||||
|
||||
/// Handles fullscreen transitions and such.
|
||||
void UpdateDisplayWindow();
|
||||
|
|
|
@ -102,13 +102,13 @@ static std::string GetExecutableNameForImage(IsoReader& iso, bool strip_subdirec
|
|||
static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
|
||||
std::vector<u8>* out_executable_data);
|
||||
|
||||
static bool LoadBIOS(const std::string& override_bios_path);
|
||||
static bool LoadBIOS(const std::string& override_bios_path, Error* error);
|
||||
static void InternalReset();
|
||||
static void ClearRunningGame();
|
||||
static void DestroySystem();
|
||||
static std::string GetMediaPathFromSaveState(const char* path);
|
||||
static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state);
|
||||
static bool CreateGPU(GPURenderer renderer, bool is_switching);
|
||||
static bool CreateGPU(GPURenderer renderer, bool is_switching, Error* error);
|
||||
static bool SaveUndoLoadState();
|
||||
static void WarnAboutUnsafeSettings();
|
||||
static void LogUnsafeSettingsToConsole(const SmallStringBase& messages);
|
||||
|
@ -126,7 +126,7 @@ static void DoRewind();
|
|||
static void SaveRunaheadState();
|
||||
static bool DoRunahead();
|
||||
|
||||
static bool Initialize(bool force_software_renderer);
|
||||
static bool Initialize(bool force_software_renderer, Error* error);
|
||||
static bool FastForwardToFirstFrame();
|
||||
|
||||
static bool UpdateGameSettingsLayer();
|
||||
|
@ -938,10 +938,11 @@ bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_device, bool
|
|||
Host::ReleaseGPUDevice();
|
||||
}
|
||||
|
||||
if (!CreateGPU(renderer, true))
|
||||
Error error;
|
||||
if (!CreateGPU(renderer, true, &error))
|
||||
{
|
||||
if (!IsStartupCancelled())
|
||||
Host::ReportErrorAsync("Error", "Failed to recreate GPU.");
|
||||
Host::ReportErrorAsync("Error", error.GetDescription());
|
||||
|
||||
DestroySystem();
|
||||
return false;
|
||||
|
@ -1464,7 +1465,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
}
|
||||
|
||||
// Load BIOS image.
|
||||
if (!LoadBIOS(parameters.override_bios))
|
||||
if (!LoadBIOS(parameters.override_bios, error))
|
||||
{
|
||||
s_state = State::Shutdown;
|
||||
ClearRunningGame();
|
||||
|
@ -1474,7 +1475,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
}
|
||||
|
||||
// Component setup.
|
||||
if (!Initialize(parameters.force_software_renderer))
|
||||
if (!Initialize(parameters.force_software_renderer, error))
|
||||
{
|
||||
s_state = State::Shutdown;
|
||||
ClearRunningGame();
|
||||
|
@ -1579,7 +1580,7 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool System::Initialize(bool force_software_renderer)
|
||||
bool System::Initialize(bool force_software_renderer, Error* error)
|
||||
{
|
||||
g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
|
||||
s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
|
||||
|
@ -1636,7 +1637,7 @@ bool System::Initialize(bool force_software_renderer)
|
|||
|
||||
CPU::CodeCache::Initialize();
|
||||
|
||||
if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false))
|
||||
if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false, error))
|
||||
{
|
||||
Bus::Shutdown();
|
||||
CPU::Shutdown();
|
||||
|
@ -2076,7 +2077,7 @@ void System::RecreateSystem()
|
|||
PauseSystem(true);
|
||||
}
|
||||
|
||||
bool System::CreateGPU(GPURenderer renderer, bool is_switching)
|
||||
bool System::CreateGPU(GPURenderer renderer, bool is_switching, Error* error)
|
||||
{
|
||||
const RenderAPI api = Settings::GetRenderAPIForRenderer(renderer);
|
||||
|
||||
|
@ -2085,13 +2086,13 @@ bool System::CreateGPU(GPURenderer renderer, bool is_switching)
|
|||
{
|
||||
if (g_gpu_device)
|
||||
{
|
||||
Log_WarningPrintf("Recreating GPU device, expecting %s got %s", GPUDevice::RenderAPIToString(api),
|
||||
GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
|
||||
Log_WarningFmt("Recreating GPU device, expecting {} got {}", GPUDevice::RenderAPIToString(api),
|
||||
GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
|
||||
PostProcessing::Shutdown();
|
||||
}
|
||||
|
||||
Host::ReleaseGPUDevice();
|
||||
if (!Host::CreateGPUDevice(api))
|
||||
if (!Host::CreateGPUDevice(api, error))
|
||||
{
|
||||
Host::ReleaseRenderWindow();
|
||||
return false;
|
||||
|
@ -2108,8 +2109,8 @@ bool System::CreateGPU(GPURenderer renderer, bool is_switching)
|
|||
|
||||
if (!g_gpu)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to initialize %s renderer, falling back to software renderer",
|
||||
Settings::GetRendererName(renderer));
|
||||
Log_ErrorFmt("Failed to initialize {} renderer, falling back to software renderer",
|
||||
Settings::GetRendererName(renderer));
|
||||
Host::AddFormattedOSDMessage(
|
||||
30.0f, TRANSLATE("OSDMessage", "Failed to initialize %s renderer, falling back to software renderer."),
|
||||
Settings::GetRendererName(renderer));
|
||||
|
@ -2117,7 +2118,7 @@ bool System::CreateGPU(GPURenderer renderer, bool is_switching)
|
|||
g_gpu = GPU::CreateSoftwareRenderer();
|
||||
if (!g_gpu)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create fallback software renderer.");
|
||||
Log_ErrorPrint("Failed to create fallback software renderer.");
|
||||
if (!s_keep_gpu_device_on_shutdown)
|
||||
{
|
||||
PostProcessing::Shutdown();
|
||||
|
@ -2250,20 +2251,29 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
|
|||
return !sw.HasError();
|
||||
}
|
||||
|
||||
bool System::LoadBIOS(const std::string& override_bios_path)
|
||||
bool System::LoadBIOS(const std::string& override_bios_path, Error* error)
|
||||
{
|
||||
std::optional<BIOS::Image> bios_image(
|
||||
override_bios_path.empty() ? BIOS::GetBIOSImage(s_region) : FileSystem::ReadBinaryFile(override_bios_path.c_str()));
|
||||
if (!bios_image.has_value())
|
||||
std::optional<BIOS::Image> bios_image;
|
||||
if (!override_bios_path.empty())
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Error", TRANSLATE("System", "Failed to load %s BIOS."),
|
||||
Settings::GetConsoleRegionName(s_region));
|
||||
return false;
|
||||
bios_image = FileSystem::ReadBinaryFile(override_bios_path.c_str(), error);
|
||||
if (!bios_image.has_value())
|
||||
{
|
||||
Error::AddPrefixFmt(error, TRANSLATE_FS("System", "Failed to load {} BIOS."),
|
||||
Settings::GetConsoleRegionName(s_region));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bios_image = BIOS::GetBIOSImage(s_region, error);
|
||||
if (!bios_image.has_value())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bios_image->size() != static_cast<u32>(Bus::BIOS_SIZE))
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("Error", TRANSLATE("System", "Incorrect BIOS image size"));
|
||||
Error::SetStringView(error, TRANSLATE_SV("System", "Incorrect BIOS image size"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -718,8 +718,11 @@ void EmuThread::startFullscreenUI()
|
|||
setInitialState(s_start_fullscreen_ui_fullscreen ? std::optional<bool>(true) : std::optional<bool>());
|
||||
m_run_fullscreen_ui = true;
|
||||
|
||||
if (!Host::CreateGPUDevice(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)) || !FullscreenUI::Initialize())
|
||||
Error error;
|
||||
if (!Host::CreateGPUDevice(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer), &error) ||
|
||||
!FullscreenUI::Initialize())
|
||||
{
|
||||
Host::ReportErrorAsync("Error", error.GetDescription());
|
||||
Host::ReleaseGPUDevice();
|
||||
Host::ReleaseRenderWindow();
|
||||
m_run_fullscreen_ui = false;
|
||||
|
|
|
@ -169,7 +169,8 @@ if(ENABLE_VULKAN)
|
|||
endif()
|
||||
|
||||
if(ENABLE_VULKAN OR APPLE)
|
||||
target_link_libraries(util PUBLIC Shaderc::shaderc_shared)
|
||||
# shaderc is loaded dynamically to reduce module loads on startup.
|
||||
target_include_directories(util PUBLIC ${SHADERC_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "gpu_device.h"
|
||||
|
@ -8,6 +8,7 @@
|
|||
#include "shadergen.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/dynamic_library.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
|
@ -36,6 +37,10 @@ Log_SetChannel(GPUDevice);
|
|||
#include "vulkan_device.h"
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_VULKAN) || defined(__APPLE__)
|
||||
#include "shaderc/shaderc.h"
|
||||
#endif
|
||||
|
||||
std::unique_ptr<GPUDevice> g_gpu_device;
|
||||
|
||||
static std::string s_pipeline_cache_path;
|
||||
|
@ -1105,3 +1110,166 @@ std::unique_ptr<GPUDevice> GPUDevice::CreateDeviceForAPI(RenderAPI api)
|
|||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ENABLE_VULKAN) || defined(__APPLE__)
|
||||
#define SHADERC_INIT_FUNCTIONS(X) \
|
||||
X(shaderc_compiler_initialize) \
|
||||
X(shaderc_compiler_release)
|
||||
|
||||
#define SHADERC_FUNCTIONS(X) \
|
||||
X(shaderc_compile_options_initialize) \
|
||||
X(shaderc_compile_options_release) \
|
||||
X(shaderc_compile_options_set_source_language) \
|
||||
X(shaderc_compile_options_set_generate_debug_info) \
|
||||
X(shaderc_compile_options_set_emit_non_semantic_debug_info) \
|
||||
X(shaderc_compile_options_set_optimization_level) \
|
||||
X(shaderc_compile_options_set_target_env) \
|
||||
X(shaderc_compile_into_spv) \
|
||||
X(shaderc_result_release) \
|
||||
X(shaderc_result_get_length) \
|
||||
X(shaderc_result_get_num_warnings) \
|
||||
X(shaderc_result_get_compilation_status) \
|
||||
X(shaderc_result_get_bytes) \
|
||||
X(shaderc_result_get_error_message)
|
||||
|
||||
// TODO: NOT thread safe, yet.
|
||||
namespace dyn_shaderc {
|
||||
static bool Open();
|
||||
|
||||
static DynamicLibrary s_library;
|
||||
static std::unique_ptr<struct shaderc_compiler, void (*)(shaderc_compiler_t)> s_compiler(nullptr, nullptr);
|
||||
|
||||
#define ADD_FUNC(F) static decltype(&::F) F;
|
||||
SHADERC_FUNCTIONS(ADD_FUNC)
|
||||
#undef ADD_FUNC
|
||||
|
||||
} // namespace dyn_shaderc
|
||||
|
||||
bool dyn_shaderc::Open()
|
||||
{
|
||||
if (s_library.IsOpen())
|
||||
return true;
|
||||
|
||||
Error error;
|
||||
if (!s_library.Open(DynamicLibrary::GetVersionedFilename("shaderc_shared").c_str(), &error))
|
||||
{
|
||||
Log_ErrorFmt("Failed to load shaderc: {}", error.GetDescription());
|
||||
return false;
|
||||
}
|
||||
|
||||
#define LOAD_FUNC(F) \
|
||||
if (!s_library.GetSymbol(#F, &F)) \
|
||||
{ \
|
||||
Log_ErrorFmt("Failed to find function {}", #F); \
|
||||
s_library.Close(); \
|
||||
return false; \
|
||||
}
|
||||
#define LOAD_INIT_FUNC(F) \
|
||||
decltype(&::F) p##F; \
|
||||
if (!s_library.GetSymbol(#F, &p##F)) \
|
||||
{ \
|
||||
Log_ErrorFmt("Failed to find function {}", #F); \
|
||||
s_library.Close(); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
SHADERC_FUNCTIONS(LOAD_FUNC)
|
||||
SHADERC_INIT_FUNCTIONS(LOAD_INIT_FUNC)
|
||||
#undef LOAD_FUNC
|
||||
#undef LOAD_INIT_FUNC
|
||||
|
||||
s_compiler = decltype(s_compiler)(pshaderc_compiler_initialize(), pshaderc_compiler_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef SHADERC_FUNCTIONS
|
||||
#undef SHADERC_INIT_FUNCTIONS
|
||||
|
||||
bool GPUDevice::CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, std::string_view source, const char* entry_point,
|
||||
bool nonsemantic_debug_info, DynamicHeapArray<u8>* out_binary)
|
||||
{
|
||||
static constexpr const std::array<shaderc_shader_kind, static_cast<size_t>(GPUShaderStage::MaxCount)> stage_kinds = {{
|
||||
shaderc_glsl_vertex_shader,
|
||||
shaderc_glsl_fragment_shader,
|
||||
shaderc_glsl_geometry_shader,
|
||||
shaderc_glsl_compute_shader,
|
||||
}};
|
||||
|
||||
// TODO: Move to library.
|
||||
static constexpr const std::pair<shaderc_compilation_status, const char*> status_names[] = {
|
||||
{shaderc_compilation_status_success, "shaderc_compilation_status_success"},
|
||||
{shaderc_compilation_status_invalid_stage, "shaderc_compilation_status_invalid_stage"},
|
||||
{shaderc_compilation_status_compilation_error, "shaderc_compilation_status_compilation_error"},
|
||||
{shaderc_compilation_status_internal_error, "shaderc_compilation_status_internal_error"},
|
||||
{shaderc_compilation_status_null_result_object, "shaderc_compilation_status_null_result_object"},
|
||||
{shaderc_compilation_status_invalid_assembly, "shaderc_compilation_status_invalid_assembly"},
|
||||
{shaderc_compilation_status_validation_error, "shaderc_compilation_status_validation_error"},
|
||||
{shaderc_compilation_status_transformation_error, "shaderc_compilation_status_transformation_error"},
|
||||
{shaderc_compilation_status_configuration_error, "shaderc_compilation_status_configuration_error"},
|
||||
};
|
||||
|
||||
if (!dyn_shaderc::Open())
|
||||
return false;
|
||||
|
||||
const std::unique_ptr<struct shaderc_compile_options, void (*)(shaderc_compile_options_t)> options(
|
||||
dyn_shaderc::shaderc_compile_options_initialize(), dyn_shaderc::shaderc_compile_options_release);
|
||||
if (!options)
|
||||
{
|
||||
Log_ErrorPrint("Failed to create shaderc options.");
|
||||
return false;
|
||||
}
|
||||
|
||||
dyn_shaderc::shaderc_compile_options_set_source_language(options.get(), shaderc_source_language_glsl);
|
||||
dyn_shaderc::shaderc_compile_options_set_target_env(options.get(), shaderc_target_env_vulkan, 0);
|
||||
|
||||
if (m_debug_device)
|
||||
{
|
||||
dyn_shaderc::shaderc_compile_options_set_generate_debug_info(options.get());
|
||||
if (nonsemantic_debug_info)
|
||||
dyn_shaderc::shaderc_compile_options_set_emit_non_semantic_debug_info(options.get());
|
||||
|
||||
dyn_shaderc::shaderc_compile_options_set_optimization_level(options.get(), shaderc_optimization_level_zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
dyn_shaderc::shaderc_compile_options_set_optimization_level(options.get(), shaderc_optimization_level_performance);
|
||||
}
|
||||
|
||||
const std::unique_ptr<struct shaderc_compilation_result, void (*)(shaderc_compilation_result_t)> result(
|
||||
dyn_shaderc::shaderc_compile_into_spv(dyn_shaderc::s_compiler.get(), source.data(), source.length(),
|
||||
stage_kinds[static_cast<size_t>(stage)], "source", entry_point,
|
||||
options.get()),
|
||||
dyn_shaderc::shaderc_result_release);
|
||||
const shaderc_compilation_status status = result ? dyn_shaderc::shaderc_result_get_compilation_status(result.get()) :
|
||||
shaderc_compilation_status_null_result_object;
|
||||
if (status != shaderc_compilation_status_success)
|
||||
{
|
||||
const char* status_name = "UNKNOWN";
|
||||
for (const auto& it : status_names)
|
||||
{
|
||||
if (status == it.first)
|
||||
{
|
||||
status_name = it.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string_view errors(result ? dyn_shaderc::shaderc_result_get_error_message(result.get()) :
|
||||
"null result object");
|
||||
Log_ErrorFmt("Failed to compile shader to SPIR-V: {}\n{}", status_name, errors);
|
||||
DumpBadShader(source, errors);
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t num_warnings = dyn_shaderc::shaderc_result_get_num_warnings(result.get());
|
||||
if (num_warnings > 0)
|
||||
Log_WarningFmt("Shader compiled with warnings:\n{}", dyn_shaderc::shaderc_result_get_error_message(result.get()));
|
||||
|
||||
const size_t spirv_size = dyn_shaderc::shaderc_result_get_length(result.get());
|
||||
DebugAssert(spirv_size > 0);
|
||||
out_binary->resize(spirv_size);
|
||||
std::memcpy(out_binary->data(), dyn_shaderc::shaderc_result_get_bytes(result.get()), spirv_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -719,6 +719,11 @@ protected:
|
|||
|
||||
void TrimTexturePool();
|
||||
|
||||
#if defined(ENABLE_VULKAN) || defined(__APPLE__)
|
||||
bool CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, std::string_view source, const char* entry_point,
|
||||
bool nonsemantic_debug_info, DynamicHeapArray<u8>* out_binary);
|
||||
#endif
|
||||
|
||||
Features m_features = {};
|
||||
u32 m_max_texture_size = 0;
|
||||
u32 m_max_multisamples = 0;
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#define FMT_EXCEPTIONS 0
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include "shaderc/shaderc.hpp"
|
||||
#include "spirv_cross_c.h"
|
||||
|
||||
#include <array>
|
||||
|
@ -59,8 +58,6 @@ static constexpr std::array<MTLPixelFormat, static_cast<u32>(GPUTexture::Format:
|
|||
MTLPixelFormatBGR10A2Unorm, // RGB10A2
|
||||
};
|
||||
|
||||
static std::unique_ptr<shaderc::Compiler> s_shaderc_compiler;
|
||||
|
||||
static NSString* StringViewToNSString(std::string_view str)
|
||||
{
|
||||
if (str.empty())
|
||||
|
@ -657,44 +654,12 @@ std::unique_ptr<GPUShader> MetalDevice::CreateShaderFromSource(GPUShaderStage st
|
|||
{
|
||||
static constexpr bool dump_shaders = false;
|
||||
|
||||
static constexpr const std::array<shaderc_shader_kind, static_cast<size_t>(GPUShaderStage::MaxCount)> stage_kinds = {{
|
||||
shaderc_glsl_vertex_shader,
|
||||
shaderc_glsl_fragment_shader,
|
||||
shaderc_glsl_geometry_shader,
|
||||
shaderc_glsl_compute_shader,
|
||||
}};
|
||||
|
||||
// TODO: NOT thread safe, yet.
|
||||
if (!s_shaderc_compiler)
|
||||
s_shaderc_compiler = std::make_unique<shaderc::Compiler>();
|
||||
|
||||
shaderc::CompileOptions spv_options;
|
||||
spv_options.SetSourceLanguage(shaderc_source_language_glsl);
|
||||
spv_options.SetTargetEnvironment(shaderc_target_env_vulkan, 0);
|
||||
|
||||
if (m_debug_device)
|
||||
{
|
||||
spv_options.SetOptimizationLevel(shaderc_optimization_level_zero);
|
||||
spv_options.SetGenerateDebugInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
spv_options.SetOptimizationLevel(shaderc_optimization_level_performance);
|
||||
}
|
||||
|
||||
const shaderc::SpvCompilationResult result = s_shaderc_compiler->CompileGlslToSpv(
|
||||
source.data(), source.length(), stage_kinds[static_cast<size_t>(stage)], "source", entry_point, spv_options);
|
||||
if (result.GetCompilationStatus() != shaderc_compilation_status_success)
|
||||
{
|
||||
const std::string errors = result.GetErrorMessage();
|
||||
DumpBadShader(source, errors);
|
||||
Log_ErrorFmt("Failed to compile shader to SPIR-V:\n{}", errors);
|
||||
DynamicHeapArray<u8> local_binary;
|
||||
DynamicHeapArray<u8>* dest_binary = out_binary ? out_binary : &local_binary;
|
||||
if (!CompileGLSLShaderToVulkanSpv(stage, source, entry_point, false, dest_binary))
|
||||
return {};
|
||||
}
|
||||
else if (result.GetNumWarnings() > 0)
|
||||
{
|
||||
Log_WarningFmt("Shader compiled with warnings:\n{}", result.GetErrorMessage());
|
||||
}
|
||||
|
||||
AssertMsg((dest_binary->size() % 4) == 0, "Compile result should be 4 byte aligned.");
|
||||
|
||||
spvc_context sctx;
|
||||
spvc_result sres;
|
||||
|
@ -710,8 +675,7 @@ std::unique_ptr<GPUShader> MetalDevice::CreateShaderFromSource(GPUShaderStage st
|
|||
sctx, [](void*, const char* error) { Log_ErrorFmt("SPIRV-Cross reported an error: {}", error); }, nullptr);
|
||||
|
||||
spvc_parsed_ir sir;
|
||||
if ((sres = spvc_context_parse_spirv(sctx, result.cbegin(), std::distance(result.cbegin(), result.cend()), &sir)) !=
|
||||
SPVC_SUCCESS)
|
||||
if ((sres = spvc_context_parse_spirv(sctx, reinterpret_cast<const u32*>(dest_binary->data()), dest_binary->size() / 4, &sir)) != SPVC_SUCCESS)
|
||||
{
|
||||
Log_ErrorFmt("spvc_context_parse_spirv() failed: {}", static_cast<int>(sres));
|
||||
DumpBadShader(source, std::string_view());
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DepsIncludeDir)SDL2</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);freetype.lib;libjpeg.lib;libpng16.lib;libwebp.lib;SDL2.lib;shaderc_shared.lib;zlib.lib;zstd.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);freetype.lib;libjpeg.lib;libpng16.lib;libwebp.lib;SDL2.lib;zlib.lib;zstd.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -8,12 +8,8 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
|
||||
#include "shaderc/shaderc.hpp"
|
||||
|
||||
Log_SetChannel(VulkanDevice);
|
||||
|
||||
static std::unique_ptr<shaderc::Compiler> s_shaderc_compiler;
|
||||
|
||||
VulkanShader::VulkanShader(GPUShaderStage stage, VkShaderModule mod) : GPUShader(stage), m_module(mod)
|
||||
{
|
||||
}
|
||||
|
@ -48,57 +44,17 @@ std::unique_ptr<GPUShader> VulkanDevice::CreateShaderFromSource(GPUShaderStage s
|
|||
const char* entry_point,
|
||||
DynamicHeapArray<u8>* out_binary)
|
||||
{
|
||||
static constexpr const std::array<shaderc_shader_kind, static_cast<size_t>(GPUShaderStage::MaxCount)> stage_kinds = {{
|
||||
shaderc_glsl_vertex_shader,
|
||||
shaderc_glsl_fragment_shader,
|
||||
shaderc_glsl_geometry_shader,
|
||||
shaderc_glsl_compute_shader,
|
||||
}};
|
||||
|
||||
// TODO: NOT thread safe, yet.
|
||||
if (!s_shaderc_compiler)
|
||||
s_shaderc_compiler = std::make_unique<shaderc::Compiler>();
|
||||
|
||||
shaderc::CompileOptions options;
|
||||
options.SetSourceLanguage(shaderc_source_language_glsl);
|
||||
options.SetTargetEnvironment(shaderc_target_env_vulkan, 0);
|
||||
|
||||
if (m_debug_device)
|
||||
DynamicHeapArray<u8> local_binary;
|
||||
DynamicHeapArray<u8>* dest_binary = out_binary ? out_binary : &local_binary;
|
||||
if (!CompileGLSLShaderToVulkanSpv(stage, source, entry_point, m_optional_extensions.vk_khr_shader_non_semantic_info,
|
||||
dest_binary))
|
||||
{
|
||||
options.SetGenerateDebugInfo();
|
||||
if (m_optional_extensions.vk_khr_shader_non_semantic_info)
|
||||
options.SetEmitNonSemanticDebugInfo();
|
||||
|
||||
options.SetOptimizationLevel(shaderc_optimization_level_zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
options.SetOptimizationLevel(shaderc_optimization_level_performance);
|
||||
}
|
||||
|
||||
const shaderc::SpvCompilationResult result = s_shaderc_compiler->CompileGlslToSpv(
|
||||
source.data(), source.length(), stage_kinds[static_cast<size_t>(stage)], "source", entry_point, options);
|
||||
if (result.GetCompilationStatus() != shaderc_compilation_status_success)
|
||||
{
|
||||
const std::string errors = result.GetErrorMessage();
|
||||
DumpBadShader(source, errors);
|
||||
Log_ErrorFmt("Failed to compile shader to SPIR-V:\n{}", errors);
|
||||
return {};
|
||||
}
|
||||
else if (result.GetNumWarnings() > 0)
|
||||
{
|
||||
Log_WarningFmt("Shader compiled with warnings:\n{}", result.GetErrorMessage());
|
||||
}
|
||||
|
||||
const size_t spirv_size = std::distance(result.cbegin(), result.cend()) * sizeof(*result.cbegin());
|
||||
DebugAssert(spirv_size > 0);
|
||||
if (out_binary)
|
||||
{
|
||||
out_binary->resize(spirv_size);
|
||||
std::copy(result.cbegin(), result.cend(), reinterpret_cast<uint32_t*>(out_binary->data()));
|
||||
}
|
||||
AssertMsg((dest_binary->size() % 4) == 0, "Compile result should be 4 byte aligned.");
|
||||
|
||||
return CreateShaderFromBinary(stage, std::span<const u8>(reinterpret_cast<const u8*>(result.cbegin()), spirv_size));
|
||||
return CreateShaderFromBinary(stage, dest_binary->cspan());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in a new issue