diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp index a649069123..70fcacc0a2 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp @@ -1,4 +1,5 @@ #include "ShaderDiskCache.hpp" +#include "CacheKey.hpp" #include "ShaderCacheDirectory.hpp" #include #include @@ -217,7 +218,6 @@ struct SampleRequest { std::array extensions{"SPV_KHR_storage_buffer_storage_class"}; std::array header{}; RecompileRequest request{}; - ResourceSpecialization specialization; std::uint32_t hostSubgroupSize = 32; SampleRequest() { @@ -236,8 +236,6 @@ struct SampleRequest { request.target.maxWorkgroupInvocations = 1024; request.target.maxWorkgroupSharedMemoryBytes = 49152; request.layout = {0, 0, 0, 128}; - specialization.images = {ResourceSpecialization::Image{}}; - specialization.boundDescriptors = {0, 0}; } std::vector Key() { @@ -245,7 +243,7 @@ struct SampleRequest { request.context.userData = userData; request.target.supportedCapabilities = capabilities; std::vector key; - ShaderDiskCache::BuildKey(request, hostSubgroupSize, specialization, key); + ShaderDiskCache::BuildKey(request, hostSubgroupSize, key); return key; } }; @@ -377,19 +375,6 @@ void verifyKeySensitivity() { changes("the first binding", [](SampleRequest& sample) { sample.request.layout.firstBinding = 1; }); changes("the push constant offset", [](SampleRequest& sample) { sample.request.layout.pushConstantOffsetBytes = 16; }); changes("the push constant size", [](SampleRequest& sample) { sample.request.layout.pushConstantSizeBytes = 64; }); - changes("an image class", [](SampleRequest& sample) { sample.specialization.images[0].numericClass = IrTextureNumericClass::Float; }); - changes("an image dimension", [](SampleRequest& sample) { sample.specialization.images[0].dimension = static_cast(1); }); - changes("an image mip count", [](SampleRequest& sample) { sample.specialization.images[0].mipCount = 2; }); - changes("an image conversion", [](SampleRequest& sample) { sample.specialization.images[0].conversionFormat = static_cast(1); }); - changes("an image swizzle", [](SampleRequest& sample) { sample.specialization.images[0].shaderSwizzle = 0; }); - changes("an image indirect root", [](SampleRequest& sample) { sample.specialization.images[0].indirectRoot = 0; }); - changes("an image mapping offset", [](SampleRequest& sample) { sample.specialization.images[0].indirectMappingOffset = 4; }); - changes("an image search depth", [](SampleRequest& sample) { sample.specialization.images[0].indirectSearchIterations = 2; }); - changes("an image cube flag", [](SampleRequest& sample) { sample.specialization.images[0].cube = true; }); - changes("an image FMASK flag", [](SampleRequest& sample) { sample.specialization.images[0].fmask = true; }); - changes("an image sRGB decode", [](SampleRequest& sample) { sample.specialization.images[0].srgbDecode = true; }); - changes("the image count", [](SampleRequest& sample) { sample.specialization.images.emplace_back(); }); - changes("the bound descriptors", [](SampleRequest& sample) { sample.specialization.boundDescriptors.push_back(1); }); SampleRequest moved; moved.userData[0] ^= 0x10000u; @@ -397,6 +382,33 @@ void verifyKeySensitivity() { moved.request.shader.headerAddress += 0x1000000u; moved.header[3] = std::byte{1}; require(moved.Key() == key, "the key depends on the user data values or the addresses"); + std::array descriptors{}; + const std::array regions{MemoryRegion{0x100000u, descriptors}}; + moved.request.context.memory = regions; + std::fill(moved.userData.begin(), moved.userData.end(), 0xffffffffu); + require(moved.Key() == key, "runtime descriptors changed the artifact key"); + SampleRequest vertex; + vertex.request.shader.stage = ShaderStage::Vertex; + vertex.request.context.compute.reset(); + vertex.request.context.vertex.emplace(); + auto& input = *vertex.request.context.vertex; + input.resourcesNum = 1u; + input.resources[0].fields = {0x10000u, 16u << 16u, 64u, (22u << 12u) | 0xfacu}; + input.resourcesDst[0] = {0u, 4u, 0u, 0u}; + const auto vertexKey = vertex.Key(); + const auto contextKey = RecompileCacheKey::ContextHash(vertex.request); + input.resources[0].fields = {0x20000u, 32u << 16u, 128u, (77u << 12u) | 0xfa9u}; + require(vertex.Key() == vertexKey && RecompileCacheKey::ContextHash(vertex.request) == contextKey, "runtime vertex format, stride, swizzle or address changed the static interface key"); + input.resources[0].fields[3] = (20u << 12u) | 0xfacu; + require(vertex.Key() != vertexKey && RecompileCacheKey::ContextHash(vertex.request) != contextKey, "integer vertex input reused a float interface"); + input.resources[0].fields[3] = (77u << 12u) | 0xfa9u; + input.resourcesDst[0].registersNum = 2u; + require(vertex.Key() != vertexKey, "vertex component interface did not change the artifact key"); + input.fetchEmbedded = true; + const auto embeddedKey = vertex.Key(); + input.resources[0].fields = {}; + input.resourcesNum = 0u; + require(vertex.Key() == embeddedKey, "runtime vertex fetch descriptors changed the artifact key"); } void verifyStore() { @@ -482,7 +494,8 @@ void verifyAcrossProcesses(const char* self) { struct ClockRequest { std::vector code{0xf4900100u, 0x00000000u, 0xbf8cc07fu, 0x7e020204u, 0xe0700000u, 0x80000100u, 0xf800180fu, 0x01010101u, 0xbf810000u}; std::array userData{0x10000000u, 0x00000000u, 0x40u, 0x00027facu}; - std::array capabilities{1u}; + std::array capabilities{1u, 11u, 5347u, 4448u}; + std::array extensions{"SPV_KHR_physical_storage_buffer", "SPV_KHR_8bit_storage"}; RecompileRequest request{}; explicit ClockRequest(std::uint32_t stride = 0) { @@ -501,13 +514,15 @@ struct ClockRequest { request.target.spirvVersion = 0x00010300u; request.target.subgroupSize = 32; request.target.supportedCapabilities = capabilities; + request.target.supportedExtensions = extensions; + request.target.bdaAbiVersion = 1u; request.layout.pushConstantSizeBytes = 128; } }; std::string emissionFailure(const RecompileRequest& request) { try { - static_cast(Recompile(request)); + static_cast(PrepareShader(request)); } catch (const std::runtime_error& error) { const std::string message = error.what(); require(message.find("reads the shader clock, which needs the device's VK_KHR_shader_clock") != std::string::npos, "unexpected failure: " + message.substr(0, message.find("RecompileRequest:"))); @@ -534,12 +549,12 @@ void verifyEmissionFailureMemo() { require(diskMisses() == before.misses + 2, "another binding layout reused the failure"); ClockRequest stride(16); static_cast(emissionFailure(stride.request)); - require(diskMisses() == before.misses + 3, "another buffer specialization reused the failure"); + require(diskMisses() == before.misses + 2, "a runtime buffer stride change repeated static preparation"); ClockRequest relocated; relocated.request.shader.codeAddress += 0x1000u; static_cast(emissionFailure(relocated.request)); - require(diskMisses() == before.misses + 4, "a relocated copy reused the failure"); - require(emissionFailure(clock.request) == first && diskMisses() == before.misses + 4, "the other requests replaced the failure"); + require(diskMisses() == before.misses + 3, "a relocated copy reused the failure"); + require(emissionFailure(clock.request) == first && diskMisses() == before.misses + 3, "the other requests replaced the failure"); ClockRequest concurrent; concurrent.request.layout.pushConstantSizeBytes = 64; @@ -548,7 +563,7 @@ void verifyEmissionFailureMemo() { std::vector messages; for (auto& future : failures) messages.push_back(future.get()); require(std::all_of(messages.begin(), messages.end(), [&](const std::string& message) { return message == messages.front(); }), "concurrent requests failed with different messages"); - require(diskMisses() == before.misses + 5, "concurrent requests emitted the program " + std::to_string(diskMisses() - before.misses - 4) + " times"); + require(diskMisses() == before.misses + 4, "concurrent requests emitted the program " + std::to_string(diskMisses() - before.misses - 3) + " times"); ShaderDiskCache::Flush(); require(ShaderDiskCache::Totals().writes == before.writes, "an emission failure was stored in the disk cache"); diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp index b2608c0ed9..16c8a8101f 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp @@ -277,14 +277,12 @@ void verifyBindlessTable() { return std::vector(snapshot.flattenedSrt.end() - static_cast(1u + 2u * slots), snapshot.flattenedSrt.end()); }; const auto tableRoot = [&](const ResourceCapture& capture, std::uint32_t direct) { - require(capture.specialization.images.size() == direct + slots - 1u, "bindless: the specialization does not hold the table slots"); require(capture.snapshot.images.size() == direct + slots - 1u, "bindless: the snapshot does not hold the table slots"); std::uint32_t root = ImageResource::NoIndirectImage; for (std::uint32_t i = 0; i < direct; i++) { - if (capture.specialization.images[i].indirectRoot == i) root = i; + if (capture.plan->descriptorSources.at(capture.plan->info.images.at(i).source).indirectImage.has_value()) root = i; } require(root != ImageResource::NoIndirectImage, "bindless: no table root"); - for (std::uint32_t i = direct; i < capture.specialization.images.size(); i++) require(capture.specialization.images[i].indirectRoot == root, "bindless: an extra image is not the root's slot"); return root; }; @@ -300,6 +298,16 @@ void verifyBindlessTable() { require(tables == 1, "bindless: the table source was not planned"); for (const auto& image : plan->info.images) require(image.indirectSearchIterations == 0u, "bindless: the plan carries a search depth"); const auto direct = static_cast(plan->info.images.size()); + auto staticRequest = request; + const std::array absentResources{}; + staticRequest.context.userData = absentResources; + staticRequest.context.memory = {}; + auto staticProgram = PrepareResourceProgram(staticRequest); + ResourceMaterializer{}.ApplyStaticInterface(staticProgram); + const auto& staticImages = staticProgram.Resources().info.images; + require(staticImages.size() == direct + slots - 1u, "bindless: the static interface needs runtime descriptors"); + const auto staticRoot = std::ranges::find_if(staticImages, [](const ImageResource& image) { return image.indirectSearchIterations != 0u; }); + require(staticRoot != staticImages.end() && staticRoot->indirectResources.size() == slots && staticRoot->indirectMappingOffset == plan->srtReads.size(), "bindless: the static table interface is incomplete"); AgcDriver::ShaderMemory memory({}); const auto capture = memory.Capture(request); @@ -308,7 +316,7 @@ void verifyBindlessTable() { for (std::uint32_t i = direct + 2u; i < capture->snapshot.images.size(); i++) require(capture->snapshot.images[i].dwords == heap[2], "bindless: a pad slot is not null"); const auto mapping = mappingOf(capture->snapshot); require(std::vector(mapping.begin(), mapping.begin() + 7) == std::vector{3u, 0u, 0u, 1u, 1u, 3u, 2u}, "bindless: the (key, slot) mapping is wrong"); - require(capture->specialization.images[root].indirectMappingOffset + mapping.size() == capture->snapshot.flattenedSrt.size(), "bindless: the mapping offset does not name the block"); + require(capture->plan->srtReads.size() + mapping.size() == capture->snapshot.flattenedSrt.size(), "bindless: the mapping offset does not name the block"); auto regions = memory.Regions(); for (const auto& material : materials) require(covered(regions, &material[1], sizeof(std::uint32_t)), "bindless: a material key was not captured"); for (const auto entry : {0u, 1u, 3u}) require(covered(regions, heap[entry].data(), 32u), "bindless: a table entry was not captured"); diff --git a/core/shader/recompiler/CacheKey.hpp b/core/shader/recompiler/CacheKey.hpp index 39fd711278..1d2b784e80 100644 --- a/core/shader/recompiler/CacheKey.hpp +++ b/core/shader/recompiler/CacheKey.hpp @@ -2,6 +2,7 @@ #define CORE_SHADER_RECOMPILER_CACHEKEY_HPP #include "Recompiler.hpp" +#include "RdnaDecoder/RdnaDescriptorFormat.hpp" #include "prx/libc/include/HostThreadLocal.hpp" #include #include @@ -164,8 +165,7 @@ private: if (value.fetchEmbedded) return; append(key, value.resourcesNum); for (std::uint32_t i = 0; i < value.resourcesNum; ++i) { - append(key, value.resources[i].fields[1] & 0xffff0000u); - append(key, value.resources[i].fields[3]); + append(key, VertexInputNumericClass(static_cast((value.resources[i].fields[3] >> 12u) & 0x7fu))); append(key, value.resourcesDst[i]); } } diff --git a/core/shader/recompiler/CompiledVariant.hpp b/core/shader/recompiler/CompiledVariant.hpp index 404e466048..00b780914d 100644 --- a/core/shader/recompiler/CompiledVariant.hpp +++ b/core/shader/recompiler/CompiledVariant.hpp @@ -4,12 +4,10 @@ #include "Recompiler.hpp" #include "IntermediateRepresentation/IrMetadata/CompiledShaderInfo.hpp" #include "Optimization/include/Optimization/BindingAllocator.hpp" -#include "Optimization/include/Optimization/ResourceMaterializer.hpp" namespace ShaderRecompiler { struct CompiledVariant { - ResourceSpecialization specialization; BindingLayout layout; CompiledShaderInfo info; CompiledBindingLayout bindings; diff --git a/core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp b/core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp index 09ea8330b8..0cd825884c 100644 --- a/core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp +++ b/core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp @@ -8,46 +8,18 @@ namespace ShaderRecompiler { -struct ResourceSpecialization { - struct Image { - IrTextureNumericClass numericClass = IrTextureNumericClass::Unsupported; - RdnaImageDimension dimension = RdnaImageDimension::Unknown; - std::uint32_t mipCount = 1; - IrBufferFormat conversionFormat = IrBufferFormat::Invalid; - std::uint32_t shaderSwizzle = ShaderImageIdentitySwizzle; - std::uint32_t indirectRoot = ImageResource::NoIndirectImage; - std::uint32_t indirectMappingOffset = 0; - std::uint32_t indirectSearchIterations = 0; - bool cube = false; - bool fmask = false; - bool depthBits = false; - bool depthUnorm16 = false; - IrBufferFormat packedFormat = IrBufferFormat::Invalid; - std::uint32_t emulatedCompare = 0; - bool srgbDecode = false; - - bool operator==(const Image& other) const; - }; - - std::vector images; - - bool operator==(const ResourceSpecialization& other) const; - - std::vector boundDescriptors; -}; - // Why a bindless image table (a T# loaded from a table buffer at a runtime key) was not bound; // counted on the [bindless] line (APS5_PROFILE_DRAW). enum class BindlessRejection { Capacity, MaterialScan, NoEntry, Storage, NonUniform, ImageSlots, Count }; class ResourceMaterializer { public: - void Apply(IrProgram& program, const ResourceSpecialization& specialization) const; + static std::uint32_t EmulatedCompareState(const ShaderInfo& info, const ResourceSnapshot& snapshot, std::uint32_t index); + void ApplyStaticInterface(IrProgram& program) const; static std::vector RuntimeImageModes(const ImageResource& image); static std::uint32_t RuntimeImageMode(const ImageResource& image, const DescriptorValue& descriptor); [[nodiscard]] IrResourcePlan ExtractPlan(const IrProgram& program) const; - void Materialize(const IrResourcePlan& program, const SrtRuntime& runtime, ResourceSnapshot& snapshot, ResourceSpecialization& specialization) const; - // APS5_PROFILE_DRAW: the time Materialize spent building specializations, over every call. + void Materialize(const IrResourcePlan& program, const SrtRuntime& runtime, ResourceSnapshot& snapshot) const; static std::uint64_t SpecializationNanoseconds(); static std::uint32_t BindlessSlots(); static void CountBindlessRejection(BindlessRejection reason); diff --git a/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp b/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp index 7e56f67e47..5ff7d1939c 100644 --- a/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp +++ b/core/shader/recompiler/Optimization/include/Optimization/ResourceProgram.hpp @@ -3,7 +3,7 @@ #include "Recompiler.hpp" #include "IntermediateRepresentation/IrProgram.hpp" -#include "Optimization/ResourceMaterializer.hpp" +#include "Optimization/SrtWalker.hpp" #include #include @@ -12,13 +12,10 @@ namespace ShaderRecompiler { [[nodiscard]] IrProgram PrepareResourceProgram(const RecompileRequest& request); [[nodiscard]] std::shared_ptr GetResourcePlan(const RecompileRequest& request); -// What a driver's capture of a request produces: the plan and the materialization of the words the -// runtime read through it. Recompile(request, capture) compiles from these without a second walk. struct SourceEntry; struct ResourceCapture { std::shared_ptr plan; ResourceSnapshot snapshot; - ResourceSpecialization specialization; // The cache entry the plan belongs to; null when the request bypasses the cache. std::shared_ptr source; // The walk's read addresses when the plan has pure flat slots (the leaf of each pure slot, diff --git a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp index 4aaa73988c..67bafb0e57 100644 --- a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp +++ b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp @@ -491,8 +491,8 @@ std::uint32_t colorCompareReference(IrBufferFormat format) { } } -std::uint32_t emulatedCompareState(const IrResourcePlan& plan, const ResourceSnapshot& snapshot, std::uint32_t index) { - const auto& image = plan.info.images[index]; +std::uint32_t emulatedCompareState(const ShaderInfo& info, const ResourceSnapshot& snapshot, std::uint32_t index) { + const auto& image = info.images[index]; const auto& descriptor = snapshot.images[index]; if (!image.depthCompare || descriptor.dwordCount != 8u || nullImageDescriptor(descriptor)) return 0u; const auto format = rawImageFormat(descriptor); @@ -502,7 +502,7 @@ std::uint32_t emulatedCompareState(const IrResourcePlan& plan, const ResourceSna if (type != ImageType::Color2D && type != ImageType::Color2DArray) throw std::runtime_error("comparison sampling of a color texture is implemented only for 2D and 2D array views"); if ((descriptorImageSwizzle(descriptor) & 0x7u) != 4u) throw std::runtime_error("comparison sampling of a color texture is implemented only when the view's X channel is red"); std::optional samplerState; - for (const auto& pair : plan.info.sampledPairs) { + for (const auto& pair : info.sampledPairs) { if (pair.image != index) continue; if (pair.sampler >= snapshot.samplers.size() || snapshot.samplers[pair.sampler].dwordCount != 4u) throw std::runtime_error("comparison sampling of a color texture has no sampler descriptor"); const auto& words = snapshot.samplers[pair.sampler].dwords; @@ -536,77 +536,33 @@ std::uint32_t emulatedCompareState(const IrResourcePlan& plan, const ResourceSna return *samplerState | (reference << EmulatedCompare::ReferenceShift) | (singleLevel ? EmulatedCompare::SingleLevel : 0u); } -void buildResourceSpecialization(const IrResourcePlan& plan, ResourceSnapshot& snapshot, const std::vector& tables, ResourceSpecialization& specialization) { - ResourceSpecialization result; - for (std::uint32_t i = 0; i < plan.info.buffers.size(); ++i) { +void materializeTables(const IrResourcePlan& plan, ResourceSnapshot& snapshot, const std::vector& tables) { + for (std::uint32_t i = 0u; i < plan.info.buffers.size(); ++i) { const auto decoded = decodeBufferDescriptor(snapshot.buffers.at(i)); if (decoded.Type() != 0u) throw std::runtime_error("buffer descriptor uses an unsupported type"); if (plan.stage != IrShaderStage::Compute && decoded.AddTid()) throw std::runtime_error("buffer ADD_TID is only valid for compute shaders"); } - - result.images.reserve(plan.info.images.size()); - for (std::uint32_t i = 0; i < plan.info.images.size(); i++) { + if (snapshot.flattenedSrt.size() != plan.srtReads.size()) throw std::runtime_error("runtime SRT size differs from the static interface"); + for (std::uint32_t i = 0u; i < plan.info.images.size(); ++i) { const auto& image = plan.info.images[i]; - const DecodedImage decoded = decodeImageDescriptor(snapshot.images[i], image, plan.srgbDecodeFormats); - if (decoded.fmask && std::any_of(plan.info.sampledPairs.begin(), plan.info.sampledPairs.end(), [i](const SampledResourcePair& pair) { return pair.image == i; })) { - throw std::runtime_error("FMASK requires a direct image load"); - } - ResourceSpecialization::Image entry; - entry.numericClass = image.atomic ? IrTextureNumericClass::Uint : IrTextureNumericClass::Float; - entry.dimension = image.dimension; - entry.mipCount = image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageHeapCapacity : 1u; - entry.conversionFormat = IrBufferFormat::Invalid; - entry.shaderSwizzle = ShaderImageIdentitySwizzle; - entry.indirectRoot = ImageResource::NoIndirectImage; - entry.indirectMappingOffset = 0u; - entry.indirectSearchIterations = 0u; - entry.cube = false; - entry.fmask = false; - entry.depthBits = false; - entry.depthUnorm16 = false; - entry.packedFormat = IrBufferFormat::Invalid; - entry.emulatedCompare = emulatedCompareState(plan, snapshot, i); - entry.srgbDecode = decoded.srgbDecode; - result.images.push_back(entry); - } - - // A table root is followed by its slots 1..C-1 as extra images of the root's shape; the - // (key, slot) mapping the SPIR-V selector searches is appended to the flattened SRT in a - // block of fixed size, so the offsets (part of the specialization) never depend on the keys. - for (std::uint32_t i = 0; i < plan.info.images.size(); i++) { - const auto& table = tables[i]; - if (table.slots.empty()) { + static_cast(ResourceMaterializer::RuntimeImageMode(image, snapshot.images.at(i))); + const auto& table = tables.at(i); + if (!plan.descriptorSources.at(image.source).indirectImage.has_value()) { + if (!table.slots.empty()) throw std::runtime_error("direct image has runtime table slots"); continue; } - const auto mappingOffset = static_cast(snapshot.flattenedSrt.size()); + const auto slots = ResourceMaterializer::BindlessSlots(); + if (table.slots.size() != slots) throw std::runtime_error("runtime table size differs from the static interface"); + const auto mappingOffset = snapshot.flattenedSrt.size(); snapshot.flattenedSrt.push_back(static_cast(table.mapping.size())); for (const auto& [key, slot] : table.mapping) { + if (slot >= slots) throw std::runtime_error("runtime table slot exceeds its static capacity"); snapshot.flattenedSrt.push_back(key); snapshot.flattenedSrt.push_back(slot == 0u ? i : static_cast(snapshot.images.size()) + slot - 1u); } - snapshot.flattenedSrt.resize(mappingOffset + 1u + 2u * table.slots.size(), 0u); - auto root = result.images[i]; - root.indirectRoot = i; - root.indirectMappingOffset = mappingOffset; - root.indirectSearchIterations = static_cast(std::bit_width(table.slots.size())); - result.images[i] = root; - root.indirectMappingOffset = 0u; - root.indirectSearchIterations = 0u; - for (std::uint32_t slot = 1; slot < table.slots.size(); slot++) { - result.images.push_back(root); - snapshot.images.push_back(table.slots[slot]); - } + snapshot.flattenedSrt.resize(mappingOffset + 1u + 2u * slots, 0u); + snapshot.images.insert(snapshot.images.end(), table.slots.begin() + 1u, table.slots.end()); } - - result.boundDescriptors.clear(); - result.boundDescriptors.reserve(plan.info.buffers.size() + result.images.size()); - for (std::uint32_t index = 0; index < plan.info.buffers.size(); index++) { - result.boundDescriptors.push_back(index); - } - for (std::uint32_t index = 0; index < result.images.size(); index++) { - result.boundDescriptors.push_back(index); - } - specialization = std::move(result); } } @@ -694,60 +650,44 @@ std::uint32_t ResourceMaterializer::RuntimeImageMode(const ImageResource& image, throw std::runtime_error("image descriptor is incompatible with the static runtime image interface"); } -void ResourceMaterializer::Apply(IrProgram& program, const ResourceSpecialization& specialization) const { - IrResourcePlan& resources = program.Resources(); - if (!resources.resourceTrackingComplete) { - throw std::runtime_error("ResourceMaterializer::Apply requires a completed resource plan"); - } - if (resources.info.images.size() > specialization.images.size()) { - throw std::runtime_error("ResourceMaterializer::Apply image count mismatch"); - } +std::uint32_t ResourceMaterializer::EmulatedCompareState(const ShaderInfo& info, const ResourceSnapshot& snapshot, std::uint32_t index) { + return emulatedCompareState(info, snapshot, index); +} +void ResourceMaterializer::ApplyStaticInterface(IrProgram& program) const { + auto& resources = program.Resources(); + if (!resources.resourceTrackingComplete || !resources.srtPlanComplete) throw std::runtime_error("static resource interface requires a completed resource plan"); auto images = resources.info.images; - images.reserve(specialization.images.size()); - for (std::uint32_t index = 0; index < specialization.images.size(); index++) { - const auto& source = specialization.images[index]; - if (index >= images.size()) { - if (source.indirectRoot >= resources.info.images.size()) { - throw std::runtime_error("ResourceMaterializer::Apply indirect image root is out of range"); + const auto directCount = static_cast(images.size()); + auto mappingOffset = static_cast(resources.srtReads.size()); + const auto slots = BindlessSlots(); + for (std::uint32_t index = 0u; index < directCount; ++index) { + auto image = images[index]; + if (image.indirectRoot != ImageResource::NoIndirectImage) throw std::runtime_error("static image interface was already expanded"); + image.numericClass = image.atomic ? IrTextureNumericClass::Uint : IrTextureNumericClass::Float; + image.mipCount = image.mipMode == ImageMipMode::DynamicStorage ? RuntimeAbi::StorageHeapCapacity : 1u; + if (resources.descriptorSources.at(image.source).indirectImage.has_value()) { + if (images.size() + slots - 1u > ShaderInfo::MaxImages) throw std::runtime_error("static bindless image capacity exceeded"); + image.indirectRoot = index; + image.indirectResources.push_back(index); + for (std::uint32_t slot = 1u; slot < slots; ++slot) { + auto entry = image; + entry.indirectResources.clear(); + image.indirectResources.push_back(static_cast(images.size())); + images.push_back(std::move(entry)); } - images.push_back(resources.info.images[source.indirectRoot]); + image.indirectMappingOffset = mappingOffset; + image.indirectSearchIterations = static_cast(std::bit_width(slots)); + mappingOffset += 1u + 2u * slots; } - auto& image = images[index]; - image.numericClass = source.numericClass; - image.dimension = source.dimension; - image.mipCount = source.mipCount; - image.conversionFormat = source.conversionFormat; - image.shaderSwizzle = source.shaderSwizzle; - image.indirectRoot = source.indirectRoot; - image.indirectMappingOffset = source.indirectMappingOffset; - image.indirectSearchIterations = source.indirectSearchIterations; - image.cube = source.cube; - image.depthBits = source.depthBits; - image.depthUnorm16 = source.depthUnorm16; - image.packedFormat = source.packedFormat; - image.emulatedCompare = source.emulatedCompare; - image.srgbDecode = source.srgbDecode; - if ((source.emulatedCompare & EmulatedCompare::Enabled) != 0u) image.depthCompare = false; - image.indirectResources.clear(); + images[index] = std::move(image); } - for (std::uint32_t index = 0; index < images.size(); index++) { - const auto root = images[index].indirectRoot; - if (root != ImageResource::NoIndirectImage) { - if (root >= images.size()) { - throw std::runtime_error("ResourceMaterializer::Apply indirect image root is out of range"); - } - images[root].indirectResources.push_back(index); - } - } - - auto samplers = resources.info.samplers; for (const auto& pair : resources.info.sampledPairs) { - if (pair.image >= images.size() || pair.sampler >= samplers.size()) throw std::runtime_error("runtime sampled pair is out of range"); - samplers[pair.sampler].depthCompare = samplers[pair.sampler].depthCompare || images[pair.image].depthCompare; + if (pair.image >= images.size() || pair.sampler >= resources.info.samplers.size()) throw std::runtime_error("static sampled pair is out of range"); + auto& sampler = resources.info.samplers[pair.sampler]; + sampler.depthCompare = sampler.depthCompare || images[pair.image].depthCompare; } resources.info.images = std::move(images); - resources.info.samplers = std::move(samplers); } namespace { @@ -856,7 +796,7 @@ IrResourcePlan ResourceMaterializer::ExtractPlan(const IrProgram& program) const return plan; } -void ResourceMaterializer::Materialize(const IrResourcePlan& program, const SrtRuntime& runtime, ResourceSnapshot& snapshot, ResourceSpecialization& specialization) const { +void ResourceMaterializer::Materialize(const IrResourcePlan& program, const SrtRuntime& runtime, ResourceSnapshot& snapshot) const { const IrResourcePlan& plan = program; if (!plan.resourceTrackingComplete) { throw std::runtime_error("ResourceMaterializer::Materialize requires a completed resource plan"); @@ -873,12 +813,10 @@ void ResourceMaterializer::Materialize(const IrResourcePlan& program, const SrtR reportBindless(); throw; } - ResourceSpecialization nextSpecialization; const auto started = MaterializeProfiled() ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; - buildResourceSpecialization(plan, nextSnapshot, tables, nextSpecialization); + materializeTables(plan, nextSnapshot, tables); if (MaterializeProfiled()) specializationNanoseconds.fetch_add(static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - started).count()), std::memory_order_relaxed); snapshot = std::move(nextSnapshot); - specialization = std::move(nextSpecialization); reportBindless(); } @@ -894,12 +832,4 @@ void ResourceMaterializer::CountBindlessRejection(BindlessRejection reason) { if (reason < BindlessRejection::Count) bindlessCounters().rejected[static_cast(reason)].fetch_add(1, std::memory_order_relaxed); } -bool ResourceSpecialization::Image::operator==(const Image& other) const { - return numericClass == other.numericClass && dimension == other.dimension && mipCount == other.mipCount && conversionFormat == other.conversionFormat && shaderSwizzle == other.shaderSwizzle && indirectRoot == other.indirectRoot && indirectMappingOffset == other.indirectMappingOffset && indirectSearchIterations == other.indirectSearchIterations && cube == other.cube && fmask == other.fmask && depthBits == other.depthBits && depthUnorm16 == other.depthUnorm16 && packedFormat == other.packedFormat && emulatedCompare == other.emulatedCompare && srgbDecode == other.srgbDecode; -} - -bool ResourceSpecialization::operator==(const ResourceSpecialization& other) const { - return images == other.images; -} - } diff --git a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaDescriptorFormat.hpp b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaDescriptorFormat.hpp index 32796f0101..5eb41904a0 100644 --- a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaDescriptorFormat.hpp +++ b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaDescriptorFormat.hpp @@ -17,6 +17,7 @@ enum class ImageType : std::uint32_t { Color2DMsaaArray = 15 }; +[[nodiscard]] IrTextureNumericClass VertexInputNumericClass(IrBufferFormat format); [[nodiscard]] bool IsFmaskTextureFormat(IrBufferFormat format); [[nodiscard]] IrTextureNumericClass SampledTextureNumericClass(IrBufferFormat format); [[nodiscard]] IrBufferFormat RemapTextureFormat(IrBufferFormat format); diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaDescriptorFormat.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaDescriptorFormat.cpp index a9cc292e6a..7cefea341a 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaDescriptorFormat.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaDescriptorFormat.cpp @@ -137,4 +137,30 @@ bool IsDepthBitsTexture(std::uint32_t word1, std::uint32_t word3) { return DepthBitsTextureWidth(word1, word3) != 0u; } +IrTextureNumericClass VertexInputNumericClass(IrBufferFormat format) { + switch (format) { + case IrBufferFormat::Format8UInt: + case IrBufferFormat::Format16UInt: + case IrBufferFormat::Format8_8UInt: + case IrBufferFormat::Format32UInt: + case IrBufferFormat::Format16_16UInt: + case IrBufferFormat::Format8_8_8_8UInt: + case IrBufferFormat::Format32_32UInt: + case IrBufferFormat::Format16_16_16_16UInt: + case IrBufferFormat::Format32_32_32UInt: + case IrBufferFormat::Format32_32_32_32UInt: return IrTextureNumericClass::Uint; + case IrBufferFormat::Format8SInt: + case IrBufferFormat::Format16SInt: + case IrBufferFormat::Format8_8SInt: + case IrBufferFormat::Format32SInt: + case IrBufferFormat::Format16_16SInt: + case IrBufferFormat::Format8_8_8_8SInt: + case IrBufferFormat::Format32_32SInt: + case IrBufferFormat::Format16_16_16_16SInt: + case IrBufferFormat::Format32_32_32SInt: + case IrBufferFormat::Format32_32_32_32SInt: return IrTextureNumericClass::Sint; + default: return IrTextureNumericClass::Float; + } +} + } diff --git a/core/shader/recompiler/Recompiler.cpp b/core/shader/recompiler/Recompiler.cpp index 94e8c57abc..a4326a047a 100644 --- a/core/shader/recompiler/Recompiler.cpp +++ b/core/shader/recompiler/Recompiler.cpp @@ -181,7 +181,6 @@ struct ResultMemoEntry { struct EmissionFailure { std::uint64_t codeAddress; BindingLayout layout; - ResourceSpecialization specialization; std::exception_ptr failure; }; @@ -240,6 +239,7 @@ std::shared_ptr getSource(const RecompileRequest& request) { struct SourceKeyStorage {}; auto& key = HostThreadLocal, SourceKeyStorage>(); RecompileCacheKey::Build(request, key); + key.push_back(HostSubgroupSize(request)); const auto find = [&]() -> std::shared_ptr { const auto found = sources.find(key); if (found == sources.end()) return nullptr; @@ -297,11 +297,11 @@ std::uint64_t nextVariantId() { return variants.fetch_add(1, std::memory_order_relaxed) + 1; } -CompiledVariant compileVariant(const RecompileRequest& request, IrProgram program, const ResourceSnapshot& resourceSnapshot, const ResourceSpecialization& resourceSpecialization, std::exception_ptr* emissionFailure = nullptr) { +CompiledVariant compileVariant(const RecompileRequest& request, IrProgram program, std::exception_ptr* emissionFailure = nullptr) { const auto inputInfo = RequestInputInfo(request); constexpr DeadCodeEliminator deadCodeEliminator; constexpr ResourceMaterializer resourceMaterializer; - resourceMaterializer.Apply(program, resourceSpecialization); + resourceMaterializer.ApplyStaticInterface(program); deadCodeEliminator.RemoveIdentities(program); deadCodeEliminator.Eliminate(program); @@ -312,9 +312,6 @@ CompiledVariant compileVariant(const RecompileRequest& request, IrProgram progra constexpr BindingAllocator bindingAllocator; auto bindings = bindingAllocator.Allocate(program, request.layout); - constexpr DescriptorBindingBuilder descriptorBindingBuilder; - descriptorBindingBuilder.Populate(bindings, program, resourceSnapshot, partialThreads(request)); - SpirvTargetOptions targetOptions {}; targetOptions.vulkanVersion = request.target.vulkanVersion; targetOptions.spirvVersion = request.target.spirvVersion; @@ -362,7 +359,7 @@ CompiledVariant compileVariant(const RecompileRequest& request, IrProgram progra } } - return {resourceSpecialization, request.layout, std::move(program).TakeCompiledInfo(), std::move(static_cast(bindings)), std::move(result)}; + return {request.layout, std::move(program).TakeCompiledInfo(), std::move(static_cast(bindings)), std::move(result)}; } RecompileResult materializeResult(const CompiledVariant& variant, const RecompileRequest& request, const ResourceSnapshot& snapshot) { @@ -387,25 +384,24 @@ bool sameLayout(const BindingLayout& left, const BindingLayout& right) { return left.descriptorSet == right.descriptorSet && left.firstBinding == right.firstBinding && left.pushConstantOffsetBytes == right.pushConstantOffsetBytes && left.pushConstantSizeBytes == right.pushConstantSizeBytes; } -std::shared_ptr findOrCompileVariant(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot, const ResourceSpecialization& specialization, bool& cacheHit) { +std::shared_ptr findOrCompileVariant(SourceEntry& source, const RecompileRequest& request, bool& cacheHit) { for (const auto& candidate : source.variants) { - if (sameLayout(candidate->layout, request.layout) && candidate->specialization == specialization) { + if (sameLayout(candidate->layout, request.layout)) { cacheHit = true; return candidate; } } for (const auto& failure : source.emissionFailures) { - if (failure.codeAddress == request.shader.codeAddress && sameLayout(failure.layout, request.layout) && failure.specialization == specialization) std::rethrow_exception(failure.failure); + if (failure.codeAddress == request.shader.codeAddress && sameLayout(failure.layout, request.layout)) std::rethrow_exception(failure.failure); } cacheHit = false; const bool disk = ShaderDiskCache::Enabled() && !DebugProbeActive(); std::vector diskKey; std::shared_ptr variant; if (disk) { - ShaderDiskCache::BuildKey(request, HostSubgroupSize(request), specialization, diskKey); + ShaderDiskCache::BuildKey(request, HostSubgroupSize(request), diskKey); CompiledVariant loaded; if (ShaderDiskCache::Load(diskKey, loaded)) { - loaded.specialization = specialization; loaded.layout = request.layout; loaded.artifact.variantId = nextVariantId(); variant = std::make_shared(std::move(loaded)); @@ -416,9 +412,9 @@ std::shared_ptr findOrCompileVariant(SourceEntry& source, source.program.reset(); std::exception_ptr emissionFailure; try { - variant = std::make_shared(compileVariant(request, std::move(program), snapshot, specialization, &emissionFailure)); + variant = std::make_shared(compileVariant(request, std::move(program), &emissionFailure)); } catch (...) { - if (emissionFailure != nullptr && FailureMemo()) source.emissionFailures.push_back({request.shader.codeAddress, request.layout, specialization, emissionFailure}); + if (emissionFailure != nullptr && FailureMemo()) source.emissionFailures.push_back({request.shader.codeAddress, request.layout, emissionFailure}); throw; } if (disk) ShaderDiskCache::Store(std::move(diskKey), variant); @@ -428,13 +424,12 @@ std::shared_ptr findOrCompileVariant(SourceEntry& source, return variant; } -// The cached variant of `source` for the specialization, compiled on first use. -RecompileResult materializeVariant(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot, const ResourceSpecialization& specialization) { +RecompileResult materializeVariant(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot) { std::shared_ptr variant; bool cacheHit = false; { std::lock_guard lock(source.mutex); - variant = findOrCompileVariant(source, request, snapshot, specialization, cacheHit); + variant = findOrCompileVariant(source, request, cacheHit); } auto result = materializeResult(*variant, request, snapshot); result.cacheHit = cacheHit; @@ -446,18 +441,17 @@ RecompileResult RecompileImpl(const RecompileRequest& request) { RequestMemoryView memory(request.context.memory); const auto runtime = memory.MakeRuntime(request.context.userData, request.shader.codeAddress); ResourceSnapshot snapshot; - ResourceSpecialization specialization; constexpr ResourceMaterializer materializer; if (!request.useCache) { auto program = PrepareResourceProgram(request); const auto plan = materializer.ExtractPlan(program); - materializer.Materialize(plan, runtime, snapshot, specialization); - const auto variant = compileVariant(request, std::move(program), snapshot, specialization); + materializer.Materialize(plan, runtime, snapshot); + const auto variant = compileVariant(request, std::move(program)); return materializeResult(variant, request, snapshot); } const auto source = getSource(request); - materializer.Materialize(*source->plan, runtime, snapshot, specialization); - return materializeVariant(*source, request, snapshot, specialization); + materializer.Materialize(*source->plan, runtime, snapshot); + return materializeVariant(*source, request, snapshot); } // APS5_NO_RESULT_MEMO=1: every Recompile(request, capture) materializes its own result as before. @@ -543,7 +537,7 @@ std::uint64_t snapshotHash(const RecompileRequest& request, const ResourceSnapsh // The memo'd result of `source`'s variant for the snapshot (design13 R5): a hit returns the shared // object, a miss materializes outside the source mutex and inserts (a concurrent miss's object is // as good). `memoHit` reports the hit. -std::shared_ptr materializeMemoized(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot, const ResourceSpecialization& specialization, bool* memoHit) { +std::shared_ptr materializeMemoized(SourceEntry& source, const RecompileRequest& request, const ResourceSnapshot& snapshot, bool* memoHit) { static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr; std::shared_ptr variant; bool cacheHit = false; @@ -552,7 +546,7 @@ std::shared_ptr materializeMemoized(SourceEntry& source, auto& counters = resultMemoCounters(); { std::lock_guard lock(source.mutex); - variant = findOrCompileVariant(source, request, snapshot, specialization, cacheHit); + variant = findOrCompileVariant(source, request, cacheHit); index = (variant->artifact.variantId * 0x9e3779b97f4a7c15ull) ^ hash; const auto found = source.memoIndex.find(index); if (found != source.memoIndex.end() && found->second->variantId == variant->artifact.variantId && found->second->hash == hash) { @@ -601,11 +595,11 @@ std::shared_ptr materializeMemoized(SourceEntry& source, std::shared_ptr RecompileImpl(const RecompileRequest& request, const ResourceCapture& capture, bool* memoHit) { if (!request.useCache || capture.source == nullptr) { auto program = PrepareResourceProgram(request); - const auto variant = compileVariant(request, std::move(program), capture.snapshot, capture.specialization); + const auto variant = compileVariant(request, std::move(program)); return std::make_shared(materializeResult(variant, request, capture.snapshot)); } - if (!ResultMemo()) return std::make_shared(materializeVariant(*capture.source, request, capture.snapshot, capture.specialization)); - return materializeMemoized(*capture.source, request, capture.snapshot, capture.specialization, memoHit); + if (!ResultMemo()) return std::make_shared(materializeVariant(*capture.source, request, capture.snapshot)); + return materializeMemoized(*capture.source, request, capture.snapshot, memoHit); } template @@ -636,12 +630,12 @@ namespace { void materializeCapture(ResourceCapture& capture, const SrtRuntime& runtime) { const auto& plan = *capture.plan; if (std::none_of(plan.pureFlatSlots.begin(), plan.pureFlatSlots.end(), [](std::uint8_t pure) { return pure != 0u; })) { - ResourceMaterializer{}.Materialize(plan, runtime, capture.snapshot, capture.specialization); + ResourceMaterializer{}.Materialize(plan, runtime, capture.snapshot); return; } SrtRuntime traced = runtime; traced.readTrace = &capture.readTrace; - ResourceMaterializer{}.Materialize(plan, traced, capture.snapshot, capture.specialization); + ResourceMaterializer{}.Materialize(plan, traced, capture.snapshot); auto& other = capture.readTrace.otherReads; std::sort(other.begin(), other.end()); other.erase(std::unique(other.begin(), other.end()), other.end()); diff --git a/core/shader/recompiler/Recompiler.hpp b/core/shader/recompiler/Recompiler.hpp index a675fccad9..0db57431a1 100644 --- a/core/shader/recompiler/Recompiler.hpp +++ b/core/shader/recompiler/Recompiler.hpp @@ -438,12 +438,6 @@ struct RecompileResult : CompiledShaderArtifact, ShaderInvocation { [[nodiscard]] RecompileResult Recompile(const RecompileRequest& request); -// The resource plan, snapshot and specialization a driver captured for the request (see -// CaptureResources in Optimization/ResourceProgram.hpp): this overload reuses them instead of -// materializing the request's memory regions again, and is otherwise Recompile(request). The -// result is immutable and shared: a capture that reproduces a snapshot the source's variant was -// materialized over before receives the same object (`memoHit`), so the descriptor population runs -// once per distinct snapshot. APS5_NO_RESULT_MEMO=1 materializes every call. struct ResourceCapture; [[nodiscard]] std::shared_ptr Recompile(const RecompileRequest& request, const ResourceCapture& capture, bool* memoHit = nullptr); diff --git a/core/shader/recompiler/ShaderDiskCache.cpp b/core/shader/recompiler/ShaderDiskCache.cpp index 4c8de1c11f..4e75dc4e93 100644 --- a/core/shader/recompiler/ShaderDiskCache.cpp +++ b/core/shader/recompiler/ShaderDiskCache.cpp @@ -82,8 +82,6 @@ static_assert(sizeof(IrBindingLayout) == 64, "IrBindingLayout changed: update th static_assert(sizeof(IrDescriptorBinding) == 32, "IrDescriptorBinding changed: update the layout encoder"); static_assert(sizeof(BindingAllocationResult) == 120, "BindingAllocationResult changed: update the allocation encoder"); static_assert(sizeof(CompiledBindingLayout) == 72, "CompiledBindingLayout changed: update the allocation encoder"); -static_assert(sizeof(ResourceSpecialization) == 48, "ResourceSpecialization changed: update BuildKey"); -static_assert(sizeof(ResourceSpecialization::Image) == 48, "ResourceSpecialization::Image changed: update BuildKey"); static_assert(sizeof(BindingLayout) == 16, "BindingLayout changed: update BuildKey"); #endif @@ -802,7 +800,7 @@ std::uint64_t SourceVersion() { return Generated::SourceVersion; } -void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, const ResourceSpecialization& specialization, std::vector& key) { +void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, std::vector& key) { key.clear(); Writer writer(key); writer.Value(FileMagic); @@ -818,24 +816,6 @@ void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, c writer.Value(request.layout.firstBinding); writer.Value(request.layout.pushConstantOffsetBytes); writer.Value(request.layout.pushConstantSizeBytes); - writer.List(specialization.images, [](Writer& out, const ResourceSpecialization::Image& image) { - out.Value(image.numericClass); - out.Value(image.dimension); - out.Value(image.mipCount); - out.Value(image.conversionFormat); - out.Value(image.shaderSwizzle); - out.Value(image.indirectRoot); - out.Value(image.indirectMappingOffset); - out.Value(image.indirectSearchIterations); - out.Value(image.cube); - out.Value(image.fmask); - out.Value(image.depthBits); - out.Value(image.depthUnorm16); - out.Value(image.packedFormat); - out.Value(image.emulatedCompare); - out.Value(image.srgbDecode); - }); - writer.Values(std::span(specialization.boundDescriptors)); const auto& switches = switchKey(); key.insert(key.end(), switches.begin(), switches.end()); } diff --git a/core/shader/recompiler/ShaderDiskCache.hpp b/core/shader/recompiler/ShaderDiskCache.hpp index 852f0d72eb..2af9fbf8bd 100644 --- a/core/shader/recompiler/ShaderDiskCache.hpp +++ b/core/shader/recompiler/ShaderDiskCache.hpp @@ -12,7 +12,7 @@ namespace ShaderRecompiler::ShaderDiskCache { -inline constexpr std::uint32_t FormatVersion = 10; +inline constexpr std::uint32_t FormatVersion = 11; enum class LoadStatus { Loaded, @@ -33,7 +33,7 @@ struct Counters { [[nodiscard]] std::uint64_t SourceVersion(); -void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, const ResourceSpecialization& specialization, std::vector& key); +void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, std::vector& key); [[nodiscard]] std::string EntryName(std::span key); diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp index f4b625a8cf..d221ffe2ec 100644 --- a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp @@ -24,8 +24,8 @@ struct SpirvTargetOptions { class SpirvEmitter { public: - [[nodiscard]] std::vector Emit(const IrProgram& program, const BindingAllocationResult& bindings, const SpirvTargetOptions& target) const; - [[nodiscard]] std::vector Emit(const IrProgram& program, const ShaderStageInputInfo& inputInfo, const BindingAllocationResult& bindings, const SpirvTargetOptions& target) const; + [[nodiscard]] std::vector Emit(const IrProgram& program, const CompiledBindingLayout& bindings, const SpirvTargetOptions& target) const; + [[nodiscard]] std::vector Emit(const IrProgram& program, const ShaderStageInputInfo& inputInfo, const CompiledBindingLayout& bindings, const SpirvTargetOptions& target) const; }; diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp index 2e9d0d0d43..9df006f4a1 100644 --- a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterHelpers.hpp @@ -71,7 +71,7 @@ std::uint32_t ConstantBool(SpirvEmitterState& state, bool value); std::uint32_t ConstantU64(SpirvEmitterState& state, std::uint64_t value); std::uint32_t ConstantU32CompositeZero(SpirvEmitterState& state, std::uint32_t components); std::uint32_t DefineInterfaceVariable(SpirvEmitterState& state, std::uint32_t type, std::uint32_t storage, const char* name); -void CheckBindings(const IrProgram& program, const BindingAllocationResult& bindings); +void CheckBindings(const IrProgram& program, const CompiledBindingLayout& bindings); void EmitBaseHeader(SpirvModule& module, const IrProgram& program); void DefineInputs(SpirvEmitterState& state); void DefineOutputs(SpirvEmitterState& state); diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvInterface.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvInterface.hpp index efbab39886..d6eb2bfac4 100644 --- a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvInterface.hpp +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvInterface.hpp @@ -8,8 +8,8 @@ namespace ShaderRecompiler { -void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings); -void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings); +void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const CompiledBindingLayout& bindings); +void EmitModuleHeader(SpirvEmitterState& state, const CompiledBindingLayout& bindings); void DefineModule(SpirvEmitterState& state); std::uint32_t DefineInterfaceVariable(SpirvEmitterState& state, std::uint32_t type, std::uint32_t storage, const char* name); std::uint32_t ExecutionModelForStage(IrShaderStage stage); diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvModuleSetup.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvModuleSetup.hpp index 253791eb67..9f9f948d95 100644 --- a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvModuleSetup.hpp +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvMemory/SpirvModuleSetup.hpp @@ -9,8 +9,8 @@ namespace ShaderRecompiler { -void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings); -void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings); +void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const CompiledBindingLayout& bindings); +void EmitModuleHeader(SpirvEmitterState& state, const CompiledBindingLayout& bindings); void DefineModule(SpirvEmitterState& state); std::uint32_t ExecutionModelForStage(IrShaderStage stage); std::uint32_t DefineInterfaceVariable(SpirvEmitterState& state, std::uint32_t type, std::uint32_t storage, const char* name); diff --git a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvModuleEmitter.hpp b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvModuleEmitter.hpp index 6350bda726..c176eca60f 100644 --- a/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvModuleEmitter.hpp +++ b/core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvModuleEmitter.hpp @@ -9,9 +9,9 @@ namespace ShaderRecompiler { -void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings); +void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const CompiledBindingLayout& bindings); -void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings); +void EmitModuleHeader(SpirvEmitterState& state, const CompiledBindingLayout& bindings); } diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp index 8961edf581..b663800a2f 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp @@ -229,11 +229,11 @@ std::uint32_t SpirvValueEmitContext::Label(const IrBlock* block) const { throw std::runtime_error("SPIR-V emission failed: hash=0x" + std::to_string(state.program.Resources().shaderHash) + " stage=" + std::to_string(static_cast(state.program.Resources().stage)) + " opcode=" + std::string(IrOpcodeName(inst.Opcode())) + " reason=" + reason); } -std::vector SpirvEmitter::Emit(const IrProgram& program, const BindingAllocationResult& bindings, const SpirvTargetOptions& target) const { +std::vector SpirvEmitter::Emit(const IrProgram& program, const CompiledBindingLayout& bindings, const SpirvTargetOptions& target) const { return Emit(program, ShaderStageInputInfo {}, bindings, target); } -std::vector SpirvEmitter::Emit(const IrProgram& program, const ShaderStageInputInfo& inputInfo, const BindingAllocationResult& bindings, const SpirvTargetOptions& target) const { +std::vector SpirvEmitter::Emit(const IrProgram& program, const ShaderStageInputInfo& inputInfo, const CompiledBindingLayout& bindings, const SpirvTargetOptions& target) const { if (program.Resources().stage != IrShaderStage::Compute && program.Resources().stage != IrShaderStage::Vertex && program.Resources().stage != IrShaderStage::Pixel && program.Resources().stage != IrShaderStage::Mesh && program.Resources().stage != IrShaderStage::Local && program.Resources().stage != IrShaderStage::TessellationControl && program.Resources().stage != IrShaderStage::TessellationEvaluation) { FailProgram(program, "binary SPIR-V emitter received an unsupported shader stage"); } diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp index a23ad57231..8f2fcbed7b 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp @@ -125,7 +125,7 @@ std::uint32_t PushConstantBlockType(SpirvEmitterState& state) { } -void CheckBindings(const IrProgram& program, const BindingAllocationResult& bindings) { +void CheckBindings(const IrProgram& program, const CompiledBindingLayout& bindings) { const IrProgramMetadata& metadata = program.Metadata(); if (!metadata.bindingLayoutComplete) { FailEmit("shader binding layout has not been allocated for this program"); @@ -133,59 +133,6 @@ void CheckBindings(const IrProgram& program, const BindingAllocationResult& bind if (!(bindings.layout == metadata.bindings)) { FailEmit("binding allocation result does not match the program's committed binding layout"); } - if (bindings.bindings.size() != bindings.layout.descriptors.size()) { - FailEmit("binding allocation result has a different descriptor count than the binding layout"); - } - const IrShaderStage stage = program.Resources().stage; - for (std::size_t index = 0; index < bindings.layout.descriptors.size(); index++) { - const IrDescriptorBinding& logical = bindings.layout.descriptors[index]; - const DescriptorBinding& physical = bindings.bindings[index]; - const std::uint32_t expectedCount = logical.resources.empty() ? 1u : static_cast(logical.resources.size()); - if (physical.count != expectedCount) { - FailEmit("descriptor binding " + std::to_string(index) + " has an incorrect descriptor count"); - } - if (physical.descriptorSet != 0u) { - FailEmit("descriptor binding " + std::to_string(index) + " is bound to the wrong descriptor set"); - } - if (physical.binding != NativeBinding(stage, logical.kind)) { - FailEmit("descriptor binding " + std::to_string(index) + " is bound to the wrong native binding slot"); - } - DescriptorKind expectedKind = DescriptorKind::StorageBuffer; - DescriptorRole expectedRole = DescriptorRole::GuestBuffers; - if (logical.kind == DescriptorBindingKind::Samplers) { - expectedKind = DescriptorKind::Sampler; - expectedRole = DescriptorRole::GuestSamplers; - } else if (logical.kind == DescriptorBindingKind::Gds) { - expectedRole = DescriptorRole::Gds; - } else if (logical.kind == DescriptorBindingKind::BdaPagetable) { - expectedRole = DescriptorRole::BdaPagetable; - } else if (logical.kind == DescriptorBindingKind::FaultBuffer) { - expectedRole = DescriptorRole::FaultBuffer; - } else if (logical.kind == DescriptorBindingKind::FlattenedSrt) { - expectedRole = DescriptorRole::FlattenedSrt; - } else if (logical.kind == DescriptorBindingKind::ShaderData) { - expectedRole = DescriptorRole::ShaderData; - } else { - const auto imageClass = ImageBindingResourceClass(logical.kind); - if (imageClass == ImageResourceClass::Sampled) { - expectedKind = DescriptorKind::SampledImage; - expectedRole = DescriptorRole::GuestImages; - } else if (imageClass == ImageResourceClass::Storage) { - expectedKind = DescriptorKind::StorageImage; - expectedRole = DescriptorRole::GuestImages; - } else if (logical.kind == DescriptorBindingKind::Buffers) { - expectedRole = DescriptorRole::GuestBuffers; - } else { - FailEmit("descriptor binding " + std::to_string(index) + " has an unmapped binding kind"); - } - } - if (physical.kind != expectedKind) { - FailEmit("descriptor binding " + std::to_string(index) + " has an incorrect descriptor kind"); - } - if (physical.role != expectedRole) { - FailEmit("descriptor binding " + std::to_string(index) + " has an incorrect descriptor role"); - } - } } void EmitBaseHeader(SpirvModule& module, const IrProgram& program) { diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvInputOutput.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvInputOutput.cpp index f2e2bb0920..ff0c2def59 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvInputOutput.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvInputOutput.cpp @@ -1,3 +1,4 @@ +#include "RdnaDecoder/RdnaDescriptorFormat.hpp" #include "SpirvBackend/SpirvMemory/SpirvInputOutput.hpp" #include "SpirvBackend/SpirvEmitterHelpers.hpp" #include "SpirvBackend/SpirvMemory/SpirvTypes.hpp" @@ -88,29 +89,10 @@ namespace ShaderRecompiler if (location >= static_cast(ShaderVertexInputInfo::MaxResources) || location >= static_cast(vertex.resourcesNum)) { return VertexInputScalarKind::Float; } - switch (vertex.resources[location].Format()) { - case IrBufferFormat::Format8UInt: - case IrBufferFormat::Format16UInt: - case IrBufferFormat::Format8_8UInt: - case IrBufferFormat::Format32UInt: - case IrBufferFormat::Format16_16UInt: - case IrBufferFormat::Format8_8_8_8UInt: - case IrBufferFormat::Format32_32UInt: - case IrBufferFormat::Format16_16_16_16UInt: - case IrBufferFormat::Format32_32_32UInt: - case IrBufferFormat::Format32_32_32_32UInt: return VertexInputScalarKind::Uint; - case IrBufferFormat::Format8SInt: - case IrBufferFormat::Format16SInt: - case IrBufferFormat::Format8_8SInt: - case IrBufferFormat::Format32SInt: - case IrBufferFormat::Format16_16SInt: - case IrBufferFormat::Format8_8_8_8SInt: - case IrBufferFormat::Format32_32SInt: - case IrBufferFormat::Format16_16_16_16SInt: - case IrBufferFormat::Format32_32_32SInt: - case IrBufferFormat::Format32_32_32_32SInt: return VertexInputScalarKind::Sint; - default: return VertexInputScalarKind::Float; - } + const auto numeric = VertexInputNumericClass(vertex.resources[location].Format()); + if (numeric == IrTextureNumericClass::Uint) return VertexInputScalarKind::Uint; + if (numeric == IrTextureNumericClass::Sint) return VertexInputScalarKind::Sint; + return VertexInputScalarKind::Float; } std::uint32_t VertexParameterComponentCount(const SpirvInputBinding& input) { diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp index 49f70b1f37..2689e24de5 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp @@ -11,12 +11,12 @@ namespace ShaderRecompiler { -void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const BindingAllocationResult& bindings) { +void EmitModuleHeader(SpirvModule& module, const IrProgram& program, const CompiledBindingLayout& bindings) { CheckBindings(program, bindings); EmitBaseHeader(module, program); } -void EmitModuleHeader(SpirvEmitterState& state, const BindingAllocationResult& bindings) { +void EmitModuleHeader(SpirvEmitterState& state, const CompiledBindingLayout& bindings) { CheckBindings(state.program, bindings); DefineModule(state); }