mirror of
https://github.com/Liondarkz/issn_pc.git
synced 2026-11-07 04:36:01 -10:00
Steam Deck and weak GPUs - Outputs other than 1080p render at the preset size again (startup patch, as in 0.1); live resolution changes are now a setting (live_resolution=0|1|auto, off by default). - Texture cache on integrated GPUs: thresholds follow the driver's current memory budget. The old budget (~1 GB on the Deck) kept the collector in its aggressive mode, evicting textures used 2-3 frames ago on every submission. - Evicted GPU-written images are written back synchronously, while their pages are still protected (a deferred write could land over memory the game had reused). - run.sh uses bash builtins only (the AppImage has no sed: the game did not start on NixOS). Rendering - Live scaling also reduces the game's half-resolution post-processing (960x540, including the bloom mip chain, one proxy per level). - TAA: history of thin surfaces missed by a jitter phase is kept when nothing moves (railings, window bars: static-camera flicker p99.9 -45%). - Scene texture LOD bias log2(render/output) in G-buffer passes while upscaling. - Present on its own thread and at most one frame of GPU run-ahead (even frame pacing). - Pipeline cache rebuilt instead of disabled when the shader profile changes. - GPU idle only after the draw pipeline and deferred fences are done (work_retired). - Recompiler fixes backported from shadPS4; integer interpolation fix for pre-Turing NVIDIA. - Sharpness up to 2.0 (an extra RCAS pass); native-resolution TAA mode. Launcher, mods, patches - English launcher (follows the system language), loose-file mods with load order, third-party shadPS4 XML patches, 60/90 FPS patches with pointer slots fixed. - AppImage: host NVIDIA Vulkan driver support, MangoHud, no FSR 4.1.1 models (BB_PACKAGE_FSR411=1 bundles local ones). Tools: route recording/replay (F9), consecutive frame dumps, scene scaling diagnostics. Details: docs/CHANGES_2026-10-02.md, docs/CHANGES_2026-10-03.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
136 lines
7.9 KiB
C++
136 lines
7.9 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
// Execute production camera motion SPIR-V: distant geometry, sky and stale object vectors.
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include "video_core/renderer_vulkan/vk_instance.h"
|
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
|
#include "video_core/texture_cache/image.h"
|
|
#include "video_core/host_shaders/camera_motion_comp.h"
|
|
#include <vk_mem_alloc.h>
|
|
|
|
int main() {
|
|
Vulkan::Instance instance(0, false);
|
|
static vk::detail::DynamicLoader loader;
|
|
vk::detail::DispatchLoaderDynamic d;
|
|
d.init(loader.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr"));
|
|
d.init(instance.GetInstance()); d.init(instance.GetDevice());
|
|
const auto device = instance.GetDevice();
|
|
Vulkan::Scheduler scheduler(instance);
|
|
constexpr u32 W=9,H=7,N=W*H;
|
|
std::array<VideoCore::UniqueImage,3> images;
|
|
std::array<vk::UniqueImageView,3> views;
|
|
std::array<vk::DescriptorSetLayoutBinding,3> bindings{};
|
|
for (u32 i=0;i<3;++i) {
|
|
const auto format=i==1 ? vk::Format::eR16G16Sfloat : vk::Format::eR32G32B32A32Sfloat;
|
|
images[i]=VideoCore::UniqueImage(device,instance.GetAllocator());
|
|
images[i].Create({.imageType=vk::ImageType::e2D,.format=format,.extent={W,H,1},
|
|
.mipLevels=1,.arrayLayers=1,.samples=vk::SampleCountFlagBits::e1,
|
|
.tiling=vk::ImageTiling::eOptimal,.usage=vk::ImageUsageFlagBits::eSampled |
|
|
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc |
|
|
vk::ImageUsageFlagBits::eTransferDst});
|
|
views[i]=device.createImageViewUnique({.image=vk::Image(images[i]),
|
|
.viewType=vk::ImageViewType::e2D,.format=format,
|
|
.subresourceRange={vk::ImageAspectFlagBits::eColor,0,1,0,1}},nullptr,d).value;
|
|
bindings[i]={.binding=i,.descriptorType=i==1 ? vk::DescriptorType::eStorageImage :
|
|
vk::DescriptorType::eSampledImage,.descriptorCount=1,
|
|
.stageFlags=vk::ShaderStageFlagBits::eCompute};
|
|
}
|
|
auto descriptors=device.createDescriptorSetLayoutUnique({
|
|
.flags=vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR,
|
|
.bindingCount=3,.pBindings=bindings.data()},nullptr,d).value;
|
|
struct Params {
|
|
std::array<float,12> reproject;
|
|
std::array<float,4> proj,prev_proj;
|
|
std::array<float,2> size,jitter,previous_jitter;
|
|
u32 mode;
|
|
};
|
|
const vk::PushConstantRange push{vk::ShaderStageFlagBits::eCompute,0,sizeof(Params)};
|
|
auto layout=device.createPipelineLayoutUnique({.setLayoutCount=1,.pSetLayouts=&*descriptors,
|
|
.pushConstantRangeCount=1,.pPushConstantRanges=&push},nullptr,d).value;
|
|
auto module=device.createShaderModuleUnique({.codeSize=sizeof(CAMERA_MOTION_COMP),
|
|
.pCode=CAMERA_MOTION_COMP},nullptr,d).value;
|
|
auto pipeline=device.createComputePipelineUnique({}, {
|
|
.stage={.stage=vk::ShaderStageFlagBits::eCompute,.module=*module,.pName="main"},
|
|
.layout=*layout},nullptr,d).value;
|
|
VkBuffer staging{}; VmaAllocation allocation{}; VmaAllocationInfo ai{};
|
|
const VkBufferCreateInfo bi{.sType=VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
.size=N*16*2,.usage=VK_BUFFER_USAGE_TRANSFER_SRC_BIT|VK_BUFFER_USAGE_TRANSFER_DST_BIT};
|
|
const VmaAllocationCreateInfo ac{.flags=VMA_ALLOCATION_CREATE_MAPPED_BIT |
|
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,.usage=VMA_MEMORY_USAGE_AUTO,
|
|
.requiredFlags=VK_MEMORY_PROPERTY_HOST_COHERENT_BIT};
|
|
assert(vmaCreateBuffer(instance.GetAllocator(),&bi,&ac,&staging,&allocation,&ai)==VK_SUCCESS);
|
|
const auto barrier=[&](vk::CommandBuffer cmd,vk::PipelineStageFlags2 src,
|
|
vk::AccessFlags2 src_access,vk::PipelineStageFlags2 dst,
|
|
vk::AccessFlags2 dst_access) {
|
|
const vk::MemoryBarrier2 b{.srcStageMask=src,.srcAccessMask=src_access,
|
|
.dstStageMask=dst,.dstAccessMask=dst_access};
|
|
cmd.pipelineBarrier2({.memoryBarrierCount=1,.pMemoryBarriers=&b},d);
|
|
};
|
|
const auto run=[&](const char* label,float depth,float translation,float rotation,
|
|
float object_depth,u32 mode,float expected,bool jitter=false) {
|
|
auto* pixels=static_cast<float*>(ai.pMappedData);
|
|
for (u32 p=0;p<N;++p) {
|
|
pixels[p*4]=depth;
|
|
pixels[N*4+p*4]=2.f; pixels[N*4+p*4+1]=0.f;
|
|
pixels[N*4+p*4+2]=1.f; pixels[N*4+p*4+3]=object_depth;
|
|
}
|
|
const auto cmd=scheduler.CommandBuffer();
|
|
std::array<vk::ImageMemoryBarrier2,3> transitions{};
|
|
for (u32 i=0;i<3;++i) transitions[i]={
|
|
.srcStageMask=vk::PipelineStageFlagBits2::eAllCommands,
|
|
.dstStageMask=vk::PipelineStageFlagBits2::eAllCommands,
|
|
.dstAccessMask=vk::AccessFlagBits2::eMemoryRead|vk::AccessFlagBits2::eMemoryWrite,
|
|
.oldLayout=vk::ImageLayout::eUndefined,.newLayout=vk::ImageLayout::eGeneral,
|
|
.image=vk::Image(images[i]),.subresourceRange={vk::ImageAspectFlagBits::eColor,0,1,0,1}};
|
|
cmd.pipelineBarrier2({.imageMemoryBarrierCount=3,.pImageMemoryBarriers=transitions.data()},d);
|
|
for (u32 i : {0u,2u}) {
|
|
const vk::BufferImageCopy region{.bufferOffset=i/2*N*16,
|
|
.imageSubresource={vk::ImageAspectFlagBits::eColor,0,0,1},.imageExtent={W,H,1}};
|
|
cmd.copyBufferToImage(staging,vk::Image(images[i]),vk::ImageLayout::eGeneral,region,d);
|
|
}
|
|
barrier(cmd,vk::PipelineStageFlagBits2::eTransfer,vk::AccessFlagBits2::eTransferWrite,
|
|
vk::PipelineStageFlagBits2::eComputeShader,vk::AccessFlagBits2::eShaderRead);
|
|
std::array<vk::DescriptorImageInfo,3> infos{};
|
|
std::array<vk::WriteDescriptorSet,3> writes{};
|
|
for (u32 i=0;i<3;++i) {
|
|
infos[i]={.imageView=*views[i],.imageLayout=vk::ImageLayout::eGeneral};
|
|
writes[i]={.dstBinding=i,.descriptorCount=1,.descriptorType=bindings[i].descriptorType,
|
|
.pImageInfo=&infos[i]};
|
|
}
|
|
Params params{{1,0,rotation,translation,0,1,0,0,0,0,1,0},
|
|
{1,1,1,-0.05f},{1,1,1,-0.05f},{W,H},
|
|
{jitter ? 0.37f : 0.f,jitter ? -0.29f : 0.f},{0,0},mode};
|
|
cmd.bindPipeline(vk::PipelineBindPoint::eCompute,*pipeline,d);
|
|
cmd.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute,*layout,0,writes,d);
|
|
cmd.pushConstants(*layout,vk::ShaderStageFlagBits::eCompute,0,sizeof(params),¶ms,d);
|
|
cmd.dispatch((W+7)/8,(H+7)/8,1,d);
|
|
barrier(cmd,vk::PipelineStageFlagBits2::eComputeShader,vk::AccessFlagBits2::eShaderWrite,
|
|
vk::PipelineStageFlagBits2::eTransfer,vk::AccessFlagBits2::eTransferRead);
|
|
const vk::BufferImageCopy region{.imageSubresource={vk::ImageAspectFlagBits::eColor,0,0,1},
|
|
.imageOffset={4,3,0},.imageExtent={1,1,1}};
|
|
cmd.copyImageToBuffer(vk::Image(images[1]),vk::ImageLayout::eGeneral,staging,region,d);
|
|
barrier(cmd,vk::PipelineStageFlagBits2::eTransfer,vk::AccessFlagBits2::eTransferWrite,
|
|
vk::PipelineStageFlagBits2::eHost,vk::AccessFlagBits2::eHostRead);
|
|
scheduler.Finish();
|
|
const auto* result=static_cast<u16*>(ai.pMappedData);
|
|
const auto half=[](u16 v) {
|
|
const int e=(v>>10)&31;
|
|
return (v&0x8000 ? -1.f : 1.f)*(e==0 ? std::ldexp(float(v&1023),-24) :
|
|
std::ldexp(1.f+float(v&1023)/1024.f,e-15));
|
|
};
|
|
assert(std::abs(half(result[0])-expected)<0.002f);
|
|
assert(std::abs(half(result[1]))<0.001f);
|
|
std::printf("Camera motion: %s PASS (%.5f)\n",label,half(result[0]));
|
|
};
|
|
constexpr float distant=0.999995f;
|
|
run("distant geometry moves",distant,1000,0,0,0,4.5f*1000*(1-distant)/0.05f);
|
|
run("sky rotates",1,0,0.1f,0,0,0.45f);
|
|
run("sky ignores translation",1,1000,0,0,0,0);
|
|
run("static camera cancels jitter",distant,0,0,0,0,0,true);
|
|
run("matching object vector",distant,0,0,distant,1,2);
|
|
run("stale distant object vector",distant,0,0,0.9999f,1,0);
|
|
vmaDestroyBuffer(instance.GetAllocator(),staging,allocation);
|
|
}
|