質問するログイン新規登録

質問編集履歴

1

文章を修正

2024/01/03 04:53

投稿

samidare_chan
samidare_chan

スコア18

title CHANGED
@@ -1,1 +1,1 @@
1
- 画像ロード中に同じ画像がロードされるの防ぐマルチスレッドの処理の作り方が知りたい
1
+ [ゲーム開発] 非同期のアセットのロード同じファイルがロードされない仕組みを作りたい
body CHANGED
@@ -1,67 +1,191 @@
1
1
  ##### 質問内容
2
- 提示コードの`///`部ですが、マルチスレッドの非同期処理で複数のスレッドが同じ画像をロードする際に、一つだけロードして残りのスレッドはロードしたものを使いまわすという処理で処理時間を高速化したいのですがそれを行うにはどういったコードを書けばいいのでしょうか?
3
- ##### 知りたいこと
4
- 一つだけロード残りのスレッドロードしたものを使いわすとい処理を作る方法が知りたい
2
+ 提示コードですが非同期処理を用い、アセトを非同期でローする処理なのですが現状3つの画像がそれぞれ順番にロードされてしまう原因が知りたいです。
3
+ 提示コードの非同期版のコードでは同じ画像が来た時待機するという処理を行っているのですが、なぜか同期版と同じ秒数処理時間がかかっているため同期処理と同じになって
4
+ いると考えられます。
5
5
 
6
- ##### 現状
6
+ ##### 調べたこと
7
- 画像処理をそもそもブックしてしまう形で、マルチスレッドの意味がありません
7
+ 非同期版と同期版両方のプグラムを記述して実行時間を計測
8
8
 
9
- ##### 試したこと
9
+ ##### 知りこと
10
- ミュータクスを使って処理をブロックするというやり方が一番近いと思うですが提示コードでは画像のロード処理そもそもをブロックしてしまうのでこでは非同期の意味がありません
10
+ 1,アセードで同じファイルがロードないようにしながら非同期で行いたい
11
+ 2,そもそもどの非同期機能を使ってアセットロードを実装するのが定番なのか知りたいです。
11
12
 
12
- ##### 環境
13
+ ##### 提示コードについて
13
- 言語: c++
14
+ 提示コードはコンポーネント指向を用いています。Componentを継承して非同期で行うアセットロード処理を作り、それをGameObject(コンポーネントをの挙動を管理する)で
14
- ライブラリ: SDL_Image
15
+ 処理をまとめて、更に上のSceneで全部のGameObject の非同期処理を一つにまとめ、最後に全部が終わるまで待機してからUpdate()等を実行するという処理内容です。
15
16
 
16
17
 
18
+ ##### コンソール
19
+ ```
20
+ new sprite
21
+ found sprite
22
+ new sprite
23
+ 2000
24
+ ```
17
25
 
