Qt的场景图Scene Graph

news2024/11/26 1:41:10

叫场景树更合适,本质不是图。QML场景中的Qt Quick项目将填充QSGNode实例树。

场景图是Qt Quick 2.0引入的,建立在要绘制的内容是已知的基础上。所有QML项目均使用场景图进行渲染,场景图的默认实现是与OpenGL紧密相关的低级高性能渲染堆栈。

qt的场景图和osg的场景图的组织上有些类似,都是不同节点通过一定关系构建的,但是osg的场景节点更多些,并且还关联了渲染状态。在渲染方面,qt是直接对场景图进行渲染,osg是将场景图转换为渲染树再进行渲染(避免渲染状态的频繁切换)。

qt的场景图是根据界面元素的位置、透明等信息构建出来的,而osg的场景图是直接利用节点构建出来的。也就是用户不直接参与qt场景图的构建,但是直接参与osg场景图的构建。

例如,假设用户界面包含十个项目的列表,其中每个项目都有背景色,图标和文本。使用传统的绘图技术,这将导致30次绘图调用和类似数量的状态更改。另一方面,场景图可以重组原始图元以进行渲染,以便在一次调用中绘制所有背景,然后绘制所有图标,然后绘制所有文本,从而将绘制调用的总数减少到仅3个。批处理和状态更改减少这样可以大大提高某些硬件的性能。

场景图由QQuickWindow类管理和呈现,自定义Item类型可以通过调用QQuickItem :: updatePaintNode()将其图形基元添加到场景图中。

场景图是Item场景的图形表示,它是一个独立的结构,其中包含足以渲染所有项目的信息。设置完成后,就可以独立于项目状态对其进行操作和渲染。在许多平台上,场景图形甚至会在GUI线程准备下一帧状态时在专用渲染线程上进行渲染。

场景图的结构

场景图由许多预定义的节点类型组成,每种类型都有专门的用途。尽管我们将其称为场景图,但更精确的定义是节点树。该树是根据QML场景中的QQuickItem类型构建的,然后在内部由渲染该场景的渲染器处理该场景。节点本身不包含任何活动的绘图代码或虚拟paint()函数。

即使节点树主要由现有的Qt Quick QML类型在内部构建,用户也可以添加具有自己内容的完整子树,包括表示3D模型的子树。

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

节点

对于用户而言,最重要的节点是QSGGeometryNode。它用于通过定义其几何形状和材质来定义自定义图形。使用QSGGeometry定义几何形状,并描述图形图元的形状或网格。它可以是直线,矩形,多边形,许多不连续的矩形或复杂的3D网格。该材质定义如何填充此形状的像素。

一个节点可以有任意数量的子节点,并且将渲染几何节点,以便它们以子顺序出现,并且父级位于其子级之后。

常见的节点有:

QSGClipNode

在场景图中实现裁剪功能的节点

QSGGeometryNode

用于场景图中的所有渲染内容的节点

QSGNode

场景图中所有节点的基类的节点

QSGOpacityNode

用于更改节点的不透明度的节点

QSGTransformNode

在场景图中实现变换的节点

QSGRenderNode

表示一组针对场景所使用的图形API的自定义渲染命令。

通过子类QQuickItem :: updatePaintNode()并设置QQuickItem :: ItemHasContents标志,将自定义节点添加到场景图。

至关重要的是,本机图形(OpenGL,Vulkan,Metal等)操作以及与场景图的交互必须专门在渲染线程上进行,主要是在updatePaintNode()调用期间进行。经验法则是仅在QQuickItem :: updatePaintNode()函数内使用带有“ QSG”前缀的类。

处理过程

节点具有虚拟QSGNode :: preprocess()函数,该函数将在呈现场景图之前被调用,主要用于处理节点要渲染的内容。节点子类可以设置标志QSGNode :: UsePreprocess并重写QSGNode :: preprocess()函数以对其节点进行最终准备。例如,将贝塞尔曲线划分为当前比例因子的正确细节级别或更新纹理的一部分。

节点的所有权

节点的所有权由创建者或场景图通过设置标志QSGNode :: OwnedByParent明确完成。通常,将所有权分配给场景图通常是可取的,因为这样可以简化场景图位于GUI线程之外时的清理操作。

材质

