mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-25 12:26:51 +01:00
cda236dbb3
SDWA source extraction and destination insertion are independent. Cover the reported word1-to-word0 preserve form and byte-source partial destinations in compute tests. Verify 64 distinct compute buffers survive resource tracking and binding layout, and remove the obsolete CFG rejection.
(cherry picked from commit 9e74df7dc0)
606 lines
21 KiB
C++
606 lines
21 KiB
C++
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERIR_H_
|
|
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERIR_H_
|
|
|
|
#include "common/common.h"
|
|
#include "common/stringUtils.h"
|
|
#include "graphics/guest_gpu/gpu_defs.h"
|
|
#include "graphics/guest_gpu/gpu_format.h"
|
|
#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h"
|
|
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
|
|
#include "graphics/shader/recompiler/ir/Block.h"
|
|
#include "graphics/shader/recompiler/ir/ResourceSnapshot.h"
|
|
#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.h"
|
|
#include "graphics/shader/shader.h"
|
|
|
|
#include <array>
|
|
#include <bit>
|
|
#include <deque>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
namespace Libs::Graphics::ShaderRecompiler::IR {
|
|
|
|
enum class ResourceKind {
|
|
None,
|
|
ScalarBuffer,
|
|
ScalarAddress,
|
|
Buffer,
|
|
IndirectBuffer,
|
|
Flat,
|
|
Global,
|
|
Scratch,
|
|
Lds,
|
|
Gds,
|
|
Image,
|
|
Sampler
|
|
};
|
|
|
|
[[nodiscard]] constexpr bool IsAddressResourceKind(ResourceKind kind) {
|
|
return kind == ResourceKind::ScalarAddress || kind == ResourceKind::Flat ||
|
|
kind == ResourceKind::Global || kind == ResourceKind::Scratch;
|
|
}
|
|
|
|
struct MemoryInfo {
|
|
ResourceKind kind = ResourceKind::None;
|
|
uint32_t resource = 0;
|
|
uint32_t sampler = 0;
|
|
uint32_t offset = 0;
|
|
uint32_t secondary_offset = 0;
|
|
uint32_t dmask = 0;
|
|
uint32_t data_dwords = 1;
|
|
uint32_t data_bits = 32;
|
|
uint32_t component_index = 0;
|
|
uint32_t component_count = 1;
|
|
uint32_t data_format = 0;
|
|
uint32_t number_format = 0;
|
|
uint32_t image_sample_flags = 0;
|
|
Decoder::ImageDimension image_dimension = Decoder::ImageDimension::Unknown;
|
|
uint32_t image_address_components = 0;
|
|
bool address_is_full = false;
|
|
bool data_signed = false;
|
|
bool typed = false;
|
|
bool formatted = false;
|
|
bool image_has_mip = false;
|
|
bool image_r128 = false;
|
|
bool idxen = false;
|
|
bool offen = false;
|
|
bool coherent = false;
|
|
bool planning_only = false;
|
|
|
|
[[nodiscard]] bool SupportsIndirectBufferLoad(ValueOpcode opcode) const {
|
|
return !formatted && !typed && data_bits == 32u &&
|
|
(opcode == ValueOpcode::LoadBufferU32x2 || opcode == ValueOpcode::LoadBufferU32x3 ||
|
|
opcode == ValueOpcode::LoadBufferU32x4);
|
|
}
|
|
|
|
bool operator==(const MemoryInfo& other) const = default;
|
|
};
|
|
|
|
enum class ExportTargetKind { Unknown, Null, Position, Primitive, Parameter, Mrt, MrtZ };
|
|
|
|
struct ExportInfo {
|
|
ExportTargetKind kind = ExportTargetKind::Unknown;
|
|
uint32_t target = 0;
|
|
uint32_t index = 0;
|
|
uint32_t en = 0;
|
|
bool done = false;
|
|
bool compr = false;
|
|
bool vm = false;
|
|
|
|
bool operator==(const ExportInfo& other) const = default;
|
|
};
|
|
|
|
struct BufferResource {
|
|
static constexpr uint32_t NoImageAlias = UINT32_MAX;
|
|
|
|
uint32_t source = 0;
|
|
uint32_t first_use_pc = 0;
|
|
uint32_t max_byte_extent = 0;
|
|
uint32_t packed_stride = 0;
|
|
Prospero::BufferFormat descriptor_format = Prospero::BufferFormat::kInvalid;
|
|
uint32_t descriptor_swizzle = DstSel(4, 5, 6, 7);
|
|
uint32_t image_alias = NoImageAlias;
|
|
bool read = false;
|
|
bool written = false;
|
|
bool atomic = false;
|
|
bool formatted = false;
|
|
bool scalar = false;
|
|
|
|
bool operator==(const BufferResource& other) const = default;
|
|
};
|
|
|
|
enum class ImageMipMode { None, DynamicStorage };
|
|
|
|
constexpr uint32_t ShaderImageIdentitySwizzle = 0x00000facu;
|
|
|
|
struct ImageResource {
|
|
static constexpr uint32_t NoIndirectImage = UINT32_MAX;
|
|
|
|
uint32_t source = 0;
|
|
uint32_t first_use_pc = 0;
|
|
ImageResourceClass resource_class = ImageResourceClass::None;
|
|
Prospero::TextureNumericClass numeric_class = Prospero::TextureNumericClass::Unsupported;
|
|
Decoder::ImageDimension dimension = Decoder::ImageDimension::Unknown;
|
|
ImageMipMode mip_mode = ImageMipMode::None;
|
|
uint32_t mip_count = 1;
|
|
Prospero::BufferFormat conversion_format = Prospero::BufferFormat::kInvalid;
|
|
uint32_t shader_swizzle = ShaderImageIdentitySwizzle;
|
|
bool read = false;
|
|
bool written = false;
|
|
bool atomic = false;
|
|
bool depth_compare = false;
|
|
bool cube = false;
|
|
bool r128 = false;
|
|
uint32_t indirect_root = NoIndirectImage;
|
|
uint32_t indirect_mapping_offset = 0;
|
|
uint32_t indirect_search_iterations = 0;
|
|
std::vector<uint32_t> indirect_resources;
|
|
|
|
bool operator==(const ImageResource& other) const = default;
|
|
};
|
|
|
|
struct SamplerResource {
|
|
uint32_t source = 0;
|
|
uint32_t first_use_pc = 0;
|
|
bool force_point_filtering = false;
|
|
bool depth_compare = false;
|
|
|
|
bool operator==(const SamplerResource& other) const = default;
|
|
};
|
|
|
|
struct SampledResourcePair {
|
|
uint32_t image = 0;
|
|
uint32_t sampler = 0;
|
|
uint32_t first_use_pc = 0;
|
|
|
|
bool operator==(const SampledResourcePair& other) const = default;
|
|
};
|
|
|
|
enum class TessellationAttribute {
|
|
LocalOutput,
|
|
ControlInput,
|
|
ControlOutput,
|
|
EvaluationInput,
|
|
PatchOutput,
|
|
Factor
|
|
};
|
|
|
|
enum class StageInputKind {
|
|
VertexIndex,
|
|
InvocationId,
|
|
PrimitiveId,
|
|
TessCoord,
|
|
InstanceIndex,
|
|
FragCoord,
|
|
FrontFacing,
|
|
PackedAncillary,
|
|
Layer,
|
|
SampleId,
|
|
BaryCoordSmooth,
|
|
BaryCoordSmoothCentroid,
|
|
BaryCoordNoPerspective,
|
|
WorkgroupId,
|
|
LocalInvocationId,
|
|
LocalInvocationIndex,
|
|
GlobalInvocationId,
|
|
Parameter,
|
|
};
|
|
|
|
enum class StageOutputKind {
|
|
Position,
|
|
Parameter,
|
|
Mrt,
|
|
Depth,
|
|
SampleMask,
|
|
PointSize,
|
|
ClipDistance,
|
|
CullDistance,
|
|
Layer,
|
|
ViewportIndex
|
|
};
|
|
|
|
struct PositionExportComponent {
|
|
uint32_t clip_distance = UINT32_MAX;
|
|
uint32_t cull_distance = UINT32_MAX;
|
|
bool point_size = false;
|
|
bool layer = false;
|
|
bool viewport = false;
|
|
};
|
|
|
|
inline PositionExportComponent DecodePositionExportComponent(uint32_t control,
|
|
uint32_t pos_index,
|
|
uint32_t component) {
|
|
PositionExportComponent result;
|
|
if (pos_index == 0 || component >= 4) {
|
|
return result;
|
|
}
|
|
|
|
uint32_t slot = pos_index - 1;
|
|
uint32_t vector = 3;
|
|
for (uint32_t i = 0; i < 3; i++) {
|
|
if ((control & (1u << (21u + i))) != 0) {
|
|
if (slot == 0) {
|
|
vector = i;
|
|
break;
|
|
}
|
|
slot--;
|
|
}
|
|
}
|
|
if (vector == 3) {
|
|
return result;
|
|
}
|
|
|
|
if (vector == 0) {
|
|
result.point_size = component == 0 && (control & (1u << 16u)) != 0;
|
|
result.layer = component == 2 && (control & (1u << 18u)) != 0;
|
|
result.viewport = component == 2 && (control & (1u << 19u)) != 0;
|
|
return result;
|
|
}
|
|
|
|
const auto scalar = (vector - 1) * 4 + component;
|
|
const auto lower = (1u << scalar) - 1u;
|
|
const auto clip = control & 0xffu;
|
|
const auto cull = (control >> 8u) & 0xffu;
|
|
if ((clip & (1u << scalar)) != 0) {
|
|
result.clip_distance = std::popcount(clip & lower);
|
|
}
|
|
if ((cull & (1u << scalar)) != 0) {
|
|
result.cull_distance = std::popcount(cull & lower);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct StageInput {
|
|
StageInputKind kind = StageInputKind::VertexIndex;
|
|
uint32_t location = 0;
|
|
uint32_t component_count = 1;
|
|
std::string debug_name;
|
|
bool per_vertex = false;
|
|
|
|
bool operator==(const StageInput& other) const = default;
|
|
};
|
|
|
|
struct StageOutput {
|
|
StageOutputKind kind = StageOutputKind::Parameter;
|
|
uint32_t index = 0;
|
|
uint32_t location = 0;
|
|
std::string debug_name;
|
|
|
|
bool operator==(const StageOutput& other) const = default;
|
|
};
|
|
|
|
inline constexpr uint32_t FirstImageBinding = 1u;
|
|
inline constexpr uint32_t FirstComparisonImageBinding = 22u;
|
|
inline constexpr uint32_t FirstStorageImageBinding = 29u;
|
|
inline constexpr uint32_t ImageBindingCount = 43u;
|
|
|
|
enum class DescriptorBindingKind : uint32_t {
|
|
Buffers = 0u,
|
|
Samplers = FirstImageBinding + ImageBindingCount,
|
|
Gds,
|
|
BdaPagetable,
|
|
FaultBuffer,
|
|
FlattenedSrt,
|
|
ShaderData,
|
|
Count,
|
|
};
|
|
|
|
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Samplers) == 44u);
|
|
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Count) == 50u);
|
|
|
|
struct PushData {
|
|
static constexpr uint32_t DwordCount = 32;
|
|
static constexpr uint32_t MeshDrawDwordCount = 6;
|
|
static constexpr uint32_t NoStart = UINT32_MAX;
|
|
std::array<uint32_t, DwordCount> dwords {};
|
|
|
|
[[nodiscard]] static constexpr bool CanFit(uint32_t start, uint32_t size) {
|
|
return size != 0 && start <= DwordCount && size <= DwordCount - start;
|
|
}
|
|
[[nodiscard]] static constexpr uint32_t StartFor(uint32_t cursor, uint32_t size) {
|
|
return CanFit(cursor, size) ? cursor : NoStart;
|
|
}
|
|
};
|
|
|
|
static_assert(sizeof(PushData) == 128);
|
|
constexpr uint32_t NativePushConstantSize = sizeof(PushData);
|
|
|
|
[[nodiscard]] constexpr uint32_t NativeBinding(ShaderType stage, DescriptorBindingKind kind) {
|
|
const uint32_t group = stage == ShaderType::Pixel ? 1u
|
|
: stage == ShaderType::TessellationControl ? 2u
|
|
: stage == ShaderType::TessellationEvaluation ? 3u
|
|
: 0u;
|
|
return static_cast<uint32_t>(kind) +
|
|
group * static_cast<uint32_t>(DescriptorBindingKind::Count);
|
|
}
|
|
|
|
[[nodiscard]] constexpr ImageResourceClass ImageBindingResourceClass(DescriptorBindingKind kind) {
|
|
const auto value = static_cast<uint32_t>(kind);
|
|
if (value >= FirstImageBinding && value < FirstStorageImageBinding) {
|
|
return ImageResourceClass::Sampled;
|
|
}
|
|
if (value >= FirstStorageImageBinding &&
|
|
value < static_cast<uint32_t>(DescriptorBindingKind::Samplers)) {
|
|
return ImageResourceClass::Storage;
|
|
}
|
|
return ImageResourceClass::None;
|
|
}
|
|
|
|
[[nodiscard]] constexpr uint32_t ImageBindingIndex(DescriptorBindingKind kind) {
|
|
return static_cast<uint32_t>(kind) - FirstImageBinding;
|
|
}
|
|
|
|
[[nodiscard]] constexpr std::optional<DescriptorBindingKind>
|
|
DescriptorBindingForImage(const ImageResource& image) {
|
|
constexpr uint32_t SampledFloatBinding = 1u;
|
|
constexpr uint32_t SampledUintBinding = 8u;
|
|
constexpr uint32_t SampledSintBinding = 15u;
|
|
constexpr uint32_t StorageFloatBinding = FirstStorageImageBinding;
|
|
constexpr uint32_t StorageUintBinding = StorageFloatBinding + 5u;
|
|
constexpr uint32_t AtomicUintBinding = StorageUintBinding + 5u;
|
|
|
|
uint32_t base = 0;
|
|
bool sampled = false;
|
|
if (image.resource_class == ImageResourceClass::Sampled) {
|
|
if (image.atomic) {
|
|
return std::nullopt;
|
|
}
|
|
sampled = true;
|
|
switch (image.numeric_class) {
|
|
case Prospero::TextureNumericClass::Float:
|
|
base = image.depth_compare ? FirstComparisonImageBinding : SampledFloatBinding;
|
|
break;
|
|
case Prospero::TextureNumericClass::Uint: base = SampledUintBinding; break;
|
|
case Prospero::TextureNumericClass::Sint: base = SampledSintBinding; break;
|
|
case Prospero::TextureNumericClass::Unsupported: return std::nullopt;
|
|
default: return std::nullopt;
|
|
}
|
|
if (image.depth_compare && image.numeric_class != Prospero::TextureNumericClass::Float) {
|
|
return std::nullopt;
|
|
}
|
|
} else if (image.resource_class == ImageResourceClass::Storage) {
|
|
if (image.atomic) {
|
|
if (image.numeric_class != Prospero::TextureNumericClass::Uint) {
|
|
return std::nullopt;
|
|
}
|
|
base = AtomicUintBinding;
|
|
} else {
|
|
switch (image.numeric_class) {
|
|
case Prospero::TextureNumericClass::Float: base = StorageFloatBinding; break;
|
|
case Prospero::TextureNumericClass::Uint: base = StorageUintBinding; break;
|
|
case Prospero::TextureNumericClass::Sint:
|
|
case Prospero::TextureNumericClass::Unsupported: return std::nullopt;
|
|
default: return std::nullopt;
|
|
}
|
|
}
|
|
} else {
|
|
return std::nullopt;
|
|
}
|
|
|
|
uint32_t dimension = 0;
|
|
switch (image.dimension) {
|
|
case Decoder::ImageDimension::Dim1D: break;
|
|
case Decoder::ImageDimension::Dim1DArray: dimension = 1u; break;
|
|
case Decoder::ImageDimension::Dim2D: dimension = 2u; break;
|
|
case Decoder::ImageDimension::Dim2DArray: dimension = 3u; break;
|
|
case Decoder::ImageDimension::Dim2DMsaa:
|
|
if (!sampled) {
|
|
return std::nullopt;
|
|
}
|
|
dimension = 4u;
|
|
break;
|
|
case Decoder::ImageDimension::Dim2DMsaaArray:
|
|
if (!sampled) {
|
|
return std::nullopt;
|
|
}
|
|
dimension = 5u;
|
|
break;
|
|
case Decoder::ImageDimension::Dim3D: dimension = sampled ? 6u : 4u; break;
|
|
case Decoder::ImageDimension::Unknown: return std::nullopt;
|
|
default: return std::nullopt;
|
|
}
|
|
return static_cast<DescriptorBindingKind>(base + dimension);
|
|
}
|
|
|
|
struct DescriptorBinding {
|
|
DescriptorBindingKind kind = DescriptorBindingKind::Buffers;
|
|
std::vector<uint32_t> resources;
|
|
|
|
bool operator==(const DescriptorBinding& other) const = default;
|
|
};
|
|
|
|
struct BindingLayout {
|
|
uint32_t push_data_start_dword = PushData::NoStart;
|
|
uint32_t memory_offset_dword = 0;
|
|
uint32_t memory_offset_count = 0;
|
|
std::vector<uint32_t> user_data_registers;
|
|
std::vector<DescriptorBinding> descriptors;
|
|
|
|
[[nodiscard]] uint32_t ShaderDataDwords() const {
|
|
return memory_offset_dword + (memory_offset_count + 3u) / 4u;
|
|
}
|
|
[[nodiscard]] bool UsesPushData() const {
|
|
return push_data_start_dword != PushData::NoStart;
|
|
}
|
|
void AdvancePushData(uint32_t& cursor) const {
|
|
if (UsesPushData()) {
|
|
cursor = push_data_start_dword + ShaderDataDwords();
|
|
}
|
|
}
|
|
|
|
bool operator==(const BindingLayout& other) const = default;
|
|
};
|
|
|
|
struct ShaderInfo {
|
|
static constexpr uint32_t MaxBuffers = 64;
|
|
static constexpr uint32_t MaxImages = 64;
|
|
static constexpr uint32_t MaxSamplers = 32;
|
|
static constexpr uint32_t MaxSampledPairs = 64;
|
|
|
|
std::vector<BufferResource> buffers;
|
|
std::vector<ImageResource> images;
|
|
std::vector<SamplerResource> samplers;
|
|
std::vector<SampledResourcePair> sampled_pairs;
|
|
std::vector<StageInput> inputs;
|
|
std::vector<StageOutput> outputs;
|
|
std::array<uint8_t, 32> vertex_fetch_components {};
|
|
int32_t vertex_offset_sgpr = -1;
|
|
int32_t instance_offset_sgpr = -1;
|
|
bool has_bitwise_xor = false;
|
|
bool uses_dma = false;
|
|
|
|
bool operator==(const ShaderInfo& other) const = default;
|
|
};
|
|
|
|
struct BlockInfo {
|
|
uint32_t id = 0;
|
|
uint32_t start_pc = 0;
|
|
uint32_t end_pc = 0;
|
|
CFG::Terminator terminator;
|
|
Value condition;
|
|
Value indirect_target;
|
|
};
|
|
|
|
struct DescriptorSource {
|
|
struct IndirectImage {
|
|
uint32_t material_source = UINT32_MAX;
|
|
uint32_t table_source = 0;
|
|
uint32_t selector_stride = 0;
|
|
uint32_t selector_offset = 0;
|
|
uint32_t table_offset = 0;
|
|
Value key_count;
|
|
Value selector_mask;
|
|
|
|
bool operator==(const IndirectImage& other) const = default;
|
|
};
|
|
|
|
std::array<Value, 8> dwords {};
|
|
uint32_t dword_count = 0;
|
|
std::optional<IndirectImage> indirect_image;
|
|
|
|
bool operator==(const DescriptorSource& other) const = default;
|
|
};
|
|
|
|
struct SrtRead {
|
|
Value value;
|
|
uint32_t flat_offset = 0;
|
|
|
|
bool operator==(const SrtRead& other) const = default;
|
|
};
|
|
|
|
struct ResourceBlock {
|
|
// Conditional successors are ordered true, false; an empty condition follows every edge.
|
|
Value condition;
|
|
std::vector<uint32_t> successors;
|
|
std::vector<uint32_t> sources;
|
|
};
|
|
|
|
// Stable shader metadata consumed by the renderer after native IR has been discarded.
|
|
struct CompiledShaderInfo {
|
|
ShaderType stage = ShaderType::Unknown;
|
|
uint64_t shader_hash = 0;
|
|
uint32_t wave_size = 64;
|
|
uint32_t user_data_base = 0;
|
|
uint32_t user_data_count = 64;
|
|
uint32_t scratch_dwords = 0;
|
|
uint32_t param_export_mask = 0;
|
|
ShaderInfo info;
|
|
BindingLayout bindings;
|
|
};
|
|
|
|
struct UniformFillPlan {
|
|
UniformFill fill;
|
|
std::array<Value, 4> values;
|
|
};
|
|
|
|
// Resource analysis retained by the shader cache. It owns immutable descriptor/SRT,
|
|
// condition and fill values without translated blocks, plus reusable evaluation scratch.
|
|
struct ResourcePlan {
|
|
struct EvaluationContext {
|
|
struct Entry {
|
|
uint64_t value = 0;
|
|
uint64_t generation = 0;
|
|
};
|
|
|
|
std::vector<Entry> values;
|
|
uint64_t generation = 0;
|
|
};
|
|
|
|
ResourcePlan() = default;
|
|
~ResourcePlan();
|
|
|
|
ResourcePlan(const ResourcePlan&) = delete;
|
|
ResourcePlan& operator=(const ResourcePlan&) = delete;
|
|
ResourcePlan(ResourcePlan&&) noexcept = default;
|
|
ResourcePlan& operator=(ResourcePlan&& other) noexcept;
|
|
|
|
ShaderType stage = ShaderType::Unknown;
|
|
uint64_t shader_hash = 0;
|
|
uint32_t user_data_base = 0;
|
|
uint32_t user_data_count = 64;
|
|
std::list<Inst> value_storage;
|
|
std::vector<MemoryInfo> memory_info;
|
|
std::vector<DescriptorSource> descriptor_sources;
|
|
std::vector<ResourceBlock> control_flow;
|
|
std::vector<SrtRead> srt_reads;
|
|
std::vector<uint8_t> clean_flat_slots;
|
|
bool requires_specialization_memory = false;
|
|
bool has_address_writes = false;
|
|
bool srt_plan_complete = false;
|
|
bool resource_tracking_complete = false;
|
|
ShaderInfo info;
|
|
UniformFillPlan uniform_fill;
|
|
// GPU-thread scratch for nested clean/EXEC memos, activity and material keys.
|
|
mutable std::deque<EvaluationContext> evaluation_contexts;
|
|
mutable uint32_t evaluation_value_count = 0;
|
|
mutable uint32_t evaluation_depth = 0;
|
|
mutable std::vector<uint8_t> active_sources;
|
|
mutable std::vector<uint8_t> visited_blocks;
|
|
mutable std::vector<uint32_t> pending_blocks;
|
|
mutable std::vector<uint32_t> material_keys;
|
|
mutable std::vector<std::pair<uint64_t, uint64_t>> specialization_reads;
|
|
};
|
|
|
|
struct Program: ResourcePlan {
|
|
Program() = default;
|
|
~Program();
|
|
|
|
Program(const Program&) = delete;
|
|
Program& operator=(const Program&) = delete;
|
|
Program(Program&&) noexcept = default;
|
|
Program& operator=(Program&& other) noexcept;
|
|
CompiledShaderInfo TakeCompiledInfo() &&;
|
|
|
|
std::vector<std::unique_ptr<Block>> block_storage;
|
|
BlockList blocks;
|
|
uint32_t wave_size = 64;
|
|
uint32_t scratch_dwords = 0;
|
|
bool dispatcher_fallback = false;
|
|
CFG::FailureKind cfg_failure_kind = CFG::FailureKind::None;
|
|
std::string fallback_reason;
|
|
std::vector<BlockInfo> block_info;
|
|
// Typed memory and export instructions reference shader-local metadata by dense index.
|
|
// Decoder-only details (such as NSA register numbers) have already become IR operands.
|
|
std::vector<ExportInfo> export_info;
|
|
std::vector<Value> dynamic_reads;
|
|
bool shader_info_complete = false;
|
|
BindingLayout bindings;
|
|
bool binding_layout_complete = false;
|
|
|
|
};
|
|
|
|
std::string ProgramToString(const Program& program);
|
|
bool HasShaderMemoryWrites(const Program& program);
|
|
|
|
void ValidateProgram(const Program& program, bool require_ssa);
|
|
void ResolveControlFlowIdentities(Program& program);
|
|
bool EquivalentValue(const ResourcePlan& program, Value left, Value right);
|
|
Value ResolveInvariantPhi(const ResourcePlan& program, Value value);
|
|
|
|
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
|
|
|
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SHADERIR_H_ */
|