Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ソフトウェアラスタライザ

Avatar for Fadis Fadis
August 22, 2026

 ソフトウェアラスタライザ

ラスタライザをソフトウェアで実装して3Dシーンのレンダリングの高速化を試みます
これは2026年8月22日に行われた Kernel/VM探検隊@東京 No19 での発表資料です
発表動画: https://youtu.be/ME1alNuvSjA
ソースコード : https://github.com/Fadis/gct/

Avatar for Fadis

Fadis

August 22, 2026

More Decks by Fadis

Other Decks in Programming

Transcript

  1. v2 y x t ≥ 0かつ t v 0 ある点が三角形の

    内側になる条件 s ≥ 0かつ s v1
  2. v2 y x t ≥ 0かつ t v 0 ある点が三角形の

    内側になる条件 s ≥ 0かつ s s+t≤1 v1
  3. n2 t n 0 np p s n1 np =

    n0 + (n1 − n0) ps + (n2 − n0) pt
  4. rasterization_state rasterization_init0( vec3 v0_view3, vec3 v1_view3, vec3 v2_view3, uint width,

    uint height ) { if( !rasterization_frustum_culling( v0_view3, v1_view3, v2_view3 ) ) { ... } vec2 v0_screen = rasterization_ndc_to_screen( v0_view3, width, height ); vec2 v1_screen = rasterization_ndc_to_screen( v1_view3, width, height ); vec2 v2_screen = rasterization_ndc_to_screen( v2_view3, width, height ); v0_screen -= vec2( 0.5f, 0.5f ); v1_screen -= vec2( 0.5f, 0.5f ); v2_screen -= vec2( 0.5f, 0.5f ); aabb2_type screen_space_bounding_box = aabb2_type( v0_screen, v0_screen ); aabb_append( screen_space_bounding_box, v1_screen ); aabb_append( screen_space_bounding_box, v2_screen ); screen_space_bounding_box = aabb_and( screen_space_bounding_box, aabb2_type( vec2( 0.0f, 0.0f ), vec2( width, height ) ) ); const ivec2 bounding_box_pixel_left_top = ivec2( ceil( screen_space_bounding_box.min ) ); const ivec2 bounding_box_pixel_count = ivec2( ceil( screen_space_bounding_box.max ) ) - bounding_box_pixel_left_top; return rasterization_state( bounding_box_pixel_left_top, bounding_box_pixel_count, vec2( 0.0, 0.0 ), 最小の四角形の範囲を求める スクリーン外のピクセルを 除外する
  5. void rasterization_init1( inout rasterization_state state ) { if( state.inv_v0w ==

    0.0 ) return; const vec2 v_ab = state.v1_screen - state.v0_screen; const vec2 v_ac = state.v2_screen - state.v0_screen; const float factor = 1.0f / rasterization_cross2( v_ab, v_ac ); state.ds_dx = v_ac.y * factor; state.ds_dy = -v_ac.x * factor; state.dt_dx = -v_ab.y * factor; state.dt_dy = v_ab.x * factor; const vec2 bounding_box_left_top_pixel_pos = vec2( state.left_top_pixel.x - state.v0_screen.x, state.left_top_pixel.y - state.v0_screen.y ); state.left_top_pixel_pos_barycentric = vec2( ( bounding_box_left_top_pixel_pos.x * v_ac.y - bounding_box_left_top_pixel_pos.y * v_ac.x ) * factor, ( v_ab.x * bounding_box_left_top_pixel_pos.y - v_ab.y * bounding_box_left_top_pixel_pos.x ) * factor ); } 重心座標系への 変換行列を求める 0番目のピクセルの重心座標系での座標pを求める
  6. rasterization_result rasterization_rasterize( rasterization_state state, uint index ) { const ivec2

    pixel_in_bounding_box = ivec2( index % state.bounding_box_size.x, index / state.bounding_box_size.x ); if( pixel_in_bounding_box.y >= state.bounding_box_size.y ) { return rasterization_result( ivec2( 0, 0 ), vec2( 0, 0 ), 0, false ); } const float s = state.left_top_pixel_pos_barycentric.x + pixel_in_bounding_box.x * state.ds_dx + pixel_in_bounding_box.y * state.ds_dy; const float t = state.left_top_pixel_pos_barycentric.y + pixel_in_bounding_box.x * state.dt_dx + pixel_in_bounding_box.y * state.dt_dy; const float v = 1.0f - ( s + t ); if( s < 0.0f || t < 0.0f || v < 0.0f ) { return rasterization_result( ivec2( 0, 0 ), vec2( 0, 0 ), 0, false ); } const float inv_w = v * state.inv_v0w + s * state.inv_v1w + t * state.inv_v2w; float w = 1.0f / inv_w; return rasterization_result( state.left_top_pixel + pixel_in_bounding_box, vec2( s, t ), w, true ); } index番目のピクセルの 重心座標系での座標pを 求める 判定
  7. float rasterization_interpolate( rasterization_state state, rasterization_result rast, float n0, float n1,

    float n2 ) { float n_ds = n1 - n0; float n_dt = n2 - n0; return n0 + rast.st.x * n_ds + rast.st.y * n_dt; } np = n0 + (n1 − n0) ps + (n2 − n0) pt
  8. −2.78 2.78 0 0 0 −1.30 0 4.94 0 0

    = 7.63 0 0 −1.02 −1 0 0 −0.20 1 7.67 1 0 0 0 0.89 −0.45 0 0.45 0.89 0 0 −6.71 0 0 0 1 1 0 0 0 0 −0.20 0.98 0 0 −0.98 −0.20 0 0 0 0 1 −1 −1 0 1 ローカル座標 透視投影行列 スクリーン座標 カメラ行列 モデル行列
  9. Perspective-Correct Interpolation Low, K., 2002. Technical writing, Department of Computer

    Science, University of North Carolina at Chapel Hill. Citeseer. https:// www.comp.nus.edu.sg/~lowkl/
  10. n v2 2 n0 np p n1 v1 Perspective-Correct Interpolation

    v0 n0 n0 n1 n2 np = n0 + − ps + − pt wp (( w1 w0 ) ( w2 w0 ) ) 頂点座標v1のw成分 頂点座標v0のw成分 頂点座標v2のw成分 頂点座標v0のw成分 pのw成分
  11. float rasterization_interpolate( rasterization_state state, rasterization_result rast, float n0, float n1,

    float n2 ) { float n_ds = n1 - n0; float n_dt = n2 - n0; return n0 + rast.st.x * n_ds + rast.st.y * n_dt; } float rasterization_perspective_correct_interpolate( rasterization_state state, rasterization_result rast, float n0, float n1, float n2 ) { n0 *= state.inv_v0w; n1 *= state.inv_v1w; n2 *= state.inv_v2w; float n_ds = n1 - n0; float n_dt = n2 - n0; return rast.w * ( n0 + rast.st.x * n_ds + rast.st.y * n_dt ); } 頂点属性の値を頂点座標のwで割る 線形補間後の値にピクセルの位置でのwを掛ける 修正前 修正後
  12. k+-buffer Per-Pixel Linked List(PPLL) k+-buffer: Fragment Synchronized k-buffer Real-time concurrent

    linked list construction on the GPU Andreas A. Vasilakis and Ioannis Fudos. 2014. K+-buffer: fragment synchronized k-buffer. In Proceedings of the 18th meeting of the ACM SIGGRAPH Symposium on Interactive 3D Graphics and Games (I3D '14). Association for Computing Machinery, New York, NY, USA, 143–150. https://doi.org/ 10.1145/2556700.2556702 Jason C. Yang, Justin Hensley, Holger Grün, and Nicolas Thibieroz. 2010. Real-time concurrent linked list construction on the GPU. In Proceedings of the 21st Eurographics conference on Rendering (EGSR'10). Eurographics Association, Goslar, DEU, 1297–1304. https:// doi.org/10.1111/j.1467-8659.2010.01725.x
  13. MeshTasksEXT 利点: 並列度を上げられる 照明0 照明1 froxel AO タスクシェーダー ュシェーダー タライザ

    ントシェーダー ーブレンド 合成 被写界深度 ブルーム 色空間の変換とガンマ レンズ
  14. 新しい情報E(深度0.57)を k+-bufferの状態 書きたいスレッド1 A B C D 深度 0.13 0.35

    0.8 0.92 A B C D 深度 0.13 0.35 0.8 0.92 ロード A B E C 更新 深度 0.13 0.35 0.57 0.8 ストア A B E C 深度 0.13 0.35 0.57 0.8
  15. 新しい情報E(深度0.57)を k+-bufferの状態 書きたいスレッド1 新しい情報F(深度0.32)を 書きたいスレッド3 A B C D 深度

    0.13 0.35 0.8 0.92 A B C D 深度 0.13 0.35 0.8 0.92 ロード A B E C 更新 深度 0.13 0.35 0.57 0.8 ロード A B C D 深度 0.13 0.35 0.8 0.92 A F B C 更新 深度 0.13 0.32 0.35 0.8 A F B C 深度 0.13 0.32 0.35 0.8 ストア A B E C 深度 0.13 0.35 0.57 0.8 ストア k+-bufferが 壊れる
  16. void main() { primitive_value p = read_primitive( ... ); if(

    p.albedo.a <= 0.0 ) discard; const uint visibility_index = instance_resource_index[ uint( input_id.x ) ].visibility; visibility_pool[ visibility_index ] = 1; const ivec2 image_pos = ivec2( gl_FragCoord.x, gl_FragCoord.y ); beginInvocationInterlockARB(); kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), image_pos, push_constants.gbuffer_format ); kplus_insert( iter, p, gl_FragCoord.z, input_id ); endInvocationInterlockARB(); } クリティカルセクション開始 k+-bufferを 更新 クリティカルセクション終了
  17. for( uint i = 0u; i != candidate_pixel_count; ++i )

    { rasterization_result rast_result = rasterization_rasterize( rast_state, i ); if( rast_result.valid ) { if( !fa_initialized ) { fa.vertex[ 0 ].position = wp0; fa.vertex[ 1 ].position = wp1; fa.vertex[ 2 ].position = wp2; fa.vertex[ 0 ].normal = ... fa.vertex[ 1 ].normal = ... fa.vertex[ 2 ].normal = ... fa.vertex[ 0 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); fa.vertex[ 1 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 1 ].tangent.xyz ).xyz, f.vertex[ 1 ].tangent.w ); fa.vertex[ 2 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 2 ].tangent.xyz ).xyz, f.vertex[ 2 ].tangent.w ); fa.vertex[ 0 ].texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); fa.vertex[ 1 ].texcoord = vec3( f.vertex[ 1 ].tex_coord0.xy, 0.0 ); fa.vertex[ 2 ].texcoord = vec3( f.vertex[ 2 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); fa_initialized = true; } rasterizable_vertex_attribute va = rasterization_perspective_correct_interpolate( rast_state, rast_result, fa, push_constants.gbuffer_format ); float depth = rasterization_interpolate( rast_state, rast_result, v0_view.z*rast_state.inv_v0w, v1_view.z*rast_state.inv_v1w, v2_view.z*rast_state.inv_v2w ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, depth, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } ラスタライズ atomic演算を使ったスピンロックで代用 頂点属性の補間 クリティカルセクション開始 k+-bufferを 更新 クリティカルセクション終了
  18. ハードウェア ソフトウェア 1 1 GeForce RTX 4090 66.048 1 GeForce

    RTX 2080 14.049 1 Radeon 610M 14.525 0 17.5 35 52.5 70 ハードウェアラスタライザを使う場合を1とした相対的な実行時間
  19. 三角形の内側にあるかもしれない for( uint i = 0u; i != candidate_pixel_count; ++i

    ) { rasterization_result rast_result = rasterization_rasterize( rast_state, i ); if( rast_result.valid ) { if( !fa_initialized ) { fa.vertex[ 0 ].position = wp0; fa.vertex[ 1 ].position = wp1; fa.vertex[ 2 ].position = wp2; fa.vertex[ 0 ].normal = ... fa.vertex[ 1 ].normal = ... fa.vertex[ 2 ].normal = ... fa.vertex[ 0 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); fa.vertex[ 1 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 1 ].tangent.xyz ).xyz, f.vertex[ 1 ].tangent.w ); fa.vertex[ 2 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 2 ].tangent.xyz ).xyz, f.vertex[ 2 ].tangent.w ); fa.vertex[ 0 ].texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); fa.vertex[ 1 ].texcoord = vec3( f.vertex[ 1 ].tex_coord0.xy, 0.0 ); fa.vertex[ 2 ].texcoord = vec3( f.vertex[ 2 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); fa_initialized = true; } rasterizable_vertex_attribute va = rasterization_perspective_correct_interpolate( rast_state, rast_result, fa, push_constants.gbuffer_format ); float depth = rasterization_interpolate( rast_state, rast_result, v0_view.z*rast_state.inv_v0w, v1_view.z*rast_state.inv_v1w, v2_view.z*rast_state.inv_v2w ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, depth, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } もしピクセルが 三角形の内側だったら ピクセルの数だけループ
  20. 1スレッド=1ピクセル 1スレッド=1三角形 スレッド0 スレッド1 スレッド2 スレッド3 スレッド4 スレッド0 ピクセル0 ピクセル1

    ピクセル2 ピクセル3 ピクセル4 ピクセル0 スレッド1 スレッド2 スレッド3 スレッド4 ピクセル1 ピクセル2 ピクセル3 ピクセル4 GPU フラグメントシェーダーで処理 GPU メッシュシェーダーで処理
  21. 1スレッド=1ピクセル 1スレッド=1三角形 スレッド0 スレッド1 スレッド2 スレッド3 スレッド4 スレッド0 スレッド1 スレッド2

    スレッド3 スレッド4 三角形0の 三角形1の 三角形2の 三角形3の 三角形4の 三角形0の 三角形1の 三角形2の 三角形3の 三角形4の ピクセル0 ピクセル0 ピクセル0 ピクセル0 ピクセル0 ピクセル0 ピクセル0 ピクセル0 ピクセル0 ピクセル0 GPU フラグメントシェーダーで処理 GPU メッシュシェーダーで処理
  22. const meshlet_reader reader = init_meshlet_reader( prim.mesh, meshlet_id ); const face_attribute

    f = read_face_attribute( reader ); const vec4 wp0 = l2w * f.vertex[ 0 ].position; const vec4 wp1 = l2w * f.vertex[ 1 ].position; const vec4 wp2 = l2w * f.vertex[ 2 ].position; const vec3 fn = normalize( cross( wp2.xyz - wp0.xyz, wp1.xyz - wp0.xyz ) ); const bool front = dot( fn.xyz, global_uniforms.eye_pos.xyz - wp0.xyz ) >= 0; bool visible = f.valid && ( prim.cull == 0 || front ); const vec4 v0_view = l2s * f.vertex[ 0 ].position; const vec4 v1_view = l2s * f.vertex[ 1 ].position; const vec4 v2_view = l2s * f.vertex[ 2 ].position; const ivec2 framebuffer_size = imageSize( image_pool_2dua_array[ push_constants.lock ] ).xy; rasterization_state rast_state = visible ? rasterization_init0( v0_view, v1_view, v2_view, framebuffer_size.x, framebuffer_size.y ) : rasterization_state( ... ); const uint candidate_pixel_count = rasterization_get_candidate_count( rast_state ); if( candidate_pixel_count == 0u ) { visible = false; } bool use_softrast = false; if( visible && candidate_pixel_count <= 1 ) { visible = false; use_softrast = true; } 頂点座標を 得る これを求める これの範囲内のピクセル数を得る
  23. const meshlet_reader reader = init_meshlet_reader( prim.mesh, meshlet_id ); const face_attribute

    f = read_face_attribute( reader ); const vec4 wp0 = l2w * f.vertex[ 0 ].position; const vec4 wp1 = l2w * f.vertex[ 1 ].position; const vec4 wp2 = l2w * f.vertex[ 2 ].position; const vec3 fn = normalize( cross( wp2.xyz - wp0.xyz, wp1.xyz - wp0.xyz ) ); const bool front = dot( fn.xyz, global_uniforms.eye_pos.xyz - wp0.xyz ) >= 0; bool visible = f.valid && ( prim.cull == 0 || front ); const vec4 v0_view = l2s * f.vertex[ 0 ].position; const vec4 v1_view = l2s * f.vertex[ 1 ].position; const vec4 v2_view = l2s * f.vertex[ 2 ].position; const ivec2 framebuffer_size = imageSize( image_pool_2dua_array[ push_constants.lock ] ).xy; rasterization_state rast_state = visible ? rasterization_init0( v0_view, v1_view, v2_view, framebuffer_size.x, framebuffer_size.y ) : rasterization_state( ... ); const uint candidate_pixel_count = rasterization_get_candidate_count( rast_state ); if( candidate_pixel_count == 0u ) { visible = false; } bool use_softrast = false; if( visible && candidate_pixel_count <= 1 ) { visible = false; use_softrast = true; } 頂点座標を 得る これを求める これの範囲内のピクセル数を得る ピクセル数0だったら以降の処理を省略
  24. const meshlet_reader reader = init_meshlet_reader( prim.mesh, meshlet_id ); const face_attribute

    f = read_face_attribute( reader ); const vec4 wp0 = l2w * f.vertex[ 0 ].position; const vec4 wp1 = l2w * f.vertex[ 1 ].position; const vec4 wp2 = l2w * f.vertex[ 2 ].position; const vec3 fn = normalize( cross( wp2.xyz - wp0.xyz, wp1.xyz - wp0.xyz ) ); const bool front = dot( fn.xyz, global_uniforms.eye_pos.xyz - wp0.xyz ) >= 0; bool visible = f.valid && ( prim.cull == 0 || front ); const vec4 v0_view = l2s * f.vertex[ 0 ].position; const vec4 v1_view = l2s * f.vertex[ 1 ].position; const vec4 v2_view = l2s * f.vertex[ 2 ].position; const ivec2 framebuffer_size = imageSize( image_pool_2dua_array[ push_constants.lock ] ).xy; rasterization_state rast_state = visible ? rasterization_init0( v0_view, v1_view, v2_view, framebuffer_size.x, framebuffer_size.y ) : rasterization_state( ... ); const uint candidate_pixel_count = rasterization_get_candidate_count( rast_state ); if( candidate_pixel_count == 0u ) { visible = false; } bool use_softrast = false; if( visible && candidate_pixel_count <= 1 ) { visible = false; use_softrast = true; } 頂点座標を 得る これを求める これの範囲内のピクセル数を得る ピクセル数0だったら以降の処理を省略 ピクセル数1だったら ソフトウェアラスタライザを使う
  25. if( use_softrast ) { rasterization_init1( rast_state ); rasterizable_face_attribute fa; vec4

    id_vec; rasterization_result rast_result = rasterization_rasterize( rast_state, 0 ); if( rast_result.valid ) { fa.vertex[ 0 ].position = wp0; fa.vertex[ 1 ].position = wp1; fa.vertex[ 2 ].position = wp2; fa.vertex[ 0 ].normal = ...; fa.vertex[ 1 ].normal = ...; fa.vertex[ 2 ].normal = ...; fa.vertex[ 0 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); fa.vertex[ 1 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 1 ].tangent.xyz ).xyz, f.vertex[ 1 ].tangent.w ); fa.vertex[ 2 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 2 ].tangent.xyz ).xyz, f.vertex[ 2 ].tangent.w ); fa.vertex[ 0 ].texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); fa.vertex[ 1 ].texcoord = vec3( f.vertex[ 1 ].tex_coord0.xy, 0.0 ); fa.vertex[ 2 ].texcoord = vec3( f.vertex[ 2 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); rasterizable_vertex_attribute va = rasterization_perspective_correct_interpolate( rast_state, rast_result, fa, push_constants.gbuffer_format ); float depth = rasterization_interpolate( rast_state, rast_result, v0_view.z*rast_state.inv_v0w, v1_view.z*rast_state.inv_v1w, v2_view.z*rast_state.inv_v2w ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, depth, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } 本当にピクセルの中心と 重なっているかチェック
  26. if( use_softrast ) { rasterization_init1( rast_state ); rasterizable_face_attribute fa; vec4

    id_vec; rasterization_result rast_result = rasterization_rasterize( rast_state, 0 ); if( rast_result.valid ) { fa.vertex[ 0 ].position = wp0; fa.vertex[ 1 ].position = wp1; fa.vertex[ 2 ].position = wp2; fa.vertex[ 0 ].normal = ...; fa.vertex[ 1 ].normal = ...; fa.vertex[ 2 ].normal = ...; fa.vertex[ 0 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); fa.vertex[ 1 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 1 ].tangent.xyz ).xyz, f.vertex[ 1 ].tangent.w ); fa.vertex[ 2 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 2 ].tangent.xyz ).xyz, f.vertex[ 2 ].tangent.w ); fa.vertex[ 0 ].texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); fa.vertex[ 1 ].texcoord = vec3( f.vertex[ 1 ].tex_coord0.xy, 0.0 ); fa.vertex[ 2 ].texcoord = vec3( f.vertex[ 2 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); rasterizable_vertex_attribute va = rasterization_perspective_correct_interpolate( rast_state, rast_result, fa, push_constants.gbuffer_format ); float depth = rasterization_interpolate( rast_state, rast_result, v0_view.z*rast_state.inv_v0w, v1_view.z*rast_state.inv_v1w, v2_view.z*rast_state.inv_v2w ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, depth, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } 重なっていたら 本当にピクセルの中心と 重なっているかチェック 頂点属性の補間 クリティカルセクション開始 k+-bufferを 更新 クリティカルセクション終了
  27. 全てハードウェア 1ピクセル以下をソフトウェア 1 1 GeForce RTX 4090 1.258 1 GeForce

    RTX 2080 1.296 1 1.017 1 Radeon 610M Radeon RX 9070 GRE 0.566 1 Arc Pro B50 0.253 0 0.35 0.7 1.05 1.4 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  28. 全てハードウェア 1ピクセル以下をソフトウェア 1 1 GeForce RTX 4090 1.258 1 GeForce

    RTX 2080 1.296 1 1.017 1 Radeon 610M Radeon RX 9070 GRE 0.566 1 Arc Pro B50 0.253 0 素直に速くなるGPU 0.35 0.7 1.05 1.4 何とかしないといけないGPU ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  29. if( use_softrast ) { rasterization_init1( rast_state ); rasterizable_face_attribute fa; vec4

    id_vec; rasterization_result rast_result = rasterization_rasterize( rast_state, 0 ); if( rast_result.valid ) { fa.vertex[ 0 ].position = wp0; fa.vertex[ 1 ].position = wp1; fa.vertex[ 2 ].position = wp2; fa.vertex[ 0 ].normal = ...; fa.vertex[ 1 ].normal = ...; fa.vertex[ 2 ].normal = ...; fa.vertex[ 0 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); fa.vertex[ 1 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 1 ].tangent.xyz ).xyz, f.vertex[ 1 ].tangent.w ); fa.vertex[ 2 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 2 ].tangent.xyz ).xyz, f.vertex[ 2 ].tangent.w ); fa.vertex[ 0 ].texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); fa.vertex[ 1 ].texcoord = vec3( f.vertex[ 1 ].tex_coord0.xy, 0.0 ); fa.vertex[ 2 ].texcoord = vec3( f.vertex[ 2 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); rasterizable_vertex_attribute va = rasterization_perspective_correct_interpolate( rast_state, rast_result, fa, push_constants.gbuffer_format ); float depth = rasterization_interpolate( rast_state, rast_result, v0_view.z*rast_state.inv_v0w, v1_view.z*rast_state.inv_v1w, v2_view.z*rast_state.inv_v2w ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, depth, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } 省略1 この部分を省略
  30. if( use_softrast ) { rasterization_init1( rast_state ); rasterizable_face_attribute fa; vec4

    id_vec; rasterization_result rast_result = rasterization_rasterize( rast_state, 0 ); if( rast_result.valid ) { fa.vertex[ 0 ].position = wp0; fa.vertex[ 1 ].position = wp1; fa.vertex[ 2 ].position = wp2; fa.vertex[ 0 ].normal = ...; fa.vertex[ 1 ].normal = ...; fa.vertex[ 2 ].normal = ...; fa.vertex[ 0 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); fa.vertex[ 1 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 1 ].tangent.xyz ).xyz, f.vertex[ 1 ].tangent.w ); fa.vertex[ 2 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 2 ].tangent.xyz ).xyz, f.vertex[ 2 ].tangent.w ); fa.vertex[ 0 ].texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); fa.vertex[ 1 ].texcoord = vec3( f.vertex[ 1 ].tex_coord0.xy, 0.0 ); fa.vertex[ 2 ].texcoord = vec3( f.vertex[ 2 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); rasterizable_vertex_attribute va = rasterization_perspective_correct_interpolate( rast_state, rast_result, fa, push_constants.gbuffer_format ); float depth = rasterization_interpolate( rast_state, rast_result, v0_view.z*rast_state.inv_v0w, v1_view.z*rast_state.inv_v1w, v2_view.z*rast_state.inv_v2w ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, depth, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } 省略2 この部分を省略
  31. if( use_softrast ) { rasterization_init1( rast_state ); rasterizable_face_attribute fa; vec4

    id_vec; rasterization_result rast_result = rasterization_rasterize( rast_state, 0 ); if( rast_result.valid ) { fa.vertex[ 0 ].position = wp0; fa.vertex[ 1 ].position = wp1; fa.vertex[ 2 ].position = wp2; fa.vertex[ 0 ].normal = ...; fa.vertex[ 1 ].normal = ...; fa.vertex[ 2 ].normal = ...; fa.vertex[ 0 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); fa.vertex[ 1 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 1 ].tangent.xyz ).xyz, f.vertex[ 1 ].tangent.w ); fa.vertex[ 2 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 2 ].tangent.xyz ).xyz, f.vertex[ 2 ].tangent.w ); fa.vertex[ 0 ].texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); fa.vertex[ 1 ].texcoord = vec3( f.vertex[ 1 ].tex_coord0.xy, 0.0 ); fa.vertex[ 2 ].texcoord = vec3( f.vertex[ 2 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); rasterizable_vertex_attribute va = rasterization_perspective_correct_interpolate( rast_state, rast_result, fa, push_constants.gbuffer_format ); float depth = rasterization_interpolate( rast_state, rast_result, v0_view.z*rast_state.inv_v0w, v1_view.z*rast_state.inv_v1w, v2_view.z*rast_state.inv_v2w ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, depth, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } 省略3 この部分と この部分を省略
  32. 全てハードウェア 1ピクセル以下をソフトウェア 省略1 省略2 省略3 1 1 GeForce RTX 4090

    1.023 1 1.258 1.135 1.194 1.296 0.942 1.019 GeForce RTX 2080 1 1.017 0.97 0.995 0.972 1 Radeon 610M 0.566 0.53 0.549 0.523 Radeon RX 9070 GRE 1 0.253 0.193 0.194 0.236 Arc Pro B50 0 1.241 0.35 0.7 1.05 1.4 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  33. 全てハードウェア 1ピクセル以下をソフトウェア 省略1 省略2 省略3 1 1 GeForce RTX 4090

    1.023 1 1.258 1.135 1.194 1.296 0.942 1.019 GeForce RTX 2080 1 1.017 0.97 0.995 0.972 1 Radeon 610M 0.566 0.53 0.549 0.523 Radeon RX 9070 GRE 計算 1 0.253 0.193 0.194 0.236 Arc Pro B50 0 1.241 計算と書き込み ロック 0.35 0.7 1.05 1.4 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  34. 1サブグループ (=32スレッド) の中に1つでも ソフトウェア ラスタライズする スレッドがあると 32スレッドの 完了が遅れる メッシュシェーダー メッシュレットをロード

    ⋯ 座標変換 ⋯ 背面カリング ⋯ 小さい三角形を判定 ⋯ ラスタライズ ⋯ k+バッファに書く ラスタライザ k+バッファに書く フラグメントシェーダー ⋯
  35. if( use_softrast ) { rasterization_init1( rast_state ); rasterizable_face_attribute fa; vec4

    id_vec; rasterization_result rast_result = rasterization_rasterize( rast_state, 0 ); if( rast_result.valid ) { fa.vertex[ 0 ].position = wp0; fa.vertex[ 1 ].position = wp1; fa.vertex[ 2 ].position = wp2; fa.vertex[ 0 ].normal = ...; fa.vertex[ 1 ].normal = ...; fa.vertex[ 2 ].normal = ...; fa.vertex[ 0 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); fa.vertex[ 1 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 1 ].tangent.xyz ).xyz, f.vertex[ 1 ].tangent.w ); fa.vertex[ 2 ].tangent = vec4( ( mat3(l2w) * f.vertex[ 2 ].tangent.xyz ).xyz, f.vertex[ 2 ].tangent.w ); fa.vertex[ 0 ].texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); fa.vertex[ 1 ].texcoord = vec3( f.vertex[ 1 ].tex_coord0.xy, 0.0 ); fa.vertex[ 2 ].texcoord = vec3( f.vertex[ 2 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); rasterizable_vertex_attribute va = rasterization_perspective_correct_interpolate( rast_state, rast_result, fa, push_constants.gbuffer_format ); float depth = rasterization_interpolate( rast_state, rast_result, v0_view.z*rast_state.inv_v0w, v1_view.z*rast_state.inv_v1w, v2_view.z*rast_state.inv_v2w ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, depth, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } この分岐の中の処理は できるだけ軽くしなければならない
  36. 三角形の内側かどうかを判定するために if( use_softrast ) { rasterizable_vertex_attribute va; vec4 id_vec; rasterization_result_simple

    rast_result = rasterization_rasterize_point( rast_state ); if( rast_result.valid ) { va.position = wp0; va.normal = fn; va.tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); va.texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, v0_view.z*rast_state.inv_v0w, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } ラスタライズはする np = n0 頂点属性の補間はしない
  37. 全てハードウェア 1ピクセル以下をソフトウェア(補間なし) 1ピクセル以下をソフトウェア(補間あり) 1 1 1.258 1.31 GeForce RTX 4090

    1 1.296 1.257 GeForce RTX 2080 1 1.017 1.017 1 Radeon 610M 0.566 0.559 Radeon RX 9070 GRE 1 0.253 Arc Pro B50 0 0.59 0.35 0.7 1.05 1.4 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  38. 全てハードウェア 1ピクセル以下をソフトウェア(補間なし) 1ピクセル以下をソフトウェア(補間あり) 1 1 1.258 1.31 GeForce RTX 4090

    1 1.296 1.257 GeForce RTX 2080 1 1.017 1.017 1 Radeon 610M 0.566 0.559 Radeon RX 9700 GRE 1 0.253 Arc Pro B50 0 0.59 0.35 0.7 1.05 これらのGPUではまだハードウェアラスタライザの方が速い 1.4
  39. if( use_softrast ) { rasterizable_vertex_attribute va; vec4 id_vec; rasterization_result_simple rast_result

    = rasterization_rasterize_point( rast_state ); if( rast_result.valid ) { va.position = wp0; va.normal = fn; va.tangent = vec4( ( mat3(l2w) * f.vertex[ 0 ].tangent.xyz ).xyz, f.vertex[ 0 ].tangent.w ); va.texcoord = vec3( f.vertex[ 0 ].tex_coord0.xy, 0.0 ); id_vec = vec4( id.inst, id.prim, meshlet_id, f.primitive_id ); primitive_value p = read_primitive( va, id_vec ); bool keep_waiting = true; while( keep_waiting ) { if( imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 1 ) ) != uint( 1 ) ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), rast_result.pixel, push_constants.gbuffer_format, 0 ); kplus_insert( iter, p, v0_view.z*rast_state.inv_v0w, id_vec ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], rast_result.pixel, uint( 0 ) ); keep_waiting = false; } } } } この関数の中で テクスチャサンプリングが行われる
  40. const mat3 its = mat3( tangent_, binormal, normal_ ); normal

    = its * ( normalize( texture( texture_pool[ nonuniformEXT(prim.normal_texture) ], vert_texcoord ).rgb * vec3( prim.normal_scale, prim.normal_scale, 1 ) * 2.0 - 1.0 ) ); } else { normal = normal_; } テクスチャサンプリング const vec3 pos = vert_position.xyz; const vec4 albedo = ( prim.base_color_texture != 0 ) ? from_color_profile( texture_metadata_pool[ prim.base_color_texture ], texture( texture_pool[ nonuniformEXT(prim.base_color_texture) ], vert_texcoord ) ) : prim.base_color; const vec3 emissive = ( prim.emissive_texture != 0 ) ? from_color_profile( texture_metadata_pool[ prim.emissive_texture ], texture( texture_pool[ nonuniformEXT(prim.emissive_texture) ], vert_texcoord ).rgb ) : prim.emissive.rgb; float metallic; float roughness; if( prim.metallic_roughness_texture != 0 ) { vec4 mr = texture( texture_pool[ nonuniformEXT(prim.metallic_roughness_texture) ], vert_texcoord ); metallic = mr.b; roughness = mr.g; } else { metallic = prim.metallic; roughness = prim.roughness; } float occlusion = ( prim.occlusion_texture != 0 ) ? mix( 1 - prim.occlusion_strength, 1, texture( texture_pool[ nonuniformEXT(prim.occlusion_texture) ], vert_texcoord ).r ) : 1.0; const vec3 optflow = calc_optflow( vert_current_screeen_pos, vert_previous_screen_pos ); テクスチャサンプリング テクスチャサンプリング テクスチャサンプリング テクスチャサンプリング
  41. メッシュシェーダー メッシュレットをロード ⋯ 座標変換 ⋯ 背面カリング ⋯ 小さい三角形を判定 ⋯ ラスタライズ

    ⋯ k+バッファに書く ラスタライザ k+バッファに書く フラグメントシェーダー ⋯ テクスチャサンプリングを行わずに 頂点属性をk+バッファに書く
  42. コンピュートシェーダー メッシュシェーダー メッシュレットをロード ⋯ 座標変換 ⋯ 背面カリング ⋯ 小さい三角形を判定 ⋯

    ラスタライズ ⋯ k+バッファに書く ラスタライザ k+バッファに書く フラグメントシェーダー ⋯
  43. コンピュートシェーダー メッシュシェーダー メッシュレットをロード ⋯ 座標変換 ⋯ 背面カリング ⋯ 小さい三角形を判定 ⋯

    ラスタライズ ⋯ k+バッファに書く ラスタライザ k+バッファに書く フラグメントシェーダー ⋯ k+バッファを完成させる
  44. imageStore( image_pool_2d_array[ nonuniformEXT( iter.image.depth ) ], ivec3( iter.image_pos, iter.depth_offset +

    order.new_sample_index - 1 ), vec4( depth, 0.0, 0.0, 0.0 ) ); if( gbuffer_has_layer( iter.active_layer, GCT_GBUFFER_POSITION_DEPTH ) ) { imageStore( image_pool_2d_array[ nonuniformEXT( iter.image.gbuffer ) ], ivec3( iter.image_pos, iter.gbuffer_offset + ( order.new_sample_index - 1 ) * layer_count + gbuffer_get_layer( iter.active_layer, GCT_GBUFFER_POSITION_DEPTH ) ), vec4( p.position.xyz, depth ) ); } if( gbuffer_has_layer( iter.active_layer, GCT_GBUFFER_NORMAL ) ) { imageStore( image_pool_2d_array[ nonuniformEXT( iter.image.gbuffer ) ], ivec3( iter.image_pos, iter.gbuffer_offset + ( order.new_sample_index - 1 ) * layer_count + gbuffer_get_layer( iter.active_layer, GCT_GBUFFER_NORMAL ) ), vec4( p.normal, input_id.z ) ); } if( gbuffer_has_layer( iter.active_layer, GCT_GBUFFER_METALLIC_ROUGHNESS_ID ) ) { imageStore( image_pool_2d_array[ nonuniformEXT( iter.image.gbuffer ) ], ivec3( iter.image_pos, iter.gbuffer_offset + ( order.new_sample_index - 1 ) * layer_count + gbuffer_get_layer( iter.active_layer, GCT_GBUFFER_METALLIC_ROUGHNESS_ID ) 深度を書く 法線マップ適用前の法線を書く
  45. image_pool_2d_array[ nonuniformEXT( iter.image.gbuffer ) ], ivec3( iter.image_pos, iter.gbuffer_offset + (

    order.new_sample_index - 1 ) * layer_count + gbuffer_get_layer( iter.active_layer, GCT_GBUFFER_OPTFLOW_MARK ) ), p.optflow ); } if( gbuffer_has_layer( iter.active_layer, GCT_GBUFFER_TANGENT ) ) { imageStore( image_pool_2d_array[ nonuniformEXT( iter.image.gbuffer ) ], ivec3( iter.image_pos, iter.gbuffer_offset + ( order.new_sample_index - 1 ) * layer_count + gbuffer_get_layer( iter.active_layer, GCT_GBUFFER_TANGENT ) ), p.tangent ); } if( gbuffer_has_layer( iter.active_layer, GCT_GBUFFER_TEXCOORD0_TEXCOORD1 ) ) { imageStore( image_pool_2d_array[ nonuniformEXT( iter.image.gbuffer ) ], ivec3( iter.image_pos, iter.gbuffer_offset + ( order.new_sample_index - 1 ) * layer_count + gbuffer_get_layer( iter.active_layer, GCT_GBUFFER_TEXCOORD0_TEXCOORD1 ) ), vec4( p.texcoord.x, p.texcoord.y, p.texcoord.z, 0.0f ) ); } 接線を書く テクスチャ座標を書く
  46. primitive_value read_primitive( uint primitive_id, vec4 vert_position, vec3 vert_normal, vec4 vert_tangent,

    vec3 vert_texcoord, vec4 vert_current_screeen_pos, vec4 vert_previous_screen_pos ) { const primitive_resource_index_type prim = primitive_resource_index[ primitive_id ]; const vec3 normal_ = normalize( vert_normal.xyz ); const vec3 tangent_ = normalize( vert_tangent.xyz ); vec3 normal; if( prim.normal_texture != 0 ) { const vec3 binormal = cross( tangent_, normal_ ) * vert_tangent.w; const mat3 its = mat3( tangent_, binormal, normal_ ); normal = its * ( normalize( textureLod( texture_pool[ nonuniformEXT(prim.normal_texture) ], vert_texcoord.xy, vert_texcoord.z ).rgb * vec3( prim.normal_scale, prim.normal_scale, 1 ) * 2.0 - 1.0 ) ); } else { normal = normal_; } const vec3 pos = vert_position.xyz; const vec4 albedo = ( prim.base_color_texture != 0 ) ? from_color_profile( texture_metadata_pool[ prim.base_color_texture ], textureLod( texture_pool[ nonuniformEXT(prim.base_color_texture) ], vert_texcoord.xy, vert_texcoord.z ) 法線マップを読んで 法線を修正
  47. normal = normal_; } const vec3 pos = vert_position.xyz; const

    vec4 albedo = ( prim.base_color_texture != 0 ) ? from_color_profile( texture_metadata_pool[ prim.base_color_texture ], textureLod( texture_pool[ nonuniformEXT(prim.base_color_texture) ], vert_texcoord.xy, vert_texcoord.z ) ) : prim.base_color; const vec3 emissive = ( prim.emissive_texture != 0 ) ? from_color_profile( texture_metadata_pool[ prim.emissive_texture ], textureLod( texture_pool[ nonuniformEXT(prim.emissive_texture) ], vert_texcoord.xy, vert_texcoord.z ).rgb ) : prim.emissive.rgb; float metallic; float roughness; if( prim.metallic_roughness_texture != 0 ) { vec4 mr = textureLod( texture_pool[ nonuniformEXT(prim.metallic_roughness_texture) ], vert_texcoord.xy, vert_texcoord.z ); metallic = mr.b; roughness = mr.g; } else { metallic = prim.metallic; roughness = prim.roughness; } テクスチャから アルベドを読む テクスチャから マテリアルを読む
  48. kplus_iter nearest_iter = iter; for( uint i = 0u; i

    != 4u; ++i ) { if( kplus_is_end( iter ) ) { break; } nearest_iter = iter; const vec4 normal_meshlet_id = kplus_get_normal_meshlet_id( iter ); const vec4 id = vec4( id_cache[ iter.layer ], normal_meshlet_id.w, 0.0f ); if( id.z != -1 ) { const vec3 texcoord = texcoord_cache[ iter.layer ].xyz; k+バッファの各ピクセルについて primitive_value p = read_primitive_excluding_albedo( uint( id.y ), kplus_get_pos_depth( iter ), normal_meshlet_id.xyz, kplus_get_tangent_face_id( iter ), texcoord.xyz, albedo_cache[ iter.layer ] ); kplus_insert_complement( iter, p, id ); テクスチャを読んで アルベド、法線、 マテリアルを求める 求めた値で k+バッファの内容を更新 } const uint visibility_index = instance_resource_index[ uint( id.x ) ].visibility; visibility_pool[ visibility_index ] = 1; iter = kplus_next( iter ); }
  49. 全てハードウェア 1ピクセル以下をソフトウェア(補間なし) 1ピクセル以下をソフトウェア(補間あり) 1ピクセル以下をソフトウェア(補間なし+遅延) 1 1 1.258 1.31 1.174 1

    1.296 1.257 1.429 1 1.017 1.017 1.077 1 GeForce RTX 4090 GeForce RTX 2080 Radeon 610M 0.566 0.559 Radeon RX 9070 GRE 0.253 Arc Pro B50 0 2.343 1 0.59 0.766 0.75 1.5 2.25 3 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  50. 全てハードウェア 1ピクセル以下をソフトウェア(補間なし) 1ピクセル以下をソフトウェア(補間あり) 1ピクセル以下をソフトウェア(補間なし+遅延) 1 1 1.258 1.31 1.174 1

    1.296 1.257 1.429 1 1.017 1.017 1.077 1 GeForce RTX 4090 GeForce RTX 2080 Radeon 610M 0.566 0.559 Radeon RX 9070 GRE 0.253 Arc Pro B50 0 このGPUは メッシュシェーダーから直接 テクスチャサンプリング した方が良い 2.343 1 0.59 0.766 0.75 1.5 2.25 3 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  51. 全てハードウェア 1ピクセル以下をソフトウェア(補間なし) 1ピクセル以下をソフトウェア(補間あり) 1ピクセル以下をソフトウェア(補間なし+遅延) 1 1 1.258 1.31 1.174 1

    1.296 1.257 1.429 1 1.017 1.017 1.077 1 GeForce RTX 4090 GeForce RTX 2080 Radeon 610M 0.566 0.559 Radeon RX 9070 GRE 0.253 Arc Pro B50 0 問題は どうやっても遅くなるこの辺 このGPUは メッシュシェーダーから直接 テクスチャサンプリング した方が良い 2.343 1 0.59 0.766 0.75 1.5 2.25 3 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  52. 補間なし+遅延 1080p 540p 270p 135p 1 1.174 1.137 1.101 1.068

    GeForce RTX 4090 1.429 1.062 0.991 0.96 1.077 1.033 0.977 0.949 GeForce RTX 2080 Radeon 610M Radeon RX 9070 GRE 0.848 0.766 0.719 0.724 0.828 Arc Pro B50 0 0.6 2.343 1.629 0.99 1.2 1.8 2.4 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  53. const meshlet_reader reader = init_meshlet_reader( prim.mesh, meshlet_id ); const face_attribute

    f = read_face_attribute( reader ); const vec4 wp0 = l2w * f.vertex[ 0 ].position; const vec4 wp1 = l2w * f.vertex[ 1 ].position; const vec4 wp2 = l2w * f.vertex[ 2 ].position; const vec3 fn = normalize( cross( wp2.xyz - wp0.xyz, wp1.xyz - wp0.xyz ) ); const bool front = dot( fn.xyz, global_uniforms.eye_pos.xyz - wp0.xyz ) >= 0; bool visible = f.valid && ( prim.cull == 0 || front ); const vec4 v0_view = l2s * f.vertex[ 0 ].position; const vec4 v1_view = l2s * f.vertex[ 1 ].position; const vec4 v2_view = l2s * f.vertex[ 2 ].position; const ivec2 framebuffer_size = imageSize( image_pool_2dua_array[ push_constants.lock ] ).xy; rasterization_state rast_state = visible ? rasterization_init0( v0_view, v1_view, v2_view, framebuffer_size.x, framebuffer_size.y ) : rasterization_state( ... ); const uint candidate_pixel_count = rasterization_get_candidate_count( rast_state ); if( candidate_pixel_count == 0u ) { visible = false; } bool use_softrast = false; if( visible && candidate_pixel_count <= 1 ) { visible = false; use_softrast = true; } ハードウェアで処理するか ソフトウェアで処理するかを 判定する処理
  54. beginInvocationInterlockARB(); bool keep_waiting = true; while( keep_waiting ) { if(

    imageAtomicExchange( image_pool_2dua[ push_constants.lock ], image_pos, 1u ) != 1u ) { kplus_iter iter = kplus_begin( kplus_image( push_constants.gbuffer, push_constants.position ), image_pos, push_constants.gbuffer_format, 0 ); kplus_insert_lazy( iter, p, gl_FragCoord.z, input_id ); imageAtomicExchange( image_pool_2dua[ push_constants.lock ], image_pos, uint( 0 ) ); keep_waiting = false; } } endInvocationInterlockARB(); メッシュシェーダーからk+バッファに書けるように 追加したロック }
  55. 補間なし+遅延 1080p 540p 270p 135p 1 1.08 0.85 1.012 1.063

    GeForce RTX 4090 0 0 GeForce RTX 2080 0 0 GPUのメモリが足りなくて計測不能 1.018 0.995 0.992 0.951 Radeon 610M Radeon RX 9070 GRE 0.99 0.848 1.629 2.343 0 0 Arc Pro B50 0 0 GPUのメモリが足りなくて計測不能 0 0.75 1.5 2.25 3 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  56. 補間なし+遅延 1080p 540p 1 1.08 0.85 1.012 1.063 GeForce RTX

    4090 270p 135p これ何とかならんか 0 0 GeForce RTX 2080 0 0 GPUのメモリが足りなくて計測不能 1.018 0.995 0.992 0.951 Radeon 610M Radeon RX 9070 GRE 0.99 0.848 0 0 Arc Pro B50 0 0 1.629 2.343 このGPUは遅延なしで速くなるので これは問題にはならない GPUのメモリが足りなくて計測不能 0 0.75 1.5 2.25 3 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間
  57. メッシュシェーダー メッシュレットをロード ⋯ 座標変換 ⋯ 背面カリング ⋯ 小さい三角形を判定 ⋯ ラスタライズ

    ⋯ k+バッファに書く ラスタライザ k+バッファに書く フラグメントシェーダー ⋯ メッシュシェーダーから k+バッファに書く データを減らす
  58. コンピュートシェーダー メッシュシェーダー メッシュレットをロード ⋯ 座標変換 ⋯ 背面カリング ⋯ 小さい三角形を判定 ⋯

    ラスタライズ ⋯ k+バッファを完成させる 展開 k+バッファに書く ラスタライザ k+バッファに書く ⋯ n20t11b1で圧縮 フラグメントシェーダー 圧 縮
  59. 48bit 法線 接線 x y x y 64bit 32bit z

    z b 圧 縮 n20t11b1 1 未満のサイズになる 3
  60. 補間なし+遅延+圧縮 1080p 540p 270p 135p 1 0.992 0.94 0.995 1.05

    GeForce RTX 4090 0 0 GeForce RTX 2080 GPUのメモリが足りなくて計測不能 0 0 0.976 0.981 0.976 0.955 Radeon 610M 0 0.3 0.6 0.9 1.2 ハードウェアラスタライザのみを使う場合を1とした相対的な実行時間