2021年8月1日日曜日

Unreal Eengin 4 でFASTBuildを利用する

はじめに

UE4 FASTBuildでC++やシェーダーを分散ビルドするによると, 4.26ではうまく動かないらしく, 実際に設定してみても動いていないように見えます.

4.26でFASTBuildの環境を作る

4.27からFASTBuildのバイナリをコピーしてくると動作します. 依存バイナリのダウンロード部分だけを追いきれなかったため, Setup.batで全てダウンロードしています.

4.27のFASTBuildバイナリを用意する

$ mkdir Unreal4.27 & Unreal4.27
$ git clone -b 4.27 https://github.com/EpicGames/UnrealEngine.git
$ Setup.bat

4.27から4.26へバイナリをコピーする

4.27から4.26へ次のディレクトリをコピーする. Engine\Extras\ThirdPartyNotUE\FASTBuild

共有ディレクトリを用意する

全てのホストとワーカから見える共有ディレクトリを用意します.

ビルド設定を用意する

ビルド設定にFASTBuildの設定を追加します.

ビルド設定を使う理由

UnrealBuildToolはFASTBuildが有効な場合, FBuildWorkerをGUIなしで起動しています. 実行済みのFBuildWorkerがある場合は, FBuildWorker.copyを作成して起動しているように見えます.

つまり, ホストのFASTBuildの設定を変更したいためにFBuildWorkerを自力で起動しても反映されません.

また, チームで設定を共有するためでもあります.

次の内容のBuildConfiguration.xml

<?xml version="1.0" encoding="utf-8" ?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
    <BuildConfiguration>
        <bAllowFASTBuild>true</bAllowFASTBuild>
    </BuildConfiguration>
    <FASTBuild>
        <FBuildBrokeragePath></FBuildBrokeragePath>
    </FASTBuild>
</Configuration>

次のいずれかに置きます.

  • Engine/Saved/UnrealBuildTool/BuildConfiguration.xml
  • User Folder/AppData/Roaming/Unreal Engine/UnrealBuildTool/BuildConfiguration.xml
  • My Documents/Unreal Engine/UnrealBuildTool/BuildConfiguration.xml

FBuildBrokeragePathは共有ディレクトリを指定します. その他の設定は, 公式 Build Configurationを参照してください.

ワーカでFBuildWorkerを起動する

FBuildWorkerを起動しておくだけです. ホストのFBuildWorkerUnrealBuildToolが起動しています.

プラットフォームごとのFASTBuild

現状でプラットフォームごとにFASTBuild利用の可否や設定を切り替える方法を見つけていません. BuildConfigurationのXMLスキーマでも, FASTBuildの設定はプラットフォームの子になっていません.

FASTBuildを正しく動作させるためにはコンパイラを統一することが重要で, コンシューマ機など固有のコンパイラを使うプラットフォーム向けビルドでFASTBuildを使う場合, もうひと手間必要です (WIP).

監視ツールについて

  • 次の2つに共通する事項
    • モニタリングに関しては, FASTBuildのログを監視して表示しています
  • FASTBuild-Dashboard
    • スタンドアロン型
    • 自身のFBuildWorkerを起動する
      • FBuildWorkerのプロセスが重複した場合の動作は追っていません
  • FASTBuildMonitor
    • Visual Studioの拡張, マーケットプレイスに上げていない
    • ログを監視してモニタリングするだけなので, FBuildWorkerの設定項目はない
    • Visual Studio 2019に対応していなく, プルリクエストも放置されている
    • FASTBuildMonitorをベースに自分でビルドする

シェーダコンパイル

シェーダのビルドは分散されていないように見えます.

(TRY) 4.27で次のファイルに変更があるため, 上書きして試す.

Engine\Source\Programs\UnrealBuildTool\Executors\Experimental\FASTBuild.cs

まとめ

シェーダコンパイルができない, プラットフォームごとに設定変更できない, と現状実用的と言い難いと思います.

2021年7月30日金曜日

Unreal Engine 4 でPakをLZ4圧縮

はじめに

LZ4に正式対応し, Pak File Compression Format(s)で指定できるように思えますが, そんなことはないです.

原因

FCompression::IsFormatValidが原因と思われます. コメントからすると, 不具合のように見えます.

    bool FCompression::IsFormatValid(FName FormatName)
    {
        // build in formats are always valid
        if (FormatName == NAME_Zlib || FormatName == NAME_Gzip)
        {
            return true;
        }

        // otherwise, if we can get the format class, we are good!
        return GetCompressionFormat(FormatName, false) != nullptr;
    }

圧縮形式

zlibのチェックサム計算も軽くはないことに注意です.

  • zlib
    • 圧縮率と速度のバランスが良いが, 今では旧い, zstandardを代わりに使うべき
    • checksumは必ず計算する
  • ZStandard
    • 圧縮率と速度のバランスが良い
    • checksumはオプション
  • LZ4
    • 圧縮・伸張速度に重点を置いたもの
      • LZSSだけなので上記2つより圧縮率は低い
    • 圧縮に時間をかけても, 伸張速度はほとんど変わらない
    • checksumはオプション
  • Lizard
    • LZ5を名乗る, LZ4を改良したと主張している

