void copyTexture(const ThreadDispatcher::message& msg)
    {
        const CreateTextureTask* task = (const CreateTextureTask*)msg.cbData;


        MemoryTexture* src = task->src.get();
        NativeTexture* dest = task->dest.get();

        bool done = false;

        if (isCompressedFormat(src->getFormat()))
        {
            dest->init(src->lock(), false);
            done = true;
        }
        else
        {

            Rect textureRect(0, 0, src->getWidth(), src->getHeight());

            if (dest->getHandle() == 0)
                dest->init(textureRect.getWidth(), textureRect.getHeight(), src->getFormat());

            dest->updateRegion(0, 0, src->lock());
        }

        task->ready();
    }
Esempio n. 2
0
void GFXGLCubemap::fillCubeTextures(GFXTexHandle* faces)
{
   AssertFatal( faces, "");
   AssertFatal( faces[0]->mMipLevels > 0, "");

   PRESERVE_CUBEMAP_TEXTURE();
   glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap);
   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, faces[0]->mMipLevels - 1 );
   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
   
   U32 reqWidth = faces[0]->getWidth();
   U32 reqHeight = faces[0]->getHeight();
   GFXFormat regFaceFormat = faces[0]->getFormat();
   const bool isCompressed = isCompressedFormat(regFaceFormat);
   mWidth = reqWidth;
   mHeight = reqHeight;
   mFaceFormat = regFaceFormat;
   mMipLevels = getMax( (U32)1, faces[0]->mMipLevels);
   AssertFatal(reqWidth == reqHeight, "GFXGLCubemap::fillCubeTextures - Width and height must be equal!");
   
   for(U32 i = 0; i < 6; i++)
   {
      AssertFatal(faces[i], avar("GFXGLCubemap::fillCubeFaces - texture %i is NULL!", i));
      AssertFatal((faces[i]->getWidth() == reqWidth) && (faces[i]->getHeight() == reqHeight), "GFXGLCubemap::fillCubeFaces - All textures must have identical dimensions!");
      AssertFatal(faces[i]->getFormat() == regFaceFormat, "GFXGLCubemap::fillCubeFaces - All textures must have identical formats!");
      
      mTextures[i] = faces[i];
      GFXFormat faceFormat = faces[i]->getFormat();

        GFXGLTextureObject* glTex = static_cast<GFXGLTextureObject*>(faces[i].getPointer());
        if( isCompressed )
        {
            for( U32 mip = 0; mip < mMipLevels; ++mip )
            {
                const U32 mipWidth  = getMax( U32(1), faces[i]->getWidth() >> mip );
                const U32 mipHeight = getMax( U32(1), faces[i]->getHeight() >> mip );
                const U32 mipDataSize = getCompressedSurfaceSize( mFaceFormat, mWidth, mHeight, mip );

                U8* buf = glTex->getTextureData( mip );
                glCompressedTexImage2D(faceList[i], mip, GFXGLTextureInternalFormat[mFaceFormat], mipWidth, mipHeight, 0, mipDataSize, buf);
                delete[] buf;
            }
        }
        else
        {
            U8* buf = glTex->getTextureData();
            glTexImage2D(faceList[i], 0, GFXGLTextureInternalFormat[faceFormat], mWidth, mHeight, 
                0, GFXGLTextureFormat[faceFormat], GFXGLTextureType[faceFormat], buf);
            delete[] buf;
        }
   }
	void ResAtlas::loadAtlas(CreateResourceContext &context)
	{
		//string path = context.walker.getCurrentFolder();

		pugi::xml_node node = context.walker.getNode(); 
		pugi::xml_node meta = context.walker.getMeta();

        int w = node.attribute("width").as_int(defaultAtlasWidth);
        int h = node.attribute("height").as_int(defaultAtlasHeight);
		const char *format = node.attribute("format").as_string("8888");

		atlas_data ad;


		TextureFormat tf = string2TextureFormat(format);

		pugi::xml_node meta_image = meta.child("atlas");
		
		bool compressed = false;

		while(meta_image)
		{
			const char *file = meta_image.attribute("file").value();

			int w = meta_image.attribute("w").as_int();
			int h = meta_image.attribute("h").as_int();

			const char *file_format = meta_image.attribute("format").as_string(0);
			TextureFormat ffmt = TF_UNDEFINED;
			if (file_format)
			{
				ffmt = string2TextureFormat(file_format);
				compressed = isCompressedFormat(ffmt);
			}

			std::string alpha_file = meta_image.attribute("alpha").as_string("");
			if (!alpha_file.empty())
			{
				alpha_file = *context.prebuilt_folder + alpha_file;
			}

			addAtlas(tf, *context.prebuilt_folder + file, alpha_file, w, h);

			meta_image = meta_image.next_sibling("atlas");
			context.walker.nextMeta();
		}

		//

		std::vector<ResAnim*> anims;
	
		
		while (true)
		{
			XmlWalker walker = context.walker.next();
			if (walker.empty())
				break;

			pugi::xml_node child_node = walker.getNode();
			pugi::xml_node meta_frames = walker.getMeta();

			const char *name = child_node.name();
			if (!strcmp(name, "image"))
			{
				std::string id = child_node.attribute("id").value();
				std::string file = child_node.attribute("file").value();

				if (file.empty())
				{
					ResAnim *ra = new ResAnim(this);
					ra->init(0, 0, 0, walker.getScaleFactor());
					init_resAnim(ra, file, child_node);
					ra->setParent(this);
					context.resources->add(ra);
					continue;
				}

				if (meta)
				{
					OX_ASSERT(meta_frames && "Did you recreate atlasses?");
				}
				
				MemoryTexture mt;
				ImageData im;

				int columns = 0;
				int rows = 0;
				int frame_width = 0;
				int frame_height = 0;
				float frame_scale = 1.0f;
				bool loaded = false;

				if (meta_frames  || meta)
				{
					const char *frame_size = meta_frames.attribute("fs").value();

					//int w = 0;
					//int h = 0;

					sscanf(frame_size, "%d,%d,%d,%d,%f", &columns, &rows, 
						&frame_width, &frame_height, 
						&frame_scale);
					loaded = true;
					//frame_scale = 0.
					//frame_scale /= walker.getScaleFactor();//todo! fix

					//im.w = w;
					//im.h = h;
				}
				else
				{
					file::buffer bf;
					file::read(walker.getPath("file").c_str(), bf);

					mt.init(bf, Renderer::getPremultipliedAlphaRender(), tf);
					im = mt.lock();
					if (im.w)
					{
						rows = child_node.attribute("rows").as_int();
						frame_width = child_node.attribute("frame_width").as_int();
						columns = child_node.attribute("cols").as_int();
						frame_height = child_node.attribute("frame_height").as_int();

						if (!rows)
							rows = 1;

						if (!columns)
							columns = 1;

						if (frame_width)
							columns = im.w / frame_width;				
						else
							frame_width = im.w / columns;


						if (frame_height)				
							rows = im.h / frame_height;
						else
							frame_height = im.h / rows;				

					}
				}

				if (columns)
				{
					animationFrames frames;
					int frames_count = rows * columns;
					frames.reserve(frames_count);
					
					ResAnim *ra = new ResAnim(this);

					if (meta)
					{
						OX_ASSERT(meta_frames);

						/*
						if (string(meta_frames.attribute("debug_image").as_string()) == "backleft.png")
						{

						}
						*/

						char *frames_data = (char*)meta_frames.first_child().value();

						const char *begin = frames_data;
						while(*frames_data)
						{
							if (*frames_data == ';')
							{
								*frames_data = 0;
								int id = 0;
								int x = 0;
								int y = 0;

								int bbox_x = 0;
								int bbox_y = 0;
								int bbox_w = 0;
								int bbox_h = 0;

								sscanf(begin, "%d,%d,%d,%d,%d,%d,%d", &id, &x, &y, &bbox_x, &bbox_y, &bbox_w, &bbox_h);

								begin = frames_data + 1;

								spNativeTexture &texture = _atlasses[id].base;
								spNativeTexture &alpha = _atlasses[id].alpha;

								float iw = 1.0f / texture->getWidth();
								float ih = 1.0f / texture->getHeight();

								RectF srcRect(x * iw, y * ih, bbox_w * iw, bbox_h * ih);

								float fs = frame_scale;
								RectF destRect(
									Vector2((float)bbox_x, (float)bbox_y) * fs,
									Vector2((float)bbox_w, (float)bbox_h) * fs
									);

								AnimationFrame frame;
								Diffuse df;
								df.base = texture;
								df.alpha = alpha;
								//compressed data could not be premultiplied 
								if (Renderer::getPremultipliedAlphaRender())
									df.premultiplied = !compressed;
								else
									df.premultiplied = true;//render should think that it is already premultiplied and don't worry about alpha

								frame.init(ra, df,
									srcRect, destRect, 
									Vector2((float)frame_width, (float)frame_height));

								frames.push_back(frame);
								if((int)frames.size() >= frames_count)
									break;
							}

							++frames_data;
						}
					}
					else
					{
						anims.push_back(ra);

						for (int y = 0; y < rows; ++y)
						{
							for (int x = 0; x < columns; ++x)
							{
								Rect src;
								src.pos = Point(x * frame_width, y * frame_height);
								src.size = Point(frame_width, frame_height);

								ImageData srcImage = im.getRect(src);

								Rect dest(0,0,0,0);

								if (!ad.texture)
								{
									std::string atlas_id = getName();
									next_atlas(w, h, tf, ad, atlas_id.c_str());
								}

								bool s = ad.atlas.add(&ad.mt, srcImage, dest);
								if (s == false)
								{
									apply_atlas(ad);
									next_atlas(w, h, tf, ad, walker.getCurrentFolder().c_str());
									s = ad.atlas.add(&ad.mt, srcImage, dest);
									OX_ASSERT(s);
								}

								/*
								float iw = 1.0f / ad.mt.getWidth();
								float ih = 1.0f / ad.mt.getHeight();
								*/

								float iw = 1.0f;
								float ih = 1.0f;
								
								RectF srcRect(dest.pos.x * iw, dest.pos.y * ih, dest.size.x * iw, dest.size.y * ih);

								Vector2 sizeScaled = Vector2((float)dest.size.x, (float)dest.size.y) * walker.getScaleFactor();
								RectF destRect(Vector2(0, 0), sizeScaled);

								AnimationFrame frame;
								Diffuse df;
								df.base = ad.texture;
								df.premultiplied = true;//!Renderer::getPremultipliedAlphaRender();
								frame.init(ra, df, srcRect, destRect, Vector2((float)frame_width, (float)frame_height) * walker.getScaleFactor());
								frames.push_back(frame);
							}
						}
					}	


					
					init_resAnim(ra, file, child_node);
					
					ra->init(frames, columns, walker.getScaleFactor());					
					ra->setParent(this);
					context.resources->add(ra);
				}
			}
		}

		apply_atlas(ad);

		for (std::vector<ResAnim*>::iterator i = anims.begin(); i != anims.end(); ++i)
		{
			ResAnim *rs = *i;
			int num = rs->getTotalFrames();

			for (int n = 0; n < num; ++n)
			{
				AnimationFrame &frame = const_cast<AnimationFrame&>(rs->getFrame(n));
				
				float iw = 1.0f / frame.getDiffuse().base->getWidth();
				float ih = 1.0f / frame.getDiffuse().base->getHeight();

				RectF rect = frame.getSrcRect();
				rect.pos.x *= iw;
				rect.pos.y *= ih;
				rect.size.x *= iw;
				rect.size.y *= ih;
				frame.setSrcRect(rect);
			}
		}
	}
