Merge pull request #1460 from mar753/mar753/render-to-texture-with-options

Multipass render to texture with options - implemented
This commit is contained in:
baka0815 2019-02-19 19:15:21 +01:00 committed by GitHub
commit 41907bc992
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 783 additions and 178 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -1,4 +1,5 @@
#include <math.h>
#include <types.h>
#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

View File

@ -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

View File

@ -1,6 +1,13 @@
#include "gles.h"
#include "rend/TexCache.h"
#include "hw/pvr/pvr_mem.h"
#include <cmath>
#include <map>
#include <set>
#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<u32> delayedUpdateQueue;
#if FEAT_HAS_SOFTREND
#include <xmmintrin.h>
#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>
map<u64,TextureCacheData> TexCache;
typedef map<u64,TextureCacheData>::iterator TexCacheIter;
@ -372,111 +401,489 @@ typedef map<u64,TextureCacheData>::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<u32, FBT> renderedTextures;
void BindRTT(u32 addy, u32 fbw, u32 fbh, u32 channels, u32 fmt)
{
FBT& rv=fb_rtt;
FBT *renderedTexture;
u32 location = addy >> 3;
map<u32, FBT>::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<u32, FBT>::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<u32>::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<u32, FBT>::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<list.size(); i++) {
//printf("Deleting %d\n",TexCache[list[i]].texID);
TexCache[list[i]].Delete();
TexCache.erase(list[i]);
}
}
@ -557,6 +961,30 @@ void CollectCleanup() {
void DoCleanup() {
}
void InitShadowCircle() {
s32 middle_x = shadowCircleW / 2;
s32 middle_y = shadowCircleH / 2;
u32 radius = 25;
s32 x = 0, y = 0;
for (s32 i = 0; i <= 360; i = i + 2) {
x = s32(radius * cos(i * M_PI / 180));
y = s32(radius * sin(i * M_PI / 180));
shadowCircleTexture[middle_x + x][middle_y + y] = 0x5555;
if (y < 0) {
for (s32 j = 0; j < abs(y); ++j) {
shadowCircleTexture[middle_x + x][middle_y - j] = 0xAAAA;
}
} else {
for (s32 j = 1; j < y; ++j) {
shadowCircleTexture[middle_x + x][middle_y + j] = 0xAAAA;
}
}
}
}
void killtex()
{
for (TexCacheIter i=TexCache.begin();i!=TexCache.end();i++)

View File

@ -642,6 +642,7 @@ struct settings_t
u32 RTC;
u32 region;
u32 broadcast;
u32 rttOption;
} dreamcast;
struct

View File

@ -16,6 +16,7 @@ public class Emulator extends Application {
public static final String pref_cable = "dc_cable";
public static final String pref_dcregion = "dc_region";
public static final String pref_broadcast = "dc_broadcast";
public static final String pref_rtt = "dc_rtt";
public static final String pref_limitfps = "limit_fps";
public static final String pref_nosound = "sound_disabled";
public static final String pref_mipmaps = "use_mipmaps";
@ -37,6 +38,7 @@ public class Emulator extends Application {
public static int cable = 3;
public static int dcregion = 3;
public static int broadcast = 4;
public static int rtt = 1;
public static boolean limitfps = true;
public static boolean nobatch = false;
public static boolean nosound = false;
@ -64,6 +66,7 @@ public class Emulator extends Application {
Emulator.cable = mPrefs.getInt(pref_cable, cable);
Emulator.dcregion = mPrefs.getInt(pref_dcregion, dcregion);
Emulator.broadcast = mPrefs.getInt(pref_broadcast, broadcast);
Emulator.rtt = mPrefs.getInt(pref_rtt, rtt);
Emulator.limitfps = mPrefs.getBoolean(pref_limitfps, limitfps);
Emulator.nosound = mPrefs.getBoolean(pref_nosound, nosound);
Emulator.mipmaps = mPrefs.getBoolean(pref_mipmaps, mipmaps);
@ -91,6 +94,7 @@ public class Emulator extends Application {
JNIdc.cable(Emulator.cable);
JNIdc.region(Emulator.dcregion);
JNIdc.broadcast(Emulator.broadcast);
JNIdc.rtt(Emulator.rtt);
JNIdc.limitfps(Emulator.limitfps ? 1 : 0);
JNIdc.nobatch(Emulator.nobatch ? 1 : 0);
JNIdc.nosound(Emulator.nosound ? 1 : 0);

View File

@ -340,6 +340,13 @@ public class MainActivity extends AppCompatActivity implements
builder.show();
}
public void synchronousRenderingNotice() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.synchronous_rendering_notice);
builder.create();
builder.show();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);

View File

@ -72,6 +72,7 @@ public class OptionsFragment extends Fragment {
void recreateActivity();
void onMainBrowseSelected(String path_entry, boolean games, String query);
void launchBIOSdetection();
void synchronousRenderingNotice();
}
@Override @SuppressWarnings("deprecation")
@ -345,6 +346,25 @@ public class OptionsFragment extends Fragment {
}
});
String[] rtts = getResources().getStringArray(R.array.rtt);
Spinner rtt_spnr = (Spinner) getView().findViewById(R.id.rtt_spinner);
ArrayAdapter<String> 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;

