Main change: Implemented EFB pokes in DX9/DX11.
[dolphin.git] / Source / Plugins / Plugin_VideoDX9 / Src / D3DUtil.cpp
blob85234e5681ead4e04cbe9cbf4d5a5760ecd532d7
1 // Copyright (C) 2003 Dolphin Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
18 #include "Common.h"
19 #include "StringUtil.h"
21 #include "D3DBase.h"
22 #include "D3DUtil.h"
23 #include "Render.h"
24 #include "PixelShaderCache.h"
25 #include "VertexShaderCache.h"
27 namespace D3D
29 CD3DFont font;
31 #define MAX_NUM_VERTICES 50*6
32 struct FONT2DVERTEX {
33 float x,y,z;
34 float rhw;
35 u32 color;
36 float tu, tv;
39 #define D3DFVF_FONT2DVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
40 #define D3DFVF_FONT3DVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_NORMAL|D3DFVF_TEX1)
42 inline FONT2DVERTEX InitFont2DVertex(float x, float y, u32 color, float tu, float tv)
44 FONT2DVERTEX v; v.x=x; v.y=y; v.z=0; v.rhw=1.0f; v.color = color; v.tu = tu; v.tv = tv;
45 return v;
48 CD3DFont::CD3DFont()
50 m_pTexture = NULL;
51 m_pVB = NULL;
54 enum {m_dwTexWidth = 512, m_dwTexHeight = 512};
56 int CD3DFont::Init()
58 // Create vertex buffer for the letters
59 HRESULT hr;
60 if (FAILED(hr = dev->CreateVertexBuffer(MAX_NUM_VERTICES*sizeof(FONT2DVERTEX),
61 D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0, D3DPOOL_DEFAULT, &m_pVB, NULL)))
63 return hr;
65 m_fTextScale = 1.0f; // Draw fonts into texture without scaling
67 // Prepare to create a bitmap
68 unsigned int* pBitmapBits;
69 BITMAPINFO bmi;
70 ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
71 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
72 bmi.bmiHeader.biWidth = (int)m_dwTexWidth;
73 bmi.bmiHeader.biHeight = -(int)m_dwTexHeight;
74 bmi.bmiHeader.biPlanes = 1;
75 bmi.bmiHeader.biCompression = BI_RGB;
76 bmi.bmiHeader.biBitCount = 32;
78 // Create a DC and a bitmap for the font
79 HDC hDC = CreateCompatibleDC(NULL);
80 HBITMAP hbmBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void**)&pBitmapBits, NULL, 0);
81 SetMapMode(hDC, MM_TEXT);
83 // Create a font. By specifying ANTIALIASED_QUALITY, we might get an
84 // antialiased font, but this is not guaranteed.
85 // We definitely don't want to get it cleartype'd, anyway.
86 int m_dwFontHeight = 24;
87 int nHeight = -MulDiv(m_dwFontHeight, int(GetDeviceCaps(hDC, LOGPIXELSY) * m_fTextScale), 72);
88 int dwBold = FW_NORMAL; ///FW_BOLD
89 HFONT hFont = CreateFont(nHeight, 0, 0, 0, dwBold, 0,
90 FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
91 CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
92 VARIABLE_PITCH, _T("Tahoma"));
93 if (NULL == hFont)
94 return E_FAIL;
96 HGDIOBJ hOldbmBitmap = SelectObject(hDC, hbmBitmap);
97 HGDIOBJ hOldFont = SelectObject(hDC, hFont);
99 // Set text properties
100 SetTextColor(hDC, 0xFFFFFF);
101 SetBkColor (hDC, 0);
102 SetTextAlign(hDC, TA_TOP);
104 // Loop through all printable characters and output them to the bitmap
105 // Meanwhile, keep track of the corresponding tex coords for each character.
106 int x = 0, y = 0;
107 char str[2] = "\0";
108 for (int c = 0; c < 127 - 32; c++)
110 str[0] = c + 32;
111 SIZE size;
112 GetTextExtentPoint32A(hDC, str, 1, &size);
113 if ((int)(x+size.cx+1) > m_dwTexWidth)
115 x = 0;
116 y += size.cy + 1;
119 ExtTextOutA(hDC, x+1, y+0, ETO_OPAQUE | ETO_CLIPPED, NULL, str, 1, NULL);
120 m_fTexCoords[c][0] = ((float)(x+0))/m_dwTexWidth;
121 m_fTexCoords[c][1] = ((float)(y+0))/m_dwTexHeight;
122 m_fTexCoords[c][2] = ((float)(x+0+size.cx))/m_dwTexWidth;
123 m_fTexCoords[c][3] = ((float)(y+0+size.cy))/m_dwTexHeight;
125 x += size.cx + 3; // 3 to work around annoying ij conflict (part of the j ends up with the i)
128 // Create a new texture for the font
129 // possible optimization: store the converted data in a buffer and fill the texture on creation.
130 // That way, we can use a static texture
131 hr = dev->CreateTexture(m_dwTexWidth, m_dwTexHeight, 1, D3DUSAGE_DYNAMIC,
132 D3DFMT_A4R4G4B4, D3DPOOL_DEFAULT, &m_pTexture, NULL);
133 if (FAILED(hr))
135 PanicAlert("Failed to create font texture");
136 return hr;
139 // Lock the surface and write the alpha values for the set pixels
140 D3DLOCKED_RECT d3dlr;
141 m_pTexture->LockRect(0, &d3dlr, 0, D3DLOCK_DISCARD);
142 int bAlpha; // 4-bit measure of pixel intensity
144 for (y = 0; y < m_dwTexHeight; y++)
146 u16 *pDst16 = (u16*)((u8 *)d3dlr.pBits + y * d3dlr.Pitch);
147 for (x = 0; x < m_dwTexWidth; x++)
149 bAlpha = ((pBitmapBits[m_dwTexWidth * y + x] & 0xff) >> 4);
150 pDst16[x] = (bAlpha << 12) | 0x0fff;
154 // Done updating texture, so clean up used objects
155 m_pTexture->UnlockRect(0);
157 SelectObject(hDC, hOldbmBitmap);
158 DeleteObject(hbmBitmap);
160 SelectObject(hDC, hOldFont);
161 DeleteObject(hFont);
163 return S_OK;
166 int CD3DFont::Shutdown()
168 m_pVB->Release();
169 m_pVB = NULL;
170 m_pTexture->Release();
171 m_pTexture = NULL;
172 return S_OK;
176 const int RS[6][2] =
178 {D3DRS_ALPHABLENDENABLE, TRUE},
179 {D3DRS_SRCBLEND, D3DBLEND_SRCALPHA},
180 {D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA},
181 {D3DRS_CULLMODE, D3DCULL_NONE},
182 {D3DRS_ZENABLE, FALSE},
183 {D3DRS_FOGENABLE, FALSE},
185 const int TS[6][2] =
187 {D3DTSS_COLOROP, D3DTOP_MODULATE},
188 {D3DTSS_COLORARG1, D3DTA_TEXTURE},
189 {D3DTSS_COLORARG2, D3DTA_DIFFUSE },
190 {D3DTSS_ALPHAOP, D3DTOP_MODULATE },
191 {D3DTSS_ALPHAARG1, D3DTA_TEXTURE },
192 {D3DTSS_ALPHAARG2, D3DTA_DIFFUSE },
195 static LPDIRECT3DPIXELSHADER9 ps_old = NULL;
196 static LPDIRECT3DVERTEXSHADER9 vs_old = NULL;
198 void RestoreShaders()
200 D3D::SetTexture(0, 0);
201 D3D::RefreshVertexDeclaration();
202 D3D::RefreshPixelShader();
203 D3D::RefreshVertexShader();
206 void RestoreRenderStates()
208 RestoreShaders();
209 for (int i = 0; i < 6; i++)
211 D3D::RefreshRenderState((_D3DRENDERSTATETYPE)RS[i][0]);
212 D3D::RefreshTextureStageState(0, (_D3DTEXTURESTAGESTATETYPE)int(TS[i][0]));
216 void CD3DFont::SetRenderStates()
218 D3D::SetTexture(0, m_pTexture);
220 dev->SetPixelShader(0);
221 dev->SetVertexShader(0);
223 dev->SetFVF(D3DFVF_FONT2DVERTEX);
225 for (int i = 0; i < 6; i++)
227 D3D::ChangeRenderState((_D3DRENDERSTATETYPE)RS[i][0], RS[i][1]);
228 D3D::ChangeTextureStageState(0, (_D3DTEXTURESTAGESTATETYPE)int(TS[i][0]), TS[i][1]);
233 int CD3DFont::DrawTextScaled(float x, float y, float fXScale, float fYScale, float spacing, u32 dwColor, const char* strText, bool center)
235 if (!m_pVB)
236 return 0;
238 SetRenderStates();
239 dev->SetStreamSource(0, m_pVB, 0, sizeof(FONT2DVERTEX));
241 float vpWidth = 1;
242 float vpHeight = 1;
244 float sx = x*vpWidth-0.5f;
245 float sy = y*vpHeight-0.5f;
247 float fStartX = sx;
249 float invLineHeight = 1.0f / ((m_fTexCoords[0][3] - m_fTexCoords[0][1]) * m_dwTexHeight);
250 // Fill vertex buffer
251 FONT2DVERTEX* pVertices;
252 int dwNumTriangles = 0L;
253 m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD);
255 const char *oldstrText=strText;
256 //First, let's measure the text
257 float tw=0;
258 float mx=0;
259 float maxx=0;
261 while (*strText)
263 char c = *strText++;
265 if (c == ('\n'))
266 mx = 0;
267 if (c < (' '))
268 continue;
270 float tx1 = m_fTexCoords[c-32][0];
271 float tx2 = m_fTexCoords[c-32][2];
273 float w = (tx2-tx1)*m_dwTexWidth;
274 w *= (fXScale*vpHeight)*invLineHeight;
275 mx += w + spacing*fXScale*vpWidth;
276 if (mx > maxx) maxx = mx;
279 float offset = -maxx/2;
280 strText = oldstrText;
281 //Then let's draw it
282 if (center)
284 sx+=offset;
285 fStartX+=offset;
288 float wScale = (fXScale*vpHeight)*invLineHeight;
289 float hScale = (fYScale*vpHeight)*invLineHeight;
291 while (*strText)
293 char c = *strText++;
295 if (c == ('\n'))
297 sx = fStartX;
298 sy += fYScale*vpHeight;
300 if (c < (' '))
301 continue;
303 c -= 32;
304 float tx1 = m_fTexCoords[c][0];
305 float ty1 = m_fTexCoords[c][1];
306 float tx2 = m_fTexCoords[c][2];
307 float ty2 = m_fTexCoords[c][3];
309 float w = (tx2-tx1)*m_dwTexWidth;
310 float h = (ty2-ty1)*m_dwTexHeight;
312 w *= wScale;
313 h *= hScale;
315 FONT2DVERTEX v[6];
316 v[0] = InitFont2DVertex(sx, sy+h, dwColor, tx1, ty2);
317 v[1] = InitFont2DVertex(sx, sy, dwColor, tx1, ty1);
318 v[2] = InitFont2DVertex(sx+w, sy+h, dwColor, tx2, ty2);
319 v[3] = InitFont2DVertex(sx+w, sy, dwColor, tx2, ty1);
320 v[4] = v[2];
321 v[5] = v[1];
323 memcpy(pVertices, v, 6*sizeof(FONT2DVERTEX));
325 pVertices+=6;
326 dwNumTriangles += 2;
328 if (dwNumTriangles * 3 > (MAX_NUM_VERTICES - 6))
330 // Unlock, render, and relock the vertex buffer
331 m_pVB->Unlock();
332 dev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles);
333 m_pVB->Lock(0, 0, (void**)&pVertices, D3DLOCK_DISCARD);
334 dwNumTriangles = 0;
336 sx += w + spacing*fXScale*vpWidth;
339 // Unlock and render the vertex buffer
340 m_pVB->Unlock();
341 if (dwNumTriangles > 0)
342 dev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, dwNumTriangles);
343 RestoreRenderStates();
344 return S_OK;
347 void quad2d(float x1, float y1, float x2, float y2, u32 color, float u1, float v1, float u2, float v2)
349 struct Q2DVertex { float x,y,z,rhw;u32 color;float u,v,w,h; } coords[4] = {
350 {x1-0.5f, y1-0.5f, 0, 1, color, u1, v1},
351 {x2-0.5f, y1-0.5f, 0, 1, color, u2, v1},
352 {x2-0.5f, y2-0.5f, 0, 1, color, u2, v2},
353 {x1-0.5f, y2-0.5f, 0, 1, color, u1, v2},
355 dev->SetPixelShader(0);
356 dev->SetVertexShader(0);
357 dev->SetVertexDeclaration(NULL);
358 dev->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
359 dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, coords, sizeof(Q2DVertex));
360 RestoreShaders();
363 void drawShadedTexQuad(IDirect3DTexture9 *texture,
364 const RECT *rSource,
365 int SourceWidth,
366 int SourceHeight,
367 int DestWidth,
368 int DestHeight,
369 IDirect3DPixelShader9 *PShader,
370 IDirect3DVertexShader9 *Vshader)
372 float sw = 1.0f /(float) SourceWidth;
373 float sh = 1.0f /(float) SourceHeight;
374 float dw = 1.0f /(float) DestWidth;
375 float dh = 1.0f /(float) DestHeight;
376 float u1=((float)rSource->left) * sw;
377 float u2=((float)rSource->right) * sw;
378 float v1=((float)rSource->top) * sh;
379 float v2=((float)rSource->bottom) * sh;
381 struct Q2DVertex { float x,y,z,rhw,u,v,w,h,L,T,R,B; } coords[4] = {
382 {-1.0f - dw,-1.0f + dh, 0.0f,1.0f, u1, v2, sw, sh,u1,v1,u2,v2},
383 {-1.0f - dw, 1.0f + dh, 0.0f,1.0f, u1, v1, sw, sh,u1,v1,u2,v2},
384 { 1.0f - dw,-1.0f + dh, 0.0f,1.0f, u2, v2, sw, sh,u1,v1,u2,v2},
385 { 1.0f - dw, 1.0f + dh, 0.0f,1.0f, u2, v1, sw, sh,u1,v1,u2,v2}
387 dev->SetVertexShader(Vshader);
388 dev->SetPixelShader(PShader);
389 D3D::SetTexture(0, texture);
390 dev->SetFVF(D3DFVF_XYZW | D3DFVF_TEX3 | D3DFVF_TEXCOORDSIZE4(2));
391 dev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, coords, sizeof(Q2DVertex));
392 RestoreShaders();
395 void drawShadedTexSubQuad(IDirect3DTexture9 *texture,
396 const MathUtil::Rectangle<float> *rSource,
397 int SourceWidth,
398 int SourceHeight,
399 const MathUtil::Rectangle<float> *rDest,
400 int DestWidth,
401 int DestHeight,
402 IDirect3DPixelShader9 *PShader,
403 IDirect3DVertexShader9 *Vshader)
405 float sw = 1.0f /(float) SourceWidth;
406 float sh = 1.0f /(float) SourceHeight;
407 float dw = 1.0f /(float) DestWidth;
408 float dh = 1.0f /(float) DestHeight;
409 float u1= rSource->left * sw;
410 float u2= rSource->right * sw;
411 float v1= rSource->top * sh;
412 float v2= rSource->bottom * sh;
414 struct Q2DVertex { float x,y,z,rhw,u,v,w,h,L,T,R,B; } coords[4] = {
415 { rDest->left - dw , rDest->top + dh, 1.0f,1.0f, u1, v2, sw, sh,u1,v1,u2,v2},
416 { rDest->left - dw , rDest->bottom + dh, 1.0f,1.0f, u1, v1, sw, sh,u1,v1,u2,v2},
417 { rDest->right - dw , rDest->top + dh, 1.0f,1.0f, u2, v2, sw, sh,u1,v1,u2,v2},
418 { rDest->right - dw , rDest->bottom + dh, 1.0f,1.0f, u2, v1, sw, sh,u1,v1,u2,v2}
420 dev->SetVertexShader(Vshader);
421 dev->SetPixelShader(PShader);
422 D3D::SetTexture(0, texture);
423 dev->SetFVF(D3DFVF_XYZW | D3DFVF_TEX3 | D3DFVF_TEXCOORDSIZE4(2));
424 dev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, coords, sizeof(Q2DVertex));
425 RestoreShaders();
428 // Fills a certain area of the current render target with the specified color
429 // Z buffer disabled; destination coordinates normalized to (-1;1)
430 void drawColorQuad(u32 Color, float x1, float y1, float x2, float y2)
432 struct CQVertex { float x, y, z, rhw; u32 col; } coords[4] = {
433 { x1, y2, 0.f, 1.f, Color },
434 { x2, y2, 0.f, 1.f, Color },
435 { x1, y1, 0.f, 1.f, Color },
436 { x2, y1, 0.f, 1.f, Color },
438 dev->SetVertexShader(VertexShaderCache::GetClearVertexShader());
439 dev->SetPixelShader(PixelShaderCache::GetClearProgram());
440 dev->SetFVF(D3DFVF_XYZW | D3DFVF_DIFFUSE);
441 dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, coords, sizeof(CQVertex));
442 RestoreShaders();
445 void drawClearQuad(u32 Color,float z,IDirect3DPixelShader9 *PShader,IDirect3DVertexShader9 *Vshader)
447 struct Q2DVertex { float x,y,z,rhw;u32 color;} coords[4] = {
448 {-1.0f, 1.0f, z, 1.0f, Color},
449 { 1.0f, 1.0f, z, 1.0f, Color},
450 { 1.0f, -1.0f, z, 1.0f, Color},
451 {-1.0f, -1.0f, z, 1.0f, Color}
453 dev->SetVertexShader(Vshader);
454 dev->SetPixelShader(PShader);
455 dev->SetFVF(D3DFVF_XYZW | D3DFVF_DIFFUSE);
456 dev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, coords, sizeof(Q2DVertex));
457 RestoreShaders();
461 } // namespace