psx - implement framebuffer 'normalizing' for consistent 700x480 output, at the cost of slightly warped AR in some resolutions. Ideal for debugging, but not for gaming just yet.

This commit is contained in:
zeromus 2014-12-10 21:56:12 +00:00
parent 5c53bc3924
commit 542675def4
6 changed files with 225 additions and 48 deletions

View File

@ -1,18 +1,7 @@
//TODO hook up newer file ID stuff, think about how to combine it with the disc ID
//TODO not liking the name ShockFramebufferJob
//TODO change display manager to not require 0xFF alpha channel set on videoproviders. check gdi+ and opengl! this will get us a speedup in some places
//TODO Disc.Structure.Sessions[0].length_aba was 0
//looks like we can have (in NTSC) framebuffer dimensions like this:
//width: 280, 350, 700
//height: 240, 480
//mednafen's strategy is to put everything in a 320x240 and scale it up 3x to 960x720 by default (which is adequate to contain the largest PSX framebuffer)
//heres my strategy.
//1. we should have a native output mode, for debugging. but most users wont want it (massively distorted resolutions are common in games)
//2. do the right thing:
//always double a height of 240, and double a width of 280 or 350. For 280, float content in center screen.
//but lets not do this til we're on an upgraded mednafen
using System;
using System.Runtime.InteropServices;
using System.IO;
@ -149,14 +138,13 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
}
}
//note: its annoying that we have to have a disc before constructing this.
//might want to change that later. HOWEVER - we need to definitely have a region, at least
public Octoshock(CoreComm comm, DiscSystem.Disc disc)
{
ServiceProvider = new BasicServiceProvider(this);
var domains = new List<MemoryDomain>();
CoreComm = comm;
VirtualWidth = BufferWidth = 256;
BufferHeight = 192;
Attach();
@ -177,6 +165,23 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
fixed (byte* pFirmware = firmware)
OctoshockDll.shock_Create(out psx, discInfo.region, pFirmware);
//these should track values in octoshock gpu.cpp FillVideoParams
//if (discInfo.region == OctoshockDll.eRegion.EU)
//{
// VirtualWidth = 377; // " Dunno :( "
// VirtualHeight = 288;
//}
//else
//{
// VirtualWidth = 320; // Dunno :(
// VirtualHeight = 240;
//}
//BUT-for now theyre normalized (NOTE: THIS MESSES UP THE ASPECT RATIOS)
VirtualWidth = 700;
VirtualHeight = 480;
OctoshockDll.shock_OpenTray(psx);
OctoshockDll.shock_SetDisc(psx, discInterface.OctoshockHandle);
OctoshockDll.shock_CloseTray(psx);
@ -218,7 +223,8 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
{
OctoshockDll.shock_Step(psx, OctoshockDll.eShockStep.Frame);
OctoshockDll.ShockFramebufferJob fb = new OctoshockDll.ShockFramebufferJob();
OctoshockDll.ShockFramebufferInfo fb = new OctoshockDll.ShockFramebufferInfo();
fb.flags = OctoshockDll.eShockFramebufferFlags.Normalize;
OctoshockDll.shock_GetFramebuffer(psx, ref fb);
//Console.WriteLine(fb.height);
@ -265,7 +271,7 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
public int[] GetVideoBuffer() { return frameBuffer; }
public int VirtualWidth { get; private set; }
public int VirtualHeight { get { return BufferHeight; } }
public int VirtualHeight { get; private set; }
public int BufferWidth { get; private set; }
public int BufferHeight { get; private set; }
public int BackgroundColor { get { return 0; } }

View File

@ -19,6 +19,12 @@ public unsafe static class OctoshockDll
Frame
};
public enum eShockFramebufferFlags
{
None = 0,
Normalize = 1
}
public const int SHOCK_OK = 0;
public const int SHOCK_ERROR = -1;
public const int SHOCK_NOCANDO = -2;
@ -47,9 +53,11 @@ public unsafe static class OctoshockDll
};
[StructLayout(LayoutKind.Sequential)]
public struct ShockFramebufferJob
public struct ShockFramebufferInfo
{
public int width, height;
[MarshalAs(UnmanagedType.I4)]
public eShockFramebufferFlags flags;
public void* ptr;
};
@ -93,5 +101,5 @@ public unsafe static class OctoshockDll
public static extern int shock_Step(IntPtr psx, eShockStep step);
[DllImport("octoshock.dll")]
public static extern int shock_GetFramebuffer(IntPtr psx, ref ShockFramebufferJob fb);
public static extern int shock_GetFramebuffer(IntPtr psx, ref ShockFramebufferInfo fb);
}

