mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-25 03:09:13 +01:00
Merge pull request #21218 from hrydgard/renderpass-refactor
Preparations for a rendering refactor
This commit is contained in:
17 files changed
+163
-137
No files matched your search
+39
-67
@@ -20,7 +20,7 @@ void VFS::Register(std::string_view prefix, VFSBackend *reader) {
|
||||
|
||||
void VFS::Clear() {
|
||||
for (auto &entry : entries_) {
|
||||
delete entry.reader;
|
||||
delete entry.backend;
|
||||
}
|
||||
entries_.clear();
|
||||
}
|
||||
@@ -37,6 +37,21 @@ static bool IsLocalAbsolutePath(std::string_view path) {
|
||||
return isUnixLocal || isWindowsLocal || isContentURI;
|
||||
}
|
||||
|
||||
bool VFS::MapPath(std::string_view path, VFSBackend **backend, std::string_view *relativePath) {
|
||||
int fn_len = (int)path.length();
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)entry.prefix.length();
|
||||
if (prefix_len >= fn_len) continue;
|
||||
if (0 == memcmp(path.data(), entry.prefix.data(), prefix_len)) {
|
||||
*backend = entry.backend;
|
||||
*relativePath = path.substr(prefix_len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ERROR_LOG(Log::IO, "VFS: '%.*s' has an unknown filesystem prefix.", STR_VIEW(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
// The returned data should be free'd with delete[].
|
||||
uint8_t *VFS::ReadFile(std::string_view filename, size_t *size) {
|
||||
if (IsLocalAbsolutePath(filename)) {
|
||||
@@ -45,26 +60,13 @@ uint8_t *VFS::ReadFile(std::string_view filename, size_t *size) {
|
||||
return File::ReadLocalFile(Path(filename), size);
|
||||
}
|
||||
|
||||
const int fn_len = (int)filename.length();
|
||||
bool fileSystemFound = false;
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)entry.prefix.length();
|
||||
if (prefix_len >= fn_len) continue;
|
||||
if (0 == memcmp(filename.data(), entry.prefix.data(), prefix_len)) {
|
||||
fileSystemFound = true;
|
||||
// INFO_LOG(Log::IO, "Prefix match: %s (%s) -> %s", entries[i].prefix, filename, filename + prefix_len);
|
||||
uint8_t *data = entry.reader->ReadFile(filename.substr(prefix_len), size);
|
||||
if (data)
|
||||
return data;
|
||||
else
|
||||
continue;
|
||||
// Else try the other registered file systems.
|
||||
}
|
||||
VFSBackend *backend = nullptr;
|
||||
std::string_view relativePath;
|
||||
if (!MapPath(filename, &backend, &relativePath)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!fileSystemFound) {
|
||||
ERROR_LOG(Log::IO, "Missing filesystem for '%.*s'", STR_VIEW(filename));
|
||||
} // Otherwise, the file was just missing. No need to log.
|
||||
return nullptr;
|
||||
|
||||
return backend->ReadFile(relativePath, size);
|
||||
}
|
||||
|
||||
bool VFS::GetFileListing(std::string_view path, std::vector<File::FileInfo> *listing, const char *filter) {
|
||||
@@ -75,23 +77,13 @@ bool VFS::GetFileListing(std::string_view path, std::vector<File::FileInfo> *lis
|
||||
return true;
|
||||
}
|
||||
|
||||
int fn_len = (int)path.length();
|
||||
bool fileSystemFound = false;
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)entry.prefix.length();
|
||||
if (prefix_len >= fn_len) continue;
|
||||
if (0 == memcmp(path.data(), entry.prefix.data(), prefix_len)) {
|
||||
fileSystemFound = true;
|
||||
if (entry.reader->GetFileListing(path.substr(prefix_len), listing, filter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
VFSBackend *backend = nullptr;
|
||||
std::string_view relativePath;
|
||||
if (!MapPath(path, &backend, &relativePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fileSystemFound) {
|
||||
ERROR_LOG(Log::IO, "Missing filesystem for %.*s", STR_VIEW(path));
|
||||
} // Otherwise, the file was just missing. No need to log.
|
||||
return false;
|
||||
return backend->GetFileListing(relativePath, listing, filter);
|
||||
}
|
||||
|
||||
bool VFS::GetFileInfo(std::string_view path, File::FileInfo *info) {
|
||||
@@ -101,23 +93,13 @@ bool VFS::GetFileInfo(std::string_view path, File::FileInfo *info) {
|
||||
return File::GetFileInfo(Path(path), info);
|
||||
}
|
||||
|
||||
bool fileSystemFound = false;
|
||||
int fn_len = (int)path.length();
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)entry.prefix.length();
|
||||
if (prefix_len >= fn_len) continue;
|
||||
if (0 == memcmp(path.data(), entry.prefix.data(), prefix_len)) {
|
||||
fileSystemFound = true;
|
||||
if (entry.reader->GetFileInfo(path.substr(prefix_len), info))
|
||||
return true;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
VFSBackend *backend = nullptr;
|
||||
std::string_view relativePath;
|
||||
if (!MapPath(path, &backend, &relativePath)) {
|
||||
return false;
|
||||
}
|
||||
if (!fileSystemFound) {
|
||||
ERROR_LOG(Log::IO, "Missing filesystem for '%.*s'", STR_VIEW(path));
|
||||
} // Otherwise, the file was just missing. No need to log.
|
||||
return false;
|
||||
|
||||
return backend->GetFileInfo(relativePath, info);
|
||||
}
|
||||
|
||||
bool VFS::Exists(std::string_view path) {
|
||||
@@ -127,21 +109,11 @@ bool VFS::Exists(std::string_view path) {
|
||||
return File::Exists(Path(path));
|
||||
}
|
||||
|
||||
bool fileSystemFound = false;
|
||||
int fn_len = (int)path.length();
|
||||
for (const auto &entry : entries_) {
|
||||
int prefix_len = (int)entry.prefix.length();
|
||||
if (prefix_len >= fn_len) continue;
|
||||
if (0 == memcmp(path.data(), entry.prefix.data(), prefix_len)) {
|
||||
fileSystemFound = true;
|
||||
if (entry.reader->Exists(path.substr(prefix_len)))
|
||||
return true;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
VFSBackend *backend = nullptr;
|
||||
std::string_view relativePath;
|
||||
if (!MapPath(path, &backend, &relativePath)) {
|
||||
return false;
|
||||
}
|
||||
if (!fileSystemFound) {
|
||||
ERROR_LOG(Log::IO, "Missing filesystem for '%.*s'", STR_VIEW(path));
|
||||
} // Otherwise, the file was just missing. No need to log.
|
||||
return false;
|
||||
|
||||
return backend->Exists(relativePath);
|
||||
}
|
||||
@@ -81,9 +81,11 @@ public:
|
||||
bool Exists(std::string_view path);
|
||||
|
||||
private:
|
||||
bool MapPath(std::string_view filename, VFSBackend **backend, std::string_view *relativePath);
|
||||
|
||||
struct VFSEntry {
|
||||
std::string_view prefix;
|
||||
VFSBackend *reader;
|
||||
VFSBackend *backend;
|
||||
};
|
||||
std::vector<VFSEntry> entries_;
|
||||
};
|
||||
|
||||
@@ -661,7 +661,8 @@ void __DisplayFlip(int cyclesLate) {
|
||||
}
|
||||
}
|
||||
if (nextFrame) {
|
||||
gpu->CopyDisplayToOutput(g_displayLayoutConfigCached, fbReallyDirty);
|
||||
gpu->SetCurFramebufferDirty(fbReallyDirty);
|
||||
gpu->CopyDisplayToOutput(g_displayLayoutConfigCached);
|
||||
if (fbReallyDirty) {
|
||||
DisplayFireActualFlip();
|
||||
}
|
||||
|
||||
@@ -795,7 +795,7 @@ static int ptp_send_postoffice(int idx, const void *data, int *len) {
|
||||
}
|
||||
if (ptp_send_status == AEMU_POSTOFFICE_CLIENT_OUT_OF_MEMORY) {
|
||||
// this is pretty critical
|
||||
ERROR_LOG(Log::sceNet, "%s: critical: huge client buf %d what is going on please fix", __func__, len);
|
||||
ERROR_LOG(Log::sceNet, "%s: critical: huge client buf %d what is going on please fix", __func__, len ? *len : 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1536,15 +1536,15 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G
|
||||
return tex;
|
||||
}
|
||||
|
||||
// This is internal, called from CopyDisplayToOutput.
|
||||
bool FramebufferManagerCommon::DrawFramebufferToOutput(const DisplayLayoutConfig &config, const u8 *srcPixels, int srcStride, GEBufferFormat srcPixelFormat) {
|
||||
textureCache_->ForgetLastTexture();
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
float u0 = 0.0f, u1 = 480.0f / 512.0f;
|
||||
float v0 = 0.0f, v1 = 1.0f;
|
||||
Draw::Texture *pixelsTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272);
|
||||
if (!pixelsTex)
|
||||
if (!pixelsTex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int uvRotation = useBufferedRendering_ ? config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL;
|
||||
OutputFlags flags = config.iDisplayFilter == SCALE_LINEAR ? OutputFlags::LINEAR : OutputFlags::NEAREST;
|
||||
@@ -1556,16 +1556,18 @@ bool FramebufferManagerCommon::DrawFramebufferToOutput(const DisplayLayoutConfig
|
||||
flags |= OutputFlags::POSITION_FLIPPED;
|
||||
}
|
||||
|
||||
constexpr float u0 = 0.0f, u1 = 480.0f / 512.0f;
|
||||
constexpr float v0 = 0.0f, v1 = 1.0f;
|
||||
|
||||
presentation_->UpdateUniforms(textureCache_->VideoIsPlaying());
|
||||
presentation_->SourceTexture(pixelsTex, 512, 272);
|
||||
presentation_->CopyToOutput(config, flags, uvRotation, u0, v0, u1, v1);
|
||||
presentation_->RunPostshaderPasses(config, flags, uvRotation, u0, v0, u1, v1);
|
||||
|
||||
// PresentationCommon sets all kinds of state, we can't rely on anything.
|
||||
gstate_c.Dirty(DIRTY_ALL);
|
||||
|
||||
DiscardFramebufferCopy();
|
||||
currentRenderVfb_ = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1649,7 +1651,9 @@ void FramebufferManagerCommon::CopyDisplayToOutput(const DisplayLayoutConfig &co
|
||||
if (Memory::IsValidAddress(fbaddr)) {
|
||||
// The game is displaying something directly from RAM. In GTA, it's decoded video.
|
||||
// If successful, this effectively calls presentation_->NotifyPresent();
|
||||
if (!DrawFramebufferToOutput(config, Memory::GetPointerUnchecked(fbaddr), displayStride_, displayFormat_)) {
|
||||
if (DrawFramebufferToOutput(config, Memory::GetPointerUnchecked(fbaddr), displayStride_, displayFormat_)) {
|
||||
presentation_->CopyToOutput(config);
|
||||
} else {
|
||||
if (useBufferedRendering_) {
|
||||
// Bind and clear the backbuffer. This should be the first time during the frame that it's bound.
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR }, "CopyDisplayToOutput_DrawError");
|
||||
@@ -1727,7 +1731,8 @@ void FramebufferManagerCommon::CopyDisplayToOutput(const DisplayLayoutConfig &co
|
||||
int actualHeight = (vfb->bufferHeight * vfb->renderHeight) / vfb->height;
|
||||
presentation_->UpdateUniforms(textureCache_->VideoIsPlaying());
|
||||
presentation_->SourceFramebuffer(vfb->fbo, actualWidth, actualHeight);
|
||||
presentation_->CopyToOutput(config, flags, uvRotation, u0, v0, u1, v1);
|
||||
presentation_->RunPostshaderPasses(config, flags, uvRotation, u0, v0, u1, v1);
|
||||
presentation_->CopyToOutput(config);
|
||||
} else if (useBufferedRendering_) {
|
||||
WARN_LOG(Log::FrameBuf, "Using buffered rendering, and current VFB lacks an FBO: %08x", vfb->fb_address);
|
||||
} else {
|
||||
@@ -2799,7 +2804,9 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS
|
||||
if (isPrevDisplayBuffer || isDisplayBuffer) {
|
||||
FlushBeforeCopy();
|
||||
// HACK
|
||||
DrawFramebufferToOutput(displayLayoutConfigCopy_, Memory::GetPointerUnchecked(dstBasePtr), dstStride, displayFormat_);
|
||||
if (DrawFramebufferToOutput(displayLayoutConfigCopy_, Memory::GetPointerUnchecked(dstBasePtr), dstStride, displayFormat_)) {
|
||||
presentation_->CopyToOutput(displayLayoutConfigCopy_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,9 +651,12 @@ void PresentationCommon::UpdateUniforms(bool hasVideo) {
|
||||
hasVideo_ = hasVideo;
|
||||
}
|
||||
|
||||
void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1) {
|
||||
void PresentationCommon::RunPostshaderPasses(const DisplayLayoutConfig &config, OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1) {
|
||||
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
|
||||
postShaderOutput_ = nullptr;
|
||||
outputFlags_ = flags;
|
||||
|
||||
// TODO: If shader objects have been created by now, we might have received errors.
|
||||
// GLES can have the shader fail later, shader->failed / shader->error.
|
||||
// This should auto-disable usePostShader_ and call ShowPostShaderError().
|
||||
@@ -663,7 +666,6 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
|
||||
const bool usePostShader = usePostShader_ && !useStereo && !(flags & OutputFlags::RB_SWIZZLE);
|
||||
const bool isFinalAtOutputResolution = usePostShader && postShaderFramebuffers_.size() < postShaderPipelines_.size();
|
||||
Draw::Framebuffer *postShaderOutput = nullptr;
|
||||
int lastWidth = srcWidth_;
|
||||
int lastHeight = srcHeight_;
|
||||
|
||||
@@ -678,8 +680,7 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
frame.w /= 2.0;
|
||||
pixelWidth /= 2;
|
||||
}
|
||||
FRect rc;
|
||||
CalculateDisplayOutputRect(config, &rc, 480.0f, 272.0f, frame, uvRotation);
|
||||
CalculateDisplayOutputRect(config, &rc_, 480.0f, 272.0f, frame, uvRotation);
|
||||
|
||||
// To make buffer updates easier, we use one array of verts.
|
||||
int postVertsOffset = (int)sizeof(Vertex) * 4;
|
||||
@@ -699,10 +700,10 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
// 4-7: Post-processing, other passes
|
||||
// 8-11: Post-processing, first pass (needs to handle cropping the input image, if wrong dimensions)
|
||||
Vertex verts[12] = {
|
||||
{ rc.x, rc.y, 0, finalU0, finalV0, 0xFFFFFFFF }, // TL
|
||||
{ rc.x + rc.w, rc.y, 0, finalU1, finalV0, 0xFFFFFFFF }, // TR
|
||||
{ rc.x, rc.y + rc.h, 0, finalU0, finalV1, 0xFFFFFFFF }, // BL
|
||||
{ rc.x + rc.w, rc.y + rc.h, 0, finalU1, finalV1, 0xFFFFFFFF }, // BR
|
||||
{ rc_.x, rc_.y, 0, finalU0, finalV0, 0xFFFFFFFF }, // TL
|
||||
{ rc_.x + rc_.w, rc_.y, 0, finalU1, finalV0, 0xFFFFFFFF }, // TR
|
||||
{ rc_.x, rc_.y + rc_.h, 0, finalU0, finalV1, 0xFFFFFFFF }, // BL
|
||||
{ rc_.x + rc_.w, rc_.y + rc_.h, 0, finalU1, finalV1, 0xFFFFFFFF }, // BR
|
||||
};
|
||||
|
||||
// Rescale X, Y to normalized coordinate system.
|
||||
@@ -732,7 +733,7 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
rotation ^= 2;
|
||||
}
|
||||
|
||||
static int rotLookup[4] = { 0, 1, 3, 2 };
|
||||
static int rotLookup[4] = {0, 1, 3, 2};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int otherI = rotLookup[(rotLookup[i] + rotation) & 3];
|
||||
@@ -775,10 +776,9 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
// Grab the previous framebuffer early so we can change previousIndex_ when we want.
|
||||
Draw::Framebuffer *previousFramebuffer = previousFramebuffers_.empty() ? nullptr : previousFramebuffers_[previousIndex_];
|
||||
|
||||
PostShaderUniforms uniforms;
|
||||
const auto performShaderPass = [&](const ShaderInfo *shaderInfo, Draw::Framebuffer *postShaderFramebuffer, Draw::Pipeline *postShaderPipeline, int vertsOffset) {
|
||||
if (postShaderOutput) {
|
||||
draw_->BindFramebufferAsTexture(postShaderOutput, 0, Draw::Aspect::COLOR_BIT, 0);
|
||||
if (postShaderOutput_) {
|
||||
draw_->BindFramebufferAsTexture(postShaderOutput_, 0, Draw::Aspect::COLOR_BIT, 0);
|
||||
} else {
|
||||
BindSource(0, false);
|
||||
}
|
||||
@@ -788,10 +788,11 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
|
||||
int nextWidth, nextHeight;
|
||||
draw_->GetFramebufferDimensions(postShaderFramebuffer, &nextWidth, &nextHeight);
|
||||
Draw::Viewport viewport{ 0, 0, (float)nextWidth, (float)nextHeight, 0.0f, 1.0f };
|
||||
Draw::Viewport viewport{0, 0, (float)nextWidth, (float)nextHeight, 0.0f, 1.0f};
|
||||
draw_->SetViewport(viewport);
|
||||
draw_->SetScissorRect(0, 0, nextWidth, nextHeight);
|
||||
|
||||
PostShaderUniforms uniforms;
|
||||
CalculatePostShaderUniforms(lastWidth, lastHeight, nextWidth, nextHeight, shaderInfo, &uniforms);
|
||||
|
||||
draw_->BindPipeline(postShaderPipeline);
|
||||
@@ -806,7 +807,7 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
draw_->BindVertexBuffer(vdata_, vertsOffset);
|
||||
draw_->Draw(4, 0);
|
||||
|
||||
postShaderOutput = postShaderFramebuffer;
|
||||
postShaderOutput_ = postShaderFramebuffer;
|
||||
lastWidth = nextWidth;
|
||||
lastHeight = nextHeight;
|
||||
};
|
||||
@@ -818,16 +819,16 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
bool flipped = flags & OutputFlags::POSITION_FLIPPED;
|
||||
float y0 = flipped ? 1.0f : -1.0f;
|
||||
float y1 = flipped ? -1.0f : 1.0f;
|
||||
verts[4] = { -1.0f, y0, 0.0f, 0.0f, 0.0f, 0xFFFFFFFF }; // TL
|
||||
verts[5] = { 1.0f, y0, 0.0f, 1.0f, 0.0f, 0xFFFFFFFF }; // TR
|
||||
verts[6] = { -1.0f, y1, 0.0f, 0.0f, 1.0f, 0xFFFFFFFF }; // BL
|
||||
verts[7] = { 1.0f, y1, 0.0f, 1.0f, 1.0f, 0xFFFFFFFF }; // BR
|
||||
verts[4] = {-1.0f, y0, 0.0f, 0.0f, 0.0f, 0xFFFFFFFF}; // TL
|
||||
verts[5] = {1.0f, y0, 0.0f, 1.0f, 0.0f, 0xFFFFFFFF}; // TR
|
||||
verts[6] = {-1.0f, y1, 0.0f, 0.0f, 1.0f, 0xFFFFFFFF}; // BL
|
||||
verts[7] = {1.0f, y1, 0.0f, 1.0f, 1.0f, 0xFFFFFFFF}; // BR
|
||||
|
||||
// Now, adjust for the desired input rectangle.
|
||||
verts[8] = { -1.0f, y0, 0.0f, u0, v0, 0xFFFFFFFF }; // TL
|
||||
verts[9] = { 1.0f, y0, 0.0f, u1, v0, 0xFFFFFFFF }; // TR
|
||||
verts[10] = { -1.0f, y1, 0.0f, u0, v1, 0xFFFFFFFF }; // BL
|
||||
verts[11] = { 1.0f, y1, 0.0f, u1, v1, 0xFFFFFFFF }; // BR
|
||||
verts[8] = {-1.0f, y0, 0.0f, u0, v0, 0xFFFFFFFF}; // TL
|
||||
verts[9] = {1.0f, y0, 0.0f, u1, v0, 0xFFFFFFFF}; // TR
|
||||
verts[10] = {-1.0f, y1, 0.0f, u0, v1, 0xFFFFFFFF}; // BL
|
||||
verts[11] = {1.0f, y1, 0.0f, u1, v1, 0xFFFFFFFF}; // BR
|
||||
|
||||
draw_->UpdateBuffer(vdata_, (const uint8_t *)verts, 0, sizeof(verts), Draw::UPDATE_DISCARD);
|
||||
|
||||
@@ -844,7 +845,7 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
postShaderFramebuffer = previousFramebuffers_[previousIndex_];
|
||||
}
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "PostShader");
|
||||
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, {Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE}, "PostShader");
|
||||
|
||||
// Pick vertices 8-11 for the first pass.
|
||||
int vertOffset = i == 0 ? (int)sizeof(Vertex) * 8 : (int)sizeof(Vertex) * 4;
|
||||
@@ -869,14 +870,24 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
previousIndex_ = 0;
|
||||
Draw::Framebuffer *postShaderFramebuffer = previousFramebuffers_[previousIndex_];
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "InterFrameBlit");
|
||||
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, {Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE}, "InterFrameBlit");
|
||||
performShaderPass(shaderInfo, postShaderFramebuffer, postShaderPipeline, postVertsOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config) {
|
||||
bool useNearest = outputFlags_ & OutputFlags::NEAREST;
|
||||
bool useStereo = gstate_c.Use(GPU_USE_SIMPLE_STEREO_PERSPECTIVE) && stereoPipeline_ != nullptr; // TODO: Also check that the backend has support for it.
|
||||
|
||||
const bool usePostShader = usePostShader_ && !useStereo && !(outputFlags_ & OutputFlags::RB_SWIZZLE);
|
||||
const bool isFinalAtOutputResolution = usePostShader && postShaderFramebuffers_.size() < postShaderPipelines_.size();
|
||||
int lastWidth = srcWidth_;
|
||||
int lastHeight = srcHeight_;
|
||||
|
||||
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "FinalBlit");
|
||||
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
|
||||
|
||||
Draw::Pipeline *pipeline = (flags & OutputFlags::RB_SWIZZLE) ? texColorRBSwizzle_ : texColor_;
|
||||
Draw::Pipeline *pipeline = (outputFlags_ & OutputFlags::RB_SWIZZLE) ? texColorRBSwizzle_ : texColor_;
|
||||
|
||||
if (useStereo) {
|
||||
draw_->BindPipeline(stereoPipeline_);
|
||||
@@ -891,20 +902,23 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
}
|
||||
|
||||
draw_->BindPipeline(pipeline);
|
||||
if (postShaderOutput) {
|
||||
draw_->BindFramebufferAsTexture(postShaderOutput, 0, Draw::Aspect::COLOR_BIT, 0);
|
||||
if (postShaderOutput_) {
|
||||
draw_->BindFramebufferAsTexture(postShaderOutput_, 0, Draw::Aspect::COLOR_BIT, 0);
|
||||
} else {
|
||||
BindSource(0, false);
|
||||
}
|
||||
}
|
||||
BindSource(1, false);
|
||||
|
||||
PostShaderUniforms uniforms;
|
||||
if (isFinalAtOutputResolution && previousFramebuffers_.empty()) {
|
||||
CalculatePostShaderUniforms(lastWidth, lastHeight, (int)rc.w, (int)rc.h, &postShaderInfo_.back(), &uniforms);
|
||||
CalculatePostShaderUniforms(lastWidth, lastHeight, (int)rc_.w, (int)rc_.h, &postShaderInfo_.back(), &uniforms);
|
||||
draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms));
|
||||
previousUniforms_ = uniforms;
|
||||
} else if (useStereo) {
|
||||
CalculatePostShaderUniforms(lastWidth, lastHeight, (int)rc.w, (int)rc.h, stereoShaderInfo_, &uniforms);
|
||||
CalculatePostShaderUniforms(lastWidth, lastHeight, (int)rc_.w, (int)rc_.h, stereoShaderInfo_, &uniforms);
|
||||
draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms));
|
||||
previousUniforms_ = uniforms;
|
||||
} else {
|
||||
Draw::VsTexColUB ub{};
|
||||
memcpy(ub.WorldViewProj, g_display.rot_matrix.m, sizeof(float) * 16);
|
||||
@@ -945,7 +959,6 @@ void PresentationCommon::CopyToOutput(const DisplayLayoutConfig &config, OutputF
|
||||
// Unbinds all textures and samplers too, needed since sometimes a MakePixelTexture is deleted etc.
|
||||
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
|
||||
previousUniforms_ = uniforms;
|
||||
presentedThisFrame_ = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ struct ShaderInfo;
|
||||
class TextureCacheCommon;
|
||||
|
||||
enum class OutputFlags {
|
||||
DEFAULT = 0,
|
||||
LINEAR = 0x0000,
|
||||
NEAREST = 0x0001,
|
||||
RB_SWIZZLE = 0x0002,
|
||||
@@ -125,7 +126,9 @@ public:
|
||||
void UpdateUniforms(bool hasVideo);
|
||||
void SourceTexture(Draw::Texture *texture, int bufferWidth, int bufferHeight);
|
||||
void SourceFramebuffer(Draw::Framebuffer *fb, int bufferWidth, int bufferHeight);
|
||||
void CopyToOutput(const DisplayLayoutConfig &config, OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1);
|
||||
|
||||
void RunPostshaderPasses(const DisplayLayoutConfig &config, OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1);
|
||||
void CopyToOutput(const DisplayLayoutConfig &config);
|
||||
|
||||
void CalculateRenderResolution(const DisplayLayoutConfig &config, int *width, int *height, int *scaleFactor, bool *upscaling, bool *ssaa) const;
|
||||
|
||||
@@ -189,4 +192,9 @@ protected:
|
||||
int h;
|
||||
};
|
||||
std::vector<PrevFBO> postShaderFBOUsage_;
|
||||
|
||||
// Carry over info between RunPostShaderPasses and CopyToOutput.
|
||||
Draw::Framebuffer *postShaderOutput_ = nullptr;
|
||||
FRect rc_;
|
||||
OutputFlags outputFlags_ = OutputFlags::DEFAULT;
|
||||
};
|
||||
+2
-1
@@ -124,7 +124,8 @@ public:
|
||||
uint32_t GetAddrTranslation() override;
|
||||
|
||||
virtual void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) = 0;
|
||||
virtual void CopyDisplayToOutput(const DisplayLayoutConfig &config, bool reallyDirty) = 0;
|
||||
virtual void SetCurFramebufferDirty(bool dirty) = 0;
|
||||
virtual void CopyDisplayToOutput(const DisplayLayoutConfig &config) = 0;
|
||||
virtual bool PresentedThisFrame() const = 0;
|
||||
|
||||
// Invalidate any cached content sourced from the specified range.
|
||||
|
||||
+3
-2
@@ -530,7 +530,7 @@ void GPUCommonHW::PreExecuteOp(u32 op, u32 diff) {
|
||||
CheckFlushOp(op >> 24, diff);
|
||||
}
|
||||
|
||||
void GPUCommonHW::CopyDisplayToOutput(const DisplayLayoutConfig &config, bool reallyDirty) {
|
||||
void GPUCommonHW::CopyDisplayToOutput(const DisplayLayoutConfig &config) {
|
||||
drawEngineCommon_->FlushQueuedDepth();
|
||||
// Flush anything left over.
|
||||
drawEngineCommon_->Flush();
|
||||
@@ -538,7 +538,8 @@ void GPUCommonHW::CopyDisplayToOutput(const DisplayLayoutConfig &config, bool re
|
||||
shaderManager_->DirtyLastShader();
|
||||
|
||||
// after this, render pass is active.
|
||||
framebufferManager_->CopyDisplayToOutput(config, reallyDirty);
|
||||
framebufferManager_->CopyDisplayToOutput(config, curFramebufferDirty_);
|
||||
curFramebufferDirty_ = false;
|
||||
|
||||
gstate_c.Dirty(DIRTY_TEXTURE_IMAGE);
|
||||
}
|
||||
|
||||
+4
-2
@@ -7,10 +7,11 @@
|
||||
class GPUCommonHW : public GPUCommon {
|
||||
public:
|
||||
GPUCommonHW(GraphicsContext *gfxCtx, Draw::DrawContext *draw);
|
||||
~GPUCommonHW();
|
||||
~GPUCommonHW() override;
|
||||
|
||||
// This can fail, and if so no render pass is active.
|
||||
void CopyDisplayToOutput(const DisplayLayoutConfig &config, bool reallyDirty) override;
|
||||
void SetCurFramebufferDirty(bool dirty) override { curFramebufferDirty_ = dirty; }
|
||||
void CopyDisplayToOutput(const DisplayLayoutConfig &config) override;
|
||||
void DoState(PointerWrap &p) override;
|
||||
void DeviceLost() override;
|
||||
void DeviceRestore(Draw::DrawContext *draw) override;
|
||||
@@ -105,4 +106,5 @@ protected:
|
||||
int msaaLevel_ = 0;
|
||||
bool sawExactEqualDepth_ = false;
|
||||
ShaderManagerCommon *shaderManager_ = nullptr;
|
||||
bool curFramebufferDirty_ = false;
|
||||
};
|
||||
@@ -643,10 +643,11 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(const DisplayLayoutConfig &config,
|
||||
}
|
||||
|
||||
presentation_->SourceTexture(fbTex, desc.width, desc.height);
|
||||
presentation_->CopyToOutput(config, outputFlags, config.iInternalScreenRotation, u0, v0, u1, v1);
|
||||
presentation_->RunPostshaderPasses(config, outputFlags, config.iInternalScreenRotation, u0, v0, u1, v1);
|
||||
presentation_->CopyToOutput(config);
|
||||
}
|
||||
|
||||
void SoftGPU::CopyDisplayToOutput(const DisplayLayoutConfig &config, bool reallyDirty) {
|
||||
void SoftGPU::CopyDisplayToOutput(const DisplayLayoutConfig &config) {
|
||||
drawEngine_->transformUnit.Flush(this, "output");
|
||||
// The display always shows 480x272.
|
||||
CopyToCurrentFboFromDisplayRam(config, FB_WIDTH, FB_HEIGHT);
|
||||
|
||||
@@ -137,7 +137,8 @@ public:
|
||||
void UpdateCmdInfo() override {}
|
||||
|
||||
void SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat format) override;
|
||||
void CopyDisplayToOutput(const DisplayLayoutConfig &config, bool reallyDirty) override;
|
||||
void SetCurFramebufferDirty(bool dirty) override {}
|
||||
void CopyDisplayToOutput(const DisplayLayoutConfig &config) override;
|
||||
void GetStats(char *buffer, size_t bufsize) override;
|
||||
std::vector<const VirtualFramebuffer *> GetFramebufferList() const override { return std::vector<const VirtualFramebuffer *>(); }
|
||||
void InvalidateCache(u32 addr, int size, GPUInvalidationType type) override;
|
||||
|
||||
+27
-16
@@ -1634,13 +1634,12 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
// If a boot is in progress, update it.
|
||||
ProcessGameBoot(gamePath_);
|
||||
|
||||
ScreenRenderFlags flags = ScreenRenderFlags::NONE;
|
||||
Draw::Viewport viewport{ 0.0f, 0.0f, (float)g_display.pixel_xres, (float)g_display.pixel_yres, 0.0f, 1.0f };
|
||||
const Draw::Viewport viewport{0.0f, 0.0f, (float)g_display.pixel_xres, (float)g_display.pixel_yres, 0.0f, 1.0f};
|
||||
using namespace Draw;
|
||||
|
||||
DrawContext *draw = screenManager()->getDrawContext();
|
||||
if (!draw) {
|
||||
return flags; // shouldn't really happen but I've seen a suspicious stack trace..
|
||||
return ScreenRenderFlags::NONE; // shouldn't really happen but I've seen a suspicious stack trace..
|
||||
}
|
||||
|
||||
ProcessQueuedVKeys();
|
||||
@@ -1649,8 +1648,6 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
|
||||
bool framebufferBound = false;
|
||||
|
||||
const DeviceOrientation orientation = GetDeviceOrientation();
|
||||
|
||||
if (mode & ScreenRenderMode::FIRST) {
|
||||
// Actually, always gonna be first when it exists (?)
|
||||
|
||||
@@ -1664,9 +1661,9 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
if (skipBufferEffects && !g_Config.bSoftwareRendering) {
|
||||
// We need to clear here already so that drawing during the frame is done on a clean slate.
|
||||
if (Core_IsStepping() && gpuStats.numFlips != 0) {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_BackBuffer");
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, {RPAction::KEEP, RPAction::CLEAR, RPAction::CLEAR}, "EmuScreen_BackBuffer");
|
||||
} else {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "EmuScreen_BackBuffer");
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, {RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000}, "EmuScreen_BackBuffer");
|
||||
}
|
||||
|
||||
draw->SetViewport(viewport);
|
||||
@@ -1681,6 +1678,7 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
|
||||
g_OSD.NudgeIngameNotifications();
|
||||
|
||||
const DeviceOrientation orientation = GetDeviceOrientation();
|
||||
const DisplayLayoutConfig &displayLayoutConfig = g_Config.GetDisplayLayoutConfig(orientation);
|
||||
__DisplaySetDisplayLayoutConfig(displayLayoutConfig);
|
||||
|
||||
@@ -1692,14 +1690,15 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
if (PSP_IsInited() && !skipBufferEffects) {
|
||||
_dbg_assert_(gpu);
|
||||
gpu->BeginHostFrame(displayLayoutConfig);
|
||||
gpu->CopyDisplayToOutput(displayLayoutConfig, true);
|
||||
gpu->SetCurFramebufferDirty(true);
|
||||
gpu->CopyDisplayToOutput(displayLayoutConfig);
|
||||
gpu->EndHostFrame();
|
||||
}
|
||||
if (gpu && gpu->PresentedThisFrame()) {
|
||||
framebufferBound = true;
|
||||
}
|
||||
if (!framebufferBound) {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, }, "EmuScreen_Behind");
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, {RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR,}, "EmuScreen_Behind");
|
||||
}
|
||||
|
||||
Draw::BackendState state = draw->GetCurrentBackendState();
|
||||
@@ -1708,7 +1707,7 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
// _dbg_assert_msg_(state.passes >= 1, "skipB: %d sw: %d mode: %d back: %d tag: %s behi: %d", (int)skipBufferEffects, (int)g_Config.bSoftwareRendering, (int)mode, (int)g_Config.iGPUBackend, screenManager()->topScreen()->tag(), (int)g_Config.bRunBehindPauseMenu);
|
||||
// Workaround any remaining bugs like this.
|
||||
if (state.passes == 0) {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, }, "EmuScreen_SafeFallback");
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, {RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR,}, "EmuScreen_SafeFallback");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1717,7 +1716,7 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
draw->SetViewport(viewport);
|
||||
draw->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres);
|
||||
darken();
|
||||
return flags;
|
||||
return ScreenRenderFlags::NONE;
|
||||
}
|
||||
|
||||
if (!PSP_IsInited() || readyToFinishBoot_) {
|
||||
@@ -1726,13 +1725,13 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
if (mode & ScreenRenderMode::TOP) {
|
||||
checkPowerDown();
|
||||
}
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_Invalid");
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, {RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR}, "EmuScreen_Invalid");
|
||||
// Need to make sure the UI texture is available, for "darken".
|
||||
screenManager()->getUIContext()->BeginFrame();
|
||||
draw->SetViewport(viewport);
|
||||
draw->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres);
|
||||
renderUI();
|
||||
return flags;
|
||||
return ScreenRenderFlags::NONE;
|
||||
}
|
||||
|
||||
// Freeze-frame functionality (loads a savestate on every frame).
|
||||
@@ -1748,13 +1747,24 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
PSP_UpdateDebugStats((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS || g_Config.bLogFrameDrops);
|
||||
|
||||
// Running it early allows things like direct readbacks of buffers, things we can't do
|
||||
// when we have started the final render pass. Well, technically we probably could with some manipulation
|
||||
// of pass order in the render managers..
|
||||
runImDebugger();
|
||||
|
||||
return RunEmulation(mode, framebufferBound, skipBufferEffects);
|
||||
}
|
||||
|
||||
ScreenRenderFlags EmuScreen::RunEmulation(ScreenRenderMode mode, bool framebufferBound, bool skipBufferEffects) {
|
||||
using namespace Draw;
|
||||
ScreenRenderFlags flags = ScreenRenderFlags::NONE;
|
||||
|
||||
const DeviceOrientation orientation = GetDeviceOrientation();
|
||||
const DisplayLayoutConfig &displayLayoutConfig = g_Config.GetDisplayLayoutConfig(orientation);
|
||||
DrawContext *draw = screenManager()->getDrawContext();
|
||||
const Draw::Viewport viewport{0.0f, 0.0f, (float)g_display.pixel_xres, (float)g_display.pixel_yres, 0.0f, 1.0f};
|
||||
|
||||
PSP_UpdateDebugStats((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS || g_Config.bLogFrameDrops);
|
||||
bool blockedExecution = Achievements::IsBlockingExecution();
|
||||
uint32_t clearColor = 0;
|
||||
if (!blockedExecution) {
|
||||
@@ -1794,7 +1804,8 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
|
||||
// This won't work in non-buffered, but that's fine.
|
||||
if (!framebufferBound && PSP_IsInited()) {
|
||||
// draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, clearColor }, "EmuScreen_Stepping");
|
||||
gpu->CopyDisplayToOutput(displayLayoutConfig, true);
|
||||
gpu->SetCurFramebufferDirty(true);
|
||||
gpu->CopyDisplayToOutput(displayLayoutConfig);
|
||||
framebufferBound = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ protected:
|
||||
|
||||
private:
|
||||
void CreateViews() override;
|
||||
ScreenRenderFlags RunEmulation(ScreenRenderMode mode, bool framebufferBound, bool skipBufferEffects);
|
||||
void OnDevTools(UI::EventParams ¶ms);
|
||||
void OnChat(UI::EventParams ¶ms);
|
||||
|
||||
|
||||
@@ -1405,6 +1405,7 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) {
|
||||
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
|
||||
auto co = GetI18NCategory(I18NCat::CONTROLS);
|
||||
|
||||
// Display rotation
|
||||
AddRotationPicker(screenManager(), systemSettings, true);
|
||||
|
||||
if (System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE)) {
|
||||
|
||||
+4
-2
@@ -245,8 +245,10 @@ void AddRotationPicker(ScreenManager *screenManager, UI::ViewGroup *parent, bool
|
||||
auto co = GetI18NCategory(I18NCat::CONTROLS);
|
||||
|
||||
PopupMultiChoice *rot = parent->Add(new PopupMultiChoice(&g_Config.iScreenRotation, text ? co->T("Screen Rotation") : "", screenRotation, 0, ARRAY_SIZE(screenRotation), I18NCat::CONTROLS, screenManager, text ? nullptr : new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT)));
|
||||
rot->SetHideTitle(true);
|
||||
rot->SetIconOnly(true);
|
||||
if (!text) {
|
||||
rot->SetHideTitle(true);
|
||||
rot->SetIconOnly(true);
|
||||
}
|
||||
rot->SetChoiceIcons(screenRotationIcons);
|
||||
|
||||
// Portrait Reversed is not recommended on iPhone (and we also ban it in the plist).
|
||||
|
||||
@@ -268,8 +268,10 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const
|
||||
if (draw) {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "Headless");
|
||||
// Vulkan may get angry if we don't do a final present.
|
||||
if (gpu)
|
||||
gpu->CopyDisplayToOutput(g_Config.GetDisplayLayoutConfig(DeviceOrientation::Landscape), true);
|
||||
if (gpu) {
|
||||
gpu->SetCurFramebufferDirty(true);
|
||||
gpu->CopyDisplayToOutput(g_Config.GetDisplayLayoutConfig(DeviceOrientation::Landscape));
|
||||
}
|
||||
|
||||
draw->EndFrame();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user