使用QML中的计算着色器实现段错误

时间:2017-01-31 23:05:34

标签: qt qml compute-shader

如何在大多数QML应用程序中追踪seg故障?

具体错误是:*** 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会报告问题,但没有符号/行数字我无法跟踪任何事情。

我的设置为main.qml - > Scene3d - > RenderSettingsEntity

我使用自己编写的自定义缓冲区和几何体(PointBufferPointGeometry)我写了但没有包含。它们分别从QBufferQGeomotry继承。

基本上,当激活计算着色器时(实体上的ComputeCommand组件和DispatchCompute上的viewport),我会得到上面的间歇性seg故障。我添加的唯一异步组件是我的缓冲区由我自己的线程写入,我用std::mutex包围了这个,但我不认为这是问题所在我在QBufferQGeometry代码中看不到任何互斥锁。

着色器本身会创建一个连贯的缓冲区(layout (std430, binding = 0) coherent buffer Particles),否则只需读入并打印出这些点;当我没有任何操作时工作......当然直到seg故障。

我的问题是,我是否配置了计算着色器?或者有更好的方法来调试它吗?

我能追踪的最多问题是它只发生在几何体上缓冲区刷新之后。

RenderSettings

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...
        }
    }
}

材料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 ]
    }
}

我试图限制我发布的代码量以保持问题的小,但我很乐意添加任何其他内容,如果它有助于解决问题。

主线程的My(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

更新

非常确定我已将此缩小到竞争状态。我已经淘汰了我正在使用的其他库,我认为这可能导致问题,而且没有。基本上,计算过滤器和异步重写的缓冲区的组合会导致此seg错误。我不确定是否有某种方法可以使用互斥锁来保护它。现在我想看看我是否可以用C ++重写我的实体,以便更好地控制计算着色器的执行方式。

1 个答案:

答案 0 :(得分:0)

我认为这里的seg错误的原因是我更新缓冲区的结果。在我的PointBuffer中,我有一个异步任务,它调用Qt3DRender::QBuffer::setData(new_data),其中new_dataQByteArray

要设置数据,我在堆栈上创建QByteArray,将数据复制到其中,然后使用Qt3DRender::QBuffer::setData(my_qbyte_array)

设置数据

这会将我的堆栈变量暴露给应用程序的其余部分,删除时会导致seg错误。

相反,我改为:

Qt3DRender::QBuffer::setData(
   QByteArray::fromRawData(
      reinterpret_cast<const char*>(points_.linearize()),
      sizeof(PointType) * pointCount
   )
)

这反而指向我的原始数据(因此不需要bug拷贝)并且不会绑定到任何临时数据。

我提到的另一个解决方案,我使用的是Qt 5.8.0的新版本 QBuffer::updateData docs 函数。有了这个,我创建了一个本地QByteArray,但随后将其复制到我当前的数据中。这有问题(特别是尺寸)并且效率较低,但我想我还是会提到它。