diff --git a/CMakeLists.txt b/CMakeLists.txt index b58a6603d..d87091960 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.21) project( CommunityShaders - VERSION 0.8.0 + VERSION 0.8.7 LANGUAGES CXX ) diff --git a/features/Dynamic Cubemaps - Metals/textures/cubemaps/shinybright_e.dds b/features/Dynamic Cubemaps - Metals/textures/cubemaps/shinybright_e.dds deleted file mode 100644 index f514b52a1..000000000 Binary files a/features/Dynamic Cubemaps - Metals/textures/cubemaps/shinybright_e.dds and /dev/null differ diff --git a/features/Dynamic Cubemaps - Metals/textures/cubemaps/shinycontrast_e.dds b/features/Dynamic Cubemaps - Metals/textures/cubemaps/shinycontrast_e.dds deleted file mode 100644 index 33b486151..000000000 Binary files a/features/Dynamic Cubemaps - Metals/textures/cubemaps/shinycontrast_e.dds and /dev/null differ diff --git a/features/Dynamic Cubemaps - Metals/textures/shinyglass_e.dds b/features/Dynamic Cubemaps - Metals/textures/shinyglass_e.dds deleted file mode 100644 index 6767de9e1..000000000 Binary files a/features/Dynamic Cubemaps - Metals/textures/shinyglass_e.dds and /dev/null differ diff --git a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli index 4f42ca54a..628c60e2b 100644 --- a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli +++ b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli @@ -33,10 +33,7 @@ float3 GetDynamicCubemap(float2 uv, float3 N, float3 VN, float3 V, float roughne float3 specularIrradiance = specularTexture.SampleLevel(SampColorSampler, R, level); specularIrradiance = sRGB2Lin(specularIrradiance); - - diffuseColor = sRGB2Lin(diffuseColor) * 0.5; - - specularIrradiance = lerp(specularIrradiance, diffuseColor, saturate(distance / 1000.0)); + diffuseColor = sRGB2Lin(diffuseColor); float2 specularBRDF = EnvBRDFApprox(roughness, NoV); @@ -53,16 +50,13 @@ float3 GetDynamicCubemap(float2 uv, float3 N, float3 VN, float3 V, float roughne return specularIrradiance * ((F0 + S) * specularBRDF.x + specularBRDF.y); } -float3 GetDynamicCubemapFresnel(float2 uv, float3 N, float3 VN, float3 V, float roughness, float level, float distance) +float3 GetDynamicCubemapFresnel(float2 uv, float3 N, float3 VN, float3 V, float roughness, float level, float3 diffuseColor, float distance) { float NoV = saturate(dot(N, V)); float2 specularBRDF = EnvBRDFApprox(roughness, NoV); if (specularBRDF.y > 0.001) { float3 R = reflect(-V, N); - float3 specularIrradiance = specularTexture.SampleLevel(SampColorSampler, R, level); - specularIrradiance = sRGB2Lin(specularIrradiance); - specularIrradiance = specularIrradiance * (1.0 - saturate(distance / 1000.0)); // Horizon specular occlusion // https://marmosetco.tumblr.com/post/81245981087 diff --git a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/InferCubemapCS.hlsl b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/InferCubemapCS.hlsl index df90f5acc..c3786b002 100644 --- a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/InferCubemapCS.hlsl +++ b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/InferCubemapCS.hlsl @@ -102,7 +102,7 @@ float3 Lin2sRGB(float3 color) #if defined(REFLECTIONS) color.rgb = lerp(color.rgb, sRGB2Lin(ReflectionsTexture.SampleLevel(LinearSampler, uv, 0)), saturate(mipLevel * (1.0 / 10.0))); #else - color.rgb = lerp(color.rgb, color.rgb * sRGB2Lin(DefaultCubemap.SampleLevel(LinearSampler, uv, 0)) * 10.0, saturate(mipLevel * (1.0 / 10.0))); + color.rgb = lerp(color.rgb, color.rgb * sRGB2Lin(DefaultCubemap.SampleLevel(LinearSampler, uv, 0).x) * 10.0, saturate(mipLevel * (1.0 / 10.0))); #endif color.rgb = Lin2sRGB(color.rgb); diff --git a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/defaultcubemap.dds b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/defaultcubemap.dds index cdc8a3f88..db8cfc794 100644 Binary files a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/defaultcubemap.dds and b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/defaultcubemap.dds differ diff --git a/features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini b/features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini index 19f01444d..519c4a9ce 100644 --- a/features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini +++ b/features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini @@ -1,2 +1,2 @@ [Info] -Version = 1-0-0 \ No newline at end of file +Version = 1-0-3 \ No newline at end of file diff --git a/features/Wetness Effects/Shaders/Features/WetnessEffects.ini b/features/Wetness Effects/Shaders/Features/WetnessEffects.ini index 19f01444d..735cfd23a 100644 --- a/features/Wetness Effects/Shaders/Features/WetnessEffects.ini +++ b/features/Wetness Effects/Shaders/Features/WetnessEffects.ini @@ -1,2 +1,2 @@ [Info] -Version = 1-0-0 \ No newline at end of file +Version = 1-0-1 \ No newline at end of file diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 2b632e811..32908aee3 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -1235,6 +1235,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace complexMaterial = complexMaterial && complexMaterialColor.y > (4.0 / 255.0) && (complexMaterialColor.y < (1.0 - (4.0 / 255.0))); shininess = lerp(shininess, shininess * complexMaterialColor.y, complexMaterial); float3 complexSpecular = lerp(1.0, lerp(1.0, baseColor.xyz, complexMaterialColor.z), complexMaterial); + baseColor.xyz = lerp(baseColor.xyz, lerp(baseColor.xyz, 0.0, complexMaterialColor.z), complexMaterial); # endif // defined (CPM_AVAILABLE) && defined(ENVMAP) # if defined(FACEGEN) @@ -1372,7 +1373,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif // LOD_LAND_BLEND # if defined(SNOW) - useSnowSpecular = landSnowMask > 0; + useSnowSpecular = landSnowMask != 0.0; # endif // SNOW # endif // LANDSCAPE @@ -1444,15 +1445,15 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace normal.xyz = texProjTmp2.xxx * (finalProjNormal - normal.xyz) + normal.xyz; baseColor.xyz = texProjTmp2.xxx * (projDiffuse * ProjectedUVParams2.xyz - baseColor.xyz) + baseColor.xyz; - useSnowDecalSpecular = true; # if defined(SNOW) + useSnowDecalSpecular = true; psout.SnowParameters.y = GetSnowParameterY(texProjTmp2, baseColor.w); # endif // SNOW } else { if (texProjTmp > 0) { baseColor.xyz = ProjectedUVParams2.xyz; - useSnowDecalSpecular = true; # if defined(SNOW) + useSnowDecalSpecular = true; psout.SnowParameters.y = GetSnowParameterY(texProjTmp, baseColor.w); # endif // SNOW } else { @@ -1704,7 +1705,6 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace if (shaderDescriptors[0].PixelShaderDescriptor & _DefShadow) { if (lightIndex < numShadowLights) { lightColor *= shadowColor[ShadowLightMaskSelect[lightIndex]]; - ; } } @@ -1821,7 +1821,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif # if defined(RIM_LIGHTING) - lightDiffuseColor += nsLightColor * GetRimLightMultiplier(normalizedLightDirection, viewDirection, worldSpaceNormal.xyz) * rimSoftLightColor.xyz; + lightDiffuseColor += nsLightColor * GetRimLightMultiplier(normalizedLightDirection, worldSpaceViewDirection, worldSpaceNormal.xyz) * rimSoftLightColor.xyz; # endif # if defined(BACK_LIGHTING) @@ -1829,7 +1829,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif # if defined(SPECULAR) || (defined(SPARKLE) && !defined(SNOW)) - lightsSpecularColor += GetLightSpecularInput(input, normalizedLightDirection, viewDirection, worldSpaceNormal.xyz, lightColor, shininess, uv); + lightsSpecularColor += GetLightSpecularInput(input, normalizedLightDirection, worldSpaceViewDirection, worldSpaceNormal.xyz, lightColor, shininess, uv); # endif lightsDiffuseColor += lightDiffuseColor; @@ -1914,6 +1914,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace if (dynamicCubemap) { # if defined(CPM_AVAILABLE) envRoughness = lerp(envRoughness, 1.0 - complexMaterialColor.y, (float)complexMaterial); + envRoughness *= envRoughness; F0 = lerp(F0, sRGB2Lin(complexSpecular), (float)complexMaterial); # endif @@ -1965,13 +1966,19 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif // MULTI_LAYER_PARALLAX # if defined(SPECULAR) +# if defined(CPM_AVAILABLE) && defined(ENVMAP) + specularColor = (specularColor * glossiness * MaterialData.yyy) * lerp(SpecularColor.xyz, complexSpecular, complexMaterial); +# else specularColor = (specularColor * glossiness * MaterialData.yyy) * SpecularColor.xyz; +# endif # elif defined(SPARKLE) specularColor *= glossiness; # endif // SPECULAR +# if defined(SNOW) if (useSnowSpecular) specularColor = 0; +# endif # if (defined(ENVMAP) || defined(MULTI_LAYER_PARALLAX) || defined(EYE)) # if defined(DYNAMIC_CUBEMAPS) @@ -2003,11 +2010,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace color.xyz = tmpColor.xyz + ColourOutputClamp.xxx; color.xyz = min(vertexColor.xyz, color.xyz); -# if defined(CPM_AVAILABLE) && defined(ENVMAP) - color.xyz += specularColor * complexSpecular; -# else color.xyz += specularColor; -# endif // defined (CPM_AVAILABLE) && defined(ENVMAP) color.xyz = sRGB2Lin(color.xyz); @@ -2015,18 +2018,6 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace color.xyz += wetnessSpecular * wetnessGlossinessSpecular; # endif -# if defined(DYNAMIC_CUBEMAPS) -# if defined(EYE) - color.xyz += GetDynamicCubemapFresnel(screenUV, worldSpaceNormal, worldSpaceVertexNormal, worldSpaceViewDirection, 0.5, 2, viewPosition.z) * input.Color.xyz * envMask; -# elif defined(ENVMAP) || defined(MULTI_LAYER_PARALLAX) - color.xyz += GetDynamicCubemapFresnel(screenUV, worldSpaceNormal, worldSpaceVertexNormal, worldSpaceViewDirection, 0.5 - (saturate(envMask) * 0.5), 2 - saturate(envMask), viewPosition.z) * (1.0 - ((float)dynamicCubemap * saturate(envMask))) * input.Color.xyz; -# elif defined(HAIR) - color.xyz += GetDynamicCubemapFresnel(screenUV, worldSpaceNormal, worldSpaceVertexNormal, worldSpaceViewDirection, 0.5, 2, viewPosition.z); -# else - color.xyz += GetDynamicCubemapFresnel(screenUV, worldSpaceNormal, worldSpaceVertexNormal, worldSpaceViewDirection, 0.5, 2, viewPosition.z) * input.Color.xyz; -# endif -# endif - # if defined(EYE) color.xyz *= saturate(normalize(input.EyeNormal2.xyz).y); // Occlusion # endif @@ -2131,7 +2122,6 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace tmp = saturate(tmp1 * tmp2); tmp *= tmp * (3 + -2 * tmp); psout.ScreenSpaceNormals.w = tmp * SSRParams.w; - psout.ScreenSpaceNormals.w = 0.0; # if defined(WATER_BLENDING) if (perPassWaterBlending[0].EnableWaterBlendingSSR) { diff --git a/src/Feature.cpp b/src/Feature.cpp index 600f89ce6..503152e52 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -15,6 +15,7 @@ #include "Features/WaterCaustics.h" #include "Features/WaterParallax.h" #include "Features/WetnessEffects.h" +#include "State.h" void Feature::Load(json&) { @@ -118,18 +119,9 @@ const std::vector& Feature::GetFeatureList() SubsurfaceScattering::GetSingleton() }; - static std::vector featuresVR = { - DynamicCubemaps::GetSingleton(), - GrassLighting::GetSingleton(), - GrassCollision::GetSingleton(), - ScreenSpaceShadows::GetSingleton(), - ExtendedMaterials::GetSingleton(), - WetnessEffects::GetSingleton(), - LightLimitFix::GetSingleton(), - TerrainBlending::GetSingleton(), - WaterCaustics::GetSingleton(), - SubsurfaceScattering::GetSingleton() - }; - - return REL::Module::IsVR() ? featuresVR : features; + static std::vector featuresVR(features); + std::erase_if(featuresVR, [](Feature* a) { + return !a->SupportsVR(); + }); + return (REL::Module::IsVR() && !State::GetSingleton()->IsDeveloperMode()) ? featuresVR : features; } \ No newline at end of file diff --git a/src/Feature.h b/src/Feature.h index 59ddb0c3e..681a2e1b8 100644 --- a/src/Feature.h +++ b/src/Feature.h @@ -11,6 +11,12 @@ struct Feature virtual std::string_view GetShaderDefineName() { return ""; } virtual bool HasShaderDefine(RE::BSShader::Type) { return false; } + /** + * Whether the feature supports VR. + * + * \return true if VR supported; else false + */ + virtual bool SupportsVR() { return false; } virtual void SetupResources() = 0; virtual void Reset() = 0; @@ -32,6 +38,5 @@ struct Feature virtual void WriteDiskCacheInfo(CSimpleIniA& a_ini); virtual void ClearShaderCache() {} - // Cat: add all the features in here static const std::vector& GetFeatureList(); }; \ No newline at end of file diff --git a/src/Features/DynamicCubemaps.cpp b/src/Features/DynamicCubemaps.cpp index 9c75ebeeb..02542744c 100644 --- a/src/Features/DynamicCubemaps.cpp +++ b/src/Features/DynamicCubemaps.cpp @@ -480,10 +480,6 @@ void DynamicCubemaps::SetupResources() envCaptureRawTexture->CreateSRV(srvDesc); envCaptureRawTexture->CreateUAV(uavDesc); - envInferredTexture = new Texture2D(texDesc); - envInferredTexture->CreateSRV(srvDesc); - envInferredTexture->CreateUAV(uavDesc); - envCapturePositionTexture = new Texture2D(texDesc); envCapturePositionTexture->CreateSRV(srvDesc); envCapturePositionTexture->CreateUAV(uavDesc); @@ -496,6 +492,10 @@ void DynamicCubemaps::SetupResources() envTexture->CreateSRV(srvDesc); envTexture->CreateUAV(uavDesc); + envInferredTexture = new Texture2D(texDesc); + envInferredTexture->CreateSRV(srvDesc); + envInferredTexture->CreateUAV(uavDesc); + updateCubemapCB = new ConstantBuffer(ConstantBufferDesc()); } diff --git a/src/Features/DynamicCubemaps.h b/src/Features/DynamicCubemaps.h index 2e24888cf..3bed15638 100644 --- a/src/Features/DynamicCubemaps.h +++ b/src/Features/DynamicCubemaps.h @@ -127,4 +127,6 @@ struct DynamicCubemaps : Feature void UpdateCubemapCapture(); virtual void DrawDeferred(); + + bool SupportsVR() override { return true; }; }; diff --git a/src/Features/ExtendedMaterials.h b/src/Features/ExtendedMaterials.h index 6326dd1d9..eea9b55bc 100644 --- a/src/Features/ExtendedMaterials.h +++ b/src/Features/ExtendedMaterials.h @@ -60,4 +60,6 @@ struct ExtendedMaterials : Feature virtual void Save(json& o_json); virtual void RestoreDefaultSettings(); + + bool SupportsVR() override { return true; }; }; diff --git a/src/Features/GrassCollision.h b/src/Features/GrassCollision.h index 6b353ad6e..8ea60bad7 100644 --- a/src/Features/GrassCollision.h +++ b/src/Features/GrassCollision.h @@ -66,4 +66,6 @@ struct GrassCollision : Feature virtual void Save(json& o_json); virtual void RestoreDefaultSettings(); + + bool SupportsVR() override { return true; }; }; diff --git a/src/Features/GrassLighting.h b/src/Features/GrassLighting.h index da3fcc582..7d578110f 100644 --- a/src/Features/GrassLighting.h +++ b/src/Features/GrassLighting.h @@ -47,4 +47,6 @@ struct GrassLighting : Feature virtual void Save(json& o_json); virtual void RestoreDefaultSettings(); + + bool SupportsVR() override { return true; }; }; diff --git a/src/Features/LightLimitFix.cpp b/src/Features/LightLimitFix.cpp index ca9196a6a..ca974e760 100644 --- a/src/Features/LightLimitFix.cpp +++ b/src/Features/LightLimitFix.cpp @@ -23,7 +23,11 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( EnableParticleLightsDetection, ParticleLightsSaturation, EnableParticleLightsOptimization, - ParticleLightsOptimisationClusterRadius) + ParticleLightsOptimisationClusterRadius, + ParticleBrightness, + ParticleRadius, + BillboardBrightness, + BillboardRadius) void LightLimitFix::DrawSettings() { @@ -54,11 +58,15 @@ void LightLimitFix::DrawSettings() ImGui::Spacing(); ImGui::Spacing(); - ImGui::TextWrapped("Particle Lights Color"); + ImGui::TextWrapped("Particle Lights Customisation"); ImGui::SliderFloat("Saturation", &settings.ParticleLightsSaturation, 1.0, 2.0, "%.2f"); if (auto _tt = Util::HoverTooltipWrapper()) { ImGui::Text("Particle light saturation."); } + ImGui::SliderFloat("Particle Brightness", &settings.ParticleBrightness, 0.0, 10.0, "%.2f"); + ImGui::SliderFloat("Particle Radius", &settings.ParticleRadius, 0.0, 10.0, "%.2f"); + ImGui::SliderFloat("Billboard Brightness", &settings.BillboardBrightness, 0.0, 10.0, "%.2f"); + ImGui::SliderFloat("Billboard Radius", &settings.BillboardRadius, 0.0, 10.0, "%.2f"); ImGui::Spacing(); ImGui::Spacing(); @@ -550,8 +558,6 @@ bool LightLimitFix::AddParticleLight(RE::BSRenderPass* a_pass, LightLimitFix::Co color.blue *= emittance->blue; } - float radius = 0; - if (auto rendererData = a_pass->geometry->GetGeometryRuntimeData().rendererData) { if (auto triShape = a_pass->geometry->AsTriShape()) { uint32_t vertexSize = rendererData->vertexDesc.GetSize(); @@ -585,15 +591,6 @@ bool LightLimitFix::AddParticleLight(RE::BSRenderPass* a_pass, LightLimitFix::Co color.alpha *= vertexColor->data[3] / 255.f; } } - - uint32_t offset = rendererData->vertexDesc.GetAttributeOffset(RE::BSGraphics::Vertex::Attribute::VA_POSITION); - for (int v = 0; v < triShape->GetTrishapeRuntimeData().vertexCount; v++) { - if (VertexPosition* vertex = reinterpret_cast(&rendererData->rawVertexData[vertexSize * v + offset])) { - RE::NiPoint3 position{ (float)vertex->data[0], (float)vertex->data[1], (float)vertex->data[2] }; - radius = std::max(radius, position.Length()); - } - } - radius /= 255.f; } } @@ -608,7 +605,7 @@ bool LightLimitFix::AddParticleLight(RE::BSRenderPass* a_pass, LightLimitFix::Co color.blue *= config->colorMult.blue; } - queuedParticleLights.insert({ a_pass->geometry, { color, radius, *config } }); + queuedParticleLights.insert({ a_pass->geometry, { color, *config } }); return true; } @@ -649,8 +646,8 @@ float LightLimitFix::CalculateLightDistance(float3 a_lightPosition, float a_radi void LightLimitFix::AddCachedParticleLights(eastl::vector& lightsData, LightLimitFix::LightData& light, ParticleLights::Config* a_config, RE::BSGeometry* a_geometry, double a_timer) { - static float& lightFadeStart = (*(float*)RELOCATION_ID(527668, 414582).address()); - static float& lightFadeEnd = (*(float*)RELOCATION_ID(527669, 414583).address()); + static float& lightFadeStart = (*(float*)REL::RelocationID(527668, 414582).address()); + static float& lightFadeEnd = (*(float*)REL::RelocationID(527669, 414583).address()); float distance = CalculateLightDistance(light.positionWS[0].data, light.radius); @@ -746,6 +743,7 @@ void LightLimitFix::UpdateLights() viewMatrixCached[eyeIndex] = eyeCount == 1 ? state->GetRuntimeData().cameraData.getEye(eyeIndex).viewMat : state->GetVRRuntimeData().cameraData.getEye(eyeIndex).viewMat; + viewMatrixCached[eyeIndex].Invert(viewMatrixInverseCached[eyeIndex]); } RE::NiLight* refLight = nullptr; @@ -880,9 +878,9 @@ void LightLimitFix::UpdateLights() color.x = particleLight.second.color.red * particleData->GetParticlesRuntimeData().color[p].red; color.y = particleLight.second.color.green * particleData->GetParticlesRuntimeData().color[p].green; color.z = particleLight.second.color.blue * particleData->GetParticlesRuntimeData().color[p].blue; - clusteredLight.color += Saturation(color, settings.ParticleLightsSaturation) * alpha; + clusteredLight.color += Saturation(color, settings.ParticleLightsSaturation) * alpha * settings.ParticleBrightness; - clusteredLight.radius += radius * particleLight.second.config.radiusMult; + clusteredLight.radius += radius * settings.ParticleRadius * particleLight.second.config.radiusMult; clusteredLight.positionWS[0].data.x += positionWS.x; clusteredLight.positionWS[0].data.y += positionWS.y; clusteredLight.positionWS[0].data.z += positionWS.z; @@ -900,9 +898,8 @@ void LightLimitFix::UpdateLights() light.color = Saturation(light.color, settings.ParticleLightsSaturation); - light.color *= particleLight.second.color.alpha; - - light.radius = particleLight.second.radius * 70.0f * 0.5f; + light.color *= particleLight.second.color.alpha * settings.BillboardBrightness; + light.radius = particleLight.first->worldBound.radius * settings.BillboardRadius * particleLight.second.config.radiusMult; auto position = particleLight.first->world.translate; diff --git a/src/Features/LightLimitFix.h b/src/Features/LightLimitFix.h index 2548bc490..e69e4e8f3 100644 --- a/src/Features/LightLimitFix.h +++ b/src/Features/LightLimitFix.h @@ -123,7 +123,6 @@ struct LightLimitFix : Feature struct ParticleLightInfo { RE::NiColorA color; - float radius; ParticleLights::Config& config; }; @@ -132,6 +131,7 @@ struct LightLimitFix : Feature RE::NiPoint3 eyePositionCached[2]{}; Matrix viewMatrixCached[2]{}; + Matrix viewMatrixInverseCached[2]{}; PerPass perPassData{}; @@ -169,6 +169,10 @@ struct LightLimitFix : Feature bool EnableParticleLightsCulling = true; bool EnableParticleLightsDetection = true; float ParticleLightsSaturation = 1.0f; + float ParticleBrightness = 1.0f; + float ParticleRadius = 1.0f; + float BillboardBrightness = 1.0f; + float BillboardRadius = 1.0f; bool EnableParticleLightsOptimization = true; uint ParticleLightsOptimisationClusterRadius = 32; }; @@ -312,6 +316,8 @@ struct LightLimitFix : Feature stl::write_thunk_call(REL::RelocationID(100565, 107300).address() + REL::Relocate(0x523, 0xB0E, 0x5fe)); } }; + + bool SupportsVR() override { return true; }; }; template <> diff --git a/src/Features/ScreenSpaceShadows.h b/src/Features/ScreenSpaceShadows.h index 2ed6d311d..d04146d69 100644 --- a/src/Features/ScreenSpaceShadows.h +++ b/src/Features/ScreenSpaceShadows.h @@ -88,4 +88,6 @@ struct ScreenSpaceShadows : Feature virtual void Save(json& o_json); virtual void RestoreDefaultSettings(); + + bool SupportsVR() override { return true; }; }; diff --git a/src/Features/SubsurfaceScattering.h b/src/Features/SubsurfaceScattering.h index 0ff7ad5ec..dac25629d 100644 --- a/src/Features/SubsurfaceScattering.h +++ b/src/Features/SubsurfaceScattering.h @@ -179,4 +179,6 @@ struct SubsurfaceScattering : Feature logger::info("[SSS] Installed hooks"); } }; + + bool SupportsVR() override { return true; }; }; diff --git a/src/Features/TerrainBlending.h b/src/Features/TerrainBlending.h index 04cf9fcdc..072533e35 100644 --- a/src/Features/TerrainBlending.h +++ b/src/Features/TerrainBlending.h @@ -74,4 +74,6 @@ struct TerrainBlending : Feature logger::info("[Terrain Blending] Installed hooks"); } }; + + bool SupportsVR() override { return true; }; }; diff --git a/src/Features/WaterCaustics.h b/src/Features/WaterCaustics.h index 345bf1a77..698e5d26e 100644 --- a/src/Features/WaterCaustics.h +++ b/src/Features/WaterCaustics.h @@ -30,4 +30,6 @@ struct WaterCaustics : Feature virtual void Save(json& o_json); virtual void RestoreDefaultSettings(); + + bool SupportsVR() override { return true; }; }; diff --git a/src/Features/WetnessEffects.cpp b/src/Features/WetnessEffects.cpp index 6024c8d3a..086f98e1c 100644 --- a/src/Features/WetnessEffects.cpp +++ b/src/Features/WetnessEffects.cpp @@ -324,7 +324,7 @@ void WetnessEffects::Draw(const RE::BSShader* shader, const uint32_t) lastWeatherID = lastWeather->GetFormID(); CalculateWetness(lastWeather, sky, seconds, lastWeatherWetnessDepth, lastWeatherPuddleDepth); // If it was raining, wait to transition until precipitation ends, otherwise use the current weather's fade in - if (lastWeather->data.flags.any(RE::TESWeather::WeatherDataFlag::kRainy)) { + if (lastWeather->precipitationData && lastWeather->data.flags.any(RE::TESWeather::WeatherDataFlag::kRainy)) { float rainDensity = lastWeather->precipitationData->data[static_cast(RE::BGSShaderParticleGeometryData::DataID::kParticleDensity)].f; float rainGravity = lastWeather->precipitationData->data[static_cast(RE::BGSShaderParticleGeometryData::DataID::kGravityVelocity)].f; lastWeatherRaining = std::clamp(((rainDensity * rainGravity) / AVERAGE_RAIN_VOLUME), MIN_RAINDROP_CHANCE_MULTIPLIER, MAX_RAINDROP_CHANCE_MULTIPLIER); diff --git a/src/Features/WetnessEffects.h b/src/Features/WetnessEffects.h index da37d85bd..a47b38021 100644 --- a/src/Features/WetnessEffects.h +++ b/src/Features/WetnessEffects.h @@ -114,4 +114,6 @@ struct WetnessEffects : Feature stl::write_vfunc<0x6, BSParticleShader_SetupGeometry>(RE::VTABLE_BSParticleShader[0]); } }; + + bool SupportsVR() override { return true; }; }; diff --git a/src/Menu.cpp b/src/Menu.cpp index 2ed4aa669..b06100afa 100644 --- a/src/Menu.cpp +++ b/src/Menu.cpp @@ -361,6 +361,17 @@ void Menu::DrawSettings() "Enabling will save current settings as TEST config. " "This has no impact if no settings are changed. "); } + bool useFileWatcher = shaderCache.UseFileWatcher(); + ImGui::TableNextColumn(); + if (ImGui::Checkbox("Enable File Watcher", &useFileWatcher)) { + shaderCache.SetFileWatcher(useFileWatcher); + } + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text( + "Automatically recompile shaders on file change. " + "Intended for developing."); + } + if (ImGui::Button("Dump Ini Settings", { -1, 0 })) { Util::DumpSettingsOptions(); } @@ -419,19 +430,23 @@ void Menu::DrawSettings() static size_t selectedFeature = SIZE_T_MAX; auto& featureList = Feature::GetFeatureList(); + auto sortedList{ featureList }; // need a copy so the load order is not lost + std::sort(sortedList.begin(), sortedList.end(), [](Feature* a, Feature* b) { + return a->GetName() < b->GetName(); + }); ImGui::TableNextColumn(); if (ImGui::BeginListBox("##FeatureList", { -FLT_MIN, -FLT_MIN })) { - for (size_t i = 0; i < featureList.size(); i++) - if (featureList[i]->loaded) { - if (ImGui::Selectable(fmt::format("{} ", featureList[i]->GetName()).c_str(), selectedFeature == i, ImGuiSelectableFlags_SpanAllColumns)) + for (size_t i = 0; i < sortedList.size(); i++) + if (sortedList[i]->loaded) { + if (ImGui::Selectable(fmt::format("{} ", sortedList[i]->GetName()).c_str(), selectedFeature == i, ImGuiSelectableFlags_SpanAllColumns)) selectedFeature = i; ImGui::SameLine(); - ImGui::TextDisabled(fmt::format("({})", featureList[i]->version).c_str()); - } else if (!featureList[i]->version.empty()) { - ImGui::TextDisabled(fmt::format("{} ({})", featureList[i]->GetName(), featureList[i]->version).c_str()); + ImGui::TextDisabled(fmt::format("({})", sortedList[i]->version).c_str()); + } else if (!sortedList[i]->version.empty()) { + ImGui::TextDisabled(fmt::format("{} ({})", sortedList[i]->GetName(), sortedList[i]->version).c_str()); if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text(featureList[i]->failedLoadedMessage.c_str()); + ImGui::Text(sortedList[i]->failedLoadedMessage.c_str()); } } ImGui::EndListBox(); @@ -439,10 +454,10 @@ void Menu::DrawSettings() ImGui::TableNextColumn(); - bool shownFeature = selectedFeature < featureList.size(); + bool shownFeature = selectedFeature < sortedList.size(); if (shownFeature) { if (ImGui::Button("Restore Defaults", { -1, 0 })) { - featureList[selectedFeature]->RestoreDefaultSettings(); + sortedList[selectedFeature]->RestoreDefaultSettings(); } if (auto _tt = Util::HoverTooltipWrapper()) { ImGui::Text( @@ -453,7 +468,7 @@ void Menu::DrawSettings() if (ImGui::BeginChild("##FeatureConfigFrame", { 0, 0 }, true)) { if (shownFeature) - featureList[selectedFeature]->DrawSettings(); + sortedList[selectedFeature]->DrawSettings(); else ImGui::TextDisabled("Please select a feature on the left."); } diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index 7edbfd4e8..14b5a2539 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -987,7 +987,7 @@ namespace SIE if (!shaderBlob && useDiskCache && std::filesystem::exists(diskPath)) { shaderBlob = nullptr; // check build time of cache - auto diskCacheTime = std::chrono::clock_cast(std::filesystem::last_write_time(diskPath)); + auto diskCacheTime = cache.UseFileWatcher() ? std::chrono::clock_cast(std::filesystem::last_write_time(diskPath)) : system_clock::now(); if (cache.ShaderModifiedSince(shader.fxpFilename, diskCacheTime)) { logger::debug("Diskcached shader {} older than {}", SIE::SShaderCache::GetShaderString(shaderClass, shader, descriptor, true), std::format("{:%Y%m%d%H%M}", diskCacheTime)); } else if (FAILED(D3DReadFileToBlob(diskPath.c_str(), &shaderBlob))) { @@ -1312,24 +1312,29 @@ namespace SIE ShaderCache::~ShaderCache() { Clear(); - fileWatcher->removeWatch(watchID); + StopFileWatcher(); } void ShaderCache::Clear() { - for (auto& shaders : vertexShaders) { - for (auto& [id, shader] : shaders) { - shader->shader->Release(); + std::lock_guard lockGuardV(vertexShadersMutex); + { + for (auto& shaders : vertexShaders) { + for (auto& [id, shader] : shaders) { + shader->shader->Release(); + } + shaders.clear(); } - shaders.clear(); } - for (auto& shaders : pixelShaders) { - for (auto& [id, shader] : shaders) { - shader->shader->Release(); + std::lock_guard lockGuardP(pixelShadersMutex); + { + for (auto& shaders : pixelShaders) { + for (auto& [id, shader] : shaders) { + shader->shader->Release(); + } + shaders.clear(); } - shaders.clear(); } - compilationSet.Clear(); std::unique_lock lock{ mapMutex }; shaderMap.clear(); @@ -1368,6 +1373,7 @@ namespace SIE ID3DBlob* ShaderCache::GetCompletedShader(const std::string a_key) { std::string type = SIE::SShaderCache::GetTypeFromShaderString(a_key); + UpdateShaderModifiedTime(a_key); std::scoped_lock lock{ mapMutex }; if (!shaderMap.empty() && shaderMap.contains(a_key)) { if (ShaderModifiedSince(type, shaderMap.at(a_key).compileTime)) { @@ -1504,33 +1510,82 @@ namespace SIE compilationPool.push_task(&ShaderCache::ManageCompilationSet, this, ssource.get_token()); } + bool ShaderCache::UseFileWatcher() const + { + return useFileWatcher; + } + + void ShaderCache::SetFileWatcher(bool value) + { + auto oldValue = useFileWatcher; + useFileWatcher = value; + if (useFileWatcher && !oldValue) + StartFileWatcher(); + else if (!useFileWatcher && oldValue) + StopFileWatcher(); + } + void ShaderCache::StartFileWatcher() { - fileWatcher = new efsw::FileWatcher(); - listener = new UpdateListener(); - // Add a folder to watch, and get the efsw::WatchID - // Reporting the files and directories changes to the instance of the listener - watchID = fileWatcher->addWatch("Data\\Shaders", listener, true); - // Start watching asynchronously the directories - fileWatcher->watch(); - std::string pathStr = ""; - for (auto path : fileWatcher->directories()) { - pathStr += std::format("{}; ", path); + logger::info("Starting FileWatcher"); + if (!fileWatcher) { + fileWatcher = new efsw::FileWatcher(); + listener = new UpdateListener(); + // Add a folder to watch, and get the efsw::WatchID + // Reporting the files and directories changes to the instance of the listener + watchID = fileWatcher->addWatch("Data\\Shaders", listener, true); + // Start watching asynchronously the directories + fileWatcher->watch(); + std::string pathStr = ""; + for (auto path : fileWatcher->directories()) { + pathStr += std::format("{}; ", path); + } + logger::debug("ShaderCache watching for changes in {}", pathStr); + compilationPool.push_task(&SIE::UpdateListener::processQueue, listener); + } else { + logger::debug("ShaderCache already enabled"); } - logger::debug("ShaderCache watching for changes in {}", pathStr); } - bool ShaderCache::ShaderModifiedSince(std::string a_type, system_clock::time_point a_current) + void ShaderCache::StopFileWatcher() + { + logger::info("Stopping FileWatcher"); + if (fileWatcher) { + fileWatcher->removeWatch(watchID); + fileWatcher = nullptr; + } + if (listener) { + listener = nullptr; + } + } + + bool ShaderCache::UpdateShaderModifiedTime(std::string a_type) { - if (a_type.empty() || magic_enum::enum_cast(a_type, magic_enum::case_insensitive).has_value()) // type is invalid + if (!UseFileWatcher()) + return false; + if (a_type.empty() || !magic_enum::enum_cast(a_type, magic_enum::case_insensitive).has_value()) // type is invalid return false; std::filesystem::path filePath{ SIE::SShaderCache::GetShaderPath(a_type) }; std::lock_guard lockGuard(modifiedMapMutex); - if (std::filesystem::exists(filePath) && - (modifiedShaderMap.empty() || !modifiedShaderMap.contains(a_type))) // insert timestamp when first seen; rely on filewatcher for subsequent changes - modifiedShaderMap.insert_or_assign(a_type, std::chrono::clock_cast(std::filesystem::last_write_time(filePath))); + if (std::filesystem::exists(filePath)) { + auto fileTime = std::chrono::clock_cast(std::filesystem::last_write_time(filePath)); + if (!modifiedShaderMap.contains(a_type) || modifiedShaderMap.at(a_type) != fileTime) { // insert if new or timestamp changed + modifiedShaderMap.insert_or_assign(a_type, fileTime); + return true; + } + } + return false; + } + + bool ShaderCache::ShaderModifiedSince(std::string a_type, system_clock::time_point a_current) + { + if (!UseFileWatcher()) + return false; + if (a_type.empty() || !magic_enum::enum_cast(a_type, magic_enum::case_insensitive).has_value()) // type is invalid + return false; + std::lock_guard lockGuard(modifiedMapMutex); return !modifiedShaderMap.empty() && modifiedShaderMap.contains(a_type) // map has Type - && modifiedShaderMap.at(a_type) > a_current; //modification time is older than a_current + && modifiedShaderMap.at(a_type) > a_current; //modification time is newer than a_current } RE::BSGraphics::VertexShader* ShaderCache::MakeAndAddVertexShader(const RE::BSShader& shader, @@ -1820,40 +1875,67 @@ namespace SIE GetHumanTime(GetEta() + totalMs)); } - void UpdateListener::handleFileAction(efsw::WatchID, const std::string& dir, const std::string& filename, efsw::Action action, std::string) + void UpdateListener::processQueue() { + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); + std::unique_lock lock(actionMutex, std::defer_lock); auto& cache = SIE::ShaderCache::Instance(); - const std::filesystem::path filePath = std::filesystem::path(std::format("{}\\{}", dir, filename)); - std::chrono::time_point modifiedTime{}; - std::string extension = filePath.extension().string(); - std::string parentDir = filePath.parent_path().string(); - std::string shaderTypeString = filePath.stem().string(); - auto shaderType = magic_enum::enum_cast(shaderTypeString, magic_enum::case_insensitive); - switch (action) { - case efsw::Actions::Add: - break; - case efsw::Actions::Delete: - break; - case efsw::Actions::Modified: - logger::debug("Detected changed path {}", filePath.string()); - if (std::filesystem::exists(filePath)) - modifiedTime = std::chrono::clock_cast(std::filesystem::last_write_time(filePath)); - else // if file doesn't exist, don't do anything - return; - if (!std::filesystem::is_directory(filePath) && extension.starts_with(".hlsl") && parentDir.ends_with("Shaders") && shaderType.has_value()) { // TODO: Case insensitive checks - // Shader types, so only invalidate specific shader type (e.g,. Lighting) - cache.InsertModifiedShaderMap(shaderTypeString, modifiedTime); - cache.Clear(shaderType.value()); - } else if (!std::filesystem::is_directory(filePath) && extension.starts_with(".hlsl")) { // TODO: Case insensitive checks - // all other shaders, since we don't know what is using it, clear everything - cache.DeleteDiskCache(); - cache.Clear(); - } - break; - case efsw::Actions::Moved: - break; - default: - logger::error("Filewatcher received invalid action {}", magic_enum::enum_name(action)); + while (cache.UseFileWatcher()) { + lock.lock(); + if (!queue.empty() && queue.size() == lastQueueSize) { + bool clearCache = false; + for (fileAction fAction : queue) { + const std::filesystem::path filePath = std::filesystem::path(std::format("{}\\{}", fAction.dir, fAction.filename)); + std::chrono::time_point modifiedTime{}; + std::string extension = filePath.extension().string(); + std::string parentDir = filePath.parent_path().string(); + std::string shaderTypeString = filePath.stem().string(); + auto shaderType = magic_enum::enum_cast(shaderTypeString, magic_enum::case_insensitive); + switch (fAction.action) { + case efsw::Actions::Add: + break; + case efsw::Actions::Delete: + break; + case efsw::Actions::Modified: + logger::debug("Detected changed path {}", filePath.string()); + if (std::filesystem::exists(filePath)) + modifiedTime = std::chrono::clock_cast(std::filesystem::last_write_time(filePath)); + else // if file doesn't exist, don't do anything + return; + if (!std::filesystem::is_directory(filePath) && extension.starts_with(".hlsl") && parentDir.ends_with("Shaders") && shaderType.has_value()) { // TODO: Case insensitive checks + // Shader types, so only invalidate specific shader type (e.g,. Lighting) + cache.InsertModifiedShaderMap(shaderTypeString, modifiedTime); + cache.Clear(shaderType.value()); + } else if (!std::filesystem::is_directory(filePath) && extension.starts_with(".hlsl")) { // TODO: Case insensitive checks + // all other shaders, since we don't know what is using it, clear everything + clearCache = true; + } + break; + case efsw::Actions::Moved: + break; + default: + logger::error("Filewatcher received invalid action {}", magic_enum::enum_name(fAction.action)); + } + } + if (clearCache) { + cache.DeleteDiskCache(); + cache.Clear(); + } + queue.clear(); + } + lastQueueSize = queue.size(); + lock.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + queue.clear(); + } + + void UpdateListener::handleFileAction(efsw::WatchID watchid, const std::string& dir, const std::string& filename, efsw::Action action, std::string oldFilename) + { + std::lock_guard lock(actionMutex); + if (queue.empty() || (queue.back().action != action && queue.back().filename != filename)) { + // only add if not a duplicate; esfw is very spammy + queue.push_back({ watchid, dir, filename, action, oldFilename }); } } } \ No newline at end of file diff --git a/src/ShaderCache.h b/src/ShaderCache.h index e239cec11..70d4c853f 100644 --- a/src/ShaderCache.h +++ b/src/ShaderCache.h @@ -9,7 +9,7 @@ #include #include -static constexpr REL::Version SHADER_CACHE_VERSION = { 0, 0, 0, 17 }; +static constexpr REL::Version SHADER_CACHE_VERSION = { 0, 0, 0, 20 }; using namespace std::chrono; @@ -136,7 +136,17 @@ namespace SIE void DeleteDiskCache(); void ValidateDiskCache(); void WriteDiskCacheInfo(); + bool UseFileWatcher() const; + void SetFileWatcher(bool value); + void StartFileWatcher(); + void StopFileWatcher(); + + /** @brief Update the RE::BSShader::Type timestamp based on timestamp. + @param a_type Case insensitive string for the type of shader. E.g., Lighting + @return True if the shader for the type (i.e., Lighting.hlsl) timestamp was updated + */ + bool UpdateShaderModifiedTime(std::string a_type); /** @brief Whether the ShaderFile for RE::BSShader::Type has been modified since the timestamp. @param a_type Case insensitive string for the type of shader. E.g., Lighting @param a_current The current time in system_clock::time_point. @@ -306,6 +316,7 @@ namespace SIE bool isAsync = true; bool isDump = false; bool hideError = false; + bool useFileWatcher = false; std::stop_source ssource; std::mutex vertexShadersMutex; @@ -317,15 +328,29 @@ namespace SIE std::mutex modifiedMapMutex; // efsw file watcher - efsw::FileWatcher* fileWatcher; + efsw::FileWatcher* fileWatcher = nullptr; efsw::WatchID watchID; - UpdateListener* listener; + UpdateListener* listener = nullptr; }; // Inherits from the abstract listener class, and implements the the file action handler class UpdateListener : public efsw::FileWatchListener { public: + void processQueue(); void handleFileAction(efsw::WatchID, const std::string& dir, const std::string& filename, efsw::Action action, std::string) override; + + private: + struct fileAction + { + efsw::WatchID watchID; + std::string dir; + std::string filename; + efsw::Action action; + std::string oldFilename; + }; + std::mutex actionMutex; + std::vector queue{}; + size_t lastQueueSize = queue.size(); }; } diff --git a/src/ShaderTools/ShaderCompiler.cpp b/src/ShaderTools/ShaderCompiler.cpp index f96993bc8..55a3f48d6 100644 --- a/src/ShaderTools/ShaderCompiler.cpp +++ b/src/ShaderTools/ShaderCompiler.cpp @@ -1,5 +1,6 @@ #include "d3d11.h" #include "d3dcompiler.h" +#include "util.h" namespace ShaderCompiler { @@ -40,51 +41,6 @@ namespace ShaderCompiler ID3D11PixelShader* CompileAndRegisterPixelShader(const std::wstring a_filePath) { - REL::Relocation g_ID3D11Device{ RELOCATION_ID(524729, 411348) }; - - D3D_SHADER_MACRO macros[3] = {}; - - macros[0].Name = "WINPC"; - macros[0].Definition = ""; - macros[1].Name = "DX11"; - macros[1].Definition = ""; - - UINT compilerFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_OPTIMIZATION_LEVEL3; - ID3DBlob* shaderBlob = nullptr; - ID3DBlob* shaderErrors = nullptr; - - if (FAILED(D3DCompileFromFile(a_filePath.c_str(), macros, D3D_COMPILE_STANDARD_FILE_INCLUDE, "main", "ps_5_0", compilerFlags, 0, &shaderBlob, &shaderErrors))) { - logger::error("Pixel shader compilation failed:\n{}", shaderErrors ? (const char*)shaderErrors->GetBufferPointer() : "Unknown error"); - - if (shaderBlob) - shaderBlob->Release(); - - if (shaderErrors) - shaderErrors->Release(); - - return nullptr; - } - - if (shaderErrors) - shaderErrors->Release(); - - logger::debug("shader compilation succeeded"); - - logger::debug("registering shader"); - - ID3D11PixelShader* regShader; - - if (FAILED((*g_ID3D11Device)->CreatePixelShader(shaderBlob->GetBufferPointer(), shaderBlob->GetBufferSize(), nullptr, ®Shader))) { - logger::error("pixel shader registration failed"); - - if (shaderBlob) - shaderBlob->Release(); - - return nullptr; - } - - logger::debug("shader registration succeeded"); - - return regShader; + return static_cast(Util::CompileShader(a_filePath.data(), {}, "ps_5_0")); } } \ No newline at end of file diff --git a/src/State.cpp b/src/State.cpp index 2e40af3ba..c8dec244d 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -222,6 +222,8 @@ void State::Load(bool a_test) shaderCache.compilationThreadCount = std::clamp(advanced["Compiler Threads"].get(), 1, static_cast(std::thread::hardware_concurrency())); if (advanced["Background Compiler Threads"].is_number_integer()) shaderCache.backgroundCompilationThreadCount = std::clamp(advanced["Background Compiler Threads"].get(), 1, static_cast(std::thread::hardware_concurrency())); + if (advanced["Use FileWatcher"].is_boolean()) + shaderCache.SetFileWatcher(advanced["Use FileWatcher"]); } if (settings["General"].is_object()) { @@ -270,6 +272,7 @@ void State::Save(bool a_test) advanced["Shader Defines"] = shaderDefinesString; advanced["Compiler Threads"] = shaderCache.compilationThreadCount; advanced["Background Compiler Threads"] = shaderCache.backgroundCompilationThreadCount; + advanced["Use FileWatcher"] = shaderCache.UseFileWatcher(); settings["Advanced"] = advanced; json general; diff --git a/src/Util.cpp b/src/Util.cpp index 12a472e7e..548805850 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -153,7 +153,7 @@ namespace Util macros.push_back({ nullptr, nullptr }); // Compiler setup - uint32_t flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_OPTIMIZATION_LEVEL3; + uint32_t flags = !State::GetSingleton()->IsDeveloperMode() ? (D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_OPTIMIZATION_LEVEL3) : D3DCOMPILE_DEBUG; ID3DBlob* shaderBlob; ID3DBlob* shaderErrors; diff --git a/src/XSEPlugin.cpp b/src/XSEPlugin.cpp index ebd4bd176..350a9ac8d 100644 --- a/src/XSEPlugin.cpp +++ b/src/XSEPlugin.cpp @@ -96,7 +96,8 @@ void MessageHandler(SKSE::MessagingInterface::Message* message) auto& shaderCache = SIE::ShaderCache::Instance(); shaderCache.ValidateDiskCache(); - shaderCache.StartFileWatcher(); + if (shaderCache.UseFileWatcher()) + shaderCache.StartFileWatcher(); for (auto* feature : Feature::GetFeatureList()) { if (feature->loaded) { feature->PostPostLoad();