Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: sync with dev #108

Merged
merged 9 commits into from
Sep 24, 2023
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ include(XSEPlugin)
# #######################################################################################################################
# # Find dependencies
# #######################################################################################################################
find_path(BSHOSHANY_THREAD_POOL_INCLUDE_DIRS "BS_thread_pool.hpp")
find_package(magic_enum CONFIG REQUIRED)
find_package(xbyak CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
Expand All @@ -37,6 +38,7 @@ find_package(pystring CONFIG REQUIRED)
target_include_directories(
${PROJECT_NAME}
PRIVATE
${BSHOSHANY_THREAD_POOL_INCLUDE_DIRS}
${CLIB_UTIL_INCLUDE_DIRS}
)

Expand Down
2 changes: 1 addition & 1 deletion src/Features/LightLimitFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ void LightLimitFix::UpdateLights()

if (settings.EnableFirstPersonShadows) {
if (auto playerCamera = RE::PlayerCamera::GetSingleton()) {
if (playerCamera->IsInFirstPerson()) {
if (playerCamera->IsInFirstPerson() || REL::Module::IsVR()) {
if (auto player = RE::PlayerCharacter::GetSingleton()) {
firstPersonLight = player->GetInfoRuntimeData().firstPersonLight.get();
thirdPersonLight = player->GetInfoRuntimeData().thirdPersonLight.get();
Expand Down
56 changes: 51 additions & 5 deletions src/Menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "Features/WaterBlending.h"

#define SETTING_MENU_TOGGLEKEY "Toggle Key"
#define SETTING_MENU_SKIPKEY "Skip Compilation Key"
#define SETTING_MENU_FONTSCALE "Font Scale"

void SetupImGuiStyle()
Expand Down Expand Up @@ -77,6 +78,9 @@ void Menu::Load(json& o_json)
if (o_json[SETTING_MENU_TOGGLEKEY].is_number_unsigned()) {
toggleKey = o_json[SETTING_MENU_TOGGLEKEY];
}
if (o_json[SETTING_MENU_SKIPKEY].is_number_unsigned()) {
skipCompilationKey = o_json[SETTING_MENU_SKIPKEY];
}
if (o_json[SETTING_MENU_FONTSCALE].is_number_float()) {
fontScale = o_json[SETTING_MENU_FONTSCALE];
}
Expand All @@ -86,6 +90,7 @@ void Menu::Save(json& o_json)
{
json menu;
menu[SETTING_MENU_TOGGLEKEY] = toggleKey;
menu[SETTING_MENU_SKIPKEY] = skipCompilationKey;
menu[SETTING_MENU_FONTSCALE] = fontScale;

o_json["Menu"] = menu;
Expand Down Expand Up @@ -211,11 +216,18 @@ RE::BSEventNotifyControl Menu::ProcessEvent(RE::InputEvent* const* a_event, RE::
switch (button->device.get()) {
case RE::INPUT_DEVICE::kKeyboard:
if (!button->IsPressed()) {
logger::trace("Detected key code {} ({})", KeyIdToString(key), key);
if (settingToggleKey) {
toggleKey = key;
settingToggleKey = false;
} else if (settingSkipCompilationKey) {
skipCompilationKey = key;
settingSkipCompilationKey = false;
} else if (key == toggleKey) {
IsEnabled = !IsEnabled;
} else if (key == skipCompilationKey) {
auto& shaderCache = SIE::ShaderCache::Instance();
shaderCache.backgroundCompilation = true;
}
}

Expand Down Expand Up @@ -412,10 +424,25 @@ void Menu::DrawSettings()

ImGui::AlignTextToFramePadding();
ImGui::SameLine();
if (ImGui::Button("Change")) {
if (ImGui::Button("Change##toggle")) {
settingToggleKey = true;
}
}
if (settingSkipCompilationKey) {
ImGui::Text("Press any key to set as Skip Compilation Key...");
} else {
ImGui::AlignTextToFramePadding();
ImGui::Text("Skip Compilation Key:");
ImGui::SameLine();
ImGui::AlignTextToFramePadding();
ImGui::TextColored(ImVec4(1, 1, 0, 1), "%s", KeyIdToString(skipCompilationKey));

ImGui::AlignTextToFramePadding();
ImGui::SameLine();
if (ImGui::Button("Change##skip")) {
settingSkipCompilationKey = true;
}
}

if (ImGui::SliderFloat("Font Scale", &fontScale, -2.f, 2.f, "%.2f")) {
float trueScale = exp2(fontScale);
Expand Down Expand Up @@ -479,9 +506,19 @@ void Menu::DrawSettings()
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::Text(
"Number of threads to compile shaders with. "
"The more threads the faster compilation will finish but may make the system unresponsive. "
"This should only be changed between restarts. ");
"Number of threads to use to compile shaders. "
"The more threads the faster compilation will finish but may make the system unresponsive. ");
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
ImGui::SliderInt("Background Compiler Threads", &shaderCache.backgroundCompilationThreadCount, 1, static_cast<int32_t>(std::thread::hardware_concurrency()));
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::Text(
"Number of threads to use to compile shaders while playing game. "
"This is activated if the startup compilation is skipped. "
"The more threads the faster compilation will finish but may make the system unresponsive. ");
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
Expand Down Expand Up @@ -575,7 +612,9 @@ void Menu::DrawOverlay()

auto failed = shaderCache.GetFailedTasks();
auto hide = shaderCache.IsHideErrors();
auto progressTitle = fmt::format("Compiling Shaders: {}", shaderCache.GetShaderStatsString(!state->IsDeveloperMode()).c_str());
auto progressTitle = fmt::format("{}Compiling Shaders: {}",
shaderCache.backgroundCompilation ? "Background " : "",
shaderCache.GetShaderStatsString(!state->IsDeveloperMode()).c_str());
auto percent = (float)compiledShaders / (float)totalShaders;
auto progressOverlay = fmt::format("{}/{} ({:2.1f}%)", compiledShaders, totalShaders, 100 * percent);
if (shaderCache.IsCompiling()) {
Expand All @@ -587,6 +626,13 @@ void Menu::DrawOverlay()
}
ImGui::TextUnformatted(progressTitle.c_str());
ImGui::ProgressBar(percent, ImVec2(0.0f, 0.0f), progressOverlay.c_str());
if (!shaderCache.backgroundCompilation && shaderCache.menuLoaded) {
auto skipShadersText = fmt::format(
"Press {} to proceed without completing shader compilation. "
"WARNING: Uncompiled shaders will have visual errors or cause stuttering when loading.",
KeyIdToString(skipCompilationKey));
ImGui::TextUnformatted(skipShadersText.c_str());
}

ImGui::End();
} else if (failed && !hide) {
Expand Down
3 changes: 2 additions & 1 deletion src/Menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ class Menu : public RE::BSTEventSink<RE::InputEvent*>

private:
uint32_t toggleKey = VK_END;
uint32_t skipCompilationKey = VK_ESCAPE;
bool settingToggleKey = false;

bool settingSkipCompilationKey = false;
float fontScale = 0.f; // exponential

Menu() {}
Expand Down
53 changes: 35 additions & 18 deletions src/ShaderCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <d3d11.h>
#include <d3dcompiler.h>
#include <fmt/std.h>
#include <wrl/client.h>

#include "Features/ExtendedMaterials.h"
Expand Down Expand Up @@ -1501,7 +1502,7 @@ namespace SIE

void ShaderCache::DeleteDiskCache()
{
std::lock_guard lock(compilationSet.compilationMutex);
std::scoped_lock lock{ compilationSet.compilationMutex };
try {
std::filesystem::remove_all(L"Data/ShaderCache");
logger::info("Deleted disk cache");
Expand Down Expand Up @@ -1546,10 +1547,8 @@ namespace SIE

ShaderCache::ShaderCache()
{
logger::debug("ShaderCache initialized with {} compiler threads", compilationThreadCount);
for (size_t threadIndex = 0; threadIndex < compilationThreadCount; ++threadIndex) {
compilationThreads.push_back(std::jthread(&ShaderCache::ProcessCompilationSet, this));
}
logger::debug("ShaderCache initialized with {} compiler threads", (int)compilationThreadCount);
compilationPool.push_task(&ShaderCache::ManageCompilationSet, this, ssource.get_token());
}

RE::BSGraphics::VertexShader* ShaderCache::MakeAndAddVertexShader(const RE::BSShader& shader,
Expand Down Expand Up @@ -1642,16 +1641,24 @@ namespace SIE
hideError = !hideError;
}

void ShaderCache::ProcessCompilationSet()
void ShaderCache::ManageCompilationSet(std::stop_token stoken)
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
while (true) {
const auto& task = compilationSet.WaitTake();
task.Perform();
compilationSet.Complete(task);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
while (!stoken.stop_requested()) {
const auto& task = compilationSet.WaitTake(stoken);
if (!task.has_value())
break; // exit because thread told to end
compilationPool.push_task(&ShaderCache::ProcessCompilationSet, this, stoken, task.value());
}
}

void ShaderCache::ProcessCompilationSet(std::stop_token stoken, SIE::ShaderCompilationTask task)
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
task.Perform();
compilationSet.Complete(task);
}

ShaderCompilationTask::ShaderCompilationTask(ShaderClass aShaderClass,
const RE::BSShader& aShader,
uint32_t aDescriptor) :
Expand Down Expand Up @@ -1684,15 +1691,24 @@ namespace SIE
return GetId() == other.GetId();
}

ShaderCompilationTask CompilationSet::WaitTake()
std::optional<ShaderCompilationTask> CompilationSet::WaitTake(std::stop_token stoken)
{
std::unique_lock lock(compilationMutex);
conditionVariable.wait(lock, [this]() { return !availableTasks.empty(); });
auto& shaderCache = ShaderCache::Instance();
if (!conditionVariable.wait(
lock, stoken,
[this, &shaderCache]() { return !availableTasks.empty() &&
// check against all tasks in queue to trickle the work. It cannot be the active tasks count because the thread pool itself is maximum.
(int)shaderCache.compilationPool.get_tasks_total() <=
(!shaderCache.backgroundCompilation ? shaderCache.compilationThreadCount : shaderCache.backgroundCompilationThreadCount); })) {
/*Woke up because of a stop request. */
return std::nullopt;
}
if (!ShaderCache::Instance().IsCompiling()) { // we just got woken up because there's a task, start clock
lastCalculation = lastReset = high_resolution_clock::now();
}
auto node = availableTasks.extract(availableTasks.begin());
auto task = node.value();
auto& task = node.value();
tasksInProgress.insert(std::move(node));
return task;
}
Expand Down Expand Up @@ -1727,14 +1743,15 @@ namespace SIE
auto now = high_resolution_clock::now();
totalMs += duration_cast<milliseconds>(now - lastCalculation).count();
lastCalculation = now;
std::unique_lock lock(compilationMutex);
std::scoped_lock lock(compilationMutex);
processedTasks.insert(task);
tasksInProgress.erase(task);
conditionVariable.notify_one();
}

void CompilationSet::Clear()
{
std::lock_guard lock(compilationMutex);
std::scoped_lock lock(compilationMutex);
availableTasks.clear();
tasksInProgress.clear();
processedTasks.clear();
Expand Down Expand Up @@ -1763,8 +1780,8 @@ namespace SIE
double CompilationSet::GetEta()
{
auto rate = completedTasks / totalMs;
auto remaining = (int)totalTasks - completedTasks - failedTasks;
return remaining / rate;
auto remaining = totalTasks - completedTasks - failedTasks;
return std::max(remaining / rate, 0.0);
}

std::string CompilationSet::GetStatsString(bool a_timeOnly)
Expand Down
15 changes: 10 additions & 5 deletions src/ShaderCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <RE/B/BSShader.h>

#include "BS_thread_pool.hpp"
#include <chrono>
#include <condition_variable>
#include <unordered_map>
Expand Down Expand Up @@ -60,7 +61,7 @@ namespace SIE
class CompilationSet
{
public:
ShaderCompilationTask WaitTake();
std::optional<ShaderCompilationTask> WaitTake(std::stop_token stoken);
void Add(const ShaderCompilationTask& task);
void Complete(const ShaderCompilationTask& task);
void Clear();
Expand All @@ -77,7 +78,7 @@ namespace SIE
std::unordered_set<ShaderCompilationTask> availableTasks;
std::unordered_set<ShaderCompilationTask> tasksInProgress;
std::unordered_set<ShaderCompilationTask> processedTasks; // completed or failed
std::condition_variable conditionVariable;
std::condition_variable_any conditionVariable;
std::chrono::steady_clock::time_point lastReset = high_resolution_clock::now();
std::chrono::steady_clock::time_point lastCalculation = high_resolution_clock::now();
double totalMs = (double)duration_cast<std::chrono::milliseconds>(lastReset - lastReset).count();
Expand Down Expand Up @@ -124,7 +125,6 @@ namespace SIE
void DeleteDiskCache();
void ValidateDiskCache();
void WriteDiskCacheInfo();

void Clear();

bool AddCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor, ID3DBlob* a_blob);
Expand Down Expand Up @@ -152,10 +152,15 @@ namespace SIE
bool IsHideErrors();

int32_t compilationThreadCount = std::max(static_cast<int32_t>(std::thread::hardware_concurrency()) - 1, 1);
int32_t backgroundCompilationThreadCount = std::max(static_cast<int32_t>(std::thread::hardware_concurrency()) / 2, 1);
BS::thread_pool compilationPool{};
bool backgroundCompilation = false;
bool menuLoaded = false;

private:
ShaderCache();
void ProcessCompilationSet();
void ManageCompilationSet(std::stop_token stoken);
void ProcessCompilationSet(std::stop_token stoken, SIE::ShaderCompilationTask task);

~ShaderCache();

Expand All @@ -172,7 +177,7 @@ namespace SIE
bool isDump = false;
bool hideError = false;

eastl::vector<std::jthread> compilationThreads;
std::stop_source ssource;
std::mutex vertexShadersMutex;
std::mutex pixelShadersMutex;
CompilationSet compilationSet;
Expand Down
3 changes: 3 additions & 0 deletions src/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ void State::Load()
SetDefines(advanced["Shader Defines"]);
if (advanced["Compiler Threads"].is_number_integer())
shaderCache.compilationThreadCount = std::clamp(advanced["Compiler Threads"].get<int32_t>(), 1, static_cast<int32_t>(std::thread::hardware_concurrency()));
if (advanced["Background Compiler Threads"].is_number_integer())
shaderCache.backgroundCompilationThreadCount = std::clamp(advanced["Background Compiler Threads"].get<int32_t>(), 1, static_cast<int32_t>(std::thread::hardware_concurrency()));
}

if (settings["General"].is_object()) {
Expand Down Expand Up @@ -145,6 +147,7 @@ void State::Save()
advanced["Log Level"] = logLevel;
advanced["Shader Defines"] = shaderDefinesString;
advanced["Compiler Threads"] = shaderCache.compilationThreadCount;
advanced["Background Compiler Threads"] = shaderCache.backgroundCompilationThreadCount;
settings["Advanced"] = advanced;

json general;
Expand Down
4 changes: 2 additions & 2 deletions src/XSEPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ void MessageHandler(SKSE::MessagingInterface::Message* message)
RE::BSInputDeviceManager::GetSingleton()->AddEventSink(Menu::GetSingleton());

auto& shaderCache = SIE::ShaderCache::Instance();

while (shaderCache.IsCompiling()) {
shaderCache.menuLoaded = true;
while (shaderCache.IsCompiling() && !shaderCache.backgroundCompilation) {
std::this_thread::sleep_for(100ms);
}

Expand Down
1 change: 1 addition & 0 deletions vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "",
"license": "MIT",
"dependencies": [
"bshoshany-thread-pool",
"fmt",
"directxtk",
"rapidcsv",
Expand Down