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

feat: Add advanced options to imgui #4

Merged
merged 3 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 0 additions & 61 deletions include/PCH.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,68 +71,7 @@ namespace util
using SKSE::stl::report_and_fail;
}

#define DLLEXPORT __declspec(dllexport)

#include "Plugin.h"

bool Load();

void InitializeLog()
{
#ifndef NDEBUG
auto sink = std::make_shared<spdlog::sinks::msvc_sink_mt>();
#else
auto path = logger::log_directory();
if (!path) {
util::report_and_fail("Failed to find standard logging directory"sv);
}

*path /= std::format("{}.log"sv, Plugin::NAME);
auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path->string(), true);
#endif

#ifndef NDEBUG
const auto level = spdlog::level::trace;
#else
const auto level = spdlog::level::info;
#endif

auto log = std::make_shared<spdlog::logger>("global log"s, std::move(sink));
log->set_level(level);
log->flush_on(spdlog::level::info);

spdlog::set_default_logger(std::move(log));
spdlog::set_pattern("%v"s);
}

extern "C" DLLEXPORT bool SKSEAPI SKSEPlugin_Load(const SKSE::LoadInterface* a_skse)
{
#ifndef NDEBUG
while (!WinAPI::IsDebuggerPresent()) {};
#endif
InitializeLog();
logger::info("Loaded plugin");
SKSE::Init(a_skse);
return Load();
}

extern "C" DLLEXPORT constinit auto SKSEPlugin_Version = []() noexcept {
SKSE::PluginVersionData v;
v.PluginName(Plugin::NAME.data());
v.PluginVersion(Plugin::VERSION);
v.UsesAddressLibrary(true);
v.HasNoStructUse();
return v;
}();

extern "C" DLLEXPORT bool SKSEAPI SKSEPlugin_Query(const SKSE::QueryInterface*, SKSE::PluginInfo* pluginInfo)
{
pluginInfo->name = SKSEPlugin_Version.pluginName;
pluginInfo->infoVersion = SKSE::PluginInfo::kVersion;
pluginInfo->version = SKSEPlugin_Version.pluginVersion;
return true;
}

#include <wrl/client.h>
#include <wrl/event.h>

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