PakのLZ4圧縮

FCompressionを書き換えることは影響範囲を考えてやりたくないです. エンジンのプラグインを書きます. UnrealPakは独立したプログラムのため, 単純なエディタ拡張ではUnrealPakから見えません.

LZ4CompressionFormat.cpp
#include "LZ4Compression.h"
#include "CoreMinimal.h"
#include "Misc/Compression.h"
#include "Misc/ICompressionFormat.h"
#define LOCTEXT_NAMESPACE "FLZ4CompressionModule"

namespace
{
DEFINE_LOG_CATEGORY_STATIC(LogLZ4CompressionFormat, Log, All);
struct LZ4CompressionFormat : public ICompressionFormat
{
    FName GetCompressionFormatName() override;
    bool Compress(void* CompressedBuffer, int32& CompressedSize, const void* UncompressedBuffer, int32 UncompressedSize, int32 CompressionData) override;
    bool Uncompress(void* UncompressedBuffer, int32& UncompressedSize, const void* CompressedBuffer, int32 CompressedSize, int32 CompressionData) override;
    int32 GetCompressedBufferSize(int32 UncompressedSize, int32 CompressionData) override;
    uint32 GetVersion() override;
    FString GetDDCKeySuffix() override;
};

FName LZ4CompressionFormat::GetCompressionFormatName()
{
    return "lz4";
}

bool LZ4CompressionFormat::Compress(void* CompressedBuffer, int32& CompressedSize, const void* UncompressedBuffer, int32 UncompressedSize, int32 CompressionData)
{
    UE_LOG(LogLZ4CompressionFormat, Warning, TEXT("Compress"));
    if(!FCompression::CompressMemory(NAME_LZ4, CompressedBuffer, CompressedSize, UncompressedBuffer, UncompressedSize, COMPRESS_NoFlags, CompressionData)) {
        UE_LOG(LogLZ4CompressionFormat, Warning, TEXT("Faile to compress"));
        return false;
    }
    return true;
}

bool LZ4CompressionFormat::Uncompress(void* UncompressedBuffer, int32& UncompressedSize, const void* CompressedBuffer, int32 CompressedSize, int32 CompressionData)
{
    UE_LOG(LogLZ4CompressionFormat, Warning, TEXT("Uncompress"));
    if(!FCompression::UncompressMemory(NAME_LZ4, UncompressedBuffer, UncompressedSize, CompressedBuffer, CompressedSize, COMPRESS_NoFlags, CompressionData)) {
        UE_LOG(LogLZ4CompressionFormat, Warning, TEXT("Faile to uncompress"));
        return false;
    }
    return true;
}

int32 LZ4CompressionFormat::GetCompressedBufferSize(int32 UncompressedSize, int32 CompressionData)
{
    return FCompression::CompressMemoryBound(NAME_LZ4, UncompressedSize, COMPRESS_NoFlags, CompressionData);
}

uint32 LZ4CompressionFormat::GetVersion()
{
    return FCompression::GetCompressorVersion(NAME_LZ4);
}

FString LZ4CompressionFormat::GetDDCKeySuffix()
{
    static const FString suffix = "2AEE7CBB0BD24E71B0D516ECE2AB68C1";
    return suffix;
}
} // namespace

FLZ4CompressionModule::FLZ4CompressionModule()
    : compressionFormat_(nullptr)
{
}

FLZ4CompressionModule::~FLZ4CompressionModule()
{
    delete compressionFormat_;
}

void FLZ4CompressionModule::StartupModule()
{
    compressionFormat_ = new LZ4CompressionFormat();
    IModularFeatures::Get().RegisterModularFeature(COMPRESSION_FORMAT_FEATURE_NAME, compressionFormat_);
}

void FLZ4CompressionModule::ShutdownModule()
{
    IModularFeatures::Get().UnregisterModularFeature(COMPRESSION_FORMAT_FEATURE_NAME, compressionFormat_);
    delete compressionFormat_;
    compressionFormat_ = nullptr;
}

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FLZ4CompressionModule, LZ4Compression)

まとめ

今回は, Pak File Compression Format(s)lz4を書けばLZ4圧縮になるはずです. これを応用すれば好きなフォーマットにできるはずです.

2021年7月20日火曜日

Unreal Engine 4 Editorの耳障りな音を変更する

変更

次のSEを置き換えればよい.
Engine/Content/EditorSounds/Notifications

この調子だとほとんどのエディタリソースを置き換える必要がありそうです.

2021年7月16日金曜日

WSL上のConcourse CIで, Unreal Engine 4のビルドがしたい

はじめに

CI/CD環境の構築が属人化するなら, もう私が扱いやすい環境があればいいのではないか?, ということで脱Jenkinsを行い, ConcourseでどうしてもUE4のビルドを自動化したいです. WSLやWSL上のDockerの構築については省略します.

Jenkins

