diff --git a/src/dxvk/imgui/dxvk_imgui.cpp b/src/dxvk/imgui/dxvk_imgui.cpp index 56f7abb2..6b1be649 100644 --- a/src/dxvk/imgui/dxvk_imgui.cpp +++ b/src/dxvk/imgui/dxvk_imgui.cpp @@ -280,6 +280,22 @@ namespace dxvk { {SkyAutoDetectMode::CameraPositionAndDepthFlags, "By Camera Position and Depth Flags"} } }); + static auto SkyScaleCalibrationModeCombo = ImGui::ComboWithKey( + "3D Sky Scale Calibration", + ImGui::ComboWithKey::ComboEntries { { + {SkyScaleCalibrationMode::Fixed, "Always Use Default Scale"}, + {SkyScaleCalibrationMode::DeltaAutomatic, "Calculate From Delta (Main vs Sky)"}, + {SkyScaleCalibrationMode::SourceEngineAutomatic, "Calculate From Source Engine Approximation"} + } }); + + static auto SkyScaleOffsetFormulaCombo = ImGui::ComboWithKey( + "3D Sky Offset Formula", + ImGui::ComboWithKey::ComboEntries { { + {SkyScaleOffsetFormula::Origin, "Origin Based"}, + {SkyScaleOffsetFormula::Linear, "Linear Based"}, + {SkyScaleOffsetFormula::SourceEngine, "Source Engined Based (Hyperbolic)"} + } }); + static auto upscalerNoDLSSCombo = ImGui::ComboWithKey( "Upscaler Type", { { @@ -444,7 +460,7 @@ namespace dxvk { ImPlot::DestroyContext(m_plotContext); ImGui::DestroyContext(m_context); } - + void ImGUI::AddTexture(const XXH64_hash_t hash, const Rc& imageView) { if (g_imguiTextureMap.find(hash) == g_imguiTextureMap.end()) { ImGuiTexture texture; @@ -458,7 +474,7 @@ namespace dxvk { if (RtxOptions::Get()->keepTexturesForTagging()) { return; } - + if (g_imguiTextureMap.find(hash) != g_imguiTextureMap.end()) g_imguiTextureMap.erase(hash); } @@ -556,7 +572,7 @@ namespace dxvk { type != UIType::None ? 1 : 0, 0); } } - + void ImGUI::showMaterialOptions() { if (ImGui::CollapsingHeader("Material Options (optional)", collapsingHeaderClosedFlags)) { ImGui::Indent(); @@ -696,7 +712,7 @@ namespace dxvk { m_splash->update(m_largeFont); m_about->update(ctx); - + m_capture->update(ctx); showDebugVisualizations(ctx); @@ -800,7 +816,7 @@ namespace dxvk { ImGui::NextColumn(); ImGui::Checkbox("Always Developer Menu", &RtxOptions::Get()->defaultToAdvancedUIObject()); - + ImGui::EndColumns(); ImGui::Separator(); @@ -1059,7 +1075,7 @@ namespace dxvk { if (dlss.supportsDLSS()) { m_userGraphicsSettingChanged |= getUpscalerCombo(dlss, rayReconstruction).getKey(&RtxOptions::Get()->upscalerTypeObject()); } - + ImGui::PushItemWidth(static_cast(subItemWidth)); ImGui::Indent(static_cast(subItemIndent)); @@ -1078,7 +1094,7 @@ namespace dxvk { switch (RtxOptions::Get()->upscalerType()) { - case UpscalerType::DLSS: + case UpscalerType::DLSS: if (RtxOptions::Get()->enableRayReconstruction() == false) { m_userGraphicsSettingChanged |= ImGui::Combo("DLSS Mode", &RtxOptions::Get()->qualityDLSSObject(), "Ultra Performance\0Performance\0Balanced\0Quality\0Auto\0"); @@ -1104,29 +1120,29 @@ namespace dxvk { } break; case UpscalerType::NIS: { - m_userGraphicsSettingChanged |= ImGui::Combo("NIS Preset", &RtxOptions::Get()->nisPresetObject(), "Performance\0Balanced\0Quality\0Fullscreen\0"); - RtxOptions::Get()->updateUpscalerFromNisPreset(); + m_userGraphicsSettingChanged |= ImGui::Combo("NIS Preset", &RtxOptions::Get()->nisPresetObject(), "Performance\0Balanced\0Quality\0Fullscreen\0"); + RtxOptions::Get()->updateUpscalerFromNisPreset(); - // Display NIS Upscaling Information + // Display NIS Upscaling Information - auto resolutionScale = RtxOptions::Get()->getResolutionScale(); + auto resolutionScale = RtxOptions::Get()->getResolutionScale(); - ImGui::TextWrapped(str::format("NIS Resolution Scale: ", resolutionScale).c_str()); + ImGui::TextWrapped(str::format("NIS Resolution Scale: ", resolutionScale).c_str()); - break; - } + break; + } case UpscalerType::TAAU: { - m_userGraphicsSettingChanged |= ImGui::Combo("TAA-U Preset", &RtxOptions::Get()->taauPresetObject(), "Performance\0Balanced\0Quality\0Fullscreen\0"); - RtxOptions::Get()->updateUpscalerFromTaauPreset(); + m_userGraphicsSettingChanged |= ImGui::Combo("TAA-U Preset", &RtxOptions::Get()->taauPresetObject(), "Performance\0Balanced\0Quality\0Fullscreen\0"); + RtxOptions::Get()->updateUpscalerFromTaauPreset(); - // Display TAA-U Upscaling Information + // Display TAA-U Upscaling Information - auto resolutionScale = RtxOptions::Get()->getResolutionScale(); + auto resolutionScale = RtxOptions::Get()->getResolutionScale(); - ImGui::TextWrapped(str::format("TAA-U Resolution Scale: ", resolutionScale).c_str()); + ImGui::TextWrapped(str::format("TAA-U Resolution Scale: ", resolutionScale).c_str()); - break; - } + break; + } } ImGui::Unindent(static_cast(subItemIndent)); @@ -1228,7 +1244,7 @@ namespace dxvk { m_userGraphicsSettingChanged |= denoiserQualityCombo.getKey(&RtxOptions::Get()->denoiseDirectAndIndirectLightingSeparatelyObject()); ImGui::EndDisabled(); } - + m_userGraphicsSettingChanged |= textureQualityCombo.getKey(&RtxOptions::Get()->minReplacementTextureMipMapLevelObject()); m_userGraphicsSettingChanged |= indirectLightingParticlesCombo.getKey(&indirectLightParticlesLevel); ImGui::SetTooltipToLastWidgetOnHover("Controls the quality of particles in indirect (reflection/GI) rays."); @@ -1345,7 +1361,7 @@ namespace dxvk { ImGuiWindowFlags hud_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove; if (ImGui::Begin("HUD", nullptr, hud_flags)) { - + for (auto&& message : hudMessages) { ImGui::Text(message.c_str()); } @@ -1365,7 +1381,7 @@ namespace dxvk { ImGui::SameLine(200.f); ImGui::Checkbox("Include G-Buffer", &RtxOptions::Get()->captureDebugImageObject()); - + { // Recompile Shaders button and its status message using namespace std::chrono; static enum { None, OK, Error } shaderMessage = None; @@ -1552,7 +1568,7 @@ namespace dxvk { str << (isRT ? "Render Target " : "Texture ") << imageInfo.extent.width << 'x' << imageInfo.extent.height << '\n'; str << formatName << '\n'; str << "Hash: " << hashToString(texHash) << '\n'; - + return str.str(); } @@ -1603,8 +1619,8 @@ namespace dxvk { // don't show popup window and toggle the list directly, // if was a left mouse click in the splitted lists bool toggleWithoutPopup = ImGUI::showLegacyTextureGui() && - g_wasLeftClick && - !lastOpenCategoryId.empty(); + g_wasLeftClick && + !lastOpenCategoryId.empty(); g_wasLeftClick = false; if (toggleWithoutPopup) { @@ -1644,7 +1660,7 @@ namespace dxvk { g_openWhenAvailable = false; } } - + if (ImGui::BeginPopup(POPUP_NAME)) { const XXH64_hash_t texHash = g_holdingTexture.load(); if (texHash != kEmptyHash) { @@ -1733,7 +1749,7 @@ namespace dxvk { const ImVec2 availableSize = ImGui::GetContentRegionAvail(); const float childWindowHeight = minChildHeight <= 600.0f ? minChildHeight - : availableSize.y < 600 ? 600.0f : availableSize.y; + : availableSize.y < 600 ? 600.0f : availableSize.y; ImGuiWindowFlags window_flags = ImGuiWindowFlags_None; ImGui::BeginChild(str::format("Child", uniqueId).c_str(), ImVec2(availableSize.x, childWindowHeight), false, window_flags); @@ -1870,14 +1886,14 @@ namespace dxvk { // popup for texture selection from world / ui // Only the "active" category is allowed to control the texture popup and highlighting logic if (!showLegacyTextureGui() || uniqueId == texture_popup::lastOpenCategoryId) { - const bool wasUIClick = - !texture_popup::isOpened() && + const bool wasUIClick = + !texture_popup::isOpened() && clickedOnTextureButton; const bool wasWorldClick = isWorldTextureSelectionAllowed() && !texture_popup::isOpened() && - !clickedOnTextureButton && + !clickedOnTextureButton && (ImGui::IsMouseClicked(ImGuiMouseButton_Left) || ImGui::IsMouseClicked(ImGuiMouseButton_Right)); if (wasUIClick) { @@ -1942,14 +1958,14 @@ namespace dxvk { ImGui::PushItemWidth(200); m_capture->show(ctx); - + if(ImGui::CollapsingHeader("Enhancements", collapsingHeaderFlags | ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Indent(); showEnhancementsTab(ctx); ImGui::Unindent(); } } - + void ImGUI::showEnhancementsTab(const Rc& ctx) { if (!ctx->getCommonObjects()->getSceneManager().areReplacementsLoaded()) { ImGui::Text("No USD enhancements detected, the following options have been disabled. See documentation for how to use enhancements with Remix."); @@ -2210,6 +2226,27 @@ namespace dxvk { ImGui::SliderFloat("Sky Min Z Threshold", &RtxOptions::Get()->skyMinZThresholdObject(), 0.0f, 1.0f); skyAutoDetectCombo.getKey(&RtxOptions::Get()->skyAutoDetectObject()); + if (ImGui::CollapsingHeader("3D Skybox Settings [Experimental]", collapsingHeaderClosedFlags)) { + ImGui::Checkbox("Enable Shared Depth", &RtxOptions::skySharedDepthObject()); + ImGui::Checkbox("Enable 3D Skybox Pathtracing ", &RtxOptions::skyBoxPathTracingObject()); + + if (RtxOptions::skyBoxPathTracing()) { + ImGui::InputInt("Default Scale", &RtxOptions::Get()->skyDefaultScaleObject(), 1, 1, 1); + SkyScaleCalibrationModeCombo.getKey(&RtxOptions::Get()->skyScaleCalibrationModeObject()); + SkyScaleOffsetFormulaCombo.getKey(&RtxOptions::Get()->skyScaleOffsetFormulaObject()); + + ImGui::Separator(); + auto& cameraManager = ctx->getCommonObjects()->getSceneManager().getCameraManager(); + auto cam = cameraManager.isCameraValid(CameraType::Sky) ? &cameraManager.getCamera(CameraType::Sky) : nullptr; + if (cam){ + ImGui::Text("Sky Offset: %.2f %.2f %.2f", cam->m_skyOffset.x, cam->m_skyOffset.y, cam->m_skyOffset.z); + ImGui::Text("Sky Scale: %i", cam->m_skyScale); + } + ImGui::Separator(); + } + + }; + if (ImGui::CollapsingHeader("Advanced", collapsingHeaderClosedFlags)) { ImGui::Indent(); @@ -2261,7 +2298,7 @@ namespace dxvk { void ImGUI::showVsyncOptions(bool enableDLFGGuard) { // we should never get here without a swapchain, so we must have latched the vsync value already assert(RtxOptions::Get()->enableVsync() != EnableVsync::WaitingForImplicitSwapchain); - + if (enableDLFGGuard && DxvkDLFG::enable()) { ImGui::BeginDisabled(); } @@ -2275,7 +2312,7 @@ namespace dxvk { ImGui::TextWrapped("This setting overrides the native game's V-Sync setting."); ImGui::Unindent(); ImGui::EndDisabled(); - + if (enableDLFGGuard && DxvkDLFG::enable()) { ImGui::Indent(); ImGui::TextWrapped("When Frame Generation is active, V-Sync is automatically disabled."); @@ -2548,7 +2585,7 @@ namespace dxvk { ImGui::Separator(); ImGui::Checkbox("Allow Full Screen Exclusive?", &RtxOptions::Get()->allowFSEObject()); - + ImGui::Unindent(); } @@ -2636,7 +2673,7 @@ namespace dxvk { ImGui::DragFloat("1st bounce: Min Continue Probability", &RtxOptions::Get()->russianRoulette1stBounceMinContinueProbabilityObject(), 0.01f, 0.0f, 1.0f, "%.3f", sliderFlags); ImGui::DragFloat("1st bounce: Max Continue Probability", &RtxOptions::Get()->russianRoulette1stBounceMaxContinueProbabilityObject(), 0.01f, 0.0f, 1.0f, "%.3f", sliderFlags); - + secondPlusRussianRouletteModeCombo.getKey(&RtxOptions::Get()->russianRouletteModeObject()); if (RtxOptions::Get()->russianRouletteMode() == RussianRouletteMode::ThroughputBased) { @@ -2648,18 +2685,18 @@ namespace dxvk { ImGui::DragFloat("2nd+ bounce: Specular Continue Probability", &RtxOptions::Get()->russianRouletteSpecularContinueProbabilityObject(), 0.01f, 0.0f, 1.0f, "%.3f", sliderFlags); ImGui::DragFloat("2nd+ bounce: Distance Factor", &RtxOptions::Get()->russianRouletteDistanceFactorObject(), 0.01f, 0.0f, 1.0f, "%.3f", sliderFlags); } - + ImGui::Unindent(); } ImGui::Unindent(); } - if (RtxOptions::Get()->getIsOpacityMicromapSupported() && + if (RtxOptions::Get()->getIsOpacityMicromapSupported() && ImGui::CollapsingHeader("Opacity Micromap", collapsingHeaderClosedFlags)) { ImGui::Indent(); ImGui::Checkbox("Enable Opacity Micromap", &RtxOptions::Get()->opacityMicromap.enableObject()); - + if (common->getOpacityMicromapManager()) common->getOpacityMicromapManager()->showImguiSettings(); @@ -2912,7 +2949,7 @@ namespace dxvk { ImGui::Unindent(); } } - + if (useNRD) { if (useDoubleDenoisers) { @@ -2982,7 +3019,7 @@ namespace dxvk { if (ImGui::CollapsingHeader("Post FX", collapsingHeaderClosedFlags)) common->metaPostFx().showImguiSettings(); - + ImGui::Unindent(); } @@ -3194,7 +3231,7 @@ namespace dxvk { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_Data* bd = (ImGui_ImplVulkan_Data*)io.BackendRendererUserData; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - + // Range of characters we want to use the primary font ImVector characterRange; { diff --git a/src/dxvk/rtx_render/rtx_camera.h b/src/dxvk/rtx_render/rtx_camera.h index bdb7261d..2554d5c0 100644 --- a/src/dxvk/rtx_render/rtx_camera.h +++ b/src/dxvk/rtx_render/rtx_camera.h @@ -43,7 +43,7 @@ namespace dxvk Unknown, // Unset camera state, used mainly for state tracking. Its camera object is aliased // with the Main camera object, so on access it retrieves the Main camera - Count + Count }; } @@ -199,6 +199,10 @@ namespace dxvk uint32_t flags; }; + uint32_t m_skyScale = 1; + uint32_t m_lastSkyScale = 1; + Vector3 m_skyOffset; + // Note: All camera matricies stored as double precision. While this does not do much for some matricies (which were provided // by the application in floating point precision), it does help for preserving matrix stability on those which have been inverted, // as well as in code using these matrices which may do further inversions or combination operations. If such precision is not needed @@ -330,6 +334,15 @@ namespace dxvk const RtCameraSetting& getSetting(); + void setSkyOffset(const Vector3& skyOffset) { + m_skyOffset = skyOffset; + }; + + void setSkyScale(int scale) { + m_lastSkyScale = m_skyScale; + m_skyScale = scale; + }; + private: Matrix4d getShakenViewToWorldMatrix(Matrix4d& viewToWorld, uint32_t flags); Matrix4d updateFreeCamera(uint32_t flags); @@ -392,7 +405,7 @@ namespace dxvk RTX_OPTION_ENV("rtx.cameraSequence", bool, autoLoad, false, "DXVK_CAMERA_SEQUENCE_AUTO_LOAD", "Load camera sequence automatically."); RTX_OPTION("rtx.cameraSequence", int, currentFrame, 0, "Current Frame."); RTX_OPTION_ENV("rtx.cameraSequence", Mode, mode, Mode::None, "DXVK_CAMERA_SEQUENCE_MODE", "Current mode."); - + std::vector m_settings; static RtCameraSequence* s_instance; }; diff --git a/src/dxvk/rtx_render/rtx_camera_manager.cpp b/src/dxvk/rtx_render/rtx_camera_manager.cpp index 0f64f50a..16058a30 100644 --- a/src/dxvk/rtx_render/rtx_camera_manager.cpp +++ b/src/dxvk/rtx_render/rtx_camera_manager.cpp @@ -163,6 +163,166 @@ namespace dxvk { cameraSequence->addRecord(setting); } + auto& skyCamera = getCamera(CameraType::Sky); + auto& mainCamera = getCamera(CameraType::Main); + + // Firstly, don't try skyscale if we're not pathtracing + // Additionally, don't try sky scale if we have no sky camera, or the main camera is invalid + // Finally, don't do scale calculations if the current camera isn't the main camera + auto shouldCalculateSkyScale = RtxOptions::Get()->skyBoxPathTracing() + && skyCamera.isValid(frameId) + && mainCamera.isValid(frameId) + && cameraType == CameraType::Main; + + if (shouldCalculateSkyScale) { + + // Fallback to the default scale if the camera is odd, but let the formula work after + if (isCameraCut) + skyCamera.setSkyScale(RtxOptions::Get()->skyDefaultScale()); + + auto curCamPos = (Vector3) mainCamera.getPosition(false); + auto curSkyPos = (Vector3) skyCamera.getPosition(false); + auto lastCamPos = (Vector3) inverse(mainCamera.getPreviousWorldToView(false))[3].xyz();; + auto lastSkyPos = (Vector3) inverse(skyCamera.getPreviousWorldToView(false))[3].xyz(); + + bool uniqueCamerasFound = + !areClose(curCamPos, curSkyPos) && + !areClose(curSkyPos, lastCamPos); + + /* + Do not do any transitioning if the main and skycams are equal + or have been equal last frame! Fixes occasions where first-view + gets mistaken as the main view instead of sky view + */ + if (uniqueCamerasFound) { + + switch (RtxOptions::Get()->skyScaleCalibrationMode()) { + + case SkyScaleCalibrationMode::Fixed: + skyCamera.setSkyScale(RtxOptions::Get()->skyDefaultScale()); + break; + case SkyScaleCalibrationMode::DeltaAutomatic: + if (!areClose(curSkyPos, lastSkyPos) && !areClose(curCamPos, lastCamPos)) { + + float mdiff = lengthSqr(curCamPos - lastCamPos); + float sdiff = lengthSqr(curSkyPos - lastSkyPos); + + if (sdiff != 0 && mdiff != 0) { + float ratio_scale = mdiff / sdiff; + int new_scale = ratio_scale >= 1 ? static_cast(std::sqrt(ratio_scale)) : skyCamera.m_lastSkyScale; + skyCamera.setSkyScale(new_scale); + } + } + break; + case SkyScaleCalibrationMode::SourceEngineAutomatic: + static auto getDecimal = [](double val) { + double workable = std::abs(val); + return workable - std::floor(workable); + }; + + static auto huntForScale = [](double skyPos, double mainPos) { + double error = 0.0001; + double mainDecimal = getDecimal(mainPos); + double skyDecimal = getDecimal(skyPos); + if (std::abs(mainDecimal - skyDecimal) < error) + return -1; + + bool shouldInvert = std::signbit(mainPos) != std::signbit(skyPos); + + float max_scale = 64; // Source typically uses power of 2, and must be an even integer + double max_square = std::sqrt(max_scale); + for (int i = 0; i < max_square - 1; i++) { + int predicted_scale = static_cast(std::pow(2, i)); + double predicted_skypos = mainPos / predicted_scale; + double predicted_skydec = getDecimal(predicted_skypos); + + if (shouldInvert) + predicted_skydec = std::abs(-1 * predicted_skydec + 1); //Produces the opposite during offset + + if (std::abs(predicted_skydec - skyDecimal) < error) + return predicted_scale; + + }; + + return -1; + }; + + auto corroborateScale = [skyCamera](int scalex, int scaley, int scalez) { + if (scalex == scaley && scaley == scalez && scalex > 0) + return scalex; + + if (scalex == scalez && scalex > 0) { + if (scaley < 0) + return scalex; + }; + + if (scalex == scaley && scaley > 0) { + if (scalez < 0) + return scalex; + }; + + if (scaley == scalez && scalez > 0) { + if (scalex < 0) + return scaley; + }; + + if (scalex < 0 && scaley < 0 && scalez > 0) + return scalez; + + if (scalex < 0 && scalez < 0 && scaley > 0) + return scaley; + + if (scaley < 0 && scalez < 0 && scalex > 0) + return scalex; + + return -1; + }; + + + int scalex = huntForScale(curSkyPos.x, curCamPos.x); + int scaley = huntForScale(curSkyPos.y, curCamPos.y); + int scalez = huntForScale(curSkyPos.z, curCamPos.z); + int scale = corroborateScale(scalex, scaley, scalez); + // float sky_denom = getDenominator(skyref); + // float ply_denom = getDenominator(plyref); + + //float scale = 1; + //if (skyref != plyref) { + // float sky_denom = getDenominator(skyref); + // float ply_denom = getDenominator(plyref); + // scale = sky_denom > ply_denom ? sky_denom / ply_denom : ply_denom / sky_denom; + //} + if (scale < 0) + scale = skyCamera.m_lastSkyScale; + skyCamera.setSkyScale(scale); + break; + } + } + + float skyScale = static_cast(skyCamera.m_skyScale); + switch (RtxOptions::Get()->skyScaleOffsetFormula()) { + case SkyScaleOffsetFormula::Origin: + skyCamera.setSkyOffset(Vector3(0, 0, 0)); + break; + + case SkyScaleOffsetFormula::SourceEngine: + + // Shift by sky scale to get the post scaled value + curCamPos.x *= 1 / skyScale; + curCamPos.y *= 1 / skyScale; + curCamPos.z *= 1 / skyScale; + + // Everything after is linear so don't break out of this one + case SkyScaleOffsetFormula::Linear: + auto offset = curCamPos - curSkyPos; + if (!areClose(skyCamera.m_skyOffset, offset)) + skyCamera.setSkyOffset(offset); + break; + } + + + } + // Register camera cut when there are significant interruptions to the view (like changing level, or opening a menu) if (isCameraCut && cameraType == CameraType::Main) { m_lastCameraCutFrameId = m_device->getCurrentFrameId(); diff --git a/src/dxvk/rtx_render/rtx_context.cpp b/src/dxvk/rtx_render/rtx_context.cpp index 2466fc14..a71cd17e 100644 --- a/src/dxvk/rtx_render/rtx_context.cpp +++ b/src/dxvk/rtx_render/rtx_context.cpp @@ -2026,6 +2026,10 @@ namespace dxvk { DxvkRenderTargets skyRt; skyRt.color[0].view = getResourceManager().getCompatibleViewForView(skyMatteView, m_skyRtColorFormat); skyRt.color[0].layout = VK_IMAGE_LAYOUT_GENERAL; + + if (RtxOptions::Get()->skySharedDepth()) + skyRt.depth = m_state.om.renderTargets.depth; + bindRenderTargets(skyRt); if (m_skyClearDirty) { @@ -2341,6 +2345,7 @@ namespace dxvk { // Save viewports const uint32_t curViewportCount = m_state.gp.state.rs.viewportCount(); const DxvkViewportState curVp = m_state.vp; + rasterizeToSkyMatte(params, drawCallState); rasterizeToSkyProbe(params, drawCallState); diff --git a/src/dxvk/rtx_render/rtx_instance_manager.cpp b/src/dxvk/rtx_render/rtx_instance_manager.cpp index b98fae9c..325ec7a2 100644 --- a/src/dxvk/rtx_render/rtx_instance_manager.cpp +++ b/src/dxvk/rtx_render/rtx_instance_manager.cpp @@ -403,6 +403,24 @@ namespace dxvk { Matrix4 objectToWorld = drawCall.getTransformData().objectToWorld; Matrix4 worldToProjection = drawCall.getTransformData().viewToProjection * drawCall.getTransformData().worldToView; + if (RtxOptions::Get()->skyBoxPathTracing() && drawCall.cameraType == CameraType::Sky) { + const auto* skyCamera = &cameraManager.getCamera(drawCall.cameraType); + // Note: we may accept a data even from a prev frame, as we need any information to restore; + // but if camera data is stale, it introduces an scene object transform's lag + if (!skyCamera->isValid(m_device->getCurrentFrameId()) && + !skyCamera->isValid(m_device->getCurrentFrameId() - 1)) { + skyCamera = &cameraManager.getCamera(CameraType::Main); + } + + Matrix4 scale(static_cast(skyCamera->m_skyScale)); + scale[3][3] = 1; + + // Rotating portion is missing. if it is needed in the future, just plug it between the scale/trans + + Matrix4 transformMatrix = scale * translationMatrix(skyCamera->m_skyOffset); + objectToWorld = transformMatrix * objectToWorld; + } + // An attempt to resolve cases where games pre-combine view and world matrices if (RtxOptions::Get()->resolvePreCombinedMatrices() && isIdentityExact(drawCall.getTransformData().worldToView)) { @@ -848,7 +866,8 @@ namespace dxvk { // Hide the sky instance since it is not raytraced. // Sky mesh and material are only good for capture and replacement purposes. - if (drawCall.cameraType == CameraType::Sky) { + if ((!RtxOptions::Get()->skyBoxPathTracing() && drawCall.cameraType == CameraType::Sky) + || currentInstance.m_categoryFlags == InstanceCategories::Sky) { currentInstance.m_isHidden = true; } diff --git a/src/dxvk/rtx_render/rtx_options.h b/src/dxvk/rtx_render/rtx_options.h index d76c3aa2..c678c034 100644 --- a/src/dxvk/rtx_render/rtx_options.h +++ b/src/dxvk/rtx_render/rtx_options.h @@ -134,13 +134,26 @@ namespace dxvk { View, World }; - + enum class SkyAutoDetectMode : int { None = 0, CameraPosition, CameraPositionAndDepthFlags }; + enum class SkyScaleOffsetFormula : int { + Origin = 0, + Linear, + SourceEngine + }; + + enum class SkyScaleCalibrationMode : int { + Fixed = 0, + DeltaAutomatic, + SourceEngineAutomatic + }; + + enum class EnableVsync : int { Off = 0, On = 1, @@ -159,7 +172,7 @@ namespace dxvk { "These textures will be ignored when attempting to determine the desired textures from a draw to use for ray tracing."); RW_RTX_OPTION("rtx", fast_unordered_set, skyBoxTextures, {}, "Textures on draw calls used for the sky or are otherwise intended to be very far away from the camera at all times (no parallax).\n" - "Any draw calls using a texture in this list will be treated as sky and rendered as such in a manner different from typical geometry."); + "Any draw calls using a texture in this list will be treated as sky and rendered as such in a manner different from typical geometry."); RW_RTX_OPTION("rtx", fast_unordered_set, skyBoxGeometries, {}, "Geometries from draw calls used for the sky or are otherwise intended to be very far away from the camera at all times (no parallax).\n" "Any draw calls using a geometry hash in this list will be treated as sky and rendered as such in a manner different from typical geometry.\n" @@ -176,7 +189,7 @@ namespace dxvk { RW_RTX_OPTION("rtx", fast_unordered_set, worldSpaceUiTextures, {}, "Textures on draw calls that should be treated as worldspace UI elements.\n" "Unlike typical UI textures this option is useful for improved rendering of UI elements which appear as part of the scene (moving around in 3D space rather than as a screenspace element)."); - RW_RTX_OPTION("rtx", fast_unordered_set, worldSpaceUiBackgroundTextures, {}, + RW_RTX_OPTION("rtx", fast_unordered_set, worldSpaceUiBackgroundTextures, {}, "Hack/workaround option for dynamic world space UI textures with a coplanar background.\n" "Apply to backgrounds if the foreground material is a dynamic world texture rendered in UI that is unpredictable and rapidly changing.\n" "This offsets the background texture backwards."); @@ -251,7 +264,7 @@ namespace dxvk { "Defines which asset hashes we need to generate via the geometry processing engine."); RW_RTX_OPTION("rtx", std::string, geometryAssetHashRuleString, "positions,indices,geometrydescriptor", "Defines which hashes we need to include when sampling from replacements and doing USD capture."); - + public: RTX_OPTION("rtx", bool, showRaytracingOption, true, "Enables or disables the option to toggle ray tracing in the UI. When set to false the ray tracing checkbox will not appear in the Remix UI."); @@ -265,7 +278,7 @@ namespace dxvk { "Setting this to 0 will use actual frame time delta for a given frame. Non-zero value allows the actual time delta to be overridden and is primarily used for automation to ensure determinism run to run without variance due to frame time fluctuations."); RTX_OPTION_FLAG("rtx", bool, keepTexturesForTagging, false, RtxOptionFlags::NoSave, "A flag to keep all textures in video memory, which can drastically increase VRAM consumption. Intended to assist with tagging textures that are only used for a short period of time (such as loading screens). Use only when necessary!"); - RTX_OPTION("rtx.gui", float, textureGridThumbnailScale, 1.f, + RTX_OPTION("rtx.gui", float, textureGridThumbnailScale, 1.f, "A float to set the scale of thumbnails while selecting textures.\n" "This will be scaled by the default value of 120 pixels.\n" "This value must always be greater than zero."); @@ -507,7 +520,7 @@ namespace dxvk { RTX_OPTION("rtx", bool, enablePSTRSecondaryIncidentSplitApproximation, true, "Enable transmission PSR on secondary incident transmission events such as entering a translucent material on an already-transmitted path (rather than respecting no-split path PSR rule).\n" "Typically this results in better looking glass when enabled (at the cost accuracy due to ignoring reflections off of glass seen through glass for example)."); - + // Note: In a more technical sense, any PSR reflection or transmission from a surface with "normal detail" greater than the specified value will generate a 1.0 in the // disocclusionThresholdMix mask, indicating that the alternate disocclusion threshold in the denoiser should be used. // A value of 0 is a valid setting as it means that any detail at all, no matter how small, will set that mask bit (e.g. any usage of a normal map deviating from from the @@ -522,7 +535,7 @@ namespace dxvk { "This is typically used to reduce flickering artifacts resulting from refraction on surfaces like glass leveraging normal maps as often the denoiser is too aggressive with disocclusion checks frame to frame when DLSS or other camera jittering is in use."); // Shader Execution Reordering Options - RTX_OPTION_ENV("rtx", bool, isShaderExecutionReorderingSupported, true, "DXVK_IS_SHADER_EXECUTION_REORDERING_SUPPORTED", "Enables support of Shader Execution Reordering (SER) if it is supported by the target HW and SW."); + RTX_OPTION_ENV("rtx", bool, isShaderExecutionReorderingSupported, true, "DXVK_IS_SHADER_EXECUTION_REORDERING_SUPPORTED", "Enables support of Shader Execution Reordering (SER) if it is supported by the target HW and SW."); RTX_OPTION("rtx", bool, enableShaderExecutionReorderingInPathtracerGbuffer, false, "(Note: Hard disabled in shader code) Enables Shader Execution Reordering (SER) in GBuffer Raytrace pass if SER is supported."); RTX_OPTION("rtx", bool, enableShaderExecutionReorderingInPathtracerIntegrateIndirect, true, "Enables Shader Execution Reordering (SER) in Integrate Indirect pass if SER is supported."); @@ -812,7 +825,7 @@ namespace dxvk { friend class RtxOptions; friend class ImGUI; bool isSupported = false; - RTX_OPTION_ENV("rtx.opacityMicromap", bool, enable, true, "DXVK_ENABLE_OPACITY_MICROMAP", + RTX_OPTION_ENV("rtx.opacityMicromap", bool, enable, true, "DXVK_ENABLE_OPACITY_MICROMAP", "Enables Opacity Micromaps for geometries with textures that have alpha cutouts.\n" "This is generally the case for geometries such as fences, foliage, particles, etc. .\n" "Opacity Micromaps greatly speed up raytracing of partially opaque triangles.\n" @@ -922,7 +935,7 @@ namespace dxvk { RTX_OPTION_FLAG("rtx", uint32_t, skyUiDrawcallCount, 0, RtxOptionFlags::NoSave, ""); RTX_OPTION("rtx", uint32_t, skyDrawcallIdThreshold, 0, "It's common in games to render the skybox first, and so, this value provides a simple mechanism to identify those early draw calls that are untextured (textured draw calls can still use the Sky Textures functionality."); RTX_OPTION("rtx", float, skyMinZThreshold, 1.f, "If a draw call's viewport has min depth greater than or equal to this threshold, then assume that it's a sky."); - RTX_OPTION("rtx", SkyAutoDetectMode, skyAutoDetect, SkyAutoDetectMode::None, + RTX_OPTION("rtx", SkyAutoDetectMode, skyAutoDetect, SkyAutoDetectMode::None, "Automatically tag sky draw calls using various heuristics.\n" "0 = None\n" "1 = CameraPosition - assume the first seen camera position is a sky camera.\n" @@ -937,6 +950,27 @@ namespace dxvk { "So with this option enabled, that geometry would be promoted from sky rasterization to ray tracing."); RTX_OPTION("rtx", float, skyReprojectScale, 16.0f, "Scaling of the sky geometry on reprojection to main camera space."); + + RTX_OPTION("rtx", bool, skySharedDepth, false, "By default the sky box will write to a seperate depth buffer than the main camera. Set the checkbox to cause the sky to use the same depth buffer as the main camera. Useful for source engine games."); + RTX_OPTION("rtx", bool, skyBoxPathTracing, false, "For games with skyboxes, the geometry is usually rasterized immediately into the sky camera buffer, preventing it from being path traced. Enable this setting to translate the skybox instances to the main camera."); + + RTX_OPTION("rtx", SkyScaleOffsetFormula, skyScaleOffsetFormula, SkyScaleOffsetFormula::Origin, + "How we should determine the offset to use when translating the skybox to the main view.\n" + "0 = Origin - assume the rendered skybox does not move relative to anything, and is fixed in it's coordinate space.\n" + "1 = Linear - assume the movement from the main camera effects the position of the rendered skyboxes by some linear offset.\n" + "2 = SourceEngine - assume the movement from the main camera effects the skybox instances hyperbolically. This formula is directly tailored to the source engine.\n" + "Note: The scale found in the sky calibration may be used to calculate the offset for more dynamic formulas."); + + RTX_OPTION("rtx", uint32_t, skyDefaultScale, 1, "The default / fallback scale to use when scaling the sky instances."); + + RTX_OPTION("rtx", SkyScaleCalibrationMode, skyScaleCalibrationMode, SkyScaleCalibrationMode::Fixed, + "How should we determine the scale and offset to use when scaling the skybox instances to the main view.\n" + "0 = Fixed - Use the single specified scale no matter what the game is doing.\n" + "1 = DeltaAutomatic - Use the change in positions of the main camera and sky camera to determine the skybox scale. Reliable if your skybox is a function of movement.\n" + "2 = SourceEngineAutomatic - Uses a reversed algorithm of the hyperbolic source engine formula to approximate the scale. Useful for source games with levels that have dynamic scaling skyboxes.\n" + "Note: Source Engine Calibration should only really be used in source games if you have an inconsistent scale, or the player may not move for a few frames. Delta or Fixed is far more reliable."); + + // TODO (REMIX-656): Remove this once we can transition content to new hash RTX_OPTION("rtx", bool, logLegacyHashReplacementMatches, false, ""); @@ -945,7 +979,7 @@ namespace dxvk { RTX_OPTION("rtx", bool, useBuffersDirectly, true, "When enabled Remix will use the incoming vertex buffers directly where possible instead of copying data. Note: setting the d3d9.allowDiscard to False will disable this option."); RTX_OPTION("rtx", bool, alwaysCopyDecalGeometries, true, "When set to True tells the geometry processor to always copy decals geometry. This is an optimization flag to experiment with when rtx.useBuffersDirectly is True."); - RTX_OPTION("rtx", bool, ignoreLastTextureStage, false, + RTX_OPTION("rtx", bool, ignoreLastTextureStage, false, "Removes the last texture bound to a draw call, when using fixed-function pipeline. Primary textures are untouched.\n" "Might be set to true, if a game applies a lightmap as last shading step, to omit the original lightmap data."); @@ -1013,7 +1047,7 @@ namespace dxvk { // Note: Clamp to positive values as negative luminance thresholds are not valid. RTX_OPTION_CLAMP_MIN(fireflyFilteringLuminanceThreshold, 0.0f); RTX_OPTION_CLAMP(vertexColorStrength, 0.0f, 1.0f); - + // Render pass modes //renderPassVolumeIntegrateRaytraceMode = (RenderPassVolumeIntegrateRaytraceMode) std::min( @@ -1027,7 +1061,7 @@ namespace dxvk { renderPassIntegrateDirectRaytraceModeRef() = (RenderPassIntegrateDirectRaytraceMode) std::min( (uint32_t) renderPassIntegrateDirectRaytraceMode(), (uint32_t) (RenderPassIntegrateDirectRaytraceMode::Count) - 1); - + renderPassIntegrateIndirectRaytraceModeRef() = (RenderPassIntegrateIndirectRaytraceMode) std::min( (uint32_t) renderPassIntegrateIndirectRaytraceMode(), (uint32_t) (RenderPassIntegrateIndirectRaytraceMode::Count) - 1); @@ -1048,11 +1082,11 @@ namespace dxvk { RTX_OPTION_CLAMP(resolveOpaquenessThreshold, resolveTransparencyThreshold(), 1.f); // PSR Options - + // Note: Clamped due to 8 bit usage on GPU. RTX_OPTION_CLAMP(psrrMaxBounces, static_cast(1), static_cast(254)); RTX_OPTION_CLAMP(pstrMaxBounces, static_cast(1), static_cast(254)); - + // Path Options RTX_OPTION_CLAMP(russianRouletteMaxContinueProbability, 0.0f, 1.0f); // Note: Clamped to 15 due to usage on GPU. @@ -1127,7 +1161,7 @@ namespace dxvk { // Note: Clamped to float16 max due to usage on GPU and positive values as emissive intensity values cannot be negative. RTX_OPTION_CLAMP(emissiveBlendOverrideEmissiveIntensity, 0.0f, FLOAT16_MAX); RTX_OPTION_CLAMP(particleSoftnessFactor, 0.0f, 1.0f); - + // Ray Portal Options // Note: Ensure the Ray Portal texture hashes are always in pairs of 2 auto& rayPortalModelTextureHashes = rayPortalModelTextureHashesRef(); @@ -1150,7 +1184,7 @@ namespace dxvk { assert(rayPortalSamplingWeightMinDistance() >= 0.0f); assert(rayPortalSamplingWeightMaxDistance() >= 0.0f); assert(rayPortalSamplingWeightMinDistance() <= rayPortalSamplingWeightMaxDistance()); - + // View Distance Options RTX_OPTION_CLAMP_MIN(viewDistanceOptions.distanceThreshold, 0.0f); @@ -1312,7 +1346,7 @@ namespace dxvk { bool areIndirectTranslucentShadowsEnabled() const { return enableIndirectTranslucentShadows(); } float getResolveTransparencyThreshold() const { return resolveTransparencyThreshold(); } float getResolveOpaquenessThreshold() const { return resolveOpaquenessThreshold(); } - + // Returns shared enablement composed of multiple enablement inputs bool needsMeshBoundingBox(); @@ -1323,7 +1357,7 @@ namespace dxvk { uint8_t getPSTRMaxBounces() const { return pstrMaxBounces(); } bool isPSTROutgoingSplitApproximationEnabled() const { return enablePSTROutgoingSplitApproximation(); } bool isPSTRSecondaryIncidentSplitApproximationEnabled() const { return enablePSTRSecondaryIncidentSplitApproximation(); } - + bool getIsShaderExecutionReorderingSupported() const { return isShaderExecutionReorderingSupported(); } void setIsShaderExecutionReorderingSupported(bool enabled) { isShaderExecutionReorderingSupportedRef() = enabled; } //bool isShaderExecutionReorderingInVolumeIntegrateEnabled() const { return enableShaderExecutionReorderingInVolumeIntegrate && isShaderExecutionReorderingSupported; } @@ -1388,7 +1422,7 @@ namespace dxvk { float getFogRemapMaxDistanceMax() const { return fogRemapMaxDistanceMax(); } float getFogRemapTransmittanceMeasurementDistanceMin() const { return fogRemapTransmittanceMeasurementDistanceMin(); } float getFogRemapTransmittanceMeasurementDistanceMax() const { return fogRemapTransmittanceMeasurementDistanceMax(); } - + // Alpha Test/Blend Options bool isAlphaBlendEnabled() const { return enableAlphaBlend(); } bool isAlphaTestEnabled() const { return enableAlphaTest(); } @@ -1419,7 +1453,7 @@ namespace dxvk { uint getInstanceOverrideInstanceIdx() { return instanceOverrideInstanceIdx(); } uint getInstanceOverrideInstanceIdxRange() { return instanceOverrideInstanceIdxRange(); } bool getInstanceOverrideSelectedPrintMaterialHash() { return instanceOverrideSelectedInstancePrintMaterialHash(); } - + bool getIsOpacityMicromapSupported() const { return opacityMicromap.isSupported; } void setIsOpacityMicromapSupported(bool enabled) { opacityMicromap.isSupported = enabled; } bool getEnableOpacityMicromap() const { return opacityMicromap.enable() && opacityMicromap.isSupported; } @@ -1455,11 +1489,11 @@ namespace dxvk { float getCaptureMeshTexcoordDelta() const { return captureMeshTexcoordDelta(); } float getCaptureMeshColorDelta() const { return captureMeshColorDelta(); } float getCaptureMeshBlendWeightDelta() const { return captureMeshBlendWeightDelta(); } - - + + bool isUseVirtualShadingNormalsForDenoisingEnabled() const { return useVirtualShadingNormalsForDenoising(); } bool isResetDenoiserHistoryOnSettingsChangeEnabled() const { return resetDenoiserHistoryOnSettingsChange(); } - + int32_t getPresentThrottleDelay() const { return enablePresentThrottle() ? presentThrottleDelay() : 0; } bool getValidateCPUIndexData() const { return validateCPUIndexData(); }