材质描述了如何填充QSGGeometryNode中几何图形的内部。它封装了用于图形管线顶点和片段阶段的图形着色器,并提供了足够的灵活性,尽管大多数Qt Quick项目本身仅使用非常基本的材质,例如纯色和纹理填充。

对于只想将自定义阴影应用于QML Item类型的用户,可以使用ShaderEffect类型在QML中直接执行此操作。

以下是材质类别的完整列表:

QSGFlatColorMaterial

在场景图中渲染纯色几何的便捷方法

QSGMaterial

封装着色器程序的渲染状态

QSGMaterialRhiShader

表示独立于图形API的着色器程序

QSGMaterialShader

表示渲染器中的OpenGL着色器程序

QSGMaterialType

与QSGMaterial结合用作唯一类型令牌

QSGOpaqueTextureMaterial

在场景图中渲染纹理几何的便捷方法

QSGTextureMaterial

在场景图中渲染纹理几何的便捷方法

QSGVertexColorMaterial

Convenient way of rendering per-vertex colored geometry in the scene graph

便利节点

场景图API是低级的,专注于性能而不是便利。从头开始编写自定义的几何图形和材质,即使是最基本的几何图形和材质,也需要大量的代码。因此,API包含一些便利类,以使最常见的自定义节点易于使用。

QSGSimpleRectNode-QSGGeometryNode子类,它使用纯色材质定义矩形几何。

QSGSimpleTextureNode-QSGGeometryNode子类,它使用纹理材质定义矩形几何形状。

场景图与渲染

场景图的呈现发生在QQuickWindow类的内部,并且没有公共API可以访问它。但是,呈现管道中有一些地方可供用户附加应用程序代码。可通过直接调用场景图使用的图形API(OpenGL,Vulkan,Metal等)来添加自定义场景图内容或插入任意渲染命令。这个集成点由渲染循环定义。

共有三种渲染循环变体:基本,窗口和线程。其中,基本和窗口是单线程的,而线程在专用线程上执行场景图渲染。 Qt尝试根据平台以及可能使用的图形驱动程序选择合适的循环。如果这不令人满意,或者出于测试目的,则可以使用环境变量QSG_RENDER_LOOP强制使用给定的循环。要验证使用哪个渲染循环,请启用qt.scenegraph.general日志记录类别。

线程和Windows渲染循环依赖于图形API实现来进行节流,例如,在OpenGL的情况下,通过请求交换间隔为1。一些图形驱动程序允许用户忽略此设置并将其关闭,而忽略Qt的请求。在不阻塞交换缓冲区操作(或其他位置)的情况下,渲染循环将以太快的速度运行动画并使CPU旋转100%。如果已知系统无法提供基于vsync的限制,请使用基本渲染循环,而不是在环境中设置QSG_RENDER_LOOP = basic。

基于线程的渲染循环

在许多配置中,场景图渲染将在专用渲染线程上进行。这样做是为了增加多核处理器的并行度,并更好地利用停顿时间,例如等待阻塞交换缓冲区调用。这可以显着提高性能,但是对与场景图进行交互的位置和时间施加了某些限制。

以下是有关如何使用线程渲染循环和OpenGL渲染帧的简单概述。除了OpenGL上下文的特定要求外,其他图形API的步骤也相同。

1、QML场景中发生更改,导致调用QQuickItem :: update()。例如,这可能是动画或用户输入的结果。事件被发布到渲染线程以启动新帧。

2、渲染线程准备绘制新帧。

3、在渲染线程准备新帧时,GUI线程调用QQuickItem :: updatePolish()对项目进行最终修饰,然后再渲染它们。

4、阻塞GUI线程。

5、发出QQuickWindow :: beforeSynchronizing()信号。应用程序可以对此信号进行直接连接(使用Qt :: DirectConnection),以进行调用QQuickItem :: updatePaintNode()之前所需的任何准备工作。

6、将QML状态同步到场景图中。这是通过在自上一帧以来已更改的所有项目上调用QQuickItem :: updatePaintNode()函数来完成的。这是QML项与场景图中的节点唯一的交互。

7、释放GUI线程。

8、渲染场景图

8.1、发出QQuickWindow :: beforeRendering()信号。应用程序可以对此信号进行直接连接(使用Qt :: DirectConnection),以使用自定义图形API调用,然后将其可视化地堆叠在QML场景下。

8.2、指定了QSGNode :: UsePreprocess的项目将调用其QSGNode :: preprocess()函数。

