#include "gles.h" #include "rend/TexCache.h" #include "hw/pvr/pvr_mem.h" /* Textures Textures are converted to native OpenGL textures The mapping is done with tcw:tsp -> GL texture. That includes stuff like filtering/ texture repeat To save space native formats are used for 1555/565/4444 (only bit shuffling is done) YUV is converted to 565 (some loss of quality on that) PALs are decoded to their unpaletted format, 8888 is downcasted to 4444 Mipmaps not supported for now Compression look into it, but afaik PVRC is not realtime doable */ #if FEAT_HAS_SOFTREND #include #endif u16 temp_tex_buffer[1024*1024]; extern u32 decoded_colors[3][65536]; typedef void TexConvFP(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height); struct PvrTexInfo { const char* name; int bpp; //4/8 for pal. 16 for uv, argb GLuint type; TexConvFP *PL; TexConvFP *TW; TexConvFP *VQ; }; PvrTexInfo format[8]= { {"1555", 16,GL_UNSIGNED_SHORT_5_5_5_1, &tex1555_PL,&tex1555_TW,&tex1555_VQ}, //1555 {"565", 16,GL_UNSIGNED_SHORT_5_6_5, &tex565_PL,&tex565_TW,&tex565_VQ}, //565 {"4444", 16,GL_UNSIGNED_SHORT_4_4_4_4, &tex4444_PL,&tex4444_TW,&tex4444_VQ}, //4444 {"yuv", 16,GL_UNSIGNED_SHORT_5_6_5, &texYUV422_PL,&texYUV422_TW,&texYUV422_VQ}, //yuv {"UNSUPPORTED BUMP MAPPED POLY", 16,GL_UNSIGNED_SHORT_4_4_4_4,&texBMP_PL,&texBMP_TW,&texBMP_VQ}, //bump_ns {"pal4", 4,0,0,texPAL4_TW,0}, //pal4 {"pla8", 8,0,0,texPAL8_TW,0}, //pal8 {"ns/1555", 0}, //ns, 1555 }; const u32 MipPoint[8] = { 0x00006,//8 0x00016,//16 0x00056,//32 0x00156,//64 0x00556,//128 0x01556,//256 0x05556,//512 0x15556//1024 }; const GLuint PAL_TYPE[4]= {GL_UNSIGNED_SHORT_5_5_5_1,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4,GL_UNSIGNED_SHORT_4_4_4_4}; //Texture Cache :) struct TextureCacheData { TSP tsp; //dreamcast texture parameters TCW tcw; GLuint texID; //gl texture u16* pData; int tex_type; u32 Lookups; //decoded texture info u32 sa; //pixel data start address in vram (might be offset for mipmaps/etc) u32 sa_tex; //texture data start address in vram u32 w,h; //width & height of the texture u32 size; //size, in bytes, in vram PvrTexInfo* tex; TexConvFP* texconv; u32 dirty; vram_block* lock_block; u32 Updates; //used for palette updates u32 pal_local_rev; //local palette rev u32* pal_table_rev; //table palette rev pointer u32 indirect_color_ptr; //palette color table index for pal. tex //VQ quantizers table for VQ tex //a texture can't be both VQ and PAL at the same time void PrintTextureName() { printf("Texture: %s ",tex?tex->name:"?format?"); if (tcw.VQ_Comp) printf(" VQ"); if (tcw.ScanOrder==0) printf(" TW"); if (tcw.MipMapped) printf(" MM"); if (tcw.StrideSel) printf(" Stride"); printf(" %dx%d @ 0x%X",8<bpp==4) { pal_table_rev=&pal_rev_16[tcw.PalSelect]; indirect_color_ptr=tcw.PalSelect<<4; } else if (tex->bpp==8) { pal_table_rev=&pal_rev_256[tcw.PalSelect>>4]; indirect_color_ptr=(tcw.PalSelect>>4)<<8; } else { pal_table_rev=0; } //VQ table (if VQ tex) if (tcw.VQ_Comp) indirect_color_ptr=sa; //Convert a pvr texture into OpenGL switch (tcw.PixelFmt) { case 0: //0 1555 value: 1 bit; RGB values: 5 bits each case 7: //7 Reserved Regarded as 1555 case 1: //1 565 R value: 5 bits; G value: 6 bits; B value: 5 bits case 2: //2 4444 value: 4 bits; RGB values: 4 bits each case 3: //3 YUV422 32 bits per 2 pixels; YUYV values: 8 bits each case 4: //4 -NOT_PROPERLY SUPPORTED- Bump Map 16 bits/pixel; S value: 8 bits; R value: 8 bits -NOT_PROPERLY SUPPORTED- case 5: //5 4 BPP Palette Palette texture with 4 bits/pixel case 6: //6 8 BPP Palette Palette texture with 8 bits/pixel if (tcw.ScanOrder && tex->PL) { //Texture is stored 'planar' in memory, no deswizzle is needed verify(tcw.VQ_Comp==0); //Planar textures support stride selection, mostly used for non power of 2 textures (videos) int stride=w; if (tcw.StrideSel) stride=(TEXT_CONTROL&31)*32; //Call the format specific conversion code texconv=tex->PL; //calculate the size, in bytes, for the locking size=stride*h*tex->bpp/8; } else { verify(w==h || !tcw.MipMapped); // are non square mipmaps supported ? i can't recall right now *WARN* if (tcw.VQ_Comp) { verify(tex->VQ!=0); indirect_color_ptr=sa; if (tcw.MipMapped) sa+=MipPoint[tsp.TexU]; texconv=tex->VQ; size=w*h/8; } else { verify(tex->TW!=0) if (tcw.MipMapped) sa+=MipPoint[tsp.TexU]*tex->bpp/2; texconv=tex->TW; size=w*h*tex->bpp/8; } } break; default: printf("Unhandled texture %d\n",tcw.PixelFmt); size=w*h*2; memset(temp_tex_buffer,0xFFFFFFFF,size); texconv=0; } } void Update() { //texture state tracking stuff Updates++; dirty=0; GLuint textype=tex->type; if (pal_table_rev) { textype=PAL_TYPE[PAL_RAM_CTRL&3]; pal_local_rev=*pal_table_rev; //make sure to update the local rev, so it won't have to redo the tex } palette_index=indirect_color_ptr; //might be used if pal. tex vq_codebook=(u8*)&vram[indirect_color_ptr]; //might be used if VQ tex //texture conversion work PixelBuffer pbt; pbt.p_buffer_start=pbt.p_current_line=temp_tex_buffer; pbt.pixels_per_line=w; u32 stride=w; if (tcw.StrideSel && tcw.ScanOrder && tex->PL) stride=(TEXT_CONTROL&31)*32; //I think this needs +1 ? if(texconv!=0) { texconv(&pbt,(u8*)&vram[sa],stride,h); } else { //fill it in with a temp color printf("UNHANDLED TEXTURE\n"); memset(temp_tex_buffer,0xF88F8F7F,w*h*2); } //PrintTextureName(); //lock the texture to detect changes in it lock_block = libCore_vramlock_Lock(sa_tex,sa+size-1,this); if (texID) { //upload to OpenGL ! glBindTexture(GL_TEXTURE_2D, texID); GLuint comps=textype==GL_UNSIGNED_SHORT_5_6_5?GL_RGB:GL_RGBA; glTexImage2D(GL_TEXTURE_2D, 0,comps , w, h, 0, comps, textype, temp_tex_buffer); if (tcw.MipMapped && settings.rend.UseMipmaps) glGenerateMipmap(GL_TEXTURE_2D); } else { #if FEAT_HAS_SOFTREND if (textype == GL_UNSIGNED_SHORT_5_6_5) tex_type = 0; else if (textype == GL_UNSIGNED_SHORT_5_5_5_1) tex_type = 1; else if (textype == GL_UNSIGNED_SHORT_4_4_4_4) tex_type = 2; if (pData) { _mm_free(pData); } pData = (u16*)_mm_malloc(w * h * 16, 16); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { u32* data = (u32*)&pData[(x + y*w) * 8]; data[0] = decoded_colors[tex_type][temp_tex_buffer[(x + 1) % w + (y + 1) % h * w]]; data[1] = decoded_colors[tex_type][temp_tex_buffer[(x + 0) % w + (y + 1) % h * w]]; data[2] = decoded_colors[tex_type][temp_tex_buffer[(x + 1) % w + (y + 0) % h * w]]; data[3] = decoded_colors[tex_type][temp_tex_buffer[(x + 0) % w + (y + 0) % h * w]]; } } #else die("Soft rend disabled, invalid code path"); #endif } } //true if : dirty or paletted texture and revs don't match bool NeedsUpdate() { return (dirty) || (pal_table_rev!=0 && *pal_table_rev!=pal_local_rev); } void Delete() { if (pData) { #if FEAT_HAS_SOFTREND _mm_free(pData); pData = 0; #else die("softrend disabled, invalid codepath"); #endif } if (texID) { glDeleteTextures(1, &texID); } if (lock_block) libCore_vramlock_Unlock_block(lock_block); lock_block=0; } }; #include map TexCache; typedef map::iterator TexCacheIter; //TexCacheList TexCache; struct FBT { u32 TexAddr; GLuint depthb,stencilb; GLuint tex; GLuint fbo; }; FBT fb_rtt; void BindRTT(u32 addy, u32 fbw, u32 fbh, u32 channels, u32 fmt) { FBT& rv=fb_rtt; if (rv.fbo) glDeleteFramebuffers(1,&rv.fbo); if (rv.tex) glDeleteTextures(1,&rv.tex); if (rv.depthb) glDeleteRenderbuffers(1,&rv.depthb); if (rv.stencilb) glDeleteRenderbuffers(1,&rv.stencilb); rv.TexAddr=addy>>3; // Find the largest square power of two texture that fits into the viewport // Get the currently bound frame buffer object. On most platforms this just gives 0. //glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_i32OriginalFbo); // Generate and bind a render buffer which will become a depth buffer shared between our two FBOs glGenRenderbuffers(1, &rv.depthb); glBindRenderbuffer(GL_RENDERBUFFER, rv.depthb); /* Currently it is unknown to GL that we want our new render buffer to be a depth buffer. glRenderbufferStorage will fix this and in this case will allocate a depth buffer m_i32TexSize by m_i32TexSize. */ #ifdef GLES glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, fbw, fbh); #else glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, fbw, fbh); #endif glGenRenderbuffers(1, &rv.stencilb); glBindRenderbuffer(GL_RENDERBUFFER, rv.stencilb); glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fbw, fbh); // Create a texture for rendering to glGenTextures(1, &rv.tex); glBindTexture(GL_TEXTURE_2D, rv.tex); glTexImage2D(GL_TEXTURE_2D, 0, channels, fbw, fbh, 0, channels, fmt, 0); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Create the object that will allow us to render to the aforementioned texture glGenFramebuffers(1, &rv.fbo); glBindFramebuffer(GL_FRAMEBUFFER, rv.fbo); // Attach the texture to the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rv.tex, 0); // Attach the depth buffer we created earlier to our FBO. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rv.depthb); // Check that our FBO creation was successful GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); verify(uStatus == GL_FRAMEBUFFER_COMPLETE); } GLuint gl_GetTexture(TSP tsp, TCW tcw) { if (tcw.TexAddr==fb_rtt.TexAddr && fb_rtt.tex) { return fb_rtt.tex; } //lookup texture TextureCacheData* tf; //= TexCache.Find(tcw.full,tsp.full); u64 key=((u64)tcw.full<<32) | tsp.full; TexCacheIter tx=TexCache.find(key); if (tx!=TexCache.end()) { tf=&tx->second; } else //create if not existing { TextureCacheData tfc={0}; TexCache[key]=tfc; tx=TexCache.find(key); tf=&tx->second; tf->tsp=tsp; tf->tcw=tcw; tf->Create(true); } //update if needed if (tf->NeedsUpdate()) tf->Update(); //update state for opts/stuff tf->Lookups++; //return gl texture return tf->texID; } text_info raw_GetTexture(TSP tsp, TCW tcw) { text_info rv = { 0 }; //lookup texture TextureCacheData* tf; //= TexCache.Find(tcw.full,tsp.full); u64 key = ((u64)tcw.full << 32) | tsp.full; TexCacheIter tx = TexCache.find(key); if (tx != TexCache.end()) { tf = &tx->second; } else //create if not existing { TextureCacheData tfc = { 0 }; TexCache[key] = tfc; tx = TexCache.find(key); tf = &tx->second; tf->tsp = tsp; tf->tcw = tcw; tf->Create(false); } //update if needed if (tf->NeedsUpdate()) tf->Update(); //update state for opts/stuff tf->Lookups++; //return gl texture rv.height = tf->h; rv.width = tf->w; rv.pdata = tf->pData; rv.textype = tf->tex_type; return rv; } void CollectCleanup() { vector list; u32 TargetFrame = max((u32)120,FrameCount) - 120; for (TexCacheIter i=TexCache.begin();i!=TexCache.end();i++) { if ( i->second.dirty && i->second.dirty < TargetFrame) { list.push_back(i->first); } if (list.size() > 5) break; } for (size_t i=0; isecond.Delete(); } TexCache.clear(); } void rend_text_invl(vram_block* bl) { TextureCacheData* tcd = (TextureCacheData*)bl->userdata; tcd->dirty=FrameCount; tcd->lock_block=0; libCore_vramlock_Unlock_block_wb(bl); }