Skip to content

Commit c9fa6da

Browse files
Merge branch 'underwater' into 'main'
[REMIX-2660] Support starting in translucent material based on fog state. See merge request lightspeedrtx/dxvk-remix-nv!1147
2 parents 5620baa + 62f50cb commit c9fa6da

File tree

8 files changed

+218
-54
lines changed

8 files changed

+218
-54
lines changed

src/dxvk/imgui/dxvk_imgui.cpp

+71-1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ namespace dxvk {
132132
uint32_t textureFeatureFlags = 0;
133133
};
134134
std::unordered_map<XXH64_hash_t, ImGuiTexture> g_imguiTextureMap;
135+
fast_unordered_cache<FogState> g_imguiFogMap;
136+
XXH64_hash_t g_usedFogStateHash;
137+
std::mutex g_imguiFogMapMutex; // protects g_imguiFogMap
135138

136139
struct RtxTextureOption {
137140
const char* uniqueId;
@@ -478,6 +481,12 @@ namespace dxvk {
478481
g_imguiTextureMap.erase(hash);
479482
}
480483

484+
void ImGUI::SetFogStates(const fast_unordered_cache<FogState>& fogStates, XXH64_hash_t usedFogHash) {
485+
const std::lock_guard<std::mutex> lock(g_imguiFogMapMutex);
486+
g_imguiFogMap = fogStates;
487+
g_usedFogStateHash = usedFogHash;
488+
}
489+
481490
void ImGUI::wndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
482491
ImGui::SetCurrentContext(m_context);
483492
ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam);
@@ -2264,7 +2273,6 @@ namespace dxvk {
22642273

22652274
ImGui::Unindent();
22662275
}
2267-
22682276
ImGui::Unindent();
22692277
}
22702278

@@ -2273,6 +2281,68 @@ namespace dxvk {
22732281

22742282
showMaterialOptions();
22752283

2284+
if (ImGui::CollapsingHeader("Fog Tuning", collapsingHeaderClosedFlags)) {
2285+
ImGui::Indent();
2286+
ImGui::PushID("FogInfos");
2287+
if (ImGui::CollapsingHeader("Explanation", collapsingHeaderClosedFlags)) {
2288+
ImGui::Indent();
2289+
ImGui::TextWrapped("In D3D9, every draw call comes with its own fog settings."
2290+
" In Remix pathtracing, all rays need to use the same fog setting."
2291+
" So Remix will choose the earliest valid non-sky fog to use."
2292+
2293+
"\n\nIn some games, fog can be used to indicate the player is inside some "
2294+
"translucent medium, like being underwater. In path tracing this is "
2295+
"better represented as starting inside a translucent material. To "
2296+
"support this, you can copy one or more of the fog hashes listed below, "
2297+
"and specify a translucent replacement material in your mod.usda."
2298+
2299+
"\n\nThis replacement material should share transmittance and ior properties"
2300+
" with your water material, but does not need any textures set."
2301+
2302+
"\n\nReplacing a given fog state with a translucent material will disable that "
2303+
"fog."
2304+
);
2305+
ImGui::Unindent();
2306+
}
2307+
2308+
constexpr static const char* fogModes[] = {
2309+
"D3DFOG_NONE",
2310+
"D3DFOG_EXP",
2311+
"D3DFOG_EXP2",
2312+
"D3DFOG_LINEAR",
2313+
};
2314+
2315+
{
2316+
const std::lock_guard<std::mutex> lock(g_imguiFogMapMutex);
2317+
for (const auto& pair : g_imguiFogMap) {
2318+
const std::string hashString = hashToString(pair.first);
2319+
const char* replaced = ctx->getCommonObjects()->getSceneManager().getAssetReplacer()->getReplacementMaterial(pair.first) ?
2320+
" (Replaced)" : "";
2321+
const char* usedAsMain = (g_usedFogStateHash == pair.first) ? " (Used for Rendering)" : "";
2322+
ImGui::Text("Hash: %s%s%s", hashString.c_str(), replaced, usedAsMain);
2323+
const FogState& fog = pair.second;
2324+
ImGui::Indent();
2325+
2326+
if (ImGui::Button(str::format("Copy hash to clipboard##fog_list", hashString).c_str())) {
2327+
ImGui::SetClipboardText(hashString.c_str());
2328+
}
2329+
if (uint32_t(fog.mode) < 4) {
2330+
ImGui::Text("Mode: %s", fogModes[uint32_t(fog.mode)]);
2331+
} else {
2332+
ImGui::Text("Mode: unknown enum value: %u", uint32_t(fog.mode));
2333+
}
2334+
ImGui::Text("Color: %.2f %.2f %.2f", fog.color.r, fog.color.g, fog.color.b);
2335+
ImGui::Text("Scale: %.2f", fog.scale);
2336+
ImGui::Text("End: %.2f", fog.end);
2337+
ImGui::Text("Density: %.2f", fog.density);
2338+
2339+
ImGui::Unindent();
2340+
}
2341+
}
2342+
ImGui::PopID();
2343+
ImGui::Unindent();
2344+
}
2345+
22762346
separator();
22772347
ImGui::EndTabItem();
22782348
}

