fixed mipmap loading in both plugins.
[dolphin.git] / Source / Plugins / Plugin_VideoOGL / Src / TextureMngr.cpp
blobea196cbd57422177bed6fb56db3192cac8aab34c
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 <vector>
19 #include <cmath>
21 #include "Globals.h"
22 #include "CommonPaths.h"
23 #include "StringUtil.h"
24 #include <fstream>
25 #ifdef _WIN32
26 #define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set
27 #define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset
28 #define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64
29 #define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64
30 #include <intrin.h>
31 #undef _interlockedbittestandset
32 #undef _interlockedbittestandreset
33 #undef _interlockedbittestandset64
34 #undef _interlockedbittestandreset64
35 #endif
37 #include "VideoConfig.h"
38 #include "Hash.h"
39 #include "Statistics.h"
40 #include "Profiler.h"
41 #include "ImageWrite.h"
43 #include "Render.h"
45 #include "MemoryUtil.h"
46 #include "BPStructs.h"
47 #include "TextureDecoder.h"
48 #include "TextureMngr.h"
49 #include "PixelShaderCache.h"
50 #include "PixelShaderManager.h"
51 #include "VertexShaderManager.h"
52 #include "FramebufferManager.h"
53 #include "FileUtil.h"
54 #include "HiresTextures.h"
56 u8 *TextureMngr::temp = NULL;
57 TextureMngr::TexCache TextureMngr::textures;
59 extern int frameCount;
60 static u32 s_TempFramebuffer = 0;
62 #define TEMP_SIZE (1024*1024*4)
63 #define TEXTURE_KILL_THRESHOLD 200
65 static const GLint c_MinLinearFilter[8] = {
66 GL_NEAREST,
67 GL_NEAREST_MIPMAP_NEAREST,
68 GL_NEAREST_MIPMAP_LINEAR,
69 GL_NEAREST,
70 GL_LINEAR,
71 GL_LINEAR_MIPMAP_NEAREST,
72 GL_LINEAR_MIPMAP_LINEAR,
73 GL_LINEAR,
76 static const GLint c_WrapSettings[4] = {
77 GL_CLAMP_TO_EDGE,
78 GL_REPEAT,
79 GL_MIRRORED_REPEAT,
80 GL_REPEAT,
83 bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int height)
85 std::vector<u32> data(width * height);
86 glBindTexture(textarget, tex);
87 glGetTexImage(textarget, 0, GL_BGRA, GL_UNSIGNED_BYTE, &data[0]);
88 GLenum err = GL_REPORT_ERROR();
89 if (err != GL_NO_ERROR)
91 PanicAlert("Can't save texture, GL Error: %s", gluErrorString(err));
92 return false;
95 return SaveTGA(filename, width, height, &data[0]);
98 bool TextureMngr::TCacheEntry::IntersectsMemoryRange(u32 range_address, u32 range_size)
100 if (addr + size_in_bytes < range_address)
101 return false;
102 if (addr >= range_address + range_size)
103 return false;
104 return true;
107 void TextureMngr::TCacheEntry::SetTextureParameters(TexMode0 &newmode,TexMode1 &newmode1)
109 mode = newmode;
110 mode1 = newmode1;
111 if (isRectangle)
113 // very limited!
114 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
115 (newmode.mag_filter || g_ActiveConfig.bForceFiltering) ? GL_LINEAR : GL_NEAREST);
116 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
117 (g_ActiveConfig.bForceFiltering || newmode.min_filter >= 4) ? GL_LINEAR : GL_NEAREST);
119 if (newmode.wrap_s == 2 || newmode.wrap_t == 2)
120 DEBUG_LOG(VIDEO, "cannot support mirrorred repeat mode");
122 if (newmode.wrap_s == 1 || newmode.wrap_t == 1)
123 DEBUG_LOG(VIDEO, "cannot support repeat mode");
125 else
127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
128 (newmode.mag_filter || g_Config.bForceFiltering) ? GL_LINEAR : GL_NEAREST);
130 if (bHaveMipMaps)
132 if (g_ActiveConfig.bForceFiltering && newmode.min_filter < 4)
133 mode.min_filter += 4; // take equivalent forced linear
134 int filt = newmode.min_filter;
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c_MinLinearFilter[filt & (((newmode1.max_lod >> 4) > 0)?7:4)]);
136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, newmode1.min_lod >> 4);
137 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, newmode1.max_lod >> 4);
138 //glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, (newmode.lod_bias/2.0f));
141 else
142 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
143 (g_ActiveConfig.bForceFiltering || newmode.min_filter >= 4) ? GL_LINEAR : GL_NEAREST);
144 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c_WrapSettings[newmode.wrap_s]);
145 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c_WrapSettings[newmode.wrap_t]);
148 if (g_Config.iMaxAnisotropy >= 1)
149 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)(1 << g_ActiveConfig.iMaxAnisotropy));
152 void TextureMngr::TCacheEntry::Destroy(bool shutdown)
154 if (!texture)
155 return;
156 glDeleteTextures(1, &texture);
157 if (!isRenderTarget && !shutdown && !g_ActiveConfig.bSafeTextureCache) {
158 u32 *ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr);
159 if (ptr && *ptr == hash)
160 *ptr = oldpixel;
162 texture = 0;
165 void TextureMngr::Init()
167 temp = (u8*)AllocateMemoryPages(TEMP_SIZE);
168 TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter);
169 HiresTextures::Init(globals->unique_id);
172 void TextureMngr::Invalidate(bool shutdown)
174 for (TexCache::iterator iter = textures.begin(); iter != textures.end(); ++iter)
175 iter->second.Destroy(shutdown);
176 textures.clear();
177 HiresTextures::Shutdown();
180 void TextureMngr::Shutdown()
182 Invalidate(true);
184 if (s_TempFramebuffer) {
185 glDeleteFramebuffersEXT(1, (GLuint *)&s_TempFramebuffer);
186 s_TempFramebuffer = 0;
189 FreeMemoryPages(temp, TEMP_SIZE);
190 temp = NULL;
193 void TextureMngr::ProgressiveCleanup()
195 TexCache::iterator iter = textures.begin();
196 while (iter != textures.end())
198 if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second.frameCount)
200 if (!iter->second.isRenderTarget) {
201 iter->second.Destroy(false);
202 ERASE_THROUGH_ITERATOR(textures, iter);
204 else {
205 iter->second.Destroy(false);
206 ERASE_THROUGH_ITERATOR(textures, iter);
209 else
210 iter++;
214 void TextureMngr::InvalidateRange(u32 start_address, u32 size)
216 TexCache::iterator iter = textures.begin();
217 while (iter != textures.end())
219 if (iter->second.IntersectsMemoryRange(start_address, size))
221 iter->second.Destroy(false);
222 ERASE_THROUGH_ITERATOR(textures, iter);
224 else {
225 ++iter;
230 TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width, int height, u32 tex_format, int tlutaddr, int tlutfmt)
232 // notes (about "UNsafe texture cache"):
233 // Have to be removed soon.
234 // But we keep it until the "safe" way became rock solid
235 // pros: it has an unique ID held by the texture data itself (@address) once cached.
236 // cons: it writes this unique ID in the gc RAM <- very dangerous (break MP1) and ugly
238 // notes (about "safe texture cache"):
239 // Metroids text issue (character table):
240 // Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs).
241 // That's why we have to hash the TLUT too for TLUT tex_format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2).
242 // And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but
243 // have to be a real unique ID.
244 // DONE but not satifiying yet -> may break copyEFBToTexture sometimes.
246 // Pokemon Colosseum text issue (plain text):
247 // Use a GX_TF_I4 512x512 text-flush-texture at a const address.
248 // The problem here was just the sparse hash on the texture. This texture is partly overwrited (what is needed only)
249 // so lot's of remaning old text. Thin white chars on black bg too.
251 // TODO: - clean this up when ready to kill old "unsafe texture cache"
252 // - fix the key index situation with CopyRenderTargetToTexture.
253 // Could happen only for GX_TF_C4, GX_TF_C8 and GX_TF_C14X2 fmt.
254 // Wonder if we can't use tex width&height to know if EFB might be copied to it...
255 // raw idea: TOCHECK if addresses are aligned we have few bits left...
257 if (address == 0)
258 return NULL;
260 TexMode0 &tm0 = bpmem.tex[texstage >> 2].texMode0[texstage & 3];
261 TexMode1 &tm1 = bpmem.tex[texstage >> 2].texMode1[texstage & 3];
263 bool UseNativeMips = (tm0.min_filter & 3) && (tm0.min_filter != 8);
264 int maxlevel = ((tm1.max_lod >> 4));
266 u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address);
267 int bsw = TexDecoder_GetBlockWidthInTexels(tex_format) - 1;
268 int bsh = TexDecoder_GetBlockHeightInTexels(tex_format) - 1;
269 int bsdepth = TexDecoder_GetTexelSizeInNibbles(tex_format);
270 int expandedWidth = (width + bsw) & (~bsw);
271 int expandedHeight = (height + bsh) & (~bsh);
273 u64 hash_value = 0;
274 u32 texID = address;
275 u64 texHash = 0;
276 u32 FullFormat = tex_format;
277 if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
278 FullFormat = (tex_format | (tlutfmt << 16));
279 if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures)
281 texHash = TexDecoder_GetHash64(ptr,TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
282 if ((tex_format == GX_TF_C4) || (tex_format == GX_TF_C8) || (tex_format == GX_TF_C14X2))
284 // WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up)
285 // tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower.
286 // This trick (to change the texID depending on the TLUT addr) is a trick to get around
287 // an issue with metroid prime's fonts, where it has multiple sets of fonts on top of
288 // each other stored in a single texture, and uses the palette to make different characters
289 // visible or invisible. Thus, unless we want to recreate the textures for every drawn character,
290 // we must make sure that texture with different tluts get different IDs.
291 u64 tlutHash = TexDecoder_GetHash64(&texMem[tlutaddr], TexDecoder_GetPaletteSize(tex_format),g_ActiveConfig.iSafeTextureCache_ColorSamples);
292 texHash ^= tlutHash;
293 if (g_ActiveConfig.bSafeTextureCache)
295 texID = texID ^ ((u32)(tlutHash & 0xFFFFFFFF)) ^ ((u32)((tlutHash >> 32) & 0xFFFFFFFF));
298 if (g_ActiveConfig.bSafeTextureCache)
299 hash_value = texHash;
302 bool skip_texture_create = false;
303 TexCache::iterator iter = textures.find(texID);
305 if (iter != textures.end())
307 TCacheEntry &entry = iter->second;
309 if (!g_ActiveConfig.bSafeTextureCache)
310 hash_value = ((u32 *)ptr)[0];
312 if (entry.isRenderTarget || ((address == entry.addr) && (hash_value == entry.hash) && (int) FullFormat == entry.fmt))
314 entry.frameCount = frameCount;
315 glEnable(entry.isRectangle ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D);
316 // entry.isRectangle ? TextureMngr::EnableTex2D(texstage) : TextureMngr::EnableTexRECT(texstage);
317 glBindTexture(entry.isRectangle ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D, entry.texture);
318 GL_REPORT_ERRORD();
319 if (entry.mode.hex != tm0.hex || entry.mode1.hex != tm1.hex)
320 entry.SetTextureParameters(tm0,tm1);
321 //DebugLog("%cC addr: %08x | fmt: %i | e.hash: %08x | w:%04i h:%04i", g_ActiveConfig.bSafeTextureCache ? 'S' : 'U'
322 // , address, tex_format, entry.hash, width, height);
323 return &entry;
325 else
327 // Let's reload the new texture data into the same texture,
328 // instead of destroying it and having to create a new one.
329 // Might speed up movie playback very, very slightly.
330 if (width == entry.w && height == entry.h && (int) FullFormat == entry.fmt)
332 glBindTexture(entry.isRectangle ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D, entry.texture);
333 GL_REPORT_ERRORD();
334 if (entry.mode.hex != tm0.hex || entry.mode1.hex != tm1.hex)
335 entry.SetTextureParameters(tm0,tm1);
336 skip_texture_create = true;
338 else
340 entry.Destroy(false);
341 textures.erase(iter);
346 //Make an entry in the table
347 TCacheEntry& entry = textures[texID];
348 PC_TexFormat dfmt = PC_TEX_FMT_NONE;
350 if (g_ActiveConfig.bHiresTextures)
352 //Load Custom textures
353 char texPathTemp[MAX_PATH];
354 int oldWidth = width;
355 int oldHeight = height;
357 sprintf(texPathTemp, "%s_%08x_%i", globals->unique_id, (unsigned int) texHash, tex_format);
358 dfmt = HiresTextures::GetHiresTex(texPathTemp, &width, &height, tex_format, temp);
360 if (dfmt != PC_TEX_FMT_NONE)
362 expandedWidth = width;
363 expandedHeight = height;
364 entry.scaleX = (float) width / oldWidth;
365 entry.scaleY = (float) height / oldHeight;
369 if (dfmt == PC_TEX_FMT_NONE)
370 dfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt);
372 entry.oldpixel = ((u32 *)ptr)[0];
374 if (g_ActiveConfig.bSafeTextureCache)
375 entry.hash = hash_value;
376 else
378 entry.hash = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF);
379 ((u32 *)ptr)[0] = entry.hash;
382 entry.addr = address;
383 entry.size_in_bytes = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, tex_format);
384 entry.isRenderTarget = false;
386 // For static textures, we use NPOT.
387 entry.isRectangle = false;
388 // old code: entry.isRectangle = ((width & (width - 1)) || (height & (height - 1)));
390 GLenum target = entry.isRectangle ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D;
391 if (!skip_texture_create) {
392 glGenTextures(1, (GLuint *)&entry.texture);
393 glBindTexture(target, entry.texture);
396 bool isPow2 = !((width & (width - 1)) || (height & (height - 1)));
397 int TexLevels = (width > height)?width:height;
398 TexLevels = (isPow2 && UseNativeMips && (maxlevel > 0)) ? (int)(log((double)TexLevels)/log((double)2)) + 1 : 1;
399 if(TexLevels > maxlevel && maxlevel > 0)
400 TexLevels = maxlevel;
401 int gl_format = 0;
402 int gl_iformat = 0;
403 int gl_type = 0;
404 entry.bHaveMipMaps = UseNativeMips;
405 if (dfmt != PC_TEX_FMT_DXT1)
407 switch (dfmt)
409 default:
410 case PC_TEX_FMT_NONE:
411 PanicAlert("Invalid PC texture format %i", dfmt);
412 case PC_TEX_FMT_BGRA32:
413 gl_format = GL_BGRA;
414 gl_iformat = 4;
415 gl_type = GL_UNSIGNED_BYTE;
416 break;
417 case PC_TEX_FMT_RGBA32:
418 gl_format = GL_RGBA;
419 gl_iformat = 4;
420 gl_type = GL_UNSIGNED_BYTE;
421 break;
422 case PC_TEX_FMT_I4_AS_I8:
423 gl_format = GL_LUMINANCE;
424 gl_iformat = GL_INTENSITY4;
425 gl_type = GL_UNSIGNED_BYTE;
426 break;
427 case PC_TEX_FMT_IA4_AS_IA8:
428 gl_format = GL_LUMINANCE_ALPHA;
429 gl_iformat = GL_LUMINANCE4_ALPHA4;
430 gl_type = GL_UNSIGNED_BYTE;
431 break;
432 case PC_TEX_FMT_I8:
433 gl_format = GL_LUMINANCE;
434 gl_iformat = GL_INTENSITY8;
435 gl_type = GL_UNSIGNED_BYTE;
436 break;
437 case PC_TEX_FMT_IA8:
438 gl_format = GL_LUMINANCE_ALPHA;
439 gl_iformat = GL_LUMINANCE8_ALPHA8;
440 gl_type = GL_UNSIGNED_BYTE;
441 break;
442 case PC_TEX_FMT_RGB565:
443 gl_format = GL_RGB;
444 gl_iformat = GL_RGB;
445 gl_type = GL_UNSIGNED_SHORT_5_6_5;
446 break;
448 if (expandedWidth != width)
449 glPixelStorei(GL_UNPACK_ROW_LENGTH, expandedWidth);
450 //generate mipmaps even if we use native mips to suport textures with less levels
451 bool GenerateMipmaps = isPow2 && UseNativeMips && (maxlevel > 0);
452 entry.bHaveMipMaps = GenerateMipmaps;
453 if(skip_texture_create)
455 glTexSubImage2D(target, 0,0,0,width, height, gl_format, gl_type, temp);
457 else
459 if (GenerateMipmaps)
461 gluBuild2DMipmaps(target, gl_iformat, width, height, gl_format, gl_type, temp);
463 else
465 glTexImage2D(target, 0, gl_iformat, width, height, 0, gl_format, gl_type, temp);
469 if (expandedWidth != width) // reset
470 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
472 else
474 if(skip_texture_create)
476 glCompressedTexSubImage2D(target, 0,0,0,width, height,
477 GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,expandedWidth*expandedHeight/2, temp);
479 else
481 glCompressedTexImage2D(target, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
482 width, height, 0, expandedWidth*expandedHeight/2, temp);
485 if(TexLevels > 1 && dfmt != PC_TEX_FMT_NONE)
487 int level = 1;
488 int mipWidth = width >> 1;
489 int mipHeight = height >> 1;
490 ptr += entry.size_in_bytes;
491 while((mipHeight || mipWidth) && (level < TexLevels))
493 u32 currentWidth = (mipWidth > 0)? mipWidth : 1;
494 u32 currentHeight = (mipHeight > 0)? mipHeight : 1;
495 expandedWidth = (currentWidth + bsw) & (~bsw);
496 expandedHeight = (currentHeight + bsh) & (~bsh);
497 TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, tex_format, tlutaddr, tlutfmt);
498 if (dfmt != PC_TEX_FMT_DXT1)
500 if (expandedWidth != (int)currentWidth)
501 glPixelStorei(GL_UNPACK_ROW_LENGTH, expandedWidth);
503 glTexSubImage2D(target, level,0,0,currentWidth, currentHeight, gl_format, gl_type, temp);
505 if (expandedWidth != (int)currentWidth)
506 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
508 else
510 if(skip_texture_create)
512 glCompressedTexSubImage2D(target, level,0,0,currentWidth, currentHeight, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,expandedWidth*expandedHeight/2, temp);
514 else
516 glCompressedTexImage2D(target, level, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, currentWidth, currentHeight, 0, expandedWidth*expandedHeight/2, temp);
519 GL_REPORT_ERRORD();
520 u32 size = (max(mipWidth, bsw) * max(mipHeight, bsh) * bsdepth) >> 1;
521 ptr += size;
522 mipWidth >>= 1;
523 mipHeight >>= 1;
524 level++;
527 entry.frameCount = frameCount;
528 entry.w = width;
529 entry.h = height;
530 entry.fmt = FullFormat;
531 entry.SetTextureParameters(tm0,tm1);
532 if (g_ActiveConfig.bDumpTextures) // dump texture to file
535 char szTemp[MAX_PATH];
536 char szDir[MAX_PATH];
537 const char* uniqueId = globals->unique_id;
538 bool bCheckedDumpDir = false;
540 sprintf(szDir,"%s%s",File::GetUserPath(D_DUMPTEXTURES_IDX), uniqueId);
542 if(!bCheckedDumpDir)
544 if (!File::Exists(szDir) || !File::IsDirectory(szDir))
545 File::CreateDir(szDir);
547 bCheckedDumpDir = true;
550 sprintf(szTemp, "%s/%s_%08x_%i.tga",szDir, uniqueId, (unsigned int) texHash, tex_format);
551 if (!File::Exists(szTemp))
553 SaveTexture(szTemp, target, entry.texture, expandedWidth, expandedHeight);
557 INCSTAT(stats.numTexturesCreated);
558 SETSTAT(stats.numTexturesAlive, textures.size());
559 return &entry;
563 void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle &source_rect)
565 DVSTARTPROFILE();
566 GL_REPORT_ERRORD();
568 // for intensity values, use Y of YUV format!
569 // for all purposes, treat 4bit equivalents as 8bit (probably just used for compression)
570 // RGBA8 - RGBA8
571 // RGB565 - RGB565
572 // RGB5A3 - RGB5A3
573 // I4,R4,Z4 - I4
574 // IA4,RA4 - IA4
575 // Z8M,G8,I8,A8,Z8,R8,B8,Z8L - I8
576 // Z16,GB8,RG8,Z16L,IA8,RA8 - IA8
577 GLenum gl_format = GL_RGBA;
578 GLenum gl_iformat = 4;
579 GLenum gl_type = GL_UNSIGNED_BYTE;
580 float colmat[16];
581 float fConstAdd[4] = {0};
582 memset(colmat, 0, sizeof(colmat));
584 if (bFromZBuffer)
586 switch(copyfmt)
588 case 0: // Z4
589 case 1: // Z8
590 colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
591 break;
592 case 3: // Z16 //?
593 colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
594 break;
595 case 11: // Z16 (reverse order)
596 colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1;
597 break;
598 case 6: // Z24X8
599 colmat[2] = colmat[5] = colmat[8] = colmat[15] = 1;
600 break;
601 case 9: // Z8M
602 colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
603 break;
604 case 10: // Z8L
605 colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
606 break;
607 case 12: // Z16L
608 colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
609 break;
610 default:
611 ERROR_LOG(VIDEO, "Unknown copy zbuf format: 0x%x", copyfmt);
612 colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
613 break;
616 else if (bIsIntensityFmt)
618 // TODO - verify these coefficients
619 fConstAdd[0] = fConstAdd[1] = fConstAdd[2] = 16.0f/255.0f;
620 colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f;
621 colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f;
622 colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f;
623 if (copyfmt < 2)
625 fConstAdd[3] = 16.0f / 255.0f;
626 colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f;
628 else// alpha
629 colmat[15] = 1;
631 else
633 switch (copyfmt)
635 case 0: // R4
636 colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
637 break;
638 case 8: // R8
639 colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
640 break;
641 case 2: // RA4
642 colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1;
643 break;
644 case 3: // RA8
645 colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1;
646 break;
647 case 7: // A8
648 colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1;
649 break;
650 case 9: // G8
651 colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
652 break;
653 case 10: // B8
654 colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
655 break;
656 case 11: // RG8
657 colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
658 break;
659 case 12: // GB8
660 colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
661 break;
662 case 4: // RGB565
663 colmat[0] = colmat[5] = colmat[10] = 1;
664 fConstAdd[3] = 1; // set alpha to 1
665 break;
666 case 5: // RGB5A3
667 colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
668 break;
669 case 6: // RGBA8
670 colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
671 break;
672 default:
673 ERROR_LOG(VIDEO, "Unknown copy color format: 0x%x", copyfmt);
674 colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
675 break;
679 bool bIsInit = textures.find(address) != textures.end();
681 PRIM_LOG("copytarg: addr=0x%x, fromz=%d, intfmt=%d, copyfmt=%d", address, (int)bFromZBuffer, (int)bIsIntensityFmt,copyfmt);
683 TCacheEntry& entry = textures[address];
684 entry.hash = 0;
685 entry.frameCount = frameCount;
687 int w = (abs(source_rect.GetWidth()) >> bScaleByHalf);
688 int h = (abs(source_rect.GetHeight()) >> bScaleByHalf);
692 GL_REPORT_ERRORD();
694 if (!bIsInit)
696 glGenTextures(1, (GLuint *)&entry.texture);
697 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, entry.texture);
698 GL_REPORT_ERRORD();
699 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, gl_iformat, w, h, 0, gl_format, gl_type, NULL);
700 GL_REPORT_ERRORD();
702 else
704 _assert_(entry.texture);
705 GL_REPORT_ERRORD();
706 if (entry.w == w && entry.h == h && entry.isRectangle && entry.fmt == (int) copyfmt)
708 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, entry.texture);
709 // for some reason mario sunshine errors here...
710 // Beyond Good and Evil does too, occasionally.
711 GL_REPORT_ERRORD();
712 } else {
713 // Delete existing texture.
714 glDeleteTextures(1,(GLuint *)&entry.texture);
715 glGenTextures(1, (GLuint *)&entry.texture);
716 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, entry.texture);
717 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, gl_iformat, w, h, 0, gl_format, gl_type, NULL);
718 GL_REPORT_ERRORD();
722 if (!bIsInit || !entry.isRenderTarget)
724 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
725 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
727 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
728 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
729 if (glGetError() != GL_NO_ERROR) {
730 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
731 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
732 GL_REPORT_ERRORD();
736 entry.w = w;
737 entry.h = h;
738 entry.isRectangle = true;
739 entry.isRenderTarget = true;
740 entry.fmt = copyfmt;
742 // Make sure to resolve anything we need to read from.
743 GLuint read_texture = bFromZBuffer ? g_framebufferManager.ResolveAndGetDepthTarget(source_rect) : g_framebufferManager.ResolveAndGetRenderTarget(source_rect);
745 GL_REPORT_ERRORD();
747 // We have to run a pixel shader, for color conversion.
748 Renderer::ResetAPIState(); // reset any game specific settings
750 if (s_TempFramebuffer == 0)
751 glGenFramebuffersEXT(1, (GLuint *)&s_TempFramebuffer);
753 g_framebufferManager.SetFramebuffer(s_TempFramebuffer);
754 // Bind texture to temporary framebuffer
755 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, entry.texture, 0);
756 GL_REPORT_FBO_ERROR();
757 GL_REPORT_ERRORD();
759 glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
760 glActiveTexture(GL_TEXTURE0);
761 glEnable(GL_TEXTURE_RECTANGLE_ARB);
762 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, read_texture);
764 glViewport(0, 0, w, h);
766 PixelShaderCache::EnableShader(bFromZBuffer ? PixelShaderCache::GetDepthMatrixProgram() : PixelShaderCache::GetColorMatrixProgram());
767 PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation
768 GL_REPORT_ERRORD();
770 TargetRectangle targetSource = Renderer::ConvertEFBRectangle(source_rect);
772 glBegin(GL_QUADS);
773 glTexCoord2f((GLfloat)targetSource.left, (GLfloat)targetSource.bottom); glVertex2f(-1, 1);
774 glTexCoord2f((GLfloat)targetSource.left, (GLfloat)targetSource.top ); glVertex2f(-1, -1);
775 glTexCoord2f((GLfloat)targetSource.right, (GLfloat)targetSource.top ); glVertex2f( 1, -1);
776 glTexCoord2f((GLfloat)targetSource.right, (GLfloat)targetSource.bottom); glVertex2f( 1, 1);
777 glEnd();
779 GL_REPORT_ERRORD();
781 // Unbind texture from temporary framebuffer
782 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, 0, 0);
784 // Return to the EFB.
785 g_framebufferManager.SetFramebuffer(0);
786 Renderer::RestoreAPIState();
787 VertexShaderManager::SetViewportChanged();
788 TextureMngr::DisableStage(0);
790 GL_REPORT_ERRORD();
792 if (g_ActiveConfig.bDumpEFBTarget)
794 static int count = 0;
795 SaveTexture(StringFromFormat("%sefb_frame_%i.tga", File::GetUserPath(D_DUMPTEXTURES_IDX), count++).c_str(), GL_TEXTURE_RECTANGLE_ARB, entry.texture, entry.w, entry.h);
799 void TextureMngr::DisableStage(int stage)
801 glActiveTexture(GL_TEXTURE0 + stage);
802 glDisable(GL_TEXTURE_2D);
803 glDisable(GL_TEXTURE_RECTANGLE_ARB);
806 void TextureMngr::ClearRenderTargets()
808 for (TexCache::iterator iter = textures.begin(); iter != textures.end(); iter++)
809 iter->second.isRenderTarget = false;