Skip to content

Commit

Permalink
Merge branch 'displaceOut' into 'main'
Browse files Browse the repository at this point in the history
[REMIX-3048] supporting outwards displacement with POM

See merge request lightspeedrtx/dxvk-remix-nv!973
  • Loading branch information
MarkEHenderson committed Oct 23, 2024
2 parents 8e0992a + b7636e4 commit 912a164
Show file tree
Hide file tree
Showing 34 changed files with 358 additions and 171 deletions.
2 changes: 1 addition & 1 deletion RtxOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
|rtx.terrainBaker.material.bakeReplacementMaterials|bool|True|Enables baking of replacement materials when they are present\.|
|rtx.terrainBaker.material.bakeSecondaryPBRTextures|bool|True|Enables baking of secondary textures in replacement materials when they are present\.<br>Secondary textures are all PBR textures except for albedoOpacity\. So that includes normal, roughness, etc\.|
|rtx.terrainBaker.material.maxResolutionToUseForReplacementMaterials|int|8192|Max resolution to use for preprocessing and baking of input replacement material textures other than color opacity which is used as is\.<br>Applies only to a case when a preprocessing compute shader is used to support baking of secondary PBR materials\.<br>Replacement materials need to be preprocessed prior to baking them and limitting the max resolution allows to balance the quality vs performance cost\.|
|rtx.terrainBaker.material.properties.displaceInFactor|float|1|The max depth the baked terrain can support will be larger than the max depth <br>of any incoming draw call, which results in a loss of detail\. When this is <br>too low, the displacement will lack detail\. When it is too high, the lowest <br>parts of the POM will flatten out\.|
|rtx.terrainBaker.material.properties.displaceInFactor|float|1|The max depth and height the baked terrain can support will be larger than the max <br>of any incoming draw call, which results in a loss of detail\. When this is <br>too low, the displacement will lack detail\. When it is too high, the lowest <br>and highest parts of the POM will flatten out\. This affects both displaceIn <br>and displaceOut, despite the name\.|
|rtx.terrainBaker.material.properties.emissiveColorConstant|float3|0, 0, 0|Emissive color constant\. Should be a color in sRGB colorspace with gamma encoding\.|
|rtx.terrainBaker.material.properties.emissiveIntensity|float|0|Emissive intensity\.|
|rtx.terrainBaker.material.properties.enableEmission|bool|False|A flag to determine if emission is enabled\.|
Expand Down
16 changes: 16 additions & 0 deletions documentation/RemixApiChangelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## Remix API Changelog

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.2]

### Added
- MaterialInfoOpaqueEXT.displaceOut

### Changed
- renamed MaterialInfoOpaqueEXT.heightTextureStrength to MaterialInfoOpaqueEXT.displaceIn

### Fixed

