diff --git a/src/common/binary_helper.h b/src/common/binary_helper.h index 564896f11..de1bb9b78 100644 --- a/src/common/binary_helper.h +++ b/src/common/binary_helper.h @@ -15,33 +15,4 @@ void writeBin(std::ostream& os, const T& v) { template void readBin(std::istream& is, T& v) { is.read(reinterpret_cast(&v), sizeof(T)); -} - -// Spezialfall für Arrays/Blöcke -template -void writeBlock(std::ostream& os, const T* data, size_t count) { - os.write(reinterpret_cast(data), sizeof(T) * count); -} - -template -void readBlock(std::istream& is, T* data, size_t count) { - is.read(reinterpret_cast(data), sizeof(T) * count); -} - -// Spezialfall für Container -template -void writeContainer(std::ostream& os, const std::vector& v) { - u32 n = static_cast(v.size()); - writeBin(os, n); - if (n) - writeBlock(os, v.data(), n); -} - -template -void readContainer(std::istream& is, std::vector& v) { - u32 n; - readBin(is, n); - v.resize(n); - if (n) - readBlock(is, v.data(), n); -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/common/hash.h b/src/common/hash.h index d5cacedd7..b29cfb90f 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -5,10 +5,7 @@ #include "common/types.h" -[[nodiscard]] inline u64 HashCombine(const u64 seed, const u64 hash) { - return seed ^ (hash + 0x9e3779b9 + (seed << 12) + (seed >> 4)); -} - -[[nodiscard]] inline u32 HashCombine(const u32 seed, const u32 hash) { - return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2)); +template +T HashCombine(const T& seed, const U& value) { + return seed ^ (static_cast(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); } \ No newline at end of file diff --git a/src/video_core/renderer_vulkan/shader_cache.cpp b/src/video_core/renderer_vulkan/shader_cache.cpp index 4097b140a..b256d2cda 100644 --- a/src/video_core/renderer_vulkan/shader_cache.cpp +++ b/src/video_core/renderer_vulkan/shader_cache.cpp @@ -1,12 +1,14 @@ #include #include -#include #include +#include "common/hash.h" #include "common/path_util.h" #include "common/io_file.h" #include "common/binary_helper.h" #include "common/logging/log.h" +#include "shader_recompiler/ir/type.h" #include "shader_recompiler/info.h" +#include "shader_recompiler/specialization.h" using u64 = uint64_t; using u32 = uint32_t; @@ -15,13 +17,242 @@ namespace ShaderCache { const auto shader_cache_dir = Common::FS::GetUserPath(Common::FS::PathType::ShaderDir) / "cache"; -std::string CreateShaderID(u64 pgm_hash, size_t perm_idx, std::ostream& info_serialized) { - std::ostringstream data_stream; - data_stream << pgm_hash << perm_idx; - data_stream << info_serialized.rdbuf(); - std::hash hasher; - size_t shader_id = hasher(data_stream.str()); - return std::to_string(shader_id); +u64 CalculateSpecializationHash(const Shader::StageSpecialization& spec) { + u64 hash = 0; + + // Start mit dem Hash der RuntimeInfo + // Die RuntimeInfo enthält verschiedene Unions, daher müssen wir basierend auf dem Stage-Typ + // hashen + const auto& runtime_info = spec.runtime_info; + hash = HashCombine(hash, static_cast(runtime_info.stage)); + hash = HashCombine(hash, runtime_info.num_user_data); + hash = HashCombine(hash, runtime_info.num_input_vgprs); + hash = HashCombine(hash, runtime_info.num_allocated_vgprs); + hash = HashCombine(hash, static_cast(runtime_info.fp_denorm_mode32)); + hash = HashCombine(hash, static_cast(runtime_info.fp_round_mode32)); + + // Abhängig vom Stage-Typ die spezifischen RuntimeInfo-Felder hashen + switch (runtime_info.stage) { + case Shader::Stage::Local: + hash = HashCombine(hash, runtime_info.ls_info.ls_stride); + hash = HashCombine(hash, runtime_info.ls_info.links_with_tcs); + break; + case Shader::Stage::Export: + hash = HashCombine(hash, runtime_info.es_info.vertex_data_size); + break; + case Shader::Stage::Vertex: + hash = HashCombine(hash, runtime_info.vs_info.num_outputs); + // Hash der Output-Maps + for (size_t i = 0; + i < runtime_info.vs_info.num_outputs && i < runtime_info.vs_info.outputs.size(); ++i) { + const auto& output_map = runtime_info.vs_info.outputs[i]; + for (const auto& output : output_map) { + hash = HashCombine(hash, static_cast(output)); + } + } + hash = HashCombine(hash, runtime_info.vs_info.emulate_depth_negative_one_to_one); + hash = HashCombine(hash, runtime_info.vs_info.clip_disable); + hash = HashCombine(hash, static_cast(runtime_info.vs_info.tess_type)); + hash = HashCombine(hash, static_cast(runtime_info.vs_info.tess_topology)); + hash = HashCombine(hash, static_cast(runtime_info.vs_info.tess_partitioning)); + hash = HashCombine(hash, runtime_info.vs_info.hs_output_cp_stride); + break; + case Shader::Stage::Hull: + hash = HashCombine(hash, runtime_info.hs_info.num_input_control_points); + hash = HashCombine(hash, runtime_info.hs_info.num_threads); + hash = HashCombine(hash, static_cast(runtime_info.hs_info.tess_type)); + hash = HashCombine(hash, runtime_info.hs_info.ls_stride); + hash = HashCombine(hash, runtime_info.hs_info.hs_output_cp_stride); + hash = HashCombine(hash, runtime_info.hs_info.hs_output_base); + break; + case Shader::Stage::Geometry: + hash = HashCombine(hash, runtime_info.gs_info.num_invocations); + hash = HashCombine(hash, runtime_info.gs_info.output_vertices); + hash = HashCombine(hash, runtime_info.gs_info.in_vertex_data_size); + hash = HashCombine(hash, runtime_info.gs_info.out_vertex_data_size); + hash = HashCombine(hash, static_cast(runtime_info.gs_info.in_primitive)); + // Hash der Output-Primitive-Types für alle Streams + for (const auto& out_prim : runtime_info.gs_info.out_primitive) { + hash = HashCombine(hash, static_cast(out_prim)); + } + hash = HashCombine(hash, runtime_info.gs_info.vs_copy_hash); + break; + case Shader::Stage::Fragment: + hash = HashCombine(hash, runtime_info.fs_info.en_flags.raw); + hash = HashCombine(hash, runtime_info.fs_info.addr_flags.raw); + hash = HashCombine(hash, runtime_info.fs_info.num_inputs); + + // Hash der PS-Inputs + for (u32 i = 0; + i < runtime_info.fs_info.num_inputs && i < runtime_info.fs_info.inputs.size(); ++i) { + const auto& input = runtime_info.fs_info.inputs[i]; + hash = HashCombine(hash, input.param_index); + hash = HashCombine(hash, input.is_default); + hash = HashCombine(hash, input.is_flat); + hash = HashCombine(hash, input.default_value); + } + + // Hash der ColorBuffers + for (const auto& color_buffer : runtime_info.fs_info.color_buffers) { + hash = HashCombine(hash, static_cast(color_buffer.num_format)); + hash = HashCombine(hash, static_cast(color_buffer.num_conversion)); + hash = HashCombine(hash, static_cast(color_buffer.export_format)); + hash = HashCombine(hash, color_buffer.needs_unorm_fixup); + hash = HashCombine(hash, color_buffer.swizzle.r); + hash = HashCombine(hash, color_buffer.swizzle.g); + hash = HashCombine(hash, color_buffer.swizzle.b); + hash = HashCombine(hash, color_buffer.swizzle.a); + } + break; + case Shader::Stage::Compute: + hash = HashCombine(hash, runtime_info.cs_info.shared_memory_size); + for (u32 i = 0; i < 3; ++i) { + hash = HashCombine(hash, runtime_info.cs_info.workgroup_size[i]); + hash = HashCombine(hash, runtime_info.cs_info.tgid_enable[i]); + } + break; + } + + // Hash des FetchShader-Daten + if (spec.fetch_shader_data) { + const auto& fetch_shader = *spec.fetch_shader_data; + hash = HashCombine(hash, fetch_shader.size); + hash = HashCombine(hash, static_cast(fetch_shader.vertex_offset_sgpr)); + hash = HashCombine(hash, static_cast(fetch_shader.instance_offset_sgpr)); + + // Hash der Attribute + for (const auto& attr : fetch_shader.attributes) { + hash = HashCombine(hash, static_cast(attr.semantic)); + hash = HashCombine(hash, static_cast(attr.dest_vgpr)); + hash = HashCombine(hash, static_cast(attr.num_elements)); + hash = HashCombine(hash, static_cast(attr.sgpr_base)); + hash = HashCombine(hash, static_cast(attr.dword_offset)); + hash = HashCombine(hash, static_cast(attr.instance_data)); + } + } + + // Hash der VS-Attribut-Spezialisierungen + for (const auto& vs_attrib : spec.vs_attribs) { + hash = HashCombine(hash, vs_attrib.num_components); + hash = HashCombine(hash, static_cast(vs_attrib.num_class)); + hash = HashCombine(hash, vs_attrib.dst_select.r); + hash = HashCombine(hash, vs_attrib.dst_select.g); + hash = HashCombine(hash, vs_attrib.dst_select.b); + hash = HashCombine(hash, vs_attrib.dst_select.a); + } + + // Hash des Bitsets + const std::string bitset_str = spec.bitset.to_string(); + for (size_t i = 0; i < bitset_str.size(); i += 8) { + size_t end = std::min(i + 8, bitset_str.size()); + std::string chunk = bitset_str.substr(i, end - i); + u8 value = 0; + for (size_t j = 0; j < chunk.size(); ++j) { + if (chunk[j] == '1') { + value |= (1 << j); + } + } + hash = HashCombine(hash, value); + } + + // Hash der Buffer-Spezialisierungen + for (const auto& buffer : spec.buffers) { + hash = HashCombine(hash, buffer.stride); + hash = HashCombine(hash, buffer.is_storage); + hash = HashCombine(hash, buffer.is_formatted); + hash = HashCombine(hash, buffer.swizzle_enable); + + if (buffer.is_formatted) { + hash = HashCombine(hash, buffer.data_format); + hash = HashCombine(hash, buffer.num_format); + hash = HashCombine(hash, buffer.dst_select.r); + hash = HashCombine(hash, buffer.dst_select.g); + hash = HashCombine(hash, buffer.dst_select.b); + hash = HashCombine(hash, buffer.dst_select.a); + hash = HashCombine(hash, static_cast(buffer.num_conversion)); + } + + if (buffer.swizzle_enable) { + hash = HashCombine(hash, buffer.index_stride); + hash = HashCombine(hash, buffer.element_size); + } + } + + // Hash der Bild-Spezialisierungen + for (const auto& image : spec.images) { + hash = HashCombine(hash, static_cast(image.type)); + hash = HashCombine(hash, image.is_integer); + hash = HashCombine(hash, image.is_storage); + hash = HashCombine(hash, image.is_cube); + + if (image.is_storage) { + hash = HashCombine(hash, image.dst_select.r); + hash = HashCombine(hash, image.dst_select.g); + hash = HashCombine(hash, image.dst_select.b); + hash = HashCombine(hash, image.dst_select.a); + } + + hash = HashCombine(hash, static_cast(image.num_conversion)); + } + + // Hash der FMask-Spezialisierungen + for (const auto& fmask : spec.fmasks) { + hash = HashCombine(hash, fmask.width); + hash = HashCombine(hash, fmask.height); + } + + // Hash der Sampler-Spezialisierungen + for (const auto& sampler : spec.samplers) { + hash = HashCombine(hash, sampler.force_unnormalized); + } + + // Hash der Start-Bindings + hash = HashCombine(hash, spec.start.buffer); + hash = HashCombine(hash, spec.start.unified); + hash = HashCombine(hash, spec.start.user_data); + + // Hash vom info pointer + if (spec.info) { + hash = HashCombine(hash, spec.info->pgm_hash); + hash = HashCombine(hash, static_cast(spec.info->stage)); + hash = HashCombine(hash, static_cast(spec.info->l_stage)); + hash = HashCombine(hash, spec.info->has_storage_images); + hash = HashCombine(hash, spec.info->has_discard); + hash = HashCombine(hash, spec.info->has_image_gather); + hash = HashCombine(hash, spec.info->has_image_query); + hash = HashCombine(hash, spec.info->uses_lane_id); + hash = HashCombine(hash, spec.info->uses_group_quad); + hash = HashCombine(hash, spec.info->uses_group_ballot); + hash = HashCombine(hash, spec.info->uses_shared); + hash = HashCombine(hash, spec.info->uses_fp16); + hash = HashCombine(hash, spec.info->uses_fp64); + hash = HashCombine(hash, spec.info->uses_pack_10_11_11); + hash = HashCombine(hash, spec.info->uses_unpack_10_11_11); + hash = HashCombine(hash, spec.info->stores_tess_level_outer); + hash = HashCombine(hash, spec.info->stores_tess_level_inner); + hash = HashCombine(hash, spec.info->translation_failed); + hash = HashCombine(hash, spec.info->has_readconst); + hash = HashCombine(hash, spec.info->mrt_mask); + hash = HashCombine(hash, spec.info->has_fetch_shader); + hash = HashCombine(hash, spec.info->fetch_shader_sgpr_base); + + // Hash der Flags für loads und stores + for (size_t i = 0; i < spec.info->loads.flags.size(); ++i) { + hash = HashCombine(hash, spec.info->loads.flags[i]); + } + + for (size_t i = 0; i < spec.info->stores.flags.size(); ++i) { + hash = HashCombine(hash, spec.info->stores.flags[i]); + } + + // Hash der UserDataMask + hash = HashCombine(hash, spec.info->ud_mask.mask); + + // Hash der uses_patches + hash = HashCombine(hash, spec.info->uses_patches); + } + + return hash; } void SerializeInfo(std::ostream& info_serialized, Shader::Info info) { @@ -29,9 +260,8 @@ void SerializeInfo(std::ostream& info_serialized, Shader::Info info) { writeBin(info_serialized, info.ud_mask.mask); // Buffer-Resources - u32 count = static_cast(info.buffers.size()); - writeBin(info_serialized, count); // Buffer Amount - LOG_INFO(Render_Recompiler, "ShaderCache: Buffer count: {}", info.buffers.size()); + u32 bufferCount = static_cast(info.buffers.size()); + writeBin(info_serialized, bufferCount); // Buffer Amount for (const auto& buffer : info.buffers) { writeBin(info_serialized, buffer.sharp_idx); @@ -43,9 +273,9 @@ void SerializeInfo(std::ostream& info_serialized, Shader::Info info) { } // Image-Resources - count = static_cast(info.images.size()); - writeBin(info_serialized, count); // Image Amount - LOG_INFO(Render_Recompiler, "ShaderCache: Image count: {}", info.images.size()); + u32 imageCount = static_cast(info.images.size()); + writeBin(info_serialized, imageCount); // Image Amount + for (const auto& image : info.images) { writeBin(info_serialized, image.sharp_idx); writeBin(info_serialized, static_cast(image.is_depth ? 1 : 0)); @@ -55,35 +285,36 @@ void SerializeInfo(std::ostream& info_serialized, Shader::Info info) { } // Sampler-Resources - count = static_cast(info.samplers.size()); - writeBin(info_serialized, count); // Sampler Amount - LOG_INFO(Render_Recompiler, "ShaderCache: Sampler count: {}", info.samplers.size()); + u32 samplerCount = static_cast(info.samplers.size()); + writeBin(info_serialized, samplerCount); // Sampler Amount + for (const auto& sampler : info.samplers) { writeBin(info_serialized, sampler.sharp_idx); } // FMask-Resources - count = static_cast(info.fmasks.size()); - writeBin(info_serialized, count); // FMask Amount - LOG_INFO(Render_Recompiler, "ShaderCache: FMask count: {}", info.fmasks.size()); + u32 fmaskCount = static_cast(info.fmasks.size()); + writeBin(info_serialized, fmaskCount); // FMask Amount + for (const auto& fmask : info.fmasks) { writeBin(info_serialized, fmask.sharp_idx); } // GS Copy Data - writeBin(info_serialized, info.gs_copy_data.num_attrs); - u32 mapCount = static_cast(info.gs_copy_data.attr_map.size()); writeBin(info_serialized, mapCount); - for (auto const& [loc, idx] : info.gs_copy_data.attr_map) { + for (auto const& [loc, attr_pair] : info.gs_copy_data.attr_map) { writeBin(info_serialized, loc); - writeBin(info_serialized, idx); + // Das erste Element des Paars ist ein Shader::IR::Attribute, ein Enum + writeBin(info_serialized, static_cast(attr_pair.first)); + // Das zweite Element ist ein u32 + writeBin(info_serialized, attr_pair.second); } // SRT Info u32 srtCount = static_cast(info.srt_info.srt_reservations.size()); - writeBin(info_serialized, count); + writeBin(info_serialized, srtCount); for (const auto& res : info.srt_info.srt_reservations) { writeBin(info_serialized, res.sgpr_base); @@ -91,24 +322,313 @@ void SerializeInfo(std::ostream& info_serialized, Shader::Info info) { writeBin(info_serialized, res.num_dwords); } + // Flat UD + + u32 flatCount = static_cast(info.flattened_ud_buf.size()); + writeBin(info_serialized, flatCount); + + for (const auto& flat : info.flattened_ud_buf) { + writeBin(info_serialized, flat); + } + + // Tessellation Data + writeBin(info_serialized, info.tess_consts_ptr_base); + writeBin(info_serialized, info.tess_consts_dword_offset); + + // Flags + writeBin(info_serialized, static_cast(info.has_storage_images ? 1 : 0)); + writeBin(info_serialized, static_cast(info.has_discard ? 1 : 0)); + writeBin(info_serialized, static_cast(info.has_image_gather ? 1 : 0)); + writeBin(info_serialized, static_cast(info.has_image_query ? 1 : 0)); + writeBin(info_serialized, static_cast(info.uses_lane_id ? 1 : 0)); + writeBin(info_serialized, static_cast(info.uses_group_quad ? 1 : 0)); + writeBin(info_serialized, static_cast(info.uses_group_ballot ? 1 : 0)); + writeBin(info_serialized, static_cast(info.uses_shared ? 1 : 0)); + writeBin(info_serialized, static_cast(info.uses_fp16 ? 1 : 0)); + writeBin(info_serialized, static_cast(info.uses_fp64 ? 1 : 0)); + writeBin(info_serialized, static_cast(info.uses_pack_10_11_11 ? 1 : 0)); + writeBin(info_serialized, static_cast(info.uses_unpack_10_11_11 ? 1 : 0)); + writeBin(info_serialized, static_cast(info.stores_tess_level_outer ? 1 : 0)); + writeBin(info_serialized, static_cast(info.stores_tess_level_inner ? 1 : 0)); + writeBin(info_serialized, static_cast(info.translation_failed ? 1 : 0)); + writeBin(info_serialized, static_cast(info.has_readconst ? 1 : 0)); + // MRT Mask writeBin(info_serialized, info.mrt_mask); -} -void DeserializeInfo(std::istream& info_serialized, Shader::Info& info) { - readBin(info_serialized, info.mrt_mask); + // Fetch + + writeBin(info_serialized, static_cast(info.has_fetch_shader ? 1 : 0)); + writeBin(info_serialized, info.fetch_shader_sgpr_base); + + // Stage + writeBin(info_serialized, info.stage); + writeBin(info_serialized, info.l_stage); + writeBin(info_serialized, info.pgm_hash); + + // AttributeFlags für loads + u32 loads_size = static_cast(info.loads.flags.size()); + writeBin(info_serialized, loads_size); + for (size_t i = 0; i < info.loads.flags.size(); ++i) { + writeBin(info_serialized, info.loads.flags[i]); + } + + // AttributeFlags für stores + u32 stores_size = static_cast(info.stores.flags.size()); + writeBin(info_serialized, stores_size); + for (size_t i = 0; i < info.stores.flags.size(); ++i) { + writeBin(info_serialized, info.stores.flags[i]); + } } bool CheckShaderCache(std::string shader_id) { - return 0; + // Überprüfen, ob das Verzeichnis existiert + if (!std::filesystem::exists(shader_cache_dir)) { + LOG_INFO(Render_Vulkan, "Shader-Cache-Verzeichnis existiert nicht"); + return false; + } + + // Überprüfen, ob sowohl die SPIR-V-Datei als auch die Ressourcendatei existieren + std::filesystem::path spirv_cache_file_path = shader_cache_dir / (shader_id + ".spv"); + std::filesystem::path resources_file_path = shader_cache_dir / (shader_id + ".resources"); + + if (!std::filesystem::exists(spirv_cache_file_path)) { + LOG_DEBUG(Render_Vulkan, "SPIR-V-Datei nicht gefunden: {}", spirv_cache_file_path.string()); + return false; + } + + if (!std::filesystem::exists(resources_file_path)) { + LOG_DEBUG(Render_Vulkan, "Ressourcendatei nicht gefunden: {}", + resources_file_path.string()); + return false; + } + + // Überprüfen, ob die Dateien lesbar und nicht leer sind + Common::FS::IOFile spirv_file(spirv_cache_file_path, Common::FS::FileAccessMode::Read); + Common::FS::IOFile resources_file(resources_file_path, Common::FS::FileAccessMode::Read); + + const bool spirv_valid = spirv_file.IsOpen() && spirv_file.GetSize() > 0; + const bool resources_valid = resources_file.IsOpen() && resources_file.GetSize() > 0; + + spirv_file.Close(); + resources_file.Close(); + + if (!spirv_valid || !resources_valid) { + LOG_WARNING(Render_Vulkan, "Ungueltige Dateien im Shader-Cache für ID: {}", shader_id); + // Fehlerhafte Dateien entfernen, um zukünftige Probleme zu vermeiden + if (std::filesystem::exists(spirv_cache_file_path)) { + std::filesystem::remove(spirv_cache_file_path); + } + if (std::filesystem::exists(resources_file_path)) { + std::filesystem::remove(resources_file_path); + } + return false; + } + + LOG_INFO(Render_Vulkan, "Shader mit ID {} im Cache gefunden", shader_id); + return true; } -void GetShader(std::string shader_id, Shader::Info& info) { - std::string spirv_cache_filename = shader_id + ".spv "; +void DeserializeInfo(std::istream& info_serialized, Shader::Info& info) { + // UD Mask + readBin(info_serialized, info.ud_mask.mask); + + // Buffer-Resources + u32 bufferCount; + readBin(info_serialized, bufferCount); + + info.buffers.clear(); + info.buffers.reserve(bufferCount); + for (u32 i = 0; i < bufferCount; ++i) { + Shader::BufferResource buffer; + readBin(info_serialized, buffer.sharp_idx); + u32 used_types; + readBin(info_serialized, used_types); + buffer.used_types = static_cast(used_types); + u32 buffer_type; + readBin(info_serialized, buffer_type); + buffer.buffer_type = static_cast(buffer_type); + readBin(info_serialized, buffer.instance_attrib); + u8 is_written; + readBin(info_serialized, is_written); + buffer.is_written = (is_written == 1); + u8 is_formatted; + readBin(info_serialized, is_formatted); + buffer.is_formatted = (is_formatted == 1); + info.buffers.push_back(std::move(buffer)); + } + + // Image-Resources + u32 imageCount; + readBin(info_serialized, imageCount); + + info.images.clear(); + info.images.reserve(imageCount); + for (u32 i = 0; i < imageCount; ++i) { + Shader::ImageResource image; + readBin(info_serialized, image.sharp_idx); + u8 is_depth; + readBin(info_serialized, is_depth); + image.is_depth = (is_depth == 1); + u8 is_atomic; + readBin(info_serialized, is_atomic); + image.is_atomic = (is_atomic == 1); + u8 is_array; + readBin(info_serialized, is_array); + image.is_array = (is_array == 1); + u8 is_written; + readBin(info_serialized, is_written); + image.is_written = (is_written == 1); + info.images.push_back(std::move(image)); + } + + // Sampler-Resources + u32 samplerCount; + readBin(info_serialized, samplerCount); + + info.samplers.clear(); + info.samplers.reserve(samplerCount); + for (u32 i = 0; i < samplerCount; ++i) { + Shader::SamplerResource sampler; + readBin(info_serialized, sampler.sharp_idx); + info.samplers.push_back(std::move(sampler)); + } + + // FMask-Resources + u32 fmaskCount; + readBin(info_serialized, fmaskCount); + + info.fmasks.clear(); + info.fmasks.reserve(fmaskCount); + for (u32 i = 0; i < fmaskCount; ++i) { + Shader::FMaskResource fmask; + readBin(info_serialized, fmask.sharp_idx); + info.fmasks.push_back(std::move(fmask)); + } + + // GS Copy Data + u32 mapCount; + readBin(info_serialized, mapCount); + + info.gs_copy_data.attr_map.clear(); + for (u32 i = 0; i < mapCount; ++i) { + u32 loc; + u32 attribute_value; + u32 idx; + readBin(info_serialized, loc); + readBin(info_serialized, attribute_value); + readBin(info_serialized, idx); + + // Umwandeln des numerischen Werts zurück in das Shader::IR::Attribute-Enum + Shader::IR::Attribute attribute = static_cast(attribute_value); + + // Einfügen in die Map mit dem richtigen Paar-Typ + info.gs_copy_data.attr_map.emplace(loc, std::make_pair(attribute, idx)); + } + + // SRT Info + u32 srtCount; + readBin(info_serialized, srtCount); + + info.srt_info.srt_reservations.clear(); + info.srt_info.srt_reservations.resize(srtCount); + for (u32 i = 0; i < srtCount; ++i) { + auto& res = info.srt_info.srt_reservations[i]; + readBin(info_serialized, res.sgpr_base); + readBin(info_serialized, res.dword_offset); + readBin(info_serialized, res.num_dwords); + } + + // Flat UD + u32 flatCount; + readBin(info_serialized, flatCount); + + info.flattened_ud_buf.clear(); + info.flattened_ud_buf.resize(flatCount); + for (u32 i = 0; i < flatCount; ++i) { + readBin(info_serialized, info.flattened_ud_buf[i]); + } + + // Tessellation Data + readBin(info_serialized, info.tess_consts_ptr_base); + readBin(info_serialized, info.tess_consts_dword_offset); + + // Flags + u8 flag_value; + readBin(info_serialized, flag_value); + info.has_storage_images = (flag_value == 1); + readBin(info_serialized, flag_value); + info.has_discard = (flag_value == 1); + readBin(info_serialized, flag_value); + info.has_image_gather = (flag_value == 1); + readBin(info_serialized, flag_value); + info.has_image_query = (flag_value == 1); + readBin(info_serialized, flag_value); + info.uses_lane_id = (flag_value == 1); + readBin(info_serialized, flag_value); + info.uses_group_quad = (flag_value == 1); + readBin(info_serialized, flag_value); + info.uses_group_ballot = (flag_value == 1); + readBin(info_serialized, flag_value); + info.uses_shared = (flag_value == 1); + readBin(info_serialized, flag_value); + info.uses_fp16 = (flag_value == 1); + readBin(info_serialized, flag_value); + info.uses_fp64 = (flag_value == 1); + readBin(info_serialized, flag_value); + info.uses_pack_10_11_11 = (flag_value == 1); + readBin(info_serialized, flag_value); + info.uses_unpack_10_11_11 = (flag_value == 1); + readBin(info_serialized, flag_value); + info.stores_tess_level_outer = (flag_value == 1); + readBin(info_serialized, flag_value); + info.stores_tess_level_inner = (flag_value == 1); + readBin(info_serialized, flag_value); + info.translation_failed = (flag_value == 1); + readBin(info_serialized, flag_value); + info.has_readconst = (flag_value == 1); + + // MRT Mask + readBin(info_serialized, info.mrt_mask); + + // Fetch Shader + u8 has_fetch_shader; + readBin(info_serialized, has_fetch_shader); + info.has_fetch_shader = (has_fetch_shader == 1); + readBin(info_serialized, info.fetch_shader_sgpr_base); + + // Stage + readBin(info_serialized, info.stage); + readBin(info_serialized, info.l_stage); + readBin(info_serialized, info.pgm_hash); + + // AttributeFlags für loads + u32 loads_size; + readBin(info_serialized, loads_size); + for (size_t i = 0; i < loads_size && i < info.loads.flags.size(); ++i) { + readBin(info_serialized, info.loads.flags[i]); + } + + // AttributeFlags für stores + u32 stores_size; + readBin(info_serialized, stores_size); + for (size_t i = 0; i < stores_size && i < info.stores.flags.size(); ++i) { + readBin(info_serialized, info.stores.flags[i]); + } + + // Check if there are any remaining bytes in the stream + if (info_serialized.peek() != EOF) { + LOG_WARNING(Render_Vulkan, "Es sind noch {} Bytes im Stream übrig", + info_serialized.gcount()); + } +} + + +void GetShader(std::string shader_id, Shader::Info& info, std::vector& spv) { + std::string spirv_cache_filename = shader_id + ".spv"; std::filesystem::path spirv_cache_file_path = shader_cache_dir / spirv_cache_filename; Common::FS::IOFile spirv_cache_file(spirv_cache_file_path, Common::FS::FileAccessMode::Read); - std::vector spv; spv.resize(spirv_cache_file.GetSize() / sizeof(u32)); spirv_cache_file.Read(spv); spirv_cache_file.Close(); @@ -126,7 +646,6 @@ void GetShader(std::string shader_id, Shader::Info& info) { // Verarbeite die gespeicherten Daten std::istringstream combined_stream(std::string(resources_data.begin(), resources_data.end())); - // Deserialisiere info und profile std::istringstream info_stream; info_stream.str(std::string(resources_data.begin(), resources_data.end())); DeserializeInfo(info_stream, info); @@ -146,7 +665,7 @@ void AddShader(std::string shader_id, std::vector spv, std::ostream& info_s Common::FS::IOFile resources_dump_file(resources_dump_file_path, Common::FS::FileAccessMode::Write); - // Die Streams müssen zurückgesetzt werden, bevor wir sie lesen können + // Die Streams müssen zurückgesetzt werden, bevor wir sie lesen können if (std::ostringstream* info_oss = dynamic_cast(&info_serialized)) { std::string info_data = info_oss->str(); resources_dump_file.WriteSpan(std::span(info_data.data(), info_data.size())); diff --git a/src/video_core/renderer_vulkan/shader_cache.h b/src/video_core/renderer_vulkan/shader_cache.h index 2b5c1ae75..d2320e455 100644 --- a/src/video_core/renderer_vulkan/shader_cache.h +++ b/src/video_core/renderer_vulkan/shader_cache.h @@ -5,15 +5,17 @@ #include #include #include "shader_recompiler/info.h" +#include namespace ShaderCache { -std::string CreateShaderID(u64 pgm_hash, size_t perm_idx, std::ostream& info_dump); -void SerializeInfo(std::ostream& info_serialized, Shader::Info info); +u64 CalculateSpecializationHash(const Shader::StageSpecialization& spec); +void SerializeInfo( + std::ostream& info_serialized, Shader::Info info); void DeserializeInfo(std::istream& info_serialized, Shader::Info& info); bool CheckShaderCache(std::string shader_id); -void GetShader(std::string shader_id, Shader::Info& info); +void GetShader(std::string shader_id, Shader::Info& info, std::vector& spv); void AddShader(std::string shader_id, std::vector spv, std::ostream& info_serialized); } // namespace ShaderCache diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index cb4a6c0ce..4036d3379 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -488,34 +488,36 @@ bool PipelineCache::RefreshComputeKey() { vk::ShaderModule PipelineCache::CompileModule(Shader::Info& info, Shader::RuntimeInfo& runtime_info, std::span code, size_t perm_idx, - Shader::Backend::Bindings& binding) { + Shader::Backend::Bindings& binding, Shader::StageSpecialization spec) { LOG_INFO(Render_Vulkan, "Compiling {} shader {:#x} {}", info.stage, info.pgm_hash, perm_idx != 0 ? "(permutation)" : ""); + DumpShader(code, info.pgm_hash, info.stage, perm_idx, "bin"); - - - const auto ir_program = Shader::TranslateProgram(code, pools, info, runtime_info, profile); std::string shader_name = GetShaderName(info.stage, info.pgm_hash, perm_idx); - - std::vector spv; - - if (false){ //(::ShaderCache::CheckShaderCache(shader_id)) { - LOG_INFO(Render_Vulkan, "Loaded SPIR-V from cache"); + std::string shader_id = std::to_string(::ShaderCache::CalculateSpecializationHash(spec)); + if (::ShaderCache::CheckShaderCache(shader_id)) { + LOG_INFO(Render_Vulkan, "Loaded shader {} {:#x} {} from cache", info.stage, info.pgm_hash, + perm_idx != 0 ? "(permutation)" : ""); + ::ShaderCache::GetShader(shader_id, info, spv); + info.RefreshFlatBuf(); } else { + LOG_INFO(Render_Vulkan, "Shader {} {:#x} {} not in cache", info.stage, + info.pgm_hash, perm_idx != 0 ? "(permutation)" : ""); + const auto ir_program = Shader::TranslateProgram(code, pools, info, runtime_info, profile); spv = Shader::Backend::SPIRV::EmitSPIRV(profile, runtime_info, ir_program, binding); std::ostringstream info_serialized; ::ShaderCache::SerializeInfo(info_serialized, info); - std::string shader_id = - ::ShaderCache::CreateShaderID(info.pgm_hash, perm_idx, info_serialized); + ::ShaderCache::AddShader(shader_id, spv, info_serialized); LOG_INFO(Render_Vulkan, "Shader ID: {}", shader_id); DumpShader(spv, info.pgm_hash, info.stage, perm_idx, "spv"); - LOG_INFO(Render_Vulkan, "Compiled SPIR-V and stored in cache"); + LOG_INFO(Render_Vulkan, "Compiled shader {} {:#x} {} and saved it to cache", info.stage, info.pgm_hash, + perm_idx != 0 ? "(permutation)" : ""); } vk::ShaderModule module; @@ -543,13 +545,14 @@ PipelineCache::Result PipelineCache::GetProgram(Stage stage, LogicalStage l_stag Shader::ShaderParams params, Shader::Backend::Bindings& binding) { auto runtime_info = BuildRuntimeInfo(stage, l_stage); - auto [it_pgm, new_program] = program_cache.try_emplace(params.hash); + auto [it_pgm, new_program] = program_cache.try_emplace(params.hash); // code in vs if (new_program) { it_pgm.value() = std::make_unique(stage, l_stage, params); auto& program = it_pgm.value(); auto start = binding; - const auto module = CompileModule(program->info, runtime_info, params.code, 0, binding); - const auto spec = Shader::StageSpecialization(program->info, runtime_info, profile, start); + Shader::StageSpecialization spec = + Shader::StageSpecialization(program->info, runtime_info, profile, start); + const auto module = CompileModule(program->info, runtime_info, params.code, 0, binding, spec); program->AddPermut(module, std::move(spec)); return std::make_tuple(&program->info, module, spec.fetch_shader_data, HashCombine(params.hash, 0)); @@ -566,7 +569,7 @@ PipelineCache::Result PipelineCache::GetProgram(Stage stage, LogicalStage l_stag const auto it = std::ranges::find(program->modules, spec, &Program::Module::spec); if (it == program->modules.end()) { auto new_info = Shader::Info(stage, l_stage, params); - module = CompileModule(new_info, runtime_info, params.code, perm_idx, binding); + module = CompileModule(new_info, runtime_info, params.code, perm_idx, binding, spec); program->AddPermut(module, std::move(spec)); } else { info.AddBindings(binding); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index ba3407b48..c20d06a7b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -82,7 +82,7 @@ private: std::string_view ext); vk::ShaderModule CompileModule(Shader::Info& info, Shader::RuntimeInfo& runtime_info, std::span code, size_t perm_idx, - Shader::Backend::Bindings& binding); + Shader::Backend::Bindings& binding, Shader::StageSpecialization spec); const Shader::RuntimeInfo& BuildRuntimeInfo(Shader::Stage stage, Shader::LogicalStage l_stage); private: