【Vulkan 入门系列】创建帧缓冲、命令池、命令缓存,和获取图片(六)

这一节主要介绍创建帧缓冲(Framebuffer),创建命令池,创建命令缓存,和从文件加载 PNG 图像数据,解码为 RGBA 格式,并将像素数据暂存到 Vulkan 的 暂存缓冲区中。

一、创建帧缓冲

createFramebuffers 用于创建帧缓冲(Framebuffer)的核心部分,其功能是为交换链(Swap Chain)中的每个图像视图(Image View)创建对应的帧缓冲对象。

void HelloVK::initVulkan() {createInstance();createSurface();pickPhysicalDevice();createLogicalDeviceAndQueue();setupDebugMessenger();establishDisplaySizeIdentity();createSwapChain();createImageViews();createRenderPass();createDescriptorSetLayout();createGraphicsPipeline();createFramebuffers();...
}void HelloVK::createFramebuffers() {swapChainFramebuffers.resize(swapChainImageViews.size());for (size_t i = 0; i < swapChainImageViews.size(); i++) {VkImageView attachments[] = {swapChainImageViews[i]};VkFramebufferCreateInfo framebufferInfo{};framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;framebufferInfo.renderPass = renderPass;framebufferInfo.attachmentCount = 1;framebufferInfo.pAttachments = attachments;framebufferInfo.width = swapChainExtent.width;framebufferInfo.height = swapChainExtent.height;framebufferInfo.layers = 1;VK_CHECK(vkCreateFramebuffer(device, &framebufferInfo, nullptr,&swapChainFramebuffers[i]));}
}

1.1 调整帧缓冲数组大小

根据交换链图像视图的数量调整帧缓冲数组的大小,确保两者一一对应。

swapChainFramebuffers.resize(swapChainImageViews.size());

1.2 遍历交换链图像视图

对每个交换链图像视图创建对应的帧缓冲。

for (size_t i = 0; i < swapChainImageViews.size(); i++) {...}

1.3 定义附件

此处仅使用颜色附件(swapChainImageViews[i]),即渲染结果将写入交换链图像。若需要深度、模板测试,需额外添加对应的图像视图。

VkImageView attachments[] = {swapChainImageViews[i]};

1.4 配置帧缓冲创建信息

VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = renderPass;           // 关联的渲染流程
framebufferInfo.attachmentCount = 1;               // 附件数量
framebufferInfo.pAttachments = attachments;        // 附件数组指针
framebufferInfo.width = swapChainExtent.width;     // 帧缓冲宽度
framebufferInfo.height = swapChainExtent.height;    // 帧缓冲高度
framebufferInfo.layers = 1;                        // 层数(用于多视口/立体渲染)

关键参数

  • renderPass:帧缓冲必须与渲染流程兼容(即附件格式、数量与渲染流程定义一致)。
  • widthheight:必须与交换链图像尺寸一致,否则渲染结果可能无效。
  • layers:通常为 1,用于多图层渲染(如 VR 立体视图)。

1.5 创建帧缓冲

vkCreateFramebuffer 创建实际的 Vulkan 帧缓冲对象。

VK_CHECK(vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]));

二、创建命令池

创建一个命令池,用于分配和管理命令缓冲的内存。命令缓冲用于记录 GPU 执行的渲染或计算指令。

命令池与特定的队列族(Queue Family)绑定,确保命令缓冲被提交到正确的硬件队列(如图形队列)。

void HelloVK::initVulkan() {createInstance();createSurface();pickPhysicalDevice();createLogicalDeviceAndQueue();setupDebugMessenger();establishDisplaySizeIdentity();createSwapChain();createImageViews();createRenderPass();createDescriptorSetLayout();createGraphicsPipeline();createFramebuffers();createCommandPool();...
}void HelloVK::createCommandPool() {QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);VkCommandPoolCreateInfo poolInfo{};poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();VK_CHECK(vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool));
}

2.1 获取队列族索引 QueueFamilyIndices

findQueueFamilies 函数在前面已经详细分析过,用于寻找物理设备支持的图形队列族和呈现队列族。

2.2 配置命令池创建信息

VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
  • sType:指定结构体类型为命令池创建信息。
  • flags:控制命令池的行为,此处设置为 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,允许单个命令缓冲通过 vkResetCommandBuffer 重置,而无需重置整个命令池。
  • queueFamilyIndex:指定命令池关联的队列族索引(此处为图形队列族),确保命令缓冲提交到正确的队列。

2.3 创建命令池

vkCreateCommandPool 调用 Vulkan API 创建命令池。

VK_CHECK(vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool));

三、创建命令缓存

从已创建的命令池(commandPool)中分配一组主命令缓冲(Primary Command Buffers),用于记录 GPU 执行的渲染指令。

使用 MAX_FRAMES_IN_FLIGHT 控制帧的并发数量(如双缓冲或三缓冲),避免 CPU 和 GPU 之间的资源竞争。

void HelloVK::initVulkan() {createInstance();createSurface();pickPhysicalDevice();createLogicalDeviceAndQueue();setupDebugMessenger();establishDisplaySizeIdentity();createSwapChain();createImageViews();createRenderPass();createDescriptorSetLayout();createGraphicsPipeline();createFramebuffers();createCommandPool();createCommandBuffer()...
}void HelloVK::createCommandBuffer() {commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);VkCommandBufferAllocateInfo allocInfo{};allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;allocInfo.commandPool = commandPool;allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;allocInfo.commandBufferCount = commandBuffers.size();VK_CHECK(vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()));
}

3.1 调整命令缓冲数组大小

根据预定义的 MAX_FRAMES_IN_FLIGHT(代码内设置为 2)设置命令缓冲数组的大小。每个飞行的帧需要一个独立的命令缓冲,确保 CPU 在录制下一帧时不会覆盖正在被 GPU 处理的帧数据。

commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);

3.2 配置命令缓冲分配信息

VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;                // 关联的命令池
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;  // 主命令缓冲级别
allocInfo.commandBufferCount = commandBuffers.size(); // 分配的缓冲数量

关键参数

  • commandPool:指定从哪个命令池分配内存。命令池的类型需与后续提交的队列兼容。
  • level:设置为 VK_COMMAND_BUFFER_LEVEL_PRIMARY,表示分配的是主命令缓冲(可直接提交到队列)。
级别用途
VK_COMMAND_BUFFER_LEVEL_PRIMARY直接提交到队列,可调用次级缓冲。适用于每帧的主要渲染指令。
VK_COMMAND_BUFFER_LEVEL_SECONDARY嵌入到主缓冲中,需通过主缓冲执行。适用于复用指令或并行录制。
  • commandBufferCount:需要分配的缓冲数量,与 MAX_FRAMES_IN_FLIGHT 一致。

3.3 分配命令缓冲

vkAllocateCommandBuffers 从命令池中分配指定数量的命令缓冲。

VK_CHECK(vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()));

3.4 核心概念

3.4.1 主命令缓冲(Primary Command Buffer)
  • 直接提交到队列:主缓冲可独立提交到队列执行,通常包含完整的渲染指令序列。
  • 次级缓冲的依赖:次级缓冲(SECONDARY)需通过 vkCmdExecuteCommands 在主缓冲中调用,适用于复用指令或并行录制。
3.4.2 帧并发控制(MAX_FRAMES_IN_FLIGHT)
  • 双缓冲/三缓冲:通过设置 2 或 3 个缓冲,允许 CPU 准备下一帧数据的同时,GPU 处理当前帧,避免资源冲突。
  • 同步机制:需配合信号量(Semaphore)或栅栏(Fence)确保帧的正确同步。
3.4.3 命令池与缓冲的关系
  • 内存管理:命令池负责底层内存分配,缓冲的生命周期由其所属池控制。
  • 重置行为:若命令池创建时指定了 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,可单独重置缓冲,否则需重置整个池。

