コード例 #1
0
ファイル: gx2r_surface.cpp プロジェクト: decaf-emu/decaf-emu
BOOL
GX2RCreateSurfaceUserMemory(virt_ptr<GX2Surface> surface,
                            virt_ptr<uint8_t> image,
                            virt_ptr<uint8_t> mipmap,
                            GX2RResourceFlags flags)
{
   GX2CalcSurfaceSizeAndAlignment(surface);
   surface->resourceFlags = flags;
   surface->resourceFlags &= ~GX2RResourceFlags::Locked;
   surface->resourceFlags &= ~GX2RResourceFlags::Gx2rAllocated;
   surface->image = image;
   surface->mipmaps = mipmap;

   if ((surface->resourceFlags & GX2RResourceFlags::UsageGpuWrite) ||
      (surface->resourceFlags & GX2RResourceFlags::UsageDmaWrite)) {
      DCInvalidateRange(virt_cast<virt_addr>(surface->image),
                        surface->imageSize);

      if (surface->mipmaps) {
         DCInvalidateRange(virt_cast<virt_addr>(surface->mipmaps),
                           surface->mipmapSize);
      }
   }

   return true;
}
コード例 #2
0
ファイル: wiiu_font.c プロジェクト: DSkywalk/RetroArch
static void* wiiu_font_init_font(void* data, const char* font_path,
      float font_size, bool is_threaded)
{
   uint32_t i;
   wiiu_font_t* font = (wiiu_font_t*)calloc(1, sizeof(*font));

   if (!font)
      return NULL;

   if (!font_renderer_create_default(
            &font->font_driver,
            &font->font_data, font_path, font_size))
   {
      RARCH_WARN("Couldn't initialize font renderer.\n");
      free(font);
      return NULL;
   }

   font->atlas = font->font_driver->get_atlas(font->font_data);
   font->texture.surface.width       = font->atlas->width;
   font->texture.surface.height      = font->atlas->height;
   font->texture.surface.depth       = 1;
   font->texture.surface.dim         = GX2_SURFACE_DIM_TEXTURE_2D;
   font->texture.surface.tileMode    = GX2_TILE_MODE_LINEAR_ALIGNED;
   font->texture.viewNumSlices       = 1;

   font->texture.surface.format   = GX2_SURFACE_FORMAT_UNORM_R8;
   font->texture.compMap          = GX2_COMP_SEL(_R, _R, _R, _R);

   GX2CalcSurfaceSizeAndAlignment(&font->texture.surface);
   GX2InitTextureRegs(&font->texture);
   font->texture.surface.image = MEM1_alloc(font->texture.surface.imageSize,
                                 font->texture.surface.alignment);

   for (i = 0; (i < font->atlas->height) && (i < font->texture.surface.height); i++)
      memcpy((uint8_t*)font->texture.surface.image + (i * font->texture.surface.pitch),
             font->atlas->buffer + (i * font->atlas->width), font->atlas->width);

   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, font->texture.surface.image,
                 font->texture.surface.imageSize);

   font->atlas->dirty = false;

   font->ubo_tex = MEM1_alloc(sizeof(*font->ubo_tex), GX2_UNIFORM_BLOCK_ALIGNMENT);
   font->ubo_tex->width = font->texture.surface.width;
   font->ubo_tex->height = font->texture.surface.height;
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_UNIFORM_BLOCK, font->ubo_tex,
                 sizeof(*font->ubo_tex));

   return font;
}
コード例 #3
0
ファイル: gx2_aperture.cpp プロジェクト: degasus/decaf-emu
   uint32_t
   allocate(GX2Surface *surface,
            uint32_t level,
            uint32_t depth,
            GX2EndianSwapMode endian)
   {
      // Find a free slot
      auto id = static_cast<uint32_t>(mActiveApertures.size());

      for (auto i = 0u; i < mActiveApertures.size(); ++i) {
         if (mActiveApertures[i].address == nullptr) {
            id = i;
            break;
         }
      }

      if (id == mActiveApertures.size()) {
         return 0;
      }

      if (!mHeap) {
         initialise();
      }

      // Allocate data
      ADDR_COMPUTE_SURFACE_INFO_OUTPUT output;
      gx2::internal::getSurfaceInfo(surface, level, &output);

      auto &aperture = mActiveApertures[id];
      aperture.address = reinterpret_cast<uint8_t *>(mHeap->alloc(output.sliceSize));
      aperture.size = output.sliceSize;
      aperture.surface = surface;
      aperture.level = level;
      aperture.depth = depth;

      // Untile existing to memory
      auto apertureSurface = GX2Surface { *surface };
      apertureSurface.mipmaps = nullptr;
      apertureSurface.image = nullptr;
      apertureSurface.tileMode = GX2TileMode::LinearSpecial;
      apertureSurface.swizzle &= 0xFFFF00FF;
      GX2CalcSurfaceSizeAndAlignment(&apertureSurface);

      gx2::internal::copySurface(surface, level, depth, &apertureSurface, level, depth, aperture.address, aperture.address);
      return id + 1;
   }
