Skip to content

Commit 4bdc401

Browse files
committed
Merge branch 'xbei/option_layers_4' into 'main'
[REMIX-4106] Option Layer Refactoring See merge request lightspeedrtx/dxvk-remix-nv!1717
2 parents 4e0526f + 39a54e6 commit 4bdc401

File tree

4 files changed

+144
-123
lines changed

4 files changed

+144
-123
lines changed

src/dxvk/dxvk_instance.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -707,13 +707,13 @@ namespace dxvk {
707707
// Set config so that any rtx option initialized later will use the value in that config object
708708
// The start-up config contains the values from the code and dxvk.conf, only.
709709
RtxOption<bool>::setStartupConfig(m_config);
710-
RtxOptionImpl::getRtxOptionLayerMap().insert(RtxOptionLayer(m_config, "dxvk.conf", 1, 1.0f, 1.0f));
710+
RtxOptionImpl::addRtxOptionLayer(RtxOptionLayer(m_config, "dxvk.conf", 1, 1.0f, 1.0f));
711711
Logger::info("Set startup config.");
712712
} else if constexpr ((type == Config::Type_RtxUser) || (type == Config::Type_RtxMod)) {
713713
// Set custom config after the RTX user config has been merged into the config and
714714
// update the RTX options. Contains values from rtx.conf
715715
RtxOption<bool>::setCustomConfig(m_config);
716-
RtxOptionImpl::getRtxOptionLayerMap().insert(RtxOptionLayer(m_config, "rtx.conf", 2, 1.0f, 1.0f));
716+
RtxOptionImpl::addRtxOptionLayer(RtxOptionLayer(m_config, "rtx.conf", 2, 1.0f, 1.0f));
717717
Logger::info("Set custom config.");
718718
}
719719
}

