Linux用の1k / 4kイントロの作成、パート1

「ロシアの舞台では、何かをすることでお互いを驚かせます」©manwe

(demoscene.ru/forumのSCRIMERSステータスから)



5分間のメタ:このテキストでは、子猫は、時間と労力に対する結果の製品のサイズの比率の観点から、あなたの時間を費やす方法について完全に無効であると読みます。

最大4kbのサイズのインストゥルメントのようなものを実行したいとしますが、不正であり、ウィンドウや分岐をサポートするシェーダー付きのビデオカードにはお金がありません。 または、apack / crinkler / sonant / 4klang / god-what-there-else-isから標準セットを取得したくない場合は、次の「すべて見る! 私も、距離フィールドを再構築することができます!」と同じものの数十または数百間で迷子になります 。 さて、または私たちは女の子が最終的に私たちに注意を払うことを願って、どういうわけか自慢してみたい。



一般に、それは重要ではありません。 弱いグラフィックカードを備えた何らかのLinuxがあり、すべての若者が先を行っているとします。 これをすべて試して、たとえば1024バイト以下のサイズのスタートアップファイルを作成してみましょう。これは、起動時になんらかの方法で(たとえば)ユーザーを作成して表示できます。











これには何を使用できますか? まず、X11とOpenGLがあります。 最小の血液でどのように初期化できますか:

  1. Xlib、GLX経由で直接

    +:システムで保証されています。

    -:GLコンテキストを上げるためだけに11コール
  2. 大食い

    +:GLコンテキストの4-5関数。

    -:どこからでも遠い
  3. Sdl

    +:コンテキストの2つの呼び出し、事実上の標準、ほとんどどこにでもあります。

    -:突然どこかでなくなったり、将来的に新しいバージョンとの互換性がクロールされる可能性があります
  4. 図書館を叩き、手ですべてをする

    +:噂(viznut?)によると、ダイナミックリンクを破棄して、GLコンテキストを初期化するために約300バイトを取得できます。

    -:あなたがそれを理解するまで、あなたは自分ですべてを壊すことができます




後者のオプションは間違いなく最も骨の折れるものですが、これまでのところ私にとっては複雑すぎます。 したがって、最後から2番目の処理を行います。



gccを見つけて、対処しなければならないすべてを感じてみましょう。 イントラのスケルトンから始めましょう-OpenGLコンテキストを初期化し、ビューポートをポイントして、キーが押されたときにアプリケーションを終了する簡単なイベントループを実行します。



#include <SDL.h> #define W 1280 #define H 720 #define FULLSCREEN 0//SDL_FULLSCREEN int main(int argc, char* argv) { SDL_Init(SDL_INIT_VIDEO); SDL_SetVideoMode(W, H, 32, SDL_OPENGL | FULLSCREEN); glViewport(0, 0, W, H); SDL_Event e; for(;;) { SDL_PollEvent(&e); if (e.type == SDL_KEYDOWN) break; // -    SDL_GL_SwapBuffers(); } SDL_Quit(); return 0; }
      
      







(ここの注意深い読者は以下を見つけるかもしれません: 私は答えます:それはまだあります!)



コンパイルする:



