Reference and modified code from @arnastia
This commit is contained in:
parent
c2a86d647f
commit
1eaada15df
2 changed files with 140 additions and 99 deletions
|
@ -385,9 +385,9 @@ void GuessDrawingSize(int &drawing_width, int &drawing_height) {
|
|||
int region_height = (gstate.getRegionY2() + 1) ;
|
||||
int fb_width = gstate.fbwidth & 0x3C0;
|
||||
|
||||
DEBUG_LOG(HLE,"Viewport : %ix%i, Region : %ix%i, Stride: %i", viewport_width,viewport_height, region_width, region_height, fb_width);
|
||||
DEBUG_LOG(HLE,"viewport : %ix%i, region : %ix%i, stride: %i", viewport_width,viewport_height, region_width, region_height, fb_width);
|
||||
|
||||
// Just in case viewport return as 0x0 like FF Type-0
|
||||
// In case viewport return as 0x0 like FF Type-0
|
||||
if (viewport_width <= 1 && viewport_height <=1) {
|
||||
drawing_width = default_width;
|
||||
drawing_height = default_height;
|
||||
|
@ -434,7 +434,7 @@ void FramebufferManager::DestroyFramebuf(VirtualFramebuffer *v) {
|
|||
|
||||
void FramebufferManager::SetRenderFrameBuffer() {
|
||||
if (!gstate_c.framebufChanged && currentRenderVfb_) {
|
||||
currentRenderVfb_->last_frame_used = gpuStats.numVBlanks;
|
||||
currentRenderVfb_->last_frame_used = gpuStats.numFlips;
|
||||
return;
|
||||
}
|
||||
gstate_c.framebufChanged = false;
|
||||
|
@ -499,6 +499,7 @@ void FramebufferManager::SetRenderFrameBuffer() {
|
|||
vfb->format = fmt;
|
||||
vfb->usageFlags = FB_USAGE_RENDERTARGET;
|
||||
vfb->dirtyAfterDisplay = true;
|
||||
vfb->memoryUpdated = false;
|
||||
|
||||
if (g_Config.bTrueColor) {
|
||||
vfb->colorDepth = FBO_8888;
|
||||
|
@ -555,6 +556,10 @@ void FramebufferManager::SetRenderFrameBuffer() {
|
|||
|
||||
// We already have it!
|
||||
} else if (vfb != currentRenderVfb_) {
|
||||
bool useMem = g_Config.iRenderingMode == FB_READFBOMEMORY_GPU || g_Config.iRenderingMode == FB_READFBOMEMORY_CPU ? 1 : 0;
|
||||
if(useMem && !vfb->memoryUpdated) {
|
||||
ReadFramebufferToMemory(vfb, true);
|
||||
}
|
||||
// Use it as a render target.
|
||||
DEBUG_LOG(HLE, "Switching render target to FBO for %08x: %i x %i x %i ", vfb->fb_address, vfb->width, vfb->height, vfb->format);
|
||||
vfb->usageFlags |= FB_USAGE_RENDERTARGET;
|
||||
|
@ -562,6 +567,7 @@ void FramebufferManager::SetRenderFrameBuffer() {
|
|||
vfb->last_frame_used = gpuStats.numFlips;
|
||||
frameLastFramebufUsed = gpuStats.numFlips;
|
||||
vfb->dirtyAfterDisplay = true;
|
||||
vfb->memoryUpdated = false;
|
||||
|
||||
if (useBufferedRendering_) {
|
||||
if (vfb->fbo) {
|
||||
|
@ -681,12 +687,18 @@ void FramebufferManager::CopyDisplayToOutput() {
|
|||
}
|
||||
}
|
||||
|
||||
void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb) {
|
||||
void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync) {
|
||||
// This only works with buffered rendering
|
||||
if (!useBufferedRendering_) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef USING_GLES2
|
||||
if(sync) {
|
||||
PackFramebufferAsync_(NULL); // flush async just in case when we go for synchronous update
|
||||
}
|
||||
#endif
|
||||
|
||||
if(vfb) {
|
||||
// We'll pseudo-blit framebuffers here to get a resized and flipped version of vfb.
|
||||
// For now we'll keep these on the same struct as the ones that can get displayed
|
||||
|
@ -748,20 +760,14 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb) {
|
|||
nvfb->fbo = fbo_create(nvfb->width, nvfb->height, 1, true, nvfb->colorDepth);
|
||||
if (!(nvfb->fbo)) {
|
||||
ERROR_LOG(HLE, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight);
|
||||
}
|
||||
|
||||
if (useBufferedRendering_) {
|
||||
if (nvfb->fbo) {
|
||||
fbo_bind_as_render_target(nvfb->fbo);
|
||||
} else {
|
||||
fbo_unbind();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nvfb->last_frame_used = gpuStats.numFlips;
|
||||
bvfbs_.push_back(nvfb);
|
||||
|
||||
fbo_bind_as_render_target(nvfb->fbo);
|
||||
|
||||
glstate.depthWrite.set(GL_TRUE);
|
||||
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glClearColor(0.0f,0.0f,0.0f,1.0f);
|
||||
|
@ -772,34 +778,33 @@ void FramebufferManager::ReadFramebufferToMemory(VirtualFramebuffer *vfb) {
|
|||
nvfb->last_frame_used = gpuStats.numFlips;
|
||||
nvfb->dirtyAfterDisplay = true;
|
||||
|
||||
if (useBufferedRendering_) {
|
||||
if (nvfb->fbo) {
|
||||
fbo_bind_as_render_target(nvfb->fbo);
|
||||
#ifdef USING_GLES2
|
||||
// Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering
|
||||
// to it. This broke stuff before, so now it only clears on the first use of an
|
||||
// FBO in a frame. This means that some games won't be able to avoid the on-some-GPUs
|
||||
// performance-crushing framebuffer reloads from RAM, but we'll have to live with that.
|
||||
if (nvfb->last_frame_used != gpuStats.numFlips) {
|
||||
glstate.depthWrite.set(GL_TRUE);
|
||||
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glClearColor(0,0,0,1);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
fbo_unbind();
|
||||
return;
|
||||
}
|
||||
fbo_bind_as_render_target(nvfb->fbo);
|
||||
|
||||
// Some tiled mobile GPUs benefit IMMENSELY from clearing an FBO before rendering
|
||||
// to it. This broke stuff before, so now it only clears on the first use of an
|
||||
// FBO in a frame. This means that some games won't be able to avoid the on-some-GPUs
|
||||
// performance-crushing framebuffer reloads from RAM, but we'll have to live with that.
|
||||
if (nvfb->last_frame_used != gpuStats.numFlips) {
|
||||
glstate.depthWrite.set(GL_TRUE);
|
||||
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glClearColor(0,0,0,1);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
vfb->memoryUpdated = true;
|
||||
BlitFramebuffer_(vfb, nvfb, false);
|
||||
|
||||
#ifdef USING_GLES2
|
||||
PackFramebufferGLES_(nvfb); // synchronous glReadPixels
|
||||
PackFramebufferSync_(nvfb); // synchronous glReadPixels
|
||||
#else
|
||||
PackFramebufferGL_(nvfb); // asynchronous glReadPixels using PBOs
|
||||
if(!sync) {
|
||||
PackFramebufferAsync_(nvfb); // asynchronous glReadPixels using PBOs
|
||||
} else {
|
||||
PackFramebufferSync_(nvfb); // synchronous glReadPixels
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -879,25 +884,52 @@ void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 stride, u32 height, GEBufferForma
|
|||
|
||||
#ifndef USING_GLES2
|
||||
|
||||
void FramebufferManager::PackFramebufferGL_(VirtualFramebuffer *vfb) {
|
||||
void FramebufferManager::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
|
||||
const int MAX_PBO = 2;
|
||||
GLubyte *packed = 0;
|
||||
bool unbind = false;
|
||||
bool useCPU = g_Config.iRenderingMode == FB_READFBOMEMORY_CPU ? 1 : 0;
|
||||
u8 nextPBO = (currentPBO_ + 1) % MAX_PBO;
|
||||
bool useCPU = g_Config.iRenderingMode == FB_READFBOMEMORY_CPU ? 1 : 0 ;
|
||||
|
||||
// We'll prepare two PBOs to switch between readying and reading
|
||||
if(!pixelBufObj_) {
|
||||
GLuint pbos[2];
|
||||
GLuint pbos[MAX_PBO];
|
||||
|
||||
glGenBuffers(2, pbos);
|
||||
glGenBuffers(MAX_PBO, pbos);
|
||||
|
||||
pixelBufObj_ = new AsyncPBO[2];
|
||||
pixelBufObj_ = new AsyncPBO[MAX_PBO];
|
||||
|
||||
pixelBufObj_[0].handle = pbos[0];
|
||||
pixelBufObj_[0].maxSize = 0;
|
||||
pixelBufObj_[0].reading = false;
|
||||
for(int i = 0; i < MAX_PBO; i++) {
|
||||
pixelBufObj_[i].handle = pbos[i];
|
||||
pixelBufObj_[i].maxSize = 0;
|
||||
pixelBufObj_[i].reading = false;
|
||||
}
|
||||
}
|
||||
|
||||
pixelBufObj_[1].handle = pbos[1];
|
||||
pixelBufObj_[1].maxSize = 0;
|
||||
pixelBufObj_[1].reading = false;
|
||||
|
||||
// Receive previously requested data from a PBO
|
||||
if(pixelBufObj_[nextPBO].reading) {
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBufObj_[nextPBO].handle);
|
||||
packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
||||
|
||||
if(packed) {
|
||||
DEBUG_LOG(HLE, "Reading pbo to mem, bufSize = %u, packed = %08x, fb_address = %08x, stride = %u, pbo = %u",
|
||||
pixelBufObj_[nextPBO].size, packed, pixelBufObj_[nextPBO].fb_address, pixelBufObj_[nextPBO].stride, nextPBO);
|
||||
|
||||
if(useCPU) {
|
||||
ConvertFromRGBA8888(Memory::GetPointer(pixelBufObj_[nextPBO].fb_address), packed,
|
||||
pixelBufObj_[nextPBO].stride, pixelBufObj_[nextPBO].height,
|
||||
pixelBufObj_[nextPBO].format);
|
||||
} else { // We don't need to convert, GPU already did (or should have)
|
||||
Memory::Memcpy(pixelBufObj_[nextPBO].fb_address, packed, pixelBufObj_[nextPBO].size);
|
||||
}
|
||||
|
||||
pixelBufObj_[nextPBO].reading = false;
|
||||
}
|
||||
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
|
||||
unbind = true;
|
||||
}
|
||||
|
||||
// Order packing/readback of the framebuffer
|
||||
|
@ -939,16 +971,23 @@ void FramebufferManager::PackFramebufferGL_(VirtualFramebuffer *vfb) {
|
|||
u32 bufSize = vfb->fb_stride * vfb->height * pixelSize;
|
||||
u32 fb_address = (0x44000000) | vfb->fb_address;
|
||||
|
||||
if (useBufferedRendering_) {
|
||||
if (vfb->fbo) {
|
||||
fbo_bind_for_read(vfb->fbo);
|
||||
} else {
|
||||
fbo_unbind();
|
||||
if(gl_extensions.FBO_ARB) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
return;
|
||||
if (vfb->fbo) {
|
||||
fbo_bind_for_read(vfb->fbo);
|
||||
} else {
|
||||
fbo_unbind();
|
||||
if(gl_extensions.FBO_ARB) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
ERROR_LOG(HLE, "Incomplete source framebuffer, aborting read");
|
||||
fbo_unbind();
|
||||
if(gl_extensions.FBO_ARB) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBufObj_[currentPBO_].handle);
|
||||
|
@ -964,13 +1003,6 @@ void FramebufferManager::PackFramebufferGL_(VirtualFramebuffer *vfb) {
|
|||
pixelBufObj_[currentPBO_].maxSize = bufSize;
|
||||
}
|
||||
|
||||
pixelBufObj_[currentPBO_].fb_address = fb_address;
|
||||
pixelBufObj_[currentPBO_].size = bufSize;
|
||||
pixelBufObj_[currentPBO_].stride = vfb->fb_stride;
|
||||
pixelBufObj_[currentPBO_].height = vfb->height;
|
||||
pixelBufObj_[currentPBO_].format = vfb->format;
|
||||
pixelBufObj_[currentPBO_].reading = true;
|
||||
|
||||
if(useCPU) {
|
||||
// If converting pixel formats on the CPU we'll always request RGBA8888
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
|
@ -981,37 +1013,44 @@ void FramebufferManager::PackFramebufferGL_(VirtualFramebuffer *vfb) {
|
|||
glReadPixels(0, 0, vfb->fb_stride, vfb->height, pixelFormat, pixelType, 0);
|
||||
}
|
||||
|
||||
GLenum error = glGetError();
|
||||
switch(error) {
|
||||
case 0:
|
||||
break;
|
||||
case GL_INVALID_ENUM:
|
||||
ERROR_LOG(HLE, "glReadPixels: GL_INVALID_ENUM");
|
||||
break;
|
||||
case GL_INVALID_VALUE:
|
||||
ERROR_LOG(HLE, "glReadPixels: GL_INVALID_VALUE");
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
// GL_INVALID_OPERATION will happen sometimes midframe but everything
|
||||
// seems to work out when actually mapping buffers?
|
||||
// GL_SAMPLE_BUFFERS, GL_READ_BUFFER, GL_BUFFER_SIZE/MAPPED,
|
||||
// GL_PIXEL_PACK_BUFFER_BINDING, all have the expected values.
|
||||
ERROR_LOG(HLE, "glReadPixels: GL_INVALID_OPERATION");
|
||||
break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
ERROR_LOG(HLE, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION");
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(HLE, "glReadPixels: UNKNOWN OPENGL ERROR %u", error);
|
||||
break;
|
||||
}
|
||||
|
||||
fbo_unbind();
|
||||
if(gl_extensions.FBO_ARB) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
unbind = true;
|
||||
}
|
||||
|
||||
// Receive previously requested data from a PBO
|
||||
u8 nextPBO = (currentPBO_ + 1) % 2;
|
||||
if(pixelBufObj_[nextPBO].reading) {
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBufObj_[nextPBO].handle);
|
||||
packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
||||
|
||||
if(packed) {
|
||||
DEBUG_LOG(HLE, "Reading pbo to mem, bufSize = %u, packed = %08x, fb_address = %08x, pbo = %u",
|
||||
pixelBufObj_[nextPBO].size, packed, pixelBufObj_[nextPBO].fb_address, nextPBO);
|
||||
|
||||
if(useCPU) {
|
||||
ConvertFromRGBA8888(Memory::GetPointer(pixelBufObj_[nextPBO].fb_address), packed,
|
||||
pixelBufObj_[nextPBO].stride, pixelBufObj_[nextPBO].height,
|
||||
pixelBufObj_[nextPBO].format);
|
||||
} else { // We don't need to convert, GPU already did (or should have)
|
||||
Memory::Memcpy(pixelBufObj_[nextPBO].fb_address, packed, pixelBufObj_[nextPBO].size);
|
||||
}
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
|
||||
pixelBufObj_[nextPBO].reading = false;
|
||||
}
|
||||
|
||||
unbind = true;
|
||||
pixelBufObj_[currentPBO_].fb_address = fb_address;
|
||||
pixelBufObj_[currentPBO_].size = bufSize;
|
||||
pixelBufObj_[currentPBO_].stride = vfb->fb_stride;
|
||||
pixelBufObj_[currentPBO_].height = vfb->height;
|
||||
pixelBufObj_[currentPBO_].format = vfb->format;
|
||||
pixelBufObj_[currentPBO_].reading = true;
|
||||
}
|
||||
|
||||
currentPBO_ = nextPBO;
|
||||
|
@ -1023,7 +1062,7 @@ void FramebufferManager::PackFramebufferGL_(VirtualFramebuffer *vfb) {
|
|||
|
||||
#endif
|
||||
|
||||
void FramebufferManager::PackFramebufferGLES_(VirtualFramebuffer *vfb) {
|
||||
void FramebufferManager::PackFramebufferSync_(VirtualFramebuffer *vfb) {
|
||||
if (useBufferedRendering_ && vfb->fbo) {
|
||||
fbo_bind_for_read(vfb->fbo);
|
||||
} else {
|
||||
|
@ -1033,7 +1072,7 @@ void FramebufferManager::PackFramebufferGLES_(VirtualFramebuffer *vfb) {
|
|||
|
||||
// Pixel size always 4 here because we always request RGBA8888
|
||||
size_t bufSize = vfb->fb_stride * vfb->height * 4;
|
||||
u32 fb_address = (0x44000000) | vfb->fb_address;
|
||||
u32 fb_address = (0x04000000) | vfb->fb_address;
|
||||
|
||||
GLubyte *packed = 0;
|
||||
if(vfb->format == GE_FORMAT_8888) {
|
||||
|
@ -1058,7 +1097,9 @@ void FramebufferManager::PackFramebufferGLES_(VirtualFramebuffer *vfb) {
|
|||
case GL_INVALID_VALUE:
|
||||
ERROR_LOG(HLE, "glReadPixels: GL_INVALID_VALUE");
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
case GL_INVALID_OPERATION:
|
||||
// GL_INVALID_OPERATION will happen sometimes midframe but everything
|
||||
// seems to work out when actually reading?
|
||||
ERROR_LOG(HLE, "glReadPixels: GL_INVALID_OPERATION");
|
||||
break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
|
@ -1087,7 +1128,7 @@ void FramebufferManager::EndFrame() {
|
|||
|
||||
#ifndef USING_GLES2
|
||||
// We flush to memory last requested framebuffer, if any
|
||||
PackFramebufferGL_(NULL);
|
||||
PackFramebufferAsync_(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1141,18 +1182,14 @@ std::vector<FramebufferInfo> FramebufferManager::GetFramebufferList() {
|
|||
void FramebufferManager::DecimateFBOs() {
|
||||
fbo_unbind();
|
||||
currentRenderVfb_ = 0;
|
||||
int num = g_Config.iFrameSkip > 0 && g_Config.iFrameSkip != 9 ? g_Config.iFrameSkip : 3;
|
||||
bool skipFrame = (gpuStats.numFlips % num == 0);
|
||||
bool useFramebufferToMem = g_Config.iRenderingMode != FB_BUFFERED_MODE ? 1 : 0;
|
||||
bool useMem = g_Config.iRenderingMode == FB_READFBOMEMORY_GPU || g_Config.iRenderingMode == FB_READFBOMEMORY_CPU ? 1 : 0;
|
||||
|
||||
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
||||
VirtualFramebuffer *vfb = vfbs_[i];
|
||||
int age = frameLastFramebufUsed - vfb->last_frame_used;
|
||||
|
||||
if(useFramebufferToMem) {
|
||||
// Commit framebuffers to memory
|
||||
if(skipFrame && age <= FBO_OLD_AGE)
|
||||
ReadFramebufferToMemory(vfb);
|
||||
if(useMem && !age && !vfb->memoryUpdated) {
|
||||
ReadFramebufferToMemory(vfb);
|
||||
}
|
||||
|
||||
if (vfb == displayFramebuf_ || vfb == prevDisplayFramebuf_ || vfb == prevPrevDisplayFramebuf_) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue