2014-09-09 08:12:42 -07:00
|
|
|
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official git repository and contact information can be found at
|
|
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include "Common/Common.h"
|
|
|
|
#include "GPU/Common/FramebufferCommon.h"
|
|
|
|
#include "GPU/GPUState.h"
|
|
|
|
|
|
|
|
FramebufferManagerCommon::FramebufferManagerCommon() :
|
|
|
|
displayFramebufPtr_(0),
|
|
|
|
displayStride_(0),
|
|
|
|
displayFormat_(GE_FORMAT_565),
|
|
|
|
displayFramebuf_(0),
|
|
|
|
prevDisplayFramebuf_(0),
|
|
|
|
prevPrevDisplayFramebuf_(0),
|
|
|
|
frameLastFramebufUsed_(0),
|
|
|
|
currentRenderVfb_(0),
|
|
|
|
framebufRangeEnd_(0) {
|
|
|
|
}
|
|
|
|
|
|
|
|
FramebufferManagerCommon::~FramebufferManagerCommon() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void FramebufferManagerCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) {
|
|
|
|
displayFramebufPtr_ = framebuf;
|
|
|
|
displayStride_ = stride;
|
|
|
|
displayFormat_ = format;
|
|
|
|
}
|
|
|
|
|
2014-09-09 22:09:41 -07:00
|
|
|
VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) {
|
|
|
|
VirtualFramebuffer *match = NULL;
|
|
|
|
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
|
|
|
VirtualFramebuffer *v = vfbs_[i];
|
|
|
|
if (MaskedEqual(v->fb_address, addr)) {
|
|
|
|
// Could check w too but whatever
|
|
|
|
if (match == NULL || match->last_frame_render < v->last_frame_render) {
|
|
|
|
match = v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (match != NULL) {
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_LOG(SCEGE, "Finding no FBO matching address %08x", addr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FramebufferManagerCommon::MaskedEqual(u32 addr1, u32 addr2) {
|
|
|
|
return (addr1 & 0x03FFFFFF) == (addr2 & 0x03FFFFFF);
|
|
|
|
}
|
|
|
|
|
2014-09-09 08:12:42 -07:00
|
|
|
// Heuristics to figure out the size of FBO to create.
|
|
|
|
void FramebufferManagerCommon::EstimateDrawingSize(int &drawing_width, int &drawing_height) {
|
|
|
|
static const int MAX_FRAMEBUF_HEIGHT = 512;
|
|
|
|
const int viewport_width = (int) gstate.getViewportX1();
|
|
|
|
const int viewport_height = (int) gstate.getViewportY1();
|
|
|
|
const int region_width = gstate.getRegionX2() + 1;
|
|
|
|
const int region_height = gstate.getRegionY2() + 1;
|
|
|
|
const int scissor_width = gstate.getScissorX2() + 1;
|
|
|
|
const int scissor_height = gstate.getScissorY2() + 1;
|
|
|
|
const int fb_stride = std::max(gstate.FrameBufStride(), 4);
|
|
|
|
|
|
|
|
// Games don't always set any of these. Take the greatest parameter that looks valid based on stride.
|
|
|
|
if (viewport_width > 4 && viewport_width <= fb_stride) {
|
|
|
|
drawing_width = viewport_width;
|
|
|
|
drawing_height = viewport_height;
|
|
|
|
// Some games specify a viewport with 0.5, but don't have VRAM for 273. 480x272 is the buffer size.
|
|
|
|
if (viewport_width == 481 && region_width == 480 && viewport_height == 273 && region_height == 272) {
|
|
|
|
drawing_width = 480;
|
|
|
|
drawing_height = 272;
|
|
|
|
}
|
|
|
|
// Sometimes region is set larger than the VRAM for the framebuffer.
|
|
|
|
if (region_width <= fb_stride && region_width > drawing_width && region_height <= MAX_FRAMEBUF_HEIGHT) {
|
|
|
|
drawing_width = region_width;
|
|
|
|
drawing_height = std::max(drawing_height, region_height);
|
|
|
|
}
|
|
|
|
// Scissor is often set to a subsection of the framebuffer, so we pay the least attention to it.
|
|
|
|
if (scissor_width <= fb_stride && scissor_width > drawing_width && scissor_height <= MAX_FRAMEBUF_HEIGHT) {
|
|
|
|
drawing_width = scissor_width;
|
|
|
|
drawing_height = std::max(drawing_height, scissor_height);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If viewport wasn't valid, let's just take the greatest anything regardless of stride.
|
|
|
|
drawing_width = std::min(std::max(region_width, scissor_width), fb_stride);
|
|
|
|
drawing_height = std::max(region_height, scissor_height);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assume no buffer is > 512 tall, it couldn't be textured or displayed fully if so.
|
|
|
|
if (drawing_height >= MAX_FRAMEBUF_HEIGHT) {
|
|
|
|
if (region_height < MAX_FRAMEBUF_HEIGHT) {
|
|
|
|
drawing_height = region_height;
|
|
|
|
} else if (scissor_height < MAX_FRAMEBUF_HEIGHT) {
|
|
|
|
drawing_height = scissor_height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (viewport_width != region_width) {
|
|
|
|
// The majority of the time, these are equal. If not, let's check what we know.
|
|
|
|
const u32 fb_address = gstate.getFrameBufAddress();
|
|
|
|
u32 nearest_address = 0xFFFFFFFF;
|
|
|
|
for (size_t i = 0; i < vfbs_.size(); ++i) {
|
|
|
|
const u32 other_address = vfbs_[i]->fb_address | 0x44000000;
|
|
|
|
if (other_address > fb_address && other_address < nearest_address) {
|
|
|
|
nearest_address = other_address;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unless the game is using overlapping buffers, the next buffer should be far enough away.
|
|
|
|
// This catches some cases where we can know this.
|
|
|
|
// Hmm. The problem is that we could only catch it for the first of two buffers...
|
|
|
|
const u32 bpp = gstate.FrameBufFormat() == GE_FORMAT_8888 ? 4 : 2;
|
|
|
|
int avail_height = (nearest_address - fb_address) / (fb_stride * bpp);
|
|
|
|
if (avail_height < drawing_height && avail_height == region_height) {
|
|
|
|
drawing_width = std::min(region_width, fb_stride);
|
|
|
|
drawing_height = avail_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some games draw buffers interleaved, with a high stride/region/scissor but default viewport.
|
|
|
|
if (fb_stride == 1024 && region_width == 1024 && scissor_width == 1024) {
|
|
|
|
drawing_width = 1024;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_LOG(G3D, "Est: %08x V: %ix%i, R: %ix%i, S: %ix%i, STR: %i, THR:%i, Z:%08x = %ix%i", gstate.getFrameBufAddress(), viewport_width,viewport_height, region_width, region_height, scissor_width, scissor_height, fb_stride, gstate.isModeThrough(), gstate.isDepthWriteEnabled() ? gstate.getDepthBufAddress() : 0, drawing_width, drawing_height);
|
|
|
|
}
|