コード例 #4
0
ファイル: gx2_aperture.cpp プロジェクト: degasus/decaf-emu
   void
   free(uint32_t id)
   {
      decaf_check(id >= 1 && id <= mActiveApertures.size());
      auto &aperture = mActiveApertures[id - 1];

      // Retile to original memory
      auto apertureSurface = GX2Surface { *aperture.surface };
      apertureSurface.mipmaps = aperture.address;
      apertureSurface.image = aperture.address;
      apertureSurface.tileMode = GX2TileMode::LinearSpecial;
      apertureSurface.swizzle &= 0xFFFF00FF;
      GX2CalcSurfaceSizeAndAlignment(&apertureSurface);

      auto level = aperture.level;
      auto depth = aperture.depth;
      gx2::internal::copySurface(&apertureSurface, level, depth, aperture.surface, level, depth, aperture.surface->image, aperture.surface->mipmaps);

      mHeap->free(aperture.address);
      aperture.address = nullptr;
      aperture.size = 0;
   }
コード例 #5
0
ファイル: loader.c プロジェクト: Pizzaismyfav/libwiiu
void setupColorBuffer(GX2ColorBuffer *colorBuffer, unsigned int gx2_handle)
{
	colorBuffer->surface.surfaceUse = 0xA;
    colorBuffer->surface.dim = 1;
    colorBuffer->surface.width = 1280;
    colorBuffer->surface.height = 720;
    colorBuffer->surface.depth = 1;
    colorBuffer->surface.numMips = 1;
    colorBuffer->surface.surfaceFormat = 0x1A;
    colorBuffer->surface.aa = 0;
    colorBuffer->surface.tileMode = 0;
    colorBuffer->surface.swizzle = 0;
    colorBuffer->viewMip = 0;
    colorBuffer->viewFirstSlice = 0;
    colorBuffer->viewNumSlices = 1;
	void(*GX2CalcSurfaceSizeAndAlignment)(GX2Surface *sufcace);
	OSDynLoad_FindExport(gx2_handle, 0, "GX2CalcSurfaceSizeAndAlignment", &GX2CalcSurfaceSizeAndAlignment);
	GX2CalcSurfaceSizeAndAlignment(&colorBuffer->surface);
	void(*GX2InitColorBufferRegs)(GX2ColorBuffer *buffer);
	OSDynLoad_FindExport(gx2_handle, 0, "GX2InitColorBufferRegs", &GX2InitColorBufferRegs);
    GX2InitColorBufferRegs(colorBuffer);
	//DAMN thats dirty, ignore plz!
	colorBuffer->surface.imagePtr = (void*)((0xF6000000-colorBuffer->surface.imageSize)&(~(colorBuffer->surface.alignment-1)));
}
コード例 #6
0
ファイル: wiiu_gfx.c プロジェクト: joolswills/RetroArch
static void* wiiu_gfx_init(const video_info_t* video,
                           const input_driver_t** input, void** input_data)
{
   int i;
   *input = NULL;
   *input_data = NULL;

   wiiu_video_t* wiiu = calloc(1, sizeof(*wiiu));

   if (!wiiu)
      return NULL;

   void* wiiuinput   = NULL;
   if (input && input_data)
   {
      wiiuinput = input_wiiu.init();
      *input = wiiuinput ? &input_wiiu : NULL;
      *input_data = wiiuinput;
   }

   /* video init */
   wiiu->cmd_buffer = MEM2_alloc(0x400000, 0x40);
   u32 init_attributes[] =
   {
      GX2_INIT_CMD_BUF_BASE, (u32)wiiu->cmd_buffer,
      GX2_INIT_CMD_BUF_POOL_SIZE, 0x400000,
      GX2_INIT_ARGC, 0,
      GX2_INIT_ARGV, 0,
      GX2_INIT_END
   };
   GX2Init(init_attributes);

   /* setup scanbuffers */
   u32 size = 0;
   u32 tmp = 0;
   wiiu->render_mode = wiiu_render_mode_map[GX2GetSystemTVScanMode()];
   GX2CalcTVSize(wiiu->render_mode.mode, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8, GX2_BUFFERING_MODE_DOUBLE, &size, &tmp);
   wiiu->tv_scan_buffer = MEMBucket_alloc(size, GX2_SCAN_BUFFER_ALIGNMENT);
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU, wiiu->tv_scan_buffer, size);
   GX2SetTVBuffer(wiiu->tv_scan_buffer, size, wiiu->render_mode.mode, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8,
                  GX2_BUFFERING_MODE_DOUBLE);

   GX2CalcDRCSize(GX2_DRC_RENDER_MODE_SINGLE, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8, GX2_BUFFERING_MODE_DOUBLE, &size,
                  &tmp);
   wiiu->drc_scan_buffer = MEMBucket_alloc(size, GX2_SCAN_BUFFER_ALIGNMENT);
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU, wiiu->drc_scan_buffer, size);
   GX2SetDRCBuffer(wiiu->drc_scan_buffer, size, GX2_DRC_RENDER_MODE_SINGLE, GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8,
                   GX2_BUFFERING_MODE_DOUBLE);

   memset(&wiiu->color_buffer, 0, sizeof(GX2ColorBuffer));
   wiiu->color_buffer.surface.dim = GX2_SURFACE_DIM_TEXTURE_2D;
   wiiu->color_buffer.surface.width = wiiu->render_mode.width;
   wiiu->color_buffer.surface.height = wiiu->render_mode.height;
   wiiu->color_buffer.surface.depth = 1;
   wiiu->color_buffer.surface.mipLevels = 1;
   wiiu->color_buffer.surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
   wiiu->color_buffer.surface.use = GX2_SURFACE_USE_TEXTURE_COLOR_BUFFER_TV;
   wiiu->color_buffer.viewNumSlices = 1;
   GX2CalcSurfaceSizeAndAlignment(&wiiu->color_buffer.surface);
   GX2InitColorBufferRegs(&wiiu->color_buffer);

   wiiu->color_buffer.surface.image = MEM1_alloc(wiiu->color_buffer.surface.imageSize,
                                      wiiu->color_buffer.surface.alignment);
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU, wiiu->color_buffer.surface.image, wiiu->color_buffer.surface.imageSize);

   wiiu->ctx_state = (GX2ContextState*)MEM2_alloc(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT);
   GX2SetupContextStateEx(wiiu->ctx_state, GX2_TRUE);

   GX2SetContextState(wiiu->ctx_state);
   GX2SetColorBuffer(&wiiu->color_buffer, GX2_RENDER_TARGET_0);
   GX2SetViewport(0.0f, 0.0f, wiiu->color_buffer.surface.width, wiiu->color_buffer.surface.height, 0.0f, 1.0f);
   GX2SetScissor(0, 0, wiiu->color_buffer.surface.width, wiiu->color_buffer.surface.height);
   GX2SetDepthOnlyControl(GX2_DISABLE, GX2_DISABLE, GX2_COMPARE_FUNC_ALWAYS);
   GX2SetColorControl(GX2_LOGIC_OP_COPY, 1, GX2_DISABLE, GX2_ENABLE);