8.3、渲染器处理节点。

8.4、渲染器生成状态并记录使用中的图形API的绘制调用。

8.5、发出QQuickWindow :: afterRendering()信号。应用程序可以对此信号进行直接连接(使用Qt :: DirectConnection)以发出自定义图形API调用,然后将这些调用可视化地堆叠在QML场景上。

8.6、现在帧已准备就绪。交换缓冲区(OpenGL),或记录当前命令,然后将命令缓冲区提交到图形队列(Vulkan,Metal)。 QQuickWindow :: frameSwapped()被发射。

9、在渲染线程正在渲染时,GUI可以自由地进行动画,处理事件等。

当前,默认情况下,线程渲染器可以在具有opengl32.dll的Windows平台、不包括Mesa llvmpipe的Linux平台、具有Metal的macOS平台、移动平台、具有EGLFS的嵌入式Linux平台以及所有Vulkan平台上使用,但这可能会有所更改。通过在环境中设置QSG_RENDER_LOOP = threaded,始终可以强制使用线程渲染器。

有关frameSwapped信号

当帧已排队等待呈现时,将发出此信号。启用垂直同步后,在连续动画场景中,每个vsync间隔最多发射一次信号。该信号将从场景图形渲染线程中发出。

渲染线程的渲染代码:

qtdeclarative\src\quick\scenegraph\qsgthreadedrenderloop.cpp
void QSGRenderThread::syncAndRender()
{
bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled();
if (profileFrames) {
sinceLastTime = threadTimer.nsecsElapsed();
threadTimer.start();
}
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
QElapsedTimer waitTimer;
waitTimer.start();
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "syncAndRender()");
syncResultedInChanges = false;
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage;
bool syncRequested = pendingUpdate & SyncRequest;
bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
pendingUpdate = 0;
if (syncRequested) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- updatePending, doing sync");
sync(exposeRequested);//从这里调用各个QQuickItem(包括其继承类)的updatePaintNode函数
}
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
syncTime = threadTimer.nsecsElapsed();
#endif
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync);
if (!syncResultedInChanges && !repaintRequested && sgrc->isValid()) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- no changes, render aborted");
int waitTime = vsyncDelta - (int) waitTimer.elapsed();
if (waitTime > 0)
msleep(waitTime);
return;
}
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering started");
if (animatorDriver->isRunning()) {
d->animationController->lock();
animatorDriver->advance();
d->animationController->unlock();
}
bool current = false;
if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0)
current = gl->makeCurrent(window);
// Check for context loss.
if (!current && !gl->isValid()) {
// Cannot do anything here because gui is not locked. Request a new
// sync+render round on the gui thread and let the sync handle it.
QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
}
if (current) {
d->renderSceneGraph(windowSize);//执行场景图的渲染操作,本质上是调用opengl命令
if (profileFrames)
renderTime = threadTimer.nsecsElapsed();
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopRender);
if (!d->customRenderStage || !d->customRenderStage->swap())
gl->swapBuffers(window);//交换前后缓冲区,将渲染的内容显示出来
d->fireFrameSwapped();
} else {
Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSync, 1);
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window not ready, skipping render");
}
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering done");
// Though it would be more correct to put this block directly after
// fireFrameSwapped in the if (current) branch above, we don't do
// that to avoid blocking the GUI thread in the case where it
// has started rendering with a bad window, causing makeCurrent to
// fail or if the window has a bad size.
if (exposeRequested) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- wake Gui after initial expose");
waitCondition.wakeOne();
mutex.unlock();
}
qCDebug(QSG_LOG_TIME_RENDERLOOP,
"Frame rendered with 'threaded' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)",
int(threadTimer.elapsed()),
int((syncTime/1000000)),
int((renderTime - syncTime) / 1000000),
int(threadTimer.elapsed() - renderTime / 1000000));
Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame,
QQuickProfiler::SceneGraphRenderLoopSwap);
}

非线程的渲染循环(basic或windows)

当前,默认情况下,非线程渲染循环在具有ANGLE或非默认opengl32实现的Windows,具有OpenGL的macOS和具有某些驱动程序的Linux上使用。对于后者,这主要是一种预防措施,因为并非所有OpenGL驱动程序和窗口系统的组合都已经过测试。同时,诸如ANGLE或Mesa llvmpipe之类的实现根本无法在线程渲染中正常运行,因此,对于这些线程,必须不要使用线程渲染。