GLES2RendererTexture2D::GLES2RendererTexture2D(const RendererTextureDesc &desc) :
	RendererTexture2D(desc)
{
	m_textureid        = 0;
	m_glformat         = 0;
	m_glinternalformat = 0;
	m_gltype           = 0;
	m_numLevels        = 0;
	m_data             = 0;

	m_glformat         = getGLPixelFormat(desc.format);
	m_glinternalformat = getGLPixelInternalFormat(desc.format);
	m_gltype           = getGLPixelType(desc.format);

	glGenTextures(1, &m_textureid);
	RENDERER_ASSERT(m_textureid, "Failed to create OpenGL Texture.");
	if(m_textureid)
	{
		m_width     = desc.width;
		m_height    = desc.height;
		m_numLevels = desc.numLevels;
		m_data = new PxU8*[m_numLevels];
		memset(m_data, 0, sizeof(PxU8*)*m_numLevels);

		glBindTexture(GL_TEXTURE_2D, m_textureid);
		if(isCompressedFormat(desc.format))
		{
			for(PxU32 i=0; i<desc.numLevels; i++)
			{
				PxU32 w = getLevelDimension(m_width,  i);
				PxU32 h = getLevelDimension(m_height, i);
				PxU32 levelSize = computeImageByteSize(w, h, 1, desc.format);
				m_data[i]       = new PxU8[levelSize];
				memset(m_data[i], 0, levelSize);
				glCompressedTexImage2D(GL_TEXTURE_2D, (GLint)i, m_glinternalformat, w, h, 0, levelSize, m_data[i]);
			}
		}
		else
		{
			RENDERER_ASSERT(m_glformat, "Unknown OpenGL Format!");
			for(PxU32 i=0; i<desc.numLevels; i++)
			{
				PxU32 w = getLevelDimension(m_width,  i);
				PxU32 h = getLevelDimension(m_height, i);
				PxU32 levelSize = computeImageByteSize(w, h, 1, desc.format);
				m_data[i]       = new PxU8[levelSize];
				memset(m_data[i], 0, levelSize);
				glTexImage2D(GL_TEXTURE_2D, (GLint)i, m_glinternalformat, w, h, 0, m_glformat, m_gltype, m_data[i]);
			}
		}
		
		
		// set filtering mode...
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, getGLTextureFilter(desc.filter, m_numLevels > 1));
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, getGLTextureFilter(desc.filter, false));
		if(desc.filter == FILTER_ANISOTROPIC)
		{
			GLfloat maxAnisotropy = 1.0f;
			glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy);
		}
		
		// set addressing modes...
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getGLTextureAddressing(desc.addressingU));
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getGLTextureAddressing(desc.addressingV));
		
		glBindTexture(GL_TEXTURE_2D, 0);
	}
}
Esempio n. 5
0
    void Mem2Native::updateTexture()
    {
        int SIZE = _size;
        if (!SIZE)
            SIZE = RECT_SIZE;

        MemoryTexture* src = _opt->src.get();
        NativeTexture* dest = _opt->dest.get();
        Point& prev = _prev;

        bool done = false;

        if (isCompressedFormat(src->getFormat()))
        {
            dest->init(src->lock(), false);
            done = true;
        }
        else
        {

            Rect textureRect(0, 0, src->getWidth(), src->getHeight());

            if (dest->getHandle() == 0)
                dest->init(textureRect.getWidth(), textureRect.getHeight(), src->getFormat());


            Rect srcRect(prev.x, prev.y,
                         std::min(SIZE, textureRect.getWidth()), std::min(SIZE, textureRect.getHeight()));
            srcRect.clip(textureRect);


            ImageData srcim = src->lock(&srcRect);


            ImageData destim;
            if (srcRect != textureRect)
            {
                int pitch = srcRect.getWidth() * getBytesPerPixel(dest->getFormat());
                _buffer.resize(srcRect.getHeight() * pitch);
                destim = ImageData(
                             srcRect.getWidth(), srcRect.getHeight(),
                             pitch,
                             dest->getFormat(),
                             &_buffer[0]
                         );

                operations::copy(srcim, destim);
            }
            else
            {
                destim = srcim;
            }

            dest->updateRegion(srcRect.pos.x, srcRect.pos.y, destim);

            prev.x += SIZE;
            if (prev.x >= textureRect.getWidth())
            {
                prev.x = 0;
                prev.y += SIZE;
            }

            if (prev.y >= textureRect.getBottom())
            {
                _buffer.clear();
                prev = Point(0, 0);
                done = true;
            }
        }

        if (done)
        {
            textureDone();
        }
    }
    TexturesInspector::TexturesInspector(const Vector2& size)
    {
        setSize(size);

        spSlidingActor slide = new SlidingActor;
        slide->setSize(size);
        addChild(slide);


        float offsetY = 0;

        std::vector<spNativeTexture> textures = NativeTexture::getCreatedTextures();


        spTextField text = initActor(new TextField,
                                     arg_color = Color::White,
                                     arg_pos = Vector2(1, 1),
                                     arg_w = itemSize.x * 3.0f,
                                     arg_h = 30.0f,
                                     arg_vAlign = TextStyle::VALIGN_TOP,
                                     arg_hAlign = TextStyle::HALIGN_LEFT,
                                     arg_multiline = true,
                                     arg_attachTo = slide
                                    );


        offsetY += text->getTextRect().getBottom() + 5;

        spActor content = new Actor;
        content->setX(2);


        int numX = (int)(size.x / itemSize.x);

        int n = 0;
        int mem = 0;
        for (std::vector<spNativeTexture>::reverse_iterator i = textures.rbegin(); i != textures.rend(); ++i)
        {
            spNativeTexture t = *i;
            TextureLine* line = new TextureLine(t);

            float x = (n % numX) * (itemSize.x + 5.0f);
            float y = (n / numX) * (itemSize.y + 5.0f);
            line->setX(x);
            line->setY(y + offsetY);
            content->addChild(line);
            ++n;

            if (t->getHandle())
            {
                TextureFormat fmt = t->getFormat();
                int ram = t->getWidth() * t->getHeight();
                if (isCompressedFormat(fmt))
                {
                    switch (fmt)
                    {
                        case TF_PVRTC_4RGBA:
                            ram /= 2;
                            break;
                        case TF_ETC1:
                            ram /= 2;
                            break;
                        default:
                            break;
                    }
                }
                else
                    ram *= getBytesPerPixel(fmt);

                mem += ram;
            }
        }

        char txt[255];
        safe_sprintf(txt, "created textures: %d, vram: %d kb", textures.size(), mem / 1024);

        text->setText(txt);
        if (numX > n)
            numX = n;

        content->setSize(
            (itemSize.x + 5.0f) * numX,
            (itemSize.y + 5.0f) * (n + numX - 1.0f) / numX + offsetY);

        setWidth(content->getWidth());
        slide->setWidth(content->getWidth());

        //slide->setSize()
        slide->setContent(content);
    }