Jenkinsではだめなのかというと, 扱い辛いからです. よく属人化すると言われますが, その理由を説明した資料を私は知りません. 私のまわりでは次の理由だと思います.

  • 不安定
    • 全く設定を変更していないつもりでもビルドが壊れる
  • 設定が難しい
    • パイプライン追加以前では, VCSで管理したり, 再利用しようとするとシェルスクリプトやバッチファイルにするしかなかった. 少し高級なcronでしかなかった
      • シェルスクリプトやバッチファイルに詳しい人に依存する
    • パイプラインは, GroovyのDSLでわかりにくい
      • 他に役に立たないDSLを勉強したくない, ドキュメントもわかり辛い
      • DSLなのでJenkins上でしか実行できない. パイプラインを書いてコミットしてJenkins上で実行して失敗の繰り返し, 苦痛でしかない
  • CI/CDの重要性に興味・理解ある人しか, 修正・改善をしない
    • どんなツールを使ってもこのために属人化する

WSL

WSLである理由は環境移行が楽かもしれないということです. 今日の企業ではレンタルPCで社員の環境を作ることが多く, ビルドマシンも同じくレンタルPCが多いです. それほど頻繁ではないですが, レンタル期間終了とともに環境移行を強いられます. 以前にWindows上のVM環境にビルド環境を構築して移行を楽にしようと頑張りましたが失敗しました. 原因はファイルシステムのアクセスです. VMからはともかく, CI/CDツール越しではうまくいきませんでした. WSLならもう少し楽になるのでは?

UE4のビルド

Windows向けビルドしか試していませんが, RunUAT.batが呼び出せれば大体解決しそうです.

Concourse CI インストール

鍵生成

まず, コンコースのサーバ・クライアント・ユーティリティを兼ねたバイナリを取得します. このバイナリさえあればDockerは必要ないのですが, DBの構築も面倒なので, このバイナリは鍵生成だけに使います.

$curl -OL https://github.com/concourse/concourse/releases/download/vX.X.X/concou$rse-X.X.X-linux-amd64.tgz
$tar zxvf concourse-X.X.X-linux-amd64.tgz

バイナリを取得できたら鍵を生成します. ポイントはワーカの公開鍵を別名でコピーしている部分です.

$mkdir -p keys/web keys/worker
$./concourse/bin/concourse generate-key -t ssh -f ./tsa_host_key
$./concourse/bin/concourse generate-key -t ssh -f ./worker_key

$cp ./worker_key.pub ./keys/web/authorized_worker_keys

$mv ./tsa_host_key ./keys/web/
$mv ./worker_key ./keys/worker/
$mv ./tsa_host_key.pub ./keys/worker/
$mv ./worker_key.pub ./keys/web/ 

Docker-compose

docker-compose.ymlを作成します.

docker-compose.yml
version: '3'

services:
  concourse-db:
    image: postgres
    container_name: concourse-db
    environment:
      POSTGRES_DB: concourse
      POSTGRES_PASSWORD: concourse_pass
      POSTGRES_USER: concourse_user
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - ./db/postgres:/var/lib/postgresql/data
      - ./db/logs:/var/log

  concourse:
    image: concourse/concourse
    container_name: concourse
    restart: unless-stopped
    command: web
    privileged: true
    depends_on: [concourse-db]
    ports: ["8000:8080", "2222:2222"]
    volumes: ["./keys/web:/concourse-keys"]
    environment:
      CONCOURSE_POSTGRES_HOST: concourse-db
      CONCOURSE_POSTGRES_USER: concourse_user
      CONCOURSE_POSTGRES_PASSWORD: concourse_pass
      CONCOURSE_POSTGRES_DATABASE: concourse
      CONCOURSE_EXTERNAL_URL: http://localhost:8000
      CONCOURSE_ADD_LOCAL_USER: admin:admin
      CONCOURSE_MAIN_TEAM_LOCAL_USER: admin
      CONCOURSE_WORKER_BAGGAGECLAIM_DRIVER: overlay
      
  concourse-worker:
    image: concourse/concourse
    privileged: true
    links: [concourse]
    depends_on: [concourse]
    command: worker
    volumes: ["./keys/worker:/concourse-keys"]
    environment:
      CONCOURSE_TSA_HOST: concourse:2222

Windowsのワーカ

これが重要です. WSL上のシェルからなんとかできるかと試行錯誤しましたが, 妥協してWindows上でワーカを動かす方が楽でした. Windows用のconcourseバイナリを取得して起動するだけです.

concourse worker --work-dir ./work --tsa-host localhost:2222 --tsa-public-key ./keys/worker/tsa_host_key.pub --tsa-worker-private-key ./keys/worker/worker_key

プロジェクト管理

targetはサーバに対応します. 1サーバで複数プロジェクトを管理するなら, チームを使用します.

fly -t server_name set-team --team-name project_name --local-user user_name

ログインは, -nでチーム名を指定します.

fly -t server_name login -n project_name -u user_name -p user_pass

ビルド

最も単純なビルド設定は次のようになります. 結局, バッチファイルに行きついていますが, バッチファイルの引数である程度柔軟にできると思います.

