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 "Statistics.h"
22 #include "MemoryUtil.h"
25 #include "CommonPaths.h"
29 #include "D3DTexture.h"
31 #include "FramebufferManager.h"
32 #include "PixelShaderCache.h"
33 #include "PixelShaderManager.h"
34 #include "VertexShaderManager.h"
35 #include "VertexShaderCache.h"
39 #include "TextureDecoder.h"
40 #include "TextureCache.h"
41 #include "HiresTextures.h"
42 #include "TextureConverter.h"
44 #include "debugger/debugger.h"
46 u8
*TextureCache::temp
= NULL
;
47 TextureCache::TexCache
TextureCache::textures
;
49 extern int frameCount
;
51 #define TEMP_SIZE (1024*1024*4)
52 #define TEXTURE_KILL_THRESHOLD 200
54 void TextureCache::TCacheEntry::Destroy(bool shutdown
)
59 if (!isRenderTarget
&& !shutdown
&& !g_ActiveConfig
.bSafeTextureCache
)
61 u32
*ptr
= (u32
*)g_VideoInitialize
.pGetMemoryPointer(addr
);
62 if (ptr
&& *ptr
== hash
)
67 void TextureCache::Init()
69 temp
= (u8
*)AllocateMemoryPages(TEMP_SIZE
);
70 TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig
.bTexFmtOverlayEnable
, g_ActiveConfig
.bTexFmtOverlayCenter
);
71 HiresTextures::Init(globals
->unique_id
);
74 void TextureCache::Invalidate(bool shutdown
)
76 for (TexCache::iterator iter
= textures
.begin(); iter
!= textures
.end(); ++iter
)
77 iter
->second
.Destroy(shutdown
);
79 HiresTextures::Shutdown();
82 void TextureCache::InvalidateRange(u32 start_address
, u32 size
)
84 TexCache::iterator iter
= textures
.begin();
85 while (iter
!= textures
.end())
87 int rangePosition
= iter
->second
.IntersectsMemoryRange(start_address
, size
);
88 if (rangePosition
== 0)
90 iter
->second
.Destroy(false);
91 textures
.erase(iter
++);
100 void TextureCache::MakeRangeDynamic(u32 start_address
, u32 size
)
102 TexCache::iterator iter
= textures
.begin();
103 while (iter
!= textures
.end())
105 int rangePosition
= iter
->second
.IntersectsMemoryRange(start_address
, size
);
106 if ( rangePosition
== 0)
108 iter
->second
.hash
= 0;
114 int TextureCache::TCacheEntry::IntersectsMemoryRange(u32 range_address
, u32 range_size
)
116 if (addr
+ size_in_bytes
< range_address
)
118 if (addr
>= range_address
+ range_size
)
123 void TextureCache::Shutdown()
126 FreeMemoryPages(temp
, TEMP_SIZE
);
130 void TextureCache::Cleanup()
132 TexCache::iterator iter
= textures
.begin();
133 while (iter
!= textures
.end())
135 if (frameCount
> TEXTURE_KILL_THRESHOLD
+ iter
->second
.frameCount
)
137 iter
->second
.Destroy(false);
138 iter
= textures
.erase(iter
);
147 TextureCache::TCacheEntry
*TextureCache::Load(int stage
, u32 address
, int width
, int height
, int tex_format
, int tlutaddr
, int tlutfmt
,bool UseNativeMips
, int maxlevel
)
152 u8
*ptr
= g_VideoInitialize
.pGetMemoryPointer(address
);
153 int bsw
= TexDecoder_GetBlockWidthInTexels(tex_format
) - 1; //TexelSizeInNibbles(format)*width*height/16;
154 int bsh
= TexDecoder_GetBlockHeightInTexels(tex_format
) - 1; //TexelSizeInNibbles(format)*width*height/16;
155 int bsdepth
= TexDecoder_GetTexelSizeInNibbles(tex_format
);
156 int expandedWidth
= (width
+ bsw
) & (~bsw
);
157 int expandedHeight
= (height
+ bsh
) & (~bsh
);
162 u32 FullFormat
= tex_format
;
163 bool TextureIsDinamic
= false;
164 if ((tex_format
== GX_TF_C4
) || (tex_format
== GX_TF_C8
) || (tex_format
== GX_TF_C14X2
))
165 u32 FullFormat
= (tex_format
| (tlutfmt
<< 16));
167 if (g_ActiveConfig
.bSafeTextureCache
|| g_ActiveConfig
.bHiresTextures
|| g_ActiveConfig
.bDumpTextures
)
169 texHash
= TexDecoder_GetHash64(ptr
,TexDecoder_GetTextureSizeInBytes(expandedWidth
, expandedHeight
, tex_format
),g_ActiveConfig
.iSafeTextureCache_ColorSamples
);
170 if ((tex_format
== GX_TF_C4
) || (tex_format
== GX_TF_C8
) || (tex_format
== GX_TF_C14X2
))
172 // WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up)
173 // tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower.
174 // This trick (to change the texID depending on the TLUT addr) is a trick to get around
175 // an issue with metroid prime's fonts, where it has multiple sets of fonts on top of
176 // each other stored in a single texture, and uses the palette to make different characters
177 // visible or invisible. Thus, unless we want to recreate the textures for every drawn character,
178 // we must make sure that texture with different tluts get different IDs.
179 u64 tlutHash
= TexDecoder_GetHash64(&texMem
[tlutaddr
], TexDecoder_GetPaletteSize(tex_format
),g_ActiveConfig
.iSafeTextureCache_ColorSamples
);
181 if (g_ActiveConfig
.bSafeTextureCache
)
183 texID
= texID
^ ((u32
)(tlutHash
& 0xFFFFFFFF)) ^ ((u32
)((tlutHash
>> 32) & 0xFFFFFFFF));
186 if (g_ActiveConfig
.bSafeTextureCache
)
187 hash_value
= texHash
;
190 bool skip_texture_create
= false;
191 TexCache::iterator iter
= textures
.find(texID
);
193 if (iter
!= textures
.end())
195 TCacheEntry
&entry
= iter
->second
;
197 if (!g_ActiveConfig
.bSafeTextureCache
)
199 if(entry
.isRenderTarget
|| entry
.isDinamic
)
201 if(!g_ActiveConfig
.bCopyEFBToTexture
&& g_ActiveConfig
.bVerifyTextureModificationsByCPU
)
203 hash_value
= TexDecoder_GetHash64(ptr
,TexDecoder_GetTextureSizeInBytes(expandedWidth
, expandedHeight
, tex_format
),g_ActiveConfig
.iSafeTextureCache_ColorSamples
);
204 if ((tex_format
== GX_TF_C4
) || (tex_format
== GX_TF_C8
) || (tex_format
== GX_TF_C14X2
))
206 hash_value
^= TexDecoder_GetHash64(&texMem
[tlutaddr
], TexDecoder_GetPaletteSize(tex_format
),g_ActiveConfig
.iSafeTextureCache_ColorSamples
);
216 hash_value
= ((u32
*)ptr
)[0];
221 if(entry
.isRenderTarget
|| entry
.isDinamic
)
223 if(g_ActiveConfig
.bCopyEFBToTexture
|| !g_ActiveConfig
.bVerifyTextureModificationsByCPU
)
229 if (((entry
.isRenderTarget
|| entry
.isDinamic
) && hash_value
== entry
.hash
&& address
== entry
.addr
)
230 || ((address
== entry
.addr
) && (hash_value
== entry
.hash
) && FullFormat
== entry
.fmt
/* && entry.MipLevels == maxlevel*/))
232 entry
.frameCount
= frameCount
;
233 entry
.isDinamic
= false;
234 D3D::SetTexture(stage
, entry
.texture
);
239 // Let's reload the new texture data into the same texture,
240 // instead of destroying it and having to create a new one.
241 // Might speed up movie playback very, very slightly.
242 TextureIsDinamic
= (entry
.isRenderTarget
|| entry
.isDinamic
) && !g_ActiveConfig
.bCopyEFBToTexture
;
244 if (!entry
.isRenderTarget
&&
245 ((!entry
.isDinamic
&& width
== entry
.w
&& height
==entry
.h
&& FullFormat
== entry
.fmt
/* && entry.MipLevels < maxlevel*/)
246 || (entry
.isDinamic
&& entry
.w
== width
&& entry
.h
== height
)))
248 skip_texture_create
= true;
252 entry
.Destroy(false);
253 textures
.erase(iter
);
258 // Make an entry in the table
259 TCacheEntry
& entry
= textures
[texID
];
260 entry
.isDinamic
= TextureIsDinamic
;
261 PC_TexFormat pcfmt
= PC_TEX_FMT_NONE
;
263 if (g_ActiveConfig
.bHiresTextures
)
265 // Load Custom textures
266 char texPathTemp
[MAX_PATH
];
267 int oldWidth
= width
;
268 int oldHeight
= height
;
270 sprintf(texPathTemp
, "%s_%08x_%i", globals
->unique_id
, texHash
, tex_format
);
271 pcfmt
= HiresTextures::GetHiresTex(texPathTemp
, &width
, &height
, tex_format
, temp
);
273 if (pcfmt
!= PC_TEX_FMT_NONE
)
275 expandedWidth
= width
;
276 expandedHeight
= height
;
277 entry
.scaleX
= (float) width
/ oldWidth
;
278 entry
.scaleY
= (float) height
/ oldHeight
;
282 if (pcfmt
== PC_TEX_FMT_NONE
)
283 pcfmt
= TexDecoder_Decode(temp
, ptr
, expandedWidth
, expandedHeight
, tex_format
, tlutaddr
, tlutfmt
);
286 bool swap_r_b
= false;
288 case PC_TEX_FMT_BGRA32
:
289 d3d_fmt
= D3DFMT_A8R8G8B8
;
291 case PC_TEX_FMT_RGBA32
:
292 d3d_fmt
= D3DFMT_A8R8G8B8
;
295 case PC_TEX_FMT_RGB565
:
296 d3d_fmt
= D3DFMT_R5G6B5
;
298 case PC_TEX_FMT_IA4_AS_IA8
:
299 d3d_fmt
= D3DFMT_A8L8
;
302 case PC_TEX_FMT_I4_AS_I8
:
303 d3d_fmt
= D3DFMT_A8P8
; // A hack which means the format is a packed
304 // 8-bit intensity texture. It is unpacked
305 // to A8L8 in D3DTexture.cpp
308 d3d_fmt
= D3DFMT_A8L8
;
310 case PC_TEX_FMT_DXT1
:
311 d3d_fmt
= D3DFMT_DXT1
;
315 entry
.oldpixel
= ((u32
*)ptr
)[0];
316 if (g_ActiveConfig
.bSafeTextureCache
|| entry
.isDinamic
)
317 entry
.hash
= hash_value
;
320 entry
.hash
= (u32
)(((double)rand() / RAND_MAX
) * 0xFFFFFFFF);
321 ((u32
*)ptr
)[0] = entry
.hash
;
324 entry
.addr
= address
;
325 entry
.size_in_bytes
= TexDecoder_GetTextureSizeInBytes(expandedWidth
, expandedHeight
, tex_format
);
326 entry
.isRenderTarget
= false;
327 bool isPow2
= !((width
& (width
- 1)) || (height
& (height
- 1)));
328 entry
.isNonPow2
= false;
329 int TexLevels
= (width
> height
)?width
:height
;
330 TexLevels
= (isPow2
&& UseNativeMips
&& (maxlevel
> 0)) ? (int)(log((double)TexLevels
)/log((double)2)) + 1 : ((isPow2
)? 0 : 1);
331 if(TexLevels
> (maxlevel
+ 1) && maxlevel
> 0)
332 TexLevels
= (maxlevel
+ 1);
333 entry
.MipLevels
= maxlevel
;
334 if (!skip_texture_create
)
336 entry
.texture
= D3D::CreateTexture2D((BYTE
*)temp
, width
, height
, expandedWidth
, d3d_fmt
, swap_r_b
, TexLevels
);
340 D3D::ReplaceTexture2D(entry
.texture
, (BYTE
*)temp
, width
, height
, expandedWidth
, d3d_fmt
, swap_r_b
, 0);
342 if(TexLevels
> 1 && pcfmt
!= PC_TEX_FMT_NONE
)
345 int mipWidth
= (width
+ 1) >> 1;
346 int mipHeight
= (height
+ 1) >> 1;
347 ptr
+= entry
.size_in_bytes
;
348 while((mipHeight
|| mipWidth
) && (level
< TexLevels
))
350 u32 currentWidth
= (mipWidth
> 0)? mipWidth
: 1;
351 u32 currentHeight
= (mipHeight
> 0)? mipHeight
: 1;
352 expandedWidth
= (currentWidth
+ bsw
) & (~bsw
);
353 expandedHeight
= (currentHeight
+ bsh
) & (~bsh
);
354 TexDecoder_Decode(temp
, ptr
, expandedWidth
, expandedHeight
, tex_format
, tlutaddr
, tlutfmt
);
355 D3D::ReplaceTexture2D(entry
.texture
, (BYTE
*)temp
, currentWidth
, currentHeight
, expandedWidth
, d3d_fmt
, swap_r_b
,level
);
356 u32 size
= (max(mipWidth
, bsw
) * max(mipHeight
, bsh
) * bsdepth
) >> 1;
363 entry
.frameCount
= frameCount
;
366 entry
.Scaledw
= width
;
367 entry
.Scaledh
= height
;
368 entry
.fmt
= FullFormat
;
370 if (g_ActiveConfig
.bDumpTextures
)
372 // dump texture to file
373 char szTemp
[MAX_PATH
];
374 char szDir
[MAX_PATH
];
375 const char* uniqueId
= globals
->unique_id
;
376 bool bCheckedDumpDir
= false;
378 sprintf(szDir
, "%s%s", File::GetUserPath(D_DUMPTEXTURES_IDX
), uniqueId
);
380 if (!bCheckedDumpDir
)
382 if (!File::Exists(szDir
) || !File::IsDirectory(szDir
))
383 File::CreateDir(szDir
);
385 bCheckedDumpDir
= true;
388 sprintf(szTemp
, "%s/%s_%08x_%i.png", szDir
, uniqueId
, texHash
, tex_format
);
390 if (!File::Exists(szTemp
))
391 PD3DXSaveTextureToFileA(szTemp
,D3DXIFF_PNG
,entry
.texture
,0);
394 INCSTAT(stats
.numTexturesCreated
);
395 SETSTAT(stats
.numTexturesAlive
, (int)textures
.size());
398 D3D::SetTexture(stage
, entry
.texture
);
400 DEBUGGER_PAUSE_LOG_AT(NEXT_NEW_TEXTURE
,true,{printf("A new texture (%d x %d) is loaded", width
, height
);});
404 void TextureCache::CopyRenderTargetToTexture(u32 address
, bool bFromZBuffer
, bool bIsIntensityFmt
, u32 copyfmt
, int bScaleByHalf
, const EFBRectangle
&source_rect
)
406 int efb_w
= source_rect
.GetWidth();
407 int efb_h
= source_rect
.GetHeight();
409 int tex_w
= (abs(source_rect
.GetWidth()) >> bScaleByHalf
);
410 int tex_h
= (abs(source_rect
.GetHeight()) >> bScaleByHalf
);
411 //compensate the texture grow if supersampling is enabled to conserve memory usage
412 float SuperSampleCompensation
= g_ActiveConfig
.iMultisampleMode
+ 1;
413 SuperSampleCompensation
= 1.0f
/ SuperSampleCompensation
;
414 float xScale
= Renderer::GetTargetScaleX();
415 float yScale
= Renderer::GetTargetScaleY();
417 int Scaledtex_w
= (g_ActiveConfig
.bCopyEFBScaled
)?((int)(xScale
* SuperSampleCompensation
* tex_w
)):tex_w
;
418 int Scaledtex_h
= (g_ActiveConfig
.bCopyEFBScaled
)?((int)(yScale
* SuperSampleCompensation
* tex_h
)):tex_h
;
420 TexCache::iterator iter
;
421 LPDIRECT3DTEXTURE9 tex
= NULL
;
422 iter
= textures
.find(address
);
423 bool TextureIsDinamic
= false;
424 if (iter
!= textures
.end())
426 if ((iter
->second
.isRenderTarget
&& iter
->second
.Scaledw
== Scaledtex_w
&& iter
->second
.Scaledh
== Scaledtex_h
)
427 || (iter
->second
.isDinamic
&& iter
->second
.w
== tex_w
&& iter
->second
.h
== tex_h
))
429 tex
= iter
->second
.texture
;
430 TextureIsDinamic
= iter
->second
.isDinamic
;
431 iter
->second
.frameCount
= frameCount
;
435 // Remove it and recreate it as a render target
436 if(iter
->second
.texture
)
437 iter
->second
.texture
->Release();
438 iter
->second
.texture
= 0;
439 textures
.erase(iter
);
450 entry
.addr
= address
;
451 entry
.isRenderTarget
= true;
453 entry
.frameCount
= frameCount
;
456 entry
.Scaledw
= Scaledtex_w
;
457 entry
.Scaledh
= Scaledtex_h
;
459 entry
.isNonPow2
= true;
460 entry
.isDinamic
= false;
461 D3D::dev
->CreateTexture(Scaledtex_w
, Scaledtex_h
, 1, D3DUSAGE_RENDERTARGET
, D3DFMT_A8R8G8B8
, D3DPOOL_DEFAULT
, &entry
.texture
, 0);
462 textures
[address
] = entry
;
466 // Make sure to resolve anything we need to read from.
467 LPDIRECT3DTEXTURE9 read_texture
= bFromZBuffer
? FBManager
.GetEFBDepthTexture(source_rect
) : FBManager
.GetEFBColorTexture(source_rect
);
469 // We have to run a pixel shader, for color conversion.
470 Renderer::ResetAPIState(); // reset any game specific settings
471 if(!TextureIsDinamic
|| g_ActiveConfig
.bCopyEFBToTexture
)
474 float colmat
[16]= {0.0f
};
475 float fConstAdd
[4] = {0.0f
};
483 colmat
[0] = colmat
[4] = colmat
[8] = colmat
[12] = 1.0f
;
486 colmat
[1] = colmat
[5] = colmat
[9] = colmat
[12] = 1.0f
;
487 case 11: // Z16 (reverse order)
488 colmat
[0] = colmat
[4] = colmat
[8] = colmat
[13] = 1.0f
;
491 colmat
[0] = colmat
[5] = colmat
[10] = 1.0f
;
494 colmat
[1] = colmat
[5] = colmat
[9] = colmat
[13] = 1.0f
;
497 colmat
[2] = colmat
[6] = colmat
[10] = colmat
[14] = 1.0f
;
500 colmat
[2] = colmat
[6] = colmat
[10] = colmat
[13] = 1.0f
;
503 ERROR_LOG(VIDEO
, "Unknown copy zbuf format: 0x%x", copyfmt
);
504 colmat
[2] = colmat
[5] = colmat
[8] = 1.0f
;
508 else if (bIsIntensityFmt
)
510 fConstAdd
[0] = fConstAdd
[1] = fConstAdd
[2] = 16.0f
/255.0f
;
517 colmat
[0] = 0.257f
; colmat
[1] = 0.504f
; colmat
[2] = 0.098f
;
518 colmat
[4] = 0.257f
; colmat
[5] = 0.504f
; colmat
[6] = 0.098f
;
519 colmat
[8] = 0.257f
; colmat
[9] = 0.504f
; colmat
[10] = 0.098f
;
523 fConstAdd
[3] = 16.0f
/ 255.0f
;
524 colmat
[12] = 0.257f
; colmat
[13] = 0.504f
; colmat
[14] = 0.098f
;
531 ERROR_LOG(VIDEO
, "Unknown copy intensity format: 0x%x", copyfmt
);
532 colmat
[0] = colmat
[5] = colmat
[10] = colmat
[15] = 1;
542 colmat
[0] = colmat
[4] = colmat
[8] = colmat
[12] = 1;
546 colmat
[0] = colmat
[4] = colmat
[8] = colmat
[15] = 1;
550 colmat
[3] = colmat
[7] = colmat
[11] = colmat
[15] = 1;
553 colmat
[1] = colmat
[5] = colmat
[9] = colmat
[13] = 1;
556 colmat
[2] = colmat
[6] = colmat
[10] = colmat
[14] = 1;
559 colmat
[0] = colmat
[4] = colmat
[8] = colmat
[13] = 1;
562 colmat
[1] = colmat
[5] = colmat
[9] = colmat
[14] = 1;
566 colmat
[0] = colmat
[5] = colmat
[10] = 1;
567 fConstAdd
[3] = 1; // set alpha to 1
571 colmat
[0] = colmat
[5] = colmat
[10] = colmat
[15] = 1;
575 ERROR_LOG(VIDEO
, "Unknown copy color format: 0x%x", copyfmt
);
576 colmat
[0] = colmat
[5] = colmat
[10] = colmat
[15] = 1;
581 LPDIRECT3DSURFACE9 Rendersurf
= NULL
;
582 tex
->GetSurfaceLevel(0,&Rendersurf
);
583 D3D::dev
->SetDepthStencilSurface(NULL
);
584 D3D::dev
->SetRenderTarget(0, Rendersurf
);
588 // Stretch picture with increased internal resolution
591 vp
.Width
= Scaledtex_w
;
592 vp
.Height
= Scaledtex_h
;
595 D3D::dev
->SetViewport(&vp
);
597 destrect
.bottom
= Scaledtex_h
;
599 destrect
.right
= Scaledtex_w
;
603 PixelShaderManager::SetColorMatrix(colmat
, fConstAdd
); // set transformation
604 TargetRectangle targetSource
= Renderer::ConvertEFBRectangle(source_rect
);
606 sourcerect
.bottom
= targetSource
.bottom
;
607 sourcerect
.left
= targetSource
.left
;
608 sourcerect
.right
= targetSource
.right
;
609 sourcerect
.top
= targetSource
.top
;
614 if(bScaleByHalf
|| g_ActiveConfig
.iMultisampleMode
)
616 D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER
, D3DTEXF_LINEAR
);
617 D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER
, D3DTEXF_LINEAR
);
621 D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER
, D3DTEXF_POINT
);
622 D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER
, D3DTEXF_POINT
);
627 D3D::ChangeSamplerState(0, D3DSAMP_MINFILTER
, D3DTEXF_LINEAR
);
628 D3D::ChangeSamplerState(0, D3DSAMP_MAGFILTER
, D3DTEXF_LINEAR
);
632 D3DFORMAT bformat
= FBManager
.GetEFBDepthRTSurfaceFormat();
633 int SSAAMode
= g_ActiveConfig
.iMultisampleMode
;
634 D3D::drawShadedTexQuad(
637 Renderer::GetFullTargetWidth() ,
638 Renderer::GetFullTargetHeight(),
641 ((bformat
!= FOURCC_RAWZ
&& bformat
!= D3DFMT_D24X8
) && bFromZBuffer
)? PixelShaderCache::GetDepthMatrixProgram(SSAAMode
): PixelShaderCache::GetColorMatrixProgram(SSAAMode
),
642 VertexShaderCache::GetSimpleVertexShader(SSAAMode
));
643 Rendersurf
->Release();
646 if(!g_ActiveConfig
.bCopyEFBToTexture
)
648 textures
[address
].hash
= TextureConverter::EncodeToRamFromTexture(
651 Renderer::GetFullTargetWidth(),
652 Renderer::GetFullTargetHeight(),
655 (float)((Renderer::GetFullTargetWidth() - Renderer::GetTargetWidth()) / 2),
656 (float)((Renderer::GetFullTargetHeight() - Renderer::GetTargetHeight()) / 2) ,
664 D3D::RefreshSamplerState(0, D3DSAMP_MINFILTER
);
665 D3D::RefreshSamplerState(0, D3DSAMP_MAGFILTER
);
666 D3D::SetTexture(0,NULL
);
667 D3D::dev
->SetRenderTarget(0, FBManager
.GetEFBColorRTSurface());
668 D3D::dev
->SetDepthStencilSurface(FBManager
.GetEFBDepthRTSurface());
669 Renderer::RestoreAPIState();