mirror of
https://github.com/ayozetr/bloodborne_linux.git
synced 2026-11-07 17:02:01 -10:00
+24









deadinside
Qiming-Liu
AndiMajore
adds39939
iannitram
maiconjsv
whp199
DominickCobb-rs
Sarxzer
wheat32
ArenaMMORPG
SrShadowy
Gustavo Bomfim
Teefpyre
SrDeTs
IgorWarzocha
itachi-re
Bainianzzz
hideoneric
guitaripod
unTanana
andrR89
ayozetr
JuanraCM
SaeedShow
msisovic
axpico
Aryxa
Obrikash
noelmiller
AyrtonSanabio
Supermedo
Awaken
Claude Opus 5.5
0fc2921c45
- Windows 10/11: native bb-probe.exe (llvm-mingw / MSYS2 CLANG64), WPF launcher in five languages; online module online\bbnet20.dll with both pools (Seamless Hunt + BBSC, The Hunter's Dream with Discord sign-in) and BBSC's local game server; mods and XML patches from the launcher (junctions and hard links without Developer Mode); DLSS / DLSS FG, FSR 4.1.1 and FSR 4.1.2 driver models, FSR 4 from AMD's SDK 2.4 DLLs; NVAPI power profile on NVIDIA. - The old memory model of 0.3 removed; the game's graphics heaps grow on demand. - Faster: occlusion queries counted only inside the game's open queries (164 -> 189 FPS in the heaviest view), ring dedup and copy-shader filter (less PCIe traffic), PGO retrained, Steam Deck draw preparation, FSR 3.1 FG shaders. - Fixes: cloth fluttering above 30 FPS (#58), point-light shadow wedges, HUD item-icon grid, vertex explosions across heap pieces, FPS drop after a save switch, FG ghosts, crash 0x2169edc (#336), FSR 3.1 protected memory (#85), Wayland HiDPI, launcher translations (#381). - New: AA menu (FXAA2_GS, SMAA), FSR 1, GT-VBAO, AMD Anti-Lag 2, trophies.jsonl, shader recipes before play, live resolution by default. - Packages: Windows zip, AppImage, and a Linux folder (tar.zst) with the online module open. - Removed: the shadp2p online module, LEGO Dimensions toy pad and NGS2 code. - Credits: docs/CONTRIBUTORS_0.7.md (pull requests since 0.4 and outside work). Co-authored-by: Qiming-Liu <[email protected]> Co-authored-by: AndiMajore <[email protected]> Co-authored-by: adds39939 <[email protected]> Co-authored-by: iannitram <[email protected]> Co-authored-by: maiconjsv <[email protected]> Co-authored-by: whp199 <[email protected]> Co-authored-by: DominickCobb-rs <[email protected]> Co-authored-by: Sarxzer <[email protected]> Co-authored-by: wheat32 <[email protected]> Co-authored-by: ArenaMMORPG <[email protected]> Co-authored-by: SrShadowy <[email protected]> Co-authored-by: Gustavo Bomfim <[email protected]> Co-authored-by: Teefpyre <[email protected]> Co-authored-by: SrDeTs <[email protected]> Co-authored-by: IgorWarzocha <[email protected]> Co-authored-by: itachi-re <[email protected]> Co-authored-by: Bainianzzz <[email protected]> Co-authored-by: hideoneric <[email protected]> Co-authored-by: guitaripod <[email protected]> Co-authored-by: unTanana <[email protected]> Co-authored-by: andrR89 <[email protected]> Co-authored-by: ayozetr <[email protected]> Co-authored-by: JuanraCM <[email protected]> Co-authored-by: SaeedShow <[email protected]> Co-authored-by: msisovic <[email protected]> Co-authored-by: axpico <[email protected]> Co-authored-by: Aryxa <[email protected]> Co-authored-by: Obrikash <[email protected]> Co-authored-by: noelmiller <[email protected]> Co-authored-by: AyrtonSanabio <[email protected]> Co-authored-by: Supermedo <[email protected]> Co-authored-by: Awaken <[email protected]> Co-Authored-By: Claude Opus 5.5 <[email protected]>
229 lines
9.8 KiB
C++
229 lines
9.8 KiB
C++
// SPDX-FileCopyrightText: Copyright 2026 bbport contributors
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
// bbport render: program and pipeline selection (see pipeline_cache.h).
|
|
|
|
#include <algorithm>
|
|
#include "shim/bb_env.h"
|
|
#include <atomic>
|
|
#include <bit>
|
|
#include <chrono>
|
|
#include <xxhash.h>
|
|
#include <condition_variable>
|
|
#include <csetjmp>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <deque>
|
|
#include <filesystem>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <fstream>
|
|
#include <thread>
|
|
#include <unordered_set>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <signal.h>
|
|
#include <spawn.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
extern char** environ;
|
|
#endif
|
|
|
|
#include "bbport_sections.h"
|
|
#include "bbport_toggles.h"
|
|
#include "buffers/buffer_cache.h"
|
|
#include "common/hash.h"
|
|
#include "common/scope_exit.h"
|
|
#include "render/compile_log.h"
|
|
#include "common/io_file.h"
|
|
#include "common/path_util.h"
|
|
#include "common/serdes.h"
|
|
#include "common/thread.h"
|
|
#include "core/emulator_settings.h"
|
|
#include "cp/command_processor.h"
|
|
#include "game_profile.h"
|
|
#include "gcn/decoder.h"
|
|
#include "bbport_free_check.h"
|
|
#include "render/gcn_to_vk.h"
|
|
#include "render/blob_store.h"
|
|
#include "render/pipeline_cache.h"
|
|
#include "bbport_settings.h"
|
|
#include "render/pipeline_store.h"
|
|
#include "render/scheduler.h"
|
|
#include "render/shader_capture.h"
|
|
#include "render/vk_draw_prep.h"
|
|
#include "shader/backend/spirv/emit_spirv.h"
|
|
#include "shader/info.h"
|
|
#include "shader/runtime_info.h"
|
|
#include "upscale/motion_history.h"
|
|
#include "vulkan/instance.h"
|
|
#include "vulkan/shader_module.h"
|
|
#include "render/pipeline_cache_internal.h"
|
|
|
|
namespace Vulkan {
|
|
|
|
// Shader/pipeline compile time on the GPU thread, reported by BB_FRAME_STATS.
|
|
std::atomic<u64> g_bb_compile_ns;
|
|
std::atomic<u32> g_bb_compiles;
|
|
/// For the overlay's "Compiling shaders" line: pipelines made in the background (the cache's
|
|
/// preload and imported recipes), queued and done, and whether recipes are being imported.
|
|
std::atomic<u32> g_bb_background_pipelines_total, g_bb_background_pipelines_done;
|
|
/// Every compile so far (g_bb_compiles is taken by the frame statistics every few seconds).
|
|
std::atomic<u32> g_bb_compiles_total;
|
|
std::atomic<bool> g_bb_shader_import_active;
|
|
|
|
namespace {
|
|
|
|
constexpr u32 SpirvVersion1_6 = 0x00010600U;
|
|
|
|
constexpr std::array DescriptorHeapSizes = {
|
|
vk::DescriptorPoolSize{vk::DescriptorType::eUniformBuffer, 512},
|
|
vk::DescriptorPoolSize{vk::DescriptorType::eStorageBuffer, 8192},
|
|
vk::DescriptorPoolSize{vk::DescriptorType::eSampledImage, 8192},
|
|
vk::DescriptorPoolSize{vk::DescriptorType::eStorageImage, 1024},
|
|
vk::DescriptorPoolSize{vk::DescriptorType::eSampler, 1024},
|
|
};
|
|
|
|
Shader::Profile MakeProfile(const Instance& instance, u32 sparse_page_shift) {
|
|
const auto& vk12 = instance.GetVk12Properties();
|
|
const auto driver = instance.GetDriverID();
|
|
const bool nvidia = driver == vk::DriverId::eNvidiaProprietary;
|
|
return Shader::Profile{
|
|
.max_viewport_width = instance.GetMaxViewportWidth(),
|
|
.max_viewport_height = instance.GetMaxViewportHeight(),
|
|
.max_shared_memory_size = instance.MaxComputeSharedMemorySize(),
|
|
.supported_spirv = SpirvVersion1_6,
|
|
.subgroup_size = instance.SubgroupSize(),
|
|
.sparse_page_shift = sparse_page_shift,
|
|
.support_int8 = instance.IsShaderInt8Supported(),
|
|
.support_int16 = instance.IsShaderInt16Supported(),
|
|
.support_int64 = instance.IsShaderInt64Supported(),
|
|
.support_float16 = instance.IsShaderFloat16Supported(),
|
|
.support_float64 = instance.IsShaderFloat64Supported(),
|
|
.supports_denorm_behavior_independence =
|
|
vk12.denormBehaviorIndependence != vk::ShaderFloatControlsIndependence::eNone,
|
|
.supports_rounding_mode_independence =
|
|
vk12.roundingModeIndependence != vk::ShaderFloatControlsIndependence::eNone,
|
|
.support_fp16_denorm_preserve = bool(vk12.shaderDenormPreserveFloat16),
|
|
.support_fp16_denorm_flush = bool(vk12.shaderDenormFlushToZeroFloat16),
|
|
.support_fp16_round_to_zero = bool(vk12.shaderRoundingModeRTZFloat16),
|
|
.support_fp32_denorm_preserve = bool(vk12.shaderDenormPreserveFloat32),
|
|
.support_fp32_denorm_flush = bool(vk12.shaderDenormFlushToZeroFloat32),
|
|
.support_fp32_round_to_zero = bool(vk12.shaderRoundingModeRTZFloat32),
|
|
.support_fp64_denorm_preserve = bool(vk12.shaderDenormPreserveFloat64),
|
|
.support_fp64_denorm_flush = bool(vk12.shaderDenormFlushToZeroFloat64),
|
|
.support_fp64_round_to_zero = bool(vk12.shaderRoundingModeRTZFloat64),
|
|
.support_fp16_signed_zero_inf_nan_preserve =
|
|
bool(vk12.shaderSignedZeroInfNanPreserveFloat16),
|
|
.support_fp32_signed_zero_inf_nan_preserve =
|
|
bool(vk12.shaderSignedZeroInfNanPreserveFloat32),
|
|
.support_fp64_signed_zero_inf_nan_preserve =
|
|
bool(vk12.shaderSignedZeroInfNanPreserveFloat64),
|
|
.supports_image_load_store_lod = instance.IsImageLoadStoreLodSupported(),
|
|
.supports_native_cube_calc = instance.IsAmdGcnShaderSupported(),
|
|
.supports_trinary_minmax = instance.IsAmdShaderTrinaryMinMaxSupported(),
|
|
.supports_buffer_fp32_atomic_min_max =
|
|
instance.IsShaderAtomicFloatBuffer32MinMaxSupported(),
|
|
.supports_image_fp32_atomic_min_max = instance.IsShaderAtomicFloatImage32MinMaxSupported(),
|
|
.supports_buffer_int64_atomics = instance.IsBufferInt64AtomicsSupported(),
|
|
.supports_shared_int64_atomics = instance.IsSharedInt64AtomicsSupported(),
|
|
.supports_workgroup_explicit_memory_layout =
|
|
instance.IsWorkgroupMemoryExplicitLayoutSupported(),
|
|
.supports_amd_shader_explicit_vertex_parameter =
|
|
instance.IsAmdShaderExplicitVertexParameterSupported(),
|
|
.supports_fragment_shader_barycentric = instance.IsFragmentShaderBarycentricSupported(),
|
|
.supports_shader_subgroup_clock = instance.IsShaderSubgroupClockSupported(),
|
|
.needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && nvidia,
|
|
// Older NVIDIA (Pascal) has no barycentrics; BB_INTERP_INT_FIX=0/1 overrides.
|
|
.needs_integer_interpolation_fix = [&] {
|
|
if (const char* env = bb_env("BB_INTERP_INT_FIX")) {
|
|
return env[0] == '1';
|
|
}
|
|
return !instance.IsFragmentShaderBarycentricSupported() && nvidia;
|
|
}(),
|
|
.needs_lds_barriers = nvidia || driver == vk::DriverId::eMesaKosmickrisp,
|
|
.needs_buffer_offsets = instance.StorageMinAlignment() > 4,
|
|
.needs_unorm_fixup = driver == vk::DriverId::eMesaKosmickrisp,
|
|
.needs_clip_distance_emulation = nvidia,
|
|
.supports_shader_stencil_export = instance.IsShaderStencilExportSupported(),
|
|
// BB_LAYER_MEMORY: buffers over nearly all memory go through the page table.
|
|
.paged_buffers = VideoCore::BufferCache::LayerPagedActive(),
|
|
// The game's buffer copy shader, from its profile (games/).
|
|
.buffer_copy_shader_hash = (Shader::paged_crossing_exclude =
|
|
bb_env_off("BB_PAGED_COPY_CROSSING")
|
|
? Game::BufferCopyShader()
|
|
: 0,
|
|
Game::BufferCopyShader()),
|
|
};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
|
|
BbCp::CommandProcessor* liverpool_, u32 sparse_page_shift)
|
|
: instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_},
|
|
desc_heap{instance, scheduler.GetWorkSemaphore(), DescriptorHeapSizes},
|
|
profile{MakeProfile(instance_, sparse_page_shift)} {
|
|
sel.regs = &liverpool->regs;
|
|
auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({});
|
|
ASSERT_MSG(cache_result == vk::Result::eSuccess, "Failed to create pipeline cache: {}",
|
|
vk::to_string(cache_result));
|
|
pipeline_cache = std::move(cache);
|
|
}
|
|
|
|
PipelineCache::~PipelineCache() {
|
|
if (preload_compiler) {
|
|
preload_compiler->Discard(); // a closing game does not wait for the rest
|
|
}
|
|
}
|
|
|
|
bool PipelineCache::PrepareGraphicsPipeline(PipelineSelection& worker_sel) {
|
|
// Tessellation stages read constant buffers from memory at selection time: not prepared.
|
|
if (worker_sel.regs->stage_enable.hs_en) {
|
|
return false;
|
|
}
|
|
return RefreshGraphicsKey(worker_sel) && !worker_sel.worker->failed;
|
|
}
|
|
|
|
PipelineCompiler& PipelineCache::Compiler() {
|
|
if (!compiler) {
|
|
const char* env = bb_env("BB_ASYNC_PIPELINE_THREADS");
|
|
const u32 threads =
|
|
std::max(env && *env ? u32(std::strtoul(env, nullptr, 10))
|
|
: std::clamp(std::thread::hardware_concurrency() / 4, 1u, 4u),
|
|
1u);
|
|
compiler = std::make_unique<PipelineCompiler>(threads);
|
|
std::printf("GPU: new pipelines of passes drawn every frame compile on %u threads "
|
|
"(BB_ASYNC_PIPELINES=0: on the GPU thread, the game waits)\n",
|
|
threads);
|
|
}
|
|
return *compiler;
|
|
}
|
|
|
|
std::string PipelineCache::GetShaderName(HwStage stage, u64 hash, std::optional<size_t> perm) {
|
|
if (perm) {
|
|
return fmt::format("{}_{:#018x}_{}", stage, hash, *perm);
|
|
}
|
|
return fmt::format("{}_{:#018x}", stage, hash);
|
|
}
|
|
|
|
void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, HwStage stage,
|
|
size_t perm_idx, std::string_view ext) {
|
|
if (!EmulatorSettings.IsDumpShaders()) {
|
|
return;
|
|
}
|
|
using namespace Common::FS;
|
|
const auto dump_dir = GetUserPath(PathType::ShaderDir) / "dumps";
|
|
if (!std::filesystem::exists(dump_dir)) {
|
|
std::filesystem::create_directories(dump_dir);
|
|
}
|
|
const auto filename = fmt::format("{}.{}", GetShaderName(stage, hash, perm_idx), ext);
|
|
const auto file = IOFile{dump_dir / filename, FileAccessMode::Create};
|
|
file.WriteSpan(code);
|
|
}
|
|
|
|
} // namespace Vulkan
|