jobs:
  - name: build_quickstart
    plan:
      - task: build
        config:
          platform: windows
          run:
            path: cmd.exe
            args: ["/c", "chcp 65001 & cd /d ((PROJECT_ROOT)) & call build.bat"]

パラメータ((PROJECT_ROOT))は別のファイルenvironment.ymlで定義しています.

PROJECT_ROOT: "X:/Path to Unreal Project"

パイプライン設定時に-lオプションでパラメータ設定を指定します.

$ fly -t main sp -p build -c build.yml -l environment.yml

build.batも一応書いておきます, environment.batはRunUAT.batのパスを設定しているだけです.

call environment.bat
%UNREAL_RUNUAT% BuildCookRun -project=%~dp0/project.uproject -build -cook -stage -allmap -pak -nop4 -partialgc -platform=Win64 -clientconfig=Development

DebianでDockerサービスが起動に失敗する

DockerがiptablesでNATの設定をしようとするが, Debianは別のソフトウェアでNATを設定しているので失敗する, でいいのでしょうか. 

Docker forum 

sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy

2021年6月17日木曜日

Pimplはアンチパターンだと思います

はじめに

数年前は使うこともあったのですが, 現代にはふさわしくないと思い使わなくなりました.

利点と欠点, それについての考え

利点

  • includeを減らして, コンパイル時間の短縮
    • 2021年では, 計算機, コンパイラ, そしてリンカの性能が向上しました. これは使う理由にはなりません. 聞こえていますか?20年前から来たあなた
      • 時間は金で買うべきです, ビルドマシンぐらい金をかけましょう
    • 今のプロジェクトのビルドが遅い理由は, 次です. ビルドが遅すぎて, ビルドは一日一回, agileではなく, sluggishです, stupidプロジェクトでもいいです
      • Pimplを数多く使っているのに, includeが多い. コピペのせいもあるのですが
        • リファクタリングをしないので, コピペが修正されることは2度とない
      • inlineが多い
        • inlineを理解していない
        • コンパイラオプションで, できるだけinline展開するようにしている
        • inline展開で処理が速くなるとは限らない, これは今も昔も
      • コンパイラオプションで最大限の最適化を設定している
        • O2より上は速くなるかは環境依存, 遅くなることはよくある
          • 使用者が少ないので, 不具合も多くなる
        • コンパイラを理解していない
  • 実装の隠蔽
    • 例えば, JavaやC#で困ったことはありますか?
      • 自分の意に沿わないからといって, ヘッダを書き換えるようなエンジニア?をチームに置いておきますか? そちらの方が問題だと思います
    • インターフェイスを使って, FILEのようなことをすれば完全に隠蔽できるので, 使う理由にはなりません

欠点

  • メモリコストと実行速度に悪影響がある
    • 処理落ちがどうのなど, 言っていることとやっていることが異なる
  • 冗長
    • 処理を転送するだけの記述がコード量を増やす
    • コードを追うことに2倍のコストがかかる
    • たまにインターフェイスのメソッド名と, 実体のメソッド名が異なる
      • ガンジーでも助走付けて
  • ヘビークラスを作りやすい
    • これは当社比なのですが, 10年以上のエンジニア経験では, Pimplイディオムはくそでかクラスになりやすい傾向にあります
      • コンパイル時間の短縮という錦の旗, メモリコストと実行速度を考えると, Pimplイディオムがコンポジションに向いていないことが原因と思っています
    • コンポジションやアグリゲーションで, ソフトウェアを建築する, という考え方を持っている人には受け入れがたいです

まとめ

他人に強制するつもりはありませんが, 意味のないイディオムだと思います. 

2021年6月15日火曜日

BC5 圧縮テクスチャ

はじめに

プログラムから, テクスチャ圧縮がしたいです. BC1, BC3, BC6h BC7についてはispc texture compressionを使おうと思っていましたが, BC5がないのです.

https://software.intel.com/content/www/us/en/develop/articles/fast-ispc-texture-compressor-update.html

ガイドに仕様がちゃんと書いてあります. 簡単そうなので作ってしまいます.

https://docs.microsoft.com/en-gb/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc5

ファイルフォーマット

4x4のブロックごとに圧縮するブロック圧縮です. BC4はRだけの1チャネル, BC5はRGの2チャネルを扱います. 4x4x2 = 32 bytesを次の tablex2 = 16 bytesに圧縮します. BC5_UNORMとBC5_SNORMの2バリエーションです. 2つはシェーダでサンプルした場合, [0 1]の浮動小数点数になるか, [-1 1]の浮動小数点になるかが違います.

テーブルは, 4x4の最小値と最大値が先頭に2バイト, 次に3bits x 16のテーブルへのインデックスが並びます.

/*
MSB                   LSB
| red_0               | 1 byte minimum color
| red_1               | 1 byte maximum color
| red_h | red_g | red_f | red_e | red_d | red_c | red_b | red_a | 3 bits x 8
| red_p | red_o | red_n | red_m | red_l | red_k | red_j | red_i | 3 bits x 8
*/

