save code
This commit is contained in:
+244
-40
@@ -1,4 +1,4 @@
|
||||
#include "FaceApp.h"
|
||||
#include "FaceApp.h"
|
||||
#include "hardcode_data.h"
|
||||
#include "lodepng.h"
|
||||
#include <sstream>
|
||||
@@ -47,10 +47,13 @@ void FaceApp::Stop()
|
||||
void ReceiveFacePoint(float* pos, int pointCount, int width, int height)
|
||||
{
|
||||
FaceApp* self = FaceApp::Get();
|
||||
if (self != nullptr)
|
||||
if (self == nullptr)
|
||||
{
|
||||
FaceApp::Get()->update_face_vertex_buffer(pos, pointCount);
|
||||
FACE_DBG_LOG_THROTTLED("ReceiveFacePoint.noFaceApp",
|
||||
"ReceiveFacePoint dropped: FaceApp::Get() == nullptr");
|
||||
return;
|
||||
}
|
||||
self->update_face_vertex_buffer(pos, pointCount);
|
||||
}
|
||||
|
||||
// 定义帧数据结构
|
||||
@@ -60,10 +63,12 @@ struct FrameData {
|
||||
int height;
|
||||
int rowStride;
|
||||
size_t dataSize;
|
||||
int rotation; // CameraX 给的 rotationDegrees,用于 shader 端 UV 旋转
|
||||
bool mirrorX; // 前置摄像头时为 true,shader 在 gl_Position.x 翻转一次
|
||||
|
||||
FrameData(uint8_t* d, int w, int h, int rs, size_t ds)
|
||||
: data(nullptr), width(w), height(h), rowStride(rs), dataSize(ds) {
|
||||
// 深拷贝数据
|
||||
FrameData(uint8_t* d, int w, int h, int rs, size_t ds, int rot, bool mx)
|
||||
: data(nullptr), width(w), height(h), rowStride(rs), dataSize(ds),
|
||||
rotation(rot), mirrorX(mx) {
|
||||
if (d && ds > 0) {
|
||||
data = new uint8_t[dataSize];
|
||||
memcpy(data, d, dataSize);
|
||||
@@ -77,18 +82,16 @@ struct FrameData {
|
||||
}
|
||||
}
|
||||
|
||||
// 禁止拷贝构造和赋值(使用移动语义)
|
||||
FrameData(const FrameData&) = delete;
|
||||
FrameData& operator=(const FrameData&) = delete;
|
||||
|
||||
// 移动构造
|
||||
FrameData(FrameData&& other) noexcept
|
||||
: data(other.data), width(other.width), height(other.height),
|
||||
rowStride(other.rowStride), dataSize(other.dataSize) {
|
||||
rowStride(other.rowStride), dataSize(other.dataSize),
|
||||
rotation(other.rotation), mirrorX(other.mirrorX) {
|
||||
other.data = nullptr;
|
||||
}
|
||||
|
||||
// 移动赋值
|
||||
FrameData& operator=(FrameData&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (data) delete[] data;
|
||||
@@ -97,6 +100,8 @@ struct FrameData {
|
||||
height = other.height;
|
||||
rowStride = other.rowStride;
|
||||
dataSize = other.dataSize;
|
||||
rotation = other.rotation;
|
||||
mirrorX = other.mirrorX;
|
||||
other.data = nullptr;
|
||||
}
|
||||
return *this;
|
||||
@@ -108,7 +113,7 @@ std::queue<FrameData> frameQueue;
|
||||
std::mutex queueMutex;
|
||||
const int MAX_QUEUE_SIZE = 4;
|
||||
|
||||
void TextureLoadProcessWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize)
|
||||
void TextureLoadProcessWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize, int rotation, bool mirrorX)
|
||||
{
|
||||
if (!FaceApp::Get()->isInited()) {
|
||||
return;
|
||||
@@ -116,24 +121,25 @@ void TextureLoadProcessWithVulkan(uint8_t* data, int width, int height, int rowS
|
||||
|
||||
std::lock_guard<std::mutex> lock(queueMutex);
|
||||
|
||||
// 添加新帧数据到队列
|
||||
frameQueue.emplace(data, width, height, rowStride, dataSize);
|
||||
frameQueue.emplace(data, width, height, rowStride, dataSize, rotation, mirrorX);
|
||||
|
||||
// 如果队列大小达到阈值,开始处理最旧的一帧
|
||||
if (frameQueue.size() >= MAX_QUEUE_SIZE) {
|
||||
Texture& tex_bg = FaceApp::Get()->tex_bg;
|
||||
FrameData& oldestFrame = frameQueue.front();
|
||||
|
||||
FaceApp::Get()->processWithVulkan(
|
||||
// 走 FaceApp::processCameraFrame:相比 base::processWithVulkan,它多做两件
|
||||
// 事 —— ① 检测帧尺寸变化时 destroy/recreate tex_bg(不同手机或 CameraX
|
||||
// 重建 session 后第一帧尺寸可能与之前不同);② 缓存 raw_aspect / rotation
|
||||
// / mirrorX 到 m_cameraAspect / m_cameraRotation / m_mirrorX,由 render()
|
||||
// 推到 push constant。
|
||||
FaceApp::Get()->processCameraFrame(
|
||||
oldestFrame.data,
|
||||
oldestFrame.width,
|
||||
oldestFrame.height,
|
||||
oldestFrame.rowStride,
|
||||
oldestFrame.dataSize,
|
||||
tex_bg,
|
||||
false,
|
||||
FaceApp::Get()->commandPool,
|
||||
"data_from_camera"
|
||||
oldestFrame.rotation,
|
||||
oldestFrame.mirrorX
|
||||
);
|
||||
|
||||
frameQueue.pop();
|
||||
@@ -298,15 +304,21 @@ void FaceApp::create_face_pipelines()
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_state{};
|
||||
rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rasterization_state.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
// ★ 关键:face 必须 NONE。原因:
|
||||
// 1. face mesh 是 2D 平面网格(texture.vert 让所有顶点 z=0.5),不存在
|
||||
// 自遮挡,没必要 cull;
|
||||
// 2. 前置摄像头镜子效果是在 vertex shader 里通过 NDC.x 翻转实现的
|
||||
// (`position.x *= (1.0 - 2.0 * pc.mirror_x)`)。X 翻转会把所有三角
|
||||
// 形的卷绕方向从 CCW 变 CW。如果这里 cullMode=BACK_BIT + frontFace=
|
||||
// CCW,前置时整张脸会被 cull,face 完全不可见——这正是历史踩坑点。
|
||||
// 所以请勿改回 BACK_BIT。如果未来要再加 cull,必须配两套 pipeline(前置
|
||||
// 用 CW、后置用 CCW),或在前置时改成 frontFace=CW,否则一定会复现这个 bug。
|
||||
rasterization_state.cullMode = VK_CULL_MODE_NONE;
|
||||
rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rasterization_state.flags = 0;
|
||||
rasterization_state.depthClampEnable = VK_FALSE;
|
||||
rasterization_state.lineWidth = 1.0f;
|
||||
|
||||
rasterization_state.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||
rasterization_state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
|
||||
|
||||
|
||||
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
|
||||
@@ -333,13 +345,16 @@ void FaceApp::create_face_pipelines()
|
||||
colorBlending.attachmentCount = 1;
|
||||
colorBlending.pAttachments = &colorBlendAttachment;
|
||||
|
||||
// Note: Using reversed depth-buffer for increased precision, so Greater depth values are kept
|
||||
// face 是 2D 平面网格(texture.vert 让顶点 z=0.5 一致),完全不需要 depth
|
||||
// test/write。bg 管线本来也是关 depth 的;保持两边一致,让"先 bg 再 face"
|
||||
// 的覆盖顺序由 draw 顺序而非 depth 决定,避免 reversed-depth(GREATER)+
|
||||
// renderpass 没显式 clear depth attachment 时引发的"face fragment 全部
|
||||
// 被 depth test fail"的隐性 bug。
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil_state = {};
|
||||
|
||||
depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
depth_stencil_state.depthTestEnable = VK_TRUE;
|
||||
depth_stencil_state.depthWriteEnable = VK_TRUE;
|
||||
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_GREATER;
|
||||
depth_stencil_state.depthTestEnable = VK_FALSE;
|
||||
depth_stencil_state.depthWriteEnable = VK_FALSE;
|
||||
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_ALWAYS;
|
||||
depth_stencil_state.front = depth_stencil_state.back;
|
||||
depth_stencil_state.back.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
|
||||
@@ -617,6 +632,8 @@ void FaceApp::setup_descriptor_set()
|
||||
|
||||
void FaceApp::update_descriptor_set(vector<Texture>& texs, vector<VkDescriptorSet>& descriptSet)
|
||||
{
|
||||
FACE_DBG_LOG("update_descriptor_set: texs.size=%zu descriptSet.size=%zu",
|
||||
texs.size(), descriptSet.size());
|
||||
for (int i = 0; i < texs.size(); ++i)
|
||||
{
|
||||
if (texs[i].image == VK_NULL_HANDLE)
|
||||
@@ -697,21 +714,84 @@ void FaceApp::render(VkCommandBuffer commandBuffer, long long frameTime)
|
||||
|
||||
//Application::render(commandBuffer);
|
||||
|
||||
// === 分辨率适配:每帧把"业务参数"缩放到当前屏幕物理像素 ===
|
||||
// 业务层(MotionManager / InitArg)传进来的 radius / offset_x / offset_y 一直
|
||||
// 都是按"480x480 设计画布"给的,比如 radius=240 表示在 480 屏上半径 240px
|
||||
// (即 50% 短边)。现在屏幕变成任意分辨率后,要保持视觉一致:
|
||||
// 实际像素 = 业务值 * (画布短边 / 480)
|
||||
// 同时把 canvas/screen 几何写进 push constant,shader 用它们:
|
||||
// 1) 把"画布 NDC ∈ [-1,+1]" 缩到屏幕中央正方形(letterbox);
|
||||
// 2) 把 fragment 里的圆心从写死的 (240,240) 改成屏幕真实中心。
|
||||
//
|
||||
// 注意 pushConstants 本身保留业务原始值不动(SetInitArg 设进来的),
|
||||
// 这里只把缩放后的副本 pc 推给 GPU。下面 ux/uy 仍然写回 pushConstants,
|
||||
// 是因为它们和分辨率无关,是 motion 帧的纹理坐标。
|
||||
updateCanvasMetrics();
|
||||
PushConstants pc = pushConstants;
|
||||
const float k = m_canvasSize / 480.0f;
|
||||
pc.radius *= k;
|
||||
pc.offset_x *= k;
|
||||
pc.offset_y *= k;
|
||||
pc.canvas_size = m_canvasSize;
|
||||
pc.screen_w = m_screenW;
|
||||
pc.screen_h = m_screenH;
|
||||
// 相机帧元信息:由 processCameraFrame 在每帧上传纹理之前更新。第一帧之前
|
||||
// (还没收到相机数据时)m_cameraAspect/m_cameraRotation 是构造函数设的
|
||||
// 4:3 + 90° 默认值 —— 这两个值正是 CameraX RATIO_4_3 + 大部分 Android 后置
|
||||
// 主摄竖屏的典型组合,所以即使首帧 bg 用默认值渲染也不会出现明显错位。
|
||||
pc.camera_aspect = m_cameraAspect;
|
||||
pc.camera_rotation = m_cameraRotation;
|
||||
// mirror_x: 前置摄像头时 = 1.0,所有顶点 shader 在最后输出 gl_Position 前
|
||||
// 把 NDC.x 翻转一次,达到镜子效果(用户右手 → 屏幕左侧)。bg 与 face 都
|
||||
// 同步翻转,对齐保持。后置时 = 0.0,shader 公式 (1 - 2 * mirror_x) = 1 短路。
|
||||
pc.mirror_x = m_mirrorX;
|
||||
|
||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout_bg, 0, 1, &m_descriptor_set_bg, 0, NULL);
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsPipeline_bg);
|
||||
vkCmdPushConstants(commandBuffer, m_pipelineLayout_bg, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(pushConstants), &pushConstants);
|
||||
// bg.frag 也读 push constant(r/g/b/radius/screen_w/screen_h)所以 stage 必须
|
||||
// 同时包含 fragment;老代码这里只写了 VERTEX_BIT,是个隐藏的 spec 违规。
|
||||
vkCmdPushConstants(commandBuffer, m_pipelineLayout_bg, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pc), &pc);
|
||||
|
||||
|
||||
|
||||
vkCmdDraw(commandBuffer, 6, 1, 0, 0);
|
||||
|
||||
// 诊断:把 face 渲染所有"前置门"状态打成一行节流日志。一旦 face 没出现,
|
||||
// 直接看这条就能知道断在哪一关:
|
||||
// _isChangeMostion=0 → 业务还没调 PlayMotionList / changeMotionList
|
||||
// _curMotions=0 → motion list 是空的(getMotionByName 全 miss)
|
||||
// _curMotionIndex 越界 → 业务逻辑或 callback 状态机错了
|
||||
// delta_last_update >= 2000 → mediapipe / passDataToNative 链路 2s 没动静
|
||||
const long long now_ms = getCurrentTimeMillis();
|
||||
const long long delta_lu = now_ms - (long long)last_update_time;
|
||||
FACE_DBG_LOG_THROTTLED("render.face.gate",
|
||||
"render.face gate _isChangeMostion=%d _curMotions=%zu"
|
||||
" _curMotionIndex=%d _curFrameIndex=%d delta_lastUpdate=%lldms"
|
||||
" _playMotion=%d",
|
||||
(int)_isChangeMostion, _curMotions.size(),
|
||||
(int)_curMotionIndex, (int)_curFrameIndex,
|
||||
delta_lu, (int)_playMotion);
|
||||
|
||||
if (!_isChangeMostion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_curMotions.empty())
|
||||
{
|
||||
FACE_DBG_LOG_THROTTLED("render.face.emptyMotions",
|
||||
"render.face dropped: _isChangeMostion=1 but _curMotions is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
string curMotionName = _curMotions[_curMotionIndex].name;
|
||||
int loadMotionIndex = motion_list_map[curMotionName];
|
||||
|
||||
// face draw 前关键参数节流日志,保留少量"任何时候挂了能立刻判断断点"的字段。
|
||||
FACE_DBG_LOG_THROTTLED("render.face.preDraw",
|
||||
"render.face preDraw motion='%s' loadMotionIdx=%d obj_idx=%zu mirror=%.1f",
|
||||
curMotionName.c_str(), loadMotionIndex, obj_indices.size(), pc.mirror_x);
|
||||
|
||||
//if (cur_left)
|
||||
//{
|
||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &m_descriptor_sets_left[loadMotionIndex], 0, NULL);
|
||||
@@ -730,12 +810,14 @@ void FaceApp::render(VkCommandBuffer commandBuffer, long long frameTime)
|
||||
float uy = _curMotions[_curMotionIndex].frames[_curFrameIndex].y;
|
||||
pushConstants.ux = ux;
|
||||
pushConstants.uy = uy;
|
||||
pc.ux = ux;
|
||||
pc.uy = uy;
|
||||
}
|
||||
}
|
||||
//fsValues[1] = 0;
|
||||
//fsValues[2] = 360;
|
||||
|
||||
vkCmdPushConstants(commandBuffer, m_pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT| VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pushConstants), &pushConstants);
|
||||
vkCmdPushConstants(commandBuffer, m_pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT| VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pc), &pc);
|
||||
//vkCmdPushConstants(commandBuffer, m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(fsValues), fsValues);
|
||||
|
||||
|
||||
@@ -751,7 +833,16 @@ void FaceApp::render(VkCommandBuffer commandBuffer, long long frameTime)
|
||||
#else
|
||||
if (getCurrentTimeMillis() - last_update_time < 2000)
|
||||
{
|
||||
vkCmdDrawIndexed(commandBuffer, obj_indices.size(), 1, 0, 0, 0);
|
||||
FACE_DBG_LOG_THROTTLED("render.face.draw",
|
||||
"render.face vkCmdDrawIndexed indices=%zu motion=%s frame=%d",
|
||||
obj_indices.size(), curMotionName.c_str(), (int)_curFrameIndex);
|
||||
vkCmdDrawIndexed(commandBuffer, obj_indices.size(), 1, 0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FACE_DBG_LOG_THROTTLED("render.face.heartbeatStale",
|
||||
"render.face SKIP vkCmdDrawIndexed: delta_last_update=%lldms (>= 2000ms)",
|
||||
(long long)(getCurrentTimeMillis() - last_update_time));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -964,11 +1055,14 @@ void FaceApp::recreatePipelinesForSwapchain()
|
||||
|
||||
void FaceApp::update_uniform_buffers()
|
||||
{
|
||||
uint32_t width = 480;
|
||||
uint32_t height = 480;
|
||||
float zoom = 2;
|
||||
// 注意:当前 vertex shader (texture.vert) 实际并没有使用 ubo.projection 把模型
|
||||
// 投影到屏幕——它直接拿 inPos.xy*2-1 当 NDC 用。所以下面 aspect 取 1.0 只是
|
||||
// 为了让 ubo 不再依赖原本写死的 480x480 输入分辨率;要真正影响屏幕显示比例的
|
||||
// 是 push constant 里的 canvas_size / screen_w / screen_h,由 render() 写入。
|
||||
const float aspect = 1.0f;
|
||||
const float zoom = 2.0f;
|
||||
// Vertex shader
|
||||
ubo_vs.projection = glm::perspective(glm::radians(60.0f), static_cast<float>(width) / static_cast<float>(height), 0.001f, 256.0f);
|
||||
ubo_vs.projection = glm::perspective(glm::radians(60.0f), aspect, 0.001f, 256.0f);
|
||||
glm::mat4 view_matrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, zoom));
|
||||
|
||||
ubo_vs.model = view_matrix * glm::translate(glm::mat4(1.0f), camera_pos);
|
||||
@@ -981,6 +1075,26 @@ void FaceApp::update_uniform_buffers()
|
||||
memcpy(uniform_buffer_mapped, &ubo_vs, sizeof(ubo_vs));
|
||||
}
|
||||
|
||||
void FaceApp::updateCanvasMetrics()
|
||||
{
|
||||
// 把"屏幕中央 min(w,h) 的正方形"作为设计画布。画布外的部分会落在
|
||||
// shader 计算的 NDC ∈ [-1,+1] 之外,被 GPU 自动裁剪为黑色 letterbox。
|
||||
//
|
||||
// swapChainExtent 来自 Application::createSwapChain 里的
|
||||
// surface_capabilities.currentExtent,即当前窗口/Surface 的真实分辨率。
|
||||
// 任何 swapchain 重建(onWindowLost -> onWindowInit)都会刷新它,所以
|
||||
// 这里每帧重算是廉价且自洽的。
|
||||
m_screenW = (float)swapChainExtent.width;
|
||||
m_screenH = (float)swapChainExtent.height;
|
||||
m_canvasSize = (m_screenW < m_screenH) ? m_screenW : m_screenH;
|
||||
if (m_canvasSize <= 0.0f) {
|
||||
// swapchain 还没初始化 / 已销毁时给个安全值,避免 shader 除零。
|
||||
m_canvasSize = 480.0f;
|
||||
m_screenW = 480.0f;
|
||||
m_screenH = 480.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void FaceApp::createVertexBuffer()
|
||||
{
|
||||
VkDeviceSize vertexBufferSize = sizeof(TextureLoadingVertexStructure) * obj_vertices.size();
|
||||
@@ -1029,17 +1143,85 @@ void FaceApp::uploadVertexData() {
|
||||
|
||||
|
||||
|
||||
void FaceApp::processCameraFrame(uint8_t* data, int width, int height,
|
||||
int rowStride, size_t dataSize, int rotation,
|
||||
bool mirrorX)
|
||||
{
|
||||
if (!isInited() || data == nullptr || width <= 0 || height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1) rotation 收敛到 0/90/180/270。CameraX 文档保证只会给这四个值,
|
||||
// 但理论上 ((rotation % 360) + 360) % 360 更鲁棒。
|
||||
int rotNorm = ((rotation % 360) + 360) % 360;
|
||||
if (rotNorm != 0 && rotNorm != 90 && rotNorm != 180 && rotNorm != 270) {
|
||||
// 非法值时保守用 90(与原 hardcode 一致)。
|
||||
rotNorm = 90;
|
||||
}
|
||||
|
||||
// 2) 元信息更新(无锁单写者:仅相机分析线程写,render 线程读)。
|
||||
// 放在 sizeChanged 判断前后都安全:render() 每帧重新拷到 push constant。
|
||||
m_cameraAspect = (float)width / (float)height;
|
||||
m_cameraRotation = (float)rotNorm;
|
||||
m_mirrorX = mirrorX ? 1.0f : 0.0f;
|
||||
|
||||
// 3) 检查帧尺寸是否与现有 tex_bg 一致。
|
||||
// 场景:CameraX session 重建(如 Activity onResume)后第一帧分辨率可能
|
||||
// 不同;或者 setTargetResolution 在不同设备上落地为不同尺寸。
|
||||
bool sizeChanged = (tex_bg.image != VK_NULL_HANDLE)
|
||||
&& (tex_bg.width != width || tex_bg.height != height);
|
||||
|
||||
if (!sizeChanged) {
|
||||
// 快路径(每帧):直接走 base 标准上传路径。第一次 image == VK_NULL_HANDLE
|
||||
// base 会自动 createTexture(width, height, ...) 自适应分辨率;之后每帧
|
||||
// image 已存在,直接走 staging buffer 拷贝。
|
||||
processWithVulkan(data, width, height, rowStride, dataSize,
|
||||
tex_bg, false, commandPool, "data_from_camera");
|
||||
return;
|
||||
}
|
||||
|
||||
// 慢路径(罕见,整个 session 通常 1 次):尺寸变化 → 重建 image + 刷新
|
||||
// descriptor set。整个 ceremony 必须严格串行:
|
||||
// ① createTextureMtx 与 drawFrame / 其它 processWithVulkan 互斥
|
||||
// (FaceApp::drawFrame 进 Application::drawFrame 之前会持此锁,所以
|
||||
// 我们持锁期间渲染线程被阻塞,不会有新的 cmdbuf 绑定 m_descriptor_set_bg
|
||||
// 并 submit)。
|
||||
// ② vkDeviceWaitIdle 等待所有"已 submit 但未完成"的 cmdbuf 跑完,确保
|
||||
// 销毁旧 image/view 时 GPU 已经不再采样它。
|
||||
// ③ destroy → create → updateTexture → refresh_descriptor_set_bg
|
||||
// 全部在锁内完成,期间 m_descriptor_set_bg 处于"指向无效 view"的中间
|
||||
// 状态,但因为渲染线程被锁阻塞,这个中间状态对 GPU 不可见。
|
||||
// 注意不能调 base::processWithVulkan,那会再次 lock createTextureMtx 死锁;
|
||||
// 直接调 createTexture + updateTexture,这两个 base 方法不持 createTextureMtx。
|
||||
#ifndef _WIN32
|
||||
DebugLog::log("processCameraFrame: bg size changed %dx%d -> %dx%d, recreate texture",
|
||||
tex_bg.width, tex_bg.height, width, height);
|
||||
#endif
|
||||
std::unique_lock<std::mutex> lk_tex(createTextureMtx);
|
||||
{
|
||||
std::lock_guard<std::mutex> lk_pool(poolQueueMtx);
|
||||
vkDeviceWaitIdle(device);
|
||||
}
|
||||
destroyTexture(device, tex_bg);
|
||||
createTexture(device, physicalDevice, width, height, tex_bg, false,
|
||||
"data_from_camera", dataSize);
|
||||
updateTexture(device, physicalDevice, commandPool, graphicsQueue,
|
||||
data, width, height, rowStride, dataSize, tex_bg);
|
||||
refresh_descriptor_set_bg();
|
||||
}
|
||||
|
||||
void FaceApp::update_face_vertex_buffer(float* pos, int pointCount)
|
||||
{
|
||||
if(!isInited())
|
||||
{
|
||||
FACE_DBG_LOG_THROTTLED("update_face_vertex_buffer.notInited",
|
||||
"update_face_vertex_buffer dropped: !isInited()");
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mtx_point);
|
||||
last_update_time = getCurrentTimeMillis();
|
||||
|
||||
|
||||
for (int i = 0; i < obj_vertices.size(); ++i)
|
||||
{
|
||||
|
||||
@@ -1251,8 +1433,12 @@ void FaceApp::setup_descriptor_set_layout_bg()
|
||||
pipeline_layout_create_info.pSetLayouts = &m_descriptorSetLayout_bg;
|
||||
|
||||
|
||||
// bg.frag 同样要读 push constant(r/g/b/radius,以及分辨率适配重构后的
|
||||
// screen_w/screen_h),所以 stage 必须包含 FRAGMENT_BIT。老代码这里只写了
|
||||
// VERTEX_BIT,按 Vulkan spec 是非法的(验证层会 warning,部分驱动会读到
|
||||
// 未定义内容),借这次重构修掉。
|
||||
VkPushConstantRange pushConstantRanges[1];
|
||||
pushConstantRanges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
pushConstantRanges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
pushConstantRanges[0].offset = 0;
|
||||
pushConstantRanges[0].size = sizeof(PushConstants);
|
||||
|
||||
@@ -1273,7 +1459,18 @@ void FaceApp::setup_descriptor_set_bg()
|
||||
|
||||
VK_CHECK(vkAllocateDescriptorSets(device, &alloc_info, &m_descriptor_set_bg));
|
||||
|
||||
refresh_descriptor_set_bg();
|
||||
}
|
||||
|
||||
void FaceApp::refresh_descriptor_set_bg()
|
||||
{
|
||||
if (m_descriptor_set_bg == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
if (tex_bg.view == VK_NULL_HANDLE || tex_bg.sampler == VK_NULL_HANDLE) {
|
||||
// 还没 loadTexture / processCameraFrame 过,没法绑定。
|
||||
return;
|
||||
}
|
||||
|
||||
VkDescriptorImageInfo image_descriptor;
|
||||
image_descriptor.imageView = tex_bg.view;
|
||||
@@ -1288,9 +1485,7 @@ void FaceApp::setup_descriptor_set_bg()
|
||||
write_descriptor_set.pImageInfo = &image_descriptor;
|
||||
write_descriptor_set.descriptorCount = 1;
|
||||
|
||||
std::vector<VkWriteDescriptorSet> write_descriptor_sets = { write_descriptor_set };
|
||||
|
||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL);
|
||||
vkUpdateDescriptorSets(device, 1, &write_descriptor_set, 0, NULL);
|
||||
}
|
||||
|
||||
void FaceApp::destroyTexture(VkDevice device, Texture& texture) {
|
||||
@@ -1604,6 +1799,7 @@ void FaceApp::loadMotionThread()
|
||||
string name = m.name;
|
||||
if (motion_list_map.find(name) != motion_list_map.end())
|
||||
{
|
||||
FACE_DBG_LOG("loadMotionThread: skip already-loaded motion='%s'", name.c_str());
|
||||
continue;
|
||||
}
|
||||
m_texs_left.push_back(Texture());
|
||||
@@ -1620,6 +1816,10 @@ void FaceApp::loadMotionThread()
|
||||
#endif // _WIN32
|
||||
motion_list_map[name] = m_texs_left.size() - 1;
|
||||
_loadMotionMap[name] = m;
|
||||
FACE_DBG_LOG("loadMotionThread: loaded motion='%s' idx=%zu tex_image=%p tex_view=%p tex_sampler=%p frames=%zu",
|
||||
name.c_str(), m_texs_left.size() - 1,
|
||||
(void*)newTex.image, (void*)newTex.view, (void*)newTex.sampler,
|
||||
m.frames.size());
|
||||
}
|
||||
|
||||
// for (int i = 0; i < _loadMotions.size(); ++i)
|
||||
@@ -1680,7 +1880,11 @@ void FaceApp::changeMotionList(vector<string> motions, AnimationFinishedCallback
|
||||
vector<Motion> motion_list;
|
||||
for (auto ms : motions) {
|
||||
Motion m = getMotionByName(ms);
|
||||
motion_list.push_back(m);
|
||||
auto it = motion_list_map.find(ms);
|
||||
int idx = (it == motion_list_map.end()) ? -1 : (int)it->second;
|
||||
FACE_DBG_LOG("changeMotionList: requested='%s' idx=%d frames=%zu (-1=NOT FOUND)",
|
||||
ms.c_str(), idx, m.frames.size());
|
||||
motion_list.push_back(m);
|
||||
}
|
||||
|
||||
_curMotions = motion_list;
|
||||
|
||||
Reference in New Issue
Block a user