Esempio n. 7
0
bool GLDriver::checkActiveTextures()
{
   std::vector<uint8_t> untiledImage, untiledMipmap;
   gx2::GX2Surface surface;

   for (auto i = 0; i < latte::MaxTextures; ++i) {
      auto resourceOffset = (latte::SQ_PS_TEX_RESOURCE_0 + i) * 7;
      auto sq_tex_resource_word0 = getRegister<latte::SQ_TEX_RESOURCE_WORD0_N>(latte::Register::SQ_TEX_RESOURCE_WORD0_0 + 4 * resourceOffset);
      auto sq_tex_resource_word1 = getRegister<latte::SQ_TEX_RESOURCE_WORD1_N>(latte::Register::SQ_TEX_RESOURCE_WORD1_0 + 4 * resourceOffset);
      auto sq_tex_resource_word2 = getRegister<latte::SQ_TEX_RESOURCE_WORD2_N>(latte::Register::SQ_TEX_RESOURCE_WORD2_0 + 4 * resourceOffset);
      auto sq_tex_resource_word3 = getRegister<latte::SQ_TEX_RESOURCE_WORD3_N>(latte::Register::SQ_TEX_RESOURCE_WORD3_0 + 4 * resourceOffset);
      auto sq_tex_resource_word4 = getRegister<latte::SQ_TEX_RESOURCE_WORD4_N>(latte::Register::SQ_TEX_RESOURCE_WORD4_0 + 4 * resourceOffset);
      auto sq_tex_resource_word5 = getRegister<latte::SQ_TEX_RESOURCE_WORD5_N>(latte::Register::SQ_TEX_RESOURCE_WORD5_0 + 4 * resourceOffset);
      auto sq_tex_resource_word6 = getRegister<latte::SQ_TEX_RESOURCE_WORD6_N>(latte::Register::SQ_TEX_RESOURCE_WORD6_0 + 4 * resourceOffset);
      auto baseAddress = sq_tex_resource_word2.BASE_ADDRESS() << 8;

      if (!baseAddress) {
         continue;
      }

      if (baseAddress == mPixelTextureCache[i].baseAddress
       && sq_tex_resource_word0.value == mPixelTextureCache[i].word0
       && sq_tex_resource_word1.value == mPixelTextureCache[i].word1
       && sq_tex_resource_word2.value == mPixelTextureCache[i].word2
       && sq_tex_resource_word3.value == mPixelTextureCache[i].word3
       && sq_tex_resource_word4.value == mPixelTextureCache[i].word4
       && sq_tex_resource_word5.value == mPixelTextureCache[i].word5
       && sq_tex_resource_word6.value == mPixelTextureCache[i].word6) {
         continue;  // No change in sampler state
      }

      mPixelTextureCache[i].baseAddress = baseAddress;
      mPixelTextureCache[i].word0 = sq_tex_resource_word0.value;
      mPixelTextureCache[i].word1 = sq_tex_resource_word1.value;
      mPixelTextureCache[i].word2 = sq_tex_resource_word2.value;
      mPixelTextureCache[i].word3 = sq_tex_resource_word3.value;
      mPixelTextureCache[i].word4 = sq_tex_resource_word4.value;
      mPixelTextureCache[i].word5 = sq_tex_resource_word5.value;
      mPixelTextureCache[i].word6 = sq_tex_resource_word6.value;

      // Decode resource registers
      auto pitch = (sq_tex_resource_word0.PITCH() + 1) * 8;
      auto width = sq_tex_resource_word0.TEX_WIDTH() + 1;
      auto height = sq_tex_resource_word1.TEX_HEIGHT() + 1;
      auto depth = sq_tex_resource_word1.TEX_DEPTH() + 1;

      auto format = sq_tex_resource_word1.DATA_FORMAT();
      auto tileMode = sq_tex_resource_word0.TILE_MODE();
      auto numFormat = sq_tex_resource_word4.NUM_FORMAT_ALL();
      auto formatComp = sq_tex_resource_word4.FORMAT_COMP_X();
      auto degamma = sq_tex_resource_word4.FORCE_DEGAMMA();
      auto dim = sq_tex_resource_word0.DIM();

      auto buffer = getSurfaceBuffer(baseAddress, width, height, depth, dim, format, numFormat, formatComp, degamma, sq_tex_resource_word0.TILE_TYPE());

      if (buffer->dirtyAsTexture) {
         auto swizzle = sq_tex_resource_word2.SWIZZLE() << 8;

         // Rebuild a GX2Surface
         std::memset(&surface, 0, sizeof(gx2::GX2Surface));

         surface.dim = static_cast<gx2::GX2SurfaceDim>(dim);
         surface.width = width;
         surface.height = height;

         if (surface.dim == gx2::GX2SurfaceDim::TextureCube) {
            surface.depth = depth * 6;
         } else if (surface.dim == gx2::GX2SurfaceDim::Texture3D ||
            surface.dim == gx2::GX2SurfaceDim::Texture2DMSAAArray ||
            surface.dim == gx2::GX2SurfaceDim::Texture2DArray ||
            surface.dim == gx2::GX2SurfaceDim::Texture1DArray) {
            surface.depth = depth;
         } else {
            surface.depth = 1;
         }

         surface.mipLevels = 1;
         surface.format = getSurfaceFormat(format, numFormat, formatComp, degamma);

         surface.aa = gx2::GX2AAMode::Mode1X;
         surface.use = gx2::GX2SurfaceUse::Texture;

         if (sq_tex_resource_word0.TILE_TYPE()) {
            surface.use |= gx2::GX2SurfaceUse::DepthBuffer;
         }

         surface.tileMode = static_cast<gx2::GX2TileMode>(tileMode);
         surface.swizzle = swizzle;

         // Update the sizing information for the surface
         GX2CalcSurfaceSizeAndAlignment(&surface);

         // Align address
         baseAddress &= ~(surface.alignment - 1);

         surface.image = make_virtual_ptr<uint8_t>(baseAddress);
         surface.mipmaps = nullptr;

         // Calculate a new memory CRC
         uint64_t newHash[2] = { 0 };
         MurmurHash3_x64_128(surface.image, surface.imageSize, 0, newHash);

         // If the CPU memory has changed, we should re-upload this.  This hashing is
         //  also means that if the application temporarily uses one of its buffers as
         //  a color buffer, we are able to accurately handle this.  Providing they are
         //  not updating the memory at the same time.
         if (newHash[0] != buffer->cpuMemHash[0] || newHash[1] != buffer->cpuMemHash[1]) {
            buffer->cpuMemHash[0] = newHash[0];
            buffer->cpuMemHash[1] = newHash[1];

            // Untile
            gx2::internal::convertTiling(&surface, untiledImage, untiledMipmap);

            // Create texture
            auto compressed = isCompressedFormat(format);
            auto target = getTextureTarget(dim);
            auto textureDataType = gl::GL_INVALID_ENUM;
            auto textureFormat = getTextureFormat(format);
            auto size = untiledImage.size();

            if (compressed) {
               textureDataType = getCompressedTextureDataType(format, degamma);
            } else {
               textureDataType = getTextureDataType(format, formatComp);
            }

            if (textureDataType == gl::GL_INVALID_ENUM || textureFormat == gl::GL_INVALID_ENUM) {
               decaf_abort(fmt::format("Texture with unsupported format {}", surface.format.value()));
            }

            switch (dim) {
            case latte::SQ_TEX_DIM_1D:
               if (compressed) {
                  gl::glCompressedTextureSubImage1D(buffer->object,
                                                    0, /* level */
                                                    0, /* xoffset */
                                                    width,
                                                    textureDataType,
                                                    gsl::narrow_cast<gl::GLsizei>(size),
                                                    untiledImage.data());
               } else {
                  gl::glTextureSubImage1D(buffer->object,
                                          0, /* level */
                                          0, /* xoffset */
                                          width,
                                          textureFormat,
                                          textureDataType,
                                          untiledImage.data());
               }
               break;
            case latte::SQ_TEX_DIM_2D:
               if (compressed) {
                  gl::glCompressedTextureSubImage2D(buffer->object,
                                                    0, /* level */
                                                    0, 0, /* xoffset, yoffset */
                                                    width,
                                                    height,
                                                    textureDataType,
                                                    gsl::narrow_cast<gl::GLsizei>(size),
                                                    untiledImage.data());
               } else {
                  gl::glTextureSubImage2D(buffer->object,
                                          0, /* level */
                                          0, 0, /* xoffset, yoffset */
                                          width, height,
                                          textureFormat,
                                          textureDataType,
                                          untiledImage.data());
               }
               break;
            case latte::SQ_TEX_DIM_3D:
               if (compressed) {
                  gl::glCompressedTextureSubImage3D(buffer->object,
                                                    0, /* level */
                                                    0, 0, 0, /* xoffset, yoffset, zoffset */
                                                    width, height, depth,
                                                    textureDataType,
                                                    gsl::narrow_cast<gl::GLsizei>(size),
                                                    untiledImage.data());
               } else {
                  gl::glTextureSubImage3D(buffer->object,
                                          0, /* level */
                                          0, 0, 0, /* xoffset, yoffset, zoffset */
                                          width, height, depth,
                                          textureFormat,
                                          textureDataType,
                                          untiledImage.data());
               }
               break;
            case latte::SQ_TEX_DIM_CUBEMAP:
               decaf_check(surface.depth == 6);
            case latte::SQ_TEX_DIM_2D_ARRAY:
               if (compressed) {
                  gl::glCompressedTextureSubImage3D(buffer->object,
                                                    0, /* level */
                                                    0, 0, 0, /* xoffset, yoffset, zoffset */
                                                    width, height, surface.depth,
                                                    textureDataType,
                                                    gsl::narrow_cast<gl::GLsizei>(size),
                                                    untiledImage.data());
               } else {
                  gl::glTextureSubImage3D(buffer->object,
                                          0, /* level */
                                          0, 0, 0, /* xoffset, yoffset, zoffset */
                                          width, height, surface.depth,
                                          textureFormat,
                                          textureDataType,
                                          untiledImage.data());
               }
               break;
            default:
               decaf_abort(fmt::format("Unsupported texture dim: {}", sq_tex_resource_word0.DIM()));
            }
         }

         buffer->dirtyAsTexture = false;
         buffer->state = SurfaceUseState::CpuWritten;
      }

      // Setup texture swizzle
      auto dst_sel_x = getTextureSwizzle(sq_tex_resource_word4.DST_SEL_X());
      auto dst_sel_y = getTextureSwizzle(sq_tex_resource_word4.DST_SEL_Y());
      auto dst_sel_z = getTextureSwizzle(sq_tex_resource_word4.DST_SEL_Z());
      auto dst_sel_w = getTextureSwizzle(sq_tex_resource_word4.DST_SEL_W());

      gl::GLint textureSwizzle[] = {
         static_cast<gl::GLint>(dst_sel_x),
         static_cast<gl::GLint>(dst_sel_y),
         static_cast<gl::GLint>(dst_sel_z),
         static_cast<gl::GLint>(dst_sel_w),
      };

      gl::glTextureParameteriv(buffer->object, gl::GL_TEXTURE_SWIZZLE_RGBA, textureSwizzle);
      gl::glBindTextureUnit(i, buffer->object);
   }

   return true;
}