テーブルは明示的に持たず最大値, 最小値から計算します. UNORMとSNORMで1箇所異なりますが, 圧縮するだけならばどちらでも同じなので統一します.

static constexpr u32 Shift = 20;
void createTable(s32 colors[8], s32 block[16])
{
    colors[0] = block[0];
    colors[1] = block[0];
    for(u32 i = 1; i < 16; ++i) {
        colors[0] = minimum(colors[0], block[i]);
        colors[1] = maximum(colors[1], block[i]);
    }
    colors[0] <<= Shift;
    colors[1] <<= Shift;

    if(colors[1] < colors[0]) {
        // 6 interpolated color values
        colors[2] = (6 * colors[0] + 1 * colors[1]) / 7; // bit code 010
        colors[3] = (5 * colors[0] + 2 * colors[1]) / 7; // bit code 011
        colors[4] = (4 * colors[0] + 3 * colors[1]) / 7; // bit code 100
        colors[5] = (3 * colors[0] + 4 * colors[1]) / 7; // bit code 101
        colors[6] = (2 * colors[0] + 5 * colors[1]) / 7; // bit code 110
        colors[7] = (1 * colors[0] + 6 * colors[1]) / 7; // bit code 111
    } else {
        // 4 interpolated color values
        colors[2] = (4 * colors[0] + 1 * colors[1]) / 5; // bit code 010
        colors[3] = (3 * colors[0] + 2 * colors[1]) / 5; // bit code 011
        colors[4] = (2 * colors[0] + 3 * colors[1]) / 5; // bit code 100
        colors[5] = (1 * colors[0] + 4 * colors[1]) / 5; // bit code 101
        colors[6] = 0;                                   // bit code 110
        colors[7] = 255<<Shift;                          // bit code 111
    }
}

ピクセル値に最も近いテーブルインデックスを検索して, 符号化します.

u64 findNearest(s32 colors[8], s32 x)
{
    u32 index = 0;
    s32 mind = absolute(colors[0] - x);
    for(u32 i = 1; i < 8; ++i) {
        s32 d = absolute(colors[i] - x);
        if(d < mind) {
            mind = d;
            index = i;
        }
    }
    return index;
}

まとめ

GPUOpen Compressonatorと結果が異なるため, もっといい方法がありそうです.

2021年6月10日木曜日

Windows 10 のシステムロケールの不具合

どの更新からか, Windows 10のシステムロケールに, "ベータ: ワールドワイド言語サポートでUnicode UTF-8を使用(U)"というチェックが追加された. デフォルトでONなのですが, これのせいで文字化けするアプリケーションが出てきた. 少しテストすればわかりそうなものですが.

2021年5月3日月曜日

-marchのx86-64向け, CPU命令世代オプション

GCC 11.0から, LLVM 12.0から, -marchオプションにx86-64用のCPU命令世代指定が追加された. -march=x86-64-v[234]のように, 命令セットを対象に, ある程度の範囲で絞った世代に分割して指定することができる. これまで, -march=nativeとしてコンパイルしたCPUに合わせるか, -march=skylakeなどのCPUを固定する方法しかなかった.

CPUベンダとしては, 新CPUごとに新しい命令を追加してアピールしたい. 開発者としては, ある程度古いCPUもサポートしながら, 新しい命令も使いたい. このふたつの間を埋める機能といえます. 開発者が, ある命令セットがどのCPUからサポートしているのか, 把握して管理することは手間がかかります.

オプション 有効になる命令 CPU世代
x86-64 CMOV, CMPXCHG8B, FPU, FXSR, MMX, FXSR, SCE, SSE, SSE2 Pentium 4
x86-64-v2 CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3 Nehalem
x86-64-v3 AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE Haswell
x86-64-v4 AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL Skylake Xeon

2021年3月13日土曜日

チケットの書き方

はじめに

この文書は, 最低限伝わるのチケットの書き方を, 同僚に身に着けてもらうことを目的にしています. その方法は, 読者が容易に理解できる文書を書くように心がけ, 振り返って改善を継続することです.

チケットはチームの資産です. 常に自分が作成したチケットについて, 内容が読者に理解しやすかったかを振り返り, 次の機会に改善することが重要です. 初めから完璧な文書を作成できる人などいないですし, 誰が書いた文書も常に足りていません. 伝わる文書, 伝わった文書, 伝わったからいいやの文書, それぞれ異なります.

他人が作成した, 理解が難しいチケットを修正してはいけない, という法はありません. より上手くチームが動けるように改善すべきです. 自分が作成したチケットが修正されたなら, それは学ぶ機会に恵まれたということです.

チケットは, タスク管理システムやイシュー管理システムにおける, 一つのタスクやイシューを管理するための一連の情報を言います. チケットの概念の名称はシステムによって異なるため, 読み替えてください.

チケットの文書

チケットの文書は読者に内容を理解してもらうためのものです. 自分のToDo用のチケットというものがあるかもしれません. それをチームのシステム上に作成する理由は, 他人が読むことを前提としているはずです. 本当に私的ToDoなら, 自分でシステムを立て, そこに書くべきです. チームの場と私の場を混同してはいけません.

