mirror of
https://github.com/libretro/ppsspp.git
synced 2024-11-27 10:20:49 +00:00
Merge branch 'master' of https://github.com/hrydgard/ppsspp into improvedTilControls
This commit is contained in:
commit
fc1da7c796
@ -406,7 +406,7 @@ void PSPSaveDialog::DisplaySaveDataInfo1()
|
||||
snprintf(saveTitle, 512, "%s", param.GetFileInfo(currentSelectedSave).saveTitle);
|
||||
snprintf(saveDetail, 512, "%s", param.GetFileInfo(currentSelectedSave).saveDetail);
|
||||
|
||||
PPGeDrawRect(180, 136, 980, 137, CalcFadedColor(0xFFFFFFFF));
|
||||
PPGeDrawRect(180, 136, 480, 137, CalcFadedColor(0xFFFFFFFF));
|
||||
std::string titleTxt = title;
|
||||
std::string timeTxt = time;
|
||||
std::string saveTitleTxt = saveTitle;
|
||||
|
@ -776,11 +776,11 @@ u32 sceAtracGetChannel(int atracID, u32 channelAddr) {
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracGetChannel(%i, %08x): no data", atracID, channelAddr);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
} else {
|
||||
DEBUG_LOG(ME, "sceAtracGetChannel(%i, %08x)", atracID, channelAddr);
|
||||
if (Memory::IsValidAddress(channelAddr))
|
||||
Memory::Write_U32(atrac->atracChannels, channelAddr);
|
||||
}
|
||||
|
||||
DEBUG_LOG(ME, "sceAtracGetChannel(%i, %08x)", atracID, channelAddr);
|
||||
if (Memory::IsValidAddress(channelAddr))
|
||||
Memory::Write_U32(atrac->atracChannels, channelAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -831,12 +831,12 @@ u32 sceAtracGetMaxSample(int atracID, u32 maxSamplesAddr) {
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracGetMaxSample(%i, %08x): no data", atracID, maxSamplesAddr);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
}
|
||||
|
||||
DEBUG_LOG(ME, "sceAtracGetMaxSample(%i, %08x)", atracID, maxSamplesAddr);
|
||||
if (Memory::IsValidAddress(maxSamplesAddr)) {
|
||||
int atracSamplesPerFrame = (atrac->codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
|
||||
Memory::Write_U32(atracSamplesPerFrame, maxSamplesAddr);
|
||||
} else {
|
||||
DEBUG_LOG(ME, "sceAtracGetMaxSample(%i, %08x)", atracID, maxSamplesAddr);
|
||||
if (Memory::IsValidAddress(maxSamplesAddr)) {
|
||||
int atracSamplesPerFrame = (atrac->codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
|
||||
Memory::Write_U32(atracSamplesPerFrame, maxSamplesAddr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -849,13 +849,17 @@ u32 sceAtracGetNextDecodePosition(int atracID, u32 outposAddr) {
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracGetNextDecodePosition(%i, %08x): no data", atracID, outposAddr);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
} else {
|
||||
DEBUG_LOG(ME, "sceAtracGetNextDecodePosition(%i, %08x)", atracID, outposAddr);
|
||||
if (atrac->currentSample >= atrac->endSample) {
|
||||
if (Memory::IsValidAddress(outposAddr))
|
||||
Memory::Write_U32(0, outposAddr);
|
||||
return ATRAC_ERROR_ALL_DATA_DECODED;
|
||||
} else {
|
||||
if (Memory::IsValidAddress(outposAddr))
|
||||
Memory::Write_U32(atrac->currentSample, outposAddr);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_LOG(ME, "sceAtracGetNextDecodePosition(%i, %08x)", atracID, outposAddr);
|
||||
if (atrac->currentSample >= atrac->endSample)
|
||||
return ATRAC_ERROR_ALL_DATA_DECODED;
|
||||
if (Memory::IsValidAddress(outposAddr))
|
||||
Memory::Write_U32(atrac->currentSample, outposAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -872,6 +876,7 @@ u32 sceAtracGetNextSample(int atracID, u32 outNAddr) {
|
||||
if (atrac->currentSample >= atrac->endSample) {
|
||||
if (Memory::IsValidAddress(outNAddr))
|
||||
Memory::Write_U32(0, outNAddr);
|
||||
return ATRAC_ERROR_ALL_DATA_DECODED;
|
||||
} else {
|
||||
u32 numSamples = atrac->endSample - atrac->currentSample;
|
||||
u32 atracSamplesPerFrame = (atrac->codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
|
||||
@ -892,11 +897,11 @@ u32 sceAtracGetRemainFrame(int atracID, u32 remainAddr) {
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracGetRemainFrame(%i, %08x): no data", atracID, remainAddr);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
} else {
|
||||
DEBUG_LOG(ME, "sceAtracGetRemainFrame(%i, %08x)", atracID, remainAddr);
|
||||
if (Memory::IsValidAddress(remainAddr))
|
||||
Memory::Write_U32(atrac->getRemainFrames(), remainAddr);
|
||||
}
|
||||
|
||||
DEBUG_LOG(ME, "sceAtracGetRemainFrame(%i, %08x)", atracID, remainAddr);
|
||||
if (Memory::IsValidAddress(remainAddr))
|
||||
Memory::Write_U32(atrac->getRemainFrames(), remainAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -908,12 +913,13 @@ u32 sceAtracGetSecondBufferInfo(int atracID, u32 outposAddr, u32 outBytesAddr) {
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracGetSecondBufferInfo(%i, %08x, %08x): no data", atracID, outposAddr, outBytesAddr);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
} else {
|
||||
ERROR_LOG(ME, "sceAtracGetSecondBufferInfo(%i, %08x, %08x)", atracID, outposAddr, outBytesAddr);
|
||||
if (Memory::IsValidAddress(outposAddr) && atrac)
|
||||
Memory::Write_U32(atrac->second.fileoffset, outposAddr);
|
||||
if (Memory::IsValidAddress(outBytesAddr) && atrac)
|
||||
Memory::Write_U32(atrac->second.writableBytes, outBytesAddr);
|
||||
}
|
||||
ERROR_LOG(ME, "sceAtracGetSecondBufferInfo(%i, %08x, %08x)", atracID, outposAddr, outBytesAddr);
|
||||
if (Memory::IsValidAddress(outposAddr) && atrac)
|
||||
Memory::Write_U32(atrac->second.fileoffset, outposAddr);
|
||||
if (Memory::IsValidAddress(outBytesAddr) && atrac)
|
||||
Memory::Write_U32(atrac->second.writableBytes, outBytesAddr);
|
||||
// TODO: Maybe don't write the above?
|
||||
return ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED;
|
||||
}
|
||||
@ -926,15 +932,15 @@ u32 sceAtracGetSoundSample(int atracID, u32 outEndSampleAddr, u32 outLoopStartSa
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracGetSoundSample(%i, %08x, %08x, %08x): no data", atracID, outEndSampleAddr, outLoopStartSampleAddr, outLoopEndSampleAddr);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
} else {
|
||||
DEBUG_LOG(ME, "sceAtracGetSoundSample(%i, %08x, %08x, %08x)", atracID, outEndSampleAddr, outLoopStartSampleAddr, outLoopEndSampleAddr);
|
||||
if (Memory::IsValidAddress(outEndSampleAddr))
|
||||
Memory::Write_U32(atrac->endSample, outEndSampleAddr);
|
||||
if (Memory::IsValidAddress(outLoopStartSampleAddr))
|
||||
Memory::Write_U32(atrac->loopStartSample, outLoopStartSampleAddr);
|
||||
if (Memory::IsValidAddress(outLoopEndSampleAddr))
|
||||
Memory::Write_U32(atrac->loopEndSample, outLoopEndSampleAddr);
|
||||
}
|
||||
|
||||
DEBUG_LOG(ME, "sceAtracGetSoundSample(%i, %08x, %08x, %08x)", atracID, outEndSampleAddr, outLoopStartSampleAddr, outLoopEndSampleAddr);
|
||||
if (Memory::IsValidAddress(outEndSampleAddr))
|
||||
Memory::Write_U32(atrac->endSample, outEndSampleAddr);
|
||||
if (Memory::IsValidAddress(outLoopStartSampleAddr))
|
||||
Memory::Write_U32(atrac->loopStartSample, outLoopStartSampleAddr);
|
||||
if (Memory::IsValidAddress(outLoopEndSampleAddr))
|
||||
Memory::Write_U32(atrac->loopEndSample, outLoopEndSampleAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -949,19 +955,18 @@ u32 sceAtracGetStreamDataInfo(int atracID, u32 writeAddr, u32 writableBytesAddr,
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracGetStreamDataInfo(%i, %08x, %08x, %08x): no data", atracID, writeAddr, writableBytesAddr, readOffsetAddr);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
} else {
|
||||
DEBUG_LOG(ME, "sceAtracGetStreamDataInfo(%i, %08x, %08x, %08x)", atracID, writeAddr, writableBytesAddr, readOffsetAddr);
|
||||
// reset the temp buf for adding more stream data
|
||||
atrac->first.writableBytes = std::min(atrac->first.filesize - atrac->first.size, atrac->atracBufSize);
|
||||
atrac->first.offset = 0;
|
||||
if (Memory::IsValidAddress(writeAddr))
|
||||
Memory::Write_U32(atrac->first.addr, writeAddr);
|
||||
if (Memory::IsValidAddress(writableBytesAddr))
|
||||
Memory::Write_U32(atrac->first.writableBytes, writableBytesAddr);
|
||||
if (Memory::IsValidAddress(readOffsetAddr))
|
||||
Memory::Write_U32(atrac->first.fileoffset, readOffsetAddr);
|
||||
}
|
||||
|
||||
DEBUG_LOG(ME, "sceAtracGetStreamDataInfo(%i, %08x, %08x, %08x)", atracID, writeAddr, writableBytesAddr, readOffsetAddr);
|
||||
// reset the temp buf for adding more stream data
|
||||
atrac->first.writableBytes = std::min(atrac->first.filesize - atrac->first.size, atrac->atracBufSize);
|
||||
atrac->first.offset = 0;
|
||||
|
||||
if (Memory::IsValidAddress(writeAddr))
|
||||
Memory::Write_U32(atrac->first.addr, writeAddr);
|
||||
if (Memory::IsValidAddress(writableBytesAddr))
|
||||
Memory::Write_U32(atrac->first.writableBytes, writableBytesAddr);
|
||||
if (Memory::IsValidAddress(readOffsetAddr))
|
||||
Memory::Write_U32(atrac->first.fileoffset, readOffsetAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1315,17 +1320,17 @@ u32 sceAtracSetLoopNum(int atracID, int loopNum) {
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracSetLoopNum(%i, %i):no data", atracID, loopNum);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
}
|
||||
|
||||
// Spammed in MHU
|
||||
DEBUG_LOG(ME, "sceAtracSetLoopNum(%i, %i)", atracID, loopNum);
|
||||
if (atrac->loopinfoNum == 0)
|
||||
return ATRAC_ERROR_NO_LOOP_INFORMATION;
|
||||
atrac->loopNum = loopNum;
|
||||
if (loopNum != 0 && atrac->loopinfoNum == 0) {
|
||||
// Just loop the whole audio
|
||||
atrac->loopStartSample = 0;
|
||||
atrac->loopEndSample = atrac->endSample;
|
||||
} else {
|
||||
// Spammed in MHU
|
||||
DEBUG_LOG(ME, "sceAtracSetLoopNum(%i, %i)", atracID, loopNum);
|
||||
if (atrac->loopinfoNum == 0)
|
||||
return ATRAC_ERROR_NO_LOOP_INFORMATION;
|
||||
atrac->loopNum = loopNum;
|
||||
if (loopNum != 0 && atrac->loopinfoNum == 0) {
|
||||
// Just loop the whole audio
|
||||
atrac->loopStartSample = 0;
|
||||
atrac->loopEndSample = atrac->endSample;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1385,11 +1390,11 @@ int sceAtracGetOutputChannel(int atracID, u32 outputChanPtr) {
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracGetOutputChannel(%i, %08x): no data", atracID, outputChanPtr);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
} else {
|
||||
DEBUG_LOG(ME, "sceAtracGetOutputChannel(%i, %08x)", atracID, outputChanPtr);
|
||||
if (Memory::IsValidAddress(outputChanPtr))
|
||||
Memory::Write_U32(atrac->atracOutputChannels, outputChanPtr);
|
||||
}
|
||||
|
||||
DEBUG_LOG(ME, "sceAtracGetOutputChannel(%i, %08x)", atracID, outputChanPtr);
|
||||
if (Memory::IsValidAddress(outputChanPtr))
|
||||
Memory::Write_U32(atrac->atracOutputChannels, outputChanPtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1401,7 +1406,7 @@ int sceAtracIsSecondBufferNeeded(int atracID) {
|
||||
} else if (!atrac->data_buf) {
|
||||
ERROR_LOG(ME, "sceAtracIsSecondBufferNeeded(%i): no data", atracID);
|
||||
return ATRAC_ERROR_NO_DATA;
|
||||
}
|
||||
}
|
||||
WARN_LOG(ME, "UNIMPL sceAtracIsSecondBufferNeeded(%i)", atracID);
|
||||
return 0;
|
||||
}
|
||||
|
@ -462,7 +462,7 @@ void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) {
|
||||
if (curFrameTime > nextFrameTime && doFrameSkip) {
|
||||
skipFrame = true;
|
||||
}
|
||||
} else if (g_Config.iFrameSkip > 1) {
|
||||
} else if (g_Config.iFrameSkip > 1) {
|
||||
// Other values = fixed frameskip
|
||||
if (numSkippedFrames >= g_Config.iFrameSkip - 1)
|
||||
skipFrame = false;
|
||||
@ -563,9 +563,15 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
|
||||
// 1.001f to compensate for the classic 59.94 NTSC framerate that the PSP seems to have.
|
||||
DoFrameTiming(throttle, skipFrame, (float)numVBlanksSinceFlip * (1.001f / 60.0f));
|
||||
|
||||
// Max 4 skipped frames in a row - 15 fps is really the bare minimum for playability.
|
||||
// We check for 3 here so it's 3 skipped frames, 1 non skipped, 3 skipped, etc.
|
||||
int maxFrameskip = throttle ? g_Config.iFrameSkip : 8;
|
||||
int maxFrameskip = 8;
|
||||
if (throttle) {
|
||||
if (g_Config.iFrameSkip == 1) {
|
||||
// 4 here means 1 drawn, 4 skipped - so 12 fps minimum.
|
||||
maxFrameskip = 4;
|
||||
} else {
|
||||
maxFrameskip = g_Config.iFrameSkip - 1;
|
||||
}
|
||||
}
|
||||
if (numSkippedFrames >= maxFrameskip) {
|
||||
skipFrame = false;
|
||||
}
|
||||
|
@ -287,7 +287,7 @@ void PPGeBegin()
|
||||
PPGeSetDefaultTexture();
|
||||
|
||||
WriteCmd(GE_CMD_SCISSOR1, (0 << 10) | 0);
|
||||
WriteCmd(GE_CMD_SCISSOR2, (1023 << 10) | 1023);
|
||||
WriteCmd(GE_CMD_SCISSOR2, (271 << 10) | 479);
|
||||
WriteCmd(GE_CMD_MINZ, 0);
|
||||
WriteCmd(GE_CMD_MAXZ, 0xFFFF);
|
||||
|
||||
|
@ -15,11 +15,7 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#if defined(USING_GLES2)
|
||||
#define GLSL_ES_1_0
|
||||
#else
|
||||
#define GLSL_1_3
|
||||
|
||||
#if !defined(USING_GLES2)
|
||||
// SDL 1.2 on Apple does not have support for OpenGL 3 and hence needs
|
||||
// special treatment in the shader generator.
|
||||
#if defined(__APPLE__)
|
||||
@ -131,6 +127,11 @@ bool CanReplaceAlphaWithStencil() {
|
||||
if (!gstate.isStencilTestEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gl_extensions.ARB_blend_func_extended) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gstate.isAlphaBlendEnabled()) {
|
||||
return nonAlphaSrcFactors[gstate.getBlendFuncA()] && nonAlphaDestFactors[gstate.getBlendFuncA()];
|
||||
}
|
||||
@ -295,27 +296,49 @@ void ComputeFragmentShaderID(FragmentShaderID *id) {
|
||||
void GenerateFragmentShader(char *buffer) {
|
||||
char *p = buffer;
|
||||
|
||||
// In GLSL ES 3.0, you use "in" variables instead of varying.
|
||||
bool glslES30 = false;
|
||||
const char *varying = "varying";
|
||||
bool highpFog = false;
|
||||
#if defined(GLSL_ES_1_0)
|
||||
WRITE(p, "#version 100\n"); // GLSL ES 1.0
|
||||
|
||||
#if defined(USING_GLES2)
|
||||
// Let's wait until we have a real use for this.
|
||||
// ES doesn't support dual source alpha :(
|
||||
if (false && gl_extensions.GLES3) {
|
||||
WRITE(p, "#version 300 es\n"); // GLSL ES 1.0
|
||||
glslES30 = true;
|
||||
} else {
|
||||
WRITE(p, "#version 100\n"); // GLSL ES 1.0
|
||||
}
|
||||
WRITE(p, "precision lowp float;\n");
|
||||
|
||||
// PowerVR needs highp to do the fog in MHU correctly.
|
||||
// Others don't, and some can't handle highp in the fragment shader.
|
||||
highpFog = gl_extensions.gpuVendor == GPU_VENDOR_POWERVR;
|
||||
#elif !defined(FORCE_OPENGL_2_0)
|
||||
WRITE(p, "#version 110\n");
|
||||
// Remove lowp/mediump in non-mobile implementations
|
||||
WRITE(p, "#define lowp\n");
|
||||
WRITE(p, "#define mediump\n");
|
||||
WRITE(p, "#define highp\n");
|
||||
if (gl_extensions.VersionGEThan(3, 3, 0)) {
|
||||
glslES30 = true;
|
||||
WRITE(p, "#version 330\n");
|
||||
} else if (gl_extensions.VersionGEThan(3, 0, 0)) {
|
||||
WRITE(p, "#version 130\n");
|
||||
} else {
|
||||
WRITE(p, "#version 110\n");
|
||||
// Remove lowp/mediump in non-mobile non-glsl 3 implementations
|
||||
WRITE(p, "#define lowp\n");
|
||||
WRITE(p, "#define mediump\n");
|
||||
WRITE(p, "#define highp\n");
|
||||
}
|
||||
#else
|
||||
// Remove lowp/mediump in non-mobile implementations
|
||||
// Need to remove lowp/mediump for Mac
|
||||
WRITE(p, "#define lowp\n");
|
||||
WRITE(p, "#define mediump\n");
|
||||
WRITE(p, "#define highp\n");
|
||||
#endif
|
||||
|
||||
if (glslES30) {
|
||||
varying = "in";
|
||||
}
|
||||
|
||||
bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled();
|
||||
bool doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
|
||||
bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear();
|
||||
@ -343,19 +366,19 @@ void GenerateFragmentShader(char *buffer) {
|
||||
if (gstate.isTextureMapEnabled() && gstate.getTextureFunction() == GE_TEXFUNC_BLEND)
|
||||
WRITE(p, "uniform lowp vec3 u_texenv;\n");
|
||||
|
||||
WRITE(p, "varying lowp vec4 v_color0;\n");
|
||||
WRITE(p, "%s lowp vec4 v_color0;\n", varying);
|
||||
if (lmode)
|
||||
WRITE(p, "varying lowp vec3 v_color1;\n");
|
||||
WRITE(p, "%s lowp vec3 v_color1;\n", varying);
|
||||
if (enableFog) {
|
||||
WRITE(p, "uniform lowp vec3 u_fogcolor;\n");
|
||||
WRITE(p, "varying %s float v_fogdepth;\n", highpFog ? "highp" : "mediump");
|
||||
WRITE(p, "%s %s float v_fogdepth;\n", varying, highpFog ? "highp" : "mediump");
|
||||
}
|
||||
if (doTexture)
|
||||
{
|
||||
if (doTextureProjection)
|
||||
WRITE(p, "varying mediump vec3 v_texcoord;\n");
|
||||
WRITE(p, "%s mediump vec3 v_texcoord;\n", varying);
|
||||
else
|
||||
WRITE(p, "varying mediump vec2 v_texcoord;\n");
|
||||
WRITE(p, "%s mediump vec2 v_texcoord;\n", varying);
|
||||
}
|
||||
|
||||
if (enableAlphaTest) {
|
||||
@ -370,12 +393,16 @@ void GenerateFragmentShader(char *buffer) {
|
||||
else
|
||||
WRITE(p, "vec3 roundAndScaleTo255v(in vec3 x) { return floor(x * 255.0 + 0.5); }\n");
|
||||
}
|
||||
if (gl_extensions.ARB_blend_func_extended) {
|
||||
WRITE(p, "out lowp vec4 fragColor0;\n");
|
||||
WRITE(p, "out lowp vec4 fragColor1;\n");
|
||||
}
|
||||
|
||||
WRITE(p, "void main() {\n");
|
||||
|
||||
if (gstate.isModeClear()) {
|
||||
// Clear mode does not allow any fancy shading.
|
||||
WRITE(p, " gl_FragColor = v_color0;\n");
|
||||
WRITE(p, " vec4 v = v_color0;\n");
|
||||
} else {
|
||||
const char *secondary = "";
|
||||
// Secondary color for specular on top of texture
|
||||
@ -475,31 +502,38 @@ void GenerateFragmentShader(char *buffer) {
|
||||
|
||||
if (enableFog) {
|
||||
WRITE(p, " float fogCoef = clamp(v_fogdepth, 0.0, 1.0);\n");
|
||||
WRITE(p, " gl_FragColor = mix(vec4(u_fogcolor, v.a), v, fogCoef);\n");
|
||||
WRITE(p, " v = mix(vec4(u_fogcolor, v.a), v, fogCoef);\n");
|
||||
// WRITE(p, " v.x = v_depth;\n");
|
||||
} else {
|
||||
WRITE(p, " gl_FragColor = v;\n");
|
||||
}
|
||||
}
|
||||
|
||||
const char *fragColor = "gl_FragColor";
|
||||
if (gl_extensions.ARB_blend_func_extended) {
|
||||
WRITE(p, " fragColor0 = vec4(v.rgb, 0.0);\n");
|
||||
WRITE(p, " fragColor1 = vec4(0.0, 0.0, 0.0, v.a);\n");
|
||||
fragColor = "fragColor0";
|
||||
} else {
|
||||
WRITE(p, " gl_FragColor = v;\n");
|
||||
}
|
||||
|
||||
if (stencilToAlpha) {
|
||||
switch (ReplaceAlphaWithStencilType()) {
|
||||
case STENCIL_VALUE_UNIFORM:
|
||||
WRITE(p, " gl_FragColor.a = u_stencilReplaceValue;\n");
|
||||
WRITE(p, " %s.a = u_stencilReplaceValue;\n", fragColor);
|
||||
break;
|
||||
|
||||
case STENCIL_VALUE_ZERO:
|
||||
WRITE(p, " gl_FragColor.a = 0.0;\n");
|
||||
WRITE(p, " %s.a = 0.0;\n", fragColor);
|
||||
break;
|
||||
|
||||
case STENCIL_VALUE_ONE:
|
||||
WRITE(p, " gl_FragColor.a = 1.0;\n");
|
||||
WRITE(p, " %s.a = 1.0;\n", fragColor);
|
||||
break;
|
||||
|
||||
case STENCIL_VALUE_UNKNOWN:
|
||||
// Maybe we should even mask away alpha using glColorMask and not change it at all? We do get here
|
||||
// if the stencil mode is KEEP for example.
|
||||
WRITE(p, " gl_FragColor.a = 0.0;\n");
|
||||
WRITE(p, " %s.a = 0.0;\n", fragColor);
|
||||
break;
|
||||
|
||||
case STENCIL_VALUE_KEEP:
|
||||
|
@ -570,7 +570,7 @@ static void EstimateDrawingSize(int &drawing_width, int &drawing_height) {
|
||||
int region_height = gstate.getRegionY2() + 1;
|
||||
int scissor_width = gstate.getScissorX2() + 1;
|
||||
int scissor_height = gstate.getScissorY2() + 1;
|
||||
int fb_stride = gstate.fbwidth & 0x3C0;
|
||||
int fb_stride = gstate.FrameBufStride();
|
||||
|
||||
DEBUG_LOG(SCEGE,"viewport : %ix%i, region : %ix%i , scissor: %ix%i, stride: %i, %i", viewport_width,viewport_height, region_width, region_height, scissor_width, scissor_height, fb_stride, gstate.isModeThrough());
|
||||
|
||||
@ -662,10 +662,10 @@ void FramebufferManager::SetRenderFrameBuffer() {
|
||||
|
||||
// Get parameters
|
||||
u32 fb_address = gstate.getFrameBufRawAddress();
|
||||
int fb_stride = gstate.fbwidth & 0x3C0;
|
||||
int fb_stride = gstate.FrameBufStride();
|
||||
|
||||
u32 z_address = gstate.getDepthBufRawAddress();
|
||||
int z_stride = gstate.zbwidth & 0x3C0;
|
||||
int z_stride = gstate.DepthBufStride();
|
||||
|
||||
// Yeah this is not completely right. but it'll do for now.
|
||||
//int drawing_width = ((gstate.region2) & 0x3FF) + 1;
|
||||
@ -1557,7 +1557,7 @@ void FramebufferManager::Resized() {
|
||||
|
||||
bool FramebufferManager::GetCurrentFramebuffer(GPUDebugBuffer &buffer) {
|
||||
u32 fb_address = gstate.getFrameBufRawAddress();
|
||||
int fb_stride = gstate.fbwidth & 0x3C0;
|
||||
int fb_stride = gstate.FrameBufStride();
|
||||
|
||||
VirtualFramebuffer *vfb = currentRenderVfb_;
|
||||
if (!vfb) {
|
||||
@ -1584,10 +1584,10 @@ bool FramebufferManager::GetCurrentFramebuffer(GPUDebugBuffer &buffer) {
|
||||
|
||||
bool FramebufferManager::GetCurrentDepthbuffer(GPUDebugBuffer &buffer) {
|
||||
u32 fb_address = gstate.getFrameBufRawAddress();
|
||||
int fb_stride = gstate.fbwidth & 0x3C0;
|
||||
int fb_stride = gstate.FrameBufStride();
|
||||
|
||||
u32 z_address = gstate.getDepthBufRawAddress();
|
||||
int z_stride = gstate.zbwidth & 0x3C0;
|
||||
int z_stride = gstate.DepthBufStride();
|
||||
|
||||
VirtualFramebuffer *vfb = currentRenderVfb_;
|
||||
if (!vfb) {
|
||||
@ -1617,7 +1617,7 @@ bool FramebufferManager::GetCurrentDepthbuffer(GPUDebugBuffer &buffer) {
|
||||
|
||||
bool FramebufferManager::GetCurrentStencilbuffer(GPUDebugBuffer &buffer) {
|
||||
u32 fb_address = gstate.getFrameBufRawAddress();
|
||||
int fb_stride = gstate.fbwidth & 0x3C0;
|
||||
int fb_stride = gstate.FrameBufStride();
|
||||
|
||||
VirtualFramebuffer *vfb = currentRenderVfb_;
|
||||
if (!vfb) {
|
||||
|
@ -1549,8 +1549,13 @@ void GLES_GPU::DoBlockTransfer() {
|
||||
// TODO: Notify all overlapping FBOs that they need to reload.
|
||||
|
||||
textureCache_.Invalidate(dstBasePtr + (dstY * dstStride + dstX) * bpp, height * dstStride * bpp, GPU_INVALIDATE_HINT);
|
||||
if (Memory::IsRAMAddress(srcBasePtr) && Memory::IsVRAMAddress(dstBasePtr))
|
||||
framebufferManager_.UpdateFromMemory(dstBasePtr, (dstY + height) * dstStride * bpp, true);
|
||||
if (Memory::IsRAMAddress(srcBasePtr) && Memory::IsVRAMAddress(dstBasePtr)) {
|
||||
// TODO: This causes glitches in Tactics Ogre if we don't implement both ways (which will probably be slow...)
|
||||
// The main thing this helps is videos, which will have a matching stride, and zero x/y.
|
||||
if (dstStride == srcStride && dstY == 0 && dstX == 0 && srcX == 0 && srcY == 0) {
|
||||
framebufferManager_.UpdateFromMemory(dstBasePtr, (dstY + height) * dstStride * bpp, true);
|
||||
}
|
||||
}
|
||||
|
||||
// A few games use this INSTEAD of actually drawing the video image to the screen, they just blast it to
|
||||
// the backbuffer. Detect this and have the framebuffermanager draw the pixels.
|
||||
|
@ -96,6 +96,14 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans
|
||||
glBindAttribLocation(program, ATTR_COLOR0, "color0");
|
||||
glBindAttribLocation(program, ATTR_COLOR1, "color1");
|
||||
|
||||
#ifndef USING_GLES2
|
||||
if (gl_extensions.ARB_blend_func_extended) {
|
||||
// Dual source alpha
|
||||
glBindFragDataLocationIndexed(program, 0, 0, "fragColor0");
|
||||
glBindFragDataLocationIndexed(program, 0, 1, "fragColor1");
|
||||
}
|
||||
#endif
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
// Detaching shaders is annoying when debugging with gDebugger
|
||||
@ -213,6 +221,8 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans
|
||||
if (u_world != -1) availableUniforms |= DIRTY_WORLDMATRIX;
|
||||
if (u_view != -1) availableUniforms |= DIRTY_VIEWMATRIX;
|
||||
if (u_texmtx != -1) availableUniforms |= DIRTY_TEXMATRIX;
|
||||
if (u_stencilReplaceValue != -1) availableUniforms |= DIRTY_STENCILREPLACEVALUE;
|
||||
|
||||
// Looping up to numBones lets us avoid checking u_bone[i]
|
||||
for (int i = 0; i < numBones; i++) {
|
||||
if (u_bone[i] != -1)
|
||||
@ -474,7 +484,7 @@ void LinkedShader::UpdateUniforms(u32 vertType) {
|
||||
}
|
||||
|
||||
if (dirty & DIRTY_STENCILREPLACEVALUE) {
|
||||
glUniform1f(u_stencilReplaceValue, gstate.getStencilTestRef());
|
||||
glUniform1f(u_stencilReplaceValue, (float)gstate.getStencilTestRef() * (1.0f / 255.0f));
|
||||
}
|
||||
// TODO: Could even set all bones in one go if they're all dirty.
|
||||
#ifdef USE_BONE_ARRAY
|
||||
|
@ -129,6 +129,19 @@ static const GLushort logicOps[] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
static GLenum toDualSource(GLenum blendfunc) {
|
||||
switch (blendfunc) {
|
||||
#ifndef USING_GLES2
|
||||
case GL_SRC_ALPHA:
|
||||
return GL_SRC1_ALPHA;
|
||||
case GL_ONE_MINUS_SRC_ALPHA:
|
||||
return GL_ONE_MINUS_SRC1_ALPHA;
|
||||
#endif
|
||||
default:
|
||||
return blendfunc;
|
||||
}
|
||||
}
|
||||
|
||||
static GLenum blendColor2Func(u32 fix) {
|
||||
if (fix == 0xFFFFFF)
|
||||
return GL_ONE;
|
||||
@ -182,13 +195,19 @@ void TransformDrawEngine::ApplyDrawState(int prim) {
|
||||
float constantAlpha = 1.0f;
|
||||
if (gstate.isStencilTestEnabled() && !CanReplaceAlphaWithStencil()) {
|
||||
if (gstate.isStencilTestEnabled() == STENCIL_VALUE_UNIFORM) {
|
||||
constantAlpha = gstate.getStencilTestRef() * (1.0f / 255.0f);
|
||||
constantAlpha = (float) gstate.getStencilTestRef() * (1.0f / 255.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Shortcut by using GL_ONE where possible, no need to set blendcolor
|
||||
GLuint glBlendFuncA = blendFuncA == GE_SRCBLEND_FIXA ? blendColor2Func(gstate.getFixA()) : aLookup[blendFuncA];
|
||||
GLuint glBlendFuncB = blendFuncB == GE_DSTBLEND_FIXB ? blendColor2Func(gstate.getFixB()) : bLookup[blendFuncB];
|
||||
|
||||
if (gl_extensions.ARB_blend_func_extended) {
|
||||
glBlendFuncA = toDualSource(glBlendFuncA);
|
||||
glBlendFuncB = toDualSource(glBlendFuncB);
|
||||
}
|
||||
|
||||
if (blendFuncA == GE_SRCBLEND_FIXA || blendFuncB == GE_DSTBLEND_FIXB) {
|
||||
Vec3f fixA = Vec3f::FromRGB(gstate.getFixA());
|
||||
Vec3f fixB = Vec3f::FromRGB(gstate.getFixB());
|
||||
|
@ -139,16 +139,37 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
|
||||
|
||||
// #define USE_FOR_LOOP
|
||||
|
||||
// In GLSL ES 3.0, you use "out" variables instead.
|
||||
bool glslES30 = false;
|
||||
const char *varying = "varying";
|
||||
const char *attribute = "attribute";
|
||||
bool highpFog = false;
|
||||
|
||||
#if defined(USING_GLES2)
|
||||
WRITE(p, "#version 100\n"); // GLSL ES 1.0
|
||||
// Let's wait until we have a real use for this.
|
||||
// ES doesn't support dual source alpha :(
|
||||
if (false && gl_extensions.GLES3) {
|
||||
WRITE(p, "version 300 es\n");
|
||||
glslES30 = true;
|
||||
} else {
|
||||
WRITE(p, "#version 100\n"); // GLSL ES 1.0
|
||||
}
|
||||
WRITE(p, "precision highp float;\n");
|
||||
|
||||
// PowerVR needs highp to do the fog in MHU correctly.
|
||||
// Others don't, and some can't handle highp in the fragment shader.
|
||||
highpFog = gl_extensions.gpuVendor == GPU_VENDOR_POWERVR;
|
||||
#elif !defined(FORCE_OPENGL_2_0)
|
||||
WRITE(p, "#version 110\n");
|
||||
// Remove lowp/mediump in non-mobile implementations
|
||||
WRITE(p, "#define lowp\n");
|
||||
WRITE(p, "#define mediump\n");
|
||||
WRITE(p, "#define highp\n");
|
||||
if (gl_extensions.VersionGEThan(3, 3, 0)) {
|
||||
glslES30 = true;
|
||||
WRITE(p, "#version 330\n");
|
||||
} else {
|
||||
WRITE(p, "#version 110\n");
|
||||
// Remove lowp/mediump in non-mobile non-glsl 3 implementations
|
||||
WRITE(p, "#define lowp\n");
|
||||
WRITE(p, "#define mediump\n");
|
||||
WRITE(p, "#define highp\n");
|
||||
}
|
||||
#else
|
||||
// Need to remove lowp/mediump for Mac
|
||||
WRITE(p, "#define lowp\n");
|
||||
@ -156,6 +177,11 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
|
||||
WRITE(p, "#define highp\n");
|
||||
#endif
|
||||
|
||||
if (glslES30) {
|
||||
attribute = "in";
|
||||
varying = "out";
|
||||
}
|
||||
|
||||
int lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled();
|
||||
int doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
|
||||
bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
|
||||
@ -185,23 +211,23 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
|
||||
}
|
||||
|
||||
if (useHWTransform)
|
||||
WRITE(p, "attribute vec3 position;\n");
|
||||
WRITE(p, "%s vec3 position;\n", attribute);
|
||||
else
|
||||
WRITE(p, "attribute vec4 position;\n"); // need to pass the fog coord in w
|
||||
WRITE(p, "%s vec4 position;\n", attribute); // need to pass the fog coord in w
|
||||
|
||||
if (useHWTransform && hasNormal)
|
||||
WRITE(p, "attribute mediump vec3 normal;\n");
|
||||
WRITE(p, "%s mediump vec3 normal;\n", attribute);
|
||||
|
||||
if (doTexture) {
|
||||
if (!useHWTransform && doTextureProjection)
|
||||
WRITE(p, "attribute vec3 texcoord;\n");
|
||||
WRITE(p, "%s vec3 texcoord;\n", attribute);
|
||||
else
|
||||
WRITE(p, "attribute vec2 texcoord;\n");
|
||||
WRITE(p, "%s vec2 texcoord;\n", attribute);
|
||||
}
|
||||
if (hasColor) {
|
||||
WRITE(p, "attribute lowp vec4 color0;\n");
|
||||
WRITE(p, "%s lowp vec4 color0;\n", attribute);
|
||||
if (lmode && !useHWTransform) // only software transform supplies color1 as vertex data
|
||||
WRITE(p, "attribute lowp vec3 color1;\n");
|
||||
WRITE(p, "%s lowp vec3 color1;\n", attribute);
|
||||
}
|
||||
|
||||
if (gstate.isModeThrough()) {
|
||||
@ -272,22 +298,24 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf
|
||||
WRITE(p, "uniform highp vec2 u_fogcoef;\n");
|
||||
}
|
||||
|
||||
WRITE(p, "varying lowp vec4 v_color0;\n");
|
||||
if (lmode) WRITE(p, "varying lowp vec3 v_color1;\n");
|
||||
WRITE(p, "%s lowp vec4 v_color0;\n", varying);
|
||||
if (lmode) {
|
||||
WRITE(p, "%s lowp vec3 v_color1;\n", varying);
|
||||
}
|
||||
if (doTexture) {
|
||||
if (doTextureProjection)
|
||||
WRITE(p, "varying mediump vec3 v_texcoord;\n");
|
||||
WRITE(p, "%s mediump vec3 v_texcoord;\n", varying);
|
||||
else
|
||||
WRITE(p, "varying mediump vec2 v_texcoord;\n");
|
||||
WRITE(p, "%s mediump vec2 v_texcoord;\n", varying);
|
||||
}
|
||||
|
||||
|
||||
if (enableFog) {
|
||||
// See the fragment shader generator
|
||||
if (gl_extensions.gpuVendor == GPU_VENDOR_POWERVR) {
|
||||
WRITE(p, "varying highp float v_fogdepth;\n");
|
||||
if (highpFog) {
|
||||
WRITE(p, "%s highp float v_fogdepth;\n", varying);
|
||||
} else {
|
||||
WRITE(p, "varying mediump float v_fogdepth;\n");
|
||||
WRITE(p, "%s mediump float v_fogdepth;\n", varying);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,10 +218,10 @@ struct GPUgstate
|
||||
// 0x44000000 is uncached VRAM.
|
||||
u32 getFrameBufAddress() const { return 0x44000000 | getFrameBufRawAddress(); }
|
||||
GEBufferFormat FrameBufFormat() const { return static_cast<GEBufferFormat>(framebufpixformat & 3); }
|
||||
int FrameBufStride() const { return fbwidth&0x7C0; }
|
||||
int FrameBufStride() const { return fbwidth&0x7FC; }
|
||||
u32 getDepthBufRawAddress() const { return (zbptr & 0xFFFFFF) | ((zbwidth & 0xFF0000) << 8); }
|
||||
u32 getDepthBufAddress() const { return 0x44000000 | getDepthBufRawAddress(); }
|
||||
int DepthBufStride() const { return zbwidth&0x7C0; }
|
||||
int DepthBufStride() const { return zbwidth&0x7FC; }
|
||||
|
||||
// Pixel Pipeline
|
||||
bool isModeClear() const { return clearmode & 1; }
|
||||
|
@ -173,6 +173,7 @@ public:
|
||||
Vec3() {}
|
||||
Vec3(const T a[3]) : x(a[0]), y(a[1]), z(a[2]) {}
|
||||
Vec3(const T& _x, const T& _y, const T& _z) : x(_x), y(_y), z(_z) {}
|
||||
Vec3(const Vec2<T>& _xy, const T& _z) : x(_xy.x), y(_xy.y), z(_z) {}
|
||||
|
||||
template<typename T2>
|
||||
Vec3<T2> Cast() const
|
||||
@ -334,6 +335,8 @@ public:
|
||||
Vec4() {}
|
||||
Vec4(const T a[4]) : x(a[0]), y(a[1]), z(a[2]), w(a[3]) {}
|
||||
Vec4(const T& _x, const T& _y, const T& _z, const T& _w) : x(_x), y(_y), z(_z), w(_w) {}
|
||||
Vec4(const Vec2<T>& _xy, const T& _z, const T& _w) : x(_xy.x), y(_xy.y), z(_z), w(_w) {}
|
||||
Vec4(const Vec3<T>& _xyz, const T& _w) : x(_xyz.x), y(_xyz.y), z(_xyz.z), w(_w) {}
|
||||
|
||||
template<typename T2>
|
||||
Vec4<T2> Cast() const
|
||||
|
@ -70,8 +70,7 @@ static inline int GetPixelDataOffset(unsigned int row_pitch_bits, unsigned int u
|
||||
(tile_u % tiles_in_block_horizontal) +
|
||||
(tile_u / tiles_in_block_horizontal) * (tiles_in_block_horizontal*tiles_in_block_vertical);
|
||||
|
||||
// TODO: HACK: for some reason, the second part needs to be diviced by two for CLUT4 textures to work properly.
|
||||
return tile_idx * tile_size_bits/8 + ((u % (tile_size_bits / texel_size_bits)))/((texel_size_bits == 4) ? 2 : 1);
|
||||
return tile_idx * (tile_size_bits / 8) + ((u % texels_per_tile) * texel_size_bits) / 8;
|
||||
}
|
||||
|
||||
static inline u32 LookupColor(unsigned int index, unsigned int level)
|
||||
@ -206,10 +205,18 @@ static inline void GetTextureCoordinates(const VertexData& v0, const VertexData&
|
||||
{
|
||||
// projection mapping, TODO: Move this code to TransformUnit!
|
||||
Vec3<float> source;
|
||||
if (gstate.getUVProjMode() == GE_PROJMAP_POSITION) {
|
||||
switch (gstate.getUVProjMode()) {
|
||||
case GE_PROJMAP_POSITION:
|
||||
source = ((v0.modelpos * w0 + v1.modelpos * w1 + v2.modelpos * w2) / (w0+w1+w2));
|
||||
} else {
|
||||
break;
|
||||
|
||||
case GE_PROJMAP_UV:
|
||||
source = Vec3f((v0.texturecoords * w0 + v1.texturecoords * w1 + v2.texturecoords * w2) / (w0 + w1 + w2), 0.0f);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG_REPORT(G3D, "Software: Unsupported UV projection mode %x", gstate.getUVProjMode());
|
||||
break;
|
||||
}
|
||||
|
||||
Mat3x3<float> tgen(gstate.tgenMatrix);
|
||||
@ -875,6 +882,10 @@ void DrawTriangleSlice(
|
||||
w1_base += orient2dIncY(-d02.x) * 16 * y1;
|
||||
w2_base += orient2dIncY(d01.x) * 16 * y1;
|
||||
|
||||
// All the z values are the same, no interpolation required.
|
||||
// This is common, and when we interpolate, we lose accuracy.
|
||||
const bool flatZ = v0.screenpos.z == v1.screenpos.z && v0.screenpos.z == v2.screenpos.z;
|
||||
|
||||
for (pprime.y = minY + y1 * 16; pprime.y < minY + y2 * 16; pprime.y += 16,
|
||||
w0_base += orient2dIncY(d12.x)*16,
|
||||
w1_base += orient2dIncY(-d02.x)*16,
|
||||
@ -986,9 +997,10 @@ void DrawTriangleSlice(
|
||||
Vec4<int> texcolor_tr = Vec4<int>::FromRGBA(SampleNearest(texlevel, u[1], v[1], tptr, bufwbits));
|
||||
Vec4<int> texcolor_bl = Vec4<int>::FromRGBA(SampleNearest(texlevel, u[2], v[2], tptr, bufwbits));
|
||||
Vec4<int> texcolor_br = Vec4<int>::FromRGBA(SampleNearest(texlevel, u[3], v[3], tptr, bufwbits));
|
||||
Vec4<int> t = texcolor_tl * (0xff - frac_u) + texcolor_tr * frac_u;
|
||||
Vec4<int> b = texcolor_bl * (0xff - frac_u) + texcolor_br * frac_u;
|
||||
texcolor = (t * (0xff - frac_v) + b * frac_v) / (256 * 256);
|
||||
// 0x100 causes a slight bias to tl, but without it we'd have to divide by 255 * 255.
|
||||
Vec4<int> t = texcolor_tl * (0x100 - frac_u) + texcolor_tr * frac_u;
|
||||
Vec4<int> b = texcolor_bl * (0x100 - frac_u) + texcolor_br * frac_u;
|
||||
texcolor = (t * (0x100 - frac_v) + b * frac_v) / (256 * 256);
|
||||
}
|
||||
Vec4<int> out = GetTextureFunctionOutput(prim_color_rgb, prim_color_a, texcolor);
|
||||
prim_color_rgb = out.rgb();
|
||||
@ -1006,9 +1018,11 @@ void DrawTriangleSlice(
|
||||
|
||||
// TODO: Fogging
|
||||
|
||||
u16 z = v2.screenpos.z;
|
||||
// TODO: Is that the correct way to interpolate?
|
||||
// Without the (u32), this causes an ICE in some versions of gcc.
|
||||
u16 z = (u16)(u32)(((float)v0.screenpos.z * w0 + (float)v1.screenpos.z * w1 + (float)v2.screenpos.z * w2) * wsum);
|
||||
if (!flatZ)
|
||||
z = (u16)(u32)(((float)v0.screenpos.z * w0 + (float)v1.screenpos.z * w1 + (float)v2.screenpos.z * w2) * wsum);
|
||||
|
||||
// Depth range test
|
||||
// TODO: Clear mode?
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "GPU/GPUState.h"
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/HLE/sceKernelInterrupt.h"
|
||||
@ -145,6 +146,12 @@ SoftGPU::SoftGPU()
|
||||
|
||||
fb.data = Memory::GetPointer(0x44000000); // TODO: correct default address?
|
||||
depthbuf.data = Memory::GetPointer(0x44000000); // TODO: correct default address?
|
||||
|
||||
framebufferDirty_ = true;
|
||||
// TODO: Is there a default?
|
||||
displayFramebuf_ = 0;
|
||||
displayStride_ = 512;
|
||||
displayFormat_ = GE_FORMAT_8888;
|
||||
}
|
||||
|
||||
SoftGPU::~SoftGPU()
|
||||
@ -154,7 +161,7 @@ SoftGPU::~SoftGPU()
|
||||
}
|
||||
|
||||
// Copies RGBA8 data from RAM to the currently bound render target.
|
||||
void SoftGPU::CopyToCurrentFboFromRam(u8* data, int srcwidth, int srcheight, int dstwidth, int dstheight)
|
||||
void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight, int dstwidth, int dstheight)
|
||||
{
|
||||
glDisable(GL_BLEND);
|
||||
glViewport(0, 0, dstwidth, dstheight);
|
||||
@ -163,18 +170,25 @@ void SoftGPU::CopyToCurrentFboFromRam(u8* data, int srcwidth, int srcheight, int
|
||||
glBindTexture(GL_TEXTURE_2D, temp_texture);
|
||||
|
||||
GLfloat texvert_u;
|
||||
if (gstate.FrameBufFormat() == GE_FORMAT_8888) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)gstate.FrameBufStride(), (GLsizei)srcheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
texvert_u = (float)srcwidth / gstate.FrameBufStride();
|
||||
if (displayFramebuf_ == 0) {
|
||||
u32 data[] = {0};
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
texvert_u = 1.0f;
|
||||
} else if (displayFormat_ == GE_FORMAT_8888) {
|
||||
u8 *data = Memory::GetPointer(displayFramebuf_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)displayStride_, (GLsizei)srcheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
texvert_u = (float)srcwidth / displayStride_;
|
||||
} else {
|
||||
// TODO: This should probably be converted in a shader instead..
|
||||
// TODO: Do something less brain damaged to manage this buffer...
|
||||
u32 *buf = new u32[srcwidth * srcheight];
|
||||
FormatBuffer displayBuffer;
|
||||
displayBuffer.data = Memory::GetPointer(displayFramebuf_);
|
||||
for (int y = 0; y < srcheight; ++y) {
|
||||
u32 *buf_line = &buf[y * srcwidth];
|
||||
const u16 *fb_line = &fb.as16[y * gstate.FrameBufStride()];
|
||||
const u16 *fb_line = &displayBuffer.as16[y * displayStride_];
|
||||
|
||||
switch (gstate.FrameBufFormat()) {
|
||||
switch (displayFormat_) {
|
||||
case GE_FORMAT_565:
|
||||
for (int x = 0; x < srcwidth; ++x) {
|
||||
buf_line[x] = DecodeRGB565(fb_line[x]);
|
||||
@ -194,7 +208,7 @@ void SoftGPU::CopyToCurrentFboFromRam(u8* data, int srcwidth, int srcheight, int
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG_REPORT(G3D, "Software: Unexpected framebuffer format: %d", gstate.FrameBufFormat());
|
||||
ERROR_LOG_REPORT(G3D, "Software: Unexpected framebuffer format: %d", displayFormat_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,7 +256,8 @@ void SoftGPU::CopyDisplayToOutput()
|
||||
void SoftGPU::CopyDisplayToOutputInternal()
|
||||
{
|
||||
// The display always shows 480x272.
|
||||
CopyToCurrentFboFromRam(fb.data, FB_WIDTH, FB_HEIGHT, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
|
||||
CopyToCurrentFboFromDisplayRam(FB_WIDTH, FB_HEIGHT, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
|
||||
framebufferDirty_ = false;
|
||||
}
|
||||
|
||||
void SoftGPU::ProcessEvent(GPUEvent ev) {
|
||||
@ -354,6 +369,7 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff)
|
||||
cyclesExecuted += EstimatePerVertexCost() * count;
|
||||
int bytesRead;
|
||||
TransformUnit::SubmitPrimitive(verts, indices, type, count, gstate.vertType, &bytesRead);
|
||||
framebufferDirty_ = true;
|
||||
|
||||
// After drawing, we advance the vertexAddr (when non indexed) or indexAddr (when indexed).
|
||||
// Some games rely on this, they don't bother reloading VADDR and IADDR.
|
||||
@ -408,6 +424,7 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff)
|
||||
if (!(gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME)) {
|
||||
TransformUnit::SubmitSpline(control_points, indices, sp_ucount, sp_vcount, sp_utype, sp_vtype, gstate.getPatchPrimitiveType(), gstate.vertType);
|
||||
}
|
||||
framebufferDirty_ = true;
|
||||
DEBUG_LOG(G3D,"DL DRAW SPLINE: %i x %i, %i x %i", sp_ucount, sp_vcount, sp_utype, sp_vtype);
|
||||
}
|
||||
break;
|
||||
@ -570,6 +587,8 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff)
|
||||
CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc);
|
||||
#endif
|
||||
|
||||
// Could theoretically dirty the framebuffer.
|
||||
framebufferDirty_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -797,6 +816,22 @@ void SoftGPU::UpdateMemory(u32 dest, u32 src, int size)
|
||||
{
|
||||
// Nothing to update.
|
||||
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
|
||||
// Let's just be safe.
|
||||
framebufferDirty_ = true;
|
||||
}
|
||||
|
||||
bool SoftGPU::FramebufferDirty() {
|
||||
if (g_Config.bSeparateCPUThread) {
|
||||
// Allow it to process fully before deciding if it's dirty.
|
||||
SyncThread();
|
||||
}
|
||||
|
||||
if (g_Config.iFrameSkip != 0) {
|
||||
bool dirty = framebufferDirty_;
|
||||
framebufferDirty_ = false;
|
||||
return dirty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoftGPU::GetCurrentFramebuffer(GPUDebugBuffer &buffer)
|
||||
|
@ -56,6 +56,9 @@ public:
|
||||
|
||||
virtual void BeginFrame() {}
|
||||
virtual void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
|
||||
displayFramebuf_ = framebuf;
|
||||
displayStride_ = stride;
|
||||
displayFormat_ = format;
|
||||
host->GPUNotifyDisplay(framebuf, stride, format);
|
||||
}
|
||||
virtual void CopyDisplayToOutput();
|
||||
@ -73,6 +76,8 @@ public:
|
||||
fullInfo = "Software";
|
||||
}
|
||||
|
||||
virtual bool FramebufferDirty();
|
||||
|
||||
virtual bool FramebufferReallyDirty() {
|
||||
return !(gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME);
|
||||
}
|
||||
@ -85,8 +90,13 @@ public:
|
||||
protected:
|
||||
virtual void FastRunLoop(DisplayList &list);
|
||||
virtual void ProcessEvent(GPUEvent ev);
|
||||
void CopyToCurrentFboFromRam(u8* data, int srcwidth, int srcheight, int dstwidth, int dstheight);
|
||||
void CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight, int dstwidth, int dstheight);
|
||||
|
||||
private:
|
||||
void CopyDisplayToOutputInternal();
|
||||
|
||||
bool framebufferDirty_;
|
||||
u32 displayFramebuf_;
|
||||
u32 displayStride_;
|
||||
GEBufferFormat displayFormat_;
|
||||
};
|
||||
|
@ -676,4 +676,4 @@ void EmuScreen::autoLoad() {
|
||||
if (g_Config.bEnableAutoLoad && lastSlot != -1) {
|
||||
SaveState::LoadSlot(lastSlot, 0, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -343,6 +343,7 @@ int main(int argc, const char* argv[])
|
||||
g_Config.iInternalResolution = 1;
|
||||
g_Config.bFrameSkipUnthrottle = false;
|
||||
g_Config.bEnableLogging = fullLog;
|
||||
g_Config.iNumWorkerThreads = 1;
|
||||
|
||||
#ifdef _WIN32
|
||||
InitSysDirectories();
|
||||
|
Loading…
Reference in New Issue
Block a user