Skip to content

Commit

Permalink
Merge branch 'rtt' into 'main'
Browse files Browse the repository at this point in the history
[REMIX-3098] Render Target Support

See merge request lightspeedrtx/dxvk-remix-nv!919
  • Loading branch information
MarkEHenderson committed Sep 16, 2024
2 parents a99f246 + 8336fff commit fdab8d7
Show file tree
Hide file tree
Showing 27 changed files with 388 additions and 70 deletions.
2 changes: 2 additions & 0 deletions RtxOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
|rtx.rayreconstruction.upscalerRoughnessDemodulationOffset|float|1.5|Strength of upscaler roughness demodulation\. Only used by DLSS\-RR\.|
|rtx.rayreconstruction.useSpecularHitDistance|bool|True|Use specular hit distance to reduce ghosting\.<br>|
|rtx.raytraceModePreset|int|1||
|rtx.raytracedRenderTarget.enable|bool|True|Enables or disables raytracing for render\-to\-texture effects\. The render target to be raytraced must be specified in the texture selection menu\.|
|rtx.recompileShadersOnLaunch|bool|False|When set to true runtime shader recompilation will execute on the first frame after launch\.|
|rtx.reflexMode|int|1|Reflex mode selection, enabling it helps minimize input latency, boost mode may further reduce latency by boosting GPU clocks in CPU\-bound cases\.<br>Supported enum values are 0 = None \(Disabled\), 1 = LowLatency \(Enabled\), 2 = LowLatencyBoost \(Enabled \+ Boost\)\.<br>Note that even when using the "None" Reflex mode Reflex will attempt to be initialized\. Use rtx\.isReflexEnabled to fully disable to skip this initialization if needed\.|
|rtx.reloadTextureWhenResolutionChanged|bool|False|Reload texture when resolution changed\.|
Expand Down Expand Up @@ -758,6 +759,7 @@ Tables below enumerate all the options and their defaults set by RTX Remix. Note
|rtx.playerModelTextures|hash set|||
|rtx.postfx.motionBlurMaskOutTextures|hash set||Disable motion blur for meshes with specific texture\.|
|rtx.rayPortalModelTextureHashes|hash vector||Texture hashes identifying ray portals\. Allowed number of hashes: \{0, 2\}\.|
|rtx.raytracedRenderTargetTextures|hash set||DescriptorHashes for Render Targets\. \(Screens that should display the output of another camera\)\.|
|rtx.singleOffsetDecalTextures|hash set||Warning: This option is deprecated, please use rtx\.decalTextures instead\.<br>Textures on draw calls used for geometric decals that don't inter\-overlap for a given texture hash\. Textures must be tagged as "Decal Texture" or "Dynamic Decal Texture" to apply\.<br>Applies a single shared offset to all the batched decal geometry rendered in a given draw call, rather than increasing offset per decal within the batch \(i\.e\. a quad in case of "Dynamic Decal Texture"\)\.<br>Note, the offset adds to the global offset among all decals drawn with different draw calls\.<br>The decal textures tagged this way must not inter\-overlap within a batch / single draw call since the same offset is applied to all of them\.<br>Applying a single offset is useful for stabilizing decal offsets when a game dynamically batches decals together\.<br>In addition, it makes the global decal offset index grow slower and thus it minimizes a chance of hitting the "rtx\.decals\.maxOffsetIndex limit"\.|
|rtx.skyBoxGeometries|hash set||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\)\.<br>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\.<br>The geometry hash being used for sky detection is based off of the asset hash rule, see: "rtx\.geometryAssetHashRuleString"\.|
|rtx.skyBoxTextures|hash set||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\)\.<br>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\.|
Expand Down
28 changes: 24 additions & 4 deletions src/d3d9/d3d9_common_texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,14 @@ namespace dxvk {
m_device->ChangeReportedMemory(m_size);

// Release this texture from ImGUI
if (m_image != nullptr && m_image->getHash() != 0)
ImGUI::ReleaseTexture(m_image->getHash());
if (m_image != nullptr) {
if (m_image->getHash() != 0) {
ImGUI::ReleaseTexture(m_image->getHash());
}
if (m_image->getDescriptorHash() != 0) {
ImGUI::ReleaseTexture(m_image->getDescriptorHash());
}
}
}