3.5 完整工作流程示例

  1. 初始化阶段:创建命令池 → 分配命令缓冲。
  2. 渲染循环:
    • 等待前一帧完成(通过栅栏)。
    • 重置命令缓冲 → 录制渲染指令(如绑定管线、绘制调用)。
    • 提交命令缓冲到队列 → 呈现交换链图像。
  3. 清理阶段:销毁命令池(自动释放所有关联的缓冲)。

四、获取图片

从文件加载 PNG 图像数据,解码为 RGBA 格式,并将像素数据暂存到 Vulkan 的 暂存缓冲区(Staging Buffer) 中,为后续将数据复制到 GPU 专用的纹理图像做准备。

void HelloVK::initVulkan() {createInstance();createSurface();pickPhysicalDevice();createLogicalDeviceAndQueue();setupDebugMessenger();establishDisplaySizeIdentity();createSwapChain();createImageViews();createRenderPass();createDescriptorSetLayout();createGraphicsPipeline();createFramebuffers();createCommandPool();decodeImage();...
}void HelloVK::decodeImage() {std::vector<uint8_t> imageData = LoadBinaryFileToVector("texture.png",assetManager);if (imageData.size() == 0) {LOGE("Fail to load image.");return;}// Make sure we have an alpha channel, not all hardware can do linear filtering of RGB888.const int requiredChannels = 4;unsigned char* decodedData = stbi_load_from_memory(imageData.data(),imageData.size(), &textureWidth, &textureHeight, &textureChannels, requiredChannels);if (decodedData == nullptr) {LOGE("Fail to load image to memory, %s", stbi_failure_reason());return;}if (textureChannels != requiredChannels) {textureChannels = requiredChannels;}size_t imageSize = textureWidth * textureHeight * textureChannels;VkBufferCreateInfo createInfo{};createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;createInfo.size = imageSize;createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;VK_CHECK(vkCreateBuffer(device, &createInfo, nullptr, &stagingBuffer));VkMemoryRequirements memRequirements;vkGetBufferMemoryRequirements(device, stagingBuffer, &memRequirements);VkMemoryAllocateInfo allocInfo{};allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;allocInfo.allocationSize = memRequirements.size;allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits,VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);VK_CHECK(vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory));VK_CHECK(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0));uint8_t *data;VK_CHECK(vkMapMemory(device, stagingMemory, 0, memRequirements.size, 0,(void **)&data));memcpy(data, decodedData, imageSize);vkUnmapMemory(device, stagingMemory);stbi_image_free(decodedData);
}

4.1 加载图像文件到内存

调用 LoadBinaryFileToVector 将文件内容读取到字节数组 imageData。若文件加载失败(如路径错误或文件不存在),记录错误并退出。

std::vector<uint8_t> imageData = LoadBinaryFileToVector("texture.png", assetManager);
if (imageData.size() == 0) {LOGE("Fail to load image.");return;
}

4.2 解码图像数据

使用 STB 图像库中的函数 stbi_load_from_memory 从内存解码图像。

const int requiredChannels = 4;
unsigned char* decodedData = stbi_load_from_memory(imageData.data(), imageData.size(), &textureWidth, &textureHeight, &textureChannels, requiredChannels
);
if (decodedData == nullptr) {LOGE("Fail to load image to memory, %s", stbi_failure_reason());return;
}if (textureChannels != requiredChannels) {textureChannels = requiredChannels; // 强制设为 4
}
  • requiredChannels = 4:强制解码为 RGBA 格式(4 通道),确保兼容性(某些 GPU 对 RGB 格式的线性过滤支持不佳)。
  • 输出参数:textureWidthtextureHeight(图像尺寸)、textureChannels(实际解码的通道数)。

4.3 计算图像数据大小

size_t imageSize = textureWidth * textureHeight * textureChannels; // 总字节数

4.4 创建暂存缓冲区

