save code

This commit is contained in:
xsl
2026-04-26 00:12:56 +08:00
parent 50680ee854
commit c5ec7c7dc5
22 changed files with 1170 additions and 216 deletions
+86
View File
@@ -49,6 +49,15 @@ struct InitArg
};
// 推送给 shader 的常量。前 9 个 float 是「业务原始值」,单位以 480x480 设计画布
// 为基准(向后兼容):业务给 radius=240 / offset_x=200 / offset_y=-200 等都按这个画布
// 思考。后 3 个 float 是 SDK 在每次 render() 时根据当前 swapchain 尺寸计算并写入的
// 「画布几何」,shader 通过它把「画布坐标」映射到「屏幕坐标」并把圆形 mask 中心
// 锚定在屏幕中心。
//
// ★ 字段顺序和命名必须和 GLSL 中的 layout(push_constant) 块严格一致,否则会读到
// 错位数据。修改时四个 shader (bg.vert / bg.frag / texture.vert / texture.frag)
// 都要同步更新。
struct PushConstants {
float zoom = 0.5f;
float r = 0;
@@ -59,6 +68,32 @@ struct PushConstants {
float uy = 0;
float offset_x = 0;
float offset_y = 0;
// 由 SDK 在 FaceApp::render() 内每帧写入,业务无感。
// canvas_size = min(swapchain.w, swapchain.h),单位:物理像素
// screen_w / screen_h = swapchain 当前实际尺寸,单位:物理像素
// 设计画布始终居中铺在屏幕中央正方形区域;外围是 letterbox 黑边。
float canvas_size = 480.0f;
float screen_w = 480.0f;
float screen_h = 480.0f;
// 相机帧元信息,由 SDK 在收到第一帧 / 帧尺寸变化时更新。shader 用来:
// 1) 把相机帧(任意 raw aspect)正确 cover 到 1:1 画布;
// 2) 按 camera_rotation 旋转 UV,让画面在屏幕上正立——这是不同手机的
// sensor orientation 不同导致画面侧着 / 倒着的根因。
// camera_aspect = camera_raw_w / camera_raw_h(典型 4:3 = 1.333…)
// camera_rotation = CameraX 的 ImageInfo.getRotationDegrees()
// 表示「图像顺时针旋转多少度才能正立」,离散 0/90/180/270。
// 默认值 4/3 + 90° 是兼容老 hardcode 行为(大部分 Android 手机后置主摄竖屏)。
float camera_aspect = 4.0f / 3.0f;
float camera_rotation = 90.0f;
// 镜像标志:1.0 表示前置摄像头需要"镜子效果",所有顶点 shader 会把
// gl_Position.x 翻转一次(NDC 水平翻转)。配合 FaceLandmarkerHelper 在
// bitmap 阶段做的 postScale(-1,1)bg 与 face 同步翻转、互相对齐。
// 后置摄像头此值为 0.0,shader 短路无开销。
// 用 float 而不是 bool/int 是为了和 Vulkan push constant 4-byte 对齐 + GLSL
// 端 layout 简单一致;shader 里 `1.0 - 2.0 * mirror_x` 实现无分支翻转。
float mirror_x = 0.0f;
};
using Callback = std::function<void()>;
@@ -163,13 +198,64 @@ private:
//float myFloatValue = 1.5f;
// 「业务原始值」缓存:SetInitArg 把 InitArg 拷到这里,render() 每帧读出来再
// 缩放 + 注入屏幕几何。注意 ux/uy 由 motion 帧每帧写入,也保留在这里。
PushConstants pushConstants;
// ===== 屏幕分辨率适配 =====
// 原 SDK 把屏幕写死 480x480,所有业务参数都按 480 像素画布给。现在改成支持
// 任意分辨率:把"屏幕中央 min(w,h) 边长的正方形"作为设计画布,画布外是黑边。
// 业务参数语义不变,SDK 在 render() 里按 (canvas_size / 480) 缩放成屏幕物理
// 像素后再推给 shader。
// m_canvasSize = min(swapChainExtent.w, swapChainExtent.h)
// m_screenW / m_screenH = swapChainExtent.w / swapChainExtent.h
// updateCanvasMetrics() 由 render() 在每次 vkCmdPushConstants 之前调用。
// 因为 swapchain 重建会走 onWindowLost -> onWindowInit ->
// recreatePipelinesForSwapchain,而 render() 永远在 drawFrame 里被调,所以
// 这套数值天然会跟随 swapchain 变化。
float m_canvasSize = 480.0f;
float m_screenW = 480.0f;
float m_screenH = 480.0f;
void updateCanvasMetrics();
// ===== 相机帧元信息适配 =====
// 不同手机摄像头物理分辨率和 sensor orientation 都不一样,CameraX 给的图像
// 既不一定是精确 4:3,也不一定是「正立」朝向。SDK 在这里缓存最近一帧的
// (raw_aspect, rotation)render() 时塞进 push constant 让 shader 自己处理:
// - bg.vert 按 m_cameraRotation 旋转 UV,让画面在屏幕上方向正确
// - bg.vert 按 m_cameraAspect 自适应 cover 画布,避免拉扁/拉长
// 由 processCameraFrame() 根据每次相机帧实际 width / height / rotation 更新。
// 第一帧之前用安全默认值(4:3 + 90°,对应大部分 Android 后置主摄竖屏行为)。
float m_cameraAspect = 4.0f / 3.0f;
float m_cameraRotation = 90.0f;
// 镜像标志缓存:与 m_cameraAspect 同写者(相机分析线程),由 render() 拷到
// push constant 的 mirror_x 字段。前置=1.0,后置=0.0。
float m_mirrorX = 0.0f;
public:
// JNI 入口(main.cpp)调用,用来在每帧上传相机纹理之前同步元信息。
// 内部会:
// 1) 检测 width × height 与已有 tex_bg 是否一致,不一致就 destroy + recreate
// 2) 更新 m_cameraAspect / m_cameraRotation / m_mirrorX
// 3) 调用 base 的 processWithVulkan 走 staging buffer 上传。
// mirrorX 来自 Java 层 lensFacing == LENS_FACING_FRONT,表示是否需要镜子效果。
// 必须由唯一的相机分析线程调用(FaceActivity backgroundExecutor),
// 内部依赖 createTextureMtx 序列化与 drawFrame / cleanup 的并发。
void processCameraFrame(uint8_t* data, int width, int height,
int rowStride, size_t dataSize, int rotation,
bool mirrorX);
private:
void create_pipelines_bg();
void setup_descriptor_set_layout_bg();
void setup_descriptor_set_bg();
// 把 m_descriptor_set_bg 重新绑定到当前的 tex_bg.view / sampler。
// 当相机分辨率变化导致 tex_bg 被 destroy + recreate 后,新建的 image
// 有新的 VkImageView 句柄,旧的 descriptor set 里缓存的 handle 失效,
// 必须 vkUpdateDescriptorSets 让 descriptor 指向新 view。该函数不重新
// 分配 descriptor set,仅刷新绑定。
void refresh_descriptor_set_bg();
VkPipeline m_graphicsPipeline_bg = VK_NULL_HANDLE;
VkPipelineLayout m_pipelineLayout_bg = VK_NULL_HANDLE;
VkDescriptorSetLayout m_descriptorSetLayout_bg = VK_NULL_HANDLE;