if (!lights || lightCountChanged) {
lightCount = currentLightCount;

logger::debug("Found {} lights", lightCount);
D3D11_BUFFER_DESC sbDesc{};
sbDesc.Usage = D3D11_USAGE_DYNAMIC;
sbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
Expand Down
107 changes: 106 additions & 1 deletion src/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,81 @@

#include <detours/Detours.h>

#include "Menu.h"
#include "ShaderCache.h"
#include "State.h"
#include "Menu.h"

#include "ShaderTools/BSShaderHooks.h"

std::unordered_map<void*, std::pair<std::unique_ptr<uint8_t[]>, size_t>> ShaderBytecodeMap;

void RegisterShaderBytecode(void* Shader, const void* Bytecode, size_t BytecodeLength)
{
// Grab a copy since the pointer isn't going to be valid forever
auto codeCopy = std::make_unique<uint8_t[]>(BytecodeLength);
memcpy(codeCopy.get(), Bytecode, BytecodeLength);
logger::debug(fmt::runtime("Saving shader at index {:x} with {} bytes:\t{:x}"), (std::uintptr_t)Shader, BytecodeLength, (std::uintptr_t)Bytecode);
ShaderBytecodeMap.emplace(Shader, std::make_pair(std::move(codeCopy), BytecodeLength));
}

const std::pair<std::unique_ptr<uint8_t[]>, size_t>& GetShaderBytecode(void* Shader)
{
logger::debug(fmt::runtime("Loading shader at index {:x}"), (std::uintptr_t)Shader);
return ShaderBytecodeMap.at(Shader);
}

void DumpShader(const REX::BSShader* thisClass, const RE::BSGraphics::VertexShader* shader, const std::pair<std::unique_ptr<uint8_t[]>, size_t>& bytecode)
{
uint8_t* dxbcData = new uint8_t[bytecode.second];
size_t dxbcLen = bytecode.second;
memcpy(dxbcData, bytecode.first.get(), bytecode.second);

std::string dumpDir = std::format("Data\\ShaderDump\\{}\\{}.vs.bin", thisClass->m_LoaderType, shader->id);
auto directoryPath = std::format("Data\\ShaderDump\\{}", thisClass->m_LoaderType);
logger::debug(fmt::runtime("Dumping vertex shader {} with id {:x} at {}"), thisClass->m_LoaderType, shader->id, dumpDir);

if (!std::filesystem::is_directory(directoryPath)) {
try {
std::filesystem::create_directories(directoryPath);
} catch (std::filesystem::filesystem_error const& ex) {
logger::error("Failed to create folder: {}", ex.what());
}
}

if (FILE * file; fopen_s(&file, dumpDir.c_str(), "wb") == 0) {
fwrite(dxbcData, 1, dxbcLen, file);
fclose(file);
}

delete[] dxbcData;
}

void DumpShader(const REX::BSShader* thisClass, const RE::BSGraphics::PixelShader* shader, const std::pair<std::unique_ptr<uint8_t[]>, size_t>& bytecode)
{
uint8_t* dxbcData = new uint8_t[bytecode.second];
size_t dxbcLen = bytecode.second;
memcpy(dxbcData, bytecode.first.get(), bytecode.second);

std::string dumpDir = std::format("Data\\ShaderDump\\{}\\{}.ps.bin", thisClass->m_LoaderType, shader->id);

auto directoryPath = std::format("Data\\ShaderDump\\{}", thisClass->m_LoaderType);
logger::debug(fmt::runtime("Dumping pixel shader {} with id {:x} at {}"), thisClass->m_LoaderType, shader->id, dumpDir);
if (!std::filesystem::is_directory(directoryPath)) {
try {
std::filesystem::create_directories(directoryPath);
} catch (std::filesystem::filesystem_error const& ex) {
logger::error("Failed to create folder: {}", ex.what());
}
}

if (FILE * file; fopen_s(&file, dumpDir.c_str(), "wb") == 0) {
fwrite(dxbcData, 1, dxbcLen, file);
fclose(file);
}

delete[] dxbcData;
}

void hk_BSShader_LoadShaders(RE::BSShader* shader, std::uintptr_t stream);

decltype(&hk_BSShader_LoadShaders) ptr_BSShader_LoadShaders;
Expand All @@ -17,9 +86,17 @@ void hk_BSShader_LoadShaders(RE::BSShader* shader, std::uintptr_t stream)
(ptr_BSShader_LoadShaders)(shader, stream);
auto& shaderCache = SIE::ShaderCache::Instance();
for (const auto& entry : shader->pixelShaders) {
if (entry->shader && shaderCache.IsDump()) {
auto& bytecode = GetShaderBytecode(entry->shader);
DumpShader((REX::BSShader*)shader, entry, bytecode);
}
shaderCache.GetPixelShader(*shader, entry->id);
}
for (const auto& entry : shader->vertexShaders) {
if (entry->shader && shaderCache.IsDump()) {
auto& bytecode = GetShaderBytecode(entry->shader);
DumpShader((REX::BSShader*)shader, entry, bytecode);
}
shaderCache.GetVertexShader(*shader, entry->id);
}
BSShaderHooks::hk_LoadShaders((REX::BSShader*)shader, stream);
Expand Down Expand Up @@ -57,6 +134,29 @@ void hk_BSGraphics_SetDirtyStates(bool isCompute)
State::GetSingleton()->Draw();
}

decltype(&ID3D11Device::CreateVertexShader) ptrCreateVertexShader;
decltype(&ID3D11Device::CreatePixelShader) ptrCreatePixelShader;

