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;
|
||||
uint32_t width, height, outputWidth, outputHeight;
|
||||
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;
|
||||
// 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
|
||||
@@ -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);
|
||||
// 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,
|
||||
&frame.specularHitDistance, &frame.disocclusionMask};
|
||||
const VkFormat guideFormats[6] = {VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16_SFLOAT};
|
||||
const GameImage* guides[8] = {&frame.normals, &frame.roughness, &frame.diffuseAlbedo, &frame.specularAlbedo,
|
||||
&frame.specularHitDistance, &frame.disocclusionMask, &frame.subsurfaceGuide,
|
||||
&frame.colorBeforeParticles};
|
||||
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; };
|
||||
if (!g.started) {
|
||||
if (!g.model.uploadRecorded()) g.model.recordUpload(cb);
|
||||
@@ -111,7 +113,7 @@ void GameDenoiser::record(VkCommandBuffer cb, const GameFrameR& frame) {
|
||||
g.started = true;
|
||||
}
|
||||
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 (!g.guides[i].image) {
|
||||
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.motion = directMotion ? frame.motion.view : g.fallback.motion.view;
|
||||
images.depth = directDepth ? frame.depth.view : g.fallback.depth.view;
|
||||
VkImageView* guideViews[6] = {&images.normals, &images.roughness, &images.diffuseAlbedo, &images.specularAlbedo,
|
||||
&images.specularHitDistance, &images.disocclusionMask};
|
||||
for (int i = 0; i < 6; ++i)
|
||||
VkImageView* guideViews[8] = {&images.normals, &images.roughness, &images.diffuseAlbedo, &images.specularAlbedo,
|
||||
&images.specularHitDistance, &images.disocclusionMask, &images.subsurfaceGuide,
|
||||
&images.colorBeforeParticles};
|
||||
for (int i = 0; i < 8; ++i)
|
||||
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 &&
|
||||
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;
|
||||
// optional (image null: the game passes none): DLSSD.SpecularHitDistance, DLSS.DisocclusionMask
|
||||
GameImage specularHitDistance, disocclusionMask;
|
||||
// optional too: DLSSD.ScreenSpaceSubsurfaceScatteringGuide, DLSSD.ColorBeforeParticles
|
||||
GameImage subsurfaceGuide, colorBeforeParticles;
|
||||
RFrameSettings settings;
|
||||
};
|
||||
class GameDenoiser {
|
||||
|
||||
@@ -159,8 +159,8 @@ int main(int argc, char** argv) try {
|
||||
VK_FORMAT_R8G8B8A8_UNORM};
|
||||
std::vector<Input> inputs;
|
||||
for (VkFormat f : formats) inputs.emplace_back(c, f, rw, rh);
|
||||
const char* optionalNames[] = {"hitdistance", "disocclusion"};
|
||||
const VkFormat optionalFormats[] = {VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16_SFLOAT};
|
||||
const char* optionalNames[] = {"hitdistance", "disocclusion", "sss", "particles"};
|
||||
const VkFormat optionalFormats[] = {VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT};
|
||||
std::vector<Input> optional;
|
||||
for (VkFormat f : optionalFormats) optional.emplace_back(c, f, rw, rh);
|
||||
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;
|
||||
for (float& v : s.worldToView) 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) {
|
||||
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]);
|
||||
@@ -195,7 +195,7 @@ int main(int argc, char** argv) try {
|
||||
images.normals = inputs[3].view; images.roughness = inputs[4].view;
|
||||
images.diffuseAlbedo = inputs[5].view; images.specularAlbedo = inputs[6].view;
|
||||
// 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);
|
||||
if (!probe) continue;
|
||||
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);
|
||||
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);
|
||||
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,
|
||||
`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.
|
||||
|
||||
## 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)); }
|
||||
int s16(uint u) { return (int(u) << 16) >> 16; }
|
||||
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
|
||||
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]; }
|
||||
@@ -509,10 +510,11 @@ class RRT(base.T):
|
||||
off = self.handle_off(h)
|
||||
self.textures.add(off)
|
||||
if 'base' in o and o[-1] == 's32':
|
||||
# integer coordinates still go through the texture's address mode in CUDA (clamp for every denoiser
|
||||
# texture); texelFetch does not clamp, and what it returns out of range depends on the device's robustness
|
||||
fetch = (f'texelFetch(T{off}, clamp(ivec2({self.val(c[0], "s32")}, {self.val(c[1], "s32")}), ivec2(0), '
|
||||
f'textureSize(T{off}, 0) - 1), 0)')
|
||||
# an integer fetch outside the image: CUDA applies the texture's address mode (clamp for every denoiser
|
||||
# texture), but the reference this engine is checked against is ZLUDA, whose image load returns zero there
|
||||
# (zluda_ptx_impl.cpp fetch_2D). The output stage's specular reprojection reads such neighbours at the image
|
||||
# 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':
|
||||
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':
|
||||
|
||||
@@ -20,7 +20,8 @@ import numpy as np
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
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}
|
||||
|
||||
|
||||
@@ -49,6 +50,10 @@ def main():
|
||||
for name, off in OPTIONAL.items(): # DLSSD.SpecularHitDistance, DLSS.DisocclusionMask when the game passed them
|
||||
if off in images:
|
||||
(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)
|
||||
view = mat(596) @ np.linalg.inv(clip)
|
||||
view[3, :3] = 0.0
|
||||
@@ -56,7 +61,7 @@ def main():
|
||||
mvx, mvy = f32(344) * rw / ow, f32(348) * rh / oh
|
||||
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()))
|
||||
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')
|
||||
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;
|
||||
// Optional inputs (null: the game passes none): DLSSD.SpecularHitDistance and DLSS.DisocclusionMask, one channel each
|
||||
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
|
||||
};
|
||||
struct RFrameSettings {
|
||||
@@ -264,6 +266,7 @@ struct RFrameSettings {
|
||||
float frameTimeMs = 16.666667f; // FrameTimeDeltaInMsec
|
||||
bool reset = false;
|
||||
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)
|
||||
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};
|
||||
|
||||
+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,
|
||||
// and e: this frame's and the previous frame's exposure (1x1)
|
||||
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)
|
||||
std::map<std::string, Tex> spare; // per format, a 1x1 surface for stores NGX leaves unbound
|
||||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||
@@ -201,6 +205,7 @@ struct REngine::Impl {
|
||||
// the optional inputs (capDX, 2026-10-12): unbound without them, like NGX
|
||||
case 248: return in.specularHitDistance ? G(in.specularHitDistance) : 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);
|
||||
} 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]);
|
||||
@@ -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 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]);
|
||||
// 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) {
|
||||
case 224: return T(h[par]); case 240: return Resource{output, false, false};
|
||||
case 384: return in.colorBeforeParticles ? T(pb[par]) : Resource{};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
@@ -398,6 +407,8 @@ void REngine::setFrame(const RFrameImages& images, const RFrameSettings& setting
|
||||
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, 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
|
||||
// composed with the render viewport (NDC <-> render pixels, y down)
|
||||
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);
|
||||
putF(o, 152, settings.frameTimeMs);
|
||||
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));
|
||||
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
|
||||
const uint32_t gx = exposureGrid(e.rw, e.ow), gy = exposureGrid(e.rh, e.oh);
|
||||
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;
|
||||
// descriptors for the caller's images, cached per combination: every stage, both frame parities, bound by the
|
||||
// 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
|
||||
// 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).
|
||||
@@ -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,
|
||||
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);
|
||||
// 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);
|
||||
|
||||
Reference in new issue
Block a user