1 /*
2 ** fb_d3d9.cpp
3 ** Code to let ZDoom use Direct3D 9 as a simple framebuffer
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2011 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 ** This file does _not_ implement hardware-acclerated 3D rendering. It is
34 ** just a means of getting the pixel data to the screen in a more reliable
35 ** method on modern hardware by copying the entire frame to a texture,
36 ** drawing that to the screen, and presenting.
37 **
38 ** That said, it does implement hardware-accelerated 2D rendering.
39 */
40
41 // HEADER FILES ------------------------------------------------------------
42
43 #ifdef _DEBUG
44 #define D3D_DEBUG_INFO
45 #endif
46 #define DIRECT3D_VERSION 0x0900
47 #define WIN32_LEAN_AND_MEAN
48
49 #include <windows.h>
50 #include <d3d9.h>
51
52 #include <stdio.h>
53
54 #define USE_WINDOWS_DWORD
55 #include "doomtype.h"
56
57 #include "c_dispatch.h"
58 #include "templates.h"
59 #include "i_system.h"
60 #include "i_video.h"
61 #include "i_input.h"
62 #include "v_video.h"
63 #include "v_pfx.h"
64 #include "stats.h"
65 #include "doomerrors.h"
66 #include "r_main.h"
67 #include "r_data/r_translate.h"
68 #include "f_wipe.h"
69 #include "sbar.h"
70 #include "win32iface.h"
71 #include "doomstat.h"
72 #include "v_palette.h"
73 #include "w_wad.h"
74 #include "r_data/colormaps.h"
75 #include "SkylineBinPack.h"
76
77 // MACROS ------------------------------------------------------------------
78
79 // The number of points for the vertex buffer.
80 #define NUM_VERTS 10240
81
82 // The number of indices for the index buffer.
83 #define NUM_INDEXES ((NUM_VERTS * 6) / 4)
84
85 // The number of quads we can batch together.
86 #define MAX_QUAD_BATCH (NUM_INDEXES / 6)
87
88 // The default size for a texture atlas.
89 #define DEF_ATLAS_WIDTH 512
90 #define DEF_ATLAS_HEIGHT 512
91
92 // TYPES -------------------------------------------------------------------
93
94 IMPLEMENT_CLASS(D3DFB)
95
96 struct D3DFB::PackedTexture
97 {
98 D3DFB::Atlas *Owner;
99
100 PackedTexture **Prev, *Next;
101
102 // Pixels this image covers
103 RECT Area;
104
105 // Texture coordinates for this image
106 float Left, Top, Right, Bottom;
107
108 // Texture has extra space on the border?
109 bool Padded;
110 };
111
112 struct D3DFB::Atlas
113 {
114 Atlas(D3DFB *fb, int width, int height, D3DFORMAT format);
115 ~Atlas();
116
117 PackedTexture *AllocateImage(const Rect &rect, bool padded);
118 void FreeBox(PackedTexture *box);
119
120 SkylineBinPack Packer;
121 Atlas *Next;
122 IDirect3DTexture9 *Tex;
123 D3DFORMAT Format;
124 PackedTexture *UsedList; // Boxes that contain images
125 int Width, Height;
126 bool OneUse;
127 };
128
129 class D3DTex : public FNativeTexture
130 {
131 public:
132 D3DTex(FTexture *tex, D3DFB *fb, bool wrapping);
133 ~D3DTex();
134
135 FTexture *GameTex;
136 D3DFB::PackedTexture *Box;
137
138 D3DTex **Prev;
139 D3DTex *Next;
140
141 bool IsGray;
142
143 bool Create(D3DFB *fb, bool wrapping);
144 bool Update();
145 bool CheckWrapping(bool wrapping);
146 D3DFORMAT GetTexFormat();
147 FTextureFormat ToTexFmt(D3DFORMAT fmt);
148 };
149
150 class D3DPal : public FNativePalette
151 {
152 public:
153 D3DPal(FRemapTable *remap, D3DFB *fb);
154 ~D3DPal();
155
156 D3DPal **Prev;
157 D3DPal *Next;
158
159 IDirect3DTexture9 *Tex;
160 D3DCOLOR BorderColor;
161 bool DoColorSkip;
162
163 bool Update();
164
165 FRemapTable *Remap;
166 int RoundedPaletteSize;
167 };
168
169 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
170
171 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
172
173 void DoBlending (const PalEntry *from, PalEntry *to, int count, int r, int g, int b, int a);
174
175 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
176
177 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
178
179 extern HWND Window;
180 extern IVideo *Video;
181 extern BOOL AppActive;
182 extern int SessionState;
183 extern bool VidResizing;
184
185 EXTERN_CVAR (Bool, fullscreen)
186 EXTERN_CVAR (Float, Gamma)
187 EXTERN_CVAR (Bool, vid_vsync)
188 EXTERN_CVAR (Float, transsouls)
189 EXTERN_CVAR (Int, vid_refreshrate)
190
191 extern IDirect3D9 *D3D;
192
193 extern cycle_t BlitCycles;
194
195 // PRIVATE DATA DEFINITIONS ------------------------------------------------
196
197 const char *const D3DFB::ShaderNames[D3DFB::NUM_SHADERS] =
198 {
199 "NormalColor.pso",
200 "NormalColorPal.pso",
201 "NormalColorInv.pso",
202 "NormalColorPalInv.pso",
203
204 "RedToAlpha.pso",
205 "RedToAlphaInv.pso",
206
207 "VertexColor.pso",
208
209 "SpecialColormap.pso",
210 "SpecialColorMapPal.pso",
211
212 "InGameColormap.pso",
213 "InGameColormapDesat.pso",
214 "InGameColormapInv.pso",
215 "InGameColormapInvDesat.pso",
216 "InGameColormapPal.pso",
217 "InGameColormapPalDesat.pso",
218 "InGameColormapPalInv.pso",
219 "InGameColormapPalInvDesat.pso",
220
221 "BurnWipe.pso",
222 "GammaCorrection.pso",
223 };
224
225 // PUBLIC DATA DEFINITIONS -------------------------------------------------
226
CUSTOM_CVAR(Bool,vid_hw2d,true,CVAR_NOINITCALL)227 CUSTOM_CVAR(Bool, vid_hw2d, true, CVAR_NOINITCALL)
228 {
229 V_SetBorderNeedRefresh();
230 ST_SetNeedRefresh();
231 }
232
233 CVAR(Bool, d3d_antilag, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
234 CVAR(Int, d3d_showpacks, 0, 0)
235 CVAR(Bool, vid_hwaalines, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
236
237 // CODE --------------------------------------------------------------------
238
239 //==========================================================================
240 //
241 // D3DFB - Constructor
242 //
243 //==========================================================================
244
D3DFB(UINT adapter,int width,int height,bool fullscreen)245 D3DFB::D3DFB (UINT adapter, int width, int height, bool fullscreen)
246 : BaseWinFB (width, height)
247 {
248 D3DPRESENT_PARAMETERS d3dpp;
249
250 LastHR = 0;
251
252 Adapter = adapter;
253 D3DDevice = NULL;
254 VertexBuffer = NULL;
255 IndexBuffer = NULL;
256 FBTexture = NULL;
257 TempRenderTexture = NULL;
258 RenderTexture[0] = NULL;
259 RenderTexture[1] = NULL;
260 InitialWipeScreen = NULL;
261 ScreenshotTexture = NULL;
262 ScreenshotSurface = NULL;
263 FinalWipeScreen = NULL;
264 PaletteTexture = NULL;
265 GammaTexture = NULL;
266 FrontCopySurface = NULL;
267 for (int i = 0; i < NUM_SHADERS; ++i)
268 {
269 Shaders[i] = NULL;
270 }
271 GammaShader = NULL;
272 BlockSurface[0] = NULL;
273 BlockSurface[1] = NULL;
274 VSync = vid_vsync;
275 BlendingRect.left = 0;
276 BlendingRect.top = 0;
277 BlendingRect.right = FBWidth;
278 BlendingRect.bottom = FBHeight;
279 In2D = 0;
280 Palettes = NULL;
281 Textures = NULL;
282 Accel2D = true;
283 GatheringWipeScreen = false;
284 ScreenWipe = NULL;
285 InScene = false;
286 QuadExtra = new BufferedTris[MAX_QUAD_BATCH];
287 Atlases = NULL;
288 PixelDoubling = 0;
289 SkipAt = -1;
290 CurrRenderTexture = 0;
291 RenderTextureToggle = 0;
292
293 Gamma = 1.0;
294 FlashColor0 = 0;
295 FlashColor1 = 0xFFFFFFFF;
296 FlashColor = 0;
297 FlashAmount = 0;
298
299 NeedGammaUpdate = false;
300 NeedPalUpdate = false;
301
302 if (MemBuffer == NULL)
303 {
304 return;
305 }
306
307 memcpy(SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256);
308
309 Windowed = !(static_cast<Win32Video *>(Video)->GoFullscreen(fullscreen));
310
311 TrueHeight = height;
312 if (fullscreen)
313 {
314 for (Win32Video::ModeInfo *mode = static_cast<Win32Video *>(Video)->m_Modes; mode != NULL; mode = mode->next)
315 {
316 if (mode->width == Width && mode->height == Height)
317 {
318 TrueHeight = mode->realheight;
319 PixelDoubling = mode->doubling;
320 break;
321 }
322 }
323 }
324 // Offset from top of screen to top of letterboxed screen
325 LBOffsetI = (TrueHeight - Height) / 2;
326 LBOffset = float(LBOffsetI);
327
328 FillPresentParameters(&d3dpp, fullscreen, VSync);
329
330 HRESULT hr;
331
332 LOG("CreateDevice attempt 1 hwvp\n");
333 if (FAILED(hr = D3D->CreateDevice(Adapter, D3DDEVTYPE_HAL, Window,
334 D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3dpp, &D3DDevice)) &&
335 (hr != D3DERR_DEVICELOST || D3DDevice == NULL))
336 {
337 LOG2("CreateDevice returned hr %08x dev %p; attempt 2 swvp\n", hr, D3DDevice);
338 if (FAILED(D3D->CreateDevice(Adapter, D3DDEVTYPE_HAL, Window,
339 D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3dpp, &D3DDevice)) &&
340 (hr != D3DERR_DEVICELOST || D3DDevice == NULL))
341 {
342 if (d3dpp.FullScreen_RefreshRateInHz != 0)
343 {
344 d3dpp.FullScreen_RefreshRateInHz = 0;
345 LOG2("CreateDevice returned hr %08x dev %p; attempt 3 (hwvp, default Hz)\n", hr, D3DDevice);
346 if (FAILED(hr = D3D->CreateDevice(Adapter, D3DDEVTYPE_HAL, Window,
347 D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3dpp, &D3DDevice)) &&
348 (hr != D3DERR_DEVICELOST || D3DDevice == NULL))
349 {
350 LOG2("CreateDevice returned hr %08x dev %p; attempt 4 (swvp, default Hz)\n", hr, D3DDevice);
351 if (FAILED(D3D->CreateDevice(Adapter, D3DDEVTYPE_HAL, Window,
352 D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3dpp, &D3DDevice)) &&
353 hr != D3DERR_DEVICELOST)
354 {
355 D3DDevice = NULL;
356 }
357 }
358 }
359 }
360 }
361 LOG2("Final CreateDevice returned HR %08x and device %p\n", hr, D3DDevice);
362 LastHR = hr;
363 if (D3DDevice != NULL)
364 {
365 D3DADAPTER_IDENTIFIER9 adapter_id;
366 D3DDEVICE_CREATION_PARAMETERS create_params;
367
368 if (FAILED(hr = D3DDevice->GetDeviceCaps(&DeviceCaps)))
369 {
370 memset(&DeviceCaps, 0, sizeof(DeviceCaps));
371 }
372 if (SUCCEEDED(hr = D3DDevice->GetCreationParameters(&create_params)) &&
373 SUCCEEDED(hr = D3D->GetAdapterIdentifier(create_params.AdapterOrdinal, 0, &adapter_id)))
374 {
375 // NVidia's drivers lie, claiming they don't support
376 // antialiased lines when, really, they do.
377 if (adapter_id.VendorId == 0x10de)
378 {
379 DeviceCaps.LineCaps |= D3DLINECAPS_ANTIALIAS;
380 }
381 // ATI's drivers apparently also lie, so screw this caps bit.
382 }
383 CreateResources();
384 SetInitialState();
385 }
386 }
387
388 //==========================================================================
389 //
390 // D3DFB - Destructor
391 //
392 //==========================================================================
393
~D3DFB()394 D3DFB::~D3DFB ()
395 {
396 ReleaseResources();
397 SAFE_RELEASE( D3DDevice );
398 delete[] QuadExtra;
399 }
400
401 //==========================================================================
402 //
403 // D3DFB :: SetInitialState
404 //
405 // Called after initial device creation and reset, when everything is set
406 // to D3D's defaults.
407 //
408 //==========================================================================
409
SetInitialState()410 void D3DFB::SetInitialState()
411 {
412 AlphaBlendEnabled = FALSE;
413 AlphaBlendOp = D3DBLENDOP_ADD;
414 AlphaSrcBlend = D3DBLEND(0);
415 AlphaDestBlend = D3DBLEND(0);
416
417 CurPixelShader = NULL;
418 memset(Constant, 0, sizeof(Constant));
419
420 for (unsigned i = 0; i < countof(Texture); ++i)
421 {
422 Texture[i] = NULL;
423 D3DDevice->SetSamplerState(i, D3DSAMP_ADDRESSU, (i == 1 && SM14) ? D3DTADDRESS_BORDER : D3DTADDRESS_CLAMP);
424 D3DDevice->SetSamplerState(i, D3DSAMP_ADDRESSV, (i == 1 && SM14) ? D3DTADDRESS_BORDER : D3DTADDRESS_CLAMP);
425 if (i > 1)
426 {
427 // Set linear filtering for the SM14 gamma texture.
428 D3DDevice->SetSamplerState(i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
429 }
430 }
431
432 NeedGammaUpdate = true;
433 NeedPalUpdate = true;
434 OldRenderTarget = NULL;
435
436 if (!Windowed && SM14)
437 {
438 // Fix for Radeon 9000, possibly other R200s: When the device is
439 // reset, it resets the gamma ramp, but the driver apparently keeps a
440 // cached copy of the ramp that it doesn't update, so when
441 // SetGammaRamp is called later to handle the NeedGammaUpdate flag,
442 // it doesn't do anything, because the gamma ramp is the same as the
443 // one passed in the last call, even though the visible gamma ramp
444 // actually has changed.
445 //
446 // So here we force the gamma ramp to something absolutely horrible and
447 // trust that we will be able to properly set the gamma later when
448 // NeedGammaUpdate is handled.
449 D3DGAMMARAMP ramp;
450 memset(&ramp, 0, sizeof(ramp));
451 D3DDevice->SetGammaRamp(0, 0, &ramp);
452 }
453
454 // This constant is used for grayscaling weights (.xyz) and color inversion (.w)
455 float weights[4] = { 77/256.f, 143/256.f, 37/256.f, 1 };
456 D3DDevice->SetPixelShaderConstantF(PSCONST_Weights, weights, 1);
457
458 // D3DRS_ALPHATESTENABLE defaults to FALSE
459 // D3DRS_ALPHAREF defaults to 0
460 D3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_NOTEQUAL);
461 AlphaTestEnabled = FALSE;
462
463 CurBorderColor = 0;
464
465 // Clear to black, just in case it wasn't done already.
466 D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 0, 0);
467 }
468
469 //==========================================================================
470 //
471 // D3DFB :: FillPresentParameters
472 //
473 //==========================================================================
474
FillPresentParameters(D3DPRESENT_PARAMETERS * pp,bool fullscreen,bool vsync)475 void D3DFB::FillPresentParameters (D3DPRESENT_PARAMETERS *pp, bool fullscreen, bool vsync)
476 {
477 memset (pp, 0, sizeof(*pp));
478 pp->Windowed = !fullscreen;
479 pp->SwapEffect = D3DSWAPEFFECT_DISCARD;
480 pp->BackBufferWidth = Width << PixelDoubling;
481 pp->BackBufferHeight = TrueHeight << PixelDoubling;
482 pp->BackBufferFormat = fullscreen ? D3DFMT_A8R8G8B8 : D3DFMT_UNKNOWN;
483 pp->BackBufferCount = 1;
484 pp->hDeviceWindow = Window;
485 pp->PresentationInterval = vsync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
486 if (fullscreen)
487 {
488 pp->FullScreen_RefreshRateInHz = vid_refreshrate;
489 }
490 }
491
492 //==========================================================================
493 //
494 // D3DFB :: CreateResources
495 //
496 //==========================================================================
497
CreateResources()498 bool D3DFB::CreateResources()
499 {
500 Atlases = NULL;
501 if (!Windowed)
502 {
503 // Remove the window border in fullscreen mode
504 SetWindowLong (Window, GWL_STYLE, WS_POPUP|WS_VISIBLE|WS_SYSMENU);
505 }
506 else
507 {
508 // Resize the window to match desired dimensions
509 RECT rect = { 0, 0, Width, Height };
510 AdjustWindowRectEx(&rect, WS_VISIBLE|WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW);
511 int sizew = rect.right - rect.left;
512 int sizeh = rect.bottom - rect.top;
513 LOG2 ("Resize window to %dx%d\n", sizew, sizeh);
514 VidResizing = true;
515 // Make sure the window has a border in windowed mode
516 SetWindowLong(Window, GWL_STYLE, WS_VISIBLE|WS_OVERLAPPEDWINDOW);
517 if (GetWindowLong(Window, GWL_EXSTYLE) & WS_EX_TOPMOST)
518 {
519 // Direct3D 9 will apparently add WS_EX_TOPMOST to fullscreen windows,
520 // and removing it is a little tricky. Using SetWindowLongPtr to clear it
521 // will not do the trick, but sending the window behind everything will.
522 SetWindowPos(Window, HWND_BOTTOM, 0, 0, sizew, sizeh,
523 SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE);
524 SetWindowPos(Window, HWND_TOP, 0, 0, 0, 0, SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE);
525 }
526 else
527 {
528 SetWindowPos(Window, NULL, 0, 0, sizew, sizeh,
529 SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER);
530 }
531 I_RestoreWindowedPos();
532 VidResizing = false;
533 }
534 if (!LoadShaders())
535 {
536 return false;
537 }
538 if (!CreateFBTexture() ||
539 !CreatePaletteTexture())
540 {
541 return false;
542 }
543 if (!CreateVertexes())
544 {
545 return false;
546 }
547 CreateGammaTexture();
548 CreateBlockSurfaces();
549 return true;
550 }
551
552 //==========================================================================
553 //
554 // D3DFB :: LoadShaders
555 //
556 // Returns true if all required shaders were loaded. (Gamma and burn wipe
557 // are the only ones not considered "required".)
558 //
559 //==========================================================================
560
LoadShaders()561 bool D3DFB::LoadShaders()
562 {
563 static const char models[][4] = { "30/", "20/", "14/" };
564 FString shaderdir, shaderpath;
565 unsigned model, i;
566 int lump;
567
568 // We determine the best available model simply by trying them all in
569 // order of decreasing preference.
570 for (model = 0; model < countof(models); ++model)
571 {
572 shaderdir = "shaders/d3d/sm";
573 shaderdir += models[model];
574 for (i = 0; i < NUM_SHADERS; ++i)
575 {
576 shaderpath = shaderdir;
577 shaderpath += ShaderNames[i];
578 lump = Wads.CheckNumForFullName(shaderpath);
579 if (lump >= 0)
580 {
581 FMemLump data = Wads.ReadLump(lump);
582 if (FAILED(D3DDevice->CreatePixelShader((DWORD *)data.GetMem(), &Shaders[i])) &&
583 i < SHADER_BurnWipe)
584 {
585 break;
586 }
587 }
588 }
589 if (i == NUM_SHADERS)
590 { // Success!
591 SM14 = (model == countof(models) - 1);
592 return true;
593 }
594 // Failure. Release whatever managed to load (which is probably nothing.)
595 for (i = 0; i < NUM_SHADERS; ++i)
596 {
597 SAFE_RELEASE( Shaders[i] );
598 }
599 }
600 return false;
601 }
602
603 //==========================================================================
604 //
605 // D3DFB :: ReleaseResources
606 //
607 //==========================================================================
608
ReleaseResources()609 void D3DFB::ReleaseResources ()
610 {
611 I_SaveWindowedPos ();
612 KillNativeTexs();
613 KillNativePals();
614 ReleaseDefaultPoolItems();
615 SAFE_RELEASE( ScreenshotSurface );
616 SAFE_RELEASE( ScreenshotTexture );
617 SAFE_RELEASE( PaletteTexture );
618 for (int i = 0; i < NUM_SHADERS; ++i)
619 {
620 SAFE_RELEASE( Shaders[i] );
621 }
622 GammaShader = NULL;
623 if (ScreenWipe != NULL)
624 {
625 delete ScreenWipe;
626 ScreenWipe = NULL;
627 }
628 Atlas *pack, *next;
629 for (pack = Atlases; pack != NULL; pack = next)
630 {
631 next = pack->Next;
632 delete pack;
633 }
634 GatheringWipeScreen = false;
635 }
636
637 //==========================================================================
638 //
639 // D3DFB :: ReleaseDefaultPoolItems
640 //
641 // Free resources created with D3DPOOL_DEFAULT.
642 //
643 //==========================================================================
644
ReleaseDefaultPoolItems()645 void D3DFB::ReleaseDefaultPoolItems()
646 {
647 SAFE_RELEASE( FBTexture );
648 SAFE_RELEASE( FinalWipeScreen );
649 SAFE_RELEASE( RenderTexture[0] );
650 SAFE_RELEASE( RenderTexture[1] );
651 SAFE_RELEASE( InitialWipeScreen );
652 SAFE_RELEASE( VertexBuffer );
653 SAFE_RELEASE( IndexBuffer );
654 SAFE_RELEASE( BlockSurface[0] );
655 SAFE_RELEASE( BlockSurface[1] );
656 SAFE_RELEASE( FrontCopySurface );
657 }
658
659 //==========================================================================
660 //
661 // D3DFB :: Reset
662 //
663 //==========================================================================
664
Reset()665 bool D3DFB::Reset ()
666 {
667 D3DPRESENT_PARAMETERS d3dpp;
668
669 ReleaseDefaultPoolItems();
670 FillPresentParameters (&d3dpp, !Windowed, VSync);
671 if (!SUCCEEDED(D3DDevice->Reset (&d3dpp)))
672 {
673 if (d3dpp.FullScreen_RefreshRateInHz != 0)
674 {
675 d3dpp.FullScreen_RefreshRateInHz = 0;
676 if (!SUCCEEDED(D3DDevice->Reset (&d3dpp)))
677 {
678 return false;
679 }
680 }
681 else
682 {
683 return false;
684 }
685 }
686 LOG("Device was reset\n");
687 if (!CreateFBTexture() || !CreateVertexes())
688 {
689 return false;
690 }
691 CreateBlockSurfaces();
692 SetInitialState();
693 return true;
694 }
695
696 //==========================================================================
697 //
698 // D3DFB :: CreateBlockSurfaces
699 //
700 // Create blocking surfaces for antilag. It's okay if these can't be
701 // created; antilag just won't work.
702 //
703 //==========================================================================
704
CreateBlockSurfaces()705 void D3DFB::CreateBlockSurfaces()
706 {
707 BlockNum = 0;
708 if (SUCCEEDED(D3DDevice->CreateOffscreenPlainSurface(16, 16, D3DFMT_A8R8G8B8,
709 D3DPOOL_DEFAULT, &BlockSurface[0], 0)))
710 {
711 if (FAILED(D3DDevice->CreateOffscreenPlainSurface(16, 16, D3DFMT_A8R8G8B8,
712 D3DPOOL_DEFAULT, &BlockSurface[1], 0)))
713 {
714 BlockSurface[0]->Release();
715 BlockSurface[0] = NULL;
716 }
717 }
718 }
719
720 //==========================================================================
721 //
722 // D3DFB :: KillNativePals
723 //
724 // Frees all native palettes.
725 //
726 //==========================================================================
727
KillNativePals()728 void D3DFB::KillNativePals()
729 {
730 while (Palettes != NULL)
731 {
732 delete Palettes;
733 }
734 }
735
736 //==========================================================================
737 //
738 // D3DFB :: KillNativeTexs
739 //
740 // Frees all native textures.
741 //
742 //==========================================================================
743
KillNativeTexs()744 void D3DFB::KillNativeTexs()
745 {
746 while (Textures != NULL)
747 {
748 delete Textures;
749 }
750 }
751
752 //==========================================================================
753 //
754 // D3DFB :: CreateFBTexture
755 //
756 // Creates the "Framebuffer" texture. With the advent of hardware-assisted
757 // 2D, this is something of a misnomer now. The FBTexture is only used for
758 // uploading the software 3D image to video memory so that it can be drawn
759 // to the real frame buffer.
760 //
761 // It also creates the TempRenderTexture, since this seemed like a
762 // convenient place to do so.
763 //
764 //==========================================================================
765
CreateFBTexture()766 bool D3DFB::CreateFBTexture ()
767 {
768 if (FAILED(D3DDevice->CreateTexture(Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL)))
769 {
770 int pow2width, pow2height, i;
771
772 for (i = 1; i < Width; i <<= 1) {} pow2width = i;
773 for (i = 1; i < Height; i <<= 1) {} pow2height = i;
774
775 if (FAILED(D3DDevice->CreateTexture(pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL)))
776 {
777 return false;
778 }
779 else
780 {
781 FBWidth = pow2width;
782 FBHeight = pow2height;
783 }
784 }
785 else
786 {
787 FBWidth = Width;
788 FBHeight = Height;
789 }
790 RenderTextureToggle = 0;
791 RenderTexture[0] = NULL;
792 RenderTexture[1] = NULL;
793 if (FAILED(D3DDevice->CreateTexture(FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &RenderTexture[0], NULL)))
794 {
795 return false;
796 }
797 if (Windowed || PixelDoubling)
798 {
799 // Windowed or pixel doubling: Create another render texture so we can flip between them.
800 RenderTextureToggle = 1;
801 if (FAILED(D3DDevice->CreateTexture(FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &RenderTexture[1], NULL)))
802 {
803 return false;
804 }
805 }
806 else
807 {
808 // Fullscreen and not pixel doubling: Create a render target to have the back buffer copied to.
809 if (FAILED(D3DDevice->CreateRenderTarget(Width, Height, D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &FrontCopySurface, NULL)))
810 {
811 return false;
812 }
813 }
814 // Initialize the TempRenderTextures to black.
815 for (int i = 0; i <= RenderTextureToggle; ++i)
816 {
817 IDirect3DSurface9 *surf;
818 if (SUCCEEDED(RenderTexture[i]->GetSurfaceLevel(0, &surf)))
819 {
820 D3DDevice->ColorFill(surf, NULL, D3DCOLOR_XRGB(0,0,0));
821 surf->Release();
822 }
823 }
824 TempRenderTexture = RenderTexture[0];
825 CurrRenderTexture = 0;
826 return true;
827 }
828
829 //==========================================================================
830 //
831 // D3DFB :: CreatePaletteTexture
832 //
833 //==========================================================================
834
CreatePaletteTexture()835 bool D3DFB::CreatePaletteTexture ()
836 {
837 if (FAILED(D3DDevice->CreateTexture (256, 1, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &PaletteTexture, NULL)))
838 {
839 return false;
840 }
841 return true;
842 }
843
844 //==========================================================================
845 //
846 // D3DFB :: CreateGammaTexture
847 //
848 //==========================================================================
849
CreateGammaTexture()850 bool D3DFB::CreateGammaTexture ()
851 {
852 // If this fails, you just won't get gamma correction in a window
853 // on SM14 cards.
854 assert(GammaTexture == NULL);
855 if (SM14)
856 {
857 return SUCCEEDED(D3DDevice->CreateTexture(256, 1, 1, 0, D3DFMT_A8R8G8B8,
858 D3DPOOL_MANAGED, &GammaTexture, NULL));
859 }
860 return false;
861 }
862
863 //==========================================================================
864 //
865 // D3DFB :: CreateVertexes
866 //
867 //==========================================================================
868
CreateVertexes()869 bool D3DFB::CreateVertexes ()
870 {
871 VertexPos = -1;
872 IndexPos = -1;
873 QuadBatchPos = -1;
874 BatchType = BATCH_None;
875 if (FAILED(D3DDevice->CreateVertexBuffer(sizeof(FBVERTEX)*NUM_VERTS,
876 D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_FBVERTEX, D3DPOOL_DEFAULT, &VertexBuffer, NULL)))
877 {
878 return false;
879 }
880 if (FAILED(D3DDevice->CreateIndexBuffer(sizeof(WORD)*NUM_INDEXES,
881 D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &IndexBuffer, NULL)))
882 {
883 return false;
884 }
885 return true;
886 }
887
888 //==========================================================================
889 //
890 // D3DFB :: CalcFullscreenCoords
891 //
892 //==========================================================================
893
CalcFullscreenCoords(FBVERTEX verts[4],bool viewarea_only,bool can_double,D3DCOLOR color0,D3DCOLOR color1) const894 void D3DFB::CalcFullscreenCoords (FBVERTEX verts[4], bool viewarea_only, bool can_double, D3DCOLOR color0, D3DCOLOR color1) const
895 {
896 float offset = OldRenderTarget != NULL ? 0 : LBOffset;
897 float top = offset - 0.5f;
898 float texright = float(Width) / float(FBWidth);
899 float texbot = float(Height) / float(FBHeight);
900 float mxl, mxr, myt, myb, tmxl, tmxr, tmyt, tmyb;
901
902 if (viewarea_only)
903 { // Just calculate vertices for the viewarea/BlendingRect
904 mxl = float(BlendingRect.left) - 0.5f;
905 mxr = float(BlendingRect.right) - 0.5f;
906 myt = float(BlendingRect.top) + top;
907 myb = float(BlendingRect.bottom) + top;
908 tmxl = float(BlendingRect.left) / float(Width) * texright;
909 tmxr = float(BlendingRect.right) / float(Width) * texright;
910 tmyt = float(BlendingRect.top) / float(Height) * texbot;
911 tmyb = float(BlendingRect.bottom) / float(Height) * texbot;
912 }
913 else
914 { // Calculate vertices for the whole screen
915 mxl = -0.5f;
916 mxr = float(Width << (can_double ? PixelDoubling : 0)) - 0.5f;
917 myt = top;
918 myb = float(Height << (can_double ? PixelDoubling : 0)) + top;
919 tmxl = 0;
920 tmxr = texright;
921 tmyt = 0;
922 tmyb = texbot;
923 }
924
925 //{ mxl, myt, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyt },
926 //{ mxr, myt, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyt },
927 //{ mxr, myb, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyb },
928 //{ mxl, myb, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyb },
929
930 verts[0].x = mxl;
931 verts[0].y = myt;
932 verts[0].z = 0;
933 verts[0].rhw = 1;
934 verts[0].color0 = color0;
935 verts[0].color1 = color1;
936 verts[0].tu = tmxl;
937 verts[0].tv = tmyt;
938
939 verts[1].x = mxr;
940 verts[1].y = myt;
941 verts[1].z = 0;
942 verts[1].rhw = 1;
943 verts[1].color0 = color0;
944 verts[1].color1 = color1;
945 verts[1].tu = tmxr;
946 verts[1].tv = tmyt;
947
948 verts[2].x = mxr;
949 verts[2].y = myb;
950 verts[2].z = 0;
951 verts[2].rhw = 1;
952 verts[2].color0 = color0;
953 verts[2].color1 = color1;
954 verts[2].tu = tmxr;
955 verts[2].tv = tmyb;
956
957 verts[3].x = mxl;
958 verts[3].y = myb;
959 verts[3].z = 0;
960 verts[3].rhw = 1;
961 verts[3].color0 = color0;
962 verts[3].color1 = color1;
963 verts[3].tu = tmxl;
964 verts[3].tv = tmyb;
965 }
966
967 //==========================================================================
968 //
969 // D3DFB :: GetPageCount
970 //
971 //==========================================================================
972
GetPageCount()973 int D3DFB::GetPageCount ()
974 {
975 return 1;
976 }
977
978 //==========================================================================
979 //
980 // D3DFB :: PaletteChanged
981 //
982 //==========================================================================
983
PaletteChanged()984 void D3DFB::PaletteChanged ()
985 {
986 }
987
988 //==========================================================================
989 //
990 // D3DFB :: QueryNewPalette
991 //
992 //==========================================================================
993
QueryNewPalette()994 int D3DFB::QueryNewPalette ()
995 {
996 return 0;
997 }
998
999 //==========================================================================
1000 //
1001 // D3DFB :: IsValid
1002 //
1003 //==========================================================================
1004
IsValid()1005 bool D3DFB::IsValid ()
1006 {
1007 return D3DDevice != NULL;
1008 }
1009
1010 //==========================================================================
1011 //
1012 // D3DFB :: GetHR
1013 //
1014 //==========================================================================
1015
GetHR()1016 HRESULT D3DFB::GetHR ()
1017 {
1018 return LastHR;
1019 }
1020
1021 //==========================================================================
1022 //
1023 // D3DFB :: IsFullscreen
1024 //
1025 //==========================================================================
1026
IsFullscreen()1027 bool D3DFB::IsFullscreen ()
1028 {
1029 return !Windowed;
1030 }
1031
1032 //==========================================================================
1033 //
1034 // D3DFB :: Lock
1035 //
1036 //==========================================================================
1037
Lock(bool buffered)1038 bool D3DFB::Lock (bool buffered)
1039 {
1040 if (LockCount++ > 0)
1041 {
1042 return false;
1043 }
1044 assert (!In2D);
1045 Accel2D = vid_hw2d;
1046 Buffer = MemBuffer;
1047 return false;
1048 }
1049
1050 //==========================================================================
1051 //
1052 // D3DFB :: Unlock
1053 //
1054 //==========================================================================
1055
Unlock()1056 void D3DFB::Unlock ()
1057 {
1058 LOG1 ("Unlock <%d>\n", LockCount);
1059
1060 if (LockCount == 0)
1061 {
1062 return;
1063 }
1064
1065 if (UpdatePending && LockCount == 1)
1066 {
1067 Update ();
1068 }
1069 else if (--LockCount == 0)
1070 {
1071 Buffer = NULL;
1072 }
1073 }
1074
1075 //==========================================================================
1076 //
1077 // D3DFB :: Update
1078 //
1079 // When In2D == 0: Copy buffer to screen and present
1080 // When In2D == 1: Copy buffer to screen but do not present
1081 // When In2D == 2: Set up for 2D drawing but do not draw anything
1082 // When In2D == 3: Present and set In2D to 0
1083 //
1084 //==========================================================================
1085
Update()1086 void D3DFB::Update ()
1087 {
1088 if (In2D == 3)
1089 {
1090 if (InScene)
1091 {
1092 DrawRateStuff();
1093 DrawPackedTextures(d3d_showpacks);
1094 EndBatch(); // Make sure all batched primitives are drawn.
1095 Flip();
1096 }
1097 In2D = 0;
1098 return;
1099 }
1100
1101 if (LockCount != 1)
1102 {
1103 I_FatalError ("Framebuffer must have exactly 1 lock to be updated");
1104 if (LockCount > 0)
1105 {
1106 UpdatePending = true;
1107 --LockCount;
1108 }
1109 return;
1110 }
1111
1112 if (In2D == 0)
1113 {
1114 DrawRateStuff();
1115 }
1116
1117 if (NeedGammaUpdate)
1118 {
1119 float psgamma[4];
1120 float igamma;
1121
1122 NeedGammaUpdate = false;
1123 igamma = 1 / Gamma;
1124 if (!Windowed)
1125 {
1126 D3DGAMMARAMP ramp;
1127
1128 for (int i = 0; i < 256; ++i)
1129 {
1130 ramp.blue[i] = ramp.green[i] = ramp.red[i] = WORD(65535.f * powf(i / 255.f, igamma));
1131 }
1132 LOG("SetGammaRamp\n");
1133 D3DDevice->SetGammaRamp(0, D3DSGR_CALIBRATE, &ramp);
1134 }
1135 else
1136 {
1137 if (igamma != 1)
1138 {
1139 UpdateGammaTexture(igamma);
1140 GammaShader = Shaders[SHADER_GammaCorrection];
1141 }
1142 else
1143 {
1144 GammaShader = NULL;
1145 }
1146 }
1147 psgamma[2] = psgamma[1] = psgamma[0] = igamma;
1148 psgamma[3] = 0.5; // For SM14 version
1149 D3DDevice->SetPixelShaderConstantF(PSCONST_Gamma, psgamma, 1);
1150 }
1151
1152 if (NeedPalUpdate)
1153 {
1154 UploadPalette();
1155 }
1156
1157 BlitCycles.Reset();
1158 BlitCycles.Clock();
1159
1160 LockCount = 0;
1161 HRESULT hr = D3DDevice->TestCooperativeLevel();
1162 if (FAILED(hr) && (hr != D3DERR_DEVICENOTRESET || !Reset()))
1163 {
1164 Sleep(1);
1165 return;
1166 }
1167 Draw3DPart(In2D <= 1);
1168 if (In2D == 0)
1169 {
1170 Flip();
1171 }
1172
1173 BlitCycles.Unclock();
1174 //LOG1 ("cycles = %d\n", BlitCycles);
1175
1176 Buffer = NULL;
1177 UpdatePending = false;
1178 }
1179
1180 //==========================================================================
1181 //
1182 // D3DFB :: Flip
1183 //
1184 //==========================================================================
1185
Flip()1186 void D3DFB::Flip()
1187 {
1188 assert(InScene);
1189
1190 DrawLetterbox();
1191 DoWindowedGamma();
1192 D3DDevice->EndScene();
1193
1194 CopyNextFrontBuffer();
1195
1196 // Attempt to counter input lag.
1197 if (d3d_antilag && BlockSurface[0] != NULL)
1198 {
1199 D3DLOCKED_RECT lr;
1200 volatile int dummy;
1201 D3DDevice->ColorFill(BlockSurface[BlockNum], NULL, D3DCOLOR_ARGB(0xFF,0,0x20,0x50));
1202 BlockNum ^= 1;
1203 if (!FAILED((BlockSurface[BlockNum]->LockRect(&lr, NULL, D3DLOCK_READONLY))))
1204 {
1205 dummy = *(int *)lr.pBits;
1206 BlockSurface[BlockNum]->UnlockRect();
1207 }
1208 }
1209 // Limiting the frame rate is as simple as waiting for the timer to signal this event.
1210 if (FPSLimitEvent != NULL)
1211 {
1212 WaitForSingleObject(FPSLimitEvent, 1000);
1213 }
1214 D3DDevice->Present(NULL, NULL, NULL, NULL);
1215 InScene = false;
1216
1217 if (RenderTextureToggle)
1218 {
1219 // Flip the TempRenderTexture to the other one now.
1220 CurrRenderTexture ^= RenderTextureToggle;
1221 TempRenderTexture = RenderTexture[CurrRenderTexture];
1222 }
1223 }
1224
1225 //==========================================================================
1226 //
1227 // D3DFB :: CopyNextFrontBuffer
1228 //
1229 // Duplicates the contents of the back buffer that will become the front
1230 // buffer upon Present into FrontCopySurface so that we can get the
1231 // contents of the display without wasting time in GetFrontBufferData().
1232 //
1233 //==========================================================================
1234
CopyNextFrontBuffer()1235 void D3DFB::CopyNextFrontBuffer()
1236 {
1237 IDirect3DSurface9 *backbuff;
1238
1239 if (Windowed || PixelDoubling)
1240 {
1241 // Windowed mode or pixel doubling: TempRenderTexture has what we want
1242 SAFE_RELEASE( FrontCopySurface );
1243 if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &backbuff)))
1244 {
1245 FrontCopySurface = backbuff;
1246 }
1247 }
1248 else
1249 {
1250 // Fullscreen, not pixel doubled: The back buffer has what we want,
1251 // but it might be letter boxed.
1252 if (SUCCEEDED(D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuff)))
1253 {
1254 RECT srcrect = { 0, LBOffsetI, Width, LBOffsetI + Height };
1255 D3DDevice->StretchRect(backbuff, &srcrect, FrontCopySurface, NULL, D3DTEXF_NONE);
1256 backbuff->Release();
1257 }
1258 }
1259 }
1260
1261 //==========================================================================
1262 //
1263 // D3DFB :: PaintToWindow
1264 //
1265 //==========================================================================
1266
PaintToWindow()1267 bool D3DFB::PaintToWindow ()
1268 {
1269 HRESULT hr;
1270
1271 if (LockCount != 0)
1272 {
1273 return false;
1274 }
1275 hr = D3DDevice->TestCooperativeLevel();
1276 if (FAILED(hr))
1277 {
1278 if (hr != D3DERR_DEVICENOTRESET || !Reset())
1279 {
1280 Sleep (1);
1281 return false;
1282 }
1283 }
1284 Draw3DPart(true);
1285 return true;
1286 }
1287
1288 //==========================================================================
1289 //
1290 // D3DFB :: Draw3DPart
1291 //
1292 // The software 3D part, to be exact.
1293 //
1294 //==========================================================================
1295
Draw3DPart(bool copy3d)1296 void D3DFB::Draw3DPart(bool copy3d)
1297 {
1298 if (copy3d)
1299 {
1300 RECT texrect = { 0, 0, Width, Height };
1301 D3DLOCKED_RECT lockrect;
1302
1303 if ((FBWidth == Width && FBHeight == Height &&
1304 SUCCEEDED(FBTexture->LockRect (0, &lockrect, NULL, D3DLOCK_DISCARD))) ||
1305 SUCCEEDED(FBTexture->LockRect (0, &lockrect, &texrect, 0)))
1306 {
1307 if (lockrect.Pitch == Pitch && Pitch == Width)
1308 {
1309 memcpy (lockrect.pBits, MemBuffer, Width * Height);
1310 }
1311 else
1312 {
1313 BYTE *dest = (BYTE *)lockrect.pBits;
1314 BYTE *src = MemBuffer;
1315 for (int y = 0; y < Height; y++)
1316 {
1317 memcpy (dest, src, Width);
1318 dest += lockrect.Pitch;
1319 src += Pitch;
1320 }
1321 }
1322 FBTexture->UnlockRect (0);
1323 }
1324 }
1325 InScene = true;
1326 D3DDevice->BeginScene();
1327 D3DDevice->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, vid_hwaalines);
1328 assert(OldRenderTarget == NULL);
1329 if (TempRenderTexture != NULL &&
1330 ((Windowed && TempRenderTexture != FinalWipeScreen) || GatheringWipeScreen || PixelDoubling))
1331 {
1332 IDirect3DSurface9 *targetsurf;
1333 if (SUCCEEDED(TempRenderTexture->GetSurfaceLevel(0, &targetsurf)))
1334 {
1335 if (SUCCEEDED(D3DDevice->GetRenderTarget(0, &OldRenderTarget)))
1336 {
1337 if (FAILED(D3DDevice->SetRenderTarget(0, targetsurf)))
1338 {
1339 // Setting the render target failed.
1340 }
1341 }
1342 targetsurf->Release();
1343 }
1344 }
1345
1346 SetTexture(0, FBTexture);
1347 SetPaletteTexture(PaletteTexture, 256, BorderColor);
1348 D3DDevice->SetFVF (D3DFVF_FBVERTEX);
1349 memset(Constant, 0, sizeof(Constant));
1350 SetAlphaBlend(D3DBLENDOP(0));
1351 EnableAlphaTest(FALSE);
1352 SetPixelShader(Shaders[SHADER_NormalColorPal]);
1353 if (copy3d)
1354 {
1355 FBVERTEX verts[4];
1356 D3DCOLOR color0, color1;
1357 if (Accel2D)
1358 {
1359 if (realfixedcolormap == NULL)
1360 {
1361 color0 = 0;
1362 color1 = 0xFFFFFFF;
1363 }
1364 else
1365 {
1366 color0 = D3DCOLOR_COLORVALUE(realfixedcolormap->ColorizeStart[0]/2,
1367 realfixedcolormap->ColorizeStart[1]/2, realfixedcolormap->ColorizeStart[2]/2, 0);
1368 color1 = D3DCOLOR_COLORVALUE(realfixedcolormap->ColorizeEnd[0]/2,
1369 realfixedcolormap->ColorizeEnd[1]/2, realfixedcolormap->ColorizeEnd[2]/2, 1);
1370 SetPixelShader(Shaders[SHADER_SpecialColormapPal]);
1371 }
1372 }
1373 else
1374 {
1375 color0 = FlashColor0;
1376 color1 = FlashColor1;
1377 }
1378 CalcFullscreenCoords(verts, Accel2D, false, color0, color1);
1379 D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX));
1380 }
1381 SetPixelShader(Shaders[SHADER_NormalColorPal]);
1382 }
1383
1384 //==========================================================================
1385 //
1386 // D3DFB :: DrawLetterbox
1387 //
1388 // Draws the black bars at the top and bottom of the screen for letterboxed
1389 // modes.
1390 //
1391 //==========================================================================
1392
DrawLetterbox()1393 void D3DFB::DrawLetterbox()
1394 {
1395 if (LBOffsetI != 0)
1396 {
1397 D3DRECT rects[2] = { { 0, 0, Width, LBOffsetI }, { 0, Height + LBOffsetI, Width, TrueHeight } };
1398 D3DDevice->Clear (2, rects, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.f, 0);
1399 }
1400 }
1401
1402 //==========================================================================
1403 //
1404 // D3DFB :: DoWindowedGamma
1405 //
1406 // Draws the render target texture to the real back buffer using a gamma-
1407 // correcting pixel shader.
1408 //
1409 //==========================================================================
1410
DoWindowedGamma()1411 void D3DFB::DoWindowedGamma()
1412 {
1413 if (OldRenderTarget != NULL)
1414 {
1415 FBVERTEX verts[4];
1416
1417 CalcFullscreenCoords(verts, false, true, 0, 0xFFFFFFFF);
1418 D3DDevice->SetRenderTarget(0, OldRenderTarget);
1419 D3DDevice->SetFVF(D3DFVF_FBVERTEX);
1420 SetTexture(0, TempRenderTexture);
1421 SetPixelShader(Windowed && GammaShader ? GammaShader : Shaders[SHADER_NormalColor]);
1422 if (SM14 && Windowed && GammaShader)
1423 {
1424 SetTexture(2, GammaTexture);
1425 SetTexture(3, GammaTexture);
1426 SetTexture(4, GammaTexture);
1427 }
1428 SetAlphaBlend(D3DBLENDOP(0));
1429 EnableAlphaTest(FALSE);
1430 D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX));
1431 OldRenderTarget->Release();
1432 OldRenderTarget = NULL;
1433 if (SM14 && Windowed && GammaShader)
1434 {
1435 // SetTexture(0, GammaTexture);
1436 // SetPixelShader(Shaders[SHADER_NormalColor]);
1437 // D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX));
1438 }
1439 }
1440 }
1441
1442 //==========================================================================
1443 //
1444 // D3DFB :: UpdateGammaTexture
1445 //
1446 // Updates the gamma texture used by the PS14 shader. We only use the first
1447 // half of the texture so that we needn't worry about imprecision causing
1448 // it to grab from the border.
1449 //
1450 //==========================================================================
1451
UpdateGammaTexture(float igamma)1452 void D3DFB::UpdateGammaTexture(float igamma)
1453 {
1454 D3DLOCKED_RECT lockrect;
1455
1456 if (GammaTexture != NULL && SUCCEEDED(GammaTexture->LockRect(0, &lockrect, NULL, 0)))
1457 {
1458 BYTE *pix = (BYTE *)lockrect.pBits;
1459 for (int i = 0; i <= 128; ++i)
1460 {
1461 pix[i*4+2] = pix[i*4+1] = pix[i*4] = BYTE(255.f * powf(i / 128.f, igamma));
1462 pix[i*4+3] = 255;
1463 }
1464 GammaTexture->UnlockRect(0);
1465 }
1466 }
1467
1468 //==========================================================================
1469 //
1470 // D3DFB :: DoOffByOneCheck
1471 //
1472 // Pixel Shader 1.x does not have enough precision to properly map a "color"
1473 // from the source texture to an index in the palette texture. The best we
1474 // can do is use 255 pixels of the palette and get the 256th from the
1475 // texture border color. This routine determines which pixel of the texture
1476 // is skipped so that we don't use it for palette data.
1477 //
1478 //==========================================================================
1479
DoOffByOneCheck()1480 void D3DFB::DoOffByOneCheck ()
1481 {
1482 IDirect3DSurface9 *savedrendertarget;
1483 IDirect3DSurface9 *testsurf, *readsurf;
1484 D3DLOCKED_RECT lockrect;
1485 RECT testrect = { 0, 0, 256, 1 };
1486 float texright = 256.f / float(FBWidth);
1487 float texbot = 1.f / float(FBHeight);
1488 FBVERTEX verts[4] =
1489 {
1490 { -0.5f, -0.5f, 0.5f, 1.f, 0, ~0, 0.f, 0.f },
1491 { 255.5f, -0.5f, 0.5f, 1.f, 0, ~0, texright, 0.f },
1492 { 255.5f, 0.5f, 0.5f, 1.f, 0, ~0, texright, texbot },
1493 { -0.5f, 0.5f, 0.5f, 1.f, 0, ~0, 0.f, texbot }
1494 };
1495 int i, c;
1496
1497 if (SkipAt >= 0)
1498 {
1499 return;
1500 }
1501
1502 // Create an easily recognizable R3G3B2 palette.
1503 if (SUCCEEDED(PaletteTexture->LockRect(0, &lockrect, NULL, 0)))
1504 {
1505 BYTE *pal = (BYTE *)(lockrect.pBits);
1506 for (i = 0; i < 256; ++i)
1507 {
1508 pal[i*4+0] = (i & 0x03) << 6; // blue
1509 pal[i*4+1] = (i & 0x1C) << 3; // green
1510 pal[i*4+2] = (i & 0xE0); // red;
1511 pal[i*4+3] = 255;
1512 }
1513 PaletteTexture->UnlockRect (0);
1514 }
1515 else
1516 {
1517 return;
1518 }
1519 // Prepare a texture with values 0-256.
1520 if (SUCCEEDED(FBTexture->LockRect(0, &lockrect, &testrect, 0)))
1521 {
1522 for (i = 0; i < 256; ++i)
1523 {
1524 ((BYTE *)lockrect.pBits)[i] = i;
1525 }
1526 FBTexture->UnlockRect(0);
1527 }
1528 else
1529 {
1530 return;
1531 }
1532 // Create a render target that we can draw it to.
1533 if (FAILED(D3DDevice->GetRenderTarget(0, &savedrendertarget)))
1534 {
1535 return;
1536 }
1537 if (FAILED(D3DDevice->CreateRenderTarget(256, 1, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &testsurf, NULL)))
1538 {
1539 return;
1540 }
1541 if (FAILED(D3DDevice->CreateOffscreenPlainSurface(256, 1, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &readsurf, NULL)))
1542 {
1543 testsurf->Release();
1544 return;
1545 }
1546 if (FAILED(D3DDevice->SetRenderTarget(0, testsurf)))
1547 {
1548 testsurf->Release();
1549 readsurf->Release();
1550 return;
1551 }
1552 // Write it to the render target using the pixel shader.
1553 D3DDevice->BeginScene();
1554 D3DDevice->SetTexture(0, FBTexture);
1555 D3DDevice->SetTexture(1, PaletteTexture);
1556 D3DDevice->SetFVF(D3DFVF_FBVERTEX);
1557 D3DDevice->SetPixelShader(Shaders[SHADER_NormalColorPal]);
1558 SetConstant(PSCONST_PaletteMod, 1.f, 0.5f / 256.f, 0, 0);
1559 D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX));
1560 D3DDevice->EndScene();
1561 D3DDevice->SetRenderTarget(0, savedrendertarget);
1562 savedrendertarget->Release();
1563 // Now read it back and see where it skips an entry
1564 if (SUCCEEDED(D3DDevice->GetRenderTargetData(testsurf, readsurf)) &&
1565 SUCCEEDED(readsurf->LockRect(&lockrect, &testrect, D3DLOCK_READONLY)))
1566 {
1567 const BYTE *pix = (const BYTE *)lockrect.pBits;
1568 for (i = 0; i < 256; ++i, pix += 4)
1569 {
1570 c = (pix[0] >> 6) | // blue
1571 ((pix[1] >> 5) << 2) | // green
1572 ((pix[2] >> 5) << 5); // red
1573 if (c != i)
1574 {
1575 break;
1576 }
1577 }
1578 }
1579 readsurf->UnlockRect();
1580 readsurf->Release();
1581 testsurf->Release();
1582 SkipAt = i;
1583 }
1584
UploadPalette()1585 void D3DFB::UploadPalette ()
1586 {
1587 D3DLOCKED_RECT lockrect;
1588
1589 if (SkipAt < 0)
1590 {
1591 if (SM14)
1592 {
1593 DoOffByOneCheck();
1594 }
1595 else
1596 {
1597 SkipAt = 256;
1598 }
1599 }
1600 if (SUCCEEDED(PaletteTexture->LockRect(0, &lockrect, NULL, 0)))
1601 {
1602 BYTE *pix = (BYTE *)lockrect.pBits;
1603 int i;
1604
1605 for (i = 0; i < SkipAt; ++i, pix += 4)
1606 {
1607 pix[0] = SourcePalette[i].b;
1608 pix[1] = SourcePalette[i].g;
1609 pix[2] = SourcePalette[i].r;
1610 pix[3] = (i == 0 ? 0 : 255);
1611 // To let masked textures work, the first palette entry's alpha is 0.
1612 }
1613 pix += 4;
1614 for (; i < 255; ++i, pix += 4)
1615 {
1616 pix[0] = SourcePalette[i].b;
1617 pix[1] = SourcePalette[i].g;
1618 pix[2] = SourcePalette[i].r;
1619 pix[3] = 255;
1620 }
1621 PaletteTexture->UnlockRect(0);
1622 BorderColor = D3DCOLOR_XRGB(SourcePalette[255].r, SourcePalette[255].g, SourcePalette[255].b);
1623 }
1624 }
1625
GetPalette()1626 PalEntry *D3DFB::GetPalette ()
1627 {
1628 return SourcePalette;
1629 }
1630
UpdatePalette()1631 void D3DFB::UpdatePalette ()
1632 {
1633 NeedPalUpdate = true;
1634 }
1635
SetGamma(float gamma)1636 bool D3DFB::SetGamma (float gamma)
1637 {
1638 LOG1 ("SetGamma %g\n", gamma);
1639 Gamma = gamma;
1640 NeedGammaUpdate = true;
1641 return true;
1642 }
1643
SetFlash(PalEntry rgb,int amount)1644 bool D3DFB::SetFlash (PalEntry rgb, int amount)
1645 {
1646 FlashColor = rgb;
1647 FlashAmount = amount;
1648
1649 // Fill in the constants for the pixel shader to do linear interpolation between the palette and the flash:
1650 float r = rgb.r / 255.f, g = rgb.g / 255.f, b = rgb.b / 255.f, a = amount / 256.f;
1651 FlashColor0 = D3DCOLOR_COLORVALUE(r * a, g * a, b * a, 0);
1652 a = 1 - a;
1653 FlashColor1 = D3DCOLOR_COLORVALUE(a, a, a, 1);
1654 return true;
1655 }
1656
GetFlash(PalEntry & rgb,int & amount)1657 void D3DFB::GetFlash (PalEntry &rgb, int &amount)
1658 {
1659 rgb = FlashColor;
1660 amount = FlashAmount;
1661 }
1662
GetFlashedPalette(PalEntry pal[256])1663 void D3DFB::GetFlashedPalette (PalEntry pal[256])
1664 {
1665 memcpy (pal, SourcePalette, 256*sizeof(PalEntry));
1666 if (FlashAmount)
1667 {
1668 DoBlending (pal, pal, 256, FlashColor.r, FlashColor.g, FlashColor.b, FlashAmount);
1669 }
1670 }
1671
SetVSync(bool vsync)1672 void D3DFB::SetVSync (bool vsync)
1673 {
1674 if (VSync != vsync)
1675 {
1676 VSync = vsync;
1677 Reset();
1678 }
1679 }
1680
NewRefreshRate()1681 void D3DFB::NewRefreshRate ()
1682 {
1683 if (!Windowed)
1684 {
1685 Reset();
1686 }
1687 }
1688
Blank()1689 void D3DFB::Blank ()
1690 {
1691 // Only used by movie player, which isn't working with D3D9 yet.
1692 }
1693
SetBlendingRect(int x1,int y1,int x2,int y2)1694 void D3DFB::SetBlendingRect(int x1, int y1, int x2, int y2)
1695 {
1696 BlendingRect.left = x1;
1697 BlendingRect.top = y1;
1698 BlendingRect.right = x2;
1699 BlendingRect.bottom = y2;
1700 }
1701
1702 //==========================================================================
1703 //
1704 // D3DFB :: GetScreenshotBuffer
1705 //
1706 // Returns a pointer into a surface holding the current screen data.
1707 //
1708 //==========================================================================
1709
GetScreenshotBuffer(const BYTE * & buffer,int & pitch,ESSType & color_type)1710 void D3DFB::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type)
1711 {
1712 D3DLOCKED_RECT lrect;
1713
1714 if (!Accel2D)
1715 {
1716 Super::GetScreenshotBuffer(buffer, pitch, color_type);
1717 return;
1718 }
1719 buffer = NULL;
1720 if ((ScreenshotTexture = GetCurrentScreen()) != NULL)
1721 {
1722 if (FAILED(ScreenshotTexture->GetSurfaceLevel(0, &ScreenshotSurface)))
1723 {
1724 ScreenshotTexture->Release();
1725 ScreenshotTexture = NULL;
1726 }
1727 else if (FAILED(ScreenshotSurface->LockRect(&lrect, NULL, D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK)))
1728 {
1729 ScreenshotSurface->Release();
1730 ScreenshotSurface = NULL;
1731 ScreenshotTexture->Release();
1732 ScreenshotTexture = NULL;
1733 }
1734 else
1735 {
1736 buffer = (const BYTE *)lrect.pBits;
1737 pitch = lrect.Pitch;
1738 color_type = SS_BGRA;
1739 }
1740 }
1741 }
1742
1743 //==========================================================================
1744 //
1745 // D3DFB :: ReleaseScreenshotBuffer
1746 //
1747 //==========================================================================
1748
ReleaseScreenshotBuffer()1749 void D3DFB::ReleaseScreenshotBuffer()
1750 {
1751 if (LockCount > 0)
1752 {
1753 Super::ReleaseScreenshotBuffer();
1754 }
1755 if (ScreenshotSurface != NULL)
1756 {
1757 ScreenshotSurface->UnlockRect();
1758 ScreenshotSurface->Release();
1759 ScreenshotSurface = NULL;
1760 }
1761 SAFE_RELEASE( ScreenshotTexture );
1762 }
1763
1764 //==========================================================================
1765 //
1766 // D3DFB :: GetCurrentScreen
1767 //
1768 // Returns a texture containing the pixels currently visible on-screen.
1769 //
1770 //==========================================================================
1771
GetCurrentScreen(D3DPOOL pool)1772 IDirect3DTexture9 *D3DFB::GetCurrentScreen(D3DPOOL pool)
1773 {
1774 IDirect3DTexture9 *tex;
1775 IDirect3DSurface9 *surf;
1776 D3DSURFACE_DESC desc;
1777 HRESULT hr;
1778
1779 assert(pool == D3DPOOL_SYSTEMMEM || pool == D3DPOOL_DEFAULT);
1780
1781 if (FrontCopySurface == NULL || FAILED(FrontCopySurface->GetDesc(&desc)))
1782 {
1783 return NULL;
1784 }
1785 if (pool == D3DPOOL_SYSTEMMEM)
1786 {
1787 hr = D3DDevice->CreateTexture(desc.Width, desc.Height, 1, 0, desc.Format, D3DPOOL_SYSTEMMEM, &tex, NULL);
1788 }
1789 else
1790 {
1791 hr = D3DDevice->CreateTexture(FBWidth, FBHeight, 1, D3DUSAGE_RENDERTARGET, desc.Format, D3DPOOL_DEFAULT, &tex, NULL);
1792 }
1793 if (FAILED(hr))
1794 {
1795 return NULL;
1796 }
1797 if (FAILED(tex->GetSurfaceLevel(0, &surf)))
1798 {
1799 tex->Release();
1800 return NULL;
1801 }
1802 if (pool == D3DPOOL_SYSTEMMEM)
1803 {
1804 // Video -> System memory : use GetRenderTargetData
1805 hr = D3DDevice->GetRenderTargetData(FrontCopySurface, surf);
1806 }
1807 else
1808 {
1809 // Video -> Video memory : use StretchRect
1810 RECT destrect = { 0, 0, Width, Height };
1811 hr = D3DDevice->StretchRect(FrontCopySurface, NULL, surf, &destrect, D3DTEXF_POINT);
1812 }
1813 surf->Release();
1814 if (FAILED(hr))
1815 {
1816 tex->Release();
1817 return NULL;
1818 }
1819 return tex;
1820 }
1821
1822 /**************************************************************************/
1823 /* 2D Stuff */
1824 /**************************************************************************/
1825
1826 //==========================================================================
1827 //
1828 // D3DFB :: DrawPackedTextures
1829 //
1830 // DEBUG: Draws the texture atlases to the screen, starting with the
1831 // 1-based packnum. Ignores atlases that are flagged for use by one
1832 // texture only.
1833 //
1834 //==========================================================================
1835
DrawPackedTextures(int packnum)1836 void D3DFB::DrawPackedTextures(int packnum)
1837 {
1838 D3DCOLOR empty_colors[8] =
1839 {
1840 0x50FF0000, 0x5000FF00, 0x500000FF, 0x50FFFF00,
1841 0x50FF00FF, 0x5000FFFF, 0x50FF8000, 0x500080FF
1842 };
1843 Atlas *pack;
1844 int x = 8, y = 8;
1845
1846 if (packnum <= 0)
1847 {
1848 return;
1849 }
1850 pack = Atlases;
1851 // Find the first texture atlas that is an actual atlas.
1852 while (pack != NULL && pack->OneUse)
1853 { // Skip textures that aren't used as atlases
1854 pack = pack->Next;
1855 }
1856 // Skip however many atlases we would have otherwise drawn
1857 // until we've skipped <packnum> of them.
1858 while (pack != NULL && packnum != 1)
1859 {
1860 if (!pack->OneUse)
1861 { // Skip textures that aren't used as atlases
1862 packnum--;
1863 }
1864 pack = pack->Next;
1865 }
1866 // Draw atlases until we run out of room on the screen.
1867 while (pack != NULL)
1868 {
1869 if (pack->OneUse)
1870 { // Skip textures that aren't used as atlases
1871 pack = pack->Next;
1872 continue;
1873 }
1874
1875 AddColorOnlyRect(x-1, y-1-LBOffsetI, 258, 258, D3DCOLOR_XRGB(255,255,0));
1876 int back = 0;
1877 for (PackedTexture *box = pack->UsedList; box != NULL; box = box->Next)
1878 {
1879 AddColorOnlyQuad(
1880 x + box->Area.left * 256 / pack->Width,
1881 y + box->Area.top * 256 / pack->Height,
1882 (box->Area.right - box->Area.left) * 256 / pack->Width,
1883 (box->Area.bottom - box->Area.top) * 256 / pack->Height, empty_colors[back]);
1884 back = (back + 1) & 7;
1885 }
1886 // AddColorOnlyQuad(x, y-LBOffsetI, 256, 256, D3DCOLOR_ARGB(180,0,0,0));
1887
1888 CheckQuadBatch();
1889
1890 BufferedTris *quad = &QuadExtra[QuadBatchPos];
1891 FBVERTEX *vert = &VertexData[VertexPos];
1892
1893 quad->Group1 = 0;
1894 if (pack->Format == D3DFMT_L8/* && !tex->IsGray*/)
1895 {
1896 quad->Flags = BQF_WrapUV | BQF_GamePalette/* | BQF_DisableAlphaTest*/;
1897 quad->ShaderNum = BQS_PalTex;
1898 }
1899 else
1900 {
1901 quad->Flags = BQF_WrapUV/* | BQF_DisableAlphaTest*/;
1902 quad->ShaderNum = BQS_Plain;
1903 }
1904 quad->Palette = NULL;
1905 quad->Texture = pack->Tex;
1906 quad->NumVerts = 4;
1907 quad->NumTris = 2;
1908
1909 float x0 = float(x) - 0.5f;
1910 float y0 = float(y) - 0.5f;
1911 float x1 = x0 + 256.f;
1912 float y1 = y0 + 256.f;
1913
1914 vert[0].x = x0;
1915 vert[0].y = y0;
1916 vert[0].z = 0;
1917 vert[0].rhw = 1;
1918 vert[0].color0 = 0;
1919 vert[0].color1 = 0xFFFFFFFF;
1920 vert[0].tu = 0;
1921 vert[0].tv = 0;
1922
1923 vert[1].x = x1;
1924 vert[1].y = y0;
1925 vert[1].z = 0;
1926 vert[1].rhw = 1;
1927 vert[1].color0 = 0;
1928 vert[1].color1 = 0xFFFFFFFF;
1929 vert[1].tu = 1;
1930 vert[1].tv = 0;
1931
1932 vert[2].x = x1;
1933 vert[2].y = y1;
1934 vert[2].z = 0;
1935 vert[2].rhw = 1;
1936 vert[2].color0 = 0;
1937 vert[2].color1 = 0xFFFFFFFF;
1938 vert[2].tu = 1;
1939 vert[2].tv = 1;
1940
1941 vert[3].x = x0;
1942 vert[3].y = y1;
1943 vert[3].z = 0;
1944 vert[3].rhw = 1;
1945 vert[3].color0 = 0;
1946 vert[3].color1 = 0xFFFFFFFF;
1947 vert[3].tu = 0;
1948 vert[3].tv = 1;
1949
1950 IndexData[IndexPos ] = VertexPos;
1951 IndexData[IndexPos + 1] = VertexPos + 1;
1952 IndexData[IndexPos + 2] = VertexPos + 2;
1953 IndexData[IndexPos + 3] = VertexPos;
1954 IndexData[IndexPos + 4] = VertexPos + 2;
1955 IndexData[IndexPos + 5] = VertexPos + 3;
1956
1957 QuadBatchPos++;
1958 VertexPos += 4;
1959 IndexPos += 6;
1960
1961 x += 256 + 8;
1962 if (x > Width - 256)
1963 {
1964 x = 8;
1965 y += 256 + 8;
1966 if (y > TrueHeight - 256)
1967 {
1968 return;
1969 }
1970 }
1971 pack = pack->Next;
1972 }
1973 }
1974
1975 //==========================================================================
1976 //
1977 // D3DFB :: AllocPackedTexture
1978 //
1979 // Finds space to pack an image inside a texture atlas and returns it.
1980 // Large images and those that need to wrap always get their own textures.
1981 //
1982 //==========================================================================
1983
AllocPackedTexture(int w,int h,bool wrapping,D3DFORMAT format)1984 D3DFB::PackedTexture *D3DFB::AllocPackedTexture(int w, int h, bool wrapping, D3DFORMAT format)
1985 {
1986 Atlas *pack;
1987 Rect box;
1988 bool padded;
1989
1990 // The - 2 to account for padding
1991 if (w > 256 - 2 || h > 256 - 2 || wrapping)
1992 { // Create a new texture atlas.
1993 pack = new Atlas(this, w, h, format);
1994 pack->OneUse = true;
1995 box = pack->Packer.Insert(w, h);
1996 padded = false;
1997 }
1998 else
1999 { // Try to find space in an existing texture atlas.
2000 w += 2; // Add padding
2001 h += 2;
2002 for (pack = Atlases; pack != NULL; pack = pack->Next)
2003 {
2004 // Use the first atlas it fits in.
2005 if (pack->Format == format)
2006 {
2007 box = pack->Packer.Insert(w, h);
2008 if (box.width != 0)
2009 {
2010 break;
2011 }
2012 }
2013 }
2014 if (pack == NULL)
2015 { // Create a new texture atlas.
2016 pack = new Atlas(this, DEF_ATLAS_WIDTH, DEF_ATLAS_HEIGHT, format);
2017 box = pack->Packer.Insert(w, h);
2018 }
2019 padded = true;
2020 }
2021 assert(box.width != 0 && box.height != 0);
2022 return pack->AllocateImage(box, padded);
2023 }
2024
2025 //==========================================================================
2026 //
2027 // Atlas Constructor
2028 //
2029 //==========================================================================
2030
Atlas(D3DFB * fb,int w,int h,D3DFORMAT format)2031 D3DFB::Atlas::Atlas(D3DFB *fb, int w, int h, D3DFORMAT format)
2032 : Packer(w, h, true)
2033 {
2034 Tex = NULL;
2035 Format = format;
2036 UsedList = NULL;
2037 OneUse = false;
2038 Width = 0;
2039 Height = 0;
2040 Next = NULL;
2041
2042 // Attach to the end of the atlas list
2043 Atlas **prev = &fb->Atlases;
2044 while (*prev != NULL)
2045 {
2046 prev = &((*prev)->Next);
2047 }
2048 *prev = this;
2049
2050 #if 1
2051 if (FAILED(fb->D3DDevice->CreateTexture(w, h, 1, 0, format, D3DPOOL_MANAGED, &Tex, NULL)))
2052 #endif
2053 { // Try again, using power-of-2 sizes
2054 int i;
2055
2056 for (i = 1; i < w; i <<= 1) {} w = i;
2057 for (i = 1; i < h; i <<= 1) {} h = i;
2058 if (FAILED(fb->D3DDevice->CreateTexture(w, h, 1, 0, format, D3DPOOL_MANAGED, &Tex, NULL)))
2059 {
2060 return;
2061 }
2062 }
2063 Width = w;
2064 Height = h;
2065 }
2066
2067 //==========================================================================
2068 //
2069 // Atlas Destructor
2070 //
2071 //==========================================================================
2072
~Atlas()2073 D3DFB::Atlas::~Atlas()
2074 {
2075 PackedTexture *box, *next;
2076
2077 SAFE_RELEASE( Tex );
2078 for (box = UsedList; box != NULL; box = next)
2079 {
2080 next = box->Next;
2081 delete box;
2082 }
2083 }
2084
2085 //==========================================================================
2086 //
2087 // Atlas :: AllocateImage
2088 //
2089 // Moves the box from the empty list to the used list, sizing it to the
2090 // requested dimensions and adding additional boxes to the empty list if
2091 // needed.
2092 //
2093 // The passed box *MUST* be in this texture atlas's empty list.
2094 //
2095 //==========================================================================
2096
AllocateImage(const Rect & rect,bool padded)2097 D3DFB::PackedTexture *D3DFB::Atlas::AllocateImage(const Rect &rect, bool padded)
2098 {
2099 PackedTexture *box = new PackedTexture;
2100
2101 box->Owner = this;
2102 box->Area.left = rect.x;
2103 box->Area.top = rect.y;
2104 box->Area.right = rect.x + rect.width;
2105 box->Area.bottom = rect.y + rect.height;
2106
2107 box->Left = float(box->Area.left + padded) / Width;
2108 box->Right = float(box->Area.right - padded) / Width;
2109 box->Top = float(box->Area.top + padded) / Height;
2110 box->Bottom = float(box->Area.bottom - padded) / Height;
2111
2112 box->Padded = padded;
2113
2114 // Add it to the used list.
2115 box->Next = UsedList;
2116 if (box->Next != NULL)
2117 {
2118 box->Next->Prev = &box->Next;
2119 }
2120 UsedList = box;
2121 box->Prev = &UsedList;
2122
2123 return box;
2124 }
2125
2126 //==========================================================================
2127 //
2128 // Atlas :: FreeBox
2129 //
2130 // Removes a box from the used list and deletes it. Space is returned to the
2131 // waste list. Once all boxes for this atlas are freed, the entire bin
2132 // packer is reinitialized for maximum efficiency.
2133 //
2134 //==========================================================================
2135
FreeBox(D3DFB::PackedTexture * box)2136 void D3DFB::Atlas::FreeBox(D3DFB::PackedTexture *box)
2137 {
2138 *(box->Prev) = box->Next;
2139 if (box->Next != NULL)
2140 {
2141 box->Next->Prev = box->Prev;
2142 }
2143 Rect waste;
2144 waste.x = box->Area.left;
2145 waste.y = box->Area.top;
2146 waste.width = box->Area.right - box->Area.left;
2147 waste.height = box->Area.bottom - box->Area.top;
2148 box->Owner->Packer.AddWaste(waste);
2149 delete box;
2150 if (UsedList == NULL)
2151 {
2152 Packer.Init(Width, Height, true);
2153 }
2154 }
2155
2156 //==========================================================================
2157 //
2158 // D3DTex Constructor
2159 //
2160 //==========================================================================
2161
D3DTex(FTexture * tex,D3DFB * fb,bool wrapping)2162 D3DTex::D3DTex(FTexture *tex, D3DFB *fb, bool wrapping)
2163 {
2164 // Attach to the texture list for the D3DFB
2165 Next = fb->Textures;
2166 if (Next != NULL)
2167 {
2168 Next->Prev = &Next;
2169 }
2170 Prev = &fb->Textures;
2171 fb->Textures = this;
2172
2173 GameTex = tex;
2174 Box = NULL;
2175 IsGray = false;
2176
2177 Create(fb, wrapping);
2178 }
2179
2180 //==========================================================================
2181 //
2182 // D3DTex Destructor
2183 //
2184 //==========================================================================
2185
~D3DTex()2186 D3DTex::~D3DTex()
2187 {
2188 if (Box != NULL)
2189 {
2190 Box->Owner->FreeBox(Box);
2191 Box = NULL;
2192 }
2193 // Detach from the texture list
2194 *Prev = Next;
2195 if (Next != NULL)
2196 {
2197 Next->Prev = Prev;
2198 }
2199 // Remove link from the game texture
2200 if (GameTex != NULL)
2201 {
2202 GameTex->Native = NULL;
2203 }
2204 }
2205
2206 //==========================================================================
2207 //
2208 // D3DTex :: CheckWrapping
2209 //
2210 // Returns true if the texture is compatible with the specified wrapping
2211 // mode.
2212 //
2213 //==========================================================================
2214
CheckWrapping(bool wrapping)2215 bool D3DTex::CheckWrapping(bool wrapping)
2216 {
2217 // If it doesn't need to wrap, then it works.
2218 if (!wrapping)
2219 {
2220 return true;
2221 }
2222 // If it needs to wrap, then it can't be packed inside another texture.
2223 return Box->Owner->OneUse;
2224 }
2225
2226 //==========================================================================
2227 //
2228 // D3DTex :: Create
2229 //
2230 // Creates an IDirect3DTexture9 for the texture and copies the image data
2231 // to it. Note that unlike FTexture, this image is row-major.
2232 //
2233 //==========================================================================
2234
Create(D3DFB * fb,bool wrapping)2235 bool D3DTex::Create(D3DFB *fb, bool wrapping)
2236 {
2237 assert(Box == NULL);
2238 if (Box != NULL)
2239 {
2240 Box->Owner->FreeBox(Box);
2241 }
2242
2243 Box = fb->AllocPackedTexture(GameTex->GetWidth(), GameTex->GetHeight(), wrapping, GetTexFormat());
2244
2245 if (Box == NULL)
2246 {
2247 return false;
2248 }
2249 if (!Update())
2250 {
2251 Box->Owner->FreeBox(Box);
2252 Box = NULL;
2253 return false;
2254 }
2255 return true;
2256 }
2257
2258 //==========================================================================
2259 //
2260 // D3DTex :: Update
2261 //
2262 // Copies image data from the underlying FTexture to the D3D texture.
2263 //
2264 //==========================================================================
2265
Update()2266 bool D3DTex::Update()
2267 {
2268 D3DSURFACE_DESC desc;
2269 D3DLOCKED_RECT lrect;
2270 RECT rect;
2271 BYTE *dest;
2272
2273 assert(Box != NULL);
2274 assert(Box->Owner != NULL);
2275 assert(Box->Owner->Tex != NULL);
2276 assert(GameTex != NULL);
2277
2278 if (FAILED(Box->Owner->Tex->GetLevelDesc(0, &desc)))
2279 {
2280 return false;
2281 }
2282 rect = Box->Area;
2283 if (FAILED(Box->Owner->Tex->LockRect(0, &lrect, &rect, 0)))
2284 {
2285 return false;
2286 }
2287 dest = (BYTE *)lrect.pBits;
2288 if (Box->Padded)
2289 {
2290 dest += lrect.Pitch + (desc.Format == D3DFMT_L8 ? 1 : 4);
2291 }
2292 GameTex->FillBuffer(dest, lrect.Pitch, GameTex->GetHeight(), ToTexFmt(desc.Format));
2293 if (Box->Padded)
2294 {
2295 // Clear top padding row.
2296 dest = (BYTE *)lrect.pBits;
2297 int numbytes = GameTex->GetWidth() + 2;
2298 if (desc.Format != D3DFMT_L8)
2299 {
2300 numbytes <<= 2;
2301 }
2302 memset(dest, 0, numbytes);
2303 dest += lrect.Pitch;
2304 // Clear left and right padding columns.
2305 if (desc.Format == D3DFMT_L8)
2306 {
2307 for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y)
2308 {
2309 dest[0] = 0;
2310 dest[numbytes-1] = 0;
2311 dest += lrect.Pitch;
2312 }
2313 }
2314 else
2315 {
2316 for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y)
2317 {
2318 *(DWORD *)dest = 0;
2319 *(DWORD *)(dest + numbytes - 4) = 0;
2320 dest += lrect.Pitch;
2321 }
2322 }
2323 // Clear bottom padding row.
2324 memset(dest, 0, numbytes);
2325 }
2326 Box->Owner->Tex->UnlockRect(0);
2327 return true;
2328 }
2329
2330 //==========================================================================
2331 //
2332 // D3DTex :: GetTexFormat
2333 //
2334 // Returns the texture format that would best fit this texture.
2335 //
2336 //==========================================================================
2337
GetTexFormat()2338 D3DFORMAT D3DTex::GetTexFormat()
2339 {
2340 FTextureFormat fmt = GameTex->GetFormat();
2341
2342 IsGray = false;
2343
2344 switch (fmt)
2345 {
2346 case TEX_Pal: return D3DFMT_L8;
2347 case TEX_Gray: IsGray = true; return D3DFMT_L8;
2348 case TEX_RGB: return D3DFMT_A8R8G8B8;
2349 case TEX_DXT1: return D3DFMT_DXT1;
2350 case TEX_DXT2: return D3DFMT_DXT2;
2351 case TEX_DXT3: return D3DFMT_DXT3;
2352 case TEX_DXT4: return D3DFMT_DXT4;
2353 case TEX_DXT5: return D3DFMT_DXT5;
2354 default: I_FatalError ("GameTex->GetFormat() returned invalid format.");
2355 }
2356 return D3DFMT_L8;
2357 }
2358
2359 //==========================================================================
2360 //
2361 // D3DTex :: ToTexFmt
2362 //
2363 // Converts a D3DFORMAT constant to something the FTexture system
2364 // understands.
2365 //
2366 //==========================================================================
2367
ToTexFmt(D3DFORMAT fmt)2368 FTextureFormat D3DTex::ToTexFmt(D3DFORMAT fmt)
2369 {
2370 switch (fmt)
2371 {
2372 case D3DFMT_L8: return IsGray ? TEX_Gray : TEX_Pal;
2373 case D3DFMT_A8R8G8B8: return TEX_RGB;
2374 case D3DFMT_DXT1: return TEX_DXT1;
2375 case D3DFMT_DXT2: return TEX_DXT2;
2376 case D3DFMT_DXT3: return TEX_DXT3;
2377 case D3DFMT_DXT4: return TEX_DXT4;
2378 case D3DFMT_DXT5: return TEX_DXT5;
2379 default:
2380 assert(0); // LOL WUT?
2381 return TEX_Pal;
2382 }
2383 }
2384
2385 //==========================================================================
2386 //
2387 // D3DPal Constructor
2388 //
2389 //==========================================================================
2390
D3DPal(FRemapTable * remap,D3DFB * fb)2391 D3DPal::D3DPal(FRemapTable *remap, D3DFB *fb)
2392 : Tex(NULL), Remap(remap)
2393 {
2394 int count;
2395
2396 // Attach to the palette list for the D3DFB
2397 Next = fb->Palettes;
2398 if (Next != NULL)
2399 {
2400 Next->Prev = &Next;
2401 }
2402 Prev = &fb->Palettes;
2403 fb->Palettes = this;
2404
2405 // Palette textures must be 256 entries for Shader Model 1.4
2406 if (fb->SM14)
2407 {
2408 count = 256;
2409 // If the palette isn't big enough, then we don't need to
2410 // worry about setting the gamma ramp.
2411 DoColorSkip = (remap->NumEntries >= 256 - 8);
2412 }
2413 else
2414 {
2415 int pow2count;
2416
2417 // Round up to the nearest power of 2.
2418 for (pow2count = 1; pow2count < remap->NumEntries; pow2count <<= 1)
2419 { }
2420 count = pow2count;
2421 DoColorSkip = false;
2422 }
2423 BorderColor = 0;
2424 RoundedPaletteSize = count;
2425 if (SUCCEEDED(fb->D3DDevice->CreateTexture(count, 1, 1, 0,
2426 D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &Tex, NULL)))
2427 {
2428 if (!Update())
2429 {
2430 Tex->Release();
2431 Tex = NULL;
2432 }
2433 }
2434 }
2435
2436 //==========================================================================
2437 //
2438 // D3DPal Destructor
2439 //
2440 //==========================================================================
2441
~D3DPal()2442 D3DPal::~D3DPal()
2443 {
2444 SAFE_RELEASE( Tex );
2445 // Detach from the palette list
2446 *Prev = Next;
2447 if (Next != NULL)
2448 {
2449 Next->Prev = Prev;
2450 }
2451 // Remove link from the remap table
2452 if (Remap != NULL)
2453 {
2454 Remap->Native = NULL;
2455 }
2456 }
2457
2458 //==========================================================================
2459 //
2460 // D3DPal :: Update
2461 //
2462 // Copies the palette to the texture.
2463 //
2464 //==========================================================================
2465
Update()2466 bool D3DPal::Update()
2467 {
2468 D3DLOCKED_RECT lrect;
2469 D3DCOLOR *buff;
2470 const PalEntry *pal;
2471 int skipat, i;
2472
2473 assert(Tex != NULL);
2474
2475 if (FAILED(Tex->LockRect(0, &lrect, NULL, 0)))
2476 {
2477 return false;
2478 }
2479 buff = (D3DCOLOR *)lrect.pBits;
2480 pal = Remap->Palette;
2481
2482 // See explanation in UploadPalette() for skipat rationale.
2483 skipat = MIN(Remap->NumEntries, DoColorSkip ? 256 - 8 : 256);
2484
2485 for (i = 0; i < skipat; ++i)
2486 {
2487 buff[i] = D3DCOLOR_ARGB(pal[i].a, pal[i].r, pal[i].g, pal[i].b);
2488 }
2489 for (++i; i < Remap->NumEntries; ++i)
2490 {
2491 buff[i] = D3DCOLOR_ARGB(pal[i].a, pal[i-1].r, pal[i-1].g, pal[i-1].b);
2492 }
2493 BorderColor = D3DCOLOR_ARGB(pal[i].a, pal[i-1].r, pal[i-1].g, pal[i-1].b);
2494
2495 Tex->UnlockRect(0);
2496 return true;
2497 }
2498
2499 //==========================================================================
2500 //
2501 // D3DFB :: Begin2D
2502 //
2503 // Begins 2D mode drawing operations. In particular, DrawTexture is
2504 // rerouted to use Direct3D instead of the software renderer.
2505 //
2506 //==========================================================================
2507
Begin2D(bool copy3d)2508 bool D3DFB::Begin2D(bool copy3d)
2509 {
2510 if (!Accel2D)
2511 {
2512 return false;
2513 }
2514 if (In2D)
2515 {
2516 return true;
2517 }
2518 In2D = 2 - copy3d;
2519 Update();
2520 In2D = 3;
2521
2522 return true;
2523 }
2524
2525 //==========================================================================
2526 //
2527 // D3DFB :: DrawBlendingRect
2528 //
2529 // Call after Begin2D to blend the 3D view.
2530 //
2531 //==========================================================================
2532
DrawBlendingRect()2533 void D3DFB::DrawBlendingRect()
2534 {
2535 if (!In2D || !Accel2D)
2536 {
2537 return;
2538 }
2539 Dim(FlashColor, FlashAmount / 256.f, viewwindowx, viewwindowy, viewwidth, viewheight);
2540 }
2541
2542 //==========================================================================
2543 //
2544 // D3DFB :: CreateTexture
2545 //
2546 // Returns a native texture that wraps a FTexture.
2547 //
2548 //==========================================================================
2549
CreateTexture(FTexture * gametex,bool wrapping)2550 FNativeTexture *D3DFB::CreateTexture(FTexture *gametex, bool wrapping)
2551 {
2552 D3DTex *tex = new D3DTex(gametex, this, wrapping);
2553 if (tex->Box == NULL)
2554 {
2555 delete tex;
2556 return NULL;
2557 }
2558 return tex;
2559 }
2560
2561 //==========================================================================
2562 //
2563 // D3DFB :: CreatePalette
2564 //
2565 // Returns a native texture that contains a palette.
2566 //
2567 //==========================================================================
2568
CreatePalette(FRemapTable * remap)2569 FNativePalette *D3DFB::CreatePalette(FRemapTable *remap)
2570 {
2571 D3DPal *tex = new D3DPal(remap, this);
2572 if (tex->Tex == NULL)
2573 {
2574 delete tex;
2575 return NULL;
2576 }
2577 return tex;
2578 }
2579
2580 //==========================================================================
2581 //
2582 // D3DFB :: Clear
2583 //
2584 // Fills the specified region with a color.
2585 //
2586 //==========================================================================
2587
Clear(int left,int top,int right,int bottom,int palcolor,uint32 color)2588 void D3DFB::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color)
2589 {
2590 if (In2D < 2)
2591 {
2592 Super::Clear(left, top, right, bottom, palcolor, color);
2593 return;
2594 }
2595 if (!InScene)
2596 {
2597 return;
2598 }
2599 if (palcolor >= 0 && color == 0)
2600 {
2601 color = GPalette.BaseColors[palcolor];
2602 }
2603 else if (APART(color) < 255)
2604 {
2605 Dim(color, APART(color)/255.f, left, top, right - left, bottom - top);
2606 return;
2607 }
2608 AddColorOnlyQuad(left, top, right - left, bottom - top, color | 0xFF000000);
2609 }
2610
2611 //==========================================================================
2612 //
2613 // D3DFB :: Dim
2614 //
2615 //==========================================================================
2616
Dim(PalEntry color,float amount,int x1,int y1,int w,int h)2617 void D3DFB::Dim (PalEntry color, float amount, int x1, int y1, int w, int h)
2618 {
2619 if (amount <= 0)
2620 {
2621 return;
2622 }
2623 if (In2D < 2)
2624 {
2625 Super::Dim(color, amount, x1, y1, w, h);
2626 return;
2627 }
2628 if (!InScene)
2629 {
2630 return;
2631 }
2632 if (amount > 1)
2633 {
2634 amount = 1;
2635 }
2636 AddColorOnlyQuad(x1, y1, w, h, color | (int(amount * 255) << 24));
2637 }
2638
2639 //==========================================================================
2640 //
2641 // D3DFB :: BeginLineBatch
2642 //
2643 //==========================================================================
2644
BeginLineBatch()2645 void D3DFB::BeginLineBatch()
2646 {
2647 if (In2D < 2 || !InScene || BatchType == BATCH_Lines)
2648 {
2649 return;
2650 }
2651 EndQuadBatch(); // Make sure all quads have been drawn first.
2652 VertexBuffer->Lock(0, 0, (void **)&VertexData, D3DLOCK_DISCARD);
2653 VertexPos = 0;
2654 BatchType = BATCH_Lines;
2655 }
2656
2657 //==========================================================================
2658 //
2659 // D3DFB :: EndLineBatch
2660 //
2661 //==========================================================================
2662
EndLineBatch()2663 void D3DFB::EndLineBatch()
2664 {
2665 if (In2D < 2 || !InScene || BatchType != BATCH_Lines)
2666 {
2667 return;
2668 }
2669 VertexBuffer->Unlock();
2670 if (VertexPos > 0)
2671 {
2672 SetPixelShader(Shaders[SHADER_VertexColor]);
2673 SetAlphaBlend(D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA);
2674 D3DDevice->SetStreamSource(0, VertexBuffer, 0, sizeof(FBVERTEX));
2675 D3DDevice->DrawPrimitive(D3DPT_LINELIST, 0, VertexPos / 2);
2676 }
2677 VertexPos = -1;
2678 BatchType = BATCH_None;
2679 }
2680
2681 //==========================================================================
2682 //
2683 // D3DFB :: DrawLine
2684 //
2685 //==========================================================================
2686
DrawLine(int x0,int y0,int x1,int y1,int palcolor,uint32 color)2687 void D3DFB::DrawLine(int x0, int y0, int x1, int y1, int palcolor, uint32 color)
2688 {
2689 if (In2D < 2)
2690 {
2691 Super::DrawLine(x0, y0, x1, y1, palcolor, color);
2692 return;
2693 }
2694 if (!InScene)
2695 {
2696 return;
2697 }
2698 if (BatchType != BATCH_Lines)
2699 {
2700 BeginLineBatch();
2701 }
2702 if (VertexPos == NUM_VERTS)
2703 { // Flush the buffer and refill it.
2704 EndLineBatch();
2705 BeginLineBatch();
2706 }
2707 // Add the endpoints to the vertex buffer.
2708 VertexData[VertexPos].x = float(x0);
2709 VertexData[VertexPos].y = float(y0) + LBOffset;
2710 VertexData[VertexPos].z = 0;
2711 VertexData[VertexPos].rhw = 1;
2712 VertexData[VertexPos].color0 = color;
2713 VertexData[VertexPos].color1 = 0;
2714 VertexData[VertexPos].tu = 0;
2715 VertexData[VertexPos].tv = 0;
2716
2717 VertexData[VertexPos+1].x = float(x1);
2718 VertexData[VertexPos+1].y = float(y1) + LBOffset;
2719 VertexData[VertexPos+1].z = 0;
2720 VertexData[VertexPos+1].rhw = 1;
2721 VertexData[VertexPos+1].color0 = color;
2722 VertexData[VertexPos+1].color1 = 0;
2723 VertexData[VertexPos+1].tu = 0;
2724 VertexData[VertexPos+1].tv = 0;
2725
2726 VertexPos += 2;
2727 }
2728
2729 //==========================================================================
2730 //
2731 // D3DFB :: DrawPixel
2732 //
2733 //==========================================================================
2734
DrawPixel(int x,int y,int palcolor,uint32 color)2735 void D3DFB::DrawPixel(int x, int y, int palcolor, uint32 color)
2736 {
2737 if (In2D < 2)
2738 {
2739 Super::DrawPixel(x, y, palcolor, color);
2740 return;
2741 }
2742 if (!InScene)
2743 {
2744 return;
2745 }
2746 FBVERTEX pt =
2747 {
2748 float(x), float(y), 0, 1, color
2749 };
2750 EndBatch(); // Draw out any batched operations.
2751 SetPixelShader(Shaders[SHADER_VertexColor]);
2752 SetAlphaBlend(D3DBLENDOP_ADD, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA);
2753 D3DDevice->DrawPrimitiveUP(D3DPT_POINTLIST, 1, &pt, sizeof(FBVERTEX));
2754 }
2755
2756 //==========================================================================
2757 //
2758 // D3DFB :: DrawTextureV
2759 //
2760 // If not in 2D mode, just call the normal software version.
2761 // If in 2D mode, then use Direct3D calls to perform the drawing.
2762 //
2763 //==========================================================================
2764
DrawTextureV(FTexture * img,double x,double y,uint32 tags_first,va_list tags)2765 void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, double x, double y, uint32 tags_first, va_list tags)
2766 {
2767 if (In2D < 2)
2768 {
2769 Super::DrawTextureV(img, x, y, tags_first, tags);
2770 return;
2771 }
2772
2773 DrawParms parms;
2774
2775 if (!InScene || !ParseDrawTextureTags(img, x, y, tags_first, tags, &parms, true))
2776 {
2777 return;
2778 }
2779
2780 D3DTex *tex = static_cast<D3DTex *>(img->GetNative(false));
2781
2782 if (tex == NULL)
2783 {
2784 assert(tex != NULL);
2785 return;
2786 }
2787
2788 CheckQuadBatch();
2789
2790 double xscale = parms.destwidth / parms.texwidth;
2791 double yscale = parms.destheight / parms.texheight;
2792 double x0 = parms.x - parms.left * xscale;
2793 double y0 = parms.y - parms.top * yscale;
2794 double x1 = x0 + parms.destwidth;
2795 double y1 = y0 + parms.destheight;
2796 float u0 = tex->Box->Left;
2797 float v0 = tex->Box->Top;
2798 float u1 = tex->Box->Right;
2799 float v1 = tex->Box->Bottom;
2800 double uscale = 1.f / tex->Box->Owner->Width;
2801 bool scissoring = false;
2802 FBVERTEX *vert;
2803 float yoffs;
2804
2805 if (parms.flipX)
2806 {
2807 swapvalues(u0, u1);
2808 }
2809 if (parms.windowleft > 0 || parms.windowright < parms.texwidth)
2810 {
2811 x0 += parms.windowleft * xscale;
2812 u0 = float(u0 + parms.windowleft * uscale);
2813 x1 -= (parms.texwidth - parms.windowright) * xscale;
2814 u1 = float(u1 - (parms.texwidth - parms.windowright) * uscale);
2815 }
2816
2817 #if 0
2818 float vscale = 1.f / tex->Box->Owner->Height / yscale;
2819 if (y0 < parms.uclip)
2820 {
2821 v0 += (float(parms.uclip) - y0) * vscale;
2822 y0 = float(parms.uclip);
2823 }
2824 if (y1 > parms.dclip)
2825 {
2826 v1 -= (y1 - float(parms.dclip)) * vscale;
2827 y1 = float(parms.dclip);
2828 }
2829 if (x0 < parms.lclip)
2830 {
2831 u0 += float(parms.lclip - x0) * uscale / xscale * 2;
2832 x0 = float(parms.lclip);
2833 }
2834 if (x1 > parms.rclip)
2835 {
2836 u1 -= (x1 - parms.rclip) * uscale / xscale * 2;
2837 x1 = float(parms.rclip);
2838 }
2839 #else
2840 // Use a scissor test because the math above introduces some jitter
2841 // that is noticeable at low resolutions. Unfortunately, this means this
2842 // quad has to be in a batch by itself.
2843 if (y0 < parms.uclip || y1 > parms.dclip || x0 < parms.lclip || x1 > parms.rclip)
2844 {
2845 scissoring = true;
2846 if (QuadBatchPos > 0)
2847 {
2848 EndQuadBatch();
2849 BeginQuadBatch();
2850 }
2851 RECT scissor = {
2852 parms.lclip, parms.uclip + LBOffsetI,
2853 parms.rclip, parms.dclip + LBOffsetI
2854 };
2855 D3DDevice->SetScissorRect(&scissor);
2856 D3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
2857 }
2858 #endif
2859 parms.bilinear = false;
2860
2861 D3DCOLOR color0, color1;
2862 BufferedTris *quad = &QuadExtra[QuadBatchPos];
2863
2864 if (!SetStyle(tex, parms, color0, color1, *quad))
2865 {
2866 goto done;
2867 }
2868
2869 quad->Texture = tex->Box->Owner->Tex;
2870 if (parms.bilinear)
2871 {
2872 quad->Flags |= BQF_Bilinear;
2873 }
2874 quad->NumTris = 2;
2875 quad->NumVerts = 4;
2876
2877 yoffs = GatheringWipeScreen ? 0.5f : 0.5f - LBOffset;
2878
2879 #if 0
2880 // Coordinates are truncated to integers, because that's effectively
2881 // what the software renderer does. The hardware will instead round
2882 // to nearest, it seems.
2883 x0 = floorf(x0) - 0.5f;
2884 y0 = floorf(y0) - yoffs;
2885 x1 = floorf(x1) - 0.5f;
2886 y1 = floorf(y1) - yoffs;
2887 #else
2888 x0 = x0 - 0.5f;
2889 y0 = y0 - yoffs;
2890 x1 = x1 - 0.5f;
2891 y1 = y1 - yoffs;
2892 #endif
2893
2894 vert = &VertexData[VertexPos];
2895
2896 // Fill the vertex buffer.
2897 vert[0].x = float(x0);
2898 vert[0].y = float(y0);
2899 vert[0].z = 0;
2900 vert[0].rhw = 1;
2901 vert[0].color0 = color0;
2902 vert[0].color1 = color1;
2903 vert[0].tu = u0;
2904 vert[0].tv = v0;
2905
2906 vert[1].x = float(x1);
2907 vert[1].y = float(y0);
2908 vert[1].z = 0;
2909 vert[1].rhw = 1;
2910 vert[1].color0 = color0;
2911 vert[1].color1 = color1;
2912 vert[1].tu = u1;
2913 vert[1].tv = v0;
2914
2915 vert[2].x = float(x1);
2916 vert[2].y = float(y1);
2917 vert[2].z = 0;
2918 vert[2].rhw = 1;
2919 vert[2].color0 = color0;
2920 vert[2].color1 = color1;
2921 vert[2].tu = u1;
2922 vert[2].tv = v1;
2923
2924 vert[3].x = float(x0);
2925 vert[3].y = float(y1);
2926 vert[3].z = 0;
2927 vert[3].rhw = 1;
2928 vert[3].color0 = color0;
2929 vert[3].color1 = color1;
2930 vert[3].tu = u0;
2931 vert[3].tv = v1;
2932
2933 // Fill the vertex index buffer.
2934 IndexData[IndexPos ] = VertexPos;
2935 IndexData[IndexPos + 1] = VertexPos + 1;
2936 IndexData[IndexPos + 2] = VertexPos + 2;
2937 IndexData[IndexPos + 3] = VertexPos;
2938 IndexData[IndexPos + 4] = VertexPos + 2;
2939 IndexData[IndexPos + 5] = VertexPos + 3;
2940
2941 // Batch the quad.
2942 QuadBatchPos++;
2943 VertexPos += 4;
2944 IndexPos += 6;
2945 done:
2946 if (scissoring)
2947 {
2948 EndQuadBatch();
2949 D3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
2950 }
2951 }
2952
2953 //==========================================================================
2954 //
2955 // D3DFB :: FlatFill
2956 //
2957 // Fills an area with a repeating copy of the texture.
2958 //
2959 //==========================================================================
2960
FlatFill(int left,int top,int right,int bottom,FTexture * src,bool local_origin)2961 void D3DFB::FlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin)
2962 {
2963 if (In2D < 2)
2964 {
2965 Super::FlatFill(left, top, right, bottom, src, local_origin);
2966 return;
2967 }
2968 if (!InScene)
2969 {
2970 return;
2971 }
2972 D3DTex *tex = static_cast<D3DTex *>(src->GetNative(true));
2973 if (tex == NULL)
2974 {
2975 return;
2976 }
2977 float yoffs = GatheringWipeScreen ? 0.5f : 0.5f - LBOffset;
2978 float x0 = float(left);
2979 float y0 = float(top);
2980 float x1 = float(right);
2981 float y1 = float(bottom);
2982 float itw = 1.f / float(src->GetWidth());
2983 float ith = 1.f / float(src->GetHeight());
2984 float xo = local_origin ? x0 : 0;
2985 float yo = local_origin ? y0 : 0;
2986 float u0 = (x0 - xo) * itw;
2987 float v0 = (y0 - yo) * ith;
2988 float u1 = (x1 - xo) * itw;
2989 float v1 = (y1 - yo) * ith;
2990 x0 -= 0.5f;
2991 y0 -= yoffs;
2992 x1 -= 0.5f;
2993 y1 -= yoffs;
2994
2995 CheckQuadBatch();
2996
2997 BufferedTris *quad = &QuadExtra[QuadBatchPos];
2998 FBVERTEX *vert = &VertexData[VertexPos];
2999
3000 quad->Group1 = 0;
3001 if (tex->GetTexFormat() == D3DFMT_L8 && !tex->IsGray)
3002 {
3003 quad->Flags = BQF_WrapUV | BQF_GamePalette; // | BQF_DisableAlphaTest;
3004 quad->ShaderNum = BQS_PalTex;
3005 }
3006 else
3007 {
3008 quad->Flags = BQF_WrapUV; // | BQF_DisableAlphaTest;
3009 quad->ShaderNum = BQS_Plain;
3010 }
3011 quad->Palette = NULL;
3012 quad->Texture = tex->Box->Owner->Tex;
3013 quad->NumVerts = 4;
3014 quad->NumTris = 2;
3015
3016 vert[0].x = x0;
3017 vert[0].y = y0;
3018 vert[0].z = 0;
3019 vert[0].rhw = 1;
3020 vert[0].color0 = 0;
3021 vert[0].color1 = 0xFFFFFFFF;
3022 vert[0].tu = u0;
3023 vert[0].tv = v0;
3024
3025 vert[1].x = x1;
3026 vert[1].y = y0;
3027 vert[1].z = 0;
3028 vert[1].rhw = 1;
3029 vert[1].color0 = 0;
3030 vert[1].color1 = 0xFFFFFFFF;
3031 vert[1].tu = u1;
3032 vert[1].tv = v0;
3033
3034 vert[2].x = x1;
3035 vert[2].y = y1;
3036 vert[2].z = 0;
3037 vert[2].rhw = 1;
3038 vert[2].color0 = 0;
3039 vert[2].color1 = 0xFFFFFFFF;
3040 vert[2].tu = u1;
3041 vert[2].tv = v1;
3042
3043 vert[3].x = x0;
3044 vert[3].y = y1;
3045 vert[3].z = 0;
3046 vert[3].rhw = 1;
3047 vert[3].color0 = 0;
3048 vert[3].color1 = 0xFFFFFFFF;
3049 vert[3].tu = u0;
3050 vert[3].tv = v1;
3051
3052 IndexData[IndexPos ] = VertexPos;
3053 IndexData[IndexPos + 1] = VertexPos + 1;
3054 IndexData[IndexPos + 2] = VertexPos + 2;
3055 IndexData[IndexPos + 3] = VertexPos;
3056 IndexData[IndexPos + 4] = VertexPos + 2;
3057 IndexData[IndexPos + 5] = VertexPos + 3;
3058
3059 QuadBatchPos++;
3060 VertexPos += 4;
3061 IndexPos += 6;
3062 }
3063
3064 //==========================================================================
3065 //
3066 // D3DFB :: FillSimplePoly
3067 //
3068 // Here, "simple" means that a simple triangle fan can draw it.
3069 //
3070 //==========================================================================
3071
FillSimplePoly(FTexture * texture,FVector2 * points,int npoints,double originx,double originy,double scalex,double scaley,angle_t rotation,FDynamicColormap * colormap,int lightlevel)3072 void D3DFB::FillSimplePoly(FTexture *texture, FVector2 *points, int npoints,
3073 double originx, double originy, double scalex, double scaley,
3074 angle_t rotation, FDynamicColormap *colormap, int lightlevel)
3075 {
3076 // Use an equation similar to player sprites to determine shade
3077 fixed_t shade = LIGHT2SHADE(lightlevel) - 12*FRACUNIT;
3078 BufferedTris *quad;
3079 FBVERTEX *verts;
3080 D3DTex *tex;
3081 float yoffs, uscale, vscale;
3082 int i, ipos;
3083 D3DCOLOR color0, color1;
3084 float ox, oy;
3085 float cosrot, sinrot;
3086 float rot = float(rotation * M_PI / float(1u << 31));
3087 bool dorotate = rot != 0;
3088
3089 if (npoints < 3)
3090 { // This is no polygon.
3091 return;
3092 }
3093 if (In2D < 2)
3094 {
3095 Super::FillSimplePoly(texture, points, npoints, originx, originy, scalex, scaley, rotation, colormap, lightlevel);
3096 return;
3097 }
3098 if (!InScene)
3099 {
3100 return;
3101 }
3102 tex = static_cast<D3DTex *>(texture->GetNative(true));
3103 if (tex == NULL)
3104 {
3105 return;
3106 }
3107
3108 cosrot = cos(rot);
3109 sinrot = sin(rot);
3110
3111 CheckQuadBatch(npoints - 2, npoints);
3112 quad = &QuadExtra[QuadBatchPos];
3113 verts = &VertexData[VertexPos];
3114
3115 color0 = 0;
3116 color1 = 0xFFFFFFFF;
3117
3118 quad->Group1 = 0;
3119 if (tex->GetTexFormat() == D3DFMT_L8 && !tex->IsGray)
3120 {
3121 quad->Flags = BQF_WrapUV | BQF_GamePalette | BQF_DisableAlphaTest;
3122 quad->ShaderNum = BQS_PalTex;
3123 if (colormap != NULL)
3124 {
3125 if (colormap->Desaturate != 0)
3126 {
3127 quad->Flags |= BQF_Desaturated;
3128 }
3129 quad->ShaderNum = BQS_InGameColormap;
3130 quad->Desat = colormap->Desaturate;
3131 color0 = D3DCOLOR_ARGB(255, colormap->Color.r, colormap->Color.g, colormap->Color.b);
3132 double fadelevel = clamp(shade / (NUMCOLORMAPS * 65536.0), 0.0, 1.0);
3133 color1 = D3DCOLOR_ARGB(DWORD((1 - fadelevel) * 255),
3134 DWORD(colormap->Fade.r * fadelevel),
3135 DWORD(colormap->Fade.g * fadelevel),
3136 DWORD(colormap->Fade.b * fadelevel));
3137 }
3138 }
3139 else
3140 {
3141 quad->Flags = BQF_WrapUV | BQF_DisableAlphaTest;
3142 quad->ShaderNum = BQS_Plain;
3143 }
3144 quad->Palette = NULL;
3145 quad->Texture = tex->Box->Owner->Tex;
3146 quad->NumVerts = npoints;
3147 quad->NumTris = npoints - 2;
3148
3149 yoffs = GatheringWipeScreen ? 0 : LBOffset;
3150 uscale = float(1.f / (texture->GetScaledWidth() * scalex));
3151 vscale = float(1.f / (texture->GetScaledHeight() * scaley));
3152 ox = float(originx);
3153 oy = float(originy);
3154
3155 for (i = 0; i < npoints; ++i)
3156 {
3157 verts[i].x = points[i].X;
3158 verts[i].y = points[i].Y + yoffs;
3159 verts[i].z = 0;
3160 verts[i].rhw = 1;
3161 verts[i].color0 = color0;
3162 verts[i].color1 = color1;
3163 float u = points[i].X - 0.5f - ox;
3164 float v = points[i].Y - 0.5f - oy;
3165 if (dorotate)
3166 {
3167 float t = u;
3168 u = t * cosrot - v * sinrot;
3169 v = v * cosrot + t * sinrot;
3170 }
3171 verts[i].tu = u * uscale;
3172 verts[i].tv = v * vscale;
3173 }
3174 for (ipos = IndexPos, i = 2; i < npoints; ++i, ipos += 3)
3175 {
3176 IndexData[ipos ] = VertexPos;
3177 IndexData[ipos + 1] = VertexPos + i - 1;
3178 IndexData[ipos + 2] = VertexPos + i;
3179 }
3180
3181 QuadBatchPos++;
3182 VertexPos += npoints;
3183 IndexPos = ipos;
3184 }
3185
3186 //==========================================================================
3187 //
3188 // D3DFB :: AddColorOnlyQuad
3189 //
3190 // Adds a single-color, untextured quad to the batch.
3191 //
3192 //==========================================================================
3193
AddColorOnlyQuad(int left,int top,int width,int height,D3DCOLOR color)3194 void D3DFB::AddColorOnlyQuad(int left, int top, int width, int height, D3DCOLOR color)
3195 {
3196 BufferedTris *quad;
3197 FBVERTEX *verts;
3198
3199 CheckQuadBatch();
3200 quad = &QuadExtra[QuadBatchPos];
3201 verts = &VertexData[VertexPos];
3202
3203 float x = float(left) - 0.5f;
3204 float y = float(top) - 0.5f + (GatheringWipeScreen ? 0 : LBOffset);
3205
3206 quad->Group1 = 0;
3207 quad->ShaderNum = BQS_ColorOnly;
3208 if ((color & 0xFF000000) != 0xFF000000)
3209 {
3210 quad->BlendOp = D3DBLENDOP_ADD;
3211 quad->SrcBlend = D3DBLEND_SRCALPHA;
3212 quad->DestBlend = D3DBLEND_INVSRCALPHA;
3213 }
3214 quad->Palette = NULL;
3215 quad->Texture = NULL;
3216 quad->NumVerts = 4;
3217 quad->NumTris = 2;
3218
3219 verts[0].x = x;
3220 verts[0].y = y;
3221 verts[0].z = 0;
3222 verts[0].rhw = 1;
3223 verts[0].color0 = color;
3224 verts[0].color1 = 0;
3225 verts[0].tu = 0;
3226 verts[0].tv = 0;
3227
3228 verts[1].x = x + width;
3229 verts[1].y = y;
3230 verts[1].z = 0;
3231 verts[1].rhw = 1;
3232 verts[1].color0 = color;
3233 verts[1].color1 = 0;
3234 verts[1].tu = 0;
3235 verts[1].tv = 0;
3236
3237 verts[2].x = x + width;
3238 verts[2].y = y + height;
3239 verts[2].z = 0;
3240 verts[2].rhw = 1;
3241 verts[2].color0 = color;
3242 verts[2].color1 = 0;
3243 verts[2].tu = 0;
3244 verts[2].tv = 0;
3245
3246 verts[3].x = x;
3247 verts[3].y = y + height;
3248 verts[3].z = 0;
3249 verts[3].rhw = 1;
3250 verts[3].color0 = color;
3251 verts[3].color1 = 0;
3252 verts[3].tu = 0;
3253 verts[3].tv = 0;
3254
3255 IndexData[IndexPos ] = VertexPos;
3256 IndexData[IndexPos + 1] = VertexPos + 1;
3257 IndexData[IndexPos + 2] = VertexPos + 2;
3258 IndexData[IndexPos + 3] = VertexPos;
3259 IndexData[IndexPos + 4] = VertexPos + 2;
3260 IndexData[IndexPos + 5] = VertexPos + 3;
3261
3262 QuadBatchPos++;
3263 VertexPos += 4;
3264 IndexPos += 6;
3265 }
3266
3267 //==========================================================================
3268 //
3269 // D3DFB :: AddColorOnlyRect
3270 //
3271 // Like AddColorOnlyQuad, except it's hollow.
3272 //
3273 //==========================================================================
3274
AddColorOnlyRect(int left,int top,int width,int height,D3DCOLOR color)3275 void D3DFB::AddColorOnlyRect(int left, int top, int width, int height, D3DCOLOR color)
3276 {
3277 AddColorOnlyQuad(left, top, width - 1, 1, color); // top
3278 AddColorOnlyQuad(left + width - 1, top, 1, height - 1, color); // right
3279 AddColorOnlyQuad(left + 1, top + height - 1, width - 1, 1, color); // bottom
3280 AddColorOnlyQuad(left, top + 1, 1, height - 1, color); // left
3281 }
3282
3283 //==========================================================================
3284 //
3285 // D3DFB :: CheckQuadBatch
3286 //
3287 // Make sure there's enough room in the batch for one more set of triangles.
3288 //
3289 //==========================================================================
3290
CheckQuadBatch(int numtris,int numverts)3291 void D3DFB::CheckQuadBatch(int numtris, int numverts)
3292 {
3293 if (BatchType == BATCH_Lines)
3294 {
3295 EndLineBatch();
3296 }
3297 else if (QuadBatchPos == MAX_QUAD_BATCH ||
3298 VertexPos + numverts > NUM_VERTS ||
3299 IndexPos + numtris * 3 > NUM_INDEXES)
3300 {
3301 EndQuadBatch();
3302 }
3303 if (QuadBatchPos < 0)
3304 {
3305 BeginQuadBatch();
3306 }
3307 }
3308
3309 //==========================================================================
3310 //
3311 // D3DFB :: BeginQuadBatch
3312 //
3313 // Locks the vertex buffer for quads and sets the cursor to 0.
3314 //
3315 //==========================================================================
3316
BeginQuadBatch()3317 void D3DFB::BeginQuadBatch()
3318 {
3319 if (In2D < 2 || !InScene || QuadBatchPos >= 0)
3320 {
3321 return;
3322 }
3323 EndLineBatch(); // Make sure all lines have been drawn first.
3324 VertexBuffer->Lock(0, 0, (void **)&VertexData, D3DLOCK_DISCARD);
3325 IndexBuffer->Lock(0, 0, (void **)&IndexData, D3DLOCK_DISCARD);
3326 VertexPos = 0;
3327 IndexPos = 0;
3328 QuadBatchPos = 0;
3329 BatchType = BATCH_Quads;
3330 }
3331
3332 //==========================================================================
3333 //
3334 // D3DFB :: EndQuadBatch
3335 //
3336 // Draws all the quads that have been batched up.
3337 //
3338 //==========================================================================
3339
EndQuadBatch()3340 void D3DFB::EndQuadBatch()
3341 {
3342 if (In2D < 2 || !InScene || BatchType != BATCH_Quads)
3343 {
3344 return;
3345 }
3346 BatchType = BATCH_None;
3347 VertexBuffer->Unlock();
3348 IndexBuffer->Unlock();
3349 if (QuadBatchPos == 0)
3350 {
3351 QuadBatchPos = -1;
3352 VertexPos = -1;
3353 IndexPos = -1;
3354 return;
3355 }
3356 D3DDevice->SetStreamSource(0, VertexBuffer, 0, sizeof(FBVERTEX));
3357 D3DDevice->SetIndices(IndexBuffer);
3358 bool uv_wrapped = false;
3359 bool uv_should_wrap;
3360 int indexpos, vertpos;
3361
3362 indexpos = vertpos = 0;
3363 for (int i = 0; i < QuadBatchPos; )
3364 {
3365 const BufferedTris *quad = &QuadExtra[i];
3366 int j;
3367
3368 int startindex = indexpos;
3369 int startvertex = vertpos;
3370
3371 indexpos += quad->NumTris * 3;
3372 vertpos += quad->NumVerts;
3373
3374 // Quads with matching parameters should be done with a single
3375 // DrawPrimitive call.
3376 for (j = i + 1; j < QuadBatchPos; ++j)
3377 {
3378 const BufferedTris *q2 = &QuadExtra[j];
3379 if (quad->Texture != q2->Texture ||
3380 quad->Group1 != q2->Group1 ||
3381 quad->Palette != q2->Palette)
3382 {
3383 break;
3384 }
3385 if (quad->ShaderNum == BQS_InGameColormap && (quad->Flags & BQF_Desaturated) && quad->Desat != q2->Desat)
3386 {
3387 break;
3388 }
3389 indexpos += q2->NumTris * 3;
3390 vertpos += q2->NumVerts;
3391 }
3392
3393 // Set the palette (if one)
3394 if ((quad->Flags & BQF_Paletted) == BQF_GamePalette)
3395 {
3396 SetPaletteTexture(PaletteTexture, 256, BorderColor);
3397 }
3398 else if ((quad->Flags & BQF_Paletted) == BQF_CustomPalette)
3399 {
3400 assert(quad->Palette != NULL);
3401 SetPaletteTexture(quad->Palette->Tex, quad->Palette->RoundedPaletteSize, quad->Palette->BorderColor);
3402 }
3403 #if 0
3404 // Set paletted bilinear filtering (IF IT WORKED RIGHT!)
3405 if ((quad->Flags & (BQF_Paletted | BQF_Bilinear)) == (BQF_Paletted | BQF_Bilinear))
3406 {
3407 SetPalTexBilinearConstants(quad->Texture);
3408 }
3409 #endif
3410
3411 // Set the alpha blending
3412 SetAlphaBlend(D3DBLENDOP(quad->BlendOp), D3DBLEND(quad->SrcBlend), D3DBLEND(quad->DestBlend));
3413
3414 // Set the alpha test
3415 EnableAlphaTest(!(quad->Flags & BQF_DisableAlphaTest));
3416
3417 // Set the pixel shader
3418 if (quad->ShaderNum == BQS_PalTex)
3419 {
3420 SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ?
3421 SHADER_NormalColorPalInv : SHADER_NormalColorPal]);
3422 }
3423 else if (quad->ShaderNum == BQS_Plain)
3424 {
3425 SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ?
3426 SHADER_NormalColorInv : SHADER_NormalColor]);
3427 }
3428 else if (quad->ShaderNum == BQS_RedToAlpha)
3429 {
3430 SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ?
3431 SHADER_RedToAlphaInv : SHADER_RedToAlpha]);
3432 }
3433 else if (quad->ShaderNum == BQS_ColorOnly)
3434 {
3435 SetPixelShader(Shaders[SHADER_VertexColor]);
3436 }
3437 else if (quad->ShaderNum == BQS_SpecialColormap)
3438 {
3439 int select;
3440
3441 select = !!(quad->Flags & BQF_Paletted);
3442 SetPixelShader(Shaders[SHADER_SpecialColormap + select]);
3443 }
3444 else if (quad->ShaderNum == BQS_InGameColormap)
3445 {
3446 int select;
3447
3448 select = !!(quad->Flags & BQF_Desaturated);
3449 select |= !!(quad->Flags & BQF_InvertSource) << 1;
3450 select |= !!(quad->Flags & BQF_Paletted) << 2;
3451 if (quad->Flags & BQF_Desaturated)
3452 {
3453 SetConstant(PSCONST_Desaturation, quad->Desat / 255.f, (255 - quad->Desat) / 255.f, 0, 0);
3454 }
3455 SetPixelShader(Shaders[SHADER_InGameColormap + select]);
3456 }
3457
3458 // Set the texture clamp addressing mode
3459 uv_should_wrap = !!(quad->Flags & BQF_WrapUV);
3460 if (uv_wrapped != uv_should_wrap)
3461 {
3462 DWORD mode = uv_should_wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_BORDER;
3463 uv_wrapped = uv_should_wrap;
3464 D3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, mode);
3465 D3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, mode);
3466 }
3467
3468 // Set the texture
3469 if (quad->Texture != NULL)
3470 {
3471 SetTexture(0, quad->Texture);
3472 }
3473
3474 // Draw the quad
3475 D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0,
3476 startvertex, // MinIndex
3477 vertpos - startvertex, // NumVertices
3478 startindex, // StartIndex
3479 (indexpos - startindex) / 3 // PrimitiveCount
3480 /*4 * i, 4 * (j - i), 6 * i, 2 * (j - i)*/);
3481 i = j;
3482 }
3483 if (uv_wrapped)
3484 {
3485 D3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
3486 D3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
3487 }
3488 QuadBatchPos = -1;
3489 VertexPos = -1;
3490 IndexPos = -1;
3491 }
3492
3493 //==========================================================================
3494 //
3495 // D3DFB :: EndBatch
3496 //
3497 // Draws whichever type of primitive is currently being batched.
3498 //
3499 //==========================================================================
3500
EndBatch()3501 void D3DFB::EndBatch()
3502 {
3503 if (BatchType == BATCH_Quads)
3504 {
3505 EndQuadBatch();
3506 }
3507 else if (BatchType == BATCH_Lines)
3508 {
3509 EndLineBatch();
3510 }
3511 }
3512
3513 //==========================================================================
3514 //
3515 // D3DFB :: SetStyle
3516 //
3517 // Patterned after R_SetPatchStyle.
3518 //
3519 //==========================================================================
3520
SetStyle(D3DTex * tex,DrawParms & parms,D3DCOLOR & color0,D3DCOLOR & color1,BufferedTris & quad)3521 bool D3DFB::SetStyle(D3DTex *tex, DrawParms &parms, D3DCOLOR &color0, D3DCOLOR &color1, BufferedTris &quad)
3522 {
3523 D3DFORMAT fmt = tex->GetTexFormat();
3524 FRenderStyle style = parms.style;
3525 float alpha;
3526 bool stencilling;
3527
3528 if (style.Flags & STYLEF_TransSoulsAlpha)
3529 {
3530 alpha = transsouls;
3531 }
3532 else if (style.Flags & STYLEF_Alpha1)
3533 {
3534 alpha = 1;
3535 }
3536 else
3537 {
3538 alpha = clamp<fixed_t> (parms.alpha, 0, FRACUNIT) / 65536.f;
3539 }
3540
3541 style.CheckFuzz();
3542 if (style.BlendOp == STYLEOP_Shadow)
3543 {
3544 style = LegacyRenderStyles[STYLE_TranslucentStencil];
3545 alpha = 0.3f;
3546 parms.fillcolor = 0;
3547 }
3548
3549 // FIXME: Fuzz effect is not written
3550 if (style.BlendOp == STYLEOP_FuzzOrAdd || style.BlendOp == STYLEOP_Fuzz)
3551 {
3552 style.BlendOp = STYLEOP_Add;
3553 }
3554 else if (style.BlendOp == STYLEOP_FuzzOrSub)
3555 {
3556 style.BlendOp = STYLEOP_Sub;
3557 }
3558 else if (style.BlendOp == STYLEOP_FuzzOrRevSub)
3559 {
3560 style.BlendOp = STYLEOP_RevSub;
3561 }
3562
3563 stencilling = false;
3564 quad.Palette = NULL;
3565 quad.Flags = 0;
3566 quad.Desat = 0;
3567
3568 switch (style.BlendOp)
3569 {
3570 default:
3571 case STYLEOP_Add: quad.BlendOp = D3DBLENDOP_ADD; break;
3572 case STYLEOP_Sub: quad.BlendOp = D3DBLENDOP_SUBTRACT; break;
3573 case STYLEOP_RevSub: quad.BlendOp = D3DBLENDOP_REVSUBTRACT; break;
3574 case STYLEOP_None: return false;
3575 }
3576 quad.SrcBlend = GetStyleAlpha(style.SrcAlpha);
3577 quad.DestBlend = GetStyleAlpha(style.DestAlpha);
3578
3579 if (style.Flags & STYLEF_InvertOverlay)
3580 {
3581 // Only the overlay color is inverted, not the overlay alpha.
3582 parms.colorOverlay = D3DCOLOR_ARGB(APART(parms.colorOverlay),
3583 255 - RPART(parms.colorOverlay), 255 - GPART(parms.colorOverlay),
3584 255 - BPART(parms.colorOverlay));
3585 }
3586
3587 SetColorOverlay(parms.colorOverlay, alpha, color0, color1);
3588
3589 if (style.Flags & STYLEF_ColorIsFixed)
3590 {
3591 if (style.Flags & STYLEF_InvertSource)
3592 { // Since the source color is a constant, we can invert it now
3593 // without spending time doing it in the shader.
3594 parms.fillcolor = D3DCOLOR_XRGB(255 - RPART(parms.fillcolor),
3595 255 - GPART(parms.fillcolor), 255 - BPART(parms.fillcolor));
3596 }
3597 // Set up the color mod to replace the color from the image data.
3598 color0 = (color0 & D3DCOLOR_RGBA(0,0,0,255)) | (parms.fillcolor & D3DCOLOR_RGBA(255,255,255,0));
3599 color1 &= D3DCOLOR_RGBA(0,0,0,255);
3600
3601 if (style.Flags & STYLEF_RedIsAlpha)
3602 {
3603 // Note that if the source texture is paletted, the palette is ignored.
3604 quad.Flags = 0;
3605 quad.ShaderNum = BQS_RedToAlpha;
3606 }
3607 else if (fmt == D3DFMT_L8)
3608 {
3609 quad.Flags = BQF_GamePalette;
3610 quad.ShaderNum = BQS_PalTex;
3611 }
3612 else
3613 {
3614 quad.Flags = 0;
3615 quad.ShaderNum = BQS_Plain;
3616 }
3617 }
3618 else
3619 {
3620 if (style.Flags & STYLEF_RedIsAlpha)
3621 {
3622 quad.Flags = 0;
3623 quad.ShaderNum = BQS_RedToAlpha;
3624 }
3625 else if (fmt == D3DFMT_L8)
3626 {
3627 if (parms.remap != NULL)
3628 {
3629 quad.Flags = BQF_CustomPalette;
3630 quad.Palette = reinterpret_cast<D3DPal *>(parms.remap->GetNative());
3631 quad.ShaderNum = BQS_PalTex;
3632 }
3633 else if (tex->IsGray)
3634 {
3635 quad.Flags = 0;
3636 quad.ShaderNum = BQS_Plain;
3637 }
3638 else
3639 {
3640 quad.Flags = BQF_GamePalette;
3641 quad.ShaderNum = BQS_PalTex;
3642 }
3643 }
3644 else
3645 {
3646 quad.Flags = 0;
3647 quad.ShaderNum = BQS_Plain;
3648 }
3649 if (style.Flags & STYLEF_InvertSource)
3650 {
3651 quad.Flags |= BQF_InvertSource;
3652 }
3653
3654 if (parms.specialcolormap != NULL)
3655 { // Emulate an invulnerability or similar colormap.
3656 float *start, *end;
3657 start = parms.specialcolormap->ColorizeStart;
3658 end = parms.specialcolormap->ColorizeEnd;
3659 if (quad.Flags & BQF_InvertSource)
3660 {
3661 quad.Flags &= ~BQF_InvertSource;
3662 swapvalues(start, end);
3663 }
3664 quad.ShaderNum = BQS_SpecialColormap;
3665 color0 = D3DCOLOR_RGBA(DWORD(start[0]/2*255), DWORD(start[1]/2*255), DWORD(start[2]/2*255), color0 >> 24);
3666 color1 = D3DCOLOR_RGBA(DWORD(end[0]/2*255), DWORD(end[1]/2*255), DWORD(end[2]/2*255), color1 >> 24);
3667 }
3668 else if (parms.colormapstyle != NULL)
3669 { // Emulate the fading from an in-game colormap (colorized, faded, and desaturated)
3670 if (parms.colormapstyle->Desaturate != 0)
3671 {
3672 quad.Flags |= BQF_Desaturated;
3673 }
3674 quad.ShaderNum = BQS_InGameColormap;
3675 quad.Desat = parms.colormapstyle->Desaturate;
3676 color0 = D3DCOLOR_ARGB(color1 >> 24,
3677 parms.colormapstyle->Color.r,
3678 parms.colormapstyle->Color.g,
3679 parms.colormapstyle->Color.b);
3680 double fadelevel = parms.colormapstyle->FadeLevel;
3681 color1 = D3DCOLOR_ARGB(DWORD((1 - fadelevel) * 255),
3682 DWORD(parms.colormapstyle->Fade.r * fadelevel),
3683 DWORD(parms.colormapstyle->Fade.g * fadelevel),
3684 DWORD(parms.colormapstyle->Fade.b * fadelevel));
3685 }
3686 }
3687
3688 // For unmasked images, force the alpha from the image data to be ignored.
3689 if (!parms.masked && quad.ShaderNum != BQS_InGameColormap)
3690 {
3691 color0 = (color0 & D3DCOLOR_RGBA(255, 255, 255, 0)) | D3DCOLOR_COLORVALUE(0, 0, 0, alpha);
3692 color1 &= D3DCOLOR_RGBA(255, 255, 255, 0);
3693
3694 // If our alpha is one and we are doing normal adding, then we can turn the blend off completely.
3695 if (quad.BlendOp == D3DBLENDOP_ADD &&
3696 ((alpha == 1 && quad.SrcBlend == D3DBLEND_SRCALPHA) || quad.SrcBlend == D3DBLEND_ONE) &&
3697 ((alpha == 1 && quad.DestBlend == D3DBLEND_INVSRCALPHA) || quad.DestBlend == D3DBLEND_ZERO))
3698 {
3699 quad.BlendOp = D3DBLENDOP(0);
3700 }
3701 quad.Flags |= BQF_DisableAlphaTest;
3702 }
3703 return true;
3704 }
3705
GetStyleAlpha(int type)3706 D3DBLEND D3DFB::GetStyleAlpha(int type)
3707 {
3708 switch (type)
3709 {
3710 case STYLEALPHA_Zero: return D3DBLEND_ZERO;
3711 case STYLEALPHA_One: return D3DBLEND_ONE;
3712 case STYLEALPHA_Src: return D3DBLEND_SRCALPHA;
3713 case STYLEALPHA_InvSrc: return D3DBLEND_INVSRCALPHA;
3714 default: return D3DBLEND_ZERO;
3715 }
3716 }
3717
3718
SetColorOverlay(DWORD color,float alpha,D3DCOLOR & color0,D3DCOLOR & color1)3719 void D3DFB::SetColorOverlay(DWORD color, float alpha, D3DCOLOR &color0, D3DCOLOR &color1)
3720 {
3721 if (APART(color) != 0)
3722 {
3723 int a = APART(color) * 256 / 255;
3724 color0 = D3DCOLOR_RGBA(
3725 (RPART(color) * a) >> 8,
3726 (GPART(color) * a) >> 8,
3727 (BPART(color) * a) >> 8,
3728 0);
3729 a = 256 - a;
3730 color1 = D3DCOLOR_RGBA(a, a, a, int(alpha * 255));
3731 }
3732 else
3733 {
3734 color0 = 0;
3735 color1 = D3DCOLOR_COLORVALUE(1, 1, 1, alpha);
3736 }
3737 }
3738
EnableAlphaTest(BOOL enabled)3739 void D3DFB::EnableAlphaTest(BOOL enabled)
3740 {
3741 if (enabled != AlphaTestEnabled)
3742 {
3743 AlphaTestEnabled = enabled;
3744 D3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, enabled);
3745 }
3746 }
3747
SetAlphaBlend(D3DBLENDOP op,D3DBLEND srcblend,D3DBLEND destblend)3748 void D3DFB::SetAlphaBlend(D3DBLENDOP op, D3DBLEND srcblend, D3DBLEND destblend)
3749 {
3750 if (op == 0)
3751 { // Disable alpha blend
3752 if (AlphaBlendEnabled)
3753 {
3754 AlphaBlendEnabled = FALSE;
3755 D3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
3756 }
3757 }
3758 else
3759 { // Enable alpha blend
3760 assert(srcblend != 0);
3761 assert(destblend != 0);
3762
3763 if (!AlphaBlendEnabled)
3764 {
3765 AlphaBlendEnabled = TRUE;
3766 D3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
3767 }
3768 if (AlphaBlendOp != op)
3769 {
3770 AlphaBlendOp = op;
3771 D3DDevice->SetRenderState(D3DRS_BLENDOP, op);
3772 }
3773 if (AlphaSrcBlend != srcblend)
3774 {
3775 AlphaSrcBlend = srcblend;
3776 D3DDevice->SetRenderState(D3DRS_SRCBLEND, srcblend);
3777 }
3778 if (AlphaDestBlend != destblend)
3779 {
3780 AlphaDestBlend = destblend;
3781 D3DDevice->SetRenderState(D3DRS_DESTBLEND, destblend);
3782 }
3783 }
3784 }
3785
SetConstant(int cnum,float r,float g,float b,float a)3786 void D3DFB::SetConstant(int cnum, float r, float g, float b, float a)
3787 {
3788 if (Constant[cnum][0] != r ||
3789 Constant[cnum][1] != g ||
3790 Constant[cnum][2] != b ||
3791 Constant[cnum][3] != a)
3792 {
3793 Constant[cnum][0] = r;
3794 Constant[cnum][1] = g;
3795 Constant[cnum][2] = b;
3796 Constant[cnum][3] = a;
3797 D3DDevice->SetPixelShaderConstantF(cnum, Constant[cnum], 1);
3798 }
3799 }
3800
SetPixelShader(IDirect3DPixelShader9 * shader)3801 void D3DFB::SetPixelShader(IDirect3DPixelShader9 *shader)
3802 {
3803 if (CurPixelShader != shader)
3804 {
3805 CurPixelShader = shader;
3806 D3DDevice->SetPixelShader(shader);
3807 }
3808 }
3809
SetTexture(int tnum,IDirect3DTexture9 * texture)3810 void D3DFB::SetTexture(int tnum, IDirect3DTexture9 *texture)
3811 {
3812 assert(unsigned(tnum) < countof(Texture));
3813 if (Texture[tnum] != texture)
3814 {
3815 Texture[tnum] = texture;
3816 D3DDevice->SetTexture(tnum, texture);
3817 }
3818 }
3819
SetPaletteTexture(IDirect3DTexture9 * texture,int count,D3DCOLOR border_color)3820 void D3DFB::SetPaletteTexture(IDirect3DTexture9 *texture, int count, D3DCOLOR border_color)
3821 {
3822 if (SM14)
3823 {
3824 // Shader Model 1.4 only uses 256-color palettes.
3825 SetConstant(PSCONST_PaletteMod, 1.f, 0.5f / 256.f, 0, 0);
3826 if (border_color != 0 && CurBorderColor != border_color)
3827 {
3828 CurBorderColor = border_color;
3829 D3DDevice->SetSamplerState(1, D3DSAMP_BORDERCOLOR, border_color);
3830 }
3831 }
3832 else
3833 {
3834 // The pixel shader receives color indexes in the range [0.0,1.0].
3835 // The palette texture is also addressed in the range [0.0,1.0],
3836 // HOWEVER the coordinate 1.0 is the right edge of the texture and
3837 // not actually the texture itself. We need to scale and shift
3838 // the palette indexes so they lie exactly in the center of each
3839 // texel. For a normal palette with 256 entries, that means the
3840 // range we use should be [0.5,255.5], adjusted so the coordinate
3841 // is still within [0.0,1.0].
3842 //
3843 // The constant register c2 is used to hold the multiplier in the
3844 // x part and the adder in the y part.
3845 float fcount = 1 / float(count);
3846 SetConstant(PSCONST_PaletteMod, 255 * fcount, 0.5f * fcount, 0, 0);
3847 }
3848 SetTexture(1, texture);
3849 }
3850
SetPalTexBilinearConstants(Atlas * tex)3851 void D3DFB::SetPalTexBilinearConstants(Atlas *tex)
3852 {
3853 #if 0
3854 float con[8];
3855
3856 // Don't bother doing anything if the constants won't be used.
3857 if (PalTexShader == PalTexBilinearShader)
3858 {
3859 return;
3860 }
3861
3862 con[0] = float(tex->Width);
3863 con[1] = float(tex->Height);
3864 con[2] = 0;
3865 con[3] = 1 / con[0];
3866 con[4] = 0;
3867 con[5] = 1 / con[1];
3868 con[6] = con[5];
3869 con[7] = con[3];
3870
3871 D3DDevice->SetPixelShaderConstantF(3, con, 2);
3872 #endif
3873 }
3874