mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
shader: read S_MEMREALTIME from the host device clock shifted to about 100 MHz
This commit is contained in:
@@ -43,6 +43,7 @@ struct GraphicContext {
|
|||||||
uint32_t min_subgroup_size = 0;
|
uint32_t min_subgroup_size = 0;
|
||||||
uint32_t max_subgroup_size = 0;
|
uint32_t max_subgroup_size = 0;
|
||||||
uint32_t max_push_descriptors = 0;
|
uint32_t max_push_descriptors = 0;
|
||||||
|
uint32_t device_clock_period_ps = 0;
|
||||||
vk::DescriptorSetLayout image_heap_layout = nullptr;
|
vk::DescriptorSetLayout image_heap_layout = nullptr;
|
||||||
vk::ShaderStageFlags required_subgroup_size_stages = {};
|
vk::ShaderStageFlags required_subgroup_size_stages = {};
|
||||||
Common::Mutex queue_mutex;
|
Common::Mutex queue_mutex;
|
||||||
|
|||||||
@@ -487,7 +487,8 @@ struct PipelineCache::ProgramCache {
|
|||||||
// signed-zero results; GLSL.std.450 alone does not order -0 below +0.
|
// signed-zero results; GLSL.std.450 alone does not order -0 below +0.
|
||||||
profile {.maintenance8 = graphics.maintenance8_enabled,
|
profile {.maintenance8 = graphics.maintenance8_enabled,
|
||||||
.native_float_min_max =
|
.native_float_min_max =
|
||||||
graphics.physical_device_properties.vendorID == 0x1002u} {
|
graphics.physical_device_properties.vendorID == 0x1002u,
|
||||||
|
.device_clock_period_ps = graphics.device_clock_period_ps} {
|
||||||
lookup_key.static_state.reserve(MaxStaticKeyWords);
|
lookup_key.static_state.reserve(MaxStaticKeyWords);
|
||||||
}
|
}
|
||||||
~ProgramCache() {
|
~ProgramCache() {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_vulkan.h>
|
#include <SDL3/SDL_vulkan.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
@@ -506,6 +507,13 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
|||||||
maintenance8.pNext = supported_features2.pNext;
|
maintenance8.pNext = supported_features2.pNext;
|
||||||
supported_features2.pNext = &maintenance8;
|
supported_features2.pNext = &maintenance8;
|
||||||
}
|
}
|
||||||
|
const bool shader_clock_extension =
|
||||||
|
HasExtension(device_extensions, VK_KHR_SHADER_CLOCK_EXTENSION_NAME);
|
||||||
|
vk::PhysicalDeviceShaderClockFeaturesKHR shader_clock {};
|
||||||
|
if (shader_clock_extension) {
|
||||||
|
shader_clock.pNext = supported_features2.pNext;
|
||||||
|
supported_features2.pNext = &shader_clock;
|
||||||
|
}
|
||||||
const bool image_atomic_int64_extension =
|
const bool image_atomic_int64_extension =
|
||||||
HasExtension(device_extensions, VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME);
|
HasExtension(device_extensions, VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME);
|
||||||
vk::PhysicalDeviceShaderImageAtomicInt64FeaturesEXT image_atomic_int64 {};
|
vk::PhysicalDeviceShaderImageAtomicInt64FeaturesEXT image_atomic_int64 {};
|
||||||
@@ -593,6 +601,11 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
|||||||
feedback_extensions && feedback_layout.attachmentFeedbackLoopLayout &&
|
feedback_extensions && feedback_layout.attachmentFeedbackLoopLayout &&
|
||||||
feedback_dynamic.attachmentFeedbackLoopDynamicState;
|
feedback_dynamic.attachmentFeedbackLoopDynamicState;
|
||||||
graphics.maintenance8_enabled = maintenance8_extension && maintenance8.maintenance8;
|
graphics.maintenance8_enabled = maintenance8_extension && maintenance8.maintenance8;
|
||||||
|
// The device clock ticks at timestampPeriod nanoseconds.
|
||||||
|
if (shader_clock_extension && shader_clock.shaderDeviceClock) {
|
||||||
|
graphics.device_clock_period_ps = static_cast<uint32_t>(
|
||||||
|
std::lround(graphics.physical_device_properties.limits.timestampPeriod * 1000.0f));
|
||||||
|
}
|
||||||
graphics.conditional_rendering_enabled =
|
graphics.conditional_rendering_enabled =
|
||||||
conditional_rendering_extension && conditional_rendering.conditionalRendering;
|
conditional_rendering_extension && conditional_rendering.conditionalRendering;
|
||||||
LOGF("Vulkan maintenance8 support: %s\n",
|
LOGF("Vulkan maintenance8 support: %s\n",
|
||||||
@@ -702,6 +715,10 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
|||||||
maintenance8.pNext = const_cast<void*>(create_info.pNext);
|
maintenance8.pNext = const_cast<void*>(create_info.pNext);
|
||||||
create_info.pNext = &maintenance8;
|
create_info.pNext = &maintenance8;
|
||||||
}
|
}
|
||||||
|
if (graphics.device_clock_period_ps != 0u) {
|
||||||
|
shader_clock.pNext = const_cast<void*>(create_info.pNext);
|
||||||
|
create_info.pNext = &shader_clock;
|
||||||
|
}
|
||||||
if (graphics.conditional_rendering_enabled) {
|
if (graphics.conditional_rendering_enabled) {
|
||||||
conditional_rendering.pNext = const_cast<void*>(create_info.pNext);
|
conditional_rendering.pNext = const_cast<void*>(create_info.pNext);
|
||||||
create_info.pNext = &conditional_rendering;
|
create_info.pNext = &conditional_rendering;
|
||||||
@@ -1077,6 +1094,7 @@ void WindowContext::CreateVulkan() {
|
|||||||
graphic_ctx.memory_budget_ext_enabled = true;
|
graphic_ctx.memory_budget_ext_enabled = true;
|
||||||
}
|
}
|
||||||
for (const auto* extension: {VK_KHR_MAINTENANCE_8_EXTENSION_NAME,
|
for (const auto* extension: {VK_KHR_MAINTENANCE_8_EXTENSION_NAME,
|
||||||
|
VK_KHR_SHADER_CLOCK_EXTENSION_NAME,
|
||||||
VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME,
|
VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME,
|
||||||
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME,
|
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME,
|
||||||
VK_EXT_MESH_SHADER_EXTENSION_NAME,
|
VK_EXT_MESH_SHADER_EXTENSION_NAME,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv {
|
|||||||
struct Profile {
|
struct Profile {
|
||||||
bool maintenance8 = false; // Dynamic texel offsets on image samples.
|
bool maintenance8 = false; // Dynamic texel offsets on image samples.
|
||||||
bool native_float_min_max = false; // NMin/NMax give the guest's NaN and signed-zero results.
|
bool native_float_min_max = false; // NMin/NMax give the guest's NaN and signed-zero results.
|
||||||
|
// OpReadClockKHR Device tick in picoseconds; 0 without shaderDeviceClock.
|
||||||
|
uint32_t device_clock_period_ps = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline constexpr Profile DefaultProfile {};
|
inline constexpr Profile DefaultProfile {};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <bit>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||||
@@ -881,6 +882,22 @@ uint32_t EmitGetShaderBase(ValueEmitContext& ctx) {
|
|||||||
return ctx.Def(IR::Value(uint64_t {0}));
|
return ctx.Def(IR::Value(uint64_t {0}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t EmitReadRealtimeClock(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||||
|
auto& state = ctx.state;
|
||||||
|
const auto period = state.profile.device_clock_period_ps;
|
||||||
|
if (period == 0u) {
|
||||||
|
ctx.Fail(inst, "requires shaderDeviceClock");
|
||||||
|
}
|
||||||
|
state.builder.RequireExtension("SPV_KHR_shader_clock");
|
||||||
|
state.builder.RequireCapability(spv::CapabilityShaderClockKHR);
|
||||||
|
const auto clock =
|
||||||
|
Unary(state, spv::OpReadClockKHR, TypeU64(state), ConstantU32(state, spv::ScopeDevice));
|
||||||
|
// S_MEMREALTIME counts at 100 MHz. Shift the host clock by the most bits that keep its tick at
|
||||||
|
// most 15000 ps (2/3 of 100 MHz): 1 GHz shifts by 3 to 125 MHz; 100 MHz and slower stay.
|
||||||
|
const uint32_t shift = std::bit_width(7500u / period);
|
||||||
|
return EmitShiftRightLogical64(ctx, clock, IR::Value(shift));
|
||||||
|
}
|
||||||
|
|
||||||
void EmitUnreachable(ValueEmitContext& ctx, const IR::Inst& inst) {
|
void EmitUnreachable(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||||
ctx.Fail(inst, "must be lowered before SPIR-V emission");
|
ctx.Fail(inst, "must be lowered before SPIR-V emission");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,6 +240,7 @@ uint32_t EmitGetAttributeF16(ValueEmitContext& ctx, const IR::Inst&
|
|||||||
uint32_t EmitGetInterpolationParameter(ValueEmitContext& ctx, const IR::Inst& inst);
|
uint32_t EmitGetInterpolationParameter(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||||
void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst);
|
void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||||
uint32_t EmitGetShaderBase(ValueEmitContext& ctx);
|
uint32_t EmitGetShaderBase(ValueEmitContext& ctx);
|
||||||
|
uint32_t EmitReadRealtimeClock(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||||
inline constexpr auto EmitGetSrtResource = EmitVoid;
|
inline constexpr auto EmitGetSrtResource = EmitVoid;
|
||||||
inline constexpr auto EmitGetBufferResource = EmitGetSrtResource;
|
inline constexpr auto EmitGetBufferResource = EmitGetSrtResource;
|
||||||
uint32_t EmitGetBufferResourceIndex(ValueEmitContext& ctx, const IR::Inst& inst);
|
uint32_t EmitGetBufferResourceIndex(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||||
|
|||||||
@@ -235,9 +235,9 @@ void EmitDirectInstruction(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
const auto op = inst.GetOpcode();
|
const auto op = inst.GetOpcode();
|
||||||
if (ctx.half != 0 &&
|
if (ctx.half != 0 &&
|
||||||
(op == Op::Ballot || op == Op::ReadFirstLane || op == Op::SubgroupUMinU32 ||
|
(op == Op::Ballot || op == Op::ReadFirstLane || op == Op::SubgroupUMinU32 ||
|
||||||
op == Op::ReadConstBuffer ||
|
op == Op::ReadConstBuffer || op == Op::ReadRealtimeClock ||
|
||||||
(op == Op::LoadAddressU32 && ctx.Memory(inst).kind == IR::ResourceKind::ScalarAddress))) {
|
(op == Op::LoadAddressU32 && ctx.Memory(inst).kind == IR::ResourceKind::ScalarAddress))) {
|
||||||
// Whole-wave results and SMEM loads (SGPR operands, no EXEC) are identical in both halves.
|
// Whole-wave results, clocks and SMEM loads (SGPR operands, no EXEC) match in both halves.
|
||||||
ctx.Define(inst, ctx.other_half->Result(inst));
|
ctx.Define(inst, ctx.other_half->Result(inst));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
|
||||||
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
||||||
#include "graphics/shader/recompiler/frontend/decode/ImageOps.h"
|
#include "graphics/shader/recompiler/frontend/decode/ImageOps.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||||
|
|
||||||
@@ -969,17 +967,9 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
|
|||||||
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX4:
|
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX4:
|
||||||
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX8:
|
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX8:
|
||||||
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX16: return S_LOAD(inst, false);
|
case Decoder::Opcode::S_BUFFER_LOAD_DWORDX16: return S_LOAD(inst, false);
|
||||||
case Decoder::Opcode::S_MEMREALTIME: {
|
case Decoder::Opcode::S_MEMREALTIME:
|
||||||
static std::atomic_flag warned = ATOMIC_FLAG_INIT;
|
return WriteU32Pair(inst.dst,
|
||||||
if (!warned.test_and_set(std::memory_order_relaxed)) {
|
ExtractU64(IR::U64(ir.Emit(IR::ValueOpcode::ReadRealtimeClock))));
|
||||||
Log::WriteToConsoleAndLog(
|
|
||||||
"Warning: S_MEMREALTIME uses placeholder UINT64_MAX; real-time clock not implemented.\n");
|
|
||||||
}
|
|
||||||
for (uint32_t component = 0; component < 2; component++) {
|
|
||||||
WriteOperand(ScalarDestinationOperand(inst.dst, component), IR::Value(UINT32_MAX));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Decoder::Opcode::BUFFER_LOAD_UBYTE:
|
case Decoder::Opcode::BUFFER_LOAD_UBYTE:
|
||||||
case Decoder::Opcode::BUFFER_LOAD_UBYTE_D16:
|
case Decoder::Opcode::BUFFER_LOAD_UBYTE_D16:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ VALUE_OPCODE(ConditionRef, U1, U1)
|
|||||||
VALUE_OPCODE(GetUserData, U32, ScalarReg)
|
VALUE_OPCODE(GetUserData, U32, ScalarReg)
|
||||||
VALUE_OPCODE(GetDispatchThreadExtent, U32, U32)
|
VALUE_OPCODE(GetDispatchThreadExtent, U32, U32)
|
||||||
VALUE_OPCODE(GetShaderBase, U64)
|
VALUE_OPCODE(GetShaderBase, U64)
|
||||||
|
VALUE_OPCODE(ReadRealtimeClock, U64)
|
||||||
VALUE_OPCODE(MeshDrawParameter, U32, U32)
|
VALUE_OPCODE(MeshDrawParameter, U32, U32)
|
||||||
VALUE_OPCODE(MeshAllocate, Void, U32)
|
VALUE_OPCODE(MeshAllocate, Void, U32)
|
||||||
VALUE_OPCODE(TessellationBase, U32, U32)
|
VALUE_OPCODE(TessellationBase, U32, U32)
|
||||||
|
|||||||
@@ -21246,8 +21246,10 @@ private:
|
|||||||
available_min_lod.pNext = &available_provoking_vertex;
|
available_min_lod.pNext = &available_provoking_vertex;
|
||||||
vk::PhysicalDeviceMaintenance8FeaturesKHR available_maintenance8{};
|
vk::PhysicalDeviceMaintenance8FeaturesKHR available_maintenance8{};
|
||||||
available_maintenance8.pNext = &available_min_lod;
|
available_maintenance8.pNext = &available_min_lod;
|
||||||
|
vk::PhysicalDeviceShaderClockFeaturesKHR available_clock{};
|
||||||
|
available_clock.pNext = &available_maintenance8;
|
||||||
vk::PhysicalDeviceRobustness2FeaturesEXT available_robustness2{};
|
vk::PhysicalDeviceRobustness2FeaturesEXT available_robustness2{};
|
||||||
available_robustness2.pNext = &available_maintenance8;
|
available_robustness2.pNext = &available_clock;
|
||||||
vk::PhysicalDeviceFeatures2 available_features2{};
|
vk::PhysicalDeviceFeatures2 available_features2{};
|
||||||
available_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2;
|
available_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2;
|
||||||
available_features2.pNext = &available_robustness2;
|
available_features2.pNext = &available_robustness2;
|
||||||
@@ -21295,6 +21297,8 @@ private:
|
|||||||
"image view minimum LOD is not supported");
|
"image view minimum LOD is not supported");
|
||||||
Require("VulkanHarness", "dispatch", available_maintenance8.maintenance8 == true,
|
Require("VulkanHarness", "dispatch", available_maintenance8.maintenance8 == true,
|
||||||
"maintenance8 dynamic image offsets are not supported");
|
"maintenance8 dynamic image offsets are not supported");
|
||||||
|
Require("VulkanHarness", "dispatch", available_clock.shaderDeviceClock == true,
|
||||||
|
"shader device clock is not supported");
|
||||||
Require("VulkanHarness", "graphics", available_features12.shaderOutputLayer == true,
|
Require("VulkanHarness", "graphics", available_features12.shaderOutputLayer == true,
|
||||||
"vertex layer output is not supported");
|
"vertex layer output is not supported");
|
||||||
m_rasterization_supported = available_features.fillModeNonSolid &&
|
m_rasterization_supported = available_features.fillModeNonSolid &&
|
||||||
@@ -21377,7 +21381,10 @@ private:
|
|||||||
: static_cast<void *>(&derivatives);
|
: static_cast<void *>(&derivatives);
|
||||||
vk::PhysicalDeviceMaintenance8FeaturesKHR maintenance8{};
|
vk::PhysicalDeviceMaintenance8FeaturesKHR maintenance8{};
|
||||||
maintenance8.maintenance8 = true;
|
maintenance8.maintenance8 = true;
|
||||||
maintenance8.pNext = &min_lod;
|
vk::PhysicalDeviceShaderClockFeaturesKHR shader_clock{};
|
||||||
|
shader_clock.shaderDeviceClock = true;
|
||||||
|
shader_clock.pNext = &min_lod;
|
||||||
|
maintenance8.pNext = &shader_clock;
|
||||||
vk::PhysicalDeviceRobustness2FeaturesEXT robustness2{};
|
vk::PhysicalDeviceRobustness2FeaturesEXT robustness2{};
|
||||||
robustness2.nullDescriptor = true;
|
robustness2.nullDescriptor = true;
|
||||||
robustness2.pNext = &maintenance8;
|
robustness2.pNext = &maintenance8;
|
||||||
@@ -21406,6 +21413,7 @@ private:
|
|||||||
VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME,
|
VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME,
|
||||||
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
|
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
|
||||||
VK_KHR_MAINTENANCE_8_EXTENSION_NAME,
|
VK_KHR_MAINTENANCE_8_EXTENSION_NAME,
|
||||||
|
VK_KHR_SHADER_CLOCK_EXTENSION_NAME,
|
||||||
VK_EXT_IMAGE_VIEW_MIN_LOD_EXTENSION_NAME,
|
VK_EXT_IMAGE_VIEW_MIN_LOD_EXTENSION_NAME,
|
||||||
VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME};
|
VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME};
|
||||||
if (m_rasterization_supported) {
|
if (m_rasterization_supported) {
|
||||||
@@ -31970,29 +31978,60 @@ TestCase ScalarBranchRestoresInactiveLanes(u32 wave_size, u32 threads, bool uppe
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase ScalarMemRealtimeCapturedPlaceholder() {
|
// Each case spins until S_MEMREALTIME advances 64 ticks, capped at 2^20 reads, and stores
|
||||||
|
// min(delta, 64). The profile period picks the host clock shift.
|
||||||
|
std::vector<TestCase> ScalarMemRealtimeCases() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
namespace D = ShaderRecompiler::Decoder;
|
namespace D = ShaderRecompiler::Decoder;
|
||||||
|
|
||||||
std::vector<u32> code = {0xf4940300u, 0xfa000000u};
|
std::vector<u32> code = {0xf4940300u, 0xfa000000u};
|
||||||
D::Instruction decoded;
|
D::Instruction decoded;
|
||||||
D::DecodeInstruction(code, 0, decoded);
|
D::DecodeInstruction(code, 0, decoded);
|
||||||
Require("ScalarMemRealtimeCapturedPlaceholder", "decode",
|
Require("ScalarMemRealtimeCases", "decode",
|
||||||
decoded.opcode == O::S_MEMREALTIME && decoded.word_count == 2 &&
|
decoded.opcode == O::S_MEMREALTIME && decoded.word_count == 2 &&
|
||||||
decoded.dst.kind == D::OperandKind::Sgpr && decoded.dst.reg == 12 &&
|
decoded.dst.kind == D::OperandKind::Sgpr && decoded.dst.reg == 12 &&
|
||||||
decoded.data_dwords == 2 && decoded.src_count == 0 &&
|
decoded.data_dwords == 2 && decoded.src_count == 0 &&
|
||||||
decoded.src0.kind == D::OperandKind::Unknown &&
|
decoded.src0.kind == D::OperandKind::Unknown &&
|
||||||
decoded.src1.kind == D::OperandKind::Unknown,
|
decoded.src1.kind == D::OperandKind::Unknown,
|
||||||
"captured clock instruction must write s12:s13 without memory operands");
|
"captured clock instruction must write s12:s13 without memory operands");
|
||||||
AppendStoreSgprPair(&code, 12, 0);
|
code.insert(code.end(),
|
||||||
code.insert(code.end(), {0xf4940300u, 0xfa000000u});
|
{EncodeSop1(0x03, 17, InlineU32(0)), // s_mov_b32 s17, 0
|
||||||
AppendStoreSgprPair(&code, 12, 2);
|
0xf4940380u, 0xfa000000u, // loop: s_memrealtime s[14:15]
|
||||||
|
EncodeSop2(0x01, 16, 14, 12), // s_sub_u32 s16, s14, s12
|
||||||
|
EncodeSop2(0x00, 17, 17, InlineU32(1)), // s_add_u32 s17, s17, 1
|
||||||
|
EncodeSopc(0x09, 16, InlineU32(64)), // s_cmp_ge_u32 s16, 64
|
||||||
|
EncodeSopp(0x05, 3), // s_cbranch_scc1 done
|
||||||
|
EncodeSopc(0x0a, 17, 255), 1u << 20, // s_cmp_lt_u32 s17, 1 << 20
|
||||||
|
EncodeSopp(0x05, static_cast<u32>(-9)), // s_cbranch_scc1 loop
|
||||||
|
EncodeSop2(0x07, 16, 16, InlineU32(64))}); // done: s_min_u32 s16, s16, 64
|
||||||
|
AppendStoreSgpr(&code, 16, 0);
|
||||||
AppendEnd(&code);
|
AppendEnd(&code);
|
||||||
return {"ScalarMemRealtimeCapturedPlaceholder",
|
|
||||||
code,
|
static const struct {
|
||||||
{},
|
const char *name;
|
||||||
{UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX},
|
ShaderProfile profile;
|
||||||
{O::S_MEMREALTIME, O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
std::vector<std::string> required, forbidden;
|
||||||
|
} variants[] = {
|
||||||
|
{"ScalarMemRealtimeShiftsNanosecondClock",
|
||||||
|
{.maintenance8 = true, .device_clock_period_ps = 1000},
|
||||||
|
{"OpShiftRightLogical %ulong", "%uint_3\n"},
|
||||||
|
{}},
|
||||||
|
{"ScalarMemRealtimeKeeps100MHzClock",
|
||||||
|
{.maintenance8 = true, .device_clock_period_ps = 10000},
|
||||||
|
{"OpReadClockKHR %ulong %uint_1"},
|
||||||
|
{"OpShiftRightLogical %ulong"}}};
|
||||||
|
std::vector<TestCase> tests;
|
||||||
|
for (const auto &variant : variants) {
|
||||||
|
TestCase test{variant.name, code, {}, {64},
|
||||||
|
{O::S_MEMREALTIME, O::S_MOV_B32, O::S_SUB_U32, O::S_ADD_U32, O::S_CMP_GE_U32,
|
||||||
|
O::S_CBRANCH_SCC1, O::S_CMP_LT_U32, O::S_MIN_U32, O::V_MOV_B32,
|
||||||
|
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||||
|
test.profile = &variant.profile;
|
||||||
|
test.required_spirv = variant.required;
|
||||||
|
test.forbidden_spirv = variant.forbidden;
|
||||||
|
tests.push_back(std::move(test));
|
||||||
|
}
|
||||||
|
return tests;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase ScalarMemoryLoadVariants() {
|
TestCase ScalarMemoryLoadVariants() {
|
||||||
@@ -44047,7 +44086,9 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase([] { return ScalarBranchRestoresInactiveLanes(32, 32, true); });
|
AddCase([] { return ScalarBranchRestoresInactiveLanes(32, 32, true); });
|
||||||
AddCase([] { return ScalarBranchRestoresInactiveLanes(64, 64, true); });
|
AddCase([] { return ScalarBranchRestoresInactiveLanes(64, 64, true); });
|
||||||
AddCase(BranchVccnzUsesCarryProducedWaveMask);
|
AddCase(BranchVccnzUsesCarryProducedWaveMask);
|
||||||
AddCase(ScalarMemRealtimeCapturedPlaceholder);
|
for (auto &test : ScalarMemRealtimeCases()) {
|
||||||
|
cases.push_back(std::move(test));
|
||||||
|
}
|
||||||
AddCase(ScalarMemoryLoadVariants);
|
AddCase(ScalarMemoryLoadVariants);
|
||||||
AddCase(ScalarBufferOffsetAlignmentAndCarry);
|
AddCase(ScalarBufferOffsetAlignmentAndCarry);
|
||||||
AddCase(ScalarLoadSignedImmediateOffsetAddsSoffset);
|
AddCase(ScalarLoadSignedImmediateOffsetAddsSoffset);
|
||||||
@@ -50124,7 +50165,9 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--s-memrealtime-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--s-memrealtime-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
RunCase(&vulkan, ScalarMemRealtimeCapturedPlaceholder());
|
for (const auto &test : ScalarMemRealtimeCases()) {
|
||||||
|
RunCase(&vulkan, test);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--packed-integer-neg-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--packed-integer-neg-only") == 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user