在macOS和OpenGL上,使用XCode 10(10.14 SDK)或更高版本进行构建时,不支持线程渲染循环,因为这会选择在macOS 10.14上使用基于图层的视图。您可以使用Xcode 9(10.13 SDK)进行构建,以选择不支持图层支持,在这种情况下,线程渲染循环可用并且默认情况下使用。 Metal没有这样的限制。

默认情况下,Windows用于具有ANGLE的Windows上的非线程渲染,而当需要非线程渲染时,basic用于所有其他平台。

即使在使用非线程渲染循环时,也应该像使用线程渲染器一样编写代码,否则将使代码不可移植。

以下是非线程渲染器中帧渲染序列的简化图示。

使用QQuickRenderControl自定义渲染控制

使用QQuickRenderControl时,将驱动渲染循环的责任转移到应用程序中。在这种情况下,不使用内置的渲染循环。取而代之的是,应由应用程序在适当的时候调用抛光,同步和渲染步骤。可以实现类似于上述行为的线程行为或非线程行为。

混合场景图和本机图形API

场景图提供了两种方法来集成应用程序提供的图形命令:通过直接发出OpenGL,Vulkan,Metal等命令,以及在场景图中创建纹理化节点。

通过连接到QQuickWindow :: beforeRendering()和QQuickWindow :: afterRendering()信号,应用程序可以直接在场景图渲染到的同一上下文中进行OpenGL调用。使用Vulkan或Metal之类的API,应用程序可以通过QSGRendererInterface查询本机对象,例如场景图的命令缓冲区,并在认为合适的情况下向其记录命令。如信号名称所示,用户随后可以在Qt Quick场景下或上方渲染内容。以这种方式集成的好处是不需要额外的帧缓冲区或内存来执行渲染,并且消除了可能昂贵的纹理化步骤。缺点是Qt Quick决定何时调用信号,这是唯一允许OpenGL应用程序绘制的时间。

另一个方法(当前仅适用于OpenGL)是创建一个QQuickFramebufferObject,将其渲染到其中,然后将其作为纹理显示在场景图中。 “场景图-渲染FBO”示例显示了如何完成此操作。还可以组合多个渲染上下文和多个线程以创建要在场景图中显示的内容。场景图-线程示例中的渲染FBO显示了如何完成此操作。

即使QQuickFramebufferObject当前不支持,除OpenGL之外的其他图形API也可以采用这种方法。 “场景图-金属纹理导入”示例中演示了直接使用基础API创建和渲染纹理,然后在自定义QQuickItem中的Qt Quick场景中包装和使用此资源。该示例使用了Metal,但是概念也适用于所有其他图形API。

警告:将OpenGL内容与场景图形渲染混合时,重要的是应用程序不要使OpenGL上下文处于缓冲区绑定,启用属性,z缓冲区或模版缓冲区中的特殊值或类似状态。这样做可能导致无法预测的行为。

警告:自定义渲染代码应该意识到是在线程中执行,而不是在应用程序的GUI(主)线程上执行。

使用QPainter的自定义Item

QQuickItem提供了一个子类QQuickPaintedItem,它允许用户使用QPainter渲染内容。

警告:使用QQuickPaintedItem通过软件光栅化或OpenGL帧缓冲对象(FBO)使用间接2D表面来渲染其内容,因此渲染是一个两步操作。首先栅格化表面,然后绘制表面。直接使用场景图API总是非常快。

日志功能

场景图支持许多日志记录类别。除了对Qt贡献者有所帮助之外,这些还可用于跟踪性能问题和错误。

qt.scenegraph.time.texture-记录进行纹理上传所花费的时间

qt.scenegraph.time.compilation-记录进行着色器编译所花费的时间

qt.scenegraph.time.renderer-记录渲染器各个步骤所花费的时间

qt.scenegraph.time.renderloop-记录渲染循环各个步骤所花费的时间

qt.scenegraph.time.glyph-记录准备距离场字形所花费的时间

qt.scenegraph.general-记录有关场景图和图形堆栈各个部分的常规信息

qt.scenegraph.renderloop-创建渲染所涉及的各个阶段的详细日志。此日志模式主要对使用Qt的开发人员有用。