Expand Down Expand Up @@ -345,6 +351,12 @@ namespace dxvk {
newHash = XXH3_64bits_withSeed(&renderTargetHashCounter, sizeof(uint32_t), newHash);
++renderTargetHashCounter;
image->setHash(newHash);

// Generate descriptor hash from the image properties (not including actual pixel data)
XXH64_hash_t descriptorHash = m_desc.CalculateHash();

// save hash to dxvkImage
image->setDescriptorHash(descriptorHash);
}
return image;
// NV-DXVK end
Expand Down Expand Up @@ -632,7 +644,8 @@ namespace dxvk {
if (IsRenderTarget()) {
// Assumption: All image hashes are created before creating sample view. Put assert here to track hash bugs.
assert(m_image->getHash() != kEmptyHash);
ImGUI::AddTexture(m_image->getHash(), m_sampleView.Color);
ImGUI::AddTexture(m_image->getHash(), m_sampleView.Color, ImGUI::kTextureFlagsDefault);
ImGUI::AddTexture(m_image->getDescriptorHash(), m_sampleView.Color, ImGUI::kTextureFlagsRenderTarget);
}
}

Expand Down Expand Up @@ -673,7 +686,14 @@ namespace dxvk {
m_image->setHash(imageHash);

// Let ImGUI know about this texture
ImGUI::AddTexture(imageHash, m_sampleView.Color);
ImGUI::AddTexture(imageHash, m_sampleView.Color, ImGUI::kTextureFlagsDefault);
if (IsRenderTarget()) {
// Generate descriptor hash from the image properties (not including actual pixel data)
XXH64_hash_t descriptorHash = m_desc.CalculateHash();
m_image->setDescriptorHash(descriptorHash);

ImGUI::AddTexture(descriptorHash, m_sampleView.Color, ImGUI::kTextureFlagsRenderTarget);
}
}

