Skip to content
Closed
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
181 changes: 89 additions & 92 deletions src/Features/DynamicCubemaps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,114 +25,111 @@ std::vector<std::pair<std::string_view, std::string_view>> DynamicCubemaps::GetS

void DynamicCubemaps::DrawSettings()
{
if (ImGui::TreeNodeEx("Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::TreeNodeEx("Screen Space Reflections", ImGuiTreeNodeFlags_DefaultOpen)) {
recompileFlag |= ImGui::Checkbox("Enable Screen Space Reflections", reinterpret_cast<bool*>(&settings.EnabledSSR));
if (auto _tt = Util::HoverTooltipWrapper()) {
ImGui::Text("Enable Screen Space Reflections on Water");
if (REL::Module::IsVR() && !enabledAtBoot) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
ImGui::Text(
"A restart is required to enable in VR. "
"Save Settings after enabling and restart the game.");
ImGui::PopStyleColor();
}
if (ImGui::TreeNodeEx("Screen Space Reflections", ImGuiTreeNodeFlags_DefaultOpen)) {
recompileFlag |= ImGui::Checkbox("Enable Screen Space Reflections", reinterpret_cast<bool*>(&settings.EnabledSSR));
if (auto _tt = Util::HoverTooltipWrapper()) {
ImGui::Text("Enable Screen Space Reflections on Water");
if (REL::Module::IsVR() && !enabledAtBoot) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
ImGui::Text(
"A restart is required to enable in VR. "
"Save Settings after enabling and restart the game.");
ImGui::PopStyleColor();
}
ImGui::TreePop();
}
}

if (ImGui::TreeNodeEx("Dynamic Cubemap Creator", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("You must enable creator mode by adding the shader define CREATOR");
ImGui::Checkbox("Enable Creator", reinterpret_cast<bool*>(&settings.EnabledCreator));
if (settings.EnabledCreator) {
ImGui::ColorEdit3("Color", reinterpret_cast<float*>(&settings.CubemapColor));
ImGui::SliderFloat("Roughness", &settings.CubemapColor.w, 0.0f, 1.0f, "%.2f");
if (ImGui::Button("Export")) {
auto device = globals::d3d::device;
auto context = globals::d3d::context;

D3D11_TEXTURE2D_DESC texDesc{};
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texDesc.Height = 1;
texDesc.Width = 1;
texDesc.ArraySize = 6;
texDesc.MipLevels = 1;
texDesc.SampleDesc.Count = 1;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = 0;
texDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;

D3D11_SUBRESOURCE_DATA subresourceData[6];

struct PixelData
{
uint8_t r, g, b, a;
};

static PixelData colorPixel{};

colorPixel = { (uint8_t)((settings.CubemapColor.x * 255.0f) + 0.5f),
(uint8_t)((settings.CubemapColor.y * 255.0f) + 0.5f),
(uint8_t)((settings.CubemapColor.z * 255.0f) + 0.5f),
std::min((uint8_t)254u, (uint8_t)((settings.CubemapColor.w * 255.0f) + 0.5f)) };

static PixelData emptyPixel{};

subresourceData[0].pSysMem = &colorPixel;
subresourceData[0].SysMemPitch = sizeof(PixelData);
subresourceData[0].SysMemSlicePitch = sizeof(PixelData);

for (uint i = 1; i < 6; i++) {
subresourceData[i].pSysMem = &emptyPixel;
subresourceData[i].SysMemPitch = sizeof(PixelData);
subresourceData[i].SysMemSlicePitch = sizeof(PixelData);
}

ID3D11Texture2D* tempTexture;
DirectX::ScratchImage image;
if (ImGui::TreeNodeEx("Dynamic Cubemap Creator", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("You must enable creator mode by adding the shader define CREATOR");
ImGui::Checkbox("Enable Creator", reinterpret_cast<bool*>(&settings.EnabledCreator));
if (settings.EnabledCreator) {
ImGui::ColorEdit3("Color", reinterpret_cast<float*>(&settings.CubemapColor));
ImGui::SliderFloat("Roughness", &settings.CubemapColor.w, 0.0f, 1.0f, "%.2f");
if (ImGui::Button("Export")) {
auto device = globals::d3d::device;
auto context = globals::d3d::context;

D3D11_TEXTURE2D_DESC texDesc{};
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texDesc.Height = 1;
texDesc.Width = 1;
texDesc.ArraySize = 6;
texDesc.MipLevels = 1;
texDesc.SampleDesc.Count = 1;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = 0;
texDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;

D3D11_SUBRESOURCE_DATA subresourceData[6];

struct PixelData
{
uint8_t r, g, b, a;
};

static PixelData colorPixel{};

colorPixel = { (uint8_t)((settings.CubemapColor.x * 255.0f) + 0.5f),
(uint8_t)((settings.CubemapColor.y * 255.0f) + 0.5f),
(uint8_t)((settings.CubemapColor.z * 255.0f) + 0.5f),
std::min((uint8_t)254u, (uint8_t)((settings.CubemapColor.w * 255.0f) + 0.5f)) };

static PixelData emptyPixel{};

subresourceData[0].pSysMem = &colorPixel;
subresourceData[0].SysMemPitch = sizeof(PixelData);
subresourceData[0].SysMemSlicePitch = sizeof(PixelData);

for (uint i = 1; i < 6; i++) {
subresourceData[i].pSysMem = &emptyPixel;
subresourceData[i].SysMemPitch = sizeof(PixelData);
subresourceData[i].SysMemSlicePitch = sizeof(PixelData);
}

try {
DX::ThrowIfFailed(device->CreateTexture2D(&texDesc, subresourceData, &tempTexture));
DX::ThrowIfFailed(CaptureTexture(device, context, tempTexture, image));
ID3D11Texture2D* tempTexture;
DirectX::ScratchImage image;

if (std::filesystem::create_directories(defaultDynamicCubeMapSavePath)) {
logger::info("Missing DynamicCubeMap Creator directory created: {}", defaultDynamicCubeMapSavePath);
}
try {
DX::ThrowIfFailed(device->CreateTexture2D(&texDesc, subresourceData, &tempTexture));
DX::ThrowIfFailed(CaptureTexture(device, context, tempTexture, image));

std::filesystem::path DynamicCubeMapSavePath = defaultDynamicCubeMapSavePath;
std::filesystem::path filename(std::format("R{:03d}G{:03d}B{:03d}A{:03d}.dds", colorPixel.r, colorPixel.g, colorPixel.b, colorPixel.a));
DynamicCubeMapSavePath /= filename;
if (std::filesystem::create_directories(defaultDynamicCubeMapSavePath)) {
logger::info("Missing DynamicCubeMap Creator directory created: {}", defaultDynamicCubeMapSavePath);
}

if (std::filesystem::exists(DynamicCubeMapSavePath)) {
logger::info("DynamicCubeMap Creator file for {} already exists, skipping.", filename.string());
} else {
DX::ThrowIfFailed(SaveToDDSFile(image.GetImages(), image.GetImageCount(), image.GetMetadata(), DirectX::DDS_FLAGS::DDS_FLAGS_NONE, DynamicCubeMapSavePath.c_str()));
logger::info("DynamicCubeMap Creator file for {} written", filename.string());
}
std::filesystem::path DynamicCubeMapSavePath = defaultDynamicCubeMapSavePath;
std::filesystem::path filename(std::format("R{:03d}G{:03d}B{:03d}A{:03d}.dds", colorPixel.r, colorPixel.g, colorPixel.b, colorPixel.a));
DynamicCubeMapSavePath /= filename;

} catch (const std::exception& e) {
logger::error("Failed in DynamicCubeMap Creator file: {} {}", defaultDynamicCubeMapSavePath, e.what());
if (std::filesystem::exists(DynamicCubeMapSavePath)) {
logger::info("DynamicCubeMap Creator file for {} already exists, skipping.", filename.string());
} else {
DX::ThrowIfFailed(SaveToDDSFile(image.GetImages(), image.GetImageCount(), image.GetMetadata(), DirectX::DDS_FLAGS::DDS_FLAGS_NONE, DynamicCubeMapSavePath.c_str()));
logger::info("DynamicCubeMap Creator file for {} written", filename.string());
}

image.Release();
tempTexture->Release();
} catch (const std::exception& e) {
logger::error("Failed in DynamicCubeMap Creator file: {} {}", defaultDynamicCubeMapSavePath, e.what());
}

image.Release();
tempTexture->Release();
}
Comment on lines +89 to 117

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential null/garbage dereference on cleanup when exceptions occur

  • tempTexture is uninitialized and released unconditionally after the try/catch. If CreateTexture2D throws before assignment, tempTexture is indeterminate and dereferencing it is UB.
  • Prefer RAII (ComPtr) or at least initialize to nullptr and null-check before Release().

Minimal safe fix:

-                ID3D11Texture2D* tempTexture;
+                ID3D11Texture2D* tempTexture = nullptr;
                 DirectX::ScratchImage image;
@@
-                image.Release();
-                tempTexture->Release();
+                image.Release();
+                if (tempTexture) {
+                    tempTexture->Release();
+                    tempTexture = nullptr;
+                }

Optional: Switch to Microsoft::WRL::ComPtr to make cleanup exception-safe automatically.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ID3D11Texture2D* tempTexture;
DirectX::ScratchImage image;
if (std::filesystem::create_directories(defaultDynamicCubeMapSavePath)) {
logger::info("Missing DynamicCubeMap Creator directory created: {}", defaultDynamicCubeMapSavePath);
}
try {
DX::ThrowIfFailed(device->CreateTexture2D(&texDesc, subresourceData, &tempTexture));
DX::ThrowIfFailed(CaptureTexture(device, context, tempTexture, image));
std::filesystem::path DynamicCubeMapSavePath = defaultDynamicCubeMapSavePath;
std::filesystem::path filename(std::format("R{:03d}G{:03d}B{:03d}A{:03d}.dds", colorPixel.r, colorPixel.g, colorPixel.b, colorPixel.a));
DynamicCubeMapSavePath /= filename;
if (std::filesystem::create_directories(defaultDynamicCubeMapSavePath)) {
logger::info("Missing DynamicCubeMap Creator directory created: {}", defaultDynamicCubeMapSavePath);
}
if (std::filesystem::exists(DynamicCubeMapSavePath)) {
logger::info("DynamicCubeMap Creator file for {} already exists, skipping.", filename.string());
} else {
DX::ThrowIfFailed(SaveToDDSFile(image.GetImages(), image.GetImageCount(), image.GetMetadata(), DirectX::DDS_FLAGS::DDS_FLAGS_NONE, DynamicCubeMapSavePath.c_str()));
logger::info("DynamicCubeMap Creator file for {} written", filename.string());
}
std::filesystem::path DynamicCubeMapSavePath = defaultDynamicCubeMapSavePath;
std::filesystem::path filename(std::format("R{:03d}G{:03d}B{:03d}A{:03d}.dds", colorPixel.r, colorPixel.g, colorPixel.b, colorPixel.a));
DynamicCubeMapSavePath /= filename;
} catch (const std::exception& e) {
logger::error("Failed in DynamicCubeMap Creator file: {} {}", defaultDynamicCubeMapSavePath, e.what());
if (std::filesystem::exists(DynamicCubeMapSavePath)) {
logger::info("DynamicCubeMap Creator file for {} already exists, skipping.", filename.string());
} else {
DX::ThrowIfFailed(SaveToDDSFile(image.GetImages(), image.GetImageCount(), image.GetMetadata(), DirectX::DDS_FLAGS::DDS_FLAGS_NONE, DynamicCubeMapSavePath.c_str()));
logger::info("DynamicCubeMap Creator file for {} written", filename.string());
}
image.Release();
tempTexture->Release();
} catch (const std::exception& e) {
logger::error("Failed in DynamicCubeMap Creator file: {} {}", defaultDynamicCubeMapSavePath, e.what());
}
image.Release();
tempTexture->Release();
}
// Ensure tempTexture is always initialized
ID3D11Texture2D* tempTexture = nullptr;
DirectX::ScratchImage image;
try {
DX::ThrowIfFailed(device->CreateTexture2D(&texDesc, subresourceData, &tempTexture));
DX::ThrowIfFailed(CaptureTexture(device, context, tempTexture, image));
if (std::filesystem::create_directories(defaultDynamicCubeMapSavePath)) {
logger::info("Missing DynamicCubeMap Creator directory created: {}", defaultDynamicCubeMapSavePath);
}
std::filesystem::path DynamicCubeMapSavePath = defaultDynamicCubeMapSavePath;
std::filesystem::path filename(std::format(
"R{:03d}G{:03d}B{:03d}A{:03d}.dds",
colorPixel.r, colorPixel.g, colorPixel.b, colorPixel.a));
DynamicCubeMapSavePath /= filename;
if (std::filesystem::exists(DynamicCubeMapSavePath)) {
logger::info(
"DynamicCubeMap Creator file for {} already exists, skipping.",
filename.string());
} else {
DX::ThrowIfFailed(SaveToDDSFile(
image.GetImages(), image.GetImageCount(),
image.GetMetadata(), DirectX::DDS_FLAGS::DDS_FLAGS_NONE,
DynamicCubeMapSavePath.c_str()));
logger::info(
"DynamicCubeMap Creator file for {} written",
filename.string());
}
} catch (const std::exception& e) {
logger::error(
"Failed in DynamicCubeMap Creator file: {} {}",
defaultDynamicCubeMapSavePath, e.what());
}
image.Release();
if (tempTexture) {
tempTexture->Release();
tempTexture = nullptr;
}
}
🤖 Prompt for AI Agents
In src/Features/DynamicCubemaps.cpp around lines 90-118, tempTexture is declared
uninitialized and unconditionally released after the try/catch which can cause
undefined behavior if CreateTexture2D throws; fix by making tempTexture
initialized to nullptr and only calling Release() when it's non-null, or better
replace the raw pointer with Microsoft::WRL::ComPtr<ID3D11Texture2D> (or a
similar RAII wrapper) so the texture is released automatically even if an
exception is thrown; ensure the image.Release() and texture cleanup happen
safely in all control paths (if keeping raw pointer, null-check before Release
and set to nullptr after release).

ImGui::TreePop();
}
if (REL::Module::IsVR()) {
if (ImGui::TreeNodeEx("Advanced VR Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
Util::RenderImGuiSettingsTree(iniVRCubeMapSettings, "VR");
Util::RenderImGuiSettingsTree(hiddenVRCubeMapSettings, "hiddenVR");
ImGui::TreePop();
}
ImGui::TreePop();
}
if (REL::Module::IsVR()) {
if (ImGui::TreeNodeEx("Advanced VR Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
Util::RenderImGuiSettingsTree(iniVRCubeMapSettings, "VR");
Util::RenderImGuiSettingsTree(hiddenVRCubeMapSettings, "hiddenVR");
ImGui::TreePop();
}
}

ImGui::Spacing();
ImGui::Spacing();
ImGui::Spacing();
ImGui::Spacing();

ImGui::TreePop();
}
ImGui::TreePop();
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

void DynamicCubemaps::LoadSettings(json& o_json)
Expand Down
7 changes: 6 additions & 1 deletion src/Features/PerformanceOverlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,12 @@ void PerformanceOverlay::DrawFPS()
ImGui::TableNextColumn();
ImGui::Text(this->state.isFrameGenerationActive ? "Raw FPS:" : "FPS:");
ImGui::TableNextColumn();
ImGui::Text("%.1f (%.2f ms)", this->state.smoothFps, this->state.smoothFrameTimeMs);
float avgFrameTime = std::accumulate(this->state.frameTimeHistory.GetData().begin(),
this->state.frameTimeHistory.GetData().end(), 0.0f) /
this->state.frameTimeHistory.GetData().size();
float avgFps = avgFrameTime > 0.0f ? 1000.0f / avgFrameTime : 0.0f;

ImGui::Text("%.1f (%.2f ms) | Avg: %.1f", this->state.smoothFps, this->state.smoothFrameTimeMs, avgFps);

if (this->state.isFrameGenerationActive) {
ImGui::TableNextColumn();
Expand Down
21 changes: 9 additions & 12 deletions src/Features/VolumetricLighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,19 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(

void VolumetricLighting::DrawSettings()
{
if (ImGui::TreeNodeEx("Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::Checkbox("Enable Volumetric Lighting in Exteriors", &settings.ExteriorEnabled))
SetupVL();
if (ImGui::Checkbox("Enable Volumetric Lighting in Exteriors", &settings.ExteriorEnabled))
SetupVL();

if (settings.ExteriorEnabled)
DrawVolumetricLightingSettings(settings.ExteriorQuality, settings.ExteriorCustomSize, false, !inInterior);
if (settings.ExteriorEnabled)
DrawVolumetricLightingSettings(settings.ExteriorQuality, settings.ExteriorCustomSize, false, !inInterior);

if (ImGui::Checkbox("Enable Volumetric Lighting in Interiors", &settings.InteriorEnabled))
SetupVL();
if (ImGui::Checkbox("Enable Volumetric Lighting in Interiors", &settings.InteriorEnabled))
SetupVL();

if (settings.InteriorEnabled)
DrawVolumetricLightingSettings(settings.InteriorQuality, settings.InteriorCustomSize, true, inInterior);
if (settings.InteriorEnabled)
DrawVolumetricLightingSettings(settings.InteriorQuality, settings.InteriorCustomSize, true, inInterior);

ImGui::Spacing();
ImGui::TreePop();
}
ImGui::Spacing();
}

void VolumetricLighting::DrawVolumetricLightingSettings(int32_t& quality, TextureSize& customSize, const bool isInterior, const bool inLocationType)
Expand Down
7 changes: 6 additions & 1 deletion src/Menu/SettingsTabRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void SettingsTabRenderer::RenderShadersTab()
shaderCache->SetDiskCache(useDiskCache);
}
if (auto _tt = Util::HoverTooltipWrapper()) {
ImGui::Text("Disabling this stops shaders from being loaded from disk, as well as stops shaders from being saved to it.");
ImGui::Text("Disables loading shaders from disk and prevents saving compiled shaders to disk cache.");
}

bool useAsync = shaderCache->IsAsync();
Expand All @@ -49,6 +49,11 @@ void SettingsTabRenderer::RenderShadersTab()
ImGui::Text("Skips a shader being replaced if it hasn't been compiled yet. Also makes compilation blazingly fast!");
}

if (shaderCache->GetTotalTasks() > 0) {
ImGui::Text("Last shader cache build duration: %s",
shaderCache->GetShaderStatsString(true, true).c_str());
}

ImGui::EndTabItem();
}
}
Expand Down
22 changes: 15 additions & 7 deletions src/ShaderCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2057,9 +2057,9 @@ namespace SIE
return ShaderCompilationTask::Status::Pending;
}

std::string ShaderCache::GetShaderStatsString(bool a_timeOnly)
std::string ShaderCache::GetShaderStatsString(bool a_timeOnly, bool a_elapsedOnly)
{
return compilationSet.GetStatsString(a_timeOnly);
return compilationSet.GetStatsString(a_timeOnly, a_elapsedOnly);
}

inline bool ShaderCache::IsShaderSourceAvailable(const RE::BSShader& shader)
Expand Down Expand Up @@ -2614,14 +2614,22 @@ namespace SIE
return std::max(remaining / rate, 0.0);
}

std::string CompilationSet::GetStatsString(bool a_timeOnly)
std::string CompilationSet::GetStatsString(bool a_timeOnly, bool a_elapsedOnly)
{
double totalMs = static_cast<double>(totalTime.QuadPart) * 1000.0 / frequency.QuadPart;

if (a_timeOnly)
return fmt::format("{}/{}",
GetHumanTime(totalMs),
GetHumanTime(GetEta() + totalMs));
if (a_timeOnly) {
if (a_elapsedOnly) {
// Only elapsed
return GetHumanTime(totalMs);
} else {
// Elapsed + estimated
return fmt::format("{}/{}",
GetHumanTime(totalMs),
GetHumanTime(GetEta() + totalMs));
}
}

return fmt::format("{}/{} (successful/total)\tfailed: {}\tcachehits: {}\nElapsed/Estimated Time: {}/{}",
(std::uint64_t)completedTasks,
(std::uint64_t)totalTasks,
Expand Down
4 changes: 2 additions & 2 deletions src/ShaderCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ namespace SIE
void Clear();
std::string GetHumanTime(double a_totalMs);
double GetEta();
std::string GetStatsString(bool a_timeOnly = false);
std::string GetStatsString(bool a_timeOnly = false, bool a_elapsedOnly = false);
std::atomic<uint64_t> completedTasks = 0;
std::atomic<uint64_t> totalTasks = 0;
std::atomic<uint64_t> failedTasks = 0;
Expand Down Expand Up @@ -396,7 +396,7 @@ namespace SIE
ID3DBlob* GetCompletedShader(const SIE::ShaderCompilationTask& a_task);
ID3DBlob* GetCompletedShader(ShaderClass shaderClass, const RE::BSShader& shader, uint32_t descriptor);
ShaderCompilationTask::Status GetShaderStatus(const std::string& a_key);
std::string GetShaderStatsString(bool a_timeOnly = false);
std::string GetShaderStatsString(bool a_timeOnly = false, bool a_elapsedOnly = false);

RE::BSGraphics::VertexShader* GetVertexShader(const RE::BSShader& shader, uint32_t descriptor);
RE::BSGraphics::PixelShader* GetPixelShader(const RE::BSShader& shader,
Expand Down
Loading
Loading