rr: Cyberpunk's extra inputs (SSS guide, colour before particles) and hardware depth
- GameFrameR/RFrameImages: DLSSD.ScreenSpaceSubsurfaceScatteringGuide (arg 264) and DLSSD.ColorBeforeParticles (output stage, texture 416 with its own history pair 384/392, zero-initialised on first use); guides array 6 -> 8 - DLSS.Use.HW.Depth: byte 980 and output byte 443 bit 24 (Cyberpunk passes the nonlinear depth buffer); replay's 'inverted' field becomes a bitmask - shim: bind the two new resources, read the HW-depth flag, and accept every preset hint except E's 5 by mapping it to D's diamond_wallaby kernels (harness captures show 0x80000002/0x80000005 run preset D) - harness: D4R_HARNESS_RR_EXTRA=particles,sss and D4R_HARNESS_RR_HW_DEPTH=1 - ptx2rr: out-of-range integer fetches return zero, matching ZLUDA's behaviour (the reference the stages are checked against) - engine/rr/README: the capture findings behind the offsets
This commit is contained in:
1 parent
50fa65d87b
commit
db24805160
10 files changed
+133
-28
No files matched your search
+12
-9
@@ -13,7 +13,7 @@ struct GameDenoiser::Impl {
|
|||||||
REngine engine;
|
REngine engine;
|
||||||
uint32_t width, height, outputWidth, outputHeight;
|
uint32_t width, height, outputWidth, outputHeight;
|
||||||
game_detail::CanonicalInputs fallback; // colour, motion, depth
|
game_detail::CanonicalInputs fallback; // colour, motion, depth
|
||||||
std::array<game_detail::Plane, 6> guides; // the guides and optional inputs, when one cannot be sampled directly
|
std::array<game_detail::Plane, 8> guides; // the guides and optional inputs, when one cannot be sampled directly
|
||||||
bool started = false;
|
bool started = false;
|
||||||
// debugging: D4R_ENGINE_RR_DUMP=DIR copies every engine image after each frame into a host-visible buffer and
|
// debugging: D4R_ENGINE_RR_DUMP=DIR copies every engine image after each frame into a host-visible buffer and
|
||||||
// writes it at the next frame's recording (by then the game has waited for the frame), as DIR/F-NAME.bin
|
// writes it at the next frame's recording (by then the game has waited for the frame), as DIR/F-NAME.bin
|
||||||
@@ -100,10 +100,12 @@ void GameDenoiser::record(VkCommandBuffer cb, const GameFrameR& frame) {
|
|||||||
};
|
};
|
||||||
const bool directColor = direct(frame.color), directMotion = direct(frame.motion), directDepth = direct(frame.depth);
|
const bool directColor = direct(frame.color), directMotion = direct(frame.motion), directDepth = direct(frame.depth);
|
||||||
// the four guides, then the two optional inputs (kept in their own one-channel formats when copied)
|
// the four guides, then the two optional inputs (kept in their own one-channel formats when copied)
|
||||||
const GameImage* guides[6] = {&frame.normals, &frame.roughness, &frame.diffuseAlbedo, &frame.specularAlbedo,
|
const GameImage* guides[8] = {&frame.normals, &frame.roughness, &frame.diffuseAlbedo, &frame.specularAlbedo,
|
||||||
&frame.specularHitDistance, &frame.disocclusionMask};
|
&frame.specularHitDistance, &frame.disocclusionMask, &frame.subsurfaceGuide,
|
||||||
const VkFormat guideFormats[6] = {VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT,
|
&frame.colorBeforeParticles};
|
||||||
VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16_SFLOAT};
|
const VkFormat guideFormats[8] = {VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||||
|
VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16_SFLOAT,
|
||||||
|
VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT};
|
||||||
auto present = [&](int i) { return i < 4 || guides[i]->image != VK_NULL_HANDLE; };
|
auto present = [&](int i) { return i < 4 || guides[i]->image != VK_NULL_HANDLE; };
|
||||||
if (!g.started) {
|
if (!g.started) {
|
||||||
if (!g.model.uploadRecorded()) g.model.recordUpload(cb);
|
if (!g.model.uploadRecorded()) g.model.recordUpload(cb);
|
||||||
@@ -111,7 +113,7 @@ void GameDenoiser::record(VkCommandBuffer cb, const GameFrameR& frame) {
|
|||||||
g.started = true;
|
g.started = true;
|
||||||
}
|
}
|
||||||
g.fallback.record(cb, frame.color, frame.motion, frame.depth, w, h, w, h, directColor, directMotion, directDepth);
|
g.fallback.record(cb, frame.color, frame.motion, frame.depth, w, h, w, h, directColor, directMotion, directDepth);
|
||||||
for (int i = 0; i < 6; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
if (!present(i) || direct(*guides[i])) continue;
|
if (!present(i) || direct(*guides[i])) continue;
|
||||||
if (!g.guides[i].image) {
|
if (!g.guides[i].image) {
|
||||||
g.fallback.ensure(cb, g.guides[i], guideFormats[i], w, h);
|
g.fallback.ensure(cb, g.guides[i], guideFormats[i], w, h);
|
||||||
@@ -126,9 +128,10 @@ void GameDenoiser::record(VkCommandBuffer cb, const GameFrameR& frame) {
|
|||||||
images.color = directColor ? frame.color.view : g.fallback.color.view;
|
images.color = directColor ? frame.color.view : g.fallback.color.view;
|
||||||
images.motion = directMotion ? frame.motion.view : g.fallback.motion.view;
|
images.motion = directMotion ? frame.motion.view : g.fallback.motion.view;
|
||||||
images.depth = directDepth ? frame.depth.view : g.fallback.depth.view;
|
images.depth = directDepth ? frame.depth.view : g.fallback.depth.view;
|
||||||
VkImageView* guideViews[6] = {&images.normals, &images.roughness, &images.diffuseAlbedo, &images.specularAlbedo,
|
VkImageView* guideViews[8] = {&images.normals, &images.roughness, &images.diffuseAlbedo, &images.specularAlbedo,
|
||||||
&images.specularHitDistance, &images.disocclusionMask};
|
&images.specularHitDistance, &images.disocclusionMask, &images.subsurfaceGuide,
|
||||||
for (int i = 0; i < 6; ++i)
|
&images.colorBeforeParticles};
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
if (present(i)) *guideViews[i] = direct(*guides[i]) ? guides[i]->view : g.guides[i].view;
|
if (present(i)) *guideViews[i] = direct(*guides[i]) ? guides[i]->view : g.guides[i].view;
|
||||||
const bool directOutput = frame.output.view != VK_NULL_HANDLE && frame.output.format == VK_FORMAT_R16G16B16A16_SFLOAT &&
|
const bool directOutput = frame.output.view != VK_NULL_HANDLE && frame.output.format == VK_FORMAT_R16G16B16A16_SFLOAT &&
|
||||||
frame.output.layout == VK_IMAGE_LAYOUT_GENERAL && !frame.output.x && !frame.output.y &&
|
frame.output.layout == VK_IMAGE_LAYOUT_GENERAL && !frame.output.x && !frame.output.y &&
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ struct GameFrameR {
|
|||||||
GameImage color, depth, motion, normals, roughness, diffuseAlbedo, specularAlbedo, output;
|
GameImage color, depth, motion, normals, roughness, diffuseAlbedo, specularAlbedo, output;
|
||||||
// optional (image null: the game passes none): DLSSD.SpecularHitDistance, DLSS.DisocclusionMask
|
// optional (image null: the game passes none): DLSSD.SpecularHitDistance, DLSS.DisocclusionMask
|
||||||
GameImage specularHitDistance, disocclusionMask;
|
GameImage specularHitDistance, disocclusionMask;
|
||||||
|
// optional too: DLSSD.ScreenSpaceSubsurfaceScatteringGuide, DLSSD.ColorBeforeParticles
|
||||||
|
GameImage subsurfaceGuide, colorBeforeParticles;
|
||||||
RFrameSettings settings;
|
RFrameSettings settings;
|
||||||
};
|
};
|
||||||
class GameDenoiser {
|
class GameDenoiser {
|
||||||
|
|||||||
@@ -159,8 +159,8 @@ int main(int argc, char** argv) try {
|
|||||||
VK_FORMAT_R8G8B8A8_UNORM};
|
VK_FORMAT_R8G8B8A8_UNORM};
|
||||||
std::vector<Input> inputs;
|
std::vector<Input> inputs;
|
||||||
for (VkFormat f : formats) inputs.emplace_back(c, f, rw, rh);
|
for (VkFormat f : formats) inputs.emplace_back(c, f, rw, rh);
|
||||||
const char* optionalNames[] = {"hitdistance", "disocclusion"};
|
const char* optionalNames[] = {"hitdistance", "disocclusion", "sss", "particles"};
|
||||||
const VkFormat optionalFormats[] = {VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16_SFLOAT};
|
const VkFormat optionalFormats[] = {VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT};
|
||||||
std::vector<Input> optional;
|
std::vector<Input> optional;
|
||||||
for (VkFormat f : optionalFormats) optional.emplace_back(c, f, rw, rh);
|
for (VkFormat f : optionalFormats) optional.emplace_back(c, f, rw, rh);
|
||||||
HostBuffer staging(c, VkDeviceSize(ow) * oh * 8);
|
HostBuffer staging(c, VkDeviceSize(ow) * oh * 8);
|
||||||
@@ -178,7 +178,7 @@ int main(int argc, char** argv) try {
|
|||||||
frames >> f >> s.jitter[0] >> s.jitter[1] >> s.motionScale[0] >> s.motionScale[1] >> s.preExposure >> reset >> inverted;
|
frames >> f >> s.jitter[0] >> s.jitter[1] >> s.motionScale[0] >> s.motionScale[1] >> s.preExposure >> reset >> inverted;
|
||||||
for (float& v : s.worldToView) frames >> v;
|
for (float& v : s.worldToView) frames >> v;
|
||||||
for (float& v : s.viewToClip) frames >> v;
|
for (float& v : s.viewToClip) frames >> v;
|
||||||
s.reset = reset != 0; s.depthInverted = inverted != 0;
|
s.reset = reset != 0; s.depthInverted = (inverted & 1) != 0; s.hardwareDepth = (inverted & 2) != 0; // bit 1: DLSS.Use.HW.Depth
|
||||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||||
auto data = read(inputDir + "/" + std::to_string(f) + "-" + names[i] + ".bin");
|
auto data = read(inputDir + "/" + std::to_string(f) + "-" + names[i] + ".bin");
|
||||||
if (data.size() != size_t(rw) * rh * texelBytes(formats[i])) throw std::runtime_error(std::string("input size: ") + names[i]);
|
if (data.size() != size_t(rw) * rh * texelBytes(formats[i])) throw std::runtime_error(std::string("input size: ") + names[i]);
|
||||||
@@ -195,7 +195,7 @@ int main(int argc, char** argv) try {
|
|||||||
images.normals = inputs[3].view; images.roughness = inputs[4].view;
|
images.normals = inputs[3].view; images.roughness = inputs[4].view;
|
||||||
images.diffuseAlbedo = inputs[5].view; images.specularAlbedo = inputs[6].view;
|
images.diffuseAlbedo = inputs[5].view; images.specularAlbedo = inputs[6].view;
|
||||||
// optional inputs, present when the capture had them (rr_inputs.py)
|
// optional inputs, present when the capture had them (rr_inputs.py)
|
||||||
for (int k = 0; k < 2; ++k) {
|
for (int k = 0; k < 4; ++k) {
|
||||||
std::ifstream probe(inputDir + "/" + std::to_string(f) + "-" + optionalNames[k] + ".bin", std::ios::binary);
|
std::ifstream probe(inputDir + "/" + std::to_string(f) + "-" + optionalNames[k] + ".bin", std::ios::binary);
|
||||||
if (!probe) continue;
|
if (!probe) continue;
|
||||||
auto data = read(inputDir + "/" + std::to_string(f) + "-" + optionalNames[k] + ".bin");
|
auto data = read(inputDir + "/" + std::to_string(f) + "-" + optionalNames[k] + ".bin");
|
||||||
@@ -207,7 +207,8 @@ int main(int argc, char** argv) try {
|
|||||||
vkCmdCopyBufferToImage(cb, staging.buffer, optional[k].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
vkCmdCopyBufferToImage(cb, staging.buffer, optional[k].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||||
barrier(cb, optional[k].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
|
barrier(cb, optional[k].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
|
||||||
});
|
});
|
||||||
(k == 0 ? images.specularHitDistance : images.disocclusionMask) = optional[k].view;
|
VkImageView* slots[] = {&images.specularHitDistance, &images.disocclusionMask, &images.subsurfaceGuide, &images.colorBeforeParticles};
|
||||||
|
*slots[k] = optional[k].view;
|
||||||
}
|
}
|
||||||
engine.setFrame(images, s);
|
engine.setFrame(images, s);
|
||||||
if (first) c.submit([&](VkCommandBuffer cb) { model.recordUpload(cb); engine.recordInitialize(cb); first = false; });
|
if (first) c.submit([&](VkCommandBuffer cb) { model.recordUpload(cb); engine.recordInitialize(cb); first = false; });
|
||||||
|
|||||||
@@ -171,3 +171,21 @@ exp2 / reciprocal (rrsim's exact arithmetic is further from the capture than the
|
|||||||
single-use multiply-subtract pairs into fma, merging the two constant multiplications after the log transform,
|
single-use multiply-subtract pairs into fma, merging the two constant multiplications after the log transform,
|
||||||
`precise` on the inlined exp2, NoContraction on its conversions. The differing values come in spatial clusters, one
|
`precise` on the inlined exp2, NoContraction on its conversions. The differing values come in spatial clusters, one
|
||||||
colour channel at a time, always one ulp, mostly just above 0.5 in surface 224.
|
colour channel at a time, always one ulp, mostly just above 0.5 in surface 224.
|
||||||
|
|
||||||
|
## Cyberpunk 2077: more inputs and settings (2026-10-12)
|
||||||
|
|
||||||
|
Found on harness captures (`D4R_HARNESS_RR_EXTRA=particles,sss`, `D4R_HARNESS_RR_HW_DEPTH=1`, `D4R_RR_PRESET=N`):
|
||||||
|
|
||||||
|
| what the game passes | where NGX puts it |
|
||||||
|
|---|---|
|
||||||
|
| `DLSSD.ScreenSpaceSubsurfaceScatteringGuide` (R16F) | encoder 0 texture 264 (its handle is the only switch) |
|
||||||
|
| `DLSSD.ColorBeforeParticles` (RGBA16F) | output stage texture 416, flag byte 432, and a second output-size history: surface 384, read back next frame as texture 392 (linear) |
|
||||||
|
| `DLSS.Use.HW.Depth = 1` | encoder 0 byte 980, output stage byte 443 |
|
||||||
|
| preset hint | 5 runs preset E; every other value (0-4, 6, 0x80000002, 0x80000005) runs preset D |
|
||||||
|
|
||||||
|
Integer texture fetches outside the image return zero (`fetch0`), which is what the reference does; CUDA would clamp.
|
||||||
|
With Cyberpunk's set (hit distance, particles, subsurface guide, hardware depth) the engine's presented image is
|
||||||
|
byte-identical to the CUDA path through the shim on the frames the harness can compare (640x360 -> 1920x1080), and
|
||||||
|
with RE Requiem's set the network is identical on every frame (output 107-109 dB). At 1280x720 and 1920x1080 -> 4K
|
||||||
|
without these inputs the output still falls to 43-53 dB after the first frame (the output stage's one-ulp values);
|
||||||
|
a correctly rounded log2 there matches the capture worse than the GPU instruction does.
|
||||||
+6
-4
@@ -66,6 +66,7 @@ uint HU2_(u16vec2 u) { return uint(u.x) | (uint(u.y) << 16); }
|
|||||||
uint H2U(f16vec2 h) { return HU2_(float16BitsToUint16(h)); }
|
uint H2U(f16vec2 h) { return HU2_(float16BitsToUint16(h)); }
|
||||||
int s16(uint u) { return (int(u) << 16) >> 16; }
|
int s16(uint u) { return (int(u) << 16) >> 16; }
|
||||||
float rcp_a(float x) { return 1.0 / x; }
|
float rcp_a(float x) { return 1.0 / x; }
|
||||||
|
vec4 fetch0(sampler2D t, ivec2 c) { const ivec2 n = textureSize(t, 0); return (c.x < 0 || c.y < 0 || c.x >= n.x || c.y >= n.y) ? vec4(0.0) : texelFetch(t, c, 0); }
|
||||||
// x + copysign(0.5, x) rounded toward zero and then truncated: round half away from zero, exactly
|
// x + copysign(0.5, x) rounded toward zero and then truncated: round half away from zero, exactly
|
||||||
float round_half_away(float x) { float t = trunc(x); return abs(x - t) >= 0.5 ? t + sign(x) : t; }
|
float round_half_away(float x) { float t = trunc(x); return abs(x - t) >= 0.5 ? t + sign(x) : t; }
|
||||||
uint PARAM32(uint o) { return param.w[o >> 2]; }
|
uint PARAM32(uint o) { return param.w[o >> 2]; }
|
||||||
@@ -509,10 +510,11 @@ class RRT(base.T):
|
|||||||
off = self.handle_off(h)
|
off = self.handle_off(h)
|
||||||
self.textures.add(off)
|
self.textures.add(off)
|
||||||
if 'base' in o and o[-1] == 's32':
|
if 'base' in o and o[-1] == 's32':
|
||||||
# integer coordinates still go through the texture's address mode in CUDA (clamp for every denoiser
|
# an integer fetch outside the image: CUDA applies the texture's address mode (clamp for every denoiser
|
||||||
# texture); texelFetch does not clamp, and what it returns out of range depends on the device's robustness
|
# texture), but the reference this engine is checked against is ZLUDA, whose image load returns zero there
|
||||||
fetch = (f'texelFetch(T{off}, clamp(ivec2({self.val(c[0], "s32")}, {self.val(c[1], "s32")}), ivec2(0), '
|
# (zluda_ptx_impl.cpp fetch_2D). The output stage's specular reprojection reads such neighbours at the image
|
||||||
f'textureSize(T{off}, 0) - 1), 0)')
|
# border: with clamp its surfaces were 99.92% identical to the capture, with zero 99.997% (capDX, 2026-10-12)
|
||||||
|
fetch = f'fetch0(T{off}, ivec2({self.val(c[0], "s32")}, {self.val(c[1], "s32")}))'
|
||||||
elif 'base' in o and o[-1] == 'f32':
|
elif 'base' in o and o[-1] == 'f32':
|
||||||
fetch = f'textureLod(T{off}, vec2({self.val(c[0], "f32")}, {self.val(c[1], "f32")}), 0.0)'
|
fetch = f'textureLod(T{off}, vec2({self.val(c[0], "f32")}, {self.val(c[1], "f32")}), 0.0)'
|
||||||
elif 'level' in o and o[-1] == 'f32':
|
elif 'level' in o and o[-1] == 'f32':
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ import numpy as np
|
|||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
from rr_capture import Capture # noqa: E402
|
from rr_capture import Capture # noqa: E402
|
||||||
|
|
||||||
OPTIONAL = {'hitdistance': 248, 'disocclusion': 296}
|
OPTIONAL = {'hitdistance': 248, 'disocclusion': 296, 'sss': 264} # encoder 0 textures
|
||||||
|
OPTIONAL_OUTPUT = {'particles': 416} # output stage textures
|
||||||
INPUTS = {'color': 160, 'motion': 168, 'normals': 184, 'roughness': 192, 'diffuse': 232, 'specular': 240, 'depth': 512}
|
INPUTS = {'color': 160, 'motion': 168, 'normals': 184, 'roughness': 192, 'diffuse': 232, 'specular': 240, 'depth': 512}
|
||||||
|
|
||||||
|
|
||||||
@@ -49,6 +50,10 @@ def main():
|
|||||||
for name, off in OPTIONAL.items(): # DLSSD.SpecularHitDistance, DLSS.DisocclusionMask when the game passed them
|
for name, off in OPTIONAL.items(): # DLSSD.SpecularHitDistance, DLSS.DisocclusionMask when the game passed them
|
||||||
if off in images:
|
if off in images:
|
||||||
(o.out / f'{f}-{name}.bin').write_bytes(images[off].path.read_bytes())
|
(o.out / f'{f}-{name}.bin').write_bytes(images[off].path.read_bytes())
|
||||||
|
out_images = {i.offset: i for i in cap.find('hkpn_output', f).images}
|
||||||
|
for name, off in OPTIONAL_OUTPUT.items(): # DLSSD.ColorBeforeParticles
|
||||||
|
if off in out_images:
|
||||||
|
(o.out / f'{f}-{name}.bin').write_bytes(out_images[off].path.read_bytes())
|
||||||
clip = mat(660)
|
clip = mat(660)
|
||||||
view = mat(596) @ np.linalg.inv(clip)
|
view = mat(596) @ np.linalg.inv(clip)
|
||||||
view[3, :3] = 0.0
|
view[3, :3] = 0.0
|
||||||
@@ -56,7 +61,7 @@ def main():
|
|||||||
mvx, mvy = f32(344) * rw / ow, f32(348) * rh / oh
|
mvx, mvy = f32(344) * rw / ow, f32(348) * rh / oh
|
||||||
reset = 1 if f == 1 or not o.no_reset else 0
|
reset = 1 if f == 1 or not o.no_reset else 0
|
||||||
values = ' '.join(f'{v:.9g}' for v in list(view.flatten()) + list(clip.flatten()))
|
values = ' '.join(f'{v:.9g}' for v in list(view.flatten()) + list(clip.flatten()))
|
||||||
lines.append(f'frame {f} {jx:.9g} {jy:.9g} {mvx:.9g} {mvy:.9g} 1 {reset} {u32(528)} {values}')
|
lines.append(f'frame {f} {jx:.9g} {jy:.9g} {mvx:.9g} {mvy:.9g} 1 {reset} {u32(528) | (a[980] << 1)} {values}')
|
||||||
(o.out / 'frames.txt').write_text('\n'.join(lines) + '\n')
|
(o.out / 'frames.txt').write_text('\n'.join(lines) + '\n')
|
||||||
print(f'{o.frames} frames of {o.capture} in {o.out}')
|
print(f'{o.frames} frames of {o.capture} in {o.out}')
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,8 @@ struct RFrameImages {
|
|||||||
VkImageView color, motion, depth, normals, roughness, diffuseAlbedo, specularAlbedo;
|
VkImageView color, motion, depth, normals, roughness, diffuseAlbedo, specularAlbedo;
|
||||||
// Optional inputs (null: the game passes none): DLSSD.SpecularHitDistance and DLSS.DisocclusionMask, one channel each
|
// Optional inputs (null: the game passes none): DLSSD.SpecularHitDistance and DLSS.DisocclusionMask, one channel each
|
||||||
VkImageView specularHitDistance = VK_NULL_HANDLE, disocclusionMask = VK_NULL_HANDLE;
|
VkImageView specularHitDistance = VK_NULL_HANDLE, disocclusionMask = VK_NULL_HANDLE;
|
||||||
|
// DLSSD.ScreenSpaceSubsurfaceScatteringGuide (one channel) and DLSSD.ColorBeforeParticles (colour), optional too
|
||||||
|
VkImageView subsurfaceGuide = VK_NULL_HANDLE, colorBeforeParticles = VK_NULL_HANDLE;
|
||||||
VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL; // of every view
|
VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL; // of every view
|
||||||
};
|
};
|
||||||
struct RFrameSettings {
|
struct RFrameSettings {
|
||||||
@@ -264,6 +266,7 @@ struct RFrameSettings {
|
|||||||
float frameTimeMs = 16.666667f; // FrameTimeDeltaInMsec
|
float frameTimeMs = 16.666667f; // FrameTimeDeltaInMsec
|
||||||
bool reset = false;
|
bool reset = false;
|
||||||
bool depthInverted = false; // creation flag NVSDK_NGX_DLSS_Feature_Flags_DepthInverted
|
bool depthInverted = false; // creation flag NVSDK_NGX_DLSS_Feature_Flags_DepthInverted
|
||||||
|
bool hardwareDepth = false; // DLSS.Use.HW.Depth: the depth input is the nonlinear depth buffer (Cyberpunk 2077)
|
||||||
bool roughnessPacked = false; // DLSS.Roughness.Mode 1: the roughness is the normals' w (no roughness texture)
|
bool roughnessPacked = false; // DLSS.Roughness.Mode 1: the roughness is the normals' w (no roughness texture)
|
||||||
float worldToView[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; // row-major, as the SDK passes them
|
float worldToView[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; // row-major, as the SDK passes them
|
||||||
float viewToClip[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
|
float viewToClip[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
|
||||||
|
|||||||
+29
-1
@@ -173,6 +173,10 @@ struct REngine::Impl {
|
|||||||
std::array<Tex, 2> q, r, h, e; // ping-pong: enc0's 304 / 224, dec0's 144 / enc0's 488, the output-size history,
|
std::array<Tex, 2> q, r, h, e; // ping-pong: enc0's 304 / 224, dec0's 144 / enc0's 488, the output-size history,
|
||||||
// and e: this frame's and the previous frame's exposure (1x1)
|
// and e: this frame's and the previous frame's exposure (1x1)
|
||||||
Tex out; // the presented image (the output stage's surface 240)
|
Tex out; // the presented image (the output stage's surface 240)
|
||||||
|
// the output stage's second history (surface 384, read back as texture 392), kept only for games that pass
|
||||||
|
// DLSSD.ColorBeforeParticles; created on first use
|
||||||
|
Tex pb[2];
|
||||||
|
bool pbNew = false;
|
||||||
Tex zero; // a zeroed 1x1 texture for the handles NGX leaves null (they read zero)
|
Tex zero; // a zeroed 1x1 texture for the handles NGX leaves null (they read zero)
|
||||||
std::map<std::string, Tex> spare; // per format, a 1x1 surface for stores NGX leaves unbound
|
std::map<std::string, Tex> spare; // per format, a 1x1 surface for stores NGX leaves unbound
|
||||||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||||
@@ -201,6 +205,7 @@ struct REngine::Impl {
|
|||||||
// the optional inputs (capDX, 2026-10-12): unbound without them, like NGX
|
// the optional inputs (capDX, 2026-10-12): unbound without them, like NGX
|
||||||
case 248: return in.specularHitDistance ? G(in.specularHitDistance) : Resource{};
|
case 248: return in.specularHitDistance ? G(in.specularHitDistance) : Resource{};
|
||||||
case 296: return in.disocclusionMask ? G(in.disocclusionMask) : Resource{};
|
case 296: return in.disocclusionMask ? G(in.disocclusionMask) : Resource{};
|
||||||
|
case 264: return in.subsurfaceGuide ? G(in.subsurfaceGuide) : Resource{}; // capDXsss
|
||||||
case 488: return T(r[par ^ 1], true); case 496: return T(h[par ^ 1], true); case 512: return G(in.depth);
|
case 488: return T(r[par ^ 1], true); case 496: return T(h[par ^ 1], true); case 512: return G(in.depth);
|
||||||
} else switch (offset) {
|
} else switch (offset) {
|
||||||
case 112: return T(s112[0]); case 120: return T(s120[0]); case 128: return T(s128[0]); case 304: return T(q[par]);
|
case 112: return T(s112[0]); case 120: return T(s120[0]); case 128: return T(s128[0]); case 304: return T(q[par]);
|
||||||
@@ -222,8 +227,12 @@ struct REngine::Impl {
|
|||||||
case 176: return G(in.color, true); case 192: return G(in.depth); case 200: return T(e[0]); case 208: return T(e[1]);
|
case 176: return G(in.color, true); case 192: return G(in.depth); case 200: return T(e[0]); case 208: return T(e[1]);
|
||||||
case 216: return T(h[par ^ 1], true); case 280: return G(in.diffuseAlbedo); case 288: return G(in.specularAlbedo);
|
case 216: return T(h[par ^ 1], true); case 280: return G(in.diffuseAlbedo); case 288: return G(in.specularAlbedo);
|
||||||
case 296: return T(q[par]); case 448: return T(d112[0]);
|
case 296: return T(q[par]); case 448: return T(d112[0]);
|
||||||
|
// DLSSD.ColorBeforeParticles and its history (capDXparticles, 2026-10-12)
|
||||||
|
case 416: return in.colorBeforeParticles ? G(in.colorBeforeParticles) : Resource{};
|
||||||
|
case 392: return in.colorBeforeParticles ? T(pb[par ^ 1], true) : Resource{};
|
||||||
} else switch (offset) {
|
} else switch (offset) {
|
||||||
case 224: return T(h[par]); case 240: return Resource{output, false, false};
|
case 224: return T(h[par]); case 240: return Resource{output, false, false};
|
||||||
|
case 384: return in.colorBeforeParticles ? T(pb[par]) : Resource{};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
@@ -398,6 +407,8 @@ void REngine::setFrame(const RFrameImages& images, const RFrameSettings& setting
|
|||||||
p[539] = images.disocclusionMask ? 1u : 0u;
|
p[539] = images.disocclusionMask ? 1u : 0u;
|
||||||
put32(p, 432, e.rw - 1); put32(p, 436, e.rh - 1); put32(p, 472, e.ow); put32(p, 476, e.oh);
|
put32(p, 432, e.rw - 1); put32(p, 436, e.rh - 1); put32(p, 472, e.ow); put32(p, 476, e.oh);
|
||||||
put32(p, 528, settings.depthInverted ? 1u : 0u);
|
put32(p, 528, settings.depthInverted ? 1u : 0u);
|
||||||
|
// byte 980 (and the output stage's byte 443): DLSS.Use.HW.Depth (capDXhw, 2026-10-12)
|
||||||
|
p[980] = settings.hardwareDepth ? 1u : 0u;
|
||||||
// the camera: the view's rotation (its translation row dropped) times the projection, its inverse, and both
|
// the camera: the view's rotation (its translation row dropped) times the projection, its inverse, and both
|
||||||
// composed with the render viewport (NDC <-> render pixels, y down)
|
// composed with the render viewport (NDC <-> render pixels, y down)
|
||||||
Mat4 view, clip;
|
Mat4 view, clip;
|
||||||
@@ -420,9 +431,10 @@ void REngine::setFrame(const RFrameImages& images, const RFrameSettings& setting
|
|||||||
put32(o, 56, e.w0); put32(o, 60, e.h0); put32(o, 140, e.rw - 1); put32(o, 144, e.rh - 1);
|
put32(o, 56, e.w0); put32(o, 60, e.h0); put32(o, 140, e.rw - 1); put32(o, 144, e.rh - 1);
|
||||||
putF(o, 152, settings.frameTimeMs);
|
putF(o, 152, settings.frameTimeMs);
|
||||||
o[160] = images.specularHitDistance ? 1u : 0u;
|
o[160] = images.specularHitDistance ? 1u : 0u;
|
||||||
|
o[432] = images.colorBeforeParticles ? 1u : 0u; // the particle layer is composited (textures 392 / 416, surface 384)
|
||||||
put64(o, 256, e.arenaOffset(RHkpnArena));
|
put64(o, 256, e.arenaOffset(RHkpnArena));
|
||||||
putF(o, 336, settings.motionScale[0] * (ow / rw)); putF(o, 340, settings.motionScale[1] * (oh / rh));
|
putF(o, 336, settings.motionScale[0] * (ow / rw)); putF(o, 340, settings.motionScale[1] * (oh / rh));
|
||||||
put32(o, 440, (reset ? 1u : 0u) | (settings.depthInverted ? 0x10000u : 0u));
|
put32(o, 440, (reset ? 1u : 0u) | (settings.depthInverted ? 0x10000u : 0u) | (settings.hardwareDepth ? 0x1000000u : 0u));
|
||||||
// the exposure: NGX's MODE 1 measurement (engine/exposure_k.comp) on 1 / pre-exposure scaled colour
|
// the exposure: NGX's MODE 1 measurement (engine/exposure_k.comp) on 1 / pre-exposure scaled colour
|
||||||
const uint32_t gx = exposureGrid(e.rw, e.ow), gy = exposureGrid(e.rh, e.oh);
|
const uint32_t gx = exposureGrid(e.rw, e.ow), gy = exposureGrid(e.rh, e.oh);
|
||||||
e.exposure.origin[0] = e.exposure.origin[1] = 0;
|
e.exposure.origin[0] = e.exposure.origin[1] = 0;
|
||||||
@@ -440,6 +452,10 @@ void REngine::setFrame(const RFrameImages& images, const RFrameSettings& setting
|
|||||||
e.reset = reset;
|
e.reset = reset;
|
||||||
// descriptors for the caller's images, cached per combination: every stage, both frame parities, bound by the
|
// descriptors for the caller's images, cached per combination: every stage, both frame parities, bound by the
|
||||||
// argument offsets its shader declares (rr.txt)
|
// argument offsets its shader declares (rr.txt)
|
||||||
|
if (images.colorBeforeParticles && !e.pb[0].handle) {
|
||||||
|
for (auto& t : e.pb) t.create(d, e.ow, e.oh, VK_FORMAT_R16G16B16A16_SFLOAT);
|
||||||
|
e.pbNew = true;
|
||||||
|
}
|
||||||
// texture and surface handles of the two stages with optional inputs: 1 where something is bound, 0 where NGX
|
// texture and surface handles of the two stages with optional inputs: 1 where something is bound, 0 where NGX
|
||||||
// leaves the argument null. The kernels branch on that (enc0 takes its specular hit distance path only when
|
// leaves the argument null. The kernels branch on that (enc0 takes its specular hit distance path only when
|
||||||
// argument 248's handle is not null; capDX, 2026-10-12).
|
// argument 248's handle is not null; capDX, 2026-10-12).
|
||||||
@@ -516,6 +532,18 @@ void REngine::recordFrame(VkCommandBuffer cb, VkQueryPool stamps, uint32_t first
|
|||||||
memoryBarrier(cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
memoryBarrier(cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
|
VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
|
||||||
};
|
};
|
||||||
|
if (e.pbNew) { // the particle history starts from zero, like the rest of the temporal state
|
||||||
|
VkClearColorValue zero{};
|
||||||
|
VkImageSubresourceRange range{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||||
|
for (auto& t : e.pb) {
|
||||||
|
imageBarrier(cb, t.handle, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||||
|
vkCmdClearColorImage(cb, t.handle, VK_IMAGE_LAYOUT_GENERAL, &zero, 1, &range);
|
||||||
|
}
|
||||||
|
memoryBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
|
||||||
|
e.pbNew = false;
|
||||||
|
}
|
||||||
stamp(0);
|
stamp(0);
|
||||||
// the frame's parameter blocks, then (on a reset) a clean temporal state
|
// the frame's parameter blocks, then (on a reset) a clean temporal state
|
||||||
memoryBarrier(cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
|
memoryBarrier(cb, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||||
|
|||||||
@@ -1336,6 +1336,8 @@ int main(int argc, char** argv)
|
|||||||
// capture shows where the denoiser reads them.
|
// capture shows where the denoiser reads them.
|
||||||
ID3D12Resource* hitDistance = nullptr;
|
ID3D12Resource* hitDistance = nullptr;
|
||||||
ID3D12Resource* disocclusionMask = nullptr;
|
ID3D12Resource* disocclusionMask = nullptr;
|
||||||
|
ID3D12Resource* colorBeforeParticles = nullptr;
|
||||||
|
ID3D12Resource* subsurfaceGuide = nullptr;
|
||||||
if (const char* extra = reconstruction ? std::getenv("D4R_HARNESS_RR_EXTRA") : nullptr)
|
if (const char* extra = reconstruction ? std::getenv("D4R_HARNESS_RR_EXTRA") : nullptr)
|
||||||
{
|
{
|
||||||
if (std::strstr(extra, "hit") != nullptr)
|
if (std::strstr(extra, "hit") != nullptr)
|
||||||
@@ -1360,6 +1362,31 @@ int main(int argc, char** argv)
|
|||||||
if (!disocclusionMask) return 1;
|
if (!disocclusionMask) return 1;
|
||||||
upload(disocclusionMask, mask.data(), resourceWidth * sizeof(uint16_t), srv);
|
upload(disocclusionMask, mask.data(), resourceWidth * sizeof(uint16_t), srv);
|
||||||
}
|
}
|
||||||
|
// "particles" / "sss": Cyberpunk 2077's DLSSD.ColorBeforeParticles (RGBA16F) and
|
||||||
|
// DLSSD.ScreenSpaceSubsurfaceScatteringGuide (R16F)
|
||||||
|
if (std::strstr(extra, "particles") != nullptr)
|
||||||
|
{
|
||||||
|
std::vector<uint16_t> before(static_cast<size_t>(resourceWidth) * inHeight * 4);
|
||||||
|
for (UINT y = 0; y < inHeight; ++y)
|
||||||
|
for (UINT x = 0; x < resourceWidth; ++x)
|
||||||
|
for (UINT c = 0; c < 4; ++c)
|
||||||
|
before[(static_cast<size_t>(y) * resourceWidth + x) * 4 + c] = std::bit_cast<uint16_t>(static_cast<_Float16>(
|
||||||
|
c == 3 ? 1.0f : 0.05f + 0.6f * static_cast<float>((x / 20 + 2 * (y / 20) + c) % 5) / 4.0f));
|
||||||
|
colorBeforeParticles = create_texture(resourceWidth, inHeight, DXGI_FORMAT_R16G16B16A16_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
||||||
|
if (!colorBeforeParticles) return 1;
|
||||||
|
upload(colorBeforeParticles, before.data(), resourceWidth * 4 * sizeof(uint16_t), srv);
|
||||||
|
}
|
||||||
|
if (std::strstr(extra, "sss") != nullptr)
|
||||||
|
{
|
||||||
|
std::vector<uint16_t> guide(static_cast<size_t>(resourceWidth) * inHeight);
|
||||||
|
for (UINT y = 0; y < inHeight; ++y)
|
||||||
|
for (UINT x = 0; x < resourceWidth; ++x)
|
||||||
|
guide[static_cast<size_t>(y) * resourceWidth + x] = std::bit_cast<uint16_t>(static_cast<_Float16>(
|
||||||
|
((x / 28 + y / 28) % 3 == 0) ? 0.75f : 0.0f));
|
||||||
|
subsurfaceGuide = create_texture(resourceWidth, inHeight, DXGI_FORMAT_R16_FLOAT, D3D12_RESOURCE_FLAG_NONE);
|
||||||
|
if (!subsurfaceGuide) return 1;
|
||||||
|
upload(subsurfaceGuide, guide.data(), resourceWidth * sizeof(uint16_t), srv);
|
||||||
|
}
|
||||||
std::printf("Ray Reconstruction extra inputs:%s%s\n", hitDistance ? " DLSSD.SpecularHitDistance" : "",
|
std::printf("Ray Reconstruction extra inputs:%s%s\n", hitDistance ? " DLSSD.SpecularHitDistance" : "",
|
||||||
disocclusionMask ? " DLSS.DisocclusionMask" : "");
|
disocclusionMask ? " DLSS.DisocclusionMask" : "");
|
||||||
}
|
}
|
||||||
@@ -1520,7 +1547,9 @@ int main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
d4r_ngx_set_int(parameters, "DLSS.Denoise.Mode", 1);
|
d4r_ngx_set_int(parameters, "DLSS.Denoise.Mode", 1);
|
||||||
d4r_ngx_set_int(parameters, "DLSS.Roughness.Mode", 0);
|
d4r_ngx_set_int(parameters, "DLSS.Roughness.Mode", 0);
|
||||||
d4r_ngx_set_int(parameters, "DLSS.Use.HW.Depth", 0);
|
// D4R_HARNESS_RR_HW_DEPTH=1: the depth is the hardware (nonlinear) depth buffer, as Cyberpunk 2077 declares it
|
||||||
|
d4r_ngx_set_int(parameters, "DLSS.Use.HW.Depth",
|
||||||
|
std::getenv("D4R_HARNESS_RR_HW_DEPTH") != nullptr ? std::atoi(std::getenv("D4R_HARNESS_RR_HW_DEPTH")) : 0);
|
||||||
d4r_ngx_set_void(parameters, "WorldToViewMatrix", worldToView.data());
|
d4r_ngx_set_void(parameters, "WorldToViewMatrix", worldToView.data());
|
||||||
d4r_ngx_set_void(parameters, "ViewToClipMatrix", viewToClip.data());
|
d4r_ngx_set_void(parameters, "ViewToClipMatrix", viewToClip.data());
|
||||||
const char* preset = std::getenv("D4R_HARNESS_RR_PRESET");
|
const char* preset = std::getenv("D4R_HARNESS_RR_PRESET");
|
||||||
@@ -1580,6 +1609,10 @@ int main(int argc, char** argv)
|
|||||||
d4r_ngx_set_d3d12_resource(parameters, "DLSSD.SpecularHitDistance", hitDistance);
|
d4r_ngx_set_d3d12_resource(parameters, "DLSSD.SpecularHitDistance", hitDistance);
|
||||||
if (disocclusionMask != nullptr && replayDir == nullptr)
|
if (disocclusionMask != nullptr && replayDir == nullptr)
|
||||||
d4r_ngx_set_d3d12_resource(parameters, "DLSS.DisocclusionMask", disocclusionMask);
|
d4r_ngx_set_d3d12_resource(parameters, "DLSS.DisocclusionMask", disocclusionMask);
|
||||||
|
if (colorBeforeParticles != nullptr && replayDir == nullptr)
|
||||||
|
d4r_ngx_set_d3d12_resource(parameters, "DLSSD.ColorBeforeParticles", colorBeforeParticles);
|
||||||
|
if (subsurfaceGuide != nullptr && replayDir == nullptr)
|
||||||
|
d4r_ngx_set_d3d12_resource(parameters, "DLSSD.ScreenSpaceSubsurfaceScatteringGuide", subsurfaceGuide);
|
||||||
// DLSSD.Alpha is a guide the capture records in its own right, so the synthetic alpha
|
// DLSSD.Alpha is a guide the capture records in its own right, so the synthetic alpha
|
||||||
// scenario would double-bind that name; it belongs to the static probe only.
|
// scenario would double-bind that name; it belongs to the static probe only.
|
||||||
if (alphaScenario && replayDir == nullptr)
|
if (alphaScenario && replayDir == nullptr)
|
||||||
|
|||||||
@@ -8300,10 +8300,10 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
|
|||||||
feature.handle.Id, frame);
|
feature.handle.Id, frame);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
// NGX 310.7 runs preset D's kernels when the game names no preset (harness, 2026-10-11).
|
// NGX 310.7 runs preset E's kernels for the hint 5 only and preset D's (diamond_wallaby) for every other value:
|
||||||
|
// none, the retired A / B / C (1-3), 6, and values with the top bit set such as Cyberpunk 2077's 0x80000002 or
|
||||||
|
// 0x80000005 (harness captures with D4R_RR_PRESET, 2026-10-12).
|
||||||
const bool presetE = feature.preset == 5;
|
const bool presetE = feature.preset == 5;
|
||||||
if (feature.preset != 0 && feature.preset != 4 && feature.preset != 5)
|
|
||||||
return refuse("only presets D and E are implemented");
|
|
||||||
std::call_once(g_engineR.once[presetE], [presetE] {
|
std::call_once(g_engineR.once[presetE], [presetE] {
|
||||||
g_engineR.state[presetE] = 1;
|
g_engineR.state[presetE] = 1;
|
||||||
// D4R_ENGINE_RR_SYNC=1 (harness comparisons): load in place so the engine serves the first frame
|
// D4R_ENGINE_RR_SYNC=1 (harness comparisons): load in place so the engine serves the first frame
|
||||||
@@ -8348,7 +8348,10 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
|
|||||||
// the optional inputs a game may add (RE Requiem passes both): bound when present, like NGX
|
// the optional inputs a game may add (RE Requiem passes both): bound when present, like NGX
|
||||||
ID3D12Resource* const hitDistance = get_resource(parameters, "DLSSD.SpecularHitDistance");
|
ID3D12Resource* const hitDistance = get_resource(parameters, "DLSSD.SpecularHitDistance");
|
||||||
ID3D12Resource* const disocclusion = get_resource(parameters, "DLSS.DisocclusionMask");
|
ID3D12Resource* const disocclusion = get_resource(parameters, "DLSS.DisocclusionMask");
|
||||||
for (ID3D12Resource* input : {color, depth, motion, guides[0], guides[1], guides[2], guides[3], hitDistance, disocclusion})
|
// and Cyberpunk 2077's: the subsurface scattering guide and the colour before particles
|
||||||
|
ID3D12Resource* const subsurface = get_resource(parameters, "DLSSD.ScreenSpaceSubsurfaceScatteringGuide");
|
||||||
|
ID3D12Resource* const beforeParticles = get_resource(parameters, "DLSSD.ColorBeforeParticles");
|
||||||
|
for (ID3D12Resource* input : {color, depth, motion, guides[0], guides[1], guides[2], guides[3], hitDistance, disocclusion, subsurface, beforeParticles})
|
||||||
if (input == output)
|
if (input == output)
|
||||||
return refuse("input/output texture aliasing is not supported");
|
return refuse("input/output texture aliasing is not supported");
|
||||||
d4r::GameFrameR game;
|
d4r::GameFrameR game;
|
||||||
@@ -8372,6 +8375,10 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
|
|||||||
sources.push_back({hitDistance, &game.specularHitDistance, 0, 0, rw, rh, inputState, D3D12_RESOURCE_STATE_COPY_SOURCE});
|
sources.push_back({hitDistance, &game.specularHitDistance, 0, 0, rw, rh, inputState, D3D12_RESOURCE_STATE_COPY_SOURCE});
|
||||||
if (disocclusion != nullptr)
|
if (disocclusion != nullptr)
|
||||||
sources.push_back({disocclusion, &game.disocclusionMask, 0, 0, rw, rh, inputState, D3D12_RESOURCE_STATE_COPY_SOURCE});
|
sources.push_back({disocclusion, &game.disocclusionMask, 0, 0, rw, rh, inputState, D3D12_RESOURCE_STATE_COPY_SOURCE});
|
||||||
|
if (subsurface != nullptr)
|
||||||
|
sources.push_back({subsurface, &game.subsurfaceGuide, 0, 0, rw, rh, inputState, D3D12_RESOURCE_STATE_COPY_SOURCE});
|
||||||
|
if (beforeParticles != nullptr)
|
||||||
|
sources.push_back({beforeParticles, &game.colorBeforeParticles, 0, 0, rw, rh, inputState, D3D12_RESOURCE_STATE_COPY_SOURCE});
|
||||||
// a guide with its own subrect origin (DLSS.Input.Normals.Subrect.Base etc.) is not implemented
|
// a guide with its own subrect origin (DLSS.Input.Normals.Subrect.Base etc.) is not implemented
|
||||||
for (size_t i = 0; i < sizeof(kAuxSubrectNames) / sizeof(kAuxSubrectNames[0]); ++i)
|
for (size_t i = 0; i < sizeof(kAuxSubrectNames) / sizeof(kAuxSubrectNames[0]); ++i)
|
||||||
if (p.auxSubrect[i][0] != 0 || p.auxSubrect[i][1] != 0)
|
if (p.auxSubrect[i][0] != 0 || p.auxSubrect[i][1] != 0)
|
||||||
@@ -8437,11 +8444,14 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
|
|||||||
s.frameTimeMs = p.frameTime;
|
s.frameTimeMs = p.frameTime;
|
||||||
s.reset = p.reset != 0 || feature.engineR == nullptr;
|
s.reset = p.reset != 0 || feature.engineR == nullptr;
|
||||||
s.depthInverted = (feature.flags & 0x8) != 0;
|
s.depthInverted = (feature.flags & 0x8) != 0;
|
||||||
|
s.hardwareDepth = get_uint_or(parameters, "DLSS.Use.HW.Depth", 0) != 0;
|
||||||
s.roughnessPacked = roughnessPacked;
|
s.roughnessPacked = roughnessPacked;
|
||||||
if (frame <= 2)
|
if (frame <= 2)
|
||||||
logf("engine backend (Ray Reconstruction): roughness %s, specular hit distance %s, disocclusion mask %s",
|
logf("engine backend (Ray Reconstruction): roughness %s, specular hit distance %s, disocclusion mask %s, "
|
||||||
|
"subsurface guide %s, colour before particles %s, hardware depth %d",
|
||||||
roughnessPacked ? "packed in the normals' w" : "texture", hitDistance != nullptr ? "bound" : "none",
|
roughnessPacked ? "packed in the normals' w" : "texture", hitDistance != nullptr ? "bound" : "none",
|
||||||
disocclusion != nullptr ? "bound" : "none");
|
disocclusion != nullptr ? "bound" : "none", subsurface != nullptr ? "bound" : "none",
|
||||||
|
beforeParticles != nullptr ? "bound" : "none", s.hardwareDepth ? 1 : 0);
|
||||||
std::copy(std::begin(p.auxMatrix[0]), std::end(p.auxMatrix[0]), s.worldToView);
|
std::copy(std::begin(p.auxMatrix[0]), std::end(p.auxMatrix[0]), s.worldToView);
|
||||||
std::copy(std::begin(p.auxMatrix[1]), std::end(p.auxMatrix[1]), s.viewToClip);
|
std::copy(std::begin(p.auxMatrix[1]), std::end(p.auxMatrix[1]), s.viewToClip);
|
||||||
try
|
try
|
||||||
|
|||||||
Reference in new issue
Block a user