mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-16 07:50:28 +02:00
The libretro core is the frontend, and the headless tools are the test and debugging hosts. The Windows, UWP, Android, iOS, macOS and SDL apps, the UI framework and imgui go, along with what only they used: the Win32 debugger hooks (g_frameMutex, the shutdown lock), JNI thread attachment, the Android text drawer, RAIntegration, Discord, recent files, game installs, tilt input, the audio resamplers, the control mapper, memstick moving and the remote ISO and upload server modes. The web server keeps the debugger. The MSVC, Gradle and ndk-build projects go too.
254 lines
8.5 KiB
C++
254 lines
8.5 KiB
C++
// 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 "Common/Profiler/Profiler.h"
|
|
#include "Common/Data/Text/I18n.h"
|
|
|
|
#include "Common/Log.h"
|
|
#include "Common/Serialize/Serializer.h"
|
|
#include "Common/File/FileUtil.h"
|
|
#include "Common/Data/Text/StringWriter.h"
|
|
#include "Common/GPU/GraphicsContext.h"
|
|
#include "Common/System/OSD.h"
|
|
#include "Common/VR/PPSSPPVR.h"
|
|
#include "Common/StringUtils.h"
|
|
|
|
#include "Core/Config.h"
|
|
#include "Core/Reporting.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/ELF/ParamSFO.h"
|
|
|
|
#include "GPU/GPUState.h"
|
|
#include "GPU/Common/FramebufferManagerCommon.h"
|
|
#include "GPU/GLES/ShaderManagerGLES.h"
|
|
#include "GPU/GLES/GPU_GLES.h"
|
|
#include "GPU/GLES/FramebufferManagerGLES.h"
|
|
#include "GPU/GLES/DrawEngineGLES.h"
|
|
#include "GPU/GLES/TextureCacheGLES.h"
|
|
|
|
|
|
GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
|
: GPUCommonHW(gfxCtx, draw), drawEngine_(draw), fragmentTestCache_(draw) {
|
|
gstate_c.SetUseFlags(CheckGPUFeatures());
|
|
|
|
shaderManagerGL_ = new ShaderManagerGLES(draw);
|
|
framebufferManagerGL_ = new FramebufferManagerGLES(draw);
|
|
framebufferManager_ = framebufferManagerGL_;
|
|
textureCacheGL_ = new TextureCacheGLES(draw, framebufferManager_->GetDraw2D());
|
|
textureCache_ = textureCacheGL_;
|
|
drawEngineCommon_ = &drawEngine_;
|
|
shaderManager_ = shaderManagerGL_;
|
|
|
|
drawEngine_.SetGPUCommon(this);
|
|
drawEngine_.SetShaderManager(shaderManagerGL_);
|
|
drawEngine_.SetTextureCache(textureCacheGL_);
|
|
drawEngine_.SetFramebufferManager(framebufferManagerGL_);
|
|
drawEngine_.SetFragmentTestCache(&fragmentTestCache_);
|
|
drawEngine_.Init();
|
|
framebufferManagerGL_->SetTextureCache(textureCacheGL_);
|
|
framebufferManagerGL_->SetShaderManager(shaderManagerGL_);
|
|
framebufferManagerGL_->SetDrawEngine(&drawEngine_);
|
|
framebufferManagerGL_->Init(msaaLevel_);
|
|
textureCacheGL_->SetFramebufferManager(framebufferManagerGL_);
|
|
textureCacheGL_->SetShaderManager(shaderManagerGL_);
|
|
textureCacheGL_->SetDrawEngine(&drawEngine_);
|
|
fragmentTestCache_.SetTextureCache(textureCacheGL_);
|
|
|
|
// Sanity check gstate
|
|
if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) {
|
|
ERROR_LOG(Log::G3D, "gstate has drifted out of sync!");
|
|
}
|
|
|
|
// No need to flush before the tex scale/offset commands if we are baking
|
|
// the tex scale/offset into the vertices anyway.
|
|
|
|
UpdateCmdInfo();
|
|
|
|
BuildReportingInfo();
|
|
|
|
textureCache_->NotifyConfigChanged();
|
|
|
|
// Load shader cache.
|
|
std::string discID = g_paramSFO.GetDiscID();
|
|
if (discID.size()) {
|
|
if (g_Config.bShaderCache) {
|
|
File::CreateFullPath(GetSysDirectory(DIRECTORY_APP_CACHE));
|
|
shaderCachePath_ = GetSysDirectory(DIRECTORY_APP_CACHE) / (discID + ".glshadercache");
|
|
// Actually precompiled by IsReady() since we're single-threaded.
|
|
File::IOFile f(shaderCachePath_, "rb");
|
|
if (f.IsOpen()) {
|
|
if (shaderManagerGL_->LoadCacheFlags(f, &drawEngine_)) {
|
|
gstate_c.SetUseFlags(CheckGPUFeatures());
|
|
// We're compiling now, clear if they changed.
|
|
gstate_c.useFlagsChanged = false;
|
|
|
|
if (shaderManagerGL_->LoadCache(f))
|
|
NOTICE_LOG(Log::G3D, "Precompiling the shader cache from '%s'", shaderCachePath_.c_str());
|
|
}
|
|
}
|
|
} else {
|
|
INFO_LOG(Log::G3D, "Shader cache disabled. Not loading.");
|
|
}
|
|
}
|
|
}
|
|
|
|
GPU_GLES::~GPU_GLES() {
|
|
// If we're here during app shutdown (exiting the Windows app in-game, for example)
|
|
// everything should already be cleared since DeviceLost has been run.
|
|
|
|
if (shaderCachePath_.Valid() && draw_) {
|
|
if (g_Config.bShaderCache) {
|
|
shaderManagerGL_->SaveCache(shaderCachePath_, &drawEngine_);
|
|
} else {
|
|
INFO_LOG(Log::G3D, "Shader cache disabled. Not saving.");
|
|
}
|
|
}
|
|
fragmentTestCache_.Clear();
|
|
}
|
|
|
|
// Take the raw GL extension and versioning data and turn into feature flags.
|
|
// TODO: This should use DrawContext::GetDeviceCaps() more and more, and eventually
|
|
// this can be shared between all the backends.
|
|
u32 GPU_GLES::CheckGPUFeatures() const {
|
|
u32 features = GPUCommonHW::CheckGPUFeatures();
|
|
|
|
features |= GPU_USE_16BIT_FORMATS;
|
|
|
|
if (!draw_->GetShaderLanguageDesc().bitwiseOps) {
|
|
features |= GPU_USE_FRAGMENT_TEST_CACHE;
|
|
}
|
|
|
|
// Can't use switch-case in older glsl.
|
|
if ((gl_extensions.IsGLES && !gl_extensions.GLES3) || (!gl_extensions.IsGLES && !gl_extensions.VersionGEThan(1, 3)))
|
|
features &= ~GPU_USE_LIGHT_UBERSHADER;
|
|
|
|
if (IsVREnabled() || g_Config.bForceVR) {
|
|
features |= GPU_USE_VIRTUAL_REALITY;
|
|
features &= ~GPU_USE_VS_RANGE_CULLING;
|
|
}
|
|
|
|
features = CheckGPUFeaturesLate(features);
|
|
|
|
if (draw_->GetBugs().Has(Draw::Bugs::ADRENO_RESOURCE_DEADLOCK) && g_Config.bVendorBugChecksEnabled) {
|
|
if (PSP_CoreParameter().compat.flags().OldAdrenoPixelDepthRoundingGL) {
|
|
features |= GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT;
|
|
}
|
|
}
|
|
|
|
// This is a bit ugly, but lets us reuse most of the depth logic in GPUCommon.
|
|
if (features & GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT) {
|
|
if (gl_extensions.IsGLES && !gl_extensions.GLES3) {
|
|
// Unsupported, switch to GPU_ROUND_DEPTH_TO_16BIT instead.
|
|
features &= ~GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT;
|
|
features |= GPU_ROUND_DEPTH_TO_16BIT;
|
|
}
|
|
}
|
|
|
|
if (g_Config.bSkipBufferEffects) {
|
|
features |= GPU_USE_NONBUFFERED_FLIP;
|
|
}
|
|
|
|
return features;
|
|
}
|
|
|
|
void GPU_GLES::BuildReportingInfo() {
|
|
GLRenderManager *render = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
|
|
if (!render) {
|
|
return;
|
|
}
|
|
|
|
std::string glVendor = render->GetGLString(GL_VENDOR);
|
|
std::string glRenderer = render->GetGLString(GL_RENDERER);
|
|
std::string glVersion = render->GetGLString(GL_VERSION);
|
|
std::string glSlVersion = render->GetGLString(GL_SHADING_LANGUAGE_VERSION);
|
|
std::string glExtensions;
|
|
|
|
if (gl_extensions.VersionGEThan(3, 0)) {
|
|
glExtensions = g_all_gl_extensions;
|
|
} else {
|
|
glExtensions = render->GetGLString(GL_EXTENSIONS);
|
|
}
|
|
|
|
reportingPrimaryInfo_ = glVendor;
|
|
reportingFullInfo_ = StringFromFormat("%s (%s %s), %s (extensions: %s)", glVersion.c_str(), glVendor.c_str(), glRenderer.c_str(), glSlVersion.c_str(), glExtensions.c_str());
|
|
|
|
Reporting::UpdateConfig();
|
|
}
|
|
|
|
void GPU_GLES::DeviceLost() {
|
|
INFO_LOG(Log::G3D, "GPU_GLES: DeviceLost");
|
|
|
|
// Simply drop all caches and textures.
|
|
// FBOs appear to survive? Or no?
|
|
// TransformDraw has registered as a GfxResourceHolder.
|
|
fragmentTestCache_.DeviceLost();
|
|
|
|
GPUCommonHW::DeviceLost();
|
|
}
|
|
|
|
void GPU_GLES::DeviceRestore(Draw::DrawContext *draw) {
|
|
GPUCommonHW::DeviceRestore(draw);
|
|
|
|
fragmentTestCache_.DeviceRestore(draw_);
|
|
}
|
|
|
|
void GPU_GLES::BeginHostFrame(const DisplayLayoutConfig &config) {
|
|
GPUCommonHW::BeginHostFrame(config);
|
|
drawEngine_.BeginFrame();
|
|
|
|
textureCache_->StartFrame();
|
|
|
|
// Save the cache from time to time. TODO: How often? We save on exit, so shouldn't need to do this all that often.
|
|
|
|
constexpr int saveShaderCacheFrameInterval = 32767; // power of 2 - 1. About every 10 minutes at 60fps.
|
|
if (shaderCachePath_.Valid() && !(gpuStats.totals.numFlips & saveShaderCacheFrameInterval) && coreState == CORE_RUNNING_CPU) {
|
|
shaderManagerGL_->SaveCache(shaderCachePath_, &drawEngine_);
|
|
}
|
|
|
|
gstate_c.Dirty(DIRTY_ALL_UNIFORMS | DIRTY_FRAGMENTSHADER_STATE | DIRTY_VERTEXSHADER_STATE);
|
|
|
|
framebufferManager_->BeginFrame(config);
|
|
|
|
fragmentTestCache_.Decimate();
|
|
if (gstate_c.useFlagsChanged) {
|
|
// TODO: It'd be better to recompile them in the background, probably?
|
|
// This most likely means that saw equal depth changed.
|
|
WARN_LOG(Log::G3D, "Shader use flags changed, clearing all shaders and depth buffers");
|
|
shaderManager_->ClearShaders();
|
|
framebufferManager_->ClearAllDepthBuffers();
|
|
gstate_c.useFlagsChanged = false;
|
|
}
|
|
}
|
|
|
|
void GPU_GLES::EndHostFrame() {
|
|
drawEngine_.EndFrame();
|
|
}
|
|
|
|
void GPU_GLES::FinishDeferred() {
|
|
// This finishes reading any vertex data that is pending.
|
|
drawEngine_.FinishDeferred();
|
|
}
|
|
|
|
void GPU_GLES::GetStats(StringWriter &w) {
|
|
w.F("Vertex, Fragment, Programs loaded: %d, %d, %d\n",
|
|
shaderManagerGL_->GetNumVertexShaders(),
|
|
shaderManagerGL_->GetNumFragmentShaders(),
|
|
shaderManagerGL_->GetNumPrograms()
|
|
);
|
|
FormatGPUStatsCommon(w);
|
|
}
|