void D3D9CommonTexture::SetupForRtx() {
Expand Down
12 changes: 12 additions & 0 deletions src/d3d9/d3d9_common_texture.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ namespace dxvk {
bool Discard;
bool IsBackBuffer;
bool IsAttachmentOnly;
// NV-DXVK start: stable descriptor hashing for identifying render targets.
// Forcing the extra padding byte at the end of the struct to be 0 initialized. This is required for a stable hash.
unsigned char padding = 0;

XXH64_hash_t CalculateHash() const {
// Warning: if this assert triggers, then hashes of this struct have been invalidated. To maintain stable hashes, we should transition
// to copying the data into a local struct that mimics the old struct fields and hashing that.
assert(sizeof(D3D9_COMMON_TEXTURE_DESC) == 44);

return XXH3_64bits(this, sizeof(D3D9_COMMON_TEXTURE_DESC));
}
// NV-DXVK end
};

struct D3D9ColorView {
Expand Down
6 changes: 6 additions & 0 deletions src/d3d9/d3d9_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,12 @@ namespace dxvk {
return m_rtx;
}
// NV-DXVK end

// NV-DXVK start: render target support
uint32_t GetActiveRTTextures() const {
return m_activeRTTextures;
}
// NV-DXVK end
private:

DxvkCsChunkRef AllocCsChunk() {
Expand Down
50 changes: 49 additions & 1 deletion src/d3d9/d3d9_rtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,25 @@ namespace dxvk {
D3D9Rtx::DrawCallType D3D9Rtx::makeDrawCallType(const DrawContext& drawContext) {
// Track the drawcall index so we can use it in rtx_context
m_activeDrawCallState.drawCallID = m_drawCallID++;
m_activeDrawCallState.isDrawingToRaytracedRenderTarget = false;
m_activeDrawCallState.isUsingRaytracedRenderTarget = false;

if (m_drawCallID < (uint32_t)RtxOptions::Get()->getDrawCallRange().x ||
m_drawCallID > (uint32_t)RtxOptions::Get()->getDrawCallRange().y) {
return { RtxGeometryStatus::Ignored, false };
}

// Raytraced Render Target Support
// If the bound texture for this draw call is one that has been used as a render target then store its id
if (RtxOptions::Get()->raytracedRenderTarget.enable()) {
for (uint32_t i : bit::BitMask(m_parent->GetActiveRTTextures())) {
D3D9CommonTexture* texture = GetCommonTexture(d3d9State().textures[i]);
if (lookupHash(RtxOptions::raytracedRenderTargetTextures(), texture->GetImage()->getDescriptorHash())) {
m_activeDrawCallState.isUsingRaytracedRenderTarget = true;
}
}
}

if (m_parent->UseProgrammableVS() && !useVertexCapture()) {
ONCE(Logger::info("[RTX-Compatibility-Info] Skipping draw call with shader usage as vertex capture is not enabled."));
return { RtxGeometryStatus::Ignored, false };
Expand Down Expand Up @@ -431,12 +444,24 @@ namespace dxvk {
}
}

// Raytraced Render Target
// If this isn't the primary render target but we have used this render target before then
// store the current camera matrices in case this render target is intended to be used as
// a texture for some geometry later
if (RtxOptions::Get()->raytracedRenderTarget.enable()) {
D3D9CommonTexture* texture = GetCommonTexture(d3d9State().renderTargets[kRenderTargetIndex]->GetBaseTexture());
if (texture && lookupHash(RtxOptions::raytracedRenderTargetTextures(), texture->GetImage()->getDescriptorHash())) {
m_activeDrawCallState.isDrawingToRaytracedRenderTarget = true;
return { RtxGeometryStatus::RayTraced, false };
}
}

if (!s_isDxvkResolutionEnvVarSet) {
// NOTE: This can fail when setting DXVK_RESOLUTION_WIDTH or HEIGHT
const bool isPrimary = isRenderTargetPrimary(*m_activePresentParams, d3d9State().renderTargets[kRenderTargetIndex]->GetCommonTexture()->Desc());

if (!isPrimary) {
ONCE(Logger::info("[RTX-Compatibility-Info] Found a draw call to a non-primary render target. Falling back to rasterization"));
ONCE(Logger::info("[RTX-Compatibility-Info] Found a draw call to a non-primary, non-raytraced render target. Falling back to rasterization"));
return { RtxGeometryStatus::Rasterized, false };
}
}
Expand Down Expand Up @@ -578,6 +603,23 @@ namespace dxvk {
return prepareFlagsForIgnoredDraws;
}

if (RtxOptions::Get()->raytracedRenderTarget.enable()) {
// If this draw call has an RT texture bound
if (m_activeDrawCallState.isUsingRaytracedRenderTarget) {
// We validate this state below
m_activeDrawCallState.isUsingRaytracedRenderTarget = false;
// Try and find the has of the positions
for (uint32_t i : bit::BitMask(m_parent->GetActiveRTTextures())) {
D3D9CommonTexture* texture = GetCommonTexture(d3d9State().textures[i]);
auto hash = texture->GetImage()->getDescriptorHash();
if (lookupHash(RtxOptions::raytracedRenderTargetTextures(), hash)) {
// Mark this as a valid Raytraced Render Target draw call
m_activeDrawCallState.isUsingRaytracedRenderTarget = true;
}
}
}
}

m_activeDrawCallState.categories = 0;
m_activeDrawCallState.materialData = {};

Expand Down Expand Up @@ -640,6 +682,12 @@ namespace dxvk {
m_activeDrawCallState.fogState.mode = D3DFOG_NONE;
}

// Ignore sky draw calls that are being drawn to a Raytraced Render Target
// Raytraced Render Target scenes just use the same sky as the main scene, no need to duplicate them
if (m_activeDrawCallState.isDrawingToRaytracedRenderTarget && m_activeDrawCallState.categories.test(InstanceCategories::Sky)) {
return prepareFlagsForIgnoredDraws;
}

assert(status == RtxGeometryStatus::RayTraced);

const bool preserveOriginalDraw = needVertexCapture;
Expand Down
13 changes: 13 additions & 0 deletions src/dxvk/dxvk_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ namespace dxvk {
return m_image.memory.length();
}

// NV-DXVK start: Hashes to identify textures.
void setHash(XXH64_hash_t hash) {
m_hash = hash;
}
Expand All @@ -354,6 +355,15 @@ namespace dxvk {
return m_hash;
}

void setDescriptorHash(XXH64_hash_t hash) {
m_descriptorHash = hash;
}

XXH64_hash_t getDescriptorHash() const {
return m_descriptorHash;
}
// NV-DXVK end

VkDeviceMemory getMemory() const {
return m_image.memory.memory();
}
Expand Down Expand Up @@ -386,7 +396,10 @@ namespace dxvk {
DxvkImageCreateInfo m_info;
VkMemoryPropertyFlags m_memFlags;
DxvkPhysicalImage m_image;
// NV-DXVK start: Hashes to identify textures.
XXH64_hash_t m_hash = 0;
XXH64_hash_t m_descriptorHash = 0;
// NV-DXVK end
bool m_shared = false;

small_vector<VkFormat, 4> m_viewFormats;
Expand Down
44 changes: 35 additions & 9 deletions src/dxvk/imgui/dxvk_imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,15 @@ namespace dxvk {
struct ImGuiTexture {
Rc<DxvkImageView> imageView = VK_NULL_HANDLE;
VkDescriptorSet texID = VK_NULL_HANDLE;
uint32_t textureFeatureFlags = 0;
};
std::unordered_map<XXH64_hash_t, ImGuiTexture> g_imguiTextureMap;

struct RtxTextureOption {
const char* uniqueId;
const char* displayName;
RtxOption<fast_unordered_set>* textureSetOption;
uint32_t featureFlagMask = ImGUI::kTextureFlagsDefault;
bool bufferToggle;
};

Expand All @@ -160,7 +162,8 @@ namespace dxvk {
{"playermodelbodytextures", "Player Model Body Texture (optional)", &RtxOptions::Get()->playerModelBodyTexturesObject()},
{"opacitymicromapignoretextures", "Opacity Micromap Ignore Texture (optional)", &RtxOptions::Get()->opacityMicromapIgnoreTexturesObject()},
{"ignorebakedlightingtextures","Ignore Baked Lighting Textures (optional)", &RtxOptions::Get()->ignoreBakedLightingTexturesObject()},
{"ignorealphaontextures","Ignore Alpha Channel of Textures (optional)", &RtxOptions::Get()->ignoreAlphaOnTexturesObject()}
{"ignorealphaontextures","Ignore Alpha Channel of Textures (optional)", &RtxOptions::Get()->ignoreAlphaOnTexturesObject()},
{"raytracedRenderTargetTextures","Raytraced Render Target Textures (optional)", &RtxOptions::Get()->raytracedRenderTargetTexturesObject(), ImGUI::kTextureFlagsRenderTarget}
};

ImGui::ComboWithKey<RenderPassGBufferRaytraceMode> renderPassGBufferRaytraceModeCombo {
Expand Down Expand Up @@ -445,11 +448,12 @@ namespace dxvk {
ImGui::DestroyContext(m_context);
}

void ImGUI::AddTexture(const XXH64_hash_t hash, const Rc<DxvkImageView>& imageView) {
void ImGUI::AddTexture(const XXH64_hash_t hash, const Rc<DxvkImageView>& imageView, uint32_t textureFeatureFlags) {
if (g_imguiTextureMap.find(hash) == g_imguiTextureMap.end()) {
ImGuiTexture texture;
texture.imageView = imageView; // Hold a refcount
texture.texID = VK_NULL_HANDLE;
texture.textureFeatureFlags = textureFeatureFlags;
g_imguiTextureMap[hash] = texture;
}
}
Expand Down Expand Up @@ -1410,11 +1414,12 @@ namespace dxvk {
if (ImGui::CollapsingHeader("Types", collapsingHeaderClosedFlags)) {
ImGui::Indent();
constexpr static std::pair<CameraType::Enum, const char*> cameras[] = {
{ CameraType::Main, "Main" },
{ CameraType::ViewModel, "ViewModel" },
{ CameraType::Portal0, "Portal0" },
{ CameraType::Portal1, "Portal1" },
{ CameraType::Sky, "Sky" },
{ CameraType::Main, "Main" },
{ CameraType::ViewModel, "ViewModel" },
{ CameraType::Portal0, "Portal0" },
{ CameraType::Portal1, "Portal1" },
{ CameraType::Sky, "Sky" },
{ CameraType::RenderToTexture, "RenderToTexture" },
};
// C++20: should be static_assert with std::ranges::find_if
assert(
Expand Down Expand Up @@ -1652,9 +1657,17 @@ namespace dxvk {
if (ImGui::Button("Copy Texture hash##texture_popup")) {
ImGui::SetClipboardText(hashToString(texHash).c_str());
}
uint32_t textureFeatureFlags = 0;
auto& pair = g_imguiTextureMap.find(texHash);
if (pair != g_imguiTextureMap.end()) {
textureFeatureFlags = pair->second.textureFeatureFlags;
}
for (auto& rtxOption : rtxTextureOptions) {
rtxOption.bufferToggle = rtxOption.textureSetOption->getValue().count(texHash) > 0;

if ((rtxOption.featureFlagMask & textureFeatureFlags) != rtxOption.featureFlagMask) {
// option requires a feature, but the texture doesn't have that feature.
continue;
}
if (IMGUI_ADD_TOOLTIP(ImGui::Checkbox(rtxOption.displayName, &rtxOption.bufferToggle), rtxOption.textureSetOption->getDescription())) {
toggleTextureSelection(texHash, rtxOption.uniqueId, rtxOption.textureSetOption->getValue());
}
Expand Down Expand Up @@ -1751,6 +1764,11 @@ namespace dxvk {
if (isListFiltered) {
auto& textureSet = listRtxOption.textureSetOption->getValue();
textureHasSelection = textureSet.find(texHash) != textureSet.end();

if ((listRtxOption.featureFlagMask & texImgui.textureFeatureFlags) != listRtxOption.featureFlagMask) {
// If the list needs to be filtered by texture feature, skip it for this category.
continue;
}
} else {
for (const auto rtxOption : rtxTextureOptions) {
auto& textureSet = rtxOption.textureSetOption->getValue();
Expand Down Expand Up @@ -2548,7 +2566,7 @@ namespace dxvk {
ImGui::Separator();

ImGui::Checkbox("Allow Full Screen Exclusive?", &RtxOptions::Get()->allowFSEObject());

ImGui::Unindent();
}

Expand Down Expand Up @@ -3036,6 +3054,14 @@ namespace dxvk {
ImGui::Unindent();
}

if (ImGui::CollapsingHeader("Raytraced Render Target [Experimental]", collapsingHeaderClosedFlags)) {
ImGui::Indent();
ImGui::TextWrapped("When a screen in-game is displaying the rasterized results of another camera, this can be used to raytrace that scene.\nNote that the render target texture containing the rasterized results needs to be set to `raytracedRenderTargetTextures` in the texture selection menu.");

ImGui::Checkbox("Enable Raytraced Render Targets", &RtxOptions::Get()->raytracedRenderTarget.enableObject());
ImGui::Unindent();
}

if (ImGui::CollapsingHeader("View Distance", collapsingHeaderClosedFlags)) {
ImGui::Indent();

Expand Down
8 changes: 7 additions & 1 deletion src/dxvk/imgui/dxvk_imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ namespace dxvk {
class ImGUI {

public:
// if set for a texture category, only textures with that flag can be set to that category.
static const uint32_t kTextureFlagsNone = 0;
// Use this for regular textures
static const uint32_t kTextureFlagsDefault = 1 << 0;
// Use this for render target textures
static const uint32_t kTextureFlagsRenderTarget = 1 << 1;

ImGUI(DxvkDevice* device);
~ImGUI();
Expand Down Expand Up @@ -87,7 +93,7 @@ namespace dxvk {
VkExtent2D surfaceSize,
bool vsync);

static void AddTexture(const XXH64_hash_t hash, const Rc<DxvkImageView>& imageView);
static void AddTexture(const XXH64_hash_t hash, const Rc<DxvkImageView>& imageView, uint32_t textureFeatureFlags);
static void ReleaseTexture(const XXH64_hash_t hash);
static bool checkHotkeyState(const VirtualKeys& virtKeys, const bool allowContinuousPress = false);

Expand Down
16 changes: 8 additions & 8 deletions src/dxvk/rtx_render/rtx_camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,16 +776,16 @@ namespace dxvk
inoutProjection[2][1] += clipSpaceJitter[1];
}

Camera RtCamera::getShaderConstants() const {
auto& worldToView = getWorldToView();
auto& translatedWorldToView = getTranslatedWorldToView();
auto& viewToWorld = getViewToWorld();
auto& viewToTranslatedWorld = getViewToTranslatedWorld();
Camera RtCamera::getShaderConstants(bool freecam) const {
auto& worldToView = getWorldToView(freecam);
auto& translatedWorldToView = getTranslatedWorldToView(freecam);
auto& viewToWorld = getViewToWorld(freecam);
auto& viewToTranslatedWorld = getViewToTranslatedWorld(freecam);
auto& viewToProjection = getViewToProjection();
auto& projectionToView = getProjectionToView();
auto& prevWorldToView = getPreviousWorldToView();
auto& prevTranslatedWorldToView = getPreviousTranslatedWorldToView();
auto& prevViewToWorld = getPreviousViewToWorld();
auto& prevWorldToView = getPreviousWorldToView(freecam);
auto& prevTranslatedWorldToView = getPreviousTranslatedWorldToView(freecam);
auto& prevViewToWorld = getPreviousViewToWorld(freecam);
auto& viewToProjectionJittered = getViewToProjectionJittered();
auto& projectionToViewJittered = getProjectionToViewJittered();
auto& prevViewToProjection = getPreviousViewToProjection();
Expand Down
Loading

0 comments on commit fdab8d7

Please sign in to comment.