forked from dolphin-emu/dolphin
		
	git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2233 8ced0084-cf51-0410-be5f-012b33b47a6e
		
			
				
	
	
		
			730 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			730 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright (C) 2003-2008 Dolphin Project.
 | 
						|
 | 
						|
// This program is free software: you can redistribute it and/or modify
 | 
						|
// it under the terms of the GNU General Public License as published by
 | 
						|
// the Free Software Foundation, version 2.0.
 | 
						|
 | 
						|
// This program is distributed in the hope that it will be useful,
 | 
						|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
// GNU General Public License 2.0 for more details.
 | 
						|
 | 
						|
// A copy of the GPL 2.0 should have been included with the program.
 | 
						|
// If not, see http://www.gnu.org/licenses/
 | 
						|
 | 
						|
// Official SVN repository and contact information can be found at
 | 
						|
// http://code.google.com/p/dolphin-emu/
 | 
						|
 | 
						|
#include <vector>
 | 
						|
 | 
						|
#include "Globals.h"
 | 
						|
 | 
						|
#ifdef _WIN32
 | 
						|
#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set
 | 
						|
#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset
 | 
						|
#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64
 | 
						|
#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64
 | 
						|
#include <intrin.h>
 | 
						|
#undef _interlockedbittestandset
 | 
						|
#undef _interlockedbittestandreset
 | 
						|
#undef _interlockedbittestandset64
 | 
						|
#undef _interlockedbittestandreset64
 | 
						|
#endif
 | 
						|
 | 
						|
#include "Config.h"
 | 
						|
#include "Statistics.h"
 | 
						|
#include "Profiler.h"
 | 
						|
#include "ImageWrite.h"
 | 
						|
 | 
						|
#include "Render.h"
 | 
						|
 | 
						|
#include "MemoryUtil.h"
 | 
						|
#include "BPStructs.h"
 | 
						|
#include "TextureDecoder.h"
 | 
						|
#include "TextureMngr.h"
 | 
						|
#include "PixelShaderCache.h"
 | 
						|
#include "PixelShaderManager.h"
 | 
						|
#include "VertexShaderManager.h"
 | 
						|
 | 
						|
u8 *TextureMngr::temp = NULL;
 | 
						|
TextureMngr::TexCache TextureMngr::textures;
 | 
						|
std::map<u32, TextureMngr::DEPTHTARGET> TextureMngr::mapDepthTargets;
 | 
						|
int TextureMngr::nTex2DEnabled, TextureMngr::nTexRECTEnabled;
 | 
						|
 | 
						|
extern int frameCount;
 | 
						|
static u32 s_TempFramebuffer = 0;
 | 
						|
 | 
						|
#define TEMP_SIZE (1024*1024*4)
 | 
						|
#define TEXTURE_KILL_THRESHOLD 200
 | 
						|
 | 
						|
const GLint c_MinLinearFilter[8] = {
 | 
						|
	GL_NEAREST,
 | 
						|
	GL_NEAREST_MIPMAP_NEAREST,
 | 
						|
	GL_NEAREST_MIPMAP_LINEAR,
 | 
						|
	GL_NEAREST,
 | 
						|
	GL_LINEAR,
 | 
						|
	GL_LINEAR_MIPMAP_NEAREST,
 | 
						|
	GL_LINEAR_MIPMAP_LINEAR,
 | 
						|
	GL_LINEAR
 | 
						|
};
 | 
						|
 | 
						|
const GLint c_WrapSettings[4] = {
 | 
						|
	GL_CLAMP_TO_EDGE,
 | 
						|
	GL_REPEAT,
 | 
						|
	GL_MIRRORED_REPEAT,
 | 
						|
	GL_REPEAT
 | 
						|
};
 | 
						|
 | 
						|
bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int height)
 | 
						|
{
 | 
						|
    GL_REPORT_ERRORD();
 | 
						|
	std::vector<u32> data(width * height);
 | 
						|
    glBindTexture(textarget, tex);
 | 
						|
    glGetTexImage(textarget, 0, GL_BGRA, GL_UNSIGNED_BYTE, &data[0]);
 | 
						|
    GLenum err;
 | 
						|
    GL_REPORT_ERROR();
 | 
						|
    if (err != GL_NO_ERROR)
 | 
						|
	{
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    return SaveTGA(filename, width, height, &data[0]);
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::TCacheEntry::SetTextureParameters(TexMode0 &newmode)
 | 
						|
{
 | 
						|
    mode = newmode;
 | 
						|
    if (isNonPow2) {
 | 
						|
        // very limited!
 | 
						|
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
 | 
						|
			            (newmode.mag_filter || g_Config.bForceFiltering) ? GL_LINEAR : GL_NEAREST);
 | 
						|
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
 | 
						|
			            (g_Config.bForceFiltering || newmode.min_filter >= 4) ? GL_LINEAR : GL_NEAREST);
 | 
						|
		if (newmode.wrap_s == 2 || newmode.wrap_t == 2) {
 | 
						|
            DEBUG_LOG("cannot support mirrorred repeat mode\n");
 | 
						|
		}
 | 
						|
		if (newmode.wrap_s == 1 || newmode.wrap_t == 1) {
 | 
						|
            DEBUG_LOG("cannot support repeat mode\n");
 | 
						|
		}
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
 | 
						|
			            (newmode.mag_filter || g_Config.bForceFiltering) ? GL_LINEAR : GL_NEAREST);
 | 
						|
 | 
						|
        if (bHaveMipMaps) {
 | 
						|
            int filt = newmode.min_filter;
 | 
						|
            if (g_Config.bForceFiltering && newmode.min_filter < 4)
 | 
						|
                newmode.min_filter += 4; // take equivalent forced linear
 | 
						|
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c_MinLinearFilter[filt]);
 | 
						|
        }
 | 
						|
        else
 | 
						|
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
 | 
						|
			                (g_Config.bForceFiltering || newmode.min_filter >= 4) ? GL_LINEAR : GL_NEAREST);
 | 
						|
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c_WrapSettings[newmode.wrap_s]);
 | 
						|
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c_WrapSettings[newmode.wrap_t]);
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (g_Config.iMaxAnisotropy >= 1)
 | 
						|
    {
 | 
						|
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)(1 << g_Config.iMaxAnisotropy));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::TCacheEntry::Destroy()
 | 
						|
{
 | 
						|
    if (!texture)
 | 
						|
        return;
 | 
						|
    glDeleteTextures(1, &texture);
 | 
						|
    if (!isRenderTarget) {
 | 
						|
        if (!g_Config.bSafeTextureCache) {
 | 
						|
            u32 *ptr = (u32*)g_VideoInitialize.pGetMemoryPointer(addr + hashoffset * 4);
 | 
						|
            if (*ptr == hash)
 | 
						|
                *ptr = oldpixel;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    texture = 0;
 | 
						|
} 
 | 
						|
 | 
						|
void TextureMngr::Init()
 | 
						|
{
 | 
						|
    temp = (u8*)AllocateMemoryPages(TEMP_SIZE);
 | 
						|
    nTex2DEnabled = nTexRECTEnabled = 0;
 | 
						|
	TexDecoder_SetTexFmtOverlayOptions(g_Config.bTexFmtOverlayEnable, g_Config.bTexFmtOverlayCenter);
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::Invalidate()
 | 
						|
{
 | 
						|
    TexCache::iterator iter = textures.begin();
 | 
						|
    for (; iter!=textures.end(); iter++)
 | 
						|
        iter->second.Destroy();
 | 
						|
    textures.clear();
 | 
						|
	TexDecoder_SetTexFmtOverlayOptions(g_Config.bTexFmtOverlayEnable, g_Config.bTexFmtOverlayCenter);
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::Shutdown()
 | 
						|
{
 | 
						|
    Invalidate();
 | 
						|
    std::map<u32, DEPTHTARGET>::iterator itdepth = mapDepthTargets.begin();
 | 
						|
	for (itdepth = mapDepthTargets.begin(); itdepth != mapDepthTargets.end(); ++itdepth) {
 | 
						|
		glDeleteRenderbuffersEXT(1, &itdepth->second.targ);
 | 
						|
	}
 | 
						|
    mapDepthTargets.clear();
 | 
						|
 | 
						|
    if (s_TempFramebuffer) {
 | 
						|
        glDeleteFramebuffersEXT(1, (GLuint *)&s_TempFramebuffer);
 | 
						|
        s_TempFramebuffer = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    FreeMemoryPages(temp, TEMP_SIZE);	
 | 
						|
    temp = NULL;
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::Cleanup()
 | 
						|
{
 | 
						|
    TexCache::iterator iter = textures.begin();
 | 
						|
    while (iter != textures.end()) {
 | 
						|
        if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second.frameCount) {
 | 
						|
            if (!iter->second.isRenderTarget) {
 | 
						|
                iter->second.Destroy();
 | 
						|
#ifdef _WIN32
 | 
						|
                iter = textures.erase(iter);
 | 
						|
#else
 | 
						|
				textures.erase(iter++);
 | 
						|
#endif
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                iter->second.Destroy();
 | 
						|
#ifdef _WIN32
 | 
						|
                iter = textures.erase(iter);
 | 
						|
#else
 | 
						|
				textures.erase(iter++);
 | 
						|
#endif
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
            iter++;
 | 
						|
    }
 | 
						|
 | 
						|
    std::map<u32, DEPTHTARGET>::iterator itdepth = mapDepthTargets.begin();
 | 
						|
    while (itdepth != mapDepthTargets.end()) {
 | 
						|
        if (frameCount > 20 + itdepth->second.framecount) {
 | 
						|
#ifdef _WIN32
 | 
						|
            itdepth = mapDepthTargets.erase(itdepth);
 | 
						|
#else
 | 
						|
            mapDepthTargets.erase(itdepth++);
 | 
						|
#endif
 | 
						|
        }
 | 
						|
        else ++itdepth;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//int dbgTexIdx=0;
 | 
						|
 | 
						|
TextureMngr::TCacheEntry* TextureMngr::Load(int texstage, u32 address, int width, int height, int format, int tlutaddr, int tlutfmt)
 | 
						|
{
 | 
						|
	/* notes (about "UNsafe texture cache"):
 | 
						|
	*	Have to be removed soon.
 | 
						|
	*	But we keep it until the "safe" way became rock solid
 | 
						|
	*	pros: it has an unique ID held by the texture data itself (@address) once cached.
 | 
						|
	*	cons: it writes this unique ID in the gc RAM <- very dangerous (break MP1) and ugly 
 | 
						|
	*/
 | 
						|
	/* notes (about "safe texture cache"):
 | 
						|
	*	Metroids text issue (character table):
 | 
						|
	*	Same addr, same GX_TF_C4 texture data but different TLUT (hence different outputs).
 | 
						|
	*	That's why we have to hash the TLUT too for TLUT format dependent textures (ie. GX_TF_C4, GX_TF_C8, GX_TF_C14X2).
 | 
						|
	*	And since the address and tex data don't change, the key index in the cacheEntry map can't be the address but 
 | 
						|
	*	have to be a real unique ID. 
 | 
						|
	*	DONE but not satifiying yet -> may break copyEFBToTexture sometimes.
 | 
						|
	*
 | 
						|
	*	Pokemon Colosseum text issue (plain text):
 | 
						|
	*	Use a GX_TF_I4 512x512 text-flush-texture at a const address.
 | 
						|
	*	The problem here was just the sparse hash on the texture. This texture is partly overwrited (what is needed only) 
 | 
						|
	*	so lot's of remaning old text. 	Thin white chars on black bg too.
 | 
						|
	*/
 | 
						|
 | 
						|
	// TODO:	- clean this up when ready to kill old "unsafe texture cache"
 | 
						|
	//			- fix the key index situation with CopyRenderTargetToTexture. 
 | 
						|
	//			Could happen only for GX_TF_C4, GX_TF_C8 and GX_TF_C14X2 fmt.
 | 
						|
	//			Wonder if we can't use tex width&height to know if EFB might be copied to it...
 | 
						|
	//			raw idea:  TOCHECK if addresses are aligned we have few bits left...
 | 
						|
 | 
						|
 | 
						|
    if (address == 0)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    TexMode0 &tm0 = bpmem.tex[texstage > 3].texMode0[texstage & 3];
 | 
						|
    u8 *ptr = g_VideoInitialize.pGetMemoryPointer(address);
 | 
						|
	int bs = TexDecoder_GetBlockWidthInTexels(format) - 1;
 | 
						|
    int expandedWidth = (width + bs) & (~bs);
 | 
						|
 | 
						|
	u32 hash_value;
 | 
						|
    u32 texID = address;
 | 
						|
	if (g_Config.bSafeTextureCache)
 | 
						|
	{
 | 
						|
		hash_value = TexDecoder_GetSafeTextureHash(ptr, expandedWidth, height, format, 0); // remove last arg 
 | 
						|
		if ( (format == GX_TF_C4) || (format == GX_TF_C8) || (format == GX_TF_C14X2) )
 | 
						|
		{
 | 
						|
			// WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up)
 | 
						|
			// tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower.
 | 
						|
			//texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], TexDecoder_GetPaletteSize(format));
 | 
						|
			texID ^= TexDecoder_GetTlutHash(&texMem[tlutaddr], (format == GX_TF_C4) ? 32 : 128);
 | 
						|
			//DebugLog("addr: %08x | texID: %08x | texHash: %08x", address, texID, hash_value);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	bool skip_texture_create = false;
 | 
						|
 | 
						|
    TexCache::iterator iter = textures.find(texID);
 | 
						|
 | 
						|
	if (iter != textures.end()) {
 | 
						|
        TCacheEntry &entry = iter->second;
 | 
						|
 | 
						|
		if (!g_Config.bSafeTextureCache)
 | 
						|
			hash_value = ((u32 *)ptr)[entry.hashoffset];
 | 
						|
 | 
						|
        if (entry.isRenderTarget || ((address == entry.addr) && (hash_value == entry.hash))) {
 | 
						|
            entry.frameCount = frameCount;
 | 
						|
            //glEnable(entry.isNonPow2?GL_TEXTURE_RECTANGLE_ARB:GL_TEXTURE_2D);
 | 
						|
            glBindTexture(entry.isNonPow2 ? GL_TEXTURE_RECTANGLE_ARB:GL_TEXTURE_2D, entry.texture);
 | 
						|
            if (entry.mode.hex != tm0.hex)
 | 
						|
                entry.SetTextureParameters(tm0);
 | 
						|
			//DebugLog("%cC addr: %08x | fmt: %i | e.hash: %08x | w:%04i h:%04i", g_Config.bSafeTextureCache ? 'S' : 'U'
 | 
						|
 			//		, address, format, entry.hash, width, height);
 | 
						|
			return &entry;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Let's reload the new texture data into the same texture,
 | 
						|
			// instead of destroying it and having to create a new one.
 | 
						|
			// Might speed up movie playback very, very slightly.
 | 
						|
			if (width == entry.w && height == entry.h && format == entry.fmt)
 | 
						|
            {
 | 
						|
				glBindTexture(entry.isNonPow2 ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D, entry.texture);
 | 
						|
				if (entry.mode.hex != tm0.hex)
 | 
						|
					entry.SetTextureParameters(tm0);
 | 
						|
				skip_texture_create = true;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                entry.Destroy();
 | 
						|
                textures.erase(iter);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    PC_TexFormat dfmt = TexDecoder_Decode(temp, ptr, expandedWidth, height, format, tlutaddr, tlutfmt);
 | 
						|
 | 
						|
    //Make an entry in the table
 | 
						|
	TCacheEntry& entry = textures[texID];
 | 
						|
 | 
						|
	entry.hashoffset = 0;
 | 
						|
    //entry.paletteHash = hashseed;
 | 
						|
    entry.oldpixel = ((u32 *)ptr)[entry.hashoffset];
 | 
						|
	if (g_Config.bSafeTextureCache) {
 | 
						|
		entry.hash = hash_value;
 | 
						|
	} else {
 | 
						|
		entry.hash = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF);
 | 
						|
	    ((u32 *)ptr)[entry.hashoffset] = entry.hash;
 | 
						|
	}
 | 
						|
	//DebugLog("%c  addr: %08x | fmt: %i | e.hash: %08x | w:%04i h:%04i", g_Config.bSafeTextureCache ? 'S' : 'U'
 | 
						|
 	//		, address, format, entry.hash, width, height);
 | 
						|
	
 | 
						|
 | 
						|
    entry.addr = address;
 | 
						|
    entry.isRenderTarget = false;
 | 
						|
    entry.isNonPow2 = ((width & (width - 1)) || (height & (height - 1)));
 | 
						|
 | 
						|
	GLenum target = entry.isNonPow2 ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D;
 | 
						|
	if (!skip_texture_create) {
 | 
						|
		glGenTextures(1, (GLuint *)&entry.texture);
 | 
						|
		glBindTexture(target, entry.texture);
 | 
						|
	}
 | 
						|
 | 
						|
    if (expandedWidth != width)
 | 
						|
        glPixelStorei(GL_UNPACK_ROW_LENGTH, expandedWidth);
 | 
						|
 | 
						|
	int gl_format;
 | 
						|
	int gl_iformat;
 | 
						|
	int gl_type;
 | 
						|
	switch (dfmt) {
 | 
						|
	default:
 | 
						|
	case PC_TEX_FMT_NONE:
 | 
						|
		PanicAlert("Invalid PC texture format %i", dfmt); 
 | 
						|
	case PC_TEX_FMT_BGRA32:
 | 
						|
		gl_format = GL_BGRA;
 | 
						|
		gl_iformat = 4;
 | 
						|
		gl_type = GL_UNSIGNED_BYTE;
 | 
						|
		break;
 | 
						|
	case PC_TEX_FMT_I8:
 | 
						|
		gl_format = GL_LUMINANCE;
 | 
						|
		gl_iformat = GL_INTENSITY;
 | 
						|
		gl_type = GL_UNSIGNED_BYTE;
 | 
						|
		break;
 | 
						|
	case PC_TEX_FMT_IA8:
 | 
						|
		gl_format = GL_LUMINANCE_ALPHA;
 | 
						|
		gl_iformat = GL_LUMINANCE8_ALPHA8;
 | 
						|
		gl_type = GL_UNSIGNED_BYTE;
 | 
						|
		break;
 | 
						|
	case PC_TEX_FMT_RGB565:
 | 
						|
		gl_format = GL_RGB;
 | 
						|
		gl_iformat = GL_RGB;
 | 
						|
		gl_type = GL_UNSIGNED_SHORT_5_6_5;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	if (!entry.isNonPow2 && ((tm0.min_filter & 3) == 1 || (tm0.min_filter & 3) == 2)) {
 | 
						|
		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
 | 
						|
		glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, width, height, 0, gl_format, gl_type, temp);
 | 
						|
 | 
						|
		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
 | 
						|
		entry.bHaveMipMaps = true;
 | 
						|
	}
 | 
						|
	else
 | 
						|
		glTexImage2D(target, 0, gl_iformat, width, height, 0, gl_format, gl_type, temp);
 | 
						|
 | 
						|
	if (expandedWidth != width) // reset
 | 
						|
		glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 | 
						|
 | 
						|
    entry.frameCount = frameCount;
 | 
						|
    entry.w = width;
 | 
						|
    entry.h = height;
 | 
						|
    entry.fmt = format;
 | 
						|
    entry.SetTextureParameters(tm0);
 | 
						|
 | 
						|
    if (g_Config.bDumpTextures) { // dump texture to file
 | 
						|
        static int counter = 0;
 | 
						|
        char szTemp[MAX_PATH];
 | 
						|
		sprintf(szTemp, "%s/txt_%04i_%i.tga", g_Config.texDumpPath, counter++, format);
 | 
						|
        
 | 
						|
        SaveTexture(szTemp,target, entry.texture, width, height);
 | 
						|
    }
 | 
						|
 | 
						|
    INCSTAT(stats.numTexturesCreated);
 | 
						|
    SETSTAT(stats.numTexturesAlive, textures.size());
 | 
						|
 | 
						|
    //glEnable(entry.isNonPow2?GL_TEXTURE_RECTANGLE_ARB:GL_TEXTURE_2D);
 | 
						|
	/*
 | 
						|
	if ( 1
 | 
						|
		//&& (entry.w != 640  && entry.h != 480)
 | 
						|
		//&& (entry.w != 320  && entry.h != 240)
 | 
						|
		&& (entry.w ==512 && entry.h == 512) 
 | 
						|
		//&& (entry.w > 200  && entry.h > 200)
 | 
						|
		//&& (format!=1)
 | 
						|
		)
 | 
						|
	{
 | 
						|
		char fn[256];
 | 
						|
		sprintf(fn, "z_%i_%i_%ix%i_%08x.tga", dbgTexIdx, format, entry.w, entry.h, entry.addr);
 | 
						|
		SaveTexture(fn, target, entry.texture, entry.w, entry.h);
 | 
						|
		dbgTexIdx++;
 | 
						|
	}
 | 
						|
	*/
 | 
						|
    //SaveTexture("tex.tga", target, entry.texture, entry.w, entry.h);
 | 
						|
    return &entry;
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::CopyRenderTargetToTexture(u32 address, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, bool bScaleByHalf, TRectangle *source)
 | 
						|
{
 | 
						|
    DVSTARTPROFILE();
 | 
						|
    GL_REPORT_ERRORD();
 | 
						|
 | 
						|
    // for intensity values, use Y of YUV format!
 | 
						|
    // for all purposes, treat 4bit equivalents as 8bit (probably just used for compression)
 | 
						|
    // RGBA8 - RGBA8
 | 
						|
    // RGB565 - RGB565
 | 
						|
    // RGB5A3 - RGB5A3
 | 
						|
    // I4,R4,Z4 - I4
 | 
						|
    // IA4,RA4 - IA4
 | 
						|
    // Z8M,G8,I8,A8,Z8,R8,B8,Z8L - I8
 | 
						|
    // Z16,GB8,RG8,Z16L,IA8,RA8 - IA8
 | 
						|
    bool bIsInit = textures.find(address) != textures.end();
 | 
						|
 | 
						|
    PRIM_LOG("copytarg: addr=0x%x, fromz=%d, intfmt=%d, copyfmt=%d\n", address, (int)bFromZBuffer, (int)bIsIntensityFmt,copyfmt);
 | 
						|
 | 
						|
    TCacheEntry& entry = textures[address];
 | 
						|
    entry.isNonPow2 = true;
 | 
						|
    entry.hash = 0;
 | 
						|
    entry.hashoffset = 0;
 | 
						|
    entry.frameCount = frameCount;
 | 
						|
    
 | 
						|
    int mult = bScaleByHalf?2:1;
 | 
						|
	int w = (abs(source->right-source->left)/mult);
 | 
						|
    int h = (abs(source->bottom-source->top)/mult);
 | 
						|
 | 
						|
    GL_REPORT_ERRORD();
 | 
						|
 | 
						|
    if (!bIsInit) {
 | 
						|
        glGenTextures(1, (GLuint *)&entry.texture);
 | 
						|
        glBindTexture(GL_TEXTURE_RECTANGLE_ARB, entry.texture);
 | 
						|
        glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 | 
						|
        GL_REPORT_ERRORD();
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        _assert_(entry.texture);
 | 
						|
        bool bReInit = true;
 | 
						|
 | 
						|
        if (entry.w == w && entry.h == h) {
 | 
						|
            glBindTexture(GL_TEXTURE_RECTANGLE_ARB, entry.texture);
 | 
						|
            // for some reason mario sunshine errors here...
 | 
						|
            GLenum err = GL_NO_ERROR;
 | 
						|
            GL_REPORT_ERROR();
 | 
						|
            if (err == GL_NO_ERROR)
 | 
						|
                bReInit = false;
 | 
						|
        }
 | 
						|
 | 
						|
        if (bReInit) {
 | 
						|
            // necessary, for some reason opengl gives errors when texture isn't deleted
 | 
						|
            glDeleteTextures(1,(GLuint *)&entry.texture);
 | 
						|
            glGenTextures(1, (GLuint *)&entry.texture);
 | 
						|
            glBindTexture(GL_TEXTURE_RECTANGLE_ARB, entry.texture);
 | 
						|
            glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 | 
						|
            GL_REPORT_ERRORD();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!bIsInit || !entry.isRenderTarget) {
 | 
						|
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
						|
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
						|
 | 
						|
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
						|
        glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 | 
						|
        if (glGetError() != GL_NO_ERROR) {
 | 
						|
            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
 | 
						|
            glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
 | 
						|
            GL_REPORT_ERRORD();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    entry.w = w;
 | 
						|
    entry.h = h;
 | 
						|
    entry.isRenderTarget = true;
 | 
						|
    entry.fmt = copyfmt;
 | 
						|
 | 
						|
    float colmat[16];
 | 
						|
    float fConstAdd[4] = {0};
 | 
						|
    memset(colmat, 0, sizeof(colmat));
 | 
						|
 | 
						|
    if (bFromZBuffer) {
 | 
						|
        switch(copyfmt) {
 | 
						|
            case 0: // Z4
 | 
						|
            case 1: // Z8
 | 
						|
                colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
 | 
						|
                break;
 | 
						|
            
 | 
						|
            case 3: // Z16 //?
 | 
						|
            case 11: // Z16
 | 
						|
                colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
 | 
						|
                break;
 | 
						|
            case 6: // Z24X8
 | 
						|
                colmat[0] = 1;
 | 
						|
                colmat[5] = 1;
 | 
						|
                colmat[10] = 1;
 | 
						|
                break;
 | 
						|
            case 9: // Z8M
 | 
						|
                colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
 | 
						|
                break;
 | 
						|
            case 10: // Z8L
 | 
						|
                colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
 | 
						|
                break;
 | 
						|
            case 12: // Z16L
 | 
						|
                colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                ERROR_LOG("Unknown copy zbuf format: 0x%x\n", copyfmt);
 | 
						|
                colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (bIsIntensityFmt) {
 | 
						|
        fConstAdd[0] = fConstAdd[1] = fConstAdd[2] = 16.0f/255.0f;
 | 
						|
        switch(copyfmt) {
 | 
						|
            case 0: // I4
 | 
						|
            case 1: // I8
 | 
						|
            case 2: // IA4
 | 
						|
            case 3: // IA8
 | 
						|
				// TODO - verify these coefficients
 | 
						|
                colmat[0] = 0.257f; colmat[1] = 0.504f; colmat[2] = 0.098f;
 | 
						|
                colmat[4] = 0.257f; colmat[5] = 0.504f; colmat[6] = 0.098f;
 | 
						|
                colmat[8] = 0.257f; colmat[9] = 0.504f; colmat[10] = 0.098f;
 | 
						|
                if (copyfmt < 2) {
 | 
						|
                    fConstAdd[3] = 16.0f / 255.0f;
 | 
						|
                    colmat[12] = 0.257f; colmat[13] = 0.504f; colmat[14] = 0.098f;
 | 
						|
                }
 | 
						|
                else { // alpha
 | 
						|
                    colmat[15] = 1;
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                ERROR_LOG("Unknown copy intensity format: 0x%x\n", copyfmt);
 | 
						|
                colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        switch (copyfmt) {
 | 
						|
            case 0: // R4
 | 
						|
            case 8: // R8
 | 
						|
                colmat[0] = colmat[4] = colmat[8] = colmat[12] = 1;
 | 
						|
                break;
 | 
						|
            case 2: // RA4
 | 
						|
            case 3: // RA8
 | 
						|
                colmat[0] = colmat[4] = colmat[8] = colmat[15] = 1;
 | 
						|
                break;
 | 
						|
 | 
						|
            case 7: // A8
 | 
						|
                colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1; 
 | 
						|
                break;
 | 
						|
            case 9: // G8
 | 
						|
                colmat[1] = colmat[5] = colmat[9] = colmat[13] = 1;
 | 
						|
                break;
 | 
						|
            case 10: // B8
 | 
						|
                colmat[2] = colmat[6] = colmat[10] = colmat[14] = 1;
 | 
						|
                break;
 | 
						|
            case 11: // RG8
 | 
						|
                colmat[0] = colmat[4] = colmat[8] = colmat[13] = 1;
 | 
						|
                break;
 | 
						|
            case 12: // GB8
 | 
						|
                colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1;
 | 
						|
                break;
 | 
						|
 | 
						|
            case 4: // RGB565
 | 
						|
                colmat[0] = colmat[5] = colmat[10] = 1;
 | 
						|
                fConstAdd[3] = 1; // set alpha to 1
 | 
						|
                break;
 | 
						|
            case 5: // RGB5A3
 | 
						|
            case 6: // RGBA8
 | 
						|
                colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
 | 
						|
                break;
 | 
						|
 | 
						|
            default:
 | 
						|
                ERROR_LOG("Unknown copy color format: 0x%x\n", copyfmt);
 | 
						|
                colmat[0] = colmat[5] = colmat[10] = colmat[15] = 1;
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
//    if (bCopyToTarget) {
 | 
						|
//        _assert_(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT);
 | 
						|
//        glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
 | 
						|
//        GL_REPORT_ERRORD();
 | 
						|
//        glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, source->left, source->top, source->right-source->left, source->bottom-source->top);
 | 
						|
//        entry.isUpsideDown = true; // note that the copy is upside down!!
 | 
						|
//        GL_REPORT_ERRORD(); 
 | 
						|
//        return;
 | 
						|
//    }
 | 
						|
    
 | 
						|
    Renderer::SetRenderMode(Renderer::RM_Normal); // set back to normal
 | 
						|
    GL_REPORT_ERRORD();
 | 
						|
 | 
						|
    // have to run a pixel shader
 | 
						|
 | 
						|
    Renderer::ResetGLState(); // reset any game specific settings
 | 
						|
 | 
						|
    if (s_TempFramebuffer == 0)
 | 
						|
        glGenFramebuffersEXT(1, (GLuint *)&s_TempFramebuffer);
 | 
						|
 | 
						|
    Renderer::SetFramebuffer(s_TempFramebuffer);
 | 
						|
    Renderer::SetRenderTarget(entry.texture);
 | 
						|
    GL_REPORT_ERRORD();
 | 
						|
 | 
						|
    // create and attach the render target
 | 
						|
    std::map<u32, DEPTHTARGET>::iterator itdepth = mapDepthTargets.find((h << 16) | w);
 | 
						|
    
 | 
						|
    if (itdepth == mapDepthTargets.end()) {
 | 
						|
        DEPTHTARGET& depth = mapDepthTargets[(h << 16) | w];
 | 
						|
        depth.framecount = frameCount;
 | 
						|
        glGenRenderbuffersEXT(1, &depth.targ);
 | 
						|
        glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth.targ);
 | 
						|
        glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT/*GL_DEPTH24_STENCIL8_EXT*/, w, h);
 | 
						|
        GL_REPORT_ERRORD();
 | 
						|
        Renderer::SetDepthTarget(depth.targ);
 | 
						|
        GL_REPORT_ERRORD();
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        itdepth->second.framecount = frameCount;
 | 
						|
        Renderer::SetDepthTarget(itdepth->second.targ);
 | 
						|
        GL_REPORT_ERRORD();
 | 
						|
    }
 | 
						|
 | 
						|
    glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
 | 
						|
    glActiveTexture(GL_TEXTURE0);
 | 
						|
    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, bFromZBuffer?Renderer::GetZBufferTarget():Renderer::GetRenderTarget());    
 | 
						|
    TextureMngr::EnableTexRECT(0);
 | 
						|
    
 | 
						|
    glViewport(0, 0, w, h);
 | 
						|
 | 
						|
    glEnable(GL_FRAGMENT_PROGRAM_ARB);
 | 
						|
    glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, PixelShaderCache::GetColorMatrixProgram());
 | 
						|
    PixelShaderManager::SetColorMatrix(colmat, fConstAdd); // set transformation
 | 
						|
    GL_REPORT_ERRORD();
 | 
						|
    
 | 
						|
    glBegin(GL_QUADS);
 | 
						|
    float MValueX = OpenGL_GetXmax();
 | 
						|
    float MValueY = OpenGL_GetYmax();
 | 
						|
    glTexCoord2f((float)source->left * MValueX, Renderer::GetTargetHeight()-(float)source->bottom * MValueY); glVertex2f(-1,1);
 | 
						|
    glTexCoord2f((float)source->left * MValueX, Renderer::GetTargetHeight()-(float)source->top * MValueY); glVertex2f(-1,-1);
 | 
						|
    glTexCoord2f((float)source->right * MValueX, Renderer::GetTargetHeight()-(float)source->top * MValueY); glVertex2f(1,-1);
 | 
						|
    glTexCoord2f((float)source->right * MValueX, Renderer::GetTargetHeight()-(float)source->bottom * MValueY); glVertex2f(1,1);
 | 
						|
    glEnd();
 | 
						|
 | 
						|
    GL_REPORT_ERRORD();
 | 
						|
 | 
						|
    Renderer::SetFramebuffer(0);
 | 
						|
    Renderer::RestoreGLState();
 | 
						|
    VertexShaderManager::SetViewportChanged();
 | 
						|
 | 
						|
    TextureMngr::DisableStage(0);
 | 
						|
 | 
						|
    if (bFromZBuffer)
 | 
						|
        Renderer::SetZBufferRender(); // notify for future settings
 | 
						|
 | 
						|
    GL_REPORT_ERRORD();
 | 
						|
    //SaveTexture("frame.tga", GL_TEXTURE_RECTANGLE_ARB, entry.texture, entry.w, entry.h);
 | 
						|
    //SaveTexture("tex.tga", GL_TEXTURE_RECTANGLE_ARB, bFromZBuffer?Renderer::GetZBufferTarget():Renderer::GetRenderTarget(), Renderer::GetTargetWidth(), Renderer::GetTargetHeight());
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::EnableTex2D(int stage)
 | 
						|
{
 | 
						|
    if (!(nTex2DEnabled & (1<<stage))) {
 | 
						|
        nTex2DEnabled |= (1<<stage);
 | 
						|
        glEnable(GL_TEXTURE_2D);
 | 
						|
    }
 | 
						|
    if (nTexRECTEnabled & (1<<stage)) {
 | 
						|
        nTexRECTEnabled &= ~(1<<stage);
 | 
						|
        glDisable(GL_TEXTURE_RECTANGLE_ARB);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::EnableTexRECT(int stage)
 | 
						|
{
 | 
						|
    if ((nTex2DEnabled & (1 << stage))) {
 | 
						|
        nTex2DEnabled &= ~(1 << stage);
 | 
						|
        glDisable(GL_TEXTURE_2D);
 | 
						|
    }
 | 
						|
    if (!(nTexRECTEnabled & (1 << stage))) {
 | 
						|
        nTexRECTEnabled |= (1 << stage);
 | 
						|
        glEnable(GL_TEXTURE_RECTANGLE_ARB);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::DisableStage(int stage)
 | 
						|
{
 | 
						|
    bool bset = false;
 | 
						|
    if (nTex2DEnabled & (1 << stage)) {
 | 
						|
        nTex2DEnabled &= ~(1 << stage);
 | 
						|
        glActiveTexture(GL_TEXTURE0 + stage);
 | 
						|
        glDisable(GL_TEXTURE_2D);
 | 
						|
        bset = true;
 | 
						|
    }
 | 
						|
    if (nTexRECTEnabled & (1<<stage)) {
 | 
						|
        nTexRECTEnabled &= ~(1 << stage);
 | 
						|
        if (!bset)
 | 
						|
			glActiveTexture(GL_TEXTURE0 + stage);
 | 
						|
        glDisable(GL_TEXTURE_RECTANGLE_ARB);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void TextureMngr::ClearRenderTargets()
 | 
						|
{
 | 
						|
	TexCache::iterator iter = textures.begin();
 | 
						|
    for (; iter!=textures.end(); iter++)
 | 
						|
        iter->second.isRenderTarget = false;
 | 
						|
}
 |