内容

チケットの文書では, ひとつの主題に集中します. そして, ひとつの主題について, 事実や状況といった情報と意見を正確にもれなく簡潔に記述します. 感想などの心情的要素を書きません. 情報の内容は, 主題(文書の目的)と読者の要求によって変わりますが, 意見との区別を明確にします. 事実は, あいまいな表現や主観に依存する形容詞を使わず, できるだけ明確に記述します. 意見は, 事実の上に立ち, 論理的に導き出した結果です. 意見の記述は複数の評価が並立しますが, 読者が記述された事実から評価できる必要があります.

事実と状況

事実と実際に起きた状況だけを正確に記述します. 意見とは言えない, 論理的理由のない憶測は無視されるため書かない方がいいです. 結果的にその憶測が当たったとしても偶然です, タスクの担当者は参考にしていません.

私がよく見る憶測の文章は, 次のようなものです. 憶測に時間を費やす必要はありません, あなたの仕事に時間を使ってください.

× おそらく, 〇〇〇が原因な気がする

曖昧な表現

■ たぶん, らしい, かもしれない, と思う

事実かどうか確認してから書くようにします.

タスクでは手順を追加することになります. 手順の粒度が大きい場合, 別タスクになることもあります.

たぶん, 〇〇〇の方が有効だと思います. △△△という方法で確認してみます.

イシューでは再現できなかった, 再現のために試行する時間がないこともあると思います. その場合はそのまま書けばいいです.

〇〇〇という手順で, 3/10で再現しました

〇〇〇を試しましたが, 再現できませんでした

再現するかは試していません

会話では, 次のように曖昧な表現を使ってしまった時に修正します.

たぶん, 〇〇〇だと思います. 少し待ってください, 確認します.

■ 擬音語, 擬態語

その意味や度合いは個人の心情に依存します, 絶対に使わないようにします.

× バーンと表示してほしい

× ビャーと出てくる

× ドーンって感じで

言葉の選択

誰でも理解できる言葉を選択します. 一般的にチーム人員は変わるものです. 未来の見知らぬチームメンバが理解できるように書けばいいです. 指しているものが変わる可能性がある言葉, あれ, それ, は絶対に使わないようにします. バージョンを表す, 最新版, 最新の, も使ってはいけません.
余談ですが, 聞き手が理解できないことを解った上で, 特定コミュニティだけで通じる言葉を使う同僚がいます.

簡潔な文書

英国首相であった, ウィンストン・レナード・スペンサー・チャーチルは ([1],[2],[4]),

われわれの職務を遂行するには大量の書類を読まねばならぬ。その書類のほとんど全てが長すぎる。時間が無駄だし、要点をみつけるのに手間がかかる。同僚諸兄とその部下の方々に、報告書をもっと短くするようにご配意願いたい。

という書簡を部下に送ったそうです. 読解に時間がかかる文書は, 他人に時間を浪費させています.

特に過度な敬語は無駄です. もし, チケットに過度な敬語を使う必要があるならば, チームビルドに失敗しています. チームメンバの関係を修復すべきです.

チケットの書き方

共通

会話をしない

会話は口頭やチャットツールで行います. その会話の中で決まったこと, 補足すべき事項をチケットにまとめます. チャットツールは記録を保存する目的には向いていません. 会話のやりとりで, 相手が理解した・確認が取れた, と思い込んではいけません.
チケットはチームで解決することについて書きます. 誰か特定の人にお願いするのではなく, 全員で解決するものです.

× タイトル:〇〇〇をお願いします

タスク

次の4つを書くといいです, 全てを書く必要はありません.

  • 目的
    • どうしてそれを行うのか
    • 次の手段を検討・再考することにつながる, 重要な項目です
      • 副次目的があれば, 優先順位をつけて, 書いておくといいです
      • 機能を削らなければときの判断材料です
  • 手段・手順
    • 目的を実現する手順です
    • 機能を削る場合に変更されます
  • 目標
    • 目的が達成されたかどうか判定するための材料です
    • 数値で表現できる方がいいですが, 主観に依存することも多いです
    • 具体的成果物でもいいです. できれば成果物の品質も数値で表現できるほうがいいです
  • 完了条件
    • 目標に到達したかどうかが基本ですので, 目標と混ぜて記述してもいいです
    • 完了条件と目標がずれていた場合, 目的の設定がおかしい可能性が高いです

バグ

  • 状況
    • いつ, どこで, 何をしたか
      • ソフトウェアにおいては, これは一体なことが多いです
      • ゲームなどの場合, 何をしたかと不具合発生のタイミングが対応しない場合があります
      • 真に正確であることではなく, 観測された事実であることが大事です

〇〇〇画面で, △△△したときに

〇〇〇画面で, △△△していたときに

  • 期待される状態

    • 仕様で決めた状態
    • 一般的に望ましいと思われる状態
      • 仕様の不具合を含めて見直しをする必要があります
  • 再現手順

    • あればいいが, 再現するために過度な時間を費やす必要はないです
    • 憶測は書いてはいけません