#if 1
   GX2SetBlendControl(GX2_RENDER_TARGET_0, GX2_BLEND_MODE_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD,
                      GX2_ENABLE,          GX2_BLEND_MODE_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD);
#else
   GX2SetBlendControl(GX2_RENDER_TARGET_0, GX2_BLEND_MODE_ONE, GX2_BLEND_MODE_ZERO, GX2_BLEND_COMBINE_MODE_ADD,
                      GX2_DISABLE,         GX2_BLEND_MODE_ONE, GX2_BLEND_MODE_ZERO, GX2_BLEND_COMBINE_MODE_ADD);
#endif
   GX2SetCullOnlyControl(GX2_FRONT_FACE_CCW, GX2_DISABLE, GX2_DISABLE);
#ifdef GX2_CAN_ACCESS_DATA_SECTION
   wiiu->shader = &tex_shader;
#else
   /* init shader */
   //   wiiu->shader = MEM2_alloc(sizeof(*wiiu->shader), GX2_VERTEX_BUFFER_ALIGNMENT);
   wiiu->shader = MEM2_alloc(sizeof(tex_shader), 0x1000);
   memcpy(wiiu->shader, &tex_shader, sizeof(tex_shader));
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU, wiiu->shader, sizeof(tex_shader));

   wiiu->shader->vs.program = MEM2_alloc(wiiu->shader->vs.size, GX2_SHADER_ALIGNMENT);
   memcpy(wiiu->shader->vs.program, tex_shader.vs.program, wiiu->shader->vs.size);
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, wiiu->shader->vs.program, wiiu->shader->vs.size);
   wiiu->shader->vs.attribVars = MEM2_alloc(wiiu->shader->vs.attribVarCount * sizeof(GX2AttribVar),
         GX2_SHADER_ALIGNMENT);
   memcpy(wiiu->shader->vs.attribVars, tex_shader.vs.attribVars ,
          wiiu->shader->vs.attribVarCount * sizeof(GX2AttribVar));

   wiiu->shader->ps.program = MEM2_alloc(wiiu->shader->ps.size, GX2_SHADER_ALIGNMENT);
   memcpy(wiiu->shader->ps.program, tex_shader.ps.program, wiiu->shader->ps.size);
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, wiiu->shader->ps.program, wiiu->shader->ps.size);
   wiiu->shader->ps.samplerVars = MEM2_alloc(wiiu->shader->ps.samplerVarCount * sizeof(GX2SamplerVar),
         GX2_SHADER_ALIGNMENT);
   memcpy(wiiu->shader->ps.samplerVars, tex_shader.ps.samplerVars,
          wiiu->shader->ps.samplerVarCount * sizeof(GX2SamplerVar));