HRESULT hk_CreateVertexShader(ID3D11Device* This, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader)
{
HRESULT hr = (This->*ptrCreateVertexShader)(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader);

if (SUCCEEDED(hr))
RegisterShaderBytecode(*ppVertexShader, pShaderBytecode, BytecodeLength);

return hr;
}

HRESULT STDMETHODCALLTYPE hk_CreatePixelShader(ID3D11Device* This, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader)
{
HRESULT hr = (This->*ptrCreatePixelShader)(pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader);

if (SUCCEEDED(hr))
RegisterShaderBytecode(*ppPixelShader, pShaderBytecode, BytecodeLength);

return hr;
}

namespace Hooks
{
struct BSGraphics_Renderer_Init_InitD3D
Expand All @@ -79,6 +179,11 @@ namespace Hooks

*(uintptr_t*)&ptr_IDXGISwapChain_Present = Detours::X64::DetourClassVTable(*(uintptr_t*)swapchain, &hk_IDXGISwapChain_Present, 8);

auto& shaderCache = SIE::ShaderCache::Instance();
if (shaderCache.IsDump()) {
*(uintptr_t*)&ptrCreateVertexShader = Detours::X64::DetourClassVTable(*(uintptr_t*)device, &hk_CreateVertexShader, 12);
*(uintptr_t*)&ptrCreatePixelShader = Detours::X64::DetourClassVTable(*(uintptr_t*)device, &hk_CreatePixelShader, 15);
}
State::GetSingleton()->Setup();
Menu::GetSingleton()->Init(swapchain, device, context);
}
Expand Down
36 changes: 36 additions & 0 deletions src/Menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ RE::BSEventNotifyControl Menu::ProcessEvent(RE::InputEvent* const* a_event, RE::
}
break;
case RE::INPUT_DEVICE::kMouse:
logger::trace("Detect mouse scan code {} value {} pressed: {}", scan_code, button->Value(), button->IsPressed());
if (scan_code > 7) // middle scroll
io.AddMouseWheelEvent(0, button->Value() * (scan_code == 8 ? 1 : -1));
else {
Expand Down Expand Up @@ -273,6 +274,41 @@ void Menu::DrawSettings()
}
}

if (ImGui::CollapsingHeader("Advanced", ImGuiTreeNodeFlags_DefaultOpen)) {
bool useDump = shaderCache.IsDump();
if (ImGui::Checkbox("Dump Shaders", &useDump)) {
shaderCache.SetDump(useDump);
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::Text("Dump shaders at startup. This should be used only when reversing shaders. Normal users don't need this.");
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
spdlog::level::level_enum logLevel = State::GetSingleton()->GetLogLevel();
const char* items[] = {
"trace",
"debug",
"info",
"warn",
"err",
"critical",
"off"
};
static int item_current = static_cast<int>(logLevel);
if (ImGui::Combo("Log Level", &item_current, items, IM_ARRAYSIZE(items))) {
ImGui::SameLine();
State::GetSingleton()->SetLogLevel(static_cast<spdlog::level::level_enum>(item_current));
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::Text("Log level. Trace is most verbose. Default is info.");
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
if (ImGui::CollapsingHeader("General", ImGuiTreeNodeFlags_DefaultOpen)) {
bool useCustomShaders = shaderCache.IsEnabled();
if (ImGui::Checkbox("Enable Shaders", &useCustomShaders)) {
Expand Down
20 changes: 15 additions & 5 deletions src/ShaderCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ namespace SIE
defines[1] = { nullptr, nullptr };
GetShaderDefines(type, descriptor, &defines[1]);

//logger::info("{}, {}", descriptor, MergeDefinesString(defines));
logger::debug("{}, {}", descriptor, MergeDefinesString(defines));

auto diskPath = GetDiskPath(shader.fxpFilename, descriptor, shaderClass);

Expand All @@ -1041,7 +1041,7 @@ namespace SIE
std::transform(diskPath.begin(), diskPath.end(), std::back_inserter(str), [](wchar_t c) {
return (char)c;
});
//logger::debug("Loaded shader from {}", str);
logger::debug("Loaded shader from {}", str);
return shaderBlob;
}
}
Expand All @@ -1065,8 +1065,8 @@ namespace SIE

return nullptr;
}
//logger::debug("Compiled {} shader {}::{}", magic_enum::enum_name(shaderClass),
// magic_enum::enum_name(type), descriptor);
logger::debug("Compiled {} shader {}::{}", magic_enum::enum_name(shaderClass),
magic_enum::enum_name(type), descriptor);

if (useDiskCache) {
auto directoryPath = std::format("Data/ShaderCache/{}", shader.fxpFilename);
Expand All @@ -1092,7 +1092,7 @@ namespace SIE
std::transform(diskPath.begin(), diskPath.end(), std::back_inserter(str), [](wchar_t c) {
return (char)c;
});
//logger::debug("Saved shader to {}", str);
logger::debug("Saved shader to {}", str);
}
}

Expand Down Expand Up @@ -1312,6 +1312,16 @@ namespace SIE
isAsync = value;
}

