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/
21 #include "StringUtil.h"
27 #include "Statistics.h"
29 #include "VideoConfig.h"
31 #include "VertexManager.h"
33 #include "OpcodeDecoding.h"
34 #include "BPStructs.h"
35 #include "XFStructs.h"
37 #include "VertexShaderManager.h"
38 #include "PixelShaderManager.h"
39 #include "VertexShaderCache.h"
40 #include "PixelShaderCache.h"
41 #include "VertexLoaderManager.h"
42 #include "TextureCache.h"
43 #include "EmuWindow.h"
45 #include "OnScreenDisplay.h"
46 #include "FBManager.h"
54 static bool WindowResized
;
55 static int s_target_width
;
56 static int s_target_height
;
58 static int s_Fulltarget_width
;
59 static int s_Fulltarget_height
;
61 static int s_backbuffer_width
;
62 static int s_backbuffer_height
;
64 static int s_XFB_width
;
65 static int s_XFB_height
;
70 static u32 s_blendMode
;
71 static bool XFBWrited
;
73 static bool s_bScreenshot
= false;
74 static Common::CriticalSection s_criticalScreenshot
;
75 static char s_sScreenshotName
[1024];
77 ID3D11Buffer
* access_efb_cbuf
= NULL
;
78 ID3D11DepthStencilState
* cleardepthstates
[2] = {NULL
};
79 ID3D11RasterizerState
* clearraststate
= NULL
;
80 ID3D11BlendState
* resetblendstate
= NULL
;
81 ID3D11DepthStencilState
* resetdepthstate
= NULL
;
82 ID3D11RasterizerState
* resetraststate
= NULL
;
84 // state translation lookup tables
85 static const D3D11_BLEND d3dSrcFactors
[8] =
89 D3D11_BLEND_DEST_COLOR
,
90 D3D11_BLEND_INV_DEST_COLOR
,
91 D3D11_BLEND_SRC_ALPHA
,
92 D3D11_BLEND_INV_SRC_ALPHA
,
93 D3D11_BLEND_DEST_ALPHA
,
94 D3D11_BLEND_INV_DEST_ALPHA
97 static const D3D11_BLEND d3dDestFactors
[8] =
101 D3D11_BLEND_SRC_COLOR
,
102 D3D11_BLEND_INV_SRC_COLOR
,
103 D3D11_BLEND_SRC_ALPHA
,
104 D3D11_BLEND_INV_SRC_ALPHA
,
105 D3D11_BLEND_DEST_ALPHA
,
106 D3D11_BLEND_INV_DEST_ALPHA
110 // 1 Source & destination
111 // 2 Source & ~destination
113 // 4 ~Source & destination
115 // 6 Source ^ destination = Source & ~destination | ~Source & destination
116 // 7 Source | destination
118 // 8 ~(Source | destination)
119 // 9 ~(Source ^ destination) = ~Source & ~destination | Source & destination
121 // 11 Source | ~destination
123 // 13 ~Source | destination
124 // 14 ~(Source & destination)
127 static const D3D11_BLEND_OP d3dLogicOps
[16] =
129 D3D11_BLEND_OP_ADD
,//0
130 D3D11_BLEND_OP_ADD
,//1
131 D3D11_BLEND_OP_SUBTRACT
,//2
132 D3D11_BLEND_OP_ADD
,//3
133 D3D11_BLEND_OP_REV_SUBTRACT
,//4
134 D3D11_BLEND_OP_ADD
,//5
135 D3D11_BLEND_OP_MAX
,//6
136 D3D11_BLEND_OP_ADD
,//7
138 D3D11_BLEND_OP_MAX
,//8
139 D3D11_BLEND_OP_MAX
,//9
140 D3D11_BLEND_OP_ADD
,//10
141 D3D11_BLEND_OP_ADD
,//11
142 D3D11_BLEND_OP_ADD
,//12
143 D3D11_BLEND_OP_ADD
,//13
144 D3D11_BLEND_OP_ADD
,//14
145 D3D11_BLEND_OP_ADD
//15
148 static const D3D11_BLEND d3dLogicOpSrcFactors
[16] =
151 D3D11_BLEND_DEST_COLOR
,//1
154 D3D11_BLEND_DEST_COLOR
,//4
156 D3D11_BLEND_INV_DEST_COLOR
,//6
157 D3D11_BLEND_INV_DEST_COLOR
,//7
159 D3D11_BLEND_INV_SRC_COLOR
,//8
160 D3D11_BLEND_INV_SRC_COLOR
,//9
161 D3D11_BLEND_INV_DEST_COLOR
,//10
163 D3D11_BLEND_INV_SRC_COLOR
,//12
164 D3D11_BLEND_INV_SRC_COLOR
,//13
165 D3D11_BLEND_INV_DEST_COLOR
,//14
169 static const D3D11_BLEND d3dLogicOpDestFactors
[16] =
173 D3D11_BLEND_INV_SRC_COLOR
,//2
177 D3D11_BLEND_INV_SRC_COLOR
,//6
180 D3D11_BLEND_INV_DEST_COLOR
,//8
181 D3D11_BLEND_SRC_COLOR
,//9
182 D3D11_BLEND_INV_DEST_COLOR
,//10
183 D3D11_BLEND_INV_DEST_COLOR
,//11
184 D3D11_BLEND_INV_SRC_COLOR
,//12
186 D3D11_BLEND_INV_SRC_COLOR
,//14
190 static const D3D11_CULL_MODE d3dCullModes
[4] =
198 static const D3D11_COMPARISON_FUNC d3dCmpFuncs
[8] =
200 D3D11_COMPARISON_NEVER
,
201 D3D11_COMPARISON_LESS
,
202 D3D11_COMPARISON_EQUAL
,
203 D3D11_COMPARISON_LESS_EQUAL
,
204 D3D11_COMPARISON_GREATER
,
205 D3D11_COMPARISON_NOT_EQUAL
,
206 D3D11_COMPARISON_GREATER_EQUAL
,
207 D3D11_COMPARISON_ALWAYS
212 #define TEXF_LINEAR 2
213 static const unsigned int d3dMipFilters
[4] =
218 TEXF_NONE
, //reserved
221 static const D3D11_TEXTURE_ADDRESS_MODE d3dClamps
[4] =
223 D3D11_TEXTURE_ADDRESS_CLAMP
,
224 D3D11_TEXTURE_ADDRESS_WRAP
,
225 D3D11_TEXTURE_ADDRESS_MIRROR
,
226 D3D11_TEXTURE_ADDRESS_WRAP
//reserved
229 void SetupDeviceObjects()
232 VertexLoaderManager::Init();
235 VertexShaderManager::Dirty();
236 PixelShaderManager::Dirty();
238 float colmat
[20]= {0.0f
};
239 colmat
[0] = colmat
[5] = colmat
[10] = 1.0f
;
240 D3D11_BUFFER_DESC cbdesc
= CD3D11_BUFFER_DESC(20*sizeof(float), D3D11_BIND_CONSTANT_BUFFER
, D3D11_USAGE_DEFAULT
);
241 D3D11_SUBRESOURCE_DATA data
;
242 data
.pSysMem
= colmat
;
243 D3D::device
->CreateBuffer(&cbdesc
, &data
, &access_efb_cbuf
);
245 D3D11_DEPTH_STENCIL_DESC ddesc
;
246 ddesc
.DepthEnable
= FALSE
;
247 ddesc
.DepthWriteMask
= D3D11_DEPTH_WRITE_MASK_ZERO
;
248 ddesc
.DepthFunc
= D3D11_COMPARISON_ALWAYS
;
249 ddesc
.StencilEnable
= FALSE
;
250 ddesc
.StencilReadMask
= D3D11_DEFAULT_STENCIL_READ_MASK
;
251 ddesc
.StencilWriteMask
= D3D11_DEFAULT_STENCIL_WRITE_MASK
;
252 D3D::device
->CreateDepthStencilState(&ddesc
, &cleardepthstates
[0]);
253 ddesc
.DepthWriteMask
= D3D11_DEPTH_WRITE_MASK_ALL
;
254 ddesc
.DepthEnable
= TRUE
;
255 D3D::device
->CreateDepthStencilState(&ddesc
, &cleardepthstates
[1]);
256 D3D::SetDebugObjectName((ID3D11DeviceChild
*)cleardepthstates
[0], "depth state for Renderer::ClearScreen (depth buffer disabled)");
257 D3D::SetDebugObjectName((ID3D11DeviceChild
*)cleardepthstates
[1], "depth state for Renderer::ClearScreen (depth buffer enabled)");
259 // TODO: once multisampling gets implemented, this might need to be changed
260 D3D11_RASTERIZER_DESC rdesc
= CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID
, D3D11_CULL_NONE
, false, 0, 0.f
, 0.f
, false, true, false, false);
261 D3D::device
->CreateRasterizerState(&rdesc
, &clearraststate
);
262 D3D::SetDebugObjectName((ID3D11DeviceChild
*)clearraststate
, "rasterizer state for Renderer::ClearScreen");
264 D3D11_BLEND_DESC blenddesc
;
265 blenddesc
.AlphaToCoverageEnable
= FALSE
;
266 blenddesc
.IndependentBlendEnable
= FALSE
;
267 blenddesc
.RenderTarget
[0].BlendEnable
= FALSE
;
268 blenddesc
.RenderTarget
[0].RenderTargetWriteMask
= D3D11_COLOR_WRITE_ENABLE_ALL
;
269 blenddesc
.RenderTarget
[0].SrcBlend
= D3D11_BLEND_ONE
;
270 blenddesc
.RenderTarget
[0].DestBlend
= D3D11_BLEND_ZERO
;
271 blenddesc
.RenderTarget
[0].BlendOp
= D3D11_BLEND_OP_ADD
;
272 blenddesc
.RenderTarget
[0].SrcBlendAlpha
= D3D11_BLEND_ONE
;
273 blenddesc
.RenderTarget
[0].DestBlendAlpha
= D3D11_BLEND_ZERO
;
274 blenddesc
.RenderTarget
[0].BlendOpAlpha
= D3D11_BLEND_OP_ADD
;
275 D3D::device
->CreateBlendState(&blenddesc
, &resetblendstate
);
276 D3D::SetDebugObjectName((ID3D11DeviceChild
*)resetblendstate
, "blend state for Renderer::ResetAPIState");
278 // TODO: For some reason this overwrites existing resource private data...
279 ddesc
.DepthEnable
= FALSE
;
280 ddesc
.DepthWriteMask
= D3D11_DEPTH_WRITE_MASK_ZERO
;
281 ddesc
.DepthFunc
= D3D11_COMPARISON_LESS
;
282 ddesc
.StencilEnable
= FALSE
;
283 ddesc
.StencilReadMask
= D3D11_DEFAULT_STENCIL_READ_MASK
;
284 ddesc
.StencilWriteMask
= D3D11_DEFAULT_STENCIL_WRITE_MASK
;
285 D3D::device
->CreateDepthStencilState(&ddesc
, &resetdepthstate
);
286 D3D::SetDebugObjectName((ID3D11DeviceChild
*)resetdepthstate
, "depth stencil state for Renderer::ResetAPIState");
288 // this might need to be changed once multisampling support gets added
289 D3D11_RASTERIZER_DESC rastdesc
= CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID
, D3D11_CULL_NONE
, false, 0, 0.f
, 0.f
, false, false, false, false);
290 D3D::device
->CreateRasterizerState(&rastdesc
, &resetraststate
);
291 D3D::SetDebugObjectName((ID3D11DeviceChild
*)resetraststate
, "rasterizer state for Renderer::ResetAPIState");
294 void TeardownDeviceObjects()
296 D3D::context
->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL
);
298 D3D::font
.Shutdown();
299 TextureCache::Invalidate(false);
300 VertexManager::DestroyDeviceObjects();
301 VertexLoaderManager::Shutdown();
302 VertexShaderCache::Clear();
303 PixelShaderCache::Clear();
305 SAFE_RELEASE(access_efb_cbuf
);
306 SAFE_RELEASE(cleardepthstates
[0]);
307 SAFE_RELEASE(cleardepthstates
[1]);
308 SAFE_RELEASE(clearraststate
);
309 SAFE_RELEASE(resetblendstate
);
310 SAFE_RELEASE(resetdepthstate
);
311 SAFE_RELEASE(resetraststate
);
314 bool Renderer::Init()
316 UpdateActiveConfig();
317 int x
, y
, w_temp
, h_temp
;
320 g_VideoInitialize
.pRequestWindowSize(x
, y
, w_temp
, h_temp
);
322 D3D::Create(EmuWindow::GetWnd());
324 s_backbuffer_width
= D3D::GetBackBufferWidth();
325 s_backbuffer_height
= D3D::GetBackBufferHeight();
327 s_XFB_width
= MAX_XFB_WIDTH
;
328 s_XFB_height
= MAX_XFB_HEIGHT
;
330 TargetRectangle dst_rect
;
331 ComputeDrawRectangle(s_backbuffer_width
, s_backbuffer_height
, false, &dst_rect
);
333 xScale
= (float)(dst_rect
.right
- dst_rect
.left
) / (float)s_XFB_width
;
334 yScale
= (float)(dst_rect
.bottom
- dst_rect
.top
) / (float)s_XFB_height
;
336 s_target_width
= (int)(EFB_WIDTH
* xScale
);
337 s_target_height
= (int)(EFB_HEIGHT
* yScale
);
339 s_Fulltarget_width
= s_target_width
;
340 s_Fulltarget_height
= s_target_height
;
342 SetupDeviceObjects();
344 for (unsigned int stage
= 0; stage
< 8; stage
++)
345 D3D::gfxstate
->samplerdesc
[stage
].MaxAnisotropy
= g_ActiveConfig
.iMaxAnisotropy
;
347 float ClearColor
[4] = { 0.f
, 0.f
, 0.f
, 0.f
};
348 D3D::context
->ClearRenderTargetView(FBManager
.GetEFBColorTexture()->GetRTV(), ClearColor
);
349 D3D::context
->ClearDepthStencilView(FBManager
.GetEFBDepthTexture()->GetDSV(), D3D11_CLEAR_DEPTH
, 1.f
, 0);
351 D3D11_VIEWPORT vp
= CD3D11_VIEWPORT((float)(s_Fulltarget_width
- s_target_width
) / 2.f
,
352 (float)(s_Fulltarget_height
- s_target_height
) / 2.f
,
353 (float)s_target_width
, (float)s_target_height
);
354 D3D::context
->RSSetViewports(1, &vp
);
355 D3D::context
->OMSetRenderTargets(1, &FBManager
.GetEFBColorTexture()->GetRTV(), FBManager
.GetEFBDepthTexture()->GetDSV());
357 D3D::gfxstate
->rastdesc
.ScissorEnable
= TRUE
;
361 void Renderer::Shutdown()
363 TeardownDeviceObjects();
369 int Renderer::GetTargetWidth() { return s_target_width
; }
370 int Renderer::GetTargetHeight() { return s_target_height
; }
371 int Renderer::GetFullTargetWidth() { return s_Fulltarget_width
; }
372 int Renderer::GetFullTargetHeight() { return s_Fulltarget_height
; }
373 float Renderer::GetTargetScaleX() { return xScale
; }
374 float Renderer::GetTargetScaleY() { return yScale
; }
376 int Renderer::GetFrameBufferWidth()
378 return s_backbuffer_width
;
380 int Renderer::GetFrameBufferHeight()
382 return s_backbuffer_height
;
385 // create On-Screen-Messages
386 void Renderer::DrawDebugText()
389 if (g_ActiveConfig
.bOSDHotKey
)
393 OSDTime
= Common::Timer::GetTimeMs() + 3000;
394 OSDChoice
= -OSDChoice
;
396 if ((u32
)OSDTime
> Common::Timer::GetTimeMs())
398 std::string T1
= "", T2
= "";
399 std::vector
<std::string
> T0
;
401 std::string OSDM1
= StringFromFormat("%i x %i", OSDInternalW
, OSDInternalH
);
403 switch(g_ActiveConfig
.iAspectRatio
)
408 case ASPECT_FORCE_16_9
:
411 case ASPECT_FORCE_4_3
:
419 g_ActiveConfig
.bCrop
? " (crop)" : "";
420 std::string OSDM3
= "Disabled";
422 // if there is more text than this we will have a collission
423 if (g_ActiveConfig
.bShowFPS
)
424 { T1
+= "\n\n"; T2
+= "\n\n"; }
426 T0
.push_back(StringFromFormat("3: Internal Resolution: %s\n", OSDM1
.c_str()));
427 T0
.push_back(StringFromFormat("4: Aspect Ratio: %s%s\n", OSDM21
.c_str(), OSDM22
.c_str()));
428 T0
.push_back(StringFromFormat("5: Copy EFB: %s\n", OSDM3
.c_str()));
429 T0
.push_back(StringFromFormat("6: Fog: %s\n", g_ActiveConfig
.bDisableFog
? "Disabled" : "Enabled"));
430 T0
.push_back(StringFromFormat("7: Material Lighting: %s\n", g_ActiveConfig
.bDisableLighting
? "Disabled" : "Enabled"));
432 // latest changed setting in yellow
433 T1
+= (OSDChoice
== -1) ? T0
.at(0) : "\n";
434 T1
+= (OSDChoice
== -2) ? T0
.at(1) : "\n";
435 T1
+= (OSDChoice
== -3) ? T0
.at(2) : "\n";
436 T1
+= (OSDChoice
== -4) ? T0
.at(3) : "\n";
437 T1
+= (OSDChoice
== -5) ? T0
.at(4) : "\n";
439 // other settings in cyan
440 T2
+= (OSDChoice
!= -1) ? T0
.at(0) : "\n";
441 T2
+= (OSDChoice
!= -2) ? T0
.at(1) : "\n";
442 T2
+= (OSDChoice
!= -3) ? T0
.at(2) : "\n";
443 T2
+= (OSDChoice
!= -4) ? T0
.at(3) : "\n";
444 T2
+= (OSDChoice
!= -5) ? T0
.at(4) : "\n";
446 // render a shadow, and then the text
447 Renderer::RenderText(T1
.c_str(), 21, 21, 0xDD000000);
448 Renderer::RenderText(T1
.c_str(), 20, 20, 0xFFffff00);
449 Renderer::RenderText(T2
.c_str(), 21, 21, 0xDD000000);
450 Renderer::RenderText(T2
.c_str(), 20, 20, 0xFF00FFFF);
455 void Renderer::RenderText(const char* text
, int left
, int top
, u32 color
)
457 D3D::font
.DrawTextScaled((float)left
, (float)top
, 15, 0.0f
, color
, text
, false);
460 TargetRectangle
Renderer::ConvertEFBRectangle(const EFBRectangle
& rc
)
462 int Xstride
= (s_Fulltarget_width
- s_target_width
) / 2;
463 int Ystride
= (s_Fulltarget_height
- s_target_height
) / 2;
464 TargetRectangle result
;
465 result
.left
= (int)(rc
.left
* xScale
) + Xstride
;
466 result
.top
= (int)(rc
.top
* yScale
) + Ystride
;
467 result
.right
= (int)(rc
.right
* xScale
) + Xstride
;
468 result
.bottom
= (int)(rc
.bottom
* yScale
) + Ystride
;
472 void CheckForResize()
474 while (EmuWindow::IsSizing())
477 if (EmuWindow::GetParentWnd())
479 // re-stretch window to parent window size again, if it has a parent window.
481 GetWindowRect(EmuWindow::GetParentWnd(), &rcParentWindow
);
482 int width
= rcParentWindow
.right
- rcParentWindow
.left
;
483 int height
= rcParentWindow
.bottom
- rcParentWindow
.top
;
484 if (width
!= s_backbuffer_width
|| height
!= s_backbuffer_height
)
485 ::MoveWindow(EmuWindow::GetWnd(), 0, 0, width
, height
, FALSE
);
488 GetClientRect(EmuWindow::GetWnd(), &rcWindow
);
489 int client_width
= rcWindow
.right
- rcWindow
.left
;
490 int client_height
= rcWindow
.bottom
- rcWindow
.top
;
493 if ((client_width
!= s_backbuffer_width
|| client_height
!= s_backbuffer_height
) &&
494 client_width
>= 4 && client_height
>= 4)
496 WindowResized
= true;
500 void Renderer::RenderToXFB(u32 xfbAddr
, u32 fbWidth
, u32 fbHeight
, const EFBRectangle
& sourceRc
)
502 if (!fbWidth
|| !fbHeight
)
504 VideoFifo_CheckEFBAccess();
505 VideoFifo_CheckSwapRequestAt(xfbAddr
, fbWidth
, fbHeight
);
506 FBManager
.CopyToXFB(xfbAddr
, fbWidth
, fbHeight
, sourceRc
);
509 // XXX: Without the VI, how would we know what kind of field this is? So
510 // just use progressive.
511 if (!g_ActiveConfig
.bUseXFB
)
513 Renderer::Swap(xfbAddr
, FIELD_PROGRESSIVE
, fbWidth
, fbHeight
);
514 Common::AtomicStoreRelease(s_swapRequested
, FALSE
);
518 bool Renderer::SetScissorRect()
520 int xoff
= bpmem
.scissorOffset
.x
* 2 - 342;
521 int yoff
= bpmem
.scissorOffset
.y
* 2 - 342;
522 D3D11_RECT rc
= CD3D11_RECT(bpmem
.scissorTL
.x
- xoff
- 342,
523 bpmem
.scissorTL
.y
- yoff
- 342,
524 bpmem
.scissorBR
.x
- xoff
- 341,
525 bpmem
.scissorBR
.y
- yoff
- 341);
527 int Xstride
= (s_Fulltarget_width
- s_target_width
) / 2;
528 int Ystride
= (s_Fulltarget_height
- s_target_height
) / 2;
530 rc
.left
= (int)(rc
.left
* xScale
);
531 rc
.top
= (int)(rc
.top
* yScale
);
532 rc
.right
= (int)(rc
.right
* xScale
);
533 rc
.bottom
= (int)(rc
.bottom
* yScale
);
535 if (rc
.left
< 0) rc
.left
= 0;
536 if (rc
.right
< 0) rc
.right
= 0;
537 if (rc
.left
> s_target_width
) rc
.left
= s_target_width
;
538 if (rc
.right
> s_target_width
) rc
.right
= s_target_width
;
539 if (rc
.top
< 0) rc
.top
= 0;
540 if (rc
.bottom
< 0) rc
.bottom
= 0;
541 if (rc
.top
> s_target_height
) rc
.top
= s_target_height
;
542 if (rc
.bottom
> s_target_height
) rc
.bottom
= s_target_height
;
547 rc
.bottom
+= Ystride
;
549 if (rc
.left
> rc
.right
)
555 if (rc
.top
> rc
.bottom
)
557 int temp
= rc
.bottom
;
562 if (rc
.right
>= rc
.left
&& rc
.bottom
>= rc
.top
)
564 D3D::context
->RSSetScissorRects(1, &rc
);
569 //WARN_LOG(VIDEO, "Bad scissor rectangle: %i %i %i %i", rc.left, rc.top, rc.right, rc.bottom);
570 rc
= CD3D11_RECT(Xstride
, Ystride
, Xstride
+ s_target_width
, Ystride
+ s_target_height
);
571 D3D::context
->RSSetScissorRects(1, &rc
);
577 void Renderer::SetColorMask()
579 UINT8 color_mask
= 0;
580 if (bpmem
.blendmode
.alphaupdate
) color_mask
|= D3D11_COLOR_WRITE_ENABLE_ALPHA
;
581 if (bpmem
.blendmode
.colorupdate
) color_mask
|= D3D11_COLOR_WRITE_ENABLE_RED
| D3D11_COLOR_WRITE_ENABLE_GREEN
| D3D11_COLOR_WRITE_ENABLE_BLUE
;
582 D3D::gfxstate
->SetRenderTargetWriteMask(color_mask
);
585 u32
Renderer::AccessEFB(EFBAccessType type
, int x
, int y
)
587 ID3D11Texture2D
* tex
;
589 if (!g_ActiveConfig
.bEFBAccessEnable
)
592 // get the rectangular target region covered by the EFB pixel
593 EFBRectangle efbPixelRc
;
596 efbPixelRc
.right
= x
+ 1;
597 efbPixelRc
.bottom
= y
+ 1;
599 TargetRectangle targetPixelRc
= Renderer::ConvertEFBRectangle(efbPixelRc
);
603 D3D11_RECT RectToLock
= CD3D11_RECT(targetPixelRc
.left
, targetPixelRc
.top
, targetPixelRc
.right
, targetPixelRc
.bottom
);
606 RectToLock
.bottom
+=2;
610 if ((RectToLock
.bottom
- RectToLock
.top
) > 4)
612 if ((RectToLock
.right
- RectToLock
.left
) > 4)
614 ResetAPIState(); // reset any game specific settings
615 D3D::context
->OMSetRenderTargets(1, &FBManager
.GetEFBDepthReadTexture()->GetRTV(), NULL
);
617 // Stretch picture with increased internal resolution
618 D3D11_VIEWPORT vp
= CD3D11_VIEWPORT(0.f
, 0.f
, 4.f
, 4.f
);
619 D3D::context
->RSSetViewports(1, &vp
);
621 D3D::context
->PSSetConstantBuffers(0, 1, &access_efb_cbuf
);
624 // D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
626 D3D::drawShadedTexQuad(FBManager
.GetEFBDepthTexture()->GetSRV(),
628 Renderer::GetFullTargetWidth(),
629 Renderer::GetFullTargetHeight(),
630 PixelShaderCache::GetDepthMatrixProgram(),
631 VertexShaderCache::GetSimpleVertexShader(),
632 VertexShaderCache::GetSimpleInputLayout());
634 // D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER);
636 // TODO: ?? check this code..
637 D3D::context
->OMSetRenderTargets(1, &FBManager
.GetEFBColorTexture()->GetRTV(), FBManager
.GetEFBDepthTexture()->GetDSV());
639 RectToLock
= CD3D11_RECT(0, 0, 4, 4);
641 D3D11_BOX box
= CD3D11_BOX(0, 0, 0, 4, 4, 1);
642 tex
= FBManager
.GetEFBDepthStagingBuffer();
643 D3D::context
->CopySubresourceRegion(tex
, 0, 0, 0, 0, FBManager
.GetEFBDepthReadTexture()->GetTex(), 0, &box
);
647 tex
= FBManager
.GetEFBColorStagingBuffer();
648 D3D11_BOX box
= CD3D11_BOX(RectToLock
.left
, RectToLock
.top
, 0, RectToLock
.right
, RectToLock
.bottom
, 1);
649 D3D::context
->CopySubresourceRegion(tex
, 0, 0, 0, 0, FBManager
.GetEFBColorTexture()->GetTex(), 0, &box
);
650 //change the rect to lock the entire one pixel buffer
651 RectToLock
= CD3D11_RECT(0, 0, 1, 1);
654 D3D11_MAPPED_SUBRESOURCE map
;
655 D3D::context
->Map(tex
, 0, D3D11_MAP_READ
, 0, &map
);
659 val
= ((float*)map
.pData
)[0];
660 z
= ((u32
)(val
* 0xffffff));
664 PanicAlert("Poke Z-buffer not implemented");
668 z
= ((u32
*)map
.pData
)[0];
672 PanicAlert("Poke color EFB not implemented");
675 D3D::context
->Unmap(tex
, 0);
679 // Called from VertexShaderManager
680 void UpdateViewport()
682 // reversed gxsetviewport(xorig, yorig, width, height, nearz, farz)
685 // [2] = 16777215 * (farz - nearz)
686 // [3] = xorig + width/2 + 342
687 // [4] = yorig + height/2 + 342
688 // [5] = 16777215 * farz
689 int scissorXOff
= bpmem
.scissorOffset
.x
* 2;
690 int scissorYOff
= bpmem
.scissorOffset
.y
* 2;
692 float MValueX
= Renderer::GetTargetScaleX();
693 float MValueY
= Renderer::GetTargetScaleY();
695 int Xstride
= (s_Fulltarget_width
- s_target_width
) / 2;
696 int Ystride
= (s_Fulltarget_height
- s_target_height
) / 2;
698 // Stretch picture with increased internal resolution
699 int X
= (int)(ceil(xfregs
.rawViewport
[3] - xfregs
.rawViewport
[0] - (scissorXOff
)) * MValueX
) + Xstride
;
700 int Y
= (int)(ceil(xfregs
.rawViewport
[4] + xfregs
.rawViewport
[1] - (scissorYOff
)) * MValueY
) + Ystride
;
701 int Width
= (int)ceil((int)(2 * xfregs
.rawViewport
[0]) * MValueX
);
702 int Height
= (int)ceil((int)(-2 * xfregs
.rawViewport
[1]) * MValueY
);
713 bool sizeChanged
= false;
716 s_Fulltarget_width
-= 2 * X
;
722 s_Fulltarget_height
-= 2 * Y
;
728 D3D::context
->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL
);
731 D3D::context
->OMSetRenderTargets(1, &FBManager
.GetEFBColorTexture()->GetRTV(), FBManager
.GetEFBDepthTexture()->GetDSV());
734 // some games set invalids values MinDepth and MaxDepth so fix them to the max an min allowed and let the shaders do this work
735 D3D11_VIEWPORT vp
= CD3D11_VIEWPORT((float)X
, (float)Y
, (float)Width
, (float)Height
,
736 0.f
, // (xfregs.rawViewport[5] - xfregs.rawViewport[2]) / 16777216.0f;
737 1.f
); // xfregs.rawViewport[5] / 16777216.0f;
738 D3D::context
->RSSetViewports(1, &vp
);
741 void Renderer::ClearScreen(const EFBRectangle
& rc
, bool colorEnable
, bool alphaEnable
, bool zEnable
, u32 color
, u32 z
)
743 TargetRectangle targetRc
= Renderer::ConvertEFBRectangle(rc
);
744 // update the view port for clearing the picture
745 D3D11_VIEWPORT vp
= CD3D11_VIEWPORT((float)targetRc
.left
, (float)targetRc
.top
, (float)targetRc
.GetWidth(), (float)targetRc
.GetHeight(),
748 D3D::context
->RSSetViewports(1, &vp
);
749 // always set the scissor in case it was set by the game and has not been reset
750 // TODO: Do we really need to set the scissor rect? Why not just disable scissor testing?
751 D3D11_RECT sirc
= CD3D11_RECT(targetRc
.left
, targetRc
.top
, targetRc
.right
, targetRc
.bottom
);
752 D3D::context
->RSSetScissorRects(1, &sirc
);
753 D3D::context
->OMSetDepthStencilState(cleardepthstates
[zEnable
], 0);
754 D3D::context
->RSSetState(clearraststate
);
755 D3D::drawClearQuad(color
, (z
& 0xFFFFFF) / float(0xFFFFFF),PixelShaderCache::GetClearProgram(),VertexShaderCache::GetClearVertexShader(), VertexShaderCache::GetClearInputLayout());
760 void Renderer::SetBlendMode(bool forceUpdate
)
762 #define BLEND_ENABLE_MASK 1
763 #define BLENDOP_SHIFT 2
764 #define BLENDOP_MASK 4
765 #define SRCFACTOR_SHIFT 3
766 #define DESTFACTOR_SHIFT 6
767 #define FACTOR_MASK 7
770 // blend mode bit mask
772 // 2 - reverse subtract enable (else add)
773 // 3-5 - srcRGB function
774 // 6-8 - dstRGB function
775 if (bpmem
.blendmode
.logicopenable
&& bpmem
.blendmode
.logicmode
!= 3)
778 u32 newval
= bpmem
.blendmode
.subtract
<< BLENDOP_SHIFT
;
779 if (bpmem
.blendmode
.subtract
) // enable blending src 1 dst 1
781 newval
|= BLEND_ENABLE_MASK
| (1 << SRCFACTOR_SHIFT
) | (1 << DESTFACTOR_SHIFT
);
783 else if (bpmem
.blendmode
.blendenable
)
785 newval
|= BLEND_ENABLE_MASK
; // enable blending
786 newval
|= bpmem
.blendmode
.srcfactor
<< SRCFACTOR_SHIFT
;
787 newval
|= bpmem
.blendmode
.dstfactor
<< DESTFACTOR_SHIFT
;
790 u32 changes
= forceUpdate
? 0xFFFFFFFF : newval
^ s_blendMode
;
792 if (changes
& BLEND_ENABLE_MASK
) // blend enable change
793 D3D::gfxstate
->SetAlphaBlendEnable(newval
& BLEND_ENABLE_MASK
);
795 if (changes
& BLENDOP_MASK
) // subtract enable change
796 D3D::gfxstate
->SetBlendOp((newval
& BLENDOP_MASK
) ? D3D11_BLEND_OP_REV_SUBTRACT
: D3D11_BLEND_OP_ADD
);
798 if (changes
& 0x1F8) // blend RGB change
800 D3D::gfxstate
->SetSrcBlend(d3dSrcFactors
[(newval
>> SRCFACTOR_SHIFT
) & FACTOR_MASK
]);
801 D3D::gfxstate
->SetDestBlend(d3dDestFactors
[(newval
>> DESTFACTOR_SHIFT
) & FACTOR_MASK
]);
803 s_blendMode
= newval
;
806 void Renderer::Swap(u32 xfbAddr
, FieldType field
, u32 fbWidth
, u32 fbHeight
)
808 if (g_bSkipCurrentFrame
|| (!XFBWrited
&& !g_ActiveConfig
.bUseRealXFB
) || !fbWidth
|| !fbHeight
)
810 g_VideoInitialize
.pCopiedToXFB(false);
813 // this function is called after the XFB field is changed, not after
814 // EFB is copied to XFB. In this way, flickering is reduced in games
815 // and seems to also give more FPS in ZTP
817 if (field
== FIELD_LOWER
) xfbAddr
-= fbWidth
* 2;
819 const XFBSource
** xfbSourceList
= FBManager
.GetXFBSource(xfbAddr
, fbWidth
, fbHeight
, xfbCount
);
820 if (!xfbSourceList
|| xfbCount
== 0)
822 g_VideoInitialize
.pCopiedToXFB(false);
826 Renderer::ResetAPIState();
827 // set the backbuffer as the rendering target
828 D3D::context
->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL
);
830 TargetRectangle dst_rect
;
831 ComputeDrawRectangle(s_backbuffer_width
, s_backbuffer_height
, false, &dst_rect
);
832 D3D11_VIEWPORT vp
= CD3D11_VIEWPORT(0.f
, 0.f
, (float)s_backbuffer_width
, (float)s_backbuffer_height
);
833 D3D::context
->RSSetViewports(1, &vp
);
834 float ClearColor
[4] = { 0.f
, 0.f
, 0.f
, 1.f
};
835 D3D::context
->ClearRenderTargetView(D3D::GetBackBuffer()->GetRTV(), ClearColor
);
837 int X
= dst_rect
.left
;
838 int Y
= dst_rect
.top
;
839 int Width
= dst_rect
.right
- dst_rect
.left
;
840 int Height
= dst_rect
.bottom
- dst_rect
.top
;
844 if (X
> s_backbuffer_width
) X
= s_backbuffer_width
;
845 if (Y
> s_backbuffer_height
) Y
= s_backbuffer_height
;
846 if (Width
< 0) Width
= 0;
847 if (Height
< 0) Height
= 0;
848 if (Width
> (s_backbuffer_width
- X
)) Width
= s_backbuffer_width
- X
;
849 if (Height
> (s_backbuffer_height
- Y
)) Height
= s_backbuffer_height
- Y
;
850 vp
= CD3D11_VIEWPORT((float)X
, (float)Y
, (float)Width
, (float)Height
);
851 D3D::context
->RSSetViewports(1, &vp
);
853 // TODO: Enable linear filtering here
855 const XFBSource
* xfbSource
;
857 // draw each xfb source
858 for (u32 i
= 0; i
< xfbCount
; ++i
)
860 xfbSource
= xfbSourceList
[i
];
862 MathUtil::Rectangle
<float> sourceRc
;
865 sourceRc
.right
= xfbSource
->texWidth
;
866 sourceRc
.bottom
= xfbSource
->texHeight
;
868 MathUtil::Rectangle
<float> drawRc
;
874 D3D::drawShadedTexSubQuad(xfbSource
->tex
->GetSRV(), &sourceRc
, xfbSource
->texWidth
, xfbSource
->texHeight
, &drawRc
, PixelShaderCache::GetColorCopyProgram(),VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout());
878 // copy back buffer to system memory
879 ID3D11Texture2D
* buftex
;
880 D3D11_TEXTURE2D_DESC tex_desc
= CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM
, D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight(), 1, 1, 0, D3D11_USAGE_STAGING
, D3D11_CPU_ACCESS_READ
|D3D11_CPU_ACCESS_WRITE
);
881 HRESULT hr
= D3D::device
->CreateTexture2D(&tex_desc
, NULL
, &buftex
);
882 if (FAILED(hr
)) PanicAlert("Failed to create screenshot buffer texture");
883 D3D::context
->CopyResource(buftex
, (ID3D11Resource
*)D3D::GetBackBuffer()->GetTex());
885 // D3DX11SaveTextureToFileA doesn't allow us to ignore the alpha channel, so we need to strip it out ourselves
886 D3D11_MAPPED_SUBRESOURCE map
;
887 D3D::context
->Map(buftex
, 0, D3D11_MAP_READ_WRITE
, 0, &map
);
888 for (unsigned int y
= 0; y
< D3D::GetBackBufferHeight(); ++y
)
890 u32
* ptr
= (u32
*)((u8
*)map
.pData
+ y
* map
.RowPitch
);
891 for (unsigned int x
= 0; x
< D3D::GetBackBufferWidth(); ++x
)
893 *ptr
= 0xFF000000 | (*ptr
& 0xFFFFFF);
897 D3D::context
->Unmap(buftex
, 0);
900 hr
= D3DX11SaveTextureToFileA(D3D::context
, buftex
, D3DX11_IFF_PNG
, s_sScreenshotName
);
901 if (FAILED(hr
)) PanicAlert("Failed to save screenshot");
903 s_bScreenshot
= false;
906 vp
= CD3D11_VIEWPORT(0.f
, 0.f
, s_backbuffer_width
, s_backbuffer_height
);
907 D3D::context
->RSSetViewports(1, &vp
);
910 if (g_ActiveConfig
.bShowFPS
)
913 StringCchPrintfA(fps
, 20, "FPS: %d\n", s_fps
);
914 D3D::font
.DrawTextScaled(0,30,30,0.0f
,0xFF00FFFF,fps
,false);
916 Renderer::DrawDebugText();
918 if (g_ActiveConfig
.bOverlayStats
)
921 Statistics::ToString(buf
);
922 D3D::font
.DrawTextScaled(0,30,30,0.0f
,0xFF00FFFF,buf
,false);
924 else if (g_ActiveConfig
.bOverlayProjStats
)
927 Statistics::ToStringProj(buf
);
928 D3D::font
.DrawTextScaled(0,30,30,0.0f
,0xFF00FFFF,buf
,false);
936 TextureCache::Cleanup();
938 // make any new configuration settings active.
939 UpdateActiveConfig();
940 WindowResized
= false;
943 bool xfbchanged
= false;
945 if (s_XFB_width
!= fbWidth
|| s_XFB_height
!= fbHeight
)
948 s_XFB_width
= fbWidth
;
949 s_XFB_height
= fbHeight
;
950 if (s_XFB_width
< 1) s_XFB_width
= MAX_XFB_WIDTH
;
951 if (s_XFB_width
> MAX_XFB_WIDTH
) s_XFB_width
= MAX_XFB_WIDTH
;
952 if (s_XFB_height
< 1) s_XFB_height
= MAX_XFB_HEIGHT
;
953 if (s_XFB_height
> MAX_XFB_HEIGHT
) s_XFB_height
= MAX_XFB_HEIGHT
;
956 // update FPS counter
957 static int fpscount
= 1;
958 static unsigned long lasttime
;
959 if (XFBWrited
) ++fpscount
;
960 if (Common::Timer::GetTimeMs() - lasttime
> 1000)
962 lasttime
= Common::Timer::GetTimeMs();
963 s_fps
= fpscount
- 1;
967 // set default viewport and scissor, for the clear to work correctly
970 // present backbuffer and begin next frame
972 if (xfbchanged
|| WindowResized
)
975 s_backbuffer_width
= D3D::GetBackBufferWidth();
976 s_backbuffer_height
= D3D::GetBackBufferHeight();
978 ComputeDrawRectangle(s_backbuffer_width
, s_backbuffer_height
, false, &dst_rect
);
980 xScale
= (float)(dst_rect
.right
- dst_rect
.left
) / (float)s_XFB_width
;
981 yScale
= (float)(dst_rect
.bottom
- dst_rect
.top
) / (float)s_XFB_height
;
983 s_target_width
= (int)(EFB_WIDTH
* xScale
);
984 s_target_height
= (int)(EFB_HEIGHT
* yScale
);
986 D3D::context
->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), NULL
);
991 D3D::context
->OMSetRenderTargets(1, &FBManager
.GetEFBColorTexture()->GetRTV(), FBManager
.GetEFBDepthTexture()->GetDSV());
994 Renderer::RestoreAPIState();
995 D3D::context
->OMSetRenderTargets(1, &FBManager
.GetEFBColorTexture()->GetRTV(), FBManager
.GetEFBDepthTexture()->GetDSV());
997 VertexShaderManager::SetViewportChanged();
998 g_VideoInitialize
.pCopiedToXFB(XFBWrited
);
1002 void Renderer::ResetAPIState()
1004 D3D::context
->OMSetBlendState(resetblendstate
, NULL
, 0xFFFFFFFF);
1005 D3D::context
->OMSetDepthStencilState(resetdepthstate
, 0);
1006 D3D::context
->RSSetState(resetraststate
);
1009 void Renderer::RestoreAPIState()
1011 // TODO: How much of this is actually needed?
1012 // gets us back into a more game-like state.
1013 D3D::gfxstate
->rastdesc
.ScissorEnable
= TRUE
;
1016 if (bpmem
.zmode
.testenable
) D3D::gfxstate
->depthdesc
.DepthEnable
= TRUE
;
1017 if (bpmem
.zmode
.updateenable
) D3D::gfxstate
->depthdesc
.DepthWriteMask
= D3D11_DEPTH_WRITE_MASK_ALL
;
1020 D3D::gfxstate
->ApplyState();
1023 void Renderer::SetGenerationMode()
1025 // rastdesc.FrontCounterClockwise must be false for this to work
1026 D3D::gfxstate
->rastdesc
.CullMode
= d3dCullModes
[bpmem
.genMode
.cullmode
];
1029 void Renderer::SetDepthMode()
1031 if (bpmem
.zmode
.testenable
)
1033 D3D::gfxstate
->depthdesc
.DepthEnable
= TRUE
;
1034 D3D::gfxstate
->depthdesc
.DepthWriteMask
= bpmem
.zmode
.updateenable
? D3D11_DEPTH_WRITE_MASK_ALL
: D3D11_DEPTH_WRITE_MASK_ZERO
;
1035 D3D::gfxstate
->depthdesc
.DepthFunc
= d3dCmpFuncs
[bpmem
.zmode
.func
];
1039 D3D::gfxstate
->depthdesc
.DepthEnable
= TRUE
;
1040 D3D::gfxstate
->depthdesc
.DepthWriteMask
= D3D11_DEPTH_WRITE_MASK_ZERO
;
1044 void Renderer::SetLogicOpMode()
1046 if (bpmem
.blendmode
.logicopenable
&& bpmem
.blendmode
.logicmode
!= 3)
1049 D3D::gfxstate
->SetAlphaBlendEnable(true);
1050 D3D::gfxstate
->SetBlendOp(d3dLogicOps
[bpmem
.blendmode
.logicmode
]);
1051 D3D::gfxstate
->SetSrcBlend(d3dLogicOpSrcFactors
[bpmem
.blendmode
.logicmode
]);
1052 D3D::gfxstate
->SetDestBlend(d3dLogicOpDestFactors
[bpmem
.blendmode
.logicmode
]);
1060 void Renderer::SetDitherMode()
1062 // TODO: Set dither mode to bpmem.blendmode.dither
1065 void Renderer::SetLineWidth()
1070 void Renderer::SetSamplerState(int stage
, int texindex
)
1072 const FourTexUnits
&tex
= bpmem
.tex
[texindex
];
1073 const TexMode0
&tm0
= tex
.texMode0
[stage
];
1074 const TexMode1
&tm1
= tex
.texMode1
[stage
];
1077 mip
= (tm0
.min_filter
== 8) ? TEXF_NONE
:d3dMipFilters
[tm0
.min_filter
& 3];
1078 if ((tm0
.min_filter
& 3) && (tm0
.min_filter
!= 8) && ((tm1
.max_lod
>> 4) == 0)) mip
= TEXF_NONE
;
1080 if (texindex
) stage
+= 4;
1082 // TODO: Clarify whether these values are correct
1083 // NOTE: since there's no "no filter" in DX11 we're using point filters in these cases
1084 if (tm0
.min_filter
& 4) // linear min filter
1086 if (tm0
.mag_filter
) // linear mag filter
1088 if (mip
== TEXF_NONE
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT
);
1089 else if (mip
== TEXF_POINT
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT
);
1090 else if (mip
== TEXF_LINEAR
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_MAG_MIP_LINEAR
);
1092 else // point mag filter
1094 if (mip
== TEXF_NONE
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT
);
1095 else if (mip
== TEXF_POINT
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT
);
1096 else if (mip
== TEXF_LINEAR
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR
);
1099 else // point min filter
1101 if (tm0
.mag_filter
) // linear mag filter
1103 if (mip
== TEXF_NONE
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT
);
1104 else if (mip
== TEXF_POINT
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT
);
1105 else if (mip
== TEXF_LINEAR
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR
);
1107 else // point mag filter
1109 if (mip
== TEXF_NONE
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_MAG_MIP_POINT
);
1110 else if (mip
== TEXF_POINT
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_MAG_MIP_POINT
);
1111 else if (mip
== TEXF_LINEAR
) D3D::gfxstate
->SetSamplerFilter(stage
, D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR
);
1115 D3D::gfxstate
->samplerdesc
[stage
].AddressU
= d3dClamps
[tm0
.wrap_s
];
1116 D3D::gfxstate
->samplerdesc
[stage
].AddressV
= d3dClamps
[tm0
.wrap_t
];
1118 D3D::gfxstate
->samplerdesc
[stage
].MipLODBias
= (float)tm0
.lod_bias
/32.0f
;
1119 D3D::gfxstate
->samplerdesc
[stage
].MaxLOD
= (float)tm1
.max_lod
/16.f
;
1120 D3D::gfxstate
->samplerdesc
[stage
].MinLOD
= (float)tm1
.min_lod
/16.f
;
1123 void Renderer::SetInterlacingMode()
1129 void Renderer::SetScreenshot(const char* filename
)
1131 s_criticalScreenshot
.Enter();
1132 strcpy_s(s_sScreenshotName
, filename
);
1133 s_bScreenshot
= true;
1134 s_criticalScreenshot
.Leave();