mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
softgpu: Track dirty vs really dirty per buffer.
When games draw and display with a frame lag, it becomes important that we indicate really dirty for the correct buffer. Since some triple buffer, this attempts to track at the buffer level using 1024 byte granularity.
This commit is contained in:
parent
695efe3729
commit
7cef06c191
2 changed files with 96 additions and 16 deletions
|
@ -423,7 +423,7 @@ SoftGPU::SoftGPU(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
|||
}
|
||||
}
|
||||
|
||||
framebufferDirty_ = true;
|
||||
memset(vramDirty_, (uint8_t)(SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY), sizeof(vramDirty_));
|
||||
// TODO: Is there a default?
|
||||
displayFramebuf_ = 0;
|
||||
displayStride_ = 512;
|
||||
|
@ -642,7 +642,61 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) {
|
|||
void SoftGPU::CopyDisplayToOutput(bool reallyDirty) {
|
||||
// The display always shows 480x272.
|
||||
CopyToCurrentFboFromDisplayRam(FB_WIDTH, FB_HEIGHT);
|
||||
framebufferDirty_ = false;
|
||||
MarkDirty(displayFramebuf_, displayStride_, 272, displayFormat_, SoftGPUVRAMDirty::CLEAR);
|
||||
}
|
||||
|
||||
void SoftGPU::MarkDirty(uint32_t addr, uint32_t stride, uint32_t height, GEBufferFormat fmt, SoftGPUVRAMDirty value) {
|
||||
uint32_t bytes = height * stride * (fmt == GE_FORMAT_8888 ? 4 : 2);
|
||||
MarkDirty(addr, bytes, value);
|
||||
}
|
||||
|
||||
void SoftGPU::MarkDirty(uint32_t addr, uint32_t bytes, SoftGPUVRAMDirty value) {
|
||||
// Don't bother tracking if frameskipping.
|
||||
if (g_Config.iFrameSkip == 0)
|
||||
return;
|
||||
if (!Memory::IsVRAMAddress(addr) || !Memory::IsVRAMAddress(addr + bytes - 1))
|
||||
return;
|
||||
if (lastDirtyAddr_ == addr && lastDirtySize_ == bytes && lastDirtyValue_ == value)
|
||||
return;
|
||||
|
||||
uint32_t start = ((addr - PSP_GetVidMemBase()) & 0x001FFFFF) >> 10;
|
||||
uint32_t end = start + ((bytes + 1023) >> 10);
|
||||
if (value == SoftGPUVRAMDirty::CLEAR || value == (SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY)) {
|
||||
memset(vramDirty_ + start, (uint8_t)value, end - start);
|
||||
} else {
|
||||
for (uint32_t i = start; i < end; ++i) {
|
||||
vramDirty_[i] |= (uint8_t)value;
|
||||
}
|
||||
}
|
||||
|
||||
lastDirtyAddr_ = addr;
|
||||
lastDirtySize_ = bytes;
|
||||
lastDirtyValue_ = value;
|
||||
}
|
||||
|
||||
bool SoftGPU::ClearDirty(uint32_t addr, uint32_t stride, uint32_t height, GEBufferFormat fmt, SoftGPUVRAMDirty value) {
|
||||
uint32_t bytes = height * stride * (fmt == GE_FORMAT_8888 ? 4 : 2);
|
||||
return ClearDirty(addr, bytes, value);
|
||||
}
|
||||
|
||||
bool SoftGPU::ClearDirty(uint32_t addr, uint32_t bytes, SoftGPUVRAMDirty value) {
|
||||
if (!Memory::IsVRAMAddress(addr) || !Memory::IsVRAMAddress(addr + bytes - 1))
|
||||
return false;
|
||||
|
||||
uint32_t start = ((addr - PSP_GetVidMemBase()) & 0x001FFFFF) >> 10;
|
||||
uint32_t end = start + ((bytes + 1023) >> 10);
|
||||
bool result = false;
|
||||
for (uint32_t i = start; i < end; ++i) {
|
||||
if (vramDirty_[i] & (uint8_t)value) {
|
||||
result = true;
|
||||
vramDirty_[i] &= ~(uint8_t)value;
|
||||
}
|
||||
}
|
||||
|
||||
lastDirtyAddr_ = 0;
|
||||
lastDirtySize_ = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SoftGPU::Resized() {
|
||||
|
@ -755,7 +809,7 @@ void SoftGPU::Execute_BlockTransferStart(u32 op, u32 diff) {
|
|||
cyclesExecuted += ((height * width * bpp) * 16) / 10;
|
||||
|
||||
// Could theoretically dirty the framebuffer.
|
||||
framebufferDirty_ = true;
|
||||
MarkDirty(dst, dstSize, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY);
|
||||
}
|
||||
|
||||
void SoftGPU::Execute_Prim(u32 op, u32 diff) {
|
||||
|
@ -786,7 +840,9 @@ void SoftGPU::Execute_Prim(u32 op, u32 diff) {
|
|||
drawEngine_->transformUnit.SetDirty(dirtyFlags_);
|
||||
drawEngine_->transformUnit.SubmitPrimitive(verts, indices, prim, count, gstate.vertType, &bytesRead, drawEngine_);
|
||||
dirtyFlags_ = drawEngine_->transformUnit.GetDirty();
|
||||
framebufferDirty_ = true;
|
||||
|
||||
SoftGPUVRAMDirty mark = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0 ? SoftGPUVRAMDirty::DIRTY : SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY;
|
||||
MarkDirty(gstate.getFrameBufAddress(), gstate.FrameBufStride(), gstate.getRegionY2() + 1, gstate.FrameBufFormat(), mark);
|
||||
|
||||
// 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.
|
||||
|
@ -837,7 +893,9 @@ void SoftGPU::Execute_Bezier(u32 op, u32 diff) {
|
|||
drawEngine_->transformUnit.SetDirty(dirtyFlags_);
|
||||
drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "bezier");
|
||||
dirtyFlags_ = drawEngine_->transformUnit.GetDirty();
|
||||
framebufferDirty_ = true;
|
||||
|
||||
SoftGPUVRAMDirty mark = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0 ? SoftGPUVRAMDirty::DIRTY : SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY;
|
||||
MarkDirty(gstate.getFrameBufAddress(), gstate.FrameBufStride(), gstate.getRegionY2() + 1, gstate.FrameBufFormat(), mark);
|
||||
|
||||
// After drawing, we advance pointers - see SubmitPrim which does the same.
|
||||
int count = surface.num_points_u * surface.num_points_v;
|
||||
|
@ -889,7 +947,9 @@ void SoftGPU::Execute_Spline(u32 op, u32 diff) {
|
|||
drawEngine_->transformUnit.SetDirty(dirtyFlags_);
|
||||
drawEngineCommon_->SubmitCurve(control_points, indices, surface, gstate.vertType, &bytesRead, "spline");
|
||||
dirtyFlags_ = drawEngine_->transformUnit.GetDirty();
|
||||
framebufferDirty_ = true;
|
||||
|
||||
SoftGPUVRAMDirty mark = (gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME) != 0 ? SoftGPUVRAMDirty::DIRTY : SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY;
|
||||
MarkDirty(gstate.getFrameBufAddress(), gstate.FrameBufStride(), gstate.getRegionY2() + 1, gstate.FrameBufFormat(), mark);
|
||||
|
||||
// After drawing, we advance pointers - see SubmitPrim which does the same.
|
||||
int count = surface.num_points_u * surface.num_points_v;
|
||||
|
@ -1059,7 +1119,7 @@ bool SoftGPU::PerformMemoryCopy(u32 dest, u32 src, int size)
|
|||
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
|
||||
GPURecord::NotifyMemcpy(dest, src, size);
|
||||
// Let's just be safe.
|
||||
framebufferDirty_ = true;
|
||||
MarkDirty(dest, size, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1069,7 +1129,7 @@ bool SoftGPU::PerformMemorySet(u32 dest, u8 v, int size)
|
|||
InvalidateCache(dest, size, GPU_INVALIDATE_HINT);
|
||||
GPURecord::NotifyMemset(dest, v, size);
|
||||
// Let's just be safe.
|
||||
framebufferDirty_ = true;
|
||||
MarkDirty(dest, size, SoftGPUVRAMDirty::DIRTY | SoftGPUVRAMDirty::REALLY_DIRTY);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1095,9 +1155,14 @@ bool SoftGPU::PerformStencilUpload(u32 dest, int size)
|
|||
|
||||
bool SoftGPU::FramebufferDirty() {
|
||||
if (g_Config.iFrameSkip != 0) {
|
||||
bool dirty = framebufferDirty_;
|
||||
framebufferDirty_ = false;
|
||||
return dirty;
|
||||
return ClearDirty(displayFramebuf_, displayStride_, 272, displayFormat_, SoftGPUVRAMDirty::DIRTY);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoftGPU::FramebufferReallyDirty() {
|
||||
if (g_Config.iFrameSkip != 0) {
|
||||
return ClearDirty(displayFramebuf_, displayStride_, 272, displayFormat_, SoftGPUVRAMDirty::REALLY_DIRTY);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "GPU/GPUCommon.h"
|
||||
#include "GPU/Common/GPUDebugInterface.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
|
@ -113,6 +114,14 @@ static inline SoftDirty operator ~(const SoftDirty &v) {
|
|||
class PresentationCommon;
|
||||
class SoftwareDrawEngine;
|
||||
|
||||
enum class SoftGPUVRAMDirty : uint8_t {
|
||||
CLEAR = 0,
|
||||
DIRTY = 1,
|
||||
REALLY_DIRTY = 2,
|
||||
};
|
||||
|
||||
ENUM_CLASS_BITOPS(SoftGPUVRAMDirty);
|
||||
|
||||
class SoftGPU : public GPUCommon {
|
||||
public:
|
||||
SoftGPU(GraphicsContext *gfxCtx, Draw::DrawContext *draw);
|
||||
|
@ -145,10 +154,7 @@ public:
|
|||
}
|
||||
|
||||
bool FramebufferDirty() override;
|
||||
|
||||
bool FramebufferReallyDirty() override {
|
||||
return !(gstate_c.skipDrawReason & SKIPDRAW_SKIPFRAME);
|
||||
}
|
||||
bool FramebufferReallyDirty() override;
|
||||
|
||||
bool GetCurrentFramebuffer(GPUDebugBuffer &buffer, GPUDebugFramebufferType type, int maxRes = -1) override;
|
||||
bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override;
|
||||
|
@ -187,7 +193,16 @@ protected:
|
|||
void ConvertTextureDescFrom16(Draw::TextureDesc &desc, int srcwidth, int srcheight, u8 *overrideData = nullptr);
|
||||
|
||||
private:
|
||||
bool framebufferDirty_;
|
||||
void MarkDirty(uint32_t addr, uint32_t stride, uint32_t height, GEBufferFormat fmt, SoftGPUVRAMDirty value);
|
||||
void MarkDirty(uint32_t addr, uint32_t bytes, SoftGPUVRAMDirty value);
|
||||
bool ClearDirty(uint32_t addr, uint32_t stride, uint32_t height, GEBufferFormat fmt, SoftGPUVRAMDirty value);
|
||||
bool ClearDirty(uint32_t addr, uint32_t bytes, SoftGPUVRAMDirty value);
|
||||
|
||||
uint8_t vramDirty_[2048];
|
||||
uint32_t lastDirtyAddr_ = 0;
|
||||
uint32_t lastDirtySize_ = 0;
|
||||
SoftGPUVRAMDirty lastDirtyValue_ = SoftGPUVRAMDirty::CLEAR;
|
||||
|
||||
u32 displayFramebuf_;
|
||||
u32 displayStride_;
|
||||
GEBufferFormat displayFormat_;
|
||||
|
|
Loading…
Add table
Reference in a new issue