QML 中计算着色器的段错误
Seg fault with compute shader in QML
如何在主要为 QML 的应用程序中追踪段错误?
具体错误为:*** Error in 'app/app': double free or corruption (!prev): 0x00007ff4bce5e710 ***
只有当我有一个 ComputeCommand
时才会发生(如下所示。)它本身的错误是间歇性的。这是在使用 clang 构建的 Ubuntu 14.04.03 上使用 Qt 5.7.1(同样发生在 Qt 5.8.0 和 Ubuntu 16.04)上。在调试模式下(使用 gdb)我没有得到任何有用的东西(见下文),只是我的应用程序中发生了一些事情,然后是一堆没有符号信息的 Qt3DRender 库。 Valgrind 和 Hellgrind 使应用程序变慢太多而无法使用它(这个应用程序可视化了数千个点,我无法在非常低的点数下引发问题),ASAN 和 TSAN 确实报告了问题,但没有 symbols/line我找不到任何东西。
我的设置是 main.qml
-> Scene3d
-> RenderSettings
, Entity
我使用我编写的自定义缓冲区和几何图形(PointBuffer
,PointGeometry
)我编写但未包括在内。它们分别继承自 QBuffer
和 QGeomotry
。
本质上,当计算着色器被激活时(实体上的 ComputeCommand
组件,以及 viewport
上的 DispatchCompute
),我得到上面的间歇性段错误。我添加的唯一异步组件是我的缓冲区被我自己的线程写入,我用 std::mutex
包围了它,但我认为这不是问题,因为我没有看到任何QBuffer
或 QGeometry
代码中的互斥。
着色器本身创建了一个连贯的缓冲区(layout (std430, binding = 0) coherent buffer Particles
),但除此之外只是读入并打印出点;并在我没有任何操作的情况下工作..当然直到出现段错误。
我的问题是,我的 Compute Shader 配置不当吗?或者有更好的调试方法吗?
我能追踪到的最多的问题是它只发生在几何图形的缓冲区刷新之后。
渲染设置:
RenderSettings {
property CameraSet cameraSet
property real userViewWidth: 0.79
property real topOrthoViewHeight: 0.79
activeFrameGraph: Viewport {
id: viewport
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
RenderSurfaceSelector {
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
NoDraw {}
}
// Compute Pass
DispatchCompute {
workGroupX: 1024; workGroupY: 1; workGroupZ: 1
TechniqueFilter {
matchAll: [
FilterKey { name: "type"; value: "compute" }
]
}
}
Viewport {
id: userViewport
normalizedRect: Qt.rect(0, 0, 0.5, 1.0)
CameraSelector {
id: userCameraSelectorViewport
camera: cameraSet.user.camera
}
}
// Second and third viewport...
}
}
}
Material (PointMaterial
)
Material {
property PointBuffer dataBuffer;
ShaderProgram {
id: computeShader
computeShaderCode: loadSource("qrc:/shaders/pointcloud.comp")
}
ShaderProgram {
id: drawShader
vertexShaderCode: loadSource("qrc:/shaders/pointcloud.vert")
fragmentShaderCode: loadSource("qrc:/shaders/pointcloud.frag")
}
effect: Effect {
techniques: [
Technique {
renderPasses: [
RenderPass {
shaderProgram: computeShader
parameters: [
// Point buffer
Parameter { name: "Particles"; value: dataBuffer }
]
}
] // renderpasses
filterKeys: [
FilterKey { name: "type"; value: "compute" }
]
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
profile: GraphicsApiFilter.CoreProfile
majorVersion: 4
minorVersion: 3
}
},
Technique {
renderPasses: [
RenderPass {
shaderProgram: drawShader
renderStates: [
PointSize { sizeMode: PointSize.Programmable }
]
parameters: [
Parameter { name: "pointSize"; value: 0.4 }
]
}
] // renderPasses
filterKeys: [
FilterKey { name: "type"; value: "draw" }
]
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
profile: GraphicsApiFilter.CoreProfile
majorVersion: 4
minorVersion: 3
}
} // technique
] // techniques
}
}
实体:
Entity {
property PointBuffer buffer: PointBuffer {
id: pointBuffer
type: Buffer.VertexBuffer
}
PointsMaterial {
id: pointsMaterial
dataBuffer: pointBuffer
}
Entity {
property GeometryRenderer pointRenderer: GeometryRenderer {
instanceCount: 1 // How many times to run this geometry, since we write
// all points in the buffer once, only call this once.
primitiveType: GeometryRenderer.Points
geometry: PointGeometry { buffer: pointBuffer }
}
// https://doc-snapshots.qt.io/qt5-5.7/qml-computecommand.html
property ComputeCommand computeCommand: ComputeCommand {
workGroupX: 1024; workGroupY: 1; workGroupZ: 1
}
components: [ pointRenderer, computeCommand, pointsMaterial ]
}
}
我试图限制发布的代码量以使问题变小,但我很乐意添加任何有助于解决问题的代码。
我的 (gdb) 调试器从主线程输出的是:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fff9e9cc700 (LWP 8208)]
0x00007ffff33e058c in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
(gdb) bt
#0 0x00007ffff33e058c in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#1 0x00007ffff33e6619 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#2 0x00007ffff338fe05 in Qt3DRender::Render::Renderer::performCompute(Qt3DRender::Render::RenderView const*, Qt3DRender::Render::RenderCommand*) () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#3 0x00007ffff3392610 in Qt3DRender::Render::Renderer::executeCommandsSubmission(Qt3DRender::Render::RenderView const*) () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#4 0x00007ffff3392b14 in Qt3DRender::Render::Renderer::submitRenderViews(QVector<Qt3DRender::Render::RenderView*> const&) () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#5 0x00007ffff3394bf2 in Qt3DRender::Render::Renderer::doRender() ()
from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#6 0x00007fffc723a806 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/qml/QtQuick/Scene3D/libqtquickscene3dplugin.so
#7 0x00007ffff1f56e3d in QMetaObject::activate(QObject*, int, int, void**) ()
from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Core.so.5
#8 0x00007ffff3ef865e in QQuickWindowPrivate::renderSceneGraph(QSize const&) ()
from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Quick.so.5
#9 0x00007ffff3ecabd1 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Quick.so.5
#10 0x00007ffff3ecbe58 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Quick.so.5
#11 0x00007ffff1d68539 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Core.so.5
#12 0x00007ffff0eb3184 in start_thread (arg=0x7fff9e9cc700) at pthread_create.c:312
#13 0x00007ffff11c337d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
更新
很确定我已将其缩小为竞争条件。我已经删除了我认为可能会导致问题的其他库,但什么也没有。基本上,计算过滤器和异步重写缓冲区的组合会导致此段错误。我不确定是否有某种方法可以使用互斥锁来保护它。现在我正在寻找是否可以用 C++ 重写我的实体,以更好地控制计算着色器的执行方式。
我认为这里出现段错误的原因是我更新缓冲区的方式造成的。在我的 PointBuffer
中,我有一个调用 Qt3DRender::QBuffer::setData(new_data)
的异步任务,其中 new_data
是 QByteArray
.
为了设置数据,我在堆栈上创建了一个 QByteArray
,将我的数据复制到其中,然后使用 Qt3DRender::QBuffer::setData(my_qbyte_array)
设置我的数据
这具有将我的堆栈变量暴露给应用程序其余部分的效果,删除后会导致段错误。
我改为:
Qt3DRender::QBuffer::setData(
QByteArray::fromRawData(
reinterpret_cast<const char*>(points_.linearize()),
sizeof(PointType) * pointCount
)
)
这改为指向我的原始数据(因此不需要错误副本)并且不绑定到任何临时数据。
我提到的另一种解决方案是使用 Qt 5.8.0 的新
QBuffer::updateData
docs函数。有了这个,我创建了一个本地 QByteArray
,然后将其复制到我当前的数据中。这有问题(特别是在调整大小方面)并且效率较低,但我想我还是会提到它。
如何在主要为 QML 的应用程序中追踪段错误?
具体错误为:*** Error in 'app/app': double free or corruption (!prev): 0x00007ff4bce5e710 ***
只有当我有一个 ComputeCommand
时才会发生(如下所示。)它本身的错误是间歇性的。这是在使用 clang 构建的 Ubuntu 14.04.03 上使用 Qt 5.7.1(同样发生在 Qt 5.8.0 和 Ubuntu 16.04)上。在调试模式下(使用 gdb)我没有得到任何有用的东西(见下文),只是我的应用程序中发生了一些事情,然后是一堆没有符号信息的 Qt3DRender 库。 Valgrind 和 Hellgrind 使应用程序变慢太多而无法使用它(这个应用程序可视化了数千个点,我无法在非常低的点数下引发问题),ASAN 和 TSAN 确实报告了问题,但没有 symbols/line我找不到任何东西。
我的设置是 main.qml
-> Scene3d
-> RenderSettings
, Entity
我使用我编写的自定义缓冲区和几何图形(PointBuffer
,PointGeometry
)我编写但未包括在内。它们分别继承自 QBuffer
和 QGeomotry
。
本质上,当计算着色器被激活时(实体上的 ComputeCommand
组件,以及 viewport
上的 DispatchCompute
),我得到上面的间歇性段错误。我添加的唯一异步组件是我的缓冲区被我自己的线程写入,我用 std::mutex
包围了它,但我认为这不是问题,因为我没有看到任何QBuffer
或 QGeometry
代码中的互斥。
着色器本身创建了一个连贯的缓冲区(layout (std430, binding = 0) coherent buffer Particles
),但除此之外只是读入并打印出点;并在我没有任何操作的情况下工作..当然直到出现段错误。
我的问题是,我的 Compute Shader 配置不当吗?或者有更好的调试方法吗?
我能追踪到的最多的问题是它只发生在几何图形的缓冲区刷新之后。
渲染设置:
RenderSettings {
property CameraSet cameraSet
property real userViewWidth: 0.79
property real topOrthoViewHeight: 0.79
activeFrameGraph: Viewport {
id: viewport
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
RenderSurfaceSelector {
ClearBuffers {
buffers: ClearBuffers.ColorDepthBuffer
NoDraw {}
}
// Compute Pass
DispatchCompute {
workGroupX: 1024; workGroupY: 1; workGroupZ: 1
TechniqueFilter {
matchAll: [
FilterKey { name: "type"; value: "compute" }
]
}
}
Viewport {
id: userViewport
normalizedRect: Qt.rect(0, 0, 0.5, 1.0)
CameraSelector {
id: userCameraSelectorViewport
camera: cameraSet.user.camera
}
}
// Second and third viewport...
}
}
}
Material (PointMaterial
)
Material {
property PointBuffer dataBuffer;
ShaderProgram {
id: computeShader
computeShaderCode: loadSource("qrc:/shaders/pointcloud.comp")
}
ShaderProgram {
id: drawShader
vertexShaderCode: loadSource("qrc:/shaders/pointcloud.vert")
fragmentShaderCode: loadSource("qrc:/shaders/pointcloud.frag")
}
effect: Effect {
techniques: [
Technique {
renderPasses: [
RenderPass {
shaderProgram: computeShader
parameters: [
// Point buffer
Parameter { name: "Particles"; value: dataBuffer }
]
}
] // renderpasses
filterKeys: [
FilterKey { name: "type"; value: "compute" }
]
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
profile: GraphicsApiFilter.CoreProfile
majorVersion: 4
minorVersion: 3
}
},
Technique {
renderPasses: [
RenderPass {
shaderProgram: drawShader
renderStates: [
PointSize { sizeMode: PointSize.Programmable }
]
parameters: [
Parameter { name: "pointSize"; value: 0.4 }
]
}
] // renderPasses
filterKeys: [
FilterKey { name: "type"; value: "draw" }
]
graphicsApiFilter {
api: GraphicsApiFilter.OpenGL
profile: GraphicsApiFilter.CoreProfile
majorVersion: 4
minorVersion: 3
}
} // technique
] // techniques
}
}
实体:
Entity {
property PointBuffer buffer: PointBuffer {
id: pointBuffer
type: Buffer.VertexBuffer
}
PointsMaterial {
id: pointsMaterial
dataBuffer: pointBuffer
}
Entity {
property GeometryRenderer pointRenderer: GeometryRenderer {
instanceCount: 1 // How many times to run this geometry, since we write
// all points in the buffer once, only call this once.
primitiveType: GeometryRenderer.Points
geometry: PointGeometry { buffer: pointBuffer }
}
// https://doc-snapshots.qt.io/qt5-5.7/qml-computecommand.html
property ComputeCommand computeCommand: ComputeCommand {
workGroupX: 1024; workGroupY: 1; workGroupZ: 1
}
components: [ pointRenderer, computeCommand, pointsMaterial ]
}
}
我试图限制发布的代码量以使问题变小,但我很乐意添加任何有助于解决问题的代码。
我的 (gdb) 调试器从主线程输出的是:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fff9e9cc700 (LWP 8208)]
0x00007ffff33e058c in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
(gdb) bt
#0 0x00007ffff33e058c in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#1 0x00007ffff33e6619 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#2 0x00007ffff338fe05 in Qt3DRender::Render::Renderer::performCompute(Qt3DRender::Render::RenderView const*, Qt3DRender::Render::RenderCommand*) () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#3 0x00007ffff3392610 in Qt3DRender::Render::Renderer::executeCommandsSubmission(Qt3DRender::Render::RenderView const*) () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#4 0x00007ffff3392b14 in Qt3DRender::Render::Renderer::submitRenderViews(QVector<Qt3DRender::Render::RenderView*> const&) () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#5 0x00007ffff3394bf2 in Qt3DRender::Render::Renderer::doRender() ()
from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt53DRender.so.5
#6 0x00007fffc723a806 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/qml/QtQuick/Scene3D/libqtquickscene3dplugin.so
#7 0x00007ffff1f56e3d in QMetaObject::activate(QObject*, int, int, void**) ()
from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Core.so.5
#8 0x00007ffff3ef865e in QQuickWindowPrivate::renderSceneGraph(QSize const&) ()
from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Quick.so.5
#9 0x00007ffff3ecabd1 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Quick.so.5
#10 0x00007ffff3ecbe58 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Quick.so.5
#11 0x00007ffff1d68539 in ?? () from /opt/Qt5.7.1/5.7/gcc_64/lib/libQt5Core.so.5
#12 0x00007ffff0eb3184 in start_thread (arg=0x7fff9e9cc700) at pthread_create.c:312
#13 0x00007ffff11c337d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
更新
很确定我已将其缩小为竞争条件。我已经删除了我认为可能会导致问题的其他库,但什么也没有。基本上,计算过滤器和异步重写缓冲区的组合会导致此段错误。我不确定是否有某种方法可以使用互斥锁来保护它。现在我正在寻找是否可以用 C++ 重写我的实体,以更好地控制计算着色器的执行方式。
我认为这里出现段错误的原因是我更新缓冲区的方式造成的。在我的 PointBuffer
中,我有一个调用 Qt3DRender::QBuffer::setData(new_data)
的异步任务,其中 new_data
是 QByteArray
.
为了设置数据,我在堆栈上创建了一个 QByteArray
,将我的数据复制到其中,然后使用 Qt3DRender::QBuffer::setData(my_qbyte_array)
这具有将我的堆栈变量暴露给应用程序其余部分的效果,删除后会导致段错误。
我改为:
Qt3DRender::QBuffer::setData(
QByteArray::fromRawData(
reinterpret_cast<const char*>(points_.linearize()),
sizeof(PointType) * pointCount
)
)
这改为指向我的原始数据(因此不需要错误副本)并且不绑定到任何临时数据。
我提到的另一种解决方案是使用 Qt 5.8.0 的新
QBuffer::updateData
docs函数。有了这个,我创建了一个本地 QByteArray
,然后将其复制到我当前的数据中。这有问题(特别是在调整大小方面)并且效率较低,但我想我还是会提到它。