Binary file not shown.

View File

@ -1346,6 +1346,9 @@ static void MountCPUAddressSpace()
static MDFN_Surface *VTBuffer[2] = { NULL, NULL };
static int *VTLineWidths[2] = { NULL, NULL };
static bool s_FramebufferNormalized;
static int s_FramebufferCurrent;
static int s_FramebufferCurrentWidth;
EW_EXPORT s32 shock_Create(void** psx, eRegion region, void* firmware512k)
{
@ -1374,33 +1377,37 @@ EW_EXPORT s32 shock_Create(void** psx, eRegion region, void* firmware512k)
{
sls = 0;
sle = 287;
s_ShockConfig.nominal_width = 367; // Dunno. :(
s_ShockConfig.nominal_height = 288;
s_ShockConfig.fb_width = 768;
s_ShockConfig.fb_height = 576;
}
else
{
sls = 0;
sle = 239;
s_ShockConfig.lcm_width = 2720;
s_ShockConfig.lcm_height = 480;
s_ShockConfig.nominal_width = 310;
s_ShockConfig.nominal_height = 240;
s_ShockConfig.fb_width = 768;
s_ShockConfig.fb_height = 480;
}
//setup gpu output surfaces
MDFN_PixelFormat nf(MDFN_COLORSPACE_RGB, 16, 8, 0, 24);
VTBuffer[0] = new MDFN_Surface(NULL, s_ShockConfig.fb_width, s_ShockConfig.fb_height, s_ShockConfig.fb_width, nf);
VTLineWidths[0] = (int *)calloc(s_ShockConfig.fb_height, sizeof(int));
//these steps can't be done without more information
GPU = new PS_GPU(region == REGION_EU, sls, sle);
//fetch video parameters, stash in a simpler format
MDFNGI givp;
GPU->FillVideoParams(&givp);
s_ShockConfig.lcm_width = givp.lcm_width;
s_ShockConfig.lcm_height = givp.lcm_height;
s_ShockConfig.fb_height = givp.fb_height;
s_ShockConfig.fb_width = givp.fb_width;
s_ShockConfig.fb_height = givp.fb_height;
s_ShockConfig.nominal_width = givp.nominal_width;
s_ShockConfig.nominal_height = givp.nominal_height;
//givp.fps // TODO
//setup gpu output surfaces
MDFN_PixelFormat nf(MDFN_COLORSPACE_RGB, 16, 8, 0, 24);
for(int i=0;i<2;i++)
{
VTBuffer[i] = new MDFN_Surface(NULL, s_ShockConfig.fb_width, s_ShockConfig.fb_height, s_ShockConfig.fb_width, nf);
VTLineWidths[i] = (int *)calloc(s_ShockConfig.fb_height, sizeof(int));
}
//TODO - configuration
static bool emulate_memcard[8] = {0};
@ -1521,16 +1528,161 @@ EW_EXPORT s32 shock_Step(void* psx, eShockStep step)
espec.InterlaceField = 0;
}
//pixel-double some dimensions as needed (TBD)
//new frame, hasnt been normalized
s_FramebufferNormalized = false;
s_FramebufferCurrent = 0;
s_FramebufferCurrentWidth = s_ShockConfig.fb_width;
return SHOCK_OK;
}
EW_EXPORT s32 shock_GetFramebuffer(void* psx, ShockFramebufferJob* fb)
//`normalizes` the framebuffer to 700x480 by pixel doubling and wrecking the AR a little bit as needed
void NormalizeFramebuffer()
{
//always fetch description
//mednafen's advised solution for smooth gaming: "scale the output width to z * nominal_width, and the output height to z * nominal_height, where nominal_width and nominal_height are members of the MDFNGI struct"
//IOW, mednafen's strategy is to put everything in a 320x240 and scale it up 3x to 960x720 by default (which is adequate to contain the largest PSX framebuffer of 700x480)
//psxtech says horizontal resolutions can be: 256, 320, 368, 512, 640 pixels
//mednafen will turn those into 2800/{ 10, 8, 5, 4, 7 } -> 280,350,560,700,400
//heres my strategy:
//try to do the smart thing, try to get aspect ratio near the right value
//intended AR = 320/240 = 1.3333
//280x240 - ok (AR 1.1666666666666666666666666666667)
//350x240 - ok (AR 1.4583333333333333333333333333333)
//400x240 - ok (AR 1.6666666666666666666666666666667)
//560x240 - scale vertically by 2 = 560x480 ~ 280x240
//700x240 - scale vertically by 2 = 700x480 ~ 350x240
//280x480 - scale horizontally by 2 = 560x480 ~ 280x240
//350x480 - scale horizontally by 2 = 700x480 ~ 350x240
//400x480 - scale horizontally by 2 = 800x480 ~ 400x240
//560x480 - ok ~ 280x240
//700x480 - ok ~ 350x240
//NOTE: this approach is very redundant with the displaymanager AR tracking stuff
//however, it will help us avoid stressing the displaymanager (for example, a 700x240 will freak it out kind of. we could send it a much more sensible 700x480)
int width = VTLineWidths[0][0]; //presently, except for contrived test programs, it is safe to assume this is the same for the entire frame (no known use by games)
int height = espec.DisplayRect.h;
int xs=1,ys=1,xm=0;
//I. as described above
//if(width == 280 && height == 240) {}
//if(width == 350 && height == 240) {}
//if(width == 400 && height == 240) {}
//if(width == 560 && height == 240) ys=2;
//if(width == 700 && height == 240) ys=2;
//if(width == 280 && height == 480) xs=2;
//if(width == 350 && height == 480) xs=2;
//if(width == 400 && height == 480) xs=2;
//if(width == 560 && height == 480) {}
//if(width == 700 && height == 480) {}
//II. as the snes 'always double size framebuffer'. I think thats a better idea, and we already have the concept
if(width == 280 && height == 240) xs=ys=2;
if(width == 350 && height == 240) xs=ys=2;
if(width == 400 && height == 240) xs=ys=2;
if(width == 560 && height == 240) ys=2;
if(width == 700 && height == 240) ys=2;
if(width == 280 && height == 480) xs=2;
if(width == 350 && height == 480) xs=2;
if(width == 400 && height == 480) xs=2;
if(width == 560 && height == 480) {}
if(width == 700 && height == 480) {}
xm = (700-width*xs)/2;
int curr = 0;
//1. double the height, while cropping down
if(ys==2) //should handle ntsc or pal, but not tested yet for pal
{
uint32* src = VTBuffer[curr]->pixels + (s_ShockConfig.fb_width*espec.DisplayRect.y) + espec.DisplayRect.x;
uint32* dst = VTBuffer[curr^1]->pixels;
int tocopy = width*4;
for(int y=0;y<height;y++)
{
memcpy(dst,src,tocopy);
dst += width;
memcpy(dst,src,tocopy);
dst += width;
src += s_FramebufferCurrentWidth;
}
//patch up the metrics
height *= 2;
espec.DisplayRect.x = 0;
espec.DisplayRect.y = 0;
espec.DisplayRect.h = height;
s_FramebufferCurrentWidth = width;
VTLineWidths[curr^1][0] = VTLineWidths[curr][0];
curr ^= 1;
}
//2. double the width as needed. but always float it.
//note, theres nothing to be done here if the framebuffer is already wide enough
if(width != 700)
{
uint32* src = VTBuffer[curr]->pixels + (s_ShockConfig.fb_width*espec.DisplayRect.y) + espec.DisplayRect.x;
uint32* dst = VTBuffer[curr^1]->pixels;
for(int y=0;y<height;y++)
{
//float the content horizontally
for(int x=0;x<xm;x++)
*dst++ = 0;
if(xs==2)
{
for(int x=0;x<width;x++)
{
*dst++ = *src;
*dst++ = *src++;
}
src += s_FramebufferCurrentWidth - width;
}
else
{
memcpy(dst,src,width*4);
dst += width;
src += s_FramebufferCurrentWidth;
}
//float the content horizontally
for(int x=0;x<xm;x++)
*dst++ = 0;
}
//patch up the metrics
width = 700; //we floated the content horizontally, so this becomes the new width
espec.DisplayRect.x = 0;
espec.DisplayRect.y = 0;
VTLineWidths[curr^1][0] = width;
s_FramebufferCurrentWidth = width;
curr ^= 1;
}
s_FramebufferCurrent = curr;
}
EW_EXPORT s32 shock_GetFramebuffer(void* psx, ShockFramebufferInfo* fb)
{
//if user requires normalization, do it now
if(fb->flags & eShockFramebufferFlags_Normalize)
if(!s_FramebufferNormalized)
{
NormalizeFramebuffer();
s_FramebufferNormalized = true;
}
int fbIndex = s_FramebufferCurrent;
//always fetch description
int width = VTLineWidths[fbIndex][0]; //presently, except for contrived test programs, it is safe to assume this is the same for the entire frame (no known use by games)
int height = espec.DisplayRect.h;
fb->width = width;
fb->height = height;
@ -1542,7 +1694,7 @@ EW_EXPORT s32 shock_GetFramebuffer(void* psx, ShockFramebufferJob* fb)
//maybe we need to output the framebuffer
//do a raster loop and copy it to the target
uint32* src = VTBuffer[0]->pixels + (s_ShockConfig.fb_width*espec.DisplayRect.y) + espec.DisplayRect.x;
uint32* src = VTBuffer[fbIndex]->pixels + (s_FramebufferCurrentWidth*espec.DisplayRect.y) + espec.DisplayRect.x;
uint32* dst = (u32*)fb->ptr;
int tocopy = width*4;
for(int y=0;y<height;y++)
@ -1551,7 +1703,7 @@ EW_EXPORT s32 shock_GetFramebuffer(void* psx, ShockFramebufferJob* fb)
{
*dst++ = *src++ | 0xFF000000;
}
src += s_ShockConfig.fb_width - width;
src += s_FramebufferCurrentWidth - width;
}
return SHOCK_OK;

