CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/GPU/Common/FramebufferManagerCommon.h
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617// TODO: We now have the tools in thin3d to nearly eliminate the backend-specific framebuffer managers.18// Here's a list of functionality to unify into FramebufferManagerCommon:19// * DrawActiveTexture20// * BlitFramebuffer21//22// Also, in TextureCache we should be able to unify texture-based depal.2324#pragma once2526#include <vector>27#include <unordered_map>2829#include "Common/CommonTypes.h"30#include "Common/Log.h"31#include "Common/GPU/thin3d.h"32#include "Core/ConfigValues.h"33#include "GPU/GPU.h"34#include "GPU/ge_constants.h"35#include "GPU/GPUInterface.h"36#include "GPU/Common/Draw2D.h"3738enum {39FB_USAGE_DISPLAYED_FRAMEBUFFER = 1,40FB_USAGE_RENDER_COLOR = 2,41FB_USAGE_TEXTURE = 4,42FB_USAGE_CLUT = 8,43FB_USAGE_DOWNLOAD = 16,44FB_USAGE_DOWNLOAD_CLEAR = 32,45FB_USAGE_BLUE_TO_ALPHA = 64,46FB_USAGE_FIRST_FRAME_SAVED = 128,47FB_USAGE_RENDER_DEPTH = 256,48FB_USAGE_COLOR_MIXED_DEPTH = 512,49FB_USAGE_INVALIDATE_DEPTH = 1024, // used to clear depth buffers.50};5152enum {53FB_NON_BUFFERED_MODE = 0,54FB_BUFFERED_MODE = 1,55};5657namespace Draw {58class Framebuffer;59}6061class VulkanFBO;62class ShaderWriter;6364// We have to track VFBs and depth buffers together, since bits are shared between the color alpha channel65// and the stencil buffer on the PSP.66// Sometimes, virtual framebuffers need to share a Z buffer. We emulate this by copying from on to the next67// when such a situation is detected. In order to reliably detect this, we separately track depth buffers,68// and they know which color buffer they were used with last.69// Two VirtualFramebuffer can occupy the same address range as long as they have different fb_format.70// In that case, the one with the highest colorBindSeq number is the valid one.71struct VirtualFramebuffer {72u32 fb_address;73u32 z_address; // If 0, it's a "RAM" framebuffer.74u16 fb_stride;75u16 z_stride;7677// The original PSP format of the framebuffer.78// In reality they are all RGBA8888 for better quality but this is what the PSP thinks it is. This is necessary79// when we need to interpret the bits directly (depal or buffer aliasing).80// NOTE: CANNOT be changed after creation anymore!81GEBufferFormat fb_format;8283Draw::Framebuffer *fbo;8485// width/height: The detected size of the current framebuffer, in original PSP pixels.86u16 width;87u16 height;8889// bufferWidth/bufferHeight: The pre-scaling size of the buffer itself. May only be bigger than or equal to width/height.90// In original PSP pixels - actual framebuffer is this size times the render resolution multiplier.91// The buffer may be used to render a width or height from 0 to these values without being recreated.92u16 bufferWidth;93u16 bufferHeight;9495// renderWidth/renderHeight: The scaled size we render at. May be scaled to render at higher resolutions.96// These are simply bufferWidth/Height * renderScaleFactor and are thus redundant.97u16 renderWidth;98u16 renderHeight;99100// Attempt to keep track of a bounding rectangle of what's been actually drawn. Coarse, but might be smaller101// than width/height if framebuffer has been enlarged. In PSP pixels.102u16 drawnWidth;103u16 drawnHeight;104105// The dimensions at which we are confident that we can read back this buffer without stomping on irrelevant memory.106u16 safeWidth;107u16 safeHeight;108109// The scale factor at which we are rendering (to achieve higher resolution).110u8 renderScaleFactor;111112u16 usageFlags;113114// These are used to track state to try to avoid buffer size shifting back and forth.115// You might think that doesn't happen since we mostly grow framebuffers, but we do resize down,116// if the size has shrunk for a while and the framebuffer is also larger than the stride.117// At this point, the "safe" size is probably a lie, and we have had various issues with readbacks, so this resizes down to avoid them.118// An example would be a game that always uses the address 0x00154000 for temp buffers, and uses it for a full-screen effect for 3 frames, then goes back to using it for character shadows or something much smaller.119u16 newWidth;120u16 newHeight;121122// The frame number at which this was last resized.123int lastFrameNewSize;124125// Tracking for downloads-to-CLUT.126u16 clutUpdatedBytes;127128// Means that the whole image has already been read back to memory - used when combining small readbacks (gameUsesSequentialCopies_).129bool memoryUpdated;130131// TODO: Fold into usageFlags?132bool dirtyAfterDisplay;133bool reallyDirtyAfterDisplay; // takes frame skipping into account134135// Global sequence numbers for the last time these were bound.136// Not based on frames at all. Can be used to determine new-ness of one framebuffer over another,137// can even be within a frame.138int colorBindSeq;139int depthBindSeq;140141// These are mainly used for garbage collection purposes and similar.142// Cannot be used to determine new-ness against a similar other buffer, since they are143// only at frame granularity.144int last_frame_used;145int last_frame_attached;146int last_frame_render;147int last_frame_displayed;148int last_frame_clut;149int last_frame_failed;150int last_frame_depth_updated;151int last_frame_depth_render;152153// Convenience methods154inline int WidthInBytes() const { return width * BufferFormatBytesPerPixel(fb_format); }155inline int BufferWidthInBytes() const { return bufferWidth * BufferFormatBytesPerPixel(fb_format); }156inline int FbStrideInBytes() const { return fb_stride * BufferFormatBytesPerPixel(fb_format); }157inline int ZStrideInBytes() const { return z_stride * 2; }158159inline int Stride(RasterChannel channel) const { return channel == RASTER_COLOR ? fb_stride : z_stride; }160inline u32 Address(RasterChannel channel) const { return channel == RASTER_COLOR ? fb_address : z_address; }161inline GEBufferFormat Format(RasterChannel channel) const { return channel == RASTER_COLOR ? fb_format : GE_FORMAT_DEPTH16; }162inline int BindSeq(RasterChannel channel) const { return channel == RASTER_COLOR ? colorBindSeq : depthBindSeq; }163164// Computed from stride.165int BufferByteSize(RasterChannel channel) const { return BufferByteStride(channel) * height; }166int BufferByteStride(RasterChannel channel) const {167return channel == RASTER_COLOR ? fb_stride * (fb_format == GE_FORMAT_8888 ? 4 : 2) : z_stride * 2;168}169int BufferByteWidth(RasterChannel channel) const {170return channel == RASTER_COLOR ? width * (fb_format == GE_FORMAT_8888 ? 4 : 2) : width * 2;171}172};173174struct FramebufferHeuristicParams {175u32 fb_address;176u32 z_address;177u16 fb_stride;178u16 z_stride;179GEBufferFormat fb_format;180bool isClearingDepth;181bool isWritingDepth;182bool isDrawing;183bool isModeThrough;184bool isBlending;185int viewportWidth;186int viewportHeight;187int16_t regionWidth;188int16_t regionHeight;189int16_t scissorLeft;190int16_t scissorTop;191int16_t scissorRight;192int16_t scissorBottom;193};194195struct GPUgstate;196extern GPUgstate gstate;197198void GetFramebufferHeuristicInputs(FramebufferHeuristicParams *params, const GPUgstate &gstate);199200enum BindFramebufferColorFlags {201BINDFBCOLOR_SKIP_COPY = 0,202BINDFBCOLOR_MAY_COPY = 1,203BINDFBCOLOR_MAY_COPY_WITH_UV = 3, // includes BINDFBCOLOR_MAY_COPY204BINDFBCOLOR_APPLY_TEX_OFFSET = 4,205// Used when rendering to a temporary surface (e.g. not the current render target.)206BINDFBCOLOR_FORCE_SELF = 8,207BINDFBCOLOR_UNCACHED = 16,208};209210enum DrawTextureFlags {211DRAWTEX_NEAREST = 0,212DRAWTEX_LINEAR = 1,213DRAWTEX_TO_BACKBUFFER = 8,214DRAWTEX_DEPTH = 16,215};216217inline DrawTextureFlags operator | (const DrawTextureFlags &lhs, const DrawTextureFlags &rhs) {218return DrawTextureFlags((u32)lhs | (u32)rhs);219}220221enum class TempFBO {222DEPAL,223BLIT,224// For copies of framebuffers (e.g. shader blending.)225COPY,226// Used for copies when setting color to depth.227Z_COPY,228// Used to copy stencil data, means we need a stencil backing.229STENCIL,230};231232inline Draw::DataFormat GEFormatToThin3D(GEBufferFormat geFormat) {233switch (geFormat) {234case GE_FORMAT_4444:235return Draw::DataFormat::A4R4G4B4_UNORM_PACK16;236case GE_FORMAT_5551:237return Draw::DataFormat::A1R5G5B5_UNORM_PACK16;238case GE_FORMAT_565:239return Draw::DataFormat::R5G6B5_UNORM_PACK16;240case GE_FORMAT_8888:241return Draw::DataFormat::R8G8B8A8_UNORM;242case GE_FORMAT_DEPTH16:243return Draw::DataFormat::D16;244default:245// TODO: Assert?246return Draw::DataFormat::UNDEFINED;247}248}249250// Dimensions are in bytes, later steps get to convert back into real coordinates as appropriate.251// Makes it easy to see if blits match etc.252struct BlockTransferRect {253VirtualFramebuffer *vfb;254RasterChannel channel; // We usually only deal with color, but we have limited depth block transfer support now.255256int x_bytes;257int y;258int w_bytes;259int h;260261std::string ToString() const;262263int w_pixels() const {264return w_bytes / BufferFormatBytesPerPixel(vfb->fb_format);265}266int x_pixels() const {267return x_bytes / BufferFormatBytesPerPixel(vfb->fb_format);268}269};270271namespace Draw {272class DrawContext;273}274275struct DrawPixelsEntry {276Draw::Texture *tex;277uint64_t contentsHash;278int frameNumber;279};280281struct GPUDebugBuffer;282class DrawEngineCommon;283class PresentationCommon;284class ShaderManagerCommon;285class TextureCacheCommon;286287class FramebufferManagerCommon {288public:289FramebufferManagerCommon(Draw::DrawContext *draw);290virtual ~FramebufferManagerCommon();291292void SetTextureCache(TextureCacheCommon *tc) {293textureCache_ = tc;294}295void SetShaderManager(ShaderManagerCommon * sm) {296shaderManager_ = sm;297}298void SetDrawEngine(DrawEngineCommon *td) {299drawEngine_ = td;300}301302void Init(int msaaLevel);303virtual void BeginFrame();304void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format);305void DestroyFramebuf(VirtualFramebuffer *v);306307VirtualFramebuffer *DoSetRenderFrameBuffer(FramebufferHeuristicParams ¶ms, u32 skipDrawReason);308VirtualFramebuffer *SetRenderFrameBuffer(bool framebufChanged, int skipDrawReason) {309// Inlining this part since it's so frequent.310if (!framebufChanged && currentRenderVfb_) {311currentRenderVfb_->last_frame_render = gpuStats.numFlips;312currentRenderVfb_->dirtyAfterDisplay = true;313if (!skipDrawReason)314currentRenderVfb_->reallyDirtyAfterDisplay = true;315return currentRenderVfb_;316} else {317// This is so that we will be able to drive DoSetRenderFramebuffer with inputs318// that come from elsewhere than gstate.319FramebufferHeuristicParams inputs;320GetFramebufferHeuristicInputs(&inputs, gstate);321VirtualFramebuffer *vfb = DoSetRenderFrameBuffer(inputs, skipDrawReason);322_dbg_assert_msg_(vfb, "DoSetRenderFramebuffer must return a valid framebuffer.");323_dbg_assert_msg_(currentRenderVfb_, "DoSetRenderFramebuffer must set a valid framebuffer.");324return vfb;325}326}327void SetDepthFrameBuffer(bool isClearingDepth);328329void RebindFramebuffer(const char *tag);330std::vector<const VirtualFramebuffer *> GetFramebufferList() const;331332void CopyDisplayToOutput(bool reallyDirty);333334bool NotifyFramebufferCopy(u32 src, u32 dest, int size, GPUCopyFlag flags, u32 skipDrawReason);335void PerformWriteFormattedFromMemory(u32 addr, int size, int width, GEBufferFormat fmt);336void UpdateFromMemory(u32 addr, int size);337void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor);338bool PerformWriteStencilFromMemory(u32 addr, int size, WriteStencil flags);339340// We changed our depth mode, gotta start over.341// Ideally, we should convert depth buffers here, not just clear them.342void ClearAllDepthBuffers();343344// Returns true if it's sure this is a direct FBO->FBO transfer and it has already handle it.345// In that case we hardly need to actually copy the bytes in VRAM, they will be wrong anyway (unless346// read framebuffers is on, in which case this should always return false).347// If this returns false, a memory copy will happen and NotifyBlockTransferAfter will be called.348bool NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);349350// This gets called after the memory copy, in case NotifyBlockTransferBefore returned false.351// Otherwise it doesn't get called.352void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason);353354bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags, int layer);355void ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode);356357void DownloadFramebufferForClut(u32 fb_address, u32 loadBytes);358void DrawFramebufferToOutput(const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat);359360void DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, RasterChannel channel, const char *tag);361362size_t NumVFBs() const { return vfbs_.size(); }363364u32 PrevDisplayFramebufAddr() const {365return prevDisplayFramebuf_ ? prevDisplayFramebuf_->fb_address : 0;366}367u32 CurrentDisplayFramebufAddr() const {368return displayFramebuf_ ? displayFramebuf_->fb_address : 0;369}370371u32 DisplayFramebufAddr() const {372return displayFramebufPtr_;373}374u32 DisplayFramebufStride() const {375return displayStride_;376}377GEBufferFormat DisplayFramebufFormat() const {378return displayFormat_;379}380381bool UseBufferedRendering() const {382return useBufferedRendering_;383}384385// TODO: Maybe just include the last depth buffer address in this, too.386bool MayIntersectFramebufferColor(u32 start) const {387// Clear the cache/kernel bits.388start &= 0x3FFFFFFF;389if (Memory::IsVRAMAddress(start))390start &= 0x041FFFFF;391// Most games only have two framebuffers at the start.392if (start >= framebufColorRangeEnd_ || start < PSP_GetVidMemBase()) {393return false;394}395return true;396}397398VirtualFramebuffer *GetCurrentRenderVFB() const {399return currentRenderVfb_;400}401402// This only checks for the color channel, and if there are multiple overlapping ones403// with different color depth, this might get things wrong.404// DEPRECATED FOR NEW USES - avoid whenever possible.405VirtualFramebuffer *GetVFBAt(u32 addr) const;406407// This will only return exact matches of addr+stride+format.408VirtualFramebuffer *GetExactVFB(u32 addr, int stride, GEBufferFormat format) const;409410// If this doesn't find the exact VFB, but one with a different color format with matching stride,411// it'll resolve the newest one at address to the format requested, and return that.412VirtualFramebuffer *ResolveVFB(u32 addr, int stride, GEBufferFormat format);413414// Utility to get the display VFB.415VirtualFramebuffer *GetDisplayVFB();416417int GetRenderWidth() const { return currentRenderVfb_ ? currentRenderVfb_->renderWidth : 480; }418int GetRenderHeight() const { return currentRenderVfb_ ? currentRenderVfb_->renderHeight : 272; }419int GetTargetWidth() const { return currentRenderVfb_ ? currentRenderVfb_->width : 480; }420int GetTargetHeight() const { return currentRenderVfb_ ? currentRenderVfb_->height : 272; }421int GetTargetBufferWidth() const { return currentRenderVfb_ ? currentRenderVfb_->bufferWidth : 480; }422int GetTargetBufferHeight() const { return currentRenderVfb_ ? currentRenderVfb_->bufferHeight : 272; }423int GetTargetStride() const { return currentRenderVfb_ ? currentRenderVfb_->fb_stride : 512; }424GEBufferFormat GetTargetFormat() const { return currentRenderVfb_ ? currentRenderVfb_->fb_format : displayFormat_; }425426void SetColorUpdated(int skipDrawReason) {427if (currentRenderVfb_) {428SetColorUpdated(currentRenderVfb_, skipDrawReason);429}430}431void SetSafeSize(u16 w, u16 h);432433void NotifyRenderResized(int msaaLevel);434virtual void NotifyDisplayResized();435void NotifyConfigChanged();436437void CheckPostShaders();438439virtual void DestroyAllFBOs();440441virtual void DeviceLost();442virtual void DeviceRestore(Draw::DrawContext *draw);443444Draw::Framebuffer *GetTempFBO(TempFBO reason, u16 w, u16 h);445446// Debug features447virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes);448virtual bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer);449virtual bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer);450virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer);451452const std::vector<VirtualFramebuffer *> &Framebuffers() const {453return vfbs_;454}455456Draw2D *GetDraw2D() {457return &draw2D_;458}459460// If a vfb with the target format exists, resolve it (run CopyToColorFromOverlappingFramebuffers).461// If it doesn't exist, create it and do the same.462// Returns the resolved framebuffer.463VirtualFramebuffer *ResolveFramebufferColorToFormat(VirtualFramebuffer *vfb, GEBufferFormat newFormat);464465Draw2DPipeline *Get2DPipeline(Draw2DShader shader);466467// If from==to, returns a copy pipeline.468Draw2DPipeline *GetReinterpretPipeline(GEBufferFormat from, GEBufferFormat to, float *scaleFactorX);469470// Public to be used from the texture cache's depal shenanigans.471void BlitUsingRaster(472Draw::Framebuffer *src, float srcX1, float srcY1, float srcX2, float srcY2,473Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2,474bool linearFilter,475int scaleFactor, // usually unused, except for swizzle...476Draw2DPipeline *pipeline, const char *tag);477478void ReleasePipelines();479480int GetMSAALevel() const {481return msaaLevel_;482}483484void DiscardFramebufferCopy() {485currentFramebufferCopy_ = nullptr;486}487488bool PresentedThisFrame() const;489490protected:491virtual void ReadbackFramebuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode);492// Used for when a shader is required, such as GLES.493virtual bool ReadbackDepthbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride, int destW, int destH, Draw::ReadbackMode mode);494virtual bool ReadbackStencilbuffer(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride, Draw::ReadbackMode mode);495void SetViewport2D(int x, int y, int w, int h);496Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);497void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags);498499void CopyToColorFromOverlappingFramebuffers(VirtualFramebuffer *dest);500void CopyToDepthFromOverlappingFramebuffers(VirtualFramebuffer *dest);501502bool UpdateRenderSize(int msaaLevel);503504void FlushBeforeCopy();505virtual void DecimateFBOs(); // keeping it virtual to let D3D do a little extra506507// Used by ReadFramebufferToMemory and later framebuffer block copies508void BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp, RasterChannel channel, const char *tag);509510void CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags, int layer, bool *partial);511512void EstimateDrawingSize(u32 fb_address, int fb_stride, GEBufferFormat fb_format, int viewport_width, int viewport_height, int region_width, int region_height, int scissor_width, int scissor_height, int &drawing_width, int &drawing_height);513514void NotifyRenderFramebufferCreated(VirtualFramebuffer *vfb);515static void NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb);516void NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth);517518void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst, bool allowSizeMismatch = false);519520void ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, int h, bool force = false, bool skipCopy = false);521522static bool ShouldDownloadFramebufferColor(const VirtualFramebuffer *vfb);523static bool ShouldDownloadFramebufferDepth(const VirtualFramebuffer *vfb);524void DownloadFramebufferOnSwitch(VirtualFramebuffer *vfb);525526bool FindTransferFramebuffer(u32 basePtr, int stride, int x, int y, int w, int h, int bpp, bool destination, BlockTransferRect *rect);527528VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb, RasterChannel channel);529virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {}530531VirtualFramebuffer *CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format);532533void UpdateFramebufUsage(VirtualFramebuffer *vfb) const;534535int GetFramebufferLayers() const;536537static void SetColorUpdated(VirtualFramebuffer *dstBuffer, int skipDrawReason) {538dstBuffer->memoryUpdated = false;539dstBuffer->clutUpdatedBytes = 0;540dstBuffer->dirtyAfterDisplay = true;541dstBuffer->drawnWidth = dstBuffer->width;542dstBuffer->drawnHeight = dstBuffer->height;543if ((skipDrawReason & SKIPDRAW_SKIPFRAME) == 0)544dstBuffer->reallyDirtyAfterDisplay = true;545}546547inline int GetBindSeqCount() {548return fbBindSeqCount_++;549}550551static SkipGPUReadbackMode GetSkipGPUReadbackMode();552553PresentationCommon *presentation_ = nullptr;554555Draw::DrawContext *draw_ = nullptr;556557TextureCacheCommon *textureCache_ = nullptr;558ShaderManagerCommon *shaderManager_ = nullptr;559DrawEngineCommon *drawEngine_ = nullptr;560561bool needBackBufferYSwap_ = false;562563u32 displayFramebufPtr_ = 0;564u32 displayStride_ = 0;565GEBufferFormat displayFormat_ = GE_FORMAT_565;566u32 prevDisplayFramebufPtr_ = 0;567568int fbBindSeqCount_ = 0;569570VirtualFramebuffer *displayFramebuf_ = nullptr;571VirtualFramebuffer *prevDisplayFramebuf_ = nullptr;572VirtualFramebuffer *prevPrevDisplayFramebuf_ = nullptr;573int frameLastFramebufUsed_ = 0;574575VirtualFramebuffer *currentRenderVfb_ = nullptr;576577Draw::Framebuffer *currentFramebufferCopy_ = nullptr;578579// The range of PSP memory that may contain FBOs. So we can skip iterating.580u32 framebufColorRangeEnd_ = 0;581582bool useBufferedRendering_ = false;583bool postShaderIsUpscalingFilter_ = false;584bool postShaderIsSupersampling_ = false;585586std::vector<VirtualFramebuffer *> vfbs_;587std::vector<VirtualFramebuffer *> bvfbs_; // blitting framebuffers (for download)588589std::vector<DrawPixelsEntry> drawPixelsCache_;590591bool gameUsesSequentialCopies_ = false;592593// Sampled in BeginFrame/UpdateSize for safety.594float renderWidth_ = 0.0f;595float renderHeight_ = 0.0f;596597int msaaLevel_ = 0;598int renderScaleFactor_ = 1;599int pixelWidth_ = 0;600int pixelHeight_ = 0;601int bloomHack_ = 0;602bool updatePostShaders_ = false;603604Draw::DataFormat preferredPixelsFormat_ = Draw::DataFormat::R8G8B8A8_UNORM;605606struct TempFBOInfo {607Draw::Framebuffer *fbo;608int last_frame_used;609};610611std::unordered_map<u64, TempFBOInfo> tempFBOs_;612613std::vector<Draw::Framebuffer *> fbosToDelete_;614615// Aggressively delete unused FBOs to save gpu memory.616enum {617FBO_OLD_AGE = 5,618FBO_OLD_USAGE_FLAG = 15,619};620621// Thin3D stuff for reinterpreting image data between the various 16-bit color formats.622// Safe, not optimal - there might be input attachment tricks, etc, but we can't use them623// since we don't want N different implementations.624Draw2DPipeline *reinterpretFromTo_[4][4]{};625626// Common implementation of stencil buffer upload. Also not 100% optimal, but not performance627// critical either.628Draw::Pipeline *stencilWritePipeline_ = nullptr;629Draw::SamplerState *stencilWriteSampler_ = nullptr;630631// Used on GLES where we can't directly readback depth or stencil, but here for simplicity.632Draw::Pipeline *stencilReadbackPipeline_ = nullptr;633Draw::SamplerState *stencilReadbackSampler_ = nullptr;634Draw::Pipeline *depthReadbackPipeline_ = nullptr;635Draw::SamplerState *depthReadbackSampler_ = nullptr;636637// Draw2D pipelines638Draw2DPipeline *draw2DPipelineCopyColor_ = nullptr;639Draw2DPipeline *draw2DPipelineColorRect2Lin_ = nullptr;640Draw2DPipeline *draw2DPipelineCopyDepth_ = nullptr;641Draw2DPipeline *draw2DPipelineEncodeDepth_ = nullptr;642Draw2DPipeline *draw2DPipeline565ToDepth_ = nullptr;643Draw2DPipeline *draw2DPipeline565ToDepthDeswizzle_ = nullptr;644645Draw2D draw2D_;646// The fragment shaders are "owned" by the pipelines since they're 1:1.647648// Depth readback helper state649u8 *convBuf_ = nullptr;650u32 convBufSize_ = 0;651};652653// Should probably live elsewhere.654bool GetOutputFramebuffer(Draw::DrawContext *draw, GPUDebugBuffer &buffer);655656657