mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b2963aa6e0 | |||
| 9859b3e1d6 | |||
| cd19fa283f | |||
| b6af7c73ec | |||
| ac64d202af | |||
| df44ad3fac | |||
| 0a2f4814ac | |||
| e6b4707a5d | |||
| 9a3468e3a0 | |||
| 73da43a7be | |||
| aa88d94755 | |||
| 51562cd8d2 | |||
| 822ca3c10b | |||
| 2b6fd699e7 |
@@ -105,12 +105,12 @@ jobs:
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
cmake --build _Build/windows --target launcher audio_out2_port_tests virtual_memory_allocation_tests --parallel
|
||||
cmake --build _Build/windows --target launcher audio_out2_port_tests ime_dialog_tests virtual_memory_allocation_tests --parallel
|
||||
|
||||
- name: Test
|
||||
shell: cmd
|
||||
run: |
|
||||
ctest --test-dir _Build/windows --output-on-failure -R "^(audio_out2_port|virtual_memory_allocation)$"
|
||||
ctest --test-dir _Build/windows --output-on-failure -R "^(audio_out2_port|ime_dialog|virtual_memory_allocation)$"
|
||||
|
||||
- name: Install
|
||||
shell: cmd
|
||||
@@ -186,14 +186,14 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --build _Build/macos \
|
||||
--target launcher audio_out2_port_tests virtual_memory_allocation_tests \
|
||||
--target launcher audio_out2_port_tests ime_dialog_tests virtual_memory_allocation_tests \
|
||||
--parallel
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
ctest --test-dir _Build/macos --output-on-failure \
|
||||
-R '^(audio_out2_port|virtual_memory_allocation)$'
|
||||
-R '^(audio_out2_port|ime_dialog|virtual_memory_allocation)$'
|
||||
|
||||
- name: Install
|
||||
shell: bash
|
||||
@@ -329,14 +329,14 @@ jobs:
|
||||
run: |
|
||||
cmake --build _Build/linux \
|
||||
--target launcher page_manager_tests memory_tracker_tests \
|
||||
audio_out2_port_tests virtual_memory_allocation_tests \
|
||||
audio_out2_port_tests ime_dialog_tests virtual_memory_allocation_tests \
|
||||
--parallel
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
ctest --test-dir _Build/linux --output-on-failure \
|
||||
-R '^(audio_out2_port|page_manager|memory_tracker|virtual_memory_allocation)$'
|
||||
-R '^(audio_out2_port|ime_dialog|page_manager|memory_tracker|virtual_memory_allocation)$'
|
||||
|
||||
- name: Install
|
||||
shell: bash
|
||||
|
||||
@@ -39,3 +39,6 @@
|
||||
[submodule "3rdparty/nlohmann_json"]
|
||||
path = 3rdparty/nlohmann_json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "3rdparty/imgui"]
|
||||
path = 3rdparty/imgui
|
||||
url = https://github.com/ocornut/imgui.git
|
||||
|
||||
Vendored
+19
@@ -11,6 +11,25 @@ target_include_directories(Vulkan-Headers INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Vulkan-Headers/include"
|
||||
)
|
||||
|
||||
add_library(imgui STATIC
|
||||
imgui/imgui.cpp
|
||||
imgui/imgui_draw.cpp
|
||||
imgui/imgui_tables.cpp
|
||||
imgui/imgui_widgets.cpp
|
||||
imgui/backends/imgui_impl_vulkan.cpp
|
||||
)
|
||||
target_include_directories(imgui PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/imgui"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/imgui/backends"
|
||||
)
|
||||
target_compile_definitions(imgui PRIVATE IMGUI_IMPL_VULKAN_NO_PROTOTYPES)
|
||||
target_link_libraries(imgui PUBLIC Vulkan::Headers)
|
||||
if(MSVC)
|
||||
target_compile_options(imgui PRIVATE /W0)
|
||||
else()
|
||||
target_compile_options(imgui PRIVATE -w)
|
||||
endif()
|
||||
|
||||
set(SPIRV_SKIP_TESTS ON CACHE BOOL "" FORCE)
|
||||
set(SPIRV_SKIP_EXECUTABLES ON CACHE BOOL "" FORCE)
|
||||
set(SKIP_SPIRV_TOOLS_INSTALL ON CACHE BOOL "" FORCE)
|
||||
|
||||
+1
Submodule 3rdparty/imgui added at f1cc2ae15e
+9
-1
@@ -292,7 +292,7 @@ if (KYTY_CLANG_CL)
|
||||
set_source_files_properties(${kyty_emulator_src} PROPERTIES COMPILE_FLAGS "-Wno-pragma-pack -Wno-deprecated-declarations -D_TIMESPEC_DEFINED")
|
||||
endif()
|
||||
|
||||
set(kyty_emulator_link_libraries common Vulkan::Headers spirv-tools-opt spirv-tools SDL2-static xxhash FFmpeg::ffmpeg fmt::fmt nlohmann_json::nlohmann_json LibAtrac9 kyty_git_version)
|
||||
set(kyty_emulator_link_libraries common Vulkan::Headers spirv-tools-opt spirv-tools SDL2-static imgui xxhash FFmpeg::ffmpeg fmt::fmt nlohmann_json::nlohmann_json LibAtrac9 kyty_git_version)
|
||||
|
||||
# Linux system libraries required by the static FFmpeg archive.
|
||||
if(LINUX)
|
||||
@@ -385,6 +385,12 @@ add_executable(bit_array_tests EXCLUDE_FROM_ALL
|
||||
)
|
||||
target_include_directories(bit_array_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(ime_dialog_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/ImeDialogTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/libs/imeDialog.cpp"
|
||||
)
|
||||
target_include_directories(ime_dialog_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(memory_tracker_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_TESTS_DIR}/MemoryTrackerTests.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/host_gpu/pageManager.cpp"
|
||||
@@ -499,6 +505,7 @@ if(NOT KYTY_CLANG_CL)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test(NAME ime_dialog COMMAND $<TARGET_FILE:ime_dialog_tests>)
|
||||
add_test(NAME shader_cfg COMMAND $<TARGET_FILE:shader_cfg_tests>)
|
||||
add_test(NAME scalar_provenance COMMAND $<TARGET_FILE:scalar_provenance_tests>)
|
||||
add_test(NAME image_page_table COMMAND $<TARGET_FILE:image_page_table_tests>)
|
||||
@@ -548,6 +555,7 @@ if(BUILD_TESTING)
|
||||
endif()
|
||||
|
||||
add_custom_target(kyty_tests DEPENDS
|
||||
ime_dialog_tests
|
||||
shader_cfg_tests
|
||||
scalar_provenance_tests
|
||||
image_page_table_tests
|
||||
|
||||
@@ -313,7 +313,7 @@ void CommandProcessor::Reset() {
|
||||
m_ucfg.Reset();
|
||||
m_ctx.Reset();
|
||||
m_saved_ctx.Reset();
|
||||
m_context_state_pushed = false;
|
||||
m_context_state_pushed = false;
|
||||
m_index_type_and_size = 0;
|
||||
m_index_buffer_size = 0;
|
||||
m_user_data_marker = HW::UserSgprType::Unknown;
|
||||
@@ -1176,9 +1176,9 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
||||
void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_group_y,
|
||||
uint32_t thread_group_z, uint32_t mode) {
|
||||
uint32_t frame_num = 0;
|
||||
uint32_t local_x = 1;
|
||||
uint32_t local_y = 1;
|
||||
uint32_t local_z = 1;
|
||||
// uint32_t local_x = 1;
|
||||
// uint32_t local_y = 1;
|
||||
// uint32_t local_z = 1;
|
||||
|
||||
{
|
||||
CheckBuffer();
|
||||
@@ -1202,9 +1202,9 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
|
||||
}
|
||||
|
||||
const auto& cs = m_sh_ctx.GetCs().cs_regs;
|
||||
local_x = std::max(cs.num_thread_x, 1u);
|
||||
local_y = std::max(cs.num_thread_y, 1u);
|
||||
local_z = std::max(cs.num_thread_z, 1u);
|
||||
// local_x = std::max(cs.num_thread_x, 1u);
|
||||
// local_y = std::max(cs.num_thread_y, 1u);
|
||||
// local_z = std::max(cs.num_thread_z, 1u);
|
||||
if (cs.wave_size == 64u) {
|
||||
static std::atomic_bool logged_wave64_shader {false};
|
||||
if (!logged_wave64_shader.exchange(true, std::memory_order_relaxed)) {
|
||||
@@ -1220,27 +1220,27 @@ void CommandProcessor::DispatchDirect(uint32_t thread_group_x, uint32_t thread_g
|
||||
thread_group_y, thread_group_z, mode);
|
||||
}
|
||||
|
||||
constexpr uint32_t DispatchInitiatorUseThreadDimensions = 1u << 5u;
|
||||
/*constexpr uint32_t DispatchInitiatorUseThreadDimensions = 1u << 5u;
|
||||
auto group_count = [](uint32_t threads, uint32_t group_size) {
|
||||
return (threads == 0
|
||||
? 0u
|
||||
: (threads + std::max(group_size, 1u) - 1u) / std::max(group_size, 1u));
|
||||
return (threads == 0
|
||||
? 0u
|
||||
: (threads + std::max(group_size, 1u) - 1u) / std::max(group_size, 1u));
|
||||
};
|
||||
|
||||
auto groups_x = thread_group_x;
|
||||
auto groups_y = thread_group_y;
|
||||
auto groups_z = thread_group_z;
|
||||
if ((mode & DispatchInitiatorUseThreadDimensions) != 0) {
|
||||
groups_x = group_count(thread_group_x, local_x);
|
||||
groups_y = group_count(thread_group_y, local_y);
|
||||
groups_z = group_count(thread_group_z, local_z);
|
||||
groups_x = group_count(thread_group_x, local_x);
|
||||
groups_y = group_count(thread_group_y, local_y);
|
||||
groups_z = group_count(thread_group_z, local_z);
|
||||
}
|
||||
|
||||
const uint64_t invocations =
|
||||
static_cast<uint64_t>(groups_x) * groups_y * groups_z * local_x * local_y * local_z;
|
||||
if (invocations != 0) {
|
||||
BufferFlushAndWait();
|
||||
}
|
||||
BufferFlushAndWait();
|
||||
}*/
|
||||
}
|
||||
|
||||
void CommandProcessor::DispatchIndirect(uint32_t data_offset, uint32_t mode) {
|
||||
|
||||
+18
-13
@@ -1196,7 +1196,6 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
}
|
||||
|
||||
ImageId result {};
|
||||
bool inserted_new = false;
|
||||
{
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
@@ -1243,19 +1242,12 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
}
|
||||
if (!result) {
|
||||
result = InsertImage(desc.info);
|
||||
inserted_new = true;
|
||||
auto& inserted = ResolveImage(result);
|
||||
if (m_buffer_cache.HasGpuDirtyBytes(inserted.info.data.address,
|
||||
inserted.info.data.size)) {
|
||||
inserted.MarkBufferModified();
|
||||
}
|
||||
}
|
||||
if (inserted_new) {
|
||||
InitializeImage(result, desc);
|
||||
} else {
|
||||
RefreshImage(result, desc);
|
||||
}
|
||||
|
||||
auto& image = ResolveImage(result);
|
||||
if (desc.type == BindingType::VideoOut &&
|
||||
desc.info.metadata.compression != VideoOutCompression::Uncompressed) {
|
||||
@@ -1280,6 +1272,14 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextureCache::UpdateImage(ImageId id) {
|
||||
std::lock_guard transaction(m_resource_mutex);
|
||||
CacheLock lock(*this, m_lock);
|
||||
auto& image = ResolveImage(id);
|
||||
TouchImage(image);
|
||||
RefreshImage(id, ImageDesc {.info = image.info, .type = UploadBinding(image)});
|
||||
}
|
||||
|
||||
ImageId TextureCache::FindImageFromRange(uint64_t address, uint64_t size, bool ensure_valid) {
|
||||
if (!GuestRange {address, size}.Valid()) {
|
||||
return {};
|
||||
@@ -1332,14 +1332,17 @@ vk::ImageView TextureCache::FindTexture(ImageId id, const ImageDesc& desc) {
|
||||
if (!image.registered || image.depth_id || image.binding.needs_rebind) {
|
||||
EXIT("TextureCache: texture requires rediscovery before final acquisition\n");
|
||||
}
|
||||
}
|
||||
if (desc.type == BindingType::Storage) {
|
||||
image.MarkGpuModified();
|
||||
}
|
||||
if (!image.info.data.Empty()) {
|
||||
RefreshImage(id, desc);
|
||||
}
|
||||
switch (desc.type) {
|
||||
case BindingType::Texture: break;
|
||||
case BindingType::Storage:
|
||||
if (image.info.data.Empty()) {
|
||||
image.MarkGpuModified();
|
||||
} else {
|
||||
if (!image.info.data.Empty()) {
|
||||
if (!image.registered || image.depth_id) {
|
||||
EXIT("TextureCache: cannot acquire an unavailable storage image\n");
|
||||
}
|
||||
@@ -1365,9 +1368,10 @@ vk::ImageView TextureCache::FindRenderTarget(ImageId id, const ImageDesc& desc)
|
||||
EXIT("TextureCache: color target requires rediscovery before final acquisition\n");
|
||||
}
|
||||
TouchImage(image);
|
||||
image.MarkGpuModified();
|
||||
image.usage.render_target = true;
|
||||
RefreshImage(id, desc);
|
||||
CommitGpuWrite(image);
|
||||
image.usage.render_target = true;
|
||||
TrackImageDownloadLocked(id, image);
|
||||
const auto view = image.FindView(desc.view_info);
|
||||
RetainImage(m_scheduler.Current(), id);
|
||||
@@ -1385,6 +1389,8 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) {
|
||||
EXIT("TextureCache: depth target requires rediscovery before final acquisition\n");
|
||||
}
|
||||
TouchImage(image);
|
||||
image.MarkGpuModified();
|
||||
image.usage.depth_target = true;
|
||||
RefreshImage(id, desc);
|
||||
if (desc.info.HasMetadata()) {
|
||||
image.info.metadata = desc.info.metadata;
|
||||
@@ -1392,7 +1398,6 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) {
|
||||
MetaDataInfo {.clear_mask = image.info.htile_clear_mask});
|
||||
}
|
||||
CommitGpuWrite(image);
|
||||
image.usage.depth_target = true;
|
||||
if (desc.info.HasStencil()) {
|
||||
AssociateStencilLocked(id, desc.info.stencil);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
KYTY_CLASS_NO_COPY(TextureCache);
|
||||
|
||||
[[nodiscard]] ImageId FindImage(ImageDesc& desc, bool exact_format = false);
|
||||
void UpdateImage(ImageId id);
|
||||
[[nodiscard]] ImageId FindImageFromRange(uint64_t address, uint64_t size,
|
||||
bool ensure_valid = true);
|
||||
[[nodiscard]] vk::ImageView FindTexture(ImageId id, const ImageDesc& desc);
|
||||
|
||||
@@ -663,7 +663,8 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
||||
: info(image_info), m_graphics(&graphics), m_scheduler(&scheduler) {
|
||||
KYTY_PROFILER_FUNCTION();
|
||||
ImageOps::Validate(info);
|
||||
m_cpu_dirty = !info.data.Empty();
|
||||
m_cpu_dirty =
|
||||
!info.data.Empty() && info.metadata.compression == VideoOutCompression::Uncompressed;
|
||||
if (info.pixel_format == vk::Format::eUndefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ struct RenderTargetFormatMapping {
|
||||
};
|
||||
|
||||
constexpr RenderTargetFormatMapping kRenderTargetFormats[] = {
|
||||
{Prospero::ChannelLayout::k8,
|
||||
Prospero::ChannelType::kUInt,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
{vk::Format::eR8Uint, 1}},
|
||||
{Prospero::ChannelLayout::k8_8,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
Prospero::ChannelOrder::kStandard,
|
||||
|
||||
@@ -379,6 +379,10 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
||||
tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) && !resource.read &&
|
||||
!Prospero::IsFmaskTextureFormat(descriptor.Format()) && (is_2d || is_2d_array) &&
|
||||
TileGetBlockLayout(TileBlockFamily::Depth64KB, depth_bpe, depth_block);
|
||||
const bool supported_standard_256b =
|
||||
(is_2d || is_2d_array) &&
|
||||
tile == Prospero::GpuEnumValue(Prospero::TileMode::kStandard256B) &&
|
||||
TileIsStandard256BTextureSupported(descriptor.Format());
|
||||
const bool supported_standard_tile =
|
||||
(tile == Prospero::GpuEnumValue(Prospero::TileMode::kStandard4KB) &&
|
||||
TileIsStandard4KBTextureSupported(descriptor.Format())) ||
|
||||
@@ -386,7 +390,8 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
||||
TileIsStandard64KBTextureSupported(descriptor.Format()));
|
||||
const bool supported_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kLinear) ||
|
||||
tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
|
||||
supported_depth_tile || supported_standard_tile;
|
||||
supported_depth_tile || supported_standard_256b ||
|
||||
supported_standard_tile;
|
||||
const auto swizzle = descriptor.DstSelXYZW();
|
||||
const bool supported_swizzle =
|
||||
IsValidImageSwizzle(swizzle) &&
|
||||
|
||||
@@ -36,6 +36,22 @@ VulkanMemoryBarrier MakeShaderWriteDependency() {
|
||||
return barrier;
|
||||
}
|
||||
|
||||
VulkanMemoryBarrier MakeShaderAccessDependency() {
|
||||
VulkanMemoryBarrier barrier {};
|
||||
barrier.sType = vk::StructureType::eMemoryBarrier;
|
||||
barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
|
||||
barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
||||
return barrier;
|
||||
}
|
||||
|
||||
VulkanMemoryBarrier MakeShaderWriteHazardDependency() {
|
||||
VulkanMemoryBarrier barrier {};
|
||||
barrier.sType = vk::StructureType::eMemoryBarrier;
|
||||
barrier.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
||||
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
|
||||
return barrier;
|
||||
}
|
||||
|
||||
vk::BufferMemoryBarrier MakeGdsDependency(vk::Buffer buffer) {
|
||||
EXIT_IF(buffer == nullptr);
|
||||
|
||||
@@ -84,6 +100,21 @@ bool HasShaderBufferWrites(const ShaderStageRuntime& runtime) {
|
||||
return !CollectShaderBufferWrites(*runtime.program, *runtime.resources).empty();
|
||||
}
|
||||
|
||||
void ShaderAccessBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages) {
|
||||
EXIT_IF(vk_buffer == nullptr || !source_stages);
|
||||
const auto barrier = MakeShaderAccessDependency();
|
||||
vk_buffer.pipelineBarrier(source_stages, vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::DependencyFlags {}, 1, &barrier, 0, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
void ShaderWriteHazardBarrier(vk::CommandBuffer vk_buffer,
|
||||
vk::PipelineStageFlags destination_stages) {
|
||||
EXIT_IF(vk_buffer == nullptr || !destination_stages);
|
||||
const auto barrier = MakeShaderWriteHazardDependency();
|
||||
vk_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, destination_stages,
|
||||
vk::DependencyFlags {}, 1, &barrier, 0, nullptr, 0, nullptr);
|
||||
}
|
||||
|
||||
void ShaderWriteBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages) {
|
||||
EXIT_IF(vk_buffer == nullptr || !source_stages);
|
||||
const auto barrier = MakeShaderWriteDependency();
|
||||
|
||||
@@ -18,12 +18,17 @@ struct ShaderBufferWriteRange {
|
||||
};
|
||||
|
||||
vk::PipelineStageFlags ShaderPipelineStages(vk::ShaderStageFlags stages);
|
||||
VulkanMemoryBarrier MakeShaderAccessDependency();
|
||||
VulkanMemoryBarrier MakeShaderWriteHazardDependency();
|
||||
VulkanMemoryBarrier MakeShaderWriteDependency();
|
||||
vk::BufferMemoryBarrier MakeGdsDependency(vk::Buffer buffer);
|
||||
std::vector<ShaderBufferWriteRange>
|
||||
CollectShaderBufferWrites(const ShaderRecompiler::IR::Program& program,
|
||||
const ShaderRecompiler::IR::ResourceSnapshot& resources);
|
||||
bool HasShaderBufferWrites(const ShaderStageRuntime& runtime);
|
||||
void ShaderAccessBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages);
|
||||
void ShaderWriteHazardBarrier(vk::CommandBuffer vk_buffer,
|
||||
vk::PipelineStageFlags destination_stages);
|
||||
void ShaderWriteBarrier(vk::CommandBuffer vk_buffer, vk::PipelineStageFlags source_stages);
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
@@ -325,9 +325,6 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
|
||||
|
||||
auto vk_buffer = buffer.Handle();
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline.pipeline_layout, bindings);
|
||||
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
|
||||
vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z);
|
||||
|
||||
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
||||
has_storage_writes =
|
||||
std::any_of(program.info.images.begin(), program.info.images.end(),
|
||||
@@ -338,8 +335,15 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, RenderCommandBuffer& buf
|
||||
}) ||
|
||||
has_storage_writes;
|
||||
if (has_storage_writes) {
|
||||
ShaderWriteBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||
// A host fence used to serialize every dispatch. Preserve its read-before-write ordering
|
||||
// while allowing the queue to execute asynchronously.
|
||||
ShaderWriteHazardBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||
}
|
||||
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
|
||||
vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z);
|
||||
|
||||
// The removed host fence also ordered read-only dispatches before later writers.
|
||||
ShaderAccessBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||
ResetBindings();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,876 @@
|
||||
#include "graphics/presentation/imeDialogOverlay.h"
|
||||
|
||||
#include "SDL.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/host_gpu/graphicContext.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include "libs/controller.h"
|
||||
#include "libs/imeDialog.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
namespace {
|
||||
|
||||
namespace Ime = Libs::Dialog::ImeDialog;
|
||||
|
||||
constexpr size_t INPUT_QUEUE_CAPACITY = 128;
|
||||
|
||||
enum class InputKind : uint8_t {
|
||||
Button,
|
||||
Axis,
|
||||
MousePosition,
|
||||
MouseButton,
|
||||
MouseWheel,
|
||||
ResetController
|
||||
};
|
||||
|
||||
struct InputEvent {
|
||||
InputKind kind;
|
||||
uint64_t generation;
|
||||
int id;
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct VisibilityUpdate {
|
||||
uint64_t generation;
|
||||
bool visible;
|
||||
bool capture_controller;
|
||||
bool capture_keyboard;
|
||||
bool multiline;
|
||||
};
|
||||
|
||||
std::atomic<Uint32> g_visibility_event {static_cast<Uint32>(-1)};
|
||||
std::mutex g_visibility_mutex;
|
||||
std::mutex g_input_mutex;
|
||||
std::deque<InputEvent> g_input_events;
|
||||
std::deque<VisibilityUpdate> g_visibility_updates;
|
||||
size_t g_missing_visibility_wakeups = 0;
|
||||
bool g_input_reset_requested = false;
|
||||
uint16_t g_last_external_keycode = 0;
|
||||
uint32_t g_last_external_status = 0;
|
||||
uint64_t g_input_generation = 0;
|
||||
bool g_input_active = false;
|
||||
bool g_input_controller = false;
|
||||
bool g_input_keyboard = false;
|
||||
bool g_input_multiline = false;
|
||||
bool g_input_lifecycle_active = false;
|
||||
bool g_active = false;
|
||||
bool g_controller_captured = false;
|
||||
uint64_t g_generation = 0;
|
||||
|
||||
void ClearInputEvents() {
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
g_input_events.clear();
|
||||
g_input_reset_requested = false;
|
||||
}
|
||||
|
||||
void QueueInput(InputEvent event) {
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
const bool replaceable =
|
||||
event.kind == InputKind::Axis || event.kind == InputKind::MousePosition;
|
||||
if (replaceable && !g_input_events.empty()) {
|
||||
auto& last = g_input_events.back();
|
||||
if (last.generation == event.generation && last.kind == event.kind && last.id == event.id) {
|
||||
last = event;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (g_input_events.size() == INPUT_QUEUE_CAPACITY) {
|
||||
g_input_events.clear();
|
||||
g_input_reset_requested = true;
|
||||
}
|
||||
g_input_events.push_back(event);
|
||||
}
|
||||
|
||||
void RetryVisibilityWakeup() {
|
||||
const Uint32 type = g_visibility_event.load(std::memory_order_acquire);
|
||||
if (type == static_cast<Uint32>(-1)) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
if (g_missing_visibility_wakeups == 0) {
|
||||
return;
|
||||
}
|
||||
SDL_Event event {};
|
||||
event.type = type;
|
||||
if (SDL_PushEvent(&event) > 0) {
|
||||
g_missing_visibility_wakeups--;
|
||||
}
|
||||
}
|
||||
|
||||
void OnVisibilityChanged(bool visible, uint64_t generation) {
|
||||
std::scoped_lock visibility_lock(g_visibility_mutex);
|
||||
if (!g_input_lifecycle_active) {
|
||||
return;
|
||||
}
|
||||
if (generation < g_generation || (generation == g_generation && visible == g_active)) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_generation = generation;
|
||||
bool capture_controller = false;
|
||||
bool capture_keyboard = false;
|
||||
bool multiline = false;
|
||||
if (visible) {
|
||||
Ime::HostSnapshot snapshot;
|
||||
if (Ime::GetHostSnapshot(&snapshot) && snapshot.generation == generation) {
|
||||
capture_controller = (snapshot.disable_device & Ime::DISABLE_DEVICE_CONTROLLER) == 0;
|
||||
capture_keyboard = (snapshot.disable_device & Ime::DISABLE_DEVICE_EXT_KEYBOARD) == 0;
|
||||
multiline = (snapshot.option & Ime::OPTION_MULTILINE) != 0;
|
||||
} else {
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
const bool was_controller = std::exchange(g_controller_captured, capture_controller);
|
||||
if (capture_controller || was_controller) {
|
||||
Controller::ControllerResetInputState();
|
||||
}
|
||||
g_active = visible;
|
||||
|
||||
const Uint32 type = g_visibility_event.load(std::memory_order_acquire);
|
||||
if (type != static_cast<Uint32>(-1)) {
|
||||
SDL_Event event {};
|
||||
event.type = type;
|
||||
std::scoped_lock input_lock(g_input_mutex);
|
||||
g_visibility_updates.push_back(
|
||||
{generation, visible, capture_controller, capture_keyboard, multiline});
|
||||
if (g_missing_visibility_wakeups != 0 || SDL_PushEvent(&event) <= 0) {
|
||||
g_missing_visibility_wakeups++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ExternalKeyStatus(SDL_Keymod modifiers, bool character_valid) {
|
||||
uint32_t status = 0x00000001 | (character_valid ? 0x00000002 : 0);
|
||||
if ((modifiers & KMOD_LCTRL) != 0) status |= 0x00000100;
|
||||
if ((modifiers & KMOD_LSHIFT) != 0) status |= 0x00000200;
|
||||
if ((modifiers & KMOD_LALT) != 0) status |= 0x00000400;
|
||||
if ((modifiers & KMOD_LGUI) != 0) status |= 0x00000800;
|
||||
if ((modifiers & KMOD_RCTRL) != 0) status |= 0x00001000;
|
||||
if ((modifiers & KMOD_RSHIFT) != 0) status |= 0x00002000;
|
||||
if ((modifiers & KMOD_RALT) != 0) status |= 0x00004000;
|
||||
if ((modifiers & KMOD_RGUI) != 0) status |= 0x00008000;
|
||||
if ((modifiers & KMOD_NUM) != 0) status |= 0x00010000;
|
||||
if ((modifiers & KMOD_CAPS) != 0) status |= 0x00020000;
|
||||
return status;
|
||||
}
|
||||
|
||||
Ime::ExternalInput MakeExternalInput(Ime::ExternalAction action, uint16_t keycode,
|
||||
uint32_t status) {
|
||||
Ime::ExternalInput input {};
|
||||
input.key.keycode = keycode;
|
||||
input.key.status = status;
|
||||
input.key.type = 4;
|
||||
input.action = action;
|
||||
return input;
|
||||
}
|
||||
|
||||
std::u16string Utf8ToUtf16(std::string_view text) {
|
||||
std::u16string result;
|
||||
result.reserve(text.size());
|
||||
for (size_t i = 0; i < text.size();) {
|
||||
const auto first = static_cast<uint8_t>(text[i++]);
|
||||
uint32_t codepoint = 0;
|
||||
uint32_t remaining = 0;
|
||||
if (first < 0x80) {
|
||||
codepoint = first;
|
||||
} else if ((first & 0xe0) == 0xc0) {
|
||||
codepoint = first & 0x1f;
|
||||
remaining = 1;
|
||||
} else if ((first & 0xf0) == 0xe0) {
|
||||
codepoint = first & 0x0f;
|
||||
remaining = 2;
|
||||
} else if ((first & 0xf8) == 0xf0) {
|
||||
codepoint = first & 0x07;
|
||||
remaining = 3;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (i + remaining > text.size()) {
|
||||
break;
|
||||
}
|
||||
bool valid = true;
|
||||
for (uint32_t j = 0; j < remaining; j++) {
|
||||
const auto next = static_cast<uint8_t>(text[i++]);
|
||||
if ((next & 0xc0) != 0x80) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
codepoint = (codepoint << 6) | (next & 0x3f);
|
||||
}
|
||||
if (!valid || codepoint > 0x10ffff || (codepoint >= 0xd800 && codepoint <= 0xdfff)) {
|
||||
continue;
|
||||
}
|
||||
if (codepoint <= 0xffff) {
|
||||
result.push_back(static_cast<char16_t>(codepoint));
|
||||
} else {
|
||||
codepoint -= 0x10000;
|
||||
result.push_back(static_cast<char16_t>(0xd800 + (codepoint >> 10)));
|
||||
result.push_back(static_cast<char16_t>(0xdc00 + (codepoint & 0x3ff)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string VisibleText(const Ime::HostSnapshot& snapshot, size_t max_units) {
|
||||
const size_t cursor = std::min<size_t>(snapshot.cursor, snapshot.text.size());
|
||||
const size_t payload_units = max_units > 4 ? max_units - 4 : 1;
|
||||
size_t begin = cursor > payload_units / 2 ? cursor - payload_units / 2 : 0;
|
||||
size_t end = std::min(snapshot.text.size(), begin + payload_units);
|
||||
if (end == snapshot.text.size() && end - begin < payload_units) {
|
||||
begin = end > payload_units ? end - payload_units : 0;
|
||||
}
|
||||
if (begin > 0 && snapshot.text[begin] >= 0xdc00 && snapshot.text[begin] <= 0xdfff) {
|
||||
begin--;
|
||||
}
|
||||
if (end < snapshot.text.size() && end > begin && snapshot.text[end - 1] >= 0xd800 &&
|
||||
snapshot.text[end - 1] <= 0xdbff) {
|
||||
end--;
|
||||
}
|
||||
|
||||
std::u16string visible;
|
||||
if (begin != 0) {
|
||||
visible.push_back(u'\u2026');
|
||||
}
|
||||
const size_t caret = visible.size() + cursor - begin;
|
||||
if ((snapshot.option & Ime::OPTION_PASSWORD) != 0) {
|
||||
visible.append(end - begin, u'*');
|
||||
} else {
|
||||
visible.append(snapshot.text, begin, end - begin);
|
||||
std::replace(visible.begin(), visible.end(), u'\n', u'\u21b5');
|
||||
std::replace(visible.begin(), visible.end(), u'\r', u'\u21b5');
|
||||
}
|
||||
visible.insert(std::min(caret, visible.size()), 1, u'|');
|
||||
if (end != snapshot.text.size()) {
|
||||
visible.push_back(u'\u2026');
|
||||
}
|
||||
return Common::Utf16ToUtf8(visible.c_str());
|
||||
}
|
||||
|
||||
const char* EnterLabel(Ime::EnterLabel label) {
|
||||
switch (label) {
|
||||
case Ime::EnterLabel::Send: return "Send";
|
||||
case Ime::EnterLabel::Search: return "Search";
|
||||
case Ime::EnterLabel::Go: return "Go";
|
||||
default: return "Done";
|
||||
}
|
||||
}
|
||||
|
||||
float AlignmentPivot(Ime::Alignment alignment) {
|
||||
switch (alignment) {
|
||||
case Ime::Alignment::Start: return 0.0f;
|
||||
case Ime::Alignment::End: return 1.0f;
|
||||
default: return 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiKey ControllerButtonToKey(int button) {
|
||||
switch (button) {
|
||||
case SDL_CONTROLLER_BUTTON_A: return ImGuiKey_GamepadFaceDown;
|
||||
case SDL_CONTROLLER_BUTTON_B: return ImGuiKey_GamepadFaceRight;
|
||||
case SDL_CONTROLLER_BUTTON_X: return ImGuiKey_GamepadFaceLeft;
|
||||
case SDL_CONTROLLER_BUTTON_Y: return ImGuiKey_GamepadFaceUp;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return ImGuiKey_GamepadDpadLeft;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return ImGuiKey_GamepadDpadRight;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: return ImGuiKey_GamepadDpadUp;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return ImGuiKey_GamepadDpadDown;
|
||||
default: return ImGuiKey_None;
|
||||
}
|
||||
}
|
||||
|
||||
PFN_vkVoidFunction LoadVulkanFunction(const char* name, void* user_data) {
|
||||
auto& graphics = *static_cast<GraphicContext*>(user_data);
|
||||
return graphics.instance.getProcAddr(name);
|
||||
}
|
||||
|
||||
void CheckVulkanResult(VkResult result) {
|
||||
EXIT_IF(result != VK_SUCCESS);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void InitializeImeDialogInput() {
|
||||
const Uint32 type = SDL_RegisterEvents(1);
|
||||
EXIT_IF(type == static_cast<Uint32>(-1));
|
||||
{
|
||||
std::scoped_lock lock(g_visibility_mutex);
|
||||
g_input_lifecycle_active = true;
|
||||
g_visibility_event.store(type, std::memory_order_release);
|
||||
}
|
||||
Ime::SetVisibilityCallback(OnVisibilityChanged);
|
||||
Ime::HostSnapshot snapshot;
|
||||
if (Ime::GetHostSnapshot(&snapshot)) {
|
||||
OnVisibilityChanged(true, snapshot.generation);
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownImeDialogInput() {
|
||||
Ime::SetVisibilityCallback(nullptr);
|
||||
{
|
||||
std::scoped_lock lock(g_visibility_mutex);
|
||||
g_input_lifecycle_active = false;
|
||||
g_active = false;
|
||||
if (std::exchange(g_controller_captured, false)) {
|
||||
Controller::ControllerResetInputState();
|
||||
}
|
||||
g_visibility_event.store(static_cast<Uint32>(-1), std::memory_order_release);
|
||||
}
|
||||
ClearInputEvents();
|
||||
{
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
g_visibility_updates.clear();
|
||||
g_missing_visibility_wakeups = 0;
|
||||
}
|
||||
g_input_active = false;
|
||||
g_input_controller = false;
|
||||
g_input_keyboard = false;
|
||||
g_input_multiline = false;
|
||||
if (SDL_IsTextInputActive() == SDL_TRUE) {
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
}
|
||||
|
||||
bool ProcessImeDialogInput(const SDL_Event& event) {
|
||||
RetryVisibilityWakeup();
|
||||
if (event.type == g_visibility_event.load(std::memory_order_acquire)) {
|
||||
VisibilityUpdate update {};
|
||||
{
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
if (g_visibility_updates.empty()) {
|
||||
return true;
|
||||
}
|
||||
update = g_visibility_updates.front();
|
||||
g_visibility_updates.pop_front();
|
||||
}
|
||||
ClearInputEvents();
|
||||
g_last_external_keycode = 0;
|
||||
g_last_external_status = 0;
|
||||
g_input_generation = update.generation;
|
||||
g_input_active = update.visible;
|
||||
g_input_controller = update.capture_controller;
|
||||
g_input_keyboard = update.capture_keyboard;
|
||||
g_input_multiline = update.multiline;
|
||||
if (g_input_keyboard) {
|
||||
SDL_StartTextInput();
|
||||
} else if (SDL_IsTextInputActive() == SDL_TRUE) {
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
|
||||
if (g_input_active && g_input_controller) {
|
||||
QueueInput({InputKind::ResetController, g_input_generation, 0, 0.0f, 0.0f});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!g_input_active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t generation = g_input_generation;
|
||||
const bool controller_event = event.type == SDL_CONTROLLERBUTTONDOWN ||
|
||||
event.type == SDL_CONTROLLERBUTTONUP ||
|
||||
event.type == SDL_CONTROLLERAXISMOTION;
|
||||
if (controller_event && !g_input_controller) {
|
||||
return false;
|
||||
}
|
||||
const bool keyboard_event = event.type == SDL_TEXTINPUT || event.type == SDL_TEXTEDITING ||
|
||||
event.type == SDL_KEYDOWN || event.type == SDL_KEYUP;
|
||||
if (keyboard_event && !g_input_keyboard) {
|
||||
return false;
|
||||
}
|
||||
switch (event.type) {
|
||||
case SDL_TEXTINPUT: {
|
||||
const auto text = Utf8ToUtf16(event.text.text);
|
||||
if (!text.empty()) {
|
||||
auto input = MakeExternalInput(Ime::ExternalAction::Text, g_last_external_keycode,
|
||||
g_last_external_status | 0x00000002);
|
||||
input.key.character = text.front();
|
||||
input.text = text;
|
||||
Ime::HostQueueExternalInput(generation, std::move(input));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case SDL_TEXTEDITING: return true;
|
||||
case SDL_KEYDOWN: {
|
||||
g_last_external_keycode = static_cast<uint16_t>(event.key.keysym.scancode);
|
||||
g_last_external_status =
|
||||
ExternalKeyStatus(static_cast<SDL_Keymod>(event.key.keysym.mod), false);
|
||||
auto action = Ime::ExternalAction::Text;
|
||||
bool queue = true;
|
||||
if (event.key.keysym.sym == SDLK_BACKSPACE) {
|
||||
action = Ime::ExternalAction::Backspace;
|
||||
} else if (event.key.keysym.sym == SDLK_LEFT) {
|
||||
action = Ime::ExternalAction::MoveLeft;
|
||||
} else if (event.key.keysym.sym == SDLK_RIGHT) {
|
||||
action = Ime::ExternalAction::MoveRight;
|
||||
} else if (event.key.keysym.sym == SDLK_ESCAPE) {
|
||||
action = Ime::ExternalAction::Cancel;
|
||||
} else if (event.key.keysym.sym == SDLK_RETURN ||
|
||||
event.key.keysym.sym == SDLK_KP_ENTER) {
|
||||
action =
|
||||
g_input_multiline ? Ime::ExternalAction::Newline : Ime::ExternalAction::Accept;
|
||||
} else if (event.key.keysym.sym == SDLK_TAB) {
|
||||
action = Ime::ExternalAction::None;
|
||||
} else {
|
||||
queue = false;
|
||||
}
|
||||
if (queue) {
|
||||
Ime::HostQueueExternalInput(
|
||||
generation,
|
||||
MakeExternalInput(action, g_last_external_keycode, g_last_external_status));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case SDL_KEYUP: return true;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
QueueInput({InputKind::Button, generation, event.cbutton.button,
|
||||
event.type == SDL_CONTROLLERBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
|
||||
return true;
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
QueueInput({InputKind::Axis, generation, event.caxis.axis,
|
||||
static_cast<float>(event.caxis.value), 0.0f});
|
||||
return true;
|
||||
case SDL_MOUSEMOTION:
|
||||
QueueInput({InputKind::MousePosition, generation, 0, static_cast<float>(event.motion.x),
|
||||
static_cast<float>(event.motion.y)});
|
||||
return true;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
QueueInput({InputKind::MouseButton, generation, event.button.button,
|
||||
event.type == SDL_MOUSEBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
|
||||
return true;
|
||||
case SDL_MOUSEWHEEL:
|
||||
QueueInput({InputKind::MouseWheel, generation, 0, static_cast<float>(event.wheel.x),
|
||||
static_cast<float>(event.wheel.y)});
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct ImeDialogOverlay::Impl {
|
||||
explicit Impl(GraphicContext& context): graphics(context) {}
|
||||
|
||||
~Impl() {
|
||||
ReleaseVulkan();
|
||||
if (imgui_context != nullptr) {
|
||||
ImGui::DestroyContext(imgui_context);
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureContext() {
|
||||
if (imgui_context != nullptr) {
|
||||
ImGui::SetCurrentContext(imgui_context);
|
||||
return;
|
||||
}
|
||||
IMGUI_CHECKVERSION();
|
||||
imgui_context = ImGui::CreateContext();
|
||||
ImGui::SetCurrentContext(imgui_context);
|
||||
auto& io = ImGui::GetIO();
|
||||
io.IniFilename = nullptr;
|
||||
io.LogFilename = nullptr;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
io.BackendPlatformName = "Kyty ImeDialog input";
|
||||
ImGui::StyleColorsDark();
|
||||
auto& style = ImGui::GetStyle();
|
||||
style.WindowRounding = 10.0f;
|
||||
style.FrameRounding = 6.0f;
|
||||
style.ItemSpacing = {8.0f, 8.0f};
|
||||
}
|
||||
|
||||
void EnsureVulkan(vk::Format format, uint32_t image_count) {
|
||||
EnsureContext();
|
||||
if (vulkan_initialized) {
|
||||
return;
|
||||
}
|
||||
EXIT_IF(image_count < 2);
|
||||
EXIT_IF(!ImGui_ImplVulkan_LoadFunctions(VK_API_VERSION_1_3, LoadVulkanFunction, &graphics));
|
||||
|
||||
const VkFormat color_format = static_cast<VkFormat>(format);
|
||||
ImGui_ImplVulkan_InitInfo info {};
|
||||
info.ApiVersion = VK_API_VERSION_1_3;
|
||||
info.Instance = static_cast<VkInstance>(graphics.instance);
|
||||
info.PhysicalDevice = static_cast<VkPhysicalDevice>(graphics.physical_device);
|
||||
info.Device = static_cast<VkDevice>(graphics.device);
|
||||
info.QueueFamily = graphics.queue_family;
|
||||
info.Queue = static_cast<VkQueue>(graphics.queue);
|
||||
info.DescriptorPoolSize = IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE;
|
||||
info.MinImageCount = image_count;
|
||||
info.ImageCount = image_count;
|
||||
info.UseDynamicRendering = true;
|
||||
info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
info.PipelineInfoMain.PipelineRenderingCreateInfo.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
|
||||
info.PipelineInfoMain.PipelineRenderingCreateInfo.colorAttachmentCount = 1;
|
||||
info.PipelineInfoMain.PipelineRenderingCreateInfo.pColorAttachmentFormats = &color_format;
|
||||
info.CheckVkResultFn = CheckVulkanResult;
|
||||
EXIT_IF(!ImGui_ImplVulkan_Init(&info));
|
||||
vulkan_initialized = true;
|
||||
}
|
||||
|
||||
void DrainInput(uint64_t generation) {
|
||||
std::deque<InputEvent> events;
|
||||
bool reset = false;
|
||||
{
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
events.swap(g_input_events);
|
||||
reset = g_input_reset_requested;
|
||||
g_input_reset_requested = false;
|
||||
}
|
||||
auto& io = ImGui::GetIO();
|
||||
if (reset) {
|
||||
io.ClearEventsQueue();
|
||||
io.ClearInputKeys();
|
||||
io.ClearInputMouse();
|
||||
right_stick = {};
|
||||
}
|
||||
for (const auto& event: events) {
|
||||
if (event.generation != generation) {
|
||||
continue;
|
||||
}
|
||||
switch (event.kind) {
|
||||
case InputKind::Button: {
|
||||
const bool down = event.x != 0.0f;
|
||||
if (down && event.id == SDL_CONTROLLER_BUTTON_B) {
|
||||
Ime::HostCancel(generation);
|
||||
} else if (down && event.id == SDL_CONTROLLER_BUTTON_Y) {
|
||||
Ime::HostBackspace(generation);
|
||||
}
|
||||
const ImGuiKey key = ControllerButtonToKey(event.id);
|
||||
if (key != ImGuiKey_None) {
|
||||
io.AddKeyEvent(key, down);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case InputKind::Axis: {
|
||||
const float value = event.x / (event.x < 0.0f ? 32768.0f : 32767.0f);
|
||||
if (event.id == SDL_CONTROLLER_AXIS_LEFTX) {
|
||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickLeft, value < -0.25f,
|
||||
std::max(-value, 0.0f));
|
||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickRight, value > 0.25f,
|
||||
std::max(value, 0.0f));
|
||||
} else if (event.id == SDL_CONTROLLER_AXIS_LEFTY) {
|
||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickUp, value < -0.25f,
|
||||
std::max(-value, 0.0f));
|
||||
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickDown, value > 0.25f,
|
||||
std::max(value, 0.0f));
|
||||
} else if (event.id == SDL_CONTROLLER_AXIS_RIGHTX) {
|
||||
right_stick.x = std::abs(value) > 0.2f ? value : 0.0f;
|
||||
} else if (event.id == SDL_CONTROLLER_AXIS_RIGHTY) {
|
||||
right_stick.y = std::abs(value) > 0.2f ? value : 0.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case InputKind::MousePosition: io.AddMousePosEvent(event.x, event.y); break;
|
||||
case InputKind::MouseButton: {
|
||||
int button = -1;
|
||||
if (event.id == SDL_BUTTON_LEFT) button = 0;
|
||||
if (event.id == SDL_BUTTON_RIGHT) button = 1;
|
||||
if (event.id == SDL_BUTTON_MIDDLE) button = 2;
|
||||
if (button >= 0) io.AddMouseButtonEvent(button, event.x != 0.0f);
|
||||
break;
|
||||
}
|
||||
case InputKind::MouseWheel: io.AddMouseWheelEvent(event.x, event.y); break;
|
||||
case InputKind::ResetController:
|
||||
io.ClearEventsQueue();
|
||||
io.ClearInputKeys();
|
||||
right_stick = {};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyButton(std::string_view label, char16_t value, uint64_t generation, float width,
|
||||
bool default_focus) {
|
||||
const bool pressed = ImGui::Button(label.data(), {width, button_height});
|
||||
if (default_focus) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
if (pressed) {
|
||||
Ime::HostInsertText(generation, std::u16string_view(&value, 1));
|
||||
}
|
||||
}
|
||||
|
||||
void DrawKeyRows(const Ime::HostSnapshot& snapshot, float width) {
|
||||
static constexpr std::array<std::string_view, 4> lower = {"1234567890", "qwertyuiop",
|
||||
"asdfghjkl", "zxcvbnm"};
|
||||
static constexpr std::array<std::string_view, 4> upper = {"1234567890", "QWERTYUIOP",
|
||||
"ASDFGHJKL", "ZXCVBNM"};
|
||||
static constexpr std::array<std::string_view, 3> symbols = {"1234567890", "!@#$%^&*()",
|
||||
"-_=+/:?."};
|
||||
static constexpr std::array<std::string_view, 4> number = {"123", "456", "789", "-0."};
|
||||
|
||||
std::span<const std::string_view> rows;
|
||||
if (snapshot.type == Ime::Type::Number) {
|
||||
rows = number;
|
||||
} else if (symbol_mode) {
|
||||
rows = symbols;
|
||||
} else {
|
||||
rows = shift ? std::span<const std::string_view>(upper)
|
||||
: std::span<const std::string_view>(lower);
|
||||
}
|
||||
bool first = focus_pending;
|
||||
size_t row_index = 0;
|
||||
for (const auto row: rows) {
|
||||
const float key_width =
|
||||
std::min(56.0f * ui_scale, (width - 8.0f * (row.size() - 1)) / row.size());
|
||||
const float row_width = key_width * row.size() + 8.0f * (row.size() - 1);
|
||||
ImGui::SetCursorPosX((width - row_width) * 0.5f);
|
||||
ImGui::PushID(static_cast<int>(row_index++));
|
||||
for (size_t i = 0; i < row.size(); i++) {
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
const char label[2] = {row[i], '\0'};
|
||||
KeyButton(label, static_cast<char16_t>(row[i]), snapshot.generation, key_width,
|
||||
first);
|
||||
first = false;
|
||||
ImGui::PopID();
|
||||
if (i + 1 < row.size()) ImGui::SameLine();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
focus_pending = false;
|
||||
}
|
||||
|
||||
void DrawDialog(const Ime::HostSnapshot& snapshot, vk::Extent2D extent) {
|
||||
const ImVec2 display(static_cast<float>(extent.width), static_cast<float>(extent.height));
|
||||
const bool over_2k = (snapshot.option & Ime::OPTION_USE_OVER_2K) != 0;
|
||||
const float reference_width = over_2k ? 3840.0f : 1920.0f;
|
||||
const float reference_height = over_2k ? 2160.0f : 1080.0f;
|
||||
const float scale_x = display.x / reference_width;
|
||||
const float scale_y = display.y / reference_height;
|
||||
const float width = static_cast<float>(snapshot.panel_width) * scale_x;
|
||||
const float height = static_cast<float>(snapshot.panel_height) * scale_y;
|
||||
ui_scale = std::min(scale_x, scale_y) * (over_2k ? 2.0f : 1.0f);
|
||||
button_height = std::max(28.0f, 42.0f * ui_scale);
|
||||
|
||||
const ImVec2 pivot {AlignmentPivot(snapshot.horizontal_alignment),
|
||||
AlignmentPivot(snapshot.vertical_alignment)};
|
||||
if ((snapshot.option & Ime::OPTION_FIXED_POSITION) == 0) {
|
||||
const float movement = 600.0f * ImGui::GetIO().DeltaTime;
|
||||
panel_offset.x += right_stick.x * movement;
|
||||
panel_offset.y += right_stick.y * movement;
|
||||
}
|
||||
const ImVec2 base_position {snapshot.posx * scale_x - pivot.x * width,
|
||||
snapshot.posy * scale_y - pivot.y * height};
|
||||
ImVec2 position {base_position.x + panel_offset.x, base_position.y + panel_offset.y};
|
||||
if ((snapshot.option & Ime::OPTION_DISABLE_POSITION_ADJ) == 0) {
|
||||
position.x = std::clamp(position.x, 0.0f, std::max(display.x - width, 0.0f));
|
||||
position.y = std::clamp(position.y, 0.0f, std::max(display.y - height, 0.0f));
|
||||
panel_offset = {position.x - base_position.x, position.y - base_position.y};
|
||||
}
|
||||
ImGui::SetNextWindowPos(position, ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize({width, height}, ImGuiCond_Always);
|
||||
constexpr ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings;
|
||||
ImGui::Begin("##ImeDialog", nullptr, flags);
|
||||
|
||||
const std::u16string title = snapshot.title.empty() ? u"Enter text" : snapshot.title;
|
||||
if (!snapshot.key_panel_visible) {
|
||||
std::string compact = Common::Utf16ToUtf8(title.c_str()) + ": ";
|
||||
const float glyph_width = std::max(ImGui::CalcTextSize("M").x, 1.0f);
|
||||
const float text_width =
|
||||
std::max(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(compact.c_str()).x,
|
||||
glyph_width);
|
||||
const size_t visible_units =
|
||||
std::max<size_t>(6, static_cast<size_t>(text_width / glyph_width));
|
||||
compact += snapshot.text.empty() && !snapshot.placeholder.empty()
|
||||
? Common::Utf16ToUtf8(snapshot.placeholder.c_str())
|
||||
: VisibleText(snapshot, visible_units);
|
||||
ImGui::TextUnformatted(compact.c_str());
|
||||
const float compact_height = std::min(button_height, 28.0f * ui_scale);
|
||||
if (ImGui::Button("Cancel", {90.0f * ui_scale, compact_height})) {
|
||||
Ime::HostCancel(snapshot.generation);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(EnterLabel(snapshot.enter_label),
|
||||
{90.0f * ui_scale, compact_height})) {
|
||||
Ime::HostAccept(snapshot.generation);
|
||||
}
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextUnformatted(Common::Utf16ToUtf8(title.c_str()).c_str());
|
||||
ImGui::Separator();
|
||||
const float text_height = std::max(
|
||||
32.0f, ((snapshot.option & Ime::OPTION_MULTILINE) != 0 ? 80.0f : 48.0f) * ui_scale);
|
||||
ImGui::BeginChild("##ImeText", {0.0f, text_height}, true);
|
||||
if (snapshot.text.empty() && !snapshot.placeholder.empty()) {
|
||||
ImGui::TextDisabled("%s", Common::Utf16ToUtf8(snapshot.placeholder.c_str()).c_str());
|
||||
} else {
|
||||
const float glyph_width = std::max(ImGui::CalcTextSize("M").x, 1.0f);
|
||||
const size_t columns = std::max<size_t>(
|
||||
8, static_cast<size_t>(ImGui::GetContentRegionAvail().x / glyph_width));
|
||||
const size_t lines =
|
||||
std::max<size_t>(1, static_cast<size_t>(ImGui::GetContentRegionAvail().y /
|
||||
ImGui::GetTextLineHeightWithSpacing()));
|
||||
const auto text = VisibleText(snapshot, columns * lines);
|
||||
ImGui::TextWrapped("%s", text.c_str());
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::TextDisabled("%zu / %u", snapshot.text.size(), snapshot.max_text_length);
|
||||
ImGui::Separator();
|
||||
|
||||
const float content_width = ImGui::GetContentRegionAvail().x;
|
||||
DrawKeyRows(snapshot, content_width);
|
||||
if (snapshot.type != Ime::Type::Number) {
|
||||
if (ImGui::Button(shift ? "Lower" : "Shift", {84.0f * ui_scale, button_height})) {
|
||||
shift = !shift;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(symbol_mode ? "ABC" : "Symbols", {84.0f * ui_scale, button_height})) {
|
||||
symbol_mode = !symbol_mode;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Space", {120.0f * ui_scale, button_height})) {
|
||||
Ime::HostInsertText(snapshot.generation, u" ");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
const float action_width = snapshot.type == Ime::Type::Number
|
||||
? std::max((content_width - 16.0f) / 3.0f, 48.0f)
|
||||
: 100.0f * ui_scale;
|
||||
if (ImGui::Button("Backspace", {action_width, button_height})) {
|
||||
Ime::HostBackspace(snapshot.generation);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if ((snapshot.option & Ime::OPTION_MULTILINE) != 0) {
|
||||
if (ImGui::Button("Newline", {action_width, button_height})) {
|
||||
Ime::HostInsertText(snapshot.generation, u"\n");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (ImGui::Button("Cancel", {action_width, button_height})) {
|
||||
Ime::HostCancel(snapshot.generation);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(EnterLabel(snapshot.enter_label), {action_width, button_height})) {
|
||||
Ime::HostAccept(snapshot.generation);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
bool PrepareFrame(vk::Extent2D frame_extent, vk::Format format, uint32_t image_count) {
|
||||
Ime::HostSnapshot snapshot;
|
||||
if (!Ime::GetHostSnapshot(&snapshot)) {
|
||||
return false;
|
||||
}
|
||||
const uint64_t prepared_generation = snapshot.generation;
|
||||
EnsureVulkan(format, image_count);
|
||||
if (generation != snapshot.generation) {
|
||||
generation = snapshot.generation;
|
||||
focus_pending = true;
|
||||
shift = (snapshot.option & Ime::OPTION_NO_AUTO_CAPITALIZE) == 0;
|
||||
symbol_mode = false;
|
||||
panel_offset = {};
|
||||
right_stick = {};
|
||||
auto& io = ImGui::GetIO();
|
||||
io.ClearEventsQueue();
|
||||
io.ClearInputKeys();
|
||||
io.ClearInputMouse();
|
||||
}
|
||||
DrainInput(snapshot.generation);
|
||||
|
||||
auto& io = ImGui::GetIO();
|
||||
io.DisplaySize = {static_cast<float>(frame_extent.width),
|
||||
static_cast<float>(frame_extent.height)};
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
io.DeltaTime = last_frame == std::chrono::steady_clock::time_point {}
|
||||
? 1.0f / 60.0f
|
||||
: std::clamp(std::chrono::duration<float>(now - last_frame).count(),
|
||||
1.0f / 1000.0f, 0.1f);
|
||||
last_frame = now;
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
if (!Ime::GetHostSnapshot(&snapshot) || snapshot.generation != prepared_generation) {
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
DrawDialog(snapshot, frame_extent);
|
||||
ImGui::Render();
|
||||
extent = frame_extent;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Record(vk::CommandBuffer command, vk::ImageView target) {
|
||||
vk::RenderingAttachmentInfo color {};
|
||||
color.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||
color.imageView = target;
|
||||
color.imageLayout = vk::ImageLayout::eColorAttachmentOptimal;
|
||||
color.loadOp = vk::AttachmentLoadOp::eLoad;
|
||||
color.storeOp = vk::AttachmentStoreOp::eStore;
|
||||
vk::RenderingInfo rendering {};
|
||||
rendering.sType = vk::StructureType::eRenderingInfo;
|
||||
rendering.renderArea.extent = extent;
|
||||
rendering.layerCount = 1;
|
||||
rendering.colorAttachmentCount = 1;
|
||||
rendering.pColorAttachments = &color;
|
||||
command.beginRendering(rendering);
|
||||
{
|
||||
Common::LockGuard queue_lock(graphics.queue_mutex);
|
||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(),
|
||||
static_cast<VkCommandBuffer>(command));
|
||||
}
|
||||
command.endRendering();
|
||||
}
|
||||
|
||||
void ReleaseVulkan() {
|
||||
if (!vulkan_initialized) {
|
||||
return;
|
||||
}
|
||||
ImGui::SetCurrentContext(imgui_context);
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
vulkan_initialized = false;
|
||||
}
|
||||
|
||||
GraphicContext& graphics;
|
||||
ImGuiContext* imgui_context = nullptr;
|
||||
bool vulkan_initialized = false;
|
||||
bool shift = false;
|
||||
bool symbol_mode = false;
|
||||
bool focus_pending = true;
|
||||
float ui_scale = 1.0f;
|
||||
float button_height = 42.0f;
|
||||
ImVec2 panel_offset {};
|
||||
ImVec2 right_stick {};
|
||||
uint64_t generation = 0;
|
||||
vk::Extent2D extent {};
|
||||
std::chrono::steady_clock::time_point last_frame;
|
||||
};
|
||||
|
||||
ImeDialogOverlay::ImeDialogOverlay(GraphicContext& graphics)
|
||||
: m_impl(std::make_unique<Impl>(graphics)) {}
|
||||
|
||||
ImeDialogOverlay::~ImeDialogOverlay() = default;
|
||||
|
||||
bool ImeDialogOverlay::PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count) {
|
||||
return m_impl->PrepareFrame(extent, format, image_count);
|
||||
}
|
||||
|
||||
void ImeDialogOverlay::Record(vk::CommandBuffer command, vk::ImageView target) {
|
||||
m_impl->Record(command, target);
|
||||
}
|
||||
|
||||
void ImeDialogOverlay::ReleaseVulkan() {
|
||||
m_impl->ReleaseVulkan();
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEDIALOGOVERLAY_H_
|
||||
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEDIALOGOVERLAY_H_
|
||||
|
||||
#include "common/common.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
struct GraphicContext;
|
||||
|
||||
void InitializeImeDialogInput();
|
||||
void ShutdownImeDialogInput();
|
||||
bool ProcessImeDialogInput(const SDL_Event& event);
|
||||
|
||||
class ImeDialogOverlay final {
|
||||
public:
|
||||
explicit ImeDialogOverlay(GraphicContext& graphics);
|
||||
~ImeDialogOverlay();
|
||||
KYTY_CLASS_NO_COPY(ImeDialogOverlay);
|
||||
|
||||
[[nodiscard]] bool PrepareFrame(vk::Extent2D extent, vk::Format format, uint32_t image_count);
|
||||
void Record(vk::CommandBuffer command, vk::ImageView target);
|
||||
void ReleaseVulkan();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // namespace Libs::Graphics
|
||||
|
||||
#endif // EMULATOR_SRC_GRAPHICS_PRESENTATION_IMEDIALOGOVERLAY_H_
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
CommandBuffer* producer = nullptr);
|
||||
[[nodiscard]] Frame* PrepareLastFrame();
|
||||
[[nodiscard]] bool IsGuestPaused() const noexcept;
|
||||
[[nodiscard]] bool NeedsImeRefresh() const noexcept;
|
||||
[[nodiscard]] RenderContext& Renderer() const noexcept;
|
||||
void Present(Frame& frame, bool reuse = false);
|
||||
void Discard(Frame& frame);
|
||||
|
||||
@@ -817,7 +817,24 @@ void VideoOutDriver::Impl::PresentThread(std::stop_token token) {
|
||||
}
|
||||
|
||||
VblankBegin();
|
||||
const bool presented = m_flip_queue.Flip(0);
|
||||
bool presented = m_flip_queue.Flip(0);
|
||||
if (!presented && m_presenter.NeedsImeRefresh()) {
|
||||
if (auto* frame = m_presenter.PrepareLastFrame(); frame != nullptr) {
|
||||
m_presenter.Present(*frame, true);
|
||||
presented = true;
|
||||
} else {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
{
|
||||
Common::LockGuard lock(m_mutex);
|
||||
width = m_video_out_ctx[0].width;
|
||||
height = m_video_out_ctx[0].height;
|
||||
}
|
||||
auto& blank = m_presenter.PrepareBlankFrame(width, height, true);
|
||||
m_presenter.Present(blank);
|
||||
presented = true;
|
||||
}
|
||||
}
|
||||
if (!presented && total_wait < 0) {
|
||||
bool any_open = false;
|
||||
uint32_t width = 0;
|
||||
|
||||
@@ -31,11 +31,13 @@
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/imeDialogOverlay.h"
|
||||
#include "graphics/presentation/presenter.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/videoOut.h"
|
||||
#include "graphics/presentation/window/windowInternal.h"
|
||||
#include "libs/controller.h"
|
||||
#include "libs/imeDialog.h"
|
||||
#include "loader/systemContent.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -329,8 +331,9 @@ public:
|
||||
void Create();
|
||||
void Recreate(bool surface_lost = false);
|
||||
[[nodiscard]] Status AcquireNextImage();
|
||||
void RecordPresentCommands(CommandBuffer& command, VulkanImage& source);
|
||||
void Submit(CommandBuffer& command);
|
||||
[[nodiscard]] bool PrepareImeOverlay();
|
||||
void RecordPresentCommands(CommandBuffer& command, VulkanImage& source, bool draw_ime_overlay);
|
||||
void Submit(CommandBuffer& command);
|
||||
[[nodiscard]] Status Present();
|
||||
|
||||
[[nodiscard]] uint32_t ImageCount() const noexcept {
|
||||
@@ -342,16 +345,17 @@ private:
|
||||
void Destroy();
|
||||
void RefreshSurfaceSize();
|
||||
|
||||
WindowContext& m_window;
|
||||
vk::SwapchainKHR m_handle = nullptr;
|
||||
vk::Format m_format = vk::Format::eUndefined;
|
||||
vk::Extent2D m_extent {};
|
||||
std::vector<vk::Image> m_images;
|
||||
std::vector<vk::ImageView> m_image_views;
|
||||
std::vector<vk::Semaphore> m_image_acquired;
|
||||
std::vector<vk::Semaphore> m_render_complete;
|
||||
uint32_t m_image_index = static_cast<uint32_t>(-1);
|
||||
uint32_t m_frame_index = 0;
|
||||
WindowContext& m_window;
|
||||
vk::SwapchainKHR m_handle = nullptr;
|
||||
vk::Format m_format = vk::Format::eUndefined;
|
||||
vk::Extent2D m_extent {};
|
||||
std::vector<vk::Image> m_images;
|
||||
std::vector<vk::ImageView> m_image_views;
|
||||
std::vector<vk::Semaphore> m_image_acquired;
|
||||
std::vector<vk::Semaphore> m_render_complete;
|
||||
std::unique_ptr<ImeDialogOverlay> m_ime_overlay;
|
||||
uint32_t m_image_index = static_cast<uint32_t>(-1);
|
||||
uint32_t m_frame_index = 0;
|
||||
};
|
||||
|
||||
struct Presenter::Impl {
|
||||
@@ -383,17 +387,20 @@ struct Presenter::Impl {
|
||||
desc.view_info.usage = vk::ImageUsageFlagBits::eTransferSrc;
|
||||
desc.type = TextureCache::BindingType::VideoOut;
|
||||
|
||||
auto& cache = renderer.GetTextureCache();
|
||||
auto& image = cache.GetImage(cache.FindImage(desc));
|
||||
auto& cache = renderer.GetTextureCache();
|
||||
const auto image_id = cache.FindImage(desc);
|
||||
auto& image = cache.GetImage(image_id);
|
||||
image.usage.video_out = true;
|
||||
cache.UpdateImage(image_id);
|
||||
return image;
|
||||
}
|
||||
|
||||
RenderContext& renderer;
|
||||
WindowContext& window;
|
||||
Swapchain swapchain;
|
||||
CommandScheduler present_scheduler;
|
||||
FramePool frames;
|
||||
RenderContext& renderer;
|
||||
WindowContext& window;
|
||||
Swapchain swapchain;
|
||||
CommandScheduler present_scheduler;
|
||||
FramePool frames;
|
||||
std::atomic<uint64_t> presented_ime_revision {0};
|
||||
};
|
||||
|
||||
void Swapchain::Create() {
|
||||
@@ -522,6 +529,9 @@ void Swapchain::Destroy() {
|
||||
Common::LockGuard queue_lock(graphics.queue_mutex);
|
||||
RequireVulkanSuccess(graphics.queue.waitIdle(), "wait for swapchain queue");
|
||||
}
|
||||
if (m_ime_overlay != nullptr) {
|
||||
m_ime_overlay->ReleaseVulkan();
|
||||
}
|
||||
|
||||
for (const auto semaphore: m_image_acquired) {
|
||||
if (semaphore != nullptr) {
|
||||
@@ -606,7 +616,15 @@ Swapchain::Status Swapchain::AcquireNextImage() {
|
||||
return Status::Success;
|
||||
}
|
||||
|
||||
void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& source) {
|
||||
bool Swapchain::PrepareImeOverlay() {
|
||||
if (m_ime_overlay == nullptr) {
|
||||
m_ime_overlay = std::make_unique<ImeDialogOverlay>(m_window.graphic_ctx);
|
||||
}
|
||||
return m_ime_overlay->PrepareFrame(m_extent, m_format, ImageCount());
|
||||
}
|
||||
|
||||
void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& source,
|
||||
bool draw_ime_overlay) {
|
||||
if (source.state.layout != vk::ImageLayout::eTransferSrcOptimal) {
|
||||
EXIT("invalid prepared presentation image, vk_image=%p layout=%d\n",
|
||||
static_cast<void*>(source.image), static_cast<int>(source.state.layout));
|
||||
@@ -652,11 +670,14 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& sourc
|
||||
vk::Filter::eLinear);
|
||||
|
||||
vk::ImageMemoryBarrier to_present {};
|
||||
to_present.sType = vk::StructureType::eImageMemoryBarrier;
|
||||
to_present.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||
to_present.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
|
||||
to_present.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||
to_present.newLayout = vk::ImageLayout::ePresentSrcKHR;
|
||||
to_present.sType = vk::StructureType::eImageMemoryBarrier;
|
||||
to_present.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||
to_present.dstAccessMask = draw_ime_overlay ? vk::AccessFlagBits::eColorAttachmentRead |
|
||||
vk::AccessFlagBits::eColorAttachmentWrite
|
||||
: vk::AccessFlagBits::eMemoryRead;
|
||||
to_present.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||
to_present.newLayout = draw_ime_overlay ? vk::ImageLayout::eColorAttachmentOptimal
|
||||
: vk::ImageLayout::ePresentSrcKHR;
|
||||
to_present.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
to_present.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
to_present.image = m_images[m_image_index];
|
||||
@@ -665,9 +686,22 @@ void Swapchain::RecordPresentCommands(CommandBuffer& command, VulkanImage& sourc
|
||||
to_present.subresourceRange.levelCount = 1;
|
||||
to_present.subresourceRange.baseArrayLayer = 0;
|
||||
to_present.subresourceRange.layerCount = 1;
|
||||
vk_command.pipelineBarrier(
|
||||
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1, &to_present);
|
||||
vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||
draw_ime_overlay ? vk::PipelineStageFlagBits::eColorAttachmentOutput
|
||||
: vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1,
|
||||
&to_present);
|
||||
if (draw_ime_overlay) {
|
||||
m_ime_overlay->Record(vk_command, m_image_views[m_image_index]);
|
||||
to_present.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
|
||||
to_present.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
|
||||
to_present.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
|
||||
to_present.newLayout = vk::ImageLayout::ePresentSrcKHR;
|
||||
vk_command.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, 1,
|
||||
&to_present);
|
||||
}
|
||||
command.End();
|
||||
}
|
||||
|
||||
@@ -772,6 +806,12 @@ bool Presenter::IsGuestPaused() const noexcept {
|
||||
return m_impl->window.loop.paused.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
bool Presenter::NeedsImeRefresh() const noexcept {
|
||||
const auto visual = Dialog::ImeDialog::GetVisualState();
|
||||
return visual.active ||
|
||||
visual.revision != m_impl->presented_ime_revision.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
RenderContext& Presenter::Renderer() const noexcept {
|
||||
return m_impl->renderer;
|
||||
}
|
||||
@@ -803,7 +843,8 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
||||
m_impl->RecoverSwapchain(Swapchain::Status::Recreate);
|
||||
}
|
||||
|
||||
auto& swapchain = m_impl->swapchain;
|
||||
const auto ime_visual = Dialog::ImeDialog::GetVisualState();
|
||||
auto& swapchain = m_impl->swapchain;
|
||||
for (uint32_t attempt = 0; attempt < 2; attempt++) {
|
||||
auto status = swapchain.AcquireNextImage();
|
||||
if (status != Swapchain::Status::Success) {
|
||||
@@ -816,8 +857,9 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
||||
{
|
||||
Common::LockGuard render_lock(m_impl->renderer.GetMutex());
|
||||
frame.present_commands->WaitForFenceAndReset();
|
||||
auto& command = *frame.present_commands;
|
||||
swapchain.RecordPresentCommands(command, frame.image);
|
||||
auto& command = *frame.present_commands;
|
||||
const bool draw_ime_overlay = ime_visual.active && swapchain.PrepareImeOverlay();
|
||||
swapchain.RecordPresentCommands(command, frame.image, draw_ime_overlay);
|
||||
swapchain.Submit(command);
|
||||
}
|
||||
status = swapchain.Present();
|
||||
@@ -827,6 +869,7 @@ void Presenter::Present(Frame& frame, bool reuse) {
|
||||
}
|
||||
|
||||
RenderDocOnPresent();
|
||||
m_impl->presented_ime_revision.store(ime_visual.revision, std::memory_order_release);
|
||||
window.UpdateTitle();
|
||||
m_impl->frames.Release(&frame, true);
|
||||
return;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "graphics/host_gpu/renderer/renderContext.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/imeDialogOverlay.h"
|
||||
#include "graphics/presentation/presenter.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/videoOut.h"
|
||||
@@ -1008,6 +1009,7 @@ void WindowContext::RecreateSurface() {
|
||||
}
|
||||
|
||||
WindowContext::~WindowContext() {
|
||||
ShutdownImeDialogInput();
|
||||
presenter.reset();
|
||||
LibKernel::Memory::InstallGpuResources(nullptr);
|
||||
render_context.reset();
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "graphics/host_gpu/renderer/render.h"
|
||||
#include "graphics/host_gpu/vma.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/presentation/imeDialogOverlay.h"
|
||||
#include "graphics/presentation/renderDoc.h"
|
||||
#include "graphics/presentation/window/hostInput.h"
|
||||
#include "graphics/presentation/window/windowInternal.h"
|
||||
@@ -476,6 +477,9 @@ void WindowContext::ProcessEvent(double time_s) {
|
||||
auto& game = loop;
|
||||
auto* event = &game.event;
|
||||
EXIT_IF(SDL_GetEventState(SDL_DISPLAYEVENT) != SDL_ENABLE);
|
||||
if (ProcessImeDialogInput(*event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_QUIT: GameEventQuit(game); break;
|
||||
@@ -787,6 +791,7 @@ static void WindowCreate(WindowContext& context) {
|
||||
EXIT("%s\n", SDL_GetError());
|
||||
}
|
||||
HostInputInit();
|
||||
InitializeImeDialogInput();
|
||||
|
||||
LOGF("WindowCreate(): width = %d, height = %d\n", width, height);
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ bool InstructionMaySplitSpirvBlock(const IR::Instruction& inst) {
|
||||
case IR::Opcode::AtomicAndU32:
|
||||
case IR::Opcode::AtomicOrU32:
|
||||
case IR::Opcode::AtomicXorU32:
|
||||
case IR::Opcode::AtomicFMinF32:
|
||||
case IR::Opcode::AtomicFMaxF32:
|
||||
case IR::Opcode::FlatLoadUbyte:
|
||||
case IR::Opcode::FlatLoadSbyte:
|
||||
case IR::Opcode::FlatLoadUshort:
|
||||
|
||||
@@ -57,6 +57,8 @@ constexpr MemoryOpcodeInfo MUBUF_OPS[] = {
|
||||
{0x39u, Opcode::BufferAtomicAnd, 1, 32},
|
||||
{0x3au, Opcode::BufferAtomicOr, 1, 32},
|
||||
{0x3bu, Opcode::BufferAtomicXor, 1, 32},
|
||||
{0x3fu, Opcode::BufferAtomicFMin, 1, 32},
|
||||
{0x40u, Opcode::BufferAtomicFMax, 1, 32},
|
||||
};
|
||||
|
||||
constexpr MemoryOpcodeInfo MTBUF_OPS[] = {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_DECOMPILER_OPCODETABLE_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_DECOMPILER_OPCODETABLE_H_
|
||||
|
||||
#include "graphics/shader/recompiler/decompiler/ShaderDecoder.h"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder::Detail {
|
||||
|
||||
struct OpcodeMap {
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::Unknown;
|
||||
};
|
||||
|
||||
template <typename Entry, size_t N>
|
||||
constexpr const Entry* FindOpcode(const Entry (&table)[N], uint32_t encoding) {
|
||||
for (const auto& entry: table) {
|
||||
if (entry.encoding == encoding) {
|
||||
return &entry;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Entry, size_t N>
|
||||
constexpr Opcode LookupOpcode(const Entry (&table)[N], uint32_t encoding) {
|
||||
const auto* entry = FindOpcode(table, encoding);
|
||||
return entry != nullptr ? entry->decoded : Opcode::Unsupported;
|
||||
}
|
||||
|
||||
template <typename Entry, size_t N>
|
||||
constexpr bool HasUniqueEncodings(const Entry (&table)[N]) {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
for (size_t j = i + 1; j < N; j++) {
|
||||
if (table[i].encoding == table[j].encoding) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Decoder::Detail
|
||||
|
||||
#endif // EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_DECOMPILER_OPCODETABLE_H_
|
||||
@@ -1,14 +1,11 @@
|
||||
#include "graphics/shader/recompiler/decompiler/ScalarAluOps.h"
|
||||
|
||||
#include <iterator>
|
||||
#include "graphics/shader/recompiler/decompiler/OpcodeTable.h"
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
namespace {
|
||||
|
||||
struct OpcodeMap {
|
||||
uint32_t opcode = 0;
|
||||
Opcode ir = Opcode::Unknown;
|
||||
};
|
||||
using Detail::OpcodeMap;
|
||||
|
||||
constexpr OpcodeMap SOP2_OPS[] = {
|
||||
{0x00u, Opcode::SAddU32}, {0x01u, Opcode::SSubU32}, {0x02u, Opcode::SAddI32},
|
||||
@@ -58,7 +55,8 @@ constexpr OpcodeMap SOPC_OPS[] = {
|
||||
{0x03u, Opcode::SCmpGeI32}, {0x04u, Opcode::SCmpLtI32}, {0x05u, Opcode::SCmpLeI32},
|
||||
{0x06u, Opcode::SCmpEqU32}, {0x07u, Opcode::SCmpLgU32}, {0x08u, Opcode::SCmpGtU32},
|
||||
{0x09u, Opcode::SCmpGeU32}, {0x0au, Opcode::SCmpLtU32}, {0x0bu, Opcode::SCmpLeU32},
|
||||
{0x0cu, Opcode::SBitcmp0B32}, {0x0du, Opcode::SBitcmp1B32}, {0x13u, Opcode::SCmpLgU64},
|
||||
{0x0cu, Opcode::SBitcmp0B32}, {0x0du, Opcode::SBitcmp1B32}, {0x12u, Opcode::SCmpEqU64},
|
||||
{0x13u, Opcode::SCmpLgU64},
|
||||
};
|
||||
|
||||
constexpr OpcodeMap SOPK_OPS[] = {
|
||||
@@ -78,15 +76,11 @@ constexpr OpcodeMap SOPP_OPS[] = {
|
||||
{0x0au, Opcode::SBarrier}, {0x0cu, Opcode::SWaitcnt}, {0x0eu, Opcode::SSleep},
|
||||
{0x10u, Opcode::SSendmsg}, {0x16u, Opcode::STtraceData}, {0x20u, Opcode::SInstPrefetch},
|
||||
};
|
||||
|
||||
Opcode Lookup(const OpcodeMap* ops, uint32_t count, uint32_t opcode) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (ops[i].opcode == opcode) {
|
||||
return ops[i].ir;
|
||||
}
|
||||
}
|
||||
return Opcode::Unsupported;
|
||||
}
|
||||
static_assert(Detail::HasUniqueEncodings(SOP1_OPS));
|
||||
static_assert(Detail::HasUniqueEncodings(SOP2_OPS));
|
||||
static_assert(Detail::HasUniqueEncodings(SOPK_OPS));
|
||||
static_assert(Detail::HasUniqueEncodings(SOPC_OPS));
|
||||
static_assert(Detail::HasUniqueEncodings(SOPP_OPS));
|
||||
|
||||
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) {
|
||||
@@ -111,7 +105,7 @@ bool DecodeSop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOP1;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOP1_OPS, static_cast<uint32_t>(std::size(SOP1_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOP1_OPS, opcode);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
if (inst.opcode == Opcode::Unsupported) {
|
||||
@@ -153,7 +147,7 @@ bool DecodeSop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOP2;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOP2_OPS, static_cast<uint32_t>(std::size(SOP2_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOP2_OPS, opcode);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
if (inst.opcode == Opcode::Unsupported) {
|
||||
@@ -180,7 +174,7 @@ bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPK;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOPK_OPS, static_cast<uint32_t>(std::size(SOPK_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOPK_OPS, opcode);
|
||||
inst.src0.kind = OperandKind::IntegerInlineConstant;
|
||||
inst.src0.signed_val = imm;
|
||||
inst.src0.value = static_cast<uint32_t>(imm);
|
||||
@@ -237,7 +231,7 @@ bool DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPC;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOPC_OPS, static_cast<uint32_t>(std::size(SOPC_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOPC_OPS, opcode);
|
||||
inst.dst.kind = OperandKind::Scc;
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
@@ -261,7 +255,7 @@ bool DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::SOPP;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(SOPP_OPS, static_cast<uint32_t>(std::size(SOPP_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(SOPP_OPS, opcode);
|
||||
inst.src0.kind = OperandKind::LiteralConstant;
|
||||
inst.src0.value = simm;
|
||||
inst.src0.signed_val = static_cast<int16_t>(simm);
|
||||
|
||||
@@ -77,6 +77,16 @@ std::string FormatBinary(const Instruction& inst) {
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string FormatSources(const Instruction& inst) {
|
||||
std::string text = fmt::format("0x{:08x}: {}", inst.pc, OpcodeToString(inst.opcode));
|
||||
const Operand* sources[] = {&inst.src0, &inst.src1, &inst.src2, &inst.src3};
|
||||
for (uint32_t i = 0; i < inst.src_count && i < 4u; i++) {
|
||||
text += i == 0 ? " " : ", ";
|
||||
text += OperandToString(*sources[i]);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string FormatMemory(const Instruction& inst) {
|
||||
std::string text = fmt::format("0x{:08x}: {} {}", inst.pc, OpcodeToString(inst.opcode).c_str(),
|
||||
OperandToString(inst.dst).c_str());
|
||||
@@ -511,6 +521,7 @@ std::string OpcodeToString(Opcode opcode) {
|
||||
case Opcode::SCmpGeU32: return "s_cmp_ge_u32";
|
||||
case Opcode::SCmpLtU32: return "s_cmp_lt_u32";
|
||||
case Opcode::SCmpLeU32: return "s_cmp_le_u32";
|
||||
case Opcode::SCmpEqU64: return "s_cmp_eq_u64";
|
||||
case Opcode::SCmpLgU64: return "s_cmp_lg_u64";
|
||||
case Opcode::VNop: return "v_nop";
|
||||
case Opcode::VMovB32: return "v_mov_b32";
|
||||
@@ -766,6 +777,7 @@ std::string OpcodeToString(Opcode opcode) {
|
||||
case Opcode::VCmpNeU32: return "v_cmp_ne_u32";
|
||||
case Opcode::VCmpGeU32: return "v_cmp_ge_u32";
|
||||
case Opcode::VCmpTU32: return "v_cmp_t_u32";
|
||||
case Opcode::VCmpEqI64: return "v_cmp_eq_i64";
|
||||
case Opcode::VCmpNeU64: return "v_cmp_ne_u64";
|
||||
case Opcode::VCmpxLtU32: return "v_cmpx_lt_u32";
|
||||
case Opcode::VCmpxEqU32: return "v_cmpx_eq_u32";
|
||||
@@ -823,6 +835,8 @@ std::string OpcodeToString(Opcode opcode) {
|
||||
case Opcode::BufferAtomicAnd: return "buffer_atomic_and";
|
||||
case Opcode::BufferAtomicOr: return "buffer_atomic_or";
|
||||
case Opcode::BufferAtomicXor: return "buffer_atomic_xor";
|
||||
case Opcode::BufferAtomicFMin: return "buffer_atomic_fmin";
|
||||
case Opcode::BufferAtomicFMax: return "buffer_atomic_fmax";
|
||||
case Opcode::FlatLoadUbyte: return "flat_load_ubyte";
|
||||
case Opcode::FlatLoadSbyte: return "flat_load_sbyte";
|
||||
case Opcode::FlatLoadUshort: return "flat_load_ushort";
|
||||
@@ -983,6 +997,9 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
inst.pc, FamilyToString(inst.family).c_str(), inst.opcode_id,
|
||||
RawWordsToString(inst).c_str(), inst.unsupported_reason.c_str());
|
||||
}
|
||||
if (inst.family == Family::SOPC) {
|
||||
return WithUnsupportedReason(inst, FormatSources(inst));
|
||||
}
|
||||
|
||||
switch (inst.opcode) {
|
||||
case Opcode::SMovB32:
|
||||
@@ -1110,6 +1127,8 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::BufferAtomicAnd:
|
||||
case Opcode::BufferAtomicOr:
|
||||
case Opcode::BufferAtomicXor:
|
||||
case Opcode::BufferAtomicFMin:
|
||||
case Opcode::BufferAtomicFMax:
|
||||
case Opcode::BufferLoadSbyte:
|
||||
case Opcode::BufferLoadSshort:
|
||||
case Opcode::FlatLoadUbyte:
|
||||
@@ -1171,25 +1190,6 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::DsWriteB128:
|
||||
case Opcode::DsWriteAddtidB32:
|
||||
case Opcode::DsReadAddtidB32: return WithUnsupportedReason(inst, FormatMemory(inst));
|
||||
case Opcode::SCmpEqU32:
|
||||
case Opcode::SCmpEqI32:
|
||||
case Opcode::SCmpLgU32:
|
||||
case Opcode::SCmpLgI32:
|
||||
case Opcode::SCmpGtU32:
|
||||
case Opcode::SCmpGtI32:
|
||||
case Opcode::SCmpGeU32:
|
||||
case Opcode::SCmpGeI32:
|
||||
case Opcode::SCmpLtU32:
|
||||
case Opcode::SCmpLtI32:
|
||||
case Opcode::SCmpLeU32:
|
||||
case Opcode::SCmpLeI32:
|
||||
case Opcode::SBitcmp0B32:
|
||||
case Opcode::SBitcmp1B32:
|
||||
case Opcode::SCmpLgU64:
|
||||
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: {} {}, {}", inst.pc,
|
||||
OpcodeToString(inst.opcode).c_str(),
|
||||
OperandToString(inst.src0).c_str(),
|
||||
OperandToString(inst.src1).c_str()));
|
||||
default: return WithUnsupportedReason(inst, FormatBinary(inst));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ enum class Opcode {
|
||||
SCmpGeU32,
|
||||
SCmpLtU32,
|
||||
SCmpLeU32,
|
||||
SCmpEqU64,
|
||||
SCmpLgU64,
|
||||
|
||||
VNop,
|
||||
@@ -374,6 +375,7 @@ enum class Opcode {
|
||||
VCmpNeU32,
|
||||
VCmpGeU32,
|
||||
VCmpTU32,
|
||||
VCmpEqI64,
|
||||
VCmpNeU64,
|
||||
VCmpxLtU32,
|
||||
VCmpxEqU32,
|
||||
@@ -432,6 +434,8 @@ enum class Opcode {
|
||||
BufferAtomicAnd,
|
||||
BufferAtomicOr,
|
||||
BufferAtomicXor,
|
||||
BufferAtomicFMin,
|
||||
BufferAtomicFMax,
|
||||
FlatLoadUbyte,
|
||||
FlatLoadSbyte,
|
||||
FlatLoadUshort,
|
||||
|
||||
@@ -1,43 +1,91 @@
|
||||
#include "graphics/shader/recompiler/decompiler/VectorAluOps.h"
|
||||
|
||||
#include "graphics/shader/recompiler/decompiler/OpcodeTable.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <iterator>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
namespace {
|
||||
|
||||
struct OpcodeMap {
|
||||
uint32_t opcode = 0;
|
||||
Opcode ir = Opcode::Unknown;
|
||||
using Detail::OpcodeMap;
|
||||
|
||||
// These profiles describe the selectors that the current lowering implements. They are not
|
||||
// architectural SDWA legality classes.
|
||||
enum class Vop2SdwaProfile {
|
||||
None,
|
||||
Cndmask,
|
||||
Float32,
|
||||
Float16,
|
||||
IntegerFullDestination,
|
||||
IntegerPartialDestination,
|
||||
ReverseLogicalRight,
|
||||
ReverseLogicalLeft,
|
||||
Bitwise,
|
||||
Count,
|
||||
};
|
||||
|
||||
constexpr OpcodeMap VOP2_OPS[] = {
|
||||
{0x01u, Opcode::VCndmaskB32}, {0x02u, Opcode::VDot2cF32F16},
|
||||
{0x03u, Opcode::VAddF32}, {0x04u, Opcode::VSubF32},
|
||||
{0x05u, Opcode::VSubrevF32}, {0x08u, Opcode::VMulF32},
|
||||
{0x09u, Opcode::VMulI32I24}, {0x0bu, Opcode::VMulU32U24},
|
||||
{0x0fu, Opcode::VMinF32}, {0x10u, Opcode::VMaxF32},
|
||||
{0x11u, Opcode::VMinI32}, {0x12u, Opcode::VMaxI32},
|
||||
{0x13u, Opcode::VMinU32}, {0x14u, Opcode::VMaxU32},
|
||||
{0x15u, Opcode::VLshrB32}, {0x16u, Opcode::VLshrrevB32},
|
||||
{0x17u, Opcode::VAshrI32}, {0x18u, Opcode::VAshrrevI32},
|
||||
{0x19u, Opcode::VLshlB32}, {0x1au, Opcode::VLshlrevB32},
|
||||
{0x1bu, Opcode::VAndB32}, {0x1cu, Opcode::VOrB32},
|
||||
{0x1du, Opcode::VXorB32}, {0x1eu, Opcode::VXnorB32},
|
||||
{0x1fu, Opcode::VMacF32}, {0x20u, Opcode::VMadmkF32},
|
||||
{0x21u, Opcode::VMadakF32}, {0x22u, Opcode::VBcntU32B32},
|
||||
{0x23u, Opcode::VMbcntLoU32B32}, {0x24u, Opcode::VMbcntHiU32B32},
|
||||
{0x25u, Opcode::VAddNcU32}, {0x28u, Opcode::VAddcU32},
|
||||
{0x26u, Opcode::VSubNcU32}, {0x27u, Opcode::VSubrevNcU32},
|
||||
{0x2bu, Opcode::VMacF32}, {0x2cu, Opcode::VMadmkF32},
|
||||
{0x2du, Opcode::VMadakF32}, {0x2fu, Opcode::VCvtPkrtzF16F32},
|
||||
{0x32u, Opcode::VAddF16}, {0x33u, Opcode::VSubF16},
|
||||
{0x34u, Opcode::VSubrevF16}, {0x35u, Opcode::VMulF16},
|
||||
{0x36u, Opcode::VFmacF16}, {0x37u, Opcode::VFmamkF16},
|
||||
{0x38u, Opcode::VFmaakF16}, {0x39u, Opcode::VMaxF16},
|
||||
{0x3au, Opcode::VMinF16}, {0x3cu, Opcode::VPkFmacF16},
|
||||
struct Vop2OpcodeInfo {
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::Unknown;
|
||||
Vop2SdwaProfile sdwa_profile = Vop2SdwaProfile::None;
|
||||
};
|
||||
|
||||
constexpr Vop2OpcodeInfo VOP2_OPS[] = {
|
||||
{0x01u, Opcode::VCndmaskB32, Vop2SdwaProfile::Cndmask},
|
||||
{0x02u, Opcode::VDot2cF32F16},
|
||||
{0x03u, Opcode::VAddF32, Vop2SdwaProfile::Float32},
|
||||
{0x04u, Opcode::VSubF32, Vop2SdwaProfile::Float32},
|
||||
{0x05u, Opcode::VSubrevF32},
|
||||
{0x08u, Opcode::VMulF32, Vop2SdwaProfile::Float32},
|
||||
{0x09u, Opcode::VMulI32I24, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x0bu, Opcode::VMulU32U24, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x0fu, Opcode::VMinF32},
|
||||
{0x10u, Opcode::VMaxF32},
|
||||
{0x11u, Opcode::VMinI32},
|
||||
{0x12u, Opcode::VMaxI32},
|
||||
{0x13u, Opcode::VMinU32, Vop2SdwaProfile::IntegerPartialDestination},
|
||||
{0x14u, Opcode::VMaxU32, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x15u, Opcode::VLshrB32},
|
||||
{0x16u, Opcode::VLshrrevB32, Vop2SdwaProfile::ReverseLogicalRight},
|
||||
{0x17u, Opcode::VAshrI32},
|
||||
{0x18u, Opcode::VAshrrevI32},
|
||||
{0x19u, Opcode::VLshlB32},
|
||||
{0x1au, Opcode::VLshlrevB32, Vop2SdwaProfile::ReverseLogicalLeft},
|
||||
{0x1bu, Opcode::VAndB32, Vop2SdwaProfile::Bitwise},
|
||||
{0x1cu, Opcode::VOrB32, Vop2SdwaProfile::Bitwise},
|
||||
{0x1du, Opcode::VXorB32, Vop2SdwaProfile::Bitwise},
|
||||
{0x1eu, Opcode::VXnorB32, Vop2SdwaProfile::Bitwise},
|
||||
{0x1fu, Opcode::VMacF32},
|
||||
{0x20u, Opcode::VMadmkF32},
|
||||
{0x21u, Opcode::VMadakF32},
|
||||
{0x22u, Opcode::VBcntU32B32},
|
||||
{0x23u, Opcode::VMbcntLoU32B32},
|
||||
{0x24u, Opcode::VMbcntHiU32B32},
|
||||
{0x25u, Opcode::VAddNcU32, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x28u, Opcode::VAddcU32},
|
||||
{0x26u, Opcode::VSubNcU32, Vop2SdwaProfile::IntegerPartialDestination},
|
||||
{0x27u, Opcode::VSubrevNcU32, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x2bu, Opcode::VMacF32},
|
||||
{0x2cu, Opcode::VMadmkF32},
|
||||
{0x2du, Opcode::VMadakF32},
|
||||
{0x2fu, Opcode::VCvtPkrtzF16F32},
|
||||
{0x32u, Opcode::VAddF16, Vop2SdwaProfile::Float16},
|
||||
{0x33u, Opcode::VSubF16, Vop2SdwaProfile::Float16},
|
||||
{0x34u, Opcode::VSubrevF16, Vop2SdwaProfile::Float16},
|
||||
{0x35u, Opcode::VMulF16, Vop2SdwaProfile::Float16},
|
||||
{0x36u, Opcode::VFmacF16},
|
||||
{0x37u, Opcode::VFmamkF16},
|
||||
{0x38u, Opcode::VFmaakF16},
|
||||
{0x39u, Opcode::VMaxF16, Vop2SdwaProfile::Float16},
|
||||
{0x3au, Opcode::VMinF16, Vop2SdwaProfile::Float16},
|
||||
{0x3cu, Opcode::VPkFmacF16},
|
||||
};
|
||||
static_assert(Detail::HasUniqueEncodings(VOP2_OPS));
|
||||
|
||||
constexpr Opcode LookupVop2Opcode(uint32_t encoding) {
|
||||
return Detail::LookupOpcode(VOP2_OPS, encoding);
|
||||
}
|
||||
|
||||
constexpr OpcodeMap VOP1_OPS[] = {
|
||||
{0x00u, Opcode::VNop},
|
||||
{0x01u, Opcode::VMovB32},
|
||||
@@ -135,37 +183,59 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPS[] = {
|
||||
{0x5eu, Opcode::VRndneF16},
|
||||
};
|
||||
|
||||
constexpr OpcodeMap VOPC_OPS[] = {
|
||||
{0x00u, Opcode::VCmpFF32}, {0x01u, Opcode::VCmpLtF32}, {0x02u, Opcode::VCmpEqF32},
|
||||
{0x03u, Opcode::VCmpLeF32}, {0x04u, Opcode::VCmpGtF32}, {0x05u, Opcode::VCmpLgF32},
|
||||
{0x06u, Opcode::VCmpGeF32}, {0x07u, Opcode::VCmpOF32}, {0x08u, Opcode::VCmpUF32},
|
||||
{0x09u, Opcode::VCmpNgeF32}, {0x0au, Opcode::VCmpNlgF32}, {0x0bu, Opcode::VCmpNgtF32},
|
||||
{0x0cu, Opcode::VCmpNleF32}, {0x0du, Opcode::VCmpNeqF32}, {0x0eu, Opcode::VCmpNltF32},
|
||||
{0x0fu, Opcode::VCmpTruF32}, {0x11u, Opcode::VCmpxLtF32}, {0x12u, Opcode::VCmpxEqF32},
|
||||
{0x13u, Opcode::VCmpxLeF32}, {0x14u, Opcode::VCmpxGtF32}, {0x15u, Opcode::VCmpxLgF32},
|
||||
{0x16u, Opcode::VCmpxGeF32}, {0x19u, Opcode::VCmpxNgeF32}, {0x1au, Opcode::VCmpxNlgF32},
|
||||
{0x1bu, Opcode::VCmpxNgtF32}, {0x1cu, Opcode::VCmpxNleF32}, {0x1du, Opcode::VCmpxNeqF32},
|
||||
{0x1eu, Opcode::VCmpxNltF32}, {0x80u, Opcode::VCmpFI32}, {0x81u, Opcode::VCmpLtI32},
|
||||
{0x82u, Opcode::VCmpEqI32}, {0x83u, Opcode::VCmpLeI32}, {0x84u, Opcode::VCmpGtI32},
|
||||
{0x85u, Opcode::VCmpNeI32}, {0x86u, Opcode::VCmpGeI32}, {0x87u, Opcode::VCmpTI32},
|
||||
{0x88u, Opcode::VCmpClassF32}, {0x89u, Opcode::VCmpLtI16}, {0x8au, Opcode::VCmpEqI16},
|
||||
{0x8bu, Opcode::VCmpLeI16}, {0x8cu, Opcode::VCmpGtI16}, {0x8du, Opcode::VCmpNeI16},
|
||||
{0x8eu, Opcode::VCmpGeI16}, {0x91u, Opcode::VCmpxLtI32}, {0x92u, Opcode::VCmpxEqI32},
|
||||
{0x93u, Opcode::VCmpxLeI32}, {0x94u, Opcode::VCmpxGtI32}, {0x95u, Opcode::VCmpxNeI32},
|
||||
{0x96u, Opcode::VCmpxGeI32}, {0xa9u, Opcode::VCmpLtU16}, {0xaau, Opcode::VCmpEqU16},
|
||||
{0xabu, Opcode::VCmpLeU16}, {0xacu, Opcode::VCmpGtU16}, {0xadu, Opcode::VCmpNeU16},
|
||||
{0xaeu, Opcode::VCmpGeU16}, {0xc0u, Opcode::VCmpFU32}, {0xc1u, Opcode::VCmpLtU32},
|
||||
{0xc2u, Opcode::VCmpEqU32}, {0xc3u, Opcode::VCmpLeU32}, {0xc4u, Opcode::VCmpGtU32},
|
||||
{0xc5u, Opcode::VCmpNeU32}, {0xc6u, Opcode::VCmpGeU32}, {0xc7u, Opcode::VCmpTU32},
|
||||
{0xd1u, Opcode::VCmpxLtU32}, {0xd2u, Opcode::VCmpxEqU32}, {0xd3u, Opcode::VCmpxLeU32},
|
||||
{0xd4u, Opcode::VCmpxGtU32}, {0xd5u, Opcode::VCmpxNeU32}, {0xd6u, Opcode::VCmpxGeU32},
|
||||
{0xe5u, Opcode::VCmpNeU64}, {0xc9u, Opcode::VCmpLtF16}, {0xcau, Opcode::VCmpEqF16},
|
||||
{0xcbu, Opcode::VCmpLeF16}, {0xccu, Opcode::VCmpGtF16}, {0xcdu, Opcode::VCmpLgF16},
|
||||
{0xceu, Opcode::VCmpGeF16}, {0xedu, Opcode::VCmpNeqF16}, {0xd9u, Opcode::VCmpxLtF16},
|
||||
{0xdau, Opcode::VCmpxEqF16}, {0xdbu, Opcode::VCmpxLeF16}, {0xdcu, Opcode::VCmpxGtF16},
|
||||
{0xdeu, Opcode::VCmpxGeF16}, {0xfdu, Opcode::VCmpxNeqF16}, {0xfeu, Opcode::VCmpxNltF16},
|
||||
struct VopcOpcodeInfo {
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::Unknown;
|
||||
bool supports_dpp = true;
|
||||
};
|
||||
|
||||
constexpr VopcOpcodeInfo VOPC_OPS[] = {
|
||||
{0x00u, Opcode::VCmpFF32}, {0x01u, Opcode::VCmpLtF32},
|
||||
{0x02u, Opcode::VCmpEqF32}, {0x03u, Opcode::VCmpLeF32},
|
||||
{0x04u, Opcode::VCmpGtF32}, {0x05u, Opcode::VCmpLgF32},
|
||||
{0x06u, Opcode::VCmpGeF32}, {0x07u, Opcode::VCmpOF32},
|
||||
{0x08u, Opcode::VCmpUF32}, {0x09u, Opcode::VCmpNgeF32},
|
||||
{0x0au, Opcode::VCmpNlgF32}, {0x0bu, Opcode::VCmpNgtF32},
|
||||
{0x0cu, Opcode::VCmpNleF32}, {0x0du, Opcode::VCmpNeqF32},
|
||||
{0x0eu, Opcode::VCmpNltF32}, {0x0fu, Opcode::VCmpTruF32},
|
||||
{0x11u, Opcode::VCmpxLtF32}, {0x12u, Opcode::VCmpxEqF32},
|
||||
{0x13u, Opcode::VCmpxLeF32}, {0x14u, Opcode::VCmpxGtF32},
|
||||
{0x15u, Opcode::VCmpxLgF32}, {0x16u, Opcode::VCmpxGeF32},
|
||||
{0x19u, Opcode::VCmpxNgeF32}, {0x1au, Opcode::VCmpxNlgF32},
|
||||
{0x1bu, Opcode::VCmpxNgtF32}, {0x1cu, Opcode::VCmpxNleF32},
|
||||
{0x1du, Opcode::VCmpxNeqF32}, {0x1eu, Opcode::VCmpxNltF32},
|
||||
{0x80u, Opcode::VCmpFI32}, {0x81u, Opcode::VCmpLtI32},
|
||||
{0x82u, Opcode::VCmpEqI32}, {0x83u, Opcode::VCmpLeI32},
|
||||
{0x84u, Opcode::VCmpGtI32}, {0x85u, Opcode::VCmpNeI32},
|
||||
{0x86u, Opcode::VCmpGeI32}, {0x87u, Opcode::VCmpTI32},
|
||||
{0x88u, Opcode::VCmpClassF32}, {0x89u, Opcode::VCmpLtI16},
|
||||
{0x8au, Opcode::VCmpEqI16}, {0x8bu, Opcode::VCmpLeI16},
|
||||
{0x8cu, Opcode::VCmpGtI16}, {0x8du, Opcode::VCmpNeI16},
|
||||
{0x8eu, Opcode::VCmpGeI16}, {0x91u, Opcode::VCmpxLtI32},
|
||||
{0x92u, Opcode::VCmpxEqI32}, {0x93u, Opcode::VCmpxLeI32},
|
||||
{0x94u, Opcode::VCmpxGtI32}, {0x95u, Opcode::VCmpxNeI32},
|
||||
{0x96u, Opcode::VCmpxGeI32}, {0xa9u, Opcode::VCmpLtU16},
|
||||
{0xaau, Opcode::VCmpEqU16}, {0xabu, Opcode::VCmpLeU16},
|
||||
{0xacu, Opcode::VCmpGtU16}, {0xadu, Opcode::VCmpNeU16},
|
||||
{0xaeu, Opcode::VCmpGeU16}, {0xc0u, Opcode::VCmpFU32},
|
||||
{0xc1u, Opcode::VCmpLtU32}, {0xc2u, Opcode::VCmpEqU32},
|
||||
{0xc3u, Opcode::VCmpLeU32}, {0xc4u, Opcode::VCmpGtU32},
|
||||
{0xc5u, Opcode::VCmpNeU32}, {0xc6u, Opcode::VCmpGeU32},
|
||||
{0xc7u, Opcode::VCmpTU32}, {0xa2u, Opcode::VCmpEqI64, false},
|
||||
{0xd1u, Opcode::VCmpxLtU32}, {0xd2u, Opcode::VCmpxEqU32},
|
||||
{0xd3u, Opcode::VCmpxLeU32}, {0xd4u, Opcode::VCmpxGtU32},
|
||||
{0xd5u, Opcode::VCmpxNeU32}, {0xd6u, Opcode::VCmpxGeU32},
|
||||
{0xe5u, Opcode::VCmpNeU64, false}, {0xc9u, Opcode::VCmpLtF16},
|
||||
{0xcau, Opcode::VCmpEqF16}, {0xcbu, Opcode::VCmpLeF16},
|
||||
{0xccu, Opcode::VCmpGtF16}, {0xcdu, Opcode::VCmpLgF16},
|
||||
{0xceu, Opcode::VCmpGeF16}, {0xedu, Opcode::VCmpNeqF16},
|
||||
{0xd9u, Opcode::VCmpxLtF16}, {0xdau, Opcode::VCmpxEqF16},
|
||||
{0xdbu, Opcode::VCmpxLeF16}, {0xdcu, Opcode::VCmpxGtF16},
|
||||
{0xdeu, Opcode::VCmpxGeF16}, {0xfdu, Opcode::VCmpxNeqF16},
|
||||
{0xfeu, Opcode::VCmpxNltF16},
|
||||
};
|
||||
static_assert(Detail::HasUniqueEncodings(VOPC_OPS));
|
||||
|
||||
constexpr OpcodeMap VOP3_OPS[] = {
|
||||
{0x141u, Opcode::VMadF32}, {0x142u, Opcode::VMadI32I24},
|
||||
{0x143u, Opcode::VMadU32U24}, {0x176u, Opcode::VMadU64U32},
|
||||
@@ -214,16 +284,6 @@ constexpr OpcodeMap VOP3P_OPS[] = {
|
||||
{0x22u, Opcode::VMadMixhiF16},
|
||||
};
|
||||
|
||||
Opcode Lookup(const OpcodeMap* ops, uint32_t count, uint32_t opcode) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const auto& op = ops[i];
|
||||
if (op.opcode == opcode) {
|
||||
return op.ir;
|
||||
}
|
||||
}
|
||||
return Opcode::Unsupported;
|
||||
}
|
||||
|
||||
bool IsVop2LiteralMadOpcode(uint32_t opcode) {
|
||||
return opcode == 0x20u || opcode == 0x21u || opcode == 0x2cu || opcode == 0x2du;
|
||||
}
|
||||
@@ -234,24 +294,23 @@ bool IsUnsupportedVop3EncodedVop2Alias(uint32_t opcode) {
|
||||
}
|
||||
|
||||
Opcode LookupVop3Opcode(uint32_t opcode) {
|
||||
const auto direct = Lookup(VOP3_OPS, static_cast<uint32_t>(std::size(VOP3_OPS)), opcode);
|
||||
const auto direct = Detail::LookupOpcode(VOP3_OPS, opcode);
|
||||
if (direct != Opcode::Unsupported) {
|
||||
return direct;
|
||||
}
|
||||
if (opcode <= 0xffu) {
|
||||
return Lookup(VOPC_OPS, static_cast<uint32_t>(std::size(VOPC_OPS)), opcode);
|
||||
return Detail::LookupOpcode(VOPC_OPS, opcode);
|
||||
}
|
||||
if (opcode >= 0x100u && opcode <= 0x13fu) {
|
||||
if (IsUnsupportedVop3EncodedVop2Alias(opcode - 0x100u)) {
|
||||
return Opcode::Unsupported;
|
||||
}
|
||||
return Lookup(VOP2_OPS, static_cast<uint32_t>(std::size(VOP2_OPS)), opcode - 0x100u);
|
||||
return LookupVop2Opcode(opcode - 0x100u);
|
||||
}
|
||||
if (opcode >= 0x180u && opcode <= 0x1ffu) {
|
||||
return Lookup(VOP3_ENCODED_VOP1_OPS,
|
||||
static_cast<uint32_t>(std::size(VOP3_ENCODED_VOP1_OPS)), opcode - 0x180u);
|
||||
return Detail::LookupOpcode(VOP3_ENCODED_VOP1_OPS, opcode - 0x180u);
|
||||
}
|
||||
return Lookup(VOP3_OPS, static_cast<uint32_t>(std::size(VOP3_OPS)), opcode);
|
||||
return Detail::LookupOpcode(VOP3_OPS, opcode);
|
||||
}
|
||||
|
||||
bool IsVop3EncodedVopc(uint32_t opcode) {
|
||||
@@ -791,7 +850,6 @@ Vop2SdwaFields DecodeVop2SdwaFields(uint32_t modifier) {
|
||||
}
|
||||
|
||||
struct Vop2SdwaRule {
|
||||
Opcode opcode = Opcode::Unknown;
|
||||
uint32_t dst_selectors = SdwaSelFull();
|
||||
uint32_t src0_selectors = SdwaSelFull();
|
||||
uint32_t src1_selectors = SdwaSelFull();
|
||||
@@ -804,46 +862,27 @@ constexpr uint32_t SdwaSelAll() {
|
||||
}
|
||||
|
||||
constexpr Vop2SdwaRule VOP2_SDWA_RULES[] = {
|
||||
{Opcode::VCndmaskB32, SdwaSelWords(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, false},
|
||||
{Opcode::VAddF32, SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true},
|
||||
{Opcode::VSubF32, SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true},
|
||||
{Opcode::VMulF32, SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true},
|
||||
{Opcode::VAddF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VSubF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VSubrevF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VMulF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VMaxF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VMinF16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
SdwaSelWords() | SdwaSelFull(), true, true},
|
||||
{Opcode::VMulI32I24, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VMulU32U24, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VMinU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VMaxU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VLshrrevB32, SdwaSelFull(), SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), false,
|
||||
false},
|
||||
{Opcode::VLshlrevB32, SdwaSelFull(), SdwaSelFull(), SdwaSelAll(), false, false},
|
||||
{Opcode::VAndB32, SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{Opcode::VOrB32, SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{Opcode::VXorB32, SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{Opcode::VXnorB32, SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{Opcode::VAddNcU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VSubNcU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{Opcode::VSubrevNcU32, SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{},
|
||||
{SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
true, false},
|
||||
{SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true},
|
||||
{SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
true, true},
|
||||
{SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{SdwaSelFull(), SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), false, false},
|
||||
{SdwaSelFull(), SdwaSelFull(), SdwaSelAll(), false, false},
|
||||
{SdwaSelWords() | SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
};
|
||||
static_assert(sizeof(VOP2_SDWA_RULES) / sizeof(VOP2_SDWA_RULES[0]) ==
|
||||
static_cast<size_t>(Vop2SdwaProfile::Count));
|
||||
|
||||
const Vop2SdwaRule* FindVop2SdwaRule(Opcode opcode) {
|
||||
for (const auto& rule: VOP2_SDWA_RULES) {
|
||||
if (rule.opcode == opcode) {
|
||||
return &rule;
|
||||
}
|
||||
const Vop2SdwaRule* FindVop2SdwaRule(uint32_t encoding) {
|
||||
const auto* info = Detail::FindOpcode(VOP2_OPS, encoding);
|
||||
if (info == nullptr || info->sdwa_profile == Vop2SdwaProfile::None) {
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
return &VOP2_SDWA_RULES[static_cast<size_t>(info->sdwa_profile)];
|
||||
}
|
||||
|
||||
bool IsVop2SdwaDestinationSupported(const Vop2SdwaRule& rule, const Vop2SdwaFields& fields) {
|
||||
@@ -881,7 +920,7 @@ bool ValidateVop2Sdwa(Instruction& inst, uint32_t opcode, const Vop2SdwaFields&
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto* rule = FindVop2SdwaRule(inst.opcode);
|
||||
const auto* rule = FindVop2SdwaRule(opcode);
|
||||
if (rule == nullptr) {
|
||||
SetUnsupported(inst, Family::VOP2, opcode,
|
||||
"VOP2 SDWA modifier is not supported for opcode");
|
||||
@@ -1135,6 +1174,11 @@ bool DecodeVopcDpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
SetUnsupported(inst, Family::VOPC, opcode, "VOPC opcode is not implemented");
|
||||
return true;
|
||||
}
|
||||
const auto* info = Detail::FindOpcode(VOPC_OPS, opcode);
|
||||
if (info == nullptr || !info->supports_dpp) {
|
||||
SetUnsupported(inst, Family::VOPC, opcode, "VOPC DPP modifier is not supported for opcode");
|
||||
return true;
|
||||
}
|
||||
if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error) ||
|
||||
!DecodeVectorGpr(vsrc1, inst.src1, error)) {
|
||||
return false;
|
||||
@@ -1427,7 +1471,7 @@ bool DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::VOP2;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(VOP2_OPS, static_cast<uint32_t>(std::size(VOP2_OPS)), opcode);
|
||||
inst.opcode = LookupVop2Opcode(opcode);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
switch (opcode) {
|
||||
@@ -1468,7 +1512,7 @@ bool DecodeVop1(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::VOP1;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(VOP1_OPS, static_cast<uint32_t>(std::size(VOP1_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(VOP1_OPS, opcode);
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
if (inst.opcode == Opcode::Unsupported) {
|
||||
@@ -1509,7 +1553,7 @@ bool DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.word = word;
|
||||
inst.family = Family::VOPC;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(VOPC_OPS, static_cast<uint32_t>(std::size(VOPC_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(VOPC_OPS, opcode);
|
||||
inst.dst.kind = IsVopcCompareExec(inst.opcode) ? OperandKind::ExecLo : OperandKind::VccLo;
|
||||
SetRawWords(inst, code, word_index, 1);
|
||||
|
||||
@@ -1723,7 +1767,7 @@ bool DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
inst.word = word0;
|
||||
inst.family = Family::VOP3P;
|
||||
inst.opcode_id = opcode;
|
||||
inst.opcode = Lookup(VOP3P_OPS, static_cast<uint32_t>(std::size(VOP3P_OPS)), opcode);
|
||||
inst.opcode = Detail::LookupOpcode(VOP3P_OPS, opcode);
|
||||
SetRawWords(inst, code, word_index, 2);
|
||||
|
||||
if (inst.opcode == Opcode::Unsupported) {
|
||||
|
||||
@@ -131,7 +131,9 @@ bool IsAtomic(IR::Opcode op) {
|
||||
case IR::Opcode::AtomicUMaxU32:
|
||||
case IR::Opcode::AtomicAndU32:
|
||||
case IR::Opcode::AtomicOrU32:
|
||||
case IR::Opcode::AtomicXorU32: return true;
|
||||
case IR::Opcode::AtomicXorU32:
|
||||
case IR::Opcode::AtomicFMinF32:
|
||||
case IR::Opcode::AtomicFMaxF32: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -141,6 +143,9 @@ uint32_t BufferAddressOperandCount(const IR::Instruction& inst) {
|
||||
}
|
||||
|
||||
bool ValidateInstructionContract(const IR::Instruction& inst, std::string* error) {
|
||||
if (static_cast<size_t>(inst.op) >= static_cast<size_t>(IR::Opcode::Count)) {
|
||||
return Fail(error, "IR instruction opcode is out of range");
|
||||
}
|
||||
if (inst.src_count > std::size(inst.src)) {
|
||||
return Fail(error, "instruction source count exceeds fixed IR storage");
|
||||
}
|
||||
@@ -200,6 +205,12 @@ bool ValidateInstructionContract(const IR::Instruction& inst, std::string* error
|
||||
inst.src_count != 2)) {
|
||||
return Fail(error, "storage image instruction has an invalid resource class");
|
||||
}
|
||||
const auto float_atomic =
|
||||
inst.op == IR::Opcode::AtomicFMinF32 || inst.op == IR::Opcode::AtomicFMaxF32;
|
||||
if (float_atomic &&
|
||||
(kind != IR::ResourceKind::Buffer || inst.src_count != buffer_address_count + 1u)) {
|
||||
return Fail(error, "floating-point atomic instruction must target a buffer");
|
||||
}
|
||||
if (IsAtomic(inst.op) &&
|
||||
!((kind == IR::ResourceKind::Buffer && inst.src_count == buffer_address_count + 1u) ||
|
||||
((kind == IR::ResourceKind::Lds || kind == IR::ResourceKind::Gds) &&
|
||||
@@ -389,7 +400,7 @@ bool ProgramRequiresExactSubgroupSize(const IR::Program& program) {
|
||||
if (Emitter::InstructionHasDppSource(inst)) {
|
||||
return true;
|
||||
}
|
||||
if (Emitter::IsCompareOpcode(inst.op) && !Emitter::IsSccOperand(inst.dst)) {
|
||||
if (IR::IsCompareOpcode(inst.op) && !Emitter::IsSccOperand(inst.dst)) {
|
||||
return true;
|
||||
}
|
||||
if ((IsExecDestination(inst.dst) || IsExecDestination(inst.dst2)) &&
|
||||
@@ -488,6 +499,13 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
||||
AllocateBlockLabels(state, program);
|
||||
AllocateDispatcherState(state, program);
|
||||
EmitFunction(state, program);
|
||||
if (state.unsupported_ir_instruction) {
|
||||
char pc[9] {};
|
||||
std::snprintf(pc, sizeof(pc), "%08x", state.unsupported_ir_pc);
|
||||
return Fail(error, "SPIR-V emitter has no handler for IR opcode " +
|
||||
std::string(IR::OpcodeName(state.unsupported_ir_opcode)) +
|
||||
" at pc 0x" + pc);
|
||||
}
|
||||
|
||||
ReportReserveExceeded(program, "registers", state.registers.size());
|
||||
ReportReserveExceeded(program, "inputs", state.inputs.size());
|
||||
|
||||
@@ -1486,17 +1486,20 @@ void EmitBitCompareB32(EmitterState& state, const IR::Instruction& inst, bool bi
|
||||
EmitStoreU32(state, inst.dst, ret);
|
||||
}
|
||||
|
||||
void EmitCompareNeU64(EmitterState& state, const IR::Instruction& inst) {
|
||||
const auto lhs_low = EmitSequentialValueLoad(state, inst.src[0], 0);
|
||||
const auto lhs_high = EmitSequentialValueLoad(state, inst.src[0], 1);
|
||||
const auto rhs_low = EmitSequentialValueLoad(state, inst.src[1], 0);
|
||||
const auto rhs_high = EmitSequentialValueLoad(state, inst.src[1], 1);
|
||||
const auto ne_low = state.builder.AllocateId();
|
||||
const auto ne_high = state.builder.AllocateId();
|
||||
const auto cond = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpINotEqual, state.bool_type, ne_low, lhs_low, rhs_low});
|
||||
state.builder.AddFunction({OpINotEqual, state.bool_type, ne_high, lhs_high, rhs_high});
|
||||
state.builder.AddFunction({OpLogicalOr, state.bool_type, cond, ne_low, ne_high});
|
||||
void EmitCompareU64(EmitterState& state, const IR::Instruction& inst) {
|
||||
const bool equal = inst.op == IR::Opcode::CompareEqU64;
|
||||
const auto lhs_low = EmitSequentialValueLoad(state, inst.src[0], 0);
|
||||
const auto lhs_high = EmitSequentialValueLoad(state, inst.src[0], 1);
|
||||
const auto rhs_low = EmitSequentialValueLoad(state, inst.src[1], 0);
|
||||
const auto rhs_high = EmitSequentialValueLoad(state, inst.src[1], 1);
|
||||
const auto low = state.builder.AllocateId();
|
||||
const auto high = state.builder.AllocateId();
|
||||
const auto cond = state.builder.AllocateId();
|
||||
const auto compare_op = equal ? OpIEqual : OpINotEqual;
|
||||
state.builder.AddFunction({compare_op, state.bool_type, low, lhs_low, rhs_low});
|
||||
state.builder.AddFunction({compare_op, state.bool_type, high, lhs_high, rhs_high});
|
||||
state.builder.AddFunction(
|
||||
{equal ? OpLogicalAnd : OpLogicalOr, state.bool_type, cond, low, high});
|
||||
EmitCompareResult(state, inst.dst, cond);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,6 @@ bool IsSccOperand(const IR::Operand& operand) {
|
||||
return operand.kind == IR::OperandKind::Register && operand.reg.file == IR::RegisterFile::Scc;
|
||||
}
|
||||
|
||||
bool IsCompareOpcode(IR::Opcode op);
|
||||
|
||||
void CollectMaskStateRegisters(std::vector<RegisterBinding>& registers) {
|
||||
CollectRegister(registers, {IR::RegisterFile::Exec, 0});
|
||||
CollectRegister(registers, {IR::RegisterFile::Exec, 1});
|
||||
@@ -189,11 +187,11 @@ void CollectRegisters(const IR::Program& program, std::vector<RegisterBinding>&
|
||||
if (inst.op == IR::Opcode::BitCountU64 || inst.op == IR::Opcode::FindMsbFromHighU64) {
|
||||
CollectSequentialRegisters(registers, inst.src[0], 2);
|
||||
}
|
||||
if (inst.op == IR::Opcode::CompareNeU64) {
|
||||
if (IR::IsCompare64Opcode(inst.op)) {
|
||||
CollectSequentialRegisters(registers, inst.src[0], 2);
|
||||
CollectSequentialRegisters(registers, inst.src[1], 2);
|
||||
}
|
||||
if (IsCompareOpcode(inst.op) && inst.dst.kind == IR::OperandKind::Register &&
|
||||
if (IR::IsCompareOpcode(inst.op) && inst.dst.kind == IR::OperandKind::Register &&
|
||||
inst.dst.reg.file != IR::RegisterFile::Scc) {
|
||||
CollectSequentialRegisters(registers, inst.dst, 2);
|
||||
}
|
||||
@@ -384,93 +382,6 @@ bool ProgramNeedsSubgroupShuffle(const IR::Program& program) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsCompareOpcode(IR::Opcode op) {
|
||||
switch (op) {
|
||||
case IR::Opcode::CompareFalse:
|
||||
case IR::Opcode::CompareTrue:
|
||||
case IR::Opcode::CompareEqU32:
|
||||
case IR::Opcode::CompareNeU32:
|
||||
case IR::Opcode::CompareGtU32:
|
||||
case IR::Opcode::CompareGeU32:
|
||||
case IR::Opcode::CompareLtU32:
|
||||
case IR::Opcode::CompareLeU32:
|
||||
case IR::Opcode::CompareNeU64:
|
||||
case IR::Opcode::CompareMaskEqU32:
|
||||
case IR::Opcode::CompareMaskNeU32:
|
||||
case IR::Opcode::CompareMaskGtU32:
|
||||
case IR::Opcode::CompareMaskGeU32:
|
||||
case IR::Opcode::CompareMaskLtU32:
|
||||
case IR::Opcode::CompareMaskLeU32:
|
||||
case IR::Opcode::CompareEqI32:
|
||||
case IR::Opcode::CompareNeI32:
|
||||
case IR::Opcode::CompareGtI32:
|
||||
case IR::Opcode::CompareGeI32:
|
||||
case IR::Opcode::CompareLtI32:
|
||||
case IR::Opcode::CompareLeI32:
|
||||
case IR::Opcode::CompareEqI16:
|
||||
case IR::Opcode::CompareNeI16:
|
||||
case IR::Opcode::CompareGtI16:
|
||||
case IR::Opcode::CompareGeI16:
|
||||
case IR::Opcode::CompareLtI16:
|
||||
case IR::Opcode::CompareLeI16:
|
||||
case IR::Opcode::CompareMaskEqI32:
|
||||
case IR::Opcode::CompareMaskNeI32:
|
||||
case IR::Opcode::CompareMaskGtI32:
|
||||
case IR::Opcode::CompareMaskGeI32:
|
||||
case IR::Opcode::CompareMaskLtI32:
|
||||
case IR::Opcode::CompareMaskLeI32:
|
||||
case IR::Opcode::CompareEqU16:
|
||||
case IR::Opcode::CompareNeU16:
|
||||
case IR::Opcode::CompareGtU16:
|
||||
case IR::Opcode::CompareGeU16:
|
||||
case IR::Opcode::CompareLtU16:
|
||||
case IR::Opcode::CompareLeU16:
|
||||
case IR::Opcode::CompareEqF32:
|
||||
case IR::Opcode::CompareNeF32:
|
||||
case IR::Opcode::CompareGtF32:
|
||||
case IR::Opcode::CompareGeF32:
|
||||
case IR::Opcode::CompareLtF32:
|
||||
case IR::Opcode::CompareLeF32:
|
||||
case IR::Opcode::CompareOrderedF32:
|
||||
case IR::Opcode::CompareUnorderedF32:
|
||||
case IR::Opcode::CompareUnordEqF32:
|
||||
case IR::Opcode::CompareUnordNeF32:
|
||||
case IR::Opcode::CompareUnordGtF32:
|
||||
case IR::Opcode::CompareUnordGeF32:
|
||||
case IR::Opcode::CompareUnordLtF32:
|
||||
case IR::Opcode::CompareUnordLeF32:
|
||||
case IR::Opcode::CompareClassF32:
|
||||
case IR::Opcode::CompareEqF16:
|
||||
case IR::Opcode::CompareNeF16:
|
||||
case IR::Opcode::CompareGtF16:
|
||||
case IR::Opcode::CompareGeF16:
|
||||
case IR::Opcode::CompareLtF16:
|
||||
case IR::Opcode::CompareLeF16:
|
||||
case IR::Opcode::CompareUnordNeF16:
|
||||
case IR::Opcode::CompareMaskEqF16:
|
||||
case IR::Opcode::CompareMaskNeF16:
|
||||
case IR::Opcode::CompareMaskGtF16:
|
||||
case IR::Opcode::CompareMaskGeF16:
|
||||
case IR::Opcode::CompareMaskLtF16:
|
||||
case IR::Opcode::CompareMaskLeF16:
|
||||
case IR::Opcode::CompareMaskUnordNeF16:
|
||||
case IR::Opcode::CompareMaskUnordGeF16:
|
||||
case IR::Opcode::CompareMaskEqF32:
|
||||
case IR::Opcode::CompareMaskNeF32:
|
||||
case IR::Opcode::CompareMaskGtF32:
|
||||
case IR::Opcode::CompareMaskGeF32:
|
||||
case IR::Opcode::CompareMaskLtF32:
|
||||
case IR::Opcode::CompareMaskLeF32:
|
||||
case IR::Opcode::CompareMaskUnordEqF32:
|
||||
case IR::Opcode::CompareMaskUnordNeF32:
|
||||
case IR::Opcode::CompareMaskUnordGtF32:
|
||||
case IR::Opcode::CompareMaskUnordGeF32:
|
||||
case IR::Opcode::CompareMaskUnordLtF32:
|
||||
case IR::Opcode::CompareMaskUnordLeF32: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProgramNeedsSubgroupLocalInvocationId(const IR::Program& program) {
|
||||
for (const auto& block: program.blocks) {
|
||||
for (const auto& inst: block.instructions) {
|
||||
@@ -481,7 +392,7 @@ bool ProgramNeedsSubgroupLocalInvocationId(const IR::Program& program) {
|
||||
InstructionHasDppSource(inst)) {
|
||||
return true;
|
||||
}
|
||||
if (IsCompareOpcode(inst.op)) {
|
||||
if (IR::IsCompareOpcode(inst.op)) {
|
||||
return true;
|
||||
}
|
||||
if (inst.dst.kind == IR::OperandKind::Register &&
|
||||
|
||||
@@ -516,7 +516,8 @@ void EmitInstruction(EmitterState& state, const IR::Instruction& inst) {
|
||||
case IR::Opcode::CompareGeU32: EmitCompareU32(state, inst, OpUGreaterThanEqual); break;
|
||||
case IR::Opcode::CompareLtU32: EmitCompareU32(state, inst, OpULessThan); break;
|
||||
case IR::Opcode::CompareLeU32: EmitCompareU32(state, inst, OpULessThanEqual); break;
|
||||
case IR::Opcode::CompareNeU64: EmitCompareNeU64(state, inst); break;
|
||||
case IR::Opcode::CompareEqU64:
|
||||
case IR::Opcode::CompareNeU64: EmitCompareU64(state, inst); break;
|
||||
case IR::Opcode::CompareMaskEqU32: EmitCompareMaskU32(state, inst, OpIEqual); break;
|
||||
case IR::Opcode::CompareMaskNeU32: EmitCompareMaskU32(state, inst, OpINotEqual); break;
|
||||
case IR::Opcode::CompareMaskGtU32: EmitCompareMaskU32(state, inst, OpUGreaterThan); break;
|
||||
@@ -774,6 +775,12 @@ void EmitInstruction(EmitterState& state, const IR::Instruction& inst) {
|
||||
case IR::Opcode::AtomicXorU32:
|
||||
EmitGuardedByExec(state, [&]() { EmitAtomicU32(state, inst, OpAtomicXor); });
|
||||
break;
|
||||
case IR::Opcode::AtomicFMinF32:
|
||||
EmitGuardedByExec(state, [&]() { EmitAtomicFMinF32(state, inst); });
|
||||
break;
|
||||
case IR::Opcode::AtomicFMaxF32:
|
||||
EmitGuardedByExec(state, [&]() { EmitAtomicFMaxF32(state, inst); });
|
||||
break;
|
||||
case IR::Opcode::FlatLoadUbyte: EmitFlatLoadUbyte(state, inst); break;
|
||||
case IR::Opcode::FlatLoadSbyte: EmitFlatLoadSbyte(state, inst); break;
|
||||
case IR::Opcode::FlatLoadUshort: EmitFlatLoadUshort(state, inst); break;
|
||||
@@ -850,7 +857,13 @@ void EmitInstruction(EmitterState& state, const IR::Instruction& inst) {
|
||||
}
|
||||
EmitGuardedByExec(state, [&]() { EmitExport(state, inst); });
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
if (!state.unsupported_ir_instruction) {
|
||||
state.unsupported_ir_instruction = true;
|
||||
state.unsupported_ir_opcode = inst.op;
|
||||
state.unsupported_ir_pc = inst.pc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,9 @@ struct EmitterState {
|
||||
bool needs_image_gather_extended = false;
|
||||
bool needs_function_lds = false;
|
||||
bool needs_pixel_valid_mask = false;
|
||||
bool unsupported_ir_instruction = false;
|
||||
IR::Opcode unsupported_ir_opcode = IR::Opcode::Count;
|
||||
uint32_t unsupported_ir_pc = 0;
|
||||
std::vector<RegisterBinding> registers;
|
||||
std::vector<InputBinding> inputs;
|
||||
std::vector<OutputBinding> outputs;
|
||||
@@ -611,8 +614,6 @@ bool IsMaskRegisterFile(IR::RegisterFile file);
|
||||
|
||||
bool IsSccOperand(const IR::Operand& operand);
|
||||
|
||||
bool IsCompareOpcode(IR::Opcode op);
|
||||
|
||||
void CollectMaskStateRegisters(std::vector<RegisterBinding>& registers);
|
||||
|
||||
void CollectSequentialRegisters(std::vector<RegisterBinding>& registers, const IR::Operand& base,
|
||||
@@ -651,8 +652,6 @@ bool ProgramNeedsSubgroupBallot(const IR::Program& program);
|
||||
|
||||
bool ProgramNeedsSubgroupShuffle(const IR::Program& program);
|
||||
|
||||
bool IsCompareOpcode(IR::Opcode op);
|
||||
|
||||
bool ProgramNeedsSubgroupLocalInvocationId(const IR::Program& program);
|
||||
|
||||
uint32_t PointerForRegister(const EmitterState& state, IR::Register reg);
|
||||
@@ -1015,6 +1014,9 @@ void EmitDeviceAtomicMemoryBarrier(EmitterState& state);
|
||||
|
||||
void EmitAtomicU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode);
|
||||
|
||||
void EmitAtomicFMinF32(EmitterState& state, const IR::Instruction& inst);
|
||||
void EmitAtomicFMaxF32(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitSLoadDword(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitLoadSrtDword(EmitterState& state, const IR::Instruction& inst);
|
||||
@@ -1345,7 +1347,7 @@ void EmitCompareI16(EmitterState& state, const IR::Instruction& inst, uint32_t o
|
||||
|
||||
void EmitBitCompareB32(EmitterState& state, const IR::Instruction& inst, bool bit_set);
|
||||
|
||||
void EmitCompareNeU64(EmitterState& state, const IR::Instruction& inst);
|
||||
void EmitCompareU64(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitCompareConstant(EmitterState& state, const IR::Instruction& inst, bool value);
|
||||
|
||||
@@ -1577,19 +1579,22 @@ uint32_t EmitValueOrZeroIfCondition(EmitterState& state, uint32_t condition, Fn&
|
||||
}
|
||||
|
||||
const auto then_label = state.builder.AllocateId();
|
||||
const auto then_exit = state.builder.AllocateId();
|
||||
const auto else_label = state.builder.AllocateId();
|
||||
const auto merge_label = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpSelectionMerge, merge_label, SelectionControlNone});
|
||||
state.builder.AddFunction({OpBranchConditional, condition, then_label, else_label});
|
||||
state.builder.AddFunction({OpLabel, then_label});
|
||||
const auto then_value = fn();
|
||||
state.builder.AddFunction({OpBranch, then_exit});
|
||||
state.builder.AddFunction({OpLabel, then_exit});
|
||||
state.builder.AddFunction({OpBranch, merge_label});
|
||||
state.builder.AddFunction({OpLabel, else_label});
|
||||
state.builder.AddFunction({OpBranch, merge_label});
|
||||
state.builder.AddFunction({OpLabel, merge_label});
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
{OpPhi, state.uint_type, value, then_value, then_label, ConstantU32(state, 0), else_label});
|
||||
{OpPhi, state.uint_type, value, then_value, then_exit, ConstantU32(state, 0), else_label});
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -640,8 +640,8 @@ void EmitMemoryStoreU32(EmitterState& state, const IR::Instruction& inst, IR::Re
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void EmitAtomicUpdateU32(EmitterState& state, uint32_t pointer, IR::ResourceKind kind,
|
||||
Fn&& desired_value) {
|
||||
uint32_t EmitAtomicUpdateU32(EmitterState& state, uint32_t pointer, IR::ResourceKind kind,
|
||||
Fn&& desired_value) {
|
||||
const auto scope = kind == IR::ResourceKind::Lds ? ScopeWorkgroup : ScopeDevice;
|
||||
const auto memory = kind == IR::ResourceKind::Lds ? MemorySemanticsWorkgroupMemory
|
||||
: MemorySemanticsUniformMemory;
|
||||
@@ -675,6 +675,7 @@ void EmitAtomicUpdateU32(EmitterState& state, uint32_t pointer, IR::ResourceKind
|
||||
const auto semantics = MemorySemanticsAcquireRelease | memory;
|
||||
state.builder.AddFunction(
|
||||
{OpMemoryBarrier, ConstantU32(state, scope), ConstantU32(state, semantics)});
|
||||
return observed;
|
||||
}
|
||||
|
||||
void EmitMemoryStoreSubDwordU32(EmitterState& state, const IR::Instruction& inst,
|
||||
@@ -1181,6 +1182,79 @@ void EmitAtomicU32(EmitterState& state, const IR::Instruction& inst, uint32_t op
|
||||
EmitStoreU32(state, inst.dst, old);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
uint32_t EmitF32BitsOrderedCompare(EmitterState& state, uint32_t lhs_bits, uint32_t rhs_bits,
|
||||
uint32_t comparison_opcode) {
|
||||
struct ClassifiedBits {
|
||||
uint32_t nan = 0;
|
||||
uint32_t zero = 0;
|
||||
uint32_t key = 0;
|
||||
};
|
||||
const auto Classify = [&](uint32_t bits) {
|
||||
ClassifiedBits cls;
|
||||
const auto abs_bits = EmitAndConstant(state, bits, 0x7fffffffu);
|
||||
const auto exponent_bits = EmitAndConstant(state, abs_bits, 0x7f800000u);
|
||||
const auto mantissa_bits = EmitAndConstant(state, abs_bits, 0x007fffffu);
|
||||
const auto exponent_max =
|
||||
EmitCompareU32Constant(state, OpIEqual, exponent_bits, 0x7f800000u);
|
||||
const auto mantissa_nonzero = EmitCompareU32Constant(state, OpINotEqual, mantissa_bits, 0);
|
||||
const auto negative = EmitCompareU32Constant(state, OpINotEqual,
|
||||
EmitAndConstant(state, bits, 0x80000000u), 0);
|
||||
const auto negative_key = state.builder.AllocateId();
|
||||
const auto positive_key = state.builder.AllocateId();
|
||||
// Map all non-NaN IEEE-754 encodings to monotonically increasing unsigned keys.
|
||||
state.builder.AddFunction({OpNot, state.uint_type, negative_key, bits});
|
||||
state.builder.AddFunction(
|
||||
{OpBitwiseXor, state.uint_type, positive_key, bits, ConstantU32(state, 0x80000000u)});
|
||||
cls.nan = EmitLogicalAndBool(state, exponent_max, mantissa_nonzero);
|
||||
cls.zero = EmitCompareU32Constant(state, OpIEqual, abs_bits, 0);
|
||||
cls.key = EmitSelectValueU32(state, negative, negative_key, positive_key);
|
||||
return cls;
|
||||
};
|
||||
|
||||
const auto lhs = Classify(lhs_bits);
|
||||
const auto rhs = Classify(rhs_bits);
|
||||
const auto any_nan = EmitLogicalOrBool(state, lhs.nan, rhs.nan);
|
||||
// The key transform orders -0 below +0, while the floating comparison treats them as equal.
|
||||
const auto both_zero = EmitLogicalAndBool(state, lhs.zero, rhs.zero);
|
||||
const auto ordered_nonzero =
|
||||
EmitLogicalNotBool(state, EmitLogicalOrBool(state, any_nan, both_zero));
|
||||
const auto comparison = state.builder.AllocateId();
|
||||
state.builder.AddFunction({comparison_opcode, state.bool_type, comparison, lhs.key, rhs.key});
|
||||
return EmitLogicalAndBool(state, ordered_nonzero, comparison);
|
||||
}
|
||||
|
||||
void EmitAtomicFMinMaxF32(EmitterState& state, const IR::Instruction& inst,
|
||||
uint32_t comparison_opcode) {
|
||||
const auto index =
|
||||
EmitMemoryDwordIndex(state, inst, inst.memory, 1, AddressSourceCount(inst, 1));
|
||||
const auto in_bounds = EmitStorageBufferElementInBounds(state, inst.memory, index, inst.pc);
|
||||
const auto src_u32 = EmitValueLoad(state, inst.src[0]);
|
||||
const auto old = EmitValueOrZeroIfCondition(state, in_bounds, [&]() {
|
||||
const auto pointer = EmitStorageBufferElementPointer(state, inst.memory, index, inst.pc);
|
||||
return EmitAtomicUpdateU32(state, pointer, inst.memory.kind, [&](uint32_t old_u32) {
|
||||
const auto replace_old = state.builder.AllocateId();
|
||||
const auto replace =
|
||||
EmitF32BitsOrderedCompare(state, src_u32, old_u32, comparison_opcode);
|
||||
state.builder.AddFunction(
|
||||
{OpSelect, state.uint_type, replace_old, replace, src_u32, old_u32});
|
||||
return replace_old;
|
||||
});
|
||||
});
|
||||
EmitStoreU32(state, inst.dst, old);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void EmitAtomicFMinF32(EmitterState& state, const IR::Instruction& inst) {
|
||||
EmitAtomicFMinMaxF32(state, inst, OpULessThan);
|
||||
}
|
||||
|
||||
void EmitAtomicFMaxF32(EmitterState& state, const IR::Instruction& inst) {
|
||||
EmitAtomicFMinMaxF32(state, inst, OpUGreaterThan);
|
||||
}
|
||||
|
||||
void EmitSLoadDword(EmitterState& state, const IR::Instruction& inst) {
|
||||
if (state.address_memory_variable == 0) {
|
||||
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
|
||||
|
||||
@@ -30,7 +30,9 @@ bool IsAtomic(Opcode op) {
|
||||
case Opcode::AtomicUMaxU32:
|
||||
case Opcode::AtomicAndU32:
|
||||
case Opcode::AtomicOrU32:
|
||||
case Opcode::AtomicXorU32: return true;
|
||||
case Opcode::AtomicXorU32:
|
||||
case Opcode::AtomicFMinF32:
|
||||
case Opcode::AtomicFMaxF32: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,13 +490,26 @@ bool IsScalarMinMaxOpcode(Decoder::Opcode opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ResolveIrOpcode(Decoder::Opcode decoded, Opcode& lowered, std::string* error) {
|
||||
const auto opcode = LookupIrOpcode(decoded);
|
||||
if (!opcode.has_value()) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("decoded opcode has no IR lowering: {}",
|
||||
Decoder::OpcodeToString(decoded));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
lowered = *opcode;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LowerScalarMinMaxWithScc(const Decoder::Instruction& decoded, BasicBlock& block,
|
||||
std::string* error) {
|
||||
Instruction result;
|
||||
result.pc = decoded.pc;
|
||||
result.op = LookupIrOpcode(decoded.opcode);
|
||||
result.src_count = 2;
|
||||
if (!LowerRegisterOperand(decoded.dst, result.dst, error) ||
|
||||
if (!ResolveIrOpcode(decoded.opcode, result.op, error) ||
|
||||
!LowerRegisterOperand(decoded.dst, result.dst, error) ||
|
||||
!LowerSourceOperand(decoded.src0, result.src[0], error) ||
|
||||
!LowerSourceOperand(decoded.src1, result.src[1], error)) {
|
||||
return false;
|
||||
@@ -962,7 +975,12 @@ bool LowerControlInstruction(const Decoder::Instruction& decoded, BasicBlock& bl
|
||||
return LowerControlMarker(decoded, block, Opcode::TtraceData, true, error);
|
||||
case Decoder::Opcode::SInstPrefetch:
|
||||
return LowerControlMarker(decoded, block, Opcode::InstPrefetch, true, error);
|
||||
default: return false;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("control opcode has no specialized IR lowering: {}",
|
||||
Decoder::OpcodeToString(decoded.opcode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1051,6 +1069,18 @@ bool IsTerminatorOpcode(Decoder::Opcode opcode) {
|
||||
|
||||
bool LowerImplemented(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error);
|
||||
|
||||
bool IsMemoryFamily(Decoder::Family family) {
|
||||
switch (family) {
|
||||
case Decoder::Family::SMEM:
|
||||
case Decoder::Family::MUBUF:
|
||||
case Decoder::Family::MTBUF:
|
||||
case Decoder::Family::FLAT:
|
||||
case Decoder::Family::DS:
|
||||
case Decoder::Family::MIMG: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LowerDecodedInstruction(const Decoder::Instruction& inst, BasicBlock& block,
|
||||
std::string* error) {
|
||||
switch (inst.opcode) {
|
||||
@@ -1104,6 +1134,15 @@ bool LowerDecodedInstruction(const Decoder::Instruction& inst, BasicBlock& block
|
||||
if (IsMemoryOpcode(inst.opcode)) {
|
||||
return LowerMemoryInstruction(inst, block, error);
|
||||
}
|
||||
// Memory instructions require specialized lowering to populate MemoryInfo. Never let a
|
||||
// newly decoded memory-family opcode fall through to generic register-only IR.
|
||||
if (IsMemoryFamily(inst.family)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("decoded memory-family opcode has no specialized IR lowering: {}",
|
||||
Decoder::OpcodeToString(inst.opcode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (ScalarShiftLeftAddAmount(inst.opcode) != 0) {
|
||||
return LowerScalarShiftLeftAdd(inst, block, error);
|
||||
}
|
||||
@@ -1116,23 +1155,16 @@ bool LowerDecodedInstruction(const Decoder::Instruction& inst, BasicBlock& block
|
||||
if (VectorByteConvertIndex(inst.opcode) <= 3u) {
|
||||
return LowerVectorByteConvert(inst, block, error);
|
||||
}
|
||||
if (!IsImplemented(inst.opcode)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("decoded opcode has no IR lowering yet: {}",
|
||||
Decoder::InstructionToString(inst).c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return LowerImplemented(inst, block, error);
|
||||
}
|
||||
|
||||
bool LowerImplemented(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error) {
|
||||
Instruction inst;
|
||||
inst.pc = decoded.pc;
|
||||
inst.op = LookupIrOpcode(decoded.opcode);
|
||||
inst.src_count = decoded.src_count;
|
||||
|
||||
if (!LowerRegisterOperand(decoded.dst, inst.dst, error)) {
|
||||
if (!ResolveIrOpcode(decoded.opcode, inst.op, error) ||
|
||||
!LowerRegisterOperand(decoded.dst, inst.dst, error)) {
|
||||
return false;
|
||||
}
|
||||
ApplyDppDestinationMask(decoded, inst.dst);
|
||||
|
||||
@@ -8,350 +8,48 @@
|
||||
#include "graphics/shader/shader.h"
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
enum class Opcode {
|
||||
ControlNop,
|
||||
Waitcnt,
|
||||
Barrier,
|
||||
Sendmsg,
|
||||
TtraceData,
|
||||
InstPrefetch,
|
||||
MoveU32,
|
||||
MoveF32Bits,
|
||||
MoveRelDestU32,
|
||||
MoveRelSourceU32,
|
||||
MoveU64,
|
||||
WqmB64,
|
||||
SaveexecB32,
|
||||
SaveexecB64,
|
||||
ReadFirstLaneU32,
|
||||
ReadLaneU32,
|
||||
WriteLaneU32,
|
||||
Permlane16B32,
|
||||
Permlanex16B32,
|
||||
AbsI32,
|
||||
IAddU32,
|
||||
IAddCarryU32,
|
||||
ISubBorrowU32,
|
||||
ScalarAddCarryU32,
|
||||
ScalarSubBorrowU32,
|
||||
ScalarSubBorrowCarryU32,
|
||||
ScalarSignedAddOverflowI32,
|
||||
ScalarSignedSubOverflowI32,
|
||||
ScalarShiftLeftAddCarryU32,
|
||||
ISubU32,
|
||||
IMulU32,
|
||||
UMulHighU32,
|
||||
SMulHighI32,
|
||||
IMadI24U32,
|
||||
UMadU24U32,
|
||||
UMadU64U32,
|
||||
SadU32,
|
||||
IAdd3U32,
|
||||
IMulI24U32,
|
||||
UMulU24U32,
|
||||
IMinI32,
|
||||
IMaxI32,
|
||||
IMin3I32,
|
||||
IMax3I32,
|
||||
IMed3I32,
|
||||
UMinU32,
|
||||
UMaxU32,
|
||||
UMin3U32,
|
||||
UMax3U32,
|
||||
UMed3U32,
|
||||
BitwiseAndU32,
|
||||
BitwiseAndU64,
|
||||
BitwiseAndNotU32,
|
||||
BitwiseAndNotU64,
|
||||
BitwiseOrU32,
|
||||
BitwiseOrU64,
|
||||
BitwiseOrNotU32,
|
||||
BitwiseOrNotU64,
|
||||
BitwiseAndOrU32,
|
||||
BitwiseOr3U32,
|
||||
BitwiseXorU32,
|
||||
BitwiseXorU64,
|
||||
BitwiseXor3U32,
|
||||
BitwiseNandU32,
|
||||
BitwiseNandU64,
|
||||
BitwiseNorU32,
|
||||
BitwiseNorU64,
|
||||
BitwiseXnorU32,
|
||||
BitwiseXnorU64,
|
||||
BitwiseNotU32,
|
||||
BitwiseNotU64,
|
||||
BitClearU32,
|
||||
BitSetU32,
|
||||
BitReverseU32,
|
||||
BitCountU32,
|
||||
BitCountU64,
|
||||
BitReplicateB64B32,
|
||||
BitCountAddU32,
|
||||
MaskedBitCountLowU32,
|
||||
MaskedBitCountHighU32,
|
||||
FindLsbU32,
|
||||
FindMsbFromHighU32,
|
||||
FindMsbFromHighU64,
|
||||
BitFieldMaskU32,
|
||||
BitFieldMaskU64,
|
||||
BitFieldExtractU32,
|
||||
BitFieldExtractU64,
|
||||
BitFieldExtract3U32,
|
||||
BitFieldExtract3I32,
|
||||
BitFieldInsertSelectU32,
|
||||
BitCompare0B32,
|
||||
BitCompare1B32,
|
||||
AlignBitU32,
|
||||
ShiftLeftAddU32,
|
||||
AddShiftLeftU32,
|
||||
XorAddU32,
|
||||
ShiftLeftOrU32,
|
||||
ShiftLeftLogicalU32,
|
||||
ShiftLeftLogicalU64,
|
||||
ShiftLeftLogicalU16,
|
||||
ShiftRightLogicalU32,
|
||||
ShiftRightLogicalU64,
|
||||
ShiftRightLogicalU16,
|
||||
ShiftRightArithmeticI32,
|
||||
ShiftRightArithmeticI16,
|
||||
SelectU32,
|
||||
SelectMaskU32,
|
||||
SelectF32Bits,
|
||||
SelectMaskF32Bits,
|
||||
SelectU64,
|
||||
PackLowLowU16,
|
||||
PackLowHighU16,
|
||||
PackHighHighU16,
|
||||
CompareFalse,
|
||||
CompareTrue,
|
||||
CompareEqU32,
|
||||
CompareNeU32,
|
||||
CompareGtU32,
|
||||
CompareGeU32,
|
||||
CompareLtU32,
|
||||
CompareLeU32,
|
||||
CompareNeU64,
|
||||
CompareMaskEqU32,
|
||||
CompareMaskNeU32,
|
||||
CompareMaskGtU32,
|
||||
CompareMaskGeU32,
|
||||
CompareMaskLtU32,
|
||||
CompareMaskLeU32,
|
||||
CompareEqI32,
|
||||
CompareNeI32,
|
||||
CompareGtI32,
|
||||
CompareGeI32,
|
||||
CompareLtI32,
|
||||
CompareLeI32,
|
||||
CompareEqI16,
|
||||
CompareNeI16,
|
||||
CompareGtI16,
|
||||
CompareGeI16,
|
||||
CompareLtI16,
|
||||
CompareLeI16,
|
||||
CompareMaskEqI32,
|
||||
CompareMaskNeI32,
|
||||
CompareMaskGtI32,
|
||||
CompareMaskGeI32,
|
||||
CompareMaskLtI32,
|
||||
CompareMaskLeI32,
|
||||
CompareEqU16,
|
||||
CompareNeU16,
|
||||
CompareGtU16,
|
||||
CompareGeU16,
|
||||
CompareLtU16,
|
||||
CompareLeU16,
|
||||
CompareEqF32,
|
||||
CompareNeF32,
|
||||
CompareGtF32,
|
||||
CompareGeF32,
|
||||
CompareLtF32,
|
||||
CompareLeF32,
|
||||
CompareOrderedF32,
|
||||
CompareUnorderedF32,
|
||||
CompareUnordEqF32,
|
||||
CompareUnordNeF32,
|
||||
CompareUnordGtF32,
|
||||
CompareUnordGeF32,
|
||||
CompareUnordLtF32,
|
||||
CompareUnordLeF32,
|
||||
CompareClassF32,
|
||||
CompareEqF16,
|
||||
CompareNeF16,
|
||||
CompareGtF16,
|
||||
CompareGeF16,
|
||||
CompareLtF16,
|
||||
CompareLeF16,
|
||||
CompareUnordNeF16,
|
||||
CompareMaskEqF16,
|
||||
CompareMaskNeF16,
|
||||
CompareMaskGtF16,
|
||||
CompareMaskGeF16,
|
||||
CompareMaskLtF16,
|
||||
CompareMaskLeF16,
|
||||
CompareMaskUnordNeF16,
|
||||
CompareMaskUnordGeF16,
|
||||
CompareMaskEqF32,
|
||||
CompareMaskNeF32,
|
||||
CompareMaskGtF32,
|
||||
CompareMaskGeF32,
|
||||
CompareMaskLtF32,
|
||||
CompareMaskLeF32,
|
||||
CompareMaskUnordEqF32,
|
||||
CompareMaskUnordNeF32,
|
||||
CompareMaskUnordGtF32,
|
||||
CompareMaskUnordGeF32,
|
||||
CompareMaskUnordLtF32,
|
||||
CompareMaskUnordLeF32,
|
||||
ConvertByteU32ToF32,
|
||||
ConvertU32ToF32,
|
||||
ConvertI32ToF32,
|
||||
ConvertF32ToU32,
|
||||
ConvertF32ToI32,
|
||||
ConvertF32ToF16,
|
||||
ConvertF16ToF32,
|
||||
ConvertU16ToF16,
|
||||
ConvertF16ToU16,
|
||||
ConvertI16ToF16,
|
||||
ConvertF16ToI16,
|
||||
ConvertRoundPlusInfF32ToI32,
|
||||
ConvertFloorF32ToI32,
|
||||
ConvertI4ToOffsetF32,
|
||||
LdexpF32,
|
||||
PackF32ToF16Rtz,
|
||||
PackSnorm2x16F32,
|
||||
PackUnorm2x16F32,
|
||||
PackU16U32,
|
||||
PackU8F32,
|
||||
PackB32F16,
|
||||
PackedMadI16,
|
||||
PackedMulLoU16,
|
||||
PackedAddI16,
|
||||
PackedSubI16,
|
||||
PackedLshlrevB16,
|
||||
PackedLshrrevB16,
|
||||
PackedAshrrevI16,
|
||||
PackedMaxI16,
|
||||
PackedMinI16,
|
||||
PackedMadU16,
|
||||
PackedAddU16,
|
||||
PackedSubU16,
|
||||
PackedMaxU16,
|
||||
PackedMinU16,
|
||||
PackedAddF16,
|
||||
PackedMulF16,
|
||||
PackedMinF16,
|
||||
PackedMaxF16,
|
||||
PackedFmaF16,
|
||||
AddF16,
|
||||
SubF16,
|
||||
MulF16,
|
||||
MinF16,
|
||||
MaxF16,
|
||||
FmaF16,
|
||||
MadMixF16,
|
||||
IAddU16,
|
||||
ISubI16,
|
||||
IMinI16,
|
||||
IMaxI16,
|
||||
UMinU16,
|
||||
UMaxU16,
|
||||
RcpF32,
|
||||
FractF32,
|
||||
TruncF32,
|
||||
CeilF32,
|
||||
RoundEvenF32,
|
||||
FloorF32,
|
||||
Exp2F32,
|
||||
Log2F32,
|
||||
InverseSqrtF32,
|
||||
SqrtF32,
|
||||
RcpF16,
|
||||
SqrtF16,
|
||||
InverseSqrtF16,
|
||||
Log2F16,
|
||||
Exp2F16,
|
||||
FloorF16,
|
||||
CeilF16,
|
||||
TruncF16,
|
||||
RoundEvenF16,
|
||||
SinF32,
|
||||
CosF32,
|
||||
CubeIdF32,
|
||||
CubeScF32,
|
||||
CubeTcF32,
|
||||
CubeMaF32,
|
||||
FAddF32,
|
||||
FSubF32,
|
||||
FMulF32,
|
||||
FMinF32,
|
||||
FMaxF32,
|
||||
FMadF32,
|
||||
Dot2AccF32F16,
|
||||
FMin3F32,
|
||||
FMax3F32,
|
||||
FMed3F32,
|
||||
Min3F16,
|
||||
Max3F16,
|
||||
Med3F16,
|
||||
LoadSrtDword,
|
||||
SLoadDword,
|
||||
SBufferLoadDword,
|
||||
BufferLoadUbyte,
|
||||
BufferLoadSbyte,
|
||||
BufferLoadUshort,
|
||||
BufferLoadSshort,
|
||||
BufferLoadDword,
|
||||
BufferStoreByte,
|
||||
BufferStoreShort,
|
||||
BufferStoreDword,
|
||||
AtomicSwapU32,
|
||||
AtomicAddU32,
|
||||
AtomicSubU32,
|
||||
AtomicSMinI32,
|
||||
AtomicUMinU32,
|
||||
AtomicSMaxI32,
|
||||
AtomicUMaxU32,
|
||||
AtomicAndU32,
|
||||
AtomicOrU32,
|
||||
AtomicXorU32,
|
||||
FlatLoadUbyte,
|
||||
FlatLoadSbyte,
|
||||
FlatLoadUshort,
|
||||
FlatLoadSshort,
|
||||
FlatLoadDword,
|
||||
FlatStoreByte,
|
||||
FlatStoreShort,
|
||||
FlatStoreDword,
|
||||
DsReadUbyte,
|
||||
DsReadSbyte,
|
||||
DsReadUshort,
|
||||
DsReadSshort,
|
||||
DsReadB32,
|
||||
DsWriteByte,
|
||||
DsWriteShort,
|
||||
DsWriteB32,
|
||||
DsMinF32,
|
||||
DsMaxF32,
|
||||
DsSwizzleB32,
|
||||
DsConsume,
|
||||
DsAppend,
|
||||
DsWriteAddtidB32,
|
||||
DsReadAddtidB32,
|
||||
ImageGetResinfo,
|
||||
ImageGetLod,
|
||||
ImageLoad,
|
||||
ImageStore,
|
||||
ImageSample,
|
||||
ImageGather4,
|
||||
LoadInputF32,
|
||||
Export,
|
||||
#define IR_OPCODE(name, category) name,
|
||||
#include "graphics/shader/recompiler/ir/ShaderIROpcodes.inc"
|
||||
#undef IR_OPCODE
|
||||
Count,
|
||||
};
|
||||
|
||||
enum class OpcodeClass { General, Compare, Compare64 };
|
||||
|
||||
struct OpcodeInfo {
|
||||
std::string_view name;
|
||||
OpcodeClass opcode_class = OpcodeClass::General;
|
||||
};
|
||||
|
||||
inline constexpr OpcodeInfo OPCODE_INFO[] = {
|
||||
#define IR_OPCODE(name, category) {#name, OpcodeClass::category},
|
||||
#include "graphics/shader/recompiler/ir/ShaderIROpcodes.inc"
|
||||
#undef IR_OPCODE
|
||||
};
|
||||
static_assert(sizeof(OPCODE_INFO) / sizeof(OPCODE_INFO[0]) == static_cast<size_t>(Opcode::Count));
|
||||
|
||||
constexpr const OpcodeInfo& GetOpcodeInfo(Opcode opcode) {
|
||||
return OPCODE_INFO[static_cast<size_t>(opcode)];
|
||||
}
|
||||
|
||||
constexpr std::string_view OpcodeName(Opcode opcode) {
|
||||
return GetOpcodeInfo(opcode).name;
|
||||
}
|
||||
|
||||
constexpr bool IsCompareOpcode(Opcode opcode) {
|
||||
return GetOpcodeInfo(opcode).opcode_class != OpcodeClass::General;
|
||||
}
|
||||
|
||||
constexpr bool IsCompare64Opcode(Opcode opcode) {
|
||||
return GetOpcodeInfo(opcode).opcode_class == OpcodeClass::Compare64;
|
||||
}
|
||||
|
||||
enum class RegisterFile { Scalar, Vector, Vcc, Exec, Scc, M0 };
|
||||
|
||||
struct Register {
|
||||
|
||||
@@ -3,16 +3,17 @@
|
||||
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
void SetError(std::string* error, const char* message);
|
||||
|
||||
Opcode LookupIrOpcode(Decoder::Opcode opcode);
|
||||
bool IsImplemented(Decoder::Opcode opcode);
|
||||
bool IsReversedBinary(Decoder::Opcode opcode);
|
||||
bool IsVectorCarryOutOpcode(Decoder::Opcode opcode);
|
||||
bool ScalarResultWritesSccNonZero(Decoder::Opcode opcode);
|
||||
bool ScalarResultIs64Bit(Decoder::Opcode opcode);
|
||||
std::optional<Opcode> LookupIrOpcode(Decoder::Opcode opcode);
|
||||
bool IsReversedBinary(Decoder::Opcode opcode);
|
||||
bool IsVectorCarryOutOpcode(Decoder::Opcode opcode);
|
||||
bool ScalarResultWritesSccNonZero(Decoder::Opcode opcode);
|
||||
bool ScalarResultIs64Bit(Decoder::Opcode opcode);
|
||||
|
||||
Operand MakeSccOperand();
|
||||
Operand MakeImmediateU32(uint32_t value);
|
||||
|
||||
@@ -157,345 +157,7 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
text += ExportInfoToString(inst.export_info);
|
||||
return text;
|
||||
}
|
||||
switch (inst.op) {
|
||||
case Opcode::ControlNop: text += "ControlNop"; break;
|
||||
case Opcode::Waitcnt: text += "Waitcnt"; break;
|
||||
case Opcode::Barrier: text += "Barrier"; break;
|
||||
case Opcode::Sendmsg: text += "Sendmsg"; break;
|
||||
case Opcode::TtraceData: text += "TtraceData"; break;
|
||||
case Opcode::InstPrefetch: text += "InstPrefetch"; break;
|
||||
case Opcode::MoveU32: text += "MoveU32"; break;
|
||||
case Opcode::MoveF32Bits: text += "MoveF32Bits"; break;
|
||||
case Opcode::MoveRelDestU32: text += "MoveRelDestU32"; break;
|
||||
case Opcode::MoveRelSourceU32: text += "MoveRelSourceU32"; break;
|
||||
case Opcode::MoveU64: text += "MoveU64"; break;
|
||||
case Opcode::WqmB64: text += "WqmB64"; break;
|
||||
case Opcode::SaveexecB32: text += "SaveexecB32"; break;
|
||||
case Opcode::SaveexecB64: text += "SaveexecB64"; break;
|
||||
case Opcode::ReadFirstLaneU32: text += "ReadFirstLaneU32"; break;
|
||||
case Opcode::ReadLaneU32: text += "ReadLaneU32"; break;
|
||||
case Opcode::WriteLaneU32: text += "WriteLaneU32"; break;
|
||||
case Opcode::Permlane16B32: text += "Permlane16B32"; break;
|
||||
case Opcode::Permlanex16B32: text += "Permlanex16B32"; break;
|
||||
case Opcode::AbsI32: text += "AbsI32"; break;
|
||||
case Opcode::IAddU32: text += "IAddU32"; break;
|
||||
case Opcode::IAddCarryU32: text += "IAddCarryU32"; break;
|
||||
case Opcode::ISubBorrowU32: text += "ISubBorrowU32"; break;
|
||||
case Opcode::ScalarAddCarryU32: text += "ScalarAddCarryU32"; break;
|
||||
case Opcode::ScalarSubBorrowU32: text += "ScalarSubBorrowU32"; break;
|
||||
case Opcode::ScalarSubBorrowCarryU32: text += "ScalarSubBorrowCarryU32"; break;
|
||||
case Opcode::ScalarSignedAddOverflowI32: text += "ScalarSignedAddOverflowI32"; break;
|
||||
case Opcode::ScalarSignedSubOverflowI32: text += "ScalarSignedSubOverflowI32"; break;
|
||||
case Opcode::ScalarShiftLeftAddCarryU32: text += "ScalarShiftLeftAddCarryU32"; break;
|
||||
case Opcode::ISubU32: text += "ISubU32"; break;
|
||||
case Opcode::IMulU32: text += "IMulU32"; break;
|
||||
case Opcode::UMulHighU32: text += "UMulHighU32"; break;
|
||||
case Opcode::SMulHighI32: text += "SMulHighI32"; break;
|
||||
case Opcode::IMadI24U32: text += "IMadI24U32"; break;
|
||||
case Opcode::UMadU24U32: text += "UMadU24U32"; break;
|
||||
case Opcode::UMadU64U32: text += "UMadU64U32"; break;
|
||||
case Opcode::SadU32: text += "SadU32"; break;
|
||||
case Opcode::IAdd3U32: text += "IAdd3U32"; break;
|
||||
case Opcode::IMulI24U32: text += "IMulI24U32"; break;
|
||||
case Opcode::UMulU24U32: text += "UMulU24U32"; break;
|
||||
case Opcode::IMinI32: text += "IMinI32"; break;
|
||||
case Opcode::IMaxI32: text += "IMaxI32"; break;
|
||||
case Opcode::IMin3I32: text += "IMin3I32"; break;
|
||||
case Opcode::IMax3I32: text += "IMax3I32"; break;
|
||||
case Opcode::IMed3I32: text += "IMed3I32"; break;
|
||||
case Opcode::UMinU32: text += "UMinU32"; break;
|
||||
case Opcode::UMaxU32: text += "UMaxU32"; break;
|
||||
case Opcode::UMin3U32: text += "UMin3U32"; break;
|
||||
case Opcode::UMax3U32: text += "UMax3U32"; break;
|
||||
case Opcode::UMed3U32: text += "UMed3U32"; break;
|
||||
case Opcode::BitwiseAndU32: text += "BitwiseAndU32"; break;
|
||||
case Opcode::BitwiseAndU64: text += "BitwiseAndU64"; break;
|
||||
case Opcode::BitwiseAndNotU32: text += "BitwiseAndNotU32"; break;
|
||||
case Opcode::BitwiseAndNotU64: text += "BitwiseAndNotU64"; break;
|
||||
case Opcode::BitwiseOrU32: text += "BitwiseOrU32"; break;
|
||||
case Opcode::BitwiseOrU64: text += "BitwiseOrU64"; break;
|
||||
case Opcode::BitwiseOrNotU32: text += "BitwiseOrNotU32"; break;
|
||||
case Opcode::BitwiseOrNotU64: text += "BitwiseOrNotU64"; break;
|
||||
case Opcode::BitwiseAndOrU32: text += "BitwiseAndOrU32"; break;
|
||||
case Opcode::BitwiseOr3U32: text += "BitwiseOr3U32"; break;
|
||||
case Opcode::BitwiseXorU32: text += "BitwiseXorU32"; break;
|
||||
case Opcode::BitwiseXorU64: text += "BitwiseXorU64"; break;
|
||||
case Opcode::BitwiseXor3U32: text += "BitwiseXor3U32"; break;
|
||||
case Opcode::BitwiseNandU32: text += "BitwiseNandU32"; break;
|
||||
case Opcode::BitwiseNandU64: text += "BitwiseNandU64"; break;
|
||||
case Opcode::BitwiseNorU32: text += "BitwiseNorU32"; break;
|
||||
case Opcode::BitwiseNorU64: text += "BitwiseNorU64"; break;
|
||||
case Opcode::BitwiseXnorU32: text += "BitwiseXnorU32"; break;
|
||||
case Opcode::BitwiseXnorU64: text += "BitwiseXnorU64"; break;
|
||||
case Opcode::BitwiseNotU32: text += "BitwiseNotU32"; break;
|
||||
case Opcode::BitwiseNotU64: text += "BitwiseNotU64"; break;
|
||||
case Opcode::BitClearU32: text += "BitClearU32"; break;
|
||||
case Opcode::BitSetU32: text += "BitSetU32"; break;
|
||||
case Opcode::BitReverseU32: text += "BitReverseU32"; break;
|
||||
case Opcode::BitCountU32: text += "BitCountU32"; break;
|
||||
case Opcode::BitCountU64: text += "BitCountU64"; break;
|
||||
case Opcode::BitReplicateB64B32: text += "BitReplicateB64B32"; break;
|
||||
case Opcode::BitCountAddU32: text += "BitCountAddU32"; break;
|
||||
case Opcode::MaskedBitCountLowU32: text += "MaskedBitCountLowU32"; break;
|
||||
case Opcode::MaskedBitCountHighU32: text += "MaskedBitCountHighU32"; break;
|
||||
case Opcode::FindLsbU32: text += "FindLsbU32"; break;
|
||||
case Opcode::FindMsbFromHighU32: text += "FindMsbFromHighU32"; break;
|
||||
case Opcode::FindMsbFromHighU64: text += "FindMsbFromHighU64"; break;
|
||||
case Opcode::BitFieldMaskU32: text += "BitFieldMaskU32"; break;
|
||||
case Opcode::BitFieldMaskU64: text += "BitFieldMaskU64"; break;
|
||||
case Opcode::BitFieldExtractU32: text += "BitFieldExtractU32"; break;
|
||||
case Opcode::BitFieldExtractU64: text += "BitFieldExtractU64"; break;
|
||||
case Opcode::BitFieldExtract3U32: text += "BitFieldExtract3U32"; break;
|
||||
case Opcode::BitFieldExtract3I32: text += "BitFieldExtract3I32"; break;
|
||||
case Opcode::BitFieldInsertSelectU32: text += "BitFieldInsertSelectU32"; break;
|
||||
case Opcode::BitCompare0B32: text += "BitCompare0B32"; break;
|
||||
case Opcode::BitCompare1B32: text += "BitCompare1B32"; break;
|
||||
case Opcode::AlignBitU32: text += "AlignBitU32"; break;
|
||||
case Opcode::ShiftLeftAddU32: text += "ShiftLeftAddU32"; break;
|
||||
case Opcode::AddShiftLeftU32: text += "AddShiftLeftU32"; break;
|
||||
case Opcode::XorAddU32: text += "XorAddU32"; break;
|
||||
case Opcode::ShiftLeftOrU32: text += "ShiftLeftOrU32"; break;
|
||||
case Opcode::ShiftLeftLogicalU32: text += "ShiftLeftLogicalU32"; break;
|
||||
case Opcode::ShiftLeftLogicalU64: text += "ShiftLeftLogicalU64"; break;
|
||||
case Opcode::ShiftLeftLogicalU16: text += "ShiftLeftLogicalU16"; break;
|
||||
case Opcode::ShiftRightLogicalU32: text += "ShiftRightLogicalU32"; break;
|
||||
case Opcode::ShiftRightLogicalU64: text += "ShiftRightLogicalU64"; break;
|
||||
case Opcode::ShiftRightLogicalU16: text += "ShiftRightLogicalU16"; break;
|
||||
case Opcode::ShiftRightArithmeticI32: text += "ShiftRightArithmeticI32"; break;
|
||||
case Opcode::ShiftRightArithmeticI16: text += "ShiftRightArithmeticI16"; break;
|
||||
case Opcode::SelectU32: text += "SelectU32"; break;
|
||||
case Opcode::SelectMaskU32: text += "SelectMaskU32"; break;
|
||||
case Opcode::SelectF32Bits: text += "SelectF32Bits"; break;
|
||||
case Opcode::SelectMaskF32Bits: text += "SelectMaskF32Bits"; break;
|
||||
case Opcode::SelectU64: text += "SelectU64"; break;
|
||||
case Opcode::PackLowLowU16: text += "PackLowLowU16"; break;
|
||||
case Opcode::PackLowHighU16: text += "PackLowHighU16"; break;
|
||||
case Opcode::PackHighHighU16: text += "PackHighHighU16"; break;
|
||||
case Opcode::CompareFalse: text += "CompareFalse"; break;
|
||||
case Opcode::CompareTrue: text += "CompareTrue"; break;
|
||||
case Opcode::CompareEqU32: text += "CompareEqU32"; break;
|
||||
case Opcode::CompareNeU32: text += "CompareNeU32"; break;
|
||||
case Opcode::CompareGtU32: text += "CompareGtU32"; break;
|
||||
case Opcode::CompareGeU32: text += "CompareGeU32"; break;
|
||||
case Opcode::CompareLtU32: text += "CompareLtU32"; break;
|
||||
case Opcode::CompareLeU32: text += "CompareLeU32"; break;
|
||||
case Opcode::CompareNeU64: text += "CompareNeU64"; break;
|
||||
case Opcode::CompareMaskEqU32: text += "CompareMaskEqU32"; break;
|
||||
case Opcode::CompareMaskNeU32: text += "CompareMaskNeU32"; break;
|
||||
case Opcode::CompareMaskGtU32: text += "CompareMaskGtU32"; break;
|
||||
case Opcode::CompareMaskGeU32: text += "CompareMaskGeU32"; break;
|
||||
case Opcode::CompareMaskLtU32: text += "CompareMaskLtU32"; break;
|
||||
case Opcode::CompareMaskLeU32: text += "CompareMaskLeU32"; break;
|
||||
case Opcode::CompareEqI32: text += "CompareEqI32"; break;
|
||||
case Opcode::CompareNeI32: text += "CompareNeI32"; break;
|
||||
case Opcode::CompareGtI32: text += "CompareGtI32"; break;
|
||||
case Opcode::CompareGeI32: text += "CompareGeI32"; break;
|
||||
case Opcode::CompareLtI32: text += "CompareLtI32"; break;
|
||||
case Opcode::CompareLeI32: text += "CompareLeI32"; break;
|
||||
case Opcode::CompareEqI16: text += "CompareEqI16"; break;
|
||||
case Opcode::CompareNeI16: text += "CompareNeI16"; break;
|
||||
case Opcode::CompareGtI16: text += "CompareGtI16"; break;
|
||||
case Opcode::CompareGeI16: text += "CompareGeI16"; break;
|
||||
case Opcode::CompareLtI16: text += "CompareLtI16"; break;
|
||||
case Opcode::CompareLeI16: text += "CompareLeI16"; break;
|
||||
case Opcode::CompareMaskEqI32: text += "CompareMaskEqI32"; break;
|
||||
case Opcode::CompareMaskNeI32: text += "CompareMaskNeI32"; break;
|
||||
case Opcode::CompareMaskGtI32: text += "CompareMaskGtI32"; break;
|
||||
case Opcode::CompareMaskGeI32: text += "CompareMaskGeI32"; break;
|
||||
case Opcode::CompareMaskLtI32: text += "CompareMaskLtI32"; break;
|
||||
case Opcode::CompareMaskLeI32: text += "CompareMaskLeI32"; break;
|
||||
case Opcode::CompareEqU16: text += "CompareEqU16"; break;
|
||||
case Opcode::CompareNeU16: text += "CompareNeU16"; break;
|
||||
case Opcode::CompareGtU16: text += "CompareGtU16"; break;
|
||||
case Opcode::CompareGeU16: text += "CompareGeU16"; break;
|
||||
case Opcode::CompareLtU16: text += "CompareLtU16"; break;
|
||||
case Opcode::CompareLeU16: text += "CompareLeU16"; break;
|
||||
case Opcode::CompareEqF32: text += "CompareEqF32"; break;
|
||||
case Opcode::CompareNeF32: text += "CompareNeF32"; break;
|
||||
case Opcode::CompareGtF32: text += "CompareGtF32"; break;
|
||||
case Opcode::CompareGeF32: text += "CompareGeF32"; break;
|
||||
case Opcode::CompareLtF32: text += "CompareLtF32"; break;
|
||||
case Opcode::CompareLeF32: text += "CompareLeF32"; break;
|
||||
case Opcode::CompareOrderedF32: text += "CompareOrderedF32"; break;
|
||||
case Opcode::CompareUnorderedF32: text += "CompareUnorderedF32"; break;
|
||||
case Opcode::CompareUnordEqF32: text += "CompareUnordEqF32"; break;
|
||||
case Opcode::CompareUnordNeF32: text += "CompareUnordNeF32"; break;
|
||||
case Opcode::CompareUnordGtF32: text += "CompareUnordGtF32"; break;
|
||||
case Opcode::CompareUnordGeF32: text += "CompareUnordGeF32"; break;
|
||||
case Opcode::CompareUnordLtF32: text += "CompareUnordLtF32"; break;
|
||||
case Opcode::CompareUnordLeF32: text += "CompareUnordLeF32"; break;
|
||||
case Opcode::CompareClassF32: text += "CompareClassF32"; break;
|
||||
case Opcode::CompareEqF16: text += "CompareEqF16"; break;
|
||||
case Opcode::CompareNeF16: text += "CompareNeF16"; break;
|
||||
case Opcode::CompareGtF16: text += "CompareGtF16"; break;
|
||||
case Opcode::CompareGeF16: text += "CompareGeF16"; break;
|
||||
case Opcode::CompareLtF16: text += "CompareLtF16"; break;
|
||||
case Opcode::CompareLeF16: text += "CompareLeF16"; break;
|
||||
case Opcode::CompareUnordNeF16: text += "CompareUnordNeF16"; break;
|
||||
case Opcode::CompareMaskEqF16: text += "CompareMaskEqF16"; break;
|
||||
case Opcode::CompareMaskNeF16: text += "CompareMaskNeF16"; break;
|
||||
case Opcode::CompareMaskGtF16: text += "CompareMaskGtF16"; break;
|
||||
case Opcode::CompareMaskGeF16: text += "CompareMaskGeF16"; break;
|
||||
case Opcode::CompareMaskLtF16: text += "CompareMaskLtF16"; break;
|
||||
case Opcode::CompareMaskLeF16: text += "CompareMaskLeF16"; break;
|
||||
case Opcode::CompareMaskUnordNeF16: text += "CompareMaskUnordNeF16"; break;
|
||||
case Opcode::CompareMaskUnordGeF16: text += "CompareMaskUnordGeF16"; break;
|
||||
case Opcode::CompareMaskEqF32: text += "CompareMaskEqF32"; break;
|
||||
case Opcode::CompareMaskNeF32: text += "CompareMaskNeF32"; break;
|
||||
case Opcode::CompareMaskGtF32: text += "CompareMaskGtF32"; break;
|
||||
case Opcode::CompareMaskGeF32: text += "CompareMaskGeF32"; break;
|
||||
case Opcode::CompareMaskLtF32: text += "CompareMaskLtF32"; break;
|
||||
case Opcode::CompareMaskLeF32: text += "CompareMaskLeF32"; break;
|
||||
case Opcode::CompareMaskUnordEqF32: text += "CompareMaskUnordEqF32"; break;
|
||||
case Opcode::CompareMaskUnordNeF32: text += "CompareMaskUnordNeF32"; break;
|
||||
case Opcode::CompareMaskUnordGtF32: text += "CompareMaskUnordGtF32"; break;
|
||||
case Opcode::CompareMaskUnordGeF32: text += "CompareMaskUnordGeF32"; break;
|
||||
case Opcode::CompareMaskUnordLtF32: text += "CompareMaskUnordLtF32"; break;
|
||||
case Opcode::CompareMaskUnordLeF32: text += "CompareMaskUnordLeF32"; break;
|
||||
case Opcode::ConvertByteU32ToF32: text += "ConvertByteU32ToF32"; break;
|
||||
case Opcode::ConvertU32ToF32: text += "ConvertU32ToF32"; break;
|
||||
case Opcode::ConvertI32ToF32: text += "ConvertI32ToF32"; break;
|
||||
case Opcode::ConvertF32ToU32: text += "ConvertF32ToU32"; break;
|
||||
case Opcode::ConvertF32ToI32: text += "ConvertF32ToI32"; break;
|
||||
case Opcode::ConvertF32ToF16: text += "ConvertF32ToF16"; break;
|
||||
case Opcode::ConvertF16ToF32: text += "ConvertF16ToF32"; break;
|
||||
case Opcode::ConvertU16ToF16: text += "ConvertU16ToF16"; break;
|
||||
case Opcode::ConvertF16ToU16: text += "ConvertF16ToU16"; break;
|
||||
case Opcode::ConvertI16ToF16: text += "ConvertI16ToF16"; break;
|
||||
case Opcode::ConvertF16ToI16: text += "ConvertF16ToI16"; break;
|
||||
case Opcode::ConvertRoundPlusInfF32ToI32: text += "ConvertRoundPlusInfF32ToI32"; break;
|
||||
case Opcode::ConvertFloorF32ToI32: text += "ConvertFloorF32ToI32"; break;
|
||||
case Opcode::ConvertI4ToOffsetF32: text += "ConvertI4ToOffsetF32"; break;
|
||||
case Opcode::LdexpF32: text += "LdexpF32"; break;
|
||||
case Opcode::PackF32ToF16Rtz: text += "PackF32ToF16Rtz"; break;
|
||||
case Opcode::PackSnorm2x16F32: text += "PackSnorm2x16F32"; break;
|
||||
case Opcode::PackUnorm2x16F32: text += "PackUnorm2x16F32"; break;
|
||||
case Opcode::PackU16U32: text += "PackU16U32"; break;
|
||||
case Opcode::PackU8F32: text += "PackU8F32"; break;
|
||||
case Opcode::PackB32F16: text += "PackB32F16"; break;
|
||||
case Opcode::PackedMadI16: text += "PackedMadI16"; break;
|
||||
case Opcode::PackedMulLoU16: text += "PackedMulLoU16"; break;
|
||||
case Opcode::PackedAddI16: text += "PackedAddI16"; break;
|
||||
case Opcode::PackedSubI16: text += "PackedSubI16"; break;
|
||||
case Opcode::PackedLshlrevB16: text += "PackedLshlrevB16"; break;
|
||||
case Opcode::PackedLshrrevB16: text += "PackedLshrrevB16"; break;
|
||||
case Opcode::PackedAshrrevI16: text += "PackedAshrrevI16"; break;
|
||||
case Opcode::PackedMaxI16: text += "PackedMaxI16"; break;
|
||||
case Opcode::PackedMinI16: text += "PackedMinI16"; break;
|
||||
case Opcode::PackedMadU16: text += "PackedMadU16"; break;
|
||||
case Opcode::PackedAddU16: text += "PackedAddU16"; break;
|
||||
case Opcode::PackedSubU16: text += "PackedSubU16"; break;
|
||||
case Opcode::PackedMaxU16: text += "PackedMaxU16"; break;
|
||||
case Opcode::PackedMinU16: text += "PackedMinU16"; break;
|
||||
case Opcode::PackedAddF16: text += "PackedAddF16"; break;
|
||||
case Opcode::PackedMulF16: text += "PackedMulF16"; break;
|
||||
case Opcode::PackedMinF16: text += "PackedMinF16"; break;
|
||||
case Opcode::PackedMaxF16: text += "PackedMaxF16"; break;
|
||||
case Opcode::PackedFmaF16: text += "PackedFmaF16"; break;
|
||||
case Opcode::AddF16: text += "AddF16"; break;
|
||||
case Opcode::SubF16: text += "SubF16"; break;
|
||||
case Opcode::MulF16: text += "MulF16"; break;
|
||||
case Opcode::MinF16: text += "MinF16"; break;
|
||||
case Opcode::MaxF16: text += "MaxF16"; break;
|
||||
case Opcode::FmaF16: text += "FmaF16"; break;
|
||||
case Opcode::MadMixF16: text += "MadMixF16"; break;
|
||||
case Opcode::IAddU16: text += "IAddU16"; break;
|
||||
case Opcode::ISubI16: text += "ISubI16"; break;
|
||||
case Opcode::IMinI16: text += "IMinI16"; break;
|
||||
case Opcode::IMaxI16: text += "IMaxI16"; break;
|
||||
case Opcode::UMinU16: text += "UMinU16"; break;
|
||||
case Opcode::UMaxU16: text += "UMaxU16"; break;
|
||||
case Opcode::RcpF32: text += "RcpF32"; break;
|
||||
case Opcode::FractF32: text += "FractF32"; break;
|
||||
case Opcode::TruncF32: text += "TruncF32"; break;
|
||||
case Opcode::CeilF32: text += "CeilF32"; break;
|
||||
case Opcode::RoundEvenF32: text += "RoundEvenF32"; break;
|
||||
case Opcode::FloorF32: text += "FloorF32"; break;
|
||||
case Opcode::Exp2F32: text += "Exp2F32"; break;
|
||||
case Opcode::Log2F32: text += "Log2F32"; break;
|
||||
case Opcode::InverseSqrtF32: text += "InverseSqrtF32"; break;
|
||||
case Opcode::SqrtF32: text += "SqrtF32"; break;
|
||||
case Opcode::RcpF16: text += "RcpF16"; break;
|
||||
case Opcode::SqrtF16: text += "SqrtF16"; break;
|
||||
case Opcode::InverseSqrtF16: text += "InverseSqrtF16"; break;
|
||||
case Opcode::Log2F16: text += "Log2F16"; break;
|
||||
case Opcode::Exp2F16: text += "Exp2F16"; break;
|
||||
case Opcode::FloorF16: text += "FloorF16"; break;
|
||||
case Opcode::CeilF16: text += "CeilF16"; break;
|
||||
case Opcode::TruncF16: text += "TruncF16"; break;
|
||||
case Opcode::RoundEvenF16: text += "RoundEvenF16"; break;
|
||||
case Opcode::SinF32: text += "SinF32"; break;
|
||||
case Opcode::CosF32: text += "CosF32"; break;
|
||||
case Opcode::CubeIdF32: text += "CubeIdF32"; break;
|
||||
case Opcode::CubeScF32: text += "CubeScF32"; break;
|
||||
case Opcode::CubeTcF32: text += "CubeTcF32"; break;
|
||||
case Opcode::CubeMaF32: text += "CubeMaF32"; break;
|
||||
case Opcode::FAddF32: text += "FAddF32"; break;
|
||||
case Opcode::FSubF32: text += "FSubF32"; break;
|
||||
case Opcode::FMulF32: text += "FMulF32"; break;
|
||||
case Opcode::FMinF32: text += "FMinF32"; break;
|
||||
case Opcode::FMaxF32: text += "FMaxF32"; break;
|
||||
case Opcode::FMadF32: text += "FMadF32"; break;
|
||||
case Opcode::Dot2AccF32F16: text += "Dot2AccF32F16"; break;
|
||||
case Opcode::FMin3F32: text += "FMin3F32"; break;
|
||||
case Opcode::FMax3F32: text += "FMax3F32"; break;
|
||||
case Opcode::FMed3F32: text += "FMed3F32"; break;
|
||||
case Opcode::Min3F16: text += "Min3F16"; break;
|
||||
case Opcode::Max3F16: text += "Max3F16"; break;
|
||||
case Opcode::Med3F16: text += "Med3F16"; break;
|
||||
case Opcode::LoadSrtDword: text += "LoadSrtDword"; break;
|
||||
case Opcode::SLoadDword: text += "SLoadDword"; break;
|
||||
case Opcode::SBufferLoadDword: text += "SBufferLoadDword"; break;
|
||||
case Opcode::BufferLoadUbyte: text += "BufferLoadUbyte"; break;
|
||||
case Opcode::BufferLoadSbyte: text += "BufferLoadSbyte"; break;
|
||||
case Opcode::BufferLoadUshort: text += "BufferLoadUshort"; break;
|
||||
case Opcode::BufferLoadSshort: text += "BufferLoadSshort"; break;
|
||||
case Opcode::BufferLoadDword: text += "BufferLoadDword"; break;
|
||||
case Opcode::BufferStoreByte: text += "BufferStoreByte"; break;
|
||||
case Opcode::BufferStoreShort: text += "BufferStoreShort"; break;
|
||||
case Opcode::BufferStoreDword: text += "BufferStoreDword"; break;
|
||||
case Opcode::AtomicSwapU32: text += "AtomicSwapU32"; break;
|
||||
case Opcode::AtomicAddU32: text += "AtomicAddU32"; break;
|
||||
case Opcode::AtomicSubU32: text += "AtomicSubU32"; break;
|
||||
case Opcode::AtomicSMinI32: text += "AtomicSMinI32"; break;
|
||||
case Opcode::AtomicUMinU32: text += "AtomicUMinU32"; break;
|
||||
case Opcode::AtomicSMaxI32: text += "AtomicSMaxI32"; break;
|
||||
case Opcode::AtomicUMaxU32: text += "AtomicUMaxU32"; break;
|
||||
case Opcode::AtomicAndU32: text += "AtomicAndU32"; break;
|
||||
case Opcode::AtomicOrU32: text += "AtomicOrU32"; break;
|
||||
case Opcode::AtomicXorU32: text += "AtomicXorU32"; break;
|
||||
case Opcode::FlatLoadUbyte: text += "FlatLoadUbyte"; break;
|
||||
case Opcode::FlatLoadSbyte: text += "FlatLoadSbyte"; break;
|
||||
case Opcode::FlatLoadUshort: text += "FlatLoadUshort"; break;
|
||||
case Opcode::FlatLoadSshort: text += "FlatLoadSshort"; break;
|
||||
case Opcode::FlatLoadDword: text += "FlatLoadDword"; break;
|
||||
case Opcode::FlatStoreByte: text += "FlatStoreByte"; break;
|
||||
case Opcode::FlatStoreShort: text += "FlatStoreShort"; break;
|
||||
case Opcode::FlatStoreDword: text += "FlatStoreDword"; break;
|
||||
case Opcode::DsReadUbyte: text += "DsReadUbyte"; break;
|
||||
case Opcode::DsReadSbyte: text += "DsReadSbyte"; break;
|
||||
case Opcode::DsReadUshort: text += "DsReadUshort"; break;
|
||||
case Opcode::DsReadSshort: text += "DsReadSshort"; break;
|
||||
case Opcode::DsReadB32: text += "DsReadB32"; break;
|
||||
case Opcode::DsWriteByte: text += "DsWriteByte"; break;
|
||||
case Opcode::DsWriteShort: text += "DsWriteShort"; break;
|
||||
case Opcode::DsWriteB32: text += "DsWriteB32"; break;
|
||||
case Opcode::DsMinF32: text += "DsMinF32"; break;
|
||||
case Opcode::DsMaxF32: text += "DsMaxF32"; break;
|
||||
case Opcode::DsSwizzleB32: text += "DsSwizzleB32"; break;
|
||||
case Opcode::DsConsume: text += "DsConsume"; break;
|
||||
case Opcode::DsAppend: text += "DsAppend"; break;
|
||||
case Opcode::DsWriteAddtidB32: text += "DsWriteAddtidB32"; break;
|
||||
case Opcode::DsReadAddtidB32: text += "DsReadAddtidB32"; break;
|
||||
case Opcode::ImageGetResinfo: text += "ImageGetResinfo"; break;
|
||||
case Opcode::ImageGetLod: text += "ImageGetLod"; break;
|
||||
case Opcode::ImageLoad: text += "ImageLoad"; break;
|
||||
case Opcode::ImageStore: text += "ImageStore"; break;
|
||||
case Opcode::ImageSample: text += "ImageSample"; break;
|
||||
case Opcode::ImageGather4: text += "ImageGather4"; break;
|
||||
case Opcode::LoadInputF32: text += "LoadInputF32"; break;
|
||||
case Opcode::Export: text += "Export"; break;
|
||||
}
|
||||
text += OpcodeName(inst.op);
|
||||
text += " ";
|
||||
text += OperandToString(inst.dst);
|
||||
if (inst.dst2.kind != OperandKind::Null) {
|
||||
|
||||
@@ -569,6 +569,10 @@ bool LowerMemoryInstruction(const Decoder::Instruction& decoded, BasicBlock& blo
|
||||
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicOrU32, error);
|
||||
case Decoder::Opcode::BufferAtomicXor:
|
||||
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicXorU32, error);
|
||||
case Decoder::Opcode::BufferAtomicFMin:
|
||||
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicFMinF32, error);
|
||||
case Decoder::Opcode::BufferAtomicFMax:
|
||||
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicFMaxF32, error);
|
||||
case Decoder::Opcode::FlatLoadUbyte:
|
||||
case Decoder::Opcode::FlatLoadSbyte:
|
||||
case Decoder::Opcode::FlatLoadUshort:
|
||||
@@ -673,7 +677,12 @@ bool LowerMemoryInstruction(const Decoder::Instruction& decoded, BasicBlock& blo
|
||||
case Decoder::Opcode::ImageAtomicAnd:
|
||||
case Decoder::Opcode::ImageAtomicOr:
|
||||
case Decoder::Opcode::ImageAtomicXor: return LowerImageAtomicU32(decoded, block, error);
|
||||
default: return false;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("memory opcode has no specialized IR lowering: {}",
|
||||
Decoder::OpcodeToString(decoded.opcode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,6 +738,8 @@ bool IsMemoryOpcode(Decoder::Opcode opcode) {
|
||||
case Decoder::Opcode::BufferAtomicAnd:
|
||||
case Decoder::Opcode::BufferAtomicOr:
|
||||
case Decoder::Opcode::BufferAtomicXor:
|
||||
case Decoder::Opcode::BufferAtomicFMin:
|
||||
case Decoder::Opcode::BufferAtomicFMax:
|
||||
case Decoder::Opcode::FlatLoadUbyte:
|
||||
case Decoder::Opcode::FlatLoadSbyte:
|
||||
case Decoder::Opcode::FlatLoadUshort:
|
||||
|
||||
@@ -82,6 +82,7 @@ constexpr LowerMap LOWER_OPS[] = {
|
||||
{Decoder::Opcode::SCmpGeU32, Opcode::CompareGeU32},
|
||||
{Decoder::Opcode::SCmpLtU32, Opcode::CompareLtU32},
|
||||
{Decoder::Opcode::SCmpLeU32, Opcode::CompareLeU32},
|
||||
{Decoder::Opcode::SCmpEqU64, Opcode::CompareEqU64},
|
||||
{Decoder::Opcode::SCmpLgU64, Opcode::CompareNeU64},
|
||||
{Decoder::Opcode::VReadfirstlaneB32, Opcode::ReadFirstLaneU32},
|
||||
{Decoder::Opcode::VReadlaneB32, Opcode::ReadLaneU32},
|
||||
@@ -325,6 +326,7 @@ constexpr LowerMap LOWER_OPS[] = {
|
||||
{Decoder::Opcode::VCmpNeU32, Opcode::CompareNeU32},
|
||||
{Decoder::Opcode::VCmpGeU32, Opcode::CompareGeU32},
|
||||
{Decoder::Opcode::VCmpTU32, Opcode::CompareTrue},
|
||||
{Decoder::Opcode::VCmpEqI64, Opcode::CompareEqU64},
|
||||
{Decoder::Opcode::VCmpNeU64, Opcode::CompareNeU64},
|
||||
{Decoder::Opcode::VCmpxLtU32, Opcode::CompareMaskLtU32},
|
||||
{Decoder::Opcode::VCmpxEqU32, Opcode::CompareMaskEqU32},
|
||||
@@ -350,6 +352,8 @@ constexpr LowerMap LOWER_OPS[] = {
|
||||
{Decoder::Opcode::BufferAtomicAnd, Opcode::AtomicAndU32},
|
||||
{Decoder::Opcode::BufferAtomicOr, Opcode::AtomicOrU32},
|
||||
{Decoder::Opcode::BufferAtomicXor, Opcode::AtomicXorU32},
|
||||
{Decoder::Opcode::BufferAtomicFMin, Opcode::AtomicFMinF32},
|
||||
{Decoder::Opcode::BufferAtomicFMax, Opcode::AtomicFMaxF32},
|
||||
{Decoder::Opcode::FlatLoadUbyte, Opcode::FlatLoadUbyte},
|
||||
{Decoder::Opcode::FlatLoadSbyte, Opcode::FlatLoadSbyte},
|
||||
{Decoder::Opcode::FlatLoadSshort, Opcode::FlatLoadSshort},
|
||||
@@ -432,22 +436,13 @@ void SetError(std::string* error, const char* message) {
|
||||
}
|
||||
}
|
||||
|
||||
Opcode LookupIrOpcode(Decoder::Opcode opcode) {
|
||||
std::optional<Opcode> LookupIrOpcode(Decoder::Opcode opcode) {
|
||||
for (const auto& op: LOWER_OPS) {
|
||||
if (op.decoded == opcode) {
|
||||
return op.ir;
|
||||
}
|
||||
}
|
||||
return Opcode::MoveU32;
|
||||
}
|
||||
|
||||
bool IsImplemented(Decoder::Opcode opcode) {
|
||||
for (const auto& op: LOWER_OPS) {
|
||||
if (op.decoded == opcode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool IsReversedBinary(Decoder::Opcode opcode) {
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
// Central IR opcode catalog. Keep semantic categories here so enum identity, logging,
|
||||
// and generic analysis cannot drift apart.
|
||||
IR_OPCODE(ControlNop, General)
|
||||
IR_OPCODE(Waitcnt, General)
|
||||
IR_OPCODE(Barrier, General)
|
||||
IR_OPCODE(Sendmsg, General)
|
||||
IR_OPCODE(TtraceData, General)
|
||||
IR_OPCODE(InstPrefetch, General)
|
||||
IR_OPCODE(MoveU32, General)
|
||||
IR_OPCODE(MoveF32Bits, General)
|
||||
IR_OPCODE(MoveRelDestU32, General)
|
||||
IR_OPCODE(MoveRelSourceU32, General)
|
||||
IR_OPCODE(MoveU64, General)
|
||||
IR_OPCODE(WqmB64, General)
|
||||
IR_OPCODE(SaveexecB32, General)
|
||||
IR_OPCODE(SaveexecB64, General)
|
||||
IR_OPCODE(ReadFirstLaneU32, General)
|
||||
IR_OPCODE(ReadLaneU32, General)
|
||||
IR_OPCODE(WriteLaneU32, General)
|
||||
IR_OPCODE(Permlane16B32, General)
|
||||
IR_OPCODE(Permlanex16B32, General)
|
||||
IR_OPCODE(AbsI32, General)
|
||||
IR_OPCODE(IAddU32, General)
|
||||
IR_OPCODE(IAddCarryU32, General)
|
||||
IR_OPCODE(ISubBorrowU32, General)
|
||||
IR_OPCODE(ScalarAddCarryU32, General)
|
||||
IR_OPCODE(ScalarSubBorrowU32, General)
|
||||
IR_OPCODE(ScalarSubBorrowCarryU32, General)
|
||||
IR_OPCODE(ScalarSignedAddOverflowI32, General)
|
||||
IR_OPCODE(ScalarSignedSubOverflowI32, General)
|
||||
IR_OPCODE(ScalarShiftLeftAddCarryU32, General)
|
||||
IR_OPCODE(ISubU32, General)
|
||||
IR_OPCODE(IMulU32, General)
|
||||
IR_OPCODE(UMulHighU32, General)
|
||||
IR_OPCODE(SMulHighI32, General)
|
||||
IR_OPCODE(IMadI24U32, General)
|
||||
IR_OPCODE(UMadU24U32, General)
|
||||
IR_OPCODE(UMadU64U32, General)
|
||||
IR_OPCODE(SadU32, General)
|
||||
IR_OPCODE(IAdd3U32, General)
|
||||
IR_OPCODE(IMulI24U32, General)
|
||||
IR_OPCODE(UMulU24U32, General)
|
||||
IR_OPCODE(IMinI32, General)
|
||||
IR_OPCODE(IMaxI32, General)
|
||||
IR_OPCODE(IMin3I32, General)
|
||||
IR_OPCODE(IMax3I32, General)
|
||||
IR_OPCODE(IMed3I32, General)
|
||||
IR_OPCODE(UMinU32, General)
|
||||
IR_OPCODE(UMaxU32, General)
|
||||
IR_OPCODE(UMin3U32, General)
|
||||
IR_OPCODE(UMax3U32, General)
|
||||
IR_OPCODE(UMed3U32, General)
|
||||
IR_OPCODE(BitwiseAndU32, General)
|
||||
IR_OPCODE(BitwiseAndU64, General)
|
||||
IR_OPCODE(BitwiseAndNotU32, General)
|
||||
IR_OPCODE(BitwiseAndNotU64, General)
|
||||
IR_OPCODE(BitwiseOrU32, General)
|
||||
IR_OPCODE(BitwiseOrU64, General)
|
||||
IR_OPCODE(BitwiseOrNotU32, General)
|
||||
IR_OPCODE(BitwiseOrNotU64, General)
|
||||
IR_OPCODE(BitwiseAndOrU32, General)
|
||||
IR_OPCODE(BitwiseOr3U32, General)
|
||||
IR_OPCODE(BitwiseXorU32, General)
|
||||
IR_OPCODE(BitwiseXorU64, General)
|
||||
IR_OPCODE(BitwiseXor3U32, General)
|
||||
IR_OPCODE(BitwiseNandU32, General)
|
||||
IR_OPCODE(BitwiseNandU64, General)
|
||||
IR_OPCODE(BitwiseNorU32, General)
|
||||
IR_OPCODE(BitwiseNorU64, General)
|
||||
IR_OPCODE(BitwiseXnorU32, General)
|
||||
IR_OPCODE(BitwiseXnorU64, General)
|
||||
IR_OPCODE(BitwiseNotU32, General)
|
||||
IR_OPCODE(BitwiseNotU64, General)
|
||||
IR_OPCODE(BitClearU32, General)
|
||||
IR_OPCODE(BitSetU32, General)
|
||||
IR_OPCODE(BitReverseU32, General)
|
||||
IR_OPCODE(BitCountU32, General)
|
||||
IR_OPCODE(BitCountU64, General)
|
||||
IR_OPCODE(BitReplicateB64B32, General)
|
||||
IR_OPCODE(BitCountAddU32, General)
|
||||
IR_OPCODE(MaskedBitCountLowU32, General)
|
||||
IR_OPCODE(MaskedBitCountHighU32, General)
|
||||
IR_OPCODE(FindLsbU32, General)
|
||||
IR_OPCODE(FindMsbFromHighU32, General)
|
||||
IR_OPCODE(FindMsbFromHighU64, General)
|
||||
IR_OPCODE(BitFieldMaskU32, General)
|
||||
IR_OPCODE(BitFieldMaskU64, General)
|
||||
IR_OPCODE(BitFieldExtractU32, General)
|
||||
IR_OPCODE(BitFieldExtractU64, General)
|
||||
IR_OPCODE(BitFieldExtract3U32, General)
|
||||
IR_OPCODE(BitFieldExtract3I32, General)
|
||||
IR_OPCODE(BitFieldInsertSelectU32, General)
|
||||
IR_OPCODE(BitCompare0B32, General)
|
||||
IR_OPCODE(BitCompare1B32, General)
|
||||
IR_OPCODE(AlignBitU32, General)
|
||||
IR_OPCODE(ShiftLeftAddU32, General)
|
||||
IR_OPCODE(AddShiftLeftU32, General)
|
||||
IR_OPCODE(XorAddU32, General)
|
||||
IR_OPCODE(ShiftLeftOrU32, General)
|
||||
IR_OPCODE(ShiftLeftLogicalU32, General)
|
||||
IR_OPCODE(ShiftLeftLogicalU64, General)
|
||||
IR_OPCODE(ShiftLeftLogicalU16, General)
|
||||
IR_OPCODE(ShiftRightLogicalU32, General)
|
||||
IR_OPCODE(ShiftRightLogicalU64, General)
|
||||
IR_OPCODE(ShiftRightLogicalU16, General)
|
||||
IR_OPCODE(ShiftRightArithmeticI32, General)
|
||||
IR_OPCODE(ShiftRightArithmeticI16, General)
|
||||
IR_OPCODE(SelectU32, General)
|
||||
IR_OPCODE(SelectMaskU32, General)
|
||||
IR_OPCODE(SelectF32Bits, General)
|
||||
IR_OPCODE(SelectMaskF32Bits, General)
|
||||
IR_OPCODE(SelectU64, General)
|
||||
IR_OPCODE(PackLowLowU16, General)
|
||||
IR_OPCODE(PackLowHighU16, General)
|
||||
IR_OPCODE(PackHighHighU16, General)
|
||||
IR_OPCODE(CompareFalse, Compare)
|
||||
IR_OPCODE(CompareTrue, Compare)
|
||||
IR_OPCODE(CompareEqU32, Compare)
|
||||
IR_OPCODE(CompareNeU32, Compare)
|
||||
IR_OPCODE(CompareGtU32, Compare)
|
||||
IR_OPCODE(CompareGeU32, Compare)
|
||||
IR_OPCODE(CompareLtU32, Compare)
|
||||
IR_OPCODE(CompareLeU32, Compare)
|
||||
IR_OPCODE(CompareEqU64, Compare64)
|
||||
IR_OPCODE(CompareNeU64, Compare64)
|
||||
IR_OPCODE(CompareMaskEqU32, Compare)
|
||||
IR_OPCODE(CompareMaskNeU32, Compare)
|
||||
IR_OPCODE(CompareMaskGtU32, Compare)
|
||||
IR_OPCODE(CompareMaskGeU32, Compare)
|
||||
IR_OPCODE(CompareMaskLtU32, Compare)
|
||||
IR_OPCODE(CompareMaskLeU32, Compare)
|
||||
IR_OPCODE(CompareEqI32, Compare)
|
||||
IR_OPCODE(CompareNeI32, Compare)
|
||||
IR_OPCODE(CompareGtI32, Compare)
|
||||
IR_OPCODE(CompareGeI32, Compare)
|
||||
IR_OPCODE(CompareLtI32, Compare)
|
||||
IR_OPCODE(CompareLeI32, Compare)
|
||||
IR_OPCODE(CompareEqI16, Compare)
|
||||
IR_OPCODE(CompareNeI16, Compare)
|
||||
IR_OPCODE(CompareGtI16, Compare)
|
||||
IR_OPCODE(CompareGeI16, Compare)
|
||||
IR_OPCODE(CompareLtI16, Compare)
|
||||
IR_OPCODE(CompareLeI16, Compare)
|
||||
IR_OPCODE(CompareMaskEqI32, Compare)
|
||||
IR_OPCODE(CompareMaskNeI32, Compare)
|
||||
IR_OPCODE(CompareMaskGtI32, Compare)
|
||||
IR_OPCODE(CompareMaskGeI32, Compare)
|
||||
IR_OPCODE(CompareMaskLtI32, Compare)
|
||||
IR_OPCODE(CompareMaskLeI32, Compare)
|
||||
IR_OPCODE(CompareEqU16, Compare)
|
||||
IR_OPCODE(CompareNeU16, Compare)
|
||||
IR_OPCODE(CompareGtU16, Compare)
|
||||
IR_OPCODE(CompareGeU16, Compare)
|
||||
IR_OPCODE(CompareLtU16, Compare)
|
||||
IR_OPCODE(CompareLeU16, Compare)
|
||||
IR_OPCODE(CompareEqF32, Compare)
|
||||
IR_OPCODE(CompareNeF32, Compare)
|
||||
IR_OPCODE(CompareGtF32, Compare)
|
||||
IR_OPCODE(CompareGeF32, Compare)
|
||||
IR_OPCODE(CompareLtF32, Compare)
|
||||
IR_OPCODE(CompareLeF32, Compare)
|
||||
IR_OPCODE(CompareOrderedF32, Compare)
|
||||
IR_OPCODE(CompareUnorderedF32, Compare)
|
||||
IR_OPCODE(CompareUnordEqF32, Compare)
|
||||
IR_OPCODE(CompareUnordNeF32, Compare)
|
||||
IR_OPCODE(CompareUnordGtF32, Compare)
|
||||
IR_OPCODE(CompareUnordGeF32, Compare)
|
||||
IR_OPCODE(CompareUnordLtF32, Compare)
|
||||
IR_OPCODE(CompareUnordLeF32, Compare)
|
||||
IR_OPCODE(CompareClassF32, Compare)
|
||||
IR_OPCODE(CompareEqF16, Compare)
|
||||
IR_OPCODE(CompareNeF16, Compare)
|
||||
IR_OPCODE(CompareGtF16, Compare)
|
||||
IR_OPCODE(CompareGeF16, Compare)
|
||||
IR_OPCODE(CompareLtF16, Compare)
|
||||
IR_OPCODE(CompareLeF16, Compare)
|
||||
IR_OPCODE(CompareUnordNeF16, Compare)
|
||||
IR_OPCODE(CompareMaskEqF16, Compare)
|
||||
IR_OPCODE(CompareMaskNeF16, Compare)
|
||||
IR_OPCODE(CompareMaskGtF16, Compare)
|
||||
IR_OPCODE(CompareMaskGeF16, Compare)
|
||||
IR_OPCODE(CompareMaskLtF16, Compare)
|
||||
IR_OPCODE(CompareMaskLeF16, Compare)
|
||||
IR_OPCODE(CompareMaskUnordNeF16, Compare)
|
||||
IR_OPCODE(CompareMaskUnordGeF16, Compare)
|
||||
IR_OPCODE(CompareMaskEqF32, Compare)
|
||||
IR_OPCODE(CompareMaskNeF32, Compare)
|
||||
IR_OPCODE(CompareMaskGtF32, Compare)
|
||||
IR_OPCODE(CompareMaskGeF32, Compare)
|
||||
IR_OPCODE(CompareMaskLtF32, Compare)
|
||||
IR_OPCODE(CompareMaskLeF32, Compare)
|
||||
IR_OPCODE(CompareMaskUnordEqF32, Compare)
|
||||
IR_OPCODE(CompareMaskUnordNeF32, Compare)
|
||||
IR_OPCODE(CompareMaskUnordGtF32, Compare)
|
||||
IR_OPCODE(CompareMaskUnordGeF32, Compare)
|
||||
IR_OPCODE(CompareMaskUnordLtF32, Compare)
|
||||
IR_OPCODE(CompareMaskUnordLeF32, Compare)
|
||||
IR_OPCODE(ConvertByteU32ToF32, General)
|
||||
IR_OPCODE(ConvertU32ToF32, General)
|
||||
IR_OPCODE(ConvertI32ToF32, General)
|
||||
IR_OPCODE(ConvertF32ToU32, General)
|
||||
IR_OPCODE(ConvertF32ToI32, General)
|
||||
IR_OPCODE(ConvertF32ToF16, General)
|
||||
IR_OPCODE(ConvertF16ToF32, General)
|
||||
IR_OPCODE(ConvertU16ToF16, General)
|
||||
IR_OPCODE(ConvertF16ToU16, General)
|
||||
IR_OPCODE(ConvertI16ToF16, General)
|
||||
IR_OPCODE(ConvertF16ToI16, General)
|
||||
IR_OPCODE(ConvertRoundPlusInfF32ToI32, General)
|
||||
IR_OPCODE(ConvertFloorF32ToI32, General)
|
||||
IR_OPCODE(ConvertI4ToOffsetF32, General)
|
||||
IR_OPCODE(LdexpF32, General)
|
||||
IR_OPCODE(PackF32ToF16Rtz, General)
|
||||
IR_OPCODE(PackSnorm2x16F32, General)
|
||||
IR_OPCODE(PackUnorm2x16F32, General)
|
||||
IR_OPCODE(PackU16U32, General)
|
||||
IR_OPCODE(PackU8F32, General)
|
||||
IR_OPCODE(PackB32F16, General)
|
||||
IR_OPCODE(PackedMadI16, General)
|
||||
IR_OPCODE(PackedMulLoU16, General)
|
||||
IR_OPCODE(PackedAddI16, General)
|
||||
IR_OPCODE(PackedSubI16, General)
|
||||
IR_OPCODE(PackedLshlrevB16, General)
|
||||
IR_OPCODE(PackedLshrrevB16, General)
|
||||
IR_OPCODE(PackedAshrrevI16, General)
|
||||
IR_OPCODE(PackedMaxI16, General)
|
||||
IR_OPCODE(PackedMinI16, General)
|
||||
IR_OPCODE(PackedMadU16, General)
|
||||
IR_OPCODE(PackedAddU16, General)
|
||||
IR_OPCODE(PackedSubU16, General)
|
||||
IR_OPCODE(PackedMaxU16, General)
|
||||
IR_OPCODE(PackedMinU16, General)
|
||||
IR_OPCODE(PackedAddF16, General)
|
||||
IR_OPCODE(PackedMulF16, General)
|
||||
IR_OPCODE(PackedMinF16, General)
|
||||
IR_OPCODE(PackedMaxF16, General)
|
||||
IR_OPCODE(PackedFmaF16, General)
|
||||
IR_OPCODE(AddF16, General)
|
||||
IR_OPCODE(SubF16, General)
|
||||
IR_OPCODE(MulF16, General)
|
||||
IR_OPCODE(MinF16, General)
|
||||
IR_OPCODE(MaxF16, General)
|
||||
IR_OPCODE(FmaF16, General)
|
||||
IR_OPCODE(MadMixF16, General)
|
||||
IR_OPCODE(IAddU16, General)
|
||||
IR_OPCODE(ISubI16, General)
|
||||
IR_OPCODE(IMinI16, General)
|
||||
IR_OPCODE(IMaxI16, General)
|
||||
IR_OPCODE(UMinU16, General)
|
||||
IR_OPCODE(UMaxU16, General)
|
||||
IR_OPCODE(RcpF32, General)
|
||||
IR_OPCODE(FractF32, General)
|
||||
IR_OPCODE(TruncF32, General)
|
||||
IR_OPCODE(CeilF32, General)
|
||||
IR_OPCODE(RoundEvenF32, General)
|
||||
IR_OPCODE(FloorF32, General)
|
||||
IR_OPCODE(Exp2F32, General)
|
||||
IR_OPCODE(Log2F32, General)
|
||||
IR_OPCODE(InverseSqrtF32, General)
|
||||
IR_OPCODE(SqrtF32, General)
|
||||
IR_OPCODE(RcpF16, General)
|
||||
IR_OPCODE(SqrtF16, General)
|
||||
IR_OPCODE(InverseSqrtF16, General)
|
||||
IR_OPCODE(Log2F16, General)
|
||||
IR_OPCODE(Exp2F16, General)
|
||||
IR_OPCODE(FloorF16, General)
|
||||
IR_OPCODE(CeilF16, General)
|
||||
IR_OPCODE(TruncF16, General)
|
||||
IR_OPCODE(RoundEvenF16, General)
|
||||
IR_OPCODE(SinF32, General)
|
||||
IR_OPCODE(CosF32, General)
|
||||
IR_OPCODE(CubeIdF32, General)
|
||||
IR_OPCODE(CubeScF32, General)
|
||||
IR_OPCODE(CubeTcF32, General)
|
||||
IR_OPCODE(CubeMaF32, General)
|
||||
IR_OPCODE(FAddF32, General)
|
||||
IR_OPCODE(FSubF32, General)
|
||||
IR_OPCODE(FMulF32, General)
|
||||
IR_OPCODE(FMinF32, General)
|
||||
IR_OPCODE(FMaxF32, General)
|
||||
IR_OPCODE(FMadF32, General)
|
||||
IR_OPCODE(Dot2AccF32F16, General)
|
||||
IR_OPCODE(FMin3F32, General)
|
||||
IR_OPCODE(FMax3F32, General)
|
||||
IR_OPCODE(FMed3F32, General)
|
||||
IR_OPCODE(Min3F16, General)
|
||||
IR_OPCODE(Max3F16, General)
|
||||
IR_OPCODE(Med3F16, General)
|
||||
IR_OPCODE(LoadSrtDword, General)
|
||||
IR_OPCODE(SLoadDword, General)
|
||||
IR_OPCODE(SBufferLoadDword, General)
|
||||
IR_OPCODE(BufferLoadUbyte, General)
|
||||
IR_OPCODE(BufferLoadSbyte, General)
|
||||
IR_OPCODE(BufferLoadUshort, General)
|
||||
IR_OPCODE(BufferLoadSshort, General)
|
||||
IR_OPCODE(BufferLoadDword, General)
|
||||
IR_OPCODE(BufferStoreByte, General)
|
||||
IR_OPCODE(BufferStoreShort, General)
|
||||
IR_OPCODE(BufferStoreDword, General)
|
||||
IR_OPCODE(AtomicSwapU32, General)
|
||||
IR_OPCODE(AtomicAddU32, General)
|
||||
IR_OPCODE(AtomicSubU32, General)
|
||||
IR_OPCODE(AtomicSMinI32, General)
|
||||
IR_OPCODE(AtomicUMinU32, General)
|
||||
IR_OPCODE(AtomicSMaxI32, General)
|
||||
IR_OPCODE(AtomicUMaxU32, General)
|
||||
IR_OPCODE(AtomicAndU32, General)
|
||||
IR_OPCODE(AtomicOrU32, General)
|
||||
IR_OPCODE(AtomicXorU32, General)
|
||||
IR_OPCODE(AtomicFMinF32, General)
|
||||
IR_OPCODE(AtomicFMaxF32, General)
|
||||
IR_OPCODE(FlatLoadUbyte, General)
|
||||
IR_OPCODE(FlatLoadSbyte, General)
|
||||
IR_OPCODE(FlatLoadUshort, General)
|
||||
IR_OPCODE(FlatLoadSshort, General)
|
||||
IR_OPCODE(FlatLoadDword, General)
|
||||
IR_OPCODE(FlatStoreByte, General)
|
||||
IR_OPCODE(FlatStoreShort, General)
|
||||
IR_OPCODE(FlatStoreDword, General)
|
||||
IR_OPCODE(DsReadUbyte, General)
|
||||
IR_OPCODE(DsReadSbyte, General)
|
||||
IR_OPCODE(DsReadUshort, General)
|
||||
IR_OPCODE(DsReadSshort, General)
|
||||
IR_OPCODE(DsReadB32, General)
|
||||
IR_OPCODE(DsWriteByte, General)
|
||||
IR_OPCODE(DsWriteShort, General)
|
||||
IR_OPCODE(DsWriteB32, General)
|
||||
IR_OPCODE(DsMinF32, General)
|
||||
IR_OPCODE(DsMaxF32, General)
|
||||
IR_OPCODE(DsSwizzleB32, General)
|
||||
IR_OPCODE(DsConsume, General)
|
||||
IR_OPCODE(DsAppend, General)
|
||||
IR_OPCODE(DsWriteAddtidB32, General)
|
||||
IR_OPCODE(DsReadAddtidB32, General)
|
||||
IR_OPCODE(ImageGetResinfo, General)
|
||||
IR_OPCODE(ImageGetLod, General)
|
||||
IR_OPCODE(ImageLoad, General)
|
||||
IR_OPCODE(ImageStore, General)
|
||||
IR_OPCODE(ImageSample, General)
|
||||
IR_OPCODE(ImageGather4, General)
|
||||
IR_OPCODE(LoadInputF32, General)
|
||||
IR_OPCODE(Export, General)
|
||||
@@ -56,6 +56,7 @@ public:
|
||||
void Disconnect(int id);
|
||||
void Button(int id, uint32_t button, bool down);
|
||||
void Axis(int id, Axis axis, int value);
|
||||
void ResetInputState();
|
||||
void GetConnectionInfo(bool* flag, int* count);
|
||||
void ReadState(ControllerState* state, bool* flag, int* count);
|
||||
int ReadStates(ControllerState* states, int states_num, bool* flag, int* count);
|
||||
@@ -256,6 +257,15 @@ void GameController::Axis(int id, Controller::Axis axis, int value) {
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::ResetInputState() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
ControllerState state {};
|
||||
state.time = LibKernel::KernelGetProcessTime();
|
||||
m_states_num = 0;
|
||||
m_first_state = 0;
|
||||
AddState(state);
|
||||
}
|
||||
|
||||
void GameController::GetConnectionInfo(bool* flag, int* count) {
|
||||
EXIT_IF(flag == nullptr);
|
||||
EXIT_IF(count == nullptr);
|
||||
@@ -334,6 +344,11 @@ void ControllerAxis(int id, Axis axis, int value) {
|
||||
g_controller->Axis(id, axis, value);
|
||||
}
|
||||
|
||||
void ControllerResetInputState() {
|
||||
EXIT_IF(g_controller == nullptr);
|
||||
g_controller->ResetInputState();
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PadInit() {
|
||||
PRINT_NAME();
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ void ControllerConnect(int id);
|
||||
void ControllerDisconnect(int id);
|
||||
void ControllerButton(int id, uint32_t button, bool down);
|
||||
void ControllerAxis(int id, Axis axis, int value);
|
||||
void ControllerResetInputState();
|
||||
|
||||
int KYTY_SYSV_ABI PadInit();
|
||||
int KYTY_SYSV_ABI PadOpen(int user_id, int type, int index, const void* param);
|
||||
|
||||
@@ -23,20 +23,6 @@ int KYTY_SYSV_ABI CommonDialogInitialize() {
|
||||
|
||||
} // namespace CommonDialog
|
||||
|
||||
namespace ImeDialog {
|
||||
|
||||
LIB_NAME("ImeDialog", "ImeDialog");
|
||||
|
||||
constexpr int IME_STATUS_NONE = 0;
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogGetStatus() {
|
||||
PRINT_NAME();
|
||||
|
||||
return IME_STATUS_NONE;
|
||||
}
|
||||
|
||||
} // namespace ImeDialog
|
||||
|
||||
namespace LoginDialog {
|
||||
|
||||
LIB_NAME("LoginDialog", "LoginDialog");
|
||||
|
||||
@@ -12,12 +12,6 @@ int KYTY_SYSV_ABI CommonDialogInitialize();
|
||||
|
||||
} // namespace CommonDialog
|
||||
|
||||
namespace ImeDialog {
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogGetStatus();
|
||||
|
||||
} // namespace ImeDialog
|
||||
|
||||
namespace LoginDialog {
|
||||
|
||||
int KYTY_SYSV_ABI LoginDialogInitialize();
|
||||
|
||||
@@ -0,0 +1,781 @@
|
||||
#include "libs/imeDialog.h"
|
||||
|
||||
#include "libs/errno.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Dialog::ImeDialog {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int Error(uint32_t value) {
|
||||
return static_cast<int32_t>(value);
|
||||
}
|
||||
|
||||
constexpr int ERROR_BUSY = Error(0x80bc0001);
|
||||
constexpr int ERROR_INVALID_USER_ID = Error(0x80bc0010);
|
||||
constexpr int ERROR_INVALID_TYPE = Error(0x80bc0011);
|
||||
constexpr int ERROR_INVALID_LANGUAGES = Error(0x80bc0012);
|
||||
constexpr int ERROR_INVALID_ENTER_LABEL = Error(0x80bc0013);
|
||||
constexpr int ERROR_INVALID_INPUT_METHOD = Error(0x80bc0014);
|
||||
constexpr int ERROR_INVALID_OPTION = Error(0x80bc0015);
|
||||
constexpr int ERROR_INVALID_MAX_TEXT_LENGTH = Error(0x80bc0016);
|
||||
constexpr int ERROR_INVALID_TEXT_BUFFER = Error(0x80bc0017);
|
||||
constexpr int ERROR_INVALID_POSX = Error(0x80bc0018);
|
||||
constexpr int ERROR_INVALID_POSY = Error(0x80bc0019);
|
||||
constexpr int ERROR_INVALID_HALIGN = Error(0x80bc001a);
|
||||
constexpr int ERROR_INVALID_VALIGN = Error(0x80bc001b);
|
||||
constexpr int ERROR_INVALID_EXTENDED = Error(0x80bc001c);
|
||||
constexpr int ERROR_INVALID_PARAM = Error(0x80bc0030);
|
||||
constexpr int ERROR_INVALID_ADDRESS = Error(0x80bc0031);
|
||||
constexpr int ERROR_INVALID_RESERVED = Error(0x80bc0032);
|
||||
constexpr int ERROR_INVALID_TITLE = Error(0x80bc0101);
|
||||
constexpr int ERROR_NOT_RUNNING = Error(0x80bc0105);
|
||||
constexpr int ERROR_NOT_FINISHED = Error(0x80bc0106);
|
||||
constexpr int ERROR_NOT_IN_USE = Error(0x80bc0107);
|
||||
|
||||
constexpr uint32_t VALID_OPTIONS = 0x00007bff;
|
||||
constexpr uint32_t VALID_EXTENDED_OPTIONS = 0x00005fde;
|
||||
constexpr uint32_t VALID_EXT_KEYBOARD_MODE = 0x1c000003;
|
||||
constexpr uint64_t VALID_LANGUAGES = 0x00000001ff1fffffULL;
|
||||
|
||||
struct State {
|
||||
Status status = Status::None;
|
||||
EndStatus end_status = EndStatus::Ok;
|
||||
uint64_t generation = 0;
|
||||
uint64_t revision = 0;
|
||||
Param param {};
|
||||
ExtendedParam extended {};
|
||||
bool input_changed = false;
|
||||
bool commit_pending = false;
|
||||
uint32_t cursor = 0;
|
||||
std::u16string original_text;
|
||||
std::u16string current_text;
|
||||
std::u16string title;
|
||||
std::u16string placeholder;
|
||||
std::vector<ExternalInput> external_inputs;
|
||||
};
|
||||
|
||||
std::mutex g_mutex;
|
||||
State g_state;
|
||||
std::atomic<Status> g_status {Status::None};
|
||||
std::atomic<uint64_t> g_revision {0};
|
||||
std::atomic<VisibilityCallback> g_visibility_callback {nullptr};
|
||||
|
||||
bool AllZero(const int8_t* data, size_t size) {
|
||||
return std::all_of(data, data + size, [](int8_t value) { return value == 0; });
|
||||
}
|
||||
|
||||
bool IsValidUtf16(std::u16string_view text);
|
||||
|
||||
bool ReadBounded(const char16_t* text, uint32_t limit, std::u16string* out) {
|
||||
out->clear();
|
||||
if (text == nullptr) {
|
||||
return true;
|
||||
}
|
||||
out->reserve(limit);
|
||||
for (uint32_t i = 0; i <= limit; ++i) {
|
||||
const char16_t value = text[i];
|
||||
if (value == u'\0') {
|
||||
return IsValidUtf16(*out);
|
||||
}
|
||||
if (i == limit) {
|
||||
return false;
|
||||
}
|
||||
out->push_back(value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsValidUtf16(std::u16string_view text) {
|
||||
for (size_t i = 0; i < text.size(); i++) {
|
||||
const char16_t current = text[i];
|
||||
if (current >= 0xd800 && current <= 0xdbff) {
|
||||
if (++i >= text.size() || text[i] < 0xdc00 || text[i] > 0xdfff) {
|
||||
return false;
|
||||
}
|
||||
} else if (current >= 0xdc00 && current <= 0xdfff) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsAllowedInput(char16_t value, Type type, uint32_t option) {
|
||||
if (value == u'\r') {
|
||||
return false;
|
||||
}
|
||||
if (value == u'\n') {
|
||||
return (option & OPTION_MULTILINE) != 0;
|
||||
}
|
||||
if (value == u'\0') {
|
||||
return false;
|
||||
}
|
||||
if (type == Type::Number) {
|
||||
return (value >= u'0' && value <= u'9') || value == u',' || value == u'-' || value == u'.';
|
||||
}
|
||||
if (type == Type::BasicLatin) {
|
||||
return value >= u' ' && value <= u'~';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char16_t HidCharacter(uint16_t keycode, uint32_t status) {
|
||||
const bool shift = (status & 0x00002200) != 0;
|
||||
const bool caps = (status & 0x00020000) != 0;
|
||||
if (keycode >= 4 && keycode <= 29) {
|
||||
const bool upper = shift != caps;
|
||||
return static_cast<char16_t>((upper ? u'A' : u'a') + keycode - 4);
|
||||
}
|
||||
if (keycode >= 30 && keycode <= 39) {
|
||||
static constexpr char16_t plain[] = u"1234567890";
|
||||
static constexpr char16_t shifted[] = u"!@#$%^&*()";
|
||||
return (shift ? shifted : plain)[keycode - 30];
|
||||
}
|
||||
if (keycode == 44) {
|
||||
return u' ';
|
||||
}
|
||||
if (keycode >= 45 && keycode <= 56) {
|
||||
static constexpr char16_t plain[] = u"-=[]\\#;'`,./";
|
||||
static constexpr char16_t shifted[] = u"_+{}|~:\"~<>?";
|
||||
return (shift ? shifted : plain)[keycode - 45];
|
||||
}
|
||||
if (keycode >= 89 && keycode <= 98) {
|
||||
static constexpr char16_t keypad[] = u"1234567890";
|
||||
return keypad[keycode - 89];
|
||||
}
|
||||
if (keycode == 99) {
|
||||
return u'.';
|
||||
}
|
||||
return u'\0';
|
||||
}
|
||||
|
||||
bool ApplyKeyboardFilterOutput(ExternalInput* input, uint16_t keycode, uint32_t status,
|
||||
bool multiline) {
|
||||
constexpr uint32_t KEYCODE_VALID = 0x00000001;
|
||||
constexpr uint32_t CHARACTER_VALID = 0x00000002;
|
||||
if (keycode == input->key.keycode && status == input->key.status) {
|
||||
return true;
|
||||
}
|
||||
if ((status & KEYCODE_VALID) == 0 || keycode == 0) {
|
||||
return input->action == ExternalAction::Text && (status & CHARACTER_VALID) != 0;
|
||||
}
|
||||
input->key.keycode = keycode;
|
||||
input->key.status = status;
|
||||
input->text.clear();
|
||||
switch (keycode) {
|
||||
case 40:
|
||||
case 88:
|
||||
case 158:
|
||||
input->action = multiline ? ExternalAction::Newline : ExternalAction::Accept;
|
||||
break;
|
||||
case 41: input->action = ExternalAction::Cancel; break;
|
||||
case 42:
|
||||
case 187: input->action = ExternalAction::Backspace; break;
|
||||
case 43:
|
||||
case 186: input->action = ExternalAction::None; break;
|
||||
case 79: input->action = ExternalAction::MoveRight; break;
|
||||
case 80: input->action = ExternalAction::MoveLeft; break;
|
||||
default: {
|
||||
const char16_t character = HidCharacter(keycode, status);
|
||||
if (character == u'\0') {
|
||||
return false;
|
||||
}
|
||||
input->action = ExternalAction::Text;
|
||||
input->key.character = character;
|
||||
input->text.push_back(character);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClampText(std::u16string* text, uint32_t limit) {
|
||||
if (text->size() <= limit) {
|
||||
return;
|
||||
}
|
||||
text->resize(limit);
|
||||
if (!text->empty() && text->back() >= 0xd800 && text->back() <= 0xdbff) {
|
||||
text->pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t NormalizeCursor(std::u16string_view text, uint32_t cursor) {
|
||||
cursor = std::min<uint32_t>(cursor, static_cast<uint32_t>(text.size()));
|
||||
if (cursor > 0 && cursor < text.size() && text[cursor - 1] >= 0xd800 &&
|
||||
text[cursor - 1] <= 0xdbff && text[cursor] >= 0xdc00 && text[cursor] <= 0xdfff) {
|
||||
cursor++;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
void WriteGuestText(const State& state, const std::u16string& text) {
|
||||
if (state.param.input_text_buffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
const size_t size = std::min<size_t>(text.size(), state.param.max_text_length);
|
||||
std::memcpy(state.param.input_text_buffer, text.data(), size * sizeof(char16_t));
|
||||
state.param.input_text_buffer[size] = u'\0';
|
||||
}
|
||||
|
||||
void NotifyVisibility(bool visible, uint64_t generation) {
|
||||
if (const auto callback = g_visibility_callback.load(std::memory_order_acquire);
|
||||
callback != nullptr) {
|
||||
callback(visible, generation);
|
||||
}
|
||||
}
|
||||
|
||||
int ValidateExtended(const ExtendedParam* extended) {
|
||||
if (extended == nullptr) {
|
||||
return OK;
|
||||
}
|
||||
if ((extended->option & ~VALID_EXTENDED_OPTIONS) != 0 ||
|
||||
((extended->option & 0x00004000) != 0 && (extended->option & 0x00000080) == 0) ||
|
||||
extended->priority > 3 || extended->disable_device > 7 ||
|
||||
(extended->ext_keyboard_mode & ~VALID_EXT_KEYBOARD_MODE) != 0 ||
|
||||
!AllZero(extended->reserved, sizeof(extended->reserved))) {
|
||||
return ERROR_INVALID_EXTENDED;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
int ValidateParam(const Param* param, const ExtendedParam* extended, std::u16string* initial,
|
||||
std::u16string* title, std::u16string* placeholder) {
|
||||
if (param == nullptr) {
|
||||
return ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
if (static_cast<uint32_t>(param->type) > static_cast<uint32_t>(Type::Number)) {
|
||||
return ERROR_INVALID_TYPE;
|
||||
}
|
||||
if ((param->option & ~VALID_OPTIONS) != 0) {
|
||||
return ERROR_INVALID_OPTION;
|
||||
}
|
||||
if ((param->supported_languages & ~VALID_LANGUAGES) != 0) {
|
||||
return ERROR_INVALID_LANGUAGES;
|
||||
}
|
||||
const bool over_2k = (param->option & OPTION_USE_OVER_2K) != 0;
|
||||
const float max_x = over_2k ? 3840.0f : 1920.0f;
|
||||
const float max_y = over_2k ? 2160.0f : 1080.0f;
|
||||
if (!std::isfinite(param->posx) || param->posx < 0.0f || param->posx >= max_x) {
|
||||
return ERROR_INVALID_POSX;
|
||||
}
|
||||
if (!std::isfinite(param->posy) || param->posy < 0.0f || param->posy >= max_y) {
|
||||
return ERROR_INVALID_POSY;
|
||||
}
|
||||
if (static_cast<uint32_t>(param->horizontal_alignment) > 2) {
|
||||
return ERROR_INVALID_HALIGN;
|
||||
}
|
||||
if (static_cast<uint32_t>(param->vertical_alignment) > 2) {
|
||||
return ERROR_INVALID_VALIGN;
|
||||
}
|
||||
const bool multiline = (param->option & OPTION_MULTILINE) != 0;
|
||||
const bool password = (param->option & OPTION_PASSWORD) != 0;
|
||||
if ((multiline && password) ||
|
||||
(multiline && param->type != Type::Default && param->type != Type::BasicLatin) ||
|
||||
(password && param->type != Type::BasicLatin && param->type != Type::Number)) {
|
||||
return ERROR_INVALID_PARAM;
|
||||
}
|
||||
if (param->user_id < 0 || param->user_id == 0xff) {
|
||||
return ERROR_INVALID_USER_ID;
|
||||
}
|
||||
if (!AllZero(param->reserved, sizeof(param->reserved))) {
|
||||
return ERROR_INVALID_RESERVED;
|
||||
}
|
||||
if (param->input_text_buffer == nullptr) {
|
||||
return ERROR_INVALID_TEXT_BUFFER;
|
||||
}
|
||||
const int extended_result = ValidateExtended(extended);
|
||||
if (extended_result != OK) {
|
||||
return extended_result;
|
||||
}
|
||||
if (static_cast<uint32_t>(param->enter_label) > static_cast<uint32_t>(EnterLabel::Go)) {
|
||||
return ERROR_INVALID_ENTER_LABEL;
|
||||
}
|
||||
if (param->input_method != 0) {
|
||||
return ERROR_INVALID_INPUT_METHOD;
|
||||
}
|
||||
if (param->max_text_length == 0 || param->max_text_length > IME_DIALOG_MAX_TEXT_LENGTH) {
|
||||
return ERROR_INVALID_MAX_TEXT_LENGTH;
|
||||
}
|
||||
if (!ReadBounded(param->input_text_buffer, param->max_text_length, initial)) {
|
||||
return ERROR_INVALID_TEXT_BUFFER;
|
||||
}
|
||||
if (!ReadBounded(param->title, IME_DIALOG_MAX_TITLE_LENGTH, title)) {
|
||||
return ERROR_INVALID_TITLE;
|
||||
}
|
||||
if (!ReadBounded(param->placeholder, IME_DIALOG_MAX_PLACEHOLDER_LENGTH, placeholder)) {
|
||||
return ERROR_INVALID_PARAM;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void ComputePanelSize(const Param& param, const ExtendedParam* extended, uint32_t* width,
|
||||
uint32_t* height) {
|
||||
const bool multiline = (param.option & OPTION_MULTILINE) != 0;
|
||||
const bool hide_keyboard = extended != nullptr && (param.option & OPTION_EXT_KEYBOARD) != 0 &&
|
||||
(extended->option & 0x00000400) != 0;
|
||||
if (param.type == Type::Number) {
|
||||
*width = 370;
|
||||
*height = hide_keyboard ? 102 : 522;
|
||||
} else if (param.type == Type::BasicLatin) {
|
||||
*width = 793;
|
||||
if (hide_keyboard) {
|
||||
*height = multiline ? 203 : 103;
|
||||
} else {
|
||||
*height = multiline ? 628 : 528;
|
||||
}
|
||||
} else {
|
||||
*width = 793;
|
||||
if (hide_keyboard) {
|
||||
*height = multiline ? 268 : 168;
|
||||
} else {
|
||||
*height = multiline ? 628 : 528;
|
||||
}
|
||||
}
|
||||
if ((param.option & OPTION_USE_OVER_2K) != 0) {
|
||||
*width *= 2;
|
||||
*height *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyFilterAndCommit() {
|
||||
TextFilter filter = nullptr;
|
||||
std::u16string source;
|
||||
uint32_t max_length = 0;
|
||||
uint64_t generation = 0;
|
||||
uint64_t revision = 0;
|
||||
{
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status == Status::None || (!g_state.input_changed && !g_state.commit_pending)) {
|
||||
return;
|
||||
}
|
||||
filter = g_state.input_changed ? g_state.param.filter : nullptr;
|
||||
source = g_state.current_text;
|
||||
max_length = g_state.param.max_text_length;
|
||||
generation = g_state.generation;
|
||||
revision = g_state.revision;
|
||||
g_state.input_changed = false;
|
||||
}
|
||||
|
||||
if (filter != nullptr) {
|
||||
std::vector<char16_t> output(IME_DIALOG_MAX_TEXT_LENGTH + 1, u'\0');
|
||||
uint32_t output_length = IME_DIALOG_MAX_TEXT_LENGTH;
|
||||
if (filter(output.data(), &output_length, source.c_str(),
|
||||
static_cast<uint32_t>(source.size())) == 0 &&
|
||||
output_length <= IME_DIALOG_MAX_TEXT_LENGTH &&
|
||||
IsValidUtf16(std::u16string_view(output.data(), output_length))) {
|
||||
source.assign(output.data(), output.data() + output_length);
|
||||
ClampText(&source, max_length);
|
||||
}
|
||||
}
|
||||
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.generation != generation || g_state.revision != revision ||
|
||||
g_state.status == Status::None) {
|
||||
if (g_state.generation == generation &&
|
||||
(g_state.status == Status::Running ||
|
||||
(g_state.status == Status::Finished && g_state.end_status == EndStatus::Ok))) {
|
||||
g_state.input_changed = true;
|
||||
g_state.commit_pending = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!g_state.input_changed) {
|
||||
g_state.current_text = std::move(source);
|
||||
g_state.cursor = NormalizeCursor(g_state.current_text, g_state.cursor);
|
||||
}
|
||||
const auto& committed =
|
||||
g_state.status == Status::Finished && g_state.end_status != EndStatus::Ok
|
||||
? g_state.original_text
|
||||
: g_state.current_text;
|
||||
WriteGuestText(g_state, committed);
|
||||
g_state.commit_pending = false;
|
||||
}
|
||||
|
||||
bool MatchRunningGeneration(uint64_t generation) {
|
||||
return g_state.status == Status::Running && g_state.generation == generation;
|
||||
}
|
||||
|
||||
bool FinishFromHost(uint64_t generation, EndStatus end_status) {
|
||||
uint64_t notify_generation = 0;
|
||||
{
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (!MatchRunningGeneration(generation)) {
|
||||
return false;
|
||||
}
|
||||
g_state.status = Status::Finished;
|
||||
g_state.end_status = end_status;
|
||||
g_state.commit_pending = true;
|
||||
g_state.revision++;
|
||||
notify_generation = g_state.generation;
|
||||
g_status.store(Status::Finished, std::memory_order_release);
|
||||
g_revision.store(g_state.revision, std::memory_order_release);
|
||||
}
|
||||
NotifyVisibility(false, notify_generation);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ApplyExternalInputs() {
|
||||
std::vector<ExternalInput> inputs;
|
||||
ExtKeyboardFilter filter = nullptr;
|
||||
uint64_t generation = 0;
|
||||
int32_t user_id = 0;
|
||||
bool multiline = false;
|
||||
{
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status != Status::Running || g_state.external_inputs.empty()) {
|
||||
return;
|
||||
}
|
||||
inputs.swap(g_state.external_inputs);
|
||||
filter = g_state.extended.ext_keyboard_filter;
|
||||
generation = g_state.generation;
|
||||
user_id = g_state.param.user_id;
|
||||
multiline = (g_state.param.option & OPTION_MULTILINE) != 0;
|
||||
}
|
||||
|
||||
for (auto& input: inputs) {
|
||||
{
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (!MatchRunningGeneration(generation)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
input.key.user_id = user_id;
|
||||
bool accepted = true;
|
||||
if (filter != nullptr) {
|
||||
uint16_t output_keycode = input.key.keycode;
|
||||
uint32_t output_status = input.key.status;
|
||||
if (filter(&input.key, &output_keycode, &output_status, nullptr) == 0) {
|
||||
accepted =
|
||||
ApplyKeyboardFilterOutput(&input, output_keycode, output_status, multiline);
|
||||
}
|
||||
}
|
||||
if (!accepted) {
|
||||
continue;
|
||||
}
|
||||
switch (input.action) {
|
||||
case ExternalAction::None: break;
|
||||
case ExternalAction::Text: HostInsertText(generation, input.text); break;
|
||||
case ExternalAction::Backspace: HostBackspace(generation); break;
|
||||
case ExternalAction::MoveLeft: HostMoveCursor(generation, -1); break;
|
||||
case ExternalAction::MoveRight: HostMoveCursor(generation, 1); break;
|
||||
case ExternalAction::Cancel: HostCancel(generation); break;
|
||||
case ExternalAction::Accept: HostAccept(generation); break;
|
||||
case ExternalAction::Newline: HostInsertText(generation, u"\n"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogGetPanelSize(const Param* param, uint32_t* width, uint32_t* height) {
|
||||
return ImeDialogGetPanelSizeExtended(param, nullptr, width, height);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogGetPanelSizeExtended(const Param* param, const ExtendedParam* extended,
|
||||
uint32_t* width, uint32_t* height) {
|
||||
if (param == nullptr || width == nullptr || height == nullptr) {
|
||||
return ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
if (static_cast<uint32_t>(param->type) > static_cast<uint32_t>(Type::Number)) {
|
||||
return ERROR_INVALID_TYPE;
|
||||
}
|
||||
if ((param->option & ~VALID_OPTIONS) != 0) {
|
||||
return ERROR_INVALID_OPTION;
|
||||
}
|
||||
if ((param->supported_languages & ~VALID_LANGUAGES) != 0) {
|
||||
return ERROR_INVALID_LANGUAGES;
|
||||
}
|
||||
const int extended_result = ValidateExtended(extended);
|
||||
if (extended_result != OK) {
|
||||
return extended_result;
|
||||
}
|
||||
ComputePanelSize(*param, extended, width, height);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogInit(const Param* param, const ExtendedParam* extended) {
|
||||
if (g_status.load(std::memory_order_acquire) != Status::None) {
|
||||
return ERROR_BUSY;
|
||||
}
|
||||
|
||||
std::u16string initial;
|
||||
std::u16string title;
|
||||
std::u16string placeholder;
|
||||
const int validation = ValidateParam(param, extended, &initial, &title, &placeholder);
|
||||
if (validation != OK) {
|
||||
return validation;
|
||||
}
|
||||
|
||||
uint64_t generation = 0;
|
||||
{
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status != Status::None) {
|
||||
return ERROR_BUSY;
|
||||
}
|
||||
const uint64_t next_generation = g_state.generation + 1;
|
||||
const uint64_t next_revision = g_state.revision + 1;
|
||||
g_state = {};
|
||||
g_state.status = Status::Running;
|
||||
g_state.generation = next_generation;
|
||||
g_state.revision = next_revision;
|
||||
g_state.param = *param;
|
||||
if (extended != nullptr) {
|
||||
g_state.extended = *extended;
|
||||
}
|
||||
g_state.original_text = initial;
|
||||
g_state.current_text = std::move(initial);
|
||||
g_state.cursor = static_cast<uint32_t>(g_state.current_text.size());
|
||||
g_state.title = std::move(title);
|
||||
g_state.placeholder = std::move(placeholder);
|
||||
g_state.commit_pending = true;
|
||||
generation = g_state.generation;
|
||||
g_status.store(Status::Running, std::memory_order_release);
|
||||
g_revision.store(g_state.revision, std::memory_order_release);
|
||||
}
|
||||
NotifyVisibility(true, generation);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogGetStatus() {
|
||||
ApplyExternalInputs();
|
||||
ApplyFilterAndCommit();
|
||||
return static_cast<int>(g_status.load(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogAbort() {
|
||||
uint64_t generation = 0;
|
||||
{
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status == Status::None) {
|
||||
return ERROR_NOT_IN_USE;
|
||||
}
|
||||
if (g_state.status != Status::Running) {
|
||||
return ERROR_NOT_RUNNING;
|
||||
}
|
||||
generation = g_state.generation;
|
||||
}
|
||||
if (!FinishFromHost(generation, EndStatus::Aborted)) {
|
||||
return ERROR_NOT_RUNNING;
|
||||
}
|
||||
ApplyFilterAndCommit();
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogGetResult(Result* result) {
|
||||
{
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status == Status::None) {
|
||||
return ERROR_NOT_IN_USE;
|
||||
}
|
||||
}
|
||||
if (result == nullptr) {
|
||||
return ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
if (!AllZero(result->reserved, sizeof(result->reserved))) {
|
||||
return ERROR_INVALID_RESERVED;
|
||||
}
|
||||
{
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status != Status::Finished) {
|
||||
return ERROR_NOT_FINISHED;
|
||||
}
|
||||
}
|
||||
ApplyFilterAndCommit();
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status == Status::None) {
|
||||
return ERROR_NOT_IN_USE;
|
||||
}
|
||||
if (g_state.status != Status::Finished) {
|
||||
return ERROR_NOT_FINISHED;
|
||||
}
|
||||
result->endstatus = g_state.end_status;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogTerm() {
|
||||
{
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status == Status::None) {
|
||||
return ERROR_NOT_IN_USE;
|
||||
}
|
||||
if (g_state.status != Status::Finished) {
|
||||
return ERROR_NOT_FINISHED;
|
||||
}
|
||||
}
|
||||
ApplyFilterAndCommit();
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status == Status::None) {
|
||||
return ERROR_NOT_IN_USE;
|
||||
}
|
||||
if (g_state.status != Status::Finished) {
|
||||
return ERROR_NOT_FINISHED;
|
||||
}
|
||||
const uint64_t generation = g_state.generation;
|
||||
const uint64_t revision = g_state.revision;
|
||||
g_state = {};
|
||||
g_state.generation = generation;
|
||||
g_state.revision = revision;
|
||||
g_status.store(Status::None, std::memory_order_release);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogGetPanelPositionAndForm(PositionAndForm* form) {
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status == Status::None) {
|
||||
return ERROR_NOT_IN_USE;
|
||||
}
|
||||
if (form == nullptr) {
|
||||
return ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
form->type = 2;
|
||||
form->posx = g_state.param.posx;
|
||||
form->posy = g_state.param.posy;
|
||||
form->horizontal_alignment = g_state.param.horizontal_alignment;
|
||||
form->vertical_alignment = g_state.param.vertical_alignment;
|
||||
ComputePanelSize(g_state.param, &g_state.extended, &form->width, &form->height);
|
||||
return OK;
|
||||
}
|
||||
|
||||
VisualState GetVisualState() noexcept {
|
||||
return {g_status.load(std::memory_order_acquire) == Status::Running,
|
||||
g_revision.load(std::memory_order_acquire)};
|
||||
}
|
||||
|
||||
void SetVisibilityCallback(VisibilityCallback callback) noexcept {
|
||||
g_visibility_callback.store(callback, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool GetHostSnapshot(HostSnapshot* snapshot) {
|
||||
if (snapshot == nullptr) {
|
||||
return false;
|
||||
}
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (g_state.status != Status::Running) {
|
||||
return false;
|
||||
}
|
||||
snapshot->generation = g_state.generation;
|
||||
snapshot->type = g_state.param.type;
|
||||
snapshot->enter_label = g_state.param.enter_label;
|
||||
snapshot->option = g_state.param.option;
|
||||
snapshot->max_text_length = g_state.param.max_text_length;
|
||||
snapshot->cursor = g_state.cursor;
|
||||
snapshot->disable_device = g_state.extended.disable_device;
|
||||
snapshot->key_panel_visible = (g_state.param.option & OPTION_EXT_KEYBOARD) == 0 ||
|
||||
(g_state.extended.option & 0x00000400) == 0;
|
||||
snapshot->posx = g_state.param.posx;
|
||||
snapshot->posy = g_state.param.posy;
|
||||
snapshot->horizontal_alignment = g_state.param.horizontal_alignment;
|
||||
snapshot->vertical_alignment = g_state.param.vertical_alignment;
|
||||
ComputePanelSize(g_state.param, &g_state.extended, &snapshot->panel_width,
|
||||
&snapshot->panel_height);
|
||||
snapshot->text = g_state.current_text;
|
||||
snapshot->title = g_state.title;
|
||||
snapshot->placeholder = g_state.placeholder;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HostInsertText(uint64_t generation, std::u16string_view text) {
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (!MatchRunningGeneration(generation) || text.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (!IsValidUtf16(text)) {
|
||||
return false;
|
||||
}
|
||||
std::u16string allowed;
|
||||
allowed.reserve(text.size());
|
||||
for (const char16_t value: text) {
|
||||
if (IsAllowedInput(value, g_state.param.type, g_state.param.option)) {
|
||||
allowed.push_back(value);
|
||||
}
|
||||
}
|
||||
if (allowed.empty()) {
|
||||
return false;
|
||||
}
|
||||
const size_t available = g_state.param.max_text_length - g_state.current_text.size();
|
||||
if (available == 0) {
|
||||
return false;
|
||||
}
|
||||
std::u16string insertion(std::u16string_view(allowed).substr(0, available));
|
||||
if (insertion.size() < allowed.size() && !insertion.empty() && insertion.back() >= 0xd800 &&
|
||||
insertion.back() <= 0xdbff) {
|
||||
insertion.pop_back();
|
||||
}
|
||||
if (insertion.empty()) {
|
||||
return false;
|
||||
}
|
||||
std::u16string candidate = g_state.current_text;
|
||||
candidate.insert(g_state.cursor, insertion);
|
||||
g_state.current_text = std::move(candidate);
|
||||
g_state.cursor += static_cast<uint32_t>(insertion.size());
|
||||
g_state.input_changed = true;
|
||||
g_state.commit_pending = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HostBackspace(uint64_t generation) {
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (!MatchRunningGeneration(generation) || g_state.cursor == 0) {
|
||||
return false;
|
||||
}
|
||||
uint32_t first = g_state.cursor - 1;
|
||||
if (first > 0 && g_state.current_text[first] >= 0xdc00 &&
|
||||
g_state.current_text[first] <= 0xdfff && g_state.current_text[first - 1] >= 0xd800 &&
|
||||
g_state.current_text[first - 1] <= 0xdbff) {
|
||||
first--;
|
||||
}
|
||||
g_state.current_text.erase(first, g_state.cursor - first);
|
||||
g_state.cursor = first;
|
||||
g_state.input_changed = true;
|
||||
g_state.commit_pending = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HostMoveCursor(uint64_t generation, int delta) {
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (!MatchRunningGeneration(generation) || delta == 0) {
|
||||
return false;
|
||||
}
|
||||
int next = std::clamp(static_cast<int>(g_state.cursor) + delta, 0,
|
||||
static_cast<int>(g_state.current_text.size()));
|
||||
if (delta < 0 && next > 0 && next < static_cast<int>(g_state.current_text.size()) &&
|
||||
g_state.current_text[next] >= 0xdc00 && g_state.current_text[next] <= 0xdfff &&
|
||||
g_state.current_text[next - 1] >= 0xd800 && g_state.current_text[next - 1] <= 0xdbff) {
|
||||
next--;
|
||||
} else if (delta > 0 && next > 0 && next < static_cast<int>(g_state.current_text.size()) &&
|
||||
g_state.current_text[next - 1] >= 0xd800 &&
|
||||
g_state.current_text[next - 1] <= 0xdbff && g_state.current_text[next] >= 0xdc00 &&
|
||||
g_state.current_text[next] <= 0xdfff) {
|
||||
next++;
|
||||
}
|
||||
if (next == static_cast<int>(g_state.cursor)) {
|
||||
return false;
|
||||
}
|
||||
g_state.cursor = static_cast<uint32_t>(next);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HostAccept(uint64_t generation) {
|
||||
return FinishFromHost(generation, EndStatus::Ok);
|
||||
}
|
||||
|
||||
bool HostCancel(uint64_t generation) {
|
||||
return FinishFromHost(generation, EndStatus::UserCanceled);
|
||||
}
|
||||
|
||||
bool HostQueueExternalInput(uint64_t generation, ExternalInput input) {
|
||||
std::scoped_lock lock(g_mutex);
|
||||
if (!MatchRunningGeneration(generation) || g_state.external_inputs.size() >= 128) {
|
||||
return false;
|
||||
}
|
||||
g_state.external_inputs.push_back(std::move(input));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Libs::Dialog::ImeDialog
|
||||
@@ -0,0 +1,199 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_LIBS_IMEDIALOG_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_LIBS_IMEDIALOG_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace Libs::Dialog::ImeDialog {
|
||||
|
||||
constexpr uint32_t IME_DIALOG_MAX_TEXT_LENGTH = 2048;
|
||||
constexpr uint32_t IME_DIALOG_MAX_TITLE_LENGTH = 128;
|
||||
constexpr uint32_t IME_DIALOG_MAX_PLACEHOLDER_LENGTH = 64;
|
||||
|
||||
enum class Status : uint32_t { None = 0, Running = 1, Finished = 2 };
|
||||
enum class EndStatus : uint32_t { Ok = 0, UserCanceled = 1, Aborted = 2 };
|
||||
enum class Type : uint32_t { Default = 0, BasicLatin = 1, Url = 2, Mail = 3, Number = 4 };
|
||||
enum class EnterLabel : uint32_t { Default = 0, Send = 1, Search = 2, Go = 3 };
|
||||
enum class Alignment : uint32_t { Start = 0, Center = 1, End = 2 };
|
||||
|
||||
enum Option : uint32_t {
|
||||
OPTION_MULTILINE = 0x00000001,
|
||||
OPTION_NO_AUTO_CAPITALIZE = 0x00000002,
|
||||
OPTION_PASSWORD = 0x00000004,
|
||||
OPTION_LANGUAGES_FORCED = 0x00000008,
|
||||
OPTION_EXT_KEYBOARD = 0x00000010,
|
||||
OPTION_NO_LEARNING = 0x00000020,
|
||||
OPTION_FIXED_POSITION = 0x00000040,
|
||||
OPTION_DISABLE_COPY_PASTE = 0x00000080,
|
||||
OPTION_DISABLE_RESUME = 0x00000100,
|
||||
OPTION_DISABLE_AUTO_SPACE = 0x00000200,
|
||||
OPTION_DISABLE_POSITION_ADJ = 0x00000800,
|
||||
OPTION_EXPANDED_PREEDIT = 0x00001000,
|
||||
OPTION_JAPANESE_CAPS_LOCK = 0x00002000,
|
||||
OPTION_USE_OVER_2K = 0x00004000,
|
||||
};
|
||||
|
||||
enum DisableDevice : uint32_t {
|
||||
DISABLE_DEVICE_CONTROLLER = 0x00000001,
|
||||
DISABLE_DEVICE_EXT_KEYBOARD = 0x00000002,
|
||||
DISABLE_DEVICE_REMOTE_OSK = 0x00000004,
|
||||
};
|
||||
|
||||
struct Color {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
|
||||
struct Keycode;
|
||||
|
||||
using TextFilter = int32_t(KYTY_SYSV_ABI*)(char16_t* out_text, uint32_t* out_text_length,
|
||||
const char16_t* source_text,
|
||||
uint32_t source_text_length);
|
||||
using ExtKeyboardFilter = int(KYTY_SYSV_ABI*)(const Keycode* source_keycode, uint16_t* out_keycode,
|
||||
uint32_t* out_status, void* reserved);
|
||||
|
||||
struct Param {
|
||||
int32_t user_id;
|
||||
Type type;
|
||||
uint64_t supported_languages;
|
||||
EnterLabel enter_label;
|
||||
uint32_t input_method;
|
||||
TextFilter filter;
|
||||
uint32_t option;
|
||||
uint32_t max_text_length;
|
||||
char16_t* input_text_buffer;
|
||||
float posx;
|
||||
float posy;
|
||||
Alignment horizontal_alignment;
|
||||
Alignment vertical_alignment;
|
||||
const char16_t* placeholder;
|
||||
const char16_t* title;
|
||||
int8_t reserved[16];
|
||||
};
|
||||
|
||||
struct Result {
|
||||
EndStatus endstatus;
|
||||
int8_t reserved[12];
|
||||
};
|
||||
|
||||
struct ExtendedParam {
|
||||
uint32_t option;
|
||||
Color color_base;
|
||||
Color color_line;
|
||||
Color color_text_field;
|
||||
Color color_preedit;
|
||||
Color color_button_default;
|
||||
Color color_button_function;
|
||||
Color color_button_symbol;
|
||||
Color color_text;
|
||||
Color color_special;
|
||||
uint32_t priority;
|
||||
const char* additional_dictionary_path;
|
||||
ExtKeyboardFilter ext_keyboard_filter;
|
||||
uint32_t disable_device;
|
||||
uint32_t ext_keyboard_mode;
|
||||
int8_t reserved[60];
|
||||
};
|
||||
|
||||
struct PositionAndForm {
|
||||
uint32_t type;
|
||||
float posx;
|
||||
float posy;
|
||||
Alignment horizontal_alignment;
|
||||
Alignment vertical_alignment;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
};
|
||||
|
||||
struct Keycode {
|
||||
uint16_t keycode;
|
||||
char16_t character;
|
||||
uint32_t status;
|
||||
uint32_t type;
|
||||
int32_t user_id;
|
||||
uint32_t resource_id;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
enum class ExternalAction : uint8_t {
|
||||
None,
|
||||
Text,
|
||||
Backspace,
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
Cancel,
|
||||
Accept,
|
||||
Newline,
|
||||
};
|
||||
|
||||
struct ExternalInput {
|
||||
Keycode key;
|
||||
ExternalAction action;
|
||||
std::u16string text;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Param) == 0x60);
|
||||
static_assert(offsetof(Param, input_text_buffer) == 0x28);
|
||||
static_assert(offsetof(Param, title) == 0x48);
|
||||
static_assert(sizeof(Result) == 0x10);
|
||||
static_assert(sizeof(ExtendedParam) == 0x88);
|
||||
static_assert(offsetof(ExtendedParam, additional_dictionary_path) == 0x30);
|
||||
static_assert(sizeof(PositionAndForm) == 0x1c);
|
||||
static_assert(sizeof(Keycode) == 0x20);
|
||||
|
||||
struct VisualState {
|
||||
bool active;
|
||||
uint64_t revision;
|
||||
};
|
||||
|
||||
struct HostSnapshot {
|
||||
uint64_t generation;
|
||||
Type type;
|
||||
EnterLabel enter_label;
|
||||
uint32_t option;
|
||||
uint32_t max_text_length;
|
||||
uint32_t cursor;
|
||||
uint32_t disable_device;
|
||||
bool key_panel_visible;
|
||||
float posx;
|
||||
float posy;
|
||||
Alignment horizontal_alignment;
|
||||
Alignment vertical_alignment;
|
||||
uint32_t panel_width;
|
||||
uint32_t panel_height;
|
||||
std::u16string text;
|
||||
std::u16string title;
|
||||
std::u16string placeholder;
|
||||
};
|
||||
|
||||
using VisibilityCallback = void (*)(bool visible, uint64_t generation);
|
||||
|
||||
int KYTY_SYSV_ABI ImeDialogGetPanelSize(const Param* param, uint32_t* width, uint32_t* height);
|
||||
int KYTY_SYSV_ABI ImeDialogGetPanelSizeExtended(const Param* param, const ExtendedParam* extended,
|
||||
uint32_t* width, uint32_t* height);
|
||||
int KYTY_SYSV_ABI ImeDialogInit(const Param* param, const ExtendedParam* extended);
|
||||
int KYTY_SYSV_ABI ImeDialogGetStatus();
|
||||
int KYTY_SYSV_ABI ImeDialogAbort();
|
||||
int KYTY_SYSV_ABI ImeDialogGetResult(Result* result);
|
||||
int KYTY_SYSV_ABI ImeDialogTerm();
|
||||
int KYTY_SYSV_ABI ImeDialogGetPanelPositionAndForm(PositionAndForm* form);
|
||||
|
||||
VisualState GetVisualState() noexcept;
|
||||
void SetVisibilityCallback(VisibilityCallback callback) noexcept;
|
||||
bool GetHostSnapshot(HostSnapshot* snapshot);
|
||||
bool HostInsertText(uint64_t generation, std::u16string_view text);
|
||||
bool HostBackspace(uint64_t generation);
|
||||
bool HostMoveCursor(uint64_t generation, int delta);
|
||||
bool HostAccept(uint64_t generation);
|
||||
bool HostCancel(uint64_t generation);
|
||||
bool HostQueueExternalInput(uint64_t generation, ExternalInput input);
|
||||
|
||||
} // namespace Libs::Dialog::ImeDialog
|
||||
|
||||
#endif // EMULATOR_INCLUDE_EMULATOR_LIBS_IMEDIALOG_H_
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "common/abi.h"
|
||||
#include "libs/dialog.h"
|
||||
#include "libs/imeDialog.h"
|
||||
#include "libs/libs.h"
|
||||
#include "loader/symbolDatabase.h"
|
||||
|
||||
@@ -24,7 +25,14 @@ LIB_VERSION("ImeDialog", 1, "ImeDialog", 1, 1);
|
||||
namespace ImeDialog = Dialog::ImeDialog;
|
||||
|
||||
LIB_DEFINE(InitDialog_1_ImeDialog) {
|
||||
LIB_FUNC("CRD+jSErEJQ", ImeDialog::ImeDialogGetPanelSizeExtended);
|
||||
LIB_FUNC("IADmD4tScBY", ImeDialog::ImeDialogGetStatus);
|
||||
LIB_FUNC("gyTyVn+bXMw", ImeDialog::ImeDialogTerm);
|
||||
LIB_FUNC("x01jxu+vxlc", ImeDialog::ImeDialogGetResult);
|
||||
LIB_FUNC("wqsJvRXwl58", ImeDialog::ImeDialogGetPanelSize);
|
||||
LIB_FUNC("NUeBrN7hzf0", ImeDialog::ImeDialogInit);
|
||||
LIB_FUNC("oBmw4xrmfKs", ImeDialog::ImeDialogAbort);
|
||||
LIB_FUNC("8jqzzPioYl8", ImeDialog::ImeDialogGetPanelPositionAndForm);
|
||||
}
|
||||
|
||||
} // namespace LibImeDialog
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
#include "libs/imeDialog.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
namespace Ime = Libs::Dialog::ImeDialog;
|
||||
|
||||
#define CHECK(condition) \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
std::abort(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int Error(uint32_t value) { return static_cast<int32_t>(value); }
|
||||
|
||||
int g_filter_calls = 0;
|
||||
int g_keyboard_filter_calls = 0;
|
||||
bool g_block_keyboard = false;
|
||||
uint16_t g_remap_keyboard = 0;
|
||||
|
||||
int32_t KYTY_SYSV_ABI CopyFilter(char16_t *out_text, uint32_t *out_length,
|
||||
const char16_t *source,
|
||||
uint32_t source_length) {
|
||||
g_filter_calls++;
|
||||
CHECK(*out_length >= source_length);
|
||||
std::memcpy(out_text, source, source_length * sizeof(char16_t));
|
||||
*out_length = source_length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t KYTY_SYSV_ABI SupplementaryFilter(char16_t *out_text,
|
||||
uint32_t *out_length,
|
||||
const char16_t *, uint32_t) {
|
||||
CHECK(*out_length >= 2);
|
||||
out_text[0] = 0xd83d;
|
||||
out_text[1] = 0xde00;
|
||||
*out_length = 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KeyboardFilter(const Ime::Keycode *source,
|
||||
uint16_t *out_keycode, uint32_t *out_status,
|
||||
void *) {
|
||||
g_keyboard_filter_calls++;
|
||||
if (g_block_keyboard) {
|
||||
*out_keycode = 0;
|
||||
*out_status = 0;
|
||||
return 0;
|
||||
}
|
||||
if (g_remap_keyboard != 0) {
|
||||
*out_keycode = g_remap_keyboard;
|
||||
*out_status = source->status;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Ime::Param MakeParam(char16_t *text) {
|
||||
Ime::Param param{};
|
||||
param.user_id = 1000;
|
||||
param.type = Ime::Type::Default;
|
||||
param.supported_languages = 1;
|
||||
param.enter_label = Ime::EnterLabel::Default;
|
||||
param.input_text_buffer = text;
|
||||
param.max_text_length = 31;
|
||||
return param;
|
||||
}
|
||||
|
||||
void TestLayoutAndPanelSize() {
|
||||
static_assert(sizeof(Ime::Param) == 0x60);
|
||||
static_assert(sizeof(Ime::ExtendedParam) == 0x88);
|
||||
static_assert(sizeof(Ime::Result) == 0x10);
|
||||
static_assert(sizeof(Ime::PositionAndForm) == 0x1c);
|
||||
|
||||
std::array<char16_t, 32> text{};
|
||||
auto param = MakeParam(text.data());
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
CHECK(Ime::ImeDialogGetPanelSize(¶m, &width, &height) == 0);
|
||||
CHECK(width == 793 && height == 528);
|
||||
param.option = Ime::OPTION_MULTILINE;
|
||||
CHECK(Ime::ImeDialogGetPanelSize(¶m, &width, &height) == 0);
|
||||
CHECK(width == 793 && height == 628);
|
||||
param.option = 0;
|
||||
param.type = Ime::Type::Number;
|
||||
CHECK(Ime::ImeDialogGetPanelSize(¶m, &width, &height) == 0);
|
||||
CHECK(width == 370 && height == 522);
|
||||
|
||||
param = MakeParam(text.data());
|
||||
param.option = Ime::OPTION_EXT_KEYBOARD;
|
||||
Ime::ExtendedParam extended{};
|
||||
extended.option = 0x00000400;
|
||||
CHECK(Ime::ImeDialogGetPanelSizeExtended(¶m, &extended, &width,
|
||||
&height) == 0);
|
||||
CHECK(width == 793 && height == 168);
|
||||
param.type = Ime::Type::BasicLatin;
|
||||
CHECK(Ime::ImeDialogGetPanelSizeExtended(¶m, &extended, &width,
|
||||
&height) == 0);
|
||||
CHECK(width == 793 && height == 103);
|
||||
}
|
||||
|
||||
void TestAcceptLifecycle() {
|
||||
std::array<char16_t, 32> text{u'A', u'l', u'i', u'c', u'e'};
|
||||
auto param = MakeParam(text.data());
|
||||
CHECK(Ime::ImeDialogInit(¶m, nullptr) == 0);
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Running));
|
||||
CHECK(Ime::ImeDialogInit(nullptr, nullptr) == Error(0x80bc0001));
|
||||
|
||||
Ime::HostSnapshot snapshot;
|
||||
CHECK(Ime::GetHostSnapshot(&snapshot));
|
||||
CHECK(snapshot.text == u"Alice");
|
||||
CHECK(Ime::HostInsertText(snapshot.generation, u" 2"));
|
||||
CHECK(Ime::HostBackspace(snapshot.generation));
|
||||
CHECK(Ime::HostAccept(snapshot.generation));
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Finished));
|
||||
CHECK(std::u16string(text.data()) == u"Alice ");
|
||||
|
||||
Ime::Result result{};
|
||||
CHECK(Ime::ImeDialogGetResult(&result) == 0);
|
||||
CHECK(result.endstatus == Ime::EndStatus::Ok);
|
||||
CHECK(Ime::ImeDialogTerm() == 0);
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::None));
|
||||
}
|
||||
|
||||
void TestCancelAndAbortRestoreText() {
|
||||
for (const auto expected :
|
||||
{Ime::EndStatus::UserCanceled, Ime::EndStatus::Aborted}) {
|
||||
std::array<char16_t, 32> text{u'o', u'l', u'd'};
|
||||
auto param = MakeParam(text.data());
|
||||
CHECK(Ime::ImeDialogInit(¶m, nullptr) == 0);
|
||||
Ime::HostSnapshot snapshot;
|
||||
CHECK(Ime::GetHostSnapshot(&snapshot));
|
||||
CHECK(Ime::HostInsertText(snapshot.generation, u" value"));
|
||||
if (expected == Ime::EndStatus::UserCanceled) {
|
||||
CHECK(Ime::HostCancel(snapshot.generation));
|
||||
} else {
|
||||
CHECK(Ime::ImeDialogAbort() == 0);
|
||||
}
|
||||
Ime::Result result{};
|
||||
CHECK(Ime::ImeDialogGetResult(&result) == 0);
|
||||
CHECK(result.endstatus == expected);
|
||||
CHECK(std::u16string(text.data()) == u"old");
|
||||
CHECK(Ime::ImeDialogTerm() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TestValidation() {
|
||||
std::array<char16_t, 32> text{};
|
||||
auto param = MakeParam(text.data());
|
||||
param.reserved[0] = 1;
|
||||
CHECK(Ime::ImeDialogInit(¶m, nullptr) == Error(0x80bc0032));
|
||||
param.reserved[0] = 0;
|
||||
param.max_text_length = Ime::IME_DIALOG_MAX_TEXT_LENGTH + 1;
|
||||
CHECK(Ime::ImeDialogInit(¶m, nullptr) == Error(0x80bc0016));
|
||||
CHECK(Ime::ImeDialogGetResult(nullptr) == Error(0x80bc0107));
|
||||
|
||||
param = MakeParam(text.data());
|
||||
Ime::ExtendedParam extended{};
|
||||
extended.ext_keyboard_mode = 0x00000004;
|
||||
CHECK(Ime::ImeDialogInit(¶m, &extended) == Error(0x80bc001c));
|
||||
extended = {};
|
||||
extended.option = 0x00000200;
|
||||
extended.disable_device =
|
||||
Ime::DISABLE_DEVICE_CONTROLLER | Ime::DISABLE_DEVICE_EXT_KEYBOARD;
|
||||
CHECK(Ime::ImeDialogInit(¶m, &extended) == 0);
|
||||
Ime::HostSnapshot snapshot;
|
||||
CHECK(Ime::GetHostSnapshot(&snapshot));
|
||||
CHECK(snapshot.disable_device == 3);
|
||||
CHECK(Ime::ImeDialogAbort() == 0);
|
||||
Ime::Result result{};
|
||||
CHECK(Ime::ImeDialogGetResult(&result) == 0);
|
||||
CHECK(Ime::ImeDialogTerm() == 0);
|
||||
}
|
||||
|
||||
void TestFilteringAndInputPolicy() {
|
||||
std::array<char16_t, 32> text{u'1'};
|
||||
auto param = MakeParam(text.data());
|
||||
param.type = Ime::Type::Number;
|
||||
param.filter = CopyFilter;
|
||||
g_filter_calls = 0;
|
||||
CHECK(Ime::ImeDialogInit(¶m, nullptr) == 0);
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Running));
|
||||
CHECK(g_filter_calls == 0);
|
||||
CHECK(Ime::ImeDialogGetResult(nullptr) == Error(0x80bc0031));
|
||||
CHECK(Ime::ImeDialogTerm() == Error(0x80bc0106));
|
||||
CHECK(g_filter_calls == 0);
|
||||
|
||||
Ime::HostSnapshot snapshot;
|
||||
CHECK(Ime::GetHostSnapshot(&snapshot));
|
||||
CHECK(Ime::HostInsertText(snapshot.generation, u"a2.\n"));
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Running));
|
||||
CHECK(g_filter_calls == 1);
|
||||
CHECK(std::u16string(text.data()) == u"12.");
|
||||
CHECK(Ime::HostCancel(snapshot.generation));
|
||||
Ime::Result result{};
|
||||
CHECK(Ime::ImeDialogGetResult(&result) == 0);
|
||||
CHECK(Ime::ImeDialogTerm() == 0);
|
||||
}
|
||||
|
||||
void TestExternalKeyboardFilter() {
|
||||
std::array<char16_t, 32> text{};
|
||||
auto param = MakeParam(text.data());
|
||||
Ime::ExtendedParam extended{};
|
||||
extended.ext_keyboard_filter = KeyboardFilter;
|
||||
CHECK(Ime::ImeDialogInit(¶m, &extended) == 0);
|
||||
Ime::HostSnapshot snapshot;
|
||||
CHECK(Ime::GetHostSnapshot(&snapshot));
|
||||
|
||||
Ime::ExternalInput input{};
|
||||
input.key.status = 3;
|
||||
input.key.keycode = 4;
|
||||
input.action = Ime::ExternalAction::Text;
|
||||
input.text = u"x";
|
||||
g_keyboard_filter_calls = 0;
|
||||
g_block_keyboard = true;
|
||||
CHECK(Ime::HostQueueExternalInput(snapshot.generation, input));
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Running));
|
||||
CHECK(g_keyboard_filter_calls == 1);
|
||||
CHECK(text[0] == u'\0');
|
||||
|
||||
g_block_keyboard = false;
|
||||
CHECK(Ime::HostQueueExternalInput(snapshot.generation, std::move(input)));
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Running));
|
||||
CHECK(g_keyboard_filter_calls == 2);
|
||||
CHECK(std::u16string(text.data()) == u"x");
|
||||
|
||||
input = {};
|
||||
input.key.status = 3;
|
||||
input.key.keycode = 4;
|
||||
input.action = Ime::ExternalAction::Text;
|
||||
input.text = u"x";
|
||||
g_remap_keyboard = 5;
|
||||
CHECK(Ime::HostQueueExternalInput(snapshot.generation, std::move(input)));
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Running));
|
||||
CHECK(g_keyboard_filter_calls == 3);
|
||||
CHECK(std::u16string(text.data()) == u"xb");
|
||||
|
||||
input = {};
|
||||
input.key.status = 0x00000201;
|
||||
input.key.keycode = 4;
|
||||
input.action = Ime::ExternalAction::Text;
|
||||
input.text = u"x";
|
||||
g_remap_keyboard = 50;
|
||||
CHECK(Ime::HostQueueExternalInput(snapshot.generation, std::move(input)));
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Running));
|
||||
CHECK(g_keyboard_filter_calls == 4);
|
||||
CHECK(std::u16string(text.data()) == u"xb~");
|
||||
|
||||
input = {};
|
||||
input.key.status = 3;
|
||||
input.key.keycode = 4;
|
||||
input.action = Ime::ExternalAction::Text;
|
||||
input.text = u"x";
|
||||
g_remap_keyboard = 88;
|
||||
CHECK(Ime::HostQueueExternalInput(snapshot.generation, std::move(input)));
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Finished));
|
||||
CHECK(g_keyboard_filter_calls == 5);
|
||||
Ime::Result result{};
|
||||
CHECK(Ime::ImeDialogGetResult(&result) == 0);
|
||||
CHECK(result.endstatus == Ime::EndStatus::Ok);
|
||||
CHECK(Ime::ImeDialogTerm() == 0);
|
||||
|
||||
g_remap_keyboard = 0;
|
||||
CHECK(Ime::ImeDialogInit(¶m, &extended) == 0);
|
||||
CHECK(Ime::GetHostSnapshot(&snapshot));
|
||||
|
||||
input = {};
|
||||
input.key.status = 1;
|
||||
input.key.keycode = 41;
|
||||
input.action = Ime::ExternalAction::Cancel;
|
||||
CHECK(Ime::HostQueueExternalInput(snapshot.generation, std::move(input)));
|
||||
input = {};
|
||||
input.key.status = 3;
|
||||
input.key.keycode = 4;
|
||||
input.action = Ime::ExternalAction::Text;
|
||||
input.text = u"ignored";
|
||||
CHECK(Ime::HostQueueExternalInput(snapshot.generation, std::move(input)));
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Finished));
|
||||
CHECK(g_keyboard_filter_calls == 6);
|
||||
result = {};
|
||||
CHECK(Ime::ImeDialogGetResult(&result) == 0);
|
||||
CHECK(Ime::ImeDialogTerm() == 0);
|
||||
}
|
||||
|
||||
void TestFilteredCursorBoundary() {
|
||||
std::array<char16_t, 8> text{};
|
||||
auto param = MakeParam(text.data());
|
||||
param.filter = SupplementaryFilter;
|
||||
CHECK(Ime::ImeDialogInit(¶m, nullptr) == 0);
|
||||
Ime::HostSnapshot snapshot;
|
||||
CHECK(Ime::GetHostSnapshot(&snapshot));
|
||||
CHECK(Ime::HostInsertText(snapshot.generation, u"x"));
|
||||
CHECK(Ime::ImeDialogGetStatus() == static_cast<int>(Ime::Status::Running));
|
||||
CHECK(Ime::GetHostSnapshot(&snapshot));
|
||||
CHECK(snapshot.cursor == 2);
|
||||
CHECK(text[0] == 0xd83d && text[1] == 0xde00 && text[2] == 0);
|
||||
CHECK(Ime::HostCancel(snapshot.generation));
|
||||
Ime::Result result{};
|
||||
CHECK(Ime::ImeDialogGetResult(&result) == 0);
|
||||
CHECK(Ime::ImeDialogTerm() == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
TestLayoutAndPanelSize();
|
||||
TestAcceptLifecycle();
|
||||
TestCancelAndAbortRestoreText();
|
||||
TestValidation();
|
||||
TestFilteringAndInputPolicy();
|
||||
TestExternalKeyboardFilter();
|
||||
TestFilteredCursorBoundary();
|
||||
return 0;
|
||||
}
|
||||
@@ -2997,6 +2997,7 @@ public:
|
||||
mip_padded[level].height};
|
||||
}
|
||||
const auto mip_image = texture_cache.FindImage(mip_desc);
|
||||
(void)texture_cache.FindTexture(mip_image, mip_desc);
|
||||
texture_cache.MarkGpuWritten(mip_image);
|
||||
std::vector<uint32_t> mip_stale(mip_native.size(), 0xdeadbeefu);
|
||||
Libs::LibKernel::Memory::WriteBacking(base + mip_prefix_offset, mip_stale.data(),
|
||||
@@ -3055,6 +3056,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 2, 4, 1);
|
||||
const auto array_image = texture_cache.FindImage(array_desc);
|
||||
(void)texture_cache.FindTexture(array_image, array_desc);
|
||||
texture_cache.MarkGpuWritten(array_image);
|
||||
auto volume_desc = array_desc;
|
||||
volume_desc.info.type = Prospero::ImageType::kColor3D;
|
||||
@@ -3078,6 +3080,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::ImageType::kColor3D, {1, 1, 2}, 1, sizeof(uint32_t), 1);
|
||||
const auto unique_volume_image = texture_cache.FindImage(unique_volume_desc);
|
||||
(void)texture_cache.FindTexture(unique_volume_image, unique_volume_desc);
|
||||
texture_cache.MarkGpuWritten(unique_volume_image);
|
||||
Libs::Graphics::Buffer partial_volume(
|
||||
m_runtime_context, scheduler, MemoryUsage::DeviceLocal,
|
||||
@@ -3117,6 +3120,7 @@ public:
|
||||
depth_containment.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
depth_containment.view_info.level_count = 2;
|
||||
const auto depth_containment_image = texture_cache.FindImage(depth_containment);
|
||||
(void)texture_cache.FindDepthTarget(depth_containment_image, depth_containment);
|
||||
texture_cache.MarkGpuWritten(depth_containment_image);
|
||||
Libs::Graphics::Buffer partial_depth(m_runtime_context, scheduler,
|
||||
MemoryUsage::DeviceLocal,
|
||||
@@ -3159,6 +3163,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32UInt),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 16, 1);
|
||||
const auto uncompressed_block_image = texture_cache.FindImage(uncompressed_block);
|
||||
(void)texture_cache.FindTexture(uncompressed_block_image, uncompressed_block);
|
||||
texture_cache.MarkGpuWritten(uncompressed_block_image);
|
||||
auto compressed_block = MakeLinearDesc(
|
||||
base + block_alias_offset, sizeof(block_alias_data), vk::Format::eBc3UnormBlock,
|
||||
@@ -3220,6 +3225,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16UNorm),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 4, 1);
|
||||
const auto color_image = texture_cache.FindImage(color_desc);
|
||||
(void)texture_cache.FindTexture(color_image, color_desc);
|
||||
texture_cache.MarkGpuWritten(color_image);
|
||||
const std::array<uint16_t, 2> refreshed_multisample_source {0x2000u, 0xe000u};
|
||||
Require(name, "unequal-sample source CPU write",
|
||||
@@ -3421,6 +3427,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 4, 1);
|
||||
const auto mirror_image = texture_cache.FindImage(mirror_desc);
|
||||
(void)texture_cache.FindTexture(mirror_image, mirror_desc);
|
||||
texture_cache.MarkGpuWritten(mirror_image);
|
||||
constexpr uint32_t mirror_cpu_value = 0x0f1e2d3cu;
|
||||
Require(name, "CPU-dirty formatted mirror fault",
|
||||
@@ -3448,6 +3455,7 @@ public:
|
||||
vk::AccessFlagBits::eTransferWrite);
|
||||
auto mirror_refresh_desc = mirror_desc;
|
||||
const auto mirror_refresh = texture_cache.FindImage(mirror_refresh_desc);
|
||||
(void)texture_cache.FindTexture(mirror_refresh, mirror_refresh_desc);
|
||||
Require(name, "buffer-to-image ownership",
|
||||
mirror_refresh == mirror_image &&
|
||||
!texture_cache.GetImage(mirror_refresh).IsBufferModified(),
|
||||
@@ -3464,6 +3472,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 4, 1);
|
||||
const auto exact_buffer_image = texture_cache.FindImage(exact_buffer_desc);
|
||||
(void)texture_cache.FindTexture(exact_buffer_image, exact_buffer_desc);
|
||||
texture_cache.MarkGpuWritten(exact_buffer_image);
|
||||
auto exact_buffer_binding = resources.GetBufferCache().ObtainBuffer(
|
||||
command, exact_buffer_desc.info.data.address, exact_buffer_desc.info.data.size,
|
||||
@@ -3482,6 +3491,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float);
|
||||
exact_float_desc.view_info.format = vk::Format::eR32Sfloat;
|
||||
const auto exact_float_image = texture_cache.FindImage(exact_float_desc, true);
|
||||
(void)texture_cache.FindTexture(exact_float_image, exact_float_desc);
|
||||
Require(name, "Buffer-superseded exact coexistence",
|
||||
exact_float_image != exact_buffer_image &&
|
||||
TextureCacheTestAccess::Contains(texture_cache, exact_buffer_image) &&
|
||||
@@ -3631,6 +3641,8 @@ public:
|
||||
fault_b_desc.info.data.address = base + 0x8010;
|
||||
const auto fault_a_image = texture_cache.FindImage(fault_a_desc);
|
||||
const auto fault_b_image = texture_cache.FindImage(fault_b_desc);
|
||||
(void)texture_cache.FindTexture(fault_a_image, fault_a_desc);
|
||||
(void)texture_cache.FindTexture(fault_b_image, fault_b_desc);
|
||||
texture_cache.MarkGpuWritten(fault_a_image);
|
||||
texture_cache.MarkGpuWritten(fault_b_image);
|
||||
Require(name, "per-image watcher installation",
|
||||
@@ -3659,6 +3671,8 @@ public:
|
||||
"a byte-disjoint CPU write discarded authoritative images");
|
||||
const auto retracked_a = texture_cache.FindImage(fault_a_desc);
|
||||
const auto retracked_b = texture_cache.FindImage(fault_b_desc);
|
||||
(void)texture_cache.FindTexture(retracked_a, fault_a_desc);
|
||||
(void)texture_cache.FindTexture(retracked_b, fault_b_desc);
|
||||
auto fault_a_mirror = resources.GetBufferCache().ObtainBuffer(
|
||||
command, base + 0x8000, sizeof(fault_a), false, true, true);
|
||||
if (fault_a_mirror.owner != nullptr) {
|
||||
@@ -3683,8 +3697,10 @@ public:
|
||||
"the surviving image was not protected after its alias retired");
|
||||
constexpr uint32_t fault_b_cpu = 0xa5a6a7a8u;
|
||||
std::memcpy(memory + 0x8010, &fault_b_cpu, sizeof(fault_b_cpu));
|
||||
const auto refreshed_b = texture_cache.FindImage(fault_b_desc);
|
||||
(void)texture_cache.FindTexture(refreshed_b, fault_b_desc);
|
||||
Require(name, "same-page survivor refresh",
|
||||
texture_cache.FindImage(fault_b_desc) == fault_b_image &&
|
||||
refreshed_b == fault_b_image &&
|
||||
!texture_cache.GetImage(fault_b_image).IsDefinitelyCpuDirty() &&
|
||||
texture_cache.GetImage(fault_b_image).IsGpuModified(),
|
||||
"the surviving image could not reconcile its CPU write");
|
||||
@@ -3700,6 +3716,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 4, 1);
|
||||
const auto publish_image = texture_cache.FindImage(publish_image_desc);
|
||||
(void)texture_cache.FindTexture(publish_image, publish_image_desc);
|
||||
texture_cache.MarkGpuWritten(publish_image);
|
||||
resources.GetBufferCache().FillBuffer(
|
||||
base + publish_buffer_offset, sizeof(publish_buffer_value), publish_buffer_value);
|
||||
@@ -3739,6 +3756,7 @@ public:
|
||||
standalone_ms.view_info.aspect = vk::ImageAspectFlagBits::eDepth;
|
||||
standalone_ms.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
const auto image = texture_cache.FindImage(standalone_ms);
|
||||
(void)texture_cache.FindDepthTarget(image, standalone_ms);
|
||||
texture_cache.MarkGpuWritten(image);
|
||||
uint16_t standalone_cpu_read = 1;
|
||||
std::memcpy(&standalone_cpu_read, memory + offset, sizeof(standalone_cpu_read));
|
||||
@@ -3827,6 +3845,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 4, 1);
|
||||
const auto unformatted_alias_image = texture_cache.FindImage(unformatted_alias);
|
||||
(void)texture_cache.FindTexture(unformatted_alias_image, unformatted_alias);
|
||||
texture_cache.MarkGpuWritten(unformatted_alias_image);
|
||||
auto unformatted_alias_buffer = resources.GetBufferCache().ObtainBuffer(
|
||||
command, unformatted_alias.info.data.address, unformatted_alias.info.data.size,
|
||||
@@ -3881,6 +3900,7 @@ public:
|
||||
layered_color.info.mip_layout[1] = {32, 8, 1, 1};
|
||||
layered_color.view_info.level_count = 2;
|
||||
const auto layered_color_image = texture_cache.FindImage(layered_color);
|
||||
(void)texture_cache.FindTexture(layered_color_image, layered_color);
|
||||
texture_cache.MarkGpuWritten(layered_color_image);
|
||||
auto layered_depth = layered_color;
|
||||
layered_depth.type = BindingType::DepthTarget;
|
||||
@@ -3920,6 +3940,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 4, 1);
|
||||
const auto replacement_source_image = texture_cache.FindImage(replacement_source);
|
||||
(void)texture_cache.FindTexture(replacement_source_image, replacement_source);
|
||||
texture_cache.MarkGpuWritten(replacement_source_image);
|
||||
auto replacement_desc =
|
||||
MakeLinearDesc(base + replacement_offset, replacement_size, vk::Format::eR32Uint,
|
||||
@@ -3960,7 +3981,7 @@ public:
|
||||
compressed_desc.info.metadata.compression = VideoOutCompression::Dcc256_256_0;
|
||||
compressed_desc.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||
const auto compressed_image = texture_cache.FindImage(compressed_desc);
|
||||
texture_cache.MarkGpuWritten(compressed_image);
|
||||
(void)texture_cache.FindRenderTarget(compressed_image, compressed_desc);
|
||||
uint32_t compressed_cpu_read = 0;
|
||||
std::memcpy(&compressed_cpu_read, memory + 0xc000, sizeof(compressed_cpu_read));
|
||||
Require(name, "compressed write-only read policy",
|
||||
@@ -4006,6 +4027,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::ImageType::kColor2D, {mixed_source_width, 1, 1}, 1, 4, 1);
|
||||
const auto mixed_source_image = texture_cache.FindImage(mixed_source_desc);
|
||||
(void)texture_cache.FindTexture(mixed_source_image, mixed_source_desc);
|
||||
Require(name, "mixed-page image upload",
|
||||
!texture_cache.GetImage(mixed_source_image).IsGpuModified(),
|
||||
"clean Buffer source manufactured GPU image ownership");
|
||||
@@ -4037,6 +4059,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 1, 1);
|
||||
const auto byte_mirror_image = texture_cache.FindImage(byte_mirror_desc);
|
||||
(void)texture_cache.FindTexture(byte_mirror_image, byte_mirror_desc);
|
||||
texture_cache.MarkGpuWritten(byte_mirror_image);
|
||||
auto byte_mirror = resources.GetBufferCache().ObtainBuffer(
|
||||
command, base + byte_mirror_offset, 1, false, true, true);
|
||||
@@ -4059,6 +4082,7 @@ public:
|
||||
bgra16_desc.info.bgra16 = true;
|
||||
bgra16_desc.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||
const auto bgra16_image = texture_cache.FindImage(bgra16_desc);
|
||||
(void)texture_cache.FindRenderTarget(bgra16_image, bgra16_desc);
|
||||
auto bgra16_readback = CreateHostBuffer(name, sizeof(bgra16_guest),
|
||||
vk::BufferUsageFlagBits::eTransferDst, {0, 0});
|
||||
auto& bgra16_native = texture_cache.GetImage(bgra16_image);
|
||||
@@ -4350,6 +4374,7 @@ public:
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt),
|
||||
Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 4, 1);
|
||||
const auto exact_image = texture_cache.FindImage(exact_image_desc);
|
||||
(void)texture_cache.FindTexture(exact_image, exact_image_desc);
|
||||
texture_cache.MarkGpuWritten(exact_image);
|
||||
Require(name, "same-page exact alias ownership",
|
||||
resources.GetBufferCache().HasGpuDirtyBytes(base + dirty_sibling_offset,
|
||||
@@ -4391,6 +4416,8 @@ public:
|
||||
gc_image_desc_a.info.data.size),
|
||||
"FindImageFromRange accepted an image without current GPU "
|
||||
"contents");
|
||||
(void)texture_cache.FindTexture(gc_images[0], gc_image_desc_a);
|
||||
(void)texture_cache.FindTexture(gc_images[1], gc_image_desc_b);
|
||||
for (const auto image: gc_images) {
|
||||
texture_cache.MarkGpuWritten(image);
|
||||
}
|
||||
@@ -4865,6 +4892,7 @@ public:
|
||||
vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
|
||||
d16_depth_desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
const auto d16_depth_image = texture_cache.FindImage(d16_depth_desc);
|
||||
(void)texture_cache.FindDepthTarget(d16_depth_image, d16_depth_desc);
|
||||
texture_cache.MarkGpuWritten(d16_depth_image);
|
||||
auto d16_storage_desc = d16_depth_desc;
|
||||
d16_storage_desc.type = BindingType::Storage;
|
||||
@@ -9050,6 +9078,8 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, const std::set<ShaderOpcode>&
|
||||
case Opcode::BufferAtomicAnd:
|
||||
case Opcode::BufferAtomicOr:
|
||||
case Opcode::BufferAtomicXor:
|
||||
case Opcode::BufferAtomicFMin:
|
||||
case Opcode::BufferAtomicFMax:
|
||||
case Opcode::FlatLoadUbyte:
|
||||
case Opcode::FlatLoadSbyte:
|
||||
case Opcode::FlatLoadUshort:
|
||||
@@ -9580,6 +9610,8 @@ TestCase ScalarCompareOps() {
|
||||
code.push_back(EncodeSMovB32(11, InlineU32(0)));
|
||||
code.push_back(EncodeSMovB32(12, InlineU32(2)));
|
||||
code.push_back(EncodeSMovB32(13, InlineU32(0)));
|
||||
code.push_back(EncodeSMovB32(14, InlineU32(1)));
|
||||
code.push_back(EncodeSMovB32(15, InlineU32(1)));
|
||||
|
||||
u32 dst = 20;
|
||||
auto append_compare = [&](u32 opcode, u32 src0, u32 src1) {
|
||||
@@ -9597,9 +9629,13 @@ TestCase ScalarCompareOps() {
|
||||
append_compare(0x08, 4, 3);
|
||||
append_compare(0x09, 4, 4);
|
||||
append_compare(0x0b, 3, 4);
|
||||
append_compare(0x12, 10, 10);
|
||||
append_compare(0x12, 10, 12);
|
||||
append_compare(0x12, 10, 14);
|
||||
append_compare(0x13, 10, 12);
|
||||
append_compare(0x13, 10, 14);
|
||||
|
||||
for (u32 i = 0; i < 11u; i++) {
|
||||
for (u32 i = 0; i < 15u; i++) {
|
||||
AppendStoreSgpr(&code, 20u + i, i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
@@ -9607,10 +9643,10 @@ TestCase ScalarCompareOps() {
|
||||
return {"ScalarCompareOps",
|
||||
code,
|
||||
{},
|
||||
std::vector<u32>(11, 1),
|
||||
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1},
|
||||
{O::SMovB32, O::SCmpEqI32, O::SCmpLgI32, O::SCmpGtI32, O::SCmpGeI32, O::SCmpLtI32,
|
||||
O::SCmpLeI32, O::SCmpLgU32, O::SCmpGtU32, O::SCmpGeU32, O::SCmpLeU32, O::SCmpLgU64,
|
||||
O::SCselectB32, O::VMovB32, O::BufferStoreDword, O::SEndpgm}};
|
||||
O::SCmpLeI32, O::SCmpLgU32, O::SCmpGtU32, O::SCmpGeU32, O::SCmpLeU32, O::SCmpEqU64,
|
||||
O::SCmpLgU64, O::SCselectB32, O::VMovB32, O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase ScalarShiftAddAndMaskOps() {
|
||||
@@ -10110,6 +10146,73 @@ TestCase VectorIntegerOps() {
|
||||
O::VCndmaskB32, O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase Vop2SdwaSubNcExactByte2Destination() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 11, 2);
|
||||
code.push_back(0x4c1616f9u);
|
||||
code.push_back(0x0686128du);
|
||||
AppendStoreVgpr(&code, 11, 0);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "Vop2SdwaSubNcExactByte2Destination";
|
||||
test.code = code;
|
||||
test.expected = {0x000b0002u};
|
||||
test.opcodes = {O::VMovB32, O::VSubNcU32, O::BufferStoreDword, O::SEndpgm};
|
||||
test.required_spirv = {"OpISub", "OpShiftLeftLogical", "OpBitwiseOr"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop2SdwaSubNcPreservesByteAndWordDestinations() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 0, 3);
|
||||
for (u32 dst = 10; dst <= 15; dst++) {
|
||||
AppendVMovLiteral(&code, dst, 0xa1b2c3d4u);
|
||||
}
|
||||
for (u32 sel = 0; sel <= 5; sel++) {
|
||||
code.push_back(EncodeVop2(0x26, 10 + sel, 249, 0));
|
||||
code.push_back(EncodeVop2Sdwa(InlineU32(20), sel, 2, 6, 6, 0, 0, 0, 0, 0, 0, 1));
|
||||
AppendStoreVgpr(&code, 10 + sel, sel);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
return {"Vop2SdwaSubNcPreservesByteAndWordDestinations",
|
||||
code,
|
||||
{},
|
||||
{0xa1b2c311u, 0xa1b211d4u, 0xa111c3d4u, 0x11b2c3d4u, 0xa1b20011u,
|
||||
0x0011c3d4u},
|
||||
{O::VMovB32, O::VSubNcU32, O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase Vop2SdwaMinU32PreservesWordDestination() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 10, 0xa1b2c3d4u);
|
||||
AppendVMovU32(&code, 12, 7);
|
||||
code.push_back(0x261418f9u);
|
||||
code.push_back(0x0686149fu);
|
||||
AppendStoreVgpr(&code, 10, 0);
|
||||
AppendVMovLiteral(&code, 10, 0xa1b2c3d4u);
|
||||
AppendVMovU32(&code, 12, 64);
|
||||
code.push_back(0x261418f9u);
|
||||
code.push_back(0x0686149fu);
|
||||
AppendStoreVgpr(&code, 10, 1);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "Vop2SdwaMinU32PreservesWordDestination";
|
||||
test.code = code;
|
||||
test.expected = {0xa1b20007u, 0xa1b2001fu};
|
||||
test.opcodes = {O::VMovB32, O::VMinU32, O::BufferStoreDword, O::SEndpgm};
|
||||
test.required_spirv = {"OpULessThan", "OpSelect", "OpBitwiseOr"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorShiftCountsMaskLowBits() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -11593,6 +11696,33 @@ TestCase VectorVop3CompareNeU64OnGpu() {
|
||||
O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase VectorVop3CompareEqI64OnGpu() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
code.push_back(EncodeVop1(0x01, 1, InlineU32(1)));
|
||||
code.push_back(EncodeSop1(0x04, 4, 126)); // s_mov_b64 s[4:5], exec
|
||||
|
||||
code.push_back(0xd4a2006au);
|
||||
code.push_back(0x0000087eu); // v_cmp_eq_i64 vcc, exec, s[4:5]
|
||||
code.push_back(EncodeVop2(0x01, 2, InlineU32(0), 1));
|
||||
AppendStoreVgpr(&code, 2, 0);
|
||||
|
||||
AppendSMovLiteral(&code, 4, 0);
|
||||
code.push_back(0xd4a2006au);
|
||||
code.push_back(0x0000087eu); // low dword mismatch
|
||||
code.push_back(EncodeVop2(0x01, 3, InlineU32(0), 1));
|
||||
AppendStoreVgpr(&code, 3, 1);
|
||||
AppendEnd(&code);
|
||||
|
||||
return {"VectorVop3CompareEqI64OnGpu",
|
||||
code,
|
||||
{},
|
||||
{1, 0},
|
||||
{O::VMovB32, O::SMovB64, O::VCmpEqI64, O::VCndmaskB32, O::SMovB32,
|
||||
O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase VectorCompareClassF32() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -11708,6 +11838,32 @@ TestCase Vop2SdwaCndmaskSourceModifier() {
|
||||
{O::VMovB32, O::VCmpTU32, O::VCndmaskB32, O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase Vop2SdwaCndmaskFullDestinationWithSubDwordSource() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 0, 0xabcd1234u);
|
||||
AppendVMovLiteral(&code, 1, 0x55667788u);
|
||||
code.push_back(EncodeVopc(0xc0, Vgpr(0), 0)); // v_cmp_f_u32
|
||||
code.push_back(0x020e02f9u);
|
||||
code.push_back(0x06040600u);
|
||||
AppendStoreVgpr(&code, 7, 0);
|
||||
code.push_back(EncodeVopc(0xc7, Vgpr(0), 0)); // v_cmp_t_u32
|
||||
code.push_back(0x020e02f9u);
|
||||
code.push_back(0x06040600u);
|
||||
AppendStoreVgpr(&code, 7, 1);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "Vop2SdwaCndmaskFullDestinationWithSubDwordSource";
|
||||
test.code = code;
|
||||
test.expected = {0x00001234u, 0x55667788u};
|
||||
test.opcodes = {O::VMovB32, O::VCmpFU32, O::VCmpTU32, O::VCndmaskB32,
|
||||
O::BufferStoreDword, O::SEndpgm};
|
||||
test.required_spirv = {"OpBitFieldUExtract", "OpSelect"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop3CndmaskUsesSgprMaskLaneBits() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -13803,6 +13959,192 @@ TestCase BufferAtomicGlc0DoesNotReturnOldValue() {
|
||||
{O::VMovB32, O::BufferAtomicAdd, O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase BufferAtomicFMinExactRawGlcModes() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 0, 0x40000000u); // 2.0
|
||||
code.push_back(0xe0fc0000u);
|
||||
code.push_back(0x80010000u); // exact failing buffer_atomic_fmin, GLC=0
|
||||
AppendStoreVgpr(&code, 0, 1);
|
||||
AppendVMovLiteral(&code, 0, 0x3f800000u); // 1.0
|
||||
code.push_back(0xe0fc4000u);
|
||||
code.push_back(0x80010000u); // same instruction with GLC=1
|
||||
AppendStoreVgpr(&code, 0, 2);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "BufferAtomicFMinExactRawGlcModes";
|
||||
test.code = code;
|
||||
test.initial = {0x40800000u, 0, 0}; // 4.0
|
||||
test.expected = {0x3f800000u, 0x40000000u, 0x40000000u};
|
||||
test.opcodes = {O::VMovB32, O::BufferAtomicFMin, O::BufferStoreDword, O::SEndpgm};
|
||||
const auto descriptor =
|
||||
MakeStructuredStorageBufferData(0, static_cast<u32>(test.initial.size() * sizeof(u32)));
|
||||
std::copy_n(descriptor.begin(), 4, test.user_data.begin() + 4);
|
||||
test.user_data[50] = 1u << 20u;
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferAtomicFMinSpecialValues() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
const u32 values[] = {
|
||||
0x40000000u, // 2.0
|
||||
0x7f800000u, // +infinity
|
||||
0xff800000u, // -infinity
|
||||
0x3f800000u, // 1.0
|
||||
0x7fc00000u, // quiet NaN
|
||||
0x00000000u, // +0.0
|
||||
0x80000000u, // -0.0
|
||||
0x00000000u, // +0.0
|
||||
0x80000001u, // smallest negative denorm
|
||||
};
|
||||
std::vector<u32> code;
|
||||
for (u32 i = 0; i < static_cast<u32>(std::size(values)); i++) {
|
||||
AppendVMovU32(&code, 20, i * 4u);
|
||||
AppendVMovLiteral(&code, i, values[i]);
|
||||
AppendBufferStoreOpcode(&code, 0x3f, i, 20, true);
|
||||
}
|
||||
for (u32 i = 0; i < static_cast<u32>(std::size(values)); i++) {
|
||||
AppendStoreVgpr(&code, i, i + static_cast<u32>(std::size(values)));
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
return {"BufferAtomicFMinSpecialValues",
|
||||
code,
|
||||
{0x40800000u, 0xbf800000u, 0x7f800000u, 0x7fc00000u, 0x3f800000u,
|
||||
0x80000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0x40000000u, 0xbf800000u, 0xff800000u, 0x7fc00000u, 0x3f800000u,
|
||||
0x80000000u, 0x00000000u, 0x00000000u, 0x80000001u, 0x40800000u,
|
||||
0xbf800000u, 0x7f800000u, 0x7fc00000u, 0x3f800000u, 0x80000000u,
|
||||
0x00000000u, 0x00000001u, 0x00000000u},
|
||||
{O::VMovB32, O::BufferAtomicFMin, O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase BufferAtomicFMinContendedWorkgroup() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
code.push_back(EncodeVop1(0x06, 1, Vgpr(0))); // v_cvt_f32_u32 v1, thread_id.x
|
||||
AppendVMovU32(&code, 20, 0);
|
||||
AppendBufferStoreOpcode(&code, 0x3f, 1, 20);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "BufferAtomicFMinContendedWorkgroup";
|
||||
test.code = code;
|
||||
test.initial = {0x42c80000u}; // 100.0
|
||||
test.expected = {0x00000000u}; // min(100.0, 0.0 .. 63.0)
|
||||
test.opcodes = {O::VCvtF32U32, O::VMovB32, O::BufferAtomicFMin, O::SEndpgm};
|
||||
test.compute_info.threads_num[0] = 64;
|
||||
test.compute_info.threads_num[1] = 1;
|
||||
test.compute_info.threads_num[2] = 1;
|
||||
test.compute_info.thread_ids_num = 1;
|
||||
test.has_compute_info = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferAtomicFMaxExactRawGlcModes() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 3, 0x40000000u); // 2.0
|
||||
code.push_back(0xe100000cu);
|
||||
code.push_back(0x80010300u); // exact failing buffer_atomic_fmax v3, offset 12, GLC=0
|
||||
AppendStoreVgpr(&code, 3, 4);
|
||||
AppendVMovLiteral(&code, 0, 0x40800000u); // 4.0
|
||||
code.push_back(0xe100400cu);
|
||||
code.push_back(0x80010000u); // same address with GLC=1
|
||||
AppendStoreVgpr(&code, 0, 5);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "BufferAtomicFMaxExactRawGlcModes";
|
||||
test.code = code;
|
||||
test.initial = {0, 0, 0, 0x3f800000u, 0, 0}; // memory[3] = 1.0
|
||||
test.expected = {0, 0, 0, 0x40800000u, 0x40000000u, 0x40000000u};
|
||||
test.opcodes = {O::VMovB32, O::BufferAtomicFMax, O::BufferStoreDword, O::SEndpgm};
|
||||
const auto descriptor =
|
||||
MakeStructuredStorageBufferData(0, static_cast<u32>(test.initial.size() * sizeof(u32)));
|
||||
std::copy_n(descriptor.begin(), 4, test.user_data.begin() + 4);
|
||||
test.user_data[50] = 1u << 20u;
|
||||
test.has_user_data = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase BufferAtomicFMaxSpecialValues() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
const u32 values[] = {
|
||||
0x40000000u, // 2.0, finite update
|
||||
0x40000000u, // 2.0, finite no-update
|
||||
0x7f800000u, // incoming +infinity
|
||||
0xff800000u, // incoming -infinity
|
||||
0x3f800000u, // finite against old +infinity
|
||||
0x3f800000u, // finite against old -infinity
|
||||
0x3f800000u, // finite against old quiet NaN
|
||||
0x7fcabcdeu, // incoming quiet NaN
|
||||
0x3f800000u, // finite against old signaling NaN
|
||||
0x7faabcdeu, // incoming signaling NaN
|
||||
0x00000000u, // +0.0 against old -0.0
|
||||
0x80000000u, // -0.0 against old +0.0
|
||||
0x80000000u, // -0.0 against a negative denorm
|
||||
0x80000001u, // negative denorm against -0.0
|
||||
0x00000001u, // positive denorm against +0.0
|
||||
0x00000000u, // +0.0 against a positive denorm
|
||||
};
|
||||
std::vector<u32> code;
|
||||
for (u32 i = 0; i < static_cast<u32>(std::size(values)); i++) {
|
||||
AppendVMovU32(&code, 20, i * 4u);
|
||||
AppendVMovLiteral(&code, i, values[i]);
|
||||
AppendBufferStoreOpcode(&code, 0x40, i, 20, true);
|
||||
}
|
||||
for (u32 i = 0; i < static_cast<u32>(std::size(values)); i++) {
|
||||
AppendStoreVgpr(&code, i, i + static_cast<u32>(std::size(values)));
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
return {"BufferAtomicFMaxSpecialValues",
|
||||
code,
|
||||
{0x3f800000u, 0x40800000u, 0xbf800000u, 0x3f800000u, 0x7f800000u,
|
||||
0xff800000u, 0x7fc12345u, 0x3f800000u, 0x7fa54321u, 0x3f800000u,
|
||||
0x80000000u, 0x00000000u, 0x80000001u, 0x80000000u, 0x00000000u,
|
||||
0x00000001u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0x40000000u, 0x40800000u, 0x7f800000u, 0x3f800000u, 0x7f800000u,
|
||||
0x3f800000u, 0x7fc12345u, 0x3f800000u, 0x7fa54321u, 0x3f800000u,
|
||||
0x80000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x00000001u,
|
||||
0x00000001u, 0x3f800000u, 0x40800000u, 0xbf800000u, 0x3f800000u,
|
||||
0x7f800000u, 0xff800000u, 0x7fc12345u, 0x3f800000u, 0x7fa54321u,
|
||||
0x3f800000u, 0x80000000u, 0x00000000u, 0x80000001u, 0x80000000u,
|
||||
0x00000000u, 0x00000001u},
|
||||
{O::VMovB32, O::BufferAtomicFMax, O::BufferStoreDword, O::SEndpgm}};
|
||||
}
|
||||
|
||||
TestCase BufferAtomicFMaxContendedWorkgroup() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
code.push_back(EncodeVop1(0x06, 1, Vgpr(0))); // v_cvt_f32_u32 v1, thread_id.x
|
||||
AppendVMovU32(&code, 20, 0);
|
||||
AppendBufferStoreOpcode(&code, 0x40, 1, 20);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "BufferAtomicFMaxContendedWorkgroup";
|
||||
test.code = code;
|
||||
test.initial = {0xc2c80000u}; // -100.0
|
||||
test.expected = {0x427c0000u}; // max(-100.0, 0.0 .. 63.0)
|
||||
test.opcodes = {O::VCvtF32U32, O::VMovB32, O::BufferAtomicFMax, O::SEndpgm};
|
||||
test.compute_info.threads_num[0] = 64;
|
||||
test.compute_info.threads_num[1] = 1;
|
||||
test.compute_info.threads_num[2] = 1;
|
||||
test.compute_info.thread_ids_num = 1;
|
||||
test.has_compute_info = true;
|
||||
return test;
|
||||
}
|
||||
|
||||
std::vector<u32> MakeRgbaImage(u32 width, u32 height, u32 value = 0) {
|
||||
return std::vector<u32>(static_cast<size_t>(width) * height * 4u, value);
|
||||
}
|
||||
@@ -15017,6 +15359,9 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorMoves);
|
||||
AddCase(VectorVop3MoveAppliesFloatSourceModifiers);
|
||||
AddCase(VectorIntegerOps);
|
||||
AddCase(Vop2SdwaSubNcExactByte2Destination);
|
||||
AddCase(Vop2SdwaSubNcPreservesByteAndWordDestinations);
|
||||
AddCase(Vop2SdwaMinU32PreservesWordDestination);
|
||||
AddCase(VectorShiftCountsMaskLowBits);
|
||||
AddCase(VectorVop3IntegerOps);
|
||||
AddCase(VectorBfeI32ArithmeticShiftMasksField);
|
||||
@@ -15060,10 +15405,12 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorSpecialF32FlushesDenormalInputs);
|
||||
AddCase(VectorSinCosMaxFiniteSpecialCases);
|
||||
AddCase(VectorCompareOps);
|
||||
AddCase(VectorVop3CompareEqI64OnGpu);
|
||||
AddCase(VectorVop3CompareNeU64OnGpu);
|
||||
AddCase(VectorCompareClassF32);
|
||||
AddCase(VectorCompareF16Ops);
|
||||
AddCase(Vop2SdwaCndmaskSourceModifier);
|
||||
AddCase(Vop2SdwaCndmaskFullDestinationWithSubDwordSource);
|
||||
AddCase(Vop3CndmaskUsesSgprMaskLaneBits);
|
||||
AddCase(Vop3CndmaskAllowsDataSourceModifier);
|
||||
AddCase(VectorCompareExecOps);
|
||||
@@ -15148,6 +15495,12 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(DsSwizzleInvalidSourceLaneZero);
|
||||
AddCase(BufferAtomicVariants);
|
||||
AddCase(BufferAtomicGlc0DoesNotReturnOldValue);
|
||||
AddCase(BufferAtomicFMinExactRawGlcModes);
|
||||
AddCase(BufferAtomicFMinSpecialValues);
|
||||
AddCase(BufferAtomicFMinContendedWorkgroup);
|
||||
AddCase(BufferAtomicFMaxExactRawGlcModes);
|
||||
AddCase(BufferAtomicFMaxSpecialValues);
|
||||
AddCase(BufferAtomicFMaxContendedWorkgroup);
|
||||
AddCase(ImageLoadVariants);
|
||||
AddCase(ImageLoadR32UintUsesIntegerSampledImage);
|
||||
AddCase(ImageLoad1DUsesScalarCoordinate);
|
||||
@@ -15389,6 +15742,15 @@ void CheckEmbeddedFetchVertexOffset() {
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
void CheckRenderTargetFormatContract() {
|
||||
const auto r8_uint =
|
||||
TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k8),
|
||||
Prospero::GpuEnumValue(Prospero::ChannelType::kUInt),
|
||||
Prospero::GpuEnumValue(Prospero::ChannelOrder::kStandard));
|
||||
Require("RenderTargetFormat", "R8 uint",
|
||||
r8_uint.format == vk::Format::eR8Uint && r8_uint.bytes_per_element == 1u &&
|
||||
r8_uint.export_mapping.IsIdentity(),
|
||||
"R8 uint render-target tuple was rejected");
|
||||
|
||||
const auto rgb565 = TextureGetRenderTargetFormat(16u, 0u, 0u);
|
||||
Require("RenderTargetFormat", "RGB565 UNorm",
|
||||
rgb565.format == vk::Format::eB5G6R5UnormPack16 && rgb565.bytes_per_element == 2u &&
|
||||
@@ -16404,7 +16766,7 @@ ShaderTextureResource AtomicStorageTextureDescriptor() {
|
||||
} else if (std::strcmp(kind, "type") == 0) {
|
||||
descriptor.fields[3] = (descriptor.fields[3] & 0x0fffffffu) |
|
||||
(Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) << 28u);
|
||||
} else if (std::strcmp(kind, "tile") == 0) {
|
||||
} else if (std::strcmp(kind, "standard256b-volume") == 0) {
|
||||
descriptor.fields[3] = (descriptor.fields[3] & ~(0x1fu << 20u)) |
|
||||
(Prospero::GpuEnumValue(Prospero::TileMode::kStandard256B) << 20u);
|
||||
} else if (std::strcmp(kind, "mip") == 0) {
|
||||
@@ -16614,6 +16976,38 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
"PPSA21268 uint 2D-array storage descriptor fixture is malformed");
|
||||
ValidateStorageTexture(BasicUintArrayStorageTextureResource(), uint_array, 0x10000);
|
||||
|
||||
const ShaderTextureResource standard256b {{
|
||||
0x204e4900u,
|
||||
0x43c00000u,
|
||||
0x00004000u,
|
||||
0x90100facu,
|
||||
0x00000000u,
|
||||
0x00700000u,
|
||||
0x00000000u,
|
||||
0x00000000u,
|
||||
}};
|
||||
auto standard256b_resource = BasicBgraStorageTextureResource();
|
||||
standard256b_resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||
const auto standard256b_pitch = TileGetTexturePitch(
|
||||
standard256b.Format(), standard256b.Width5() + 1u, 1, standard256b.TileMode());
|
||||
TileSizeAlign standard256b_size {};
|
||||
TileGetTextureTotalSize(standard256b.Format(), standard256b.Width5() + 1u,
|
||||
standard256b.Height5() + 1u, standard256b.Depth() + 1u,
|
||||
standard256b_pitch, 1, standard256b.TileMode(), false,
|
||||
standard256b_size);
|
||||
Require("BasicStorageTexture", "Standard256B uint 2D descriptor",
|
||||
standard256b.Width5() + 1u == 2 && standard256b.Height5() + 1u == 2 &&
|
||||
standard256b.Depth() + 1u == 1 &&
|
||||
standard256b.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
|
||||
standard256b.Format() ==
|
||||
Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UInt) &&
|
||||
standard256b.TileMode() ==
|
||||
Prospero::GpuEnumValue(Prospero::TileMode::kStandard256B) &&
|
||||
standard256b.DstSelXYZW() == DstSel(4, 5, 6, 7) &&
|
||||
standard256b_size.size == 0x100 && standard256b_size.align == 0x100,
|
||||
"captured PPSA08511 Standard256B storage descriptor is malformed");
|
||||
ValidateStorageTexture(standard256b_resource, standard256b, standard256b_size.size);
|
||||
|
||||
const auto standard4kb_array = Standard4KBUintArrayStorageTextureDescriptor();
|
||||
const auto standard4kb_pitch =
|
||||
TileGetTexturePitch(standard4kb_array.Format(), 1, 1, standard4kb_array.TileMode());
|
||||
@@ -16750,7 +17144,7 @@ void CheckBasicStorageTextureDescriptor() {
|
||||
"GetModuleFileName failed");
|
||||
for (const char* kind: {"resource",
|
||||
"type",
|
||||
"tile",
|
||||
"standard256b-volume",
|
||||
"mip",
|
||||
"swizzle",
|
||||
"linear-rgb1-read",
|
||||
|
||||
@@ -623,6 +623,7 @@ ImageTestUserData(Prospero::ImageType type = Prospero::ImageType::kColor2D) {
|
||||
std::array<uint32_t, 64> data {};
|
||||
for (uint32_t start = 0; start + 3u < data.size(); start += 4u) {
|
||||
data[start] = 0x1000u + start * 0x100u;
|
||||
data[start + 1u] = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm) << 20u;
|
||||
data[start + 2u] = UINT32_MAX;
|
||||
data[start + 3u] = Prospero::GpuEnumValue(type) << 28u;
|
||||
}
|
||||
@@ -1169,6 +1170,8 @@ void TestNewShaderRecompilerMoreAluFamilies() {
|
||||
EncodeVop2Sdwa(5, 6, 0, 4, 5),
|
||||
0x025e6af9u,
|
||||
0x16060635u, // v_cndmask_b32 v47, v53, -v53
|
||||
0x020e02f9u,
|
||||
0x06040600u, // v_cndmask_b32 v7, v0.lo, v1; full-width destination
|
||||
0x100490f9u,
|
||||
0x86860600u, // v_mul_f32 v2, s0, s72 (SDWA full)
|
||||
EncodeVop2(0x35, 9, 249, 1),
|
||||
@@ -1395,6 +1398,9 @@ void TestNewShaderRecompilerMoreAluFamilies() {
|
||||
Check(Common::ContainsStr(result.decoded_dump, "v_cndmask_b32 v47, v53,") &&
|
||||
Common::ContainsStr(result.decoded_dump, "v53.neg"),
|
||||
"new decoder did not decode V_CNDMASK_B32 SDWA source modifier");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "v_cndmask_b32 v7, v0.sdwa(sel=4") &&
|
||||
Common::ContainsStr(result.decoded_dump, "v1"),
|
||||
"new decoder did not decode full-destination V_CNDMASK_B32 with SDWA source");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "v_add_f32 v125, v5.dpp"),
|
||||
"new decoder did not decode VOP2 DPP source metadata");
|
||||
Check(!Common::ContainsStr(result.decoded_dump, "VOP2 SDWA/DPP modifiers are not implemented"),
|
||||
@@ -1699,6 +1705,8 @@ void TestNewShaderRecompilerMoreAluFamilies() {
|
||||
Check(Common::ContainsStr(result.ir_dump, "SelectMaskF32Bits v47, vcc_lo, v53.neg, v53") &&
|
||||
Common::ContainsStr(result.ir_dump, "v53.neg"),
|
||||
"V_CNDMASK_B32 SDWA source modifier did not lower to float-bit select IR");
|
||||
Check(Common::ContainsStr(result.ir_dump, "SelectMaskU32 v7, vcc_lo, v1, v0.sdwa(sel=4"),
|
||||
"full-destination V_CNDMASK_B32 with SDWA source did not lower to integer select IR");
|
||||
Check(Common::ContainsStr(result.ir_dump, "FAddF32 v125.dpp") &&
|
||||
Common::ContainsStr(result.ir_dump, "v5.dpp"),
|
||||
"VOP2 DPP source/destination did not lower to IR metadata");
|
||||
@@ -2416,6 +2424,10 @@ void TestNewShaderRecompilerBootB16PackedAndSdwaOpcodes() {
|
||||
0x0c860688u, // v_add_nc_u32 v5, 8, sign-extended v4.lo
|
||||
0x4a0c0cf9u,
|
||||
0x0d860688u, // v_add_nc_u32 v6, 8, sign-extended v6.hi
|
||||
0x4c1616f9u,
|
||||
0x0686128du, // v_sub_nc_u32 v11.byte2, 13, v11; preserve other destination bytes
|
||||
0x261418f9u,
|
||||
0x0686149fu, // v_min_u32 v10.word0, 31, v12; preserve upper destination word
|
||||
0xbf810000u,
|
||||
};
|
||||
|
||||
@@ -2464,6 +2476,10 @@ void TestNewShaderRecompilerBootB16PackedAndSdwaOpcodes() {
|
||||
Check(Common::ContainsStr(result.decoded_dump, "v_add_nc_u32 v6") &&
|
||||
Common::ContainsStr(result.decoded_dump, "v6.sdwa(sel=5,sext=1"),
|
||||
"new decoder did not decode V_ADD_NC_U32 SDWA sign-extended high word");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "v_sub_nc_u32 v11.sdwa(sel=2"),
|
||||
"new decoder did not decode V_SUB_NC_U32 SDWA byte-2 destination");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "v_min_u32 v10.sdwa(sel=4"),
|
||||
"new decoder did not decode V_MIN_U32 SDWA low-word destination");
|
||||
Check(!Common::ContainsStr(result.decoded_dump, "unsupported family=VOP2 opcode=0x00"),
|
||||
"literal/SDWA extension words were decoded as phantom VOP2 instructions");
|
||||
Check(!Common::ContainsStr(result.decoded_dump,
|
||||
@@ -2497,6 +2513,10 @@ void TestNewShaderRecompilerBootB16PackedAndSdwaOpcodes() {
|
||||
Check(Common::ContainsStr(result.ir_dump, "IAddU32 v6") &&
|
||||
Common::ContainsStr(result.ir_dump, "v6.sdwa(sel=5,sext=1"),
|
||||
"V_ADD_NC_U32 SDWA sign-extended high word did not lower to IR");
|
||||
Check(Common::ContainsStr(result.ir_dump, "ISubU32 v11.sdwa(sel=2"),
|
||||
"V_SUB_NC_U32 SDWA byte-2 destination did not lower to IR");
|
||||
Check(Common::ContainsStr(result.ir_dump, "UMinU32 v10.sdwa(sel=4"),
|
||||
"V_MIN_U32 SDWA low-word destination did not lower to IR");
|
||||
Check(SpirvContainsOpcode(result.spirv, 128),
|
||||
"SPIR-V binary does not contain OpIAdd for U16 operations");
|
||||
Check(SpirvContainsOpcode(result.spirv, 202),
|
||||
@@ -2895,6 +2915,61 @@ void CheckNewDecoderUnsupported(const uint32_t* shader, uint32_t words, const ch
|
||||
"unsupported lowering error was not explicit");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerRejectsDppOn64BitCompares() {
|
||||
const uint32_t opcodes[] = {0xa2u, 0xe5u}; // v_cmp_eq_i64, v_cmp_ne_u64
|
||||
for (const auto opcode: opcodes) {
|
||||
const uint32_t shader[] = {
|
||||
EncodeVopc(opcode, 250u, 0u), // DPP escape in SRC0
|
||||
EncodeVop2Dpp(0u),
|
||||
0xbf810000u,
|
||||
};
|
||||
|
||||
ShaderRecompiler::Decoder::Program program;
|
||||
std::string error;
|
||||
Check(ShaderRecompiler::Decoder::DecodeProgram(shader, program, &error), error.c_str());
|
||||
Check(program.instructions.size() == 2u,
|
||||
"64-bit VOPC DPP decode did not consume its modifier word");
|
||||
const auto& compare = program.instructions.front();
|
||||
Check(compare.opcode == ShaderRecompiler::Decoder::Opcode::Unsupported,
|
||||
"64-bit VOPC illegally accepted a DPP modifier");
|
||||
Check(Common::ContainsStr(compare.unsupported_reason,
|
||||
"VOPC DPP modifier is not supported for opcode"),
|
||||
"64-bit VOPC DPP rejection reason was not explicit");
|
||||
}
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerIrLookupMissFailsExplicitly() {
|
||||
ShaderRecompiler::Decoder::Program decoded;
|
||||
ShaderRecompiler::Decoder::Instruction missing;
|
||||
missing.pc = 0u;
|
||||
missing.family = ShaderRecompiler::Decoder::Family::VOP1;
|
||||
missing.opcode = ShaderRecompiler::Decoder::Opcode::Unknown;
|
||||
decoded.instructions.push_back(missing);
|
||||
|
||||
ShaderRecompiler::CFG::Graph cfg;
|
||||
ShaderRecompiler::CFG::BasicBlock block;
|
||||
block.inst_end = 1u;
|
||||
cfg.blocks.push_back(block);
|
||||
cfg.entry_block = 0u;
|
||||
|
||||
ShaderRecompiler::IR::Program ir;
|
||||
std::string error;
|
||||
Check(!ShaderRecompiler::IR::LowerProgram(decoded, cfg, ShaderType::Compute, 64u, ir, &error),
|
||||
"missing decoder-to-IR mapping unexpectedly lowered as an instruction");
|
||||
Check(Common::ContainsStr(error, "no IR lowering"),
|
||||
"missing decoder-to-IR mapping did not report an explicit error");
|
||||
Check(ir.blocks.empty(), "missing decoder-to-IR mapping emitted a fallback IR block");
|
||||
|
||||
decoded.instructions.front().family = ShaderRecompiler::Decoder::Family::MUBUF;
|
||||
decoded.instructions.front().opcode = ShaderRecompiler::Decoder::Opcode::VAddNcU32;
|
||||
error.clear();
|
||||
Check(!ShaderRecompiler::IR::LowerProgram(decoded, cfg, ShaderType::Compute, 64u, ir, &error),
|
||||
"memory-family opcode bypassed specialized memory lowering");
|
||||
Check(Common::ContainsStr(error, "memory-family opcode") &&
|
||||
Common::ContainsStr(error, "specialized IR lowering"),
|
||||
"memory-family lowering bypass did not report an explicit error");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerMemoryFamilyLowering() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeSmem0(0x00, 0, 4),
|
||||
@@ -4722,6 +4797,10 @@ void TestNewShaderRecompilerAtomicLowering() {
|
||||
EncodeMubuf1(5, 0, 1), // buffer_atomic_or
|
||||
EncodeMubuf0(0x3b, 40, true, true),
|
||||
EncodeMubuf1(6, 0, 1), // buffer_atomic_xor
|
||||
0xe0fc0000u,
|
||||
0x80010000u, // exact buffer_atomic_fmin v0, s[4:7], 0 (GLC=0)
|
||||
0xe100000cu,
|
||||
0x80010300u, // exact failing buffer_atomic_fmax v3, s[4:7], 12 (GLC=0)
|
||||
EncodeDs0(0x00),
|
||||
EncodeDs1(0, 2, 1), // ds_add_u32
|
||||
EncodeDs0(0x01),
|
||||
@@ -4776,6 +4855,10 @@ void TestNewShaderRecompilerAtomicLowering() {
|
||||
"new decoder did not decode buffer atomic signed max");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_xor"),
|
||||
"new decoder did not decode buffer atomic xor");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_fmin"),
|
||||
"new decoder did not decode buffer atomic float min");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "buffer_atomic_fmax"),
|
||||
"new decoder did not decode buffer atomic float max");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "ds_add_u32"),
|
||||
"new decoder did not decode DS atomic add");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "ds_sub_u32"),
|
||||
@@ -4808,6 +4891,10 @@ void TestNewShaderRecompilerAtomicLowering() {
|
||||
"buffer atomic or did not lower to IR");
|
||||
Check(Common::ContainsStr(result.ir_dump, "AtomicXorU32 v6"),
|
||||
"buffer atomic xor did not lower to IR");
|
||||
Check(Common::ContainsStr(result.ir_dump, "AtomicFMinF32 null, v0"),
|
||||
"buffer atomic float min did not lower to IR without a GLC return");
|
||||
Check(Common::ContainsStr(result.ir_dump, "AtomicFMaxF32 null, v3"),
|
||||
"buffer atomic float max did not lower to IR without a GLC return");
|
||||
Check(Common::ContainsStr(result.ir_dump, "AtomicAddU32 null, v2"),
|
||||
"DS no-return atomic add did not lower to IR");
|
||||
Check(Common::ContainsStr(result.ir_dump, "AtomicSubU32 null, v10"),
|
||||
@@ -4851,6 +4938,8 @@ void TestNewShaderRecompilerAtomicLowering() {
|
||||
Check(SpirvContainsOpcode(result.spirv, 240), "SPIR-V binary does not contain OpAtomicAnd");
|
||||
Check(SpirvContainsOpcode(result.spirv, 241), "SPIR-V binary does not contain OpAtomicOr");
|
||||
Check(SpirvContainsOpcode(result.spirv, 242), "SPIR-V binary does not contain OpAtomicXor");
|
||||
Check(SpirvContainsOpcode(result.spirv, 230),
|
||||
"SPIR-V binary does not contain OpAtomicCompareExchange for float min");
|
||||
Check(SpirvContainsOpcode(result.spirv, 225),
|
||||
"buffer atomic SPIR-V binary does not contain OpMemoryBarrier");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
@@ -6638,6 +6727,35 @@ void TestNewShaderRecompilerNativeBindingPlan() {
|
||||
rejected_spirv == rejected_before && rejected_error.find("atomic") != std::string::npos,
|
||||
"malformed atomic instruction reached a fatal descriptor path");
|
||||
|
||||
auto malformed_float_atomic = result.program;
|
||||
atomic_inst = FindBufferInstruction(&malformed_float_atomic);
|
||||
Check(atomic_inst != nullptr, "native validation fixture lacks a float atomic candidate");
|
||||
atomic_inst->op = ShaderRecompiler::IR::Opcode::AtomicFMaxF32;
|
||||
atomic_inst->memory.kind = ShaderRecompiler::IR::ResourceKind::Lds;
|
||||
atomic_inst->memory.resource = 0;
|
||||
atomic_inst->src_count = 2;
|
||||
rejected_spirv = rejected_before;
|
||||
rejected_error.clear();
|
||||
Check(!ShaderRecompiler::Spirv::EmitProgram(malformed_float_atomic, result.resources, nullptr,
|
||||
nullptr, nullptr, rejected_spirv,
|
||||
&rejected_error) &&
|
||||
rejected_spirv == rejected_before &&
|
||||
rejected_error.find("floating-point atomic") != std::string::npos,
|
||||
"buffer-only floating-point atomic accepted a non-buffer resource");
|
||||
|
||||
auto invalid_ir_opcode = result.program;
|
||||
Check(!invalid_ir_opcode.blocks.empty() && !invalid_ir_opcode.blocks.front().instructions.empty(),
|
||||
"native validation fixture lacks an invalid-opcode candidate");
|
||||
invalid_ir_opcode.blocks.front().instructions.front().op =
|
||||
ShaderRecompiler::IR::Opcode::Count;
|
||||
rejected_spirv = rejected_before;
|
||||
rejected_error.clear();
|
||||
Check(!ShaderRecompiler::Spirv::EmitProgram(invalid_ir_opcode, result.resources, nullptr, nullptr,
|
||||
nullptr, rejected_spirv, &rejected_error) &&
|
||||
rejected_spirv == rejected_before &&
|
||||
rejected_error.find("opcode is out of range") != std::string::npos,
|
||||
"out-of-range IR opcode was not rejected transactionally before emission");
|
||||
|
||||
auto overwide_atomic = result.program;
|
||||
atomic_inst = FindBufferInstruction(&overwide_atomic);
|
||||
Check(atomic_inst != nullptr, "native validation fixture lacks an overwide atomic candidate");
|
||||
@@ -7417,6 +7535,8 @@ int main() {
|
||||
TestNewShaderRecompilerScalarVectorAlu();
|
||||
TestNewShaderRecompilerVop3LaneReadDestinationEncoding();
|
||||
TestNewShaderRecompilerMoreAluFamilies();
|
||||
TestNewShaderRecompilerRejectsDppOn64BitCompares();
|
||||
TestNewShaderRecompilerIrLookupMissFailsExplicitly();
|
||||
TestNewShaderRecompilerExpandedAluBatch();
|
||||
TestNewShaderRecompilerVop3pPackedF16();
|
||||
TestNewShaderRecompilerStagedShaderOps();
|
||||
|
||||
Reference in New Issue
Block a user