(注1:バイナリ配布のユーザーの場合:コンパイラ(gcc)、OpenGLのヘッダーファイル(通常はmesa)、および開発者向けのSDLバージョン(libsdl-devなど)が必要です。

(注2:-m32フラグは、64ビットディストリビューションの所有者にのみ必要です)



 cc -m32 -o intro intro.c `pkg-config --libs --cflags sdl` -lGL
      
      







そして、このような単純なプログラムには7キロバイト近くかかることを恐れます!



私たちはカブを傷つけますが、crtやその他のブルジョアの過剰はイチジクをあきらめなかったので、それらを切り取りました。

必要性:



 #include <SDL.h> #define W 1280 #define H 720 #define FULLSCREEN 0//SDL_FULLSCREEN void _start() { SDL_Init(SDL_INIT_VIDEO); SDL_SetVideoMode(W, H, 32, SDL_OPENGL | FULLSCREEN); glViewport(0, 0, W, H); SDL_Event e; for(;;) { SDL_PollEvent(&e); if (e.type == SDL_KEYDOWN) break; // -    SDL_GL_SwapBuffers(); } SDL_Quit(); asm ( \ "xor %eax,%eax\n" \ "inc %eax\n" \ "int $0x80\n" \ ); }
      
      





さらに、gccオプション-nostartfilesを指定する必要があります。

同時に、すぐにバイナリを削除します。

 cc -m32 -o intro intro.c `pkg-config --libs --cflags sdl` -lGL -Os -s -nostartfiles
      
      







約4.7キロバイトを取得します。これは約30%向上しますが、それでもかなり多くなります。

この便利なコードのプログラムでは、猫も泣き、ほとんどすべての場所がさまざまなエルフヘッダーで占められています。 ヘッダーから無用なものをカットするために、elfkickersセット(http://www.muppetlabs.com/~breadbox/software/elfkickers.html)からsstripユーティリティがあります:

 sstrip intro
      
      





これにより約600バイトが追加され、危険なことにバイナリのサイズが4kbになります。 そして彼はまだ有用なことは何もしていません。



再び動揺して、16進エディタでバイナリを見てゼロを見つけます。 それらの多く。 そして、誰がゼロや他のアイデンティティと戦うのが一番ですか?

正しく:

 cat intro | 7z a dummy -tGZip -mx=9 -si -so > intro.gz
      
      





(注1-自明性:p7zipパッケージをインストールする必要があります。なぜp7zipがそれほど複雑なのですか?(a)gzipなどの圧縮率が数パーセント低下している、(b)ファイル名などの余分なメタデータを持つgzヘッダーを削除する必要がある)

(注2:なぜgzipであり、bzip2ではないのでしょうか?経験的にp7zip -tGZipは私が試したすべてのものから最高の結果を出し、そのアンパックはどこにでもあるからです)

intro.gzはすでに約650バイトかかります! 実行させるためだけに残っています。 次の内容でunpack_headerファイルを作成します。

 T=/tmp/i;tail -n+2 $0|zcat>$T;chmod +x $T;$T;rm $T;exit
      
      





このヘッダーをアーカイブに貼り付けます:

 cat unpack_header intro.gz > intro.sh
      
      





ファイルを実行可能にして、得られたものを見てみましょう。

 chmod +x intro.sh ./intro.sh
      
      





これらの700ペニーバイトのバイトが実際に空のウィンドウを作成することを確認してください。

私たちがやったことはgzipドロップと呼ばれ、そのルーツはキャブドロップ方式に戻ります。これは当時(クリンクラーと男の時代の前)に人気があり、まったく同じことをWindowsで行っていました。



残りの300バイトにどれだけ予約できるかを見てみましょう。 伝統的に、次のことを行います。画面全体にglRectを表示します。これは、color = f(x、y)の形式の特別なシェーダーによって描画されます。

 #include <SDL.h> #include <GL/gl.h> #define W 1280 #define H 720 #define FULLSCREEN 0//SDL_FULLSCREEN char* shader_vtx[] = { "varying vec4 p;" "void main(){gl_Position=p=gl_Vertex;}" }; char* shader_frg[] = { "varying vec4 p;" "void main(){" "gl_FragColor=p;" "}" }; void shader(char** src, int type, int p) { int s = glCreateShader(type); glShaderSource(s, 1, src, 0); glCompileShader(s); glAttachShader(p, s); } void _start() { SDL_Init(SDL_INIT_VIDEO); SDL_SetVideoMode(W, H, 32, SDL_OPENGL | FULLSCREEN); glViewport(0, 0, W, H); int p = glCreateProgram(); shader(shader_vtx, GL_VERTEX_SHADER, p); shader(shader_frg, GL_FRAGMENT_SHADER, p); glLinkProgram(p); glUseProgram(p); SDL_Event e; for(;;) { SDL_PollEvent(&e); if (e.type == SDL_KEYDOWN) break; glRecti(-1,-1,1,1); SDL_GL_SwapBuffers(); } SDL_Quit(); asm ( \ "xor %eax,%eax\n" \ "inc %eax\n" \ "int $0x80\n" \ ); }
      
      





コードは非常に透過的です。ただし、おそらくvec4 pが変化することを除いて、正規化された画面座標を頂点シェーダーからフラグメント1にドラッグし、ウィンドウの物理サイズに依存しません。



それで、収集してみましょう:

 cc -m32 -o intro intro.c `pkg-config --libs --cflags sdl` -lGL -Os -s -nostartfiles && \ sstrip intro && \ cat intro | 7z a dummy -tGZip -mx=9 -si -so > intro.gz && \ cat unpack_header intro.gz > intro.sh && chmod +x intro.sh && \ wc -c intro.sh && \ ./intro.sh
      
      







このような些細なことで、余分な50バイトを既に取得しており、許可された1キロバイトをクロールしていることがわかります。 「あなたはまだ私と一緒に踊ります、あなたは踊ります!」、私たちは思うし、私たちの胸から最後のトリックを得る:動的ライブラリからすべての必要な機能を手動でロードする:

 #include <dlfcn.h> #include <SDL.h> #include <GL/gl.h> #define W 1280 #define H 720 #define FULLSCREEN 0//SDL_FULLSCREEN const char* shader_vtx[] = { "varying vec4 p;" "void main(){gl_Position=p=gl_Vertex;}" }; const char* shader_frg[] = { "varying vec4 p;" "void main(){" "gl_FragColor=p;" "}" }; const char dl_nm[] = "libSDL-1.2.so.0\0" "SDL_Init\0" "SDL_SetVideoMode\0" "SDL_PollEvent\0" "SDL_GL_SwapBuffers\0" "SDL_Quit\0" "\0" "libGL.so.1\0" "glViewport\0" "glCreateProgram\0" "glLinkProgram\0" "glUseProgram\0" "glCreateShader\0" "glShaderSource\0" "glCompileShader\0" "glAttachShader\0" "glRecti\0\0\0"; // : #define _SDL_Init ((void(*)(int))dl_ptr[0]) #define _SDL_SetVideoMode ((void(*)(int,int,int,int))dl_ptr[1]) #define _SDL_PollEvent ((void(*)(void*))dl_ptr[2]) #define _SDL_GL_SwapBuffers ((void(*)())dl_ptr[3]) #define _SDL_Quit ((void(*)())dl_ptr[4]) #define _glViewport ((void(*)(int,int,int,int))dl_ptr[5]) #define _glCreateProgram ((int(*)())dl_ptr[6]) #define _glLinkProgram ((void(*)(int))dl_ptr[7]) #define _glUseProgram ((void(*)(int))dl_ptr[8]) #define _glCreateShader ((int(*)(int))dl_ptr[9]) #define _glShaderSource ((void(*)(int,int,const char**,int))dl_ptr[10]) #define _glCompileShader ((void(*)(int))dl_ptr[11]) #define _glAttachShader ((void(*)(int,int))dl_ptr[12]) #define _glRecti ((void(*)(int,int,int,int))dl_ptr[13]) void* dl_ptr[14]; //  -    void dl() { const char* pn = dl_nm; void** pp = dl_ptr; for(;;) //    { void* f = dlopen(pn, RTLD_LAZY); //  for(;;) //    precious  { while(*(pn++) != 0); //         if (*pn == 0) break; //    ,     so' *pp++ = dlsym(f, pn); } //     if (*++pn == 0) break; //   -  ,  ,   } } void shader(const char** src, int type, int p) { int s = _glCreateShader(type); _glShaderSource(s, 1, src, 0); _glCompileShader(s); _glAttachShader(p, s); } void _start() { dl(); _SDL_Init(SDL_INIT_VIDEO); _SDL_SetVideoMode(W, H, 32, SDL_OPENGL | FULLSCREEN); _glViewport(0, 0, W, H); int p = _glCreateProgram(); shader(shader_vtx, GL_VERTEX_SHADER, p); shader(shader_frg, GL_FRAGMENT_SHADER, p); _glLinkProgram(p); _glUseProgram(p); SDL_Event e; for(;;) { _SDL_PollEvent(&e); if (e.type == SDL_KEYDOWN) break; _glRecti(-1,-1,1,1); _SDL_GL_SwapBuffers(); } _SDL_Quit(); asm ( \ "xor %eax,%eax\n" \ "inc %eax\n" \ "int $0x80\n" \ ); }
      
      





うわー 最後に、多かれ少なかれ肉付きが悪くなります!

ここで何が起こるか:これらのすべてのデータをエルフヘッダー内の非常にゆるくて不十分な構造で保存する代わりに、ライブラリから必要なライブラリと関数をゼロ区切りで1行に保存します。 文字列からの関数は、ポインターの配列だけに読み込まれ、適切なタイミングでマクロから既に呼び出されています。 特定のタイプのパラメーターは妥当な範囲では問題にならないことに注意してください-スタック上で4バイトずつ調整されます。 戻り値は、必要ない場合は一般に無視できます-とにかく、呼び出し関数は慣例により、eaxが保存されることを期待していません。 さらに、エラーをチェックする必要はありません-だれも与えないだらしないとぼろきれだけがエラーをチェックされます。

ビルドチームでは、ld-linux.so(一般に動的リンクを可能にするインタープリター)およびdlopenおよびdlsym関数を含むlibld.soのみに依存関係を残して、目標までバイナリを削除します。

 cc -Wall -m32 -c intro.c `pkg-config --cflags sdl` -Os -nostartfiles && \ ld -melf_i386 -dynamic-linker /lib32/ld-linux.so.2 -ldl intro.o -o intro && \ sstrip intro && \ cat intro | 7z a dummy -tGZip -mx=9 -si -so > intro.gz && \ cat unpack_header intro.gz > intro.sh && chmod +x intro.sh && \ wc -c intro.sh && \ ./intro.sh
      
      





899バイト! Creativity™を使用すると、ファットバイトでさらに100を獲得できます。



そこに何が収まりますか? たとえば、誰もがすでに病気になっている古いトンネル効果(はい、上記のスクリーンショットの効果)。

最初に、時間をシェーダーにドラッグする必要があります。 glUniformを使用してオタクのようにこれを行うのではなく、庭の男の子のように単純な方法で行います。

 float t = _SDL_GetTicks() / 1000. + 1.; _glRectf(-t, -t, t, t);
      
      





補間で時間が失われないようにするには、頂点シェーダーからの少しの助けが必要です。

 const char* shader_vtx[] = { "varying vec4 p;" "void main(){gl_Position=p=gl_Vertex;pz=length(p.xy);}" };
      
      





これで、pzコンポーネントに現在時刻が表示されました。 トンネルに接続するためだけに残ります。

トンネルは単純です-a = atan(px、py)角度と素朴な遠近法があります:z = 1 / length(p.xy)、残っているのはテクスチャカラー= f(a、z、t ) もちろん、座標自体に時間を混ぜることもできます。 そして一般的には、たとえば、このナンセンスなナフィグを読み終えて散歩に行くなど、すべてを行うことができます-素晴らしい晴天、大きなきれいな雪の吹きだまり、家から2歩離れた松林があります!







一般的に、科学的な反復的な突刺しの方法を使用して、これを取得します。

 #include <dlfcn.h> #include <SDL.h> #include <GL/gl.h> #define W 1280 #define H 720 #define FULLSCREEN 0//SDL_FULLSCREEN const char* shader_vtx[] = { "varying vec4 p;" "void main(){gl_Position=p=gl_Vertex;pz=length(p.xy);}" }; const char* shader_frg[] = { "varying vec4 p;" "void main(){" "float " "z=1./length(p.xy)," "a=atan(px,py)+sin(p.z+z);" "gl_FragColor=" "2.*abs(.2*sin(pz*3.+z*3.)+sin(p.z+a*4.)*p.xyxx*sin(vec4(z,a,a,a)))+(z-1.)*.1;" "}" }; const char dl_nm[] = "libSDL-1.2.so.0\0" "SDL_Init\0" "SDL_SetVideoMode\0" "SDL_PollEvent\0" "SDL_GL_SwapBuffers\0" "SDL_GetTicks\0" "SDL_Quit\0" "\0" "libGL.so.1\0" "glViewport\0" "glCreateProgram\0" "glLinkProgram\0" "glUseProgram\0" "glCreateShader\0" "glShaderSource\0" "glCompileShader\0" "glAttachShader\0" "glRectf\0\0\0"; // : #define _SDL_Init ((void(*)(int))dl_ptr[0]) #define _SDL_SetVideoMode ((void(*)(int,int,int,int))dl_ptr[1]) #define _SDL_PollEvent ((void(*)(void*))dl_ptr[2]) #define _SDL_GL_SwapBuffers ((void(*)())dl_ptr[3]) #define _SDL_GetTicks ((unsigned(*)())dl_ptr[4]) #define _SDL_Quit ((void(*)())dl_ptr[5]) #define _glViewport ((void(*)(int,int,int,int))dl_ptr[6]) #define _glCreateProgram ((int(*)())dl_ptr[7]) #define _glLinkProgram ((void(*)(int))dl_ptr[8]) #define _glUseProgram ((void(*)(int))dl_ptr[9]) #define _glCreateShader ((int(*)(int))dl_ptr[10]) #define _glShaderSource ((void(*)(int,int,const char**,int))dl_ptr[11]) #define _glCompileShader ((void(*)(int))dl_ptr[12]) #define _glAttachShader ((void(*)(int,int))dl_ptr[13]) #define _glRectf ((void(*)(float,float,float,float))dl_ptr[14]) static void* dl_ptr[15]; //  -    static void dl() __attribute__((always_inline)); static void dl() { const char* pn = dl_nm; void** pp = dl_ptr; for(;;) //    { void* f = dlopen(pn, RTLD_LAZY); //  for(;;) //    precious  { while(*(pn++) != 0); //         if (*pn == 0) break; //    ,     so' *pp++ = dlsym(f, pn); } //     if (*++pn == 0) break; //   -  ,  ,   } } static void shader(const char** src, int type, int p) __attribute__((always_inline)); static void shader(const char** src, int type, int p) { int s = _glCreateShader(type); _glShaderSource(s, 1, src, 0); _glCompileShader(s); _glAttachShader(p, s); } void _start() { dl(); _SDL_Init(SDL_INIT_VIDEO); _SDL_SetVideoMode(W, H, 32, SDL_OPENGL | FULLSCREEN); _glViewport(0, 0, W, H); int p = _glCreateProgram(); shader(shader_vtx, GL_VERTEX_SHADER, p); shader(shader_frg, GL_FRAGMENT_SHADER, p); _glLinkProgram(p); _glUseProgram(p); SDL_Event e; for(;;) { _SDL_PollEvent(&e); if (e.type == SDL_KEYDOWN) break; float t = _SDL_GetTicks() / 400. + 1.; _glRectf(-t, -t, t, t); _SDL_GL_SwapBuffers(); } _SDL_Quit(); asm ( \ "xor %eax,%eax\n" \ "inc %eax\n" \ "int $0x80\n" \ ); }
      
      







このnyashkaは、正確に1024バイトのパックでgcc-4.5.3に送られます。



さて、ここでもっと良いことも難しいこともできるとは思いません。 私たち自身にほぼ満足している、私たちはこのカテゴリの他の が何をしているかを見ています...



そして、私たちは落胆します。



何も残っていません。アセンブラーに登らなければなりません。

でも、次回はどうにかして。



All Articles