forked from unom/punktfunk
encode.rs + encode/* (NVENC, VAAPI, native AMF, AMF/QSV ffmpeg, direct-SDK NVENC/CUDA, raw Vulkan-Video, PyroWave, openh264) move into crates/pf-encode behind one Encoder trait + open_video selector (plan §W6). The crate speaks the shared frame vocabulary (pf-frame: CapturedFrame/PixelFormat + the DXGI identity D3d11Frame/make_device) and pf-zerocopy (CUDA context/buffers), and NEVER pf-capture — the capture→encode edge is one-way (ZeroCopyPolicy, prior commit). Dep moves: the heavy encoder deps (ffmpeg-next, the NVENC SDK, openh264, pyrowave-sys) move from the host to pf-encode; the host's nvenc/amf-qsv/vulkan-encode/pyrowave features now FORWARD to pf-encode/*. The host keeps a mod-encode shim (pub use pf_encode) so every crate::encode::* path (negotiator + GameStream/native/mgmt planes) is unchanged. resolve_render_adapter_luid moves from the host's windows/win_adapter.rs into pf-gpu (both pf-encode and pf-capture need it as a peer of GPU selection); its 5 call sites (encode amf/nvenc, capture idd_push/synthetic_nv12, vdisplay manager) rewire to pf_gpu::resolve_render_adapter_luid and win_adapter.rs is deleted. pf-frame's make_device gains a # Safety section (public-unsafe-fn lint, latent since the pf-frame carve — a full-workspace -D warnings clippy catches it). Verified: Linux clippy -D warnings (pf-encode + host nvenc,vulkan-encode,pyrowave --all-targets) + 13/13 pf-encode + 299/299 host tests; Windows clippy -D warnings (pf-encode nvenc,amf-qsv --all-targets + host nvenc,amf-qsv --all-targets) Finished exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
2.9 KiB
Plaintext
52 lines
2.9 KiB
Plaintext
#version 450
|
|
// RGB(A) -> NV12 (BT.709 limited range). One invocation per chroma sample = 2x2 luma block.
|
|
// Optionally blends a straight-alpha RGBA cursor bitmap over the RGB *before* the YUV conversion
|
|
// (so the chroma stays correct) — cursor-as-metadata for the GPU zero-copy paths. The blend is
|
|
// gated by the push constant and touches only the cursor's small rectangle, so a disabled or
|
|
// off-screen cursor costs one compare per invocation.
|
|
layout(local_size_x = 8, local_size_y = 8) in;
|
|
layout(binding = 0) uniform sampler2D rgb; // packed RGB input (sampled; BGRA import ok)
|
|
layout(binding = 1, r8) uniform writeonly image2D yImg; // full-res Y
|
|
layout(binding = 2, rg8) uniform writeonly image2D uvImg; // half-res UV (interleaved)
|
|
layout(binding = 3) uniform sampler2D cursorTex; // straight-alpha RGBA cursor (top-left)
|
|
|
|
layout(push_constant) uniform Push {
|
|
ivec2 curOrigin; // top-left of the cursor in frame pixels (position - hotspot)
|
|
ivec2 curSize; // cursor w,h in pixels; x <= 0 => disabled
|
|
} pc;
|
|
|
|
float lumaY(vec3 c) { return 16.0/255.0 + 0.1826*c.r + 0.6142*c.g + 0.0620*c.b; }
|
|
|
|
// Blend the cursor over `col` at frame pixel `p`, when `p` falls inside the cursor rectangle.
|
|
vec3 withCursor(ivec2 p, vec3 col) {
|
|
if (pc.curSize.x <= 0) return col;
|
|
ivec2 cp = p - pc.curOrigin;
|
|
if (cp.x < 0 || cp.y < 0 || cp.x >= pc.curSize.x || cp.y >= pc.curSize.y) return col;
|
|
vec4 c = texelFetch(cursorTex, cp, 0);
|
|
return mix(col, c.rgb, c.a);
|
|
}
|
|
|
|
// Source may be SMALLER than the coded (16-aligned) Y plane — e.g. 1080 source vs 1088 coded. Clamp
|
|
// every fetch to the source edge so the alignment-padding rows duplicate the last real row instead
|
|
// of reading out of bounds (undefined → green garbage that shows if a client ignores the SPS
|
|
// conformance-window crop). `textureSize` gives the bound source's real extent.
|
|
void main() {
|
|
ivec2 sz = imageSize(yImg);
|
|
ivec2 rmax = textureSize(rgb, 0) - 1;
|
|
ivec2 uvc = ivec2(gl_GlobalInvocationID.xy);
|
|
ivec2 p = uvc * 2;
|
|
if (p.x >= sz.x || p.y >= sz.y) return;
|
|
vec3 c00 = withCursor(p, texelFetch(rgb, min(p, rmax), 0).rgb);
|
|
vec3 c10 = withCursor(p + ivec2(1, 0), texelFetch(rgb, min(p + ivec2(1, 0), rmax), 0).rgb);
|
|
vec3 c01 = withCursor(p + ivec2(0, 1), texelFetch(rgb, min(p + ivec2(0, 1), rmax), 0).rgb);
|
|
vec3 c11 = withCursor(p + ivec2(1, 1), texelFetch(rgb, min(p + ivec2(1, 1), rmax), 0).rgb);
|
|
imageStore(yImg, p, vec4(lumaY(c00), 0, 0, 1));
|
|
imageStore(yImg, p + ivec2(1, 0), vec4(lumaY(c10), 0, 0, 1));
|
|
imageStore(yImg, p + ivec2(0, 1), vec4(lumaY(c01), 0, 0, 1));
|
|
imageStore(yImg, p + ivec2(1, 1), vec4(lumaY(c11), 0, 0, 1));
|
|
vec3 a = (c00 + c10 + c01 + c11) * 0.25;
|
|
float U = 128.0/255.0 - 0.1006*a.r - 0.3386*a.g + 0.4392*a.b;
|
|
float V = 128.0/255.0 + 0.4392*a.r - 0.3989*a.g - 0.0403*a.b;
|
|
imageStore(uvImg, uvc, vec4(U, V, 0, 1));
|
|
}
|