18
-
19
- ##### 提示コード
26
+ ##### ソースコード
20
27
  ```cpp
21
- #include <Context.hpp>
22
28
  #include <vector>
23
29
  #include <future>
24
30
  #include <iostream>
25
31
  #include <glm/glm.hpp>
26
32
  #include <thread>
27
33
  #include <mutex>
34
+ #include <condition_variable>
35
+ #include <chrono>
36
+ #include <random>
37
+ #include <thread>
28
38
 
29
39
  struct Sprite
30
40
  {
31
- SDL_Surface* handle;
41
+ int handle;
32
42
  std::string path;
33
43
  };
34
44
 
35
45
 
36
- std::vector<Sprite> sprites;
46
+ std::vector<Sprite> sprites; //ロードした画像リスト
47
+ std::vector<std::string> nowLoad; //現在ロード中の画像リスト
48
+ std::mutex loadMutex;
49
+ std::condition_variable loadCV;
37
50
 
51
+
52
+ /*##############################################################
53
+ # 乱数取得
54
+ ##############################################################*/
55
+ int GetRandom(int rand)
56
+ {
57
+ // 現在の時間を取得して種(seed)として使用する
58
+ auto now = std::chrono::high_resolution_clock::now();
59
+ auto seed = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
60
+
61
+ // 乱数生成器を初期化
38
- std::mutex loadMutex;
62
+ std::mt19937_64 generator(seed);
63
+
64
+ // ミリ秒単位の乱数を生成
65
+ std::uniform_int_distribution<int> distribution(0, rand);
66
+ int random_number = distribution(generator);
67
+
68
+ //std::cout << "ミリ秒単位の乱数: " << random_number << std::endl;
69
+
70
+ return random_number;
71
+ }
72
+
39
- //////////////////////////////////////////////////////////////////////////////////////////////////////
73
+ /*##############################################################
74
+
75
+ # 非同期 版
76
+
77
+ # スプライトロード関数 
78
+ ※ テスト用のためロード時間を乱数でミリ秒待機
79
+ ##############################################################*/
40
80
  Sprite LoadSprite(const char* filePath)
41
81
  {
82
+ std::unique_lock<std::mutex> lock(loadMutex);
42
83
 
43
- //std::lock_guard<std::mutex> lock(loadMutex);
84
+ // 他のスレッドが同じ画像をロード中であれば待機
44
-
45
-
46
- for(const Sprite& s : sprites)
85
+ loadCV.wait(lock, [&filePath]()
47
86
  {
48
- if(s.path == std::string(filePath))
87
+ for(const std::string& path : nowLoad)
49
88
  {
89
+ if (path == std::string(filePath))
90
+ {
91
+ return false;
92
+ }
93
+ }
94
+
95
+ return true;
96
+ });
97
+
98
+ //スプライトリストから取り出す
99
+ for (const Sprite& s : sprites)
100
+ {
101
+ if (s.path == std::string(filePath))
102
+ {
50
- std::cout<<"found sprite"<<std::endl;
103
+ std::cout << "found sprite" << std::endl;
51
104
  return s;
52
105
  }
53
106
  }
54
-
55
-
56
- std::cout<<"new sprite"<<std::endl;
57
- SDL_Surface* image = IMG_Load(filePath);
58
- sprites.push_back(Sprite{image,std::string(filePath)});
59
107
 
108
+ std::cout << "new sprite" << std::endl;
109
+
110
+ // ロード中の画像リストに追加
111
+ nowLoad.push_back(std::string(filePath));
112
+ lock.unlock(); // ロックを解除して他のスレッドがロードを開始できるようにする
113
+
114
+ //画像ロード
115
+ int milliseconds = 1000;
116
+ //int milliseconds = GetRandom(1000);
117
+ std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
118
+
119
+
120
+ lock.lock(); // ロックを再取得して安全に状態を更新する
121
+
122
+
123
+ //ロードが終わったためリストから削除
124
+ int i = 0;
125
+ for(const std::string& s : nowLoad)
126
+ {
127
+ if(s == filePath)
128
+ {
129
+ break;
130
+ }
131
+
132
+ i++;
133
+ }
134
+ nowLoad.erase(nowLoad.begin() + i);
135
+
136
+ //ロード済みスプライトリストに追加
137
+ sprites.push_back(Sprite{ milliseconds, std::string(filePath) });
138
+
139
+ // 画像のロードが完了したことを通知
140
+ loadCV.notify_all();
141
+
60
142
  return sprites.back();
143
+
61
144
 
62
145
  }
63
- //////////////////////////////////////////////////////////////////////////////////////////////////////
64
146
 
147
+ /*##############################################################
148
+
149
+ # 同期 版
150
+
151
+ # スプライトロード関数 
152
+ ※ テスト用のためロード時間を乱数でミリ秒待機
153
+ ##############################################################*/
154
+ Sprite Sync_LoadSprite(const char* filePath)
155
+ {
156
+ //スプライトリストから取り出す
157
+ for (const Sprite& s : sprites)
158
+ {
159
+ if (s.path == std::string(filePath))
160
+ {
161
+ std::cout << "found sprite" << std::endl;
162
+ return s;
163
+ }
164
+ }
165
+
166
+ std::cout << "new sprite" << std::endl;
167
+
168
+ //画像ロード
169
+ int milliseconds =1000;
170
+ //int milliseconds = GetRandom(1000);
171
+ std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
172
+
173
+
174
+ //ロード済みスプライトリストに追加
175
+ sprites.push_back(Sprite{ milliseconds, std::string(filePath) });
176
+
177
+ return sprites.back();
178
+
179
+
180
+ }
181
+
182
+
183
+
184
+
185
+
186
+ /*##############################################################
187
+ # Component 基底
188
+ ##############################################################*/
65
189
  class Component
66
190
  {
67
191
  public:
@@ -80,70 +204,158 @@
80
204
  }
81
205
 
82
206
  };
207
+ /*##############################################################
208
+ # GameObject 基底
209
+ ##############################################################*/
210
+ class GameObject
211
+ {
212
+ public:
213
+ GameObject()
214
+ {
83
215
 
216
+ }
84
217
 
218
+ std::vector<std::future<void>> getPreLoad()
219
+ {
220
+ std::vector<std::future<void>> futures;
221
+
85
- class PlayerControl : public Component
222
+ for(Component *c : components)
223
+ {
224
+ futures.push_back(std::async(std::launch::async,c->PreLoad()));
225
+ }
226
+
227
+ return futures;
228
+ }
229
+
230
+ void Update()
231
+ {
232
+
233
+ }
234
+
235
+ void Render()
236
+ {
237
+
238
+ }
239
+
240
+ std::vector<Component*> components;
241
+ };
242
+
243
+ /*##############################################################
244
+ # Scene 基底
245
+ ##############################################################*/
246
+ class Scene
86
247
  {
248
+ public:
249
+
250
+ std::vector<GameObject*> gameObjects;
251
+ void Preaload()
252
+ {
253
+ std::vector<std::future<void>> futures;
254
+ for(GameObject *g : gameObjects)
255
+ {
256
+ for(std::future<void> &f : g->getPreLoad())
257
+ {
258
+ futures.push_back(std::move(f));
259
+ }
260
+ }
261
+
262
+ for(std::future<void> &f : futures)
263
+ {
264
+ f.wait();
265
+ }
266
+ }
267
+
268
+ };
269
+
270
+ /*##############################################################
271
+ # Control コンポーネント
272
+ ##############################################################*/
273
+ class Control : public Component
274
+ {
87
275
  public:
88
- PlayerControl(): Component()
276
+ Control(): Component()
89
277
  {
90
278
 
91
279
  }
92
280
 
93
- virtual void (*PreLoad())() override
281
+ void (*PreLoad())() override
94
282
  {
95
283
  return []() ->void
96
284
  {
97
285
 
98
- std::this_thread::sleep_for(std::chrono::seconds(1));
99
- Sprite sp = LoadSprite("tile01.jpg");
100
- std::cout<<"PlayerControl"<<std::endl;
101
286
 
287
+ // 同期
288
+ /*
289
+ auto startTime = std::chrono::high_resolution_clock::now();
290
+
291
+ Sprite sp1 = Sync_LoadSprite("tile1.jpg");
292
+ Sprite sp2 = Sync_LoadSprite("tile1.jpg");
293
+ Sprite sp3 = Sync_LoadSprite("tile2.jpg");
294
+
295
+ auto endTime = std::chrono::high_resolution_clock::now();
296
+ auto time = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
297
+
298
+ std::cout<<time.count()<<std::endl;
299
+ */
300
+
301
+ //非同期
302
+ auto startTime = std::chrono::high_resolution_clock::now();
303
+
304
+ Sprite sp1 = LoadSprite("tile1.jpg");
305
+ Sprite sp2 = LoadSprite("tile1.jpg");
306
+ Sprite sp3 = LoadSprite("tile2.jpg");
307
+
308
+ auto endTime = std::chrono::high_resolution_clock::now();
309
+ auto time = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
310
+
311
+ std::cout<<time.count()<<std::endl;
312
+
313
+
314
+
315
+
316
+
317
+
318
+
102
319
  };
103
320
  }
104
321
  };
105
322
 
323
+ /*##############################################################
324
+ # Player ゲームオブジェクト
325
+ ##############################################################*/
106
- class EnemyControl : public Component
326
+ class Player : public GameObject
107
327
  {
108
328
  public:
109
- EnemyControl(): Component()
329
+ Player(): GameObject()
110
330
  {
111
-
331
+ components.push_back(new Control());
112
332
  }
333
+ };
113
334
 
335
+ /*##############################################################
336
+ # Game シーン
337
+ ##############################################################*/
114
- virtual void (*PreLoad())() override
338
+ class Game : public Scene
339
+ {
340
+ public:
341
+ Game(): Scene()
115
342
  {
116
- return []() ->void
117
- {
118
- std::this_thread::sleep_for(std::chrono::seconds(1));
343
+ gameObjects.push_back(new Player());
119
- Sprite sp = LoadSprite("tile01.jpg")
120
- std::cout<<"EnemyControl"<<std::endl;
121
-
122
- };
123
344
  }
124
345
  };
125
346
 
126
347
 
127
348
 
349
+
128
350
  int main()
129
351
  {
130
- Engine::Context::Init("Game",glm::ivec2(860,480));
131
352
 
132
- std::vector<std::future<void>> futures;
353
+ Game *g = new Game();
133
354
 
134
- PlayerControl player;
135
- EnemyControl enemy;
355
+ g->Preaload();
136
356
 
137
- futures.push_back(std::async(std::launch::async,enemy.PreLoad()));
138
- futures.push_back(std::async(std::launch::async,player.PreLoad()));
139
357
 
140
- for(std::future<void>& f : futures)
141
- {
142
- f.wait();
143
- }
144
358
 
145
-
146
- Engine::Context::Finalize();
147
359
  return 0;
148
360
  }
149
361
  ```