View File

@ -125,6 +125,11 @@ enum eShockStep
eShockStep_Frame
};
enum eShockFramebufferFlags
{
eShockFramebufferFlags_None = 0,
eShockFramebufferFlags_Normalize = 1
};
enum eShockSetting
{
@ -203,9 +208,10 @@ struct ShockDiscInfo
char id[5]; //SCEI, SCEA, SCEE, etc. with null terminator
};
struct ShockFramebufferJob
struct ShockFramebufferInfo
{
int width, height;
s32 width, height;
s32 flags;
void* ptr;
};
@ -250,4 +256,4 @@ EW_EXPORT s32 shock_Step(void* psx, eShockStep step);
//Fetches the framebuffer. Can retrieve parameters (set the job ptr to NULL) or fill the provided job ptr with the framebuffer (make sure its big enough).
//This helps us copy fewer times.
//TODO - support pitch and color format conversion if needed
EW_EXPORT s32 shock_GetFramebuffer(void* psx, ShockFramebufferJob* fb);
EW_EXPORT s32 shock_GetFramebuffer(void* psx, ShockFramebufferInfo* fb);

View File

@ -286,15 +286,19 @@ private:
int main(int argc, char **argv)
{
const char* fwpath = argv[1];
const char* discpath = argv[2];
const char* outdir = argv[3];
FILE* inf;
//load up the firmware
char firmware[512*1024];
inf = fopen("myfirmware.bin","rb");
inf = fopen(fwpath,"rb");
fread(firmware,1,512*1024,inf);
fclose(inf);
BinReader2352 bin("mybin.bin");
BinReader2352 bin(discpath);
ShockDiscInfo info;
shock_AnalyzeDisc(bin.disc, &info);
printf("disc id: %s\n",info.id);
@ -316,12 +320,13 @@ int main(int argc, char **argv)
if(framectr%60==0)
{
//dump a screen grab
ShockFramebufferJob fbinfo;
ShockFramebufferInfo fbinfo;
static u32 buf[1024*1024];
fbinfo.ptr = buf;
fbinfo.flags = eShockFramebufferFlags_Normalize;
shock_GetFramebuffer(psx,&fbinfo);
char fname[128];
sprintf(fname,"c:\\dump\\test%03d.bmp",framectr/60);
sprintf(fname,"%s\\test%03d.bmp",outdir,framectr/60);
WriteBMP32(fbinfo.width,fbinfo.height,buf,fname); //rgb is backwards
}