#endif
   wiiu->shader->fs.size = GX2CalcFetchShaderSizeEx(2, GX2_FETCH_SHADER_TESSELLATION_NONE, GX2_TESSELLATION_MODE_DISCRETE);
   wiiu->shader->fs.program = MEM2_alloc(wiiu->shader->fs.size, GX2_SHADER_ALIGNMENT);
   GX2InitFetchShaderEx(&wiiu->shader->fs, (uint8_t*)wiiu->shader->fs.program,
                        sizeof(wiiu->shader->attribute_stream) /  sizeof(GX2AttribStream),
                        (GX2AttribStream*)&wiiu->shader->attribute_stream,
                        GX2_FETCH_SHADER_TESSELLATION_NONE, GX2_TESSELLATION_MODE_DISCRETE);
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, wiiu->shader->fs.program, wiiu->shader->fs.size);
   GX2SetVertexShader(&wiiu->shader->vs);
   GX2SetPixelShader(&wiiu->shader->ps);
   GX2SetFetchShader(&wiiu->shader->fs);

   wiiu->position = MEM2_alloc(4 * sizeof(*wiiu->position), GX2_VERTEX_BUFFER_ALIGNMENT);
   wiiu_set_position(wiiu->position, &wiiu->color_buffer, 0, 0, wiiu->color_buffer.surface.width, wiiu->color_buffer.surface.height);

   wiiu->tex_coord = MEM2_alloc(4 * sizeof(*wiiu->tex_coord), GX2_VERTEX_BUFFER_ALIGNMENT);
   wiiu_set_tex_coords(wiiu->tex_coord, &wiiu->texture, 0, 0, wiiu->texture.surface.width, wiiu->texture.surface.height);

   GX2SetAttribBuffer(0, 4 * sizeof(*wiiu->position), sizeof(*wiiu->position), wiiu->position);
   GX2SetAttribBuffer(1, 4 * sizeof(*wiiu->tex_coord), sizeof(*wiiu->tex_coord), wiiu->tex_coord);

   wiiu->menu.position = MEM2_alloc(4 * sizeof(*wiiu->menu.position), GX2_VERTEX_BUFFER_ALIGNMENT);
   wiiu_set_position(wiiu->menu.position, &wiiu->color_buffer, 0, 0, wiiu->color_buffer.surface.width, wiiu->color_buffer.surface.height);

   wiiu->menu.tex_coord = MEM2_alloc(4 * sizeof(*wiiu->menu.tex_coord), GX2_VERTEX_BUFFER_ALIGNMENT);
   wiiu_set_tex_coords(wiiu->menu.tex_coord, &wiiu->menu.texture, 0, 0, wiiu->menu.texture.surface.width, wiiu->menu.texture.surface.height);

   /* init frame texture */
   memset(&wiiu->texture, 0, sizeof(GX2Texture));
   wiiu->texture.surface.width    = video->input_scale * RARCH_SCALE_BASE;
   wiiu->texture.surface.height   = video->input_scale * RARCH_SCALE_BASE;
   wiiu->texture.surface.depth    = 1;
   wiiu->texture.surface.dim      = GX2_SURFACE_DIM_TEXTURE_2D;
   wiiu->texture.surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED;
   wiiu->texture.viewNumSlices    = 1;
   wiiu->rgb32 = video->rgb32;
   if(wiiu->rgb32)
   {
      wiiu->texture.surface.format   = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
      wiiu->texture.compMap          = GX2_COMP_SEL(_G, _B, _A, _1);
   }
   else
   {
      wiiu->texture.surface.format   = GX2_SURFACE_FORMAT_UNORM_R5_G6_B5;
      wiiu->texture.compMap          = GX2_COMP_SEL(_B, _G, _R, _1);
   }
   GX2CalcSurfaceSizeAndAlignment(&wiiu->texture.surface);
   GX2InitTextureRegs(&wiiu->texture);

   wiiu->texture.surface.image = MEM2_alloc(wiiu->texture.surface.imageSize,
                                 wiiu->texture.surface.alignment);
   memset(wiiu->texture.surface.image, 0x0, wiiu->texture.surface.imageSize);
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, wiiu->texture.surface.image,
                 wiiu->texture.surface.imageSize);

   /* init menu texture */
   memset(&wiiu->menu.texture, 0, sizeof(GX2Texture));
   wiiu->menu.texture.surface.width    = 512;
   wiiu->menu.texture.surface.height   = 512;
   wiiu->menu.texture.surface.depth    = 1;
   wiiu->menu.texture.surface.dim      = GX2_SURFACE_DIM_TEXTURE_2D;
   wiiu->menu.texture.surface.format   = GX2_SURFACE_FORMAT_UNORM_R4_G4_B4_A4;
   wiiu->menu.texture.surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED;
   wiiu->menu.texture.viewNumSlices    = 1;
   wiiu->menu.texture.compMap          = GX2_COMP_SEL(_A, _R, _G, _B);
   GX2CalcSurfaceSizeAndAlignment(&wiiu->menu.texture.surface);
   GX2InitTextureRegs(&wiiu->menu.texture);

   wiiu->menu.texture.surface.image = MEM2_alloc(wiiu->menu.texture.surface.imageSize,
                                      wiiu->menu.texture.surface.alignment);

   memset(wiiu->menu.texture.surface.image, 0x0, wiiu->menu.texture.surface.imageSize);
   GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, wiiu->menu.texture.surface.image,
                 wiiu->menu.texture.surface.imageSize);

   /* init samplers */
   GX2InitSampler(&wiiu->sampler_nearest, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_POINT);
   GX2InitSampler(&wiiu->sampler_linear, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_LINEAR);

   /* set Texture and Sampler */
   GX2SetPixelTexture(&wiiu->texture, wiiu->shader->sampler.location);
   GX2SetPixelSampler(&wiiu->sampler_linear, wiiu->shader->sampler.location);

   /* clear leftover image */
   GX2ClearColor(&wiiu->color_buffer, 0.0f, 0.0f, 0.0f, 1.0f);
   GX2CopyColorBufferToScanBuffer(&wiiu->color_buffer, GX2_SCAN_TARGET_DRC);
   GX2CopyColorBufferToScanBuffer(&wiiu->color_buffer, GX2_SCAN_TARGET_TV);

   GX2SwapScanBuffers();
   GX2Flush();
   GX2WaitForVsync();

   GX2SetTVEnable(GX2_ENABLE);
   GX2SetDRCEnable(GX2_ENABLE);

   wiiu->keep_aspect   = true;
   wiiu->should_resize = true;
   wiiu->smooth        = video->smooth;
   wiiu->vsync         = video->vsync;
   GX2SetSwapInterval(!!video->vsync);

   wiiu->vp.x           = 0;
   wiiu->vp.y           = 0;
   wiiu->vp.width       = 854;
   wiiu->vp.height      = 480;
   wiiu->vp.full_width  = 854;
   wiiu->vp.full_height = 480;
   video_driver_set_size(&wiiu->vp.width, &wiiu->vp.height);

   float refresh_rate = 60.0f / 1.001f;
   driver_ctl(RARCH_DRIVER_CTL_SET_REFRESH_RATE, &refresh_rate);

   return wiiu;
}
コード例 #7
0
ファイル: opengl_texture.cpp プロジェクト: degasus/decaf-emu
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;
}