// 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 #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; } // 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); }