参考までに, エンジニアの手順を示します.

  1. 再現して, 確かに起きることを確認します
    • 100%再現できる状態を探します. プログラムの原理的にこれは必ず存在します
      • ハードウェアや宇宙線によるビット破壊は考慮しません
      • 外部モジュールが原因の場合は, 全く別の解決方法になります
  2. 不具合が発生した瞬間に実行されていたプログラム片を特定します
    • そこで実行されていた命令か, 使用していたデータのどちらか, または両方に原因があります
  3. 原因を特定して修正します
  4. 1.で特定した再現手順で不具合が起きないことを確認します
    • 原因によって, 影響がある範囲も不具合が起きないことを確認します
    • ログやアサーションなど, 同じ不具合が起きた場合に検出できる仕組みを導入します

まとめ

あなたの読者が内容を理解できるか? それだけです. そのために改善を継続します.

最初から誰に対しても厳格にする必要はありません, より良い状態に改善していけばいいだけです.

〇〇〇画面がおかしい

というタイトルのチケットを回されたら, エンジニアが修正すればいいだけです. 時にはチケット上で会話することもあるでしょう. 方法に縛られる必要はありません.

正しく動作することを確認しました. 修正ありがとうございます. クローズします.

参考文献

  • [1] 木下 是雄,「理科系の作文技術」,1981
  • [2] 木下 是雄,久間月 慧太郎,「まんがでわかる 理科系の作文技術」,2018
  • [3] Barbara Minto,山崎 康司,「新版 考える技術・書く技術」,1999
  • [4] https://studying.jp/engineer/blog/20191226.html

2021年2月26日金曜日

File Size in C

Problem with using fseek/ftell

From cppreference.com - fseek, SEEK_END may not be supported. In this case, ftell doesn't indicates true file size.

Binary streams are not required to support SEEK_END, in particular if additional null bytes are output.

Reliable method

Should use OS specific functions.

Windows

HANDLE file;
LARGE_INTEGER result;
int64_t size = TRUE == GetFileSizeEx(file, &result)? result.QuadPart : 0;

Others

FILE file;
int32_t fd = fileno(file); //If file has not been opened, the result will be undefined.
struct stat64 status;
int64_t size = 0<=fstat64(fd, &status)? status.st_size : 0;

2021年2月14日日曜日

Usage of DirectX Shader Compiler

Build

  • Repository
  • Prerequisites
    • ATL
    • .Net Framework 4.5

Options

Common Options

Common Options
-help Display available options
-nologo Suppress copyright message
-Qunused-arguments Don't emit warning for unused driver arguments

Compilation Options