src/dxvk/imgui/dxvk_imgui.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,7 @@ namespace dxvk {
19941994
ImGui::Dummy(ImVec2(0.0f, 5.0f));
19951995
const std::string optionLayerText = std::to_string(optionLayerCounter++) + ". " + optionLayer.getName();
19961996
const std::string optionLayerStrengthText = optionLayer.getName() + " Strength";
1997+
const std::string optionLayerThresholdText = optionLayer.getName() + " Threshold";
19971998
if (ImGui::Checkbox(optionLayerText.c_str(), &optionLayer.isEnabledRef())) {
19981999
optionLayer.setDirty(true);
19992000
}
@@ -2003,6 +2004,11 @@ namespace dxvk {
20032004
optionLayer.setBlendStrengthDirty(true);
20042005
}
20052006

2007+
if (IMGUI_ADD_TOOLTIP(ImGui::SliderFloat(optionLayerThresholdText.c_str(), &optionLayer.getBlendThresholdRef(), 0.0f, 1.0f),
2008+
"Sets the blending strength threshold for this option layer. Only applicable to non-float variables. The option is applied only when the blend strength exceeds this threshold.")) {
2009+
optionLayer.setBlendStrengthDirty(true);
2010+
}
2011+
20062012
if (ImGui::CollapsingHeader((optionLayer.getName() + " Details").c_str(), collapsingHeaderClosedFlags)) {
20072013
ImGui::Indent();
20082014
const std::string priorityText = "Priority: " + std::to_string(optionLayer.getPriority());

src/dxvk/rtx_render/rtx_option.cpp

Lines changed: 67 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -255,11 +255,30 @@ namespace dxvk {
255255
releaseValue(optionLayer.second.value, type);
256256
}
257257

258-
for (int i = 0; i < (int)ValueType::Count; i++) {
259-
releaseValue(valueList[i], type);
258+
releaseValue(resolvedValue, type);
259+
}
260+
261+
const GenericValue& RtxOptionImpl::getGenericValue(const ValueType valueType) const {
262+
if (valueType == ValueType::DefaultValue) {
263+
return optionLayerValueQueue.at(0).value;
264+
} else if (valueType == ValueType::PendingValue) {
265+
return optionLayerValueQueue.begin()->second.value;
266+
} else if (valueType == ValueType::Value) {
267+
return resolvedValue;
268+
} else {
269+
Logger::warn("[RTX Option]: Unknown generic value type.");
270+
static const GenericValue dummyValue {};
271+
return dummyValue;
260272
}
261273
}
262274

275+
GenericValue& RtxOptionImpl::getGenericValue(const ValueType valueType) {
276+
// Reuse the const overload to avoid duplicating switch logic.
277+
// const_cast is safe here because we are returning a non-const reference
278+
// only when called on a non-const RtxOptionImpl instance.
279+
return const_cast<GenericValue&>(static_cast<const RtxOptionImpl*>(this)->getGenericValue(valueType));
280+
}
281+
263282
const char* RtxOptionImpl::getTypeString() const {
264283
switch (type) {
265284
case OptionType::Bool: return "bool";
@@ -280,7 +299,7 @@ namespace dxvk {
280299
}
281300

282301
std::string RtxOptionImpl::genericValueToString(ValueType valueType) const {
283-
const GenericValue& value = valueList[static_cast<int>(valueType)];
302+
const auto& value = getGenericValue(valueType);
284303
return genericValueToString(value);
285304
}
286305

@@ -334,7 +353,7 @@ namespace dxvk {
334353
}
335354

336355
bool RtxOptionImpl::clampValue(ValueType valueType) {
337-
GenericValue& value = valueList[static_cast<int>(valueType)];
356+
auto& value = getGenericValue(valueType);
338357
bool changed = false;
339358

340359
switch (type) {
@@ -446,17 +465,17 @@ namespace dxvk {
446465

447466
void RtxOptionImpl::readOption(const Config& options, RtxOptionImpl::ValueType valueType) {
448467
const std::string fullName = getFullName();
449-
auto& value = valueList[(int) valueType];
468+
auto& value = getGenericValue(valueType);
450469
readValue(options, fullName, value);
451470

452471
clampValue(valueType);
453472

454473
if (valueType == ValueType::PendingValue) {
455474
// If reading into the pending value, need to mark the option as dirty so it gets copied to the value at the end of the frame.
456-
markDirty();
475+
// markDirty();
457476
} else if (valueType == ValueType::Value) {
458477
// If reading into the value, need to immediately copy to the pending value so they stay in sync.
459-
copyValue(ValueType::Value, ValueType::PendingValue);
478+
copyValue(resolvedValue, getGenericValue(ValueType::PendingValue));
460479

461480
// Also mark the option dirty so the onChange callback is invoked at the normal time.
462481
markDirty();
@@ -468,7 +487,7 @@ namespace dxvk {
468487
return;
469488

470489
std::string fullName = getFullName();
471-
auto& value = valueList[(int) ValueType::Value];
490+
auto& value = resolvedValue;
472491

473492
if (changedOptionOnly) {
474493
if (isDefault()) {
@@ -518,6 +537,15 @@ namespace dxvk {
518537
}
519538
}
520539

540+
void RtxOptionImpl::insertEmptyOptionLayer(const uint32_t priority, const float blendStrength, const float blendStrengthThreshold) {
541+
GenericValue optionLayerValue = createGenericValue(type);
542+
const PrioritizedValue newValue(optionLayerValue, priority, blendStrength, blendStrengthThreshold);
543+
auto [it, inserted] = optionLayerValueQueue.emplace(priority, newValue);
544+
if (!inserted) {
545+
Logger::warn("[RTX Option]: Duplicate priority " + std::to_string(priority) + " ignored (only first kept).");
546+
}
547+
}
548+
521549
void RtxOptionImpl::insertOptionLayerValue(const GenericValue& value, const uint32_t priority, const float blendStrength, const float blendStrengthThreshold) {
522550
// Check if there's a same priority layer
523551
for (const auto& optionLayerValue : optionLayerValueQueue) {
@@ -527,21 +555,10 @@ namespace dxvk {
527555
}
528556
}
529557

530-
// Only float or float based vectors are allowed to mix with other option layers
531-
float layerBlendStrenth = blendStrength;
532-
if (type != OptionType::Float && type != OptionType::Vector2 && type != OptionType::Vector3 && type != OptionType::Vector4) {
533-
// For disallowed types, snap the strength to either 0.0f or 1.0f, based on whether it passes the strength threshold
534-
if (blendStrength >= blendStrengthThreshold || priority == 0) {
535-
layerBlendStrenth = 1.0f;
536-
} else {
537-
layerBlendStrenth = 0.0f;
538-
}
539-
}
540-
541558
GenericValue optionLayerValue = createGenericValue(type);
542559
copyValue(value, optionLayerValue);
543560

544-
const PrioritizedValue newValue { optionLayerValue, priority, layerBlendStrenth };
561+
const PrioritizedValue newValue(optionLayerValue, priority, blendStrength, blendStrengthThreshold);
545562
auto [it, inserted] = optionLayerValueQueue.emplace(priority, newValue);
546563
if (!inserted) {
547564
Logger::warn("[RTX Option]: Duplicate priority " + std::to_string(priority) + " ignored (only first kept).");
@@ -557,8 +574,6 @@ namespace dxvk {
557574
insertOptionLayerValue(value.data, optionLayer.getPriority(), optionLayer.getBlendStrength(), optionLayer.getBlendStrengthThreshold());
558575
// When adding a new option layer, dirty current option
559576
markDirty();
560-
} else {
561-
Logger::warn("[RTX Option] Attempted to read option that is not defined in the option layer.");
562577
}
563578
}
564579

@@ -584,27 +599,16 @@ namespace dxvk {
584599
// Find the option layer value that the strength needs to be updated (same priority)
585600
for (auto& optionLayerValue : optionLayerValueQueue) {
586601
if (optionLayer.getPriority() == optionLayerValue.second.priority) {
587-
if (type == OptionType::Float || type == OptionType::Vector2 || type == OptionType::Vector3 || type == OptionType::Vector4) {
588-
// Only float or float based vectors are allowed to mix with other option layers
589-
optionLayerValue.second.blendStrength = optionLayer.getBlendStrength();
590-
} else {
591-
// For disallowed types, snap the strength to either 0.0f or 1.0f, based on whether it passes the strength threshold
592-
if (optionLayer.getBlendStrength() >= optionLayer.getBlendStrengthThreshold()) {
593-
optionLayerValue.second.blendStrength = 1.0f;
594-
} else {
595-
optionLayerValue.second.blendStrength = 0.0f;
596-
}
597-
}
602+
optionLayerValue.second.blendStrength = optionLayer.getBlendStrength();
603+
optionLayerValue.second.blendThreshold = optionLayer.getBlendStrengthThreshold();
598604
return;
599605
}
600606
}
601-
} else {
602-
Logger::warn("[RTX Option] Attempted to update option that is not defined in the option layer.");
603607
}
604608
}
605609

606610
bool RtxOptionImpl::isDefault() const {
607-
return isEqual(ValueType::Value, ValueType::DefaultValue);
611+
return isEqual(resolvedValue, getGenericValue(ValueType::DefaultValue));
608612
}
609613

610614
bool RtxOptionImpl::isEqual(const GenericValue& aValue, const GenericValue& bValue) const {
@@ -649,24 +653,20 @@ namespace dxvk {
649653
return false;
650654
}
651655

652-
bool RtxOptionImpl::isEqual(ValueType a, ValueType b) const {
653-
return isEqual(valueList[(int) a], valueList[(int) b]);
654-
}
655-
656656
void RtxOptionImpl::resetOption() {
657657
if (flags & (uint32_t) RtxOptionFlags::NoReset)
658658
return;
659659

660660
// If value and defaultValue are equal, no need to to change the Value.
661-
if (isEqual(ValueType::Value, ValueType::DefaultValue)) {
661+
if (isEqual(resolvedValue, getGenericValue(ValueType::DefaultValue))) {
662662
// Check if the option has a pending value, and if so reset that.
663-
if (isEqual(ValueType::PendingValue, ValueType::DefaultValue)) {
664-
copyValue(ValueType::DefaultValue, ValueType::PendingValue);
663+
if (isEqual(getGenericValue(ValueType::PendingValue), getGenericValue(ValueType::DefaultValue))) {
664+
copyValue(getGenericValue(ValueType::DefaultValue), getGenericValue(ValueType::PendingValue));
665665
}
666666
return;
667667
}
668668

669-
copyValue(ValueType::DefaultValue, ValueType::PendingValue);
669+
copyValue(getGenericValue(ValueType::DefaultValue), getGenericValue(ValueType::PendingValue));
670670
markDirty();
671671
}
672672

@@ -713,10 +713,6 @@ namespace dxvk {
713713
}
714714
}
715715

716-
void RtxOptionImpl::copyValue(ValueType source, ValueType target) {
717-
copyValue(valueList[(int) source], valueList[(int) target]);
718-
}
719-
720716
void RtxOptionImpl::addWeightedValue(const GenericValue& source, const float weight, GenericValue& target) {
721717
switch (type) {
722718
case OptionType::Float:
@@ -746,21 +742,9 @@ namespace dxvk {
746742
}
747743
}
748744

749-
void RtxOptionImpl::copyValueToOptionLayer(ValueType source) {
750-
const auto& sourceValue = valueList[(int) source];
751-
// Ensure the top-most layer is the runtime layer, insert if missing.
752-
if (optionLayerValueQueue.begin()->second.priority != RtxOptionLayer::s_runtimeOptionLayerPriority) {
753-
// Insert runtime layer with full strength so runtime overrides win immediately.
754-
insertOptionLayerValue(sourceValue, RtxOptionLayer::s_runtimeOptionLayerPriority, 1.0f, 1.0f);
755-
} else {
756-
// Runtime layer already present at top, update its value in-place.
757-
copyValue(sourceValue, optionLayerValueQueue.begin()->second.value);
758-
}
759-
}
760-
761745
void RtxOptionImpl::copyOptionLayerToValue() {
762746
GenericValue& optionValueTop = optionLayerValueQueue.begin()->second.value;
763-
const auto& pendingValue = valueList[(int) RtxOptionImpl::ValueType::PendingValue];
747+
const auto& pendingValue = getGenericValue(RtxOptionImpl::ValueType::PendingValue);
764748

765749
if (optionLayerValueQueue.begin()->second.priority == RtxOptionLayer::s_runtimeOptionLayerPriority &&
766750
!isEqual(pendingValue, optionValueTop)) {
@@ -802,18 +786,25 @@ namespace dxvk {
802786
float throughput = 1.0f;
803787
// Loop layers from highest priority to lowest to lerp the value across layers base on the blend strength of layers
804788
for (const auto& optionLayer : optionLayerValueQueue) {
805-
// Stop when the blend strength is larger than 1, because lerp(a, b, 1.0f) => b, we don't need to loop lower priority values
806-
if (optionLayer.second.blendStrength >= 1.0f) {
807-
addWeightedValue(optionLayer.second.value, throughput, optionValue.data);
808-
break;
809-
}
789+
if (type == OptionType::Float || type == OptionType::Vector2 || type == OptionType::Vector3 || type == OptionType::Vector4) {
790+
// Stop when the blend strength is larger than 1, because lerp(a, b, 1.0f) => b, we don't need to loop lower priority values
791+
if (optionLayer.second.blendStrength >= 1.0f) {
792+
addWeightedValue(optionLayer.second.value, throughput, optionValue.data);
793+
break;
794+
}
810795

811-
addWeightedValue(optionLayer.second.value, optionLayer.second.blendStrength * throughput, optionValue.data);
812-
throughput *= (1.0f - optionLayer.second.blendStrength);
796+
addWeightedValue(optionLayer.second.value, optionLayer.second.blendStrength * throughput, optionValue.data);
797+
throughput *= (1.0f - optionLayer.second.blendStrength);
798+
} else {
799+
if (optionLayer.second.blendStrength >= optionLayer.second.blendThreshold || optionLayer.second.priority == 0) {
800+
addWeightedValue(optionLayer.second.value, throughput, optionValue.data);
801+
break;
802+
}
803+
}
813804
}
814805

815-
// Copy to valueList
816-
copyValue(optionValue.data, valueList[(int) RtxOptionImpl::ValueType::Value]);
806+
// Copy to resolvedValue
807+
copyValue(optionValue.data, resolvedValue);
817808
}
818809

819810
bool RtxOptionImpl::writeMarkdownDocumentation(const char* outputMarkdownFilePath) {
@@ -1017,6 +1008,12 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
10171008
return s_rtxOptionLayers;
10181009
}
10191010

1011+
void RtxOptionImpl::addRtxOptionLayer(const RtxOptionLayer& layer) {
1012+
if (layer.isValid()) {
1013+
getRtxOptionLayerMap().insert(layer);
1014+
}
1015+
}
1016+
10201017
bool writeMarkdownDocumentation(const char* outputMarkdownFilePath) {
10211018
return dxvk::RtxOptionImpl::writeMarkdownDocumentation(outputMarkdownFilePath);
10221019
}

0 commit comments

Comments
 (0)