旧版QSG_INFO环境变量也可用。将其设置为非零值将启用qt.scenegraph.general类别。

注意:遇到图形问题时,或不确定正在使用哪个渲染循环或图形API时,请始终在至少启用qt.scenegraph.general和qt.rhi。*或设置QSG_INFO = 1的情况下启动应用程序。然后,这将在初始化期间将一些基本信息打印到调试输出上。

除公共API外,场景图还具有适应层,该适应层打开实现以进行硬件特定的适应。这是一个未公开的内部和专用插件API,可让硬件适应小组充分利用其硬件。这包括:

自定义纹理:特别是QQuickWindow :: createTextureFromImage的实现以及Image和BorderImage类型使用的纹理的内部表示。

自定义渲染器:适配层使插件可以决定如何遍历和渲染场景图,从而有可能针对特定硬件优化渲染算法或使用可提高性能的扩展。

许多默认QML类型的自定义场景图实现,包括其文本和字体渲染。

自定义动画驱动程序:允许动画系统连接到低级显示设备的垂直刷新中,以获得平滑的渲染。

自定义渲染循环:可以更好地控制QML如何处理多个窗口。

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

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

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

相关文章

谈谈前端性能优化-面试版

前言 当我们去面试的时候,很大概率会被面试官问这么一个问题:你有尝试过对项目做性能优化吗?或者你了解哪些性能优化的方法?听到这个问题的你可能是这样的: 似曾相识但又说不清楚,往往只能零散地说出那么几…

Go string原理简析

引入 当查看string类型的变量所占的空间大小时,会发现是16字节(64位机器)。 str : "hello"fmt.Println(unsafe.Sizeof(str)) // 16也许你会好奇,为什么是16字节,它的底层存储模型是什么样子的。 源码分析 …

焦脱镁叶绿酸-a修饰量子点/荧光/药物/小分子抑制剂/上转换纳米颗粒/树枝状聚合物

小编在这里为大家分享的科研内容是焦脱镁叶绿酸-a修饰量子点/荧光/药物/小分子抑制剂/上转换纳米颗粒/树枝状聚合物的相关研究,来看! 焦脱镁叶绿酸-a简介: 焦脱镁叶绿素-a是产物叶绿素a通过脱甲氧羰基、去植物醇、去Mg后的产物。该类物质具有…

day19【代码随想录】删除字符串中的所有相邻重复项、逆波兰表达式求值、滑动窗口最大值、前 K 个高频元素、数组中的第K个最大元素

文章目录前言一、删除字符串中的所有相邻重复项(力扣047)二、逆波兰表达式求值(力扣150)三、滑动窗口最大值(力扣239)四、前 K 个高频元素(力扣347)五、数组中的第K个最大元素&#…

MyBatis系列---crud返回值

目录1. service与mapper2. 更新操作3. 查询操作3.1. 返回值存储3.2. 简单映射3.3. ResultSet 的预处理3.4. 确定 ResultMap3.5. 创建映射结果对象3.6. 自动映射3.7. 存储对象3.8. 返回结果为单行数据3.9. 返回结果为多行数据3.10. 结论1. service与mapper mybatis一般与spring…

深度活体模型带交互模型版

🍿*★,*:.☆欢迎您/$:*.★* 🍿

点击Tab标签切换不同查询数据,并选择数据存入缓存实现两个界面带参数跳转

项目场景: 在不同的tab标签页中点击不同的标签页查找不同的内容,然后选中其中一个页面中的一条数据将此数据某个信息选中然后存入session缓存当中然后另一个界面从session中取出,从而达到带参数跳转界面的需求 问题描述 可以做到跳转界面但是数据会显示到地址栏当…

做开发4年了,年薪还不如2年经验的测试。我该适应当下节奏吗...

代码码了这么些年,你年薪达到多少了? 我,4年码龄,薪资最高的时候16k*12薪,年薪不到20W。都说IT行业薪资高,但年薪百万的还是金字塔尖极少数,像我这样的才是普通的大多数,却也还要用…

电脑维护与故障处理

第一章 认识电脑的组成 1.1 硬件组成 1.1.1 CPU 1.1.2 主板 1.1.3 内存 1.1.4 硬盘 1.1.5 电源 1.1.6 显示器 1.1.7 键盘和鼠标 1.1.8 光驱 1.1.9 显卡 1.1.10 其他外部设备 1.2 软件组成 1.2.1 操作系统 Windows XP Windows 7 服务器操作系统 —— Windows Ser…