暂存缓冲区作为 CPU 与 GPU 之间的数据传输桥梁。后续需通过传输命令将数据从此缓冲区复制到 GPU 专用的纹理图像。

VkBufferCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.size = imageSize;                           // 缓冲区大小
createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;   // 用途:传输源
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;    // 独占访问模式
VK_CHECK(vkCreateBuffer(device, &createInfo, nullptr, &stagingBuffer));

关键参数

  • usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT:标记为传输源。
  • sharingMode = VK_SHARING_MODE_EXCLUSIVE:缓冲区仅由图形队列独占使用(无需多队列共享)。

4.5 查询内存需求

调用 vkGetBufferMemoryRequirements 获取缓冲区的内存需求(大小、对齐、内存类型掩码)。

VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(device, stagingBuffer, &memRequirements);

4.6 分配暂存内存

调用 vkAllocateMemory 用于分配暂存内存。

VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits,VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
);
VK_CHECK(vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory));
  • VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT:内存可被 CPU 直接访问(通过 vkMapMemory)。
  • VK_MEMORY_PROPERTY_HOST_COHERENT_BIT:确保 CPU 与 GPU 内存访问的自动一致性(无需手动刷新缓存)。

findMemoryType 用于找到符合特定缓冲区内存要求的内存堆的索引。Vulkan 将这些要求以位集的形式管理,在这种情况下通过 uint32_t 来表示。

uint32_t HelloVK::findMemoryType(uint32_t typeFilter,VkMemoryPropertyFlags properties) {VkPhysicalDeviceMemoryProperties memProperties;vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags &properties) == properties) {return i;}}assert(false);  // failed to find suitable memory type!return -1;
}
  1. 调用 vkGetPhysicalDeviceMemoryProperties 获取物理设备的内存信息,包括内存类型(memoryTypes)和内存堆(memoryHeaps)。
  2. 遍历所有可用的内存类型(通常数量较小)。
  3. typeFilter & (1 << i) 检查第 i 位是否为 1。若为真,表示内存类型 i 是候选类型。
  4. (memProperties.memoryTypes[i].propertyFlags & properties) == properties 确保内存类型的属性(propertyFlags)包含 properties 的所有标志。例如,若 properties 要求内存同时是主机可见和一致的,则内存类型必须同时具备这两个属性。
  5. 返回第一个满足条件的内存类型索引。
  6. 若未找到合适内存类型,触发断言错误(调试模式下终止程序),并返回无效值 -1。

4.7 绑定内存到缓冲区

将分配的内存与缓冲区关联,偏移量设为 0(从内存起始位置绑定)。

VK_CHECK(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0));

4.8 映射内存并拷贝数据

uint8_t *data;
VK_CHECK(vkMapMemory(device, stagingMemory, 0, memRequirements.size, 0, (void **)&data));
memcpy(data, decodedData, imageSize);
vkUnmapMemory(device, stagingMemory);
  1. vkMapMemory 将 GPU 内存映射到 CPU 可访问的指针 data
  2. memcpy 将解码后的像素数据复制到映射的内存中。
  3. vkUnmapMemory 解除映射,确保数据写入完成。

4.9 释放解码数据

STB 库要求手动释放解码后的像素数据,避免内存泄漏。

stbi_image_free(decodedData);

4.10 关键概念

4.10.1 暂存缓冲区(Staging Buffer)

GPU 专用内存通常无法直接被 CPU 访问,需通过暂存缓冲区中转。

典型流程

  1. CPU 将数据写入暂存缓冲区。
  2. 提交传输命令(如 vkCmdCopyBufferToImage),将数据复制到设备本地纹理。
  3. 销毁暂存资源。
4.10.2 内存一致性

HOST_COHERENT_BIT:确保 CPU 写入的数据立即可被 GPU 读取(无缓存同步问题)。若无此标志需手动调用 vkFlushMappedMemoryRangesvkInvalidateMappedMemoryRanges 刷新缓存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/902789.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu的普通用户相关配置

1.切换到普通用户下&#xff0c;不出现&#xff0c;用户名主机ip, 环境变量被破坏&#xff0c; 参考&#xff1a;一文教你快速修改ubuntu终端显示的主机名和用户名_ubuntu终端名称-CSDN博客 2.如果登陆进去无法使用ls,cd&#xff0c;vi等命令 2.1 环境变量 如果 PATH 被清空…

腾讯云×数语科技:Datablau DDM (AI智能版)上架云应用!

在数据爆炸式增长的时代&#xff0c;传统的数据建模方式已难以满足企业对敏捷性、智能化、自动化的需求。数语科技联合腾讯云推出的 Datablau DDM 数据建模平台&#xff08;AI智能版&#xff09;&#xff0c;基于AI语义建模技术&#xff0c;深度融合腾讯混元大模型能力&#xf…

Spark-streaming(一)

Spark-Streaming概述 Spark Streaming 用于流式数据的处理。 和 Spark 基于 RDD 的概念很相似&#xff0c;Spark Streaming 使用离散化流(discretized stream)作为抽象表示&#xff0c;叫作 DStream。 DStream 是随时间推移而收到的数据的序列。 Spark-Streaming的特点&…

CS144 Lab 6 实战记录:构建 IP 路由器

1 实验背景与目标 在 CS144 的 Lab 6 中&#xff0c;我们需要在之前实现的 NetworkInterface&#xff08;Lab 5&#xff09;基础上构建一个完整的 IP 路由器。路由器的主要任务是根据路由表将接收到的 IP 数据报转发到正确的网络接口&#xff0c;并发送给正确的下一跳&#xf…

【网络安全】社会工程学策略

1. 社会工程学简介 社会工程攻击是威胁行为者常用的攻击方式。这是因为&#xff0c;诱骗人们提供访问权限、信息或金钱通常比利用软件或网络漏洞更容易。 您可能还记得&#xff0c;社会工程学是一种利用人为错误来获取私人信息、访问权限或贵重物品的操纵技术。它是一个涵盖性…

【含文档+PPT+源码】基于SpringBoot的开放实验管理平台设计与实现

项目介绍 本课程演示的是一款基于SpringBoot的开放实验管理平台设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统…

鸿蒙NEXT开发定位工具类 (WGS-84坐标系)(ArkTs)

import geoLocationManager from ohos.geoLocationManager; import { BusinessError, Callback } from ohos.base; import { LogUtil } from ./LogUtil; import { PermissionUtil } from ./PermissionUtil; import { map, mapCommon } from kit.MapKit; /*** 定位工具类 (WGS-8…

SSM从入门到上手-全面讲解SSM框架的使用.

一、SSM框架整合 将Spring、Spring MVC和MyBatis结合在一起&#xff0c;形成一个高效且易于维护的Web应用程序架构。具体整合的方式如下&#xff1a; Spring管理Bean&#xff1a;Spring负责管理所有的Java对象&#xff0c;包括Service层、DAO层等。通过Spring的IoC容器进行依赖…

学员答题pk知识竞赛小程序怎么做

制作学员答题PK知识竞赛小程序&#xff0c;主要有以下步骤&#xff1a; 一、规划设计 明确需求&#xff1a;确定小程序的使用场景是校园知识竞赛、培训机构考核还是企业内部培训等。答题功能&#xff0c;规定答题的具体规则&#xff0c;包括题目类型&#xff08;单选、多选、…

视频分析设备平台EasyCVR视频技术驱动下,监控上墙全组件解析与组网应用方案

随着数字化进程的加速推进&#xff0c;视频监控技术在工业、商业、社区等诸多领域得到了广泛应用。尽管不同场景对监控功能的具体需求存在差异&#xff0c;但底层硬件架构具有显著的共性特征。实际部署中&#xff0c;仅需依据网络环境等实际情况&#xff0c;灵活调整设备的连接…

idea使用docker插件一键部署项目

一、首先保证我们电脑上已经安装了docker docker -v查看docker版本&#xff0c;如果不能识别&#xff0c;需要先下载docker destop&#xff0c;在官网下载正常安装即可。 安装成功就可以使用docker 命令了 二、idea下载docker插件并配置docker参数 我是通过tcp连接docker服务…

SQL Tuning Advisor

什么是SQL Tuning Advisor STA可以用来优化那些已经被发现的高负载SQL. 默认情况下, Oracle数据库在自动维护窗口中自动认证那些有问题的SQL并且执行优化建议&#xff0c;找寻提升高负载SQL执行计划性能的方法. ** 如何查看自动优化维护窗口产生的报告? ** SQL> set ser…

uniapp-商城-31-shop页面中的 我的订单

前面的章节讲了很多关于页面 布局 的知识。 现在来看看其他栏目&#xff0c;我的订单页面。 1 页面样式图 基本的样式包含shop页面 我的订单 点击我的订单&#xff0c;跳转到订单页面 点击订单的每一条订单&#xff0c;跳转到订单详情 2、创建订单页面 2.1 创建sub页面文件…

深入探讨JavaScript性能瓶颈与优化实战指南

JavaScript作为现代Web开发的核心语言,其性能直接影响用户体验与业务指标。随着2025年前端应用的复杂性持续增加,性能优化已成为开发者必须掌握的核心技能。本文将从性能瓶颈分析、优化策略、工具使用三个维度,结合实战案例,系统梳理JavaScript性能优化的关键路径。 一、Ja…

基于AI与drawio的图表生成技术及其在学术研究中的应用前景分析

一、研究背景与冲突 在当今数字化时代&#xff0c;学术研究与信息传播的方式发生了深刻变革。随着数据量的爆炸式增长以及研究内容的日益复杂&#xff0c;高效、精准地呈现研究成果变得至关重要。图表作为一种直观、简洁且信息承载量大的表达方式&#xff0c;在学术研究中扮演着…

uniapp 仿小红书轮播图效果

通过对小红书的轮播图分析&#xff0c;可得出以下总结&#xff1a; 1.单张图片时容器根据图片像素定高 2.多图时轮播图容器高度以首图为锚点 3.比首图长则固高左右留白 4.比首图短则固宽上下留白 代码如下&#xff1a; <template><view> <!--轮播--><s…

【ORACLE】记录一些ORACLE的merge into语句的BUG

【ORACLE】记录一些ORACLE的merge into语句的BUG 一、自相矛盾-DML重启动行为差异,违反acid原则 发现版本&#xff1a;10g ~ 23ai 这个用例在我之前的文章里有提过&#xff0c;ORACLE和PG系关于并发事务行为有一个非常大的差异&#xff0c;就是ORACLE在某些并发冲突的场景下会…

2025上海车展:光峰科技全球首发“灵境”智能车载光学系统

当AI为光赋予思想&#xff0c;汽车将会变成什么样&#xff1f;深圳光峰科技为您揭晓答案。 2025年4月23日&#xff0c;在刚刚开幕的“2025上海车展”上&#xff0c;全球领先的激光核心器件公司光峰科技举办了主题为“AI光影盛宴&#xff0c;智享未来出行”的媒体发布会&#x…

密码学的hash函数,哈希碰撞, collision resistance, BTC用到的SHA-256简介

密码学中的哈希函数、哈希碰撞、抗碰撞性&#xff08;collision resistance&#xff09;以及比特币中使用的 SHA-256 的简明介绍&#xff1a; &#x1f9e9; 一、哈希函数&#xff08;Hash Function&#xff09; 定义&#xff1a; 哈希函数是一种将任意长度的输入&#xff08;…

unity TEngine学习4

上一篇我们学习了UI部分&#xff0c;这一篇我们学习其他部分&#xff0c;按照老规矩还是先打开官方文档 ResourceModule 在官方文档里介绍了当前加载的设置&#xff0c;但是我们是小白看不懂&#xff0c;那就不管他内部怎么实现的&#xff0c;我们主要看下面的代码给的方法&am…