Linux では事実上標準の OpenGL の実装である mesa には OSmesa というライブラリがあり、これを使うことによってソフトウェアラスタライジング、つまり GPU なしで OpenGL を使うことができます。
同じ目的を持つ実装には他にも TransGaming 社の SwiftShader と radgametools 社の Pixomatic があります。両方とも商用製品で DirectX をターゲットとします。ちなみに SwiftShader は Chrome/FlashPlayer のソフトウェアフォールバックとして使われており、元々 swShader が元です。
OSmesa のメリットは?
CPU なしで OpenGL を使うことのメリットは制限された環境で OpenGL を使うことが出来るようになることです。例えば QuickLook はサンドボックス環境で実行されるため Apple のソフトウェア実装を含むネイティブの OpenGL を使うことができませんが、OSmesa を使うことでこの制約を超えて使うことが出来るようになります。
実際の活用例はあるの?
実装例は自分が開発した ql4pmx がありますので、実際に QuickLook で OpenGL を使う際の参考としていかがでしょうか。
使い方
まず mesa のソースコードをダウンロードし、configure または scons でビルドします。ここでは OSX のケースとして scons でビルドします。
cd $MESA_SRC_DIR
scons
ビルドすると build/darwin-x86(_64) に成果物が作られます。OSmesa を使うにはその中の mesa/libmesa.a と mesa/drivers/osmesa/libosmesa.dylib をリンクします。
LDFLAGS := -L$MESA_SRC_DIR/darwin-x86_64/mesa/libosmesa.a -L$MESA_SRC_DIR/darwin-x86_64/mesa/drivers/osmesa -lmesa -losmesa
CFLAGS := -I$MESA_SRC_DIR/include
CXXFLAGS :=-I$MESA_SRC_DIR/include
簡単な使用例(C++)を以下に示します。
#include <GL/osmesa.h>
#include <iostream>
#include <vector>
int main(int argc, char *argv[])
{
OSMesaContext context = 0;
const int width = 640, height = 480;
std::vector<uint8_t> buffer;
try {
buffer.resize(640 * 480);
context = OSMesaCreateContextExt(GL_RGBA, 24, 0, 0, 0);
if (context) {
OSMesaMakeCurrent(context, &buffer[0], GL_UNSIGNED_BYTE, width, height);
/*
* ここの間で OpenGL の各関数が利用可能になるのでなにかをする。
* OpenGL の拡張を利用する場合は OSMesaGetProcAddress を使うこと。
* OSmesa は現時点 (mesa 9.0) で OpenGL 2.1 相当の実装を持ってる。
*/
}
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
}
if (context) {
OSMesaDestroyContext(context);
}
return 0;
}
OpenGL で描画した結果は buffer に画像として出力されます。
追記(2014/2/8)
OSX 上で mesa をビルド (必ず scons を使うこと) するときは以下の変更を加えないとコンパイルを通らない。
diff --git a/include/GL/glext.h b/include/GL/glext.h
index fea9e1f..197a4da 100644
--- a/include/GL/glext.h
+++ b/include/GL/glext.h
@@ -3554,11 +3554,13 @@ GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value);
#ifndef GL_ARB_shader_objects
#define GL_ARB_shader_objects 1
+/*
#ifdef __APPLE__
typedef void *GLhandleARB;
#else
+*/
typedef unsigned int GLhandleARB;
-#endif
+/* #endif */
typedef char GLcharARB;
#define GL_PROGRAM_OBJECT_ARB 0x8B40
#define GL_SHADER_OBJECT_ARB 0x8B48
これは OSX 上では __APPLE__
が定義されるため GLhandleARB が void*
となってしまい、64bit 上では型不一致 (unsigned int != void*
) になるため。mesa が OSX 上でのビルドをサポートしないのはこの問題のためだろうと思われる。
上記の変更を加える事で mesa の追記時点での現行最新版である 10.0.3 のコンパイルが通ることを確認している。
追記2 (2014/8/10)
http://www.paraview.org/Wiki/ParaView/ParaView_And_Mesa_3D をながめていたら classic よりも高速な llvmpipe を使う OSmesa のビルド手順が出来たので追記として公開。
事前準備
事前に LLVM をインストールする必要があるので、LLVM をインストールし、llvm-config
をパスに通せるようにしておく。OSX の場合は macports か homebrew で一発インストール出来るはず。
OSX の場合は mesa3d に対して以下のパッチをあてておく。これは OSX のリンカが -Wl,--no-undefined
を認識しないため。
diff --git a/configure.ac b/configure.ac
index 2aaba61..f2704ba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -338,6 +338,8 @@ dnl
case "$host_os" in
openbsd*)
LD_NO_UNDEFINED="" ;;
+darwin*)
+ LD_NO_UNDEFINED="" ;;
*)
LD_NO_UNDEFINED="-Wl,--no-undefined" ;;
esac
ビルド
autogen で configure を生成し、configure から以下のオプションでビルドする。OSX の場合は CFLAGS
と CXXFLAGS
に -fno-common
をつけないとリンク時にコンパイルが通らないのでつける。
ここでの --prefix
はユーザ権限でインストール出来るように mesa3d 直下のディレクトリにインストールされる。
#!/bin/bash
./configure \
CXXFLAGS="-O2 -DDEFAULT_SOFTWARE_DEPTH_BITS=31" \
CFLAGS="-O2 -DDEFAULT_SOFTWARE_DEPTH_BITS=31" \
--enable-static \
--disable-shared \
--disable-xvmc \
--disable-glx \
--disable-dri \
--disable-llvm-shared-libs \
--with-dri-drivers="" \
--with-gallium-drivers="swrast" \
--enable-texture-float \
--disable-shared-glapi \
--disable-egl \
--with-egl-platforms="" \
--enable-gallium-osmesa \
--enable-gallium-llvm=yes \
--prefix=`pwd`/install-root/release
通常通り make && make install する。
OSmesa をつかう
ここでは cmake の例をかくが、Makefile でも同じことが出来るだろう。
OSmesa を静的リンクするため、llvm-config からリンクするための情報を取得し、実行時リンクにその情報を追加すると OSmesa とリンクできる。
# 以下2つを書き換えておく
set(OSMESA_INSTALL_PREFIX "/path/to/mesa3d/install-root/release")
set(LLVM_CONFIG_PATH "/path/to/llvm")
include_directories(${OSMESA_INSTALL_PREFIX}/include)
find_library(OSMESA_LIBRARY osmesa PATH ${OSMESA_INSTALL_PREFIX}/lib NO_DEFAULT_PATH)
execute_process(COMMAND ${LLVM_CONFIG_PATH}/llvm-config --libs OUTPUT_VARIABLE LLVM_CONFIG_LIBS)
string(STRIP ${LLVM_CONFIG_LIBS} LLVM_CONFIG_LIBS)
execute_process(COMMAND ${LLVM_CONFIG_PATH}/llvm-config --ldflags OUTPUT_VARIABLE LLVM_CONFIG_LDFLAGS)
string(STRIP ${LLVM_CONFIG_LDFLAGS} LLVM_CONFIG_LDFLAGS)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LLVM_CONFIG_LDFLAGS} ${LLVM_CONFIG_LIBS}")
# ビルドする時は以下のように OSmesa をリンクする
# add_executable(${PROJECT_NAME} main.cc)
# target_link_libraries(${PROJECT_NAME} ${OSMESA_LIBRARY})
ちなみに OSmesa の情報を取ると OpenGL 3.0 にそれなりに多くの拡張実装をもってきてくれる。その中でもとくに OpenGL をデバッグするために非常に重要な機能拡張である GL_ARB_debug_output
と GL_KHR_debug
が含まれている。
version: 3.0 Mesa 10.2.5 (git-a53047f)
vendor: VMware, Inc.
renderer: Gallium 0.4 on llvmpipe (LLVM 3.3, 256 bits)
extensions : GL_ARB_multisample GL_EXT_abgr GL_EXT_bgra GL_EXT_blend_color GL_EXT_blend_minmax GL_EXT_blend_subtract GL_EXT_copy_texture GL_EXT_polygon_offset GL_EXT_subtexture GL_EXT_texture_object GL_EXT_vertex_array GL_EXT_compiled_vertex_array GL_EXT_texture GL_EXT_texture3D GL_IBM_rasterpos_clip GL_ARB_point_parameters GL_EXT_draw_range_elements GL_EXT_packed_pixels GL_EXT_point_parameters GL_EXT_rescale_normal GL_EXT_separate_specular_color GL_EXT_texture_edge_clamp GL_SGIS_generate_mipmap GL_SGIS_texture_border_clamp GL_SGIS_texture_edge_clamp GL_SGIS_texture_lod GL_ARB_framebuffer_sRGB GL_ARB_multitexture GL_EXT_framebuffer_sRGB GL_IBM_multimode_draw_arrays GL_IBM_texture_mirrored_repeat GL_ARB_texture_cube_map GL_ARB_texture_env_add GL_ARB_transpose_matrix GL_EXT_blend_func_separate GL_EXT_fog_coord GL_EXT_multi_draw_arrays GL_EXT_secondary_color GL_EXT_texture_env_add GL_EXT_texture_lod_bias GL_INGR_blend_func_separate GL_NV_blend_square GL_NV_light_max_exponent GL_NV_texgen_reflection GL_NV_texture_env_combine4 GL_SUN_multi_draw_arrays GL_ARB_texture_border_clamp GL_ARB_texture_compression GL_EXT_framebuffer_object GL_EXT_texture_env_combine GL_EXT_texture_env_dot3 GL_MESA_window_pos GL_NV_packed_depth_stencil GL_NV_texture_rectangle GL_ARB_depth_texture GL_ARB_occlusion_query GL_ARB_shadow GL_ARB_texture_env_combine GL_ARB_texture_env_crossbar GL_ARB_texture_env_dot3 GL_ARB_texture_mirrored_repeat GL_ARB_window_pos GL_EXT_stencil_two_side GL_EXT_texture_cube_map GL_NV_depth_clamp GL_NV_fog_distance GL_APPLE_packed_pixels GL_APPLE_vertex_array_object GL_ARB_draw_buffers GL_ARB_fragment_program GL_ARB_fragment_shader GL_ARB_shader_objects GL_ARB_vertex_program GL_ARB_vertex_shader GL_ATI_draw_buffers GL_ATI_texture_env_combine3 GL_ATI_texture_float GL_EXT_shadow_funcs GL_EXT_stencil_wrap GL_MESA_pack_invert GL_MESA_ycbcr_texture GL_NV_primitive_restart GL_ARB_depth_clamp GL_ARB_fragment_program_shadow GL_ARB_half_float_pixel GL_ARB_occlusion_query2 GL_ARB_point_sprite GL_ARB_shading_language_100 GL_ARB_sync GL_ARB_texture_non_power_of_two GL_ARB_vertex_buffer_object GL_ATI_blend_equation_separate GL_EXT_blend_equation_separate GL_OES_read_format GL_ARB_color_buffer_float GL_ARB_pixel_buffer_object GL_ARB_texture_compression_rgtc GL_ARB_texture_float GL_ARB_texture_rectangle GL_ATI_texture_compression_3dc GL_EXT_packed_float GL_EXT_pixel_buffer_object GL_EXT_texture_compression_rgtc GL_EXT_texture_mirror_clamp GL_EXT_texture_rectangle GL_EXT_texture_sRGB GL_EXT_texture_shared_exponent GL_ARB_framebuffer_object GL_EXT_framebuffer_blit GL_EXT_framebuffer_multisample GL_EXT_packed_depth_stencil GL_ARB_vertex_array_object GL_ATI_separate_stencil GL_ATI_texture_mirror_once GL_EXT_draw_buffers2 GL_EXT_draw_instanced GL_EXT_gpu_program_parameters GL_EXT_texture_array GL_EXT_texture_compression_latc GL_EXT_texture_integer GL_EXT_texture_sRGB_decode GL_EXT_timer_query GL_OES_EGL_image GL_ARB_copy_buffer GL_ARB_depth_buffer_float GL_ARB_draw_instanced GL_ARB_half_float_vertex GL_ARB_instanced_arrays GL_ARB_map_buffer_range GL_ARB_texture_rg GL_ARB_texture_swizzle GL_ARB_vertex_array_bgra GL_EXT_texture_swizzle GL_EXT_vertex_array_bgra GL_NV_conditional_render GL_AMD_conservative_depth GL_AMD_draw_buffers_blend GL_AMD_seamless_cubemap_per_texture GL_ARB_ES2_compatibility GL_ARB_blend_func_extended GL_ARB_debug_output GL_ARB_draw_buffers_blend GL_ARB_draw_elements_base_vertex GL_ARB_explicit_attrib_location GL_ARB_fragment_coord_conventions GL_ARB_provoking_vertex GL_ARB_sampler_objects GL_ARB_seamless_cube_map GL_ARB_shader_texture_lod GL_ARB_texture_multisample GL_ARB_texture_rgb10_a2ui GL_ARB_uniform_buffer_object GL_ARB_vertex_type_2_10_10_10_rev GL_EXT_provoking_vertex GL_EXT_texture_snorm GL_MESA_texture_signed_rgba GL_ARB_get_program_binary GL_ARB_robustness GL_ARB_separate_shader_objects GL_ARB_shader_bit_encoding GL_ARB_timer_query GL_ARB_transform_feedback2 GL_ARB_transform_feedback3 GL_NV_vdpau_interop GL_ARB_conservative_depth GL_ARB_internalformat_query GL_ARB_map_buffer_alignment GL_ARB_shading_language_420pack GL_ARB_shading_language_packing GL_ARB_texture_storage GL_ARB_transform_feedback_instanced GL_EXT_framebuffer_multisample_blit_scaled GL_EXT_transform_feedback GL_AMD_shader_trinary_minmax GL_ARB_clear_buffer_object GL_ARB_invalidate_subdata GL_ARB_texture_storage_multisample GL_ARB_vertex_attrib_binding GL_KHR_debug GL_ARB_multi_bind GL_ARB_texture_mirror_clamp_to_edge GL_ARB_vertex_type_10f_11f_11f_rev GL_EXT_shader_integer_mix