Compare commits

...
Author SHA1 Message Date
nmzik 9da390bc0a general: explicit SEC_COMMIT (Windows), default printf logging to silent 2026-08-29 15:31:20 +04:00
nmzik ef639610e7 shader: allow SDWA add-with-carry 2026-08-29 15:31:20 +04:00
Brandon Strongandnmzik 6c3b562822 graphics: support Standard64KB color target arrays (#315)
graphics: support Standard64KB render target arrays

Co-authored-by: nmzik <Nmzik@mail.ru>
2026-08-29 15:18:44 +04:00
nmzik f1413569ce fix windows build 2026-08-29 14:46:54 +04:00
nmzik fcc9dff5b9 shader: DS_WRITE_B16_D16_HI 2026-08-29 14:40:13 +04:00
nmzik d9ae3ed64c shader: V_FFBH_I32 2026-08-29 14:03:57 +04:00
nmzik 618926b124 network: implement resolver destruction 2026-08-29 13:40:56 +04:00
nmzik dca7ece2ca shader: legalize 1D level-zero gathers 2026-08-29 13:32:20 +04:00
nmzik ccecebfeff loader: report unhandled host exceptions 2026-08-29 13:01:57 +04:00
nmzikandFerran 1752f1382f graphical: add per-game Vulkan pipeline caching (driver-side improvements only for now)
Co-authored-by: Ferran <ferran.duarri@me.com>
2026-08-29 12:28:48 +04:00
nmzik 53a59434d8 shader: implement V_CMP_EQ_U64 2026-08-29 11:26:05 +04:00
GodsWill DevandSueryRD 18b6c9059b shader: support DS_READ_U16_D16_HI (#356)
Decode the high-half D16 LDS read and route it through the existing unsigned-short load lowering. Represent the destination as a high-half partial write so the low 16 bits of the VGPR remain intact. Add captured-instruction decode coverage and a Vulkan execution regression that verifies the preserved half.

Co-authored-by: SueryRD <the.ai.tester2@gmail.com>
2026-08-29 10:39:45 +04:00
GodsWill DevandSueryRD 715539804b shader: accept SDWA for VOP1 bit operations (#360)
Allow byte, word, and full-dword SDWA source selection for V_NOT_B32 and V_FFBL_B32. Add captured instruction tests that verify decoding, IR extraction, SPIR-V generation, and runtime results.

Co-authored-by: SueryRD <the.ai.tester2@gmail.com>
2026-08-29 10:28:28 +04:00
31 changed files with 692 additions and 110 deletions

No files matched your search

+1
View File
@@ -5,3 +5,4 @@ build/
_Build/vscode-clang/
_Build/
cmake-build-debug/
_PipelineCache/
+1 -1
View File
@@ -59,7 +59,7 @@ struct ConfigOptions {
bool command_buffer_dump_enabled = false;
std::filesystem::path command_buffer_dump_folder = "_Buffers";
bool graphics_debug_dump_enabled = false;
OutputDirection printf_direction = OutputDirection::Console;
OutputDirection printf_direction = OutputDirection::Silent;
std::filesystem::path printf_output_file = "_kyty.txt";
ProfilerDirection profiler_direction = ProfilerDirection::None;
bool spirv_debug_printf_enabled = false;
+3 -6
View File
@@ -25,9 +25,6 @@
#ifdef CopyFile
#undef CopyFile
#endif
#ifdef MoveFile
#undef MoveFile
#endif
#endif
namespace Common {
@@ -544,14 +541,14 @@ bool File::CopyFile(const std::filesystem::path& src,
return SysFileCopyFile(src, dst);
}
bool File::MoveFile(const std::filesystem::path& src,
const std::filesystem::path& dst) // @suppress("Member declaration not found")
bool File::RenameFile(const std::filesystem::path& src,
const std::filesystem::path& dst) // @suppress("Member declaration not found")
{
if (IsFileExisting(dst)) {
DeleteFile(dst); // @suppress("Invalid arguments")
}
return SysFileMoveFile(src, dst);
return SysFileRenameFile(src, dst);
}
void File::RemoveReadonly(const std::filesystem::path& name) {
+1 -4
View File
@@ -20,9 +20,6 @@
#ifdef CopyFile
#undef CopyFile
#endif
#ifdef MoveFile
#undef MoveFile
#endif
#endif
namespace Common {
@@ -107,7 +104,7 @@ public:
static std::vector<FindInfo> FindFiles(const std::filesystem::path& path);
static std::vector<DirEntry> GetDirEntries(const std::filesystem::path& path);
static bool CopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
static bool MoveFile(const std::filesystem::path& src, const std::filesystem::path& dst);
static bool RenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
static void RemoveReadonly(const std::filesystem::path& name);
KYTY_CLASS_NO_COPY(File);
+1 -1
View File
@@ -80,7 +80,7 @@ void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_fi
// NOLINTNEXTLINE(google-runtime-references)
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out);
bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
bool SysFileMoveFile(const std::filesystem::path& src, const std::filesystem::path& dst);
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
void SysFileRemoveReadonly(const std::filesystem::path& name);
#endif /* KYTY_COMMON_PLATFORM_SYSFILEIO_H_ */
+1 -1
View File
@@ -693,7 +693,7 @@ bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::pa
!error;
}
bool SysFileMoveFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
auto real_src = get_internal_name(src);
auto real_dst = get_internal_name(dst);
+1 -1
View File
@@ -565,7 +565,7 @@ bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::pa
return CopyFileW(src_wide.c_str(), dst_wide.c_str(), FALSE) != 0;
}
bool SysFileMoveFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
auto src_wide = src.wstring();
auto dst_wide = dst.wstring();
return MoveFileW(src_wide.c_str(), dst_wide.c_str()) != 0;
+5 -2
View File
@@ -1,5 +1,6 @@
set(KYTY_GIT_VERSION "unknown")
set(KYTY_GIT_HASH "unknown")
set(KYTY_GIT_REVISION "unknown")
if(GIT_EXECUTABLE)
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --tags --always --dirty
@@ -14,16 +15,18 @@ if(GIT_EXECUTABLE)
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-parse --short=7 HEAD
COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD
WORKING_DIRECTORY "${GIT_WORKING_DIRECTORY}"
OUTPUT_VARIABLE KYTY_GIT_HASH
OUTPUT_VARIABLE KYTY_GIT_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE GIT_HASH_RESULT
ERROR_QUIET
)
if(NOT GIT_HASH_RESULT EQUAL 0)
set(KYTY_GIT_HASH "unknown")
set(KYTY_GIT_REVISION "unknown")
else()
string(SUBSTRING "${KYTY_GIT_REVISION}" 0 7 KYTY_GIT_HASH)
execute_process(
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
WORKING_DIRECTORY "${GIT_WORKING_DIRECTORY}"
@@ -219,8 +219,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
rt.attrib.num_fragments, bytes_per_element, rt.cmask.addr, rt.fmask.addr,
rt.dcc_addr.addr);
}
if ((standard64 || depth_tile) && (rt.attrib3.dimension != 1 || rt.attrib3.depth != 0 ||
view.base_layer != 0 || view.image_layers != 1)) {
if ((standard64 || depth_tile) &&
(rt.attrib3.dimension != 1 || rt.attrib3.depth != 0 ||
(depth_tile && (view.base_layer != 0 || view.image_layers != 1)))) {
EXIT("unsupported 64KB texture-tiled render-target view: dimension=%u depth=%u"
" layer=%u/%u\n",
rt.attrib3.dimension, rt.attrib3.depth, view.base_layer, view.image_layers);
@@ -1,6 +1,7 @@
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
#include "common/assert.h"
#include "common/file.h"
#include "common/logging/log.h"
#include "common/profiler.h"
#include "graphics/guest_gpu/hardwareContext.h"
@@ -11,18 +12,64 @@
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/shader/shaderCompiler.h"
#include "kytyGitVersion.h"
#include "loader/systemContent.h"
#include <algorithm>
#include <atomic>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <limits>
#include <span>
#include <string_view>
#include <utility>
#include <vector>
#include <fmt/format.h>
#include <xxhash.h>
namespace Libs::Graphics {
namespace {
std::string DriverCacheSignature(const vk::PhysicalDeviceProperties& properties) {
constexpr char hex[] = "0123456789abcdef";
std::string uuid(VK_UUID_SIZE * 2, '0');
for (size_t i = 0; i < VK_UUID_SIZE; i++) {
uuid[i * 2] = hex[properties.pipelineCacheUUID[i] >> 4u];
uuid[i * 2 + 1] = hex[properties.pipelineCacheUUID[i] & 0xfu];
}
return fmt::format("KytyPC1:{}:{:08x}:{:08x}:{:08x}:{}\n", KYTY_GIT_REVISION,
properties.vendorID, properties.deviceID, properties.driverVersion, uuid);
}
std::string PipelineCacheTitleId() {
std::string title_id;
if ((!Loader::SystemContentParamSfoGetString("TITLE_ID", &title_id) || title_id.empty()) &&
(!Loader::SystemContentParamSfoGetString("CONTENT_ID", &title_id) || title_id.empty())) {
return {};
}
if (!std::ranges::all_of(title_id, [](unsigned char c) {
return std::isalnum(c) != 0 || c == '-' || c == '_';
})) {
return {};
}
return title_id;
}
template <typename... Args>
void PipelineCacheLog(fmt::format_string<Args...> format, Args&&... args) {
auto message = fmt::format(format, std::forward<Args>(args)...);
message += '\n';
if (Log::GetDirection() != Log::Direction::Console) {
std::fwrite(message.data(), 1, message.size(), stdout);
std::fflush(stdout);
}
Log::Write(message);
Log::Flush();
}
void NormalizeStaticParamsForDynamicState(PipelineStaticParameters& static_params) {
static_params.viewport_scale[0] = 0.5f;
static_params.viewport_scale[1] = 0.5f;
@@ -119,9 +166,11 @@ struct PipelineCache::ProgramCache {
PipelineCache::PipelineCache(GraphicContext& graphics)
: m_graphics(graphics), m_program_cache(std::make_unique<ProgramCache>(graphics.device)) {
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
InitializeDriverCache();
}
PipelineCache::~PipelineCache() {
Save();
auto destroy = [this](const auto& pipelines) {
for (const auto& [key, pipeline]: pipelines) {
(void)key;
@@ -138,6 +187,155 @@ PipelineCache::~PipelineCache() {
m_graphics.device.destroyShaderModule(permutation.handle.module, nullptr);
}
}
if (m_driver_cache != nullptr) {
m_graphics.device.destroyPipelineCache(m_driver_cache, nullptr);
}
}
void PipelineCache::InitializeDriverCache() {
const auto title_id = PipelineCacheTitleId();
if (title_id.empty()) {
return;
}
if (KYTY_BUILD != KYTY_BUILD_RELEASE) {
PipelineCacheLog("Vulkan pipeline cache: disabled (non-Release build)");
return;
}
const std::string_view git_hash = KYTY_GIT_HASH;
const std::string_view git_revision = KYTY_GIT_REVISION;
if (git_hash == "unknown" || git_revision == "unknown") {
PipelineCacheLog("Vulkan pipeline cache: disabled (unknown git revision)");
return;
}
if (git_hash.ends_with("-dirty")) {
PipelineCacheLog("Vulkan pipeline cache: disabled (dirty build)");
return;
}
m_driver_cache_path = std::filesystem::path("_PipelineCache") / (title_id + ".bin");
const auto path = Common::PathToString(m_driver_cache_path);
const bool cache_exists = Common::File::IsFileExisting(m_driver_cache_path);
if (cache_exists) {
PipelineCacheLog("Vulkan pipeline cache: loading {}", path);
} else {
PipelineCacheLog("Vulkan pipeline cache: initializing {}", path);
}
std::vector<uint8_t> initial_data;
if (cache_exists) {
Common::File file(m_driver_cache_path, Common::File::Mode::Read);
const auto file_size = file.IsInvalid() ? 0 : file.Size();
const auto signature = DriverCacheSignature(m_graphics.GetPhysicalDeviceProperties());
if (file_size >= signature.size() + sizeof(uint64_t) &&
file_size <= std::numeric_limits<uint32_t>::max()) {
std::string cached_signature(signature.size(), '\0');
uint64_t payload_hash = 0;
initial_data.resize(file_size - signature.size() - sizeof(payload_hash));
uint32_t signature_read = 0;
uint32_t hash_read = 0;
uint32_t payload_read = 0;
file.Read(cached_signature.data(), static_cast<uint32_t>(cached_signature.size()),
&signature_read);
file.Read(&payload_hash, sizeof(payload_hash), &hash_read);
file.Read(initial_data.data(), static_cast<uint32_t>(initial_data.size()), &payload_read);
file.Close();
if (signature_read != cached_signature.size() || hash_read != sizeof(payload_hash) ||
payload_read != initial_data.size() || cached_signature != signature ||
XXH3_64bits(initial_data.data(), initial_data.size()) != payload_hash) {
initial_data.clear();
PipelineCacheLog(
"Vulkan pipeline cache: invalidating {} (driver, emulator, or data mismatch)",
path);
}
} else {
file.Close();
PipelineCacheLog("Vulkan pipeline cache: invalidating {} (invalid file size)",
path);
}
}
vk::PipelineCacheCreateInfo create {};
create.sType = vk::StructureType::ePipelineCacheCreateInfo;
create.initialDataSize = initial_data.size();
create.pInitialData = initial_data.empty() ? nullptr : initial_data.data();
auto result = m_graphics.device.createPipelineCache(&create, nullptr, &m_driver_cache);
if (result != vk::Result::eSuccess && !initial_data.empty()) {
PipelineCacheLog("Vulkan pipeline cache: driver rejected {} ({}); starting empty", path,
VulkanToString(result));
initial_data.clear();
create.initialDataSize = 0;
create.pInitialData = nullptr;
result = m_graphics.device.createPipelineCache(&create, nullptr, &m_driver_cache);
}
if (result != vk::Result::eSuccess) {
PipelineCacheLog("Vulkan pipeline cache: disabled ({})", VulkanToString(result));
m_driver_cache = nullptr;
return;
}
if (!initial_data.empty()) {
PipelineCacheLog("Vulkan pipeline cache: loaded {} bytes from {}", initial_data.size(),
path);
} else {
PipelineCacheLog("Vulkan pipeline cache: initialized empty");
}
}
void PipelineCache::Save() {
Common::LockGuard lock(m_mutex);
if (m_driver_cache == nullptr) {
return;
}
size_t size = 0;
vk::Result result;
std::vector<uint8_t> payload;
for (uint32_t attempt = 0; attempt < 3; attempt++) {
size = 0;
result = m_graphics.device.getPipelineCacheData(m_driver_cache, &size, nullptr);
if (result != vk::Result::eSuccess || size == 0 ||
size > std::numeric_limits<uint32_t>::max()) {
break;
}
payload.resize(size);
result = m_graphics.device.getPipelineCacheData(m_driver_cache, &size, payload.data());
if (result != vk::Result::eIncomplete) {
break;
}
}
if (result != vk::Result::eSuccess || size == 0 ||
size > std::numeric_limits<uint32_t>::max()) {
PipelineCacheLog("Vulkan pipeline cache: save failed ({}, {} bytes)",
VulkanToString(result), size);
return;
}
payload.resize(size);
auto prefix = DriverCacheSignature(m_graphics.GetPhysicalDeviceProperties());
const auto payload_hash = XXH3_64bits(payload.data(), payload.size());
prefix.append(reinterpret_cast<const char*>(&payload_hash), sizeof(payload_hash));
if (!Common::File::CreateDirectories(m_driver_cache_path.parent_path())) {
PipelineCacheLog("Vulkan pipeline cache: failed to create cache directory");
return;
}
auto temp_path = m_driver_cache_path;
temp_path += ".tmp";
Common::File file;
uint32_t prefix_written = 0;
uint32_t payload_written = 0;
if (file.Create(temp_path)) {
file.Write(prefix.data(), static_cast<uint32_t>(prefix.size()), &prefix_written);
file.Write(payload.data(), static_cast<uint32_t>(payload.size()), &payload_written);
}
const bool flushed = !file.IsInvalid() && file.Flush();
file.Close();
if (prefix_written != prefix.size() || payload_written != payload.size() || !flushed ||
!Common::File::RenameFile(temp_path, m_driver_cache_path)) {
PipelineCacheLog("Vulkan pipeline cache: failed to write {}",
Common::PathToString(m_driver_cache_path));
return;
}
PipelineCacheLog("Vulkan pipeline cache: saved {} bytes to {}", payload.size(),
Common::PathToString(m_driver_cache_path));
m_graphics.device.destroyPipelineCache(m_driver_cache, nullptr);
m_driver_cache = nullptr;
}
ShaderProgram PipelineCache::GetVertexProgram(const HW::VertexShaderInfo& regs,
@@ -324,7 +522,7 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
auto cached = std::make_unique<GraphicsPipeline>(p);
LogPipelineTrace("CreatePipelineInternal begin", vs_id, ps_id);
CreatePipelineInternal(m_graphics, *cached, rendering, vs_input_info, vertex_program.module,
ps_input_info, pixel_program.module, static_params);
ps_input_info, pixel_program.module, static_params, m_driver_cache);
LogPipelineTrace("CreatePipelineInternal done", vs_id, ps_id);
EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr);
@@ -360,7 +558,7 @@ PipelineCache::CreateComputePipeline(ShaderComputeInputInfo& input_info,
}
auto cached = std::make_unique<ComputePipeline>(p);
CreatePipelineInternal(m_graphics, *cached, input_info, compute_program.module);
CreatePipelineInternal(m_graphics, *cached, input_info, compute_program.module, m_driver_cache);
EXIT_NOT_IMPLEMENTED(cached->pipeline == nullptr);
EXIT_NOT_IMPLEMENTED(cached->pipeline_layout == nullptr);
@@ -10,6 +10,7 @@
#include "graphics/shader/shader.h"
#include <cstddef>
#include <filesystem>
#include <memory>
#include <span>
#include <type_traits>
@@ -109,6 +110,7 @@ public:
explicit PipelineCache(GraphicContext& graphics);
~PipelineCache();
KYTY_CLASS_NO_COPY(PipelineCache);
void Save();
struct Pipeline {
vk::PipelineLayout pipeline_layout = nullptr;
@@ -214,12 +216,16 @@ private:
GraphicContext& m_graphics;
std::unique_ptr<ProgramCache> m_program_cache;
vk::PipelineCache m_driver_cache = nullptr;
std::filesystem::path m_driver_cache_path;
std::unordered_map<GraphicsPipelineKey, std::unique_ptr<GraphicsPipeline>,
GraphicsPipelineKeyHash>
m_graphics_pipelines;
std::unordered_map<ComputePipelineKey, std::unique_ptr<ComputePipeline>, ComputePipelineKeyHash>
m_compute_pipelines;
Common::Mutex m_mutex;
void InitializeDriverCache();
};
void LogPipelineTrace(const char* phase, uint64_t vertex_program_id, uint64_t pixel_program_id);
@@ -227,10 +233,11 @@ void CreatePipelineInternal(
GraphicContext& graphics, PipelineCache::GraphicsPipeline& pipeline,
const PipelineRenderingState& rendering, const ShaderVertexInputInfo& vs_input_info,
vk::ShaderModule vertex_module, const ShaderPixelInputInfo* ps_input_info,
vk::ShaderModule pixel_module, const PipelineStaticParameters& static_params);
vk::ShaderModule pixel_module, const PipelineStaticParameters& static_params,
vk::PipelineCache driver_cache);
void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipeline& pipeline,
const ShaderComputeInputInfo& input_info,
vk::ShaderModule compute_module);
vk::ShaderModule compute_module, vk::PipelineCache driver_cache);
} // namespace Libs::Graphics
@@ -423,7 +423,8 @@ void CreatePipelineInternal(
GraphicContext& graphics, PipelineCache::GraphicsPipeline& pipeline,
const PipelineRenderingState& rendering, const ShaderVertexInputInfo& vs_input_info,
vk::ShaderModule vertex_module, const ShaderPixelInputInfo* ps_input_info,
vk::ShaderModule pixel_module, const PipelineStaticParameters& static_params) {
vk::ShaderModule pixel_module, const PipelineStaticParameters& static_params,
vk::PipelineCache driver_cache) {
const bool ps_active = ps_input_info != nullptr;
EXIT_IF(vertex_module == nullptr || (ps_active && pixel_module == nullptr));
@@ -940,7 +941,7 @@ void CreatePipelineInternal(
viewport.y, viewport.width, viewport.height, scissor.offset.x, scissor.offset.y,
scissor.extent.width, scissor.extent.height);
}
result = graphics.device.createGraphicsPipelines(nullptr, 1, &pipeline_info, nullptr,
result = graphics.device.createGraphicsPipelines(driver_cache, 1, &pipeline_info, nullptr,
&pipeline.pipeline);
if (graphics_debug_dump_enabled()) {
LOGF("PipelineTrace: vkCreateGraphicsPipelines done result=%s pipeline=%p\n",
@@ -961,7 +962,7 @@ void CreatePipelineInternal(
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipeline& pipeline,
const ShaderComputeInputInfo& input_info,
vk::ShaderModule compute_module) {
vk::ShaderModule compute_module, vk::PipelineCache driver_cache) {
EXIT_IF(compute_module == nullptr);
vk::PipelineShaderStageCreateInfo comp_shader_stage_info {};
@@ -1024,7 +1025,8 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipe
LOGF("PipelineTrace: vkCreateComputePipelines begin layout=%p\n",
static_cast<void*>(pipeline.pipeline_layout));
result = graphics.device.createComputePipelines(nullptr, 1, &info, nullptr, &pipeline.pipeline);
result = graphics.device.createComputePipelines(driver_cache, 1, &info, nullptr,
&pipeline.pipeline);
LOGF("PipelineTrace: vkCreateComputePipelines done result=%s pipeline=%p\n",
VulkanToString(result).c_str(), static_cast<void*>(pipeline.pipeline));
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
@@ -28,6 +28,7 @@
#include "common/timer.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/renderer/render.h"
#include "graphics/host_gpu/renderer/renderContext.h"
#include "graphics/host_gpu/vma.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include "graphics/presentation/imeOverlay.h"
@@ -858,6 +859,7 @@ void WindowRun() {
EXIT_IF(g_window == nullptr);
g_window->Run();
g_window->render_context->GetPipelineCache().Save();
}
void WindowShutdown() {
@@ -439,6 +439,49 @@ uint32_t UnpackImageGather(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uin
return result;
}
uint32_t EmitOneDimensionalGatherLz(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t pc,
uint32_t coord, bool integer) {
auto& state = ctx.state;
state.builder.RequireCapability(CapabilityImageQuery);
const auto image = LoadSampledImageDescriptor(state, mem, pc, ImageViewKind::Dim1D);
const auto width = state.builder.AllocateId();
state.builder.AddFunction(
{OpImageQuerySizeLod, TypeU32(state), width, image, ConstantU32(state, 0)});
const auto width_f32 = state.builder.AllocateId();
state.builder.AddFunction({OpConvertUToF, TypeF32(state), width_f32, width});
const auto left = state.builder.AllocateId();
state.builder.AddFunction(
{OpExtInst, TypeF32(state), left, GlslStd450(state), GlslFloor,
Binary(state, OpFSub, TypeF32(state),
Binary(state, OpFMul, TypeF32(state), coord, width_f32),
ConstantF32(state, 0x3f000000u))});
const auto sampled = MakeSampledImage(state, mem, pc, ImageViewKind::Dim1D);
const auto vector_type = integer ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
const auto scalar_type = integer ? TypeU32(state) : TypeF32(state);
const auto component = ImageConversionFormat(state, mem).format == Prospero::BufferFormat::kInvalid
? ImageGatherComponent(mem.dmask)
: 0u;
uint32_t values[2] {};
for (uint32_t index = 0; index < 2u; index++) {
const auto sample_coord = Binary(
state, OpFDiv, TypeF32(state),
Binary(state, OpFAdd, TypeF32(state), left,
ConstantF32(state, index == 0u ? 0x3f000000u : 0x3fc00000u)),
width_f32);
const auto texel = state.builder.AllocateId();
state.builder.AddFunction({OpImageSampleExplicitLod, vector_type, texel, sampled, sample_coord,
ImageOperandsLodMask, ZeroF32(state)});
values[index] = state.builder.AllocateId();
state.builder.AddFunction(
{OpCompositeExtract, scalar_type, values[index], texel, component});
}
const auto result = state.builder.AllocateId();
state.builder.AddFunction({OpCompositeConstruct, vector_type, result, values[0], values[1],
values[1], values[0]});
return result;
}
uint32_t PackImageTexel(ValueEmitContext& ctx, const IR::MemoryInfo& mem, uint32_t texel) {
const auto info = ImageConversionFormat(ctx.state, mem);
if (info.format == Prospero::BufferFormat::kInvalid) return texel;
@@ -613,6 +656,22 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto coord =
CoordF32(ctx, mem, *address, layout.coord, ImageViewCoordinateComponents(view));
if (op == IR::ValueOpcode::ImageGatherRaw) {
if (view == ImageViewKind::Dim1D) {
if (dref || !HasFlag(mem, Decoder::ImageSampleFlagLevelZero) ||
HasFlag(mem, Decoder::ImageSampleFlagOffset) ||
HasFlag(mem, Decoder::ImageSampleFlagGatherHorizontal)) {
ctx.Fail(inst, "has an unsupported 1D gather variant");
return true;
}
const auto sample = EmitOneDimensionalGatherLz(ctx, mem, pc, coord, integer);
ctx.Define(inst, ResultVector(ctx, UnpackImageGather(ctx, mem, sample), integer,
false, mem, true));
return true;
}
if (view == ImageViewKind::Dim1DArray) {
ctx.Fail(inst, "has an unsupported 1D-array gather");
return true;
}
const auto sampled = MakeSampledImage(state, mem, pc, view);
const auto sample = state.builder.AllocateId();
std::vector<uint32_t> words =
@@ -103,7 +103,9 @@ constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = {
{0x4du, Opcode::DS_WRITE_B64, 2, 32}, {0x4eu, Opcode::DS_WRITE2_B64, 4, 32},
{0x4fu, Opcode::DS_WRITE2ST64_B64, 4, 32}, {0x76u, Opcode::DS_READ_B64, 2, 32},
{0x77u, Opcode::DS_READ2_B64, 4, 32}, {0x78u, Opcode::DS_READ2ST64_B64, 4, 32},
{0xa1u, Opcode::DS_WRITE_B16_D16_HI, 1, 16},
{0xa6u, Opcode::DS_READ_U16_D16, 1, 16},
{0xa7u, Opcode::DS_READ_U16_D16_HI, 1, 16},
{0xb0u, Opcode::DS_WRITE_ADDTID_B32, 1, 32}, {0xb1u, Opcode::DS_READ_ADDTID_B32, 1, 32},
{0xdeu, Opcode::DS_WRITE_B96, 3, 32}, {0xdfu, Opcode::DS_WRITE_B128, 4, 32},
{0xfeu, Opcode::DS_READ_B96, 3, 32}, {0xffu, Opcode::DS_READ_B128, 4, 32},
@@ -147,6 +149,7 @@ bool IsDsWriteOpcode(Opcode opcode) {
switch (opcode) {
case Opcode::DS_WRITE_B8:
case Opcode::DS_WRITE_B16:
case Opcode::DS_WRITE_B16_D16_HI:
case Opcode::DS_WRITE2_B32:
case Opcode::DS_WRITE2ST64_B32:
case Opcode::DS_WRITE2_B64:
@@ -421,13 +424,17 @@ void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
}
DecodeVectorGpr(vdst, inst.dst);
if (inst.opcode == Opcode::DS_READ_U16_D16) {
if (inst.opcode == Opcode::DS_READ_U16_D16 ||
inst.opcode == Opcode::DS_READ_U16_D16_HI) {
// Native D16 reads update only the selected destination half. Reuse the partial
// destination representation so typed IR translation preserves the upper word.
inst.dst.sdwa_sel = 4u;
// destination representation so typed IR translation preserves the other half.
inst.dst.sdwa_sel = inst.opcode == Opcode::DS_READ_U16_D16 ? 4u : 5u;
}
DecodeVectorGpr(addr, inst.src0);
DecodeVectorGpr(data0, inst.src1);
if (inst.opcode == Opcode::DS_WRITE_B16_D16_HI) {
inst.src1.sdwa_sel = 5u;
}
DecodeVectorGpr(data1, inst.src2);
inst.src_count = DsSourceCount(inst.opcode);
}
@@ -675,6 +675,7 @@ std::string InstructionToString(const Instruction& inst) {
case Opcode::DS_READ_B128:
case Opcode::DS_WRITE_B8:
case Opcode::DS_WRITE_B16:
case Opcode::DS_WRITE_B16_D16_HI:
case Opcode::DS_WRITE2_B32:
case Opcode::DS_WRITE2ST64_B32:
case Opcode::DS_WRITE2_B64:
@@ -185,6 +185,7 @@ enum class Opcode {
V_BFREV_B32,
V_FFBH_U32,
V_FFBL_B32,
V_FFBH_I32,
V_ADD_F32,
V_SUB_F32,
V_SUBREV_F32,
@@ -391,6 +392,7 @@ enum class Opcode {
V_CMP_GE_U32,
V_CMP_T_U32,
V_CMP_EQ_I64,
V_CMP_EQ_U64,
V_CMP_GT_U64,
V_CMP_NE_U64,
V_CMPX_NE_I64,
@@ -497,6 +499,7 @@ enum class Opcode {
DS_READ_I16,
DS_READ_U16,
DS_READ_U16_D16,
DS_READ_U16_D16_HI,
DS_READ2_B32,
DS_READ2ST64_B32,
DS_READ_B32,
@@ -507,6 +510,7 @@ enum class Opcode {
DS_READ_B128,
DS_WRITE_B8,
DS_WRITE_B16,
DS_WRITE_B16_D16_HI,
DS_WRITE2_B32,
DS_WRITE2ST64_B32,
DS_WRITE2_B64,
@@ -59,7 +59,7 @@ constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = {
{0x23u, Opcode::V_MBCNT_LO_U32_B32},
{0x24u, Opcode::V_MBCNT_HI_U32_B32},
{0x25u, Opcode::V_ADD_NC_U32, Vop2SdwaProfile::IntegerPartialDestination},
{0x28u, Opcode::V_ADDC_U32},
{0x28u, Opcode::V_ADDC_U32, Vop2SdwaProfile::IntegerFullDestination},
{0x2au, Opcode::V_SUBREV_CO_CI_U32},
{0x26u, Opcode::V_SUB_NC_U32, Vop2SdwaProfile::IntegerPartialDestination},
{0x27u, Opcode::V_SUBREV_NC_U32, Vop2SdwaProfile::IntegerFullDestination},
@@ -119,6 +119,7 @@ constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
{0x38u, Opcode::V_BFREV_B32},
{0x39u, Opcode::V_FFBH_U32},
{0x3au, Opcode::V_FFBL_B32},
{0x3bu, Opcode::V_FFBH_I32},
{0x3fu, Opcode::V_FREXP_EXP_I32_F32},
{0x40u, Opcode::V_FREXP_MANT_F32},
{0x42u, Opcode::V_MOVRELD_B32},
@@ -169,6 +170,7 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPCODE_LIST[] = {
{0x38u, Opcode::V_BFREV_B32},
{0x39u, Opcode::V_FFBH_U32},
{0x3au, Opcode::V_FFBL_B32},
{0x3bu, Opcode::V_FFBH_I32},
{0x3fu, Opcode::V_FREXP_EXP_I32_F32},
{0x40u, Opcode::V_FREXP_MANT_F32},
{0x42u, Opcode::V_MOVRELD_B32},
@@ -234,7 +236,8 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = {
{0xd1u, Opcode::V_CMPX_LT_U32}, {0xd2u, Opcode::V_CMPX_EQ_U32},
{0xd3u, Opcode::V_CMPX_LE_U32}, {0xd4u, Opcode::V_CMPX_GT_U32},
{0xd5u, Opcode::V_CMPX_NE_U32}, {0xd6u, Opcode::V_CMPX_GE_U32},
{0xe4u, Opcode::V_CMP_GT_U64, false}, {0xe5u, Opcode::V_CMP_NE_U64, false},
{0xe2u, Opcode::V_CMP_EQ_U64, false}, {0xe4u, Opcode::V_CMP_GT_U64, false},
{0xe5u, Opcode::V_CMP_NE_U64, false},
{0xf5u, Opcode::V_CMPX_NE_U64, false},
{0xc9u, Opcode::V_CMP_LT_F16}, {0xcau, Opcode::V_CMP_EQ_F16},
{0xcbu, Opcode::V_CMP_LE_F16}, {0xccu, Opcode::V_CMP_GT_F16},
@@ -515,6 +518,8 @@ constexpr Vop1SdwaRule VOP1_SDWA_RULES[] = {
{Opcode::V_CVT_F32_U32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_CVT_F32_I32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_CVT_F32_UBYTE0, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_NOT_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_FFBL_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
{Opcode::V_CVT_F32_F16, SdwaSelWords() | SdwaSelFull(), 0, 0, true},
{Opcode::V_CVT_F16_F32, SdwaSelFull(), SdwaSelWords(), SdwaSelFull(), false},
{Opcode::V_CVT_F16_U16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
@@ -224,11 +224,14 @@ bool Translator::S_FF1_I32_B64(const Decoder::Instruction& inst) {
return true;
}
bool Translator::V_FFBH_U32(const Decoder::Instruction& inst) {
const auto source = ReadU32(inst.src0);
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {source}));
bool Translator::V_FFBH_32(const Decoder::Instruction& inst, bool sign) {
const auto source = ReadU32(inst.src0);
const auto value = sign ? ir.BitwiseXor(
source, ir.ShiftRightArithmetic(source, IR::U32(IR::Value(31u))))
: source;
const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {value}));
const auto position = ir.ISub(IR::U32(IR::Value(31u)), msb);
const auto result = ir.Select(ir.INotEqual(source, IR::U32(IR::Value(0u))), position,
const auto result = ir.Select(ir.INotEqual(value, IR::U32(IR::Value(0u))), position,
IR::U32(IR::Value(0xffffffffu)));
WriteOperand(DestinationOperand(inst), result);
return true;
@@ -257,6 +257,7 @@ Decoder::Operand MemorySourceAt(const Decoder::Instruction& decoded, uint32_t in
return index == 0u ? decoded.src1 : index == 1u ? decoded.src0 : decoded.src2;
case Decoder::Opcode::DS_WRITE_B8:
case Decoder::Opcode::DS_WRITE_B16:
case Decoder::Opcode::DS_WRITE_B16_D16_HI:
case Decoder::Opcode::DS_WRITE_B32:
case Decoder::Opcode::DS_WRITE_B64:
case Decoder::Opcode::DS_WRITE_B96:
@@ -1050,6 +1051,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error
case Decoder::Opcode::DS_READ_I16:
case Decoder::Opcode::DS_READ_U16:
case Decoder::Opcode::DS_READ_U16_D16:
case Decoder::Opcode::DS_READ_U16_D16_HI:
case Decoder::Opcode::DS_READ_B32:
case Decoder::Opcode::DS_READ_B64:
case Decoder::Opcode::DS_READ_B96:
@@ -1060,6 +1062,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error
case Decoder::Opcode::DS_WRITE2ST64_B64: return DS_WRITE2(inst);
case Decoder::Opcode::DS_WRITE_B8:
case Decoder::Opcode::DS_WRITE_B16:
case Decoder::Opcode::DS_WRITE_B16_D16_HI:
case Decoder::Opcode::DS_WRITE_B32:
case Decoder::Opcode::DS_WRITE_B64:
case Decoder::Opcode::DS_WRITE_B96:
@@ -182,7 +182,7 @@ private:
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_U32(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);
@@ -96,6 +96,7 @@ bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual32, IR::Type::U32, false, true);
return true;
case O::V_CMP_EQ_I64:
case O::V_CMP_EQ_U64:
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, false, false);
return true;
case O::V_CMP_GT_U64:
@@ -490,7 +491,8 @@ bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error
case O::V_AND_OR_B32: return V_AND_OR_B32(inst);
case O::V_OR3_B32: return V_OR3_B32(inst);
case O::V_XOR3_B32: return V_XOR3_B32(inst);
case O::V_FFBH_U32: return V_FFBH_U32(inst);
case O::V_FFBH_U32: return V_FFBH_32(inst, false);
case O::V_FFBH_I32: return V_FFBH_32(inst, true);
case O::V_MAD_I32_I24: return Integer24(inst, true, true);
case O::V_MAD_U32_U24: return Integer24(inst, false, true);
+1 -1
View File
@@ -1066,7 +1066,7 @@ int KYTY_SYSV_ABI KernelRename(const char* from, const char* to) {
Common::File::DeleteFile(real_to);
}
if (!Common::File::MoveFile(real_from, real_to)) {
if (!Common::File::RenameFile(real_from, real_to)) {
return KERNEL_ERROR_EIO;
}
+3 -3
View File
@@ -44,9 +44,9 @@ public:
explicit GuestBackingStore(uint64_t size): m_size(size) {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
m_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_EXECUTE_READWRITE,
static_cast<DWORD>(m_size >> 32u),
static_cast<DWORD>(m_size & 0xffffffffu), nullptr);
m_handle = CreateFileMappingA(
INVALID_HANDLE_VALUE, nullptr, PAGE_EXECUTE_READWRITE | SEC_COMMIT,
static_cast<DWORD>(m_size >> 32u), static_cast<DWORD>(m_size & 0xffffffffu), nullptr);
if (m_handle == nullptr) {
LOGF_COLOR(Log::Color::Red,
"\t direct-memory backing: CreateFileMapping failed: 0x%08" PRIx32 "\n",
+3
View File
@@ -1,7 +1,10 @@
#include "cmake_config.h"
#define KYTY_GIT_VERSION "@KYTY_GIT_VERSION@"
// Short, human-facing commit name used in build labels.
#define KYTY_GIT_HASH "@KYTY_GIT_HASH@"
// Full commit identity used for compatibility checks.
#define KYTY_GIT_REVISION "@KYTY_GIT_REVISION@"
/* Build provenance, shown in the window title and in crash reports. */
#if defined(KYTY_OFFICIAL_BUILD)
+5
View File
@@ -113,6 +113,10 @@ int KYTY_SYSV_ABI NetResolverCreate(const char* name, int memid, int flags) {
return NET_CALL(Net::NetResolverCreate(name, memid, flags));
}
int KYTY_SYSV_ABI NetResolverDestroy(int rid) {
return NET_CALL(Net::NetResolverDestroy(rid));
}
int KYTY_SYSV_ABI NetResolverStartNtoa(int rid, const char* hostname, void* addr, int timeout,
int retry, int flags) {
return NET_CALL(Net::NetResolverStartNtoa(rid, hostname, addr, timeout, retry, flags));
@@ -194,6 +198,7 @@ LIB_DEFINE(InitNet_1_Net) {
LIB_FUNC("dgJBaeJnGpo", LibNet::NetPoolCreate);
LIB_FUNC("K7RlrTkI-mw", LibNet::NetPoolDestroy);
LIB_FUNC("C4UgDHHPvdw", LibNet::NetResolverCreate);
LIB_FUNC("kJlYH5uMAWI", LibNet::NetResolverDestroy);
LIB_FUNC("Nd91WaWmG2w", LibNet::NetResolverStartNtoa);
LIB_FUNC("8Kcp5d-q1Uo", LibNet::NetInetPton);
LIB_FUNC("9vA2aW+CHuA", LibNet::NetInetNtop);
+25
View File
@@ -101,6 +101,7 @@ public:
int PoolCreate(const char* name, int size);
bool PoolDestroy(int memid);
int ResolverCreate(const char* name, int memid);
bool ResolverDestroy(int rid);
bool ResolverValid(int rid);
Id SslInit(uint64_t pool_size);
@@ -291,6 +292,17 @@ int Network::ResolverCreate(const char* name, int memid) {
return -1;
}
bool Network::ResolverDestroy(int rid) {
Common::LockGuard lock(m_mutex);
if (rid >= 0 && rid < RESOLVERS_MAX && m_resolvers[rid].used) {
m_resolvers[rid] = {};
return true;
}
return false;
}
bool Network::ResolverValid(int rid) {
Common::LockGuard lock(m_mutex);
@@ -1231,6 +1243,19 @@ int KYTY_SYSV_ABI NetResolverCreate(const char* name, int memid, int flags) {
return id;
}
int KYTY_SYSV_ABI NetResolverDestroy(int rid) {
PRINT_NAME();
LOGF("\t rid = %d\n", rid);
EXIT_IF(g_net == nullptr);
if (!g_net->ResolverDestroy(rid)) {
return NET_ERROR_EBADF;
}
return OK;
}
int KYTY_SYSV_ABI NetResolverStartNtoa(int rid, const char* hostname, void* addr, int timeout,
int retry, int flags) {
PRINT_NAME();
+1
View File
@@ -40,6 +40,7 @@ int KYTY_SYSV_ABI NetTerm();
int KYTY_SYSV_ABI NetPoolCreate(const char* name, int size, int flags);
int KYTY_SYSV_ABI NetPoolDestroy(int memid);
int KYTY_SYSV_ABI NetResolverCreate(const char* name, int memid, int flags);
int KYTY_SYSV_ABI NetResolverDestroy(int rid);
int KYTY_SYSV_ABI NetResolverStartNtoa(int rid, const char* hostname, void* addr, int timeout,
int retry, int flags);
int KYTY_SYSV_ABI NetInetPton(int af, const char* src, void* dst);
+4 -1
View File
@@ -801,7 +801,10 @@ static bool KytyExceptionHandler(const Common::HostException::ExceptionInfo& exc
return true;
}
}
return false;
EXIT("Unhandled host exception: type=%u code=%u pc=0x%016" PRIx64
" access=%u address=0x%016" PRIx64 "\n",
static_cast<unsigned>(info->type), info->native_code, info->exception_address,
static_cast<unsigned>(info->access_violation_type), info->access_violation_vaddr);
}
static void EncodeId64(uint16_t in_id, std::string* out_id) {
+273 -42
View File
@@ -7283,26 +7283,30 @@ public:
std::printf("[gpu] %-32s ok\n", name);
}
void CheckRenderExecutorColorStandard4TileDiscovery() {
constexpr const char *name = "RenderExecutorColorStandard4Tile";
void CheckRenderExecutorColorStandardTileDiscovery() {
constexpr const char *name = "RenderExecutorColorStandardTile";
constexpr uintptr_t base = 0x0000000203b00000ull;
constexpr uint64_t target_address = base + 0x4000;
constexpr uint64_t target_size = 0x180000;
constexpr uint64_t allocation_size = 0x184000;
constexpr uint64_t allocation_alignment = 0x4000;
constexpr uint64_t allocation_size = 0x190000;
constexpr uint64_t allocation_alignment = 0x10000;
constexpr auto format = Prospero::BufferFormat::k32Float;
constexpr auto tile = Prospero::TileMode::kStandard4KB;
struct TileCase {
Prospero::TileMode tile;
uint64_t target_offset;
uint64_t alignment;
};
constexpr std::array cases{
TileCase{Prospero::TileMode::kStandard4KB, 0x4000, 0x1000},
TileCase{Prospero::TileMode::kStandard64KB, 0x10000, 0x10000},
};
EnsureRuntimeContext();
const auto pitch = TileGetTexturePitch(format, 256, tile);
TileSizeAlign slice{};
TileGetTextureSize(format, 256, 256, 1, tile, &slice, nullptr, nullptr);
const auto odd_pitch = TileGetTexturePitch(format, 257, tile);
const auto odd_pitch =
TileGetTexturePitch(format, 257, Prospero::TileMode::kStandard4KB);
TileSizeAlign odd_slice{};
TileGetTextureSize(format, 257, 131, 1, tile, &odd_slice, nullptr, nullptr);
TileGetTextureSize(format, 257, 131, 1, Prospero::TileMode::kStandard4KB,
&odd_slice, nullptr, nullptr);
Require(name, "tile layout",
pitch == 256 && slice.size == 0x40000 && slice.align == 0x1000 &&
odd_pitch == 288 && odd_slice.size == 0x2d000 &&
odd_pitch == 288 && odd_slice.size == 0x2d000 &&
odd_slice.align == 0x1000,
"Standard4KB pitch, footprint, or block alignment is incorrect");
@@ -7311,17 +7315,29 @@ public:
Libs::LibKernel::Memory::KernelAllocateDirectMemory(
0, Libs::LibKernel::Memory::KernelGetDirectMemorySize(),
allocation_size, allocation_alignment, 0, &direct_offset) == 0,
"Standard4KB color allocation failed");
"standard-tiled color allocation failed");
void *mapped = reinterpret_cast<void *>(base);
Require(name, "direct mapping",
Libs::LibKernel::Memory::KernelMapDirectMemory(
&mapped, allocation_size, 0x3, 0x10, direct_offset,
allocation_alignment) == 0 &&
mapped == reinterpret_cast<void *>(base),
"Standard4KB color mapping failed");
"standard-tiled color mapping failed");
std::memset(mapped, 0, allocation_size);
{
for (const auto &tile_case : cases) {
const auto pitch = TileGetTexturePitch(format, 256, tile_case.tile);
TileSizeAlign slice{};
TileGetTextureSize(format, 256, 256, 1, tile_case.tile, &slice, nullptr,
nullptr);
Require(
name, "tile layout",
pitch == 256 && slice.size == 0x40000 &&
slice.align == tile_case.alignment,
"standard tile pitch, footprint, or block alignment is incorrect");
const auto target_address = base + tile_case.target_offset;
const auto target_size = slice.size * 6ull;
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
@@ -7335,7 +7351,7 @@ public:
registers.SetColorView(
0, {.base_array_slice_index = 5, .last_array_slice_index = 5});
registers.SetColorAttrib2(0, {.height = 255, .width = 255});
registers.SetColorAttrib3(0, {.tile_mode = tile,
registers.SetColorAttrib3(0, {.tile_mode = tile_case.tile,
.dimension = 1,
.cmask_pipe_aligned = true,
.dcc_pipe_aligned = true});
@@ -7358,7 +7374,7 @@ public:
color.base_array_layer == 5 &&
color.desc.info.data.address == target_address &&
color.desc.info.data.size == target_size &&
color.desc.info.tile_mode == tile &&
color.desc.info.tile_mode == tile_case.tile &&
color.desc.info.extent == vk::Extent3D{256, 256, 1} &&
color.desc.info.resources == ImageSubresources{1, 6} &&
color.desc.info.pitch == pitch &&
@@ -7367,9 +7383,9 @@ public:
color.desc.view_info.type == vk::ImageViewType::e2D &&
color.desc.view_info.base_layer == 5 &&
color.desc.view_info.layer_count == 1 &&
(target_address & 0xffffu) == 0x4000 &&
(target_address & (tile_case.alignment - 1)) == 0 &&
image.usage.render_target && image.IsGpuModified(),
"slice 5 Standard4KB render target was not resolved");
"slice 5 standard-tiled render target was not resolved");
RenderExecutorTestAccess::ResetBindings(executor);
resources.UnmapMemory(base, allocation_size);
@@ -7378,11 +7394,11 @@ public:
Require(name, "unmap direct backing",
Libs::LibKernel::Memory::KernelMunmap(base, allocation_size) == 0,
"Standard4KB color mapping release failed");
"standard-tiled color mapping release failed");
Require(name, "release direct backing",
Libs::LibKernel::Memory::KernelReleaseDirectMemory(
direct_offset, allocation_size) == 0,
"Standard4KB color allocation release failed");
"standard-tiled color allocation release failed");
std::printf("[gpu] %-32s ok\n", name);
}
@@ -12018,6 +12034,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
case Opcode::DS_READ_I16:
case Opcode::DS_READ_U16:
case Opcode::DS_READ_U16_D16:
case Opcode::DS_READ_U16_D16_HI:
case Opcode::DS_READ2_B32:
case Opcode::DS_READ_B32:
case Opcode::DS_READ_B64:
@@ -12026,6 +12043,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
case Opcode::DS_READ_B128:
case Opcode::DS_WRITE_B8:
case Opcode::DS_WRITE_B16:
case Opcode::DS_WRITE_B16_D16_HI:
case Opcode::DS_WRITE2_B32:
case Opcode::DS_WRITE2ST64_B32:
case Opcode::DS_WRITE_B32:
@@ -13355,6 +13373,83 @@ TestCase VectorIntegerOps() {
O::S_ENDPGM}};
}
TestCase VectorFfbhI32NativeAndVop3OnGpu() {
using O = ShaderOpcode;
constexpr std::array inputs = {0x00000000u, 0x40000000u, 0x80000000u,
0x0fffffffu, 0xffff0000u, 0xfffffffeu,
0xffffffffu, 0x80000000u};
std::vector<u32> code;
for (u32 i = 0; i < inputs.size() - 1u; i++) {
AppendVMovU32(&code, 30, i * 4u);
AppendBufferLoadDword(&code, 14, 30);
code.push_back(0x7e1c770eu); // v_ffbh_i32 v14, v14
AppendStoreVgpr(&code, 14, i);
}
AppendVMovU32(&code, 30, (inputs.size() - 1u) * 4u);
AppendBufferLoadDword(&code, 14, 30);
AppendVop3(&code, 0x1bbu, 15, Vgpr(14), 0);
AppendStoreVgpr(&code, 15, inputs.size() - 1u);
AppendEnd(&code);
TestCase test;
test.name = "VectorFfbhI32NativeAndVop3OnGpu";
test.code = std::move(code);
test.initial.assign(inputs.begin(), inputs.end());
test.expected = {0xffffffffu, 1u, 1u, 4u, 16u, 31u, 0xffffffffu, 1u};
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_FFBH_I32,
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
test.decoded_counts = {{"V_FFBH_I32 v14, v14", inputs.size() - 1u},
{"V_FFBH_I32 v15, v14", 1u}};
return test;
}
TestCase Vop1SdwaFfblCapturedHighWordSource() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendBufferLoadDword(&code, 2, 30);
code.push_back(0x7e2074f9u);
code.push_back(0x00050602u); // v_ffbl_b32 v16, v2.word1
AppendStoreVgpr(&code, 16, 0);
AppendEnd(&code);
TestCase test;
test.name = "Vop1SdwaFfblCapturedHighWordSource";
test.code = std::move(code);
test.initial = {0x00400000u};
test.expected = {6u};
test.opcodes = {O::BUFFER_LOAD_DWORD, O::V_FFBL_B32, O::BUFFER_STORE_DWORD,
O::S_ENDPGM};
test.decoded_counts = {{"V_FFBL_B32 v16, v2.sdwa(sel=5,sext=0)", 1}};
test.ir_counts = {{" = BitFieldUExtract ", 1}, {" = FindILsb32 ", 1}};
test.required_spirv = {"OpBitFieldUExtract"};
return test;
}
TestCase Vop1SdwaNotCapturedByte0Source() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendBufferLoadDword(&code, 2, 30);
code.push_back(0x7e046ef9u);
code.push_back(0x00000602u); // v_not_b32 v2, v2.byte0
AppendStoreVgpr(&code, 2, 0);
AppendEnd(&code);
TestCase test;
test.name = "Vop1SdwaNotCapturedByte0Source";
test.code = std::move(code);
test.initial = {0x12345678u};
test.expected = {0xffffff87u};
test.opcodes = {O::BUFFER_LOAD_DWORD, O::V_NOT_B32, O::BUFFER_STORE_DWORD,
O::S_ENDPGM};
test.decoded_counts = {{"V_NOT_B32 v2, v2.sdwa(sel=0,sext=0)", 1}};
test.ir_counts = {{" = BitFieldUExtract ", 1}, {" = BitwiseNot32 ", 1}};
test.required_spirv = {"OpBitFieldUExtract", "OpNot"};
return test;
}
TestCase Vop2SdwaSubNcExactByte2Destination() {
using O = ShaderOpcode;
@@ -13862,37 +13957,35 @@ TestCase VectorAddcUsesPerLaneCarryIn() {
TestCase VectorAddcWritesPerLaneCarryOut() {
using O = ShaderOpcode;
std::vector<u32> code = {
EncodeVop1(0x01, 1, 0),
EncodeVop2(0x1a, 1, InlineU32(2), 1),
EncodeVop2(0x25, 1, Vgpr(0), 1),
EncodeVopc(0xc0, Vgpr(0), 0),
};
AppendVMovLiteral(&code, 2, 0xffffffffu);
code.push_back(EncodeVop1(0x01, 3, InlineU32(1)));
code.push_back(EncodeVop2(0x28, 5, Vgpr(2), 3));
std::vector<u32> code = {EncodeVop1(0x01, 1, Vgpr(0)),
EncodeVop2(0x1a, 4, InlineU32(2), 1),
EncodeVopc(0xc7, Vgpr(0), 0)};
AppendBufferLoadDword(&code, 26, 4);
code.push_back(0x500434f9u);
code.push_back(0x0c860680u);
code.push_back(EncodeVop1(0x01, 6, 106));
code.push_back(EncodeVop2(0x1a, 4, InlineU32(2), 1));
AppendBufferStoreDword(&code, 6, 4);
AppendEnd(&code);
TestCase test;
test.name = "VectorAddcWritesPerLaneCarryOut";
test.code = code;
test.initial = {0x0000ffffu, 0u, 0x0000ffffu, 0u};
// VCC is represented as the current invocation's carry bit, not a shared
// four-lane ballot word.
test.expected = std::vector<u32>(8, 1u);
test.opcodes = {O::V_MOV_B32, O::V_LSHLREV_B32, O::V_ADD_NC_U32,
O::V_CMP_F_U32, O::V_ADDC_U32, O::BUFFER_STORE_DWORD,
test.expected = {1u, 0u, 1u, 0u};
test.opcodes = {O::V_MOV_B32, O::V_LSHLREV_B32, O::V_CMP_T_U32,
O::BUFFER_LOAD_DWORD, O::V_ADDC_U32, O::BUFFER_STORE_DWORD,
O::S_ENDPGM};
test.decoded_counts = {
{"V_ADDC_U32 v2, vcc_lo, 0, v26.sdwa(sel=4,sext=1), vcc_lo", 1}};
test.ir_counts = {{" = BitFieldSExtract ", 1}, {" = IAddCarry32 ", 2}};
test.required_spirv = {"OpBitFieldSExtract"};
test.compute_info.threads_num[0] = 4;
test.compute_info.threads_num[1] = 1;
test.compute_info.threads_num[2] = 1;
test.compute_info.group_id[0] = true;
test.compute_info.thread_ids_num = 1;
test.compute_info.workgroup_register = 0;
test.has_compute_info = true;
test.dispatch_x = 2;
return test;
}
@@ -15367,6 +15460,36 @@ TestCase VectorCompareOps() {
O::V_CMP_T_U32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase VectorVop3CompareEqU64OnGpu() {
using O = ShaderOpcode;
std::vector<u32> code;
code.push_back(EncodeVop1(0x01, 1, InlineU32(1)));
code.push_back(EncodeSop1(0x04, 106, 126)); // s_mov_b64 vcc, exec
code.push_back(0xd4e2006au);
code.push_back(0x0000d47eu); // v_cmp_eq_u64 vcc, exec, vcc
code.push_back(EncodeVop2(0x01, 2, InlineU32(0), 1));
AppendStoreVgpr(&code, 2, 0);
AppendSMovLiteral(&code, 107, 1); // high-dword mismatch
code.push_back(0xd4e2006au);
code.push_back(0x0000d47eu); // v_cmp_eq_u64 vcc, exec, vcc
code.push_back(EncodeVop2(0x01, 3, InlineU32(0), 1));
AppendStoreVgpr(&code, 3, 1);
AppendEnd(&code);
TestCase test{"VectorVop3CompareEqU64OnGpu",
code,
{},
{1, 0},
{O::V_MOV_B32, O::S_MOV_B64, O::V_CMP_EQ_U64,
O::V_CNDMASK_B32, O::S_MOV_B32, O::BUFFER_STORE_DWORD,
O::S_ENDPGM}};
test.decoded_counts = {{"V_CMP_EQ_U64 vcc_lo, exec_lo, vcc_lo", 2}};
return test;
}
TestCase VectorVop3CompareNeU64OnGpu() {
using O = ShaderOpcode;
@@ -17992,6 +18115,64 @@ TestCase DsReadU16D16CapturedPreservesHighHalf() {
return test;
}
TestCase DsWriteB16D16HiCapturedUsesHighHalf() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendVMovU32(&code, 1, 0);
AppendVMovLiteral(&code, 3, 0xdeadbeefu);
code.push_back(EncodeDs0(0x0d));
code.push_back(EncodeDs1(0, 3, 1)); // ds_write_b32 v1, v3
AppendVMovU32(&code, 20, 2);
AppendVMovLiteral(&code, 2, 0x1234abcdu);
AppendVMovLiteral(&code, 0, 0x87654321u);
code.push_back(0xda840000u);
code.push_back(0x00000214u); // captured ds_write_b16_d16_hi v20, v2
code.push_back(EncodeDs0(0x36));
code.push_back(EncodeDs1(4, 0, 1)); // ds_read_b32 v4, v1
AppendStoreVgpr(&code, 4, 0);
AppendStoreVgpr(&code, 0, 1);
AppendEnd(&code);
TestCase test{"DsWriteB16D16HiCapturedUsesHighHalf",
code,
std::vector<u32>(2, 0),
{0x1234beefu, 0x87654321u},
{O::V_MOV_B32, O::DS_WRITE_B32, O::DS_WRITE_B16_D16_HI,
O::DS_READ_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
test.decoded_counts = {{"DS_WRITE_B16_D16_HI", 1}};
return test;
}
TestCase DsReadU16D16HiCapturedPreservesLowHalf() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendVMovU32(&code, 1, 0);
AppendVMovLiteral(&code, 2, 0x89abcdefu);
code.push_back(EncodeDs0(0x0d));
code.push_back(EncodeDs1(0, 2, 1)); // ds_write_b32 v2, v1
AppendVMovLiteral(&code, 5, 0x12345678u);
AppendVMovU32(&code, 6, 0);
code.push_back(0xda9c0000u);
code.push_back(0x05000006u); // captured ds_read_u16_d16_hi v5, v6
AppendStoreVgpr(&code, 5, 0);
AppendEnd(&code);
TestCase test{"DsReadU16D16HiCapturedPreservesLowHalf",
code,
std::vector<u32>(1, 0),
{0xcdef5678u},
{O::V_MOV_B32, O::DS_WRITE_B32, O::DS_READ_U16_D16_HI,
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
test.decoded_counts = {{"DS_READ_U16_D16_HI", 1}};
return test;
}
TestCase DsReadWrite2Variants() {
using O = ShaderOpcode;
@@ -18936,6 +19117,44 @@ TestCase ImageLoad1DUsesScalarCoordinate() {
return test;
}
TestCase ImageGather2DInstructionWith1DDescriptor() {
using O = ShaderOpcode;
std::vector<u32> code;
AppendVMovLiteral(&code, 20, 0x3f000000u);
AppendVMovLiteral(&code, 21, 0x3f000000u);
code.push_back(EncodeMimg0(0x47, 0x1));
code.push_back(EncodeMimg1(0, 20));
for (u32 i = 0; i < 4u; i++) {
AppendStoreVgpr(&code, i, i);
}
AppendEnd(&code);
auto image = MakeRgbaImage(4, 1);
SetRgbaPixel(&image, 4, 1, 0, 0x40000000u, 0, 0, 0);
SetRgbaPixel(&image, 4, 2, 0, 0x40400000u, 0, 0, 0);
TestCase test;
test.name = "ImageGather2DInstructionWith1DDescriptor";
test.code = code;
test.expected = {0x40000000u, 0x40400000u, 0x40400000u, 0x40000000u};
test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4_LZ, O::BUFFER_STORE_DWORD,
O::S_ENDPGM};
test.image_width = 4;
test.image_height = 1;
test.sampled_image_rgba = image;
test.sampled_image_type = vk::ImageType::e1D;
test.sampled_image_view_type = vk::ImageViewType::e1D;
test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32Float);
test.user_data[3] = static_cast<uint32_t>(Prospero::ImageType::kColor1D)
<< 28u;
test.has_user_data = true;
test.required_spirv = {"sampled_1d", "OpImageQuerySizeLod",
"OpImageSampleExplicitLod", "Floor"};
test.forbidden_spirv = {"OpImageGather"};
return test;
}
TestCase ImageLoad1DArrayUsesLayerCoordinate() {
using O = ShaderOpcode;
@@ -20350,6 +20569,9 @@ std::vector<TestCase> MakeCases() {
AddCase(VectorMoves);
AddCase(VectorVop3MoveAppliesFloatSourceModifiers);
AddCase(VectorIntegerOps);
AddCase(VectorFfbhI32NativeAndVop3OnGpu);
AddCase(Vop1SdwaFfblCapturedHighWordSource);
AddCase(Vop1SdwaNotCapturedByte0Source);
AddCase(Vop2SdwaSubNcExactByte2Destination);
AddCase(Vop2SdwaAddNcCapturedHighWordDestination);
AddCase(Vop2SdwaAshrrevCapturedWord0SignExtends);
@@ -20411,6 +20633,7 @@ std::vector<TestCase> MakeCases() {
AddCase(VectorSinCosMaxFiniteSpecialCases);
AddCase(VectorCompareOps);
AddCase(VectorVop3CompareEqI64OnGpu);
AddCase(VectorVop3CompareEqU64OnGpu);
AddCase(VectorVop3CompareGtU64OnGpu);
AddCase(VectorVop3CompareNeU64OnGpu);
AddCase(VectorVopcCmpxNeU64CapturedExecMask);
@@ -20507,7 +20730,9 @@ std::vector<TestCase> MakeCases() {
AddCase(ScratchIsPrivatePerInvocation);
AddCase(FlatStoreVariants);
AddCase(DsReadWriteVariants);
AddCase(DsWriteB16D16HiCapturedUsesHighHalf);
AddCase(DsReadU16D16CapturedPreservesHighHalf);
AddCase(DsReadU16D16HiCapturedPreservesLowHalf);
AddCase(DsAppendConsumeUsesEncodedLdsSelector);
AddCase(DsAppendUsesEncodedGdsSelector);
AddCase(DsGdsSubdwordAndAtomicWrites);
@@ -20535,6 +20760,7 @@ std::vector<TestCase> MakeCases() {
AddCase(ImageSamplePackedUintConvertsSampleAndGather);
AddCase(ImageLoadR128IgnoresAdjacentMaskSgprs);
AddCase(ImageLoad1DUsesScalarCoordinate);
AddCase(ImageGather2DInstructionWith1DDescriptor);
AddCase(ImageLoad1DArrayUsesLayerCoordinate);
AddCase(ImageLoad1DArrayDescriptorUsesSelectedLayer);
AddCase(ImageLoadMipUsesVaddr2Lod2D);
@@ -24290,6 +24516,11 @@ int main(int argc, char **argv) {
RunCase(&vulkan, Vop2SdwaAshrrevCapturedWord0SignExtends());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--sdwa-addc-only") == 0) {
VulkanHarness vulkan;
RunCase(&vulkan, VectorAddcWritesPerLaneCarryOut());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--cvt-pk-i16-only") == 0) {
VulkanHarness vulkan;
RunCase(&vulkan, Vop3CvtPkI16I32Captured());
@@ -24391,9 +24622,9 @@ int main(int argc, char **argv) {
CheckStandard64RenderTargetTileRoundTrip();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--standard4-rt-only") == 0) {
if (argc == 2 && std::strcmp(argv[1], "--standard-tile-rt-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckRenderExecutorColorStandard4TileDiscovery();
vulkan.CheckRenderExecutorColorStandardTileDiscovery();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--image-view-only") == 0) {
@@ -24514,7 +24745,7 @@ int main(int argc, char **argv) {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
vulkan.CheckRenderExecutorColor1DDiscovery();
vulkan.CheckRenderExecutorColorVolumeDiscovery();
vulkan.CheckRenderExecutorColorStandard4TileDiscovery();
vulkan.CheckRenderExecutorColorStandardTileDiscovery();
vulkan.CheckRenderExecutorColorDepthTileDiscovery();
vulkan.CheckRenderExecutorStencilBindingDiscovery();
vulkan.CheckUnifiedTextureCacheFlow();
+48 -26
View File
@@ -4007,6 +4007,30 @@ void TestNewShaderDecoderArchitecture() {
program_direct.word_count,
"program decoder diverged from the single-instruction decoder");
const uint32_t cmp_eq_u64_code[] = {0xd4e2006au, 0x0000d47eu};
Instruction cmp_eq_u64;
Check(DecodeInstruction(cmp_eq_u64_code, 0u, cmp_eq_u64, &error),
error.c_str());
Check(cmp_eq_u64.family == Family::VOP3 &&
cmp_eq_u64.opcode == Opcode::V_CMP_EQ_U64 &&
cmp_eq_u64.dst.kind == OperandKind::VccLo &&
cmp_eq_u64.src_count == 2u &&
cmp_eq_u64.src0.kind == OperandKind::ExecLo &&
cmp_eq_u64.src1.kind == OperandKind::VccLo,
"decoder rejected the captured VOP3 V_CMP_EQ_U64 instruction");
const uint32_t ffbh_i32_code[] = {0x7e1c770eu};
Instruction ffbh_i32;
Check(DecodeInstruction(ffbh_i32_code, 0u, ffbh_i32, &error),
error.c_str());
Check(ffbh_i32.family == Family::VOP1 &&
ffbh_i32.opcode == Opcode::V_FFBH_I32 &&
ffbh_i32.dst.kind == OperandKind::Vgpr && ffbh_i32.dst.reg == 14u &&
ffbh_i32.src_count == 1u &&
ffbh_i32.src0.kind == OperandKind::Vgpr &&
ffbh_i32.src0.reg == 14u,
"decoder rejected the captured VOP1 V_FFBH_I32 instruction");
const uint32_t literal_code[] = {EncodeVop1(0x01, 2, 255u), 0x12345678u};
Instruction literal;
Check(DecodeInstruction(literal_code, 0u, literal, &error), error.c_str());
@@ -4099,6 +4123,28 @@ void TestNewShaderDecoderArchitecture() {
Check(boot.opcode == Opcode::DS_SWIZZLE_B32 && boot.offset == 0xc480u,
"DS decoder rejected a captured boot-shader instruction");
const uint32_t d16_hi_ds[] = {0xda9c0000u, 0x05000006u};
Instruction d16_hi;
Check(DecodeInstruction(d16_hi_ds, 0u, d16_hi, &error), error.c_str());
Check(d16_hi.opcode == Opcode::DS_READ_U16_D16_HI &&
d16_hi.src_count == 1u && d16_hi.data_bits == 16u &&
d16_hi.dst.reg == 5u && d16_hi.dst.sdwa_sel == 5u &&
d16_hi.dst.sdwa_dst_unused == 2u && d16_hi.src0.reg == 6u,
"DS decoder rejected the captured high-half D16 read");
const uint32_t d16_hi_write_ds[] = {0xda840000u, 0x00000214u};
Instruction d16_hi_write;
Check(DecodeInstruction(d16_hi_write_ds, 0u, d16_hi_write, &error),
error.c_str());
Check(d16_hi_write.family == Family::DS &&
d16_hi_write.opcode == Opcode::DS_WRITE_B16_D16_HI &&
d16_hi_write.word_count == 2u && d16_hi_write.src_count == 2u &&
d16_hi_write.data_dwords == 1u && d16_hi_write.data_bits == 16u &&
d16_hi_write.offset == 0u && !d16_hi_write.gds &&
d16_hi_write.src0.reg == 20u && d16_hi_write.src1.reg == 2u &&
d16_hi_write.src1.sdwa_sel == 5u,
"DS decoder rejected the captured high-half D16 write");
constexpr uint32_t packed_source_selectors[][2] = {
{0xcc0e0000u, 0x0c0a0300u}, // Source 0: instruction bit 59.
{0xcc0e0000u, 0x140a0300u}, // Source 1: instruction bit 60.
@@ -4117,8 +4163,8 @@ void TestNewShaderDecoderArchitecture() {
void TestNewShaderRecompilerRejectsDppOn64BitCompares() {
const uint32_t opcodes[] = {
0xa2u, 0xb5u, 0xe4u, 0xe5u,
0xf5u}; // eq_i64, cmpx_ne_i64, gt_u64, ne_u64, cmpx_ne_u64
0xa2u, 0xb5u, 0xe2u, 0xe4u, 0xe5u,
0xf5u}; // eq_i64, cmpx_ne_i64, eq_u64, gt_u64, ne_u64, cmpx_ne_u64
for (const auto opcode : opcodes) {
const uint32_t shader[] = {
EncodeVopc(opcode, 250u, 0u), // DPP escape in SRC0
@@ -5088,30 +5134,6 @@ void TestNewShaderRecompilerNullImageUsesCanonical2DView() {
"null image retained the decoded 1D descriptor binding");
}
void TestNewShaderRecompilerRejectsOneDimensionalGather() {
constexpr std::array cases{
std::pair{0u, Prospero::ImageType::kColor1D},
std::pair{4u, Prospero::ImageType::kColor1DArray},
};
for (const auto &[dimension, type] : cases) {
const uint32_t shader[] = {
EncodeMimg0(0x47, 0x1, false, dimension),
EncodeMimg1(0, 0, 1, 0),
0xbf810000u,
};
auto user_data = ImageTestUserData(type);
auto options = MakeCompileOptions(ShaderType::Compute);
options.user_data = user_data.data();
ShaderRecompiler::CompileResult result;
std::string error;
Check(!ShaderRecompiler::TryRecompile(shader, options, result, &error) &&
Common::ContainsStr(error,
"1D image gather is not supported by SPIR-V"),
"1D image gather escaped as invalid SPIR-V");
}
}
void TestNewShaderRecompilerImageGatherVariants() {
const uint32_t shader[] = {
EncodeMimg0(0x47, 0x1),