優れたSokol GFXライブラリを介して、最新のグラフィックスAPIを使用して、純粋なCの最小効率クロスプラットフォーム2Dグラフィック画家。
Sokol GP、またはSGPはSokol Graphics Painterの略です。
Sokol GFXは、最新のグラフィックスカードの固定されていないパイプラインを使用してレンダリングするための優れたライブラリですが、単純な2D図面には複雑すぎるため、APIは一般的すぎて3Dレンダリングに特化しています。 2Dスタッフを描くには、プログラマーは通常、Sokol GFXを使用するときにカスタムシェーダーをセットアップするか、Sokol GL Extra Libraryを使用する必要がありますが、Sokol GLには3Dデザインを念頭に置いたAPIもあり、コストと制限があります。
このライブラリは、Sokol GFXを介して2Dプリミティブを簡単に描画するために作成されました。3D使用量を考慮しないことにより、2Dレンダリングのみに最適化され、さらに自動バッチオプティマイザーを備えており、詳細については以下に説明します。
図書館を描画すると、まだ描画されていないすべてのプリミティブの描画コマンドキューを作成すると、新しい描画コマンドが追加されるたびに、バッチオプティマイザーが最近の8つのドローコマンド(これが調整可能)を調べ、次の基準を満たす以前の描画コマンドを見つけた場合は、描画コマンドを再配置してマージしようとします。
これを行うことにより、バッチオプティマイザーは、たとえば、テクスチャドローコールをマージすることができます。描画時の効率はより効率的です。
このライブラリは、実行時に舞台裏でドローコールを自動的にマージすることにより、効率的な2D描画バッチシステムを作成する多くの作業を回避できます。したがって、プログラマーはバッチ付きドローコールを手動で管理する必要はありません。また、バッチ付きテクスチャドローコールを並べ替える必要もありません。
バッチアルゴリズムは高速ですが、追加されるすべての新しい描画コマンドのO(n) CPUの複雑さがあります。ここで、 nはSGP_BATCH_OPTIMIZER_DEPTH構成です。デフォルトとして8使用する実験では、デフォルトは適切ですが、ケースに応じて異なる値を試してみることができます。アルゴリズムは以前の描画コマンドをスキャンする長すぎて、より多くのCPUリソースを消費する可能性があるため、高すぎる値を使用することは推奨されません。
SGP_BATCH_OPTIMIZER_DEPTH 0に設定することにより、バッチオプティマイザーを無効にすることができます。それを使用してその影響を測定できます。
このリポジトリのサンプルディレクトリには、バスオプティマイザーが有効/無効になって描画をテストするベンチマークの例があります。私のマシンでは、そのベンチマークが有効になったときに2.2倍の要因でパフォーマンスを向上させることができました。一部のプライベートゲームプロジェクトでは、バッチオプティマイザーの利益は、グラフィックスバックエンドをこのライブラリに置き換えるだけでFPSのパフォーマンスを1.5倍以上増加させることが証明されました。ゲーム自体に内部変更はありません。
ライブラリには、パフォーマンスを念頭に置いたいくつかのデザインの選択肢があり、ここで簡単に説明します。
Sokol GFXと同様に、Sokol GPはDraw Loopで割り当てを行うことはありません。したがって、初期化するときは、DrawコマンドキューバッファーとVerticesバッファの最大サイズを事前に構成する必要があります。
すべての2Dスペース変換( sgp_rotateのような機能)はCPUによって行われ、GPUによって行われます。これは、GPUに追加のオーバーヘッドを追加することを意図的に避けるためです。通常、2Dアプリケーションの頂点の数はそれほど大きくなく、GPUの右側ではなく、GPUを使用するよりもGPUを使用するのではなく、GPUを右に延長するのではなく、GPUを使用するのではなく、すべての変換を実行する方が効率的です。 CPU <-> GPUバス。対照的に、3Dアプリケーションは通常、頂点シェーダーを使用してGPUへの頂点変換を頂点に発送します。3Dオブジェクトの頂点の量が非常に大きく、通常は最良の選択であるため、これを行いますが、これは2Dレンダリングには当てはまりません。
翻訳、回転、スケールなど、プリミティブを描画する前に2Dスペースを変換する多くのAPIが利用可能です。 3DグラフィックスAPIで利用可能なものと同様に使用できますが、2Dのみで作成されます。たとえば、2Dを使用する場合は、4x4または3x3マトリックスを使用して頂点変換を実行する必要はありません。代わりにコードは2Dに特化しており、2x3マトリックスを使用できます。
テクスチャされていないプリミティブを描くときでさえ、すべてのパイプラインは常にそれに関連するテクスチャを使用します。これにより、テクスチャコールとテクスチャーコールを混合して効率が向上するときにグラフィックスパイプラインの変化を最小限に抑えるためです。
ライブラリはソコルGFXヘッダーのスタイルでコーディングされており、そこから多くのマクロを再利用すると、カスタムアロケーター、カスタムログ関数、その他の詳細などのセマンティクスのいくつかを変更できます。その詳細については、 sokol_gfx.hドキュメントをお読みください。
sokol_gp.hと他のSokolヘッダーと同じフォルダーにコピーします。通常どおりSokol GFXをセットアップし、SG_SETUP sgp_setup(desc) sg_setup(desc) )に呼び出しを追加し、sg_shutdown sgp_shutdown() sg_shutdown() )を呼び出します。通常、SGPがsgp_is_valid()で作成された後にSGPが有効であるかどうかを確認し、そうでない場合はエラーで優雅に終了する必要があることに注意してください。
フレームドロー機能では、SGP描画機能を呼び出す前に、 sgp_begin(width, height)を追加してから、プリミティブを描画します。フレーム(またはフレームバッファー)の終わりには、Sokol GFX Begin/End Render Passの間で常にsgp_flush()を呼び出す必要があります。SGP_FLUSH sgp_flush()は、すべての描画コマンドをソコルGFXにディスパッチします。次に、すぐにsgp_end()を呼び出して、描画コマンドキューを破棄します。
このセットアップの実際の例を以下に示します。
以下は、Sokol GFXおよびSokolアプリを使用したこのライブラリの方法に関する簡単な例です。
// This is an example on how to set up and use Sokol GP to draw a filled rectangle.
// Includes Sokol GFX, Sokol GP and Sokol APP, doing all implementations.
#define SOKOL_IMPL
#include "sokol_gfx.h"
#include "sokol_gp.h"
#include "sokol_app.h"
#include "sokol_glue.h"
#include "sokol_log.h"
#include <stdio.h> // for fprintf()
#include <stdlib.h> // for exit()
#include <math.h> // for sinf() and cosf()
// Called on every frame of the application.
static void frame ( void ) {
// Get current window size.
int width = sapp_width (), height = sapp_height ();
float ratio = width /( float ) height ;
// Begin recording draw commands for a frame buffer of size (width, height).
sgp_begin ( width , height );
// Set frame buffer drawing region to (0,0,width,height).
sgp_viewport ( 0 , 0 , width , height );
// Set drawing coordinate space to (left=-ratio, right=ratio, top=1, bottom=-1).
sgp_project ( - ratio , ratio , 1.0f , -1.0f );
// Clear the frame buffer.
sgp_set_color ( 0.1f , 0.1f , 0.1f , 1.0f );
sgp_clear ();
// Draw an animated rectangle that rotates and changes its colors.
float time = sapp_frame_count () * sapp_frame_duration ();
float r = sinf ( time ) * 0.5 + 0.5 , g = cosf ( time ) * 0.5 + 0.5 ;
sgp_set_color ( r , g , 0.3f , 1.0f );
sgp_rotate_at ( time , 0.0f , 0.0f );
sgp_draw_filled_rect ( -0.5f , -0.5f , 1.0f , 1.0f );
// Begin a render pass.
sg_pass pass = {. swapchain = sglue_swapchain ()};
sg_begin_pass ( & pass );
// Dispatch all draw commands to Sokol GFX.
sgp_flush ();
// Finish a draw command queue, clearing it.
sgp_end ();
// End render pass.
sg_end_pass ();
// Commit Sokol render.
sg_commit ();
}
// Called when the application is initializing.
static void init ( void ) {
// Initialize Sokol GFX.
sg_desc sgdesc = {
. environment = sglue_environment (),
. logger . func = slog_func
};
sg_setup ( & sgdesc );
if (! sg_isvalid ()) {
fprintf ( stderr , "Failed to create Sokol GFX context!n" );
exit ( -1 );
}
// Initialize Sokol GP, adjust the size of command buffers for your own use.
sgp_desc sgpdesc = { 0 };
sgp_setup ( & sgpdesc );
if (! sgp_is_valid ()) {
fprintf ( stderr , "Failed to create Sokol GP context: %sn" , sgp_get_error_message ( sgp_get_last_error ()));
exit ( -1 );
}
}
// Called when the application is shutting down.
static void cleanup ( void ) {
// Cleanup Sokol GP and Sokol GFX resources.
sgp_shutdown ();
sg_shutdown ();
}
// Implement application main through Sokol APP.
sapp_desc sokol_main ( int argc , char * argv []) {
( void ) argc ;
( void ) argv ;
return ( sapp_desc ){
. init_cb = init ,
. frame_cb = frame ,
. cleanup_cb = cleanup ,
. window_title = "Rectangle (Sokol GP)" ,
. logger . func = slog_func ,
};
}この例を実行するには、最初にsokol_gp.hヘッダーを他のSOKOLヘッダーと同じフォルダーにコピーし、適切なリンクフラグを使用して任意のCコンパイラとコンパイルします( sokol_gfx.hを読みます)。
フォルダーのsamplesでは、ライブラリのすべてのAPIをカバーする次の完全な例を見つけることができます。
sgp_begin()を使用する方法を示す例です。これらの例は、ライブラリのテストスイートとして使用されており、 makeを入力することでそれらを構築できます。
多くのドローコール後、コマンドまたは頂点バッファーがオーバーフローする可能性があります。その場合、ライブラリはエラーエラー状態を設定し、普通に動作し続けますが、 sgp_flush()で描画コマンドキューをフラッシングすると、描画コマンドは発送されません。これは、ライブラリが事前に割り当てられたバッファーを使用しているために発生する可能性があります。そのような場合、 sgp_setup()を呼び出すときにプレフィックス付きコマンドキューバッファーと頂点バッファーを増やすことで問題を修正できます。
sgp_push_transform()とsgp_pop_transform()のプッシュ/ポップの数を無効にするか、 sgp_begin()とsgp_end()をネストすることもエラーにつながる可能性があります。
このような場合、 SOKOL_DEBUG Macroを有効にして、 sgp_end()を呼び出した後にsgp_get_last_error()読むことでプログラムでエラーをデバッグしたり、エラーを処理できます。また、Sokolで開発する際にSOKOL_DEBUG有効にすることをお勧めします。そのため、早めに間違いをキャッチできます。
ライブラリは、2Dで使用される最も一般的なブレンドモードをサポートしています。これは次のとおりです。
SGP_BLENDMODE_NONEブレンドなし( dstRGBA = srcRGBA )。SGP_BLENDMODE_BLEND -ALPHA BLENDING( dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))およびdstA = srcA + (dstA * (1-srcA)) )SGP_BLENDMODE_BLEND_PREMULTIPLIED -PREMULTIPRIED ALPHA BLENDING( dstRGBA = srcRGBA + (dstRGBA * (1-srcA)) )SGP_BLENDMODE_ADD添加剤ブレンド( dstRGB = (srcRGB * srcA) + dstRGBおよびdstA = dstA )SGP_BLENDMODE_ADD_PREMULTIPLIED事前に拡大する添加剤ブレンド( dstRGB = srcRGB + dstRGBおよびdstA = dstA )SGP_BLENDMODE_MOD -color mosulate( dstRGB = srcRGB * dstRGBおよびdstA = dstA )SGP_BLENDMODE_MUL -COLOR MULTIPLY( dstRGB = (srcRGB * dstRGB) + (dstRGB * (1-srcA))およびdstA = (srcA * dstA) + (dstA * (1-srcA)) ) sgp_viewport(x, y, width, height)を呼び出すことで、画面領域を描画することができます。 sgp_project(left, right, top, bottom)を呼び出すことにより、2Dスペースの座標系を変更できます。
sgp_translate(x, y) 、 sgp_rotate(theta)など、ライブラリが提供する変換関数を使用して、ドローコールの前に2Dスペースを翻訳、回転、またはスケーリングできます。チートシートまたはヘッダーにさらに確認できます。
変換状態を保存および復元するにはsgp_push_transform()および後のsgp_pop_transform()を呼び出す必要があります。
ライブラリは、すべての基本的なプリミティブ、つまり、 sgp_draw_line()やsgp_draw_filled_rect()などのポイント、線、三角形、長方形の描画関数を提供します。チートシートまたはヘッダーを詳細に確認してください。それらはすべてバッチのバリエーションを持っています。
テクスチャの長方形を描画するにはsgp_set_image(0, img)を使用してからsgp_draw_filled_rect()使用できます。これにより、テクスチャ全体が長方形に引き付けられます。後でsgp_reset_image(0)で画像をリセットして、バインドされた画像をデフォルトの白い画像に復元する必要があります。そうしないと、しっかりした色を描くときにグリッチがあります。
テクスチャから特定のソースを描画する場合は、代わりにsgp_draw_textured_rect()を使用する必要があります。
デフォルトでは、テクスチャを使用して単純な最寄りのフィルターサンプラーを使用してテクスチャが描画されます。SGP_SET_SAMPLER sgp_set_sampler(0, smp)を使用してサンプラーを変更する前に、 sgp_reset_sampler(0)を使用してデフォルトサンプラーを復元することをお勧めします。
すべての一般的なパイプラインには色の変調があり、 sgp_set_color(r,g,b,a)で現在の状態の色を設定することで描画の前に色を変調できます。後で、 sgp_reset_color()でデフォルト(白)に色をリセットする必要があります。
カスタムシェーダーを使用する場合、 sgp_make_pipeline(desc)を使用してパイプラインを作成する必要があります。シェーダー、ブレンドモード、およびそれに関連するプリミティブを描画する必要があります。次に、シェーダーを描画する前に、 sgp_set_pipeline()を呼び出す必要があります。同じブレンドモードを使用し、作成されたパイプラインとプリミティブを描画する責任があります。
カスタムユニフォームはsgp_set_uniform(vs_data, vs_size, fs_data, fs_size)を使用してシェーダーに渡すことができます。ここでは、頂点とフラグメントシェーダーで定義されたものとまったく同じスキーマとサイズの構造体にポインターを渡す必要があります。
各グラフィックバックエンドのカスタムシェーダーを手動で作成できますが、単一の.glslファイルから複数のバックエンドのシェーダーを生成できるため、SokolシェーダーコンパイラSHDCを使用する必要があることをお勧めします。これは通常うまく機能します。
デフォルトでは、ドローコールごとにライブラリの均一バッファーには8つのフロートユニフォーム( SGP_UNIFORM_CONTENT_SLOTS構成)があり、カスタムシェーダーで使用するには低すぎる場合があります。これはデフォルトです。通常、新人はカスタム2Dシェーダーを使用したくない場合があり、より大きな値を増やすことはより頭上を意味するためです。カスタムシェーダーを使用している場合は、最大のシェーダーのユニフォームの数を保持するのに十分な大きさにこの値を増やしてください。
次のマクロは、ライブラリの動作を変更する前に定義できます。
SGP_BATCH_OPTIMIZER_DEPTHバッチオプティマイザーが振り返る描画コマンドの数。デフォルトは8です。SGP_UNIFORM_CONTENT_SLOTS各ドローコールの均一バッファーに保存できるフロートの最大数。デフォルトは8です。SGP_TEXTURE_SLOTSドローコールごとにバインドできるテクスチャの最大数。デフォルトは4です。 MIT、ライセンスファイルまたはsokol_gp.hファイルの最後を参照してください。
迅速な参照のために、すべてのライブラリ関数のクイックリストを次に示します。
/* Initialization and de-initialization. */
void sgp_setup ( const sgp_desc * desc ); /* Initializes the SGP context, and should be called after `sg_setup`. */
void sgp_shutdown ( void ); /* Destroys the SGP context. */
bool sgp_is_valid ( void ); /* Checks if SGP context is valid, should be checked after `sgp_setup`. */
/* Error handling. */
sgp_error sgp_get_last_error ( void ); /* Returns last SGP error. */
const char * sgp_get_error_message ( sgp_error error ); /* Returns a message with SGP error description. */
/* Custom pipeline creation. */
sg_pipeline sgp_make_pipeline ( const sgp_pipeline_desc * desc ); /* Creates a custom shader pipeline to be used with SGP. */
/* Draw command queue management. */
void sgp_begin ( int width , int height ); /* Begins a new SGP draw command queue. */
void sgp_flush ( void ); /* Dispatch current Sokol GFX draw commands. */
void sgp_end ( void ); /* End current draw command queue, discarding it. */
/* 2D coordinate space projection */
void sgp_project ( float left , float right , float top , float bottom ); /* Set the coordinate space boundary in the current viewport. */
void sgp_reset_project ( void ); /* Resets the coordinate space to default (coordinate of the viewport). */
/* 2D coordinate space transformation. */
void sgp_push_transform ( void ); /* Saves current transform matrix, to be restored later with a pop. */
void sgp_pop_transform ( void ); /* Restore transform matrix to the same value of the last push. */
void sgp_reset_transform ( void ); /* Resets the transform matrix to identity (no transform). */
void sgp_translate ( float x , float y ); /* Translates the 2D coordinate space. */
void sgp_rotate ( float theta ); /* Rotates the 2D coordinate space around the origin. */
void sgp_rotate_at ( float theta , float x , float y ); /* Rotates the 2D coordinate space around a point. */
void sgp_scale ( float sx , float sy ); /* Scales the 2D coordinate space around the origin. */
void sgp_scale_at ( float sx , float sy , float x , float y ); /* Scales the 2D coordinate space around a point. */
/* State change for custom pipelines. */
void sgp_set_pipeline ( sg_pipeline pipeline ); /* Sets current draw pipeline. */
void sgp_reset_pipeline ( void ); /* Resets to the current draw pipeline to default (builtin pipelines). */
void sgp_set_uniform ( const void * vs_data , uint32_t vs_size , const void * fs_data , uint32_t fs_size ); /* Sets uniform buffer for a custom pipeline. */
void sgp_reset_uniform ( void ); /* Resets uniform buffer to default (current state color). */
/* State change functions for the common pipelines. */
void sgp_set_blend_mode ( sgp_blend_mode blend_mode ); /* Sets current blend mode. */
void sgp_reset_blend_mode ( void ); /* Resets current blend mode to default (no blending). */
void sgp_set_color ( float r , float g , float b , float a ); /* Sets current color modulation. */
void sgp_reset_color ( void ); /* Resets current color modulation to default (white). */
void sgp_set_image ( int channel , sg_image image ); /* Sets current bound image in a texture channel. */
void sgp_unset_image ( int channel ); /* Remove current bound image in a texture channel (no texture). */
void sgp_reset_image ( int channel ); /* Resets current bound image in a texture channel to default (white texture). */
void sgp_set_sampler ( int channel , sg_sampler sampler ); /* Sets current bound sampler in a texture channel. */
void sgp_unset_sampler ( int channel ); /* Remove current bound sampler in a texture channel (no sampler). */
void sgp_reset_sampler ( int channel ); /* Resets current bound sampler in a texture channel to default (nearest sampler). */
/* State change functions for all pipelines. */
void sgp_viewport ( int x , int y , int w , int h ); /* Sets the screen area to draw into. */
void sgp_reset_viewport ( void ); /* Reset viewport to default values (0, 0, width, height). */
void sgp_scissor ( int x , int y , int w , int h ); /* Set clip rectangle in the viewport. */
void sgp_reset_scissor ( void ); /* Resets clip rectangle to default (viewport bounds). */
void sgp_reset_state ( void ); /* Reset all state to default values. */
/* Drawing functions. */
void sgp_clear ( void ); /* Clears the current viewport using the current state color. */
void sgp_draw ( sg_primitive_type primitive_type , const sgp_vertex * vertices , uint32_t count ); /* Low level drawing function, capable of drawing any primitive. */
void sgp_draw_points ( const sgp_point * points , uint32_t count ); /* Draws points in a batch. */
void sgp_draw_point ( float x , float y ); /* Draws a single point. */
void sgp_draw_lines ( const sgp_line * lines , uint32_t count ); /* Draws lines in a batch. */
void sgp_draw_line ( float ax , float ay , float bx , float by ); /* Draws a single line. */
void sgp_draw_lines_strip ( const sgp_point * points , uint32_t count ); /* Draws a strip of lines. */
void sgp_draw_filled_triangles ( const sgp_triangle * triangles , uint32_t count ); /* Draws triangles in a batch. */
void sgp_draw_filled_triangle ( float ax , float ay , float bx , float by , float cx , float cy ); /* Draws a single triangle. */
void sgp_draw_filled_triangles_strip ( const sgp_point * points , uint32_t count ); /* Draws strip of triangles. */
void sgp_draw_filled_rects ( const sgp_rect * rects , uint32_t count ); /* Draws a batch of rectangles. */
void sgp_draw_filled_rect ( float x , float y , float w , float h ); /* Draws a single rectangle. */
void sgp_draw_textured_rects ( int channel , const sgp_textured_rect * rects , uint32_t count ); /* Draws a batch textured rectangle, each from a source region. */
void sgp_draw_textured_rect ( int channel , sgp_rect dest_rect , sgp_rect src_rect ); /* Draws a single textured rectangle from a source region. */
/* Querying functions. */
sgp_state * sgp_query_state ( void ); /* Returns the current draw state. */
sgp_desc sgp_query_desc ( void ); /* Returns description of the current SGP context. */ このライブラリは、2020年以来プライベートプロジェクトでテストされており、安定していることが証明されています。
このライブラリはもともとMMORPGゲームMedivia Onlineが後援しています。私の仕事をサポートしてくれたことに感謝したいと思います。
さまざまな色機能のバッチドローコールをスポンサーしてくれた@kkukshtelに感謝します。
@FlooOOHの優れたSokolプロジェクトを必ずチェックアウトしてください。ゲーム開発に使用できる品質で作られた多くの便利なシングルヘッダーCライブラリを備えています。
また、私の他のシングルヘッダーCライブラリミニコロをチェックすることもできます。それは、ゲームの開発の有限状態マシンを簡素化するのに非常に役立つCに積み重なったコルーチンをもたらします。
sgp_set_uniform APIの破損が変化し、新しい事前総合ブレンドモードが追加されました。samplesディレクトリ内のすべてのサンプルのスクリーンショットを次に示します。任意の画像をクリックして、ブラウザで実際にリアルタイムでレンダリングされている画像を表示します。
プリミティブサンプル:
ブレンドモードのサンプル:
フレームバッファサンプル:
長方形のサンプル:
効果サンプル:
SDFサンプル:
私はフルタイムのオープンソース開発者であり、GitHubからの寄付の量は高く評価され、これやその他のオープンソースプロジェクトをサポートし続けることを奨励することができます。プロジェクトの目標に沿った小さな機能またはマイナーな機能強化について、1回限りのスポンサーシップを受け入れることができます。この場合、私に連絡してください。