### Removed
3 changes: 1 addition & 2 deletions documentation/TerrainSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ Caveats/limitations:
- Using too high resolution replacement textures can have noticeable performance impact. Adjust [rtx.terrainBaker.material.maxResolutionToUseForReplacementMaterials](../RtxOptions.md) to balance quality vs performance cost of baking replacement textures.
- Terrain baking is not free. It has a computational and memory cost. You can parametrize its properties to suit your needs and memory limitations. In the future there may be a more adaptive mechanism to fit in-game scenario and resource availability. Tune following settings to adjust resolution and memory overhead of the Terrain System [rtx.terrainBaker.cascadeMap.*](../RtxOptions.md) and [rtx.terrainBaker.material.maxResolutionToUseForReplacementMaterials](../RtxOptions.md).
- Programmable shaders with Shader Model 2.0+ utilize a preprocessing compute pass to support baking of secondary PBR textures. This is an expensive operation both performance and memory wise. Draw calls using fixed function pipeline and programmable pipelines with Shader Model 1.0 instead rely on shader injection. This shader injection functionality is controlled via [rtx.terrainBaker.material.replacementSupportInPS_programmableShaders](../RtxOptions.md) and [rtx.terrainBaker.material.replacementSupportInPS_fixedFunction](../RtxOptions.md) that needs to be set prior to launching the game on modification. Both of these are enabled by default. While these options should work they are still in experimental stage. If you observe any crashes or quality issues which get fixed by disabling the two options then file github issues for them.
- Differences between the UV tiling size of the incoming terrain draw calls and the baked terrain map can cause precision loss in baked heightmaps. Specifically, the baked heightmap's max displacement distance may be significantly larger (or smaller) than the actual max displacement distance of any of the incoming draw calls, resulting in a loss of detail. [rtx.terrainBaker.material.properties.displaceInFactor](../RtxOptions.md) can be used to fix that by shrinking the baked heightmap's max displacement. When it is too low, the displacement will lack detail. When it is too high, the lowest parts of the POM will flatten out. The `Calculate Scene's Optimal Displace In Factor` button in the terrain material properties panel can be used to automatically calculate the highest safe value for a given scene. `rtx.conf` creators should use that button in representative sample of a game's levels, and then set `displaceInFactor` to the lowest observed value.
- Differences between the UV tiling size of the incoming terrain draw calls and the baked terrain map can cause precision loss in baked heightmaps. Specifically, the baked heightmap's max displacement distance may be significantly larger (or smaller) than the actual max displacement distance of any of the incoming draw calls, resulting in a loss of detail. [rtx.terrainBaker.material.properties.displaceInFactor](../RtxOptions.md) can be used to fix that by shrinking the baked heightmap's max displacement. When it is too low, the displacement will lack detail. When it is too high, the lowest and highest parts of the POM will flatten out. The `Calculate Scene's Optimal Displacement Factor` button in the terrain material properties panel can be used to automatically calculate the highest safe value for a given scene. `rtx.conf` creators should use that button in representative sample of a game's levels, and then set `displaceInFactor` to the lowest observed value.

Future considerations / not supported (yet):
- Material constants per input terrain replacement material.
- Supporting Quadtree POM with baked heightmaps.
- Orthogonal surfaces (i.e. walls with floor) and separate multi-layer surfaces (i.e. baked road on a bridge over another baked road below). For these cases you can use the decal system. The benefit of the terrain system to the decal system is that the terrain baking exactly replicates original game's blending while the decal system only approximates the original blending. On the other hand, decal system supports blending in any direction and the input decal textures are sampled at the desired resolution at the time of ray hits the surface while the terrain system resamples original textures to a shared terrain texture. This can result in a of loss of image fidelity depending on the parametrization of the terrain system (see [rtx.terrainBaker.*](../RtxOptions.md)).
2 changes: 1 addition & 1 deletion packman-external.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<package name="rtx-remix-nv_usd" version="5" />
</dependency>
<dependency name="omni_core_materials" linkPath="external/omni_core_materials">
<package name="rtx-remix-omni_core_materials" version="13" />
<package name="rtx-remix-omni_core_materials" version="15" />
</dependency>
<dependency name="reflex" linkPath="external/reflex">
<package name="rtx-remix-reflex" version="1" />
Expand Down
3 changes: 2 additions & 1 deletion public/include/remix/remix.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,13 +275,14 @@ namespace remix {
thinFilmThickness_value = 200.f;
alphaIsThinFilmThickness = false;
heightTexture = {};
heightTextureStrength = 0.0f;
displaceIn = 0.0f;
useDrawCallAlphaState = true;
blendType_hasvalue = false;
blendType_value = 0;
invertedBlend = false;
alphaTestType = 7;
alphaReferenceValue = 0;
displaceOut = 0.0f;
static_assert(sizeof remixapi_MaterialInfoOpaqueEXT == 112);
}

Expand Down
7 changes: 5 additions & 2 deletions public/include/remix/remix_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
#include <stdint.h>
#include <windows.h>

#ifndef REMIX_ALLOW_X86
#if _WIN64 != 1
#error Remix API requires 64-bit for the ray tracing features.
#endif
#endif


// __stdcall convention
Expand All @@ -52,7 +54,7 @@

#define REMIXAPI_VERSION_MAJOR 0
#define REMIXAPI_VERSION_MINOR 4
#define REMIXAPI_VERSION_PATCH 1
#define REMIXAPI_VERSION_PATCH 2


