質問編集履歴

1

文章を修正

2024/01/03 04:53

投稿

samidare_chan
samidare_chan

スコア17

test CHANGED
@@ -1 +1 @@
1
- 画像ロード中に同じ画像がロードされるの防ぐマルチスレッドの処理の作り方が知りたい
1
+ [ゲーム開発] 非同期のアセットのロード同じファイルがロードされない仕組みを作りたい
test CHANGED
@@ -1,67 +1,191 @@
1
1
  ##### 質問内容
2
+ 提示コードですが非同期処理を用いて、アセットを非同期でロードする処理なのですが現状3つの画像がそれぞれ順番にロードされてしまう原因が知りたいです。
2
- 提示コードの`///`部ですが、マルチスレッドの非同期処理で複数スレッ同じ画像をロードする際に、一つだけロードして残りのスレッドはロードしたものを使いまわすという処理で処理時間を高速化しいのですがそれを行うはどういたコードを書けばいいのでしょうか?
3
+ 提示コードの非同期コーでは同じ画像が来た時待機するという処理を行っているのすが、なぜか同期版と同じ秒数処理時間がかかっているめ同期処理と同じ
4
+ いると考えられます。
5
+
6
+ ##### 調べたこと
7
+ 非同期版と同期版両方のプログラムを記述して実行時間を計測
8
+
3
9
  ##### 知りたいこと
4
- 一つだけロードして残りのスレッドはロードしたものを使まわすとい処理を作る方法知りたい
10
+ 1,アセットのロードで同じファイルがロードされなにしなら非同期で行いたい
5
-
11
+ 2,そもそもどの非同期機能を使ってアセットロードを実装するのが定番なのか知りたいです。
12
+
6
- ##### 現状
13
+ ##### 提示コードについて
7
- 画像処理そもそもブロックしてしま形で、マルチスレッドの意味がありません
14
+ 提示コードはコンポーネント指向用いています。Componentを継承して非同期で行アセトロー処理を作り、それをGameObject(コンポーネントを挙動を管理する)で
8
-
15
+ 処理をまとめて、更に上のSceneで全部のGameObject の非同期処理を一つにまとめ、最後に全部が終わるまで待機してからUpdate()等を実行するという処理内容です。
16
+
17
+
9
- ##### 試したこと
18
+ ##### コンソール
10
- ミュータックスを使って処理をブロックするというやり方が一番近いと思うのですが提示コードでは画像のロード処理そもそもをブロックしてしまうのでこれでは非同期の意味がありません。
11
-
12
- ##### 環境
19
+ ```
13
- 言語: c++
20
+ new sprite
14
- ライブラリ: SDL_Image
21
+ found sprite
15
-
22
+ new sprite
16
-
23
+ 2000
17
-
24
+ ```
18
-
25
+
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; //ロードした画像リスト
37
-
47
+ std::vector<std::string> nowLoad; //現在ロード中の画像リスト
38
48
  std::mutex loadMutex;
49
+ std::condition_variable loadCV;
50
+
51
+
39
- //////////////////////////////////////////////////////////////////////////////////////////////////////
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
+ // 乱数生成器を初期化
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
+
73
+ /*##############################################################
74
+
75
+ # 非同期 版
76
+
77
+ # スプライトロード関数 
78
+ ※ テスト用のためロード時間を乱数でミリ秒待機
79
+ ##############################################################*/
40
80
  Sprite LoadSprite(const char* filePath)
41
81
  {
42
-
43
- //std::lock_guard<std::mutex> lock(loadMutex);
82
+ std::unique_lock<std::mutex> lock(loadMutex);
83
+
44
-
84
+ // 他のスレッドが同じ画像をロード中であれば待機
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
-
107
+
56
- std::cout<<"new sprite"<<std::endl;
108
+ std::cout << "new sprite" << std::endl;
109
+
110
+ // ロード中の画像リストに追加
57
- SDL_Surface* image = IMG_Load(filePath);
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
+ //ロード済みスプライトリストに追加
58
- sprites.push_back(Sprite{image,std::string(filePath)});
137
+ sprites.push_back(Sprite{ milliseconds, std::string(filePath) });
138
+
139
+ // 画像のロードが完了したことを通知
140
+ loadCV.notify_all();
59
141
 
60
142
  return sprites.back();
143
+
61
144
 
62
145
  }
146
+
63
- //////////////////////////////////////////////////////////////////////////////////////////////////////
147
+ /*##############################################################
148
+
64
-
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
  };
83
-
207
+ /*##############################################################
84
-
208
+ # GameObject 基底
209
+ ##############################################################*/
210
+ class GameObject
211
+ {
212
+ public:
213
+ GameObject()
214
+ {
215
+
216
+ }
217
+
218
+ std::vector<std::future<void>> getPreLoad()
219
+ {
220
+ std::vector<std::future<void>> futures;
221
+
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
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
+ ##############################################################*/
85
- class PlayerControl : public Component
273
+ class Control : public Component
86
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
 
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
+
98
- std::this_thread::sleep_for(std::chrono::seconds(1));
298
+ std::cout<<time.count()<<std::endl;
299
+ */
300
+
301
+ //非同期
302
+ auto startTime = std::chrono::high_resolution_clock::now();
303
+
99
- Sprite sp = LoadSprite("tile01.jpg");
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
+
100
- std::cout<<"PlayerControl"<<std::endl;
311
+ std::cout<<time.count()<<std::endl;
312
+
313
+
314
+
315
+
316
+
317
+
101
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
+ }
113
-
333
+ };
334
+
335
+ /*##############################################################
336
+ # Game シーン
337
+ ##############################################################*/
114
- virtual void (*PreLoad())() override
338
+ class Game : public Scene
115
- {
339
+ {
340
+ public:
116
- return []() ->void
341
+ Game(): Scene()
117
- {
342
+ {
118
- std::this_thread::sleep_for(std::chrono::seconds(1));
343
+ gameObjects.push_back(new Player());
119
- Sprite sp = LoadSprite("tile01.jpg")
344
+ }
120
- std::cout<<"EnemyControl"<<std::endl;
121
-
122
- };
345
+ };
123
- }
346
+
124
- };
125
347
 
126
348
 
127
349
 
128
350
  int main()
129
351
  {
130
- Engine::Context::Init("Game",glm::ivec2(860,480));
352
+
131
-
132
- std::vector<std::future<void>> futures;
133
-
134
- PlayerControl player;
353
+ Game *g = new Game();
135
- EnemyControl enemy;
354
+
136
-
137
- futures.push_back(std::async(std::launch::async,enemy.PreLoad()));
138
- futures.push_back(std::async(std::launch::async,player.PreLoad()));
139
-
140
- for(std::future<void>& f : futures)
141
- {
142
- f.wait();
143
- }
144
-
145
-
146
- Engine::Context::Finalize();
355
+ g->Preaload();
356
+
357
+
358
+
147
359
  return 0;
148
360
  }
149
361
  ```