diff --git a/core/hw/pvr/ta_ctx.cpp b/core/hw/pvr/ta_ctx.cpp index f7a3b736c..1115a1849 100644 --- a/core/hw/pvr/ta_ctx.cpp +++ b/core/hw/pvr/ta_ctx.cpp @@ -146,14 +146,15 @@ bool QueueRender(TA_context* ctx) bool too_fast = (cycle_span / time_span) > (SH4_MAIN_CLOCK * 1.2); - if (rqueue && too_fast && settings.pvr.SynchronousRender) { + if ((rqueue && too_fast && settings.pvr.SynchronousRender) || + (settings.dreamcast.rttOption != 0 && rqueue && ctx->rend.isRTT)) { //wait for a frame if // we have another one queue'd and // sh4 run at > 120% on the last slice // and SynchronousRendering is enabled frame_finished.Wait(); verify(!rqueue); - } + } if (rqueue) { tactx_Recycle(ctx); diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 95e5a3aef..2e10c840f 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -292,6 +292,7 @@ void LoadSettings() settings.dreamcast.RTC = cfgLoadInt("config", "Dreamcast.RTC", GetRTC_now()); settings.dreamcast.region = cfgLoadInt("config", "Dreamcast.Region", 3); settings.dreamcast.broadcast = cfgLoadInt("config", "Dreamcast.Broadcast", 4); + settings.dreamcast.rttOption = cfgLoadInt("config", "Dreamcast.Rtt", 0); settings.aica.LimitFPS = cfgLoadInt("config", "aica.LimitFPS", 1); settings.aica.NoBatch = cfgLoadInt("config", "aica.NoBatch", 0); settings.aica.NoSound = cfgLoadInt("config", "aica.NoSound", 0); @@ -390,4 +391,5 @@ void SaveSettings() cfgSaveInt("config","Dreamcast.RTC", settings.dreamcast.RTC); cfgSaveInt("config","Dreamcast.Region", settings.dreamcast.region); cfgSaveInt("config","Dreamcast.Broadcast", settings.dreamcast.broadcast); + cfgSaveInt("config","Dreamcast.Rtt", settings.dreamcast.rttOption); } diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index 54a3df6b0..6b3d438eb 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -146,15 +146,18 @@ s32 SetTileClip(u32 val, bool set) return 0; if (set && clip_mode) { - csy = 480 - csy; - cey = 480 - cey; - float dc2s_scale_h = screen_height / 480.0f; - float ds2s_offs_x = (screen_width - dc2s_scale_h * 640) / 2; - csx = csx * dc2s_scale_h + ds2s_offs_x; - cex = cex * dc2s_scale_h + ds2s_offs_x; - csy = csy * dc2s_scale_h; - cey = cey * dc2s_scale_h; - glUniform4f(CurrentShader->pp_ClipTest, csx, cey, cex, csy); + if (!pvrrc.isRTT) { + float t = cey; + cey = 480 - csy; + csy = 480 - t; + float dc2s_scale_h = screen_height / 480.0f; + float ds2s_offs_x = (screen_width - dc2s_scale_h * 640) / 2; + csx = csx * dc2s_scale_h + ds2s_offs_x; + cex = cex * dc2s_scale_h + ds2s_offs_x; + csy = csy * dc2s_scale_h; + cey = cey * dc2s_scale_h; + } + glUniform4f(CurrentShader->pp_ClipTest, csx, csy, cex, cey); } return clip_mode; @@ -1172,11 +1175,18 @@ void fullscreenQuadPrepareFramebuffer(float xScale, float yScale) { // Reduce width to keep 4:3 aspect ratio (640x480) u32 reducedWidth = 4 * screen_height / 3; - u32 reducedWidthOffset = (screen_width - reducedWidth) / 2; - glScissor(reducedWidthOffset, 0, reducedWidth, screen_height); - if (settings.rend.WideScreen && - (pvrrc.fb_X_CLIP.min==0) && ((pvrrc.fb_X_CLIP.max+1)/xScale==640) && - (pvrrc.fb_Y_CLIP.min==0) && ((pvrrc.fb_Y_CLIP.max+1)/yScale==480 )) + + // handle odd/even screen width + if (screen_width % 2 == 0) { + u32 reducedWidthOffset = (screen_width - reducedWidth) / 2; + glScissor(reducedWidthOffset, 0, reducedWidth, screen_height); + } + else { + u32 reducedWidthOffset = (screen_width + 1 - reducedWidth) / 2; + glScissor(reducedWidthOffset, 0, reducedWidth, screen_height); + } + + if (settings.rend.WideScreen) { glDisable(GL_SCISSOR_TEST); } diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 15fae4dac..5d07d6a8a 100755 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1,4 +1,5 @@ #include +#include #include "gles.h" #include "rend/TexCache.h" #include "cfg/cfg.h" @@ -177,10 +178,10 @@ const char* VertexShaderSource = #endif - - - - +//0 - not in use +//1 - in use since the last frame +u8 rttInUse = 0; +u32 rttDepthCounter = 0; /* @@ -482,10 +483,17 @@ gl_ctx gl; int screen_width; int screen_height; -GLuint fogTextureId; +int currentScreenWidth = -1; +int currentScreenHeight = -1; + +GLuint fogTextureId; GLFramebufferData fullscreenQuad; +bool isExtensionSupported(const char * name) { + return strstr((const char *)glGetString(GL_EXTENSIONS), name) != NULL; +} + #if (HOST_OS != OS_DARWIN) && !defined(TARGET_NACL32) #if defined(GLES) && !defined(USE_SDL) // Create a basic GLES context @@ -555,6 +563,8 @@ GLFramebufferData fullscreenQuad; screen_width=w; screen_height=h; + gl.renderer = (char *)glGetString(GL_RENDERER); + printf("EGL config: %p, %08X, %08X %dx%d\n",gl.setup.context,gl.setup.display,gl.setup.surface,w,h); return true; } @@ -1209,6 +1219,8 @@ bool gles_init() if (!gl_create_resources()) return false; + InitShadowCircle(); + #if defined(GLES) && HOST_OS != OS_DARWIN && !defined(TARGET_NACL32) #ifdef TARGET_PANDORA fbdev=open("/dev/fb0", O_RDONLY); @@ -1228,13 +1240,6 @@ bool gles_init() return true; } -bool isExtensionSupported(const char * name) { - if (!strstr((const char *)glGetString(GL_EXTENSIONS), name)) { - return false; - } - return true; -} - void UpdateFogTexture(u8 *fog_table, GLenum texture_slot, GLint fog_image_format) { glActiveTexture(texture_slot); @@ -1635,15 +1640,16 @@ void fullscreenQuadCreateTemporaryFBO(float & screenToNativeXScale, float & scre // Generate and bind a render buffer which will become a depth buffer if (!fullscreenQuad.framebufferRenderbuffer) { glGenRenderbuffers(1, &fullscreenQuad.framebufferRenderbuffer); + } + if (currentScreenWidth != screen_width || currentScreenHeight != screen_height) { + glBindRenderbuffer(GL_RENDERBUFFER, fullscreenQuad.framebufferRenderbuffer); #ifdef GLES if (isExtensionSupported("GL_OES_packed_depth_stencil")) { glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, screen_width, screen_height); - } - else if (isExtensionSupported("GL_OES_depth24")) { + } else if (isExtensionSupported("GL_OES_depth24")) { glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, screen_width, screen_height); - } - else { + } else { glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, screen_width, screen_height); } #else @@ -1662,6 +1668,10 @@ void fullscreenQuadCreateTemporaryFBO(float & screenToNativeXScale, float & scre glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); } + if (currentScreenWidth != screen_width || currentScreenHeight != screen_height) { + glBindTexture(GL_TEXTURE_2D, fullscreenQuad.framebufferTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screen_width, screen_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } // Create the object that will allow us to render to the aforementioned texture (one for every rtt texture address) if (!fullscreenQuad.framebuffer) { @@ -1689,9 +1699,10 @@ void fullscreenQuadCreateTemporaryFBO(float & screenToNativeXScale, float & scre bool ProcessFrame(TA_context* ctx) { - //disable RTTs for now .. - if (ctx->rend.isRTT) + if (ctx->rend.isRTT && settings.dreamcast.rttOption == Disabled) + { return false; + } ctx->rend_inuse.Lock(); ctx->MarkRend(); @@ -1830,16 +1841,8 @@ bool RenderFrame() { gcflip=1; - //For some reason this produces wrong results - //so for now its hacked based like on the d3d code - /* - dc_width=FB_X_CLIP.max-FB_X_CLIP.min+1; - dc_height=FB_Y_CLIP.max-FB_Y_CLIP.min+1; - u32 pvr_stride=(FB_W_LINESTRIDE.stride)*8; - */ - - dc_width=640; - dc_height=480; + dc_width = FB_W_LINESTRIDE.stride ? FB_W_LINESTRIDE.stride * 4 : FB_X_CLIP.max - FB_X_CLIP.min + 1; + dc_height = FB_Y_CLIP.max - FB_Y_CLIP.min + 1; } scale_x = 1; @@ -1899,16 +1902,32 @@ bool RenderFrame() /* Handle Dc to screen scaling */ - float dc2s_scale_h=screen_height/480.0f; - float ds2s_offs_x=(screen_width-dc2s_scale_h*640)/2; + float dc2s_scale_h = screen_height / 480.0; + float ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0) / 2; + // handle odd screen width + if (screen_width % 2) { + ds2s_offs_x = (screen_width + 1 - dc2s_scale_h * 640.0) / 2; + } + + if (!is_rtt) { + ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x); + ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; + } else { + if (dc_width == (FB_X_CLIP.max - FB_X_CLIP.min + 1)) { + ShaderUniforms.scale_coefs[0] = 2.0f / (dc_width * scale_x); + } + else { //is stride + ShaderUniforms.scale_coefs[0] = 2.0f / ((FB_X_CLIP.max - FB_X_CLIP.min + 1) * scale_x); + } + dc2s_scale_h = screen_width / dc_width; + ds2s_offs_x = 0; + ShaderUniforms.scale_coefs[2] = 1; + } //-1 -> too much to left - ShaderUniforms.scale_coefs[0]=2.0f/(screen_width/dc2s_scale_h*scale_x); ShaderUniforms.scale_coefs[1]=(is_rtt?2:-2)/dc_height; - ShaderUniforms.scale_coefs[2]=1-2*ds2s_offs_x/(screen_width); ShaderUniforms.scale_coefs[3]=(is_rtt?1:-1); - ShaderUniforms.depth_coefs[0]=2/(vtx_max_fZ-vtx_min_fZ); ShaderUniforms.depth_coefs[1]=-vtx_min_fZ-1; ShaderUniforms.depth_coefs[2]=0; @@ -1916,7 +1935,6 @@ bool RenderFrame() //printf("scale: %f, %f, %f, %f\n",scale_coefs[0],scale_coefs[1],scale_coefs[2],scale_coefs[3]); - //VERT and RAM fog color constants u8* fog_colvert_bgra=(u8*)&FOG_COL_VERT; u8* fog_colram_bgra=(u8*)&FOG_COL_RAM; @@ -1945,7 +1963,6 @@ bool RenderFrame() glUniform4fv( gl.modvol_shader.scale, 1, ShaderUniforms.scale_coefs); glUniform4fv( gl.modvol_shader.depth_scale, 1, ShaderUniforms.depth_coefs); - GLfloat td[4]={0.5,0,0,0}; glUseProgram(gl.OSD_SHADER.program); @@ -1964,61 +1981,79 @@ bool RenderFrame() ShaderUniforms.Set(s); } + //setup render target first if (is_rtt) { - GLuint channels,format; + GLuint channels, format; switch(FB_W_CTRL.fb_packmode) { case 0: //0x0 0555 KRGB 16 bit (default) Bit 15 is the value of fb_kval[7]. channels=GL_RGBA; - format=GL_UNSIGNED_SHORT_5_5_5_1; + // When using RGBA5551 format on Adreno 506, rendering is extremely slow + // (probably by some internal format conversions), thus using GL_UNSIGNED_BYTE for Adreno + if (!strncmp(gl.renderer, gl.workarounds.adrenoRenderer, 6)) { + format = GL_UNSIGNED_BYTE; + } + else { + format = GL_UNSIGNED_SHORT_5_5_5_1; + } break; case 1: //0x1 565 RGB 16 bit - channels=GL_RGB; - format=GL_UNSIGNED_SHORT_5_6_5; + channels = GL_RGB; + format = GL_UNSIGNED_SHORT_5_6_5; break; case 2: //0x2 4444 ARGB 16 bit - channels=GL_RGBA; - format=GL_UNSIGNED_SHORT_5_5_5_1; + channels = GL_RGBA; + format = GL_UNSIGNED_SHORT_4_4_4_4; break; case 3://0x3 1555 ARGB 16 bit The alpha value is determined by comparison with the value of fb_alpha_threshold. - channels=GL_RGBA; - format=GL_UNSIGNED_SHORT_5_5_5_1; + channels = GL_RGBA; + // When using RGBA5551 format on Adreno 506, rendering is extremely slow + // (probably by some internal format conversions), thus using GL_UNSIGNED_BYTE for Adreno + if (!strncmp(gl.renderer, gl.workarounds.adrenoRenderer, 6)) { + format = GL_UNSIGNED_BYTE; + } + else { + format = GL_UNSIGNED_SHORT_5_5_5_1; + } break; case 4: //0x4 888 RGB 24 bit packed - channels=GL_RGB; - format=GL_UNSIGNED_SHORT_5_6_5; - break; - case 5: //0x5 0888 KRGB 32 bit K is the value of fk_kval. - channels=GL_RGBA; - format=GL_UNSIGNED_SHORT_4_4_4_4; - break; - case 6: //0x6 8888 ARGB 32 bit - channels=GL_RGBA; - format=GL_UNSIGNED_SHORT_4_4_4_4; - break; - case 7: //7 invalid - die("7 is not valid"); - break; + default: + die("Not supported RTT format"); + return false; } - BindRTT(FB_W_SOF1&VRAM_MASK,FB_X_CLIP.max-FB_X_CLIP.min+1,FB_Y_CLIP.max-FB_Y_CLIP.min+1,channels,format); + + if (rttInUse == 1) { + rttDepthCounter++; + } + BindRTT(FB_W_SOF1 & VRAM_MASK, dc_width, dc_height, channels, format); + rttInUse = 1; } else { #if HOST_OS != OS_DARWIN + if (rttInUse == 1) { + ReadRTT(); + rttInUse = 0; + } + rttDepthCounter = 0; + if (settings.rend.VerticalResolution == 100 && settings.rend.HorizontalResolution == 100) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, screen_width, screen_height); } else { fullscreenQuadCreateTemporaryFBO(screenToNativeXScale, screenToNativeYScale); + currentScreenWidth = screen_width; + currentScreenHeight = screen_height; } #endif } @@ -2056,9 +2091,6 @@ bool RenderFrame() glBufferData(GL_ARRAY_BUFFER,pvrrc.modtrig.bytes(),pvrrc.modtrig.head(),GL_STREAM_DRAW); glCheck(); } - int offs_x=ds2s_offs_x+0.5f; - //this needs to be scaled - //not all scaling affects pixel operations, scale to adjust for that scale_x *= scissoring_scale_x; @@ -2069,25 +2101,17 @@ bool RenderFrame() printf("SCI: %f, %f, %f, %f\n", offs_x+pvrrc.fb_X_CLIP.min/scale_x,(pvrrc.fb_Y_CLIP.min/scale_y)*dc2s_scale_h,(pvrrc.fb_X_CLIP.max-pvrrc.fb_X_CLIP.min+1)/scale_x*dc2s_scale_h,(pvrrc.fb_Y_CLIP.max-pvrrc.fb_Y_CLIP.min+1)/scale_y*dc2s_scale_h); #endif - if (settings.rend.VerticalResolution == 100 && settings.rend.HorizontalResolution == 100) { - if (settings.rend.WideScreen && pvrrc.fb_X_CLIP.min == 0 && - ((pvrrc.fb_X_CLIP.max + 1) / scale_x == 640) && (pvrrc.fb_Y_CLIP.min == 0) && - ((pvrrc.fb_Y_CLIP.max + 1) / scale_y == 480)) { + if (is_rtt || (settings.rend.VerticalResolution == 100 && settings.rend.HorizontalResolution == 100)) { + glScissor(ds2s_offs_x + 0.5f + pvrrc.fb_X_CLIP.min / scale_x, + (pvrrc.fb_Y_CLIP.min / scale_y) * dc2s_scale_h, + (pvrrc.fb_X_CLIP.max - pvrrc.fb_X_CLIP.min + 1) / scale_x * dc2s_scale_h, + (pvrrc.fb_Y_CLIP.max - pvrrc.fb_Y_CLIP.min + 1) / scale_y * dc2s_scale_h); + if (settings.rend.WideScreen) + { glDisable(GL_SCISSOR_TEST); - } else { - float width = (pvrrc.fb_X_CLIP.max - pvrrc.fb_X_CLIP.min + 1) / scale_x; - float height = (pvrrc.fb_Y_CLIP.max - pvrrc.fb_Y_CLIP.min + 1) / scale_y; - float min_x = pvrrc.fb_X_CLIP.min / scale_x; - float min_y = pvrrc.fb_Y_CLIP.min / scale_y; - if (!is_rtt) { - // Add x offset for aspect ratio > 4/3 - min_x = min_x * dc2s_scale_h + ds2s_offs_x; - // Invert y coordinates when rendering to screen - min_y = screen_height - (min_y + height) * dc2s_scale_h; - width *= dc2s_scale_h; - height *= dc2s_scale_h; - } - glScissor(min_x + 0.5f, min_y + 0.5f, width + 0.5f, height + 0.5f); + } + else + { glEnable(GL_SCISSOR_TEST); } } @@ -2095,7 +2119,11 @@ bool RenderFrame() //restore scale_x scale_x /= scissoring_scale_x; - DrawStrips(); + if (!(is_rtt && (settings.dreamcast.rttOption > Disabled && settings.dreamcast.rttOption <= ShadowCircle))) + { + rttCheckIfUpdated(); + DrawStrips(); + } #if HOST_OS==OS_WINDOWS //Sleep(40); //to test MT stability diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index 74c7bff83..fe8e2fd9f 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -65,11 +65,24 @@ struct GLFramebufferData { //vertex types extern u32 gcflip; +extern u32 rttDepthCounter; extern GLFramebufferData fullscreenQuad; +enum rttSelectedOption +{ + Disabled = 0, + Zeros, + Ones, + ShadowCircle, + Full +}; + void DrawStrips(); void DrawFullscreenQuad(float, float, float, float); +bool isExtensionSupported(const char *); +void rttCheckIfUpdated(); + struct PipelineShader { GLuint program; @@ -112,6 +125,11 @@ struct gl_ctx GLuint program,scale,depth_scale; } OSD_SHADER; + struct + { + const char *adrenoRenderer = "Adreno"; + } workarounds; + struct { GLuint geometry,modvols,idxs,idxs2; @@ -122,6 +140,7 @@ struct gl_ctx GLuint fullscreenQuadShader; + const char *renderer; const char *gl_version; const char *glsl_version_header; int gl_major; @@ -147,9 +166,11 @@ void DoCleanup(); void SortPParams(); void BindRTT(u32 addy, u32 fbw, u32 fbh, u32 channels, u32 fmt); +void ReadRTT(); int GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, u32 pp_FogCtrl); +void InitShadowCircle(); bool CompilePipelineShader(PipelineShader* s); #define TEXTURE_LOAD_ERROR 0 diff --git a/core/rend/gles/gltex.cpp b/core/rend/gles/gltex.cpp index 171c6897b..331f8c4f9 100644 --- a/core/rend/gles/gltex.cpp +++ b/core/rend/gles/gltex.cpp @@ -1,6 +1,13 @@ #include "gles.h" #include "rend/TexCache.h" #include "hw/pvr/pvr_mem.h" +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* Textures @@ -20,11 +27,17 @@ Compression look into it, but afaik PVRC is not realtime doable */ +const u32 shadowCircleW = 128; +const u32 shadowCircleH = 128; +u16 shadowCircleTexture[shadowCircleW][shadowCircleH] = {0}; +set delayedUpdateQueue; + #if FEAT_HAS_SOFTREND #include #endif u16 temp_tex_buffer[1024*1024]; +u32 temp_tex_buffer_32[1024*1024]; extern u32 decoded_colors[3][65536]; typedef void TexConvFP(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height); @@ -138,7 +151,7 @@ struct TextureCacheData else { texID = 0; } - + pData = 0; tex_type = 0; @@ -222,7 +235,7 @@ struct TextureCacheData 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) + if (tcw.StrideSel) stride=(TEXT_CONTROL&31)*32; //Call the format specific conversion code texconv=tex->PL; @@ -260,8 +273,12 @@ struct TextureCacheData } } - void Update() + void Update(bool isSourceVram = true, u16 *sourceData = NULL) { + if (!isSourceVram && !sourceData) { + return; + } + //texture state tracking stuff Updates++; dirty=0; @@ -274,22 +291,28 @@ struct TextureCacheData 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 + 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; + pbt.p_buffer_start = pbt.p_current_line = temp_tex_buffer; + u32 stride = w; if (tcw.StrideSel && tcw.ScanOrder && tex->PL) - stride=(TEXT_CONTROL&31)*32; //I think this needs +1 ? + stride = (TEXT_CONTROL & 31) * 32; - if(texconv!=0) + if(texconv != 0) { - texconv(&pbt,(u8*)&vram[sa],stride,h); + if (isSourceVram) { + pbt.pixels_per_line = w; + texconv(&pbt, (u8*)&vram[sa], stride, h); + } + else + { + pbt.pixels_per_line = stride; + texconv(&pbt, (u8*)sourceData, stride, h); + } } else { @@ -301,13 +324,20 @@ struct TextureCacheData //PrintTextureName(); //lock the texture to detect changes in it - lock_block = libCore_vramlock_Lock(sa_tex,sa+size-1,this); + if (isSourceVram) { + 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); + GLuint comps = textype == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA; + + if (isSourceVram) { + glTexImage2D(GL_TEXTURE_2D, 0, comps, w, h, 0, comps, textype, temp_tex_buffer); + } + else { + glTexImage2D(GL_TEXTURE_2D, 0, comps, stride, h, 0, comps, textype, temp_tex_buffer); + } if (tcw.MipMapped && settings.rend.UseMipmaps) glGenerateMipmap(GL_TEXTURE_2D); } @@ -364,7 +394,6 @@ struct TextureCacheData } }; -#include map TexCache; typedef map::iterator TexCacheIter; @@ -372,111 +401,489 @@ typedef map::iterator TexCacheIter; struct FBT { - u32 TexAddr; - GLuint depthb,stencilb; + u32 texAddress; + u16 texData[1024*1024]; + GLuint renderBuffer; GLuint tex; + GLuint renderTex; GLuint fbo; + u32 w; + u32 h; + TextureCacheData tf; + u32 kval_bit; + u32 fb_alpha_threshold; + u32 fb_packmode; + bool is565; + bool texDataValid; + bool updated; + bool initialized; + + FBT(): initialized(false), updated(false), tf({0}), tex(0), renderTex(0), texDataValid(false) {} }; -FBT fb_rtt; +void createTexture(u32 w, u32 h, u32 textureFormat, u32 textureType, GLuint & textureID) +{ + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_2D, textureID); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, w, h, 0, textureFormat, textureType, 0); +} + +double binaryFractionToDouble(u32 numberIntegerPart, u32 numberFractionalPart, u32 fractionalPartLength) +{ + double sum = 0; + + for (u32 i = 1; i <= fractionalPartLength; ++i) { + sum += !!(numberFractionalPart & (1 << fractionalPartLength - i)) * pow(.5, i); + } + + return numberIntegerPart + sum; +} + +map renderedTextures; void BindRTT(u32 addy, u32 fbw, u32 fbh, u32 channels, u32 fmt) { - FBT& rv=fb_rtt; + FBT *renderedTexture; + u32 location = addy >> 3; + map::iterator iter = renderedTextures.find(location); - 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); + if (iter != renderedTextures.end()) { + renderedTexture = &iter->second; + } else { + renderedTexture = &renderedTextures[location]; + } - rv.TexAddr=addy>>3; + u32 fbhViewport = fbh; + if (SCALER_CTL.vscalefactor != 0x0400) { + fbh = round(fbh * binaryFractionToDouble( + SCALER_CTL.vscalefactor >> 10, SCALER_CTL.vscalefactor & 0x3FF, 10)); + } - // Find the largest square power of two texture that fits into the viewport + renderedTexture->texAddress = location; + renderedTexture->fb_packmode = FB_W_CTRL.fb_packmode; + renderedTexture->updated = true; + renderedTexture->texDataValid = false; + renderedTexture->kval_bit = (FB_W_CTRL.fb_kval & 0x80) >> 7; + renderedTexture->fb_alpha_threshold = FB_W_CTRL.fb_alpha_threshold; + renderedTexture->w = fbw; + renderedTexture->h = fbh; + renderedTexture->is565 = (fmt == GL_UNSIGNED_SHORT_5_6_5); - // Get the currently bound frame buffer object. On most platforms this just gives 0. - //glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_i32OriginalFbo); + if (!renderedTexture->tex && (fmt != GL_UNSIGNED_SHORT_5_6_5) && (fmt != GL_UNSIGNED_SHORT_4_4_4_4)) { + createTexture(fbw, fbh, channels, fmt, renderedTexture->tex); + } - // 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); + //if 'full RTT' is disabled we can return here + if (settings.dreamcast.rttOption != Full) { + return; + } - /* - 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. - */ + // Generate and bind a render buffer which will become a depth buffer + if (!renderedTexture->renderBuffer) { + glGenRenderbuffers(1, &renderedTexture->renderBuffer); + + glBindRenderbuffer(GL_RENDERBUFFER, renderedTexture->renderBuffer); + + /* + 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); + if (isExtensionSupported("GL_OES_packed_depth_stencil")) { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, fbw, fbh); + } + else if (isExtensionSupported("GL_OES_depth24")) { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, fbw, fbh); + } + else { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, fbw, fbh); + } #else - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, fbw, fbh); + //OpenGL >= 3.0 is required + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 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 - may be a color render buffer as well + if (!renderedTexture->renderTex) { + createTexture(fbw, fbh, channels, fmt, renderedTexture->renderTex); + } - // Create a texture for rendering to - glGenTextures(1, &rv.tex); - glBindTexture(GL_TEXTURE_2D, rv.tex); + // Create the object that will allow us to render to the aforementioned texture (one for every rtt texture address) + if (!renderedTexture->fbo) { + glGenFramebuffers(1, &renderedTexture->fbo); - glTexImage2D(GL_TEXTURE_2D, 0, channels, fbw, fbh, 0, channels, fmt, 0); + glBindFramebuffer(GL_FRAMEBUFFER, renderedTexture->fbo); - 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); + // Attach the texture to the FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderedTexture->renderTex, 0); - // Create the object that will allow us to render to the aforementioned texture - glGenFramebuffers(1, &rv.fbo); - glBindFramebuffer(GL_FRAMEBUFFER, rv.fbo); + // Attach the depth (and/or stencil) buffer we created earlier to our FBO. + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderedTexture->renderBuffer); +#ifdef GLES + if (isExtensionSupported("GL_OES_packed_depth_stencil")) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderedTexture->renderBuffer); + } +#else + //OpenGL >= 3.0 is required + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderedTexture->renderBuffer); +#endif + // Check that our FBO creation was successful + GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); - // Attach the texture to the FBO - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rv.tex, 0); + verify(uStatus == GL_FRAMEBUFFER_COMPLETE); + } + else { + glBindFramebuffer(GL_FRAMEBUFFER, renderedTexture->fbo); + } - // Attach the depth buffer we created earlier to our FBO. - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rv.depthb); + glViewport(0, 0, fbw, fbhViewport); +} - // Check that our FBO creation was successful - GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); +GLint checkSupportedReadFormat() +{ + //framebuffer from which we are reading must be bound before + GLint readFormat; + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat); + return readFormat; +} - verify(uStatus == GL_FRAMEBUFFER_COMPLETE); +GLint checkSupportedReadType() +{ + //framebuffer from which we are reading must be bound before + GLint readType; + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType); + return readType; +} + +void handleKRGB1555(GLint w, GLint h, FBT &fbt) { + const u32 kval_bit = fbt.kval_bit; + GLint framebufferReadType = checkSupportedReadType(); + + if (framebufferReadType == GL_UNSIGNED_SHORT_5_5_5_1) { + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, fbt.texData); + + // convert RGBA5551 to KRGB1555 + const u16 *dataPointer = fbt.texData; + for (u32 i = 0; i < w * h; i++) { + fbt.texData[i] = (kval_bit << 15) | ((*dataPointer & 0xFFFE) >> 1); + *dataPointer++; + } + } + else if (framebufferReadType == GL_UNSIGNED_BYTE) { + // Adreno 506 slow rendering fix - 'texconv' in case of RGBA5551 format is not needed + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, temp_tex_buffer_32); + for (u32 i = 0; i < w * h; i++) { + temp_tex_buffer_32[i] = (kval_bit << 31) | (temp_tex_buffer_32[i] & 0x00FFFFFF); + } + glBindTexture(GL_TEXTURE_2D, fbt.tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_tex_buffer_32); + fbt.updated = false; + } + else { + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, temp_tex_buffer_32); + + // convert R8G8B8A8 to KRGB1555 + const u32 *dataPointer32 = temp_tex_buffer_32; + for (u32 i = 0; i < w * h; i++) { + //additional variables just for better visibility + const u8 blue = ((*dataPointer32 & 0x00FF0000UL) >> 19); //5bits for blue + const u8 green = ((*dataPointer32 & 0x0000FF00UL) >> 11); //5bits for green + const u8 red = (*dataPointer32 & 0x000000FFUL) >> 3; //5bits for red + + //convert to 16bit color with K value bit + temp_tex_buffer_32[i] = (kval_bit << 15) | (red << 10) | (green << 5) | blue; + *dataPointer32++; + + //assign to 16bit buffer (type conversions should use static_cast<> in C++) + fbt.texData[i] = (u16)temp_tex_buffer_32[i]; + } + } +} + +void handleARGB1555(GLint w, GLint h, FBT &fbt) { + const u32 fb_alpha_threshold = fbt.fb_alpha_threshold; + GLint framebufferReadType = checkSupportedReadType(); + + if (framebufferReadType == GL_UNSIGNED_SHORT_5_5_5_1) { + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, fbt.texData); + + // convert RGBA5551 to ARGB1555 + const u16 *dataPointer = fbt.texData; + for (u32 i = 0; i < w * h; i++) { + // value has 1-bit precision only (RGBA5551), where fb_alpha_threshold is 8-bit + const u16 alpha = (*dataPointer & 0x0001) >= fb_alpha_threshold ? 1 : 0; + fbt.texData[i] = (alpha << 15) | ((*dataPointer & 0xFFFE) >> 1); + *dataPointer++; + } + } + else if (framebufferReadType == GL_UNSIGNED_BYTE) { + // Adreno 506 slow rendering fix - 'texconv' in case of RGBA5551 format is not needed + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, temp_tex_buffer_32); + for (u32 i = 0; i < w * h; i++) { + const u8 alphaThresholded = ((temp_tex_buffer_32[i] >> 31) >= fb_alpha_threshold) ? 1 : 0; + temp_tex_buffer_32[i] = (alphaThresholded << 31) | (temp_tex_buffer_32[i] & 0x00FFFFFF); + } + glBindTexture(GL_TEXTURE_2D, fbt.tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_tex_buffer_32); + fbt.updated = false; + } + else { + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, temp_tex_buffer_32); + + // convert R8G8B8A8 to ARGB1555 + const u32 *dataPointer32 = temp_tex_buffer_32; + for (u32 i = 0; i < w * h; i++) { + //additional variables just for better visibility + const u8 alpha = ((*dataPointer32 & 0xFF000000UL) >> 31); //1bit for alpha + const u8 blue = ((*dataPointer32 & 0x00FF0000UL) >> 19); //5bits for blue + const u8 green = ((*dataPointer32 & 0x0000FF00UL) >> 11); //5bits for green + const u8 red = (*dataPointer32 & 0x000000FFUL) >> 3; //5bits for red + + const u8 alphaThresholded = (alpha >= fb_alpha_threshold) ? 1 : 0; + + //convert to 16bit color and make the alpha channel swap + temp_tex_buffer_32[i] = (alphaThresholded << 15) | (red << 10) | (green << 5) | blue; + *dataPointer32++; + + //assign to 16bit buffer (type conversions should use static_cast<> in C++) + fbt.texData[i] = (u16)temp_tex_buffer_32[i]; + } + } +} + +void handlePackModeRTT(GLint w, GLint h, FBT &fbt) +{ + switch (fbt.fb_packmode) { + case 0: //0x0 0555 KRGB 16 bit (default) Bit 15 is the value of fb_kval[7]. + { + handleKRGB1555(w, h, fbt); + break; + } + + case 1: //0x1 565 RGB 16 bit + { + //handled elsewhere + break; + } + + case 2: //0x2 4444 ARGB 16 bit + { + fbt.tex = fbt.renderTex; + fbt.updated = false; + break; + } + + case 3: //0x3 1555 ARGB 16 bit The alpha value is determined by comparison with the value of fb_alpha_threshold. + { + handleARGB1555(w, h, fbt); + break; + } + + default: + { + //clear unsupported texture to avoid artifacts + memset(temp_tex_buffer, '\0', (size_t)((long long) w * h)); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, temp_tex_buffer); + break; + } + } +} + +void ReadRTT() +{ + map::iterator it; + + for ( it = renderedTextures.begin(); it != renderedTextures.end(); it++ ) + { + FBT &fbt = it->second; + if(!fbt.updated) { + continue; + } + + GLint w = fbt.w; + GLint h = fbt.h; + + if (settings.dreamcast.rttOption == ShadowCircle) + { + glBindTexture(GL_TEXTURE_2D, fbt.tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shadowCircleW, shadowCircleH, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, shadowCircleTexture[0]); + fbt.updated = false; + } + else if (settings.dreamcast.rttOption == Ones) + { + for (u32 i = 0; i < w * h; i++) + temp_tex_buffer[i] = (u16)~0; + + glBindTexture(GL_TEXTURE_2D, fbt.tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, temp_tex_buffer); + fbt.updated = false; + } + else if (settings.dreamcast.rttOption == Zeros) + { + for (u32 i = 0; i < w * h; i++) + temp_tex_buffer[i] = 0; + + glBindTexture(GL_TEXTURE_2D, fbt.tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, temp_tex_buffer); + fbt.updated = false; + } + else if (settings.dreamcast.rttOption == Full) + { + if (fbt.fb_packmode != 1) { + glBindFramebuffer(GL_FRAMEBUFFER, fbt.fbo); + } + if (!fbt.texDataValid) { + handlePackModeRTT(w, h, fbt); + } + fbt.texDataValid = true; + } + } +} + +void rttCheckIfUpdated() { + if(!pvrrc.isRTT && delayedUpdateQueue.size()) + { + for (set::iterator it=delayedUpdateQueue.begin(); it != delayedUpdateQueue.end(); ++it) { + //We can directly read because this address exists already + if (renderedTextures[*it].initialized && renderedTextures[*it].updated) + { + renderedTextures[*it].tf.Update(false, renderedTextures[*it].texData); + renderedTextures[*it].updated = false; + } + } + delayedUpdateQueue.clear(); + } +} + +void initializeRttTexture(const TSP & tsp, const TCW & tcw, FBT * tempRenderedTexture) { + if (!tempRenderedTexture->initialized) + { + tempRenderedTexture->tf.tsp = tsp; + tempRenderedTexture->tf.tcw = tcw; + tempRenderedTexture->tf.Create(false); + tempRenderedTexture->tf.texID = tempRenderedTexture->tex; + for (u32 i = 0; i < sizeof(tempRenderedTexture->texData)/sizeof(tempRenderedTexture->texData[0]); ++i) { + tempRenderedTexture->texData[i] = 0; + } + tempRenderedTexture->initialized = true; + } +} + +void handleRGB565Exceptions(FBT &fbt, u32 newTexAddr) +{ + GLint w = fbt.w; + GLint h = fbt.h; + + if (fbt.tf.tex->type != GL_UNSIGNED_SHORT_5_6_5) + { + if (!fbt.tex || (fbt.tex == fbt.renderTex)) { + createTexture((u32) w, (u32) h, GL_RGBA, fbt.tf.tex->type, fbt.tex); + } + fbt.tf.texID = fbt.tex; + + glBindFramebuffer(GL_FRAMEBUFFER, fbt.fbo); + if (checkSupportedReadType() == GL_UNSIGNED_SHORT_5_6_5) { + // can be directly read + glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fbt.texData); + } + else { + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, temp_tex_buffer_32); + + // convert R8G8B8A8 to RGB565 + const u32 *dataPointer32 = temp_tex_buffer_32; + for (u32 i = 0; i < w * h; i++) { + //additional variables just for better visibility + const u8 blue = ((*dataPointer32 & 0x00FF0000UL) >> 19); //5bits for blue + const u8 green = ((*dataPointer32 & 0x0000FF00UL) >> 10); //6bits for green + const u8 red = (*dataPointer32 & 0x000000FFUL) >> 3; //5bits for red + + //convert to 16bit color + temp_tex_buffer_32[i] = (red << 11) | (green << 5) | blue; + *dataPointer32++; + + //assign to 16bit buffer (type conversions should use static_cast<> in C++) + fbt.texData[i] = (u16)temp_tex_buffer_32[i]; + } + } + delayedUpdateQueue.insert(newTexAddr); + } + else { + if (fbt.tex && (fbt.tex != fbt.renderTex)) { //to avoid memory leaks + glDeleteTextures(1, &fbt.tex); + } + fbt.tex = fbt.renderTex; + fbt.updated = false; + } } GLuint gl_GetTexture(TSP tsp, TCW tcw) { - if (tcw.TexAddr==fb_rtt.TexAddr && fb_rtt.tex) - { - return fb_rtt.tex; + FBT* tempRenderedTexture = NULL; + map::iterator it = renderedTextures.find(tcw.TexAddr); + if (it != renderedTextures.end()) { + tempRenderedTexture = &it->second; + } + + if (tempRenderedTexture && tcw.TexAddr == tempRenderedTexture->texAddress) { + //create for the first time + initializeRttTexture(tsp, tcw, tempRenderedTexture); + + //if there was no update, it is not an RTT frame (BindRTT was not invoked) then proceed the standard way + if (tempRenderedTexture->tf.tcw.full == tcw.full) + { + if (tempRenderedTexture->updated && !tempRenderedTexture->is565) + { + delayedUpdateQueue.insert(tcw.TexAddr); + tempRenderedTexture->tf.tsp = tsp; + tempRenderedTexture->tf.Create(false); + tempRenderedTexture->tf.texID = tempRenderedTexture->tex; + } + else if (tempRenderedTexture->updated && tempRenderedTexture->is565) + { + tempRenderedTexture->tf.tsp = tsp; + tempRenderedTexture->tf.Create(false); + handleRGB565Exceptions(*tempRenderedTexture, tcw.TexAddr); + } + return tempRenderedTexture->tex; + } } //lookup texture - TextureCacheData* tf; + TextureCacheData *tf; //= TexCache.Find(tcw.full,tsp.full); - u64 key=((u64)tcw.full<<32) | tsp.full; + u64 key = ((u64) tcw.full << 32) | tsp.full; - TexCacheIter tx=TexCache.find(key); + TexCacheIter tx = TexCache.find(key); - if (tx!=TexCache.end()) - { - tf=&tx->second; + if (tx != TexCache.end()) { + tf = &tx->second; } else //create if not existing { - TextureCacheData tfc={0}; - TexCache[key]=tfc; + TextureCacheData tfc = {0}; + TexCache[key] = tfc; - tx=TexCache.find(key); - tf=&tx->second; + tx = TexCache.find(key); + tf = &tx->second; + + tf->tsp = tsp; + tf->tcw = tcw; - tf->tsp=tsp; - tf->tcw=tcw; tf->Create(true); } //update if needed - if (tf->NeedsUpdate()) + if (tf->NeedsUpdate()) { tf->Update(); + } //update state for opts/stuff tf->Lookups++; @@ -485,7 +892,6 @@ GLuint gl_GetTexture(TSP tsp, TCW tcw) return tf->texID; } - text_info raw_GetTexture(TSP tsp, TCW tcw) { text_info rv = { 0 }; @@ -526,8 +932,7 @@ text_info raw_GetTexture(TSP tsp, TCW tcw) rv.width = tf->w; rv.pdata = tf->pData; rv.textype = tf->tex_type; - - + return rv; } @@ -549,7 +954,6 @@ void CollectCleanup() { for (size_t i=0; i rttAdapter = new ArrayAdapter<>( + getActivity(), R.layout.spinner_selected, rtts); + rttAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rtt_spnr.setAdapter(rttAdapter); + rtt_spnr.setSelection(mPrefs.getInt(Emulator.pref_rtt, Emulator.rtt), true); + rtt_spnr.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parent, View view, int pos, long id) { + mPrefs.edit().putInt(Emulator.pref_rtt, pos).apply(); + if (pos == 4) { + mCallback.synchronousRenderingNotice(); + } + } + + public void onNothingSelected(AdapterView arg0) { + } + }); + OnCheckedChangeListener limitfps_option = new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { @@ -721,6 +741,7 @@ public class OptionsFragment extends Fragment { mPrefs.edit().remove(Emulator.pref_cable).apply(); mPrefs.edit().remove(Emulator.pref_dcregion).apply(); mPrefs.edit().remove(Emulator.pref_broadcast).apply(); + mPrefs.edit().remove(Emulator.pref_rtt).apply(); mPrefs.edit().remove(Emulator.pref_limitfps).apply(); mPrefs.edit().remove(Emulator.pref_mipmaps).apply(); mPrefs.edit().remove(Emulator.pref_resolution).apply(); @@ -743,6 +764,7 @@ public class OptionsFragment extends Fragment { Emulator.cable = 3; Emulator.dcregion = 3; Emulator.broadcast = 4; + Emulator.rtt = 1; Emulator.limitfps = true; Emulator.mipmaps = true; Emulator.widescreen = false; diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java index dc7bfb077..0813765e5 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java @@ -36,6 +36,7 @@ public final class JNIdc public static native void cable(int cable); public static native void region(int region); public static native void broadcast(int broadcast); + public static native void rtt(int rtt); public static native void limitfps(int limiter); public static native void nobatch(int nobatch); public static native void nosound(int noaudio); diff --git a/shell/android-studio/reicast/src/main/jni/src/Android.cpp b/shell/android-studio/reicast/src/main/jni/src/Android.cpp index af976e201..0d04c1b3f 100644 --- a/shell/android-studio/reicast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/reicast/src/main/jni/src/Android.cpp @@ -52,6 +52,7 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_safemode(JNIEnv *env, JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_cable(JNIEnv *env,jobject obj, jint cable) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_region(JNIEnv *env,jobject obj, jint region) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_broadcast(JNIEnv *env,jobject obj, jint broadcast) __attribute__((visibility("default"))); +JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rtt(JNIEnv *env,jobject obj, jint rtt) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_clipping(JNIEnv *env,jobject obj, jint clipping) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_limitfps(JNIEnv *env,jobject obj, jint limiter) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_nobatch(JNIEnv *env,jobject obj, jint nobatch) __attribute__((visibility("default"))); @@ -105,6 +106,11 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_broadcast(JNIEnv *env settings.dreamcast.broadcast = broadcast; } +JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rtt(JNIEnv *env,jobject obj, jint rtt) +{ + settings.dreamcast.rttOption = rtt; +} + JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_limitfps(JNIEnv *env,jobject obj, jint limiter) { settings.aica.LimitFPS = limiter; @@ -127,7 +133,7 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_mipmaps(JNIEnv *env,j JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_clipping(JNIEnv *env,jobject obj, jint clipping) { - settings.rend.Clipping = clipping; + settings.rend.Clipping = clipping; } JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_widescreen(JNIEnv *env,jobject obj, jint stretch) diff --git a/shell/android-studio/reicast/src/main/res/layout-v14/configure_fragment.xml b/shell/android-studio/reicast/src/main/res/layout-v14/configure_fragment.xml index 681623a27..6fe776646 100644 --- a/shell/android-studio/reicast/src/main/res/layout-v14/configure_fragment.xml +++ b/shell/android-studio/reicast/src/main/res/layout-v14/configure_fragment.xml @@ -637,6 +637,35 @@ + + + + + + + + + + diff --git a/shell/android-studio/reicast/src/main/res/layout/configure_fragment.xml b/shell/android-studio/reicast/src/main/res/layout/configure_fragment.xml index d72ad1e7a..d2f026a2f 100644 --- a/shell/android-studio/reicast/src/main/res/layout/configure_fragment.xml +++ b/shell/android-studio/reicast/src/main/res/layout/configure_fragment.xml @@ -637,6 +637,35 @@ + + + + + + + + + + diff --git a/shell/android-studio/reicast/src/main/res/values-pl/strings.xml b/shell/android-studio/reicast/src/main/res/values-pl/strings.xml index 17275683e..c32982b04 100644 --- a/shell/android-studio/reicast/src/main/res/values-pl/strings.xml +++ b/shell/android-studio/reicast/src/main/res/values-pl/strings.xml @@ -18,6 +18,7 @@ Opcje Dynarec Niestabilne optymalizacje Region DC + Renderuj do tekstury Limit FPS Użyj Mipmap (fix dla starego SGX540) Tryb Szerokoekranowy @@ -67,4 +68,6 @@ O nas Oceń nas + Zalecane jest także włączenie opcji `Synchronous Rendering` + diff --git a/shell/android-studio/reicast/src/main/res/values-ru/strings.xml b/shell/android-studio/reicast/src/main/res/values-ru/strings.xml index 65cccc0a7..652383a93 100644 --- a/shell/android-studio/reicast/src/main/res/values-ru/strings.xml +++ b/shell/android-studio/reicast/src/main/res/values-ru/strings.xml @@ -38,6 +38,7 @@ Тип кабеля Регион Dreamcast Система ТВ + Рендеринг текстуры (RTT) Ограничение FPS MIP-карты (откл. для SGX540) Широкоэкранный режим diff --git a/shell/android-studio/reicast/src/main/res/values/donottranslate.xml b/shell/android-studio/reicast/src/main/res/values/donottranslate.xml index 5d99042de..ad7d9785b 100644 --- a/shell/android-studio/reicast/src/main/res/values/donottranslate.xml +++ b/shell/android-studio/reicast/src/main/res/values/donottranslate.xml @@ -62,6 +62,14 @@ Katana + + Disabled - skip frames + Zeros + Ones + Shadow circle + Full + + J E diff --git a/shell/android-studio/reicast/src/main/res/values/strings.xml b/shell/android-studio/reicast/src/main/res/values/strings.xml index 198435dee..027b85d1e 100644 --- a/shell/android-studio/reicast/src/main/res/values/strings.xml +++ b/shell/android-studio/reicast/src/main/res/values/strings.xml @@ -43,6 +43,7 @@ Cable Type DC Region Broadcast + Render to texture Limit FPS Use Mipmaps (fixes SGX540) Resolution Mode @@ -186,4 +187,7 @@ Emu Sound Turbo disk_loading - \ No newline at end of file + + It is recommended to enable `Synchronous Rendering` option as well + +