自分用メモ
メモ20200611 – 関数定義をわざわざ int (func)(){}とするケース
- 実は int(func)(){}も関数定義になる
- int(func)(){}と書かないとけない時は#define func(var) というマクロが定義されている時にマクロ展開されないようにするため。例:numeric_limits::max
メモ20200608 – ビットマスクを扱う時の型について
ビット幅を意識しないで済む場合にインタフェースをintで統一すると利用者はキャストしないで済むというメリットがあり、時折ビットマスクもintで扱っている場合があるが僕はそれには反対する。
マスクを扱った処理としてintから64bitに拡張したい場合にint64_t/uint64_tにキャストした時に32bit型の最上位ビットが立っているとそれが64bitの最上位ビットとなってしまうというのはビットを扱っている場合に意図するところではないからである。int型なのだから当たり前ではあるがビット操作をする場合にはこういう不都合がある。
メモ20200407 – 固定小数点
固定小数点とは小数点の位置が固定されている表現。
C 言語での表現
- 整数型を使って自分で整数部、小数部を管理する必要がある
- 参考:http://www.sage-p.com/compone/toda/fixdec.htm
メモ20200328 – メモ
- POD = trivial + standard layout
- trivialはaccess modifierだけ持っている
- standard layoutはデストラクタの宣言のみ?ok
参考:https://stackoverflow.com/questions/6496545/trivial-vs-standard-layout-vs-pod - standard layoutは最初のメンバ変数型のreinterpret_castで最初のメンバに安全にキャストできる。
参考:9.2-15 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htm - ユーザー定義コンストラクタを定義すると非ユーザー定義デフォルトコンストラクタは明示的にコールされない限りはコンパイラによって生成されない。つまり、翻訳単位で見た時にデフォルトコンストラクタを持たないため std::is_trivial にならない。非ユーザ定義デフォルトコンストラクタの定義を強制するために T() = default; を記述する。これにより、std::is_trivial<T>::value = false になるのを回避できる。
参考:https://stackoverflow.com/questions/23007358/are-constructors-specified-with-the-default-keyword-trivial
参考:https://stackoverflow.com/questions/20828907/the-new-syntax-default-in-c11
参考:https://en.cppreference.com/w/cpp/language/default_constructor
メモ20200322 – メモ
- ダンプ関数はopcodeをインデックスとする関数ポインタのテーブルにして引けばいい。
メモ20200313 – non-type parameterを使う事について
- コンストラクタの引数に渡そうが個別の型になる
- Temp<3>のインスタンスにTemp<4>のインスタンスはコピーできない。
- Temp<3>のインスタンスにTemp<3>のインスタンスは代入できる
- コンストラクタにnon-type parameterを渡す事の是非は?私はそれこそ個別の型として代入不可能な別のtypeを作りたい時は良いと思う。non-type parameterをenum classとかにして。enum classの列挙子毎に型にするとか。
メモ20200313 – レジスタ表現の参考実装
実装例: arm intrinsics
https://github.com/intel/ARM_NEON_2_x86_SSE/blob/master/NEON_2_SSE.h
- uint64_t vget_lane_u64()
64bitレジスタの値を取得
- vld1_dup_u64(uint64_t const * ptr)
64bitデータをneonレジスタにロード - vget_low(), vget_high()
ローサイド、ハイサイドをそれぞれ取得
メモ20200312 – setter/getter の考え方
参考:https://www.artima.com/intv/goldilocks.html#part3
- classの存在意義というのはinvariabntにある
- 例:vectorの例だとストレージのポインタと配列サイズをinvariantという
- classはこのinvariantを管理する事に意味がある
- インタフェースはこのinvariantが壊れない様なインタフェースにすべき
- 例えば全てのメンバ変数がどんな値でも取れる時はclassにする必要がない。
- classが必要になる場合はname, addressがデータとしてあった場合にaddressが有効なアドレスかどうかをチェックしないといけない場合などはそのメソッドを追加する。
その他参考
https://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197?pgno=1
templateのHolderクラスとダウンキャストについて
- http://www.java2s.com/Tutorial/Cpp/0260__template/templateholderclass.htm
- https://stackoverflow.com/questions/20518237/c-design-cast-from-base-to-derived-class-with-no-extra-data-members
- PODを派生クラスへキャスト
https://stackoverflow.com/questions/23630513/casting-a-pod-struct-into-a-derived-type/23631941 - 検索ワード
安全にアクセスする実装例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
struct Data { int data; }; class Holder { public: Holder(Data &data) : m_Data(data) { } protected: Data& m_Data; }; class DataAccessor : private Holder { public: // 継承コンストラクタ // https://ja.cppreference.com/w/cpp/language/using_declaration using Holder::Holder; DataAccessor& SetData(int val) { m_Data.data = val; return *this; } int GetData() const { return m_Data.data; } }; int main() { Data data; auto accsessor = DataAccessor(data).SetData(1); std::cout << accsessor.GetData() << std::endl; return 0; } |
メモ20200307
- レジスタファイルの全メンバ変数に対して便利にmovを発行できないか?
- vectorにpushしてイテレータでゲットする様な事ができると良さそうでは?
- TODO: イテレータ
- テンプレートクラスが渡されて最初のメンバのベースクラスにダイレクトキャストしているがこれ何とかならんのか?
- 下記でDrawRegisterFileとPixelRegisterFileを受け取ったビルダーが先頭をRegisterStorage<false>にキャストしてアクセスして大丈夫なのか?という話。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#include <cstdint> template<bool IsPaired> struct RegisterStorage { uint32_t storage[IsPaired ? 2 : 1]; }; template<bool IsPaired> struct Register : RegisterStorage<IsPaired> { void SetRaw(int val) { this->storage[0] = val; } int GetRaw() const { return this->storage[0]; } }; struct DrawRegisterType { typedef Register<true> SprgPointer; typedef Register<false> AttribOffset; }; struct PixelRegisterType { typedef Register<false> Width; typedef Register<false> Height; }; struct DrawRegisterFile { DrawRegisterType::SprgPointer sprgPointer; DrawRegisterType::AttribOffset attribOffset; }; struct PixelRegisterFile { PixelRegisterType::Width width; PixelRegisterType::Height height; }; |
- 結局メンバ変数でイテレートする機能があればいいんあがどう実装するのか?
- 例えばこういう解法もあるかもしれないけどこれはキャストしているのと同じ。サイズは同じなのが保証されるから多少は安全。
123456union DrawRegisterFileStorage{RegisterStorage<false> storages[3];DrawRegisterFile drawRegFile;};static_assert(sizeof(DrawRegisterFileStorage) == sizeof(DrawRegisterFile)); - アクセサからstorage32にキャストすれば取得個所を1箇所にできるのでそうするか…。
メモ 20200305
- 独自のレジスタファイル型を定義し、そのストレージは SSBO として使える
- レジスタファイル内のメンバ変数をイテレータで巡回して値を取得したい
- -> メンバ変数毎に mov を発行するため
- イテレータでアクセスが想定されるメソッドは
- GetValue()
- ::RegisterIndex
- IsPaird()
- GetValue64() : 64bit 値で取得
- 考え方
- mov 時はただの 32bit or 64bit のデータが取得できればいいので型はどうでもいい
- その他
- パラメータはフルのレジスタファイルに固めてメソッドチェインで値をセットしている。フルレジスタファイルが取得できる。そこから更にmovなら必要なパラメータを拾ってmovを追加しないといけない。ldmの場合もレジスタファイルマスクを作らないといけない。問題点はレジスタファイルを作るためにパラメータ選択、mov生成、ldm生成のためにまたパラメータ選択をしているのが無駄に感じる。
- 先に選択して値のセットをし、mov生成、ldm生成できないか?。
- 取り敢えずCRTPで子クラスに実装したメソッドをコールして*derivedを返すメソッドをベースクラスに実装すればレジスタに値を入れてかつメソッドチェインを作れそう。
- 課題
- その場合ストレージはどこに持たせるか?
- レジスタファイルクラスである事をそのベースクラスに教えてやらないといけないけどどうやってレジスタファイルクラスであるのを保証させるか?
- あるクラス内に定義されたレジスタクラスがCRTPのベースクラスから呼べればいいだけなので保証とかは要らないかも?
- やっぱあるパラメータだけいらんわーの場合のために手動でオフレル仕組みは必要。
- 課題
疑似コード 20200303
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
struct RegisterFile { RegisterPair sprgPtr0; RegisterPair sprgPtr1; Register attribOffset; }; enum class RegisterIndex { SprdPtr0 = 0, SprgPtr1 = 2, AttribOffset = 3; }; // Register から RegisterIndex を無くして // enum でも問題無さそう。 // ただ、レジスタペアは2bitオンにして欲しい。 RegisterFileMask.Set(RegisterIndex::SprgPtr0); |
疑似コード
.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
#include <cstdint> #include <cstring> #include <type_traits> namespace reg5 { typedef int Result; typedef uint32_t RegisterData; constexpr int DrawRegCount = 32; // レジスタデータへのアクセサ template<int TCount> class RegisterDeclaration { private: RegisterData storage[TCount]; public: typedef decltype( RegisterDeclaration<TCount>::storage ) StorageType; //using StorageType = decltype(typename RegisterDeclaration<TCount>::storage); //typedef decltype( storage ) StorageType; void SetRaw( StorageType src ) { memcpy( ( void* )this->storage, src, TCount * sizeof( uint32_t ) ); } const StorageType& GetRaw() { return this->storage; } static const int Count = TCount; }; template<int TCount> const int RegisterDeclaration<TCount>::Count; template<int TRegisterIndex, typename Tag = void> class RegisterPair : private RegisterDeclaration<2> { public: typedef RegisterDeclaration<2> RegisterDeclarationType; static const int RegisterIndex = TRegisterIndex; static const int Next = RegisterDeclarationType::Count + TRegisterIndex; static const int Count = RegisterDeclarationType::Count; using StorageType = RegisterDeclarationType::StorageType; void Set( uint64_t value ) { this->SetRaw( ( StorageType& )value ); } uint64_t Get() { auto& ret = this->GetRaw(); return *reinterpret_cast< const uint64_t* >( &ret[0] ); } }; // detail template<int TRegCount> class RegisterFileBuilderDecl { public: static const int RegisterCount = TRegCount; // cpp に実装したい virtual void DisableParameter( int registerIndex ) = 0; int GetRegisterMask() { return m_Mask[0]; } // ldm 個数を取得 Result CalculateLdmCount( int* pOut ) { int ret = 0; // LSB から最初の mask index を取得 int regStartIndex; // DrawRegFileRegCount まで 16 個ずつ捜査 //for( regStartIndex < DrawRegFileRegCount ) { int regIndex; ++ret; } *pOut = ret; return Result(); } // ldm マスクを取得 Result CalculateLdmMask( int* pMask, int ldmIndex ) { // ldmIndex 内の有効になっている mask を取得 // TODO: parameterIndex のレジスタが register pair なのかどうか知りたい。 // -> TODO: struct の typedef の上から順番に捜査できないか目論んでいる。 int paramIndex; return Result(); } // ldm マスクから計算可能だが一応用意しておく。 Result CalculateRegisterSetLength( int* pLength, int ldmIndex ) { return Result(); } // ldm start index Result CalculateLdmStartIndex( int* pRegIndex, int ldmIndex ) { return Result(); } // 指定 ldm インデックスのレジスタセットを出力する Result Build( RegisterData* pRegisters, int length, int ldmIndex ) { return Result(); } void SetRegMask( int registerIndex ) { m_Mask[0] = registerIndex; } private: uint32_t m_Mask[RegisterCount / 32]; }; class DrawRegisterFileBuilderImpl : public RegisterFileBuilderDecl<DrawRegCount> { public: void DisableParameter( int registerIndex ) override; }; // オフセット等を取得できる構造体 // - register index, register count を取得できる。 struct DrawRegFile { typedef uint32_t Registermask[3]; typedef RegisterPair<0, DrawRegFile> SprgPointer; typedef RegisterPair<SprgPointer::Next, DrawRegFile> ShaderTablePointer; SprgPointer sprgPointer; ShaderTablePointer srtPointer; ShaderTablePointer dummy[14]; }; class DrawRegisterFileBuilder3 : private DrawRegFile { public: //DrawRegisterFileBuilder& SetDrawRegisterFileParameterMask( DrawRegisterFileParameterMask ); DrawRegisterFileBuilder3& SetSprgPointer( uint64_t val ) { this->sprgPointer.Set( val ); return *this; } DrawRegisterFileBuilder3& ShaderTablePointer( uint64_t val ) { this->srtPointer.Set( val ); return *this; } uint64_t GetSprgPointer() { return this->sprgPointer.Get(); } uint64_t GetShaderTablePointer() { return this->srtPointer.Get(); } // NOTE: impl にしている余計な機能は一旦無くてもいいかも。 DrawRegisterFileBuilder3& DisableParameter( int registerIndex ) { m_Impl.DisableParameter( registerIndex ); return *this; } int GetRegisterMask() { return m_Impl.GetRegisterMask(); } private: DrawRegisterFileBuilderImpl m_Impl; }; } |
.cpp
1 2 3 4 5 6 |
#include "Register5.h" void reg5::DrawRegisterFileBuilderImpl::DisableParameter( int registerIndex ) { SetRegMask( registerIndex ); } |
オールド
- アライン計算に必要な物
- format
- tiling
- タイリングも必要なんだっけ?
- plane index
- 書き出し時は plane によってアラインが異なるため
- read 時は plane によらず同じ
- usage
- 圧縮フラグ
- row stride 計算に必要な物
- format
- ピクセルサイズ
- コンポーネントサイズ
- ブロックサイズ
- Linear
- 非圧縮 ⇒ ピクセルサイズで並べただけ
- 圧縮 ⇒ 圧縮ブロックを並べる
- Block
- 非圧縮 ⇒ ピクセルサイズを 16×16 のブロックで並べる
- 圧縮 ⇒ 圧縮ブロックを 4×4 で並べる
- TODO: YUV のブロックサイズが分からない。
- Linear
- tiling
- plane index
- usage
- read/write で変わる可能性があるから
- 圧縮フラグ
- format/usage/圧縮フラグから求まるアライメントの情報も必要
- format
YUV フォーマット
YUV444
Y 面について UV 面を一切間引かないフォーマット。
DXGI_FORMAT_Y410 が対応する。
YUV422
UV 面について水平方向に間引いたフォーマットの事を 4:2:2 と言う。
4 つの Y 画素について 2 つの U と V の画素がある。
YUV420
UV 面について水平・垂直方向に間引いて UV 面の解像度を 1/2 に落としたフォーマットの事を 4:2:0 と言う。
420 は特に意味は無く慣例的に 422 特別するための記号と考えて良い。
DXGI_FORMAT_420_OPAQUE では 8bit
コメントを残す