04-Nginx-conf配置文件基本了解

Nginx负载均衡,反向代理入门配置: nginx.conf整体结构 nginx入门基本配置 Nginx.conf配置文件详解(upstream和location负载均衡和反向代理配置): #运行用户 user www-data; #启动进程,通常设置成和cpu的数量相等 wor…

基于边缘智能网关打造智慧体育场

运动健身是民众广泛存在的生活需求,体育场馆作为承载各种体育运动的基础设施,其运营管理效率、服务水平和智能化场景应用等都与用户体验紧密相关。 得益于物联网、边缘计算、AI智能等新技术的广泛应用,当前已有越来越多体育场馆通过部署基于…

数据结构与算法——Java实现稀疏数组和队列

目录 一、基本介绍 1.1 线性结构 1.2 非线性顺序结构 二、稀疏数组 2.1 基本介绍 2.1.1 应用场景 2.1.2 实现思路 2.2 代码实现 2.2.1 原始数组 2.2.2 原始数组转化为稀疏数组 2.2.3 稀疏数组转化为原始数组 三、队列的应用场景和介绍 3.1 数组模拟队列 3.1.1数组模拟队列的…

Find My资讯|Seinxon推出支持苹果 Find My 防丢卡

在美国,平均每个人每年丢失 3,000 件物品。而在 2021 年,Pixie 数据显示,丢失产品的更换成本超过 25 亿美元。每周超过两次,将近 1/4 的美国人丢失房门钥匙、钱包、宠物、电话、眼镜、耳机、遥控器、手提箱或孩子最喜欢的物品。 …

GIT系列(七)切换ssh连接,上传不再输入账号、密码

文章目录前言操作流程前言 使用HTTP连接方式时,上传代码总是需要登录,键盘都打坏了,切换SSH可以无需密码,直接上传。 操作流程 step 1 确保在git服务器已经部署本机公钥。 没有配置SSH的,戳这里 GIT系列(…

k8s教程(18)-pod之DaemonSet(每个node上只调度一个pod)

文章目录01 引言02 DaemonSet2.1 应用场景2.2 举例2.3 注意事项03 文末01 引言 声明:本文为《Kubernetes权威指南:从Docker到Kubernetes实践全接触(第5版)》的读书笔记 DaemonSet是 Kubernetes1.2 版本新增的一种资源对象&#xf…

事件轮询机制 Event Loop、浏览器更新渲染时机、setTimeout VS setInterval

目录 1. 事件轮询机制(Event Loop)是什么 1.1 宏任务、微任务 1.2 Event Loop 循环过程 1.3 经典题目分析 1.3.1 第一轮事件循环 1.3.2 第二、三次事件循环 1.3.3 参考文章 2. async、await 在事件轮询中的执行时机 3. 浏览器更新渲染时机、Vue…

线上使用雪花算法生成id重复问题

项目中使用的是hutool工具类库提供的雪花算法生成id方式&#xff0c;版本使用的是5.3.1 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.1</version></dependency>雪花算法生成id…

10分钟数仓实战之kettle整合Hadoop

1.写在前面 很多朋友在做数仓的ETL的动作的时候&#xff0c;还是喜欢比较易上手的kettle 前面章节有介绍过安装kettle&#xff0c;可以参考 ETL工具--安装kettle_老码试途的博客-CSDN博客_spoon.bat 安装 kettle在Windows系统中对数据的转换、表和文件的转换等&#xff0c;…

Blender 3D环境场景创建教程

Blender 3D环境场景创建教程 学习 Blender 3.2&#xff0c;探索几何节点并创建美妙的 3D 环境 课程英文名&#xff1a;Creating 3D Environments in Blender 2.81 by Rob Tuytel (2019) 此视频教程共8.0小时&#xff0c;中英双语字幕&#xff0c;画质清晰无水印&#xff0c;…

腾讯云从业者基础认证完整笔记

腾讯云从业者基础认证完整笔记 就考这些&#xff0c;干就完事儿了&#xff01;不要介意图多哟&#xff0c;ppt能更好的表达意思呀 一、云计算基础 1.1 数据中心 一般企业要么自建数据中心EDC&#xff0c;EDC分层如下&#xff1a; 要么租用或者托管也就是IDC如下&#xff…