102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
com_directdrawsurface7.c
Go to the documentation of this file.
1// Implements the IDirectDrawSurface7 COM translator
2// https://learn.microsoft.com/en-us/windows/win32/api/ddraw/nn-ddraw-idirectdrawsurface7
3
5#include "graphics_private.h"
6#include <dttr_log.h>
7#include <khash.h>
8#include <limits.h>
9#include <stdlib.h>
10#include <string.h>
11#include <xxhash.h>
12
13// https://learn.microsoft.com/en-us/windows/win32/api/ddraw/ns-ddraw-ddsurfacedesc2
14// https://learn.microsoft.com/en-us/windows/win32/api/ddraw/ns-ddraw-ddpixelformat
15
16KHASH_MAP_INIT_INT64(dttr_surface_texture_cache, DTTR_Texture)
17
18typedef struct {
19 uint64_t source_hash;
20 uint32_t width;
21 uint32_t height;
22 uint32_t bpp;
23 uint32_t r_mask;
24 uint32_t g_mask;
25 uint32_t b_mask;
26 uint32_t a_mask;
27 uint16_t colorkey;
28 uint8_t has_colorkey;
30
31static khash_t(dttr_surface_texture_cache) * surface_texture_cache;
32
33typedef struct {
34 uint32_t x;
35 uint32_t y;
36 uint32_t w;
37 uint32_t h;
39
40static bool surface_bgra_upload_size(uint32_t width, uint32_t height, size_t *out_size) {
41 if (out_size) {
42 *out_size = 0;
43 }
44
45 if (!width || !height || width > (uint32_t)INT_MAX || height > (uint32_t)INT_MAX) {
46 return false;
47 }
48
49 if ((size_t)width > SIZE_MAX / (size_t)height / sizeof(uint32_t)) {
50 return false;
51 }
52
53 const size_t size = (size_t)width * (size_t)height * sizeof(uint32_t);
54 if (size == 0 || size > UINT32_MAX) {
55 return false;
56 }
57
58 if (out_size) {
59 *out_size = size;
60 }
61
62 return true;
63}
64
66 uint32_t width,
67 uint32_t height,
68 uint32_t bpp,
69 uint32_t *out_pitch,
70 size_t *out_pixel_size
71) {
72 if (out_pitch) {
73 *out_pitch = 0;
74 }
75
76 if (out_pixel_size) {
77 *out_pixel_size = 0;
78 }
79
80 if (!width || !height || (bpp != 16u && bpp != 32u)) {
81 return DDERR_INVALIDPARAMS;
82 }
83
84 const uint64_t pitch_bits = (uint64_t)width * (uint64_t)bpp;
85 const uint64_t pitch = (pitch_bits + 7u) / 8u;
86 if (pitch == 0 || pitch > UINT32_MAX || pitch > SIZE_MAX / height) {
87 return DDERR_INVALIDPARAMS;
88 }
89
90 if (!surface_bgra_upload_size(width, height, NULL)) {
91 return DDERR_INVALIDPARAMS;
92 }
93
94 if (out_pitch) {
95 *out_pitch = (uint32_t)pitch;
96 }
97
98 if (out_pixel_size) {
99 *out_pixel_size = (size_t)pitch * (size_t)height;
100 }
101
102 return S_OK;
103}
104
106 const RECT *rect,
107 uint32_t default_w,
108 uint32_t default_h,
109 blit_rect *out
110) {
111 if (!out) {
112 return DDERR_INVALIDPARAMS;
113 }
114
115 if (!rect) {
116 *out = (blit_rect){0, 0, default_w, default_h};
117 return S_OK;
118 }
119
120 if (rect->left < 0 || rect->top < 0 || rect->right < rect->left
121 || rect->bottom < rect->top) {
122 return DDERR_INVALIDRECT;
123 }
124
125 *out = (blit_rect){
126 .x = (uint32_t)rect->left,
127 .y = (uint32_t)rect->top,
128 .w = (uint32_t)(rect->right - rect->left),
129 .h = (uint32_t)(rect->bottom - rect->top),
130 };
131
132 return S_OK;
133}
134
137 uint32_t surface_w,
138 uint32_t surface_h
139) {
140 if (!rect || rect->w == 0 || rect->h == 0 || rect->x >= surface_w
141 || rect->y >= surface_h) {
142 return false;
143 }
144
145 const uint32_t max_w = surface_w - rect->x;
146 const uint32_t max_h = surface_h - rect->y;
147 if (rect->w > max_w) {
148 rect->w = max_w;
149 }
150
151 if (rect->h > max_h) {
152 rect->h = max_h;
153 }
154
155 return rect->w != 0 && rect->h != 0;
156}
157
159 if (!surface_texture_cache) {
160 return;
161 }
162
163 kh_destroy(dttr_surface_texture_cache, surface_texture_cache);
164 surface_texture_cache = NULL;
165}
166
168 if (surface_texture_cache) {
169 return true;
170 }
171
172 surface_texture_cache = kh_init(dttr_surface_texture_cache);
173 return surface_texture_cache != NULL;
174}
175
176static void surface_texture_cache_insert_locked(uint64_t key, DTTR_Texture tex) {
177 if (!key || !tex || !surface_texture_cache_ensure_initialized()) {
178 return;
179 }
180
181 int put_ret = 0;
182 const khint_t
183 it = kh_put(dttr_surface_texture_cache, surface_texture_cache, key, &put_ret);
184 if (it == kh_end(surface_texture_cache)) {
185 return;
186 }
187
188 kh_value(surface_texture_cache, it) = tex;
189}
190
191static void surface_texture_cache_remove_locked(uint64_t key, DTTR_Texture tex) {
192 if (!surface_texture_cache || !key || !tex) {
193 return;
194 }
195
196 const khint_t it = kh_get(dttr_surface_texture_cache, surface_texture_cache, key);
197 if (it == kh_end(surface_texture_cache)) {
198 return;
199 }
200
201 if (kh_value(surface_texture_cache, it) == tex) {
202 kh_del(dttr_surface_texture_cache, surface_texture_cache, it);
203 }
204}
205
207 if (!surface_texture_cache || !key) {
209 }
210
211 const khint_t it = kh_get(dttr_surface_texture_cache, surface_texture_cache, key);
212 if (it == kh_end(surface_texture_cache)) {
214 }
215
216 const DTTR_Texture tex = kh_value(surface_texture_cache, it);
217 const int idx = (int)tex - 1;
218 if (idx < 0 || idx >= dttr_backend.staged_texture_count) {
219 kh_del(dttr_surface_texture_cache, surface_texture_cache, it);
221 }
222
223 const DTTR_StagedTexture *st = &dttr_backend.staged_textures[idx];
224 if (st->refcount == 0) {
225 kh_del(dttr_surface_texture_cache, surface_texture_cache, it);
227 }
228
229 return tex;
230}
231
234 uint32_t width,
235 uint32_t height,
236 uint64_t source_hash
237) {
238 const surface_texture_cache_seed seed = {
239 .source_hash = source_hash,
240 .width = width,
241 .height = height,
242 .bpp = self->bpp,
243 .r_mask = self->r_mask,
244 .g_mask = self->g_mask,
245 .b_mask = self->b_mask,
246 .a_mask = self->a_mask,
247 .colorkey = self->colorkey,
248 .has_colorkey = self->has_colorkey ? 1 : 0,
249 };
250
251 return XXH3_64bits(&seed, sizeof(seed));
252}
253
256 DDPIXELFORMAT *pf
257) {
258 memset(pf, 0, sizeof(*pf));
259 pf->dwSize = sizeof(*pf);
260 pf->dwFlags = DDPF_RGB | (self->a_mask ? DDPF_ALPHAPIXELS : 0);
261 pf->dwFourCC = 0;
262 pf->dwRGBBitCount = self->bpp;
263 pf->dwRBitMask = self->r_mask;
264 pf->dwGBitMask = self->g_mask;
265 pf->dwBBitMask = self->b_mask;
266 pf->dwRGBAlphaBitMask = self->a_mask;
267}
268
271 const uint16_t *restrict src,
272 uint32_t *restrict dst,
273 uint32_t w,
274 uint32_t h,
275 uint32_t src_pitch
276) {
277 for (uint32_t y = 0; y < h; y++) {
278 const uint16_t *row = (const uint16_t *)((const uint8_t *)src + y * src_pitch);
279 for (uint32_t x = 0; x < w; x++) {
280 uint16_t px = row[x];
281 // Expand 4-bit channels to 8-bit by replicating each nibble
282 uint8_t a = (px >> 12) & 0xF;
283 a = (a << 4) | a;
284 uint8_t r = (px >> 8) & 0xF;
285 r = (r << 4) | r;
286 uint8_t g = (px >> 4) & 0xF;
287 g = (g << 4) | g;
288 uint8_t b = px & 0xF;
289 b = (b << 4) | b;
290 dst[y * w + x] = ((uint32_t)a << 24) | ((uint32_t)r << 16)
291 | ((uint32_t)g << 8) | b;
292 }
293 }
294}
295
298 const uint16_t *restrict src,
299 uint32_t *restrict dst,
300 uint32_t w,
301 uint32_t h,
302 uint32_t src_pitch,
303 bool has_colorkey,
304 uint16_t colorkey
305) {
306 for (uint32_t y = 0; y < h; y++) {
307 const uint16_t *row = (const uint16_t *)((const uint8_t *)src + y * src_pitch);
308 for (uint32_t x = 0; x < w; x++) {
309 uint16_t px = row[x];
310 if (px == 0x0000 || (has_colorkey && px == colorkey)) {
311 dst[y * w + x] = 0x00000000; // Fully transparent
312 continue;
313 }
314
315 uint8_t r = (px >> 11) & 0x1F;
316 r = (r << 3) | (r >> 2);
317 uint8_t g = (px >> 5) & 0x3F;
318 g = (g << 2) | (g >> 4);
319 uint8_t b = px & 0x1F;
320 b = (b << 3) | (b >> 2);
321 dst[y * w + x] = 0xFF000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
322 }
323 }
324}
325
328 size_t size
329) {
330 if (!self || size == 0) {
331 return NULL;
332 }
333
334 if (self->convert_rgba && self->convert_rgba_capacity >= size) {
335 return (uint32_t *)self->convert_rgba;
336 }
337
338 void *resized = realloc(self->convert_rgba, size);
339 if (!resized) {
340 return NULL;
341 }
342
343 self->convert_rgba = resized;
344 self->convert_rgba_capacity = size;
345 return (uint32_t *)self->convert_rgba;
346}
347
350 uint32_t upload_w,
351 uint32_t upload_h
352) {
353 if (!self || !self->pixels || upload_w == 0 || upload_h == 0) {
354 return 0;
355 }
356
357 const uint32_t bytes_per_pixel = self->bpp / 8;
358 const size_t row_bytes = (size_t)upload_w * bytes_per_pixel;
359 const uint8_t has_colorkey = self->has_colorkey ? 1 : 0;
360 XXH3_state_t hash_state;
361 if (XXH3_64bits_reset(&hash_state) != XXH_OK) {
362 return 0;
363 }
364
365 XXH3_64bits_update(&hash_state, &self->bpp, sizeof(self->bpp));
366 XXH3_64bits_update(&hash_state, &self->a_mask, sizeof(self->a_mask));
367 XXH3_64bits_update(&hash_state, &has_colorkey, sizeof(has_colorkey));
368 XXH3_64bits_update(&hash_state, &self->colorkey, sizeof(self->colorkey));
369 for (uint32_t y = 0; y < upload_h; y++) {
370 const uint8_t *row = (const uint8_t *)self->pixels + (size_t)y * self->pitch;
371 XXH3_64bits_update(&hash_state, row, row_bytes);
372 }
373
374 return XXH3_64bits_digest(&hash_state);
375}
376
378 if (!state || idx < 0 || idx >= state->staged_texture_count) {
379 return;
380 }
381
382 DTTR_StagedTexture *st = &state->staged_textures[idx];
383 if (st->pending_upload) {
384 return;
385 }
386
387 st->pending_upload = true;
388 kv_push(int, state->pending_upload_indices, idx);
389}
390
393 if (!tex) {
394 return 0;
395 }
396
397 const int idx = (int)tex - 1;
398 if (idx < 0 || idx >= state->staged_texture_count) {
399 return 0;
400 }
401
402 uint32_t refs = 0;
403 SDL_LockMutex(state->texture_mutex);
404 refs = state->staged_textures[idx].refcount;
405 SDL_UnlockMutex(state->texture_mutex);
406 return refs;
407}
408
409// Adds one shared reference to an existing staged texture handle.
412 if (!tex) {
413 return false;
414 }
415
416 const int idx = (int)tex - 1;
417 if (idx < 0 || idx >= state->staged_texture_count) {
418 return false;
419 }
420
421 bool retained = false;
422 SDL_LockMutex(state->texture_mutex);
423 DTTR_StagedTexture *st = &state->staged_textures[idx];
424 if (st->refcount > 0) {
425 st->refcount++;
426 retained = true;
427 }
428
429 SDL_UnlockMutex(state->texture_mutex);
430 return retained;
431}
432
435 int width,
436 int height,
437 const void *pixels,
438 uint64_t cache_key
439) {
441 size_t size = 0;
442 if (width <= 0 || height <= 0
443 || !surface_bgra_upload_size((uint32_t)width, (uint32_t)height, &size)) {
445 }
446
447 SDL_LockMutex(state->texture_mutex);
448 const DTTR_Texture cached_tex = surface_texture_cache_lookup_locked(cache_key);
449 if (cached_tex != DTTR_INVALID_TEXTURE) {
450 const int cached_idx = (int)cached_tex - 1;
451 DTTR_StagedTexture *cached_st = &state->staged_textures[cached_idx];
452 cached_st->refcount++;
453 SDL_UnlockMutex(state->texture_mutex);
454 return cached_tex;
455 }
456
457 int idx = -1;
458 for (int i = 0; i < state->staged_texture_count; i++) {
459 if (state->staged_textures[i].refcount != 0) {
460 continue;
461 }
462
463 if (state->staged_textures[i].gpu_tex != NULL) {
464 continue;
465 }
466
467 idx = i;
468 break;
469 }
470
471 if (idx < 0) {
472 if (state->staged_texture_count >= DTTR_MAX_STAGED_TEXTURES) {
473 SDL_UnlockMutex(state->texture_mutex);
474 DTTR_LOG_ERROR("Too many textures");
476 }
477
478 idx = state->staged_texture_count++;
479 }
480
481 DTTR_StagedTexture *st = &state->staged_textures[idx];
482 st->gpu_tex = NULL;
483 st->width = width;
484 st->height = height;
485 st->pending_upload = false;
486 st->last_update_frame = state->frame_index;
487 st->update_streak = 1;
488 st->refcount = 1;
489 st->cache_key_valid = cache_key != 0;
490 st->cache_key = cache_key;
491
492 const bool is_new_slot = (idx == state->staged_texture_count - 1);
493 st->pixels = malloc(size);
494 if (!st->pixels) {
495 st->refcount = 0;
496 if (is_new_slot) {
497 state->staged_texture_count--;
498 }
499
500 SDL_UnlockMutex(state->texture_mutex);
502 }
503
504 if (pixels) {
505 memcpy(st->pixels, pixels, size);
506 } else {
507 memset(st->pixels, 0, size);
508 }
509
510 if (st->cache_key_valid) {
512 }
513
515
516 SDL_UnlockMutex(state->texture_mutex);
517 return (DTTR_Texture)(idx + 1);
518}
519
523 if (!tex) {
524 return;
525 }
526
527 const int idx = (int)tex - 1;
528 if (idx < 0 || idx >= state->staged_texture_count) {
529 return;
530 }
531
532 SDL_LockMutex(state->texture_mutex);
533 DTTR_StagedTexture *st = &state->staged_textures[idx];
534 if (st->refcount > 1) {
535 st->refcount--;
536 SDL_UnlockMutex(state->texture_mutex);
537 return;
538 }
539
540 if (st->refcount == 0) {
541 SDL_UnlockMutex(state->texture_mutex);
542 return;
543 }
544
545 st->refcount = 0;
546 if (st->cache_key_valid) {
548 st->cache_key_valid = false;
549 st->cache_key = 0;
550 }
551
552 if (state->bound_texture == st->gpu_tex) {
553 state->bound_texture = NULL;
554 state->bound_texture_handle = DTTR_INVALID_TEXTURE;
555 }
556
557 state->renderer->defer_texture_destroy(state, idx);
558 st->gpu_tex = NULL;
559 free(st->pixels);
560 st->pixels = NULL;
561 st->pending_upload = false;
562 st->width = 0;
563 st->height = 0;
564 st->last_update_frame = 0;
565 st->update_streak = 0;
566 SDL_UnlockMutex(state->texture_mutex);
567}
568
572 DTTR_Texture tex,
573 int width,
574 int height,
575 const void *pixels,
576 uint64_t cache_key
577) {
579 size_t size = 0;
580 if (!tex || !pixels || width <= 0 || height <= 0
581 || !surface_bgra_upload_size((uint32_t)width, (uint32_t)height, &size)) {
582 return false;
583 }
584
585 const int idx = (int)tex - 1;
586 if (idx < 0 || idx >= state->staged_texture_count) {
587 return false;
588 }
589
590 bool updated = false;
591 SDL_LockMutex(state->texture_mutex);
592 DTTR_StagedTexture *st = &state->staged_textures[idx];
593 if (st->refcount != 1) {
594 SDL_UnlockMutex(state->texture_mutex);
595 return false;
596 }
597
598 const bool had_cache_key = st->cache_key_valid;
599 const uint64_t old_cache_key = st->cache_key;
600 if (had_cache_key) {
601 surface_texture_cache_remove_locked(old_cache_key, tex);
602 }
603
604 void *resized = realloc(st->pixels, size);
605 if (!resized) {
606 if (had_cache_key) {
607 surface_texture_cache_insert_locked(old_cache_key, tex);
608 }
609
610 SDL_UnlockMutex(state->texture_mutex);
611 return false;
612 }
613
614 const uint64_t current_frame = state->frame_index;
615 if (current_frame <= st->last_update_frame + 1) {
616 st->update_streak++;
617 } else {
618 st->update_streak = 1;
619 }
620
621 st->last_update_frame = current_frame;
622
623 st->pixels = resized;
624 memcpy(st->pixels, pixels, size);
625 if (st->width != width || st->height != height) {
626 state->renderer->defer_texture_destroy(state, idx);
627 st->gpu_tex = NULL;
628 }
629
630 st->width = width;
631 st->height = height;
632 st->cache_key = cache_key;
633 st->cache_key_valid = cache_key != 0;
634 if (st->cache_key_valid) {
636 }
637
639 updated = true;
640
641 SDL_UnlockMutex(state->texture_mutex);
642 return updated;
643}
644
646static void surface_present() {
647 if (dttr_backend.frame_active) {
649 }
650}
651
654 if (!self->pixels || self->width == 0 || self->height == 0) {
655 return;
656 }
657
658 const uint32_t upload_w = self->content_width ? self->content_width : self->width;
659 const uint32_t upload_h = self->content_height ? self->content_height : self->height;
660 const uint64_t source_hash = surface_hash_source_pixels(self, upload_w, upload_h);
661 const uint64_t
662 cache_key = surface_texture_cache_key(self, upload_w, upload_h, source_hash);
663
664 if (self->dttr_texture != DTTR_INVALID_TEXTURE && self->last_upload_valid
665 && self->last_upload_width == upload_w && self->last_upload_height == upload_h
666 && self->last_upload_hash == source_hash) {
667 self->dirty = false;
668 return;
669 }
670
671 uint32_t *converted = NULL;
672
673 if (self->bpp == 16) {
674 size_t converted_size = 0;
675 if (!surface_bgra_upload_size(upload_w, upload_h, &converted_size)) {
676 return;
677 }
678
679 converted = surface_ensure_convert_buffer(self, converted_size);
680 if (!converted) {
681 return;
682 }
683
684 if (self->a_mask == 0xF000) {
685 // ARGB4444 content uses surface pitch for row stride.
687 (const uint16_t *)self->pixels,
688 converted,
689 upload_w,
690 upload_h,
691 self->pitch
692 );
693 } else {
694 // RGB565 uses black or an explicit color key as transparent.
696 (const uint16_t *)self->pixels,
697 converted,
698 upload_w,
699 upload_h,
700 self->pitch,
701 self->has_colorkey,
702 self->colorkey
703 );
704 }
705 } else {
706 // 32bpp surfaces are already staged in backend upload format.
707 converted = (uint32_t *)self->pixels;
708 }
709
710 const void *upload_data = converted;
711
712 bool upload_ok = false;
713 if (self->dttr_texture != DTTR_INVALID_TEXTURE
714 && surface_texture_refcount(self->dttr_texture) > 1) {
715 // Detach shared content before looking up the new cache key.
716 surface_texture_release(self->dttr_texture);
717 self->dttr_texture = DTTR_INVALID_TEXTURE;
718 }
719
720 if (self->dttr_texture == DTTR_INVALID_TEXTURE) {
722 upload_w,
723 upload_h,
724 upload_data,
725 cache_key
726 );
727 upload_ok = (self->dttr_texture != DTTR_INVALID_TEXTURE);
728 } else {
730 self->dttr_texture,
731 upload_w,
732 upload_h,
733 upload_data,
734 cache_key
735 );
736 }
737
738 if (upload_ok) {
739 self->last_upload_valid = true;
740 self->last_upload_width = upload_w;
741 self->last_upload_height = upload_h;
742 self->last_upload_hash = source_hash;
743 }
744
745 self->dirty = !upload_ok;
746}
747
748static HRESULT __stdcall ddrawsurface7_queryinterface(
750 void *riid,
751 void **ppv
752) {
753
754 const GUID *iid = (const GUID *)riid;
755
756 if (!iid) {
757 if (ppv) {
758 *ppv = NULL;
759 }
760
761 return E_NOINTERFACE;
762 }
763
764 // IDirect3DTexture2 queries return the surface texture interface.
765 if (memcmp(iid, &IID_IDirect3DTexture2, sizeof(GUID)) == 0) {
766
767 if (!self->texture) {
769 }
770
771 if (ppv) {
772 *ppv = self->texture;
773 }
774
775 return S_OK;
776 }
777
778 // The surface object answers its own and IUnknown queries.
779 if (memcmp(iid, &IID_IDirectDrawSurface7, sizeof(GUID)) == 0
780 || memcmp(iid, &IID_IUnknown, sizeof(GUID)) == 0) {
781 self->refcount++;
782 if (ppv) {
783 *ppv = self;
784 }
785
786 return S_OK;
787 }
788
789 if (ppv) {
790 *ppv = NULL;
791 }
792
793 return E_NOINTERFACE;
794}
795
797 return ++self->refcount;
798}
799
801 ULONG rc = --self->refcount;
802
803 if (rc != 0) {
804 return rc;
805 }
806
807 if (self->dttr_texture) {
808 surface_texture_release(self->dttr_texture);
809 self->dttr_texture = 0;
810 }
811
812 if (self->back_buffer) {
813 self->back_buffer->vtbl->Release(self->back_buffer);
814 self->back_buffer = NULL;
815 }
816
817 free(self->pixels);
818 self->pixels = NULL;
819 free(self->convert_rgba);
820 self->convert_rgba = NULL;
821 self->convert_rgba_capacity = 0;
822 free(self->texture);
823 self->texture = NULL;
824 free(self);
825
826 return rc;
827}
828
830 ddrawsurface7_addattachedsurface,
832 void *surf
833)
834
836 ddrawsurface7_addoverlaydirtyrect,
838 void *rect
839)
840
841static HRESULT __stdcall ddrawsurface7_blt(
843 void *dstRect,
844 void *srcSurf,
845 void *srcRect,
846 DWORD flags,
847 void *bltFx
848) {
849
850 // The game uses colorfill to fill padding areas in video textures.
851 // https://learn.microsoft.com/en-us/windows/win32/api/ddraw/nf-ddraw-idirectdrawsurface7-blt
852 if ((flags & DDBLT_COLORFILL)) {
853 return S_OK;
854 }
855
856 // Blit from source surface to destination
857 if (srcSurf) {
860
861 if (src->pixels && self->pixels && src->bpp == self->bpp) {
862 const uint32_t bpp = src->bpp / 8;
863 if (bpp != 2u && bpp != 4u) {
864 return DDERR_INVALIDPARAMS;
865 }
866
867 blit_rect src_region;
868 HRESULT rect_result = blit_rect_from_optional(
869 (const RECT *)srcRect,
870 src->width,
871 src->height,
872 &src_region
873 );
874 if (rect_result != S_OK) {
875 return rect_result;
876 }
877
878 blit_rect dst_region;
879 rect_result = blit_rect_from_optional(
880 (const RECT *)dstRect,
881 self->width,
882 self->height,
883 &dst_region
884 );
885 if (rect_result != S_OK) {
886 return rect_result;
887 }
888
889 const bool src_visible = blit_rect_clip_to_surface(
890 &src_region,
891 src->width,
892 src->height
893 );
894 const bool dst_visible = blit_rect_clip_to_surface(
895 &dst_region,
896 self->width,
897 self->height
898 );
899 if (!src_visible || !dst_visible) {
900 return S_OK;
901 }
902
903 // Source regions larger than the destination use nearest-neighbor downscale.
904 const bool
905 needs_scale = (src_region.w > dst_region.w || src_region.h > dst_region.h);
906
907 if (needs_scale) {
908 const uint32_t out_w = dst_region.w;
909 const uint32_t out_h = dst_region.h;
910
911 for (uint32_t y = 0; y < out_h; y++) {
912 const uint32_t src_y = src_region.y + (y * src_region.h) / out_h;
913 const uint8_t *src_row = (const uint8_t *)src->pixels
914 + src_y * src->pitch;
915 uint8_t *dst_row = (uint8_t *)self->pixels
916 + (dst_region.y + y) * self->pitch;
917 for (uint32_t x = 0; x < out_w; x++) {
918 const uint32_t src_x = src_region.x + (x * src_region.w) / out_w;
919 memcpy(
920 dst_row + (dst_region.x + x) * bpp,
921 src_row + src_x * bpp,
922 bpp
923 );
924 }
925 }
926
927 self->content_width = dst_region.x + out_w;
928 self->content_height = dst_region.y + out_h;
929 } else {
930 uint32_t copy_w = src_region.w;
931 uint32_t copy_h = src_region.h;
932 if (copy_w > dst_region.w) {
933 copy_w = dst_region.w;
934 }
935
936 if (copy_h > dst_region.h) {
937 copy_h = dst_region.h;
938 }
939
940 if (copy_w == 0 || copy_h == 0) {
941 return S_OK;
942 }
943
944 for (uint32_t y = 0; y < copy_h; y++) {
945 const uint8_t *src_row = (const uint8_t *)src->pixels
946 + (src_region.y + y) * src->pitch
947 + src_region.x * bpp;
948 uint8_t *dst_row = (uint8_t *)self->pixels
949 + (dst_region.y + y) * self->pitch
950 + dst_region.x * bpp;
951 memcpy(dst_row, src_row, copy_w * bpp);
952 }
953
954 self->content_width = dst_region.x + copy_w;
955 self->content_height = dst_region.y + copy_h;
956 }
957
958 self->dirty = true;
960 } else if (src->dttr_texture != DTTR_INVALID_TEXTURE) {
961 if (self->dttr_texture != src->dttr_texture) {
962 if (self->dttr_texture != DTTR_INVALID_TEXTURE) {
963 surface_texture_release(self->dttr_texture);
964 self->dttr_texture = DTTR_INVALID_TEXTURE;
965 }
966
967 if (surface_texture_retain(src->dttr_texture)) {
968 self->dttr_texture = src->dttr_texture;
969 }
970 }
971 }
972 }
973
974 return S_OK;
975}
976
978 ddrawsurface7_bltbatch,
980 void *batch,
981 DWORD count,
983)
984
986 ddrawsurface7_bltfast,
990 void *srcSurf,
991 void *srcRect,
993)
994
996 ddrawsurface7_deleteattachedsurface,
998 DWORD flags,
999 void *surf
1000)
1001
1003 ddrawsurface7_enumattachedsurfaces,
1005 void *ctx,
1006 void *cb
1007)
1008
1010 ddrawsurface7_enumoverlayzorders,
1012 DWORD flags,
1013 void *ctx,
1014 void *cb
1015)
1016
1017static HRESULT __stdcall ddrawsurface7_flip(
1019 void *target,
1021) {
1022
1023 // Present the active backend frame.
1025 return S_OK;
1026}
1027
1028static HRESULT __stdcall ddrawsurface7_getattachedsurface(
1030 void *caps,
1031 void **surf
1032) {
1033
1034 if (!surf) {
1035 return S_OK;
1036 }
1037
1038 // Return an existing back buffer or create one matching this surface.
1039 if (self->back_buffer) {
1040 *surf = self->back_buffer;
1041 } else {
1043 self->width,
1044 self->height,
1045 self->bpp,
1046 self->r_mask,
1047 self->g_mask,
1048 self->b_mask,
1049 self->a_mask
1050 );
1051 *surf = self->back_buffer;
1052 }
1053
1054 return S_OK;
1055}
1056
1058 ddrawsurface7_getbltstatus,
1060 DWORD flags
1061)
1062
1064 ddrawsurface7_getcaps,
1066 void,
1068)
1069
1071 ddrawsurface7_getclipper,
1072 void *,
1073 NULL,
1075)
1076
1077static HRESULT __stdcall ddrawsurface7_getcolorkey(
1079 DWORD flags,
1081) {
1082 if (colorKey) {
1083 DDCOLORKEY *ck = (DDCOLORKEY *)colorKey;
1084 ck->dwColorSpaceLowValue = self->has_colorkey ? self->colorkey : 0;
1085 ck->dwColorSpaceHighValue = ck->dwColorSpaceLowValue;
1086 }
1087
1088 return S_OK;
1089}
1090
1092 ddrawsurface7_getdc,
1093 HDC,
1094 NULL,
1096)
1097
1099 ddrawsurface7_getflipstatus,
1101 DWORD flags
1102)
1103
1104static HRESULT __stdcall ddrawsurface7_getoverlayposition(
1106 LONG *x,
1107 LONG *y
1108) {
1109 if (x) {
1110 *x = 0;
1111 }
1112
1113 if (y) {
1114 *y = 0;
1115 }
1116
1117 return S_OK;
1118}
1119
1121 ddrawsurface7_getpalette,
1122 void *,
1123 NULL,
1125)
1126
1127static HRESULT __stdcall ddrawsurface7_getpixelformat(
1129 void *fmt
1130) {
1131
1132 if (!fmt) {
1133 return S_OK;
1134 }
1135
1136 DDPIXELFORMAT *pf = (DDPIXELFORMAT *)fmt;
1138
1139 return S_OK;
1140}
1141
1145 DDSURFACEDESC2 *d
1146) {
1147 d->dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | DDSD_PIXELFORMAT;
1148 d->dwHeight = self->height;
1149 d->dwWidth = self->width;
1150 d->lPitch = self->pitch;
1151 surface_fill_pixelformat(self, &d->ddpfPixelFormat);
1152}
1153
1154static HRESULT __stdcall ddrawsurface7_getsurfacedesc(
1156 void *desc
1157) {
1158
1159 if (!desc) {
1160 return S_OK;
1161 }
1162
1163 DDSURFACEDESC2 *d = (DDSURFACEDESC2 *)desc;
1164 const DWORD size = d->dwSize;
1165
1167 memset((char *)desc + 4, 0, size - 4);
1169 } else if (size == 0) {
1170 d->dwSize = DTTR_SIZEOF_DDSURFACEDESC;
1171 memset((char *)desc + 4, 0, DTTR_SIZEOF_DDSURFACEDESC - 4);
1173 }
1174
1175 return S_OK;
1176}
1177
1179 ddrawsurface7_initialize,
1181 void *dd,
1182 void *desc
1183)
1184
1186
1187static HRESULT __stdcall ddrawsurface7_lock(
1189 void *rect,
1190 void *desc,
1191 DWORD flags,
1192 HANDLE event
1193) {
1194
1195 if (!self->pixels) {
1196 return DTTR_DDERR_GENERIC;
1197 }
1198
1199 self->locked = true;
1200
1201 if (!desc) {
1202 return S_OK;
1203 }
1204
1205 DDSURFACEDESC2 *d = (DDSURFACEDESC2 *)desc;
1206 const DWORD size = d->dwSize;
1207
1209 // Preserve dwSize while clearing the caller-visible fields.
1210 memset((char *)desc + 4, 0, size - 4);
1211
1213
1214 d->lpSurface = self->pixels;
1215 d->dwFlags |= DDSD_LPSURFACE;
1216 } else if (size == 0) {
1217 d->dwSize = DTTR_SIZEOF_DDSURFACEDESC;
1218 memset((char *)desc + 4, 0, DTTR_SIZEOF_DDSURFACEDESC - 4);
1219
1220 d->dwFlags = DDSD_LPSURFACE | DDSD_PITCH;
1221 d->lPitch = self->pitch;
1222 d->lpSurface = self->pixels;
1223 }
1224
1225 return S_OK;
1226}
1227
1229 ddrawsurface7_releasedc,
1231 HDC dc
1232)
1233
1235
1237 ddrawsurface7_setclipper,
1239 void *clipper
1240)
1241
1242static HRESULT __stdcall ddrawsurface7_setcolorkey(
1244 DWORD flags,
1245 void *colorKey
1246) {
1247 if (colorKey) {
1248 const DDCOLORKEY *ck = (const DDCOLORKEY *)colorKey;
1249 self->has_colorkey = true;
1250 self->colorkey = (uint16_t)ck->dwColorSpaceLowValue;
1251 self->last_upload_valid = false;
1252 self->dirty = true;
1254 }
1255
1256 return S_OK;
1257}
1258
1260 ddrawsurface7_setoverlayposition,
1262 LONG x,
1263 LONG y
1264)
1265
1267 ddrawsurface7_setpalette,
1269 void *palette
1270)
1271
1272static HRESULT __stdcall ddrawsurface7_unlock(
1274 void *rect
1275) {
1276
1277 if (!self->locked) {
1278 return S_OK;
1279 }
1280
1281 self->locked = false;
1282 self->dirty = true;
1283
1284 // Upload changed pixels to the staged GPU texture.
1286
1287 return S_OK;
1288}
1289
1291 ddrawsurface7_updateoverlay,
1293 void *srcRect,
1294 void *dstSurf,
1295 void *dstRect,
1296 DWORD flags,
1297 void *fx
1298)
1299
1301 ddrawsurface7_updateoverlaydisplay,
1303 DWORD flags
1304)
1305
1307 ddrawsurface7_updateoverlayzorder,
1309 DWORD flags,
1310 void *refSurf
1311)
1312
1314 ddrawsurface7_getddinterface,
1315 void *,
1318)
1319
1321 ddrawsurface7_pagelock,
1323 DWORD flags
1324)
1325
1327 ddrawsurface7_pageunlock,
1329 DWORD flags
1330)
1331
1333 ddrawsurface7_setsurfacedesc,
1335 void *desc,
1336 DWORD flags
1337)
1338
1340 ddrawsurface7_setprivatedata,
1342 void *tag,
1343 void *data,
1344 DWORD size,
1345 DWORD flags
1346)
1347
1349 ddrawsurface7_getprivatedata,
1350 DWORD,
1351 0,
1353 void *tag,
1354 void *data
1355)
1356
1358 ddrawsurface7_freeprivatedata,
1360 void *tag
1361)
1362
1364 ddrawsurface7_getuniquenessvalue,
1365 DWORD,
1366 1,
1368)
1369
1371 ddrawsurface7_changeuniquenessvalue,
1373)
1374
1376 ddrawsurface7_setpriority,
1378 DWORD priority
1379)
1380
1382 ddrawsurface7_getpriority,
1384 0,
1386)
1387
1389 ddrawsurface7_setlod,
1391 DWORD lod
1392)
1393
1395 ddrawsurface7_getlod,
1396 DWORD,
1397 0,
1399)
1400
1402 .QueryInterface = ddrawsurface7_queryinterface,
1403 .AddRef = ddrawsurface7_addref,
1404 .Release = ddrawsurface7_release,
1405 .AddAttachedSurface = ddrawsurface7_addattachedsurface,
1406 .AddOverlayDirtyRect = ddrawsurface7_addoverlaydirtyrect,
1407 .Blt = ddrawsurface7_blt,
1408 .BltBatch = ddrawsurface7_bltbatch,
1409 .BltFast = ddrawsurface7_bltfast,
1410 .DeleteAttachedSurface = ddrawsurface7_deleteattachedsurface,
1411 .EnumAttachedSurfaces = ddrawsurface7_enumattachedsurfaces,
1412 .EnumOverlayZOrders = ddrawsurface7_enumoverlayzorders,
1413 .Flip = ddrawsurface7_flip,
1414 .GetAttachedSurface = ddrawsurface7_getattachedsurface,
1415 .GetBltStatus = ddrawsurface7_getbltstatus,
1416 .GetCaps = ddrawsurface7_getcaps,
1417 .GetClipper = ddrawsurface7_getclipper,
1418 .GetColorKey = ddrawsurface7_getcolorkey,
1419 .GetDC = ddrawsurface7_getdc,
1420 .GetFlipStatus = ddrawsurface7_getflipstatus,
1421 .GetOverlayPosition = ddrawsurface7_getoverlayposition,
1422 .GetPalette = ddrawsurface7_getpalette,
1423 .GetPixelFormat = ddrawsurface7_getpixelformat,
1424 .GetSurfaceDesc = ddrawsurface7_getsurfacedesc,
1425 .Initialize = ddrawsurface7_initialize,
1426 .IsLost = ddrawsurface7_islost,
1427 .Lock = ddrawsurface7_lock,
1428 .ReleaseDC = ddrawsurface7_releasedc,
1429 .Restore = ddrawsurface7_restore,
1430 .SetClipper = ddrawsurface7_setclipper,
1431 .SetColorKey = ddrawsurface7_setcolorkey,
1432 .SetOverlayPosition = ddrawsurface7_setoverlayposition,
1433 .SetPalette = ddrawsurface7_setpalette,
1434 .Unlock = ddrawsurface7_unlock,
1435 .UpdateOverlay = ddrawsurface7_updateoverlay,
1436 .UpdateOverlayDisplay = ddrawsurface7_updateoverlaydisplay,
1437 .UpdateOverlayZOrder = ddrawsurface7_updateoverlayzorder,
1438 .GetDDInterface = ddrawsurface7_getddinterface,
1439 .PageLock = ddrawsurface7_pagelock,
1440 .PageUnlock = ddrawsurface7_pageunlock,
1441 .SetSurfaceDesc = ddrawsurface7_setsurfacedesc,
1442 .SetPrivateData = ddrawsurface7_setprivatedata,
1443 .GetPrivateData = ddrawsurface7_getprivatedata,
1444 .FreePrivateData = ddrawsurface7_freeprivatedata,
1445 .GetUniquenessValue = ddrawsurface7_getuniquenessvalue,
1446 .ChangeUniquenessValue = ddrawsurface7_changeuniquenessvalue,
1447 .SetPriority = ddrawsurface7_setpriority,
1448 .GetPriority = ddrawsurface7_getpriority,
1449 .SetLOD = ddrawsurface7_setlod,
1450 .GetLOD = ddrawsurface7_getlod,
1451};
1452
1454 uint32_t width,
1455 uint32_t height,
1456 uint32_t bpp,
1457 uint32_t r_mask,
1458 uint32_t g_mask,
1459 uint32_t b_mask,
1460 uint32_t a_mask
1461) {
1462 uint32_t pitch = 0;
1463 size_t pixel_size = 0;
1465 width,
1466 height,
1467 bpp,
1468 &pitch,
1469 &pixel_size
1470 )
1471 != S_OK) {
1472 return NULL;
1473 }
1474
1476 1,
1478 );
1479 if (surf) {
1480 surf->vtbl = &vtbl;
1481 surf->refcount = 1;
1482 // Create the texture interface up front because the game can query it directly.
1484 if (!surf->texture) {
1485 free(surf);
1486 return NULL;
1487 }
1488
1489 surf->width = width;
1490 surf->height = height;
1491 surf->bpp = bpp;
1492 surf->pitch = pitch;
1493 surf->r_mask = r_mask;
1494 surf->g_mask = g_mask;
1495 surf->b_mask = b_mask;
1496 surf->a_mask = a_mask;
1497
1498 surf->pixels = calloc(1, pixel_size);
1499 if (!surf->pixels) {
1500 free(surf->texture);
1501 free(surf);
1502 return NULL;
1503 }
1504 }
1505
1506 return surf;
1507}
static DTTR_Graphics_COM_Direct3D7_VT vtbl
void * cb
DTTR_Graphics_COM_Direct3DDevice7 * self
DTTR_Graphics_COM_Direct3DDevice7 void *status DTTR_Graphics_COM_Direct3DDevice7 DWORD DWORD void DWORD DWORD f DTTR_Graphics_COM_Direct3DDevice7 DWORD void DWORD st
return S_OK
DTTR_Graphics_COM_Direct3DDevice7 void *status DTTR_Graphics_COM_Direct3DDevice7 DWORD DWORD void DWORD DWORD f DTTR_Graphics_COM_Direct3DDevice7 DWORD void DWORD DWORD DWORD f DTTR_Graphics_COM_Direct3DDevice7 void float * r
DTTR_Graphics_COM_Direct3DDevice7 void DWORD flags DWORD count
DTTR_Graphics_COM_Direct3DDevice7 void *status DTTR_Graphics_COM_Direct3DDevice7 DWORD DWORD void * d
const DTTR_BackendState * state
void void * ctx
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void * dst
const uint8_t * src
DTTR_Graphics_COM_Direct3DDevice7 void DWORD flags DWORD void DWORD flags
DTTR_Graphics_COM_Direct3DTexture2 * dttr_graphics_com_create_direct3d_texture2(DTTR_Graphics_COM_DirectDrawSurface7 *surface)
DTTR_Graphics_COM_DirectDraw7 *self DWORD DWORD DWORD bpp
void * caps
DTTR_Graphics_COM_DirectDraw7 *self DWORD DWORD h
void DWORD DWORD * free
DTTR_Graphics_COM_DirectDraw7 *self DWORD w
DTTR_Graphics_COM_DirectDraw7 DWORD void * desc
static bool blit_rect_clip_to_surface(blit_rect *rect, uint32_t surface_w, uint32_t surface_h)
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void DTTR_Graphics_COM_DirectDrawSurface7 *self DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags DTTR_Graphics_COM_DirectDrawSurface7 void * tag
DTTR_Graphics_COM_DirectDrawSurface7 *self DWORD void * colorKey
static void surface_upload_texture(DTTR_Graphics_COM_DirectDrawSurface7 *self)
Uploads dirty surface pixels to the staged texture cache.
DTTR_Graphics_COM_DirectDrawSurface7 DWORD static flags HRESULT ddrawsurface7_getoverlayposition(DTTR_Graphics_COM_DirectDrawSurface7 *self, LONG *x, LONG *y)
static DTTR_Texture surface_texture_create_or_retain(int width, int height, const void *pixels, uint64_t cache_key)
Creates or reuses a staged GPU texture handle for a CPU pixel buffer.
static HRESULT ddrawsurface7_getattachedsurface(DTTR_Graphics_COM_DirectDrawSurface7 *self, void *caps, void **surf)
static bool surface_bgra_upload_size(uint32_t width, uint32_t height, size_t *out_size)
DTTR_Graphics_COM_DirectDrawSurface7 DWORD DWORD void * srcSurf
static bool surface_texture_cache_ensure_initialized()
DTTR_Graphics_COM_DirectDrawSurface7 void *static palette HRESULT ddrawsurface7_unlock(DTTR_Graphics_COM_DirectDrawSurface7 *self, void *rect)
const DWORD size
static khash_t(dttr_surface_texture_cache)
DTTR_Graphics_COM_DirectDrawSurface7 DWORD x
DTTR_Graphics_COM_DirectDrawSurface7 * dttr_graphics_com_create_directdrawsurface7(uint32_t width, uint32_t height, uint32_t bpp, uint32_t r_mask, uint32_t g_mask, uint32_t b_mask, uint32_t a_mask)
DTTR_Graphics_COM_DirectDrawSurface7 DWORD DWORD void void * srcRect
DTTR_Graphics_COM_DirectDrawSurface7 void *static clipper HRESULT ddrawsurface7_setcolorkey(DTTR_Graphics_COM_DirectDrawSurface7 *self, DWORD flags, void *colorKey)
static HRESULT ddrawsurface7_getsurfacedesc(DTTR_Graphics_COM_DirectDrawSurface7 *self, void *desc)
void * rect
static DTTR_Texture surface_texture_cache_lookup_locked(uint64_t key)
static uint64_t surface_hash_source_pixels(const DTTR_Graphics_COM_DirectDrawSurface7 *self, uint32_t upload_w, uint32_t upload_h)
static void surface_texture_cache_insert_locked(uint64_t key, DTTR_Texture tex)
void dttr_graphics_surface_texture_cache_reset()
static void surface_fill_pixelformat(const DTTR_Graphics_COM_DirectDrawSurface7 *self, DDPIXELFORMAT *pf)
static HRESULT blit_rect_from_optional(const RECT *rect, uint32_t default_w, uint32_t default_h, blit_rect *out)
DTTR_Graphics_COM_DirectDrawSurface7 DWORD DWORD y
static void surface_fill_desc(const DTTR_Graphics_COM_DirectDrawSurface7 *self, DDSURFACEDESC2 *d)
Fills a caller-provided DDSURFACEDESC2 buffer from surface state.
static HRESULT ddrawsurface7_queryinterface(DTTR_Graphics_COM_DirectDrawSurface7 *self, void *riid, void **ppv)
static void surface_convert_rgb565_to_bgra8888(const uint16_t *restrict src, uint32_t *restrict dst, uint32_t w, uint32_t h, uint32_t src_pitch, bool has_colorkey, uint16_t colorkey)
Converts RGB565 pixels to BGRA8888 and applies black/colorkey transparency.
static void surface_queue_pending_upload_locked(DTTR_BackendState *state, int idx)
void void DWORD HANDLE event
DTTR_Graphics_COM_DirectDrawSurface7 void *static rect HRESULT ddrawsurface7_blt(DTTR_Graphics_COM_DirectDrawSurface7 *self, void *dstRect, void *srcSurf, void *srcRect, DWORD flags, void *bltFx)
static bool surface_texture_update_unique(DTTR_Texture tex, int width, int height, const void *pixels, uint64_t cache_key)
static void surface_convert_argb4444_to_bgra8888(const uint16_t *restrict src, uint32_t *restrict dst, uint32_t w, uint32_t h, uint32_t src_pitch)
Converts ARGB4444 surface pixels to BGRA8888 pixels.
static ULONG ddrawsurface7_release(DTTR_Graphics_COM_DirectDrawSurface7 *self)
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void DTTR_Graphics_COM_DirectDrawSurface7 *self DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags DTTR_Graphics_COM_DirectDrawSurface7 void void * data
static void surface_texture_release(DTTR_Texture tex)
Releases one staged texture reference and destroys GPU resources at zero refs.
static void surface_texture_cache_remove_locked(uint64_t key, DTTR_Texture tex)
static uint32_t * surface_ensure_convert_buffer(DTTR_Graphics_COM_DirectDrawSurface7 *self, size_t size)
static uint32_t surface_texture_refcount(DTTR_Texture tex)
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
DTTR_Graphics_COM_DirectDrawSurface7 DWORD DWORD void void DWORD flags DTTR_Graphics_COM_DirectDrawSurface7 void void *cb void * target
static uint64_t surface_texture_cache_key(const DTTR_Graphics_COM_DirectDrawSurface7 *self, uint32_t width, uint32_t height, uint64_t source_hash)
HRESULT dttr_graphics_com_validate_directdrawsurface7(uint32_t width, uint32_t height, uint32_t bpp, uint32_t *out_pitch, size_t *out_pixel_size)
static bool surface_texture_retain(DTTR_Texture tex)
static ULONG ddrawsurface7_addref(DTTR_Graphics_COM_DirectDrawSurface7 *self)
static void surface_present()
Ends and presents the current frame when a frame is active.
#define DTTR_LOG_ERROR(...)
Definition dttr_log.h:31
void dttr_graphics_end_frame()
Definition graphics.c:610
#define DTTR_SIZEOF_DDSURFACEDESC2
#define DTTR_COM_STUB_MEMSET(fn, size, buf_type,...)
#define DTTR_COM_STUB_SET(fn, out_type, val,...)
#define DTTR_COM_NOOP_HRESULT(fn,...)
#define DTTR_SIZEOF_DDSURFACEDESC
#define DTTR_SIZEOF_DDSCAPS2
#define DTTR_DDERR_GENERIC
uint32_t DTTR_Texture
DTTR_BackendState dttr_backend
Definition util.c:8
#define DTTR_MAX_STAGED_TEXTURES
#define DTTR_INVALID_TEXTURE
DTTR_Graphics_COM_DirectDrawSurface7_VT * vtbl
DTTR_Graphics_COM_Direct3DTexture2 * texture