Files
face_sdk/vulkan/FaceApp.cpp
T
2026-04-26 00:12:56 +08:00

1926 lines
67 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "FaceApp.h"
#include "hardcode_data.h"
#include "lodepng.h"
#include <sstream>
#include <queue>
#include <vector>
#include <mutex>
#ifndef _WIN32
#include "../app/src/main/cpp/DebugLog.h"
#define FACE_DBG_LOG(...) DebugLog::log(__VA_ARGS__)
#define FACE_DBG_LOG_THROTTLED(key, ...) DebugLog::log_throttled(key, __VA_ARGS__)
#else
#define FACE_DBG_LOG(...) ((void)0)
#define FACE_DBG_LOG_THROTTLED(key, ...) ((void)0)
#endif
FaceApp* FaceApp::faceIns = nullptr;
FaceApp::FaceApp(/* args */)
{
faceIns = this;
}
FaceApp::~FaceApp()
{
if(worker_.joinable())
{
worker_.join();
}
}
void FaceApp::Stop()
{
FACE_DBG_LOG("FaceApp::Stop called (_running=%d worker_joinable=%d)",
(int)_running, (int)worker_.joinable());
_running = false;
if(worker_.joinable())
{
worker_.join();
}
FACE_DBG_LOG("FaceApp::Stop done");
}
void ReceiveFacePoint(float* pos, int pointCount, int width, int height)
{
FaceApp* self = FaceApp::Get();
if (self == nullptr)
{
FACE_DBG_LOG_THROTTLED("ReceiveFacePoint.noFaceApp",
"ReceiveFacePoint dropped: FaceApp::Get() == nullptr");
return;
}
self->update_face_vertex_buffer(pos, pointCount);
}
// 定义帧数据结构
struct FrameData {
uint8_t* data;
int width;
int height;
int rowStride;
size_t dataSize;
int rotation; // CameraX 给的 rotationDegrees,用于 shader 端 UV 旋转
bool mirrorX; // 前置摄像头时为 trueshader 在 gl_Position.x 翻转一次
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);
}
}
~FrameData() {
if (data) {
delete[] data;
data = nullptr;
}
}
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),
rotation(other.rotation), mirrorX(other.mirrorX) {
other.data = nullptr;
}
FrameData& operator=(FrameData&& other) noexcept {
if (this != &other) {
if (data) delete[] data;
data = other.data;
width = other.width;
height = other.height;
rowStride = other.rowStride;
dataSize = other.dataSize;
rotation = other.rotation;
mirrorX = other.mirrorX;
other.data = nullptr;
}
return *this;
}
};
// 全局队列和互斥锁
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, int rotation, bool mirrorX)
{
if (!FaceApp::Get()->isInited()) {
return;
}
std::lock_guard<std::mutex> lock(queueMutex);
frameQueue.emplace(data, width, height, rowStride, dataSize, rotation, mirrorX);
// 如果队列大小达到阈值,开始处理最旧的一帧
if (frameQueue.size() >= MAX_QUEUE_SIZE) {
FrameData& oldestFrame = frameQueue.front();
// 走 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,
oldestFrame.rotation,
oldestFrame.mirrorX
);
frameQueue.pop();
}
}
bool FaceApp::LoadOBJ(const std::string& filename,
std::vector<TextureLoadingVertexStructure>& vertices,
std::vector<uint32_t>& indices) {
// 临时存储从OBJ文件读取的原始数据
std::vector<float> temp_positions;
std::vector<float> temp_texcoords;
std::vector<float> temp_normals;
// 用于处理顶点索引
std::vector<int> vertexIndices, uvIndices, normalIndices;
std::vector<char> data = readFile(filename);
// 将 vector<char> 转换为以 null 结尾的字符串(安全做法)
std::string content(data.begin(), data.end());
std::istringstream iss(content); // 用字符串创建字符串流
std::string line;
while (std::getline(iss, line)) {
// 跳过空行和注释行
if (line.empty() || line[0] == '#') {
continue;
}
std::istringstream iss(line);
std::string type;
iss >> type;
if (type == "v") { // 顶点位置
float x, y, z;
iss >> x >> y >> z;
temp_positions.push_back(x);
temp_positions.push_back(y);
temp_positions.push_back(z);
}
else if (type == "vt") { // 纹理坐标
float u, v;
iss >> u >> v;
temp_texcoords.push_back(u);
temp_texcoords.push_back(1 - v);
}
else if (type == "vn") { // 法线
float nx, ny, nz;
iss >> nx >> ny >> nz;
temp_normals.push_back(nx);
temp_normals.push_back(ny);
temp_normals.push_back(nz);
}
else if (type == "f") { // 面(三角形)
std::string vertex1, vertex2, vertex3;
iss >> vertex1 >> vertex2 >> vertex3;
// 处理每个顶点的索引
for (const std::string& vertex : { vertex1, vertex2, vertex3 }) {
std::istringstream viss(vertex);
std::string v, vt, vn;
// 解析顶点索引格式:v/vt/vn 或 v//vn 或 v
std::getline(viss, v, '/');
std::getline(viss, vt, '/');
std::getline(viss, vn, '/');
int posIndex = std::stoi(v) - 1; // OBJ索引从1开始
int texIndex = -1, normIndex = -1;
if (!vt.empty()) texIndex = std::stoi(vt) - 1;
if (!vn.empty()) normIndex = std::stoi(vn) - 1;
vertexIndices.push_back(posIndex);
uvIndices.push_back(texIndex);
normalIndices.push_back(normIndex);
}
}
}
// 创建顶点数据
vertices.clear();
indices.clear();
// 用于去重的哈希映射
std::map<std::string, uint32_t> vertexMap;
for (size_t i = 0; i < vertexIndices.size(); i++) {
int posIndex = vertexIndices[i];
int texIndex = uvIndices[i];
int normIndex = normalIndices[i];
// 创建唯一标识符
std::string vertexKey = std::to_string(posIndex) + "/" +
std::to_string(texIndex) + "/" +
std::to_string(normIndex);
// 检查是否已经存在相同的顶点
if (vertexMap.find(vertexKey) != vertexMap.end()) {
// 使用现有顶点的索引
indices.push_back(vertexMap[vertexKey]);
}
else {
// 创建新顶点
TextureLoadingVertexStructure vertex;
// 设置位置
if (posIndex >= 0 && posIndex * 3 + 2 < temp_positions.size()) {
vertex.pos[0] = temp_positions[posIndex * 3];
vertex.pos[1] = temp_positions[posIndex * 3 + 1];
vertex.pos[2] = temp_positions[posIndex * 3 + 2];
}
else {
vertex.pos[0] = vertex.pos[1] = vertex.pos[2] = 0.0f;
}
// 设置纹理坐标
if (texIndex >= 0 && texIndex * 2 + 1 < temp_texcoords.size()) {
vertex.uv[0] = temp_texcoords[texIndex * 2];
vertex.uv[1] = temp_texcoords[texIndex * 2 + 1];
}
else {
vertex.uv[0] = vertex.uv[1] = 0.0f;
}
// 设置法线
if (normIndex >= 0 && normIndex * 3 + 2 < temp_normals.size()) {
vertex.normal[0] = temp_normals[normIndex * 3];
vertex.normal[1] = temp_normals[normIndex * 3 + 1];
vertex.normal[2] = temp_normals[normIndex * 3 + 2];
}
else {
vertex.normal[0] = vertex.normal[1] = 0.0f;
vertex.normal[2] = 1.0f; // 默认法线
}
// 添加新顶点并记录索引
uint32_t newIndex = static_cast<uint32_t>(vertices.size());
vertices.push_back(vertex);
indices.push_back(newIndex);
obj_vertices_map[newIndex] = posIndex;
vertexMap[vertexKey] = newIndex;
}
}
return true;
}
void FaceApp::create_face_pipelines()
{
VkPipelineInputAssemblyStateCreateInfo input_assembly_state{};
input_assembly_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly_state.flags = 0;
input_assembly_state.primitiveRestartEnable = VK_FALSE;
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;
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT;
// 常用的Alpha混合公式
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
// face 是 2D 平面网格(texture.vert 让顶点 z=0.5 一致),完全不需要 depth
// test/write。bg 管线本来也是关 depth 的;保持两边一致,让"先 bg 再 face"
// 的覆盖顺序由 draw 顺序而非 depth 决定,避免 reversed-depthGREATER+
// 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_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;
VkPipelineViewportStateCreateInfo viewport_state{};
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state.viewportCount = 1;
viewport_state.scissorCount = 1;
viewport_state.flags = 0;
VkPipelineMultisampleStateCreateInfo multisample_state{};
multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisample_state.flags = 0;
// 动态定义视口和剪裁,暂时用不到
//std::vector<VkDynamicState> dynamic_state_enables = {
// VK_DYNAMIC_STATE_VIEWPORT,
// VK_DYNAMIC_STATE_SCISSOR };
//VkPipelineDynamicStateCreateInfo dynamic_state{};
//dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
//dynamic_state.pDynamicStates = dynamic_state_enables.data();
//dynamic_state.dynamicStateCount = static_cast<uint32_t>(dynamic_state_enables.size());
//dynamic_state.flags = 0;
// 视口状态
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)swapChainExtent.width;
viewport.height = (float)swapChainExtent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = { 0, 0 };
scissor.extent = swapChainExtent;
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
std::vector<char> vertShaderCode;
std::vector<char> fragShaderCode;
if (kThick)
{
vertShaderCode = readFile("shaders/texture_thick.vert.spv");
fragShaderCode = readFile("shaders/texture_thick.frag.spv");
}
else
{
vertShaderCode = readFile("shaders/texture.vert.spv");
fragShaderCode = readFile("shaders/texture.frag.spv");
}
VkShaderModule vertShaderModule = createShaderModule(this->device, vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule(this->device, fragShaderCode);
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo shader_stages[] = { vertShaderStageInfo, fragShaderStageInfo };
// Vertex bindings and attributes
std::vector<VkVertexInputBindingDescription> vertex_input_bindings{};;
VkVertexInputBindingDescription vertex_input_binding_description{};
vertex_input_binding_description.binding = 0;
vertex_input_binding_description.stride = sizeof(TextureLoadingVertexStructure);
vertex_input_binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
vertex_input_bindings.push_back(vertex_input_binding_description);
std::vector<VkVertexInputAttributeDescription> vertex_input_attributes{};
VkVertexInputAttributeDescription viaPos{};
viaPos.location = 0;
viaPos.binding = 0;
viaPos.format = VK_FORMAT_R32G32B32_SFLOAT;
viaPos.offset = offsetof(TextureLoadingVertexStructure, pos);
vertex_input_attributes.push_back(viaPos);
VkVertexInputAttributeDescription viaUv{};
viaUv.location = 1;
viaUv.binding = 0;
viaUv.format = VK_FORMAT_R32G32_SFLOAT;
viaUv.offset = offsetof(TextureLoadingVertexStructure, uv);
vertex_input_attributes.push_back(viaUv);
VkVertexInputAttributeDescription viaNormal{};
viaNormal.location = 2;
viaNormal.binding = 0;
viaNormal.format = VK_FORMAT_R32G32B32_SFLOAT;
viaNormal.offset = offsetof(TextureLoadingVertexStructure, normal);
vertex_input_attributes.push_back(viaNormal);
VkPipelineVertexInputStateCreateInfo vertex_input_state{};
vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_state.vertexBindingDescriptionCount = static_cast<uint32_t>(vertex_input_bindings.size());
vertex_input_state.pVertexBindingDescriptions = vertex_input_bindings.data();
vertex_input_state.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertex_input_attributes.size());
vertex_input_state.pVertexAttributeDescriptions = vertex_input_attributes.data();
VkGraphicsPipelineCreateInfo pipeline_create_info{};
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_create_info.layout = m_pipelineLayout;
pipeline_create_info.renderPass = renderPass;
pipeline_create_info.flags = 0;
pipeline_create_info.basePipelineIndex = -1;
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_create_info.pVertexInputState = &vertex_input_state;
pipeline_create_info.pInputAssemblyState = &input_assembly_state;
pipeline_create_info.pRasterizationState = &rasterization_state;
pipeline_create_info.pColorBlendState = &colorBlending; //&color_blend_state;
pipeline_create_info.pMultisampleState = &multisample_state;
pipeline_create_info.pViewportState = &viewport_state;
pipeline_create_info.pDepthStencilState = &depth_stencil_state;
//pipeline_create_info.pDynamicState = &dynamic_state;
pipeline_create_info.pViewportState = &viewportState;
pipeline_create_info.stageCount = 2;
pipeline_create_info.pStages = shader_stages;
VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_create_info, nullptr, &m_graphicsPipeline));
// 销毁着色器模块
vkDestroyShaderModule(device, fragShaderModule, nullptr);
vkDestroyShaderModule(device, vertShaderModule, nullptr);
}
void FaceApp::setup_descriptor_set_layout()
{
VkDescriptorSetLayoutBinding set_layout_binding_vertex{};
set_layout_binding_vertex.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
set_layout_binding_vertex.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
set_layout_binding_vertex.binding = 0;
set_layout_binding_vertex.descriptorCount = 1;
VkDescriptorSetLayoutBinding set_layout_binding_fragment{};
set_layout_binding_fragment.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
set_layout_binding_fragment.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
set_layout_binding_fragment.binding = 1;
set_layout_binding_fragment.descriptorCount = 1;
if (kThick)
{
// 添加第二个纹理绑定点
VkDescriptorSetLayoutBinding set_layout_binding_fragment1{};
set_layout_binding_fragment1.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
set_layout_binding_fragment1.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
set_layout_binding_fragment1.binding = 2;
set_layout_binding_fragment1.descriptorCount = 1;
std::vector<VkDescriptorSetLayoutBinding> set_layout_bindings{ set_layout_binding_vertex , set_layout_binding_fragment, set_layout_binding_fragment1 };
VkDescriptorSetLayoutCreateInfo descriptor_layout{};
descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptor_layout.pBindings = set_layout_bindings.data();
descriptor_layout.bindingCount = static_cast<uint32_t>(set_layout_bindings.size());
VK_CHECK(vkCreateDescriptorSetLayout(device, &descriptor_layout, nullptr, &m_descriptorSetLayout));
}
else
{
std::vector<VkDescriptorSetLayoutBinding> set_layout_bindings{ set_layout_binding_vertex , set_layout_binding_fragment };
VkDescriptorSetLayoutCreateInfo descriptor_layout{};
descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptor_layout.pBindings = set_layout_bindings.data();
descriptor_layout.bindingCount = static_cast<uint32_t>(set_layout_bindings.size());
VK_CHECK(vkCreateDescriptorSetLayout(device, &descriptor_layout, nullptr, &m_descriptorSetLayout));
}
VkPipelineLayoutCreateInfo pipeline_layout_create_info{};
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_create_info.setLayoutCount = 1;
pipeline_layout_create_info.pSetLayouts = &m_descriptorSetLayout;
//VkPushConstantRange pushConstantRange{};
//pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
//pushConstantRange.offset = 0;
//pushConstantRange.size = sizeof(float);
//pipeline_layout_create_info.pushConstantRangeCount = 1;
//pipeline_layout_create_info.pPushConstantRanges = &pushConstantRange;
VkPushConstantRange pushConstantRanges[1];
pushConstantRanges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT| VK_SHADER_STAGE_FRAGMENT_BIT;
pushConstantRanges[0].offset = 0;
pushConstantRanges[0].size = sizeof(PushConstants);
//pushConstantRanges[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
//pushConstantRanges[1].offset = sizeof(float);
//pushConstantRanges[1].size = 2*sizeof(float);
pipeline_layout_create_info.pushConstantRangeCount = 1;
pipeline_layout_create_info.pPushConstantRanges = &pushConstantRanges[0];
VK_CHECK(vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &m_pipelineLayout));
}
void FaceApp::setup_descriptor_pool()
{
std::vector<VkDescriptorPoolSize> pool_sizes = {
{
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = (kTextureMax *2)
},
{
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = ((kTextureMax + 1)*2)
},
// 如果需要其他类型的描述符,在这里添加
};
VkDescriptorPoolCreateInfo descriptor_pool_info{};
descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptor_pool_info.poolSizeCount = static_cast<uint32_t>(pool_sizes.size());
descriptor_pool_info.pPoolSizes = pool_sizes.data();
descriptor_pool_info.maxSets = kTextureMax + (kTextureMax + 1 + kTextureMax);
descriptor_pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
VK_CHECK(vkCreateDescriptorPool(device, &descriptor_pool_info, nullptr, &descriptor_pool));
}
void FaceApp::setup_descriptor_set()
{
m_descriptor_sets_left.resize(kTextureMax);
std::vector<VkDescriptorSetLayout> layouts(kTextureMax, m_descriptorSetLayout);
VkDescriptorSetAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = descriptor_pool;
alloc_info.pSetLayouts = layouts.data();
alloc_info.descriptorSetCount = m_descriptor_sets_left.size();
VK_CHECK(vkAllocateDescriptorSets(device, &alloc_info, m_descriptor_sets_left.data()));
update_descriptor_set(m_texs_left, m_descriptor_sets_left);
//m_descriptor_sets_right.resize(kTextureMax);
//VK_CHECK(vkAllocateDescriptorSets(device, &alloc_info, m_descriptor_sets_right.data()));
//update_descriptor_set(m_texs_left, m_descriptor_sets_right);
}
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)
{
break;
}
VkDescriptorBufferInfo buffer_descriptor{};
buffer_descriptor.buffer = uniform_buffer_vs;
buffer_descriptor.range = VK_WHOLE_SIZE;
buffer_descriptor.offset = 0;
VkDescriptorImageInfo image_descriptor;
image_descriptor.imageView = texs[i].view;
image_descriptor.sampler = texs[i].sampler;
image_descriptor.imageLayout = texs[i].image_layout;
VkWriteDescriptorSet write_descriptor_set_uniform{};
write_descriptor_set_uniform.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set_uniform.dstSet = descriptSet[i];
write_descriptor_set_uniform.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
write_descriptor_set_uniform.dstBinding = 0;
write_descriptor_set_uniform.pBufferInfo = &buffer_descriptor;
write_descriptor_set_uniform.descriptorCount = 1;
VkWriteDescriptorSet write_descriptor_set_image{};
write_descriptor_set_image.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set_image.dstSet = descriptSet[i];
write_descriptor_set_image.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set_image.dstBinding = 1;
write_descriptor_set_image.pImageInfo = &image_descriptor;
write_descriptor_set_image.descriptorCount = 1;
if (kThick)
{
// 第二个纹理描述符 - 假设你的第二个纹理变量名为 tex_demo1
VkDescriptorImageInfo image_descriptor1;
image_descriptor1.imageView = m_texs_thick[i].view;
image_descriptor1.sampler = m_texs_thick[i].sampler;
image_descriptor1.imageLayout = m_texs_thick[i].image_layout;
// 添加第二个纹理的写入描述符
VkWriteDescriptorSet write_descriptor_set_image1{};
write_descriptor_set_image1.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set_image1.dstSet = descriptSet[i];
write_descriptor_set_image1.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set_image1.dstBinding = 2; // 绑定到位置2
write_descriptor_set_image1.pImageInfo = &image_descriptor1;
write_descriptor_set_image1.descriptorCount = 1;
std::vector<VkWriteDescriptorSet> write_descriptor_sets =
{
write_descriptor_set_uniform,
write_descriptor_set_image,
write_descriptor_set_image1, // 添加第二个纹理
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL);
}
else
{
std::vector<VkWriteDescriptorSet> write_descriptor_sets =
{
write_descriptor_set_uniform,
write_descriptor_set_image,
};
vkUpdateDescriptorSets(device, static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL);
}
}
}
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 constantshader 用它们:
// 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);
// bg.frag 也读 push constantr/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);
//}
//else
//{
// vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &m_descriptor_sets_right[_curMotionIndex], 0, NULL);
//}
//float fsValues[3] = { myFloatValue, 0 , 0};
if (_curMotionIndex < _curMotions.size())
{
if (_curFrameIndex < _curMotions[_curMotionIndex].frames.size())
{
float ux = _curMotions[_curMotionIndex].frames[_curFrameIndex].x;
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(pc), &pc);
//vkCmdPushConstants(commandBuffer, m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(fsValues), fsValues);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsPipeline);
VkDeviceSize offsets[1] = { 0 };
VkBuffer vertexBuffers[] = { m_vertexBuffer };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(commandBuffer, m_indexBuffer, 0, VK_INDEX_TYPE_UINT32);
#ifdef _WIN32
vkCmdDrawIndexed(commandBuffer, obj_indices.size(), 1, 0, 0, 0);
#else
if (getCurrentTimeMillis() - last_update_time < 2000)
{
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
}
void FaceApp::createVmaAllocator()
{
//VmaAllocatorCreateInfo allocatorInfo = {};
//allocatorInfo.physicalDevice = physicalDevice;
//allocatorInfo.device = device;
//allocatorInfo.instance = instance;
//allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_0;
//vmaCreateAllocator(&allocatorInfo, &allocator);
// 1. 设置 Vulkan 函数指针
VmaVulkanFunctions vulkanFunctions{};
vulkanFunctions.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
vulkanFunctions.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
// 2. 配置 Allocator
VmaAllocatorCreateInfo allocatorInfo{};
allocatorInfo.physicalDevice = physicalDevice;
allocatorInfo.device = device;
allocatorInfo.instance = instance;
allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_0;
allocatorInfo.pVulkanFunctions = &vulkanFunctions;
VkResult result = vmaCreateAllocator(&allocatorInfo, &allocator);
}
void FaceApp::initVulkan()
{
static int s_initVulkanCallCount = 0;
++s_initVulkanCallCount;
FACE_DBG_LOG("FaceApp::initVulkan enter, call#%d _applicationInited=%d _faceAppInited=%d _secondfaceAppInited=%d",
s_initVulkanCallCount, (int)_applicationInited, (int)_faceAppInited, (int)_secondfaceAppInited);
Application::initVulkan();
if (!_faceAppInited)
{
createVmaAllocator();
LoadOBJ("face_picture_3dmax.obj", obj_vertices, obj_indices);
//m_texs_left.resize(kTextureMax);
//m_texs_right.resize(kTextureMax);
if (kThick)
{
m_texs_thick.resize(kTextureMax);
}
std::vector<unsigned char> data = readFileUnsignedChar("dummy.png", false);
unsigned error = lodepng::decode(dummy_data, dummy_w, dummy_h, data, LCT_RGBA, 8);
for (int i = 0; i < kTextureInit; ++i)
{
//string id_str = std::to_string(i);
//loadTexture(dummy_data, dummy_data.size(), dummy_w, dummy_h, m_texs_left[i], true, commandPool, "dummy.png_left_" + id_str);
//loadTexture(dummy_data, dummy_data.size(), dummy_w, dummy_h, m_texs_right[i], true, commandPool, "dummy.png_right_" + id_str);
if (kThick)
{
//loadTexture(dummy_data, dummy_data.size(), dummy_w, dummy_h, m_texs_thick[i], true, commandPool, "dummy.png_thick_" + id_str);
}
}
loadTexture("out.png", tex_bg, true, commandPool);
createVertexBuffer();
createUniformBuffer();
setup_descriptor_pool();
setup_descriptor_set_layout();
setup_descriptor_set();
create_face_pipelines();
setup_descriptor_set_layout_bg();
setup_descriptor_set_bg();
create_pipelines_bg();
uploadVertexData();
last_update_time = getCurrentTimeMillis();
//changeMotion(_initArg.motion);
Start();
_faceAppInited = true;
#if _WIN32
std::vector<float> floatArray;
std::string& str = HardCodeData::Get().face_result_point_str;
std::stringstream ss(str);
std::string token;
while (std::getline(ss, token, ',')) {
floatArray.push_back(std::stof(token));
}
//ReceiveFacePoint(floatArray.data(), floatArray.size() / 3, 480, 480);
#endif
}
if (!_secondfaceAppInited)
{
Start();
_playMotion = true;
_secondfaceAppInited = true;
}
FACE_DBG_LOG("FaceApp::initVulkan exit, call#%d _applicationInited=%d _faceAppInited=%d _secondfaceAppInited=%d",
s_initVulkanCallCount, (int)_applicationInited, (int)_faceAppInited, (int)_secondfaceAppInited);
}
void FaceApp::clearnSecondFaceApp()
{
FACE_DBG_LOG("FaceApp::clearnSecondFaceApp: _secondfaceAppInited %d -> 0",
(int)_secondfaceAppInited);
_secondfaceAppInited = false;
}
void FaceApp::Start()
{
FACE_DBG_LOG("FaceApp::Start: _running %d -> 1", (int)_running);
_running = true;
}
void FaceApp::onWindowLost()
{
FACE_DBG_LOG("FaceApp::onWindowLost enter _applicationInited=%d _faceAppInited=%d _sceondInited=%d _secondfaceAppInited=%d _running=%d",
(int)_applicationInited, (int)_faceAppInited, (int)_sceondInited,
(int)_secondfaceAppInited, (int)_running);
// Stop the render loop from touching Vulkan while we tear down.
_running = false;
// Serialize against JNI callbacks that may concurrently submit GPU work
// via commandPool / commandPool_ex (processImageNative -> update texture,
// passDataToNative -> update vertex buffer, changeMotionList).
std::unique_lock<std::mutex> lk_point(mtx_point);
std::unique_lock<std::mutex> lk_motion(changeMotionMtx);
std::unique_lock<std::mutex> lk_tex(createTextureMtx);
// 同时阻塞所有并发的 GPU 提交(drawFrame / copyBuffer / updateTexture)。
// cleanupForWindowLost() 会执行 vkDeviceWaitIdle 并销毁 swapchain / surface /
// semaphores / fences 等窗口相关资源,如果此时有其它线程正在 vkQueueSubmit
// 或 vkQueuePresentKHR,会触发 FORTIFY: pthread_mutex_lock called on a
// destroyed mutex。这里持锁确保销毁与提交是互斥的。
std::unique_lock<std::mutex> lk_pool(poolQueueMtx);
Application::cleanupForWindowLost();
_secondfaceAppInited = false;
FACE_DBG_LOG("FaceApp::onWindowLost done");
}
void FaceApp::onWindowInit()
{
FACE_DBG_LOG("FaceApp::onWindowInit enter _applicationInited=%d _faceAppInited=%d _sceondInited=%d _secondfaceAppInited=%d",
(int)_applicationInited, (int)_faceAppInited, (int)_sceondInited,
(int)_secondfaceAppInited);
if (!_applicationInited) {
// First-time path: go through the full initVulkan pipeline.
initVulkan();
} else {
// Recovery path after an earlier onWindowLost. Rebuild only the
// window-dependent Vulkan objects; keep renderPass / pipelines /
// FaceApp GPU resources intact unless the new swapchain is
// incompatible (e.g. rotation changed extent).
std::unique_lock<std::mutex> lk_point(mtx_point);
std::unique_lock<std::mutex> lk_motion(changeMotionMtx);
std::unique_lock<std::mutex> lk_tex(createTextureMtx);
bool swapchainIncompatible = Application::reinitForNewWindow();
if (swapchainIncompatible && _faceAppInited) {
FACE_DBG_LOG("FaceApp::onWindowInit: swapchain incompatible, rebuilding FaceApp pipelines");
recreatePipelinesForSwapchain();
}
if (!_secondfaceAppInited) {
_secondfaceAppInited = true;
_playMotion = true;
_running = true;
}
}
FACE_DBG_LOG("FaceApp::onWindowInit done _applicationInited=%d _faceAppInited=%d _sceondInited=%d _secondfaceAppInited=%d _running=%d",
(int)_applicationInited, (int)_faceAppInited, (int)_sceondInited,
(int)_secondfaceAppInited, (int)_running);
}
void FaceApp::recreatePipelinesForSwapchain()
{
// Destroy the two FaceApp pipelines. Layouts / descriptor set layouts are
// swapchain-independent and kept as-is so existing descriptor sets keep
// pointing at the same texture/uniform resources.
if (m_graphicsPipeline != VK_NULL_HANDLE) {
vkDestroyPipeline(device, m_graphicsPipeline, nullptr);
m_graphicsPipeline = VK_NULL_HANDLE;
}
if (m_graphicsPipeline_bg != VK_NULL_HANDLE) {
vkDestroyPipeline(device, m_graphicsPipeline_bg, nullptr);
m_graphicsPipeline_bg = VK_NULL_HANDLE;
}
// Recreate against the fresh renderPass (if format changed) and the new
// swapChainExtent (viewport/scissor are baked statically into these).
create_face_pipelines();
create_pipelines_bg();
FACE_DBG_LOG("FaceApp::recreatePipelinesForSwapchain: rebuilt for extent=%ux%u",
swapChainExtent.width, swapChainExtent.height);
}
void FaceApp::update_uniform_buffers()
{
// 注意:当前 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), 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);
ubo_vs.model = glm::rotate(ubo_vs.model, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
ubo_vs.model = glm::rotate(ubo_vs.model, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
ubo_vs.model = glm::rotate(ubo_vs.model, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
ubo_vs.view_pos = glm::vec4(0.0f, 0.0f, -zoom, 0.0f);
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();
VkDeviceSize indexBufferSize = sizeof(uint32_t) * obj_indices.size();
VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufferInfo.size = vertexBufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &m_vertexBuffer, &m_vertexBufferAllocation, nullptr);
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &m_stagingBuffer, &m_stagingBufferAllocation, nullptr);
bufferInfo.size = indexBufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &m_indexBuffer, &m_indexBufferAllocation, nullptr);
}
void FaceApp::uploadVertexData() {
void* data;
vmaMapMemory(allocator, m_stagingBufferAllocation, &data);
memcpy(data, obj_vertices.data(), sizeof(TextureLoadingVertexStructure) * obj_vertices.size());
vmaUnmapMemory(allocator, m_stagingBufferAllocation);
copyBuffer(m_stagingBuffer, m_vertexBuffer, sizeof(TextureLoadingVertexStructure) * obj_vertices.size());
vmaMapMemory(allocator, m_stagingBufferAllocation, &data);
memcpy(data, obj_indices.data(), sizeof(uint32_t) * obj_indices.size());
vmaUnmapMemory(allocator, m_stagingBufferAllocation);
copyBuffer(m_stagingBuffer, m_indexBuffer, sizeof(uint32_t) * obj_indices.size());
}
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)
{
int face_index = obj_vertices_map[HardCodeData::Get().indexMap[i]];
float x = pos[face_index * 3 + 0];
float y = pos[face_index * 3 + 1];
float z = pos[face_index * 3 + 2];
obj_vertices[i].pos[0] = x;
obj_vertices[i].pos[1] = y;
obj_vertices[i].pos[2] = z;
}
calculateVertexNormals(obj_vertices, obj_indices);
uploadVertexData();
}
void FaceApp::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
{
// 改造说明:
// 旧实现是 vkAllocate(commandPool) + vkBegin + vkCmdCopyBuffer + vkEnd +
// vkQueueSubmit(graphicsQueue) + vkQueueWaitIdle + vkFree(commandPool)
// 每帧 update_face_vertex_buffer 会调两次 copyBuffer(顶点 + 索引),
// 长期高频 allocate/free 会把驱动 per-pool mutex 玩坏,触发
// FORTIFY: pthread_mutex_lock called on a destroyed mutex。
//
// 现在改走基类的 runTransferCommand,复用 commandPool_ex 上预分配的
// 3 个 cmdbuf + fence,并发安全由 m_xferMtx + poolQueueMtx 联合保证。
runTransferCommand([&](VkCommandBuffer commandBuffer) {
VkBufferCopy copyRegion = {};
copyRegion.size = size;
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
});
}
void FaceApp::createUniformBuffer()
{
VkDeviceSize bufferSize = sizeof(ubo_vs);
VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufferInfo.size = bufferSize;
bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
// 创建缓冲区和内存分配
vmaCreateBuffer(allocator, &bufferInfo, &allocInfo,
&uniform_buffer_vs,
&uniform_buffer_allocation,
nullptr);
// 映射内存以便直接写入
vmaMapMemory(allocator, uniform_buffer_allocation, &uniform_buffer_mapped);
update_uniform_buffers();
}
void FaceApp::create_pipelines_bg()
{
VkPipelineInputAssemblyStateCreateInfo input_assembly_state{};
input_assembly_state.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly_state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly_state.flags = 0;
input_assembly_state.primitiveRestartEnable = VK_FALSE;
VkPipelineRasterizationStateCreateInfo rasterization_state{};
rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_state.polygonMode = VK_POLYGON_MODE_FILL;
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;
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.colorWriteMask = 0xf;
VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
VkPipelineDepthStencilStateCreateInfo depth_stencil_state = {};
depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depth_stencil_state.depthTestEnable = VK_FALSE;
depth_stencil_state.depthWriteEnable = VK_FALSE;
depth_stencil_state.depthCompareOp = VK_COMPARE_OP_GREATER;
depth_stencil_state.front = depth_stencil_state.back;
depth_stencil_state.back.compareOp = VK_COMPARE_OP_ALWAYS;
VkPipelineViewportStateCreateInfo viewport_state{};
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state.viewportCount = 1;
viewport_state.scissorCount = 1;
viewport_state.flags = 0;
VkPipelineMultisampleStateCreateInfo multisample_state{};
multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisample_state.flags = 0;
// 视口状态
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)swapChainExtent.width;
viewport.height = (float)swapChainExtent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = { 0, 0 };
scissor.extent = swapChainExtent;
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
auto vertShaderCode = readFile("shaders/bg.vert.spv");
auto fragShaderCode = readFile("shaders/bg.frag.spv");
VkShaderModule vertShaderModule = createShaderModule(this->device, vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule(this->device, fragShaderCode);
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo shader_stages[] = { vertShaderStageInfo, fragShaderStageInfo };
VkPipelineVertexInputStateCreateInfo vertex_input_state{};
vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
VkGraphicsPipelineCreateInfo pipeline_create_info{};
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_create_info.layout = m_pipelineLayout_bg;
pipeline_create_info.renderPass = renderPass;
pipeline_create_info.flags = 0;
pipeline_create_info.basePipelineIndex = -1;
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_create_info.pVertexInputState = &vertex_input_state;
pipeline_create_info.pInputAssemblyState = &input_assembly_state;
pipeline_create_info.pRasterizationState = &rasterization_state;
pipeline_create_info.pColorBlendState = &colorBlending; //&color_blend_state;
pipeline_create_info.pMultisampleState = &multisample_state;
pipeline_create_info.pViewportState = &viewport_state;
pipeline_create_info.pDepthStencilState = &depth_stencil_state;
//pipeline_create_info.pDynamicState = &dynamic_state;
pipeline_create_info.pViewportState = &viewportState;
pipeline_create_info.stageCount = 2;
pipeline_create_info.pStages = shader_stages;
VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipeline_create_info, nullptr, &m_graphicsPipeline_bg));
// 销毁着色器模块
vkDestroyShaderModule(device, fragShaderModule, nullptr);
vkDestroyShaderModule(device, vertShaderModule, nullptr);
}
void FaceApp::setup_descriptor_set_layout_bg()
{
VkDescriptorSetLayoutBinding set_layout_binding{};
set_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
set_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
set_layout_binding.binding = 0;
set_layout_binding.descriptorCount = 1;
std::vector<VkDescriptorSetLayoutBinding> set_layout_bindings ={set_layout_binding };
VkDescriptorSetLayoutCreateInfo descriptor_layout{};
descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptor_layout.pBindings = set_layout_bindings.data();
descriptor_layout.bindingCount = static_cast<uint32_t>(set_layout_bindings.size());
VK_CHECK(vkCreateDescriptorSetLayout(device, &descriptor_layout, nullptr, &m_descriptorSetLayout_bg));
VkPipelineLayoutCreateInfo pipeline_layout_create_info{};
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_create_info.setLayoutCount = 1;
pipeline_layout_create_info.pSetLayouts = &m_descriptorSetLayout_bg;
// bg.frag 同样要读 push constantr/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 | VK_SHADER_STAGE_FRAGMENT_BIT;
pushConstantRanges[0].offset = 0;
pushConstantRanges[0].size = sizeof(PushConstants);
pipeline_layout_create_info.pushConstantRangeCount = 1;
pipeline_layout_create_info.pPushConstantRanges = &pushConstantRanges[0];
VK_CHECK(vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr, &m_pipelineLayout_bg));
}
void FaceApp::setup_descriptor_set_bg()
{
VkDescriptorSetAllocateInfo alloc_info{};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = descriptor_pool;
alloc_info.pSetLayouts = &m_descriptorSetLayout_bg;
alloc_info.descriptorSetCount = 1;
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;
image_descriptor.sampler = tex_bg.sampler;
image_descriptor.imageLayout = tex_bg.image_layout;
VkWriteDescriptorSet write_descriptor_set{};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstSet = m_descriptor_set_bg;
write_descriptor_set.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set.dstBinding = 0;
write_descriptor_set.pImageInfo = &image_descriptor;
write_descriptor_set.descriptorCount = 1;
vkUpdateDescriptorSets(device, 1, &write_descriptor_set, 0, NULL);
}
void FaceApp::destroyTexture(VkDevice device, Texture& texture) {
// 注意销毁顺序:先销毁依赖对象,后销毁被依赖对象
// 1. 销毁采样器
if (texture.sampler != VK_NULL_HANDLE) {
vkDestroySampler(device, texture.sampler, nullptr);
texture.sampler = VK_NULL_HANDLE;
}
// 2. 销毁图像视图
if (texture.view != VK_NULL_HANDLE) {
vkDestroyImageView(device, texture.view, nullptr);
texture.view = VK_NULL_HANDLE;
}
// 3. 销毁图像
if (texture.image != VK_NULL_HANDLE) {
vkDestroyImage(device, texture.image, nullptr);
texture.image = VK_NULL_HANDLE;
}
// 4. 释放设备内存
if (texture.device_memory != VK_NULL_HANDLE) {
vkFreeMemory(device, texture.device_memory, nullptr);
texture.device_memory = VK_NULL_HANDLE;
//std::cout << "vkFreeMemory device_memory " << texture.texture_path << std::endl;
}
if (texture.stagingBuffer != VK_NULL_HANDLE)
{
vkDestroyBuffer(device, texture.stagingBuffer, nullptr);
texture.stagingBuffer = VK_NULL_HANDLE;
}
if (texture.stagingBufferMemory != VK_NULL_HANDLE)
{
vkFreeMemory(device, texture.stagingBufferMemory, nullptr);
//std::cout << "vkFreeMemory stagingBufferMemory " << texture.texture_path << std::endl;
texture.stagingBufferMemory = VK_NULL_HANDLE;
}
}
void FaceApp::cleanupResources(VkDevice device, VmaAllocator allocator) {
// 销毁图形管线
if (m_graphicsPipeline != VK_NULL_HANDLE) {
vkDestroyPipeline(device, m_graphicsPipeline, nullptr);
m_graphicsPipeline = VK_NULL_HANDLE;
}
// 销毁管线布局
if (m_pipelineLayout != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(device, m_pipelineLayout, nullptr);
m_pipelineLayout = VK_NULL_HANDLE;
}
// 销毁描述符集布局
if (m_descriptorSetLayout != VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(device, m_descriptorSetLayout, nullptr);
m_descriptorSetLayout = VK_NULL_HANDLE;
}
// 销毁图形管线
if (m_graphicsPipeline_bg != VK_NULL_HANDLE) {
vkDestroyPipeline(device, m_graphicsPipeline_bg, nullptr);
m_graphicsPipeline_bg = VK_NULL_HANDLE;
}
// 销毁管线布局
if (m_pipelineLayout_bg != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(device, m_pipelineLayout_bg, nullptr);
m_pipelineLayout_bg = VK_NULL_HANDLE;
}
// 销毁描述符集布局
if (m_descriptorSetLayout_bg != VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(device, m_descriptorSetLayout_bg, nullptr);
m_descriptorSetLayout_bg = VK_NULL_HANDLE;
}
// 销毁顶点缓冲区
if (m_vertexBuffer != VK_NULL_HANDLE) {
vkDestroyBuffer(device, m_vertexBuffer, nullptr);
m_vertexBuffer = VK_NULL_HANDLE;
}
if (m_vertexBufferAllocation != VK_NULL_HANDLE) {
vmaFreeMemory(allocator, m_vertexBufferAllocation);
m_vertexBufferAllocation = VK_NULL_HANDLE;
}
// 销毁暂存缓冲区
if (m_stagingBuffer != VK_NULL_HANDLE) {
vkDestroyBuffer(device, m_stagingBuffer, nullptr);
m_stagingBuffer = VK_NULL_HANDLE;
}
if (m_stagingBufferAllocation != VK_NULL_HANDLE) {
vmaFreeMemory(allocator, m_stagingBufferAllocation);
m_stagingBufferAllocation = VK_NULL_HANDLE;
}
// 销毁索引缓冲区
if (m_indexBuffer != VK_NULL_HANDLE) {
vkDestroyBuffer(device, m_indexBuffer, nullptr);
m_indexBuffer = VK_NULL_HANDLE;
}
if (m_indexBufferAllocation != VK_NULL_HANDLE) {
vmaFreeMemory(allocator, m_indexBufferAllocation);
m_indexBufferAllocation = VK_NULL_HANDLE;
}
if (descriptor_pool != VK_NULL_HANDLE)
{
vkDestroyDescriptorPool(device, descriptor_pool, nullptr);
descriptor_pool = VK_NULL_HANDLE;
}
// 清空描述符集列表(不需要单独销毁,由描述符池管理)
m_descriptor_sets_left.clear();
//m_descriptor_sets_right.clear();
}
void FaceApp::cleanup()
{
FACE_DBG_LOG("FaceApp::cleanup enter");
vkDeviceWaitIdle(device);
if (uniform_buffer_mapped != nullptr)
{
vmaUnmapMemory(allocator, uniform_buffer_allocation);
uniform_buffer_mapped = nullptr;
}
if (uniform_buffer_vs != VK_NULL_HANDLE)
{
vmaDestroyBuffer(allocator, uniform_buffer_vs, uniform_buffer_allocation);
uniform_buffer_vs = VK_NULL_HANDLE;
uniform_buffer_allocation = VK_NULL_HANDLE; // 可选,但推荐重置
}
cleanupResources(device, allocator);
for (int i = 0; i < this->m_texs_left.size(); ++i)
{
destroyTexture(device, m_texs_left[i]);
}
//for (int i = 0; i < this->m_texs_right.size(); ++i)
//{
// destroyTexture(device, m_texs_right[i]);
//}
destroyTexture(device, tex_bg);
// 必须在销毁 commandPool_ex 之前释放从它分配的 transfer cmdbuf + fence。
// vkDeviceWaitIdle 已在本函数开头调过,提交不会再有 in-flight。
destroyTransferResources();
vkDestroyCommandPool(device, commandPool, nullptr);
vkDestroyCommandPool(device, commandPool_ex, nullptr);
Application::cleanup();
//if (allocator != VK_NULL_HANDLE) {
// vmaDestroyAllocator(allocator);
// allocator = VK_NULL_HANDLE;
//}
FACE_DBG_LOG("FaceApp::cleanup done");
}
void FaceApp::SetInitArg(const char* arg)
{
_initArg = json::parse(arg);
pushConstants.zoom = _initArg.zoom;
pushConstants.r = _initArg.r;
pushConstants.g = _initArg.g;
pushConstants.b = _initArg.b;
pushConstants.radius = _initArg.radius;
pushConstants.offset_x = _initArg.offset_x;
pushConstants.offset_y = _initArg.offset_y;
}
void FaceApp::drawFrame(long long frameTime)
{
if (!_running)
{
return;
}
// if (_motionState == loading_next_motion)
// {
// int i = _nextMotionIndex;
// //for (int i = 0; i < _curMotion.png_names.size(); ++i)
// {
//
//#ifdef _WIN32
// string path = "pic";
// string path_name = path + "/" + _nextMotion.name + "/" + _nextMotion.png_names[i];// +std::to_string(i) + ".png";
// loadTextureExample(UTF8ToWideString(path_name), m_texs_next[i], true);
//#else
// //string path = _curMotion.type + "/" + _curMotion.technique + "/" + _curMotion.step + "/" + _curMotion.show_type;
// string path = "pic";
// string path_name = path + "/" + _nextMotion.type + "/" + _nextMotion.png_names[i];// +std::to_string(i) + ".png";
// loadTexture(path_name, m_texs_next[i], true);
//#endif // _WIN32
//
// _nextMotionIndex++;
// if (_nextMotionIndex >= _nextMotion.png_names.size())
// {
// _motionState = load_next_motion_finished;
// _callback_loadfinish(_nextMotion.name);
// }
// }
// }
if (_curMotions.size() > 0)
{
static long long game_time = 0;
if (_playMotion)
{
game_time += frameTime;
}
long long actionTime = (1000 / _initArg.action_fps);
if (game_time > actionTime)
{
if (_curFrameIndex < _curMotions[_curMotionIndex].frames.size() - 1)
{
_curFrameIndex += 1;
}
else
{
if (_curMotionIndex < _curMotions.size() - 1)
{
_curMotionIndex++;
_curFrameIndex = 0;
}
else
{
if (_animationFinishedCallback != nullptr)
{
_animationFinishedCallback();
}
if (_animationLoop)
{
_curMotionIndex = 0;
_curFrameIndex = 0;
}
else
{
_animationFinishedCallback = nullptr;
}
}
}
game_time = game_time - actionTime;
}
}
std::unique_lock<std::mutex> lock_point(mtx_point);
std::unique_lock<std::mutex> lock_changeMotion(changeMotionMtx);
std::unique_lock<std::mutex> lock_Texture(createTextureMtx);
Application::drawFrame(frameTime);
}
//void FaceApp::changeMotion(const char* motion_type)
//{
// //Motion motion = json::parse(json);
// changeMotion(motion);
//}
string FaceApp::preLoadMotionList(string motion_list_str, Callback callback)
{
FACE_DBG_LOG("FaceApp::preLoadMotionList called str_len=%zu _isLoadMotion=%d",
motion_list_str.size(), (int)_isLoadMotion);
if (_isLoadMotion)
{
return "failue load not finished";
}
_isLoadMotion = true;
_callback_loadfinish = callback;
_curLoadMotionList.clear();
json j = json::parse(motion_list_str);
MotionList motion_list = j.get<MotionList>();
_curLoadMotionList = motion_list.motions;
if (worker_.joinable())
{
worker_.join();
}
worker_ = std::thread(&FaceApp::loadMotionThread, this);
FACE_DBG_LOG("FaceApp::preLoadMotionList worker_ started, todo_count=%zu", _curLoadMotionList.size());
return "ok";
}
void FaceApp::loadMotionThread()
{
FACE_DBG_LOG("FaceApp::loadMotionThread enter, todo_count=%zu", _curLoadMotionList.size());
while (!isInited())
{
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
//vector<Texture>* load_text = nullptr;
//if (cur_left)
//{
// load_text = &m_texs_right;
//}
//else
//{
// load_text = &m_texs_left;
//}
//vector<Texture>& pre_texs = *load_text;
for (auto m : _curLoadMotionList) {
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());
Texture& newTex = m_texs_left[m_texs_left.size() - 1];
loadTexture(dummy_data, dummy_data.size(), dummy_w, dummy_h, newTex, true, commandPool, "");
#ifdef _WIN32
string path = "pic";
string path_name = path + "/" + name + "ex.png";
loadTextureExample(UTF8ToWideString(path_name), newTex, true, commandPool_ex);
#else
string path = "pic";
string path_name = path + "/" + name + "ex.png";
loadTexture(path_name, newTex, true, commandPool_ex);
#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)
// {
// if (pre_texs[i].image == VK_NULL_HANDLE)
// {
// loadTexture(dummy_data, dummy_data.size(), dummy_w, dummy_h, pre_texs[i], true, commandPool, "");
// }
//
//
//#ifdef _WIN32
// string path = "pic";
// string path_name = path + "/" + _loadMotions[i].name + "ex.png";
// loadTextureExample(UTF8ToWideString(path_name), pre_texs[i], true, commandPool_ex);
//#else
// string path = "pic";
// string path_name = path + "/" + _loadMotions[i].name + "ex.png";
// loadTexture(path_name, pre_texs[i], true, commandPool_ex);
//#endif // _WIN32
//
// motion_list_map[_loadMotions[i].name] = i;
// }
update_descriptor_set(m_texs_left, m_descriptor_sets_left);
_isLoadMotion = false;
FACE_DBG_LOG("FaceApp::loadMotionThread done, loaded_total=%zu", motion_list_map.size());
_callback_loadfinish();
}
Motion FaceApp::getMotionByName(string name)
{
return _loadMotionMap[name];
}
void FaceApp::changeMotionList(vector<string> motions, AnimationFinishedCallback callback, bool loop)
{
FACE_DBG_LOG_THROTTLED("FaceApp.changeMotionList",
"FaceApp::changeMotionList called count=%zu loop=%d _isLoadMotion=%d",
motions.size(), (int)loop, (int)_isLoadMotion);
if (_isLoadMotion)
{
return;
}
std::unique_lock<std::mutex> lock_changeMotion(changeMotionMtx);
//for (int i = 0; i < _preLoadMotions.size(); ++i)
//{
// destroyTexture(device, m_texs[i]);
//}
//for (int i = 0; i < _preLoadMotions.size(); ++i)
//{
// m_texs[i] = m_texs_next[i];
// //m_texs_next[i].reset();
//}
vector<Motion> motion_list;
for (auto ms : motions) {
Motion m = getMotionByName(ms);
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;
//if (cur_left)
//{
// update_descriptor_set(m_texs_right, m_descriptor_sets_right);
// cur_left = false;
//}
//else
//{
// update_descriptor_set(m_texs_left, m_descriptor_sets_left);
// cur_left = true;
//}
_curFrameIndex = 0;
_curMotionIndex = 0;
_animationFinishedCallback = callback;
_animationLoop = loop;
_isChangeMostion = true;
}
void FaceApp::StopMotion() {
FACE_DBG_LOG("FaceApp::StopMotion: _playMotion %d -> 0", (int)_playMotion);
_playMotion = false;
}
void FaceApp::ResumeMotion() {
FACE_DBG_LOG("FaceApp::ResumeMotion: _playMotion %d -> 1", (int)_playMotion);
_playMotion = true;
}