Qt Scene Graph是Qt5提出的新渲染底层,它是Qt Quick在绝大多数平台上渲染的基础(Qt4的是依赖Qt Graphics View)。了解和学习Qt的SceneGraph,怎么也不为过。最近我发现一个现象,那就是Qt Scene Graph在不同平台上的渲染流程是不一样的。通常情况下这没什么,可是它会影响Qt Quick的一些特定的类,比如说Animator。
蒋彩阳原创文章,首发地址:http://blog.csdn.net/gamesdev/article/details/48278951。欢迎同行前来探讨。
首先来认识一下Qt5.2引入的一个类(事实上Qt 5.5我才看到它),叫Animator,它的作用和Animation非常相似,除了不能反向运动、值不在中途更新等。它的原理是将动画分配到不同的渲染线程来完成,借助OpenGL的着色器设置,将渲染的压力分摊到其它的线程中了,听起来很好,不过我们在使用的过程中,和Animation类相差无几。那差别究竟在哪儿呢?下面我来制作一个小的例子,这个例子将模拟复杂的运算,然后看复杂运算究竟会不会影响Animator的操作。
首先是C++的部分,很简单的一个类,以上下文变量的方式注入QML环境中:
// TestObject.h #ifndef TESTOBJECT_H #define TESTOBJECT_H #include <QObject> class TestObject : public QObject { Q_OBJECT public: explicit TestObject(QObject *parent = 0); Q_INVOKABLE void longOperation( void ); signals: public slots: }; #endif // TESTOBJECT_H
// TestObject.cpp #include <QCoreApplication> #include "TestObject.h" TestObject::TestObject(QObject *parent) : QObject(parent) { } void TestObject::longOperation( void ) { for ( int i = 3; i < 100000; ++i ) { srand( i++ ); int output = rand( ); qDebug( "rand output is: %d", output ); } }
// main.cpp #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QtQml> #include "TestObject.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterType<TestObject>( "Test", 0, 1, "TestObject" ); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
这里使用TestObject::longOperation( )来模拟复杂的运算,然后我们在QML中找一个按钮触发这个运算:
// main.qml import QtQuick 2.3 import QtQuick.Window 2.2 import QtQuick.Controls 1.4 import Test 0.1 Window { visible: true width: 270 height: 480 Rectangle { id: rotatingBox width: 150 height: 150 color: "lightsteelblue" RotationAnimator { target: rotatingBox from: 0 to: 360 duration: 1000 loops: Animator.Infinite running: true } } Button { anchors.centerIn: parent text: "A long operation" onClicked: { to.longOperation( ); } } TestObject { id: to } }
对,我们对名为“A long operation”的按钮进行复杂运算的模拟。在按下这个按钮的时候,将会执行复杂的运算。我们首先在Windows上测试:结果是这样的:
实际运行时我们发现,点击按钮的时候,左上角的方块仍然可以转动。得益于Animator的子类RotationAnimator,在执行业务运算的时候不会影响界面的运动,不会造成界面暂停卡死。
我们将其部署在Android手机上,结果如下:
我们又使用Ubuntu进行测试,结果如下:
实际运行的时候,Animator卡死了。
那么原因究竟在哪儿呢?这和Qt内部的运行机制密切相关。虽然这一部分代码对于我们是透明的,但是它们的影响我们必须要了解。
我们在上述代码的基础上,对其环境变量设置一下,添加名为QSG_INFO这样的环境变量值,并且设置为1。如下:
然后运行程序,在Windows 8系统的控制台上显示的内容如下:
qt.scenegraph.info:threaded render loop
qt.scenegraph.info:Using sg animation driver
qt.scenegraph.info:Animation Driver: using vsync: 16.67 ms
qt.scenegraph.info:Animation Driver: using vsync: 16.67 ms
qt.scenegraph.info:texture atlas dimensions: 512x512
qt.scenegraph.info:R/G/B/A Buffers: 8 8 8 8
qt.scenegraph.info:Depth Buffer: 24
qt.scenegraph.info:Stencil Buffer: 8
qt.scenegraph.info:Samples: 0
qt.scenegraph.info:GL_VENDOR: Intel
qt.scenegraph.info:GL_RENDERER: Intel(R) HD Graphics 4600
qt.scenegraph.info:GL_VERSION: 4.3.0 - Build 10.18.14.4170
qt.scenegraph.info:GL_EXTENSIONS: GL_ARB_stencil_texturing GL_ARB_copy_imageGL_ARB_half_float_vertex GL_ARB_clear_buffer_object GL_EXT_shadow_funcsGL_ARB_arrays_of_arrays GL_NV_conditional_renderGL_ARB_shading_language_420pack GL_ARB_texture_env_crossbarGL_ARB_texture_multisample GL_ARB_separate_shader_objectsGL_EXT_geometry_shader4 GL_NV_blend_square GL_EXT_texture_integerGL_ARB_vertex_shader GL_ARB_texture_stencil8 GL_EXT_framebuffer_objectGL_EXT_blend_color GL_ARB_provoking_vertex WGL_EXT_create_context_es_profileGL_3DFX_texture_compression_FXT1 GL_EXT_draw_range_elementsGL_ARB_draw_indirect GL_ARB_texture_buffer_range GL_EXT_texture_rectangleGL_ARB_gpu_shader5 GL_EXT_multi_draw_arrays GL_NV_primitive_restartGL_EXT_clip_control GL_ARB_texture_query_levels GL_SGIS_generate_mipmapGL_EXT_texture_filter_anisotropic GL_EXT_bgra GL_ARB_vertex_attrib_64bitGL_ARB_texture_env_dot3 GL_ARB_shader_image_size GL_INTEL_map_textureGL_ARB_ES3_compatibility GL_ARB_texture_cube_map GL_NV_texgen_reflection GL_ARB_texture_rgGL_ARB_vertex_program GL_ARB_sample_shading GL_ARB_syncGL_AMD_vertex_shader_layer GL_EXT_texture_lod_bias GL_ARB_draw_buffers_blendGL_EXT_blend_equation_separate GL_EXT_abgr GL_ARB_depth_clampGL_ARB_texture_rgb10_a2ui GL_ARB_robustness GL_ARB_transform_feedback2GL_ARB_texture_gather GL_ARB_tessellation_shader GL_EXT_framebuffer_blitGL_EXT_draw_buffers2 GL_ARB_explicit_uniform_locationGL_ARB_texture_compression GL_ARB_vertex_type_2_10_10_10_revGL_EXT_separate_specular_color GL_ARB_fragment_program_shadowGL_ARB_debug_output GL_ARB_gpu_shader_fp64 GL_EXT_fog_coordGL_ARB_texture_border_clamp GL_ARB_framebuffer_objectGL_ARB_program_interface_query GL_EXT_stencil_wrap WGL_ARB_framebuffer_sRGBGL_ARB_sampler_objects GL_ARB_compressed_texture_pixel_storageWGL_EXT_depth_float GL_ARB_conservative_depth GL_EXT_texture_compression_s3tcGL_ARB_texture_env_combine GL_ARB_texture_env_add GL_ARB_vertex_array_bgraGL_EXT_texture_edge_clamp GL_ARB_base_instance GL_ATI_separate_stencilGL_ARB_fragment_program GL_ARB_texture_non_power_of_two GL_ARB_instanced_arraysGL_ARB_seamless_cube_map GL_ARB_shading_language_100GL_ARB_texture_storage_multisample GL_ARB_blend_func_extendedGL_ARB_occlusion_query GL_EXT_direct_state_access GL_EXT_clip_volume_hint GL_ARB_compute_shaderGL_ARB_framebuffer_sRGB GL_ARB_color_buffer_float GL_EXT_blend_subtractWGL_ARB_create_context GL_ARB_copy_buffer GL_ARB_shading_language_packingGL_EXT_texture_swizzle GL_INTEL_fragment_shader_ordering WGL_ARB_buffer_regionGL_ARB_texture_rectangle GL_ARB_texture_float GL_EXT_stencil_two_sideGL_ARB_transform_feedback_instanced GL_ARB_shader_bit_encodingGL_ARB_compatibility GL_ARB_fragment_shader GL_ARB_shader_image_load_storeGL_EXT_texture_env_add GL_EXT_packed_pixels GL_SUN_multi_draw_arraysGL_ARB_shader_objects GL_ARB_texture_swizzle GL_ARB_texture_buffer_object_rgb32WGL_ARB_pbuffer GL_ARB_draw_elements_base_vertex GL_EXT_texture_sRGBGL_EXT_packed_depth_stencil GL_ARB_shadow GL_EXT_framebuffer_multisampleGL_ARB_draw_instanced GL_ARB_transform_feedback3 GL_ARB_multi_draw_indirectGL_ARB_framebuffer_no_attachments GL_ARB_buffer_storageGL_EXT_blend_func_separate GL_ARB_shader_atomic_counters GL_WIN_swap_hintWGL_EXT_swap_control GL_ARB_internalformat_query GL_ARB_invalidate_subdata GL_ARB_vertex_array_objectGL_ARB_texture_compression_rgtc GL_EXT_secondary_color WGL_ARB_multisampleGL_ARB_texture_view GL_ARB_transpose_matrix GL_EXT_texture_arrayGL_EXT_gpu_program_parameters GL_ARB_pixel_buffer_objectGL_ARB_shader_subroutine GL_ARB_viewport_array GL_EXT_transform_feedbackGL_KHR_blend_equation_advanced GL_EXT_texture3D GL_ARB_multisampleWGL_EXT_create_context_es2_profile GL_EXT_texture_shared_exponentGL_ARB_map_buffer_alignment WGL_ARB_extensions_stringGL_ARB_robust_buffer_access_behavior WGL_EXT_swap_control_tearGL_ARB_multitexture GL_EXT_compiled_vertex_array GL_ARB_timer_queryGL_EXT_texture_env_combine GL_ARB_shader_storage_buffer_objectWGL_EXT_extensions_string WGL_ARB_create_context_robustnessGL_ARB_texture_query_lod GL_ARB_get_program_binary GL_ARB_occlusion_query2GL_INTEL_performance_query GL_EXT_texture_sRGB_decodeGL_ARB_fragment_layer_viewport GL_ARB_shader_precision WGL_ARB_pixel_formatGL_EXT_gpu_shader4 GL_EXT_polygon_offset_clamp WGL_ARB_pixel_format_floatGL_IBM_texture_mirrored_repeat GL_EXT_texture_storageWGL_ARB_create_context_profile WGL_ARB_make_current_readGL_ARB_vertex_attrib_binding GL_ARB_ES2_compatibility GL_ARB_geometry_shader4GL_ARB_internalformat_query2 GL_ARB_map_buffer_range WGL_NV_DX_interop GL_KHR_debugGL_ARB_window_pos GL_ARB_texture_cube_map_arrayGL_ARB_texture_mirror_clamp_to_edge GL_ARB_draw_buffers GL_SGIS_texture_lodGL_INTEL_performance_queries GL_EXT_packed_float GL_ARB_query_buffer_objectGL_AMD_vertex_shader_viewport_index GL_ARB_texture_compression_bptcGL_ARB_half_float_pixel GL_ARB_texture_storage GL_ARB_point_parametersGL_ARB_fragment_coord_conventions GL_EXT_texture_snormGL_SGIS_texture_edge_clamp GL_EXT_blend_minmax GL_ARB_cl_eventGL_EXT_rescale_normal GL_ARB_explicit_attrib_location GL_ARB_point_spriteGL_ARB_vertex_buffer_object GL_ARB_depth_buffer_floatGL_ARB_uniform_buffer_object WGL_EXT_pixel_format_packed_floatGL_ARB_depth_texture
qt.scenegraph.info:Max Texture Size: 16384
qt.scenegraph.info:Debug context: false
值得注意的是第一行:“threaded renderloop”,它标明我们使用的是QSGThreadedRenderLoop。QSGThreadedRenderLoop这个类我们是无法使用的,它位于Qt源码的“qt5\qtdeclarative\src\quick\scenegraph”中,顾名思义,它的作用是将渲染任务分发给线程进行线程渲染的。这个类在Qt 5.4的时候已经应用在Android上,Windows部分还是处于实验的阶段,因此没有被启用,Qt 5.5在Android和Windows中都已经被启用。
而我们运行Ubuntu应用的时候,得出的是:
qt.scenegraph.info: QSG: basic render loop
qt.scenegraph.info: texture atlas dimensions: 512x512
qt.scenegraph.info: R/G/B/A Buffers: 8 8 8 8
qt.scenegraph.info: Depth Buffer: 24
qt.scenegraph.info: Stencil Buffer: 8
qt.scenegraph.info: Samples: -1
qt.scenegraph.info: GL_VENDOR: Intel Open Source TechnologyCenter
qt.scenegraph.info: GL_RENDERER: Mesa DRI Intel(R) HaswellMobile
qt.scenegraph.info: GL_VERSION: 3.0 Mesa 10.5.2
qt.scenegraph.info: GL_EXTENSIONS: GL_ARB_map_buffer_rangeGL_ARB_vertex_array_bgra GL_ARB_draw_instanced GL_ARB_texture_viewGL_ARB_invalidate_subdata GL_EXT_separate_specular_colorGL_ANGLE_texture_compression_dxt3 GL_ARB_texture_env_add GL_EXT_texture_objectGL_ARB_texture_rg GL_ARB_instanced_arrays GL_EXT_gpu_program_parametersGL_EXT_texture_sRGB_decode GL_ARB_texture_gather GL_IBM_rasterpos_clipGL_EXT_blend_color GL_ATI_texture_float GL_ARB_internalformat_queryGL_ARB_depth_clamp GL_IBM_multimode_draw_arrays GL_ARB_multi_bind GL_ARB_texture_query_levelsGL_ARB_window_pos GL_NV_texture_rectangle GL_ARB_sample_shadingGL_EXT_blend_minmax GL_ARB_texture_cube_map_array GL_ARB_blend_func_extendedGL_ARB_texture_storage_multisample GL_ARB_vertex_programGL_EXT_pixel_buffer_object GL_EXT_stencil_two_side GL_NV_conditional_renderGL_EXT_texture_array GL_ARB_vertex_buffer_object GL_SGIS_generate_mipmapGL_ARB_texture_border_clamp GL_ARB_draw_buffers GL_ARB_uniform_buffer_objectGL_ARB_explicit_attrib_location GL_OES_read_format GL_ARB_fragment_shader GL_ARB_half_float_pixelGL_EXT_texture_snorm GL_EXT_bgra GL_EXT_copy_textureGL_ANGLE_texture_compression_dxt5 GL_ARB_timer_queryGL_ATI_texture_env_combine3 GL_EXT_texture_cube_map GL_NV_texgen_reflectionGL_MESA_window_pos GL_ARB_texture_compression_rgtc GL_ATI_blend_equation_separateGL_S3_s3tc GL_ARB_vertex_type_10f_11f_11f_rev GL_ARB_texture_multisampleGL_ARB_transpose_matrix GL_ARB_depth_texture GL_EXT_subtextureGL_ARB_point_parameters GL_ARB_texture_env_crossbarGL_AMD_seamless_cubemap_per_texture GL_3DFX_texture_compression_FXT1GL_EXT_texture_swizzle GL_ARB_shading_language_100 GL_EXT_textureGL_ARB_copy_image GL_EXT_abgr GL_ARB_vertex_attrib_bindingGL_ARB_texture_swizzle GL_SUN_multi_draw_arrays GL_EXT_compiled_vertex_arrayGL_ARB_pixel_buffer_object GL_ARB_shader_texture_lodGL_MESA_texture_signed_rgba GL_ARB_ES3_compatibility GL_ARB_occlusion_queryGL_APPLE_object_purgeable GL_ARB_sync GL_EXT_texture_shared_exponentGL_ARB_texture_mirror_clamp_to_edge GL_EXT_draw_range_elements GL_IBM_texture_mirrored_repeatGL_ARB_shading_language_packing GL_ARB_get_program_binaryGL_EXT_framebuffer_object GL_EXT_blend_func_separate GL_NV_primitive_restartGL_ARB_shading_language_420pack GL_EXT_vertex_array GL_EXT_framebuffer_sRGBGL_ARB_fragment_coord_conventions GL_KHR_context_flush_controlGL_AMD_shader_trinary_minmax GL_ARB_vertex_type_2_10_10_10_revGL_ARB_conservative_depth GL_EXT_texture_filter_anisotropicGL_ARB_fragment_program_shadow GL_EXT_polygon_offset_clampGL_EXT_vertex_array_bgra GL_AMD_draw_buffers_blendGL_ARB_shader_atomic_counters GL_MESA_pack_invert GL_ARB_ES2_compatibilityGL_KHR_debug GL_APPLE_packed_pixels GL_ARB_texture_env_combineGL_ARB_vertex_shader GL_EXT_shader_integer_mix GL_EXT_texture_compression_s3tcGL_SGIS_texture_edge_clamp GL_ARB_half_float_vertexGL_ARB_separate_shader_objects GL_EXT_multi_draw_arrays GL_ARB_multisampleGL_EXT_shadow_funcs GL_ARB_vertex_array_object GL_ARB_color_buffer_floatGL_EXT_framebuffer_multisample GL_ARB_provoking_vertex GL_ARB_texture_floatGL_ARB_texture_env_dot3 GL_ARB_shadow GL_ARB_texture_storageGL_EXT_secondary_color GL_ARB_texture_cube_map GL_ARB_shader_bit_encodingGL_EXT_texture_sRGB GL_EXT_texture_compression_dxt1 GL_ARB_fragment_programGL_EXT_draw_buffers2 GL_ARB_multitexture GL_EXT_texture_rectangleGL_ARB_texture_rgb10_a2ui GL_ARB_seamless_cubemap_per_textureGL_ARB_clear_texture GL_ARB_derivative_control GL_EXT_stencil_wrapGL_ARB_buffer_storage GL_ARB_debug_output GL_EXT_texture_edge_clampGL_EXT_fog_coord GL_ARB_map_buffer_alignment GL_ARB_framebuffer_objectGL_SGIS_texture_border_clamp GL_ARB_texture_compressionGL_ARB_draw_elements_base_vertex GL_ARB_texture_non_power_of_twoGL_ARB_texture_compression_bptc GL_EXT_blend_equation_separateGL_ATI_separate_stencil GL_EXT_framebuffer_blit GL_NV_packed_depth_stencilGL_ARB_draw_buffers_blend GL_NV_light_max_exponent GL_EXT_rescale_normalGL_ARB_clear_buffer_object GL_EXT_transform_feedbackGL_ARB_explicit_uniform_location GL_OES_EGL_imageGL_ARB_conditional_render_inverted GL_EXT_texture3D GL_APPLE_vertex_array_objectGL_ARB_seamless_cube_map GL_EXT_packed_pixels GL_EXT_texture_lod_biasGL_ARB_robustness GL_SGIS_texture_lod GL_NV_blend_squareGL_ARB_texture_mirrored_repeat GL_EXT_texture_env_addGL_EXT_framebuffer_multisample_blit_scaled GL_EXT_packed_floatGL_ARB_framebuffer_sRGB GL_ARB_compressed_texture_pixel_storageGL_EXT_texture_env_combine GL_AMD_conservative_depth GL_ARB_shader_objectsGL_EXT_texture_env_dot3 GL_EXT_timer_query GL_ARB_texture_rectangleGL_EXT_draw_instanced GL_EXT_texture_integer GL_ARB_point_spriteGL_ARB_occlusion_query2 GL_ARB_depth_buffer_float GL_EXT_polygon_offsetGL_ARB_sampler_objects GL_EXT_blend_subtract GL_EXT_texture_compression_rgtcGL_ARB_copy_buffer GL_EXT_packed_depth_stencil GL_EXT_point_parameters GL_NV_texture_env_combine4GL_INGR_blend_func_separate GL_ARB_texture_query_lod GL_EXT_provoking_vertexGL_NV_depth_clamp GL_ATI_draw_buffers
qt.scenegraph.info: Max Texture Size: 8192
qt.scenegraph.info: Debug context: false
第一行表明其使用的是“basic render loop”,也就是QSGRenderLoop这个类。有关系统是如何采用何种render loop的呢,这个要看环境以及需求而定。一般来说,如果显卡较好,支持多线程OpenGL渲染,那么就会启用ThreadedRenderLoop,如果是ANGLE来负责渲染,那么会使用WindowsRenderLoop,实在不行就使用BasicRenderLoop了。在QSGRenderLoop*QSGRenderLoop::instance()函数中有最全的解释。
不同的render loop在具体的渲染方法是不同的,所以我们看到,使用QSGRenderLoop,也就是被标记为basic的render loop,对Animator的处理和Animation是一样的,一旦有耗时的操作,那么界面动画就会停止,直到操作结束。