예제 #1
0
CGSH_OpenGL::TEXTURE_INFO CGSH_OpenGL::PrepareTexture(const TEX0& tex0)
{
	TEXTURE_INFO texInfo;

	for(const auto& candidateFramebuffer : m_framebuffers)
	{
		bool canBeUsed = false;
		float offsetX = 0;

		//Case: TEX0 points at the start of a frame buffer with the same width
		if(candidateFramebuffer->m_basePtr == tex0.GetBufPtr() &&
			candidateFramebuffer->m_width == tex0.GetBufWidth() &&
			IsCompatibleFramebufferPSM(candidateFramebuffer->m_psm, tex0.nPsm))
		{
			canBeUsed = true;
		}

		//Another case: TEX0 is pointing to the start of a page within our framebuffer (BGDA does this)
		else if(candidateFramebuffer->m_basePtr <= tex0.GetBufPtr() &&
			candidateFramebuffer->m_psm == tex0.nPsm)
		{
			uint32 framebufferOffset = tex0.GetBufPtr() - candidateFramebuffer->m_basePtr;

			//Bail if offset is not aligned on a page boundary
			if((framebufferOffset & (CGsPixelFormats::PAGESIZE - 1)) != 0) continue;

			auto framebufferPageSize = CGsPixelFormats::GetPsmPageSize(candidateFramebuffer->m_psm);
			uint32 framebufferPageCountX = candidateFramebuffer->m_width / framebufferPageSize.first;
			uint32 framebufferPageIndex = framebufferOffset / CGsPixelFormats::PAGESIZE;

			//Bail if pointed page isn't on the first line
			if(framebufferPageIndex >= framebufferPageCountX) continue;

			canBeUsed = true;
			offsetX = static_cast<float>(framebufferPageIndex * framebufferPageSize.first) / static_cast<float>(candidateFramebuffer->m_width);
		}

		if(canBeUsed)
		{
			CommitFramebufferDirtyPages(candidateFramebuffer, 0, tex0.GetHeight());

			//We have a winner
			glBindTexture(GL_TEXTURE_2D, candidateFramebuffer->m_texture);

			float scaleRatioX = static_cast<float>(tex0.GetWidth()) / static_cast<float>(candidateFramebuffer->m_textureWidth);
			float scaleRatioY = static_cast<float>(tex0.GetHeight()) / static_cast<float>(candidateFramebuffer->m_height);

			//If we're currently in interlaced mode, framebuffer will have twice the height
			bool halfHeight = GetCrtIsInterlaced() && GetCrtIsFrameMode();
			if(halfHeight) scaleRatioY *= 2.0f;

			texInfo.offsetX = offsetX;
			texInfo.scaleRatioX = scaleRatioX;
			texInfo.scaleRatioY = scaleRatioY;

			return texInfo;
		}
	}

	auto texture = TexCache_Search(tex0);
	if(texture)
	{
		glBindTexture(GL_TEXTURE_2D, texture->m_texture);
		auto& cachedArea = texture->m_cachedArea;

		if(cachedArea.HasDirtyPages())
		{
			auto texturePageSize = CGsPixelFormats::GetPsmPageSize(tex0.nPsm);
			auto pageRect = cachedArea.GetPageRect();

			for(unsigned int dirtyPageIndex = 0; dirtyPageIndex < CGsCachedArea::MAX_DIRTYPAGES; dirtyPageIndex++)
			{
				if(!cachedArea.IsPageDirty(dirtyPageIndex)) continue;

				uint32 pageX = dirtyPageIndex % pageRect.first;
				uint32 pageY = dirtyPageIndex / pageRect.first;
				uint32 texX = pageX * texturePageSize.first;
				uint32 texY = pageY * texturePageSize.second;
				uint32 texWidth = texturePageSize.first;
				uint32 texHeight = texturePageSize.second;
				if(texX >= tex0.GetWidth()) continue;
				if(texY >= tex0.GetHeight()) continue;
				//assert(texX < tex0.GetWidth());
				//assert(texY < tex0.GetHeight());
				if((texX + texWidth) > tex0.GetWidth())
				{
					texWidth = tex0.GetWidth() - texX;
				}
				if((texY + texHeight) > tex0.GetHeight())
				{
					texHeight = tex0.GetHeight() - texY;
				}
				((this)->*(m_textureUpdater[tex0.nPsm]))(tex0.GetBufPtr(), tex0.nBufWidth, texX, texY, texWidth, texHeight);
			}

			cachedArea.ClearDirtyPages();
		}
	}
	else
	{
		//Validate texture dimensions to prevent problems
		auto texWidth = tex0.GetWidth();
		auto texHeight = tex0.GetHeight();
		assert(texWidth <= 1024);
		assert(texHeight <= 1024);
		texWidth = std::min<uint32>(texWidth, 1024);
		texHeight = std::min<uint32>(texHeight, 1024);

		GLuint nTexture = 0;
		glGenTextures(1, &nTexture);
		glBindTexture(GL_TEXTURE_2D, nTexture);
		((this)->*(m_textureUploader[tex0.nPsm]))(tex0.GetBufPtr(), tex0.nBufWidth, texWidth, texHeight);
		TexCache_Insert(tex0, nTexture);
	}

	return texInfo;
}
예제 #2
0
CGSH_Direct3D9::TEXTURE_INFO CGSH_Direct3D9::LoadTexture(const TEX0& tex0, uint32 maxMip, const MIPTBP1& miptbp1, const MIPTBP2& miptbp2)
{
	TEXTURE_INFO result;

	{
		D3DXMATRIX textureMatrix;
		D3DXMatrixIdentity(&textureMatrix);
		D3DXMatrixScaling(&textureMatrix, 1, 1, 1);
		m_device->SetTransform(D3DTS_TEXTURE0, &textureMatrix);
	}

	for(const auto& candidateFramebuffer : m_framebuffers)
	{
		if(candidateFramebuffer->m_basePtr == tex0.GetBufPtr() &&
			candidateFramebuffer->m_width == tex0.GetBufWidth() &&
			candidateFramebuffer->m_canBeUsedAsTexture)
		{
			float scaleRatioX = static_cast<float>(tex0.GetWidth()) / static_cast<float>(candidateFramebuffer->m_width);
			float scaleRatioY = static_cast<float>(tex0.GetHeight()) / static_cast<float>(candidateFramebuffer->m_height);

			{
				D3DXMATRIX textureMatrix;
				D3DXMatrixIdentity(&textureMatrix);
				D3DXMatrixScaling(&textureMatrix, scaleRatioX, scaleRatioY, 1);
				m_device->SetTransform(D3DTS_TEXTURE0, &textureMatrix);
			}

			result.texture = candidateFramebuffer->m_renderTarget;
			result.isRenderTarget = true;
			result.renderTargetWidth = candidateFramebuffer->m_width;
			result.renderTargetHeight = candidateFramebuffer->m_height;

			return result;
		}
	}

	HRESULT resultCode = S_OK;

	auto texture = m_textureCache.Search(tex0);
	if(!texture)
	{
		uint32 width  = tex0.GetWidth();
		uint32 height = tex0.GetHeight();

		D3DFORMAT textureFormat = D3DFMT_A8R8G8B8;
		switch(tex0.nPsm)
		{
		case PSMCT32:
			textureFormat = D3DFMT_A8R8G8B8;
			break;
		case PSMCT16:
			textureFormat = D3DFMT_A1R5G5B5;
			break;
		case PSMT8:
		case PSMT4:
			textureFormat = D3DFMT_L8;
			break;
		default:
			assert(false);
			break;
		}

		resultCode = m_device->CreateTexture(width, height, 1 + maxMip, D3DUSAGE_DYNAMIC, textureFormat, D3DPOOL_DEFAULT, &result.texture, NULL);
		assert(SUCCEEDED(resultCode));

		m_textureCache.Insert(tex0, result.texture);
		texture = m_textureCache.Search(tex0);
		assert(result.texture == texture->m_textureHandle);

		texture->m_cachedArea.Invalidate(0, RAMSIZE);
	}

	auto& cachedArea = texture->m_cachedArea;

	if(cachedArea.HasDirtyPages())
	{
		D3DLOCKED_RECT lockedRect;
		resultCode = texture->m_textureHandle->LockRect(0, &lockedRect, nullptr, 0);
		assert(SUCCEEDED(resultCode));

		auto texturePageSize = CGsPixelFormats::GetPsmPageSize(tex0.nPsm);
		auto pageRect = cachedArea.GetPageRect();

		for(unsigned int dirtyPageIndex = 0; dirtyPageIndex < CGsCachedArea::MAX_DIRTYPAGES; dirtyPageIndex++)
		{
			if(!cachedArea.IsPageDirty(dirtyPageIndex)) continue;

			uint32 pageX = dirtyPageIndex % pageRect.first;
			uint32 pageY = dirtyPageIndex / pageRect.first;
			uint32 texX = pageX * texturePageSize.first;
			uint32 texY = pageY * texturePageSize.second;
			uint32 texWidth = texturePageSize.first;
			uint32 texHeight = texturePageSize.second;
			if(texX >= tex0.GetWidth()) continue;
			if(texY >= tex0.GetHeight()) continue;
			if((texX + texWidth) > tex0.GetWidth())
			{
				texWidth = tex0.GetWidth() - texX;
			}
			if((texY + texHeight) > tex0.GetHeight())
			{
				texHeight = tex0.GetHeight() - texY;
			}
			((this)->*(m_textureUpdater[tex0.nPsm]))(&lockedRect, tex0.GetBufPtr(), tex0.nBufWidth, texX, texY, texWidth, texHeight);
		}

		cachedArea.ClearDirtyPages();

		resultCode = texture->m_textureHandle->UnlockRect(0);
		assert(SUCCEEDED(resultCode));

		//Update mipmap levels
		for(uint32 mipLevel = 1; mipLevel <= maxMip; mipLevel++)
		{
			uint32 mipLevelWidth = std::max<uint32>(tex0.GetWidth() >> mipLevel, 1);
			uint32 mipLevelHeight = std::max<uint32>(tex0.GetHeight() >> mipLevel, 1);

			uint32 mipLevelBufferPointer = 0;
			uint32 mipLevelBufferWidth = 0;
			switch(mipLevel)
			{
			case 1: mipLevelBufferPointer = miptbp1.GetTbp1(); mipLevelBufferWidth = miptbp1.GetTbw1(); break;
			case 2: mipLevelBufferPointer = miptbp1.GetTbp2(); mipLevelBufferWidth = miptbp1.GetTbw2(); break;
			case 3: mipLevelBufferPointer = miptbp1.GetTbp3(); mipLevelBufferWidth = miptbp1.GetTbw3(); break;
			case 4: mipLevelBufferPointer = miptbp2.GetTbp4(); mipLevelBufferWidth = miptbp2.GetTbw4(); break;
			case 5: mipLevelBufferPointer = miptbp2.GetTbp5(); mipLevelBufferWidth = miptbp2.GetTbw5(); break;
			case 6: mipLevelBufferPointer = miptbp2.GetTbp6(); mipLevelBufferWidth = miptbp2.GetTbw6(); break;
			}

			if(mipLevelBufferWidth == 0) break;

			resultCode = texture->m_textureHandle->LockRect(mipLevel, &lockedRect, nullptr, 0);
			assert(SUCCEEDED(resultCode));

			((this)->*(m_textureUpdater[tex0.nPsm]))(&lockedRect, mipLevelBufferPointer, mipLevelBufferWidth / 64, 0, 0, mipLevelWidth, mipLevelHeight);

			resultCode = texture->m_textureHandle->UnlockRect(mipLevel);
			assert(SUCCEEDED(resultCode));
		}
	}