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/Vulkan/GPU_Vulkan.cpp
Views: 1401
1// Copyright (c) 2015- PPSSPP Project.23// This program is free software: you can redistribute it and/or modify4// it under the terms of the GNU General Public License as published by5// the Free Software Foundation, version 2.0 or later versions.67// This program is distributed in the hope that it will be useful,8// but WITHOUT ANY WARRANTY; without even the implied warranty of9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the10// GNU General Public License 2.0 for more details.1112// A copy of the GPL 2.0 should have been included with the program.13// If not, see http://www.gnu.org/licenses/1415// Official git repository and contact information can be found at16// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1718#include <thread>1920#include "Common/Profiler/Profiler.h"2122#include "Common/Log.h"23#include "Common/File/FileUtil.h"24#include "Common/GraphicsContext.h"25#include "Common/Serialize/Serializer.h"26#include "Common/TimeUtil.h"27#include "Common/Thread/ThreadUtil.h"2829#include "Core/Config.h"30#include "Core/Debugger/Breakpoints.h"31#include "Core/MemMapHelpers.h"32#include "Core/Reporting.h"33#include "Core/System.h"34#include "Core/ELF/ParamSFO.h"3536#include "GPU/GPUState.h"37#include "GPU/ge_constants.h"38#include "GPU/GeDisasm.h"39#include "GPU/Common/FramebufferManagerCommon.h"40#include "GPU/Vulkan/ShaderManagerVulkan.h"41#include "GPU/Vulkan/GPU_Vulkan.h"42#include "GPU/Vulkan/FramebufferManagerVulkan.h"43#include "GPU/Vulkan/DrawEngineVulkan.h"44#include "GPU/Vulkan/TextureCacheVulkan.h"45#include "Common/GPU/Vulkan/VulkanRenderManager.h"46#include "Common/GPU/Vulkan/VulkanQueueRunner.h"4748GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw)49: GPUCommonHW(gfxCtx, draw), drawEngine_(draw) {50gstate_c.SetUseFlags(CheckGPUFeatures());51drawEngine_.InitDeviceObjects();5253VulkanContext *vulkan = (VulkanContext *)gfxCtx->GetAPIContext();5455vulkan->SetProfilerEnabledPtr(&g_Config.bGpuLogProfiler);5657shaderManagerVulkan_ = new ShaderManagerVulkan(draw);58pipelineManager_ = new PipelineManagerVulkan(vulkan);59framebufferManagerVulkan_ = new FramebufferManagerVulkan(draw);60framebufferManager_ = framebufferManagerVulkan_;61textureCacheVulkan_ = new TextureCacheVulkan(draw, framebufferManager_->GetDraw2D(), vulkan);62textureCache_ = textureCacheVulkan_;63drawEngineCommon_ = &drawEngine_;64shaderManager_ = shaderManagerVulkan_;6566drawEngine_.SetTextureCache(textureCacheVulkan_);67drawEngine_.SetFramebufferManager(framebufferManagerVulkan_);68drawEngine_.SetShaderManager(shaderManagerVulkan_);69drawEngine_.SetPipelineManager(pipelineManager_);70drawEngine_.Init();71framebufferManagerVulkan_->SetTextureCache(textureCacheVulkan_);72framebufferManagerVulkan_->SetDrawEngine(&drawEngine_);73framebufferManagerVulkan_->SetShaderManager(shaderManagerVulkan_);74framebufferManagerVulkan_->Init(msaaLevel_);75textureCacheVulkan_->SetFramebufferManager(framebufferManagerVulkan_);76textureCacheVulkan_->SetShaderManager(shaderManagerVulkan_);77textureCacheVulkan_->SetDrawEngine(&drawEngine_);7879InitDeviceObjects();8081// Sanity check gstate82if ((int *)&gstate.transferstart - (int *)&gstate != 0xEA) {83ERROR_LOG(Log::G3D, "gstate has drifted out of sync!");84}8586BuildReportingInfo();8788textureCache_->NotifyConfigChanged();8990// Load shader cache.91std::string discID = g_paramSFO.GetDiscID();92if (discID.size()) {93File::CreateFullPath(GetSysDirectory(DIRECTORY_APP_CACHE));94shaderCachePath_ = GetSysDirectory(DIRECTORY_APP_CACHE) / (discID + ".vkshadercache");95LoadCache(shaderCachePath_);96}97}9899void GPU_Vulkan::LoadCache(const Path &filename) {100if (!g_Config.bShaderCache) {101WARN_LOG(Log::G3D, "Shader cache disabled. Not loading.");102return;103}104105PSP_SetLoading("Loading shader cache...");106// Actually precompiled by IsReady() since we're single-threaded.107FILE *f = File::OpenCFile(filename, "rb");108if (!f)109return;110111// First compile shaders to SPIR-V, then load the pipeline cache and recreate the pipelines.112// It's when recreating the pipelines that the pipeline cache is useful - in the ideal case,113// it can just memcpy the finished shader binaries out of the pipeline cache file.114bool result = shaderManagerVulkan_->LoadCacheFlags(f, &drawEngine_);115if (!result) {116WARN_LOG(Log::G3D, "ShaderManagerVulkan failed to load cache header.");117}118if (result) {119// Reload use flags in case LoadCacheFlags() changed them.120if (drawEngineCommon_->EverUsedExactEqualDepth()) {121sawExactEqualDepth_ = true;122}123gstate_c.SetUseFlags(CheckGPUFeatures());124result = shaderManagerVulkan_->LoadCache(f);125if (!result) {126WARN_LOG(Log::G3D, "ShaderManagerVulkan failed to load cache.");127}128}129if (result) {130// WARNING: See comment in LoadPipelineCache if you are tempted to flip the second parameter to true.131result = pipelineManager_->LoadPipelineCache(f, false, shaderManagerVulkan_, draw_, drawEngine_.GetPipelineLayout(), msaaLevel_);132}133fclose(f);134135if (!result) {136WARN_LOG(Log::G3D, "Incompatible Vulkan pipeline cache - rebuilding.");137// Bad cache file for this GPU/Driver/etc. Delete it.138File::Delete(filename);139} else {140INFO_LOG(Log::G3D, "Loaded Vulkan pipeline cache.");141}142}143144void GPU_Vulkan::SaveCache(const Path &filename) {145if (!g_Config.bShaderCache) {146INFO_LOG(Log::G3D, "Shader cache disabled. Not saving.");147return;148}149150if (!draw_) {151// Already got the lost message, we're in shutdown.152WARN_LOG(Log::G3D, "Not saving shaders - shutting down from in-game.");153return;154}155156FILE *f = File::OpenCFile(filename, "wb");157if (!f)158return;159shaderManagerVulkan_->SaveCache(f, &drawEngine_);160// WARNING: See comment in LoadCache if you are tempted to flip the second parameter to true.161pipelineManager_->SavePipelineCache(f, false, shaderManagerVulkan_, draw_);162INFO_LOG(Log::G3D, "Saved Vulkan pipeline cache");163fclose(f);164}165166GPU_Vulkan::~GPU_Vulkan() {167if (draw_) {168VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);169// This now also does a hard sync with the render thread, so that we can safely delete our pipeline layout below.170rm->StopThreads();171rm->CheckNothingPending();172}173174SaveCache(shaderCachePath_);175176// StopThreads should have ensured that no pipelines are queued to compile at this point. So we can tear it down.177delete pipelineManager_;178pipelineManager_ = nullptr;179180// Note: We save the cache in DeviceLost181DestroyDeviceObjects();182drawEngine_.DeviceLost();183shaderManager_->ClearShaders();184185// other managers are deleted in ~GPUCommonHW.186if (draw_) {187VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);188rm->StartThreads();189}190}191192u32 GPU_Vulkan::CheckGPUFeatures() const {193uint32_t features = GPUCommonHW::CheckGPUFeatures();194195VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);196197// Could simplify this, but it's good as documentation.198switch (vulkan->GetPhysicalDeviceProperties().properties.vendorID) {199case VULKAN_VENDOR_AMD:200// Accurate depth is required on AMD (due to reverse-Z driver bug) so we ignore the compat flag to disable it on those. See #9545201features |= GPU_USE_ACCURATE_DEPTH;202break;203case VULKAN_VENDOR_QUALCOMM:204// Accurate depth is required on Adreno too (seems to also have a reverse-Z driver bug).205features |= GPU_USE_ACCURATE_DEPTH;206break;207case VULKAN_VENDOR_ARM:208{209// This check is probably not exactly accurate. But old drivers had problems with reverse-Z, just like AMD and Qualcomm.210211// NOTE: Galaxy S8 has version 16 but still seems to have some problems with accurate depth.212213// TODO: Move this check to thin3d_vulkan.214215bool driverTooOld = IsHashMaliDriverVersion(vulkan->GetPhysicalDeviceProperties().properties)216|| VK_VERSION_MAJOR(vulkan->GetPhysicalDeviceProperties().properties.driverVersion) < 14;217218if (!PSP_CoreParameter().compat.flags().DisableAccurateDepth || driverTooOld) {219features |= GPU_USE_ACCURATE_DEPTH;220} else {221features &= ~GPU_USE_ACCURATE_DEPTH;222}223break;224}225case VULKAN_VENDOR_IMGTEC:226// We ignore the disable flag on IMGTec. Another reverse-Z bug (plus, not really any reason to bother). See #17044227features |= GPU_USE_ACCURATE_DEPTH;228break;229default:230// On other GPUs we'll just assume we don't need inaccurate depth, leaving ARM Mali as the odd one out.231features |= GPU_USE_ACCURATE_DEPTH;232break;233}234235// Might enable this later - in the first round we are mostly looking at depth/stencil/discard.236// if (!g_Config.bEnableVendorBugChecks)237// features |= GPU_USE_ACCURATE_DEPTH;238239// Mandatory features on Vulkan, which may be checked in "centralized" code240features |= GPU_USE_TEXTURE_LOD_CONTROL;241features |= GPU_USE_INSTANCE_RENDERING;242features |= GPU_USE_VERTEX_TEXTURE_FETCH;243features |= GPU_USE_TEXTURE_FLOAT;244245// Fall back to geometry shader culling if we can't do vertex range culling.246// Checking accurate depth here because the old depth path is uncommon and not well tested for this.247if (draw_->GetDeviceCaps().geometryShaderSupported && (features & GPU_USE_ACCURATE_DEPTH) != 0) {248const bool useGeometry = g_Config.bUseGeometryShader && !draw_->GetBugs().Has(Draw::Bugs::GEOMETRY_SHADERS_SLOW_OR_BROKEN);249const bool vertexSupported = draw_->GetDeviceCaps().clipDistanceSupported && draw_->GetDeviceCaps().cullDistanceSupported;250if (useGeometry && (!vertexSupported || (features & GPU_USE_VS_RANGE_CULLING) == 0)) {251// Switch to culling via the geometry shader if not fully supported in vertex.252features |= GPU_USE_GS_CULLING;253features &= ~GPU_USE_VS_RANGE_CULLING;254}255}256257if (!draw_->GetBugs().Has(Draw::Bugs::PVR_BAD_16BIT_TEXFORMATS)) {258// These are VULKAN_4444_FORMAT and friends.259// Note that we are now using the correct set of formats - the only cases where some may be missing260// are non-conformant implementations like MoltenVK.261uint32_t fmt4444 = draw_->GetDataFormatSupport(Draw::DataFormat::B4G4R4A4_UNORM_PACK16);262uint32_t fmt1555 = draw_->GetDataFormatSupport(Draw::DataFormat::A1R5G5B5_UNORM_PACK16);263uint32_t fmt565 = draw_->GetDataFormatSupport(Draw::DataFormat::R5G6B5_UNORM_PACK16);264if ((fmt4444 & Draw::FMT_TEXTURE) && (fmt565 & Draw::FMT_TEXTURE) && (fmt1555 & Draw::FMT_TEXTURE)) {265features |= GPU_USE_16BIT_FORMATS;266} else {267INFO_LOG(Log::G3D, "Deficient texture format support: 4444: %d 1555: %d 565: %d", fmt4444, fmt1555, fmt565);268}269}270271if (g_Config.bStereoRendering && draw_->GetDeviceCaps().multiViewSupported) {272features |= GPU_USE_SINGLE_PASS_STEREO;273features |= GPU_USE_SIMPLE_STEREO_PERSPECTIVE;274275if (features & GPU_USE_GS_CULLING) {276// Many devices that support stereo and GS don't support GS during stereo.277features &= ~GPU_USE_GS_CULLING;278features |= GPU_USE_VS_RANGE_CULLING;279}280}281282// Attempt to workaround #17386283if (draw_->GetBugs().Has(Draw::Bugs::UNIFORM_INDEXING_BROKEN)) {284features &= ~GPU_USE_LIGHT_UBERSHADER;285}286287features |= GPU_USE_FRAMEBUFFER_ARRAYS;288return CheckGPUFeaturesLate(features);289}290291void GPU_Vulkan::BeginHostFrame() {292GPUCommonHW::BeginHostFrame();293294drawEngine_.BeginFrame();295textureCache_->StartFrame();296297VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);298int curFrame = vulkan->GetCurFrame();299300framebufferManager_->BeginFrame();301302shaderManagerVulkan_->DirtyLastShader();303gstate_c.Dirty(DIRTY_ALL);304305if (gstate_c.useFlagsChanged) {306// TODO: It'd be better to recompile them in the background, probably?307// This most likely means that saw equal depth changed.308WARN_LOG(Log::G3D, "Shader use flags changed, clearing all shaders and depth buffers");309// TODO: Not all shaders need to be recompiled. In fact, quite few? Of course, depends on310// the use flag change.. This is a major frame rate hitch in the start of a race in Outrun.311shaderManager_->ClearShaders();312pipelineManager_->Clear();313framebufferManager_->ClearAllDepthBuffers();314gstate_c.useFlagsChanged = false;315}316317if (dumpNextFrame_) {318NOTICE_LOG(Log::G3D, "DUMPING THIS FRAME");319dumpThisFrame_ = true;320dumpNextFrame_ = false;321} else if (dumpThisFrame_) {322dumpThisFrame_ = false;323}324}325326void GPU_Vulkan::EndHostFrame() {327VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);328329drawEngine_.EndFrame();330331GPUCommonHW::EndHostFrame();332}333334// Needs to be called on GPU thread, not reporting thread.335void GPU_Vulkan::BuildReportingInfo() {336VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);337const auto &props = vulkan->GetPhysicalDeviceProperties().properties;338const auto &available = vulkan->GetDeviceFeatures().available;339340#define CHECK_BOOL_FEATURE(n) do { if (available.standard.n) { featureNames += ", " #n; } } while (false)341#define CHECK_BOOL_FEATURE_MULTIVIEW(n) do { if (available.multiview.n) { featureNames += ", " #n; } } while (false)342343std::string featureNames = "";344CHECK_BOOL_FEATURE(fullDrawIndexUint32);345CHECK_BOOL_FEATURE(geometryShader);346CHECK_BOOL_FEATURE(sampleRateShading);347CHECK_BOOL_FEATURE(dualSrcBlend);348CHECK_BOOL_FEATURE(logicOp);349CHECK_BOOL_FEATURE(multiDrawIndirect);350CHECK_BOOL_FEATURE(drawIndirectFirstInstance);351CHECK_BOOL_FEATURE(depthClamp);352CHECK_BOOL_FEATURE(depthBiasClamp);353CHECK_BOOL_FEATURE(depthBounds);354CHECK_BOOL_FEATURE(samplerAnisotropy);355CHECK_BOOL_FEATURE(textureCompressionETC2);356CHECK_BOOL_FEATURE(textureCompressionASTC_LDR);357CHECK_BOOL_FEATURE(textureCompressionBC);358CHECK_BOOL_FEATURE(occlusionQueryPrecise);359CHECK_BOOL_FEATURE(pipelineStatisticsQuery);360CHECK_BOOL_FEATURE(fragmentStoresAndAtomics);361CHECK_BOOL_FEATURE(shaderTessellationAndGeometryPointSize);362CHECK_BOOL_FEATURE(shaderStorageImageMultisample);363CHECK_BOOL_FEATURE(shaderSampledImageArrayDynamicIndexing);364CHECK_BOOL_FEATURE(shaderClipDistance);365CHECK_BOOL_FEATURE(shaderCullDistance);366CHECK_BOOL_FEATURE(shaderInt64);367CHECK_BOOL_FEATURE(shaderInt16);368CHECK_BOOL_FEATURE_MULTIVIEW(multiview);369CHECK_BOOL_FEATURE_MULTIVIEW(multiviewGeometryShader);370371#undef CHECK_BOOL_FEATURE372373if (!featureNames.empty()) {374featureNames = featureNames.substr(2);375}376377char temp[16384];378snprintf(temp, sizeof(temp), "v%08x driver v%08x (%s), vendorID=%d, deviceID=%d (features: %s)", props.apiVersion, props.driverVersion, props.deviceName, props.vendorID, props.deviceID, featureNames.c_str());379reportingPrimaryInfo_ = props.deviceName;380reportingFullInfo_ = temp;381382Reporting::UpdateConfig();383}384385void GPU_Vulkan::FinishDeferred() {386drawEngine_.FinishDeferred();387}388389void GPU_Vulkan::InitDeviceObjects() {390INFO_LOG(Log::G3D, "GPU_Vulkan::InitDeviceObjects");391392uint32_t hacks = 0;393if (PSP_CoreParameter().compat.flags().MGS2AcidHack)394hacks |= QUEUE_HACK_MGS2_ACID;395if (PSP_CoreParameter().compat.flags().SonicRivalsHack)396hacks |= QUEUE_HACK_SONIC;397398// Always on.399hacks |= QUEUE_HACK_RENDERPASS_MERGE;400401if (hacks) {402VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);403rm->GetQueueRunner()->EnableHacks(hacks);404}405}406407void GPU_Vulkan::DestroyDeviceObjects() {408INFO_LOG(Log::G3D, "GPU_Vulkan::DestroyDeviceObjects");409// Need to turn off hacks when shutting down the GPU. Don't want them running in the menu.410if (draw_) {411VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);412if (rm)413rm->GetQueueRunner()->EnableHacks(0);414}415}416417void GPU_Vulkan::CheckRenderResized() {418if (renderResized_) {419GPUCommonHW::CheckRenderResized();420pipelineManager_->InvalidateMSAAPipelines();421framebufferManager_->ReleasePipelines();422}423}424425void GPU_Vulkan::DeviceLost() {426// draw_ is normally actually still valid here in Vulkan. But we null it out in GPUCommonHW::DeviceLost so we don't try to use it again.427// So, we have to save it here to be able to call ReleaseCompileQueue().428Draw::DrawContext *draw = draw_;429if (draw) {430VulkanRenderManager *rm = (VulkanRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);431rm->StopThreads();432}433434if (shaderCachePath_.Valid()) {435SaveCache(shaderCachePath_);436}437DestroyDeviceObjects();438pipelineManager_->DeviceLost();439440GPUCommonHW::DeviceLost();441442if (draw) {443VulkanRenderManager *rm = (VulkanRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);444rm->StartThreads();445}446}447448void GPU_Vulkan::DeviceRestore(Draw::DrawContext *draw) {449GPUCommonHW::DeviceRestore(draw);450451VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);452pipelineManager_->DeviceRestore(vulkan);453454InitDeviceObjects();455}456457void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) {458size_t offset = FormatGPUStatsCommon(buffer, bufsize);459buffer += offset;460bufsize -= offset;461if ((int)bufsize < 0)462return;463const DrawEngineVulkanStats &drawStats = drawEngine_.GetStats();464char texStats[256];465textureCacheVulkan_->GetStats(texStats, sizeof(texStats));466snprintf(buffer, bufsize,467"Vertex, Fragment, Pipelines loaded: %i, %i, %i\n"468"Pushbuffer space used: Vtx %d, Idx %d\n"469"%s\n",470shaderManagerVulkan_->GetNumVertexShaders(),471shaderManagerVulkan_->GetNumFragmentShaders(),472pipelineManager_->GetNumPipelines(),473drawStats.pushVertexSpaceUsed,474drawStats.pushIndexSpaceUsed,475texStats476);477}478479std::vector<std::string> GPU_Vulkan::DebugGetShaderIDs(DebugShaderType type) {480switch (type) {481case SHADER_TYPE_PIPELINE:482return pipelineManager_->DebugGetObjectIDs(type);483case SHADER_TYPE_SAMPLER:484return textureCacheVulkan_->DebugGetSamplerIDs();485default:486return GPUCommonHW::DebugGetShaderIDs(type);487}488}489490std::string GPU_Vulkan::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {491switch (type) {492case SHADER_TYPE_PIPELINE:493return pipelineManager_->DebugGetObjectString(id, type, stringType, shaderManagerVulkan_);494case SHADER_TYPE_SAMPLER:495return textureCacheVulkan_->DebugGetSamplerString(id, stringType);496default:497return GPUCommonHW::DebugGetShaderString(id, type, stringType);498}499}500501502