View File

@ -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);

View File

@ -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)

View File

@ -637,6 +637,35 @@
</LinearLayout>
</TableRow>
<TableRow
android:layout_marginTop="10dp"
android:gravity="center_vertical" >
<TextView
android:id="@+id/rtt_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:ems="10"
android:gravity="center_vertical|left"
android:text="@string/select_rtt" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"
android:orientation="vertical" >
<Spinner
android:id="@+id/rtt_spinner"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:ems="10" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_marginTop="10dp"
android:gravity="center_vertical" >

View File

@ -637,6 +637,35 @@
</LinearLayout>
</TableRow>
<TableRow
android:layout_marginTop="10dp"
android:gravity="center_vertical" >
<TextView
android:id="@+id/rtt_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:ems="10"
android:gravity="center_vertical|left"
android:text="@string/select_rtt" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"
android:orientation="vertical" >
<Spinner
android:id="@+id/rtt_spinner"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:ems="10" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_marginTop="10dp"
android:gravity="center_vertical" >

View File

@ -18,6 +18,7 @@
<string name="select_dynarec">Opcje Dynarec</string>
<string name="select_unstable">Niestabilne optymalizacje</string>
<string name="select_region">Region DC</string>
<string name="select_rtt">Renderuj do tekstury</string>
<string name="select_limitfps">Limit FPS</string>
<string name="select_mipmaps">Użyj Mipmap (fix dla starego SGX540)</string>
<string name="select_stretch">Tryb Szerokoekranowy</string>
@ -67,4 +68,6 @@
<string name="about">O nas</string>
<string name="rateme">Oceń nas</string>
<string name="synchronous_rendering_notice">Zalecane jest także włączenie opcji `Synchronous Rendering`</string>
</resources>

View File

@ -38,6 +38,7 @@
<string name="select_cable">Тип кабеля</string>
<string name="select_region">Регион Dreamcast</string>
<string name="select_broadcast">Система ТВ</string>
<string name="select_rtt">Рендеринг текстуры (RTT)</string>
<string name="select_limitfps">Ограничение FPS</string>
<string name="select_mipmaps">MIP-карты (откл. для SGX540)</string>
<string name="select_stretch">Широкоэкранный режим</string>

View File

@ -62,6 +62,14 @@
<item>Katana</item>
</string-array>
<string-array name="rtt">
<item>Disabled - skip frames</item>
<item>Zeros</item>
<item>Ones</item>
<item>Shadow circle</item>
<item>Full</item>
</string-array>
<string-array name="bioscode">
<item>J</item>
<item>E</item>

View File

@ -43,6 +43,7 @@
<string name="select_cable">Cable Type</string>
<string name="select_region">DC Region</string>
<string name="select_broadcast">Broadcast</string>
<string name="select_rtt">Render to texture</string>
<string name="select_limitfps">Limit FPS</string>
<string name="select_mipmaps">Use Mipmaps (fixes SGX540)</string>
<string name="select_resolution">Resolution Mode</string>
@ -186,4 +187,7 @@
<string name="popup_button_audio">Emu Sound</string>
<string name="popup_button_turbo">Turbo</string>
<string name="textview">disk_loading</string>
</resources>
<string name="synchronous_rendering_notice">It is recommended to enable `Synchronous Rendering` option as well</string>
</resources>