// External
Expand Down Expand Up @@ -194,14 +196,15 @@ extern "C" {
float thinFilmThickness_value;
remixapi_Bool alphaIsThinFilmThickness;
remixapi_Path heightTexture;
float heightTextureStrength;
float displaceIn;
// If true, InstanceInfoBlendEXT is used as a source for alpha state
remixapi_Bool useDrawCallAlphaState;
remixapi_Bool blendType_hasvalue;
int blendType_value;
remixapi_Bool invertedBlend;
int alphaTestType;
uint8_t alphaReferenceValue;
float displaceOut;
} remixapi_MaterialInfoOpaqueEXT;

// Valid only if remixapi_MaterialInfo contains remixapi_MaterialInfoOpaqueEXT in pNext chain
Expand Down
4 changes: 4 additions & 0 deletions src/d3d9/d3d9_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6665,7 +6665,9 @@ namespace dxvk {
data->Stages[i].BumpEnvLScale = bit::cast<float>(m_state.textureStages[i][DXVK_TSS_BUMPENVLSCALE]);
data->Stages[i].BumpEnvLOffset = bit::cast<float>(m_state.textureStages[i][DXVK_TSS_BUMPENVLOFFSET]);
// NV-DXVK start: support height map scaling in terrain baking
data->Stages[i].texturePreOffset = 0.f;
data->Stages[i].textureScale = 1.f;
data->Stages[i].texturePostOffset = 0.f;
// NV-DXVK end
}
}
Expand Down Expand Up @@ -7392,7 +7394,9 @@ namespace dxvk {

auto& rs = m_state.renderStates;
DecodeD3DCOLOR((D3DCOLOR) rs[D3DRS_TEXTUREFACTOR], data->textureFactor.data);
data->texturePreOffset = 0.f;
data->textureScale = 1.f;
data->texturePostOffset = 0.f;

EmitCs([
cBuffer = m_psFixedFunction,
Expand Down
64 changes: 54 additions & 10 deletions src/d3d9/d3d9_fixed_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ namespace dxvk {

// NV-DXVK start: support height map scaling in terrain baking
float_t,
float_t,
float_t,
// NV-DXVK end
};

Expand Down Expand Up @@ -422,12 +424,18 @@ namespace dxvk {
offset += sizeof(float);

// NV-DXVK start: support height map scaling in terrain baking
spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_TexturePreOffset, offset);
offset += sizeof(float);

spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_TextureScale, offset);
offset += sizeof(float);
// NV-DXVK end

// Padding...
spvModule.memberDecorateOffset(structType, stage * D3D9SharedPSStages_Count + D3D9SharedPSStages_TexturePostOffset, offset);
offset += sizeof(float);

// Padding
offset += 3 * sizeof(float);
// NV-DXVK end
}

uint32_t sharedState = spvModule.newVar(
Expand Down Expand Up @@ -540,7 +548,9 @@ namespace dxvk {

enum D3D9FFPSMembers {
TextureFactor = 0,
TextureScale = 1,
TexturePreOffset = 1,
TextureScale = 2,
TexturePostOffset = 3,

MemberCount
};
Expand All @@ -551,7 +561,9 @@ namespace dxvk {

struct {
uint32_t textureFactor;
uint32_t texturePreOffset;
uint32_t textureScale;
uint32_t texturePostOffset;
} constants;

struct {
Expand Down Expand Up @@ -1693,7 +1705,9 @@ namespace dxvk {
uint32_t textureValue,
uint32_t texcoord,
uint32_t texcoordType,
std::function<uint32_t()> loadTexturePreOffsetFnc,
std::function<uint32_t()> loadTextureScaleFnc,
std::function<uint32_t()> loadTexturePostOffsetFnc,
std::function<uint32_t()> loadAlbedoOpacityFnc,
std::function<void(uint32_t vec4value)> storeVec4ValueToRegisterFnc,
std::function<uint32_t()> loadVec4ValueFromRegisterFnc) {
Expand Down Expand Up @@ -1759,11 +1773,17 @@ namespace dxvk {
// Divide textureScale by the max input uv delta.
uint32_t textureScale = spvModule.opFDiv(floatType, loadTextureScaleFnc(), maxDelta);

// Scale the texture components towards 1 instead of towards 0 by doing:
// new value = 1 - (textureScale * (1 - value))
textureValue = spvModule.opFSub(vec4Type, spvModule.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f), textureValue);
// The height values need to scale relative to a neutral value (which is different in the input image and the output map).
// First, subtract the neutral value from the pixel color:
uint32_t texturePreOffset4 = spvModule.opVectorTimesScalar(vec4Type, spvModule.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f), loadTexturePreOffsetFnc());
textureValue = spvModule.opFAdd(vec4Type, texturePreOffset4, textureValue);

// Second, apply all of the scale operations:
textureValue = spvModule.opVectorTimesScalar(vec4Type, textureValue, textureScale);
textureValue = spvModule.opFSub(vec4Type, spvModule.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f), textureValue);

// Third, add the output neutral value:
uint32_t texturePostOffset4 = spvModule.opVectorTimesScalar(vec4Type, spvModule.constvec4f32(1.0f, 1.0f, 1.0f, 1.0f), loadTexturePostOffsetFnc());
textureValue = spvModule.opFAdd(vec4Type, texturePostOffset4, textureValue);

// The new texture value needs to be stored before branching out, and reloaded after
storeVec4ValueToRegisterFnc(textureValue);
Expand Down Expand Up @@ -1994,15 +2014,27 @@ namespace dxvk {
auto loadVec4ValueFromRegisterFnc = [&]() -> uint32_t {
return m_module.opLoad(vec4Type, registerStorage);
};
auto loadTexturePreOffsetFnc = [&]() -> uint32_t {
uint32_t texturePreOffsetOffset = m_module.constu32(D3D9SharedPSStages_Count * kTerrainBakerSecondaryTextureStage + D3D9SharedPSStages_TexturePreOffset);
uint32_t texturePreOffsetPtr = m_module.opAccessChain(m_module.defPointerType(m_floatType, spv::StorageClassUniform),
m_ps.sharedState, 1, &texturePreOffsetOffset);
return m_module.opLoad(m_floatType, texturePreOffsetPtr);
};
auto loadTextureScaleFnc = [&]() -> uint32_t {
uint32_t textureScaleOffset = m_module.constu32(D3D9SharedPSStages_Count * kTerrainBakerSecondaryTextureStage + D3D9SharedPSStages_TextureScale);
uint32_t textureScalePtr = m_module.opAccessChain(m_module.defPointerType(m_floatType, spv::StorageClassUniform),
m_ps.sharedState, 1, &textureScaleOffset);
return m_module.opLoad(m_floatType, textureScalePtr);
};
auto loadTexturePostOffsetFnc = [&]() -> uint32_t {
uint32_t texturePostOffsetOffset = m_module.constu32(D3D9SharedPSStages_Count * kTerrainBakerSecondaryTextureStage + D3D9SharedPSStages_TexturePostOffset);
uint32_t texturePostOffsetPtr = m_module.opAccessChain(m_module.defPointerType(m_floatType, spv::StorageClassUniform),
m_ps.sharedState, 1, &texturePostOffsetOffset);
return m_module.opLoad(m_floatType, texturePostOffsetPtr);
};


return postprocessTextureReadForTerrainBaking(m_module, textureValue, texcoord, texcoord_t, loadTextureScaleFnc, loadAlbedoOpacityFnc, storeVec4ValueToRegisterFnc, loadVec4ValueFromRegisterFnc);
return postprocessTextureReadForTerrainBaking(m_module, textureValue, texcoord, texcoord_t, loadTexturePreOffsetFnc, loadTextureScaleFnc, loadTexturePostOffsetFnc, loadAlbedoOpacityFnc, storeVec4ValueToRegisterFnc, loadVec4ValueFromRegisterFnc);
};

texture = postprocessColorOutputTextureRead(texture);
Expand Down Expand Up @@ -2376,8 +2408,10 @@ namespace dxvk {

// Constant Buffer for PS.
std::array<uint32_t, uint32_t(D3D9FFPSMembers::MemberCount)> members = {
m_vec4Type, // Texture Factor
m_floatType // Texture Scale
m_vec4Type, // TextureFactor
m_floatType, // TexturePreOffset
m_floatType, // TextureScale
m_floatType // TexturePostOffset
};

const uint32_t structType =
Expand All @@ -2389,12 +2423,20 @@ namespace dxvk {
m_module.memberDecorateOffset(structType, D3D9FFPSMembers::TextureFactor, offset);
offset += sizeof(Vector4);

m_module.memberDecorateOffset(structType, D3D9FFPSMembers::TexturePreOffset, offset);
offset += sizeof(float);

m_module.memberDecorateOffset(structType, D3D9FFPSMembers::TextureScale, offset);
offset += sizeof(float);

m_module.memberDecorateOffset(structType, D3D9FFPSMembers::TexturePostOffset, offset);
offset += sizeof(float);

m_module.setDebugName(structType, "D3D9FixedFunctionPS");
m_module.setDebugMemberName (structType, D3D9FFPSMembers::TextureFactor, "TextureFactor");
m_module.setDebugMemberName (structType, D3D9FFPSMembers::TexturePreOffset, "TexturePreOffset");
m_module.setDebugMemberName (structType, D3D9FFPSMembers::TextureScale, "TextureScale");
m_module.setDebugMemberName (structType, D3D9FFPSMembers::TexturePostOffset, "TexturePostOffset");

m_ps.constantBuffer = m_module.newVar(
m_module.defPointerType(structType, spv::StorageClassUniform),
Expand Down Expand Up @@ -2426,7 +2468,9 @@ namespace dxvk {
};

m_ps.constants.textureFactor = LoadConstant(m_vec4Type, uint32_t(D3D9FFPSMembers::TextureFactor));
m_ps.constants.texturePreOffset = LoadConstant(m_floatType, uint32_t(D3D9FFPSMembers::TexturePreOffset));
m_ps.constants.textureScale = LoadConstant(m_floatType, uint32_t(D3D9FFPSMembers::TextureScale));
m_ps.constants.texturePostOffset = LoadConstant(m_floatType, uint32_t(D3D9FFPSMembers::TexturePostOffset));

// Samplers
for (uint32_t i = 0; i < caps::TextureStageCount; i++) {
Expand Down
2 changes: 2 additions & 0 deletions src/d3d9/d3d9_fixed_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,9 @@ namespace dxvk {
uint32_t textureValue,
uint32_t texcoord,
uint32_t texcoordType,
std::function<uint32_t()> loadTexturePreOffsetFnc,
std::function<uint32_t()> loadTextureScaleFnc,
std::function<uint32_t()> loadTexturePostOffsetFnc,
std::function<uint32_t()> loadAlbedoOpacityFnc,
std::function<void(uint32_t vec4value)> storeVec4ValueToRegisterFnc,
std::function<uint32_t()> loadVec4ValueFromRegisterFnc);
Expand Down
10 changes: 9 additions & 1 deletion src/d3d9/d3d9_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,11 @@ namespace dxvk {

struct D3D9FixedFunctionPS {
Vector4 textureFactor;
// NV-DXVK start: support height map scaling in terrain baking
float texturePreOffset;
float textureScale;
float texturePostOffset;
// NV-DXVK end
};

enum D3D9SharedPSStages {
Expand All @@ -178,7 +182,9 @@ namespace dxvk {
D3D9SharedPSStages_BumpEnvLScale,
D3D9SharedPSStages_BumpEnvLOffset,
// NV-DXVK start: support height map scaling in terrain baking
D3D9SharedPSStages_TexturePreOffset,
D3D9SharedPSStages_TextureScale,
D3D9SharedPSStages_TexturePostOffset,
// NV-DXVK end
D3D9SharedPSStages_Count,
};
Expand All @@ -190,8 +196,10 @@ namespace dxvk {
float BumpEnvLScale;
float BumpEnvLOffset;
// NV-DXVK start: support height map scaling in terrain baking
float texturePreOffset;
float textureScale;
float Padding;
float texturePostOffset;
float padding[3];
// NV-DXVK end
} Stages[8];
};
Expand Down
Loading

0 comments on commit 912a164

Please sign in to comment.