bool ShaderCache::IsDump() const
{
return isDump;
}

void ShaderCache::SetDump(bool value)
{
isDump = value;
}

bool ShaderCache::IsDiskCache() const
{
return isDiskCache;
Expand Down
3 changes: 3 additions & 0 deletions src/ShaderCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ namespace SIE
void SetEnabled(bool value);
bool IsAsync() const;
void SetAsync(bool value);
bool IsDump() const;
void SetDump(bool value);

bool IsDiskCache() const;
void SetDiskCache(bool value);
Expand Down Expand Up @@ -129,6 +131,7 @@ namespace SIE
bool isEnabled = false;
bool isDiskCache = false;
bool isAsync = true;
bool isDump = false;

std::vector<std::jthread> compilationThreads;
std::mutex vertexShadersMutex;
Expand Down
27 changes: 27 additions & 0 deletions src/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ void State::Load()
Menu::GetSingleton()->Load(settings["Menu"]);
}

if (settings["Advanced"].is_object()) {
json& advanced = settings["Advanced"];
if (advanced["Dump Shaders"].is_boolean())
shaderCache.SetDump(advanced["Dump Shaders"]);
if (advanced["Log Level"].is_number_integer()) {
logLevel = static_cast<spdlog::level::level_enum>(max(spdlog::level::trace, min(spdlog::level::off, (int)advanced["Log Level"])));
}
}

if (settings["General"].is_object()) {
json& general = settings["General"];

Expand Down Expand Up @@ -118,6 +127,11 @@ void State::Save()

Menu::GetSingleton()->Save(settings);

json advanced;
advanced["Dump Shaders"] = shaderCache.IsDump();
advanced["Log Level"] = logLevel;
settings["Advanced"] = advanced;

json general;
general["Enable Shaders"] = shaderCache.IsEnabled();
general["Enable Disk Cache"] = shaderCache.IsDiskCache();
Expand Down Expand Up @@ -159,4 +173,17 @@ void State::WriteDiskCacheInfo(CSimpleIniA& a_ini)
GrassLighting::GetSingleton()->WriteDiskCacheInfo(a_ini);
DistantTreeLighting::GetSingleton()->WriteDiskCacheInfo(a_ini);
GrassCollision::GetSingleton()->WriteDiskCacheInfo(a_ini);
}

void State::SetLogLevel(spdlog::level::level_enum a_level)
{
logLevel = a_level;
spdlog::set_level(logLevel);
spdlog::flush_on(logLevel);
logger::info("Log Level set to {} ({})", magic_enum::enum_name(logLevel), static_cast<int>(logLevel));
}

spdlog::level::level_enum State::GetLogLevel()
{
return logLevel;
}
Loading