src/dxvk/imgui/dxvk_imgui.h

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ namespace dxvk {
9696
static void AddTexture(const XXH64_hash_t hash, const Rc<DxvkImageView>& imageView, uint32_t textureFeatureFlags);
9797
static void ReleaseTexture(const XXH64_hash_t hash);
9898
static bool checkHotkeyState(const VirtualKeys& virtKeys, const bool allowContinuousPress = false);
99+
static void SetFogStates(const fast_unordered_cache<FogState>& fogStates, XXH64_hash_t usedFogHash);
99100

100101
void switchMenu(UIType type, bool force = false);
101102

src/dxvk/rtx_render/rtx_context.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,7 @@ namespace dxvk {
11091109

11101110
constants.volumeArgs = getSceneManager().getVolumeManager().getVolumeArgs(cameraManager,
11111111
rtOutput.m_froxelVolumeExtent, rtOutput.m_numFroxelVolumes, getSceneManager().getFogState(), enablePortalVolumes);
1112+
constants.startInMediumMaterialIndex = getSceneManager().getStartInMediumMaterialIndex();
11121113
RtxOptions::Get()->opaqueMaterialOptions.fillShaderParams(constants.opaqueMaterialArgs);
11131114
RtxOptions::Get()->translucentMaterialOptions.fillShaderParams(constants.translucentMaterialArgs);
11141115
RtxOptions::Get()->viewDistanceOptions.fillShaderParams(constants.viewDistanceArgs, RtxOptions::Get()->getMeterToWorldUnitScale());

src/dxvk/rtx_render/rtx_scene_manager.cpp

+89-46
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ namespace dxvk {
482482
}
483483

484484
m_activePOMCount = 0;
485+
m_startInMediumMaterialIndex = BINDING_INDEX_INVALID;
485486

486487
if (m_uniqueObjectSearchDistance != RtxOptions::uniqueObjectDistance()) {
487488
m_uniqueObjectSearchDistance = RtxOptions::uniqueObjectDistance();
@@ -503,8 +504,29 @@ namespace dxvk {
503504
return;
504505
}
505506

506-
if (m_fog.mode == D3DFOG_NONE && input.getFogState().mode != D3DFOG_NONE) {
507-
m_fog = input.getFogState();
507+
if (input.getFogState().mode != D3DFOG_NONE) {
508+
XXH64_hash_t fogHash = input.getFogState().getHash();
509+
if (m_fogStates.find(fogHash) == m_fogStates.end()) {
510+
// Only do anything if we haven't seen this fog before.
511+
m_fogStates[fogHash] = input.getFogState();
512+
513+
MaterialData* pFogReplacement = m_pReplacer->getReplacementMaterial(fogHash);
514+
if (pFogReplacement) {
515+
// Fog has been replaced by a translucent material to start the camera in,
516+
// meaning that it was being used to indicate 'underwater' or something similar.
517+
if (pFogReplacement->getType() != MaterialDataType::Translucent) {
518+
Logger::warn(str::format("Fog replacement materials must be translucent. Ignoring material for ", std::hex, m_fog.getHash()));
519+
} else {
520+
std::optional<RtSurfaceMaterial> surfaceMaterial {};
521+
522+
createSurfaceMaterial(ctx, surfaceMaterial, *pFogReplacement, input);
523+
m_startInMediumMaterialIndex = m_surfaceMaterialCache.track(*surfaceMaterial);
524+
}
525+
} else if (m_fog.mode == D3DFOG_NONE) {
526+
// render the first unreplaced fog.
527+
m_fog = input.getFogState();
528+
}
529+
}
508530
}
509531

510532
// Get Material and Mesh replacements
@@ -718,7 +740,9 @@ namespace dxvk {
718740
}
719741

720742
void SceneManager::clearFogState() {
743+
ImGUI::SetFogStates(m_fogStates, m_fog.getHash());
721744
m_fog = FogState();
745+
m_fogStates.clear();
722746
}
723747

724748
void SceneManager::updateBufferCache(RaytraceGeometry& newGeoData) {
@@ -879,18 +903,67 @@ namespace dxvk {
879903
const auto renderMaterialDataType = renderMaterialData.getType();
880904
std::optional<RtSurfaceMaterial> surfaceMaterial{};
881905

906+
createSurfaceMaterial(ctx, surfaceMaterial, renderMaterialData, drawCallState);
907+
908+
assert(surfaceMaterial.has_value());
909+
assert(surfaceMaterial->validate());
910+
911+
// Cache this
912+
m_surfaceMaterialCache.track(*surfaceMaterial);
913+
914+
RtInstance* instance = m_instanceManager.processSceneObject(m_cameraManager, m_rayPortalManager, *pBlas, drawCallState, renderMaterialData, *surfaceMaterial);
915+
916+
// Check if a light should be created for this Material
917+
if (instance && RtxOptions::Get()->shouldConvertToLight(drawCallState.getMaterialData().getHash())) {
918+
createEffectLight(ctx, drawCallState, instance);
919+
}
920+
921+
const bool objectPickingActive = m_device->getCommon()->getResources().getRaytracingOutput()
922+
.m_primaryObjectPicking.isValid();
923+
924+
if (objectPickingActive && instance && g_allowMappingLegacyHashToObjectPickingValue) {
925+
auto meta = DrawCallMetaInfo {};
926+
{
927+
XXH64_hash_t h;
928+
h = drawCallState.getMaterialData().getColorTexture().getImageHash();
929+
if (h != kEmptyHash) {
930+
meta.legacyTextureHash = h;
931+
}
932+
h = drawCallState.getMaterialData().getColorTexture2().getImageHash();
933+
if (h != kEmptyHash) {
934+
meta.legacyTextureHash2 = h;
935+
}
936+
}
937+
938+
{
939+
std::lock_guard lock { m_drawCallMeta.mutex };
940+
auto [iter, isNew] = m_drawCallMeta.infos[m_drawCallMeta.ticker].emplace(instance->surface.objectPickingValue, meta);
941+
ONCE_IF_FALSE(isNew, Logger::warn(
942+
"Found multiple draw calls with the same \'objectPickingValue\'. "
943+
"Ignoring further MetaInfo-s, some objects might be not be available through object picking"));
944+
}
945+
}
946+
947+
return instance ? instance->getId() : UINT64_MAX;
948+
}
949+
950+
void SceneManager::createSurfaceMaterial( Rc<DxvkContext> ctx,
951+
std::optional<RtSurfaceMaterial>& surfaceMaterial,
952+
const MaterialData& renderMaterialData,
953+
const DrawCallState& drawCallState) {
954+
ScopedCpuProfileZone();
882955
const bool hasTexcoords = drawCallState.hasTextureCoordinates();
956+
const auto renderMaterialDataType = renderMaterialData.getType();
883957

884958
// We're going to use this to create a modified sampler for replacement textures.
885959
// Legacy and replacement materials should follow same filtering but due to lack of override capability per texture
886960
// legacy textures use original sampler to stay true to the original intent while replacements use more advanced filtering
887961
// for better quality by default.
888962
const Rc<DxvkSampler>& originalSampler = drawCallState.getMaterialData().getSampler(); // convenience variable for debug
889963
Rc<DxvkSampler> sampler = originalSampler;
890-
const bool isLegacyMaterial = (renderMaterialDataType == MaterialDataType::Legacy);
891964
// If the original sampler if valid and the new rendering material is not legacy type
892965
// go ahead with patching and maybe merging the sampler states
893-
if(originalSampler != nullptr && !isLegacyMaterial) {
966+
if(originalSampler != nullptr && renderMaterialDataType != MaterialDataType::Legacy) {
894967
DxvkSamplerCreateInfo samplerInfo = originalSampler->info(); // Use sampler create info struct as convenience
895968
renderMaterialData.populateSamplerInfo(samplerInfo);
896969

@@ -900,7 +973,7 @@ namespace dxvk {
900973
}
901974
uint32_t samplerIndex = trackSampler(sampler);
902975

903-
if (isLegacyMaterial || renderMaterialDataType == MaterialDataType::Opaque || drawCallState.isUsingRaytracedRenderTarget) {
976+
if (renderMaterialDataType == MaterialDataType::Legacy || renderMaterialDataType == MaterialDataType::Opaque || drawCallState.isUsingRaytracedRenderTarget) {
904977
uint32_t albedoOpacityTextureIndex = kSurfaceMaterialInvalidTextureIndex;
905978
uint32_t normalTextureIndex = kSurfaceMaterialInvalidTextureIndex;
906979
uint32_t tangentTextureIndex = kSurfaceMaterialInvalidTextureIndex;
@@ -1109,46 +1182,6 @@ namespace dxvk {
11091182

11101183
surfaceMaterial.emplace(rayPortalSurfaceMaterial);
11111184
}
1112-
assert(surfaceMaterial.has_value());
1113-
assert(surfaceMaterial->validate());
1114-
1115-
// Cache this
1116-
m_surfaceMaterialCache.track(*surfaceMaterial);
1117-
1118-
RtInstance* instance = m_instanceManager.processSceneObject(m_cameraManager, m_rayPortalManager, *pBlas, drawCallState, renderMaterialData, *surfaceMaterial);
1119-
1120-
// Check if a light should be created for this Material
1121-
if (instance && RtxOptions::Get()->shouldConvertToLight(drawCallState.getMaterialData().getHash())) {
1122-
createEffectLight(ctx, drawCallState, instance);
1123-
}
1124-
1125-
const bool objectPickingActive = m_device->getCommon()->getResources().getRaytracingOutput()
1126-
.m_primaryObjectPicking.isValid();
1127-
1128-
if (objectPickingActive && instance && g_allowMappingLegacyHashToObjectPickingValue) {
1129-
auto meta = DrawCallMetaInfo {};
1130-
{
1131-
XXH64_hash_t h;
1132-
h = drawCallState.getMaterialData().getColorTexture().getImageHash();
1133-
if (h != kEmptyHash) {
1134-
meta.legacyTextureHash = h;
1135-
}
1136-
h = drawCallState.getMaterialData().getColorTexture2().getImageHash();
1137-
if (h != kEmptyHash) {
1138-
meta.legacyTextureHash2 = h;
1139-
}
1140-
}
1141-
1142-
{
1143-
std::lock_guard lock { m_drawCallMeta.mutex };
1144-
auto [iter, isNew] = m_drawCallMeta.infos[m_drawCallMeta.ticker].emplace(instance->surface.objectPickingValue, meta);
1145-
ONCE_IF_FALSE(isNew, Logger::warn(
1146-
"Found multiple draw calls with the same \'objectPickingValue\'. "
1147-
"Ignoring further MetaInfo-s, some objects might be not be available through object picking"));
1148-
}
1149-
}
1150-
1151-
return instance ? instance->getId() : UINT64_MAX;
11521185
}
11531186

11541187
std::optional<XXH64_hash_t> SceneManager::findLegacyTextureHashByObjectPickingValue(uint32_t objectPickingValue) {
@@ -1377,7 +1410,10 @@ namespace dxvk {
13771410
if (m_surfaceMaterialCache.getTotalCount() > 0) {
13781411
ScopedGpuProfileZone(ctx, "updateSurfaceMaterials");
13791412
// Note: We duplicate the materials in the buffer so we don't have to do pointer chasing on the GPU (i.e. rather than BLAS->Surface->Material, do, BLAS->Surface, BLAS->Material)
1380-
const auto surfaceMaterialsGPUSize = m_accelManager.getSurfaceCount() * kSurfaceMaterialGPUSize;
1413+
size_t surfaceMaterialsGPUSize = m_accelManager.getSurfaceCount() * kSurfaceMaterialGPUSize;
1414+
if (m_startInMediumMaterialIndex != BINDING_INDEX_INVALID) {
1415+
surfaceMaterialsGPUSize += kSurfaceMaterialGPUSize;
1416+
}
13811417

13821418
info.size = align(surfaceMaterialsGPUSize, kBufferAlignment);
13831419
info.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
@@ -1394,6 +1430,13 @@ namespace dxvk {
13941430
surfaceIndex++;
13951431
}
13961432

1433+
if (m_startInMediumMaterialIndex != BINDING_INDEX_INVALID) {
1434+
auto&& surfaceMaterial = m_surfaceMaterialCache.getObjectTable()[m_startInMediumMaterialIndex];
1435+
surfaceMaterial.writeGPUData(surfaceMaterialsGPUData.data(), dataOffset, surfaceIndex);
1436+
m_startInMediumMaterialIndex = surfaceIndex;
1437+
surfaceIndex++;
1438+
}
1439+
13971440
assert(dataOffset == surfaceMaterialsGPUSize);
13981441
assert(surfaceMaterialsGPUData.size() == surfaceMaterialsGPUSize);
13991442

src/dxvk/rtx_render/rtx_scene_manager.h

+10
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,10 @@ class SceneManager : public CommonDeviceObject, public ResourceCache {
178178
RtCamera& getCamera() { return m_cameraManager.getMainCamera(); }
179179

180180
FogState& getFogState() { return m_fog; }
181+
const fast_unordered_cache<FogState>& getFogStates() const { return m_fogStates; }
181182
void clearFogState();
183+
184+
uint32_t getStartInMediumMaterialIndex() { return m_startInMediumMaterialIndex; }
182185

183186
uint32_t getActivePOMCount() {return m_activePOMCount;}
184187

@@ -225,6 +228,11 @@ class SceneManager : public CommonDeviceObject, public ResourceCache {
225228
// Consumes a draw call state and updates the scene state accordingly
226229
uint64_t processDrawCallState(Rc<DxvkContext> ctx, const DrawCallState& blasInput, const MaterialData* replacementMaterialData);
227230

231+
void createSurfaceMaterial( Rc<DxvkContext> ctx,
232+
std::optional<RtSurfaceMaterial>& surfaceMaterial,
233+
const MaterialData& renderMaterialData,
234+
const DrawCallState& drawCallState);
235+
228236
// Updates ref counts for new buffers
229237
void updateBufferCache(RaytraceGeometry& newGeoData);
230238

@@ -270,6 +278,8 @@ class SceneManager : public CommonDeviceObject, public ResourceCache {
270278
std::unique_ptr<TerrainBaker> m_terrainBaker;
271279

272280
FogState m_fog;
281+
fast_unordered_cache<FogState> m_fogStates;
282+
uint32_t m_startInMediumMaterialIndex = BINDING_INDEX_INVALID;
273283

274284
// TODO: Move the following resources and getters to RtResources class
275285
Rc<DxvkBuffer> m_surfaceMaterialBuffer;

src/dxvk/rtx_render/rtx_types.h

+4
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,10 @@ struct FogState {
402402
float scale = 0.f;
403403
float end = 0.f;
404404
float density = 0.f;
405+
406+
XXH64_hash_t getHash() const {
407+
return XXH3_64bits(this, sizeof(FogState));
408+
}
405409
};
406410

407411
enum class InstanceCategories : uint32_t {

0 commit comments

Comments
 (0)