Compilation Options
-all-resources-bound Enables agressive flattening
-auto-binding-space <value> Set auto binding space - enables auto resource binding in libraries
-Cc Output color coded assembly listings
-default-linkage <value> Set default linkage for non-shader functions when compiling or linking to a library target (internal, external)
-denorm <value> select denormal value options (any, preserve, ftz). any is the default.
-D <value> Define macro
-enable-16bit-types Enable 16bit types and disable min precision types. Available in HLSL 2018 and shader model 6.2
-encoding <value> Set default encoding for text outputs (utf8
-export-shaders-only Only export shaders when compiling a library
-exports <value> Specify exports when compiling a library: export1[[,export1_clone,...]=internal_name][;...]
-E <value> Entry point name
-Fc <file> Output assembly code listing file
-fdiagnostics-show-option Print option name with mappable diagnostics
-Fd <file> Write debug information to the given file, or automatically named file in directory when ending in ''
-Fe <file> Output warnings and errors to the given file
-Fh <file> Output header file containing object code
-flegacy-macro-expansion Expand the operands before performing token-pasting operation (fxc behavior)
-flegacy-resource-reservation Reserve unused explicit register assignments for compatibility with shader model 5.0 and below
-fno-diagnostics-show-option Do not print option name with mappable diagnostics
-force-rootsig-ver <profile> force root signature version (rootsig_1_1 if omitted)
-Fo <file> Output object file
-Fre <file> Output reflection to the given file
-Frs <file> Output root signature to the given file
-Fsh <file> Output shader hash to the given file
-Gec Enable backward compatibility mode
-Ges Enable strict mode
-Gfa Avoid flow control constructs
-Gfp Prefer flow control constructs
-Gis Force IEEE strictness
-HV <value> HLSL version (2016, 2017, 2018). Default is 2018
-H Show header includes and nesting depth
-ignore-line-directives Ignore line directives
-I <value> Add directory to include search path
-Lx Output hexadecimal literals
-Ni Output instruction numbers in assembly listings
-no-legacy-cbuf-layout Do not use legacy cbuffer load
-no-warnings Suppress warnings
-No Output instruction byte offsets in assembly listings
-Odump Print the optimizer commands.
-Od Disable optimizations
-pack-optimized Optimize signature packing assuming identical signature provided for each connecting stage
-pack-prefix-stable (default) Pack signatures preserving prefix-stable property - appended elements will not disturb placement of prior elements
-recompile recompile from DXIL container with Debug Info or Debug Info bitcode file
-res-may-alias Assume that UAVs/SRVs may alias
-rootsig-define <value> Read root signature from a #define
-T <profile> Set target profile.
ps_6_0, ps_6_1, ps_6_2, ps_6_3, ps_6_4, ps_6_5,
vs_6_0, vs_6_1, vs_6_2, vs_6_3, vs_6_4, vs_6_5,
gs_6_0, gs_6_1, gs_6_2, gs_6_3, gs_6_4, gs_6_5,
hs_6_0, hs_6_1, hs_6_2, hs_6_3, hs_6_4, hs_6_5,
ds_6_0, ds_6_1, ds_6_2, ds_6_3, ds_6_4, ds_6_5,
cs_6_0, cs_6_1, cs_6_2, cs_6_3, cs_6_4, cs_6_5,
lib_6_1, lib_6_2, lib_6_3, lib_6_4, lib_6_5,
ms_6_5,
as_6_5,
-Vd Disable validation
-Vi Display details about the include process.
-Vn <name> Use <name> as variable name in header file
-WX Treat warnings as errors
-Zi Enable debug information
-Zpc Pack matrices in column-major order
-Zpr Pack matrices in row-major order
-Zsb Compute Shader Hash considering only output binary
-Zss Compute Shader Hash considering source information

Optimization Options

Optimization Options
-O0 Optimization Level 0
-O1 Optimization Level 1
-O2 Optimization Level 2
-O3 Optimization Level 3 (Default)

Rewriter Options

Rewriter Options
-extract-entry-uniforms Move uniform parameters from entry point to global scope
-global-extern-by-default Set extern on non-static globals
-keep-user-macro Write out user defines after rewritten HLSL
-line-directive Add line directive
-remove-unused-functions Remove unused functions and types
-remove-unused-globals Remove unused static globals and functions
-skip-fn-body Translate function definitions to declarations
-skip-static Remove static functions and globals when used with -skip-fn-body
-unchanged Rewrite HLSL, without changes.

SPIR-V CodeGen Options

SPIR-V CodeGen Options
-fspv-debug=<value> Specify whitelist of debug info category (file -> source -> line, tool)
-fspv-extension=<value> Specify SPIR-V extension permitted to use
-fspv-flatten-resource-arrays Flatten arrays of resources so each array element takes one binding number
-fspv-reflect Emit additional SPIR-V instructions to aid reflection
-fspv-target-env=<value> Specify the target environment: vulkan1.0 (default) or vulkan1.1
-fvk-auto-shift-bindings Apply fvk-*-shift to resources without an explicit register assignment.
-fvk-b-shift <shift> <space> Specify Vulkan binding number shift for b-type register
-fvk-bind-globals <binding> <set> Specify Vulkan binding number and set number for the $Globals cbuffer
-fvk-bind-register <type-number> <space> <binding> <set> Specify Vulkan descriptor set and binding for a specific register
-fvk-invert-y Negate SV_Position.y before writing to stage output in VS/DS/GS to accommodate Vulkan's coordinate system
-fvk-s-shift <shift> <space> Specify Vulkan binding number shift for s-type register
-fvk-t-shift <shift> <space> Specify Vulkan binding number shift for t-type register
-fvk-u-shift <shift> <space> Specify Vulkan binding number shift for u-type register
-fvk-use-dx-layout Use DirectX memory layout for Vulkan resources
-fvk-use-dx-position-w Reciprocate SV_Position.w after reading from stage input in PS to accommodate the difference between Vulkan and DirectX
-fvk-use-gl-layout Use strict OpenGL std140/std430 memory layout for Vulkan resources
-fvk-use-scalar-layout Use scalar memory layout for Vulkan resources
-Oconfig=<value> Specify a comma-separated list of SPIRV-Tools passes to customize optimization configuration (see http://khr.io/hlsl2spirv#optimization)
-spirv Generate SPIR-V code

Utility Options

Utility Options
-dumpbin Load a binary file rather than compiling
-extractrootsignature Extract root signature from shader bytecode (must be used with /Fo <file>)
-getprivate <file> Save private data from shader blob
-P <value> Preprocess to file (must be used alone)
-Qembed_debug Embed PDB in shader container (must be used with /Zi)
-Qstrip_debug Strip debug information from 4_0+ shader bytecode (must be used with /Fo <file>)
-Qstrip_priv Strip private data from shader bytecode (must be used with /Fo <file>)
-Qstrip_reflect Strip reflection data from shader bytecode (must be used with /Fo <file>)
-Qstrip_rootsignature Strip root signature data from shader bytecode (must be used with /Fo <file>)
-setprivate <file> Private data to add to compiled shader blob
-setrootsignature <file> Attach root signature to shader bytecode
-verifyrootsignature <file> Verify shader bytecode with root signature

Warning Options

Warning Options
-W[no-]<warning> Enable/Disable the specified warning