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:
parent
5c53bc3924
commit
542675def4
|
@ -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; } }
|
||||
|
|
|
@ -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.
|
@ -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;
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue