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