mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
beb8047083 | ||
|
|
1df06e230d | ||
|
|
39a34fa343 | ||
|
|
96c64d113e | ||
|
|
66ca225c76 | ||
|
|
0de4cfbf80 | ||
|
|
42980fe6df | ||
|
|
1592cf2b1b | ||
|
|
a9166f8546 | ||
|
|
b77978fd1e | ||
|
|
4330cb9462 | ||
|
|
af8aecfdde | ||
|
|
13533bd053 | ||
|
|
cd0f0a428a | ||
|
|
6758d67942 | ||
|
|
dc5b7c19a6 | ||
|
|
2a54306e8b | ||
|
|
7a6aaafe52 | ||
|
|
1469c53048 | ||
|
|
9992ab18fc | ||
|
|
18af08460f | ||
|
|
39ee9972b8 |
No files matched your search
@@ -47,6 +47,10 @@ PresentMode GetPresentMode() {
|
||||
return g_config->present_mode;
|
||||
}
|
||||
|
||||
int32_t GetGpuIndex() {
|
||||
return g_config->gpu_index;
|
||||
}
|
||||
|
||||
bool FullscreenEnabled() {
|
||||
return g_config->fullscreen_enabled;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ struct ConfigOptions {
|
||||
std::string user_name = "Kyty";
|
||||
int32_t user_id = DEFAULT_USER_ID;
|
||||
PresentMode present_mode = PresentMode::Fifo;
|
||||
int32_t gpu_index = -1;
|
||||
bool fullscreen_enabled = false;
|
||||
uint32_t vblank_frequency = 60;
|
||||
uint32_t console_language = DEFAULT_CONSOLE_LANGUAGE;
|
||||
@@ -80,6 +81,7 @@ uint32_t GetScreenHeight();
|
||||
const std::string& GetUserName();
|
||||
int32_t GetUserId();
|
||||
PresentMode GetPresentMode();
|
||||
int32_t GetGpuIndex();
|
||||
bool FullscreenEnabled();
|
||||
uint32_t GetVblankFrequency();
|
||||
uint32_t GetConsoleLanguage();
|
||||
|
||||
@@ -23,9 +23,8 @@ constexpr auto MakeContextDispatchTable() {
|
||||
g_hw_ctx_func[Pm4::DB_STENCIL_CLEAR] = HwCtxSetStencilClear;
|
||||
g_hw_ctx_func[Pm4::DB_DEPTH_CLEAR] = HwCtxSetDepthClear;
|
||||
g_hw_ctx_func[Pm4::PA_SC_SCREEN_SCISSOR_TL] = HwCtxSetScreenScissor;
|
||||
g_hw_ctx_func[Pm4::DB_Z_INFO] = HwCtxSetDepthRenderTarget;
|
||||
g_hw_ctx_func[Pm4::DB_STENCIL_INFO] = HwCtxSetStencilInfo;
|
||||
g_hw_ctx_func[Pm4::DB_HTILE_SURFACE] = HwCtxSetDepthHtileSurface;
|
||||
g_hw_ctx_func[Pm4::DB_SHADING_RATE_ENCODING] = HwCtxSetDepthShadingRateEncoding;
|
||||
g_hw_ctx_func[Pm4::PA_SU_HARDWARE_SCREEN_OFFSET] = HwCtxSetHardwareScreenOffset;
|
||||
g_hw_ctx_func[Pm4::PA_SC_WINDOW_OFFSET] = HwCtxSetWindowOffset;
|
||||
g_hw_ctx_func[Pm4::PA_SC_WINDOW_SCISSOR_TL] = HwCtxSetWindowScissor;
|
||||
@@ -75,7 +74,6 @@ constexpr auto MakeContextDispatchTable() {
|
||||
g_hw_ctx_func[Pm4::PA_SC_BINNER_CNTL_0] = HwCtxSetPaScExtendedControl;
|
||||
g_hw_ctx_func[Pm4::PA_SC_BINNER_CNTL_1] = HwCtxSetPaScExtendedControl;
|
||||
g_hw_ctx_func[Pm4::PA_SC_CONSERVATIVE_RASTERIZATION_CNTL] = HwCtxSetPaScExtendedControl;
|
||||
g_hw_ctx_func[Pm4::PA_SC_NGG_MODE_CNTL] = HwCtxSetPaScExtendedControl;
|
||||
g_hw_ctx_func[Pm4::DB_ALPHA_TO_MASK] = HwCtxSetAlphaToMask;
|
||||
g_hw_ctx_func[Pm4::VGT_DRAW_PAYLOAD_CNTL] = HwCtxSetDrawPayloadControl;
|
||||
g_hw_ctx_func[Pm4::VGT_PRIMITIVEID_RESET] = HwCtxSetPrimitiveIdReset;
|
||||
@@ -83,7 +81,6 @@ constexpr auto MakeContextDispatchTable() {
|
||||
g_hw_ctx_func[Pm4::VGT_SHADER_STAGES_EN] = HwCtxSetShaderStages;
|
||||
|
||||
for (uint32_t slot = 0; slot < 8; slot++) {
|
||||
g_hw_ctx_func[Pm4::CB_COLOR0_BASE + slot * 15] = HwCtxSetRenderTarget;
|
||||
g_hw_ctx_func[Pm4::CB_COLOR0_INFO + slot * 15] = HwCtxSetColorInfo;
|
||||
|
||||
g_hw_ctx_func[Pm4::CB_BLEND0_CONTROL + slot * 1] = HwCtxSetBlendControl;
|
||||
@@ -116,25 +113,21 @@ constexpr auto MakeShaderDispatchTable() {
|
||||
}
|
||||
|
||||
for (uint32_t slot = 0; slot < 4; slot++) {
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_PS_0 + slot] = HwShSetPsUserAccumSgpr;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_PS_0 + slot] = HwShIgnoreUserAccumulator;
|
||||
}
|
||||
|
||||
for (uint32_t slot = 0; slot < 4; slot++) {
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_ESGS_0 + slot] = HwShSetGsUserAccumSgpr;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_ESGS_0 + slot] = HwShIgnoreUserAccumulator;
|
||||
}
|
||||
for (uint32_t slot = 0; slot < 4; slot++) {
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_LSHS_0 + slot] = HwShSetHsUserAccumSgpr;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_ACCUM_LSHS_0 + slot] = HwShIgnoreUserAccumulator;
|
||||
}
|
||||
|
||||
for (uint32_t slot = 0; Pm4::COMPUTE_USER_ACCUM_0 + slot < Pm4::COMPUTE_USER_DATA_0; slot++) {
|
||||
g_hw_sh_func[Pm4::COMPUTE_USER_ACCUM_0 + slot] = HwShSetCsUserAccumSgpr;
|
||||
for (uint32_t slot = 0; slot < 4; slot++) {
|
||||
g_hw_sh_func[Pm4::COMPUTE_USER_ACCUM_0 + slot] = HwShIgnoreUserAccumulator;
|
||||
}
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_LO] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_HI] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TBA_LO] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TBA_HI] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TMA_LO] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TMA_HI] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_RSRC1] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_RSRC2] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_START_X] = HwShSetCsRegisters;
|
||||
@@ -144,30 +137,16 @@ constexpr auto MakeShaderDispatchTable() {
|
||||
g_hw_sh_func[Pm4::COMPUTE_NUM_THREAD_Y] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_NUM_THREAD_Z] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_RESOURCE_LIMITS] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DESTINATION_EN_SE0] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DESTINATION_EN_SE1] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_TMPRING_SIZE] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DESTINATION_EN_SE2] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DESTINATION_EN_SE3] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PGM_RSRC3] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_SHADER_CHKSUM] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_DISPATCH_TUNNEL] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_TBA_LO_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC3_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC4_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_TMA_LO_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_TMA_HI_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_REQ_CTRL_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC3_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC4_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::COMPUTE_PACE_ID] = HwShSetCsRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_PS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_GS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_REQ_CTRL_ESGS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC3_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_PGM_RSRC4_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_GRAPHICS_SHADER_CONTROL_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_LO_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_USER_DATA_ADDR_HI_HS] = HwShIgnoreRegisters;
|
||||
g_hw_sh_func[Pm4::SPI_SHADER_REQ_CTRL_LSHS] = HwShIgnoreRegisters;
|
||||
|
||||
return g_hw_sh_func;
|
||||
}
|
||||
@@ -179,6 +158,7 @@ constexpr auto MakeUconfigDispatchTable() {
|
||||
g_hw_uc_func[Pm4::VGT_INDEX_TYPE] = HwUcSetIndexType;
|
||||
g_hw_uc_func[Pm4::VGT_OBJECT_ID] = HwUcSetObjectId;
|
||||
g_hw_uc_func[Pm4::TEXTURE_GRADIENT_FACTORS] = HwUcSetTextureGradientFactors;
|
||||
g_hw_uc_func[Pm4::TEXTURE_GRADIENT_CONTROL] = HwUcSetTextureGradientFactors;
|
||||
g_hw_uc_func[Pm4::IA_MULTI_VGT_PARAM] = HwUcSetIaMultiVgtParam;
|
||||
g_hw_uc_func[Pm4::GE_MULTI_PRIM_IB_RESET_EN] = HwUcSetMultiPrimIbReset;
|
||||
g_hw_uc_func[Pm4::TA_CS_BC_BASE_ADDR] = HwUcSetBorderColorTableAddr;
|
||||
|
||||
@@ -44,11 +44,9 @@ uint32_t HwCtxSetBorderColorTableAddr(CommandProcessor&, uint32_t, uint32_t, con
|
||||
uint32_t HwCtxSetStencilClear(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetDepthClear(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetScreenScissor(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetDepthRenderTarget(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwCtxSetStencilInfo(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetDepthHtileSurface(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwCtxSetDepthShadingRateEncoding(CommandProcessor&, uint32_t, uint32_t,
|
||||
const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetHardwareScreenOffset(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwCtxSetWindowOffset(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
@@ -89,7 +87,6 @@ uint32_t HwCtxSetDrawPayloadControl(CommandProcessor&, uint32_t, uint32_t, const
|
||||
uint32_t HwCtxSetPrimitiveIdReset(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetObjprimIdControl(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetShaderStages(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetRenderTarget(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetColorInfo(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetBlendControl(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwCtxSetViewportScissor(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
@@ -100,10 +97,8 @@ uint32_t HwShSetPsUserSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t
|
||||
uint32_t HwShSetGsUserSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetHsUserSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetCsUserSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetPsUserAccumSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetGsUserAccumSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetHsUserAccumSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShSetCsUserAccumSgpr(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShIgnoreUserAccumulator(CommandProcessor&, uint32_t, uint32_t, const uint32_t*,
|
||||
uint32_t);
|
||||
uint32_t HwShSetCsRegisters(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwShIgnoreRegisters(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
uint32_t HwUcSetPrimitiveType(CommandProcessor&, uint32_t, uint32_t, const uint32_t*, uint32_t);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -407,7 +407,19 @@ enum class BufferFormat : uint32_t {
|
||||
k5_5_5_1UNorm = 134,
|
||||
k1_5_5_5UNorm = 135,
|
||||
k4_4_4_4UNorm = 136,
|
||||
kFmask8_S2_F1 = 156,
|
||||
kFmask8_S4_F1 = 157,
|
||||
kFmask8_S8_F1 = 158,
|
||||
kFmask8_S2_F2 = 159,
|
||||
kFmask8_S4_F2 = 160,
|
||||
kFmask8_S4_F4 = 161,
|
||||
kFmask16_S16_F1 = 162,
|
||||
kFmask16_S8_F2 = 163,
|
||||
kFmask32_S16_F2 = 164,
|
||||
kFmask32_S8_F4 = 165,
|
||||
kFmask32_S8_F8 = 166,
|
||||
kFmask64_S16_F4 = 167,
|
||||
kFmask64_S16_F8 = 168,
|
||||
kBc1UNorm = 169,
|
||||
kBc1Srgb = 170,
|
||||
kBc2UNorm = 171,
|
||||
|
||||
@@ -68,7 +68,19 @@ constexpr FormatInfo kFormatInfo[] = {
|
||||
{BufferFormat::k5_5_5_1UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::k1_5_5_5UNorm, 0, 0, 2, false, false},
|
||||
{BufferFormat::k4_4_4_4UNorm, 2, 0, 2, true, false},
|
||||
{BufferFormat::kFmask8_S2_F1, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S4_F1, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S8_F1, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S2_F2, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S4_F2, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask8_S4_F4, 1, 0, 1, true, false},
|
||||
{BufferFormat::kFmask16_S16_F1, 2, 0, 2, true, false},
|
||||
{BufferFormat::kFmask16_S8_F2, 2, 0, 2, true, false},
|
||||
{BufferFormat::kFmask32_S16_F2, 4, 0, 4, true, false},
|
||||
{BufferFormat::kFmask32_S8_F4, 4, 0, 4, true, false},
|
||||
{BufferFormat::kFmask32_S8_F8, 4, 0, 4, true, false},
|
||||
{BufferFormat::kFmask64_S16_F4, 8, 0, 8, true, false},
|
||||
{BufferFormat::kFmask64_S16_F8, 8, 0, 8, true, false},
|
||||
{BufferFormat::kBc1UNorm, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc1Srgb, 0, 8, 0, true, false},
|
||||
{BufferFormat::kBc2UNorm, 0, 16, 0, true, false},
|
||||
@@ -203,7 +215,7 @@ uint32_t RenderTargetBytesPerElement(BufferFormat format) {
|
||||
}
|
||||
|
||||
bool IsFmaskTextureFormat(BufferFormat format) {
|
||||
return format == BufferFormat::kFmask8_S4_F4;
|
||||
return format >= BufferFormat::kFmask8_S2_F1 && format <= BufferFormat::kFmask64_S16_F8;
|
||||
}
|
||||
|
||||
bool IsSampledTextureFormat(BufferFormat format) {
|
||||
|
||||
@@ -11,15 +11,6 @@ struct ColorBase {
|
||||
uint64_t addr = 0;
|
||||
};
|
||||
|
||||
struct ColorPitch {
|
||||
uint32_t pitch_div8_minus1 = 0;
|
||||
uint32_t fmask_pitch_div8_minus1 = 0;
|
||||
};
|
||||
|
||||
struct ColorSlice {
|
||||
uint32_t slice_div64_minus1 = 0;
|
||||
};
|
||||
|
||||
struct ColorView {
|
||||
uint32_t base_array_slice_index = 0;
|
||||
uint32_t last_array_slice_index = 0;
|
||||
@@ -36,20 +27,15 @@ struct ColorInfo {
|
||||
bool blend_clamp = false;
|
||||
bool blend_bypass = false;
|
||||
bool round_mode = false;
|
||||
uint32_t cmask_is_linear = 0;
|
||||
uint32_t cmask_addr_type = 0;
|
||||
bool alt_tile_mode = false;
|
||||
Prospero::ChannelLayout format = Prospero::ChannelLayout::kInvalid;
|
||||
Prospero::ChannelType channel_type = Prospero::ChannelType::kUNorm;
|
||||
Prospero::ChannelOrder channel_order = Prospero::ChannelOrder::kStandard;
|
||||
};
|
||||
|
||||
struct ColorAttrib {
|
||||
bool force_dest_alpha_to_one = false;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
|
||||
Prospero::TileMode fmask_tile_mode = Prospero::TileMode::kLinear;
|
||||
uint32_t num_samples = 0;
|
||||
uint32_t num_fragments = 0;
|
||||
bool force_dest_alpha_to_one = false;
|
||||
uint32_t num_samples = 0;
|
||||
uint32_t num_fragments = 0;
|
||||
};
|
||||
|
||||
struct ColorAttrib2 {
|
||||
@@ -59,41 +45,33 @@ struct ColorAttrib2 {
|
||||
};
|
||||
|
||||
struct ColorAttrib3 {
|
||||
uint32_t depth = 0;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kLinear;
|
||||
uint32_t dimension = 0;
|
||||
bool cmask_pipe_aligned = false;
|
||||
bool dcc_pipe_aligned = false;
|
||||
uint32_t depth = 0;
|
||||
Prospero::TileMode tile_mode = Prospero::TileMode::kRenderTarget;
|
||||
uint32_t dimension = 0;
|
||||
bool metadata_pipe_aligned = false;
|
||||
bool write_vrs_rate_hint_to_cmask = false;
|
||||
};
|
||||
|
||||
struct ColorDccControl {
|
||||
uint32_t max_uncompressed_block_size = 0;
|
||||
uint32_t max_compressed_block_size = 0;
|
||||
uint32_t min_compressed_block_size = 0;
|
||||
uint32_t color_transform = 0;
|
||||
bool dcc_clear_key_enable = false;
|
||||
bool overwrite_combiner_disable = false;
|
||||
bool independent_64b_blocks = false;
|
||||
bool independent_128b_blocks = false;
|
||||
bool data_write_on_dcc_clear_to_reg = false;
|
||||
enum class IndependentBlockSize : uint8_t { Disabled, Bytes64, Bytes128 };
|
||||
|
||||
uint32_t max_uncompressed_block_size = 2;
|
||||
uint32_t max_compressed_block_size = 2;
|
||||
uint32_t color_transform = 0;
|
||||
bool dcc_clear_key_enable = false;
|
||||
bool overwrite_combiner_disable = false;
|
||||
IndependentBlockSize independent_block_size = IndependentBlockSize::Disabled;
|
||||
bool data_write_on_dcc_clear_to_reg = false;
|
||||
};
|
||||
|
||||
struct ColorCmask {
|
||||
uint64_t addr = 0;
|
||||
};
|
||||
|
||||
struct ColorCmaskSlice {
|
||||
uint32_t slice_minus1 = 0;
|
||||
};
|
||||
|
||||
struct ColorFmask {
|
||||
uint64_t addr = 0;
|
||||
};
|
||||
|
||||
struct ColorFmaskSlice {
|
||||
uint32_t slice_minus1 = 0;
|
||||
};
|
||||
|
||||
struct ColorClearWord0 {
|
||||
uint32_t word0 = 0;
|
||||
};
|
||||
@@ -106,15 +84,8 @@ struct ColorDccAddr {
|
||||
uint64_t addr = 0;
|
||||
};
|
||||
|
||||
struct ColorSize {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
struct RenderTarget {
|
||||
ColorBase base;
|
||||
ColorPitch pitch;
|
||||
ColorSlice slice;
|
||||
ColorView view;
|
||||
ColorInfo info;
|
||||
ColorAttrib attrib;
|
||||
@@ -122,13 +93,10 @@ struct RenderTarget {
|
||||
ColorAttrib3 attrib3;
|
||||
ColorDccControl dcc;
|
||||
ColorCmask cmask;
|
||||
ColorCmaskSlice cmask_slice;
|
||||
ColorFmask fmask;
|
||||
ColorFmaskSlice fmask_slice;
|
||||
ColorClearWord0 clear_word0;
|
||||
ColorClearWord1 clear_word1;
|
||||
ColorDccAddr dcc_addr;
|
||||
ColorSize size;
|
||||
};
|
||||
|
||||
struct DepthZInfo {
|
||||
@@ -136,7 +104,7 @@ struct DepthZInfo {
|
||||
uint32_t num_samples = 0;
|
||||
Prospero::TextureCompatiblePlaneCompression texture_compatibility =
|
||||
Prospero::TextureCompatiblePlaneCompression::kDisable;
|
||||
Prospero::ZCompareBase z_compare_base = Prospero::ZCompareBase::kZMin;
|
||||
Prospero::ZCompareBase z_compare_base = Prospero::ZCompareBase::kZMax;
|
||||
bool htile_acceleration = false;
|
||||
bool expclear_enabled = false;
|
||||
bool partially_resident = false;
|
||||
@@ -171,7 +139,7 @@ struct DepthStencilInfo {
|
||||
Prospero::TextureCompatibleStencil texture_compatibility =
|
||||
Prospero::TextureCompatibleStencil::kDisable;
|
||||
bool expclear_enabled = false;
|
||||
bool htile_stencil_disabled = false;
|
||||
bool htile_stencil_disabled = true;
|
||||
bool partially_resident = false;
|
||||
|
||||
[[nodiscard]] static DepthStencilInfo Decode(uint32_t value) {
|
||||
@@ -194,16 +162,6 @@ struct DepthStencilInfo {
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthRenderTargetDepthInfo {
|
||||
uint32_t addr5_swizzle_mask = 0;
|
||||
uint32_t array_mode = 0;
|
||||
uint32_t pipe_config = 0;
|
||||
uint32_t bank_width = 0;
|
||||
uint32_t bank_height = 0;
|
||||
uint32_t macro_tile_aspect = 0;
|
||||
uint32_t num_banks = 0;
|
||||
};
|
||||
|
||||
struct DepthDepthView {
|
||||
uint32_t slice_start = 0;
|
||||
uint32_t slice_max = 0;
|
||||
@@ -218,36 +176,18 @@ struct DepthDepthSizeXY {
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
struct DepthRenderTargetHTileSurface {
|
||||
uint32_t linear = 0;
|
||||
uint32_t full_cache = 0;
|
||||
uint32_t htile_uses_preload_win = 0;
|
||||
uint32_t preload = 0;
|
||||
uint32_t prefetch_width = 0;
|
||||
uint32_t prefetch_height = 0;
|
||||
uint32_t dst_outside_zero_to_one = 0;
|
||||
};
|
||||
|
||||
struct DepthRenderTarget {
|
||||
DepthZInfo z_info;
|
||||
DepthStencilInfo stencil_info;
|
||||
DepthDepthView depth_view;
|
||||
DepthDepthSizeXY size;
|
||||
DepthRenderTargetDepthInfo depth_info;
|
||||
DepthRenderTargetHTileSurface htile_surface;
|
||||
DepthZInfo z_info;
|
||||
DepthStencilInfo stencil_info;
|
||||
DepthDepthView depth_view;
|
||||
DepthDepthSizeXY size;
|
||||
|
||||
uint64_t z_read_base_addr = 0;
|
||||
uint64_t stencil_read_base_addr = 0;
|
||||
uint64_t z_write_base_addr = 0;
|
||||
uint64_t stencil_write_base_addr = 0;
|
||||
uint32_t pitch_div8_minus1 = 0;
|
||||
uint32_t height_div8_minus1 = 0;
|
||||
bool pitch_height_valid = false;
|
||||
uint32_t slice_div64_minus1 = 0;
|
||||
uint64_t htile_data_base_addr = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
bool width_height_valid = false;
|
||||
uint8_t shading_rate_encoding = 0;
|
||||
};
|
||||
|
||||
struct RenderControl {
|
||||
@@ -256,6 +196,8 @@ struct RenderControl {
|
||||
bool resummarize_enable = false;
|
||||
bool stencil_compress_disable = false;
|
||||
bool depth_compress_disable = false;
|
||||
bool copy_depth_to_color = false;
|
||||
bool copy_stencil_to_color = false;
|
||||
bool copy_centroid = false;
|
||||
uint8_t copy_sample = 0;
|
||||
};
|
||||
@@ -299,16 +241,14 @@ struct ClipControl {
|
||||
};
|
||||
|
||||
struct DepthControl {
|
||||
bool stencil_enable = false;
|
||||
bool z_enable = false;
|
||||
bool z_write_enable = false;
|
||||
bool depth_bounds_enable = false;
|
||||
uint8_t zfunc = 0;
|
||||
bool backface_enable = false;
|
||||
uint8_t stencilfunc = 0;
|
||||
uint8_t stencilfunc_bf = 0;
|
||||
bool color_writes_on_depth_fail_enable = false;
|
||||
bool color_writes_on_depth_pass_disable = false;
|
||||
bool stencil_enable = false;
|
||||
bool z_enable = false;
|
||||
bool z_write_enable = false;
|
||||
bool depth_bounds_enable = false;
|
||||
uint8_t zfunc = 0;
|
||||
bool backface_enable = false;
|
||||
uint8_t stencilfunc = 0;
|
||||
uint8_t stencilfunc_bf = 0;
|
||||
};
|
||||
|
||||
struct StencilControl {
|
||||
@@ -468,27 +408,6 @@ struct ScreenViewport {
|
||||
bool clip_rect_window_offset_enable[4] = {};
|
||||
};
|
||||
|
||||
struct VsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
bool ieee_mode = false;
|
||||
uint8_t vgpr_component_count = 0;
|
||||
bool cu_group_enable = false;
|
||||
bool require_forward_progress = false;
|
||||
bool fp16_overflow = false;
|
||||
};
|
||||
|
||||
struct VsShaderResource2 {
|
||||
bool scratch_en = false;
|
||||
uint8_t user_sgpr = 0;
|
||||
bool offchip_lds = false;
|
||||
bool streamout_enabled = false;
|
||||
uint8_t shared_vgprs = 0;
|
||||
};
|
||||
|
||||
struct HsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
@@ -511,7 +430,6 @@ struct HsShaderResource2 {
|
||||
|
||||
struct PsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
@@ -535,7 +453,6 @@ struct PsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
PsShaderResource1 rsrc1;
|
||||
PsShaderResource2 rsrc2;
|
||||
uint64_t chksum = 0;
|
||||
};
|
||||
|
||||
struct CsStageRegisters {
|
||||
@@ -545,12 +462,13 @@ struct CsStageRegisters {
|
||||
uint32_t num_thread_y = 0;
|
||||
uint32_t num_thread_z = 0;
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
bool debug_mode = false;
|
||||
bool ieee_mode = false;
|
||||
bool require_forward_progress = false;
|
||||
bool fp16_overflow = false;
|
||||
bool bulky = false;
|
||||
bool threadgroup_configuration = false;
|
||||
uint8_t wave_size = 64;
|
||||
bool scratch_en = false;
|
||||
@@ -561,6 +479,7 @@ struct CsStageRegisters {
|
||||
bool tg_size_en = false;
|
||||
uint8_t tidig_comp_cnt = 0;
|
||||
uint16_t lds_size = 0;
|
||||
uint8_t shared_vgprs = 0;
|
||||
};
|
||||
|
||||
struct EsStageRegisters {
|
||||
@@ -568,21 +487,17 @@ struct EsStageRegisters {
|
||||
};
|
||||
|
||||
struct LsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
VsShaderResource1 rsrc1;
|
||||
VsShaderResource2 rsrc2;
|
||||
uint64_t data_addr = 0;
|
||||
};
|
||||
|
||||
struct HsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
HsShaderResource1 rsrc1;
|
||||
HsShaderResource2 rsrc2;
|
||||
uint64_t chksum = 0;
|
||||
};
|
||||
|
||||
struct GsShaderResource1 {
|
||||
uint8_t vgprs = 0;
|
||||
uint8_t sgprs = 0;
|
||||
uint8_t priority = 0;
|
||||
uint8_t float_mode = 0;
|
||||
bool dx10_clamp = false;
|
||||
@@ -608,7 +523,6 @@ struct GsStageRegisters {
|
||||
uint64_t data_addr = 0;
|
||||
GsShaderResource1 rsrc1;
|
||||
GsShaderResource2 rsrc2;
|
||||
uint64_t chksum = 0;
|
||||
};
|
||||
|
||||
struct ShaderRegisters {
|
||||
@@ -664,8 +578,7 @@ struct ShaderRegisters {
|
||||
enum class UserSgprType { Unknown, Region, Vsharp };
|
||||
|
||||
struct UserSgprInfo {
|
||||
// Changed from 16 to 64
|
||||
static constexpr int SGPRS_MAX = 64;
|
||||
static constexpr int SGPRS_MAX = 32;
|
||||
|
||||
uint32_t value[SGPRS_MAX] = {0};
|
||||
UserSgprType type[SGPRS_MAX] = {};
|
||||
@@ -712,12 +625,6 @@ public:
|
||||
void Reset() { *this = Context(); }
|
||||
|
||||
void SetColorBase(uint32_t slot, const ColorBase& base) { m_render_targets[slot].base = base; }
|
||||
void SetColorPitch(uint32_t slot, const ColorPitch& pitch) {
|
||||
m_render_targets[slot].pitch = pitch;
|
||||
}
|
||||
void SetColorSlice(uint32_t slot, const ColorSlice& slice) {
|
||||
m_render_targets[slot].slice = slice;
|
||||
}
|
||||
void SetColorView(uint32_t slot, const ColorView& view) { m_render_targets[slot].view = view; }
|
||||
void SetColorInfo(uint32_t slot, const ColorInfo& info) { m_render_targets[slot].info = info; }
|
||||
void SetColorAttrib(uint32_t slot, const ColorAttrib& attrib) {
|
||||
@@ -735,15 +642,9 @@ public:
|
||||
void SetColorCmask(uint32_t slot, const ColorCmask& cmask) {
|
||||
m_render_targets[slot].cmask = cmask;
|
||||
}
|
||||
void SetColorCmaskSlice(uint32_t slot, const ColorCmaskSlice& cmask_slice) {
|
||||
m_render_targets[slot].cmask_slice = cmask_slice;
|
||||
}
|
||||
void SetColorFmask(uint32_t slot, const ColorFmask& fmask) {
|
||||
m_render_targets[slot].fmask = fmask;
|
||||
}
|
||||
void SetColorFmaskSlice(uint32_t slot, const ColorFmaskSlice& fmask_slice) {
|
||||
m_render_targets[slot].fmask_slice = fmask_slice;
|
||||
}
|
||||
void SetColorClearWord0(uint32_t slot, const ColorClearWord0& clear_word0) {
|
||||
m_render_targets[slot].clear_word0 = clear_word0;
|
||||
}
|
||||
@@ -753,7 +654,6 @@ public:
|
||||
void SetColorDccAddr(uint32_t slot, const ColorDccAddr& dcc_addr) {
|
||||
m_render_targets[slot].dcc_addr = dcc_addr;
|
||||
}
|
||||
void SetColorSize(uint32_t slot, const ColorSize& size) { m_render_targets[slot].size = size; }
|
||||
[[nodiscard]] const RenderTarget& GetRenderTarget(uint32_t slot) const {
|
||||
return m_render_targets[slot];
|
||||
}
|
||||
@@ -1072,21 +972,16 @@ public:
|
||||
|
||||
void SetEsShaderBase(uint64_t addr) { m_vs.es_regs.data_addr = addr; }
|
||||
void SetLsShaderBase(uint64_t addr) { m_vs.ls_regs.data_addr = addr; }
|
||||
void SetLsShaderResource1(const VsShaderResource1& rsrc1) { m_vs.ls_regs.rsrc1 = rsrc1; }
|
||||
void SetLsShaderResource2(const VsShaderResource2& rsrc2) { m_vs.ls_regs.rsrc2 = rsrc2; }
|
||||
void SetHsShaderBase(uint64_t addr) { m_vs.hs_regs.data_addr = addr; }
|
||||
void SetHsShaderResource1(const HsShaderResource1& rsrc1) { m_vs.hs_regs.rsrc1 = rsrc1; }
|
||||
void SetHsShaderResource2(const HsShaderResource2& rsrc2) { m_vs.hs_regs.rsrc2 = rsrc2; }
|
||||
void SetHsShaderChksum(uint32_t value) { m_vs.hs_regs.chksum = value; }
|
||||
void SetGsShaderBase(uint64_t addr) { m_vs.gs_regs.data_addr = addr; }
|
||||
void SetGsShaderResource1(const GsShaderResource1& rsrc1) { m_vs.gs_regs.rsrc1 = rsrc1; }
|
||||
void SetGsShaderResource2(const GsShaderResource2& rsrc2) { m_vs.gs_regs.rsrc2 = rsrc2; }
|
||||
void SetGsShaderChksum(uint32_t value) { m_vs.gs_regs.chksum = value; }
|
||||
|
||||
void SetPsShaderBase(uint64_t addr) { m_ps.ps_regs.data_addr = addr; }
|
||||
void SetPsShaderResource1(const PsShaderResource1& rsrc1) { m_ps.ps_regs.rsrc1 = rsrc1; }
|
||||
void SetPsShaderResource2(const PsShaderResource2& rsrc2) { m_ps.ps_regs.rsrc2 = rsrc2; }
|
||||
void SetPsShaderChksum(uint32_t value) { m_ps.ps_regs.chksum = value; }
|
||||
|
||||
void SetCsShader(const CsStageRegisters& cs_regs) { m_cs.cs_regs = cs_regs; }
|
||||
void SetCsWaveSize(uint8_t wave_size) { m_cs.cs_regs.wave_size = wave_size; }
|
||||
|
||||
+35
-125
@@ -98,6 +98,10 @@ constexpr uint32_t DB_RENDER_CONTROL_DEPTH_CLEAR_ENABLE_SHIFT = 0;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_DEPTH_CLEAR_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_CLEAR_ENABLE_SHIFT = 1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_CLEAR_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_DEPTH_TO_COLOR_SHIFT = 2;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_DEPTH_TO_COLOR_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_STENCIL_TO_COLOR_SHIFT = 3;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_COPY_STENCIL_TO_COLOR_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_RESUMMARIZE_ENABLE_SHIFT = 4;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_RESUMMARIZE_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t DB_RENDER_CONTROL_STENCIL_COMPRESS_DISABLE_SHIFT = 5;
|
||||
@@ -163,22 +167,6 @@ constexpr uint32_t PA_SC_SCREEN_SCISSOR_BR_BR_Y_MASK = 0xFFFF;
|
||||
|
||||
constexpr uint32_t DB_DFSM_CONTROL = 0xE;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_INFO = 0xF;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ADDR5_SWIZZLE_MASK_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ADDR5_SWIZZLE_MASK_MASK = 0xF;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ARRAY_MODE_SHIFT = 4;
|
||||
constexpr uint32_t DB_DEPTH_INFO_ARRAY_MODE_MASK = 0xF;
|
||||
constexpr uint32_t DB_DEPTH_INFO_PIPE_CONFIG_SHIFT = 8;
|
||||
constexpr uint32_t DB_DEPTH_INFO_PIPE_CONFIG_MASK = 0x1F;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_WIDTH_SHIFT = 13;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_WIDTH_MASK = 0x3;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_HEIGHT_SHIFT = 15;
|
||||
constexpr uint32_t DB_DEPTH_INFO_BANK_HEIGHT_MASK = 0x3;
|
||||
constexpr uint32_t DB_DEPTH_INFO_MACRO_TILE_ASPECT_SHIFT = 17;
|
||||
constexpr uint32_t DB_DEPTH_INFO_MACRO_TILE_ASPECT_MASK = 0x3;
|
||||
constexpr uint32_t DB_DEPTH_INFO_NUM_BANKS_SHIFT = 19;
|
||||
constexpr uint32_t DB_DEPTH_INFO_NUM_BANKS_MASK = 0x3;
|
||||
|
||||
constexpr uint32_t DB_Z_INFO = 0x10;
|
||||
constexpr uint32_t DB_Z_INFO_FORMAT_SHIFT = 0;
|
||||
constexpr uint32_t DB_Z_INFO_FORMAT_MASK = 0x3;
|
||||
@@ -222,16 +210,6 @@ constexpr uint32_t DB_STENCIL_READ_BASE = 0x13;
|
||||
constexpr uint32_t DB_Z_WRITE_BASE = 0x14;
|
||||
constexpr uint32_t DB_STENCIL_WRITE_BASE = 0x15;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_SIZE = 0x16;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_PITCH_TILE_MAX_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_PITCH_TILE_MAX_MASK = 0x7FF;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_HEIGHT_TILE_MAX_SHIFT = 11;
|
||||
constexpr uint32_t DB_DEPTH_SIZE_HEIGHT_TILE_MAX_MASK = 0x7FF;
|
||||
|
||||
constexpr uint32_t DB_DEPTH_SLICE = 0x17;
|
||||
constexpr uint32_t DB_DEPTH_SLICE_SLICE_TILE_MAX_SHIFT = 0;
|
||||
constexpr uint32_t DB_DEPTH_SLICE_SLICE_TILE_MAX_MASK = 0x3FFFFF;
|
||||
|
||||
constexpr uint32_t DB_Z_READ_BASE_HI = 0x1A;
|
||||
constexpr uint32_t DB_STENCIL_READ_BASE_HI = 0x1B;
|
||||
constexpr uint32_t DB_Z_WRITE_BASE_HI = 0x1C;
|
||||
@@ -424,11 +402,6 @@ constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_SHIFT = 8
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_MASK = 0x7;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_BF_SHIFT = 20;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_STENCILFUNC_BF_MASK = 0x7;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_ENABLE_COLOR_WRITES_ON_DEPTH_FAIL_SHIFT = 30;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_ENABLE_COLOR_WRITES_ON_DEPTH_FAIL_MASK = 0x1;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_DISABLE_COLOR_WRITES_ON_DEPTH_PASS_SHIFT = 31;
|
||||
constexpr uint32_t DB_DEPTH_CONTROL_DISABLE_COLOR_WRITES_ON_DEPTH_PASS_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t DB_EQAA = 0x201;
|
||||
constexpr uint32_t DB_EQAA_MAX_ANCHOR_SAMPLES_SHIFT = 0;
|
||||
constexpr uint32_t DB_EQAA_MAX_ANCHOR_SAMPLES_MASK = 0x7;
|
||||
@@ -560,21 +533,9 @@ constexpr uint32_t VGT_DRAW_PAYLOAD_CNTL = 0x2A6;
|
||||
constexpr uint32_t VGT_ESGS_RING_ITEMSIZE = 0x2AB;
|
||||
constexpr uint32_t VGT_REUSE_OFF = 0x2AD;
|
||||
|
||||
constexpr uint32_t DB_HTILE_SURFACE = 0x2AF;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_LINEAR_SHIFT = 0;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_LINEAR_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_FULL_CACHE_SHIFT = 1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_FULL_CACHE_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_HTILE_USES_PRELOAD_WIN_SHIFT = 2;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_HTILE_USES_PRELOAD_WIN_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PRELOAD_SHIFT = 3;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PRELOAD_MASK = 0x1;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_WIDTH_SHIFT = 4;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_WIDTH_MASK = 0x3F;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_HEIGHT_SHIFT = 10;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_PREFETCH_HEIGHT_MASK = 0x3F;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_SHIFT = 16;
|
||||
constexpr uint32_t DB_HTILE_SURFACE_DST_OUTSIDE_ZERO_TO_ONE_MASK = 0x1;
|
||||
constexpr uint32_t DB_SHADING_RATE_ENCODING = 0x2AF;
|
||||
constexpr uint32_t DB_SHADING_RATE_ENCODING_SHADING_RATE_ENCODING_SHIFT = 19;
|
||||
constexpr uint32_t DB_SHADING_RATE_ENCODING_SHADING_RATE_ENCODING_MASK = 0x3;
|
||||
|
||||
constexpr uint32_t DB_SRESULTS_COMPARE_STATE0 = 0x2B0;
|
||||
constexpr uint32_t DB_SRESULTS_COMPARE_STATE1 = 0x2B1;
|
||||
@@ -621,7 +582,6 @@ constexpr uint32_t PA_SC_SHADER_CONTROL = 0x310;
|
||||
constexpr uint32_t PA_SC_BINNER_CNTL_0 = 0x311;
|
||||
constexpr uint32_t PA_SC_BINNER_CNTL_1 = 0x312;
|
||||
constexpr uint32_t PA_SC_CONSERVATIVE_RASTERIZATION_CNTL = 0x313;
|
||||
constexpr uint32_t PA_SC_NGG_MODE_CNTL = 0x314;
|
||||
constexpr uint32_t CB_COLOR0_BASE = 0x318;
|
||||
|
||||
constexpr uint32_t CB_COLOR0_VIEW = 0x31B;
|
||||
@@ -649,24 +609,13 @@ constexpr uint32_t CB_COLOR0_INFO_BLEND_BYPASS_SHIFT = 16;
|
||||
constexpr uint32_t CB_COLOR0_INFO_BLEND_BYPASS_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_ROUND_MODE_SHIFT = 18;
|
||||
constexpr uint32_t CB_COLOR0_INFO_ROUND_MODE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_CMASK_IS_LINEAR_SHIFT = 19;
|
||||
constexpr uint32_t CB_COLOR0_INFO_CMASK_IS_LINEAR_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_FMASK_COMPRESSION_DISABLE_SHIFT = 26;
|
||||
constexpr uint32_t CB_COLOR0_INFO_FMASK_COMPRESSION_DISABLE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_FMASK_COMPRESS_1FRAG_ONLY_SHIFT = 27;
|
||||
constexpr uint32_t CB_COLOR0_INFO_FMASK_COMPRESS_1FRAG_ONLY_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_DCC_ENABLE_SHIFT = 28;
|
||||
constexpr uint32_t CB_COLOR0_INFO_DCC_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_INFO_CMASK_ADDR_TYPE_SHIFT = 29;
|
||||
constexpr uint32_t CB_COLOR0_INFO_CMASK_ADDR_TYPE_MASK = 0x3;
|
||||
constexpr uint32_t CB_COLOR0_INFO_ALT_TILE_MODE_SHIFT = 31;
|
||||
constexpr uint32_t CB_COLOR0_INFO_ALT_TILE_MODE_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB = 0x31D;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_TILE_MODE_INDEX_SHIFT = 0;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_TILE_MODE_INDEX_MASK = 0x1F;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_FMASK_TILE_MODE_INDEX_SHIFT = 5;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_FMASK_TILE_MODE_INDEX_MASK = 0x1F;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_NUM_SAMPLES_SHIFT = 12;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_NUM_SAMPLES_MASK = 0x7;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB_NUM_FRAGMENTS_SHIFT = 15;
|
||||
@@ -681,8 +630,6 @@ constexpr uint32_t CB_COLOR0_DCC_CONTROL_KEY_CLEAR_ENABLE_SHIFT
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_KEY_CLEAR_ENABLE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MAX_UNCOMPRESSED_BLOCK_SIZE_SHIFT = 2;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MAX_UNCOMPRESSED_BLOCK_SIZE_MASK = 0x3;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MIN_COMPRESSED_BLOCK_SIZE_SHIFT = 4;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MIN_COMPRESSED_BLOCK_SIZE_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MAX_COMPRESSED_BLOCK_SIZE_SHIFT = 5;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_MAX_COMPRESSED_BLOCK_SIZE_MASK = 0x3;
|
||||
constexpr uint32_t CB_COLOR0_DCC_CONTROL_COLOR_TRANSFORM_SHIFT = 7;
|
||||
@@ -739,31 +686,25 @@ constexpr uint32_t CB_COLOR0_ATTRIB3_CMASK_PIPE_ALIGNED_SHIFT = 26;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_CMASK_PIPE_ALIGNED_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_DCC_PIPE_ALIGNED_SHIFT = 30;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_DCC_PIPE_ALIGNED_MASK = 0x1;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_WRITE_VRS_RATE_HINT_TO_CMASK_SHIFT = 31;
|
||||
constexpr uint32_t CB_COLOR0_ATTRIB3_WRITE_VRS_RATE_HINT_TO_CMASK_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t CB_COLOR7_ATTRIB3 = 0x3BF;
|
||||
|
||||
/* Fake codes. Don't exist on real HW */
|
||||
|
||||
constexpr uint32_t CX_NOP = 0x800003FF;
|
||||
constexpr uint32_t CX_NOP = 0xDB;
|
||||
|
||||
constexpr uint32_t CX_NUM = 0x3FF + 1;
|
||||
|
||||
/* Shader registers */
|
||||
|
||||
constexpr uint32_t SPI_SHADER_TBA_LO_PS = 0x0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC4_PS = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_TMA_LO_PS = 0x2;
|
||||
constexpr uint32_t SPI_SHADER_TMA_HI_PS = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_PS = 0x6;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC3_PS = 0x7;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_PS = 0x8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_PS = 0x9;
|
||||
constexpr uint32_t SPI_SHADER_PACE_ID_PS = 0x6;
|
||||
constexpr uint32_t SPI_GRAPHICS_SHADER_CONTROL_PS = 0x7;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_PS = 0x8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_PS = 0x9;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS = 0xA;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_PRIORITY_SHIFT = 10;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_PRIORITY_MASK = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_PS_FLOAT_MODE_SHIFT = 12;
|
||||
@@ -800,14 +741,11 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC2_PS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_0 = 0xC;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_15 = 0x1B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_PS_31 = 0x2B;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_PS = 0x30;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_PS_0 = 0x32;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_PS_3 = 0x35;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_PRIORITY_SHIFT = 10;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_PRIORITY_MASK = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_VS_FLOAT_MODE_SHIFT = 12;
|
||||
@@ -838,19 +776,20 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC2_VS_USER_SGPR_MSB_MASK = 0x1;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_VS_SHARED_VGPR_CNT_SHIFT = 28;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_VS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_GS = 0x80;
|
||||
constexpr uint32_t SPI_SHADER_PACE_ID_GS = 0x80;
|
||||
// AGC compiler records use this numeric tag as opaque metadata. It is not a shader checksum
|
||||
// register and intentionally has no checksum-state consumer in HardwareContext.
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_GS = SPI_SHADER_PACE_ID_GS;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC4_GS = 0x81;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_LO_GS = 0x82;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_HI_GS = 0x83;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC3_GS = 0x87;
|
||||
constexpr uint32_t SPI_GRAPHICS_SHADER_CONTROL_GS = 0x87;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_GS = 0x88;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_GS = 0x89;
|
||||
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS = 0x8A;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_PRIORITY_SHIFT = 10;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_PRIORITY_MASK = 0x3;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_GS_FLOAT_MODE_SHIFT = 12;
|
||||
@@ -891,22 +830,15 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC2_GS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_0 = 0x8C;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_15 = 0x9B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_GS_31 = 0xAB;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_ESGS = 0xB0;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_0 = 0xB2;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_ESGS_3 = 0xB5;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_ES_GS = 0xBC;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_ES = 0xC8;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_ES = 0xC9;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_ES = 0xCA;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_ES = 0xCB;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_0 = 0xCC;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_15 = 0xDB;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ES_31 = 0xEB;
|
||||
// Retained only as an opaque AGC compiler-record tag; 0x100 is not a PS5 SH register.
|
||||
constexpr uint32_t SPI_SHADER_PGM_CHKSUM_HS = 0x100;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC4_HS = 0x101;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_LO_HS = 0x102;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_ADDR_HI_HS = 0x103;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC3_HS = 0x107;
|
||||
constexpr uint32_t SPI_GRAPHICS_SHADER_CONTROL_HS = 0x107;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_HS = 0x108;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_HS = 0x109;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_HS = 0x10A;
|
||||
@@ -944,13 +876,10 @@ constexpr uint32_t SPI_SHADER_PGM_RSRC2_HS_SHARED_VGPR_CNT_MASK = 0xF;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_0 = 0x10C;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_15 = 0x11B;
|
||||
constexpr uint32_t SPI_SHADER_USER_DATA_HS_31 = 0x12B;
|
||||
constexpr uint32_t SPI_SHADER_REQ_CTRL_LSHS = 0x130;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_LSHS_0 = 0x132;
|
||||
constexpr uint32_t SPI_SHADER_USER_ACCUM_LSHS_3 = 0x135;
|
||||
constexpr uint32_t SPI_SHADER_PGM_LO_LS = 0x148;
|
||||
constexpr uint32_t SPI_SHADER_PGM_HI_LS = 0x149;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC1_LS = 0x14A;
|
||||
constexpr uint32_t SPI_SHADER_PGM_RSRC2_LS = 0x14B;
|
||||
constexpr uint32_t COMPUTE_START_X = 0x204;
|
||||
constexpr uint32_t COMPUTE_START_Y = 0x205;
|
||||
constexpr uint32_t COMPUTE_START_Z = 0x206;
|
||||
@@ -959,28 +888,26 @@ constexpr uint32_t COMPUTE_NUM_THREAD_Y = 0x208;
|
||||
constexpr uint32_t COMPUTE_NUM_THREAD_Z = 0x209;
|
||||
constexpr uint32_t COMPUTE_PGM_LO = 0x20C;
|
||||
constexpr uint32_t COMPUTE_PGM_HI = 0x20D;
|
||||
constexpr uint32_t COMPUTE_TBA_LO = 0x20E;
|
||||
constexpr uint32_t COMPUTE_TBA_HI = 0x20F;
|
||||
constexpr uint32_t COMPUTE_TMA_LO = 0x210;
|
||||
constexpr uint32_t COMPUTE_TMA_HI = 0x211;
|
||||
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1 = 0x212;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_VGPRS_MASK = 0x3F;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_SHIFT = 6;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_SGPRS_MASK = 0xF;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_PRIORITY_SHIFT = 10;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_PRIORITY_MASK = 0x3;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FLOAT_MODE_SHIFT = 12;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FLOAT_MODE_MASK = 0xFF;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DX10_CLAMP_SHIFT = 21;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DX10_CLAMP_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DEBUG_MODE_SHIFT = 22;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_DEBUG_MODE_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_IEEE_MODE_SHIFT = 23;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_IEEE_MODE_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_SHIFT = 24;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_BULKY_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FP16_OVFL_SHIFT = 26;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FP16_OVFL_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_WGP_MODE_SHIFT = 29;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_WGP_MODE_MASK = 0x1;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FWD_PROGRESS_SHIFT = 31;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC1_FWD_PROGRESS_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t COMPUTE_DISPATCH_INITIATOR_CS_W32_EN_SHIFT = 15;
|
||||
constexpr uint32_t COMPUTE_DISPATCH_INITIATOR_CS_W32_EN_MASK = 0x1;
|
||||
@@ -1011,21 +938,16 @@ constexpr uint32_t COMPUTE_PGM_RSRC2_LDS_SIZE_SHIFT = 15;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC2_LDS_SIZE_MASK = 0x1FF;
|
||||
|
||||
constexpr uint32_t COMPUTE_RESOURCE_LIMITS = 0x215;
|
||||
constexpr uint32_t COMPUTE_DESTINATION_EN_SE0 = 0x216;
|
||||
constexpr uint32_t COMPUTE_DESTINATION_EN_SE1 = 0x217;
|
||||
constexpr uint32_t COMPUTE_TMPRING_SIZE = 0x218;
|
||||
constexpr uint32_t COMPUTE_DESTINATION_EN_SE2 = 0x219;
|
||||
constexpr uint32_t COMPUTE_DESTINATION_EN_SE3 = 0x21A;
|
||||
constexpr uint32_t COMPUTE_USER_ACCUM_0 = 0x224;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC3 = 0x228;
|
||||
constexpr uint32_t COMPUTE_SHADER_CHKSUM = 0x22A;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC3_SHARED_VGPRS_SHIFT = 0;
|
||||
constexpr uint32_t COMPUTE_PGM_RSRC3_SHARED_VGPRS_MASK = 0xF;
|
||||
constexpr uint32_t COMPUTE_PACE_ID = 0x22A;
|
||||
constexpr uint32_t COMPUTE_USER_DATA_0 = 0x240;
|
||||
constexpr uint32_t COMPUTE_USER_DATA_15 = 0x24F;
|
||||
constexpr uint32_t COMPUTE_DISPATCH_TUNNEL = 0x27D;
|
||||
|
||||
/* Fake codes. Don't exist on real HW */
|
||||
|
||||
constexpr uint32_t SH_NOP = 0x800002FF;
|
||||
constexpr uint32_t SH_NOP = 0x280;
|
||||
|
||||
constexpr uint32_t SH_NUM = 0x2FF + 1;
|
||||
|
||||
@@ -1042,6 +964,7 @@ constexpr uint32_t GE_INDX_OFFSET = 0x24A;
|
||||
constexpr uint32_t GE_MULTI_PRIM_IB_RESET_EN = 0x24B;
|
||||
constexpr uint32_t VGT_HS_OFFCHIP_PARAM = 0x24F;
|
||||
constexpr uint32_t VGT_TF_MEMORY_BASE = 0x250;
|
||||
constexpr uint32_t IA_MULTI_VGT_PARAM = 0x258;
|
||||
|
||||
constexpr uint32_t GE_CNTL = 0x25B;
|
||||
constexpr uint32_t GE_CNTL_PRIM_GRP_SIZE_SHIFT = 0;
|
||||
@@ -1063,28 +986,15 @@ constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR2_MASK = 0x1;
|
||||
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR3_SHIFT = 2;
|
||||
constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR3_MASK = 0x1;
|
||||
|
||||
constexpr uint32_t IA_MULTI_VGT_PARAM = 0x258;
|
||||
constexpr uint32_t TA_CS_BC_BASE_ADDR = 0x380;
|
||||
constexpr uint32_t TA_CS_BC_BASE_ADDR_HI = 0x381;
|
||||
constexpr uint32_t TEXTURE_GRADIENT_FACTORS = 0x382;
|
||||
constexpr uint32_t TEXTURE_GRADIENT_FACTORS = 0x383;
|
||||
constexpr uint32_t TEXTURE_GRADIENT_CONTROL = 0x384;
|
||||
constexpr uint32_t GDS_OA_CNTL = 0x41D;
|
||||
constexpr uint32_t GDS_OA_COUNTER = 0x41E;
|
||||
constexpr uint32_t GDS_OA_ADDRESS = 0x41F;
|
||||
|
||||
/* Fake codes. Don't exist on real HW */
|
||||
|
||||
constexpr uint32_t FSR_EXTEND_SUBPIXEL_ROUNDING = 0x80003FF4;
|
||||
constexpr uint32_t FSR_ALPHA_VALUE0 = 0x80003FF5;
|
||||
constexpr uint32_t FSR_ALPHA_VALUE1 = 0x80003FF6;
|
||||
constexpr uint32_t FSR_CONTROL_POINT0 = 0x80003FF7;
|
||||
constexpr uint32_t FSR_CONTROL_POINT1 = 0x80003FF8;
|
||||
constexpr uint32_t FSR_CONTROL_POINT2 = 0x80003FF9;
|
||||
constexpr uint32_t FSR_CONTROL_POINT3 = 0x80003FFA;
|
||||
constexpr uint32_t FSR_WINDOW0 = 0x80003FFB;
|
||||
constexpr uint32_t FSR_WINDOW1 = 0x80003FFC;
|
||||
constexpr uint32_t TEXTURE_GRADIENT_CONTROL = 0x80003FFD;
|
||||
constexpr uint32_t MEMORY_MAPPING_MASK = 0x80003FFE;
|
||||
constexpr uint32_t UC_NOP = 0x80003FFF;
|
||||
constexpr uint32_t UC_NOP = 0xA2;
|
||||
|
||||
constexpr uint32_t UC_NUM = 0x3FFF + 1;
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ MemoryTracker::~MemoryTracker() = default;
|
||||
#if KYTY_BUILD == KYTY_BUILD_DEBUG
|
||||
void MemoryTracker::ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
||||
const char* operation) const noexcept {
|
||||
if (vaddr == 0 || size == 0 || size > UINT64_MAX - vaddr ||
|
||||
(vaddr & (TRACKER_PAGE_SIZE - 1)) != 0 || (size & (TRACKER_PAGE_SIZE - 1)) != 0) {
|
||||
if (!GuestRange {vaddr, size}.Valid() || (vaddr & (TRACKER_PAGE_SIZE - 1)) != 0 ||
|
||||
(size & (TRACKER_PAGE_SIZE - 1)) != 0) {
|
||||
EXIT("MemoryTracker: invalid dirty-page validation range\n");
|
||||
}
|
||||
for (auto page = vaddr; page < vaddr + size; page += TRACKER_PAGE_SIZE) {
|
||||
@@ -37,9 +37,6 @@ void MemoryTracker::ValidateGpuDirtyPages(const RangeSet& dirty, uint64_t vaddr,
|
||||
void MemoryTracker::ValidateGpuDirtyOwnership(const RangeSet& dirty, uint64_t vaddr, uint64_t size,
|
||||
const char* operation) {
|
||||
ValidateRange(vaddr, size);
|
||||
if (vaddr + size > UINT64_MAX - (TRACKER_PAGE_SIZE - 1)) {
|
||||
EXIT("MemoryTracker: dirty ownership range alignment overflow\n");
|
||||
}
|
||||
const auto begin = vaddr & ~(TRACKER_PAGE_SIZE - 1);
|
||||
const auto end = (vaddr + size + TRACKER_PAGE_SIZE - 1) & ~(TRACKER_PAGE_SIZE - 1);
|
||||
for (auto page = begin; page < end; page += TRACKER_PAGE_SIZE) {
|
||||
@@ -56,8 +53,7 @@ void MemoryTracker::ValidateGpuDirtyOwnership(const RangeSet& dirty, uint64_t va
|
||||
#endif
|
||||
|
||||
void MemoryTracker::ValidateRange(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
EXIT("invalid memory tracker range\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ private:
|
||||
};
|
||||
|
||||
void ValidateRange(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= ADDRESS_SIZE || size > ADDRESS_SIZE - vaddr) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
Fatal("invalid range vaddr=0x%016" PRIx64 ", size=0x%016" PRIx64, vaddr, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "common/bitArray.h"
|
||||
#include "common/common.h"
|
||||
|
||||
#include <compare>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
constexpr uint64_t TRACKER_PAGE_SIZE = 4ull * 1024ull;
|
||||
@@ -11,6 +13,20 @@ constexpr uint64_t TRACKER_REGION_SIZE = 4ull * 1024ull * 1024ull;
|
||||
constexpr uint64_t TRACKER_ADDRESS_SIZE = 1ull << 40u;
|
||||
constexpr size_t TRACKER_REGION_PAGES = TRACKER_REGION_SIZE / TRACKER_PAGE_SIZE;
|
||||
|
||||
struct GuestRange {
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
|
||||
[[nodiscard]] constexpr bool Empty() const noexcept { return address == 0 && size == 0; }
|
||||
[[nodiscard]] constexpr bool Valid() const noexcept {
|
||||
return address != 0 && size != 0 && address < TRACKER_ADDRESS_SIZE &&
|
||||
size <= TRACKER_ADDRESS_SIZE - address;
|
||||
}
|
||||
[[nodiscard]] constexpr bool ValidOrEmpty() const noexcept { return Empty() || Valid(); }
|
||||
[[nodiscard]] constexpr uint64_t End() const noexcept { return address + size; }
|
||||
auto operator<=>(const GuestRange&) const = default;
|
||||
};
|
||||
|
||||
enum class DirtySource { Cpu, Gpu };
|
||||
using RegionBits = Common::BitArray<TRACKER_REGION_PAGES>;
|
||||
static_assert(sizeof(RegionBits) == TRACKER_REGION_PAGES / 8);
|
||||
|
||||
+5
-9
@@ -241,8 +241,7 @@ BufferCache::~BufferCache() {
|
||||
}
|
||||
|
||||
void BufferCache::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
EXIT("BufferCache: invalid memory-invalidation range\n");
|
||||
}
|
||||
m_memory_tracker.InvalidateRegion(vaddr, size,
|
||||
@@ -314,7 +313,7 @@ BufferId BufferCache::FindBuffer(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0) {
|
||||
return NULL_BUFFER_ID;
|
||||
}
|
||||
if (size == 0 || vaddr >= TRACKER_ADDRESS_SIZE || size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
EXIT("BufferCache: invalid buffer discovery request\n");
|
||||
}
|
||||
const auto* owner = m_page_table.Find(vaddr >> PageTable::kPageBits);
|
||||
@@ -443,8 +442,7 @@ std::pair<Buffer*, uint64_t> BufferCache::ObtainBuffer(uint64_t vaddr, uint64_t
|
||||
bool is_written, bool is_texel_buffer,
|
||||
BufferId id) {
|
||||
auto& command = m_scheduler.Current();
|
||||
if (command.IsInvalid() || vaddr == 0 || size == 0 ||
|
||||
vaddr >= TRACKER_ADDRESS_SIZE || size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
if (command.IsInvalid() || !GuestRange {vaddr, size}.Valid()) {
|
||||
EXIT("BufferCache: buffer request requires a recording command buffer\n");
|
||||
}
|
||||
|
||||
@@ -474,8 +472,7 @@ std::pair<Buffer*, uint64_t> BufferCache::ObtainBuffer(uint64_t vaddr, uint64_t
|
||||
}
|
||||
|
||||
std::pair<Buffer*, uint64_t> BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
EXIT("BufferCache: invalid image source\n");
|
||||
}
|
||||
auto find_owner = [&]() -> Buffer* {
|
||||
@@ -661,8 +658,7 @@ void BufferCache::CopyBuffer(uint64_t dst_vaddr, uint64_t src_vaddr, uint64_t si
|
||||
}
|
||||
|
||||
bool BufferCache::IsRegionRegistered(uint64_t vaddr, uint64_t size) {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
EXIT("BufferCache: invalid registered-region query\n");
|
||||
}
|
||||
// Cached buffers are ordered and non-overlapping. The last buffer beginning before the query
|
||||
|
||||
@@ -35,8 +35,7 @@ bool GpuResourceManager::InvalidateMemory(uint64_t vaddr, uint64_t size) {
|
||||
}
|
||||
|
||||
bool GpuResourceManager::IsMapped(uint64_t vaddr, uint64_t size) const noexcept {
|
||||
if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE ||
|
||||
size > TRACKER_ADDRESS_SIZE - vaddr) {
|
||||
if (!GuestRange {vaddr, size}.Valid()) {
|
||||
return false;
|
||||
}
|
||||
std::shared_lock lock(m_mapped_ranges_mutex);
|
||||
|
||||
@@ -204,13 +204,11 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
if (texture_tile &&
|
||||
(!TileGetTextureBlockLayout(transfer_format, rt.attrib3.tile_mode, volume,
|
||||
texture_tile_layout) ||
|
||||
rt.pitch.pitch_div8_minus1 != 0 ||
|
||||
(rt.base.addr & (texture_tile_layout.block.block_size - 1u)) != 0 ||
|
||||
rt.info.fmask_compression_enable || rt.info.fmask_data_compression_disable ||
|
||||
rt.info.fmask_one_frag_mode || rt.info.cmask_fast_clear_enable ||
|
||||
rt.info.dcc_compression_enable || rt.info.cmask_is_linear != 0 ||
|
||||
rt.info.cmask_addr_type != 0 || rt.info.alt_tile_mode || rt.cmask.addr != 0 ||
|
||||
rt.fmask.addr != 0 || rt.dcc_addr.addr != 0 || rt.dcc.data_write_on_dcc_clear_to_reg)) {
|
||||
rt.info.dcc_compression_enable || rt.cmask.addr != 0 || rt.fmask.addr != 0 ||
|
||||
rt.dcc_addr.addr != 0 || rt.dcc.data_write_on_dcc_clear_to_reg)) {
|
||||
EXIT("unsupported texture-tiled render target: addr=0x%016" PRIx64 " tile=%u"
|
||||
" dimension=%u depth=%u levels=%u layer=%u/%u samples=%u fragments=%u bpe=%u"
|
||||
" cmask=0x%016" PRIx64 " fmask=0x%016" PRIx64 " dcc=0x%016" PRIx64 "\n",
|
||||
@@ -226,9 +224,7 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
" layer=%u/%u\n",
|
||||
rt.attrib3.dimension, rt.attrib3.depth, view.base_layer, view.image_layers);
|
||||
}
|
||||
if (rt.pitch.pitch_div8_minus1 != 0) {
|
||||
pitch = (rt.pitch.pitch_div8_minus1 + 1u) << 3u;
|
||||
} else if (tile) {
|
||||
if (tile) {
|
||||
if (volume) {
|
||||
pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode);
|
||||
} else if (texture_tile) {
|
||||
@@ -294,12 +290,6 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
mip_sizes[0] = {static_cast<uint32_t>(size), 0, 0, 0, 0, 0};
|
||||
mip_padded[0] = {pitch, height};
|
||||
}
|
||||
if (rt.slice.slice_div64_minus1 != 0 &&
|
||||
(static_cast<uint64_t>(rt.slice.slice_div64_minus1) + 1u) * 64u != size) {
|
||||
EXIT("render-target slice span mismatch: encoded=0x%016" PRIx64 " derived=0x%016" PRIx64
|
||||
"\n",
|
||||
(static_cast<uint64_t>(rt.slice.slice_div64_minus1) + 1u) * 64u, size);
|
||||
}
|
||||
if (size == 0 || (!volume && size > UINT64_MAX / view.image_layers)) {
|
||||
EXIT("render-target memory footprint is invalid\n");
|
||||
}
|
||||
@@ -309,7 +299,7 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
if (backing_size == 0) {
|
||||
EXIT("render-target backing is empty\n");
|
||||
}
|
||||
if (backing_size > TRACKER_ADDRESS_SIZE - rt.base.addr) {
|
||||
if (!GuestRange {rt.base.addr, backing_size}.Valid()) {
|
||||
EXIT("render-target backing range is invalid\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -97,17 +97,11 @@ void sh_print(const char* func, const HW::Shader& /*uc*/) {
|
||||
|
||||
std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt) {
|
||||
std::vector<std::string> dst;
|
||||
dst.reserve(53);
|
||||
dst.reserve(40);
|
||||
|
||||
dst.push_back(fmt::format("{}\n", func));
|
||||
|
||||
dst.push_back(fmt::format("\t base.addr = 0x{:016x}\n", rt.base.addr));
|
||||
dst.push_back(
|
||||
fmt::format("\t pitch.pitch_div8_minus1 = 0x{:08x}\n", rt.pitch.pitch_div8_minus1));
|
||||
dst.push_back(fmt::format("\t pitch.fmask_pitch_div8_minus1 = 0x{:08x}\n",
|
||||
rt.pitch.fmask_pitch_div8_minus1));
|
||||
dst.push_back(fmt::format("\t slice.slice_div64_minus1 = 0x{:08x}\n",
|
||||
rt.slice.slice_div64_minus1));
|
||||
dst.push_back(fmt::format("\t view.base_array_slice_index = 0x{:08x}\n",
|
||||
rt.view.base_array_slice_index));
|
||||
dst.push_back(fmt::format("\t view.last_array_slice_index = 0x{:08x}\n",
|
||||
@@ -128,12 +122,6 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
|
||||
rt.info.cmask_fast_clear_enable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.dcc_compression_enable = {}\n",
|
||||
rt.info.dcc_compression_enable ? "true" : "false"));
|
||||
dst.push_back(
|
||||
fmt::format("\t info.cmask_is_linear = 0x{:08x}\n", rt.info.cmask_is_linear));
|
||||
dst.push_back(
|
||||
fmt::format("\t info.cmask_addr_type = 0x{:08x}\n", rt.info.cmask_addr_type));
|
||||
dst.push_back(fmt::format("\t info.alt_tile_mode = {}\n",
|
||||
rt.info.alt_tile_mode ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t info.format = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.info.format)));
|
||||
dst.push_back(fmt::format("\t info.channel_type = 0x{:08x}\n",
|
||||
@@ -148,10 +136,6 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
|
||||
rt.info.round_mode ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib.force_dest_alpha_to_one = {}\n",
|
||||
rt.attrib.force_dest_alpha_to_one ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib.tile_mode = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.attrib.tile_mode)));
|
||||
dst.push_back(fmt::format("\t attrib.fmask_tile_mode = 0x{:08x}\n",
|
||||
static_cast<uint32_t>(rt.attrib.fmask_tile_mode)));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib.num_samples = 0x{:08x}\n", rt.attrib.num_samples));
|
||||
dst.push_back(
|
||||
@@ -166,42 +150,32 @@ std::vector<std::string> rt_print(const char* func, const HW::RenderTarget& rt)
|
||||
static_cast<uint32_t>(rt.attrib3.tile_mode)));
|
||||
dst.push_back(
|
||||
fmt::format("\t attrib3.dimension = 0x{:08x}\n", rt.attrib3.dimension));
|
||||
dst.push_back(fmt::format("\t attrib3.cmask_pipe_aligned = {}\n",
|
||||
rt.attrib3.cmask_pipe_aligned ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib3.dcc_pipe_aligned = {}\n",
|
||||
rt.attrib3.dcc_pipe_aligned ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib3.metadata_pipe_aligned = {}\n",
|
||||
rt.attrib3.metadata_pipe_aligned ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t attrib3.write_vrs_rate_hint_to_cmask = {}\n",
|
||||
rt.attrib3.write_vrs_rate_hint_to_cmask ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.max_uncompressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_uncompressed_block_size));
|
||||
dst.push_back(fmt::format("\t dcc.max_compressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.max_compressed_block_size));
|
||||
dst.push_back(fmt::format("\t dcc.min_compressed_block_size = 0x{:08x}\n",
|
||||
rt.dcc.min_compressed_block_size));
|
||||
dst.push_back(
|
||||
fmt::format("\t dcc.color_transform = 0x{:08x}\n", rt.dcc.color_transform));
|
||||
dst.push_back(fmt::format("\t dcc.overwrite_combiner_disable = {}\n",
|
||||
rt.dcc.overwrite_combiner_disable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.independent_64b_blocks = {}\n",
|
||||
rt.dcc.independent_64b_blocks ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.independent_128b_blocks = {}\n",
|
||||
rt.dcc.independent_128b_blocks ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.independent_block_size = 0x{:02x}\n",
|
||||
static_cast<uint8_t>(rt.dcc.independent_block_size)));
|
||||
dst.push_back(fmt::format("\t data_write_on_dcc_clear_to_reg = {}\n",
|
||||
rt.dcc.data_write_on_dcc_clear_to_reg ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t dcc.dcc_clear_key_enable = {}\n",
|
||||
rt.dcc.dcc_clear_key_enable ? "true" : "false"));
|
||||
dst.push_back(fmt::format("\t cmask.addr = 0x{:016x}\n", rt.cmask.addr));
|
||||
dst.push_back(fmt::format("\t cmask_slice.slice_minus1 = 0x{:08x}\n",
|
||||
rt.cmask_slice.slice_minus1));
|
||||
dst.push_back(fmt::format("\t fmask.addr = 0x{:016x}\n", rt.fmask.addr));
|
||||
dst.push_back(fmt::format("\t fmask_slice.slice_minus1 = 0x{:08x}\n",
|
||||
rt.fmask_slice.slice_minus1));
|
||||
dst.push_back(
|
||||
fmt::format("\t clear_word0.word0 = 0x{:08x}\n", rt.clear_word0.word0));
|
||||
dst.push_back(
|
||||
fmt::format("\t clear_word1.word1 = 0x{:08x}\n", rt.clear_word1.word1));
|
||||
dst.push_back(
|
||||
fmt::format("\t dcc_addr.addr = 0x{:016x}\n", rt.dcc_addr.addr));
|
||||
dst.push_back(fmt::format("\t size.width = 0x{:08x}\n", rt.size.width));
|
||||
dst.push_back(fmt::format("\t size.height = 0x{:08x}\n", rt.size.height));
|
||||
|
||||
return dst;
|
||||
}
|
||||
@@ -231,13 +205,8 @@ static bool RenderIsColorDimension(uint32_t dimension) {
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
static void RtCheck(const HW::RenderTarget& rt) {
|
||||
if (rt.base.addr != 0) {
|
||||
// bool render_to_texture = (rt.attrib.tile_mode == 0x0d);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.base_addr == 0);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(rt.pitch.pitch_div8_minus1 != 0);
|
||||
EXIT_NOT_IMPLEMENTED(rt.pitch.fmask_pitch_div8_minus1 != 0);
|
||||
EXIT_NOT_IMPLEMENTED(rt.slice.slice_div64_minus1 != 0);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(rt.view.base_array_slice_index > rt.view.last_array_slice_index);
|
||||
if (rt.view.base_array_slice_index != 0x00000000 ||
|
||||
rt.view.last_array_slice_index != 0x00000000) {
|
||||
@@ -257,11 +226,6 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
const bool native_2x_one_frag =
|
||||
rt.info.fmask_compression_enable && !rt.info.fmask_data_compression_disable &&
|
||||
rt.info.cmask_fast_clear_enable && rt.fmask.addr != 0 && rt.cmask.addr != 0 &&
|
||||
rt.attrib.num_samples == 1 && rt.attrib.num_fragments == 1;
|
||||
EXIT_NOT_IMPLEMENTED(rt.info.fmask_one_frag_mode && !native_2x_one_frag);
|
||||
if (rt.info.fmask_compression_enable) {
|
||||
EXIT_NOT_IMPLEMENTED(rt.attrib.num_samples == 0 && rt.attrib.num_fragments == 0);
|
||||
static bool logged = false;
|
||||
@@ -286,22 +250,6 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
if (rt.info.cmask_is_linear != 0x00000000 || rt.info.cmask_addr_type != 0x00000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("RenderTarget: temporary: ignoring PS5 CMASK metadata fields "
|
||||
"is_linear=%u addr_type=%u\n",
|
||||
rt.info.cmask_is_linear, rt.info.cmask_addr_type);
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
if (rt.info.alt_tile_mode) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("RenderTarget: temporary: ignoring alternate tile mode flag\n");
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
if (rt.info.blend_bypass) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -328,8 +276,6 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
// EXIT_NOT_IMPLEMENTED(rt.tile_mode != 0x0000000a);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.fmask_tile_mode != 0x0000000a);
|
||||
if (rt.attrib.num_samples != 0x00000000 || rt.attrib.num_fragments != 0x00000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -364,34 +310,22 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
if (!RenderIsColorDimension(rt.attrib3.dimension)) {
|
||||
EXIT("unknown PS5 render-target dimension: 0x%08" PRIx32 "\n", rt.attrib3.dimension);
|
||||
}
|
||||
if (!rt.attrib3.cmask_pipe_aligned) {
|
||||
if (!rt.attrib3.metadata_pipe_aligned) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("RenderTarget: temporary: accepting unaligned PS5 CMASK pipe flag\n");
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
if (!rt.attrib3.dcc_pipe_aligned) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
LOGF("RenderTarget: temporary: accepting unaligned PS5 DCC pipe flag\n");
|
||||
LOGF("RenderTarget: temporary: accepting unaligned PS5 metadata pipe flag\n");
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(rt.attrib3.write_vrs_rate_hint_to_cmask);
|
||||
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc_max_uncompressed_block_size != 0x00000002);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.max_compressed_block_size != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(rt.dcc.min_compressed_block_size != 0x00000000);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.color_transform != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(rt.dcc.overwrite_combiner_disable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.force_independent_blocks != false);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.independent_128b_blocks != false);
|
||||
// EXIT_NOT_IMPLEMENTED(rt.dcc.data_write_on_dcc_clear_to_reg != false);
|
||||
EXIT_NOT_IMPLEMENTED(rt.dcc.dcc_clear_key_enable != false);
|
||||
if (rt.cmask.addr != 0x0000000000000000 || rt.cmask_slice.slice_minus1 != 0x00000000 ||
|
||||
rt.fmask.addr != 0x0000000000000000 ||
|
||||
(rt.fmask_slice.slice_minus1 != 0x00000000 &&
|
||||
rt.fmask_slice.slice_minus1 != rt.slice.slice_div64_minus1) ||
|
||||
if (rt.cmask.addr != 0x0000000000000000 || rt.fmask.addr != 0x0000000000000000 ||
|
||||
rt.dcc_addr.addr != 0x0000000000000000) {
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
@@ -401,9 +335,6 @@ static void RtCheck(const HW::RenderTarget& rt) {
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(rt.size.width != 0);
|
||||
EXIT_NOT_IMPLEMENTED(rt.size.height != 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,37 +354,20 @@ static void ZPrint(const char* func, const HW::DepthRenderTarget& z) {
|
||||
"\t stencil_info.htile_stencil_disabled = %s\n"
|
||||
"\t stencil_info.expclear_enabled = %s\n"
|
||||
"\t stencil_info.partially_resident = %s\n"
|
||||
"\t depth_info.addr5_swizzle_mask = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.array_mode = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.pipe_config = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.bank_width = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.bank_height = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.macro_tile_aspect = 0x%08" PRIx32 "\n"
|
||||
"\t depth_info.num_banks = 0x%08" PRIx32 "\n"
|
||||
"\t depth_view.slice_start = 0x%08" PRIx32 "\n"
|
||||
"\t depth_view.slice_max = 0x%08" PRIx32 "\n"
|
||||
"\t depth_view.current_mip_level = 0x%02" PRIx8 "\n"
|
||||
"\t depth_view.depth_write_disable = %s\n"
|
||||
"\t depth_view.stencil_write_disable = %s\n"
|
||||
"\t htile_surface.linear = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.full_cache = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.htile_uses_preload_win = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.preload = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.prefetch_width = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.prefetch_height = 0x%08" PRIx32 "\n"
|
||||
"\t htile_surface.dst_outside_zero_to_one = 0x%08" PRIx32 "\n"
|
||||
"\t z_read_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t stencil_read_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t z_write_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t stencil_write_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t pitch_div8_minus1 = 0x%08" PRIx32 "\n"
|
||||
"\t height_div8_minus1 = 0x%08" PRIx32 "\n"
|
||||
"\t slice_div64_minus1 = 0x%08" PRIx32 "\n"
|
||||
"\t htile_data_base_addr = 0x%016" PRIx64 "\n"
|
||||
"\t width = 0x%08" PRIx32 "\n"
|
||||
"\t height = 0x%08" PRIx32 "\n"
|
||||
"\t shading_rate_encoding = 0x%02" PRIx8 "\n"
|
||||
"\t size.x_max = 0x%04" PRIx16 "\n"
|
||||
"\t size.y_max = 0x%04" PRIx16 "\n",
|
||||
"\t size.y_max = 0x%04" PRIx16 "\n"
|
||||
"\t size.valid = %s\n",
|
||||
static_cast<uint32_t>(z.z_info.format), z.z_info.num_samples,
|
||||
static_cast<uint32_t>(z.z_info.texture_compatibility),
|
||||
z.z_info.htile_acceleration ? "true" : "false",
|
||||
@@ -464,30 +378,27 @@ static void ZPrint(const char* func, const HW::DepthRenderTarget& z) {
|
||||
static_cast<uint32_t>(z.stencil_info.texture_compatibility),
|
||||
z.stencil_info.htile_stencil_disabled ? "true" : "false",
|
||||
z.stencil_info.expclear_enabled ? "true" : "false",
|
||||
z.stencil_info.partially_resident ? "true" : "false", z.depth_info.addr5_swizzle_mask,
|
||||
z.depth_info.array_mode, z.depth_info.pipe_config, z.depth_info.bank_width,
|
||||
z.depth_info.bank_height, z.depth_info.macro_tile_aspect, z.depth_info.num_banks,
|
||||
z.depth_view.slice_start, z.depth_view.slice_max, z.depth_view.current_mip_level,
|
||||
z.stencil_info.partially_resident ? "true" : "false", z.depth_view.slice_start,
|
||||
z.depth_view.slice_max, z.depth_view.current_mip_level,
|
||||
z.depth_view.depth_write_disable ? "true" : "false",
|
||||
z.depth_view.stencil_write_disable ? "true" : "false", z.htile_surface.linear,
|
||||
z.htile_surface.full_cache, z.htile_surface.htile_uses_preload_win,
|
||||
z.htile_surface.preload, z.htile_surface.prefetch_width, z.htile_surface.prefetch_height,
|
||||
z.htile_surface.dst_outside_zero_to_one, z.z_read_base_addr, z.stencil_read_base_addr,
|
||||
z.z_write_base_addr, z.stencil_write_base_addr, z.pitch_div8_minus1, z.height_div8_minus1,
|
||||
z.slice_div64_minus1, z.htile_data_base_addr, z.width, z.height, z.size.x_max,
|
||||
z.size.y_max);
|
||||
z.depth_view.stencil_write_disable ? "true" : "false", z.z_read_base_addr,
|
||||
z.stencil_read_base_addr, z.z_write_base_addr, z.stencil_write_base_addr,
|
||||
z.htile_data_base_addr, z.shading_rate_encoding, z.size.x_max, z.size.y_max,
|
||||
z.size.valid ? "true" : "false");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
|
||||
const HW::RenderControl& rc) {
|
||||
const bool depth_active =
|
||||
dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable || rc.depth_clear_enable;
|
||||
const bool stencil_active = dc.stencil_enable || rc.stencil_clear_enable;
|
||||
const bool depth_active = dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable ||
|
||||
rc.depth_clear_enable || rc.copy_depth_to_color;
|
||||
const bool stencil_active =
|
||||
dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color;
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(rc.copy_depth_to_color || rc.copy_stencil_to_color);
|
||||
EXIT_NOT_IMPLEMENTED(!z.z_info.HasValidTextureCompatibility());
|
||||
EXIT_NOT_IMPLEMENTED(!z.stencil_info.HasValidTextureCompatibility());
|
||||
if (z.z_info.format == Prospero::DepthFormat::kInvalid) {
|
||||
@@ -524,21 +435,7 @@ static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
|
||||
|
||||
if (z.z_info.format != Prospero::DepthFormat::kInvalid ||
|
||||
z.stencil_info.format != Prospero::StencilFormat::kInvalid) {
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.addr5_swizzle_mask != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.array_mode != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.pipe_config != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.bank_width != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.bank_height != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.macro_tile_aspect != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.depth_info.num_banks != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.linear != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.full_cache != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.htile_uses_preload_win != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.preload != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.prefetch_width != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.prefetch_height != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.htile_surface.dst_outside_zero_to_one != 0x00000000);
|
||||
EXIT_NOT_IMPLEMENTED(z.shading_rate_encoding != 0);
|
||||
|
||||
if (z.depth_view.current_mip_level != 0x00000000) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
@@ -560,23 +457,8 @@ static void ZCheck(const HW::DepthRenderTarget& z, const HW::DepthControl& dc,
|
||||
EXIT_NOT_IMPLEMENTED(!z.depth_view.stencil_write_disable &&
|
||||
z.stencil_read_base_addr != z.stencil_write_base_addr);
|
||||
EXIT_NOT_IMPLEMENTED(!z.depth_view.depth_write_disable && z.z_write_base_addr == 0);
|
||||
// EXIT_NOT_IMPLEMENTED(z.pitch_div8_minus1 != 0x000000ff);
|
||||
// EXIT_NOT_IMPLEMENTED(z.height_div8_minus1 != 0x0000008f);
|
||||
// EXIT_NOT_IMPLEMENTED(z.slice_div64_minus1 != 0x00008fff);
|
||||
// EXIT_NOT_IMPLEMENTED(z.htile_data_base_addr == 0);
|
||||
// EXIT_NOT_IMPLEMENTED(z.width != 0x00000780);
|
||||
// EXIT_NOT_IMPLEMENTED(z.height != 0x00000438);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(z.width != 0);
|
||||
EXIT_NOT_IMPLEMENTED(z.height != 0);
|
||||
if (z.size.x_max == 0 || z.size.y_max == 0) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) < 16) {
|
||||
LOGF("DepthTarget: temporary: inferring PS5 default depth extent x_max=0x%04" PRIx16
|
||||
", y_max=0x%04" PRIx16 "\n",
|
||||
z.size.x_max, z.size.y_max);
|
||||
}
|
||||
}
|
||||
EXIT_NOT_IMPLEMENTED(!z.size.valid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,8 +641,6 @@ static void DPrint(const char* func, const HW::DepthControl& c, const HW::Stenci
|
||||
"\t backface_enable = %s\n"
|
||||
"\t stencilfunc = %" PRIu8 "\n"
|
||||
"\t stencilfunc_bf = %" PRIu8 "\n"
|
||||
"\t color_writes_on_depth_fail_enable = %s\n"
|
||||
"\t color_writes_on_depth_pass_disable = %s\n"
|
||||
"\t stencil_fail = %" PRIu8 "\n"
|
||||
"\t stencil_zpass = %" PRIu8 "\n"
|
||||
"\t stencil_zfail = %" PRIu8 "\n"
|
||||
@@ -777,12 +657,11 @@ static void DPrint(const char* func, const HW::DepthControl& c, const HW::Stenci
|
||||
"\t stencil_opval_bf = %" PRIu8 "\n",
|
||||
c.stencil_enable ? "true" : "false", c.z_enable ? "true" : "false",
|
||||
c.z_write_enable ? "true" : "false", c.depth_bounds_enable ? "true" : "false", c.zfunc,
|
||||
c.backface_enable ? "true" : "false", c.stencilfunc, c.stencilfunc_bf,
|
||||
c.color_writes_on_depth_fail_enable ? "true" : "false",
|
||||
c.color_writes_on_depth_pass_disable ? "true" : "false", s.stencil_fail, s.stencil_zpass,
|
||||
s.stencil_zfail, s.stencil_fail_bf, s.stencil_zpass_bf, s.stencil_zfail_bf,
|
||||
sm.stencil_testval, sm.stencil_mask, sm.stencil_writemask, sm.stencil_opval,
|
||||
sm.stencil_testval_bf, sm.stencil_mask_bf, sm.stencil_writemask_bf, sm.stencil_opval_bf);
|
||||
c.backface_enable ? "true" : "false", c.stencilfunc, c.stencilfunc_bf, s.stencil_fail,
|
||||
s.stencil_zpass, s.stencil_zfail, s.stencil_fail_bf, s.stencil_zpass_bf,
|
||||
s.stencil_zfail_bf, sm.stencil_testval, sm.stencil_mask, sm.stencil_writemask,
|
||||
sm.stencil_opval, sm.stencil_testval_bf, sm.stencil_mask_bf, sm.stencil_writemask_bf,
|
||||
sm.stencil_opval_bf);
|
||||
}
|
||||
|
||||
static void DCheck(const HW::DepthControl& c, const HW::StencilControl& s,
|
||||
@@ -794,8 +673,6 @@ static void DCheck(const HW::DepthControl& c, const HW::StencilControl& s,
|
||||
// Back-face stencil state is handled separately when enabled.
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencilfunc != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencilfunc_bf != 0);
|
||||
EXIT_NOT_IMPLEMENTED(c.color_writes_on_depth_fail_enable != false);
|
||||
EXIT_NOT_IMPLEMENTED(c.color_writes_on_depth_pass_disable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(s.stencil_fail != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(s.stencil_zpass != 0);
|
||||
// EXIT_NOT_IMPLEMENTED(s.stencil_zfail != 0);
|
||||
@@ -827,7 +704,12 @@ static void EqaaPrint(const char* func, const HW::EqaaControl& c) {
|
||||
c.static_anchor_associations ? "true" : "false");
|
||||
}
|
||||
|
||||
static void EqaaCheck(const HW::EqaaControl& c) {
|
||||
static void EqaaCheck(const HW::EqaaControl& c, const HW::AaConfig& cf) {
|
||||
// The EQAA controls only take effect while multisampling is on, so a single-sample
|
||||
// target leaves them inert and has nothing to warn about.
|
||||
if (cf.msaa_num_samples == 0) {
|
||||
return;
|
||||
}
|
||||
if (c.max_anchor_samples != 0 || c.ps_iter_samples != 0 || c.mask_export_num_samples != 0 ||
|
||||
c.alpha_to_mask_num_samples != 0 || c.high_quality_intersections ||
|
||||
c.incoherent_eqaa_reads || c.interpolate_comp_z || c.static_anchor_associations) {
|
||||
@@ -1192,7 +1074,7 @@ void hw_check(const CommandBuffer& buffer) {
|
||||
log_phase("blend");
|
||||
BcCheck(bc, bclr, cc);
|
||||
log_phase("eqaa");
|
||||
EqaaCheck(eqaa);
|
||||
EqaaCheck(eqaa, ac);
|
||||
log_phase("aa");
|
||||
AaCheck(aa, ac);
|
||||
log_phase("done");
|
||||
|
||||
@@ -108,9 +108,10 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
const auto& sc = hw.GetStencilControl();
|
||||
const auto& sm = hw.GetStencilMask();
|
||||
const bool has_stencil = z.stencil_info.format != Prospero::StencilFormat::kInvalid;
|
||||
const bool depth_active =
|
||||
dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable || rc.depth_clear_enable;
|
||||
const bool stencil_active = has_stencil && (dc.stencil_enable || rc.stencil_clear_enable);
|
||||
const bool depth_active = dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable ||
|
||||
rc.depth_clear_enable || rc.copy_depth_to_color;
|
||||
const bool stencil_active =
|
||||
has_stencil && (dc.stencil_enable || rc.stencil_clear_enable || rc.copy_stencil_to_color);
|
||||
if (!depth_active && !stencil_active) {
|
||||
return;
|
||||
}
|
||||
@@ -127,22 +128,14 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
!z.stencil_info.expclear_enabled && !z.stencil_info.partially_resident &&
|
||||
z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 &&
|
||||
z.depth_view.current_mip_level == 0 && !z.depth_view.depth_write_disable &&
|
||||
!z.depth_view.stencil_write_disable && z.depth_info.addr5_swizzle_mask == 0 &&
|
||||
z.depth_info.array_mode == 0 && z.depth_info.pipe_config == 0 &&
|
||||
z.depth_info.bank_width == 0 && z.depth_info.bank_height == 0 &&
|
||||
z.depth_info.macro_tile_aspect == 0 && z.depth_info.num_banks == 0 &&
|
||||
z.htile_surface.linear == 0 && z.htile_surface.full_cache == 0 &&
|
||||
z.htile_surface.htile_uses_preload_win == 0 && z.htile_surface.preload == 0 &&
|
||||
z.htile_surface.prefetch_width == 0 && z.htile_surface.prefetch_height == 0 &&
|
||||
z.htile_surface.dst_outside_zero_to_one == 0 && z.z_read_base_addr == 0 &&
|
||||
z.z_write_base_addr == 0 && z.stencil_read_base_addr == 0 &&
|
||||
z.stencil_write_base_addr == 0 && z.htile_data_base_addr == 0 &&
|
||||
!z.depth_view.stencil_write_disable && z.z_read_base_addr == 0 && z.z_write_base_addr == 0 &&
|
||||
z.stencil_read_base_addr == 0 && z.stencil_write_base_addr == 0 &&
|
||||
z.htile_data_base_addr == 0 &&
|
||||
// DB_DEPTH_SIZE_XY is independent state and may remain programmed after the attachment
|
||||
// formats and addresses are unbound. A zero encoding is the valid 1x1 value, so its
|
||||
// presence alone must not manufacture a depth attachment.
|
||||
!z.z_info.htile_acceleration && !z.width_height_valid && !z.pitch_height_valid &&
|
||||
z.size.x_max == 0 && z.size.y_max == 0 && z.pitch_div8_minus1 == 0 &&
|
||||
z.height_div8_minus1 == 0 && z.slice_div64_minus1 == 0 && z.width == 0 && z.height == 0;
|
||||
!z.z_info.htile_acceleration && z.shading_rate_encoding == 0 && z.size.x_max == 0 &&
|
||||
z.size.y_max == 0;
|
||||
if (attachment_unbound) {
|
||||
static std::atomic_bool logged = false;
|
||||
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
||||
@@ -165,17 +158,12 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
DepthFatal("invalid depth view: base=%u last=%u", z.depth_view.slice_start,
|
||||
z.depth_view.slice_max);
|
||||
}
|
||||
if (rc.copy_centroid || rc.copy_sample != 0 || z.z_info.expclear_enabled ||
|
||||
if (rc.copy_depth_to_color || rc.copy_stencil_to_color || rc.copy_centroid ||
|
||||
rc.copy_sample != 0 || z.z_info.expclear_enabled ||
|
||||
z.stencil_info.expclear_enabled || z.z_info.partially_resident ||
|
||||
z.stencil_info.partially_resident || z.z_info.max_mip_level != 0 ||
|
||||
z.depth_view.current_mip_level != 0 || z.depth_info.addr5_swizzle_mask != 0 ||
|
||||
z.depth_info.array_mode != 0 || z.depth_info.pipe_config != 0 ||
|
||||
z.depth_info.bank_width != 0 || z.depth_info.bank_height != 0 ||
|
||||
z.depth_info.macro_tile_aspect != 0 || z.depth_info.num_banks != 0 ||
|
||||
z.htile_surface.linear != 0 || z.htile_surface.full_cache != 0 ||
|
||||
z.htile_surface.htile_uses_preload_win != 0 || z.htile_surface.preload != 0 ||
|
||||
z.htile_surface.prefetch_width != 0 || z.htile_surface.prefetch_height != 0 ||
|
||||
z.htile_surface.dst_outside_zero_to_one != 0 || z.z_read_base_addr == 0 ||
|
||||
z.depth_view.current_mip_level != 0 || z.shading_rate_encoding != 0 ||
|
||||
z.z_read_base_addr == 0 ||
|
||||
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
|
||||
(z.z_read_base_addr & 0xffffu) != 0 ||
|
||||
dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways)) {
|
||||
@@ -203,18 +191,13 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
DepthFatal("HTile clear tracking supports at most 32 slices");
|
||||
}
|
||||
}
|
||||
const bool size_xy_valid = z.size.valid;
|
||||
const bool wh_valid = z.width_height_valid && z.width != 0 && z.height != 0;
|
||||
if (!size_xy_valid && !wh_valid) {
|
||||
if (!z.size.valid) {
|
||||
DepthFatal("missing depth extent");
|
||||
}
|
||||
const uint32_t width = size_xy_valid ? static_cast<uint32_t>(z.size.x_max) + 1u : z.width;
|
||||
const uint32_t height = size_xy_valid ? static_cast<uint32_t>(z.size.y_max) + 1u : z.height;
|
||||
if (width > 16384 || height > 16384 ||
|
||||
(size_xy_valid && wh_valid && (width != z.width || height != z.height)) ||
|
||||
(!z.pitch_height_valid &&
|
||||
(z.pitch_div8_minus1 != 0 || z.height_div8_minus1 != 0 || z.slice_div64_minus1 != 0))) {
|
||||
DepthFatal("inconsistent depth extent or encoded layout");
|
||||
const uint32_t width = static_cast<uint32_t>(z.size.x_max) + 1u;
|
||||
const uint32_t height = static_cast<uint32_t>(z.size.y_max) + 1u;
|
||||
if (width > 16384 || height > 16384) {
|
||||
DepthFatal("invalid depth extent");
|
||||
}
|
||||
const auto* policy = FindDepthFormatPolicy(z.z_info.format);
|
||||
if (policy == nullptr) {
|
||||
@@ -229,11 +212,6 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
const auto guest_format = policy->guest_format;
|
||||
const uint32_t bytes = policy->bytes_per_element;
|
||||
const auto pitch = TileGetDepthPitch(width, bytes, z.z_info.num_samples);
|
||||
if (z.pitch_height_valid && ((static_cast<uint64_t>(z.pitch_div8_minus1) + 1u) * 8u != pitch ||
|
||||
(static_cast<uint64_t>(z.height_div8_minus1) + 1u) * 8u !=
|
||||
((static_cast<uint64_t>(height) + 7u) & ~7ull))) {
|
||||
DepthFatal("encoded depth pitch or height mismatch");
|
||||
}
|
||||
TileSizeAlign depth_size {};
|
||||
TileSizeAlign stencil_size {};
|
||||
TileSizeAlign htile_size {};
|
||||
@@ -244,14 +222,6 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
(has_htile != (htile_size.align == 32768 && htile_size.size != 0))) {
|
||||
DepthFatal("unsupported depth/stencil/HTile footprint");
|
||||
}
|
||||
if (z.pitch_height_valid &&
|
||||
(static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u != depth_size.size) {
|
||||
DepthFatal("depth footprint mismatch: extent=%ux%u pitch=%u expected=0x%016" PRIx64
|
||||
" align=0x%016" PRIx64 " encoded_valid=%u encoded=0x%016" PRIx64,
|
||||
width, height, pitch, depth_size.size, depth_size.align,
|
||||
z.pitch_height_valid ? 1u : 0u,
|
||||
(static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u);
|
||||
}
|
||||
if (depth_size.size > UINT64_MAX / view.image_layers ||
|
||||
stencil_size.size > UINT64_MAX / view.image_layers ||
|
||||
htile_size.size > UINT64_MAX / view.image_layers) {
|
||||
@@ -260,9 +230,9 @@ void RenderExecutor::ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer&
|
||||
const auto depth_backing_size = depth_size.size * view.image_layers;
|
||||
const auto stencil_backing_size = stencil_size.size * view.image_layers;
|
||||
const auto htile_backing_size = htile_size.size * view.image_layers;
|
||||
if (depth_backing_size > TRACKER_ADDRESS_SIZE - z.z_read_base_addr ||
|
||||
(has_stencil && stencil_backing_size > TRACKER_ADDRESS_SIZE - z.stencil_read_base_addr) ||
|
||||
(has_htile && htile_backing_size > TRACKER_ADDRESS_SIZE - z.htile_data_base_addr)) {
|
||||
if (!GuestRange {z.z_read_base_addr, depth_backing_size}.Valid() ||
|
||||
(has_stencil && !GuestRange {z.stencil_read_base_addr, stencil_backing_size}.Valid()) ||
|
||||
(has_htile && !GuestRange {z.htile_data_base_addr, htile_backing_size}.Valid())) {
|
||||
DepthFatal("layered depth backing range is invalid");
|
||||
}
|
||||
r.htile = has_htile;
|
||||
|
||||
@@ -73,10 +73,8 @@ namespace {
|
||||
return usage;
|
||||
}
|
||||
|
||||
void ValidateRange(GuestRange range, const char* name) {
|
||||
if ((range.address == 0) != (range.size == 0) ||
|
||||
(range.address != 0 && (range.address >= TRACKER_ADDRESS_SIZE ||
|
||||
range.size > TRACKER_ADDRESS_SIZE - range.address))) {
|
||||
void ValidateOptionalRange(GuestRange range, const char* name) {
|
||||
if (!range.ValidOrEmpty()) {
|
||||
EXIT("invalid %s image range: address=0x%016llx size=0x%016llx\n", name,
|
||||
static_cast<unsigned long long>(range.address),
|
||||
static_cast<unsigned long long>(range.size));
|
||||
@@ -567,13 +565,13 @@ void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
|
||||
namespace ImageOps {
|
||||
|
||||
void Validate(const ImageInfo& info) {
|
||||
ValidateRange(info.data, "data");
|
||||
ValidateRange(info.stencil, "stencil");
|
||||
ValidateOptionalRange(info.data, "data");
|
||||
ValidateOptionalRange(info.stencil, "stencil");
|
||||
|
||||
if (info.pixel_format == vk::Format::eUndefined) {
|
||||
const bool metadata_empty =
|
||||
info.metadata.range.address == 0 && info.metadata.range.size == 0 &&
|
||||
info.metadata.kind == ImageMetadataKind::None && info.metadata.control == 0 &&
|
||||
info.metadata.range.Empty() && info.metadata.kind == ImageMetadataKind::None &&
|
||||
info.metadata.control == 0 &&
|
||||
info.metadata.compression == VideoOutCompression::Uncompressed &&
|
||||
!info.metadata.stencil_compressed;
|
||||
if (info.data.Empty() || info.HasStencil() || !metadata_empty || info.extent.width == 0 ||
|
||||
@@ -620,16 +618,14 @@ void Validate(const ImageInfo& info) {
|
||||
}
|
||||
switch (info.metadata.kind) {
|
||||
case ImageMetadataKind::None:
|
||||
if (info.metadata.range.address != 0 || info.metadata.range.size != 0 ||
|
||||
info.metadata.control != 0 ||
|
||||
if (!info.metadata.range.Empty() || info.metadata.control != 0 ||
|
||||
info.metadata.compression != VideoOutCompression::Uncompressed ||
|
||||
info.metadata.stencil_compressed) {
|
||||
EXIT("metadata-free image has metadata state\n");
|
||||
}
|
||||
break;
|
||||
case ImageMetadataKind::Htile:
|
||||
ValidateRange(info.metadata.range, "HTILE");
|
||||
if (info.metadata.range.Empty() ||
|
||||
if (!info.metadata.range.Valid() ||
|
||||
info.metadata.compression != VideoOutCompression::Uncompressed) {
|
||||
EXIT("invalid HTILE metadata\n");
|
||||
}
|
||||
|
||||
@@ -15,18 +15,6 @@
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct GuestRange {
|
||||
uint64_t address = 0;
|
||||
uint64_t size = 0;
|
||||
|
||||
[[nodiscard]] constexpr bool Empty() const noexcept { return address == 0 || size == 0; }
|
||||
[[nodiscard]] constexpr bool Valid() const noexcept {
|
||||
return !Empty() && address < TRACKER_ADDRESS_SIZE && size <= TRACKER_ADDRESS_SIZE - address;
|
||||
}
|
||||
[[nodiscard]] constexpr uint64_t End() const noexcept { return address + size; }
|
||||
auto operator<=>(const GuestRange&) const = default;
|
||||
};
|
||||
|
||||
enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_256_0, Dcc256_64_64, Unsupported };
|
||||
|
||||
enum class ImageMetadataKind : uint8_t { None, Htile, Dcc };
|
||||
|
||||
@@ -895,9 +895,9 @@ PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runti
|
||||
EXIT_IF(!runtime);
|
||||
const auto& program = *runtime.program;
|
||||
const auto& snapshot = *runtime.resources;
|
||||
std::string error;
|
||||
if (!ShaderRecompiler::IR::ValidateResourceSpecialization(program, snapshot, &error)) {
|
||||
EXIT("invalid native shader runtime snapshot: %s\n", error.c_str());
|
||||
if (!ShaderRecompiler::IR::ValidateResourceSpecialization(program, snapshot)) {
|
||||
EXIT("invalid native shader runtime snapshot: hash=0x%016" PRIx64 " stage=%u\n",
|
||||
program.shader_hash, static_cast<unsigned>(program.stage));
|
||||
}
|
||||
|
||||
PreparedBindings prepared;
|
||||
|
||||
@@ -411,7 +411,7 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf
|
||||
static bool DrawHasValidVertexShader(const HW::Shader& sh_ctx) {
|
||||
|
||||
const auto& vs = sh_ctx.GetVs();
|
||||
return vs.gs_regs.chksum != 0 && ShaderAddressValid(vs.es_regs.data_addr);
|
||||
return ShaderAddressValid(vs.es_regs.data_addr);
|
||||
}
|
||||
|
||||
static bool PixelShaderHasDepthOrCoverageSideEffects(const HW::ShaderRegisters& sh_regs) {
|
||||
@@ -443,7 +443,6 @@ static bool ShouldSkipGeShader(const CommandBuffer& buffer) {
|
||||
};
|
||||
|
||||
const bool ps5_ngg_vertex_path = stages == 0x02002000 && vertex_info.es_regs.data_addr != 0 &&
|
||||
vertex_info.gs_regs.chksum != 0 &&
|
||||
sh_regs.m_vgtGsMaxVertOut == 0x00000000 &&
|
||||
is_known_gs_out_prim_type(sh_regs.m_vgtGsOutPrimType);
|
||||
|
||||
@@ -1450,7 +1449,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, uint32_
|
||||
if (graphics_debug_dump_enabled()) {
|
||||
LOGF("DrawIndexAuto: skipping rect-list draw with no VS param exports and PS inputs: "
|
||||
"ps_inputs=%u ps=0x%016" PRIx64 " es=0x%016" PRIx64 " gs=0x%016" PRIx64 "\n",
|
||||
state.ps_input_info.input_num, sh_ctx.GetPs().ps_regs.chksum,
|
||||
state.ps_input_info.input_num, sh_ctx.GetPs().ps_regs.data_addr,
|
||||
sh_ctx.GetVs().es_regs.data_addr, sh_ctx.GetVs().gs_regs.data_addr);
|
||||
}
|
||||
ResetBindings();
|
||||
|
||||
@@ -157,6 +157,10 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
|
||||
});
|
||||
EXIT_NOT_IMPLEMENTED(devices.empty());
|
||||
|
||||
if (Config::GetGpuIndex() >= 0) {
|
||||
devices = {devices[Config::GetGpuIndex()]};
|
||||
}
|
||||
|
||||
vk::PhysicalDevice best_device = nullptr;
|
||||
uint32_t best_queue_family = static_cast<uint32_t>(-1);
|
||||
SurfaceCapabilities best_capabilities;
|
||||
|
||||
@@ -30,14 +30,6 @@ namespace Libs::Graphics::ShaderRecompiler {
|
||||
|
||||
namespace {
|
||||
|
||||
bool ReadZeroMemory(void*, uint64_t, uint32_t* value) {
|
||||
if (value == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*value = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* GetDumpLabel(const CompileOptions& options) {
|
||||
return options.dump_label != nullptr ? options.dump_label : "ShaderRecompiler";
|
||||
}
|
||||
@@ -472,20 +464,14 @@ EmbeddedFetchData DetectEmbeddedVertexFetch(const Decoder::Program& decoded
|
||||
|
||||
} // namespace
|
||||
|
||||
bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
CompileResult& result, std::string* error) {
|
||||
CompileResult Recompile(std::span<const uint32_t> code, const CompileOptions& options) {
|
||||
if (code.empty()) {
|
||||
if (error != nullptr) {
|
||||
*error = "invalid shader recompiler input";
|
||||
}
|
||||
return false;
|
||||
EXIT("shader recompiler input is empty\n");
|
||||
}
|
||||
if (options.stage != ShaderType::Compute && options.stage != ShaderType::Vertex &&
|
||||
options.stage != ShaderType::Pixel) {
|
||||
if (error != nullptr) {
|
||||
*error = "shader recompiler supports compute, vertex, and pixel stages";
|
||||
}
|
||||
return false;
|
||||
EXIT("shader recompiler received unsupported stage %u\n",
|
||||
static_cast<unsigned>(options.stage));
|
||||
}
|
||||
|
||||
const auto compile_begin = std::chrono::steady_clock::now();
|
||||
@@ -500,9 +486,7 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
static_cast<uint64_t>(code.size()));
|
||||
|
||||
Decoder::Program decoded;
|
||||
if (!Decoder::DecodeProgram(code, decoded, error)) {
|
||||
return false;
|
||||
}
|
||||
Decoder::DecodeProgram(code, decoded);
|
||||
LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " decode instructions=%" PRIu64
|
||||
" elapsed_ms=%" PRIu64 "\n",
|
||||
GetDumpLabel(options), StageName(options.stage), options.shader_hash,
|
||||
@@ -516,12 +500,9 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
}
|
||||
}
|
||||
|
||||
CFG::Graph cfg;
|
||||
LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " CFG BuildGraph\n", GetDumpLabel(options),
|
||||
StageName(options.stage), options.shader_hash);
|
||||
if (!CFG::BuildGraph(decoded, cfg, error)) {
|
||||
return false;
|
||||
}
|
||||
auto cfg = CFG::BuildGraph(decoded);
|
||||
LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " CFG BuildGraph blocks=%" PRIu64
|
||||
" loops=%" PRIu64 " back_edges=%" PRIu64 " elapsed_ms=%" PRIu64 "\n",
|
||||
GetDumpLabel(options), StageName(options.stage), options.shader_hash,
|
||||
@@ -534,13 +515,12 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
dispatcher_reason = cfg.unsupported_reason;
|
||||
LogDispatcherFallback(options, cfg, "build", dispatcher_reason);
|
||||
} else {
|
||||
std::string structure_error;
|
||||
const auto unstructured_cfg = cfg;
|
||||
const auto unstructured_cfg = cfg;
|
||||
LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " CFG Structurize\n",
|
||||
GetDumpLabel(options), StageName(options.stage), options.shader_hash);
|
||||
if (!CFG::Structurize(cfg, &structure_error)) {
|
||||
if (!CFG::Structurize(cfg)) {
|
||||
dispatcher_fallback = true;
|
||||
dispatcher_reason = structure_error;
|
||||
dispatcher_reason = cfg.unsupported_reason;
|
||||
const auto failure_kind = cfg.failure_kind;
|
||||
const auto failure_block = cfg.failure_block;
|
||||
LogDispatcherFallback(options, cfg, "structurize", dispatcher_reason);
|
||||
@@ -548,7 +528,7 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
cfg.unsupported = true;
|
||||
cfg.failure_kind = failure_kind;
|
||||
cfg.failure_block = failure_block;
|
||||
cfg.unsupported_reason = structure_error;
|
||||
cfg.unsupported_reason = dispatcher_reason;
|
||||
} else {
|
||||
LOGF("%s structured CFG success: blocks=%" PRIu64 "\n", GetDumpLabel(options),
|
||||
static_cast<uint64_t>(cfg.blocks.size()));
|
||||
@@ -560,20 +540,26 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
static_cast<uint64_t>(cfg.natural_loops.size()), phase_ms());
|
||||
}
|
||||
|
||||
IR::Program ir;
|
||||
const ShaderVertexInputInfo* vertex = nullptr;
|
||||
const ShaderPixelInputInfo* pixel = nullptr;
|
||||
const ShaderComputeInputInfo* compute = nullptr;
|
||||
switch (options.stage) {
|
||||
case ShaderType::Vertex: vertex = options.input_info.vertex; break;
|
||||
case ShaderType::Pixel: pixel = options.input_info.pixel; break;
|
||||
case ShaderType::Compute: compute = options.input_info.compute; break;
|
||||
case ShaderType::Vertex:
|
||||
vertex = options.input_info.vertex;
|
||||
break;
|
||||
case ShaderType::Pixel:
|
||||
pixel = options.input_info.pixel;
|
||||
break;
|
||||
case ShaderType::Compute:
|
||||
compute = options.input_info.compute;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
EmbeddedFetchData embedded_fetch;
|
||||
if (options.stage == ShaderType::Vertex && vertex->fetch_embedded) {
|
||||
embedded_fetch = DetectEmbeddedVertexFetch(decoded, vertex, options.user_data_base,
|
||||
options.user_data_count, options.wave_size);
|
||||
static_cast<uint32_t>(options.user_data.size()),
|
||||
options.wave_size);
|
||||
if (!embedded_fetch.loads.empty()) {
|
||||
LOGF("%s embedded vertex fetch plan: detected=%" PRIu64 "\n", GetDumpLabel(options),
|
||||
static_cast<uint64_t>(embedded_fetch.loads.size()));
|
||||
@@ -584,7 +570,7 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
.wave_size = options.wave_size,
|
||||
.shader_hash = options.shader_hash,
|
||||
.user_data_base = options.user_data_base,
|
||||
.user_data_count = options.user_data_count,
|
||||
.user_data_count = static_cast<uint32_t>(options.user_data.size()),
|
||||
.scratch_dwords = options.scratch_dwords,
|
||||
.dispatcher_fallback = dispatcher_fallback,
|
||||
.cfg_failure_kind = cfg.failure_kind,
|
||||
@@ -597,9 +583,7 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
};
|
||||
LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " IR TranslateProgram\n",
|
||||
GetDumpLabel(options), StageName(options.stage), options.shader_hash);
|
||||
if (!Frontend::TranslateProgram(decoded, cfg, translate_options, ir, error)) {
|
||||
return false;
|
||||
}
|
||||
auto ir = Frontend::TranslateProgram(decoded, cfg, translate_options);
|
||||
LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " IR TranslateProgram blocks=%" PRIu64
|
||||
" elapsed_ms=%" PRIu64 "\n",
|
||||
GetDumpLabel(options), StageName(options.stage), options.shader_hash,
|
||||
@@ -626,66 +610,35 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
lds_barriers.inserted_barriers);
|
||||
}
|
||||
}
|
||||
std::string srt_error;
|
||||
if (!IR::BuildSrtPlan(ir, &srt_error)) {
|
||||
LOGF("%s SRT planning failed: %s\n", GetDumpLabel(options), srt_error.c_str());
|
||||
if (error != nullptr) {
|
||||
*error = std::move(srt_error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
IR::BuildSrtPlan(ir);
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
if (!IR::TrackResources(ir, error)) {
|
||||
return false;
|
||||
}
|
||||
IR::TrackResources(ir);
|
||||
IR::EliminateDeadCode(ir.blocks);
|
||||
if (options.stage == ShaderType::Vertex) {
|
||||
ir.info.vertex_offset_sgpr = embedded_fetch.vertex_offset_sgpr;
|
||||
}
|
||||
|
||||
const IR::SrtRuntime runtime {
|
||||
.user_data = options.user_data,
|
||||
.shader_base = reinterpret_cast<uint64_t>(code.data()),
|
||||
.read_memory = options.read_memory,
|
||||
.userdata = options.read_memory_data,
|
||||
.read_specialization_memory = options.read_specialization_memory,
|
||||
};
|
||||
IR::ResourceSnapshot resources;
|
||||
if (options.resource_snapshot != nullptr) {
|
||||
resources = *options.resource_snapshot;
|
||||
} else {
|
||||
IR::SrtRuntime runtime;
|
||||
std::array<uint32_t, 64> zero_user_data {};
|
||||
if (options.user_data == nullptr) {
|
||||
for (uint32_t i = 2; i < zero_user_data.size(); i += 4) {
|
||||
zero_user_data[i] = UINT32_MAX;
|
||||
}
|
||||
}
|
||||
const auto* user_data =
|
||||
options.user_data != nullptr ? options.user_data : zero_user_data.data();
|
||||
runtime.user_data = std::span<const uint32_t>(user_data, options.user_data_count);
|
||||
runtime.shader_base = options.shader_base != 0 ? options.shader_base
|
||||
: reinterpret_cast<uint64_t>(code.data());
|
||||
runtime.read_memory = options.read_memory;
|
||||
runtime.read_specialization_memory = options.read_specialization_memory;
|
||||
if (runtime.read_memory == nullptr && options.user_data == nullptr) {
|
||||
runtime.read_memory = ReadZeroMemory;
|
||||
}
|
||||
runtime.userdata = options.read_memory_data;
|
||||
if (!IR::MaterializeResources(ir, runtime, resources, error)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!IR::SpecializeResources(ir, resources, error)) {
|
||||
return false;
|
||||
if (!IR::MaterializeResources(ir, runtime, resources)) {
|
||||
EXIT("shader resource materialization failed: stage=%s hash=0x%016" PRIx64 "\n",
|
||||
StageName(options.stage), options.shader_hash);
|
||||
}
|
||||
IR::SpecializeResources(ir, resources);
|
||||
|
||||
IR::ShaderInfoOptions info_options;
|
||||
info_options.vertex = vertex;
|
||||
info_options.pixel = pixel;
|
||||
info_options.compute = compute;
|
||||
if (!IR::CollectShaderInfo(ir, info_options, error)) {
|
||||
return false;
|
||||
}
|
||||
if (!IR::AllocateBindings(ir, options.push_constant_offset, error)) {
|
||||
return false;
|
||||
}
|
||||
if (!Spirv::AnalyzeProgramRequirements(ir, error)) {
|
||||
return false;
|
||||
}
|
||||
IR::CollectShaderInfo(ir, info_options);
|
||||
IR::AllocateBindings(ir, options.push_constant_offset);
|
||||
Spirv::AnalyzeProgramRequirements(ir);
|
||||
std::string ir_dump;
|
||||
if (options.dump_ir) {
|
||||
ir_dump = MakeIrDump(cfg, ir);
|
||||
@@ -694,36 +647,22 @@ bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> spirv;
|
||||
std::string emit_error;
|
||||
LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " SPIR-V EmitProgram\n",
|
||||
GetDumpLabel(options), StageName(options.stage), options.shader_hash);
|
||||
if (!Spirv::EmitProgram(ir, resources, options.input_info, spirv, &emit_error)) {
|
||||
LOGF("%s typed SPIR-V emit failed: %s\n", GetDumpLabel(options), emit_error.c_str());
|
||||
if (dispatcher_fallback && error != nullptr) {
|
||||
*error = fmt::format("dispatcher fallback failed after {}: {}",
|
||||
dispatcher_reason.c_str(), emit_error.c_str());
|
||||
LOGF("%s dispatcher fallback emit failed: %s\n", GetDumpLabel(options), error->c_str());
|
||||
} else if (error != nullptr) {
|
||||
*error = emit_error;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
auto spirv = Spirv::EmitProgram(ir, resources, options.input_info);
|
||||
LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " SPIR-V EmitProgram words=%" PRIu64
|
||||
" elapsed_ms=%" PRIu64 "\n",
|
||||
GetDumpLabel(options), StageName(options.stage), options.shader_hash,
|
||||
static_cast<uint64_t>(spirv.size()), phase_ms());
|
||||
CompileResult result;
|
||||
result.spirv = std::move(spirv);
|
||||
result.program = std::move(ir);
|
||||
result.resources = std::move(resources);
|
||||
if (options.dump_ir) {
|
||||
result.decoded_dump = std::move(decoded_dump);
|
||||
result.ir_dump = std::move(ir_dump);
|
||||
} else {
|
||||
result.decoded_dump.clear();
|
||||
result.ir_dump.clear();
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler
|
||||
@@ -15,19 +15,16 @@ struct CompileOptions {
|
||||
ShaderType stage = ShaderType::Compute;
|
||||
uint32_t wave_size = 64;
|
||||
uint32_t user_data_base = 0;
|
||||
uint32_t user_data_count = 64;
|
||||
uint32_t scratch_dwords = 0;
|
||||
uint64_t shader_hash = 0;
|
||||
uint64_t shader_base = 0;
|
||||
uint32_t push_constant_offset = 0;
|
||||
bool dump_ir = true;
|
||||
bool early_dump = false;
|
||||
const char* dump_label = nullptr;
|
||||
const uint32_t* user_data = nullptr;
|
||||
std::span<const uint32_t> user_data;
|
||||
IR::SrtMemoryReader read_memory = nullptr;
|
||||
IR::SrtMemoryReader read_specialization_memory = nullptr;
|
||||
void* read_memory_data = nullptr;
|
||||
const IR::ResourceSnapshot* resource_snapshot = nullptr;
|
||||
ShaderStageInputInfo input_info;
|
||||
};
|
||||
|
||||
@@ -39,8 +36,8 @@ struct CompileResult {
|
||||
IR::ResourceSnapshot resources;
|
||||
};
|
||||
|
||||
bool TryRecompile(std::span<const uint32_t> code, const CompileOptions& options,
|
||||
CompileResult& result, std::string* error);
|
||||
[[nodiscard]] CompileResult Recompile(std::span<const uint32_t> code,
|
||||
const CompileOptions& options);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler
|
||||
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv {
|
||||
|
||||
using ShaderError::Fail;
|
||||
|
||||
namespace {
|
||||
|
||||
bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
[[noreturn]] void Fail(const IR::Program& program, const char* reason) {
|
||||
EXIT("SPIR-V validation failed: hash=0x%016" PRIx64 " stage=%u reason=%s\n",
|
||||
program.shader_hash, static_cast<unsigned>(program.stage), reason);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void ValidateNativeProgram(const IR::Program& program) {
|
||||
using Kind = IR::DescriptorBindingKind;
|
||||
constexpr auto KindCount = static_cast<size_t>(Kind::Count);
|
||||
std::array<std::vector<uint32_t>, KindCount> expected;
|
||||
@@ -36,13 +40,13 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
const auto kind = IR::DescriptorBindingForImage(program.info.images[i]);
|
||||
if (!kind.has_value()) {
|
||||
return Fail(error, "native shader plan has an invalid image class");
|
||||
Fail(program, "native shader plan has an invalid image class");
|
||||
}
|
||||
present[static_cast<size_t>(*kind)] = true;
|
||||
const auto dynamic = program.info.images[i].mip_mode == IR::ImageMipMode::DynamicStorage;
|
||||
const auto count = dynamic ? program.info.images[i].mip_count : 1u;
|
||||
if (count == 0u || (!dynamic && program.info.images[i].mip_count != 1u)) {
|
||||
return Fail(error, "native shader plan has an invalid image mip descriptor count");
|
||||
Fail(program, "native shader plan has an invalid image mip descriptor count");
|
||||
}
|
||||
expected[static_cast<size_t>(*kind)].insert(expected[static_cast<size_t>(*kind)].end(),
|
||||
count, i);
|
||||
@@ -58,11 +62,11 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
}
|
||||
const auto index = inst.Flags<IR::MemoryFlags>().index;
|
||||
if (index >= program.memory_info.size()) {
|
||||
return Fail(error, "shared operation has invalid memory metadata");
|
||||
Fail(program, "shared operation has invalid memory metadata");
|
||||
}
|
||||
const auto kind = program.memory_info[index].kind;
|
||||
if (kind != IR::ResourceKind::Lds && kind != IR::ResourceKind::Gds) {
|
||||
return Fail(error, "shared operation has invalid resource kind");
|
||||
Fail(program, "shared operation has invalid resource kind");
|
||||
}
|
||||
uses_gds |= kind == IR::ResourceKind::Gds;
|
||||
}
|
||||
@@ -91,13 +95,13 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
const auto kind = static_cast<size_t>(binding.kind);
|
||||
if (kind >= KindCount || seen[kind] || !present[kind] ||
|
||||
binding.resources != expected[kind]) {
|
||||
return Fail(error, "native descriptor groups do not match shader topology");
|
||||
Fail(program, "native descriptor groups do not match shader topology");
|
||||
}
|
||||
seen[kind] = true;
|
||||
}
|
||||
for (size_t i = 0; i < KindCount; i++) {
|
||||
if (present[i] != seen[i]) {
|
||||
return Fail(error, "native shader plan is missing a required descriptor group");
|
||||
Fail(program, "native shader plan is missing a required descriptor group");
|
||||
}
|
||||
}
|
||||
const auto has_user_storage = present[static_cast<size_t>(Kind::UserData)];
|
||||
@@ -114,7 +118,7 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
std::adjacent_find(program.bindings.user_data_registers.begin(),
|
||||
program.bindings.user_data_registers.end()) !=
|
||||
program.bindings.user_data_registers.end()) {
|
||||
return Fail(error, "native user-data layout is inconsistent");
|
||||
Fail(program, "native user-data layout is inconsistent");
|
||||
}
|
||||
|
||||
const auto planning_only_handle = [&](const IR::Inst& handle) {
|
||||
@@ -139,7 +143,7 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
break;
|
||||
}
|
||||
if (dense >= program.info.buffers.size()) {
|
||||
return Fail(error, "typed buffer handle has an invalid dense resource");
|
||||
Fail(program, "typed buffer handle has an invalid dense resource");
|
||||
}
|
||||
break;
|
||||
case IR::ValueOpcode::GetAddressResource:
|
||||
@@ -147,29 +151,29 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
break;
|
||||
}
|
||||
if (inst.NumArgs() != 2 || !program.info.uses_dma) {
|
||||
return Fail(error, "typed address handle has invalid DMA metadata");
|
||||
Fail(program, "typed address handle has invalid DMA metadata");
|
||||
}
|
||||
break;
|
||||
case IR::ValueOpcode::GetScratchResource:
|
||||
if (inst.NumArgs() != 0 || program.scratch_dwords == 0) {
|
||||
return Fail(error, "typed scratch handle has invalid shader metadata");
|
||||
Fail(program, "typed scratch handle has invalid shader metadata");
|
||||
}
|
||||
break;
|
||||
case IR::ValueOpcode::GetImageResource:
|
||||
if (dense >= program.info.images.size()) {
|
||||
return Fail(error, "typed image handle has an invalid dense resource");
|
||||
Fail(program, "typed image handle has an invalid dense resource");
|
||||
}
|
||||
break;
|
||||
case IR::ValueOpcode::GetSamplerResource:
|
||||
if (dense >= program.info.samplers.size()) {
|
||||
return Fail(error, "typed sampler handle has an invalid dense resource");
|
||||
Fail(program, "typed sampler handle has an invalid dense resource");
|
||||
}
|
||||
break;
|
||||
case IR::ValueOpcode::ReadConst: {
|
||||
const auto slot = inst.Arg(1).Resolve();
|
||||
if (!slot.IsImmediate() || slot.GetType() != IR::Type::U32 ||
|
||||
slot.U32() >= program.srt_reads.size()) {
|
||||
return Fail(error, "flattened SRT read has an invalid dense slot");
|
||||
Fail(program, "flattened SRT read has an invalid dense slot");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -177,12 +181,11 @@ bool ValidateNativeProgram(const IR::Program& program, std::string* error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
|
||||
void AnalyzeProgramRequirements(IR::Program& program) {
|
||||
program.spirv_requirements.reset();
|
||||
IR::SpirvRequirements requirements {};
|
||||
const auto MarkBallot = [&] { requirements.subgroup_ballot = true; };
|
||||
@@ -192,30 +195,30 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
|
||||
if (address_access != IR::AddressAccess::None) {
|
||||
const auto memory_index = inst.Flags<IR::MemoryFlags>().index;
|
||||
if (memory_index >= program.memory_info.size()) {
|
||||
return Fail(error, "address operation has invalid memory metadata");
|
||||
Fail(program, "address operation has invalid memory metadata");
|
||||
}
|
||||
if (program.memory_info[memory_index].kind == IR::ResourceKind::Scratch) {
|
||||
if (program.scratch_dwords == 0) {
|
||||
return Fail(error, "scratch operation has no per-thread storage");
|
||||
Fail(program, "scratch operation has no per-thread storage");
|
||||
}
|
||||
requirements.function_scratch = true;
|
||||
} else if (address_access == IR::AddressAccess::Write) {
|
||||
return Fail(error, "writable FLAT/GLOBAL addresses require GPU ownership tracking");
|
||||
Fail(program, "writable FLAT/GLOBAL addresses require GPU ownership tracking");
|
||||
}
|
||||
}
|
||||
if (IR::BufferAccessOf(inst.GetOpcode()) != IR::BufferAccess::None) {
|
||||
const auto memory_index = inst.Flags<IR::MemoryFlags>().index;
|
||||
if (memory_index >= program.memory_info.size()) {
|
||||
return Fail(error, "buffer operation has invalid memory metadata");
|
||||
Fail(program, "buffer operation has invalid memory metadata");
|
||||
}
|
||||
const auto& memory = program.memory_info[memory_index];
|
||||
if (memory.kind == IR::ResourceKind::Buffer) {
|
||||
if (memory.resource >= program.info.buffers.size()) {
|
||||
return Fail(error, "buffer operation has invalid resource metadata");
|
||||
Fail(program, "buffer operation has invalid resource metadata");
|
||||
}
|
||||
if ((program.info.buffers[memory.resource].packed_stride & (1u << 20u)) != 0u) {
|
||||
if (program.stage != ShaderType::Compute) {
|
||||
return Fail(error, "buffer ADD_TID is only valid for compute shaders");
|
||||
Fail(program, "buffer ADD_TID is only valid for compute shaders");
|
||||
}
|
||||
requirements.subgroup_local_invocation_id = true;
|
||||
}
|
||||
@@ -225,11 +228,11 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
|
||||
if (shared_access != IR::SharedAccess::None) {
|
||||
const auto index = inst.Flags<IR::MemoryFlags>().index;
|
||||
if (index >= program.memory_info.size()) {
|
||||
return Fail(error, "shared operation has invalid memory metadata");
|
||||
Fail(program, "shared operation has invalid memory metadata");
|
||||
}
|
||||
const auto kind = program.memory_info[index].kind;
|
||||
if (kind != IR::ResourceKind::Lds && kind != IR::ResourceKind::Gds) {
|
||||
return Fail(error, "shared operation has invalid resource kind");
|
||||
Fail(program, "shared operation has invalid resource kind");
|
||||
}
|
||||
if (program.stage != ShaderType::Compute && kind == IR::ResourceKind::Lds) {
|
||||
requirements.function_lds = true;
|
||||
@@ -283,7 +286,7 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
|
||||
case IR::ValueOpcode::SetAttribute: {
|
||||
const auto index = inst.Flags<IR::ExportFlags>().index;
|
||||
if (index >= program.export_info.size()) {
|
||||
return Fail(error, "attribute export has invalid metadata");
|
||||
Fail(program, "attribute export has invalid metadata");
|
||||
}
|
||||
if (program.stage == ShaderType::Pixel &&
|
||||
program.export_info[index].vm) {
|
||||
@@ -296,37 +299,30 @@ bool AnalyzeProgramRequirements(IR::Program& program, std::string* error) {
|
||||
}
|
||||
}
|
||||
program.spirv_requirements.emplace(requirements);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resources,
|
||||
ShaderStageInputInfo input_info, std::vector<uint32_t>& spirv,
|
||||
std::string* error) {
|
||||
std::vector<uint32_t> EmitProgram(const IR::Program& program,
|
||||
const IR::ResourceSnapshot& resources,
|
||||
ShaderStageInputInfo input_info) {
|
||||
using namespace Emitter;
|
||||
|
||||
if (program.stage != ShaderType::Compute && program.stage != ShaderType::Vertex &&
|
||||
program.stage != ShaderType::Pixel) {
|
||||
SetError(error, "binary SPIR-V emitter supports compute, vertex, and pixel shaders");
|
||||
return false;
|
||||
Fail(program, "binary SPIR-V emitter supports compute, vertex, and pixel shaders");
|
||||
}
|
||||
if (!program.srt_plan_complete || !program.resource_tracking_complete ||
|
||||
!program.shader_info_complete || !program.binding_layout_complete ||
|
||||
!program.spirv_requirements.has_value()) {
|
||||
SetError(error, "SPIR-V emitter requires a fully planned native shader program");
|
||||
return false;
|
||||
Fail(program, "SPIR-V emitter requires a fully planned native shader program");
|
||||
}
|
||||
if (!IR::ValidateResourceSnapshot(program, resources, error)) {
|
||||
return false;
|
||||
if (!IR::ValidateResourceSnapshot(program, resources)) {
|
||||
Fail(program, "resource snapshot is inconsistent with the native shader plan");
|
||||
}
|
||||
if (!IR::ValidateResourceSpecialization(program, resources, error)) {
|
||||
return false;
|
||||
}
|
||||
if (!ValidateNativeProgram(program, error)) {
|
||||
return false;
|
||||
}
|
||||
if (!IR::ValidateProgram(program, true, error)) {
|
||||
return false;
|
||||
if (!IR::ValidateResourceSpecialization(program, resources)) {
|
||||
Fail(program, "resource specialization is inconsistent with the native shader plan");
|
||||
}
|
||||
ValidateNativeProgram(program);
|
||||
IR::ValidateProgram(program, true);
|
||||
EmitterState state(program, resources, input_info);
|
||||
state.stage = program.stage;
|
||||
state.wave_size = program.wave_size;
|
||||
@@ -337,17 +333,13 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
||||
AllocateInputVariables(state);
|
||||
AllocateOutputVariables(state);
|
||||
DefineModule(state);
|
||||
if (!EmitProgram(state, program, error)) {
|
||||
return false;
|
||||
}
|
||||
EmitProgram(state, program);
|
||||
|
||||
auto binary = state.builder.Build();
|
||||
if (binary.empty()) {
|
||||
SetError(error, "SPIR-V builder returned an empty module");
|
||||
return false;
|
||||
Fail(program, "SPIR-V builder returned an empty module");
|
||||
}
|
||||
spirv = std::move(binary);
|
||||
return true;
|
||||
return binary;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Spirv
|
||||
@@ -9,10 +9,11 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv {
|
||||
|
||||
bool AnalyzeProgramRequirements(IR::Program& program, std::string* error);
|
||||
void AnalyzeProgramRequirements(IR::Program& program);
|
||||
|
||||
bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resources,
|
||||
ShaderStageInputInfo input_info, std::vector<uint32_t>& spirv, std::string* error);
|
||||
std::vector<uint32_t> EmitProgram(const IR::Program& program,
|
||||
const IR::ResourceSnapshot& resources,
|
||||
ShaderStageInputInfo input_info);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Spirv
|
||||
|
||||
|
||||
@@ -37,12 +37,6 @@ bool PixelParameterIsCustom(const EmitterState& state, uint32_t attr) {
|
||||
ShaderPixelParameterIsCustom(*state.input_info.pixel, attr);
|
||||
}
|
||||
|
||||
void SetError(std::string* error, const char* message) {
|
||||
if (error != nullptr) {
|
||||
*error = message;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasOutput(const std::vector<OutputBinding>& outputs, IR::StageOutputKind kind,
|
||||
uint32_t index) {
|
||||
return std::any_of(outputs.begin(), outputs.end(), [kind, index](const OutputBinding& binding) {
|
||||
|
||||
@@ -439,7 +439,8 @@ struct ValueEmitContext {
|
||||
const IR::MemoryInfo& Memory(const IR::Inst& inst) const;
|
||||
const IR::ExportInfo& Export(const IR::Inst& inst) const;
|
||||
uint32_t Label(const IR::Block* block) const;
|
||||
void Fail(const IR::Inst& inst, const char* reason);
|
||||
[[noreturn]] void Fail(const char* reason) const;
|
||||
[[noreturn]] void Fail(const IR::Inst& inst, const char* reason) const;
|
||||
|
||||
EmitterState& state;
|
||||
const IR::Program& program;
|
||||
@@ -449,8 +450,6 @@ struct ValueEmitContext {
|
||||
std::unordered_map<const IR::Inst*, std::pair<uint32_t, uint32_t>> dispatcher_block_loads;
|
||||
const IR::Block* current_block = nullptr;
|
||||
uint32_t scratch_u32_variable = 0;
|
||||
bool failed = false;
|
||||
std::string error;
|
||||
};
|
||||
|
||||
enum class VertexInputScalarKind { Float, Sint, Uint };
|
||||
@@ -613,8 +612,6 @@ uint32_t VertexParameterVectorOrScalarType(EmitterState& state, VertexInputScala
|
||||
uint32_t VertexParameterInputPointerType(EmitterState& state, VertexInputScalarKind kind,
|
||||
uint32_t components);
|
||||
|
||||
void SetError(std::string* error, const char* message);
|
||||
|
||||
bool HasOutput(const std::vector<OutputBinding>& outputs, IR::StageOutputKind kind, uint32_t index);
|
||||
|
||||
void CopyProgramInputsAndOutputs(EmitterState& state, const IR::Program& program);
|
||||
@@ -864,7 +861,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
|
||||
bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
|
||||
bool EmitProgram(EmitterState& state, const IR::Program& program, std::string* error);
|
||||
void EmitProgram(EmitterState& state, const IR::Program& program);
|
||||
|
||||
void DefineGetBdaPointer(EmitterState& state);
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <fmt/format.h>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
@@ -196,57 +197,50 @@ uint32_t EmitDispatcherNextPc(ValueEmitContext& ctx, const DispatcherFunctionSta
|
||||
}
|
||||
}
|
||||
|
||||
bool EmitDirectInstruction(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
void EmitDirectInstruction(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
if (EmitValueFlow(ctx, inst) || EmitValueAlu(ctx, inst) || EmitValueMemory(ctx, inst) ||
|
||||
EmitValueImage(ctx, inst)) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
ctx.Fail(inst, "has no direct SPIR-V emitter");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EmitStructuredInstruction(ValueEmitContext& ctx, StructuredFunctionState& structured,
|
||||
void EmitStructuredInstruction(ValueEmitContext& ctx, StructuredFunctionState& structured,
|
||||
const IR::Inst& inst) {
|
||||
if (inst.GetOpcode() == IR::ValueOpcode::Phi) {
|
||||
const auto type = ctx.TypeId(inst.GetType());
|
||||
if (type == 0 || inst.NumArgs() == 0) {
|
||||
ctx.Fail(inst, "has no native SPIR-V representation");
|
||||
return false;
|
||||
}
|
||||
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
||||
const auto* predecessor = inst.PhiBlock(index);
|
||||
if (predecessor == nullptr || !ctx.labels.contains(predecessor)) {
|
||||
ctx.Fail(inst, "has a predecessor outside the structured function");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
structured.deferred_phis.push_back(
|
||||
{ctx.state.builder.AddDeferredPhi(type, ctx.Result(inst), inst.NumArgs()), &inst});
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
return EmitDirectInstruction(ctx, inst);
|
||||
EmitDirectInstruction(ctx, inst);
|
||||
}
|
||||
|
||||
bool EmitDispatcherInstruction(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher,
|
||||
void EmitDispatcherInstruction(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher,
|
||||
const IR::Inst& inst) {
|
||||
if (inst.GetOpcode() == IR::ValueOpcode::Phi) {
|
||||
const auto type = ctx.TypeId(inst.GetType());
|
||||
if (type == 0) {
|
||||
ctx.Fail(inst, "cannot be loaded by the dispatcher");
|
||||
return false;
|
||||
}
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpLoad, type, ctx.Result(inst), dispatcher.spills.at(&inst)});
|
||||
return true;
|
||||
}
|
||||
if (!EmitDirectInstruction(ctx, inst)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
EmitDirectInstruction(ctx, inst);
|
||||
if (const auto found = dispatcher.spills.find(&inst); found != dispatcher.spills.end()) {
|
||||
ctx.state.builder.AddFunction(
|
||||
{OpStore, found->second, ctx.Def(IR::Value(const_cast<IR::Inst*>(&inst)))});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename EmitInstruction>
|
||||
@@ -258,14 +252,11 @@ void EmitBlock(ValueEmitContext& ctx, const IR::Block* block, EmitInstruction&&
|
||||
if (inst.GetOpcode() == IR::ValueOpcode::Phi) {
|
||||
if (emitted_non_phi) {
|
||||
ctx.Fail(inst, "appears after a non-Phi instruction");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
emitted_non_phi = true;
|
||||
}
|
||||
if (!emit_instruction(inst)) {
|
||||
return;
|
||||
}
|
||||
emit_instruction(inst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,15 +266,10 @@ void PatchStructuredPhis(ValueEmitContext& ctx, StructuredFunctionState& structu
|
||||
const auto* predecessor = deferred.instruction->PhiBlock(index);
|
||||
const auto found = structured.block_exit_labels.find(predecessor);
|
||||
if (found == structured.block_exit_labels.end()) {
|
||||
ctx.failed = true;
|
||||
ctx.error = "typed Phi predecessor was not emitted";
|
||||
return;
|
||||
ctx.Fail(*deferred.instruction, "has a predecessor that was not emitted");
|
||||
}
|
||||
ctx.state.builder.PatchDeferredPhi(
|
||||
deferred.phi, index, ctx.Def(deferred.instruction->Arg(index)), found->second);
|
||||
if (ctx.failed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,20 +277,15 @@ void PatchStructuredPhis(ValueEmitContext& ctx, StructuredFunctionState& structu
|
||||
void EmitStructuredFunction(ValueEmitContext& ctx) {
|
||||
StructuredFunctionState structured;
|
||||
ctx.state.builder.AddFunction({OpBranch, ctx.Label(ctx.program.blocks.front())});
|
||||
for (size_t index = 0; index < ctx.program.blocks.size() && !ctx.failed; index++) {
|
||||
for (size_t index = 0; index < ctx.program.blocks.size(); index++) {
|
||||
const auto* block = ctx.program.blocks[index];
|
||||
EmitBlock(ctx, block, [&](const IR::Inst& inst) {
|
||||
return EmitStructuredInstruction(ctx, structured, inst);
|
||||
EmitStructuredInstruction(ctx, structured, inst);
|
||||
});
|
||||
if (ctx.failed) {
|
||||
break;
|
||||
}
|
||||
structured.block_exit_labels.emplace(block, ctx.state.current_label);
|
||||
EmitStructuredTerminator(ctx, block, ctx.program.block_info[index]);
|
||||
}
|
||||
if (!ctx.failed) {
|
||||
PatchStructuredPhis(ctx, structured);
|
||||
}
|
||||
PatchStructuredPhis(ctx, structured);
|
||||
}
|
||||
|
||||
void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher) {
|
||||
@@ -312,11 +293,8 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
|
||||
const auto* entry = ctx.program.blocks.front();
|
||||
state.builder.AddFunction({OpBranch, ctx.Label(entry)});
|
||||
EmitBlock(ctx, entry, [&](const IR::Inst& inst) {
|
||||
return EmitDispatcherInstruction(ctx, dispatcher, inst);
|
||||
EmitDispatcherInstruction(ctx, dispatcher, inst);
|
||||
});
|
||||
if (ctx.failed) {
|
||||
return;
|
||||
}
|
||||
const auto initial_pc =
|
||||
EmitDispatcherNextPc(ctx, dispatcher, entry, ctx.program.block_info.front());
|
||||
const auto initial_parent = state.current_label;
|
||||
@@ -347,22 +325,16 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState
|
||||
std::vector<uint32_t> next_pc_words {OpPhi, TypeU32(state), next_pc,
|
||||
ConstantU32(state, UINT32_MAX), dispatcher.select_label};
|
||||
|
||||
for (size_t index = 1; index < ctx.program.blocks.size() && !ctx.failed; index++) {
|
||||
for (size_t index = 1; index < ctx.program.blocks.size(); index++) {
|
||||
EmitBlock(ctx, ctx.program.blocks[index], [&](const IR::Inst& inst) {
|
||||
return EmitDispatcherInstruction(ctx, dispatcher, inst);
|
||||
EmitDispatcherInstruction(ctx, dispatcher, inst);
|
||||
});
|
||||
if (ctx.failed) {
|
||||
break;
|
||||
}
|
||||
const auto selected = EmitDispatcherNextPc(ctx, dispatcher, ctx.program.blocks[index],
|
||||
ctx.program.block_info[index]);
|
||||
next_pc_words.push_back(selected);
|
||||
next_pc_words.push_back(state.current_label);
|
||||
state.builder.AddFunction({OpBranch, dispatcher.after_switch_label});
|
||||
}
|
||||
if (ctx.failed) {
|
||||
return;
|
||||
}
|
||||
EmitLabel(state, dispatcher.after_switch_label);
|
||||
state.builder.AddFunction(next_pc_words);
|
||||
state.builder.AddFunction({OpBranch, dispatcher.continue_label});
|
||||
@@ -408,9 +380,7 @@ uint32_t ValueEmitContext::Def(IR::Value value) {
|
||||
}
|
||||
const auto* inst = value.ResolveInstruction();
|
||||
if (inst == nullptr) {
|
||||
failed = true;
|
||||
error = "direct SPIR-V emitter received a non-value argument";
|
||||
return ConstantU32(state, 0);
|
||||
Fail("direct SPIR-V emitter received a non-value argument");
|
||||
}
|
||||
if (dispatcher_spills != nullptr && current_block != nullptr &&
|
||||
inst->Parent() != current_block) {
|
||||
@@ -469,9 +439,7 @@ uint32_t ValueEmitContext::Define(const IR::Inst& inst, uint32_t value) {
|
||||
uint32_t ValueEmitContext::ResourceIndex(IR::Value value, IR::ValueOpcode opcode) {
|
||||
const auto* inst = value.ResolveInstruction();
|
||||
if (inst == nullptr || inst->GetOpcode() != opcode) {
|
||||
failed = true;
|
||||
error = "typed resource handle has the wrong producer";
|
||||
return 0;
|
||||
Fail("typed resource handle has the wrong producer");
|
||||
}
|
||||
return inst->Flags<uint32_t>();
|
||||
}
|
||||
@@ -479,9 +447,7 @@ uint32_t ValueEmitContext::ResourceIndex(IR::Value value, IR::ValueOpcode opcode
|
||||
const IR::Inst* ValueEmitContext::ImageAddress(IR::Value value) {
|
||||
const auto* inst = value.ResolveInstruction();
|
||||
if (inst == nullptr || inst->GetOpcode() != IR::ValueOpcode::MakeImageAddress) {
|
||||
failed = true;
|
||||
error = "typed image address was not constructed by MakeImageAddress";
|
||||
return nullptr;
|
||||
Fail("typed image address was not constructed by MakeImageAddress");
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
@@ -498,12 +464,20 @@ uint32_t ValueEmitContext::Label(const IR::Block* block) const {
|
||||
return labels.at(block);
|
||||
}
|
||||
|
||||
void ValueEmitContext::Fail(const IR::Inst& inst, const char* reason) {
|
||||
failed = true;
|
||||
error = fmt::format("typed opcode {} {}", IR::ValueOpcodeName(inst.GetOpcode()), reason);
|
||||
[[noreturn]] void ValueEmitContext::Fail(const char* reason) const {
|
||||
EXIT("SPIR-V emission failed: hash=0x%016" PRIx64 " stage=%u reason=%s\n",
|
||||
program.shader_hash, static_cast<unsigned>(program.stage), reason);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
bool EmitProgram(EmitterState& state, const IR::Program& program, std::string* error) {
|
||||
[[noreturn]] void ValueEmitContext::Fail(const IR::Inst& inst, const char* reason) const {
|
||||
EXIT("SPIR-V emission failed: hash=0x%016" PRIx64 " stage=%u opcode=%s reason=%s\n",
|
||||
program.shader_hash, static_cast<unsigned>(program.stage),
|
||||
IR::ValueOpcodeName(inst.GetOpcode()), reason);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void EmitProgram(EmitterState& state, const IR::Program& program) {
|
||||
ValueEmitContext ctx(state, program);
|
||||
std::optional<DispatcherFunctionState> dispatcher;
|
||||
if (state.stage == ShaderType::Pixel && state.requirements.pixel_valid_mask) {
|
||||
@@ -562,10 +536,6 @@ bool EmitProgram(EmitterState& state, const IR::Program& program, std::string* e
|
||||
dispatch.merge_label = state.builder.AllocateId();
|
||||
ctx.dispatcher_spills = &dispatch.spills;
|
||||
}
|
||||
if (ctx.failed) {
|
||||
SetError(error, ctx.error.c_str());
|
||||
return false;
|
||||
}
|
||||
DefineGetBdaPointer(state);
|
||||
for (const auto* block: program.blocks) {
|
||||
if (std::ranges::any_of(*block, [](const IR::Inst& inst) {
|
||||
@@ -630,11 +600,6 @@ bool EmitProgram(EmitterState& state, const IR::Program& program, std::string* e
|
||||
EmitStructuredFunction(ctx);
|
||||
}
|
||||
state.builder.AddFunction({OpFunctionEnd});
|
||||
if (ctx.failed) {
|
||||
SetError(error, ctx.error.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fmt/format.h>
|
||||
#include <iterator>
|
||||
@@ -24,19 +26,18 @@ struct SetpcTargetInfo {
|
||||
std::vector<uint32_t> selector_target_pcs;
|
||||
};
|
||||
|
||||
void SetError(std::string* error, const std::string& message) {
|
||||
if (error != nullptr) {
|
||||
*error = message;
|
||||
}
|
||||
}
|
||||
|
||||
void SetFailure(Graph& graph, FailureKind kind, uint32_t block_id, const std::string& message,
|
||||
std::string* error) {
|
||||
void SetFailure(Graph& graph, FailureKind kind, uint32_t block_id, const std::string& message) {
|
||||
graph.unsupported = true;
|
||||
graph.failure_kind = kind;
|
||||
graph.failure_block = block_id;
|
||||
graph.unsupported_reason = message;
|
||||
SetError(error, message);
|
||||
}
|
||||
|
||||
[[noreturn]] void ExitBuildFailure(Graph& graph, FailureKind kind, uint32_t block_id,
|
||||
const std::string& message) {
|
||||
SetFailure(graph, kind, block_id, message);
|
||||
EXIT("shader CFG build failed: %s", message.c_str());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
uint32_t InstructionEndPc(const Instruction& inst) {
|
||||
@@ -1267,7 +1268,7 @@ bool MergeLeavesContainingLoop(const Graph& graph, uint32_t header, uint32_t mer
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CanonicalizeNaturalLoops(Graph& graph, std::string* error) {
|
||||
bool CanonicalizeNaturalLoops(Graph& graph) {
|
||||
const auto rewrite_budget = graph.blocks.size() * 2u + 16u;
|
||||
for (size_t rewrite = 0; rewrite < rewrite_budget; rewrite++) {
|
||||
bool changed = false;
|
||||
@@ -1322,19 +1323,18 @@ bool CanonicalizeNaturalLoops(Graph& graph, std::string* error) {
|
||||
}
|
||||
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, graph.entry_block,
|
||||
"CFG loop canonicalization exceeded rewrite budget", error);
|
||||
"CFG loop canonicalization exceeded rewrite budget");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsolateSemanticLoopHeaders(Graph& graph, std::string* error) {
|
||||
bool IsolateSemanticLoopHeaders(Graph& graph) {
|
||||
const auto isolation_budget = graph.natural_loops.size() + 1u;
|
||||
for (size_t isolation = 0; isolation < isolation_budget; isolation++) {
|
||||
const auto loop = std::find_if(graph.natural_loops.begin(), graph.natural_loops.end(),
|
||||
[&](const auto& value) {
|
||||
const auto* header = graph.FindBlock(value.header);
|
||||
return header != nullptr &&
|
||||
header->inst_begin != header->inst_end;
|
||||
});
|
||||
const auto loop = std::find_if(
|
||||
graph.natural_loops.begin(), graph.natural_loops.end(), [&](const auto& value) {
|
||||
const auto* header = graph.FindBlock(value.header);
|
||||
return header != nullptr && header->inst_begin != header->inst_end;
|
||||
});
|
||||
if (loop == graph.natural_loops.end()) {
|
||||
return true;
|
||||
}
|
||||
@@ -1346,14 +1346,13 @@ bool IsolateSemanticLoopHeaders(Graph& graph, std::string* error) {
|
||||
// displacing OpLoopMerge into a helper-created block.
|
||||
if (!IsolateSemanticLoopHeader(graph, loop->header)) {
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, loop->header,
|
||||
fmt::format("failed to isolate semantic loop header {}", loop->header),
|
||||
error);
|
||||
fmt::format("failed to isolate semantic loop header {}", loop->header));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, graph.entry_block,
|
||||
"CFG semantic loop-header isolation exceeded rewrite budget", error);
|
||||
"CFG semantic loop-header isolation exceeded rewrite budget");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1453,7 +1452,7 @@ std::vector<uint32_t> SelectionRegion(const Graph& graph, const BasicBlock& head
|
||||
return region;
|
||||
}
|
||||
|
||||
bool SplitOneSelectionMerge(Graph& graph, std::string* error) {
|
||||
bool SplitOneSelectionMerge(Graph& graph) {
|
||||
std::vector<uint32_t> loop_headers;
|
||||
loop_headers.reserve(graph.natural_loops.size());
|
||||
for (const auto& loop: graph.natural_loops) {
|
||||
@@ -1501,8 +1500,7 @@ bool SplitOneSelectionMerge(Graph& graph, std::string* error) {
|
||||
graph, FailureKind::StructuredControlFlow, block_id,
|
||||
fmt::format("selection header block {} has externally entered region block {}; "
|
||||
"semantic block cloning is disabled",
|
||||
block_id, *external),
|
||||
error);
|
||||
block_id, *external));
|
||||
return false;
|
||||
}
|
||||
const auto construct_blocks = DominatedBlocks(graph, block_id, merge);
|
||||
@@ -1514,11 +1512,11 @@ bool SplitOneSelectionMerge(Graph& graph, std::string* error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SplitSharedMergeBlocks(Graph& graph, std::string* error) {
|
||||
bool SplitSharedMergeBlocks(Graph& graph) {
|
||||
const auto original_block_count = static_cast<uint32_t>(graph.blocks.size());
|
||||
const auto split_budget = std::max<uint32_t>(16u, original_block_count * 4u);
|
||||
for (uint32_t splits = 0; splits < split_budget; splits++) {
|
||||
if (!SplitOneLoopMerge(graph) && !SplitOneSelectionMerge(graph, error)) {
|
||||
if (!SplitOneLoopMerge(graph) && !SplitOneSelectionMerge(graph)) {
|
||||
return !graph.unsupported;
|
||||
}
|
||||
RebuildPredecessors(graph);
|
||||
@@ -1528,8 +1526,7 @@ bool SplitSharedMergeBlocks(Graph& graph, std::string* error) {
|
||||
fmt::format("CFG shared merge splitting exceeded budget: original_blocks={} "
|
||||
"current_blocks={} split_budget={}",
|
||||
original_block_count, static_cast<uint64_t>(graph.blocks.size()),
|
||||
split_budget),
|
||||
error);
|
||||
split_budget));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1890,12 +1887,11 @@ bool RouteSharedSelectionArm(Graph& graph, uint32_t route_variable) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* error) {
|
||||
graph = {};
|
||||
|
||||
Graph BuildGraph(const Decoder::Program& program) {
|
||||
Graph graph;
|
||||
if (program.instructions.empty()) {
|
||||
SetError(error, "cannot build CFG for empty shader");
|
||||
return false;
|
||||
ExitBuildFailure(graph, FailureKind::InvalidLabel, UINT32_MAX,
|
||||
"cannot build CFG for empty shader");
|
||||
}
|
||||
|
||||
const auto first_pc = program.instructions.front().pc;
|
||||
@@ -1905,11 +1901,10 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
|
||||
for (const auto& inst: program.instructions) {
|
||||
instruction_pcs.insert(inst.pc);
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
SetFailure(graph, FailureKind::UnsupportedInstruction, UINT32_MAX,
|
||||
fmt::format("unsupported decoded instruction in CFG at pc 0x{:08x}: {}",
|
||||
inst.pc, Decoder::InstructionToString(inst).c_str()),
|
||||
error);
|
||||
return false;
|
||||
ExitBuildFailure(
|
||||
graph, FailureKind::UnsupportedInstruction, UINT32_MAX,
|
||||
fmt::format("unsupported decoded instruction in CFG at pc 0x{:08x}: {}", inst.pc,
|
||||
Decoder::InstructionToString(inst).c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1923,11 +1918,9 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
|
||||
const auto next_pc = InstructionEndPc(inst);
|
||||
if (IsBranch(inst.opcode)) {
|
||||
if (!IsValidTarget(inst.branch_target, instruction_pcs, first_pc, end_pc)) {
|
||||
SetFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||
fmt::format("branch at pc 0x{:08x} targets invalid pc 0x{:08x}", inst.pc,
|
||||
inst.branch_target),
|
||||
error);
|
||||
return false;
|
||||
ExitBuildFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||
fmt::format("branch at pc 0x{:08x} targets invalid pc 0x{:08x}",
|
||||
inst.pc, inst.branch_target));
|
||||
}
|
||||
labels.insert(inst.branch_target);
|
||||
if (next_pc <= end_pc) {
|
||||
@@ -1936,21 +1929,19 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
|
||||
} else if (inst.opcode == Opcode::S_SETPC_B64) {
|
||||
SetpcTargetInfo target_info;
|
||||
if (!ResolveSetpcTargets(program, i, target_info)) {
|
||||
SetFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||
fmt::format("unsupported dynamic S_SETPC_B64 at pc 0x{:08x}", inst.pc),
|
||||
error);
|
||||
return false;
|
||||
ExitBuildFailure(
|
||||
graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||
fmt::format("unsupported dynamic S_SETPC_B64 at pc 0x{:08x}", inst.pc));
|
||||
}
|
||||
const auto& target_pcs = target_info.indirect
|
||||
? target_info.target_pcs
|
||||
: std::vector<uint32_t> {target_info.target};
|
||||
for (const auto target: target_pcs) {
|
||||
if (!IsValidTarget(target, instruction_pcs, first_pc, end_pc)) {
|
||||
SetFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||
fmt::format("S_SETPC_B64 at pc 0x{:08x} targets invalid pc 0x{:08x}",
|
||||
inst.pc, target),
|
||||
error);
|
||||
return false;
|
||||
ExitBuildFailure(
|
||||
graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||
fmt::format("S_SETPC_B64 at pc 0x{:08x} targets invalid pc 0x{:08x}",
|
||||
inst.pc, target));
|
||||
}
|
||||
labels.insert(target);
|
||||
}
|
||||
@@ -1971,10 +1962,9 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
|
||||
continue;
|
||||
}
|
||||
if (start != end_pc && !instruction_pcs.contains(start)) {
|
||||
SetFailure(graph, FailureKind::InvalidLabel, UINT32_MAX,
|
||||
fmt::format("CFG label does not start on an instruction: 0x{:08x}", start),
|
||||
error);
|
||||
return false;
|
||||
ExitBuildFailure(
|
||||
graph, FailureKind::InvalidLabel, UINT32_MAX,
|
||||
fmt::format("CFG label does not start on an instruction: 0x{:08x}", start));
|
||||
}
|
||||
|
||||
BasicBlock block;
|
||||
@@ -2046,11 +2036,10 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
|
||||
block.terminator.true_block = pc_to_block.at(last.branch_target);
|
||||
const auto fallthrough = pc_to_block.find(next_pc);
|
||||
if (fallthrough == pc_to_block.end()) {
|
||||
SetFailure(graph, FailureKind::MissingFallthrough, block.id,
|
||||
fmt::format("conditional branch at pc 0x{:08x} has no fallthrough block",
|
||||
last.pc),
|
||||
error);
|
||||
return false;
|
||||
ExitBuildFailure(
|
||||
graph, FailureKind::MissingFallthrough, block.id,
|
||||
fmt::format("conditional branch at pc 0x{:08x} has no fallthrough block",
|
||||
last.pc));
|
||||
}
|
||||
block.terminator.false_block = fallthrough->second;
|
||||
} else {
|
||||
@@ -2131,27 +2120,26 @@ bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* erro
|
||||
graph.unsupported = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return graph;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool StructurizeImpl(Graph& graph, std::string* error) {
|
||||
bool StructurizeImpl(Graph& graph) {
|
||||
if (graph.unsupported || graph.irreducible) {
|
||||
if (graph.unsupported_reason.empty()) {
|
||||
graph.unsupported_reason = "unsupported CFG";
|
||||
}
|
||||
SetError(error, graph.unsupported_reason);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CanonicalizeNaturalLoops(graph, error)) {
|
||||
if (!CanonicalizeNaturalLoops(graph)) {
|
||||
return false;
|
||||
}
|
||||
if (!SplitSharedMergeBlocks(graph, error)) {
|
||||
if (!SplitSharedMergeBlocks(graph)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsolateSemanticLoopHeaders(graph, error)) {
|
||||
if (!IsolateSemanticLoopHeaders(graph)) {
|
||||
return false;
|
||||
}
|
||||
ClearStructuredTerminators(graph);
|
||||
@@ -2164,8 +2152,7 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
|
||||
graph, FailureKind::StructuredControlFlow, header,
|
||||
fmt::format(
|
||||
"duplicate structured merge block {} for header {} (already used by header {})",
|
||||
merge, header, it->second),
|
||||
error);
|
||||
merge, header, it->second));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -2176,16 +2163,14 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
|
||||
if (header == nullptr || loop.merge == UINT32_MAX || loop.continue_block == UINT32_MAX) {
|
||||
SetFailure(
|
||||
graph, FailureKind::StructuredControlFlow, loop.header,
|
||||
fmt::format("loop at block {} has no structured merge/continue", loop.header),
|
||||
error);
|
||||
fmt::format("loop at block {} has no structured merge/continue", loop.header));
|
||||
return false;
|
||||
}
|
||||
if (header->inst_begin != header->inst_end ||
|
||||
header->terminator.kind != TerminatorKind::Branch) {
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, loop.header,
|
||||
fmt::format("loop header {} is not a dedicated empty control block",
|
||||
loop.header),
|
||||
error);
|
||||
SetFailure(
|
||||
graph, FailureKind::StructuredControlFlow, loop.header,
|
||||
fmt::format("loop header {} is not a dedicated empty control block", loop.header));
|
||||
return false;
|
||||
}
|
||||
if (!reserve_merge_block(loop.header, loop.merge)) {
|
||||
@@ -2208,8 +2193,7 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
|
||||
const auto merge = FindSelectionMerge(graph, block);
|
||||
if (merge == UINT32_MAX) {
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, block.id,
|
||||
fmt::format("conditional block {} has no structured merge", block.id),
|
||||
error);
|
||||
fmt::format("conditional block {} has no structured merge", block.id));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2224,16 +2208,15 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Structurize(Graph& graph, std::string* error) {
|
||||
bool Structurize(Graph& graph) {
|
||||
Graph original = graph;
|
||||
if (StructurizeImpl(graph, error)) {
|
||||
if (StructurizeImpl(graph)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Graph failed_graph = std::move(graph);
|
||||
std::string failed_error = error != nullptr ? *error : std::string {};
|
||||
graph = std::move(original);
|
||||
const auto route_budget = static_cast<uint32_t>(graph.blocks.size());
|
||||
Graph failed_graph = std::move(graph);
|
||||
graph = std::move(original);
|
||||
const auto route_budget = static_cast<uint32_t>(graph.blocks.size());
|
||||
// Apply one route at a time and retry. Eagerly routing every matching diamond can
|
||||
// rewrite unrelated selections that were already structurally valid.
|
||||
for (uint32_t route_variable = 0; route_variable < route_budget; route_variable++) {
|
||||
@@ -2241,18 +2224,12 @@ bool Structurize(Graph& graph, std::string* error) {
|
||||
break;
|
||||
}
|
||||
Graph routed = graph;
|
||||
if (error != nullptr) {
|
||||
error->clear();
|
||||
}
|
||||
if (StructurizeImpl(routed, error)) {
|
||||
if (StructurizeImpl(routed)) {
|
||||
graph = std::move(routed);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
graph = std::move(failed_graph);
|
||||
if (error != nullptr) {
|
||||
*error = std::move(failed_error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,8 +108,8 @@ struct Graph {
|
||||
uint32_t FindNearestCommonPostDominator(uint32_t block_a, uint32_t block_b) const;
|
||||
};
|
||||
|
||||
bool BuildGraph(const Decoder::Program& program, Graph& graph, std::string* error);
|
||||
bool Structurize(Graph& graph, std::string* error);
|
||||
Graph BuildGraph(const Decoder::Program& program);
|
||||
bool Structurize(Graph& graph);
|
||||
std::string BranchConditionToString(BranchCondition condition);
|
||||
std::string FailureKindToString(FailureKind kind);
|
||||
std::string GraphToString(const Graph& graph);
|
||||
|
||||
@@ -357,8 +357,8 @@ void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
|
||||
DecodeVectorGpr(vdata, inst.dst);
|
||||
DecodeVectorGpr(vaddr, inst.src0);
|
||||
DecodeScalarSource(srsrc * 4u, pc, inst.src1, nullptr);
|
||||
DecodeScalarSource(ssamp * 4u, pc, inst.src2, nullptr);
|
||||
DecodeScalarSource(srsrc * 4u, pc, inst.src1);
|
||||
DecodeScalarSource(ssamp * 4u, pc, inst.src2);
|
||||
inst.src_count = 3;
|
||||
}
|
||||
|
||||
|
||||
@@ -241,11 +241,11 @@ void DecodeSmem(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
MarkMemoryUnsupported(inst, Family::SMEM, opcode, "SMEM opcode is not implemented");
|
||||
}
|
||||
|
||||
DecodeScalarDestination(sdst, pc, inst.dst, nullptr);
|
||||
DecodeScalarDestination(sdst, pc, inst.dst);
|
||||
// SMEM encodes SBASE in SGPR pairs. Scalar-buffer loads still use the same
|
||||
// pair index; their descriptor operand consumes four SGPRs from that base.
|
||||
DecodeScalarSource(sbase * 2u, pc, inst.src0, nullptr);
|
||||
DecodeScalarSource(soffset, pc, inst.src1, nullptr);
|
||||
DecodeScalarSource(sbase * 2u, pc, inst.src0);
|
||||
DecodeScalarSource(soffset, pc, inst.src1);
|
||||
inst.src_count = 2;
|
||||
}
|
||||
|
||||
@@ -278,8 +278,8 @@ void DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
|
||||
DecodeVectorGpr(vdata, inst.dst);
|
||||
DecodeVectorGpr(vaddr, inst.src0);
|
||||
DecodeScalarSource(srsrc * 4u, pc, inst.src1, nullptr);
|
||||
DecodeScalarSource(soffset, pc, inst.src2, nullptr);
|
||||
DecodeScalarSource(srsrc * 4u, pc, inst.src1);
|
||||
DecodeScalarSource(soffset, pc, inst.src2);
|
||||
inst.src_count = 3;
|
||||
}
|
||||
|
||||
@@ -316,8 +316,8 @@ void DecodeMtbuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
|
||||
DecodeVectorGpr(vdata, inst.dst);
|
||||
DecodeVectorGpr(vaddr, inst.src0);
|
||||
DecodeScalarSource(srsrc * 4u, pc, inst.src1, nullptr);
|
||||
DecodeScalarSource(soffset, pc, inst.src2, nullptr);
|
||||
DecodeScalarSource(srsrc * 4u, pc, inst.src1);
|
||||
DecodeScalarSource(soffset, pc, inst.src2);
|
||||
inst.src_count = 3;
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ void DecodeFlat(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
DecodeVectorGpr(addr + 1u, inst.src1);
|
||||
inst.src_count = 2;
|
||||
} else {
|
||||
DecodeScalarSource(saddr, pc, inst.src1, nullptr);
|
||||
DecodeScalarSource(saddr, pc, inst.src1);
|
||||
inst.src_count = 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,21 +105,18 @@ constexpr auto SOPK_OPS = Detail::MakeOpcodeTable<0x20>(SOPK_OPCODE_LIST);
|
||||
constexpr auto SOPC_OPS = Detail::MakeOpcodeTable<0x80>(SOPC_OPCODE_LIST);
|
||||
constexpr auto SOPP_OPS = Detail::MakeOpcodeTable<0x80>(SOPP_OPCODE_LIST);
|
||||
|
||||
bool DecodeBinarySources(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst, uint32_t ssrc0, uint32_t ssrc1, std::string* error) {
|
||||
if (!DecodeScalarSource(ssrc0, pc, inst.src0, error) ||
|
||||
!DecodeScalarSource(ssrc1, pc, inst.src1, error)) {
|
||||
return false;
|
||||
}
|
||||
void DecodeBinarySources(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst, uint32_t ssrc0, uint32_t ssrc1) {
|
||||
DecodeScalarSource(ssrc0, pc, inst.src0);
|
||||
DecodeScalarSource(ssrc1, pc, inst.src1);
|
||||
inst.src_count = 2;
|
||||
ReadLiteralOperands(code, word_index, inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error) {
|
||||
void DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst) {
|
||||
const uint32_t word = code[word_index];
|
||||
const uint32_t opcode = (word >> 8u) & 0xffu;
|
||||
const uint32_t ssrc0 = word & 0xffu;
|
||||
@@ -134,35 +131,31 @@ bool DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
SetUnsupported(inst, Family::SOP1, opcode, "SOP1 opcode is not implemented");
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (inst.opcode) {
|
||||
case Opcode::S_GETPC_B64:
|
||||
inst.src_count = 0;
|
||||
return DecodeScalarDestination(sdst, pc, inst.dst, error);
|
||||
DecodeScalarDestination(sdst, pc, inst.dst);
|
||||
return;
|
||||
case Opcode::S_SETPC_B64:
|
||||
inst.src_count = 1;
|
||||
inst.dst.kind = OperandKind::Null;
|
||||
if (!DecodeScalarSource(ssrc0, pc, inst.src0, error)) {
|
||||
return false;
|
||||
}
|
||||
DecodeScalarSource(ssrc0, pc, inst.src0);
|
||||
ReadLiteralOperands(code, word_index, inst);
|
||||
return true;
|
||||
return;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (!DecodeScalarSource(ssrc0, pc, inst.src0, error) ||
|
||||
!DecodeScalarDestination(sdst, pc, inst.dst, error)) {
|
||||
return false;
|
||||
}
|
||||
DecodeScalarSource(ssrc0, pc, inst.src0);
|
||||
DecodeScalarDestination(sdst, pc, inst.dst);
|
||||
inst.src_count = 1;
|
||||
ReadLiteralOperands(code, word_index, inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error) {
|
||||
void DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst) {
|
||||
const uint32_t word = code[word_index];
|
||||
const uint32_t opcode = (word >> 23u) & 0x7fu;
|
||||
const uint32_t ssrc1 = (word >> 8u) & 0xffu;
|
||||
@@ -178,17 +171,15 @@ bool DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
SetUnsupported(inst, Family::SOP2, opcode, "SOP2 opcode is not implemented");
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DecodeScalarDestination(sdst, pc, inst.dst, error)) {
|
||||
return false;
|
||||
}
|
||||
return DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1, error);
|
||||
DecodeScalarDestination(sdst, pc, inst.dst);
|
||||
DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1);
|
||||
}
|
||||
|
||||
bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error) {
|
||||
void DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst) {
|
||||
const uint32_t word = code[word_index];
|
||||
const uint32_t opcode = (word >> 23u) & 0x1fu;
|
||||
const uint32_t sdst = (word >> 16u) & 0x7fu;
|
||||
@@ -207,18 +198,18 @@ bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
SetUnsupported(inst, Family::SOPK, opcode, "SOPK opcode is not implemented");
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (inst.opcode) {
|
||||
case Opcode::S_MOVK_I32: return DecodeScalarDestination(sdst, pc, inst.dst, error);
|
||||
case Opcode::S_MOVK_I32: DecodeScalarDestination(sdst, pc, inst.dst); return;
|
||||
case Opcode::S_WAITCNT: {
|
||||
const uint32_t waitcnt = word & 0xffffu;
|
||||
inst.dst.kind = OperandKind::Null;
|
||||
inst.src0.signed_val = static_cast<int32_t>(waitcnt);
|
||||
inst.src0.value = waitcnt;
|
||||
inst.src_count = 1;
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
case Opcode::S_SETREG_B32:
|
||||
inst.dst.kind = OperandKind::Null;
|
||||
@@ -226,26 +217,25 @@ bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.src1.value = word & 0xffffu;
|
||||
inst.src1.signed_val = static_cast<int32_t>(imm);
|
||||
inst.src_count = 2;
|
||||
return DecodeScalarSource(sdst, pc, inst.src0, error);
|
||||
DecodeScalarSource(sdst, pc, inst.src0);
|
||||
return;
|
||||
default: break;
|
||||
}
|
||||
|
||||
inst.src1 = inst.src0;
|
||||
if (!DecodeScalarSource(sdst, pc, inst.src0, error)) {
|
||||
return false;
|
||||
}
|
||||
DecodeScalarSource(sdst, pc, inst.src0);
|
||||
if (inst.opcode == Opcode::S_ADD_I32 || inst.opcode == Opcode::S_MULK_I32) {
|
||||
inst.src_count = 2;
|
||||
return DecodeScalarDestination(sdst, pc, inst.dst, error);
|
||||
DecodeScalarDestination(sdst, pc, inst.dst);
|
||||
return;
|
||||
}
|
||||
|
||||
inst.dst.kind = OperandKind::Scc;
|
||||
inst.src_count = 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error) {
|
||||
void DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst) {
|
||||
const uint32_t word = code[word_index];
|
||||
const uint32_t ssrc1 = (word >> 8u) & 0xffu;
|
||||
const uint32_t ssrc0 = word & 0xffu;
|
||||
@@ -261,10 +251,10 @@ bool DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
|
||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||
SetUnsupported(inst, Family::SOPC, opcode, "SOPC opcode is not implemented");
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
return DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1, error);
|
||||
DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1);
|
||||
}
|
||||
|
||||
void DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
|
||||
bool DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error);
|
||||
bool DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error);
|
||||
bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error);
|
||||
bool DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error);
|
||||
void DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
void DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
void DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
void DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
void DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/frontend/decode/ExportOps.h"
|
||||
#include "graphics/shader/recompiler/frontend/decode/ImageOps.h"
|
||||
#include "graphics/shader/recompiler/frontend/decode/MemoryOps.h"
|
||||
@@ -17,12 +18,6 @@ uint32_t FloatBits(float value) {
|
||||
return std::bit_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
void SetError(std::string* error, const char* message) {
|
||||
if (error != nullptr) {
|
||||
*error = message;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasLiteral(const Instruction& inst) {
|
||||
return inst.src0.kind == OperandKind::LiteralConstant ||
|
||||
inst.src1.kind == OperandKind::LiteralConstant ||
|
||||
@@ -216,86 +211,76 @@ const char* ImageDimensionToString(ImageDimension dimension) {
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::string* error) {
|
||||
void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand) {
|
||||
operand = {};
|
||||
|
||||
if (code <= 105u) {
|
||||
operand.kind = OperandKind::Sgpr;
|
||||
operand.reg = code;
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if (code >= 128u && code <= 192u) {
|
||||
operand.kind = OperandKind::IntegerInlineConstant;
|
||||
operand.signed_val = static_cast<int32_t>(code - 128u);
|
||||
operand.value = static_cast<uint32_t>(operand.signed_val);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if (code >= 193u && code <= 208u) {
|
||||
operand.kind = OperandKind::IntegerInlineConstant;
|
||||
operand.signed_val = 192 - static_cast<int32_t>(code);
|
||||
operand.value = static_cast<uint32_t>(operand.signed_val);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if (code >= 240u && code <= 247u) {
|
||||
constexpr float values[] = {0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f, 4.0f, -4.0f};
|
||||
operand.kind = OperandKind::FloatInlineConstant;
|
||||
operand.float_val = values[code - 240u];
|
||||
operand.value = FloatBits(operand.float_val);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if (code >= 256u && code <= 511u) {
|
||||
DecodeVectorGpr(code - 256u, operand);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case 106u: operand.kind = OperandKind::VccLo; return true;
|
||||
case 107u: operand.kind = OperandKind::VccHi; return true;
|
||||
case 124u: operand.kind = OperandKind::M0; return true;
|
||||
case 125u: operand.kind = OperandKind::Null; return true;
|
||||
case 126u: operand.kind = OperandKind::ExecLo; return true;
|
||||
case 127u: operand.kind = OperandKind::ExecHi; return true;
|
||||
case 239u: operand.kind = OperandKind::PopsExitingWaveId; return true;
|
||||
case 106u: operand.kind = OperandKind::VccLo; return;
|
||||
case 107u: operand.kind = OperandKind::VccHi; return;
|
||||
case 124u: operand.kind = OperandKind::M0; return;
|
||||
case 125u: operand.kind = OperandKind::Null; return;
|
||||
case 126u: operand.kind = OperandKind::ExecLo; return;
|
||||
case 127u: operand.kind = OperandKind::ExecHi; return;
|
||||
case 239u: operand.kind = OperandKind::PopsExitingWaveId; return;
|
||||
case 248u:
|
||||
operand.kind = OperandKind::FloatInlineConstant;
|
||||
operand.float_val = 0.15915494309189535f;
|
||||
operand.value = FloatBits(operand.float_val);
|
||||
return true;
|
||||
case 251u: operand.kind = OperandKind::VccZ; return true;
|
||||
case 252u: operand.kind = OperandKind::ExecZ; return true;
|
||||
case 253u: operand.kind = OperandKind::Scc; return true;
|
||||
case 255u: operand.kind = OperandKind::LiteralConstant; return true;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("unsupported scalar source operand 0x{:08x} at pc 0x{:08x}",
|
||||
code, pc);
|
||||
}
|
||||
return false;
|
||||
return;
|
||||
case 251u: operand.kind = OperandKind::VccZ; return;
|
||||
case 252u: operand.kind = OperandKind::ExecZ; return;
|
||||
case 253u: operand.kind = OperandKind::Scc; return;
|
||||
case 255u: operand.kind = OperandKind::LiteralConstant; return;
|
||||
default: EXIT("unsupported scalar source operand 0x%08x at pc 0x%08x", code, pc);
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand, std::string* error) {
|
||||
void DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand) {
|
||||
operand = {};
|
||||
|
||||
if (code <= 105u) {
|
||||
operand.kind = OperandKind::Sgpr;
|
||||
operand.reg = code;
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (code) {
|
||||
case 106u: operand.kind = OperandKind::VccLo; return true;
|
||||
case 107u: operand.kind = OperandKind::VccHi; return true;
|
||||
case 124u: operand.kind = OperandKind::M0; return true;
|
||||
case 125u: operand.kind = OperandKind::Null; return true;
|
||||
case 126u: operand.kind = OperandKind::ExecLo; return true;
|
||||
case 127u: operand.kind = OperandKind::ExecHi; return true;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"unsupported scalar destination operand 0x{:08x} at pc 0x{:08x}", code, pc);
|
||||
}
|
||||
return false;
|
||||
case 106u: operand.kind = OperandKind::VccLo; return;
|
||||
case 107u: operand.kind = OperandKind::VccHi; return;
|
||||
case 124u: operand.kind = OperandKind::M0; return;
|
||||
case 125u: operand.kind = OperandKind::Null; return;
|
||||
case 126u: operand.kind = OperandKind::ExecLo; return;
|
||||
case 127u: operand.kind = OperandKind::ExecHi; return;
|
||||
default: EXIT("unsupported scalar destination operand 0x%08x at pc 0x%08x", code, pc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,39 +353,33 @@ Family GetInstructionFamily(uint32_t word) {
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeInstruction(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error) {
|
||||
void DecodeInstruction(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst) {
|
||||
const uint32_t pc = word_index * sizeof(uint32_t);
|
||||
switch (GetInstructionFamily(code[word_index])) {
|
||||
case Family::SOP1: return DecodeSop1(pc, code, word_index, inst, error);
|
||||
case Family::SOP2: return DecodeSop2(pc, code, word_index, inst, error);
|
||||
case Family::SOPK: return DecodeSopk(pc, code, word_index, inst, error);
|
||||
case Family::SOPC: return DecodeSopc(pc, code, word_index, inst, error);
|
||||
case Family::SOPP: DecodeSopp(pc, code, word_index, inst); return true;
|
||||
case Family::VOP1: return DecodeVop1(pc, code, word_index, inst, error);
|
||||
case Family::VOP2: return DecodeVop2(pc, code, word_index, inst, error);
|
||||
case Family::VOP3: return DecodeVop3(pc, code, word_index, inst, error);
|
||||
case Family::VOP3P: return DecodeVop3p(pc, code, word_index, inst, error);
|
||||
case Family::VOPC: return DecodeVopc(pc, code, word_index, inst, error);
|
||||
case Family::VINTRP: DecodeVintrp(pc, code, word_index, inst); return true;
|
||||
case Family::SMEM: DecodeSmem(pc, code, word_index, inst); return true;
|
||||
case Family::MUBUF: DecodeMubuf(pc, code, word_index, inst); return true;
|
||||
case Family::MTBUF: DecodeMtbuf(pc, code, word_index, inst); return true;
|
||||
case Family::FLAT: DecodeFlat(pc, code, word_index, inst); return true;
|
||||
case Family::DS: DecodeDs(pc, code, word_index, inst); return true;
|
||||
case Family::MIMG: DecodeMimg(pc, code, word_index, inst); return true;
|
||||
case Family::EXP: DecodeExp(pc, code, word_index, inst); return true;
|
||||
case Family::SOP1: DecodeSop1(pc, code, word_index, inst); return;
|
||||
case Family::SOP2: DecodeSop2(pc, code, word_index, inst); return;
|
||||
case Family::SOPK: DecodeSopk(pc, code, word_index, inst); return;
|
||||
case Family::SOPC: DecodeSopc(pc, code, word_index, inst); return;
|
||||
case Family::SOPP: DecodeSopp(pc, code, word_index, inst); return;
|
||||
case Family::VOP1: DecodeVop1(pc, code, word_index, inst); return;
|
||||
case Family::VOP2: DecodeVop2(pc, code, word_index, inst); return;
|
||||
case Family::VOP3: DecodeVop3(pc, code, word_index, inst); return;
|
||||
case Family::VOP3P: DecodeVop3p(pc, code, word_index, inst); return;
|
||||
case Family::VOPC: DecodeVopc(pc, code, word_index, inst); return;
|
||||
case Family::VINTRP: DecodeVintrp(pc, code, word_index, inst); return;
|
||||
case Family::SMEM: DecodeSmem(pc, code, word_index, inst); return;
|
||||
case Family::MUBUF: DecodeMubuf(pc, code, word_index, inst); return;
|
||||
case Family::MTBUF: DecodeMtbuf(pc, code, word_index, inst); return;
|
||||
case Family::FLAT: DecodeFlat(pc, code, word_index, inst); return;
|
||||
case Family::DS: DecodeDs(pc, code, word_index, inst); return;
|
||||
case Family::MIMG: DecodeMimg(pc, code, word_index, inst); return;
|
||||
case Family::EXP: DecodeExp(pc, code, word_index, inst); return;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error =
|
||||
fmt::format("unknown RDNA2 instruction family at pc 0x{:08x}, raw=0x{:08x}", pc,
|
||||
code[word_index]);
|
||||
}
|
||||
return false;
|
||||
EXIT("unknown RDNA2 instruction family at pc 0x%08x, raw=0x%08x", pc, code[word_index]);
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string* error) {
|
||||
void DecodeProgram(std::span<const uint32_t> code, Program& program) {
|
||||
program.instructions.clear();
|
||||
program.instructions.reserve(code.size());
|
||||
program.code = code;
|
||||
@@ -408,10 +387,7 @@ bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string
|
||||
std::vector<bool> branch_targets;
|
||||
for (uint32_t word_index = 0; word_index < code.size();) {
|
||||
program.instructions.emplace_back();
|
||||
if (!DecodeInstruction(code, word_index, program.instructions.back(), error)) {
|
||||
program.instructions.pop_back();
|
||||
return false;
|
||||
}
|
||||
DecodeInstruction(code, word_index, program.instructions.back());
|
||||
|
||||
const auto& inst = program.instructions.back();
|
||||
word_index += inst.word_count;
|
||||
@@ -425,12 +401,11 @@ bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string
|
||||
}
|
||||
if (inst.opcode == Opcode::S_ENDPGM &&
|
||||
(word_index >= code.size() || branch_targets.empty() || !branch_targets[word_index])) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SetError(error, "shader decode reached the code boundary before S_ENDPGM");
|
||||
return false;
|
||||
EXIT("shader decode reached the code boundary before S_ENDPGM");
|
||||
}
|
||||
|
||||
std::string OperandToString(const Operand& operand) {
|
||||
|
||||
@@ -351,6 +351,7 @@ enum class Opcode {
|
||||
V_CMP_GE_I32,
|
||||
V_CMP_T_I32,
|
||||
V_CMP_CLASS_F32,
|
||||
V_CMPX_CLASS_F32,
|
||||
V_CMP_LT_I16,
|
||||
V_CMP_EQ_I16,
|
||||
V_CMP_LE_I16,
|
||||
@@ -631,6 +632,7 @@ struct Operand {
|
||||
uint32_t dpp_ctrl = 0;
|
||||
uint32_t dpp_row_mask = 0xf;
|
||||
uint32_t dpp_bank_mask = 0xf;
|
||||
bool explicit_sdwa_dst = false;
|
||||
bool sdwa_sext = false;
|
||||
bool dpp_fetch_inactive = false;
|
||||
bool dpp_bound_ctrl = false;
|
||||
@@ -702,12 +704,11 @@ struct Program {
|
||||
// Code spans are trusted to contain complete instructions, valid branch targets, and 32-bit PCs.
|
||||
Family GetInstructionFamily(uint32_t word);
|
||||
// The output object must be freshly initialized.
|
||||
bool DecodeInstruction(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error);
|
||||
bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string* error);
|
||||
void DecodeInstruction(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst);
|
||||
void DecodeProgram(std::span<const uint32_t> code, Program& program);
|
||||
|
||||
bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::string* error);
|
||||
bool DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand, std::string* error);
|
||||
void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand);
|
||||
void DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand);
|
||||
void DecodeVectorGpr(uint32_t reg, Operand& operand);
|
||||
void ReadLiteralOperands(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst);
|
||||
void SetRawWords(Instruction& inst, std::span<const uint32_t> code, uint32_t word_index,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -5,16 +5,16 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
|
||||
bool DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error);
|
||||
bool DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error);
|
||||
bool DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error);
|
||||
bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
|
||||
std::string* error);
|
||||
bool DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst, std::string* error);
|
||||
void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
void DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
void DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
void DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
void DecodeVintrp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
Instruction& inst);
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
@@ -47,14 +46,13 @@ IR::ExportFlags Translator::AddExportInfo(const Decoder::Instruction& inst) {
|
||||
return {.index = index, .pc = inst.pc};
|
||||
}
|
||||
|
||||
bool Translator::TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
void Translator::TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
uint32_t component_count) {
|
||||
for (uint32_t component = 0; component < component_count; component++) {
|
||||
const auto value =
|
||||
ir.Emit(IR::ValueOpcode::GetAttribute, {IR::Value(attribute), IR::Value(component)});
|
||||
WriteOperand(OffsetOperand(inst.dst, component), value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Translator::V_INTERP_P1_F32() {}
|
||||
@@ -65,25 +63,20 @@ void Translator::V_INTERP_P2_F32(const Decoder::Instruction& inst) {
|
||||
WriteOperand(inst.dst, value);
|
||||
}
|
||||
|
||||
bool Translator::V_INTERP_MOV_F32(const Decoder::Instruction& inst, std::string* error) {
|
||||
void Translator::V_INTERP_MOV_F32(const Decoder::Instruction& inst) {
|
||||
if (inst.src0.value >= 3u) {
|
||||
return ShaderError::Fail(error,
|
||||
fmt::format("v_interp_mov_f32 mode {} is reserved at pc 0x{:08x}",
|
||||
inst.src0.value, inst.pc));
|
||||
EXIT("v_interp_mov_f32 mode %u is reserved at pc 0x%08x", inst.src0.value, inst.pc);
|
||||
}
|
||||
const auto value = ir.Emit(
|
||||
IR::ValueOpcode::GetInterpolationParameter,
|
||||
{IR::Value(inst.src1.value), IR::Value(inst.src2.value), IR::Value(inst.src0.value)});
|
||||
WriteOperand(inst.dst, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::EXP(const Decoder::Instruction& inst, std::string* error) {
|
||||
void Translator::EXP(const Decoder::Instruction& inst) {
|
||||
uint32_t index = 0;
|
||||
if (ExportTargetKindFromTarget(inst.exp.target, index) == IR::ExportTargetKind::Unknown) {
|
||||
return ShaderError::Fail(error,
|
||||
fmt::format("unsupported EXP target 0x{:02x} at pc 0x{:08x}",
|
||||
inst.exp.target, inst.pc));
|
||||
EXIT("unsupported EXP target 0x%02x at pc 0x%08x", inst.exp.target, inst.pc);
|
||||
}
|
||||
std::array<IR::Value, 4> components {IR::Value(0u), IR::Value(0u), IR::Value(0u),
|
||||
IR::Value(0u)};
|
||||
@@ -93,14 +86,13 @@ bool Translator::EXP(const Decoder::Instruction& inst, std::string* error) {
|
||||
const auto data = ir.Emit(IR::ValueOpcode::CompositeConstructU32x4,
|
||||
{components[0], components[1], components[2], components[3]});
|
||||
ir.Emit(IR::ValueOpcode::SetAttribute, {data, ir.GetExec()}, AddExportInfo(inst));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::EmitInterpolation(const Decoder::Instruction& inst, std::string* error) {
|
||||
bool Translator::EmitInterpolation(const Decoder::Instruction& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_INTERP_P1_F32: V_INTERP_P1_F32(); return true;
|
||||
case Decoder::Opcode::V_INTERP_P2_F32: V_INTERP_P2_F32(inst); return true;
|
||||
case Decoder::Opcode::V_INTERP_MOV_F32: return V_INTERP_MOV_F32(inst, error);
|
||||
case Decoder::Opcode::V_INTERP_MOV_F32: V_INTERP_MOV_F32(inst); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ void Translator::EmitCompareResult(const Decoder::Instruction& inst, IR::U1 valu
|
||||
if (cmpx) {
|
||||
const auto mask = BallotMask(masked);
|
||||
ir.SetExec(masked);
|
||||
ir.SetExecMaskTag(IR::U1(IR::Value(true)));
|
||||
ir.SetExecLo(mask[0]);
|
||||
ir.SetExecHi(mask[1]);
|
||||
return;
|
||||
@@ -55,11 +56,11 @@ void Translator::EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool
|
||||
EmitCompareResult(inst, ordered ? ir.LogicalNot(unordered) : unordered, false, false);
|
||||
}
|
||||
|
||||
void Translator::V_CMP_CLASS_F32(const Decoder::Instruction& inst) {
|
||||
void Translator::EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx) {
|
||||
const auto value = ReadOperand(inst.src0, IR::Type::F32);
|
||||
const auto mask = ReadOperand(inst.src1, IR::Type::U32);
|
||||
EmitCompareResult(inst, IR::U1(ir.Emit(IR::ValueOpcode::FPCmpClass32, {value, mask})), false,
|
||||
false);
|
||||
cmpx);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
@@ -23,18 +24,28 @@ Decoder::Operand ConditionOperand(Decoder::OperandKind kind) {
|
||||
|
||||
void Translator::S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode operation,
|
||||
bool negate_exec, bool negate_source, bool write_64) {
|
||||
if (write_64) {
|
||||
const auto exec_operand = ConditionOperand(Decoder::OperandKind::ExecLo);
|
||||
const auto old = ReadScalarU64(exec_operand);
|
||||
const auto bit_operation = operation == IR::ValueOpcode::LogicalAnd
|
||||
? IR::ValueOpcode::BitwiseAnd32
|
||||
: IR::ValueOpcode::BitwiseOr32;
|
||||
const auto result = BinaryScalarU64(old, ReadScalarU64(inst.src0), operation, bit_operation,
|
||||
negate_exec, negate_source, false);
|
||||
WriteScalarU64(inst.dst, old);
|
||||
WriteScalarU64(exec_operand, result);
|
||||
ir.SetScc(ScalarU64NonZero(result));
|
||||
return;
|
||||
}
|
||||
const auto old = ir.GetExec();
|
||||
const auto src = ReadMask(inst.src0);
|
||||
const auto lhs = negate_exec ? ir.LogicalNot(old) : old;
|
||||
const auto rhs = negate_source ? ir.LogicalNot(src) : src;
|
||||
const auto result = IR::U1(ir.Emit(operation, {lhs, rhs}));
|
||||
if (write_64) {
|
||||
WriteMask64(inst.dst, old);
|
||||
} else {
|
||||
WriteMask(inst.dst, old);
|
||||
}
|
||||
WriteMask(inst.dst, old);
|
||||
const auto mask = BallotMask(result);
|
||||
ir.SetExec(result);
|
||||
ir.SetExecMaskTag(IR::U1(IR::Value(true)));
|
||||
ir.SetExecLo(mask[0]);
|
||||
ir.SetExecHi(mask[1]);
|
||||
ir.SetScc(result);
|
||||
@@ -153,12 +164,12 @@ void Translator::S_INST_PREFETCH() {
|
||||
ir.Emit(IR::ValueOpcode::InstPrefetch);
|
||||
}
|
||||
|
||||
bool Translator::S_GETPC_B64(const Decoder::Instruction& inst, std::string* error) {
|
||||
void Translator::S_GETPC_B64(const Decoder::Instruction& inst) {
|
||||
const auto base = IR::U64(ir.Emit(IR::ValueOpcode::GetShaderBase));
|
||||
const auto address = IR::U64(
|
||||
ir.Emit(IR::ValueOpcode::IAdd64, {base, IR::Value(static_cast<uint64_t>(inst.pc) + 4u)}));
|
||||
if (inst.dst.kind == Decoder::OperandKind::Null) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
auto high = PlainOperand(inst.dst);
|
||||
switch (inst.dst.kind) {
|
||||
@@ -169,25 +180,19 @@ bool Translator::S_GETPC_B64(const Decoder::Instruction& inst, std::string* erro
|
||||
high.kind = Decoder::OperandKind::VccLo;
|
||||
high.reg = 0u;
|
||||
} else {
|
||||
if (error != nullptr) {
|
||||
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
|
||||
}
|
||||
return false;
|
||||
EXIT("S_GETPC_B64 destination does not name a valid scalar pair at pc 0x%08x",
|
||||
inst.pc);
|
||||
}
|
||||
break;
|
||||
case Decoder::OperandKind::VccLo: high.kind = Decoder::OperandKind::VccHi; break;
|
||||
case Decoder::OperandKind::M0: high.kind = Decoder::OperandKind::Null; break;
|
||||
case Decoder::OperandKind::ExecLo: high.kind = Decoder::OperandKind::ExecHi; break;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = "S_GETPC_B64 destination does not name a valid scalar pair";
|
||||
}
|
||||
return false;
|
||||
EXIT("S_GETPC_B64 destination does not name a valid scalar pair at pc 0x%08x", inst.pc);
|
||||
}
|
||||
const auto words = ExtractU64(address);
|
||||
WriteOperand(inst.dst, words[0]);
|
||||
WriteOperand(high, words[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Translator::S_CSELECT_B32(const Decoder::Instruction& inst) {
|
||||
@@ -196,30 +201,21 @@ void Translator::S_CSELECT_B32(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
void Translator::S_CSELECT_B64(const Decoder::Instruction& inst) {
|
||||
const auto condition = ir.GetScc();
|
||||
const auto lhs = ReadU32Pair(inst.src0);
|
||||
const auto rhs = ReadU32Pair(inst.src1);
|
||||
const auto selected_mask = IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::SelectU1, {condition, ReadMask(inst.src0), ReadMask(inst.src1)}));
|
||||
const auto selected_mask_valid =
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
|
||||
{condition, ReadMaskValid(inst.src0), ReadMaskValid(inst.src1)}));
|
||||
if (IsExecOrVcc(inst.dst)) {
|
||||
WriteMask64(inst.dst, selected_mask);
|
||||
return;
|
||||
}
|
||||
WriteU32Pair(inst.dst,
|
||||
{ir.Select(condition, lhs[0], rhs[0]), ir.Select(condition, lhs[1], rhs[1])});
|
||||
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
||||
ir.SetThreadBitScalarReg(dst, selected_mask);
|
||||
ir.SetScalarMaskTag(dst, selected_mask_valid);
|
||||
}
|
||||
const auto condition = ir.GetScc();
|
||||
const auto lhs = ReadScalarU64(inst.src0);
|
||||
const auto rhs = ReadScalarU64(inst.src1);
|
||||
WriteScalarU64(inst.dst, SelectScalarU64(condition, lhs, rhs));
|
||||
}
|
||||
|
||||
void Translator::MOV_B32(const Decoder::Instruction& inst, bool apply_float_modifiers) {
|
||||
if (inst.src0.kind == Decoder::OperandKind::Sgpr &&
|
||||
inst.dst.kind == Decoder::OperandKind::Sgpr && inst.src0.reg == inst.dst.reg) {
|
||||
return;
|
||||
}
|
||||
if (IsExecOrVcc(inst.src0) && IsExecOrVcc(inst.dst)) {
|
||||
WriteMask(inst.dst, ReadMask(inst.src0));
|
||||
if (inst.src0.kind != inst.dst.kind) {
|
||||
WriteRawU32(inst.dst, ReadRawU32(inst.src0));
|
||||
}
|
||||
} else if (apply_float_modifiers && (inst.src0.negate || inst.src0.absolute)) {
|
||||
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::F32));
|
||||
} else {
|
||||
@@ -228,67 +224,28 @@ void Translator::MOV_B32(const Decoder::Instruction& inst, bool apply_float_modi
|
||||
}
|
||||
|
||||
void Translator::S_MOV_B64(const Decoder::Instruction& inst) {
|
||||
if (IsExecOrVcc(inst.dst) || IsExecOrVcc(inst.src0)) {
|
||||
WriteMask64(inst.dst, ReadMask(inst.src0));
|
||||
return;
|
||||
}
|
||||
const bool scalar_copy =
|
||||
inst.dst.kind == Decoder::OperandKind::Sgpr && inst.src0.kind == Decoder::OperandKind::Sgpr;
|
||||
IR::U1 source_mask;
|
||||
IR::U1 source_mask_valid;
|
||||
if (scalar_copy) {
|
||||
source_mask = ir.GetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.src0.reg));
|
||||
source_mask_valid = ir.GetScalarMaskTag(static_cast<IR::ScalarReg>(inst.src0.reg));
|
||||
}
|
||||
WriteU32Pair(inst.dst, ReadU32Pair(inst.src0));
|
||||
if (scalar_copy) {
|
||||
ir.SetThreadBitScalarReg(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask);
|
||||
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(inst.dst.reg), source_mask_valid);
|
||||
}
|
||||
WriteScalarU64(inst.dst, ReadScalarU64(inst.src0));
|
||||
}
|
||||
|
||||
void Translator::S_WQM_B64(const Decoder::Instruction& inst) {
|
||||
if (!IsExecOrVcc(inst.dst) && !IsExecOrVcc(inst.src0)) {
|
||||
const auto mask_valid = ReadMaskValid(inst.src0);
|
||||
const auto invocation_result =
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
|
||||
const auto result =
|
||||
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
|
||||
WriteOperand(DestinationOperand(inst), result);
|
||||
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
||||
ir.SetThreadBitScalarReg(dst, invocation_result);
|
||||
ir.SetScalarMaskTag(dst, mask_valid);
|
||||
const auto raw_nonzero =
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})}));
|
||||
ir.SetScc(IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero})));
|
||||
} else {
|
||||
ir.SetScc(
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto result = IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
|
||||
WriteMask64(inst.dst, result);
|
||||
ir.SetScc(result);
|
||||
const auto source = ReadScalarU64(inst.src0);
|
||||
const auto raw = ExtractU64(
|
||||
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ir.ConstructU64(source.raw[0], source.raw[1])})));
|
||||
const auto invocation = IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {source.invocation}));
|
||||
const auto result = MakeScalarU64Result(raw, invocation, source.mask_valid);
|
||||
WriteScalarU64(inst.dst, result);
|
||||
ir.SetScc(ScalarU64NonZero(result));
|
||||
}
|
||||
|
||||
bool Translator::V_MOVRELS_B32(const Decoder::Instruction& inst, std::string* error) {
|
||||
void Translator::V_MOVRELS_B32(const Decoder::Instruction& inst) {
|
||||
if (inst.dst.kind != Decoder::OperandKind::Vgpr ||
|
||||
inst.src0.kind != Decoder::OperandKind::Vgpr) {
|
||||
if (error != nullptr) {
|
||||
*error = "V_MOVRELS_B32 requires VGPR source and destination";
|
||||
}
|
||||
return false;
|
||||
EXIT("V_MOVRELS_B32 requires VGPR source and destination at pc 0x%08x", inst.pc);
|
||||
}
|
||||
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
|
||||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate || inst.src0.absolute ||
|
||||
inst.src0.dpp) {
|
||||
if (error != nullptr) {
|
||||
*error = "V_MOVRELS_B32 modifiers are not implemented";
|
||||
}
|
||||
return false;
|
||||
EXIT("V_MOVRELS_B32 modifiers are not implemented at pc 0x%08x", inst.pc);
|
||||
}
|
||||
const auto base = inst.src0.reg;
|
||||
const auto m0 = ir.BitwiseAnd(ReadU32(ConditionOperand(Decoder::OperandKind::M0)),
|
||||
@@ -299,23 +256,16 @@ bool Translator::V_MOVRELS_B32(const Decoder::Instruction& inst, std::string* er
|
||||
selected = ir.Select(match, ir.GetVectorReg(static_cast<IR::VectorReg>(index)), selected);
|
||||
}
|
||||
WriteOperand(DestinationOperand(inst), selected);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::V_MOVRELD_B32(const Decoder::Instruction& inst, std::string* error) {
|
||||
void Translator::V_MOVRELD_B32(const Decoder::Instruction& inst) {
|
||||
if (inst.dst.kind != Decoder::OperandKind::Vgpr) {
|
||||
if (error != nullptr) {
|
||||
*error = "V_MOVRELD_B32 requires VGPR destination";
|
||||
}
|
||||
return false;
|
||||
EXIT("V_MOVRELD_B32 requires VGPR destination at pc 0x%08x", inst.pc);
|
||||
}
|
||||
if (inst.dst.sdwa_sel != 6u || inst.dst.omod != 0u || inst.dst.clamp ||
|
||||
inst.src0.sdwa_sel != 6u || inst.src0.sdwa_sext || inst.src0.negate || inst.src0.absolute ||
|
||||
inst.src0.dpp) {
|
||||
if (error != nullptr) {
|
||||
*error = "V_MOVRELD_B32 modifiers are not implemented";
|
||||
}
|
||||
return false;
|
||||
EXIT("V_MOVRELD_B32 modifiers are not implemented at pc 0x%08x", inst.pc);
|
||||
}
|
||||
const auto base = inst.dst.reg;
|
||||
const auto value = ReadU32(inst.src0);
|
||||
@@ -327,7 +277,6 @@ bool Translator::V_MOVRELD_B32(const Decoder::Instruction& inst, std::string* er
|
||||
const auto write = ir.LogicalAnd(ir.GetExec(), match);
|
||||
ir.SetVectorReg(reg, ir.Select(write, value, ir.GetVectorReg(reg)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Translator::V_READFIRSTLANE_B32(const Decoder::Instruction& inst) {
|
||||
|
||||
@@ -141,8 +141,10 @@ void Translator::V_CVT_OFF_F32_I4(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
void Translator::V_CVT_PKRTZ_F16_F32(const Decoder::Instruction& inst) {
|
||||
const auto lhs = ReadOperand(SourceAt(inst, 0), IR::Type::F32);
|
||||
const auto rhs = ReadOperand(SourceAt(inst, 1), IR::Type::F32);
|
||||
const auto lhs = ApplyF32ResultModifiers(
|
||||
inst.dst, IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32)));
|
||||
const auto rhs = ApplyF32ResultModifiers(
|
||||
inst.dst, IR::F32(ReadOperand(SourceAt(inst, 1), IR::Type::F32)));
|
||||
WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::PackFloat2x16Rtz, {lhs, rhs}));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
bool Translator::TranslateInstruction(const Decoder::Instruction& inst, std::string* error) {
|
||||
void Translator::TranslateInstruction(const Decoder::Instruction& inst) {
|
||||
current_opcode = inst.opcode;
|
||||
current_pc = inst.pc;
|
||||
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::UNKNOWN:
|
||||
case Decoder::Opcode::COUNT:
|
||||
if (error != nullptr) {
|
||||
*error = "decoded opcode has no IR translation";
|
||||
}
|
||||
return false;
|
||||
EXIT("decoded opcode has no IR translation at pc 0x%08x", inst.pc);
|
||||
case Decoder::Opcode::UNSUPPORTED:
|
||||
if (error != nullptr) {
|
||||
*error = "unsupported decoded instruction: " + Decoder::InstructionToString(inst);
|
||||
}
|
||||
return false;
|
||||
EXIT("unsupported decoded instruction: %s", Decoder::InstructionToString(inst).c_str());
|
||||
default: break;
|
||||
}
|
||||
|
||||
@@ -27,27 +22,30 @@ bool Translator::TranslateInstruction(const Decoder::Instruction& inst, std::str
|
||||
case Decoder::Family::SOP2:
|
||||
case Decoder::Family::SOPK:
|
||||
case Decoder::Family::SOPC:
|
||||
case Decoder::Family::SOPP: translated = EmitScalar(inst, error); break;
|
||||
case Decoder::Family::SOPP: translated = EmitScalar(inst); break;
|
||||
case Decoder::Family::VOP1:
|
||||
case Decoder::Family::VOP2:
|
||||
case Decoder::Family::VOP3:
|
||||
case Decoder::Family::VOP3P:
|
||||
case Decoder::Family::VOPC: translated = EmitVector(inst, error); break;
|
||||
case Decoder::Family::VOPC: translated = EmitVector(inst); break;
|
||||
case Decoder::Family::SMEM:
|
||||
case Decoder::Family::MUBUF:
|
||||
case Decoder::Family::MTBUF:
|
||||
case Decoder::Family::FLAT:
|
||||
case Decoder::Family::DS:
|
||||
case Decoder::Family::MIMG: translated = EmitMemory(inst, error); break;
|
||||
case Decoder::Family::VINTRP: translated = EmitInterpolation(inst, error); break;
|
||||
case Decoder::Family::EXP: translated = EXP(inst, error); break;
|
||||
case Decoder::Family::MIMG: translated = EmitMemory(inst); break;
|
||||
case Decoder::Family::VINTRP: translated = EmitInterpolation(inst); break;
|
||||
case Decoder::Family::EXP:
|
||||
EXP(inst);
|
||||
translated = true;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (!translated && error != nullptr && error->empty()) {
|
||||
*error = "decoded opcode has no IR translation";
|
||||
if (!translated) {
|
||||
EXIT("opcode %s at pc 0x%08x has no IR translation",
|
||||
Decoder::InstructionToString(inst).c_str(), inst.pc);
|
||||
}
|
||||
return translated;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
@@ -77,68 +77,15 @@ bool Translator::PackedInteger16MinMax(const Decoder::Instruction& inst, IR::Val
|
||||
return true;
|
||||
}
|
||||
|
||||
IR::U1 Translator::U64MaskBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool negate_rhs, bool negate_result) {
|
||||
const auto lhs = ReadMask(inst.src0);
|
||||
auto rhs = ReadMask(inst.src1);
|
||||
if (negate_rhs) {
|
||||
rhs = ir.LogicalNot(rhs);
|
||||
}
|
||||
auto result = IR::U1(ir.Emit(opcode, {lhs, rhs}));
|
||||
return negate_result ? ir.LogicalNot(result) : result;
|
||||
}
|
||||
|
||||
bool Translator::S_U64_MASK(const Decoder::Instruction& inst, IR::ValueOpcode logical_opcode,
|
||||
IR::ValueOpcode bit_opcode, bool negate_rhs, bool negate_result,
|
||||
bool unary) {
|
||||
const auto invocation_result =
|
||||
unary ? ir.LogicalNot(ReadMask(inst.src0))
|
||||
: U64MaskBinary(inst, logical_opcode, negate_rhs, negate_result);
|
||||
const auto is_exec_or_vcc = [](const Decoder::Operand& operand) {
|
||||
switch (operand.kind) {
|
||||
case Decoder::OperandKind::ExecLo:
|
||||
case Decoder::OperandKind::ExecHi:
|
||||
case Decoder::OperandKind::VccLo:
|
||||
case Decoder::OperandKind::VccHi: return true;
|
||||
default: return false;
|
||||
}
|
||||
};
|
||||
if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src0) ||
|
||||
(inst.src_count > 1u && is_exec_or_vcc(inst.src1))) {
|
||||
WriteMask64(inst.dst, invocation_result);
|
||||
ir.SetScc(invocation_result);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto lhs = ReadU32Pair(inst.src0);
|
||||
auto mask_valid = ReadMaskValid(inst.src0);
|
||||
if (inst.src_count > 1u) {
|
||||
mask_valid = ir.LogicalAnd(mask_valid, ReadMaskValid(inst.src1));
|
||||
}
|
||||
std::array<IR::U32, 2> result;
|
||||
if (unary) {
|
||||
result = {ir.BitwiseNot(lhs[0]), ir.BitwiseNot(lhs[1])};
|
||||
} else {
|
||||
const auto rhs = ReadU32Pair(inst.src1);
|
||||
for (uint32_t component = 0; component < 2u; component++) {
|
||||
auto value = ir.Emit(
|
||||
bit_opcode, {lhs[component], negate_rhs ? IR::Value(ir.BitwiseNot(rhs[component]))
|
||||
: IR::Value(rhs[component])});
|
||||
result[component] = negate_result ? ir.BitwiseNot(IR::U32(value)) : IR::U32(value);
|
||||
}
|
||||
}
|
||||
WriteU32Pair(inst.dst, result);
|
||||
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
||||
ir.SetThreadBitScalarReg(dst, invocation_result);
|
||||
ir.SetScalarMaskTag(dst, mask_valid);
|
||||
const auto raw_nonzero =
|
||||
ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u)));
|
||||
ir.SetScc(IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero})));
|
||||
} else {
|
||||
ir.SetScc(ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u))));
|
||||
}
|
||||
const auto lhs = ReadScalarU64(inst.src0);
|
||||
const auto result = unary ? NotScalarU64(lhs)
|
||||
: BinaryScalarU64(lhs, ReadScalarU64(inst.src1), logical_opcode,
|
||||
bit_opcode, false, negate_rhs, negate_result);
|
||||
WriteScalarU64(inst.dst, result);
|
||||
ir.SetScc(ScalarU64NonZero(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#include "common/magicEnum.h"
|
||||
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
@@ -37,6 +35,7 @@ Decoder::Operand OffsetDecodedRegister(const Decoder::Operand& operand, uint32_t
|
||||
result.dpp_ctrl = 0;
|
||||
result.dpp_row_mask = 0xf;
|
||||
result.dpp_bank_mask = 0xf;
|
||||
result.explicit_sdwa_dst = false;
|
||||
result.dpp_fetch_inactive = false;
|
||||
result.dpp_bound_ctrl = false;
|
||||
result.dpp = false;
|
||||
@@ -56,11 +55,14 @@ uint32_t ResourceIndexFromOperand(const Decoder::Operand& operand) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RawScalarLoadBase(const Decoder::Operand& operand) {
|
||||
if (operand.kind == Decoder::OperandKind::Sgpr) {
|
||||
return operand.reg;
|
||||
Decoder::Operand RawScalarLoadBase(const Decoder::Operand& operand) {
|
||||
if (operand.kind != Decoder::OperandKind::M0) {
|
||||
return operand;
|
||||
}
|
||||
return operand.kind == Decoder::OperandKind::VccLo ? 106u : 0u;
|
||||
auto result = operand;
|
||||
// The compressed SBASE value 62 decodes as code 124, but means NULL for S_LOAD.
|
||||
result.kind = Decoder::OperandKind::Null;
|
||||
return result;
|
||||
}
|
||||
|
||||
ResourceKind FlatSegmentResourceKind(uint32_t segment) {
|
||||
@@ -320,8 +322,9 @@ Translator::AddressOperands Translator::ReadAddressOperands(const Decoder::Instr
|
||||
return {GetAddressResource(low, high), low, high};
|
||||
}
|
||||
|
||||
IR::Value Translator::GetScalarAddressResource(uint32_t base) {
|
||||
return GetAddressResource(ReadScalarCode(base), ReadScalarCode(base + 1u));
|
||||
IR::Value Translator::GetScalarAddressResource(const Decoder::Operand& base) {
|
||||
const auto raw = ReadU32Pair(base);
|
||||
return GetAddressResource(raw[0], raw[1]);
|
||||
}
|
||||
|
||||
IR::Value Translator::GetImageResource(const IR::MemoryInfo& memory) {
|
||||
@@ -885,7 +888,7 @@ bool Translator::DS_BPERMUTE_B32(const Decoder::Instruction& inst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error) {
|
||||
bool Translator::EmitMemory(const Decoder::Instruction& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::S_LOAD_DWORD:
|
||||
case Decoder::Opcode::S_LOAD_DWORDX2:
|
||||
@@ -1075,12 +1078,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error
|
||||
case Decoder::Opcode::DS_WRITE_B64:
|
||||
case Decoder::Opcode::DS_WRITE_B96:
|
||||
case Decoder::Opcode::DS_WRITE_B128: return DS_WRITE(inst);
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("memory-family opcode has no specialized IR translation: {}",
|
||||
magic_enum::enum_name(inst.opcode));
|
||||
}
|
||||
return false;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
bool Translator::EmitScalar(const Decoder::Instruction& inst, std::string* error) {
|
||||
bool Translator::EmitScalar(const Decoder::Instruction& inst) {
|
||||
using O = Decoder::Opcode;
|
||||
switch (inst.opcode) {
|
||||
case O::S_MOV_B32:
|
||||
case O::S_MOVK_I32: MOV_B32(inst, false); return true;
|
||||
case O::S_MOV_B64: S_MOV_B64(inst); return true;
|
||||
case O::S_WQM_B64: S_WQM_B64(inst); return true;
|
||||
case O::S_GETPC_B64: return S_GETPC_B64(inst, error);
|
||||
case O::S_GETPC_B64: S_GETPC_B64(inst); return true;
|
||||
case O::S_SETPC_B64: return true;
|
||||
case O::S_CSELECT_B32: S_CSELECT_B32(inst); return true;
|
||||
case O::S_CSELECT_B64: S_CSELECT_B64(inst); return true;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <fmt/format.h>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
using ShaderError::Fail;
|
||||
|
||||
Decoder::Operand Translator::SourceAt(const Decoder::Instruction& inst, uint32_t index) {
|
||||
switch (index) {
|
||||
case 0: return inst.src0;
|
||||
@@ -104,6 +102,7 @@ Decoder::Operand Translator::PlainOperand(const Decoder::Operand& operand) {
|
||||
result.dpp_ctrl = 0;
|
||||
result.dpp_row_mask = 0xf;
|
||||
result.dpp_bank_mask = 0xf;
|
||||
result.explicit_sdwa_dst = false;
|
||||
result.dpp_fetch_inactive = false;
|
||||
result.dpp_bound_ctrl = false;
|
||||
result.dpp = false;
|
||||
@@ -152,11 +151,8 @@ IR::U32 Translator::ReadScalarCode(uint32_t code) {
|
||||
case 106u: return ir.GetVccLo();
|
||||
case 107u: return ir.GetVccHi();
|
||||
case 124u: return ir.GetM0();
|
||||
case 126u:
|
||||
case 127u: {
|
||||
const auto mask = BallotMask(ir.GetExec());
|
||||
return mask[code - 126u];
|
||||
}
|
||||
case 126u: return ir.GetExecLo();
|
||||
case 127u: return ir.GetExecHi();
|
||||
default: return IR::U32(IR::Value(0u));
|
||||
}
|
||||
}
|
||||
@@ -316,19 +312,23 @@ void Translator::WriteRawU32(const Decoder::Operand& operand, IR::U32 value) {
|
||||
case Decoder::OperandKind::VccLo:
|
||||
ir.SetVccLo(IR::U32(value));
|
||||
ir.SetVcc(ThreadBit(IR::U32(value)));
|
||||
ir.SetVccMaskValidTag(IR::U1(IR::Value(false)));
|
||||
break;
|
||||
case Decoder::OperandKind::VccHi:
|
||||
ir.SetVccHi(IR::U32(value));
|
||||
ir.SetVcc(ThreadBit(ir.GetVccLo()));
|
||||
ir.SetVccMaskValidTag(IR::U1(IR::Value(false)));
|
||||
break;
|
||||
case Decoder::OperandKind::M0: ir.SetM0(IR::U32(value)); break;
|
||||
case Decoder::OperandKind::ExecLo:
|
||||
ir.SetExecLo(IR::U32(value));
|
||||
ir.SetExec(ThreadBit(IR::U32(value)));
|
||||
ir.SetExecMaskTag(IR::U1(IR::Value(false)));
|
||||
break;
|
||||
case Decoder::OperandKind::ExecHi:
|
||||
ir.SetExecHi(IR::U32(value));
|
||||
ir.SetExec(ThreadBit(ir.GetExecLo()));
|
||||
ir.SetExecMaskTag(IR::U1(IR::Value(false)));
|
||||
break;
|
||||
case Decoder::OperandKind::Scc:
|
||||
ir.SetScc(ir.INotEqual(value, IR::U32(IR::Value(0u))));
|
||||
@@ -373,6 +373,7 @@ void Translator::WriteOperand(const Decoder::Operand& operand, IR::Value value)
|
||||
case Decoder::OperandKind::ExecHi: {
|
||||
const auto mask = BallotMask(IR::U1(value));
|
||||
ir.SetExec(IR::U1(value));
|
||||
ir.SetExecMaskTag(IR::U1(IR::Value(true)));
|
||||
ir.SetExecLo(mask[0]);
|
||||
ir.SetExecHi(mask[1]);
|
||||
return;
|
||||
@@ -381,6 +382,7 @@ void Translator::WriteOperand(const Decoder::Operand& operand, IR::Value value)
|
||||
case Decoder::OperandKind::VccHi: {
|
||||
const auto mask = BallotMask(IR::U1(value));
|
||||
ir.SetVcc(IR::U1(value));
|
||||
ir.SetVccMaskValidTag(IR::U1(IR::Value(true)));
|
||||
ir.SetVccLo(mask[0]);
|
||||
ir.SetVccHi(mask[1]);
|
||||
return;
|
||||
@@ -392,12 +394,12 @@ void Translator::WriteOperand(const Decoder::Operand& operand, IR::Value value)
|
||||
}
|
||||
}
|
||||
if (type == IR::Type::U16) {
|
||||
WriteRawU32(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {IR::U16(value)})));
|
||||
Write16Bits(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {IR::U16(value)})));
|
||||
return;
|
||||
}
|
||||
if (type == IR::Type::F16) {
|
||||
const auto bits = IR::U16(ir.Emit(IR::ValueOpcode::BitCastU16F16, {value}));
|
||||
WriteRawU32(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {bits})));
|
||||
Write16Bits(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {bits})));
|
||||
return;
|
||||
}
|
||||
if (type == IR::Type::U64) {
|
||||
@@ -417,38 +419,32 @@ IR::U32 Translator::PackHalf2x16(IR::F32 low, IR::F32 high) {
|
||||
return IR::U32(ir.Emit(IR::ValueOpcode::PackHalf2x16, {pair}));
|
||||
}
|
||||
|
||||
void Translator::Write16Bits(const Decoder::Operand& operand, IR::U32 value) {
|
||||
auto destination = operand;
|
||||
// Native GFX10 16-bit results preserve the unselected half. Plain/DPP destinations use the
|
||||
// low half; native VOP3 may already select either half. Explicit SDWA retains encoded DST_U.
|
||||
if (!destination.explicit_sdwa_dst) {
|
||||
if (destination.sdwa_sel == 6u) {
|
||||
destination.sdwa_sel = 4u;
|
||||
}
|
||||
destination.sdwa_dst_unused = 2u;
|
||||
}
|
||||
destination.omod = 0u;
|
||||
destination.op_sel = false;
|
||||
destination.clamp = false;
|
||||
WriteRawU32(destination, ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu))));
|
||||
}
|
||||
|
||||
void Translator::WriteF16(const Decoder::Operand& operand, IR::F32 value) {
|
||||
value = ApplyF32ResultModifiers(operand, value);
|
||||
const auto half = IR::F16(ir.Emit(IR::ValueOpcode::ConvertF16F32, {value}));
|
||||
const auto bits = IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16,
|
||||
{IR::U16(ir.Emit(IR::ValueOpcode::BitCastU16F16, {half}))}));
|
||||
auto merged = bits;
|
||||
if (operand.sdwa_sel == 4u || operand.sdwa_sel == 5u) {
|
||||
merged = IR::U32(ir.Emit(IR::ValueOpcode::BitFieldInsert,
|
||||
{ReadRawU32(PlainOperand(operand)), bits,
|
||||
IR::Value(operand.sdwa_sel == 5u ? 16u : 0u), IR::Value(16u)}));
|
||||
}
|
||||
auto raw = operand;
|
||||
raw.sdwa_sel = 6u;
|
||||
raw.omod = 0u;
|
||||
raw.op_sel = false;
|
||||
raw.clamp = false;
|
||||
WriteRawU32(raw, merged);
|
||||
Write16Bits(operand, bits);
|
||||
}
|
||||
|
||||
void Translator::WriteU16(const Decoder::Operand& operand, IR::U32 value) {
|
||||
auto merged = ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu)));
|
||||
if (operand.sdwa_sel == 4u || operand.sdwa_sel == 5u) {
|
||||
merged = IR::U32(ir.Emit(IR::ValueOpcode::BitFieldInsert,
|
||||
{ReadRawU32(PlainOperand(operand)), merged,
|
||||
IR::Value(operand.sdwa_sel == 5u ? 16u : 0u), IR::Value(16u)}));
|
||||
}
|
||||
auto raw = operand;
|
||||
raw.sdwa_sel = 6u;
|
||||
raw.omod = 0u;
|
||||
raw.op_sel = false;
|
||||
raw.clamp = false;
|
||||
WriteRawU32(raw, merged);
|
||||
Write16Bits(operand, value);
|
||||
}
|
||||
|
||||
IR::U32 Translator::ReadU32(const Decoder::Operand& operand) {
|
||||
@@ -587,11 +583,13 @@ void Translator::WriteU32Pair(const Decoder::Operand& operand,
|
||||
switch (operand.kind) {
|
||||
case Decoder::OperandKind::ExecLo:
|
||||
ir.SetExec(thread_bit);
|
||||
ir.SetExecMaskTag(IR::U1(IR::Value(false)));
|
||||
ir.SetExecLo(value[0]);
|
||||
ir.SetExecHi(value[1]);
|
||||
return;
|
||||
case Decoder::OperandKind::VccLo:
|
||||
ir.SetVcc(thread_bit);
|
||||
ir.SetVccMaskValidTag(IR::U1(IR::Value(false)));
|
||||
ir.SetVccLo(value[0]);
|
||||
ir.SetVccHi(value[1]);
|
||||
return;
|
||||
@@ -602,6 +600,87 @@ void Translator::WriteU32Pair(const Decoder::Operand& operand,
|
||||
WriteRawU32(OffsetOperand(operand, 1), value[1]);
|
||||
}
|
||||
|
||||
Translator::ScalarU64 Translator::ReadScalarU64(const Decoder::Operand& operand) {
|
||||
return {ReadU32Pair(operand), ReadMask(operand), ReadMaskValid(operand)};
|
||||
}
|
||||
|
||||
Translator::ScalarU64 Translator::MakeScalarU64Mask(IR::U1 value) {
|
||||
return {BallotMask(value), value, IR::U1(IR::Value(true))};
|
||||
}
|
||||
|
||||
Translator::ScalarU64 Translator::MakeScalarU64Result(const std::array<IR::U32, 2>& raw,
|
||||
IR::U1 invocation, IR::U1 mask_valid) {
|
||||
return {raw,
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation, ThreadBit(raw[0])})),
|
||||
mask_valid};
|
||||
}
|
||||
|
||||
Translator::ScalarU64 Translator::SelectScalarU64(IR::U1 condition, const ScalarU64& lhs,
|
||||
const ScalarU64& rhs) {
|
||||
return {
|
||||
{ir.Select(condition, lhs.raw[0], rhs.raw[0]),
|
||||
ir.Select(condition, lhs.raw[1], rhs.raw[1])},
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {condition, lhs.invocation, rhs.invocation})),
|
||||
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {condition, lhs.mask_valid, rhs.mask_valid}))};
|
||||
}
|
||||
|
||||
Translator::ScalarU64 Translator::NotScalarU64(const ScalarU64& value) {
|
||||
return MakeScalarU64Result({ir.BitwiseNot(value.raw[0]), ir.BitwiseNot(value.raw[1])},
|
||||
ir.LogicalNot(value.invocation), value.mask_valid);
|
||||
}
|
||||
|
||||
Translator::ScalarU64 Translator::BinaryScalarU64(const ScalarU64& lhs, const ScalarU64& rhs,
|
||||
IR::ValueOpcode logical_opcode,
|
||||
IR::ValueOpcode bit_opcode, bool negate_lhs,
|
||||
bool negate_rhs, bool negate_result) {
|
||||
auto lhs_mask = negate_lhs ? ir.LogicalNot(lhs.invocation) : lhs.invocation;
|
||||
auto rhs_mask = negate_rhs ? ir.LogicalNot(rhs.invocation) : rhs.invocation;
|
||||
auto mask = IR::U1(ir.Emit(logical_opcode, {lhs_mask, rhs_mask}));
|
||||
if (negate_result) {
|
||||
mask = ir.LogicalNot(mask);
|
||||
}
|
||||
std::array<IR::U32, 2> raw;
|
||||
for (uint32_t component = 0; component < raw.size(); component++) {
|
||||
auto lhs_raw = negate_lhs ? ir.BitwiseNot(lhs.raw[component]) : lhs.raw[component];
|
||||
auto rhs_raw = negate_rhs ? ir.BitwiseNot(rhs.raw[component]) : rhs.raw[component];
|
||||
raw[component] = IR::U32(ir.Emit(bit_opcode, {lhs_raw, rhs_raw}));
|
||||
if (negate_result) {
|
||||
raw[component] = ir.BitwiseNot(raw[component]);
|
||||
}
|
||||
}
|
||||
return MakeScalarU64Result(raw, mask, ir.LogicalAnd(lhs.mask_valid, rhs.mask_valid));
|
||||
}
|
||||
|
||||
void Translator::WriteScalarU64(const Decoder::Operand& operand, const ScalarU64& value) {
|
||||
WriteU32Pair(operand, value.raw);
|
||||
switch (operand.kind) {
|
||||
case Decoder::OperandKind::ExecLo:
|
||||
case Decoder::OperandKind::ExecHi:
|
||||
ir.SetExec(value.invocation);
|
||||
ir.SetExecMaskTag(value.mask_valid);
|
||||
break;
|
||||
case Decoder::OperandKind::VccLo:
|
||||
case Decoder::OperandKind::VccHi:
|
||||
ir.SetVcc(value.invocation);
|
||||
ir.SetVccMaskValidTag(value.mask_valid);
|
||||
break;
|
||||
case Decoder::OperandKind::Sgpr: {
|
||||
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
|
||||
ir.SetThreadBitScalarReg(reg, value.invocation);
|
||||
ir.SetScalarMaskTag(reg, value.mask_valid);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
IR::U1 Translator::ScalarU64NonZero(const ScalarU64& value) {
|
||||
const auto raw_nonzero =
|
||||
ir.INotEqual(ir.BitwiseOr(value.raw[0], value.raw[1]), IR::U32(IR::Value(0u)));
|
||||
return IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::SelectU1, {value.mask_valid, value.invocation, raw_nonzero}));
|
||||
}
|
||||
|
||||
IR::U1 Translator::ThreadBit(IR::U32 low) {
|
||||
return ir.INotEqual(low, IR::U32(IR::Value(0u)));
|
||||
}
|
||||
@@ -657,9 +736,9 @@ IR::U1 Translator::ReadMaskValid(const Decoder::Operand& operand) {
|
||||
case Decoder::OperandKind::Sgpr:
|
||||
return ir.GetScalarMaskTag(static_cast<IR::ScalarReg>(operand.reg));
|
||||
case Decoder::OperandKind::ExecLo:
|
||||
case Decoder::OperandKind::ExecHi:
|
||||
case Decoder::OperandKind::ExecHi: return ir.GetExecMaskTag();
|
||||
case Decoder::OperandKind::VccLo:
|
||||
case Decoder::OperandKind::VccHi:
|
||||
case Decoder::OperandKind::VccHi: return ir.GetVccMaskValidTag();
|
||||
case Decoder::OperandKind::VccZ:
|
||||
case Decoder::OperandKind::ExecZ:
|
||||
case Decoder::OperandKind::Scc: return IR::U1(IR::Value(true));
|
||||
@@ -692,6 +771,7 @@ void Translator::WriteMask(const Decoder::Operand& operand, IR::U1 value) {
|
||||
case Decoder::OperandKind::ExecHi: {
|
||||
const auto mask = BallotMask(value);
|
||||
ir.SetExec(value);
|
||||
ir.SetExecMaskTag(IR::U1(IR::Value(true)));
|
||||
ir.SetExecLo(mask[0]);
|
||||
ir.SetExecHi(mask[1]);
|
||||
return;
|
||||
@@ -700,6 +780,7 @@ void Translator::WriteMask(const Decoder::Operand& operand, IR::U1 value) {
|
||||
case Decoder::OperandKind::VccHi: {
|
||||
const auto mask = BallotMask(value);
|
||||
ir.SetVcc(value);
|
||||
ir.SetVccMaskValidTag(IR::U1(IR::Value(true)));
|
||||
ir.SetVccLo(mask[0]);
|
||||
ir.SetVccHi(mask[1]);
|
||||
return;
|
||||
@@ -713,25 +794,7 @@ void Translator::WriteMask(const Decoder::Operand& operand, IR::U1 value) {
|
||||
}
|
||||
|
||||
void Translator::WriteMask64(const Decoder::Operand& operand, IR::U1 value) {
|
||||
if (operand.kind != Decoder::OperandKind::Sgpr) {
|
||||
WriteMask(operand, value);
|
||||
return;
|
||||
}
|
||||
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
|
||||
const auto mask = BallotMask(value);
|
||||
ir.SetThreadBitScalarReg(reg, value);
|
||||
ir.SetScalarMaskTag(reg, IR::U1(IR::Value(true)));
|
||||
if (IR::RegIndex(reg) > 0u) {
|
||||
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(IR::RegIndex(reg) - 1u),
|
||||
IR::U1(IR::Value(false)));
|
||||
}
|
||||
ir.SetScalarReg(reg, mask[0]);
|
||||
if (IR::RegIndex(reg) + 1u < IR::NumScalarRegs) {
|
||||
const auto high = static_cast<IR::ScalarReg>(IR::RegIndex(reg) + 1u);
|
||||
ir.SetScalarReg(high, mask[1]);
|
||||
ir.SetThreadBitScalarReg(high, IR::U1(IR::Value(false)));
|
||||
ir.SetScalarMaskTag(high, IR::U1(IR::Value(false)));
|
||||
}
|
||||
WriteScalarU64(operand, MakeScalarU64Mask(value));
|
||||
}
|
||||
|
||||
void Translator::WriteCompareResult(const Decoder::Operand& operand, IR::U1 value) {
|
||||
@@ -745,11 +808,10 @@ void Translator::WriteCompareResult(const Decoder::Operand& operand, IR::U1 valu
|
||||
WriteMask(operand, ir.LogicalAnd(ir.GetExec(), value));
|
||||
}
|
||||
|
||||
bool Translator::AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info,
|
||||
std::string* error) {
|
||||
void Translator::AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info) {
|
||||
if (source.terminator.goto_value >= 0) {
|
||||
if (source.terminator.goto_variable == UINT32_MAX) {
|
||||
return Fail(error, fmt::format("block {} sets an invalid goto variable", source.id));
|
||||
EXIT("block %u sets an invalid goto variable", source.id);
|
||||
}
|
||||
ir.SetGotoVariable(source.terminator.goto_variable,
|
||||
IR::U1(IR::Value(source.terminator.goto_value != 0)));
|
||||
@@ -761,13 +823,13 @@ bool Translator::AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo
|
||||
info.indirect_target =
|
||||
ir.GetScalarReg(static_cast<IR::ScalarReg>(source.terminator.indirect_pc_sgpr));
|
||||
} else {
|
||||
return Fail(error, fmt::format("block {} has no indirect branch selector", source.id));
|
||||
EXIT("block %u has no indirect branch selector", source.id);
|
||||
}
|
||||
ir.Emit(IR::ValueOpcode::ReferenceU32, {info.indirect_target});
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if (source.terminator.kind != CFG::TerminatorKind::ConditionalBranch) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
// EXEC and VCC are invocation-local Boolean masks. Branching on that Boolean lets inactive
|
||||
// invocations leave the region without reconstructing a host-subgroup mask.
|
||||
@@ -782,17 +844,15 @@ bool Translator::AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo
|
||||
case CFG::BranchCondition::ExecNonZero: condition = ir.GetExec(); break;
|
||||
case CFG::BranchCondition::GotoVariable:
|
||||
if (source.terminator.goto_variable == UINT32_MAX) {
|
||||
return Fail(error,
|
||||
fmt::format("block {} reads an invalid goto variable", source.id));
|
||||
EXIT("block %u reads an invalid goto variable", source.id);
|
||||
}
|
||||
condition = ir.GetGotoVariable(source.terminator.goto_variable);
|
||||
break;
|
||||
case CFG::BranchCondition::Unknown:
|
||||
return Fail(error, fmt::format("block {} has an unknown branch condition", source.id));
|
||||
EXIT("block %u has an unknown branch condition", source.id);
|
||||
}
|
||||
info.condition = condition;
|
||||
ir.Emit(IR::ValueOpcode::Reference, {condition});
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -914,47 +974,42 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidateTranslateOptions(const TranslateOptions& options, std::string* error) {
|
||||
void ValidateTranslateOptions(const TranslateOptions& options) {
|
||||
if (options.wave_size != 32u && options.wave_size != 64u) {
|
||||
return Fail(error, "shader translation requires wave32 or wave64");
|
||||
EXIT("shader translation requires wave32 or wave64, got %u", options.wave_size);
|
||||
}
|
||||
switch (options.stage) {
|
||||
case ShaderType::Vertex:
|
||||
return options.vertex != nullptr ||
|
||||
Fail(error, "vertex shader translation has no vertex input metadata");
|
||||
if (options.vertex == nullptr) {
|
||||
EXIT("vertex shader translation has no vertex input metadata");
|
||||
}
|
||||
return;
|
||||
case ShaderType::Pixel:
|
||||
return options.pixel != nullptr ||
|
||||
Fail(error, "pixel shader translation has no pixel input metadata");
|
||||
if (options.pixel == nullptr) {
|
||||
EXIT("pixel shader translation has no pixel input metadata");
|
||||
}
|
||||
return;
|
||||
case ShaderType::Compute:
|
||||
return options.compute != nullptr ||
|
||||
Fail(error, "compute shader translation has no compute input metadata");
|
||||
default: return Fail(error, "shader translation has an unsupported stage");
|
||||
if (options.compute == nullptr) {
|
||||
EXIT("compute shader translation has no compute input metadata");
|
||||
}
|
||||
return;
|
||||
default:
|
||||
EXIT("shader translation has unsupported stage %u",
|
||||
static_cast<uint32_t>(options.stage));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
const TranslateOptions& options, IR::Program& result, std::string* error) {
|
||||
result = {};
|
||||
struct ResetOnFailure {
|
||||
IR::Program& program;
|
||||
bool complete = false;
|
||||
|
||||
~ResetOnFailure() {
|
||||
if (!complete) {
|
||||
program = {};
|
||||
}
|
||||
}
|
||||
} reset_on_failure {result};
|
||||
|
||||
if (!ValidateTranslateOptions(options, error)) {
|
||||
return false;
|
||||
}
|
||||
IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
const TranslateOptions& options) {
|
||||
ValidateTranslateOptions(options);
|
||||
if (cfg.blocks.empty()) {
|
||||
return Fail(error, "cannot translate an empty CFG");
|
||||
EXIT("cannot translate an empty CFG");
|
||||
}
|
||||
|
||||
IR::Program result;
|
||||
result.stage = options.stage;
|
||||
result.wave_size = options.wave_size;
|
||||
result.shader_hash = options.shader_hash;
|
||||
@@ -972,8 +1027,8 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
for (const auto& cfg_block: cfg.blocks) {
|
||||
for (uint32_t index = cfg_block.inst_begin; index < cfg_block.inst_end; index++) {
|
||||
if (index >= decoded.instructions.size()) {
|
||||
return Fail(error,
|
||||
"CFG block references an instruction outside the decoded program");
|
||||
EXIT("CFG block %u references instruction %u outside decoded program of size %zu",
|
||||
cfg_block.id, index, decoded.instructions.size());
|
||||
}
|
||||
const auto& instruction = decoded.instructions[index];
|
||||
if (IsCodeTableLoad(cfg, instruction.pc)) {
|
||||
@@ -990,7 +1045,7 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
return block.id;
|
||||
})->id;
|
||||
if (max_id == UINT32_MAX) {
|
||||
return Fail(error, "cannot allocate a typed entry block id");
|
||||
EXIT("cannot allocate a typed entry block id");
|
||||
}
|
||||
CFG::Terminator terminator;
|
||||
terminator.kind = CFG::TerminatorKind::Branch;
|
||||
@@ -1004,7 +1059,7 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
block_indices.reserve(cfg.blocks.size());
|
||||
for (const auto& source_block: cfg.blocks) {
|
||||
if (!block_indices.emplace(source_block.id, result.blocks.size()).second) {
|
||||
return Fail(error, fmt::format("CFG contains duplicate block id {}", source_block.id));
|
||||
EXIT("CFG contains duplicate block id %u", source_block.id);
|
||||
}
|
||||
result.block_storage.push_back(std::make_unique<IR::Block>());
|
||||
result.blocks.push_back(result.block_storage.back().get());
|
||||
@@ -1016,8 +1071,7 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
for (const auto successor: source_block.successors) {
|
||||
const auto target = block_indices.find(successor);
|
||||
if (target == block_indices.end()) {
|
||||
return Fail(error, fmt::format("CFG block {} has unknown successor {}",
|
||||
source_block.id, successor));
|
||||
EXIT("CFG block %u has unknown successor %u", source_block.id, successor);
|
||||
}
|
||||
result.blocks[source_index]->AddBranch(result.blocks[target->second]);
|
||||
}
|
||||
@@ -1041,6 +1095,7 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
entry_ir.SetScalarMaskTag(reg, IR::U1(IR::Value(false)));
|
||||
}
|
||||
entry_ir.SetExec(IR::U1(IR::Value(true)));
|
||||
entry_ir.SetExecMaskTag(IR::U1(IR::Value(true)));
|
||||
entry_ir.SetExecLo(IR::U32(IR::Value(1u)));
|
||||
entry_ir.SetExecHi(IR::U32(IR::Value(0u)));
|
||||
if (options.stage == ShaderType::Compute) {
|
||||
@@ -1115,7 +1170,7 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
}
|
||||
}
|
||||
for (const auto& cfg_block: cfg.blocks) {
|
||||
const auto typed_index = block_indices.at(cfg_block.id);
|
||||
const auto typed_index = block_indices.at(cfg_block.id);
|
||||
Translator translator(result, result.blocks[typed_index], vector_limit, options.wave_size);
|
||||
for (uint32_t index = cfg_block.inst_begin; index < cfg_block.inst_end; index++) {
|
||||
const auto& instruction = decoded.instructions[index];
|
||||
@@ -1131,44 +1186,27 @@ bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
instruction.data_dwords == embedded->components &&
|
||||
instruction.dst.kind == Decoder::OperandKind::Vgpr) {
|
||||
if (options.vertex == nullptr) {
|
||||
return Fail(error, "embedded vertex fetch plan has no vertex input metadata");
|
||||
EXIT("embedded vertex fetch plan has no vertex input metadata");
|
||||
}
|
||||
const auto resource = ResolveEmbeddedFetchResource(*options.vertex, *embedded);
|
||||
if (resource < 0 || resource >= options.vertex->resources_num) {
|
||||
return Fail(
|
||||
error,
|
||||
fmt::format(
|
||||
"embedded vertex fetch at 0x{:08x} has no resource for attribute {}",
|
||||
instruction.pc, embedded->attrib_id));
|
||||
EXIT("embedded vertex fetch at 0x%08x has no resource for attribute %d",
|
||||
instruction.pc, embedded->attrib_id);
|
||||
}
|
||||
result.info.vertex_fetch_components[static_cast<size_t>(resource)] =
|
||||
static_cast<uint8_t>(std::max<uint32_t>(
|
||||
result.info.vertex_fetch_components[static_cast<size_t>(resource)],
|
||||
embedded->components));
|
||||
if (!translator.TranslateEmbeddedFetch(instruction, static_cast<uint32_t>(resource),
|
||||
embedded->components)) {
|
||||
return false;
|
||||
}
|
||||
translator.TranslateEmbeddedFetch(instruction, static_cast<uint32_t>(resource),
|
||||
embedded->components);
|
||||
continue;
|
||||
}
|
||||
if (!translator.TranslateInstruction(instruction, error)) {
|
||||
if (error == nullptr || error->empty()) {
|
||||
return Fail(error, fmt::format("opcode {} at 0x{:08x} has no IR translation",
|
||||
magic_enum::enum_name(instruction.opcode),
|
||||
instruction.pc));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!translator.AddBranchCondition(cfg_block, result.block_info[typed_index], error)) {
|
||||
return false;
|
||||
translator.TranslateInstruction(instruction);
|
||||
}
|
||||
translator.AddBranchCondition(cfg_block, result.block_info[typed_index]);
|
||||
}
|
||||
if (!IR::ValidateProgram(result, false, error)) {
|
||||
return false;
|
||||
}
|
||||
reset_on_failure.complete = true;
|
||||
return true;
|
||||
IR::ValidateProgram(result, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
@@ -38,7 +38,7 @@ struct TranslateOptions {
|
||||
const EmbeddedFetchPlan* embedded_fetch = nullptr;
|
||||
};
|
||||
|
||||
bool TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
const TranslateOptions& options, IR::Program& result, std::string* error);
|
||||
IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& cfg,
|
||||
const TranslateOptions& options);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
@@ -13,12 +13,20 @@ public:
|
||||
: program(program), ir(block), current_vector_limit(vector_limit),
|
||||
current_wave_size(wave_size) {}
|
||||
|
||||
bool TranslateInstruction(const Decoder::Instruction& inst, std::string* error);
|
||||
bool TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
void TranslateInstruction(const Decoder::Instruction& inst);
|
||||
void TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute,
|
||||
uint32_t component_count);
|
||||
bool AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info, std::string* error);
|
||||
void AddBranchCondition(const CFG::BasicBlock& source, IR::BlockInfo& info);
|
||||
|
||||
private:
|
||||
struct ScalarU64 {
|
||||
std::array<IR::U32, 2> raw;
|
||||
// Raw words and Kyty's per-invocation mask model remain independent.
|
||||
// mask_valid selects invocation for logical consumers and SCC.
|
||||
IR::U1 invocation;
|
||||
IR::U1 mask_valid;
|
||||
};
|
||||
|
||||
Decoder::Operand SourceAt(const Decoder::Instruction& inst, uint32_t index);
|
||||
Decoder::Operand DestinationOperand(const Decoder::Instruction& inst);
|
||||
Decoder::Operand OffsetOperand(const Decoder::Operand& operand, uint32_t offset);
|
||||
@@ -34,6 +42,7 @@ private:
|
||||
IR::F32 ApplyF32ResultModifiers(const Decoder::Operand& operand, IR::F32 value);
|
||||
void WriteOperand(const Decoder::Operand& operand, IR::Value value);
|
||||
IR::U32 PackHalf2x16(IR::F32 low, IR::F32 high);
|
||||
void Write16Bits(const Decoder::Operand& operand, IR::U32 value);
|
||||
void WriteF16(const Decoder::Operand& operand, IR::F32 value);
|
||||
void WriteU16(const Decoder::Operand& operand, IR::U32 value);
|
||||
IR::U32 ReadU32(const Decoder::Operand& operand);
|
||||
@@ -47,14 +56,25 @@ private:
|
||||
IR::U32 ReadU16AsU32(const Decoder::Operand& operand, bool sign_extend);
|
||||
IR::U32 ReadF16LaneBits(const Decoder::Operand& operand, bool high_lane);
|
||||
std::array<IR::U32, 2> ExtractU64(IR::U64 value);
|
||||
void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
|
||||
IR::U1 ReadCondition(const Decoder::Operand& operand);
|
||||
IR::U32 ConditionBit(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMask(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMaskValid(const Decoder::Operand& operand);
|
||||
void WriteMask(const Decoder::Operand& operand, IR::U1 value);
|
||||
void WriteMask64(const Decoder::Operand& operand, IR::U1 value);
|
||||
void WriteCompareResult(const Decoder::Operand& operand, IR::U1 value);
|
||||
void WriteU32Pair(const Decoder::Operand& operand, const std::array<IR::U32, 2>& value);
|
||||
ScalarU64 ReadScalarU64(const Decoder::Operand& operand);
|
||||
ScalarU64 MakeScalarU64Mask(IR::U1 value);
|
||||
ScalarU64 MakeScalarU64Result(const std::array<IR::U32, 2>& raw, IR::U1 invocation,
|
||||
IR::U1 mask_valid);
|
||||
ScalarU64 SelectScalarU64(IR::U1 condition, const ScalarU64& lhs, const ScalarU64& rhs);
|
||||
ScalarU64 NotScalarU64(const ScalarU64& value);
|
||||
ScalarU64 BinaryScalarU64(const ScalarU64& lhs, const ScalarU64& rhs,
|
||||
IR::ValueOpcode logical_opcode, IR::ValueOpcode bit_opcode,
|
||||
bool negate_lhs, bool negate_rhs, bool negate_result);
|
||||
void WriteScalarU64(const Decoder::Operand& operand, const ScalarU64& value);
|
||||
IR::U1 ScalarU64NonZero(const ScalarU64& value);
|
||||
IR::U1 ReadCondition(const Decoder::Operand& operand);
|
||||
IR::U32 ConditionBit(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMask(const Decoder::Operand& operand);
|
||||
IR::U1 ReadMaskValid(const Decoder::Operand& operand);
|
||||
void WriteMask(const Decoder::Operand& operand, IR::U1 value);
|
||||
void WriteMask64(const Decoder::Operand& operand, IR::U1 value);
|
||||
void WriteCompareResult(const Decoder::Operand& operand, IR::U1 value);
|
||||
|
||||
IR::MemoryFlags AddMemoryInfo(const IR::MemoryInfo& memory, uint32_t pc);
|
||||
IR::ExportFlags AddExportInfo(const Decoder::Instruction& inst);
|
||||
@@ -72,7 +92,7 @@ private:
|
||||
IR::U32 GetResourceDword(uint32_t index, uint32_t dword);
|
||||
IR::Value GetBufferResource(const IR::MemoryInfo& memory);
|
||||
IR::Value GetAddressResource(IR::Value low, IR::Value high);
|
||||
IR::Value GetScalarAddressResource(uint32_t base);
|
||||
IR::Value GetScalarAddressResource(const Decoder::Operand& base);
|
||||
IR::Value GetImageResource(const IR::MemoryInfo& memory);
|
||||
IR::Value GetSamplerResource(const IR::MemoryInfo& memory);
|
||||
IR::Value MakeImageAddress(const Decoder::Instruction& inst, const Decoder::Operand& base);
|
||||
@@ -127,7 +147,7 @@ private:
|
||||
void EmitFloatCompare(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool half,
|
||||
bool cmpx);
|
||||
void EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered);
|
||||
void V_CMP_CLASS_F32(const Decoder::Instruction& inst);
|
||||
void EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx);
|
||||
void V_CVT_F32_UBYTE(const Decoder::Instruction& inst, uint32_t byte_index);
|
||||
void V_CVT_F32_U32(const Decoder::Instruction& inst);
|
||||
void V_CVT_F32_I32(const Decoder::Instruction& inst);
|
||||
@@ -174,28 +194,26 @@ private:
|
||||
bool PackedInteger16MinMax(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
|
||||
bool S_U64_MASK(const Decoder::Instruction& inst, IR::ValueOpcode logical_opcode,
|
||||
IR::ValueOpcode bit_opcode, bool negate_rhs, bool negate_result, bool unary);
|
||||
IR::U1 U64MaskBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool negate_rhs,
|
||||
bool negate_result);
|
||||
bool SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode opcode, IR::Type type,
|
||||
bool reverse, bool mask_shift_count, bool update_scc);
|
||||
bool ComposedIntegerBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool negate_rhs, bool negate_result, bool update_scc);
|
||||
bool V_AND_OR_B32(const Decoder::Instruction& inst);
|
||||
bool V_OR3_B32(const Decoder::Instruction& inst);
|
||||
bool V_XOR3_B32(const Decoder::Instruction& inst);
|
||||
bool S_FF1_I32_B64(const Decoder::Instruction& inst);
|
||||
bool V_FFBH_32(const Decoder::Instruction& inst, bool sign);
|
||||
bool S_FLBIT_I32_B64(const Decoder::Instruction& inst);
|
||||
bool Integer24(const Decoder::Instruction& inst, bool sign, bool addend);
|
||||
bool V_MAD_U64_U32(const Decoder::Instruction& inst);
|
||||
bool V_SAD_U32(const Decoder::Instruction& inst);
|
||||
bool V_ADD3_U32(const Decoder::Instruction& inst);
|
||||
bool S_BITSET_B32(const Decoder::Instruction& inst, bool set);
|
||||
bool V_BCNT_U32_B32(const Decoder::Instruction& inst);
|
||||
bool V_MBCNT_U32_B32(const Decoder::Instruction& inst, bool low);
|
||||
bool S_BITREPLICATE_B64_B32(const Decoder::Instruction& inst);
|
||||
bool S_QUADMASK_B64(const Decoder::Instruction& inst);
|
||||
bool BFM_B32(const Decoder::Instruction& inst);
|
||||
bool SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode opcode, IR::Type type,
|
||||
bool reverse, bool mask_shift_count, bool update_scc);
|
||||
bool ComposedIntegerBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool negate_rhs, bool negate_result, bool update_scc);
|
||||
bool V_AND_OR_B32(const Decoder::Instruction& inst);
|
||||
bool V_OR3_B32(const Decoder::Instruction& inst);
|
||||
bool V_XOR3_B32(const Decoder::Instruction& inst);
|
||||
bool S_FF1_I32_B64(const Decoder::Instruction& inst);
|
||||
bool V_FFBH_32(const Decoder::Instruction& inst, bool sign);
|
||||
bool S_FLBIT_I32_B64(const Decoder::Instruction& inst);
|
||||
bool Integer24(const Decoder::Instruction& inst, bool sign, bool addend);
|
||||
bool V_MAD_U64_U32(const Decoder::Instruction& inst);
|
||||
bool V_SAD_U32(const Decoder::Instruction& inst);
|
||||
bool V_ADD3_U32(const Decoder::Instruction& inst);
|
||||
bool S_BITSET_B32(const Decoder::Instruction& inst, bool set);
|
||||
bool V_BCNT_U32_B32(const Decoder::Instruction& inst);
|
||||
bool V_MBCNT_U32_B32(const Decoder::Instruction& inst, bool low);
|
||||
bool S_BITREPLICATE_B64_B32(const Decoder::Instruction& inst);
|
||||
bool S_QUADMASK_B64(const Decoder::Instruction& inst);
|
||||
bool BFM_B32(const Decoder::Instruction& inst);
|
||||
IR::U32 RightMask32(IR::U32 count);
|
||||
IR::U64 RightMask64(IR::U32 count);
|
||||
bool S_BFM_B64(const Decoder::Instruction& inst);
|
||||
@@ -228,27 +246,27 @@ private:
|
||||
void S_SENDMSG();
|
||||
void S_TTRACEDATA();
|
||||
void S_INST_PREFETCH();
|
||||
bool S_GETPC_B64(const Decoder::Instruction& inst, std::string* error);
|
||||
void S_GETPC_B64(const Decoder::Instruction& inst);
|
||||
void S_CSELECT_B32(const Decoder::Instruction& inst);
|
||||
void S_CSELECT_B64(const Decoder::Instruction& inst);
|
||||
void MOV_B32(const Decoder::Instruction& inst, bool apply_float_modifiers);
|
||||
void S_MOV_B64(const Decoder::Instruction& inst);
|
||||
void S_WQM_B64(const Decoder::Instruction& inst);
|
||||
bool V_MOVRELS_B32(const Decoder::Instruction& inst, std::string* error);
|
||||
bool V_MOVRELD_B32(const Decoder::Instruction& inst, std::string* error);
|
||||
void V_MOVRELS_B32(const Decoder::Instruction& inst);
|
||||
void V_MOVRELD_B32(const Decoder::Instruction& inst);
|
||||
void V_READFIRSTLANE_B32(const Decoder::Instruction& inst);
|
||||
void V_READLANE_B32(const Decoder::Instruction& inst);
|
||||
void V_WRITELANE_B32(const Decoder::Instruction& inst);
|
||||
void V_PERMLANE16_B32(const Decoder::Instruction& inst, bool x16);
|
||||
void V_INTERP_P1_F32();
|
||||
void V_INTERP_P2_F32(const Decoder::Instruction& inst);
|
||||
bool V_INTERP_MOV_F32(const Decoder::Instruction& inst, std::string* error);
|
||||
bool EXP(const Decoder::Instruction& inst, std::string* error);
|
||||
void V_INTERP_MOV_F32(const Decoder::Instruction& inst);
|
||||
void EXP(const Decoder::Instruction& inst);
|
||||
|
||||
bool EmitScalar(const Decoder::Instruction& inst, std::string* error);
|
||||
bool EmitVector(const Decoder::Instruction& inst, std::string* error);
|
||||
bool EmitInterpolation(const Decoder::Instruction& inst, std::string* error);
|
||||
bool EmitMemory(const Decoder::Instruction& inst, std::string* error);
|
||||
bool EmitScalar(const Decoder::Instruction& inst);
|
||||
bool EmitVector(const Decoder::Instruction& inst);
|
||||
bool EmitInterpolation(const Decoder::Instruction& inst);
|
||||
bool EmitMemory(const Decoder::Instruction& inst);
|
||||
|
||||
IR::Program& program;
|
||||
IR::IREmitter ir;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
|
||||
bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error) {
|
||||
bool Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
using O = Decoder::Opcode;
|
||||
switch (inst.opcode) {
|
||||
case O::V_NOP: return true;
|
||||
@@ -13,8 +13,8 @@ bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error
|
||||
case O::V_SUBREV_CO_CI_U32: SUBB_U32(inst, true); return true;
|
||||
|
||||
case O::V_MOV_B32: MOV_B32(inst, true); return true;
|
||||
case O::V_MOVRELS_B32: return V_MOVRELS_B32(inst, error);
|
||||
case O::V_MOVRELD_B32: return V_MOVRELD_B32(inst, error);
|
||||
case O::V_MOVRELS_B32: V_MOVRELS_B32(inst); return true;
|
||||
case O::V_MOVRELD_B32: V_MOVRELD_B32(inst); return true;
|
||||
case O::V_READFIRSTLANE_B32: V_READFIRSTLANE_B32(inst); return true;
|
||||
case O::V_READLANE_B32: V_READLANE_B32(inst); return true;
|
||||
case O::V_WRITELANE_B32: V_WRITELANE_B32(inst); return true;
|
||||
@@ -271,7 +271,8 @@ bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error
|
||||
return true;
|
||||
case O::V_CMP_O_F32: EmitFloatOrderedCompare(inst, true); return true;
|
||||
case O::V_CMP_U_F32: EmitFloatOrderedCompare(inst, false); return true;
|
||||
case O::V_CMP_CLASS_F32: V_CMP_CLASS_F32(inst); return true;
|
||||
case O::V_CMP_CLASS_F32: EmitFloatClassCompare(inst, false); return true;
|
||||
case O::V_CMPX_CLASS_F32: EmitFloatClassCompare(inst, true); return true;
|
||||
|
||||
case O::V_CVT_F32_UBYTE0: V_CVT_F32_UBYTE(inst, 0); return true;
|
||||
case O::V_CVT_F32_UBYTE1: V_CVT_F32_UBYTE(inst, 1); return true;
|
||||
|
||||
@@ -76,6 +76,14 @@ void IREmitter::SetExec(U1 value) {
|
||||
Emit(ValueOpcode::SetExec, {value});
|
||||
}
|
||||
|
||||
U1 IREmitter::GetExecMaskTag() {
|
||||
return U1(Emit(ValueOpcode::GetExecMaskTag));
|
||||
}
|
||||
|
||||
void IREmitter::SetExecMaskTag(U1 value) {
|
||||
Emit(ValueOpcode::SetExecMaskTag, {value});
|
||||
}
|
||||
|
||||
U32 IREmitter::GetExecLo() {
|
||||
return U32(Emit(ValueOpcode::GetExecLo));
|
||||
}
|
||||
@@ -100,6 +108,14 @@ void IREmitter::SetVcc(U1 value) {
|
||||
Emit(ValueOpcode::SetVcc, {value});
|
||||
}
|
||||
|
||||
U1 IREmitter::GetVccMaskValidTag() {
|
||||
return U1(Emit(ValueOpcode::GetVccMaskValidTag));
|
||||
}
|
||||
|
||||
void IREmitter::SetVccMaskValidTag(U1 value) {
|
||||
Emit(ValueOpcode::SetVccMaskValidTag, {value});
|
||||
}
|
||||
|
||||
U32 IREmitter::GetVccLo() {
|
||||
return U32(Emit(ValueOpcode::GetVccLo));
|
||||
}
|
||||
|
||||
@@ -39,12 +39,16 @@ public:
|
||||
void SetScc(U1 value);
|
||||
U1 GetExec();
|
||||
void SetExec(U1 value);
|
||||
U1 GetExecMaskTag();
|
||||
void SetExecMaskTag(U1 value);
|
||||
U32 GetExecLo();
|
||||
void SetExecLo(U32 value);
|
||||
U32 GetExecHi();
|
||||
void SetExecHi(U32 value);
|
||||
U1 GetVcc();
|
||||
void SetVcc(U1 value);
|
||||
U1 GetVccMaskValidTag();
|
||||
void SetVccMaskValidTag(U1 value);
|
||||
U32 GetVccLo();
|
||||
void SetVccLo(U32 value);
|
||||
U32 GetVccHi();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <map>
|
||||
#include <new>
|
||||
@@ -11,10 +10,13 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
using ShaderError::Fail;
|
||||
|
||||
namespace {
|
||||
|
||||
[[noreturn]] void Fail(std::string_view message) {
|
||||
EXIT("shader IR validation failed: %s", std::string(message).c_str());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
bool IsRegisterStatePseudo(ValueOpcode opcode) {
|
||||
switch (opcode) {
|
||||
case ValueOpcode::GetThreadBitScalarRegister:
|
||||
@@ -31,12 +33,16 @@ bool IsRegisterStatePseudo(ValueOpcode opcode) {
|
||||
case ValueOpcode::SetScc:
|
||||
case ValueOpcode::GetExec:
|
||||
case ValueOpcode::SetExec:
|
||||
case ValueOpcode::GetExecMaskTag:
|
||||
case ValueOpcode::SetExecMaskTag:
|
||||
case ValueOpcode::GetExecLo:
|
||||
case ValueOpcode::SetExecLo:
|
||||
case ValueOpcode::GetExecHi:
|
||||
case ValueOpcode::SetExecHi:
|
||||
case ValueOpcode::GetVcc:
|
||||
case ValueOpcode::SetVcc:
|
||||
case ValueOpcode::GetVccMaskValidTag:
|
||||
case ValueOpcode::SetVccMaskValidTag:
|
||||
case ValueOpcode::GetVccLo:
|
||||
case ValueOpcode::SetVccLo:
|
||||
case ValueOpcode::GetVccHi:
|
||||
@@ -149,10 +155,10 @@ Value ResolveInvariantPhi(const Program& program, Value value) {
|
||||
return invariant;
|
||||
}
|
||||
|
||||
bool ValidateProgram(const Program& program, bool require_ssa, std::string* error) {
|
||||
void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
if (program.blocks.size() != program.block_info.size() ||
|
||||
program.blocks.size() != program.block_storage.size()) {
|
||||
return Fail(error, "value IR block storage is inconsistent");
|
||||
return Fail("value IR block storage is inconsistent");
|
||||
}
|
||||
std::unordered_set<const Block*> blocks;
|
||||
std::unordered_map<const Block*, size_t> block_indices;
|
||||
@@ -162,26 +168,26 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
const auto* block = program.blocks[block_index];
|
||||
if (block == nullptr || program.block_storage[block_index] == nullptr ||
|
||||
block != program.block_storage[block_index].get()) {
|
||||
return Fail(error, "value IR block pointer is inconsistent");
|
||||
return Fail("value IR block pointer is inconsistent");
|
||||
}
|
||||
if (!blocks.insert(block).second) {
|
||||
return Fail(error, "value IR block pointer is duplicated");
|
||||
return Fail("value IR block pointer is duplicated");
|
||||
}
|
||||
block_indices.emplace(block, block_index);
|
||||
if (program.block_info[block_index].id == UINT32_MAX) {
|
||||
return Fail(error, "value IR block uses the reserved exit id");
|
||||
return Fail("value IR block uses the reserved exit id");
|
||||
}
|
||||
if (!blocks_by_id.emplace(program.block_info[block_index].id, block).second) {
|
||||
return Fail(error, "value IR block id is duplicated");
|
||||
return Fail("value IR block id is duplicated");
|
||||
}
|
||||
for (const auto& inst: *block) {
|
||||
if (!instructions.insert(&inst).second) {
|
||||
return Fail(error, "value IR instruction is duplicated");
|
||||
return Fail("value IR instruction is duplicated");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!program.blocks.empty() && !program.blocks.front()->ImmPredecessors().empty()) {
|
||||
return Fail(error, "value IR entry block has a predecessor");
|
||||
return Fail("value IR entry block has a predecessor");
|
||||
}
|
||||
|
||||
for (size_t block_index = 0; block_index < program.blocks.size(); block_index++) {
|
||||
@@ -189,28 +195,28 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
std::unordered_set<const Block*> predecessors;
|
||||
for (const auto* predecessor: block->ImmPredecessors()) {
|
||||
if (predecessor == nullptr || !blocks.contains(predecessor)) {
|
||||
return Fail(error, "value IR block has a foreign predecessor");
|
||||
return Fail("value IR block has a foreign predecessor");
|
||||
}
|
||||
if (!predecessors.insert(predecessor).second) {
|
||||
return Fail(error, "value IR block predecessor is duplicated");
|
||||
return Fail("value IR block predecessor is duplicated");
|
||||
}
|
||||
if (std::ranges::find(predecessor->ImmSuccessors(), block) ==
|
||||
predecessor->ImmSuccessors().end()) {
|
||||
return Fail(error, "value IR predecessor edge is not reciprocal");
|
||||
return Fail("value IR predecessor edge is not reciprocal");
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<const Block*> successors;
|
||||
for (const auto* successor: block->ImmSuccessors()) {
|
||||
if (successor == nullptr || !blocks.contains(successor)) {
|
||||
return Fail(error, "value IR block has a foreign successor");
|
||||
return Fail("value IR block has a foreign successor");
|
||||
}
|
||||
if (!successors.insert(successor).second) {
|
||||
return Fail(error, "value IR block successor is duplicated");
|
||||
return Fail("value IR block successor is duplicated");
|
||||
}
|
||||
if (std::ranges::find(successor->ImmPredecessors(), block) ==
|
||||
successor->ImmPredecessors().end()) {
|
||||
return Fail(error, "value IR successor edge is not reciprocal");
|
||||
return Fail("value IR successor edge is not reciprocal");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,41 +240,40 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
switch (terminator.kind) {
|
||||
case CFG::TerminatorKind::Branch:
|
||||
if (!add_target(terminator.true_block)) {
|
||||
return Fail(error, "value IR branch target is missing");
|
||||
return Fail("value IR branch target is missing");
|
||||
}
|
||||
break;
|
||||
case CFG::TerminatorKind::ConditionalBranch:
|
||||
if (!add_target(terminator.true_block) || !add_target(terminator.false_block)) {
|
||||
return Fail(error, "value IR conditional branch target is missing");
|
||||
return Fail("value IR conditional branch target is missing");
|
||||
}
|
||||
if (!validate_control_value(program.block_info[block_index].condition, Type::U1)) {
|
||||
return Fail(error, "value IR conditional branch condition is invalid");
|
||||
return Fail("value IR conditional branch condition is invalid");
|
||||
}
|
||||
break;
|
||||
case CFG::TerminatorKind::IndirectBranch: {
|
||||
if (!validate_control_value(program.block_info[block_index].indirect_target,
|
||||
Type::U32)) {
|
||||
return Fail(error, "value IR indirect branch selector is invalid");
|
||||
return Fail("value IR indirect branch selector is invalid");
|
||||
}
|
||||
std::unordered_set<uint32_t> indirect_targets;
|
||||
for (const auto target: terminator.indirect_targets) {
|
||||
if (!indirect_targets.insert(target).second) {
|
||||
return Fail(error, "value IR indirect branch target is duplicated");
|
||||
return Fail("value IR indirect branch target is duplicated");
|
||||
}
|
||||
if (!add_target(target)) {
|
||||
return Fail(error, "value IR indirect branch target is missing");
|
||||
return Fail("value IR indirect branch target is missing");
|
||||
}
|
||||
}
|
||||
if (terminator.indirect_selector_values.size() !=
|
||||
terminator.indirect_selector_targets.size()) {
|
||||
return Fail(error, "value IR indirect selector table is inconsistent");
|
||||
return Fail("value IR indirect selector table is inconsistent");
|
||||
}
|
||||
for (const auto target: terminator.indirect_selector_targets) {
|
||||
const auto found = blocks_by_id.find(target);
|
||||
if (found == blocks_by_id.end() ||
|
||||
!expected_successors.contains(found->second)) {
|
||||
return Fail(error,
|
||||
"value IR indirect selector target is not a CFG successor");
|
||||
return Fail("value IR indirect selector target is not a CFG successor");
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -280,73 +285,73 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
!blocks_by_id.contains(terminator.merge_block)) ||
|
||||
(terminator.continue_block != UINT32_MAX &&
|
||||
!blocks_by_id.contains(terminator.continue_block))) {
|
||||
return Fail(error, "value IR structured control target is missing");
|
||||
return Fail("value IR structured control target is missing");
|
||||
}
|
||||
if (successors != expected_successors) {
|
||||
return Fail(error, "value IR terminator and successor graph disagree");
|
||||
return Fail("value IR terminator and successor graph disagree");
|
||||
}
|
||||
|
||||
bool saw_non_phi = false;
|
||||
for (const auto& inst: *block) {
|
||||
if (inst.Parent() != block) {
|
||||
return Fail(error, "value IR instruction has the wrong parent block");
|
||||
return Fail("value IR instruction has the wrong parent block");
|
||||
}
|
||||
if (inst.GetOpcode() == ValueOpcode::Phi) {
|
||||
if (saw_non_phi) {
|
||||
return Fail(error, "value IR Phi appears after a non-Phi instruction");
|
||||
return Fail("value IR Phi appears after a non-Phi instruction");
|
||||
}
|
||||
if (inst.NumPhiBlocks() != inst.NumArgs()) {
|
||||
return Fail(error, "value IR Phi parent table is inconsistent");
|
||||
return Fail("value IR Phi parent table is inconsistent");
|
||||
}
|
||||
if (inst.NumArgs() == 0) {
|
||||
return Fail(error, "value IR Phi has no incoming values");
|
||||
return Fail("value IR Phi has no incoming values");
|
||||
}
|
||||
std::unordered_set<const Block*> incoming_blocks;
|
||||
for (size_t arg_index = 0; arg_index < inst.NumArgs(); arg_index++) {
|
||||
const auto* predecessor = inst.PhiBlock(arg_index);
|
||||
if (predecessor == nullptr || !blocks.contains(predecessor) ||
|
||||
!predecessors.contains(predecessor)) {
|
||||
return Fail(error, "value IR Phi has a foreign or non-predecessor parent");
|
||||
return Fail("value IR Phi has a foreign or non-predecessor parent");
|
||||
}
|
||||
if (!incoming_blocks.insert(predecessor).second) {
|
||||
return Fail(error, "value IR Phi parent is duplicated");
|
||||
return Fail("value IR Phi parent is duplicated");
|
||||
}
|
||||
if (inst.Arg(arg_index).GetType() != inst.GetType()) {
|
||||
return Fail(error, "value IR Phi incoming type does not match its result");
|
||||
return Fail("value IR Phi incoming type does not match its result");
|
||||
}
|
||||
}
|
||||
if (incoming_blocks != predecessors) {
|
||||
return Fail(error, "value IR Phi does not cover every predecessor");
|
||||
return Fail("value IR Phi does not cover every predecessor");
|
||||
}
|
||||
} else {
|
||||
saw_non_phi = true;
|
||||
}
|
||||
if (require_ssa && IsRegisterStatePseudo(inst.GetOpcode())) {
|
||||
return Fail(error, fmt::format("register-state pseudo {} survived SSA rewrite",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("register-state pseudo {} survived SSA rewrite",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
if (inst.GetOpcode() != ValueOpcode::Phi && inst.GetOpcode() != ValueOpcode::Identity &&
|
||||
inst.GetType() == Type::Opaque) {
|
||||
return Fail(error, fmt::format("untyped opcode {} survived translation",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("untyped opcode {} survived translation",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const bool fixed_signature =
|
||||
inst.GetOpcode() != ValueOpcode::Phi && inst.GetOpcode() != ValueOpcode::Identity;
|
||||
if (fixed_signature && inst.NumArgs() != NumArgsOf(inst.GetOpcode())) {
|
||||
return Fail(error, fmt::format("{} has {} arguments, expected {}",
|
||||
ValueOpcodeName(inst.GetOpcode()), inst.NumArgs(),
|
||||
NumArgsOf(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has {} arguments, expected {}",
|
||||
ValueOpcodeName(inst.GetOpcode()), inst.NumArgs(),
|
||||
NumArgsOf(inst.GetOpcode())));
|
||||
}
|
||||
if (inst.GetOpcode() == ValueOpcode::ReadConstBuffer) {
|
||||
const auto memory_index = inst.Flags<MemoryFlags>().index;
|
||||
if (memory_index >= program.memory_info.size()) {
|
||||
return Fail(error, fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const auto& memory = program.memory_info[memory_index];
|
||||
if (memory.kind != ResourceKind::ScalarBuffer) {
|
||||
return Fail(error, fmt::format("{} has an invalid scalar-memory resource kind",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has an invalid scalar-memory resource kind",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const bool valid_group_width =
|
||||
memory.component_count == 1u || memory.component_count == 2u ||
|
||||
@@ -354,23 +359,23 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
memory.component_count == 16u;
|
||||
if (memory.data_bits != 32u || memory.data_dwords != 1u || !valid_group_width ||
|
||||
memory.component_index >= memory.component_count) {
|
||||
return Fail(error, fmt::format("{} has inconsistent scalar-memory metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has inconsistent scalar-memory metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
}
|
||||
const auto address_info = AddressOpcodeInfoOf(inst.GetOpcode());
|
||||
if (address_info.access != AddressAccess::None) {
|
||||
const auto memory_index = inst.Flags<MemoryFlags>().index;
|
||||
if (memory_index >= program.memory_info.size()) {
|
||||
return Fail(error, fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const auto& memory = program.memory_info[memory_index];
|
||||
if (!IsAddressResourceKind(memory.kind) ||
|
||||
(memory.kind == ResourceKind::ScalarAddress &&
|
||||
inst.GetOpcode() != ValueOpcode::LoadAddressU32)) {
|
||||
return Fail(error, fmt::format("{} has an invalid address resource kind",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has an invalid address resource kind",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const bool scalar_address = memory.kind == ResourceKind::ScalarAddress;
|
||||
const bool valid_group_width =
|
||||
@@ -382,50 +387,50 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
if (memory.data_bits != address_info.data_bits || memory.data_dwords != 1u ||
|
||||
!valid_group_width || memory.component_index >= memory.component_count ||
|
||||
memory.sampler != 0u) {
|
||||
return Fail(error, fmt::format("{} has inconsistent address-memory metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has inconsistent address-memory metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
}
|
||||
const auto buffer_components = BufferComponentCount(inst.GetOpcode());
|
||||
if (buffer_components != 0u) {
|
||||
const auto memory_index = inst.Flags<MemoryFlags>().index;
|
||||
if (memory_index >= program.memory_info.size()) {
|
||||
return Fail(error, fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const auto& memory = program.memory_info[memory_index];
|
||||
if (memory.kind != ResourceKind::Buffer &&
|
||||
memory.kind != ResourceKind::ScalarBuffer) {
|
||||
return Fail(error, fmt::format("{} has a non-buffer resource kind",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has a non-buffer resource kind",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
if (buffer_components > 1u &&
|
||||
(memory.kind != ResourceKind::Buffer || memory.data_bits != 32u ||
|
||||
memory.data_dwords != buffer_components || memory.component_index != 0u)) {
|
||||
return Fail(error, fmt::format("{} has inconsistent native-wide metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has inconsistent native-wide metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
if (buffer_components == 1u &&
|
||||
(inst.GetOpcode() == ValueOpcode::LoadBufferU32 ||
|
||||
inst.GetOpcode() == ValueOpcode::StoreBufferU32) &&
|
||||
memory.data_dwords != 1u) {
|
||||
return Fail(error, fmt::format("{} retains scalar-sibling width metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} retains scalar-sibling width metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
}
|
||||
const auto shared_components = SharedComponentCount(inst.GetOpcode());
|
||||
if (shared_components != 0u) {
|
||||
const auto memory_index = inst.Flags<MemoryFlags>().index;
|
||||
if (memory_index >= program.memory_info.size()) {
|
||||
return Fail(error, fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const auto& memory = program.memory_info[memory_index];
|
||||
if ((memory.kind != ResourceKind::Lds && memory.kind != ResourceKind::Gds) ||
|
||||
memory.resource != 0u || memory.sampler != 0u || memory.component_count == 0u ||
|
||||
memory.component_index >= memory.component_count) {
|
||||
return Fail(error, fmt::format("{} has invalid shared-memory metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has invalid shared-memory metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
uint32_t expected_bits = 32u;
|
||||
if (inst.GetOpcode() == ValueOpcode::LoadSharedU8 ||
|
||||
@@ -437,26 +442,26 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
}
|
||||
if (memory.data_bits != expected_bits || memory.data_dwords != shared_components ||
|
||||
(shared_components > 1u && memory.component_index != 0u)) {
|
||||
return Fail(error, fmt::format("{} has inconsistent shared-memory width",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has inconsistent shared-memory width",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
}
|
||||
const auto image_info = ImageOpcodeInfoOf(inst.GetOpcode());
|
||||
if (image_info.access != ImageAccess::None) {
|
||||
const auto memory_index = inst.Flags<MemoryFlags>().index;
|
||||
if (memory_index >= program.memory_info.size()) {
|
||||
return Fail(error, fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const auto& memory = program.memory_info[memory_index];
|
||||
if (!ImageResourceKindMatches(memory.kind, image_info.resource_class)) {
|
||||
return Fail(error, fmt::format("{} has invalid image-memory metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has invalid image-memory metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
}
|
||||
if (inst.GetOpcode() == ValueOpcode::SetAttribute &&
|
||||
inst.Flags<ExportFlags>().index >= program.export_info.size()) {
|
||||
return Fail(error, "SetAttribute has an invalid export-info index");
|
||||
return Fail("SetAttribute has an invalid export-info index");
|
||||
}
|
||||
uint32_t composite_components = 0u;
|
||||
switch (inst.GetOpcode()) {
|
||||
@@ -469,25 +474,24 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
if (composite_components != 0u &&
|
||||
(!inst.Arg(1).IsImmediate() || inst.Arg(1).GetType() != Type::U32 ||
|
||||
inst.Arg(1).U32() >= composite_components)) {
|
||||
return Fail(error, fmt::format("{} has an invalid component index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(fmt::format("{} has an invalid component index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
for (size_t arg_index = 0; arg_index < inst.NumArgs(); arg_index++) {
|
||||
const auto arg = inst.Arg(arg_index);
|
||||
if (arg.IsEmpty()) {
|
||||
return Fail(error, fmt::format("{} has an empty argument",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return Fail(
|
||||
fmt::format("{} has an empty argument", ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
if (fixed_signature && arg.GetType() != ArgTypeOf(inst.GetOpcode(), arg_index)) {
|
||||
return Fail(error,
|
||||
fmt::format("{} argument {} has type {}, expected {}",
|
||||
return Fail(fmt::format("{} argument {} has type {}, expected {}",
|
||||
ValueOpcodeName(inst.GetOpcode()), arg_index,
|
||||
TypeName(arg.GetType()),
|
||||
TypeName(ArgTypeOf(inst.GetOpcode(), arg_index))));
|
||||
}
|
||||
if (const auto* definition = arg.TryInstruction();
|
||||
definition != nullptr && !instructions.contains(definition)) {
|
||||
return Fail(error, "value IR argument has a foreign definition");
|
||||
return Fail("value IR argument has a foreign definition");
|
||||
}
|
||||
if (const auto* definition = arg.TryInstruction(); definition != nullptr) {
|
||||
const auto& uses = definition->Uses();
|
||||
@@ -495,8 +499,7 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
return candidate.user == &inst && candidate.operand == arg_index;
|
||||
});
|
||||
if (use == uses.end()) {
|
||||
return Fail(error,
|
||||
fmt::format("{} argument {} is absent from {} reverse uses",
|
||||
return Fail(fmt::format("{} argument {} is absent from {} reverse uses",
|
||||
ValueOpcodeName(inst.GetOpcode()), arg_index,
|
||||
ValueOpcodeName(definition->GetOpcode())));
|
||||
}
|
||||
@@ -506,7 +509,7 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
}
|
||||
|
||||
if (program.blocks.empty()) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<bool> reachable(program.blocks.size(), false);
|
||||
@@ -523,7 +526,7 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
}
|
||||
}
|
||||
if (!std::ranges::all_of(reachable, [](bool value) { return value; })) {
|
||||
return Fail(error, "value IR contains an unreachable block");
|
||||
return Fail("value IR contains an unreachable block");
|
||||
}
|
||||
|
||||
std::vector<std::vector<bool>> dominators(program.blocks.size(),
|
||||
@@ -577,28 +580,25 @@ bool ValidateProgram(const Program& program, bool require_ssa, std::string* erro
|
||||
const auto* predecessor = inst.PhiBlock(arg_index);
|
||||
if (definition->Parent() != predecessor &&
|
||||
!dominates(definition->Parent(), predecessor)) {
|
||||
return Fail(error,
|
||||
"value IR Phi incoming definition does not dominate its edge");
|
||||
return Fail("value IR Phi incoming definition does not dominate its edge");
|
||||
}
|
||||
} else if (definition->Parent() == block) {
|
||||
if (instruction_positions.at(definition) >= instruction_positions.at(&inst)) {
|
||||
return Fail(error,
|
||||
"value IR instruction uses a same-block definition before it");
|
||||
return Fail("value IR instruction uses a same-block definition before it");
|
||||
}
|
||||
} else if (!dominates(definition->Parent(), block)) {
|
||||
return Fail(error, "value IR instruction definition does not dominate its use");
|
||||
return Fail("value IR instruction definition does not dominate its use");
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto& info = program.block_info[block_index];
|
||||
if (!info.condition.IsEmpty() && !control_dominates(info.condition, block)) {
|
||||
return Fail(error, "value IR branch condition definition does not dominate its use");
|
||||
return Fail("value IR branch condition definition does not dominate its use");
|
||||
}
|
||||
if (!info.indirect_target.IsEmpty() && !control_dominates(info.indirect_target, block)) {
|
||||
return Fail(error, "value IR indirect selector definition does not dominate its use");
|
||||
return Fail("value IR indirect selector definition does not dominate its use");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResolveControlFlowIdentities(Program& program) {
|
||||
|
||||
@@ -477,7 +477,6 @@ struct DescriptorSource {
|
||||
struct SrtRead {
|
||||
Value value;
|
||||
uint32_t flat_offset = 0;
|
||||
uint32_t use_pc = 0;
|
||||
|
||||
bool operator==(const SrtRead& other) const = default;
|
||||
};
|
||||
@@ -523,7 +522,7 @@ struct Program {
|
||||
|
||||
std::string ProgramToString(const Program& program);
|
||||
|
||||
bool ValidateProgram(const Program& program, bool require_ssa, std::string* error);
|
||||
void ValidateProgram(const Program& program, bool require_ssa);
|
||||
void ResolveControlFlowIdentities(Program& program);
|
||||
bool EquivalentValue(const Program& program, Value left, Value right);
|
||||
Value ResolveInvariantPhi(const Program& program, Value value);
|
||||
|
||||
@@ -23,12 +23,16 @@ VALUE_OPCODE(GetScc, U1)
|
||||
VALUE_OPCODE(SetScc, Void, U1)
|
||||
VALUE_OPCODE(GetExec, U1)
|
||||
VALUE_OPCODE(SetExec, Void, U1)
|
||||
VALUE_OPCODE(GetExecMaskTag, U1)
|
||||
VALUE_OPCODE(SetExecMaskTag, Void, U1)
|
||||
VALUE_OPCODE(GetExecLo, U32)
|
||||
VALUE_OPCODE(SetExecLo, Void, U32)
|
||||
VALUE_OPCODE(GetExecHi, U32)
|
||||
VALUE_OPCODE(SetExecHi, Void, U32)
|
||||
VALUE_OPCODE(GetVcc, U1)
|
||||
VALUE_OPCODE(SetVcc, Void, U1)
|
||||
VALUE_OPCODE(GetVccMaskValidTag, U1)
|
||||
VALUE_OPCODE(SetVccMaskValidTag, Void, U1)
|
||||
VALUE_OPCODE(GetVccLo, U32)
|
||||
VALUE_OPCODE(SetVccLo, Void, U32)
|
||||
VALUE_OPCODE(GetVccHi, U32)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/BindingLayout.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -9,12 +10,17 @@
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
namespace {
|
||||
|
||||
constexpr auto FirstImageBinding = DescriptorBindingKind::Sampled1D;
|
||||
constexpr auto FirstImageBinding = DescriptorBindingKind::Sampled1D;
|
||||
constexpr uint32_t ImageBindingCount =
|
||||
static_cast<uint32_t>(DescriptorBindingKind::StorageAtomic3D) -
|
||||
static_cast<uint32_t>(FirstImageBinding) + 1u;
|
||||
|
||||
bool CollectUserData(const Program& program, std::vector<uint32_t>& result) {
|
||||
[[noreturn]] void BindingFail(const char* message) {
|
||||
EXIT("shader binding layout failed: %s", message);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
std::vector<uint32_t> CollectUserData(const Program& program) {
|
||||
std::array<bool, NumScalarRegs> registers {};
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
@@ -22,22 +28,22 @@ bool CollectUserData(const Program& program, std::vector<uint32_t>& result) {
|
||||
continue;
|
||||
}
|
||||
if (inst.Arg(0).GetType() != Type::ScalarReg) {
|
||||
return false;
|
||||
BindingFail("typed shader contains an invalid user-data register");
|
||||
}
|
||||
const auto index = RegIndex(inst.Arg(0).ScalarRegister());
|
||||
if (index >= NumScalarRegs) {
|
||||
return false;
|
||||
BindingFail("typed shader contains an invalid user-data register");
|
||||
}
|
||||
registers[index] = true;
|
||||
}
|
||||
}
|
||||
result.clear();
|
||||
std::vector<uint32_t> result;
|
||||
for (uint32_t index = 0; index < registers.size(); index++) {
|
||||
if (registers[index]) {
|
||||
result.push_back(index);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddBinding(BindingLayout& layout, DescriptorBindingKind kind,
|
||||
@@ -45,8 +51,8 @@ void AddBinding(BindingLayout& layout, DescriptorBindingKind kind,
|
||||
layout.descriptors.push_back({kind, std::move(resources)});
|
||||
}
|
||||
|
||||
bool UsesGds(const Program& program, bool& uses_gds) {
|
||||
uses_gds = false;
|
||||
bool UsesGds(const Program& program) {
|
||||
bool uses_gds = false;
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (SharedAccessOf(inst.GetOpcode()) == SharedAccess::None) {
|
||||
@@ -54,44 +60,36 @@ bool UsesGds(const Program& program, bool& uses_gds) {
|
||||
}
|
||||
const auto index = inst.Flags<MemoryFlags>().index;
|
||||
if (index >= program.memory_info.size()) {
|
||||
return false;
|
||||
BindingFail("typed shader contains invalid shared-memory metadata");
|
||||
}
|
||||
const auto kind = program.memory_info[index].kind;
|
||||
if (kind != ResourceKind::Lds && kind != ResourceKind::Gds) {
|
||||
return false;
|
||||
BindingFail("typed shader contains invalid shared-memory metadata");
|
||||
}
|
||||
uses_gds |= kind == ResourceKind::Gds;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return uses_gds;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::string* error) {
|
||||
void AllocateBindings(Program& program, uint32_t push_constant_offset) {
|
||||
if (!program.shader_info_complete || program.binding_layout_complete) {
|
||||
if (error != nullptr) {
|
||||
*error = !program.shader_info_complete ? "shader info is not ready"
|
||||
: "binding layout already allocated";
|
||||
}
|
||||
return false;
|
||||
EXIT("shader binding layout failed: %s", !program.shader_info_complete
|
||||
? "shader info is not ready"
|
||||
: "binding layout already allocated");
|
||||
}
|
||||
if (push_constant_offset > NativePushConstantSize ||
|
||||
push_constant_offset % sizeof(uint32_t) != 0) {
|
||||
if (error != nullptr) {
|
||||
*error = "push-constant offset exceeds the Vulkan minimum guarantee or is unaligned";
|
||||
}
|
||||
return false;
|
||||
EXIT("shader binding layout failed: push-constant offset %u exceeds the Vulkan minimum "
|
||||
"guarantee or is unaligned",
|
||||
push_constant_offset);
|
||||
}
|
||||
|
||||
BindingLayout next;
|
||||
next.push_constant_offset = push_constant_offset;
|
||||
if (!CollectUserData(program, next.user_data_registers)) {
|
||||
if (error != nullptr) {
|
||||
*error = "typed shader contains an invalid user-data register";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
next.user_data_registers = CollectUserData(program);
|
||||
next.memory_offset_dword = static_cast<uint32_t>(next.user_data_registers.size());
|
||||
next.memory_offset_count = static_cast<uint32_t>(program.info.buffers.size());
|
||||
|
||||
@@ -107,26 +105,18 @@ bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::stri
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
const auto kind = DescriptorBindingForImage(program.info.images[i]);
|
||||
if (!kind.has_value()) {
|
||||
if (error != nullptr) {
|
||||
*error = "shader info contains an invalid image binding class";
|
||||
}
|
||||
return false;
|
||||
EXIT("shader binding layout failed: image %u has an invalid binding class", i);
|
||||
}
|
||||
const auto group = static_cast<uint32_t>(*kind) - static_cast<uint32_t>(FirstImageBinding);
|
||||
if (group >= image_groups.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = "shader info contains an unmapped image binding class";
|
||||
}
|
||||
return false;
|
||||
EXIT("shader binding layout failed: image %u has an unmapped binding class", i);
|
||||
}
|
||||
auto& resources = image_groups[group];
|
||||
const auto dynamic = program.info.images[i].mip_mode == ImageMipMode::DynamicStorage;
|
||||
const auto count = dynamic ? program.info.images[i].mip_count : 1u;
|
||||
if (count == 0u || (!dynamic && program.info.images[i].mip_count != 1u)) {
|
||||
if (error != nullptr) {
|
||||
*error = "image has an invalid specialized mip descriptor count";
|
||||
}
|
||||
return false;
|
||||
EXIT("shader binding layout failed: image %u has invalid specialized mip count %u", i,
|
||||
program.info.images[i].mip_count);
|
||||
}
|
||||
resources.insert(resources.end(), count, i);
|
||||
}
|
||||
@@ -146,14 +136,7 @@ bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::stri
|
||||
}
|
||||
AddBinding(next, DescriptorBindingKind::Samplers, std::move(resources));
|
||||
}
|
||||
bool uses_gds = false;
|
||||
if (!UsesGds(program, uses_gds)) {
|
||||
if (error != nullptr) {
|
||||
*error = "typed shader contains invalid shared-memory metadata";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (uses_gds) {
|
||||
if (UsesGds(program)) {
|
||||
AddBinding(next, DescriptorBindingKind::Gds);
|
||||
}
|
||||
if (program.info.uses_dma) {
|
||||
@@ -178,7 +161,6 @@ bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::stri
|
||||
|
||||
program.bindings = std::move(next);
|
||||
program.binding_layout_complete = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
const DescriptorBinding* FindBinding(const BindingLayout& layout, DescriptorBindingKind kind) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
bool AllocateBindings(Program& program, uint32_t push_constant_offset, std::string* error);
|
||||
void AllocateBindings(Program& program, uint32_t push_constant_offset);
|
||||
|
||||
const DescriptorBinding* FindBinding(const BindingLayout& layout, DescriptorBindingKind kind);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/shaderBindings.h"
|
||||
@@ -18,6 +19,11 @@ namespace {
|
||||
constexpr uint64_t AddressMask = 0x0000ffffffffffffull;
|
||||
constexpr uint64_t MaxIndirectImageProbes = 65536u;
|
||||
|
||||
[[noreturn]] void SpecializationFail(std::string_view message) {
|
||||
EXIT("shader resource specialization failed: %s", std::string(message).c_str());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
Decoder::ImageDimension DescriptorDimension(const DescriptorValue& descriptor,
|
||||
Decoder::ImageDimension requested) {
|
||||
const bool is_array = requested == Decoder::ImageDimension::Dim1DArray ||
|
||||
@@ -55,7 +61,8 @@ bool NullImageDescriptor(const DescriptorValue& descriptor) {
|
||||
bool ValidImageDescriptor(const DescriptorValue& descriptor, bool r128 = false) {
|
||||
const auto type = static_cast<Prospero::ImageType>((descriptor.dwords[3] >> 28u) & 0xfu);
|
||||
const auto format = static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
||||
if (type < Prospero::ImageType::kColor1D || format == Prospero::BufferFormat::kInvalid) {
|
||||
if (type < Prospero::ImageType::kColor1D || format == Prospero::BufferFormat::kInvalid ||
|
||||
format > Prospero::BufferFormat::kBc7Srgb) {
|
||||
return false;
|
||||
}
|
||||
if (r128 && type != Prospero::ImageType::kColor1D && type != Prospero::ImageType::kColor2D &&
|
||||
@@ -79,7 +86,7 @@ uint32_t DescriptorImageSwizzle(const DescriptorValue& descriptor) {
|
||||
|
||||
Prospero::BufferFormat ImageConversionFormat(Prospero::BufferFormat format) {
|
||||
return Prospero::RemapTextureFormat(format) != format ? format
|
||||
: Prospero::BufferFormat::kInvalid;
|
||||
: Prospero::BufferFormat::kInvalid;
|
||||
}
|
||||
|
||||
bool DescriptorIsCube(const DescriptorValue& descriptor) {
|
||||
@@ -153,8 +160,7 @@ bool ReadSpecializationWord(const SrtRuntime& runtime, uint64_t address, uint32_
|
||||
}
|
||||
|
||||
bool ReadScalarBufferWord(const ShaderBufferResource& descriptor, uint32_t dynamic_offset,
|
||||
uint32_t immediate_offset, const SrtRuntime& runtime, uint32_t& word,
|
||||
std::string* error, uint32_t use_pc) {
|
||||
uint32_t immediate_offset, const SrtRuntime& runtime, uint32_t& word) {
|
||||
const auto byte_offset = static_cast<uint64_t>(dynamic_offset) + immediate_offset;
|
||||
const auto aligned = byte_offset & ~uint64_t {3};
|
||||
const auto size = ScalarBufferSize(descriptor);
|
||||
@@ -164,20 +170,10 @@ bool ReadScalarBufferWord(const ShaderBufferResource& descriptor, uint32_t dynam
|
||||
}
|
||||
const auto base = descriptor.Base48() & ~uint64_t {3};
|
||||
if (aligned > AddressMask - base) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("indirect image scalar read at pc 0x{:08x} exceeds the "
|
||||
"48-bit address space",
|
||||
use_pc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const auto address = base + aligned;
|
||||
if (!ReadSpecializationWord(runtime, address, word)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("indirect image scalar read at pc 0x{:08x} failed at "
|
||||
"0x{:016x}",
|
||||
use_pc, address);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -186,23 +182,14 @@ bool ReadScalarBufferWord(const ShaderBufferResource& descriptor, uint32_t dynam
|
||||
bool MaterializeIndirectImage(const DescriptorSource::IndirectImage& indirect,
|
||||
const DescriptorValue& material_value,
|
||||
const DescriptorValue& heap_value, const SrtRuntime& runtime,
|
||||
ResourceSnapshot::IndirectImage& result, std::string* error,
|
||||
uint32_t use_pc) {
|
||||
ResourceSnapshot::IndirectImage& result) {
|
||||
ShaderBufferResource material;
|
||||
ShaderBufferResource heap;
|
||||
if (!DecodeBufferDescriptor(material_value, material) ||
|
||||
!DecodeBufferDescriptor(heap_value, heap)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("indirect image tables at pc 0x{:08x} have invalid descriptors",
|
||||
use_pc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (material.Stride() != indirect.selector_stride) {
|
||||
if (error != nullptr) {
|
||||
*error =
|
||||
fmt::format("indirect image material table at pc 0x{:08x} changed stride", use_pc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -215,11 +202,6 @@ bool MaterializeIndirectImage(const DescriptorSource::IndirectImage& indirect,
|
||||
const auto limit = std::min<uint64_t>(UINT32_MAX, size + 3u);
|
||||
const auto probe_count = residue <= limit ? (limit - residue) / step + 1u : 0u;
|
||||
if (probe_count > MaxIndirectImageProbes) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("indirect image material table at pc 0x{:08x} requires {} "
|
||||
"bounded probes",
|
||||
use_pc, probe_count);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -227,8 +209,7 @@ bool MaterializeIndirectImage(const DescriptorSource::IndirectImage& indirect,
|
||||
std::unordered_set<uint32_t> seen {0u};
|
||||
for (uint64_t offset = residue; offset <= limit && probe_count != 0u; offset += step) {
|
||||
uint32_t key = 0;
|
||||
if (!ReadScalarBufferWord(material, static_cast<uint32_t>(offset), 0u, runtime, key, error,
|
||||
use_pc)) {
|
||||
if (!ReadScalarBufferWord(material, static_cast<uint32_t>(offset), 0u, runtime, key)) {
|
||||
return false;
|
||||
}
|
||||
if (seen.insert(key).second) {
|
||||
@@ -248,7 +229,7 @@ bool MaterializeIndirectImage(const DescriptorSource::IndirectImage& indirect,
|
||||
const auto heap_offset = key << 5u;
|
||||
for (uint32_t dword = 0; dword < candidate.dword_count; dword++) {
|
||||
if (!ReadScalarBufferWord(heap, heap_offset, dword * sizeof(uint32_t), runtime,
|
||||
candidate.dwords[dword], error, use_pc)) {
|
||||
candidate.dwords[dword])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -281,35 +262,22 @@ size_t FlattenedRuntimeDwords(const Program& program) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& snapshot,
|
||||
std::string* error) {
|
||||
bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& snapshot) {
|
||||
if (!program.resource_tracking_complete) {
|
||||
if (error != nullptr) {
|
||||
*error = "shader resources were not tracked";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (snapshot.buffers.size() != program.info.buffers.size() ||
|
||||
snapshot.images.size() != program.info.images.size() ||
|
||||
snapshot.samplers.size() != program.info.samplers.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = "resource snapshot does not match dense shader topology";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (snapshot.flattened_srt.size() != FlattenedRuntimeDwords(program)) {
|
||||
if (error != nullptr) {
|
||||
*error = "flattened SRT snapshot does not match the shader plan";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (program.binding_layout_complete) {
|
||||
for (const auto reg: program.bindings.user_data_registers) {
|
||||
if (reg < program.user_data_base ||
|
||||
reg - program.user_data_base >= snapshot.user_data.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("runtime snapshot is missing user SGPR {}", reg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -317,19 +285,12 @@ bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& sn
|
||||
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
||||
const auto alias = program.info.buffers[i].image_alias;
|
||||
if (alias != BufferResource::NoImageAlias && alias >= program.info.images.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("buffer resource {} has invalid image alias {}", i, alias);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const auto CheckWidth = [&](const auto& values, uint32_t width, const char* kind) {
|
||||
const auto CheckWidth = [&](const auto& values, uint32_t width) {
|
||||
for (uint32_t i = 0; i < values.size(); i++) {
|
||||
if (values[i].dword_count != width) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("{} descriptor {} has {} dwords", kind, i,
|
||||
values[i].dword_count);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -345,56 +306,37 @@ bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& sn
|
||||
table.keys.empty() || table.keys.size() != table.candidates.size() ||
|
||||
table.capacity < table.keys.size() || table.descriptors.empty() ||
|
||||
!indirect_resources.insert(table.resource).second) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image snapshot does not match the shader plan";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
for (const auto candidate: table.candidates) {
|
||||
if (candidate >= table.descriptors.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image snapshot has an invalid candidate";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (snapshot.images[table.resource] != table.descriptors[table.candidates[0]]) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image snapshot has an inconsistent root descriptor";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return CheckWidth(snapshot.buffers, 4, "buffer") && CheckWidth(snapshot.images, 8, "image") &&
|
||||
CheckWidth(snapshot.samplers, 4, "sampler");
|
||||
return CheckWidth(snapshot.buffers, 4) && CheckWidth(snapshot.images, 8) &&
|
||||
CheckWidth(snapshot.samplers, 4);
|
||||
}
|
||||
|
||||
bool ValidateResourceSpecialization(const Program& program, const ResourceSnapshot& snapshot,
|
||||
std::string* error) {
|
||||
if (!ValidateResourceSnapshot(program, snapshot, error)) {
|
||||
bool ValidateResourceSpecialization(const Program& program, const ResourceSnapshot& snapshot) {
|
||||
if (!ValidateResourceSnapshot(program, snapshot)) {
|
||||
return false;
|
||||
}
|
||||
if (!snapshot.indirect_images.empty()) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image snapshot was not specialized";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
||||
const auto& buffer = program.info.buffers[i];
|
||||
ShaderBufferResource descriptor;
|
||||
if (!DecodeBufferDescriptor(snapshot.buffers[i], descriptor)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("buffer descriptor {} has invalid width", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (buffer.packed_stride != descriptor.PackedStride() ||
|
||||
buffer.descriptor_format != descriptor.Format() ||
|
||||
buffer.descriptor_swizzle != descriptor.DstSelXYZW()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("buffer descriptor {} no longer matches specialization", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -403,9 +345,6 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
const auto& descriptor = snapshot.images[i];
|
||||
const auto mip_count = StorageMipCount(image, descriptor);
|
||||
if (mip_count == 0u || mip_count != image.mip_count) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("storage image descriptor {} changed mip range", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (NullImageDescriptor(descriptor)) {
|
||||
@@ -419,10 +358,6 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
program.info.images[root].conversion_format != image.conversion_format ||
|
||||
program.info.images[root].shader_swizzle != image.shader_swizzle ||
|
||||
program.info.images[root].cube != image.cube) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"null indirect image descriptor {} changed binding class", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@@ -434,10 +369,6 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
}
|
||||
if (image.dimension != Decoder::ImageDimension::Dim2D || image.cube ||
|
||||
!canonical_kind) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"image descriptor {} no longer matches canonical null specialization", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@@ -445,14 +376,6 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
const auto dimension = DescriptorDimension(descriptor, image.dimension);
|
||||
if (dimension == Decoder::ImageDimension::Unknown || dimension != image.dimension ||
|
||||
DescriptorIsCube(descriptor) != image.cube) {
|
||||
if (error != nullptr) {
|
||||
*error =
|
||||
fmt::format("image descriptor {} no longer matches specialized dimension: "
|
||||
"{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x}",
|
||||
i, descriptor.dwords[0], descriptor.dwords[1], descriptor.dwords[2],
|
||||
descriptor.dwords[3], descriptor.dwords[4], descriptor.dwords[5],
|
||||
descriptor.dwords[6], descriptor.dwords[7]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (image.kind == ResourceKind::Image || image.kind == ResourceKind::ImageUint ||
|
||||
@@ -463,49 +386,25 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
const auto format =
|
||||
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
||||
if (image.atomic && format != Prospero::BufferFormat::k32UInt) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"atomic image descriptor {} changed to unsupported format {}", i,
|
||||
static_cast<uint32_t>(format));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const auto conversion_format = ImageConversionFormat(format);
|
||||
if (image.conversion_format != conversion_format) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("image descriptor {} changed format conversion", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ((storage || conversion_format != Prospero::BufferFormat::kInvalid) &&
|
||||
image.shader_swizzle != DescriptorImageSwizzle(descriptor)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("image descriptor {} changed swizzle", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const bool raw_sint_storage =
|
||||
storage && format == Prospero::BufferFormat::k32SInt && image.written &&
|
||||
!image.read && !image.atomic;
|
||||
const bool uint_descriptor =
|
||||
Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt &&
|
||||
image.written && !image.read && !image.atomic;
|
||||
const bool uint_descriptor = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
const auto uint_program = image.kind == ResourceKind::ImageUint ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
if (uint_descriptor != uint_program && !(image.atomic && uint_program)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"image descriptor {} no longer matches specialized format: "
|
||||
"{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x},{:08x}",
|
||||
i, descriptor.dwords[0], descriptor.dwords[1], descriptor.dwords[2],
|
||||
descriptor.dwords[3], descriptor.dwords[4], descriptor.dwords[5],
|
||||
descriptor.dwords[6], descriptor.dwords[7]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (image.depth_compare && uint_program) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("integer image descriptor {} uses depth comparison", i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -514,11 +413,8 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh
|
||||
}
|
||||
|
||||
bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
|
||||
ResourceSnapshot& snapshot, std::string* error) {
|
||||
ResourceSnapshot& snapshot) {
|
||||
if (!program.resource_tracking_complete) {
|
||||
if (error != nullptr) {
|
||||
*error = "shader resources were not tracked";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -527,17 +423,12 @@ bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
|
||||
requests.reserve(program.info.buffers.size() + program.info.images.size() * 2u +
|
||||
program.info.samplers.size());
|
||||
for (const auto& buffer: program.info.buffers) {
|
||||
requests.push_back({buffer.source, buffer.first_use_pc});
|
||||
requests.push_back({buffer.source});
|
||||
}
|
||||
for (const auto& image: program.info.images) {
|
||||
const auto* source = Source(program, image.source);
|
||||
if (source != nullptr && source->indirect_image.has_value()) {
|
||||
if (runtime.read_specialization_memory == nullptr) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("indirect image table at pc 0x{:08x} has no clean "
|
||||
"memory reader",
|
||||
image.first_use_pc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
MarkCleanFlatSlots(program, Source(program, source->indirect_image->material_source),
|
||||
@@ -545,16 +436,16 @@ bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
|
||||
MarkCleanFlatSlots(program, Source(program, source->indirect_image->heap_source),
|
||||
clean_flat_slots);
|
||||
} else {
|
||||
requests.push_back({image.source, image.first_use_pc});
|
||||
requests.push_back({image.source});
|
||||
}
|
||||
}
|
||||
for (const auto& sampler: program.info.samplers) {
|
||||
requests.push_back({sampler.source, sampler.first_use_pc});
|
||||
requests.push_back({sampler.source});
|
||||
}
|
||||
std::vector<DescriptorValue> values;
|
||||
std::vector<uint32_t> flattened_srt;
|
||||
if (!EvaluateRuntimeSources(program, requests, runtime, values, flattened_srt, clean_flat_slots,
|
||||
error)) {
|
||||
if (!EvaluateRuntimeSources(program, requests, runtime, values, flattened_srt,
|
||||
clean_flat_slots)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -581,20 +472,19 @@ bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
|
||||
continue;
|
||||
}
|
||||
const std::array requests {
|
||||
DescriptorSourceRequest {source->indirect_image->material_source,
|
||||
image.first_use_pc},
|
||||
DescriptorSourceRequest {source->indirect_image->heap_source, image.first_use_pc}};
|
||||
DescriptorSourceRequest {source->indirect_image->material_source},
|
||||
DescriptorSourceRequest {source->indirect_image->heap_source}};
|
||||
SrtRuntime clean_runtime = runtime;
|
||||
clean_runtime.read_memory = runtime.read_specialization_memory;
|
||||
std::vector<DescriptorValue> tables;
|
||||
if (!EvaluateDescriptorSources(program, requests, clean_runtime, tables, error)) {
|
||||
if (!EvaluateDescriptorSources(program, requests, clean_runtime, tables)) {
|
||||
return false;
|
||||
}
|
||||
const auto& material = tables[0];
|
||||
const auto& heap = tables[1];
|
||||
ResourceSnapshot::IndirectImage table;
|
||||
if (!MaterializeIndirectImage(*source->indirect_image, material, heap, runtime, table,
|
||||
error, image.first_use_pc)) {
|
||||
if (!MaterializeIndirectImage(*source->indirect_image, material, heap, runtime,
|
||||
table)) {
|
||||
return false;
|
||||
}
|
||||
if (image.indirect_root == ImageResource::NoIndirectImage) {
|
||||
@@ -608,20 +498,12 @@ bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
|
||||
}
|
||||
if (table.keys.size() > image.indirect_mapping_capacity ||
|
||||
table.descriptors.size() > image.indirect_resources.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"indirect image table at pc 0x{:08x} changed candidate topology",
|
||||
image.first_use_pc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
for (uint32_t candidate = 0; candidate < image.indirect_resources.size(); candidate++) {
|
||||
const auto resource = image.indirect_resources[candidate];
|
||||
if (resource >= program.info.images.size() ||
|
||||
program.info.images[resource].indirect_root != image_index) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image specialization has an invalid candidate resource";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
next.images[resource] =
|
||||
@@ -649,31 +531,26 @@ bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
|
||||
}
|
||||
}
|
||||
if (std::ranges::find(image_written, uint8_t {0}) != image_written.end()) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image specialization left an unmaterialized candidate";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
next.samplers.assign(cursor, cursor + program.info.samplers.size());
|
||||
next.user_data.assign(runtime.user_data.begin(), runtime.user_data.end());
|
||||
if (!ValidateResourceSnapshot(program, next, error)) {
|
||||
if (!ValidateResourceSnapshot(program, next)) {
|
||||
return false;
|
||||
}
|
||||
snapshot = std::move(next);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::string* error) {
|
||||
void SpecializeResources(Program& program, ResourceSnapshot& snapshot) {
|
||||
if (!program.resource_tracking_complete || program.shader_info_complete ||
|
||||
program.binding_layout_complete) {
|
||||
if (error != nullptr) {
|
||||
*error = !program.resource_tracking_complete ? "shader resources were not tracked"
|
||||
: "resource specialization is too late";
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(!program.resource_tracking_complete
|
||||
? "shader resources were not tracked"
|
||||
: "resource specialization is too late");
|
||||
}
|
||||
if (!ValidateResourceSnapshot(program, snapshot, error)) {
|
||||
return false;
|
||||
if (!ValidateResourceSnapshot(program, snapshot)) {
|
||||
return SpecializationFail("invalid resource snapshot");
|
||||
}
|
||||
|
||||
auto next = program.info;
|
||||
@@ -681,10 +558,8 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
for (const auto& table: snapshot.indirect_images) {
|
||||
if (table.resource >= next.images.size() || table.descriptors.size() < 2u ||
|
||||
next.images.size() + table.descriptors.size() - 1u > ShaderInfo::MaxImages) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image candidates exceed the dense image resource limit";
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(
|
||||
"indirect image candidates exceed the dense image resource limit");
|
||||
}
|
||||
const auto root_image = next.images[table.resource];
|
||||
std::vector<uint32_t> resources(table.descriptors.size());
|
||||
@@ -720,10 +595,7 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
for (uint32_t i = 0; i < next.buffers.size(); i++) {
|
||||
ShaderBufferResource descriptor;
|
||||
if (!DecodeBufferDescriptor(next_snapshot.buffers[i], descriptor)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("buffer descriptor {} has invalid width", i);
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(fmt::format("buffer descriptor {} has invalid width", i));
|
||||
}
|
||||
next.buffers[i].packed_stride = descriptor.PackedStride();
|
||||
next.buffers[i].descriptor_format = descriptor.Format();
|
||||
@@ -734,10 +606,8 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
auto& image = next.images[i];
|
||||
image.mip_count = StorageMipCount(image, descriptor);
|
||||
if (image.mip_count == 0u) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("storage image descriptor {} has an invalid mip range", i);
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(
|
||||
fmt::format("storage image descriptor {} has an invalid mip range", i));
|
||||
}
|
||||
if (NullImageDescriptor(descriptor)) {
|
||||
image.dimension = Decoder::ImageDimension::Dim2D;
|
||||
@@ -755,37 +625,31 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
}
|
||||
const auto descriptor_dimension = DescriptorDimension(descriptor, image.dimension);
|
||||
if (descriptor_dimension == Decoder::ImageDimension::Unknown) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"image descriptor {} has unsupported type {}: {:08x},{:08x},{:08x},{:08x},"
|
||||
"{:08x},{:08x},{:08x},{:08x}",
|
||||
i, (descriptor.dwords[3] >> 28u) & 0xfu, descriptor.dwords[0],
|
||||
descriptor.dwords[1], descriptor.dwords[2], descriptor.dwords[3],
|
||||
descriptor.dwords[4], descriptor.dwords[5], descriptor.dwords[6],
|
||||
descriptor.dwords[7]);
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(fmt::format(
|
||||
"image descriptor {} has unsupported type {}: {:08x},{:08x},{:08x},{:08x},"
|
||||
"{:08x},{:08x},{:08x},{:08x}",
|
||||
i, (descriptor.dwords[3] >> 28u) & 0xfu, descriptor.dwords[0], descriptor.dwords[1],
|
||||
descriptor.dwords[2], descriptor.dwords[3], descriptor.dwords[4],
|
||||
descriptor.dwords[5], descriptor.dwords[6], descriptor.dwords[7]));
|
||||
}
|
||||
image.dimension = descriptor_dimension;
|
||||
image.cube = DescriptorIsCube(descriptor);
|
||||
const auto format =
|
||||
static_cast<Prospero::BufferFormat>((descriptor.dwords[1] >> 20u) & 0x1ffu);
|
||||
if (image.atomic && format != Prospero::BufferFormat::k32UInt) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("atomic image descriptor {} uses unsupported format {}", i,
|
||||
static_cast<uint32_t>(format));
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(
|
||||
fmt::format("atomic image descriptor {} uses unsupported format {}", i,
|
||||
static_cast<uint32_t>(format)));
|
||||
}
|
||||
const bool storage = image.kind == ResourceKind::StorageImage ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
const bool storage = image.kind == ResourceKind::StorageImage ||
|
||||
image.kind == ResourceKind::StorageImageUint;
|
||||
image.conversion_format = ImageConversionFormat(format);
|
||||
if (storage || image.conversion_format != Prospero::BufferFormat::kInvalid) {
|
||||
image.shader_swizzle = DescriptorImageSwizzle(descriptor);
|
||||
}
|
||||
const bool raw_sint_storage = storage && format == Prospero::BufferFormat::k32SInt &&
|
||||
image.written && !image.read && !image.atomic;
|
||||
const bool uint_image = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
const bool uint_image = Prospero::IsUintTextureFormat(format) || raw_sint_storage;
|
||||
if (uint_image) {
|
||||
switch (image.kind) {
|
||||
case ResourceKind::Image: image.kind = ResourceKind::ImageUint; break;
|
||||
@@ -803,19 +667,13 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
static_cast<size_t>(root.indirect_mapping_offset) + 1u +
|
||||
static_cast<size_t>(root.indirect_mapping_capacity) * 2u >
|
||||
next_snapshot.flattened_srt.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image specialization has an invalid key mapping";
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail("indirect image specialization has an invalid key mapping");
|
||||
}
|
||||
uint32_t exemplar = ImageResource::NoIndirectImage;
|
||||
for (const auto resource: root.indirect_resources) {
|
||||
if (resource >= next.images.size() ||
|
||||
next.images[resource].indirect_root != root_index) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image specialization has an invalid candidate";
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail("indirect image specialization has an invalid candidate");
|
||||
}
|
||||
if (!NullImageDescriptor(next_snapshot.images[resource])) {
|
||||
exemplar = resource;
|
||||
@@ -823,10 +681,7 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
}
|
||||
}
|
||||
if (exemplar == ImageResource::NoIndirectImage) {
|
||||
if (error != nullptr) {
|
||||
*error = "indirect image specialization has no typed candidate";
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail("indirect image specialization has no typed candidate");
|
||||
}
|
||||
const auto& image_class = next.images[exemplar];
|
||||
for (uint32_t candidate = 0; candidate < next.images.size(); candidate++) {
|
||||
@@ -849,12 +704,9 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
image.shader_swizzle != image_class.shader_swizzle ||
|
||||
image.depth_compare != image_class.depth_compare ||
|
||||
image.cube != image_class.cube) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"indirect image table at pc 0x{:08x} has incompatible candidates",
|
||||
root.first_use_pc);
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(
|
||||
fmt::format("indirect image table at pc 0x{:08x} has incompatible candidates",
|
||||
root.first_use_pc));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -867,11 +719,8 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
std::vector<SamplerUsage> sampler_usage(next.samplers.size());
|
||||
for (const auto& pair: next.sampled_pairs) {
|
||||
if (pair.image >= next.images.size() || pair.sampler >= next.samplers.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("sampled resource pair at pc 0x{:08x} is invalid",
|
||||
pair.first_use_pc);
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(
|
||||
fmt::format("sampled resource pair at pc 0x{:08x} is invalid", pair.first_use_pc));
|
||||
}
|
||||
auto& usage = sampler_usage[pair.sampler];
|
||||
if (next.images[pair.image].conversion_format != Prospero::BufferFormat::kInvalid) {
|
||||
@@ -891,10 +740,8 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
continue;
|
||||
}
|
||||
if (next.samplers.size() >= ShaderInfo::MaxSamplers) {
|
||||
if (error != nullptr) {
|
||||
*error = "point-filter sampler variants exceed the dense sampler resource limit";
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(
|
||||
"point-filter sampler variants exceed the dense sampler resource limit");
|
||||
}
|
||||
point_sampler[i] = static_cast<uint32_t>(next.samplers.size());
|
||||
auto sampler = next.samplers[i];
|
||||
@@ -915,19 +762,13 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
}
|
||||
const auto index = inst.Flags<MemoryFlags>().index;
|
||||
if (index >= memory_info.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("typed image instruction has invalid memory metadata {}",
|
||||
index);
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(
|
||||
fmt::format("typed image instruction has invalid memory metadata {}", index));
|
||||
}
|
||||
auto& memory = memory_info[index];
|
||||
if (memory.resource >= next.images.size()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("typed image instruction has invalid resource {}",
|
||||
memory.resource);
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(fmt::format(
|
||||
"typed image instruction has invalid resource {}", memory.resource));
|
||||
}
|
||||
const auto& image = next.images[memory.resource];
|
||||
if (image_opcode.needs_sampler &&
|
||||
@@ -937,22 +778,18 @@ bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::stri
|
||||
}
|
||||
if (image.indirect_root == memory.resource &&
|
||||
inst.GetOpcode() != ValueOpcode::ImageSampleRaw) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format(
|
||||
"indirect image table at pc 0x{:08x} is used by unsupported {}",
|
||||
inst.Flags<MemoryFlags>().pc, ValueOpcodeName(inst.GetOpcode()));
|
||||
}
|
||||
return false;
|
||||
return SpecializationFail(
|
||||
fmt::format("indirect image table at pc 0x{:08x} is used by unsupported {}",
|
||||
inst.Flags<MemoryFlags>().pc, ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
memory.kind = image.kind;
|
||||
memory.image_dimension = image.dimension;
|
||||
memory.image_cube = image.cube;
|
||||
}
|
||||
}
|
||||
program.info = std::move(next);
|
||||
program.info = std::move(next);
|
||||
program.memory_info = std::move(memory_info);
|
||||
snapshot = std::move(next_snapshot);
|
||||
return true;
|
||||
snapshot = std::move(next_snapshot);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
@@ -22,19 +22,17 @@ struct ResourceSnapshot {
|
||||
std::vector<IndirectImage> indirect_images;
|
||||
};
|
||||
|
||||
bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& snapshot,
|
||||
std::string* error);
|
||||
bool ValidateResourceSpecialization(const Program& program, const ResourceSnapshot& snapshot,
|
||||
std::string* error);
|
||||
bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& snapshot);
|
||||
bool ValidateResourceSpecialization(const Program& program, const ResourceSnapshot& snapshot);
|
||||
|
||||
// Resolves the immutable dense resource topology against one runtime user-data/SRT snapshot.
|
||||
// On failure the destination is unchanged.
|
||||
bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
|
||||
ResourceSnapshot& snapshot, std::string* error);
|
||||
ResourceSnapshot& snapshot);
|
||||
|
||||
// Applies runtime descriptor shape/format facts to a copied dense topology before layout and
|
||||
// emission. On failure the program is unchanged.
|
||||
bool SpecializeResources(Program& program, ResourceSnapshot& snapshot, std::string* error);
|
||||
// emission. Invalid specializations are fatal.
|
||||
void SpecializeResources(Program& program, ResourceSnapshot& snapshot);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceTracking.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fmt/format.h>
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t SamplerBorderClampMask = (1u << 2u) | (1u << 5u) | (1u << 8u);
|
||||
constexpr uint32_t SamplerBorderClampMask = (1u << 2u) | (1u << 5u) | (1u << 8u);
|
||||
constexpr uint32_t SamplerDword3ReservedMask = 0x3ffff000u;
|
||||
|
||||
uint32_t PossibleU32Bits(Value value) {
|
||||
@@ -101,19 +101,17 @@ public:
|
||||
m_info.uses_dma = false;
|
||||
}
|
||||
|
||||
bool Run(std::string* error) {
|
||||
void Run() {
|
||||
if (m_program.resource_tracking_complete) {
|
||||
return Fail(0, error, "resources already tracked");
|
||||
Fail(0, "resources already tracked");
|
||||
}
|
||||
if (!m_program.srt_plan_complete) {
|
||||
return Fail(0, error, "SRT plan is not ready");
|
||||
Fail(0, "SRT plan is not ready");
|
||||
}
|
||||
PlanIndirectImages();
|
||||
for (auto* block: m_program.blocks) {
|
||||
for (auto& inst: *block) {
|
||||
if (!Collect(inst, error)) {
|
||||
return false;
|
||||
}
|
||||
Collect(inst);
|
||||
}
|
||||
}
|
||||
LinkImageAliases();
|
||||
@@ -145,10 +143,9 @@ public:
|
||||
return std::ranges::find(plan.reads, inst) != plan.reads.end();
|
||||
});
|
||||
});
|
||||
m_program.descriptor_sources = std::move(m_sources);
|
||||
m_program.descriptor_sources = std::move(m_sources);
|
||||
m_program.info = std::move(m_info);
|
||||
m_program.resource_tracking_complete = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -173,18 +170,19 @@ private:
|
||||
std::array<const Inst*, 8> reads {};
|
||||
};
|
||||
|
||||
bool Fail(uint32_t pc, std::string* error, const std::string& reason) const {
|
||||
return ShaderError::Fail(
|
||||
error, fmt::format("shader resource tracking: hash=0x{:016x} stage={} pc=0x{:08x} {}",
|
||||
m_program.shader_hash, StageName(m_program.stage), pc, reason));
|
||||
[[noreturn]] void Fail(uint32_t pc, const std::string& reason) const {
|
||||
const auto message =
|
||||
fmt::format("shader resource tracking: hash=0x{:016x} stage={} pc=0x{:08x} {}",
|
||||
m_program.shader_hash, StageName(m_program.stage), pc, reason);
|
||||
EXIT("%s", message.c_str());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
bool MakeSource(const Inst& handle, uint32_t width, bool sampler, bool sample_adjust,
|
||||
DescriptorSource& descriptor, uint32_t pc, std::string* error) const {
|
||||
void MakeSource(const Inst& handle, uint32_t width, bool sampler, bool sample_adjust,
|
||||
DescriptorSource& descriptor, uint32_t pc) const {
|
||||
if (handle.NumArgs() != width) {
|
||||
return Fail(pc, error,
|
||||
fmt::format("{} has {} descriptor dwords, expected {}",
|
||||
ValueOpcodeName(handle.GetOpcode()), handle.NumArgs(), width));
|
||||
Fail(pc, fmt::format("{} has {} descriptor dwords, expected {}",
|
||||
ValueOpcodeName(handle.GetOpcode()), handle.NumArgs(), width));
|
||||
}
|
||||
descriptor.dword_count = width;
|
||||
for (uint32_t i = 0; i < width; i++) {
|
||||
@@ -199,18 +197,15 @@ private:
|
||||
// Border color and its table index are unused unless a clamp axis selects border mode.
|
||||
descriptor.dwords[3] = Value(0u);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateSource(const DescriptorSource& descriptor, std::string& reason,
|
||||
uint32_t& bad_dword) const {
|
||||
bool ValidateSource(const DescriptorSource& descriptor, uint32_t& bad_dword) const {
|
||||
for (uint32_t i = 0; i < descriptor.dword_count; i++) {
|
||||
bad_dword = i;
|
||||
if (descriptor.dwords[i].Resolve().GetType() != Type::U32) {
|
||||
reason = "is not U32";
|
||||
return false;
|
||||
}
|
||||
if (!ValidateRuntimeValue(m_program, descriptor.dwords[i], reason)) {
|
||||
if (!ValidateRuntimeValue(m_program, descriptor.dwords[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -286,13 +281,12 @@ private:
|
||||
|
||||
bool MakeRuntimeBufferSource(const Inst& handle, uint32_t pc, uint32_t& source,
|
||||
DescriptorSource& descriptor) {
|
||||
if (handle.GetOpcode() != ValueOpcode::GetBufferResource ||
|
||||
!MakeSource(handle, 4u, false, false, descriptor, pc, nullptr)) {
|
||||
if (handle.GetOpcode() != ValueOpcode::GetBufferResource) {
|
||||
return false;
|
||||
}
|
||||
std::string reason;
|
||||
uint32_t bad_dword = 0;
|
||||
if (!ValidateSource(descriptor, reason, bad_dword)) {
|
||||
MakeSource(handle, 4u, false, false, descriptor, pc);
|
||||
uint32_t bad_dword = 0;
|
||||
if (!ValidateSource(descriptor, bad_dword)) {
|
||||
return false;
|
||||
}
|
||||
source = InternSource(descriptor);
|
||||
@@ -469,38 +463,30 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool GetHandle(Value value, ValueOpcode expected, uint32_t width, uint32_t pc, Inst*& handle,
|
||||
uint32_t& source, std::string* error, bool sampler = false,
|
||||
bool sample_adjust = false) {
|
||||
void GetHandle(Value value, ValueOpcode expected, uint32_t width, uint32_t pc, Inst*& handle,
|
||||
uint32_t& source, bool sampler = false, bool sample_adjust = false) {
|
||||
handle = value.Resolve().TryInstruction();
|
||||
if (handle == nullptr || handle->GetOpcode() != expected) {
|
||||
return Fail(pc, error,
|
||||
fmt::format("memory operation requires {}", ValueOpcodeName(expected)));
|
||||
Fail(pc, fmt::format("memory operation requires {}", ValueOpcodeName(expected)));
|
||||
}
|
||||
DescriptorSource descriptor;
|
||||
if (!MakeSource(*handle, width, sampler, sample_adjust, descriptor, pc, error)) {
|
||||
return false;
|
||||
}
|
||||
std::string reason;
|
||||
uint32_t bad_dword = 0;
|
||||
if (!ValidateSource(descriptor, reason, bad_dword)) {
|
||||
return Fail(
|
||||
pc, error,
|
||||
fmt::format("{} dword {} {}", ValueOpcodeName(expected), bad_dword, reason));
|
||||
MakeSource(*handle, width, sampler, sample_adjust, descriptor, pc);
|
||||
uint32_t bad_dword = 0;
|
||||
if (!ValidateSource(descriptor, bad_dword)) {
|
||||
Fail(pc, fmt::format("{} dword {} is not a valid runtime value",
|
||||
ValueOpcodeName(expected), bad_dword));
|
||||
}
|
||||
source = InternSource(descriptor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateAddressHandle(Value value, uint32_t pc, std::string* error) const {
|
||||
void ValidateAddressHandle(Value value, uint32_t pc) const {
|
||||
const auto* handle = value.Resolve().TryInstruction();
|
||||
if (handle == nullptr || handle->GetOpcode() != ValueOpcode::GetAddressResource) {
|
||||
return Fail(pc, error, "address operation requires GetAddressResource");
|
||||
Fail(pc, "address operation requires GetAddressResource");
|
||||
}
|
||||
if (handle->NumArgs() != 2) {
|
||||
return Fail(pc, error, "GetAddressResource must have two address dwords");
|
||||
Fail(pc, "GetAddressResource must have two address dwords");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t AddBuffer(uint32_t source, const MemoryInfo& memory, ValueOpcode op, uint32_t pc) {
|
||||
@@ -540,7 +526,7 @@ private:
|
||||
const auto mip = MipMode(memory);
|
||||
const bool depth = (memory.image_sample_flags & Decoder::ImageSampleFlagCompare) != 0;
|
||||
for (uint32_t i = 0; i < m_info.images.size(); i++) {
|
||||
auto& image = m_info.images[i];
|
||||
auto& image = m_info.images[i];
|
||||
const bool compatible_kind = image.kind == memory.kind || (IsStorageImage(image.kind) &&
|
||||
IsStorageImage(memory.kind));
|
||||
if (image.source == source && compatible_kind &&
|
||||
@@ -593,155 +579,139 @@ private:
|
||||
return static_cast<uint32_t>(m_info.samplers.size() - 1);
|
||||
}
|
||||
|
||||
bool AddSampledPair(uint32_t image, uint32_t sampler, uint32_t pc, std::string* error) {
|
||||
void AddSampledPair(uint32_t image, uint32_t sampler, uint32_t pc) {
|
||||
for (auto& pair: m_info.sampled_pairs) {
|
||||
if (pair.image == image && pair.sampler == sampler) {
|
||||
pair.first_use_pc = std::min(pair.first_use_pc, pc);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (m_info.sampled_pairs.size() >= ShaderInfo::MaxSampledPairs) {
|
||||
return Fail(pc, error, "sampled image/sampler pair limit exceeded");
|
||||
Fail(pc, "sampled image/sampler pair limit exceeded");
|
||||
}
|
||||
m_info.sampled_pairs.push_back({image, sampler, pc});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddHandlePatch(Inst* handle, uint32_t resource, uint32_t pc, std::string* error) {
|
||||
void AddHandlePatch(Inst* handle, uint32_t resource, uint32_t pc) {
|
||||
for (const auto& patch: m_handle_patches) {
|
||||
if (patch.handle == handle) {
|
||||
return patch.resource == resource ||
|
||||
Fail(pc, error,
|
||||
fmt::format("{} is reused with incompatible resource classes",
|
||||
ValueOpcodeName(handle->GetOpcode())));
|
||||
if (patch.resource != resource) {
|
||||
Fail(pc, fmt::format("{} is reused with incompatible resource classes",
|
||||
ValueOpcodeName(handle->GetOpcode())));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_handle_patches.push_back({handle, resource});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddMemoryPatch(uint32_t index, uint32_t resource, uint32_t sampler, bool has_sampler,
|
||||
uint32_t pc, std::string* error) {
|
||||
void AddMemoryPatch(uint32_t index, uint32_t resource, uint32_t sampler, bool has_sampler,
|
||||
uint32_t pc) {
|
||||
for (auto& patch: m_memory_patches) {
|
||||
if (patch.index != index) {
|
||||
continue;
|
||||
}
|
||||
if (patch.resource != resource ||
|
||||
(has_sampler && patch.has_sampler && patch.sampler != sampler)) {
|
||||
return Fail(pc, error, "memory metadata is reused with incompatible resources");
|
||||
Fail(pc, "memory metadata is reused with incompatible resources");
|
||||
}
|
||||
if (has_sampler) {
|
||||
patch.sampler = sampler;
|
||||
patch.has_sampler = true;
|
||||
}
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
m_memory_patches.push_back({index, resource, sampler, has_sampler});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Collect(Inst& inst, std::string* error) {
|
||||
void Collect(Inst& inst) {
|
||||
const auto op = inst.GetOpcode();
|
||||
const auto buffer = BufferAccessOf(op);
|
||||
const auto address_info = AddressOpcodeInfoOf(op);
|
||||
const auto image_info = ImageOpcodeInfoOf(op);
|
||||
if (buffer == BufferAccess::None && address_info.access == AddressAccess::None &&
|
||||
image_info.access == ImageAccess::None) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
const auto flags = inst.Flags<MemoryFlags>();
|
||||
if (flags.index >= m_program.memory_info.size()) {
|
||||
return Fail(flags.pc, error,
|
||||
fmt::format("memory metadata index {} is out of range", flags.index));
|
||||
Fail(flags.pc, fmt::format("memory metadata index {} is out of range", flags.index));
|
||||
}
|
||||
if (inst.NumArgs() == 0) {
|
||||
return Fail(flags.pc, error, "memory operation has no resource handle");
|
||||
Fail(flags.pc, "memory operation has no resource handle");
|
||||
}
|
||||
const auto& memory = m_program.memory_info[flags.index];
|
||||
if (memory.planning_only || IsIndirectPlanningMemory(flags.index)) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
Inst* handle = nullptr;
|
||||
uint32_t source = 0;
|
||||
uint32_t resource = 0;
|
||||
|
||||
if (buffer != BufferAccess::None) {
|
||||
if (!GetHandle(inst.Arg(0), ValueOpcode::GetBufferResource, 4, flags.pc, handle, source,
|
||||
error)) {
|
||||
return false;
|
||||
}
|
||||
GetHandle(inst.Arg(0), ValueOpcode::GetBufferResource, 4, flags.pc, handle, source);
|
||||
resource = AddBuffer(source, memory, op, flags.pc);
|
||||
if (resource == UINT32_MAX) {
|
||||
return Fail(flags.pc, error, "buffer resource limit exceeded");
|
||||
Fail(flags.pc, "buffer resource limit exceeded");
|
||||
}
|
||||
return AddHandlePatch(handle, resource, flags.pc, error) &&
|
||||
AddMemoryPatch(flags.index, resource, 0, false, flags.pc, error);
|
||||
AddHandlePatch(handle, resource, flags.pc);
|
||||
AddMemoryPatch(flags.index, resource, 0, false, flags.pc);
|
||||
return;
|
||||
}
|
||||
if (address_info.access != AddressAccess::None) {
|
||||
if (!IsAddressResourceKind(memory.kind)) {
|
||||
return Fail(flags.pc, error, "address operation has invalid resource kind");
|
||||
Fail(flags.pc, "address operation has invalid resource kind");
|
||||
}
|
||||
if (memory.kind == ResourceKind::Scratch) {
|
||||
handle = inst.Arg(0).Resolve().TryInstruction();
|
||||
if (handle == nullptr || handle->GetOpcode() != ValueOpcode::GetScratchResource ||
|
||||
handle->NumArgs() != 0) {
|
||||
return Fail(flags.pc, error, "scratch operation requires GetScratchResource");
|
||||
Fail(flags.pc, "scratch operation requires GetScratchResource");
|
||||
}
|
||||
if (m_program.scratch_dwords == 0) {
|
||||
return Fail(flags.pc, error,
|
||||
"scratch operation requires a nonzero AGC per-thread size");
|
||||
Fail(flags.pc, "scratch operation requires a nonzero AGC per-thread size");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!ValidateAddressHandle(inst.Arg(0), flags.pc, error)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
ValidateAddressHandle(inst.Arg(0), flags.pc);
|
||||
m_info.uses_dma = true;
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ImageResourceKindMatches(memory.kind, image_info.resource_class)) {
|
||||
return Fail(flags.pc, error, "image operation has invalid resource kind");
|
||||
Fail(flags.pc, "image operation has invalid resource kind");
|
||||
}
|
||||
handle = inst.Arg(0).Resolve().TryInstruction();
|
||||
const auto* indirect = handle != nullptr ? FindIndirectImage(*handle) : nullptr;
|
||||
if (indirect != nullptr) {
|
||||
source = indirect->source;
|
||||
} else if (!GetHandle(inst.Arg(0), ValueOpcode::GetImageResource, 8, flags.pc, handle,
|
||||
source, error)) {
|
||||
return false;
|
||||
} else {
|
||||
GetHandle(inst.Arg(0), ValueOpcode::GetImageResource, 8, flags.pc, handle, source);
|
||||
}
|
||||
resource = AddImage(source, memory, op, flags.pc);
|
||||
if (resource == UINT32_MAX) {
|
||||
return Fail(flags.pc, error, "image resource limit exceeded");
|
||||
}
|
||||
if (!AddHandlePatch(handle, resource, flags.pc, error)) {
|
||||
return false;
|
||||
Fail(flags.pc, "image resource limit exceeded");
|
||||
}
|
||||
AddHandlePatch(handle, resource, flags.pc);
|
||||
uint32_t sampler = 0;
|
||||
if (image_info.needs_sampler) {
|
||||
if (inst.NumArgs() < 2) {
|
||||
return Fail(flags.pc, error, "sampled image operation has no sampler handle");
|
||||
Fail(flags.pc, "sampled image operation has no sampler handle");
|
||||
}
|
||||
Inst* sampler_handle = nullptr;
|
||||
uint32_t sampler_source = 0;
|
||||
Inst* sampler_handle = nullptr;
|
||||
uint32_t sampler_source = 0;
|
||||
const bool sample_adjust =
|
||||
(memory.image_sample_flags & Decoder::ImageSampleFlagAdjust) != 0;
|
||||
if (!GetHandle(inst.Arg(1), ValueOpcode::GetSamplerResource, 4, flags.pc,
|
||||
sampler_handle, sampler_source, error, true, sample_adjust)) {
|
||||
return false;
|
||||
}
|
||||
GetHandle(inst.Arg(1), ValueOpcode::GetSamplerResource, 4, flags.pc, sampler_handle,
|
||||
sampler_source, true, sample_adjust);
|
||||
sampler = AddSampler(sampler_source, flags.pc);
|
||||
if (sampler == UINT32_MAX) {
|
||||
return Fail(flags.pc, error, "sampler resource limit exceeded");
|
||||
}
|
||||
if (!AddHandlePatch(sampler_handle, sampler, flags.pc, error) ||
|
||||
!AddSampledPair(resource, sampler, flags.pc, error)) {
|
||||
return false;
|
||||
Fail(flags.pc, "sampler resource limit exceeded");
|
||||
}
|
||||
AddHandlePatch(sampler_handle, sampler, flags.pc);
|
||||
AddSampledPair(resource, sampler, flags.pc);
|
||||
}
|
||||
return AddMemoryPatch(flags.index, resource, sampler, image_info.needs_sampler, flags.pc,
|
||||
error);
|
||||
AddMemoryPatch(flags.index, resource, sampler, image_info.needs_sampler, flags.pc);
|
||||
}
|
||||
|
||||
const DescriptorSource* Source(uint32_t source) const {
|
||||
@@ -783,8 +753,8 @@ private:
|
||||
|
||||
} // namespace
|
||||
|
||||
bool TrackResources(Program& program, std::string* error) {
|
||||
return Tracker(program).Run(error);
|
||||
void TrackResources(Program& program) {
|
||||
Tracker(program).Run();
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
@@ -7,7 +7,7 @@ namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
// Collects immutable resource topology from typed SSA handles, interns their resolved dwords in
|
||||
// descriptor_sources, then writes dense indices to handle flags and MemoryInfo.
|
||||
bool TrackResources(Program& program, std::string* error);
|
||||
void TrackResources(Program& program);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ShaderInfoCollection.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -8,6 +9,11 @@
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
namespace {
|
||||
|
||||
[[noreturn]] void Fail(std::string_view message) {
|
||||
EXIT("shader info collection failed: %s", std::string(message).c_str());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void AddInput(ShaderInfo& info, StageInputKind kind, uint32_t location, uint32_t components,
|
||||
std::string name, bool per_vertex = false) {
|
||||
const auto input = std::find_if(info.inputs.begin(), info.inputs.end(), [=](const auto& value) {
|
||||
@@ -34,38 +40,38 @@ void AddOutput(ShaderInfo& info, StageOutputKind kind, uint32_t index, uint32_t
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidateOptions(const Program& program, const ShaderInfoOptions& options, std::string* error) {
|
||||
auto Fail = [&](const char* message) {
|
||||
if (error != nullptr) {
|
||||
*error = message;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
void ValidateOptions(const Program& program, const ShaderInfoOptions& options) {
|
||||
switch (program.stage) {
|
||||
case ShaderType::Vertex:
|
||||
if (options.vertex == nullptr) {
|
||||
return Fail("vertex shader has no input metadata");
|
||||
}
|
||||
if (options.vertex->resources_num < 0 ||
|
||||
options.vertex->resources_num > ShaderVertexInputInfo::RES_MAX) {
|
||||
return Fail("vertex resource count is out of range");
|
||||
}
|
||||
return true;
|
||||
return;
|
||||
case ShaderType::Pixel:
|
||||
return options.pixel->input_num <= std::size(options.pixel->interpolator_settings) ||
|
||||
Fail("pixel input count is out of range");
|
||||
if (options.pixel == nullptr) {
|
||||
return Fail("pixel shader has no input metadata");
|
||||
}
|
||||
if (options.pixel->input_num > std::size(options.pixel->interpolator_settings)) {
|
||||
return Fail("pixel input count is out of range");
|
||||
}
|
||||
return;
|
||||
case ShaderType::Compute:
|
||||
return (options.compute->thread_ids_num >= 0 && options.compute->thread_ids_num <= 3) ||
|
||||
Fail("compute thread ID count is out of range");
|
||||
if (options.compute == nullptr) {
|
||||
return Fail("compute shader has no input metadata");
|
||||
}
|
||||
if (options.compute->thread_ids_num < 0 || options.compute->thread_ids_num > 3) {
|
||||
return Fail("compute thread ID count is out of range");
|
||||
}
|
||||
return;
|
||||
default: return Fail("unsupported shader stage for info collection");
|
||||
}
|
||||
}
|
||||
|
||||
bool ValidateValueReferences(const Program& program, const ShaderInfoOptions& options,
|
||||
std::string* error) {
|
||||
const auto Fail = [&](const char* message) {
|
||||
if (error != nullptr) {
|
||||
*error = message;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
void ValidateValueReferences(const Program& program, const ShaderInfoOptions& options) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
switch (inst.GetOpcode()) {
|
||||
@@ -148,7 +154,6 @@ bool ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CollectVertexInputs(const Program& program, const ShaderVertexInputInfo* vertex,
|
||||
@@ -269,14 +274,8 @@ void CollectBuiltinInputs(const Program& program, ShaderInfo& info) {
|
||||
}
|
||||
}
|
||||
|
||||
bool CollectOutputs(const Program& program, const ShaderVertexInputInfo* vertex,
|
||||
const ShaderPixelInputInfo* pixel, ShaderInfo& info, std::string* error) {
|
||||
auto Fail = [&](std::string message) {
|
||||
if (error != nullptr) {
|
||||
*error = std::move(message);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
void CollectOutputs(const Program& program, const ShaderVertexInputInfo* vertex,
|
||||
const ShaderPixelInputInfo* pixel, ShaderInfo& info) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (inst.GetOpcode() != ValueOpcode::SetAttribute) {
|
||||
@@ -347,31 +346,23 @@ bool CollectOutputs(const Program& program, const ShaderVertexInputInfo* vertex,
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool CollectShaderInfo(Program& program, const ShaderInfoOptions& options, std::string* error) {
|
||||
void CollectShaderInfo(Program& program, const ShaderInfoOptions& options) {
|
||||
if (!program.resource_tracking_complete || program.shader_info_complete) {
|
||||
if (error != nullptr) {
|
||||
*error = !program.resource_tracking_complete ? "shader resources were not tracked"
|
||||
: "shader info already collected";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!ValidateOptions(program, options, error)) {
|
||||
return false;
|
||||
}
|
||||
if (!ValidateValueReferences(program, options, error)) {
|
||||
return false;
|
||||
return Fail(!program.resource_tracking_complete ? "shader resources were not tracked"
|
||||
: "shader info already collected");
|
||||
}
|
||||
ValidateOptions(program, options);
|
||||
ValidateValueReferences(program, options);
|
||||
|
||||
auto next = program.info;
|
||||
next.inputs.clear();
|
||||
next.outputs.clear();
|
||||
next.has_bitwise_xor = std::any_of(
|
||||
program.blocks.begin(), program.blocks.end(), [](const auto* block) {
|
||||
next.has_bitwise_xor =
|
||||
std::any_of(program.blocks.begin(), program.blocks.end(), [](const auto* block) {
|
||||
return std::any_of(block->begin(), block->end(), [](const auto& inst) {
|
||||
return inst.GetOpcode() == ValueOpcode::BitwiseXor32;
|
||||
});
|
||||
@@ -380,15 +371,12 @@ bool CollectShaderInfo(Program& program, const ShaderInfoOptions& options, std::
|
||||
case ShaderType::Vertex: CollectVertexInputs(program, options.vertex, next); break;
|
||||
case ShaderType::Pixel: CollectPixelInputs(program, options.pixel, next); break;
|
||||
case ShaderType::Compute: CollectComputeInputs(options.compute, next); break;
|
||||
default: return false;
|
||||
default: return Fail("unsupported shader stage for info collection");
|
||||
}
|
||||
CollectBuiltinInputs(program, next);
|
||||
if (!CollectOutputs(program, options.vertex, options.pixel, next, error)) {
|
||||
return false;
|
||||
}
|
||||
CollectOutputs(program, options.vertex, options.pixel, next);
|
||||
program.info = std::move(next);
|
||||
program.shader_info_complete = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
@@ -13,7 +13,7 @@ struct ShaderInfoOptions {
|
||||
|
||||
// Completes the immutable shader interface after resource tracking. On failure Program::info and
|
||||
// all completion state remain unchanged.
|
||||
bool CollectShaderInfo(Program& program, const ShaderInfoOptions& options, std::string* error);
|
||||
void CollectShaderInfo(Program& program, const ShaderInfoOptions& options);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
@@ -120,10 +120,10 @@ class RuntimeValidator {
|
||||
public:
|
||||
explicit RuntimeValidator(const Program& program): m_program(program) {}
|
||||
|
||||
bool Run(Value value, std::string& reason) { return Validate(value, reason); }
|
||||
bool Run(Value value) { return Validate(value); }
|
||||
|
||||
private:
|
||||
bool Validate(Value value, std::string& reason) {
|
||||
bool Validate(Value value) {
|
||||
value = value.Resolve();
|
||||
const auto* inst = value.TryInstruction();
|
||||
if (inst == nullptr) {
|
||||
@@ -133,14 +133,10 @@ private:
|
||||
case Type::U16:
|
||||
case Type::U32:
|
||||
case Type::U64: return true;
|
||||
default:
|
||||
reason = fmt::format("contains a non-integer immediate of type {}",
|
||||
TypeName(value.GetType()));
|
||||
return false;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
if (!m_visiting.insert(inst).second) {
|
||||
reason = fmt::format("contains a cyclic {} value", ValueOpcodeName(inst->GetOpcode()));
|
||||
return false;
|
||||
}
|
||||
const auto finish = [&](bool valid) {
|
||||
@@ -151,25 +147,21 @@ private:
|
||||
if (op == ValueOpcode::UndefU1 || op == ValueOpcode::UndefU8 ||
|
||||
op == ValueOpcode::UndefU16 || op == ValueOpcode::UndefU32 ||
|
||||
op == ValueOpcode::UndefU64 || op == ValueOpcode::Void) {
|
||||
reason = fmt::format("contains {}", ValueOpcodeName(op));
|
||||
return finish(false);
|
||||
}
|
||||
if (op == ValueOpcode::GetUserData) {
|
||||
if (inst->NumArgs() != 1 || inst->Arg(0).GetType() != Type::ScalarReg) {
|
||||
reason = "contains a malformed GetUserData";
|
||||
return finish(false);
|
||||
}
|
||||
const auto reg = RegIndex(inst->Arg(0).ScalarRegister());
|
||||
if (reg < m_program.user_data_base ||
|
||||
reg - m_program.user_data_base >= m_program.user_data_count) {
|
||||
reason = fmt::format("references unavailable user SGPR {}", reg);
|
||||
return finish(false);
|
||||
}
|
||||
return finish(true);
|
||||
}
|
||||
if (op == ValueOpcode::GetShaderBase) {
|
||||
if (inst->NumArgs() != 0) {
|
||||
reason = "contains a malformed GetShaderBase";
|
||||
return finish(false);
|
||||
}
|
||||
return finish(true);
|
||||
@@ -177,14 +169,12 @@ private:
|
||||
if (op == ValueOpcode::Phi) {
|
||||
const auto invariant = ResolveInvariantPhi(m_program, value);
|
||||
if (invariant.IsEmpty()) {
|
||||
reason = "contains a control-dependent phi";
|
||||
return finish(false);
|
||||
}
|
||||
return finish(Validate(invariant, reason));
|
||||
return finish(Validate(invariant));
|
||||
}
|
||||
if (op == ValueOpcode::GetSrtResource) {
|
||||
if (inst->NumArgs() != 0) {
|
||||
reason = "contains a malformed GetSrtResource";
|
||||
return finish(false);
|
||||
}
|
||||
return finish(true);
|
||||
@@ -196,7 +186,6 @@ private:
|
||||
ValueOpcode::GetSrtResource ||
|
||||
!slot.IsImmediate() || slot.GetType() != Type::U32 ||
|
||||
slot.U32() >= m_program.srt_reads.size()) {
|
||||
reason = "contains a malformed flattened SRT read";
|
||||
return finish(false);
|
||||
}
|
||||
} else if (op == ValueOpcode::LoadAddressU32 || op == ValueOpcode::ReadConstBuffer) {
|
||||
@@ -206,13 +195,11 @@ private:
|
||||
const auto* handle = inst->NumArgs() != 0 ? inst->Arg(0).ResolveInstruction() : nullptr;
|
||||
if (!IsRawRead(m_program, *inst) || handle == nullptr ||
|
||||
handle->GetOpcode() != expected) {
|
||||
reason = fmt::format("contains a non-scalar {}", ValueOpcodeName(op));
|
||||
return finish(false);
|
||||
}
|
||||
} else if (op == ValueOpcode::CompositeExtractU64) {
|
||||
const auto index = inst->NumArgs() == 2 ? inst->Arg(1).Resolve() : Value {};
|
||||
if (!index.IsImmediate() || index.GetType() != Type::U32 || index.U32() >= 2u) {
|
||||
reason = "contains an invalid U64 component index";
|
||||
return finish(false);
|
||||
}
|
||||
} else if (op == ValueOpcode::CompositeExtractU32x2) {
|
||||
@@ -222,7 +209,6 @@ private:
|
||||
index.U32() >= 2u ||
|
||||
(source->GetOpcode() != ValueOpcode::CompositeConstructU32x2 &&
|
||||
source->GetOpcode() != ValueOpcode::IAddCarry32)) {
|
||||
reason = "contains an unsupported composite runtime source";
|
||||
return finish(false);
|
||||
}
|
||||
}
|
||||
@@ -233,17 +219,14 @@ private:
|
||||
: op == ValueOpcode::GetSamplerResource ? 4u
|
||||
: 2u;
|
||||
if (inst->NumArgs() != expected) {
|
||||
reason = fmt::format("contains a malformed {}", ValueOpcodeName(op));
|
||||
return finish(false);
|
||||
}
|
||||
} else if (op != ValueOpcode::ReadConst && op != ValueOpcode::ReadConstBuffer &&
|
||||
op != ValueOpcode::LoadAddressU32 && !IsRuntimeIntegerOp(op)) {
|
||||
reason =
|
||||
fmt::format("contains unsupported or control-dependent {}", ValueOpcodeName(op));
|
||||
return finish(false);
|
||||
}
|
||||
for (size_t index = 0; index < inst->NumArgs(); index++) {
|
||||
if (!Validate(inst->Arg(index), reason)) {
|
||||
if (!Validate(inst->Arg(index))) {
|
||||
return finish(false);
|
||||
}
|
||||
}
|
||||
@@ -258,7 +241,7 @@ class PlanBuilder {
|
||||
public:
|
||||
explicit PlanBuilder(Program& program): m_program(program) {}
|
||||
|
||||
bool Run(std::string* error) {
|
||||
void Run() {
|
||||
m_program.srt_reads.clear();
|
||||
m_program.dynamic_reads.clear();
|
||||
for (auto* block: m_program.blocks) {
|
||||
@@ -273,34 +256,29 @@ public:
|
||||
(op == ValueOpcode::ReadConstBuffer &&
|
||||
kind == ResourceKind::ScalarAddress);
|
||||
if (crosswired) {
|
||||
return Fail(flags.pc, error,
|
||||
fmt::format("{} has incompatible scalar memory metadata",
|
||||
ValueOpcodeName(op)));
|
||||
Fail(flags.pc,
|
||||
fmt::format("{} has incompatible scalar memory metadata",
|
||||
ValueOpcodeName(op)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (IsDescriptorHandle(inst.GetOpcode())) {
|
||||
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
||||
if (!Collect(inst.Arg(index), 0, error)) {
|
||||
return false;
|
||||
}
|
||||
Collect(inst.Arg(index), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto* block: m_program.blocks) {
|
||||
for (auto& inst: *block) {
|
||||
std::string reason;
|
||||
if (inst.GetOpcode() == ValueOpcode::LoadAddressU32 && IsRawRead(m_program, inst) &&
|
||||
inst.Arg(1).Resolve().IsImmediate() &&
|
||||
ValidateRuntimeValue(m_program, Value(&inst), reason) &&
|
||||
!Collect(Value(&inst), inst.Flags<MemoryFlags>().pc, error)) {
|
||||
return false;
|
||||
ValidateRuntimeValue(m_program, Value(&inst))) {
|
||||
Collect(Value(&inst), inst.Flags<MemoryFlags>().pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
PatchReads();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -310,18 +288,20 @@ private:
|
||||
bool keep = false;
|
||||
};
|
||||
|
||||
bool Fail(uint32_t pc, std::string* error, const std::string& message) const {
|
||||
return ShaderError::Fail(error, Diagnostic(m_program, pc, message));
|
||||
[[noreturn]] void Fail(uint32_t pc, const std::string& message) const {
|
||||
const auto diagnostic = Diagnostic(m_program, pc, message);
|
||||
EXIT("shader SRT planning failed: %s", diagnostic.c_str());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
bool Collect(Value value, uint32_t use_pc, std::string* error) {
|
||||
void Collect(Value value, uint32_t use_pc) {
|
||||
value = value.Resolve();
|
||||
if (value.IsImmediate()) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
auto* inst = value.TryInstruction();
|
||||
if (inst == nullptr) {
|
||||
return Fail(use_pc, error, "invalid typed planning value");
|
||||
Fail(use_pc, "invalid typed planning value");
|
||||
}
|
||||
const auto cycle = std::ranges::find(m_visiting, inst);
|
||||
if (cycle != m_visiting.end()) {
|
||||
@@ -329,44 +309,40 @@ private:
|
||||
return value->GetOpcode() == ValueOpcode::Phi;
|
||||
});
|
||||
if (contains_phi) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
return Fail(use_pc, error,
|
||||
fmt::format("cyclic typed planning value {} without a phi",
|
||||
ValueOpcodeName(inst->GetOpcode())));
|
||||
Fail(use_pc, fmt::format("cyclic typed planning value {} without a phi",
|
||||
ValueOpcodeName(inst->GetOpcode())));
|
||||
}
|
||||
if (std::ranges::find(m_visited, inst) != m_visited.end()) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
m_visiting.push_back(inst);
|
||||
for (size_t index = 0; index < inst->NumArgs(); index++) {
|
||||
if (!Collect(inst->Arg(index), use_pc, error)) {
|
||||
return false;
|
||||
}
|
||||
Collect(inst->Arg(index), use_pc);
|
||||
}
|
||||
m_visiting.pop_back();
|
||||
m_visited.push_back(inst);
|
||||
if (!IsRawRead(m_program, *inst)) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
const auto offset = inst->Arg(1).Resolve();
|
||||
if (!offset.IsImmediate() || offset.GetType() != Type::U32) {
|
||||
if (std::ranges::find(m_program.dynamic_reads, value) == m_program.dynamic_reads.end()) {
|
||||
if (std::ranges::find(m_program.dynamic_reads, value) ==
|
||||
m_program.dynamic_reads.end()) {
|
||||
m_program.dynamic_reads.push_back(value);
|
||||
}
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
use_pc = inst->Flags<MemoryFlags>().pc;
|
||||
for (uint32_t slot = 0; slot < m_program.srt_reads.size(); slot++) {
|
||||
if (EquivalentValue(m_program, value, m_program.srt_reads[slot].value)) {
|
||||
m_patches.push_back({inst, slot, false});
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto slot = static_cast<uint32_t>(m_program.srt_reads.size());
|
||||
m_program.srt_reads.push_back({value, slot, use_pc});
|
||||
m_program.srt_reads.push_back({value, slot});
|
||||
m_patches.push_back({inst, slot, true});
|
||||
return true;
|
||||
}
|
||||
|
||||
void PatchReads() {
|
||||
@@ -411,14 +387,12 @@ class Evaluator {
|
||||
public:
|
||||
Evaluator(const Program& program, const SrtRuntime& runtime,
|
||||
std::span<const uint8_t> clean_flat_slots = {}, Evaluator* clean_evaluator = nullptr)
|
||||
: m_program(program), m_runtime(runtime),
|
||||
m_clean_flat_slots(clean_flat_slots), m_clean_evaluator(clean_evaluator) {}
|
||||
: m_program(program), m_runtime(runtime), m_clean_flat_slots(clean_flat_slots),
|
||||
m_clean_evaluator(clean_evaluator) {}
|
||||
|
||||
void SetUsePc(uint32_t pc) { m_use_pc = pc; }
|
||||
|
||||
bool Evaluate(Value value, uint32_t& result, std::string* error) {
|
||||
bool Evaluate(Value value, uint32_t& result) {
|
||||
uint64_t wide = 0;
|
||||
if (!EvaluateWide(value, wide, error)) {
|
||||
if (!EvaluateWide(value, wide)) {
|
||||
return false;
|
||||
}
|
||||
result = static_cast<uint32_t>(wide);
|
||||
@@ -426,11 +400,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool Fail(std::string* error, const std::string& message) const {
|
||||
return ShaderError::Fail(error, Diagnostic(m_program, m_use_pc, message));
|
||||
}
|
||||
|
||||
bool EvaluateWide(Value value, uint64_t& result, std::string* error) {
|
||||
bool EvaluateWide(Value value, uint64_t& result) {
|
||||
value = value.Resolve();
|
||||
if (value.IsImmediate()) {
|
||||
switch (value.GetType()) {
|
||||
@@ -439,23 +409,23 @@ private:
|
||||
case Type::U16: result = value.U16(); return true;
|
||||
case Type::U32: result = value.U32(); return true;
|
||||
case Type::U64: result = value.U64(); return true;
|
||||
default: return Fail(error, "non-integer immediate in runtime expression");
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
auto* inst = value.TryInstruction();
|
||||
if (inst == nullptr) {
|
||||
return Fail(error, "invalid typed runtime value");
|
||||
return false;
|
||||
}
|
||||
if (const auto found = m_cache.find(inst); found != m_cache.end()) {
|
||||
result = found->second;
|
||||
return true;
|
||||
}
|
||||
if (std::ranges::find(m_visiting, inst) != m_visiting.end()) {
|
||||
return Fail(error, "cyclic typed runtime value");
|
||||
return false;
|
||||
}
|
||||
m_visiting.push_back(inst);
|
||||
uint64_t out = 0;
|
||||
if (!EvaluateInst(*inst, out, error)) {
|
||||
if (!EvaluateInst(*inst, out)) {
|
||||
return false;
|
||||
}
|
||||
m_visiting.pop_back();
|
||||
@@ -464,28 +434,27 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Arg(const Inst& inst, size_t index, uint64_t& result, std::string* error) {
|
||||
return EvaluateWide(inst.Arg(index), result, error);
|
||||
bool Arg(const Inst& inst, size_t index, uint64_t& result) {
|
||||
return EvaluateWide(inst.Arg(index), result);
|
||||
}
|
||||
|
||||
bool EvaluatePhi(const Inst& inst, uint64_t& result, std::string* error) {
|
||||
bool EvaluatePhi(const Inst& inst, uint64_t& result) {
|
||||
const auto value = ResolveInvariantPhi(m_program, Value(const_cast<Inst*>(&inst)));
|
||||
return !value.IsEmpty() ? EvaluateWide(value, result, error)
|
||||
: Fail(error, "typed phi has runtime-dependent values");
|
||||
return !value.IsEmpty() && EvaluateWide(value, result);
|
||||
}
|
||||
|
||||
bool EvaluateExtract(const Inst& inst, uint64_t& result, std::string* error) {
|
||||
bool EvaluateExtract(const Inst& inst, uint64_t& result) {
|
||||
const auto index = inst.Arg(1).Resolve();
|
||||
if (!index.IsImmediate() || index.GetType() != Type::U32) {
|
||||
return Fail(error, "dynamic composite extract in runtime expression");
|
||||
return false;
|
||||
}
|
||||
const auto component = index.U32();
|
||||
if (component >= 2u) {
|
||||
return Fail(error, "unsupported composite runtime source");
|
||||
return false;
|
||||
}
|
||||
if (inst.GetOpcode() == ValueOpcode::CompositeExtractU64) {
|
||||
uint64_t packed = 0;
|
||||
if (!Arg(inst, 0, packed, error)) {
|
||||
if (!Arg(inst, 0, packed)) {
|
||||
return false;
|
||||
}
|
||||
result = static_cast<uint32_t>(packed >> (component * 32u));
|
||||
@@ -493,15 +462,15 @@ private:
|
||||
}
|
||||
const auto* source = inst.Arg(0).ResolveInstruction();
|
||||
if (source == nullptr) {
|
||||
return Fail(error, "unsupported composite runtime source");
|
||||
return false;
|
||||
}
|
||||
if (source->GetOpcode() == ValueOpcode::CompositeConstructU32x2) {
|
||||
return EvaluateWide(source->Arg(component), result, error);
|
||||
return EvaluateWide(source->Arg(component), result);
|
||||
}
|
||||
if (source->GetOpcode() == ValueOpcode::IAddCarry32) {
|
||||
uint64_t lhs = 0;
|
||||
uint64_t rhs = 0;
|
||||
if (!Arg(*source, 0, lhs, error) || !Arg(*source, 1, rhs, error)) {
|
||||
if (!Arg(*source, 0, lhs) || !Arg(*source, 1, rhs)) {
|
||||
return false;
|
||||
}
|
||||
const auto sum =
|
||||
@@ -510,24 +479,23 @@ private:
|
||||
component == 0u ? static_cast<uint32_t>(sum) : static_cast<uint32_t>(sum >> 32u);
|
||||
return true;
|
||||
}
|
||||
return Fail(error, "unsupported composite runtime source");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EvaluateRawRead(const Inst& inst, uint64_t& result, std::string* error) {
|
||||
bool EvaluateRawRead(const Inst& inst, uint64_t& result) {
|
||||
const auto flags = inst.Flags<MemoryFlags>();
|
||||
if (flags.index >= m_program.memory_info.size()) {
|
||||
return Fail(error, "raw scalar read has invalid metadata");
|
||||
return false;
|
||||
}
|
||||
const auto& mem = m_program.memory_info[flags.index];
|
||||
const auto* handle = inst.Arg(0).ResolveInstruction();
|
||||
if (handle == nullptr) {
|
||||
return Fail(error, "raw scalar read has no descriptor handle");
|
||||
return false;
|
||||
}
|
||||
uint64_t low = 0;
|
||||
uint64_t high = 0;
|
||||
uint64_t offset = 0;
|
||||
if (!Arg(*handle, 0, low, error) || !Arg(*handle, 1, high, error) ||
|
||||
!Arg(inst, 1, offset, error)) {
|
||||
if (!Arg(*handle, 0, low) || !Arg(*handle, 1, high) || !Arg(inst, 1, offset)) {
|
||||
return false;
|
||||
}
|
||||
const auto base = ((high << 32u) | static_cast<uint32_t>(low)) & AddressMask;
|
||||
@@ -536,12 +504,11 @@ private:
|
||||
if (inst.GetOpcode() == ValueOpcode::ReadConstBuffer) {
|
||||
uint64_t records = 0;
|
||||
uint64_t word3 = 0;
|
||||
if (handle->NumArgs() != 4u || !Arg(*handle, 2, records, error) ||
|
||||
!Arg(*handle, 3, word3, error)) {
|
||||
return Fail(error, "constant-buffer descriptor has invalid width");
|
||||
if (handle->NumArgs() != 4u || !Arg(*handle, 2, records) || !Arg(*handle, 3, word3)) {
|
||||
return false;
|
||||
}
|
||||
if (immediate < 0) {
|
||||
return Fail(error, "constant-buffer read has a negative immediate offset");
|
||||
return false;
|
||||
}
|
||||
const auto byte_offset =
|
||||
static_cast<uint64_t>(immediate) + static_cast<uint32_t>(offset);
|
||||
@@ -551,21 +518,20 @@ private:
|
||||
? static_cast<uint64_t>(static_cast<uint32_t>(records))
|
||||
: static_cast<uint64_t>(stride) * static_cast<uint32_t>(records);
|
||||
if (aligned > size || size - aligned < sizeof(uint32_t)) {
|
||||
return Fail(
|
||||
error, fmt::format("constant-buffer offset {} exceeds size {}", aligned, size));
|
||||
return false;
|
||||
}
|
||||
address = ((base & ~uint64_t {3}) + byte_offset) & ~uint64_t {3};
|
||||
} else {
|
||||
const auto relative = (immediate & ~int64_t {3}) +
|
||||
static_cast<int64_t>(static_cast<uint32_t>(offset) & ~3u);
|
||||
if (!AddSignedAddress(base & ~uint64_t {3}, relative, address)) {
|
||||
return Fail(error, "raw scalar read is outside the 48-bit address space");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint32_t word = 0;
|
||||
if (m_runtime.read_memory != nullptr) {
|
||||
if (!m_runtime.read_memory(m_runtime.userdata, address, &word)) {
|
||||
return Fail(error, fmt::format("constant read failed at 0x{:016x}", address));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
std::memcpy(&word, reinterpret_cast<const void*>(address), sizeof(word));
|
||||
@@ -574,28 +540,28 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EvaluateInst(const Inst& inst, uint64_t& result, std::string* error) {
|
||||
bool EvaluateInst(const Inst& inst, uint64_t& result) {
|
||||
uint64_t a = 0;
|
||||
uint64_t b = 0;
|
||||
uint64_t c = 0;
|
||||
const auto binary = [&]() { return Arg(inst, 0, a, error) && Arg(inst, 1, b, error); };
|
||||
const auto binary = [&]() { return Arg(inst, 0, a) && Arg(inst, 1, b); };
|
||||
const auto ternary = [&]() {
|
||||
return Arg(inst, 0, a, error) && Arg(inst, 1, b, error) && Arg(inst, 2, c, error);
|
||||
return Arg(inst, 0, a) && Arg(inst, 1, b) && Arg(inst, 2, c);
|
||||
};
|
||||
switch (inst.GetOpcode()) {
|
||||
case ValueOpcode::GetUserData: {
|
||||
const auto reg = RegIndex(inst.Arg(0).ScalarRegister());
|
||||
if (reg < m_program.user_data_base ||
|
||||
reg - m_program.user_data_base >= m_runtime.user_data.size()) {
|
||||
return Fail(error, fmt::format("user SGPR {} is unavailable", reg));
|
||||
return false;
|
||||
}
|
||||
result = m_runtime.user_data[reg - m_program.user_data_base];
|
||||
return true;
|
||||
}
|
||||
case ValueOpcode::GetShaderBase: result = m_runtime.shader_base; return true;
|
||||
case ValueOpcode::Phi: return EvaluatePhi(inst, result, error);
|
||||
case ValueOpcode::Phi: return EvaluatePhi(inst, result);
|
||||
case ValueOpcode::CompositeExtractU64:
|
||||
case ValueOpcode::CompositeExtractU32x2: return EvaluateExtract(inst, result, error);
|
||||
case ValueOpcode::CompositeExtractU32x2: return EvaluateExtract(inst, result);
|
||||
case ValueOpcode::CompositeConstructU64:
|
||||
if (!binary()) {
|
||||
return false;
|
||||
@@ -607,20 +573,19 @@ private:
|
||||
const auto slot = inst.Arg(1).Resolve();
|
||||
if (!slot.IsImmediate() || slot.GetType() != Type::U32 ||
|
||||
slot.U32() >= m_program.srt_reads.size()) {
|
||||
return Fail(error, "invalid flattened SRT index");
|
||||
return false;
|
||||
}
|
||||
if (slot.U32() < m_clean_flat_slots.size() &&
|
||||
m_clean_flat_slots[slot.U32()] != 0u && m_clean_evaluator != nullptr) {
|
||||
m_clean_evaluator->SetUsePc(m_use_pc);
|
||||
return m_clean_evaluator->EvaluateWide(m_program.srt_reads[slot.U32()].value,
|
||||
result, error);
|
||||
result);
|
||||
}
|
||||
return EvaluateWide(m_program.srt_reads[slot.U32()].value, result, error);
|
||||
return EvaluateWide(m_program.srt_reads[slot.U32()].value, result);
|
||||
}
|
||||
case ValueOpcode::LoadAddressU32:
|
||||
case ValueOpcode::ReadConstBuffer:
|
||||
if (IsRawRead(m_program, inst)) {
|
||||
return EvaluateRawRead(inst, result, error);
|
||||
return EvaluateRawRead(inst, result);
|
||||
}
|
||||
break;
|
||||
case ValueOpcode::IAdd32:
|
||||
@@ -690,7 +655,7 @@ private:
|
||||
}
|
||||
return false;
|
||||
case ValueOpcode::BitwiseNot32:
|
||||
if (Arg(inst, 0, a, error)) {
|
||||
if (Arg(inst, 0, a)) {
|
||||
result = ~static_cast<uint32_t>(a);
|
||||
return true;
|
||||
}
|
||||
@@ -737,7 +702,7 @@ private:
|
||||
const auto offset = static_cast<uint32_t>(b);
|
||||
const auto width = static_cast<uint32_t>(c);
|
||||
if (offset > 32u || width > 32u - offset) {
|
||||
return Fail(error, "invalid unsigned bit-field range");
|
||||
return false;
|
||||
}
|
||||
const auto mask = width == 32u ? UINT32_MAX
|
||||
: width == 0u ? 0u
|
||||
@@ -751,7 +716,7 @@ private:
|
||||
const auto offset = static_cast<uint32_t>(b);
|
||||
const auto width = static_cast<uint32_t>(c);
|
||||
if (offset > 32u || width > 32u - offset) {
|
||||
return Fail(error, "invalid signed bit-field range");
|
||||
return false;
|
||||
}
|
||||
if (width == 0u) {
|
||||
result = 0;
|
||||
@@ -768,13 +733,13 @@ private:
|
||||
return false;
|
||||
case ValueOpcode::BitFieldInsert: {
|
||||
uint64_t d = 0;
|
||||
if (!ternary() || !Arg(inst, 3, d, error)) {
|
||||
if (!ternary() || !Arg(inst, 3, d)) {
|
||||
return false;
|
||||
}
|
||||
const auto offset = static_cast<uint32_t>(c);
|
||||
const auto width = static_cast<uint32_t>(d);
|
||||
if (offset > 32u || width > 32u - offset) {
|
||||
return Fail(error, "invalid inserted bit-field range");
|
||||
return false;
|
||||
}
|
||||
if (width == 0u) {
|
||||
result = static_cast<uint32_t>(a);
|
||||
@@ -836,7 +801,7 @@ private:
|
||||
}
|
||||
return false;
|
||||
case ValueOpcode::LogicalNot:
|
||||
if (Arg(inst, 0, a, error)) {
|
||||
if (Arg(inst, 0, a)) {
|
||||
result = a == 0u;
|
||||
return true;
|
||||
}
|
||||
@@ -845,18 +810,16 @@ private:
|
||||
case ValueOpcode::UndefU8:
|
||||
case ValueOpcode::UndefU16:
|
||||
case ValueOpcode::UndefU32:
|
||||
case ValueOpcode::UndefU64: return Fail(error, "undefined typed runtime value");
|
||||
case ValueOpcode::UndefU64: return false;
|
||||
default: break;
|
||||
}
|
||||
return Fail(error, fmt::format("unsupported typed runtime opcode {}",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
return false;
|
||||
}
|
||||
|
||||
const Program& m_program;
|
||||
const SrtRuntime& m_runtime;
|
||||
std::span<const uint8_t> m_clean_flat_slots;
|
||||
Evaluator* m_clean_evaluator = nullptr;
|
||||
uint32_t m_use_pc = 0;
|
||||
std::unordered_map<const Inst*, uint64_t> m_cache;
|
||||
std::vector<const Inst*> m_visiting;
|
||||
};
|
||||
@@ -872,18 +835,12 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
|
||||
std::span<const DescriptorSourceRequest> requests,
|
||||
const SrtRuntime& runtime, std::vector<DescriptorValue>& results,
|
||||
std::vector<uint32_t>& flat, bool evaluate_flat,
|
||||
std::span<const uint8_t> clean_flat_slots, std::string* error) {
|
||||
std::span<const uint8_t> clean_flat_slots) {
|
||||
if (!program.srt_plan_complete) {
|
||||
if (error != nullptr) {
|
||||
*error = Diagnostic(program, 0, "typed SRT plan is not ready");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (std::ranges::any_of(clean_flat_slots, [](uint8_t clean) { return clean != 0u; }) &&
|
||||
runtime.read_specialization_memory == nullptr) {
|
||||
if (error != nullptr) {
|
||||
*error = Diagnostic(program, 0, "clean flattened SRT read has no memory reader");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
SrtRuntime clean_runtime = runtime;
|
||||
@@ -895,17 +852,12 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
|
||||
for (const auto& request: requests) {
|
||||
const auto* source = Source(program, request.source);
|
||||
if (source == nullptr) {
|
||||
if (error != nullptr) {
|
||||
*error = Diagnostic(program, request.use_pc,
|
||||
fmt::format("invalid descriptor source {}", request.source));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
evaluator.SetUsePc(request.use_pc);
|
||||
DescriptorValue value;
|
||||
value.dword_count = source->dword_count;
|
||||
for (uint32_t index = 0; index < source->dword_count; index++) {
|
||||
if (!evaluator.Evaluate(source->dwords[index], value.dwords[index], error)) {
|
||||
if (!evaluator.Evaluate(source->dwords[index], value.dwords[index])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -918,9 +870,8 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
|
||||
const bool clean = read.flat_offset < clean_flat_slots.size() &&
|
||||
clean_flat_slots[read.flat_offset] != 0u;
|
||||
auto& selected = clean ? clean_evaluator : evaluator;
|
||||
selected.SetUsePc(read.use_pc);
|
||||
if (read.flat_offset >= flattened.size() ||
|
||||
!selected.Evaluate(read.value, flattened[read.flat_offset], error)) {
|
||||
!selected.Evaluate(read.value, flattened[read.flat_offset])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -934,31 +885,24 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ValidateRuntimeValue(const Program& program, Value value, std::string& reason) {
|
||||
return RuntimeValidator(program).Run(value, reason);
|
||||
bool ValidateRuntimeValue(const Program& program, Value value) {
|
||||
return RuntimeValidator(program).Run(value);
|
||||
}
|
||||
|
||||
bool BuildSrtPlan(Program& program, std::string* error) {
|
||||
void BuildSrtPlan(Program& program) {
|
||||
if (program.resource_tracking_complete) {
|
||||
if (error != nullptr) {
|
||||
*error = "cannot rebuild SRT after resource tracking";
|
||||
}
|
||||
return false;
|
||||
EXIT("shader SRT planning failed: cannot rebuild SRT after resource tracking");
|
||||
}
|
||||
program.srt_plan_complete = false;
|
||||
if (!PlanBuilder(program).Run(error)) {
|
||||
return false;
|
||||
}
|
||||
PlanBuilder(program).Run();
|
||||
program.srt_plan_complete = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EvaluateDescriptorSource(const Program& program, uint32_t source, uint32_t use_pc,
|
||||
const SrtRuntime& runtime, DescriptorValue& result,
|
||||
std::string* error) {
|
||||
const DescriptorSourceRequest request {source, use_pc};
|
||||
bool EvaluateDescriptorSource(const Program& program, uint32_t source, const SrtRuntime& runtime,
|
||||
DescriptorValue& result) {
|
||||
const DescriptorSourceRequest request {source};
|
||||
std::vector<DescriptorValue> results;
|
||||
if (!EvaluateDescriptorSources(program, std::span {&request, 1}, runtime, results, error)) {
|
||||
if (!EvaluateDescriptorSources(program, std::span {&request, 1}, runtime, results)) {
|
||||
return false;
|
||||
}
|
||||
result = results.front();
|
||||
@@ -967,26 +911,23 @@ bool EvaluateDescriptorSource(const Program& program, uint32_t source, uint32_t
|
||||
|
||||
bool EvaluateDescriptorSources(const Program& program,
|
||||
std::span<const DescriptorSourceRequest> requests,
|
||||
const SrtRuntime& runtime, std::vector<DescriptorValue>& results,
|
||||
std::string* error) {
|
||||
const SrtRuntime& runtime, std::vector<DescriptorValue>& results) {
|
||||
std::vector<uint32_t> ignored;
|
||||
return EvaluateRuntimeSourcesImpl(program, requests, runtime, results, ignored, false, {},
|
||||
error);
|
||||
return EvaluateRuntimeSourcesImpl(program, requests, runtime, results, ignored, false, {});
|
||||
}
|
||||
|
||||
bool EvaluateRuntimeSources(const Program& program,
|
||||
std::span<const DescriptorSourceRequest> requests,
|
||||
const SrtRuntime& runtime, std::vector<DescriptorValue>& results,
|
||||
std::vector<uint32_t>& flat, std::span<const uint8_t> clean_flat_slots,
|
||||
std::string* error) {
|
||||
std::vector<uint32_t>& flat,
|
||||
std::span<const uint8_t> clean_flat_slots) {
|
||||
return EvaluateRuntimeSourcesImpl(program, requests, runtime, results, flat, true,
|
||||
clean_flat_slots, error);
|
||||
clean_flat_slots);
|
||||
}
|
||||
|
||||
bool WalkSrt(const Program& program, const SrtRuntime& runtime, std::vector<uint32_t>& flat,
|
||||
std::string* error) {
|
||||
bool WalkSrt(const Program& program, const SrtRuntime& runtime, std::vector<uint32_t>& flat) {
|
||||
std::vector<DescriptorValue> ignored;
|
||||
return EvaluateRuntimeSources(program, {}, runtime, ignored, flat, {}, error);
|
||||
return EvaluateRuntimeSources(program, {}, runtime, ignored, flat, {});
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
@@ -13,43 +13,38 @@ using SrtMemoryReader = bool (*)(void* userdata, uint64_t address, uint32_t* val
|
||||
|
||||
struct SrtRuntime {
|
||||
std::span<const uint32_t> user_data;
|
||||
uint64_t shader_base = 0;
|
||||
SrtMemoryReader read_memory = nullptr;
|
||||
void* userdata = nullptr;
|
||||
uint64_t shader_base = 0;
|
||||
SrtMemoryReader read_memory = nullptr;
|
||||
void* userdata = nullptr;
|
||||
SrtMemoryReader read_specialization_memory = nullptr;
|
||||
};
|
||||
|
||||
struct DescriptorSourceRequest {
|
||||
uint32_t source = 0;
|
||||
uint32_t use_pc = 0;
|
||||
};
|
||||
|
||||
// Collects reachable ReadConst values. Immediate offsets receive compact flat-buffer slots;
|
||||
// dynamic offsets remain explicit and are never assigned a fake slot.
|
||||
bool BuildSrtPlan(Program& program, std::string* error);
|
||||
bool ValidateRuntimeValue(const Program& program, Value value, std::string& reason);
|
||||
void BuildSrtPlan(Program& program);
|
||||
bool ValidateRuntimeValue(const Program& program, Value value);
|
||||
|
||||
bool EvaluateDescriptorSource(const Program& program, uint32_t source, uint32_t use_pc,
|
||||
const SrtRuntime& runtime, DescriptorValue& result,
|
||||
std::string* error);
|
||||
bool EvaluateDescriptorSource(const Program& program, uint32_t source, const SrtRuntime& runtime,
|
||||
DescriptorValue& result);
|
||||
|
||||
// Evaluates one runtime snapshot transactionally. Scalar values and ReadConst results shared by
|
||||
// several descriptors are memoized once across the batch.
|
||||
bool EvaluateDescriptorSources(const Program& program,
|
||||
std::span<const DescriptorSourceRequest> requests,
|
||||
const SrtRuntime& runtime, std::vector<DescriptorValue>& results,
|
||||
std::string* error);
|
||||
const SrtRuntime& runtime, std::vector<DescriptorValue>& results);
|
||||
|
||||
// Evaluates descriptor sources and the flattened immediate SRT with one memoized scalar walk.
|
||||
// On failure neither destination is changed.
|
||||
bool EvaluateRuntimeSources(const Program& program,
|
||||
std::span<const DescriptorSourceRequest> requests,
|
||||
const SrtRuntime& runtime, std::vector<DescriptorValue>& results,
|
||||
std::vector<uint32_t>& flat, std::span<const uint8_t> clean_flat_slots,
|
||||
std::string* error);
|
||||
std::vector<uint32_t>& flat, std::span<const uint8_t> clean_flat_slots);
|
||||
|
||||
bool WalkSrt(const Program& program, const SrtRuntime& runtime, std::vector<uint32_t>& flat,
|
||||
std::string* error);
|
||||
bool WalkSrt(const Program& program, const SrtRuntime& runtime, std::vector<uint32_t>& flat);
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ struct ScalarMaskTag {
|
||||
struct ExecTag {
|
||||
auto operator<=>(const ExecTag&) const = default;
|
||||
};
|
||||
struct ExecMaskTag {
|
||||
auto operator<=>(const ExecMaskTag&) const = default;
|
||||
};
|
||||
struct ExecLoTag {
|
||||
auto operator<=>(const ExecLoTag&) const = default;
|
||||
};
|
||||
@@ -31,6 +34,9 @@ struct ExecHiTag {
|
||||
struct VccTag {
|
||||
auto operator<=>(const VccTag&) const = default;
|
||||
};
|
||||
struct VccMaskValidTag {
|
||||
auto operator<=>(const VccMaskValidTag&) const = default;
|
||||
};
|
||||
struct VccLoTag {
|
||||
auto operator<=>(const VccLoTag&) const = default;
|
||||
};
|
||||
@@ -45,9 +51,9 @@ struct GotoVariable {
|
||||
auto operator<=>(const GotoVariable&) const = default;
|
||||
};
|
||||
|
||||
using Variable =
|
||||
std::variant<ScalarReg, ThreadBitScalarReg, ScalarMaskTag, VectorReg, GotoVariable, SccTag,
|
||||
ExecTag, ExecLoTag, ExecHiTag, VccTag, VccLoTag, VccHiTag, M0Tag>;
|
||||
using Variable = std::variant<ScalarReg, ThreadBitScalarReg, ScalarMaskTag, VectorReg, GotoVariable,
|
||||
SccTag, ExecTag, ExecMaskTag, ExecLoTag, ExecHiTag, VccTag,
|
||||
VccMaskValidTag, VccLoTag, VccHiTag, M0Tag>;
|
||||
using ValueMap = std::unordered_map<Block*, Value>;
|
||||
|
||||
struct DefTable {
|
||||
@@ -99,12 +105,16 @@ struct DefTable {
|
||||
void Set(Block* block, SccTag, Value value) { scc[block] = value; }
|
||||
const Value& Get(Block* block, ExecTag) { return exec[block]; }
|
||||
void Set(Block* block, ExecTag, Value value) { exec[block] = value; }
|
||||
const Value& Get(Block* block, ExecMaskTag) { return exec_mask_tag[block]; }
|
||||
void Set(Block* block, ExecMaskTag, Value value) { exec_mask_tag[block] = value; }
|
||||
const Value& Get(Block* block, ExecLoTag) { return exec_lo[block]; }
|
||||
void Set(Block* block, ExecLoTag, Value value) { exec_lo[block] = value; }
|
||||
const Value& Get(Block* block, ExecHiTag) { return exec_hi[block]; }
|
||||
void Set(Block* block, ExecHiTag, Value value) { exec_hi[block] = value; }
|
||||
const Value& Get(Block* block, VccTag) { return vcc[block]; }
|
||||
void Set(Block* block, VccTag, Value value) { vcc[block] = value; }
|
||||
const Value& Get(Block* block, VccMaskValidTag) { return vcc_mask_valid_tag[block]; }
|
||||
void Set(Block* block, VccMaskValidTag, Value value) { vcc_mask_valid_tag[block] = value; }
|
||||
const Value& Get(Block* block, VccLoTag) { return vcc_lo[block]; }
|
||||
void Set(Block* block, VccLoTag, Value value) { vcc_lo[block] = value; }
|
||||
const Value& Get(Block* block, VccHiTag) { return vcc_hi[block]; }
|
||||
@@ -114,9 +124,11 @@ struct DefTable {
|
||||
|
||||
ValueMap scc;
|
||||
ValueMap exec;
|
||||
ValueMap exec_mask_tag;
|
||||
ValueMap exec_lo;
|
||||
ValueMap exec_hi;
|
||||
ValueMap vcc;
|
||||
ValueMap vcc_mask_valid_tag;
|
||||
ValueMap vcc_lo;
|
||||
ValueMap vcc_hi;
|
||||
ValueMap m0;
|
||||
@@ -144,6 +156,9 @@ ValueOpcode UndefOpcode(SccTag) {
|
||||
ValueOpcode UndefOpcode(ExecTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ExecMaskTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(ExecLoTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
@@ -153,6 +168,9 @@ ValueOpcode UndefOpcode(ExecHiTag) {
|
||||
ValueOpcode UndefOpcode(VccTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VccMaskValidTag) {
|
||||
return ValueOpcode::UndefU1;
|
||||
}
|
||||
ValueOpcode UndefOpcode(VccLoTag) {
|
||||
return ValueOpcode::UndefU32;
|
||||
}
|
||||
@@ -184,6 +202,9 @@ Value InitialValue(SccTag) {
|
||||
Value InitialValue(ExecTag) {
|
||||
return Value(false);
|
||||
}
|
||||
Value InitialValue(ExecMaskTag) {
|
||||
return Value(false);
|
||||
}
|
||||
Value InitialValue(ExecLoTag) {
|
||||
return Value(0u);
|
||||
}
|
||||
@@ -193,6 +214,9 @@ Value InitialValue(ExecHiTag) {
|
||||
Value InitialValue(VccTag) {
|
||||
return Value(false);
|
||||
}
|
||||
Value InitialValue(VccMaskValidTag) {
|
||||
return Value(false);
|
||||
}
|
||||
Value InitialValue(VccLoTag) {
|
||||
return Value(0u);
|
||||
}
|
||||
@@ -351,9 +375,15 @@ void VisitInstruction(Pass& pass, Block* block, Inst& inst) {
|
||||
break;
|
||||
case ValueOpcode::SetScc: pass.Write(SccTag {}, block, inst.Arg(0)); break;
|
||||
case ValueOpcode::SetExec: pass.Write(ExecTag {}, block, inst.Arg(0)); break;
|
||||
case ValueOpcode::SetExecMaskTag:
|
||||
pass.Write(ExecMaskTag {}, block, inst.Arg(0));
|
||||
break;
|
||||
case ValueOpcode::SetExecLo: pass.Write(ExecLoTag {}, block, inst.Arg(0)); break;
|
||||
case ValueOpcode::SetExecHi: pass.Write(ExecHiTag {}, block, inst.Arg(0)); break;
|
||||
case ValueOpcode::SetVcc: pass.Write(VccTag {}, block, inst.Arg(0)); break;
|
||||
case ValueOpcode::SetVccMaskValidTag:
|
||||
pass.Write(VccMaskValidTag {}, block, inst.Arg(0));
|
||||
break;
|
||||
case ValueOpcode::SetVccLo: pass.Write(VccLoTag {}, block, inst.Arg(0)); break;
|
||||
case ValueOpcode::SetVccHi: pass.Write(VccHiTag {}, block, inst.Arg(0)); break;
|
||||
case ValueOpcode::SetM0: pass.Write(M0Tag {}, block, inst.Arg(0)); break;
|
||||
@@ -375,9 +405,15 @@ void VisitInstruction(Pass& pass, Block* block, Inst& inst) {
|
||||
break;
|
||||
case ValueOpcode::GetScc: inst.ReplaceUsesWith(pass.Read(SccTag {}, block)); break;
|
||||
case ValueOpcode::GetExec: inst.ReplaceUsesWith(pass.Read(ExecTag {}, block)); break;
|
||||
case ValueOpcode::GetExecMaskTag:
|
||||
inst.ReplaceUsesWith(pass.Read(ExecMaskTag {}, block));
|
||||
break;
|
||||
case ValueOpcode::GetExecLo: inst.ReplaceUsesWith(pass.Read(ExecLoTag {}, block)); break;
|
||||
case ValueOpcode::GetExecHi: inst.ReplaceUsesWith(pass.Read(ExecHiTag {}, block)); break;
|
||||
case ValueOpcode::GetVcc: inst.ReplaceUsesWith(pass.Read(VccTag {}, block)); break;
|
||||
case ValueOpcode::GetVccMaskValidTag:
|
||||
inst.ReplaceUsesWith(pass.Read(VccMaskValidTag {}, block));
|
||||
break;
|
||||
case ValueOpcode::GetVccLo: inst.ReplaceUsesWith(pass.Read(VccLoTag {}, block)); break;
|
||||
case ValueOpcode::GetVccHi: inst.ReplaceUsesWith(pass.Read(VccHiTag {}, block)); break;
|
||||
case ValueOpcode::GetM0: inst.ReplaceUsesWith(pass.Read(M0Tag {}, block)); break;
|
||||
|
||||
@@ -173,6 +173,11 @@ static const ShaderBinaryInfo* GetBinaryInfo(const uint32_t* code) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static uint64_t GetDeclaredShaderHash(uint64_t shader_addr) {
|
||||
const auto* header = GetBinaryInfo(reinterpret_cast<const uint32_t*>(shader_addr));
|
||||
return header != nullptr ? (static_cast<uint64_t>(header->hash1) << 32u) | header->hash0 : 0;
|
||||
}
|
||||
|
||||
static ShaderParams GetShaderParams(uint64_t shader_addr, const char* label, uint64_t declared_hash,
|
||||
std::span<const uint32_t> user_data,
|
||||
const ShaderMappedData& data) {
|
||||
@@ -210,7 +215,6 @@ static void vs_check(const HW::VertexShaderInfo& vs, const HW::ShaderRegisters&
|
||||
};
|
||||
const bool ps5_ngg_passthrough_triangle_path =
|
||||
vs.es_regs.data_addr != 0 && vs.gs_regs.data_addr == vs.es_regs.data_addr &&
|
||||
vs.gs_regs.chksum != 0 &&
|
||||
sh.m_geNggSubgrpCntl == 0x00000001 && sh.m_vgtGsMaxVertOut == 0x00000003 &&
|
||||
sh.m_vgtGsOutPrimType == 0x00000002 && sh.m_geMaxOutputPerSubgroup <= 0x000000c0;
|
||||
|
||||
@@ -275,7 +279,6 @@ static void ps_check(const HW::PsStageRegisters& ps, const HW::ShaderRegisters&
|
||||
// EXIT_NOT_IMPLEMENTED(ps.m_spiShaderPgmRsrc1Ps != 0x002c0000);
|
||||
// EXIT_NOT_IMPLEMENTED(ps.m_spiShaderPgmRsrc2Ps != 0x00000000);
|
||||
// EXIT_NOT_IMPLEMENTED(ps.vgprs != 0x00 && ps.vgprs != 0x01);
|
||||
// EXIT_NOT_IMPLEMENTED(ps.sgprs != 0x00 && ps.sgprs != 0x01);
|
||||
EXIT_NOT_IMPLEMENTED(ps.rsrc1.priority != 0);
|
||||
EXIT_NOT_IMPLEMENTED(ps.rsrc1.float_mode != 192);
|
||||
EXIT_NOT_IMPLEMENTED(ps.rsrc1.dx10_clamp != true);
|
||||
@@ -365,8 +368,10 @@ static void cs_check(const HW::CsStageRegisters& cs, const HW::ShaderRegisters&
|
||||
// EXIT_NOT_IMPLEMENTED(cs.num_thread_y != 0x00000001);
|
||||
// EXIT_NOT_IMPLEMENTED(cs.num_thread_z != 0x00000001);
|
||||
// EXIT_NOT_IMPLEMENTED(cs.vgprs != 0x00 && cs.vgprs != 0x01);
|
||||
// EXIT_NOT_IMPLEMENTED(cs.sgprs != 0x01 && cs.sgprs != 0x02);
|
||||
EXIT_NOT_IMPLEMENTED(cs.bulky != 0x00);
|
||||
EXIT_NOT_IMPLEMENTED(cs.priority != 0x00);
|
||||
EXIT_NOT_IMPLEMENTED(cs.debug_mode != false);
|
||||
EXIT_NOT_IMPLEMENTED(cs.require_forward_progress != false);
|
||||
EXIT_NOT_IMPLEMENTED(cs.shared_vgprs != 0x00);
|
||||
EXIT_NOT_IMPLEMENTED(cs.scratch_en != 0x00);
|
||||
// EXIT_NOT_IMPLEMENTED(cs.user_sgpr != 0x0c);
|
||||
if (cs.tgid_x_en == 0x00) {
|
||||
@@ -689,7 +694,7 @@ static bool ShaderGetStaticInputInfoVS(const HW::VertexShaderInfo& regs,
|
||||
info.export_count = static_cast<int>(sh.GetExportCount());
|
||||
info.pa_cl_vs_out_cntl = sh.m_paClVsOutCntl;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(regs.es_regs.data_addr == 0 || regs.gs_regs.chksum == 0);
|
||||
EXIT_NOT_IMPLEMENTED(regs.es_regs.data_addr == 0);
|
||||
|
||||
uint64_t shader_addr = regs.es_regs.data_addr;
|
||||
const HW::UserSgprInfo& user_sgpr = regs.gs_user_sgpr;
|
||||
@@ -698,8 +703,8 @@ static bool ShaderGetStaticInputInfoVS(const HW::VertexShaderInfo& regs,
|
||||
|
||||
if (data.user_data == nullptr) {
|
||||
LOGF("ShaderGetInputInfoVS(): no AGC user data for shader=0x%016" PRIx64 " es=0x%016" PRIx64
|
||||
" gs=0x%016" PRIx64 " chksum=0x%016" PRIx64 " user_sgpr_num=%u\n",
|
||||
shader_addr, regs.es_regs.data_addr, regs.gs_regs.data_addr, regs.gs_regs.chksum,
|
||||
" gs=0x%016" PRIx64 " user_sgpr_num=%u\n",
|
||||
shader_addr, regs.es_regs.data_addr, regs.gs_regs.data_addr,
|
||||
static_cast<uint32_t>(user_sgpr_num));
|
||||
}
|
||||
ShaderVertexMetadata metadata;
|
||||
@@ -841,23 +846,22 @@ static void ApplyPixelOutputs(ShaderPixelInputInfo& info,
|
||||
}
|
||||
}
|
||||
|
||||
static bool LogPermutationMismatch(const ShaderRecompiler::IR::Program& program,
|
||||
const char* stage, const std::string& error) {
|
||||
static bool LogPermutationMismatch(const ShaderRecompiler::IR::Program* program,
|
||||
const char* stage) {
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (Config::GraphicsDebugDumpEnabled() &&
|
||||
log_count.fetch_add(1, std::memory_order_relaxed) < 64) {
|
||||
LOGF("ShaderProgramCache native runtime mismatch %s shader=0x%016" PRIx64 ": %s\n",
|
||||
stage, program.shader_hash, error.c_str());
|
||||
LOGF("ShaderProgramCache native runtime mismatch %s shader=0x%016" PRIx64 "\n",
|
||||
stage, program != nullptr ? program->shader_hash : 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MaterializeProgram(const std::shared_ptr<const ShaderRecompiler::IR::Program>& program,
|
||||
const ShaderParams& params, ShaderVertexInputInfo& info) {
|
||||
std::string error;
|
||||
if (!ShaderMaterializeStageRuntime(program, params.user_data, params.Base(), info.stage, &error,
|
||||
if (!ShaderMaterializeStageRuntime(program, params.user_data, params.Base(), info.stage,
|
||||
ReadShaderGuestMemory)) {
|
||||
return LogPermutationMismatch(*program, "VS", error);
|
||||
return LogPermutationMismatch(program.get(), "VS");
|
||||
}
|
||||
ApplyVertexOutputs(info, *program);
|
||||
return true;
|
||||
@@ -865,10 +869,9 @@ bool MaterializeProgram(const std::shared_ptr<const ShaderRecompiler::IR::Progra
|
||||
|
||||
bool MaterializeProgram(const std::shared_ptr<const ShaderRecompiler::IR::Program>& program,
|
||||
const ShaderParams& params, ShaderPixelInputInfo& info) {
|
||||
std::string error;
|
||||
if (!ShaderMaterializeStageRuntime(program, params.user_data, params.Base(), info.stage, &error,
|
||||
if (!ShaderMaterializeStageRuntime(program, params.user_data, params.Base(), info.stage,
|
||||
ReadShaderGuestMemory)) {
|
||||
return LogPermutationMismatch(*program, "PS", error);
|
||||
return LogPermutationMismatch(program.get(), "PS");
|
||||
}
|
||||
ApplyPixelOutputs(info, *program);
|
||||
return true;
|
||||
@@ -876,10 +879,9 @@ bool MaterializeProgram(const std::shared_ptr<const ShaderRecompiler::IR::Progra
|
||||
|
||||
bool MaterializeProgram(const std::shared_ptr<const ShaderRecompiler::IR::Program>& program,
|
||||
const ShaderParams& params, ShaderComputeInputInfo& info) {
|
||||
std::string error;
|
||||
if (!ShaderMaterializeStageRuntime(program, params.user_data, params.Base(), info.stage, &error,
|
||||
if (!ShaderMaterializeStageRuntime(program, params.user_data, params.Base(), info.stage,
|
||||
ReadShaderGuestMemory)) {
|
||||
return LogPermutationMismatch(*program, "CS", error);
|
||||
return LogPermutationMismatch(program.get(), "CS");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -982,7 +984,7 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::ShaderRe
|
||||
EXIT("failed to prepare vertex shader program\n");
|
||||
}
|
||||
return GetShaderParams(
|
||||
regs.es_regs.data_addr, "ShaderRecompiler VS", regs.gs_regs.chksum,
|
||||
regs.es_regs.data_addr, "ShaderRecompiler VS", GetDeclaredShaderHash(regs.es_regs.data_addr),
|
||||
std::span<const uint32_t>(regs.gs_user_sgpr.value, regs.gs_regs.rsrc2.user_sgpr), data);
|
||||
}
|
||||
|
||||
@@ -994,7 +996,7 @@ ShaderParams PrepareProgram(
|
||||
const auto data = ShaderGetMappedData(regs.ps_regs.data_addr, "ShaderGetInputInfoPS():");
|
||||
ShaderGetStaticInputInfoPS(regs, sh, vs_info, target_export_mapping, data, ps_info);
|
||||
return GetShaderParams(
|
||||
regs.ps_regs.data_addr, "ShaderRecompiler PS", regs.ps_regs.chksum,
|
||||
regs.ps_regs.data_addr, "ShaderRecompiler PS", GetDeclaredShaderHash(regs.ps_regs.data_addr),
|
||||
std::span<const uint32_t>(regs.ps_user_sgpr.value, regs.ps_regs.rsrc2.user_sgpr), data);
|
||||
}
|
||||
|
||||
@@ -1002,11 +1004,8 @@ ShaderParams PrepareProgram(const HW::ComputeShaderInfo& regs, const HW::ShaderR
|
||||
ShaderComputeInputInfo& info) {
|
||||
const auto data = ShaderGetMappedData(regs.cs_regs.data_addr, "ShaderGetInputInfoCS():");
|
||||
ShaderGetStaticInputInfoCS(regs, sh, data, info);
|
||||
const auto* header = GetBinaryInfo(reinterpret_cast<const uint32_t*>(regs.cs_regs.data_addr));
|
||||
const auto declared_hash =
|
||||
header != nullptr ? (static_cast<uint64_t>(header->hash1) << 32u) | header->hash0 : 0;
|
||||
return GetShaderParams(
|
||||
regs.cs_regs.data_addr, "ShaderRecompiler CS", declared_hash,
|
||||
regs.cs_regs.data_addr, "ShaderRecompiler CS", GetDeclaredShaderHash(regs.cs_regs.data_addr),
|
||||
std::span<const uint32_t>(regs.cs_user_sgpr.value, regs.cs_regs.user_sgpr), data);
|
||||
}
|
||||
|
||||
@@ -1237,9 +1236,7 @@ static ShaderRecompiler::CompileOptions MakeCompileOptions(const ShaderParams& p
|
||||
ShaderRecompiler::CompileOptions options;
|
||||
options.stage = stage;
|
||||
options.shader_hash = params.hash;
|
||||
options.shader_base = params.Base();
|
||||
options.user_data_count = static_cast<uint32_t>(params.user_data.size());
|
||||
options.user_data = params.user_data.data();
|
||||
options.user_data = params.user_data;
|
||||
options.read_specialization_memory = ReadShaderGuestMemory;
|
||||
options.dump_ir = ShaderRecompilerTextDumpEnabled();
|
||||
options.early_dump = options.dump_ir;
|
||||
@@ -1252,11 +1249,7 @@ static vk::ShaderModule CompileModule(vk::Device device, const ShaderParams& par
|
||||
ShaderStageRuntime& stage) {
|
||||
const auto* stage_name = ShaderStageName(options.stage);
|
||||
const auto* label = ShaderStageLabel(options.stage);
|
||||
ShaderRecompiler::CompileResult result;
|
||||
std::string error;
|
||||
if (!ShaderRecompiler::TryRecompile(params.code, options, result, &error)) {
|
||||
ExitShaderRecompilerFailure(label, options.shader_hash, error.c_str());
|
||||
}
|
||||
auto result = ShaderRecompiler::Recompile(params.code, options);
|
||||
|
||||
DumpShaderRecompilerOriginal(stage_name, options.shader_hash, params.code, result.decoded_dump);
|
||||
if (!SpirvValidateBinary(label, options.shader_hash, result.spirv)) {
|
||||
|
||||
@@ -57,7 +57,7 @@ using ShaderSpecializationMemoryReader = bool (*)(void* userdata, uint64_t addre
|
||||
bool ShaderMaterializeStageRuntime(
|
||||
std::shared_ptr<const ShaderRecompiler::IR::Program> program,
|
||||
std::span<const uint32_t> user_data, uint64_t shader_base, ShaderStageRuntime& stage,
|
||||
std::string* error, ShaderSpecializationMemoryReader read_specialization_memory = nullptr,
|
||||
ShaderSpecializationMemoryReader read_specialization_memory = nullptr,
|
||||
void* read_memory_data = nullptr);
|
||||
|
||||
constexpr uint32_t DstSel(uint32_t x, uint32_t y = 0, uint32_t z = 0, uint32_t w = 0) {
|
||||
|
||||
@@ -7,13 +7,10 @@ namespace Libs::Graphics {
|
||||
|
||||
bool ShaderMaterializeStageRuntime(std::shared_ptr<const ShaderRecompiler::IR::Program> program,
|
||||
std::span<const uint32_t> user_data, uint64_t shader_base,
|
||||
ShaderStageRuntime& stage, std::string* error,
|
||||
ShaderStageRuntime& stage,
|
||||
ShaderSpecializationMemoryReader read_specialization_memory,
|
||||
void* read_memory_data) {
|
||||
if (program == nullptr) {
|
||||
if (error != nullptr) {
|
||||
*error = "missing native shader plan";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ShaderRecompiler::IR::SrtRuntime runtime;
|
||||
@@ -22,8 +19,8 @@ bool ShaderMaterializeStageRuntime(std::shared_ptr<const ShaderRecompiler::IR::P
|
||||
runtime.read_specialization_memory = read_specialization_memory;
|
||||
runtime.userdata = read_memory_data;
|
||||
ShaderRecompiler::IR::ResourceSnapshot snapshot;
|
||||
if (!ShaderRecompiler::IR::MaterializeResources(*program, runtime, snapshot, error) ||
|
||||
!ShaderRecompiler::IR::ValidateResourceSpecialization(*program, snapshot, error)) {
|
||||
if (!ShaderRecompiler::IR::MaterializeResources(*program, runtime, snapshot) ||
|
||||
!ShaderRecompiler::IR::ValidateResourceSpecialization(*program, snapshot)) {
|
||||
return false;
|
||||
}
|
||||
auto resources =
|
||||
|
||||
+40
-22
@@ -145,7 +145,6 @@ struct RegisterDefaultsStorage {
|
||||
|
||||
static constexpr uint32_t GRAPHICS_REGISTER_DEFAULTS_MAX_VERSION = 13;
|
||||
static constexpr uint32_t GRAPHICS_REGISTER_DEFAULTS_FALLBACK_VERSION = 13;
|
||||
static constexpr uint32_t GRAPHICS_INIT_NO_FEATURE_STATE = 0;
|
||||
|
||||
static std::mutex g_register_defaults_mutex;
|
||||
|
||||
@@ -336,20 +335,12 @@ int KYTY_SYSV_ABI AgcInit(uint32_t* state, uint32_t ver) {
|
||||
"\t ver = %u\n",
|
||||
reinterpret_cast<uint64_t>(state), ver);
|
||||
|
||||
if (state == nullptr) {
|
||||
LOGF_COLOR(Log::Color::Red, "\t state is null\n");
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (ver > GRAPHICS_REGISTER_DEFAULTS_MAX_VERSION) {
|
||||
LOGF_COLOR(Log::Color::Red, "\t unsupported version %u\n", ver);
|
||||
}
|
||||
|
||||
printf("version = %u\n", ver);
|
||||
|
||||
state[0] = ver;
|
||||
state[1] = GRAPHICS_INIT_NO_FEATURE_STATE;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -903,8 +894,12 @@ static bool is_native_reg_indirect_packet(const uint32_t* cmd, RegIndirectPacket
|
||||
return ((cmd[0] >> 8u) & 0xffu) == reg_indirect_native_op(type);
|
||||
}
|
||||
|
||||
static constexpr bool is_trinity_mode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t reg_indirect_pm4_r(RegIndirectPacket type) {
|
||||
return (type == RegIndirectPacket::Uc && AgcGetIsTrinityMode() != 0 ? 1u : 0u);
|
||||
return (type == RegIndirectPacket::Uc && is_trinity_mode() ? 1u : 0u);
|
||||
}
|
||||
|
||||
static void reg_indirect_write_packet(uint32_t* cmd, uint64_t vaddr, uint32_t num_regs,
|
||||
@@ -1574,15 +1569,6 @@ int KYTY_SYSV_ABI AgcWriteDataPatchSetAddressOrOffset(uint32_t* cmd, uint64_t ad
|
||||
return GRAPHICS5_ERROR_INVALID_PACKET;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI AgcUnknownJumpPatchSetTarget(uint32_t* cmd, const volatile uint32_t* target,
|
||||
uint32_t size_in_dwords) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("Calling unknown stub");
|
||||
|
||||
return AgcJumpPatchSetTarget(cmd, target, size_in_dwords);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI AgcJumpPatchSetTarget(uint32_t* cmd, const volatile uint32_t* target,
|
||||
uint32_t size_in_dwords) {
|
||||
PRINT_NAME();
|
||||
@@ -1690,9 +1676,8 @@ uint64_t KYTY_SYSV_ABI AgcDcbContextStateOpGetSize(uint32_t operation) {
|
||||
return static_cast<uint64_t>(context_state_op_size_dw(operation)) * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
uint64_t KYTY_SYSV_ABI AgcGetIsTrinityMode() {
|
||||
// PS5 Pro?
|
||||
return 0;
|
||||
void KYTY_SYSV_ABI AgcGetIsTrinityMode(uint8_t* result) {
|
||||
*result = is_trinity_mode();
|
||||
}
|
||||
|
||||
static constexpr int GRAPHICS5_DRIVER_ERROR_INVALID_VALUE = static_cast<int>(0x8a6c0033u);
|
||||
@@ -3240,6 +3225,27 @@ uint32_t KYTY_SYSV_ABI AgcAcbCondExecGetSize() {
|
||||
return AgcDcbCondExecGetSize();
|
||||
}
|
||||
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbJump(CommandBuffer* buf, uint8_t cache_policy,
|
||||
const uint32_t* target, uint32_t size_in_dwords) {
|
||||
if (buf == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* cmd = buf->AllocateDW(4);
|
||||
if (cmd == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto target_address = reinterpret_cast<uint64_t>(target);
|
||||
cmd[0] = KYTY_PM4(4, Pm4::IT_INDIRECT_BUFFER, 0u);
|
||||
cmd[1] = static_cast<uint32_t>(target_address) & ~0x3u;
|
||||
cmd[2] = static_cast<uint32_t>(target_address >> 32u);
|
||||
cmd[3] = 0x0f900000u | ((static_cast<uint32_t>(cache_policy) & 0x3u) << 28u) |
|
||||
(size_in_dwords & 0xfffffu);
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
uint32_t KYTY_SYSV_ABI AgcAcbJumpGetSize() {
|
||||
return 0x10u;
|
||||
}
|
||||
@@ -3251,6 +3257,12 @@ uint32_t* KYTY_SYSV_ABI AgcAcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8
|
||||
poll_cycles);
|
||||
}
|
||||
|
||||
uint64_t KYTY_SYSV_ABI AgcAcbWaitOnAddressGetSize(uint8_t size) {
|
||||
PRINT_NAME();
|
||||
|
||||
return AgcDcbWaitOnAddressGetSize(size);
|
||||
}
|
||||
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbDmaData(CommandBuffer* buf, uint8_t dst, uint8_t dst_cache_policy,
|
||||
uint64_t dst_address_or_offset, uint8_t src,
|
||||
uint8_t src_cache_policy,
|
||||
@@ -3322,6 +3334,12 @@ uint32_t* KYTY_SYSV_ABI AgcAcbCopyData(CommandBuffer* buf, uint8_t dst, uint8_t
|
||||
return cmd;
|
||||
}
|
||||
|
||||
uint64_t KYTY_SYSV_ABI AgcAcbCopyDataGetSize() {
|
||||
PRINT_NAME();
|
||||
|
||||
return AgcDcbCopyDataGetSize();
|
||||
}
|
||||
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbDispatchIndirect(CommandBuffer* buf,
|
||||
const volatile void* indirect_args,
|
||||
uint32_t modifier) {
|
||||
|
||||
+5
-3
@@ -71,14 +71,12 @@ int KYTY_SYSV_ABI AgcCreateInterpolantMapping2(ShaderRegister* regs, const Shade
|
||||
int KYTY_SYSV_ABI AgcGetDataPacketPayloadAddress(uint32_t** addr, uint32_t* cmd, int type);
|
||||
int KYTY_SYSV_ABI AgcGetDataPacketPayloadRange(MemoryRange* range, uint32_t* cmd, int type);
|
||||
int KYTY_SYSV_ABI AgcWriteDataPatchSetAddressOrOffset(uint32_t* cmd, uint64_t address_or_offset);
|
||||
int KYTY_SYSV_ABI AgcUnknownJumpPatchSetTarget(uint32_t* cmd, const volatile uint32_t* target,
|
||||
uint32_t size_in_dwords);
|
||||
int KYTY_SYSV_ABI AgcJumpPatchSetTarget(uint32_t* cmd, const volatile uint32_t* target,
|
||||
uint32_t size_in_dwords);
|
||||
int KYTY_SYSV_ABI AgcSuspendPoint();
|
||||
uint32_t* KYTY_SYSV_ABI AgcDcbContextStateOp(CommandBuffer* buf, uint32_t operation);
|
||||
uint64_t KYTY_SYSV_ABI AgcDcbContextStateOpGetSize(uint32_t operation);
|
||||
uint64_t KYTY_SYSV_ABI AgcGetIsTrinityMode();
|
||||
void KYTY_SYSV_ABI AgcGetIsTrinityMode(uint8_t* result);
|
||||
|
||||
uint32_t KYTY_SYSV_ABI AgcDriverGetDefaultOwner();
|
||||
uint32_t KYTY_SYSV_ABI AgcDriverGetResourceRegistrationMaxNameLength();
|
||||
@@ -204,10 +202,13 @@ uint32_t KYTY_SYSV_ABI AgcAcbAcquireMemGetSize();
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbCondExec(CommandBuffer* buf, const volatile uint32_t* address,
|
||||
uint32_t num_dwords);
|
||||
uint32_t KYTY_SYSV_ABI AgcAcbCondExecGetSize();
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbJump(CommandBuffer* buf, uint8_t cache_policy,
|
||||
const uint32_t* target, uint32_t size_in_dwords);
|
||||
uint32_t KYTY_SYSV_ABI AgcAcbJumpGetSize();
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8_t compare_function,
|
||||
uint8_t cache_policy, const volatile void* address,
|
||||
uint64_t reference, uint64_t mask, uint32_t poll_cycles);
|
||||
uint64_t KYTY_SYSV_ABI AgcAcbWaitOnAddressGetSize(uint8_t size);
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbDmaData(CommandBuffer* buf, uint8_t dst, uint8_t dst_cache_policy,
|
||||
uint64_t dst_address_or_offset, uint8_t src,
|
||||
uint8_t src_cache_policy,
|
||||
@@ -218,6 +219,7 @@ uint32_t* KYTY_SYSV_ABI AgcAcbCopyData(CommandBuffer* buf, uint8_t dst, uint8_t
|
||||
uint64_t dst_address, uint8_t src, uint8_t src_cache_policy,
|
||||
uint64_t src_address_or_immediate, uint8_t item_size,
|
||||
uint8_t write_confirm);
|
||||
uint64_t KYTY_SYSV_ABI AgcAcbCopyDataGetSize();
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbDispatchIndirect(CommandBuffer* buf,
|
||||
const volatile void* indirect_args,
|
||||
uint32_t modifier);
|
||||
|
||||
@@ -37,7 +37,6 @@ LIB_DEFINE(InitAgcDriver_1) {
|
||||
LIB_FUNC("V++UgBtQhn0", Gen5::AgcGetDataPacketPayloadAddress);
|
||||
LIB_FUNC("s+VGAMDQ0AQ", Gen5::AgcGetDataPacketPayloadRange);
|
||||
LIB_FUNC("fPSCdQxgpSw", Gen5::AgcWriteDataPatchSetAddressOrOffset);
|
||||
LIB_FUNC("bxca0BK4FNg", Gen5::AgcUnknownJumpPatchSetTarget);
|
||||
LIB_FUNC("2BS4EtAaF28", Gen5::AgcJumpPatchSetTarget);
|
||||
LIB_FUNC("h9z6+0hEydk", Gen5::AgcSuspendPoint);
|
||||
LIB_FUNC("qj7QZpgr9Uw", Gen5::AgcDcbContextStateOp);
|
||||
@@ -73,10 +72,13 @@ LIB_DEFINE(InitAgcDriver_1) {
|
||||
LIB_FUNC("ewobAQeMo5k", Gen5::AgcAcbAcquireMemGetSize);
|
||||
LIB_FUNC("qyM2bxYFPAk", Gen5::AgcAcbCondExec);
|
||||
LIB_FUNC("ozKzBP4aki4", Gen5::AgcAcbCondExecGetSize);
|
||||
LIB_FUNC("e1DFTg+Sd8U", Gen5::AgcAcbJump);
|
||||
LIB_FUNC("b-oySn+G2tE", Gen5::AgcAcbJumpGetSize);
|
||||
LIB_FUNC("htn36gPnBk4", Gen5::AgcAcbWaitRegMem);
|
||||
LIB_FUNC("idlaArvdXEs", Gen5::AgcAcbWaitOnAddressGetSize);
|
||||
LIB_FUNC("-RnpfpxIhec", Gen5::AgcAcbDmaData);
|
||||
LIB_FUNC("qzMN2XKGA4k", Gen5::AgcAcbCopyData);
|
||||
LIB_FUNC("CbQh3DKMSno", Gen5::AgcAcbCopyDataGetSize);
|
||||
LIB_FUNC("j3EtxFkSIhQ", Gen5::AgcAcbDispatchIndirect);
|
||||
LIB_FUNC("eZ4+17OQz4Q", Gen5::AgcAcbWriteData);
|
||||
LIB_FUNC("xAeBOa0A3kk", Gen5::AgcAcbSetMarker);
|
||||
|
||||
@@ -52,6 +52,8 @@ static void PrintUsage() {
|
||||
Config::DEFAULT_USER_ID);
|
||||
::printf(
|
||||
" --present-mode <value> Fifo, Mailbox, or Immediate. Default: Fifo.\n");
|
||||
::printf(
|
||||
" --gpu <index> Vulkan physical device index. Default: auto.\n");
|
||||
::printf(" --fullscreen Run in borderless desktop fullscreen.\n");
|
||||
::printf(" --vblank-frequency <num> Virtual vblank frequency. Default: 60.\n");
|
||||
::printf(" --console-language <0-29> Console language. Default: 1 (English US).\n");
|
||||
@@ -234,6 +236,8 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he
|
||||
::printf("invalid present mode: %s\n", value.c_str());
|
||||
return false;
|
||||
}
|
||||
} else if (arg == "--gpu") {
|
||||
options.config.gpu_index = Common::ToInt32(value);
|
||||
} else if (arg == "--vblank-frequency") {
|
||||
const int32_t vblank_frequency = Common::ToInt32(value);
|
||||
options.config.vblank_frequency =
|
||||
|
||||
@@ -29,9 +29,11 @@
|
||||
|
||||
namespace {
|
||||
|
||||
using Libs::Graphics::GuestRange;
|
||||
using Libs::Graphics::MemoryTracker;
|
||||
using Libs::Graphics::PageManager;
|
||||
using Libs::Graphics::RangeSet;
|
||||
using Libs::Graphics::TRACKER_ADDRESS_SIZE;
|
||||
|
||||
void Check(bool value, const char *text) {
|
||||
if (!value) {
|
||||
@@ -208,6 +210,27 @@ void TestRangeSet() {
|
||||
"range set subtraction did not preserve both exact tails");
|
||||
}
|
||||
|
||||
void TestGuestRange() {
|
||||
constexpr GuestRange empty{};
|
||||
constexpr GuestRange first_byte{1, 1};
|
||||
constexpr GuestRange last_byte{TRACKER_ADDRESS_SIZE - 1, 1};
|
||||
|
||||
static_assert(empty.Empty() && !empty.Valid() && empty.ValidOrEmpty());
|
||||
static_assert(!first_byte.Empty() && first_byte.Valid() &&
|
||||
first_byte.ValidOrEmpty() && first_byte.End() == 2);
|
||||
static_assert(last_byte.Valid() && last_byte.End() == TRACKER_ADDRESS_SIZE);
|
||||
|
||||
Check(!GuestRange{0, 1}.Empty() && !GuestRange{0, 1}.ValidOrEmpty(),
|
||||
"zero-address nonempty guest range is rejected");
|
||||
Check(!GuestRange{1, 0}.Empty() && !GuestRange{1, 0}.ValidOrEmpty(),
|
||||
"nonzero-address empty guest range is rejected");
|
||||
Check(!GuestRange{TRACKER_ADDRESS_SIZE, 1}.Valid(),
|
||||
"first address beyond the guest range is rejected");
|
||||
Check(!GuestRange{TRACKER_ADDRESS_SIZE - 1, 2}.Valid(),
|
||||
"guest range crossing the address-space end is rejected");
|
||||
Check(!GuestRange{UINT64_MAX, 2}.Valid(), "wrapping guest range is rejected");
|
||||
}
|
||||
|
||||
void TestQueriesDoNotRequireMappedOwnership() {
|
||||
constexpr uint64_t address = 0x0000000203000000ull;
|
||||
TrackerHarness harness;
|
||||
@@ -829,6 +852,7 @@ int main(int argc, char **argv) {
|
||||
if (argc == 3 && std::strcmp(argv[1], "--death") == 0) {
|
||||
RunDeathCase(argv[2]);
|
||||
}
|
||||
TestGuestRange();
|
||||
TestRangeSet();
|
||||
TestQueriesDoNotRequireMappedOwnership();
|
||||
TestConcurrentRegionPublication();
|
||||
|
||||
+164
-192
@@ -29,6 +29,19 @@ void Check(bool condition, const char *message) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void CheckFatal(F &&function, std::string_view expected, const char *message) {
|
||||
try {
|
||||
function();
|
||||
} catch (const std::runtime_error &error) {
|
||||
Check(std::string_view(error.what()).find(expected) !=
|
||||
std::string_view::npos,
|
||||
message);
|
||||
return;
|
||||
}
|
||||
Check(false, message);
|
||||
}
|
||||
|
||||
struct Fixture {
|
||||
Program program;
|
||||
Block *block = nullptr;
|
||||
@@ -75,8 +88,7 @@ struct Fixture {
|
||||
}
|
||||
|
||||
MemoryFlags AddMemory(MemoryInfo memory, uint32_t pc) {
|
||||
const auto index =
|
||||
static_cast<uint32_t>(program.memory_info.size());
|
||||
const auto index = static_cast<uint32_t>(program.memory_info.size());
|
||||
program.memory_info.push_back(memory);
|
||||
return {index, pc};
|
||||
}
|
||||
@@ -113,10 +125,8 @@ struct Fixture {
|
||||
}
|
||||
|
||||
void PlanAndTrack() {
|
||||
std::string error;
|
||||
if (!BuildSrtPlan(program, &error) || !TrackResources(program, &error)) {
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
BuildSrtPlan(program);
|
||||
TrackResources(program);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -241,10 +251,7 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
auto fixture = MakeIndirectImageFixture(false);
|
||||
fixture->PlanAndTrack();
|
||||
EliminateDeadCode(fixture->program.blocks);
|
||||
std::string validation_error;
|
||||
Check(
|
||||
ValidateProgram(fixture->program, true, &validation_error),
|
||||
"post-tracking dead-code elimination invalidated descriptor provenance");
|
||||
ValidateProgram(fixture->program, true);
|
||||
|
||||
Check(fixture->program.info.buffers.size() == 1 &&
|
||||
fixture->program.info.images.size() == 1 &&
|
||||
@@ -291,7 +298,6 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
.userdata = &memory,
|
||||
.read_specialization_memory = ReadLinearTestMemory};
|
||||
ResourceSnapshot snapshot;
|
||||
std::string error;
|
||||
const auto same_snapshot = [](const ResourceSnapshot &lhs,
|
||||
const ResourceSnapshot &rhs) {
|
||||
return lhs.buffers == rhs.buffers && lhs.images == rhs.images &&
|
||||
@@ -300,7 +306,7 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
lhs.user_data == rhs.user_data &&
|
||||
lhs.indirect_images.empty() == rhs.indirect_images.empty();
|
||||
};
|
||||
Check(MaterializeResources(fixture->program, runtime, snapshot, &error) &&
|
||||
Check(MaterializeResources(fixture->program, runtime, snapshot) &&
|
||||
snapshot.images.size() == 1 &&
|
||||
std::equal(image_descriptor.begin(), image_descriptor.end(),
|
||||
snapshot.images[0].dwords.begin()),
|
||||
@@ -308,8 +314,7 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
|
||||
const auto prior_snapshot = snapshot;
|
||||
memory.fail_address = 0x1004u;
|
||||
Check(!MaterializeResources(fixture->program, runtime, snapshot, &error) &&
|
||||
error.find("scalar read") != std::string::npos &&
|
||||
Check(!MaterializeResources(fixture->program, runtime, snapshot) &&
|
||||
same_snapshot(snapshot, prior_snapshot),
|
||||
"rejected planning memory read mutated the snapshot");
|
||||
memory.fail_address = UINT64_MAX;
|
||||
@@ -325,12 +330,11 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
memory.words[(0x2020u - memory.base) / 4u + 3u] =
|
||||
image_descriptor[3] ^ (1u << 28u);
|
||||
ResourceSnapshot null_snapshot;
|
||||
Check(
|
||||
MaterializeResources(fixture->program, runtime, null_snapshot, &error) &&
|
||||
null_snapshot.indirect_images.empty() &&
|
||||
std::ranges::all_of(null_snapshot.images[0].dwords,
|
||||
[](uint32_t dword) { return dword == 0u; }),
|
||||
"stale typed null image descriptors were not canonicalized");
|
||||
Check(MaterializeResources(fixture->program, runtime, null_snapshot) &&
|
||||
null_snapshot.indirect_images.empty() &&
|
||||
std::ranges::all_of(null_snapshot.images[0].dwords,
|
||||
[](uint32_t dword) { return dword == 0u; }),
|
||||
"stale typed null image descriptors were not canonicalized");
|
||||
|
||||
for (uint32_t dword = 0; dword < image_descriptor.size(); dword++) {
|
||||
memory.words[(0x2000u - memory.base) / 4u + dword] =
|
||||
@@ -341,13 +345,13 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
memory.words[(0x2020u - memory.base) / 4u] ^= 1u;
|
||||
memory.words[(0x1000u - memory.base + 36u) / 4u] = 1u;
|
||||
ResourceSnapshot dynamic_snapshot;
|
||||
Check(MaterializeResources(fixture->program, runtime, dynamic_snapshot,
|
||||
&error) &&
|
||||
Check(MaterializeResources(fixture->program, runtime, dynamic_snapshot) &&
|
||||
dynamic_snapshot.images.size() == 1 &&
|
||||
dynamic_snapshot.indirect_images.size() == 1 &&
|
||||
dynamic_snapshot.indirect_images[0].descriptors.size() == 2 &&
|
||||
SpecializeResources(fixture->program, dynamic_snapshot, &error) &&
|
||||
fixture->program.info.images.size() == 2 &&
|
||||
dynamic_snapshot.indirect_images[0].descriptors.size() == 2,
|
||||
"dynamic indirect image table did not materialize");
|
||||
SpecializeResources(fixture->program, dynamic_snapshot);
|
||||
Check(fixture->program.info.images.size() == 2 &&
|
||||
fixture->program.info.images[0].indirect_root == 0 &&
|
||||
fixture->program.info.images[0].indirect_mapping_capacity != 0 &&
|
||||
fixture->program.info.images[0].indirect_resources.size() == 2 &&
|
||||
@@ -364,27 +368,21 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
memory.words[(0x2000u - memory.base) / 4u] += 0x100u;
|
||||
memory.words[(0x2020u - memory.base) / 4u] += 0x101u;
|
||||
ResourceSnapshot rebound_snapshot;
|
||||
Check(MaterializeResources(fixture->program, runtime, rebound_snapshot,
|
||||
&error) &&
|
||||
ValidateResourceSpecialization(fixture->program, rebound_snapshot,
|
||||
&error),
|
||||
Check(MaterializeResources(fixture->program, runtime, rebound_snapshot) &&
|
||||
ValidateResourceSpecialization(fixture->program, rebound_snapshot),
|
||||
"stable indirect key mapping did not accept changed image addresses");
|
||||
memory.words[(0x2020u - memory.base) / 4u] =
|
||||
memory.words[(0x2000u - memory.base) / 4u];
|
||||
Check(MaterializeResources(fixture->program, runtime, rebound_snapshot,
|
||||
&error) &&
|
||||
ValidateResourceSpecialization(fixture->program, rebound_snapshot,
|
||||
&error),
|
||||
Check(MaterializeResources(fixture->program, runtime, rebound_snapshot) &&
|
||||
ValidateResourceSpecialization(fixture->program, rebound_snapshot),
|
||||
"runtime indirect key mapping did not accept collapsed candidates");
|
||||
const auto collapsed_snapshot = rebound_snapshot;
|
||||
ResourceSnapshot capacity_snapshot;
|
||||
for (const uint32_t records : {1u, 3u}) {
|
||||
user_data[2] = records;
|
||||
Check(
|
||||
MaterializeResources(fixture->program, runtime, capacity_snapshot,
|
||||
&error) &&
|
||||
ValidateResourceSpecialization(fixture->program, capacity_snapshot,
|
||||
&error),
|
||||
MaterializeResources(fixture->program, runtime, capacity_snapshot) &&
|
||||
ValidateResourceSpecialization(fixture->program, capacity_snapshot),
|
||||
"runtime indirect key mapping rejected a fitting material-table size");
|
||||
}
|
||||
user_data[2] = 2u;
|
||||
@@ -398,9 +396,7 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
}
|
||||
memory.words[(0x1000u - memory.base + 68u) / 4u] = 2u;
|
||||
Check(
|
||||
!MaterializeResources(fixture->program, runtime, rebound_snapshot,
|
||||
&error) &&
|
||||
error.find("candidate topology") != std::string::npos &&
|
||||
!MaterializeResources(fixture->program, runtime, rebound_snapshot) &&
|
||||
same_snapshot(rebound_snapshot, collapsed_snapshot),
|
||||
"larger indirect candidate topology reused or mutated a cached snapshot");
|
||||
|
||||
@@ -418,26 +414,26 @@ void TestInvariantIndirectImageMaterialization() {
|
||||
.read_specialization_memory =
|
||||
ReadLinearTestMemory};
|
||||
Check(!MaterializeResources(memory_backed->program, memory_backed_runtime,
|
||||
snapshot, &error) &&
|
||||
error.find("constant read failed") != std::string::npos &&
|
||||
snapshot) &&
|
||||
same_snapshot(snapshot, prior_snapshot),
|
||||
"rejected indirect table descriptor read mutated the snapshot");
|
||||
memory.fail_address = UINT64_MAX;
|
||||
|
||||
auto malformed = MakeIndirectImageFixture(true);
|
||||
Check(BuildSrtPlan(malformed->program, &error) &&
|
||||
!TrackResources(malformed->program, &error) &&
|
||||
error.find("ReadFirstLane") != std::string::npos &&
|
||||
!malformed->program.resource_tracking_complete &&
|
||||
BuildSrtPlan(malformed->program);
|
||||
CheckFatal([&] { TrackResources(malformed->program); }, "not a valid runtime value",
|
||||
"malformed indirect image pattern was accepted");
|
||||
Check(!malformed->program.resource_tracking_complete &&
|
||||
malformed->program.info.images.empty() &&
|
||||
malformed->program.descriptor_sources.empty(),
|
||||
"malformed indirect image pattern was partially accepted");
|
||||
|
||||
auto wrapped_immediate = MakeIndirectImageFixture(false, 4u);
|
||||
Check(BuildSrtPlan(wrapped_immediate->program, &error) &&
|
||||
!TrackResources(wrapped_immediate->program, &error) &&
|
||||
error.find("ReadFirstLane") != std::string::npos &&
|
||||
!wrapped_immediate->program.resource_tracking_complete,
|
||||
BuildSrtPlan(wrapped_immediate->program);
|
||||
CheckFatal([&] { TrackResources(wrapped_immediate->program); },
|
||||
"not a valid runtime value",
|
||||
"wrapped scalar immediate entered the invariant image proof");
|
||||
Check(!wrapped_immediate->program.resource_tracking_complete,
|
||||
"wrapped scalar immediate entered the invariant image proof");
|
||||
}
|
||||
|
||||
@@ -494,16 +490,12 @@ void TestDenseBufferTracking() {
|
||||
second.Instruction()->Flags<uint32_t>() == 1,
|
||||
"typed handles were not assigned dense indices");
|
||||
Check(fixture.program.memory_info[load_flags.index].resource == 0 &&
|
||||
fixture.program.memory_info[store_flags.index].resource ==
|
||||
0 &&
|
||||
fixture.program.memory_info[other_flags.index].resource ==
|
||||
1,
|
||||
fixture.program.memory_info[store_flags.index].resource == 0 &&
|
||||
fixture.program.memory_info[other_flags.index].resource == 1,
|
||||
"typed memory metadata was not patched to dense indices");
|
||||
|
||||
std::string error;
|
||||
Check(!TrackResources(fixture.program, &error) &&
|
||||
error.find("already tracked") != std::string::npos,
|
||||
"resource tracking allowed a second mutation pass");
|
||||
CheckFatal([&] { TrackResources(fixture.program); }, "already tracked",
|
||||
"resource tracking allowed a second mutation pass");
|
||||
}
|
||||
|
||||
void TestScalarAndVectorBufferAlias() {
|
||||
@@ -531,8 +523,7 @@ void TestScalarAndVectorBufferAlias() {
|
||||
fixture.program.info.buffers[0].scalar,
|
||||
"typed scalar and vector uses of one descriptor were split");
|
||||
Check(fixture.program.memory_info[scalar_flags.index].resource == 0 &&
|
||||
fixture.program.memory_info[vector_flags.index].resource ==
|
||||
0,
|
||||
fixture.program.memory_info[vector_flags.index].resource == 0,
|
||||
"scalar/vector alias did not share a dense index");
|
||||
}
|
||||
|
||||
@@ -552,16 +543,13 @@ void TestRuntimeUnsignedMinDescriptor() {
|
||||
std::array<uint32_t, 1> user_data{0xffffffffu};
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
DescriptorValue value;
|
||||
std::string error;
|
||||
const auto source = fixture.program.info.buffers[0].source;
|
||||
Check(EvaluateDescriptorSource(fixture.program, source, 0x330, runtime, value,
|
||||
&error) &&
|
||||
Check(EvaluateDescriptorSource(fixture.program, source, runtime, value) &&
|
||||
value.dwords[3] == 0x100u,
|
||||
"runtime descriptor unsigned minimum did not clamp its first operand");
|
||||
user_data[0] = 0x80u;
|
||||
Check(
|
||||
EvaluateDescriptorSource(fixture.program, source, 0x330, runtime, value,
|
||||
&error) &&
|
||||
EvaluateDescriptorSource(fixture.program, source, runtime, value) &&
|
||||
value.dwords[3] == 0x80u,
|
||||
"runtime descriptor unsigned minimum did not preserve its first operand");
|
||||
}
|
||||
@@ -624,9 +612,7 @@ void TestImagesSamplersAndAliases() {
|
||||
repeated.second.Instruction()->Flags<uint32_t>() == 0,
|
||||
"unused sampler border colors prevented source interning");
|
||||
const auto sampler_source = fixture.program.info.samplers[0].source;
|
||||
Check(fixture.program.descriptor_sources[sampler_source]
|
||||
.dwords[3]
|
||||
.U32() == 0,
|
||||
Check(fixture.program.descriptor_sources[sampler_source].dwords[3].U32() == 0,
|
||||
"unused sampler border color was not canonicalized");
|
||||
Check(fixture.program.info.buffers[0].image_alias == 0,
|
||||
"buffer/image descriptor alias was not linked");
|
||||
@@ -673,9 +659,7 @@ void TestSampleAdjustSamplerScratch() {
|
||||
std::array<uint32_t, 4> user_data{4u, 1u, 2u, 0x80000abcu};
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
DescriptorValue descriptor;
|
||||
std::string error;
|
||||
Check(EvaluateDescriptorSource(fixture.program, source, 0x1ec, runtime,
|
||||
descriptor, &error) &&
|
||||
Check(EvaluateDescriptorSource(fixture.program, source, runtime, descriptor) &&
|
||||
descriptor.dwords[3] == 0x80000abcu,
|
||||
"SampleAdjust canonicalization lost sampler border fields");
|
||||
|
||||
@@ -704,11 +688,9 @@ void TestSampleAdjustSamplerScratch() {
|
||||
rejected.Emit(ValueOpcode::ImageSampleRaw,
|
||||
{rejected_image, rejected_sampler, rejected.ImageAddress()},
|
||||
rejected.AddMemory(rejected_memory, 0x200));
|
||||
std::string rejected_error;
|
||||
Check(BuildSrtPlan(rejected.program, &rejected_error) &&
|
||||
!TrackResources(rejected.program, &rejected_error) &&
|
||||
rejected_error.find("WqmMask") != std::string::npos,
|
||||
message);
|
||||
BuildSrtPlan(rejected.program);
|
||||
CheckFatal([&] { TrackResources(rejected.program); },
|
||||
"not a valid runtime value", message);
|
||||
};
|
||||
CheckRejected(0u, 12u,
|
||||
"ordinary sampling accepted SampleAdjust reserved scratch");
|
||||
@@ -778,15 +760,13 @@ void TestDynamicStorageMipTracking() {
|
||||
descriptor.dword_count = 8;
|
||||
ResourceSnapshot snapshot;
|
||||
snapshot.images.assign(images.size(), descriptor);
|
||||
std::string error;
|
||||
Check(SpecializeResources(fixture.program, snapshot, &error) &&
|
||||
fixture.program.info.images[1].mip_count == 3 &&
|
||||
ValidateResourceSpecialization(fixture.program, snapshot, &error),
|
||||
SpecializeResources(fixture.program, snapshot);
|
||||
Check(fixture.program.info.images[1].mip_count == 3 &&
|
||||
ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
"base-1 through last-3 dynamic storage range was not specialized");
|
||||
ShaderComputeInputInfo compute{};
|
||||
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error) &&
|
||||
AllocateBindings(fixture.program, 0, &error),
|
||||
"dynamic storage mip bindings were not allocated");
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
AllocateBindings(fixture.program, 0);
|
||||
const auto *storage_binding =
|
||||
FindBinding(fixture.program.bindings, DescriptorBindingKind::Storage2D);
|
||||
Check(storage_binding != nullptr &&
|
||||
@@ -805,19 +785,19 @@ void TestDynamicStorageMipTracking() {
|
||||
DescriptorValue null_descriptor{};
|
||||
null_descriptor.dword_count = 8;
|
||||
null_snapshot.images.push_back(null_descriptor);
|
||||
Check(SpecializeResources(null_program, null_snapshot, &error) &&
|
||||
null_program.info.images[0].mip_count == 1 &&
|
||||
ValidateResourceSpecialization(null_program, null_snapshot, &error),
|
||||
SpecializeResources(null_program, null_snapshot);
|
||||
Check(null_program.info.images[0].mip_count == 1 &&
|
||||
ValidateResourceSpecialization(null_program, null_snapshot),
|
||||
"canonical null dynamic storage image did not retain one descriptor");
|
||||
|
||||
snapshot.images[1].dwords[3] =
|
||||
(snapshot.images[1].dwords[3] & ~(0xfu << 16u)) | (2u << 16u);
|
||||
Check(!ValidateResourceSpecialization(fixture.program, snapshot, &error),
|
||||
Check(!ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
"a changed dynamic storage mip count reused the specialization");
|
||||
snapshot.images[1].dwords[3] =
|
||||
(snapshot.images[1].dwords[3] & ~((0xfu << 12u) | (0xfu << 16u))) |
|
||||
(4u << 12u) | (3u << 16u);
|
||||
Check(!ValidateResourceSpecialization(fixture.program, snapshot, &error),
|
||||
Check(!ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
"an inverted dynamic storage mip range was accepted");
|
||||
}
|
||||
|
||||
@@ -861,11 +841,9 @@ void TestSrtFlatteningAndRuntimeMemoization() {
|
||||
.userdata = &memory};
|
||||
std::vector<DescriptorValue> descriptors;
|
||||
std::vector<uint32_t> flat;
|
||||
const DescriptorSourceRequest request{fixture.program.info.buffers[0].source,
|
||||
12};
|
||||
std::string error;
|
||||
const DescriptorSourceRequest request{fixture.program.info.buffers[0].source};
|
||||
Check(EvaluateRuntimeSources(fixture.program, std::span{&request, 1}, runtime,
|
||||
descriptors, flat, {}, &error),
|
||||
descriptors, flat, {}),
|
||||
"typed runtime source evaluation failed");
|
||||
Check(descriptors.size() == 1 && descriptors[0].dwords[0] == 0xdeadbeefu &&
|
||||
flat == std::vector<uint32_t>{0xdeadbeefu} && memory.reads == 1,
|
||||
@@ -876,16 +854,16 @@ void TestSrtFlatteningAndRuntimeMemoization() {
|
||||
descriptors = {{{1u}, 1u}};
|
||||
flat = {2u};
|
||||
Check(!EvaluateRuntimeSources(fixture.program, std::span{&request, 1},
|
||||
runtime, descriptors, flat, {}, &error) &&
|
||||
runtime, descriptors, flat, {}) &&
|
||||
descriptors == std::vector<DescriptorValue>{{{1u}, 1u}} &&
|
||||
flat == std::vector<uint32_t>{2u},
|
||||
"runtime evaluation failure was not transactional");
|
||||
|
||||
ShaderComputeInputInfo compute{};
|
||||
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error) &&
|
||||
AllocateBindings(fixture.program, 0, &error) &&
|
||||
FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::FlattenedSrt) != nullptr,
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
AllocateBindings(fixture.program, 0);
|
||||
Check(FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::FlattenedSrt) != nullptr,
|
||||
"flattened typed SRT reads did not receive a binding");
|
||||
}
|
||||
|
||||
@@ -919,18 +897,16 @@ void TestDynamicSrtReadRemainsExplicit() {
|
||||
.read_memory = ReadTestMemory,
|
||||
.userdata = &memory};
|
||||
DescriptorValue value;
|
||||
std::string error;
|
||||
Check(EvaluateDescriptorSource(fixture.program,
|
||||
fixture.program.info.buffers[0].source, 8,
|
||||
runtime, value, &error) &&
|
||||
fixture.program.info.buffers[0].source, runtime, value) &&
|
||||
value.dwords[0] == 0xabcdef01u && memory.reads == 1,
|
||||
"dynamic typed scalar descriptor source was not evaluated");
|
||||
|
||||
ShaderComputeInputInfo compute{};
|
||||
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error) &&
|
||||
AllocateBindings(fixture.program, 0, &error) &&
|
||||
FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::FlattenedSrt) == nullptr &&
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
AllocateBindings(fixture.program, 0);
|
||||
Check(FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::FlattenedSrt) == nullptr &&
|
||||
FindBinding(fixture.program.bindings,
|
||||
DescriptorBindingKind::BdaPagetable) != nullptr &&
|
||||
FindBinding(fixture.program.bindings,
|
||||
@@ -966,12 +942,10 @@ void TestPhiValidation() {
|
||||
{handle, Value(0u), Value(0u), Value(0u), Value(true)},
|
||||
fixture.AddMemory(memory, 20), merge);
|
||||
|
||||
std::string error;
|
||||
Check(BuildSrtPlan(fixture.program, &error),
|
||||
"SRT planning rejected a well-formed typed phi");
|
||||
Check(!TrackResources(fixture.program, &error) &&
|
||||
error.find("control-dependent phi") != std::string::npos &&
|
||||
!fixture.program.resource_tracking_complete &&
|
||||
BuildSrtPlan(fixture.program);
|
||||
CheckFatal([&] { TrackResources(fixture.program); }, "not a valid runtime value",
|
||||
"control-dependent descriptor phi was accepted");
|
||||
Check(!fixture.program.resource_tracking_complete &&
|
||||
fixture.program.info.buffers.empty() &&
|
||||
fixture.program.descriptor_sources.empty(),
|
||||
"control-dependent descriptor phi was not rejected transactionally");
|
||||
@@ -994,9 +968,7 @@ void TestLoopCycleEnteredThroughRuntimeValue() {
|
||||
{carried, Value(0u), Value(0u), Value(0u)}, MemoryFlags{0, 12},
|
||||
loop);
|
||||
|
||||
std::string error;
|
||||
Check(BuildSrtPlan(fixture.program, &error),
|
||||
"SRT planning rejected a valid loop entered through a runtime value");
|
||||
BuildSrtPlan(fixture.program);
|
||||
}
|
||||
|
||||
void TestInvariantLoopPhi() {
|
||||
@@ -1023,10 +995,8 @@ void TestInvariantLoopPhi() {
|
||||
std::array<uint32_t, 1> user_data{0x12345678u};
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
DescriptorValue descriptor;
|
||||
std::string error;
|
||||
Check(EvaluateDescriptorSource(fixture.program,
|
||||
fixture.program.info.buffers[0].source, 4,
|
||||
runtime, descriptor, &error) &&
|
||||
fixture.program.info.buffers[0].source, runtime, descriptor) &&
|
||||
descriptor.dwords[0] == user_data[0],
|
||||
"loop-invariant descriptor phi was not evaluated through typed SSA");
|
||||
}
|
||||
@@ -1057,11 +1027,9 @@ void TestDmaAddressMaterialization() {
|
||||
std::array<uint32_t, 2> user_data{0x2008u, 0u};
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
std::string error;
|
||||
Check(MaterializeResources(fixture.program, runtime, snapshot, &error),
|
||||
Check(MaterializeResources(fixture.program, runtime, snapshot),
|
||||
"DMA shader resources did not materialize");
|
||||
Check(SpecializeResources(fixture.program, snapshot, &error),
|
||||
"DMA shader resources were not specialized");
|
||||
SpecializeResources(fixture.program, snapshot);
|
||||
}
|
||||
|
||||
void TestDynamicFlatAddressesUseDma() {
|
||||
@@ -1089,8 +1057,7 @@ void TestDynamicFlatAddressesUseDma() {
|
||||
std::array<uint32_t, 3> user_data{0x23456780u, 1u, 1u};
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
std::string error;
|
||||
Check(MaterializeResources(fixture.program, runtime, snapshot, &error),
|
||||
Check(MaterializeResources(fixture.program, runtime, snapshot),
|
||||
"exec-masked FLAT shader resources did not materialize");
|
||||
|
||||
Fixture mismatch;
|
||||
@@ -1137,15 +1104,15 @@ void TestBufferSwizzleSpecialization() {
|
||||
(1u << 24u)};
|
||||
SrtRuntime runtime{.user_data = user_data};
|
||||
ResourceSnapshot snapshot;
|
||||
std::string error;
|
||||
Check(MaterializeResources(fixture.program, runtime, snapshot, &error) &&
|
||||
SpecializeResources(fixture.program, snapshot, &error) &&
|
||||
fixture.program.info.buffers[0].descriptor_swizzle == swizzle &&
|
||||
ValidateResourceSpecialization(fixture.program, snapshot, &error),
|
||||
Check(MaterializeResources(fixture.program, runtime, snapshot),
|
||||
"buffer resources did not materialize");
|
||||
SpecializeResources(fixture.program, snapshot);
|
||||
Check(fixture.program.info.buffers[0].descriptor_swizzle == swizzle &&
|
||||
ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
"buffer destination selectors were not specialized");
|
||||
|
||||
snapshot.buffers[0].dwords[3] ^= 1u << 9u;
|
||||
Check(!ValidateResourceSpecialization(fixture.program, snapshot, &error),
|
||||
Check(!ValidateResourceSpecialization(fixture.program, snapshot),
|
||||
"buffer swizzle change did not invalidate specialization");
|
||||
}
|
||||
|
||||
@@ -1171,17 +1138,14 @@ void TestShaderInfoAndBindingLayout() {
|
||||
|
||||
ShaderComputeInputInfo compute{};
|
||||
compute.dispatch_thread_dimensions = true;
|
||||
std::string error;
|
||||
Check(CollectShaderInfo(fixture.program, {.compute = &compute}, &error),
|
||||
"typed shader info collection failed");
|
||||
CollectShaderInfo(fixture.program, {.compute = &compute});
|
||||
Check(fixture.program.info.has_bitwise_xor &&
|
||||
!fixture.program.info.inputs.empty() &&
|
||||
fixture.program.info.inputs[0].kind ==
|
||||
StageInputKind::GlobalInvocationId,
|
||||
"typed shader values were not reflected in shader info");
|
||||
|
||||
Check(AllocateBindings(fixture.program, 0, &error),
|
||||
"typed binding allocation failed");
|
||||
AllocateBindings(fixture.program, 0);
|
||||
Check(FindBinding(fixture.program.bindings, DescriptorBindingKind::Buffers) !=
|
||||
nullptr &&
|
||||
FindBinding(fixture.program.bindings, DescriptorBindingKind::Gds) !=
|
||||
@@ -1211,53 +1175,57 @@ void TestGraphicsPushConstantLayout() {
|
||||
}
|
||||
fixture.program.shader_info_complete = true;
|
||||
};
|
||||
|
||||
std::string error;
|
||||
Fixture vertex(ShaderType::Vertex);
|
||||
AddUserData(vertex, 9);
|
||||
Check(AllocateBindings(vertex.program, 0, &error) &&
|
||||
vertex.program.bindings.push_constant_offset == 0 &&
|
||||
AllocateBindings(vertex.program, 0);
|
||||
Check(vertex.program.bindings.push_constant_offset == 0 &&
|
||||
vertex.program.bindings.push_constant_size == 9 * sizeof(uint32_t),
|
||||
"vertex shader was not placed at the start of the graphics push bank");
|
||||
|
||||
const auto pixel_offset = vertex.program.bindings.push_constant_size;
|
||||
Fixture pixel(ShaderType::Pixel);
|
||||
AddUserData(pixel, 4);
|
||||
Check(AllocateBindings(pixel.program, pixel_offset, &error) &&
|
||||
pixel.program.bindings.push_constant_offset == pixel_offset &&
|
||||
pixel.program.bindings.push_constant_size == 4 * sizeof(uint32_t) &&
|
||||
FindBinding(pixel.program.bindings, DescriptorBindingKind::UserData) ==
|
||||
nullptr,
|
||||
"pixel shader did not follow the vertex data in the graphics push bank");
|
||||
AllocateBindings(pixel.program, pixel_offset);
|
||||
Check(
|
||||
pixel.program.bindings.push_constant_offset == pixel_offset &&
|
||||
pixel.program.bindings.push_constant_size == 4 * sizeof(uint32_t) &&
|
||||
FindBinding(pixel.program.bindings,
|
||||
DescriptorBindingKind::UserData) == nullptr,
|
||||
"pixel shader did not follow the vertex data in the graphics push bank");
|
||||
|
||||
Fixture edge(ShaderType::Pixel);
|
||||
AddUserData(edge, 1);
|
||||
Check(AllocateBindings(edge.program, NativePushConstantSize - sizeof(uint32_t),
|
||||
&error) &&
|
||||
edge.program.bindings.push_constant_size == sizeof(uint32_t),
|
||||
AllocateBindings(edge.program, NativePushConstantSize - sizeof(uint32_t));
|
||||
Check(edge.program.bindings.push_constant_size == sizeof(uint32_t),
|
||||
"last aligned push-constant dword did not fit in the graphics bank");
|
||||
|
||||
Fixture spill(ShaderType::Pixel);
|
||||
AddUserData(spill, 32);
|
||||
Check(AllocateBindings(spill.program, pixel_offset, &error) &&
|
||||
spill.program.bindings.push_constant_size == 0 &&
|
||||
FindBinding(spill.program.bindings, DescriptorBindingKind::UserData) !=
|
||||
nullptr,
|
||||
"pixel shader overlapping the vertex push data did not spill to storage");
|
||||
AllocateBindings(spill.program, pixel_offset);
|
||||
Check(
|
||||
spill.program.bindings.push_constant_size == 0 &&
|
||||
FindBinding(spill.program.bindings,
|
||||
DescriptorBindingKind::UserData) != nullptr,
|
||||
"pixel shader overlapping the vertex push data did not spill to storage");
|
||||
|
||||
Fixture full(ShaderType::Pixel);
|
||||
AddUserData(full, 1);
|
||||
Check(AllocateBindings(full.program, NativePushConstantSize, &error) &&
|
||||
full.program.bindings.push_constant_size == 0 &&
|
||||
FindBinding(full.program.bindings, DescriptorBindingKind::UserData) !=
|
||||
nullptr,
|
||||
AllocateBindings(full.program, NativePushConstantSize);
|
||||
Check(full.program.bindings.push_constant_size == 0 &&
|
||||
FindBinding(full.program.bindings,
|
||||
DescriptorBindingKind::UserData) != nullptr,
|
||||
"full graphics push bank did not spill pixel user data to storage");
|
||||
|
||||
Fixture invalid(ShaderType::Pixel);
|
||||
AddUserData(invalid, 1);
|
||||
Check(!AllocateBindings(invalid.program,
|
||||
NativePushConstantSize + sizeof(uint32_t), &error) &&
|
||||
!invalid.program.binding_layout_complete,
|
||||
CheckFatal(
|
||||
[&] {
|
||||
AllocateBindings(invalid.program,
|
||||
NativePushConstantSize + sizeof(uint32_t));
|
||||
},
|
||||
"push-constant offset",
|
||||
"push-constant placement beyond the graphics bank was accepted");
|
||||
Check(!invalid.program.binding_layout_complete,
|
||||
"push-constant placement beyond the graphics bank was accepted");
|
||||
}
|
||||
|
||||
@@ -1273,12 +1241,11 @@ void TestResourceLimitIsTransactional() {
|
||||
{handle, Value(0u), Value(0u), Value(0u), Value(true)},
|
||||
fixture.AddMemory(memory, index * 4u));
|
||||
}
|
||||
std::string error;
|
||||
Check(BuildSrtPlan(fixture.program, &error),
|
||||
"SRT plan failed before resource-limit test");
|
||||
Check(!TrackResources(fixture.program, &error) &&
|
||||
error.find("buffer resource limit exceeded") != std::string::npos &&
|
||||
!fixture.program.resource_tracking_complete &&
|
||||
BuildSrtPlan(fixture.program);
|
||||
CheckFatal([&] { TrackResources(fixture.program); },
|
||||
"buffer resource limit exceeded",
|
||||
"resource-limit failure was not reported");
|
||||
Check(!fixture.program.resource_tracking_complete &&
|
||||
fixture.program.info.buffers.empty() &&
|
||||
fixture.program.descriptor_sources.empty(),
|
||||
"resource-limit failure partially mutated typed resource state");
|
||||
@@ -1293,12 +1260,11 @@ void TestMalformedMemoryKindsRejected() {
|
||||
fixture.Emit(ValueOpcode::StoreAddressU32,
|
||||
{address, Value(0u), Value(0u), Value(1u), Value(true)},
|
||||
fixture.AddMemory(memory, 4));
|
||||
std::string error;
|
||||
Check(BuildSrtPlan(fixture.program, &error) &&
|
||||
!TrackResources(fixture.program, &error) &&
|
||||
error.find("address operation has invalid resource kind") !=
|
||||
std::string::npos,
|
||||
"resource tracking accepted an address opcode with buffer metadata");
|
||||
BuildSrtPlan(fixture.program);
|
||||
CheckFatal(
|
||||
[&] { TrackResources(fixture.program); },
|
||||
"address operation has invalid resource kind",
|
||||
"resource tracking accepted an address opcode with buffer metadata");
|
||||
}
|
||||
{
|
||||
Fixture fixture;
|
||||
@@ -1311,12 +1277,11 @@ void TestMalformedMemoryKindsRejected() {
|
||||
fixture.Emit(ValueOpcode::ImageRead,
|
||||
{image, fixture.ImageAddress(), Value(true)},
|
||||
fixture.AddMemory(memory, 8));
|
||||
std::string error;
|
||||
Check(BuildSrtPlan(fixture.program, &error) &&
|
||||
!TrackResources(fixture.program, &error) &&
|
||||
error.find("image operation has invalid resource kind") !=
|
||||
std::string::npos,
|
||||
"resource tracking accepted an image opcode with address metadata");
|
||||
BuildSrtPlan(fixture.program);
|
||||
CheckFatal(
|
||||
[&] { TrackResources(fixture.program); },
|
||||
"image operation has invalid resource kind",
|
||||
"resource tracking accepted an image opcode with address metadata");
|
||||
}
|
||||
{
|
||||
Fixture fixture;
|
||||
@@ -1329,12 +1294,11 @@ void TestMalformedMemoryKindsRejected() {
|
||||
fixture.Emit(ValueOpcode::ImageRead,
|
||||
{image, fixture.ImageAddress(), Value(true)},
|
||||
fixture.AddMemory(memory, 12));
|
||||
std::string error;
|
||||
Check(BuildSrtPlan(fixture.program, &error) &&
|
||||
!TrackResources(fixture.program, &error) &&
|
||||
error.find("image operation has invalid resource kind") !=
|
||||
std::string::npos,
|
||||
"resource tracking accepted a sampled read with storage metadata");
|
||||
BuildSrtPlan(fixture.program);
|
||||
CheckFatal(
|
||||
[&] { TrackResources(fixture.program); },
|
||||
"image operation has invalid resource kind",
|
||||
"resource tracking accepted a sampled read with storage metadata");
|
||||
}
|
||||
{
|
||||
Fixture fixture;
|
||||
@@ -1347,12 +1311,10 @@ void TestMalformedMemoryKindsRejected() {
|
||||
fixture.Emit(ValueOpcode::ImageAtomicIAdd32,
|
||||
{image, fixture.ImageAddress(), Value(1u), Value(true)},
|
||||
fixture.AddMemory(memory, 16));
|
||||
std::string error;
|
||||
Check(
|
||||
BuildSrtPlan(fixture.program, &error) &&
|
||||
!TrackResources(fixture.program, &error) &&
|
||||
error.find("image operation has invalid resource kind") !=
|
||||
std::string::npos,
|
||||
BuildSrtPlan(fixture.program);
|
||||
CheckFatal(
|
||||
[&] { TrackResources(fixture.program); },
|
||||
"image operation has invalid resource kind",
|
||||
"resource tracking accepted a uint atomic with float storage metadata");
|
||||
}
|
||||
}
|
||||
@@ -1399,14 +1361,24 @@ int main() {
|
||||
// target links only fmt; keep assertion failures observable without widening
|
||||
// its focused build manifest.
|
||||
namespace Common {
|
||||
int DbgExitHandler(const char *, int, std::string_view text) {
|
||||
throw std::runtime_error(std::string(text));
|
||||
}
|
||||
|
||||
int DbgExitHandler(const char *, int, fmt::text_style, std::string_view text) {
|
||||
throw std::runtime_error(std::string(text));
|
||||
}
|
||||
|
||||
int DbgExitIfHandler(const char *expression, const char *file, int line) {
|
||||
throw std::runtime_error(std::string("typed IR assertion: ") + expression +
|
||||
" at " + file + ':' + std::to_string(line));
|
||||
}
|
||||
|
||||
int DbgNotImplementedHandler(const char *expression, const char *file, int line) {
|
||||
throw std::runtime_error(std::string("typed IR not implemented: ") + expression +
|
||||
" at " + file + ':' + std::to_string(line));
|
||||
int DbgNotImplementedHandler(const char *expression, const char *file,
|
||||
int line) {
|
||||
throw std::runtime_error(std::string("typed IR not implemented: ") +
|
||||
expression + " at " + file + ':' +
|
||||
std::to_string(line));
|
||||
}
|
||||
|
||||
void DbgExit(int) { throw std::runtime_error("typed IR assertion failed"); }
|
||||
@@ -1415,8 +1387,8 @@ void DbgExit(int) { throw std::runtime_error("typed IR assertion failed"); }
|
||||
// Keep this focused standalone target self-contained by amalgamating its small
|
||||
// typed-IR implementation set.
|
||||
#include "graphics/shader/recompiler/ir/Block.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Program.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Type.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Value.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Program.cpp"
|
||||
#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"
|
||||
#include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.cpp"
|
||||
@@ -20,6 +20,19 @@ void Check(bool condition, const char *message) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void CheckFatal(F &&function, std::string_view expected, const char *message) {
|
||||
try {
|
||||
function();
|
||||
} catch (const std::runtime_error &error) {
|
||||
Check(std::string_view(error.what()).find(expected) !=
|
||||
std::string_view::npos,
|
||||
message);
|
||||
return;
|
||||
}
|
||||
Check(false, message);
|
||||
}
|
||||
|
||||
struct Fixture {
|
||||
Program program;
|
||||
|
||||
@@ -58,10 +71,7 @@ struct Fixture {
|
||||
return static_cast<uint32_t>(program.memory_info.size() - 1u);
|
||||
}
|
||||
|
||||
void Plan() {
|
||||
std::string error;
|
||||
Check(BuildSrtPlan(program, &error), error.c_str());
|
||||
}
|
||||
void Plan() { BuildSrtPlan(program); }
|
||||
};
|
||||
|
||||
struct TestMemory {
|
||||
@@ -112,8 +122,7 @@ void TestImmediateFlatteningAndGvn() {
|
||||
TestMemory memory_image{{{0x1020u, 0xfeedbeefu}}};
|
||||
SrtRuntime runtime{.read_memory = ReadMemory, .userdata = &memory_image};
|
||||
std::vector<uint32_t> flat;
|
||||
std::string error;
|
||||
Check(WalkSrt(fixture.program, runtime, flat, &error), error.c_str());
|
||||
Check(WalkSrt(fixture.program, runtime, flat), "flattened SRT walk failed");
|
||||
Check(flat == std::vector<uint32_t>{0xfeedbeefu} && memory_image.reads == 1,
|
||||
"flattened SRT did not evaluate its canonical read once");
|
||||
}
|
||||
@@ -130,8 +139,7 @@ void TestRawScalarComponentAlignment() {
|
||||
TestMemory memory_image{{{0x1000u, 0x12345678u}}};
|
||||
SrtRuntime runtime{.read_memory = ReadMemory, .userdata = &memory_image};
|
||||
std::vector<uint32_t> flat;
|
||||
std::string error;
|
||||
Check(WalkSrt(fixture.program, runtime, flat, &error), error.c_str());
|
||||
Check(WalkSrt(fixture.program, runtime, flat), "raw scalar SRT walk failed");
|
||||
Check(
|
||||
flat == std::vector<uint32_t>{0x12345678u} && memory_image.reads == 1,
|
||||
"raw scalar base, immediate, and offset were not aligned independently");
|
||||
@@ -141,11 +149,9 @@ void TestScalarMemoryDomainMismatchFails() {
|
||||
Fixture raw;
|
||||
const auto raw_memory = raw.AddMemory(ResourceKind::ScalarBuffer);
|
||||
RawRead(raw, Address(raw, Value(0x1000u), Value(0u)), Value(0u), raw_memory);
|
||||
std::string error;
|
||||
Check(!BuildSrtPlan(raw.program, &error) &&
|
||||
error.find("incompatible scalar memory metadata") !=
|
||||
std::string::npos,
|
||||
"raw scalar load accepted descriptor-buffer metadata");
|
||||
CheckFatal([&] { BuildSrtPlan(raw.program); },
|
||||
"incompatible scalar memory metadata",
|
||||
"raw scalar load accepted descriptor-buffer metadata");
|
||||
|
||||
Fixture buffer;
|
||||
const auto buffer_memory = buffer.AddMemory(ResourceKind::ScalarAddress);
|
||||
@@ -154,11 +160,9 @@ void TestScalarMemoryDomainMismatchFails() {
|
||||
{Value(0x1000u), Value(0u), Value(16u), Value(0u)});
|
||||
buffer.EmitMemory(ValueOpcode::ReadConstBuffer, {resource, Value(0u)},
|
||||
buffer_memory);
|
||||
error.clear();
|
||||
Check(!BuildSrtPlan(buffer.program, &error) &&
|
||||
error.find("incompatible scalar memory metadata") !=
|
||||
std::string::npos,
|
||||
"descriptor scalar load accepted raw-address metadata");
|
||||
CheckFatal([&] { BuildSrtPlan(buffer.program); },
|
||||
"incompatible scalar memory metadata",
|
||||
"descriptor scalar load accepted raw-address metadata");
|
||||
}
|
||||
|
||||
void TestDynamicReadRemainsTyped() {
|
||||
@@ -191,8 +195,7 @@ void TestNestedSrtWalk() {
|
||||
TestMemory memory_image{{{0x1000u, 0x2000u}, {0x2000u, 0xabcdef01u}}};
|
||||
SrtRuntime runtime{.read_memory = ReadMemory, .userdata = &memory_image};
|
||||
std::vector<uint32_t> flat;
|
||||
std::string error;
|
||||
Check(WalkSrt(fixture.program, runtime, flat, &error), error.c_str());
|
||||
Check(WalkSrt(fixture.program, runtime, flat), "nested SRT walk failed");
|
||||
Check(flat == std::vector<uint32_t>({0x2000u, 0xabcdef01u}),
|
||||
"nested typed SRT reads were not evaluated in dependency order");
|
||||
}
|
||||
@@ -215,10 +218,8 @@ void TestShaderBaseAndUserData() {
|
||||
SrtRuntime runtime{.user_data = user_data,
|
||||
.shader_base = 0x12345678abcdef00ull};
|
||||
DescriptorValue result;
|
||||
std::string error;
|
||||
Check(EvaluateDescriptorSource(fixture.program, 0, 0x90, runtime, result,
|
||||
&error),
|
||||
error.c_str());
|
||||
Check(EvaluateDescriptorSource(fixture.program, 0, runtime, result),
|
||||
"shader-relative descriptor evaluation failed");
|
||||
Check(result.dword_count == 3 && result.dwords[0] == 0xabcdef00u &&
|
||||
result.dwords[1] == 0x12345678u && result.dwords[2] == 0x24u,
|
||||
"shader-relative typed descriptor expression evaluated incorrectly");
|
||||
@@ -242,9 +243,8 @@ void TestCarryAndBitFields() {
|
||||
{.dwords = {low, high, inserted, sign}, .dword_count = 4});
|
||||
|
||||
DescriptorValue result;
|
||||
std::string error;
|
||||
Check(EvaluateDescriptorSource(fixture.program, 0, 0, {}, result, &error),
|
||||
error.c_str());
|
||||
Check(EvaluateDescriptorSource(fixture.program, 0, {}, result),
|
||||
"carry and bit-field descriptor evaluation failed");
|
||||
Check(result.dwords[0] == 1u && result.dwords[1] == 1u &&
|
||||
result.dwords[2] == 0x89abcdefu && result.dwords[3] == 0xffffffffu,
|
||||
"typed carry or bit-field runtime evaluation is incorrect");
|
||||
@@ -267,17 +267,13 @@ void TestInvariantAndDivergentPhi() {
|
||||
{.dwords = {Value(&divergent)}, .dword_count = 1});
|
||||
|
||||
DescriptorValue result;
|
||||
std::string error;
|
||||
Check(
|
||||
EvaluateDescriptorSource(fixture.program, 0, 0x100, {}, result, &error) &&
|
||||
result.dwords[0] == 7u,
|
||||
"loop-invariant typed phi was rejected");
|
||||
Check(EvaluateDescriptorSource(fixture.program, 0, {}, result) &&
|
||||
result.dwords[0] == 7u,
|
||||
"loop-invariant typed phi was rejected");
|
||||
result.dword_count = 4;
|
||||
result.dwords[0] = 0xdeadbeefu;
|
||||
Check(!EvaluateDescriptorSource(fixture.program, 1, 0x104, {}, result,
|
||||
&error) &&
|
||||
result.dword_count == 4 && result.dwords[0] == 0xdeadbeefu &&
|
||||
error.find("runtime-dependent") != std::string::npos,
|
||||
Check(!EvaluateDescriptorSource(fixture.program, 1, {}, result) &&
|
||||
result.dword_count == 4 && result.dwords[0] == 0xdeadbeefu,
|
||||
"divergent phi did not fail transactionally");
|
||||
}
|
||||
|
||||
@@ -318,8 +314,7 @@ void TestRuntime64BitDescriptorOps() {
|
||||
{.dwords = {low, high}, .dword_count = 2});
|
||||
|
||||
DescriptorValue result;
|
||||
std::string error;
|
||||
Check(EvaluateDescriptorSource(fixture.program, 0, 0, {}, result, &error) &&
|
||||
Check(EvaluateDescriptorSource(fixture.program, 0, {}, result) &&
|
||||
result.dwords[0] == 0xabcdu && result.dwords[1] == 0x1235u,
|
||||
"64-bit typed descriptor arithmetic evaluation is incorrect");
|
||||
}
|
||||
@@ -339,10 +334,9 @@ void TestConstantBufferBounds() {
|
||||
TestMemory memory_image{{{0x300cu, 0xa5a5a5a5u}}};
|
||||
SrtRuntime runtime{.read_memory = ReadMemory, .userdata = &memory_image};
|
||||
std::vector<uint32_t> flat;
|
||||
std::string error;
|
||||
Check(WalkSrt(fixture.program, runtime, flat, &error) &&
|
||||
Check(WalkSrt(fixture.program, runtime, flat) &&
|
||||
flat == std::vector<uint32_t>{0xa5a5a5a5u},
|
||||
error.c_str());
|
||||
"constant-buffer SRT walk failed");
|
||||
|
||||
Fixture overflow;
|
||||
const auto overflow_memory = overflow.AddMemory(ResourceKind::ScalarBuffer);
|
||||
@@ -356,9 +350,8 @@ void TestConstantBufferBounds() {
|
||||
{overflow_read, Value(0u), Value(16u), Value(0u)});
|
||||
overflow.Plan();
|
||||
flat = {0x55u};
|
||||
Check(!WalkSrt(overflow.program, runtime, flat, &error) &&
|
||||
flat == std::vector<uint32_t>{0x55u} &&
|
||||
error.find("exceeds size") != std::string::npos,
|
||||
Check(!WalkSrt(overflow.program, runtime, flat) &&
|
||||
flat == std::vector<uint32_t>{0x55u},
|
||||
"out-of-bounds constant-buffer walk was not transactional");
|
||||
}
|
||||
|
||||
@@ -420,10 +413,9 @@ void TestControlFlowValueSurvivesReadLaneFolding() {
|
||||
Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return;
|
||||
|
||||
const auto undef = fixture.Emit(ValueOpcode::UndefU32);
|
||||
const auto write = fixture.Emit(
|
||||
ValueOpcode::WriteLane, {undef, Value(42u), Value(5u)});
|
||||
const auto read =
|
||||
fixture.Emit(ValueOpcode::ReadLane, {write, Value(5u)});
|
||||
const auto write =
|
||||
fixture.Emit(ValueOpcode::WriteLane, {undef, Value(42u), Value(5u)});
|
||||
const auto read = fixture.Emit(ValueOpcode::ReadLane, {write, Value(5u)});
|
||||
entry_info.condition =
|
||||
fixture.Emit(ValueOpcode::IEqual32, {read, Value(42u)});
|
||||
fixture.Emit(ValueOpcode::Reference, {entry_info.condition});
|
||||
@@ -435,11 +427,9 @@ void TestControlFlowValueSurvivesReadLaneFolding() {
|
||||
RemoveIdentities(fixture.program.blocks);
|
||||
EliminateDeadCode(fixture.program.blocks);
|
||||
|
||||
std::string error;
|
||||
Check(entry_info.condition == Value(true) &&
|
||||
ValidateProgram(fixture.program, true, &error),
|
||||
error.empty() ? "folded branch condition did not survive identity removal"
|
||||
: error.c_str());
|
||||
Check(entry_info.condition == Value(true),
|
||||
"folded branch condition did not survive identity removal");
|
||||
ValidateProgram(fixture.program, true);
|
||||
}
|
||||
|
||||
void TestUndefinedRuntimeValueFails() {
|
||||
@@ -450,11 +440,8 @@ void TestUndefinedRuntimeValueFails() {
|
||||
{.dwords = {undef}, .dword_count = 1});
|
||||
DescriptorValue result;
|
||||
result.dword_count = 3;
|
||||
std::string error;
|
||||
Check(!EvaluateDescriptorSource(fixture.program, 0, 0x200, {}, result,
|
||||
&error) &&
|
||||
result.dword_count == 3 &&
|
||||
error.find("undefined typed runtime value") != std::string::npos,
|
||||
Check(!EvaluateDescriptorSource(fixture.program, 0, {}, result) &&
|
||||
result.dword_count == 3,
|
||||
"undefined typed descriptor source did not fail transactionally");
|
||||
}
|
||||
|
||||
@@ -464,6 +451,14 @@ namespace Common {
|
||||
|
||||
int DbgExitIfHandler(const char *, const char *, int) { return 1; }
|
||||
|
||||
int DbgExitHandler(const char *, int, std::string_view text) {
|
||||
throw std::runtime_error(std::string(text));
|
||||
}
|
||||
|
||||
int DbgExitHandler(const char *, int, fmt::text_style, std::string_view text) {
|
||||
throw std::runtime_error(std::string(text));
|
||||
}
|
||||
|
||||
void DbgExit(int) { std::abort(); }
|
||||
|
||||
} // namespace Common
|
||||
@@ -496,9 +491,9 @@ int main() {
|
||||
// Keep this focused standalone target self-contained by amalgamating its small
|
||||
// typed-IR implementation set.
|
||||
#include "../src/graphics/shader/recompiler/ir/Block.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/Program.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/Type.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/Value.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/Program.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/passes/ConstantPropagation.cpp"
|
||||
#include "../src/graphics/shader/recompiler/ir/passes/DeadCodeElimination.cpp"
|
||||
File diff suppressed because it is too large.
Load diff
@@ -52,7 +52,7 @@ SrtProgram(uint64_t address) {
|
||||
ValueOpcode::LoadAddressU32,
|
||||
{Value(&handle), Value(0u), Value(0u), Value(true)});
|
||||
raw.SetFlags(MemoryFlags{.index = 0, .pc = 0x40});
|
||||
program.srt_reads.push_back({Value(&raw), 0, 0x40});
|
||||
program.srt_reads.push_back({Value(&raw), 0});
|
||||
|
||||
auto &srt = value_block.AppendNewInst(ValueOpcode::GetSrtResource);
|
||||
auto &flat = value_block.AppendNewInst(ValueOpcode::ReadConst,
|
||||
@@ -77,17 +77,38 @@ UnbasedFlatProgram() {
|
||||
return std::make_shared<const Program>(std::move(program));
|
||||
}
|
||||
|
||||
std::shared_ptr<const Libs::Graphics::ShaderRecompiler::IR::Program>
|
||||
UserDataBufferProgram() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
Program program;
|
||||
program.stage = Libs::Graphics::ShaderType::Compute;
|
||||
program.srt_plan_complete = true;
|
||||
program.resource_tracking_complete = true;
|
||||
auto &value_block = AddValueBlock(program);
|
||||
|
||||
auto &user_data = value_block.AppendNewInst(
|
||||
ValueOpcode::GetUserData, {Value(static_cast<ScalarReg>(0))});
|
||||
DescriptorSource source;
|
||||
source.dwords[0] = Value(&user_data);
|
||||
source.dwords[1] = Value(0u);
|
||||
source.dwords[2] = Value(0u);
|
||||
source.dwords[3] = Value(0u);
|
||||
source.dword_count = 4;
|
||||
program.descriptor_sources.push_back(source);
|
||||
program.info.buffers.push_back({.source = 0});
|
||||
return std::make_shared<const Program>(std::move(program));
|
||||
}
|
||||
|
||||
void TestMappedSrtUsesDirectReaderByDefault() {
|
||||
using namespace Libs::Graphics;
|
||||
const uint32_t dword = 0x12345678;
|
||||
auto cached_program = SrtProgram(reinterpret_cast<uint64_t>(&dword));
|
||||
ShaderStageRuntime stage;
|
||||
std::string error;
|
||||
uint32_t specialization_reads = 0;
|
||||
Check(ShaderMaterializeStageRuntime(cached_program, {}, 0, stage, &error,
|
||||
Check(ShaderMaterializeStageRuntime(cached_program, {}, 0, stage,
|
||||
RejectSpecializationRead,
|
||||
&specialization_reads),
|
||||
error.c_str());
|
||||
"mapped SRT stage materialization failed");
|
||||
Check(specialization_reads == 0,
|
||||
"ordinary SRT read used the specialization reader");
|
||||
Check(stage.program == cached_program && stage.resources != nullptr,
|
||||
@@ -104,16 +125,36 @@ void TestUnbasedFlatCacheHitMaterializes() {
|
||||
auto prior_resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
ShaderStageRuntime stage{prior_program, prior_resources};
|
||||
std::string error;
|
||||
Check(ShaderMaterializeStageRuntime(cached_program, {}, 0, stage, &error), error.c_str());
|
||||
Check(ShaderMaterializeStageRuntime(cached_program, {}, 0, stage),
|
||||
"unbased FLAT stage materialization failed");
|
||||
Check(stage.program == cached_program && stage.resources != prior_resources,
|
||||
"unbased FLAT cache hit did not publish its BDA stage snapshot");
|
||||
}
|
||||
|
||||
void TestFailedMaterializationPreservesPriorStage() {
|
||||
using namespace Libs::Graphics;
|
||||
auto cached_program = UserDataBufferProgram();
|
||||
auto prior_program = std::make_shared<const ShaderRecompiler::IR::Program>();
|
||||
auto prior_resources =
|
||||
std::make_shared<const ShaderRecompiler::IR::ResourceSnapshot>();
|
||||
ShaderStageRuntime stage{prior_program, prior_resources};
|
||||
|
||||
Check(!ShaderMaterializeStageRuntime(cached_program, {}, 0, stage),
|
||||
"missing runtime user data did not reject the cached stage");
|
||||
Check(stage.program == prior_program && stage.resources == prior_resources,
|
||||
"failed cache materialization replaced the prior stage");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Common {
|
||||
|
||||
int DbgExitHandler(const char *, int, std::string_view) { std::abort(); }
|
||||
|
||||
int DbgExitHandler(const char *, int, fmt::text_style, std::string_view) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
int DbgExitIfHandler(const char *, const char *, int) { return 1; }
|
||||
|
||||
void DbgExit(int) { std::abort(); }
|
||||
@@ -123,6 +164,7 @@ void DbgExit(int) { std::abort(); }
|
||||
int main() {
|
||||
TestMappedSrtUsesDirectReaderByDefault();
|
||||
TestUnbasedFlatCacheHitMaterializes();
|
||||
TestFailedMaterializationPreservesPriorStage();
|
||||
std::puts("ShaderStageRuntimeTests: all cases passed");
|
||||
return 0;
|
||||
}
|
||||
@@ -130,7 +172,7 @@ int main() {
|
||||
// Keep this focused standalone target self-contained by amalgamating its small
|
||||
// typed-IR implementation set.
|
||||
#include "graphics/shader/recompiler/ir/Block.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Program.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Type.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Value.cpp"
|
||||
#include "graphics/shader/recompiler/ir/Program.cpp"
|
||||
#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp"
|
||||
+752
-1324
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user