Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
TexDX9.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: TexDX9.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Direct3D Texture Cache
13 */
14 
15 #include "MemDebug.h"
16 #include "TexDX9.h"
17 #include "VideoDX9.h"
18 #include "Bitmap.h"
19 #include "Color.h"
20 
21 // +--------------------------------------------------------------------+
22 
23 void Print(const char* fmt, ...);
24 void VideoDX9Error(const char* msg, HRESULT err);
25 
26 #define TEXDX9_VERBOSE 0
27 #define DX9_TEXTURE_CACHE_SIZE 256
28 
29 #ifndef RELEASE
30 #define RELEASE(x) if (x) { x->Release(); x=NULL; }
31 #endif
32 
33 // +--------------------------------------------------------------------+
34 
35 void
37 {
39 }
40 
41 // +--------------------------------------------------------------------+
42 
43 void
45 {
47  bitmap_id = 0;
48  used_last = 0;
49  last_modified = 0;
50 }
51 
52 // +--------------------------------------------------------------------+
53 
55 : video(v), count(0), mru(0)
56 {
57  d3d = video->Direct3D();
58  d3ddevice = video->D3DDevice();
59 
60  // clear the texture cache
61  cache = new(__FILE__,__LINE__) TexCacheDX9Entry[DX9_TEXTURE_CACHE_SIZE];
62  vidmem = video->VidMemFree();
63 }
64 
66 {
67  int used = 0;
68 
69  if (cache) {
70  for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
71  if (cache[i].bitmap_id)
72  used++;
73  cache[i].Unload();
74  }
75 
76  delete [] cache;
77  cache = 0;
78  }
79 
80  Print("TexCacheDX9: final used count = %d\n", used);
81 }
82 
83 // +--------------------------------------------------------------------+
84 
85 bool
87 {
88  if (!d3ddevice) return false;
89 
90  HRESULT hr = D3D_OK;
91 
92  // create the texture, if necessary
93  if (!entry->texture || entry->bitmap_id != bmp->Handle()) {
94  hr = d3ddevice->CreateTexture(bmp->Width(),
95  bmp->Height(),
96  1, // one mip level
97  0, // no specific usage
98  D3DFMT_A8R8G8B8, // format matching Color::rgba
99  D3DPOOL_MANAGED,
100  &entry->texture,
101  0);
102 
103  if (FAILED(hr) || !entry->texture) {
104  VideoDX9Error("LoadTexture - could not create texture", hr);
105  return false;
106  }
107  }
108 
109  // lock the surface for writing
110  D3DLOCKED_RECT locked_rect;
111  hr = entry->texture->LockRect(0, &locked_rect, 0, 0);
112 
113  if (FAILED(hr)) {
114  VideoDX9Error("LoadTexture - could not lock texture surface", hr);
115  entry->Unload();
116  return false;
117  }
118 
119  // load the bitmap into the texture surface
120  for (int i = 0; i < bmp->Height(); i++) {
121  BYTE* src = (BYTE*) (bmp->HiPixels() + i * bmp->Width());
122  BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
123 
124  CopyMemory(dst, src, bmp->Width() * sizeof(Color));
125  }
126 
127  // unlock the surface
128  entry->texture->UnlockRect(0);
129 
130  entry->last_modified = bmp->LastModified();
131  vidmem = video->VidMemFree();
132  return true;
133 }
134 
135 // +--------------------------------------------------------------------+
136 
137 void
138 TexCacheDX9::CreateNormalMap(int index, float amp)
139 {
140  if (d3ddevice && cache[index].texture && !cache[index].normal) {
141  HRESULT hr = D3D_OK;
142  TexCacheDX9Entry* entry = &cache[index];
143  Bitmap* bmp = Bitmap::GetBitmapByID(entry->bitmap_id);
144 
145  IDirect3DTexture9* normal_map = 0;
146 
147  // create the normal map texture
148  hr = d3ddevice->CreateTexture(bmp->Width(),
149  bmp->Height(),
150  1, // one mip levels
151  0, // no specific usage
152  D3DFMT_A8R8G8B8, // format matching Color::rgba
153  D3DPOOL_MANAGED,
154  &normal_map,
155  0);
156 
157  if (FAILED(hr) || !normal_map) {
158  VideoDX9Error("CreateNormalMap - could not create texture", hr);
159  return;
160  }
161 
162  D3DXComputeNormalMap(normal_map, entry->texture ,NULL, 0, D3DX_CHANNEL_RED, amp);
163 
164  entry->texture->Release();
165  entry->texture = normal_map;
166  entry->normal = true;
167 
168  // The D3DX function destroys the alpha channel data
169  // when it builds the normal map. We want the original
170  // height data stored in the alpha channel, so we need
171  // to add it back in now.
172 
173  // lock the surface for writing
174  D3DLOCKED_RECT locked_rect;
175  hr = normal_map->LockRect(0, &locked_rect, 0, 0);
176 
177  if (FAILED(hr)) {
178  VideoDX9Error("CreateNormalMap - could not insert height channel", hr);
179  return;
180  }
181 
182  // load the bitmap into the texture surface
183  for (int i = 0; i < bmp->Height(); i++) {
184  BYTE* src = (BYTE*) (bmp->HiPixels() + i * bmp->Width());
185  BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
186 
187  src += 2; // red channel
188  dst += 3; // alpha channel
189 
190  for (int n = 0; n < bmp->Width(); n++) {
191  *dst = *src;
192 
193  dst += 4;
194  src += 4;
195  }
196  }
197 
198  // unlock the surface
199  normal_map->UnlockRect(0);
200  }
201 }
202 
203 // +--------------------------------------------------------------------+
204 
205 IDirect3DTexture9*
207 {
208  int avail = -1;
209 
210  if (!bmp)
211  return 0;
212 
213  // check most recently used:
214  if (cache[mru].bitmap_id == bmp->Handle()) {
215  cache[mru].used_last = frame_number;
216 
217  // need to refresh?
218  if (cache[mru].last_modified < bmp->LastModified()) {
219  LoadTexture(bmp, &cache[mru]);
220  cache[mru].normal = false;
221  }
222 
223  return cache[mru].texture;
224  }
225 
226  // find cache entry, or first free:
227  for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++)
228  if (cache[i].bitmap_id == bmp->Handle()) {
229  cache[i].used_last = frame_number;
230  mru = i;
231 
232  // need to refresh?
233  if (cache[mru].last_modified < bmp->LastModified()) {
234  LoadTexture(bmp, &cache[mru]);
235  cache[mru].normal = false;
236  }
237 
238  return cache[i].texture;
239  }
240  else if (avail < 0 && cache[i].bitmap_id == 0)
241  avail = i;
242 
243  // no free space
244  if (avail < 0)
245  if (FreeUpCache())
246  return FindTexture(bmp);
247  else
248  return 0;
249 
250  TexCacheDX9Entry* entry = &cache[avail];
251  entry->bitmap_id = bmp->Handle();
252  entry->used_last = frame_number;
253 
254  if (LoadTexture(bmp, entry)) {
255 #if TEXDX9_VERBOSE
256  Print(" Tex %3d: id=%2d, size=%3d, type=%d, hTex=%3d, frame=%6d vmf=%8d\n",
257  avail, bmp->Handle(), bmp->Width(), bmp->Type(),
258  cache[avail].texture, cache[avail].used_last, vidmem);
259 #endif
260  mru = avail;
261  cache[mru].normal = false;
262  return entry->texture;
263  }
264  else {
265  // failed to load texture,
266  // erase cache entry:
267  entry->Unload();
268  }
269 
270  return 0;
271 }
272 
273 // +--------------------------------------------------------------------+
274 
275 IDirect3DTexture9*
277 {
278  int avail = -1;
279 
280  if (!bmp)
281  return 0;
282 
283  // check most recently used:
284  if (cache[mru].bitmap_id == bmp->Handle()) {
285  cache[mru].used_last = frame_number;
286 
287  // need to refresh?
288  if (cache[mru].last_modified < bmp->LastModified()) {
289  LoadTexture(bmp, &cache[mru]);
290  cache[mru].normal = false;
291  }
292 
293  if (!cache[mru].normal)
294  CreateNormalMap(mru, amp);
295 
296  return cache[mru].texture;
297  }
298 
299  // find cache entry, or first free:
300  for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++)
301  if (cache[i].bitmap_id == bmp->Handle()) {
302  cache[i].used_last = frame_number;
303  mru = i;
304 
305  // need to refresh?
306  if (cache[i].last_modified < bmp->LastModified()) {
307  LoadTexture(bmp, &cache[mru]);
308  cache[i].normal = false;
309  }
310 
311  if (!cache[i].normal)
312  CreateNormalMap(i, amp);
313 
314  return cache[i].texture;
315  }
316  else if (avail < 0 && cache[i].bitmap_id == 0)
317  avail = i;
318 
319  // no free space
320  if (avail < 0)
321  if (FreeUpCache())
322  return FindTexture(bmp);
323  else
324  return 0;
325 
326  TexCacheDX9Entry* entry = &cache[avail];
327  entry->bitmap_id = bmp->Handle();
328  entry->used_last = frame_number;
329 
330  if (LoadTexture(bmp, entry)) {
331 #if TEXDX9_VERBOSE
332  Print(" Tex %3d: id=%2d, size=%3d, type=%d, hTex=%3d, frame=%6d vmf=%8d\n",
333  avail, bmp->Handle(), bmp->Width(), bmp->Type(),
334  cache[avail].texture, cache[avail].used_last, vidmem);
335 #endif
336  mru = avail;
337  CreateNormalMap(mru, amp);
338  return entry->texture;
339  }
340  else {
341  // failed to load texture,
342  // erase cache entry:
343  entry->Unload();
344  }
345 
346  return 0;
347 }
348 
349 // +--------------------------------------------------------------------+
350 
351 int
352 TexCacheDX9::FreeLRU(int tries)
353 {
354  int unloaded = 0;
355 
356  while (tries--) {
357  int oldest = -1;
358  DWORD old = frame_number;
359 
360  for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
361  DWORD ul = cache[i].used_last;
362 
363  if (ul && ul < old && ul != frame_number) {
364  old = ul;
365  oldest = i;
366  }
367  }
368 
369  if (oldest >= 0) {
370  cache[oldest].Unload();
371  unloaded++;
372  }
373  else
374  break;
375  }
376 
377  vidmem = video->VidMemFree();
378 
379 #if TEXDX9_VERBOSE
380  Print(" FreeLRU() frame=%6d unloaded=%2d vmf=%8d\n", frame_number, unloaded, vidmem);
381 #endif
382 
383  return unloaded;
384 }
385 
386 // +--------------------------------------------------------------------+
387 
388 int
389 TexCacheDX9::FreeUpCache()
390 {
391  int unloaded = 0;
392 
393  for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
394  if (cache[i].used_last && cache[i].used_last < frame_number) {
395  cache[i].Unload();
396  unloaded++;
397  }
398  }
399 
400  vidmem = video->VidMemFree();
401 
402  Print(" FreeUpCache() frame=%6d unloaded=%2d vmf=%8d\n", frame_number, unloaded, vidmem);
403 
404  return unloaded;
405 }
406 
407 // +--------------------------------------------------------------------+
408 
409 void
411 {
412  for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
413  cache[i].Unload();
414  cache[i].normal = false;
415  }
416 
417  vidmem = video->VidMemFree();
418 }